[Spring MVC] DTO(Data Transfer Object)
Categories: Spring
Tags: MVC, Spring, ๊ฐ๋ ์ ๋ฆฌ
๐ ๊ฐ์ธ์ ์ธ ๊ณต๊ฐ์ผ๋ก ๊ณต๋ถ๋ฅผ ๊ธฐ๋กํ๊ณ ๋ณต์ตํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ธ๋ก๊ทธ์
๋๋ค.
์ ํํ์ง ์์ ์ ๋ณด๊ฐ ์์ ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค :๐ธ
[ํ๋ฆฐ ๋ด์ฉ์ ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๋ฉด ๋ณต๋ฐ์ผ์ค๊ฑฐ์์]
HTTP ์์ฒญ/์๋ต์์์ DTO
DTO ๋?
- Transfer๋ผ๋ ์๋ฏธ์์ ์ ์ ์๋ฏ์ด ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํ ์ฉ๋์ ๊ฐ์ฒด
- DTO๋ ์ฃผ๋ก ํด๋ผ์ด์ธํธ์์ ์๋ฒ ์ชฝ์ผ๋ก ์ ์กํ๋ ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ ๋, ์๋ฒ์์ ํด๋ผ์ด์ธํธ ์ชฝ์ผ๋ก ์ ์กํ๋ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํ ์ฉ๋๋ก ์ฌ์ฉ
-
๋ ์ด์ด๋ ์ํคํ ์ฒ์ ๊ธฐ๋ณธํ๋ฆ
Client
โ โโ โ DTO(โRequest/Responseโ)
โ Request : ํด๋ผ์ด์ธํธ ์ชฝ์์ JSON ํ์์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ ์ชฝ์ผ๋ก ์ ์ก โ ์๋ฒ์์ JSON ํ์์ ๋ฐ์ดํฐ๋ฅผ DTO๊ฐ์ JAVA๊ฐ์ฒด๋ก ๋ณํ=์ญ์ง๋ ฌํ(Deserialization)
โ Response : ์๋ฒ์ชฝ์์ client์๊ฒ ์๋ต๋ฐ์ดํฐ๋ฅผ ์ ์ก , ์๋ต๋ฐ์ดํฐ java์์ JSONํ์์ผ๋ก ๋ณํ =์ง๋ ฌํ (Serialization)
API (Controller)
โ โโ โ Entity (DB์ ํ ์ด๋ธ๊ณผ 1:1๋ก ๋งตํ๋๋ ๊ฐ์ฒด)
Service
โ โโ โ Entity
Repository
โ โโ โ
DB
DTO ์ฌ์ฉ์ด์
- DTO ํด๋์ค๋ฅผ ์ด์ฉํ ์ฝ๋์ ๊ฐ๊ฒฐ์ฑ
-
ํ์์ ๋ณด๋ฅผ ๋ฑ๋กํ ๋ ์ ๋ณด๊ฐ ๋ง์ ์๋ก
postMember()
์ ํ๋ผ๋ฏธํฐ๋ก ์ถ๊ฐ๋๋@RequestParam
์ ๊ฐ์๊ฐ ๋์ด๋จ โ DTO ํด๋์ค๊ฐ ๋ฐ๋ก ์์ฒญ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ๊ฐ์ฒด๋ก ์ ๋ฌ๋ฐ๋ ์ญํ ์ ํด์ค.[๊ธฐ์กด์ฝ๋]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@RestController @RequestMapping("/v1/members") public class MemberController { @PostMapping public ResponseEntity postMember(@RequestParam("email") String email, @RequestParam("name") String name, @RequestParam("phone") String phone) { Map<String, String> map = new HashMap<>(); map.put("email", email); map.put("name", name); map.put("phone", phone); return new ResponseEntity<Map>(map, HttpStatus.CREATED); }
[DTO]
1 2 3 4 5 6 7
@RestController @RequestMapping("/v1/members") public class MemberController { @PostMapping public ResponseEntity postMember(MemberDto memberDto) { return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED); }
-
- ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฆ์ ๋จ์ํ
- ์๋ฒ ์ชฝ์์ ์ ํจํ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ๊ธฐ ์ํด ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ์ ์ ํจ์ฑ(Validation) ๊ฒ์ฆ
- HTTP ์์ฒญ์ ์ ๋ฌ๋ฐ๋ ํธ๋ค๋ฌ ๋ฉ์๋๋ ์์ฒญ์ ์ ๋ฌ๋ฐ๋ ๊ฒ์ด ์ฃผ๋ชฉ์ ์ด๊ธฐ ๋๋ฌธ์ ์ต๋ํ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํด์ผ ํจ.
-
DTO ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํจ์ฑ ๊ฒ์ฆ ๋ก์ง์ DTO ํด๋์ค๋ก ๋นผ๋ด์ด ํธ๋ค๋ฌ ๋ฉ์๋์ ๊ฐ๊ฒฐํจ์ ์ ์ง๊ฐ๋ฅ
[๊ธฐ์กด ์ฝ๋]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@RestController @RequestMapping("/no-dto-validation/v1/members") public class MemberController { @PostMapping public ResponseEntity postMember(@RequestParam("email") String email, @RequestParam("name") String name, @RequestParam("phone") String phone) { // (1) email ์ ํจ์ฑ ๊ฒ์ฆ if (!email.matches("^[a-zA-Z0-9_!#$%&'\\\\*+/=?{|}~^.-]+@[a-zA-Z0-9.-]+$")) { throw new InvalidParameterException(); } Map<String, String> map = new HashMap<>(); map.put("email", email); map.put("name", name); map.put("phone", phone); return new ResponseEntity<Map>(map, HttpStatus.CREATED); } ... ... }
[DTO]
DTO ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ ํจ์ฑ ๊ฒ์ฆ ๋ก์ง์ DTO ํด๋์ค๋ก ๋นผ๋ด์ด ํธ๋ค๋ฌ ๋ฉ์๋์ ๊ฐ๊ฒฐํจ์ ์ ์ง๊ฐ๋ฅ โ Getter Setter๋ก ๋ถ๋ฌ์ค๊ฑฐ๋ , ํ๊ธฐ ์ฝ๋์ฒ๋ผ lombok ์ฌ์ฉ
1 2 3 4 5 6 7 8 9 10
import lombok.Getter; import lombok.Setter; @Getter @Setter public class MemberDto { @Email private String email; private String name; private String phone; }
1 2 3 4 5 6 7 8 9 10 11
@RestController @RequestMapping("/v1/members") public class MemberController { @PostMapping public ResponseEntity postMember(@Valid MemberDto memberDto) { return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED); } ... ... }
DTO ํด๋์ค ์์ฑ์ ์ฃผ์์ฌํญ
- ๋ฉค๋ฒ ๋ณ์ ์ด์ธ์ getter ๋ฉ์๋๊ฐ ์์ด์ผ ํจ โ Response Body์ ํด๋น ๋ฉค๋ฒ ๋ณ์์ ๊ฐ์ด ํฌํจ๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์
- getter & setter ์์ฑ ์ alt+insert ์ฌ์ฉํ์ฌ ๊ฐํธํ๊ฒ ๋ฑ๋ก ๊ฐ๋ฅ.
HTTP ์์ฒญ/์๋ต ๋ฐ์ดํฐ์ DTO ์ ์ฉํ๊ธฐ
โ ๊ธฐ์กด ์ฝ๋์์ ๋ฆฌํฉํ ๋งํ๊ธฐ.
- HTTP Request Body๊ฐ JSON ํ์
@RequestBody
์ ๋ํ ์ด์ - JSON ํ์์ Request Body๋ฅผ MemberPostDto ํด๋์ค์ ๊ฐ์ฒด๋ก ๋ณํ์ ์์ผ์ฃผ๋ ์ญํ (ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ ์กํ๋ Request Body๋ JSON ํ์์ด์ด์ผ ํ๋ค)
@ResponseBody
์ ๋ํ ์ด์ - JSON ํ์์ Response Body๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํ๊ธฐ ์ํด DTO ํด๋์ค์ ๊ฐ์ฒด๋ฅผ Response Body๋ก ๋ณํํ๋ ์ญํ
- Spring MVC์์๋ ํธ๋ค๋ฌ ๋ฉ์๋์
@ResponseBody
์ ๋ํ ์ด์ ์ด ๋ถ๊ฑฐ๋ ํธ๋ค๋ฌ ๋ฉ์๋์ ๋ฆฌํด ๊ฐ์ดResponseEntity
์ผ ๊ฒฝ์ฐ, ๋ด๋ถ์ ์ผ๋กHttpMessageConverter
๊ฐ ๋์ํ๊ฒ ๋์ด ์๋ต ๊ฐ์ฒด(์ฌ๊ธฐ์๋ DTO ํด๋์ค์ ๊ฐ์ฒด)๋ฅผ JSON ํ์์ผ๋ก ๋ฐ๊ฟ์ค
DTO ์ ํจ์ฑ ๊ฒ์ฆ
ํด๋น ํ์๊ฐ์ ๋ฐฑ์๋์์ ๊ณ ๋ คํด๋ณผ ๋งํ ์ฌํญ.
- ์ด๋ฉ์ผ ๊ฒ์ฆ
- ์ด๋ฆ - ์ซ์X, ํ๊ตญ์ธ๋ง๊ฐ๋ฅ? -ํ๊ธ๋ง โ annotation์์
- ํด๋์ ํ 3๊ฐ, 4๊ฐ, 4๊ฐ ์ซ์๋ง ์ฌ ์ ์๊ฒ
- ๊ณต๋ฐฑ๊ฒ์ฆํด์ผ๋จ. โ annotation ์์
DTO ํด๋์ค์ ์ ํจ์ฑ ๊ฒ์ฆ ์ ์ฉ
-
์ ํจ์ฑ ๊ฒ์ฆ์ ์ํ ์์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
1 2 3 4 5
dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' ... ... }
-
์ ํจ์ฑ ๊ฒ์ฆ์ ์ฌ์ฉ๋๋ Annotation
Annotation Description Null ํ์ฉ = null์ผ ๊ฒฝ์ฐ ์ ํจ์ฑ๊ฒ์ฆ x,
not null์ด๋ฉด ๊ฒ์ฆํจ@NotBlank โโ (๊ณต๋ฐฑ) X, โ โ(์คํ์ด์ค) X, ย ๊ฒ์ฆ ์คํจ์ ์๋ฌ๋ฉ์ธ์ง ์ถ๋ ฅ๋จ x ย @NotNull โโ, โ โ ๋ ๋ค ํ์ฉ x @NotEmpty โโ (๊ณต๋ฐฑ)X, โ โ (์คํ์ด์ค)ํ์ฉ x @Email ์ ํจํ ์ด๋ฉ์ผ ์ฃผ์์ธ์ง๋ฅผ ๊ฒ์ฆ x @Pattern(regexp=โ~โ) ์ ๊ทํํ์(~์์ ๋ค์ด์์ผ ํจ.), O @Min/ @Max ์ง์ ๋ ๊ฐ ์ดํ/ ์ง์ ๋ ๊ฐ ์ด์์ด์ด์ผ ํจ. O @Range(min = , max = ) ๊ฐ์ด min ์ด์, max ์ดํ์ฌ์ผ ํจ(min๊ณผ max๋ long ํ์ ) ย @Length(min = , max = ) ๋ฌธ์์ด์ ๊ธธ์ด๊ฐ min ์ด์, max ์ดํ์ฌ์ผ ํจ ย -
์ ํจ์ฑ ๊ฒ์ฆ์ Controller Metnod์ ์์ฑํด์ผ ํ๋ ๊ฒ.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
@RestController @RequestMapping("/v1/members") @Validated public class MemberController { ... ... @PatchMapping("/{member-id}") public ResponseEntity patchMember(@PathVariable("member-id") @Min(2) long memberId, @Valid @RequestBody MemberPatchDto memberPatchDto) { memberPatchDto.setMemberId(memberId); // No need Business logic return new ResponseEntity<>(memberPatchDto, HttpStatus.OK); } }
- @Valid @RequestBody ๋ ๊ผญ ์์ฑํด์ผ ํจ. Valid ์์ฑํ์ง ์์ ๊ฒฝ์ฐ ์ ํจ์ฑ ๊ฒ์ฆ์ด ์ด๋ฃจ์ด์ง์ง ์์.
-
@PathVariable("member-id") long memberId
๋ณ์๋ URI path์ ์ฌ์ฉ๋๋๋ฐ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ์ดํฐ ์๋ณ์๋ 0์ด์์ ์ซ์์ด๋ฏ๋ก @PathVariable(โmember-idโ) @Min(1) long memberId - ์ด๋ฐ์์ผ๋ก ์ ์ฝ์กฐ๊ฑด ๊ฑธ ์ ์์.โ PathVariable์ ์ ํจ์ฑ ๊ฒ์ฆ์ด ์ ์์ ์ผ๋ก ์ํ๋๋ ค๋ฉด ํด๋์ค ๋ ๋ฒจ์ @Validated ๋ถ์ฌ์ค์ผ ํจ.
- Jakarta Bean Validation์ด๋?
- DTO ํด๋์ค์ ์ ํจ์ฑ ๊ฒ์ฆ์ ์ํด์ ์ฌ์ฉํ ์ ๋ํ ์ด์ ์ Jakarta Bean Validation์ด๋ผ๋ ์ ํจ์ฑ ๊ฒ์ฆ์ ์ํ ํ์ค ์คํ์์ ์ง์ํ๋ ๋ด์ฅ ์ ๋ํ ์ด์
- Jakarta Bean Validation์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ API๊ฐ ์๋ ์คํ(์ฌ์, Specification) ์์ฒด
- Jakarta Bean Validation ์คํ์ ๊ตฌํํ ๊ตฌํ์ฒด๊ฐ ๋ฐ๋ก Hibernate Validator
- Custom Validator๋ฅผ ์ฌ์ฉํ ์ ํจ์ฑ ๊ฒ์ฆ
- ๋ชฉ์ ์ ๋ง๋ ์ ๋ํ ์ด์ ์ด ์์๊ฒฝ์ฐ ์ง์ ๋ง๋ค์ด์ ์ ํจ์ฑ ๊ฒ์ฆ ๊ฐ๋ฅ.
- ๊ตฌํ ๊ณผ์
- Custom Validator๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ Custom Annotation์ ์ ์
- ์ ์ํ Custom Annotation์ ๋ฐ์ธ๋ฉ๋๋ Custom Validator๋ฅผ ๊ตฌํ
- ์ ํจ์ฑ ๊ฒ์ฆ์ด ํ์ํ DTO ํด๋์ค์ ๋ฉค๋ฒ ๋ณ์์ Custom Annotation์ ์ถ๊ฐ
### comment
์ค๋์ ์ด์ ๋ณด๋ค ๋์๋ฏ, dto ์ด๋ ต๋ค๊ณ ํด์ ์กฐ๊ธ ๊ฑฑ์ ํ๋๋ฐ ๋คํ์ด๋ ์ดํด๋ชปํ ์ ๋๋ ์๋์๋ค, ์ค์ต๋ ์ด๋ป๊ฒ๋ ๊ตฌํํ๊ณ ! ๋ณต์ตํ๊ณ DI๋ฅผ ๋ค์ ๋ด์ผํ ๊ฒ ๊ฐ๋ค..DI๋ ์์ง๋ ์ดํด๋ชปํจ ์ ๊ทธ๋ฆฌ๊ณ ์ ๊ท์โฆ!์ ํ๋ฒ๋ง ๋ด์ผ๊ฒ ๋ค..
๐ ๊ณต์ง
Leave a comment