화요일

GitHub Actions secrets 환경변수 비어있음 해결 — secret not available 원인 분석

🔍 검색 키워드: GitHub Actions secrets undefined, GitHub Actions 환경변수 비어있음, secrets not available 해결, GitHub Actions secret 적용 안 됨, CI 시크릿 설정, GitHub Actions secret empty

증상: 분명히 설정했는데 값이 없다고 한다

GitHub Actions 워크플로우는 돌리면 이런 상황이 생긴다.

Error: API_KEY is undefined
Error: Cannot read properties of undefined (reading 'length')

아니면 시크릿 값이 빈 문자열로 들어오거나, 더 황당하게는 배포가 그냥 조용히 실패한다. Secrets 탭에서 분명히 등록했는데 워크플로우가 못 읽는다.

CI 처음 세팅할 때, 또는 레포를 포크하거나 환경(Environment)을 새로 만들었을 때 이 문제를 자주 만난다.

원인 분류

1. 시크릿 이름 대소문자 불일치

가장 흔한 실수. Secrets UI에서 API_KEY로 등록했는데 워크플로우에서 ${{ secrets.api_key }}로 참조하면 빈 값이 온다. 시크릿은 대소문자 구분한다.

# 잘못된 예
env:
  API_KEY: ${{ secrets.api_key }}  # 실제 이름이 API_KEY면 못 읽음

# 올바른 예
env:
  API_KEY: ${{ secrets.API_KEY }}

2. Environment 시크릿인데 job에 environment 지정 안 함

GitHub에서 환경(Environment)을 별도로 만들어서 거기에 시크릿을 등록했다면, job에서 그 environment를 명시해야 한다. 안 하면 해당 시크릿은 아예 조회 자체가 안 된다.

# 잘못된 예 — environment 지정 없이 env 시크릿 접근 시도
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - run: echo ${{ secrets.PROD_DB_URL }}  # production environment의 시크릿이면 빈 값

# 올바른 예
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production  # ← 이게 있어야 environment 시크릿 접근 가능
    steps:
      - run: echo ${{ secrets.PROD_DB_URL }}

3. Fork PR에서는 시크릿 접근이 차단된다

외부 기여자가 fork해서 올린 PR의 워크플로우는 보안 정책상 시크릿에 접근할 수 없다. pull_request 이벤트 트리거를 쓰면 이 제한이 적용된다.

Warning: Context access might be invalid: secrets

의도적인 제한이다. 악의적인 코드가 PR로 들어와서 시크릿을 탈취하는 걸 막기 위한 것.

4. 시크릿 참조 문법 오류

# 틀린 문법들
${{ secret.API_KEY }}       # secrets가 아니라 secret (오타)
${{ secrets[API_KEY] }}     # 대괄호 안에 따옴표 필요
${{ env.secrets.API_KEY }}  # env와 secrets 혼용

# 올바른 문법
${{ secrets.API_KEY }}
${{ secrets[env.SECRET_NAME] }}  # 동적 키 참조는 이렇게

해결 방법

Step 1. 시크릿 이름 확인

# GitHub CLI로 현재 등록된 시크릿 목록 확인
gh secret list

# environment 시크릿 확인
gh secret list --env production

워크플로우 YAML의 이름과 정확히 일치하� 체크.

Step 2. 시크릿이 실제로 전달되는지 테스트

값을 직접 출력하면 안 된다 (마스킹됨). 대신�길이나 존재 여부를 확인:

steps:
  - name: Check secrets
    run: |
      if [ -z "${{ secrets.API_KEY }}" ]; then
        echo "API_KEY is EMPTY"
      else
        echo "API_KEY is set (length: ${#API_KEY})"
      fi
    env:
      API_KEY: ${{ secrets.API_KEY }}

Step 3. Environment 시크릿 설정

레포 Settings → Environments → 환경 선택 → Environment secrets에서 등록했다면:

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production   # 반드시 명시
    steps:
      - name: Deploy
        env:
          DB_URL: ${{ secrets.DB_URL }}
          API_KEY: ${{ secrets.API_KEY }}
        run: ./deploy.sh

environment 이름은 Environments 탭에 있는 이름과 정확히 일치해야 한다.

Step 4. Fork PR 시크릿 접근 처리

내부 PR이라면 pull_request_target 이벤트 사용을 고려할 수 있다. 단, pull_request_target은 base 브랜치의 코드를 실행하므로 PR 코드를 checkout해서 실행하면 보안 취약점이 된다.

# 안전한 패턴 — PR 코드는 테스트만 하고, 배포는 main 병합 후에
on:
  pull_request:
    # 시크릿 없이 테스트만
  push:
    branches: [main]
    # 여기서 시크릿 써서 배포

Step 5. Organization 시크릿 접근 권한

조직(organization) 레벨 시크릿은 레포별로 접근 권한이 따로 있다. 조직 Settings → Secrets → 해당 시크릿 → Repository access에서 해당 레포가 포함되어 있는지 확인.

상황별 체크리스트

증상원인조치
시크릿 값이 빈 문자열이름 대소문자 불일치gh secret list로 정확한 이름 확인
environment 시크릿 못 읽음job에 environment 미지정environment: 필드 추가
Fork PR에서만 실패보안 정책으로 차단시크릿 없이 동작하도록 CI 설계 변경
조직 시크릿 못 읽음레포 접근 권한 없음Organization 설정에서 레포 추가
로컬에서는 되는데 CI에서만환경변수 주입 누락env: 블록에서 명시적 주입 확인

자주 하는 실수 — 디버깅 시 값 출력하려다 마스킹에 막히는 경우

# 이렇게 하면 *** 로 마스킹되어 아무 의미 없음
- run: echo ${{ secrets.API_KEY }}

# 디버깅 목적이면 이렇게
- run: |
    echo "Length: ${#MY_SECRET}"
    echo "First char: ${MY_SECRET:0:1}"
  env:
    MY_SECRET: ${{ secrets.API_KEY }}

시크릿 값 자체를 로그에 출력하는 건 GitHub이 자동 마스킹한다. 길이나 첫 글자 정도로 존재 여부 확인하는 게 현실적인 디버깅 방법이다.

실무 팁: 시크릿 관리 패턴

# 공통 시크릿은 레포 레벨에
# 환경별 시크릿(prod DB URL 등)은 environment 레벨에 분리

jobs:
  test:
    runs-on: ubuntu-latest
    # environment 없음 — 레포 레벨 시크릿만 접근
    steps:
      - run: npm test
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}  # 레포 레벨 시크릿

  deploy-prod:
    runs-on: ubuntu-latest
    environment: production   # environment 레벨 시크릿 접근
    needs: test
    steps:
      - run: ./deploy.sh
        env:
          DB_URL: ${{ secrets.DB_URL }}        # production environment 시크릿
          API_KEY: ${{ secrets.API_KEY }}      # production environment 시크릿

시크릿을 환경별로 분리하면 실수로 개발용 키를 프로덕션에 쓰는 사고를 막을 수 있다.

정리

GitHub Actions 시크릿이 안 읽히는 케이스는 이름 대소문자 불일치, environment 미지정, fork PR 보안 정책 세 가지가 대부분을 차지한다. gh secret list로 정확한 이름 확인하고, environment 시크릿이면 job에 environment: 명시하는 것만 체크해도 80%는 해결된다.

관련 글: GitHub Actions Node.js 빌드 실패 해결 — CI 에러 원인과 대처법

댓글 없음:

댓글 쓰기