보일러플레이트 코딩을 그만! MapStruct로 Java Bean 매핑 간소화

발행: (2026년 3월 14일 오후 01:34 GMT+9)
8 분 소요
원문: Dev.to

Source: Dev.to

Cover image for Stop Writing Boilerplate! Simplify Java Bean Mapping with MapStruct

Manoj Mishra

수동으로 DTO, Entities, 그리고 Java beans 간의 객체 매핑을 작성하는 것은 Java 개발에서 가장 반복적이고 오류가 발생하기 쉬운 작업 중 하나입니다. 개발자들은 종종 getter, setter 및 수동 변환을 사용해 수백 줄의 보일러플레이트 코드를 작성하게 되며, 이는 코드를 유지보수하기 어렵게 만들고 깨지기 쉬워집니다.

다행히도, 더 스마트한 방법이 있습니다.

MapStruct는 컴파일 시점에 타입‑안전한 빈‑매핑 코드를 자동으로 생성하는 강력한 Java 애노테이션 프로세서입니다. 번거로운 매핑 로직을 직접 작성하는 대신, MapStruct가 깔끔하고 효율적인 매퍼 구현을 만들어 줍니다.

리플렉션‑기반 라이브러리와 달리, MapStruct는 순수 Java 코드를 생성하므로 성능 향상, 컴파일‑시점 안전성, 그리고 디버깅 용이성을 제공합니다.

이 가이드에서는 MapStruct가 Java 객체 매핑을 어떻게 단순화하는지, 보일러플레이트 코드를 제거하고 더 깔끔하고 유지보수가 쉬운 애플리케이션을 만드는 데 어떻게 도움이 되는지 배울 수 있습니다.

MapStruct는 정확히 무엇인가?

MapStruct는 리플렉션에 의존하는 런타임 매핑 라이브러리가 아닙니다. Java 컴파일러에 플러그인되는 코드 생성기입니다.

  1. 인터페이스를 정의하고 MapStruct 어노테이션으로 표시합니다.
  2. 컴파일 시, MapStruct는 해당 인터페이스의 구현체를 모든 필요한 매핑 로직과 함께 생성합니다.

주요 장점

  • 타입 안전성 – 매핑 오류가 컴파일 시점에 감지됩니다.
  • 고성능 – 생성된 코드는 리플렉션 없이 순수 Java입니다.
  • 디버깅 용이 – 생성된 코드는 읽기 쉽고 디버깅 가능합니다.

왜 수동 매핑이나 다른 라이브러리보다 MapStruct를 선택해야 할까요?

  • 보일러플레이트 감소
  • 컴파일 타임 안전성
  • 높은 성능
  • 명확한 선언적 매핑
  • Spring 및 Lombok과 같은 프레임워크와의 뛰어난 통합

강력한 기능

MapStruct는 다음을 지원합니다:

  • 다른 필드 이름 매핑
  • 중첩 빈 매핑
  • 컬렉션 매핑
  • 타입 변환
  • 사용자 정의 매핑 메서드
  • 기본값 및 표현식

시작하기: MapStruct 설정

Maven 구성

<properties>
    <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
    <lombok.version>1.18.30</lombok.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

핵심 개념 및 예시

1. 기본 매핑

Domain 클래스

public class User {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
}

DTO

public class UserDto {
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
}

Mapper

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDto userToUserDto(User user);
    User userDtoToUser(UserDto userDto);
}

사용 예시

User user = new User();
UserDto dto = UserMapper.INSTANCE.userToUserDto(user);

2. 서로 다른 필드 이름 처리

@Mapper
public interface SourceTargetMapper {
    SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);

    @Mapping(source = "ageYears", target = "age")
    TargetBean sourceToTarget(SourceBean source);

    @Mapping(source = "age", target = "ageYears")
    SourceBean targetToSource(TargetBean target);
}

3. 중첩 객체 매핑

@Mapper(uses = {AddressMapper.class})
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    UserDto userToUserDto(User user);
}

4. 컬렉션 매핑

@Mapper
public interface UserMapper {
    List<UserDto> usersToUserDtos(List<User> users);
}

5. @Named을 활용한 커스텀 로직

@Mapper
public abstract class OrderMapper {

    @Named("formatDate")
    String formatDate(LocalDate date) {
        return date.format(DateTimeFormatter.ISO_LOCAL_DATE);
    }

    @Mapping(source = "orderDate", target = "orderDateString", qualifiedByName = "formatDate")
    public abstract OrderDto orderToDto(Order order);
}

결론

MapStruct는 수동 bean 매핑과 관련된 지루하고 오류가 발생하기 쉬운 보일러플레이트를 제거하면서 컴파일 타임 안전성과 높은 성능을 제공합니다. 빌드 프로세스에 통합하면 반복적인 변환 코드를 작성하는 대신 비즈니스 로직에 집중할 수 있습니다. 즐거운 매핑 되세요!

6. 기본값 및 표현식

@Mapper
public interface ProductMapper {

    @Mapping(target = "status", defaultValue = "AVAILABLE")
    @Mapping(
        target = "description",
        expression = "java(product.getName().toUpperCase() + \" - AWESOME!\")"
    )
    ProductDto productToProductDto(Product product);
}

모범 사례

  • 매퍼를 집중시킵니다 – 각 매퍼는 하나의 논리적 관심사만 처리해야 합니다.
  • uses를 사용해 매퍼를 조합합니다 – 복잡한 매핑은 다른 매퍼에 위임합니다.
  • 명시적인 @Mapping을 선호합니다 – 매핑 의도를 명확히 하고 예기치 않은 동작을 방지합니다.
  • Lombok과 결합합니다 – Lombok은 getter/setter를 생성하고, MapStruct는 매핑을 담당합니다.
  • 맞춤 로직을 단위 테스트합니다 – 특히 @Named 메서드나 표현식은 반드시 테스트합니다.

Spring 프로젝트의 경우

@Mapper(componentModel = "spring")

결론

MapStruct는 컴파일 타임에 타입‑안전하고 고성능 코드를 생성함으로써 객체 매핑을 단순화합니다.

장점

  • 보일러플레이트 감소
  • 컴파일‑타임 안전성
  • 유지보수성 향상
  • 개발자 생산성 향상

MapStruct를 아직 사용해 보지 않았다면, 한 번 시도해 보세요!

핵심 요약

  • MapStruct는 매핑 코드를 컴파일 시점에 생성합니다.
  • 수동 보일러플레이트 매핑을 제거합니다.
  • 타입‑안전한 DTO 변환을 제공합니다.
  • Spring BootLombok과 원활하게 작동합니다.

최종 생각

Java 애플리케이션에서 DTO와 엔티티를 자주 매핑한다면, MapStruct는 보일러플레이트 코드를 크게 줄이고 유지보수성 및 성능을 향상시킬 수 있습니다.

프로젝트에서 MapStruct를 사용해 본 적이 있나요? 댓글로 경험을 공유해주세요!

0 조회
Back to Blog

관련 글

더 보기 »

상속에 대한 간단한 코딩 예제

은행 시스템에 대한 간단한 코딩 예제 java package bank.task; public class BankAccount { int accountNumber; double balance; public void deposit(double depositAm...