Spring Boot 컨트롤러의 문자열 반환과 ViewResolver 동작 원리

Jayson·2025년 6월 2일
0
post-thumbnail

Spring Boot로 웹 개발을 하다 보면, 컨트롤러 메서드에서 return "home"; 같이 문자열을 반환하는 코드를 자주 볼 수 있는데요! 그런데 메서드가 문자열만 반환했는데도 어떻게 해당 이름의 HTML 페이지가 브라우저에 표시되는 걸까요? 이번 글에서는 그 원리를 알아보겠습니다. Spring MVC의 ViewResolver가 어떤 역할을 해서 이러한 동작이 이루어지는지, 뷰 템플릿 파일들은 어디에 위치해야 하는지, 그리고 @Controller와 @RestController 사이에 이러한 처리 방식의 차이는 무엇인지 알아보겠습니다!

ViewResolver와 문자열 뷰 이름

Spring MVC에서 컨트롤러 메서드가 String 타입을 반환하면, 그 값은 뷰의 논리 이름(view name)으로 해석됩니다. 예를 들어 return "home";이라고 하면 "home"이라는 뷰 이름을 나타내는 것이지, "home"이라는 단순 텍스트를 응답으로 보내라는 뜻이 아닙니다. 이 때 Spring MVC의 디스패처 서블릿(DispatcherServlet)은 뷰 이름을 처리해 줄 적절한 ViewResolver에게 처리를 맡깁니다. ViewResolver는 뷰 이름을 실제 뷰(페이지 템플릿)로 변환해주는 컴포넌트입니다.

Spring MVC 요청 처리 흐름:

  1. 클라이언트 요청이 DispatcherServlet(Front Controller)에 전달되고, 컨트롤러가 모델과 뷰 이름을 반환합니다.

  2. 이후 DispatcherServlet이 ViewResolver를 통해 해당 뷰 이름에 맞는 뷰 템플릿을 찾고 렌더링을 위임합니다.

  3. View 템플릿에서 모델 데이터를 활용해 HTML 응답을 생성하며, 최종적으로 브라우저에 HTML이 표시됩니다. 컨트롤러가 문자열로 뷰 이름을 반환하면, ViewResolver가 prefix와 suffix를 조합하여 해당 이름의 템플릿 파일을 찾고 렌더링하게 됩니다. 예를 들어 ViewResolver에 미리 설정된 prefix가 /templates/, suffix가 .html로 되어 있다면, 뷰 이름 "home"은 /templates/home.html 위치의 파일을 가리키게 되는 식입니다.

※ 참고: 뷰 이름이 "redirect:..." 또는 "forward:..." 로 시작하면 이는 ViewResolver에 의해 특별히 처리되어, 해당 경로로 리다이렉트하거나 서버 내 포워드하도록 동작합니다. 예를 들어 return "redirect:/login";처럼 반환하면 뷰를 렌더링하지 않고 지정한 경로로 리다이렉트합니다.

Spring Boot의 기본 ViewResolver 설정 (Thymeleaf)

Spring Boot에서는 별도 설정을 하지 않더라도 기본적인 ViewResolver가 동작하도록 자동 구성(auto-configuration)이 마련되어 있습니다. 특히 Thymeleaf 템플릿 엔진을 사용한다면, Spring Boot가 ThymeleafViewResolver를 자동 등록하고 합리적인 기본값들을 설정해줍니다. 기본적으로 템플릿 파일들은 src/main/resources/templates 경로에 두면 되고, 뷰 이름과 파일 이름은 동일하게 맞춥니다. 예를 들어 "home" 뷰 이름을 반환하면 templates 폴더 내의 home.html 파일을 찾게 됩니다. (별도로 Model에 담은 데이터는 템플릿에서 ${...} 등을 통해 출력할 수 있습니다.)

Spring Boot의 Thymeleaf 설정에서는 prefix와 suffix 기본값이 이미 지정되어 있습니다. 기본 prefix는 classpath:/templates/이고 기본 suffix는 .html입니다. 즉, 컨트롤러가 뷰 이름 "home"을 반환하면 실제로는 classpath:/templates/home.html 파일을 찾아서 뷰로 사용하게 됩니다. 이 값들은 application.properties (또는 application.yml)에서 변경할 수 있는데, 예를 들어 템플릿 폴더를 다른 경로로 옮기고 싶다면 다음과 같이 설정하면 됩니다:

spring.thymeleaf.prefix=classpath:/templates/views/
spring.thymeleaf.suffix=.html

이렇게 하면 컨트롤러에서 "home"을 반환할 때 /WEB-INF/jsp/home.jsp를 뷰로 찾아 렌더링하게 됩니다. (단, Jar 형태 배포에서는 /WEB-INF 경로의 JSP를 로딩하는 데 문제가 생길 수 있으므로, JSP 사용 시에는 War 패키징과 톰캣 Jasper 의존성 추가 등이 필요합니다. 이것은 Spring Boot에서 JSP를 권장하지 않는 이유 중 하나입니다.)

@Controller와 @RestController의 차이

스프링에서 웹 컨트롤러를 작성할 때 사용하는 @Controller와 @RestController는 반환값 처리 면에서 중요한 차이가 있습니다. 기본적으로 @Controller를 사용한 클래스의 메서드는 반환한 문자열을 뷰 이름으로 간주하여 ViewResolver가 처리합니다. 반면, @RestController를 사용하면 클래스 내 모든 메서드에 @ResponseBody가 자동 적용된 것으로 처리되어, 반환값을 뷰 이름으로 보지 않고 HTTP 응답 바디로 직접 작성합니다. 쉽게 말해, 일반 @Controller는 HTML 페이지(View)를 반환하기 위한 것이고, @RestController는 JSON/텍스트 등의 데이터를 바로 반환하기 위한 용도입니다. 따라서 @RestController에서는 문자열을 반환하면 그 값이 그대로 응답으로 쓰이지만 (예: "home"이라는 텍스트가 응답으로 내려감), @Controller에서는 문자열을 반환하면 해당 이름의 뷰 템플릿을 찾게 되는 것입니다.

예를 들어 아래와 같은 두 컨트롤러를 비교해봅시다:

@Controller
public class PageController {
    @GetMapping("/home")
    public String home(Model model) {
        model.addAttribute("message", "Hello, Spring!");
        return "home";  // 뷰 이름 "home" -> home.html 템플릿을 찾게 됨
    }
}
@RestController
public class ApiController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring!";  // 이 문자열이 응답 바디에 그대로 쓰임 (뷰 이름 아님)
    }
}

첫 번째 PageController는 @Controller이므로 /home 요청 시 Thymeleaf의 home.html 템플릿을 찾아 렌더링합니다. 이때 model에 담은 "message" 값을 home.html 파일에서 사용해 동적으로 내용을 채울 수 있습니다. 예를 들어 src/main/resources/templates/home.html 파일이 다음과 같다고 하면:

<!-- src/main/resources/templates/home.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Home</title></head>
<body>
    <p th:text="${message}">메시지가 여기에 출력됩니다.</p>
</body>
</html>

브라우저에는 위 HTML이 렌더링되어 "Hello, Spring!" 문구가 화면에 보이게 됩니다. 반면 두 번째 ApiController는 @RestController로 선언되어 있으므로 /hello 요청에 대해 뷰 탐색을 하지 않고, 메서드가 반환한 문자열 "Hello, Spring!"을 그대로 HTTP 응답으로 보냅니다. 결과적으로 사용자는 브라우저에서 JSON이나 일반 텍스트 형태로 "Hello, Spring!"이라는 응답을 받게 되며, 별도의 HTML 페이지가 렌더링되지 않습니다.

이처럼 @Controller는 뷰 템플릿 렌더링(서버 사이드 HTML 생성)을 목적으로 하고, @RestController는 데이터(JSON/XML/문자열 등) 전송을 목적으로 합니다. 웹 애플리케이션에서 페이지를 띄워야 할 경우 컨트롤러에 @Controller를 사용해 뷰 이름을 반환하고, API 서버처럼 JSON 데이터를 줄 경우 @RestController를 사용하는 것이죠. 만약 @Controller 클래스에서 개별 메서드만 JSON 등을 반환하고 싶다면 그 메서드에 @ResponseBody를 붙이면 되고, 반대로 @RestController에서 뷰를 반환하고 싶다면 @RestController 대신 @Controller를 써야 합니다 (혹은 @RestController 클래스 내에서 특정 메서드에 @ResponseBody를 제거하는 건 불가능하므로 별도 @Controller 클래스로 분리해야 합니다).

맺음말

정리하면, Spring MVC의 ViewResolver는 컨트롤러가 반환한 문자열을 받아서 어떤 뷰(template)를 보여줄지 결정하는 역할을 합니다. Spring Boot에서는 기본적으로 Thymeleaf를 통한 뷰 해석을 지원하며, 특별한 설정 없이도 resources/templates 폴더의 .html 파일들을 뷰로 사용할 수 있게 prefix/suffix 등이 미리 세팅되어 있습니다. 덕분에 컨트롤러에서 단순히 "home" 같은 문자열만 반환해도 알맞은 HTML 페이지가 사용자에게 보이게 되는 것입니다. 다만 @Controller와 @RestController의 차이를 꼭 기억해야 합니다. 전자는 문자열을 뷰 이름으로 처리하여 페이지를 렌더링하고, 후자는 문자열을 데이터로 바로 응답하기 때문에, 상황에 맞게 올바른 애노테이션을 사용하는 것이 중요합니다.

참고

profile
Small Big Cycle

0개의 댓글