thumbnail

자바스크립트 ES6 문법 정리

생성일2024. 10. 16.
태그
작성자Beomgu Jeon

자바스크립트 ES6 문법 정리

ES6 (ECMAScript 6) 이란?

ES6 (ECMAScript 6)는 2015년에 도입된 자바스크립트의 표준안이다. 이는 2009년에 ES5 표준안이 나온 이후로 6년 만의 업데이트인 만큼 많은 기능들이 추가되었다. 이렇게 추가된 기능들을 잘 활용하면 코드를 좀 더 간결하고 읽기 쉽게 작성할 수 있다. 오늘은 이 중에서 많이 사용하는 것들에 대해 간단하게 소개하고 알아볼 예정이다.
 
아래는 오늘 알아볼 ES6 에서 추가된 내용이다.
 
  • var, let, const
  • 화살표 함수 (Arrow Function)
  • Desstructing
  • Rest / Spread Operator
  • for/of
  • Map / Set
  • 템플릿 리터럴 (Template Literals)
  • 클래스 (class)
  • Promise
  • Default Parameter
  • 모듈 가져오기 / 내보내기 (import / export)
 

1. var, let, const

ES6 이전에는 변수 선언 방식이 var 하나로 통일되어 있었다. 하지만 var 로 선언할 시에는 몇 가지 문제가 발생할 수 있다.
 
  1. 함수 스코프로 for, if, while 문 등에서 임시로 정의한 변수들이라고 하더라도, 동일한 함수 내에 있으면 블록이 끝나더라도 지속적으로 해당 변수에 접근할 수 있다.
    1. function loop() { for(var i = 0; i < 5 i++) { console.log(i); // 0 ~ 4 } console.log('finished for loop'); // var 는 함수 스코프로 for 문이 종료되더라도 참조 가능 console.log(i); // 4 } loop();
  1. 호이스팅 과정으로 인해 정의를 하기 전에 var 로 선언된 변수를 참조할 수 있는 문제가 발생한다. (이전 호이스팅 포스팅 참조)
    1. console.log(value); // undefined var value = 10;
  1. 변수의 재정의가 가능하다.
    1. var value = 10; var value = 20; console.log(value); // 20
  1. 변수의 재할당이 가능하다. (상수 사용 불가)
    1. var value = 10; console.log(value); // 10 value = 20; console.log(value); // 20
 
이러한 문제점을 해결하기 위해 나온 키워드가 바로 letconst 이다. letconst 는 모두 블록 스코프이고, 이들 또한 호이스팅 과정이 일어나지만, 선언 전에는 참조할 수 없다 (이전 호이스팅 포스팅 참조). 또한 변수의 재정의가 불가능해서 재정의로 인한 에러를 방지해준다. 이제 여기서 letconst 의 차이가 있다. 바로 let 은 변수의 재할당이 가능하지만 const 는 재할당을 할 수 없게 만들어준다. 만약 const 키워드로 선언된 변수에 재할당을 하려고 하면 에러가 난다.
 
  1. 함수 스코프 → 블록 스코프
    1. function loop() { for (let i = 0; i < 5; i++) { console.log(i); // 0 ~ 4 } console.log('finished loop'); console.log(i); // reference error } loop();
  1. 선언 전 참조 방지
    1. console.log(value); // reference error let value = 10;
  1. 재정의 불가
    1. let value = 10; let value = 20; // syntax error
  1. 재할당 불가 (const 키워드만)
    1. let value1 = 10; value1 = 20; console.log(value1); // 20 const value2 = 10; value2 = 20; // type error console.log(value2);
 

2. 화살표 함수 (Arrow Function)

화살표 함수의 경우 함수를 만들 때 코드의 가독성을 높이기 위해 나온 기능이다. 다만 이는 반드시 익명 함수로 사용해야한다. 설명보다는 예제를 통해 알아보자.
 
function func1() { console.log('Daniel, 25'); } const arrowFunc1 = () => { console.log('Daniel, 25'); }; function func2(name) { console.log(name + ', 25'); } // 파라미터가 1개 일 때는 소괄호 생략 가능 (선택사항) // 함수 바디가 1줄 일 때는 중괄호 생략 가능 const arrowFunc2 = name => console.log(name + ', 25'); function func3(name, age) { console.log(name + ', ' + age); } const arrowFunc3 = (name, age) => { console.log(name + ', ' + age); };
 

3. Destructing (구조 분해 할당)

Destructing(구조 분해 할당)은 객체 혹은 배열에 저장되어 있는 값들을 조금 더 쉽게 저장할 수 있는 기능을 지원한다. 객체의 경우는 키값으로, 배열의 경우는 순서에 따라 각 변수에 값을 저장한다. 아래 예제를 보면서 이해해보자.
 
  • 객체
    • 객체는 키 값을 이용하여 변수에 값을 할당할 수 있다. 이 과정에서 키의 순서는 중요하지 않다. 만약 변수명을 키 값과 다르게 가져가고 싶은 경우 키: 새로운 변수명 형식으로 적어주면 새로운 변수명에 값을 할당할 수 있다.
      const information = { name: 'Daniel', age: 25 }; const { name, age } = information; console.log(name); // Daniel console.log(age); // 25 const { name: newName, age: newAge } = information; console.log(newName); // Daniel console.log(newAge); // 25
  • 배열
    • 객체는 키값으로 값을 접근하는 반면에, 배열은 무조건 앞에서부터 순차적으로 꺼내와야 한다.
      const arr = [10, 20, 30]; const [num1, num2, num3] = arr; console.log(num1); // 10 console.log(num2); // 20 console.log(num3); // 30
 

4. Rest / Spread 연산자

Rest 와 Spread 연산자의 문법은 동일하게 앞에 변수 앞에 ... 을 붙여주면 된다. Rest 연산자의 경우 배열을 destructing 을 하거나, 동적으로 함수의 인자를 받을 때 사용한다. Spread 연산자의 경우 배열 안에 있는 요소들을 모두 꺼내오고 싶을 때 사용한다.
 
  • Rest
    • // destructing const arr = [1, 2, 3, 4, 5]; const [num1, num2, ...restNumbers] = arr; console.log(restNumbers); // [3, 4, 5] // 함수 매개변수 function func1(arg1, arg2, ...args) { console.log(arg1, arg2, args); } func1(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5]
  • Spread
    • const arr = [1, 2, 3, 4, 5]; const newArr = [...arr, 6, 7, 8]; console.log(newArr); // [1, 2, 3, 4, 5, 6, 7, 8]
 

5. for / of

자바와 같은 다른 언어에서 있는 enhanced for 문 처럼 인덱스를 사용하지 않고 바로 iterable 한 객체에서 요소들을 하나씩 가져오고 싶을 때 for/of 문법을 사용해서 가져올 수 있다.
const arr = [1, 2, 3, 4, 5]; for (const e of arr) { console.log(e); // 1, 2, 3, 4, 5 } const str = 'JavaScript'; for (const s of str) { console.log(s); // J, a, v, a, S, c, r, i, p, t }
 

6. Map / Set

key-value 구조의 Map 자료구조와 중복된 요소가 존재하지 않게 해주는 Set 자료구조가 ES6 문법에서 기본적으로 지원하게 되었다.
 
const map = new Map([ ['a', 1], ['b', 2], ['c', 3], ]); map.set('d', 4); console.log(map); // 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4 map.set('a', 5); console.log(map.get('a')); // 5 const set = new Set([1, 2, 3]); console.log(set); // { 1, 2, 3 } set.add(1); set.add(4); set.add(5); console.log(set); // { 1, 2, 3, 4, 5 }
 

7. 템플릿 리터럴 (Template Literals)

템플릿 리터럴은 문자열을 표현할 때 + 연산자를 사용하지 않고 백틱(`) 기호로 문자열을 감싸고, 변수의 경우 ${변수명}의 형태로 표현하는 방식이다. 이는 문자열 출력시에도 사용할 수 있지만, 그 자체를 변수에 저장할 수도 있다.
let name = 'Daniel'; let age = 25; console.log('name: ' + name + ', age: ' + age); console.log(`name: ${name}, age: ${age}`); let str = `name: ${name}, age: ${age}`; console.log(str);
 

8. 클래스 (class)

객체지향 프로그래밍(Object-Oriented Programming)을 지원하기 위해 ES6 부터는 class 키워드로 객체를 만들 수 있도록 지원한다. 이를 이용하여 객체지향 프로그래밍을 한다면, 객체지향의 장점들인 캡슐화, 상속, 다형성, 추상화의 기능을 그대로 가져갈 수 있다. 문법은 아래 예제를 통해 살펴보자.
 
// class 클래스명 class Person { // 2 args 생성자 constructor(name = 'None', age = 0) { this.name = name; this.age = age; } // setter setName(name) { this.name = name; } setAge(age) { this.age = age; } // getter getName() { return this.name; } getAge() { return this.age; } // public function displayInfo() { console.log(`name: ${this.name}, age: ${this.age}`); } } let p1 = new Person(); let p2 = new Person('Daniel', 25); p1.displayInfo(); p2.displayInfo();
 
여기서 주의할 점은 자바스크립트에서는 생성자를 하나만 가질 수 있다는 것이다. 그래서, 생성자를 여러 개로 사용하고 싶다면 아래에서 살펴볼 default parameter 를 이용하여 함수 파라미터의 기본 값을 지정해야 한다.
 

9. Promise

자바스크립트로 개발을 하다보면 비동기 코드를 작성해야 할 때가 많다. 이 때마다 콜백함수를 등록하면서 흔히 말하는 콜백 지옥(callback hell)에 빠질 때가 많다. 이를 해결하기 위해 나온 것이 바로 Promise 문법이다. 여기서는 Promise 의 기본 문법에 대해서만 이야기 할 예정이고, 다음 포스팅에서 콜백 지옥, Promise, 그리고 추후에 등장한 async/await 를 이용한 비동기 처리에 대해서 자세히 얘기해 볼 예정이다.
 
Promise 는 하나의 객체라고 볼 수 있으며, 이는 new 연산자를 이용해 생성할 수 있다. Promise 객체는 만들 때 resolvereject 를 넘겨주는데 resolve 의 경우 함수의 실행이 정상적으로 되었음을 의미하고, reject 는 함수의 실행이 정상적으로 동작하지 않아 예외가 발생했음을 알려준다. 각각 resolvethen 블록으로, rejectcatch 블록으로 연결된다. 아래 코드를 실행해보면서 확인해보자.
 
const isEven = number => new Promise((resolve, reject) => { if (number % 2 === 0) { resolve('val is even number'); // go to then block } else { reject('val is odd number'); // go to catch block } });
 
위의 함수는 number 를 파라미터로 받아서 짝수인지 판별해주는 단순한 함수를 Promise 객체를 이용해 비동기적으로 만들어보았다. 이를 바탕으로 아래의 코드를 실행해보면, 각각 짝수의 경우 resolve 를 실행하여 then 블록으로, 홀수의 경우 reject 를 실행하여 catch 블록으로 진행되는 것을 볼 수 있다.
 
// 2는 짝수이기 때문에 resolve isEven(2) .then(result => { console.log(result); // val is even number }) .catch(err => { console.log(err); }); // 3는 홀수이기 때문에 reject isEven(3) .then(result => { console.log(result); }) .catch(err => { console.log(err); // val is odd number });
 

10. Default Parameter

default parameter 는 다른 언어처럼 함수의 매개변수를 받을 때, 기본 값을 지정할 수 있는 기능이다. 이를 사용할 때 주의할 점은, 기본 값 지정은 매개변수 마지막 부분에서 처음 쪽으로 차례로 해주어야 한다는 것이다.
 
function func1(a, b, c = 3) {} // O function func2(a = 1, b, c) {} // X function func3(a, b = 2, c) {} // X function func4(a, b = 2, c = 3) {} // O
 

11. 모듈 가져오기 / 내보내기 import / export

ES6 문법 이전에는 보통 requiremodule.exports 를 사용하여 모듈을 가져오고 내보냈지만 ES6 로 오면서 importexport 구문이 추가되었다. 이를 사용하기 위해서는 주의할 점이 하나 있는데, 바로 Node 환경에서 사용하기 위해서는 package.json 파일에 아래 필드를 추가해주어야 한다.
{ ... "type": "module" ... }
다른 방법으로는 js 파일의 확장자를 .mjs 로 하여 ES6 모듈을 명시해줄 수도 있다.
 
아래에는 importexport 를 이용하여 함수들을 내보내고 가져오는 간단한 예제이다.
 
calc.js
function add(a, b) { return a + b; } function substract(a, b) { return a - b; } export function multiply(a, b) { return a * b; } export function divide(a, b) { if (b === 0) { throw new Error('Cannot divide by zero'); } return a / b; } export { add, substract };
main.js
import { add, substract, multiply, divide } from './calc.js'; console.log(add(1, 2)); console.log(substract(1, 2)); console.log(multiply(1, 2)); console.log(divide(1, 2)); // import * as calculator from './calc.js'; // console.log(calculator.add(1, 2)); // console.log(calculator.substract(1, 2)); // console.log(calculator.multiply(1, 2)); // console.log(calculator.divide(1, 2));
 

결론

오늘은 ES6 문법에 대해 간략하게 정리해보았는데, 이전에 ES6 문법을 모르고 프로그래밍을 했을 때와 비교해보면 ES6 문법을 사용하면 코드의 가독성이 올라가는 것을 느꼈고, 최근에도 많은 문법이 계속해서 추가되고 있으므로 공부를 계속 해야하는 것 같다.