본문 바로가기

개발공부/기술면접준비

스코프(Scope)에 대해서

스코프(scope)란?

Scope를 직역하면 영역, 범위라는 뜻이다.
하지만 프로그래밍 언어에서 스코프는 어느 범위까지 참조하는지.
즉, 변수와 매개변수(parameter)의 접근성과 생존 기간을 뜻한다.
따라서 유효범위의 개념을 잘 알고 있으면 변수와 매개변수의 접근성과 생존기간을 제어할 수 있다.

스코프(scope)의 종류

유효범위의 종류는 크게 두가지가 있다.
하나는 전역 스코프(Global Scope), 또 하나는 지역 스코프(Local Scope)이다.

전역 스코프(Global Scope)는 스크립트 전체에서 참조되는 것을 의미하는데, 말 그대로 스크립트 내 어느 곳에서든 참조된다.
지역 스코프(Local Scope or Function-level Scope)는 정의된 함수 코드 블록이 만든 스코프로 함수 자신과 자신의 하위 함수에서만 참조할 수 있고, 함수 밖에서는 참조하지 못한다.

전역 변수(Global variable)는 전역에서 선언된 변수로 어디든 참조 가능하다.
지역 변수(Local variable)는 지역(함수)내에 선언된 변수로 그 지역(함수)과 그 지역의 하위 지역에서만 참조 가능하다.

예제코드와 함께 한번 살펴 보도록 하자

var x = 'global';

function example() {
    var x  = 'function scope';
    console.log(x);
}

example();
console.log(x);

여기서 첫번째 줄에서 var x='global';, function example()내에서 var x = 'function scope';가 선언되었다.

그렇다면 위 예제코드의 console.log(x);는 각각 어떠한 값을 의미할까?

함수 example내에서 기재된 console.log(x)에는 function scope를 의미하고 있으며,
함수 바깥에서 기재된 console.log(x)에는 global를 의미하고 있다.

이와 같이 같은 변수명 x가 중복 선언되었을때, 중복된 2개의 변수 중 어떤 변수를 참조해야하는지?
그리고 자바스크립트는 어떻게 변수를 식별하는것인지에 대해서 알아보도록 하자.

스코프는 참조 대상 식별자(identifier, 변수, 함수의 이름과 같이 어떤 대상을 다른 대상과 구분하여 식별할 수 있는 유일한 이름)를 찾아내기 위한 규칙이다.
자바스크립트는 이 규칙대로 식별자를 찾는다.

프로그래밍은 변수를 선언하고 값을 할당하며 변수를 참조하는 기본적인 기능을 제공하며 이것으로 프로그램의 상태를 관리할 수 있다.
변수는 전역 또는 코드 블록(if, for, while과 같은 반복문, try/catch 등)이나 함수 내에 선언하며 코드블록이나 함수는 중첩될 수 있다.
식별자는 자신이 어디에서 선언됐는지에 의해 자신이 유효한(다른 코드가 자신을 참조할 수 있는) 범위를 갖는다.

위 예제에서 전역에서 선언된 var x = 'global';는 어디에든 참조할 수 있다.
하지만 함수 example()내에서 선언된 변수 var x = 'function scope';는 함수 example()내부에서만 참조할 수 있고,
함수 외부에서는 참조 할 수 없다.
이러한 규칙을 스코프라고 한다.

만약 스코프가 없다면, 같은 식별자 이름은 충돌을 일으키게 되므로 프로그램 전체에서 하나밖에 사용할 수 없게 된다.
디렉토리가 없는 컴퓨터로 예를 들자면 같은 이름을 갖는 파일을 하나밖에 만들수없게 되는것이다.

스코프도 이와 같이 식별자 이름의 충돌을 방지한다.

스코프의 특징

블록 레벨 스코프와 함수 레벨 스코프(block-level scope & function-level scope)

블록 레벨 스코프(Block-level Scope)

C언어의 경우 if문 내에서 선언된 변수에 대해서는 if문 코드블록 내에서만 유효하다.
즉, if문 코드블록 밖에서는 참조가 불가능하다.

반면, 위 이미지와 같이 자바스크립트는 함수 레벨 스코프(Function-level Scope)를 따른다.
함수 레벨 스코프란 함수 코드블록 내에서 선언된 변수는 함수 코드블록 내에서만 유효하고 함수 외부에서는 유효하지 않다(참조할 수 없다)는 것이다.
var키워드를 사용한 변수 x는 블록 레벨 스코프를 무시하기 때문에 블록 외부의 console.log(x);에 대한 값이 1로 나타내어지고 있다.

하지만 ES6에서 도입된 let키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다.
변수 y는 let키워드를 사용한 예시이다.

위 이미지는 var키워드는 블록 스코프를 무시한다는 예시를 한번 더 보여주고 있다.
따라서 var키워드를 사용한 변수 x는 블록 레벨 스코프를 사용하지 않으므로 전역 스코프를 갖게 되므로, 변수 x는 전역변수이다.

함수 레벨 스코프(Function-level Scope)

자바스크립트는 함수 레벨 스코프를 사용하므로, 함수 내에서 선언된 매개변수 와 변수는 함수 외부에서 유효하지 않다.
따라서 변수 a는 전역변수, 변수 b는 지역 변수이다.

전역 변수 x와 지역변수 x가 중복 선언되었을때, 전역 영역에서는 전역변수만이 참조가능하고 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능하나 위 이미지와 같이 변수명이 중복된 경우, 지역변수를 우선하여 참조한다.

그렇다면 이번에는 함수 내에 함수가 존재하는 내부함수의 경우를 살펴보도록 하자.

example()함수 내부에 innerExample()이라는 함수를 만들고 함수내에 변수 x가 어떤 값을 의미하는지를 위 이미지를 통해서 알 수 있다.
내부함수는 자신을 포함하고 있는 외부함수의 변수에 접근할 수 있다.
innerExample()함수가 참조하는 변수 x는 example()함수에서 선언된 지역변수 x이다.
이는 실행 컨텍스트의 스코프 체인에 의해 참조 순위에서 전역변수 var x = 'global';가 뒤로 밀렸기 때문이다.

위 이미지에서는 함수(지역)영역에서 전역변수를 참조할 수 있으므로 전역변수의 값도 변경할 수 있음을 보여주고 있다.
전역변수는 물론 상위 함수에서 선언한 변수에 접근/변경이 가능하다.

위 이미지와 같이 중첩스코프는 가장 인접한 지역을 우선하여 참조한다.
innerExample()함수와 가장 인접한 지역인 example2()함수의 y를 참조하고 있다.

  • 중괄호({})를 기준으로 범위가 구분된다.
  • function을 제외한 if나 for문 등 중괄호({})안에 있는 범위를 블록 스코프라 한다.
  • function의 중괄호({}) 안에 있는 범위를 함수 스코프라고 한다.
  • var는 블록 스코프를 무시한다.

전역 스코프(Global Scope)

전역에 변수를 선언하면 이 변수는 어디서든지 참조할 수 있는 전역 스코프를 갖는 전역 변수가 된다.
var키워드로 선언한 전역변수는 전역 객체(Global Object) window의 프로퍼티이다.

위 이미지에서 변수 global는 함수 영역 밖의 전역에서 선언되었다.
자바스크립트는 타 언어와는 달리 특별한 시작점(Entry point)이 없어서 위 코드와 같이 전역에 변수나 함수를 선언하기 쉽다.
하지만 이로인해 전역에 변수를 선언하기 쉬워지게 되므로 전역 변수를 남발하는 문제를 야기시킨다.
따라서 전역 변수의 사용은 변수 이름이 중복될 수도 있고, 의도치 않은 재할당으로 인한 상태 변화로 코드를 예측하기 어렵게 만들게되므로 사용을 자제해야 한다.

  • 자바스크립트의 스코프는 변수에 접근할 수 있는 유효범위이다.
  • 전역 스코프에서 선언한 변수는 지역 스코프에서 사용가능하다.
  • 지역 스코프에서 선언한 변수는 전역 스코프에서 사용 불가능하다.
  • 스코프는 중첩이 가능하다.
  • 가장 바깥은 전역 스코프라 하고 나머지는 지역 스코프이다.
  • 지역변수가 전역변수보다 우선순위가 더 높다.

let, const, var의 차이

let : 재할당 가능, 재선언 불가, 유효범위-블록스코프 및 함수스코프
const : 재할당 불가, 재선언 불가, 유효범위-블록스코프 및 함수스코프
var : 재할당 가능, 재선언 불가, 유효범위-함수스코프

let, const, var의 가장 큰 차이점은 유효범위

함께 알아두면 좋은 스코프 체인과 실행 컨텍스트

스코프 체인(scope chain)이란 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 스코프가 어떻게 연결(chain)되고 있는지 보여주는 것을 말한다.

실행 컨텍스트(Execution context)는 우리가 작성한 코드가 실행되는 환경을 말하며, scope, hoisting, this, function, closure 등의 동작 원리를 담고 있는 자바스크립트의 핵심 원리를 말한다.
실행 컨텍스트에는 두개의 실행 컨텍스트가 존재하는데, 하나는 글로벌 실행 컨텍스트(Global Execution Context)이고, 다른 하나는 함수 실행 컨텍스트(Functional Execution Context)이다.

글로벌 실행 컨텍스트는 코드가 실행되기 전에 생성이 되며, 함수 내에 없는 코드는 모두 전역 실행 컨텍스트안에 존재한다.
그렇기 때문에, 자바스크립트 엔진은 일부 자바스크립트 코드를 실행할 때마다 글로벌 실행 컨텍스트를 작성한다.
글로벌 실행 컨텍스트의 특징으로는 무조건 하나의 전역 실행 컨텍스트만이 존재하며, 애플리케이션이 종료될 때(웹페이지에서 나가거나 브라우저를 닫을때)까지 유지하는 것이다.

함수 실행 컨텍스트는 전역 실행 컨텍스트가 생성된 후, 함수가 실행(ex 호출)될 때마다 새로운 실행 컨텍스트가 작성된다.

그렇다면 실행 컨텍스트에서 스코프 체인은 어떻게 작동할까?
실행 컨텍스트는 LIFO(Last in First Out)구조의 스택으로, 코드 실행 중에 생성된 모든 실행 컨텍스트를 저장하는데 사용된다.
실행 컨텍스트가 실행되면, 엔진이 스코프 체인을 통해 렉시컬 스코프를 먼저 파악한다.
그러고 나서 함수가 중첩 상태일때 하위 함수 내에서 상위 함수의 스코프와 전역 스코프까지 참조 할 수 있는데 이것을 스코프 체인을 통해 탐색하는 것이다.

정리하자면, 자기 자신의 스코프를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 스코프 체인이라 할 수 있다.

참고자료 : 넥스트리소프트-Scope 이해, poiemaweb-scope, 블로그-스코프체인