Spring

Spring Boot, Thymeleaf, Lombok + 실습

oink24 2023. 9. 25. 11:00

 

 

 

1. Spring Boot란?

  • Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".
  • 기존 Spring Framework를 보다 가볍고 편리하게 개발 할 수 있는 웹 애플리케이션 개발에 최적화 된 프레임워크
  • 복잡한 Spring 설정을 자동화하고 내장 WAS를 통해 단독 웹 애플리케이션으로 실행
  • Spring5 버전 이후로 Spring MVC 는 Spring Boot로 대체

 

 

2. Thymeleaf

  • Thymeleaf is a modern server-side Java template engine for both web and standalone environments.
  • Spring Boot 템플릿 엔진으로 Jsp, Thymeleaf, Freemarker, Mustache, Groovy 등
  • Spring Boot 에서 기본 템플릿 엔진으로 JSP 대신 Thymeleaf 사용을 권장
  • JSP와 달리 Thymeleaf는 온전한 View 컴포넌트로 프론트 개발에 집중

 

 

3. Lombok

  • Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
  • getter, setter, toString 등의 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리
  • 다양한 Annotation을 지원하며 Spring Boot 기반 프로젝트에서 필수로 사용되는 라이브러리

 

 

 


 

 

[실습] Thymeleaf, Lombok 이용한 Spring Boot 실습

1️⃣ 설정파일

1) pom.xml

pom.xml 전체보기
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>kr.ch07</groupId>
    <artifactId>Ch07</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Ch07</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>3.0.2</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/nz.net.ultraq.thymeleaf/thymeleaf-layout-dialect -->
        <dependency>
            <groupId>nz.net.ultraq.thymeleaf</groupId>
            <artifactId>thymeleaf-layout-dialect</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

 

2) application.properties

확장자가 xml이 아닌 properties

context-path 설정을 포함한 기본 설정DataSource 설정, MyBatis 설정, Logger 설정을 해준다.

application.properties 전체보기
# 기본 Setting
spring.devtools.livereload.enabled=true
spring.thymeleaf.cache=false
server.servlet.context-path=/Ch07

# DataSource 설정
spring.datasource.url=jdbc:mysql://IP주소:3306/db명
spring.datasource.username=user
spring.datasource.password=비밀번호
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis 설정
mybatis.mapper-locations=classpath:mappers/**/*.xml

# Logger 설정
logging.level.root=info
logging.level.jdbc.sqlonly=info
logging.level.jdbc.sqltiming=info
logging.level.jdbc.connection=info
logging.level.jdbc.resultsettable=info
logging.level.org.hibernate=info
logging.level.org.springframework.security=info
logging.file.name=log/Ch07.log

 

 

2️⃣ Thymeleaf Layout

<html xmlns:th="http://www.thymeleaf.org">

 

 

3️⃣ Thymeleaf Include

 

 

 

 

4️⃣ Spring Boot CRUD 실습

1) View

Thymeleaf 사용

<html xmlns:th="http://www.thymeleaf.org">

Thymeleaf 기본 출력 두가지 방법
Thymeleaf 조건문
Thymeleaf 반복문
Thymeleaf 링크

 

<a th:href="@{/user6/modify?uid={uid}(uid=${user.uid})}">수정</a>

<label><input type="radio" name="gender" value="1" th:checked="${user.gender eq 1}">남자</label>
list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>user6::list</title>
    </head>
    <body>
        <h3>user6 목록</h3>

        <a th:href="@{/user6/register}">user6 등록</a>
        <form action="/Ch07/user6/register" method="post">
            <table border="1">
                <tr>
                    <td>아이디</td>
                    <td>이름</td>
                    <td>생년월일</td>
                    <td>성별</td>
                    <td>나이</td>
                    <td>주소</td>
                    <td>휴대폰</td>
                    <td>관리</td>
                </tr>
                <tr th:each="user:${users}">
                    <td>[[${user.uid}]]</td>
                    <td>[[${user.name}]]</td>
                    <td>[[${user.birth}]]</td>
                    <td>
                        <span th:if="${user.gender == 1}">남자</span>
                        <span th:if="${user.gender == 2}">여자</span>
                    </td>
                    <td>[[${user.age}]]</td>
                    <td>[[${user.address}]]</td>
                    <td>[[${user.hp}]]</td>
                    <td>
                        <a th:href="@{/user6/modify?uid={uid}(uid=${user.uid})}">수정</a>
                        <a th:href="@{/user6/delete?uid={uid}(uid=${user.uid})}">삭제</a>
                    </td>
                </tr>
            </table>
        </form>
    </body>
</html>
register.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>user6::register</title>
    </head>
    <body>
        <h3>user6 등록</h3>

        <a th:href="@{/user6/list}">user6 목록</a>
        <form action="/Ch07/user6/register" method="post">
            <table border="1">
                <tr>
                    <td>아이디</td>
                    <td><input type="text" name="uid" required></td>
                </tr>
                <tr>
                    <td>이름</td>
                    <td><input type="text" name="name" required></td>
                </tr>
                <tr>
                    <td>생년월일</td>
                    <td><input type="text" name="birth"></td>
                </tr>
                <tr>
                    <td>성별</td>
                    <td>
                        <label><input type="radio" name="gender" value="1" checked="checked">남자</label>
                        <label><input type="radio" name="gender" value="2">여자</label>
                    </td>
                </tr>
                <tr>
                    <td>나이</td>
                    <td><input type="number" name="age"></td>
                </tr>
                <tr>
                    <td>주소</td>
                    <td><input type="text" name="address"></td>
                </tr>
                <tr>
                    <td>휴대폰</td>
                    <td><input type="text" name="hp"></td>
                </tr>
                <tr>
                    <td colspan="2" align="right"><input type="submit" value="등록하기"></td>
                </tr>
            </table>
        </form>
    </body>
</html>
modify.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>user6::modify</title>
    </head>
    <body>
        <h3>user6 수정</h3>

        <a th:href="@{/user6/list}">user6 목록</a>
        <form action="/Ch07/user6/modify" method="post">
            <table border="1">
                <tr>
                    <td>아이디</td>
                    <td><input type="text" name="uid" th:value="${user.uid}" readonly></td>
                </tr>
                <tr>
                    <td>이름</td>
                    <td><input type="text" name="name" th:value="${user.name}" required></td>
                </tr>
                <tr>
                    <td>생년월일</td>
                    <td><input type="text" name="birth" th:value="${user.birth}"></td>
                </tr>
                <tr>
                    <td>성별</td>
                    <td>
                        <label><input type="radio" name="gender" value="1" th:checked="${user.gender eq 1}">남자</label>
                        <label><input type="radio" name="gender" value="2" th:checked="${user.gender eq 2}">여자</label>
                    </td>
                </tr>
                <tr>
                    <td>나이</td>
                    <td><input type="number" name="age" th:value="${user.age}"></td>
                </tr>
                <tr>
                    <td>주소</td>
                    <td><input type="text" name="address" th:value="${user.address}"></td>
                </tr>
                <tr>
                    <td>휴대폰</td>
                    <td><input type="text" name="hp" th:value="${user.hp}"></td>
                </tr>
                <tr>
                    <td colspan="2" align="right"><input type="submit" value="수정하기"></td>
                </tr>
            </table>
        </form>
    </body>
</html>

 


2) Model 및 Controller

Lombok 사용

- DTO

User6DTO.java
package kr.ch07.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class User6DTO {

	private String uid;
	private String name;
	private String birth;
	private int gender;
	private int age;
	private String address;
	private String hp;
}

 

 

 

- Mapper 인터페이스

Mapping 파일에 기재된 SQL을 호출하기 위한 인터페이스

Mapping 파일에 있는 SQL을 자바 인터페이스를 통해 호출할 수 있도록 해줌

이전에 설정한 MyBatis :: application.properties

 

- mapper/User6Mapper.java

User6Mapper.java
package kr.ch07.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import kr.ch07.dto.User6DTO;

@Mapper
public interface User6Mapper {
	
	public void insertUser6(User6DTO dto);
	public User6DTO selectUser6(String uid);
	public List<User6DTO> selectUser6s();
	public void updateUser6(User6DTO dto);
	public void deleteUser6(String uid);
}

 

- mappers/user6.xml

user6.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper 
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.ch07.mapper.User6Mapper">

    <insert id="insertUser6">
        INSERT INTO `user6` VALUES (#{uid}, #{name}, #{birth}, #{gender}, #{age}, #{address}, #{hp});
    </insert>
    <select id="selectUser6" resultType="kr.ch07.dto.User6DTO">
        SELECT * FROM `user6` WHERE `uid`=#{uid};
    </select>
    <select id="selectUser6s" resultType="kr.ch07.dto.User6DTO">
        SELECT * FROM `user6`;
    </select>
    <update id="updateUser6">
        UPDATE `user6` SET
            `name`=#{name},
            `birth`=#{birth},
            `gender`=#{gender},
            `age`=#{age},
            `address`=#{address},
            `hp`=#{hp}
        WHERE
            `uid`=#{uid};	
    </update>
    <delete id="deleteUser6">
        DELETE FROM `user6` WHERE `uid`=#{uid};
    </delete>
</mapper>

 


- DAO

 

- Service

 

 

- MainController.java (→ 아래의 index.html이 view)

초기화 방법에는 생성자를 이용, setter 이용, Builder 이용이 있음

MainController.java
package kr.ch07.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import kr.ch07.dto.User1DTO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class MainController {
	
	@GetMapping(value = {"/", "/index"})
	public String index(Model model) {
		
		String str1 = "스프링부트";
		String str2 = "타임리프";
		
		// 생성자를 이용한 초기화
		User1DTO user1 = new User1DTO("A101", "김유신", "010-1234-1001", 23);
		
		// setter를 이용한 초기화
		User1DTO user2 = new User1DTO();
		user2.setUid("A102");
		user2.setName("김춘추");
		user2.setHp("010-1234-1002");
		user2.setAge(25);
		
		// Builder를 이용한 초기화
		User1DTO user3 = new User1DTO().builder()
									.uid("A103")
									.name("장보고")
									.hp("010-1234-1003")
									.age(34)
									.build();
		
		User1DTO user4 = null;
		
		// List 생성
		List<User1DTO> users = new ArrayList<>();
		users.add(new User1DTO("A101", "김유신", "010-1111-1001", 25));
		users.add(new User1DTO("A102", "김춘추", "010-1111-1002", 35));
		users.add(new User1DTO("A103", "장보고", "010-1111-1003", 44));
		users.add(new User1DTO("A104", "강감찬", "010-1111-1004", 37));
		users.add(new User1DTO("A105", "이순신", "010-1111-1005", 50));
		
		model.addAttribute("str1", str1);
		model.addAttribute("str2", str2);
		model.addAttribute("user1", user1);
		model.addAttribute("user2", user2);
		model.addAttribute("user3", user3);
		model.addAttribute("user4", user4);
		model.addAttribute("users", users);
		
		log.info(user1.toString());
		log.info(user2.toString());
		log.info(user3.toString());
		
		return "/index";
	}
	
	@GetMapping("/include")
	public String include(String name, int age) {
		
		log.info("name : " + name);
		log.info("age : " + age);
		
		return "/include";
	}
	
	@GetMapping("/layout")
	public String layout() {
		return "/layout";
	}
}

 

 

- index.html

index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Insert title here</title>
    </head>
    <body>
        <h3>Ch07. Spring Boot 실습</h3>

        <h4>Thymeleaf 기본 출력</h4>
        <p>str1 : [[${str1}]]</p>
        <p th:text="${str2}">str2 출력</p>
        <p th:text="${user1}">user1 출력</p>
        <p th:text="${user2}">user2 출력</p>
        <p th:text="${user3}">user3 출력</p>
        <p th:text="${user4}">user4 출력</p>

        <h4>Thymeleaf 조건문</h4>
        <p>
            <span th:if="${user1.name == '홍길동'}">user1은 홍길동입니다.</span>
            <span th:if="${user1.name == '김유신'}">user1은 김유신입니다.</span>
        </p>
        <p>
            <span th:if="${user2}">user2는 참입니다.</span>
            <span th:unless="${user2}">user2는 거짓입니다.</span>
        </p>
        <p>
            <span th:if="${user4}">user4는 참입니다.</span>
            <span th:unless="${user4}">user4는 거짓입니다.</span>
        </p>
        <p th:switch="${user3.age > 30}">
            <span th:case="true">[[${user3.name}]]은(는) 30세 이상입니다.</span>
            <span th:case="false">[[${user3.name}]]은(는) 30세 미만입니다.</span>
        </p>

        <h4>Thymeleaf 반복문</h4>
        <table border="1">
            <tr>
                <th>아이디</th>
                <th>이름</th>
                <th>휴대폰</th>
                <th>나이</th>
            </tr>
            <tr th:each="user:${users}">
                <td th:text="${user.uid}">아이디</td>
                <td th:text="${user.name}">이름</td>
                <td>[[${user.hp}]]</td>
                <td>[[${user.age}]]</td>
            </tr>
        </table>

        <h4>Thymeleaf 링크</h4>
        <a th:href="@{/include}">include 실습</a>
        <a th:href="@{/layout}">layout 실습</a>
    </body>
</html>

 

 

- User6Controller.java (→ 위의 list,register,modify가 view)

서비스를 통해 DAO 호출하는 로직 동일

User6Controller.java
package kr.ch07.controller;

import java.util.List;

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

import kr.ch07.dto.User6DTO;
import kr.ch07.service.User6Service;

@Controller
public class User6Controller {
	
	@Autowired
	private User6Service service;
	
	@GetMapping("/user6/register")
	public String register() {
		return "/user6/register";
	}
	@PostMapping("/user6/register")
	public String register(User6DTO dto) {
		service.insertUser6(dto);
		return "redirect:/user6/list";
	}
	
	@GetMapping("/user6/list")
	public String list(Model model) {
		
		List<User6DTO> users = service.selectUser6s();
		model.addAttribute("users", users);
		
		return "/user6/list";
	}
	
	@GetMapping("/user6/modify")
	public String modify(String uid, Model model) {
		
		User6DTO user = service.selectUser6(uid);
		model.addAttribute("user", user);
		
		return "/user6/modify";
	}
	@PostMapping("/user6/modify")
	public String modify(User6DTO dto) {
		
		service.updateUser6(dto);
		return "redirect:/user6/list";
	}
	
	@GetMapping("/user6/delete")
	public String delete(String uid) {
		
		service.deleteUser6(uid);
		return "redirect:/user6/list";
	}
}