1.배열 — 순서가 있는 목록
지금까지는 변수 하나에 값 하나를 담았습니다. 그런데 학생 30명의 점수를 변수 30개에 담을 수는 없습니다. 배열은 여러 값을 순서대로 한 이름 아래 담는 자료형입니다. 대괄호 안에 쉼표로 값을 늘어놓아 [70, 85, 90]처럼 만듭니다. 4강에서 for...of로 하나씩 꺼낸 그 목록이 배열입니다.
배열의 값은 위치 번호인 인덱스로 꺼냅니다. 인덱스는 1이 아니라 0부터 시작하므로 첫 값은 arr[0], 두 번째 값은 arr[1]입니다. 값의 개수는 arr.length이고, 마지막 값의 인덱스는 언제나 arr.length - 1입니다. 4강에서 본 off-by-one 실수가 바로 여기서 생깁니다.
없는 인덱스를 읽어도 오류가 나지 않고 undefined가 나옵니다. 길이가 3인 배열에서 arr[3]이나 arr[-1]을 읽으면 둘 다 undefined입니다. 뒤에서부터 세고 싶다면 arr.at(-1)을 쓰면 마지막 값이 나옵니다. 배열에는 숫자·문자열·불리언을 섞어 담을 수도 있지만, 실제로는 같은 종류의 값을 담아야 다루기 쉽습니다.
undefinedconst fruits = ['사과', '배', '귤'];
console.log(fruits[0], fruits[2]);
console.log(fruits.length);
console.log(fruits[fruits.length - 1]);
console.log(fruits[3]);
console.log(fruits.at(-1));사과 귤 3 귤 undefined 귤
2.배열 바꾸기와 찾기 — `push`, `pop`, `includes`, `indexOf`
push는 배열 끝에 값을 더하고, pop은 끝의 값을 꺼내 돌려주면서 배열에서 뺍니다. arr[1] = '포도'처럼 인덱스에 직접 넣어 값을 바꿀 수도 있습니다. 여기서 헷갈리기 쉬운 점이 있습니다. const로 만든 배열도 내용은 바꿀 수 있습니다. const가 막는 것은 '그 이름에 다른 배열을 다시 넣는 것'이지 배열 속 칸을 바꾸는 것이 아닙니다.
값이 들어 있는지 확인할 때는 includes가 참·거짓을, indexOf가 위치를 돌려줍니다. indexOf는 찾지 못하면 -1을 돌려줍니다. 4강에서 '못 찾음'을 -1로 표시한 것과 같은 약속입니다. 그래서 if (arr.indexOf(x))처럼 쓰면 첫 칸(0)에 있을 때 거짓이 되고, 못 찾은 경우(-1)는 참이 되어 버립니다(3강의 truthy·falsy). 있는지만 알고 싶다면 includes를 쓰세요.
배열을 다른 변수에 넣으면 복사가 아니라 '같은 배열을 가리키는 이름'이 하나 더 생깁니다. 아래 코드에서 b에 push하면 a도 바뀝니다. 같은 집의 주소를 두 사람이 나눠 가진 것과 같습니다. 정말 따로 된 사본이 필요하면 [...a]처럼 펼쳐서 새 배열을 만듭니다. 객체도 똑같이 동작합니다.
push·pop·includes·indexOfconst cart = ['우유', '빵'];
cart.push('달걀');
console.log(cart, cart.length);
const last = cart.pop();
console.log(last, cart);
console.log(cart.includes('빵'), cart.indexOf('빵'));
console.log(cart.indexOf('김'));[ '우유', '빵', '달걀' ] 3 달걀 [ '우유', '빵' ] true 1 -1
const a = [1, 2];
const b = a;
b.push(3);
console.log(a);
const c = [...a];
c.push(4);
console.log(a, c);[ 1, 2, 3 ] [ 1, 2, 3 ] [ 1, 2, 3, 4 ]
3.객체 — 이름표 붙은 칸
배열이 '몇 번째'로 값을 찾는다면, 객체는 '이름'으로 값을 찾습니다. 중괄호 안에 키: 값을 쉼표로 늘어놓아 { name: '민수', age: 20 }처럼 만듭니다. 각 키와 값의 쌍을 속성이라고 합니다. 회원 한 명, 상품 하나처럼 여러 정보가 모여 한 대상을 이룰 때 객체가 잘 맞습니다.
속성은 user.name처럼 점으로 읽는 것이 보통이고, 키 이름이 변수에 들어 있을 때는 user[key]처럼 대괄호로 읽습니다. 없는 속성을 읽으면 배열의 없는 칸처럼 undefined가 나옵니다. 그런데 그 undefined에서 다시 속성을 읽으면 그때는 TypeError가 납니다. 아래 코드가 그 경우로, 키 이름을 nmae로 잘못 쓴 것이 원인입니다. 메시지의 정확한 문구는 브라우저마다 조금씩 다릅니다.
이 오류는 AI가 짠 코드를 실제 데이터에 붙였을 때 가장 자주 보는 오류 가운데 하나입니다. 대개 데이터에 있는 키 이름과 코드가 기대한 키 이름이 다르거나, 어떤 항목에만 그 속성이 빠져 있을 때 생깁니다. 오류 메시지의 괄호 안 (reading 'length')는 '무엇을 읽으려다 실패했는지'를 알려 주므로, 그 바로 앞의 값이 왜 undefined인지 거슬러 찾아가면 됩니다(8강).
undefinedconst user = { name: '민수', age: 20 };
console.log(user.name, user.age);
user.age = 21;
user.city = '부산';
console.log(user);
const key = 'name';
console.log(user[key]);
console.log(user.email);민수 20
{ name: '민수', age: 21, city: '부산' }
민수
undefinedundefined에서 다시 읽으면 TypeErrorconst user = { name: '민수' };
console.log(user.nmae);
console.log(user.nmae.length);undefined
TypeError: Cannot read properties of undefined (reading 'length')4.객체의 배열 = 표
실제 데이터는 대부분 '객체의 배열' 모양입니다. 배열의 각 칸이 표의 한 행이고, 객체의 키가 열 이름입니다. 엑셀 같은 스프레드시트의 표 한 장을 코드로 옮겼다고 생각하면 됩니다. 아래 표와 코드는 같은 가상의 판매 기록을 두 가지로 나타낸 것입니다.
이 모양에서는 4강의 반복문과 지금 배운 속성 읽기를 합치면 거의 모든 계산을 할 수 있습니다. 행을 하나씩 꺼내(for...of) 필요한 열을 읽고(row.qty) 누적하거나 셉니다. 9강에서는 텍스트로 된 표를 이 모양으로 바꾸어 합계·평균·그룹별 합계를 직접 구해 봅니다.
| (인덱스) | `item` | `price` | `qty` |
|---|---|---|---|
| 0 | 공책 | 1500 | 4 |
| 1 | 연필 | 500 | 10 |
| 2 | 지우개 | 800 | 3 |
const sales = [
{ item: '공책', price: 1500, qty: 4 },
{ item: '연필', price: 500, qty: 10 },
{ item: '지우개', price: 800, qty: 3 },
];
let revenue = 0;
for (const row of sales) {
revenue += row.price * row.qty;
}
console.log(revenue);13400
- 1단계 무엇을 쌓는가: 매출 합계. 시작값
0. - 2단계 한 행의 일:
row.price * row.qty를 더합니다. 공책 6,000, 연필 5,000, 지우개 2,400. - 3단계 결과: 6,000 + 5,000 + 2,400 = 13,400원.
- 검산: 행이 3개이니 바퀴도 3번이어야 합니다. 행마다 중간 합계를 찍어 보면 6000 → 11000 → 13400 순서로 늘어납니다.
5.`map`·`filter`·`reduce` 맛보기
배열에는 반복문을 대신하는 메서드가 있습니다. 가장 많이 쓰는 셋이 map·filter·reduce입니다. 셋 모두 5강의 화살표 함수를 받아서 배열의 값마다 그 함수를 불러 줍니다. map은 값마다 바꾼 결과로 새 배열을 만들고(길이가 같음), filter는 함수가 참을 돌려준 값만 골라 새 배열을 만들며(길이가 같거나 짧음), reduce는 값들을 하나로 접어 합계 같은 값 하나를 만듭니다.
중요한 점은 map과 filter가 원래 배열을 바꾸지 않고 새 배열을 돌려준다는 것입니다. 5강의 순수 함수와 같은 성질이라 결과를 예측하기 쉽습니다. reduce는 두 번째 인자로 시작값을 꼭 넣으세요. reduce((s, x) => s + x, 0)의 0이 4강 누적 패턴의 let sum = 0에 해당합니다. 시작값을 빼면 빈 배열에서 TypeError가 납니다.
처음에는 반복문이 더 읽기 쉬울 수 있고, 그래도 괜찮습니다. 반복문으로 할 수 있는 일을 짧게 쓰는 방법일 뿐입니다. 다만 AI가 짠 코드에는 이 셋이 사슬처럼 이어져 나오는 경우가 많으므로, 한 단계씩 끊어 중간 결과를 찍어 보며 읽는 연습을 해 두면 좋습니다.
map은 바꾸기, filter는 고르기, reduce는 접기 — 원래 배열은 그대로const prices = [1500, 500, 800, 12000];
const withTax = prices.map((p) => p * 1.1);
console.log(withTax.map((p) => Math.round(p)));
const cheap = prices.filter((p) => p < 1000);
console.log(cheap);
const total = prices.reduce((s, p) => s + p, 0);
console.log(total);
console.log(prices);[ 1650, 550, 880, 13200 ] [ 500, 800 ] 14800 [ 1500, 500, 800, 12000 ]
| 메서드 | 하는 일 | 돌려주는 것 | 반복문으로 치면 |
|---|---|---|---|
map | 값마다 바꾸기 | 같은 길이의 새 배열 | 새 배열에 바꾼 값을 push |
filter | 조건에 맞는 값 고르기 | 같거나 짧은 새 배열 | 조건이 참일 때만 push |
reduce | 하나로 접기 | 값 하나(합계 등) | 누적 패턴 |
6.JSON — 데이터를 주고받는 글자 형식
JSON은 객체와 배열을 글자(문자열)로 적는 형식입니다. 이름은 JavaScript에서 왔지만 지금은 거의 모든 프로그래밍 언어와 웹 서비스가 데이터를 주고받을 때 씁니다. 설정 파일, 서버의 응답, 앱이 저장하는 데이터 상당수가 JSON입니다. JSON.stringify는 값을 JSON 글자로 바꾸고, JSON.parse는 JSON 글자를 다시 값으로 되돌립니다.
JSON은 JavaScript 객체 문법과 닮았지만 더 엄격합니다. 키는 반드시 큰따옴표로 감싸야 하고, 문자열도 큰따옴표만 됩니다. 마지막 항목 뒤 쉼표는 허용되지 않고, 주석도 쓸 수 없습니다. 함수와 undefined는 담을 수 없어서, stringify하면 객체 속성일 때는 빠지고 배열 안에서는 null로 바뀝니다. 규칙을 하나라도 어기면 JSON.parse가 SyntaxError를 냅니다.
JSON이 길게 한 줄로 붙어 있으면 읽기 어렵습니다. 그럴 때는 JSON 포맷터 도구에 붙여 넣으면 들여쓰기를 해 주고, 규칙이 깨진 곳을 짚어 주기도 합니다. 다만 실제 회원 정보처럼 개인정보가 든 데이터는 외부 도구나 AI에 붙여 넣지 말고, 구조만 같은 가상의 데이터로 바꿔서 확인하세요(10강).
const item = { name: '공책', price: 1500, tags: ['문구'] };
const text = JSON.stringify(item);
console.log(text);
console.log(typeof text);
const back = JSON.parse(text);
console.log(back.price + 500);{"name":"공책","price":1500,"tags":["문구"]}
string
2000const bad = "{ name: '공책' }";
console.log(JSON.parse(bad));SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)JSON.stringify(값, null, 2)처럼 세 번째 인자에 2를 주면 두 칸 들여쓰기를 한 여러 줄 글자로 바꿔 줍니다. 이 페이지의 실습장도 여러 줄을 그대로 보여 주므로, 긴 데이터를 찍어 구조를 살필 때 써 보세요.📌 핵심 정리
- 배열은 순서 있는 목록 — 인덱스는 0부터, 마지막은
arr.length - 1, 없는 칸은undefined - 객체는 이름(키)으로 값을 찾는 묶음 —
undefined에서 다시 속성을 읽으면TypeError - 배열·객체를 다른 변수에 넣으면 복사가 아니라 같은 것을 가리킨다
- 객체의 배열은 표다 — 행마다 꺼내 열을 읽어 누적하거나
map·filter·reduce로 가공한다 - JSON은 큰따옴표 키, 끝 쉼표 금지 —
stringify로 글자로,parse로 값으로
🧪 코드 실습
배열과 객체를 읽고, 객체의 배열(표)에서 값을 쌓고 고르는 함수를 만듭니다. 빈 배열 같은 경계값도 함께 채점됩니다.
코드는 이 브라우저 안의 격리된 공간에서만 실행되고 서버로 보내지 않습니다. 네트워크에는 접속할 수 없고, 2초가 넘으면 멈춥니다. 고친 코드는 이 브라우저에만 저장됩니다. Ctrl+Enter(⌘+Enter)로 실행하고, Tab 키는 공백 두 칸입니다(Esc 다음 Tab 은 다음 칸으로 이동).
자바스크립트가 꺼져 있어 실행할 수는 없지만, 문제와 처음 코드, 자동 검사 내용, 예시 답안은 그대로 읽을 수 있습니다.
1마지막 값 꺼내기
배열 arr의 마지막 값을 돌려주는 함수 lastItem(arr)를 만드세요. 빈 배열이면 null을 돌려줍니다.
lastItem([1,2,3])기대값3lastItem(["사과"])기대값"사과"lastItem([])기대값nulllastItem([0,false])기대값false
💡 힌트
빈 배열에서 arr[arr.length - 1]은 arr[-1]이라 undefined입니다. 그래서 빈 배열은 먼저 따로 처리합니다.
예시 답안 보기
function lastItem(arr) {
if (arr.length === 0) {
return null;
}
return arr[arr.length - 1];
}2수량 합계
{ item, qty } 모양 객체의 배열 rows를 받아 qty의 합계를 돌려주는 함수 totalQty(rows)를 만드세요. 빈 배열이면 0입니다.
totalQty([{"item":"공책","qty":4},{"item":"연필","qty":10}])기대값14totalQty([])기대값0totalQty([{"item":"지우개","qty":3}])기대값3totalQty([{"item":"a","qty":0},{"item":"b","qty":0}])기대값0
💡 힌트
행 하나는 객체입니다. 점 표기로 row.qty를 읽으세요. reduce로 한 줄에 쓸 수도 있습니다.
예시 답안 보기
function totalQty(rows) {
let total = 0;
for (const row of rows) {
total += row.qty;
}
return total;
}3조건에 맞는 이름만 고르기
{ item, price } 모양 객체의 배열 rows와 기준 가격 min을 받아, 가격이 min 이상인 행의 item만 원래 순서대로 담은 배열을 돌려주는 함수 namesFrom(rows, min)을 만드세요. 맞는 행이 없으면 빈 배열 []입니다.
namesFrom([{"item":"공책","price":1500},{"item":"연필","price":500},{"item":"가방","price":20000}], 1000)기대값["공책","가방"]namesFrom([], 100)기대값[]namesFrom([{"item":"연필","price":500}], 500)기대값["연필"]namesFrom([{"item":"연필","price":500}], 501)기대값[]
💡 힌트
filter로 행을 고른 뒤 map으로 이름만 뽑는 두 단계입니다. 반복문으로 if와 push를 써도 됩니다.
예시 답안 보기
function namesFrom(rows, min) {
return rows
.filter((r) => r.price >= min)
.map((r) => r.item);
}4항목별 개수 세기
문자열 배열 words를 받아, 각 단어가 몇 번 나왔는지 담은 객체를 돌려주는 함수 countBy(words)를 만드세요. 예: ['사과', '배', '사과'] → { 사과: 2, 배: 1 }. 빈 배열이면 빈 객체 {}입니다.
countBy(["사과","배","사과"])기대값{"배":1,"사과":2}countBy([])기대값{}countBy(["a"])기대값{"a":1}countBy(["x","x","x","y"])기대값{"x":3,"y":1}
💡 힌트
키 이름이 변수 w에 들어 있으니 점 표기가 아니라 대괄호 counts[w]를 씁니다. 처음 나온 단어는 undefined입니다.
예시 답안 보기
function countBy(words) {
const counts = {};
for (const w of words) {
if (counts[w] === undefined) {
counts[w] = 1;
} else {
counts[w] += 1;
}
}
return counts;
}🤖 AI에게 이렇게 물어보세요
복사해서 [ ] 부분을 내 상황으로 바꿔 쓰세요. 답을 그대로 믿지 말고 본문과 비교해 확인하세요.
데이터를 배열·객체로 어떻게 담을지 고민될 때
다음 가상의 데이터를 JavaScript의 '객체의 배열'로 바꿔 줘. 키 이름은 영어 소문자로 짓고, 숫자는 문자열이 아닌 숫자로 해 줘. 그리고 이 데이터로 할 수 있는 계산 3가지(합계·조건 고르기·그룹별 세기)를 반복문 버전과 `map`/`filter`/`reduce` 버전으로 나란히 보여 줘. 데이터: [가상의 표 붙여 넣기]
`map`·`filter`·`reduce`가 이어진 코드를 읽기 어려울 때
아래 코드의 `map`/`filter`/`reduce` 사슬을 한 단계씩 끊어서, 단계마다 중간 결과가 어떤 배열이나 값이 되는지 작은 예시 입력으로 보여 줘. 같은 일을 하는 `for...of` 반복문 버전도 써 줘. 코드: [붙여 넣기]
`JSON.parse`가 오류를 낼 때
다음 JSON 문자열이 왜 `JSON.parse`에서 오류가 나는지 규칙 위반을 모두 찾아 줘(따옴표, 끝 쉼표, 주석 등). 고친 JSON도 보여 줘. 내용은 가상의 예시 데이터야: [붙여 넣기]
🧰 관련 도구
이 강의에서 다룬 JSON·정규식·텍스트를 바로 확인하고 다듬어 볼 수 있는 도구입니다. 실제 개인정보나 비밀번호는 붙여 넣지 마세요.
- MDN Web Docs — JavaScript 안내서(Guide): 인덱스 기반 컬렉션(Indexed collections), 객체로 작업하기(Working with objects)
- MDN Web Docs — Array.prototype.map / filter / reduce, JSON.stringify / JSON.parse 참조
- ECMA-404 JSON 데이터 교환 형식 표준의 일반적 규칙
학습 목표를 모두 이뤘다면 완료를 눌러 진도에 기록하세요.
이 브라우저에서는 저장할 수 없어 이 화면에서만 표시됩니다.