GitHub Actions도 이제 step을 병렬 실행합니다: CI 설계가 달라지는 이유
결론 요약
- GitHub Actions는 2026년 6월 25일부터 하나의 job 안에서 step을 병렬 실행하는 background·wait·wait-all·cancel·parallel 문법을 지원합니다.
- 독립 빌드는 parallel로 묶고, 테스트용 서버처럼 수명이 긴 프로세스는 background와 cancel로 관리하면 로그 분리와 정리 동작을 함께 얻을 수 있습니다.
- 공유 작업 디렉터리와 출력값 경쟁을 먼저 제거하고, 실행 시간뿐 아니라 러너 CPU·메모리와 실패 재현성까지 측정해야 안전하게 빨라집니다.
GitHub Actions도 이제 step을 병렬 실행합니다: CI 설계가 달라지는 이유
CI 화면에서 세 작업이 차례를 기다리는 모습을 본 적 있으신가요? 프런트엔드 빌드가 끝난 뒤 백엔드 빌드가 시작되고, 그것이 끝나야 문서 빌드가 시작됩니다. 서로 결과를 필요로 하지 않는데도 같은 job의 step이라는 이유로 한 줄로 서 있었습니다. 급한 출근 시간에 빈 계산대 세 개를 두고 한 계산대만 쓰는 것과 비슷한 모습입니다.
물론 기존에도 방법은 있었습니다. job을 여러 개로 나누거나 matrix를 사용하면 병렬 실행이 가능했고, 셸 명령 뒤에 &를 붙여 백그라운드로 보낼 수도 있었습니다. 하지만 job을 나누면 체크아웃과 환경 준비, 캐시 복원 같은 작업을 반복해야 합니다. 셸 백그라운드는 여러 프로세스의 로그가 섞이고 종료 상태를 제대로 다루기 어려웠습니다.
GitHub는 2026년 6월 25일 하나의 job 안에서 step을 동시에 실행할 수 있는 공식 문법을 공개했습니다. 핵심은 background, wait, wait-all, cancel, parallel입니다. 단순히 YAML이 짧아진 것이 아니라, 같은 러너와 작업 디렉터리를 공유하면서도 step별 로그와 상태를 유지하는 새로운 선택지가 생겼습니다. 이제 어떤 작업을 job으로 나누고 어떤 작업을 병렬 step으로 묶을지 CI 구조를 다시 생각해 볼 때입니다.
GitHub Actions 병렬 step: 다섯 키워드의 역할
background: true는 해당 step을 비동기로 시작하고 다음 step으로 즉시 진행합니다. 백그라운드 step에는 id를 붙여 나중에 기다리거나 중단할 수 있습니다. wait는 지정한 background step이 끝날 때까지 기다리고, wait-all은 앞서 시작한 모든 background step의 완료를 기다립니다. cancel은 더 이상 필요 없는 background step에 정상 종료 신호를 보내고, 짧은 유예 시간 뒤에도 끝나지 않으면 강제 종료합니다.
parallel은 독립된 여러 step을 동시에 실행한 뒤 모두 끝날 때까지 기다리는 축약 문법입니다. 내부적으로 각 항목을 background step처럼 실행하고 마지막에 암묵적인 wait를 둔 것으로 이해하면 됩니다.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- parallel:
- name: Build frontend
run: npm run build:frontend
- name: Build backend
run: npm run build:backend
- name: Build docs
run: npm run build:docs
- name: Run tests
run: npm test
이 예제에서 세 빌드는 동시에 시작하고, 세 작업이 모두 성공해야 테스트로 넘어갑니다. 각 빌드 로그는 별도 step으로 남으므로 셸에서 command-a & command-b & wait를 실행할 때보다 문제를 찾기 쉽습니다.
parallel과 background는 언제 나눠 쓸까요?
시작과 종료 시점이 같은 독립 작업 묶음에는 parallel이 잘 맞습니다. 여러 패키지 빌드, 독립적인 린터와 정적 분석, 서로 다른 문서 포맷 생성처럼 모두 끝난 뒤 다음 단계로 가면 되는 경우입니다.
반면 다른 step이 실행되는 동안 계속 살아 있어야 하는 프로세스에는 background가 적합합니다. 통합 테스트용 웹 서버, 데이터베이스, 성능 모니터링 도구가 대표적입니다. 특정 결과만 먼저 기다리거나 테스트가 끝난 뒤 프로세스를 중단하는 세밀한 제어가 가능하기 때문입니다.
steps:
- uses: actions/checkout@v6
- name: Start test server
id: test-server
run: npm run start:test
background: true
- name: Run integration tests
run: npm run test:integration
- name: Stop test server
cancel: test-server
예전에는 서버를 셸 백그라운드로 띄우고 PID 파일을 저장한 뒤 trap으로 정리하는 코드가 필요했습니다. 이제 러너가 step의 수명과 로그를 알고 있으므로 종료 처리가 명확해집니다. cancel은 항상 실행되며 if 조건을 지원하지 않는다는 점도 기억해야 합니다. 정리 단계가 조건 때문에 건너뛰어지는 일을 막기 위한 동작입니다.
GitHub Actions 병렬화와 job 분리: 무엇이 더 적합할까요?
병렬 step이 생겼다고 모든 멀티 job 워크플로를 하나로 합칠 필요는 없습니다. 두 방식은 자원을 공유하는 범위가 다릅니다.
여러 job은 각자 다른 러너를 사용할 수 있어 CPU와 메모리도 따로 확보합니다. 운영체제나 런타임 버전 조합을 시험하는 matrix, 보안 권한이 다른 배포 단계, 서로 완전히 독립된 대형 테스트 묶음에는 여전히 job 분리가 적합합니다. 대신 각 job은 체크아웃과 도구 설치, 캐시 복원을 반복할 수 있고 결과 전달에 artifact나 job output이 필요합니다.
병렬 step은 하나의 러너 안에서 실행됩니다. 작업 디렉터리와 네트워크, 설치된 도구를 그대로 공유하므로 준비 비용이 적고 로컬 서비스와 통합하기 쉽습니다. 하지만 CPU 코어와 메모리도 나눠 씁니다. 두 빌드를 동시에 실행했는데 메모리 부족으로 스왑이 발생하면, 순차 실행보다 오히려 늦어질 수 있습니다.
판단 기준은 간단합니다. 준비 환경을 공유해야 하고 합친 자원 사용량이 러너 용량 안에 들어오면 병렬 step을 고려합니다. 서로 다른 운영체제나 권한, 독립 자원이 필요하면 job을 유지합니다. “병렬”이라는 이름만 같을 뿐 하나는 같은 작업대에서 여러 손을 움직이는 방식이고, 다른 하나는 작업대를 여러 개 빌리는 방식입니다.
최대 10개라고 10개를 모두 채우지는 마세요
공식 문서상 하나의 job에서 background step은 최대 10개까지 동시에 실행할 수 있고, 초과 항목은 자리가 날 때까지 대기합니다. 이 숫자는 목표가 아니라 상한입니다. 일반적인 GitHub-hosted runner에서 컴파일 작업 열 개를 동시에 돌리면 CPU 경쟁과 메모리 압박이 커질 가능성이 높습니다.
처음에는 실행 시간이 긴 독립 작업 두 개만 묶어 보세요. 전체 job 시간과 각 step 시간, 최대 메모리, CPU 사용률을 비교한 뒤 세 개로 늘립니다. 병렬화의 이득은 가장 긴 경로를 줄이는 데서 나오므로 5초짜리 작업 여러 개를 묶기보다 3분짜리 두 작업을 겹치는 편이 효과가 큽니다.
GitHub Actions 병렬 step의 함정: 공유 상태와 실패 전파
같은 러너를 공유한다는 장점은 동시에 가장 큰 위험입니다. 두 step이 같은 파일을 수정하거나 같은 포트를 사용하면 경쟁 상태가 생깁니다. 예를 들어 프런트엔드와 문서 빌드가 모두 dist/를 비우고 결과를 쓰면 타이밍에 따라 파일이 사라질 수 있습니다. 테스트 서버 두 개가 같은 3000번 포트에 바인딩하려 해도 한쪽은 실패합니다.
병렬화 전에 각 step의 입력과 출력 경로를 표로 적어보는 것이 좋습니다. 쓰기 경로가 겹치면 별도 디렉터리를 사용하고 마지막에 합치세요. 패키지 매니저 캐시는 읽기만 공유하고, 캐시 갱신은 한 단계에서 처리하는 편이 재현성이 좋습니다. 데이터베이스를 공유한다면 테스트별 스키마나 고유 데이터베이스 이름을 사용해야 합니다.
출력값과 환경 변경 시점도 주의해야 합니다. background step이 만든 output과 환경 변경은 해당 step을 wait 또는 wait-all로 기다린 뒤에야 이후 step에서 사용할 수 있습니다. 아직 끝나지 않은 작업의 결과를 읽는 것은 빵이 오븐에 있는데 완성품 무게를 재려는 것과 같습니다.
실패는 기다리는 지점에서 드러납니다. background step이 실패하면 그 대상을 기다리는 wait도 실패합니다. 별도의 wait 없이 job 끝까지 갔다면 post-job 정리 전에 암묵적인 wait-all이 실행됩니다. 실패를 무시하고 싶다면 이유를 명확히 하고 continue-on-error를 제한적으로 사용해야 합니다. 핵심 빌드 실패를 텔레메트리 업로드 실패와 같은 수준으로 취급하면 원인을 찾기 어려워집니다.
현재 제약도 설계에 포함해야 합니다
병렬 step 문법은 composite action 내부에서는 사용할 수 없습니다. composite action 자체를 background로 실행할 수는 있지만, 그 내부에서 다시 background나 parallel을 선언할 수는 없습니다. 공용 composite action 안에 병렬 로직을 숨기려 했다면 워크플로 수준으로 끌어올리거나 job 병렬화를 유지해야 합니다.
또한 동시에 실행되는 step의 로그는 분리되지만, 애플리케이션이 외부 서비스나 같은 로그 파일에 쓰는 내용까지 자동으로 분리되지는 않습니다. 로그 파일명과 테스트 결과 경로에도 step 식별자를 넣어야 합니다. 병렬 실행에서 재현성이 떨어지는 문제는 대개 도구 자체보다 이런 암묵적 공유 상태에서 시작합니다.
GitHub Actions CI 전환 절차: 빠르게보다 측정 가능하게
첫째, 현재 워크플로의 임계 경로를 찾습니다. 실행 시간 순으로 step을 나열하고 서로 의존하지 않는 긴 작업을 표시합니다. 앞 단계 output을 참조하거나 같은 파일을 수정하면 독립 작업이 아닙니다.
둘째, 두 작업만 시범 병렬화합니다. 시작과 끝이 함께 움직이면 parallel, 다른 테스트 동안 살아 있어야 하면 background와 cancel을 선택합니다. 셸의 &, 수동 PID 파일, 임시 sleep은 공식 수명 관리 문법으로 바꿉니다.
셋째, 준비 상태를 명시적으로 확인합니다. 서버를 background로 띄운 직후 테스트를 시작하면 포트가 열리기 전에 요청이 도착할 수 있습니다. 고정된 sleep 10 대신 health endpoint나 포트에 재시도하는 준비 확인 step을 두세요. 빠른 날에는 바로 진행하고 느린 날에는 필요한 만큼 기다릴 수 있습니다.
넷째, 성능과 안정성을 함께 비교합니다. 전체 소요 시간의 중앙값과 상위 95% 값, 실패율, 재실행률을 최소 여러 번 수집합니다. 한 번 2분 빨라졌지만 간헐 실패가 늘었다면 개선이 아닙니다. GitHub-hosted runner 사용 시간과 self-hosted runner의 CPU·메모리도 함께 확인하세요.
다섯째, 실패 시 로그가 충분한지 살펴봅니다. 병렬 step별 출력 경로, 테스트 리포트, artifact 이름을 분리하고, 어떤 wait에서 어떤 background step이 실패했는지 바로 알 수 있어야 합니다. CI는 성공할 때 빠른 것보다 실패했을 때 이유를 빨리 보여주는 것이 더 중요할 때가 많습니다.
결론: GitHub Actions 병렬 step은 CI의 빈 대기 시간을 줄입니다
GitHub Actions의 background, wait, wait-all, cancel, parallel은 하나의 job 안에 있던 불필요한 줄서기를 없애는 도구입니다. 독립 빌드를 동시에 실행하고, 테스트 서버를 별도 로그로 유지하며, 필요한 시점에 기다리거나 정리할 수 있습니다. 기존의 셸 백그라운드 꼼수를 공식 실행 모델로 옮긴 점도 큰 장점입니다.
하지만 병렬화는 순서를 지우는 작업이 아니라 의존성을 드러내는 작업입니다. 파일과 포트, CPU와 메모리, 출력값이 어디에서 공유되는지 모르면 CI는 빠른 대신 불안정해집니다. 같은 러너를 공유하는 병렬 step과 자원을 분리하는 멀티 job의 차이를 이해하고 작업 성격에 맞춰 선택해야 합니다.
지금 워크플로에서 가장 오래 걸리는 step 세 개를 펼쳐보세요. 그중 서로 결과를 기다릴 필요가 없는 두 개가 있다면 좋은 첫 후보입니다. 두 작업만 병렬로 바꾸고 전후 시간을 재는 작은 실험이, 거대한 CI 재작성보다 빠르고 안전하게 빌드 대기 시간을 돌려줄 것입니다.
참고한 공식 자료
This post was drafted with the help of AI tools and personally reviewed and edited.
Thanks for reading!
If you found this post useful, check out more articles on the homepage.
Read More Posts