ASP.NET Core 애플리케이션을 개발할 때 가장 기본이 되는 구성 요소 중 하나는 컨트롤러(Controller)입니다. 이는 MVC 아키텍처의 'C'를 담당하며, 모델과 뷰 사이의 흐름을 제어합니다. 특히 .NET Core 2.1에서는 Web API와 웹 앱 프로젝트 템플릿 모두에서 일관된 컨트롤러 기반 설계를 제공하는데, 그 핵심은 ControllerBase, Controller, 그리고 다양한 속성(Attribute)들 간의 상속 및 역할 분리에 있습니다.
ControllerBase: 모든 컨트롤러의 근간
ControllerBase는 이름에서 알 수 있듯이 모든 컨트롤러 클래스의 기본 추상 클래스입니다. 이 클래스는 Microsoft.AspNetCore.Mvc 네임스페이스에 위치하며, Microsoft.AspNetCore.Mvc.Core.dll 어셈블리 내에 정의되어 있습니다.
[Controller]
public abstract class ControllerBase
{
// HTTP 응답 생성, 모델 바인딩, 상태 코드 처리 등을 위한 기본 메서드 제공
}
중요한 점은 이 클래스가 뷰(View)를 렌더링하는 기능을 포함하지 않는다는 것입니다. 즉, JSON 또는 XML과 같은 데이터 형식만 반환하는 Web API 컨트롤러에 적합합니다. 또한 클래스 자체에 [Controller] 특성이 부여되어 있어, 라우팅 시스템이 이를 컨트롤러 후보로 인식할 수 있도록 합니다.
Controller: 뷰를 지원하는 MVC 전용 컨트롤러
반면 Controller 클래스는 ControllerBase를 상속받아 확장한 형태이며, Microsoft.AspNetCore.Mvc.ViewFeatures.dll에 포함되어 있습니다. 주 용도는 Razor 뷰를 사용하는 전통적인 MVC 웹 애플리케이션입니다.
public abstract class Controller : ControllerBase, IActionFilter, IAsyncActionFilter, IDisposable
{
// View(), PartialView(), RedirectToAction() 등의 뷰 관련 메서드 제공
}
이 클래스는 액션 필터(IActionFilter), 비동기 필터(IAsyncActionFilter) 인터페이스를 구현하며, 리소스 정리를 위한 IDisposable도 포함합니다. 따라서 요청 후처리나 세션 관리 등 더 복잡한 웹 페이지 로직을 다룰 수 있습니다.
Controller 특성: 컨트롤러 인식 메커니즘
[Controller] 특성은 특정 클래스를 MVC 라우팅 파이프라인에서 컨트롤러로 인식하게 만듭니다. 기본적으로 클래스 이름이 "Controller" 접미사를 가지면 자동으로 컨트롤러로 간주되지만, 이 규칙을 벗어나고 싶을 경우 명시적으로 이 특성을 사용할 수 있습니다.
[Controller]
public class Endpoint { }
[Route("api/[controller]")]
public class Products : Endpoint
{
[HttpGet]
public IActionResult List() => Ok(new[] { "Laptop", "Phone" });
}
위 예제에서 Products 클래스는 Endpoint로부터 상속받지만, [Controller] 특성이 붙어 있으므로 정상적으로 라우팅됩니다. 대신 계층 구조 내 어디든 [NonController] 특성이 존재하면 해당 클래스는 컨트롤러로 간주되지 않습니다. 이 특성은 [Controller]보다 우선 순위가 높습니다.
ApiController 특성: Web API 개발을 위한 편의 기능 제공
[ApiController]는 Web API 개발 시 생산성을 높이기 위해 도입된 특성으로, ControllerAttribute를 상속받으며 IApiBehaviorMetadata를 구현합니다. 이 특성을 컨트롤러에 적용하면 다음과 같은 자동화된 동작이 활성화됩니다:
- 자동 모델 유효성 검사: 유효성 오류 발생 시 자동으로 400 Bad Request 응답 반환
- 바인딩 소스 매핑: 라우트/쿼리 문자열/본문 등의 매개변수를 자동으로 매핑
- [FromBody] 생략 가능: 복합 타입 매개변수는 기본으로 본문에서 바인딩
- API 루트 기반 라우팅 강제:
[Route(...)]필요
[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
[HttpGet("{id:int}")]
public IActionResult Get(int id)
{
if (id <= 0) return BadRequest();
return Ok(new { Id = id, Name = "John" });
}
}
이 특성은 RESTful 스타일의 API 개발에 최적화되어 있으며, 불필요한 유효성 검사 코드를 줄여줍니다.