[코드 리뷰] LOUD 클론 코딩

Carrie·2024년 1월 31일
0
post-thumbnail

1. JSON 비동기 통신을 통해 데이터 바인딩하기

JSON 비동기 통신과 javascript의 fetch API를 사용하여 외부 JSON 파일에서 데이터를 동적으로 로드하고 화면에 표시하는 방법을 구현하였다.

수정 전🤔

function designerData() {
  fetch("assets/json/designerData.json") // fetch를 사용하여 비동기적으로 json 파일 로드
    .then((res) => res.json()) // json 형태로 파싱
    .then((json) => {
      data = json.resultData; // 실제 데이터 추출

      let html = ``;
      data.forEach((element) => {
        const formattedPrize = formatPrize(element.userInfo.totalPrice);
        const label1 = element.label ? element.label[0] : "";
        const label2 = element.label ? (element.label[1] ? element.label[1] : "") : "";
        html += `
          <li>
            ... 생략
          </li>
          `;
      });
      const designerList = document.querySelector(".designer-list");
      designerList.innerHTML = html;
      designerList.innerHTML += html;
      designerList.style.width = "200%";
    });
}
designerData();

수정 후🤗

디자이너 관련 탭이 4개로 구성되어 있었고, 각 탭마다 필요한 디자이너 관련 데이터의 양이 상당히 많았다. 처음에는 모든 데이터를 한 파일에서 관리하려 했으나, 데이터 로딩 속도가 현저히 느려지는 문제가 발생했다. 이에 대한 해결책으로 JSON 파일을 여러개로 나누고, 필요한 데이터만을 선택적으로 로드할 수 있도록 수정하였다.

$(".sc-designer .tab-list li").click(function () {
  const tabIndex = $(this).index(); // 클릭한 탭의 인덱스를 가져온다.
  const element = $(this).closest(".header").find("strong");
  const text = $(this).text();

  designerData(tabIndex, text); // 탭 인덱스를 파라미터로 하여 designerData 함수 호출
  element.text(text);
  $(this).addClass("active").siblings().removeClass("active");
});

function designerData(tabIndex = 0, text) {
  const jsonFiles = [
    "assets/json/designerData1.json", // 각 탭마다 JSON 파일 별도로 분리
    "assets/json/designerData2.json",
    "assets/json/designerData3.json",
    "assets/json/designerData4.json",
  ];
  const jsonData = jsonFiles[tabIndex]; // 클릭한 탭 인덱스에 해당하는 파일을 변수에 할당
  
  fetch(jsonData)
    .then((res) => res.json())
    .then((json) => {
      const data = json.resultData;
    .
    .
    .
    생략

📌 fecth를 사용해 데이터를 바인딩하는 과정 살펴보기

fetch(jsonData)
👉 fetch는 promise를 반환한다. 네트워크 요청이 성공적으로 완료된 경우 .then() 메소드가 호출된다.

.then((res) => res.json())
👉 .then() 메소드는 응답 객체 (res)를 인자로 받는다. res.json() 메소드는 본문을 JSON 형태로 파싱(변환)한다. 이 메소드 역시 promise를 반환한다.

.then((json) => { ... }
👉 promise가 성공적으로 반환되면, 다음 then() 메소드가 호출된다. 이 때 파싱된 JSON 객체 (json)을 인자로 받는다. 이 JSON 객체는 실제로 필요한 데이터를 담고 있다.

💡fetch란?
fetch는 비동기적으로 네트워크 통신을 수행하여 데이터를 가져오는 함수이다. 또한, HTTP 요청을 쉽게 할 수 있게 해주며, Promise 기반의 API로 비동기 작업을 간결하고 효율적으로 처리할 수 있도록 해준다.
그럼 Promise는?
비동기 작업의 최종 성공 또는 실패를 나타내는 객체로, 비동기 작업이 완료되면 그 결과에 따라 resolve(성공) 또는 reject(실패) 상태가 된다.

2. GSAP 라이브러리로 숫자 카운터 구현하기

gsap과 ScrollTrigger를 사용하여 숫자가 증가하는 애니메이션을 구현하였는데, 이 과정에서 소수점 값을 처리하는 로직에 개선이 필요했다. 우선 처음의 코드는 아래와 같다.

수정 전🤔

처음에는 counter 값이 목표값보다 작을 때 Math.round()를 사용해서 정수로 표시하고, counter 값과 목표값이 같을 때는 다시 원래의 목표값을 표시하는 방식으로 구현했었다. 이 방식의 경우 업데이트 되는 과정에서 소수점 값을 유지해야 하는 특정 숫자("98.7%") 표시에 문제가 있었다.

const counter = { counter: 0 };
function countUp(element, number, unit) {
  gsap.to(counter, {
    counter: number,
    duration: 2,
    scrollTrigger: {
      trigger: ".sc-count",
      start: "0% 100%",
      end: "100% 0%",
      toggleActions: "play none none reverse",
      // markers: true,
    },
    onUpdate: () => {
      if (counter.counter < number) {
        document.querySelector(element).innerHTML = Math.round(counter.counter) + unit;
      } else {
        document.querySelector(element).innerHTML = counter.counter + unit;
      }
    },
  });
}
countUp(".count-designer", 24, "만 명+");
countUp(".count-request", 4, "만 건+");
countUp(".count-satisfaction", 98.7, "%");
countUp(".count-domestic", 80, "%");

수정 후🤗

Number.isInteger(value)를 사용하여 목표값이 정수인지 소수인지를 판단하고, 소수인 경우 소수점 한자리까지 표시하는 방식으로 수정하였다. 이렇게 수정함으로써 모든 숫자 카운터가 목표값에 도달할 때까지 정확한 형식으로 표시될 수 있도록 하였고, 사용자에게도 정확한 정보를 전달할 수 있게 되었다.

const counter = { counter: 0 };
function countUp(selector, value, unit) {
  const element = document.querySelector(selector);
  const isInteger = Number.isInteger(value);
  gsap.to(counter, {
    counter: value,
    duration: 2,
    scrollTrigger: {
      trigger: ".sc-count",
      start: "0% 100%",
      end: "100% 0%",
      // markers: true,
    },
    onUpdate: () => {
      element.innerHTML = isInteger
        ? counter.counter.toFixed() + unit
        : counter.counter.toFixed(1) + unit;
    },
  });
}
countUp(".count-designer", 24, "만 명+");
countUp(".count-request", 4, "만 건+");
countUp(".count-satisfaction", 98.7, "%");
countUp(".count-domestic", 80, "%");
profile
Markup Developer🧑‍💻

0개의 댓글