[shopping mall] Request and response, Refactoring fetch (2021 02 26) - adelakim5/fe-w3-shopping GitHub Wiki

새롭게 구현한 내용

  1. 통신
  2. 각 콘텐츠마다 사용하는 공통된 fetch를 범용화하는 함수

통신

처음엔 app.js에 전역변수를 두고 사용했으나, 여러 자료를 찾아본 결과 routes폴더 안에서 작성하는 것이 맞겠다는 생각이 들었다.

또, 서버에서는 클라이언트가 요청한 데이터를 구별해서 보내주는 편이 좋겠다고 생각하여 다음과 같이 수정했다.

// routes/index.js
const express = require("express");
const router = express.Router();
const fs = require("fs");

router.use(express.json());
const planningEvent = JSON.parse(fs.readFileSync("./data/planningEvent.json")); // data 폴터 안에 있는 planningEvent.json 파일을 읽어옴

router.get("/event.json", function (req, res, next) { // "http://localhost:3000/event.json"으로 요청하면 
  res.json(planningEvent.event); // planningEvent의 event 키값을 가진 데이터만 보냄 
});

그럼, 클라이언트에서는 다음과 같이 요청할 수 있다.

// main.js
fetch("http://localhost:3000/event.json").then(res => res.json()).then(data => data) // planningEvent의 event 키값을 가진 데이터 요청해서 받아옴

범용 함수 작성

근데.. 작성하고 보니 아이템마다 저 구문을 반복하게 된다.

그래서 요청하는 아이템마다 반복되는 공통된 부분을 담당하는 하나의 범용 함수를 만들기로 했다.

const api = (url) => (callback, ...fns) => (arg, ...optionalFns) => 
   fetch(url)
     .then(res => res.json())
     .then(data => {
        callback(...fns)(data, arg); 
        if(optionalFns.length) optionalFns.forEach(fn => fn());
     })
     .catch(err => console.log(err));

구조는 다음과 같이 이뤄진다.

  1. 요청하는 url 주소를 매개변수로 받는다.
  2. res.json() 으로 data를 받는다.
  3. data를 가지고 동작하는 callback함수를 매개변수로 받아 호출한다.
    • 내가 공통적으로 동작시키는 callback함수는 또다른 함수를 매개변수로 받아 동작시키기 때문에 저렇게 적었다.
  4. 만약 또 저 안에서 동작시키고 싶은 함수들이 있다면 해당 함수들을 받아 호출한다.

위 함수를 가지고 재구성하면, fetch는 다음과 같이 작성된다.

// htmlMaker.js
const eventItem = (data) => {
  const { linkurl, imgurl } = data;
  return `<a href="${linkurl}"><img src="${imgurl}"/></a>`;
};

const insertContents = (...htmls) => (...contents) => {
  if (htmls.length !== contents.length) throw new Error("CANNOT INSERT STRS INTO HTMLS");
  htmls.forEach((html, index) => (html.innerHTML = contents[index]));
};

export {eventItem, insertContents};
// setters.js
const setHtmls = (fn1, fn2) => (arg1, arg2) => {
  const result = fn1(arg1);
  fn2(arg2)(result);
};

export {setHtmls};
import * as htmlMaker from "./htmlMaker.js"; // eventItem 함수 불러오기
import {setHtmls, insertContents} from "./setters.js"; // setHtmls, insertContents 함수 불러오기
import api from "./api.js"; // api 불러오기

const urls = {
   event:"http://localhost:3000/event.json"
};

const eventItemHtml = document.querySelector(".event__item")

const eventItem = api(urls.event)(setHtmls, htmlMaker.eventItem, insertContents)(eventItemHtml);
/* 
   1. urls.event로 fetch
   2. api 내부는 아래와 같이 실행됨
      // ...
     .then(data => {
       setHtmls(htmlMaker.eventItem, insertContents) => (data, eventItemHtml) => {
         const result = htmlMaker.eventItem(data);
         insertContents(eventItemHtml)(result);   
       };  
     })
   3. 이에 따라 eventItemHtml에 data가 심어지게 됨 
*/

이렇게 구성하니까 좀 더 범용적으로 사용할 수 있게 되었다.

예를들어, mileageItem, mallEventItem의 경우에는 다음과 같이 작성할 수 있었다.

const mileageItems = api(urls.mileageList)(setHtmls, htmlMaker.mileageListHtml, insertContents)(slideList, registerBasicCarousel); 
// optional 함수를 적음 => 캐러셀 등록하는 함수도 안에서 호출!

const mallEventItems = api(urls.mallEventList)(setHtmls, htmlMaker.mallEventListHtml, insertContents)(mallEventSlideHtml);

근데 아쉬운건..

코드의 가독성이 더더더더더욱 떨어지는 느낌이 드는건 기분탓일까? ㅋㅋㅋㅋㅋ ㅜㅜ

사실 mileageItems의 예시에서 넣은 optional 함수(registerBasicCarousel)에서 사용하는 인자들은 전역변수다(!!).

저 함수가 순수함수가 되려면 해당 함수 안에서 사용되는 인자를 매개변수로 받아오는 부분을 작성해야 하는데, 그러다보니 api 함수의 길이가 끝도 없어지는것..!

무언가 잘못되어가고 있는 것 같은 느낌이 들었다. 좋은 방법이 또 없을까 싶긴 한데, 또 캐러셀은 class로 구현하다 보니까능.. 각기 다른 캐러셀들은 개별적으로 생성해주어야하는데.. 흐엉 ㅜㅜ 건너지 말아야 할 강을 건넌 것 같은 기분이 들었다. 수습할 수 있을까 ..