Swimming/Think

나는 왜 Mapper를 사용하지 않는가?

JKROH 2023. 5. 31. 00:24
반응형

 부트캠프 내에서 Dto를 엔티티 객체로 전환하는 방법 중 하나로 mapper를 사용하는 방식을 배웠다. Dto를 객체로 '변환'하는 역할은 어떤 객체가 해내야만 하는 일이었고, 부트캠프에서는 이 방법 중 하나인 mapper를 가르쳐주며, 상당히 간편하게 사용할 수 있음을 말해주었다.

 

 실제로 그랬다. 애너테이션을 붙이고 JPA를 사용하듯이 메서드 이름만 작성해주는 인터페이스를 사용하면 간단하게 매핑이 가능했다.

 

 문제는 복잡한 매핑에서 일어났다. JPA에서도 복잡한 쿼리는 쿼리 애너테이션을 붙이고 직접 작성하듯이, mapper 에서도 복잡한 매핑은 defualt 메서드를 만들고 내부적으로 매핑의 과정을 거쳐야 했다. 그런데 생각해보니 이게 정말 '매핑'이 맞나? 하는 생각이 들었다.

 

 mapping이란 무엇인가. 이를 한글로 표현하면 '대응시키기' 정도가 되겠다. 즉, A는 1, B는 2와 같이 '별도의 복잡한 과정을 거치지 않고' 서로 다른 두 개의 오브젝트를 매칭시켜줘야한다. 그렇다면, mapper가 하는 일은 무엇일까. 당연히 대응을 직접 시키는 것이다. 그것도 별도의 복잡한 과정을 거치지 않고.

 

 그런데, mapper를 사용해서 객체를 변환하는 코드를 작성하는 과제를 수행하던 중, 복잡한 매핑을 해줘야 하는 경우를 마주한 순간이 있었다. N-N 관계가 형성된 객체를 어떻게 매핑하느냐를 구현해야 했는데, 작성을 완료하고나니 거의 모든 매핑 메서드를 내가 직접 default 메서드로 구현하고 있었다. 처음 과제를 다 수행했을 때 든 생각은 '와 다했다!' 가 아니라 '이게 맞나?' 였다. 인터페이스로 간단하게 구현할 수 있고, 외부 API에 매핑의 책임을 넘긴다는 매퍼의 장점은 이미 사라지고 없었다. 대신, 까딱 잘못하면, 별도의 처리 로직이 주입될 가능성만 높아진 기이한 객체가 남아있었다.

 

 그럼 Dto를 엔티티 객체로 바꿔주는 역할은 누가해야할까? 내가 선택한 답은 Dto가 알아서 하는 것이었다. 처음에는 매퍼를 제외한 3가지 선택지가 있었다.

  1. 컨트롤러에서 바꿔준다.
  2. 서비스에서 바꿔준다.
  3. Dto가 알아서 변신한다.

 컨트롤러에서 바꿔주는 것은 아니었다. 컨트롤러는 클라이언트로부터 뭘 받아서 서비스에 처리해달라고 넘기기만 하는 멍청한 놈으로 남아야한다.

 

 서비스에서 바꿔주자니 굳이? 싶었다. 내가 처리해야하는 객체의 변환은 크게 복잡하지 않다. 안 그래도 맡은 바 책임이 엄청난 서비스에게 추가적으로 업무를 쥐어주고 싶지 않았다.

 

 마지막으로 Dto가 알아서 변하는 것을 생각해보았다. 크게 나쁘지 않아 보였다. Dto는 본체 엔티티 객체를 알고 있어도 됐고, Dto에 넘어오는 값들을 setter를 사용해서 엔티티 객체에 넣어주기만 하면 됐다. Dto에서 처리하기 어려운 정보들은 서비스로 살짝만 넘겨주면 됐다.

 

 이렇게 리팩토링을 진행하고 나니, 복잡한 mapper는 필요가 없어졌다. 단순한 값 전달은 Dto에서 전담했고, 그 외의 복잡한 로직은 복잡한 로직을 처리하라고 만들어진 서비스에서 담당했다. 물론, mapper에만 매핑로직이 전담되어 있는 것에 비하면 기능이 서비스와 Dto 두 군데로 분산되었지만, 이는 크게 문제가 되지 않았다. 값 자체가 넘어오는 로직이 추가되면 Dto에서 처리하면 됐고, 값을 가공해야하면 해당 기능을 담당하는 서비스의 로직에서 처리하면 됐다. 되려, 맡은 바 역할 이상의 일을 처리할 가능성이 있는 mapper를 제거하여 더 마음 편하게 Dto와 값들을 다룰 수 있게 되었다.

 

 물론, 이 역시 정답은 아닐 수 있다. 하지만 내가 생각하기에는 mapper를 사용하는 것보다 이 방식으로 처리하는 것이 더 안전하게 프로그래밍 할 수 있는 방법인 것 같다. 좀 더 규모가 큰 프로젝트를 진행하며 이 과정에 대해 고민해볼 기회가 오면 한 번 더 고민해봐야겠다.

 

 

반응형