| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- MySQL
- 배열
- dao
- explode()
- php
- 오류
- error
- 멀티캠퍼스it부트캠프
- 정규식
- INSERT
- DOM
- myshortcut
- DTO
- OOP
- 깃허브
- ES6
- react
- 노션
- JDBC
- formula
- 부트캠프후기
- jQuery
- node.js
- JavaScript
- 객체지향
- Java
- 현대이지웰java풀스택개발자아카데미6월
- oracle
- strpos()
- mybatis
- Today
- Total
코딩짜는 일상
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 21차 - DTO와 Map을 이용한 데이터베이스 조회와 API 결과물 차이 비교 본문
[현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 21차 - DTO와 Map을 이용한 데이터베이스 조회와 API 결과물 차이 비교
Remily 2025. 12. 9. 23:54📚 서론
이번주는... 제가 최근에 어마어마한 업보 청산을 당해서...
...그것에 대해 기록 해볼까 합니다.🥲

시작은 2개 이상의 테이블을 JOIN하여 데이터를 가져올 때
2개 테이블들의 필드가 전부 있는 별도의 DTO를 만들어야 하는 번거로움에서 시작되었습니다.
이때 강사님께 Map<String, Object>를 사용하여 데이터를 받는 방법도 있다고 조언 받았고
단일 테이블을 조회하는 게 아니면 대부분 Map을 이용하여 서비스를 구현하게 되었습니다.🤪
프로젝트 초반에는 별 문제가 없었는데
중반 쯤 지나 구현할 리엑트 컴포넌트가 늘어나면서
작업을 좀 줄여보려고 컴포넌트를 분리하고 재사용하면서 문제가 생겼습니다.😓
1️⃣ 필드명 스타일 통일의 필요성
오라클은 큰따옴표(")를 쓰지 않는 한 대소문자 구분을 하지 않습니다.
때문에 테이블을 만들때도 테이블명, 필드명에 큰따옴표를 쓰지 않으면
모든 알파벳이 대문자로 통일됩니다.
이 상태에서 Map으로 데이터를 받게 되면 대문자인 테이블 필드명을 그대로 따라
서비스로 전달되는 데이터도 필드가 전부 대문자로 통일됩니다.
그런데 저는 가독성을 위해 DTO를 만들 땐 필드명을 카멜타입으로 작성했기에
리엑트에서 보면 어떤 건 only 대문자, 어떤 건 카멜타입으로 데이터가 들어오기에
컴포넌트가 많아질 수록 햇갈릴 수 밖에 없었습니다.

그래서 통일하기 위해 DTO의 모든 필드명을 대문자로 바꾸면서 새로운 문제가 발생합니다.
2️⃣ 필드명 스타일 섞임


DTO의 모든 필드명 스타일을 대문자로 통일했지만
결과는 위 사진처럼 필드명이 전부 소문자 스타일로 나오고
날짜만 대문자 스타일로 한 번 더 출력됩니다.
DTO를 보면 ODREGDATE에만 @JsonFormat 어노테이션이 붙어있는데
날짜 포맷을 처리하는 과정에서 DTO에 명시된 필드명 스타일을 인지하고
대문자로 한 번 더 출력한 것이 아닌가 싶습니다.
자, 그럼 다른 필드는 왜 전부 소문자로 스타일이 통일된 것일까요?
🎷 원인 : Lomboc과 Jackson의 환장의 콜라보
1️⃣ 롬복의 Getter/Setter 네이밍 규칙
롬복(Lomboc)은 주로 DTO, VO, Entity 같은 클래스에서 Getter/Setter 또는 생성자 또는 toString 같은
반복적인 코드를 자동으로 생성해주는 자바 라이브러리 입니다.
저는 주로 Getter와 Setter를 만드는 데 사용했는데 필드명이 전부 대문자 스타일이면
Getter는 getODREGDATE(), Setter는 setODREGDATE()가 됩니다.😉
2️⃣ JavaBeans 규칙
잭슨(Jackson)은 자바 객체를 JSON문자열로 전환해주는 라이브러리 입니다.
JavaBeans 규칙을 적용해서 자동으로 프로퍼티명을 만들고 api 응답에 회신하기 위해
결과 객체를 JSON으로 자동 변환해줍니다.
JavaBeans규칙을 차근차근 살펴보면 우선 get/set/is 같은 접두사를 제거한 뒤
첫 글자를 소문자로 변경해 주는데, "앞의 두 글자가 모두 대문자면 그대로 둔다"는 규칙이 있어서
getODREGDATE() 를 통해 자동으로 생성되는 프로퍼티명은 계획대로 ODREGDATE이 됩니다.
그런데 여기서 로우 카멜 케이스(LowerCamelCase)를 기본으로 하는 Jackson의 자체 규칙이 추가로 적용되고
ODREGDATE가 결과적으로 odregdate가 되면서 API의 모든 필드명은 소문자 스타일로 통일됩니다.😨
🎺 해결책들
여러가지 해결책이 있습니다.
전부 AI들이 추천해준 방식이죠...
먼저 Jackson의 네이밍 규칙을 어퍼 카멜 케이스(UpperCamelCase)로 변경하는 방법입니다.
하지만 이 방법은 모든 API에 통합 적용되는 것이며
팀 프로젝트 중반인 지금와서 변경하면 모든 API를 다시 검수해야 하므로 부적절합니다.😅
그 다음으로는 @JsonProperty 어노테이션을 붙여서 명시적으로 JSON 필드명을 지정하는 것입니다.
하지만 어노테이션을 따라 만든 대문자JSON 필드명 하나, Jackson이 자동으로 만든 소문자JSON 필드명 하나,
이렇게 총 2개가 각 필드마다 생겨나므로 주고 받는 데이터 패킷이 2배가 되어 부적절 합니다.😓
다음은 DTO를 소문자 스타일로 바꾸고 Map<String, Object>으로 받는 데이터도 소문자 스타일로 변경하는 것인데요.
이 방법은 resultMap에서 오라클 필드명과 매핑해줄 소문자 스타일의 필드명을 정의해주거나
약어(AS) 문법으로 각 필드마다 소문자 스타일의 필드명을 정의해주면 됩니다.
SELECT PRDNAME prdname, PRDPRICE prdprice, PRDSTOCK prdstock, ...
FROM PRODUCT
하지만 모든 필드명을 일일이 언급해주면 필드를 찾는 연산에서 쿼리 실행 시간이 증가할 수 있고
차라리 DTO를 만드는 편이 가독성이 더 좋을 것이므로 Map을 쓰는 의미가 없어집니다.😫

📯 최종 해결책 : DTO를 카멜 스타일로 통일하고 Map 방식을 배제
그리하여 최종 선택한 해결책은 다시 처음으로 돌아와
Map<String, Object>로 받는 방식을 전부 DTO로 변경하고
DTO의 필드명 스타일을 카멜 스타일로 통일하는 방식을 채택했습니다.
일과가 끝날 쯤 되어서야 카멜 스타일로 만든 DTO는 카멜 스타일 그대로 받았다는 걸 떠올린 덕분이었어요...🥲
소문자나 대문자로 통일하면 필드명 가독성이 떨어지니까 카멜 스타일로 통일했고
Map을 쓰는 의미가 없어졌으니 DTO로 데이터를 받도록 변경한 것입니다.

🔥 결론
위 에피소드를 겪으면서... 차라리 처음부터 DTO를 쓸껄 하고 얼마나 후회 했는지 모릅니다.😣
그냥 얌전히 DTO를 썼으면 @JsonFormat을 이용해 편하게 날짜형식을 바꿀 수 있었을텐데
괜히 Map으로 받아온다고 for문으로 KST포맷터를 만들어 일일이 적용하는 방식도 추가했었거든요.
// KST 포맷터
private static final DateTimeFormatter KST_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.of("Asia/Seoul"));
// 날짜 필드 목록 (필요 시 추가 가능)
private static final List<String> DATE_FIELDS = Arrays.asList(
"ODREGDATE"
, "USERREGDATE"
);
// 불러온 데이터에 for로 포매터 사용...
List<Map<String, Object>> resultList = dao.메소드(params);
for (Map<String, Object> row : resultList) {
for (String field : DATE_FIELDS) {
Object value = row.get(field);
if (value instanceof Timestamp ts) {
row.put(field, KST_FORMATTER.format(ts.toInstant()));
}
}
}
어짜피 롬복 쓸 거... getter/setter 만드는 귀찮음도 없는데 진짜 왜 그랬지...
저는 DTO가 필요한 이유를 단순히 계층간에 데이터 전달을 위해서라고만 생각했는데
주고 받는 필드에 무엇이 있는지 바로 확인할 수 있어 테이블을 외우고 있지 않은 사람도 api를 쓰기 쉬워졌고
2개의 DTO를 필드로 갖고 있는 별도의 DTO를 만들어줌으로써
모든 필드명을 언급하지 않아도 다른 DTO의 모든 필드를 사용할 수 있는 등
사용하는 방법이 생각보다 유연해서 답답하지 않았습니다.

프로젝트 하면서 별의 별 에피소드를 다 겪는 것 같은데
내일은 또 무슨 에피소드가 기다리고 있을지 기다...려...지네요...😂
'TIL > TIL 챌린지' 카테고리의 다른 글
| [현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 20차 - 프로젝트에서 MyBatis 사용하기2 (feat.Oracle) (0) | 2025.12.01 |
|---|---|
| [현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 19차 - 프로젝트에서 MyBatis 사용하기 (feat.Oracle) (0) | 2025.11.25 |
| [현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 18차 - MSA (0) | 2025.11.18 |
| [현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 17차 - 오라클과 MySQL의 차이 (0) | 2025.11.11 |
| [현대이지웰 Java 풀스택 개발자 아카데미 6월] TIL 16차 - 깃허브 그룹 프로젝트(이슈 생성, 커밋, 풀 리퀘스트) (0) | 2025.11.05 |
