ํด๋น ๊ธ์ ํฌ๋ ์ด๊ทธ ์์ฆ๋์ด ์ฐ๊ณ , ์ฌ์ฌ์ฒ ๋์ด ์ฎ๊ธด Spring in Action ์ 5ํ์ ์ ๋ฆฌํ ๋ด์ฉ์์ ๋ฏธ๋ฆฌ ์๋ฆฝ๋๋ค.
- ๋ชจ๋ธ ๋ฐ์ดํฐ ๋ธ๋ผ์ฐ์ ์์ ๋ณด์ฌ์ฃผ๊ธฐ
- ํผ ์ ๋ ฅ ์ฒ๋ฆฌํ๊ณ ๊ฒ์ฌํ๊ธฐ
- ๋ทฐ ํ ํ๋ฆฟ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํํ๊ธฐ
1์ฅ์์ ์ปจํธ๋กค๋ฌ์ ์ญํ ์ ๋๋ต์ ์ผ๋ก ์์๋ณด์๋ค. ๋ค์ํ๋ฒ ์๊ธฐ ์์ผ๋ณด์๋ฉด, ์คํ๋ง ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ปจํธ๋กค๋ฌ๋ ์์ฒญ์ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ ์ด๋ฅผ ์ฒ๋ฆฌํ์ฌ ๋ทฐ์๊ฒ ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐ๊ฒ ๋๋ค. ์ดํ ๋ทฐ์์๋ ์ ๋ฌ ๋ฐ์ ๋ฐ์ดํฐ๋ก ํ๋ฉด์ ๊ตฌ์ฑํ๋ค. ์ด ํ๋ฆ์ ๋ณด๊ธฐ ์ฝ๊ฒ ๋ํ๋ด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ทธ๋ฆผ์ผ๋ก ๋ํ๋ผ ์ ์๋ค.
์ฑ ์์ ๋๋ฉ์ธ์ ํด๋น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ดํด์ ํ์ํ ๊ฐ๋ ์ ๋ค๋ฃจ๋ ์์ญ์ด๋ผ๊ณ ์ค๋ช ํ๊ณ ์๋ค. ๋ํ ์ฃผ์์ผ๋ก ์์ธํ ๋ด์ฉ์ ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ๋ฅผ ์ฐธ๊ณ ํ๋ผ๊ณ ์ ํ์๋ค. ๊ฐ๋จํ๊ฒ DDD(Domain-Driven Design)์ ๋ํด ์์๋ณด๊ณ ๊ฐ์.
DDD์์์ ๋๋ฉ์ธ์ ์ฌ์ฉ์๊ฐ ์ฌ์ฉํ๋ ๋ชจ๋ ๊ฒ์ ์ง์นญํ๋ค. ์๋ฅผ๋ค์ด ์ฑ ์์ ๋ค๋ฃจ๊ณ ์๋ ํ์ฝ, ํ์ฝ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์์ฌ๋ฃ, ํ์ฝ๋ฅผ ์ฃผ๋ฌธํ๋ ์ฌ๋์ ์ ๋ณด ๋ฑ ์ฌ์ฉ์๊ฐ ํด๋น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ด์ ์ฌ์ฉํ๋ ๊ฒ๋ค์ ๋๋ฉ์ธ์ด๋ผ๊ณ ํ๋ค.(์ฐธ๊ณ - ๋๋ฉ์ธ ์ฃผ๋ ์ค๊ณ(Domain-Driven Design) in Real Project โ ๋๋ฉ์ธ)
๊ทธ๋ ๋ค๋ฉด ๊ฐ๋จํ๊ฒ ํ์ฝ์ ๋ค์ด๊ฐ๋ ์์์ฌ๋ฅผ ์ ์ํด ๋ณด์.
// ์์์ฌ
@Data
public class Ingredient {
private final String id;
private final String name;
private final Type type;
public static enum Type {
WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
}
}
์์์ฌ ๋ง๋ค์ ํน๋ณํ id, ์ด๋ฆ, ํ์
์ด ์กด์ฌํ ๊ฒ์ด๋ค. ์ด์ธ์๋ ์ฌ๋ฌ๊ฐ์ง ์ถ๊ฐํ ๊ฒ๋ค๋ ์๊ฒ ์ง๋ง, ์ค์ํ ๊ฒ์ @Data
์ ๋
ธํ
์ด์
์ด๋ค. ํด๋น ์ ๋
ธํ
์ด์
์ Lombok์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ ๊ฒ์ผ๋ก, 1์ฅ์์ ์์กด์ฑ์ ์ถ๊ฐํ์๋ค. ์ด๋ฌํ ๋กฌ๋ณต์ ์ ํ๋์ ์ ๋
ธํ
์ด์
์ผ๋ก ๊ฒํฐ, ์ธํฐ, toString ๋ฉ์๋๋ฑ ํ์ํ ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํด์ค๋ค.
// Ingredient.class
public class Ingredient {
private final String id;
private final String name;
private final Ingredient.Type type;
public Ingredient(String id, String name, Ingredient.Type type) {
this.id = id;
this.name = name;
this.type = type;
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
public Ingredient.Type getType() {
return this.type;
}
public boolean equals(Object o) {
...
}
protected boolean canEqual(Object other) {
return other instanceof Ingredient;
}
public int hashCode() {
...
}
public String toString() {
...
}
public static enum Type {
WRAP,
PROTEIN,
VEGGIES,
CHEESE,
SAUCE;
private Type() {
}
}
}
์ปดํ์ผ ๋ ๋ฐ์ดํธ ์ฝ๋๋ฅผ IDE๋ฅผ ํตํด ์ฝ๋๋ก ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ๋ก ์ ์ํ์ง ์์ ๋ฉ์๋๋ค์ด ๋ํ๋๊ฒ ๋๋ค. ์ฐธ๊ณ ๋ก ์ฌ๊ธฐ์ ์ธํฐ๊ฐ ๋ง๋ค์ด์ง์ง ์์ ๊ฒ์ ๋ณ์ ์ ์ธ์ final ํค์๋๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ ๊ฒ ๋กฌ๋ณต์ ๋ฐํ์ ์์ ์ํ๋ ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ์์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์ ๊ฐ๋ ์ฑ๊ณผ ์์ฐ์ฑ์ ๋ํ ์ ์๋ค.
ํ์ง๋ง, ๋ ๋ค๋ฆฌ๋ ๋๋ค๊ฒจ ๋ณด๊ณ ๋์ด๊ฐ๋ ๋ง์ด ์๋ฏ ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์์๋ณด๊ณ ์ฌ์ฉํด์ผ ๋๋ค. ๋กฌ๋ณต ๋ํ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ์์๋ณด๊ณ ์ฌ์ฉํด์ผ ํ๋ค. ๋ํ์ ์ธ ์๋ก ์์์ ์ฌ์ฉํ๋ @Data
์ ๋
ธํ
์ด์
ํน์ @ToString
์ ๋
ธํ
์ด์
์ผ๋ก toString ๋ฉ์๋๋ฅผ ์์ฑํ์ ๋, ์ํ ์ฐธ์กฐ ๋ฌธ์ ๋ก ์ธํด ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๊ทธ๋์ @Data
๊ฐ์ด ํ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๋ฅผ ๋๋ฑ ๋ง๋ค์ด์ฃผ๋ ์ ๋
ธํ
์ด์
์ ์ง์ํ๋ค๊ณ ํ๋ค. ์ด์ธ์๋ ์ฌ๋ฌ๊ฐ์ง ์๊ฐํ ์ ๋ค์ด ์๊ธฐ ๋๋ฌธ์ ์ฐธ๊ณ ํ์ฌ ์ ์ฌ์ฉํด๋ณด์.(์ฐธ๊ณ - ์ค๋ฌด์์ Lombok ์ฌ์ฉ๋ฒ)
์ด๋ ํ ์ปจํธ๋กค๋ฌ์ URL์ ๋งตํํ๋ @RequestMapping
์ ๋
ธํ
์ด์
์ ์ ์๊ณ ์์ ๊ฒ์ด๋ค. ์ด๋ ์คํ๋ง 4.3๋ฒ์ ์ด์ ์์ ์ฌ์ฉ๋๋ ๊ฒ์ผ๋ก GET ํน์ POST ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ method ์์ฑ์ ์ค์ ํด ์ฃผ์ด์ผ ํ๋ค.
@RequestMapping(value="/" mehtod=RequestMehod.GET)
์คํ๋ง 4.3๋ถํฐ๋ ๊ฐ ์์ฒญ ๋ฉ์๋์ ํนํ๋ ์ ๋ ธํ ์ด์ ๋ค์ด ๋ฑ์ฅํ๊ฒ ๋์๋ค.
์ ๋ ธํ ์ด์ | ์ค๋ช |
---|---|
@RequestMapping | ๋ค๋ชฉ์ ์ผ๋ก ์์ฒญ์ ์ฒ๋ฆฌ |
@GetMapping | HTTP GET ์์ฒญ ์ฒ๋ฆฌ |
@PostMapping | HTTP POST์์ฒญ ์ฒ๋ฆฌ |
@PutMapping | HTTP PUT ์์ฒญ ์ฒ๋ฆฌ |
@DeleteMapping | HTTP DELETE ์์ฒญ ์ฒ๋ฆฌ |
@PatchMapping | HTTP PATCH ์์ฒญ ์ฒ๋ฆฌ |
ํผ ํ๊ทธ์ ๊ฒฝ์ฐ, method ์์ฑ์ด POST๋ก ์ค์ ํด๋ action ์์ฑ์ด ์ ์ธ๋์ง ์์๋ค๋ฉด, GET ์์ฒญ๊ณผ ๊ฐ์ ๊ฒฝ๋ก๋ก ์๋ฒ์ POST ์์ฒญ์ ๋ณด๋ด๊ฒ ๋๋ค.
<!-- localhost:8080/example -->
<form method="POST">
<!-- /example ์ผ๋ก POST ์์ฒญ -->
</form>
<form method="GET">
<!-- /example ์ผ๋ก GET ์์ฒญ -->
</form>
์์์ ์ปจํธ๋กค๋ฌ๋ค์ ๋ง๋ค์์ ๋ ๋ฆฌํด ๊ฐ์ผ๋ก ๋ทฐ์ ์ด๋ฆ์ ์ง์ ํ๋ค. ์ด๋ ์๋์ ๊ฐ์ด "redirection:"
์ ์ฌ์ฉํ์ฌ ๋ณ๊ฒฝ๋ ๊ฒฝ๋ก๋ก ์ฌ์ ์ํ ์ ์๋๋ก ์ ๋ ํ ์ ์๋ค.
@PostMapping
public String redirectMethod() {
return "redirection:/dest";
}
๋ง์ฝ ์ฌ์ฉ์๊ฐ ํผ์ ์ ๋ณด๋ฅผ ์ ๋ ฅํ๋ค ์กฐ๊ฑด์ ๋ง์ง ์๋ ๋ด์ฉ์ ์์ฑํ๊ฑฐ๋, ์์ ์ ๋ ฅํ์ง ์์ ๋ถ๋ถ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ผ ๋ ๊น? ํ๋ก ํธ ๋จ์์ ๋จผ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ์ฌ ์ ์ก์ ๋ง์ ์๋ ์๊ณ , ์๋ฒ ๋จ์์ ์ ์ฅ์ด ๋์ง ์๋๋ก ํ ์๋ ์๋ค. ์คํ๋ง์ ์ฌ์ฉํ๊ณ ์๋ ์ฐ๋ฆฌ๋ ์๋ฒ ๋จ์์ ์ด๋ฅผ ์ด๋ป๊ฒ ๊ฒ์ฌํ ์ง ์์๋ณด์.
์คํ๋ง์์๋ ์๋ฐ์ ๋น ์ ํจ์ฑ ๊ฒ์ฌ API๋ฅผ ์ง์ํ๊ธฐ์ if/else ๋ธ๋ก๋ค์ ์ฌ์ฉํ์ง ์๊ณ ๋ ์ฝ๊ฒ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ ์ ์๋ค. ์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ spring-boot-starter-validation
์์กด์ฑ์ ์ถ๊ฐํด์ฃผ๋๋ก ํ๋ค(๊ตฌํ์ฒด๋ก Hibernate Validator ์ฌ์ฉ). ์ฑ
์์๋ ํด๋น ์์กด์ฑ์ ์ถ๊ฐํ์ง ์๋๋ค. ์ด๋ 2.2.X ๋ฒ์ ์์๋ spring-boot-starter-validation
๊ฐ ์กด์ฌํ์์ง๋ง, 2.3.X ๋ถํฐ๋ ํฌํจ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ๋ ธํ ์ด์ | ๊ธฐ๋ฅ |
---|---|
@AssertFalse | flase ๊ฐ ์ฒดํฌ |
@AssertTrue | true ๊ฐ ์ฒดํฌ |
@DecimalMax(value=) | ์ง์ ๋ ๊ฐ ์ดํ์ ์ค์๋ง |
@DecimalMin(value=) | ์ง์ ๋ ๊ฐ ์ด์์ ์ค์๋ง |
@Digits(integer=, fraction=) | ์ง์ ๋ ์ ์์ ์์ ์๋ฆฌ์๋ณด๋ค ์์ ๊ฒฝ์ฐ |
@Future | ๋์ ๋ ์ง๊ฐ ํ์ฌ๋ณด๋ค ๋ฏธ๋์ผ ๊ฒฝ์ฐ๋ง |
@Past | ๋์ ๋ ์ง๊ฐ ํ์ฌ๋ณด๋ค ๊ณผ๊ฑฐ์ผ ๊ฒฝ์ฐ๋ง |
@Max(value=) | ์ง์ ๋ ๊ฐ๋ณด๋ค ์์ ๊ฒฝ์ฐ |
@Min(value=) | ์ง์ ๋ ๊ฐ๋ณด๋ค ํด ๊ฒฝ์ฐ |
@NotNull | null ๊ฐ์ด ์๋ ๊ฒฝ์ฐ |
@Null | null์ผ ๊ฒฝ์ฐ |
@Pattern(regex=, flag=) | ํด๋น ์ ๊ท์์ ๋ง์กฑํ๋ ๊ฒฝ์ฐ |
@Size(min=, max=) | ๋ฌธ์์ด ๋๋ ๋ฐฐ์ด์ด min๊ณผ max ์ฌ์ด์ผ ๊ฒฝ์ฐ |
@Valid | ๋์ ๊ฐ์ฒด์ ํ์ธ ์กฐ๊ฑด์ ๋ง์กฑํ ๊ฒฝ์ฐ ํต๊ณผ |
@NotBlank | ๋ฌธ์์ด์ด๋ ๋ฐฐ์ด์ด null์ด ์๋๊ณ ๊ธธ์ด๊ฐ 0์ด ์๋ ๊ฒฝ์ฐ |
์ด๋ฉ์ผ ํ์ ์ฒดํฌ | |
@CreditCardNumber | Luhn(๋ฃฌ) ์๊ณ ๋ฆฌ์ฆ์ ์ํ ์ ์ฉ์นด๋ ๋ฒํธ ์ฒดํฌ |
@Controller
@RequestMapping("/simple")
public class SimpleController {
@GetMapping
public String simple() {
return "simple";
}
}
์์ ๊ฐ์ด ๋ชจ๋ธ ๋ฐ์ดํฐ ํน์ ์ฌ์ฉ์์ ์ ๋ ฅ์ด ํ์์๋ ๊ฐ๋จํ ์ปจํธ๋กค๋ฌ์ ๊ฒฝ์ฐ, ์๋์ ์ฝ๋์ ๊ฐ์ด ๋ทฐ ์ปจํธ๋กค๋ฌ(๋ทฐ์ ์์ฒญ ์ ๋ฌ๋ง ํ๋ ์ญํ )๋ก ์ ์๊ฐ ๊ฐ๋ฅํ๋ค.
@Configuration
public class webCofig implements WebMvcConfigurer {
@Override
public void addViewController(ViewControllerRegistry registry) {
registry.addViewController("/simple").setViewName("simple");
}
}
WebMvcConfigurer
์ธํฐํ์ด์ค๋ฅผ ์ดํด๋ณด๋ฉด ์ฌ๋ฌ๊ฐ์ง ๋ฉ์๋๋ค์ด default๋ก ์ ์ธ๋์ด์๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ๋ฐ๋ผ์ ์ํ๋ ์ค์ ์ ํ๊ธฐ ์ํด ํด๋น ๋ฉ์๋๋ง ์ค๋ฒ๋ผ์ด๋ํ์ฌ ์ฌ์ฉํ๋ฉด ๋๋ค.
์คํ๋ง ๋ถํธ์์ ์๋-๊ตฌ์ฑ ์ง์๋๋ ํ ํ๋ฆฟ์ ๋ค์๊ณผ ๊ฐ๋ค.
ํ ํ๋ฆฟ | ์คํํฐ ์์กด์ฑ |
---|---|
FreeMarker | spring-boot-starter-freemarker |
Groovy | spring-boot-starter-groovy-templates |
Java Server Pages(JSP) | ์์ |
Mustache | spring-boot-starter-mustache |
Thymeleaf | spring-boot-starter-thymeleaf |
ํ ํ๋ฆฟ์ค์์ JSP๋ ์์กด์ฑ์ ์ง์ ํ์ง ์์๋ ๋๋ค. ๊ทธ ์ด์ ๋ ์๋ธ๋ฆฟ ์ปจํ ์ด๋์์ JSP ๋ช ์ธ๋ฅผ ๊ตฌํํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ง์ ํ ํ์๊ฐ ์๋ ๊ฒ์ด๋ค. ํ์ง๋ง JSP๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๊ณ ๋ คํ ์ฌํญ์ด ์๊ธด๋ค. ์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก /WEB-INF ํ์์์ JSP ์ฝ๋๋ฅผ ์ฐพ๊ฒ ๋๋๋ฐ, JAR ํ์ผ๋ก ์์ฑํ์ ๊ฒฝ์ฐ ์ด๊ฐ ์๋ค. ๋ฐ๋ผ์ WAR ํ์ผ๋ก ํจํค์งํ๋ ํ๋ก์ ํธ์์ JSP๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ๋ค๋ฅธ ํ ํ๋ฆฟ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ์ด ์ข๋ค.