혼자 적어보는 노트

프로그래머스 자바스크립트 스터디 - mission2 본문

스터디

프로그래머스 자바스크립트 스터디 - mission2

jinist 2022. 1. 24. 06:24

mission2의 중점은 컴포넌트 만들기이다.

app 컴포넌트 안에서 todoList, todoInput, todoCount를 보여주는 것인데

코드 적고 나면 뭔가 이상한 것 같아서 자꾸 수정하고 시간이 조금 걸렸던 것 같다. 

 


1. Event delegate

자식컴포넌트에 이벤트를 넣어야 할 때 일일히 넣지 않고

부모컴포넌트를 통해서 이벤트를 delegation 할수 있다는 것을 처음 알았다.

 

[이전 코드]

this.onClickRemove = (event) => {
    event.stopPropagation()
    const listId = event.currentTarget.parentNode.id
    this.removeTodo(listId)
  }
  this.changeComplete = (event) => {
    const listId = event.currentTarget.id
    this.updateComplete(listId)
 }
 
 ...
 
 li.addEventListener('click', this.changeComplete)
 button.addEventListener('click', this.onClickRemove)

ul 안에 li가 있고 li 안에 button이 있는 구조에서

li를 클릭했을때 나타나는 이벤트와, button을 클릭했을 때 나타나는 이벤트 두 가지를 생성해 주었었다.

코드가 조금 생략되어 있지만 ul 안에는 li의 갯수가 많았고 일일히 forEach로 이벤트들을 넣어준 상황이었다.

 

 

✅ 수정된 코드

  this.onClick = (event) => {
    const li = event.target.closest('.todo-list-item')
    if (li) {
      const listId = li.id
      event.target.tagName == 'BUTTON'
        ? this.removeTodo(listId)
        : this.updateComplete(listId)
    }
  }
  
  ul.addEventListener('click', this.onClick)

event delegate를 이용하여 li와 button의 부모인 ul에 이벤트를 넣어서

하나의 이벤트로 처리할 수 있게 되었다. 👍

 


2. Custom Event

이벤트 콜백으로 컴포넌트의 파라미터로 내려줘야 할 때

컴포넌트의 depth가 너무 깊어질 때 사용하면 유용하다.

하지만 너무 남용하면 추적이 어려워지니 팀과 협의 후 사용하기!

 

function Parent() {
  this.$target = document.createElement("ul");

  document.body.append(this.$target);
  const child = new Child(this.$target);

  this.$target.addEventListener("welcome", () => {
    alert("Event 실행");
  });
}

function Child(target) {
  this.$target = target;

  this.$list = document.createElement("li");
  this.$target.append(this.$list);

  this.render = function () {
    this.$list.innerHTML = "<button>Click</button>";

    this.$list.querySelector("button").addEventListener("click", function () {
      this.dispatchEvent(new CustomEvent("welcome", { bubbles: true }));
    });
  };
  this.render();
}

const parent = new Parent();

study에서 진행한 코드는 아니지만 이렇게 부모 컴포넌트에서 함수를 전달해 주지 않아도

자식 컴포넌트에서 호출한 이벤트를 부모 컴포넌트에서 받아서 처리할 수 있다는 것을 알게되었다.

 


3. localStorage

로컬 스토리지를 사용한 state 관리는 어떻게 하는 걸까 했는데

state에 로컬 스토리지를 불러와서 담으면 되던 것이었다..

하지만 로컬스토리지에 state를 담을 때는 주의사항이 있었다.

 

[이전 코드]

  this.setState = function (nextData) {
    this.data = nextData
    localStorage.setItem('todos', JSON.stringify(this.data))
    this.setState(update)
  }

처음엔 위와 같이 직접적으로 담았지만

로컬 스토리지를 다루는 부분은 별도의 함수로 분리하는 것이 안전하다.

 

[이전 코드]

  this.$target = $target
  this.data = JSON.parse(localStorage.getItem('todos')) || []

그리고 데이터 또한 바로 json을 불러와서 담았는데 이렇게 안전장치 없이

state에 바로 로컬 스토리지를 넣으면 🙅위험🙅하다.

 

만약 json의 데이터가 이상한 값이라면? JSON.parse()가 에러를 던지고 앱이 동작되지 않는다.

에러를 먼저 던져버리기 때문에 뒤의 || [] 는 의미가 없어진다.

 

✅ 수정된 코드

function getStorage() {
  let data = []
  if (localStorage.getItem('todos')) {
    data = JSON.parse(localStorage.getItem('todos'))
  }
  return typeof data === 'object' ? data : []
}

function setStorage(data) {
  localStorage.setItem('todos', JSON.stringify(data))
}

이렇게 storage에 관련된 함수를 따로 생성하여 데이터 체크를 해 주었다!

 


4. innerHTML의 최소화

최소화의 기준이 어떻게 되는 걸까 하고 진짜 극 최소화를 시키자 하고

처음에 전부 append로 엘리먼트들을 담아서 표현했었는데

가독성이 떨어져서 이게 맞나 의문이 들어서 일부 코드를 변경했었다..

각 컴포넌트에서 1회만 접근해서 사용하는 것이 좋다는 답변을 받았다!

 


5. 번들러 없이 export 사용

번들러를 사용하지 않고 import, export가 되는지 이번에 알게 되었다.

 

<script src="./main.js" type="module"></script>

이렇게 Html안에 script를 넣어주고 type속성에 module로 적어주면 export가 가능하다.

 

import SearchHistory from './searchHistory.js'
import SearchInput from './SearchInput.js'

처음에 뒤에 확장자를 안붙여주어서 불러와지지 않았었는데,

node.js에서와는 달리 뒤에 확장자를 꼭 붙여 주어야 한다.

 


 

✍ mission 2를 진행하면서 느낀 점

vanilla javascript로 컴포넌트 만들기를 해본 적이 없었고

생성자함수를 사용하는 것이 익숙치가 않아서 이번에 조금이나마 감이 생긴 것 같았다.

평소에 사용해 보고 싶었던 this도 사용해 보고 컴포넌트의 구조에 대해 이전보다는 이해가 되었다.

처음에 쓴 코드가 좀 복잡했었는데 구조를 어떻게 잡냐에 따라

코드가 엄청 복잡해질 수도 있다고 느꼈다.

그리고 처음에 코드가 이상하다고 느끼면 나중에 봐도 이상하다..

그리고 보통 이상한 건 이상하게 만든 게 맞았다..

그래도 리뷰를 받고 하나씩 고쳐나가는 것이 혼자 할 때의 답답함이 조금은 해소되는 듯했다.

다음 미션도 열심히 해봐야겠다👊👊

Comments