0. 왜, 지금, Thymeleaf?
사실 나도 Thymeleaf를 열렬히 선호하는 팬이 아니며, 그렇게 많이 사용하지도 않았다.
하지만 근래에 2개의 기업에서 채용 절차를 진행하던 중에 과제를 요구받았고,
이 과제들은 Backend 개발자 전형이었기 때문에 딱히 client를 요구하지는 않았지만,
내가 구현해낸 어떤 API들을 간단하게라도 출력해줄 수 있는 화면이 있었으면 좋겠다는 생각이 들었다.
그리고 이를 수행해내기에 안성맞춤인 도구가 바로 Thymeleaf였다.
그리고 현업에서도 admin 페이지를 만들 때 Thymeleaf를 활용한다고도 하니,
이번 기회에 한번쯤 정리해두면 좋겠다는 생각이 들었다.
이번 포스팅에선 Thymeleaf의 기본적인 사용법을 다시금 간략하게 정리하고,
또한 과제 수행 중에 겪었던 몇몇 에러들을 해결했던 방법에 대해 다뤄보고자 한다.
1. IntelliJ IDEA에서 나타나는 '할당되지 않은 변수' 에러
위에 보이는 submitters라는 변수는 분명히 controller 단에서 model.addAttribute를 통해 전달해준 변수이지만,
해당 html 파일에서는 당장 원하는 변수를 찾아낼 수 없기 때문에 컴파일 에러가 발생한다.
기능상의 문제는 없지만 빨간 줄들이 눈엣가시다.
이 문제는 맨 위에 thymeleaf를 설정하는 부분을 이상하게(?) 바꿔주면 해결이 되었다.
이렇게 하면 thymeleaf 문법 체크를 해주진 않지만 적어도 컴파일 에러는 발생하지 않는다.
그리고 역시 기능상의 문제는 없었다.
컴파일 에러 쪽이 훨씬 더 무섭기 때문에 thymeleaf 파일들은 전부 이렇게 작업을 해주었다.
2. if문과 for문, 그리고 href
과제 중에 조회 조건을 2가지로 나누되, 이 2가지 모두 한 html 페이지에 표현할 수 있도록 구현한 적이 있었는데,
이 html 안에 간단한 페이징도 구현을 했기 때문에 if문과 for문을 함께 활용한 예시가 탄생했다.
model을 통해 주입 받은 searchingCondition에 따라 다른 페이징 정보를 가질 수 있게 했다.
if문을 사용하기 위해선 th:if="${}" 안에 원하는 조건을 넣으면 된다.
그렇게 하면 해당 태그는 해당 조건이 맞아야만 표시되는 태그가 된다.
그리고 2개의 조건 사이에 and를 발견할 수 있는데, 이처럼 and나 or를 사용해서
if문의 && 조건이나 || 조건 또한 간단하게 구현할 수 있음을 확인할 수 있다.
th:each 부분은 반복문이다.
원래는 주로 이렇게 List를 받아 순회하면서 그 안의 속성값들을 출력하는 데에 사용했지만,
이번 과제를 기점으로 totalPages 같은 정수 값을 넘겨 받아서
for (int i = 0; i < pages; i++) 같은 느낌으로도 사용할 수 있음을 알게 되었다.
th:href="@{}"는 기존의 링크를 대체한다.
경로 뒤에 ()를 붙여서 안에 필요한 파라미터 값을 model을 통해 주입 받은 변수나 자체 변수로 넣어줄 수 있다.
3. th:field와 th:value를 한 태그 안에 쓰면 안돼!
removeProduct라는 DTO에서 customerId와 productName이라는 파라미터를 갖고 있었기 때문에
같은 이름의 필드로 연동해주고, 해당 필드에 대입하는 값은 controller단에서 model을 통해 부여해주는 값으로 잡았다.
그랬더니 이런 에러가 발생했다.
에러만 보고는 갈피가 안 잡혔기 때문에 몇 시간 정도 소모했던 걸로 기억한다.
결론부터 말하자면 th:field라는 친구는 id, name, value, field 등을
한번에 묶어서 사용하게 해주기 때문에 발생한 에러이다.
에러 문구를 잘 살펴보면 customerId는 같은 문자열을 넣어주었기 때문에 별 상관이 없다는 듯이 넘어갔지만,
내가 대입하고자 하는 필드 이름과, model로부터 전달 받은 값이 달랐던 productName 쪽은
나의 잘못된 thymeleaf 사용에 대해 친히 에러 메시지를 내어주셨다.
해결 방법도 단도직입적으로 말하자면, 그냥 둘 중 하나를 쓰지 않으면 된다.
필자는 처음에 DTO의 필드들에 대응하는 같은 이름을 가진 요소들이 필요하다고 생각했는데,
굳이 그렇게 하지 않아도 필드들이 자동으로 대입되었다. 😅
4. EC2에서 gradlew로 빌드해서 실행하니까・・・
TemplateInputException: Error resolving template [/index], template might not exist or might not be accessible by any of the configured Template Resolvers
이런 에러가 발생했다.
해결 방법은 무척이나 간단했다.
이런 방식으로 구성되어 있던 Controller를・・・
이런 식으로 return하는 문자열 앞에 / 를 제거해주니 해결되었다.
앞으로도 thymeleaf로 구성한 뷰 템플릿을 배포할 때는 반드시 유의해야만 하겠다.
5. POST 요청 후 결과를 Alert 창으로 띄워주고 싶어!
이 작업을 위해선 JavaScript를 활용한 작업이 필요했지만 그다지 어려운 난이도의 것은 아니었다.
우선은 요청에 대한 응답을 ResponseEntity의 body에 담아줄 수 있도록 설계했다.
이렇게 받은 응답값을 ModelAndView의 addObject를 통해 담아주고,
setViewName을 통해 이동할 html 문서를 지정해주었다.
그리고 submit-result.html 페이지를 다음과 같이 작성해주었다.
thymeleaf를 통해, mode.addAttribute로 받은 것과 동일하게 변수명으로 메시지를 받아내고,
<script> 태그 안에 JavaScript 문법으로 alert 창을 메시지와 함께 띄웠다.
그리고 alert 창을 닫았을 때의 경로는 index 페이지로 지정했다.
실행 결과 다음과 같이 alert 창이 정상 작동했다!