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์ ๋ฃ๊ธฐ
โ ์ ๋ฆฌ ํ
๐ ๋ณด์ | ๋ฏผ๊ฐ ์ ๋ณด ๋ ธ์ถ | ํ์ํ ์ ๋ณด๋ง ์ ๋ฌ |
๐งฑ ํฌ๋งท | ๋ณด๊ธฐ ์ด๋ ค์ด 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 |