ES2023에서 도입된 자바스크립트의 새로운 배열 복사 메서드
자바스크립트를 사용할때 도움이 되는 배열 객체에 대한 몇가지 새로운 메서드에 대해 알아보자.
toSorted, toReversed, toSpliced, with 메서드를 사용하면, 기존 배열의 데이터를 변경하지 않고 복사본을 만든 뒤 복사본을 변경하여 배열에 대한 연산을 수행한다.
변경과 사이드이펙트
배열 객체에는 항상 몇 가지 특이한 점이 있다. sort, reverse, splice 메서드는 원본 배열을 변경한다. 반면 concat, map, filter와 같은 메서드는 배열의 복사본을 만든 다음 복사본에 대해 연산한다. 객체 자체를 변경시키는 연산을 수행하면 사이드 이펙트가 발생하여 시스템 내에서 예기치 않은 동작이 발생할 수 있다.
배열의 변경과 리액트
원본 배열을 변경하는 메서드를 사용했을 때 발생할 수 있는 문제 중 하나는 리액트 컴포넌트에서 배열을 사용할 때이다. 배열 자체가 동일한 객체이기 때문에, 배열을 변경한 후 새로운 상태로 설정하려고 해도 새 렌더링이 발생하지 않는다. 대신 배열을 먼저 복사한 다음 복사본을 변경하고 이를 새 상태로 설정해야 한다.
먼저 복사하고 변경하기
이 문제를 해결하는 방법은 배열을 먼저 복사한 다음 변경하는 것이다. 배열의 복사본을 만드는 방법에는 Array.from, 스프레드 연산자(...), 인수가 없는 slice함수등 여러가지가 있다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = Array.from(languages).reverse();
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
console.log(Object.is(languages, reversed));
// => false
상태 변경 전 복사를 먼저 수행하는 것은 불편하다.
새로운 메서드는 복사를 이용해 변경한다.
앞서말한 toSorted, toReversed, toSpliced, with 메서드는 원본 배열을 복사하고 복사본을 변경한 후 반환한다. 하나의 함수만 호출하면 되기 때문에 코드를 작성하는 데에도 더 쉬워지고 배열을 먼저 복사할 필요가 없으므로 읽기도 쉬워진다.
Array.prototype.toSorted
toSorted함수는 새롭게 정렬된 배열을 반환한다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const sorted = languages.toSorted();
console.log(sorted);
// => [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
sort함수처럼 toSorted함수도 주의할 점이 있다. 이때 비교함수를 작성하면서 해결할 수 있다.
const numbers = [5, 3, 10, 7, 1];
const sorted = numbers.toSorted();
console.log(sorted);
// => [ 1, 10, 3, 5, 7 ]
const sortedCorrectly = numbers.toSorted((a, b) => a - b);
console.log(sortedCorrectly);
// => [ 1, 3, 5, 7, 10 ]
const strings = ["abc", "äbc", "def"];
const sorted = strings.toSorted();
console.log(sorted);
// => [ 'abc', 'def', 'äbc' ]
const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b));
console.log(sortedCorrectly);
// => [ 'abc', 'äbc', 'def' ]
Array.prototype.toReversed
toReversed함수를 사용하면 순서가 반전된 새 배열이 반환된다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = languages.toReversed();
console.log(reversed);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
reverse를 사용해 새 변수에 할당하는 것은 원래의 배열도 변경되었기 때문에 오류가 발생할 가능성이 있다. 이때 toReversed를 사용할 수 있다.
Array.prototype.toSpliced
toSpliced함수는 기존의 splice와 조금 다르다. splice는 제공된 인덱스 값에 요소를 삭제하고 추가하며 기존 배열을 변경하고 삭제된 요소를 포함하는 배열을 반환하지만, toSpliced 함수는 제거된 요소 없이 추가된 요소만 포함하여 새로운 배열을 반환한다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]
Array.prototype.with
with함수는 배열의 한 요소를 변경하기 위해 대괄호 표기법을 사용하는 것과 같은 복사기능이다.
기존은 아래와 같다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
languages[2] = "WebAssembly";
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
배열을 복사하고 요소를 변경할 수 있다.
const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const updated = languages.with(2, "WebAssembly");
console.log(updated);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', CoffeeScript' ]