Code Review IndexData CRUD 구현 리뷰(Findex)

M2 실제 코드리뷰 피드백 3가지
CodeRabbit + 멘토 리뷰에서 받은 실제 피드백을 중심으로 Before -> After 흐름으로 살펴봐요.

1. 더티체킹 - update()에서 save() 없애기

왜 save() 가 필요 없을까?
동작 흐름
1. findById() -> 영속성 컨텍스트에 엔티티 등록 (1차 캐시)
2. data.update() -> 필드 값 변경
3. @Transactional 종료 -> JPA가 스냅샷과 비교
4. 변경 감지 -> UPDATE SQL 자동 실행
save()를 호출하면 오히려 SELECT -> merge 로직이 추가로 실행됨
- 영속 상태의 엔티티는 트랜잭션 종료 시 자동으로 변경이 감지됨
- update() 메서드를 엔티티 내부에 만들어 캡슐화 - 외부에서 필드 직접 접근 불가
- @Transactional이 없으면 변경 감지가 동작하지 않으니 반드시 필요
2. 중복 방어 - 레이스 컨디션 대응


- 1차 방어: exists() 체크 - 일반적인 중복 요청을 사전 차단
- 2차 방어: DataIntegrityViolationException catch-DBunique 제약으로 최종 보장
- DB 엔티티에 @UniqueConstraint(columnNames = {"index_info)id", "base_date"}) 필수
레이스 컨디션: 두 요청이 거의 동시에 exists() 통과 → 둘 다 save() 도달 → DB에서 막아줌
3. Validation 강화 - @NotNull 만으론 부족


- 가격/거래량 필드는 도메인 상 음수가 불가능하므로 @DecimalMin("0.0") 추가
- 등락률(fluctuationRate)은 음수가 가능하므로 범위 제한 없음
- IndexDataUpdateRequest 에도 동일하게 적용 필요
Validation은 Controller 진입 시점에서 막아줘야 Service 코드가 깔끔해짐. @Valid와 함께 사용.
4. MapStruct 전환 - from() 메서드 제거


- 필드가 추가되거나 이름이 바뀌어도 MapStruct가 자동으로 처리
- 중첩 객체(indexInfo.indexName)는 @Mapping 으로 명시 필요
- 맵핑 불일치는 컴파일 시점에 오류로 잡힘-런타임 NPE 예방
from()방식 대비 코드량 80% 감소 + 컴파일 타임 안전성 확보