Today I Learned
Mapper
#Mapper의 사용 목적
Mapper는 객체 간 변환을 담당하는 컴포넌트로, 주로 DTO ↔ Entity 간 변환에 사용된다.
비즈니스 로직과 데이터 변환 로직을 분리함으로써 코드의 가독성과 유지보수성을 높여준다.
- 계층 분리: Controller, Service, Repository 간 데이터 전달을 명확히 분리
- 중복 제거: 변환 로직을 한 곳에 모아 재사용 가능
- 테스트 용이성: 변환 로직을 독립적으로 테스트 가능
#Mapper 구현 방식
1. 수동 매핑
직접 변환 로직을 작성
- 장점: 세밀한 제어 가능
- 단점: 코드량 증가, 반복 작업 많음
public MemberDto toDto(Member member) {
return new MemberDto(member.getId(), member.getEmail());
}
2. ModelMapper
라이브러리를 활용한 자동 매핑
- 장점: 빠른 개발, 설정만으로 매핑 가능
- 단점: 복잡한 매핑에는 예외 처리 필요
ModelMapper modelMapper = new ModelMapper();
MemberDto dto = modelMapper.map(member, MemberDto.class);
3. MapStruct
컴파일 타임에 코드를 생성하는 방식
- 장점: 성능 우수, 타입 안정성 확보
- 단점: 설정이 다소 복잡할 수 있음
@Mapper
public interface MemberMapper {
MemberDto toDto(Member member);
Member toEntity(MemberDto dto);
}
#Mapper 사용 시 고려사항
1. 변환 로직은 Service에 섞지 않고 Mapper에 집중
2. 필요에 따라 toDto / toEntity를 분리
3. List, Set 등도 변환 메서드로 따로 정의
4. 내부 객체도 Mapper로 분리하여 관리
5. Null-safe 매핑을 고려해야 함
마치며
Mapper에 대해 공부하면서 단순한 변환 도구 이상의 가치를 느낄 수 있었다. 변환 로직이 분리되니 코드의 가독성이 높아지고, 각 계층의 책임이 명확해져 전체 구조가 훨씬 깔끔해지는 효과가 있었다. 특히 Service나 Controller에서 불필요한 로직이 빠지면서 유지보수 측면에서도 장점이 크다는 걸 알게 되었다.
하지만 동시에 Mapper가 항상 정답은 아니라는 점도 인상 깊었다. 자동 매핑에 지나치게 의존하면 의도치 않은 필드 누락이나 타입 불일치로 인해 디버깅이 어려워질 수 있고, 복잡한 매핑 로직이 오히려 코드의 흐름을 흐릴 수도 있다. 결국 Mapper는 상황에 따라 신중하게 적용해야 하며, 명확한 책임과 경계를 설정하는 것이 중요하다는 걸 배울 수 있었다.