Entity ๋Œ€์‹  DTO๋ฅผ ์จ์•ผ ํ•˜๋Š” ์ด์œ 

2025. 6. 22. 20:57ใ†spring

๋ฐ˜์‘ํ˜•

โœ… 1. ๐Ÿ” ๋ณด์•ˆ ๋ฌธ์ œ – ๋ฏผ๊ฐํ•œ ์ •๋ณด ๋…ธ์ถœ ๋ฐฉ์ง€

๐Ÿ’ฌ ์„ค๋ช…

Entity์—๋Š” ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋ฉด ์•ˆ ๋˜๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด๋„ ํ•จ๊ป˜ ํฌํ•จ๋ผ ์žˆ์„ ์ˆ˜ ์žˆ์–ด์š”.
์˜ˆ๋ฅผ ๋“ค์–ด, User ์—”ํ‹ฐํ‹ฐ์— password, email, role ๊ฐ™์€ ํ•„๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด๊ฑธ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์œ„ํ—˜ํ•ฉ๋‹ˆ๋‹ค.

 

โŒ Entity ์ง์ ‘ ๋ฐ˜ํ™˜ ์˜ˆ

@Entity
public class User {
    private Long id;
    private String username;
    private String password;   // โ—๏ธ์ ˆ๋Œ€ ๋…ธ์ถœ๋˜๋ฉด ์•ˆ ๋จ
    private String email;
    private String role;
}
@GetMapping("/users")
public List<User> getUsers() {
    return userService.findAll();  // โ—๏ธpassword๊นŒ์ง€ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌ๋จ
}

 

โœ… DTO ์‚ฌ์šฉ ์˜ˆ

public class UserResponseDto {
    private String username;
    private String email;
}
@GetMapping("/users")
public List<UserResponseDto> getUsers() {
    return userService.findAll().stream()
        .map(user -> new UserResponseDto(user.getUsername(), user.getEmail()))
        .collect(Collectors.toList());
}

๐Ÿ” → DTO๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ์„ ํƒํ•ด์„œ ์ค„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋ณด์•ˆ์— ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

 

โœ… 2. ๐Ÿงฑ ์‘๋‹ต ํฌ๋งท ์ปค์Šคํ„ฐ๋งˆ์ด์ง• – ํ•„์š”ํ•œ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ

๐Ÿ’ฌ ์„ค๋ช…

Entity๋Š” DB ๋ชจ๋ธ์ด๊ธฐ ๋•Œ๋ฌธ์— ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ฃผ๊ธฐ์—” ๋ถˆ์นœ์ ˆํ•œ ๊ตฌ์กฐ์ผ ์ˆ˜ ์žˆ์–ด์š”.
DTO์—์„œ๋Š” ํ•„๋“œ๋ช…์„ ๋ฐ”๊พธ๊ฑฐ๋‚˜, ๋‚ ์งœ ํฌ๋งท ๋ณ€๊ฒฝ, ๊ฐ€๊ณต๋œ ๊ฐ’ ์ถ”๊ฐ€ ๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โŒ Entity ์ง์ ‘ ๋ฐ˜ํ™˜ ์‹œ

@Entity
public class Board {
    private LocalDateTime createdAt;
}

ํด๋ผ์ด์–ธํŠธ๋Š” "createdAt": "2024-10-24T16:25:00" ์ด๋ ‡๊ฒŒ ๋ฐ›์Œ → ๋ณด๊ธฐ ์–ด๋ ต๊ณ  ํ˜•์‹์ด ๋‹ค์–‘ํ•  ์ˆ˜ ์žˆ์Œ.


โœ… DTO๋กœ ํฌ๋งท ๊ฐ€๊ณต

public class BoardResponseDto {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    private LocalDateTime createdAt;
}

๐Ÿ“ฆ ํด๋ผ์ด์–ธํŠธ๋Š” "createdAt": "2024-10-24 16:25"์ฒ˜๋Ÿผ ๋ณด๊ธฐ ์ข‹์€ ํ˜•์‹์œผ๋กœ ๋ฐ›๊ฒŒ ๋จ.

๋˜๋Š” ํ•„๋“œ๋ช…๋„ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ:

@JsonProperty("created_date")
private LocalDateTime createdAt;

 

 

โœ… 3. ๐Ÿ”„ ์œ ์—ฐ์„ฑ ํ™•๋ณด – Entity ๋ณ€๊ฒฝ์ด ์™ธ๋ถ€ API์— ์˜ํ–ฅ ์—†๋„๋ก

๐Ÿ’ฌ ์„ค๋ช…

Entity ๊ตฌ์กฐ๋Š” ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ์–ธ์ œ๋“  ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์–ด์š”.
์˜ˆ๋ฅผ ๋“ค์–ด, DB ์„ค๊ณ„ ๋ณ€๊ฒฝ์œผ๋กœ Board ์—”ํ‹ฐํ‹ฐ์— category ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์„ ๋•Œ,
Entity๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ API ์‘๋‹ต์ด ๋ฐ”๋€Œ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

API ์‘๋‹ต์„ ๊ณ ์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด DTO๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โŒ Entity ์ง์ ‘ ๋ฐ˜ํ™˜ ์‹œ

// ํด๋ผ์ด์–ธํŠธ๋Š” ์ด๋ ‡๊ฒŒ ๋ฐ›๊ฒŒ ๋˜๋Š”๋ฐ
{
  "id": 1,
  "title": "์ œ๋ชฉ",
  "content": "๋‚ด์šฉ",
  "writer": "ํ™๊ธธ๋™",
  "category": "๊ณต์ง€์‚ฌํ•ญ"  // ํ•„๋“œ ์ถ”๊ฐ€๋จ
}

→ ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ


โœ… DTO ์‚ฌ์šฉ ์‹œ

public class BoardResponseDto {
    private Long id;
    private String title;
    private String content;
    private String writer;
}

 

→ Entity๋Š” ๋ฐ”๋€Œ์–ด๋„, DTO๋Š” ๊ณ ์ •๋œ ํ•„๋“œ๋งŒ ์ œ๊ณตํ•˜๋ฏ€๋กœ API ์•ˆ์ •์„ฑ ํ™•๋ณด ๊ฐ€๋Šฅ


โœ… 4. ๐Ÿ” ์ˆœํ™˜์ฐธ์กฐ ๋ฐฉ์ง€ – JPA ์—ฐ๊ด€๊ด€๊ณ„๋กœ ์ธํ•œ JSON ๋ฌดํ•œ ๋ฃจํ”„ ๋ง‰๊ธฐ

๐Ÿ’ฌ ์„ค๋ช…

JPA๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์—์„œ ์„œ๋กœ ์ฐธ์กฐํ•˜๋Š” ํ•„๋“œ๋“ค์ด JSON์œผ๋กœ ๋ณ€ํ™˜๋  ๋•Œ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด์š”.

โŒ ์˜ˆ: Entity ์ง์ ‘ ๋ฐ˜ํ™˜ ์‹œ ์ˆœํ™˜์ฐธ์กฐ

@Entity
public class Board {
    @ManyToOne
    private User writer; // Board → User
}

@Entity
public class User {
    @OneToMany(mappedBy = "writer")
    private List<Board> boards; // User → Board
}

→ Board → User → Board → User → ... ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐœ์ƒ

 

Board ๊ฐ์ฒด๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋ฐ˜ํ™˜ํ•  ๋•Œ, Spring์€ ๋‚ด๋ถ€์ ์œผ๋กœ Jackson์ด๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด Board๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ํ•  ๋•Œ:

{
  "id": 1,
  "title": "์ œ๋ชฉ",
  "writer": {
    "id": 10,
    "name": "ํ™๊ธธ๋™",
    "boards": [  // ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ Board ๋ฆฌ์ŠคํŠธ ์‹œ์ž‘!
      {
        "id": 1,
        "title": "์ œ๋ชฉ",
        "writer": {
          ...
        }
      }
    ]
  }
}

์ด๋ ‡๊ฒŒ JSON ๋ณ€ํ™˜ ์‹œ, Jackson์€ Board → User → List<Board> → User ...
๋์—†์ด ์ฐธ์กฐ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ๋ฌดํ•œํžˆ JSON์„ ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•˜๋‹ค๊ฐ€ StackOverflowError ๋ฐœ์ƒ!

 

์‹คํ–‰ ์‹œ ์—๋Ÿฌ:

com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion

 

โœ… DTO๋กœ ๋ฐฉ์ง€

ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ๊บผ๋‚ด์„œ DTO์— ๋‹ด์œผ๋ฉด, ์ˆœํ™˜ ๊ตฌ์กฐ ์ž์ฒด๊ฐ€ ์‚ฌ๋ผ์ ธ์š”.

public class BoardResponseDto {
    private String title;
    private String writerName;  // writer.getName()๋งŒ ์‚ฌ์šฉ
}

→ User๋ฅผ ์ „๋ถ€ ๋‹ด์ง€ ์•Š๊ณ , writer.getName()์œผ๋กœ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ๊บผ๋‚ด์™€์„œ DTO์— ๋„ฃ๊ธฐ

 

โœ… ์ •๋ฆฌ ํ‘œ

ํ•ญ๋ชฉ๋ฌธ์ œ์  (Entity ์ง์ ‘ ๋ฐ˜ํ™˜ ์‹œ)DTO ์‚ฌ์šฉ ํšจ๊ณผ
๐Ÿ” ๋ณด์•ˆ ๋ฏผ๊ฐ ์ •๋ณด ๋…ธ์ถœ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ์ „๋‹ฌ
๐Ÿงฑ ํฌ๋งท ๋ณด๊ธฐ ์–ด๋ ค์šด JSON, ํ•„๋“œ๋ช… ๋ณด๊ธฐ ์ข‹์€ ํ˜•ํƒœ, ์ด๋ฆ„ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ
๐Ÿ”„ ์œ ์—ฐ์„ฑ Entity ๋ณ€๊ฒฝ ์‹œ API๋„ ์˜ํ–ฅ API ์‘๋‹ต ํ˜•์‹ ๊ณ ์ • ๊ฐ€๋Šฅ
๐Ÿ” ์ˆœํ™˜์ฐธ์กฐ JSON ๋ณ€ํ™˜ ์‹œ ๋ฌดํ•œ ๋ฃจํ”„ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ์ถ”์ถœํ•ด ์ „๋‹ฌ
๋ฐ˜์‘ํ˜•

'spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

์Šคํ”„๋ง ํ•„ํ„ฐ  (1) 2025.06.13
์Šคํ”„๋ง ์ธํ„ฐ์…‰ํ„ฐ(Interceptor)  (0) 2025.06.13
CORS (Cross-Origin Resource Sharing)  (1) 2025.06.13
CSRF (Cross-Site Request Forgery)  (1) 2025.06.13
์Šคํ”„๋ง JWT(JSON Web Token) ์ธ์ฆ  (1) 2025.06.13