2015년에 자바스크립트 문법에 매우 큰 변화가 있었습니다. ES2015(이하 ES6)의 등장입니다.
이때를 기점으로 매년 문법 변경 사항이 발표되고 있으며, 새로운 문법 specification에 대해서도 활발한 논의가 이루어지고 있습니다.
보통 자바스크립트를 배울 때는 var로 변수를 선언하는 방법부터 배웁니다. 하지만 이제 var은 const와 let이 대체합니다.
function testScope() { if (true) { let x = 10; // let은 블록 스코프 const y = 20; // const도 블록 스코프 var z = 30; // var는 함수 스코프 console.log("블록 내부:", x, y, z); } // console.log(x); // ❌ ReferenceError: x is not defined // console.log(y); // ❌ ReferenceError: y is not defined console.log("블록 밖:", z); // ✅ var는 함수 전체에서 접근 가능 }
두 변수 타입이 공통적으로 갖는 블록 스코프(범위) 예제입니다. 이렇게 var과 const, let은 스코프의 차이를 지닙니다. var을 이용하면 프로그래머가 생각한 스코프 밖에서 변수에 접근이 가능하여 다양한 오류를 발생시킵니다. 이런 스코프의 차이를 통해 호이스팅 같은 문제도 해결되고 코드 관리도 수월해졌습니다.
자바스크립트에서 변수 선언(var, let, const)과 함수 선언이 코드 실행 전에 메모리 상단으로 끌어올려지는 현상을 말합니다. 즉, 선언은 위로 끌어올려지지만 초기화는 끌어올려지지 않기 때문에, 정의 전에 변수를 참조하면 undefined 또는 ReferenceError가 발생할 수 있습니다.
const는 상수입니다.
let은 변수입니다.
const와 반대되는 특성을 지닙니다.템플릿 문자열은 큰따옴표나 작은따옴표로 감싸는 기존 문자열과 달리 백틱으로 감쌉니다. 특이한 점은 문자열 안에 변수를 넣을 수 있다는 점입니다.
이를 통해 깔끔한 코드를 유지할 수 있으며, 큰따옴표와 작은따옴표와 함께 사용할 수 있습니다.
const name = "시쿠"; const age = 25; // 템플릿 문자열 사용 const message = `안녕하세요, ${name}님! 나이는 ${age}살입니다.`; console.log(message);
안녕하세요, 시쿠님! 나이는 25살입니다.
자바스크립트에서 객체(Object)는 데이터를 구조적으로 표현할 때 가장 기본이 되는 형태입니다. ES6에서는 객체 리터럴 문법이 크게 개선되어, 더 간결하고 직관적인 객체 정의가 가능해졌습니다.
이전에는 객체를 정의할 때 키와 값의 이름이 같더라도 모두 명시해야 했습니다.
const name = "시쿠"; const age = 25; const user = { name: name, age: age };
ES6부터는 키와 변수명이 동일하다면 하나만 써도 됩니다 👇
const user = { name, age }; console.log(user); // { name: "시쿠", age: 25 }
객체 리터럴에 추가된 문법은 코딩의 편의성을 향상시키고자 만들어진 것이라는 느낌이 강합니다. 따라서 익숙해지면 코드의 양을 많이 줄일 수 있습니다.
ES6에서는 기존의 function 키워드를 대체할 수 있는 새로운 함수 표현 방식인 화살표 함수(Arrow Function)가 도입되었습니다. 이 문법은 코드의 가독성을 높이고, 특히 콜백 함수나 간단한 연산을 표현할 때 매우 유용합니다.
화살표 함수는 function 대신 =>(화살표)를 많이 사용합니다.
// 기존 함수 표현식 const add = function (a, b) { return a + b; }; // 화살표 함수 const add = (a, b) => a + b; console.log(add(2, 3)); // 5
return문이 단일 표현식이라면 중괄호({})와 return 키워드를 생략할 수 있습니다. 하지만 여러 줄의 로직을 포함할 때는 반드시 {}로 블록을 감싸야 합니다.
const greet = (name) => { const message = `안녕하세요, ${name}님!`; return message; }; console.log(greet("시쿠"));
화살표 함수의 가장 큰 특징 중 하나는 this 바인딩 방식이 다르다는 것입니다.
기존 함수는 호출 시점에 따라 this가 달라지지만, 화살표 함수는 자신이 선언된 위치(상위 스코프)의 this를 유지합니다.
function Counter() { this.count = 0; setInterval(function () { this.count++; // ❌ 일반 함수에서는 this가 전역 객체(window)를 가리킴 console.log(this.count); }, 1000); } new Counter(); // NaN, NaN, NaN...
위의 문제는 화살표 함수를 사용하면 간단히 해결됩니다 👇
function Counter() { this.count = 0; setInterval(() => { this.count++; // ✅ 화살표 함수는 외부 스코프(this)를 유지 console.log(this.count); }, 1000); } new Counter(); // 1, 2, 3, 4, ...
화살표 함수는 간결하고 직관적이며, 특히 콜백 함수나 클래스 내부의 메서드 정의에서 깔끔한 코드를 작성할 때 매우 유용합니다.
구조 분해 할당은 배열이나 객체의 값을 손쉽게 변수로 꺼내 쓸 수 있는 문법입니다. 이전에는 인덱스나 키를 일일이 지정해야 했지만, 이제는 더 간결하고 직관적으로 데이터를 다룰 수 있습니다.
배열에서 값을 꺼내는 일반적인 방법은 다음과 같습니다.
const arr = [1, 2, 3]; const a = arr[0]; const b = arr[1]; const c = arr[2]; console.log(a, b, c); // 1 2 3
ES6에서는 구조 분해 문법을 사용해 이렇게 쓸 수 있습니다 👇
const [a, b, c] = [1, 2, 3]; console.log(a, b, c); // 1 2 3
필요한 값만 골라서 받을 수도 있습니다.
const numbers = [10, 20, 30, 40]; const [first, , third] = numbers; console.log(first, third); // 10 30
또한 기본값(default value) 을 설정할 수도 있습니다.
const [x, y = 5] = [10]; console.log(x, y); // 10 5
배열의 일부를 나머지(rest) 로 받는 것도 가능합니다.
const [head, ...rest] = [1, 2, 3, 4]; console.log(head); // 1 console.log(rest); // [2, 3, 4]
구조 분해 할당은 배열뿐만이 아니라, 배열과 객체 모두에서 사용할 수 있는 구조 기반 값 추출 문법입니다.
ES6에서는 기존의 프로토타입 기반 객체지향 문법을 좀 더 직관적이고 선언적인 방식으로 사용할 수 있도록 class 문법이 도입되었습니다. 이는 자바나 파이썬과 같은 언어의 클래스 개념과 유사하게 동작합니다.
하지만 다른 언어처럼 클래스 기반으로 동작하는 것이 아니라, 동작은 여전히 프로토타입 기반입니다.
// 클래스 정의 class Person { constructor(name, age) { this.name = name; this.age = age; } // 메서드 정의 introduce() { console.log(`안녕하세요, ${this.name}입니다. 나이는 ${this.age}살이에요.`); } } // 인스턴스 생성 const user = new Person("시쿠", 25); user.introduce(); // 안녕하세요, 시쿠입니다. 나이는 25살이에요.
extends 키워드를 사용하면 기존 클래스를 상속하여 새로운 클래스를 만들 수 있습니다.super()를 통해 부모 클래스의 생성자나 메서드에 접근할 수 있습니다.class Student extends Person { constructor(name, age, major) { super(name, age); // 부모 클래스의 생성자 호출 this.major = major; } study() { console.log(`${this.name}은(는) ${this.major}를 전공하고 있습니다.`); } } const student = new Student("준수", 24, "컴퓨터공학"); student.introduce(); // 부모 클래스 메서드 사용 student.study(); // 자식 클래스 메서드 사용
자바스크립트는 비동기 처리를 위해 콜백(callback) 함수를 사용했지만, 코드가 복잡해질수록 “콜백 지옥(callback hell)” 문제가 발생했습니다. 이를 해결하기 위해 ES6에서는 Promise라는 새로운 비동기 처리 방식이 도입되었습니다.
Promise는 비동기 작업의 성공 또는 실패 결과를 약속하는 객체입니다. 즉, 나중에 완료될(혹은 실패할) 작업의 결과를 다루기 위한 표준화된 방법입니다.
const promise = new Promise((resolve, reject) => { const success = true; if (success) { resolve("작업이 성공했습니다!"); } else { reject("작업이 실패했습니다!"); } }); promise .then((result) => console.log(result)) // 성공 시 실행 .catch((error) => console.error(error)); // 실패 시 실행
Promise는 세 가지 상태(state)를 가집니다.

then()을 연속으로 사용하면 비동기 작업을 순서대로 처리할 수 있습니다.
fetch("https://api.example.com/user") .then((response) => response.json()) .then((data) => console.log("유저 정보:", data)) .catch((error) => console.error("에러 발생:", error));
ES8(2017)에서는 비동기 코드를 동기 코드처럼 읽기 쉽게 작성할 수 있는 async/await 문법이 도입되었습니다.
이 문법은 Promise를 기반으로 동작하며, 복잡한 .then() 체인을 깔끔하게 정리할 수 있습니다.
function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function greet() { console.log("3초 뒤 인사합니다..."); await delay(3000); // Promise가 끝날 때까지 기다림 console.log("안녕하세요, 시쿠님!"); } greet();
3초 뒤 인사합니다... (3초 후) 안녕하세요, 시쿠님!
위 코드는 Promise의 .then()을 여러 번 이어 쓰는 대신, 마치 동기적인 흐름처럼 자연스럽게 작성할 수 있습니다.
기존의 Promise 체이닝 방식은 가독성이 떨어질 수 있었습니다 .
fetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log("데이터:", data)) .catch((error) => console.error("에러 발생:", error));
async/await 문법으로 바꾸면 이렇게 훨씬 간결해집니다. try...catch 문을 사용하면 에러 처리까지 깔끔하게 통합할 수 있습니다.
async function fetchData() { try { const response = await fetch("https://api.example.com/data"); const data = await response.json(); console.log("데이터:", data); } catch (error) { console.error("에러 발생:", error); } } fetchData();
await은 순차 실행이 기본이지만, 동시에 실행해야 할 경우에는 Promise.all()을 함께 사용할 수 있습니다.
async function loadAll() { const [user, posts] = await Promise.all([ fetch("/user").then((res) => res.json()), fetch("/posts").then((res) => res.json()) ]); console.log("유저:", user); console.log("게시글:", posts); } loadAll();
이렇게 하면 병렬로 동시에 요청을 보내고, 두 결과가 모두 도착하면 한 번에 처리할 수 있습니다.

async/await은 Promise를 더 쉽게 사용하기 위한 문법입니다.
복잡한 비동기 로직을 마치 순차적으로 실행되는 코드처럼 다룰 수 있어, 현대 자바스크립트의 표준 비동기 패턴으로 자리 잡았습니다.
다양한 자바스크립트의 문법이 있지만, ES6 이후로 등장하는 문법들을 잘 알아야 노드 개발을 시작하기 좋을 것 같아요!
또 이 문법의 동작 원리에 대해 딥다이브 해보는 것도 좋을 것 같습니다 ! :)