DOM을 이용해 HTML 요소를 탐색·변경하고, 배열 데이터를 화면에 렌더링하며 사용자 이벤트를 처리하는 방법을 학습했다.
DOM(Document Object Model)은 HTML 문서를 JavaScript가 다룰 수 있도록 트리 형태의 객체로 표현한 것이다.
document
└── html
├── head
└── body
├── div
├── input
└── button
JavaScript에서는 document 객체를 통해 HTML 요소를 탐색하고 변경한다.
const title = document.querySelector("#title");
title.textContent = "변경된 제목";
BOM(Browser Object Model)은 브라우저 자체를 제어하기 위한 객체 모델이다. 최상위 객체는 window다.
window.alert("알림");
window.location.href = "./main.html";
window.prompt("값을 입력하세요.");
document: HTML 문서를 다룬다.window: 브라우저 창과 주소, 알림 등의 기능을 다룬다.window.document를 줄여 document로 작성할 수 있다.JavaScript에서는 함수를 변수에 담거나 다른 함수에 전달할 수 있다.
const greeting = () => {
console.log("안녕하세요.");
};
greeting();
객체 구조 분해 할당을 사용하면 필요한 프로퍼티를 바로 꺼낼 수 있다.
const student = {
html: 90,
css: 85,
javascript: 95,
};
const { html, css, javascript } = student;
console.log(html, css, javascript);
배열은 반복문뿐만 아니라 배열이 제공하는 메서드로 처리할 수 있다.
const ages = [20, 24, 26, 30, 34, 35];
const doubledAges = ages.map((age) => age * 2);
const evenAges = ages.filter((age) => age % 2 === 0);
for: 인덱스를 직접 제어하며 반복한다.forEach: 배열의 각 요소를 순회한다.map: 각 요소를 변경해 새로운 배열을 만든다.filter: 조건을 만족하는 요소로 새로운 배열을 만든다.for...of: 배열의 값을 순회한다.for...in: 객체의 키를 순회하는 용도로 주로 사용한다.ages.forEach((value, index) => {
console.log(`index: ${index}, value: ${value}`);
});
배열에 저장된 객체를 반복하면서 HTML 문자열을 생성할 수 있다.
const cards = [
{
img: "html.png",
heading: "HTML",
},
{
img: "css.png",
heading: "CSS",
},
];
let cardHTML = "";
cards.forEach((card) => {
cardHTML += `
<div class="card">
<img src="${card.img}" alt="${card.heading}" />
<h2>${card.heading}</h2>
</div>
`;
});
document.querySelector("#display").innerHTML = cardHTML;
템플릿 리터럴을 사용하면 변수와 HTML 문자열을 함께 작성하기 편리하다.
다만 외부에서 전달받은 값을 검증하지 않고 innerHTML에 넣으면 XSS 문제가 발생할 수 있다. 신뢰할 수 없는 데이터는 textContent나 안전한 DOM 생성 방식을 사용하는 것이 좋다.
일반 JavaScript에서는 변수의 값만 변경한다고 화면이 자동으로 다시 그려지지 않는다.
let count = 0;
count = 1;
// 값은 변경되지만 화면은 자동으로 바뀌지 않는다.
변경된 값을 화면에 표시하려면 DOM을 직접 수정해야 한다.
React에서는 화면에 영향을 주는 데이터를 state로 관리한다. state가 변경되면 해당 컴포넌트가 다시 렌더링된다.
사용자 이벤트
↓
state 변경
↓
컴포넌트 다시 렌더링
↓
변경된 화면 표시
입력값이나 조건에 따라 서로 다른 동작을 실행할 수 있다.
if (age >= 20) {
console.log("성인입니다.");
} else {
console.log("미성년자입니다.");
}
값에 따라 여러 경우를 구분할 때는 switch를 사용할 수 있다.
switch (choice) {
case 1:
console.log("토레타");
break;
case 2:
console.log("박카스");
break;
default:
console.log("잘못된 선택입니다.");
}
간단한 조건은 삼항 연산자로 표현할 수 있다.
const message = age >= 20 ? "성인" : "미성년자";
삼항 연산자는 속도보다 코드의 간결함을 위해 사용한다. 조건이 복잡하다면 if문을 사용하는 편이 읽기 쉽다.
Bootstrap을 사용해 이메일과 비밀번호 입력 화면을 구성했다.
<div class="container">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input
id="email"
name="email"
type="email"
class="form-control"
/>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input
id="password"
name="password"
type="password"
class="form-control"
/>
</div>
<div id="errorMessage" style="display: none">
이메일과 비밀번호를 입력해 주세요.
</div>
<button id="signInButton" type="button" class="btn btn-primary">
로그인
</button>
</div>
입력값은 요소의 value로 가져올 수 있다.
const email = document.querySelector("#email").value;
const password = document.querySelector("#password").value;
값이 비어 있다면 오류 메시지를 표시한다.
if (email === "" || password === "") {
document.querySelector("#errorMessage").style.display = "block";
}
이벤트는 브라우저에서 발생하는 사용자 행동이나 상태 변화를 의미한다.
이벤트가 발생했을 때 실행되는 함수를 이벤트 핸들러라고 한다.
const signInButton = document.querySelector("#signInButton");
const handleSignIn = () => {
console.log("로그인 버튼 클릭");
};
signInButton.addEventListener("click", handleSignIn);
이벤트를 연결하는 방법에는 여러 가지가 있다.
<button onclick="handleSignIn()">로그인</button>
button.onclick = handleSignIn;
button.addEventListener("click", handleSignIn);
일반 JavaScript에서는 HTML과 JavaScript를 분리할 수 있고 여러 이벤트 핸들러를 등록할 수 있는 addEventListener() 방식이 권장된다.
React의 JSX에서는 문법이 다르다.
<button
type="button"
className="btn btn-primary"
onClick={handleSignIn}
>
로그인
</button>
class, onclickclassName, onClick다음처럼 비밀번호를 URL에 포함하면 안 된다.
location.href = `main.html?email=${email}&password=${password}`;
URL은 브라우저 기록, 서버 로그 등에 남을 수 있기 때문이다. 로그인 정보는 HTTPS 환경에서 POST 요청의 본문으로 전달해야 한다.
fetch("/api/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
window를 통해 알림과 주소 이동 등을 처리할 수 있다.map()과 filter()는 기존 배열을 직접 변경하지 않고 새로운 배열을 반환한다.forEach(), map(), filter()의 반환값과 목적이 헷갈렸다.onclick과 React의 onClick 문법이 혼동됐다.map(), filter(), forEach()를 이용해 배열을 처리했다.배열의 데이터를 반복해 HTML 요소로 렌더링할 수 있었고, DOM에서 입력값을 가져와 조건에 따라 오류 메시지를 표시할 수 있었다. 또한 이벤트 발생부터 핸들러 실행, DOM 변경까지의 흐름을 이해했다.
JavaScript 변수의 값은 변경됐지만 화면에 바로 반영되지 않았고, 이벤트를 HTML 속성·onclick·addEventListener() 중 어떤 방식으로 연결해야 하는지 헷갈렸다.
또한 로그인 정보를 URL의 쿼리 스트링에 포함하는 코드에는 보안 문제가 있었다.
변수 변경과 DOM 렌더링은 별개이므로 일반 JavaScript에서는 DOM을 직접 수정해야 한다는 것을 확인했다. React에서는 화면 데이터를 state로 관리해 변경 시 다시 렌더링하도록 한다.
이벤트는 HTML과 JavaScript를 분리할 수 있는 addEventListener()로 연결하고, 로그인 정보는 URL이 아닌 POST 요청 본문에 담도록 수정했다.
createElement()를 이용해 innerHTML 없이 카드 목록 만들기useState로 로그인 입력값과 오류 메시지 관리하기