2025-04-08T19:56:24
This commit is contained in:
366
docs/25_map struct.md
Normal file
366
docs/25_map struct.md
Normal file
@@ -0,0 +1,366 @@
|
||||
# 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<UserDto> toDtoList(List<User> users);
|
||||
Set<User> toEntitySet(Set<UserDto> 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` 객체도 매핑 가능 (자동 빌더 생성) |
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user