프론트엔드 개발 가이드라인
목적
Suspense 기반 데이터 페칭, 지연 로딩, 적절한 파일 구조화, 성능 최적화를 강조하는 현대적인 React 개발을 위한 종합 가이드입니다.
이 스킬을 사용해야 하는 경우
- 새로운 컴포넌트나 페이지를 생성할 때
- 새로운 기능을 구축할 때
- TanStack Query로 데이터를 페칭할 때
- TanStack Router로 라우팅을 설정할 때
- MUI v7로 컴포넌트를 스타일링할 때
- 성능 최적화를 할 때
- 프론트엔드 코드를 구조화할 때
- TypeScript 모범 사례를 적용할 때
빠른 시작
새 컴포넌트 체크리스트
컴포넌트를 만드시나요? 다음 체크리스트를 따르세요:
- TypeScript와 함께
React.FC<Props>패턴 사용 - 무거운 컴포넌트는 지연 로딩:
React.lazy(() => import()) - 로딩 상태를 위해
<SuspenseLoader>로 감싸기 - 데이터 페칭에
useSuspenseQuery사용 - 임포트 별칭 사용:
@/,~types,~components,~features - 스타일: 100줄 미만이면 인라인, 100줄 초과면 별도 파일
- 자식에게 전달되는 이벤트 핸들러에
useCallback사용 - 기본 내보내기는 파일 하단에
- 로딩 스피너로 조기 반환 금지
- 사용자 알림에
useMuiSnackbar사용
새 기능 체크리스트
기능을 만드시나요? 다음 구조를 설정하세요:
-
features/{feature-name}/디렉토리 생성 - 하위 디렉토리 생성:
api/,components/,hooks/,helpers/,types/ - API 서비스 파일 생성:
api/{feature}Api.ts -
types/에 TypeScript 타입 설정 -
routes/{feature-name}/index.tsx에 경로 생성 - 기능 컴포넌트 지연 로딩
- Suspense 경계 사용
- 기능
index.ts에서 공개 API 내보내기
임포트 별칭 빠른 참조
| 별칭 | 확인 위치 | 예시 |
|---|---|---|
@/ | src/ | import { apiClient } from '@/lib/apiClient' |
~types | src/types | import type { User } from '~types/user' |
~components | src/components | import { SuspenseLoader } from '~components/SuspenseLoader' |
~features | src/features | import { authApi } from '~features/auth' |
정의 위치: vite.config.ts 180-185줄
공통 임포트 치트시트
// React 및 지연 로딩
import React, { useState, useCallback, useMemo } from 'react';
const Heavy = React.lazy(() => import('./Heavy'));
// MUI 컴포넌트
import { Box, Paper, Typography, Button, Grid } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';
// TanStack Query (Suspense)
import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query';
// TanStack Router
import { createFileRoute } from '@tanstack/react-router';
// 프로젝트 컴포넌트
import { SuspenseLoader } from '~components/SuspenseLoader';
// 훅
import { useAuth } from '@/hooks/useAuth';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
// 타입
import type { Post } from '~types/post';
주제별 가이드
🎨 컴포넌트 패턴
현대적인 React 컴포넌트는 다음을 사용합니다:
- 타입 안전성을 위한
React.FC<Props> - 코드 분할을 위한
React.lazy() - 로딩 상태를 위한
SuspenseLoader - 명명된 const + 기본 내보내기 패턴
핵심 개념:
- 무거운 컴포넌트 지연 로딩 (DataGrid, 차트, 에디터)
- 지연 로딩된 컴포넌트는 항상 Suspense로 감싸기
- SuspenseLoader 컴포넌트 사용 (페이드 애니메이션 포함)
- 컴포넌트 구조: Props → Hooks → Handlers → Render → Export
📖 전체 가이드: resources/component-patterns.md
📊 데이터 페칭
주요 패턴: useSuspenseQuery
- Suspense 경계와 함께 사용
- 캐시 우선 전략 (API 호출 전 그리드 캐시 확인)
isLoading확인 대체- 제네릭을 통한 타입 안전
API 서비스 계층:
features/{feature}/api/{feature}Api.ts생성apiClientaxios 인스턴스 사용- 기능별 중앙 집중식 메서드
- 경로 형식:
/form/route(/api/form/route가 아님)
📖 전체 가이드: resources/data-fetching.md
📁 파일 구조화
features/ 와 components/ 의 차이:
features/: 도메인 특화 (게시물, 댓글, 인증)components/: 진정한 재사용 가능 (SuspenseLoader, CustomAppBar)
기능 하위 디렉토리:
features/
my-feature/
api/ # API 서비스 계층
components/ # 기능 컴포넌트
hooks/ # 커스텀 훅
helpers/ # 유틸리티 함수
types/ # TypeScript 타입
📖 전체 가이드: resources/file-organization.md
🎨 스타일링
인라인 vs 별도 파일:
- 100줄 미만: 인라인
const styles: Record<string, SxProps<Theme>> - 100줄 초과: 별도
.styles.ts파일
주요 방법:
- MUI 컴포넌트에
sxprop 사용 SxProps<Theme>로 타입 안전- 테마 접근:
(theme) => theme.palette.primary.main
MUI v7 Grid:
<Grid size={{ xs: 12, md: 6 }}> // ✅ v7 문법
<Grid xs={12} md={6}> // ❌ 이전 문법
📖 전체 가이드: resources/styling-guide.md
🛣️ 라우팅
TanStack Router - 폴더 기반:
- 디렉토리:
routes/my-route/index.tsx - 컴포넌트 지연 로딩
createFileRoute사용- 로더에 빵 부스러기 데이터 포함
예시:
import { createFileRoute } from '@tanstack/react-router';
import { lazy } from 'react';
const MyPage = lazy(() => import('@/features/my-feature/components/MyPage'));
export const Route = createFileRoute('/my-route/')({
component: MyPage,
loader: () => ({ crumb: 'My Route' }),
});
📖 전체 가이드: resources/routing-guide.md
⏳ 로딩 및 에러 상태
중요 규칙: 조기 반환 금지
// ❌ 절대 이렇게 하지 마세요 - 레이아웃 이동 유발
if (isLoading) {
return <LoadingSpinner />;
}
// ✅ 항상 이렇게 하세요 - 일관된 레이아웃 유지
<SuspenseLoader>
<Content />
</SuspenseLoader>
이유: 누적 레이아웃 이동(CLS) 방지, 더 나은 사용자 경험
에러 처리:
- 사용자 피드백에
useMuiSnackbar사용 - 절대
react-toastify사용 금지 - TanStack Query
onError콜백
📖 전체 가이드: resources/loading-and-error-states.md
⚡ 성능
최적화 패턴:
useMemo: 비용이 많이 드는 계산 (필터, 정렬, 매핑)useCallback: 자식에게 전달되는 이벤트 핸들러React.memo: 비용이 많이 드는 컴포넌트- 디바운스 검색 (300-500ms)
- 메모리 누수 방지 (useEffect에서 정리)
📖 전체 가이드: resources/performance.md
📘 TypeScript
표준:
- 엄격 모드,
any타입 사용 금지 - 함수에 명시적 반환 타입 명시
- 타입 임포트:
import type { User } from '~types/user' - JSDoc이 포함된 컴포넌트 prop 인터페이스
📖 전체 가이드: resources/typescript-standards.md
🔧 공통 패턴
다루는 주제:
- Zod 유효성 검사가 포함된 React Hook Form
- DataGrid 래퍼 계약
- 다이얼로그 컴포넌트 표준
- 현재 사용자를 위한
useAuth훅 - 캐시 무효화가 포함된 뮤테이션 패턴
📖 전체 가이드: resources/common-patterns.md
📚 완전한 예제
전체 작동 예시:
- 모든 패턴이 적용된 현대적인 컴포넌트
- 완전한 기능 구조
- API 서비스 계층
- 지연 로딩이 포함된 경로
- Suspense + useSuspenseQuery
- 유효성 검사가 포함된 폼
📖 전체 가이드: resources/complete-examples.md
탐색 가이드
| 작업이 필요할 때... | 이 리소스를 읽으세요 |
|---|---|
| 컴포넌트 생성 | component-patterns.md |
| 데이터 페칭 | data-fetching.md |
| 파일/폴더 구조화 | file-organization.md |
| 컴포넌트 스타일링 | styling-guide.md |
| 라우팅 설정 | routing-guide.md |
| 로딩/에러 처리 | loading-and-error-states.md |
| 성능 최적화 | performance.md |
| TypeScript 타입 | typescript-standards.md |
| 폼/인증/DataGrid | common-patterns.md |
| 전체 예시 확인 | complete-examples.md |
핵심 원칙
- 무거운 것은 모두 지연 로딩: 경로, DataGrid, 차트, 에디터
- 로딩은 Suspense로: 조기 반환 대신 SuspenseLoader 사용
- useSuspenseQuery: 새 코드의 주요 데이터 페칭 패턴
- 기능은 구조화: api/, components/, hooks/, helpers/ 하위 디렉토리
- 크기에 따른 스타일: 100줄 미만 인라인, 초과 시 별도 파일
- 임포트 별칭: @/, ~types, ~components, ~features 사용
- 조기 반환 금지: 레이아웃 이동 방지
- useMuiSnackbar: 모든 사용자 알림에 사용
빠른 참조: 파일 구조
src/
features/
my-feature/
api/
myFeatureApi.ts # API 서비스
components/
MyFeature.tsx # 메인 컴포넌트
SubComponent.tsx # 관련 컴포넌트
hooks/
useMyFeature.ts # 커스텀 훅
useSuspenseMyFeature.ts # Suspense 훅
helpers/
myFeatureHelpers.ts # 유틸리티
types/
index.ts # TypeScript 타입
index.ts # 공개 내보내기
components/
SuspenseLoader/
SuspenseLoader.tsx # 재사용 가능한 로더
CustomAppBar/
CustomAppBar.tsx # 재사용 가능한 앱바
routes/
my-route/
index.tsx # 경로 컴포넌트
create/
index.tsx # 중첩 경로
현대적인 컴포넌트 템플릿 (빠른 복사)
import React, { useState, useCallback } from 'react';
import { Box, Paper } from '@mui/material';
import { useSuspenseQuery } from '@tanstack/react-query';
import { featureApi } from '../api/featureApi';
import type { FeatureData } from '~types/feature';
interface MyComponentProps {
id: number;
onAction?: () => void;
}
export const MyComponent: React.FC<MyComponentProps> = ({ id, onAction }) => {
const [state, setState] = useState<string>('');
const { data } = useSuspenseQuery({
queryKey: ['feature', id],
queryFn: () => featureApi.getFeature(id),
});
const handleAction = useCallback(() => {
setState('updated');
onAction?.();
}, [onAction]);
return (
<Box sx={{ p: 2 }}>
<Paper sx={{ p: 3 }}>
{/* 내용 */}
</Paper>
</Box>
);
};
export default MyComponent;
전체 예시는 resources/complete-examples.md를 참조하세요.
관련 스킬
- error-tracking: Sentry를 통한 에러 추적 (프론트엔드에도 적용)
- backend-dev-guidelines: 프론트엔드가 소비하는 백엔드 API 패턴
스킬 상태: 점진적 로딩이 적용된 모듈식 구조로 최적의 컨텍스트 관리