Spring Boot CI 트러블슈팅 - GitHub Actions에서 Test 실패 해결기

wjd15sheep·2025년 8월 4일
0

프로젝트

목록 보기
10/10

K-FreeMarket 프로젝트의 서버(Spring Boot)에서 GitHub Actions를 활용해 Docker Hub에 이미지를 자동 배포하는 CI를 구성하던 중 문제가 발생했습니다.
이번 포스트에서는 테스트 단계에서 발생한 오류와 이를 어떻게 해결했는지 정리해보려 합니다.

❗ 문제: 테스트 단계 실패

CI 도중 contextLoads() 테스트에서 아래와 같이 실패했습니다.

원인 분석

  • 환경변수 미인식: 테스트 코드 실행 시 필요한 환경변수를 찾을 수 없음
  • DB 연결 실패: MySQL DB에 접속하지 못함

🛠️ 문제 해결 과정

1️⃣ 테스트 환경에서 환경변수 주입하기

처음에는 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에서도 문제 없습니다.


2️⃣ 테스트 환경에서 MySQL 연결하기

환경변수를 해결하니, 이제는 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
  • 테스트용 DB 이름은 test로 지정
  • env.properties에서도 같은 DB명(test)을 사용하여 일치시킴
  • Health check를 통해 MySQL 준비 완료 후 테스트가 실행되도록 설정

결과: MySQL 연결 성공, 테스트 통과!

최종적인 gitAction gradle.yml

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에 대한 이해도가 훨씬 높아졌고,
이 경험을 바탕으로 앞으로 더 안정적이고 유연한 배포 환경을 설계할 수 있을 것 같습니다.

계속 성장하는 개발자가 되겠습니다!


[참고]

profile
성장 위해 노력하는 웹 개발자 주니어

0개의 댓글