18년된 프로젝트를 현대화적인 프로젝트로 전환하며 느낀 점
멀티 모듈 프로젝트를 적절히 만드는 비법
아키텍쳐는 프로젝트 초기에 이루어져야하는 일련의 설계 결정이다. 아키텍처는 요소의 구조와 그 관계에 관한 것.
아키텍처라는 단어를 멀티 모듈로 바꿔서 생각해보자. 멀티 모듈 프로젝트는 초기에 이루어져야하는 일련의 설계 결정이다. 멀티 모듈 프로젝트는 요소의 구조와 그 관계에 관한 것.
- 결국 멀티모듈 프로젝트는 나중에 변경하기 어렵다(CI/CD와 밀접하므로, CI/CD의 프로세스까지 모두 바꾸어야 함.)
- 리스크를 줄이기 위한 시작이라는 뜻.
서비스 초기에는 core, command, admin, api, batch 구조에서 멀티모듈이 적절하게 분배되어 있을거임 그러나 서비스가 길어지면 core와 common이 비대칭적으로 커지고, api, batch 등이 계속해서 만들어진다. 이렇게 되는 이유는 중복을 제거하라는 개발자의 강박과 같은 버릇 때문이다. 그렇다면 core, common이 모든 곳에서 정말 다 필요한가?
중복을 제거하기 위해서 core & common과 같은 곳에 모두 구현하면 많은 문제점이 생겨난다. 그 중 대표 4가지.
- Too many connections
- common을 의존하는 모든 서비스들이 커넥션 풀을 모두 할당 받는다.
- NoClassDefFoundError
- 특정한 모듈이 하위 버전 라이브러리를 참조하는 경우 업그레이드가 난해함
- "Copy & Paste"
- 참조하는 곳이 너무 많아 일단 if.. else 분기처리 copy * paste
- 즉, 코드를 점점 이상하게 만들어내고, 참조하는 클래스가 많아서 어디서 사이드 이펙트가 발생할지 모름
- 깨진창문 효과 -> 급속도로 나쁜 코드들이 생겨남
- All Build & Deploy
- 특정한 클래스를 수정해서 배포할 때도, 글자하나 바꿔서 배포할 떄도... 무조건 All Build & Deploy
이렇게 멀티 모듈 프로젝트를 나누는 기준이 적절하지 않다면, 4가지 사례가 언젠가 나타날 수 있다. 그 때 수정을 해야한다면 굉장히 고통스럽고 많은 비용이 들 수 있다.
오늘 내용 중 가장 중요한 챕터
core & common의 역할, 책임, 협력 관계가 올바른가? (사진) 데이터 접근 방식에 따라(기술 베이스 지향적인) 멀티 모듈을 또 나누게 될 것이다. 그럼 이건 도메인 기반이 아니라 기술 베이스인데... 이게 올바른건가?
노래를 하나 추가한다고 가정해보자. META 라는 멀티 모듈에 노래 정보를 때려박는다. (뮤직서비스의 기반이 되는 공통 모듈) 재생은 MySQL, 가사는 MongoDB에 적재되는 경우 META 도메인은 어떤 모듈에 위치해야하는가?
가사라면 가사 번역, 재생이라면 스트리밍, AI, VOD 등 외부 유관부서, 업체 연동 모듈이 계속해서 추가되게 된다. 지속적으로 멀티모듈이 늘어난다. 접접 복잡해지고 있다.
"뭘 기준으로 나뉘어야...?"
일단 무조건 CORE 와 COMMON을 삭제하고 시작해라. 우리는 이미 다 알고 있다. 이미 다 있다. 코드가 일부 중복되는 것보다, core와 common이 잠재적으로 가지고 있는 위험성이 더 큰 문제다. 우리가 겪고 있는 지금 시대는 10년, 20년 전에 비해 엄청나게 복잡하고 고도화되어 있다. 그 때는 맞고, 지금은 틀리다. 중복은 제거되어야 한다고 말을 하지만, 요즘은 중복이 유지되는게 오히려 낫다.
무얼 기준으로 -> DDD에서 경계나누기로 불리우는, 바운디드 컨텍스트를 기준으로 생각해보자. DDD는 큰 모델을 서로 다른 Bounded Context로 나누고 상호관계를 명시하여 처리한다.
BOOT(Server) 서버모듈 (batch, admin, api) 코드의 변화가 제일 잦게 일어남. 이걸 하나ㅡ이 그룹으로 생각한다. DATA(DOMAIN) 데이터모듈(domain, meta, user, chart) INFRA(연동모듈) 구현되고 나면 변화는 적지만, 버전업이 되면 아주 큰 변화 CLOUD(SYSTEM) 모듈 (사진)
4개를 기준으로 그룹을 나누고 프로젝트를 구성하면 된다. 특징에 맞고 성격에 맞고 하도록...
발표한대로 해봤는데, 계속늘어나는 모듈들 > 복잡도 증가 > 늘어나는 빌드 시간 + .... 프로젝트의 변경 뿐만 아니라 다른 프로젝트의 변경사항으로도 웹훅이 반응해버린다. 이런걸 어떻게 해결해야할까? 프로젝트를 조금더, 멀티그룹의 특성에 맞게 한번 더 쪼개자.
- BOOT, DATA는 그대로 둔다. 잘 되어있음.
- CLOUD 쪽을 나눔.
- INFRA를 INFRA-LIBRARY로 나눔
DATA <-> INFRA 멀티그룹간 관계 구현. DATA 모듈에서 디비 접근까지 다 관리하고, INFRA 모듈은 INFRA 책임만 지도록. INFRA 모듈에서 디비에 접근하지 않도록.
BOOT <-> DATA 서비스(@Service) 구현체를 어디에 두어야 하는가? 둘 다 있어야 한다. 대신 규칙이 하나 필요한데, boot 모듈에서 data 모듈로 절대 ServletRequest 관련 객체가 내려가면 안된다. DATA 모듈은 모든 모듈에서 참조하기 때문에, (모두가 웹 서버 방식이 아님) 때문에 웹과 의존적인걸 내려보내주면 안된다. 웹 기반 의존성이 주입되면 테스트 코드를 작성할 때 웹서버 관련된 라이브러리가 전부 필요해지고, 라이브러리가 의존성을 갖게 되는 순간 Mock객체를 만들어내는 사람도 생기고, DATA 모듈이라는 의미가 상시로딘다.
CLOUD <-> BOOT 관계 CLOUD 제품에 버전업이 발생하면 BOOT 쪽도 전부 빌드 배포가 되어야 한다. (대표적으로 Discovery 와 같은 제품) 둘 간의 역할과 책임이 덜 분리된게 아닐까? 의문이 생김. 이걸 한방에 해결해준게 Istio
왜 멀티모듈 프로젝트 구조가 중요한가?
- 잘뭇 구성되면 나중에 변경하기 고통스럽다.
- 프로젝트 초기에 이루어져야 하는 일련의 설계 과정이다.
- 개발 생산성에 많은 영향을 미친다.
무엇을 기준으로 멀티 모듈 프로젝트 구조를 나뉘어야 할까요?
- 경계 안에서 의미를 갖을 수 있는 그룹을 정의하는(나누는)것이 가장 중요하다 (바운디드 컨텍스트)
- 역할, 책임, 협력 관계가 올바른지 다시 한번 생각한다.
- BOOT(server), INFRA, DATA(DOMAIN), SYSTEM(Cloud)
어떻게 실전 멀티 모듈 프로젝트 구현을 해야할까요?
- 프로젝트가 커지고 있다면 다시 경계를 나누고 그 기준으로 소스 저장소를 분리한다.
- INFRA(외부) 라이브러리에너는 DATA 관련 구현을 지향한다. (Anticouruption Layer)
- 서비스 구현은 각자 역할에 맞게 각각 구현될 수 있다. (공통으로 한쪽에 구현하지 않는다.)
- 시스템 레벨 구현이 실제 서비스 애플리케이션과 밀접하게 연관되지 않게 격리하거나 전환(Istio)한다.
잘 설계된 멀티모듈 프로젝트는 하나로 합쳐지거나 잘 나뉠 수 있도록 설계된게 잘 설계된 멀티모듈 프로젝트
질문 -> 중복코드가 생길텐데 어떻게 관리함? 답변 -> core & common 을 안만들고, 만약에 보이면 저장소를 완전히 분리해서 common에 진짜 커먼한게 아니면 코드 작성할 의욕이 안생기게함
질문 -> 여러 개의 모듈을 의존해야하는 요구사항도 있고, 의존성이 복잡해지게 될 수도 있는데, 그런 경우에는 어떻게 구성해야하는가? 답변 -> 멀티모듈이라기보다, DDD의 영역에 가까운거 같은데, 바운디드 컨텍스트에서도 '고객'이라는게 상품을 주문할 떄 고객일수도 있고, 다르게 바라볼 수도 있기 때문에, DDD 영역으로 풀어내는게 올바른 방법. 멀티 모듈로 선언해서 정답을 찾기 보다 바운디드 컨텍스트 특성에 맞게 고민해보아라.