모든 글

너무 이른 멀티모듈은 설계를 더 어렵게 만든다

모듈은 이해하지 못한 도메인이나 아키텍처 그림을 미리 고정하는 장치가 아니라 구현에서 발견한 경계를 강제하는 수단이어야 합니다.

출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다. gpt-5.6-sol 모델을 사용해 생성·편집했습니다.

“멀티모듈을 쓰지 말자”는 일부러 강하게 붙인 제목이지 실제 원칙은 아닙니다. 저도 모듈을 사용하고 자주 설명합니다. 문제는 소프트웨어가 무엇을 하는지 충분히 알기 전에 멀티모듈 구성을 아키텍처의 증거처럼 다루는 태도입니다.

모듈은 무언가를 격리하고 의존성에 제약을 걸 때 쓸모가 있습니다. 좋은 모듈 경계는 응집을 강화하고 설계 의도를 보여줍니다. 잘못된 경계는 추측을 고정하고 관련 코드를 흩뜨리며, 빌드 파일만 늘어난 의존성 문제를 다시 만듭니다.

첫인상을 도메인 모듈로 만들지 않기

학교 소프트웨어를 만들어 달라는 요청을 받았다고 해보겠습니다. 처음에는 학교에서 눈에 보이는 평범한 명사부터 떠오릅니다. 책, 책상, 사물함, 교실, 교복 같은 것들입니다. 동작을 만들기도 전에 이 명사로 모듈을 생성하고 싶어질 수 있습니다.

그것은 도메인 이해가 아니라 물건 목록입니다.

의미 있는 경계는 입학, 출결, 수업, 평가일 수 있고, 해당 학교의 운영 방식에만 있는 무엇일 수도 있습니다. 처음에는 학교 전체가 하나의 도메인일 수도 있습니다. ’학교’라는 단어만으로는 알 수 없습니다. 제품을 오래 운영한 사람이나, 실제 업무를 깊이 아는 기획자 또는 도메인 전문가가 있다면 훨씬 나은 모델을 만들 수 있습니다. 운영 경험의 예로 2년 정도를 말할 수는 있지만 2년이 지나면 자동으로 올바른 설계가 생긴다는 규칙은 아닙니다.

그 이해가 생기기 전에는 구현에서 시작하는 편이 낫습니다. 관련 코드를 패키지에 두고 실제 유즈케이스를 만들면서 어떤 개념이 같이 변하는지 봅니다. 도메인이 미성숙할 때 패키지는 변경 여지를 남기지만, 이른 모듈 분리는 추측한 경계를 굳히고 모듈 의존성을 얽히게 할 수 있습니다.

순서가 중요합니다. 모듈은 좋은 구현이 분리된 채 유지되도록 도울 수 있습니다. 모듈이 존재한다는 이유만으로 구현이 좋아지지는 않습니다.

기술로 가득한 도메인 모듈은 경계를 의심해야 한다

각 도메인 모듈 안에 Spring 설정, Redis 클라이언트, Kafka 연동, 영속성 세부사항과 여러 인프라 코드가 전부 들어가 있다면 또 다른 경고가 보입니다. 도메인 모듈에 기술 코드가 있으면 무조건 틀렸다는 뜻은 아닙니다. 이 분리가 무엇을 지키는지가 질문입니다.

book, desk, locker 모듈이 모두 같은 기술 의존성을 들고 있고 서로에게도 의존한다면 도메인을 격리한 것이 아닙니다. 얽힌 구현을 작은 통으로 잘랐을 뿐입니다. 곧 deskbook에 의존하고, lockerbook을 보며, 다시 book이 둘 다 참조합니다. 의존성 그래프가 의도보다 이해 부족을 보여주게 됩니다.

모듈의 순환 관계는 멈춰서 볼 강한 신호입니다. 명확히 감수한 트레이드오프일 수 있지만 보통은 개념이나 소유권을 잘못 나눴을 가능성이 큽니다. Gradle 설정을 더한다고 비즈니스 경계가 정리되지는 않습니다.

이런 상황이라면 더 큰 패키지 경계로 코드를 합치고 구현을 개선한 뒤 기다리는 편을 택할 수 있습니다. 책임이 선명해진 다음에는 추출이 쉬워집니다. 근거 없이 만든 벽은 수정할 때마다 넘어야 합니다.

아키텍처는 모듈 그림일 필요가 없다

모듈 트리가 아키텍처 그림을 그대로 따라야 한다는 습관에도 동의하지 않습니다.

레이어드 아키텍처를 쓴다고 presentation, business, data-access 모듈이 각각 필요하지는 않습니다. 헥사고날 아키텍처라고 포트마다 모듈 하나, 어댑터마다 모듈 하나를 만들 필요도 없습니다. 실제 의존성을 강제할 필요가 있다면 모듈로 구현할 수 있지만, 그 물리 구조가 있어야 아키텍처가 성립하는 것은 아닙니다.

아키텍처는 구현 안의 역할과 흐름을 설명합니다. 모듈은 접근을 강제할 수 있는 물리 경계를 제공합니다. 서로 도울 수는 있지만 같은 것이 아닙니다.

패키지도 마찬가지입니다. 패키지 트리가 아키텍처를 쉽게 보여줄 수 있고 팀이 그런 규칙을 선택할 수도 있습니다. 그렇다고 아키텍처의 필수 조건은 아닙니다. 레이어 이름이 붙은 패키지를 지웠을 때 설계도 함께 사라진다면 설계가 이름에만 있었는지 의심해야 합니다.

저는 가장 격리하고 싶은 곳, 우발적인 의존성의 비용이 큰 곳, 빌드가 변경자에게 한 번 더 생각하게 만들어야 하는 곳에서 모듈을 추출합니다. 그림의 모든 상자를 재현하려고 만들지는 않습니다.

중요한 경계는 여러 크기로 생긴다

소프트웨어에는 하나의 ’코어’만 있고 나머지는 전혀 중요하지 않은 경우가 드뭅니다. 핵심 제품 안에도 중요한 개념이 있습니다. 어드민 영역이 제품의 중심 도메인은 아니더라도 자체 정책과 책임을 가집니다. 동작이 쌓이면 각 영역 안에서도 더 작은 경계가 나타납니다.

중첩된 모든 개념을 모듈로 만들어야 한다는 뜻은 아닙니다. 중요성과 응집이 여러 크기로 존재한다는 뜻입니다. 작은 경계는 클래스, 패키지, 테스트, 인터페이스나 신중한 이름만으로도 표현할 수 있습니다. 모듈은 더 강한 수단이므로 더 강한 필요에 답해야 합니다.

제 예제 프로젝트에서는 제품 의도를 알고 있었지만 구현이 Q&A나 리뷰 도메인 모듈로 나눌 만큼 성숙하지 않았기 때문에 넓은 도메인 모듈을 유지했습니다. 기술 의존성은 그 영역 밖에 두었고, 협업자가 알아차리길 바라는 경계였기 때문에 어드민 영역은 격리했습니다. 이것은 범용 템플릿이 아닙니다. 알고 있는 의도에는 모듈을 쓰고 불확실한 구분은 유연하게 남긴 사례입니다.

구현 우선은 아무렇게나 짜자는 말이 아니다

’구현이 먼저’라는 말을 일단 엉망으로 만든 뒤 나중에 고치겠다는 허락으로 받아들일 수 있습니다. 제가 말하는 뜻은 다릅니다.

가장 단순하면서도 응집된 구현을 만드십시오. 책임을 읽을 수 있게 하고, 함께 바뀌는 코드를 가까이 두고, 중요한 동작을 테스트합니다. 불필요한 의존성도 피합니다. 실제 기능, 변경과 운영에서 얻은 근거를 이용해 어느 경계에 더 강한 제약이 필요한지 결정합니다.

모듈을 추출하려면 적어도 이런 질문에 답할 수 있어야 합니다.

  • 다른 영역이 무엇을 사용하지 못하게 해야 하는가?
  • 같은 변경 이유와 주기를 가진 코드는 무엇인가?
  • 어떤 기술을 구현 세부사항으로 숨겨야 하는가?
  • 빌드가 어느 의존성 방향을 강제해야 하는가?
  • 이 경계를 누가 이해하고 유지할 것인가?

답이 “멀티모듈 아키텍처를 쓰기 위해서”뿐이라면 지금은 패키지로 두는 편이 낫습니다.

모듈은 성취 배지가 아니라 제약이고, 제약에는 비용이 있습니다. 구현을 통해 무엇을 지켜야 하는지 보인 뒤 적용하십시오. 그래야 모듈이 초기 추측을 영구 구조로 만드는 대신 팀이 얻은 지식을 기록합니다.