목록으로

프론트엔드 아키텍처 마이그레이션 (VSA 하이브리드)

2026년 4월Frontend Developer주식회사 루멘테라
Next.js (App Router)ReactTypeScriptNode.js CLIPlaywrightGitHub Actions

Overview

수백 개 파일 규모의 프론트엔드 구조를 재설계했습니다. 후보 4안을 비교한 뒤, 의존성 전수조사 결과를 근거로 교과서적인 정답(완전한 Vertical Slice)을 채택하지 않고 하이브리드 구조를 택했습니다.

기능이 늘면서 관련 코드가 레이어별로 흩어져 변경 영향 범위를 예측하기 어려웠습니다. FSD·Screaming Architecture·Vertical Slice·점진적 하이브리드 네 가지를 비교했고, 여기서 멈추지 않고 실제 hooks·types·api가 몇 개의 라우트에서 쓰이는지 전수조사했습니다. 그 결과 '완전한 코로케이션'이 이 코드베이스에서는 불가능하다는 사실이 드러나 하이브리드로 방향을 정했고, 정한 규칙은 검증 CLI로 만들어 CI에 고정했습니다.

Decisions

레이어 기준으로 나눌 것인가, 라우트 기준으로 모을 것인가

App Router 호환성 · 마이그레이션 비용 · 현재 코드베이스와의 거리, 이 세 기준으로 후보를 평가했습니다.

  • 기각

    FSD (Feature-Sliced Design)

    레이어 계층을 라우팅 바깥에 두는 방식이라, App Router가 이미 소유하고 있는 폴더 구조와 역할이 겹칩니다. 재배치 범위도 가장 넓고, 슬라이스마다 배럴 파일을 강제하면 트리셰이킹에도 영향이 갑니다.

  • 기각

    Screaming Architecture (도메인 폴더링)

    App Router와 충돌하지 않고 현재 구조에서 자연스럽게 진화할 수 있지만, 코드가 여전히 라우트 바깥에 놓입니다. '이 화면을 고치려면 어디를 봐야 하는가'라는 원래 문제가 남습니다.

  • 채택

    Vertical Slice (라우트 코로케이션)

    Next.js가 기본으로 지원하는 방식이고, 이미 이 패턴으로 만들어져 있던 화면이 코드베이스 안에 있어 실증 사례로 삼을 수 있었습니다.

  • 기각

    점진적 FSD-Lite (핵심 원칙만 차용)

    절충안이지만 결과물이 '우리만의 규칙'이 됩니다. 외부 레퍼런스와 온보딩 자료를 쓸 수 없게 되는 비용이 절충의 이득보다 크다고 봤습니다.

Vertical Slice를 유력안으로 정하고, 그대로 적용이 가능한지 검증하는 단계로 넘어갔습니다.

교과서적인 Vertical Slice를 그대로 적용할 수 있는가

원칙이 맞는지는 실제 코드를 봐야 알 수 있다고 판단했습니다. 훅·타입·API가 각각 몇 개의 라우트에서 쓰이는지 전수조사했습니다.

  • 기각

    완전한 Vertical Slice (모든 코드를 라우트 안으로)

    훅의 65%가 여러 라우트를 가로질렀고, 타입은 100%가 전역 선언이라 물리적으로 이동조차 불가능했습니다. API도 대부분이 여러 라우트에서 직접 쓰이고 있었습니다.

  • 채택

    하이브리드 (컴포넌트만 코로케이션)

    컴포넌트는 라우트에 모으고, 훅·타입·API는 공통 레벨에 유지합니다. 단일 라우트 전용인 것만 선택적으로 끌어옵니다.

정답으로 알려진 구조라도 이 코드베이스에는 맞지 않는다는 결론이었습니다. 대신 새로 정한 규칙은 검증 CLI로 만들어 모든 PR에서 자동 실행되도록 고정했습니다.

Structure

VSA 하이브리드: 레이어별 분산 구조와 채택한 구조, 그리고 판단 근거가 된 의존성 전수조사전 — 레이어별로 분산app/components/containers/hooks/types/api/한 기능의 파일이 여섯 폴더에 흩어짐 (● 표시)후 — 하이브리드app/[route]/_components/라우트 전용공통 레벨 유지hooks · types · api컴포넌트만 라우트로 이동 · 나머지는 공통 레벨판단 근거 — 의존성 전수조사hooks여러 라우트 공유 65%35%types여러 라우트 공유 100%api여러 라우트 공유 80%20%옮길 수 있는 비율(파랑)이 낮아 완전한 코로케이션은 채택하지 않음
완전한 코로케이션이 정답처럼 보였지만, 실제 의존성을 전수조사하니 대부분이 여러 라우트를 가로질렀다. 그래서 컴포넌트만 옮기고 나머지는 공통 레벨에 두는 하이브리드를 택했다.

Challenges & Solutions

수백 개 파일을 깨뜨리지 않고 옮기기

Before

한 번에 옮기면 회귀를 잡을 방법이 없고, 어디서 깨졌는지 특정하기도 어려운 규모

After
  • 이동 전에 라우트 스모크 테스트 27개를 먼저 만들어 회귀 안전망 확보 (27/27 통과 유지)
  • 네이밍 정규화 → 단일 파일 → 다중 파일 → 도메인별 → 정리 순으로 Phase를 나눠 티켓 11개로 분할 진행
  • 컨테이너 약 92개·공통 모달 import 121곳·상대경로 27곳을 단계적으로 이관
  • 이동 과정에서 방치돼 있던 미사용 파일과 라우트를 함께 정리

컨벤션을 코드리뷰에만 맡기지 않기

Before

폴더 구조와 의존 방향 규칙이 리뷰어의 기억에 의존해, 시간이 지나면 다시 무너질 수밖에 없는 상태

After
  • 라우트 간 직접 import 금지·비표준 네이밍 금지·상대경로 금지 등 7개 규칙을 검증하는 CLI를 직접 구현
  • PR마다 자동 실행되는 검사로 배선해 규칙 위반이 머지되지 않도록 고정
  • 실제 운영해 보니 일부 규칙은 정상 패턴까지 잡아내어, 해당 규칙은 ERROR에서 WARN으로 재조정

Impact

수백 개

미사용 파일·라우트 정리 포함

마이그레이션 대상 TS/TSX 파일

상시 검증

모든 PR

아키텍처 규칙 자동 검증

0 errors

라우트 스모크 27/27 통과

7규칙 검증 통과 상태 유지