레거시 Next.js를 모노레포로 옮기기 위한 설계와 전환 계획
1. 개요
본 문서에서는 Legacy Web과 Mobile을 모노레포 프로젝트로 통합하는 설계와 전환 전략을 제시한다.
2. 현재 상황 & 문제점
기술 스택
- Next.js 12.3.6 + JavaScript(ES6)
- MobX 6.5.0, Material-UI v4
- React Query v3, Axios, npm
주요 문제
- 개발 환경 복잡
- Express 기반 커스텀 서버(500+줄), API 프록시 중복
- 환경 변수 관리 난이도 높음
- 폴더 구조 비표준화
- v1/v2 혼재, Atomic Design 일부 적용
- 컴포넌트 재사용성 낮음
- 코드 및 의존성 중복
- 동일 로직·UI를 두 프로젝트에 중복 구현
- 의존성 100% 중복 설치
- 기술 부채
- Next.js 12, React 17, MUI v4 등 구버전
- JavaScript → TypeScript 필요
# 문제점에 대한 자세한 설명
Express 기반 커스텀 서버(500+줄), API 프록시 중복
// server/index.js (27-50행)
server.post("/api/v2/*/upload", upload.single("file"), async (req, res) => {
// 동일한 헤더 처리 로직이 10번 이상 반복됨
const headers = { ...req.headers };
delete headers["content-length"];
delete headers.host;
// ...
});
// server/index.js (52-86행) - 거의 동일한 코드 패턴
server.post("/api/v2/*", async (req, res) => {
// 동일한 헤더 처리 로직 반복
const headers = { ...req.headers };
delete headers["content-length"];
delete headers.host;
// ...
});
- server/index.js: 985줄의 거대한 파일
- 동일한 헤더 처리 로직이 10번 이상 반복
- API 프록시 로직 중복 (axiosUrl, axiosFileUrl)
환경 변수 관리 난이도 높음
// Dockerfile에서 22개의 환경변수 ARG/ENV 정의
ARG ENV_ALB_URL
ARG ENV_ALB_PORT
ARG ENV_API_VERSION
// ... 22개 변수
// server/server.utils.js - 환경별 분기 로직
const axiosUrl = (req) => {
return process.env.NODE_ENV === "local" ?
`${process.env.ENV_LOCAL_API_HOST}:${process.env.ENV_LOCAL_API_PORT}${req.url}`:
process.env.NODE_ENV === "development" ?
`${process.env.ENV_DEV_API_HOST}:${process.env.ENV_DEV_API_PORT}${req.url}` :
`${process.env.ENV_ALB_URL}:${process.env.ENV_ALB_PORT}${req.url}`;
};
- Dockerfile에 22개 이상의 환경변수 정의
- 환경별 분기 로직이 여러 파일에 분산
동일한 페이지에서 v1/v2 컴포넌트 혼재
// pages/inventory/center/search/product/index.js (69줄)
import MuiTableSelectOneV2 from "/components/common/MuiTable/MuiTableSelectOneV2"; // V2 사용
import MuiTableTitle from "/components/common/MuiTable/MuiTableTitle"; // V1 사용
import MuiTableWrapper from "/components/common/MuiTable/MuiTableWrapper"; // V1 사용
import AgGrid from "/components/common/AgGrid"; // V1 사용
// 1163줄에서 V2 컴포넌트 사용
<MuiTableSelectOneV2
name={"userSelectConnectSubAccountListSelect"}
columns={USER_SELECT_CONNECT_SUB_ACCOUNT_LIST_SELECT_COLUMNS}
// V2 방식의 props
/>
// 하지만 동일 페이지에서 V1 컴포넌트도 사용
<MuiTableTitle titleText={getLang("WD000183")} /> // V1 방식
<MuiTableWrapper> // V1 방식
- components/common/ (v1) + components/v2/ 혼재
- v1, v2 컴포넌트의 차이점 파악에 개발 시간 소요
- 스타일링 방식 및 props 구조 다름 → 일관성 없는 UX
컴포넌트 재사용성 낮음
components/common/MuiTable/
├── MuiTable/ # 기본 테이블
├── MuiTable2/ # 기본 테이블 변형
├── MuiTableSelectOne/ # V1 단일 선택
├── MuiTableSelectOneV2/ # V2 단일 선택
├── MuiTableForSelectedRows/ # V1 선택된 행 표시
├── MuiTableForSelectedRowsV2/ # V2 선택된 행 표시
├── MuiTableSelectV2/ # V2 다중 선택
├── MuiCustomTable/ # 커스텀 테이블
├── MuiTableExpand/ # 확장 가능 테이블
├── MuiTableWithpaging/ # 페이징 포함 테이블
└── ... 15개 더
- 25개 이상의 테이블 컴포넌트 - 기본 기능이 같은데도 불구하고 중복 구현
- 7개의 모달 컴포넌트: Modal, MuiTableClickModal, CommonAlertModal 등 유사 기능 중복
- 공통화 없이 페이지별로 컴포넌트 코드를 복사 붙여넣기하여 사용
3. 모노레포 전환 필요성
- 통합 개발 환경
- 단일 저장소·개발 서버 → Turbo 병렬 빌드 & Hot Reload 최적화
- 코드 재사용 극대화
- @repo/ui, @repo/shared, @repo/react-query 등 패키지화
- 최신 스택 적용
- Next.js 15 App Router, TypeScript, Zustand, TanStack Query v5, Zod
- DX 향상
- ESLint/Prettier/Husky/Lint-staged 도입
- 통합 스크립트: pnpm dev:web, pnpm dev:admin
| AS-IS | TO-BE | 전환 이유 / 기대 효과 |
|---|---|---|
| JavaScript | TypeScript | 타입 안정성 확보, 대규모 코드베이스 유지보수 용이, 런타임 오류 사전 방지 |
| Next.js 12 + Page Router | Next.js 15 + App Router | Next.js 12는 장기 지원 종료 예정, App Router로 성능·데이터 패칭 방식 개선 |
| Material-UI v4 | 커스텀 디자인 시스템 | MUI v4는 유지보수 중단, 디자인 일관성 강화 및 불필요한 의존성 제거로 번들 사이즈 감소 |
| MobX | Zustand | MobX TS 지원 미흡, 경량·간단한 API 제공, TypeScript 친화적 |
| React Query v3 | TanStack Query v5 | v3는 Maintenance Mode, 최신 버전에서 캐싱·동기화 성능 및 DevTool 개선 |
| npm | pnpm | 디스크 공간 절약, 설치 속도 향상, 모노레포 환경 최적화 |
| npm run build | Turbo build | 빌드 캐싱·병렬 처리 지원, 증분 빌드로 빌드 시간 단축 |
4. 점진적 배포 및 라우팅 전략
ECS 서비스 구성
ECS Cluster: (서비스명)-cluster
├── (서비스명)-web-service (NEW) - 모노레포 Web App
├── (서비스명)-mobile-service (NEW) - 모노레포 Mobile App
├── (서비스명)-admin-ui-service (NEW) - 모노레포 Admin App
├── (서비스명)-ui-service (LEGACY) - 레거시 Web
└── (서비스명)-mobile-ui-service (LEGACY) - 레거시 Mobile
ALB 라우팅 규칙
Application Load Balancer
├── Listener :443 (HTTPS)
│ ├── Path /* → (서비스명)-ui-service (Legacy Web)
│ └── Path /v2/* → (서비스명)-web-service (Monorepo Web)
│
├── Listener :8443 (HTTPS)
│ └── Path /* → (서비스명)-mobile-ui-service (Legacy Mobile)
│ └── Path /v2/* → (서비스명)-mobile-service (Monorepo Mobile)
│
└── Listener :9443 (HTTPS)
└── Path /* → (서비스명)-admin-ui-service (Monorepo Admin)
전환 방식
- 기존 경로(/) 요청 → 레거시 서비스로 라우팅
- 신규 개발 페이지(/v2/*) 요청 → 신규 web-service, mobile-service로 라우팅
- Admin 서비스는 독립 포트(9443)에서 신규 모노레포 버전 운영
목적
- 신규 개발된 페이지는 /v2/* 경로를 통해 제공
- 기존 서비스와 신규 서비스 동시 운영하여 안정적인 마이그레이션 가능
- 기능별/페이지별 점진적 전환으로 리스크 최소화
- 최종적으로 / 경로를 신규 환경으로 전환
5. 새로운 아키텍처
(서비스명)_frontend/
├── apps/
│ ├── admin/ # 관리자 서비스
│ ├── mobile/ # Mobile
│ └── web/ # Web
├── packages/
│ ├── ui/ # 디자인 시스템
│ ├── shared/ # 비즈니스 로직
│ ├── react-query/
│ ├── zustand/
│ ├── zod/
│ ├── configs/
│ └── eslint-config/
- packages 관리(패키지화)를 통해 코드 재사용 극대화
6. 마이그레이션 단계
Phase 1: 기반 구조 구축 (pnpm, Turbo, 공통 패키지, CI/CD)
Phase 2: UI 라이브러리 구축 (TS 전환, Atomic Design, Storybook, 디자인 토큰)
Phase 3: Web, Mobile App 마이그레이션 (커스텀 서버 제거, 반응형·성능 최적화)
Phase 4: 운영 최적화 (번들 최적화, 모니터링·로그 통합, 문서화)
7. 기대 효과
개발 생산성
- 병렬 빌드 및 Turbo 캐시로 빌드 시간 60~70% 단축
- 공통 코드 90% 이상 재사용, 수정·배포 횟수 절반으로 감소
코드 품질
- 타입 안정성·자동완성·안전한 리팩토링
- ESLint + Prettier로 스타일 일관성 확보
운영 효율성
- 변경된 앱만 배포
UI/UX 개선
- 2.0 디자인 시스템 도입
