티스토리 뷰

반응형

✏️ 배경

기존 .github/workflows/deploy.yml을 점검하면서 구조적인 문제 세 가지를 발견했다.
이 문제를 해결하기 위해 개선 작업을 진행했고, 그 과정에서 예상치 못한 기존 버그 2개를 추가로 발견해 함께 처리하게 됐다.

  • CI(테스트)와 CD(배포)가 하나의 Job으로 뭉쳐 있음
  • 빌드 시 -x test 옵션으로 테스트를 건너뛴 채 배포
  • PR 시점에 테스트를 강제하는 게이트가 전혀 없음 → 테스트가 깨진 코드도 그냥 머지 가능

 

✏️ 이슈 분리

두 가지 개선 방향은 리스크 성격이 달라서 이슈를 분리하였고 리스크가 낮고 독립적으로 검증 가능한 #385부터 진행하기로 했다.

이슈 내용 상태
#385 PR 시점에 테스트를 자동 실행하는 CI 워크플로우 신설 (ci.yml) 완료
#386 기존 deploy.yml을 빌드/배포 Job으로 분리 미착수

 

✏️ ci.yml 설계 및 구현

 

요구사항 정리

application-test.yml이 실제 MySQL 연결을 요구하는 구조라 Mock 없이 실제 DB가 필요했다.
GitHub Actions의 service container로 MySQL을 띄우는 방식을 선택했다.

고려한 세 가지 포인트:

 

MySQL Readiness 컨테이너가 뜬다고 바로 연결 가능한 게 아니므로 options에 헬스체크를 반드시 설정해야 한다.

options: >-
  --health-cmd="mysqladmin ping -h localhost"
  --health-interval=10s
  --health-timeout=5s
  --health-retries=10

AWS 자격증명 / Slack 토큰 테스트 환경에서는 더미 값으로 충분하다.

JWT 시크릿 처음엔 워크플로우 파일에 더미 값을 하드코딩했는데, 코드 리뷰 과정에서 지적을 받았다.
"하드코딩된 시크릿 패턴은 향후 실수로 실제 값이 들어갈 위험한 선례가 될 수 있다"

실행 시점에 랜덤 생성하는 방식으로 변경했다. HS512 서명 방식은 최소 64바이트 키가 필요하기 때문에 openssl rand -base64 64로 생성한다.

- name: 테스트용 JWT 시크릿 생성
  run: echo "EATSSU_JWT_SECRET_TEST=$(openssl rand -base64 64 | tr -d '\n')" >> "$GITHUB_ENV"

최종 워크플로우 구조

name: EAT-SSU Server PR 테스트 게이트

on:
  pull_request:
    branches: [ "develop", "main" ]

permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: eatssu_test
          MYSQL_ROOT_PASSWORD: test
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping -h localhost"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=10

 

✏️ 로컬 검증 중 발견한 버그 2건

 

ci.yml을 실제로 돌려보기 전에 로컬에서 ./gradlew test를 먼저 돌려봤는데, 관련 없어 보이는 테스트 7개가 깨졌다.

버그 1: Ratings NPE (#387)

원인

Ratings 임베디드 객체 생성 시 평점 값이 null이면 Assert.isTrue(amountRating >= 0 ...) 구문에서 오토언박싱 과정에 NPE가 발생하는 구조였다. ReviewServiceTest의 리뷰 생성/삭제/업데이트 테스트 3개가 이 문제로 실패했다.

 

분석 과정에서 확인한 사실

  • DB 마이그레이션(V1__baseline.sql) 상 amount_rating, main_rating, taste_rating 모두 nullable
  • ReviewCreateRequest, UploadReviewRequest의 편의 생성자가 amount/taste를 null로 남기는 경로 존재
  • ReviewController에 @Valid가 없어 DTO 레벨 검증이 실제로는 실행되지 않음 → Ratings가 사실상 유일한 검증 지점

코드 리뷰 반영 (PR #390)

Gemini Code Assist에서 mainRating이 null이면 검증 없이 통과되는 허점을 추가로 지적했다.

  • Assert.notNull(mainRating, ...) 추가
  • 평점 범위를 0~5에서 1~5로 통일 (코드베이스에서 0값 사용 사례가 없음을 grep으로 확인 후 반영)

버그 2: NicknameValidatorTest (#388)

원인

NicknameValidatorTest에서 닉네임이 숫자로 시작/끝나는 경우 예외가 발생해야 한다고 기대하고 있었는데, 실제로는 통과해서 테스트가 실패했다.

 

분석

처음엔 NicknameValidator의 정규식이 과도하게 허용적인 버그라고 가정했다.
하지만 BaseResponseStatus의 사용자 노출 에러 메시지를 확인해보니

  • INVALID_START_OF_NICKNAME → "첫 글자는 한글, 영문, 숫자만 사용할 수 있습니다."
  • INVALID_END_OF_NICKNAME → "마지막 글자는 한글, 영문, 숫자만 사용할 수 있습니다."

숫자가 시작/끝에 오는 것을 의도적으로 허용한다는 게 명시돼 있었다.
버그는 프로덕션 코드가 아니라 테스트의 기대값이 잘못된 것이었다. 정규식은 건드리지 않고 테스트 케이스를 스펙에 맞게 수정했다.

 

코드 리뷰 반영 (PR #391)

테스트 메서드명이 한글이고 given/when/then 주석이 없다는 지적을 받았다. 이번 PR에서 실제로 수정한 3개 메서드만 영문명 + given 주석으로 변경하고, diff 범위 밖의 기존 한글 메서드명은 손대지 않았다.

✏️ 머지 순서 결정

세 개의 PR을 어떤 순서로 머지할지 트레이드오프를 검토했다.

핵심 고민은 "CI 게이트가 처음 실전에 등장하는 순간을 빨간불로 만들 것인가, 초록불로 만들 것인가" 였다.

버그 수정 PR을 먼저 머지해 develop을 완전히 그린 상태로 만든 뒤, 마지막으로 CI 워크플로우 PR을 머지하는 방향으로 결정했다.

→ #390 머지 (Ratings NPE 수정)
→ #391 머지 (NicknameValidatorTest 수정)
→ develop 최신 상태를 ci.yml 브랜치에 반영
→ CI 재실행 확인
→ #389 머지 (ci.yml 추가)

 

✏️ 브랜치 업데이트 및 최종 검증

#391까지 develop에 머지된 뒤, ci.yml 브랜치가 여전히 두 버그 수정 이전 코드를 기준으로 하고 있어 PR 페이지의 CI 체크가 실패 상태로 남아있었다. 브랜치 자체의 문제가 아니라 develop의 최신 수정사항을 반영하지 못한 것이 원인이었다.
충돌 없이 머지되었고, 푸시 후 CI가 재트리거되어 SUCCESS로 통과했다.

git checkout feat/#385-add-pr-test-ci-workflow
git merge origin/develop
git push origin feat/#385-add-pr-test-ci-workflow

 

✏️ 남은 작업

이슈 내용
#386 deploy.yml을 빌드/배포 Job으로 분리 - 미착수
FIXME Ratings의 amount/taste 평점을 필수값으로 강제할지 선택값으로 둘지 - 비즈니스 요구사항 확정 필요

 

✏️ 회고

 

테스트 게이트 자체가 버그 탐지 도구였다 겉보기엔 무관한 인프라 개선 작업(CI 워크플로우 추가)이 실제로 실행해보기 전까지 드러나지 않던 프로덕션 버그 2개를 표면화시켰다. 테스트를 게이트로 강제하는 행위 자체가 이미 버그 탐지 도구 역할을 한 셈이다.

 

테스트 실패 = 프로덕션 코드 버그가 아닐 수 있다 테스트가 실패했을 때 "정규식이 잘못됐다"고 성급히 결론 내리지 않고, 사용자 노출 메시지·비즈니스 스펙까지 확인한 뒤 방향을 바꾼 것이 중요했다. 테스트 이름과 기대값이 항상 정답은 아니다.

머지 순서도 설계의 일부다 여러 개의 상호 의존적인 변경사항을 다룰 때는 "무엇을 먼저 머지해야 게이트가 깨끗하게 등장하는가"까지 고려한 머지 순서 설계가 필요했다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/07   »
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 31
글 보관함