๋๋ฃํ์ต์ ํตํด์ ํ์์ ์๊ฐํ ์ํฐ๋ ํ๋ฆฌ์จ๋ณด๋ฉ ํ๋ก ํธ์๋ ์ธํด์ญ ์ ๋ฐ ๊ณผ์ ์ Best Pratice๋ฅผ ๋ง๋ค๊ณ ์ ์ถํด์ฃผ์ธ์.
Best Practice๋ ๋ชจ๋ฒ์ฌ๋ก๋ผ๋ ๋ง๋ก์, ํน์ ๋ฌธ์ ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํด๊ฒฐํ๊ธฐ ์ํ ๊ฐ์ฅ ์ฑ๊ณต์ ์ธ ํด๊ฒฐ์ฑ ๋๋ ๋ฐฉ๋ฒ๋ก ์ ์๋ฏธํฉ๋๋ค.
์งํ ๊ธฐ๊ฐ: 2023.07.11 ~ 2023.07.14
- ๐ ๋ฐฐํฌ ๋งํฌ
- โ๏ธ ์คํ ๋ฐฉ๋ฒ
- ๐ค ํ ๊ท์น
- ๐ ํด๋ ๊ตฌ์กฐ
- ๐ ๏ธ ๊ธฐ์ ์คํ
- ๐ ์๋น์ค ์๊ฐ
- ๐ Best Practice
์ค์น ๋ฐ ์คํ
$ npm install
$ npm start
- upstream์๋ main ๋ธ๋์น๋ง ์กด์ฌ
- ๋ธ๋์น๋ช
:
feature/#์ด์๋ฒํธ-๊ฐ๋จํ์ค๋ช
- ex:
feature/#7-setting
- ex:
- fork ํด์ ๋ธ๋์นํ์ ์์
ํ๋ค
upstream:main
์ผ๋ก PR ๋ ๋ฆผ - ์ฝ๋๋ฆฌ๋ทฐ ๋ฐ๊ณ ์น์ธ ๋ฐ์ผ๋ฉด
upstream:main
์ merge
๐ฆsrc
โโโ ๐index.css
โโโ ๐index.tsx
โโโ ๐App.tsx
โโโ ๐apis
โโโ ๐contexts
โโโ ๐components
โโโ ๐hooks
โโโ ๐pages
โโโ ๐utils
โโโ ๐router
ํน์ ๊นํ๋ธ ๋ ํ์งํ ๋ฆฌ์ ์ด์ ๋ชฉ๋ก๊ณผ ์์ธ ๋ด์ฉ์ ์กฐํํ ์ ์๋ ์น ์ฌ์ดํธ ๊ตฌ์ถ
- ์ด์ ๋ฆฌ์คํธ ์กฐํ
- ๊ฐ๋ณ ์ด์ ์กฐํ
- ์ด์ 5๊ฐ๋ง๋ค ๊ด๊ณ ์ฝ์
์ด์ ๋ชฉ๋ก | ๊ฐ๋ณ ์ด์ |
---|---|
- ์ฝ๋์ ๊ฐ๋ ์ฑ ๋ฐ ์ฌ์ฌ์ฉ์ฑ
- ๊ฐ๋ ์ฑ์ ํ์ ์ ํ๋ฉด์๋ ์ค์ํ๋ฉฐ, ์ ์ง ๋ณด์์ฑ์ ํฅ์
- ์ค๋ณต ์ฝ๋๋ฅผ ์ต์ํ ํ๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ ํธ๋ฆฌํฐ ํจ์ ์์ฑ(useHook)
- ํ์ฅ์ฑ
- ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ฅ์ ์ ์ฐํ๊ฒ ํ์ฅํ๊ณ ์ ์ง ๊ด๋ฆฌํ ์ ์๋๋ก ์์ฑ
- ์ฌ์ฉ์ ํธ์์ฑ
- ์ฌ์ฉ์๋ค์ด ์น ๋๋ ์ฑ ์ธํฐํ์ด์ค๋ฅผ ์ฝ๊ฒ ์ดํดํ๊ณ ์กฐ์ํ ์ ์๋๋ก ์ค๊ณ
- Best Practice๋ฅผ ์ ์ ํ๊ธฐ์ ๊ฐ์ ๊ตฌํํ ์ฝ๋๋ฅผ ์ค๋ช ํ๋ฉฐ ์ฝ๋๋ฆฌ๋ทฐ๋ฅผ ์งํ
- ๊ฐ์ ๋งก์ ์ฝ๋ ๊ตฌํ ๋ฐฉ๋ฒ๋ค์ ์ ๋ฆฌ ํ ์คํฌ๋ผ์ ํตํด Best Practice ์ ์
- ์ฝ๋์ปจ๋ฒค์ ๋ฐ ์ธ์ด, ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํด๋๊ตฌ์กฐ ์ ๋ฆฌ
- Best Practice๋ก ๋ฝํ ๊ตฌํ๋ฐฉ์์ค ๊ตฌํํ์ง ์์ ๋ถ๋ถ๋ค์ ๋๋์ด ์ ๋ฌด ๋ถ๋ด
- ์ด์ ์์ฑ ๋ฐ ๊ฐ๋ณ์ ์ผ๋ก ์ฝ๋ ๊ตฌํ ํ pr ํ ์ฝ๋๋ฆฌ๋ทฐ๋ฅผ ์งํ ํ๊ณ merge
axios instance
- baseURL ์ง์
- header์ ๋ชจ๋ ์์ฒญ ์ ๊ณตํต์ผ๋ก ๋ค์ด๊ฐ๋ Accept, Authorization(ํ ํฐ) ์ง์
ํน์ ์ ์ฅ์์ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ api class
- private ํค์๋: path, query์ ์ธ๋ถ ์ ๊ทผ์ ๋ง๊ธฐ์ํด private ํค์๋ ์ค์
- getIssueList: ์ด์ ๋ชฉ๋ก์ ์์ฒญํ๋ ๋ฉ์๋
- getIssue: ๋จ์ผ ์ด์๋ฅผ ์์ฒญํ๋ ๋ฉ์๋
sort type
- ๊ธฐ์กด ์ฝ๋๋ sort ์ต์
์ค ํ๋์ธ
comments
๋ฅผQUERY_SORT_TYPE
์ ํ ๋นํด์ ์ฌ์ฉํ๊ณ ์์ - sort ์ต์
์
created
,updated
,comments
์ด 3๊ฐ์ง๊ฐ ์๋๋ฐ, ์ด ์ธ์ ๋ค๋ฅธ ๋ฌธ์์ด์ด ํ ๋น๋๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์. ๊ทธ๋์ ๊ธฐ์กด์ string ํ์ ๋ณด๋ค ๋ ๊ตฌ์ฒด์ ์ธ ๋ฆฌํฐ๋ด ํ์ ์ ์ง์ ํด ์ค๋ฅ๋ฅผ ๋ฐฉ์งํจ
type SortType = 'created' | 'updated' | 'comments';
export class RepositoryAPI {
private static QUERY_SORT_TYPE: SortType = 'comments';
// code...
}
- ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ปดํฌ๋ํธ์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง ๋ณด์์ฑ์ ํฅ์
-
๋ฌดํ ์คํฌ๋กค ๋ก์ง, ์ด์ ๋ฐ์ดํฐ ๋ฐ ์ํ ๊ด๋ฆฌ๋ IssueContext๋ก ๋ถ๋ฆฌ๋์ด ๊ด์ฌ์ฌ ๋ถ๋ฆฌ
-
Intersection Observer๊ณผ ๊ด๋ จ๋ ๋ก์ง์ useIntersectionObserver ํ ์ ํตํด ๋ณ๋๋ก ๋ถ๋ฆฌ
// src/pages/IssueList.tsx const { issues, getInfiniteIssues, isLoading, isError } = useContext(IssueContext); const handleIntersection = () => { if (!isLoading) getInfiniteIssues(); }; const ref = useIntersectionObserver({ callback: handleIntersection });
- ์๋ฌ ์ฒ๋ฆฌ : ๋ฆฌ๋ค์ด๋ ์ vs ์๋ฌ ์ปดํฌ๋ํธ
-
์๋ฌ๊ฐ ๋ฐ์ํ ์ปดํฌ๋ํธ๋ง ์ํฅ์ ๋ฐ๊ณ , ๋ค๋ฅธ ์๋น์ค๋ ๊ธฐ๋ฅ์ ์ ์์ ์ผ๋ก ๋์ํ ์ ์์
-
์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ์ฑ์ ์ ์งํ๋ฉด์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๋ถ๋ถ๋ง ์ฒ๋ฆฌํ ์ ์์
{ isError ? <Error /> : <li ref={ref}></li>; }
-
Intersection Observer๋ฅผ ์ฌ์ฉํ์ฌ ์์์ ๊ฐ์์ฑ ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ณ , ์ง์ ๋ ์ฝ๋ฐฑ ํจ์๋ฅผ ํธ์ถํ๋ ์ญํ
-
์ฝ๋์ ๊ฐ๋ ์ฑ์ด ํฅ์. Intersection Observer์ ๋ก์ง์ด ๋ถ๋ฆฌ๋์ด ๋์ฑ ๋ช ํํ๊ฒ ์ดํดํ ์ ์์
-
์ฌ์ฌ์ฉ์ฑ์ด ๋์์ง. useIntersectionObserver ํ ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์๋ ํ์ฉํ์ฌ ๊ฐ์์ฑ ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์
const useIntersectionObserver = ({ callback, option = {} }) => { const ref = useRef(null); const handleIntersection = (entries) => { const target = entries[0]; if (target.isIntersecting) callback(); }; useEffect(() => { const observer = new IntersectionObserver(handleIntersection, { threshold: 0.5, ...option, }); if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, [handleIntersection]); return ref; };
[๊ณ ๋ฏผ ์ฌํญ]
์ด์๋ค์ ๋ฌดํ์ผ๋ก ๋ถ๋ฌ์ค๊ธฐ ์ํ page ๋ณ์๋ฅผ ์ด๋์ ๊ด๋ฆฌํ ์ง์ ๋ํ ๊ณ ๋ฏผ
- Issue Context๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ ๋จ(
IssueList
ํ์ด์ง)์์ ๊ด๋ฆฌ - Issue Context์์ ๊ด๋ฆฌ
- ๋ ผ์ ๊ฒฐ๊ณผ: Issue Context์์ ๊ด๋ฆฌ
- ์ด์ : page ๋ณ์๋ ์ค๋ก์ง ์ด์๋ฅผ ์ํด์๋ง ์กด์ฌํ๊ธฐ ๋๋ฌธ์, issue๋ฅผ ๊ด๋ฆฌํ๋ IssueContext ์์ ๊ด๋ฆฌํ๋ ๊ฒ ์ ํฉํ๋ค๊ณ ํ๋จ
[๊ทธ ์ธ ๊ฐ์ ์ฌํญ]
- ์ด์๋ฅผ ๋ถ๋ฌ์ฌ ๋ ์ฌ์ฉํ๋ page ๋ณ์๋ state ๋์ useRef ์ฌ์ฉํ์ฌ ๋ถํ์ํ ๋ฆฌ๋๋๋ง ๋ฐฉ์ง
isEndRef
๋ฅผ ์ฌ์ฉํ์ฌ ๋ ์ด์ ๋ถ๋ฌ์ฌ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด api๋ฅผ ํธ์ถํ์ง ์๋๋ก ๋ง์
๊ธฐ์กด์ ๋ผ์ฐํ
๊ธฐ๋ฅ๋ณด๋ค ๋ง์ ๊ธฐ๋ฅ๋ค์ด ์ถ๊ฐ๋์ด ์์ด ํ์ฉ์ฑ์ด ๋๋ค.
ํ์ ์ปดํฌ๋ํธ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๊ฐ๋ฅ, ๊ฒฝ๋ก๊ฐ ๋ง๋ค๋ฉด ๊ฐ๋
์ฑ์ด ์ข์ผ๋ฉฐ ์๋ฌ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๋ก ์ค์ ํ ์ ์๋ค.
- App.tsx ์์ RouterProvider ์ฐ๊ฒฐ
- routerํด๋ > Router.tsx์์ createBrowserRouter ์ฌ์ฉ
ํ์ฌ '/' ๋ฉ์ธ ํ์ด์ง๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ์ index๊ฐ์ true๋ก ์ค์ ํ์ฌ '/' ์ ๊ทผ์ '/repos/facebook/react/issues' ๊ฒฝ๋ก๋ก ์ด๋ํ๋๋ก ์ค์
๊ฒฝ๋ก๊ฐ '/repos/:owner/:repo/issues'๋ผ๋ฉด ๋ฅผ ๋ ๋๋ง
๊ฒฝ๋ก๊ฐ '/repos/:owner/:repo/issues/:id'๋ผ๋ฉด ๋ฅผ ๋ ๋๋ง
router์ ์ค์ ํ ์ค์ ๋๋ก ์ง์ ํ url์ด ์๋ ๊ฒฝ์ฐ์ ์๋ฌ ํ์ด์ง(NotFoundPage) ๋ ๋๋ง
import { createBrowserRouter, Navigate } from 'react-router-dom';
import IssueDetail from '../pages/IssueDetail';
import IssueList from '../pages/IssueList';
import NotFoundPage from '../pages/NotFoundPage';
import { Root } from './Root';
export const router = createBrowserRouter([
{
path: '/',
element: <Root />,
children: [
{
index: true,
element: <Navigate to={'/repos/facebook/react/issues'} />,
},
{
path: '/repos/:owner/:repo/issues',
element: <IssueList />,
},
{
path: '/repos/:owner/:repo/issues/:id',
element: <IssueDetail />,
},
],
errorElement: <NotFoundPage />,
},
]);
๋ํ Header text(owner/repo) ๊ตฌํ์,
ํน์ ๋ ํฌ๋ฅผ ์ ํํ์ฌ ๊ธฐ์กด ํ๋์ฝ๋ฉ์ผ๋ก ๊ตฌํํ ๊ฒ๋ณด๋ค ํ์ฅ์ฑ์ ๊ณ ๋ คํ์ฌ
Router.tsx์์ ๊ฒฝ๋ก ์ค์ ์ '/repos/facebook/react/issues' ์ด๋ ๊ฒ ๋ง๋ค์ด
useParams๋ก owner/repo๋ฅผ ๊บผ๋ด์ฌ ์ ์๋ค.
Router.tsx์์ owner/repo๋ฅผ ๋ฐ๊พธ๊ธฐ๋ง ํ๋ฉด ์ํ๋ ๋ ํฌ์ ์ด์๋ฅผ ์์ฒญํ ์ ์๊ฒ ๋ง๋ฆ
//Header.tsx
import { useParams } from 'react-router-dom';
export default function Header() {
const { owner, repo } = useParams();
return (
<header className='header p-6 pl-16 bg-blue-500 text-white text-xl mb-5 flex items-center justify-center'>
<h1>
{owner}/{repo}
</h1>
</header>
);
}
- ๋
ผ์ ์ฌํญ
- IssueContext์์ ๋ชจ๋ ์ด์๋ฅผ ๊ด๋ฆฌํ๋ ๋ก๋ฉ, ์๋ฌ ์ํ๋ ๊ด๋ฆฌ ์ํจ
- IssueContext์์๋ ์ด์ ๋ฌดํ์คํฌ๋กค๋ก ๋ถ๋ฌ์ค๋ ๊ฒ๋ง ๋ด๋น, ๊ทธ์ ๋ฐ๋ฅธ ๋ก๋ฉ & ์๋ฌ ์ํ๋ ๊ฐ์ด ๊ด๋ฆฌ
- ๋
ผ์ ๊ฒฐ๊ณผ
- IssueContext์์ ๋ชจ๋ ์ด์๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋๋ฉด ๋ก๋ฉ, ์๋ฌ ์ํ ๊ณต์ ํด์ ์ฐ๊ฒ ๋๊ธฐ ๋๋ฌธ์ IssueContext๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ ๋จ์์ ๋ก๋ฉ, ์๋ฌ ์ํ ๋ณ์๋ฅผ ๋ง๋ค์ด์ผ ํด์ ๊ฐ๋ณ ์ด์ api ํธ์ถ์ IssueDetail์์ ์ง์ ํ๋๊ฒ ์ ํฉ
- ํด๊ฒฐ ๋ฐฉ๋ฒ
- ๊ฐ๋ณ ์ด์ apiํธ์ถ์ IssueContext๊ฐ ์๋ IssueDetail์์ ์งํ