Dependency Injection 개념과 Ninject 사용법

소프트웨어를 잘 만들기 위해서 많은 디자인 패턴들이 사용되는데, 그중에서 DI(Dependency Injection, 의존성 주입)에 대한 개념과 .NET MVC에서 많이 사용되는 DI Framework들 중 하나인 Ninject(Open Source Project)에 대해서 알아보겠다.

DI(Dependency Injection) 란?

프로그래밍에서 사용되는 객체들 사이의 의존관계를 소스코드가 아닌 외부 설정파일 등을 통해 정의하게 하는 디자인 패턴이다. 개발자는 각 객체의 의존관계를 일일이 소스코드에 작성할 필요 없이 설정파일에 의존관계가 필요하다는 정보만 된다. 그러면 객체들이 생성될때, 외부로부터 의존관계를 주입 받아 관계가 설정 된다.

DI를 적용하면, 의존관계 설정이 컴파일시 고정 되는 것이 아니라 실행시 설정파일을 통해 이루어져 모듈간의 결합도(Coupling)을 낮출 수 있다.
결합도가 낮아지면, 코드의 재사용성이 높아져서 모듈을 여러 곳에서 수정 없이 사용할 수 있게 되고, 모의 객체를 구성하기 쉬워지기 때문에 단위 테스트에 용이해 진다.

이제, .NET에서 MVC 프로젝트를 만들때 DI를 구현하기 위해 가장 많이 사용하는 Open Source인 Ninject에 대해서 알아보자.

NInject 알아보기

(공식 페이지 : http://www.ninject.org/)

간단히 이름부터 살펴보면, NInject는, N(Ninja) + Inject로 대표 이미지로도 Ninja로 되어 있다.

홈페이지 대문에 보면 “Be fast, be agile, be precise”라는 슬로건이 있는데 닌자처럼 빠르고 민첩하며 정확하게 프로그램을 만들수 있게 하겠다는 정신이 담겨있는 것 같다.

(Nate Kohari라는 소프트웨어 엔지니어가 최초 개발을 했는데, 개인적인 생각으로는 N을 중의적인 의미로 사용한게 아닌가 싶다. 참고 : https://www.infoq.com/articles/ninject10-released/)

NInject는 Open Source 라이브러리로 Apache License 2.0에 따라 배포되었으며,
2007년 부터 .NET 어플리케이션의 DI를 구현하기 쉽게 해주도록 지원하고 있다.

이제, 실제로 사용해 보자

NInject 사용해 보기

  1. Package Install
    • Visual Studio에서 제공하는 Nuget Package Installer를 사용하여 다음 Package들을 설치한다.
      – Ninject
      – Ninject.Web.WebApi
      – Ninject.Web.Common
      – Ninject.Web.Common.WebHost
  2. Edit Ninject.Web.Common.cs
    • 위 Package 설치가 완료되면, 프로젝트 최상단에서 App_Start 폴더에 Ninject.Web.Common.cs 파일이 생성된 것을 확인 할 수 있다.
    • 해당 파일을 열어 보면, CreateKernel이라는 method가 있는데 다음 코드를 추가한다. 그러면 NInject가 controller의 의존성 주입을 구성해 줄 수 있게 된다.
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
  1. Register Service
    • 스크롤을 조금 내려 보면, RegisterServices라는 method를 확인 할 수 있는데, 실제로 의존관계를 설정(bind)하는 곳이다. 의존성을 주입할 객체들의 관계를 다음과 같이 추가한다.
private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ICommonStore>().To<CommonStore>();
}
  1. Use it on controller
    • 이제 의존성 주입을 위한 IoC 설정과, 의존관계 설정(bind)작업을 모두 하였으니 Controller에서 사용해보자.
public class CommonStoreController : Controller
{    
    public CommonStoreController(ICommonStore common)
    {
        this.commonStore = common;
    }

    private ICommonStore commonStore;

    public int GetItemCount(string id)
    {
        return commonStore.Add(id);
    }
}

Design Pattern 중 DI(Dependency Injection, 의존성 주입)이라는 패턴에은 표준 프로그래밍을 할 때 중요한 요소이긴하지만 무조건 사용해야 하는 것은 아니다. 간단한 프로그램이나 객체간의 결합이 명확하여 구분하지 않아도 되는 경우 굳이 프로젝트를 무겁게(?) 만들 필요없다.

그리고 Ninject는 .NET에서 많이 사용되는 Open Source DI Framework 중 하나로 DI를 할 때 쉽게 구현할 수 있어서 선호되는 편이지만 다양한 DI Open Source Framework들이 있으며, 성능 면에서도 훨씬 더 좋은 것들이 있으니 확인하고 사용하길 바란다.
(참고 : https://www.claudiobernasconi.ch/2019/01/24/the-ultimate-list-of-net-dependency-injection-frameworks/)

[.NET]XML주석을 활용하여 Swagger UI에 세부 메타 데이터 정보 추가 하기

Swagger를 사용하여 개발하다 보면 API에 대한 좀더 자세한 설명을 제공 하고 싶을 때가 있는데, 스팩화면을 별도로 개발하거나 문서를 작성하지 않고 C#코드에 추가한 XML주석을 활용하여 Swagger UI에 바로 세부 메타 데이터 정보 추가 할 수 있다.

먼저, C# 코드의 각 Class, Method, Properties 등에 XML 주석을 추가 작성해 보자.
(TIP, 정의 코드 위에서 ‘/(Slash)’를 연속 3번 누르면 자동으로 XML 테그를 생성해준다.)

이제 작성된 XML 코드들이 문서(파일)로 저장 될 수 있도록 프로젝트 환경설정에 설정 하도록 한다.
프로젝트 속성에서 빌드 메뉴를 살펴 보면 출력 관련 설정하는 영역을 확인 할 수 있다.

XML 문서 파일 체크 박스를 그림과 같이 체크한다, 설정(체크) 하면 우측에 경로가 기본값으로 셋팅 되며, 해당 경로 값을 복사해 둔다.
(원하는 경로가 있다면 변경해도 되지만, 기본값 쓰는 것을 권장한다.)

다음으로, 저장된 XML 문서 파일을 Swagger에서 열어서 화면에 표시 할 수 있도록 설정 해야 한다. 
SwaggerConfig.cs을 열어 다음과 같이 코드를 추가 한다.  

GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
 c.IncludeXmlComments(string.Format(@"{0}\bin\SwaggerFramework.XML"
, System.AppDomain.CurrentDomain.BaseDirectory));
})

/*.......... 생략 ............*/

(복사한 경로값으로 “\bin\SwaggerFramework.XML”부분을 변경하여 설정 한다.)   

이제, 설정이 완료 되었으니 프로젝트를 실행 시켜 어떻게 나오는지 확인해 보자.

Swagger UI를 확인해 보면 위 XML코드를 작성한 내용이 태그 이름에 따라 화면에 나타는 것을 확인 할 수 있다.

이와 같이, Swagger UI에서 C#코드의 사용자 정의 XML(주석)을 사용하여 API들에 대한 설명들을 메타 데이터 표현할 수 있도록 함으로써, 추가적인 별다른 노력 없이 좀 더 자세한 API 스팩 문서를 작성 및 제공 할 수 있다.

Azure Cloud Service Too many redirects

Azure의 Cloud Service를 개발 할 때, Local에서 테스트 할 때는 정상 작동했는데, 프로젝트 패키지를 프로덕션(혹은 스테이징)에 배포하고 확인 하니 Too many redirects 라는 오류 페이지가 뜨고 아무것도 확인 할 수 없을 때가 있다.

이런 오류는 보통 Cloud Service에 설정된 Framework과 로컬에서 프로젝트에 구성한 Framework버전이 달라서 생기는 문제다.

우선 WebRole을 비롯한 솔루션에 추가된(종속된) 프로젝트들의 속성에 들어가 그림과 같이 Target Framework(대상 프레임워크) 버전을 확인 한다. (되도록이면, 사용하는 프로젝트들의 Framework 버전중 최상위 버전 1가지로 통일 하는 것을 추천하며 예제에서는 4.6을 사용한다고 가정한다.) 

각 프로젝트의 버전을 확인 한 후 Cloud Service에서 해당 Framework 버전을 지원하는 Azure Guest OS Release 버전을 확인 한다.  
(참조 링크: https://docs.microsoft.com/ko-kr/azure/cloud-services/cloud-services-guestos-update-matrix)

링크에 들어가서 보면 알 수 있겠지만 글 작성일 기준으로 각 Guest OS에 설치된 Framework Version을 정리해 보면 다음 표와 같다.

Guest OS Release Installed .NET Framework Version
Release 5, Windows Server 2016 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2
Release 4, Windows Server 2012 R2 4.0, 4.5, 4.5.1, 4.5.2
Release 3, Windows Server 2012 4.0, 4.5, 4.5.1, 4.5.2
Release 2, Windows Server 2008 R2 SP1 3.5, 4.0, 4.5, 4.5.1, 4.5.2

이제 위에서 확인한 프로젝트들의 target Framework 버전을 지원하는 Guest OS가 설정되어 있는지 Cloud Service 환경설정을 확인 해보아야 한다. Cloud Service의 환경설정을 확인 하기 위해서 아래 그림의 위치의 *.cscfg 파일을 열어 보자.

확인 해 보면 Line:2의 ‘ServiceConfiguration‘태그의 ‘osFamily‘속성을 확인 해 볼 수 있는데, 이곳이 OS Release 버전을 설정하는 곳이다. 프로젝트들의 Target Framework(대상 프레임웍)버전 설정이 Cloud Service에 설정된 osFamily에서 지원하는 것인지 확인하여 지원하는 것으로 고쳐 준다. (예제는 Release 4로 설정되어 있는 osFamily 속성을 4.6버전을 지원하는 Release 5로 변경하였다.)

AS-IS

<ServiceConfiguration serviceName="CloudServiceSample1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2015-04.2.6">

TO-BE

<ServiceConfiguration serviceName="CloudServiceSample1" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="5" osVersion="*" schemaVersion="2015-04.2.6">

이제 다시 빌드 하고 배포하면 정상적인 프로젝트 동작을 확인 해 볼 수 있다.

[.NET]Swagger를 이용하여 Azure API APP 문서 만들기

Swagger를 사용하면 개발된 Restful API에대한 스팩을 별도의 문서를 만들지 않고도 자동으로 문서화 하여 웹형태로 쉽게 제공 할 수 있다.

먼저, Swagger에서 제공하는 API 스팩 문서 화면을 살펴 보자.

Rest API의 URL(Route 주소)목록을 제일 먼저 볼 수 있는데, 직관적으로 해당 API APP에 어떤기능들 있는지 한번에 파악 할 수 있게해준다. 특정 URL을 클릭하면 그 API안에 구현된 HTTP Method 리스트를 볼 수 있다.

Restful API의 동작은 동일한 Route 주소에 HTTP Method를 통해서 CRUD(생성 – Post, 조회 – Get, 변경 – Put, 삭제 – Delete)기능을 구분하며 이러한 구분을 Swagger에서는 4가지 색으로 구분하여 표현해준다.

다음으로, Get Method를 클릭하여 Swagger에서 해당 API에 대한 문서를 어떻게 제공하는지 실제 코드랑 비교하 살펴보자.

Swagger Source Code

// GET api/values/5
[SwaggerOperation("GetById")]
[SwaggerResponse(HttpStatusCode.OK)]        
[SwaggerResponse(HttpStatusCode.NotFound)]
public string Get(int id)
{
    return "value";
}

Swagger Document

첫 번째, 최상단의 GET /api/Values/{id}는 Rest API를 호출하기 위한 라우트 주소로 메서드 역할을 정의하는 public string Get(int id) 의 정보이다.

두 번째, 최상단 바로 및의 Parameters영역은 API의 메서드의 변수에 대한 설명을 확인 할 수 있으며, Value 필드에 임의 값을 넣고 테스트 해볼 수 도 있다. 
(값을 넣고 아래 Try it Out! 이라는 버튼을 누르면 개발한 API가 동작한다.)

마지막, Response Messages영역은 API가 HTTP통신을 할때 반환하는 HttpResponseMessage의 HTTP State Code에 대한 정의이다. Swashbuckle의 속성 설정 방법을 통해서 정의 할 수 있다.
(예: [SwaggerResponse(HttpStatusCode.OK)], [SwaggerResponse(HttpStatusCode.NotFound)])

Restful API를 개발하는 사람이라면 시각화가 되어 있지 않은 API의 각종 정보들을 다른 사람에게 전달하기 정말 까다로운 일이라는 것을 알고 있을 것이다. 이런 고충을 Swagger Framework을 통해서 해소 할 수가 있다. 

Azure의 APP API서비스는 Swagger Framework을 기본으로 제공해 주고 있어서 Azure에서 API를 개발 할 때, 몇가지 정의 방법으로 인터페이스 설명 및 기본 설정을 하면, Swagger.UI에서 보기 좋게 문서화 해서 Web으로 잘 보여 준다. 그래서 개발자가 문서 작업때문에 시간들일 필요도, 머리 아플일도 전혀 없다. 그리고 표준 Rest API 정의에 대한 표현을 Swagger에서 정말 잘 다루고 있기 때문에 어느정도 Rest API에 대한 인식이 있다면 API를 제공받은 사람 입장에서도 서비스에 대한 정보를 별다른 노력 없이 쉽게 인식할 수 있다.