3.7 KiB
3.7 KiB
name, description
| name | description |
|---|---|
| dev-api-state | API 연동 패턴(useFetch / useAsyncData / $fetch)과 Pinia 상태관리 코드를 상황에 맞게 자동 선택·생성합니다. BFF 패턴, 에러 핸들링, 로딩 상태를 포함합니다. 다음 상황에서 반드시 사용하세요: - "이 API 연동 + 스토어 만들어줘", "Pinia store 작성해줘" - "API 데이터 페칭 어떻게 해?", "useFetch vs useAsyncData 언제 써?" - API 키 보호를 위한 BFF(server/api/) 패턴이 필요할 때 |
API 연동 & 상태관리 (dev-api-state)
API 스펙 → useFetch/useAsyncData 패턴 선택 → Pinia Setup Store 코드 자동 생성.
언제 사용하는가
- 새 API 연동 코드와 Pinia 스토어를 함께 작성할 때
useFetch/$fetch/useAsyncData중 적절한 패턴을 결정할 때- API 키를 클라이언트에 노출하지 않기 위한 BFF 패턴이 필요할 때
데이터 페칭 패턴 선택 기준
| 상황 | 권장 패턴 |
|---|---|
| SSR 페이지 초기 데이터 | useAsyncData + $fetch |
| 컴포넌트 마운트 후 데이터 | useFetch |
| 사용자 액션으로 트리거 | $fetch (직접 호출) |
| API 키 보호 필요 | server/api/ BFF + useFetch |
| 복잡한 캐싱/의존성 | useAsyncData with key |
작업 순서
Phase 1: API 스펙 파악
- 엔드포인트, 요청/응답 타입을 파악한다.
- 아래를 확인한다:
- 인증 헤더 필요 여부
- API 키 노출 위험 여부 → BFF 패턴 적용
- 캐시 전략 (항상 최신 vs TTL)
Phase 2: 타입 정의
// types/product.ts
export interface Product {
id: string
name: string
price: number
imageUrl: string
}
export interface ProductListResponse {
data: Product[]
total: number
page: number
}
Phase 3: BFF 레이어 (필요 시)
API 키 보호가 필요한 경우 server/api/ 경유:
// server/api/products/index.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const response = await $fetch<ProductListResponse>(
`${process.env.API_BASE}/products`,
{
headers: { 'x-api-key': process.env.API_KEY! },
query,
}
)
return response
})
Phase 4: Pinia Setup Store 생성
Setup Store 패턴을 기본으로 사용한다:
// stores/product.ts
import type { Product } from '~/types/product'
export const useProductStore = defineStore('product', () => {
const items = ref<Product[]>([])
const isLoading = ref(false)
const error = ref<string | null>(null)
const fetchProducts = async (page = 1) => {
isLoading.value = true
error.value = null
try {
const { data } = await useFetch('/api/products', { query: { page } })
items.value = data.value?.data ?? []
} catch (e) {
error.value = e instanceof Error ? e.message : '알 수 없는 오류'
} finally {
isLoading.value = false
}
}
return { items, isLoading, error, fetchProducts }
})
Phase 5: 컴포넌트 연결 예시
<script setup lang="ts">
const productStore = useProductStore()
const { items, isLoading, error } = storeToRefs(productStore)
onMounted(() => productStore.fetchProducts())
</script>
<template>
<div v-if="isLoading" aria-live="polite">로딩 중...</div>
<div v-else-if="error" role="alert">{{ error }}</div>
<ul v-else>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
출력 형식
## API 연동: <기능명>
### 선택된 패턴
- 페칭: useFetch | useAsyncData | $fetch
- BFF: 사용 | 미사용
- 이유: [선택 근거]
### 파일 목록
- `types/<domain>.ts`
- `server/api/...` (BFF 사용 시)
- `stores/<domain>.ts`
### 코드
[타입 → BFF → 스토어 → 컴포넌트 순]