Java/Spring으로 웹 개발을 진행하다 보면, Entity와 DTO간의 변환을 수행하는 로직을 마주하게 된다.
일단 코드로 예시를 한번 보자
Entity - DTO 매핑 (수동)
UserEntity
@Getter
@AllArgsConstructor
public class UserEntity {
private Long id;
private String name;
private String email;
}
UserDTO
@Getter
@Setter
@AllArgsConstructor
public class UserDTO {
private Long userId;
private String userName;
private String userEmail;
// Getters and Setters
}
UserMapper
public class UserMapper {
// Entity to DTO 변환 메서드
public static UserDTO toDTO(UserEntity userEntity) {
if (userEntity == null) {
return null;
}
UserDTO userDTO = new UserDTO(
userEntity.getId(),
userEntity.getName(),
userEntity.getEmail()
);
return userDTO;
}
// DTO to Entity 변환 메서드
public static UserEntity toEntity(UserDTO userDTO) {
if (userDTO == null) {
return null;
}
UserEntity userEntity = new UserEntity(
userDTO.getUserId(),
userDTO.getUserName(),
userDTO.getUserEmail()
);
return userEntity;
}
}
이런식으로 UserMapper에 객체간 변환을 위해 필드를 하나하나 변환하는 메서드로 로직을 작성해줘야 한다.
이 방식은 필드가 많아질수록 코드가 복잡해지고, 유지보수가 어려워 진다.
이럴 떄 Mapstruct 라이브러리를 사용하면 매핑 코드를 자동으로 생성하여 수동 매핑에서 발생할 수 있는 실수를 줄일 수도 있다.
Mapstruct란?
MapStruct는 Java에서 객체 간 매핑을 쉽게 해주는 라이브러리이다.
일반적으로 DTO - Entity 간의 데이터를 변환해야 하는 경우가 많은데, MapStruct는 이러한 변환 작업을 컴파일 시점에 자동으로 처리하는 코드를 생성하여 성능을 높이고, 매핑 코드 작성의 수고를 덜어준다.
특징
- 컴파일 타임 매핑: MapStruct는 런타임이 아니라 컴파일 타임에 매핑 코드를 생성하므로 성능에 영향을 주지 않고, 타입 안정성을 보장한다.
- 빠른 매핑 성능: 컴파일 타임에 코드를 생성하므로, 리플렉션을 사용하는 매핑 라이브러리보다 빠르다.
- 간단한 사용법: 매핑이 필요한 메서드를 선언하기만 하면 매핑 구현체가 자동 생성된다.
- 다양한 매핑 기능: 기본 매핑뿐만 아니라 필드 이름이 다를 때의 매핑, 복잡한 객체 매핑, 컬렉션 및 리스트 매핑 등 다양한 매핑을 제공한다.
사용법 및 유의 사항
1. 빌드 파일에 의존성 추가
Maven
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
<scope>provided</scope>
</dependency>
Gradle
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}
2. Lombok과 Mapstruct
MapStruct로 맵핑을 하려고 할때
build.gradle에서 의존성 순서에 따라 MapStruct가 생성하는 Mapper의 구현체의 내용이 다르게 생성된다.
만약, MapStruct를 먼저 추가하고 Lombok의 의존성을 추가할 경우
Mapper 구현체안에 Setter와 Getter로 매핑을 해주는 내용이 생성되지 않는다.
그러므로 아래와 같이 꼭! Lombok을 먼저 추가하고, MapStruct를 후에 추가하자!
//Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
//mapstruct
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
Mapstruct 사용
Entity - DTO 매핑 (자동)
UserMapper
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "name", target = "userName"),
@Mapping(source = "email", target = "userEmail")
})
UserDTO toDTO(UserEntity userEntity);
@InheritInverseConfiguration
UserEntity toEntity(UserDTO userDTO);
}
- Interface로 UserMapper를 만든다.
- @Mapper: 매핑 인터페이스에서 MapStruct를 사용한다고 지정하는 어노테이션이다.
- @Mappings: 여러 개의 @Mapping을 한 번에 설정할 수 있다.
- @Mapper(componentModel = "spring"): Spring과 함께 사용할 때 componentModel을 "spring"으로 설정하면 Spring Bean으로 등록된다.
- @Mapping: 특정 필드를 매핑할 때 사용하며, source와 target 속성을 통해 각 필드 간의 매핑을 정의할 수 있다.
- @InheritInverseConfiguration: 반대 방향으로의 매핑 메서드를 자동으로 생성해준다.
- @MappingTarget: 기존 객체를 업데이트할 때 매핑 결과를 기존 객체에 저장할 수 있다.
UserService
// Spring에서 UserMapper를 주입 받아 사용
@Service
public class UserService {
private final UserMapper userMapper;
public UserService(UserMapper userMapper) {
this.userMapper = userMapper;
}
public UserDTO getUserDTO(UserEntity userEntity) {
return userMapper.toDTO(userEntity); // Entity -> DTO
}
}
'JAVA' 카테고리의 다른 글
| [Java] Lombok 이란? (4) | 2024.11.08 |
|---|---|
| [자바/JAVA] 부모 참조(Super, Super Class) (0) | 2024.06.14 |
| [자바/JAVA] 상속(Inheritance) (0) | 2024.06.14 |
| [자바/JAVA] final(상수) (3) | 2024.06.13 |
| [자바/JAVA] Static변수와 Static메서드 (0) | 2024.06.13 |