Java Bean Mapping library ์ ์ฉ - MapStruct
2023-05-26
MapStruct๋ก dto ๋งคํ ์ง์ฅ ํ์ถ
์ ์ฉ ๋ฐฐ๊ฒฝ
- ํ๋ก์ ํธ ๊ฐ๋ฐ ์ค ๋ณ์๊ฐ 40๊ฐ ์ด์์ด๋๋ ํ ์ด๋ธ์ ์กฐํํด์ DTO๋ก ๋ณํ ํ ๋ฆฌํดํด์ค์ผํ๋ API๋ฅผ ๋ง๋ฌ๋ค
- getter setter ์ข์ข ํ์ดํํ ์๊ฐ์ ์ ์ ์ด ์๋ํด์ ธ์ ๊ตฌ๊ธ๋ง์ผ๋ก Entity to Dto ๋ฅผ ๊ฒ์ํ๋ค
- ModelMapper ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๊ฒฌ ํ ์ ์ฉํด๋ณด๋ ค๋๋ฐ ๋๋ ๋๋ฌด ์๋ง์์ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋ณด์๊ณ ๊ทธ๊ฒ์ด MapStruct์ด๋ค
MapStruct ์์ํ๊ธฐ
-
์์กด์ฑ ์ถ๊ฐํ๊ธฐ
build.gradlelombok ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์์ฑ ์์๊ฐ ๋งค์ฐ ์ค์ํ๋ค ๐ lombok ์ดํ์ ์ ์
1dependencies { 2 ... 3 implementation 'org.projectlombok:lombok' 4 annotationProcessor 'org.projectlombok:lombok' 5 implementation 'org.mapstruct:mapstruct:1.5.3.Final' 6 annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final' 7 ... 8} -
Mapper interface ์์ฑ
1import org.mapstruct.Mapper; 2import org.mapstruct.Mapping; 3import org.mapstruct.factory.Mappers; 4 5@Mapper //์ด๋ ธํ ์ด์ ์ ์ฉ 6public interface ConditionsMapper { 7 ConditionsMapper INSTANCE = Mappers.getMapper(ConditionsMapper.class); 8 // Mappers.getMapper()๋ก ์ฑ๊ธํค ์์ฑํ์ฌ INSTANCE๋ฅผ ํธ์ถํด์ ์ฌ์ฉ 9 10 /* abstract method ์ ์ 11 * @Mapping : ํด๋น ์ด๋ ธํ ์ด์ ์ ํตํด ๋ณ์ ์ด๋ฆ๋ง์ผ๋ก ์์ฑ๋ getter, setter๋ง์ผ๋ก ๋งคํ์ด ๋ถ๊ฐ๋ฅํ ๊ฒฝ์ฐ๋ฅผ ์ ์ 12 * * source : method์ ํ๋ผ๋ฏธํฐ์ค์์ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋งคํํ ๊ฒ์ธ์ง ๋ช ์ํ๋ฉด ๋จ 13 ์์ ์ฝ๋์์๋ method ํ๋ผ๋ฏธํฐ๊ฐ 1๊ฐ์ด์ง๋ง, 2๊ฐ ์ด์์ ๊ฐ์ฒด๋ฅผ ๋ฐ์์ ํ๋์ target ๊ฐ์ฒด๋ก ๋งคํํ๋ ๊ฒฝ์ฐ๋ ์ ์ํ ์ ์๋ค. 14 ๋จ, ํ๋์ @Mapping source ์์ฑ์์ ํ๋์ ํ๋ผ๋ฏธํฐ ๊ฐ์ฒด๋ง ๋ค๋ฃฐ ์ ์์ -> @Mapping ์ฌ๋ฌ๊ฐ๋ฅผ ์ฌ์ฉํ ์ ์์ 15 * * target : method์ ๋ฆฌํด ํ์ 16 */ 17 @Mapping(source = "condition.equipmentType", target = "equipmentType", qualifiedByName = "equipmentTypeToString") 18 /* โ source์ชฝ ๊ฐ์ฒด์์ equipmentType์ด๋ผ๋ ๋ณ์์ ํ์ ์ด EquipmentType์ด๋ผ๋ Enum๊ฐ์ฒด์ธ๋ฐ, 19 target์ชฝ ๊ฐ์ฒด์์๋ String type์ผ๋ก ์ ์๋์ด์๋ ๊ฒฝ์ฐ -> ๋ณ๋ ์ค์ ์์ผ๋ฉด Enum name์ด ๊ทธ๋๋ก target์ผ๋ก ๋งคํ๋จ 20 ์ด ๊ฒฝ์ฐ Enum์์ ๋ณ๋๋ก ์ฌ์ฉํ๊ณ ์ถ์ fullName์ ์ง์ ํ๋ค๊ณ ๊ฐ์ ํ๊ณ ๊ทธ๊ฒ์ ๋งคํํ๋ ๋ฐฉ๋ฒ์ ์๋ก ๋ค์์ */ 21 @Mapping(source = "condition", target = "product", qualifiedByName = "productToBaseProduct") 22 /* โ source ๊ฐ์ฒด ์์ฒด๋ฅผ target ๋ณ์์ ๋งคํํ๋๋ฐ ์ฌ์ฉํ ์ ์์ 23 ์ด ๊ฒฝ์ฐ equipmentType๋ณ๋ก ๋งคํํด์ผํ๋ Product ํด๋์ค๊ฐ ๋ค๋ฅธ ๊ฒฝ์ฐ๋ฅผ ๊ฐ์ ํ๊ณ ์๋ฅผ ๋ค์์ */ 24 @Mapping(source = "condition.param", target = "params") 25 /* โ ๋จ์ํ ๋ณ์์ ์ด๋ฆ๋ง ๋ค๋ฅธ ๊ฒฝ์ฐ ์์ ๊ฐ์ด ๊ฐ๋จํ๊ฒ ์ ์ํ ์ ์์ 26 ์ด ๊ฒฝ์ฐ source์ param์ List<Param>์ด๊ณ target์ List<ParamDto> params๋ก ์ ์๋์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ๊ฐ์ ํ์๊ณ 27 MapStruct์์ ์์์ ๊ฐ์ฒด, ์ฝ๋ ์ ๋งคํ์ ํ์ํ ๋ฉ์๋๋ฅผ 2๊ฐ ์์ฑํจ */ 28 ConditionDto toConditionDto(Condition condition); 29 30 //๋ณ์๋ช , ํ์ ๋ฑ์์ ํน์ด์ฌํญ์ด ์๋ ๊ฒฝ์ฐ ์๋์ ๊ฐ์ด ์ถ์๋ฉ์๋ ์ ์๋ง ํด๋๋ฉด ๋งคํ ์ฝ๋๊ฐ ์์ฑ๋๋ค. 31 ProductChild1 toChild1(Product product); 32 ProductChild2 toChild2(Product product); 33 34 /* static method ๊ตฌํ 35 * toConditionDto ๋ฉ์๋์ ์ฒซ๋ฒ์งธ @Mapping ์ค์ ์ค qualifiedByName ์์ฑ์์ ํธ์ถํ๊ฒ ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํ ์์ญ์ด๋ค. 36 * MapStruct๊ฐ ๋งคํ ํด๋์ค๋ฅผ ๊ตฌํํด์ค ๋ @Named ์ด๋ ธํ ์ด์ ์ ์์ฑํ ์ด๋ฆ์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ qualifiedByName์ด๋ ๋ง์ถฐ์ค์ผํจ 37 */ 38 @Named("equipmentTypeToString") 39 static String equipmentTypeToString(EquipmentType equipmentType){ 40 return equipmentType.getFullName(); 41 } 42 43 @Named("productToBaseProduct") 44 static BaseProduct productToBaseProduct(Condition condition){ 45 BaseProduct returnProduct = null; 46 Product product = condition.getProduct(); 47 48 switch (condition.getEquipmentType()){ 49 case EQUIP1: 50 returnProduct = INSTANCE.toChild1(product); 51 break; 52 case EQUIP2: 53 returnProduct = INSTANCE.toChild2(product); 54 break; 55 } 56 57 return returnProduct; 58 } 59} -
์ปดํ์ผ ํ ์๋ ์์ฑ๋ ํด๋์ค ๊ตฌ๊ฒฝ
๋๋ฌด ๊ธธ์ด์..๐ gist link
-
2์์ ์์ฑํ INSTANCE๋ฅผ ํตํด ๋ฉ์๋ ํธ์ถ
1public ConditionDto getCondition(Long id){ 2 ... 3 Condition condition = conditionRepository.findById(id); 4 5 return ConditionMapper.INSTANCE.toConditionDto(condition); 6}
MapStruct์ ModelMapper์ ์ฐจ์ด์ (=MapStruct๊ฐ ๋ ์ข์ ์ด์ )
-
ModelMapper
- Collection, Enum ํ์ ์ ๋ณ์๋ฅผ ๋ณํํ ๋ ์์ฑํ๋ ์ฝ๋๊ฐ ๋๋ฌด ์ฅํฉํจ
- ๋ฐํ์์์ ์์ธ๋ฅผ ๋ฐํํจ
- ๋ฌธ์ ๊ฐ ์์ ๋ ๋๋ฒ๊น ์ด ๋ถ๊ฐ๋ฅํจ
-
MapStruct
- Collection, Enum ํ์ ๋ณํ ์ฝ๋๊ฐ ๊ฐ๋จํจ
- ์ปดํ์ผํ ๋ ์์ธ๋ฅผ ๋ฐํํจ
- ๋๋ฒ๊น ์ด ์ฌ์ : ์ธํฐํ์ด์ค๋ก ๋งคํํ ๋ด์ฉ์ ์ ์ํ๋ฉด ์ปดํ์ผ ์ getter, setter๋ฅผ ์ฌ์ฉํ ํด๋์ค๋ฅผ ์๋์์ฑํด์ฃผ๊ธฐ ๋๋ฌธ์
-
Collection type ๋ณํ ์ฝ๋ ๋น๊ต
- ์กฐํ ๊ฒฐ๊ณผ๊ฐ findAll์ธ ๊ฒฝ์ฐ
1// ModelMapper 2List<User> users = userRepository.getUsers(); 3List<UserDto> userList = users.stream() 4 .map(user -> modelMapper.map(user, UserDto.class)) 5 .collect(Collectors.toList());1// MapStruct 2UserDto toUserDto(User user); 3List<UserDto> toUserDtoList(List<User> users); - ์กฐํ ๊ฒฐ๊ณผ๊ฐ findOne์ธ ๊ฒฝ์ฐ
1// ModelMapper 2User user = orderRepository.getOrdersGroupByUser(); 3List<OrderDto> orders = user.getOrders() 4 .map(order -> modelMapper.map(order, OrderDto.class)) 5 .collect(Collectors.toList()); 6UserDto userDto = modelMapper.map(user, UserDto.class); 7userDto.setOrders(orders);1// MapStruct : findAll์์ ์์ฑํ toUserDto๋ก ์ฒ๋ฆฌ ๊ฐ๋ฅ
- ์กฐํ ๊ฒฐ๊ณผ๊ฐ findAll์ธ ๊ฒฝ์ฐ
-
Enum type ๋ณํ ์ฝ๋ ๋น๊ต
-
Enum
1@Getter 2public enum Currency{ 3 WON("๏ฟฆ"), 4 DOLLAR("$"); 5 6 private String symbol; 7 8 Currency(String symbol){ 9 this.symbol = symbol; 10 } 11} -
ModelMapper
1@Bean 2public ModelMapper modelMapper(){ 3 ModelMapper modelMapper = new ModelMapper(); 4 5 Converter<Currency, String> currencyConverter = ctx -> ctx.getSource().getSymbol(); 6 PropertiMap<Item, ItemDto> itemMap = new PropertyMap<>(){ 7 protected void configure(){ 8 using(currencyConverter).map(source.getCurrency()).setCurrency(null); 9 } 10 } 11 modelMapper.addMappings(itemMap); 12 13 return modelMapper; 14} -
MapStruct
1@Mapping(source = "item.currency", target="currency", qualifiedByName = "currencyToSymbol") 2ItemDto toItemDto(Item item); 3 4@Named("currencyToSymbol") 5static String currencyToSymbol(Currency currency){ 6 return currency.getSymbol(); 7}
-
-
์ฑ๋ฅ ์ฐจ์ด : MapStruct > ModelMapper
์ถ์ฒ : Java ๋งคํ ํ๋ ์์ํฌ์ ์ฑ๋ฅ
๊ฒฐ๋ก
๋ ์ด์ MapStruct ์์ด JPA๋ฅผ ์ฌ์ฉํ ์ ์๋ ๋ชธ์ด ๋์๋ค. ์ฐ๋ฆฌ๋๋ผ์์๋ ModelMapper์ ์ ์ ์จ์ด ๋ ๋๋ค๊ณ ํ๋๋ฐ MapStruct ๋ง์ด ์ฐ๋ฉด ์ข๊ฒ ๋ค.
์ฐธ์กฐ
NHN Cloud Meetup Object Mapping ์ด๋๊น์ง ํด๋ดค๋?
/end of Java Bean Mapping library ์ ์ฉ - MapStruct
CONTENT LISTMERRI๏ผs DEVELOG