Step 4 프론트엔드 개발 표준 및 구조 설계서 (FE Architecture & Standards) - 100-hours-a-week/16-team-katopia-fe GitHub Wiki

프론트엔드 개발 표준 및 구조 설계 (FE Architecture & Standards)

배경 (Background)

본 프로젝트는 이미지 중심의 패션 SNS 서비스로, 사용자는 피드를 소비하고(스크롤/탐색), 게시물과 투표를 작성하며, 투표 하기·좋아요·댓글·북마크 등 상태 변경 인터랙션을 반복적으로 수행한다.

또한 비로그인 사용자에게는 일부 콘텐츠만 노출하고, 주요 기능은 로그인 세션 기반으로 제공하는 등 권한별 UX 차등이 존재한다.

  • 성능 최적화 (Visual-First): 다량의 고화질 이미지를 처리하기 위해 Next.js의 Image Optimization과 지연 로딩 전략을 최우선으로 한다.
  • 응집도 높은 설계 (Feature-Driven): 도메인(Feed, Vote, Profile) 간 결합도를 낮추어 기능 확장 시 서로 영향을 주지 않는 구조를 지향한다.
  • 예측 가능한 상태 (Strict State Separation): 서버 캐시와 클라이언트 상태를 엄격히 분리하여 데이터 정합성 문제를 원천 차단한다.

목표가 아닌 것 (Non-goals)

이번 문서에서 범위에 포함하지 않는 것은 아래와 같다.

  • 디자인 시스템 자체의 완전한 정의(토큰/모든 컴포넌트 카탈로그)

    → 최소한의 UI 규칙과 공통 컴포넌트 원칙만 정의하고, 상세 컴포넌트 목록은 별도 문서로 분리

  • 백엔드 아키텍처/DB 설계/배포 인프라 상세

    → 프론트 관점에서 필요한 연동 방식(계약, 에러 규격, 인증 플로우)만 명시

  • E2E 자동화의 “완벽한 커버리지”

    → 핵심 사용자 플로우 중심으로 최소 커버리지부터 단계적 확장

  • 레거시 브라우저(예: IE) 지원

    → 공식 지원 범위에서 제외하고, 현대 브라우저 기준으로 최적화


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

  • Framework: Next.js (App Router)
    • SEO/OG, 이미지 최적화, 라우팅 구조화, 성능(스트리밍/캐싱) 활용
  • Language: TypeScript
    • API 계약/도메인 모델 타입화로 협업 비용 감소
  • State/Data
    • 서버 상태: TanStack Query(React Query)
    • 클라이언트 상태: Zustand(또는 Context 최소화)
    • 폼: React Hook Form + Zod
  • Testing
    • E2E: Playwright
  • Lint/Format: ESLint + Prettier

1) 프로젝트 폴더 구조 (Folder Structure)

기능별 응집도를 높이기 위해 Feature-based Architecture를 채택한다.

src/
├── app/                  # Next.js App Router (Routing, Layout, Server Component)
├── features/             # 도메인별 핵심 비즈니스 로직 (응집도 극대화)
│   ├── feed/
│   │   ├── api/          # 해당 도메인 전용 TanStack Query Hooks
│   │   ├── components/   # 해당 도메인 내에서만 재사용되는 컴포넌트
│   │   ├── hooks/        # 도메인 로직이 포함된 커스텀 훅
│   │   ├── types/        # API Response/Request DTO 및 도메인 모델
│   │   └── utils/        # 도메인 전용 헬퍼 함수
├── shared/               # 전역 공통 레이어 (범용성)
│   ├── components/       # UI Kit (Button, Input, Modal, Toast, Skeleton)
│   ├── hooks/            # 범용 훅 (useDebounce, useIntersectionObserver)
│   ├── lib/              # 외부 라이브러리 설정 (axiosInstance, queryClient)
│   ├── constants/        # 전역 상수 (API_ENDPOINT, STORAGE_KEY)
│   └── types/            # 전역 공통 타입
├── store/                # 전역 클라이언트 상태 (Zustand - Auth, UI State)
└── styles/               # 전역 스타일 (Tailwind Config, Tokens)

2) 컴포넌트 설계 원칙 (Component Design)

2-1. 역할 분리

  • Page(라우트): 라우팅 파라미터 처리, Server Component로서 초기 데이터 패팅 수행, 여러 Feature 컴포넌트를 조합하여 레이아웃 구성
  • Feature: 특정 도메인의 비즈니스 로직 소유, TanStack Query 훅을 직접 호출하여 상태 관리, Shared 컴포넌트에 데이터와 이벤트 핸들러를 주입
  • Shared(Presentational): 도메인 지식이 전혀 없는 순수 UI 컴포넌트, 오직 Props에만 의존하며 재사용성이 가장 높음 , 스타일 가이드의 최소 단위

2-2. 재사용 기준

  • Shared 영역 (shared/components):
    • 프로젝트 전반에서 쓰이는 공통 UI.
    • 규칙 : 내부에서 특정 API를 호출하거나 features 폴더의 요소를 참조해서는 안된다.
  • Feature 영역 (features/<domain>/components):
    • 특정 기능에 종속된 UI.
    • 규칙 : 해당 고메인의 데이터 모델을 알고 있어도 되지만, 다른 도메인의 로직을 직접 포함하지 않는다.

2-3. Props 규칙

  • Props Naming Convention:
    • Boolean : is, has, can, should 접두사를 사용한다. (예: isLoading, hasError, canEdit)
    • Event Handler: on 접두사를 사용하여 이벤트임을 명시한다. (예: onClick, onClose)
    • Internal Handler: 컴포넌트 내부에서 처리하는 함수는 handle 접두사를 사용한다. (예: const handleClick = () => { onOpen(); };)
  • 컴포넌트 합성(Composition):
    • props drilling을 피하기 위해 children을 적극 활용한다.
    • 복잡한 UI는 내부에서 모든 것을 처리하기 보다 작은 컴포넌트들을 조립하는 방식을 지향한다.

2-4. 성능 및 안정성 가이드

  • Memoization 시점:
    • 모든 컴포넌트에 memo를 남용하지 않는다. 렌더링 비용이 큰 리스트 아이템이나, Context 영향을 자주 받는 하위 컴포넌트에 한해 성능 측정 후 적용한다.
  • TypeScript 적용:
    • 모든 컴포넌트는 interface 또는 type을 통해 Props 타입을 명시한다.
    • Optional Props는 기본값(Default Parameters)을 지정하여 undefined로 인한 런타임 에러를 방지한다.

3) 상태 관리 전략 (State Management)

3-1. 서버 상태 관리 (TanStack Query v5)

  • Query Key 관리 체계 (Factory Pattern): 문자열 배열만 사용하지 않고 도메인 별 queryKeys 객체를 정의하여 오타를 방지하고 일관성을 유지한다.
  • 캐싱 및 재요청 정책 (StaleTime/GcTime):
    • 패션 피드와 같이 데이터 변경 빈도가 낮고 조회가 잦은 데이터는 staleTime을 5분 정도로 설정하여 불필요한 네트워크 비용을 절감한다.
    • 실시간이 중요한 알림 등은 staleTime : 0으로 설정하여 항상 최신 상태를 유지한다.
  • Mutation 후처리 (Synchronization):
    • Invalidation: 게시글 작성/수정 후에는 관련 queryKeyinvalidateQueries 처리하여 서버 데이터를 재조회한다.
    • Optimistic Update: '좋아요'나 '투표'처럼 사용자 반응이 빨라야 하는 인터랙션은 낙관적 업데이트를 적용해 즉각적인 피드백을 제공한다.
  • 데이터 패칭 최적화: 무한 스크롤은 useInfiniteQuery를 사용하며, 커서 기반 페이지네이션을 표준으로 채택한다.

3-2. 클라이언트 상태 관리(Zustand)

  • 스토어 분리(Atimic Stores):
    • 하나의 거대한 스토어를 지양하고 기능별로 스토어를 분리하여 불필요한 리렌더링을 방지한다.
  • 서버 상태와의 경계:
    • 원칙 : 서버에서 온 데이터를 Zustand에 복사하여 저장하지 않는다. 서버 데이터는 useQuery 결과값을 참조한다.
    • 예외: 프로필 수정 시 서버 데이터를 불러와서 입력 폼의 초기값으로 세팅하는 등의 '편집용 임시 상태'는 Zustand나 로컬 상태를 활용한다.

3-3. 전역 인증 상태 및 인가 전략 (Auth & Authorization Strategy)

  • 인증 데이터 저장 및 관리
    • Access Token: 보안과 성능을 고려하여 클라이언트 메모리(Zustand authStore)에 관리한다. API 요청 시 인터셉터를 통해 Authorization 헤더에 삽입합니다.
    • Refresh Token: 보안 위협(XSS)을 방지하기 위해 httpOnly, Secure 옵션이 적용된 쿠키에 저장하며, 토큰 재발급 로직은 백엔드와 쿠키 기반으로 자동 수행한다.
    • 인증 상태 SSOT: isAuthenticated 상태값은 오직 authStore에서 관리하며, 이 값의 변화에 따라 UI(네비게이션 바, 기능 버튼 등)가 즉각적으로 반응하도록 설계한다.
  • 인가 가드 (Authorization Guard) 및 UX 전략
    • 서버 사이드 (Next.js Middleware):
      • 대상: /mypage, /profile/setup 등 비로그인 유저가 절대 진입해서는 안 되는 경로
      • 동작: 미들웨어에서 인증 쿠키가 없을 경우, 사용자를 메인 페이지로 리다이렉트 시키되 URL 파라미터를 전달한다.
    • 클라이언트 사이드 (Global Auth Trigger):
      • 동작: 루트 레이아웃에서 URL 파라미터를 감시하거나 전역 상태를 체크한다. auth_required 파라미터가 감지되면 즉시 전역 로그인 바텀시트를 노출한다.
      • 인터랙션 가드: ‘투표하기’ , ‘좋아요’ 등 특정 버튼 클릭 시, 페이지 이동 없이 isAuthenticated를 체크하여 false일 경우 즉시 로그인 바텀시트를 호출하는 전역 유틸리티 훅을 사용한다.

4) API 연동 방식 & 비동기 처리

4-1. API Client 설계 (Axios Instance)

  • 인스턴스 설정:
    • baseURL : 환경 변수를 기반으로 서버 엔드포인트 설정.
    • timeout: 네트워크 지연 시 무한 대기를 방지하기 위해 10,000ms 설정.
    • withCredentials: Refresh Token 전송을 위해 필수 활성화.
  • 인터셉터 활용:
    • Request : authStore에서 Access Token을 가져와 Authorization: Bearer {token} 헤더를 자동으로 주입한다.
    • Response (Token Refresh):
      1. 401(Unauthorized) 에러 발생 시, 인터셉터 내에서 토큰 재발급 API를 1회 호출한다.
      2. 재발급 성공 시 실패했던 요청을 새로운 토큰으로 재시도(Retry)한다.
      3. 재발급 실패 시(세션 만료), 전역 상태를 초기화하고 로그인 유도 바텀시트를 활성화한다.

4-2. 에러 처리 정책

  1. 치명적 (Critical) : 시스템 장애, 네트워크 단절, 데이터 없음
    • UI 피드백 방식 : Global/Route Error Boundary
    • 처리 : 전체 화면을 에러 폴백 UI로 대체하고 '재시도' 버튼 제공
  2. 기능적 (Functional): 중복 투표, 권한 없음, 만료된 게시물
    • UI 피드백 방식 : Toast Message
    • 처리: 화면 하단에 일시적으로 알림 노출 (인터랙션 흐름 유지)
  3. 입력 (Form): 유효하지 않은 형식, 중복된 닉네임
    • UI 피드백 방식: Field Helper Text
    • 처리: 해당 입력 필드 하단에 붉은색 텍스트로 즉각 표시

4-3. 로딩 UX 전략 (Loading Experience)

  • 최초 진입 (Skeleton UI):
    • Next.js의 Suspenseloading.tsx를 활용하여 데이터 로딩 중 실제 콘텐츠의 구조와 유사한 스켈레톤 UI를 제공한다.
    • CLS(Layout Shift) 최적화: 이미지 카드나 프로필 영역의 높이값을 고정하여 로딩 후 콘텐츠가 튀는 현상을 방지한다.
  • 무한 스크롤 (Footer Spinner):
    • 피드 하단부에서 다음 데이터를 불러올 때 IntersectionObserver를 감지하여 하단 중앙에 스피너를 노출한다.
  • 버튼 액션 (Loading State):
    • 투표, 좋아요, 폼 제출 버튼은 클릭 시 Spinner로 변경하고 disabled 처리하여 API 중복 호출을 방지한다.

4-4. 비동기 데이터 정합성 유지 (Sync Policy)

  • Optimistic Update (낙관적 업데이트):
    • 투표나 좋아요처럼 성공 확률이 높고 즉각적인 반응이 필요한 기능은 서버 응답 전에 UI를 먼저 업데이트한다.
    • API 실패 시에는 TanStack QueryonSettled를 통해 이전 상태로 롤백처리한다.

5) 반응형 디자인 지원 전략(Responsive Design Strategy)

5-1. Mobile-First 및 Breakpoint 규격

패션 SNS 특성상 모바일을 기본으로 설계하고, 화면이 커짐에 따라 레이아웃을 확장하는 Mobile-First 방식을 채택한다. Tailwind CSS의 기본 규격을 준수하되 프로젝트 환경에 맞춰 커스텀한다.

Breakpoint Viewport Range 주요 레이아웃 전략
base (Mobile) < 600px 1열 피드 구성, 하단 탭 바 사용, 풀 스크린 바텀시트
sm (Tablet) 600px ~ 899px 2~3열 그리드 구성, 모달 너비 조정
md (Desktop S) 900px ~ 1199px 3~4열 그리드, 사이드 네비게이션 고려
lg (Desktop L) 1200px ~ 1535px 고정 컨테이너 폭(Max-width), 여백 최적화
xl (Wide) > 1536px 초고해상도 대응 및 와이드 스크린 레이아웃

5-2. 레이아웃 및 성능 최적화 원칙

  1. CLS 최소화:
    • Aspect Ratio 고정: 이미지 로딩 전 화면이 덜컹거리는 현상을 막기 위해 모든 피드 카드와 이미지 컨테이너에 aspect-ratio 속성을 부여한다.
    • Skeleton UI 매칭: 스켈레톤 UI의 높이값을 실제 콘텐츠와 1:1로 일치시켜 로딩 후 레이아웃 변화를 0에 가깝게 유지한다.
  2. 터치 인터페이스 최적화:
    • 터치 타겟: 모든 클릭 요소(좋아요, 북마크, 투표 옵션 등)는 최소 44x44px 이상의 클릭 영역을 확보한다. (Apple/Google 가이드 준수)
    • Safe Area 대응: 하단 인디케이터 영역에서 UI가 가려지지 않도록 env(safe-area-inset-*)를 전역 레이아웃에 적용한다.

5-3. 고도화된 이미지 최적화 (next/image 전략)

  • 포맷 최적화: WebP 및 AVIF 포맷을 우선적으로 지원하여 원본 대비 용량을 30~50% 절감한다.
  • Sizes 속성 필수 적용: 브라우저가 최적의 이미지 크기를 미리 알 수 있도록 sizes 속성을 전략적으로 작성한다.
    • 예) sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
  • LCP(Largest Contentful Paint) 개선:
    • Priority: 피드의 상단 1~2개 게시물 이미지에는 priority 속성을 부여하여 최우선 로딩한다.
    • Lazy Loading: 나머지 이미지는 브라우저 기본 지연 로딩을 활용하여 불필요한 네트워크 리소스 소모를 방지한다.
  • 아트 디렉션 (Art Direction): 디바이스 해상도(DPR 1.0, 2.0, 3.0)에 따라 적절한 밀도의 이미지를 서빙하도록 Next.js의 deviceSizes를 설정한다.

6) 코드 컨벤션 (Coding Convention)

6-1. 네이밍 규칙 (Naming Convention)

  • 파일 및 컴포넌트: PascalCase를 사용하며, 파일명과 대표 컴포넌트 명을 일치시킨다.
  • 함수 및 변수: camelCase를 사용한다.
  • 상수: UPPER_SNAKE_CASE를 사용하며, 전역적으로 관리되는 값에 적용한다.
  • 타입 및 인터페이스: PascalCase를 사용한다.
    • 접미사 활용: API 응답 모델은 DTO, 컴포넌트 Props는 Props를 붙여 구분한다.
  • 커스텀 훅: 반드시 use 접두사로 시작하며 camelCase를 따른다.

6-2. Import 규칙 (Import Order)

  1. React 및 Next.js 라이브러리: react, next/*
  2. 외부 라이브러리 (External): zustand, axios, lucide-react
  3. 내부 절대 경로 (Alias): @/shared, @/features, @/store
  4. 상대 경로: ./components, ../hooks

7) Lint / Formatting (정적 분석 & 포맷)

7-1. ESLint 설정 (Static Analysis)

  • no-console: production 환경에서는 console.log 사용을 금지한다.
  • react-hooks/rules-of-hooks: 훅의 호출 순서와 규칙을 강제한다.
  • jsx-a11y: 이미지 alt 태그 필수 등 기본적인 웹 접근성 규칙을 체크한다.
  • @typescript-eslint/no-unused-vars: 사용하지 않는 변수가 남아있지 않도록 엄격히 제한한다.

7-2. Prettier 설정 (Formatting)

  • semi: true (세미콜론 사용)
  • singleQuote: true (홀따옴표 사용)
  • tabWidth: 2 (들여쓰기 2칸)
  • printWidth: 100 (한 줄 최대 길이 100자)
  • trailingComma: 'all' (멀티라인 시 마지막 콤마 추가)

8) 지원 브라우저 범위 (Browser Compatibility)

8-1. 공식 지원 대상

플랫폼 브라우저 지원 버전 비고
Desktop Chrome, Edge 최신 2개 메이저 버전 Chromium 기반 브라우저 통합 지원
  Safari 최신 2개 메이저 버전 macOS 최신/이전 버전 대응
  Firefox 최신 2개 메이저 버전 -
Mobile iOS Safari 최신 2개 메이저 버전 아이폰 사용자 필수 대응 (Webkit)
  Android Chrome 최신 2개 메이저 버전 안드로이드 표준 브라우저
  Samsung Internet 최신 2개 메이저 버전 국내 안드로이드 높은 점유율 고려

8-2. 지원 제외 및 대응 정책

  • Internet Explorer (IE): 전 버전 지원 대상에서 제외한다. 접근 시 "지원하지 않는 브라우저" 안내 페이지로 리다이렉트하거나 안내 메시지를 노출한다.

9) Polyfill 전략

9-1. 기본 폴리필(Next.js 내장)

  • SWC 기반 컴파일: Next.js의 기본 컴파일러(SWC)가 제공하는 기본 폴리필 세트를 활용합니다. 이는 Promise, Symbol, Object.assign 등 ES6+의 핵심 기능을 자동으로 포함합니다.

9-2. 조건부 및 수동 폴리필 (Conditional Load)

  • IntersectionObserver: 무한 스크롤 및 이미지 지연 로딩(Lazy Loading)의 핵심 API이다. 지원하지 않는 환경(매우 구형 모바일 등)을 대비하여 polyfill.io 또는 동적 import()를 통해 로드한다.
  • ResizeObserver: 반응형 레이아웃 감지에 사용되는 API로, 필요 시 별도 폴리필을 추가한다.
  • Aspect-ratio: 구형 브라우저에서 aspect-ratio CSS가 동작하지 않을 경우를 대비해, 패딩 햌(Padding-top hack) 등을 폴백(Fallback) 스타일로 준비한다.

10) 테스트 전략 (Testing)

10-1. 크로스 브라우징 테스트

  • 실기기 테스트: 주요 타겟인 iPhone(iOS Safari)과 Galaxy(Android Chrome) 환경에서 실제 터치 인터랙션과 스크롤 성능을 테스트한다.
  • 가상 환경: BrowserStack이나 LambdaTest를 활용하여 지원 대상에 포함된 다양한 버전의 데스크탑 브라우저 레이아웃을 검증한다.

10-2. 배포 전 자동 검사

  • Lighthouse: 성능 및 웹 표준 준수 여부를 측정하여 호환성 문제가 성능 점수에 영향을 주는지 모니터링한다.
  • CI/CD Pipeline: 빌드 시 type-check와 함께 browserslist 기준에 부합하는지 확인하는 단계를 거친다.

용어 정의 (Glossary)

  • RQ(React Query): 서버 상태 캐싱/동기화 라이브러리(TanStack Query)
  • SSR: Server-Side Rendering (요청 시 서버 렌더)
  • SSG: Static Site Generation (빌드 시 정적 생성)
  • CSR: Client-Side Rendering (클라이언트 렌더)
  • Web Vitals: 사용자 체감 성능 지표(LCP/CLS/INP 등)
  • CLS: 레이아웃 이동 지표(이미지/폰트 로딩 영향)
  • OG 태그: 링크 공유 시 미리보기(썸네일/제목) 메타 정보
  • BrowserStack : 웹사이트나 모바일 애플리케이션을 수천 개의 실제 브라우저와 모바일 기기 환경에서 테스트할 수 있게 해주는 클라우드 기반의 크로스 브라우징 테스트 플랫폼
  • LambdaTest : BrowserStack과 직접적으로 경쟁하는 클라우드 기반의 차세대 크로스 브라우징 테스트 플랫폼
  • 폴백(Fallback) 스타일 : 특정 최신 CSS 속성이나 브라우저의 기능을 지원하지 않는 환경을 대비하여 대안으로 준비해두는 예비 스타일
⚠️ **GitHub.com Fallback** ⚠️