| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- springboot
- ci/cd
- 백엔드
- 마이그레이션
- GitHub Packages
- 도커
- SpringCloud
- Flyway
- PostgreSQL
- 자바
- Database
- 아키텍처
- 마이크로서비스아키텍처
- Java 8
- 분산시스템
- github actions
- CS
- 마이크로서비스
- MSA
- docker
- 멀티모듈
- 백엔드면접준비
- 공통모듈
- gradle
- java
- dockercompose
- 컨테이너
- 인프라
- GCP
- 트러블슈팅
- Today
- Total
NYO_O
공통 모듈 의존성 주입 및 연동 가이드 본문
지난 시간, 공통 모듈을 GitHub Packages에 배포하고 GitHub Actions로 자동화 파이프라인을 구성하는 방법을 살펴보았습니다. 이제 패키지 레지스트리에는 우리가 만든 공통 모듈이 버전별로 잘 쌓여 있는 상태입니다.
2026.05.27 - [Tech/MSA] - GitHub Packages를 활용한 공통 모듈 배포 파이프라인 구축
GitHub Packages를 활용한 공통 모듈 배포 파이프라인 구축
지난 시간, 공통 모듈에 어떤 내용을 담아야 하는지, 그리고 포함해서는 안 되는 것들은 무엇인지 그 설계 기준을 살펴보았습니다. 비즈니스 로직을 배제하고, 서비스들이 함께 공유하는 기술적
ddangnyo.tistory.com
그런데 막상 여기까지 오고 나면 새로운 고민이 생깁니다.
"배포된 공통 모듈을, 각 도메인 서비스에서 어떻게 연동해야 할까?"
단순히 implementation으로 선언하는 것만으로는 부족합니다. Spring Boot의 자동 설정(Auto-configuration) 메커니즘을 이해하고, 공통 모듈이 각 서비스에 자연스럽게 녹아들 수 있도록 구조를 잡아주는 작업이 필요합니다. 오늘은 의존성 주입부터 자동 설정 연동까지 전체 과정을 정리해 보겠습니다.
전체 연동 흐름 이해
설정을 하나씩 살펴보기 전에 전체 그림을 먼저 확인하면 이해가 쉽습니다.
[GitHub Packages]
└─ common-module-x.x.x.jar
│
▼
[도메인 서비스 - build.gradle]
└─ 저장소 선언 + 의존성 선언
│
▼
[Spring Boot Auto-configuration]
└─ AutoConfiguration.imports 인식
│
▼
[도메인 서비스 ApplicationContext]
└─ 공통 모듈의 빈(Bean)이 자동으로 등록되어 사용 가능
공통 모듈에 자동 설정을 구성해두면, 도메인 서비스 개발자는 별도의 @ComponentScan 설정 없이 의존성 선언만으로 공통 모듈의 기능을 바로 사용할 수 있습니다.
1단계: 도메인 서비스의 build.gradle 설정
먼저 공통 모듈이 올라가 있는 GitHub Packages 저장소를 도메인 서비스의 repositories 블록에 추가해 줍니다.
// build.gradle
repositories {
mavenCentral()
maven {
name = 'GitHubPackages'
url = uri('https://maven.pkg.github.com/YOUR_GITHUB_USERNAME/YOUR_REPO_NAME')
credentials {
username = System.getenv('GITHUB_ACTOR') ?: project.findProperty('gpr.user')
password = System.getenv('GITHUB_TOKEN') ?: project.findProperty('gpr.key')
}
}
}
dependencies {
implementation 'com.example:common-module:1.0.0'
// 버전 업그레이드 시 버전 번호만 변경하면 됩니다
}
로컬 개발 환경에서는 ~/.gradle/gradle.properties에 아래처럼 인증 정보를 등록해 두는 방식을 사용합니다.
# ~/.gradle/gradle.properties (로컬 환경 전용 - 절대 커밋하지 않는다)
gpr.user=YOUR_GITHUB_USERNAME
gpr.key=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN
이 파일이 .gitignore에 포함되어 있는지 꼭 확인하는 것이 좋습니다. 실수로 커밋되면 토큰이 노출됩니다.
2단계: 공통 모듈에 Auto-configuration 구성하기
의존성 선언만으로는 공통 모듈의 빈(Bean)이 도메인 서비스에 자동으로 등록되지 않습니다. Spring Boot는 기본적으로 자신의 패키지 하위만 컴포넌트 스캔 대상으로 삼기 때문에, 외부 라이브러리의 빈은 별도의 자동 설정 파일을 통해 ApplicationContext에 등록해줘야 합니다.
Spring Boot 3.x 기준으로 자동 설정 등록 방식이 변경되었습니다. 처음에 이 변경 사항을 모르고 기존 방식(spring.factories)만 적용하면 빈이 제대로 등록되지 않아 꽤 당황스러울 수 있습니다.
Spring Boot 버전별 자동 설정 등록 방식
Spring Boot 버전 등록 파일
| 2.x | META-INF/spring.factories |
| 3.x | META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |
Spring Boot 3.x에서는 하위 호환성을 위해 두 파일을 모두 작성해 두는 것이 좋습니다.
AutoConfiguration 클래스 작성
공통 모듈의 src/main/java 아래에 자동 설정 클래스를 작성합니다.
// com/example/common/config/CommonModuleAutoConfiguration.java
package com.example.common.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@AutoConfiguration
@ComponentScan(basePackages = "com.example.common")
public class CommonModuleAutoConfiguration {
// 공통 모듈의 빈들을 명시적으로 등록하거나,
// @ComponentScan으로 패키지 전체를 스캔하도록 구성합니다
}
특정 빈만 선택적으로 등록하고 싶다면 @ComponentScan 대신 @Bean 메서드를 직접 작성하는 방식도 활용할 수 있습니다.
@AutoConfiguration
public class CommonModuleAutoConfiguration {
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
@Bean
public CommonResponseAdvice commonResponseAdvice() {
return new CommonResponseAdvice();
}
}
자동 설정 파일 등록
작성한 AutoConfiguration 클래스를 Spring Boot가 인식할 수 있도록 리소스 파일을 추가해 줍니다.
Spring Boot 3.x용
# src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.common.config.CommonModuleAutoConfiguration
Spring Boot 2.x 하위 호환용
# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.common.config.CommonModuleAutoConfiguration
두 파일을 모두 작성해두면 Spring Boot 버전에 관계없이 자동 설정이 동작합니다.
3단계: 조건부 빈 등록으로 유연성 확보하기
공통 모듈의 기능을 모든 도메인 서비스가 동일하게 사용할 수는 없습니다. 예를 들어 특정 서비스는 이미 자체적인 예외 처리 로직을 갖고 있어서 공통 모듈의 전역 예외 처리가 충돌할 수 있습니다. 이런 상황에서는 @ConditionalOn* 어노테이션을 활용해 조건부로 빈을 등록하는 것이 좋습니다.
@ConditionalOnMissingBean 활용
이미 동일한 타입의 빈이 등록되어 있다면 공통 모듈의 빈을 등록하지 않도록 구성할 수 있습니다.
@AutoConfiguration
public class CommonModuleAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GlobalExceptionHandler.class)
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
}
이렇게 구성해두면, 도메인 서비스에서 GlobalExceptionHandler를 직접 정의한 경우 공통 모듈의 빈은 등록되지 않습니다. 반대로 따로 정의하지 않은 서비스는 공통 모듈의 기본 빈을 그대로 사용하게 됩니다.
@ConditionalOnProperty 활용
프로퍼티 값을 기준으로 기능을 활성화하거나 비활성화하는 방법도 있습니다.
@Bean
@ConditionalOnProperty(
name = "common.exception-handler.enabled",
havingValue = "true",
matchIfMissing = true // 프로퍼티가 없으면 기본적으로 활성화
)
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
도메인 서비스의 application.yml에서 아래처럼 선택적으로 비활성화할 수 있습니다.
# 공통 모듈 예외 처리가 필요 없는 서비스에서
common:
exception-handler:
enabled: false
이 방식을 활용하면 각 도메인 서비스가 필요한 기능만 골라서 사용하는 것이 가능해집니다.
4단계: 공통 모듈 의존성 주입 확인
연동 설정이 완료되면 도메인 서비스를 실행해서 빈이 정상적으로 등록되었는지 확인해보는 것이 좋습니다.
기동 로그로 확인하기
Spring Boot 기동 시 DEBUG 레벨 로그에서 빈 등록 여부를 확인할 수 있습니다.
# application.yml
logging:
level:
org.springframework.boot.autoconfigure: DEBUG
로그에서 아래와 같은 메시지가 출력되면 자동 설정이 정상적으로 동작한 것입니다.
CommonModuleAutoConfiguration matched:
- @ConditionalOnMissingBean (types: GlobalExceptionHandler) did not find any beans
반대로 아래처럼 출력된다면 이미 동일한 타입의 빈이 등록되어 있어 공통 모듈의 빈이 생략된 상황입니다.
CommonModuleAutoConfiguration:
Did not match:
- @ConditionalOnMissingBean (types: GlobalExceptionHandler) found beans 'customExceptionHandler'
테스트 코드로 확인하기
간단한 통합 테스트를 작성해서 빈이 제대로 주입되는지 검증해두는 것도 권장합니다.
@SpringBootTest
class CommonModuleIntegrationTest {
@Autowired(required = false)
private GlobalExceptionHandler globalExceptionHandler;
@Test
void 공통_모듈의_예외_핸들러가_정상적으로_주입된다() {
assertThat(globalExceptionHandler).isNotNull();
}
}
5단계: 도메인 서비스 CI/CD에서 인증 처리
도메인 서비스도 GitHub Actions로 빌드한다면, 워크플로우에서 GitHub Packages 인증을 함께 처리해줘야 합니다.
# 도메인 서비스의 .github/workflows/build.yml
- name: Build
run: ./gradlew clean build
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR와 GITHUB_TOKEN을 환경 변수로 주입하면, 빌드 시 GitHub Packages에서 공통 모듈을 자동으로 받아옵니다.
주의: 공통 모듈 저장소와 도메인 서비스 저장소가 동일한 GitHub 조직(Organization) 또는 동일한 계정 아래에 있어야 기본 GITHUB_TOKEN으로 접근이 됩니다. 서로 다른 계정에 걸쳐 있다면 read:packages 권한으로 발급한 별도의 PAT을 Repository Secret으로 등록해줘야 합니다.
연동 과정에서 자주 마주치는 문제들
실제로 연동을 진행하다 보면 몇 가지 상황에서 막히는 경우가 있습니다.
빈이 등록되지 않는 경우
AutoConfiguration.imports 파일 경로가 정확한지 확인해보는 것이 좋습니다. 오타나 경로 오류로 인식이 안 되는 경우가 꽤 있습니다.
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
파일 위치가 맞더라도 JAR 내부에 제대로 포함되었는지 확인해볼 수 있습니다.
jar tf common-module-1.0.0.jar | grep META-INF
패키지 충돌 문제
공통 모듈과 도메인 서비스의 패키지 최상위 경로가 같으면 @ComponentScan이 중복 스캔을 유발할 수 있습니다. 공통 모듈의 패키지는 com.example.common처럼 별도 네임스페이스로 분리해두는 것을 권장합니다.
순환 의존성 문제
공통 모듈이 도메인 서비스의 클래스를 참조하거나, 설정 순서가 꼬이면 순환 의존성 오류가 발생할 수 있습니다. 공통 모듈은 어디에도 의존하지 않는 순수한 라이브러리로 유지하는 것이 이런 문제를 예방하는 가장 좋은 방법입니다.
마무리
오늘은 도메인 서비스에서 공통 모듈을 의존성으로 주입하고 Spring Boot Auto-configuration을 통해 연동하는 전체 과정을 살펴보았습니다. build.gradle 설정부터 AutoConfiguration.imports 등록, 조건부 빈 구성까지 단계적으로 구성해두면 도메인 서비스 개발자가 별도의 설정 없이 공통 기능을 바로 활용할 수 있는 구조가 만들어집니다.
다음 시간에는 이렇게 연동된 공통 모듈을 실제로 활용하는 첫 번째 사례로, 여러 서비스에서 일관된 형태로 동작하는 전역 예외 처리 로직을 공통 모듈에 구축하는 방법을 다루어 보겠습니다.
'Architecture > MSA' 카테고리의 다른 글
| 공통 모듈 적용 - 일관된 전역 예외 처리 로직 구축 (0) | 2026.05.27 |
|---|---|
| 공통 모듈의 역할별 분리 전략 (0) | 2026.05.27 |
| GitHub Packages를 활용한 공통 모듈 배포 파이프라인 구축 (0) | 2026.05.27 |
| 공통 모듈, 무엇을 담고 무엇을 빼야할까? (1) | 2026.05.27 |
| MSA 환경에서 공통 모듈이 필요한 이유 (0) | 2026.05.27 |