자바스크립트 DOM - CRUD

제리·2023년 9월 22일
0

마이버추얼트립

목록 보기
2/9
post-thumbnail

Vanila JS로 DOM을 제어해 CRUD를 연습한다.


Review

구현하고자 하는 대로 화면을 그리는 것 위주로 신경을 썼는데 중간중간 리액트 방식이 떠올라서 혼선이 있었다. 리액트였다면 이렇게 했을 텐데 이번에는 어떻게 하지?를 시작으로 여러 방면으로 생각해 보면서 화면 위주와 데이터 위주 구현 방식의 차이를 느낄 수 있었다.

개선하기

습관적으로 완성이 되었을 때의 실행 순서대로 코드를 짠다는 것을 알게 되었다. 단순한 기능이다 보니 코드를 작성하기도 전에 중복을 신경 썼던 것 같다. 더미데이터를 활용해 작은 단위부터 만들어나가는 것이 좋다고 느꼈다. 구현을 위한 흐름과 완성 이후 실행을 위한 흐름을 다르게 생각해보아야 한다. 특히 처음부터 중복 제거를 위해 함수로 분리하려고 하면 복잡하게 느껴진다.

암기식 코딩 벗어나기

조금만 삐끗해도 예상했던 것과 전혀 다른 결과를 만들 수 있다. 암기식 코딩이 익숙해지면 언제나 예상한 대로 흘러가야 하기 때문에 그렇지 않은 모든 상황이 버겁다. 이를 벗어나기 위한 가장 좋은 방법은 불편함을 느끼고 해결하는 과정을 경험하는 것이다. 몇 번 반복한 지금은 에러가 발생하거나 막히는 구간이 생기면 새로운 소재를 찾은 것 같아 즐겁다. 무엇이든지 당연한 것은 없다는 것을 기억하고 한 번 더 고민해 보자. 시간은 소요되지만 조금은 깊게 파고 들어보는 것도 좋은 공부가 된다.


Demo

Demo 바로가기


Markup

<div class="app">
  <header id="header">...</header>
  <main id="wrapper">
	 <section class="shopping">
        <div class="section__title" id="shopping__title">
          <h3>탐욕 바구니 🥹</h3>
        </div>
        <ul class="section__list disabled" id="shopping__list"></ul>
     </section>
     <section class="packing">
        <div class="section__title" id="packing__title">
          <h3>꼭 챙기기 🧳</h3>
        </div>
        <ul class="section__list disabled" id="packing__list"></ul>
	</section>
  </main>
  <section class="control">...</section>
</div>
  1. 등록되는 아이템이 적용될 카테고리는 2가지이다.
  • 카테고리 01 : shopping
  • 카테고리 02 : packing
  1. 아이템은 스크립트를 이용해 동적으로 생성하여 카테고리 하위로 넣어준다.
  2. control 섹션은 카테고리를 선택하고, 내용을 입력하고, 등록하는 패널이다.

Check

  • 아이템이 생성되면 카테고리 펼치기

Flow

[처음 사고 흐름]
1. DOM 요소 가져오기
2. 버튼에 이벤트 등록
3. 카테고리와 내용 입력값 얻기
4. 입력받은 값으로 Item 생성
5. 카테고리에 맞는 List에 Item 넣기

[구현을 위한 흐름]
1. DOM 요소 가져오기
2. 버튼 이벤트 등록
3. 임의 데이터로 Item 생성
4. Item을 카테고리에 맞는 List에 넣기
5. 4번까지 구현이 되면 사용자에게 받은 입력값으로 Item 생성되도록 함수 구현
6. 기능별 코드 분리 & 중복 코드 제거


Contents

01. 카테고리에 맞는 리스트 찾기

const addItem = (category, value) => {
    const item = createItem(value);
    const list = setActiveState(category);
    list.append(item);
};
    
const setActiveState = (category) => {
    const list = document.querySelector(`#${category}__list`);
    const title = document.querySelector(`#${category}__title`);
    list.classList.remove('disabled');
    title.classList.add('active');
    return list;
};

아이템 추가 버튼을 공통된 control로 처리해 생성된 Item을 카테고리에 맞게 넣는 것과 펼침효과를 어디에 어떻게 주어야 할지 고민했다.

✦ select 태그로 받은 값을 이용해서 Item이 들어가야 하는 List를 필터 한다.
List 필터와 상태 변경은 createItem 함수에서 모두 적용한 뒤 List에 넣어주는 것까지 할 수도 있지만, createItem 함수에서는 생성만 작업하고 추가 기능은 분리하는 것이 더 좋다고 생각했다.


02. create

DOM 요소를 생성할 때 createElement를 이용할 수도 있고, 하위 요소들이 많아지는 등의 복잡한 마크업이 필요하다면 innerHTML을 활용할 수도 있다. 이렇게 DOM을 동적으로 생성할 때 고려해야 하는 점은 적용이 되기 전까지 화면에 나타나지 않기 때문에 document로 접근할 수 없다는 것이다. 처음 배울 때는 ‘document.createElement’ 자체를 하나의 공식처럼 사용했었다.

  • createElement로 생성한 요소를 변수에 담아 접근하기
  • element.innerHTML을 이용해 마크업 한 뒤 element로 접근하기

등의 방법으로 생성되기 전 DOM요소를 선택하고 조작할 수 있다.


03. 체크 이벤트 등록

체크박스는 input 태그를 사용하고 checked 속성을 이용해 분기했다. checked 속성은 체크가 되어 있으면 true를, 아니면 false값을 갖는다. 체크가 true가 되었을 때, text에도 스타일을 주고 싶다면 분기에 따라 스타일을 위한 클래스를 추가/제거하면 된다.

  • input 태그가 아닌 div 등 다른 태그를 적용한 뒤 CSS로 꾸밀 수 있다.

04. Update

수정을 위해서는 1. 수정하려는 요소를 선택하고 2. 새로운 값을 입력받고 3. 기존 값을 새로운 값으로 대체해야 한다. 데이터 위주로 구현할 때는 id 값을 이용하여 이벤트가 발생한 요소의 id와 일치하는 데이터를 찾는다. 하지만 이번에는 별도의 id를 부여하지 않아 다른 방법을 이용했다.

생성되는 아이템에 버튼이 있고, 그 버튼에 이벤트를 등록한다. 그리고 수정을 위한 함수(editItem)에 아이템을 통째로 전달한다. 처음에는 아이템을 전달할 때 똑같이 생긴 아이템이 계속해서 생기는데 그중에서 수정하려는 아이템이 맞는지 어떻게 확인하지? id와 같은 식별자가 있어야 하는 게 아닐까?라는 생각을 했었다.

그렇게 생각한 이유는 동적으로 생성하는 요소에 같은 이벤트가 반복적으로 등록된다는 것을 머릿속으로 명확히 그려내지 못해서였다. 결론적으로 createItem 함수가 호출되면 이벤트나 속성 등을 모두 등록한 DOM 요소를 매번 새롭게 반환한다. 어떤 아이템의 수정 버튼에 클릭 이벤트가 발생하면 관련 Element 또는 Node를 알아낼 수 있다.


  • item01
  • item02
    • li 태그
    • 수정 버튼 : Click 이벤트 등록
    • 삭제 버튼 : Click 이벤트 등록

✔︎

  • item01
    • li 태그
    • 수정 버튼 : Click 이벤트 등록
    • 삭제 버튼 : Click 이벤트 등록
  • item02
    • li 태그
    • 수정 버튼 : Click 이벤트 등록
    • 삭제 버튼 : Click 이벤트 등록

05. Update - UI

새로운 값을 받기 위한 입력창이 필요했다. prompt를 이용할 수도 있지만, 화면상에서 수정 모드를 명확히 표현하고 싶었다. 새로운 input 태그를 생성해 수정하려는 아이템을 덮는 방법 대신 UI를 갈아 끼우는 것처럼 구현했다.

✦ 내용을 담고 있는 태그를 숨기고, 새로운 요소를 생성해 추가한다.

const editItem = (item) => {
	item.firstElementChild.classList.add('disabled');
	const newData = document.createElement('div');
	newData.className = 'item__form--edit';
	newData.innerHTML = `
                        <div>
                            <input type="text" class="item__input--edit" />
                        </div>
                        <div>
                            <button type="button" class="item__button--submit">수정완료</button>
                        </div>
                        `;
	item.append(newData);
 	...
    ...
}

수정 내용을 등록하고 나면 기존 태그를 다시 보이게 한다. prompt 방법에 비해 코드가 굉장히 길어진다. 리액트가 그리워진다.


06. Delete

const editBtn = item.querySelector('.item__button--edit');
    editBtn.addEventListener('click', () => {
        editItem(item);
    });

item.addEventListener('click', (e) => {
    if (e.target.className === 'item__button--delete') {
        const title = item.parentNode.previousElementSibling;
        item.previousElementSibling ? '' : title.classList.remove('active');
        item.remove();
    }
});

a. 버튼에 이벤트 등록 / 1 부모 : 1 자식
b. 이벤트 위임 / 1 부모 : N 자식

2가지 방법을 생각했다. 삭제 버튼에 이벤트를 등록할지, 삭제 버튼을 가지고 있는 Item에 등록할 지에 따라 다르다. 부모인 item에 등록하면 수정과 삭제를 함께 처리할 수 있다.

추가로 더 이상 삭제할 Item이 없을 때는 메뉴를 접힘으로 변경하고 싶었다. Item이 생성되는 시점에는 상위 리스트나 섹션에 추가되기 전이기 때문에 카테고리 정보를 받아와서 분기처리를 해야 해서 조금 번거롭지만 타고 타고 올라가는 방법을 선택했다.


Note

✳︎ DOM 요소 제어하기

생성

  • createElement
  • innerHTML

선택

  • document.getElementsByClassName()
  • document.getElementsByTagName()
  • document.getElementsById()
  • document.querySelector()
  • document.querySelectorAll()
profile
DOM과 친해지기

0개의 댓글

관련 채용 정보