Back-End/Spring

[Spring] MapStruct์˜ ์‚ฌ์šฉ๋ฒ• ๋ฐ ModelMapper์™€์˜ ๋น„๊ต

Splin 2023. 4. 18. 11:43
๐Ÿ’ก ์ด ๊ธ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
    1. MapStruct์™€ ModelMapper ๋น„๊ต
    2. MapStruct์˜ ์˜์กด์„ฑ ์„ค์ • ๋ฐ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

Controller, Service, Repository ๋“ฑ ๋ ˆ์ด์–ด ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋ฅผ ํƒ€์ž…์ด ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ํ˜•(Type) ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ํ•ฉ์น˜๋Š” ์ผ์€ ๋งค์šฐ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ž‘์—…์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ชจ๋‘ ์ง์ ‘ ํ•˜๊ฒŒ๋˜๋ฉด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ ์„ ๋ช‡ ๊ฐ€์ง€ ๋‚˜์—ดํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์žฌ๋ฏธ๊ฐ€ ์—†๊ณ  ๋ฐ˜๋ณต์ ์ด๊ณ  ์ฝ”๋“œ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • ํ•„๋“œ๊ฐ€ ์ถ”๊ฐ€๋‚˜ ์ˆ˜์ •, ์‚ญ์ œ๊ฐ€ ์ผ์–ด๋‚  ๊ฒฝ์šฐ ๋ณ€ํ™˜ํ•˜๋Š” ๋กœ์ง์— ๋Œ€ํ•ด์„œ ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์„ž์ด๊ฒŒ ๋˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ตญ ์ƒ์‚ฐ์„ฑ์„ ๋–จ์–ด๋œจ๋ฆฝ๋‹ˆ๋‹ค.

 

UserEntity userDTOToEntity(UserDTO userDTO) {
        return new userEntity(userDTO.getId(), 
                             userDTO.getPassword(),
                             userDTO.getName());
}

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ž‘์„ฑํ•œ DTO๋ฅผ Entity๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ์ธ๋ฐ, ์—ฌ๊ธฐ์„œ๋Š” ๋น„๊ต์  ๋‹จ์ˆœํ•œ ๊ฐ์ฒด๋ผ ์ฝ”๋“œ๊ฐ€ ์งง์ง€๋งŒ ํ•„๋“œ๊ฐ€ ๋ช‡ ๊ฐœ๋งŒ ๋” ๋Š˜์–ด๋‚˜๋„ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ”ผ๊ณคํ•œ ์ž‘์—…์ด ๋ฉ๋‹ˆ๋‹ค.

Object Mapping ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ์•„์ฃผ ์ข‹์€ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. Object Mapping์„ ์œ„ํ•œ ModelMapper์™€ MapStruct์— ๋Œ€ํ•ด์„œ ์˜์กด์„ฑ ์„ค์ •, Mapping ์‚ฌ์šฉ๋ฒ•๊ณผ ์ฐจ์ด์ ์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

MapStruct vs ModelMapper

๋จผ์ € ๋น„๊ตํ•˜๊ธฐ ์ „์— ๋‹ค๋ฅธ ๋ธ”๋กœ๊ทธ์—์„œ 50๋งŒ ๋ฒˆ ์‹คํ–‰ํ•œ ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์†๋„ ์ฐจ์ด๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด MapStruct๊ฐ€ ์†๋„๊ฐ€ ์›”๋“ฑํžˆ ๋น ๋ฅธ ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ์žฅ์ ๋“ค์ด ๋งŽ์Šต๋‹ˆ๋‹ค.

  • MapStruct๋Š” ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์ฝ์–ด ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌํ”Œ๋ ‰์…˜์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    • ModelMapper๋Š” Mapping์ด ์ผ์–ด๋‚  ๋•Œ ๋ฆฌํ”Œ๋ ‰์…˜์ด ๋ฐœ์ƒ
  • MapStruct์˜ ์ฒ˜๋ฆฌ์†๋„๊ฐ€ 10-5 m/s๋กœ ์••๋„์ ์œผ๋กœ ๋น ๋ฆ…๋‹ˆ๋‹ค.
    • ModelMapper๋Š” 0.002m/s
  • MapStruct๋Š” ์ปดํŒŒ์ผ ์‹œ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • MapStruct๋Š” ๋””๋ฒ„๊น…์ด ์‰ฝ์Šต๋‹ˆ๋‹ค.
  • MapStruct๋Š” ์ƒ์„ฑ๋œ ๋งคํ•‘ ์ฝ”๋“œ๋ฅผ ๋ˆˆ์œผ๋กœ ์ง์ ‘ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ฆฌํ”Œ๋ ‰์…˜ ์ด๋ž€ ?
๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค์˜ ์ •๋ณด๋ฅผ ๋ถ„์„ํ•ด ๋‚ด๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•. ๊ตฌ์ฒด์ ์€ ํด๋ž˜์Šค ํƒ€์ž…์„ ์•Œ์ง€ ๋ชปํ•ด๋„ ์ปดํŒŒ์ผ๋œ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์—ญ์œผ๋กœ ํด๋ž˜์Šค์˜ ์ •๋ณด๋ฅผ ์•Œ์•„๋‚ด์–ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ๋ชจ๋ฅด๋Š” ์ƒํƒœ์—์„œ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰ ๋™์  ๋ฐ”์ธ๋”ฉ์ด ๋˜์ง€ ์•Š๋˜ ์ž๋ฐ”์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์ด๋ผ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ๋™์  ๋ฐ”์ธ๋”ฉ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ MapStruct๋Š” ๋งŽ์€ ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— MapStruct ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

๋˜, ๋ฐ‘์˜ ๊ตฌ๊ธ€ ํŠธ๋ Œ๋“œ๋กœ ๋ดค์„ ๋•Œ๋„ MapStruct๊ฐ€ Java Mapping ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

์˜์กด์„ฑ ์„ค์ •

MapStruct์˜ Gradle ์˜์กด์„ฑ ์„ค์ •์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

 

 

Entity/Dto ์ƒ์„ฑ

์˜ˆ์ œ์— ์‚ฌ์šฉํ•  Entity์™€ Dto ๊ฐ์ฒด๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

UserEntity

@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserEntity {
    private Long id;
    private String password;
    private String name;
    private String nickName;
    private Date createDate;
    
    @Builder
    private UserEntity(Long id, String password, String name,
    		String nickName, Date createDate) {
    	this.id = id;
    	this.password = password;
    	this.name = name;
    	this.nickName = nickName;
    	this.createDate = createDate;
    }
}

 

UserDto

@Getter
@Setter
@ToString
public class UserDTO {
    private Long id;
    private String password;
    private String name;
   
}

 

 

Mapper ์ƒ์„ฑ ๋ฐ ํ…Œ์ŠคํŠธ

@Mapper
public interface UserMapper {
	UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
	
	UserEntity userDTOToEntity(UserDTO userDTO);
	
	UserDTO userEntityToDTO(UserEntity userEntity);
}
  • ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— @Mapper๋ฅผ ๋ถ™์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค. @Mapper๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด MapStruct๊ฐ€ ์ž๋™์œผ๋กœ UserMapper๋ฅผ ์ƒ์†๋ฐ›์•„์„œ UserMapperImpl๋ฅผ ๊ตฌํ˜„ํ•ด์ค๋‹ˆ๋‹ค. (์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„๋œ ๋ถ€๋ถ„์€ target -> generated-sources->...->Mapper๊ฐ€ ์žˆ๋Š” ํด๋”์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)
  • MapStruct์—์„œ๋Š” UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ Mapper Bean์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
    • @Mapper(componentModel = "spring")๊ณผ @Autowired๋ฅผ ์ด์šฉํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-06-03T16:00:52+0900",
    comments = "version: 1.4.2.Final, compiler: Eclipse JDT (IDE) 1.3.1200.v20200916-0645, environment: Java 15.0.2 (Oracle Corporation)"
)
public class UserMapperImpl implements UserMapper {

    @Override
    public UserEntity userDTOToEntity(UserDTO userDTO) {
        if ( userDTO == null ) {
            return null;
        }

        UserEntityBuilder userEntity = UserEntity.builder();

        userEntity.id( userDTO.getId() );
        userEntity.name( userDTO.getName() );
        userEntity.password( userDTO.getPassword() );

        return userEntity.build();
    }

    @Override
    public UserDTO userEntityToDTO(UserEntity userEntity) {
        if ( userEntity == null ) {
            return null;
        }

        UserDTO userDTO = new UserDTO();

        userDTO.setId( userEntity.getId() );
        userDTO.setName( userEntity.getName() );
        userDTO.setPassword( userEntity.getPassword() );

        return userDTO;
    }
}

๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์˜ค๋Š” ๊ฐ์ฒด๋Š” Getter, ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ์ฒด๋Š” Setter๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, Target ๊ฐ์ฒด(๋ฉ”์„œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ์ฒด)์— @Builder ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ ค์žˆ๋‹ค๋ฉด Builder ๋ฉ”์†Œ๋“œ๋ฅผ ์šฐ์„  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. (userDTOToEntity๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด Builder๋ฅผ ์ด์šฉํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

๋˜, @Mapping ์–ด๋…ธํ…Œ์ด์…˜๊ณผ ์†์„ฑ์„ ์ด์šฉํ•ด ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ๋ฅผ ์„ค์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์ด์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜์—์„œ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.)

 

 

Test

@Test
	public void MapStructTest() throws Exception {
		
		UserEntity userEntity = UserEntity.builder()
            	.id(1L)
				.password("testPassword")
				.name("testName")
				.nickName("testNickName")
				.createDate(new Date())
				.build();
		
		// Entity โžก๏ธ DTO
		UserDTO resultDTO = UserMapper.INSTANCE.userEntityToDTO(userEntity);
		// DTO โžก๏ธ Entity, Setter๊ฐ€ ์—†์–ด๋„ @Builder๊ฐ€ ๋ถ™์–ด์žˆ๋‹ค๋ฉด ๋ณ€ํ™˜ ๊ฐ€๋Šฅ
		UserEntity resultEntity = UserMapper.INSTANCE.userDTOToEntity(resultDTO);
		
		System.out.println(resultDTO);
		System.out.println(resultEntity);
	}

INSTANCE๋ฅผ ์ด์šฉํ•ด ๋ณ€ํ™˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ ธ์™€ ๊ฐ์ฒด๋ฅผ Mappingํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

@Mapping์„ ์ด์šฉํ•œ ๋‹ค์–‘ํ•œ Mapping

MapStruct๋Š” @Mapping ์–ด๋…ธํ…Œ์ด์…˜์˜ ์†์„ฑ์„ ์ด์šฉํ•ด ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ์˜ Mapping์„ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์„œ๋กœ ๋‹ค๋ฅธ ์†์„ฑ ๋งคํ•‘

์œ„์˜ UserDTO์—์„œ ๋‹‰๋„ค์ž„์„ nick์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ  UserEntity์˜ nickName์„ UserDTO์˜ nick์— Mappingํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ source, target ์†์„ฑ์„ ์ด์šฉํ•ด ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Getter
@Setter
@ToString
public class UserDTO {
    private Long id;
    private String password;
    private String name;
    private String nick;
}

์•„๋ž˜์™€ ๊ฐ™์ด Mapping ๋ฉ”์„œ๋“œ์— @Mapping(source = "ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜์ด๋ฆ„", target = "๋ฆฌํ„ด ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜์ด๋ฆ„")์™€ ๊ฐ™์ด ์„ค์ •ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@Mapper
public interface UserMapper {
	UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    
    @Mapping(source = "nick", target = "nickName")
    UserEntity userDTOToEntity(UserDTO userDTO);
	
    @Mapping(source = "nickName", target = "nick")
    UserDTO userEntityToDTO(UserEntity userEntity);
}

 

ํŠน์ • ์†์„ฑ ์ž„์˜๋กœ ์ถ”๊ฐ€

UserDTO์— message ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ์ž„์˜๋กœ ๊ฐ’์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Getter
@Setter
@ToString
public class UserDTO {
    private Long id;
    private String password;
    private String name;
    private String nick;
    private String message;
}

์•„๋ž˜์™€ ๊ฐ™์ด ํŒŒ๋ผ๋ฏธํ„ฐ์— message๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋•Œ ๊ธฐ์กด์˜ @Mapping(source = "nickName", target = "nick")์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ผ์„œ source(ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ์ฒด)์˜ nickName์ด ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ์ฒด์˜ ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— @Mapping(source = "userEntity.nickName", target = "nick") ๊ฐ™์ด ๊ฐ์ฒด๋ช…์„ ๋ช…์‹œํ•ด์ค๋‹ˆ๋‹ค.

expression = "java(message + \".msg\")"์ฒ˜๋Ÿผ expression์†์„ฑ์„ ์‚ฌ์šฉํ•ด message๋’ค์— ์ž๋™์œผ๋กœ .msg๋ฅผ ๋ถ™์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.  java()์•ˆ์—๋Š” ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด๊ฐˆ์ˆ˜๋„ ์žˆ๊ณ , ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Mapper
public interface UserMapper {
	UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
	
    @Mapping(source = "nick", target = "nickName")
    UserEntity userDTOToEntity(UserDTO userDTO);
	
    @Mapping(source = "userEntity.nickName", target = "nick")
    @Mapping(target = "message", expression = "java(message + \".msg\")")
    UserDTO userEntityToDTO(UserEntity userEntity, String message);
}

 

๊ฐ์ฒด ์†์„ฑ ์ถ”๊ฐ€

Address๋ผ๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , UserDTO์— Address๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๋Š” ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

@Getter
@Setter
@ToString
public class Address {
	private String address;
	private int addressNum;
}
@Getter
@Setter
@ToString
public class UserDTO {
    private Long id;
    private String password;
    private String name;
    private String nick;
    private String message;
    private Address add;
}

์•„๋ž˜์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ๊ฐœ์˜ @Mapping์„ ์ด์šฉํ•ด source, target์„ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

UserDTO์˜ Address์†์„ฑ์˜ ์ด๋ฆ„์€ add์ด๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜จ Address ๊ฐ์ฒด์˜ ์ด๋ฆ„์€ address์ด๊ธฐ ๋•Œ๋ฌธ์— source, target์œผ๋กœ ์ง€์ •ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ •์ฑ… ์„ค์ • ๋ฐ ์†์„ฑ ๋ฌด์‹œ

UserEntity์—๋Š” createDate๊ฐ€ ์žˆ๋Š”๋ฐ DTO์—๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์— DTO โžก๏ธ Entity๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ createDate์—๋Š” null ๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๋•Œ, @Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)์„ ์ด์šฉํ•ด ์ •์ฑ…์„ ์„ค์ •ํ•ด์„œ target ๊ฐ์ฒด์— ๋งคํ•‘ ์‹œ ๋งคํ•‘๋˜์ง€ ์•Š์€ ์†์„ฑ์ด ์žˆ๋‹ค๋ฉด ์ปดํŒŒ์ผ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ @Mapping์˜ ignore์†์„ฑ์„ ์ด์šฉํ•ด ๋งคํ•‘๋˜์ง€ ์•Š๋Š” ์†์„ฑ์„ ๋ฌด์‹œํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์ •์ฑ… ์„ค์ •
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface UserMapper {
	UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
	
    // ignore = true๋กœ ๋ฌด์‹œ
    @Mapping(target = "createDate", ignore = true)
    @Mapping(source = "nick", target = "nickName")
    UserEntity userDTOToEntity(UserDTO userDTO);
	
    @Mapping(source = "userEntity.nickName", target = "nick")
    @Mapping(source = "address", target = "add")
    UserDTO userEntityToDTO(UserEntity userEntity, String message, Address address);
}

 

์ •์ฑ…

๋งคํ•‘ ์ •์ฑ…(Policy)๊ณผ ์ „๋žต(Strategy)๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์œ ์šฉํ•œ ๋งคํ•‘ ์ •์ฑ…๊ณผ ์ „๋žต์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.

์ •์ฑ… ๊ฐ’ ์„ค๋ช…
unmappedSourcePolicy IGNORE(default), WARN, ERROR Source์˜ ํ•„๋“œ๊ฐ€ Target์— ๋งคํ•‘๋˜์ง€ ์•Š์„ ๋•Œ ์ •์ฑ…์ž…๋‹ˆ๋‹ค. ์˜ˆ, ERROR๋กœ ์„ค์ •ํ•˜๋ฉด ๋งคํ•‘ ์‹œ Source.aField๊ฐ€ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
unmappedTargetPolicy IGNORE, WARN(default), ERROR Target์˜ ํ•„๋“œ๊ฐ€ ๋งคํ•‘๋˜์ง€ ์•Š์„ ๋•Œ ์ •์ฑ…์ž…๋‹ˆ๋‹ค. ์˜ˆ, ERROR๋กœ ์„ค์ •ํ•˜๋ฉด ๋งคํ•‘ ์‹œ Target.aField์— ๊ฐ’์ด ๋งคํ•‘๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
typeConversionPolicy IGNORE(default), WARN, ERROR ํƒ€์ž… ๋ณ€ํ™˜ ์‹œ ์œ ์‹ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์„ ๋•Œ ์ •์ฑ…์ž…๋‹ˆ๋‹ค. ์˜ˆ, ERROR๋กœ ์„ค์ •ํ•˜๋ฉด long์—์„œ int๋กœ ๊ฐ’์„ ๋„˜๊ธธ ๋•Œ ๊ฐ’์— ์œ ์‹ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์— ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
์ „๋žต ๊ฐ’ ์„ค๋ช…
nullValueMappingStrategy RETURN_NULL(default), RETURN_DEFAULT Source๊ฐ€ null์ผ ๋•Œ ์ •์ฑ…์ž…๋‹ˆ๋‹ค.
nullValuePropertyMappingStrategy SET_TO_NULL(default), SET_TO_DEFAULT, IGNORE Source์˜ ํ•„๋“œ๊ฐ€ null์ผ ๋•Œ ์ •์ฑ…์ž…๋‹ˆ๋‹ค.

 

Mapping ์ฝ”๋“œ ์ง์ ‘ ๊ตฌํ˜„

๊ฐ€๋” MapStruct์—์„œ ๋งคํ•‘ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•˜๊ฑฐ๋‚˜ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ๋  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. MapStruct๋Š” default ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•ด Mapping ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๊ฐ์ฒด๋ฅผ String์œผ๋กœ Mappingํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

@Mapper
public interface JsonMapper {
    ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    default String toString(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

ObjectMapper๋Š” Jackson์—์„œ Java Object์™€ JSON์‚ฌ์ด์˜ ๋ณ€ํ™˜์„ ์‰ฝ๊ฒŒ ํ•ด์ฃผ๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

  • writeValueAsString(obj) : obj๊ฐ์ฒด๋ฅผ Json String์œผ๋กœ ๋ณ€ํ™˜์‹œ์ผœ ์ค๋‹ˆ๋‹ค.
  • readValue(arg, type) : arg์„ type์— ํ•ด๋‹นํ•˜๋Š” ํด๋ž˜์Šค๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Class๊ฐ์ฒด, TypeReference๊ฐ€ ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ex) OBJECT_MAPPER.readValue(arg, ArrayList.class); โžก๏ธ arg์— ํ•ด๋‹นํ•˜๋Š” Json String์„ ArrayList๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

Mapping ์†์„ฑ ์„ค์ • ๊ณตํ†ตํ™”

Mapper๊ฐ€ ๋งŽ์•„์ ธ์„œ Mapper๋งˆ๋‹ค์˜ ์„ค์ • ์ค‘ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•  ๋•Œ ์†์„ฑ ์„ค์ •์„ ๊ณตํ†ตํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. MapStructMapperConfig๋ฅผ ๋งŒ๋“ค์–ด @MapperConfig์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•ด ๊ณตํ†ตํ™”ํ•  ์†์„ฑ์„ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

ํŠน์ • ํƒ€์ž…์ด๋‚˜ ๊ฐ์ฒด ๊ฐ„ Mapping์„ ์Šค์Šค๋กœํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๋‹ค๋ฅธ Mapper๋ฅผ ์ด์šฉํ•ด์•ผ ํ•  ๋•Œ uses์†์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. uses = JsonMapper.class๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด ๊ฐ์ฒด์—์„œ String์œผ๋กœ ๋ณ€ํ™˜์ด ํ•„์š”ํ•  ๋•Œ JsonMapper๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@MapperConfig(unmappedTargetPolicy = ReportingPolicy.ERROR, uses = JsonMapper.class)
public interface MapStructMapperConfig {

}

์•„๋ž˜์™€ ๊ฐ™์ด Mapper๋งˆ๋‹ค ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ฃผ์„์ฒ˜๋Ÿผ JsonMapper์— ์ ์šฉํ•˜๊ฒŒ ๋˜๋ฉด `uses = JsonMapper.class`๋•Œ๋ฌธ์— ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์กฐ์‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@Mapper(config = MapStructMapperConfig.class)
public interface UserMapper {
	...
}

// @Mapper(config = MapStructMapperConfig.class)
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface JsonMapper {
    ...
}

 


์ฐธ๊ณ  ๋งํฌ