Skip to content

교훈과 회고 - 시작하기 전에 알았으면 좋았을 것들

Dooray 마이크로 프론트엔드 전환 프로젝트에서 얻은 세 가지 핵심 교훈과 점진적 마이그레이션의 총정리.

학습 목표

  1. Redux를 전체 서비스 필수 기술로 지정한 것이 왜 문제였는지 설명할 수 있다.
  2. 프래그먼트와 라우팅을 같은 프로젝트로 묶었을 때의 문제점을 이해한다.
  3. CSS-in-JS(Emotion)를 인프라 패키지의 기반 기술로 선택한 것의 리스크를 파악한다.
  4. 점진적 마이그레이션의 장점과 한계를 균형 있게 평가할 수 있다.

본문

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 3Module Federation, 독립 배포 파이프라인중간
확산Drive -> Mail -> 나머지 서비스 순차 적용길고 계속됨

점진적 마이그레이션의 장점

장점설명
리스크 분산단계마다 검증하고 문제 시 롤백 가능
비즈니스 연속성서비스 운영을 중단하지 않고 아키텍처 전환
팀 학습한 서비스로 경험을 쌓고 다음 서비스에 적용
유연한 우선순위비즈니스 상황에 따라 전환 속도 조절 가능

점진적 마이그레이션의 어려움

어려움설명
과도기 상태 장기화완전한 전환까지 오랜 시간이 소요되며, 중간 상태가 불편함
두 가지 방식 공존빌드타임 공유와 런타임 공유가 공존하는 기간 발생
인내심 필요즉각적인 성과가 보이지 않아 팀의 사기가 떨어질 수 있음
기술 부채 누적과도기 코드(alias, 임시 설정 등)가 기술 부채로 남을 수 있음
완벽한 설계의 유혹모든 것을 한 번에 완벽하게 바꾸고 싶은 유혹을 이겨야 함

6. 이 사례에서 배울 점

이 프로젝트 사례는 하나의 참고 사례일 뿐이다. 프로젝트 리드도 "지금이라면 다르게 했을 것"이라고 밝힌 부분이 여러 곳이다.

적용할 때 주의할 점:

  1. 조직의 규모와 문화에 맞게 조정해야 한다.
  2. 기술 선택은 팀의 역량과 경험을 고려해야 한다.
  3. 과도기 전략은 "완벽한 설계"가 아니라 "충분히 좋은 전환"을 목표로 한다.
  4. 실수에서 배우되, 동일한 실수를 반복하지 않아야 한다.

핵심 정리

교훈문제대안
Redux 강제전역 스토어가 서비스 경계를 무시Shell에 최소 상태만, 앱별 독립 상태 관리
프래그먼트+라우터 결합불필요한 의존성이 프래그먼트에 포함프래그먼트를 별도 경량 패키지로 분리
Emotion 기반 UIKit모든 서비스에 Emotion 강제CSS/CSS Variables 등 안정적 기본 기술 사용

공통 원칙: 인프라 레벨에서는 변동성이 낮은 기본 기술을 사용하고, 각 팀에게 기술 선택의 자율성을 부여해야 마이크로 프론트엔드의 가치를 극대화할 수 있다.

점진적 마이그레이션 핵심:

  • 한 번에 완벽하게 바꾸지 않는다.
  • 매 단계마다 검증하고 롤백할 수 있어야 한다.
  • 과도기 상태를 인내하되, 방향성을 잃지 않아야 한다.
  • TypeScript는 안전한 코드 이동의 핵심 도구이다.

다음 단계

다음 장에서는 레거시 환경에서의 마이크로 프론트엔드 통합을 다룬다. 이미 운영 중인 레거시 시스템에 마이크로 프론트엔드를 도입할 때의 전략과 기법을 살펴본다.

다음: 레거시 통합 기초 - 레거시 환경의 MFA ->