# MapStruct란? **MapStruct**는 Java Bean 간의 매핑을 쉽게 해주는 코드 생성 기반 매퍼 프레임워크입니다. DTO(Data Transfer Object)와 Entity 간의 변환을 할 때 반복되는 getter/setter 작업을 자동화해주며, **런타임이 아닌 컴파일 타임에 매핑 코드를 생성**하므로 성능도 뛰어납니다. ## 주요 특징 - **컴파일 타임에 코드 생성** (런타임 Reflection 사용 X) - Lombok과 잘 연동됨 - 커스터마이징 가능 (부분 매핑, `@Mapping` 등) - Spring과 쉽게 통합 --- ## Spring Boot에서 MapStruct 사용 예시 ### 1. Gradle 설정 ```kotlin dependencies { implementation ("org.mapstruct:mapstruct:1.6.3") annotationProcessor ("org.mapstruct:mapstruct-processor:1.6.3") } tasks.withType(JavaCompile) { options.annotationProcessorPath = configurations.annotationProcessor } ``` --- ### 2. DTO 및 Entity 클래스 ```java // User.java (Entity) @Getter @Setter public class User { private Long id; private String name; private String email; } ``` ```java // UserDto.java @Getter @Setter public class UserDto { private String name; private String email; } ``` --- ### 3. 매퍼 인터페이스 작성 ```java @Mapper(componentModel = "spring") // Spring Bean으로 등록 public interface UserMapper { UserDto toDto(User user); User toEntity(UserDto userDto); } ``` > `componentModel = "spring"`을 설정하면 Spring의 `@Component`로 등록되므로, 의존성 주입이 가능합니다. --- ### 4. 서비스에서 사용 ```java @Service @RequiredArgsConstructor public class UserService { private final UserMapper userMapper; public UserDto getUserDto(User user) { return userMapper.toDto(user); } } ``` --- ## 마무리 MapStruct는 반복적인 매핑 코드를 없애고, 명시적이고 빠른 매핑을 제공하는 훌륭한 도구입니다. 특히 **Spring Boot + Lombok**과 함께 사용하면 생산성과 유지보수성이 크게 향상됩니다. 필요하다면 `@Mapping` 어노테이션을 사용해 필드명을 다르게 매핑하거나, 일부 필드만 매핑하는 커스터마이징도 가능합니다. --- 좋습니다! 이번에는 **MapStruct의 고급 기능**들을 소개해드릴게요. 실무에서 자주 쓰이는 매핑 커스터마이징 기능과 예제 중심으로 설명드릴게요. --- ## MapStruct 고급 기능 ### 1. **@Mapping**: 이름이 다른 필드 매핑 DTO와 Entity의 필드명이 다를 경우 사용합니다. ```java @Getter @Setter public class User { private Long id; private String fullName; } ``` ```java @Getter @Setter public class UserDto { private String name; } ``` ```java @Mapper(componentModel = "spring") public interface UserMapper { @Mapping(source = "fullName", target = "name") UserDto toDto(User user); } ``` > `source`: 원본 객체의 필드 > `target`: 매핑될 대상 객체의 필드 --- ### 2. **@Mapping(target = ..., ignore = true)**: 특정 필드 무시 ```java @Mapping(target = "id", ignore = true) User toEntity(UserDto dto); ``` `id`는 DB에서 자동 생성되므로 무시할 때 자주 사용합니다. --- ### 3. **@InheritInverseConfiguration**: 역 매핑 정의 재사용 ```java @Mapper(componentModel = "spring") public interface UserMapper { @Mapping(source = "fullName", target = "name") UserDto toDto(User user); @InheritInverseConfiguration User toEntity(UserDto dto); } ``` > `toDto`에 정의된 매핑 규칙을 `toEntity`에도 반대로 적용합니다. --- ### 4. **@AfterMapping / @BeforeMapping**: 매핑 전후 로직 삽입 ```java @Mapper(componentModel = "spring") public interface UserMapper { UserDto toDto(User user); @AfterMapping default void enrichDto(@MappingTarget UserDto dto) { dto.setName(dto.getName().toUpperCase()); // 예: 이름을 대문자로 처리 } } ``` --- ### 5. **중첩 객체 매핑 (Nested Mapping)** ```java @Getter @Setter public class User { private Address address; } @Getter @Setter public class Address { private String city; } ``` ```java @Getter @Setter public class UserDto { private String city; } ``` ```java @Mapping(source = "address.city", target = "city") UserDto toDto(User user); ``` > 중첩된 객체의 필드도 `. (dot)` 문법으로 접근 가능합니다. --- ### 6. **Collection 매핑** ```java List toDtoList(List users); Set toEntitySet(Set dtos); ``` > List, Set, Map 같은 컬렉션도 자동 매핑됩니다. --- ### 7. **Mapper 간 의존성 (다른 Mapper 사용)** ```java @Mapper(componentModel = "spring", uses = { AddressMapper.class }) public interface UserMapper { UserDto toDto(User user); } ``` > 복잡한 구조에서는 하위 매퍼를 만들어 분리할 수 있어요. --- ## 정리 MapStruct 고급 기능을 사용하면 다음과 같은 작업이 쉬워집니다: - 필드명이 다른 경우 매핑 - 무시할 필드 지정 - 매핑 재사용 (`@InheritInverseConfiguration`) - 후처리 로직 삽입 - 복잡한 구조/컬렉션/다형성 처리 --- 좋아요! 이번에는 **MapStruct의 고급 기능** 중에서도 실무에서 자주 사용되는 **Enum 매핑**, **커스텀 매핑 메서드**, **Builder 패턴 지원**에 대해 자세히 설명드릴게요. --- ## 1. Enum 매핑 MapStruct는 기본적으로 **같은 이름을 가진 Enum 상수끼리 자동으로 매핑**합니다. ### 예시: 이름이 같은 Enum 매핑 ```java public enum Role { ADMIN, USER } public enum RoleDto { ADMIN, USER } ``` ```java @Mapper(componentModel = "spring") public interface UserMapper { RoleDto toDto(Role role); } ``` > 이름이 같다면 자동으로 매핑됩니다. > 이름이 다르면 `@ValueMapping` 또는 `@Mapping`을 사용해야 해요. ### 예시: 이름이 다른 Enum 매핑 ```java public enum Role { ADMINISTRATOR, NORMAL_USER } public enum RoleDto { ADMIN, USER } ``` ```java @ValueMappings({ @ValueMapping(source = "ADMINISTRATOR", target = "ADMIN"), @ValueMapping(source = "NORMAL_USER", target = "USER") }) RoleDto toDto(Role role); ``` --- ## 2. 커스텀 매핑 메서드 MapStruct는 매핑 중 호출할 **사용자 정의 메서드**도 지원합니다. ### 예시: 날짜 형식 변환 ```java @Getter @Setter public class User { private LocalDateTime registeredAt; } @Getter @Setter public class UserDto { private String registeredDate; } ``` ```java @Mapper(componentModel = "spring") public interface UserMapper { @Mapping(source = "registeredAt", target = "registeredDate") UserDto toDto(User user); default String map(LocalDateTime dateTime) { return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } } ``` > MapStruct는 `LocalDateTime -> String` 변환 시 `map(...)` 메서드를 자동으로 호출합니다. --- ## 3. Builder 패턴 지원 Builder 패턴을 사용하는 객체도 MapStruct에서 지원합니다. Lombok의 `@Builder`를 쓰거나 직접 Builder 클래스를 만든 경우에도 동작합니다. ### 예시: Lombok + Builder ```java @Getter @Setter @Builder public class UserDto { private String name; private String email; } ``` ```java @Mapper(componentModel = "spring", builder = @Builder(disableBuilder = false)) public interface UserMapper { UserDto toDto(User user); } ``` > MapStruct는 `@Builder`가 붙은 클래스에 대해 자동으로 `.builder().field(...).build()`를 사용해 객체를 생성합니다. ### 주의할 점: - `@Builder(disableBuilder = true)`이면 Builder 무시 - `@Builder(disableBuilder = false)` 또는 생략하면 Builder 사용 - 커스텀 Builder 클래스의 경우 구조가 달라지면 빌더 매핑이 안 될 수도 있으니 주의 --- ## 마무리 정리 | 기능 | 설명 | |------|------| | **Enum 매핑** | Enum 이름 자동 매핑, 다를 경우 `@ValueMapping` 사용 | | **커스텀 메서드** | 복잡한 변환 로직을 메서드로 정의 가능 | | **Builder 패턴** | `@Builder` 객체도 매핑 가능 (자동 빌더 생성) | ---