[TIL] 2022-04-12/13 / 17, 18일차

미음제

·

2022. 4. 14. 00:04

 

오늘 배운 내용

 

커스텀 이벤트, dispatch event

 

간단한 에디터를 만드는 강의에서 처음 등장한 개념이다. "커스텀 이벤트". 말 그대로 사용자 지정의 이벤트 함수이다. 이미 제공하고 있는 "click", "submit" 등 외에도 사용자가 이벤트를 직접 만들어 사용할 수 있다.

 

강의 내용을 빌리자면, 에디터 편집 화면에서 글 목록으로 돌아가는 버튼을 누르는 요청이 들어오면 history api를 수정하고 라우팅을 호출하는 이벤트를 호출해야 한다. 함수는 한 가지 일만 하도록 작성해야 하는데, 클릭 이벤트 함수에 history api를 수정하고 라우팅을 하는 것은 적절하지 않다(라고 생각합니다.. 맞는진 모르겠으나). 이 동작을 제어하는 특정 함수를 따로 호출하고 싶을 때 커스텀 이벤트를 사용하는 것이다.

 

커스텀 이벤트를 정의한 후 이벤트를 걸어준 뒤 dispatch(trigger 역할)을 하는 함수를 실행해주면 원하는 타이밍에 동작을 제어할 수 있게 된다.

 

new CustomEvent(typeArg, customEventInit);

 

커스텀 이벤트는 생성자 함수로 선언하며, 이벤트 인스턴스를 생성한다. 인자로 2개의 인자를 받을 수 있는데 각각은 다음과 같다.

  • typeArg : 커스텀 이벤트 이름
  • customEventInit : 선택 사항으로 'detail'이란 프로퍼티를 갖는다. 이때 설정한 detail은 event 객체를 통해 접근할 수 있다.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <main id="app"></main>
    <script type="module" src="./index.js"></script>
</body>
</html>

 

// index.js
const $target = document.querySelector('#app');
const $div = document.createElement('div');
$target.appendChild($div);


$div.innerHTML = '<button class="button">test button</button>'

window.addEventListener('myEvent', (e) => {
    console.log(e);
    console.log(e.detail);
});

const customEventTrigger = () => {
    window.dispatchEvent(new CustomEvent('myEvent',{
        detail:{
            name: "커스텀 이벤트"
        }
    }));
};
$div.addEventListener('click', (e) => {
    const { target } = e;
    if(target.classList.contains("button")){
        customEventTrigger();
    }
});

 

이런 간단한 구조가 있다고 생각하고, 해당 코드를 실행한 뒤 버튼을 클릭하면 다음과 같은 결과가 나온다.

 

 

전역 객체에 'myEvent'를 걸어두고, trigger 함수인 customEventTrigger 함수를 정의해 둔다. 그리고 버튼이 클릭되면 해당 함수를 실행하도록 설정해둔다. 이렇게 설정된 상태에서 버튼을 클릭하면 click 이벤트가 호출됨에 따라 trigger 이벤트가 호출된다. 

 


 

재귀로 트리 구조의 html dom 생성하기

 

노션 클론 코딩 개인 프로젝트가 시작되었고 어제부터 진행하게 되었다. 처음 화면 레이아웃을 Html을 작성해 디자인한 후 CSS를 적용하고 더미 데이터를 통해 잘 들어오는지 실행해보았다.

 

<body>
    <main id="app">
        <div class="list-container">
            <ul>
                <li>
                    test 1
                    <ul>
                        <li>test 1의 test 1</li>
                    </ul>
                </li>
                <li>test 1</li>
                <li>test 1</li>
            </ul>
        </div>
        <div class="editor-cotainer">
            <input name="title" type="text" placeholder="Add title">
            <textarea name="content" placeholder="Add content"></textarea>
        </div>
    </main>
</body>

 

이런 구조가 될 수 있도록 코드를 작성하기로 했다. main.js 파일에서 App 컴포넌트를 호출한 후 App 컴포넌트에서는 list-container를 그리는 컴포넌트를 호출해 작성했다. 더미 데이터를 map 함수로 순회하면서 각각의 데이터의 title을 li 태그의 text로 지정하기로 했다.

 

//App.js
import ListContainer from "./ListContainer.js";
export default function App({
    $target
}){
    const $listContainer = document.createElement('div');
    $target.appendChild($listContainer);
    $listContainer.classList.add("list-container");
    const DUMMY_DATA = [
        {
          "id": 1, // Document id
          "title": "노션을 만들자", // Document title
          "documents": [
            {
              "id": 2,
              "title": "블라블라",
              "documents": [
                {
                  "id": 3,
                  "title": "함냐함냐",
                  "documents": []
                }
              ]
            }
          ]
        },
        {
          "id": 4,
          "title": "hello!",
          "documents": []
        }
    ];
    new ListContainer({
        $target: $listContainer,
        initialState: DUMMY_DATA
    });
}
// ListContainer.js
export default function ListContainer({
    $target,
    initialState
}){
    const $documentList = document.createElement('div');
    $target.append($documentList);

    this.state = initialState;
    this.setState = (nextState) => {
        this.state = nextState;
        this.render();
    };

    this.render = () => {
        $documentList.innerHTML = `
            <ul>
                ${this.state.map(item => 
                    `<li data-id=${item.id}>${item.title}</li>`
                ).join('')}
            </ul>
        `;
    }

    this.render();
}

 

CSS를 약간 수정하고, 코드를 실행해보니 원하는 결과대로 작성되지 않았다. 더미 데이터에서 부모였던(id 1번과, 4번)것만 li 태그로 작성되었고, 자식들은 작성되지 않았다.

 

해당 문서가 부모일 때, 자식 문서를 갖고 있는 경우 해당 태그의 하위 트리로 구성을 해주어야 했다. 화면 렌더링에서 데이터 객체를 여러 번 순회하는 것은 좋지 않고 한 번에 모두 그려주어야 하는데, 어떻게 작성해야 할지 몰라 하루 동안 완성하지 못했고, 오늘 고민 끝에 해결하게 되었다.

 

과제 기본 요구사항 중 문서를 렌더링하는 것에 대한 부분은 다음과 같이 기술되어 있다.

 

화면 좌측에 Root Documents를 불러오는 API를 통해 루트 Documents를 렌더링 합니다.
해당 Root Document에 하위 Document가 있는 경우, 해당 Document 아래에 트리 형태로 렌더링 합니다.

 

데이터 객체를 순회하면서 하위 documents가 있는 경우, ul 태그를 작성하고 title을 text로 작성한 후, 하위 ul 태그를 작성한 뒤 해당 함수를 다시 호출한다. 만약 하위 documents가 없는 경우에는 li 태그를 작성하고 title을 text로 작성하도록 한다.

 

// CreatList.js
export const createList = (data) => {
    return `
        ${data.map(item => {
            const { id, title, documents } = item;
            if(documents.length > 0){
                return `
                    <ul class="documents "data-id=${id}>${title}</ul>
                        <ul>${createList(documents)}</ul>
                `
            } else {
                return `<li class="documents "data-id=${id}>${title}</li>`
            }
        }).join('')}
    `
};

 

import { createList } from "./utils/CreateList.js";
export default function ListContainer({
    $target,
    initialState
}){
    const $documentList = document.createElement('div');
    $target.append($documentList);

    this.state = initialState;
    this.setState = (nextState) => {
        this.state = nextState;
        this.render();
    };

    this.render = () => {
        $documentList.innerHTML = `${createList(this.state)}`;
    }

    this.render();
}

 

이렇게 리스트를 작성하는 함수를 작성하고, 해당 컴포넌트에서 렌더링 하는 곳에서 호출하면 원하는 결과를 얻을 수 있다. 트리 구조의 힌트를 보고 재귀 함수로 구현해 성공했다. 아직까지는 잘 동작되지만 이후 url 라우팅과 새로운 문서가 추가되었을 때도 잘 동작할지는 모르겠다.

 


 

POSTMAN Header와 Body 추가하는 방법

 

노션 프로젝트를 시작하고 API 명세대로 작성해야 해서 POSTMAN으로 데이터가 잘 전송되고, 받아져 오는지 확인할 필요가 있었다. 코드를 작성할 때, fetch 함수를 통해 options 객체를 전달해 사용하는 방식을 POSTMAN에서는 다음과 같이 수정해 사용할 수 있다.

 

 

Header를 추가하는 방법

 

 

Body를 추가하는 방법

 

 


 

부족했던 점

  • history api
  • 코드 리뷰

 

느낀 점

 

정신없이 시작했다. 코로나 확진으로 지난주부터 이번 주 월요일까지 너무 정신이 없었다. 강의 내용을 따라잡기도 벅찼고, 3주 차 과제에 대한 피드백도 달리고 있었고, 화요일부터는 개인 프로젝트가 시작되었다. 간단한 컴포넌트를 API로 데이터를 받아 온 후 렌더링 하는데 2일이라는 시간을 사용했다. 완성도로 치자면 아직 10%도 하지 못한 것인데 20일까지 어느 정도의 완성품을 만들 수 있을지 걱정이 앞선다. 주말에는 코드 리뷰 반영부터 하고 나머지는 모두 프로젝트에 시간을 쏟아야 할 것 같다.

 

애매한 타이밍이긴 하지만 간단히 회고해보자면, 지난 시간 동안 많은 것을 배웠고(배운 것도 있겠지만 보고 지나간 게 더 많지 않은지.. 복습할 내용도 많고 찾아봐야 할 내용도 많다) 조금은 발전했다고 느꼈다. 발전이라 하기에는 다른 사람들은 이미 다들 하는 것이지만 나에겐 그렇다. 자꾸 우아한테크코스를 떨어졌던 것과 비교하게 되는데, 그때 당시와 비교하면 발전한 게 맞다. 그때는 화면 레이아웃을 구성하는데 하루 종일 걸렸고, click 이벤트 처리를 하는데 하루 종일 걸렸고, localStorage에서 데이터를 저장하고 불러오는데도 실패했었다. 그 결과 당연히 탈락했었고.

 

그때에 비하면 많이 발전했다. 더미 데이터를 통해 미리 화면 레이아웃을 그릴 수 있게 되었고, API 요청을 통해 데이터를 받아와 화면에 렌더링 할 수 있게 되었다. 우아한테크코스 프리코스 당시에 못했던 것들, 그리고 인턴 근무 당시 제일 어려웠던 API를 통해 데이터를 받아오는 것 등 못했던 부분들이 가능해지고 있다는 것이 보이기 시작하니까 새로운 재미를 느끼게 되었다. 알고리즘, CS 수업을 들었을 때 한 번 그리고 최근 To do App, 자동 편집기 만드는 강의를 들으며 두 번 "잘할 수 있는 건가, 잘하고 있는 건가" 회의적인 생각을 하기도 했었다. 그때 익혔던 트리 구조에서의 재귀 함수 호출, API 요청을 통해 렌더링을 적용하는 것을 보고 회의적인 생각이 흥미로 바뀌었다. 이 흥미를 계속 유지하길 바라며 남은 과제를 진행하고 싶다.

 

 

 

반응형

'프로그래머스 > 데브코스 프론트엔드' 카테고리의 다른 글

[TIL] 2022-04-19 / 22일차  (0) 2022.04.20
[TIL] 2022-04-18 / 21일차  (2) 2022.04.18
[TIL] 2022-04-05 / 12일차  (0) 2022.04.06
[TIL] 2022-04-04 / 11일차  (0) 2022.04.04
[TIL] 2022-04-01 / 10일차  (0) 2022.04.01