포탈 통합 셸을 먼저 못 만든 것
처음엔 모듈마다 독립 앱으로 쪼개는 “마이크로 프론트엔드”가 멋져 보였습니다. 큰 회사들이 쓴다는 패턴이었으니까요. 그래서 11개 업무 모듈을 11개의 독립 앱으로 나누는 인프라부터 깔았습니다. 화면 한 장 만들기도 전에요. 하지만 1인 개발 + 한 대 서버라는 현실에서, 그건 멋이 아니라 과설계였습니다. 결국 모든 모듈을 단일 포탈 하나로 다시 합쳤습니다.
용어부터 — MFE와 통합 셸
섹션 제목: “용어부터 — MFE와 통합 셸”11개 모듈을 11개 앱으로 쪼개다
섹션 제목: “11개 모듈을 11개 앱으로 쪼개다”프로젝트 초기, 대기업 사례로 자주 언급되는 MFE 패턴에 끌렸습니다. 모듈을 독립적으로 배포하고, 모듈마다 별도 포트와 경로 접두사(예: /m/<모듈>)를 두고, 필요하면 따로 떼어 굴린다 — 청사진은 그럴듯했습니다. 그래서 업무 모듈들을 각각 독립 앱으로 분리하고 모듈마다 별도 포트와 경로 접두사를 박는 작업을 했습니다. 화면을 채우기도 전에 “쪼개는” 인프라부터 깐 것입니다.
그런데 깔고 나니 곧바로 마찰이 시작됐습니다. 불과 이틀 뒤에는 URL에서 모듈 접두사(/m/)를 제거해 경로를 단순화하는 작업을 했습니다. 쪼개기 위해 붙였던 구조가 벌써 거추장스러워지기 시작한 것입니다. 그리고 약 한 달 뒤, 결국 분리해 둔 route group을 평탄화(flatten)해 단일 구조로 합치는 큰 정리를 했습니다.
| 시점 | 한 일 | 방향 |
|---|---|---|
| 2026-04-24 | 11개 업무 모듈을 독립 앱으로 분리 (MFE 구축) | 쪼개기 → |
| 2026-04-26 | URL에서 모듈 접두사 /m/ 제거, 경로 단순화 | ← 되돌리기 시작 |
| 2026-05-22 | route group 평탄화 — 분리 wrapper 제거, 단일 구조로 합침 | ← 단일 포탈로 회귀 |
표를 보면 흐름이 분명합니다. 한 달 남짓 동안 “쪼개기 → 단순화 → 통합”으로 방향이 한 바퀴 돌았습니다. 쪼개는 데 쓴 시간과 다시 합치는 데 쓴 시간이 둘 다 들었고, 그 사이 새 가치는 거의 늘지 않았습니다.
무엇이 전제 오류였나
섹션 제목: “무엇이 전제 오류였나”핵심은 패턴 자체가 나쁘다는 게 아닙니다. MFE는 좋은 패턴입니다 — 그 전제가 맞는 곳에서는요. MFE가 빛나는 전제는 “여러 팀이 서로 다른 속도로, 서로 독립적으로 배포해야 하는” 상황입니다. 그런데 이 프로젝트의 전제는 정반대였습니다. 개발자는 한 명이고, 배포가 돌아가는 곳은 개발 서버 한 대였습니다. 독립 배포할 팀이 없는데 독립 배포 인프라를 깐 것입니다.
쪼갠 대가 — 보이지 않는 세금
섹션 제목: “쪼갠 대가 — 보이지 않는 세금”독립 앱이 여러 개가 되면 거의 모든 것이 곱절이 됩니다. 그런데 이 비용은 한눈에 안 보입니다. 사용자에게 보이는 화면은 그대로인데, 뒤에서 유지비만 조용히 불어나기 때문입니다. 그래서 “보이지 않는 세금”이라 부를 만합니다.
구체적으로 어떤 것들이 곱절이 됐는지 봅시다.
- 빌드 — 앱이 N개면 빌드도 N번. 한 줄 고쳐도 여러 앱을 다시 빌드해야 합니다.
- 공유 인프라 — 인증, 공통 컴포넌트, 디자인 토큰을 앱마다 똑같이 심고 동기화해야 합니다. (이게 어긋나면 화면마다 다른 파란색 같은 사고로 이어집니다.)
- 화면 이동 — 모듈에서 모듈로 넘어갈 때 앱 경계를 넘게 되는데, 그 경계에서 깨지기 쉬운 지점이 생깁니다.
- 인지 부담 — 1인 개발자가 머릿속에 “지금 어느 앱을 만지고 있지?”를 항상 들고 있어야 합니다.
| 비용 항목 | 독립 앱 N개 | 단일 포탈 |
|---|---|---|
| 빌드 횟수 | 앱 수만큼 (N번) | 1번 |
| 인증·토큰·공통 컴포넌트 | 앱마다 심고 동기화 | 한 벌을 전 모듈이 공유 |
| 모듈 간 화면 이동 | 앱 경계를 넘어 깨지기 쉬움 | 같은 앱 안 이동 — 경계 없음 |
| 얻는 이득(독립 배포) | 팀이 없어 못 누림 | (애초에 불필요) |
단일 포탈로의 회귀 — 그리고 구조는 남긴다
섹션 제목: “단일 포탈로의 회귀 — 그리고 구조는 남긴다”결국 방향을 틀었습니다. 단일 Next.js 앱 하나에 모든 모듈 화면을 route group으로 담는 구조로 회귀했습니다. 전자결재도 별도 앱이 아니라 포탈 안의 한 route group(예: (sign))이 됐고, 어드민과 업무 모듈도 각각의 그룹(예: (portal))으로 정리됐습니다. 모듈 경계는 폴더로 유지하되, 빌드·배포·인증·패키지는 전부 하나로 공유합니다.
처음에 깔았어야 할 건 “담는 셸”이었다
섹션 제목: “처음에 깔았어야 할 건 “담는 셸”이었다”되돌아보면 첫날 우선순위가 뒤집혀 있었습니다. 가장 먼저 만들었어야 할 건 모듈을 쪼개는 인프라가 아니라, 모듈을 담는 통합 셸이었습니다. 순서를 바로 세우면 이렇습니다.
- 셸을 세운다. 네비게이션·인증·공통 레이아웃·디자인 토큰을 가진 단일 셸 하나를 먼저 만듭니다.
- 모듈을 폴더로 담는다. 새 모듈은 셸 안의 route group(폴더)으로 추가합니다. 경계는 폴더로 유지하되 인프라는 공유합니다.
- 분리는 보류한다. 독립 앱으로 떼어내는 일은 하지 않습니다. 대신 미래에 떼어낼 수 있도록 구조의 자리만 남겨 둡니다.
- 정말 아플 때 떼어낸다. 특정 모듈의 트래픽이 실제로 커졌을 때, 그때 그 모듈만 독립 영역으로 분리합니다.
네비게이션·인증·공통 레이아웃·디자인 토큰을 가진 셸을 하나 세우고 그 안에서 모듈을 한 장씩 늘려 갔다면, MFE를 깔았다가 걷어내는 한 달짜리 왕복은 애초에 없었을 것입니다.
분리는 언제 해도 늦지 않습니다. 아플 만큼 커진 다음에 떼어내면 됩니다. 반대로 처음부터 분리하면, 분리가 주는 이득(독립 배포)은 하나도 못 누리면서 분리의 비용(곱절의 유지비)만 먼저 냅니다. “나중에 합치면 되지”보다 “나중에 쪼개면 되지”가 거의 항상 더 쌉니다 — 합치는 것보다 쪼개는 게 늘 쉽기 때문입니다.
이 글은 SL.AIMS를 만들며 겪은 현장 회고 중 하나입니다. 전체 그림은 〈사례연구: SL.AIMS〉에, 관련 글은 〈디자인 시스템을 통일하지 않고 시작한 대가〉에 있습니다.