- 목차 -

배열을 자르거나, 정렬하거나, 평탄화할 때 원본 배열 변경 여부는 매우 매우 매우 * 1000 중요합니다‼️ 제가 자바스크립트 어레이에 대해 깊게 알아보고 싶었던 계기이기도 합니다. 자바스크립트가 주 언어라면 가장 많이 사용하고 활용하는 Array 관련 메서드들의 특징과 사용법은 충분히 알고 있어야 됩니다.

의도치 않은 원본 변경으로 디버깅에 엄청 고생했던 기억이 새록새록 😅

이번에는 배열의 구조를 바꾸는 주요 메서드들을 정리하고, 원본 변경 여부, 성능, 주의사항을 꼼꼼히 정리해 보겠습니다.

sort

  • 배열을 정렬하며, 기본적으로 문자열 기준으로 정렬하므로 숫자 정렬 시 비교 함수가 필요합니다.

  • 기본 정렬은 문자열 기준 (Unicode code)
  • 숫자 정렬 시 반드시 비교 함수를 제공해야 함

⭕️ 원본 배열을 변경

const arr = [10, 2, 30];
arr.sort(); // [10, 2, 30] → ['10', '2', '30'] → 정렬 이상함!

arr.sort((a, b) => a - b); // [2, 10, 30]

reverse

  • 배열의 순서를 반대로 뒤집습니다.

⭕️ 원본 배열을 변경

const arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]

slice

  • 배열의 일부를 추출하여 새로운 배열로 반환합니다.

  • 시작 인덱스 포함, 끝 인덱스는 제외
  • 음수 인덱스 사용 가능: slice(-2) → 끝에서 2개

☑️ 원본 배열 유지

const arr = [1, 2, 3, 4];
const part = arr.slice(1, 3); // [2, 3]

splice

  • 배열의 일부를 삭제하거나 추가합니다.

  • 첫 번째 인자: 시작 인덱스
  • 두 번째 인자: 삭제할 개수
  • 세 번째 이후: 삽입할 요소들

⭕️ 원본 배열을 변경

const arr = [1, 2, 3, 4];
const removed = arr.splice(1, 2); // [2, 3], arr → [1, 4]

arr.splice(1, 0, 'a'); // [1, 'a', 4]

concat

  • 여러 배열이나 값을 병합하여 새로운 배열을 반환합니다.

  • 여러 배열/값을 이어 붙일 수 있음

☑️ 원본 배열 유지

const a = [1, 2];
const b = [3, 4];
const c = a.concat(b); // [1, 2, 3, 4]

flat

  • 중첩된 배열을 지정한 깊이까지 평탄화하여 새로운 배열을 반환합니다.

  • 기본 depth는 1
  • Infinity를 주면 모든 깊이를 평탄화

☑️ 원본 배열 유지

const arr = [1, [2, [3]]];
arr.flat(); // [1, 2, [3]]
arr.flat(2); // [1, 2, 3]

flatMap

  • map을 수행한 후 그 결과를 1단계 평탄화한 새로운 배열을 반환합니다.

  • map().flat(1)과 같지만 더 효율적이고 많이 사용

☑️ 원본 배열 유지

const arr = [1, 2, 3];
const result = arr.flatMap(x => [x, x * 2]);
// [1, 2, 2, 4, 3, 6]

원본 변경 여부 총 정리 ✏️

메서드 원본 변경 여부 설명
sort() O 기본 문자열 정렬, 숫자 정렬 시 compare 필요
reverse() O 배열 순서 뒤집기
slice() X 일부 추출, 새 배열 반환
splice() O 일부 제거/추가
concat() X 배열 병합, 새 배열 반환
flat() X 중첩 배열 평탄화
flatMap() X map + flat(1)

Non-destructive 변경 메서드 (ES2023)

ES2023에서는 원본 배열을 변경하지 않으면서 정렬, 뒤집기, 특정 요소 교체를 수행하는 새로운 메서드들이 추가되었습니다. 🎊 이들은 기존의 sort(), reverse(), splice()와 기능은 유사하지만 항상 새로운 배열을 반환하여 불변성을 유지하는 데 도움이 됩니다. 정말 세상 좋아졌어요 😊

toReversed

  • 배열의 순서를 뒤집은 새로운 배열을 반환합니다. 원본 배열은 변경되지 않습니다.
const arr = [1, 2, 3];
const reversedArr = arr.toReversed(); // [3, 2, 1]
console.log(arr);       // [1, 2, 3] (원본 유지)

toSorted

  • 배열을 정렬한 새로운 배열을 반환합니다. sort()와 동일하게 비교 함수를 인자로 받을 수 있으며, 원본 배열은 변경되지 않습니다.
const arr = [10, 2, 30];
const sortedArr = arr.toSorted((a, b) => a - b); // [2, 10, 30]
console.log(arr);      // [10, 2, 30] (원본 유지)

toSpliced

  • splice()와 동일한 인자를 받아 배열의 일부를 삭제하거나 추가한 새로운 배열을 반환합니다. 원본 배열은 변경되지 않습니다.
const arr = [1, 2, 3, 4];
const splicedArr1 = arr.toSpliced(1, 2); // [1, 4]
console.log(arr);         // [1, 2, 3, 4] (원본 유지)

const splicedArr2 = arr.toSpliced(1, 0, 'a'); // [1, 'a', 2, 3, 4]
console.log(arr);         // [1, 2, 3, 4] (원본 유지)
  1. toString(): 배열을 문자열로 변환 toString(): 배열의 모든 요소를 쉼표로 연결한 단일 문자열로 변환하여 반환합니다. 원본 배열은 변경되지 않습니다.
const arr = [1, 2, 3];
const str = arr.toString(); // "1,2,3"
console.log(arr); // [1, 2, 3] (원본 유지)

성능 🚨

  • 대량의 배열을 다룰 땐 불변성을 유지하는 slice와 concat을 선호
  • splice는 상태 관리에서 위험할 수 있음
  • sort는 매번 정렬되는 방식이므로, 자주 정렬하면 성능 저하
  • 단순 배열 복사에는 전개 구문 (…)이 slice()보다 간결하고 성능도 좋음
  • toReversed(), toSorted(), toSpliced() 등 ES2023에 추가된 비파괴적 메서드들은 불변성을 유지해야 하는 최신 개발 환경에서 매우 유용함

배열 구조 변경 시 부작용

배열을 정렬하고 자르고 붙이는 작업은 실무에서 자주 발생합니다. 하지만 원본을 변경하는 메서드를 무심코 사용하면, 불변성이 중요한 상황에서는 큰 문제가 될 수 있습니다. 이번 편을 통해 어떤 메서드가 원본을 유지하고, 어떤 메서드가 파괴적(mutative)인지를 확실히 이해했다면, 실수 없는 배열 조작이 가능해질 것입니다.

배열을 다룰 때 가장 많은 오류는 ‘의도치 않은 원본 변경’에서 시작됩니다.