Skip to content

마이그레이션 가이드: MVVM에서 Context-Layered로

이 문서는 기존 MVVM 또는 단순 React Context 구조에서 Context-Layered Architecture로 이동할 때의 기준점을 설명합니다. 핵심은 “한 번에 전부 바꾸는 것”이 아니라, 책임 분리 기준에 맞춰 점진적으로 재배치하는 것입니다.

가장 큰 차이

관점기존 MVVM/단순 구조Context-Layered
초점개념적 계층구현 가능한 책임 계층
비즈니스 로직ViewModel이나 컴포넌트에 섞임businesshandlers로 분리
의존성 주입context 또는 import에 의존props 기반 DI 가능
실행 흐름컴포넌트 중심handler 중심
테스트컴포넌트 통합 테스트 위주레이어별 테스트 가능

이전 구조와 이후 구조

이전

text
src/components/checkout/
├── CheckoutModel.tsx
├── CheckoutViewModel.tsx
├── CheckoutView.tsx
└── CheckoutPerformance.tsx

이후

text
src/pages/checkout/
├── contexts/
├── business/
├── handlers/
├── actions/
├── hooks/
├── views/
└── CheckoutPage.tsx

권장 마이그레이션 순서

1. Context 정의부터 분리

먼저 액션과 스토어 타입, provider 생성 코드를 contexts/로 옮깁니다. 이 단계에서는 동작 변경보다 “경계 분리”가 목적입니다.

2. 순수 로직을 business/로 이동

다음 항목을 순수 함수로 떼어냅니다.

  • validation
  • 계산 규칙
  • 상태 전이 규칙
  • 도메인별 파생 값 계산

이 단계가 끝나면 UI와 handler에서 계산 코드가 눈에 띄게 줄어듭니다.

3. side effect를 handlers/로 이동

API 호출, store 업데이트, ref focus, 로그 기록 같은 실행 흐름은 handler에 둡니다.

ts
useCheckoutHandler('submit', async (payload) => {
  const current = checkoutStore.getValue();
  const result = createOrderDraft(payload, current);

  if (result.success) {
    checkoutStore.setValue(result.nextState);
    await apiClient.save(result.order);
  }
});

4. view에서 직접 dispatch 코드를 actions/로 감싸기

ts
export function useCheckoutActions() {
  const dispatch = useCheckoutDispatch();

  return {
    submit: (order: OrderData) => dispatch('submit', order),
    reset: () => dispatch('reset'),
  };
}

이렇게 하면 view가 action 이름과 payload 모양에 덜 직접적으로 결합됩니다.

5. store 직접 접근을 hooks/로 정리

view가 여러 store를 직접 읽기 시작하면 다시 복잡도가 올라갑니다. view용 데이터 접근은 hooks/에 모으는 것이 좋습니다.

6. 최종적으로 Page에서 조립

provider 구성, handler 등록, 외부 의존성 주입은 페이지에서 담당하게 정리합니다.

마이그레이션 체크리스트

  • context 생성 코드가 contexts/에 모였는가
  • 검증과 계산 로직이 business/로 분리되었는가
  • API 호출과 store 업데이트가 handlers/에 모였는가
  • view는 hook과 action만 사용하도록 단순화되었는가
  • 테스트가 컴포넌트 하나에 몰리지 않고 레이어별로 분산되었는가

흔한 실수

  • views/에서 store를 직접 update하기
  • handlers/ 안에 검증 규칙을 길게 쓰기
  • actions/ 없이 view에서 dispatch 이름을 직접 남발하기
  • contexts/에 runtime logic을 넣기

추천 후속 문서

Released under the Apache-2.0 License.