11.0 Product Model
hot reloading을 하면 일종의 서버 재시작이 돼서
PrismaClient가 계속 new로 생성됨.
DB에 커넥션 리밋이 있음. Planet Scale에 리밋이 있을 것이다.
좋지 않은 것이니. 고쳐

항상 다음 순으로 한다.
Model 생성 -> data base 수정 -> mutation -> 데이터 가져오기

Model 생성

model Product {
  id          Int      @id @default(autoincrement())
  created     DateTime @default(now())
  updated     DateTime @updatedAt
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId      Int
  image       String
  name        String
  price       Int
  description String   @db.MediumText

  @@index([userId])
}

 

11.1 Upload Form
11.2 Upload API

  const { name, price, description } = req.body;
  const { user } = req.session;
  // vs
  const {
    body: { name, price, description },
    session: { user },
  } = req;


Prisma client(back end)의 type을 갖다 쓸 수있다.

 

11.3 See Products
프로덕트에서 상품 정보와 상품등록한 계정의 정보 선택적으로 가져오기.

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  console.log(req.query);
  const { id } = req.query;
  const product = await client.product.findUnique({
    where: {
      id: Number(id),
    },
    include: {
      user: {
        select: {
          id: true,
          name: true,
          avatar: true,
        },
      },
    },
  });
  res.json({ ok: true, product });
}

UI: skeleton 로딩화면 tailwind css로 만드는 법
https://www.section.io/engineering-education/skeleton-loading-in-nextjs-with-tailwindcss/

11.4 Product Detail
11.5 Related products

interface extends 해서 type만들기

prisma client의 search 조건
https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#filter-conditions-and-operators

Docs에 있는 것을 아래처럼 구현

 

 

11.6 Favorite Products

import { NextApiRequest, NextApiResponse } from "next";
import withHandler, { ResponseType } from "@libs/server/withHandler";
import client from "@libs/server/client";
import { withApiSession } from "@libs/server/withSession";

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  const {
    query: { id },
    session: { user },
  } = req;
  const alreadyExists = await client.fav.findFirst({
    where: {
      productId: Number(id),
      userId: user?.id,
    },
  });
  if (alreadyExists) {
    await client.fav.delete({
      //delete하려면 unique 필드가 필요함
      where: {
        id: alreadyExists.id,
      },
    });
  } else {
    await client.fav.create({
      data: {
        user: {
          connect: {
            id: Number(user?.id),
          },
        },
        product: {
          connect: {
            id: Number(id),
          },
        },
      },
    });
  }
  res.json({ ok: true });
}
export default withApiSession(
  withHandler({
    methods: ["POST"],
    handler,
  })
);


11.7 Favorite Products part Two
Optimistic UI Update - 어차피 API성공할테니 응답 기다리지 말고 일단 UI바꾸기

  const [toggleFav] = useMutation(`/api/products/${router.query.id}/fav`);
  const onFavClick = () => {
    toggleFav({});
  };

toggleFav({})는 POST니까 바디 보내야 하지만 empty 바디 보내도 됨.


11.8 Bound Mutations

useSWR로 data와 mutate를 받을 수 있다.
mutate()의 처음 인자는 변경할 데이터, 두번째 인자는 해당 API를 재 호출 할지 선택하는 옵션이다. default는 true.
첫번째 mutate함수 처럼 사용하면, 기존의 swr 캐에 저장된 data가 인자 {product:{name:"potato"}}로 치환 되기 때문에 화면에 potato 관련 정보만 보이고 나머지는 다 날아가버린다.
그래서 두번 째 mutate처럼 써야한다.
원래 data를 전개해서 넣고, product안을 수정하는데 product에서도 나머지는 전개해서 넣고, name만 바꾼다.

이건 그냥 자바스크립트 문법.

다음 처럼 하면 favorite이 Optimistic UI Update 되는거야.
캐시만 변경하는거지.
mutate({ ...data, isLiked: !data.isLiked }, false);

두번째 인자가 false니까 다시 api 불러오지 않게
그리고 주석처리 된 toggleFave({})도 주석풀어야 실제 API콜이 될테니까.
최종본

그럼 왜 bound냐? 왜 bound mutation이냐?
useSWR로 받아온 mutate와 짝지어진 데이터만 변경 가능해서.
다른 request로 받은 놈의 cache도 바꾸고싶다?
다른 화면의 다른 요청의 cache를 바꾸고싶다?
unbound mutation !!

11.9 Unound Mutations

import useSWR, { useSWRConfig } from "swr";
const { mutate } = useSWRConfig();
mutate("/api/users/me", (prev) => prev && { ok: !prev.ok }, false);
//prev -> 현재 caching된 데이터 가져오기. 어디서든 접근 가능.
mutate("/api/users/me") //이렇게 하면 그냥 업데이트

 

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

13 Profile  (0) 2023.01.09
12 동네생활  (0) 2023.01.06
10 AUTHORIZATION - SWR  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27
8 REFACTORING  (0) 2022.12.27

authentication = 유저가 누구인지 알아내는 것.
authorization = 유저가 권한이 있는지 알아내는 것.

10.0 Introduction
사용자가
incognito 모드로 접속하면 req.session.user?.id 사용 불가.
따라서 에러가 남. 이렇게 검증되지 않은 요청으로부터 핸들러를 보호해보자.

10.1 Protected Handlers

아래 코드 처럼 flag를 저렇게 넣는건 bad practice임. 
어떤것도 유추 할 수 없으니.

export default withApiSession(withHandler("GET", handler, true, false, true));

그래서 두개 이상의 arguments가 있으면 객체로 설정값을 보내라.

export default withApiSession(
  withHandler({
    method: "GET",
    handler,
    isPrivate: true,
  })
);

그럼 훨씬 self explanatory[ɪk│splænətɔːri] 하지.

10.2 useUser Hook
return router.push("/enter"); push는 페이지 이동이라 브라우저 history에 남음
return router.replace("/enter"); //남지않음
페이지가 성공적으로 로드 됐을 때만 히스토리에 남음

페이지마다 같은 api( 유저정보불러오기)를 다시 불러오면 좋지 않지.
메모리에 담아서 캐싱해. SWR을 써보자.

10.3 SWR (Stale-While-Revalidate = http 캐시 무효화 전략)
SWR은 캐싱된 데이터가 있으면 일단 화면을 그리고,
백그라운드에서 API call을 보내서 
데이터가 바뀐게 있는지 체크하고, 있으면 업데이트 한다.
SWR 설치
npm install swr

참고로 저렇게 중복 메시지가 나오는 이유는 리액트가 컴포넌트 2개 만들었기 때문. 그리고
react strict mode이기 때문. next.config.js에서 reactStrictMode를 false로 바꾸면됨.
근데 true해야 에러 더 잘 잡음.

useSWR 은 처음 인자로 url을 받는데 이를 key라고 하고, 두번째는 함수를 받고 fetcher라고 한다.
그래서 URL을 이용하여 fetcher함수를 통해 api call을 하고 data를 주고 에러가 났을 경우 error를 준다.

앞서 말한 key는 데이터의 id이기도 하다.
useSWR 안에는 super_cache라는게있다.

super_cache = {
	"/api/users/me" : {
        "ok": true,
        "profile": {
            "id": 10,
            "phone": "123456",
            "email": null,
            "name": "Anonymous",
            "avatar": null,
            "created": "2022-12-28T07:04:45.224Z",
            "updated": "2022-12-28T07:04:45.224Z"
        }
    }
}

이 super_cache는 앱 전체에서 공유되기 때문에
앱 어디서든 유저가 해당 url로 api을 하면 캐시를 준다!!!
당연히 업데이트 되면 스리슬쩍 바꿔주지 로딩 보여줄 필요 없이.
mutation도있는데 나중에 like 기능 있을 때보고.

useSWR은 유저가 다른 탭에 갔다가 돌아오면 데이터 새로고침 해줌. ㄷㄷ
아무도 모르게...

10.4 useUser Refactor
근데 매번 fetcher를 쓰는 건 번거로움
그래서 

const fetcher = (url: string) => fetch(url).then((response) => response.json());

그래서 _app.tsx에서 SWRConfig 컴포넌트를  불러와서 최상위 엘리먼트를 감싸면 됨!

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

12 동네생활  (0) 2023.01.06
11 Products  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27
8 REFACTORING  (0) 2022.12.27
7 REACT HOOK FORM  (0) 2022.12.26

+ Recent posts