탱구탱구 개발자 일기

프로그래밍 언어에서 스코프(scope, 유효 범위)는 가장 기본적인 개념이다. 특히 자바스크립트에서는 스코프에 대해 이해하지 못한다면 개발 도중 예기치 못한 오류들이 남발하는 상황에 맞닥뜨릴 수 있다. 아래 코드의 실행 결과에 대해 예측해보자.

 

[스코프 예제 코드]

// 자바스크립트 스코프(scope)

// 전역 변수 선언
var a = 10;

// 함수선언문으로 scope 선언
function scope() {
    var a = 20;
    console.log('a:', a); // 1. a.value = ?

    if (true) {
        console.log('a:', a); // 2. a.value = ?
        var a = 50;
        console.log('a:', a); // 3. a.value = ?
    }

    var f1 = inner();

    function inner() {
        var a = 30;
        var b = 5;
        console.log('a:', a); // 4. a.value = ?
        console.log('b:', b); // 5. b.value = ?
    }
    console.log('a:', a); // 6. a.value = ?
    console.log('b:', b); // 7. b.value = ?
}

// scope 함수 호출
scope();
console.log('a:', a); // 8. a.value = ?
console.log('b:', b); // 9. b.value = ?

 

 

예제가 조금 복잡할 수도 있다. 예제를 설명하면 크게 3 부분으로 나눠서 볼 수 있다.

  • 먼저 처음에 var 변수 선언 키워드를 통해서 변수 a가 전역 변수로 선언되었다.
  • 두 번째로 함수 scope가 선언되어 있는데 그 안에 중첩함수로 inner 함수가 선언되어있다.
  • 마지막으로 해당 scope 함수를 호출하는 코드와 콘솔 실행부로 되어있다.

위 코드에서 변수명 a, b로 선언된 변수의 값이 어떻게 바뀌는지 살펴보면 된다.

 

결론부터 말하면 아래와 같다.

1. a : 20
2. a : 20
3. a : 50
4. a : 30
5. b : 5
6. a : 50
7. Uncaught ReferenceError: b is not defined // 참조오류로 인해 프로그램이 멈춤

참조 오류를 무시하고 끝까지 진행된다고 가정했을 때 아래 값이 추가된다.

8. a: 10
9. Uncaught ReferenceError: b is not defined

 

이처럼 자바스크립트는 이름이 같은 변수를 참조할 때 스코프를 통해 어떤 변숫값을 끌어와야 할지 결정한다. 즉, 스코프는 참조 대상 식별자(identifier)를 찾아내기 위한 규칙이다. 

 

  • 전역 변수로 선언된 변수 a의 값은 프로그램 어디에서든 참조할 수 있다.
  • 그러나 함수 scope 안에서 선언된 변수 a는 해당 함수 내부에서만 참조 가능하다.
  • 함수 scope 내 if 조건문 안에서 선언된 변수 a 또한 scope 함수 안에서 유효하다.
  • 함수 scope에 중첩된 함수 inner에서 선언된 변수 a, b는 함수 inner 범위 안에서 유효하다.

따라서 실행 문맥 순서에서 변수 f1에 inner 함수까지 할당하고 난 뒤 console에서 b를 찍으려고 했지만 b는 이미 메모리 상에서 지워진 상태이므로 Uncaught ReferenceError: b is not defined라는 참조 오류가 발생한 것이다.

 

또한 만약에 inner 함수에서 변수 a에 대한 선언이 없었다면 inner 함수 안에서 찍힌 console.a는 외부 함수 scope의 지역변수 a의 참조값을 읽는다. 이처럼 스코프는 정말 중요하다.

 

 

[스코프의 구분]

 

자바스크립트에서 스코프는 2가지로 구분된다.

  • 전역 스코프 (Global scope) : 프로그램 어디든 참조 가능
  • 지역 스코프 (Local scope or Function-level scope) : 함수 블록이 만든 스코프로 함수 자신과 내부 함수에서만 참조 가능

[변수의 스코프]

  • 전역 변수 (Global variable) : 전역에서 선언된 변수이며 어디에든 참조 가능
  • 지역 변수 (Local variable) : 지역(함수) 내에서 선언된 변수이며 그 지역과 그 지역의 하부 지역에서만 참조 가능

[자바스크립트 스코프 특징]

 

자바스크립트는 타 언어와는 구별되는 특징이 있다. 위 코드 중 일부를 가져왔다.

function scope() {
    var a = 20;
    console.log('a:', a); // 1. a.value = ?

    if (true) {
        console.log('a:', a); // 2. a.value = ?
        var a = 50;
        console.log('a:', a); // 3. a.value = ?
    }
    console.log('a:', a); // 4. a.value = ?
 ...   
}

코드를 보면 if 조건문 안에서 선언된 변수 a는 조건문 안에서만 유효할 것 같다. 따라서 조건문 밖에 있는 콘솔 값에는 20이 찍힐 것 같다. 그러나 if 조건문 안에서 선언된 변수 a는 조건문 밖에서도 유효하다. 왜 그럴까??

 

자바나 C에서는 블록(중괄호) 안에서 선언한 변수는 해당 블록 안에서만 유효하지 않는가?? 이러한 특징을 블록 레벨 스코프(block-level-scope)라고 한다. 아래 자바 코드에서 확인해보자.

public class HelloWorld {

    public static void main(String[] args) {
        test();
    }
    
    public static void test(){
         
         if(true) {
             int x = 10;
             System.out.println("x : " + x); // x : 10
         }
          System.out.println("x : " + x); // 에러
    }
}

//Compilation Errors Detected

//File: ALIEN_31015/source/domain/HelloWorld.java
//Line: 13
//cannot find symbol
  //symbol:   variable x
  //location: class domain.HelloWorld

test라는 메서드를 main에서 실행시켰을 때 if 조건문 안에서 선언된 변수 x는 해당 블록 안에서는 접근 가능하지만 블록 밖에서는 변수를 찾을 수 없다는 오류가 발생한다. 

 

그러나 자바스크립트는 기본적으로 함수 레벨 스코프(function-level-scope)를 따른다. 이는 함수 코드 블록 내에서 선언된 변수는 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다는 것이다.

 

function scope() {
    var a = 20;
    console.log('a:', a); // 1. a.value = 20

    if (true) {
        console.log('a:', a); // 2. a.value = 20
        var a = 50;
        console.log('a:', a); // 3. a.value = 50
    }
    console.log('a:', a); // 4. a.value = 50
 ...   
}

console.log('a:', a); // Uncaught ReferenceError: a is not defined

즉, if 조건문의 블록과 상관없이 해당 변수가 선언되어 있는 함수 코드 블록 내부이기 때문에 마지막 콘솔 실행 문맥에서도 변수 a에 대한 참조가 가능할 뿐만 아니라 조건문 내부에서 접근한 변수를 변경하더라도 그 값이 적용된다는 의미다.

그리고 해당 함수 외부 환경에선 접근이 불가능하다.

 

이러한 자바스크립트 특징 때문에 타 언어와 다르게 변수 선언에 대한 자유로움이 있었고 그로 인한 이점도 있었지만 전역 변수를 남발하게 되는 현상이 생겼다. 또한 동일한 변수명으로 재선언, 재할당이 가능했기 때문에 프로그램이 고도화되었을 때 메모리 누수, 디버깅의 어려움, 가독성 저하 등의 문제로 인해 유지보수가 힘들다는 문제점이 발생했다.

 

이러한 문제들을 해결하고자 자바스크립트에서도 ECMAScript6 이후 블록 레벨 스코프를 갖는 새로운 변수 선언 키워드인 let, const가 등장하였다. 이 내용은 다음글에서 살펴보겠다.

이 글을 공유합시다

facebook twitter kakaoTalk kakaostory naver band
loading