[JavaScript] 자바스크립트 - ES6 문법

배창민·2025년 11월 20일
post-thumbnail

ES6 핵심 문법 정리

화살표 함수 · 스프레드 문법 · 구조 분해 할당


1. 화살표 함수 (Arrow Function)

1-1. 기본 문법

ES6에서 도입된 화살표 함수는 function 키워드 대신 => 를 써서 함수를 더 간단하게 표현하는 문법이다. 항상 익명 함수로 정의되고, 본문이 짧은 함수에서 특히 유용하다.

let message;

// 기존 함수 표현식
message = function () {
  return 'Hello World!';
};
console.log(message());

// 화살표 함수로 변경
message = () => {
  return 'Arrow Function!';
};
console.log(message());

// 한 줄 본문 + 표현식이면 중괄호와 return 생략
message = () => 'Arrow Functions are Simple!';
console.log(message());

매개변수

// 매개변수 여러 개
message = (val1, val2) => 'Arrow ' + val1 + val2;
console.log(message('Function', '!'));

// 매개변수 하나면 괄호 생략 가능
message = val => 'Arrow ' + val;
console.log(message('Functions are GOOD!!!'));

객체 리터럴 반환

객체를 바로 반환할 때는 소괄호로 감싸야 한다. 그렇지 않으면 {} 가 함수 몸체로 해석된다.

const createUser = (id, name) => ({ id, name });
console.log(createUser(1, '유관순'));

const createUser2 = (id, name) => { id, name };
console.log(createUser2(2, '홍길동')); // undefined

고차 함수에서 사용

console.log([1, 2, 3, 4, 5].map(function (val) { return val * 10; }));
console.log([1, 2, 3, 4, 5].map(val => val * 10));

기존 콜백보다 훨씬 간결해진다.


1-2. 화살표 함수의 특징 (일반 함수와 차이)

1) this 를 가지지 않는다 (lexical this)

화살표 함수는 자신의 this 를 갖지 않고, 바깥 스코프의 this를 그대로 캡처한다.

let theater = {
  store: '건대점',
  titles: ['어벤져스', '겨울왕국', '스파이더맨', '라이온킹', '알라딘'],
  showMovieList() {
    // 화살표 함수: 바깥 this = theater
    this.titles.forEach(title => console.log(this.store + ' : ' + title));

    // 일반 함수: this는 전역 객체(window/global)를 가리킨다
    this.titles.forEach(function (title) {
      console.log(this);              // 전역
      console.log(this.store + ' : ' + title); // undefined
    });
  }
};

theater.showMovieList();

콜백 내부에서 바깥 객체의 this 를 그대로 쓰고 싶을 때 화살표 함수가 매우 편하다.


2) 생성자 함수로 쓸 수 없다 (new 불가)

화살표 함수는 thisprototype 을 갖지 않기 때문에 생성자 함수로 쓸 수 없다.

const arrowFunc = () => {};

// new arrowFunc(); // TypeError: arrowFunc is not a constructor

console.log(arrowFunc.hasOwnProperty('prototype')); // false

3) super 를 가지지 않는다

화살표 함수도 super 를 직접 가지지 않고, 상위 스코프의 super 를 그대로 가져다 쓴다.

class Animal {
  constructor(name, weight) {
    this.name = name;
    this.weight = weight;
  }

  move(lostWeight) {
    if (this.weight > lostWeight) this.weight -= lostWeight;
    console.log(`${this.name}(은)는 움직임으로 인해 ${lostWeight}kg만큼 감량되어 ${this.weight}kg가 되었다.`);
  }
}

class Tiger extends Animal {
  move(lostWeight) {
    // 화살표 함수: 바깥의 super 를 그대로 사용
    setTimeout(() => super.move(lostWeight), 3000);

    // 일반 함수로 super 사용하면 SyntaxError
    // setTimeout(function(){ super.move(lostWeight) }, 3000);

    console.log('먹이를 찾아 산기슭을 어슬렁~');
  }
}

let tiger = new Tiger('백두산 호랭이', 90);
tiger.move(1);

4) arguments 를 가지지 않는다

화살표 함수는 자체 arguments 객체도 갖지 않고 상위 스코프의 arguments 를 참조한다.

(function () {
  const arrowFunc = () => console.log(arguments);

  arrowFunc(3, 4); // [1, 2] 출력 (즉시 실행 함수의 arguments)
})(1, 2);

그래서 화살표 함수에서 가변 인수를 다루고 싶으면 arguments 대신 나머지 매개변수(...args) 를 쓰는 쪽이 자연스럽다.


2. 스프레드 문법과 나머지 매개변수

2-1. 나머지 매개변수 (Rest Parameter)

함수에 정의된 매개변수보다 적게 인수를 넘기면 undefined, 많이 넘기면 초과 인수는 버려진다.
나머지 매개변수는 남은 인수들을 배열로 한 번에 받는 문법이다. 항상 매개변수 목록의 마지막에 둔다.

function merge(msg1, msg2) {
  return msg1 + msg2;
}

console.log(merge('안녕하세요.'));
console.log(merge('안녕하세요.', '반갑다.'));
console.log(merge('안녕하세요.', '반갑다.', '제 이름은 홍길동이다.')); // 초과 인수 무시

function mergeAll(...args) {
  let message = '';
  for (let arg of args) message += arg;
  return message;
}

console.log(mergeAll('안녕하세요.'));
console.log(mergeAll('안녕하세요.', '반갑다.'));
console.log(mergeAll('안녕하세요.', '반갑다.', '제 이름은 홍길동이다.'));

2-2. 스프레드 문법 (Spread Syntax)

나머지 매개변수가 인수 목록 → 배열 로 모으는 역할이라면,
스프레드 문법은 그 반대로 배열 → 인수 목록 으로 펼치는 역할을 한다.

console.log(`가장 큰 값 : ${Math.max(10, 20, 30)}`);

let arr = [10, 20, 30];

// 배열 그대로 넘기면 제대로 동작하지 않는다
console.log(`가장 큰 값 : ${Math.max(arr)}`);

// 스프레드 문법으로 배열을 펼친다
console.log(`가장 큰 값 : ${Math.max(...arr)}`);

여러 배열을 섞거나, 일반 값과 함께 사용할 수도 있다.

let arr1 = [10, 30, 20];
let arr2 = [100, 300, 200];

console.log(`가장 작은 값 : ${Math.min(...arr1, ...arr2)}`);
console.log(`가장 작은 값 : ${Math.min(1, ...arr1, 2, ...arr2, 3)}`);

2-3. 배열/문자열 병합과 복사

배열 병합

let arr = [10, 20, 30];
let arr2 = [100, 200, 300];

// concat 보다 간결
let merged = [0, ...arr, 2, ...arr2];
console.log(merged);

문자열을 배열로 변환

let str = 'JavaScript';
console.log([...str]); // ['J','a','v','a','S','c','r','i','p','t']

2-4. 배열/객체 복사 (얕은 복사)

// 배열 복사
let arr = [10, 20, 30];
let arrCopy = [...arr];

console.log(arr);
console.log(arrCopy);
console.log(arr === arrCopy); // false

// 객체 복사
let obj = { name: '홍길동', age: 20 };
let objCopy = { ...obj };

console.log(obj);
console.log(objCopy);
console.log(obj === objCopy); // false

스프레드 복사는 얕은 복사(shallow copy) 라는 점을 기억해 두면 좋다.


2-5. rest vs spread 정리

  • 스프레드 문법(spread)

    • 배열(또는 이터러블)을 펼쳐서 값 목록으로 만든다
    • 함수 호출 인수, 배열/객체 리터럴 내부 등에서 쓴다
  • 나머지 매개변수(rest)

    • 인수 목록의 나머지를 배열로 모은다
    • 함수 매개변수 목록 마지막 위치에만 올 수 있다

둘을 함께 쓰면 인수 목록과 배열 사이를 매우 쉽게 오갈 수 있다.


3. 구조 분해 할당 (Destructuring Assignment)

배열이나 객체에서 값을 꺼낼 때 obj.a, arr[0] 처럼 매번 접근하지 않고,
패턴을 기준으로 한 번에 변수에 할당하는 문법이다.


3-1. 배열 구조 분해 할당

기본 문법

let nameArr = ['Gildong', 'Hong'];

// 기존 방식
// let firstName = nameArr[0];
// let lastName = nameArr[1];

let [firstName, lastName] = nameArr;
console.log(firstName); // Gildong
console.log(lastName);  // Hong

반환 값이 배열인 함수에도 그대로 쓸 수 있다.

let [firstName2, lastName2] = 'Saimdang Shin'.split(' ');
console.log(firstName2); // Saimdang
console.log(lastName2);  // Shin

필요 없는 요소 건너뛰기

let arr = ['firstName', 'middleName', 'lastName'];
let [firstName3, , lastName3] = arr;

console.log(firstName3); // firstName
console.log(lastName3);  // lastName

변수 교환

let student = '학생';
let teacher = '선생님';

[student, teacher] = [teacher, student];
console.log(`student : ${student}, teacher : ${teacher}`);

rest 요소

let [sign1, sign2, ...rest] = ['양자리', '황소자리', '쌍둥이자리', '게자리', '사자자리'];

console.log(sign1); // 양자리
console.log(sign2); // 황소자리
console.log(rest);  // ['쌍둥이자리', '게자리', '사자자리']

기본값

let [firstName4 = '아무개', lastName4 = '김'] = ['길동'];

console.log(firstName4); // '길동' (배열 값)
console.log(lastName4);  // '김'   (기본 값)

객체 프로퍼티에 바로 할당

let user = {};

[user.firstName, user.lastName] = 'Gwansoon Yu'.split(' ');
console.log(user); // { firstName: 'Gwansoon', lastName: 'Yu' }

3-2. 객체 구조 분해 할당

기본 문법

let pants = {
  productName: '배기팬츠',
  color: '검정색',
  price: 30000
};

let { productName, color, price } = pants;

console.log(productName);
console.log(color);
console.log(price);

변수 이름을 바꾸고 싶으면 프로퍼티: 변수명 형태로 쓴다.

let { color: co, price: pr, productName: pn } = pants;

console.log(co);
console.log(pr);
console.log(pn);

원하는 프로퍼티만 뽑기

let { productName: onlyName } = pants;
console.log(`productName : ${onlyName}`);

기본값

객체에 존재하지 않는 프로퍼티에는 기본값을 줄 수 있다.

let shirts = {
  productName: '베이직 셔츠'
};

let {
  productName: pn,
  color: co = '어떤 색상',
  price: pr = 0
} = shirts;

console.log(pn); // '베이직 셔츠'
console.log(co); // '어떤 색상'
console.log(pr); // 0

rest 프로퍼티

let { productName: productName2, ...rest } = pants;

console.log(productName2); // '배기팬츠'
console.log(rest);         // { color: '검정색', price: 30000 }

이미 선언된 변수에 할당할 때 주의

객체 리터럴과 헷갈리지 않도록 소괄호로 감싼다.

let productName3, color3, price3;

// { productName: productName3, color: color3, price: price3 } = pants; // SyntaxError
({ productName: productName3, color: color3, price: price3 } = pants);

console.log(productName3);
console.log(color3);
console.log(price3);

3-3. 중첩 구조 분해 (Nested Destructuring)

객체 안에 객체/배열이 중첩된 경우에도 한 번에 꺼낼 수 있다.

let product = {
  size: {
    width: 10,
    height: 30
  },
  items: ['doll', 'robot']
};

let {
  size: { width, height },
  items: [item1, item2],
  producer = '홍길동'
} = product;

console.log(width);    // 10
console.log(height);   // 30
console.log(item1);    // 'doll'
console.log(item2);    // 'robot'
console.log(producer); // '홍길동' (기본값)

3-4. 함수 매개변수에서의 구조 분해

매개변수가 많을 때, 또는 기본값이 필요할 때 구조 분해를 쓰면 코드가 훨씬 읽기 좋아진다.

기존 방식의 문제

function displayProduct(producer = '아무개', width = 0, height = 0, items = []) {}

displayProduct('신사임당', undefined, undefined, ['Coffee', 'Donut']);
// 기본값을 쓰려고 undefined 를 중간중간 넘겨야 해서 지저분해진다.

구조 분해 매개변수

function displayProduct2({
  producer = '아무개',
  width = 0,
  height = 0,
  items = []
}) {
  console.log(producer);
  console.log(width);
  console.log(height);
  console.log(items);
}

let exampleProduct = {
  items: ['Coffee', 'Donut'],
  producer: '신사임당'
};

displayProduct2(exampleProduct);
  • 인수 순서에 상관없이 프로퍼티 이름 기준으로 값을 넣을 수 있다.
  • 기본값도 각 프로퍼티 옆에 바로 정의할 수 있어 의미가 훨씬 명확해진다.

마무리

정리하면,

  • 화살표 함수

    • function 보다 문법이 간결하고, this / super / arguments 를 가지지 않고 바깥 스코프를 캡처한다.
    • 콜백 함수에서 this 문제를 해결하는 용도로 자주 쓰인다.
  • 나머지 매개변수 / 스프레드 문법

    • rest: 인수 목록을 배열로 모으는 역할 ((...args)), 매개변수 마지막에서만 사용
    • spread: 배열·이터러블을 펼쳐 인수 목록/리터럴에 넣는 역할 (...arr)
  • 구조 분해 할당

    • 배열과 객체에서 필요한 값만 꺼내 변수에 한 번에 할당할 수 있다.
    • 중첩 구조, rest, 기본값, 함수 매개변수와 조합하면 코드 가독성이 크게 좋아진다.

이 세 가지 문법은 ES6 이후 자바스크립트 코드 스타일의 핵심 축이라, 한 번 익혀 두면 이후 코드 작성과 리팩터링이 훨씬 수월해진다.

profile
개발자 희망자

0개의 댓글