[최종 프로젝트 - React with typescript] Supabase(2)_DB 가져오기

Habin Lee·2024년 1월 11일
1

Supabase 패키지 설치

  • Supabase에 프로젝트를 만들었으니 이어서 패키지를 설치해주자.
  • 나는 yarn을 사용하기 때문에 yarn으로 설치해줬다.
yarn add @supabase/supabase-js

Supabase 연동

1. URL과 API key 가져오기

  • 앞서 생성한 프로젝트에서 Setting - API에서 Project URL과 API keys를 가져온다.
    (내가 필요한 프로젝트 이름에 맞는 지 확인!)

  • key가 노출되지 않도록 .env 파일에 저장해준다.

  • 이렇게 URL과 API key를 변수로 만들어 준 뒤

2. Supabase 서버와 연결하기

  • src 아래에 utils 폴더를 만들고 SupabaseClient.ts 파일을 생성한 후 아래와 같이 적어서 서버와 연결시켜준다. (우리는 typescript를 사용하여 URL과 API key의 타입도 지정해주었다.)
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = process.env.REACT_APP_SUPABASE_URL as string
const supabaseKey = process.env.REACT_APP_SUPABASE_KEY as string

// Supabase client
const supabase = createClient(supabaseUrl, supabaseKey);

export {supabase}
  • 이렇게 하면 서버와 연결 완료!

3. Supabase에 test data 넣기

  • 데이터를 읽어오기 위해 supabase테이블에 직접 test data를 넣어보자.
  • Table Editor 에서 insert를 누르면 아래와 같이 뜨는데, insert row를 클릭
  • 아래처럼 보이는 창에 원하는 데이터를 타입에 맞게 넣어준다.
  • id와 created_at은 기본값이니 Optional Fields만 쭉쭉 작성해주면 된다.
  • 그럼 이렇게 귀여운 내 test data가 생성된 것을 볼 수 있다.

4. Supabase 읽어오기

  • test data를 이제 화면에 뿌려줘야할 때!

(1) ProductList.tsx

  • 제품들의 리스트가 보여질 ProductList.tsx 컴포넌트를 만들어주고
  • SupabaseClient.ts에 들어있는 supabase를 import해준다.
import { supabase } from '../../../utils/SupabaseClient';
  • 그리고 typescript를 사용할 것이기 때문에 불러올 데이터의 타입도 함께 지정해준다.
    (어마무시한 데이터의 양..)
type ProductsPost = {
  id:string,
  post_user: string,
  nickname: string,    
  created_at: string,
  title: string,
  contents: string,
  price: number,
  count: number, 
  tags: string[], 
  location: string,
  dealType: string,
  like_user: {uid:string, }[],
  likes: number,
  quality: string,
  changable: boolean,
  shipping_cost: boolean,
  agreement: boolean,
};
  • 가져오는 로직은 친절하게도 Supabase 홈페이지 내에서 찾을 수 있다.

  • 설정 위에 API Docs를 누르고 Tables and Views의 products를 눌러주면 아래와 같이 원하는 로직을 가져올 수 있다. 나는 모든 행을 다 가져올 수 있도록 첫 번째 로직 선택!

  • useEffet를 사용하여 getProducts 함수를 마구잡이로 호출하지 않도록 빈 배열을 넣어주고

  • async/await를 사용하여 데이터를 불러온다. (data에 이름은 굳이 따로 붙여주지 않았다.)

  • if (data != null)을 넣어 null이 아닐 경우에만 변경하도록 해준다.

const ProductList = () => {

  const [products, setProducts] = useState<ProductsPost[]>([]);

  useEffect(() => {
    getProducts();
  }, [])

  const getProducts = async () => {
    try {
      const { data, error } = await supabase
        .from('products')
        .select('*')
      if (error) throw error;
      if (data != null) {
        setProducts(data);
      }
    } catch (error) {
      alert('예상치 못한 문제가 발생하였습니다. 다시 시도하여 주십시오.')
    }
  }
  • 리스트가 보여질 부분을 return 아래쪽에 넣어준다.(css는 무시..😂)
  • products에 있는 내용을 map함수로 돌려 카드를 하나씩 만들어 보여주는 로직을 짠다.
  • 제품카드 모양을 담고 있는 ProductsCard.tsx 컴포넌트를 만들어주고
  • product={product} 라는 props를 내려준다.
return (
    <div style={{display: 'flex', flexDirection: 'row', gap: '10px'}}>
      {products.map((product) => {
        return <ProductsCard product={product}/>
      })}
    </div>
  )

(2) ProductsCard.tsx

  • 자 이제 ProductList.tsx(부모 컴포넌트)에서 props를 내려준 자식 컴포넌트를 만들어보자.
  • 여기는 뿌려줄 제품카드 한 장에 대한 내용만 있어서 리스트 컴포넌트보다 간단하다.
  • 타입은 부모 컴포넌트와 똑같이 만들어주고(타입 이름만 Product로 수정)
type Product = {
  id:string,
  post_user: string,
  nickname: string,    
  created_at: string,
  title: string,
  contents: string,
  price: number,
  count: number, 
  tags: string[], 
  location: string,
  dealType: string,
  like_user: {uid:string, }[],
  likes: number,
  quality: string,
  changable: boolean,
  shipping_cost: boolean,
  agreement: boolean,
};
  • 인라인 css는 코드 가독성을 위해 살짝 지워줬다. 다 지우니 너무 간단..!
    (image도 아직 storage 연결을 안해서 사진 여기 있어요~를 보여주는 틀만 짜준 상태😉)
  • 내려준 props(product)를 넣어주고 타입도 지정해준다.
  • product로 가져온 데이터 중 필요한 데이터만 쇽쇽 뽑아 분해시켜준다.
  • 그리고 아래쪽에 보여질 카드 부분에 데이터 이름만 적어주면!!!
const ProductsCard = (product: Product) => {
  
  const { title, price, tags } = product

  return (
    <div>
      <div>
        <image>
        </image>
      </div>
      <div>
        <li>{tags}</li>
      </div>
      <h2>{title}</h2>
      <h3>{price}</h3>
    </div>
  )
}
  • 오류가 뜬다. 🤔 뭐야 왜이래! (여기서부터 2시간의 삽질 시작..)
  • 타입을 바꿔보고 지워도 봤다가 추가도 해보고 없던 것도 만들어보고 별짓을 다 하다가 겨우 찾은 한 줄기의 빛(블로그)...!

4-1. props type 오류 해결하기

🤔 에러 메세지

TS2322: Type '{ product: ProductsPost; }' is not assignable to type 'IntrinsicAttributes & Product'.
Property 'product' does not exist on type 'IntrinsicAttributes & Product'.

'{ product: ProductsPost; }' 형식은 'IntrinsicAttributes & Product' 형식에 할당할 수 없습니다.
'IntrinsicAttributes & Product' 형식에 'product' 속성이 없습니다.ts(2322)

💡 에러 원인

  • 문제는 부모 컴포넌트에서 자식 컴포넌트로 props를 넘길 때 인수를 받아오는 형식이 잘못 되었기 때문에 발생!!!

* React component에서 props를 넘길 때 인수 객체 자체가 전달이 되는데,

{ title: "붓", price: 10000 }
  • 즉 내가 보내고자 하는 props가 위와 같다면
const ProductsCard = (product: Product) => { }
  • 첫 번째 방법으로 props를 받았을 때의 product의 모습은 아래와 같은 모습이 된다는 뜻이다.
{ product : { title: "붓", price: 10000 } }

🔑 해결 방법

  • 그래서 이러한 타입 에러를 피하기 위해서는 인수객체에서 product 데이터만 꺼내서 가져오는 방법을 사용하는 것이 좋다.
  • product 인수 객체에서 product만 디스트럭쳐링 할당(구조분해할당)으로 꺼내온다.
  • 이때 주의할 점은 ProductsCard({product: Product})나 ProductsCard({product}: product)는 올바른 타입 지정이 아니다.

※ 객체 디스트럭처링의 경우 ProductsCard({product}: {product: Product})와 같이 작성해야 한다.

const ProductsCard = ({product}: {product: Product}) => {
  const { title, price, tags } = product

  return (
    <div>
      <div>
        <image>
        </image>
      </div>
      <div>
        <li>{tags}</li>
      </div>
      <h2>{title}</h2>
      <h3>{price}</h3>
    </div>
  )
}
  • 이렇게 바꿔주면 짜잔! 아래처럼 데이터를 잘 읽어와 화면을 예쁘게 잘 보여준다!

느낀 점

참고

0개의 댓글