본문으로 건너뛰기

안동민 개발노트

본문 시작

구조 분해 할당과 스프레드 연산자

배열·객체에서 필요한 값을 구조 분해하고 스프레드로 복사·결합·함수 인수 전달을 간결하게 처리합니다.

우리는 8장을 통해 let, const로 변수 선언을 개선하고, 화살표 함수로 코드 작성 방식을 더 간결하고 예측 가능하게 만들었습니다.

이제 데이터 처리 방식 자체를 크게 바꿔준 다음 핵심 기능으로 넘어갑니다.

구조 분해 할당(Destructuring Assignment)스프레드 연산자(Spread Operator)는 배열이나 객체에서 데이터를 추출하거나, 배열이나 객체를 복사/결합할 때 사용되는 문법입니다.

이 두 기능은 코드를 훨씬 간결하고 가독성 높게 만들어주며, 데이터를 다루는 다양한 상황에서 개발자의 편의성을 극대화합니다.

이번 장에서는 구조 분해 할당으로 값 추출하기, 스프레드 연산자로 배열/객체 다루기를 중심으로 핵심 패턴을 정리합니다.

구조 분해 할당과 스프레드 연산자

구조 분해는 필요한 값을 이름으로 꺼내고, 스프레드는 남은 값이나 복사 범위를 펼칩니다. 두 문법 모두 데이터 모양을 코드 표면에 드러냅니다.

  1. 값을 꺼내고 새 컬렉션을 만드는 순서

    구조 분해 할당은 배열이나 객체에서 필요한 값을 바로 꺼냅니다.

  2. 스프레드 연산자

    배열이나 객체의 내용을 펼쳐 전달합니다.

  3. 구조 분해와 스프레드 정리

    배열과 객체 데이터를 꺼내고 조합하는 코드를 간결하게 만듭니다.

  4. 배열 구조 분해

    위치를 기준으로 값을 꺼내 변수에 바로 할당합니다.

  5. 복사 깊이와 누락 값을 먼저 확인

    구조 분해 할당은 필요한 값을 꺼내고, 스프레드 연산자는 기존 값을 복사하거나 합칠 때 사용합니다.


구조 분해 할당

구조 분해 할당과 스프레드 연산자에서는 값의 변화, 실행 순서, 화면 반응을 함께 봅니다.

구조 분해 할당

객체는 property name, 배열은 index position으로 값을 꺼내며 alias, default, rest, nested 패턴을 함께 쓸 수 있다.

패턴기준예시주의점
Object속성 이름으로 꺼냄const { name } = user속성 이름 오타
Array순서 위치로 꺼냄const [first] = list순서 의미가 불명확
Alias다른 변수명으로 받음{ name: userName }원래 이름과 새 이름 혼동
Default값이 undefined일 때 대체{ count = 0 }null에는 적용되지 않음
Rest남은 값 묶기{ id, ...rest }얕은 복사임

구조 분해 할당 (Destructuring Assignment)은 배열이나 객체의 속성(property)을 해체하여 그 값을 개별 변수에 할당할 수 있게 하는 자바스크립트 표현식입니다.

복잡한 데이터 구조에서 필요한 값만 깔끔하게 추출할 때 매우 유용합니다.

배열 구조 분해 (Array Destructuring)

배열의 각 요소를 해체하여 변수에 할당할 수 있습니다.

const colors = ["red", "green", "blue"];

// 배열 구조 분해 할당
const [firstColor, secondColor, thirdColor] = colors;

console.log(firstColor);  // 결과: "red"
console.log(secondColor); // 결과: "green"
console.log(thirdColor);  // 결과: "blue"
다양한 활용
  • 일부 값만 추출: 필요한 요소만 선택적으로 가져올 수 있습니다. 건너뛰고 싶은 요소는 쉼표로 비워둡니다.

    const numbers = [10, 20, 30, 40, 50];
    const [a, , c, , e] = numbers; // 두 번째와 네 번째 요소 건너뛰기
    console.log(a); // 결과: 10
    console.log(c); // 결과: 30
    console.log(e); // 결과: 50
  • 기본값 설정: 할당할 값이 undefined일 경우에 대비하여 기본값을 설정할 수 있습니다.

    const fruits = ["apple"];
    const [myFruit, otherFruit = "orange"] = fruits;
    console.log(myFruit);    // 결과: "apple"
    console.log(otherFruit); // 결과: "orange" (fruits 배열에 두 번째 요소가 없으므로 기본값 사용)
    const [item1, item2 = "default"] = [];
    console.log(item1); // 결과: undefined
    console.log(item2); // 결과: "default"
  • 나머지 요소 가져오기 (...rest): 배열의 나머지 요소들을 새로운 배열로 묶을 수 있습니다. (스프레드 연산자와 동일한 문법이지만 여기서는 나머지 파라미터 역할)

    const scores = [80, 90, 70, 60, 95];
    const [math, english, ...restScores] = scores;
    console.log(math);       // 결과: 80
    console.log(english);    // 결과: 90
    console.log(restScores); // 결과: [70, 60, 95] (새로운 배열)
  • 변수 값 교환: 임시 변수 없이 두 변수의 값을 쉽게 교환할 수 있습니다.

    let x = 1;
    let y = 2;
    [x, y] = [y, x]; // x와 y의 값을 교환
    console.log(x); // 결과: 2
    console.log(y); // 결과: 1

객체 구조 분해 (Object Destructuring)

객체의 프로퍼티를 해체하여 변수에 할당할 수 있습니다.

변수 이름은 객체의 프로퍼티 이름과 동일해야 합니다.

const person = {
    name: "Alice",
    age: 30,
    city: "New York"
};

// 객체 구조 분해 할당
const { name, age } = person;

console.log(name); // 결과: "Alice"
console.log(age);  // 결과: 30
// console.log(city); // ReferenceError: city is not defined (city는 할당하지 않음)
다양한 활용
  • 다른 이름으로 할당: 객체의 프로퍼티 이름과 다른 이름으로 변수를 선언하고 싶을 때 사용합니다.

    const { name: userName, age: userAge } = person;
    console.log(userName); // 결과: "Alice"
    console.log(userAge);  // 결과: 30
  • 기본값 설정: 할당할 프로퍼티가 undefined일 경우에 대비하여 기본값을 설정할 수 있습니다.

    const { country = "Korea", age } = person; // person 객체에 country 프로퍼티가 없으므로 기본값 사용
    console.log(country); // 결과: "Korea"
    console.log(age);     // 결과: 30
  • 나머지 프로퍼티 가져오기 (...rest): 객체의 나머지 프로퍼티들을 새로운 객체로 묶을 수 있습니다.

    const { name, ...restInfo } = person;
    console.log(name);     // 결과: "Alice"
    console.log(restInfo); // 결과: { age: 30, city: "New York" } (새로운 객체)
  • 중첩된 객체 구조 분해: 중첩된 객체에서 깊숙이 있는 프로퍼티를 한 번에 추출할 수 있습니다.

    const userProfile = {
        id: 1,
        userInfo: {
            fullName: "Bob Johnson",
            email: "bob@example.com"
        },
        preferences: {
            theme: "dark"
        }
    };
    const {
        userInfo: { fullName, email }, // userInfo 객체를 분해하여 fullName, email 추출
        preferences: { theme: userTheme } // preferences 객체를 분해하여 theme를 userTheme으로 추출
    } = userProfile;
    console.log(fullName);   // 결과: "Bob Johnson"
    console.log(email);      // 결과: "bob@example.com"
    console.log(userTheme);  // 결과: "dark"
  • 함수 매개변수에서의 구조 분해: 함수의 매개변수에서 객체나 배열을 직접 구조 분해하여 사용할 수 있어 코드가 더욱 간결해집니다.

    // 객체 매개변수 구조 분해
    function displayUser({ name, age }) {
        console.log(`이름: ${name}, 나이: ${age}`);
    }
    const myUser = { name: "철수", age: 25 };
    displayUser(myUser); // 결과: 이름: 철수, 나이: 25
    // 배열 매개변수 구조 분해
    function printCoordinates([x, y]) {
        console.log(`X: ${x}, Y: ${y}`);
    }
    const point = [10, 20];
    printCoordinates(point); // 결과: X: 10, Y: 20

스프레드 연산자 ...

스프레드 연산자 (Spread Operator) (...)는 이터러블(iterable) 객체(배열, 문자열 등)나 객체의 내용을 개별 요소로 확장(펼치는)하는데 사용됩니다.

배열, 객체, 함수의 인자 등 다양한 곳에서 활용됩니다.

배열에서의 스프레드 연산자

  • 배열 결합 (Concatenation): 여러 배열을 쉽게 결합할 수 있습니다.

    const arr1 = [1, 2];
    const arr2 = [3, 4];
    const combinedArr = [...arr1, ...arr2];
    console.log(combinedArr); // 결과: [1, 2, 3, 4]
    const newArr = [0, ...arr1, 5, ...arr2];
    console.log(newArr); // 결과: [0, 1, 2, 5, 3, 4]
  • 배열 복사 (Shallow Copy): 원본 배열을 변경하지 않고 새로운 배열의 사본을 만들 수 있습니다. (얕은 복사임에 유의)

    const original = [1, 2, 3];
    const copy = [...original];
    console.log(copy);          // 결과: [1, 2, 3]
    console.log(original === copy); // 결과: false (다른 메모리 주소)
    copy.push(4);
    console.log(original); // 결과: [1, 2, 3] (원본 불변)
  • 배열 요소 추가/삽입: 배열의 특정 위치에 요소를 쉽게 추가하거나 삽입할 수 있습니다.

    const baseArray = [1, 2, 3];
    const newArrayAtStart = [0, ...baseArray]; // 맨 앞에 추가
    console.log(newArrayAtStart); // 결과: [0, 1, 2, 3]
    const newArrayAtMiddle = [1, ...baseArray.slice(1, 2), 99, ...baseArray.slice(2)]; // 중간에 삽입 (복잡)
    console.log(newArrayAtMiddle); // 결과: [1, 2, 99, 3]
    // 실용적으로는 splice나 map, filter를 조합하는 경우가 많음
  • 이터러블을 배열로 변환: 유사 배열 객체나 다른 이터러블(예: Set, String)을 배열로 쉽게 변환할 수 있습니다.

    const myString = "hello";
    const charArray = [...myString];
    console.log(charArray); // 결과: ["h", "e", "l", "l", "o"]
    const mySet = new Set([1, 2, 3]);
    const setToArray = [...mySet];
    console.log(setToArray); // 결과: [1, 2, 3]

객체에서의 스프레드 연산자 (ES2018부터)

  • 객체 병합 (Object Merging): 여러 객체를 병합하여 새로운 객체를 만들 수 있습니다.

    const user = { name: "John", age: 30 };
    const address = { city: "Seoul", zip: "12345" };
    const details = { job: "Developer" };
    const fullProfile = { ...user, ...address, ...details };
    console.log(fullProfile);
    // 결과: { name: 'John', age: 30, city: 'Seoul', zip: '12345', job: 'Developer' }

    이때 동일한 키가 있으면 뒤에 오는 객체의 값이 덮어씁니다.

    const objA = { a: 1, b: 2 };
    const objB = { b: 3, c: 4 };
    const mergedObj = { ...objA, ...objB };
    console.log(mergedObj); // 결과: { a: 1, b: 3, c: 4 } (b가 덮어써짐)
  • 객체 복사 (Shallow Copy): 원본 객체를 변경하지 않고 새로운 객체의 사본을 만들 수 있습니다. (얕은 복사임에 유의)

    const originalObj = { x: 1, y: 2 };
    const copyObj = { ...originalObj };
    console.log(copyObj);          // 결과: { x: 1, y: 2 }
    console.log(originalObj === copyObj); // 결과: false
    copyObj.x = 10;
    console.log(originalObj.x); // 결과: 1 (원본 불변)
  • 객체 프로퍼티 추가/업데이트: 기존 객체에 새로운 프로퍼티를 추가하거나, 특정 프로퍼티 값을 업데이트할 때 유용합니다.

    const product = { id: 1, name: "Laptop", price: 1200 };
    const updatedProduct = { ...product, price: 1100, category: "Electronics" };
    console.log(updatedProduct);
    // 결과: { id: 1, name: 'Laptop', price: 1100, category: 'Electronics' }

함수 호출 시 스프레드 연산자 (...args)

함수에 배열의 요소들을 개별적인 인자로 전달할 때 사용합니다.

(이때는 나머지 파라미터와는 다름)

function sum(a, b, c) {
    return a + b + c;
}

const numbersToSum = [1, 2, 3];
const result = sum(...numbersToSum); // 배열 요소를 개별 인자로 펼쳐서 전달
console.log(result); // 결과: 6

서버에서 받은 JSON 데이터를 화면 상태로 바꾸는 과정에서도 구조 분해와 스프레드는 함께 쓰입니다.

응답 데이터에서 화면 상태까지

HTTP JSON을 받은 뒤 구조 분해로 필요한 필드만 꺼내고, 스프레드로 기존 상태를 얕게 복사해 렌더 입력을 만든다.

단계하는 일문법검토할 것
ReceiveJSON 응답을 받음await res.json()응답 구조 검증
Pick필요 필드를 꺼냄const { items } = data없는 필드 기본값
Rename화면에서 쓸 이름으로 변환{ id: userId }의미 있는 변수명
Copy기존 상태를 얕게 복사{ ...state, items }중첩 객체 mutation 주의
Render inputDOM이나 컴포넌트에 전달setState(next)원본 응답과 화면 상태 분리

구조 분해와 스프레드 정리

이번 장에서는 ES2015(ES6)에서 도입된 매우 강력하고 유용한 문법인 구조 분해 할당(Destructuring Assignment)스프레드 연산자(Spread Operator)에 대해 심도 있게 학습했습니다.

구조 분해 할당은 배열과 객체에서 필요한 값만 꺼낼 때 사용합니다.

스프레드 연산자는 배열 결합, 얕은 복사, 객체 병합, 함수 인자 전달처럼 값을 펼쳐 전달해야 하는 상황에서 자주 쓰입니다.

이 두 기능은 데이터를 꺼내고 합치는 코드를 짧게 만들지만, 원본 변경 여부와 얕은 복사 범위는 항상 함께 확인해야 합니다.

예제를 직접 바꿔 보며 배열과 객체에서 어떤 값이 새로 만들어지는지 확인해 보세요.


구조 분해와 스프레드는 객체와 배열을 직접 바꾸지 않고 필요한 값만 꺼내거나 새 값으로 조합하는 데 자주 쓰입니다.

꺼낼 때 구조분해, 합칠 때 스프레드를 쓴다

두 문법은 ...처럼 비슷해 보이는 기호도 있지만, 값 추출과 값 복사의 위치가 다르므로 목적을 먼저 나누면 쉽다.

목적구조 분해스프레드주의점
값 꺼내기const { name } = user해당 없음없는 값 기본값 처리
이름 바꾸기{ name: label }해당 없음원래 속성명과 변수명 구분
남은 값 묶기{ id, ...rest }rest property얕은 복사
값 펼치기해당 없음{ ...user, active:true }뒤 값이 앞 값을 덮음
배열 조합[first, ...others][...a, ...b]중첩 배열은 그대로 공유

마지막으로 상태나 응답 데이터를 다룰 때는 어떤 값이 추출되고 어떤 값이 새 객체로 복사되는지 한 흐름으로 확인합니다.

구조 분해와 스프레드 판단

값을 꺼낼지, 새 객체나 배열을 만들지, 중첩 값을 안전하게 복사할지에 따라 문법 선택이 달라진다.

질문쓸 문법좋은 예위험 신호
필드만 꺼내나구조 분해const { id } = item필드가 없을 때 처리 없음
이름을 바꾸나alias{ id: itemId }변수 의미가 흐림
나머지를 보관하나rest{ id, ...rest }rest도 얕은 복사임
새 값을 합치나spread{ ...prev, done:true }덮어쓰기 순서 착각
깊은 복사가 필요한가structuredClone 등 별도 처리중첩 변경 방지spread가 deep copy라고 오해
구조 분해와 스프레드를 얕은 복사로 이해하기

구조 분해는 값을 꺼내고 스프레드는 새 배열·객체를 만들기 쉽지만, 중첩 객체까지 자동으로 복제하지는 않는다.

작업겉보기실제 경계주의할 버그
Object spread새 객체 생성1단계 속성만 새로 복사중첩 객체는 같은 참조
Array spread새 배열 생성요소 참조는 그대로객체 요소 mutation 공유
Destructure필드 값을 변수로 꺼냄꺼낸 값이 참조일 수 있음값을 바꾸면 원본에 영향 가능
Nested update중첩 단계마다 새 객체 필요{ ...a, b:{ ...a.b } }한 단계만 복사
Deep copy깊은 복사가 필요할 때 별도 도구structuredClone함수/특수 객체 처리 주의
구조 분해와 스프레드의 데이터 복사 경계

구조 분해와 스프레드는 객체·배열의 모양을 읽기 쉽게 다루지만, 얕은 복사 한계를 알아야 상태 변경 버그를 피한다.

경계안전한 경우위험한 경우대응
Primitive숫자와 문자열을 꺼내 사용타입 변환만 주의그대로 사용 가능
Top-level object1단계 속성만 바꾸기중첩 객체를 함께 수정수정할 단계마다 복사
Array item원시값 배열 변환객체 배열의 item mutationmap으로 새 item 생성
Merge order뒤 값이 앞 값을 덮음기본값과 사용자값 순서 착각우선순위를 코드에 드러냄
State update불변 업데이트가 필요한 UI 상태원본을 직접 수정새 참조와 새 중첩 참조 생성