Error handling
Skill ComeOnOliver/skillshub/skills/aiskillstore/marketplace/doyajin174/error-handling
π§ The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.
npx -y skills add ComeOnOliver/skillshub --skill error-handlingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
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.
The file declares its own license as MIT. That is the authorβs claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
9.9 KB, as published. Nobody here has run it
Error Handling Patterns
μ μ ν μλ¬ μ²λ¦¬ ν¨ν΄μ κ°μ νλ μ€ν¬μ λλ€.
Core Principle
"μλ¬λ μ¨κΈ°μ§ μκ³ , μ μ ν μ²λ¦¬νκ³ , μ¬μ©μμκ² μλ¦°λ€." "Fail gracefully, recover when possible."
Rules
| κ·μΉ | μν | μ€λͺ |
|---|---|---|
| λΉ catch λΈλ‘ κΈμ§ | π΄ νμ | μ΅μ λ‘κΉ νμ |
| μ¬μ©μ μΉνμ λ©μμ§ | π΄ νμ | κΈ°μ μ μλ¬ λ©μμ§ λ ΈμΆ κΈμ§ |
| Error Boundary μ¬μ© | π΄ νμ (React) | μ»΄ν¬λνΈ μλ¬ κ²©λ¦¬ |
| Graceful Degradation | π‘ κΆμ₯ | λΆλΆ μ€ν¨ μ λμ μ 곡 |
κΈ°λ³Έ ν¨ν΄
Try-Catch μ¬λ°λ₯Έ μ¬μ©
// β BAD: λΉ catch λΈλ‘
try {
await fetchData();
} catch (e) {
// μ무κ²λ μ ν¨ - μλ¬ λ¬΄μ
}
// β BAD: λͺ¨λ μλ¬ λμΌ μ²λ¦¬
try {
await fetchData();
} catch (e) {
console.log('μλ¬ λ°μ'); // μ 보 λΆμ‘±
}
// β
GOOD: μ μ ν μλ¬ μ²λ¦¬
try {
await fetchData();
} catch (error) {
// 1. μλ¬ λ‘κΉ
(κ°λ°μμ©)
console.error('fetchData failed:', error);
// 2. μλ¬ μΆμ μλΉμ€ μ μ‘
errorTracker.capture(error);
// 3. μ¬μ©μμκ² μλ¦Ό
showToast('λ°μ΄ν°λ₯Ό λΆλ¬μ€λλ° μ€ν¨νμ΅λλ€. λ€μ μλν΄μ£ΌμΈμ.');
// 4. νμμ μ¬μλ λλ λμ μ 곡
return fallbackData;
}
μλ¬ νμ ꡬλΆ
// β
GOOD: μλ¬ νμ
λ³ μ²λ¦¬
async function fetchUser(id: string) {
try {
const response = await api.get(`/users/${id}`);
return response.data;
} catch (error) {
if (error instanceof NetworkError) {
// λ€νΈμν¬ μλ¬: μ¬μλ μ μ
showToast('λ€νΈμν¬ μ°κ²°μ νμΈν΄μ£ΌμΈμ.');
return null;
}
if (error instanceof NotFoundError) {
// 404: μ¬μ©μ μμ
showToast('μ¬μ©μλ₯Ό μ°Ύμ μ μμ΅λλ€.');
return null;
}
if (error instanceof AuthError) {
// μΈμ¦ μλ¬: λ‘κ·ΈμΈ νμ΄μ§λ‘
router.push('/login');
return null;
}
// μμμΉ λͺ»ν μλ¬
console.error('Unexpected error:', error);
errorTracker.capture(error);
showToast('μ€λ₯κ° λ°μνμ΅λλ€. μ μ ν λ€μ μλν΄μ£ΌμΈμ.');
return null;
}
}
컀μ€ν μλ¬ ν΄λμ€
// errors.ts
export class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode?: number,
public isOperational: boolean = true
) {
super(message);
this.name = 'AppError';
}
}
export class ValidationError extends AppError {
constructor(message: string, public field?: string) {
super(message, 'VALIDATION_ERROR', 400);
this.name = 'ValidationError';
}
}
export class NetworkError extends AppError {
constructor(message: string = 'λ€νΈμν¬ μ°κ²°μ νμΈν΄μ£ΌμΈμ') {
super(message, 'NETWORK_ERROR', 0);
this.name = 'NetworkError';
}
}
export class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource}μ(λ₯Ό) μ°Ύμ μ μμ΅λλ€`, 'NOT_FOUND', 404);
this.name = 'NotFoundError';
}
}
React Error Boundary
κΈ°λ³Έ Error Boundary
// ErrorBoundary.tsx
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
this.props.onError?.(error, errorInfo);
// μλ¬ μΆμ μλΉμ€λ‘ μ μ‘
errorTracker.captureException(error, { extra: errorInfo });
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultErrorFallback error={this.state.error} />;
}
return this.props.children;
}
}
// κΈ°λ³Έ ν΄λ°± UI
function DefaultErrorFallback({ error }: { error?: Error }) {
return (
<div className="error-fallback">
<h2>λ¬Έμ κ° λ°μνμ΅λλ€</h2>
<p>νμ΄μ§λ₯Ό μλ‘κ³ μΉ¨νκ±°λ μ μ ν λ€μ μλν΄μ£ΌμΈμ.</p>
<button onClick={() => window.location.reload()}>
μλ‘κ³ μΉ¨
</button>
</div>
);
}
Error Boundary μ¬μ©
// μ± μ 체 κ°μΈκΈ°
function App() {
return (
<ErrorBoundary fallback={<FullPageError />}>
<Router>
<Routes />
</Router>
</ErrorBoundary>
);
}
// νΉμ μΉμ
λ§ κ°μΈκΈ°
function Dashboard() {
return (
<div>
<Header />
<ErrorBoundary fallback={<ChartError />}>
<Chart data={data} />
</ErrorBoundary>
<ErrorBoundary fallback={<TableError />}>
<DataTable data={data} />
</ErrorBoundary>
</div>
);
}
Async μλ¬ μ²λ¦¬
Promise μλ¬
// β BAD: unhandled rejection
fetchData().then(data => setData(data));
// β
GOOD: catch μ²λ¦¬
fetchData()
.then(data => setData(data))
.catch(error => {
console.error('Failed to fetch:', error);
setError(error);
});
// β
BETTER: async/await
async function loadData() {
try {
const data = await fetchData();
setData(data);
} catch (error) {
console.error('Failed to fetch:', error);
setError(error);
}
}
μ¬λ¬ Promise μ²λ¦¬
// β BAD: νλλΌλ μ€ν¨νλ©΄ μ 체 μ€ν¨
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts(),
]);
// β
GOOD: κ°λ³ κ²°κ³Ό μ²λ¦¬
const results = await Promise.allSettled([
fetchUsers(),
fetchPosts(),
]);
const users = results[0].status === 'fulfilled' ? results[0].value : [];
const posts = results[1].status === 'fulfilled' ? results[1].value : [];
// μ€ν¨ν κ²λ§ λ‘κΉ
results
.filter((r): r is PromiseRejectedResult => r.status === 'rejected')
.forEach(r => console.error('Failed:', r.reason));
Graceful Degradation
κΈ°λ₯ μ ν ν¨ν΄
// β
GOOD: μ€ν¨ μ λμ μ 곡
async function getRecommendations(userId: string) {
try {
// 1μ°¨: κ°μΈνλ μΆμ²
return await fetchPersonalizedRecommendations(userId);
} catch (error) {
console.warn('Personalized recommendations failed:', error);
try {
// 2μ°¨: μΈκΈ° μ½ν
μΈ
return await fetchPopularContent();
} catch (error) {
console.warn('Popular content failed:', error);
// 3μ°¨: μΊμλ κΈ°λ³Έ μΆμ²
return getCachedDefaultRecommendations();
}
}
}
UI λμ μ 곡
function UserAvatar({ userId }: { userId: string }) {
const [imageError, setImageError] = useState(false);
const user = useUser(userId);
if (imageError || !user?.avatarUrl) {
// μ΄λ―Έμ§ λ‘λ μ€ν¨ μ λμ
return (
<div className="avatar-placeholder">
{user?.name?.charAt(0) || '?'}
</div>
);
}
return (
<img
src={user.avatarUrl}
alt={user.name}
onError={() => setImageError(true)}
/>
);
}
μ¬μ©μ μΉνμ λ©μμ§
λ©μμ§ λ§€ν
const errorMessages: Record<string, string> = {
NETWORK_ERROR: 'λ€νΈμν¬ μ°κ²°μ νμΈν΄μ£ΌμΈμ.',
UNAUTHORIZED: 'λ‘κ·ΈμΈμ΄ νμν©λλ€.',
FORBIDDEN: 'μ κ·Ό κΆνμ΄ μμ΅λλ€.',
NOT_FOUND: 'μμ²ν μ 보λ₯Ό μ°Ύμ μ μμ΅λλ€.',
VALIDATION_ERROR: 'μ
λ ₯ μ 보λ₯Ό νμΈν΄μ£ΌμΈμ.',
RATE_LIMIT: 'μμ²μ΄ λ무 λ§μ΅λλ€. μ μ ν λ€μ μλν΄μ£ΌμΈμ.',
SERVER_ERROR: 'μλ² μ€λ₯κ° λ°μνμ΅λλ€. μ μ ν λ€μ μλν΄μ£ΌμΈμ.',
DEFAULT: 'μ€λ₯κ° λ°μνμ΅λλ€. λ€μ μλν΄μ£ΌμΈμ.',
};
function getUserFriendlyMessage(error: unknown): string {
if (error instanceof AppError) {
return errorMessages[error.code] || errorMessages.DEFAULT;
}
return errorMessages.DEFAULT;
}
π΄ κΈμ§: κΈ°μ μ λ©μμ§ λ ΈμΆ
// β BAD: μ¬μ©μμκ² κΈ°μ μ λ©μμ§ νμ
showToast(error.message); // "TypeError: Cannot read property 'id' of undefined"
showToast(error.stack); // μ€ν νΈλ μ΄μ€ λ
ΈμΆ
// β
GOOD: μΉνμ λ©μμ§
showToast(getUserFriendlyMessage(error));
λ‘κΉ μ λ΅
// logger.ts
export const logger = {
error: (message: string, error: unknown, context?: object) => {
// κ°λ° νκ²½: μ½μ μΆλ ₯
if (process.env.NODE_ENV === 'development') {
console.error(message, error, context);
}
// νλ‘λμ
: μλ¬ μΆμ μλΉμ€
errorTracker.captureException(error, {
tags: { message },
extra: context,
});
},
warn: (message: string, context?: object) => {
console.warn(message, context);
},
};
Checklist
μ½λ μμ± μ
- try-catchμ μ μ ν μλ¬ μ²λ¦¬ λ‘μ§
- λΉ catch λΈλ‘ μμ
- μλ¬ νμ λ³ λΆκΈ° μ²λ¦¬
- μ¬μ©μ μΉνμ λ©μμ§ νμ
- μλ¬ λ‘κΉ /μΆμ
React μ»΄ν¬λνΈ
- Error Boundary μ μ©
- λ‘λ©/μλ¬ μν UI
- μ¬μλ κΈ°λ₯ μ 곡
- ν΄λ°± UI ꡬν
API νΈμΆ
- λ€νΈμν¬ μλ¬ μ²λ¦¬
- νμμμ μ²λ¦¬
- μ¬μλ λ‘μ§ (νμμ)
- μΊμ ν΄λ°± (νμμ)