✏️MFA 와 FSD 와 MONO-REPO

ZYE KIM·2026년 2월 9일

Tech log

목록 보기
13/15

들어가며

서비스 리뉴얼을 요청사항 중, 두 개의 독립적인 B2B 서비스하나의 프로젝트로 통합하면서도, 필요에 따라 특정 모듈을 독립적인 프로젝트로 즉시 분리할 수 있는 고도의 유연성이 요구되었다. 😅 (2년차인 나에게 또 하나의 도전을..선사..함)
그러면서 알게된 프론트엔드 프로젝트 아키텍처를 간단히 정의하고, 요구사항에 맞는 아키텍처 선택의 이유에 대해 설명하려고 한다.

1. 아키텍처 및 구조 설계 관련 용어 정리

① 모놀리식 (Monolithic)

📣 모든 기능이 하나의 코드 베이스와 하나의 빌드 단위로 묶인 구조

😊: 개발 초기 속도가 빠르고 배포 파이프라인이 단순

😡: 서비스 규모가 커지면 코드 간 의존성이 꼬여 수정이 어렵고, 전체를 다시 빌드해야 하므로 배포 효율이 떨어짐

② 모노레포 (Monorepo)

📣 두 개 이상의 독립적인 프로젝트를 하나의 레포지토리에서 관리하는 방식. 도메인별로 독립적인 패키지 분리가능

😊: 프로젝트 간 코드 공유(Shared 패키지)가 쉬워 의존성 관리 용이, 일관성유지하기 쉬움

😡: 레포지토리 크기가 커지며, 빌드 도구(pnpm, Turborepo 등)에 대한 숙련도가 필요

이미지 출처: Moving from multiple repositories to a lerna-js mono-repo

③ MFA (Micro Frontends Architecture)

📣 MSA(Microservices Architecture)의 프론트 버전으로 독립적으로 배포 가능한 작은 단위로 쪼개는 설계 방식

😊: 서비스별 독립 배포, 기술 스택의 자율성, 특정 모듈의 즉각적인 독립 프로젝트화 가능.

😡: 초기 설정이 복잡하며, 런타임 시 의존성 충돌을 관리 필요. 많은 프론트엔드 개발자 필요.

④ FSD (Feature-Sliced Design)

📣 기능 중심으로 폴더를 계층화하는 프론트엔드 전용 설계 패턴

😊: 구조 명확하고 코드의 응집도가 높고, 특정 기능을 떼어내기 매우 유리, 유지보수성이 향상

😡: 폴더 구조가 깊어지고 모든 작업자가 아키텍처에 대해 학습이 필요

이미지출처: FSD공식문서

2. 요구사항에 맞춘 최적의 아키텍처: "MFA 기반 모노레포 + FSD"

요구 사항을 다시 정리 하자면, 🤦‍♂️"두개의 서비스를 합치나, 언제든 특정 모듈을 뽑아서 별도 프로젝트로 만들 수 있어야 한다"
때문에 리서치 한 결과로 생각해보면, 모노 레포 + MFA로 한 레포지토리에 두개 도메인을 관리하고
각 도메인 폴더는 언제든 특정 모듈을 뽑아서 프로젝트를 생성할 수 있게 feature, entities 단위로 구조를 짜려고 한다

  • 거시적(Macro) 관점 - 모노레포 & MFA: 물리적인 프로젝트 분리. SCM과 특송을 별도의 앱으로 격리하여 서로 영향을 주지 않게 함.
  • 미시적(Micro) 관점 - FSD: 프로젝트 내부의 논리적 분리. 특정 기능(모듈)이 자기 완결성을 갖도록 설계하여 복사-붙여넣기만으로 기능 이전이 가능하게 함.

3. 추천 아키텍처 폴더 구조 예시

① 거시적 - Monorepo (pnpm Workspaces)


root/
├── apps/
│   ├── shell/                # 메인 포털 (SCM + 특송을 담는 그릇)
│   ├── scm-service/          # 공급망 관리 App
│   └── express-service/      # 국제 특송 관리 App
├── packages/
│   ├── shared-ui/            # 공통 버튼, 입력폼 등 Design System
│   ├── shared-api/           # Axios 인스턴스, 공통 API 호출 로직
│   └── shared-utils/         # 날짜 포맷팅, 권한 체크 등 공통 함수
├── package.json
└── pnpm-workspace.yaml

② 미시적 - 개별 App 내부 (FSD 구조)

각 모듈이 독립성을 가짐.

src/
├── app/                      # 앱의 진입점 (Provider, Router 설정)
├── pages/                    # 라우트 페이지 (예: 운송장 목록 페이지)
├── widgets/                  # 복잡한 컴포넌트 조합 (예: 상단 네비바 + 검색창)
├── features/                 # 실제 비즈니스 가치 (예: 운송장-출력, 배송-추적)
│   └── ship-tracking/        # 이 폴더만 떼어내면 다른 프로젝트에서도 사용 가능
│       ├── ui/               # 해당 기능 전용 컴포넌트
│       ├── model/            # Pinia 스토어 (특송 서비스 전용 상태)
│       └── api/              # 해당 기능 전용 API 호출
├── entities/                 # 비즈니스 엔티티 (예: 화물, 고객, 기사)
└── shared/                   # 프로젝트 내부 공통 모듈

결론: 왜 이 구조인가?

1️⃣재사용성: features/ 하위의 특정 폴더와 packages/shared만 복사하면, 당장 새로운 B2B 서비스를 런칭가능
2️⃣유연성: vite Module Federation 을 통해 필요한 시점에만 모듈을 로드하므로 초기 로딩 속도 최적화에 유리



BUT 작업자는 나 혼자임

😭MFA를 사용해 독립배포 가능하게 하자니 초기 세팅 및 유지 보수하는데 업무 부담이 클 것 같고,
😭FSD를 구조대로 넣자니 관리 포인트가 너무 많아져 역시 혼자 업무하는데 개발 속도가 나지 않을 것같다...
🔔그래서 b2b 프로젝트에서 작업해봤을 때, 고민이 되었던 부분을 녹여 위에서 조사한 각 장점..?의 일부를 조금씩 더해서 아래와 같이 구조를 설계해 보았다.

src/
├── apps/
│   ├── scm-service/           # 공급망 관리 앱
│   │   ├── views/         # 도메인별 페이지
│   │   │   ├── order/     # 주문 도메인
│   │   │   │   ├── components/
│   │   │   │   ├── store.ts
│   │   │   │   └── OrderList.vue
│   │   │   └── product/   # 상품 도메인
│   │   └── main.ts
│   └── express-service/       # 특송 관리 앱 (구조 동일)
│
├── packages/                  # 재사용 가능한 "부품"||"재료"들
│   ├── shared/                # [Pure Shared] 비즈니스 로직 없음
│   │   ├── ui/                # 공통 헤더,푸터, 테이블, 툴팁, 버튼
│   │   ├── api/               # Axios 인스턴스, Interceptor
│   │   └── utils/             # 포맷팅, Swal Wrapper
│   └── auth/                  # [Auth Domain] 로그인, 권한체크, 토큰관리
│       ├── components/
│       └── store/
│
├── layout/                    # 공통 레이아웃
│   ├── DefaultLayout.vue
│   └── AuthLayout.vue

다시 결론 : 왜 이 구조인가?

1️⃣ 서비스 경계가 명확해서 나중에 서비스가 확장되어 개발자가 늘어나 팀이 커진다면! MFA도입해 각 독립적 배포 가능하게 확장 할수 있지 않을까.
2️⃣ FSD 구조에 맞추면 혼자 개발 생산성에 좋지 않아서 도메인 단위로 views 분리해서 나중에 필요한 도메인만 뽑아 새로운 프로젝트를 생성하기 쉽지 않을까.



이렇게..정리를 해보았고.. 이제 시작이닿
그래도 폴더가 많아지네요 ㅎ..........ㅎ

profile
주니어 프론트엔드개발자

0개의 댓글