스프링 웹 MVC 애플리케이션으로 동적 컨텐츠를 생성하는 방법을 살펴본다.

템플릿엔진

템플릿엔진은 주로 뷰(동적 컨텐츠)를 만드는데 사용 하지만, 코드 generation이나 이메일 템플릿에도 사용할 수 있다.

스프링 부트가 자동 설정을 지원하는 템플릿 엔진

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache

JSP는?

JSP를 사용하면 jar로 패키징한 파일을 사용할 수 없으며 war로 패키징해야 한다. 물론 war로 패키징하더라도 java -jar로 실행할 수 있으며 다른 서블릿 엔진에 배포할 수도 있다. 하지만, 서블릿 엔진 중 가장 최근에 JBoss 진영에서 개발한 Undertow는 JSP를 지원하지 않는다. 또한, JSP는 의존성 문제가 생길 수도 있다.

스프링부트는 독립적으로 실행 가능한 내장 톰캣으로 빠르게 앱을 배포하길 원한다. JSP는 이러한 스프링부트의 지향점과 어긋나기 때문에 스프링부트에서 권장하지 않는다.

Thymleaf

Thymleaf에 대해서 살펴보자. Thymleaf는 비교적 최근에 만들어진 템플릿 엔진이다.

실습

먼저 컨트롤러 테스트 코드를 작성해보자.

@RunWith(SpringRunner.class)
@WebMvcTest(SampleController.class)
public class SampleControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void hello() throws Exception {
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andDo(print())
                .andExpect(view().name("hello"))
                .andExpect(model().attribute("name", is("jch")));
    }
}

hello를 매핑하는 컨트롤러를 작성하지 않았기 때문에 테스트는 실패한다. 컨트롤러를 작성하자.

@Controller
public class SampleController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("name", "jch");
        return "hello";
    }
}

이번에는 thymleaf에서 템플릿 파일을 찾지 못한다는 이유로 org.thymeleaf.exceptions.TemplateInputException 예외가 발생한다. src/main/resources/templates 폴더에 hello.html을 생성하자.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${name}">Name</h1>
</body>
</html>

이제 테스트를 실행해보자.

뷰의 이름이 hello이고, 모델의 name 속성에 jch가 있기 떄문에 테스트가 통과한다. print()의 출력 내용을 살펴보면 응답의 Body로 랜더링되는 결과인 템플릿 파일의 내용을 확인할 수 있다.

템플릿 엔진을 사용하면 이렇게 본문을 확인할 수 있다. 하지만, JSP를 사용하면 이렇게 랜더링 결과인 본문을 확인하기가 굉장히 힘들다.

서블릿 엔진은 랜더링을 처리하며 템플릿을 완성한다. JSP를 사용한다면 JSP 스펙을 구현한 서블릿 엔진을 사용해야 한다.

반면, Thymleaf는 서블릿 컨테이너가 개입하지 않는 서블릿 컨테이너에 독립적인 엔진이다. Thymleaf는 독자적으로 최종적인 view를 완성하며 랜더링되는 결과까지도 확인할 수 있다.

참고로 MockMvc는 실제 서블릿 컨테이너를 띄우지 않는다. [Springboot] 테스트와 테스트 유틸 참조

다시 테스트로 돌아가서 응답 본문을 테스트해보자.

@Test
public void hello() throws Exception {
    mockMvc.perform(get("/hello"))
        .andExpect(status().isOk())
        .andDo(print())
        .andExpect(view().name("hello"))
        .andExpect(model().attribute("name", is("jch")))
        .andExpect(content().string(containsString("jch")));
}

응답 본문에 문자열 "jch"가 포함되어 있는지 테스트한다. h1태그에서 model의 name 속성을 출력하기 때문에 테스트는 통과한다.

이처럼 랜더링 결과를 쉽게 테스트할 수 있긴 하지만, 전문적인 HTML-UNIT 이라는 테스팅 툴을 사용하면 정확한 테스트가 가능하다. 이번에는 간단하게 테스트해보고 다음 포스팅에서 HTML-UNIT에 대해 살펴보자.

참고

Thymleaf는 Getting started with the Standard dialects in 5 minutes예제 를 참고하여 개발하면 된다. Thymleaf는 html이기 때문에 웹서버를 돌리지 않아도 확인이 가능하다.

해당 포스팅은 스프링 부트 개념과 활용 강의 내용을 토대로 작성하였습니다.