주요 포인트 한눈에 보기
배열(Array)은 장바구니, 검색 결과, 태그 목록처럼 “화면에 반복 렌더링되는 목록”의 출발점입니다. 이 문서는 배열을 만들고(생성), 원본을 지키고(복사), 필요한 것만 골라 쓰는(메서드) 흐름을 예제로 빠르게 정리합니다. reduce()는 활용 패턴이 많아 별도 글로 분리했으니, 아래 링크 섹션을 참고해 주세요.
- 배열 개념과 특징
- 배열 생성·복사·구조 분해
- 추가·삭제·수정: 원본 변경 메서드
- 순회·검색·검증 메서드
- 변환·가공 메서드
- 정렬·역순·부분 변경: 비파괴 메서드
- 배열 메서드 정리표 (2행)
- reduce 알아보기
- 결론
- FAQ (면접 질문)
배열 개념과 특징
배열은 “순서가 있는 목록”입니다. 인덱스와 length만 익히면, 대부분의 배열 코드를 읽기 시작할 수 있습니다.
const cartItems = ['apple', 'banana', 'peach'];
console.log(cartItems[0]); // 'apple' (첫 번째)
console.log(cartItems[cartItems.length - 1]); // 'peach' (마지막)
console.log(cartItems.length); // 3 (총 개수)
동작 설명
배열은 “번호가 붙은 목록”입니다. cartItems[0]처럼 번호(인덱스)로 바로 꺼낼 수 있고, length로는 “몇 개가 들어있는지”를 확인합니다.
초보자를 위한 포인트!
- 인덱스는 0부터 시작합니다. (0번째가 첫 번째)
- 마지막 값은
arr[arr.length - 1]로 자주 꺼냅니다. - 배열은 참조 타입이라
const b = a는 복사가 아닙니다. (복사는 아래 섹션)
배열 생성·복사·구조 분해
이 파트는 어렵게 외우지 않으셔도 됩니다. “만들기 → 복사하기 → 꺼내기”만 예시로 바로 확인하시면 됩니다.
배열 만들기
const arr = [10, 20, 30];
console.log(arr);
[]로 만들면 끝입니다. 값이 순서대로 들어가고, 그 순서대로 꺼내 쓰는 “목록”이 됩니다.
배열 복사 (원본 보호)
const origin = [1, 2, 3];
const alias = origin; // 복사 아님(같은 배열 공유)
const copy = [...origin]; // 새 배열(복사)
alias.push(4);
console.log(origin); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3]
alias = origin은 “복사”가 아니라 “같은 배열을 같이 보는 것”입니다. 원본을 지키려면 [...origin]처럼 새 배열을 만들어 작업하시면 됩니다.
객체가 들어있을 때 주의
const users = [{ id: 1 }, { id: 2 }];
const copied = [...users];
copied[0].id = 999;
console.log(users[0].id); // 999
배열은 새로 만들었지만, 안의 객체는 공유될 수 있습니다. 그래서 복사본에서 객체 속성을 바꾸면 원본 쪽에도 영향이 갈 수 있습니다.
Array.from으로 배열로 변환
function demo() {
const realArray = Array.from(arguments);
return realArray.map((v) => String(v));
}
console.log(demo(1, 2, 3)); // ['1','2','3']
arguments처럼 배열처럼 보이지만 배열 메서드 사용이 애매한 값이 있습니다. 이럴 땐 Array.from()으로 배열로 바꾼 다음 쓰시면 됩니다.
구조 분해로 값 꺼내기
const point = [10, 20];
const [x, y] = point;
console.log(x); // 10
console.log(y); // 20
인덱스를 여러 번 쓰지 않고도, 필요한 값만 변수로 바로 꺼낼 수 있습니다.
추가·삭제·수정: 원본 변경 메서드
이 파트의 메서드는 배열 “원본”을 직접 바꿉니다. 빠르고 편하지만, 같은 배열을 여러 곳에서 공유하면 예상치 못한 결과가 생길 수 있어 주의가 필요합니다.
push / pop (끝에 추가·삭제)
const cart = ['apple'];
cart.push('banana');
cart.pop();
console.log(cart); // ['apple']
이 메서드들은 “원본 배열을 직접 바꿉니다”. 로컬 임시 배열을 만들 때는 편하지만, 공유 데이터에는 조심하셔야 합니다.
splice (중간 삭제/삽입)
const list = ['a', 'b', 'c', 'd'];
list.splice(1, 2, 'x');
console.log(list); // ['a', 'x', 'd']
splice는 중간을 잘라내거나 바꾸는 데 강력하지만, 원본이 바뀌기 때문에 “원본 유지가 필요한 곳”에서는 주의가 필요합니다.
sort (원본 변경) + 안전한 정렬
const scores = [3, 1, 2];
const sorted1 = [...scores].sort((a, b) => a - b);
console.log(scores); // [3, 1, 2]
console.log(sorted1); // [1, 2, 3]
sort()는 원본을 바꿉니다. 그래서 안전하게 정렬하려면 [...scores]로 복사 후 정렬하는 방식이 가장 흔합니다.
순회·검색·검증 메서드
배열에서 “찾기/확인하기”를 담당하는 메서드들입니다. 무엇이 있는지 확인하거나, 조건을 만족하는 값을 찾고, 필요하면 중간에 멈추는 패턴까지 함께 익힙니다.
includes / indexOf (있나? 어디 있나?)
const tags = ['js', 'ts', 'react'];
console.log(tags.includes('ts')); // true
console.log(tags.indexOf('react')); // 2
console.log(tags.indexOf('next')); // -1
includes는 “있는지”만 필요할 때 가장 깔끔합니다. indexOf는 “몇 번째인지”가 필요할 때 씁니다. 없으면 -1이 나옵니다.
find / findIndex (첫 번째로 찾기)
const users = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
];
const u = users.find((x) => x.id === 2);
const idx = users.findIndex((x) => x.id === 2);
console.log(u); // { id: 2, name: 'B' }
console.log(idx); // 1
find는 “요소 자체”를, findIndex는 “위치(인덱스)”를 반환합니다. 찾자마자 멈추기 때문에 “첫 번째만 필요할 때” 좋습니다.
forEach는 중단이 안 됩니다
const nums = [1, 2, 3, 4];
nums.forEach((n) => {
if (n === 3) return; // 반복 중단이 아님 (그냥 이번 콜백만 종료)
console.log(n);
});
forEach는 중간에 멈출 수 없습니다. “찾으면 멈추기”가 필요하면 아래처럼 some 또는 find를 쓰는 편이 안전합니다.
some으로 “찾으면 멈추기”
const nums = [1, 2, 3, 4];
nums.some((n) => {
if (n === 3) return true; // true면 즉시 중단
console.log(n);
return false;
});
some은 콜백이 true를 반환하는 순간 즉시 멈춥니다. “조건을 만족하는 게 하나라도 있나요?” 같은 질문에 특히 잘 맞습니다.
변환·가공 메서드
원본은 그대로 두고, 새 배열을 만들어 “모양을 바꾸거나(map)” “필요한 것만 남기는(filter)” 작업을 합니다. 화면에 뿌릴 데이터를 만들 때 가장 자주 쓰입니다.
map / filter (변환 vs 추출)
const nums = [1, 2, 3, 4];
const doubled = nums.map((n) => n * 2);
const evens = nums.filter((n) => n % 2 === 0);
console.log(doubled); // [2, 4, 6, 8]
console.log(evens); // [2, 4]
map은 “모양 바꾸기(변환)”, filter는 “조건에 맞는 것만 남기기(추출)”입니다. 둘 다 새 배열을 만들어 원본은 그대로 둡니다.
flat / flatMap (중첩을 펴기)
const nested = [[1, 2], [3, 4]];
console.log(nested.flat()); // [1, 2, 3, 4]
const words = ['hi', 'ok'];
console.log(words.flatMap((w) => w.split(''))); // ['h','i','o','k']
flat은 중첩 배열을 펴고, flatMap은 “map + flat”을 한 번에 처리합니다.
오류 예제: map에서 push 쓰기
const nums = [1, 2, 3];
const out = [];
nums.map((n) => {
out.push(n * 2);
});
console.log(out); // [2, 4, 6]
map은 “반환값으로 새 배열을 만드는 도구”인데, 위처럼 외부 배열에 push하면 의도가 흐려집니다.
개선 예제: map은 반환으로 끝내기
const nums = [1, 2, 3];
const out = nums.map((n) => n * 2);
console.log(out); // [2, 4, 6]
같은 결과라도 “의도가 보이게” 쓰면 코드가 훨씬 읽기 쉬워집니다.
정렬·역순·부분 변경: 비파괴 메서드
정렬·역순·부분 교체가 필요해도, 원본을 지키고 새 결과를 얻는 방법입니다. 상태 관리나 렌더링 데이터처럼 “원본 보호”가 중요한 곳에서 특히 유용합니다.
toSorted / toReversed (원본 유지)
const a = [3, 1, 2];
const sorted = a.toSorted((x, y) => x - y);
const reversed = a.toReversed();
console.log(a); // [3, 1, 2]
console.log(sorted); // [1, 2, 3]
console.log(reversed); // [2, 1, 3]
이 메서드들은 원본을 바꾸지 않고, 결과를 새 배열로 반환합니다. “원본 유지가 중요한 코드”에서 특히 안전합니다. 구형 환경을 고려해야 한다면 [...a].sort() 같은 방식으로 대체하는 편이 무난합니다.
toSpliced / with (부분 수정도 안전하게)
const items = ['a', 'b', 'c', 'd'];
const removed = items.toSpliced(1, 2); // 1번부터 2개 제거한 새 배열
const replaced = items.with(1, 'x'); // 1번 값을 바꾼 새 배열
console.log(items); // ['a', 'b', 'c', 'd']
console.log(removed); // ['a', 'd']
console.log(replaced); // ['a', 'x', 'c', 'd']
중간 삭제/교체가 필요해도 원본을 보존할 수 있습니다. splice와 달리 “원본을 지키는” 점이 핵심입니다.
배열 메서드 정리표 (2행)
모바일에서 깨지지 않도록 2행으로만 정리했습니다. 원본을 바꾸는 메서드와, 원본을 유지하는 메서드를 빠르게 구분하는 용도입니다.
| 구분 | 대표 메서드 | 한 줄 요약 |
|---|---|---|
| 원본 변경(주의) | push, pop, splice, sort, reverse | 배열 자체가 바뀜 (공유 데이터면 버그 원인) |
| 비변경/비파괴(추천) | map, filter, slice, concat, toSorted, toReversed, toSpliced, with | 원본 유지 + 새 결과를 반환 (상태/렌더링에 안전) |
reduce 알아보기
reduce()는 활용 패턴이 많아서 따로 자세히 정리해두었습니다. 필요하실 때 아래 링크를 참고해 주세요.
[JavaScript] reduce – 무조건 이해되는 자바스크립트 (전체 가이드)
결론
배열은 결국 “목록을 화면에 쓰기 좋은 형태로 만들기” 위한 도구입니다. 그래서 실무에서는 문법을 외우기보다, 어떤 메서드를 쓰면 의도가 잘 보이는지를 기준으로 선택하시는 편이 훨씬 빠르게 늘어납니다.
- 찾기/확인: 하나만 찾으면
find, 여러 개면filter, 존재 여부면includes/some을 우선 떠올립니다. - 가공: 값 모양을 바꾸면
map, 조건으로 걸러내면filter를 기본으로 잡습니다. - 원본 변경 주의:
splice,sort,reverse는 원본을 바꾸므로 공유 데이터에서는 특히 조심합니다. - 안전한 업데이트: 원본을 지키고 싶으면 복사 후 작업(
[...arr].sort())이나 비파괴 메서드(toSorted,toSpliced,with)를 고려합니다.
FAQ (면접 질문)
Q. map과 forEach의 차이와 선택 기준을 말해보세요.
map은 “새 배열을 만들 때” 쓰고, 콜백의 반환값이 새 배열의 요소가 됩니다. forEach는 “그냥 실행” 용도라 반환값이 의미 없고, 중간에 멈추기도 어렵습니다.
Q. find와 filter는 결과가 어떻게 다르며, 언제 각각 쓰나요?
find는 조건을 만족하는 “첫 번째 요소 1개”를 반환하고 못 찾으면 undefined입니다. filter는 조건을 만족하는 “전부”를 모아 “새 배열”로 반환합니다.
Q. sort나 splice가 실무에서 위험하다고 하는 이유는 무엇인가요?
둘 다 원본 배열을 바꿉니다. 같은 배열을 다른 화면/로직이 공유하고 있으면, 한 번의 정렬·삭제가 다른 곳 결과까지 바꾸면서 버그가 나기 쉽습니다. 그래서 보통 “복사 후 작업”을 합니다.
Q. includes와 indexOf는 어떤 차이가 있나요?
includes는 “있는지(불리언)”만 필요할 때 가장 읽기 쉽습니다. indexOf는 “몇 번째인지(숫자)”가 필요할 때 쓰고, 없으면 -1이 나옵니다.
Q. […arr]로 복사했는데도 원본이 바뀌는 것처럼 보일 때가 있습니다. 왜 그런가요?
배열 안에 객체가 들어있으면, 배열은 새로 만들었더라도 “안의 객체”는 공유될 수 있습니다. 그래서 복사본에서 객체 속성을 바꾸면 원본 쪽에도 영향이 갈 수 있습니다.