Step 3 공통 도메인 테크 스펙 - 100-hours-a-week/16-team-katopia-fe GitHub Wiki

공통 도메인 테크 스펙(System / Core)

공통 도메인은 Auth + profile / Post / Search 등 모든 기능 도메인이 공통으로 사용하는 UI/상태/네트워크/에러/접근제어/비동기 규칙을 제공하는 시스템 레이어다.

배경(Background)

프로젝트 목표(Objective)

본 서비스는 비로그인 사용자에게 일부 콘텐츠를 노출하면서도 좋아요/댓글/북마크/투표/글쓰기 등 주요 액션은 로그인 이후에만 가능한 SNS 구조를 가진다.

따라서 도메인별로 인증 유도, 로딩, 에러 UX가 다르게 구현되면, 사용자 경험이 도메인마다 제각각이 되고, 팀 개발 생산성이 떨어지며 추후 Post/Search 확장 시 중복 코드가 폭팔한다.

공통 도메인은 이를 해결하기 위해, “일관된 사용자 경험 + 재사용 가능한 공통 로직 + 예측 가능한 상태 기준”을 제공한다.

핵심 결과(Key Results)

  • KR1: 공통 UX 정책(로그인 유도/로딩/에러/토스트) 적용률 90% 이상
  • KR2: API 호출 실패 시 사용자 피드백(토스트/리트라이/리다이렉트) 정책 1가지로 통일
  • KR3: 다른 도메인 구현 시 공통 모듈만 사용하여 신규 페이지 개발 시간 20% 단축
  • KR4: 인증 필요 액션의 “로그인 유도” 처리 코드 중복 0

문제정의(Problem)

  1. 로그인 유도 로직이 도메인마다 흩어짐

    • HomeFeed에서는 바텀시트
    • Search에서는 그냥 막힘

    → 같은 상황에서 UX가 다르면 사용자 혼란 + QA 비용 증가

  2. 서버 에러 처리 UX가 제각각

    • 어떤 페이지는 toast
    • 어떤 페이지는 빈화면

    → 서비스가 불안정하다는 인식 발생

  3. 상태 관리 경계가 모호함

    • 서버에서 받은 데이터까지 Zustand로 저장하면 정합성 깨짐
    • 반대로 UI 상태까지 Query로 하면 복잡도 증가

    → 기준이 필요

  4. API Client / Retry / Token 만료 처리 분산

    • axios 설정이 페이지마다 있으면 유지보수 지옥
    • 인증 오류 처리 정책이 통일되지 않음.

목표가 아닌 것(Non-goals)

  • Post/Search/Auth의 비즈니스 로직 자체 구현(API payload 규칙, 도메인 정책)은 포함하지 않음.
  • 디자인 시스템 자체 리뉴얼은 포함하지 않음.

설계 및 기술 자료 (Architecture and Technical Documentation)

아키텍처 개요(Architecture Overview)

기술스택 (전 도메인 공통)

  • 프레임 워크/ 라우팅
    • Next.js 16(App Router)
      • App Router 기반의 File-based Routing을 사용한다.
      • Layout 계층을 활용하여 공통 Provider, 전역 UI(Toast, BottomSheet 등)를 한 번만 구성한다.
      • 서버/클라이언트 렌더링 경계를 명확히 분리하여 예측 가능한 렌더링 구조를 유지한다.
  • 언어(Language)
    • TypeScript 5.x
      • 전역 상태, API 응답, 공통 컴포넌트 Props에 대해 명시적인 타입을 명시한다.
      • 도메인 간 인터페이스 불일치로 인한 런타임 오류를 사전에 방지한다.
  • 상태 관리(State Management)
    • Zustand
      • 인증 상태, 공통 UI 상태를 전역으로 관리한다.
      • Redux 대비 Boilerplate가 적고, Context API 대비 불필요한 리렌더링을 줄일 수 있어 공통 상태 관리에 적합하다.
      • 서버에서 내려오는 데이터는 관리하지 않고, UI/클라이언트 상태에만 한정하여 사용한다.
  • 서버 상태 관리(Server Cache)
    • TanStack Query v5
      • 서버에서 내려오는 데이터는 Single Source of Truth로 간주하고 Query Cache로 관리한다.
      • 인증 상태를 기준으로 Query 활성화 여부를 제어한다.
      • 전 도메인에서 동일한 캐싱/재시도/에러 처리 정책을 적용할 수 있도록 공통 규칙을 제공
  • 네트워크 계층
    • Axios
      • 공통 Axios Instance를 생성하여 모든 API 요청을 일관된 방식으로 처리한다.
  • 데이터 검증
    • Zod
      • 백엔드 API Response에 대해 런타임 스키마 검증을 수행한다.
      • 외부 시스템으로부터 전달받은 데이터의 신뢰성을 확보한다.
      • 잘못된 응답 구조로 인한 UI 오류 전파를 사전에 차단한다.
  • 스타일링
    • Tailwind CSS
      • 유틸리티 클래스 기반으로 공통 UI 스타일 규칙을 정의한다.
      • 모바일 기준으로 반응형 UI를 일관되게 유지한다.

주요 페이지 / 컴포넌트 구조 (System / Common Domain)

페이지(Pages)

  • /error : 공통 에러 페이지
    • 주요 기능
      • 네트워크 장애/ 서버 오류/ 예상치 못한 오류에 대한 사용자 안내
      • “다시 시도/ 홈으로/ 로그인”등 복구 액션 제공
    • 사용 컴포넌트
      • ErrorLayout
      • ErrorView (상태 코드/메시지/CTA 렌더링)
      • RetryButton, GoHomeButton
    • 라우팅
      • 전역 에러 핸들러에서 /error 이동시키거나 현재 페이지에서 ErrorView를 인라인 렌더링
  • /not-found : 404(Not Found) 페이지
    • 주요 기능
      • 잘못된 경로/ 존재하지 않는 리소스 접근에 대한 안내
    • 사용 컴포넌트
      • NotFoundView
      • GoBackButton, GoHomeButton
    • 라우팅
      • Next.js App Router의 not-found.tsx로 처리

주요 컴포넌트(Components)

  • AppProviders : 전역 Provider 집합 컴포넌트

    개요(Overview)

    • 역할 : 앱 전역에서 필요한 Provider를 단일 진입점으로 구성한다.

    • 책임

      • Provider 중첩 순서를 고정하여 “환경 차이로 인한 버그”를 줄인다.
      • 전 도메인 동일한 방식으로 Query/Toast/Auth 상태를 사용하도록 보장한다.
    • props & Interface

      interface AppProvidersProps {
        children: React.ReactNode;
      }
    • 내부 상태 & 이벤트

      • 포함 Provider 예시
        • QueryClientProvider
        • ToastProvider
        • BottomSheetProvider
    • 사용 예시 (Usage)

      export default function RootLayout({ children }: { children: React.ReactNode }) {
        return (
          <html>
            <body>
              <AppProviders>{children}</AppProviders>
            </body>
          </html>
        );
      }
    • 에러 처리 / 엣지 케이스

      • Provider 초기화 실패 시 GlobalErrorBoundary로 fallback UI 제공

  • GlobalErrorBoundary : 전역 에러 경계

    개요 (Overview)

    • 역할: 렌더링/런타임 오류를 UI 레벨에서 포착하여 앱 전체 크래시를 방지한다.

    • 책임

      • 에러 발생 시 공통 에러 UI 제공
      • 에러 로그(개발 환경 console, 운영 환경 로깅 도구) 표준화
    • Props & Interface

      interface GlobalErrorBoundaryProps {
        children: React.ReactNode;
        fallback?: React.ReactNode;
      }
    • 내부 상태 & 이벤트

      • 내부적으로 error 상태를 잡고 fallback 렌더링
      • “다시 시도” 클릭 시 boundary reset
    • 사용 예시 (Usage)

      <GlobalErrorBoundary fallback={<ErrorView />}>
        <AppProviders>{children}</AppProviders>
      </GlobalErrorBoundary>
    • 에러 처리 / 엣지 케이스

      • 네트워크 에러는 Query 레벨에서 처리하고, “렌더링 크래시”만 여기서 담당하도록 책임 분리

  • Toast 시스템 : ToastProvider + useToast

    개요

    • 역할 : 전 도메인 동일한 방식으로 성공/실패/경고 메시지를 노출한다.

    • 책임

      • 중복 토스트 방지
      • 네트워크/인증 오류 등 공통 에러 메시지 표준 제공
    • props & Interface

      type ToastVariant = 'success' | 'error' | 'info';
      
      interface ToastPayload {
        title?: string;
        message: string;
        variant: ToastVariant;
        durationMs?: number;
      }
      
      interface ToastContextValue {
        push: (payload: ToastPayload) => void;
        clear: () => void;
      }
    • 사용 예시 (Usage)

      const { push } = useToast();
      push({variant:'error',message:'네트워크 오류가 발생했습니다.' });
    • 에러 처리 / 엣지 케이스

      • “서버 메시지 그대로 노출”이 아니라 사용자 친화 메시지로 매핑하는 레이어 제공
    • 스타일링

      • Tailwind 기반 공통 토스트 UI

  • AppBootLoader : 앱 초기 로딩 컴포넌트

    개요(Overview)

    • 역할

      앱 최초 진입 시 아직 아래 정보들이 확정되지 않은 상태에서 사용자에게 보여주는 초기 로딩 UI

      • Access Token이 유효한지?
      • /home으로 갈지 /profile/setup으로 갈지?

      → 흰 화면(White Screen)을 방지하고 “앱이 멈춘 것 같은 느낌”을 제거하는 것이 핵심 목적이다.

    • 책임

      • 앱 초기 진입 시 임시 UI 제공
      • 인증/세션/초기 라우팅 판단이 끝날 때 까지 사용자 대기 UX 개선
    • Props & Interface

    interface AppBootLoaderProps {
      label?: string; // "불러오는 중..." 등
    }
    • 내부 상태 & 이벤트

      • AppBootLoader 는 자기 판단을 하지 않는다. 보여줄지 말지는 상위에서 결정한다.
    • 표시/제거 방식

      {isBooting && <AppBootLoader />}
      • isBooting === true
        • AppBootLoader 표시
      • isBooting === false
        • 자동으로 사라짐
    • 에러 처리 / 엣지 케이스

      • 복구 가능한 경우 → 로그인 유도 / 홈 이동
      • 치명적 오류가 발생한 경우 → /error 라우팅 또는 ErrorView 노출

상태 관리 전략 (State Management Strategy)

  • Global UI & System State (Zustand)

    앱 전역에서 공유되어야 하는 비-서버 데이터를 관리한다.

    • 관리 대상 : 인증 상테, 전역 UI, 시스템 설정
    • 초기화 시점 : 앱 최초 진입시 또는 로그아웃 시
  • authStore : 인증 및 세션 관리

    interface AuthState {
      isLoggedIn: boolean;
      accessToken: string | null;
      user: UserProfile | null; // 프로필 이미지, 닉네임 등 최소 정보
      isBooting: boolean;       // 앱 초기 로딩 여부
    
      // Actions
      setAuth: (token: string, user: UserProfile) => void;
      clearAuth: () => void;    // 로그아웃 시 모든 전역 상태 초기화 트리거
      updateUser: (user: UserProfile) => void;
    }
    • 상태 전이 흐름
      1. AppBootRouter 실행 → 토큰 유효성 검사
      2. 성공 시 setAuth → 메인 피드 진입
      3. 실패 / 만료 시 clearAuth → 로그인 유도 바텀시트 노출.
  • uiStore : 전역 인터랙션 관리

    interface UIState {
      globalBottomSheet: {
        isOpen: boolean;
        type: 'LOGIN_GUIDE' | 'PROFILE_EDIT' | null;
      };
      // Actions
      openBottomSheet: (type: UIState['globalBottomSheet']['type']) => void;
      closeBottomSheet: () => void;
    }
  • Server Cache State (TanStack Query v5)

    서버가 진실의 근원(SSOT)인 데이터를 관리하며, 도메인 간 데이터 공유 및 동기화를 담당한다.

    • 관리 전략:
      • Zod Integration: API 응답이 캐시에 저장되기 전, Zod 스키마를 통해 런타임 검증을 수행합니다. 잘못된 데이터는 캐시에 진입하기 전 에러를 발생시켜 UI 크래시를 원천 차단합니다.
      • Stale-While-Revalidate: 공통 정책에 따라 staleTime(기본 0~5분)과 gcTime을 설정하여 네트워크 비용을 최적화합니다.
    • 상태 전이 흐름:
      1. Fetch: Axios 인스턴스가 요청 → Zod 스키마 검증 → Query Cache 저장.
      2. Mutation: 좋아요/수정 액션 발생 → Optimistic Update로 UI 우선 반영 → 서버 성공 시 invalidateQueries로 최신 데이터 동기화.
      3. Error: 401(인증 만료) 발생 시 authStoreclearAuth 호출 및 로그인 바텀시트 팝업.
  • Local State (React useState / useReducer)

    특정 컴포넌트 내부에서만 유효하며, 외부 공유가 불필요한 휘발성 상태를 관리합니다.

    • 관리 대상: 입력 필드(Input) 값, 로컬 토글(Accordion), 컴포넌트 내부 애니메이션 상태.
    • 전략:
      • Zustand나 Query로 올리지 않고 컴포넌트 생명주기에 맞춥니다.
      • Props Drilling 방지: 로컬 상태가 3단계 이상 하위로 전달될 경우에만 Context API 또는 Local Zustand Store 사용을 검토합니다.

라우팅(Routing)

  • Next.js App Router 기반 표준 구조:
    • 모든 도메인은 App Router의 File-based Routing 규칙을 준수하여 URL 구조와 파일 구조를 일치시킨다.
    • 공통 레이아웃 활용 : RootLayout에서 전역 Provider와 전역 UI를 관리하여 페이지 전환 시에도 공통 요소가 유지되도록 한다.
  • 접근 제어 및 미들웨어(Authorization Guards)
    • Next.js Middleware : 서버 사이드에서 세션 쿠키를 검증하여 인가가 필요한 페이지에 비로그인 사용자가 접근할 경우, 즉시 로그인 페이지로 리다이렉트하거나 메인으로 튕겨내는 전역 가이드 로직을 수행한다.
    • Client-side Guard: 클라이언트 네비게이션 중 인증이 필요한 액션 발생 시, authStore 상태를 확인하여 전역 로그인 유도 바텀시트를 호출하는 표준 프로세스를 제공한다.
  • 공통 네비게이션 및 레이아웃 구조
    • Persistent Bottom Navigation: 이미지에서 확인된 5개의 메뉴는 RootLayout 하위에 상주하며, 페이지 전환 시에도 초기화되지 않고 유지된다.
    • Active State 관리 : 현재 URL 경로와 네비게이션 아이템을 매핑하여 활성 탭에 시각적 강조를 적용하는 로직을 공통화한다.

폼 처리 및 유효성 검증(Forms & Validation)

  • 폼 관리 표준 (React Hook Form)
    • 최상단 정의 원칙: 각 페이지 컴포넌트 최상단에서 useForm을 선언하고, 복잡한 폼은 FormProvider를 통해 하위 컴포넌트에 상태를 전파한다.
    • 입력 보존(Preservation): 작성 플로우(예: 게시글 작성) 중 실수로 네비게이션 바를 눌러 이탈하려는 경우, isDirty 상태를 체크하여 작성 취소 확인 모달을 띄우는 이탈 방지 로직을 공통화한다.
  • 유효성 검증 표준 (Zod)
    • Schema-based Validation: 모든 폼은 Zod 스키마를 통해 정의됩니다. 이는 클라이언트 측 유효성 검사뿐만 아니라 API 응답 데이터 검증(Runtime Validation)에도 동일하게 사용된다.
    • 공통 규칙 제공:
      • 공통 필드: 닉네임 규칙, 내용 글자 수 제한, 이미지 파일 확장자 제한 등은 common/schemas에 정의하여 도메인 간 일관성을 유지한다.
      • 검증 시점: 사용자 피드백의 즉각성을 위해 mode: 'onChange' 또는 onBlur를 기본값으로 설정한다.
  • 에러 처리 및 피드백 UX
    • 인라인 에러 메시지 : 각 입력 필드 하단에 유효성 검사 실패 메시지를 일관된 스타일로 노출한다.
    • 서버 에러 연동
      • API 호출 결과가 400일 경우, 백엔드에서 내려주는 에러 코드를 React Hook FormsetError와 매핑하여 특정 필드에 에러를 표시한다.
    • 제출 중 상태 제어: isSubmitting 상태 동안 네비게이션 바를 포함한 모든 액션 버튼을 비활성화(Disabled) 처리하여 데이터 중복 제출을 원천 차단한다.

용어 정의(Glossary)

  • SSOT(Single Source of Truth) : 서버 데이터를 단일 진실의 근원으로 두고 클라이언트는 캐시된 뷰만을 관리하는 설계 원칙

  • Global UI State : 페이지를 넘나들며 유지되는 UI 상태

⚠️ **GitHub.com Fallback** ⚠️