Blazor와 C#으로 풀스택 웹 개발하기

Blazor — Full-stack web development with C# and WebAssembly

웹 어셈블리는 웹 프론트엔드 관련 기술들 중에서도 가장 넓은 범용성을 발휘하는, 비교적 최근에 태동한 새로운 기술입니다. 계속해서 우리나라 안에서는 논란이 되고 있는 ActiveX 기술이나 Java Virtual Machine이나 Adobe Flash 같은 Rich Internet Application, 혹은 Google의 NaCl (Native Client) 같은 기술을 연상하게 하는 측면도 있습니다.

그렇지만 이전과 달리 웹 어셈블리는 Microsoft, Google, Firefox, Apple 등 업체를 가리지 않고 모두가 공통적인 스펙을 지원하도록 생태계가 만들어지고 있으며, 이전처럼 특정 업체가 본인들의 이상향만을 무리하게 밀어넣지 않도록 한다는 점에 있어서 다르다고 할 수 있습니다.

그 와중에, 업계에서 유의미한 수준으로 웹 어셈블리를 다룰 수 있도록 해주는 기술 세트가 새롭게 발표되었으며, 개인적으로는 여기에 많은 관심을 두기 시작하였습니다. 바로 Blazor (블레이저)라는 새로운 웹 프레임워크입니다.

Blazor는 Microsoft가 오픈 소스로 공개한 웹 어셈블리 기반 웹 프레임워크 기술 집합입니다.

Blazor는 무엇인가

Blazor는 .NET Core의 프로젝트 빌드 시스템을 활용하여, Mono 런타임과 여러분이 참조하게 될 각종 라이브러리, 그리고 여러분의 웹 UI 코드와 C# 코드를 한 데 묶어 브라우저에서 직접 실행 가능하게 만들어줍니다.

이 때, 빠른 전송 속도를 유지할 수 있도록 꼭 필요한 최종 실행 코드만을 남기도록 최적화를 해주기 때문에, 여러분이 알고 있는 무거운 닷넷 프레임워크 기반 애플리케이션과는 양상이 다릅니다.

말로만 들어보면 굉장히 그럴싸해보입니다. 그런데 한 가지 궁금한 점이 생깁니다.

실버라이트에 비해서 무엇이 달라진 것일까?

실버라이트와 같이 C#을 사용하고, 닷넷 런타임을 제공하기 때문에 꽤나 비슷해 보입니다. 그러나 블레이저는 실버라이트가 아닙니다.

브라우저가 웹 어셈블리에 대한 처리가 가능하다면, 실버라이트처럼 별도의 ActiveX 컨트롤 없이 오로지 브라우저의 처리 능력에만 기대어 실행합니다. 그리고 이 웹 어셈블리는 여러 최신 브라우저들이 예외없이 모두 지원하는 정식 기술입니다.

그리고 UI를 만들기 위해서 복잡한 IDE나 디자인 도구를 이용하지 않고, HTML 태그와 C# 코드만을 이용하여 코드를 작성하기 때문에, XAML이라는 새로운 수단을 배워야 했던 이전과는 다릅니다.

여기에 더하여 ASP.NET MVC의 Razor 뷰 문법을 기반으로 하기 때문에 뷰에서 직접 조건문이나 반복문을 즉석에서 사용하거나, 클래스/변수 정의를 바로 할 수 있습니다.

마지막으로 블레이저는 데스크톱 뿐만 아니라 모바일도 지원합니다. 웹 어셈블리를 지원하는 모든 모바일 브라우저에서 사용이 가능합니다. (참고 — https://caniuse.com/#search=webassembly, 2018년 8월 현재 iOS 사파리, Android Chrome에서 WebAssembly 실행이 지원됩니다.)

이미 업계에서 C#은 Windows Forms, WPF와 같은 오래된 기술 뿐 아니라, 최근 Windows 10의 등장과 함께 계속해서 진화해나가고 있는 UWP, 화제의 Android/iOS 동시 지원 UI 프레임워크인 Xamarin, 모바일 게임 개발의 대표 주자인 Unity 등 쓰이지 않는 곳이 없는 전천후 프로그래밍 언어가 되었습니다.

이러한 C#을 이용하여 이제 React나 NodeJS에서 다루던 프론트엔드를 다룰 수 있다면 이 만한 이상향도 없을 것입니다.

아직 갈길이 멀고, 실험적인 성격이 짙으며, 지금 프로덕션에 도입하기에는 무리가 있음에도, 블레이저가 많은 사람들의 주목을 받는 것은 이 때문입니다.

Blazor 맛보기

https://blazor-demo.github.io/ 에 접속하면 실제로 컴파일된 Blazor 앱을 실행해보실 수 있습니다.

Blazor 샘플 앱의 동작 예시, 화면 우측의 Network 탭의 결과에 주목해주세요.

화면 우측의 Network 탭에서 보여지듯이, mono.wasm 파일을 시작으로 우리가 기존에 알던 닷넷 어셈블리 코드들을 그대로 가져오는 것을 볼 수 있습니다. 즉, 웹 어셈블리를 이용하여 Mono 런타임을 구현하고, 나머지는 기존의 MSIL 코드를 그대로 이용하는 것입니다.

그리고 그 후에는, 자바스크립트로 처리하던 일들을 C#으로 작성하여 컴파일한 MSIL 코드들이 대신 처리하게 됩니다.

이 과정들을 요약한 아키텍처 이미지는 다음과 같습니다.

Blazor의 아키텍처

또한 핵심 런타임에 해당하는 mono.wasm 파일과 다른 클래스 라이브러리들은 GZIP 압축과 캐싱 정책을 이용하여 최초 한 번만 다운로드를 받게 되므로 속도와 데이터 전송량에 대한 걱정도 조금 덜 수 있습니다. 물론 더 개선될 여지가 많이 있습니다.

Blazor로 첫 애플리케이션 만들어보기

C#을 다루는 많은 개발자가 그러하듯, 저는 Windows를 기본 OS로 사용하여 개발을 진행합니다. 그래서 이 아티클에서도 일단은 Visual Studio 2017과 Windows 10을 기준으로 설명합니다.

하지만, Visual Studio로 할 수 있는 개발과는 별개로 지금 만들 코드는 macOS, Linux, 그리고 Docker에서도 실행할 수 있는 엄연한 크로스플랫폼 코드입니다.

Blazor를 이용하여 프론트엔드 코드를 작성하기 위해서는 최소한 다음의 구성 요소가 필요합니다.

  • .NET Core 2.1 SDK — http://dot.net 에서 설치할 수 있습니다. macOS, Windows, Linux를 모두 지원합니다.
  • Blazor Project Template — dotnet new -i Microsoft.AspNetCore.Blazor.Templates 명령으로 템플릿을 닷넷 코어에 설치할 수 있습니다.

그리고 Visual Studio 2017의 경우에는 다음의 구성 요소가 필요합니다.

모든 설치가 끝나면, 커맨드라인에서는 아래와 같이 새 프로젝트를 만들 수 있습니다.

dotnet new blazor -n MyBlazorTest

또는 Visual Studio 2017을 실행하고, 다음과 같이 새 ASP.NET 코어 웹 프로젝트를 만들도록 합니다.

새 ASP.NET Core 웹 애플리케이션 생성

이어서 나타나는 템플릿 선택 대화 상자에서는 Blazor 템플릿을 선택합니다.

Blazor 템플릿을 선택하고, OK 버튼을 클릭합니다.

프로젝트를 새로 만든 후 결과물을 살펴보면, Pages 폴더에는 우리가 아는 실제 페이지들의 CSHTML 코드가, 그리고 Shared 폴더에는 레이아웃과 함께 여러 페이지에서 공통으로 쓰일 수 있을만한 컴포넌트들이 들어있습니다.

프로젝트를 만들고나서 구성을 살펴보면 그렇게 복잡하지 않은 구성을 가지고 있고, 한눈에 내용이 파악되실 겁니다.

Blazor 프로젝트의 구성. 기본은 ASP.NET Core 프로젝트입니다.

실제 Blazor 애플리케이션을 실행해보면 Index.cshtml 파일의 내용이 화면에표시되는 것을 볼 수 있을 것입니다. 아래 코드를 보면 / 경로에 대해 Index.cshtml 이 표시되도록 @page 지시자를 사용했기 때문입니다.

@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />

그리고 SurveyPrompt 라는 태그는 Shared 디렉터리의 SurveyPrompt.cshtml 이라는 컴포넌트를 가져와서 사용한 것인데, 이 컴포넌트는 결국 프로젝트 상단의 _ViewImports.cshtml 에서 가져오도록 설정한 네임스페이스 안에 포함되어있는 것입니다. 그리고 네임스페이스의 규칙은 프로젝트 내의 폴더 경로들과 같습니다.

그리고 정말 중요한 내용을 가지고 있는 샘플이 또 있습니다. Counter.cshtml 파일의 내용을 열어보면 다음과 같이 만들어져있습니다.

@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" onclick="@IncrementCount">Click me</button>
@functions {
int currentCount = 0;
void IncrementCount() {
currentCount++;
}
}

버튼을 클릭했을 때 카운트 값을 올리기 위한 동작을 구현하고 있는 것을 볼 수 있습니다. 흥미로운 것은, 버튼에 대한 클릭 이벤트 구현을 담고 있는 메서드의 이름을 @ 지시자로 지정하고 있는 부분인데, 문법만 맞으면 마치 C#에서 대리자를 사용하던 것과 같이 함수의 이름을 지정하거나, 아예 저 부분에 람다식을 대입할 수도 있습니다.

그리고 currentCount 라는 변수에 별다른 장치가 없으며, 그저 뷰에서 @currentCount 로 연결짓기만 했을 뿐입니다. 값이 동적으로 변경되는 것을 보기 위해서 뭔가 더 해주어야 할 것 같지만, 이 부분에 대한 바인딩 처리를 Blazor가 모두 대행해줍니다. 즉, 코드가 매우 간결해지게 됩니다.

좀 더 복잡하고 실용적인 예제를 보고 싶으시다면, 이어서 FetchData.cshtml 파일에 네트워크 통신, 비동기 처리, 의존성 주입, 그리고 데이터 반복문 처리에 대한 내용이 모두 들어있습니다.

@inject HttpClient Http

아쉽게도 Blazor는 엄연히 브라우저의 샌드 박스 안에서 실행되기 때문에, 주어지는 자유도에는 걸맞지 않게 웹 브라우저가 제공하는 딱 그만큼의 네트워크 기능, 즉 HTTP/HTTPS 통신만을 지원합니다. 그래서 HttpClient (맞습니다. 닷넷 프레임워크의 그 HttpClient 입니다.) 를 이용하여 통신을 시도하게 됩니다.

protected override async Task OnInitAsync() {
forecasts = await Http.GetJsonAsync<WeatherForecast[]>("sample-data/weather.json");
}

컴포넌트가 시작하자마자 수행할 작업은 OnInitAsync 라는 추상 함수를 재정의하여 만들 수 있습니다.

프로젝트의 코드 구조와는 별개로 브라우저 입장에서 상대 경로를 사용하게 되므로, wwwroot 디렉터리 내부의 sample-data/weather.json 파일의 내용을 마치 API 호출 결과처럼 다운로드하여 사용하게 됩니다. 그리고 asyncawait 키워드를 사용하여 비동기 작업을 처리합니다.

좀 더 자세히 프로젝트 살펴보기

보시다시피 Blazor 프로젝트의 기본 뼈대는 ASP.NET Core 프로젝트입니다. 하지만 혼동하지 말아야 할 것은, 앞에서도 이야기하였듯이 Blazor는 C#을 사용하고 있지만 서버 사이드 프로젝트가 아니라는 점입니다.

wwwroot 디렉터리는 Blazor의 실행에 필요한 웹 어셈블리 코드를 띄워주는 정적 페이지들을 담고 있는 디렉터리입니다. Blazor의 결과물이 만들어지면 웹 콘텐츠를 정상적으로 제공할 수 있는 어떤 종류의 서버이든 Blazor 애플리케이션을 제공할 수 있습니다. 그리고 index.html 파일에는 여러분이 선호하는 자바스크립트나 타입스크립트 라이브러리를 추가할 수 있습니다.

그리고 Startup.cs 가 Blazor 응용프로그램의 기본 시작점이 됩니다. Program.cs 에 들어있는 Main 메서드는 ASP.NET Core를 위한 코드입니다.

프로젝트 루트에 들어있는 _ViewImports.cshtml 파일은 같은 디렉터리에 속하는 다른 CSHTML 파일들에 자동으로 포함시킬 네임스페이스 가져오기 구문들 또는 미리 공통으로 포함시킬 컴포넌트를 지정할 수 있도록 되어있습니다. 주로 여기에 마스터 레이아웃 같이 공통적으로 쓰이는 구성 요소를 배치한다고 보면 되겠습니다.

Blazor의 진짜 힘 — 공유 프로젝트와 NuGet 패키지

너무 당연한 부분이라 언급할 필요가 없을진 모르겠지만, 기존에 가지고 있던 C# 프로젝트의 모델 클래스들을 담아놓은 공유 프로젝트가 있다면 당연히 Blazor에서 가져와서 쓸 수 있습니다.

그리고 NuGet 패키지가 특정 시스템이나 버전에 종속된 것이 아니고, 닷넷 스탠다드나 모노를 기반으로 하는 경우에는 충분히 재사용할 수 있습니다. 단, 아직은 개발 중인 기능이라 약간의 조치가 더 필요하긴 합니다.

이해를 돕기 위한 예를 들면, Blazor의 기본 JSON 처리 기능 대신 잘 알려진 Newtonsoft JSON 라이브러리를 대신 사용하기로 했다고 가정해보겠습니다. 일단 Blazor 프로젝트에 NuGet 라이브러리를 추가한 후, Index.cshtml 페이지에 다음과 같은 코드를 추가해보겠습니다.

<p>@(Newtonsoft.Json.JsonConvert.SerializeObject(new { name = "Paul", age = 15 }))</p>

그리고 앞서 이야기한대로 약간의 조치가 필요합니다. 프로젝트 csproj 파일을 편집하는 메뉴를 클릭하여 텍스트 에디터를 엽니다. (참고: .NET Core 계통 프로젝트들은 Visual Studio 솔루션 탐색기에서 직접 텍스트로 편집할 수 있는 기능을 제공합니다.)

그리고 아래의 코드 블록을 Project 태그 하위 레벨에 추가하도록 합니다.

<ItemGroup>
<BlazorLinkerDescriptor Include="Linker.xml" />
</ItemGroup>

이어서 Linker.xml 파일의 내용은 다음과 같이 추가합니다. 단, 프로젝트 이름 부분만 여러분의 프로젝트 이름을 대신 넣어주도록 합니다.

<?xml version="1.0" encoding="utf-8" ?>
<linker>
<assembly fullname="mscorlib">
<!-- Preserve all methods on WasmRuntime, because these are called by JS-side code to implement timers. Fixes https://github.com/aspnet/Blazor/issues/239 -->
<type fullname="System.Threading.WasmRuntime" />
</assembly>
<assembly fullname="System.Core">
<!-- This is required by JSon.NET and any expression.Compile caller -->
<type fullname="System.Linq.Expressions*" />
</assembly>
<!-- Name of the entry point assembly -->
<assembly fullname="프로젝트 이름" />
</linker>

이렇게 하고 프로젝트를 빌드 후 실행해보면 정말 Newton JSON 라이브러리를 브라우저에서 복잡한 과정 없이 그대로 가져다 쓸 수 있다는 것을 확인하실 수 있습니다.

Newtonsoft JSON 라이브러리를 이용하여 JSON Serialization 한 결과를 표시

더 알아보기

Blazor에 대한 관심도가 높기 때문에, 생각보다 다양한 곳에서 의미있는 리소스를 쉽게 찾을 수 있습니다. 저의 관심사 위주로 몇 가지 리소스들에 대한 언급을 추가해보았습니다.

그리고 저는 최근에 Korea Blazor User Group을 새로 만들었습니다. 새로운 웹 프레임워크에 대한 호기심, 그리고 기존에 알고 있던 C#과 닷넷의 새로운 모습을 보고 싶으신 분들 모두 환영합니다.

Korea Blazor User Group 바로 가기: https://www.facebook.com/groups/kblug/

Like what you read? Give 남정현 a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.