๐ค ๊ทธ๋ฅ DTO ํ๋๋ก ์ฐ๋ฉด ์ ๋๋๋ ์ง๋ฌธ์ ์ง์ง ๋ง์ด ๋ฐ๋๋ค.
๊ฒฐ๋ก ๋ถํฐ ๋งํ๋ฉด ๋ถ๋ฆฌํ๋ ๊ฒ ๋ง๋ค.
๊ทธ๋ฆฌ๊ณ ํ๋ก์ ํธ๊ฐ ๊ธ์์ค๋ฝ๊ฒ Mybatis ๋ก ๋ณ๊ฒฝ๋์ด ์ด๋ ๊ฒ ์ฐ๋ ๊ฒ์ด์ง, JPA ์ ๊ฒฝ์ฐ๋ Entity ๊น์ง ๋ถ๋ฆฌํ์ ๊ฒ์ด๋ค.
๋๋ ์ฒจ์๋ DTO๋ง ์ฐ๋ ค๊ณ ํ์๋๋ฐ (Mapper์์ DTO๋ก ๋ฐ์์ ๊ทธ๋๋ก ๋ฆฌํดํ๋ฉด ํธํ๋๊น..)
๋ช ๋ฒ ๊ฒช์ด๋ณด๋๊น ๋ถ๋ฆฌํ๋๊ฒ ๋ง๋๋ผ
๐ฆ ๋ด๊ฐ ์ฌ์ฉํ๋ ํจํด
domain/
โโโ book/
โโโ controller/
โ โโโ BookController.java
โโโ dto/
โ โโโ BookDto.java ← DB ๋งคํ์ฉ
โโโ mapper/
โ โโโ BookMapper.java ← MyBatis ์ธํฐํ์ด์ค
โโโ model/
โ โโโ BookListResponse.java ← API ์๋ต์ฉ
โ โโโ BookDetailResponse.java
โโโ service/
โโโ BookService.java
DTO - DB ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋๋ค
@Getter
@Setter
public class BookDto {
private Long seq;
private String title;
private String author;
private String isbn;
private String publisherCode; // DB์๋ ์ฝ๋๋ก ์ ์ฅ
private String statusCode; // 'A': ๋์ถ๊ฐ๋ฅ, 'B': ๋์ถ์ค, 'D': ํ๊ธฐ
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String createdBy; // ๋ด๋ถ ๊ด๋ฆฌ์ฉ
private String internalMemo; // ๋ฏผ๊ฐ ์ ๋ณด (API ๋
ธ์ถ X)
}
์ด๊ฑด ๋ง ๊ทธ๋๋ก DB ๊ฒฐ๊ณผ ๋ด๋ ์ฉ๋๋ค.
์ปฌ๋ผ์ด ๋์ด๋๋ , ์ด๋ฆ์ด ๋ฐ๋๋ ์๊ด์๋ค.
Response - API๋ก ๋ด๋ณด๋ผ ํํ๋ฅผ ์ ์ํ๋ค
@Getter
@Builder
public class BookListResponse {
private Long id;
private String title;
private String author;
private String publisher; // ์ฝ๋ → ์ด๋ฆ์ผ๋ก ๋ณํ๋จ
private String status; // 'A' → "๋์ถ ๊ฐ๋ฅ"
private String registeredDate; // yyyy-MM-dd ํฌ๋งท
public static BookListResponse from(BookDto dto) {
return BookListResponse.builder()
.id(dto.getSeq())
.title(dto.getTitle())
.author(dto.getAuthor())
.publisher(PublisherCode.toName(dto.getPublisherCode()))
.status(BookStatus.fromCode(dto.getStatusCode()).getDisplayName())
.registeredDate(formatDate(dto.getCreatedAt()))
.build();
}
private static String formatDate(LocalDateTime dateTime) {
if (dateTime == null) return null;
return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
}
์ฌ๊ธฐ์๋ ํ๋ก ํธ๊ฐ ๋ฐ๋ก ์ฐ๊ธฐ ์ข์ ํํ๋ก ๋ง๋ ๋ค.
DB ๊ตฌ์กฐ๋ ์ ๊ฒฝ ์ ์จ๋ ๋๋ค.
Service์์ ๋ณํ
@Service
@RequiredArgsConstructor
public class BookService {
private final BookMapper bookMapper;
public List<BookListResponse> getBookList(BookSearchCondition condition) {
List<BookDto> books = bookMapper.selectBookList(condition);
return books.stream()
.map(BookListResponse::from) // DTO → Response ๋ณํ
.collect(Collectors.toList());
}
}
Service๊ฐ ๊น๋ํด์ง๋ค. ๋น์ฆ๋์ค ๋ก์ง ์ฝ๊ธฐ๋ ํธํ๋ค.
๐ฏ ์ ์ด๋ ๊ฒ ๋ถ๋ฆฌํ ๊น?
1. DB ์คํค๋ง ๋ฐ๋์ด๋ API๋ ์ ๊ฑด๋๋ฆฌ๊ณ ์ถ์ด์
DB ์ปฌ๋ผ๋ช
์ด `pub_code`์์ `publisher_cd`๋ก ๋ฐ๋์ด๋?
→ DTO๋ง ์์ ํ๋ฉด ๋จ. Response๋ ๊ทธ๋๋ก.
// DTO๋ง ๋ณ๊ฒฝ
public class BookDto {
// private String pubCode; // before
private String publisherCd; // after - MyBatis ๋งคํ๋ง ์์
}
// Response๋ ๋ณ๊ฒฝ ์์ - API ์คํ ์ ์ง
public class BookListResponse {
private String publisher; // ๊ทธ๋๋ก
}
2. ๋ฏผ๊ฐํ ์ ๋ณด๊ฐ ์ค์๋ก ๋ ธ์ถ๋๋๊ฑธ ๋ง๊ธฐ ์ํด
// DTO์๋ ์์ง๋ง
public class BookDto {
private String internalMemo; // ๋ด๋ถ ๊ด๋ฆฌ์ฉ ๋ฉ๋ชจ
private String createdBy; // ๋ฑ๋ก์ ์ฌ๋ฒ
private BigDecimal purchasePrice; // ๊ตฌ๋งค๊ฐ
}
// Response์์๋ ์๋์ ์ผ๋ก ์ ์ธ
public class BookListResponse {
// ์ ํ๋๋ค ์์ = API์ ์ ๋ ๋
ธ์ถ ์ ๋จ
}
>> `@JsonIgnore` ๋ก๋ ๊ฐ๋ฅํ์ง๋ง, ์์ ํ๋๊ฐ ์๋ ๊ฒ ๋ ์์ ํ๋ค.
3. API ๋ฒ์ ๊ด๋ฆฌ๊ฐ ์ฌ์์ง๋ค
๐ V1, V2 API๋ฅผ ๋์์ ์ด์ํด์ผ ํ ๋:
// V1 - ๊ธฐ์กด ์๋ต
public class BookListResponseV1 {
private String status; // "๋์ถ ๊ฐ๋ฅ"
public static BookListResponseV1 from(BookDto dto) { ... }
}
// V2 - ์๋ก์ด ์๋ต ํฌ๋งท
public class BookListResponseV2 {
private BookStatus status; // { code: "A", name: "๋์ถ ๊ฐ๋ฅ", available: true }
public static BookListResponseV2 from(BookDto dto) { ... }
}
>> DTO๋ ํ๋, Response๋ง ๋ฒ์ ๋ณ๋ก ๊ด๋ฆฌ.
4. ํ๋ก ํธ์๋ ์นํ์ ์ธ ์๋ต์ ๋ง๋ค ์ ์๋ค
// DB์๋ ์ด๋ ๊ฒ ์ ์ฅ๋์ด ์์ง๋ง
BookDto {
statusCode: "A",
publisherCode: "PUB001",
createdAt: "2024-01-15T14:30:00"
}
// API ์๋ต์ ํ๋ก ํธ๊ฐ ๋ฐ๋ก ์ธ ์ ์๊ฒ
BookListResponse {
status: "๋์ถ ๊ฐ๋ฅ",
publisher: "๋ฏผ์์ฌ",
registeredDate: "2024-01-15"
}
๐ก from() ๋ฉ์๋๋ฅผ Response์ ๋๋ ์ด์
๋ณํ ๋ก์ง์ ์ด๋์ ๋๊น? ์ธ ๊ฐ์ง ์ ํ์ง๊ฐ ์๋ค:
| ์์น | ์์ | ํน์ง |
| Service | `toResponse(dto)` | Service๊ฐ ๋น๋ํด์ง |
| ๋ณ๋ Mapper | `BookMapper.toResponse(dto)` | ํ์ผ์ด ๋์ด๋จ |
| Response ๋ด๋ถ | `BookListResponse.from(dto)` | ์์ง๋ ๋์ |
๋๋ Response ํด๋์ค ๋ด๋ถ์ `from()` ์ ์ ๋ฉ์๋๋ฅผ ๋๋ ๋ฐฉ์์ ์ ํธํ๋ค.
// Response๊ฐ "๋๋ ์ด DTO๋ก๋ถํฐ ๋ง๋ค์ด์ง ์ ์์ด"๋ฅผ ์ค์ค๋ก ์ ์
public static BookListResponse from(BookDto dto) {
return BookListResponse.builder()
.id(dto.getSeq())
// ... ๋ณํ ๋ก์ง
.build();
}
์ฅ์ :
- ์์ง๋: ๋ณํ ๋ก์ง์ด Response ํด๋์ค ์์ ์บก์ํ๋จ
- ๊ฐ๋ ์ฑ: `BookListResponse.from(dto)` - ์ฝ๊ธฐ ์ฌ์
- ํ ์คํธ: Response ํด๋์ค๋ง ๋จ์ ํ ์คํธํ๋ฉด ๋จ
๐ท๏ธ ์ ๋ฆฌ
| ๊ตฌ๋ถ | DTO | Response |
| ์ญํ | DB ↔ ์ ํ๋ฆฌ์ผ์ด์ | ์ ํ๋ฆฌ์ผ์ด์ ↔ ํด๋ผ์ด์ธํธ |
| ์์น | `dto/` ํจํค์ง | `model/` ํจํค์ง |
| ํ๋ | DB ์ปฌ๋ผ๊ณผ 1:1 | API ์คํ์ ๋ง๊ฒ ๊ฐ๊ณต |
| ๋ฏผ๊ฐ์ ๋ณด | ํฌํจ ๊ฐ๋ฅ | ๋ ธ์ถํ ๊ฒ๋ง |
| ๋ณ๊ฒฝ ์ฃผ๊ธฐ | DB ์คํค๋ง ๋ณ๊ฒฝ ์ | API ์คํ ๋ณ๊ฒฝ ์ |
์ฒ์์ ์ค๊ณํ ๋ ํด๋์ค๊ฐ ๋๋ฌด ๋ง์์ง๋ ๊ฑฐ ์๋๊ฐ ์๊ฐํ์ง๋ง ์ ์ฉํด๋ณด๋ ๋งค์ฐ ํฉ๋ฆฌ์ ์ธ ์ ํ์ด๋ผ๋ ์๊ฐ์ด ๋ ๋ค ใ
'๐ป ๊ฐ๋ฐ > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Spring Cloud Gateway์์ JWT ์ธ์ฆ ํํฐ ๊ตฌํํ๊ธฐ (0) | 2026.01.13 |
|---|