글을 쓴 이유 :
모던자바스크립트 deep dive 책을 통한 자바스크립트의 개념 공부도 끝이 보이고 있다. 실전을 병행하며 무수히 되짚어야겠지만, 이쯤에서 ES6를 ES5와 구분 짓는 주요한 차이점들을 짚어보고 싶었다. 왜 ES6를 그토록 중요한 변환점이라 말하는지, 왜 그러한 변화가 있었고 우리는 이를 어떻게 이용해야할지 정리해보았다.
ES6 에서 추가된 요소 중 가장 크리티컬한 것들을 꼽으라면 변수 선언문(let, const)의 출현, 화살표 함수, 이터레이션 프로토콜을 꼽고 싶다. 물론 클래스, Map, Set, 새로 추가된 다양한 빌트인 메서드 등 다른 중요한 변화들이 있고 또 여전히 활발하게 업데이트되고 있다.
그 중에서도 변수 선언문, 화살표 함수, 이터레이션 프로토콜을 꼽고자 하는 이유는 자바스크립트의 확장된 사용 범위에 따른 터닝포인트이기 때문이다. 다른 말로, 왠만하면 알아서 적당히 처리해주던 자바스크립트 엔진이 이제 똑똑한 개발자를 믿고 컨트롤 역할을 넘겨준 부분이라고 할 수 있다.
var는 ES5까지 유일무이한 변수 선언 키워드였다. 이때까지 변수를 선언하려면 var를 쓸 수밖에 없었다. 어떤 문제들이 있었을까?
사실 var을 쓰지 않아도 작동하는 경우가 많긴 했다. 친절한 자바스크립트 엔진은 개발자가 깜빡했거니 하고 알아서 식별자 등록을 해주었기 때문이다.
// 암묵적 선언
// 함수 내부에 선언되지 않은 변수 a에 10을 할당했다.
function foo(){
a = 10;
}
// 함수 호출
foo();
// 전역에서 a를 불렀는데 10이라는 값이 나온다.
console.log(a); // 10
function foo2(){
console.log(a);
}
foo2(); // 10
위의 경우 함수 foo를 호출했을 때 함수 foo 렉시컬 환경의 환경 레코드에서 a 라는 식별자를 찾는다. 찾을 수 없으니 스코프체인을 타고 외부 참조 스코프인 전역 스코프에서 찾는다. 이곳에도 없는 경우 전역 객체의 프로퍼티로 생성하고 값을 넣어준다.
심지어 같은 이름으로 선언을 해도 에러를 발생시키지 않고 그러려니 못 본 척 해준다. 아래의 예제를 보자
// 중복 선언
var b = 10; // 변수 b를 선언하고 10을 할당
var b; // 같은 이름으로 변수 b 선언 -> 무시
console.log(b); // 10
var b = 'hi'; // 같은이름의 변수를 선언하고 값 할당
console.log(b); // 'hi' 선언
스코프에 관한 문제도 있다. 함수 레벨 스코프를 가진 var는 함수 이외의 블럭에서 선언될 경우 전역에 전언한 것과 마찬가지로 등록된다.
var i = 'I am i';
for (var i = 0; i<3; i++){
console.log(i);
} // 0 1 2
console.log(i); // 3
var는 함수 레벨 스코프(식별자들을 관리하는 환경 레코드를 생성하는 기준이 함수인 것)이므로 반복문인 for문에서 개별적인 스코프를 가지지 않는다. 따라서 var i 는 전역 객체의 i를 중복 선언한 것 과 같으며, 이는 자바스크립트 엔진이 너그럽게 못 본척 해주기 때문에 i에 값을 재할당 해주는 것과 같다. 따라서 반복문이 돌며 0,1,2,3이 되고 3인 순간 조건에 맞지 않아 반복문은 종료된다.
따라서 마지막에 console.log(i)를 찍었을 땐 3이 나오게 된다.
잠깐, 전역변수로 등록되는 것이 왜 문제가 될까?
위의 예제에서도 봤듯이 전역변수는 언제 어디서든 참조할 수 있는, 즉, 암묵적 결합을 허용하는 변수다. 따라서 의도치 않은 변수 값의 변경이 일어날 수 있어 위험하다.
성능 측면에서도 프로그램의 생명주기와 동일한 생명주기를 가지기 때문에 잠깐 필요한 변수라면 전역으로 등록될 경우 불필요한 메모리 낭비를 하게 된다. 이 외에도 스코프 체인의 종점에 있기 때문에 식별자 검색 속도가 가장 느린 문제, 네임 스페이스의 오염을 일으키는 문제 등이 있다.
런타임 실행시 마치 var 로 선언된 변수들이 코드의 상단으로 끌어올려와진 것 같은 현상이 일어나는 것을 변수 호이스팅이라 한다. 자바스크립트 엔진은 코드를 실행하기전 평가 단계를 가지는데, 이 때 var로 선언된 변수, 함수 선언문을 먼저 평가하여 스코프에 등록하기 때문이다. 이때 undefined 값을 저장해주는데, 따라서 변수를 선언하기 전 변수를 호출한다면 undefined 값이 불러와지는 것이다.
console.log(c); // undefined
var c = 10;
console.log(c); // 10
자바스크립트 엔진의 실행 과정을 알면 호이스팅에 대해 쉽게 이해할 수 있다.
var는 코드 실행 전 undefined
값을 초기화한다. 따라서 실행 중 선언된 위치보다 이르게 변수를 만나면 undefined를 반환한다. let, const는 코드 실행 전 선언 후 초기화하지 않고 uninitialized로 둔다. 따라서 코드 실행 중 호이스팅이 일어나지 않는 것 처럼 보인다.
var가 가지던 문제들을 해결한다.
- 암묵적 식별자 등록 안됨
- 블록 레벨 스코프
- 재선언 불가
- 변수 호이스팅이 일어나지 않는 것 처럼 동작
- 변수, 상수의 용도 구분(const는 객체일 경우 참조값만 바뀌지 않으면 된다.)
var에서 암묵적으로 처리해주던 것들이 오히려 섬세한 개발을 하는데 문제가 되었다. 자바스크립트가 더이상 브라우저에서 단순한 작동만을 처리하던 언어가 아닌, 복잡한 프레임워크 및 백엔드까지 활용되서 그에 맞게 개발자에게 더 많은 권한과 책입을 준 것이다.
ECMA6 에서 눈에 띄는 이러한 변화들은 화살표 함수, 이터레이션 프로토콜에서도 볼 수 있다. 다음 글에선 화살표 함수와 이터레이션 프로토콜에 대해 알아보고 개발자가 ES6에서 더 부여받은 역할은 어떤 것인지 살펴보려고 한다.