← 모든 기록
GitHub Actions secrets와 variables 차이: 민감 정보 안전하게 관리하는 방법 대표 이미지
개발 가이드

GitHub Actions secrets와 variables 차이: 민감 정보 안전하게 관리하는 방법

GitHub Actions에서 secrets와 variables의 차이점과 올바른 사용법을 공식 문서 바탕으로 정리했습니다. 민감 정보를 안전하게 보호하고 배포 워크플로를 구성하는 방법을 단계별로 안내합니다.

GitHub Actions에서 secrets는 API 키나 비밀번호 같은 민감 정보를 안전하게 저장하고, variables는 노출되어도 무방한 설정 값을 관리합니다. secrets는 저장 후 다시 확인할 수 없고 로그에 값이 노출되지 않지만, variables는 필요할 때 자유롭게 편집할 수 있습니다. 이 글에서는 두 기능의 차이와 함께 각각의 등록 방법부터 실전 배포 워크플로에 적용하는 구체적인 예제까지 살펴봅니다.

워크플로 자동화에 관심이 많다면, GitHub Actions 스케줄 사용법이나 깨진 링크 자동 점검에 관한 글도 함께 읽어보세요.

왜 설정 값을 분리해야 할까요?

애플리케이션 배포나 테스트 자동화를 설계할 때, 외부 서비스 API 키나 데이터베이스 접속 정보를 소스 코드에 직접 작성하는 것은 큰 보안 위험을 초래합니다. 실수로 비밀 값이 커밋되어 유출되면 해커가 악용할 수 있고, 환경마다 값이 달라져야 하는 상황에서는 관리가 어려워집니다. GitHub Actions는 이런 문제를 해결하기 위해 SecretsVariables라는 두 가지 설정 관리 기능을 제공합니다. 이들을 활용하면 소스 코드와 분리된 채로 안전하게 자격 증명과 설정 값을 주입할 수 있으며, 워크플로를 여러 환경에서 재사용하기도 훨씬 수월해집니다.

Secrets와 Variables의 핵심 차이

Secrets: 민감 정보를 안전하게 보관

Secrets는 API 토큰, 배포 키, 데이터베이스 비밀번호처럼 워크플로 안에서만 필요한 민감한 데이터를 저장하기 위한 기능입니다. GitHub Secrets는 로그에서 ***로 표시되고 저장 후 값을 확인할 수 없습니다. 또한 한 번 등록하면 재확인이 불가능하며, 워크플로 실행 로그에 실수로 출력되더라도 자동으로 마스킹됩니다. 보안을 위해 포크된 리포지토리에서 트리거되는 워크플로에는 GITHUB_TOKEN을 제외한 비밀이 실행기로 전달되지 않습니다. 그리고 Dependabot 이벤트로 트리거되는 워크플로에서는 비밀을 사용할 수 없습니다. 이러한 제한 덕분에 외부 기여나 의존성 업데이트 시에도 비밀이 노출되지 않습니다. 비밀은 리포지토리, 환경, 조직 수준으로 나누어 관리할 수 있으며, 가장 좁은 범위가 우선 적용됩니다.

Variables: 비민감 설정 값 관리

Variables는 배포 환경 이름, 리전 구분자, 기능 플래그처럼 외부에 노출되어도 문제가 없는 설정을 저장합니다. GitHub Variables 변수는 보안이 필요 없는 값을 저장하고 스크립트 전체에서 사용할 수 있습니다. 값이 마스킹되지 않으므로 로그에서 그대로 확인할 수 있으며, 설정 페이지에서 언제든지 읽고 수정할 수 있습니다. 개인 계정 리포지토리에서 리포지토리 협력자만 비밀이나 변수를 만들 수 있습니다. 조직 소유자와 특정 권한을 가진 사용자가 조직 수준 변수를 관리할 수 있습니다. 참고로 GitHub Actions env 파일 변수는 로드된 job 내에서만 사용할 수 있습니다. 따라서 여러 job이 변수를 공유해야 한다면 리포지토리 또는 조직 수준의 vars를 사용해야 합니다.

주요 차이점 비교

아래 표는 Secrets와 Variables의 동작 방식을 요약합니다.

특성SecretsVariables
용도민감 정보 보호비민감 설정 관리
로그 마스킹자동 마스킹(***)마스킹 없음
값 확인저장 후 재확인 불가설정 화면에서 확인 가능
크기 제한48KB명시적 제한 없음
전달 제한포크 워크플로, Dependabot, 재사용 워크플로에 자동 전달 안 됨제한 없이 모든 워크플로에서 접근 가능
참조 방법${{ secrets.NAME }}${{ vars.NAME }}

Secrets 등록하고 워크플로에서 사용하기

Secrets를 등록하려면 해당 리포지토리의 협력자 권한이 필요합니다. 리포지토리 설정의 Secrets and variables > Actions로 이동해 New repository secret 버튼을 누르면 됩니다. 개인 계정 리포지토리에서 환경 비밀을 만들려면 리포지토리 소유자여야 합니다.

리포지토리 수준 Secrets 등록 절차

  1. GitHub 리포지토리 메인 페이지에서 Settings 탭으로 이동합니다.
  2. 왼쪽 사이드바의 Security 섹션에서 Secrets and variables를 확장한 뒤 Actions를 클릭합니다.
  3. Secrets 탭에서 New repository secret 버튼을 클릭합니다.
  4. Name 필드에 SECRET_NAME 형태의 이름을, Secret 필드에 실제 값을 입력하고 Add secret을 클릭합니다.

이제 워크플로에서 ${{ secrets.SECRET_NAME }}으로 참조할 수 있습니다.

워크플로에서 ${{ secrets.NAME }}으로 사용하기

다음은 등록한 비밀을 사용하는 최소한의 예제입니다.

steps:
  - name: Checkout code
    uses: actions/checkout@v4
  - name: Deploy using secret
    run: ./deploy.sh --token ${{ secrets.DEPLOY_TOKEN }}

설정되지 않은 비밀을 참조하면 빈 문자열이 반환됩니다. 따라서 비밀이 반드시 존재해야 하는 단계라면, 워크플로가 중단되도록 사전 체크 조건을 추가하는 것도 좋은 방법입니다.

Variables 등록하고 활용하기

Variables도 유사한 경로로 등록할 수 있습니다. 리포지토리 Settings > Secrets and variables > Actions에서 Variables 탭으로 이동해 New repository variable을 추가합니다. 이름과 값을 입력하면 바로 ${{ vars.VARIABLE_NAME }} 형태로 어디서든 접근 가능합니다. 비밀과 달리 저장 후에도 값이 보이므로 운영에 필요한 여러 설정을 투명하게 관리할 수 있습니다. 리포지토리 전체, 특정 환경, 조직 수준까지 세 가지 범위로 변수를 정의할 수 있어 다중 환경 배포가 편리합니다.

실전 예제: 배포 워크플로에서 Secrets와 Variables 조합

아래는 프로덕션 배포 시나리오를 가정한 워크플로입니다. DEPLOY_ENV 변수로 배포 환경을 구분하고, API_KEY 시크릿으로 인증 정보를 전달합니다.

name: Production Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set deployment environment
        run: echo "DEPLOY_ENV=${{ vars.DEPLOY_ENV }}" >> $GITHUB_ENV
      - name: Deploy to production
        run: |
          curl -X POST https://api.example.com/deploy \
            -H "Authorization: Bearer ${{ secrets.API_KEY }}" \
            -d '{"env": "${{ env.DEPLOY_ENV }}"}'

보안 모범 사례와 주의할 점

로그 마스킹과 명령줄 노출 주의

Secrets의 마스킹은 완벽하지 않을 수 있습니다. 로그에서 자동으로 *** 처리되더라도, 변수를 echo하거나 디버그 로그로 직접 출력하면 값이 노출될 위험이 있습니다. 특히 명령줄 인자로 비밀을 직접 전달하면 ps 명령 등으로 다른 사용자에게 보일 수 있습니다. 가능하면 명령줄에서 프로세스 간에 비밀을 전달하지 않는 것이 좋습니다. 대신 환경 변수나 STDIN으로 안전하게 전달하는 패턴을 사용하세요. Secrets가 워크플로 로그에 출력되면 GitHub이 자동으로 감지하고 경고를 띄우지만, 일부 고의적인 우회 방법(예: base64로 인코딩된 값)은 감지하지 못할 수 있습니다.

48KB 제한과 base64 인코딩 활용

48KB보다 큰 비밀은 별도의 해결 방법을 사용해야 합니다. 시크릿 하나의 최대 크기는 48KB이므로, 인증서 파일이나 JSON 설정 전체를 저장할 수 없습니다. 이때 Base64 인코딩을 사용하면 작은 이진 blob을 비밀로 저장할 수 있습니다. 예를 들어, openssl base64 -in key.pem 명령으로 인코딩한 문자열을 시크릿으로 등록한 뒤 워크플로에서 echo "$SECRET" | base64 --decode > key.pem으로 복원할 수 있습니다.

포크 및 재사용 워크플로 등 비밀 전달 제한

비밀은 재사용 가능한 워크플로에 자동으로 전달되지 않습니다. 호출하는 쪽에서 secrets: inherit를 명시해야 상위 수준의 비밀을 하위 워크플로에서 받을 수 있습니다. 또한 포크된 리포지토리에서 트리거되는 워크플로에는 기본적으로 비밀이 전달되지 않으며, Dependabot이 생성한 PR 역시 시크릿에 접근할 수 없습니다. 이는 보안상 매우 중요한 제한이므로, 외부 기여자가 실행하는 워크플로에서 민감 정보에 의존하지 않도록 설계해야 합니다. 한편 조직 수준 비밀은 GitHub Free의 프라이빗 리포지토리에서는 액세스할 수 없습니다. 따라서 무료 요금제에서 조직 전체에 공유해야 하는 비밀은 사용에 제약이 있습니다.

마무리: 안전한 자동화를 위한 체크리스트

워크플로를 설계할 때 다음 항목을 점검해보세요.

  • API 키, 비밀번호 등은 반드시 Secrets에 저장하고 Variables에는 저장하지 않았는가?
  • Secrets 이름은 일관된 네이밍 규칙(예: PROD_DB_PASSWORD)을 사용했는가?
  • 워크플로 로그에 Secret이 노출될 수 있는 echo나 디버그 명령이 없는가?
  • 명령줄 인자 대신 환경 변수나 STDIN으로 비밀을 전달하는가?
  • 48KB를 넘는 파일은 base64 인코딩 후 Secrets에 저장했는가?
  • 포크 PR이나 Dependabot이 트리거하는 워크플로가 비밀에 의존하지 않는가?
  • 필요한 경우 환경(Environment) 수준으로 비밀 범위를 제한했는가?

참고 자료