Mapper가 제대로 작동이 안되어 팀원이 4시간이 넘는 트러블 슈팅을 겪었다
문제 상황
분명 Mapper 문법에 맞게 작성했는데 구현체가 생성되지 않고, 코드가 제대로 실행되지 않음
결론부터 말하자면 Target 에 Setter가 누락되어서 생기는 오류였다.
Mapper가 어떻게 작동하는지 부터 다시 되짚어보며 문제를 해결해봤다.
Mapper, 그럼 어떻게 구현되는가?
@Mapper
public interface TestMapper{
void updateHuman(TestDto testDto, @MappingTarget Test test);
}
이렇게 코드를 작성하면 testDto 의 내용이 Test 객체로 매핑된다!
어떻게 작동되는 것일까?
공식 문서를 보자

Mapper는 구현될 때 target에 setter를 이용하여 매핑을 해준다는 것을 알 수 있다.
즉 직접 setter를 이용해서 필드의 값을 하나씩 매핑하는 과정을 생략해주는 것이라고 볼 수 있다!
코드로 확인해보자
코드로 DTO를 target(엔티티)로 매핑하는 과정을 보면서 Setter가 있는경우와 없는 경우를 비교해 보자
testUse(Target이 될 클래스)
@Getter
public class TestUse {
private String f1;
private String f2;
}
DTO
public record Dto (
String f1,
String f2
){}
Mapper Interface 정의
@Mapper
public interface TestMapper {
TestMapper INSTANCE = Mappers.getMapper(TestMapper.class);
TestUse toTestUse(Dto dto);
}
테스트코드
public class UserMapperTest {
@Test
public void testEntityToDTOWithoutSetters() {
Dto dto = new Dto("f1","f2");
TestUse testUse = TestMapper.INSTANCE.toTestUse(dto);
System.out.println(testUse.getF1());
}
}
dto 를 생성해주고 TestUse와 매핑, 그 결과를 출력해보자
결과
null
예상처럼 null이 반환된다.
Setter를 정의해주지 않아 객체에 값을 넣어주지 못해 null을 반환하는 것.
구현체도 확인해보자
public class TestMapperImpl implements TestMapper {
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
TestUse testUse = new TestUse();
return testUse;
}
}
구현체에 TestUse를 설정하는 코드가 생성되지 않는다!
즉 setF1, setF2를 해 주지 못해 빈 객체를 반환하게 된다.
Setter 대신 Builder로도 확인해볼까?
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
TestUse.TestUseBuilder testUse = TestUse.builder();
testUse.f1( dto.f1() );
testUse.f2( dto.f2() );
return testUse.build();
}
Builder 어노테이션이 있다면 이를 감지하여 자동으로 구현체를 생성해준다.
생성자를 정의해 주어도 이렇게 잘 감지해서 동작하는 것을 알 수 있다.
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
String f1 = null;
String f2 = null;
f1 = dto.f1();
f2 = dto.f2();
TestUse testUse = new TestUse( f1, f2 );
return testUse;
}
마무리
MapStruct 사용하면 편하게 객체사이를 매핑 할 수 있지만 이 또한 자동이라고 생각하지 말자.
MapStruct의 역할은 객체의 Setter, Builder, 생성자를 이용해 매핑을 자동화 해 주는 것이므로 매핑이 제대로 되지 않는것 같다면
클래스에 Setter, Builder가 선언되어있는지 확인해 보면 좋을것 같다.
Mapper가 제대로 작동이 안되어 팀원이 4시간이 넘는 트러블 슈팅을 겪었다
문제 상황
분명 Mapper 문법에 맞게 작성했는데 구현체가 생성되지 않고, 코드가 제대로 실행되지 않음
결론부터 말하자면 Target 에 Setter가 누락되어서 생기는 오류였다.
Mapper가 어떻게 작동하는지 부터 다시 되짚어보며 문제를 해결해봤다.
Mapper, 그럼 어떻게 구현되는가?
@Mapper
public interface TestMapper{
void updateHuman(TestDto testDto, @MappingTarget Test test);
}
이렇게 코드를 작성하면 testDto 의 내용이 Test 객체로 매핑된다!
어떻게 작동되는 것일까?
공식 문서를 보자

Mapper는 구현될 때 target에 setter를 이용하여 매핑을 해준다는 것을 알 수 있다.
즉 직접 setter를 이용해서 필드의 값을 하나씩 매핑하는 과정을 생략해주는 것이라고 볼 수 있다!
코드로 확인해보자
코드로 DTO를 target(엔티티)로 매핑하는 과정을 보면서 Setter가 있는경우와 없는 경우를 비교해 보자
testUse(Target이 될 클래스)
@Getter
public class TestUse {
private String f1;
private String f2;
}
DTO
public record Dto (
String f1,
String f2
){}
Mapper Interface 정의
@Mapper
public interface TestMapper {
TestMapper INSTANCE = Mappers.getMapper(TestMapper.class);
TestUse toTestUse(Dto dto);
}
테스트코드
public class UserMapperTest {
@Test
public void testEntityToDTOWithoutSetters() {
Dto dto = new Dto("f1","f2");
TestUse testUse = TestMapper.INSTANCE.toTestUse(dto);
System.out.println(testUse.getF1());
}
}
dto 를 생성해주고 TestUse와 매핑, 그 결과를 출력해보자
결과
null
예상처럼 null이 반환된다.
Setter를 정의해주지 않아 객체에 값을 넣어주지 못해 null을 반환하는 것.
구현체도 확인해보자
public class TestMapperImpl implements TestMapper {
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
TestUse testUse = new TestUse();
return testUse;
}
}
구현체에 TestUse를 설정하는 코드가 생성되지 않는다!
즉 setF1, setF2를 해 주지 못해 빈 객체를 반환하게 된다.
Setter 대신 Builder로도 확인해볼까?
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
TestUse.TestUseBuilder testUse = TestUse.builder();
testUse.f1( dto.f1() );
testUse.f2( dto.f2() );
return testUse.build();
}
Builder 어노테이션이 있다면 이를 감지하여 자동으로 구현체를 생성해준다.
생성자를 정의해 주어도 이렇게 잘 감지해서 동작하는 것을 알 수 있다.
@Override
public TestUse toTestUse(Dto dto) {
if ( dto == null ) {
return null;
}
String f1 = null;
String f2 = null;
f1 = dto.f1();
f2 = dto.f2();
TestUse testUse = new TestUse( f1, f2 );
return testUse;
}
마무리
MapStruct 사용하면 편하게 객체사이를 매핑 할 수 있지만 이 또한 자동이라고 생각하지 말자.
MapStruct의 역할은 객체의 Setter, Builder, 생성자를 이용해 매핑을 자동화 해 주는 것이므로 매핑이 제대로 되지 않는것 같다면
클래스에 Setter, Builder가 선언되어있는지 확인해 보면 좋을것 같다.