icon
8장 : 최신 ECMAScript 기능

let, const와 블록 스코프

우리는 7장 "자바스크립트 심화 II"를 통해 자바스크립트의 깊은 내부 동작 원리부터 고급 비동기 처리, 모듈 시스템, 디자인 패턴에 이르기까지 폭넓은 지식을 쌓았습니다. 이제 여러분은 기본적인 웹 애플리케이션을 개발할 수 있는 역량을 갖추었을 것입니다. 하지만 자바스크립트는 ECMAScript라는 표준을 통해 매년 새로운 기능들이 추가되며 발전하고 있습니다.

특히 2015년에 발표된 ES2015 (ES6) 는 자바스크립트에 엄청난 변화를 가져온 버전입니다. 단순히 문법적 설탕을 넘어, 언어의 핵심적인 동작 방식과 개발자 경험을 크게 개선하는 많은 기능들이 추가되었습니다. 이 외에도 매년 ECMAScript는 새로운 기능들을 도입하며 더욱 강력하고 유연한 언어로 진화하고 있습니다.

8장 "최신 ECMAScript 기능"에서는 현대 자바스크립트 개발에서 필수적으로 사용되는 ES2015 이후의 주요 문법과 개념들을 집중적으로 다룹니다. 이 기능들을 익히는 것은 여러분의 코드를 더욱 간결하고 효율적이며, 가독성 높게 만드는 데 큰 도움이 될 것입니다.

오랜 기간 자바스크립트에서 변수를 선언하는 유일한 방법은 var 키워드였습니다. var는 유연했지만, 동시에 많은 혼란과 잠재적인 버그를 유발하는 몇 가지 특성을 가지고 있었습니다. ES2015(ES6)에서 도입된 letconst 키워드는 이러한 var의 문제점을 해결하고, 자바스크립트의 변수 선언 방식을 더욱 예측 가능하고 안전하게 만들었습니다. 이와 함께 중요한 개념인 블록 스코프(Block Scope) 가 등장했습니다.

이번 장에서는 var의 문제점을 되짚어보고, letconst의 특징, 그리고 자바스크립트의 스코프 개념을 깊이 있게 이해하는 시간을 가지겠습니다.


var 키워드의 문제점 (복습 및 심화)

var는 함수 스코프를 따르며, 몇 가지 독특한 동작 때문에 개발자에게 혼란을 주거나 의도치 않은 버그를 발생시키기 쉬웠습니다.

  1. 함수 스코프 (Function Scope): var로 선언된 변수는 오직 함수 내부에서만 지역 변수로 취급되며, if문이나 for문과 같은 블록 내부에서는 지역 변수로 취급되지 않고 해당 함수의 스코프(또는 전역 스코프)에 속하게 됩니다.

    if (true) {
        var x = 10;
        console.log(x); // 결과: 10
    }
    console.log(x); // 결과: 10 (if 블록 밖에서도 접근 가능)
    
    function introduce() {
        var name = "김철수";
        console.log(name); // 결과: 김철수
    }
    introduce();
    // console.log(name); // ReferenceError: name is not defined (함수 스코프)

    이는 다른 언어에서 블록 스코프가 일반적인 것과 달라 혼란을 야기했습니다.

  2. 변수 호이스팅 (Variable Hoisting): var로 선언된 변수는 선언부가 스코프의 최상단으로 끌어올려진 것처럼 동작합니다. 할당은 원래 위치에서 이루어지지만, 선언만 먼저 처리됩니다. 이 때문에 변수를 선언하기 전에 참조해도 오류가 발생하지 않고 undefined가 출력됩니다.

    console.log(myVar); // 결과: undefined
    var myVar = "hello";
    console.log(myVar); // 결과: hello
    // 실제로는 다음과 같이 동작합니다:
    // var myVar;
    // console.log(myVar);
    // myVar = "hello";
    // console.log(myVar);

    이는 코드를 예측하기 어렵게 만들고 잠재적인 버그의 원인이 됩니다.

  3. 변수 재선언 허용: var는 동일한 이름의 변수를 여러 번 재선언해도 오류를 발생시키지 않습니다.

    var myName = "Alice";
    console.log(myName); // 결과: Alice
    
    var myName = "Bob"; // 동일한 이름으로 재선언해도 에러 없음
    console.log(myName); // 결과: Bob

    이는 실수로 기존 변수를 덮어쓸 위험이 있어 대규모 프로젝트에서 버그를 유발할 수 있습니다.

이러한 var의 문제점들은 자바스크립트 개발을 어렵게 만드는 주요 원인이었습니다.


let 키워드: 새로운 변수 선언의 시작

let 키워드는 var의 문제점을 해결하기 위해 도입된 변수 선언 방식입니다.

블록 스코프 (Block Scope)

let으로 선언된 변수는 선언된 블록({}) 안에서만 유효합니다. 블록은 함수, if문, for문, while문 등의 중괄호로 둘러싸인 영역을 의미합니다.

let y = 100; // 전역 스코프 또는 상위 블록 스코프

if (true) {
    let z = 20; // if 블록 내부에서만 유효
    console.log(z); // 결과: 20
    console.log(y); // 결과: 100 (상위 스코프 변수는 접근 가능)
}
// console.log(z); // ReferenceError: z is not defined (블록 밖에서 접근 불가)

for (let i = 0; i < 3; i++) {
    console.log(i); // 결과: 0, 1, 2
}
// console.log(i); // ReferenceError: i is not defined (for 블록 밖에서 접근 불가)

블록 스코프는 코드를 더 예측 가능하게 만들고, 변수 이름 충돌의 위험을 줄여줍니다.

변수 재선언 불가능

let은 동일한 스코프 내에서 같은 이름의 변수를 재선언하는 것을 허용하지 않습니다.

let fruit = "사과";
// let fruit = "바나나"; // SyntaxError: 'fruit' has already been declared

이는 개발자의 실수를 방지하고 코드의 견고성을 높여줍니다.

변수 호이스팅과 TDZ

let 변수도 호이스팅되지만, var와 다르게 초기화되기 전까지는 접근할 수 없습니다. 이 영역을 TDZ (Temporal Dead Zone, 시간상 사각 지대) 라고 부릅니다. TDZ에 있는 변수에 접근하려고 하면 ReferenceError가 발생합니다.

console.log(myLetVar); // ReferenceError: Cannot access 'myLetVar' before initialization
let myLetVar = "hello";
console.log(myLetVar); // 결과: hello

TDZ는 개발자가 변수를 선언하기 전에 사용하는 실수를 방지하여 코드의 안정성을 높입니다.


키워드: 상수 선언

const 키워드는 let과 마찬가지로 블록 스코프를 따르며, 변수 호이스팅과 TDZ의 특성도 공유합니다. 하지만 가장 중요한 차이점은 상수(constant)를 선언할 때 사용된다는 것입니다. 즉, 한 번 할당된 값을 변경할 수 없습니다.

재할당 불가능

const로 선언된 변수는 선언과 동시에 값을 할당해야 하며, 이후에는 재할당이 불가능합니다.

const PI = 3.14159;
console.log(PI); // 결과: 3.14159

// PI = 3.14; // TypeError: Assignment to constant variable. (재할당 불가능)

// const TAX_RATE; // SyntaxError: Missing initializer in const declaration (선언과 동시에 할당 필수)

객체/배열과 const

const가 재할당을 금지한다는 것은 변수가 가리키는 메모리 주소(참조)를 변경할 수 없다는 의미입니다. 객체나 배열과 같은 참조 타입의 경우, const로 선언해도 객체/배열 내부의 프로퍼티나 요소는 변경할 수 있습니다.

const user = {
    name: "Jane",
    age: 25
};

user.age = 26; // 객체 내부의 프로퍼티 변경은 가능
console.log(user.age); // 결과: 26

// user = { name: "Mike", age: 30 }; // 하지만 객체 자체를 재할당하는 것은 불가능
// TypeError: Assignment to constant variable.

const colors = ["red", "green"];
colors.push("blue"); // 배열에 요소 추가는 가능
console.log(colors); // 결과: ["red", "green", "blue"]

// colors = ["yellow"]; // 배열 자체를 재할당하는 것은 불가능
// TypeError: Assignment to constant variable.

객체나 배열 내부까지도 변경 불가능하게 만들려면, 깊은 복사(deep copy)를 사용하거나 Object.freeze()와 같은 메서드를 활용해야 합니다.


var vs let vs const

현대 자바스크립트 개발에서는 다음과 같은 규칙을 따르는 것이 일반적입니다.

  • 기본적으로 const를 사용합니다.
    • 변수의 값이 변경될 일이 없는 상수나, 참조할 객체/배열이 재할당될 일이 없는 경우에 사용합니다.
    • 이는 코드의 의도를 명확히 하고, 잠재적인 버그를 줄이는 데 큰 도움이 됩니다.
  • 재할당이 필요한 경우에만 let을 사용합니다.
    • 반복문 내의 카운터 변수, 조건에 따라 값이 변경될 수 있는 변수 등에 사용합니다.
  • var는 더 이상 사용하지 않는 것을 권장합니다.
    • 레거시 코드(오래된 코드)를 유지보수하는 경우가 아니라면 var를 새로운 코드에서 사용하는 것은 피하는 것이 좋습니다.

모범 사례

// 전역 설정값, 절대 변하지 않는 값
const API_URL = "https://api.example.com";
const MAX_ITEMS_PER_PAGE = 20;

// 한 번 할당되면 변하지 않는 참조형 값 (내부 변경은 가능)
const config = {
    theme: "dark",
    version: "1.0.0"
};
config.theme = "light"; // 가능

let counter = 0; // 값이 증가해야 하므로 let
for (let i = 0; i < 5; i++) { // 반복문 카운터는 let
    counter += i;
}

if (someCondition) {
    let result = "Success"; // if 블록 내에서만 필요한 변수는 let
    console.log(result);
}
// console.log(result); // 에러

마무리하며

이번 장에서는 자바스크립트에서 변수를 선언하는 새로운 표준인 letconst 키워드, 그리고 그와 함께 도입된 블록 스코프(Block Scope) 개념에 대해 심도 있게 학습했습니다.

여러분은 var 키워드가 가졌던 함수 스코프, 변수 호이스팅, 재선언 허용 등의 문제점들을 이해했으며, letconst가 어떻게 블록 스코프, 재선언 불가능, 그리고 TDZ(Temporal Dead Zone)를 통해 이러한 문제점들을 해결하고 코드의 예측 가능성과 안정성을 높이는지 알아보았습니다. 또한, const가 참조형 값의 내부 변경을 허용한다는 중요한 특징도 파악했습니다.

현대 자바스크립트 개발에서는 var 대신 letconst를 사용하는 것이 강력히 권장됩니다. const를 기본으로 사용하고, 재할당이 필요한 경우에만 let을 사용하는 습관을 들이는 것이 좋습니다. 이 새로운 변수 선언 방식은 여러분의 자바스크립트 코드를 더욱 견고하고 가독성 높게 만들어 줄 것입니다.