JavaScript는 웹 개발의 중심에 있는 언어다. 1995년 처음 등장한 이후로 꾸준히 발전해왔고, 특히 ES6(ECMAScript 2015)부터는 언어의 패러다임을 바꿀 정도로 큰 변화가 있었다. 이후에도 매년 새로운 기능과 문법이 추가되며 개발자들에게 더 나은 생산성과 가독성을 제공하고 있다. ES6 이후의 주요 문법 변화들을 살펴보면서, 이 변화들이 실제 개발 과정에서 어떤 영향을 미쳤는지 이야기해 보겠다.
1. let과 const: 변수 선언 방식의 변화
ES6 이전에는 변수를 선언할 때 var만 사용할 수 있었다. 하지만 var는 호이스팅(hoisting)과 블록 스코프(block scope) 미지원 문제로 인해 코드의 예측 가능성을 떨어뜨렸다. 이를 해결하기 위해 ES6에서는 let과 const가 도입되었다.
- let: 블록 스코프를 가지며 재할당이 가능하다.
- const: 블록 스코프를 가지며 재할당이 불가능하다.
if (true) {
let x = 10;
console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined
const y = 20;
y = 30; // TypeError: Assignment to constant variable.
ES6 이전까진 개발을 하다 보면 var로 인해 의도치 않은 버그를 경험한 적이 한두 번이 아니었다. 특히 함수 내부에서 선언한 변수가 전역 변수처럼 동작하거나, 반복문 안에서 예상치 못한 값이 출력되는 경우가 많았다. let과 const는 이러한 문제를 원천적으로 차단해 준다. 개인적으로는 이제 var를 사용할 일이 거의 없다고 느낀다. 팀원들과 협업할 때도 코드의 안정성과 가독성이 훨씬 좋아졌다는 점에서 큰 장점이다.
2. 화살표 함수(Arrow Function): 간결한 함수 표현
화살표 함수는 기존의 함수 선언 방식보다 간결하게 함수를 작성할 수 있게 해준다. 특히 콜백 함수나 익명 함수 작성 시 유용하다.
// 기존 함수 선언 방식
function add(a, b) {
return a + b;
}
// 화살표 함수
const add = (a, b) => a + b;
화살표 함수는 단순히 문법을 줄이는 데서 그치지 않는다. 가장 큰 특징은 this 바인딩 방식이다. 기존 함수에서는 this가 호출 컨텍스트에 따라 달라졌지만, 화살표 함수는 상위 스코프의 this를 그대로 사용한다.
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++;
console.log(this.seconds); // NaN (this가 전역 객체를 참조)
}, 1000);
}
function TimerWithArrowFunction() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds); // 정상적으로 증가
}, 1000);
}
처음 화살표 함수를 접했을 땐 단순히 코드를 짧게 쓰기 위한 문법이라고 생각했다. 하지만 실제 프로젝트에서 이벤트 핸들러나 비동기 작업을 다룰 때 이 문법의 진가를 알게 되었다. 특히 클래스 내부에서 콜백 함수를 사용할 때 this 문제로 골머리를 썩던 기억이 떠오른다. 화살표 함수 덕분에 이런 문제가 자연스럽게 해결되었다.
3. 템플릿 리터럴: 문자열 처리의 혁신
ES6 이전에는 문자열을 연결하기 위해 + 연산자를 사용해야 했다. 하지만 복잡한 문자열을 다룰 때는 코드가 난잡해지고 가독성이 떨어졌다. 템플릿 리터럴은 백틱(``)을 사용해 문자열을 작성하며, ${}를 통해 변수나 표현식을 삽입할 수 있다.
const name = "John";
const age = 30;
// 기존 방식
const message1 = "My name is " + name + " and I am " + age + " years old.";
// 템플릿 리터럴
const message2 = `My name is ${name} and I am ${age} years old.`;
템플릿 리터럴은 다중 행 문자열(multi-line string)을 지원하기 때문에 HTML 템플릿이나 JSON 데이터를 작성할 때도 유용하다.
const html = `
<div>
<h1>Hello, ${name}!</h1>
</div>
`;
템플릿 리터럴은 개인적으로 가장 자주 사용하는 ES6 기능 중 하나다. 특히 서버와 데이터를 주고받으며 JSON 형태로 응답을 구성하거나, UI 컴포넌트를 동적으로 생성하는 작업에서 큰 도움이 된다. 코드가 깔끔해지는 것은 물론이고, 실수를 줄이는 데도 효과적이다.
4. 디스트럭처링(Destructuring): 데이터 분해와 재구성
디스트럭처링은 배열이나 객체의 값을 쉽게 분해하여 변수에 할당할 수 있게 해준다.
4.1 배열 디스트럭처링
const numbers = [1, 2, 3];
const [first, second] = numbers;
console.log(first); // 1
console.log(second); // 2
4.2 객체 디스트럭처링
const person = { name: "Alice", age: 25 };
const { name, age } = person;
console.log(name); // Alice
console.log(age); // 25
디스트럭처링은 기본값 설정, 중첩 구조 처리 등 다양한 활용이 가능하다.
const person = { name: "Bob" };
const { name, age = 30 } = person;
console.log(age); // 30 (기본값)
디스트럭처링은 데이터를 다루는 로직에서 코드의 간결함과 명확성을 크게 높여준다. 처음에는 생소했지만, 프로젝트에서 API 응답 데이터를 처리하는 등의 비즈니스 로직 핸들링 시 유용함을 느꼈던 적이 많다.
5. 모듈 시스템: CommonJS에서 ES Modules로
ES6 이전에는 모듈 시스템으로 CommonJS(require/export)를 주로 사용했다. 하지만 이는 브라우저 환경에서는 바로 사용할 수 없었고 번들러가 필요했다. ES6에서는 표준 모듈 시스템인 ES Modules(ESM)이 도입되었다.
// module.js
export const greet = () => console.log("Hello");
// main.js
import { greet } from "./module.js";
greet(); // Hello
ES Modules는 정적 구조를 기반으로 하므로 트리 쉐이킹(tree-shaking)이 가능하다. 즉, 사용하지 않는 코드는 번들링 시 제거되어 최적화된 결과물을 얻을 수 있다.
모듈 시스템의 변화는 개발 환경에 큰 영향을 미쳤다. Node.js에서도 ESM 지원이 점점 확대되고 있어 CommonJS와 ESM 사이에서 고민하던 시기도 이제 끝나가는 것 같다.
마무리하며
ES6 이후 JavaScript는 단순히 웹 브라우저 스크립트 언어라는 틀을 넘어 현대적인 프로그래밍 언어로 자리 잡았다. 위에서 살펴본 let, 화살표 함수, 템플릿 리터럴, 디스트럭처링, 모듈 시스템 외에도 Promise, async/await, Set/Map 등 많은 기능이 추가되었다.
다른 기능들은 다음 글에서 이어서 다루도록 하겠다.
개발자로서 느끼는 점은 JavaScript가 더 이상 과거처럼 불안정하고 예측하기 어려운 언어가 아니라는 것이다. 새로운 문법들은 코드 품질을 높이고 생산성을 크게 향상시켰다. 물론 모든 기능을 무조건 사용하는 것이 능사는 아니다. 프로젝트와 팀의 상황에 맞게 적절히 활용하는 것이 중요하다.
'프로그래밍 > 자바스크립트' 카테고리의 다른 글
[JavaScript] 배열 메서드 - map, filter, reduce (0) | 2025.02.11 |
---|---|
[JavaScript] Callback, Promise, async/await (0) | 2025.02.10 |
[JavaScript] ES6 이후의 주요 문법 변화 (2) (0) | 2025.02.10 |
자바스크립트의 역사와 발전 과정 (0) | 2025.02.08 |