JavaScript 독학 가이드 9 - ES6+ 최신 문법

learning by Seven Fingers Studio 16분
JavaScriptES6화살표함수구조분해모던JavaScript프로그래밍

2015년 ES6(ES2015)가 나오면서 JavaScript가 완전히 바뀌었어요. 코드가 훨씬 짧아지고 읽기 쉬워졌죠. 요즘 JavaScript 코드는 거의 다 ES6+ 문법으로 작성돼요. 옛날 문법만 알면 최신 코드 읽을 수가 없어요!

let과 const

이미 배웠지만 다시 한 번 강조할게요.

// var는 쓰지 마세요
var old = "옛날 방식";

// let: 변경 가능
let count = 0;
count = 1; // OK

// const: 변경 불가 (기본으로 사용)
const PI = 3.14;
// PI = 3.14159; // 에러!

실행 결과:

> let count = 0
> count = 1
> console.log(count)
1
> const PI = 3.14
> PI = 3.14159
TypeError: Assignment to constant variable.

const는 재할당이 불가능합니다

원칙: const를 기본으로 쓰고, 꼭 바꿔야 할 때만 let

화살표 함수

더 짧고 간결한 함수 문법이에요.

// 기존 방식
function add(a, b) {
    return a + b;
}

// 화살표 함수
const add = (a, b) => {
    return a + b;
};

// 더 짧게 (한 줄이면 return, 중괄호 생략)
const add = (a, b) => a + b;

// 매개변수 하나면 괄호도 생략
const double = n => n * 2;

// 매개변수 없으면 빈 괄호
const greet = () => console.log("Hello");

실행 결과:

> const add = (a, b) => a + b
> console.log(add(5, 3))
8
> const double = n => n * 2
> console.log(double(4))
8
> const greet = () => console.log("Hello")
> greet()
Hello

화살표 함수의 this

const person = {
    name: "철수",
    sayHi: function() {
        setTimeout(function() {
            console.log(this.name); // undefined (this가 바뀜)
        }, 1000);
    },
    sayHello: function() {
        setTimeout(() => {
            console.log(this.name); // "철수" (this 유지됨)
        }, 1000);
    }
};

화살표 함수는 this를 상위 스코프에서 가져와요.

템플릿 리터럴

백틱(`)으로 문자열을 만들면 변수를 쉽게 넣을 수 있어요.

let name = "철수";
let age = 25;

// 옛날 방식
let intro1 = "제 이름은 " + name + "이고, 나이는 " + age + "살입니다.";

// 템플릿 리터럴
let intro2 = `제 이름은 ${name}이고, 나이는 ${age}살입니다.`;

// 표현식도 가능
let result = `10 + 5 = ${10 + 5}`; // "10 + 5 = 15"

// 여러 줄 문자열
let message = `
    첫 번째 줄
    두 번째 줄
    세 번째 줄
`;

실행 결과:

> console.log(intro2)
제 이름은 철수이고, 나이는 25살입니다.
> console.log(result)
10 + 5 = 15
> console.log(message)
첫 번째 줄
두 번째 줄
세 번째 줄

템플릿 리터럴로 변수 삽입과 여러 줄 문자열을 쉽게 만들 수 있습니다

구조 분해 할당

객체나 배열에서 값을 꺼내는 편리한 방법이에요.

객체 구조 분해

let person = {
    name: "철수",
    age: 25,
    email: "chulsu@test.com"
};

// 옛날 방식
let name = person.name;
let age = person.age;

// 구조 분해
let { name, age } = person;
console.log(name); // "철수"
console.log(age); // 25

// 다른 이름으로 저장
let { name: userName, age: userAge } = person;

// 기본값 설정
let { name, phone = "없음" } = person;
console.log(phone); // "없음"

실행 결과:

> let { name, age } = person
> console.log(name)
철수
> console.log(age)
25
> let { name: userName, age: userAge } = person
> console.log(userName)
철수
> let { name, phone = "없음" } = person
> console.log(phone)
없음

배열 구조 분해

let colors = ["빨강", "파랑", "초록"];

// 옛날 방식
let first = colors[0];
let second = colors[1];

// 구조 분해
let [first, second] = colors;
console.log(first); // "빨강"
console.log(second); // "파랑"

// 건너뛰기
let [, , third] = colors;
console.log(third); // "초록"

// 나머지 가져오기
let [red, ...rest] = colors;
console.log(rest); // ["파랑", "초록"]

실행 결과:

> let [first, second] = colors
> console.log(first)
빨강
> console.log(second)
파랑
> let [, , third] = colors
> console.log(third)
초록
> let [red, ...rest] = colors
> console.log(rest)
["파랑", "초록"]

스프레드 연산자

배열이나 객체를 펼치는 연산자예요 (...).

배열 스프레드

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];

// 배열 합치기
let combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]

// 배열 복사
let copied = [...arr1];
copied.push(4);
console.log(arr1); // [1, 2, 3] (원본 안 바뀜)
console.log(copied); // [1, 2, 3, 4]

// 함수 인자로 전달
function sum(a, b, c) {
    return a + b + c;
}
console.log(sum(...arr1)); // 6

실행 결과:

> let combined = [...arr1, ...arr2]
> console.log(combined)
[1, 2, 3, 4, 5, 6]
> let copied = [...arr1]
> copied.push(4)
> console.log(arr1)
[1, 2, 3]
> console.log(copied)
[1, 2, 3, 4]
> console.log(sum(...arr1))
6

스프레드 연산자로 배열을 펼치고 복사할 수 있습니다

객체 스프레드

let person = { name: "철수", age: 25 };
let details = { email: "chulsu@test.com", phone: "010-1234-5678" };

// 객체 합치기
let user = { ...person, ...details };
console.log(user);
// { name: "철수", age: 25, email: "chulsu@test.com", phone: "010-1234-5678" }

// 객체 복사
let copied = { ...person };

// 속성 덮어쓰기
let updated = { ...person, age: 26 };
console.log(updated); // { name: "철수", age: 26 }

실행 결과:

> let user = { ...person, ...details }
> console.log(user)
{ name: "철수", age: 25, email: "chulsu@test.com", phone: "010-1234-5678" }
> let updated = { ...person, age: 26 }
> console.log(updated)
{ name: "철수", age: 26 }

Rest 파라미터

함수에서 여러 인자를 배열로 받아요.

function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5)); // 15

// 일반 매개변수와 함께 사용
function greet(greeting, ...names) {
    return `${greeting}, ${names.join(" and ")}!`;
}

console.log(greet("Hello", "철수", "영희")); // "Hello, 철수 and 영희!"

실행 결과:

> console.log(sum(1, 2, 3))
6
> console.log(sum(1, 2, 3, 4, 5))
15
> console.log(greet("Hello", "철수", "영희"))
Hello, 철수 and 영희!

Rest 파라미터로 가변 인자를 배열로 받을 수 있습니다

단축 속성명

객체 만들 때 변수명과 속성명이 같으면 생략 가능해요.

let name = "철수";
let age = 25;

// 옛날 방식
let person1 = {
    name: name,
    age: age
};

// 단축 속성명
let person2 = { name, age };
console.log(person2); // { name: "철수", age: 25 }

실행 결과:

> let person2 = { name, age }
> console.log(person2)
{ name: "철수", age: 25 }

변수명과 속성명이 같으면 한 번만 써도 됩니다

기본 매개변수

// 옛날 방식
function greet(name) {
    name = name || "손님";
    console.log(`안녕하세요, ${name}님`);
}

// ES6+
function greet(name = "손님") {
    console.log(`안녕하세요, ${name}님`);
}

greet("철수"); // 안녕하세요, 철수님
greet(); // 안녕하세요, 손님님

실행 결과:

> greet("철수")
안녕하세요, 철수님
> greet()
안녕하세요, 손님님

옵셔널 체이닝

객체의 속성에 안전하게 접근해요.

let user = {
    name: "철수",
    address: {
        city: "서울"
    }
};

// 옛날 방식
let zipcode = user && user.address && user.address.zipcode;

// 옵셔널 체이닝
let zipcode = user?.address?.zipcode;
console.log(zipcode); // undefined (에러 안 남)

// 함수 호출에도 사용
user.getName?.(); // getName이 있으면 호출, 없으면 undefined

실행 결과:

> let zipcode = user?.address?.zipcode
> console.log(zipcode)
undefined
> user.getName?.()
undefined

존재하지 않는 속성에 접근해도 에러가 발생하지 않습니다

Nullish 병합 연산자

null이나 undefined일 때만 기본값 사용해요.

let count = 0;

// || 사용 (문제 있음)
let result1 = count || 10;
console.log(result1); // 10 (0도 falsy라서)

// ?? 사용 (올바름)
let result2 = count ?? 10;
console.log(result2); // 0 (null/undefined가 아니므로)

let name = null;
let displayName = name ?? "손님";
console.log(displayName); // "손님"

실행 결과:

> let result1 = count || 10
> console.log(result1)
10
> let result2 = count ?? 10
> console.log(result2)
0
> let displayName = name ?? "손님"
> console.log(displayName)
손님

??는 null과 undefined만 체크하고, 0이나 빈 문자열은 유효한 값으로 취급합니다

배열 메서드

map

let numbers = [1, 2, 3, 4, 5];
let doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

실행 결과:

> console.log(doubled)
[2, 4, 6, 8, 10]

filter

let numbers = [1, 2, 3, 4, 5];
let evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

실행 결과:

> console.log(evens)
[2, 4]

reduce

let numbers = [1, 2, 3, 4, 5];
let sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 15

실행 결과:

> console.log(sum)
15

find / findIndex

let users = [
    { name: "철수", age: 25 },
    { name: "영희", age: 23 },
    { name: "민수", age: 27 }
];

let user = users.find(u => u.name === "영희");
console.log(user); // { name: "영희", age: 23 }

let index = users.findIndex(u => u.name === "영희");
console.log(index); // 1

실행 결과:

> console.log(user)
{ name: "영희", age: 23 }
> console.log(index)
1

실전 예제

1. 사용자 정보 처리

function createUser({ name, age, email = "없음", ...rest }) {
    return {
        name,
        age,
        email,
        createdAt: new Date(),
        ...rest
    };
}

let user = createUser({
    name: "철수",
    age: 25,
    phone: "010-1234-5678"
});

console.log(user);
// {
//   name: "철수",
//   age: 25,
//   email: "없음",
//   createdAt: 2025-11-25...,
//   phone: "010-1234-5678"
// }

실행 결과:

> console.log(user)
{
name: "철수",
age: 25,
email: "없음",
createdAt: 2025-11-25T12:00:00.000Z,
phone: "010-1234-5678"
}

구조 분해, 기본값, rest 파라미터, 단축 속성명을 모두 활용한 예제입니다

2. 배열 데이터 처리

let products = [
    { name: "노트북", price: 1000000, category: "전자제품" },
    { name: "마우스", price: 20000, category: "전자제품" },
    { name: "책상", price: 150000, category: "가구" }
];

// 전자제품만 필터링하고 10% 할인
let discounted = products
    .filter(p => p.category === "전자제품")
    .map(p => ({
        ...p,
        price: p.price * 0.9
    }));

console.log(discounted);

실행 결과:

> console.log(discounted)
[
{ name: "노트북", price: 900000, category: "전자제품" },
{ name: "마우스", price: 18000, category: "전자제품" }
]

filter와 map을 체이닝하여 데이터를 가공했습니다

3. 객체 병합

let defaults = {
    theme: "light",
    language: "ko",
    notifications: true
};

let userSettings = {
    theme: "dark"
};

let settings = { ...defaults, ...userSettings };
console.log(settings);
// { theme: "dark", language: "ko", notifications: true }

실행 결과:

> console.log(settings)
{ theme: "dark", language: "ko", notifications: true }

스프레드 연산자로 기본 설정에 사용자 설정을 병합했습니다

다음 단계

다음 글에서는 비동기 처리와 API 호출을 배웁니다. Promise, async/await로 서버에서 데이터를 가져오고 처리하는 방법을 알아볼 거예요. JavaScript 독학 가이드의 마지막 편입니다!

ES6+ 문법은 현대 JavaScript의 필수예요. 이걸 모르면 최신 코드를 읽을 수가 없어요!


JavaScript 독학 가이드 시리즈:

← 블로그 목록으로