테마
교훈과 회고 - 시작하기 전에 알았으면 좋았을 것들
Dooray 마이크로 프론트엔드 전환 프로젝트에서 얻은 세 가지 핵심 교훈과 점진적 마이그레이션의 총정리.
학습 목표
- Redux를 전체 서비스 필수 기술로 지정한 것이 왜 문제였는지 설명할 수 있다.
- 프래그먼트와 라우팅을 같은 프로젝트로 묶었을 때의 문제점을 이해한다.
- CSS-in-JS(Emotion)를 인프라 패키지의 기반 기술로 선택한 것의 리스크를 파악한다.
- 점진적 마이그레이션의 장점과 한계를 균형 있게 평가할 수 있다.
본문
1. 교훈 1: Redux를 전체 서비스 필수 기술로 지정한 것은 옳지 않았다
Dooray 팀은 프로젝트 초기에 Redux(Redux Toolkit + Redux Saga)를 전체 서비스의 필수 기술로 결정했다. 모든 서비스가 하나의 Redux Store를 공유하고, Redux를 적극 활용하는 방향으로 서비스를 만들어 나갔다.
왜 문제가 되었는가
| 문제 | 상세 |
|---|---|
| 다이나믹 로드의 어려움 | Redux Store는 정적으로 구성되므로, 마이크로 앱이 런타임에 로드될 때 리듀서를 동적으로 추가하기 어려움 |
| 액션의 경계 무너짐 | dispatch된 액션이 패키지를 넘나들어 격리가 불가능 |
| Saga의 전역 영향 | Redux Saga의 이펙트가 서비스 경계를 무시하고 동작 |
| 분리 비용 폭증 | 마이크로 앱마다 스토어를 분리하려 했으나 이미 깊이 결합된 상태 |
동적 리듀서 로딩 라이브러리가 존재하지만, Saga와 결합된 상태에서 서비스 간 경계를 명확히 나누기는 매우 어려웠다.
더 나은 접근법
- Shell(중앙)에는 인증, 전역 설정 등 최소한의 공유 상태만 둔다.
- 각 마이크로 앱은 독자적인 상태 관리 기술을 자유롭게 선택한다.
- 서비스 간 통신이 필요하면 Redux가 아니라 명시적 프로토콜(커스텀 이벤트, 콜백 props 등)을 사용한다.
"특정 라이브러리를 전체 서비스의 필수 기술로 강하게 가져갈 경우, 점진적으로 변화하면서 이것을 뜯어내기에 생각보다 어려움이 많다."
2. 교훈 2: 프래그먼트와 라우팅을 하나의 프로젝트로 묶지 말 것
Dooray에서는 Drive 서비스가 제공하는 라우터(페이지 묶음)와 프래그먼트(다른 서비스에 삽입되는 UI 조각)를 같은 Drive 프로젝트 안에서 관리했다.
왜 문제가 되었는가
라우터와 프래그먼트는 의존성 구조가 본질적으로 다르다.
| 구분 | 라우터 (Route Pages) | 프래그먼트 (Fragment) |
|---|---|---|
| 의존 방향 | Drive의 전체 의존성 사용 | 소비자(Mail 등)의 기술 스택에 영향받음 |
| 번들 크기 | Drive 전체 기능 포함 가능 | 최소한의 코드만 포함해야 함 |
| 기술 스택 | Drive 내부 기술 자유 사용 | 가능한 순수 React API만 사용해야 함 |
| 업데이트 주기 | Drive 서비스 배포 시 | 소비자 서비스의 요구에 따라 |
같은 프로젝트에 두면 프래그먼트가 Drive의 모든 의존성을 물고 들어가게 되어, Mail 서비스가 Drive 프래그먼트를 로드할 때 불필요하게 큰 번들을 받게 된다.
더 나은 접근법
- 라우터는 서비스 앱 프로젝트에 포함한다.
- 프래그먼트는 별도의 경량 패키지로 분리한다.
- 프래그먼트 패키지는 순수 React API로 작성하여 소비자 기술 스택에 영향받지 않도록 한다.
- 프래그먼트는 소비자의 에러 바운더리 안에서 동작하도록 설계한다.
3. 교훈 3: CSS-in-JS(Emotion)를 인프라 패키지의 기반으로 삼지 말 것
Dooray의 UIKit(디자인 시스템)은 Emotion을 기반으로 제작되었다. 모든 서비스가 이 UIKit을 사용했기 때문에, Emotion이 사실상 전체 서비스의 필수 의존성이 되었다.
왜 문제가 되었는가
| 문제 | 영향 |
|---|---|
| 기술 선택권 제한 | 각 서비스 팀이 다른 스타일링 라이브러리를 사용하고 싶어도 Emotion이 강제됨 |
| 번들 중복 | Emotion 런타임이 모든 마이크로 앱에 포함되어야 함 |
| 기술 변화 대응 어려움 | CSS-in-JS 생태계 변화 시 전체 서비스에 영향 |
| SSR/성능 이슈 | Emotion의 런타임 오버헤드가 모든 서비스에 전파 |
더 나은 접근법
- 인프라 레벨 패키지(UIKit 등)는 CSS, CSS Modules, CSS Variables 등 기술 변화에 흔들리지 않는 기본 기술을 기반으로 한다.
- 각 서비스 팀은 자체적으로 스타일링 기술을 선택할 수 있어야 한다.
- 디자인 토큰은 CSS Custom Properties로 제공하면 어떤 스타일링 기술을 사용하든 활용 가능하다.
4. 세 교훈의 공통 패턴
세 가지 교훈은 모두 같은 패턴을 보여준다.
특정 라이브러리/기술을 전체 서비스의 필수 기술로 강하게 결합하면, 마이크로 프론트엔드의 핵심 가치인 "팀 자율성"과 "독립적 진화"가 훼손된다.
| 결정 | 결합도 | 팀 자율성 | 분리 비용 |
|---|---|---|---|
| Redux를 전체 필수 기술로 | 높음 | 없음 | 매우 높음 |
| 프래그먼트를 라우터와 합침 | 중간 | 제한적 | 높음 |
| Emotion을 UIKit 기반으로 | 높음 | 없음 | 높음 |
프로젝트 초기에 마이크로 프론트엔드 전환을 예측하기 어렵더라도, 전환 시점에서 해당 기술을 덜어내는 방향으로 전략을 잡는 것이 중요하다.
5. 점진적 마이그레이션 총정리
전체 전환 흐름 요약
| 단계 | 핵심 작업 | 기간 특성 |
|---|---|---|
| Phase 0 | 현재 시스템 분석, 문제 인식 | 짧음 |
| Phase 1 | 모노레포 내 패키지 분리 (물리적 코드 이동) | 길고 반복적 |
| Phase 2a | 의존성 정리, 패키지 계층 분리, 추상화 | 길고 반복적 |
| Phase 2b | 패키지 매니저 변경, NX 도입, 빌드 설정 | 중간 |
| Phase 3 | Module Federation, 독립 배포 파이프라인 | 중간 |
| 확산 | Drive -> Mail -> 나머지 서비스 순차 적용 | 길고 계속됨 |
점진적 마이그레이션의 장점
| 장점 | 설명 |
|---|---|
| 리스크 분산 | 단계마다 검증하고 문제 시 롤백 가능 |
| 비즈니스 연속성 | 서비스 운영을 중단하지 않고 아키텍처 전환 |
| 팀 학습 | 한 서비스로 경험을 쌓고 다음 서비스에 적용 |
| 유연한 우선순위 | 비즈니스 상황에 따라 전환 속도 조절 가능 |
점진적 마이그레이션의 어려움
| 어려움 | 설명 |
|---|---|
| 과도기 상태 장기화 | 완전한 전환까지 오랜 시간이 소요되며, 중간 상태가 불편함 |
| 두 가지 방식 공존 | 빌드타임 공유와 런타임 공유가 공존하는 기간 발생 |
| 인내심 필요 | 즉각적인 성과가 보이지 않아 팀의 사기가 떨어질 수 있음 |
| 기술 부채 누적 | 과도기 코드(alias, 임시 설정 등)가 기술 부채로 남을 수 있음 |
| 완벽한 설계의 유혹 | 모든 것을 한 번에 완벽하게 바꾸고 싶은 유혹을 이겨야 함 |
6. 이 사례에서 배울 점
이 프로젝트 사례는 하나의 참고 사례일 뿐이다. 프로젝트 리드도 "지금이라면 다르게 했을 것"이라고 밝힌 부분이 여러 곳이다.
적용할 때 주의할 점:
- 조직의 규모와 문화에 맞게 조정해야 한다.
- 기술 선택은 팀의 역량과 경험을 고려해야 한다.
- 과도기 전략은 "완벽한 설계"가 아니라 "충분히 좋은 전환"을 목표로 한다.
- 실수에서 배우되, 동일한 실수를 반복하지 않아야 한다.
핵심 정리
| 교훈 | 문제 | 대안 |
|---|---|---|
| Redux 강제 | 전역 스토어가 서비스 경계를 무시 | Shell에 최소 상태만, 앱별 독립 상태 관리 |
| 프래그먼트+라우터 결합 | 불필요한 의존성이 프래그먼트에 포함 | 프래그먼트를 별도 경량 패키지로 분리 |
| Emotion 기반 UIKit | 모든 서비스에 Emotion 강제 | CSS/CSS Variables 등 안정적 기본 기술 사용 |
공통 원칙: 인프라 레벨에서는 변동성이 낮은 기본 기술을 사용하고, 각 팀에게 기술 선택의 자율성을 부여해야 마이크로 프론트엔드의 가치를 극대화할 수 있다.
점진적 마이그레이션 핵심:
- 한 번에 완벽하게 바꾸지 않는다.
- 매 단계마다 검증하고 롤백할 수 있어야 한다.
- 과도기 상태를 인내하되, 방향성을 잃지 않아야 한다.
- TypeScript는 안전한 코드 이동의 핵심 도구이다.
다음 단계
다음 장에서는 레거시 환경에서의 마이크로 프론트엔드 통합을 다룬다. 이미 운영 중인 레거시 시스템에 마이크로 프론트엔드를 도입할 때의 전략과 기법을 살펴본다.