[260707화 07일차] JavaScript

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

# JavaScript 학습일지

[JS 2일차] 이벤트 · 반복문 · 배열/객체 · 클래스 · 디버깅 · 서버 연동
정적인 페이지에 반응과 데이터를 불어넣기

2026.07.07 · 학습 범위: JS 08번 ~ 17번

📋 오늘 다룬 것들

어제(1일차)가 변수·조건문·함수 같은 JS의 "문법"을 익히는 시간이었다면, 오늘은 그 문법으로 진짜 "동작하는" 페이지를 만드는 도구들을 배웠다. 클릭에 반응하는 이벤트, 지겨운 반복을 대신 해주는 반복문, 글자·배열을 다루는 메서드, 값을 하나로 묶는 객체와 그 틀인 클래스, 코드가 안 될 때 범인을 잡는 디버깅, 그리고 브라우저 저장소(localStorage)를 넘어 진짜 서버와 데이터를 주고받는 fetch까지 — 총 10개 스텝(08~17)을 훑었다. 오늘 배운 것들이 사실상 내일 미니 프로젝트(여행 소비 기록장)의 뼈대가 된다.

이벤트
addEventListeneronclick
속성 다루기
getAttributesetAttribute
저장소
localStoragesetItem/getItem
반복문
forwhile
문자열 메서드
.lengthtoUpperCasereplace
배열 메서드
pushjoin
객체 & 클래스
속성classconstructornew
디버깅
console.logdebuggerF12
서버 연동
fetchasync/awaitCRUD

🗂️ 항목별 메모

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 vs addEventListeneronclick은 속성이라 새로 쓰면 앞 함수가 지워져 마지막 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")}`;
속성 vs 메서드 구분법 — 점 뒤에 ( )가 붙으면 메서드(동작), 안 붙으면 속성(값). .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 nullquerySelector가 요소를 못 찾음 (id 오타·script 위치)
X is not defined변수·함수 이름 오타거나 선언을 안 함
X is not a function오타거나 그 값에 없는 메서드를 호출함
왜 중요한가 — 코드는 눈에 안 보이게 돌아간다. 찍어봐야 "내 생각 vs 실제 값"이 맞는지 알 수 있다. 버그 대부분이 "값이 예상과 다름"이라 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 기본fetch(주소, 옵션), method로 CREATE(POST)·READ(GET)·UPDATE(PUT)·DELETE 구분. JSON.stringify(객체)로 보낼 때 글자로 바꾸고, res.json()으로 받은 응답을 다시 객체로 바꾼다.
② 비동기 · async/awaitfetch는 비동기라 응답을 기다리는 동안 브라우저가 멈추지 않는다. await("기다리다")는 그 결과가 올 때까지 그 줄만 대기시키고, async는 함수 앞에 붙여 "이 함수 안에서 await 씀"을 표시하는 팻말이다. 둘은 짝꿍 — awaitasync 함수 안에서만 쓸 수 있다.
③ 실행 환경 주의json-server는 꼭 npm start로 실행해야 이 폴더에 고정된 버전을 쓴다(전역 설치본은 최신 버전이라 id 형식이 다를 수 있음). JSON 객체는 키 순서가 의미 없어서 {id, name}{name, id}는 저장상 같은 데이터다 — 화면에 보기 좋게 찍으려고 norm() 함수로 순서만 통일했다.

✍️ 마치며

오늘 배운 열 가지 — 이벤트, 반복문, 문자열/배열 메서드, 객체, 클래스, 디버깅, fetch — 를 하나씩 보면 작은 예제들이지만, 모아 놓고 보니 이게 곧 "실제로 쓰는 웹앱을 만드는 데 필요한 전부"라는 생각이 들었다. 특히 render()로 데이터가 바뀔 때마다 화면을 다시 그리는 패턴과, localStorage로 값을 저장·공유하는 방식은 내일 진행할 멀티페이지 미니 프로젝트에서 그대로, 그리고 훨씬 큰 규모로 다시 쓰게 됐다. console.logdebugger를 믿고 값부터 찍어보는 습관도 오늘부터 몸에 붙이기로 했다.

🏷️ JavaScript  |  DOM 이벤트  |  localStorage  |  배열/객체  |  클래스  |  fetch  |  학습일지