헥사고날-아키텍쳐로-구현하는-작은-스프링-부트-토이-프로젝트-우아한스터디-11
만들면서 배우는 클린 아키텍쳐 11장 정리
의식적으로 지름길 사용하기
의식적으로 지름길을 사용해보는 이유는 이를 통해 우발적으로 사용되는 지름길을 인식할 수 있게 되고, 이를 수정할 수 있기 때문입니다.
또는 정당한 지름길이라면 의식적으로 지름길을 택할 수 있습니다.
소프트웨어는 하드웨어에 비해 쉽게 변경할 수 있기 때문에 어떤 때는 의식적으로 먼저 지름길을 택하여 구현한 다음 나중에 리팩터링을 통해 고치는 것이 더 경제적일 수 있습니다.
왜 지름길은 깨진 창문 같을까?
품질이 떨어진 코드에서 작업할 때 더 낮은 풀질의 코드를 추가하기 쉽습니다.
(이미 망쳤기 때문에 좀 더 망치더라도 그게 큰 차이는 없을거라고 생각할 수 있습니다.)
코딩 큐칙을 많이 어긴 코드에서 작업할 때 또 다른 규칙을 어기기 쉽습니다.
지름길을 많이 사용한 코드에서 작업할 때 또 다른 지름길을 추가하기 쉽습니다.
이 모든 것을 고려해 봤을 때 소위 레거시라고 불리는 많은 코드의 품질이 시간이 지나면서 더욱 더 심하게 낮아지는 것이 당연합니다.
깨끗한 상태로 시작할 책임
레거시 코드에 영향을 안받고 싶어도 그게 그리 쉽지 않습니다.
그래서 가능한 지름길을 쓰지 않고, 기술 부채를 지지 않은 채로 프로젝트를 깨끗하게 시작하는 것이 중요합니다.
타협을 하게 되면 나중에 더 많은 타협을 끌어 들이게 되기 때문입니다.
소프트웨어 프로젝트는 대개 큰 비용이 들고 장기적인 노력을 필요로 하기 때문에 애초에 편볍을 사용하지 말고, 제대로 시작할 필요가 있습니다.
그러나 작업 중인 부분이 프로젝트 전체로 봤을 떄 그리 중요하지 않거나 프로토타이핑 작업 중이거나 하는 상황에서 경제적인 이유로 지름길을 취하는 것이 더 실용적일 때가 있습니다.
이러한 의도적인 지름길에 대해서는 세심하게 잘 기록해야 합니다.
왜냐하면 현재 지름길을 택한다는 엇은 미래에 현재 프로젝트를 인계받는 개발자들에게 빚을 지고 있는 것이기 때문입니다.
그리고 원래 이것이 옳은 것이 아닌데 어쩔 수 없이 지금만 사용한다는 문서를 통해 다른 팀원들도 지름길로 같이 가려고 하는 것을 어느정도는 막을 수 있습니다.
유스케이스 간 모델 공유하기
유스테이스 간에 입출력 모델을 공유하게 되면 유스케이스들 사이에 결합이 생깁니다.
공유하고 있는 입출력 모델이 바뀌면 이를 공유하고 있는 유스케이스들도 모두 영향을 받습니다.
즉, 변경할 이유를 공유하고 있는 것입니다.
유스케이스 간 입출력 모델을 공유하는 것은 유스케이스들이 기능적으로 묶여 있을 때 유효합니다.
특정 요구사항을 공유하는 경우, 즉, 특정 세부사항을 변경할 경우 실제로 두 유스케이스 모두에게 영향을 의도적으로 싶은 경우에 괜찮습니다.
만약에 이러한 의도가 아니라면 이는 나쁜 의미의 지름길이 될 수 있습니다.
지금은 두 유스케이스 간에 입출력 모델이 거의 똑같더라도 아니 완전히 같더라도 프로그램이 복잡해짐에 따라 장차 독립적으로 가야할 확률이 높다면 복사 붙여넣기를 하더라도 일단은 입출력 모델을 분리해서 시작하는 것이 좋습니다.
그래서 비슷한 개념의 유스케이스를 여러 개 만든다면 유스케이스를 독립적으로 분리하여 발전시킬 필요가 있는지 주기적으로 질문해야 합니다.
만약에 그렇다면 그 때가 바로 유스케이스 간에 입출력 모델을 분리해아하는 시점입니다.
도메인 엔티티를 입출력 모델로 사용하기
이렇게 되면 인커밍 포트, 즉, 유스케이스는 도메인 엔티티에 의존성이 생깁니다.
유스케이스가 도메인 엔티티에 대한 의존성이 생기기 때문에 유스케이스가 바뀌어도 도메인에는 영향을 받지 않을 거라고 생각하지만 실제로는 그렇지 않을 수 있습니다.
예를 들어, 현재 도메인 엔티티에 존재하지 않는 정보를 유스케이스에서 필요로 한다고 생각해봅시다.
이 정보는 최종적으로는 도메인 엔티티에 저장되어 있으면 안되고 다른 영역에 저장되어야 하지만 그것보다는 유스케이스에서 도메인 엔티티 영역을 바로 사용하기 때문에 도메인 영역에 이 정보를 추가하면 손쉽게 해결할 수 있을 것이라는 유혹이 생깁니다.
간단한 생성이나 업데이트 유스케이스에서는 유스케이스 인터페이스 입출력 모델로 도메인 엔티티를 바로 사용해도 괜찮습니다.
왜냐하면 데이터베이스에 저장해야 하는 그 상태 정보가 바로 도메인 엔티티에 존재하기 때문입니다.
하지만 유스케이스가 복잡한 도메인 로직을 구현해야 한다면 반드시 유스케이스 인터페이스에 대한 전용 입출력 모델을 만들어야 합니다.
그렇지 않으면 유스케이스의 변경이 도메인 엔티티의 변경까지 유발하기 때문입니다.
도메인 엔티티를 입출력 모델로 바로 사용하는 것이 위험한 이유는 대다수의 유스케이스들이 간단한 생성 또는 업데이트 유스케이스로 시작해서 시간이 지나면서 점점 복잡한 도메인 로직이 되기 때문입니다.
이렇게 되면 처음에는 유스케이스에 영향을 받지 않던 도메인 엔티티가 점점 더 유스케이스로 인해 많은 영향을 받게 됩니다.
그러므로 처음에는 도메인 엔티티를 바로 입력 모델로 사용했더라도 이러한 시점이 도달한다면 유스케이스 전용 입력 모델을 만들어 교체해야 합니다.
인커밍 포트 건너뛰기
인커밍 어댑터(컨트롤러)가 인커밍 포트(유스케이스 인터페이스)없이 애플리케이션 구현체(유스케이스 인터페이스 구현체) 직접 접근하는 것은 의존성 역정에 있어서 필수적인 요소는 아니고 이로 인해 오히려 컨트롤러와 서비스 계층 사이의 추상화 계층을 없앴습니다.
보통 쓸모없는 추상화를 줄이는 것은 좋게 여겨집니다.
하지만 인커밍 포트(유스케이스 인터페이스)는 애플리케이션의 중심에 접근하는 진입점 역할을 합니다.
그런데 이를 제거해버리면 특정 유스케이스를 구현하기 위해, 즉, 컨트롤러에서 어떤 서비스 메서드를 호출해야 할지 알기 위해서 애플리케이션 내부 동작에 대한 더 깊은 이해가 필요합니다.
하지만 전용 인커밍 포트가 있다면 한 번애 진입점을 식별할 수 있습니다.
(왜냐하면 전용 인커밍 포트를 구현한 구현체는 메서드가 하나 밖에 없기 때문에 고민을 할 필요가 없습니다.)
특히 이 것은 애플리케이션에 대한 이해가 없는 새로운 개발자가 코드를 파악할 때 큰 도움이 됩니다.
또한 인커밍 포트를 이용하면 아키텍처를 쉽게 강제할 수 있습니다.
10장에서 이야기한 package-private 방법을 이용해 application 계층의 구현체들은 모두 package-private 제한자로 설정하고, 인커밍 포트(유스케이스 인터페이스)는 public으로 설정해두면 인커밍 어댑터(컨트롤러)가 애플리케이션 서비스를 호출할 수 없고(package-private이기 때문에), 인커밍 포트만 호출할 수 있게 강제할 수 있습니다.
애플리케이션의 규모가 작거나 인커밍 어댑터가 하나밖에 없어서 굳이 유스케이스 인터페이스가 필요없을 수도 있습니다.
하지만 애플리케이션의 규모가 더 커진다면 분명히 문제가 발생할 것입니다.
애플리케이션 서비스 건너뛰기
어떤 유스케이스에서는 애플리케이션 서비스를 건너뛰고 싶은 경우가 있을 수도 있습니다.
이는 특히 간단한 CRUD 유스케이스에서는 보통 애플리케이션 서비스가 별도의 도메인 로직없이 단순히 생성, 업데이트, 삭제 요청을 그대로 영속성 어댑터에 전달하는 역할만 하기 때문에 애플리케이션 서비스의 존재 이유에 대해 의구심이 들 수 있습니다.
이런 경우 차라리 애플리케이션 서비스를 없애고, 영속성 어댑터가 직접 유스케이스를 구현하도록 할 수 있습니다.
하지만 이렇게 되면 인커밍 어댑터(컨트롤러)와 아웃고잉 어댑터(영속성 계층)사이에 도메인 엔티티 모델을 공유해야 합니다.
더 나아가 애플리케이션 코어에 유스케이스가 사라지게 됩니다.
단순한 CRUD일 경우 괜찮지만, 만약에 시간이 지남에 따라 유스케이스가 복잡해질 경우 이 도메인 로직을 영속성 계층에서 구현하고 싶은 유혹이 들 것이고, 그렇게 되면 도메인 로직이 여기저기 흩어져서 나중에 도메인 로직을 찾는 데 시간이 많이 걸려서 유지보수하기가 힘들어집니다.
즉, 처음에는 애플리케이션 코드가 단순히 전달만 하는 보일러플레이트 코드 같아서 이를 없애기 위해 간단한 CRUD 케이스에서는 애플리케이션 서비스를 건너뛸 수 있습니다.
하지만 만약에 단순히 CRUD 케이스였던 것이 더 복잡해져서 추가 로직이 필요할 경우 애플리케이션 서비스를 다시 구현해야 하는 경우가 생길 수 있습니다.
유지보구 가능한 소프트웨어를 만드는 데 어떻게 도움이 될까?
때로는 지름길을 선택하는 것이 경제적인 관점에서 봤을 때 더 합리적인 경우도 있습니다.
간단한 CRUD 유스케이스의 경우 굳이 전체 아키텍처를 구현하는 것은 지나칠 수 있기 때문에 지름길을 가는 것이 나을 수도 있습니다.
하지만 모든 애플리케이션은 처음에는 작게 시작하여 점점 더 커지고 복잡해지기 때문에 언젠가는 지름길로 구성한 코드를 장기적으로 더 유지보수하기 좋은 아키텍처로 변경해야 하는 시점이 옵니다.
만약 단순 CRUD 상태에서 벗어나지 않는 유스케이스도 있을 수 있는데 그런 경우는 지름길을 계속 유지하는 것이 더 경제적입니다.
그리고 마지막으로 가장 중요한 것은 나중에 나 자신(자기도 자기가 왜 이렇게 코드를 짰는지 까먹을 수 있기 떄문에)과 미래에 프로젝트를 인계받는 개발자들을 위해 아키텍처에 대해, 그리고 왜 특정 지름길을 선택했는지에 대한 기록을 남겨둬야 합니다.
댓글남기기