2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00
2026-01-30 09:01:27 +09:00

Frontend 프로젝트 가이드

Vite + React + TypeScript 기반 프론트엔드 프로젝트 개발 가이드입니다.


목차

  1. 개발 규칙
  2. 개발 환경 및 옵션 리스팅
  3. 설치 및 동작 방법
  4. 아키텍처 & 책임 분리 규칙
  5. 네이밍 & 패키지 규칙
  6. 예외 처리 & 에러 응답 규칙
  7. 로그 & 감사(Audit) 규칙
  8. 설정 관리 규칙
  9. 보안 관련 추가 규칙
  10. 테스트 & 검증 규칙
  11. 금지 패턴 / 안티 패턴 목록
  12. 성능 최적화 가이드
  13. 의존성 관리
  14. 트러블슈팅 가이드

1. 개발 규칙

1.1 시큐어 코딩 규칙 준수

  • OWASP Top 10 기반 보안 규칙 준수 필수
  • XSS 방지: dangerouslySetInnerHTML 금지, 필요 시 DOMPurify 사용
  • 인증 정보 저장 금지: localStorage/sessionStorage에 토큰 저장 금지
  • URL 인코딩: encodeURIComponent 사용 필수
  • eval / new Function 사용 금지
  • 환경 변수에 비밀키 저장 금지

상세 규칙은 base_arcitectures_md/SECURE_RULE.md 참조

1.2 공통 소스 기능 및 활용 방법

공통 API (common/api/)

  • http.ts: Axios 인스턴스

    • baseURL, timeout, withCredentials 설정
    • 전역 헤더 설정
  • endpoints.ts: API 엔드포인트 상수

    import { API } from "@/common/api/endpoints";
    const response = await http.get(API.AUTH.ME);
    
  • interceptors.ts: 전역 인터셉터

    • 요청: 전역 로딩 시작
    • 응답: 전역 로딩 종료, 401/403 처리

공통 인증 (common/auth/)

  • AuthContext: 인증 상태 Context

  • AuthProvider: 인증 상태 제공자

  • useAuth: 인증 훅

    const { user, login, logout, refreshMe } = useAuth();
    
  • ProtectedRoute: 인증 필요 라우트 보호

    <Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
    
  • authService: 인증 API 호출

    • login(), logout(), me()

공통 컴포넌트 (common/components/)

디렉터리 구조:

common/components/
├── form/          # 폼 입력 컴포넌트 (Input, Select, Textarea, Checkbox, Radio)
├── action/        # 액션 컴포넌트 (Button)
├── display/       # 표시 컴포넌트 (Table)
├── system/        # 시스템 컴포넌트 (LoadingOverlay, LoadingProvider, Toast, CommonModal)
└── index.ts       # 통합 export

공통 원칙:

  • 모든 공통 컴포넌트는 id, name, className 전달 옵션 지원
  • 스타일 관련 세부 구현은 CSS로 처리 가능한 영역은 최대한 CSS에 위임
  • 컴포넌트는 범용성 위주로 설계하되, 과도한 추상화는 지양
  • 동작이 필요한 부분은 props 옵션으로만 제어

입력 / 액션 계열 컴포넌트:

  • Button.tsx: 버튼 컴포넌트

    import { Button } from "@/common/components";
    
    <Button
      disabled={false}
      loading={false}
      size="md" // "sm" | "md" | "lg"
      variant="primary" // "primary" | "secondary" | "danger" | "outline" | "ghost"
      onClick={() => {}}
    >
      버튼 텍스트
    </Button>
    
    • disabled: 비활성화
    • loading: 로딩 상태 (중복 클릭 방지)
    • size: 버튼 크기 (sm, md, lg)
    • variant: 버튼 스타일 변형
  • Input.tsx: 입력 컴포넌트 (검색 기능 포함)

    import { Input } from "@/common/components";
    
    // 기본 텍스트 입력
    <Input
      type="text"
      value={value}
      onChange={setValue}
      format={{ formatEmail: true }} // 이메일 포맷
      format={{ formatPhone: true }} // 전화번호 포맷 (010-1234-5678)
      format={{ validationRegex: /^[a-z]+$/ }} // 정규식 검증
      search={{
        enabled: true,
        debounceMs: 300,
        commitOnEnter: true,
        clearOnEscape: true,
        onCommit: (value) => {}
      }}
    />
    
    // 비밀번호 입력
    <Input type="password" value={value} onChange={setValue} />
    
    // 숫자 입력 (숫자 이외 문자 자동 제거)
    <Input type="number" value={value} onChange={setValue} />
    
    • 지원 타입: text, password, number
    • format: 이메일/전화번호 자동 포맷, 정규식 검증
    • search: debounce, commitOnEnter, clearOnEscape 옵션
  • Select.tsx: 선택 컴포넌트

    import { Select } from "@/common/components";
    
    <Select
      options={[
        { value: "1", label: "옵션 1" },
        { value: "2", label: "옵션 2", disabled: true }
      ]}
      value={selectedValue}
      onChange={(value) => {}}
      placeholder="선택하세요"
    />
    
  • Textarea.tsx: 텍스트 영역 컴포넌트

    import { Textarea } from "@/common/components";
    
    <Textarea
      value={value}
      onChange={setValue}
      rows={5}
    />
    
    • 기본 속성: resize: none, width: 100%
  • Checkbox.tsx: 체크박스 컴포넌트

    import { Checkbox } from "@/common/components";
    
    <Checkbox
      checked={checked}
      onChange={setChecked}
      label="체크박스 레이블"
    />
    
  • Radio.tsx: 라디오 버튼 컴포넌트

    import { Radio } from "@/common/components";
    
    <Radio
      name="radio-group"
      checked={selected === "value"}
      onChange={(checked) => {}}
      label="라디오 레이블"
    />
    

표시 계열 컴포넌트:

  • Table.tsx: 표준 테이블 컴포넌트

    import { Table } from "@/common/components";
    
    <Table
      columns={columns}
      rows={rows}
      getRowId={(row) => row.id}
      loading={isLoading}
      emptyText="데이터가 없습니다"
      onRowClick={(row) => {}}
      selection={selectionState}
      sort={sortValue}
      onSortChange={setSortValue}
    />
    
    • 로딩, 빈 상태, 선택, 정렬, 페이징 지원
  • CommonModal.tsx: 공통 모달 컴포넌트

    import { CommonModal } from "@/common/components";
    
    <CommonModal
      open={isOpen}
      onClose={() => setIsOpen(false)}
      title="모달 제목"
      showFooter={true}
      onConfirm={() => {}} // 전달 시 "확인", "닫기" 버튼 모두 표시
      confirmText="확인"
      cancelText="닫기"
      width="50vw" // 기본값
      height="50vh" // 기본값
    >
      모달 컨텐츠
    </CommonModal>
    
    • 구조: 헤더(타이틀, X 닫기) / 컨텐츠 / 푸터(확인/닫기 버튼)
    • onConfirm 전달 시: "확인", "닫기" 버튼 모두 표시
    • onConfirm 없을 시: "확인" 버튼만 표시 (단순 닫기)
    • ESC 키로 닫기 지원
    • 기본 사이즈: width: 50vw, height: 50vh

시스템 컴포넌트:

  • LoadingOverlay.tsx: 전역 로딩 오버레이

    import { LoadingOverlay } from "@/common/components";
    
    <LoadingOverlay visible={isLoading} />
    
  • LoadingProvider.tsx: 전역 로딩 상태 관리

    import { LoadingProvider, useLoading } from "@/common/components";
    
    const { visible } = useLoading(); // API 호출 시 자동으로 로딩 표시
    
  • Toast.tsx: 토스트 알림 시스템

    import { useToast } from "@/common/components";
    
    const toast = useToast();
    toast.push("작업이 완료되었습니다.", "success"); // "success" | "error" | "warning" | "info"
    

통합 Import:

// 통합 import (권장) - 모든 컴포넌트를 한 번에 import
import { 
  Button, 
  Input, 
  Select, 
  Textarea, 
  Checkbox, 
  Radio, 
  CommonModal,
  Table,
  LoadingOverlay,
  LoadingProvider,
  useLoading,
  useToast
} from "@/common/components";

// 또는 디렉터리별 import
import { Button } from "@/common/components/action";
import { Input, Select, Textarea, Checkbox, Radio } from "@/common/components/form";
import { Table } from "@/common/components/display/table";
import { CommonModal, LoadingOverlay, LoadingProvider, useToast } from "@/common/components/system";

공통 훅 (common/hooks/)

  • useFilterQuery: URL 검색 파라미터 관리

    • 뒤로가기/앞으로가기/새로고침 시 상태 유지
    const { state, update } = useFilterQuery();
    // state: { q, page, size, sort, filters }
    // update({ q: "검색어" }): URL 업데이트
    
  • useAppError: 에러 처리 훅

    const { handle } = useAppError();
    try {
      await someApiCall();
    } catch (error) {
      handle(error); // 자동으로 Toast 표시
    }
    

공통 Query (common/query/)

  • queryClient.ts: TanStack Query 클라이언트 설정
  • keys.ts: Query Key 표준화
  • QueryProvider: TanStack Query 제공자 (옵션)

1.3 공통소스 활용해서 서비스 구현하는 규칙

  1. Pages (features/<domain>/pages/): 화면 구성만 담당

    • useFilterQuery로 URL 상태 관리
    • useAuth로 인증 상태 확인
    • Table, Input 등 공통 컴포넌트 사용
  2. Components (features/<domain>/components/): 도메인 특화 컴포넌트

    • 공통 컴포넌트를 조합하여 도메인 특화 UI 구성
  3. API 호출: http 인스턴스 사용

    • endpoints.ts의 상수 활용
    • 인터셉터가 자동으로 로딩/에러 처리
  4. 상태 관리:

    • 전역 상태: Context API (AuthContext)
    • 서버 상태: TanStack Query (옵션)
    • URL 상태: useFilterQuery

1.4 공통 소스 수정 규칙

  • 공통 소스는 불가피한 경우가 아니면 절대 수정 금지
  • 추가(Extension)는 허용: 새로운 훅, 유틸 함수 추가 가능
  • 수정이 필요한 경우 반드시 팀 리뷰 및 승인 필요
  • 공통 소스 수정 시 모든 도메인에 미치는 영향 검토 필수

2. 개발 환경 및 옵션 리스팅

2.1 필수 환경

  • Node.js: 18.x 이상
  • npm: 9.x 이상 (또는 yarn, pnpm)
  • 브라우저: Chrome, Firefox, Safari, Edge 최신 버전

2.2 주요 의존성

  • react: ^19.2.0
  • react-dom: ^19.2.0
  • react-router-dom: ^6.25.1
  • axios: ^1.7.9
  • typescript: ~5.9.3
  • vite: ^7.2.4

2.3 옵션 기능

  • TanStack Query: 서버 상태 관리 (옵션)
    • 활성화: .envVITE_USE_REACT_QUERY=true
    • 비활성화: VITE_USE_REACT_QUERY=false 또는 미설정

2.4 개발 도구

  • ESLint: 코드 품질 검사
  • TypeScript: 타입 안정성
  • Vite: 빠른 개발 서버 및 빌드

3. 설치 및 동작 방법

3.1 사전 준비

  1. Node.js 설치 확인

    node -v
    npm -v
    
  2. 환경 변수 파일 생성

    • .env.development: 개발 환경
    • .env.production: 운영 환경
    • 자세한 내용은 ENV_SETUP.md 참조

3.2 프로젝트 설정

  1. 의존성 설치

    cd frontend
    npm install
    

    또는

    npm ci
    
  2. 환경 변수 설정

    • .env.development 파일 생성 및 설정
    VITE_API_BASE_URL=http://localhost:8080
    VITE_API_PREFIX=/api
    VITE_USE_REACT_QUERY=false
    

3.3 실행

  1. 개발 서버 실행

    npm run dev
    
    • 기본 주소: http://localhost:5173
  2. 빌드

    npm run build
    
    • 빌드 결과: dist/ 디렉터리
  3. 프리뷰 (빌드 결과 확인)

    npm run preview
    

3.4 확인

  • 개발 서버 실행 후 브라우저에서 http://localhost:5173 접속
  • 콘솔에서 에러 없이 로드되는지 확인

4. 아키텍처 & 책임 분리 규칙

4.1 계층별 책임 명확화

Pages (features/<domain>/pages/)

  • 책임: 화면 구성 및 조합
  • 역할:
    • 라우팅 대상 컴포넌트
    • 공통 컴포넌트 조합
    • URL 상태 관리 (useFilterQuery)
    • 인증 상태 확인 (useAuth)
  • 금지:
    • 직접 API 호출 (Service 계층 사용 권장)
    • 복잡한 비즈니스 로직 포함

Components (features/<domain>/components/)

  • 책임: 도메인 특화 UI 컴포넌트
  • 역할:
    • 공통 컴포넌트 조합
    • 도메인 특화 로직 (프레젠테이션 로직만)
  • 금지:
    • 직접 API 호출
    • 전역 상태 직접 조작

Common (common/)

  • 책임: 공통 기능 제공
  • 구성:
    • api/: API 통신 (http, endpoints, interceptors)
    • auth/: 인증 관리 (Context, Provider, Service)
    • components/: 공통 UI 컴포넌트
    • hooks/: 공통 훅
    • query/: TanStack Query 설정 (옵션)
    • utils/: 유틸리티 함수
    • types/: 공통 타입

4.2 비즈니스 로직 위치 규칙

위치 비즈니스 로직 허용 여부
Pages 금지 (프레젠테이션 로직만)
Components 금지 (프레젠테이션 로직만)
Common/Utils 금지 (순수 함수만)
Service (선택) 허용 (API 호출 및 데이터 변환)

참고: 프론트엔드는 주로 프레젠테이션 로직만 포함하며, 복잡한 비즈니스 로직은 백엔드에서 처리.

예시:

// ❌ 잘못된 예: Page에 복잡한 로직
function UserListPage() {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    // 복잡한 필터링/정렬 로직
    const filtered = users.filter(...).sort(...);
    setUsers(filtered);
  }, []);
}

// ⭕ 올바른 예: 백엔드에서 필터링/정렬 처리
function UserListPage() {
  const { state, update } = useFilterQuery();
  const { data } = useQuery({
    queryKey: ["users", state],
    queryFn: () => fetchUsers(state), // 백엔드에 필터링/정렬 파라미터 전달
  });
}

4.3 공통 모듈과 도메인 모듈의 경계 규칙

  • 공통 모듈 (common/): 모든 도메인에서 공통으로 사용하는 기능

    • 도메인 특화 로직 포함 금지
    • 재사용 가능한 순수 함수/컴포넌트만
  • 도메인 모듈 (features/<domain>/): 특정 도메인 전용 기능

    • 다른 도메인에서 직접 참조 금지
    • 도메인 간 공통 기능은 common/으로 이동

4.4 "이 로직은 어디에 두어야 하는가" 판단 기준

  1. URL 상태 관리: useFilterQuery (Pages에서 사용)
  2. 인증 상태: useAuth (Pages/Components에서 사용)
  3. API 호출: http 인스턴스 또는 TanStack Query
  4. 전역 로딩: 자동 처리 (interceptors)
  5. 에러 처리: useAppError 또는 TanStack Query 에러 핸들링
  6. 공통 UI: common/components/의 컴포넌트 사용

5. 네이밍 & 패키지 규칙

5.1 디렉터리 구조 규칙

  • app/: 앱 레벨 설정 (라우팅, 레이아웃)
  • common/: 공통 기능
  • features/: 도메인별 기능
    • features/<domain>/pages/: 페이지 컴포넌트
    • features/<domain>/components/: 도메인 특화 컴포넌트

5.2 파일 / 컴포넌트 / 함수 네이밍 기준

파일

  • 컴포넌트: PascalCase.tsx (예: UserList.tsx, LoginForm.tsx)
  • : camelCase.ts + use 접두사 (예: useAuth.ts, useFilterQuery.ts)
  • 유틸: camelCase.ts (예: errorMapper.ts)
  • 타입: camelCase.ts (예: api.ts, error.ts)

컴포넌트

  • PascalCase 사용
  • 명확한 의미 전달 (예: UserList, DocumentTable, LoginForm)

함수/변수

  • camelCase 사용
  • boolean: is, has, can 접두사 (예: isLoading, hasPermission)
  • 이벤트 핸들러: handle 접두사 (예: handleSubmit, handleClick)

상수

  • UPPER_SNAKE_CASE 사용 (예: API_BASE_URL, MAX_FILE_SIZE)

5.3 약어 사용 허용 / 금지 리스트

허용 약어

  • api, auth, config, util, props, ctx, ref

금지 약어

  • usr (→ user), svc (→ service), cmp (→ component)
  • mgr (→ manager), info (→ information), num (→ number)

5.4 API 필드 ↔ 컴포넌트 Props ↔ 상태 변수 매핑 규칙

API 응답 → 컴포넌트

  • 카멜 케이스 유지 (JSON 기본)
    • API: {"userId": 1, "email": "user@example.com"}
    • TypeScript: { userId: number; email: string }

컴포넌트 Props

  • 카멜 케이스 사용
    type UserCardProps = {
      userId: number;
      email: string;
      displayName: string;
    };
    

상태 변수

  • 카멜 케이스 사용
    const [user, setUser] = useState<User | null>(null);
    const [isLoading, setIsLoading] = useState(false);
    

6. 예외 처리 & 에러 응답 규칙

6.1 공통 Exception 구조

AppError 타입

type AppError = {
  kind: "network" | "timeout" | "unauthorized" | "forbidden" | "notfound" | "validation" | "server" | "unknown";
  status?: number;
  code?: string;
  message: string;
  details?: unknown;
};

errorMapper

  • mapAxiosError(): AxiosErrorAppError 변환
  • 자동으로 에러 종류 분류 및 사용자 친화적 메시지 제공

6.2 비즈니스 예외 vs 시스템 예외 구분

예외 타입 발생 위치 처리 방법 사용자 메시지
비즈니스 예외 API 응답 (4xx) useAppError.handle() 서버 메시지 또는 기본 메시지
시스템 예외 네트워크/타임아웃 useAppError.handle() 일반화된 메시지

예시:

// useAppError로 자동 처리
const { handle } = useAppError();
try {
  await someApiCall();
} catch (error) {
  handle(error); // 자동으로 Toast 표시 및 AppError 반환
}

6.3 API 응답 포맷 통일 규칙

모든 API 응답은 다음 형식:

type ApiResponse<T> = {
  success: boolean;
  data: T | null;
  error: {
    code: string;
    message: string;
  } | null;
};

사용 예시:

const response = await http.get<ApiResponse<User>>(API.AUTH.ME);
if (response.data.success) {
  const user = response.data.data; // User 타입
} else {
  const error = response.data.error; // { code, message }
}

6.4 로그 기록 기준과 사용자 노출 메시지 분리 규칙

  • 콘솔 로그: 개발 환경에서만 (import.meta.env.DEV)
  • 사용자 메시지: Toast 또는 에러 페이지에 표시
  • 민감 정보: 절대 로그/메시지에 포함하지 않음

예시:

// ❌ 잘못된 예: 민감 정보 콘솔 출력
console.log("User login:", { email, password });

// ⭕ 올바른 예: 개발 환경에서만 제한적 로깅
if (import.meta.env.DEV && import.meta.env.VITE_HTTP_LOGGING === "true") {
  console.log("User login:", { email: maskEmail(email) });
}

7. 로그 & 감사(Audit) 규칙

7.1 로그 레벨 사용 기준

프론트엔드는 주로 console.log, console.warn, console.error 사용:

레벨 사용 시기 예시
console.log 개발 중 디버깅 파라미터 값, 상태 변화
console.warn 예상 가능한 문제 API 응답 경고, 사용자 입력 경고
console.error 예상치 못한 오류 예외 발생, 시스템 오류

주의: 운영 빌드에서는 console.log 제거 (Vite가 자동 처리)

7.2 개인정보 및 민감정보 마스킹 규칙

마스킹 대상:

  • 비밀번호, 토큰
  • 이메일 (일부 마스킹 가능)
  • 전화번호 (일부 마스킹 가능)

마스킹 방법:

  • 직접 구현 또는 라이브러리 사용
  • 로그/콘솔 출력 전 마스킹 필수

예시:

// ❌ 잘못된 예: 민감 정보 콘솔 출력
console.log("User data:", user);

// ⭕ 올바른 예: 마스킹 후 출력
console.log("User data:", {
  ...user,
  email: maskEmail(user.email),
});

7.3 공통 로깅 유틸 사용 규칙

  • Axios Interceptor: 자동 요청/응답 로깅 (개발 환경)
  • 수동 로깅: 필요한 경우에만 console.log 사용
  • 운영 빌드: Vite가 자동으로 console.log 제거

7.4 요청 추적용 식별자(Request ID 등) 사용 여부

현재는 기본 구조만 제공. 필요 시 다음 추가 가능:

  • Axios Interceptor에서 Request ID 생성/추가
  • 응답 헤더에서 Request ID 추출
  • 로그에 Request ID 포함

8. 설정 관리 규칙

8.1 환경 변수 파일 분리 전략

  • .env.development: 개발 환경
  • .env.production: 운영 환경
  • .env.local: 로컬 오버라이드 (Git 제외)

환경 변수 접두사: VITE_ (Vite 요구사항)

8.2 환경별 설정 원칙

  • 공통 설정: 코드에 하드코딩 (예: 기본 타임아웃)
  • 환경별 설정: 환경 변수 사용
  • 민감 정보: 환경 변수 사용 (절대 코드에 포함 금지)

8.3 옵션 처리 기준 (enable / disable 방식)

옵션 기능은 환경 변수로 제어:

# TanStack Query 사용 여부
VITE_USE_REACT_QUERY=true  # 또는 false

코드에서 사용:

const useReactQuery = import.meta.env.VITE_USE_REACT_QUERY === "true";

8.4 TanStack Query 사용 여부를 옵션으로 제어하는 규칙

  • 활성화: VITE_USE_REACT_QUERY=true

    • QueryProviderQueryClientProvider로 래핑
    • TanStack Query 훅 사용 가능
  • 비활성화: VITE_USE_REACT_QUERY=false 또는 미설정

    • QueryProvider가 children만 반환
    • 직접 http 인스턴스로 API 호출

예시:

// TanStack Query 사용 (옵션 활성화 시)
const { data } = useQuery({
  queryKey: ["users"],
  queryFn: () => fetchUsers(),
});

// 직접 API 호출 (옵션 비활성화 시)
const [users, setUsers] = useState([]);
useEffect(() => {
  http.get(API.USER.LIST).then(res => setUsers(res.data.data));
}, []);

9. 보안 관련 추가 규칙 (시큐어 코딩 보강)

9.1 인증 / 인가 흐름 준수 규칙

인증 (Authentication)

  • 세션 기반 인증 사용
  • AuthProvider에서 /api/auth/me 호출로 인증 상태 확인
  • 로그인 성공 시 세션 쿠키 자동 설정 (백엔드)

인가 (Authorization)

  • ProtectedRoute: 인증 필요 라우트 보호
  • 백엔드에서 권한 검증 (403 Forbidden)

예시:

// ProtectedRoute 사용
<Route
  path="/dashboard"
  element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  }
/>

9.2 세션 / 토큰 사용 시 주의 사항

  • 세션 쿠키: withCredentials: true 설정 (Axios)
  • 토큰 저장 금지: localStorage/sessionStorage에 토큰 저장 절대 금지
  • 자동 갱신: 백엔드에서 세션 관리, 프론트엔드는 /api/auth/me로 상태 확인

9.3 파일 업로드 / 다운로드 처리 규칙

업로드

  1. 파일 크기 제한: 클라이언트에서 사전 검증
  2. 파일 타입 검증: 확장자 및 MIME 타입 확인
  3. FormData 사용: multipart/form-data 전송

다운로드

  1. 안전한 다운로드: 백엔드에서 권한 검증 후 다운로드 URL 제공
  2. Content-Disposition: 안전한 파일명 설정

9.4 외부 API 연동 시 보안 체크리스트

  • CORS 설정 확인 (백엔드)
  • HTTPS만 사용 (HTTP 금지)
  • 타임아웃 설정 (무한 대기 방지)
  • 입력값 검증 (외부 API로 전송 전)
  • 응답 검증 (예상 형식 확인)

9.5 XSS 방지 규칙

  • dangerouslySetInnerHTML 금지: HTML 직접 삽입 금지
  • 필요 시 DOMPurify: HTML 삽입이 불가피한 경우 DOMPurify로 sanitize
  • URL 인코딩: encodeURIComponent 사용
  • eval / new Function 금지: 동적 코드 실행 금지

예시:

// ❌ 잘못된 예: XSS 위험
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// ⭕ 올바른 예: DOMPurify 사용
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />

// ⭕ 더 안전한 예: 텍스트로 렌더링
<div>{userInput}</div>

10. 테스트 & 검증 규칙

10.1 단위 테스트 작성 기준 (필수 / 선택 구분)

선택 테스트

  • 컴포넌트 테스트: React Testing Library 사용 (선택)
  • 훅 테스트: 커스텀 훅 테스트 (선택)
  • 유틸 테스트: 순수 함수 테스트 (선택)

예시:

import { render, screen } from "@testing-library/react";
import { UserList } from "./UserList";

test("renders user list", () => {
  render(<UserList users={mockUsers} />);
  expect(screen.getByText("User 1")).toBeInTheDocument();
});

10.2 테스트용 데이터 작성 규칙

  • Mock 데이터: __mocks__/ 디렉터리에 정의
  • 격리: 각 테스트는 독립적으로 실행 가능해야 함

10.3 로컬 테스트 → 통합 테스트 흐름

  1. 로컬 컴포넌트 테스트: 단위 테스트
  2. 로컬 통합 테스트: 여러 컴포넌트 조합 테스트
  3. E2E 테스트: 실제 브라우저 테스트 (선택, Playwright/Cypress)

10.4 테스트 미수행 시 병합 제한 여부

  • 현재는 권고 사항 (필수 아님)
  • 향후 CI/CD 파이프라인에서 테스트 실패 시 병합 차단 가능

11. 금지 패턴 / 안티 패턴 목록

11.1 공통 Util에 비즈니스 로직 포함

// ❌ 잘못된 예
export function isAdminUser(email: string): boolean {
  return email.includes("@admin.com");
}

// ⭕ 올바른 예: 도메인 로직은 해당 도메인에 위치
// (또는 백엔드에서 처리)

11.2 직접 API 호출 (인터셉터 우회)

// ❌ 잘못된 예: axios 직접 import
import axios from "axios";
const response = await axios.get("http://localhost:8080/api/users");

// ⭕ 올바른 예: 공통 http 인스턴스 사용
import { http } from "@/common/api/http";
import { API } from "@/common/api/endpoints";
const response = await http.get(API.USER.LIST);

11.3 옵션 무시 후 하드코딩

// ❌ 잘못된 예: 옵션 무시하고 하드코딩
import { useQuery } from "@tanstack/react-query";
const { data } = useQuery({ ... });

// ⭕ 올바른 예: 옵션 확인
const useReactQuery = import.meta.env.VITE_USE_REACT_QUERY === "true";
if (useReactQuery) {
  const { data } = useQuery({ ... });
} else {
  const [data, setData] = useState(null);
  useEffect(() => {
    http.get(API.USER.LIST).then(res => setData(res.data.data));
  }, []);
}

11.4 공통 코드 복사 후 개별 컴포넌트에 포함

// ❌ 잘못된 예: 공통 로직을 각 컴포넌트에 복사
function UserList() {
  const [loading, setLoading] = useState(false);
  // 로딩 처리 로직 복사
}

function DocumentList() {
  const [loading, setLoading] = useState(false);
  // 동일한 로딩 처리 로직 복사
}

// ⭕ 올바른 예: 공통 훅/컴포넌트 사용
function UserList() {
  // 인터셉터가 자동으로 로딩 처리
  const { data } = useQuery({ ... });
}

11.5 localStorage/sessionStorage에 인증 정보 저장

// ❌ 잘못된 예: 토큰을 localStorage에 저장
localStorage.setItem("token", token);

// ⭕ 올바른 예: 세션 쿠키 사용 (백엔드에서 관리)
// 프론트엔드는 withCredentials만 설정

11.6 dangerouslySetInnerHTML 사용

// ❌ 잘못된 예: XSS 위험
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// ⭕ 올바른 예: 텍스트로 렌더링 또는 DOMPurify
<div>{userInput}</div>
// 또는
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />

11.7 eval / new Function 사용

// ❌ 잘못된 예: 동적 코드 실행
eval(userInput);
new Function(userInput)();

// ⭕ 올바른 예: 안전한 방법 사용
// (동적 코드 실행이 필요한 경우는 거의 없음)

11.8 URL 상태 관리 무시 (useFilterQuery 미사용)

// ❌ 잘못된 예: useState로만 상태 관리
const [q, setQ] = useState("");
const [page, setPage] = useState(1);

// ⭕ 올바른 예: useFilterQuery 사용 (뒤로가기/새로고침 대응)
const { state, update } = useFilterQuery();
// state: { q, page, size, sort, filters }
// update({ q: "검색어" }): URL 업데이트

11.9 전역 로딩 중복 처리

// ❌ 잘못된 예: 수동 로딩 처리
const [loading, setLoading] = useState(false);
const fetchData = async () => {
  setLoading(true);
  try {
    await http.get(API.USER.LIST);
  } finally {
    setLoading(false);
  }
};

// ⭕ 올바른 예: 인터셉터가 자동 처리
const fetchData = async () => {
  await http.get(API.USER.LIST); // 인터셉터가 자동으로 로딩 처리
};

12. 성능 최적화 가이드

12.1 번들 크기 최적화

코드 스플리팅

  • 라우트 기반 스플리팅: React.lazy 사용
    const UserListPage = lazy(() => import("@/features/user/pages/UserListPage"));
    

불필요한 의존성 제거

  • 트리 쉐이킹: 사용하지 않는 import 제거
  • 대안 라이브러리: 가벼운 라이브러리 선택 (예: date-fns vs moment)

12.2 렌더링 최적화

React 최적화

  • useMemo / useCallback: 불필요한 재계산 방지
  • React.memo: 컴포넌트 메모이제이션
  • 가상화: 대량 리스트는 react-window 사용

예시:

// ❌ 잘못된 예: 매번 새로운 객체 생성
function UserCard({ user }) {
  const style = { color: "blue" }; // 매 렌더링마다 새 객체
  return <div style={style}>{user.name}</div>;
}

// ⭕ 올바른 예: useMemo 사용
function UserCard({ user }) {
  const style = useMemo(() => ({ color: "blue" }), []);
  return <div style={style}>{user.name}</div>;
}

12.3 API 호출 최적화

중복 호출 방지

  • TanStack Query: 자동 캐싱 및 중복 제거
  • Debounce: 검색 입력 시 debounce 적용

데이터 페이징

  • 서버 사이드 페이징: 대량 데이터는 페이징 필수
  • 무한 스크롤: 필요 시 react-infinite-scroll 활용

12.4 이미지 최적화

  • 이미지 포맷: WebP 사용 권장
  • 이미지 크기: 적절한 해상도로 리사이즈
  • Lazy Loading: loading="lazy" 속성 사용

13. 의존성 관리

13.1 버전 관리 원칙

  • 명시적 버전 지정: package.json에 버전 명시
  • 보안 패치 우선: 취약점 발견 시 즉시 업데이트
  • 마이너 버전 업데이트: 정기적으로 검토 및 업데이트

13.2 보안 취약점 점검

정기 점검:

npm audit
npm audit fix

수동 점검:

  • npm audit 활용
  • GitHub Dependabot 설정 권장

13.3 업데이트 프로세스

  1. 의존성 업데이트: package.json 수정
  2. 로컬 테스트: 업데이트 후 빌드 및 테스트
  3. 통합 테스트: 개발 환경에서 검증
  4. 운영 배포: 검증 완료 후 배포

13.4 lock 파일 관리

  • package-lock.json: Git에 커밋 필수
  • npm ci 사용: CI/CD에서는 npm ci 사용 (lock 파일 기반 설치)

14. 트러블슈팅 가이드

14.1 자주 발생하는 문제

문제 1: CORS 오류

증상: Access to XMLHttpRequest has been blocked by CORS policy

해결:

  1. 백엔드 CORS 설정 확인 (WebMvcConfig.addCorsMappings)
  2. withCredentials: true 설정 확인 (프론트엔드)
  3. 백엔드에서 allowCredentials: true 확인
  4. 프록시 설정 확인 (개발 환경)

문제 2: 세션이 유지되지 않음

증상: 로그인 후 요청 시 401 Unauthorized

해결:

  1. withCredentials: true 설정 확인 (Axios)
  2. CORS 설정에서 allowCredentials: true 확인 (백엔드)
  3. 쿠키 도메인/경로 설정 확인
  4. 브라우저 쿠키 차단 여부 확인

문제 3: 환경 변수가 적용되지 않음

증상: import.meta.env.VITE_*undefined

해결:

  1. 환경 변수 파일 이름 확인 (.env.development, .env.production)
  2. VITE_ 접두사 확인
  3. 개발 서버 재시작
  4. 빌드 시 환경 변수 파일 위치 확인

문제 4: 빌드 실패 (TypeScript 오류)

증상: npm run build 실패

해결:

  1. npm run lint로 오류 확인
  2. TypeScript 타입 오류 수정
  3. tsconfig.json 설정 확인

14.2 디버깅 팁

React DevTools

  • 컴포넌트 트리: 컴포넌트 상태 확인
  • Profiler: 성능 분석

브라우저 DevTools

  • Network 탭: API 호출 확인
  • Console 탭: 에러 메시지 확인
  • Application 탭: 쿠키/로컬 스토리지 확인

Vite DevTools

  • HMR (Hot Module Replacement): 빠른 개발 피드백
  • 에러 오버레이: 빌드 에러 즉시 확인

참고 문서

  • 아키텍처 문서: base_arcitectures_md/FRONT_ARCHITECTURE_V1.md
  • 보안 규칙: base_arcitectures_md/FRONTEND_SECURE_RULE.md
  • 공통 보안 규칙: ../SECURE_RULE.md
  • 환경 변수 설정: ENV_SETUP.md

마지막 업데이트: 2024년

Description
No description provided
Readme 125 KiB
Languages
TypeScript 95.4%
CSS 3%
JavaScript 1%
HTML 0.6%