← 포트폴리오 이력서
2024|Build / Architecture
EDU-CORE 빌드 개선

처음엔 느린 빌드를 고치는 일이라 생각했지만, 진짜 문제는 여러 팀이 함께 쓰는 시스템이라 누구도 쉽게 바꾸기 어려운 구조였습니다. 22개 앱·21명이 공유하던 React + Express SSR 모놀리스를 부분 빌드가 가능한 모노레포로 다시 설계한 기록입니다.

이 글은 발표자료를 보시면 더 좋습니다.

프로젝트 한눈에 보기

항목내용
환경React + Express SSR, 22개 앱, 21명 공동 개발
출발 문제오래 걸리는 빌드
역할병목 측정, Webpack5 전환, 앱 단위 빌드 설계, Turborepo 도입, 점진적 마이그레이션
핵심 성과스쿼드별 독립적 기술 의사결정 가능, 운영 빌드 65~98% 단축, Feature branch 99.5% 단축
핵심 포인트빌드 속도가 아니라, 스쿼드가 스스로 기술을 결정할 수 있게 된 것

1. 출발점 — 느린 빌드

edu-core는 구름EDU의 핵심 레포입니다. React + Express SSR 구조 안에서 22개 앱이 함께 돌았고, 21명 이상이 동시에 수정하고 있었습니다.

원인은 전형적인 빌드 레거시였습니다.

이 시점의 문제 정의는 단순했습니다. 빌드 시스템이 현재 규모를 감당하지 못한다.

2. 첫 번째 시도 — 빌드를 빠르게

기존 구조를 유지한 채 빌드 체계를 현대화했습니다.

개발 환경에서는 성과가 분명했습니다.

항목BeforeAfter개선율
2차 빌드 (캐싱)~115초~15초87% ↓
dist 용량529MB326MB40% ↓

하지만 운영 반영에 실패했습니다. 새 webpack 설정을 검증하는 동안에도 기존 config로 새 페이지가 계속 추가됐고, 21명을 같은 시점에 옮기는 일은 불가능했습니다.

교훈은 분명했습니다. 기술 선택은 맞았지만, 전환 방식이 틀렸다. 큰 공유 레포에서 중요한 것은 더 좋은 설정이 아니라 멈추지 않고 옮겨갈 수 있는 구조였습니다.

3. 문제 재정의 — 느린 빌드는 증상이었다

질문을 바꿔봤습니다.

답은 조직과 시스템의 불일치였습니다. edu-core는 한 팀이 빠르게 움직이던 시절의 구조였지만, 지금은 EDU B2C, EDU B2B, DEVTH로 스쿼드가 나뉘어 있었습니다. 비즈니스 책임은 분리됐는데, 시스템은 여전히 하나였습니다. 시스템 전반에 영향을 주는 변경은 어느 스쿼드도 단독으로 결정하기 어려워 늘 뒤로 밀렸습니다.

이 관점에서 기술 병목도 다시 보였습니다.

  1. 단일 dist·단일 라우트frontApp 한 줄을 고쳐도 examApp 포함 전체가 다시 빌드됨
  2. SSR 무관 static 페이지 공존devth, channel, obserview 등이 같은 빌드 흐름에 묶임
  3. 무관한 앱 강제 빌드 — 경로 중간 앱까지 띄워야 해 자원·HMR 모두 악화

새 설계 조건은 네 가지였습니다.

  1. 필요한 부분만 빌드할 수 있어야 한다
  2. 앱 단위로 책임을 나눌 수 있어야 한다
  3. 전체를 멈추지 않고 점진적으로 옮길 수 있어야 한다
  4. 이후 이미지 기반 배포에도 버틸 수 있어야 한다

필요한 것은 빠른 webpack 설정이 아니라 작은 앱 단위로 분리되고 순차 이전 가능한 구조였습니다.

4. 두 번째 시도 — 모노레포로 구조를 바꾸다

빌드 시간이 아니라, 빌드 비용이 커질 수밖에 없는 구조를 해체했습니다.

4-1. Static 페이지 분리

SSR과 무관한 devth, obserview, channel, swcamp, captain을 별도 레포·배포 흐름으로 떼어냈습니다. 이 한 단계로 static 페이지 수정 시 운영 빌드는 1748초 → 22초.

4-2. 앱 단위 독립 구조

산출물과 라우팅을 앱 기준으로 쪼갰습니다.

기존
dist/client -> 모든 앱이 함께 번들링

변경
dist/frontApp/client
dist/examApp/client
...

각 앱이 독립된 package.json, webpack.config.js, client/server entry를 갖게 했습니다. 필요한 앱만 빌드하고, 나머지는 404 또는 기존 결과물을 재사용했습니다.

4-3. Config 패키지화와 Webpack 4/5 공존

첫 시도의 결과물을 그대로 재활용했습니다.

앱마다 old/new를 선택하게 만들어, 전체를 한 번에 바꾸는 대신 각 스쿼드가 준비된 앱부터 순서대로 옮길 수 있게 했습니다. 첫 시도의 new-webpack-config가 여기서 핵심 자산이 됐습니다.

4-4. Turborepo 기반 증분 빌드

앱 경계가 생긴 뒤 Turborepo로 변경된 앱만 빌드하게 했습니다.

핵심은 캐시 도입이 아니라, 캐시가 의미 있게 동작할 수 있는 구조를 먼저 만든 것이었습니다.

4-5. 운영 대응과 개발 경험

SRE의 이미지 기반 배포에 맞춰 remote cache를 연결했고, 앱별로 달라진 설정·HMR 실행은 gext dev, gext build 같은 내부 CLI로 감쌌습니다. 구조는 앱 단위로 나뉘었지만 매일 쓰는 명령은 단순하게 유지했습니다.

5. 결과

기술 성과

시나리오BeforeAfter개선율
static 페이지 수정1748초22초98% ↓
서버 코드만 수정767초68초91% ↓
React 앱 1개 수정1748초204초88% ↓
공통 영역 수정1748초606초65% ↓
로컬 개발환경 시작약 15분약 1분93% ↓
Feature branch 빌드약 10분3초99.5% ↓
2차 빌드 (캐싱)약 115초약 15초87% ↓
dist 사이즈529MB326MB40% ↓

조직 성과

더 중요한 변화는 의사결정 범위였습니다. 이전에는 빌드 구조, 모듈 업데이트, 언어 도입 같은 변경을 어느 스쿼드도 단독으로 밀어붙이기 어려웠지만, 전환 이후에는 각 스쿼드가 담당 앱 안에서 직접 결정할 수 있게 됐습니다.

예를 들어 특정 페이지에 TypeScript를 도입할 때, 예전 같으면 다른 스쿼드와 영향도를 먼저 논의해야 했지만 이제는 담당 스쿼드가 앱 단위에서 바로 적용했습니다.

빌드 시간 단축은 눈에 보이는 성과였고, 본질적 변화는 기술 부채를 작은 단위로 나눠 실제로 해결할 수 있는 구조가 생긴 것이었습니다.

6. 회고

구분첫 번째 시도두 번째 시도
문제 관점빌드 성능조직 + 시스템 구조
해결 단위Webpack config앱 단위 경계
전환 방식일괄점진적
DEV 결과성공성공
OP 결과실패성공
남긴 것교훈 + config 자산기술 성과 + 조직 변화

핵심 세 가지를 얻었습니다.

  1. 레거시의 진짜 문제는 기술이 아니라 누구도 쉽게 바꾸기 어려운 구조일 수 있다.
  2. 공유 시스템 변경은 성능보다 점진적 전환 가능성이 더 중요할 수 있다.
  3. 실패한 시도도 버릴 자산이 아니다. 첫 시도의 결과물이 두 번째 시도의 핵심이 됐습니다.

결국 이 프로젝트는 빌드를 빠르게 만든 사례이면서, 여러 팀이 함께 쓰는 시스템을 실제로 바꿀 수 있는 구조로 다시 설계한 사례였습니다.

← 포트폴리오 목록