본문으로 건너뛰기
기술스택 · stack/javascript/배열: 순서 있는 목록 다루기

JavaScript · 5장 자료구조와 자료형

배열: 순서 있는 목록 다루기

장바구니 상품, 검색 결과, 알림 목록처럼 순서가 있는 값은 배열에 담아요. 객체가 "이름으로 찾는 상자"라면 배열은 "번호로 찾는 줄"이에요. 이 글에서는 배열에 넣고 빼는 기본 동작과, 배열이 사실 객체라서 생기는 일을 정리해요.

#5.3

01. 배열 만들고 읽기

대괄호 안에 값을 쉼표로 나열하면 배열이 돼요. 각 값은 0부터 시작하는 번호(인덱스)로 읽어요. length는 항목 개수이고, 마지막 항목은 arr[arr.length - 1] 또는 더 짧게 arr.at(-1)로 읽어요. 없는 번호를 읽으면 오류가 아니라 undefined예요.

const fruits = ['사과', '배', '귤'];

console.log(fruits[0]);       // 사과
console.log(fruits.length);   // 3
console.log(fruits.at(-1));   // 귤 — 뒤에서 첫 번째
console.log(fruits[5]);       // undefined — 없는 번호

fruits[1] = '복숭아';          // const여도 안의 항목은 바꿀 수 있어요
console.log(fruits);          // ['사과', '복숭아', '귤']

const는 "이 변수가 다른 배열을 가리키지 않는다"는 뜻이지, 배열 안을 바꾸지 못한다는 뜻이 아니에요(참조). 배열 항목에는 숫자 · 문자열 · 객체를 섞어 넣을 수 있지만, 실무에서는 같은 모양의 값만 담아야 다루기 쉬워요.

02. 끝과 앞, 가운데에서 넣고 빼기

넣고 빼는 메서드는 위치에 따라 나뉘어요. 모두 원래 배열을 직접 바꿔요.

  • push(값) · pop() — 끝에 넣고, 끝에서 빼요.
  • unshift(값) · shift() — 앞에 넣고, 앞에서 빼요.
  • splice(위치, 지울 개수, …넣을 값) — 가운데에서 지우고 넣어요.
const cart = [];
cart.push('연필');
cart.push('공책', '지우개');       // 여러 개도 한 번에
console.log(cart);                // ['연필', '공책', '지우개']
console.log(cart.pop(), cart);    // 지우개 ['연필', '공책'] — 뺀 값을 돌려줘요

const queue = ['A', 'B', 'C'];    // 먼저 온 순서대로 처리하는 대기열
console.log(queue.shift(), queue); // A ['B', 'C']
queue.unshift('Z');
console.log(queue);               // ['Z', 'B', 'C']
①왼쪽은 push로 끝에 넣는 모습이에요. 있던 칸의 번호는 그대로예요. 오른쪽은 unshift로 앞에 넣는 모습이에요. 있던 칸이 모두 한 칸씩 밀리고 번호가 하나씩 바뀌어요. 그래서 항목이 아주 많으면 앞에서 넣고 빼는 쪽이 더 느려요.

가운데 항목을 지울 때는 먼저 indexOf로 위치를 찾고 splice로 지워요. indexOf는 없으면 -1을 돌려주는데, splice(-1, 1)은 마지막 항목을 지워 버리니 꼭 확인해요.

const tags = ['js', 'css', 'html', 'react'];

const i = tags.indexOf('html');
if (i !== -1) tags.splice(i, 1);  // i번 자리에서 1개 지우기
console.log(tags);                // ['js', 'css', 'react']

tags.splice(1, 0, 'ts');          // 1번 자리에 0개 지우고 'ts' 넣기
console.log(tags);                // ['js', 'ts', 'css', 'react']

화면 상태처럼 원래 배열을 바꾸면 안 되는 곳에서는 push · splice 대신 새 배열을 만드는 방법([...arr, 값], filter, toSpliced)을 써요. 원본을 바꾸지 않는 메서드와 불변 업데이트 글에서 이어서 다뤄요.

03. 배열은 특별한 객체예요

배열은 따로 있는 자료형이 아니라, 번호를 키로 쓰고 length를 알아서 관리하는 객체예요. 그래서 객체의 성질이 그대로 따라와요.

console.log(typeof []);               // object — 배열인지 확인은 이걸로 못 해요
console.log(Array.isArray([]));       // true — 이걸 써요

const a = [1, 2];
const b = a;                          // 복사가 아니라 같은 배열을 가리켜요
b.push(3);
console.log(a);                       // [1, 2, 3]
console.log([1, 2] === [1, 2]);       // false — 내용이 아니라 같은 배열인지 비교해요

const letters = ['a'];
letters[4] = 'e';                     // 중간을 건너뛰고 넣으면
console.log(letters.length, letters[2]); // 5 undefined — 빈칸이 생겨요
  • 참조: 배열을 다른 변수에 담거나 함수에 넘기면 같은 배열을 함께 써요. 복사본이 필요하면 [...arr]나 structuredClone(arr)를 써요(얕은 복사 · 깊은 복사).
  • 비교: 두 배열의 내용이 같은지는 ===로 알 수 없어요. 항목을 하나씩 비교하거나, 단순한 값 배열이면 JSON.stringify로 바꿔 비교해요.
  • 돌기: 배열은 for...of로 돌아요. for...in은 번호를 문자열로 주고 빈칸 처리도 달라서 배열에는 쓰지 않아요(for...of).

상품을 볼 때마다 "최근 본 상품" 목록을 갱신하는 함수예요. 지금은 본 순서대로 끝에 쌓이기만 해요. 아래 규칙대로 고쳐서 ['F', 'E', 'D', 'B', 'C']가 나오게 해 보세요.

  • 가장 최근에 본 상품이 맨 앞에 와요.
  • 이미 목록에 있는 상품을 다시 보면 원래 자리에서 빼고 맨 앞으로 옮겨요.
  • 목록은 5개까지만 남겨요.
const recent = [];

function addRecent(id) {
  recent.push(id);
}

for (const id of ['A', 'B', 'C', 'B', 'D', 'E', 'F']) addRecent(id);
console.log(recent);
해답 보기

indexOf로 이미 있는지 찾아 splice로 빼고, unshift로 맨 앞에 넣은 뒤, 5개를 넘으면 pop으로 가장 오래된 것을 빼요.

function addRecent(id) {
  const i = recent.indexOf(id);
  if (i !== -1) recent.splice(i, 1);
  recent.unshift(id);
  if (recent.length > 5) recent.pop();
}
// ['F', 'E', 'D', 'B', 'C']

04. 실전 포인트

  • 언제 쓰나요? 순서가 중요하거나 같은 모양의 값을 여러 개 담을 때 배열을 써요. 끝에 넣고 빼는 push · pop이 가장 흔하고 빨라요.
  • 대신 쓰는 것: 이름(키)으로 바로 찾아야 하면 객체나 Map, 중복 없는 모음이면 Set을 써요(Map과 Set). 화면 상태처럼 원본을 바꾸면 안 되면 새 배열을 만드는 방법을 써요.
  • 흔한 실수: indexOf가 -1인지 확인하지 않고 splice해서 마지막 항목을 지우는 것, const b = a를 복사라고 생각하는 것, typeof로 배열인지 확인하는 것, 배열을 ===로 비교하는 것이에요.

정리

  • 배열은 0부터 시작하는 번호로 읽어요. 마지막 항목은 at(-1), 없는 번호는 undefined예요.
  • push · pop은 끝, unshift · shift는 앞, splice는 가운데에서 넣고 빼요. 모두 원래 배열을 바꿔요.
  • 배열은 객체라서 참조로 전달되고, ===는 같은 배열인지만 봐요. 확인은 Array.isArray로 해요.
const list = ['a', 'b']; list.push('c');는 const인데 오류가 날까요?

오류가 나지 않아요. const는 변수가 다른 배열을 가리키지 못하게 막을 뿐이고, 배열 안의 항목을 바꾸는 건 막지 않아요.

tags.splice(tags.indexOf('vue'), 1)에서 'vue'가 없으면 어떻게 될까요?

indexOf가 -1을 돌려주고, splice(-1, 1)은 뒤에서 첫 번째 항목을 지워요. 엉뚱한 항목이 사라지니 -1인지 먼저 확인해요.

값이 배열인지 확인할 때 typeof 대신 무엇을 쓰고, 왜 그럴까요?

Array.isArray(값)을 써요. 배열은 객체라서 typeof가 'object'를 돌려줘 보통 객체 · null과 구분이 안 되기 때문이에요.

SIMPLE · 기술스택 / JavaScript — 5장 자료구조와 자료형