K-FreeMarket 프로젝트의 서버(Spring Boot)에서 GitHub Actions를 활용해 Docker Hub에 이미지를 자동 배포하는 CI를 구성하던 중 문제가 발생했습니다.
이번 포스트에서는 테스트 단계에서 발생한 오류와 이를 어떻게 해결했는지 정리해보려 합니다.
CI 도중 contextLoads() 테스트에서 아래와 같이 실패했습니다.
원인 분석
처음에는 GitHub 레포지토리의 Settings > Secrets and variables에서 다음과 같이 환경변수를 등록했습니다.
env:
JWT_SECRETKEY: ${{ secrets.JWT_SECRETKEY }}
DB_URL: ${{ secrets.DB_URL }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
...
그러나 문제는 해결되지 않았습니다.
📌 이유: 테스트 환경은 실행 시점에 격리되어 있어, 일반적인 환경변수 주입만으로는 테스트 코드에서 해당 값을 인식하지 못했습니다.
💡 해결 방법: env.properties 파일 직접 생성
테스트용 env.properties 파일을 직접 만들어 환경변수를 주입했습니다.
# test를 위한 env.properites 파일 생성하기
- name: Create env.properties file for tests
run: |
mkdir -p src/test/resources/properties
echo "jwt.secretKey=${{ secrets.JWT_SECRETKEY }}" > src/test/resources/properties/env.properties
echo "db.url=jdbc:mysql://127.0.0.1:3306/test" >> src/test/resources/properties/env.properties
echo "db.user=${{ secrets.DB_USER }}" >> src/test/resources/properties/env.properties
echo "db.password=${{ secrets.DB_PASSWORD }}" >> src/test/resources/properties/env.properties
echo "naver.client.id=${{ secrets.NAVER_CLIENT_ID }}" >> src/test/resources/properties/env.properties
echo "naver.client.secret=${{ secrets.NAVER_CLIENT_SECRET }}" >> src/test/resources/properties/env.properties
그리고 테스트 코드에서 해당 파일을 명시적으로 불러오도록 설정했습니다:
@SpringBootTest
@ActiveProfiles("test")
@TestPropertySource("classpath:properties/env.properties")
class ReemarketServerApplicationTests {
@Test
void contextLoads() {
}
}
결과: 테스트 실행 시 필요한 설정값이 정상적으로 주입되어, 환경변수 관련 오류는 해결되었습니다.
src/test/properties/env.properties 파일 생성 및 env 값을 넣어주시면 Local에서도 문제 없습니다.
환경변수를 해결하니, 이제는 DB 연결 오류가 발생했습니다.
💡 해결 방법: MySQL Docker 컨테이너 실행
테스트용으로 사용할 MySQL을 GitHub Actions의 services 옵션을 통해 실행시켰습니다.
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: ${{ secrets.DB_PASSWORD }}
MYSQL_DATABASE: test
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping --silent" --health-interval=10s --health-timeout=5s --health-retries=5
그리고 applicant-test.yml에서도 DB_URL를 test로 명시 해줍니다.
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
username: ${DB_USER}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
결과: MySQL 연결 성공, 테스트 통과!

name: Java CI with Gradle
# master 브랜치에 push, PR 이벤트 발생시 동작.
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: ${{ secrets.DB_PASSWORD }}
MYSQL_DATABASE: test
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping --silent" --health-interval=10s --health-timeout=5s --health-retries=5
env:
JWT_SECRETKEY: ${{ secrets.JWT_SECRETKEY }}
DB_URL: ${{ secrets.DB_URL }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }}
NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }}
NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }}
steps:
# (1) 프로그램 빌드 (Java 빌드) 1) Java 및 Docekr 빌드를 위한 환경 설정
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Create env.properties file for tests
run: |
mkdir -p src/test/resources/properties
echo "jwt.secretKey=${{ secrets.JWT_SECRETKEY }}" > src/test/resources/properties/env.properties
echo "db.url=jdbc:mysql://127.0.0.1:3306/test" >> src/test/resources/properties/env.properties
echo "db.user=${{ secrets.DB_USER }}" >> src/test/resources/properties/env.properties
echo "db.password=${{ secrets.DB_PASSWORD }}" >> src/test/resources/properties/env.properties
echo "naver.client.id=${{ secrets.NAVER_CLIENT_ID }}" >> src/test/resources/properties/env.properties
echo "naver.client.secret=${{ secrets.NAVER_CLIENT_SECRET }}" >> src/test/resources/properties/env.properties
# (1) 프로그램 빌드 (Java 빌드) 2) Java 빌드를 위한 ./gradlew 파일 권한 변경
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew
# (1) 프로그램 빌드 (Java 빌드) 3) Java 빌드
- name: Spring Boot Build
run: ./gradlew clean build --info
# (2) Docker 이미지 빌드 1) DockerFile 을 기반으로 Docekr Image 빌드
- name: docker image build
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/k-freemarket-demo .
# (2) Docker 이미지 빌드 2) Docker Hub 에 Login
- name: docker login
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PW }}
# (2) Docker 이미지 빌드 3) Docker Hub 에 빌드된 이미지 push
- name: docker Hub push
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/k-freemarket-demo
이전에는 CI/CD 구성 시 환경변수나 DB 연결을 따로 고려하지 않아도 될 만큼 단순한 구조였지만,
이번 프로젝트에서는 OAuth, JWT, DB 등 다양한 환경 설정이 포함되다 보니 처음으로 CI 환경의 격리성에 대해 깊이 고민하게 되었습니다.
해결 방법을 찾아가며 CI/CD에 대한 이해도가 훨씬 높아졌고,
이 경험을 바탕으로 앞으로 더 안정적이고 유연한 배포 환경을 설계할 수 있을 것 같습니다.
계속 성장하는 개발자가 되겠습니다!
[참고]