매퍼(Mapper)

Backend kwon·2023년 8월 17일
0

매퍼(Mapper)를 이용한 DTO클래스 <-> 엔티티 클래스 매핑

Mapper 클래스구현

@Component  
public class MemberMapper {
		//MemberPostDto를 Member로 변환
    public Member memberPostDtoToMember(MemberPostDto memberPostDto) {
        return new Member(0L,
                memberPostDto.getEmail(), 
                memberPostDto.getName(), 
                memberPostDto.getPhone());
    }

		//MemberPatchDto를 Member로 변환
    public Member memberPatchDtoToMember(MemberPatchDto memberPatchDto) {
        return new Member(memberPatchDto.getMemberId(),
                null, 
                memberPatchDto.getName(), 
                memberPatchDto.getPhone());
    }

    //Member를 MemberResponseDto로 변환
    public MemberResponseDto memberToMemberResponseDto(Member member) {
        return new MemberResponseDto(member.getMemberId(),
                member.getEmail(), 
                member.getName(), 
                member.getPhone());
    }
}

MemberMapper를 Spring의 Bean으로 등록하기 위해서 @Component 애너테이션을 추가했습니다. 등록된 Bean은 MemberController에서 사용됩니다.

 

Controller의 핸들러 메서드에 매퍼(Mapper) 클래스 적용

public class MemberController {
    private final MemberService memberService;
    private final MemberMapper mapper;

		//MemberMapper DI
    public MemberController(MemberService memberService, MemberMapper mapper) {
        this.memberService = memberService;
        this.mapper = mapper;
    }

    @PostMapping
    public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
				//매퍼를 이용해서 MemberPostDto를 Member로 변환
        Member member = mapper.memberPostDtoToMember(memberDto);

        Member response = memberService.createMember(member);

				//매퍼를 이용해서 Member를 MemberResponseDto로 변환
        return new ResponseEntity<>(mapper.memberToMemberResponseDto(response), 
                HttpStatus.CREATED);
    }
                                .
                                .
                                .

Spring Bean에 등록된 MemberMapper 객체를 MemberController에서 사용하기 위해 DI로 주입받고 있습니다.
List의 경우는 안의 Member 객체들을 하나씩 꺼내어서 MemberResponseDto 객체로 변환해주어야 하는데, 이 작업은 Java의 Stream이 해줍니다.

 

MapStruct를 이용한 Mapper 자동 생성

매퍼(Mapper) 클래스를 사용하면 DTO 클래스와 엔티티(Entity) 클래스의 변환 작업을 깔끔하게 처리할 수 있습니다.

어떤 도메인 업무 기능이 늘어날 때마다 개발자가 일일이 수작업으로 매퍼(Mapper) 클래스를 만드는 것은 비효율적입니다.

MapStruct가 매퍼 클래스를 자동으로 구현해 줌으로써 개발자의 생산성을 향상해줄 수 있습니다.

MapStruct는 DTO 클래스처럼 Java Bean 규약을 지키는 객체들 간의 변환 기능을 제공하는 매퍼(Mapper) 구현 클래스를 자동으로 생성해 주는 코드 자동 생성기입니다

1. MapStruct 의존 라이브러리 설정

dependencies {
	...
	...
	implementation 'org.mapstruct:mapstruct:1.4.2.Final'
	annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

2. MapStruct 기반의 매퍼(Mapper) 인터페이스 정의

@Mapper(componentModel = "spring")  // (1)
public interface MemberMapper {
    Member memberPostDtoToMember(MemberPostDto memberPostDto);
    Member memberPatchDtoToMember(MemberPatchDto memberPatchDto);
    MemberResponseDto memberToMemberResponseDto(Member member);
}

@Mapper 애너테이션을 추가함으로써 해당 인터페이스는 MapStruct의 매퍼 인터페이스로 정의가 되는 것입니다.

@Mapper 애너테이션의 애트리뷰트로 componentModel = "spring"을 지정해 주면 Spring의 Bean으로 등록이 됩니다.

MapStruct가 자동으로 생성해 준 MemberMapper 인터페이스의 구현 클래스는 Gradle의 build task를 실행하면 자동으로 생성됩니다.

 

DTO 클래스와 엔티티 클래스의 역할 분리가 필요한 이유

  • 계층별 관심사의 분리
    DTO 클래스는 API 계층에서 요청 데이터를 전달받고, 응답 데이터를 전송하는 것이 주 목적인 반면에 Entity 클래스는 서비스 계층에서 데이터 액세스 계층과 연동하여 비즈니스 로직의 결과로 생성된 데이터를 다루는 것이 주목적입니다.

  • 코드 구성의 단순화
    DTO 클래스에서 사용하는 유효성 검사 애너테이션이 Entity 클래스에서 사용이 된다면 JPA에서 사용하는 애너테이션과 뒤섞인 상태가 되어 유지보수하기 상당히 어려운 코드가 됩니다.

  • REST API 스펙의 독립성 확보
    데이터 액세스 계층에서 전달받은 데이터로 채워진 Entity 클래스를 클라이언트의 응답으로 그대로 전달하게 되면 원치 않는 데이터까지 클라이언트에게 전송될 수 있습니다.
    DTO 클래스를 사용하면 회원의 로그인 패스워드 같은 정보를 클라이언트에게 노출하지 않고, 원하는 정보만 제공할 수 있습니다.

profile
백엔드개발자를 향해서

0개의 댓글