GitHub Actions secrets: inherit, 한 줄이 일으키는 의외의 일들

재사용 워크플로우(reusable workflow) 쓰다 보면 거의 반사적으로 secrets: inherit 한 줄 박아두게 된다. 편하니까. 근데 이게 동작 방식을 정확히 모르고 쓰면 디버깅하다가 한 시간씩 날려먹는 포인트가 몇 개 있다. 오늘은 그 중 자주 걸리는 것들만 짧게 정리.
inherit는 "직접 호출한 워크플로우"까지만 간다
체인 호출에서 제일 많이 헷갈리는 부분이다. A → B → C 구조라고 하자. A에서 B를 부르면서 secrets: inherit을 줬다. B에서 C를 부르는데 거기에 secrets: inherit을 또 안 적으면, C는 시크릿을 못 본다. inherit는 한 단계만 전파된다.
# A.yml
jobs:
call-b:
uses: ./.github/workflows/B.yml
secrets: inherit # B까지만 흘러감
# B.yml
jobs:
call-c:
uses: ./.github/workflows/C.yml
secrets: inherit # 이거 빼먹으면 C는 못 봄
명시적으로 매번 적어줘야 한다. "한번 inherit 했으니 알아서 흘러가겠지" 하면 안 된다.
with: 안에서는 inherit한 시크릿을 못 쓴다
이거 처음 만나면 진짜 한참 헤맨다. secrets: inherit은 호출된 워크플로우 내부에서만 시크릿을 노출시킨다. 호출하는 쪽의 with: 블록에서는 그 시크릿을 참조할 수 없다.
# 안 됨
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
api-key: ${{ secrets.API_KEY }} # 컨텍스트에서 못 찾음
secrets: inherit
inputs로 시크릿을 넘기고 싶으면 secrets.MY_KEY를 명시적으로 정의해서 매핑해야 한다. 아니면 그냥 inherit만 쓰고, 호출된 워크플로우 내부에서 ${{ secrets.API_KEY }} 형태로 직접 참조하는 게 깔끔하다.
Environment 시크릿은 inherit으로 안 따라온다
on.workflow_call에는 environment 키워드가 없다. 그래서 호출된 워크플로우의 잡(job) 레벨에서 environment: production을 지정하면, 그 시점에 환경 시크릿이 적용된다. 호출하는 쪽에서 같은 이름의 시크릿을 넘겨도 환경 시크릿이 우선이다.
운영 환경별로 시크릿을 분리해두고 재사용 워크플로우 쓰는 팀이라면 이거 한번 점검해볼 만하다. "왜 stg에서는 되는데 prd에서는 빈 값이지" 하면 십중팔구 이 이슈다.
그리고 2026년에 바뀐다
GitHub이 올해 발표한 Actions 보안 로드맵에서 implicit secret inheritance를 손보겠다고 했다. 재사용 워크플로우로 시크릿이 암묵적으로 흘러들어가면서 신뢰 경계가 모호해진다는 지적이 계속 있었는데, scoped secrets라는 형태로 명시적 실행 컨텍스트에 시크릿을 바인딩하는 방향으로 가는 듯하다.
지금부터라도 secrets: inherit 박혀있는 워크플로우들 한번 훑어보고, 정말 inherit이 필요한 건지 아니면 명시적으로 필요한 시크릿만 넘기는 게 나은 건지 검토해두면 나중에 마이그레이션이 편하다.
오늘은 여기까지. 시크릿 한 줄로 시작했는데 의외로 할 얘기가 많다. 다른 함정 알고 계신 분 있으면 댓글로 공유 부탁드린다.