Awesome-omni-skill Frontend Engineer

프론트엔드 개발 시 준수해야 할 아키텍처, 컨벤션, 워크플로우 가이드

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/frontend-engineer-monam2" ~/.claude/skills/diegosouzapw-awesome-omni-skill-frontend-engineer-b9195c && rm -rf "$T"
manifest: skills/development/frontend-engineer-monam2/SKILL.md
source content

Agent: Frontend Engineer

Mandate

SSOT 원칙과 접근성을 준수하는 사용자 인터페이스 구현

Codex 필수: 모든 코드 작성 및 수정은 Codex MCP를 호출해 수행


개발 워크플로우

컴포넌트 우선 개발 (Component First)

1. 컴포넌트 스펙 정의
   └─ Props, 상태, 동작 정의

2. 컴포넌트 구현
   └─ 기능 구현 + 스타일링

3. Storybook 문서화
   └─ 스토리 작성 + 인터랙션 테스트

4. 페이지에 통합
   └─ 실제 데이터 연결

순서

단계작업산출물
1컴포넌트 스펙인터페이스 정의
2컴포넌트 생성
*.tsx
3Storybook
*.stories.tsx
4페이지 통합
page.tsx
  • 스토리북 검증 후 페이지에 적용

Architecture Standards

도메인 기반 구조 (Domain-Driven)

FSD(Feature-Sliced Design)와 유사하나 일부 계층을 단순화한 도메인 중심 아키텍처를 채택한다.

  1. src/
    구조 도입: 모든 소스 코드는
    src
    디렉토리 하위에 위치하며,
    @
    alias는
    src
    를 가리킨다.

  2. app/
    (Pages):

    • 각 페이지는 하나의 도메인을 대변하거나 도메인들의 조합.
    • page.tsx
      (진입점)는 오직 도메인 컴포넌트와 Shared 컴포넌트의 배치(Layout) 역할만 수행.
    • 비즈니스 로직을 직접 포함하지 않음.
  3. domains/
    :

    • 프로젝트 핵심 비즈니스 로직과 UI가 응집된 곳.
    • 구조:
      /src/domains/[DomainName]/
      • components/
        : 해당 도메인 전용 UI
      • hooks/
        : 해당 도메인 전용 로직
      • entities/
        : 타입 정의 (Interface, Type)
      • schema/
        : Zod 스키마
      • api/
        : API 호출 함수
      • utils/
        : 도메인 전용 유틸리티
  4. shared/
    :

    • 프로젝트 전반에서 사용되는 범용 요소.
    • 구조:
      /src/shared/
      • components/
        : UI 라이브러리(Shadcn), 공통 모달 등
      • hooks/
        : 범용 훅 (useDebounce 등)
      • utils/
        : 날짜 포맷팅 등 순수 함수
      • entities/
        : 전역 타입 (API Response 등)

코딩 컨벤션

Import 정렬 규칙

길이 기준 내림차순 정렬:

  1. React 및 외부 패키지
  2. Domain 계층 (
    @/domains/...
    )
  3. Shared 계층 (
    @/shared/...
    )
  4. 상대 경로 (
    ./
    ,
    ../
    )

Barrel Export

  • 각 디렉토리(
    components
    ,
    hooks
    등)에
    index.ts
    를 두어 외부에서 깔끔하게 import 하도록 함.

Pages

경로설명
/
랜딩 페이지
/analyze
분석 요청
/report/[id]
결과 (SSE 스트리밍)
/history
히스토리 (모달)

API Layer Architecture

┌─────────────────────────────────────────────────┐
│              Component Layer                     │
│  useSuspenseQuery → AsyncBoundary 감싸기         │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│              Query Hook Layer                    │
│  useAnalysis, useReport, useHistory             │
│  - useMyQuery.getQueryKeys() 메서드 포함         │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│              API Function Layer                  │
│  analysisApi.create(), reportApi.get()          │
│  - ky 인스턴스 사용                              │
│  - 커스텀 에러 코드 파싱                          │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│              Route Config Layer                  │
│  /lib/api/routes.ts                             │
│  API_ROUTES.ANALYZE, API_ROUTES.REPORT          │
└─────────────────────────────────────────────────┘

React Query 패턴

// 쿼리 훅 생성 패턴 (Object.assign)
const getAnalysisKey = (id: string) => ["analysis", id];

export const useAnalysis = Object.assign(
  (id: string) => {
    return useSuspenseQuery({
      queryKey: getAnalysisKey(id),
      queryFn: () => getAnalysis(id),
    });
  },
  {
    getQueryKey: getAnalysisKey,
  },
);

// 사용
const { data } = useAnalysis(id);
const queryKey = useAnalysis.getQueryKey(id);

상태 관리 (State Management)

  1. URL as State:
    id
    ,
    tab
    ,
    filter
    등 공유 가능한 상태는 URL Query Parameter로 관리한다.
  2. No Prop Drilling: URL 파라미터나 React Query 캐시 데이터는 부모에서 자식으로 props로 전달하지 않고, 자식 컴포넌트에서 직접 hook을 호출하여 가져온다.
    • 이를 통해 컴포넌트 결합도를 낮추고 독립성을 높인다.
    • 예:
      AnalysisView
      에서
      id
      를 props로 받지 않고
      useSearchParams
      로 직접 조회.

선언적 JSX (Declarative JSX)

  1. If Component: 조건부 렌더링에 삼항 연산자(
    condition ? A : B
    )나 논리 연산자(
    &&
    ) 대신
    <If />
    컴포넌트를 사용한다.
    <If
      condition={isLoading}
      ifTrue={<LoadingView />}
      ifFalse={<ContentView />}
    />
    
  2. Switch/Case: 복잡한 조건 분기는 IIFE와 switch 문을 사용하여 선언적으로 표현한다.

유틸리티 라이브러리

  • es-toolkit: 성능과 번들 사이즈 최적화를 위해
    lodash
    대신
    es-toolkit
    을 사용한다.
  • isEmpty
    ,
    get
    등 일반적인 유틸리티 함수는
    es-toolkit/compat
    또는
    es-toolkit
    에서 가져와 사용한다.

AsyncBoundary

Suspense + ErrorBoundary 통합 컴포넌트

<AsyncBoundary
  fallback={<Skeleton />}
  errorFallback={({ error, reset }) => (
    <ErrorUI error={error} onRetry={reset} />
  )}
>
  <DataComponent />
</AsyncBoundary>
  • useSuspenseQuery 우선 사용
  • 적합하지 않은 경우에만 useQuery 사용

스켈레톤 UI

API 응답 대기 중 표시하는 로딩 상태

  • 각 페이지/컴포넌트별 맞춤 스켈레톤
  • 실제 콘텐츠와 동일한 레이아웃

접근성

  • 스킬 참조:
    .agent/skills/accessibility/SKILL.md
  • WCAG 2.1 AA 준수
  • 키보드 네비게이션
  • 스크린 리더 지원

Constraints

  • 순수 유틸/훅 작성
  • 사이드이펙트 필요 시 전용 훅으로 분리
  • Shadcn UI 컴포넌트 우선 사용
  • State Management: Props/State 끌어올리기(Hoisting)를 지양한다. React Query 캐시 또는 Path/Query Parameter에서 필요한 데이터를 하위 컴포넌트가 직접 가져와 사용(Colocation)한다.
  • Declarative JSX:
    renderFunction
    이나
    Map
    객체를 사용한 렌더링을 지양한다. 조건부 로직이 복잡할 경우
    ts-pattern
    이나 IIFE(즉시 실행 함수)를 사용하여 JSX 내에서 흐름을 명확히 한다.
  • Client Component 원칙: React Hook(예:
    useState
    ,
    useEffect
    ,
    useSearchParams
    ), 이벤트 핸들러, 또는 브라우저 전용 API(
    window
    ,
    document
    )를 사용하는 컴포넌트/유틸리티는 반드시 파일 최상단에
    "use client"
    를 명시해야 한다. 가능한 한 Leaf Node(말단 컴포넌트)로 Client Boundary를 미루어 Server Component의 이점을 살린다.

비동기 Fallback

모든 비동기 작업에 대해 fallback UI 구성

// 페이지 레벨
<AsyncBoundary fallback={<PageSkeleton />}>
  <Page />
</AsyncBoundary>

// 컴포넌트 레벨
<AsyncBoundary fallback={<CardSkeleton />}>
  <DataCard />
</AsyncBoundary>

// 버튼 로딩
<Button disabled={isPending}>
  {isPending ? <Spinner /> : '분석 시작'}
</Button>

애니메이션

부드러운 UX를 위한 전역 애니메이션 적용

요소애니메이션
모달fadeIn/fadeOut + scale
페이지 전환slide 또는 fade
확인창fadeIn + slideUp
드롭다운slideDown
스켈레톤pulse
// Framer Motion 또는 CSS transition
<motion.div
  initial={{ opacity: 0, scale: 0.95 }}
  animate={{ opacity: 1, scale: 1 }}
  exit={{ opacity: 0, scale: 0.95 }}
>
  <ModalContent />
</motion.div>

반응형 디자인

모바일 우선 (Mobile First) 접근

브레이크포인트범위
sm640px
md768px
lg1024px
xl1280px
// Tailwind 반응형 클래스
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {items.map(item => <Card key={item.id} />)}
</div>

// 모바일 네비게이션
<nav className="hidden md:flex">Desktop Nav</nav>
<nav className="md:hidden">Mobile Nav</nav>
Frontend Engineer — OpenSkillIndex