2026. 7. 8. 15:59ㆍAI 기반 프론트엔드 웹개발자 양성과정

[JS 2일차] 이벤트 · 반복문 · 배열/객체 · 클래스 · 디버깅 · 서버 연동
정적인 페이지에 반응과 데이터를 불어넣기
2026.07.07 · 학습 범위: JS 08번 ~ 17번
📋 오늘 다룬 것들
어제(1일차)가 변수·조건문·함수 같은 JS의 "문법"을 익히는 시간이었다면, 오늘은 그 문법으로 진짜 "동작하는" 페이지를 만드는 도구들을 배웠다. 클릭에 반응하는 이벤트, 지겨운 반복을 대신 해주는 반복문, 글자·배열을 다루는 메서드, 값을 하나로 묶는 객체와 그 틀인 클래스, 코드가 안 될 때 범인을 잡는 디버깅, 그리고 브라우저 저장소(localStorage)를 넘어 진짜 서버와 데이터를 주고받는 fetch까지 — 총 10개 스텝(08~17)을 훑었다. 오늘 배운 것들이 사실상 내일 미니 프로젝트(여행 소비 기록장)의 뼈대가 된다.
🗂️ 항목별 메모
08 — 이벤트: 사용자 행동에 반응하기
이벤트는 클릭·키 누르기 같은 사용자 행동이다. "이 행동이 일어나면 이걸 해줘"라고 addEventListener로 미리 약속해두면 페이지가 반응한다. 버튼을 누를 때마다 숫자가 올라가는 카운터로 요소를 찾고(querySelector) → 이벤트를 걸고(addEventListener) → 화면을 바꾼다(textContent)는 흐름을 다시 확인했다.
// 누른 횟수를 셀 상자 n (값이 변하니 let) let n = 0; const btn = document.querySelector("#btn"); const out = document.querySelector("#count"); btn.addEventListener("click", () => { n++; // n = n + 1 과 같음 out.textContent = `${n}번 눌렀어요`; });
addEventListener("click", 함수) — "클릭되면 그 함수를 실행하라"는 예약. click 말고도 mouseover(마우스 올림)·keydown(키 누름) 같은 이벤트가 많다.09 — 이미지 바꾸기: 클릭하면 그림 토글
그림을 클릭할 때마다 두 그림이 번갈아 바뀌는 예제. getAttribute로 지금 걸린 그림 주소를 읽고, if/else로 비교해 setAttribute로 반대 그림으로 바꿔치기했다.
myImage.setAttribute("src", IMG_A); myImage.onclick = () => { const mySrc = myImage.getAttribute("src"); if (mySrc === IMG_A) { myImage.setAttribute("src", IMG_B); } else { myImage.setAttribute("src", IMG_A); } };
onclick은 속성이라 새로 쓰면 앞 함수가 지워져 마지막 1개만 남는다. addEventListener는 등록할 때마다 쌓여서 클릭 한 번에 여러 함수가 순서대로 다 실행된다.
// onclick — 덮어써서 마지막 1개만 남음 btn.onclick = 저장하기; btn.onclick = 알림띄우기; // 저장하기는 사라짐 // addEventListener — 쌓여서 전부 실행 btn.addEventListener("click", 저장하기); btn.addEventListener("click", 알림띄우기); // 클릭 1번 → 둘 다 실행그래서 실무에서는
addEventListener를 기본으로 쓴다.
10 — 환영 메시지 저장: localStorage로 이름 기억하기
localStorage는 브라우저의 작은 메모장이다. setItem으로 저장하면 새로고침해도, 브라우저를 껐다 켜도 값이 남아있다. 이름을 입력해 저장하면 다음에 열었을 때도 그 이름으로 인사하게 만들었다.
// 처음 열 때 — 저장된 이름이 있으면(getItem) 꺼내서 인사 const saved = localStorage.getItem("name"); if (saved) { greet.textContent = `안녕, ${saved}!`; } document.querySelector("#save").addEventListener("click", () => { const myName = input.value; if (!myName) { return; } localStorage.setItem("name", myName); greet.textContent = `안녕, ${myName}!`; });
| 메서드 | 역할 |
|---|---|
setItem("key", 값) | 브라우저 메모장에 저장 |
getItem("key") | 저장된 값 꺼내기, 없으면 null |
removeItem("key") | 저장된 값 지우기 |
getItem이 없으면 null을 돌려주기 때문에 if (saved) { ... }로 존재 여부부터 확인하는 패턴이 기본. 미니 프로젝트에서 여행·지출 데이터를 페이지끼리 공유하는 방식도 바로 이 localStorage다.11 — 반복문: 똑같은 일을 여러 번
같은 일을 여러 번 시킬 때 코드를 복붙하면 힘들다. for에게 "몇 번 돌지"만 정해주면 그 안의 일을 알아서 반복한다. 숫자를 입력하고 버튼을 누르면 for로 1부터 N까지, while로 N부터 1까지 목록을 그렸다.
// for (시작값; 계속할 조건; 매번 변화) for (let i = 1; i <= count; i++) { const li = document.createElement("li"); li.textContent = `${i}번째 🍎`; out.appendChild(li); } // while (조건): 조건이 참인 동안 반복 let i = Number(n.value); while (i > 0) { // ... 목록에 추가 ... i--; // 안 줄이면 무한반복 주의! }
| 구분 | 구조 | 특징 |
|---|---|---|
for | 초기화·조건·증감이 ( ) 한 줄에 모임 | "몇 번 돌지" 명확할 때 |
while | 초기화는 위, 증감은 본문 안 | 조건만 있고 횟수가 불명확할 때 |
i++는 i = i + 1과 같다. while은 조건을 바꾸는 i--를 빼먹으면 무한반복에 빠지니 주의.12 — 문자열 메서드: 글자 다루는 도구
글자(문자열)에는 길이 재기·대문자 바꾸기·일부 바꾸기 같은 편한 도구가 딸려 있다. 점(.)을 찍고 도구 이름을 부르면 된다.
const text = s.value; out.innerHTML = `글자 수(length): ${text.length}` + "<br>" + `대문자(toUpperCase): ${text.toUpperCase()}` + "<br>" + `e→E 바꾸기(replace): ${text.replace("e", "E")}`;
( )가 붙으면 메서드(동작), 안 붙으면 속성(값). .length는 속성, .toUpperCase()·.replace()는 메서드다.13 — 배열 메서드: 여러 값을 한 줄에
배열은 값 여러 개를 사물함처럼 칸칸이 담는 묶음이다. 과일 이름을 넣고 추가를 누르면 push로 배열 끝에 쌓이고, render 함수가 화면을 다시 그린다.
let fruits = ["사과", "바나나"]; // 화면을 다시 그리는 화살표 함수 — 데이터가 바뀔 때마다 호출 const render = () => { out.textContent = fruits.join(", "); info.textContent = `개수(length): ${fruits.length}`; }; fruits.push(fruit.value); // 배열 끝에 새 값 추가 render();
render 패턴 — 데이터(배열·객체)가 바뀔 때마다 화면을 통째로 다시 그리는 함수를 따로 빼두는 방식. 이 패턴이 내일 미니 프로젝트의 지출 목록·대시보드를 그리는 핵심 뼈대로 그대로 이어진다.14 — 객체: 한 덩어리로 묶기
객체는 서로 관련된 값을 이름표(속성) 붙여 한 덩어리로 묶은 것. 사람 한 명을 이름·나이로 묶듯, 점(.)을 찍고 이름표를 불러 값을 읽고 바꿨다.
let person = { name: "콩이", age: 10 }; const render = () => { out.textContent = `${person.name} (${person.age}살)`; }; person.age++; // person.age = person.age + 1 person.name = "두부";
{ 이름: 값, … } — 중괄호 안에 이름표(속성)와 값을 쌍으로. 같은 모양 객체를 여러 개 찍어내는 틀이 바로 다음에 나오는 클래스다.15 — 클래스: 객체 찍어내는 틀
객체를 하나하나 손으로 만들면 번거롭다. 클래스는 같은 모양 객체를 여러 개 찍어내는 붕어빵 틀이고, new로 찍으면 객체(인스턴스)가 나온다. Dog 클래스로 두 마리를 찍어내 각자 이름으로 짖게 했다.
class Dog { constructor(name) { // new로 만들 때 자동 호출 this.name = name; // this = 지금 만들어지는 객체 자신 } bark() { // 메서드 — 모든 Dog가 함께 씀 return `${this.name}: 멍멍!`; } } const poppy = new Dog("뽀삐"); const choco = new Dog("초코");
| 용어 | 뜻 |
|---|---|
class 이름 { } | 객체를 찍어내는 틀(설계도) |
constructor(재료) | new로 만들 때 자동 실행, this.속성에 값 저장 |
this | 지금 만들어지는 객체 자신을 가리킴 |
new Dog("뽀삐") | 틀로 객체(인스턴스) 찍어내기, 여러 개 가능 |
Document(대문자)는 클래스, document(소문자)는 그 틀로 찍어낸 객체 — 그동안 매번 쓰던 document가 사실은 클래스의 인스턴스였다는 걸 오늘 처음 연결 지었다.16 — 디버깅: 에러 잡고 값 확인하기
코드가 안 될 때 추측은 금물. 개발자도구(F12)의 Console 탭에서 console.log(JS의 프린트)로 값을 직접 확인하고, 빨간 에러 메시지를 읽는 법을 익혔다. 에러는 적이 아니라 어디가 틀렸는지 알려주는 힌트라는 게 인상적이었다.
console.log("총합:", price * count); // 총합: 3000 console.log(typeof price); // "number" // id 오타로 요소를 못 찾은 흔한 실수 const btn = document.querySelector("#by"); // 진짜 id는 #buy btn.addEventListener("click", () => {}); // ❌ TypeError: Cannot read properties of null (reading 'addEventListener')
| 에러 메시지 | 원인 |
|---|---|
Cannot read properties of null | querySelector가 요소를 못 찾음 (id 오타·script 위치) |
X is not defined | 변수·함수 이름 오타거나 선언을 안 함 |
X is not a function | 오타거나 그 값에 없는 메서드를 호출함 |
console.log 한 줄이 원인을 바로 드러낸다. debugger;를 코드에 적으면 그 줄에서 실행이 멈춰(중단점) 콘솔에 변수값을 직접 쳐볼 수 있다.17 — 서버 연동: fetch로 CRUD 하기
지금까지는 localStorage(내 브라우저에만 저장)만 썼다. 진짜 서버가 있으면 fetch로 데이터를 주고받아 생성·조회·수정·삭제(CRUD)가 가능하다. json-server로 가짜 서버를 띄워 db.json을 상대로 연습했다.
// CREATE — POST document.querySelector("#btn-create").addEventListener("click", async () => { const res = await fetch(API, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "John" }) }); show("CREATE 결과", norm(await res.json())); });
fetch(주소, 옵션), method로 CREATE(POST)·READ(GET)·UPDATE(PUT)·DELETE 구분. JSON.stringify(객체)로 보낼 때 글자로 바꾸고, res.json()으로 받은 응답을 다시 객체로 바꾼다.
fetch는 비동기라 응답을 기다리는 동안 브라우저가 멈추지 않는다. await("기다리다")는 그 결과가 올 때까지 그 줄만 대기시키고, async는 함수 앞에 붙여 "이 함수 안에서 await 씀"을 표시하는 팻말이다. 둘은 짝꿍 — await은 async 함수 안에서만 쓸 수 있다.
json-server는 꼭 npm start로 실행해야 이 폴더에 고정된 버전을 쓴다(전역 설치본은 최신 버전이라 id 형식이 다를 수 있음). JSON 객체는 키 순서가 의미 없어서 {id, name}과 {name, id}는 저장상 같은 데이터다 — 화면에 보기 좋게 찍으려고 norm() 함수로 순서만 통일했다.
✍️ 마치며
오늘 배운 열 가지 — 이벤트, 반복문, 문자열/배열 메서드, 객체, 클래스, 디버깅, fetch — 를 하나씩 보면 작은 예제들이지만, 모아 놓고 보니 이게 곧 "실제로 쓰는 웹앱을 만드는 데 필요한 전부"라는 생각이 들었다. 특히 render()로 데이터가 바뀔 때마다 화면을 다시 그리는 패턴과, localStorage로 값을 저장·공유하는 방식은 내일 진행할 멀티페이지 미니 프로젝트에서 그대로, 그리고 훨씬 큰 규모로 다시 쓰게 됐다. console.log와 debugger를 믿고 값부터 찍어보는 습관도 오늘부터 몸에 붙이기로 했다.
🏷️ JavaScript | DOM 이벤트 | localStorage | 배열/객체 | 클래스 | fetch | 학습일지
'AI 기반 프론트엔드 웹개발자 양성과정' 카테고리의 다른 글
| [260709 09일차] React (0) | 2026.07.09 |
|---|---|
| [260708수 08일차] Mini-Project #2 (0) | 2026.07.08 |
| [260706월 06일차] Mini-Project, JavaScript (0) | 2026.07.06 |
| [260703금 05일차] CSS (0) | 2026.07.03 |
| [260702목 04일차] CSS (0) | 2026.07.02 |