-
최종 프로젝트 - Thymeleaf XML 설정(기본, 이메일)프로그래밍/프로젝트 2019. 7. 21. 01:08
깃허브 주소: https://github.com/LuiGee3471/BitCampFinal
LuiGee3471/BitCampFinal
Contribute to LuiGee3471/BitCampFinal development by creating an account on GitHub.
github.com
이번 프로젝트에서는 Thymeleaf를 사용 중입니다. JSP에서 금방 넘어가고 있습니다. HTML 구조를 반드시 만들어줘야하다보니 가끔 JSP와 EL-JSTL의 결합보다 불편할 때도 있긴 하지만 공식 문서도 친절하고 문법도 비슷해서 잘 적응해서 쓰고 있습니다.
다만, 이번 프로젝트에서는 XML을 이용해서 스프링 프로젝트 설정을 해주고 있는데 공식 문서나 인터넷 상 정보가 거의 Java 코드 설정으로 되어 있어서 불편할 때가 있었는데요. 저를 위한 일종의 아카이브기도 하고 한 분이라도 같은 경험을 하시면 필요할 것 같아서 이번 프로젝트 때 사용한 XML 설정을 공유하고 설명해볼까 합니다.
1. pom.xml
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring4</artifactId> <version>3.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> <version>3.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> <version>3.0.4.RELEASE</version> </dependency>
Maven 프로젝트에서 다음과 같이 dependency를 설정해줬습니다. Gradle을 사용할 때도 사용하는 것은 같겠죠. 위부터 설명하면 먼저 Thymeleaf의 코어 라이브러리입니다. 가장 핵심이기 때문에 반드시 필요합니다. 두 번째는 스프링 4버전을 사용하기 때문에 필요한 spring4 통합 모듈입니다. 스프링 3, 4, 5버전에 맞는 통합 모듈이 있으니 자신의 프로젝트에 맞는 버전을 사용하시면 됩니다.
세 번째는 Spring Security와 통합하기 위해 사용하는 모듈입니다. 역시 버전에 따라 다르기 때문에 자신이 사용하는 버전에 맞는 것을 사용해주시면 됩니다. Security 기능을 사용하지 않으실 거면 필요 없습니다. 마지막은 Java 8에 추가된 java.time API 사용을 위한 통합 모듈입니다. Thymeleaf는 원래 Date 객체와 Calendar 객체를 지원해주는데요. LocalDate와 LocalDateTime을 사용하려면 이 모듈이 필요합니다. 1차 프로젝트 때 유용함을 느껴서 이 모듈을 설치했고 역시 이를 사용할 생각이 없으시면 필요 없습니다.
2. Dispatcher Servlet 설정
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="characterEncoding" value="UTF-8" /> </bean> <bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML" /> <property name="characterEncoding" value="UTF-8" /> <property name="cacheable" value="false" /> </bean> <bean id="emailTemplateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/mail/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML" /> <property name="characterEncoding" value="UTF-8" /> <property name="cacheable" value="false" /> </bean> <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver"> <ref bean="templateResolver" /> </property> <property name="additionalDialects"> <set> <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect" /> <bean class="org.thymeleaf.extras.java8time.dialect.Java8TimeDialect" /> </set> </property> </bean> <bean id="emailTemplateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver" ref="emailTemplateResolver" /> </bean>
이번 프로젝트에서 사용하고 있는 dispatcher-servlet에서 Thymeleaf의 부분만 가져왔습니다. 뷰 리졸버에 Thymeleaf를 사용하고 이메일을 위한 템플릿 엔진 역시 Thymeleaf를 사용하고 있습니다. 나눠서 살펴보겠습니다.
2-1. 기본 뷰 설정
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML" /> <property name="characterEncoding" value="UTF-8" /> <property name="cacheable" value="false" /> </bean>
먼저 템플릿 리졸버입니다. prefix에는 파일 앞에 붙을 접두어를, suffix에는 파일 뒤에 붙을 접미어를 써놓습니다. 이 프로젝트에서 모든 컨트롤러가 반환하는 String이나 View는 이 접두어와 접미사가 붙은 파일을 찾아갈 것입니다. 즉, 컨트롤러의 함수가 home을 반환하면 실제로는 /WEB-INF/views/home.html 파일을 사용자에게 보여줍니다. templateMode는 HTML이라고 명시해주는 역할입니다. (Thymeleaf는 HTML만 사용 가능한 것은 아니기 때문입니다.) 문자 인코딩을 UTF-8로 설정해주었습니다. 마지막 cacheable은 캐시 기능을 활성화시킬지 유무인데, 기본은 true입니다. 캐시 기능이 켜져있으면 변경 사항이 있을 때만 페이지를 다시 불러오려는 시도를 합니다.
저희는 개발 중이어서 작은 변경 사항이 매번 있기 때문에 false로 해놓았습니다. 개발 중에는 false로 하다가 배포 시에 true로 바꾸면 괜찮을 것 같습니다.
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver"> <ref bean="templateResolver" /> </property> <property name="additionalDialects"> <set> <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect" /> <bean class="org.thymeleaf.extras.java8time.dialect.Java8TimeDialect" /> </set> </property> </bean>
템플릿 리졸버를 작성했으면 적용시켜야겠죠. 다음과 같이 템플릿 엔진을 작성합니다. 여기서 중요한 것은 아래쪽 additionalDialects 부분인데요. 이 부분에서 저렇게 해주지 않으면 pom.xml에서 설정해놓아도 작동하지 않습니다. 이걸 몰라서 분명히 설치했는데 왜 작동을 안하지 하면서 엄청 고민했습니다. 저렇게 작동을 하면 다음 기능을 사용할 수 있습니다.
<!-- MANAGER라는 권한을 가진 사람을 제외하고 보임 --> <div class="nav-menu" sec:authorize="!hasRole('MANAGER')"> <!-- message 객체의 timeLocal이라는 LocalDateTime 객체를 yyyy-MM-dd HH:mm 형태로 표시 --> <span class="message-time" th:text="${#temporals.format(message.timeLocal, 'yyyy-MM-dd HH:mm')}"> 2019-07-20 20:39 </span>
이렇게 사용할 수 있습니다. 시간 부분은 어떻게든 다른 방식으로 구현할 수 있지만 Spring Security는 Thymeleaf와 사용하기 위해서는 이 방법 밖에는 없기 때문에 중요합니다.
<bean id="viewResolver" class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="characterEncoding" value="UTF-8" /> </bean>
마지막으로 뷰 리졸버를 위에서 만든 템플릿 엔진을 설정해주고 만들어주면 기본 뷰를 담당하는 Thymeleaf 설정은 끝나게 됩니다.
2-2. 이메일 템플릿 설정
이메일 기능을 Thymeleaf와 사용하고 싶어서 봤는데 어디에서도 XML 설정을 찾을 수 없었습니다. 공식 홈페이지가 제공하는 샘플에서 Java 이용 설정과 기존에 만들어놓은 자료를 참고해서 설정한 방법입니다.
<bean id="emailTemplateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/mail/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML" /> <property name="characterEncoding" value="UTF-8" /> <property name="cacheable" value="false" /> </bean> <bean id="emailTemplateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine"> <property name="templateResolver" ref="emailTemplateResolver" /> </bean>
사실 위의 방법과 완전히 동일합니다. 아예 이렇게 별도의 resolver로 만들어주면 됩니다. 위의 템플릿 리졸버와는 다르게 접두어가 "/WEB-INF/mail"로 바뀐 것이 보이실 겁니다. 물론 이 폴더 위치나 명칭은 여러분 마음대로입니다. 이러면 이제 메일을 보낼 때 이름을 지정해주면 저 폴더 안의 이름을 가진 html 파일을 찾아볼 것입니다.
템플릿 엔진도 별도로 만들어서 사용하면 됩니다. 처음에는 하나의 템플릿 엔진으로 둘다 하려고 했는데 작동하지 않더라고요. 이메일을 사용할 때는 저 템플릿 엔진을 사용하시면 됩니다.
package kr.or.bit.service; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; import kr.or.bit.model.Member; @Service public class MailService { // 메일 보내는 객체 @Autowired private JavaMailSenderImpl mailSender; // 위에서 만든 이메일용 템플릿 엔진 @Autowired private TemplateEngine emailTemplateEngine; public void sendNewTeacherEmail(String to, String name) throws MessagingException { // 이메일 문서에 값을 전달하기 위한 객체 Context context = new Context(); context.setVariable("name", name); String from = "보내는 사람의 메일 주소"; MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true, "utf-8"); helper.setFrom(from); helper.setTo(to); helper.setSubject("메일 제목"); String htmlContent = emailTemplateEngine.process("newMail", context); helper.setText(htmlContent, true); mailSender.send(message); } }
위에서 만든 빈 객체를 사용하는 방법입니다. 좀 불분명한 타입들이 있을 것 같아서 일부러 패키지명이 모두 나오는 전체 코드를 가져왔습니다. Java를 이용해 메일을 보내는 방법은 검색해보면 많이 나오니 여기서는 그 설명은 최소화하겠습니다.
우선 org.thymeleaf.context.Context 객체는 이메일 문서에 값을 전달하기 위한 객체입니다. 일반적인 문서에서 HttpServletRequest 객체를 통해 값이나 객체를 전달하는 것과 비슷합니다. 다만, 이메일은 직접 페이지를 넘어가는 것이 아니기 때문에 이렇게 별도로 전달해줘야 합니다. 여기서는 받는 사람의 이름(name)을 저장해서 넘겨주기로 했습니다.
메일을 받는 주소와 보내는 주소를 설정하고 제목까지 설정한 후에 메일의 내용을 설정해주는 단계에서 아까 만든 emailTemplateEngine을 사용합니다. Spring이니 반드시 변수의 이름은 XML에서 설정해준 id 값과 같아야 합니다. process 함수에 emailTemplateResolver에서 지정해준 폴더와 파일을 찾아갈 수 있도록 문자열을 넘겨주고 위에서 만든 context까지 넘겨줍니다. 그 결과를 문자열로 받고 setText 함수에 전달, HTML 형태이므로 true도 같이 전달해줍니다.
그리고 보내주면 메일이 완성됩니다.
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h3>가입을 축하합니다!</h3> <p>안녕하세요, <span th:text="${name}">강사</span> 회원님!</p> <p>회원가입해주셔서 감사합니다.</p> </body> </html>
위에서 찾아가는 /WEB-INF/mail/newMail.html은 이렇게 생겼습니다. 보시듯이 평범하게 쓰는 Thymeleaf와 똑같습니다. Context 객체로 넘어온 값을 사용하기 위해 ${} 문법을 사용하는 것까지 똑같습니다. 원하는 대로 꾸미고 이미지도 넣을 수 있습니다. 이러면 단순 문자열이 아닌 HTML 형식을 가지고 있는 메일이 상대방에게 전달됩니다.
이렇게 이번 프로젝트에서 사용한 Thymeleaf 설정을 같이 살펴봤습니다. 용케 해내기는 했지만 사실 느끼는 점은 빨리 Java로 설정하는 방법을 배워야겠다 싶다는 것입니다. 물론 이번 프로젝트는 일부러라도 XML로 진행하기는 했습니다. 지금부터 Java로만 하면 왠지 XML로 하는 방법을 계속 못배울 것 같다는 생각이 들었기 때문입니다. 혹시나 XML로 Thymeleaf를 사용하는데 잘 작동하지 않아 고민하는 분들에게 도움이 되는 글이면 좋겠네요.
'프로그래밍 > 프로젝트' 카테고리의 다른 글
[Sunlapse] #1 촬영할 시간 구하기 (0) 2020.12.15 [Sunlapse] #0 프로젝트 소개: 매일의 하늘을 타임랩스로 (0) 2020.12.14 최종 프로젝트 - 게시판 페이지 처리 (0) 2019.07.14 최종 프로젝트 - Spring Security (0) 2019.06.29 최종 프로젝트 - 기초 작업 (1) (0) 2019.06.23