☁️ goormTIL | JavaScript #20

매루·2025년 10월 1일

goormTIL

목록 보기
18/67
post-thumbnail

📅 2025-09-30

➡️ JavaScript 컬렉션, 이벤트 위임, 옵셔널 체이닝에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리 + 🤔❓


🔎 학습 리마인드

📌 컬렉션

  • 여러 값을 묶어서 관리하는 자료구조의 총칭 예) 배열(Array), 객체(Object), NodeList, HTMLCollection
  • 컬렉션 기반으로 사고하기
    → 어떤 로직을 짤 때, 데이터(컬렉션)의 흐름컬렉션에 적용할 연산(반복, 변환, 검색 등)을 중심으로 사고하는 것
  • 중요 개념
    • 컬렉션의 요소를 순회(iteration)하여 필요한 작업을 반복
    • 요소들을 필터링(filtering)하거나 추출(mapping)하여 원하는 결과를 도출
    • 다양한 내장 메서드(예: Array.prototype.map, filter, reduce, forEach 등)를 활용
// 사람들에서 나이가 20 이상인 사람만 골라내고, 이름만 뽑아야지?
const people = [
  { name: 'Alice', age: 18 },
  { name: 'Bob', age: 22 },
  { name: 'Charlie', age: 27 },
  { name: 'Dave', age: 16 }
];

const result = people
  .filter(person => person.age >= 20)
  .map(person => person.name);

TodoList 만들어보기 실습

  • Todo API
    "https://jsonplaceholder.typicode.com/todos?_limit=5"
  • index.html
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <title>Todo List</title>
        <link rel="stylesheet" href="style.css" />
      </head>
      <body>
        <h1>TodoList</h1>
        <div class="input-area">
          <input
            type="text"
            id="todoInput"
            placeholder="할 일을 입력하세요"
            autocomplete="off"
          />
          <button id="addBtn">추가</button>
        </div>
        <ul id="todoList"></ul>
        <p id="infoText"></p>
        <script src="app.js"></script>
      </body>
    </html>
  • style.css
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
      padding: 0;
    }
    h1 {
      margin-bottom: 10px;
    }
    .input-area {
      margin-bottom: 10px;
    }
    #todoInput {
      width: 200px;
      padding: 5px;
      margin-right: 5px;
    }
    #addBtn,
    #loadBtn {
      padding: 5px 10px;
      cursor: pointer;
      margin-right: 5px;
    }
    #todoList {
      list-style-type: none;
      padding-left: 0;
      width: 320px;
      margin-top: 10px;
    }
    #todoList li {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin: 5px 0;
      padding: 8px;
      background-color: #f9f9f9;
      transition: background-color 0.3s ease;
      cursor: pointer;
      border: 1px solid #ddd;
      border-radius: 4px;
    }
    #todoList li:hover {
      background-color: #ececec;
    }
    .title {
      flex: 1;
      margin-right: 10px;
    }
    .completed {
      text-decoration: line-through;
      color: gray;
    }
    .edit-btn,
    .delete-btn {
      margin-left: 5px;
      padding: 3px 6px;
      cursor: pointer;
      border: none;
      border-radius: 3px;
    }
    .edit-btn {
      background-color: #ffe066;
    }
    .delete-btn {
      background-color: #ff8787;
    }
    #infoText {
      margin-top: 15px;
      font-weight: bold;
      color: #666;
    }
  • app.js
    // js 코드 작성해보기

🌊 Deep Dive

📌 tmdb 토이 프로젝트 진행 중 알게 된 것 정리

🤔 이벤트 위임 (event delegation)

  • 특정 노드에 이벤트 리스너를 추가할 필요 없이, 이벤트 리스너를 하나의 부모 노드에 추가
  • 해당 부모 노드에 추가된 이벤트 리스너는 버블링된 이벤트를 분석하여 자식 요소에서 일치하는 이벤트를 찾음
    <ul id="parent-list" >
    	<li id="post-1">항목 1</li>
    	<li id="post-2">항목 2</li>
    	<li id="post-3">항목 3</li>
    	<li id="post-4">항목 4</li>
    	<li id="post-5">항목 5</li>
    	<li id="post-6">항목 6</li>
    </ul>
    document.getElementById("parent-list").addEventListener("click", (e) => {
    	if (e.target && e.target.nodeName == "LI")
    		console.log("목록 항목", e.target.id.replace("post- ", " "), "을 클릭 했습니다!");
    });
  • 즉, 요소마다 핸들러를 할당하지 않고 공통 부모 또는 조상에게 핸들러를 하나만 할당하여 여러 요소를 한 번에 다룰 수 있음!

  • element.closest(selector)
    • 선택된 요소의 상위 요소 중 selector와 일치하는 가장 근접한 조상 요소 반환

      <div class="card-list">
      	<div class="movie-card"></div>
      	<div class="movie-card"></div>
      	<div class="movie-card"></div>
      	<div class="movie-card"></div>
      </div>
      document.querySelector('.card-list').addEventListener('click', async (e) => {
          const movieCard = e.target.closest('.movie-card'); // 이벤트 위임
          console.log(movieCard);
      		// card-list에 이벤트 위임을 등록하고, 클릭된 요소의 가장 가까운 .movie-card 요소를 찾아 출력
      });

참고

🔗 https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Scripting/Events

🔗 https://davidwalsh.name/event-delegate

🔗 https://ko.javascript.info/event-delegation

🔗 https://velog.io/@nulbo/TIL-JavaScript-event.target-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9C%84%EC%9E%84


🤔 옵셔널 체이닝(?.)

  • ?.을 사용하면 프로퍼티가 없는 중첩 객체를 에러 없이 안전하게 접근할 수 있음
  • ?. 연산자는 객체의 속성에 접근할 때, 해당 경로 중 하나라도 null이나 undefined이면 에러를 내지 않고 undefined를 반환
  • 예시
    const user = {
      profile: {
        name: "철수",
        info: {
          age: 25,
          city: "Seoul"
        }
      }
    };
    
    console.log(user.profile.info.age); // 25
    • profile이나 info가 없다면 TypeError 발생
    • 옵셔널 체이닝을 사용하면 에러 대신 undefined가 반환됨
      console.log(user.profile?.info?.age); // 25
      
      const emptyUser = {};
      console.log(emptyUser.profile?.info?.age); // undefined
  • 메서드 호출에서도 활용 가능
    const person = {
      greet() {
        return "Hello!";
      }
    };
    
    console.log(person.greet?.());   // "Hello!"
    console.log(person.sayBye?.());  // undefined (sayBye가 없으므로 안전하게 undefined 반환)

참고

🔗 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Optional_chaining


🤔 널 병합 연산자

  • ?? 연산자는 왼쪽 피연산자가 null 또는 undefined일 때만 오른쪽 값 선택
  • 0, false, ""(빈 문자열) 같은 falsy한 값은 그대로 인정, 진짜 값이 없을 때만 대체값 사용
// 위치 값이 없으면 기본값 사용
let userLocation = null;
console.log(userLocation ?? "Unknown"); // "Unknown"

userLocation = "Seoul";
console.log(userLocation ?? "Unknown"); // "Seoul"

// falsy 값(0)도 유효하게 인정됨
const temperature = 0;
console.log(temperature ?? 25); // 0

참고

🔗 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing


0개의 댓글