GitHub Actions에서 "Argument list too long" 에러 해결하기

2026-02-26 hit count image

모노레포 CI에서 변경 파일이 많을 때 발생하는 "Argument list too long" 에러의 원인과, 이를 해결하기 위해 Bash에서 JavaScript(actions/github-script)로 전환한 방법에 대해 알아봅니다.

github_actions

문제 상황

모노레포 환경에서 CI/CD 파이프라인을 운영하다 보면 예상치 못한 에러를 만나게 됩니다. 저희 팀에서도 GitHub Actions의 파일 체크 워크플로우에서 Argument list too long이라는 에러가 발생했습니다.

이 워크플로우는 PR에서 변경된 파일들이 해당 서비스 브랜치에서 수정 가능한 파일인지 검증하는 역할을 합니다. 예를 들어, service-a 브랜치에서는 serviceA 디렉토리의 파일만 수정할 수 있도록 제한하는 것이죠.

기존 구현은 tj-actions/changed-files로 변경된 파일 목록을 가져온 후, Bash의 for 루프에서 환경 변수로 전달된 파일 리스트를 순회하는 방식이었습니다.

for file in $ALL_CHANGED_FILES; do
  service_type=$(echo "$file" | cut -d'/' -f2)
  if [ "$service_type" != "$service_directory" ]; then
    echo "The file $file is not allowed to be changed in the $SERVICE_NAME branch."
    exit 1
  fi
done

문제는 변경된 파일 수가 많을 때 발생합니다. $ALL_CHANGED_FILES 환경 변수에 담긴 파일 경로들이 OS의 인자 길이 제한(Linux의 경우 기본 ARG_MAX = 약 2MB)을 초과하면 Argument list too long 에러가 발생합니다. 모노레포의 규모가 커질수록 한 PR에서 수백, 수천 개의 파일이 변경되는 경우가 빈번해지며, 이 제한에 걸리기 쉬워집니다.

해결 방법

해결의 핵심은 두 가지입니다.

1. 변경 파일 목록을 파일로 저장

tj-actions/changed-fileswrite_output_files 옵션을 활용하여 변경된 파일 목록을 환경 변수 대신 파일로 저장하도록 변경했습니다.

- name: Get changed files
  id: changed-files
  uses: tj-actions/[email protected]
  with:
    files: |
      **
    write_output_files: true
    output_dir: /tmp

이렇게 하면 /tmp/all_changed_files.txt에 변경 파일 목록이 기록됩니다. 파일 시스템을 통해 데이터를 주고받기 때문에 OS의 인자 길이 제한에 영향을 받지 않습니다.

2. Bash에서 JavaScript로 전환

파일을 읽어서 처리하는 로직을 Bash 대신 actions/github-script를 사용한 JavaScript로 재작성했습니다.

- name: Validate file changes
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');

      const serviceDirectoryMap = {
        'service-a': 'serviceA',
        'service-b': 'serviceB',
        'service-c': 'serviceC',
        // ...
      };

      const serviceName = process.env.SERVICE_NAME;
      const serviceDirectory = serviceDirectoryMap[serviceName];
      // ...

      const fileContent = fs.readFileSync('/tmp/all_changed_files.txt', 'utf8');
      const files = fileContent.trim().split(/\s+/).filter(file => file.trim() !== '');

      for (const filename of files) {
        // 허용 파일 체크 및 서비스 디렉토리 검증
      }

이 전환으로 얻은 이점은 다음과 같습니다.

항목Bash (기존)JavaScript (변경 후)
파일 목록 전달환경 변수 (길이 제한 있음)파일 I/O (제한 없음)
서비스 매핑case객체 리터럴 (Map)
에러 핸들링exit 1core.setFailed()
가독성Shell 특유의 문법직관적인 JavaScript

핵심 포인트

환경 변수로 대량의 데이터를 전달하지 마세요. 이는 GitHub Actions에 국한된 문제가 아니라, Unix/Linux 시스템 전반에 해당하는 제약입니다. 셸 명령의 인자 길이에는 OS 수준의 제한이 있으며, CI 환경에서 변경 파일 수가 예측 불가능하다면 반드시 파일 기반 전달 방식을 사용해야 합니다.

또한, actions/github-script는 GitHub Actions에서 복잡한 로직을 처리할 때 Bash의 좋은 대안입니다. Node.js 런타임 위에서 동작하므로 fs, path 등 표준 모듈을 자유롭게 사용할 수 있고, core.setFailed()와 같은 Actions 전용 API도 바로 활용할 수 있습니다.

마무리

작은 수정이지만, CI 파이프라인의 안정성을 크게 높인 변경이었습니다. 모노레포 규모가 커질수록 이런 시스템 수준의 제약을 만날 가능성이 높아지므로, “지금은 동작하니까 괜찮다”가 아니라 확장성을 고려한 설계가 중요하다는 점을 다시 한번 느꼈습니다.

파일 체크 워크플로우의 전체 구조에 대해 더 알고 싶다면 모노레포에서 브랜치별 파일 변경 범위를 제한하는 GitHub Actions 워크플로우를 참고해 주세요.

제 블로그가 도움이 되셨나요? 하단의 댓글을 달아주시면 저에게 큰 힘이 됩니다!

앱 홍보

책 홍보

블로그를 운영하면서 좋은 기회가 생겨 책을 출판하게 되었습니다.

아래 링크를 통해 제가 쓴 책을 구매하실 수 있습니다.
많은 분들에게 도움이 되면 좋겠네요.



SHARE
Twitter Facebook RSS