๐Ÿฅต์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š”๋ฐ ์‹คํ–‰์ด ๋œ๋‹ค?!๐Ÿฅต

์†”ยท2024๋…„ 1์›” 11์ผ
1

TIL series

๋ชฉ๋ก ๋ณด๊ธฐ
23/28
post-thumbnail

๋‚˜๋ฅผ ํ™˜์žฅํ•˜๊ฒŒ ํ•˜๋Š” ๋นจ๊ฐ„์ค„

์˜ค๋Š˜ ๋ญ”๊ฐ€ ์˜์ฐจ์˜์ฐจํ•ด์„œ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ํ–ˆ๋‹ค. (์˜ค๋Š˜ ์™„์„ฑํ•˜๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ)

์™€ TIL์“ฐ๋ ค๊ณ  ์ฝ”๋“œ ๋ณต์‚ฌํ•˜๋ฉด์„œ ์ž‰?์ด๊ฑฐ ์™œ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋ƒ? ์‹ถ์€๊ฑธ ๊ณ ์ณค๋”๋‹ˆ ์—๋Ÿฌ๊ฐ€ 6๊ฐœ์—์„œ 2๊ฐœ๋กœ ์ค„์—ˆ๋‹ค..!!

๊ทธ๋ž˜์„œ ์˜ค๋Š˜ ๋ฌด์—‡์„ ํ–ˆ๋Š”๊ฐ€

1. ์žฅ๋ฐ”๊ตฌ๋‹ˆ api route ์ž‘์„ฑ

import { supabase } from "@/service/supabase";
import { NextResponse, NextRequest } from "next/server";

export const GET = async (res: NextResponse, context: { params: { userId: string } }) => {
  const {
    params: { userId }
  } = context;
  let { data: cart, error } = await supabase
    .from("cart")
    .select(`*, store(name), product(name, thumbnail, category(category_name), price, percentage_off)`)
    .eq("user_id", userId);
  if (error) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
  return NextResponse.json(cart);
};

http://localhost:3000/api/cart/`userId`๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํ•ด๋‹น ์œ ์ €๊ฐ€ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด์€ ์•„์ดํ…œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž˜ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฐ์ดํ„ฐ๋ฅผ fetch ํ•ด์˜ฌ useCart.ts ์ž‘์„ฑ

import { useQuery } from "@tanstack/react-query";

interface CartBox {
  cart: {
    count: number | null;
    id: string;
    product_id: string | null;
    store_id: string | null;
    user_id: string;
    product: {
      name: string;
      thumbnail: string;
      price: number;
      percentage_off: number;
      category: {
        category_name: string;
      };
    };
    store: {
      name: string;
    };
  };
}

const useCart = (id: string) => {
  const {
    data: cart,
    isLoading,
    isError
  } = useQuery<CartBox[]>({
    queryFn: async (): Promise<CartBox[]> => {
      const response = await fetch(`/api/cart/${id}`, { method: "GET" });
      const data = await response.json();
      return data;
    },
    queryKey: ["cart"]
  });
  // console.log(cart);
  return { cart, isLoading };
};

export default useCart;

3. Cart ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ


์š” ๋…€์„์ด ๊ฒฐ์ œ ํŽ˜์ด์ง€์—์„œ ๋™์ผํ•˜๊ฒŒ ๋“ค์–ด๊ฐ€์„œ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๋Š”๋ฐ ์ผ๋‹จ,, ์ผ๋‹จ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ณ ,,

"use client";
import React, { useState } from "react";
import Image from "next/image";
// import CartItem from "./CartItem";  <- ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“œ๋ ค๊ณ  ํ–ˆ๋˜ ํ”์ ...

import useCart from "@/hooks/useCart";

interface CartBox {
  cart: {
    count: number | null;
    id: string;
    product_id: string | null;
    store_id: string | null;
    user_id: string;
    product: {
      name: string;
      thumbnail: string;
      price: number;
      percentage_off: number;
      category: {
        category_name: string;
      };
    };
    store: {
      name: string;
    };
  };
}

// const CartItem = ({ cart, product, store }: Props) => {
const CartItem = (cart: CartBox) => {
  const { count, id, product_id, store_id, user_id, store, product } = cart.cart;
  const { name: product_name, thumbnail, price, percentage_off, category } = product;
  console.log(price);

  //์ˆ˜์ •ํ•„
  const [cnt, setCnt] = useState(count || 0);
  return (
    <>
      <div className="border border-gray w-[60%] p-2">
        <div className="flex flex-row my-2">
          <div className="mx-5 ">
            <Image src={thumbnail} width={160} height={120} alt="์ƒํ’ˆ๋Œ€ํ‘œ์ด๋ฏธ์ง€" />
          </div>
          <div className="mx-3">
            <p>{category.category_name}</p>
            <p>{store.name}</p>
            <p>๋‚ ์งœ</p>
            <p>{price}์›</p>
            <div className="flex flex-row">
              <button onClick={() => setCnt(cnt - 1)} className="bg-gray-300  w-[25px] h-[25px]">
                -
              </button>

              <p className="w-[25px] h-[25px] text-center border border-gray-300">{cnt}</p>
              <button onClick={() => setCnt(cnt + 1)} className="bg-gray-300  w-[25px] h-[25px]">
                +
              </button>
            </div>
          </div>
        </div>
        <div className="flex flex-row border-t">
          <p>์ƒํ’ˆ๊ธˆ์•ก{price}์›</p>
          <p>์ˆ˜๋Ÿ‰ {cnt}๊ฐœ</p>
          <p>์ด๊ธˆ์•ก{cnt * price}์›</p>
        </div>
      </div>
    </>
  );
};

const page = () => {
  //useCart์— ์‚ฌ์šฉ์ž id
  const { cart, isLoading } = useCart("aba26c49-82c0-42b2-913c-c7676527b553");
  // console.log(cart);
  return (
    <>
      {!isLoading ? (
        <div>
          {(cart as CartBox[]).map((cartItem) => {
            console.log("cartItem:", cartItem);
            return cartItem && <CartItem cart={cartItem} key={cartItem.id} />;		
            //โ†‘์—ฌ๊ธฐ์„œ ์—๋Ÿฌ๊ฐ€ 2๊ฐœ๋‚œ๋‹ค. ๋‚˜์ฆ๋ง ํ™˜์žฅํ•ด
          })}
        </div>
      ) : (
        <div>Loading...</div>
      )}
    </>
  );
};

export default page;

์–ด๋–ค ์—๋Ÿฌ 2๊ฐœ๊ฐ€ ๋‚˜๋ƒ~~

์ฒซ๋ฒˆ์งธ ์—๋Ÿฌ๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๋‘๋ฒˆ์งธ ์—๋Ÿฌ๋Š” ์ •๋ง ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ฝ˜์†”์— key๊ฐ’ ์—†๋‹ค๊ณ  warning๋„ ์•ˆ๋œจ๊ณ  cartItem์„ ์ฝ˜์†”์— ์ฐ์–ด๋ดค์„ ๋•Œ ๋ถ„๋ช…ํžˆ id๊ฐ’์ด ์žˆ๋Š”๋ฐ ์™œ,, ์™œ ์—†๋‹ค๊ณ  ํ•˜๋‹ˆ,,

์‹คํ–‰์€ ๋ฉ€์ฉกํ•˜๊ฒŒ ์ž˜ ๋œ๋‹ค. (๋ถˆ๊ธธํ•ด๋ณด์ด๋Š” warning์€ ์‚ฌ์ง„ ์‚ฌ์ด์ฆˆ ๋•Œ๋ฌธ์ด๋‹ค. ๋‚˜์ค‘์— ์ˆ˜์ •ํ•ด์•ผ๋””)
๊ธฐ๊ณ„์นœ๊ตฌํ•œํ…Œ ๋ฌผ์–ด๋ด๋„ ์‹œ์›ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์–ป์ง€ ๋ชปํ–ˆ๋‹ค.

๋‚ด์ผ ์ ์‹ฌ ๋จน๊ธฐ ์ „์— ๊ผญ ํ•ด๊ฒฐํ•ด์•ผ์ง€. ๊ทธ๋ž˜์„œ ์ €๋… ๋จน๊ธฐ์ „์— ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌํ•˜๊ณ  cart ํŽ˜์ด์ง€ ํ‹€๊นŒ์ง€ ์žก๋Š”๊ฒŒ ๋ชฉํ‘œ!!

0๊ฐœ์˜ ๋Œ“๊ธ€