vanilla javascript project refactoring

lbr·2022년 8월 17일
0

nyt project refactoring

input 입력 후 0.5초동안 추가입력이 없는 경우에만 api를 호출

개선 가능성

클로저를 활용하여 함수형 코딩과 로직의 재활용에 용이하게 설계할 수 있고, 반복적인 호출작업을 좀 더 직관적으로 보이게 할 수 있습니다.

내 코드 리팩토링

리팩토링 전

searchInputEl.addEventListener("input", () => {
  // setTimeout 함수를 취소합니다.
  if (setTimeoutId) {
    clearTimeout(setTimeoutId);
    setTimeoutId = null;
  }

  // 0.5초 후 검색을 진행합니다.
  const searchkeyword = searchInputEl.value.trim();
  if (searchkeyword) {
    setTimeoutId = setTimeout(() => {
      store.page = 1;
      printNewsList(
        NEWS_URL.replace("@searchkeyword", searchkeyword),
        NEW_SEARCH
      );
      // 검색에 성공했으므로 검색어를 배열에 저장하고 출력하는 함수로 보냅니다.
      saveSearchWordtoHistory(searchkeyword);

      // 수행된 검색에 대한 결과 정보 출력합니다.
      searchResult();
    }, 500);
  }
});

리팩토링 후

const processChanges = debounce(() => processFlow());

function debounce(callback) {
  let timeout;

  return () => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      callback();
    }, 500);
  };
}

searchInputEl.addEventListener("input", () => {
  processChanges();
});

function processFlow() {
  const searchkeyword = searchInputEl.value.trim();

  store.page = 1;
  printNewsList(NEWS_URL.replace("@searchkeyword", searchkeyword), NEW_SEARCH);
  // 검색에 성공했으므로 검색어를 배열에 저장하고 출력하는 함수로 보냅니다.
  saveSearchWordtoHistory(searchkeyword);
  // 수행된 검색에 대한 결과 정보 출력합니다.
  searchResult();
}

target="_blank"의 문제점

  1. 보안상 취약점이 생긴다
  2. 퍼포먼스가 떨어질 수 있다

문제점 해결 방법

noopener 지정

noopener(노오프너)를 지정하면, 링크된 페이지에서 window.opener을 사용해서 링크를 건 페이지를 참조(reference)할 수 없게 됩니다. 더불어 링크된 페이지와 링크를 건 페이지는 별개의 프로세스로 취급되기 때문에 서로 연결되어 퍼포먼스가 떨어지는 일도 없게 됩니다.

신뢰할 수 없는 페이지로 이동하는 링크를 부득이하게 만들어야 할 때 사용하면 유용하겠죠?

noreferrer 지정

noreferrer(노리퍼러)를 지정하면 다른 페이지로 이동할 때, 링크를 건 페이지의 주소 등의 정보를 브라우저가 Referer: HTTP 헤더로 리퍼러(referer 또는 referrer)로서 송신하지 않습니다.

지정법

코드는 아래와 같이 작성합니다. rel 속성은 링크된 페이지(href="연결할 페이지의 URL")와의 관계(relationship)를 나타냅니다. 그리고 공백으로 구분해서 관계 목록을 나열하여 지정할 수 있습니다.

<a href="연결할 페이지의 URL" target="_blank" rel="noopener noreferrer">새 탭에서 보기</a>

참조 https://joshua-dev-story.blogspot.com/2020/12/html-rel-noopener-noreferrer.html

내 코드 리팩토링

새 창으로 열리는 a 태그에 rel = noopener noreferrer 를 추가했습니다.

리팩토링 전

// 뉴스보러가기 anchor를 생성합니다.
    const a = document.createElement("a");
    a.href = store.newsList[i].web_url;
    a.target = "_blank";
    a.title = "open in new window";
    rightdiv.appendChild(a);

리팩토링 후

// 뉴스보러가기 anchor를 생성합니다.
    const a = document.createElement("a");
    a.href = store.newsList[i].web_url;
    a.target = "_blank";
    a.rel = "noopener noreferrer"; // 리팩토링: 추가
    a.title = "open in new window";
    rightdiv.appendChild(a);

api token이 git에 포함시키지 않고, 쉽게 교체할 수 있는 구조

내 코드 리팩토링

app.js에 있는 API token을 따로 api.js 파일로 분리 및 import export를 활용하여 쉽게 교체할 수 있게 수정하고, .gitignore 를 이용하여 해당 api.js 파일을 git에서 제외시킬 수 있도록 수정했습니다.

  1. 새로 api.js 파일 생성
const API_TOKEN = "api 키";

export default API_TOKEN;
  1. 수정된 app.js
import API_TOKEN from "./api.js";
// ...
  1. .gitignore에 api.js 파일 추가

검색창 이벤트 수정

검색창 addEventListener의 이벤트로 기존의 keyupinput 으로 수정했습니다.

문제점 해결 방법

HTMLElement: input event

input 이벤트는 <input>, <select><textarea> 요소의 value 속성이 바뀔 때마다 발생한다.

또한, 이 이벤트는 아무 요소의 contenteditable 속성 및 designMode 속성이 활성화 되어도 발생할 수 있다. 이런 경우, contenteditableesignMode, 속성이 활성화된 자식을 가진 편집 불가능한 최초 부모 요소에서 발생한다. 예를 들어 특정 요소에서 부모자식관계 중 자식 관계 여러개가 해당 속성이 활성화되어 내용 변경 시 이벤트가 발생해야 할 때, 해당 속성이 활성화되지 않은 최초의 부모 요소를 기준으로 발생하게 된다.

type=checkboxtype=radio, 속성을 가진 <input> 요소의 경우, HTML5 규격에 의하면, input 이벤트는 반드시 사용자가 작동시킬 때마다 발생된다. 하지만 애초부터 그렇게 설계되어 있지 않은 경우가 있으므로, 반드시 아래 호환성 문단을 참고하거나, 호환되지 않을 경우, change 이벤트를 대신해서 사용하도록 한다.

참고 : 참고: The input 이벤트는 change 이벤트와는 달리 value 속성이 바뀔 시마다 반드시 일어난다. 값을 선택하거나 옵션 선택하자마자 일어나지만, 특정 글자를 입력 시에는 입력이 끝나고 value 속성에 적용되어야 발생하는데, 예를 들면, 한글 입력의 경우 한글자가 완성된 뒤 다른 키를 입력(예: 엔터 키)이 일어나야 발생된다. 이 또한 브라우저마다 다르므로 호환성을 확인하여 대응해야 한다. (역자 주)

내 코드 리팩토링

리팩토링 전

searchInputEl.addEventListener("keyup", (e) => {
  // 유저의 입력으로 input Element의 값이 변경되는 경우에만
  // event가 동작하도록 하기 위해 값의 변경과 관련없는 키들을
  // if문으로 검사하여 return으로 동작하지 않게 했습니다.
  if (
    e.key === "Tab" ||
    e.key === "CapsLock" ||
    e.key === "Shift" ||
	// ...
  ) {
    return;
  }
  // ...
}

리팩토링 후

searchInputEl.addEventListener("input", (e) => {
  // event를 input으로하면 실제 값이 바뀔 때에만 동작합니다.
  // Ctrl, Alt..등 값과 관련없는 키에는 반응하지 않습니다.
  // ...
}

0개의 댓글