사이드 프로젝트를 진행할 때 JPA를 사용하며 Entity라는 개념을 알게 되었다 기존 DTO만 사용하던 형태에서 DTO, Entity로 늘어났다.
처음엔 몰랐지만 Entity, DTO의 수가 점차 많아지고 그 안에 필드 수가 많아지자 두 객체 간 변환을 하는 코드가 상당히 막노동 작업이 되어버렸다.
맘 같아서는 Entity를 그냥 DTO + Entity로 한 번에 쓰고 싶지만 알맞은 용도로 써야 한다는 책의 내용을 보고 수긍했다.
하지만 단순 변환코드 작성이 갈수록 오래 걸렸고 내 코딩 시간을 잡아먹는 게 아까워 고민하고 검색해 본 결과 3가지의 방법을 찾았으며 그중 가장 괜찮은 방법을 소개해 볼까 한다.
일반적으로 DTO, Entity를 변환하는 작업은
따로 getter, setter 를 통해 변환하거나 DTO, Entity Class 내부에 반대 객체를 반환하는 함수를 작성하는 등 명시적으로 코드 작성으로 처리할 것같다.
나는 이런 시간만 잡아먹는 단순 작업을 아래와같이 바꿔보았다.
맨 처음 사용한 방식인데 생각보다 코드가 길며 가독성이 어려운 점이있다.
그리고 무엇보다 너무나도 오류가 생기기 쉬운 구조이다.
// Entity를 DTO로 변환하는 함수
public static <D, E> D entityToDto(E entity, Class<D> dtoClass) {
if (entity == null) {
return null;
}
try {
D dto = dtoClass.newInstance();
for (Field entityField : entity.getClass().getDeclaredFields()) {
entityField.setAccessible(true);
Object value = entityField.get(entity);
try {
Field dtoField = dtoClass.getDeclaredField(entityField.getName());
dtoField.setAccessible(true);
dtoField.set(dto, value);
} catch (NoSuchFieldException ignored) {
}
}
return dto;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Conversion error", e);
}
}
// DTO를 Entity로 변환하는 함수
public static <D, E> E dtoToEntity(D dto, Class<E> entityClass) {
if (dto == null) {
return null;
}
try {
E entity = entityClass.newInstance();
for (Field dtoField : dto.getClass().getDeclaredFields()) {
dtoField.setAccessible(true);
Object value = dtoField.get(dto);
try {
Field entityField = entityClass.getDeclaredField(dtoField.getName());
entityField.setAccessible(true);
entityField.set(entity, value);
} catch (NoSuchFieldException ignored) {
}
}
return entity;
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Conversion error", e);
}
}
예시 코드 만드는중
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
@Mapper
public interface MyMapper {
MyMapper INSTANCE = Mappers.getMapper(MyMapper.class);
MyDto entityToDTO(MyEntity e);
MyEntity dtoToEntity(MyDto d);
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
private final MyMapper myMapper = MyMapper.INSTANCE;
public MyDto getMyEntityById(Long id) {
MyEntity myEntity = MyRepository.findById(id).orElse(null);
return myMapper.entityToDTO(myEntity);
}
public UserDto saveUser(MyDto myDto) {
MyEntity myEntity = MyMapper.dtoToEntity(myDto);
MyEntity savedMyEntity = MyRepository.save(myEntity);
return MyMapper.entityToDTO(savedMyEntity);
}
}
장점
단점
장점
단점
장점
단점
엔터프라이즈급 서비스를 운영하는 기업이 이 MapStruct를 쓰는 경우가 많다하며 여러가지 측면에 우수 하므로 MapStruct를 쓰자!!