[Thymeleaf] 텍스트 -text, utext

2025. 9. 11. 17:45·타임리프

웹 애플리케이션을 만들 때 서버에서 가져온 데이터를 화면(HTML)에 보여주는 것은 가장 기본적인 기능이다. 예를 들어 로그인한 사용자의 이름, 상품명, 게시글 제목 같은 것들이 그렇다.

 

스프링에서 뷰 템플릿 엔진으로 타임리프(Thymeleaf) 를 사용하면, 컨트롤러에서 넘겨준 데이터를 HTML에 아주 쉽게 출력할 수 있다. 이번 글에서는 타임리프의 가장 기본 기능인 텍스트 출력을 어떻게 하는지, 그리고 여기서 발생하는 이스케이프(escape) 개념과 th:text, th:utext의 차이를 정리해본다.

 

1. 컨트롤러에서 데이터 담기

스프링 컨트롤러는 Model 객체를 통해 데이터를 뷰에 전달한다.

@Controller
@RequestMapping("/basic")
public class BasicController {

    @GetMapping("/text-basic")
    public String textBasic(Model model) {
        // "data"라는 이름으로 문자열 데이터를 담는다
        model.addAttribute("data", "Hello Spring!");
        // templates/basic/text-basic.html 로 이동
        return "basic/text-basic";
    }
}

여기서 "data"라는 키에 "Hello Spring!" 값이 담겨 뷰 템플릿으로 넘어간다.

 

2. 뷰 템플릿에서 출력하기

넘겨받은 데이터를 HTML에 출력하려면 타임리프 문법을 사용한다.

resources/templates/basic/text-basic.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>text-basic</title>
</head>
<body>
  <h1>데이터 출력하기</h1>
  <ul>
    <!-- 속성 방식 -->
    <li>th:text 사용 → <span th:text="${data}"></span></li>
    <!-- 인라인 방식 -->
    <li>인라인 출력 → [[${data}]]</li>
  </ul>
</body>
</html>
  • th:text : HTML 속성으로 데이터를 넣는 방식
  • [[...]] : 콘텐츠 영역에 바로 데이터를 출력하는 방식

실행 결과: 브라우저 화면에 Hello Spring! 이 정상적으로 보인다.

 

3. 그런데 문제가 생긴다 – 이스케이프

데이터가 단순 텍스트라면 문제가 없지만, 만약 데이터 안에 HTML 태그가 들어 있다면 어떻게 될까?

@GetMapping("/text-unescaped")
public String textUnescaped(Model model) {
    model.addAttribute("data", "Hello <b>Spring!</b>");
    return "basic/text-unescaped";
}

개발자는 <b> 태그가 적용돼 "Spring!" 글자가 굵게 보이기를 기대한다.
하지만 타임리프는 기본적으로 이스케이프 처리를 한다.

  • 화면: Hello <b>Spring!</b> (굵게 적용되지 않음)
  • 페이지 소스: Hello &lt;b&gt;Spring!&lt;/b&gt;

즉, < 와 > 같은 특수 문자를 HTML 엔티티로 변환해 태그가 실행되지 않고 글자로 그대로 표시된다.

 

 

왜 이렇게 동작할까?

 

(1). 보안을 위해서

브라우저는 <script> ... </script> 같은 태그를 만나면 자바스크립트 코드를 실행한다.
만약 누군가 입력칸에 이런 코드를 써서 서버로 보낸 뒤, 그게 그대로 실행된다면?
화면에 해킹 코드가 실행될 수 있다. (예: 알림창 뜨기, 쿠키 빼가기 등)

 

그래서 타임리프는 < 같은 기호를 그냥 “문자”로 바꿔서 보여준다.
즉, <script>를 “<script>” 라는 글자로 보여주지, 실제로 실행되지 않게 막아준다.

 

(2). 화면이 깨지지 않도록

HTML은 <태그>로 구조를 짠다.
만약 데이터 안에 <div> 같은 게 들어가 있으면, 원래 작성한 HTML 구조가 망가질 수도 있다.

예를 들어, 원래는 <ul><li>안녕</li></ul> 인데, 데이터가 <li>악의적인 태그</li> 라면?
리스트 구조가 꼬이면서 의도치 않은 화면이 나올 수 있다.

 

타임리프는 이런 문제를 막으려고, < 와 > 같은 기호를 “특수 문자(HTML 엔티티)”로 변환한다.
그래서 브라우저는 태그로 해석하지 않고, 그냥 글자로만 보여준다.

 

 

4. 해결 방법 – th:utext

HTML 태그까지 실행해서 보여주고 싶을 때는 th:utext 를 사용한다.

resources/templates/basic/text-unescaped.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>text-unescaped</title>
</head>
<body>
  <h1>text vs utext</h1>
  <ul>
    <li>th:text  → <span th:text="${data}"></span></li>
    <li>th:utext → <span th:utext="${data}"></span></li>
  </ul>

  <h1><span th:inline="none">[[...]] vs [(...)]</span></h1>
  <ul>
    <li>[[...]] = [[${data}]]</li>
    <li>[(...)] = [(${data})]</li>
  </ul>
</body>
</html>
  • th:text, [[...]] : 이스케이프 처리됨 (안전)
  • th:utext, [(...)] : 이스케이프 해제, HTML 태그 적용됨

실행하면 Spring! 이 실제로 굵게 표시된다.

 

하지만 여기서 주의해야 한다.

왜냐하면 th:utext는 데이터를 있는 그대로 HTML로 실행하기 때문이다.

  • 만약 데이터 안에 <script>...</script> 같은 코드가 있으면 그대로 실행돼서 해킹 문제가 생길 수 있다.
  • 또, <div>나 <table> 같은 태그가 들어가 있으면 원래 화면 구조가 망가질 수 있다.

그래서 기본은 무조건 th:text 를 쓰는 게 안전하다.
단순히 글자를 굵게 보이고 싶으면, 데이터 자체에 <b>를 넣는 대신 이렇게 하면 된다.

<strong th:text="${data}"></strong>

이렇게 하면 데이터는 안전하게 문자 그대로 출력되면서, <strong> 태그 덕분에 굵게 표시된다.

 

마무리하며

오늘은 백엔드에서 데이터를 준비하고, 타임리프를 통해 화면에 보여주는 방법을 처음부터 차근차근 알아보았다.

 

가장 먼저 컨트롤러에서 Model 객체에 데이터를 담아 뷰로 넘겨주었다. 그리고 뷰 템플릿에서는 th:text와 [[...]]를 사용해 이 데이터를 HTML에 출력했다. 이렇게 하면 서버의 값이 화면에 안전하게 표시된다.

 

그런데 데이터 안에 HTML 태그가 들어 있는 경우에는 문제가 생겼다. <b> 태그처럼 글자를 굵게 만드는 태그도, 타임리프가 기본적으로는 그대로 글자로만 보여주기 때문이다. 이 과정을 이스케이프(escape) 라고 부른다.

 

이렇게 동작하는 이유는 두 가지였다.

  • 첫째, 해킹 같은 보안 문제를 막기 위해서다. <script> 코드가 그대로 실행되면 큰 위험이 생길 수 있다.
  • 둘째, HTML 구조가 깨지는 것을 막기 위해서다. 데이터 속 태그가 의도치 않게 HTML 구조에 섞이면 화면이 엉망이 될 수 있다.

이 문제를 해결하려면 th:utext나 [(...)]를 쓰면 된다. 이 방법은 데이터 안에 있는 태그를 실제로 실행시켜 화면에 적용한다. 그래서 <b> 태그가 있으면 글자가 굵게 표시된다. 하지만 이 방식은 조심해서 써야 한다. 사용자 입력 같은 신뢰할 수 없는 데이터에는 절대 쓰면 안 되고, 반드시 안전한 경우에만 사용해야 한다.

 

결국 정리하면 이렇게 생각하면 쉽다:

  • 기본 출력은 무조건 th:text, [[...]] 로 한다.
  • 글자를 꾸미고 싶으면, 데이터 안에 태그를 넣는 대신 <strong>이나 <span> 태그, CSS 스타일을 사용한다.

감사합니다.

'타임리프' 카테고리의 다른 글

[Thymeleaf] 리터럴  (1) 2025.09.15
[Thymeleaf] URL링크  (0) 2025.09.15
[Thymeleaf] 유틸리티 객체와 날짜  (0) 2025.09.15
[Thymeleaf] 변수 -SpringEL  (0) 2025.09.11
타임리프(Thymeleaf)에 대하여  (0) 2025.09.11
'타임리프' 카테고리의 다른 글
  • [Thymeleaf] URL링크
  • [Thymeleaf] 유틸리티 객체와 날짜
  • [Thymeleaf] 변수 -SpringEL
  • 타임리프(Thymeleaf)에 대하여
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    최적화
    백준
    쿼리dsl
    mvc
    thymeleaf
    타임리프
    fetch join
    자바
    코딩 테스트
    SpringDataJpa
    LocalDateTime
    스프링
    SOLID
    spring
    컬렉션
    재귀
    예외 처리
    코딩테스트
    객체지향
    예외처리
    Java
    다형성
    쿼리
    불변객체
    스프링 컨테이너
    dfs
    스프링 데이터 JPA
    JPA
    QueryDSL
    BFS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[Thymeleaf] 텍스트 -text, utext
상단으로

티스토리툴바