요즘 지인에게 선물로 받은 '자바스크립트 코딩의 기술'이란 책을 재밌게 읽고있다. 이 책을 통해 평소에 간과했던 메소드라던가 기본적인 개념에 대해 다시한번 짚고 넘어가고, 여러가지 좋은 팁들을 알 수 있었는데, 이 글은 책에서 인상깊게 읽었던 파트 중 하나인 전개 연산자의 예제들과 팁들을 정리한 글이다.
배열은 데이터를 다룰 때 엄청난 수준의 유연성을 제공하지만, 수많은 메서드를 인해 조작 시 예상하지못한 문제를 겪을 수 있다.
그러나 ES6+의 전개 연산자 (블로그 정리글)를 잘 활용 한다면 짧은 코드와 훌륭한 가독성을 모두 잡을 수 있다.
배열이나 객체같은 데이터의 컬렉션을 직접적으로 추가하거나 삭제하는 방법은 자칫하면 골치 아픈 일을 만들어낼 수 있다고 한다. 물론 항상 문제가 생기는건 아니지만 잠재적으로 문제가 생길 수 있기때문에 가능하면 직접적인 조작은 피하는것이 좋다. 또한 모던 자바스크립트는 대부분 함수형 프로그래밍 형식을 취하기 때문에 Side-effect와 조작이 없는 코드를 작성해야한다.
배열을 예로들어 생각해보자. 우리가 배열을 조작할때는 주로 push()
메서드를 사용한다. push()
메서드는 새로운 항목을 기존 배열 뒤에 추가하여 원본 배열을 변경한다. 즉, 항목을 추가하면 원본 배열을 조작하는 셈이다. 이는 위에서 말한것처럼 잠재적으로 문제가 생길 수 있기때문에 전개연산자를 통해 이를 방지 해보자.
const cart = [
{
name: "The Foundation Trilogy",
price: 19.99,
discount: false,
},
{
name: "Godel, Escher, Bach",
price: 15.99,
discount: false,
},
{
name: "Red Mars",
price: 5.99,
discount: true,
},
];
const reward = {
name: "Guide to Science Fiction",
discount: true,
price: 0,
};
function addFreeGift(cart) {
if (cart.length > 2) {
cart.push(reward);
return cart;
}
return cart;
}
function summarizeCart(cart) {
const discountable = cart.filter((item) => item.discount);
const cartWithReward = addFreeGift(cart);
if (discountable.length > 1) {
return {
error: "할인 상품은 하나만 주문할 수 있습니다.",
};
}
return {
discounts: discountable.length,
items: cartWithReward.length,
cart: cartWithReward,
};
}
const newArr = summarizeCart(cart);
console.log(newArr);
/*{
discounts: 1,
items: 4,
cart: [
{ name: 'The Foundation Trilogy', price: 19.99, discount: false },
{ name: 'Godel, Escher, Bach', price: 15.99, discount: false },
{ name: 'Red Mars', price: 5.99, discount: true },
{ name: 'Guide to Science Fiction', discount: true, price: 0 }
]
}
*/
위 예제는 전개 연산자가 아닌, push() 메서드를 이용한 예제이다. 장바구니 상품 목록을 받아서 할인(discount)상품이 두개 이상이면
error: "할인 상품은 하나만 주문할 수 있습니다."
라는 객체를 반환하고, 오류 없이, 구매한 상품이 2개 이상이라면,reward
라는 사은품 객체를 장바구니에push()
하여 사은품이 포함된 장바구니 객체를 반환한다.
물론 작동엔 아무런 이상이 없지만, 만일 다른 개발자가 코드를 정리하기위해 모든 변수를 함수의 상단으로 옮기게 된다면 에러가 발생한다.
function summarizeCart(cart) {
const cartWithReward = addFreeGift(cart);
const discountable = cart.filter((item) => item.discount);
if (discountable.length > 1) {
return {
error: "할인 상품은 하나만 주문할 수 있습니다.",
};
}
return {
discounts: discountable.length,
items: cartWithReward.length,
cart: cartWithReward,
};
}
const newArr = summarizeCart(cart);
console.log(newArr);
// { error: '할인 상품은 하나만 주문할 수 있습니다.' }
함수
addFreeGift
를 사용하면 배열 cart를 직접적으로 조작하게된다.cartWithReward
란 변수에 사은품이 추가된 배열을 할당하더라도, 원본 배열이 이미 조작된 후이기에 상품을 세가지 이상 선택하고 그중 하나가 할인상품인 고객 모두에게 오류가 발생하게된다.
위 예제의 문제는 분리된 함수에서 의도치않게 원본을 조작한것이 원인이다. 함수를 호출할때는 함수에 전달한 값을 변경하지않을 것이라는 신뢰가 필요한데, Side-effect 없는 함수를 순수 함수라고한다.
순수 함수는 어떠한 Side-effect(부수효과)도 만들어 내지않는다. 즉, 순수함수는 어떤 외부 상태도 변환하지 않는 다는 것을 의미한다. 그렇기에 활용이 쉽고 리팩토링 및 재구성에 있어서 많은 이점이 있다.
function addFreeGift(cart) {
if (cart.length > 2) {
let addCart = { ...cart, reward };
return addCart;
}
return addCart;
}
function summarizeCart(cart) {
const cartWithReward = addFreeGift(cart);
const discountable = cart.filter((item) => item.discount);
if (discountable.length > 1) {
return {
error: "할인 상품은 하나만 주문할 수 있습니다.",
};
}
return {
discounts: discountable.length,
items: cartWithReward.length,
cart: cartWithReward,
};
}
const newArr = summarizeCart(cart);
console.log(newArr);
/*
{
discounts: 1,
items: undefined,
cart: {
'0': { name: 'The Foundation Trilogy', price: 19.99, discount: false },
'1': { name: 'Godel, Escher, Bach', price: 15.99, discount: false },
'2': { name: 'Red Mars', price: 5.99, discount: true },
reward: { name: 'Guide to Science Fiction', discount: true, price: 0 }
}
}*/
이러한 문제를 해결하기위해 addFreeGift
함수의 addCart란 변수에다가 전개연산자를 이용하여, 기존 객체를 복사한뒤 새로운 객체를 추가해 주었다. 이는 새로운 배열을 생성하기때문에, 기존의 원본 배열은 안전해진다.