우당탕탕 FE 개발자 이야기

[JavaScript] 배열 메서드 완벽 정리 (Array Method) 3탄 - 변환 본문

개발

[JavaScript] 배열 메서드 완벽 정리 (Array Method) 3탄 - 변환

우당탕탕 FE 2025. 12. 3. 16:28

 

1탄에서는 배열 요소 추가/제거를, 2탄에서는 배열 탐색/검색을 다뤘습니다. 이번 3탄에서는 배열을 변환하는 가장 강력하고 자주 사용되는 메서드들을 정리해보겠습니다. 특히 map(), filter(), reduce()는 현대 자바스크립트 개발에서 필수적인 메서드입니다!

1. map() - 각 요소를 변환하여 새 배열 생성

map()은 배열의 모든 요소를 변환하여 새로운 배열을 만듭니다. 원본 배열은 변경되지 않습니다.

 

문법:

array.map(callback(element, index, array))

 

기본 예시:

const numbers = [1, 2, 3, 4, 5];

// 각 숫자를 2배로
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5] (원본 유지)

// 각 숫자를 제곱
const squared = numbers.map(num => num ** 2);
console.log(squared); // [1, 4, 9, 16, 25]

 

실무 활용 예시:

// 1. API 응답 데이터 변환
const users = [
  { id: 1, firstName: '철수', lastName: '김' },
  { id: 2, firstName: '영희', lastName: '이' },
  { id: 3, firstName: '민수', lastName: '박' }
];

// 전체 이름으로 변환
const fullNames = users.map(user => `${user.lastName}${user.firstName}`);
console.log(fullNames); // ['김철수', '이영희', '박민수']

// 특정 속성만 추출
const userIds = users.map(user => user.id);
console.log(userIds); // [1, 2, 3]

// 2. 가격 데이터에 할인 적용
const products = [
  { name: '노트북', price: 1000000 },
  { name: '마우스', price: 30000 },
  { name: '키보드', price: 80000 }
];

// 10% 할인가 계산
const discountedProducts = products.map(product => ({
  ...product,
  originalPrice: product.price,
  discountedPrice: product.price * 0.9
}));

console.log(discountedProducts);
// [
//   { name: '노트북', price: 1000000, originalPrice: 1000000, discountedPrice: 900000 },
//   { name: '마우스', price: 30000, originalPrice: 30000, discountedPrice: 27000 },
//   { name: '키보드', price: 80000, originalPrice: 80000, discountedPrice: 72000 }
// ]

// 3. HTML 요소 생성
const menuItems = ['홈', '소개', '서비스', '연락처'];

const menuHTML = menuItems.map(item => `<li>${item}</li>`).join('');
console.log(menuHTML);
// <li>홈</li><li>소개</li><li>서비스</li><li>연락처</li>

// 4. 날짜 포맷 변환
const dates = ['2024-01-15', '2024-02-20', '2024-03-10'];

const formattedDates = dates.map(date => {
  const [year, month, day] = date.split('-');
  return `${year}년 ${month}월 ${day}일`;
});
console.log(formattedDates);
// ['2024년 01월 15일', '2024년 02월 20일', '2024년 03월 10일']

 

주의사항:

// ❌ map()에서 조건부로 요소 제외하기 (안티패턴)
const numbers = [1, 2, 3, 4, 5];
const result = numbers.map(num => {
  if (num % 2 === 0) return num * 2;
  // 홀수는 undefined가 됨
});
console.log(result); // [undefined, 4, undefined, 8, undefined]

// ✅ 올바른 방법: filter()와 함께 사용
const result2 = numbers
  .filter(num => num % 2 === 0)
  .map(num => num * 2);
console.log(result2); // [4, 8]

2. filter() - 조건에 맞는 요소만 추출

filter()는 주어진 조건을 만족하는 모든 요소로 새로운 배열을 만듭니다.

 

문법:

array.filter(callback(element, index, array))

 

기본 예시:

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 짝수만 필터링
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]

// 5보다 큰 숫자만
const largeNumbers = numbers.filter(num => num > 5);
console.log(largeNumbers); // [6, 7, 8, 9, 10]

 

실무 활용 예시:

// 1. 검색 기능 구현
const products = [
  { id: 1, name: '노트북', category: 'electronics', price: 1000000 },
  { id: 2, name: '마우스', category: 'electronics', price: 30000 },
  { id: 3, name: '책상', category: 'furniture', price: 150000 },
  { id: 4, name: '의자', category: 'furniture', price: 80000 }
];

// 카테고리별 필터링
const electronics = products.filter(p => p.category === 'electronics');
console.log(electronics);

// 가격대 필터링
const affordable = products.filter(p => p.price <= 100000);
console.log(affordable);

// 여러 조건 조합
const cheapElectronics = products.filter(p => 
  p.category === 'electronics' && p.price < 50000
);
console.log(cheapElectronics); // [{ id: 2, name: '마우스', ... }]

// 2. 활성 사용자만 추출
const users = [
  { id: 1, name: '김철수', active: true, age: 25 },
  { id: 2, name: '이영희', active: false, age: 30 },
  { id: 3, name: '박민수', active: true, age: 28 }
];

const activeUsers = users.filter(user => user.active);
console.log(activeUsers); // 활성 사용자만

// 성인이면서 활성 사용자
const activeAdults = users.filter(user => user.active && user.age >= 18);
console.log(activeAdults);

// 3. 중복 제거 (unique values)
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = numbers.filter((num, index, arr) => 
  arr.indexOf(num) === index
);
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]

// 4. 빈 값 제거
const inputs = ['hello', '', 'world', null, 'test', undefined, ''];
const validInputs = inputs.filter(input => input);
console.log(validInputs); // ['hello', 'world', 'test']

// 더 엄격한 검증
const cleanInputs = inputs.filter(input => 
  input !== null && input !== undefined && input !== ''
);
console.log(cleanInputs); // ['hello', 'world', 'test']

 

map()과 filter() 함께 사용하기:

const users = [
  { name: '김철수', age: 17, city: '서울' },
  { name: '이영희', age: 25, city: '부산' },
  { name: '박민수', age: 30, city: '서울' },
  { name: '최지은', age: 22, city: '대구' }
];

// 서울에 사는 성인의 이름만 추출
const seoulAdultNames = users
  .filter(user => user.city === '서울' && user.age >= 18)
  .map(user => user.name);

console.log(seoulAdultNames); // ['박민수']

3. reduce() - 배열을 하나의 값으로 축약

reduce()는 배열의 모든 요소를 순회하며 하나의 값으로 축약(reduce)합니다. 가장 강력하지만 처음에는 어려울 수 있는 메서드입니다.

 

문법:

array.reduce(callback(accumulator, currentValue, index, array), initialValue)
  • accumulator: 누적값
  • currentValue: 현재 처리 중인 요소
  • initialValue: 초기값 (선택사항이지만 항상 제공하는 것을 권장)

기본 예시:

const numbers = [1, 2, 3, 4, 5];

// 합계 구하기
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 15

// 동작 과정:
// 1회: acc = 0, num = 1 → 0 + 1 = 1
// 2회: acc = 1, num = 2 → 1 + 2 = 3
// 3회: acc = 3, num = 3 → 3 + 3 = 6
// 4회: acc = 6, num = 4 → 6 + 4 = 10
// 5회: acc = 10, num = 5 → 10 + 5 = 15

// 곱하기
const product = numbers.reduce((acc, num) => acc * num, 1);
console.log(product); // 120

// 최댓값 찾기
const max = numbers.reduce((acc, num) => Math.max(acc, num));
console.log(max); // 5

 

실무 활용 예시:

// 1. 장바구니 총액 계산
const cart = [
  { name: '노트북', price: 1000000, quantity: 1 },
  { name: '마우스', price: 30000, quantity: 2 },
  { name: '키보드', price: 80000, quantity: 1 }
];

const totalPrice = cart.reduce((total, item) => 
  total + (item.price * item.quantity), 0
);
console.log(totalPrice); // 1,140,000

// 2. 배열을 객체로 변환
const users = [
  { id: 1, name: '김철수' },
  { id: 2, name: '이영희' },
  { id: 3, name: '박민수' }
];

const userMap = users.reduce((acc, user) => {
  acc[user.id] = user.name;
  return acc;
}, {});

console.log(userMap);
// { 1: '김철수', 2: '이영희', 3: '박민수' }

// 3. 그룹화 (groupBy)
const students = [
  { name: '철수', grade: 'A' },
  { name: '영희', grade: 'B' },
  { name: '민수', grade: 'A' },
  { name: '지은', grade: 'C' },
  { name: '준호', grade: 'B' }
];

const groupedByGrade = students.reduce((acc, student) => {
  if (!acc[student.grade]) {
    acc[student.grade] = [];
  }
  acc[student.grade].push(student.name);
  return acc;
}, {});

console.log(groupedByGrade);
// {
//   A: ['철수', '민수'],
//   B: ['영희', '준호'],
//   C: ['지은']
// }

// 4. 배열 평탄화 (flatten)
const nested = [[1, 2], [3, 4], [5, 6]];

const flattened = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flattened); // [1, 2, 3, 4, 5, 6]

// 5. 카운팅
const fruits = ['사과', '바나나', '사과', '오렌지', '바나나', '사과'];

const fruitCount = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(fruitCount);
// { 사과: 3, 바나나: 2, 오렌지: 1 }

// 6. 파이프라인 구성 (여러 작업을 순차적으로)
const data = [1, 2, 3, 4, 5];

const result = data.reduce((acc, num) => {
  // 짝수만 필터링하고 2배로 만든 후 합계
  if (num % 2 === 0) {
    return acc + (num * 2);
  }
  return acc;
}, 0);

console.log(result); // (2*2) + (4*2) = 4 + 8 = 12

// 7. 고유값 추출 (unique values)
const numbers = [1, 2, 2, 3, 4, 4, 5];

const unique = numbers.reduce((acc, num) => {
  if (!acc.includes(num)) {
    acc.push(num);
  }
  return acc;
}, []);

console.log(unique); // [1, 2, 3, 4, 5]

 

reduce()의 고급 활용:

// Promise 순차 실행
const asyncTasks = [task1, task2, task3];

asyncTasks.reduce((promise, task) => 
  promise.then(result => task(result)),
  Promise.resolve()
);

// 깊은 속성 접근
const obj = { a: { b: { c: { d: 'value' } } } };
const path = ['a', 'b', 'c', 'd'];

const value = path.reduce((acc, key) => acc[key], obj);
console.log(value); // 'value'

4. sort() - 배열 정렬

sort()는 배열의 요소를 정렬합니다. 주의: 원본 배열을 직접 수정합니다!

 

문법:

array.sort(compareFunction)

 

기본 예시:

// 문자열 정렬 (기본: 알파벳 순)
const fruits = ['바나나', '사과', '오렌지', '딸기'];
fruits.sort();
console.log(fruits); // ['딸기', '바나나', '사과', '오렌지'] (가나다 순)

// ❌ 숫자 정렬 주의! (기본 sort는 문자열로 변환하여 정렬)
const numbers = [10, 5, 40, 25, 1000, 1];
numbers.sort();
console.log(numbers); // [1, 10, 1000, 25, 40, 5] (문자열 순서!)

// ✅ 올바른 숫자 정렬
const numbers2 = [10, 5, 40, 25, 1000, 1];

// 오름차순
numbers2.sort((a, b) => a - b);
console.log(numbers2); // [1, 5, 10, 25, 40, 1000]

// 내림차순
numbers2.sort((a, b) => b - a);
console.log(numbers2); // [1000, 40, 25, 10, 5, 1]

 

실무 활용 예시:

// 1. 객체 배열 정렬
const products = [
  { name: '노트북', price: 1000000 },
  { name: '마우스', price: 30000 },
  { name: '키보드', price: 80000 },
  { name: '모니터', price: 300000 }
];

// 가격 오름차순
products.sort((a, b) => a.price - b.price);
console.log(products);

// 가격 내림차순
products.sort((a, b) => b.price - a.price);

// 이름 가나다 순
products.sort((a, b) => a.name.localeCompare(b.name));
console.log(products);

// 2. 날짜 정렬
const events = [
  { name: '회의', date: new Date('2024-03-15') },
  { name: '발표', date: new Date('2024-01-20') },
  { name: '워크샵', date: new Date('2024-02-10') }
];

// 날짜 오름차순 (가장 빠른 날짜부터)
events.sort((a, b) => a.date - b.date);
console.log(events);

// 3. 다중 조건 정렬
const students = [
  { name: '철수', grade: 'A', score: 95 },
  { name: '영희', grade: 'B', score: 85 },
  { name: '민수', grade: 'A', score: 90 },
  { name: '지은', grade: 'B', score: 88 }
];

// 등급순으로 정렬하고, 같은 등급이면 점수순
students.sort((a, b) => {
  if (a.grade === b.grade) {
    return b.score - a.score; // 점수 내림차순
  }
  return a.grade.localeCompare(b.grade); // 등급 오름차순
});
console.log(students);

// 4. 원본 유지하면서 정렬 (불변성)
const original = [3, 1, 4, 1, 5];

// 스프레드 연산자 사용
const sorted1 = [...original].sort((a, b) => a - b);

// slice() 사용
const sorted2 = original.slice().sort((a, b) => a - b);

console.log(original); // [3, 1, 4, 1, 5] (원본 유지)
console.log(sorted1);  // [1, 1, 3, 4, 5]

 

한글 정렬:

const names = ['홍길동', '김철수', '이영희', '박민수'];

// localeCompare 사용 (한글 자모 순서 고려)
names.sort((a, b) => a.localeCompare(b, 'ko-KR'));
console.log(names); // ['김철수', '박민수', '이영희', '홍길동']

5. reverse() - 배열 순서 뒤집기

reverse()는 배열의 순서를 반대로 뒤집습니다. 주의: 원본 배열을 직접 수정합니다!

 

기본 예시:

const numbers = [1, 2, 3, 4, 5];
numbers.reverse();
console.log(numbers); // [5, 4, 3, 2, 1]

const fruits = ['사과', '바나나', '오렌지'];
fruits.reverse();
console.log(fruits); // ['오렌지', '바나나', '사과']

 

실무 활용 예시:

// 1. 최신순 정렬
const posts = [
  { id: 1, title: '첫 번째 글', date: '2024-01-01' },
  { id: 2, title: '두 번째 글', date: '2024-01-05' },
  { id: 3, title: '세 번째 글', date: '2024-01-10' }
];

// 날짜 오름차순 정렬 후 reverse로 최신순
const latestPosts = [...posts]
  .sort((a, b) => new Date(a.date) - new Date(b.date))
  .reverse();

console.log(latestPosts);

// 또는 sort에서 바로 내림차순
const latestPosts2 = [...posts]
  .sort((a, b) => new Date(b.date) - new Date(a.date));

// 2. 문자열 뒤집기
const str = 'hello';
const reversed = str.split('').reverse().join('');
console.log(reversed); // 'olleh'

// 3. 원본 유지하면서 reverse
const original = [1, 2, 3, 4, 5];
const reversed2 = [...original].reverse();

console.log(original); // [1, 2, 3, 4, 5] (원본 유지)
console.log(reversed2); // [5, 4, 3, 2, 1]

메서드 체이닝 (Method Chaining)

여러 배열 메서드를 연결하여 강력한 데이터 처리 파이프라인을 만들 수 있습니다.

const orders = [
  { id: 1, product: '노트북', price: 1000000, quantity: 1, status: 'completed' },
  { id: 2, product: '마우스', price: 30000, quantity: 2, status: 'completed' },
  { id: 3, product: '키보드', price: 80000, quantity: 1, status: 'pending' },
  { id: 4, product: '모니터', price: 300000, quantity: 1, status: 'completed' },
  { id: 5, product: '헤드셋', price: 50000, quantity: 3, status: 'cancelled' }
];

// 완료된 주문의 총 매출액 계산
const totalRevenue = orders
  .filter(order => order.status === 'completed')  // 완료된 주문만
  .map(order => order.price * order.quantity)     // 각 주문의 총액 계산
  .reduce((sum, amount) => sum + amount, 0);      // 모두 합산

console.log(totalRevenue); // 1,360,000

// 가격대별 상품 목록 (10만원 이상/미만)
const categorizedProducts = orders
  .map(order => ({
    name: order.product,
    totalPrice: order.price * order.quantity
  }))
  .sort((a, b) => b.totalPrice - a.totalPrice)
  .reduce((acc, item) => {
    const category = item.totalPrice >= 100000 ? 'premium' : 'standard';
    if (!acc[category]) acc[category] = [];
    acc[category].push(item.name);
    return acc;
  }, {});

console.log(categorizedProducts);
// {
//   premium: ['노트북', '모니터', '헤드셋'],
//   standard: ['마우스', '키보드']
// }

 

실전 예제: 데이터 분석

const sales = [
  { date: '2024-01', amount: 1000000, category: 'A' },
  { date: '2024-01', amount: 500000, category: 'B' },
  { date: '2024-02', amount: 1500000, category: 'A' },
  { date: '2024-02', amount: 800000, category: 'B' },
  { date: '2024-03', amount: 1200000, category: 'A' }
];

// 카테고리 A의 월별 평균 매출
const categoryAAverage = sales
  .filter(sale => sale.category === 'A')
  .map(sale => sale.amount)
  .reduce((sum, amount, _, arr) => sum + amount / arr.length, 0);

console.log(categoryAAverage); // 1,233,333

// 월별 총 매출
const monthlyTotal = sales
  .reduce((acc, sale) => {
    if (!acc[sale.date]) acc[sale.date] = 0;
    acc[sale.date] += sale.amount;
    return acc;
  }, {});

console.log(monthlyTotal);
// { '2024-01': 1500000, '2024-02': 2300000, '2024-03': 1200000 }

메서드 비교표

메서드 반환값 원본 변경 용도
map() 새 배열 각 요소를 변환
filter() 새 배열 조건에 맞는 요소만 추출
reduce() 단일 값 배열을 하나의 값으로 축약
sort() 정렬된 배열 배열 정렬
reverse() 뒤집힌 배열 배열 순서 반전

성능 최적화 팁

// ❌ 비효율적: 여러 번 순회
const data = [1, 2, 3, 4, 5];
const result1 = data.filter(n => n % 2 === 0);
const result2 = result1.map(n => n * 2);
const result3 = result2.reduce((sum, n) => sum + n, 0);

// ✅ 효율적: 한 번에 처리
const result = data.reduce((sum, n) => {
  if (n % 2 === 0) {
    return sum + (n * 2);
  }
  return sum;
}, 0);

// 하지만 가독성을 위해 체이닝을 선택할 수도 있음
const result4 = data
  .filter(n => n % 2 === 0)
  .map(n => n * 2)
  .reduce((sum, n) => sum + n, 0);
// 성능보다 코드 가독성이 중요한 경우가 많습니다!

실전 종합 예제

// 전자상거래 대시보드 데이터 처리
const transactions = [
  { id: 1, userId: 101, product: '노트북', amount: 1000000, date: '2024-01-15', status: 'completed' },
  { id: 2, userId: 102, product: '마우스', amount: 30000, date: '2024-01-16', status: 'completed' },
  { id: 3, userId: 101, product: '키보드', amount: 80000, date: '2024-01-17', status: 'completed' },
  { id: 4, userId: 103, product: '모니터', amount: 300000, date: '2024-01-18', status: 'pending' },
  { id: 5, userId: 102, product: '헤드셋', amount: 50000, date: '2024-01-19', status: 'completed' },
  { id: 6, userId: 104, product: '웹캠', amount: 70000, date: '2024-01-20', status: 'cancelled' }
];

// 1. 완료된 거래의 총 매출
const totalRevenue = transactions
  .filter(t => t.status === 'completed')
  .reduce((sum, t) => sum + t.amount, 0);

console.log(`총 매출: ${totalRevenue.toLocaleString()}원`);

// 2. 사용자별 구매 금액
const userPurchases = transactions
  .filter(t => t.status === 'completed')
  .reduce((acc, t) => {
    acc[t.userId] = (acc[t.userId] || 0) + t.amount;
    return acc;
  }, {});

console.log('사용자별 구매 금액:', userPurchases);

// 3. 인기 상품 TOP 3
const productSales = transactions
  .filter(t => t.status === 'completed')
  .reduce((acc, t) => {
    acc[t.product] = (acc[t.product] || 0) + t.amount;
    return acc;
  }, {});

const top3Products = Object.entries(productSales)
  .sort((a, b) => b[1] - a[1])
  .slice(0, 3)
  .map(([product, amount]) => ({ product, amount }));

console.log('인기 상품 TOP 3:', top3Products);

// 4. 일별 거래 통계
const dailyStats = transactions
  .reduce((acc, t) => {
    if (!acc[t.date]) {
      acc[t.date] = {
        totalTransactions: 0,
        totalAmount: 0,
        statuses: {}
      };
    }
    
    acc[t.date].totalTransactions++;
    acc[t.date].totalAmount += t.amount;
    acc[t.date].statuses[t.status] = (acc[t.date].statuses[t.status] || 0) + 1;
    
    return acc;
  }, {});

console.log('일별 통계:', dailyStats);

// 5. VIP 고객 (10만원 이상 구매)
const vipCustomers = transactions
  .filter(t => t.status === 'completed')
  .reduce((acc, t) => {
    acc[t.userId] = (acc[t.userId] || 0) + t.amount;
    return acc;
  }, {})
  .filter(amount => amount >= 100000);

console.log('VIP 고객:', Object.keys(vipCustomers));

마무리

배열을 변환하는 5가지 핵심 메서드를 알아봤습니다!

 

간단 정리:

  • map(): 각 요소를 변환하여 새 배열 생성
  • filter(): 조건에 맞는 요소만 추출
  • reduce(): 배열을 하나의 값으로 축약
  • sort(): 배열 정렬 (원본 변경!)
  • reverse(): 배열 순서 뒤집기 (원본 변경!)

핵심 포인트:

  • map(), filter(), reduce()는 원본을 변경하지 않습니다 (불변성)
  • sort(), reverse()는 원본을 변경합니다 (주의!)
  • 메서드 체이닝으로 강력한 데이터 처리 가능
  • 가독성과 성능 사이의 균형을 고려하세요

이것으로 자바스크립트 배열 메서드 3부작이 완성되었습니다! 🎉


시리즈 전체 보기: