2023 하반기~2025 2026~ → 채널 확장 Phase 1 Phase 2 Phase 3 주요 서비스가 집중된 레포 서비스별 서버, 독립 배포 Host / Remote, 채널 확장 초기 인프라 AWS 인프라, 모노레포 Micro Frontend → 서비스별 독립 배포와 채널 확장이 가능한 아키텍처로 발전 7
서비스별 Next.js 서버 SSR 서비스: EKS 정적 리소스: SSR 전용 CDN CSR 서비스 종목, 주식홈, 커뮤니티, 검색/발견, 연금 등 도메인 단위 CloudFront + S3, JS, CSS, 이미지 Lambda@Edge 서비스 점검, 미리보기 판단 CloudFront + S3 CSR 전용 CDN → SSR은 서비스별 Next.js 서버로 EKS에서 운영, 정적 리소스는 CDN 11
서비스별 Next.js 서버 SSR 서비스: EKS 정적 리소스: SSR 전용 CDN CSR 서비스 종목, 주식홈, 커뮤니티, 검색/발견, 연금 등 도메인 단위 CloudFront + S3, JS, CSS, 이미지 Lambda@Edge 서비스 점검, 미리보기 판단 CloudFront + S3 CSR 전용 CDN → CSR은 서버 없이, CDN 앞단(Lambda@Edge)에서 점검, 미리보기 판단 12
리소스 오토스케일링: CPU, 메모리 사용량 + 시간대 기준 Pod 자동 증감 서비스 A 서비스 B Next.js 서버 Next.js 서버 서비스 C Pod 수십 대 Pod 여러 대 Pod 몇 대 지표 모니터링 적정 Pod 수 유지 지표 모니터링 운영 리소스 × 서비스 수 적정 Pod 수 유지 Next.js 서버 지표 모니터링 적정 Pod 수 유지 적으면 트래픽 감당 불가 ⇄ 많으면 비용 증가 → 성능 측정으로 Pod 수는 최적화, 하지만 서비스가 늘수록 운영 리소스도 함께 증가 15
어려운 구조 카카오페이 앱 웹뷰 A 다른 플랫폼, 일반 웹 플랫폼 정책에 따라 웹뷰 개수가 제한될 수 있음 웹뷰 1개 네이티브 상단바 웹뷰 B 웹뷰 C 서비스 A 서비스 B 서비스 C 한 웹뷰 안에서 서비스 페이지 이동 ✓ 네이티브가 공통 기능을 한 곳에서 제공 ✕ 같은 기능을 서비스마다 개별 구현 → 네이티브가 한 곳에서 제공하던 공통 기능을 서비스 페이지마다 개별 구현해야 함 16
구조 Host: 공통 영역 네이티브가 하던 공통 기능을 웹에서 대신 - 공통 UI: 상단바, 하단탭, 스낵바 - 공통 기능: 인증, 실시간 연결, 화면 전환 Remote: 화면 서비스별 독립 배포 → 공통은 Host 한 곳에서, 서비스 화면은 Remote로 독립 배포 Host 공통 영역 서비스 A 서비스 B Remote 화면 서비스 C 화면 이동 시 공통 영역(Host)은 유지, 서비스 화면(Remote)만 교체 18
영역 Host Remote 코드로 보면 개념 예시 라우팅 카카오페이 앱 어댑터 테마, 인증, 세션 가입, 네이티브 기능 데이터 프리패칭 실시간 연결 카카오뱅크 어댑터 테마, 인증, 세션 가입, 네이티브 기능 공통 UI PC Web 어댑터 테마, 인증, 세션 가입, PC 기능 Remote: 채널이 달라도 같은 코드로 동작 const CHANNEL_ADAPTERS = { PAYAPP: payAppAdapter, KAKAO_BANK: kakaoBankAdapter, PUBLIC_WEB: publicWebAdapter, } satisfies Record< ChannelType, ChannelAdapter>; <CommonDataLayer adapter={CHANNEL_ADAPTERS[channel]}> <RemoteApp /> </CommonDataLayer> → 채널이 늘면 어댑터만 추가, 서비스 화면 코드 변경 최소화 19
부적합 ✗ 부적합 △ 보류 ◦ 채택 @module-federation/ nextjs-mf Next.js Multi-Zones Module Federation ESM + Import Map 유지보수 모드 App Router, RSC 미지원 Zone 간 이동 시 전체 새로고침 번들러별 플러그인 - 전용 코드 필요 플러그인 없이 웹 표준만으로 구현 → 소켓, 화면 상태 유지 불가 webpack, Vite 등 → 프레임워크, 번들러 변화에 영향 적음 → 웹 표준이라 외부 변화에 강한 ESM + Import Map 채택, 대신 공유 라이브러리 버전은 직접 관리 20
내려 주는 HTML <script type="importmap"> { "imports": { "react": ".../_shared/[email protected]", "@remote/home": ".../home/a1b2/index.js" } } </script> Host가 화면 불러오기 const home = await import('@remote/home'); home.mount(container); Remote 번들 안 import { useState } from 'react'; • react는 번들에서 빼고 Host가 1번만 로드(external) • 서비스 이동마다 React 약 190KB (gzip 약 60KB) 다시 받지 않음 → 모듈 이름 → 실제 주소 매핑, 공유 라이브러리는 Host가 제공해 모든 화면이 함께 사용 21
화면 JS 빌드 home/a1b2.js 기존 파일은 그대로, 새 파일만 추가 "home": ".../home/c3d4.js" 이슈 발생 CloudFront Host 재조회 캐시 무효화 반영 캐시 무효화 import-map.json "home": ".../home/a1b2.js" 롤백 + home/c3d4.js 1분마다, 반영 한 줄 교체 재빌드 없음, 이전 JS는 S3에 그대로 import-map.json "home": ".../home/a1b2.js" 같은 줄 되돌림 → 배포와 롤백 모두 Import Map 한 줄 교체, 재빌드와 재기동 없이 반영 22
이유 09 00 국내 장 시작 22 30 미국 장 시작 서버 운영 리소스: 장 시작과 시장 상황에 따른 트래픽 급증, 서버 모니터링과 운영 MFE와의 적합성: Next.js는 하나의 큰 앱에서 라우팅과 렌더링을 관리, 서비스별 화면을 Host 안에서 조립하기 어려움 개발 경험: 로컬 서버 기동, 느린 HMR, 빌드 시간 증가 시간대별 트래픽 추이 → 서버 운영 리소스, MFE와의 적합성, 개발 경험, 세 가지 이유로 서비스 화면(Remote)은 CSR로 전환 23
대응 캐시: 정적 리소스(JS, CSS) 영구 캐시, HTML 캐시 안 함 브라우저 페이지 요청 Host 서버 HTML 응답 SSR Skeleton 미리 받은 API 데이터 공통 UI, Skeleton 화면 API 미리 호출 상단바, 하단탭 modulepreload 미리 받기 브라우저 Remote JS 실행 CDN, 캐시 화면 그리기 CSR → 공통 UI, Skeleton은 SSR, 화면은 CSR: 첫 화면 체감 속도 유지 24
요청 집중 모든 서비스 요청이 Host 서버 한 곳으로 최대 약 7,000 rps FE 서버 기준 대응 역할 축소 화면 렌더링은 브라우저로, 서버는 HTML 뼈대, Import Map 공통 UI, Skeleton SSR + 첫 화면용 최소 프리패치 Next.js → Fastify Express와 함께 검토, 빠른 요청 처리, 자체 직렬화 처리 성능, 안정적 유지보수 → 모든 요청이 모이는 Host, 서버는 최소한의 일만 하도록 가볍게 25
담당 1. 싱글톤으로 관리되어야 하는 것 WebSocket React 히스토리 인증 세션 2. 모든 서비스에서 동일하게 동작해야 하는 것 로그, 에러 수집 공통 UI Remote 담당 Host로 옮긴 사례: WebSocket 연결 기존 환경 화면 A 화면 B 화면 C 화면마다 개별 연결 신규 환경: Host가 하나로 Host, 연결 1개 유지 화면, 화면 안의 상태 서비스별 라이브러리, 비즈니스 로직 구독, 해지 구독, 해지 구독, 해지 → 싱글톤, 모든 서비스 공통 동작은 Host, 나머지는 Remote 26
고정하기 Host ESM 빌드 CDN 업로드 기준 버전 배포 React 등 공유 라이브러리 [email protected] Remote catalog external 같은 버전 으로 빌드 react: 19.2.5 한 버전 주소 기록 PR 코멘트 (패키지, 서비스명 일반화) 번들에서 제외 CI, 버전 일치 확인 공통 패키지 A Remote 8개 공통 패키지 B Remote 14개 Remote는 external을 번들에 넣지 않으므로 런타임에 로드되는 것은 Host가 제공하는 버전입니다. 타입은 통과하지만 런타임 API가 다를 수 있습니다. 운영 Host import map ↔ Remote catalog 매핑 누락 import map 버전 불일치 catalog 미사용 → Host가 기준 버전 배포, Remote는 같은 버전으로 빌드, CI가 일치 여부 확인 27
공통 패키지 저장소 changesets 변경 내용과 버전 올림 범위 기록 사내 패키지 저장소 각 서비스 1.0.2 1.1.0 2.0.0 서비스 A: 2.0.0 서비스 B: 1.1.0 서비스 C: 1.1.0 Nexus → 서비스마다 필요한 시점에 버전 업데이트, 문제가 생기면 그 서비스만 되돌림 29
기존 자동화 Jenkins 배포할 패키지 직접 입력 빌드, publish 의존 관계, 버전, 순서를 개발자가 직접 판단 ⚠ 배포 누락, 순서 오류 흰색: 개발자가 하는 일, 색: 자동 PR 생성 main 머지 자동: alpha 배포 자동: 배포 PR 생성 배포 워크플로 정식 배포 테스트 앱 alpha 검증 대상, 의존 순서 계산 사내 패키지 저장소 배포 PR 머지 버전, 변경 내역 확인 사용 가이드 문서 갱신 Slack 배포 알림 → 패키지 간 의존 관계, 버전, 배포 순서 판단은 워크플로가, 개발자는 코드 수정과 리뷰에 집중 30
3 서비스별 독립 배포 불가, 설정과 공통 코드 중복 앞으로의 과제 채널 확장 AWS 인프라, 모노레포 기존 서비스 전환 아쉬운 점: 공통 코드 관리, 서버 운영 리소스, 채널 확장 대응 성능 가시화 Micro Frontend (Host / Remote) 채널별 서비스 점검 제어 초기 인프라 서비스별 배포 확보 네이티브 의존 최소화, 채널 확장 첫 신대륙: 카카오뱅크 앱 환경 PC웹(WTS), 신규 채널 채널별 이중 관리 해소 상시 점검, 자동 감지 특정 채널 또는 전체 점검 → 아키텍처에 정답은 없음, 그때의 상황과 풀어야 할 문제에 맞는 구조를 설계하고 만들어 온 과정 32