Skip to content

(선수) JS/DOM/이벤트

실력 향상 Tip

초기 동작에 만족하지 말고, 더 나은 코드를 지속적으로 고민하고 개선하세요.

click 이벤트 리스너

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <title>DOM</title>
</head>
<body>
    <h1>hello world</h1>
    <button id="btn">클릭햇!</button>
    <script>
        const heading = document.querySelector('h1')
        const btn = document.querySelector('#btn')
        // 노드.addEventListener(이벤트이름, 함수)
        btn.addEventListener('click', function(e){
            alert('버튼을 클릭했습니다!')
        })
        heading.addEventListener('click', e => {
            heading.style.color = 'red'
            console.log(e)
        })
    </script>
</body>
</html>

JS를 통한 DOM 생성/추가

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <title>DOM</title>
</head>
<body>
    <p>hello</p>
    <script>
        const data = [
            { name: '키링', price: 1000 },
            { name: '플스5', price: 500000 },
            { name: '오븐', price: 100000 },
            { name: '곤충채집통', price: 15000 },
        ]
        const p1 = document.createElement('p')
        p1.textContent = data[0].name
        document.body.appendChild(p1)

        const p2 = document.createElement('p')
        p2.textContent = data[1].name
        document.body.appendChild(p2)

        const p3 = document.createElement('p')
        p3.textContent = data[2].name
        document.body.appendChild(p3)

        const p4 = document.createElement('p')
        p4.textContent = data[3].name
        document.body.appendChild(p4)

    </script>

</body>
</html>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <title>DOM</title>
</head>
<body>
    <p>hello</p>
    <script>
        const data = [
            { name: '키링', price: 1000 },
            { name: '플스5', price: 500000 },
            { name: '오븐', price: 100000 },
            { name: '곤충채집통', price: 15000 },
        ]

        // 각 항목의 name을 <p>로 감싸는 HTML 문자열 생성
        const html = data.map(item => `<p>${item.name}</p>`).join('')

        // body에 추가
        document.body.innerHTML += html
    </script>
</body>
</html>

폼 제출 이벤트

이벤트의 기본동작을 막고, JS를 통해 다른 로직 수행하기

form submit 기본 동작

페이지가 전환되며, action 지정 주소로 입력 데이터 전송하고, 서버 응답으로 화면 전체를 다시 그리기

출처 코드를 변경했습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <title>폼 제출 이벤트</title>
</head>
<body>
    <form action="">
        <input type="text" name="username" value="익명" />
        <input type="password" name="password" />
        <button type="submit" class="submit">로그인</button>
    </form>

    <div class="responseText"></div>

    <script>
    const submit = document.querySelector('.submit');
    submit.addEventListener('click', (event) => {
        event.preventDefault();

        const username = document.querySelector("[name=username]").value;
        const password = document.querySelector("[name=password]").value;

        // 서버로 전송할 데이터 (파일 업로드 지원 불가)
        const data = { username, password };

        // TODO: 서버 응답 받기
        const responseMessage = !! data.password ? "로그인 성공" : "암호를 입력하세요.";

        const responseTextEl = document.querySelector('.responseText');
        responseTextEl.innerHTML = responseMessage;
    });
    </script>
</body>
</html>

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8" />
    <title>폼 제출 이벤트</title>
</head>
<body>
    <form action=""
          id="loginForm"
    >
        <input type="text" name="username" value="익명" />
        <input type="password" name="password" />
        <button type="submit">로그인</button>
    </form>

    <div id="responseText"></div>

    <script>
    const formEl = document.querySelector("#loginForm");
    formEl.addEventListener("submit", (e) => {
      e.preventDefault();

      const _formEl = e.target;
      const formData = new FormData(_formEl);  // 파일 업로드 가능.

      // TODO: 서버 응답 받기
      const responseMessage = !! formData.get('password') ? "로그인 성공" : "암호를 입력하세요.";

      const responseTextEl = document.querySelector('#responseText');
      responseTextEl.innerHTML = responseMessage;
    });
    </script>
</body>
</html>

비동기 로딩 및 UI 반영

<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <style></style>
    </head>
    <body>
        <h1>상품 목록</h1>
        <ul id="product-list1"></ul>
        <ul id="product-list2"></ul>

        <script src="./fetch.js"></script>
        <script src="./asyncAwait.js"></script>
    </body>
</html>
const API_URL = "https://dev.wenivops.co.kr/services/fastapi-crud/1/product";

fetch(API_URL)
    .then((response) => response.json())
    .then((json) => {
        const productList = document.getElementById("product-list1");
        json.forEach((product) => {
            const li = document.createElement("li");
            // console.log(product);
            li.textContent = `${product.productName}: ${product.price}`;
            productList.appendChild(li);
        });
    })
    .catch((error) => console.log(error));
asyncAwait.js
async function fetchProducts() {
    try {
        const response = await fetch(API_URL);
        const json = await response.json();
        // console.log("async-await: ", json);
        const productList = document.getElementById("product-list2");
        json.forEach((product) => {
            const li = document.createElement("li");
            // console.log(product);
            li.textContent = `${product.productName}: ${product.price}`;
            productList.appendChild(li);
        });
    } catch (error) {
        console.log(error);
    }
}

fetchProducts();

Warning

JS 함수 내에서의 DOM 하드코딩 접근은 함수의 재사용성을 낮출 수 있기에 주의해주세요.

  • id는 웹페이지 내에서 유일해야 합니다. 같은 id의 요소가 2개 이상 있다면, 그 중 1개만 선택 가능.
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Document</title>
    <style></style>
</head>
<body>
<h1>상품 목록</h1>
<ul id="product-list1"></ul>
<ul id="product-list2"></ul>

<!-- <script src="./fetch.js"></script> -->
<!-- <script src="./asyncAwait.js"></script> -->
</body>

<!-- 특정 페이지 내에서 사용되는 JS라면, HTML에 포함시키는 것이 관리성이 낫습니다. 관련있는 코드는 가가운 곳에 -->
<!-- 물론 정답은 없고, 고민해보세요. -->

<script>

</script>

<script>
  loadProducts1("#product-list1");
  loadProducts2("#product-list2");

  /* JSON 포맷 응답 : Client Side Rendering */
  function loadProducts1(targetSelector) {
    const targetEl = document.querySelector(targetSelector);
    const API_URL = "https://raw.githubusercontent.com/pyhub-kr/dump-data/refs/heads/main/weniv/fastapi-crud/product.json";

    // JSON 응답 => DOM 변환이 필요
    fetch(API_URL)
      .then((response) => response.json())
      .then((json) => {
        json.forEach((product) => {
          const li = document.createElement("li");
          li.textContent = `${product.productName}: ${product.price}`;
          targetEl.appendChild(li);
        });
      })
      .catch((error) => console.log(error));
  }

  /* HTML 포맷 응답 : Server Side Rendering의 한 종류 */
  async function loadProducts2(targetSelector) {
    const targetEl = document.querySelector(targetSelector);
    const API_URL = "https://raw.githubusercontent.com/pyhub-kr/dump-data/refs/heads/main/weniv/fastapi-crud/product.html";

    try {
      const response = await fetch(API_URL);
      // HTML 응답을 그대로 DOM에 반영
      targetEl.innerHTML = await response.text();
    } catch (error) {
      console.log(error)
    }
  }
</script>
</html>

Comments