카카오웹툰 프론트엔드가 조건문 덩어리로 불어난 Composite action을 Map 구조의 JavaScript action으로 옮긴 기록이다. env 5개와 region 4개의 조합을 셸 스크립트 중첩 if로 처리하던 것을 객체 조회 한 줄로 바꾸는 과정을 실제 파일 단위로 보여준다. 옮기는 중에 만난 node_modules 문제와 그 해법까지 이어진다.
핵심 포인트- 셸 기반 Composite action은 변수와 조건이 늘어날수록 문자열 분해와 중첩 if로 읽기 어려워진다.
- 조건 조합을 중첩 객체 Map으로 바꾸면 각 항목에 대응하는 값이 한눈에 보인다.
- JavaScript action은 action.yml에서 using을 node로 바꾸고 main에 스크립트 파일을 지정하면 된다.
- @actions/core의 getInput과 setOutput으로 입력을 받고 출력을 내보낸다.
- 그대로 push하면 node_modules가 없어 모듈을 못 찾는 에러가 난다.
- node_modules를 커밋하는 대신 워크플로에 setup-node와 cache, npm ci 스텝을 넣어 실행 시점에 설치한다.
상세 정리- 문제의 출발: 처음 Composite action을 만들 때는 변수가 적고 조건도 단순해 셸 스크립트로 충분했다.
- 2년 뒤 상황: 서비스 region이 늘면서 코드가 불어나고 문자열 분해나 파일 치환 같은 문법이 섞여 읽기 어려워졌다.
- 규모: env 5개에 region 4개이고 env마다 region이 달라 배포에 필요한 값이 각각 다르다. 그래서 중첩 if의 깊이가 계속 늘었다.
- Map 전환의 취지: 테마와 타입 조합으로 아이콘을 고르는 예제를 들어, 중첩 if 대신 객체를 두 번 인덱싱하는 형태가 가독성이 낫다고 본다.
- 팀의 선호: 이런 조건문이 있을 때 Map 형태로 개발하는 것을 팀이 선호하며 TypeScript라면 타입을 잘 정의해야 한다고 덧붙인다.
- 기존 구조: 사용자가 workflow_dispatch로 env와 region을 넘기면 워크플로가 Composite action에 전달하고, 그 안에서 값을 꺼내 outputs로 돌려준다.
- 기존 코드: action.yml의 셸 스텝에서 env와 region을 중첩 if로 분기해 GITHUB_OUTPUT에 값을 붙인다. region 2개, env 2개만 가정해도 이미 복잡해진다.
- 전환 1: @actions/core를 설치한다.
- 전환 2: action.yml의 runs를 composite에서 node로 바꾸고 main에 실행할 스크립트 파일을 지정한다. outputs 항목은 value 대신 description만 남긴다.
- 전환 3: 스크립트에서 env와 region을 키로 하는 중첩 객체를 만들고, getInput으로 받은 값으로 조회한 뒤 setOutput으로 내보낸다. 실패 시 setFailed로 메시지를 남긴다.
- 첫 실행 실패: push하면 모듈을 찾을 수 없다는 에러가 난다. @actions/core에 필요한 node_modules가 저장소에 없기 때문이다.
- 선택지 검토: gitignore에서 node_modules를 빼고 커밋하는 방법도 있지만 패키지가 늘면 용량을 감당하기 어렵다고 본다.
- 해결: 워크플로에 setup-node, package-lock 해시를 키로 하는 캐시, 캐시 미스일 때만 도는 npm ci 스텝을 추가한다.
- 결과: 워크플로 실행 시점에 항상 node_modules가 갖춰지고 캐시 덕에 성능 부담도 없어 전환이 마무리됐다.
- 추가 활용: 워크플로 성패를 슬랙으로 알리려면 트리거 정보가 필요한데, @actions/github을 설치해 스크립트에서 저장소와 실행자 같은 컨텍스트를 읽을 수 있다.
왜 읽나Composite action의 중첩 if가 감당이 안 되는 상태라면 JavaScript action으로 옮기는 최소 절차와 node_modules 함정을 그대로 따라할 수 있다.