필요한 정보 모아보기
테크

GitHub Actions Node 20 지원 종료(9월 23일) — node20 액션은 이제 Node 24로 강제 실행

#GitHub Actions#Node.js#CI/CD

GitHub Actions에서 Node 20이 9월 23일부로 완전히 빠졌다. 이제 GitHub 러너는 JavaScript 액션을 Node 24로 실행하고, action.yml에 runs.using: node20이라고 적힌 액션도 예외 없이 Node 24로 돌린다. 6월부터 쓸 수 있던 임시 탈출구(ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION)도 함께 막혔다.

GitHub 체인지로그는 이번 공지를 "the final notification"(마지막 안내)이라고 부른다. 핵심 문장은 짧다. "Runners now use Node 24 for JavaScript actions."(이제 러너는 JavaScript 액션에 Node 24를 쓴다.) 워크플로가 갑자기 빨간불이 되는 식의 변화는 아니라서 오히려 놓치기 쉽다. 대신 경고 주석이 쌓인다.

node20 액션은 실패할까, 강제 실행될까

여기서 말하는 JavaScript 액션은 action.yml의 runs.using에 Node 버전을 적고, 러너에 내장된 Node로 main 파일을 실행하는 액션이다. actions/checkout, actions/cache 같은 공식 액션 대부분이 이 형태다. 셸 명령을 묶은 composite 액션이나 Docker 컨테이너 액션은 이번 변화와 상관이 없다.

그럼 using: node20으로 선언된 액션은 어떻게 될까. 공개된 러너 소스(actions/runner, MIT 라이선스)를 직접 읽어 보면 답이 나온다. node20으로 선언된 액션은 실행 직전에 node24로 바꿔치기되고, 작업 요약에는 이런 주석이 남는다. "The following actions target Node.js 20 but are being forced to run on Node.js 24"(다음 액션은 Node.js 20을 대상으로 하지만 Node.js 24로 강제 실행되고 있다). 즉 실패가 아니라 강제 실행이다. 더 오래된 node12, node16 선언도 먼저 node20으로 올린 뒤 같은 경로를 탄다.

9월 23일 이후 달라진 점은 이 강제 전환을 끌 수 없다는 것이다. 소스 주석 표현을 빌리면 "Always use Node 24 regardless of environment variables"(환경변수와 상관없이 항상 Node 24) 단계다. 그동안 거쳐 온 일정은 이렇다.

시기러너 동작탈출구
2025년 8월(러너 v2.328.0)Node 20 기본, Node 24 사전 테스트 가능FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true로 미리 24 사용
2026년 6월 16일Node 24가 기본, node20 액션도 24로 실행ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true로 20 유지
2026년 9월 23일Node 24 강제없음

처음 계획은 훨씬 빨랐다. 2025년 9월 공지는 2025년 가을 전환을 예고했다가, 편집자 주석으로 날짜가 2026년 6월, 다시 6월 16일로 바뀌었고, 8월 25일 주석에서 제거일이 9월 23일로 확정됐다. 1년 가까이 유예를 준 셈이다.

배경에는 Node.js 자체의 일정이 있다. Node.js 공식 릴리스 일정표 기준 Node 20은 4월 30일에 지원이 끝났다. 그 뒤 6월과 7월 Node.js 보안 릴리스에서는 심각도 '높음' 취약점이 연달아 고쳐졌는데, 공지마다 "End-of-Life versions are always affected when a security release occurs"(보안 릴리스가 나오면 지원 종료 버전은 항상 영향을 받는다)라는 문장이 붙는다. 패치가 더는 나오지 않는 런타임으로 남의 코드를 돌리는 건 CI 입장에서 위험하다는 판단이다. 참고로 Node 24도 10월 20일부터 유지보수(maintenance) 단계에 들어가고, 지원 종료는 2028년 4월 30일이다.

실제로 확인한 것: 공식 액션은 어느 버전부터 node24인가

10월 2일 macOS에서 gh 2.101.0으로 각 액션의 action.yml을 태그별로 받아 runs.using 값을 확인했다(읽기 전용 API 호출).

액션node20 마지막 메이저node24 첫 메이저현재 최신
actions/checkoutv4v5v7.0.1
actions/setup-nodev4v5v7.0.0
actions/setup-pythonv5v6v7.0.0
actions/setup-javav4v5v6.0.1
actions/setup-gov5v6v7.0.0
actions/cachev4v5v6.1.0
actions/upload-artifactv5v6v7.0.1
actions/download-artifactv6v7v8.0.1
actions/github-scriptv7v8v9.0.0

눈여겨볼 건 아티팩트 쪽이다. upload-artifact@v5, download-artifact@v6는 숫자만 보면 최신 같지만 아직 node20이다. "메이저를 하나 올렸으니 됐다"고 넘기기 쉬운 자리다.

서드파티도 몇 개 봤다. docker/login-action(v4부터), docker/build-push-action, aws-actions/configure-aws-credentials, softprops/action-gh-release, pnpm/action-setup, peter-evans/create-pull-request의 최신 릴리스는 전부 node24였다. 반면 많이 쓰는 docker/login-action@v3, github/codeql-action/*@v3는 node20이다. codecov/codecov-action 최신판은 composite 액션이라 애초에 대상이 아니다.

영향 범위도 대략 재 봤다. GitHub 코드 검색 API에서 파일명이 action.yml이고 node20이 들어간 파일은 9,296개, node24가 들어간 파일은 6,944개로 나왔다. 검색 색인에 잡힌 기본 브랜치만 세고 단어가 다른 줄에 있어도 걸리는 근사치라 정확한 숫자로 보면 안 된다. 그래도 "아직 node20 선언이 더 많다"는 감은 충분히 준다.

우리 저장소는 괜찮을까: 점검 순서

  1. 최근 실행된 워크플로의 작업 요약(Summary) 화면에서 "being forced to run on Node.js 24" 주석이 붙은 액션 목록을 본다. 가장 빠른 방법이다.
  2. 아래 스크립트로 워크플로가 쓰는 액션의 runs.using을 한 번에 뽑는다.
  3. node20으로 나온 액션은 최신 메이저를 확인해 올린다. 메이저가 바뀌면 입력값 이름이 바뀌었을 수 있으니 릴리스 노트를 같이 본다.
  4. 업데이트가 끊긴 서드파티 액션이라면 대체 액션을 찾거나, 포크해서 using: node24로 바꾼 뒤 동작을 확인한다.
  5. 직접 관리하는 사내 액션은 using 값을 바꾸고 새 릴리스(태그)를 낸다. 체인지로그도 정확히 이 순서를 요구한다.

저장소 루트에서 실행하는 스크립트다. gh가 로그인돼 있어야 하고, 로컬 액션(./)은 건너뛴다.

grep -rhoE 'uses:[[:space:]]*[^.[:space:]#][^[:space:]#]*@[^[:space:]#]+' .github/workflows \
 | sed -E 's/uses:[[:space:]]*//; s/["'"'"']//g' | sort -u \
 | while IFS=@ read -r a ref; do
     r=$(echo "$a" | cut -d/ -f1-2); p=$(echo "$a" | cut -d/ -f3-)
     u=$(for f in action.yml action.yaml; do
           gh api "repos/$r/contents/${p:+$p/}$f?ref=$ref" -H 'Accept: application/vnd.github.raw' 2>/dev/null && break
         done | awk '/^runs:/{f=1} f&&/using:/{gsub(/["'"'"']/,"",$2); print $2; exit}')
     echo "$a@$ref ${u:-?}"
   done

옛 버전을 섞어 둔 예시 워크플로로 돌려 보니 2.5초 만에 이렇게 나왔다.

actions/cache@v4 node20
actions/checkout@v4 node20
actions/setup-node@v5 node24
actions/upload-artifact@v5 node20
docker/login-action@v3 node20
github/codeql-action/init@v3 node20

github/codeql-action/init처럼 저장소 하위 경로에 있는 액션도 경로를 따라 찾아간다. 결과가 ?면 Docker 액션이거나 태그가 없는 경우다.

자체 호스팅 러너라면 따로 볼 것

GitHub 호스팅 러너는 알아서 바뀌었지만, 자체 호스팅 러너는 몇 가지가 더 걸린다.

  • 지원이 끝난 플랫폼: 체인지로그는 "Node 24 is incompatible with macOS 13.4 and earlier"(Node 24는 macOS 13.4 이하와 호환되지 않는다)라고 밝히고, Linux ARM32도 공식 지원 대상이 아니라고 했다. 이 환경의 러너는 이제 지원 대상에서 빠졌다. 러너 소스에는 ARM32에서 작업을 아예 실패시키는 스위치도 들어가 있다.
  • 러너 내부용 Node는 아직 20이다: 이 부분이 헷갈린다. hashFiles() 같은 러너 자체 기능은 여전히 번들된 Node 20을 쓴다. 9월 24일 올라온 이슈에서 GitHub 직원은 "The Node 20 removal in the changelog is about JavaScript actions"(체인지로그의 Node 20 제거는 JavaScript 액션 이야기다)라고 선을 그었다. NixOS처럼 패키지에서 Node 20을 뺀 환경에서는 hashFiles()가 실패했다. 내부 Node를 24로 바꾸는 수정은 10월 1일 병합됐고, 담당자는 다음 주 러너 릴리스에 넣겠다고 밝혔다. 그 전까지 러너 디렉터리의 externals/node20은 지우면 안 된다.
  • 러너 버전: Node 24 지원은 v2.328.0부터 들어갔고, 10월 2일 기준 최신은 v2.337.0이다. 자동 업데이트를 끈 러너도 GitHub 문서상 새 버전이 나오고 30일 안에는 올려야 한다.
  • GitHub Enterprise Server: 이번 공지의 적용 범위는 github.com과 데이터 레지던시(GHE.com)로 명시돼 있다. GHES는 별도 일정을 따른다.

보안 사고 쪽도 찾아봤다. 이번 전환과 관련해 GitHub 러너에 새로 공개된 보안 권고(GHSA)는 없었다. 러너 저장소의 권고는 2022년 Docker 명령 이스케이프 건 하나뿐이다. 위험은 런타임 쪽, 즉 패치가 끊긴 Node 20에 있다.

경고만 보고 넘기기 쉬운 이유

강제 실행이 기본이라서 대부분의 워크플로는 아무 일 없이 초록불을 유지한다. 개인적으로는 이게 오히려 함정이라고 본다. 6월 이전 러너가 node20 액션에 붙이던 경고 문구부터가 "may not work as expected"(예상대로 동작하지 않을 수 있다)였다. 액션 작성자가 Node 24에서 시험해 본 적 없는 코드를 돌리는 셈이고, 문제가 생겨도 단서는 작업 요약의 노란 주석 하나뿐이다.

그래서 이번엔 버전 숫자를 올리는 김에 고정 방식도 같이 손보는 걸 권한다. @v4 같은 메이저 태그 대신 커밋 SHA로 고정하고 Dependabot이 액션 업데이트 PR을 올리게 해 두면, 다음 런타임 전환 때는 위 스크립트를 돌릴 필요조차 없다. 러너 소스에는 node12와 node16 선언을 node20으로 끌어올리던 코드가 지금도 남아 있다 — 같은 일이 벌써 세 번째라는 뜻이다. 2028년 4월이면 Node 24 차례다.

GitHub 체인지로그 문장은 원문을 짧게 인용했고, actions/runner 소스·이슈 문장은 MIT 라이선스 저장소에서, Node.js 보안 공지 문장은 MIT 라이선스 nodejs.org 저장소에서 인용했다. GitHub 문서(docs.github.com) 내용은 CC BY 4.0 라이선스를 따른다.

출처 github.blog/…/2026-09-23-node-20-is-no-longer-available-in-github-actionsgithub.blog/…/2025-09-19-deprecation-of-node-20-on-github-actions-runnersgithub.com/…/4303github.com/…/4737docs.github.comgithub.com/…/Release

내용이 틀렸나요? 정정 요청

관련 글