Blazor WebAssembly + Webpack + TypeScript + Sass 개발 환경 구축
Blazor 를 다른 환경과 통합하여 사용하는 예제를 찾을 수 없어, 직접 연구하여 공유합니다. Blazor 와 관련된 자료는 공식 문서 외에 Awesome Blazor 에서 다양하게 찾을 수 있습니다.
1. 프로젝트 생성
위 그림과 같이 Blazor WebAssembly App 프로젝트를 생성합니다.
생성하면 위 그림과 같은 결과를 얻을 수 있습니다.
현재 상태로 빌드해서 실행하면 위 그림과 같은 결과를 얻게 됩니다.
이제 여기서부터 하나하나 환경을 세팅해보도록 합시다.
2. Webpack 추가
node -v
npm -v
현재 프로젝트에 Webpack 을 추가하려면 Node.js 를 설치해야 합니다. 설치가 완료되었다면 Windows Terminal 에서 위 그림처럼 Node.js 와 npm 의 버전을 확인하여 설치가 완료되었는지 확인합니다.
webpack 5 는 Node.js 10.13.0 버전 이상이 필요합니다.
Terminal 의 경로를 위 그림과 같이 프로젝트 경로로 맞춥니다.
npm init
위 그림과 같이 npm init
을 실행하여 package.json
파일을 만듭니다.
npm install webpack webpack-cli webpack-dev-server --save-dev
위 명령어를 터미널에 입력하여 webpack, webpack-cli, webpack-dev-server 를 설치합니다.
위 그림처럼 package.json 파일에 webpack 으로 빌드하는 npm 명령어를 추가합니다.
const webpack = require("webpack");
const path = require("path");
const isDevelopment = process.env.NODE_ENV !== "production";
module.exports = {
mode: isDevelopment ? "development" : "production",
entry: [
"./index.js"
],
output: {
path: path.resolve(__dirname, "wwwroot"),
filename: "BlazorApp.js",
}
};
위 그림처럼 webpack.config.js 파일을 프로젝트에 추가하고 내용을 채웁니다.
그리고 index.js 파일을 위 그림처럼 추가합니다.
npm run build
Webpack 이 정상적으로 동작하는지 검증하기 위해 터미널에 npm run build
명령어를 입력하여, 에러가 없는지 확인합니다. 그리고 빌드 결과로 위 그림과 같이 BlazorApp.js 파일을 얻을 수 있습니다. 그러면 webpack 설치가 완료되었습니다.
위 단계를 진행하다 문제가 발생했을 시 관련 내용을 댓글로 알려주시면 글을 수정하거나 Troubleshooting 을 추가하겠습니다.
3. TypeScript 추가
npm install --save-dev typescript ts-loader
먼저 위 그림처럼 TypeScript 를 npm 으로 설치합니다.
위 그림처럼 프로젝트에 tsconfig.json 파일을 추가하고, 아래 내용을 작성합니다.
{
"compilerOptions": {
"baseUrl": "./", // 상대 경로의 base 폴더를 정한다. '.' 는 tsconfig.json 가 있는 폴더다.
"outDir": "wwwroot" // TS 가 컴파일한 결과가 저장될 경로다.
},
"exclude": [ "node_modules" ]
}
그리고 webpack.config.js 파일을 아래와 같이 수정합니다.
const webpack = require("webpack");
const path = require("path");
const isDevelopment = process.env.NODE_ENV !== "production";
module.exports = {
mode: isDevelopment ? "development" : "production",
entry: [
"./index.ts"
],
output: {
path: path.resolve(__dirname, "wwwroot"),
filename: "BlazorApp.js"
},
module: {
rules: [
// TypeScript
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: "ts-loader"
}
]
},
};
위 그림처럼 이제 entry
를 수정하고 rules
에 ts-loader
를 추가합니다.
위 그림처럼 index.js 파일의 확장자를 js
에서 ts
로 변경하고, 이번에 변경한 내용들이 잘 반영되는지 확인하기 위해 아래 그림처럼 index.ts 파일에 코드를 추가합니다.
그리고 빌드를 하면 아래와 같은 결과를 얻을 수 있습니다.
빌드된 BlazorApp.js 파일을 실행하고 로드하여 사용하려면, wwwroot 폴더의 index.html 에 다음 코드를 추가합니다.
<script src="BlazorApp.js"></script>
그리고 프로젝트를 실행하면 브라우저의 콘솔 창에서 Blazor
가 출력되어 있고, 브라우저의 소스 창에서 Blazor.js 파일을 확인하실 수 있습니다.
여기까지 TypeScript 를 Blazor WebAssembly 에 추가하여 사용하는 과정이었습니다. TypeScript 에 함수를 작성하고 활용하는건 공식 문서를 참고하시면 됩니다.
3. Sass 추가
이전 과정을 통해 Webpack 이 준비되어 있기 때문에 TypeScript 를 추가하는 과정과 마찬가지로 Sass 를 추가하면 됩니다.
먼저 관련 npm 패키지를 설치합니다.
npm install sass style-loader css-loader sass-loader --save-dev
그리고 TypeScript 때처럼 webpack.config.js 파일에 sass 파일을 해석하기 위한 loader 를 다음과 같이 추가합니다.
const webpack = require("webpack");
const path = require("path");
const isDevelopment = process.env.NODE_ENV !== "production";
module.exports = {
mode: isDevelopment ? "development" : "production",
entry: [
"./index.ts"
],
output: {
path: path.resolve(__dirname, "wwwroot"),
filename: "BlazorApp.js",
},
module: {
rules: [
// TypeScript
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: "ts-loader",
},
// Style
{
test: /\.s[ac]ss$/i,
exclude: /node_modules/,
use: [
"style-loader",
"css-loader",
"sass-loader",
],
},
]
},
};
이제 프로젝트에 index.scss 파일을 다음과 같이 추가합니다.
body {
background-color: yellow;
}
body
의 배경색을 눈에 띄는 yellow
로 지정해서 Sass 가 제대로 반영되었는지 확인할 수 있도록 index.scss 파일을 작성합니다.
그리고 index.ts 파일에 index.scss 파일을 import 합니다. 그래야 webpack 으로 bundling 할 때 index.scss 파일도 같이 번들링이 됩니다.
webpack 번들링에 추가하는 방법이 반드시 ts 파일에 scss 파일을 import 하는 방법만 있는건 아닙니다. scss 파일만 따로 모아 BlazorApp.css 파일로 만드는 방법도 있으나, 저는 번거롭다 판단하여 하나의 js 파일로 만들기 위해 현재 방법을 선택한 것 뿐입니다.
이제 터미널에서 npm run build 명령어를 수행하여 새로운 BlazorApp.js 파일을 얻고, 프로젝트를 실행하여 배경색이 노란색이 되었다면 Sass 도 프로젝트에 추가된 걸 확인하실 수 있습니다.
npm run build
여기까지 해서 Blazor WebAssembly 프로젝트에 Webpack + TypeScript + Sass 까지 추가했습니다. 그런데 여기까지만 하면 개발환경이 매우 불편합니다.
먼저 Visual Studio 에서 빌드를 해도 webpack 은 빌드되지 않아 BlazorApp.js 파일이 업데이트되지 않는 불편함이 있고, .NET 6 의 기능 중 하나인 Hot Reload 를 활용할 수 없어 개발 생산성이 떨어지는 불편함이 있습니다.
다음 단계인 빌드 프로세스 개선에서 이러한 불편함을 해결해보도록 합시다.
4. 빌드 프로세스 개선
먼저 Visual Studio 에서 프로젝트를 빌드하면 webpack 도 빌드하도록 csproj 파일을 수정해야 합니다. 수정하기 전 csproj 파일은 아래와 같이 작성되어 있습니다.
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
</ItemGroup>
</Project>
여기에 Running npm tasks when building a .NET project - Meziantou's blog 글을 참고하여 아래 코드를 추가합니다.
Thanks, Meziantou. You help me out with this particular problem with npm packaging.
<!--
https://www.meziantou.net/running-npm-tasks-when-building-a-dotnet-project.htm
1. npm 패키지를 설치한다. 'Incremental builds' 가 적용되어 있기 때문에 package.json 파일이 수정되어야 실행된다.
https://docs.microsoft.com/en-us/visualstudio/msbuild/incremental-builds?view=vs-2022
-->
<Target Name="NpmInstall" Inputs="package.json" Outputs="node_modules/.install-stamp">
<!-- https://devblogs.microsoft.com/nuget/enable-repeatable-package-restores-using-a-lock-file/ -->
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<Exec Command="npm ci" Condition="$(RestorePackagesWithLockFile) == 'true'" />
<Exec Command="npm install" Condition="$(RestorePackagesWithLockFile) != 'true'" />
<!-- 'Incremental builds' 가 되도록 stamp 파일을 업데이트한다. -->
<Touch Files="node_modules/.install-stamp" AlwaysCreate="true" />
</Target>
<!-- 2. 빌드를 시작하기 전에, webpack 으로 BlazorApp.js 파일을 만든다. -->
<Target Name="NpmRunBuild" DependsOnTargets="NpmInstall" BeforeTargets="BeforeBuild">
<Exec Command="npm run build" />
</Target>
위 코드를 추가하면 아래 그림과 같은 코드가 됩니다.
이제 BlazorApp.csproj 를 Rebuild 하면 아래 그림과 같이 Output Build 에서 읽기 매우 어렵지만 webpack --mode=production
명령어가 실행되고 빌드가 진행되는 걸 확인하실 수 있습니다.
혹시 읽기 편하게 만드는 방법을 알고 계신 분은 댓글로 알려주시면 감사하겠습니다.
이제 새로운 .NET 의 핵심 기술은 Hot Reload 를 적용하는 방법을 안내드리겠습니다. 설명에 앞서 아래와 같은 기술적인 한계가 있어 Hot Reload 기능과 함께 Debugging 기능이 조화롭게 동작하기는 어렵다는 점을 안내드립니다.
*In Visual Studio 2022 GA release Hot Reload support for Blazor WebAssembly when using the Visual Studio debugger isn’t enabled yet. You can still get Hot Reload If you start your app through Visual Studio without the debugger, and we are working to resolve this in the next Visual Studio update.
Update on .NET Hot Reload progress and Visual Studio 2022 Highlights 에 따르면 Blazor WebAssembly 프로젝트는 Visual Studio 2022 17.0.2 버전에서 debugger 를 사용한 Hot Reload 기능을 지원하지 않습니다. 그리고 webpack-dev-server 패키지를 활용해 ts 파일 또는 scss 파일의 Hot Reload 기능을 활용하기 위해 저는 .NET CLI 를 사용하는 방법을 선택했고 이를 소개하도록 하겠습니다.
지금까지 서론이었고 본론으로 돌어가 Hot Reload 기능을 적용하는 방법을 안내드리겠습니다. 먼저 아래 코드를 webpack.config.js 파일에 추가하여 webpack 의 DevServer 를 활성화합니다.
devtool: "inline-source-map",
devServer: {
static: {
directory: path.resolve(__dirname, "wwwroot"),
},
port: 9000,
historyApiFallback: true,
hot: true,
devMiddleware: {
writeToDisk: true,
}
}
그리고 package.json 의 scripts 에 "watch": "webpack serve"
을 추가합니다.
webpack 의 DevServer 가 동작하는지 확인하기 위해 아래 명령어를 터미널에 입력하고 결과를 관찰합니다.
npm run watch
위 그림과 성공적으로 동작했는지 확인한 뒤 index.ts 파일을 수정하면 아래와 같이 webpack DevServer 가 변경된 사항을 반영하여 다시 컴파일하는지 확인하면 됩니다.
위와 같은 결과를 얻었다면 webpack DevServer 가 정상적으로 동작한다는 걸 확인하실 수 있습니다.
.NET 의 Hot Reload 기능은 dotnet watch 라는 명령어를 통해 활성화할 수 있습니다. csproj 파일이 있는 경로에서 아래 명령어를 입력하면 Hot Reload 기능을 사용하실 수 있습니다.
아래 명령어가 실행되면 브라우저가 자동으로 실행되며, 해킹된 것이 아니니 놀라지 않으셔도 됩니다.
dotnet watch run
.NET Hot Reload 기능을 킨 상태로 razor 파일을 수정하면 아래와 같은 결과를 얻을 수 있습니다.
여기까지 정상적으로 동작하셨다면 .NET Hot Reload 기능도 활성화할 수 있는 프로젝트 세팅이 완료되었습니다.
Hot Reload 기능을 사용하려면 npm run watch
명령어와 dotnet watch run
명령어를 매번 터미널에 입력해야 하기 때문에 이를 해결하고자 저는 다음과 같은 PowerShell Script 를 작성하여 프로젝트 폴더에 Watch.ps1 파일로 저장하여 사용하고 있습니다.
wt --window 0 --focus -d . PowerShell -c { npm run watch }
wt --window 0 split-pane --focus -d . PowerShell -c { dotnet watch run }
5. 마치며...
Blazor WebAssembly 를 사용한다고 해서 JavaScript 의 거대한 생태계와 결별해야 하는건 아닙니다. 저는 아직 필요하지 않아 사용해본 적은 없지만, Blazor WebAssembly 에 React 컴포넌트를 사용하는 것도 이론적으로 가능합니다. 왜냐하면 Blazor WebAssembly 의 바탕이 되는 mono 런타임에 JavaScript 를 호출하는 코드 그리고 JavaScript 에서 C# 을 호출하는 코드가 있기 때문입니다. 이 기능을 사용하는 구체적인 방법은 공식 문서에서 소개하고 있으니 참고바랍니다. 다만 개발환경을 어떻게 생산성 높게 구성할지에 대한 연구가 필요합니다.
지금까지 구성한 개발환경에서 더 개선할 수 있는 부분이 있다면 댓글로 남겨주시길 부탁드립니다. 그래야 글을 수정하고 동일한 문제로 여러 개발자가 고생하지 않을 수 있습니다.
지금까지 읽어주셔서 감사합니다.
개발환경
글을 작성할 때의 개발 환경은 다음과 같습니다.
- Windows 10.0.19043
- Visual Studio Community 2022 (17.0.2)
- .NET 6.0.100
- Node.js v17.2.0
- npm 7.11.2
'Develop > Frontend 가이드' 카테고리의 다른 글
[Blazor] Sass 를 적용하는 방법 (2) | 2021.06.13 |
---|---|
[FE] webpack 에서 Source Map 이 동작하는 원리 (1) | 2021.05.30 |
[FE] Web API - WebSocket API (0) | 2021.05.30 |
[FE] Web API - Client-side storage : Service Worker API & Cache (0) | 2021.05.28 |
[FE] Web API - Client-side storage : IndexedDB (0) | 2021.05.12 |
꾸준히 노력하는 개발자 "김예건" 입니다.