대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 ASP.NET 리부트 - ASP.NET 5 개발의 변화
등록일 조회수 7462
첨부파일  

ASP.NET 리부트

ASP.NET 5 개발의 변화



MVC와 Web API가 ASP.NET MVC 버전 6에서 통합됐다. 또한, 태그 헬퍼(Tag Helper)가 새롭게 도입되어 HTML의 읽기 흐름을 거스르는 HTML 헬퍼 대신 깔끔하고 직관적인 HTML 코딩이 가능해졌다. 애플리케이션 설계와 테스트 관점에서 모두 중요한 종속성 주입은 이제 MVC 프레임워크에서 기본적으로 지원한다.



첫 번째 글에서는 플랫폼의 변화를, 두 번째 글에서는 솔루션 구조에 대한 변화를 알아봤다. 이번 호에서는 개발자들이 흥미로워할 ASP.NET 5 개발 측면에서의 변화를 소개한다.



1. ASP.NET MVC 6

마이크로소프트(MS)는 제품의 영향력에 비해 브랜딩이 취약하다는 평을 듣는다. ASP.NET 5에 대해서도 브랜딩에 대한 개발자들의 불평이 많다. MVC 6도 그 밋밋한 이름에 비해 많은 변화가 있다. 이중, 새로운 개념에 대해서 알아본다.

1) Web API 통합
ASP.NET의 개발 모델 통합계획에는 원래 Web Pages도 포함되어 있었다고 한다. 이번 MVC 6 릴리즈에서는 Web API만 통합이 이루어지지만 MVC의 마이너 릴리즈 즈음에 Web Pages도 통합될 것으로 예상된다. MVC 6는 결국 Web API 3와 Web Pages 4를 포함하는 새로운 통합 모델이 될 것이다. 물론, Web API 3와 Web Pages 4 라는 버전은 차기 버전을 지칭하려는 의도일 뿐 실재하지 않는다.

MVC가 처음 소개될 당시에는 웹 폼(Web Forms) 개발에 영향을 끼치지 않는 것이 목표였다고 한다. MVC는 새로운 개발 모델이지만 기존 모델(웹 폼)과 ASP.NET 파이프라인 등 기초적인 기반을 공유하기 때문에 새 모델로 인한 System.Web의 변경은 조심스러울 수 밖에 없었다.

MVC 등장 2년 후에는 간단한 웹 애플리케이션을 빠르게 작성하고자 하는 목적의 Web Pages라는 개발 모델이 등장했다. MVC 버전 3에서 새롭게 소개된 Razor 뷰 엔진 덕분이다. 이 새로운 엔진은 정적인 HTML 페이지에 닷넷 프로그래밍 언어를 사용해 동적인 내용을 추가할 수 있는 기술로써 뷰에 서버 코드를 삽입할 수 있게 해준다. 과거에 클래식 ASP 페이지를 작성해본 경험이 있다면 그 것과 유사한 방식이라고 이해하면 된다. 이런 개발 모델은 하나의 웹 페이지에서 HTML, CSS, JavaScript와 서버 코드를 사용하기 때문에 초보자가 이해하기 쉽고, 그 단순함 덕에 MVC로의 전환도 쉽다.

스마트폰, 태블릿의 확산으로 모바일 기기가 대중화되고 백엔드 서비스에 대한 수요가 증가하면서 UI 없이 데이터만 서비스하는 Web API가 등장한다. MVC 컨트롤러를 사용해서도 API 제공이 가능하지만 Web API 라는 다른 이름으로 각자의 길을 가게 된 역사적인 배경에는 호스팅 문제가 있었다.

API는 주목 받는 클라우드 환경 등을 고려해, 웹 서버에 독립적인 셀프 호스팅 시나리오를 지원해야 했다. 반면, MVC와 웹 폼을 포함하는 ASP.NET 프레임워크는 요청 처리와 인증 등의 문제로 IIS와 강하게 묶여 있었고 이런 프레임워크로는 셀프 호스팅 시나리오를 구현할 수 없었다. 따라서, MVC 프레임워크의 아키텍트와 상당 부분이 겹치는 중복을 감수하면서도 새로운 기반으로 Web API가 탄생한다. Web API는 닷넷 매니지드 코드로 개발된 모든 프로그램에서 호스팅할 수 있도록 설계됐다.



MVC와 Web API가 같은 개념을 공유하면서도 MVC에서 개발한 필터를 Web API에 사용할 수 없다는 것은 상당히 아쉬운 문제였다. 한 프로젝트에서 MVC와 Web API를 함께 사용할 때, 라우트 설정을 각기 해야하고 종속성 주입도 서로 다른 장소에서 해야하는 것은 프로젝트의 구조를 복잡하게 만드는 요인이었다.

또한, MS 입장에서도 기능을 제공함에 있어 MVC와 Web API를 각각 지원하기 위해 두 번 구현해야 하는 비효율적인 문제도 있었기 때문에 이번 통합은 개선이 필요한 부분이었다.

2) MVC는 미들웨어
1회 연재에서 잠깐 다루었지만 MVC는 애플리케이션 역할을 하는 미들웨어다. 여기서 애플리케이션의 의미는 최종 응답을 처리하는 역할을 맡기 때문이다. 또한, MVC는 이제 일종의 종속성으로 별도 설치가 필요한 NuGet 패키지이다. MVC를 사용하기 위해서는 project.json 파일에서 MVC에 대한 종속성을 선언함으로써 NuGet 패키지를 설치하고, Startup 클래스에서 미들웨어로 구성해야 한다.



<리스트 1> project.json 파일에 MVC 종속성 추가 "dependencies": { "Microsoft.AspNet.Server.IIS": "1.0.0-beta4", "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4", "Microsoft.AspNet.Mvc": "6.0.0-beta4" }



<리스트 2> Startup.cs 파일에서 MVC를 사용하는 미들웨어 구성 public void ConfigureServices(IServiceCollection services) { services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory) { app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); }); }



Startup.cs 파일에 있는 두 개의 메서드는 Startup 클래스의 핵심 메서드들로 런타임에 의해 ConfigureServices, Configure 순서대로 호출된다. ConfigureServices 메서드에서 필요한 서비스를 추가해 두고 Configure 메서드에서 사용 관련 설정을 한다. 설정을 마쳤으면, MVC 컨벤션에 따라 컨트롤러, 모델, 뷰를 추가하는 것으로 기존처럼 MVC 애플리케이션을 작성할 수 있다.



2. TagHelpers

TagHelpers는 이전 버전의 HtmlHelpers와 비교되는데 둘 다 HTML을 동적으로 생성하는 것을 목표로 한다. HTML을 동적으로 생성해야 하는 상황은 조회 데이터를 출력 한다거나 반복되는 HTML 마크업을 효율적으로 처리하기 위한 경우가 많다. 기능상으로 봤을 때, 웹 폼의 서버 컨트롤과 유사하다. 두 가지 헬퍼 모두 사용목적과 결과가 같지만 그 사용 방법은 클라이언트 개발자 또는 웹 디자이너의 관점에서 TagHelpers가 훨씬 우아하다.



<리스트 6> 하이퍼링크를 표현하는 두 가지 방법 [HtmlHelpers] @Html.ActionLink("Back Home", "Index", "Home", new { @class = “linkstyle” }) [TagHelpers] < a asp-action="Index" asp-controller="Home" class=”linkstyle”>Back Home< /a> [결과] < a href="/" class=”linkstyle”>Back Home< /a>



<리스트 6>에서는 동일한 결과를 만드는 두 가지 헬퍼를 비교하고 있다. HtmlHelpers를 사용한 문장은 웹 디자이너들이 직관적으로 이해하기 어렵다는 것이 문제였다. HTML 속성을 설정하기 위해 익명 객체를 사용해야 하고 게다가 C#과 키워드가 겹치는 경우에는 @를 사용해서 escape 처리까지 해야하니 웹 디자이너들에게는 외계어처럼 보였을 것이 분명하다.

TagHelpers를 사용하면 AngularJS와 유사하게 asp- 접두어로 시작하는 지시어를 HTML에 직접 삽입할 수 있다. Razor 뷰 엔진은 해당 HTML 요소에 연관된 TagHelpers가 있는지 조사해서 그 태그에만 특화된 처리를 한다. TagHelpers도 MVC를 추가했던 것과 마찬가지로 종속성으로 프로젝트에 추가해야 한다. project.json 파일의 dependencies 영역에 다음 한 줄을 추가하면 된다.

"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-beta4"

패키지가 설치되면 아래에 열거한 TagHelpers를 사용할 수 있다. 한편, TagHelper 클래스를 상속하는 것으로 사용자 정의 태그 헬퍼도 만들 수 있다는 것을 기억하자.

- Anchor (하이퍼 링크 생성)
- Cache (부분 페이지 캐싱 관리)
- Environment (실행 환경에 기반하여 컨텐트 조정)
- Form (폼 요소 생성)
- Input (폼의 입력 요소 생성)
- Label (라벨 요소 생성)
- Link (외부 스크립트, CSS 파일등을 참조하는 링크 처리)
- Option (드롭다운리스트에서 각각의 선택 항목이 대상)
- Script (스크립트 태그 처리)
- Select (드롭다운리스트 생성)
- TextArea (textarea 태그 처리)
- ValidationMessage (개별 항목에 대한 유효성 검사 에러 메시지 생성)
- ValidationSummary (유효성 검사의 전체 결과 메시지 생성)

asp- 접두어로 시작하는 태그 헬퍼들을 쉽게 사용하려면 종속성으로 다음 줄을 추가하여 비주얼스튜디오에서 인텔리센스를 사용하자.

"Microsoft.AspNet.Tooling.Razor": "1.0.0-beta4“

TagHelpers는 뷰에서 선택적으로 사용하는 기술이기 때문에 @addTagHelper라는 지시어로 TagHelpers의 사용을 뷰에 명시적으로 선언해야 한다. TagHelpers를 글로벌하게 사용하려면ViewsShared\_ViewImports.csthml 파일에 선언할 수도 있다.

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers“
TagHelpers를 사용한다고 선언하면서 와일드카드(*)를 사용했다. TagHelpers가 제공하는 모든 태그 기능을 사용한다는 의미이다. 특정 헬퍼를 사용하지 않으려면 아래와 같이 제거할 수도 있다.

@removeTagHelper"Microsoft.AspNet.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNet.Mvc.TagHelpers“

이제 인텔리센스의 도움을 받아 TagHelpers 를 마치 HTML 마크업처럼 사용할 수 있게 되었다.



TagHelper를 사용하면 깔끔한 HTML을 유지할 수 있어 웹 디자이너와의 협업에 도움이 될 뿐만 아니라 웹 폼 스타일의 사용자 컨트롤도 작성할 수 있기 때문에 개발자들에게 큰 인기를 끌 것 같다.



3. 종속성 주입 (Dependency Injection)

IT 업계에서 변하지 않는 유일한 것이 있다면 변화 그 자체라는 말이 있다. 그럼에도 불구하고 변화에 유연하게 대처할 수 있는 애플리케이션을 작성하는 일은 쉬운 일이 아니다. 객체지향적으로 클래스가 잘 설계되었다 해도 한 클래스에서 다른 클래스의 인스턴스를 생성하는 방식은 클래스간의 의존성을 만들기 때문에 부분적인 변경이라도 전체적인 영향을 항상 검토해야 한다. 따라서, 클래스 간의 결합은 느슨해야 하고 이렇게 결합력이 약한 구조는 변경에 유연하게 대처할 수 있다.

‘Program to interface, not implementation’ 라는 중요한 설계 원칙이 있다. 인터페이스에 주목해야하는데 인터페이스는 일종의 계약이다. 계약은 지켜져야 하고 합의 후 변경할 수 없다.

이렇게 계약에 기반하여 클래스를 개발하는 것을 인터페이스를 구현한다고 하고, 인터페이스를 사용하는 코드는 인터페이스에 할당되는 클래스를 바꾸는 것만으로 애플리케이션의 변경 효과를 가져올 수 있다. 아래의 예제 코드는 주문과 함께 로깅이 필요한 시나리오에서 로깅 모듈이 어떻게 주문 모듈과 결합되어 있는지 보여준다. 종속성 주입 컨테이너에 대해서는 나중에 설명한다.

코드에 포함되지는 않았지만 Save 라는 메서드를 정의하는 간단한 ILogger 인터페이스가 있고 이를 구현하고 있는 XmlLogger가 사용되었다. 만약, 로깅을 위해 데이터베이스가 필요하다고 판단되면 ILogger를 구현하는 DatabaseLogger 클래스를 만들어서 XmlLogger 대신 바인딩 하면 그만이다. 이런 변경은 로깅을 사용하는 상위 모듈, 즉 Order 클래스를 변경하지 않고 가능해야 좋은 설계라고 할 수 있다.



<리스트 7> 주문시 로깅처리하는 예제 // 종속성 주입 컨테이너 준비 IKernel ninjectKernel = new StandardKernel(); // ILogger 인터페이스에 XmlLogger를 사용하도록 지정 ninjectKernel.Bind< ILogger>().To< XmlLogger>(); // 종속성 주입 컨테이너가 바인딩 설정에 기반해서 클래스를 생성하고 // ILogger 형식 변수에 클래스를 할당한다. ILogger logger = ninjectKernel.Get< ILogger>(); // 주문 클래스를 생성하면서 종속성을 주입한다. Order o = new Order(logger); // 주문 후 처리 결과는 주입된 logger에 의해 기록된다. o.Place();



상위 모듈은 하위 모듈을 사용하면서 어쩔 수 없이 의존성을 갖게 되지만 하위 모듈의 변경이 상위 모듈의 변경으로 이어지는 것은 유지보수를 어렵게 만든다. 이 것이 모듈들을 느슨하게 결합해야 하는 이유이다. 종속성은 비즈니스 로직(여기서는 주문)과 상관없는 실행환경에서 관리되어야 한다. 만약, Order 클래스에서 XmlLogger를 직접 사용한다면 강한 결합이 되어 로깅 모듈 수정시 Order 클래스에 대한 수정을 피할 수 없다. 다시 말해, 상위 모듈이 영향을 받는 셈이다. Order 클래스의 생성자를 통해 로깅 모듈을 주입하면 외부에서 로깅 모듈을 제어하는 셈이 된다. 이 때 주입하는 것은 인터페이스인데, 이 인터페이스에 어떤 클래스를 붙여 주는가 하는 것이 종속성 주입 컨테이너의 역할이고 리스트 7의 초반부 설정에 표현되어 있다. 느슨하게 갖고 있는 의존성은 테스트에도 도움이 된다. 주문 과정을 테스트하는데 로깅은 부차적인 요소이다. 외부환경에 영향을 받는 파일이나 데이터베이스 대신 콘솔에 로깅하는 Mock 클래스를 만들어 테스트에 사용하면 편리하다.

종속성을??용한 Ninject 또는 Autofac 등이 많이 사용되며 MS에서도 Unity Container를 NuGet 패키지로 제공해 왔다.

MVC 6에 들어서 별도 패키지로 설치해야 했던 종속성 주입 컨테이너가 프레임워크에 빌트인으로 통합되었다. 그렇다고 이 빌트인 컨테이너가 강제화된 것은 아니다. MVC 6는 개발자의 편의를 위해 별도 설치 없이 프레임워크 차원에서 최소 기능의 종속성 주입 컨테이너를 제공할 뿐이다. 보다 강력한 기능이 필요하거나 복잡한 시나리오에서는 선호하는 종속성 주입 컨테이너를 사용하면 된다. 리스트 8은 MVC 6에서 종속성을 주입하는 방법을 보여주고 있다.



<리스트 8> MVC 6 프레임워크를 사용한 종속성 주입 public class Startup { public void Configure(IBuilder app) { ... app.UseServices(services => { ... // 종속성 설정 services.AddTransient< ILogger, XmlLogger>(); ... }); ... } }



3회에 걸쳐 ASP.NET 5 라는 새로운 웹 개발 프레임워크를 소개했다. Ruby on Rails, Node.js, AngularJS 등 인기 있는 웹 개발 프레임워크에서 아이디어를 많이 차용한 것을 보면서 MS의 접근법에 큰 변화가 있었다는 것을 느낄 수 있었다.



출처 : 마이크로소프트웨어 10월호

제공 : 데이터 전문가 지식포털 DBguide.net