Next.js 공식문서를 보면서 커머스 사이트(와이즐리) 구현하기 (5)

undefined·2022년 12월 5일
1

추천 상품 페이지이면서 동시에 메인 페이지 작업이 끝났습니다.

아래의 화면에서 사용자에게 제공해야 할 3개의 데이터가 눈에 띄네요.

  • 장바구니에 담긴 상품의 개수
  • 메인 배너(슬라이더) 이미지
  • 추천 상품 리스트

이 데이터를 제공하기 위해서 서버로 부터 상품 데이터를 가져와야 합니다.

레퍼런스 사이트의 바텀 네비게이션 중 카테고리를 클릭해 보면
상품은 대략 아래의 구조로 되어 있는것을 확인 할 수 있습니다.

상품 대분류 카테고리 > 상품 세분류 카테고리 > 상품

먼저 카테고리의 정보를 가져와서 메뉴를 구성해주어야 할 것 같습니다.
Next.js에서는 어떻게 데이터 페칭을 할 수 있을까요?

이전 포스팅에서 설명한대로 아래 3가지 중 하나의 방식을 고려해야 합니다.

  1. SSG (빌드시에 단 1번 Fetching)
  2. SSR (페이지 Request시에 Fetching)
  3. CSR (수시로 Fetching)

특히 카테고리의 경우 해당 사이트가 어떤 상품들을 취급하고 있는지 검색엔진에 제공해야 할 중요한 데이터가 될 수 있기 때문에 SSR/SSG 방식 중 하나를 사용하는 것이 좋습니다.

아래와 같이 간략하게 만들어봅시다.

Ant Design의 Menu Component를 사용하였습니다.

각 메뉴의 동작은 두 가지로 정의 되네요.
1. 해당 링크로 이동한다.
2. 서브메뉴를 펼친다

데이터를 제공해줄 임시 테스트서버(Springboot + JPA)를 먼저 만들었습니다.

아래와 같이 데이터를 주는 API를 생성하고요

getServerSideProps를 사용해서 해당 데이터를 가져와 봅니다.
페이지 접근 시마다 요청하는 것이 보입니다.

getStaticProps로 대체하여 비교해 보았습니다.
아래와 같이 빌드시에 한번 데이터를 가져옵니다.

그리고 그 이후엔 서버에 요청을 하지 않습니다.
데이터 수정이 이뤄진 후에는 한번 더 리빌드가 필요하겠네요.

하지만 MD와 마케터분들의 열정이 넘치시면 제품의 카테고리가 자주 추가되거나 삭제가 되는건 불가피 하겠죠?
그럴때마다 새로 빌드를 하기엔 부담스럽습니다.

그렇다면 getServerSideProps로 SSR 방식을 사용해야 하는건지..에 대한 고민을 하고 있을 때 문서에서는 다음과 같은 방법을 제안하고 있습니다.

ISR

Incremental Static Regeneration

ISR(Incremental Static Regeneration)을 통해 전체의 사이트를 리빌드 할 필요 없이 페이지 단위로 static generation을 가능하도록 해준다고 합니다.

사용방법도 간단합니다. revalidate 속성에 시간을 지정해주면 되는데요.
예제처럼 10초로 설정한다면 페이지의 요청이 있을 때 10초를 기준으로 데이터 revalidate를 수행하게 됩니다.

(해당 페이지 안에 머무르고 있거나, 다른 페이지에 있을 때는 요청하지 않아서 과부하를 막아줍니다.)

Client-side data fetching

마지막으로 유저의 이벤트 수행으로 인한 데이터의 잦은 변경 사항을 화면에 즉각 반영해야 한다면 어떻게 해야 할까요?

우리의 화면에서 그렇게 처리해야 할 곳이 1군데 있습니다.
바로 장바구니 입니다.

카트 아이콘 우측상단의 뱃지 컴포넌트를 통해 장바구니에 담긴 상품의 개수를 보여주어야 하는데요.

사용자(Client)의 동작 수행으로 인한 데이터 변경이니 CSR 방식을 통해 처리 하도록 합니다.

Client-side data fetching with SWR

보통 아래처럼 useEffect 훅을 통해 데이터를 가져오는것이 익숙하실 겁니다.

dependencies array에 state, props를 추가하고 값 변동이 있을 시 재수행하도록 할텐데, 최근 리액트 진영에서 이것을 권장하지 않는다는 영상을 본적이 있네요.

시간나시면 아래의 영상 보세요
https://www.youtube.com/watch?v=gb2DGJB1ZsM

넥스트 진영에서는 SWR을 사용할 것을 highly recommend하고 있습니다.(그냥 recommend도 아니고)

SWR의 사용방법을 익히기 위해서 SWR 공식 문서를 참조해야겠네요.

https://swr.vercel.app/

swr은 넥스트를 만든 vercel에서 배포한 data fetching hook으로

넥스트로 서비스를 구현한다면 반드시 사용을 고려해보는 것이 좋을 것 같습니다.
(캐싱으로 요청 횟수 최적화, 자동 revalidation 등 여러 장점이 많습니다.)

SWR - LocalStorage

서비스 각각의 정책에 따라 다르겠지만
보통의 커머스 사이트는 회원이 아닌 방문자에게도 장바구니 기능을 이용할 수 있도록 하고 있습니다.

(29cm 같은 경우는 회원들만 이용할 수 있네요!)

어떻게 하면 불특정 방문자가 장바구니를 이용하고 상품을 관리할 수 있을까요?

브라우저 스토리지를 이용하면 될 것 같습니다.
(세션 스토리지, 로컬 스토리지, Indexed DB 등등..)

세션 스토리지는 탭을 종료하면 데이터가 소실되어 버립니다. 반면에 로컬 스토리지는 보존 되고요.

다만 두개의 스토리지는 데이터를 문자열 형태로만 저장할 수 있고 저장 용량도 적습니다.

보안에 일부 취약한 점도 있어서 Indexed DB 사용이 권장되는 추세입니다.

하지만 장바구니에 담긴 상품 정보가 보안에 그렇게 민감하지 않고 대용량을 요구하지는 않기 때문에 여기서는 로컬스토리지를 사용하도록 하겠습니다.

물론 비회원에 한해 로컬 스토리지를 사용하고 로그인한 회원일 경우 서버 데이터베이스를 사용해야 합니다.

리액트에서 localStorage 사용할 때 주의점

리액트에서 로컬 스토리지를 사용할 때 주의할 점이 있습니다.
로컬스토리지는 window 객체에 포함되는데 컴포넌트가 마운트(렌더링) 되기 전까지는 window 객체를 찾을 수 없습니다. (실행 환경이 브라우저인것도 알지 못하는 상태)

그래서 아래와 같은 오류가 납니다.

해결책은 useEffect 훅을 사용해서 렌더링 종료 이후 로컬스토리지에 접근하는 방법이 있고,

아래와 같이 로컬스토리지 접근 로직을 따로 구성한 후

const { data = [] } = useSWR("waikiki_basket_guest", storage);

와 같이 구조분해 할당 하는 부분의 디폴트 값을 지정해주면 해당 오류를 막을 수 있었습니다.

아래와 같이 코드를 만들었구요.

// lib/storage.js

export default async function storage(key) {
  const value = localStorage.getItem(key);
  if (!value) return undefined;
  return JSON.parse(value);
}
// addCart 함수를 사용해서 로컬스토리지에 데이터를 추가하고 swr mutate
const { data = [] } = useSWR("waikiki_basket_guest", storage);

const addCart = (code) => {
    messageApi.info("상품이 장바구니에 담겼습니다.");
    data.push(code);
    localStorage.setItem("waikiki_basket_guest", JSON.stringify(data));
    mutate("waikiki_basket_guest");
  };

장바구니 상태가 즉각 리렌더링 되는 것을 확인할 수 있네요.

점점 기능이 구체화되어 갈 수록 확인해야 할 부분이 많아지네요.

다음편도 잘 준비해 보도록 하겠습니다.
감사합니다.

profile
여러 고민의 흔적

0개의 댓글