티스토리 뷰
[Spring Boot] CI/CD 환경에서 Firebase 인증 오류 해결기 (GcpFirestoreAutoConfiguration)
owal_returns 2025. 9. 9. 14:02[Spring Boot] CI/CD 환경에서 Firebase 인증 오류 해결기 (GcpFirestoreAutoConfiguration)
안녕하세요!
오늘은 Spring Boot 프로젝트에 Firebase 의존성을 추가한 뒤, 로컬에서는 잘 동작하던 테스트 코드가
CI/CD 환경(GitHub Actions, Jenkins 등)에서 실패하는 문제와 그 해결 과정을 공유하고자 합니다.
BeanCreationException과 Failed to obtain credentials 메시지로 고통받으셨다면 이 글이 도움이 될 겁니다!
📜 사건 (The Incident)
새로운 기능을 위해 Spring Boot 프로젝트에 spring-cloud-gcp-starter-data-firestore 의존성을 추가했습니다.
로컬 환경에서 API를 개발하고 테스트 코드를 작성했을 때는 모든 것이 완벽하게 동작했습니다.
하지만 코드를 Git에 Push하고 CI/CD 파이프라인이 동작하자, 테스트 단계에서 다음과 같은 낯익은 오류와 함께 빌드가 실패했습니다.
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:655
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:177
Caused by: java.lang.RuntimeException at FirestoreOptions.java:211
Caused by: java.io.IOException at DefaultCredentialsProvider.java:127
분명 로컬에서는 잘 되던 테스트인데, 왜 원격 저장소의 빌드 환경에서는 실패하는 걸까요?
🧐 원인 (The Cause)
원인은 바로 "실행 환경의 차이" 때문이었습니다.
//build.gradle 에서
api 'com.google.firebase:firebase-admin:9.2.0'
위 의존성을 설정하면 springboot 애플리케이션이 시작될 때 auto configuration으로 자동으로 Firebase/GCP 관련 Bean들을 생성하려고 시도합니다.
이 과정에서 인증 정보(Credentials)를 필요로 합니다.
👨💻 로컬 환경: 제 PC에는 gcloud CLI를 통해 로그인 정보가 저장되어 있거나, GOOGLE_APPLICATION_CREDENTIALS 환경 변수에 서비스 계정 키 경로가 설정되어 있었습니다. 자동 설정은 이 정보를 성공적으로 찾아 인증에 사용했습니다.
🤖 CI/CD 환경: CI/CD 서버는 아무것도 설정되지 않은 깨끗한 환경입니다. 인증 정보가 없으니 자동 설정은 인증 정보를 찾으려다 실패하고, 결국 IOException을 발생시켜 Spring ApplicationContext 로딩 자체가 실패하게 된 것입니다.
즉, 테스트 코드조차 실제 Firebase에 연결을 시도하는 것이 문제였습니다. 테스트는 외부 환경에 의존하지 않고 독립적으로 실행되어야 합니다.
✅ 해결 (The Solution)
해결책의 핵심은 "테스트 환경에서는 Firebase 자동 설정을 아예 비활성화하는 것" 입니다. 여러 방법을 시도했지만, 가장 깔끔하고 확실했던 방법은 테스트용 프로필(application-test.yml)을 만들어 자동 설정을 제외(exclude)하는 전략이었습니다.
1. 테스트용 설정 파일 생성
src/test/resources/ 경로에 application-test.yml 파일을 생성하고 아래 내용을 추가합니다.
src/test/resources/application-test.yml
YAML
spring:
autoconfigure:
exclude:
# 이 자동 설정 클래스를 테스트 중에는 로드하지 않도록 지정
- com.google.cloud.spring.autoconfigure.firestore.GcpFirestoreAutoConfiguration
- com.google.cloud.spring.autoconfigure.storage.GcpStorageAutoConfiguration
2. 테스트 클래스에서 test 프로필 활성화
Java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
@SpringBootTest(properties = "spring.config.location="classpath:/application-test.yml")
class MyServiceTest {
// ... 테스트 로직 ...
// 만약 firebase를 필요한 서비스라면 @MockBean으로 가짜 객체를 주입합니다.
// @MockBean
// private FirebaseService service;
}
다른 해결 방법: CI/CD에서 직접 인증 정보 주입하기
제가 선택한 '자동 설정 제외' 방법 외에도, CI/CD 파이프라인에서 직접 인증 정보를 주입하여 테스트를 통과시키는 방법도 있습니다.
이 전략은 테스트 환경에 실제 인증 키를 안전하게 전달하여, 애플리케이션이 로컬 환경에서처럼 인증 정보를 성공적으로 찾도록 만드는 것입니다.
순수 단위 테스트가 아닌, 실제 Firebase DB와 연동이 필요한 통합 테스트(Integration Test)를 CI/CD 환경에서 실행해야 할 때 유용합니다. 단위 테스트라면 이전처럼 자동 설정을 제외하고 Mocking하는 것이 좋습니다.
GitHub Actions 적용 예시
yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# 1. GitHub Secret에 저장된 인증 키 내용을 파일로 생성합니다.
- name: Make Firebase Credentials File
run: echo "${{ secrets.FIREBASE_CREDENTIALS }}" > ${{ github.workspace }}/firebase-key.json
# 2. 생성된 파일의 경로를 환경 변수로 설정합니다.
# GOOGLE_APPLICATION_CREDENTIALS는 ADC가 가장 먼저 확인하는 환경 변수입니다.
- name: Set Google Application Credentials
run: echo "GOOGLE_APPLICATION_CREDENTIALS=${{ github.workspace }}/firebase-key.json" >> $GITHUB_ENV
# 3. 이후 빌드 및 테스트 단계는 위 환경 변수를 인식하여 실행됩니다.
- name: Build with Gradle
run: ./gradlew build
---
📚 부록: Firebase 인증 정보 탐색 우선순위
이번 이슈를 통해 Spring Cloud GCP 라이브러리가 어떻게 인증 정보를 찾는지 명확히 알게 되었습니다. 이 과정은 Application Default Credentials (ADC) 라는 표준 전략을 따릅니다.
우선순위 방법 설명
1. GOOGLE_APPLICATION_CREDENTIALS 환경 변수 서비스 계정 키(.json) 파일의 경로를 직접 지정합니다. 가장 우선순위가 높습니다.
2. 로컬 gcloud CLI 인증 정보 gcloud auth application-default login 명령으로 생성된 개발자의 로컬 인증 정보를 사용합니다.
3. Google Cloud 내장 서비스 계정 GCP(GCE, GKE, Cloud Run 등) 환경에서 제공하는 기본 서비스 계정 정보를 메타데이터 서버를 통해 가져옵니다.
CI/CD 환경은 위 3가지에 모두 해당하지 않기 때문에 오류가 발생했던 것입니다.
이 글을 통해 저와 비슷한 문제를 겪는 분들의 시간이 절약되기를 바랍니다!
'spring' 카테고리의 다른 글
Cloud CDN 연동 시 CORS 문제 해결: Fetch API와 이미지 태그 요청의 차이점 (0) | 2025.09.11 |
---|---|
Spring Boot 스케줄러 잡 중복 실행 방지 구현기 (0) | 2025.09.02 |
🌱 Spring AI vs 🔗 LangChain4j: Java AI 프레임워크 전격 비교 (0) | 2025.08.27 |
Spring MockMvc와 MockMvcTester 비교: 컨트롤러 테스트를 위한 두 가지 접근법 (1) | 2025.08.27 |
Could not find org.ysb33r.gradle:grolifant (0) | 2022.11.09 |
- Total
- Today
- Yesterday
- Firebase 의존성 오류
- ContentCachingRequestWrapper caching # ContentCachingRequestWrapper file upload
- spring test
- asciidoctorExtensions
- pessimistic lock
- Spring MockMvc
- 톰캣 로그파일 자동삭제
- Job Lock
- springboot
- Spring Boot
- mysql dump sql import
- MockMvcTester
- RAG
- GcpFirestoreAutoConfiguration
- ContentCachingRequestWrapper caching error
- teefilter file upload error
- org.asciidoctor.jvm.convert
- url구조
- Controller Testing
- sql import
- asciidoctor sourceDir
- CloudStorage
- springai
- 멀티 인스턴스
- LangChain4j
- Could not find org.ysb33r.gradle:grolifant:0.16.1
- 톰캣 로그파일 자동 삭제
- PreflightRequest
- CloudCDN
- fetchapi
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |