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">
<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을 자바 인터페이스를 통해 호출할 수 있도록 해줌
- 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";
}
}