[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JM016B138QH9VQNEBZYA2W":3,"skill-same:01M6JM016B138QH9VQNEBZYA2W":79},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":52,"commitSha":53,"skillDirectory":54,"forksCount":13,"repositoryHtmlUrl":55,"repositoryHomepageUrl":6,"ownerHtmlUrl":56,"ownerBlogUrl":6,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M6JM016B138QH9VQNEBZYA2W","error-handling",null,"Enforce proper error handling patterns. Use when writing async code, API calls, or user-facing features. Covers try-catch, error boundaries, graceful degradation, and user feedback.","superhealth\u002Fskills-hub","superhealth","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6292112?v=4","01M6JA6E7F21BYK5W3E4P270TT","01M6JA6EC0W8MPHKDJVBZDSEZ0",0,"MIT","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fdoyajin174\u002Ferror-handling\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"error handling","error","write","features","calls","degradation","enforce","error handling patterns","async","use","edit","writing","read","grep","feedback","Bash","team","api","code","antigravity","2026-11-10 10:12:30",false,"---\nname: error-handling\ndescription: Enforce proper error handling patterns. Use when writing async code, API calls, or user-facing features. Covers try-catch, error boundaries, graceful degradation, and user feedback.\nallowed-tools: Read, Glob, Grep, Edit, Write, Bash\nlicense: MIT\nmetadata:\n  author: antigravity-team\n  version: \"1.0\"\n---\n\n# Error Handling Patterns\n\n적절한 에러 처리 패턴을 강제하는 스킬입니다.\n\n## Core Principle\n\n> **\"에러는 숨기지 않고, 적절히 처리하고, 사용자에게 알린다.\"**\n> **\"Fail gracefully, recover when possible.\"**\n\n## Rules\n\n| 규칙 | 상태 | 설명 |\n|------|------|------|\n| 빈 catch 블록 금지 | ? 필수 | 최소 로깅 필수 |\n| 사용자 친화적 메시지 | ? 필수 | 기술적 에러 메시지 노출 금지 |\n| Error Boundary 사용 | ? 필수 (React) | 컴포넌트 에러 격리 |\n| Graceful Degradation | ? 권장 | 부분 실패 시 대안 제공 |\n\n## 기본 패턴\n\n### Try-Catch 올바른 사용\n\n```typescript\n\u002F\u002F ❌ BAD: 빈 catch 블록\ntry {\n  await fetchData();\n} catch (e) {\n  \u002F\u002F 아무것도 안 함 - 에러 무시\n}\n\n\u002F\u002F ❌ BAD: 모든 에러 동일 처리\ntry {\n  await fetchData();\n} catch (e) {\n  console.log('에러 발생');  \u002F\u002F 정보 부족\n}\n\n\u002F\u002F ✅ GOOD: 적절한 에러 처리\ntry {\n  await fetchData();\n} catch (error) {\n  \u002F\u002F 1. 에러 로깅 (개발자용)\n  console.error('fetchData failed:', error);\n\n  \u002F\u002F 2. 에러 추적 서비스 전송\n  errorTracker.capture(error);\n\n  \u002F\u002F 3. 사용자에게 알림\n  showToast('데이터를 불러오는데 실패했습니다. 다시 시도해주세요.');\n\n  \u002F\u002F 4. 필요시 재시도 또는 대안 제공\n  return fallbackData;\n}\n```\n\n### 에러 타입 구분\n\n```typescript\n\u002F\u002F ✅ GOOD: 에러 타입별 처리\nasync function fetchUser(id: string) {\n  try {\n    const response = await api.get(`\u002Fusers\u002F${id}`);\n    return response.data;\n  } catch (error) {\n    if (error instanceof NetworkError) {\n      \u002F\u002F 네트워크 에러: 재시도 제안\n      showToast('네트워크 연결을 확인해주세요.');\n      return null;\n    }\n\n    if (error instanceof NotFoundError) {\n      \u002F\u002F 404: 사용자 없음\n      showToast('사용자를 찾을 수 없습니다.');\n      return null;\n    }\n\n    if (error instanceof AuthError) {\n      \u002F\u002F 인증 에러: 로그인 페이지로\n      router.push('\u002Flogin');\n      return null;\n    }\n\n    \u002F\u002F 예상치 못한 에러\n    console.error('Unexpected error:', error);\n    errorTracker.capture(error);\n    showToast('오류가 발생했습니다. 잠시 후 다시 시도해주세요.');\n    return null;\n  }\n}\n```\n\n## 커스텀 에러 클래스\n\n```typescript\n\u002F\u002F errors.ts\nexport class AppError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode?: number,\n    public isOperational: boolean = true\n  ) {\n    super(message);\n    this.name = 'AppError';\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, public field?: string) {\n    super(message, 'VALIDATION_ERROR', 400);\n    this.name = 'ValidationError';\n  }\n}\n\nexport class NetworkError extends AppError {\n  constructor(message: string = '네트워크 연결을 확인해주세요') {\n    super(message, 'NETWORK_ERROR', 0);\n    this.name = 'NetworkError';\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(resource: string) {\n    super(`${resource}을(를) 찾을 수 없습니다`, 'NOT_FOUND', 404);\n    this.name = 'NotFoundError';\n  }\n}\n```\n\n## React Error Boundary\n\n### 기본 Error Boundary\n\n```tsx\n\u002F\u002F ErrorBoundary.tsx\nimport { Component, ReactNode } from 'react';\n\ninterface Props {\n  children: ReactNode;\n  fallback?: ReactNode;\n  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\ninterface State {\n  hasError: boolean;\n  error?: Error;\n}\n\nexport class ErrorBoundary extends Component\u003CProps, State> {\n  state: State = { hasError: false };\n\n  static getDerivedStateFromError(error: Error): State {\n    return { hasError: true, error };\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    console.error('Error caught by boundary:', error, errorInfo);\n    this.props.onError?.(error, errorInfo);\n\n    \u002F\u002F 에러 추적 서비스로 전송\n    errorTracker.captureException(error, { extra: errorInfo });\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback || \u003CDefaultErrorFallback error={this.state.error} \u002F>;\n    }\n    return this.props.children;\n  }\n}\n\n\u002F\u002F 기본 폴백 UI\nfunction DefaultErrorFallback({ error }: { error?: Error }) {\n  return (\n    \u003Cdiv className=\"error-fallback\">\n      \u003Ch2>문제가 발생했습니다\u003C\u002Fh2>\n      \u003Cp>페이지를 새로고침하거나 잠시 후 다시 시도해주세요.\u003C\u002Fp>\n      \u003Cbutton onClick={() => window.location.reload()}>\n        새로고침\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n### Error Boundary 사용\n\n```tsx\n\u002F\u002F 앱 전체 감싸기\nfunction App() {\n  return (\n    \u003CErrorBoundary fallback={\u003CFullPageError \u002F>}>\n      \u003CRouter>\n        \u003CRoutes \u002F>\n      \u003C\u002FRouter>\n    \u003C\u002FErrorBoundary>\n  );\n}\n\n\u002F\u002F 특정 섹션만 감싸기\nfunction Dashboard() {\n  return (\n    \u003Cdiv>\n      \u003CHeader \u002F>\n      \u003CErrorBoundary fallback={\u003CChartError \u002F>}>\n        \u003CChart data={data} \u002F>\n      \u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={\u003CTableError \u002F>}>\n        \u003CDataTable data={data} \u002F>\n      \u003C\u002FErrorBoundary>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n## Async 에러 처리\n\n### Promise 에러\n\n```typescript\n\u002F\u002F ❌ BAD: unhandled rejection\nfetchData().then(data => setData(data));\n\n\u002F\u002F ✅ GOOD: catch 처리\nfetchData()\n  .then(data => setData(data))\n  .catch(error => {\n    console.error('Failed to fetch:', error);\n    setError(error);\n  });\n\n\u002F\u002F ✅ BETTER: async\u002Fawait\nasync function loadData() {\n  try {\n    const data = await fetchData();\n    setData(data);\n  } catch (error) {\n    console.error('Failed to fetch:', error);\n    setError(error);\n  }\n}\n```\n\n### 여러 Promise 처리\n\n```typescript\n\u002F\u002F ❌ BAD: 하나라도 실패하면 전체 실패\nconst [users, posts] = await Promise.all([\n  fetchUsers(),\n  fetchPosts(),\n]);\n\n\u002F\u002F ✅ GOOD: 개별 결과 처리\nconst results = await Promise.allSettled([\n  fetchUsers(),\n  fetchPosts(),\n]);\n\nconst users = results[0].status === 'fulfilled' ? results[0].value : [];\nconst posts = results[1].status === 'fulfilled' ? results[1].value : [];\n\n\u002F\u002F 실패한 것만 로깅\nresults\n  .filter((r): r is PromiseRejectedResult => r.status === 'rejected')\n  .forEach(r => console.error('Failed:', r.reason));\n```\n\n## Graceful Degradation\n\n### 기능 저하 패턴\n\n```typescript\n\u002F\u002F ✅ GOOD: 실패 시 대안 제공\nasync function getRecommendations(userId: string) {\n  try {\n    \u002F\u002F 1차: 개인화된 추천\n    return await fetchPersonalizedRecommendations(userId);\n  } catch (error) {\n    console.warn('Personalized recommendations failed:', error);\n\n    try {\n      \u002F\u002F 2차: 인기 콘텐츠\n      return await fetchPopularContent();\n    } catch (error) {\n      console.warn('Popular content failed:', error);\n\n      \u002F\u002F 3차: 캐시된 기본 추천\n      return getCachedDefaultRecommendations();\n    }\n  }\n}\n```\n\n### UI 대안 제공\n\n```tsx\nfunction UserAvatar({ userId }: { userId: string }) {\n  const [imageError, setImageError] = useState(false);\n  const user = useUser(userId);\n\n  if (imageError || !user?.avatarUrl) {\n    \u002F\u002F 이미지 로드 실패 시 대안\n    return (\n      \u003Cdiv className=\"avatar-placeholder\">\n        {user?.name?.charAt(0) || '?'}\n      \u003C\u002Fdiv>\n    );\n  }\n\n  return (\n    \u003Cimg\n      src={user.avatarUrl}\n      alt={user.name}\n      onError={() => setImageError(true)}\n    \u002F>\n  );\n}\n```\n\n## 사용자 친화적 메시지\n\n### 메시지 매핑\n\n```typescript\nconst errorMessages: Record\u003Cstring, string> = {\n  NETWORK_ERROR: '네트워크 연결을 확인해주세요.',\n  UNAUTHORIZED: '로그인이 필요합니다.',\n  FORBIDDEN: '접근 권한이 없습니다.',\n  NOT_FOUND: '요청한 정보를 찾을 수 없습니다.',\n  VALIDATION_ERROR: '입력 정보를 확인해주세요.',\n  RATE_LIMIT: '요청이 너무 많습니다. 잠시 후 다시 시도해주세요.',\n  SERVER_ERROR: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.',\n  DEFAULT: '오류가 발생했습니다. 다시 시도해주세요.',\n};\n\nfunction getUserFriendlyMessage(error: unknown): string {\n  if (error instanceof AppError) {\n    return errorMessages[error.code] || errorMessages.DEFAULT;\n  }\n  return errorMessages.DEFAULT;\n}\n```\n\n### ? 금지: 기술적 메시지 노출\n\n```typescript\n\u002F\u002F ❌ BAD: 사용자에게 기술적 메시지 표시\nshowToast(error.message);  \u002F\u002F \"TypeError: Cannot read property 'id' of undefined\"\nshowToast(error.stack);    \u002F\u002F 스택 트레이스 노출\n\n\u002F\u002F ✅ GOOD: 친화적 메시지\nshowToast(getUserFriendlyMessage(error));\n```\n\n## 로깅 전략\n\n```typescript\n\u002F\u002F logger.ts\nexport const logger = {\n  error: (message: string, error: unknown, context?: object) => {\n    \u002F\u002F 개발 환경: 콘솔 출력\n    if (process.env.NODE_ENV === 'development') {\n      console.error(message, error, context);\n    }\n\n    \u002F\u002F 프로덕션: 에러 추적 서비스\n    errorTracker.captureException(error, {\n      tags: { message },\n      extra: context,\n    });\n  },\n\n  warn: (message: string, context?: object) => {\n    console.warn(message, context);\n  },\n};\n```\n\n## Checklist\n\n### 코드 작성 시\n\n- [ ] try-catch에 적절한 에러 처리 로직\n- [ ] 빈 catch 블록 없음\n- [ ] 에러 타입별 분기 처리\n- [ ] 사용자 친화적 메시지 표시\n- [ ] 에러 로깅\u002F추적\n\n### React 컴포넌트\n\n- [ ] Error Boundary 적용\n- [ ] 로딩\u002F에러 상태 UI\n- [ ] 재시도 기능 제공\n- [ ] 폴백 UI 구현\n\n### API 호출\n\n- [ ] 네트워크 에러 처리\n- [ ] 타임아웃 처리\n- [ ] 재시도 로직 (필요시)\n- [ ] 캐시 폴백 (필요시)\n\n## References\n\n- [React Error Boundaries](https:\u002F\u002Freact.dev\u002Freference\u002Freact\u002FComponent#catching-rendering-errors-with-an-error-boundary)\n- [JavaScript Error Handling](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FGuide\u002FControl_flow_and_error_handling)\n","# Error Handling Patterns\n\n적절한 에러 처리 패턴을 강제하는 스킬입니다.\n\n## Core Principle\n\n> **\"에러는 숨기지 않고, 적절히 처리하고, 사용자에게 알린다.\"**\n> **\"Fail gracefully, recover when possible.\"**\n\n## Rules\n\n| 규칙 | 상태 | 설명 |\n|------|------|------|\n| 빈 catch 블록 금지 | ? 필수 | 최소 로깅 필수 |\n| 사용자 친화적 메시지 | ? 필수 | 기술적 에러 메시지 노출 금지 |\n| Error Boundary 사용 | ? 필수 (React) | 컴포넌트 에러 격리 |\n| Graceful Degradation | ? 권장 | 부분 실패 시 대안 제공 |\n\n## 기본 패턴\n\n### Try-Catch 올바른 사용\n\n```typescript\n\u002F\u002F ❌ BAD: 빈 catch 블록\ntry {\n  await fetchData();\n} catch (e) {\n  \u002F\u002F 아무것도 안 함 - 에러 무시\n}\n\n\u002F\u002F ❌ BAD: 모든 에러 동일 처리\ntry {\n  await fetchData();\n} catch (e) {\n  console.log('에러 발생');  \u002F\u002F 정보 부족\n}\n\n\u002F\u002F ✅ GOOD: 적절한 에러 처리\ntry {\n  await fetchData();\n} catch (error) {\n  \u002F\u002F 1. 에러 로깅 (개발자용)\n  console.error('fetchData failed:', error);\n\n  \u002F\u002F 2. 에러 추적 서비스 전송\n  errorTracker.capture(error);\n\n  \u002F\u002F 3. 사용자에게 알림\n  showToast('데이터를 불러오는데 실패했습니다. 다시 시도해주세요.');\n\n  \u002F\u002F 4. 필요시 재시도 또는 대안 제공\n  return fallbackData;\n}\n```\n\n### 에러 타입 구분\n\n```typescript\n\u002F\u002F ✅ GOOD: 에러 타입별 처리\nasync function fetchUser(id: string) {\n  try {\n    const response = await api.get(`\u002Fusers\u002F${id}`);\n    return response.data;\n  } catch (error) {\n    if (error instanceof NetworkError) {\n      \u002F\u002F 네트워크 에러: 재시도 제안\n      showToast('네트워크 연결을 확인해주세요.');\n      return null;\n    }\n\n    if (error instanceof NotFoundError) {\n      \u002F\u002F 404: 사용자 없음\n      showToast('사용자를 찾을 수 없습니다.');\n      return null;\n    }\n\n    if (error instanceof AuthError) {\n      \u002F\u002F 인증 에러: 로그인 페이지로\n      router.push('\u002Flogin');\n      return null;\n    }\n\n    \u002F\u002F 예상치 못한 에러\n    console.error('Unexpected error:', error);\n    errorTracker.capture(error);\n    showToast('오류가 발생했습니다. 잠시 후 다시 시도해주세요.');\n    return null;\n  }\n}\n```\n\n## 커스텀 에러 클래스\n\n```typescript\n\u002F\u002F errors.ts\nexport class AppError extends Error {\n  constructor(\n    message: string,\n    public code: string,\n    public statusCode?: number,\n    public isOperational: boolean = true\n  ) {\n    super(message);\n    this.name = 'AppError';\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, public field?: string) {\n    super(message, 'VALIDATION_ERROR', 400);\n    this.name = 'ValidationError';\n  }\n}\n\nexport class NetworkError extends AppError {\n  constructor(message: string = '네트워크 연결을 확인해주세요') {\n    super(message, 'NETWORK_ERROR', 0);\n    this.name = 'NetworkError';\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(resource: string) {\n    super(`${resource}을(를) 찾을 수 없습니다`, 'NOT_FOUND', 404);\n    this.name = 'NotFoundError';\n  }\n}\n```\n\n## React Error Boundary\n\n### 기본 Error Boundary\n\n```tsx\n\u002F\u002F ErrorBoundary.tsx\nimport { Component, ReactNode } from 'react';\n\ninterface Props {\n  children: ReactNode;\n  fallback?: ReactNode;\n  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\ninterface State {\n  hasError: boolean;\n  error?: Error;\n}\n\nexport class ErrorBoundary extends Component\u003CProps, State> {\n  state: State = { hasError: false };\n\n  static getDerivedStateFromError(error: Error): State {\n    return { hasError: true, error };\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    console.error('Error caught by boundary:', error, errorInfo);\n    this.props.onError?.(error, errorInfo);\n\n    \u002F\u002F 에러 추적 서비스로 전송\n    errorTracker.captureException(error, { extra: errorInfo });\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback || \u003CDefaultErrorFallback error={this.state.error} \u002F>;\n    }\n    return this.props.children;\n  }\n}\n\n\u002F\u002F 기본 폴백 UI\nfunction DefaultErrorFallback({ error }: { error?: Error }) {\n  return (\n    \u003Cdiv className=\"error-fallback\">\n      \u003Ch2>문제가 발생했습니다\u003C\u002Fh2>\n      \u003Cp>페이지를 새로고침하거나 잠시 후 다시 시도해주세요.\u003C\u002Fp>\n      \u003Cbutton onClick={() => window.location.reload()}>\n        새로고침\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n### Error Boundary 사용\n\n```tsx\n\u002F\u002F 앱 전체 감싸기\nfunction App() {\n  return (\n    \u003CErrorBoundary fallback={\u003CFullPageError \u002F>}>\n      \u003CRouter>\n        \u003CRoutes \u002F>\n      \u003C\u002FRouter>\n    \u003C\u002FErrorBoundary>\n  );\n}\n\n\u002F\u002F 특정 섹션만 감싸기\nfunction Dashboard() {\n  return (\n    \u003Cdiv>\n      \u003CHeader \u002F>\n      \u003CErrorBoundary fallback={\u003CChartError \u002F>}>\n        \u003CChart data={data} \u002F>\n      \u003C\u002FErrorBoundary>\n      \u003CErrorBoundary fallback={\u003CTableError \u002F>}>\n        \u003CDataTable data={data} \u002F>\n      \u003C\u002FErrorBoundary>\n    \u003C\u002Fdiv>\n  );\n}\n```\n\n## Async 에러 처리\n\n### Promise 에러\n\n```typescript\n\u002F\u002F ❌ BAD: unhandled rejection\nfetchData().then(data => setData(data));\n\n\u002F\u002F ✅ GOOD: catch 처리\nfetchData()\n  .then(data => setData(data))\n  .catch(error => {\n    console.error('Failed to fetch:', error);\n    setError(error);\n  });\n\n\u002F\u002F ✅ BETTER: async\u002Fawait\nasync function loadData() {\n  try {\n    const data = await fetchData();\n    setData(data);\n  } catch (error) {\n    console.error('Failed to fetch:', error);\n    setError(error);\n  }\n}\n```\n\n### 여러 Promise 처리\n\n```typescript\n\u002F\u002F ❌ BAD: 하나라도 실패하면 전체 실패\nconst [users, posts] = await Promise.all([\n  fetchUsers(),\n  fetchPosts(),\n]);\n\n\u002F\u002F ✅ GOOD: 개별 결과 처리\nconst results = await Promise.allSettled([\n  fetchUsers(),\n  fetchPosts(),\n]);\n\nconst users = results[0].status === 'fulfilled' ? results[0].value : [];\nconst posts = results[1].status === 'fulfilled' ? results[1].value : [];\n\n\u002F\u002F 실패한 것만 로깅\nresults\n  .filter((r): r is PromiseRejectedResult => r.status === 'rejected')\n  .forEach(r => console.error('Failed:', r.reason));\n```\n\n## Graceful Degradation\n\n### 기능 저하 패턴\n\n```typescript\n\u002F\u002F ✅ GOOD: 실패 시 대안 제공\nasync function getRecommendations(userId: string) {\n  try {\n    \u002F\u002F 1차: 개인화된 추천\n    return await fetchPersonalizedRecommendations(userId);\n  } catch (error) {\n    console.warn('Personalized recommendations failed:', error);\n\n    try {\n      \u002F\u002F 2차: 인기 콘텐츠\n      return await fetchPopularContent();\n    } catch (error) {\n      console.warn('Popular content failed:', error);\n\n      \u002F\u002F 3차: 캐시된 기본 추천\n      return getCachedDefaultRecommendations();\n    }\n  }\n}\n```\n\n### UI 대안 제공\n\n```tsx\nfunction UserAvatar({ userId }: { userId: string }) {\n  const [imageError, setImageError] = useState(false);\n  const user = useUser(userId);\n\n  if (imageError || !user?.avatarUrl) {\n    \u002F\u002F 이미지 로드 실패 시 대안\n    return (\n      \u003Cdiv className=\"avatar-placeholder\">\n        {user?.name?.charAt(0) || '?'}\n      \u003C\u002Fdiv>\n    );\n  }\n\n  return (\n    \u003Cimg\n      src={user.avatarUrl}\n      alt={user.name}\n      onError={() => setImageError(true)}\n    \u002F>\n  );\n}\n```\n\n## 사용자 친화적 메시지\n\n### 메시지 매핑\n\n```typescript\nconst errorMessages: Record\u003Cstring, string> = {\n  NETWORK_ERROR: '네트워크 연결을 확인해주세요.',\n  UNAUTHORIZED: '로그인이 필요합니다.',\n  FORBIDDEN: '접근 권한이 없습니다.',\n  NOT_FOUND: '요청한 정보를 찾을 수 없습니다.',\n  VALIDATION_ERROR: '입력 정보를 확인해주세요.',\n  RATE_LIMIT: '요청이 너무 많습니다. 잠시 후 다시 시도해주세요.',\n  SERVER_ERROR: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.',\n  DEFAULT: '오류가 발생했습니다. 다시 시도해주세요.',\n};\n\nfunction getUserFriendlyMessage(error: unknown): string {\n  if (error instanceof AppError) {\n    return errorMessages[error.code] || errorMessages.DEFAULT;\n  }\n  return errorMessages.DEFAULT;\n}\n```\n\n### ? 금지: 기술적 메시지 노출\n\n```typescript\n\u002F\u002F ❌ BAD: 사용자에게 기술적 메시지 표시\nshowToast(error.message);  \u002F\u002F \"TypeError: Cannot read property 'id' of undefined\"\nshowToast(error.stack);    \u002F\u002F 스택 트레이스 노출\n\n\u002F\u002F ✅ GOOD: 친화적 메시지\nshowToast(getUserFriendlyMessage(error));\n```\n\n## 로깅 전략\n\n```typescript\n\u002F\u002F logger.ts\nexport const logger = {\n  error: (message: string, error: unknown, context?: object) => {\n    \u002F\u002F 개발 환경: 콘솔 출력\n    if (process.env.NODE_ENV === 'development') {\n      console.error(message, error, context);\n    }\n\n    \u002F\u002F 프로덕션: 에러 추적 서비스\n    errorTracker.captureException(error, {\n      tags: { message },\n      extra: context,\n    });\n  },\n\n  warn: (message: string, context?: object) => {\n    console.warn(message, context);\n  },\n};\n```\n\n## Checklist\n\n### 코드 작성 시\n\n- [ ] try-catch에 적절한 에러 처리 로직\n- [ ] 빈 catch 블록 없음\n- [ ] 에러 타입별 분기 처리\n- [ ] 사용자 친화적 메시지 표시\n- [ ] 에러 로깅\u002F추적\n\n### React 컴포넌트\n\n- [ ] Error Boundary 적용\n- [ ] 로딩\u002F에러 상태 UI\n- [ ] 재시도 기능 제공\n- [ ] 폴백 UI 구현\n\n### API 호출\n\n- [ ] 네트워크 에러 처리\n- [ ] 타임아웃 처리\n- [ ] 재시도 로직 (필요시)\n- [ ] 캐시 폴백 (필요시)\n\n## References\n\n- [React Error Boundaries](https:\u002F\u002Freact.dev\u002Freference\u002Freact\u002FComponent#catching-rendering-errors-with-an-error-boundary)\n- [JavaScript Error Handling](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FGuide\u002FControl_flow_and_error_handling)\n",{"name":5,"license":14,"metadata":48,"description":7,"allowed-tools":51},{"author":49,"version":50},"antigravity-team","1.0","Read, Glob, Grep, Edit, Write, Bash","master","08cc2532077058aa50e6e18f54695bc256c88152","skills\u002Fdoyajin174\u002Ferror-handling","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub","https:\u002F\u002Fgithub.com\u002Fsuperhealth","java engineer","2026-04-06 22:07:23",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add superhealth\u002Fskills-hub --skill error-handling",{"tool":65,"command":66},"bunx","bunx skills add superhealth\u002Fskills-hub --skill error-handling",{"tool":68,"command":69},"pnpm","pnpm dlx skills add superhealth\u002Fskills-hub --skill error-handling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fdoyajin174\u002Ferror-handling\u002FSKILL.md",true,[73],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":78},"skill-report.json","REFERENCE","参考文档",12314,"https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fdoyajin174\u002Ferror-handling\u002Fskill-report.json",[80,110,137,150,166,185],{"skillNo":81,"name":82,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":91,"publishedAt":43},"01M6MC9F3WHV8S8FZ2W4TRB0FE","vercel-react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fzhanlincui\u002Fvercel-react-best-practices\u002FSKILL.md",[86],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[92,93,94,95,96,97,41,98,99,100,101,102,103,104,105,106,107,108,109,34],"react","vercel react best practices","best practices","vercel","react best practices","data fetching","triggers","pages","next","patterns","refactoring","data","Optimization","tasks","guidelines","react components","components","performance",{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":120,"publishedAt":43},"01M6MB8KWE1Q5A5KERR7RFTQWP","vue-testing-best-practices","Use for Vue.js testing. Covers Vitest, Vue Test Utils, component testing, mocking, testing patterns, and Playwright for E2E testing.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-testing-best-practices\u002FSKILL.md",[116,119],{"categoryNo":117,"categoryName":118,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},[121,122,123,94,124,125,126,127,128,32,129,101,130,131,132,133,134,135,136],"vue testing best practices","testing best practices","vue","testing","testing patterns","js","e2e","vitest","component","test","mocking","for","utils","e2e testing","github","playwright",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":144,"publishedAt":43},"01M6MB8GK06M6GA4KVYNDV1BZP","vue-router-best-practices","Vue Router 4 patterns, navigation guards, route params, and route-component lifecycle interactions.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-router-best-practices\u002FSKILL.md",[143],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},[94,123,145,146,147,101,148,149,129,135],"router","vue router best practices","route","navigation","lifecycle",{"skillNo":151,"name":152,"nameZh":6,"description":153,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":154,"categories":155,"keywords":157,"publishedAt":43},"01M6MB8DQF21TZMQ9XC3745ERW","vue-pinia-best-practices","Pinia stores, state management patterns, store setup, and reactivity with stores.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-pinia-best-practices\u002FSKILL.md",[156],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},[158,159,123,94,160,161,101,162,163,164,165,135],"vue pinia best practices","pinia","with","store","management","state","state management","setup",{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":177,"publishedAt":43},"01M6MB8A7MC08747J9TV4GWQ9A","vue-options-api-best-practices","Vue 3 Options API style (data(), methods, this context). Each reference shows Options API solution only.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-options-api-best-practices\u002FSKILL.md",[172,173,176],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},{"categoryNo":174,"categoryName":175,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[123,178,179,40,94,180,181,182,183,103,135,184],"options","vue options api best practices","methods","reference","only","context","style",{"skillNo":186,"name":187,"nameZh":6,"description":188,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":189,"categories":190,"keywords":192,"publishedAt":43},"01M6MB85ZTQC24Q04X49MM4YXN","vue-development-guides","A collection of best practices and tips for developing applications using Vue.js. This skill MUST be apply when developing, refactoring or reviewing Vue.js or Nuxt projects.","https:\u002F\u002Fgithub.com\u002Fsuperhealth\u002Fskills-hub\u002Fblob\u002Fmaster\u002Fskills\u002Fvuejs-ai\u002Fvue-development-guides\u002FSKILL.md",[191],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},[193,123,194,195,135,196,197,132,198,199,200,126,102,201,202,94],"development","guides","vue development guides","tips","apply","project","nuxt","projects","collection","applications"]