NYO_O

MSA 환경에서 공통 모듈이 필요한 이유 본문

Architecture/MSA

MSA 환경에서 공통 모듈이 필요한 이유

NYO_O 2026. 5. 27. 07:17
반응형

지금까지 다룬 내용들을 통해 인프라 관점에서의 MSA 설계는 어느 정도 마무리가 되었습니다. 이제 개발팀은 모놀리식 통짜 프로젝트를 30개의 마이크로서비스 프로젝트로 물리적으로 분리하는 데 성공했습니다. 각 서비스는 각자의 데이터베이스를 가지며, 필요에 따라 유연하게 스케일 아웃(Scale-out)됩니다.

하지만 물리적인 인프라가 완벽하게 분리되었다고 해서 개발자들의 고통이 끝나는 것은 아닙니다. 오히려 '코드 레벨'에서는 새로운 재앙이 시작됩니다. 클라이언트의 요청이 들어오면 모든 서비스는 공통적으로 사용자의 JWT 토큰을 파싱하고 검증해야 합니다. 또한, 일관된 규격으로 로그를 남겨야 하며, 비즈니스 예외가 발생했을 때 프론트엔드에게 동일한 형태의 JSON 에러 응답 구조를 내려주어야 합니다.

만약 이 공통된 기반 로직들을 30개의 프로젝트에 각자 구현하게 된다면 어떤 일이 벌어질까요?

복사-붙여넣기(Copy & Paste) 주도 개발의 함정

가장 흔하게 발생하는 안티 패턴은 이른바 '복붙(Copy & Paste)' 방식입니다. 처음에 잘 만들어둔 회원 서비스의 JwtValidator.java와 GlobalExceptionHandler.java 파일을 나머지 29개 서비스 프로젝트에 그대로 복사해서 붙여넣습니다. 당장은 로직이 잘 동작하므로 개발 속도가 매우 빠른 것처럼 느껴집니다.

하지만 비즈니스는 끊임없이 변합니다. 보안팀에서 "JWT 토큰 파싱 로직에 새로운 암호화 검증 알고리즘을 추가해 주세요"라는 요구사항을 전달했다고 가정해 보겠습니다. 모놀리식 환경이었다면 단 하나의 파일을 수정하고 한 번 배포하면 끝날 일이었습니다. 그러나 현재 환경에서는 개발자가 30개의 레포지토리를 열어 30개의 JwtValidator.java를 일일이 수정해야 합니다. 30번의 PR(Pull Request)을 올리고, 30번의 리뷰를 거쳐, 30번의 배포 파이프라인을 태워야 합니다.

만약 실수로 단 하나의 서비스에서 수정을 누락한다면, 전체 시스템 중 해당 특정 서비스로 라우팅되는 API들만 간헐적으로 인증 에러를 뱉어내는, 추적하기 매우 까다로운 장애로 직결됩니다.

마이크로서비스의 '독립성'에 대한 치명적인 오해

이쯤 되면 "마이크로서비스는 원래 서로 독립적으로 개발하고 배포하는 아키텍처 아닌가요? 코드를 강하게 묶으면 MSA의 철학에 위배되는 것 아닐까요?"라는 의문이 생길 수 있습니다.

이는 MSA의 '비즈니스 결합도'와 '인프라 및 규약의 결합도'를 혼동해서 생기는 치명적인 오해입니다. 주문 서비스와 배송 서비스는 서로의 비즈니스 로직(도메인)이나 데이터베이스 스키마에 대해서 철저하게 몰라야 하는 것이 맞습니다. 하지만, 시스템 전체를 관통하는 '표준 규약(Cross-Cutting Concerns)'만큼은 하나의 기준으로 강하게 통제되어야 합니다.

결제 서비스의 에러 응답은 {"code": "E001", "message": "잔액 부족"}인데, 장바구니 서비스의 에러 응답은 {"status": 500, "error": "Internal Error"} 형태라면 프론트엔드 개발자는 호출하는 API마다 예외 처리 로직을 다르게 짜야 하는 끔찍한 상황을 마주하게 됩니다. 비즈니스는 완벽히 나뉘어 있더라도, 밖으로 보여지는 시스템은 철저히 통일된 하나의 체계로 움직여야 합니다.

공통 모듈(Common Module)의 도입

파편화된 코드를 통일하고 시스템의 규약을 하나로 묶기 위해 도입하는 개념이 바로 '공통 모듈(Common Module)'입니다.

팀 내에서 합의된 전역 예외 처리(Global Exception Handler), 공통 응답 포맷(Base Response), JWT 인증 필터, 공통 유틸리티 클래스 등을 모아 common-lib이라는 하나의 독립된 라이브러리(Jar) 프로젝트로 분리합니다. 그리고 30개의 마이크로서비스들은 build.gradle이나 pom.xml을 통해 이 공통 모듈을 의존성(Dependency)으로 주입받아 사용하게 됩니다.

공통 모듈을 도입하면 앞서 언급한 보안 알고리즘 추가 요구사항이 들어와도 두렵지 않습니다. 개발자는 오직 common-lib 프로젝트의 코드만 수정하여 새로운 버전(예: v1.1.0)을 사내 저장소에 배포하면 됩니다. 나머지 30개 마이크로서비스는 다음번 배포 시점에 자연스럽게 공통 모듈의 버전만 갱신하여 빌드하면, 시스템 전체에 변경 사항이 누락 없이 일관되게 적용됩니다.

마무리

오늘은 Part 2 'MSA 실무 편'의 첫 시작으로, 아키텍처 분리 이후 필연적으로 마주하게 되는 코드 중복 문제와 이를 해결하기 위한 공통 모듈의 핵심 가치에 대해 알아보았습니다. 공통 모듈은 단순한 코드 재사용을 넘어, 분산된 시스템에 동일한 '팀의 기준과 규약'을 강제하는 가장 확실하고 강력한 통제 수단입니다.

하지만 공통 모듈은 양날의 검과 같습니다. "이 비즈니스 코드도 다른 곳에서 공통으로 쓰일 것 같은데?"라며 도메인 로직을 하나둘씩 공통 모듈에 밀어 넣다 보면, 공통 모듈 자체가 엄청나게 무겁고 변경하기 두려운 또 다른 거대한 모놀리식이 되어버립니다. 최악의 경우, 공통 모듈의 유틸리티 코드 한 줄을 수정했더니 30개의 서비스가 전부 컴파일 에러를 뿜어내는 '스파게티 의존성 지옥'이 펼쳐질 수도 있습니다.

그렇다면 우리는 이 위험을 피하기 위해 '공통 모듈에 정확히 어떤 내용만 넣고, 어떤 내용을 빼야 할까요?' 다음 포스팅에서는 공통 모듈이 비대해지는 것을 원천적으로 차단하기 위한 엄격한 설계 원칙과, 반드시 포함되어야 할 필수 구성 요소들에 대해 상세히 다루어 보겠습니다.

반응형