본문으로 건너뛰기
팀7개 프로젝트·브랜치 최대 12개 · 데이터 모듈 17개 · 610개 파일 · 81 suites/978 tests

공통 패키지·CI 플랫폼 — fork 정렬, 7개 프로젝트 업그레이드 자동화, 테스트 CI 단축

백엔드 개발자 → Platform Part 리더 · 패키지 이관·업그레이드 자동화·CI 개선 직접 수행

2026-02-01 — 2026-04-30

2년 넘게 어긋난 게임 서버의 데이터 모듈 fork를 공통 npm 패키지로 정렬(610개 파일·310+ import 마이그레이션)하고, 7개 프로젝트 업그레이드를 GitHub Actions로 자동화(수동 약 3시간→약 15분), 공통 서버 키트의 테스트 job 평균 시간을 61% 단축(15분47초→6분6초)

성과 지표

테스트 job 평균
이전: 15분47초이후: 6분6초
61% 단축 · CI 전체 약 16분→약 7분
패키지 업그레이드
이전: 수동 약 3시간이후: 자동 약 15분
7개 프로젝트 matrix 병렬
모듈 정렬
이전: 2년+ 어긋난 fork이후: npm 버전 bump
610개 파일·마이그레이션 47개 삭제
이관 회귀 복구
이전: live 500 오류 보고이후: 82분 만에 hotfix
관련 작업 9건 추적
공통 패키지·CI 플랫폼 — fork 정렬, 7개 프로젝트 업그레이드 자동화, 테스트 CI 단축 project cover image

문제 해결 과정

1

한 게임 서버가 공통 데이터 모듈을 fork한 뒤 2년 넘게 upstream과 정렬되지 않아, 내부 모듈 17개를 계속 발전하는 외부 npm 패키지로 바꾸려면 610개 파일과 import 310+곳을 옮겨야 했습니다.

  1. 증상: 2년+ 어긋난 fork · 610개 파일 · import 310+
  2. 조치: 내부 모듈 → 패키지 → 최신 버전, 두 단계 이관
  3. 조치: 게임 고유 기능은 wrapper 6개로 유지
  4. 결과: 610개 파일·마이그레이션 47개 삭제 · 버전 bump로 반영
해결 과정

다른 게임에서 쓰던 정렬 방식을 기준으로 두 단계로 나눴습니다. 먼저 내부 모듈을 패키지로 바꾸고, 그다음 같은 패턴과 패키지 관리 마이그레이션으로 최신 버전까지 맞췄습니다. 게임 고유 기능(현지화, 금칙어, 동적 환경 등)은 얇은 wrapper 모듈 6개로 남겼습니다.

결과

내부 모듈 610개 파일과 로컬 마이그레이션 47개를 지웠고, 이후 신규 기능은 패키지 버전 bump로 들여올 수 있게 됐습니다. 이관 회귀로 live 관리자 기능에 500 오류가 났을 때는 보고 82분 만에 hotfix를 배포했고, 이관을 포함한 관련 작업 9건으로 나눠 추적했습니다.

2

공통 패키지를 올릴 때마다 7개 프로젝트의 main/develop 브랜치(최대 12개)를 손으로 업그레이드해 약 3시간이 걸렸고 작업 전환 비용도 컸습니다.

  1. 증상: 7개 프로젝트 · 브랜치 최대 12개 수동 · 약 3시간
  2. 조치: workflow_dispatch + matrix로 7개 병렬
  3. 결과: 약 15분 · Job Summary에 skip/upgrade/downgrade
해결 과정

workflow_dispatch로 실행하는 GitHub Actions 워크플로를 만들고 matrix로 7개 프로젝트를 병렬 처리했습니다. 프로젝트별 결과는 Job Summary에 skip/upgrade/downgrade로 남겼습니다.

결과

업그레이드가 수동 약 3시간에서 자동 약 15분으로 줄었습니다.

3

공통 서버 키트에서 ts-jest 컴파일이 파일당 약 15초로 테스트 시간의 53%를 차지해, 순수 단위 테스트도 15~19초씩 걸렸습니다.

  1. 증상: 순수 단위 테스트도 15~19초
  2. 계측: ts-jest 컴파일이 테스트 시간의 53% · 파일당 약 15초
  3. 조치: isolatedModules · 타입 검사는 tsc --noEmit 단계로
  4. 결과: 테스트 job 평균 15분47초→6분6초(61%) · 978 tests 통과
해결 과정

ts-jest를 isolatedModules로 바꿔 파일 단위로만 변환하고, 타입 검사는 CI의 별도 tsc --noEmit 단계에 맡겼습니다. 이 모드에서는 데코레이터 메타데이터가 다른 파일의 타입을 참조하지 못해, 영향받는 엔티티 4개에 컬럼 타입을 명시했습니다.

결과

파일당 컴파일 15초→1~3초, 4개 shard의 테스트 job 평균 15분47초→6분6초(61% 단축), 빌드·품질 검사를 포함한 CI 전체 약 16분→약 7분. 81 suites·978 tests 전체 통과.

운영 한계

isolatedModules에서는 테스트 단계가 타입 검사를 하지 않으므로, 타입 오류는 CI의 별도 tsc --noEmit 단계에서 잡습니다.
업그레이드 자동화가 적용되지 않은 프로젝트에는 수동 롤아웃이 남았고, 그중 한 번은 주석을 제외한 필드 1,073개를 byte 단위로 비교해 하위 호환을 확인했습니다.
이관 회귀는 배포 후 live에서 발견됐고, 사전 테스트로 잡지 못했습니다.

프로젝트 설명

한 게임 서버가 공통 게임 데이터 모듈을 fork한 채 2년 넘게 정렬되지 않았고, 공통 패키지 업그레이드는 7개 프로젝트에 손으로 반복됐으며, 공통 서버 키트의 테스트는 ts-jest 컴파일에 시간을 쓰고 있었습니다. fork는 npm 패키지와 패키지가 관리하는 마이그레이션으로 정렬하고, 업그레이드는 workflow_dispatch + matrix로 자동화하고, 테스트 컴파일은 isolatedModules 전환으로 줄였습니다. 이관 중 생긴 live 회귀와 복구 과정도 함께 적었습니다. 내부 제품·게임 이름은 일반 명칭으로 바꿨습니다.

주요 내용

  • 2년 넘게 upstream과 어긋난 게임 서버의 데이터 모듈 17개를 공통 npm 패키지로 교체: 610개 파일·로컬 마이그레이션 47개 삭제, import 310+곳 갱신
  • 이후 데이터 모듈 기능은 패키지 버전 bump로 들여올 수 있고, 스키마는 패키지가 관리하는 마이그레이션 CLI로 맞춤
  • 7개 프로젝트(브랜치 최대 12개) 업그레이드를 workflow_dispatch + matrix로 병렬화하고 Job Summary로 skip/upgrade/downgrade 보고: 수동 약 3시간→약 15분
  • ts-jest isolatedModules 전환으로 파일당 컴파일 약 15초→1~3초, 테스트 job 평균 15분47초→6분6초(61%), CI 전체 약 16분→약 7분

기술 선택 근거

  • ▶fork 유지 대신 upstream 정렬: 게임별 차이는 wrapper 모듈로 분리하고 나머지는 패키지에 맡겨, 이후 업데이트가 버전 bump로 끝나게 했습니다.
  • ▶두 단계 이관: 먼저 내부 모듈을 패키지로 바꾸고, 그다음 패턴·마이그레이션을 최신 버전에 맞춰 한 번에 섞이는 변경 종류를 줄였습니다.
  • ▶업그레이드는 matrix 자동화로: 프로젝트별 결과를 Job Summary에 남겨 건너뛴 곳과 실패한 곳을 한 화면에서 확인합니다.
  • ▶테스트는 isolatedModules: 파일 단위 변환으로 컴파일 시간을 줄이고, 그 대가로 생긴 데코레이터 타입 공백은 컬럼 타입 명시로 메웠습니다.

깨달은 점

  • •fork는 시간이 갈수록 정렬 비용이 커진다. 게임별 차이는 wrapper로 좁히고 나머지는 공통 패키지에 맡긴다.
  • •프로젝트 수만큼 반복되는 작업은 자동화하고, 결과를 한 화면에 남겨야 빠진 곳이 보인다.
  • •속도를 위해 끈 검사는 다른 곳이 맡아야 한다. 타입 검사는 별도 tsc 단계로 옮기고, 데코레이터 메타데이터 공백은 컬럼 타입 명시로 메웠다.

기술 스택

TypeScriptNestJSnpm packagesGitHub ActionsJest (ts-jest)TypeORM