- ํ๋ก์ ํธ ๊ธฐ๊ฐ: 2024.04.30 ~ 2024.05.17
- ๋ฐฐํฌ URL: ๐ Fandom-K
- 'Fandom-K'๋ ์์ด๋ ์กฐ๊ณต ํ๋ซํผ์ ๋๋ค.
- ํฌ๋ ๋ง์ ์ถฉ์ ํ์ฌ ์์ด๋์๊ฒ ํ์๊ณผ ์ธ๊ธฐ ํฌํ๋ฅผ ํ ์ ์์ต๋๋ค.
- ๋ชจ๊ธ๋ ํฌ๋ ๋ง๊ณผ ์์ด๋ ์ธ๊ธฐ ์์๋ฅผ ์ค์๊ฐ์ผ๋ก ํ์ธ ํ ์ ์์ต๋๋ค.
- ๊ด์ฌ์๋ ์์ด๋์ ๋ง์ดํ์ด์ง์์ ํ๋ก์ ํ ์ ์์ต๋๋ค.
๋ชฉ์ฐจ
๐ ์ํ๋ฆฐ | ๐ ์๋ํ | ๐ป ์ฒ๊ถํฌ | ๐จ ์ด์จ |
---|---|---|---|
blog: Rynn github: miraclee1226 |
blog: Eun_Frontend github: edhcoding |
blog: alexgoni github: alexgoni |
github: yulrang |
- GitHub Projects: ์งํ ์ํฉ์ ๊ณต์ ํ๊ณ ์ GitHub Issues์์ ๊ฐ์ ๋งก์ ์ ๋ฌด๋ฅผ ์ด์ ํ ํ๋ฆฟ์ ์ฒดํฌ๋ฆฌ์คํธ ํ์์ผ๋ก ๊ณต์ ํ์ต๋๋ค.
- Figma: ๋์์ธ ์์์ ์ฐธ๊ณ ํ์ต๋๋ค.
- Discord: ์ํํ ์์ฌ์ํต์ ์ํด ๋์ค์ฝ๋์์ ์์ ๋ฐ ์์ฑํตํ๋ฅผ ์ ๊ทน ํ์ฉํ์ต๋๋ค.
๊ธฐ๋ฅ ๋ณ๋ก ๋ด๋นํ์ฌ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ณ ์ Git Flow ๋ฐฉ์์ ์ฌ์ฉํ์ต๋๋ค. ๊ธฐ๋ฅ ๋ณ ๋ธ๋์น๋ฅผ ๋ง๋ค๊ณ ๊ฐ์ ์์ ๋ธ๋์น๋ฅผ ๋ฐ๋ก ์์ฑํ์ฌ develop ๋ธ๋์น๋ก PR ๋ฐ Merge๋ฅผ ์งํํฉ๋๋ค.
- Featย : ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ
- Fixย : ๋ฒ๊ทธ๋ฅผ ๊ณ ์น๊ฒฝ์ฐ
- Docsย : ๋ฌธ์๋ฅผ ์์ ํ ๊ฒฝ์ฐ
- Styleย : ์ฝ๋ ํฌ๋งท ๋ณ๊ฒฝ, ์ธ๋ฏธ์ฝ๋ก ๋๋ฝ, ์ฝ๋ ์์ ์ด ์๋๊ฒฝ์ฐ
- Refactorย : ์ฝ๋ ๋ฆฌํํ ๋ง
- Testย : ํ
์คํธ ์ฝ๋. ๋ฆฌํํ ๋ง ํ
์คํธ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ ๋
- Choreย : ๋น๋ ์
๋ฌด ์์ , ํจํค์ง ๋งค๋์ ์์
- Designย : CSS ๋ฑ ์ฌ์ฉ์๊ฐ UI ๋์์ธ์ ๋ณ๊ฒฝํ์ ๋
- Renameย : ํ์ผ๋ช
(or ํด๋๋ช
) ์ ์์ ํ ๊ฒฝ์ฐ
- Removeย : ์ฝ๋(ํ์ผ)์ ์ญ์ ํ ๊ฒฝ์ฐ
ํต์ผ์ฑ ์๋ ์ฝ๋ ์์ฑ์ ์ํด ๋ค์ํ ์ฝ๋ ์ปจ๋ฒค์ ์ ์ ํด ์ฌ์ฉํ์ต๋๋ค.
{
"printWidth": 80,
"singleQuote": false,
"jsxSingleQuote": false,
"tabWidth": 2,
"semi": true,
"trailingComma": "all",
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "crlf"
}
{
"extends": ["react-app", "react-app/jest", "naver", "prettier"],
"rules": {
"no-console": 1,
"react-hooks/exhaustive-deps": 0,
"no-unused-expression": 0,
"no-unused-vars": "off",
"react/prop-types": "off",
"arrow-body-style": "off"
}
}
๊ฐ์ ๋งก์ ์ ๋ฌด๋ฅผ ์ด์ ํ ํ๋ฆฟ์ ์ฒดํฌ๋ฆฌ์คํธ ํ์์ผ๋ก ๊ณต์ ํ์ต๋๋ค.
Discord ์ค๋ ๋์ ๋ฐ์ผ๋ฆฌ ์คํฌ๋ผ ๋ด์ฉ์ ์ ๋ฆฌํด ์ ๋ก๋ ํ์ต๋๋ค.
- ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ์งํํ๊ฑฐ๋ ์ ์ฒด ์์๊ฐ ํ์ํ ์์ ์ ํ ๋, VSCode์ Live Share ํ์ฅ ๊ธฐ๋ฅ์ ์ ๊ทน ํ์ฉํ์ต๋๋ค.
- localStorage
- ํ์ ์ ํฌ๋ ๋ง ์ฐจ๊ฐ
- ํฌํ ์ ํฌ๋ ๋ง ์ฐจ๊ฐ
- ๊ด์ฌ์๋ ์์ด๋ ์ถ๊ฐ
- ๋ฌดํ ์คํฌ๋กค
- api/ : ์ธ๋ถ API ํต์
- assets/ : ์ด๋ฏธ์ง, ์์ด์ฝ
- components/ : ๊ณตํต ์ปดํฌ๋ํธ
- pages/ : ์์ฑํ ์ปดํฌ๋ํธ๋ค์ ์กฐํฉํด ๊ฐ๋ณ ๊ธฐ๋ฅ ๊ตฌํ
- context/ : ์ ์ญ ์ํ ๊ด๋ฆฌ
- styles/ : ๊ณตํต ์คํ์ผ, reset.scss
- utils/ : util ํจ์
๐ฆsrc
โฃ ๐api
โฃ ๐assets
โฃ ๐components
โ โฃ ๐Button
โ โฃ ๐Card
โ โฃ ๐ChartItem
โ โฃ ๐Image
โ โฃ ๐Layout
โ โฃ ๐Modal
โ โฃ ๐Navbar
โ โฃ ๐Skeleton
โ โฃ ๐Tab
โ โฃ ๐TouchArea
โ โ ๐WebpLoader
โฃ ๐context
โฃ ๐hooks
โฃ ๐pages
โ โฃ ๐Landing
โ โฃ ๐List
โ โ ๐MyPage
โฃ ๐styles
โฃ ๐utils
โฃ ๐App.jsx
โ ๐index.js
์์ธ๊ธฐ๋ฅ
- '๋ก๊ณ ๋ฒํผ'์ ํด๋ฆญํ๋ฉด
/
ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค. - '์ง๊ธ ์์ํ๊ธฐ' ๋ฒํผ์ ํด๋ฆญ ์ localStorage๋ฅผ ์ด๊ธฐํํ๊ณ
/list
ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
์์ธ๊ธฐ๋ฅ
- '๋ก๊ณ ' ๋ฒํผ์ ํด๋ฆญํ๋ฉด
/list
ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค. ( ํ์ฌ๋ ์๋ก ๊ณ ์นจ ) - ๋ด ํฌ๋ ๋ง์ localstorage๋ก ๊ด๋ฆฌํฉ๋๋ค.
- '์ถฉ์ ํ๊ธฐ' ๋ฒํผ์ ํด๋ฆญ ์ ์ถฉ์ ํ๊ธฐ ๋ชจ๋ฌ์ฐฝ์ด ๋ํ๋ฉ๋๋ค.
- ์๋จ์ ํ๋กํ ์ด๋ฏธ์ง๋ฅผ ํด๋ฆญ์
/mypage
๋ก ์ด๋ํฉ๋๋ค. - ํ๋กํ ์ด๋ฏธ์ง๋ ์์ ๋กญ๊ฒ ์ ํํด์ฃผ์ธ์.
- PC์์ ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์กฐ๊ณต ๋ฆฌ์คํธ๋ ์ข/์ฐ์ธก ๋ฒํผ ํด๋ฆญ ์ ๋ค์ ์์์ ์กฐ๊ณต ์นด๋๋ค์ด ๋ณด์ ๋๋ค.
- PC์์ ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์กฐ๊ณต ๋ฆฌ์คํธ ์ฒซ ์์์ผ ๋๋ ์ข์ธก ๋ฒํผ์ด ๋ณด์ด์ง ์๊ณ , ๋ง์ง๋ง ์์์ผ ๋๋ ์ฐ์ธก ๋ฒํผ์ด ๋ณด์ด์ง ์์ต๋๋ค.
- Tablet์์ ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์กฐ๊ณต ๋ฆฌ์คํธ ๋ชฉ๋ก ์์ญ์ด ํ๋ฉด์ ๋๋น๋ฅผ ๋์ด๊ฐ ๊ฒฝ์ฐ ํฐ์น๋ก ์ข์ฐ ์คํฌ๋กค ๊ฐ๋ฅํฉ๋๋ค.
- Mobile์์ ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์กฐ๊ณต ๋ฆฌ์คํธ ๋ชฉ๋ก ์์ญ์ด ํ๋ฉด์ ๋๋น๋ฅผ ๋์ด๊ฐ ๊ฒฝ์ฐ ํฐ์น๋ก ์ข์ฐ ์คํฌ๋กค ๊ฐ๋ฅํฉ๋๋ค.
- ํ์์ ๊ธฐ๋ค๋ฆฌ๋ ์กฐ๊ณต์์ ์ํ๋ ์์ด๋ ์นด๋์ 'ํ์ํ๊ธฐ' ๋ฒํผ์ ๋๋ฅด๋ฉด ํด๋น ์์ด๋์ ํ์ํ ์ ์๋ ๋ชจ๋ฌ์ฐฝ์ด ๋ํ๋ฉ๋๋ค.
์์ธ๊ธฐ๋ฅ
- ์ถฉ์ ํ ๊ธ์ก์ ์ ํํ โ์ถฉ์ ํ๊ธฐโ ๋ฒํผ์ ๋๋ฅด๋ฉด localstorage๋ก ๊ด๋ฆฌ๋๋ ๋ด ํฌ๋ ๋ง์ด ์ถฉ์ ๋ฉ๋๋ค.
์์ธ๊ธฐ๋ฅ
- ๋ด ํฌ๋ ๋ง ๋ณด๋ค ๋ถ์กฑํ ํฌ๋ ๋ง์ ์ ๋ ฅ์ ํ์๋ โํ์ํ๊ธฐ" ๋ฒํผ์ด ํ์ฑํ ๋ฉ๋๋ค.
- ํ์ฑํ ๋ โํ์ํ๊ธฐโ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ํ์์ด ์๋ฃ๋ฉ๋๋ค.
- ํ์ํ ๋งํผ localstorage์์ ๊ด๋ฆฌ๋๋ ํฌ๋ ๋ง์ด ์ค์ด๋ญ๋๋ค.
์์ธ๊ธฐ๋ฅ
- ๋ด ํฌ๋ ๋ง์ localstorage๋ก ๊ด๋ฆฌํฉ๋๋ค.
- ์ด๋ฌ์ ์ฌ์ ์์ด๋ ํญ์ ํด๋ฆญํ๋ฉด ํฌํ๊ฐ ๋ง์ ์์ผ๋ก ์ฌ์ ์์ด๋์ ๋ณด์ฌ์ค๋๋ค.
- ์ด๋ฌ์ ๋จ์ ์์ด๋ ํญ์ ํด๋ฆญํ๋ฉด ํฌํ๊ฐ ๋ง์ ์์ผ๋ก ๋จ์ ์์ด๋์ ๋ณด์ฌ์ค๋๋ค.
- '์ฐจํธ ํฌํํ๊ธฐ' ๋ฒํผ์ ํด๋ฆญ ์ ํฌํํ๊ธฐ ๋ชจ๋ฌ์ฐฝ์ด ๋ํ๋ฉ๋๋ค.
์์ธ๊ธฐ๋ฅ
- ํฌํํ๋ ๋ฐ 1000 ํฌ๋ ๋ง์ด ์๋ชจ๋ฉ๋๋ค.
- ์ํ๋ ์์ด๋์๊ฒ ๋ฌดํ์ผ๋ก ํฌํํ ์ ์์ต๋๋ค.
- ํฌํํ ๋งํผ localstorage์์ ๊ด๋ฆฌ๋๋ ํฌ๋ ๋ง์ด ์ค์ด๋ญ๋๋ค.
์์ธ๊ธฐ๋ฅ
- ๊ด์ฌ ์๋ ์์ด๋๋ก ์ถ๊ฐํ๊ณ ์ถ์ ์นด๋๋ฅผ ์ค๋ณต์ผ๋ก ์ ํ์ ํ ์ ์์ต๋๋ค.
- ์ ํ๋ ์นด๋๋ ์ฒดํฌํ์๊ฐ ๋ฉ๋๋ค.
- '์ถ๊ฐํ๊ธฐ' ๋ฒํผ์ ๋๋ฅด๋ฉด ์ ํ๋ ์นด๋๋ค์ด ๋ด๊ฐ ๊ด์ฌ์๋ ์์ด๋์ ์ถ๊ฐ๊ฐ ๋ฉ๋๋ค.
axios๋ก ์๋ฒ์ ํต์ ํ๋ ๋ชจ๋ ๋ถ๋ถ์์ ์๋ฒ ์ฃผ์์ ์ฝ๋๊ฐ ๋ฐ๋ณต๋์ด ์ด๋ฅผ ์ค์ด๊ธฐ ์ํด custom axios๋ฅผ ์ฌ์ฉํ์์ต๋๋ค.
import axios from "axios";
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
const dispatcher = async (options) => {
const client = axiosInstance({ ...options });
await client;
return client;
};
export default dispatcher;
๋น๋๊ธฐ ์์ฒญ์ ๊ด๋ฆฌํ๋ ์ปค์คํ ํ ์ ์ฌ์ฉํ์ฌ ๋ก๋ฉ ์ํ, ์๋ฌ ์ํ ๋ฑ์ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก ํ์ต๋๋ค.
import { useState, useEffect } from "react";
import dispatcher from "api/dispatcher";
// @ts-check
/**
* RequestConfig
* @typedef {Object} RequestConfig
* @property {AxiosRequestConfig} options - axios instance์ ์ ๋ฌํ options
* @property {boolean} skip - true: ๋ง์ดํธ๋ ๋ data fetching ๊ธ์ง
* @property {any[]} deps - useEffect ์์กด์ฑ ๋ฐฐ์ด
*/
/**
* @param {RequestConfig} params
* @returns {Object} Object - { data, isLoading, error, requestFunc }
*/
export default function useRequest({ options, skip = false, deps = [] }) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const requestFunc = async (...args) => {
setIsLoading(true);
setError(null);
try {
const response = await dispatcher({ ...options, ...args });
setData(() => response);
return response;
} catch (err) {
setError(() => err);
return err;
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (skip) return;
requestFunc();
}, deps);
return { data, isLoading, error, requestFunc };
}
ํ์ฅ์ฑ ์๊ณ ์ฝ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด ํฉ์ฑ ์ปดํฌ๋ํธ ํจํด์ ์ฌ์ฉํ์์ต๋๋ค.
const Button = Object.assign(DefaultButton, {
Border: BorderButton,
Arrow: ArrowButton,
Round: RoundButton,
Radio: RadioButton,
Text: TextButton,
Link: LinkButton,
});
export default Button;
import Button from 'components/Button';
export default function ExampleComponent() {
return (
<>
<Button.Arrow />
<Button.Round />
</>
)
}
Jotai๋ฅผ ์ฌ์ฉํ์ฌ atom๋จ์์ localStorage๋ฅผ ๊ด๋ฆฌํ์ฌ ์ฝ๋ ์ค๋ณต์ ํด๊ฒฐํ์ต๋๋ค.
import { atom } from "jotai";
const storedCreditAtom = atom(localStorage.getItem("Credit") || 0);
const creditAtomWithPersistence = atom(
(get) => get(storedCreditAtom),
(get, set, newCredit) => {
set(storedCreditAtom, newCredit);
localStorage.setItem("Credit", newCredit);
},
);
export default creditAtomWithPersistence;
ํ์ด์ง ๋ด์ ์ฌ์ฉ๋๋ ๋ชจ๋ ์ด๋ฏธ์ง๋ฅผ Webp ํ์ฅ์๋ก ๋ณํํ์ต๋๋ค.
๋ํ Intersection Observer API๋ฅผ ์ฌ์ฉํด Lazy Loading ๊ธฐ๋ฒ์ ์ ์ฉํ์ฌ ๋ชฉ๋ก ํ์ด์ง์ ๋ง์ดํ์ด์ง์ ์ด๋ฏธ์ง๊ฐ viewport ๋ด์ ๋ค์ด์ค๋ ์๊ฐ ๋ก๋ฉ๋๋๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
import { useEffect, useRef, useState } from "react";
export default function useLazyImageObserver({ src }) {
const [imgSrc, setImgSrc] = useState("");
const imgRef = useRef(null);
useEffect(() => {
let observer;
if (imgRef && !imgSrc) {
observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setImgSrc(src);
if (imgRef.current) observer.unobserve(imgRef.current);
}
});
},
{ threshold: 0.25 },
);
observer.observe(imgRef.current);
}
return () => {
observer && observer.disconnect();
};
}, [imgRef, imgSrc, src]);
return { imgSrc, imgRef };
}
๋คํธ์ํฌ๊ฐ ์๋๊ฐ ๋๋ฆฐ ํ๊ฒฝ์์ ์ ์ํ์ ๊ฒฝ์ฐ, ๋ก๋ฉ ์ค์ ์ปจํ ์ธ ์ค๊ณฝ์ ๋ํ๋ด๋ UI๋ฅผ ๊ตฌํํ์ต๋๋ค.
- ์ด๋ฒ ํ์ ์ผ๋ก ๊ฐ๋ฐ ๋ฅ๋ ฅ๋ ์ค์ํ์ง๋ง ํ์ ๋ฅ๋ ฅ๋ ์ค์ํ๋ค๋ ๊ฒ์ ๋๊ผ์ต๋๋ค. ๋ ์ ๋ง ์ข์ ํ์๋ถ๋ค์ ๋ง๋์ ํฐ ์ฑ์ฅ์ ํ ์ ์์ด์ ํ์๋ค์๊ฒ ๊ฐ์ฌํฉ๋๋ค:)
- ์ฒ์์ผ๋ก ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์ข์ ํ์๋ค์ ๋ง๋ ๊ฐ์ง ๊ฒฝํ๊ณผ ์ข์ ์ถ์ต์ ์ป๊ฒ ๋์์ต๋๋ค. ๋ค์ํ ๊ธฐ์ ๋ค์ ๋ณด๋ฉด์ ๋ถ์กฑํ ๋ถ๋ถ์ ํ์ ํ๊ธฐ ์ฌ์ ๊ณ ์ด๋ฅผ ํตํด ์ค์ค๋ก ๋ฌธ์ ์ ์ ์ฐพ๊ณ ํด๊ฒฐํ๋ ๋ฅ๋ ฅ์ด ํฅ์๋์์ต๋๋ค.๐ฅฐ
- ํ ํ์ ๊ฐ๋ฐ์ด ์ฒ์์ด์๋๋ฐ ์ข์ ํ์๋ค๊ณผ ํ ์ ์์ด์ ์ด์ด ์ข์๋ ๊ฒ ๊ฐ์ต๋๋ค. ํฌ๋ช ํ๊ฒ ์ํตํ๋ ๊ฒ์ด ์ ๋ง ์ค์ํ๋ค๋ ๊ฒ๊ณผ ํ์ ์์์ git ๊ด๋ฆฌ๋ฅผ ๋ฐฐ์ ๊ณ , ๋ ํ๋ก์ ํธ ์งํํ๋ฉด์ ์๋ก ์ฑ์ฅ์ ๋ง์ด ํ ์ ์์๋ ์๊ฐ์ด์๋ค๊ณ ์๊ฐํฉ๋๋ค. ๐
- ํ์ ์ ํตํด ์ ๋ถ์กฑํ ์ ์ ๋๋ ์ ์์๊ณ , ๋ ๋ฐ์ ํ ์ ์๋ ๊ธฐํ๊ฐ ๋์์ต๋๋ค. ์๋ก์ด ๊ฒ์ ์ฃผ์ ํ์ง๋ง๊ณ ๋์ ํด์ผ๊ฒ ์ต๋๋ค ๐