본문으로 건너뛰기 JavaScript Error Handling | try/catch· the Error Object

JavaScript Error Handling | try/catch· the Error Object

JavaScript Error Handling | try/catch· the Error Object

이 글의 핵심

Master JavaScript error handling: try/catch/finally, throw, built-in Error types, custom errors, async errors with Promises and async/await, retries, and logging patterns.

Introduction

Error handling deals with exceptional conditions that occur while your program runs.

1. try-catch-finally

Basics

try {
    const result = riskyOperation();
    console.log(result);
} catch (error) {
    console.error("에러 발생:", error.message);
} finally {
    console.log("정리 작업");
}

Practical example

function divide(a, b) {
    if (b === 0) {
        throw new Error("0으로 나눌 수 없습니다");
    }
    return a / b;
}
try {
    console.log(divide(10, 2));  // 5
    console.log(divide(10, 0));  // Error!
    console.log("이 줄은 실행 안 됨");
} catch (error) {
    console.error("에러:", error.message);
} finally {
    console.log("계산 완료");
}

Nested try-catch

try {
    try {
        throw new Error("내부 에러");
    } catch (innerError) {
        console.log("내부 처리:", innerError.message);
        throw new Error("외부 에러");
    }
} catch (outerError) {
    console.log("외부 처리:", outerError.message);
}

2. The Error object

Built-in error types

// Error: generic
throw new Error("일반 에러");
// SyntaxError
try {
    eval("{ invalid json");
} catch (e) {
    console.log(e.name);  // SyntaxError
}
// ReferenceError
try {
    console.log(nonExistent);
} catch (e) {
    console.log(e.name);  // ReferenceError
}
// TypeError
try {
    null.toString();
} catch (e) {
    console.log(e.name);  // TypeError
}
// RangeError
try {
    new Array(-1);
} catch (e) {
    console.log(e.name);  // RangeError
}

Error properties

try {
    throw new Error("테스트 에러");
} catch (error) {
    console.log(error.name);     // Error
    console.log(error.message);  // 테스트 에러
    console.log(error.stack);    // stack trace
}

3. Custom errors

Custom error classes

class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = "ValidationError";
    }
}
class NetworkError extends Error {
    constructor(message, statusCode) {
        super(message);
        this.name = "NetworkError";
        this.statusCode = statusCode;
    }
}
function validateAge(age) {
    if (typeof age !== 'number') {
        throw new ValidationError("나이는 숫자여야 합니다");
    }
    if (age < 0 || age > 150) {
        throw new ValidationError("나이는 0-150 사이여야 합니다");
    }
    return true;
}
try {
    validateAge("25");
} catch (error) {
    if (error instanceof ValidationError) {
        console.error("유효성 에러:", error.message);
    } else {
        console.error("알 수 없는 에러:", error);
    }
}

Handling multiple error types

class DatabaseError extends Error {
    constructor(message, query) {
        super(message);
        this.name = "DatabaseError";
        this.query = query;
    }
}
function processData(data) {
    try {
        if (!data) {
            throw new ValidationError("데이터가 없습니다");
        }
        
        if (data.age < 0) {
            throw new ValidationError("나이는 양수여야 합니다");
        }
        
        return data;
    } catch (error) {
        if (error instanceof ValidationError) {
            console.error("유효성 에러:", error.message);
        } else if (error instanceof DatabaseError) {
            console.error("DB 에러:", error.message, error.query);
        } else {
            console.error("알 수 없는 에러:", error);
        }
        throw error;
    }
}

4. Asynchronous errors

Promises

// .catch()
fetch("https://api.example.com/data")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error("에러:", error));
// Error mid-chain
Promise.resolve(1)
    .then(x => {
        throw new Error("에러!");
    })
    .then(x => console.log(x))
    .catch(error => console.error(error.message))
    .then(() => console.log("복구됨"));
// Multiple promises
Promise.all([
    fetch("/api/users"),
    fetch("/api/posts")
])
.then(responses => Promise.all(responses.map(r => r.json())))
.then(data => console.log(data))
.catch(error => console.error("하나라도 실패:", error));

async/await

async function fetchData() {
    try {
        const response = await fetch("https://api.example.com/data");
        
        if (!response.ok) {
            throw new NetworkError(`HTTP ${response.status}`, response.status);
        }
        
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("에러:", error.message);
        return null;
    }
}
async function complexOperation() {
    try {
        const data = await fetchData();
        const result = processData(data);
        return result;
    } catch (error) {
        if (error instanceof NetworkError) {
            console.error("네트워크 에러:", error.statusCode);
        } else if (error instanceof ValidationError) {
            console.error("유효성 에러:", error.message);
        } else {
            console.error("알 수 없는 에러:", error);
        }
        throw error;
    }
}

5. Practical patterns

Pattern 1: Result wrapper

class Result {
    constructor(success, data, error) {
        this.success = success;
        this.data = data;
        this.error = error;
    }
    
    static ok(data) {
        return new Result(true, data, null);
    }
    
    static fail(error) {
        return new Result(false, null, error);
    }
}
async function fetchUserSafe(id) {
    try {
        const response = await fetch(`/api/users/${id}`);
        const user = await response.json();
        return Result.ok(user);
    } catch (error) {
        return Result.fail(error.message);
    }
}
const result = await fetchUserSafe(1);
if (result.success) {
    console.log("데이터:", result.data);
} else {
    console.error("에러:", result.error);
}

Pattern 2: Retry with backoff

async function retry(fn, maxRetries = 3, delay = 1000) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await fn();
        } catch (error) {
            if (i === maxRetries - 1) {
                throw error;
            }
            console.log(`재시도 ${i + 1}/${maxRetries}`);
            await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
        }
    }
}
retry(() => fetch("https://api.example.com/data"))
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error("최종 실패:", error));

Pattern 3: Error logging

class ErrorLogger {
    static log(error, context = {}) {
        const errorInfo = {
            name: error.name,
            message: error.message,
            stack: error.stack,
            timestamp: new Date().toISOString(),
            ...context
        };
        
        console.error("에러 로그:", JSON.stringify(errorInfo, null, 2));
        
        // Send to server
        // fetch('/api/errors', { method: 'POST', body: JSON.stringify(errorInfo) });
    }
}
try {
    throw new Error("테스트 에러");
} catch (error) {
    ErrorLogger.log(error, { userId: 123, action: "데이터 로드" });
}

6. Practical example: API client

class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    async request(endpoint, options = {}) {
        const url = `${this.baseUrl}${endpoint}`;
        
        try {
            const response = await fetch(url, options);
            
            if (!response.ok) {
                throw new NetworkError(
                    `HTTP ${response.status}: ${response.statusText}`,
                    response.status
                );
            }
            
            const data = await response.json();
            return Result.ok(data);
        } catch (error) {
            if (error instanceof NetworkError) {
                console.error("네트워크 에러:", error.message);
            } else if (error instanceof SyntaxError) {
                console.error("JSON 파싱 에러:", error.message);
            } else {
                console.error("알 수 없는 에러:", error);
            }
            return Result.fail(error.message);
        }
    }
    
    async get(endpoint) {
        return this.request(endpoint);
    }
    
    async post(endpoint, body) {
        return this.request(endpoint, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(body)
        });
    }
}
const api = new ApiClient("https://api.example.com");
const result = await api.get("/users/1");
if (result.success) {
    console.log("사용자:", result.data);
} else {
    console.error("에러:", result.error);
}

Summary

Key takeaways

  1. try-catch-finally: structured error handling
  2. throw: signal failure
  3. Error object: name, message, stack
  4. Custom errors: class extends Error
  5. Async: .catch() or try/catch with async/await

Tips

  • Clear messages: make failures easy to diagnose
  • Branch by type: use instanceof (or error codes) for control flow
  • Re-throw: propagate with throw error when you cannot handle
  • Logging: record enough context for production debugging

Next steps



Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Master JavaScript error handling: try/catch/finally, throw, built-in Error types, custom errors, async errors with Promi…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the JavaScript series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers JavaScript, Error Handling, try-catch, Error, Exception, Debugging.