[Spring Boot 3] 백엔드 개발자도 UI 짤 수 있다?! ✉️ Thymeleaf로 이메일 전송 폼 & API 구현하기

송하연·2024년 4월 2일
1

스프링부트🌱

목록 보기
3/8
post-thumbnail

1. 서론

"백엔드 개발자한테 UI가 왜 필요한데?!"
백엔드 개발을 하다보면 주로 칙칙한 검은 화면이나 Postman을 통해 내가 구현한 API를 테스트하는 경우가 많다,,, 하지만 UI를 활용하여 시각적으로 표현한다면 API가 동작하는 과정도 더 직관적으로 이해할 수 있을 것이다.

Thymeleaf란 무엇인가

ThymeleafJava 템플릿 엔진으로 주로 스프링 프레임워크에서 HTML, XML, JavaScript, CSS 문서를 처리하는 기능을 수행한다.

이처럼 HTML 템플릿 내에서 자바 코드를 자연스럽게 통합할 수 있는 기능을 제공하여 개발자가 웹 어플리케이션을 유연하게 구성할 수 있도록 한다.

Thymeleaf의 기본적인 문법 알아보기

thymeleaf 공식 문서

1. 템플릿 표현식(Expressions): ${...} 형식을 사용하여 변수 표현식 출력

ex)  ${user.name}

2. 속성 바인딩(Attribute Binding): 타임리프 속성을 사용하여 HTML 요소의 속성 값을 동적으로 설정

ex) : <a th:href="@{/home}">Home</a>

3. 반복(Iteration): th:each를 사용하여 리스트나 배열과 같은 컬렉션의 요소를 반복적으로 출력

ex) <li th:each="item : ${items}" th:text="${item}"></li>

4. 조건문(Conditional Statements): th:if, th:unless, th:switch, th:case 등을 사용하여 조건부로 HTML 요소 출력

ex) <div th:if="${user.isAdmin()}">Admin</div>

5. 레이아웃(Layout): 여러 페이지에서 공통된 레이아웃을 정의하고 확장

ex) <div th:replace="layout/header :: header"></div>

6. 이벤트 처리(Event Handling): th:onclick, th:attr 등을 사용하여 클라이언트 측 이벤트 처리

ex) <button th:onclick="'submit()'" th:text="#{button.submit}"></button>
  1. 템플릿 변수(Temporal Variables): th:with를 사용하여 템플릿 내에서 임시 변수 정의
ex) <div th:with="varName=${expression}"></div>

2. 스프링 부트 프로젝트에서 Thymeleaf 활용해서 이메일 전송폼 UI 화면 구현하기

build.gradle에 의존성 추가

스프링부트 웹어플리케이션을 동작하기위한 spring boot web을 포함해서 thymeleafjava mail 관련한 의존성을 추가해주었다.

// spring boot web
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    //thymeleaf
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

    // java mail
    implementation 'org.springframework.boot:spring-boot-starter-mail'

application.yml 설정 추가

application.yml에 필요한 설정을 추가하는데
usernamepassword는 각자에 맞는 값을 입력해주어야 한다.

spring:
  thymeleaf:
    cache: false
    check-template-location: true
    prefix: classpath:/templates/
    suffix: .html

  mail:
    host: smtp.gmail.com
    port: 587
    username: {SMTP 인증에 사용될 이메일 계정 - 발신자}
    password: {SMTP 인증에 사용될 이메일 계정의 password}
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true

각각에 대한 설명의 아래와 같다.

spring.thymeleaf.cache: Thymeleaf 템플릿 캐싱 비활성화
spring.thymeleaf.check-template-location: 템플릿 파일 위치를 확인하여 유효성을 검사
spring.thymeleaf.prefix: Thymeleaf 템플릿 파일이 위치한 디렉토리 경로 지정
spring.thymeleaf.suffix: Thymeleaf 템플릿 파일의 확장자 지정

spring.mail.host: 이메일을 보낼 SMTP 서버의 호스트 주소 지정
spring.mail.port: SMTP 서버의 포트 번호 지정
spring.mail.username: SMTP 인증에 사용될 이메일 계정 지정 (발신자)
spring.mail.password: SMTP 인증에 사용될 이메일 계정의 password 지정
spring.mail.properties.mail.smtp.auth: SMTP 인증 활성화
spring.mail.properties.mail.smtp.starttls.enable: STARTTLS를 통한 보안 연결 활성화

html 파일 만들기(디렉토리 구조)

위 설정에서 템플릿 파일을 프로젝트의 classpath에 있는 templates 디렉토리에서 찾게 지정해두었다.

따라서src/main/resources/templates 디렉토리 안에 index.html를 만들었다.

이미지도 사용해보기 위해 src/main/resources/static 디렉토리 안에 images 디렉토리를 만들고 사용할 이미지 파일을 넣어주었다.

thymeleaf를 활용하여 html 작성하기 : 이메일 입력 폼 UI 구현

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>입력 폼</title>

    <style>
        .container {
            width: 300px;
            margin: 0 auto;
            padding: 20px;
            border: 1px solid #ccc;
            border-radius: 5px;
            background-color: #f9f9f9;
        }
        .subject-group{
            text-align: center;
        }
        .form-group {
            margin-bottom: 10px;
        }
        label {
            display: block;
            font-weight: bold;
        }
        input,
        textarea {
            width: calc(100% - 10px);
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 3px;
            box-sizing: border-box;
            margin-top: 3px;
        }
        button{
            width: 100%;
            padding: 10px;
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 16px;
        }
    </style>

</head>
<body>
<div class="container" >
    <div class="subject-group">
    <h1>입력 폼</h1>
    <img th:src="@{/images/email.png}" style="height:60px" alt="email"/>
    </div>

    <form th:action="@{/submit}" method="post">
        <div class="form-group">
            <label for="name">이름:</label>
            <input type="text" id="name" name="name">
        </div>
        <div class="form-group">
            <label for="email">이메일:</label>
            <input type="email" id="email" name="email">
        </div>
        <div class="form-group">
            <label for="message">메시지:</label>
            <textarea id="message" name="message"></textarea>
        </div>
        <button type="submit">전송</button>
    </form>
    <div th:if="${successMessage}" class="success-message" th:text="${successMessage}"></div>
</div>
</body>
</html>

스타일 관련 속성은 css 문서를 따로 만들어서 관리해도 되지만 <style> 태그를 사용해서 html문서 안에 같이 써주었다.


아래는 Thymeleaf를 사용하여 작성된 HTML 템플릿 파일에 대한 설명이다.

<html xmlns:th="http://www.thymeleaf.org">
// hymeleaf 네임스페이스를 사용하기 위해 HTML 문서에 선언
<img th:src="@{/images/email.png}" style="height:60px" alt="email"/>
// 이미지를 표시하는 <img> 요소 정의
<form th:action="@{/submit}" method="post"> 
// 폼 요소의 액션을 지정하고 HTTP 메소드를 POST로 설정. 
// @{/submit}는 컨텍스트 상대 URL로, /submit으로 요청을 보낼 것을 의미.
<input type="text" id="name" name="name">
// 이름을 입력하는 텍스트 필드를 정의. id와 name 속성을 사용하여 식별.
<button type="submit">전송</button>
///폼을 제출하는 버튼 정의. 이 버튼을 클릭하면 폼 데이터가 서버로 전송된다.
<div th:if="${successMessage}" class="success-message" th:text="${successMessage}"></div>
// 요청 성공 시 성공 메시지를 출력하는 <div> 요소를 정의

html 파일 실행해보기

작성한 HTML 템플릿을 실행해보기 위해 컨트롤러를 작성하여 index.html 파일을 웹에 띄워보았다.

package com.example.thymeleaf;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.ui.Model;

@RequiredArgsConstructor
@Controller
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/")
    public String index(){
        return "index";
    }

이제 http://localhost:8080/에 접속해보자. 입력 폼이 잘 뜨는 것을 확인할 수 있다.

3. 스프링 부트 프로젝트에서 이메일 전송 API 구현하기

만들어놓은 입력폼이 동작하도록 이메일 전송 API를 구현해보자.

thymeleaf를 활용하여 입력폼에서 전송 버튼을 클릭하면 폼 데이터가 서버로 전송되며 /submit으로 요청보내도록 HTML 문서를 작성하였다.
따라서 /submit 엔드포인트에 대한 컨트롤러와 서비스 코드를 작성해야한다.

Message 컨트롤러 코드 작성

package com.example.thymeleaf;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.ui.Model;

@RequiredArgsConstructor
@Controller
public class MessageController {

    private final MessageService messageService;

    @GetMapping("/")
    public String index(){
        return "index";
    }

    @PostMapping("/submit")
    public String submitForm(String name, String email, String message, Model model) {
        // 이메일 보내기
        messageService.sendEmail(email, "새로운 메시지가 도착했습니다.", message);

        // 성공 메시지
        model.addAttribute("successMessage", "메시지가 성공적으로 전송되었습니다.");
        return "index";
    }
}

Message 서비스 코드 작성

JavaMailSender를 주입받아 이메일을 보내는 sendEmail 메서드를 구현하였다.
이메일의 수신자, 제목, 내용을 설정하여 이메일을 전송하고 있다.

package com.example.thymeleaf;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@RequiredArgsConstructor
@Service
public class MessageService {

    private final JavaMailSender emailSender;

    public void sendEmail(String to, String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to); // 보낼 대상
        message.setSubject(subject); // 제목
        message.setText(text); // 내용
        emailSender.send(message);
    }
}

이메일 전송 기능 테스트

값을 넣고 전송 버튼을 눌러보자
❗이 때 이메일은 실제로 유효한 이메일로 바꿔서 입력해줘야한다❗

실행 결과는?!

❌❌❌ 인증 관련한 에러가 발생하였는데 이메일을 전송하기 위한 계정의 인증에 실패했다는 것을 의미한다. => Google의 보안 정책으로 인해 액세스를 허용을 거부당한 것이다.

인증 관련 에러 해결 방법

구글 계정 설정하러가기 링크 클릭 > 발신자로 설정했던 구글 계정으로 로그인 > 보안 클릭 > 보안 수준이 낮은 앱의 액세스 사용으로 변경

성공 화면 ✉️

인증 관련 이슈를 해결한 후 다시 실행해보자

그러면 해당 이메일 계정과 내가 설정한 제목과 내용으로 이메일이 전송되는 것을 확인할 수 있다

전송이 완료된 후 입력 폼에서도 성공 메세지를 확인할 수 있다!

4. 마무리

오늘은 Thymeleaf를 사용하여 스프링부트 프로젝트 내에서 사용자 인터페이스이메일을 전송하는 API를 구현해보았다 ✨✨

추가적인 활용 방안
회원 가입 시 이메일 인증, 소셜로그인 UI 화면 구성 및 기능 구현하는 데 활용할 수 있다!

Thymeleaf를 활용한 이메일 입력 폼 UI & API 구현 깃허브 보러가기

0개의 댓글