10. 오류와 친구하기 (에러 처리와 디버깅)/10.1 에러 처리하기

10.1.3 에러 정보 - 문제를 자세히 알려주는 친절한 안내서

thejavascript4kids 2025. 7. 14. 04:03

📘 10.1.3 에러 정보 - 문제를 자세히 알려주는 친절한 안내서

안녕하세요, 여러분. 지난 시간에 우리는 finally를 통해 어떤 상황에서든 마무리 작업을 확실히 하는 방법을 배웠습니다. 마치 하루를 마무리하는 저녁 시간처럼, 무슨 일이 있어도 꼭 해야 할 일들을 처리하는 방법을 익혔지요.

오늘은 우리가 catch 블록에서 만나게 되는 특별한 친구, 에러 정보에 대해 자세히 알아볼 거예요. 에러 정보는 마치 문제에 대한 상세한 설명서처럼, 무엇이 잘못되었는지 친절하게 알려주는 도우미랍니다.

🧠 새로운 단어들과 친해지기

에러 정보와 함께 사용되는 중요한 단어들을 쉽게 풀어서 설명해드리겠습니다.

단어 쉬운 설명
에러 정보 (Error Object) 문제가 발생했을 때 그 문제에 대한 자세한 정보를 담고 있는 정보 묶음입니다.
message (메시지) 에러 정보 안에 들어있는 문제 상황을 설명하는 친절한 말입니다.
name (이름) 에러 정보의 종류나 이름을 나타내는 라벨 같은 것입니다.

에러 정보는 "Error Object"라는 영어에서 온 말로, 문제 상황에 대한 정보를 하나의 묶음에 정리해둔 것을 의미합니다. 마치 도서관에서 책을 분류할 때 사용하는 카드 목록처럼, 문제를 체계적으로 분류하고 설명해주는 역할을 해요.

✨ 에러 정보가 뭔지 알아보기

에러 정보는 자바스크립트에서 문제 해결의 열쇠와 같은 존재입니다. 단순히 "뭔가 잘못되었다"고 알려주는 것이 아니라, "어떤 문제가 왜 발생했는지"를 구체적이고 친절하게 설명해주는 똑똑한 안내서거든요.

에러가 발생하면 자바스크립트는 자동으로 에러 정보라는 특별한 정보 묶음을 만들어줍니다. 이 묶음 안에는 문제에 대한 중요한 정보들이 깔끔하게 정리되어 들어있어요. 가장 중요한 두 가지 정보는 namemessage입니다.

name은 문제의 종류를 알려주고, message는 문제의 구체적인 내용을 설명해줍니다. 마치 병원에서 진단서를 받을 때 "감기"라는 병명과 함께 "목이 아프고 열이 난다"는 구체적인 증상을 함께 적어주는 것과 같아요.

이런 자세한 정보 덕분에 프로그래머는 문제를 빠르게 파악하고 적절한 해결책을 찾을 수 있어요. 또한 사용자에게도 이해하기 쉬운 메시지를 보여줄 수 있고요.

따뜻한 비유: 학급 반장의 문제 신고서

에러 정보를 더 쉽게 이해하기 위해 '학급 반장의 문제 신고서'에 비유해볼게요.

교실에서 문제가 생겼을 때를 상상해보세요. 예를 들어, 누군가 실수로 화분을 넘어뜨렸다면, 반장이 선생님께 보고서를 작성해야 해요.

이 보고서에는 문제의 종류(화분 사고), 구체적인 상황(언제, 어디서, 어떻게 일어났는지), 누가 관련되었는지 등이 자세히 적혀있지요. 선생님은 이 보고서를 보고 어떤 조치를 취해야 할지 쉽게 판단할 수 있어요.

반장이 "어... 뭔가 문제가 생겼어요"라고만 말한다면 선생님은 무엇을 해야 할지 모르겠지요? 하지만 "3교시 쉬는 시간에 뒷자리 철수가 일어나다가 창가 화분을 넘어뜨려서 흙이 쏟아졌어요"라고 구체적으로 보고하면, 선생님은 바로 청소 도구를 가져오고 철수에게 조심하라고 말씀해주실 수 있어요.

에러 정보도 바로 이런 체계적인 문제 보고서와 같습니다. 코드에서 문제가 생기면 자바스크립트가 자동으로 상세한 보고서(에러 정보)를 만들어서 우리에게 전달해주는 것이지요. 덕분에 우리는 문제를 빠르게 이해하고 해결할 수 있어요.

🎯 왜 에러 정보를 사용할까요?

에러 정보가 왜 이렇게 중요한지 알아보겠습니다.

가장 큰 이유는 문제의 정확한 원인 찾기입니다. 단순히 "에러가 발생했다"고만 알려주면 무엇이 잘못되었는지 알기 어려워요. 하지만 에러 정보는 "어떤 종류의 문제인지", "구체적으로 무엇이 잘못되었는지"를 명확하게 알려줍니다.

둘째로 사용자가 이해하기 쉬운 메시지 만들기가 가능해집니다. 에러 정보를 활용하면 어려운 컴퓨터 용어 대신 사용자가 이해하기 쉬운 말로 문제를 설명할 수 있어요. 예를 들어, "SyntaxError"라는 어려운 말 대신 "입력하신 정보에 오타가 있어요"라고 친절하게 알려줄 수 있어요.

셋째로 문제 찾기와 해결하기에 큰 도움이 됩니다. 에러 정보는 개발자가 코드의 어느 부분에서 문제가 발생했는지 추적할 수 있는 중요한 단서들을 제공해요.

마지막으로 기록 남기기와 분석하기에도 활용됩니다. 어떤 종류의 문제가 얼마나 자주 발생하는지 분석하여 프로그램을 더욱 안정적으로 만들 수 있어요.

⚙️ 에러 정보 사용법 배우기

에러 정보를 만들고 사용하는 기본적인 방법들을 알아보겠습니다.

직접 에러 정보 만들기:

let myError = new Error("설명 메시지");

에러 정보의 내용 확인하기:

console.log(myError.name);     // "Error"
console.log(myError.message);  // "설명 메시지"

try...catch에서 에러 정보 받기:

try {
    throw new Error("문제가 발생했어요!");
} catch (error) {
    console.log("문제 종류: " + error.name);
    console.log("문제 내용: " + error.message);
}

이 구조에서 가장 중요한 점은 catch 블록에서 받는 error가 바로 에러 정보라는 것입니다. 이 정보를 통해 문제에 대한 자세한 내용을 얻을 수 있어요.

🧪 직접 해보면서 배우기

이제 실제 예시를 통해서 에러 정보가 어떻게 동작하는지 자세히 살펴보겠습니다.

🔹 첫 번째 예시: 에러 정보의 기본 내용 확인하기

첫 번째 예시에서는 에러 정보가 어떤 내용을 담고 있는지 직접 확인해보겠습니다.

// 에러 정보를 만들고 내용 확인하기
function exploreErrorObject() {
    try {
        // 직접 에러를 발생시켜보기
        throw new Error("이것은 연습용 에러입니다!");
    } catch (error) {
        console.log("에러 종류: " + error.name);     // 문제가 어떤 종류인지 알려줘요
        console.log("에러 메시지: " + error.message); // 문제에 대한 자세한 설명이에요
        console.log("전체 에러: " + error);          // 에러 정보 전체를 보여줘요
    }
}

exploreErrorObject();

이 과정을 단계별로 살펴보면, 먼저 new Error()를 사용해서 연습용 에러 정보를 직접 만들고 throw로 발생시킵니다. 그다음 catch 블록에서 그 에러 정보를 받아서 name, message 정보를 각각 확인해보지요. 마지막으로 에러 정보 전체를 출력해서 어떤 형태로 표시되는지도 관찰합니다.

🔹 두 번째 예시: 나이를 확인하는 기능 만들기

두 번째 예시에서는 상황에 따라 적절한 에러 메시지를 만드는 방법을 알아보겠습니다.

// 나이를 확인하는 기능에서 에러 정보 활용하기
function checkAge(age) {
    try {
        if (age < 0) {
            // 음수 나이일 때의 친절한 안내
            throw new Error("나이는 0보다 작을 수 없어요!");
        }
        if (age > 120) {
            // 너무 큰 나이일 때의 친절한 안내
            throw new Error("나이가 너무 많아요. 다시 확인해주세요!");
        }
        console.log("입력하신 나이는 " + age + "세입니다.");
    } catch (error) {
        console.log("나이 확인 문제: " + error.message); // 문제 상황을 친근하게 알려줘요
    }
}

checkAge(25);   // 정상 동작하는 모습을 봐요
checkAge(-5);   // 첫 번째 에러 메시지가 나와요
checkAge(150);  // 두 번째 에러 메시지가 나와요

이 예시에서는 다양한 상황을 처리하는 과정을 관찰할 수 있어요. 먼저 정상적인 나이(25세)를 입력하여 문제없이 처리되는 모습을 확인합니다. 그다음 음수 나이를 입력하여 첫 번째 에러 조건을 확인하고, 비현실적으로 큰 나이를 입력하여 두 번째 에러 조건을 확인하지요. 각 상황에 맞는 구체적인 에러 메시지가 어떻게 사용되는지 살펴볼 수 있어요.

🔹 세 번째 예시: 이름 입력에서 에러 정보 활용하기

세 번째 예시에서는 사용자 입력을 확인하는 상황에서 에러 정보를 활용해보겠습니다.

// 친구 이름을 확인하는 기능
function introduceMe(myName, friendName) {
    try {
        if (myName === "") {
            // 내 이름이 비어있을 때의 친절한 안내
            throw new Error("내 이름을 입력해주세요!");
        }
        if (friendName === "") {
            // 친구 이름이 비어있을 때의 친절한 안내
            throw new Error("친구 이름을 입력해주세요!");
        }
        console.log("안녕하세요! 저는 " + myName + "이고, 제 친구는 " + friendName + "입니다.");
    } catch (error) {
        console.log("소개 실패: " + error.message); // 어떤 정보가 빠졌는지 알려줘요
        console.log("다시 시도해주세요!");            // 사용자에게 격려의 메시지를 보내요
    }
}

introduceMe("철수", "영희");  // 정상 동작하는 모습을 봐요
introduceMe("", "영희");     // 내 이름 에러가 나와요
introduceMe("철수", "");     // 친구 이름 에러가 나와요

이 과정을 분석해보면, 먼저 모든 정보가 올바르게 입력된 경우를 확인하여 성공적인 소개가 이루어지는 모습을 관찰합니다. 그다음 내 이름이 빈 문자열인 경우와 친구 이름이 빈 문자열인 경우를 각각 테스트하여, 상황에 맞는 구체적인 에러 메시지가 표시되는 것을 확인하지요. 이를 통해 에러 정보가 어떻게 사용자가 이해하기 쉬운 피드백을 제공하는지 이해할 수 있어요.

🧚‍♀️ 이야기로 다시 배우기: 학교 보건실의 진료 카드

지금까지 배운 내용을 하나의 이야기로 다시 정리해볼까요?

여러분이 학교 보건실에 방문하는 상황을 상상해보세요.

몸이 아파서 보건실에 가면, 보건 선생님이 진료 카드를 작성해주세요. 이 카드에는 아주 중요한 정보들이 적혀있어요. "증상 종류"(두통, 복통, 열 등), "구체적인 상태"(언제부터 아팠는지, 어떻게 아픈지), "권장 조치"(약 복용, 휴식, 병원 방문 등) 같은 것들이지요.

이 진료 카드 덕분에 여러분은 자신의 상태를 정확히 알 수 있고, 부모님께 전화할 때도 구체적으로 설명할 수 있어요. "그냥 아파요"가 아니라 "머리가 아프고, 오전 10시부터 시작됐어요"라고 말할 수 있는 거지요.

에러 정보도 바로 이런 진료 카드와 같습니다. 코드에서 문제가 생기면 자바스크립트가 자동으로 상세한 진료 카드(에러 정보)를 만들어줍니다. 여기에는 "문제 종류"(name)와 "구체적인 상황"(message)이 친절하게 적혀있어요.

덕분에 프로그래머는 "그냥 에러가 났어요"가 아니라 "입력 값이 비어있어서 처리할 수 없어요"라고 정확히 파악할 수 있고, 사용자에게도 명확한 해결 방법을 안내할 수 있답니다.

🔄 에러 정보 활용하는 순서 정리하기

지금까지 학습한 에러 정보의 생성과 활용 과정을 자연스럽게 정리해보겠습니다.

첫 번째 단계는 문제 상황 발생입니다. 코드 실행 중에 예상치 못한 문제나 의도적으로 감지한 잘못된 상황이 발생하는 순간이지요.

두 번째로는 에러 정보 생성 단계가 있습니다. 자바스크립트가 자동으로 문제 정보를 담은 에러 정보를 만들거나, 개발자가 new Error()를 사용해서 직접 만들 수 있어요.

세 번째는 에러 정보 수집 단계입니다. 생성된 에러 정보에 문제의 종류(name)와 구체적인 설명(message) 등의 정보가 자동으로 담기게 됩니다.

네 번째로는 catch로 전달 단계가 있습니다. try...catch 구문을 통해 에러 정보가 catch 블록의 매개변수로 전달됩니다.

마지막으로 가장 중요한 에러 정보 활용 단계에서는 받은 에러 정보의 내용을 사용해서 적절한 처리를 수행합니다. 사용자에게 친절한 메시지를 보여주거나, 기록에 남기거나, 문제를 해결하는 등의 작업을 하게 되지요.

🧠 자주 하는 실수와 주의할 점

에러 정보를 사용할 때 초보자들이 자주 하는 실수들을 미리 알아두면 더 안전한 코딩을 할 수 있어요.

❌ 실수 1: 에러 정보의 유용한 내용을 무시하기

try {
    throw new Error("중요한 문제가 발생했습니다!");
} catch (error) {
    console.log("에러가 발생했어요"); // error 정보의 내용을 활용하지 않음
}

이런 실수가 발생하는 이유는 에러 정보에 얼마나 유용한 내용이 들어있는지 모르기 때문입니다. error.messageerror.name을 활용하면 훨씬 더 구체적이고 도움이 되는 메시지를 사용자에게 제공할 수 있어요. 마치 의사가 진단서를 무시하고 "그냥 아파요"라고만 말하는 것과 같아요.

❌ 실수 2: 에러 메시지를 너무 간단하게 작성하기

function checkPassword(password) {
    if (password.length < 4) {
        throw new Error("에러"); // 너무 간단해서 도움이 안 됨
    }
}

에러 메시지는 문제 해결의 중요한 단서입니다. "에러"라고만 하면 사용자는 무엇이 잘못되었는지, 어떻게 해야 하는지 전혀 알 수 없어요. "비밀번호가 너무 짧아요. 4글자 이상 입력해주세요"처럼 구체적이고 친절한 메시지를 작성해야 해요.

❌ 실수 3: 에러 정보 전체만 출력하기

try {
    someFunction();
} catch (error) {
    console.log(error); // 에러 정보 전체만 출력
}

에러 정보 전체를 출력하면 어려운 컴퓨터 정보가 너무 많이 나와서 사용자가 이해하기 어려워요. 대신 error.messageerror.name 같은 핵심 정보만 골라서 사용자가 이해하기 쉬운 형태로 보여주는 것이 좋아요.

✏️ 직접 해보기 - 연습이 필요한 순간

이제 배운 내용을 연습 문제를 통해서 확실히 익혀보겠습니다.

시간이 흘러 여러분의 손끝에서 코드가 춤을 추기 시작합니다. 한 글자 한 글자 입력할 때마다 작은 기적이 일어나는 것을 느끼실 거예요. 그 기적을 직접 만들어보세요.

Ex1) 에러 정보의 name과 message를 확인해보자

// 에러 정보의 name과 message 확인하기
function exploreError() {
    try {
        throw new Error("연습용 에러 메시지입니다!"); // 연습용 에러 정보를 만들어요
    } catch (error) {
        console.log("에러 종류: " + error.name);     // 에러의 종류를 출력해요
        console.log("에러 내용: " + error.message);  // 에러의 구체적인 내용을 출력해요
    }
}

exploreError();

이 연습을 통해 에러 정보의 기본 구조와 주요 내용들을 직접 확인할 수 있어요.

Ex2) 점수를 확인하고 문제가 있으면 적절한 메시지를 보여주자

// 점수를 확인하는 기능
function checkTestScore(score) {
    try {
        if (score < 0) {
            throw new Error("점수는 0보다 작을 수 없어요!");  // 음수 점수일 때 친절한 안내
        }
        if (score > 100) {
            throw new Error("점수는 100보다 클 수 없어요!"); // 너무 큰 점수일 때 친절한 안내
        }
        console.log("시험 점수: " + score + "점");         // 정상 점수일 때 결과 출력
    } catch (error) {
        console.log("점수 확인 실패: " + error.message);   // 문제 상황을 친근하게 알려줘요
    }
}

checkTestScore(85);   // 정상적인 점수를 확인해요
checkTestScore(-10);  // 음수 점수로 에러를 발생시켜요
checkTestScore(120);  // 100을 넘는 점수로 에러를 발생시켜요

이 문제는 에러 정보를 실제 상황에서 어떻게 활용하는지 연습하는 데 도움이 됩니다.

Ex3) 나눗셈을 할 때 0으로 나누면 "0으로 나눌 수 없어요!"라고 말해주자

// 나눗셈 계산 기능
function divide(a, b) {
    try {
        if (b === 0) {
            throw new Error("0으로 나눌 수 없어요!");  // 0으로 나누기를 시도할 때 친절한 안내
        }
        let result = a / b;                         // 정상적인 나눗셈 계산을 해요
        console.log(a + " ÷ " + b + " = " + result); // 계산 결과를 보여줘요
    } catch (error) {
        console.log("계산 문제: " + error.message);   // 문제 상황을 친근하게 알려줘요
    }
}

divide(10, 2);  // 정상적인 나눗셈을 해봐요
divide(10, 0);  // 0으로 나누기를 시도해서 에러를 발생시켜요

이 연습문제를 통해 수학적 조건을 확인하고 적절한 에러 처리를 하는 방법을 익힐 수 있어요.

🤔 조금 더 어려운 문제로 실력 확인하기

기본 연습을 마쳤다면, 이제 조금 더 깊이 있는 문제들을 통해서 에러 정보에 대한 이해를 확인해보겠습니다.

Q1. 다음 코드의 실행 결과를 예상하고 그 이유를 설명해 보세요.

try {
    let testError = new Error("테스트 메시지");
    console.log(testError.name);
    console.log(testError.message);
} catch (error) {
    console.log("catch 블록 실행");
}

정답: "Error"와 "테스트 메시지"가 순서대로 출력됩니다. catch 블록은 실행되지 않아요.

해설: new Error()는 에러 정보를 생성하기만 할 뿐, 실제로 에러를 발생시키지는 않아요. 에러를 발생시키려면 throw 키워드를 함께 사용해야 해요. 따라서 try 블록이 정상적으로 실행되고, catch 블록은 건너뛰게 됩니다. 에러 정보의 기본 name은 "Error"이고, message는 생성할 때 전달한 문자열이 저장됩니다.

Q2. 에러 메시지를 작성할 때 가장 중요한 원칙은 무엇일까요?

정답: 사용자가 이해하기 쉽고, 문제 해결에 도움이 되는 구체적인 정보를 포함해야 합니다.

해설: 좋은 에러 메시지는 단순히 "에러가 발생했다"고 알려주는 것이 아니라, "무엇이 잘못되었는지", "어떻게 해결할 수 있는지"를 명확하게 안내해야 해요. 예를 들어, "에러"보다는 "이름이 비어있어요. 이름을 입력해주세요"가 훨씬 도움이 되는 메시지입니다. 마치 친절한 선생님이 틀린 문제를 지적할 때 "틀렸어"가 아니라 "이 부분을 다시 확인해보세요"라고 구체적으로 알려주는 것과 같아요.

🔄 9단원 복습 - this와 친해지기

잠깐! 이전에 배운 내용을 다시 한번 복습해볼까요? 9단원에서 배운 this에 대해 간단한 문제로 기억을 되살려보겠습니다.

복습 문제 1: 메서드에서 this 사용하기

// 동물 정보를 담은 객체를 만들어보세요
let dog = {
    name: "멍멍이",
    age: 3,
    bark: function() {
        console.log(this.name + "이 멍멍 소리를 내요!");
    }
};

dog.bark();  // 실행해보세요!

해답과 설명:

  • this.namedog.name인 "멍멍이"를 가리켜요
  • 객체의 메서드에서 this는 그 객체 자신을 뜻해요
  • 결과: "멍멍이이 멍멍 소리를 내요!"가 출력됩니다

복습 문제 2: 화살표 함수와 일반 함수에서 this 차이

// 다음 두 함수의 차이점을 생각해보세요
let person = {
    name: "철수",
    sayHello1: function() {
        console.log("안녕, 나는 " + this.name + "야!");
    },
    sayHello2: () => {
        console.log("안녕, 나는 " + this.name + "야!");
    }
};

person.sayHello1();  // 결과는?
person.sayHello2();  // 결과는?

해답과 설명:

  • sayHello1(일반 함수): thisperson 객체를 가리켜서 "안녕, 나는 철수야!" 출력
  • sayHello2(화살표 함수): this가 제대로 동작하지 않아서 undefined 출력
  • 화살표 함수는 this를 다르게 처리하므로 메서드로 사용할 때 주의해야 해요

지금까지 에러 정보의 모든 특성과 활용법을 따뜻한 이야기와 함께 알아보았습니다. 에러 정보는 우리가 문제를 빠르고 정확하게 파악할 수 있도록 도와주는 든든한 안내서입니다. 다음 시간에는 이런 에러 처리 기법들을 더 복잡한 상황에서 어떻게 조합해서 사용하는지 배워보겠습니다!

✅ 학습 완료 체크리스트

이번 시간에 배운 내용들을 모두 이해했는지 확인해보세요!

학습 내용 이해했나요?
에러 정보의 기본 개념
기본 사용법과 문법
주요 특징과 차이점
자주 하는 실수들
실전 예제 이해

🎯 추가 연습 문제들

조금 더 연습하고 싶은 분들을 위한 추가 문제들입니다!

추가 문제 1. 에러 객체의 속성들을 확인하는 함수를 만들어보세요.

// 답:
function showErrorInfo() {
    try {
        throw new Error("연습용 에러!");
    } catch (error) {
        console.log("에러 이름:", error.name);
        console.log("에러 메시지:", error.message);
    }
}

showErrorInfo();

추가 문제 2. 간단한 메시지 함수에서 에러 객체를 사용해보세요.

// 답:
function sayHello(name) {
    try {
        if (name === "") {
            throw new Error("이름을 입력해주세요!");
        }
        console.log("안녕 " + name + "!");
    } catch (error) {
        console.log("에러: " + error.message);
    }
}

sayHello("철수");  // 정상
sayHello("");     // 에러 발생

추가 문제 3. 숫자 더하기에서 에러 객체를 사용해보세요.

// 답:
function addNumbers(a, b) {
    try {
        if (a > 1000 || b > 1000) {
            throw new Error("숫자가 너무 커요!");
        }
        let sum = a + b;
        console.log("합계: " + sum);
    } catch (error) {
        console.log("계산 실패: " + error.message);
    }
}

addNumbers(5, 3);     // 정상
addNumbers(1500, 10); // 에러 발생

추가 문제 4. 다음 코드의 결과를 예상해보고, 그 이유를 설명해보세요.

try {
    let myError = new Error("내가 만든 에러");
    console.log(myError.name);
    console.log(myError.message);
} catch (error) {
    console.log("에러 발생");
}

답: "Error"와 "내가 만든 에러"가 출력됩니다. new Error()로 만든 객체는 에러를 발생시키지 않고 단순히 객체를 만드는 것이므로 catch 블록은 실행되지 않습니다.

추가 문제 5. 다음 중 어떤 에러 메시지가 더 도움이 될까요?

// A
throw new Error("에러");

// B
throw new Error("이름이 비어있어요! 이름을 입력해주세요.");

답: B번이 더 도움이 됩니다. 구체적인 문제와 해결 방법을 포함하여 사용자가 쉽게 이해하고 대응할 수 있습니다.

📂 마무리 정보

오늘 배운 10.1.3 내용이 여러분의 자바스크립트 지식에 잘 저장되었나요? 다음 시간에는 더 흥미로운 내용으로 만나요!

기억할 점: 오늘 배운 내용을 꼭 연습해보시고, 궁금한 점이 있으면 언제든 다시 돌아와서 읽어보세요.


🚀 더 체계적인 JavaScript 학습을 원하신다면?
이 포스팅에서 다룬 내용을 실제로 실습해보세요!
무료 JavaScript 학습 플랫폼에서 단계별 학습과 실시간 코드 실행을 통해
더욱 효과적이고 재미있게 학습하실 수 있습니다.
📝 실시간 코드 실행 📊 학습 진도 관리 👥 체계적 커리큘럼
📚 171개 체계적 학습레슨 · 📋 855개 4지선다 연습문제 · 🆓 완전 무료 · ⚡ 즉시 시작