Frontend Core Skill
When to Use
- Creating React components or pages
- Implementing forms with validation
- Setting up data fetching or state management
- Working with routing (/c, /o, /t)
- Working with files under
frontend/
CRITICAL: Irreversible Action Confirmation (MANDATORY)
Redeem Flow
Customer screen shows "사용 처리" button → Trigger 2-step modal:
Modal Content:
- Title: "되돌릴 수 없는 작업입니다"
- Body: "매장 직원이 확인 후 눌러주세요"
- Buttons: [취소] (easy to hit) / [확인]
TTL Enforcement:
- If modal not confirmed within 30-60s → auto-expire
- Show "요청이 만료되었습니다" → retry CTA
Why This Matters:
- Prevents accidental customer-only redemption
- Forces store-side confirmation
- Abuse mitigation (no auto-click scripts)
Stack
Default (popular) stack:
- React + TypeScript + Vite
- Tailwind CSS
- React Router
- TanStack Query
- Axios
- React Hook Form + Zod
Libraries Policy
- Use the existing stack first
- Adding a new library requires:
- clear benefit
- small footprint
- at least one example usage
Architecture
User Types & Viewports
- Customer Wallet: mobile-first
- Owner Backoffice: desktop-first
- Store Terminal: always-on approval screen
Component Composition Pattern
Page (route-level)
└─ Container (data fetch + state)
└─ View (presentational)
File Organization
src/components/ - Common UI elements
src/features/<feature>/components/ - Feature-specific UI
Code Style
Base: Airbnb JavaScript Style Guide, with these overrides:
- Indentation: 4 spaces
- Max line length: 120 characters
- Semicolons: false
Naming Conventions
| Type |
Convention |
Example |
| Components/Files |
PascalCase |
UserProfile.tsx |
| Variables/Functions/Hooks |
camelCase |
const isOpen = useState() |
| Constants |
SCREAMING_SNAKE_CASE |
const MAX_RETRY_COUNT = 3 |
No Abbreviations:
// bad
const idx = 0
// good
const index = 0
Boolean Naming:
- Use prefixes:
is, has, can, should
- Event handlers:
handle* for functions, on* for props
// good
const handleOpen = () => {}
<Button />
Component Internal Order
- State declarations (
useState)
- Memoization (
useMemo, useCallback)
- Side effects (
useEffect)
- Event handlers
- JSX Rendering
TypeScript Rules
- Never use
any (use unknown if uncertain)
interface for objects (Props, API responses)
type for unions/aliases
- Use
as const instead of enums:
const ROLES = {
ADMIN: "ADMIN",
USER: "USER",
} as const
type Role = (typeof ROLES)[keyof typeof ROLES]
Prettier Config
{
"semi": false,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "es5",
"printWidth": 120,
"plugins": ["prettier-plugin-tailwindcss"]
}
State Branching (REQUIRED)
Every page MUST handle:
- Loading state
- Empty state
- Error state (with retry CTA)
Error Handling
- Show user-friendly message
- Provide a recovery action (retry / back)
State & Data Fetching
TanStack Query Patterns
useQuery for reads
useMutation for writes
- Invalidate queries on mutation success
Polling (MVP REQUIRED)
Issuance approval status and terminal lists must support polling:
- Default interval: 2-3 seconds
- Stop when: status is final OR TTL expires
Routing & Navigation
Use React Router.
Route Grouping
/c/* - customer
/o/* - owner/backoffice
/t/* - store terminal
Navigation Rules
- Keep route params explicit (e.g.,
storeId, stampCardId)
- Avoid deep nesting unless it improves clarity
Forms & Validation
- Use
react-hook-form for forms
- Use
zod schemas for validation
UX Requirements
- Display field errors near the field
- Disable submit while loading
- Prevent double submits
Import Rules
Use Absolute Paths
Use @/ prefix for major directories (components/, hooks/, etc.)
Import Sorting Order
- React core libraries
- Third-party libraries
- Global/Common components
- Domain-specific components
- Hooks, Utils, Types
- Assets (images, css)
Control Flow & Depth
- Braces Required: Do not omit
{} even for single-line if
- Depth Limit: Maintain depth of 1 or less (max 2)
- Actively use early returns
Linting & Formatting
Tools:
- ESLint: Code quality, accessibility (a11y), import sorting
- Prettier: Code formatting and Tailwind class sorting
- Husky & lint-staged: Automated verification before
git commit
PR Checklist
1---2name: frontend-core3description: Build React components, pages, forms, and state management with TypeScript and TanStack Query. Use for frontend development, component creation, routing, and data fetching.4---5
6# Frontend Core Skill
7
8## When to Use
9
10- Creating React components or pages
11- Implementing forms with validation
12- Setting up data fetching or state management
13- Working with routing (/c, /o, /t)
14- Working with files under `frontend/`
15
16---
17
18## CRITICAL: Irreversible Action Confirmation (MANDATORY)
19
20### Redeem Flow
21
22Customer screen shows "사용 처리" button → Trigger 2-step modal:
23
24**Modal Content:**
25- Title: "되돌릴 수 없는 작업입니다"
26- Body: "매장 직원이 확인 후 눌러주세요"
27- Buttons: [취소] (easy to hit) / [확인]
28
29**TTL Enforcement:**
30- If modal not confirmed within 30-60s → auto-expire
31- Show "요청이 만료되었습니다" → retry CTA
32
33**Why This Matters:**
34- Prevents accidental customer-only redemption
35- Forces store-side confirmation
36- Abuse mitigation (no auto-click scripts)
37
38---
39
40## Stack
41
42**Default (popular) stack:**
43- React + TypeScript + Vite
44- Tailwind CSS
45- React Router
46- TanStack Query
47- Axios
48- React Hook Form + Zod
49
50### Libraries Policy
51- Use the existing stack first
52- Adding a new library requires:
53 - clear benefit
54 - small footprint
55 - at least one example usage
56
57---
58
59## Architecture
60
61### User Types & Viewports
62- **Customer Wallet:** mobile-first
63- **Owner Backoffice:** desktop-first
64- **Store Terminal:** always-on approval screen
65
66### Component Composition Pattern
67```
68Page (route-level)
69 └─ Container (data fetch + state)
70 └─ View (presentational)
71```
72
73### File Organization
74- `src/components/` - Common UI elements
75- `src/features/<feature>/components/` - Feature-specific UI
76
77---
78
79## Code Style
80
81**Base:** Airbnb JavaScript Style Guide, with these overrides:
82- Indentation: 4 spaces
83- Max line length: 120 characters
84- Semicolons: false
85
86### Naming Conventions
87
88| Type | Convention | Example |
89|------|------------|---------|
90| Components/Files | PascalCase | `UserProfile.tsx` |
91| Variables/Functions/Hooks | camelCase | `const isOpen = useState()` |
92| Constants | SCREAMING_SNAKE_CASE | `const MAX_RETRY_COUNT = 3` |
93
94**No Abbreviations:**
95```typescript
96// bad
97const idx = 0
98
99// good
100const index = 0
101```
102
103**Boolean Naming:**
104- Use prefixes: `is`, `has`, `can`, `should`
105- Event handlers: `handle*` for functions, `on*` for props
106
107```tsx
108// good
109const handleOpen = () => {}
110<Button onClick={handleOpen} />
111```
112
113### Component Internal Order
1141. State declarations (`useState`)
1152. Memoization (`useMemo`, `useCallback`)
1163. Side effects (`useEffect`)
1174. Event handlers
1185. JSX Rendering
119
120### TypeScript Rules
121- **Never** use `any` (use `unknown` if uncertain)
122- `interface` for objects (Props, API responses)
123- `type` for unions/aliases
124- Use `as const` instead of enums:
125
126```typescript
127const ROLES = {
128 ADMIN: "ADMIN",
129 USER: "USER",
130} as const
131
132type Role = (typeof ROLES)[keyof typeof ROLES]
133```
134
135### Prettier Config
136```json
137{
138 "semi": false,
139 "singleQuote": true,
140 "tabWidth": 4,
141 "trailingComma": "es5",
142 "printWidth": 120,
143 "plugins": ["prettier-plugin-tailwindcss"]
144}
145```
146
147---
148
149## State Branching (REQUIRED)
150
151Every page **MUST** handle:
152- **Loading** state
153- **Empty** state
154- **Error** state (with retry CTA)
155
156### Error Handling
157- Show user-friendly message
158- Provide a recovery action (retry / back)
159
160---
161
162## State & Data Fetching
163
164### TanStack Query Patterns
165- `useQuery` for reads
166- `useMutation` for writes
167- Invalidate queries on mutation success
168
169### Polling (MVP REQUIRED)
170
171Issuance approval status and terminal lists must support polling:
172- **Default interval:** 2-3 seconds
173- **Stop when:** status is final OR TTL expires
174
175---
176
177## Routing & Navigation
178
179Use React Router.
180
181### Route Grouping
182- `/c/*` - customer
183- `/o/*` - owner/backoffice
184- `/t/*` - store terminal
185
186### Navigation Rules
187- Keep route params explicit (e.g., `storeId`, `stampCardId`)
188- Avoid deep nesting unless it improves clarity
189
190---
191
192## Forms & Validation
193
194- Use `react-hook-form` for forms
195- Use `zod` schemas for validation
196
197### UX Requirements
198- Display field errors near the field
199- Disable submit while loading
200- Prevent double submits
201
202---
203
204## Import Rules
205
206### Use Absolute Paths
207Use `@/` prefix for major directories (`components/`, `hooks/`, etc.)
208
209### Import Sorting Order
2101. React core libraries
2112. Third-party libraries
2123. Global/Common components
2134. Domain-specific components
2145. Hooks, Utils, Types
2156. Assets (images, css)
216
217---
218
219## Control Flow & Depth
220
221- **Braces Required:** Do not omit `{}` even for single-line `if`
222- **Depth Limit:** Maintain depth of 1 or less (max 2)
223- Actively use **early returns**
224
225---
226
227## Linting & Formatting
228
229**Tools:**
230- **ESLint:** Code quality, accessibility (a11y), import sorting
231- **Prettier:** Code formatting and Tailwind class sorting
232- **Husky & lint-staged:** Automated verification before `git commit`
233
234---
235
236## PR Checklist
237
238- [ ] Loading/Empty/Error states exist
239- [ ] Keyboard navigation works
240- [ ] Mobile-first layout checked
241- [ ] No unnecessary re-renders / infinite loops
242- [ ] API errors are handled