How We’re Surviving 600+ Legacy Angular Components While Migrating to Next.js, GraphQL, and a Monorepo
- 원문 링크: https://dev.to/ujja/sunsetting-legacy-angular-how-were-migrating-to-nextjs-graphql-and-a-monorepo-without-a-big-1295
- 번역 생성일: 2026-02-16 (KST)
수명이 긴 프론트엔드에서 흔히 겪는 문제는 비슷합니다. 앱이 커지고 기능이 쌓이면서 기술 부채가 늘고, 개발 속도가 점점 느려집니다.
글의 팀도 그 상황이었습니다.
Angular 14 기반의 큰 애플리케이션(컴포넌트 600개+)이 있었고, 단일(모놀리식) 구조와 복잡성 증가로 개발이 계속 어려워졌습니다. 전면 재작성(빅뱅 리라이트)은 매력적이지만 비용·리스크·비즈니스 중단이 너무 큽니다.
그래서 “한 번에 갈아엎기” 대신, 아래 도구/패턴을 조합해 점진적 마이그레이션을 설계했습니다.
- Next.js
- GraphQL Federation
- 모노레포 아키텍처
- 프레임워크 사이를 잇는 브릿지로서 Web Components
이 글은 아키텍처 구성, 마이그레이션 접근, 그 과정의 교훈을 정리합니다.
기존 Angular 앱은 오랫동안 비즈니스의 핵심이었고, 아래 기능을 담당했습니다.
- 고객/사용자 등록 플로우
- 서비스 제공자 검색
- 결제 처리
- 역할 기반 사용자 관리
- 복잡한 다단계 폼
- AWS Cognito 인증
동작은 했지만, 시간이 지나면서 한계가 뚜렷해졌습니다.
-
모놀리식 아키텍처
- 루트 모듈 1개에 637개 컴포넌트 선언 → 코드 이해/변경이 어려움
-
수동 의존성 주입
- 커스텀 HTTP 서비스를 60곳 이상에서 직접 생성 → Angular DI를 우회
-
강한 결합
- 컴포넌트가 특정 API 응답 형태에 직접 묶임
-
재사용성 부족
- UI 컴포넌트가 Angular 전용 → 다른 곳에서 재사용 불가
-
빌드 느림
- 앱이 커지며 빌드 시간이 계속 증가
-
기술 부채
- Angular 14
- Bootstrap 4
- jQuery 의존
여러 환경(dev/test/uat/prod)까지 운영 중이라, 전면 재작성은 12~18개월 이상 걸리고 비즈니스 리스크가 큽니다.
높은 수준의 구조는 다음과 같습니다.
- Angular는 레거시 UI를 계속 실행
- 신규 기능은 React로 개발
- React 앱을 Web Components 형태로 배포
- GraphQL이 프론트엔드와 백엔드 사이의 API 레이어
- Next.js가 인증을 담당
이렇게 하면 비즈니스 운영을 깨지 않고 기능 단위로 하나씩 교체할 수 있습니다.
기존 시스템을 유지한 채, 일부를 새로운 구현으로 점점 감싸서 교체하는 Strangler Fig 패턴을 채택했습니다.
핵심 기둥 3개:
- 모노레포 기반
- GraphQL 기반 API
- Web Components 브릿지
pnpm + Turborepo로 모노레포를 구성했습니다.
예시 구조:
monorepo/
├── apps/
│ ├── auth-service/
│ ├── graphs/
│ ├── services/
│ └── notification-service/
├── packages/
│ ├── design-system/
│ ├── authentication/
│ ├── logger/
│ ├── database/
│ └── web-components/
- 앱 간 공용 코드 공유
- 엔드투엔드 TypeScript
- 빌드 속도 개선(70% 향상)
- 스택 전반 변경을 하나의 PR로 처리(원자적 PR)
- 버전 관리/릴리스 조율 쉬움
모놀리식 REST 대신, 도메인 기반 GraphQL 서비스들로 분리했습니다.
예시 스키마:
type Business {
id: ID!
name: String!
subscriptionPlans: [Plan!]!
defaultPlanId: Int
}
type Query {
searchBusinesses(country: String!, searchTerm: String!): [Business!]!
}- 도메인 경계가 명확해짐
- 독립 배포 가능
- 강한 타입
- 클라이언트가 필요한 데이터만 요청
- Federation 준비(확장) 구조
React 기능을 Angular 앱 안에 끼워 넣기 위해 Web Components를 사용했습니다.
React 컴포넌트를 Web Component로 래핑:
import { r2wc } from '@r2wc/react-to-web-component';
import { UserRegistrationWithApollo } from './UserRegistration';
const UserRegistrationWC = r2wc(UserRegistrationWithApollo, {
props: {
businessGraphApiUrl: 'string',
providerGraphApiUrl: 'string',
},
});
customElements.define('user-registration', UserRegistrationWC);Angular에서 사용:
<user-registration
[businessGraphApiUrl]="businessApiUrl"
[providerGraphApiUrl]="providerApiUrl">
</user-registration>- 프레임워크에 덜 종속적인 UI 구성
- 점진적 마이그레이션 가능
- 현대적인 React 패턴 활용
- 여러 앱에서 재사용 가능
도메인별 GraphQL 엔드포인트가 여러 개이므로, 클라이언트도 분리해 구성했습니다.
export const createApolloClients = (
businessUri: string,
providerUri: string
) => {
const businessClient = new ApolloClient({
link: authLink.concat(httpLink(businessUri)),
cache: new InMemoryCache(),
});
const providerClient = new ApolloClient({
link: authLink.concat(httpLink(providerUri)),
cache: new InMemoryCache(),
});
return { businessClient, providerClient };
};Next.js API route를 인증(토큰 갱신 등)에서 활용했습니다.
export async function POST(request: Request) {
const { refreshToken } = await request.json();
const newTokens = await refreshCognitoToken(refreshToken);
return Response.json({
accessToken: newTokens.accessToken,
idToken: newTokens.idToken
});
}- 인증용 API routes
- Docker 배포에 적합
- Angular/React 모두에서 공유 가능
- 마이그레이션 후반까지 이어갈 기반
export const Feature = () => {
const { data, loading } = useQuery(GET_DATA_QUERY);
if (loading) return <Spinner />;
return (
<Card>
<CardHeader>
<CardTitle>{data.title}</CardTitle>
</CardHeader>
<CardContent>
{/* Feature implementation */}
</CardContent>
</Card>
);
};const FeatureWC = r2wc(FeatureWithApollo, {
props: {
apiUrl: 'string',
userId: 'string'
}
});
customElements.define('app-feature', FeatureWC);<app-feature
[apiUrl]="apiUrl"
[userId]="currentUser.id">
</app-feature><app-feature *ngIf="featureFlags.useNewFeature"></app-feature>
<legacy-feature *ngIf="!featureFlags.useNewFeature"></legacy-feature>- 플래그 제거
- Angular 컴포넌트 삭제
- 서비스 정리
- 테스트 업데이트
- 주요 기능 15개 마이그레이션 완료
- 빌드 70% 빨라짐
- 중복 코드 40% 감소
- 신규 기능의 80%를 React로 개발
- 마이그레이션 관련 사고 0건
레거시를 “한 번에 갈아엎기”로 끝내지 않아도 됩니다.
모노레포 + GraphQL + Web Components + Next.js 조합으로, 비즈니스는 계속 돌리면서 기능을 하나씩 교체할 수 있었습니다.
핵심은 기술만이 아니라 워크플로우·확신·개발 경험(DX) 입니다. 점진적으로 도구와 패턴을 도입하면서도, 즉시 가치를 만들고 다음 단계를 위한 기반을 쌓을 수 있습니다.