회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 최대 700만 원 지원받으세요
자바스크립트(Javascript)는 단순히 웹 브라우저에서 실행되는 스크립트 언어를 넘어, 웹 개발은 물론 백엔드, 모바일 앱, 그리고 데스크톱 애플리케이션까지 다양한 분야에서 널리 사용되는 언어입니다. 처음엔 HTML과 CSS와 함께 웹 페이지의 동적인 동작을 담당하는 클라이언트 사이드 언어로 사용되었지만, 현재는 Node.js와 같은 런타임 환경 덕분에 서버 사이드에서도 많이 사용되고 있습니다. 또한 React, Vue와 같은 프론트엔드 라이브러리, 프레임워크부터 Express, Nest와 같은 백엔드 프레임워크, 그리고 Electron, React Native를 활용한 크로스 플랫폼 애플리케이션 개발까지 가능해, 자바스크립트는 현대 개발 환경에서 빼놓을 수 없는 핵심적인 언어가 되었습니다.
회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!
확인
자바스크립트(Javascript)는 단순히 웹 브라우저에서 실행되는 스크립트 언어를 넘어, 웹 개발은 물론 백엔드, 모바일 앱, 그리고 데스크톱 애플리케이션까지 다양한 분야에서 널리 사용되는 언어입니다. 처음엔 HTML과 CSS와 함께 웹 페이지의 동적인 동작을 담당하는 클라이언트 사이드 언어로 사용되었지만, 현재는 Node.js와 같은 런타임 환경 덕분에 서버 사이드에서도 많이 사용되고 있습니다. 또한 React, Vue와 같은 프론트엔드 라이브러리, 프레임워크부터 Express, Nest와 같은 백엔드 프레임워크, 그리고 Electron, React Native를 활용한 크로스 플랫폼 애플리케이션 개발까지 가능해, 자바스크립트는 현대 개발 환경에서 빼놓을 수 없는 핵심적인 언어가 되었습니다.
그렇다면 자바스크립트가 이렇게 다양한 분야에서 활약할 수 있는 이유는 무엇일까요? 여러 이유가 있겠지만, 그중 하나는 바로 자바스크립트가 가진 독특한 실행 방식에 있습니다. 자바스크립트는 다른 언어들과 비교했을 때 ‘스코프(Scope)’와 ‘호이스팅(Hoisting)’과 같은 독특한 개념을 바탕으로 동작하며, 이 두 개념은 자바스크립트를 배우고 사용하기 위해 필수적으로 알아야 하는 개념 중 하나로, 이번 글에서 살펴보겠습니다.
먼저 스코프에 대해 알아볼까요? 스코프는 ‘범위’를 의미하며, 자바스크립트에서 생성된 변수나 함수가 어디에서 접근 가능하고, 유효한지를 결정하는 규칙을 말합니다. 쉽게 말해, 어떤 변수나 함수가 영향을 미칠 수 있는 범위를 스코프라고 부릅니다. 자바스크립트에는 여러 종류의 스코프가 존재합니다.
먼저 자바스크립트의 스코프는 크게 전역(Global)과 지역(Local)으로 나눌 수 있습니다. 전역 스코프는 프로그램 전체에서 접근 가능한 변수의 범위를 의미하며, 지역 스코프는 특정 함수나 블록 내부에서만 유효한 범위를 뜻합니다. 간단한 예제를 통해 이 두 스코프의 차이를 살펴볼게요.
let globalNum = 100;
function example() {
let localNum = 50;
console.log(globalNum); //100
console.log(localNum); //50
}
example();
console.log(globalNum); //100
console.log(localNum); //localNum is not defined
위 코드는 globalNum과 localNum 변수가 서로 다른 스코프를 가지고 있는 상황을 보여주는 코드인데요. 이제 두 변수가 어떤 유효 범위를 가지며, 코드의 실행 결과는 어떻게 나오는지 살펴보겠습니다. 먼저 코드의 가장 바깥쪽인 전역 공간에 선언된 globalNum 변수를 살펴봅시다. 전역 변수는 프로그램 전체에서 접근할 수 있는 특징을 가지고 있는데요. 이러한 전역 변수는 전역 스코프를 가집니다. 전역 스코프를 가진 변수는 어디에서든 접근이 가능하다는 특징이 있습니다.
코드에서 example 함수 내부의 console.log(globalNum)이 실행되었을 때, globalNum 변수는 함수 내부에 선언되지는 않았지만, 전역 스코프를 가진 변수이기 때문에 전역 공간에 선언된 globalNum 값인 100에 접근해 출력할 수 있습니다. 또한 함수 호출 이후, 함수 외부에서 console.log(globalNum)을 실행할 때도 전역 변수인 globalNum 변수에 접근 가능하므로 같은 값이 출력됩니다.
다음으로, example 함수 내부에 선언된 localNum 변수를 살펴보겠습니다. localNum 변수는 함수 내부에서만 유효한 지역 변수로, 전역 변수인 globalNum과는 다른 접근 범위를 가집니다. 지역 변수는 지역 스코프를 가지며, 해당 함수나 블록 내부에서만 유효합니다.
코드에서 console.log(localNum)가 example 내부에서 실행될 때, localNum 변수는 함수 내부에 선언된 변수이므로, 해당 변수의 값인 50이 정상적으로 출력되죠. 그렇다면, 함수 호출이 끝난 이후에 함수 외부에서 console.log(localNum)를 실행하면 어떻게 될까요?
localNum 변수는 지역 스코프를 갖는 지역 변수로, example 함수 내부에서만 유효한 접근 범위를 갖기 때문에, 함수 바깥에서는 존재하지 않는 변수로 간주됩니다. 따라서 접근이 불가능하며 오류가 발생하게 됩니다. 이처럼 지역 변수는 선언된 함수나 블록 내부에서만 유효한 범위를 갖고, 스코프를 벗어나면 더 이상 접근할 수 없습니다.
전역 변수와 지역 변수가 어떤 범위를 가지는지, 그 차이를 그림으로 나타내면 다음과 같습니다.
전역 스코프와 지역 스코프의 차이를 이해했으니, 이제 자바스크립트에서 중요한 또 다른 스코프인 함수 스코프(Function Scope)와 블록 스코프(Block Scope)를 알아볼게요.
함수 스코프와 블록 스코프는 변수 선언 방식에 따라 동작 방식이 달라지며, 자바스크립트를 사용하는 데 중요한 역할을 합니다. 함수 스코프는 함수 내부에서 선언된 변수나 함수가 해당 함수 내에서만 유효한 범위를 가지는 스코프를 말합니다. 자바스크립트의 var 키워드는 함수 스코프를 따르며, 함수 내부에 선언된 변수는 함수 외부에서 접근할 수 없습니다. 아래의 예시를 통해 살펴볼게요.
function example() {
var funcVar = "나는 함수 스코프 변수!";
console.log(funcVar); // "나는 함수 스코프 변수!"
}
example();
console.log(funcVar); // 오류: funcVar is not defined
funcVar 변수는 함수 내부에서 선언되어 있기 때문에, 함수 외부에서는 접근할 수 없습니다. 따라서 example 함수의 내부에서 funcVar 함수에 접근하려 한다면, 위의 코드와 같이 funcVar 변수가 정의되어 있지 않다는 오류가 발생합니다. 함수 스코프는 함수가 실행될 때 변수가 생성되고, 함수가 종료되면 변수가 소멸되는 구조를 가집니다.
함수 스코프와는 다르게, 블록 스코프는 {} 중괄호로 감싸진 블록 내부에서만 유효한 범위를 가집니다. let과 const 키워드는 블록 스코프를 따르며, 해당 블록을 벗어나면 변수에 접근할 수 없습니다. 마찬가지로 예제 코드를 통해 자세하게 확인해 보겠습니다.
if (true) {
let blockVar = "나는 블록 스코프 변수!";
console.log(blockVar); // "나는 블록 스코프 변수!"
}
console.log(blockVar); // 오류: blockVar is not defined
blockVar 변수는 let 키워드로 선언되어 있기 때문에 블록 스코프를 갖고, 블록 스코프는 중괄호로 감싸진 블록 내부에서만 유효하므로, 위의 코드에서는 if 문의 내부에서만 유효한 변수입니다. 그러므로 블록을 벗어난 위치에서 blockVar 변수에 접근하게 될 경우에는 위와 같은 오류가 발생합니다.
이처럼 블록 스코프는 변수의 유효 범위를 좀 더 명확하게 관리할 수 있도록 도와줍니다. 특히 블록 스코프를 활용하면 함수 스코프(var)의 의도치 않은 동작을 방지할 수 있어, 코드의 안정성과 가독성을 높일 수 있다는 장점이 있습니다. 그렇다면 자바스크립트는 어떻게 변수의 유효 범위를 결정할까요? 이제 자바스크립트의 스코프 결정 방법을 살펴보겠습니다.
자바스크립트에는 변수와 함수가 어디에서 유효한지를 결정하는 두 가지 방식이 있습니다. 바로 동적 스코프(Dynamic Scope)와 정적 스코프(Static Scope)입니다. 이 개념들은 코드가 작성된 위치와 실행되는 시점에 따라, 특정 변수에 접근할 수 있는지를 정의하는 중요한 개념인데요. 이를 통해 자바스크립트는 코드의 동작 방식을 결정하고, 변수 간의 혼란을 방지할 수 있습니다.
이제 동적 스코프와 정적 스코프가 각각 어떤 특징을 가지는지, 그리고 자바스크립트가 이 중 어떤 방식을 따르는지 볼까요?
동적 스코프는 함수나 변수가 호출된 위치를 기준으로 변수의 유효 범위를 결정하는 방식입니다. 실행 시점에 활성화된 환경에 따라 변수의 값을 찾기 때문에, 코드의 작성 시점과 실행 결과가 다를 수 있습니다. 예제 코드를 통해 살펴보겠습니다.
let globalVar = "나는 전역 변수";
function outer() {
let outerVar = "나는 외부 변수";
inner();
}
function inner() {
console.log(globalVar); // "나는 전역 변수"
console.log(outerVar); // 오류 발생
}
outer();
자바스크립트가 동적 스코프를 선택하는 경우, inner 함수가 outer 함수에서 호출되었기 때문에 outerVar 변수에 접근할 수 있습니다. 그러나 실제로 코드를 실행하면 inner 함수는 outerVar 변수에 접근할 수 없기 때문에 오류가 발생합니다. 그 이유는 자바스크립트가 동적 스코프를 사용하지 않기 때문이죠.
동적 스코프는 변수의 유효 범위가, 실행되는 시점에 따라 동적으로 결정된다는 특징이 있습니다. 실행 중인 환경에 따라 변수의 유효 범위가 달라지기 때문에, 호출되는 위치에 따라 변수의 값을 찾는 방식이 달라질 수 있습니다. 이로 인해 코드의 결과를 예측하기 어려운 경우가 많고, 동적 스코프는 이런 불확실성 때문에 유지보수와 디버깅이 어렵습니다. 그래서 현대 프로그래밍 언어에서는 거의 사용되지 않는 방식입니다.
대신 자바스크립트를 포함한 대부분의 언어는 예측 가능성과 안정성을 위해 정적 스코프를 채택하고 있는데요. 정적 스코프는 코드가 작성된 위치를 기준으로, 즉 변수가 선언된 시점을 기준으로 변수의 유효 범위를 결정하는 방식입니다. 실행 시점과는 관계없이, 변수와 함수가 선언된 위치를 기준으로 코드가 동작하며, 정적 스코프는 실행 환경에 상관없이 코드의 동작을 예측 가능하게 만든다는 특징이 있습니다.
정적 스코프는 ‘렉시컬 스코프’라고 불리기도 합니다. 그리고 자바스크립트는 바로 이 렉시컬 스코프를 따르는 언어입니다. 그러므로 함수의 호출 위치가 아닌, 선언된 위치를 기준으로 스코프가 고정됩니다. 이를 통해 변수와 함수의 접근 가능 범위가 명확하게 정의되며, 코드는 더 직관적이고 예측이 가능해집니다.
let globalVar = "나는 전역 변수";
function outer() {
let outerVar = "나는 외부 변수";
function inner() {
console.log(globalVar); // "나는 전역 변수"
console.log(outerVar); // "나는 외부 변수"
}
inner();
}
outer();
위의 코드에서 inner 함수는 outer 함수 내부에 선언되어 있으므로, inne 함수는 선언된 위치를 기준으로 outerVar 변수와 globalVar 변수에 접근할 수 있습니다. 호출 위치와는 상관없이, 작성된 위치를 기준으로 변수가 결정되는 것이 렉시컬 스코프의 핵심이라고 할 수 있습니다.
이처럼 렉시컬 스코프와 함께 자바스크립트의 동작 방식을 이해하려면, ‘호이스팅(Hoisting)’이라는 개념도 필수적으로 알고 있어야 하는데요. 호이스팅은 변수와 함수의 선언문을 코드의 최상단으로 끌어올리는 자바스크립트의 동작 방식을 뜻합니다. 그리고 호이스팅을 사용하면 코드의 실행 순서와 작성 순서를 다르게 변경할 수도 있습니다.
자바스크립트에서는 호이스팅을 사용하면, 다음과 같이 함수가 선언되기 전에 함수를 호출할 수 있습니다.
sayHello();
function sayHello() {
console.log("hello world!");
}
코드를 보면, sayHello 함수가 선언되기 이전에 호출되었지만 실행 결과, 코드가 정상적으로 실행됩니다. 이는 자바스크립트의 엔진이 실행 시점에 함수 선언을 코드의 최상단으로 끌어올리기 때문입니다. 하지만, 함수 표현식은 호이스팅되지 않아 아래와 같은 코드는 오류를 발생시키므로 주의해서 사용해야 합니다.
sayHello(); // TypeError: sayHello is not a function
const sayHello = function () {
console.log("hello world!");
};
변수 선언 또한 호이스팅의 영향을 받습니다. var 키워드로 선언된 변수는 선언문만 끌어올려지고, 초기화는 호이스팅 되지 않기 때문에 다음과 같이 선언 전에 변수에 접근하면 undefined가 출력됩니다.
console.log(num); // undefined
var num = 10;
위의 코드를 자바스크립트 엔진이 해석하는 순서로 나타내보면, 다음과 같이 나타낼 수 있습니다.
var num; // 선언문이 끌어올려짐
console.log(num); //undefined
num = 10; //초기화는 나중에 이뤄짐
위 코드와 같이, var num; 이라는 선언문은 코드의 최상단으로 끌어올려졌지만, num = 10; 초기화 문은 끌어올려지지 않았기 때문에 undefined가 출력됩니다. 이러한 동작은 예기치 않은 결과를 유발할 수 있으므로, 변수를 선언하기 전에 사용하는 습관은 지양하는 것이 좋습니다.
var 키워드뿐만 아니라, 또 다른 변수 선언 키워드인 let과 const 키워드도 호이스팅이 가능합니다. 하지만 var 키워드와는 다르게 초기화 이전에 접근하려고 하면 오류를 발생시키는데요. 그 이유는 바로 TDZ라는 공간 때문입니다.
TDZ는 Temporal Dead Zone, 즉 ‘일시적인 죽음의 공간’을 뜻하며, 변수를 사용할 수 없는 상태를 의미합니다. let과 const로 선언된 변수는 스코프의 시작 지점부터 초기화가 완료될 때까지 TDZ에 머물게 됩니다. 이 때문에 초기화 전에 변수를 참조하려고 하면 오류가 발생합니다.
하지만 오류가 발생한다고 해서 호이스팅이 일어나지 않는 것은 아닙니다. 선언 자체는 여전히 호이스팅되고, 초기화가 이루어지기 전까지는 TDZ에 의해 보호되며 참조가 차단됩니다. 그리고 이러한 특징은 자바스크립트가 코드의 안전성을 보장하기 위한 장치라고 할 수 있습니다.
console.log(num); // ReferenceError: Cannot access ‘num’ before initialization
let num = 10;
실제로 위의 코드처럼 num 변수의 선언문 위에서 num 변수에 접근하면 위와 같은 오류가 발생합니다. 그리고 이러한 현상은 TDZ 때문이며, TDZ는 변수가 선언되기 전에는 접근할 수 없도록 막아주는 역할을 합니다.
지금까지 자바스크립트의 스코프와 호이스팅에 대해 알아보았습니다. 스코프는 변수와 함수의 유효 범위를 결정하며, 자바스크립트는 렉시컬 스코프를 사용해 코드 작성 시점에 변수의 선언 위치에 따라 변수가 유효한 범위를 고정합니다. 이를 통해 자바스크립트는 코드를 더 직관적이고, 예측 가능하게 만들 수 있습니다.
또한 호이스팅은 변수와 함수 선언을 코드의 최상단으로 끌어올리는 듯한 자바스크립트의 독특한 동작 방식으로, 유연한 실행 환경을 제공합니다. 그러나 이러한 유연성이 때로는 의도하지 않은 오류를 발생시킬 수 있는데요. 이를 방지하기 위해 let과 const를 사용하여 블록 스코프와 TDZ를 활용하는 것이 더 안전합니다. 스코프와 호이스팅은 자바스크립트를 더 깊이 이해하기 위한 첫걸음이라고 할 수 있습니다. 이러한 개념들을 이해하면, 코드 최적화나 유지보수 측면에서도 더욱 명확하고 쉬운 코드를 작성할 수 있죠. 이제 이 개념들을 활용해 더 효율적이고 안정적인 코드를 작성해 보시길 바랍니다.
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.