Development
DTO - μ΄λ κ² μ¬μ©νμ§ λ§μΈμ
3/6/2025

DTOData Transfer Objectλ κ³μΈ΅ κ° κ΅νμ μ¬μ©λλ λ°μ΄ν°λ₯Ό λ΄μ κ°μ²΄μ
λλ€. κ°λ ν¨κ» 곡λΆνλ κ°λ°μλ€μ μ½λλ₯Ό λ³Ό λ, 'μ΄λ° μ½λλ λ€μκ³Ό κ°μ΄ λ°λμμ μ’κ² λ€'λΌλ μκ°μ΄ λ€ λκ° μμ΄ κ°λ¨νκ² μ λ¦¬ν΄ λ³΄μμ΅λλ€.
κ³μΈ΅μ 무λ ₯ννλ DTO
DTOμ μλͺ»λ μ¬μ© μ¬λ‘λ₯Ό μ΄ν΄λ³΄κ³ , κ·Έλ‘ μΈν΄ λ°μνλ λ¬Έμ μ μ μμλ³΄κ² μ΅λλ€.
μμ λ‘ μ¬μ©ν Article λλ©μΈμ κ²μνμ κ²μκΈμ λνλ΄λ©°, title, content, authorName μ κ°λλ€κ³ κ°μ ν©λλ€.
κ²μκΈμ μμ±νλ κΈ°λ₯μ κ°λ°νκΈ° μν΄, μ¬μ©μλ‘λΆν° λ°μμΌνλ DTO κ°μ²΄λ₯Ό μ μνκ³ μ»¨νΈλ‘€λ¬λ₯Ό μμ±ν΄λ³΄κ² μ΅λλ€. λν, contentλ 50μ μ΄μ, 200μ μ΄νλΌλ μ μ½μ λ£μ΄μ£Όμμ΅λλ€.
@Getter
public class ArticleCreateRequest {
private String title;
@Size(min = 50, max = 200)
private String content;
private String authorName;
}@RestController
@RequiredArgsConstructor
@RequestMapping("/api/articles")
public class ArticleController {
@PostMapping
public void createArticle(
@Valid @RequestBody ArticleCreateRequest articleCreateRequest
)
{
//todo call article service
}
}κ³μΈ΅μ ꡬλΆνλ κ²μ μ€μνλ Articleμ μμ±νλ λΉμ¦λμ€ λ‘μ§μ μλΉμ€ λ μ΄μ΄μμ μ²λ¦¬νλλ‘ ν©λλ€. μλΉμ€ λ©μλμμ νμν μ 보λ title, contentμ authorNameμΌλ‘, 곡κ΅λ‘κ²λ DTOμ λͺ¨λ ν¬ν¨λμ΄μμΌλ―λ‘ κ·Έλλ‘ μλΉμ€ λ μ΄μ΄μ μ λ¬νλ λ°©μμΌλ‘ ꡬννκ² μ΅λλ€.
@Service
public class ArticleService {
private final ArticleRepository articleRepository;
@Transactional
public void createArticle(ArticleCreateRequest req) {
Article article = new Article(
req.getTitle(), req.getContent(), req.getAuthorName()
);
articleRepository.save(article);
}
}μλ£λμμ΅λλ€! ν μ€νΈλ₯Ό ν΄λ³΄λ μ λμνλ κ²μΌλ‘ 보μ λλ€. μ΄μ PR λ λ¦¬κ³ λ°λ»κ³ λμμ 리뷰λ₯Ό κΈ°λ€λ¦΄ μ°¨λ‘μ λλ€.
λ¬Έμ μ
νλ μ΄λ¬ν ꡬ쑰λ λͺκ°μ§ λ¬Έμ μ μ κ°μ΅λλ€.
1. μΉ κ³μΈ΅μ λ³κ²½μ΄ λλ©μΈ μλΉμ€ λ‘μ§μ μν₯μ μ€λ€.
μ΄μ μΈμ¦ μμ€ν μ΄ μΆκ°λμ΄μ μ¬μ©μλ‘λΆν° authorNameμ λ°μ§ μμ΅λλ€. aurhorNameμ μ¬μ©μ ν ν°μμ μΆμΆνλ€κ³ κ°μ ν©μλ€.
//ArticleCreateRequest.java
public ArticleCreateRequest {
private String title;
@Size(min = 50, max = 200)
private String content;
}
//ArticleController.java
public class ArticleController {
public void createArticle(
@Login String username,
@RequestBody ArticleCreateRequest articleCreateRequest
) {
articleService.createArticle(articleCreateRequest); //???
}
}ArticleCreateRequestμμλ λ μ΄μ authorNameμ ν¬ν¨νμ§ μμΌλ©°, 컨νΈλ‘€λ¬μμ @Login μ λ Έν μ΄μ μ ν΅ν΄ μΆμΆν μ μμ΅λλ€. μΉ κ³μΈ΅μμ μ¬μ©μμ μ΄λ¦μ μ»λ ννλ§ λ°λ κ²μ λλ€. νμ§λ§ DTOκ° λλ©μΈ μλΉμ€ λ‘μ§κΉμ§ μΉ¨ν¬ν κ²°κ³Ό, 컨νΈλ‘€λ¬ λΏλ§ μλλΌ μλΉμ€ λ©μλκΉμ§ λ³κ²½ν΄μΌνλ λ¬Έμ κ° λ°μν©λλ€.
//method parameter λ³κ²½
public void createArticle(String authorName, ArticleCreateRequest req) {
//article μμ± νλΌλ―Έν° λ³κ²½
Article article = new Article(
req.getTitle(), req.getContent(), authorName
);
articleRepository.save(article);
}μλΉμ€ λ©μλμμ μ νκ° λλμ λ€νμ΄μ§, λ§μ½ Repositoryμλ λμΌν DTOλ₯Ό μ¬μ©νλ€λ©΄ λ³κ²½μ μ νλ μμμ± κ³μΈ΅κΉμ§ νΌμ‘μ κ²μ λλ€.
λλ©μΈ μλΉμ€κ° νμλ‘ νλ κ° μ체μλ λ³ν¨μ΄ μμ§λ§, DTOλ₯Ό κ·Έλλ‘ λ겨λ°μκΈ°μ λ³κ²½μ΄ νμνμ΅λλ€. κ²°κ΅ DTOλ₯Ό λκΈ°λ νμλ μλΉμ€ λ©μλλ₯Ό λ³κ²½ν΄μΌνλ μ΄μ λ₯Ό λλ©μΈ λ‘μ§μ λ³κ²½ λΏλ§ μλ μΉ κ³μΈ΅μ λ³κ²½κΉμ§λ‘ νμ₯νλ λ¬Έμ λ₯Ό λ³μ΅λλ€. 'νλμ ν΄λμ€λ λ³κ²½ν΄μΌνλ μ΄μ κ° λ¨ νλμ¬μΌ νλ€'λΌλ SRPμ μμΉμ μλ°μ λλ€.
2. μλΉμ€ λ©μλμ μ¬μ¬μ©μ΄ μ΄λ ΅λ€.
ArticleServiceμ createArticleμ λ€λ₯Έ μλΉμ€μμ μ¬μ©ν΄μΌνλ μΌμ΄ μκ²Όλ€κ³ κ°μ ν΄λ΄ μλ€.
@RequiredArgsConstructor
public class AnotherService {
private final ArticleService articleService;
public void someMehtod(...) {
...
ArticleCreateRequest req = new ArticleCreateRequest(...);
articleService.createArticle(req);
}
}μ μ½λμμ λ΄μ©μ΄ 50μ μ΄μ 200μ μ΄νμ¬μΌ νλ€λ μ λ ₯ μ ν¨μ±μ μ§μΌμ§ μ μμ΅λλ€. 컨νΈλ‘€λ¬μμλ§ κ²μ¦μ΄ μνλκΈ° λλ¬Έμ λλ€. λ°λΌμ μ΄λ° κ²½μ°μλ λκ΅°κ°λ κ²μ¦μ μνν΄μΌ ν©λλ€.
첫λ²μ§Έ λ°©λ²μ createArticleμ νΈμΆνλ μΈ‘μμ κ²μ¦νλ κ²μ λλ€. νμ§λ§ μ΄ λ°©λ²μ λλ€λ₯Έ κ³³μμ createArticleμ΄ μ¬μ©λλ κ²½μ° κ²μ¦ λ‘μ§μ μ€λ³΅μ μ΄λνλ©°, μ€μλ‘ κ²μ¦ μμ΄ μλΉμ€ λ©μλλ₯Ό νΈμΆν κ°λ₯μ±μ λ§λ€κ² λ©λλ€.
μ무λλ μ λ°©λ²μ μλΉμ€ λ©μλμ μ μ₯μμ μ λ ₯ μ ν¨μ±μ μ λ’°ν μ μλ λ°©λ²μΈ κ² κ°μ΅λλ€. κ·ΈλΌ μλΉμ€ λ©μλκ° μ§μ κ²μ¦μ μννλ©΄ ν΄κ²°λ κΉμ? μ΄ κ²½μ°μλ μμν λΉμ¦λμ€ κ·μΉλ§μ μ§μΌμΌ ν λλ©μΈ μλΉμ€ λ‘μ§μ΄ μ λ ₯ μ ν¨μ± κ²μ¦ μ½λλ‘ μ€μΌλμ΄ μ± μμ΄ κ³Όλ€ν΄μ§λ€λ λ¬Έμ κ° λ°μν©λλ€. (λ΄μ©μ κΈΈμ΄λ₯Ό κ²μ¦νλ κ²μ΄ μ λ ₯ μ ν¨μ±μΈμ§ λΉμ¦λμ€ κ·μΉμΈμ§λ λ Όμ λμμ΄μ§λ§, μ΄λ λμ€μ λ€λ₯Έ ν ν½μΌλ‘ λ€λ£¨λλ‘ νκ² μ΅λλ€.)
κ²°κ΅ μ΄λ€ λ°©λ²μΌλ‘λ μλΉμ€ λ©μλλ₯Ό μ¬μ¬μ©νκΈ°μλ μ‘°κΈμ© κ»λλ¬μ΄ μ μ΄ μκΉλλ€. μ΄ μμ 컨νΈλ‘€λ¬ λ 벨μ DTOλ₯Ό μλΉμ€ λ©μλμμ μ¬μ©νκΈ°μ μκΈ°λ λ¬Έμ λ‘ λ³Ό μ μμ΅λλ€.
ν΄κ²°μ±
DTO λ₯Ό λκΈ°μ§ μκΈ°
첫λ²μ§Έ λ¬Έμ μλ μΉ κ³μΈ΅μ λ³κ²½μ΄ λλ©μΈ λ‘μ§μ μν₯μ μ£Όμ§ μκ² νλ λ°©λ²μ, κ·Έμ DTOλ₯Ό λ°μ§ μλ λ°©λ²μ λλ€.
public class ArticleService {
public void createArticle(String title, String content, String authorName) {..}
}λ§μ½ createArticle λ©μλκ° DTOκ° μλ κ°κ°μ κ°λ€μ λͺ νν μ λ¬λ°μλ€λ©΄, 컨νΈλ‘€λ¬κ° κ°μ μ΄λμ μ΄λ»κ² κ°μ Έμ€λ κ·Έκ²μ λ μ΄μ μλΉμ€ λ©μλμ κ΄μ¬μ¬κ° μλλλ€. κ·Έμ νμν κ°μ λ°κ³ μ¬μ©ν λΏμ λλ€. λ§μ½ λ©μλκ° μμ κ°μ΄ μμ±λμλ€λ©΄ 첫λ²μ§Έ λ¬Έμ λ λ°μνμ§ μμμ κ²μ΄κ³ , λ³κ²½μ μ€μ§ 컨νΈλ‘€λ¬μμλ§ λ°μνμ κ²μ λλ€.
νμ§λ§ μ΄ ν΄κ²°μ± μ λλ²μ§Έ λ¬Έμ λ₯Ό ν΄κ²°νμ§ μμ΅λλ€. μ¬μ ν κ²μ¦μ λν μ± μμ λΆμ¬νκΈ°κ° μ λ§€ν μν©μ λλ€.
λ³λμ DTO μ¬μ©νκΈ°
λλ²μ§Έ λ¬Έμ λ₯Ό ν΄κ²°νλ λ°©λ²μΌλ‘λ μλΉμ€ κ³μΈ΅μ λ³λμ DTOλ₯Ό μ μνλ κ²μ λλ€. μλΉμ€ κ³μΈ΅μ DTOλ μλΉμ€ λ©μλκ° νμλ‘ νλ μ λ ₯ λͺ¨λΈμ μ 곡νλ©°, μΉ κ³μΈ΅μ DTOμ λΆλ¦¬λ λ³λμ ν΄λμ€λ‘ μμ±λμ΄μΌ ν©λλ€
@Getter
public class ArticleCreateCommand {
public ArticleCreateCommand(String title, String content, String authorName) {
if (content.size() < 50 && content.size() > 200) {
throw ValidationError("Content size not valid");
}
this.title = title;
this.content = content;
this.authorName = authorName;
}
private final String title;
private final String content;
private final String authorName;
}ArticleCreateCommandλ Articleμ μμ±νλλ° νμν λ°μ΄ν°λ₯Ό λͺ¨λ λ΄κ³ μμ΅λλ€. λν μμ±μμμ μ λ ₯ μ ν¨μ± κ²μ¦μ μννμ¬ μμ±λ μ΄νμλ κ°μ λ³κ²½μ΄ μ νλκΈ° λλ¬Έμ, μμ€ν λ΄μ μ ν¨νμ§ μμ ArticleCreateCommand κ°μ²΄λ μ‘΄μ¬ν μ μμ΅λλ€. μ΄ κ°μ²΄λ₯Ό createArticleμ΄ λ°λλ‘ λ§λ€λ©΄ λ©λλ€.
public class ArticleService {
public void createArticle(ArticleCreateCommand command) {
Article article = new Article(
command.getTitle(),
command.getContent(),
command.getAuthorName()
);
// save
}
}μ΄λ¬ν λ°©λ²μ μ¬μ©νμ λμλ μ λ ₯ λͺ¨λΈμλ§ λ§μΆμ΄ μλΉμ€ λ©μλλ₯Ό νΈμΆν΄μ€λ€λ©΄ μΉ κ³μΈ΅μ΄ λ³κ²½λλλΌλ μλΉμ€ λ©μλλ λ³κ²½λ μ΄μ κ° μμ΅λλ€. 첫λ²μ§Έ λ¬Έμ λ ν΄κ²°λμμ΅λλ€.
μ΄ λ°©λ²μ λ λ²μ§Έ λ¬Έμ μμ ν΄κ²°ν©λλ€. λ€λ₯Έ μλΉμ€μμ createArticleμ νΈμΆν λλ, ν΄λΉ μλΉμ€ λ©μλμ λ³λμ κ²μ¦ λ‘μ§μ μμ±ν νμκ° μμ΅λλ€. createArticleμ 컀맨λ κ°μ²΄λ₯Ό μ λ’°ν μ μμΌλ©° λΉμ¦λμ€ κ·μΉμλ§ μ§μ€ν μ μμ΅λλ€. κ²μ¦ λ‘μ§μ μ€λ³΅λμ§ μμ κ²μ΄λ©°, κ²μ¦ λμμ λ°μ΄ν°λ₯Ό κ°κ³ μλ ν΄λμ€μ ν¨κ» μ‘΄μ¬νκΈ°μ λ°λμ§νκ² μ± μμ΄ λΆλ°°λμμ΅λλ€.
λ¬Όλ‘ λ¨μ λ μμ΄μ
μ΄ λ°©λ²μ κ°κ°μ λ μ΄μ΄λ§λ€ DTOλ₯Ό λκΈ° λλ¬Έμ μμ€ν λ΄μμ κ΄λ¦¬ν΄μΌνλ ν΄λμ€μ μκ° λΆμ© λμ΄λκ² λ©λλ€. μ§κΈ μμμλ createArticleμ΄λΌλ λ©μλ νλλ§ λ€λ£¨μμ§λ§, ν΄λμ€μ μλ μλΉμ€ λ©μλμ κ°μλ§νΌ λ°μν κ²μ λλ€. μ΄λ 볡μ‘λλ₯Ό λμΌ μλ μμ΅λλ€λ§, κ³μΈ΅μ λΆλ¦¬μ μν€ν μ²λ₯Ό μν΄μλ κ°μν μ μλ μΌμ΄λΌλ μκ°μ΄ λ¦λλ€.
λν μ΄μ λ 컨νΈλ‘€λ¬μμλ μΉ DTOμ μλΉμ€ DTOκ° λ³νμ ν΄μ£Όλ λ‘μ§μ΄ νμνκΈ° λλ¬Έμ, μ΄μ©λ©΄ ν μ€λ‘ λλ μ μμλ 컨νΈλ‘€λ¬μ μ½λκ° λμ΄λκ² λλ λ¬Έμ κ° μκΉλλ€.
λ§λ¬΄λ¦¬
3λ μ μ κ°μ²΄μ§ν₯ νλ‘κ·Έλλ° κ°μλ₯Ό λ€μμ λμ κ΅μλμ λ§μμ΄ λ μ€λ¦ λλ€.
μ€κ³λΌλ κ²μ 볡μ‘ν¨μ ν΅ν΄μ μ μ°ν¨μ μΆκ΅¬νλ νμμ΄λ€.
볡μ‘ν μ½λλ μ μ°νκ³ , λ¨μνκ³ λ³΄κΈ°μ’μ μ½λλ λ³κ²½μ΄ μ΄λ ΅κΈ°μ μ½λμ μ λ΅μ μλ€λ μκ°μ΄ λλλ€. μλΉμ€ λ μ΄μ΄μ DTOλ₯Ό λλ κ² μμ μ λ΅μ μλλλ€. κ·ΈλΌμλ μ΄λ¬ν λ Όμ νΉμ λ¬Έμ μ μ λͺ¨λ₯΄λ μνλ‘ μ½λλ₯Ό μμ±νλ κ²κ³Ό, μκ³ μμμλ λμΌν μ½λλ₯Ό μμ±νλ κ²μ λΆλͺ ν μ°¨μ΄κ° μλ€κ³ μκ°ν©λλ€. μ무μͺΌλ‘ λμμ΄ λμμΌλ©΄ μ’κ² μ΅λλ€.