[ES6] var, let 그리고 const

지금까지 Javscript 에서 변수를 선언하는 방법은 var 를 이용하는 것 뿐이었습니다. 물론 Javascript 특성상 var 없이 변수를 선언하는 것도 가능하지만 부작용이 심각하기 때문에 실무에서는 적용하지 않는 것이 보통입니다. 일이 아니더라도 var 선언 없이 변수를 사용하는 것은 정신건강에 좋지 않기 때문에 절대로 사용하지 말 것을 강력하게 권장합니다.

본론으로 넘어가서, ES6로 넘어오면서 let과 const라는 새로운 선언방법이 생겼습니다. 새롭게 탄생한 let와 const는 도대체 어떤 녀석이며, 어떤 특징이 있는지 한번 짚어보고자 합니다.

1. 이미 선언 하셨는데요?

var 는 상당히 너그러운 친구입니다.

var foo = 'bar1';
var foo = 'bar2';

같은 이름의 변수를 두번 선언하고 있습니다. foo라는 변수에는 ‘bar1′ 이라는 문자열이 할당되었다가 다음 선언에서 ‘bar2’라는 변수가 할당됩니다. 별다른 에러도 발생시키지 않습니다.

하지만 let과 const는 엄격합니다.

let foo = 'bar1';
let foo = 'bar2';

// ERROR: Uncaught SyntaxError: Identifier 'foo' has already been declared

앞서 선언한 변수를 다시 선언하게 되면 칼 같은 오류를 발생시킵니다. 규모가 큰 코드에서 버그를 방지 할 수 있는 매우 바람직한 특징입니다.

2. 난 그때 거기 없었어요

뜬금 없이 변수 하나를 호출해 보겠습니다.

console.log(foo);
// Error: Uncaught ReferenceError: foo is not defined

어디에도 존재하지 않는 변수인 foo를 호출하면 당연히 에러가 발생합니다. 하지만 var를 이용해 아래와 같이 변수를 선언하면 조금 재미있는 현상이 발생합니다.

console.log(foo); // undefined

var foo;

더 이상 에러가 발생하지 않습니다. 변수 foo는 값이 정의되지 않는 형(type)인 undefined가 되어 있을 뿐입니다. 선언보다 호출이 먼저 있었음에도 불구하고 이 코드는 정상적으로 작동합니다. 왜 이런 현상이 발생하는지는 Hoisting(호이스팅) 이라는 키워드로 검색해 보시길 바랍니다.

그럼 let과 const는 어떨까요?

console.log(foo);
// Error: Uncaught ReferenceError: foo is not defined

let foo;

호출한 시점에서 변수가 선언되어 있지 않음을 알리는 에러가 발생합니다. 일시적 사각지대(Temporal Dead Zone; TDZ) 라는 개념인데, 개인적인으로는 특정 개념을 정의해서 설명하지 않더라도 let과 const의 동작방식이 직관적이고 자연스럽다고 생각합니다.

3. 이 블록의 변수는 나야!

변수의 유효 범위에 대한 부분입니다. var의 경우에는 Function-scope 라고 합니다. 유효 범위가 함수 단위라는 이야기죠.

var foo = 'bar1';
cosole.log(foo); // bar1

if (true) {
  var foo = 'bar2';
  console.log(foo); // bar2
}

console.log(foo); // bar2

위 코드가 하나의 함수 구문 안에 존재한다고 가정했을 때 if문 밖의 변수 foo와 if문 안의 변수 foo는 동일한 변수가 됩니다. 중복 선언을 했지만 앞서 말한바와 같이 별다른 에러를 발생시키지 않고, 값마저 ‘bar2’로 변경해 버렸습니다.

하지만 let과 const Block-scope 라고 합니다. 유효 범위가 블록, 즉 {}로 감싸지 범위라는 이야기 입니다.

let foo = 'bar1';
console.log(foo); // bar1

if (true) {
  let foo = 'bar2';
  console.log(foo) // bar2
}

console.log(foo); // bar1

위 코드에서는 var를 사용한 경우와는 달리 if문 밖의 foo와 if문 안의 foo는 서로 다른 변수 입니다. 따라서 중복 선언으로 인한 에러도 발생하지 않으며, if문 안쪽에서 선언한 foo의 경우는 if문이 닫히는 시점에서 유효범위가 끝납니다.

그런데 여기서 의문이 생깁니다. if문 안에서 foo를 먼저 호출한 다음 let으로 foo를 다시 선언하게 되면 어떤일이 발생할까요? 일단 if 문에서 단순히 foo를 호출만 해보면

let foo = 'bar1';
console.log(foo); // bar1

if (true) {
  console.log(foo) // bar1
  foo = 'bar2';
  console.log(foo) // bar2
}

console.log(foo); // bar2

정상적으로 호출도되고 값의 변경에도 아무 문제가 없습니다. 그런 foo 호출 이후에 let으로 foo를 선언해 보겠습니다.

let foo = 'bar1';
console.log(foo); // bar1

if (true) {
  console.log(foo);
  // Uncaught ReferenceError: foo is not defined

  let foo = 'bar2';
}

console.log(foo);

foo는 정의되지 않았다는 에러가 발생합니다. 앞에서 말한 임시적 사각지대(TDZ)의 정체가 이것입니다. 어떤 변수가 호출되었을 때 블록 안에 같은 이름의 변수가 없으면 상위 블록에서 선언된 같은 이름의 변수를 호출합니다. 하지만 블록 안에서 let이나 const로 변수 선언이 있었다면 그 이름의 변수는 변수가 선언되기 이전까지 그 블록안에서는 정의되지 않은 변수로 간주되는 것이죠.

4. let과 const는 적절한 관계

그럼 let과 const는 무슨 차이가 있는 걸까요. 일단 이름으로만 봐서는 const는 상수 선언으로 보이는데 말입니다. 실제로 원시형(Primitives type: string, number, boolean, null, undefined)에서 const는 상수로 동작합니다. 따라서 const 로 선언되면 값을 재할당 할 경우 에러가 발생합니다(당연하지만 초기값을 설정하지 않아도 에러가 발생합니다)

const foo = 0;
foo = 1;
// Error: Uncaught TypeError: Assignment to constant variable.

따라서 단순형의 경우 값의 변경이 있는 경우에는 let으로, 상수로 사용하는 경우에는 const로 선언하는 것이 바람직하겠습니다.

하지만, 참조형(Complex type: array, object, function)의 경우 결론부터 말씀드리면 const 로 선언하는 것이 바람직합니다. 참조형은 const로 선언하더라도 멤버값을 조작하는 것이 가능합니다.

const foo = [0, 1];
const bar = foo;

foo.push(2);
bar[0] = 10;

console.log(foo, bar)
// [10, 1, 2] [10, 1, 2]

위의 결과를 보시면 아시겠지만 const bar = foo; 의 선언으로 bar는 foo를 참조합니다. 참조가 아니라 값을 복사(copy)하는 경우에는 array는 … 연산자를 사용하고, object는 assign()함수를 사용합니다.

const arg = [0, 1];
const obj = {foo: 'bar'};

const newArg = [...arg];
const newObj = Ojbect.assign({}, obj);

newArg[0] = 10;
newObj.foo = 'rab';

console.log(arg, obj);
// [0, 1], {foo: 'bar'}

console.log(newArg, newObj);
// [10, 1], {foo: 'rab'}

5. 결론
– ES6 에서는 var는 지양하고 가급적 let과 const를 사용하자
– 원시형에서 변수는 let, 상수는 const로 선언한다
– 참조형은 const로 선언한다

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>