혼자 적어보는 노트

프로그래머스 데브코스 - 노션 과제 피드백 본문

스터디

프로그래머스 데브코스 - 노션 과제 피드백

jinist 2022. 5. 10. 05:21

 

 

멘토님께 피드백을 받은지는 좀 되었지만 연속되는 과제와 강의^^; 로 조금 늦게 작성을 하게 되었다.

받은 피드백을 다시 한번 상기시키기 위해 적으면서 기록을 해보려고 한다.

 

1. 마크다운 shortcut 부분 코드 수정

 

✍ 기존 코드

export const EditorShortcut = (text) => {
  let tag;

  if (/^#\s/.test(text)) {
    // #
    tag = document.createElement("h1");
    tag.innerText = text.substring(1);

    return tag;
  } else if (/^##\s/.test(text)) {
    // ##
    tag = document.createElement("h2");
    tag.innerText = text.substring(2);

    return tag;
  } else if (/^###\s/.test(text)) {
    // ###
    tag = document.createElement("h3");
    tag.innerText = text.substring(3);

    return tag;
  } else {
    return false;
  }
};

text를 받아와서 처리하는 부분이 조금 반복된다고 느끼기는 했었는데

어떻게 처리를 할 지 고민을 했던 부분이었었다.

멘토님께서 shortcut정보를 한 군데에 모아놓고 관리를 하는게 좋을 것 같다고 피드백을 받았다.

 

✅ 수정된 코드

const shortcutMap = [
  { regex: /^#\s/, tagName: "h1" },
  { regex: /^##\s/, tagName: "h2" },
  { regex: /^###\s/, tagName: "h3" },
];

export const EditorShortcut = (text) => {
  let tag;

  const shortcut = shortcutMap.find((item) => item.regex.test(text));

  if (shortcut) {
    const remove = text.length - 1;
    tag = document.createElement(shortcut.tagName);
    tag.innerText = text.substring(remove);

    return tag;
  } else {
    return false;
  }
};

shortcut의 내용을 보기 쉽게 정리하고 해당 데이터를 토대로

EditorShortcut 함수에서 중복 코드 없이 처리하는 방식으로 변경했다.

subString을 하는 부분도 다시 생각해보니 text의 length로 처리해도 될 듯 하여 수정했다.

 

이전보다는 조금은 보기 편해졌다 😊

 

 

2. title변경 부분 Debounce 주기 낮추기

Editor에서 title 변경 시 Document List에서 보여지는 title부분도 변경되게 적용을 했었는데

Editor에 걸어놓은 디바운스와 같은 주기여서 약간의 딜레이가 있었다.

 

✍ 기존 코드

    onEditing: (document) => {
      debounce(async () => {
        await fetchEditDocument(document);
        changeTitle();
      }, 1000);
    },

분리를 해보려고 살펴보니, Title과 content부분의 수정이 일어났을 때

동일한 onEditing함수를 호출하다보니

content를 변경하는데도 title을 실시간으로 변경시키는 changeTitle을 호출 했다.

 

✅ 수정된 코드

    onEditContent: (document) => {
      debounce(async () => {
        await fetchEditDocument(document);
      }, 1000);
    },
    onEditTitle: (document) => {
      debounce(async () => {
        await fetchEditDocument(document);
        changeTitle();
      }, 300);
    }

코드를 2개로 분리하여 처리했다.

중복되는 함수가 있어서 인자로 document와 title과content를 구분할수 있게 작성을 하려 했지만

로직이 조금 복잡해 지는 듯 하여 조금 직관적으로 작성했다.

 

 

3. 헬퍼 함수 만들어보기

class가 있는 엘리먼트를 생성할 때 util함수로 빼서 생성을 했는데

class를 추가/제거 하는 것도 간단하게 만들어서 사용하면 좋을 것 같다는 피드백을 받았다.

 

✍ 기존 코드

  $sidebar.classList.remove("hide");
  $container.classList.add("hide");

 

✅ 수정된 코드

 

  removeClass($sidebar, "hide");
  addClass($editorContainer, "hide");

코드길이는 비슷하지 않을까? 라고 생각했는데 

행위에 대해 앞부분에 명시적으로 작성이 되어있다보니 확실히 가독성이 좋다는 느낌을 받았다.

 

[util/helper.js]

export const addClass = (element, className) => {
  element.classList.add(className);
}

export const removeClass = (element, className) => {
  element.classList.remove(className);
}

 

 

4. 템플릿 리터럴 수정

post의 list를 보여주는 함수 내부의 템플릿 리터럴이 조금 복잡해져서

문자열을 반환하는 함수를 짜보기로 했다.

 

✍ 기존 코드

  const createPost = (documents, depth) => {
    const MAX_DEPTH = 5;
    const POST_PADDING = depth * 10;

    const selected = getItem(SELECTED_DOCUMENT, null);

    return `
    ${documents
      .map(({ id, title, documents }) => {
        const isOpen = (openList && openList[id]) || false;

        return `<li class="post__item" data-id="${id}">
          <div class="item__content ${selected && selected.id == id ? "selected" : ""}"
          style="padding-left:${depth === MAX_DEPTH ? POST_PADDING + 10 : POST_PADDING}px">
            ${depth < MAX_DEPTH ? `<button class="item__button--toggle"> ${isOpen ? "▼" : "▶"}</button> ` : ""}
            <div class="item__title">${title ? title : "제목 없음"}</div>
            <div class="item__buttons">
              <button class="item__button--remove"><i class="fa-solid fa-trash-can"></i> </button>
              ${depth < MAX_DEPTH ? `<button class="item__button--add"><i class="fa-solid fa-plus"></i></button> ` : ""}
            </div>
          </div>
          ${
            isOpen
              ? documents.length > 0
                ? `<ul>${createPost(documents, depth + 1)}</ul>`
                : `<ul><li class="empty-post-message"> <span>하위 페이지가 없습니다</span></li> <ul>`
              : ""
          }
          </li>
         `;
      })
      .join("")}
    `;
  };

위 코드를 작성할 당시에도 되게 복잡하다고 느끼기는 했었다.

 

✅ 수정된 코드

  const toggleBtn = (isOpen) => {
    return `<button class="item__button--toggle"> ${isOpen ? "▼" : "▶"}</button>`;
  }

  const AddDocumentBtn = () => {
    return '<button class="item__button--add"><i class="fa-solid fa-plus"></i></button>';
  }

  const childDocuments = (documents, depth) => {
    if(documents.length > 0){
      return `<ul>${createPost(documents, depth + 1)}</ul>`;
    } 
    
    return  `<ul><li class="empty-post-message"> <span>하위 페이지가 없습니다</span></li> <ul>`;
  }


  const createPost = (documents, depth) => {
    const MAX_DEPTH = 5;
    const POST_PADDING = depth * 10;

    const selected = getItem(SELECTED_DOCUMENT, null);

    return `
    ${documents
      .map(({ id, title, documents }) => {
        const isOpen = (openList && openList[id]) || false;
        const paddingLeft = depth === MAX_DEPTH ? POST_PADDING + 10 : POST_PADDING;
        const isSelected = selected && selected.id == id ? "selected" : "";
 
        return `
          <li class="post__item" data-id="${id}">
            <div class="item__content ${isSelected}" style="padding-left:${paddingLeft}px">
              ${depth < MAX_DEPTH ? toggleBtn(isOpen) : ""}
              <div class="item__title">${title ? title : "제목 없음"}</div>
              <div class="item__buttons">
                <button class="item__button--remove"><i class="fa-solid fa-trash-can"></i> </button>
                ${depth < MAX_DEPTH ? AddDocumentBtn() : ''}
              </div>
            </div>
            ${isOpen ? childDocuments(documents, depth + 1) : "" }
          </li>
         `;
      })
      .join("")}
    `;
  };

조건에 따라 다른 값을 반환하는 부분들은 함수로 처리해서 조금 정리를 해보았지만

아직은 조금 복잡한 것 같아서 다시 한번 더 시도해봐야 될 것 같다.

 

 

5. tagName/nodeName 비교 시 유틸함수로 변경

 

코드 내에 tagName이나 nodeName을 비교하는 부분들이 있었는데

유틸성 함수로 만들어서 사용해도 좋을 것 같다는 피드백을 받았다.

 

✍ 기존코드

event.target.tagName === "I" ? e.target.closest("button") : e.target;

 

✅ 수정된 코드

compareName(e.target.tagName, "I") ? e.target.closest("button") : e.target;
export const compareName = (name, text) => {
  if(name === text){
    return true;
  }
  return false;
}

 

 

💡 tagName과 nodeName의 차이

 

태그이름을 확인하는 프로퍼티인 tagName과 nodeName의 차이가 궁금해서 알아보니,

tagName은 Element에만 존재하고 nodeName은 모든 Node에 존재한다고 한다.

 

그 차이는 document와 주석 노드에서 확인할 수 있다.

<body><!-- 주석 -->

  <script>
    console.log( document.body.firstChild.tagName ); // undefined
    console.log( document.body.firstChild.nodeName ); // #comment

    console.log( document.tagName ); // undefined 
    console.log( document.nodeName ); // #document
  </script>
</body>

 

Element node만을 확인하는 목적이라면 둘 다 사용해도 된다.

 

 


✍ 느낀 점

코드 리뷰는 매우 좋은 것 같다. 내 코드 뿐만 아니라 다른사람들이 받은 코드 리뷰를 보고도

다시 한번 생각해보는 기회가 생긴다. 그리고 무조건 적용을 직접 해보는 것이 좋은 것 같다.

하는 과정에서도 또 다른 방법들이 생각나기도 하고 적용해보면서 궁금한 부분들이나

팀원들과 논의해 볼 주제들이 생기기도 하고 여러모로 유익한 시간이었다!

Comments