본문 바로가기
혼공학습단 11기(完)

[혼공스] 08 - 1 '구문 오류와 예외' 정리

by jaeheon0520 2024. 2. 7.

 

오늘은 08 - 1장의 내용을 정리해보자.

 

08 - 1장의 제목은 '구문 오류와 예외'로, 실무에서 오류를 처리하는 방법에 대해서 설명하고 있다. 

 

예제는 소규모이기 때문에 필요성을 못 느낄수도 있지만, 실무에서는 많이 사용하므로 꼭 기억해두자.

 

그럼 정리 시작!

 

오류의 종류

프로그래밍 언어의 오류(error)에는 크게 2가지 종류가 있다.

 

  • 프로그램 실행 전에 발생하는 오류
  • 프로그램 실행 중에 발생하는 오류

 

괄호 개수를 잘못 입력하는 등의 오류로 코드가 실행조차 되지 않는 오류를 구문 오류(syntax error)라고 하고, 이러한 문법적 오류를 제외하고 코드 실행 중간에 발생하는 오류를 예외(exception) 또는 런타임 오류(runtime error)라고 부른다. 그리고 이를 처리하는 것을 예외처리(excepetion handling)라고 부른다.

 

구문 오류

구문 오류는 괄호의 짝을 맞추지 않았다든지, 문자열을 열었는데 닫지 않았다든지 할 때 발생하는 오류이다. 이러한 구문 오류가 있으면 웹 브라우저가 코드를 분석조차 하지 못하므로 실행 되지 않는다.

 

괄호를 닫지 않은 코드

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        // 프로그램 시작 확인
        console.log("# 프로그램이 시작 되었습니다!")

        // 구문 오류가 발생하는 부분
        console.log("괄호를 닫지 않는 실수를 했습니다"
    </script>
</head>
<body>
   
</body>
</html>

 

구문 오류가 발생 하는 코드

 

코드를 실행하고 콘솔을 보면 곧바로 Syntax Error라는 오류가 발생한 것을 확인할 수 있다. 구문 오류라는 의미이다.

 

예외

예외(exception) 또는 런타임 오류(runtime error)실행중에 발생하는 오류를 의미한다. console.log() 메소드를 사용해야 하는데 console.rog() 라고 잘못 입력한 상태이다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        // 프로그램 시작 확인
        console.log("# 프로그램이 시작 되었습니다!")

        // 구문 오류가 발생하는 부분
        console.rog("log를 rog로 잘못 입력했습니다.") // 식별자를 잘못 입력했다.
    </script>
</head>
<body>
   
</body>
</html>

 

코드를 실행하고 콘솔을 살펴보자. 마찬가지로 오류가 발생해서 오류를 출력한다.

 

예외가 발생하는 코드

 

그런데 이전의 코드와 큰 차이점이 있다. 바로 "#프로그램이 시작되었습니다!"라는 문자열을 출력했다는 것이다. 즉 일단 코드는 실행된다.

 

하지만 console.rog() 줄을 읽는 순간 rog라는 식별자가 없어서 undefined인데, 이를 함수 호출 형태로 사용하니 "console.rog is not a function" 이라는 오류를 출력하는 것이다.

 

이처럼 실행 중에 발생하는 오류가 예외이다. 자바스크립트에서는 SyntaxError라고 출력되는 오류 이외에 모든 오류(TypeError, ReferenceError, RangeError)가 예외로 분류된다.

 

기본 예외 처리

조건문을 사용해서 예외가 발생하지 않게 만드는 것을 기본 예외 처리라고 부른다. 이전보다는 약간 복잡한 예제를 사용해서 기본 예외 처리를 살펴보자.

 

다음 코드는 querySelector() 메소드로 문서 객체를 추출한 뒤 textContent 속성에 글자를 할당하는 코드이다. 그런데 body 태그 내부에 h1 태그가 없다. 따라서 예외가 발생한다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
   
</body>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        const h1 = document.querySelector('h1')
        h1.textContent = '안녕하세요'
    })
</script>
</html>

 

querySelector() 메소드로 추출된 문서 객체가 없는 경우

 

문서 객체를 선택했는데 문서 객체가 없는 경우라면, 다음과 같이 조건문으로 h1이 존재하는 경우에만 textContent 속성을 변경하도록 예외 처리 할 수 있다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
   
</body>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        const h1 = document.querySelector('h1')
        if (h1) {
            h1.textContent = '안녕하세요'
        } else {
            console.log('h1 태그를 추출할 수 없습니다.')
        }
    })
</script>
</html>

 

기본 예외 처리

 

자바스크립트는 다른 프로그래밍 언어와 비교해서 굉장히 유연하기 때문에 예외를 발생할 가능성이 적은 편이다. 예를 들어 대부분의 프로그래밍 언어는 배열의 길이를 넘는 위치를 선택할 경우 오류를 발생하지만 자바스크립트는 undefined를 출력하기만 한다.

 

자바스크립트의 유연함

 

이와 같은 유연함 때문에 예외를 발생하지 않는다고 무조건 좋은 것은 아니다. 프로그램에 문제가 발생했는데도 죽지 않고 실행되면 계속해서 문제를 만들 가능성이 있다. 따라서 문제가 발생할 수 있는 부분은 조건문 등으로 처리해주어야 한다.

 

고급 예외 처리

이전 절에서 알아보았던 예외를 조금 더 쉽게 잡을 수 있는 기능으로 try catch finally 구문이 있다. 이와 같은 try catch finally 구문을 사용해서 예외를 처리하는 방법을 고급 예외 처리라고 부른다.

 

try catch finally 구문의 기본적인 형태는 다음과 같다.

 

try {
    // 예외가 발생할 가능성이 있는 코드
}	catch(exception) {
    // 예외가 발생했을 떄 실행할 코드
}	finally {
    // 무조건 실행할 코드, 필요한 경우에만 사용
}

 

try 구문 안에서 예외가 발생하면 이를 catch 구문에서 처리한다. finally 구문은 필수 사항은 아니며 예외 발생 여부와 상관없이 수행해야 하는 작업이 있을 때 사용한다.

 

다음 코드는 변수 willExcept 자체가 존재하지 않는데, willExcept의 byeBye() 메소드를 사용한다. willExcept 객체도 없고 byeBye() 메소드도 존재하지 않기 때문에 프로그램은 예외를 발생해서 종료된다. try 구문 안에서 예외가 발생하면 더 이상 try 구문을 진행하지 않고 catch 구문을 실행한다. willExcept.byeBye()를 실행하려는 순간 예외가 발생해 catch 구문을 실행한다. 따라서 첫 번째 console.log 메소드의 출력은 하지 않으며, 2번째 내용만 출력한다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        try {
            willExcept.byeBye()
            console.log('try 구문의 마지막 줄')
        } catch (exception) {
            console.log('catch 구문의 마지막 줄')
        }
    </script>
</head>
<body>
   
</body>
</html>

 

실행 결과

 

finally 구문도 간단하게 짚어보자. "finally 구문은 무조건 실행된다"라는 것을 생각하면서 다음 코드의 실행 결과를 예측해보자.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        try {
            console.log('try 구문의 첫번째 줄')
            willExcept.Byebye()
            console.log('try 구문의 마지막 줄')
        } catch (exception) {
            console.log('catch 구문의 마지막 줄')
        } finally {
            console.log('finally 구문의 마지막 줄') // 예외의 발생 여부와 관련없이 무조건 실행된다.
        }
    </script>
</head>
<body>
   
</body>
</html>

 

실행 결과

 

try 구문에서 예외가 발생하고 catch 구문이 실행된다. try 구문에서 예외가 발생하든 안하든 무조건 finally 구문은 실행되므로 위와 같은 결과를 볼 수 있다.

 

finally 구문을 사용하는 이유

try catch finally 구문을 배우고 나면 finally 구문을 왜 써야 하는지에 대해 궁금증을 가질 수 있다. catch 구문 내부에 finally 구문을 넣어도 결과가 비슷하게 나올 수 있다고 생각할 수 있는데 그 차이를 다음 코드를 통해 알아보자.

 

두 코드의 실행 결과를 예측해보자. catch 구문 내부에서 return 키워드를 사용한 경우이다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        function test () {
            try {
                console.log('A 위치입니다.')
                throw "예외 강제 발생" // throw 키워드로 예외를 강제로 발생시킨다.
            } catch (exception) {
                console.log('B 위치입니다.')
                return
            }
            alert('C 위치입니다.')
        }

        test()
    </script>
</head>
<body>
   
</body>
</html>

 

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <script>
        function test () {
            try {
                console.log('A 위치입니다.')
                throw "예외 강제 발생" // throw 키워드로 예외를 강제로 발생시킨다.
            } catch (exception) {
                console.log('B 위치입니다.')
                return
            } finally {
                console.log('C 위치입니다.')
            }
        }

        test()
    </script>
</head>
<body>
   
</body>
</html>

 

첫번째 코드의 실행결과

 

두번째 코드의 실행결과

 

코드를 실행하면

 

첫번째 코드는, "A 위치입니다." 와 "B 위치입니다."만 출력하는 것을 볼 수 있다. return 키워드를 사용해 함수를 벗어났으므로 "C 위치입니다."라는 글자를 출력하지 않는 것이다.

 

하지만 두번째 코드는 "A 위치입니다.", "B 위치입니다.", "C 위치입니다."를 모두 출력한다. 이는 finally 구문을 반드시 실행한다는 특성 때문이다.

 

이처럼 다음과 같은 경우에 결과가 달라진다.

 

  • try catch 구문 내부에서 return 키워드를 만날 때
  • try catch 구문 내부에서 break 또는 continue 키워드를 만날 때

 

Node.js처럼 서버로 사용하는 자바스크립트에서는 이러한 내용을 알아야 안전하게 사용할 수 있으니 주의하도록 하자.

 

확인문제

1. 다음 코드 중에서 구문 오류가 발생하는 코드를 고르세요.

 

① cons 오타 => 구문 오류 발생

<script>
    cons a = 10
    console.log(a * a)
</script>

 

② llog() 없는 식별자 사용 => 예외 발생(TypeError)

<script>
    console.llog('안녕하세요')
</script>

 

③ number() 없는 식별자 사용 => 예외발생 (TypeError)

<script>
    const number = 10
    console.log(number() + number())
</script>

 

④ 잘못된 인덱스 사용 => undefined 반환, 예외로 처리되지 않음

<script>
    const number = 20
    console.log(number[20])
</script>

 

2. 예외 처리 구문의 조합으로 옳지 않은 것을 고르세요.

 

try {} catch (exception) {} finally{}

try {} finally {}

③ try {} finally {} catch (exception) {}

try {} catch (exception) {}

 

풀이: try - catch - finally 순서로 작성해야 한다. catch와 finally 둘 중 하나는 생략 될 수 있다. 하지만 모두 생략되어서는 안된다. 

 

3. 다음 코드 중에서 try catch finally 구문으로 처리할 수 없는 코드를 고르세요.

 

error, error() 없는 식별자 사용 => 예외 발생(처리가능)(Reference Error)

<script>
    error.error.error()
</script>

 

배열 범위를 벗어난 index 사용 => 예외 발생하지 않음

<script>
    let array = [1, 2, 3, 4, 5]
    console.log(array[-100])
</script>

 

객체 생성 방법이 잘못됨 (NEW => new) => 구문 오류, try catch finally로 처리할 수 없음

<script>
    let number = NEW Number(10)
</script>

 

④ number() 없는 식별자 사용 => 예외 발생 (TypeError)

<script>
    let number = 20
    number()
</script>

 

08 - 1장의 내용은 이렇게 마무리하면 될 것 같다.

 

오류의 종류와 예외를 처리하는 2가지 방법에 대해서 알아보았다.

 

내일은 08 - 2장의 내용을 정리하고 마지막 6주차 미션을 진행해보자.

 

오늘 하루도 쌓였다.