티스토리 뷰

몇년전만 해도 기본 DataType으로 Map과 VO가 혼재되어 사용하고 있었는데 점차 VO로 굳혀져 가고 있는듯하다. 서비스당 VO도 하나씩만 작성해서 사용하면 참 좋겠지만 계층간 아키텍처를 명확하게 하기 위해 DTO(data transfer object)와 Entity로 구분을 해서 사용을 한다. DTO는 프로세스간의 데이터를 전달하는 객체이고 Entity는 Persistent 영역과의 통신을 위해 사용되는 객체라고 생각하면 된다. 즉 화면에서 보낸 요청에 실린 Parameter를 DTO에 담아서 계층간 이동을 하고 쿼리에 바인딩될때, 쿼리로부터 결과를 받은것은 Entity에 담는다. 이를 또 응답으로 주기 위해서 값을 DTO로 변환을 하여 담는다. 이를 위해 DTO -> Entity, Entity -> DTO 변환을 해야 하는데 이를 해주는것이 바로 ModelMapper이다. ModelMapper를 사용하는 방법에 대해 알아보자.


기본 설정

pom.xml

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.4.4</version>
</dependency>

pom.xml 에서는 modelmapper dependency를 추가해준다. 


Case 1. 동일한 필드명일때 매핑

DTO

@Data
public class EmployeeDto {
    private String employeeId;
    private String employeeName;
    private String employeeAddr;
}

Entity

@Data
public class EmployeeEntity {
    private String employeeId;
    private String employeeName;
    private String employeeAddr;
}

클래스의 이름만 바뀌었을 뿐이지 속성은 동일하다. 이런 상황일때 ModelMapper를 사용해서 매핑하는 방법에 대해 알아보자. 

 

ModelMapper 사용법

@Test
void modelMapperTest(){
    EmployeeDto employeeDto = new EmployeeDto();
    employeeDto.setEmployeeId("12345");
    employeeDto.setEmployeeName("oing");
    employeeDto.setEmployeeAddr("동작구");
    
    ModelMapper modelMapper = new ModelMapper();
    EmployeeEntity employeeEntity = modelMapper.map(employeeDto, EmployeeEntity.class);
    
    System.out.println(employeeEntity.getEmployeeId());
    System.out.println(employeeEntity.getEmployeeName());
    System.out.println(employeeEntity.getEmployeeAddr());
}

ModelMapper 객체를 생성해 map이라는 메소드를 통해 사용이 가능하다. map 메소드 인자의 순서는 source, target 이다. 간단하다. 


Case 2. 다른 필드명일때 매핑

DTO

@Data
public class EmployeeDto {
    private String employeeId;
    private String employeeName;
    private String employeeAddr;
}

Entity

@Data
public class EmployeeEntity {
    private String employeeId;
    private String employeeName;
    private String employeeAddress;
}

아까와는 다르게 Entity에서 employeeAddr -> employeeAddress로 변경을 했다. DTO와 Entity가 서로 다른 필드명이 있기에 Case1과 같은 방식으로 매핑을 하면 employeeAddress 에는 값이 들어가지 않는다. 이런 경우에 매핑 룰을 추가하여 DTO와 Entity를 매핑시켜 줄 수 있다.

 

ModelMapper 사용법

@Test
void modelMapperTest(){
    EmployeeDto employeeDto = new EmployeeDto();
    employeeDto.setEmployeeId("12345");
    employeeDto.setEmployeeName("oing");
    employeeDto.setEmployeeAddr("동작구");
    
    PropertyMap<EmployeeDto, EmployeeEntity> employeeMap = new PropertyMap<EmployeeDto, EmployeeEntity>() {
        protected void configure() {
            map().setEmployeeAddress(source.getEmployeeAddr());
        }
    };
    
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.addMappings(employeeMap);
    EmployeeEntity employeeEntity = modelMapper.map(employeeDto, EmployeeEntity.class);
    
    System.out.println(employeeEntity.getEmployeeId());
    System.out.println(employeeEntity.getEmployeeName());
    System.out.println(employeeEntity.getEmployeeAddress());
}

ModelMapper에서 제공하는 PropertyMap 이라는것으로 매핑룰을 설정할 수 있다. 제네릭에 들어가는 순서는 마찬가지로 source, target 순서이다. 매핑룰을 설정했으면 ModelMapper에 넣어주고 이전과 같이 하면 값이 잘 들어가는 것을 확인 할 수 있다.  


Matching Strategy

ModelMapper에는 Matching Strategy(매칭 전략)이 있는데 이건 두개의 VO 클래스의 매핑의 레벨같은 옵션같은거라고 보면 된다. ModelMapper 사이트에 나온 내용은 이렇다. 

 

Matching Strategies

Matching strategies are used during the matching process to match source and destination properties to each other. Below is a description of each strategy.

Standard

The Standard matching strategy allows for source properties to be intelligently matched to destination properties, requiring that all destination properties be matched and all source property names have at least one token matched. The following rules apply:

  • Tokens can be matched in any order
  • All destination property name tokens must be matched
  • All source property names must have at least one token matched

The standard matching strategy is configured by default, and while it is not exact, it is ideal to use in most scenarios.

Loose

The Loose matching strategy allows for source properties to be loosely matched to destination properties by requiring that only the last destination property in a hierarchy be matched. The following rules apply:

  • Tokens can be matched in any order
  • The last destination property name must have all tokens matched
  • The last source property name must have at least one token matched

The loose matching strategy is ideal to use for source and destination object models with property hierarchies that are very dissimilar. It may result in a higher level of ambiguous matches being detected, but for well-known object models it can be a quick alternative to defining mappings.

Strict

The strict matching strategy allows for source properties to be strictly matched to destination properties. This strategy allows for complete matching accuracy, ensuring that no mismatches or ambiguity occurs. But it requires that property name tokens on the source and destination side match each other precisely. The following rules apply:

  • Tokens are matched in strict order
  • All destination property name tokens must be matched
  • All source property names must have all tokens matched

The strict matching strategy is ideal to use when you want to ensure that no ambiguity or unexpected mapping occurs without having to inspect a TypeMap. The drawback is that the strictness may result in some destination properties remaining unmatched.

 

많은 개발자들에게 이런 환경을 제공해야 하는 입장이라면 Strict가 적합하다. 그래야 사고를 방지할 수 있다. 조금 귀찮더라도 PropertyMap을 활용해 매핑룰을 설정하여 완전하게 매핑시키는것을 추천한다. (뒤에 설정방법 있음)


ModelMapper Configuration

ModelMapper 공식 홈페이지에 나온 Config 하는 방법에 대해 옮겨봤다. 

 

protected 메서드가 일치하도록를 구성

modelMapper.getConfiguration().setMethodAccessLevel(AccessLevel.PROTECTED);

개인 필드가 일치되도록를 구성

modelMapper.getConfiguration()
  .setFieldMatchingEnabled(true)
  .setFieldAccessLevel(AccessLevel.PRIVATE);

Loose[MatchingStrategies 일치 전략] 을 사용 하도록 구성

modelMapper.getConfiguration()
  .setMatchingStrategy(MatchingStrategies.LOOSE);

모든 소스 및 대상 속성 이름이 일치 할 수 있도록 구성

modelMapper.getConfiguration()
  .setSourceNamingConvention(NamingConventions.NONE);
  .setDestinationNamingConvention(NamingConventions.NONE);

Underscore 매핑

modelMapper.getConfiguration()
  .setSourceNameTokenizer(NameTokenizers.UNDERSCORE)
  .setDestinationNameTokenizer(NameTokenizers.UNDERSCORE)

 

참조 : http://modelmapper.org/user-manual/configuration/#matching-strategies

 

끝!

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31