TIL

[240113] MapStruct unmapped 오류 수정

진진리 2024. 1. 13. 14:37
728x90
이번 프로젝트에서 객체와 dto간의 변환을 위해 MapStruct를 사용하였다.
생성자를 이용하여 변환해주는 방법이 있지만 새로운 라이브러리를 활용하고 익숙해지는 연습을 하고, 코드를 더 깔끔하게 작성하여 가독성을 높이기 위해 적용해보았다.

그러던 중 게시글 관련 기능을 개발하던 중에 오류 메세지를 마주하게 되었다.

 

Unmapped 메세지

에러 메세지: nickname 필드를 매핑하지 못하고 있다.

Unmapped target property: "nickname". Mapping from property "Board reportBoard.board" to "BoardGetRes reportBoardGetRes.board".
Unmapped target property: "nickname". Mapping from Collection element "Comment reportBoard.board.comments" to "CommentGetRes reportBoardGetRes.board.comments".

 

에러가 발생한 mapper 코드는 다음과 같다.

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

    @Mapping(source = "board.id", target = "reportedBoardId")
    @Mapping(source = "user.id", target = "reporterUserId")
    ReportBoardRes toReportBoardRes(ReportBoard reportBoard);

    List<ReportBoardGetRes> toReportBoardListGetRes(List<ReportBoard> reportBoards);
}

 

Mapper는 변환 과정에서 필드의 이름이 같으면 자동으로 값을 대입해준다.

하지만 필드명이 다르다면 @Mapping을 사용해서 변환해줄 값을 지정하여 매핑시켜줄 수 있다.

이때  BoardGetRes의 nickname과 

 

에러 발생 이유

List<ReportBoard>를 List<ReportBoardGetRes>로 변환하는 과정에서 오류가 왜 발생했는지 알아보기 위해서 두 객체 코드를 보자.

 

  • ReportBoard
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_boardreport")
public class ReportBoard extends TimeStamp {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column private String reason;

    @ManyToOne
    @JoinColumn(name = "boardId", nullable = false)
    private Board board;

    @ManyToOne
    @JoinColumn(name = "userId", nullable = false)
    private User user;

    @Builder
    private ReportBoard(Board board, User user, String reason) {
        this.board = board;
        this.user = user;
        this.reason = reason;
    }
}

 

필드로 Board, User, reason을 가지고 있다.

 

  • ReportBoardGetRes
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReportBoardGetRes {
    private Long id;
    private String reason;
    private BoardGetRes board;

    @Builder
    private ReportBoardGetRes(Long id, String reason, BoardGetRes board) {
        this.id = id;
        this.reason = reason;
        this.board = board;
    }
}

 

필드로 id, reason, BoardGetRes를 가지고 있다.

 

첫 번째 오류에서는 Board를 BoardGetRes로 변환하는 과정에서 nickname이라는 필드를 매핑하지 못하고 있다.

따라서 다음 코드를 추가해줘야 한다.

@Mapping(source = "user.nickname", target = "nickname") // toReportBoardListGetRes 에서 필요
BoardGetRes toBoardGetRes(Board board);

 

사실 위의 코드는 BoardServiceMapper라는 곳에 존재한다.

그래서 처음에는 mapper가 자동으로 그곳에 있는 코드를 사용해여 변환 작업을 할 것이라고 생각했다.

하지만 다른 곳의 mapper를 가져오겠다는 추가적인 코드가 없는 상태에서는 한 mapper 내에서만 변환작업을 하기 때문에 이 곳에 추가해주어야 한다.

이를 통해서 한 메서드에서 매핑 작업을 할 때 같은 mapper에 존재하는 다른 매서드를 이용한다는 것을 알게 되었다.

 

두 번째 오류도 같은 맥락에서 발생했다.

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BoardGetRes {
    private String nickname;
    private String title;
    private String content;
    private String fileURL;
    private String departure;
    private String destination;
    private List<String> stopover;
    private RegionEnum region;

    private List<CommentGetRes> comments;
    
}

 

BoardGetRes에는 List<CommentGetRes> comments 필드가 존재한다.

따라서 Board 객체에 있는 Comment를 CommentGetRes 로 변환할 수 있도록 코드를 추가한다.

@Mapping(source = "user.nickname", target = "nickname") // toBoardGetRes 에서 필요
CommentGetRes toCommentGetRes(Comment comment);

 

 

즉 ReportBoardServiceMapper의 코드를 다음과 같이 수정해주면 오류를 해결할 수 있다.

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

    @Mapping(source = "board.id", target = "reportedBoardId")
    @Mapping(source = "user.id", target = "reporterUserId")
    ReportBoardRes toReportBoardRes(ReportBoard reportBoard);

    List<ReportBoardGetRes> toReportBoardListGetRes(List<ReportBoard> reportBoards);

    @Mapping(source = "user.nickname", target = "nickname") // toReportBoardListGetRes 에서 필요
    BoardGetRes toBoardGetRes(Board board);

    @Mapping(source = "user.nickname", target = "nickname") // toBoardGetRes 에서 필요
    CommentGetRes toCommentGetRes(Comment comment);
}