😀 개요
전 직장에서 next + typescript로만 개발을 해오고 그마저도 템플릿이 있어 환경설정을 할 필요가 없었다. 또한 리액트 프로젝트를 생성할 때 Create React App으로만 해보고 vite도 한 번도 사용해보지 않아 새로운 환경에서 개발을 해보고 싶었다! 환경설정부터 백엔드 아닌 백엔드까지 전 과정을 혼자서 경험해보고자 이 프로젝트를 시작하게 되었다.

오랜만에 보는 리액트 로고..

vite는 처음인데.. 생성속도가 아주 빨라서 좋았다.
1️⃣ vite로 새로운 프로젝트 생성하기
yarn create vite
? Project name: 프로젝트이름
? Select a framework: (Use arrow keys)
❯ vanilla
vue
react
preact
lit
svelte
react + typeScript로 선택!
2️⃣ vite-plugin-next-react-router 사용하기 (라우터 처리)
1. 설치
yarn add vite-plugin-next-react-router
2. vite.config.ts 파일 수정
강의를 보면서 따라하다가 _'"vite-plugin-next-react-router"' 모듈에 내보낸 멤버 'reactRouterPlugin'이(가) 없습니다. 대신 '"vite-plugin-next-react-router"에서 reactRouterPlugin 가져오기'를 사용하시겠습니까?ts(2614) 라는 오류가 나서..
(강의를 하신 시점이랑 보고있는 시점이랑 버전이 달라져서 오류가 나는 듯 했다)
구글링을 하였더니 마침 나와 같은 오류를 경험한 블로그가 있어서 그 블로그를 참고했다
참고 블로그 : https://velog.io/@cjy921004
[ React ] vite 프로젝트 생성 & vite-plugin-next-router 사용하기
Vite는 Vue.js 팀에서 개발한 웹 개발 빌드 도구이다. Vite의 주요 목표는 개발 서버와 빌드 시스템을 최적화하여 더 빠른 개발 경험을 제공하는 것이다.Vite는 기존의 번들러(Bundler)와는 다른 접근 방
velog.io
3. vite.config.ts 수정
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import withReactRouter from "vite-plugin-next-react-router";
export default defineConfig({
plugins: [
react(),
withReactRouter({
pageDir: "src/pages",
extensions: ["js", "jsx", "ts", "tsx"],
layout: "_layout",
}),
],
});
4. src/pages/ _layout.tsx 파일 생성
import React, { Suspense } from "react";
import { Outlet } from "react-router-dom";
const Layout: React.FC = () => {
return (
<div>
<Suspense fallback={"loading..."}>
<Outlet />
</Suspense>
</div>
);
};
export default Layout;
이렇게 하면 Next.js에서 하던 대로 쉽게 라우팅을 할 수 있다.
3️⃣ 상품목록 페이지 만들기
1. dummyData 사용하기
- 이런 더미데이터가 있는지 몰랐는데 프론트엔드가 백엔드 없이 프로젝트 진행할 때 이용하면 좋은 것 같다!
Fake Store API
Fake store rest api for your ecommerce or shopping website prototype
fakestoreapi.com
2. react-query 사용하기
- 드디어 react-query를 사용해보는구나.. 리액트 쿼리 리액트 쿼리 말만 들었지 한 번도 제대로 사용해 본 적이 없는데 이 프로젝트를 통해 완전히 내 것으로 만들고 싶다!
yarn add react-query
2-1. src/queryClient.ts 파일 생성
https://tanstack.com/query/v4/docs/react/quick-start
Quick Start | TanStack Query Docs
This code snippet very briefly illustrates the 3 core concepts of React Query: If you're looking for a fully functioning example, please have a look at our simple codesandbox example tsx import { useQuery, useMutation, useQueryClient, QueryClient, QueryCli
tanstack.com
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from "react-query";
// Create a client
export const getClient = (() => {
let client: QueryClient | null = null;
return () => {
if (!client) client = new QueryClient({});
return client;
};
})();
2-2. react-query dev-tool 사용
- src/_layout.tsx 에 getClient와 react-query-devtools를 import 해준다
- vite가 업데이트 되어서 main.tsx / app.tsx가 필요없게 되었다. 완전히 지워도 가능!
- 단, index.html에서
<script type="module" src="/src/main.tsx"></script>
이 부분 지워주기!
자세한 내용은
caught Error: No QueryClient set, use ueryClientProvider 에러 - 인프런
저는 .routes 파일이 생성이 되지않아서 그냥 진행했습니다. 라우터는 잘 작동하더라구요.App에서 elem 대신 <ProductList> 를 감싸줬습니다. ProductList 에 데이터를 불러오는 과정에서 'caught Error: No QueryC
www.inflearn.com
여기에서 확인
- src/_layout.tsx
import React, { Suspense } from "react";
import { Outlet } from "react-router-dom";
import { QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import { getClient } from "../queryClient";
const Layout: React.FC = () => {
const queryClient = getClient();
return (
<QueryClientProvider client={queryClient}>
<Suspense fallback={"loading..."}>
<Outlet />
</Suspense>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
};
export default Layout;
- '@tansack/react-query' / '@tansack/react-query/devtools'로 import 하면 오류나니 주의!
2-3. queryClient.ts 메서드, 옵션 설정
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from "react-query";
// any 타입 미리 만들어줌
type AnyOBJ = { [key: string]: any };
// Create a client
export const getClient = (() => {
let client: QueryClient | null = null;
return () => {
if (!client) client = new QueryClient({});
return client;
};
})();
// 기본 url
const BASE_URL = "https://fakestoreapi.com";
// async로 요청
export const fetcher = async ({
method,
path,
body,
params,
}: {
// 메소드 타입 정의
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
// url대신 path를 받음
path: string;
// post나 put의 경우엔 body가 필요하므로
body?: AnyOBJ;
// 파라미터
params?: AnyOBJ;
}) => {
try {
// 기본 url + path
const url = `${BASE_URL}${path}`;
// RequestInit은 node에 기본적으로 정의되어 있음
const fetchOptions: RequestInit = {
method,
headers: {
"Contnet-Type": "application/json",
"Access-Control-Allow-Origin": BASE_URL,
},
};
// url와 옵션들 요청
// 메서드와 path를 받아서 완성
const res = await fetch(url, fetchOptions);
// 받은 것을 json으로 바꾸기
const json = await res.json();
return json;
// 에러 출력
} catch (err) {
console.error(err);
}
};
// 쿼리 키 만들기
export const QueryKeys = {
PRODUCTS: "PRODUCTS",
};
3. src/products/index.tsx에 상품목록 불러오기 / scss 적용
import { useQuery } from "react-query";
import { QueryKeys, fetcher } from "../../queryClient";
import ProductItem from "../../assets/components/product/item";
const ProductsList = () => {
// type 정의
const { data } = useQuery<Product[]>(QueryKeys.PRODUCTS, () =>
fetcher({
method: "GET",
path: "/products",
})
);
console.log(data);
return (
<div>
<ul className="products">
{data?.map((product) => (
<ProductItem {...product} key={product.id} />
))}
</ul>
</div>
);
};
export default ProductsList;
3-1. 타입은 따로 type.ts 파일 생성해서 적용하기
type Rating = {
rate: number;
count: number;
};
type Product = {
category: string;
description: string;
id: number;
image: string;
price: number;
rating: Rating;
title: string;
};
// interface는 union이 불가
4. ProductItem 컴포넌트 생성
const ProductItem = ({
category,
description,
image,
price,
rating,
title,
}: Product) => {
return (
<li className="products-item">
<p className="products-item__category">{category}</p>
<p className="products-item__title">{title}</p>
<p className="products-item__description">{description}</p>
<img className="products-item__image" src={image} />
<span className="products-item__price">${price}</span>
<span className="products-item__rate">{rating.rate}</span>
</li>
);
};
export default ProductItem;
상품목록 불러오기 성공!!
4️⃣ 상품 상세 페이지로 이동
1. <Link> 태그로 감싸서 id를 받아 이동
import { Link } from "react-router-dom";
const ProductItem = ({
category,
image,
price,
rating,
title,
id,
}: Product) => {
return (
<li className="products-item">
<Link to={`/products/${id}`}>
<p className="products-item__category">{category}</p>
<p className="products-item__title">{title}</p>
<img className="products-item__image" src={image} />
<span className="products-item__price">${price}</span>
<span className="products-item__rate">{rating.rate}</span>
</Link>
</li>
);
};
export default ProductItem;
2. src/pages/products/[id].tsx 작성
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import { QueryKeys, fetcher } from "../../queryClient";
import ProductDetail from "../../assets/components/product/detail";
const ProductsDetail = () => {
const { id } = useParams();
// 데이터 가져오기, type 정의
const { data } = useQuery<Product>([QueryKeys.PRODUCTS, id], () =>
fetcher({
method: "GET",
path: `/products/${id}`,
})
);
if (!data) return null;
return (
<div>
<h2>상품상세</h2>
<ProductDetail item={data} />
</div>
);
};
export default ProductsDetail;
react-query부터 scss까지 한 번도 안 써봤던 걸 써서 애먹은 것도 있었지만,, 새로운 걸 배우니 너무 재밌고 시간도 빨리간다! ㅎㅎ
3. queryClient.ts 코드 추가
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from "react-query";
// any 타입 미리 만들어줌
type AnyOBJ = { [key: string]: any };
// Create a client
export const getClient = (() => {
let client: QueryClient | null = null;
return () => {
if (!client)
client = new QueryClient({
defaultOptions: {
queries: {
// 캐시타임 : 이 시간 안에는 다시 상세페이지 들어가도 요청 안 함
cacheTime: 1000 * 60 * 60 * 24,
staleTime: 1000 * 60,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
},
},
});
return client;
};
})();
// 기본 url
const BASE_URL = "https://fakestoreapi.com";
// async로 요청
export const fetcher = async ({
method,
path,
body,
params,
}: {
// 메소드 타입 정의
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
// url대신 path를 받음
path: string;
// post나 put의 경우엔 body가 필요하므로
body?: AnyOBJ;
// 파라미터
params?: AnyOBJ;
}) => {
try {
// 기본 url + path
let url = `${BASE_URL}${path}`;
// RequestInit은 node에 기본적으로 정의되어 있음
const fetchOptions: RequestInit = {
method,
headers: {
"Contnet-Type": "application/json",
"Access-Control-Allow-Origin": BASE_URL,
},
};
// param이 오면
if (params) {
const searchParams = new URLSearchParams(params);
url += "?" + searchParams.toString();
}
// body가 오면
if (body) fetchOptions.body = JSON.stringify(body);
// url와 옵션들 요청
// 메서드와 path를 받아서 완성
const res = await fetch(url, fetchOptions);
// 받은 것을 json으로 바꾸기
const json = await res.json();
return json;
// 에러 출력
} catch (err) {
console.error(err);
}
};
// 쿼리 키 만들기
export const QueryKeys = {
PRODUCTS: "PRODUCTS",
};
5️⃣ cart 페이지 추가, Gnb 추가
- 장바구니 페이지와 페이지 이동을 할 수 있는 네비게이션 바도 추가해 주었다.
새로운 걸 배우니까 생소하고 어렵지만 시간 빨리가고 재밌다!! ㅎㅎ
'REACT > 쇼핑몰 프로젝트' 카테고리의 다른 글
[REACT] React + Typescript + Vite + SCSS로 만드는 쇼핑몰 개인프로젝트 (7/10) (0) | 2024.01.24 |
---|---|
[REACT] React + Typescript + Vite + SCSS로 만드는 쇼핑몰 개인프로젝트 (6/10) (0) | 2024.01.23 |
[REACT] React + Typescript + Vite + SCSS로 만드는 쇼핑몰 개인프로젝트 (5/10) (0) | 2024.01.22 |
[REACT] React + Typescript + Vite + SCSS로 만드는 쇼핑몰 개인프로젝트 (4/10) (0) | 2024.01.22 |
[REACT] React + Typescript + Vite + SCSS로 만드는 쇼핑몰 개인프로젝트 (3/10) (0) | 2024.01.18 |