티스토리 뷰

헥사고날 아키텍처의 기본 도식

 

2~3가지 서비스를 거치면서 개발했던 방식은 Layered 아키텍처를 벗어난 적이 없다.

 

6년짜리 레거시 코드를 Layered 아키텍처로 운영/개발할 때도 큰 불편함을 느끼지 못 했었다.

 

당시를 떠올려보면 그렇게 생각했던 이유가 몇 가지 있는데..

 

1. 테스트를 구현하지 않았음

2. 서비스의 규모와 팀의 규모가 크지 않았음

3. R&R을 나누는 직급이 아니었음

 

이 정도가 아닐까 싶다.

 

그러나 이직 후 업무 분담도 하고, 서비스 규모가 점점 커지는 개발을 하다보니 Layered 아키텍처의 문제를 느끼게 됐다.

 

(내가 느꼈던) Layered 아키텍처의 문제

 

레이어드 아키텍처의 기본 도식

 

1. Service가 너무 커져서 파생되는 문제들

 

서비스 하나의 크기가 1388줄이다.

 

- 어떤 기능을 추가하면 Service에만 추가하고, 비슷한 Service를 만들게 되서 계속해서 커지고 역할분리가 애매하게 됨

- 그에 따른 한눈에 들어오지 않는 Service의 역할(어떤 컨트롤러에서 쓰이고 어느 서비스에서 이 매서드를 불러가나?)

- 또 그에 따른 종종 발생하는 순환 참조 문제

- Service를 테스트 하기 위해서는 엄청난 양의 Mock 객체가 필요해짐

 

역할 분리를 엄격히 잘해뒀다면, 코드의 길이는 문제가 되지 않겠지만 Layered 아키텍처에서는 나누기가 어려웠다.

 

2. R&R을 나누기 어려움

- 객체든 사람이든 기능보다는 데이터에 의존해서 역할을 나누게 됨 -> 적절한 크기로 작업이 분배가 안됨

- 한 Service에서 여러명이 작업하고 Merge하면, 모든 사람이 무수한 Conflict와 마주하게 됨

 

위와 같은 문제가 발생하기 시작했다.

 

무엇보다 테스트 작성을 해야하는데, 테스트를 하는건지 Mocking을 하는건지 모를 정도로 많은 Mock 객체가 들어와서 난감했다.

 

그래서 테스트가 자주 누락되는 현상이 생기게 되면서

 

모든 테스트는 아니더라도 누가봐도 헷갈리는 부분은 테스트가 반드시 필요한데 그것조차 없는 경우가 생겼다.

 

일단, 테스트라도 쉽게 만들어보자는 생각에 한 부분을 잡고 대대적인 리팩토링에 들어갔다.

(일부 상용서비스 중인 부분이 있어서 안전을 위해 통합테스트는 모두 만들어놓고 시작했다)

 

CRUD 위주의 API 20개 남짓한 프로젝트여서 빠르게 결과를 볼 수 있었다.

 

Hexagonal 아키텍처로 변환 후 느낀점들

느낀점 1. 생각보다 쉽지 않았던 Port-Adapter 구조로의 변환

인터페이스를 기반으로 개발하는게 익숙하지 않다면, 이 부분이 가장 큰 러닝 커브를 만드는 요소다.

 

"도메인을 유지하면서 의존성을 외부에서 주입받는 구조를 만든다. 주입받는 방식은 자유롭게 선택한다."

 

이 개념은 초기 구축하는 사람이 아니고선 느끼기 어려울 수도 있겠다는 생각이 들었다.

 

그리고 Spring Data JPA는 영속성을 감춰두고 쓰기 때문에, 이걸 굳이 분리해서 써야하나? 란 생각이 들 수 밖에 없다.

 

내가 사용하는게 Spring Data JPA만이 아니라 여러 DB를 쓴다면? 이라는 가정은 작은 시스템에선 하기 어렵다.

 

추상화가 가져다 주는 편의성이 본질을 가려버리는 대표적인 예가 되는 것 같다.

 

느낀점 2. 굳이..? 라고 생각했던 영속성 Port와 과하게 나누게 되는 Usecase들

거의 Spring Data JPA + QueryDSL로 사용하면 이미 많은 부분이 추상화가 되어있다.

 

그러다보니 영속성 Port에 Spring Data JPA에서는 작성하지 않아도 될 중복 코드가 많이 생기는 문제가 생겼다.

 

다른 영속성 라이브러리나 DB를 쓰는 부분이 많다면, 고려해야할 부분이 있겠지만 지금은 써봐야 Redis 정도다.

 

그래서 영속성 Port의 유지는 생각을 해봐야할 문제 같다. 테스트가 문제 없다면 과감히 삭제할 수도 있을 것 같다.

 

마지막으로, Usecase들은 팀 컨벤션에 따라 어느정도 묶어두는걸 허용하는게 좋을 것 같다.

 

아직 메인 서비스에 적용 전이지만, 중복 파일이 많이 생길게 눈에 보인다.

 

느낀점 3. R&R은 Usecase로 나누면 됨

데이터로 R&R을 나누게되면 한 사람이 맡아야하는 작업의 비중이 커지는데, 이 부분을 쉽게 해결할 수 있을 것 같다.

 

그리고 Service가 implements한 Usecase들만 확인하면 서비스의 역할도 쉽게 확인할 수 있다.

 

에러 분석할 때 더이상 컨트롤러와 여러 서비스들을 방황할 필요가 없어졌다.

 

느낀점 4. Service에서 테스트를 만들기 쉬워짐

Service의 의존성이 Usecase와 Port로 분리되면서 Fake객체를 만들 수 있게 됐다.

 

통합 테스트에 의존하지 않고, 소형 테스트를 구현 하기 편해졌다.

 

TDD까지는 아니더라도 중요한 부분에는 테스트가 반드시 들어가야 한다고 생각하는 입장이라 장점밖에 안 느껴졌다.

 

느낀점 4. 테스트는 기능을 테스트하는 용도도 크지만 히스토리 용도도 크다

아키텍처와는 다른 곳에서 느낀 점이지만, 테스트는 사이드 이펙트를 잡는 용도도 크지만 이 코드가 왜 만들어졌는가를 남기는 요소도 포함하고 있는 것 같다.

 

주석도 서비스 로직에 남겨져 있는 것보다, 테스트에 남기는게 최신화 하기 쉽다.(여러 곳에서 주석을 남기지 말라고 하는 이유는 주석이 최신화 되지 않았을 때의 악영향이 크기 때문이었다.)

 

마치며

리팩토링을 고민하고 시작하게 된 계기는 이 강의를 듣고 나서였다.

 

리팩토링을 하면서 느낀 거지만, 이 강의는 헥사고날 아키텍처에서 테스트에 필요한 부분만 차용한 강의였다.

 

그냥 리팩토링 하는 것에서 그친게 아닌 본인 철학이 담긴 강의였다.

 

물론 강의 한편만 보고 시도해본 건 아니긴하다.

 

내가 마주한 문제를 해결하기 위한 한 방법이었을 뿐... 더 좋은 방법이 보이면 그 쪽을 알아보지 않을까?

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
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
글 보관함