Programming
-
[DI] 의존성과 계층화2019.12.16
-
[C#] interface 와 abstract class 의 차이2019.12.12
-
[C#] 인터페이스 (interface)에 대한 정리2019.12.06
-
MOCK을 활용한 .NET 단위 테스트2019.12.05
-
BDD 중심의 시나리오 테스트2019.12.02
[DI] 의존성과 계층화
모든 소프트웨어는 의존성(dependency) 를 가지고 있다.
1. 같은 기반코드를 이용하는 코드에 대한 First-Party 의존성.
2. 외부 어셈블리에 대한 Third-Party 의존성.
3. 닷넷 프레임워크에 대한 보편적인 의존성
일 수 있으며, 웬만한 프로젝트라면 이 세가지 의존성을 모두 찾아볼수 있다.
의존성은 호출자 코드로부터 기능을 추상화 한다. 모든 의존성에 대한 관리만큼은 정확히 해야 한다.
의존성 체인을 제대로 관리하지 못하면, 개발자들은 존재할 필요가 없는 의존성 때문에 불필요한 어셈블리 참조로 복잡하게 꼬인 코드를 관리해야 한다.
계층화 (Layering)는 가장 일반적인 아키텍처 패턴 중 하나이다.
의존성의 정의
의존성( Dependency )은 별개의 두 엔티티(entity) 사이의 연관 관계로 인해, 어느 한 엔티티가 다른 엔티티의 기능 없이는 (혹은 존재 없이는) 자신의 기능을 실행하지 못하는 관계를 의미합니다.
엔티티는 주로 어셈블리를 의미하며, 어셈블리 A 가 다른 어셈블리 B를 사용하고 있다면, A가 B에 의존적이라고 말할 수 있습니다. 이런 관계를 서술하는 가장 일반적인 방법은 A가 B의 클라이언트(client) 이고, B는 A의 서비스(service) 라고 표현하는 방법이다.
퍼스트파티 의존성 - 간단예제
하나의 솔루션에 두개의 프로젝트를 생성했습니다.
ClassLibrary 는 클래스 라이브러리 프로젝트이며,
SimpleDependency 는 콘솔 앱 프로젝트입니다. (.NET Framework) 기반
SimpleDependency 프로젝트에 ClassLibrary 프로젝트를 참조한 뒤, 별다른 코딩없이 실행시켜봅니다.
디버그로 실행한 뒤, 디버그 > 창 > 모듈 을 선택하면,
현재 로드된 모듈에 대한 정보를 볼 수 있습니다.
ClassLibrary를 참조 하였음에도, 실질적인 사용 부분이 없다면 로드되지 않는 모습을 볼 수 있습니다.
어플리케이션이 ClassLibrary 어셈블리가 제공하는 기능을 전혀 사용하지 않기 때문에 닷넷 런타임이 해당 모듈을 로드하지 않은 것입니다.
using System;
namespace SimpleDependency
{
class Program
{
static void Main(string[] args)
{
var service = new ClassLibrary.Reward();
service.SendReward();
Console.ReadKey();
}
}
}
실질적으로 사용하는 코드를 작성하고 디버그 모드로 실행 했을 때 모듈창에서 ClassLibrary.dll 이 로드 된 것을 볼 수 있습니다. 실제 사용하는 순간 모듈을 로드 하며, 같은 솔루션 내에 구현되어 있는, 이를 일반적으로 First-Party 의존성이라고 합니다.
프레임워크 의존성
위 두 프로젝트는 닷넷 프레임워크 어셈블리들에 의존성을 가지고 있습니다. SimpleDependency 프로젝트는 닷넷프레임워크에 대한 여러가지 참조를 가지고 있고, 이런 의존성은 대부분 모든 콘솔 어플리케이션 프로젝트가 기본적으로 가지게 되는 의존성입니다.
프레임워크 어셈블리는 항상 로드된다.
다른 의존성과는 달리 닷넷 프레임워크 어셈블리를 참조하면, 이 어셈블리들은 항상 로드된다는 점이다.
이런 어셈블리들을 실제로 사용하지 않는다 해도 어플리케이션을 시작할때 자동으로 로드한다.
다행인 점은 솔루션 내의 여러 프로젝트가 모두 같은 어셈블리를 참조하면 단 하나의 어셈블리 인스턴스만 메모리에 로드되어 공유된다는 점이다.
기본 참조 목록
Visual Studio\Common7\IDE\ProjectTemplates\CSharp\Windows\1042\ConsoleApplication
<ItemGroup>
<Reference Include="System"/>
$if$ ($targetframeworkversion$ >= 3.5)
<Reference Include="System.Core"/>
<Reference Include="System.Xml.Linq"/>
<Reference Include="System.Data.DataSetExtensions"/>
$endif$
$if$ ($targetframeworkversion$ >= 4.0)
<Reference Include="Microsoft.CSharp"/>
$endif$
<Reference Include="System.Data"/>
$if$ ($targetframeworkversion$ >= 4.5)
<Reference Include="System.Net.Http"/>
$endif$
<Reference Include="System.Xml"/>
</ItemGroup>
콘솔 어플리케이션에 대한 기본 참조 목록은 위 경로에 템플릿으로 정의되어 있다.
서드파티 의존성
개발자에 의해 개발된 어셈블리에 대한 의존성입니다. 닷넷 프레임워크가 제공하지 않는 기능에 대해, 퍼스트 파티 의존성을 추가할 수 있으나 너무 많은 노력이 듭니다. 이런 경우 미리 만들어진 솔루션을 선택할 수 있는데요.
예를 들어 객체 관계 매퍼 (ORM, Object Relational Mapper) 같은 경우입니ㅏㄷ.
서드파티 의존성을 선택하는 가장 중요한 이유는, 어떤 기능이나 인프라스트럭처를 직접 구현하느라 많은 노력을 투자하는 대신, 이미 만들어진 것을 가져와 적절히 활용할 수 있기 때문입니다.
또한 Nuget을 이용하여 서드파티 의존성을 관리하는 방법도 있습니다.
'Programming' 카테고리의 다른 글
[DI] 의존성 관리하기 (2) (0) | 2019.12.25 |
---|---|
[DI] 의존성 관리하기 (1) (0) | 2019.12.17 |
[C#] interface 와 abstract class 의 차이 (0) | 2019.12.12 |
[C#] 인터페이스 (interface)에 대한 정리 (0) | 2019.12.06 |
MOCK을 활용한 .NET 단위 테스트 (0) | 2019.12.05 |
[C#] interface 와 abstract class 의 차이
C# 인터페이스와 추상클래스의 차이점
Interface | Abstract Class | |
접근 지정자 |
- 함수에 대한 접근 지정자를 가질수 없습니다. |
- 함수에 대한 접근 지정자를 가질 수 있습니다. |
구현 | - 구현이 아닌 서명만 가질 수 있습니다. | - 구현을 제공할 수 있습니다. |
속도 | - 인터페이스가 상대적으로 느립니다. | - 추상 클래스가 빠릅니다. |
인스턴스화 | - 인터페이스는 추상적이며 인스턴스화 할 수 없습니다. | - 추상클래스는 인스턴스화 할 수 없습니다. |
필드 | - 인터페이스는 필드를 가질 수 없습니다. | - 추상클래스는 필드와 상수를 정의 할 수 있습니다. |
메소드 | - 인터페이스에는 추상메소드만 있습니다. | - 추상클래스에는 비추상메소드가 있을 수 있습니다. |
- C#에서 클래스는 하나 이상의 인터페이스를 상속합니다. 그러나 클래스는 하나의 추상클래스만 상속 할 수 있습니다.
- C#에서 인터페이스는 생성자를 선언할 수 없습니다. 추상 클래스는 생성자를 선언할 수 있습니다.
- C#에서 인터페이스는 클래스의 외부 능력을 정의하는 데 사용됩니다. 추상 클래스는 클래스의 실제 ID를 정의하는 데 사용되며 객체 또는 동일한 유형으로 사용됩니다.
- C#에서 다양한 구현이 메소드 서명 만 공유하는 경우 인터페이스가 사용됩니다. 다양한 구현이 동일한 종류이고 동일한 동작 또는 상태를 사용하는 경우 추상 클래스가 사용됩니다.
- C#에서 새 메소드가 인터페이스에 추가 된 경우 모든 인터페이스가 구현 된 위치를 추적하고 해당 메소드의 구현도 추가해야합니다. 추상 클래스에서 새 메소드가 추가 된 경우 기본 구현을 추가 할 수있는 옵션이 있으므로 모든 기존 코드가 올바르게 작동합니다.
정리
C#은 데이터 추상화에 사용되었습니다. 여러 클래스가 인터페이스를 구현해야하는 경우 인터페이스가 추상 클래스보다 낫습니다. 인터페이스 멤버는 정적일 수 없으며, 추상 클래스의 유일한 완전한 멤버는 정적 일 수 있습니다.
C#은 다중 상속을 지원하지 않으며 인터페이스는 주로 다중 상속을 구현하는 데 사용됩니다.
클래스는 하나 이상의 인터페이스를 구현할 수 있으며 하나의 추상 클래스에서만 상속합니다.
인터페이스는 주로 메서드 나 기능을 구현할 필요가없는 경우에만 사용됩니다.
추상 클래스는 최소한의 기본구현을 필요할때 사용됩니다.
C# 인터페이스와 추상클래스는 요구 사항에 따라 응용 프로그램을 개발하는 데 많이 사용되는 객체 지향 프로그래밍 개념입니다. 그것은 더 편안하고 비즈니스 요구 사항에 맞는 기술 리드에 의해 순수하게 선택됩니다.
C# 인터페이스와 추상 클래스는 모두 사용하기 쉽고 모든 프로그래밍 언어에서 쉽게 배울 수 있습니다.
'Programming' 카테고리의 다른 글
[DI] 의존성 관리하기 (1) (0) | 2019.12.17 |
---|---|
[DI] 의존성과 계층화 (0) | 2019.12.16 |
[C#] 인터페이스 (interface)에 대한 정리 (0) | 2019.12.06 |
MOCK을 활용한 .NET 단위 테스트 (0) | 2019.12.05 |
BDD 중심의 시나리오 테스트 (0) | 2019.12.02 |
[C#] 인터페이스 (interface)에 대한 정리
실무에서 interface 를 기반으로 설계하기란, 여간 번거로운게 아니다.
DI Pattern 으로 인해 개인적으로 interface에 대한 관심이 높아져서, 몇차례에 걸쳐서 정리해보려고 한다.
인터페이스 정의하기
인터페이스는 메서드 원형들의 집합에 이름을 붙인 것입니다.
공용 런타임과 이를 기반으로 하는 모든 관리 프로그래밍 언어들은 다중 상속을 지원하지 않습니다.
CLR은 인터페이스를 이용해서 축소된 형태의 다중상속을 지원합니다.
일반적으로 interface라는 용어는 "상이한 시스템간의 계약 정보를 표현하는 것" 이라고 정의할 수 있습니다.
C#의 예약어이면서, OOP의 기본개념으로 자리잡고 있는 Interface는 형태상으로 몇가지 특징이 있습니다.
1. 선언(Declaration)은 있고, 정의(Definition)은 없다.
2. 다중 상속이 가능하다.
3. C#의 Property는 내부적으로 메소드로 구현되기 때문에 포함할 수 있다.
4. C# 컴파일러는 인터페이스 메서드를 구현할때, public 으로 선언하도록 요구한다.
CLR은 인터페이스 메서드가 virtual 로 정의 할 것을 요구한다.
소스코드에서 메서드 구현시 명시적으로 virtual 키워드를 사용하지 않으면,
컴파일러가 해당 메서드에 virtual 과 seald 키워드를 포함시키며,
이 경우 이 클래스를 상속한 다른 클래스에서 인터페이스 메서드를 재정의 할 수 없다.
만약 명시적으로 메서드에 virtual 키워드를 지정하면, 상속한 다른 클래스에서 재정의 할 수 있다.
인터페이스의 제약
1. 인터페이스는 생성자 메서드를 정의 할 수 없다.
2. 인터페이스는 정적 맴버도 정의 할 수 없다.
3. 인터페이스 내에서는 형식들을 중첩 사용할 수 없다. (나열형, 구조체, 클래스, 인터페이스)
4. 인터페이스는 다른 인터페이스를 상속 받을 수 있지만, 구조체 또는 클래스로부터 상속받을 수 없다.
인터페이스? 추상클래스?
인터페이스는 간단하게 계약(contract)라고 정의 되며, 구현 없이 메서드 선언만 포함되어 있다.
인터페이스를 추상메서드만 담고 있는 추상 클래스라고 생각해도 될것같다.
abstract class Drawing
{
public abstract void Draw();
public abstract void Move();
}
interface IDraw
{
void Draw();
void Move();
}
그럼 추상 클래스를 사용하지 인터페이스를 왜 사용할까?
추상 클래스는 단일 상속이지만, 인터페이스는 다중상속이 가능하다.
추상클래스는 클래스로 정의된 타입이여서 다중 상속을 할 수 없지만, 인터페이스는 클래스가 아니기 때문에 다중상속이 허용된다.
인터페이스의 메서드를 자식 클래스에서 구현할 때는 반드시 public 접근 제한자를 명시해야 한다.
C#의 프로퍼티는 내부적으로 메소드로 구현되기 때문에 인터페이스에는 프로퍼티 역시 포함 할 수 있다.
인터페이스를 사용해야 하는 이유
모든 클래스에 대해 상위 인터페이스를 만들 필요는 없으나, 객체 간 호출이 많다면 인터페이스 분리가 좋다.
1. 외부 시스템에 제공할 서비스 또는 객체 정보를 직접적인 class가 아닌 "선언" 만을 제공한다.
2. 다중 상속에 따른 class의 표현을 여러가지로 가능하게 한다.
3. 의도하지 않은 속성/메소드 공개를 막기 위해서도 사용한다.
4. 실제 object 의 class 정보를 제공하고 싶지 않을 때 혹은 명확히 하고 싶을 때 사용한다.
명시적 인터페이스
C#에서 메서드의 이름 앞에 해당 메서드를 정의한 인터페이스 이름을 접두사로 붙여서 메서드를 정의 하면,
명시적 인터페이스 구현 메서드 (Explicit Interface Method Implementation, EIMI) 를 만들게 된다.
또한 명시적 인터페이스 구현 메서드에는 virtual 키워드도 지정할 수 없으며, 재정의도 불가능 하다.
명시적 인터페이스 구현 메서드는 실제로는 타입 객체모델의 일부가 아니기 때문이다.
'Programming' 카테고리의 다른 글
[DI] 의존성 관리하기 (1) (0) | 2019.12.17 |
---|---|
[DI] 의존성과 계층화 (0) | 2019.12.16 |
[C#] interface 와 abstract class 의 차이 (0) | 2019.12.12 |
MOCK을 활용한 .NET 단위 테스트 (0) | 2019.12.05 |
BDD 중심의 시나리오 테스트 (0) | 2019.12.02 |
MOCK을 활용한 .NET 단위 테스트
현재 저는 UnitTest를 이용해서, 제작중인 프로젝트의 테스트를 진행하고 있습니다.
프로젝트를 이용해서 단위테스트를 제작하다가 보면, 테스트 환경에서는 실질적인 DB가 갖춰져 있는 않은 상태가 많이 있습니다.
이럴경우 Data 단의 로직은 항상 실패상황이 발생하게 됩니다.
이럴경우 Mock 라이브러리를 이용하면, 실제 DB에 접속해서 데이터를 가져오는 것 처럼 흉내낼 수 있습니다.
저희도 Mock 라이브러리를 이용해서 UnitTest를 구성중에 있어서 한번 정리해봤습니다.
Mock 라이브러리
Mock 라이브러리는 .NET 에서 주목 받고 있는 모의 라이브러리 입니다.
Moq는 간단한 API를 가지고 있고, Visual Studio의 Intelisence 기능을 활용할 수 있어 손쉽게 사용이 가능합니다.
.NET 환경에서 NMock, TypeMock.NET, EasyMock.NET, RhinoMock 등 많은 라이브러리가 존재하지만, 간단한 사용법을 가지고 있어 사용하게 되었습니다.
설치
Moq의 이용은 C # 3.0 (.NET Framework3.5) 이상이 필요합니다.
테스트 도구의 종류는 무엇이든 상관 없지만, 저희는 Visual Studio의 단위 테스트 기능을 사용했습니다.
패키지는 Nuget 패키지를 이용해서 설치 하였습니다.
검색은 "Moq" 로 해주시면 됩니다.
https://github.com/moq/moq4
moq/moq4
Repo for managing Moq 4.x . Contribute to moq/moq4 development by creating an account on GitHub.
github.com
샘플
public class Stamina
{
public virtual int Add(int current, int add)
{
return current + add;
}
}
public void TestMethod2()
{
var mock = new Mock<SampleProject.Stamina>();
mock.Setup( x => x.Add( 1, 2 ) ).Returns( 3 );
SampleProject.Stamina stamina = mock.Object;
Assert.AreEqual( 3, stamina.Add( 1, 2 ));
mock.VerifyAll();
}
코드의 설명
우선 Mock을 만들려면 해당 클래스를 지정하여 Mock 인스턴스를 만듭니다.
Mock 클래스의 Setup 메소드를 이용하여 값을 제어 합니다.
여기에서는 「mock.Setup (x => m.Add (1, 2)). Returns (3) "이라고하여 Add 메서드가 인수 1과 2에서 불려 갔을 경우에 3을 반환하도록 지정 하고 있습니다.
모의를 이용하려면 Object 속성을 이용합니다.
이제 Stamina 클래스의 모의를 검색 할 수 있습니다.꺼낸 모의 사용법은 원래 Stamina 클래스와 동일합니다.
마지막 VerifyAll 방법은 모의가 제대로 호출되었는지 여부를 확인할 수 있습니다.
클래스의 모의를 생성 할 때의 주의점
Moq는 모의 메서드를 재정의하는 클래스의 대상 메소드에 virtual 한정자를 둘 필요가 있습니다.
또한 Interface 를 이용해서 진행하는 방식도 있습니다.
더 고급버전은 다음 블로그에서 설명하겠습니다.
'Programming' 카테고리의 다른 글
[DI] 의존성 관리하기 (1) (0) | 2019.12.17 |
---|---|
[DI] 의존성과 계층화 (0) | 2019.12.16 |
[C#] interface 와 abstract class 의 차이 (0) | 2019.12.12 |
[C#] 인터페이스 (interface)에 대한 정리 (0) | 2019.12.06 |
BDD 중심의 시나리오 테스트 (0) | 2019.12.02 |
BDD 중심의 시나리오 테스트
시나리오 테스트?
게임 서버를 개발 중에 있어, 현재의 고민은 최대한 문제 없이 서버를 오픈하기 위한 여러가지 방법을 도입하고, 팀원들과 연구하고 있습니다.
그 중 한가지가 개발중인 컨텐츠와 기존에 개발되었던 시스템간에 문제가 없는지를 상시적으로 테스트 할 수 있는 방법론들을 찾아서 도입하고 있는데요.
그래서 생각해 낸 방법이, 여러 상황에 대한 시나리오를 작성하고, 정해진 시나리오 대로 서버 패킷 들을 테스트 하는 방법이 좋겠다라는 생각에 도달하였습니다.
이 외적으로 단위 테스트도 도입하였는데요. 해당 이야기는 다음에 정리하겠습니다.
시나리오를 정해서 테스트 한다라는 개념은, 어느정도 많이 알려진 개념이였지만 BDD라는 개발 방법론으로 정리가 되어있었다는 것은 이번에 알게 되어 같이 정리해보려고 합니다.
BDD 란?
Behavior-driven development 의 약자로 동작 지향 개발, 행동 기반 개발의 의미인 프로그램 개발 방법의 일종입니다.
이는 테스트 주도 개발 (TDD) 에서 파생되어진 개념입니다.
BDD는 2003년 Dan North 에 의해 처음 개념이 언급되었고, 이후 TDD 지지자들의 성원 속에 스토리(시나리오) 테스트의 자동화라는 형태로 지원 프레임워크가 속속 등장하게 된다.
꽤 오래전에 언급된 개념인데, 일반적으로 생각할 수 있는 개념인데 이렇게 정형화되어서 정리가 된 방법론인지는 이제야 알게됬다.
https://dannorth.net/introducing-bdd/
BDD는 함수 단위 테스트를 권장하지 않고, 시나리오 기반으로 테스트 케이스를 작성한다. 이 시나리오는 개발자가 아닌 사람이 봐도 이해할 수 있는 수준으로 갖성되어야 하고,
하나의 시나리오는 Given, When, Then 구조를 가지는 것을 기본 패턴으로 권장한다.
Feature : 테스트 대상의 기능/책임을 명시한다.
Scenario : 테스트 목적에 대한 상황을 설명한다.
Given : 시나리오 진행에 필요한 값을 설정한다.
When : 시나리오를 진행하는데 필요한 조건을 명시한다.
Then : 시나리오를 완료했을 때 보장해야 하는 결과를 명시한다.
그래서 도입은??
실질적인 도입은 BDD 개념으로 시작 했지만, 위 방법론을 정확하게 따르며 구현하지는 않았습니다.
의미에 대해서는 충분히 인지를 하였고, 구체화하는 과정은 현 개발팀의 상황에 맞게 구현하는게 맞다고 생각했습니다.
개발된 모든 패킷을 검증 할 수 있는 각각의 시나리오를 작성하였으며, 해당 기능이 동작하는 Process 를 개발 하여 도입할 예정입니다.
구현방식에 대해서는 다음에 설명하도록 하겠습니다.
'Programming' 카테고리의 다른 글
[DI] 의존성 관리하기 (1) (0) | 2019.12.17 |
---|---|
[DI] 의존성과 계층화 (0) | 2019.12.16 |
[C#] interface 와 abstract class 의 차이 (0) | 2019.12.12 |
[C#] 인터페이스 (interface)에 대한 정리 (0) | 2019.12.06 |
MOCK을 활용한 .NET 단위 테스트 (0) | 2019.12.05 |