Blazor 프로젝트 구조 이해 — 폴더와 핵심 파일 역할 완벽 정리

프로젝트 구조 이해
📚 이 글에서 다루는 것

프로젝트 구조 이해

Blazor 프로젝트의 폴더 구조와 주요 파일 역할을 파악합니다.
Blazor 프로젝트 구조 이해
Blazor 프로젝트의 폴더 구조와 주요 파일 역할을 체계적으로 학습하여 개발의 탄탄한 기반을 마련합니다.

📌 학습 목표

  • dotnet new blazor 명령으로 생성된 프로젝트의 전체 폴더 구조를 설명할 수 있습니다.
  • Program.cs의 역할과 앱 시작 흐름의 3단계를 이해합니다.
  • App.razor, _Imports.razor, wwwroot 등 핵심 파일의 목적을 구체적으로 파악합니다.
  • 페이지 컴포넌트(Pages)와 레이아웃 컴포넌트(Layout)가 어떻게 협력하는지 이해합니다.
  • Blazor Server와 Blazor WebAssembly의 구조적 차이점을 비교할 수 있습니다.

📝 개념 설명

Blazor 프로젝트 생성 방법

Blazor 프로젝트는 .NET CLI 또는 Visual Studio를 통해 생성할 수 있습니다. .NET 8 이상에서는 통합 Blazor 프로젝트 템플릿이 제공됩니다.

# .NET 8 통합 Blazor (권장)
dotnet new blazor -n MyBlazorApp

# Blazor Server (전통 방식 — .NET 6/7)
dotnet new blazorserver -n MyBlazorServer

# Blazor WebAssembly (전통 방식 — .NET 6/7)
dotnet new blazorwasm -n MyBlazorWasm

프로젝트 폴더 구조 전체 개요 (.NET 8 기준)

프로젝트를 생성하면 아래와 같은 구조가 자동으로 만들어집니다.

MyBlazorApp/
├── Components/              ← Razor 컴포넌트 전체 모음
│   ├── Layout/              ← 레이아웃 컴포넌트
│   │   ├── MainLayout.razor     공통 껍데기 (헤더·사이드바 등)
│   │   └── NavMenu.razor        사이드바 메뉴
│   ├── Pages/               ← 페이지 컴포넌트 (@page 지시어 있음)
│   │   ├── Home.razor
│   │   ├── Counter.razor
│   │   └── Weather.razor
│   ├── App.razor            ← 앱 최상위 루트 컴포넌트 (Router 포함)
│   └── _Imports.razor       ← 전역 using 네임스페이스 선언
├── wwwroot/                 ← 정적 리소스 (CSS, JS, 이미지)
│   ├── app.css
│   └── favicon.png
├── appsettings.json         ← 앱 설정 (DB 연결 문자열, 로깅 등)
├── Program.cs               ← 앱 진입점 + 의존성 주입(DI) 등록 + 미들웨어
└── MyBlazorApp.csproj       ← 프로젝트 파일 (NuGet 패키지 참조)
📌 핵심 파일 역할 한눈에 보기
⚙️
Program.cs
앱 시작점. 의존성 주입(DI)(의존성 주입) 서비스 등록, 미들웨어 파이프라인 구성, Blazor 라우팅 활성화
🌳
App.razor
Blazor 컴포넌트 트리의 최상위 루트. URL을 페이지 컴포넌트에 연결하는 Router 컴포넌트 포함
📦
_Imports.razor
모든 .razor 파일에 자동 적용되는 전역 using 네임스페이스 선언 모음
🖼️
wwwroot/
CSS, JavaScript, 이미지 등 브라우저에 직접 서빙되는 정적 파일 전용 폴더

Program.cs — 앱의 진입점

Program.cs는 Blazor 애플리케이션이 시작될 때 가장 먼저 실행되는 파일입니다. .NET 6 이후 간결한 최상위 명령문(Top-level statements) 방식으로 작성됩니다.

var builder = WebApplication.CreateBuilder(args);

// ① 서비스 등록 (의존성 주입(DI) 컨테이너)
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// ② HTTP 요청 파이프라인 구성 (미들웨어)
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();

// ③ Blazor 라우팅 활성화
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();
💡 Program.cs의 3단계 흐름
builder 생성 단계 → 서비스·의존성 주입(DI) 등록
app = builder.Build() 단계 → 미들웨어 파이프라인 구성
app.Run() 단계 → 서버 시작, HTTP 요청 수신 대기

App.razor — 루트 컴포넌트

App.razor는 Blazor 컴포넌트 트리에서 가장 최상위에 위치하는 루트 컴포넌트입니다. URL에 따라 어떤 페이지 컴포넌트를 렌더링할지 결정하는 Router 컴포넌트를 포함합니다.

<Router AppAssembly="typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="routeData"
                   DefaultLayout="typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="typeof(Layout.MainLayout)">
            <p role="alert">해당 주소에 페이지가 없습니다.</p>
        </LayoutView>
    </NotFound>
</Router>

<Found> 블록은 URL에 매핑된 페이지가 존재할 때 렌더링되고, <NotFound> 블록은 매핑된 페이지가 없을 때 렌더링됩니다. DefaultLayoutMainLayout을 지정하여 모든 페이지에 공통 레이아웃을 적용합니다.

_Imports.razor — 전역 네임스페이스 선언

_Imports.razor에 선언된 @using 지시어는 같은 폴더 및 하위 폴더의 모든 .razor 파일에 자동으로 적용됩니다. 매 파일마다 반복해서 using을 추가하는 번거로움을 없애줍니다.

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using MyBlazorApp
@using MyBlazorApp.Components

Components/Pages/ — 페이지 컴포넌트

Pages 폴더 내 .razor 파일은 @page 지시어를 통해 URL 경로를 부여받은 페이지 컴포넌트입니다. 기본 예시인 Counter.razor를 살펴봅니다.

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>
<p role="status">현재 카운트: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">
    클릭하세요
</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}

Components/Layout/ — 레이아웃 컴포넌트

레이아웃 컴포넌트는 공통 UI 껍데기(헤더, 사이드바, 푸터 등)를 정의합니다. MainLayout.razor가 기본 레이아웃이며, @Body로 표시된 위치에 각 페이지 컴포넌트 내용이 주입됩니다.

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />    <!-- 네비게이션 메뉴 컴포넌트 -->
    </div>
    <main>
        <div class="top-row px-4">
            <a href="https://learn.microsoft.com/aspnet/core/"
               target="_blank">About</a>
        </div>
        <article class="content px-4">
            @Body    <!-- 여기에 페이지 컴포넌트 내용이 삽입됨 -->
        </article>
    </main>
</div>

💡 예제 & 실습 — 새 페이지 직접 추가해 보기

프로젝트 구조를 이해했다면, 직접 새 페이지를 만들어 구조가 어떻게 작동하는지 확인해봅니다.

단계 1: Components/Pages/ 폴더에 About.razor 파일 생성

@page "/about"

<PageTitle>소개 페이지</PageTitle>

<h1>Blazor 프로젝트 구조 학습</h1>
<p>이 페이지는 프로젝트 구조 이해를 위해 직접 생성했습니다.</p>
<p>현재 시각: @DateTime.Now.ToString("HH:mm:ss")</p>

@code {
    // 이 @code 블록에 C# 코드를 작성합니다
}

단계 2: Components/Layout/NavMenu.razor에 메뉴 항목 추가

<div class="nav-item px-3">
    <NavLink class="nav-link" href="about">
        <span class="bi bi-info-circle-fill" aria-hidden="true"></span>
        About
    </NavLink>
</div>

단계 3: 개발 서버 실행 및 확인

dotnet run

브라우저에서 https://localhost:5001/about으로 이동하면 새로 만든 페이지가 표시됩니다. 사이드바에도 “About” 메뉴 항목이 추가된 것을 확인할 수 있습니다.

🔄 Blazor 페이지 렌더링 흐름
브라우저 URL 요청
Program.cs
(라우팅 활성화)
App.razor
(Router 컴포넌트)
MainLayout.razor
(@Body 위치)
Pages/*.razor
(실제 페이지)

Blazor Server vs Blazor WebAssembly 구조 비교

⚖️ Blazor Server vs Blazor WebAssembly 구조 비교
항목Blazor ServerBlazor WebAssembly
C# 실행 위치서버(ASP.NET Core)브라우저(WebAssembly)
Program.cs 역할서버 의존성 주입(DI) + SignalR 설정WebAssembly 호스트 설정
추가 파일없음 (단일 프로젝트)wwwroot/index.html (진입 HTML)
appsettings.json 위치프로젝트 루트 (서버 측)wwwroot/ 내부 (클라이언트 측)
.NET 8 통합 방식blazor 템플릿 하나로 Server·WebAssembly 렌더 모드를 함께 사용 가능

⚠️ 자주 틀리는 것 / 주의사항

⚠️ 주의 1: @page 지시어 누락 시 URL 접근 불가
페이지 컴포넌트에 @page "/경로" 지시어가 없으면 URL로 직접 접근이 불가능합니다. 컴포넌트를 다른 컴포넌트 안에서 태그로 사용할 때는 @page 없이 사용하고, URL 직접 접근이 필요한 경우에만 @page를 추가합니다.
⚠️ 주의 2: _Imports.razor의 적용 범위(스코프) 혼동
_Imports.razor는 파일이 위치한 폴더와 그 하위 폴더에만 적용됩니다. Components/_Imports.razor는 Components 하위 전체에 적용되지만, Components/Pages/_Imports.razor는 Pages 폴더에만 적용됩니다. 특정 폴더에서만 필요한 using은 해당 폴더의 _Imports.razor에 분리하여 관리하는 것이 좋습니다.
⚠️ 주의 3: 정적 파일은 반드시 wwwroot/ 내부에
CSS, JS, 이미지 파일은 반드시 wwwroot/ 폴더 내에 위치해야 합니다. 프로젝트 루트나 Components 폴더에 직접 넣으면 브라우저가 접근할 수 없습니다. 정적 파일 접근은 app.UseStaticFiles() 미들웨어가 wwwroot를 기준으로 처리하기 때문입니다.
⚠️ 주의 4: .NET 버전별 폴더 구조 차이 주의
.NET 6/7의 Blazor 프로젝트는 Shared/ 폴더와 Pages/ 폴더를 최상위에 사용하지만, .NET 8부터는 Components/Layout/Components/Pages/로 구조가 변경되었습니다. 온라인 튜토리얼을 참고할 때는 반드시 .NET 버전을 확인하세요.

🎯 마무리 — 핵심 내용 재정리와 실전 활용 팁

Blazor 프로젝트의 폴더 구조와 핵심 파일 역할을 파악하면 프로젝트 전체의 동작 방식을 예측할 수 있습니다. 각 파일이 담당하는 책임이 명확히 분리되어 있으므로, 새 기능을 추가하거나 오류가 발생했을 때 어느 파일을 수정해야 할지 빠르게 판단할 수 있습니다.

실전 활용 팁:
  • 새 서비스(데이터베이스 컨텍스트, HTTP 클라이언트 등)를 추가할 때는 Program.csbuilder.Services에 먼저 등록합니다.
  • 모든 페이지에서 공통으로 사용할 UI(헤더, 사이드바, 네비게이션)는 Components/Layout/ 내 레이아웃 컴포넌트에 작성합니다.
  • 자주 사용하는 네임스페이스는 _Imports.razor에 한 번만 선언하면 하위 모든 컴포넌트에서 재사용됩니다.
  • CSS 테마 파일, 외부 JS 라이브러리, 폰트 등은 wwwroot/ 하위에 폴더를 만들어 체계적으로 관리합니다.
✅ 핵심 정리
  • Program.cs: 앱 진입점 — ① 서비스 의존성 주입(DI) 등록 → ② 미들웨어 파이프라인 구성 → ③ app.Run() 서버 시작
  • App.razor: 루트 컴포넌트 — Router로 URL을 페이지 컴포넌트에 연결하고 DefaultLayout 지정
  • _Imports.razor: 전역 using 선언 — 같은 폴더 및 하위 모든 .razor에 자동 적용
  • Components/Pages/: @page "/경로" 지시어를 가진 라우팅 가능 페이지 컴포넌트 모음
  • Components/Layout/: @Body로 페이지를 끼워넣는 공통 레이아웃(MainLayout)과 NavMenu 보관
  • wwwroot/: CSS·JS·이미지 등 브라우저에 직접 서빙되는 정적 파일 전용 폴더
  • 렌더링 순서: URL 요청 → Program.cs(라우터 활성화) → App.razor(Router) → MainLayout(@Body) → 페이지 컴포넌트

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.