안녕하세요. 오랫만입니다. 토이 프로젝트를 하다가 controller에서 mapping하는 로직이 있었습니다. 예를 들어, bookController에 있는 addBook 메서드를 보겠습니다.

 

 BookRegInfo 객체를 받아서, Book 객체에 넘겨 받은 객체에 대한 정보를 넣습니다. 그리고 이 객체를 service 단으로 넘겨 버립니다. 사실 이게 끝이긴 합니다. 그런데, 만약에 book의 필드가 많아지면 어떨까요? 컨트롤러 안의 addBook에서 mapping을 하는 로직을 어떻게 뺄지 고민을 했어요. 혹은, 이러한 일련의 작업들을 쉽게 해 주는 것이 없나? 도 찾아보았어요.

 

 찾아보니 mapstruct가 있었는데요. 이번 시간에는 이 친구에 대해 간단하게 알아 보겠습니다.

 


 mapstruct와 mapstruct-processor를 추가합니다. 그리고, 간단한 문제 상황을 하나 정의하도록 하겠습니다.

 

 

 먼저, testModel1입니다. userName과 pw가 있어요. getter와 setter가 있습니다.

 

 

 testModel2입니다. userName과 pw, 그리고 email이 있어요. getter와 setter도 있어요.

 

 

 다음에, testModel입니다. 우리는 testModel1이나 testModel2로 들어온 것을 testModel로 mapping을 시켜버린 다음에 service layer로 보내버리는 것이 목적입니다. mapstruct는 mapping을 시키는 부분을 쉽게 해결할 수 있어요. 간단한 튜토리얼 문서를 보고 따라해 봅시다.

 

 


 먼저, Mapping 역할을 할 mapper interface를 만들 거에요. testMapper를 만들었는데요. 위에 Mapper 어노테이션을 붙입니다. 그리고, instance를 하나 생성했는데요. 이것은 문서에 따르면, client가 mapper implements에 접근하기 위한 무언가를 의미합니다. 클라이언트가 무엇을 의미하는지는 아래에서 후술하겠습니다.

 

 

 다음에, testModel1ToTestModel과 testModel2ToTestModel이 있어요. 이것은 각각 testModel1을 testModel로, testModel2를 testModel로 mapping 시켜주는 메서드입니다. 그럴 거 같이 생겼네요. source와 target이 있는데요. A에서 B로 mapping이 된다고 했을 때, source는 A의 필드명, target은 B의 필드명을 의미합니다.

 

 그러면 Impl는 어디에 구현이 되어 있을까요? 컴파일을 한 후에 class 폴더를 보면 떡하니 testMapperImpl.class가 있는데요.

 

 

 디컴파일 된 결과를 보면 위와 같습니다. testModel1의 Username 속성을 testModel의 Username에 넣고, testModel1의 pw 속성을 testModel의 pw 속성에 넣습니다.

 

 

 controller는 위와 같이 작성하였어요.  위에서 클라이언트는 controller 안의 test1 메서드였던 셈입니다. 여기서 mapper에 접근하기 때문입니다. 15번째 줄을 보겠습니다. testMapper의 instance는 단순하게 testMapperImpl에 접근하기 위한 용도라고 보시면 될 듯 합니다. 저는 간단하게 테스트를 해 보기 위해서, testModel로 요청 받은 것을 testModel 객체로 떨어트리겠습니다. 포스트맨으로 테스트 해 보겠습니다.

 

 

 userName과 pw를 각각 "aaaa", "aaaa"로 넘겨 봅시다.

 

 

 그러면, 제가 의도했던 대로 잘 나왔음을 볼 수 있어요. 그런데 여기서 하나 의문점이 있어요. pw는 bcrypt 등으로 인코딩이 되어 있지 않네요. 사실, 제 토이 프로젝트에서는 pw를 받은 다음에 인코딩을 시켜서 User 객체에 넣은 다음에 service 단에 넘겨버리는데요. 이렇게 mapping 하는 로직을 custom하게 하려면 어떻게 하면 좋을지는 다음 시간에 해 보도록 하겠습니다.