티스토리 뷰

반응형

기존에는 자바스크립트에서 var로 변수를 선언했지만 ES6부터 let과 const가 도입되었고, 필요에 맞게 변수를 정의·선언하는 것이 더 용이해졌다. 자바스크립트에서 var, let, const로 변수를 선언할 때, 각각의 키워드는 변수의 범위(scope)와 할당 가능성(mutability)에 대해 다른 동작을 한다. 이 점을 기억해두면서 var, let, const는 각각 무엇이 다른지 알아보자.


var

var는 ES5까지 주로 사용된 변수를 선언하는 키워드이다. var는 오래된 자바스크립트 코드와의 호환성을 위해서 사용하는 것이 권장되어질 정도로 오래된 방식이다. 오래되었기 때문에 여러가지 문제점들이 발견되었으며, 이 문제를 해결하기 위해 나온 것이 let과 const라고 할 수 있겠다.

 

 

1) 함수 스코프(function scope)

var로 선언된 변수는 함수 스코프(function scope)를 가지며, 함수 내에서 선언된 var 변수는 함수 전체에서 접근할 수 있다. 이 말은 즉, 함수 내에서 선언된 변수는 함수 내에서만 유효하며, 함수 외부에서는 접근할 수 없다는 것이다.

function example() {
  var x = 1; // 함수 스코프 내에서 선언된 변수 x

  if (true) {
    var y = 2; // 함수 스코프 내에서 선언된 변수 y
    console.log(x); // 1 (함수 내부에서 변수 x에 접근 가능)
    console.log(y); // 2 (함수 내부에서 변수 y에 접근 가능)
  }

  console.log(x); // 1 (함수 내부에서 변수 x에 접근 가능)
  console.log(y); // 2 (함수 스코프 내에서 변수 y에 접근 가능)

}

example();

console.log(x); // ReferenceError: x is not defined (함수 외부에서 변수 x에 접근 불가능)
console.log(y); // ReferenceError: y is not defined (함수 외부에서 변수 y에 접근 불가능)

위 코드에서 'var'로 선언된 변수 'x'와 'y'는 'example()' 함수 내에서 선언되었다. 따라서 'if'문 내에서도 접근 가능하며, 함수 'example()' 내의 어떤 위치에서든 접근 가능하다. 하지만 함수 외부에서 'console.log(x)'나 'console.log(y)'를 호출하면 'ReferenceError'가 발생한다. 외부에서는 함수 스코프 내에서 선언된 변수에 접근할 수 없기 때문이다.

 

반면 for 루프(블록 스코프) 내에서 var 키워드로 변수를 선언하면, 이 변수를 for 루프 밖에서도 사용할 수 있다.

for (var i = 0; i < 10; i++) {
	var aa = "I am an AA outside of the loop";
}

console.log(aa);
// I am an AA outside of the loop

function myFunc(){
	var bb = "I am an BB inside this function";
	console.log(bb);
}
myFunc();
// I am an BB inside this function
console.log(bb);
// ReferenceError : bb is not defined

첫번째 출력에서는 aa의 값이 블록 스코프(for문은 블록 스코프를 가짐)를 벗어나도 for 루프 외부에서 접근할 수 있다.

그리고 myFunc()을 호출할 때는 myFunc 함수 내부에 출력문이 있는데, 이때 bb의 출력문은 myFunc함수 내부에 있으므로 정상적으로 출력이 가능한 것을 확인할 수 있다.

마지막줄의 console.log(bb) 출력에서는 bb가 함수 스코프 내에 제한되어 있기 때문에 함수 외부에서 접근할 수 없어 에러가 발생한다.

 

여기서 중요한 점은 var가 외부에서 선언 될 때의 범위는 전역이며, 함수 블록 외부에서 var 사용시 선언된 모든 변수를 전체 윈도우 상에서 사용 가능하다.

 

 

2) 호이스팅(Hoisting)

호이스팅이란 변수와 함수 선언이 맨 위로 이동되는 자바스크립트 매커니즘이다. var는 변수가 선언되기 전에도 사용할 수 있으며, 호이스팅 동작을 수행한다. 이로 인해 변수를 선언하기 전에 사용할 수도 있고, 변수가 선언된 위치와 상관없이 동작할 수도 있다.

만약 아래와 같이 코드를 작성했다고 치자.

console.log(x); // undefined
var x = 10;

변수 선언 전에 출력하므로 console.log 출력에서는 에러가 뜰거라 생각하지만, var는 선언되기 이전에 미리 스코프에서 올려져 있으므로 에러가 뜨지 않는다. 대신 아직 변수가 선언되기 전이므로 값은 초기화되지 않았기에 undefined가 뜬다. var는 변수가 선언된 위치와 상관없이 동작하기에 코드 가독성과 예측성을 저하시킬 수 있으므로, var 사용을 최소화하는 것을 권장한다.

 

 

3) 재선언과 재할당

var로 선언된 변수는 재선언과 재할당이 가능하다.

var name = "Harry";
var name = "Kane"; // 재선언 가능

var age = 28;
age = 20; // 재할당 가능

하지만 이 기능으로 취약점이 발생할 수 있다.

var name = "Harry";
var age = 28;

if (age < 30){
    var name = "Kane"; 
}
console.log(name) // Kane

위 코드에서 조건문이 true를 반환하여 name 변수가 재선언되는데, 철저히 계산하여 코딩한 경우라면 문제 없겠지만 var로 재선언 코드를 늘리다보면 생각한 것과 다른 결과물이 출력될 수 있다. 위 코드는 간단한 예제 코드여서 실감이 안 나겠지만, 코드가 복잡해지고 양이 많아질 수록 재선언문 한 줄이 큰 버그를 일으킬 수 있다. 이렇게 동일한 이름의 변수를 재선언하거나 중복 선언하게 되면 문법 에러도 발생할 수 있어 개발자의 실수가 쉽게 노출될 것이다. 


let

let은 var의 취약점을 보안해서 ES6 이후에 나온 키워드이다. 무엇이 개선되었는지 확인해보자.

 

 

1) 블록 스코프(Block Scope)

let으로 선언된 변수는 블록 스코프를 가진다. 즉, 변수가 선언된 블록(중괄호로 둘러싸인 영역, {} )과 그 하위 블록 내에서만 사용할 수 있다. 이때, 하나의 블록은 중괄호 속에서 존재하며 중괄호 안에 있는 것은 모두 블록 범위에 해당된다. 

// let 사용의 예
let x = "global";

if (x === "global"){
	let x = "block-scoped";

	console.log(x); // block-scoped
}
console.log(x); // global
// var 사용의 예
let y = "global";

if (y === "global"){
	var y = "block-scoped";

	console.log(y); // block-scoped
}
console.log(y); // block-scoped

위 코드를 보면 let과 var가 다르게 동작하는 것을 볼 수 있다. 블록 스코프 내에서 let으로 선언한 변수 x에 새 값을 할당했을 때 블록 바깥에서는 그 값이 변경되어 출력되지 않았다. 반면에, var로 선언된 변수 y는 블록 스코프 외부에서 접근이 가능하므로 블록 바깥에서도 값이 변경되는 것을 볼 수 있다.

 

 

2) 호이스팅(Hoisting)

let은 호이스팅이 발생되지만, 변수가 선언되기 전에는 사용할 수 없으며 변수 선언 전에 접근하면 'ReferenceError'가 발생한다. 아래의 코드에서 undefined로 초기화되는 var와 다르게 let의 키워드는 초기화 되지 않는다.

console.log(a); // ReferenceError: a is not defined
console.log(b); // b is undefined

let a = 1;
var b = 2;

 

 

3) 재선언과 재할당

let으로 선언된 변수는 재할당이 가능하지만, 재선언은 허용되지 않는다. 아래 코드를 보면 재선언을 하면 에러가 발생된다.

// 재할당
let footballPlayer = "Harry";
footballPlayer = "Harry Kane";

// 재선언
let baseballPlayer = "Ohtani";
let baseballPlayer = "Ohtani Shohei"; // error: Identifier 'baseballPlayer' has already been declared

그러나 아래 코드처럼 같은 변수가 다른 범위 내에서 재선언한 경우에 에러는 발생하지 않는다. 

// let 사용의 예
let x = "global";

if (x === "global"){
	let x = "block-scoped";

	console.log(x); // block-scoped
}
console.log(x); // global

첫번째 변수 x는 글로벌 영역(전역변수)에서 선언되었고, if문 안에 있는 변수 x는 블록 스코프에서 선언되었기에 서로 다른 범위를 가지므로 서로 다른 변수 취급을 받는다. 두번째 변수 x는 if문 안에서만 존재하기에 같은 이름의 첫번째 변수 x에 대해서 신경쓰지 않아도 된다. var와 달리 재선언으로 인한 취약점이 발생하지 않는다.


const

let과 마찬가지로 const도 var의 취약점을 보안해서 ES6 이후에 나왔다. const는 일정한 상수 값을 선언하는 데 사용되는 키워드이다.

 

 

1) 블록 스코프(Block Scope)

let과 같이 const로 선언된 변수도 블록 스코프( {} )를 가지므로 블록 외부에서는 접근할 수 없다.

function example() {
  if (true) {
    var x = 10; // 함수 스코프를 가지는 변수
    let y = 20; // 블록 스코프를 가지는 변수
    const z = 30; // 블록 스코프를 가지는 상수

    console.log(x); // 10
    console.log(y); // 20
    console.log(z); // 30
  }

  console.log(x); // 10
  console.log(y); // ReferenceError: y is not defined
  console.log(z); // ReferenceError: z is not defined
}

example();

위 코드에서 var x는 함수 스코프를 가지기 때문에 if  블록 외부에서도 접근이 가능하다. 그러나 let y와 const z는 블록 스코프를 가지기 때문에 if 블록 외부에서는 접근할 수 없어 에러가 발생한다.

 

 

2) 호이스팅(Hoisting)

const도 var와 let와 같이 호이스팅의 대상이 되지만 초기화 되지는 않는다. 변수 선언 전에 const 변수를 호출한다면 에러가 발생한다.

console.log(x); // undefined
console.log(y); // ReferenceError: y is not defined
console.log(z); // ReferenceError: z is not defined

var x = 10;
let y = 20;
const z = 30;

var x는 호이스팅으로 인해 undefined로 출력되지만, let y와 const z는 ReferenceError가 발생한다. 따라서 let과 const를 사용하면 변수를 선언하기 전에 사용하는 실수를 방지할 수 있다.  let과 const를 사용하면 변수의 스코프와 재할당 가능 여부를 더 명확하게 제어할 수 있으며, 코드의 가독성과 예측성을 향상시킬 수 있다.

 

 

3) 재선언과 재할당

const 변수는 재선언과 재할당이 불가능하며, 이는 변수의 값이 한 번 할당되면 그 값을 변경할 수 없음을 의미한다. 한 번 const로 선언하면 변수의 값이 해당 범위 내에서 동일하게 유지되기에, const로 선언된 변수는 선언과 동시에 초기화되어야 한다.

var x = 10;
let y = 20;
const z = 30;

x = 50; // 재할당 가능
y = 60; // 재할당 가능
z = 70; // TypeError: Assignment to constant variable.

 

그러나 const로 선언된 변수가 불변이라는 의미는 아니다. const로 선언된 객체나 배열의 경우, 객체 또는 배열 자체는 변경할 수 없지만, 객체 또는 배열 내부의 속성이나 요소는 수정할 수 있다.

const person = {
	name : 'Harry Kane',
	age : 28,
};

person.age = 20;
console.log(person.age);// 20

위 코드의 경우에는 변수 전체를 재할당하는 것이 아니라 그 속성 중 하나만 재할당하는 것이므로 문제가 없다. 하나 추가하자면, 객체의 내용을 변경할 수 없게 아래의 코드처럼 const 객체를 고정할 수는 있다. 하지만 객체의 값을 변경하려고 시도할 때 자바스크립트가 오류를 던지지는 않는다.

const person = {
	name : 'Harry Kane',
	age : 28,
};

person.age = 20;
console.log(person.age); // 20

Object.freeze(person); // 값 고정

person.age = 30;

console.log(person.age); // 20

var, let, const를 적재적소에 쓰는 법 추천

변수를 어떻게 선언할 것인지는 규칙은 따로 없으며 개발자 사이에서도 의견이 갈린다. 많은 의견 중 2가지의 방법을 추천한다.

 

첫번째 방법

① 기본적으로 const를 사용하자.

② 재할당이 필요한 경우에만 let을 사용하자.

③ var는 ES6에서 절대 사용하지 않는다.

 

두번째 방법

① 여러 큰 스코프에서 공유하기 위한 최상위 변수에는 var를 사용한다.

② 작은 스코프의 로컬 변수에는 let을 사용한다.

③ 코드 작성이 어느 정도 진행된 경우에만 let을 const로 리팩토링한다. 이때, 변수 재할당을 막아야하는 경우라는 것이 확실해야 한다.

 

어느 방법을 사용할 것인지는 개발자의 취향에 따라 다를 수 있다. 하지만 분명한 것은 var보다는 let과 const를 사용하는 것을 권장한다. 

반응형
댓글
공지사항