Boundary-First · 경계 우선 프론트엔드 설계

화면에서 시작하지 마라.
경계에서 시작하라.

Do not start from screens. Start from boundaries.

React·Next.js·React Native에 공통으로 적용하는 프론트엔드 설계 규격. 화면을 먼저 그리는 대신 라우팅·상태·도메인·통합·실패·런타임의 경계를 먼저 정의하고, 그 결정에서 화면이 따라 나오게 한다. 원칙은 문서가 아니라 아키텍처 검증기로 강제된다.

Propositions

  1. 1

    화면은 결과이고, 경계는 원인이다.

    화면은 이미 다른 곳에서 내려진 결정들의 배열일 뿐이다 — 데이터가 어디서 오는지, 각 상태를 누가 소유하는지, 실패하면 무엇이 일어나는지. 그 결정을 화면 안에서 즉흥적으로 내리면 모든 화면이 그것을 다시 발명하고, 코드베이스는 표류한다.

  2. 2

    좋은 아키텍처는 세 가지를 보장한다.

    • · 모든 변경에는 예측 가능한 위치가 있다.
    • · 모든 의존성에는 허가된 방향이 있다.
    • · 모든 런타임 실패에는 사전 정의된 경계가 있다.
  3. 3

    소스는 방향 있는 모듈 그래프다.

    G = (V, E)
      V = 모듈의 집합
      E = 방향 있는 import  (A → B  ≡  A imports B)
    
    불변식 1 — 순환 없음
      ∀ v ∈ V,  경로 v → … → v 는 존재하지 않는다.
    
    불변식 2 — 계층 순서
      L(module) ∈ ℕ
      A는  L(A) ≥ L(B)  일 때만  B를 import한다.

    순환은 “한 곳만 고치면 된다”는 성질을 파괴한다. 낮은 계층은 높은 계층에 의존하지 않는다. 두 불변식 모두 check:arch가 거부한다.

  4. 4

    화면 이전에 일곱 개의 경계를 정의한다.

    • 01Routing / Navigation사용자가 위치 사이를 어떻게 이동하는가.
    • 02State각 상태를 누가 소유하고, 어디에 사는가.
    • 03DomainUI와 무관한 비즈니스 개념은 무엇인가.
    • 04UI도메인에 종속되지 않는 재사용 프리미티브.
    • 05API / SDK Integration외부 데이터가 앱으로 들어오는 방식.
    • 06Failure비동기 흐름이 성공하지 못했을 때 무엇이 일어나는가.
    • 07Runtime / Platform네트워크·생명주기·플랫폼 같은 실행 조건.
  5. 5

    하나의 원리, 세 런타임.

    TemplateRuntimeMain Boundary
    ReactBrowser SPAState / Feature / API
    Next.jsServer + BrowserRoute / Server-Client / Cache
    React NativeiOS / AndroidNavigation / Native / Runtime
  6. 6

    검증기는 문서가 아니라 게이트다.

    pnpm check:arch   # forbidden imports · circular deps
                      # env access · deep imports

    규칙은 의존성 매트릭스의 실행 가능한 형태다. 위반하면 빌드가 깨진다. 이것이 의견과 규격을 가른다.

Import Direction

approuteswidgetsfeaturesentitiesshared

import는 항상 아래로만 흐른다. 상위(application-specific) 계층은 하위 계층을 import할 수 있지만, 하위 계층은 상위를 절대 참조하지 않는다. shared는 도메인 코드를 모른다.

세 템플릿의 실제 모듈 그래프를 3D로 보기 →

Specification

세 개의 독립 실행 가능한 스타터 템플릿과 아키텍처 검증기가 레포지토리에 공개되어 있다. 데모 도메인은 일부러 지루하게 두었다 — 주제는 기능이 아니라 경계이기 때문이다.