Spring Boot 애플리케이션에서 엔티티(Entity)와 DTO(Data Transfer Object) 간의 데이터를 변환할 때,
수동으로 변환하는 대신 Mapper를 사용하면 코드의 가독성을 높이고 유지보수를 용이하게 할 수 있다.
이를 위해 흔히 사용되는 라이브러리가 MapStruct이다.
(이전에는 항상 수동으로 하나하나 엔티티와 DTO를 매핑했었는데 이번에 새롭게 MapStruct를 알게 되어 사용해 보게 되었다.)
🩵 MapStruct란?
MapStruct는 자바 애플리케이션에서 객체 간의 매핑을 간편하게 해주는 코드 생성기이다.
컴파일 타임에 매핑 코드를 생성하여 런타임 오류를 줄이고, 매우 효율적인 매핑 구현을 제공한다.
Spring Boot와의 통합도 쉽게 이루어지며, 주로 엔티티를 DTO로 변환하거나 그 반대의 작업에 사용된다.
MapStruct 사용 방법
1. 의존성 추가: 먼저 build.gradle 또는 pom.xml에 MapStruct 의존성을 추가한다.
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.2.Final'
}
2. Mapper 인터페이스 정의: 엔티티와 DTO 간의 매핑을 정의하는 인터페이스를 작성한다.
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserResponseDTO toUserResponseDTO(User user);
User toUser(UserRequestDTO userRequestDTO);
}
- @Mapper : 해당 인터페이스가 MapStruct 매퍼임을 나타낸다.
- Mappers.getMapper(Class) 메소드는 매핑 인스턴스를 생성한다.
3. 커스텀 매핑 정의: 기본 매핑 외에, 특정 필드 이름이 다를 경우 @Mapping 어노테이션을 사용해 매핑을 정의할 수 있다.
@Mapping(source = "username", target = "userName")
UserResponseDTO toUserResponseDTO(User user);
위의 코드에서는 User 엔티티의 username 필드를 UserResponseDTO의 userName 필드로 매핑한다.
4. Service 클래스에서 매핑 사용: Mapper를 Service 클래스에서 사용하여 엔티티를 DTO로 변환하거나, DTO를 엔티티로 변환한다.
@Service
public class UserService {
private final UserRepository userRepository;
private final UserMapper userMapper = UserMapper.INSTANCE;
public UserResponseDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
return userMapper.toUserResponseDTO(user);
}
}
🩵메소드 이름이 곧 기능을 암시하는 방식: 인터페이스 기반 프로그래밍
( MapStruct의 사용 방식이 JPA의 매소드처럼 메소드 명이 곧 기능을 의미하는 방식과 동일한 거 같다는 느낌이 들어 이 부분에 대해 궁금해져서 추가로 찾아보았다.)
Spring Boot에서 UserResponseDTO toUserResponseDTO(User user); 와 같은 메소드 정의는 내부 코드 없이도 정상적으로 동작한다. 이는 MapStruct와 같은 라이브러리에서 인터페이스 기반 프로그래밍 방식을 사용하기 때문이다.
인터페이스 기반 프로그래밍이란?
인터페이스 기반 프로그래밍이란, 실제 로직을 인터페이스에 정의하고 이를 구현하는 클래스나 프레임워크가 내부 구현을 제공하는 방식이다. 이 접근법은 프로그래밍에서 추상화와 느슨한 결합을 구현하는 데 매우 유용하게 사용된다.
Spring Data JPA나 MapStruct 같은 라이브러리는 이 방식을 사용하여, 개발자가 직접 구현하지 않고도 인터페이스 선언만으로 필요한 메소드를 자동으로 생성하고 실행할 수 있게 해 준다.
MapStruct와 인터페이스 기반 프로그래밍
MapStruct는 Java 애플리케이션에서 객체 간의 매핑을 담당하는 라이브러리로, 인터페이스 기반 프로그래밍을 통해 코드의 자동 생성을 지원합니다. 개발자가 UserResponseDTO toUserResponseDTO(User user);와 같은 메소드를 인터페이스에 정의하면, MapStruct는 컴파일 시점에 자동으로 해당 메소드의 구현을 생성합니다.
이 메소드의 이름과 파라미터 타입은 MapStruct가 어떤 기능을 수행해야 하는지 암시하는 역할을 합니다. 예를 들어, 이 메소드는 User 객체를 받아 UserResponseDTO 객체로 변환하는 기능을 수행하도록 명시적으로 지정된 것입니다.

'개발일지 > League Linker' 카테고리의 다른 글
🤦🏻♀️고민하기 - Spring Security_API 경로에 따른 인증 및 역할 관리(1) (2) | 2024.09.04 |
---|---|
[spring boot] - Swagger로 API 문서화하기 (0) | 2024.09.03 |
🚨ERROR - [spring boot] GET 요청 시 406 오류 발생 (0) | 2024.08.31 |
🤦🏻♀️고민하기 - [개발환경] 포크 앤드 풀(Fork and Pull) 워크플로우 (0) | 2024.08.10 |
[개발환경] H2 DB 선택과 연결하기 (0) | 2024.08.09 |