Mapstruct 를 소개할 까 한다.
=> MapStruct는 Java 언어를 위한 코드 생성 라이브러리이다.
=> [Spring] Java Bean 간의 매핑을 크게 단순화하는 코드 생성기
이제 우리는 이를 이용하여 거의 대부분의 도메인에 dto 생성자 함수를 따로 만드는 시간을 없애버릴 것이다.
dto를 정의해주고 vo와 dto를 매핑하는 함수를 인터페이스에 정의만 해주면 알아서 다 해준다.
어노테이션 기반: MapStruct는 @Mapper, @Mapping 등의 어노테이션을 제공하여 매핑 규칙을 정의 해야함. 그럼 해당 규칙에 따라 자동으로 변환 코드를 생성해줌.
타입 안전: MapStruct는 컴파일 시점에 매핑 코드를 생성하므로, 매핑 과정에서의 타입 불일치 등의 문제를 미리 파악할 수 있음.
성능: MapStruct는 리플렉션을 사용하지 않고, 직접 코드를 생성하기 때문에 매핑 성능이 뛰어남.
=> 컴파일 시간에 Bean 매핑을 생성하여 높은 성능을 보장한다는 뜻.
=> 즉, 빌드 자동화 도구로 compile을 진행해줘야함.
유연성: MapStruct는 고급 매핑, 커스텀 매핑 메소드, 표현식 및 상수 매핑 등 다양한 매핑 기능을 제공함.
개꿀: 인터페이스만 생성하면 됨.
한 객체의 데이터를 다른 객체로 복사하는 것이다.
앞서 언급 했듯 우리는 데이터베이스로부터 조회한 데이터를 DTO로 변환할 때 사용할 것이고 이때 객체 매핑이 이루어질 것이다.
코드의 간결성: 객체 매핑 작업을 수행하면 객체 간의 변환을 위한 보일러 플레이트 코드를 제거하고 코드를 더 간결하게 만들 수 있다.
타입 안전성: 객체 매핑 라이브러리를 사용하면 컴파일 시간에 매핑 정보를 생성하고 이를 사용하여 객체를 매핑함.
이는 런타임에러로 발생할 수 있는 타입 관련 오류를 미연에 방지할 수 있다.
개발 효율성: 객체 매핑 작업을 자동화하면 개발자가 편하다.
유연하게 사용하면 되다. 예를들어 아래 코드엔 굳이 DTO를 사용할 필요는 없다.
@Override
public TestInfoResponsePayload getTest() {
List<TestInfo> testInfoList = testInfoRepository.findAll();
ArrayList<TestInfoDTO> testInfoDTOArrayList = new ArrayList<>();
testInfoList.forEach(el -> {
testInfoDTOArrayList.add(DTOMapper.INSTANCE.TestInfo2TestInfoDTO(el));
});
return TestInfoResponsePayload.builder().testList(testInfoDTOArrayList).build();
}
사실 그냥 return testInfoRepository.findAll();
해주면 되는 부분이다.
다만 조회해온 데이터가 변형이 이루어지면(비즈니스 로직을 타면) 반드시 DTO로 실어서 보내야한다.
왜냐하면 순환참조가 일어나기 때문이다.
또 다른 문제는
1. 서비스 메서드를 다른 서비스 메서드에서 사용할 수 있다. (사실 얘가 순환참조)
2. 특히 jpa를 사용하여 update 로직을 작성할 때 entity를 그대로 갖다 쓰면 db가 바뀌어버린다.
바로 이러한 상황들 때문에 따로 dto를 파서 얘를 운용해줘야한다.
@Data
@ToString
public class SomethingInfoDTO {
Type somethingField;
...
}
vo를 변형하여 데이터를 운반할 dto를 선언해준다.
...
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface DTOMapper {
DTOMapper INSTANCE = Mappers.getMapper(DTOMapper.class);
SomethingInfoDTO somethingInfo2SomethingInfoDTO(SomethingInfo somethingInfo);
...
예를 만들어서 갖다가 쓰면 된다.
여기서 앞서 언급한 것 처럼 @Mapper
어노테이션을 사용하여 규칙을 정의하면 된다.
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SomethingServiceImpl implements ISomethingService {
private final SomethingDependency somthingdependency;
...
@Override
public ResponseType somethingMethod() {
List<SomethingInfo> somethingInfoList = somethingRepository.findAll();
ArrayList<SomethingInfoDTO> somethingInfoDTOArrayList = new ArrayList<>();
somethingInfoList.forEach(sometingInfo -> {
somethingInfoDTOArrayList.add(DTOMapper.INSTANCE.somethingInfo2SomethingInfoDTO(sometingInfo));
});
return ResponseType.builder().somethingFieldValueList(somethingInfoDTOArrayList).build();
}
...
이제 서비스 로직에서 이 처럼 불러다 쓰면 된다.