Original article: JavaScript Array of Objects Tutorial – How to Create, Update, and Loop Through Objects Using JS Array Methods

저는 평균적으로 일주일에 18번 JSON 데이터로 작업합니다. 그럼에도 불구하고 JSON 데이터를 다룰 때마다 구체적인 메서드 사용법을 검색해야 합니다. 이럴 때마다 명쾌한 해답을 주는 안내서가 있다면 얼마나 좋을까요?

이 기사에서는 JavaScript 배열의 기본적인 사용법에 대해 설명하겠습니다.

JSON은 JavaScript Object Notation의 약자이며, JavaScript 객체 문법을 따르는 문자 기반의 데이터 형식입니다. JSON을 활용하여 작업을 해본 적이 있다면 JavaScript 객체를 이미 다뤄본 셈입니다.

객체를 만드는 방법은 무척 간단합니다.

예시:

{
  "color": "purple",
  "type": "minivan",
  "registration": new Date('2012-02-03'),
  "capacity": 7
}

위의 객체는 자동차를 나타냅니다. 여러 종류와 색상의 자동차가 존재할 수 있고, 각각의 객체는 각기 다른 자동차를 나타냅니다.

객체를 상징하는 보라색 차

대부분의 경우 외부 서비스에 보낸 요청에 대한 응답값으로 객체 데이터를 받아옵니다. 그러나 객체와 해당 객체가 들어있는 배열을 직접 만들어야 할 때도 있습니다. 제가 이 온라인 샵을 만들었던 때처럼요.

다양한 음식들이 나열되어 있는 메뉴의 웹사이트

만일 리스트의 모든 항목이 다음과 같은 HTML로 각각 작성되어야 한다면:

메뉴 중 하나의 항목의 데이터가 담긴 HTML 코드 블록

위의 코드를 수동으로 12번 반복하는 방법은 매우 번거롭고 유지보수를 어렵게 만들 것입니다.

객체가 들어있는 배열 생성하기

자동차 객체 이야기로 잠시 돌아가 봅시다. 아래의 자동차 세트를 살펴볼까요.

일렬로 나열된 객체를 상징하는 다양한 색깔의 자동차들

위의 자동차 세트를 다음과 같은 배열로 표현할 수 있습니다.

let cars = [
  {
    "color": "purple",
    "type": "minivan",
    "registration": new Date('2017-01-03'),
    "capacity": 7
  },
  {
    "color": "red",
    "type": "station wagon",
    "registration": new Date('2018-03-03'),
    "capacity": 5
  },
  {
    ...
  },
  ...
]

배열을 조작해야 하는 일이 자주 발생하기 때문에 일반적으로 배열의 모양은 변화를 거듭합니다. 먼저 객체가 들어있는 기존의 배열에 새로운 객체를 추가하는 방법을 알아보겠습니다.

Array.unshift: 배열의 앞쪽에 추가하기

새로운 자동차 객체를 배열 맨 앞에 추가하는 이미지

Array.unshift를 사용하면 배열의 맨 앞에 객체를 추가할 수 있습니다.

let car = {
  "color": "red",
  "type": "cabrio",
  "registration": new Date('2016-05-02'),
  "capacity": 2
}
cars.unshift(car);

Array.push: 배열의 뒤쪽에 추가하기

새로운 자동차 객체를 배열 맨 끝에 추가하는 이미지

Array.push를 사용하면 배열의 맨 뒤에 객체를 추가할 수 있습니다.

let car = {
 "color": "red",
 "type": "cabrio",
 "registration": new Date('2016-05-02'),
 "capacity": 2
}
cars.push(car);

Array.splice: 배열의 중간에 추가하기

새로운 자동차 객체를 배열의 특정 인덱스에 추가하는 이미지

Array.splice를 사용하면 배열의 중간에 객체를 추가할 수 있습니다. 기존 요소를 삭제하려 할 때도 이 메서드를 사용할 수 있습니다. 필요한 매개변수는 다음과 같습니다.

Array.splice(
  {배열의 변경을 시작할 인덱스},
  {배열에서 제거할 요소의 수},
  {배열에 추가할 요소}
);

예를 들어, cars 배열의 다섯 번째 위치에 빨간색 폭스바겐 카브리오를 추가하려면 다음처럼 splice를 사용하면 됩니다.

let car = {
  "color": "red",
  "type": "cabrio",
  "registration": new Date('2016-05-02'),
  "capacity": 2
}
cars.splice(4, 0, car);

객체 배열을 통한 루프

여기서 짚고 넘어갈 문제가 한 가지 있습니다. 배열의 순회를 필요로 하는 이유는 무엇인가요? 대부분의 경우 배열의 순회는 그저 목적을 달성하기 위한 수단일 뿐 순회 자체가 목적이 되지는 않는다는 것이 질문의 요지입니다.

JavaScript는 순회 로직을 구현하지 않아도 원하는 문제를 해결할 수 있도록 여러 기능을 자체적으로 제공합니다. 이어지는 내용에서 살펴보겠습니다.

Array.find: 배열 내 특정 값을 만족하는 객체 찾기

빨간색 차를 찾고 싶다고 가정하고 Array.find 메서드를 사용해봅시다.

차 객체 배열 중 빨간 차 객체 하나를 찾는 것을 시각화한 이미지

let car = cars.find(car => car.color === "red");

Array.find 메서드는 조건에 일치하는 첫 번째 요소를 반환합니다.

console.log(car);
// 출력된 값:
// {
//   color: 'red',
//   type: 'station wagon',
//   registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//   capacity: 5
// }

한 번에 여러 조건을 만족하는 요소를 검색할 수도 있습니다.

let car = cars.find(car => car.color === "red" && car.type === "cabrio");

이 경우에는 위의 배열 중 가장 마지막 차를 반환하게 됩니다.

Array.filter: 특정 조건을 만족하는 모든 객체 찾기

Array.find 함수는 조건을 만족하는 첫 번째 객체만을 반환합니다. 만일 위의 배열 안에 있는 빨간 차를 모두 구하려면 Array.filter를 사용해야 합니다.

차 객체 배열 중 빨간 차 객체 모두 찾는 것을 시각화한 이미지

let redCars = cars.filter(car => car.color === "red");
console.log(redCars);
// 출력된 값:
// [
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   {
//     color: 'red',
//     type: 'cabrio',
//     registration: 'Sat Mar 03 2012 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 2
//   }
// ]

Array.map: 배열 내 객체 바꾸기

Array.map를 사용해 기존 배열을 다른 배열로 변형하는 작업은 무척 빈번하게 발생합니다. 이번에는 자동차를 크기에 따라 세 그룹으로 분류해보겠습니다.

여러 색과 크기의 차 객체들을 담은 배열

let sizes = cars.map(car => {
  if (car.capacity <= 3){
    return "small";
  }
  if (car.capacity <= 5){
    return "medium";
  }
  return "large";
});
console.log(sizes);
// 출력된 값:
// ['large','medium','medium', ..., 'small']

더 자세한 정보를 담고 있는 새 객체를 생성할 수도 있습니다.

let carsProperties = cars.map(car => {
 let properties = {
   "capacity": car.capacity,
   "size": "large"
 };
 if (car.capacity <= 5){
   properties['size'] = "medium";
 }
 if (car.capacity <= 3){
   properties['size'] = "small";
 }
 return properties;
});
console.log(carsProperties);
// 출력된 값:
// [
//   { capacity: 7, size: 'large' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 5, size: 'medium' },
//   { capacity: 2, size: 'small' },
//   ...
// ]

Array.forEach: 객체에 새 프로퍼티(property) 추가하기

만약 자동차 크기 값을 추가하고 싶은 상황이라면 어떡해야 할까요? 이 경우에는 size를 객체의 새로운 프로퍼티로 정의할 수 있습니다. Array.forEach 함수의 좋은 활용 사례입니다.

cars.forEach(car => {
 car['size'] = "large";
 if (car.capacity <= 5){
   car['size'] = "medium";
 }
 if (car.capacity <= 3){
   car['size'] = "small";
 }
});

Array.sort: 객체 정렬하기

보통 원하는 형태로 객체를 가공하고 나면 특정 기준에 따른 정렬 작업이 필요하게 됩니다.

일반적으로 배열 내 모든 객체가 가지고 있는 프로퍼티 중 한 가지의 값을 기준으로 정렬이 적용됩니다. 이에 Array.sort 함수를 사용하려면 순서를 정의하는 별도의 함수가 필요합니다.

자동차를 수용 가능 인원에 따라 내림차순으로 정렬해보겠습니다.

차량 수용 가능 인원 순으로 배열 정리를 한 이미지

let sortedCars = cars.sort((c1, c2) => (c1.capacity < c2.capacity) ? 1 : (c1.capacity > c2.capacity) ? -1 : 0);
console.log(sortedCars);
// 출력된 값:
// [
//   {
//     color: 'purple',
//     type: 'minivan',
//     registration: 'Wed Feb 01 2017 00:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 7
//   },
//   {
//     color: 'red',
//     type: 'station wagon',
//     registration: 'Sat Mar 03 2018 01:00:00 GMT+0100 (GMT+01:00)',
//     capacity: 5
//   },
//   ...
// ]

Array.sort 함수는 두 객체를 비교해 정렬 함수의 결과가 양수이면 첫 번째 객체를 두 번째 위치에 놓습니다. 즉 "첫 번째 객체를 두 번째 위치에 놓아야 하나요?"라는 질문을 묻는 것과 같은 셈이지요.

차 객체가 정렬되는 프로세스

불필요한 정렬을 방지하기 위해 비교되는 두 객체의 값이 같을 때 0값이 반환되도록 명시하는 걸 잊지 마세요.

Array.every와 Array.includes: 조건을 충족하는지 확인하기

배열의 객체가 주어진 조건을 충족하는지 확인할 때 Array.every 또는 Array.some를 사용할 수 있습니다. 다음과 같은 질문에 대한 답을 찾기 위해 활용할 수 있습니다.

차 중에 빨간색 카브리오가 있습니까? 모든 차에 최소 4명 이상 탑승할 수 있나요? (또는 좀 더 웹과 관련있는 질문으로) 장바구니에 특정 제품이 들어있습니까?

cars.some(car => car.color === "red" && car.type === "cabrio");
// 출력된 값: true

cars.every(car => car.capacity >= 4);
// 출력된 값: false

Array.includesArray.some과 비슷하지만 원시(primitive) 자료형에서만 작동한다는 중요한 차이점이 있습니다.

마치며

이 기사에서는 객체 배열의 생성, 조작, 변환 및 순회와 관련된 기본 기능들에 대해 설명했습니다. 객체 배열을 다루는 문제 대부분은 위의 메서드를 활용해 해결할 수 있을 거예요.

더 고급 기능을 적용해야 하는 문제가 있을 경우 더 자세한 가이드 [영문]를 살펴보거나 W3 schools 참고 자료를 확인해보세요.

또는 제게 연락해 주신다면 새로운 기사를 준비해보도록 하겠습니다 :-)