ASP.NET Core MVC로 News 섹션 만들기
0. 시작하기 전에
이 실습은 다음 두 가지 방식 중 하나로 진행할 수 있습니다.
방법 1: 새 프로젝트 생성
- 프로젝트 이름: Azunt (또는 독자가 원하는 모든 이름 가능)
- 템플릿: ASP.NET Core Web App (Model-View-Controller)
방법 2: 기존 프로젝트 활용
이미 학습 중인 다음 프로젝트가 있다면 해당 프로젝트를 열어 작업해도 됩니다.
- DotNetNote
- Hawaso
- VisualAcademy
실습의 목적은 MVC 구조 이해이므로, 기존 프로젝트에 NewsController와 Views/News 폴더를 추가해도 무방합니다. 핵심은 “프로젝트 생성”이 아니라 “MVC 구조의 흐름을 이해하는 것”입니다.
1. 프로젝트 생성 또는 기존 프로젝트 열기
1.1 새 프로젝트 생성 (Azunt)
Visual Studio 2026 실행 → Create a new project → ASP.NET Core Web App (Model-View-Controller) 선택 → 프로젝트 이름: Azunt

- Framework: 최신 버전 선택
- Authentication: None (학습 목적)
MVC 템플릿을 선택하는 이유는 이미 Controllers, Views, wwwroot 구조가 준비된 상태에서 시작할 수 있기 때문입니다. 학습 초기에는 구조를 직접 만드는 것보다, “구조를 이해하는 것”에 집중하는 것이 중요합니다.
2. 라우팅 설정 (Program.cs)
2.1 Program.cs 파일 열기
솔루션 탐색기에서 Program.cs를 열고, News 전용 라우트를 추가합니다.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute(
name: "news",
pattern: "news/{action=Index}/{id?}",
defaults: new { controller = "News" });
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

이 설정을 통해 /news 경로는 NewsController로 연결됩니다.
Program.cs에서 라우팅을 설정하는 이유는 “URL과 컨트롤러의 연결 지점”이 바로 이곳이기 때문입니다. MVC에서 라우팅은 가장 먼저 요청을 받아들이는 단계이며, 이 설정이 없으면 URL이 컨트롤러로 전달되지 않습니다. 학습 목적상, 라우팅을 명시적으로 추가하여 URL 구조와 액션의 관계를 분명히 이해하도록 합니다.
3. 모델 작성 (Azunt.Models)
3.1 Models 폴더에 NewsArticle.cs 추가
- Models 폴더 → Add → Class
- 파일 이름: NewsArticle.cs
namespace Azunt.Models;
public class NewsArticle
{
public int Id { get; set; }
public string Title { get; set; } = "";
public string Summary { get; set; } = "";
public string Content { get; set; } = "";
public string Category { get; set; } = "general";
public DateTime PublishedAt { get; set; } = DateTime.UtcNow;
}

Model은 MVC에서 “데이터를 표현하는 역할”을 담당합니다. 이 클래스는 데이터베이스 테이블이 아니라, 화면에 전달할 데이터를 표현하는 개체입니다. 지금은 단순한 클래스지만, 이후 EF Core를 연결하면 테이블과 매핑되는 구조로 확장됩니다.
4. 학습용 Repository 작성 (선택 단계)
DB 없이 MVC 흐름을 이해하기 위해 인메모리 Repository를 사용합니다.
학습 초기에는 데이터베이스 연결까지 동시에 진행하면 구조 이해가 어려워질 수 있습니다. 따라서 이 단계에서는 단순한 컬렉션 기반 Repository를 사용하여, “컨트롤러 → 모델 → 뷰” 흐름에 집중합니다.
4.1 Data 폴더 생성
- 프로젝트 루트 → Add → New Folder
- 폴더 이름: Repositories
실제 프로젝트에서는 Repositories, Services, Infrastructure 등 계층을 나누어 관리합니다. 여기서는 단순화를 위해 Repository 폴더만 생성합니다.
4.2 NewsRepository.cs 추가
using Azunt.Models;
namespace Azunt.Repositories;
public static class NewsRepository
{
private static readonly List<NewsArticle> _articles = new()
{
new NewsArticle
{
Id = 1234,
Title = "AI in Healthcare: What Changed",
Summary = "A quick overview of recent trends.",
Content = "Long content here...",
Category = "tech",
PublishedAt = new DateTime(2026, 2, 20)
}
};
public static IEnumerable<NewsArticle> GetAll() =>
_articles.OrderByDescending(x => x.PublishedAt);
public static NewsArticle? GetById(int id) =>
_articles.FirstOrDefault(x => x.Id == id);
}

Repository는 데이터 접근을 담당하는 계층입니다. 현재는 메모리 기반이지만, 이후에는 EF Core 기반 구현으로 교체할 수 있습니다. 이렇게 계층을 나누는 이유는 구조적 확장성을 확보하기 위함입니다.
5. NewsController 추가
5.1 Controllers 폴더에서 추가
- Controllers → Add → Controller
- MVC Controller - Empty
- 이름: NewsController
using Microsoft.AspNetCore.Mvc;
using Azunt.Repositories;
namespace Azunt.Controllers;
public class NewsController : Controller
{
public IActionResult Index()
{
var articles = NewsRepository.GetAll().Take(10);
return View(articles);
}
[HttpGet("/news/details/{id:int}")]
public IActionResult Details(int id)
{
var article = NewsRepository.GetById(id);
if (article == null) return NotFound();
return View(article);
}
}

Controller는 HTTP 요청을 받아서 적절한 Model을 준비한 후 View로 전달하는 역할을 합니다. IActionResult를 반환하는 이유는 View, Redirect, Json 등 다양한 결과를 유연하게 반환하기 위함입니다. Details 액션에서
{id:int}제약을 둔 이유는 정수형 URL만 허용하도록 하기 위함입니다.
6. Views/News 폴더 생성
6.1 폴더 생성
- Views 폴더 선택
- Add → New Folder
- 이름: News

MVC는 “관례 기반 구조(Convention-based)”를 따릅니다. 컨트롤러 이름이 NewsController라면, Views/News 폴더에서 View를 찾습니다. 이 규칙을 이해하면 View 위치를 헷갈리지 않게 됩니다.
7. Index.cshtml 생성
/Views/News 폴더에 Add → Razor View → 이름: Index.cshtml
@model IEnumerable<Azunt.Models.NewsArticle>
<h2>News</h2>
<ul>
@foreach (var a in Model)
{
<li>
<a href="/news/details/@a.Id">@a.Title</a>
</li>
}
</ul>

@model 지시문은 View가 어떤 타입의 데이터를 받을 것인지 지정합니다. 여기서
IEnumerable<NewsArticle>을 지정했기 때문에, Controller의 Index 액션에서 동일한 타입을 반환해야 합니다. 이것이 MVC에서 Model과 View의 계약입니다.
8. Details.cshtml 생성
/Views/News 폴더에 Add → Razor View → 이름: Details.cshtml
@model Azunt.Models.NewsArticle
<h2>@Model.Title</h2>
<p>@Model.PublishedAt.ToString("yyyy-MM-dd")</p>
<hr />
<div>@Model.Content</div>

단일 개체를 받기 때문에 @model은 NewsArticle입니다. Model 속성은 강력한 형식 검사를 제공하므로, 컴파일 시점에 오류를 확인할 수 있습니다.
9. 보충: QueryString 기반 Article 액션
9.1 NewsController에 추가
public IActionResult Article(int id)
{
if (id <= 0) return BadRequest();
var article = NewsRepository.GetById(id);
if (article == null) return NotFound();
return View(article);
}

/news/article?Id=1234형태는 QueryString 기반 URL입니다. 과거 웹 애플리케이션에서 널리 사용되던 방식이며, 모델 바인딩이 QueryString 값을 자동으로 매개변수 id에 매핑합니다. Route 기반과 QueryString 기반을 모두 이해하는 것이 실무 적응에 도움이 됩니다.
10. 실행 및 학습 점검
다음 URL을 직접 입력해 봅니다.
- /news
- /news/details/1234
- /news/article?Id=1234

다음 질문에 답할 수 있어야 합니다.
- Route 기반과 QueryString 기반의 차이는 무엇인가?
- 모델 바인딩은 어떻게 id 값을 전달하는가?
- 컨트롤러와 View는 어떤 규칙으로 연결되는가?
URL을 직접 변경해 보면서 액션이 어떻게 호출되는지 확인하십시오. MVC는 “코드”보다 “흐름”을 이해하는 것이 먼저입니다. 이 흐름이 머릿속에 정리되면 CRUD, 인증, EF Core 확장도 자연스럽게 이해됩니다.
마무리
이 실습은 MVC의 구조를 이해하기 위한 최소 구성 예제입니다.
Azunt 프로젝트에서 직접 구현해 보고, 또는 DotNetNote, Hawaso, VisualAcademy 프로젝트에 추가하여 구조를 확장해 보십시오.
MVC의 핵심은 다음 흐름입니다.
라우팅 → 컨트롤러 → 모델 바인딩 → 뷰 렌더링
이 흐름을 스스로 설명할 수 있을 때, ASP.NET Core MVC의 기본기를 갖추었다고 할 수 있습니다.