[BlazorWebApp 시작하기] 01_프로젝트 생성부터 첫 화면이 출력되는 과정 이해하기
오늘 작업 목표
오늘은 Blazor로 웹 프로젝트를 처음 만들면서, 프로젝트가 실행되고 첫 화면이 브라우저에 출력되기까지의 흐름을 정리했다.
아직 로그인, 데이터베이스, 외부 API, Android WebView 연결까지 들어가지는 않았다.
이번 글의 목표는 딱 여기까지다.
Blazor Web App 프로젝트 생성
↓
렌더링 방식 선택
↓
Program.cs 이해
↓
App.razor 이해
↓
Routes.razor 이해
↓
Home.razor가 첫 화면으로 뜨는 이유 이해
처음에는 단순히 "웹 프로젝트 하나 만들면 되겠지"라고 생각했는데, 막상 Blazor 프로젝트를 만들려고 보니 선택지가 생각보다 많았다.
특히 아래 용어들이 처음부터 헷갈렸다.
Blazor Web App
Blazor Server
Blazor WebAssembly
Interactive Server
대화형 위치 - Global
이 부분을 모른 채로 넘어가면 나중에 프로젝트 구조를 이해하기 힘들 것 같아서, 이번에는 코드 작성보다 실행 흐름을 먼저 잡는 데 집중했다.
현재 상황
이번에 만들려는 웹은 단순한 연습용 웹만을 목표로 하지 않는다.
나중에는 기존에 구현되어 있는 Android 모바일 앱에 WebView 형태로 붙일 가능성이 있다.
전체적으로 생각하고 있는 구조는 이런 느낌이다.
기존 Android 앱
↓
WebView
↓
Blazor Web App
↓
ASP.NET Core 서버
↓
로그인 / 외부 회사 데이터 / DB / 업무 처리
이 웹에서는 나중에 다음 기능들이 들어갈 예정이다.
로그인
외부 회사 데이터 조회
데이터 처리
목록 / 상세 화면
검색 / 필터
권한 관리
Android WebView 연동
다만 처음부터 전부 만들면 구조가 너무 복잡해진다.
그래서 오늘은 가장 기본인 Blazor 프로젝트 생성과 실행 구조부터 확인했다.
Blazor Web Form이 맞는 표현인가?
처음에는 "C# Blazor Web Form" 같은 식으로 생각했는데, 이 표현은 정확하지 않다.
정확히는 아래에 가깝다.
ASP.NET Core Blazor Web App
여기서 Web Forms는 예전 ASP.NET Framework 시절의 기술이다.
Blazor에서도 폼을 만들 수는 있지만, 그때는 Web Forms라고 부르지 않고 Blazor의 EditForm 같은 컴포넌트를 사용한다.
그래서 새 프로젝트를 만들 때는 Blazor Web App을 선택하는 게 맞다.
이번 프로젝트도 Visual Studio에서 아래 템플릿으로 생성했다.
Blazor Web App
Blazor Web App, Server, WebAssembly 관계
처음에 가장 헷갈렸던 부분은 이거였다.
Blazor Web App이 있고
Blazor Server도 있고
Blazor WebAssembly도 있는데
이게 서로 다른 건가?
이 부분은 이렇게 이해하면 된다.
ASP.NET Core
↓
Blazor Web App
↓
Interactive Server
Interactive WebAssembly
Interactive Auto
예전에는 Blazor Server와 Blazor WebAssembly를 각각 다른 프로젝트 템플릿처럼 생각해야 했다.
하지만 현재 Blazor Web App 템플릿에서는 하나의 프로젝트 안에서 어떤 방식으로 동작할지 선택하는 구조에 가깝다.
이번에는 Interactive Server를 선택했다.
이유는 간단하다.
이번 프로젝트는 나중에 로그인, 외부 API, 데이터 처리, DB 접근 같은 서버 중심 기능이 많이 들어갈 가능성이 높다.
WebAssembly는 브라우저에서 C# 코드가 실행되는 방식이라 클라이언트 앱에 가까운 느낌이 강하다.
반면 Interactive Server는 C# 코드가 서버에서 실행되고, 브라우저는 서버와 연결된 상태에서 화면을 갱신한다.
이번 단계에서는 서버 쪽 구조를 먼저 이해하는 것이 더 적합하다고 판단했다.
프로젝트 생성 옵션
Visual Studio에서 Blazor Web App 프로젝트를 만들면서 대략 아래처럼 설정했다.
| 항목 | 선택 값 |
|---|---|
| 프로젝트 템플릿 | Blazor Web App |
| Framework | 설치된 최신 .NET LTS |
| Authentication | None |
| Interactive render mode | Server |
| Interactivity location | Global |
| HTTPS | 체크 |
아직 로그인 기능을 직접 만들지 않았기 때문에 Authentication은 None으로 두었다.
나중에 로그인 구조를 제대로 잡을 때 ASP.NET Core Identity, Cookie 인증, JWT 같은 선택지를 다시 비교할 예정이다.
대화형 위치 - Global은 무엇인가?
프로젝트 생성 중에 대화형 위치라는 옵션이 나왔다.
여기서 Global이라는 선택지가 있었는데 처음에는 이게 무슨 뜻인지 바로 감이 오지 않았다.
Blazor에서 "대화형"이라는 말은 사용자가 버튼을 누르거나 입력을 했을 때 C# 코드가 반응할 수 있는 상태라고 보면 된다.
예를 들어 HTML만 있다면 아래 버튼은 그냥 화면에 보이는 요소일 뿐이다.
<button>로그인</button>
하지만 Blazor에서 이 버튼에 C# 메서드를 연결하면 버튼 클릭 시 실제 코드가 실행된다.
<button @onclick="Login">로그인</button>
이런 동작이 가능하려면 해당 페이지나 컴포넌트가 Interactive 상태여야 한다.
Interactivity location은 이 Interactive 설정을 어디에 적용할지 정하는 옵션이다.
크게 보면 이런 차이다.
Global
↓
앱 전체에 Interactive 적용
Per page
↓
필요한 페이지마다 Interactive 적용
뉴스 페이지나 소개 페이지처럼 정적인 페이지가 많다면 페이지별로 설정하는 것이 효율적일 수 있다.
하지만 이번 프로젝트는 거의 모든 화면에서 버튼 클릭, 검색, 조회, 데이터 처리 같은 동작이 필요할 가능성이 높다.
그래서 처음에는 Global로 두는 것이 이해하기 쉽고 관리하기도 편하다고 판단했다.
나중에 프로젝트가 커지고 정적 페이지와 동적 페이지가 명확히 나뉘면 그때 페이지별 설정을 고려해도 될 것 같다.
프로젝트 실행 흐름
Visual Studio에서 실행 버튼을 누르면 단순히 브라우저만 열리는 것이 아니다.
실제로는 이런 과정이 일어난다.
Visual Studio 실행
↓
dotnet build
↓
C# 코드 컴파일
↓
ASP.NET Core 서버 실행
↓
브라우저 실행
↓
localhost 접속
↓
Blazor 화면 출력
여기서 중요한 점은 브라우저가 C# 프로그램을 직접 시작하는 것이 아니라는 점이다.
먼저 ASP.NET Core 서버 프로그램이 실행된다.
그 다음 브라우저가 그 서버에 접속해서 화면을 받아온다.
그래서 Blazor Web App은 단순한 HTML 파일 묶음이라기보다는 C#으로 작성된 웹 서버 애플리케이션에 가깝다.
생성된 기본 구조
프로젝트를 만들고 나면 대략 이런 구조가 보인다.
WebApp_forWebView
├── Components
│ ├── Layout
│ ├── Pages
│ ├── App.razor
│ └── Routes.razor
├── Properties
├── wwwroot
├── appsettings.json
├── Program.cs
└── WebApp_forWebView.csproj
프로젝트 이름은 환경마다 다를 수 있다.
중요한 파일은 아래 정도다.
| 파일 / 폴더 | 역할 |
|---|---|
| Program.cs | 웹 애플리케이션 시작점 |
| Components | Blazor 컴포넌트들이 들어가는 곳 |
| Components/App.razor | 전체 HTML 문서 구조 |
| Components/Routes.razor | URL에 따라 어떤 페이지를 보여줄지 결정 |
| Components/Pages | 실제 페이지 컴포넌트들이 들어가는 곳 |
| wwwroot | CSS, JS, 이미지 같은 정적 파일 |
| appsettings.json | 설정 파일 |
| .csproj | 프로젝트 설정 파일 |
처음에는 파일이 많아 보여서 복잡해 보였는데, 실행 흐름만 잡고 나니 생각보다 구조가 명확했다.
Program.cs
↓
App.razor
↓
Routes.razor
↓
Pages/Home.razor
이 흐름만 먼저 기억하면 된다.
Program.cs 확인
가장 먼저 본 파일은 Program.cs다.
C나 C++에서는 main() 함수가 프로그램의 시작점이다.
Java에서는 보통 아래처럼 시작한다.
public static void main(String[] args)
{
}
ASP.NET Core에서는 Program.cs가 그 역할을 한다.
이번 프로젝트에서 생성된 Program.cs는 아래와 같았다.
using WebApp_forWebView.Components;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
처음 보면 익숙하지 않은 문법이 많다.
그래도 흐름으로 보면 생각보다 단순하다.
웹 애플리케이션 만들 준비
↓
Blazor 컴포넌트 등록
↓
앱 객체 생성
↓
운영 환경 설정
↓
HTTPS, 보안, 정적 파일 설정
↓
App.razor를 시작 컴포넌트로 등록
↓
서버 실행
Program.cs 한 줄씩 이해하기
using
using WebApp_forWebView.Components;
Components 네임스페이스 안의 코드를 사용하겠다는 의미다.
C++의 #include와 완전히 같지는 않지만, 외부에 있는 타입을 현재 파일에서 쓸 수 있게 가져온다는 점에서는 비슷하게 느껴졌다.
여기서는 나중에 나오는 App 컴포넌트를 사용하기 위해 필요하다.
WebApplication.CreateBuilder
var builder = WebApplication.CreateBuilder(args);
웹 애플리케이션을 만들기 위한 준비 객체를 생성한다.
여기서 var는 C#에서 타입 추론을 의미한다.
C++의 auto와 느낌이 비슷하다.
auto number = 10;
C#에서는 이렇게 쓴다.
var number = 10;
다만 타입이 사라지는 것은 아니다.
컴파일러가 오른쪽 값을 보고 타입을 판단해준다.
AddRazorComponents
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
Blazor Razor 컴포넌트를 서비스에 등록한다.
그리고 Interactive Server 방식으로 동작할 수 있게 설정한다.
여기서 Services는 나중에 의존성 주입(DI, Dependency Injection)을 배울 때 자주 나오게 될 부분이다.
지금은 이렇게 이해했다.
이 앱에서 사용할 기능들을 미리 등록하는 공간
Build
var app = builder.Build();
지금까지 준비한 설정을 바탕으로 실제 웹 애플리케이션 객체를 만든다.
builder는 만들기 전 준비 단계이고, app은 실제로 실행될 애플리케이션에 가깝다.
개발 환경과 운영 환경 구분
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
개발 환경이 아닐 때만 실행되는 코드다.
IsDevelopment()는 현재 앱이 개발 환경인지 확인한다.
앞에 !가 붙었기 때문에 의미는 반대가 된다.
개발 환경이 아니라면
운영 환경에서는 사용자에게 개발자용 오류 화면을 그대로 보여주면 안 된다.
그래서 별도의 에러 페이지로 보내거나, HSTS(HTTP Strict Transport Security) 같은 보안 설정을 적용한다.
HTTPS 리다이렉션과 보안
app.UseHttpsRedirection();
app.UseAntiforgery();
UseHttpsRedirection()은 HTTP 요청을 HTTPS로 돌려주는 역할을 한다.
UseAntiforgery()는 요청 위조를 막기 위한 보안 기능이다.
아직 직접 폼을 만들지는 않았지만, 나중에 로그인이나 데이터 입력을 구현할 때 중요해질 부분이다.
정적 파일 연결
app.MapStaticAssets();
CSS, JavaScript, 이미지 같은 정적 리소스를 연결한다.
Blazor 화면 자체는 컴포넌트로 만들지만, CSS나 이미지 파일은 여전히 필요하다.
이런 파일들은 보통 wwwroot 아래에 들어간다.
App.razor 등록
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
여기서 App이 바로 App.razor다.
즉 Blazor 앱의 시작 컴포넌트로 App.razor를 사용하겠다는 의미다.
그리고 Interactive Server 렌더링 모드를 추가한다.
여기까지 오면 Program.cs에서 App.razor로 흐름이 넘어간다.
app.Run
app.Run();
서버를 실행한다.
콘솔 프로그램이라면 마지막 줄까지 실행하고 프로그램이 종료될 수 있다.
하지만 웹 서버는 요청을 계속 기다려야 한다.
그래서 app.Run() 이후에는 서버가 계속 살아 있으면서 브라우저 요청을 처리한다.
App.razor 확인
다음으로 본 파일은 Components/App.razor다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<ResourcePreloader />
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["WebApp-forWebView.styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
<Routes @rendermode="InteractiveServer" />
<ReconnectModal />
<script src="@Assets["_framework/blazor.web.js"]"></script>
</body>
</html>
처음 봤을 때는 .razor 파일인데 거의 HTML처럼 생겨서 조금 의외였다.
하지만 생각해보면 Blazor도 결국 브라우저에 HTML을 보여주는 웹 기술이다.
그래서 전체 문서 구조는 여전히 HTML을 기반으로 한다.
App.razor는 메인 페이지가 아니다
처음에는 App.razor가 메인 화면인가 싶었다.
하지만 실제로는 메인 페이지라기보다는 앱 전체를 감싸는 뼈대에 가깝다.
App.razor
↓
HTML 문서 전체 구조
↓
Routes
↓
실제 페이지
실제 첫 화면은 Home.razor다.
App.razor는 그 페이지가 들어갈 전체 틀을 제공한다.
App.razor에서 중요한 부분
CSS 연결
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["WebApp-forWebView.styles.css"]" />
기본 Bootstrap CSS와 프로젝트 CSS를 연결한다.
여기서 프로젝트 이름이 들어간 CSS 파일은 프로젝트명에 따라 다르게 생성될 수 있다.
나중에 화면 디자인을 바꾸려면 app.css나 각 컴포넌트 CSS 파일을 수정하게 될 가능성이 높다.
HeadOutlet
<HeadOutlet @rendermode="InteractiveServer" />
HeadOutlet은 각 페이지에서 설정한 head 관련 내용을 문서의 <head>에 반영하는 역할을 한다.
예를 들어 나중에 Home.razor에서 아래 코드를 사용한다.
<PageTitle>Home</PageTitle>
그러면 브라우저 탭 제목에 반영된다.
Routes
<Routes @rendermode="InteractiveServer" />
여기가 아주 중요했다.
Routes는 URL을 보고 어떤 페이지를 보여줄지 결정하는 컴포넌트다.
예를 들어 브라우저에서 /로 들어오면 Home.razor를 보여주고, /counter로 들어오면 Counter.razor를 보여주는 식이다.
여기에도 @rendermode="InteractiveServer"가 붙어 있다.
프로젝트 생성 시 대화형 위치를 Global로 선택했기 때문에 전체 라우팅 영역에 Interactive Server가 적용된 형태라고 볼 수 있다.
ReconnectModal
<ReconnectModal />
Interactive Server 방식에서는 브라우저와 서버가 연결을 유지한다.
연결이 끊기면 사용자가 화면에서 알 수 있어야 한다.
ReconnectModal은 그런 상황에서 재연결 관련 UI를 보여주는 역할을 한다.
처음에는 그냥 지나칠 수 있는 코드지만, Server 방식에서는 꽤 중요한 요소다.
blazor.web.js
<script src="@Assets["_framework/blazor.web.js"]"></script>
Blazor가 동작하기 위해 필요한 JavaScript 파일이다.
Blazor를 쓰면 JavaScript를 거의 직접 작성하지 않을 수 있지만, 내부적으로는 브라우저와 Blazor를 연결하기 위한 JavaScript가 필요하다.
특히 Interactive Server 방식에서는 브라우저와 서버 간의 연결이 중요하므로 이 스크립트가 필요하다.
Routes.razor 확인
다음으로 Components/Routes.razor를 확인했다.
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
여기서 Blazor 라우팅 구조가 나온다.
처음 보면 typeof, AppAssembly, RouteData 같은 단어들이 나와서 어렵게 보인다.
하지만 역할로 보면 이렇다.
현재 URL 확인
↓
프로젝트 안에서 @page가 붙은 Razor 페이지 검색
↓
URL과 맞는 페이지 찾기
↓
RouteView로 화면 출력
↓
맞는 페이지가 없으면 NotFound 페이지 출력
Router
<Router AppAssembly="typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
Router는 현재 URL에 맞는 페이지를 찾아주는 컴포넌트다.
예를 들어 아래 주소로 접속했다고 하자.
https://localhost:<PORT>/
그러면 Router는 프로젝트 안에서 아래와 같은 페이지를 찾는다.
@page "/"
그리고 해당 페이지를 화면에 보여준다.
AppAssembly
AppAssembly="typeof(Program).Assembly"
여기서 typeof(Program).Assembly는 Program 타입이 들어 있는 어셈블리를 의미한다.
처음에는 말이 어렵지만, 지금 단계에서는 이렇게 이해하면 충분하다.
현재 프로젝트 안에서 페이지를 찾아라
typeof는 C#에서 타입 자체의 정보를 가져올 때 사용한다.
Java로 치면 아래와 비슷한 느낌이다.
User.class
C#에서는 이렇게 쓴다.
typeof(User)
객체를 만드는 것이 아니라, User라는 타입의 정보를 가져오는 것이다.
NotFoundPage
NotFoundPage="typeof(Pages.NotFound)"
존재하지 않는 주소로 들어왔을 때 보여줄 페이지를 지정한다.
예를 들어 이런 주소로 들어간다고 하자.
https://localhost:<PORT>/wrong-page
프로젝트 안에 /wrong-page에 해당하는 페이지가 없으면 Pages.NotFound 페이지를 보여준다.
웹에서 흔히 말하는 404 페이지에 해당한다.
Found와 RouteView
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
Found는 URL에 맞는 페이지를 찾았을 때 실행되는 영역이다.
routeData에는 현재 찾은 페이지에 대한 정보가 들어 있다.
그리고 RouteView가 실제로 그 페이지를 화면에 출력한다.
Router가 페이지를 찾음
↓
routeData에 페이지 정보 저장
↓
RouteView가 해당 페이지 출력
DefaultLayout
DefaultLayout="typeof(Layout.MainLayout)"
Blazor에서는 페이지마다 공통 레이아웃을 적용할 수 있다.
예를 들어 대부분의 웹사이트는 이런 구조를 가진다.
상단 메뉴
본문
하단 영역
본문만 바뀌고 상단 메뉴나 공통 구조는 유지된다.
이런 공통 틀을 Layout이라고 한다.
현재는 기본 레이아웃으로 Layout.MainLayout을 사용하고 있다.
그래서 Home.razor 같은 페이지는 그냥 혼자 출력되는 것이 아니라, MainLayout 안에 들어가서 출력된다.
FocusOnNavigate
<FocusOnNavigate RouteData="routeData" Selector="h1" />
페이지 이동 시 h1 요소에 포커스를 맞추는 접근성 관련 기능이다.
지금 당장은 직접 수정할 필요는 없지만, Blazor 기본 템플릿이 접근성까지 어느 정도 고려해서 생성된다는 점은 기억해둘 만하다.
Home.razor 확인
마지막으로 실제 첫 화면인 Home.razor를 확인했다.
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
이 파일은 아주 짧다.
하지만 Blazor 라우팅을 이해하는 데 필요한 가장 중요한 코드가 들어 있다.
@page "/"
이 한 줄 때문에 이 파일이 루트 주소의 페이지가 된다.
@page의 의미
@page "/"
이 코드는 이 Razor 컴포넌트를 특정 URL과 연결한다.
예를 들어 아래처럼 쓸 수 있다.
@page "/"
/
@page "/login"
/login
@page "/member"
/member
즉 Router는 프로젝트 안에서 @page가 붙은 컴포넌트를 찾고, 현재 URL과 일치하는 페이지를 화면에 보여준다.
현재 Home.razor에는 @page "/"가 있기 때문에 브라우저에서 루트 주소로 접속하면 이 페이지가 나온다.
@ 기호의 의미
Blazor .razor 파일에서 @는 HTML 안에서 C#이나 Razor 문법을 사용하겠다는 표시다.
예를 들어 아래는 Razor 지시문이다.
@page "/"
나중에는 이런 코드도 나오게 된다.
@if (isLogin)
{
<p>로그인 상태입니다.</p>
}
@foreach (var item in items)
{
<p>@item.Name</p>
}
@code
{
private string message = "Hello Blazor";
}
즉 .razor 파일은 HTML과 C#이 섞이는 파일이다.
처음에는 낯설지만, 이 구조 덕분에 화면과 데이터를 한 파일 안에서 자연스럽게 연결할 수 있다.
PageTitle
<PageTitle>Home</PageTitle>
브라우저 탭 제목을 지정하는 컴포넌트다.
예를 들어 로그인 페이지를 만들면 이렇게 쓸 수 있다.
<PageTitle>로그인</PageTitle>
그러면 브라우저 탭 제목도 로그인으로 표시된다.
HTML 영역
<h1>Hello, world!</h1>
Welcome to your new app.
이 부분은 일반 HTML이다.
Blazor라고 해서 HTML이 없어지는 것은 아니다.
여전히 아래 요소들을 그대로 사용한다.
<h1></h1>
<div></div>
<p></p>
<input />
<button></button>
<table></table>
Blazor는 HTML을 대체한다기보다는, HTML에 C#을 연결할 수 있게 해주는 방식에 가깝다.
전체 실행 흐름 정리
이번에 본 파일들을 실행 순서대로 정리하면 이렇게 된다.
1. Visual Studio에서 실행
↓
2. Program.cs 실행
↓
3. ASP.NET Core 서버 생성
↓
4. Blazor Razor 컴포넌트 등록
↓
5. App.razor를 앱 시작 컴포넌트로 등록
↓
6. App.razor에서 HTML 문서 구조 생성
↓
7. App.razor 안의 Routes 실행
↓
8. Routes.razor의 Router가 현재 URL 확인
↓
9. @page "/"가 붙은 Home.razor 찾기
↓
10. MainLayout 안에 Home.razor 출력
↓
11. 브라우저에 Hello, world! 표시
처음에는 파일이 따로따로 있는 것처럼 보였는데, 흐름으로 보면 연결이 된다.
Program.cs
↓
App.razor
↓
Routes.razor
↓
Home.razor
이 구조를 먼저 이해한 것이 이번 작업에서 가장 중요했다.
결과 확인
프로젝트를 실행하면 브라우저가 열리고 로컬 주소로 접속된다.
주소는 환경에 따라 다르다.
https://localhost:<PORT>/
정상적으로 실행되면 화면에 아래 문구가 보인다.
Hello, world!
Welcome to your new app.
이 문구는 Home.razor에 작성되어 있던 내용이다.
<h1>Hello, world!</h1>
Welcome to your new app.
즉 브라우저에 보이는 첫 화면이 Home.razor에서 왔다는 것을 확인할 수 있다.
작업하면서 헷갈렸던 부분
이번에 가장 헷갈렸던 부분은 세 가지였다.
Blazor Web App과 Blazor Server, WebAssembly의 관계
대화형 위치 Global의 의미
Home.razor가 왜 첫 화면으로 뜨는지
처음에는 Blazor Server와 WebAssembly가 완전히 별개의 프로젝트처럼 느껴졌다.
그런데 실제로 프로젝트를 만들면서 보니, Blazor Web App 안에서 렌더링 방식을 선택하는 구조로 이해하는 것이 더 자연스러웠다.
또 Global이라는 옵션도 처음에는 막연했다.
하지만 App.razor에서 아래 코드가 들어간 것을 보고 조금 이해가 됐다.
<HeadOutlet @rendermode="InteractiveServer" />
<Routes @rendermode="InteractiveServer" />
전체 라우팅 영역에 Interactive Server 렌더링 모드가 적용되는 형태라고 볼 수 있었다.
그리고 Home.razor가 첫 화면으로 뜨는 이유도 처음에는 자동으로 정해진 것처럼 보였다.
하지만 실제로는 아래 한 줄 때문이었다.
@page "/"
Router가 이 값을 보고 루트 주소 /와 Home.razor를 연결한다.
이번 단계에서 일부러 하지 않은 것
오늘은 일부러 기능 구현을 하지 않았다.
아직 아래 작업들은 하지 않았다.
로그인 화면 만들기
버튼 이벤트 만들기
C# 변수 출력하기
DB 연결하기
외부 API 호출하기
Android WebView 연결하기
처음에는 빨리 화면을 만들고 싶었지만, 실행 구조를 모른 채로 기능부터 넣으면 나중에 문제가 생겼을 때 어디를 봐야 할지 모를 것 같았다.
그래서 오늘은 프로젝트가 어떤 순서로 시작되는지만 먼저 정리했다.
최종 구조
현재까지 이해한 구조는 아래와 같다.
WebApp_forWebView
├── Program.cs
│ └── ASP.NET Core 서버 시작
│
├── Components
│ ├── App.razor
│ │ └── 전체 HTML 문서 구조
│ │
│ ├── Routes.razor
│ │ └── URL과 페이지 연결
│ │
│ ├── Layout
│ │ └── MainLayout.razor
│ │ └── 공통 화면 틀
│ │
│ └── Pages
│ ├── Home.razor
│ │ └── "/" 주소의 첫 화면
│ │
│ └── NotFound.razor
│ └── 없는 주소 처리
│
├── wwwroot
│ └── CSS, JS, 이미지 등 정적 파일
│
└── appsettings.json
└── 설정 파일
이 구조만 기억해도 다음에 새 프로젝트를 만들었을 때 덜 헤맬 것 같다.
오늘 느낀 점
처음에는 Blazor를 그냥 "C#으로 웹 만드는 기술" 정도로만 생각했다.
그런데 프로젝트를 직접 만들고 파일을 하나씩 보니, 단순히 Razor 파일만 작성하는 것이 아니라 ASP.NET Core 서버 위에서 Blazor 컴포넌트가 동작하는 구조라는 점이 보였다.
특히 Program.cs가 생각보다 중요했다.
C나 Java에서 프로그램 시작점을 먼저 봐야 전체 구조가 잡히는 것처럼, Blazor에서도 Program.cs를 먼저 보는 게 맞는 것 같다.
그리고 App.razor, Routes.razor, Home.razor를 순서대로 따라가니 브라우저에 첫 화면이 뜨는 과정이 조금씩 연결됐다.
아직 C# 문법이 익숙하지는 않지만, Java나 C++와 비교하면서 보면 완전히 낯선 느낌은 아니었다.
앞으로는 Blazor 문법을 배우면서 필요한 C# 문법도 같이 익혀가는 방식이 좋을 것 같다.
오늘 배운 점
Blazor Web App을 선택했다
새 프로젝트는 Blazor Web App으로 만드는 것이 맞다.
Web Forms는 예전 ASP.NET Framework 계열 기술이고, 지금 새로 만드는 Blazor 프로젝트와는 다르다.
Interactive Server를 선택했다
이번 프로젝트는 나중에 로그인, 외부 데이터 처리, DB 접근이 들어갈 가능성이 높다.
그래서 처음에는 서버에서 C# 코드가 실행되는 Interactive Server 방식이 적합하다고 판단했다.
대화형 위치는 Global로 두었다
대부분의 화면에서 버튼 클릭, 검색, 조회 같은 동작이 필요할 가능성이 높다.
그래서 처음에는 전체 앱에 Interactive를 적용하는 Global 방식으로 시작했다.
Program.cs는 시작점이다
Program.cs는 Blazor Web App의 시작점이다.
웹 서버를 만들고, Blazor 컴포넌트를 등록하고, App.razor를 시작 컴포넌트로 연결한다.
App.razor는 전체 HTML 구조다
App.razor는 실제 메인 페이지라기보다는 앱 전체를 감싸는 HTML 문서 구조다.
이 안에서 Routes가 실행된다.
Routes.razor는 URL과 페이지를 연결한다
Routes.razor의 Router가 현재 URL을 보고 어떤 Razor 페이지를 보여줄지 결정한다.
Home.razor가 첫 화면인 이유는 @page "/" 때문이다
Home.razor에 아래 코드가 있기 때문에 루트 주소 /로 접속했을 때 첫 화면으로 표시된다.
@page "/"
다음 작업
다음에는 Home.razor를 직접 수정하면서 Razor 문법과 C# 기초를 같이 볼 예정이다.
다음 작업 목표는 아래 정도로 잡았다.
Home.razor 문구 수정
↓
C# 변수 만들기
↓
화면에 변수 출력하기
↓
@code 블록 이해하기
↓
버튼 클릭 이벤트 만들기
↓
클릭하면 화면 문구가 바뀌도록 만들기
여기서부터는 C# 문법이 조금씩 들어갈 것 같다.
예를 들어 아래 같은 코드가 나오게 될 것이다.
@page "/"
<h1>@message</h1>
@code
{
private string message = "Hello Blazor";
}
이 코드를 통해 private, string, 변수, Razor의 @, @code 블록을 같이 이해할 예정이다.
아직은 아주 기본 단계지만, 이 흐름대로 가면 나중에 로그인 화면이나 데이터 조회 화면도 자연스럽게 만들 수 있을 것 같다.