-
Notifications
You must be signed in to change notification settings - Fork 0
DTO Mapper
English ID edited this page Dec 19, 2024
·
8 revisions
다음 이유로 MapStruct를 채택했습니다.
- 다른 기술에 비해 초기에 약간의 설명을 듣거나 조금은 파악해야 쉽게 사용할 수 있지만, 초기 진입이 충분히 쉽습니다.
(적어도 초기에 설명을 듣고도 진입을 어려워하는 사례를 거의 볼 수 없었습니다.) - 복잡한 객체의 매핑도 명시적으로 지정할 수 있기 때문에 혼란이 적고 매우 편리합니다. (주로 ModelMapper와 비교할 때 장점)
- 컴파일타임에 준비되기 때문에 성능이 좋습니다. (단, 매핑 성능이 전체 성능에 큰 영향을 주는 케이스는 드뭅니다.)
다음은 대안 기술과 비교한 표입니다.
구분 | dto.toEntity() 함수 | ModelMapper | ✅ MapStruct |
---|---|---|---|
설명 | DTO의 메서드를 사용하여 엔티티로 변환 | 리플렉션을 사용하여 DTO를 엔티티로 매핑 | 컴파일 타임에 코드 생성을 통해 매핑 수행 |
성능 | ✅ 높음 (직접 코드 작성) | 중간 (리플렉션 때문에 다소 느림) | ✅ 매우 높음 (컴파일 최적화된 코드) |
진입의 용이성 | ✅ 매우 높음 (직접 제어 가능) | ✅ 높음 (단순 설정으로 사용 가능) | 중간 (종종 추가적인 설정 필요, 애노테이션 사용) |
유지보수성 | 중간 (수동 작업이 많지만 명확함) | 낮음 (자동 매핑은 추적과 수정이 어려움) | ✅ 높음 (명시적 매핑으로 변경이 명확하게 관리됨) |
설정의 복잡성 | ✅ 없음 (수동 코드 작성) | 중간 (설정 파일이나 API 필요) | ✅ 낮음 (애노테이션 기반, 설정 최소화) |
대규모 프로젝트 적합성 | 낮음 (수동 작업이 많아짐) | 높음 (자동화로 인한 효율 증가) | ✅ 매우 높음 (코드 자동 생성으로 일관성 및 효율 유지) |
추가적으로 다음과 같은 특징이 있습니다.
- Java 16 record에 대한 매핑도 잘 지원합니다.
- 매우 스마트한 자동 매핑을 지원합니다. (생성자, public 필드, setter, 빌더, new Builder, 문자열과 각종 타입 간 매핑 등)
- 스프링 프레임워크에 종속적인 라이브러리가 아닙니다.
- 여러 커스텀 매핑 전략 지원 (다소 학습이 필요합니다. 한국어 번역 자료는 매우 부족합니다.)
build.gradle.kts
파일에 다음을 추가합니다. (버전은 예시입니다.)
dependencies {
// mapstruct
implementation("org.mapstruct:mapstruct:1.6.3")
annotationProcessor("org.mapstruct:mapstruct-processor:1.6.3")
annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0")
}
선택 사항: default component model
맵 스트럭트 사용 시 component model을 "spring"으로 명시하는 작업을 생략하기 위해, 컴파일 명령에 다음 옵션을 추가할 수 있습니다.
-Amapstruct.defaultComponentModel=spring
예시(build.gradle.kts):
tasks.withType<JavaCompile> { options.compilerArgs.addAll(listOf( "--enable-preview", "-Amapstruct.defaultComponentModel=spring", // << added )) }
import org.mapstruct.Mapper;
import org.springframework.data.domain.Page;
import java.time.Instant;
// SPRING = "spring"
import static org.mapstruct.MappingConstants.ComponentModel.SPRING;
@Mapper(componentModel = SPRING) // "spring"
public interface BoardDtoMapper {
Board toEntity(BoardCreateRequest dto, BoardStatus status, Instant createdAt, Instant updatedAt);
}
이로써 빈 등록이 완료됩니다.
- 빈 스캔 범위에 있거나, 별도 빈 등록 수단을 사용해야 합니다. 대부분의 간략한 아키텍처에서 빈 스캔 범위에 잘 포함되어 있습니다.
Board
예시
// simplified example
public class Board extends BaseEntity {
private String title;
private String content;
private BoardStatus status;
private Instant createdAt;
private Instant updatedAt;
}
BoardCreateRequest
예시
// simplified example
public record BoardCreateRequest(String title, String content) {/* can be empty here */}
등록된 매퍼 빈을 주입받아 사용합니다.
// simplified example
@Component // @Controller, @Service 등 다른 애노테이션을 사용할 수도 있습니다.
public class ExampleShower {
private final BoardDtoMapper mapper;
public ExampleShower(BoardDtoMapper boardDtoMapper) {
this.mapper = boardDtoMapper;
}
public void doExample(BoardCreateRequest dto) {
Instant now = Instant.now();
Board entity = mapper.toEntity(
dto,
BoardStatus.ACTIVE,
now,
now
);
// handle the entity now
// ... (skipped)
}
}
- Home
- Code Styles
- Controller
- Service
- Interface: Use Cases
- Command Service
- Query Service
- Repository (Spring Data JPA Only)
- CQRS
- Command Repository
- Query Repository
- Domain
- Enum Status
- Base Entity
- UUID Base Entity
- JPA Entity
- Error Code & Exceptional Response
- Interface Error Code
- Enum Error Code (impl. Error Code)
- Custom Exception
- Extended Custom Exception
- 상수 관리
- Static Final 기호상수
- Enum을 통한 상수 관리
- 프로젝트 보조 도구 활용
- 작업 환경 및 운영 환경
- Docker Compose (작업 환경)
- Flyway (작업 환경, 운영 환경의 일치 필요)
- 작업 환경 및 운영 환경