ํ๋๋์ฅ(ํ๋ณตํ ๋๋ค๋ฅผ ์ํ ๋ํ์ ์ฅ์)
๋๋ค ์ฌ๊ฑด,์ฌ๊ณ ์ ๋ฌธ์ ์ ๋ณด๋ค ์ฝ๊ฒ ์ ๊ทผํ๊ณ ๊ณต์ ํ๋ ์ปค๋ฎค๋ํฐ ์๋น์ค
๐ ์ด์ํ ๐
์ญํ :
- ํค๋
- ํ์ด์ง ์ด๋ ๋ฐ ๋ก๊ทธ์์
- ๋ฉ์ธํ์ด์ง
- ๋ฐ์ํ UI ๊ตฌํ
- Kakao Map API๋ก ์ง๋์ ๋ง์ปค ๋ฐ ํด๋ฌ์คํฐ๋ฌ ์ฌ์ฉ
- ํ ์ง์ญ ๊ฒ์๋ฌผ ์ธํผ๋ํธ ์คํฌ๋กค ๊ตฌํ
- ์ง์ญ ๊ฒ์ ๋ฐ ์ด๋ ๊ธฐ๋ฅ
- ํ์ฌ ์์น ์ด๋ ๊ธฐ๋ฅ
- ์ง๋ ํ ์ง์ญ ์ฃผ์ ํ์
- ๋ง์ดํ์ด์ง
- ๋ฐ์ํ UI ๊ตฌํ
- ์ฌ์ฉ์๊ฐ ๊ฒ์ํ ๊ฒ์๋ฌผ, ๋๊ธ์ ์ด ๊ฒ์๋ฌผ, '๋๋ ๋ถํธํด์' ๊ฒ์๊ธ ๋ชฉ๋ก ๋ณด๊ธฐ
- ํ์ด์ง๋ค์ด์ ์ฒ๋ฆฌ
๐ ์ํ๋ฃจ ๐
์ญํ :
- ํด๋ ๊ตฌ์กฐ ์ ๋ฆฝ ๋ฐ eslint, prettier ์ค์
- ํ์๊ฐ์ , ๋ก๊ทธ์ธ ๋ชจ๋ฌ ๊ตฌํ ๋ฐ ๋ฐ์ํ UI
- ์ผ๋ฐ ๋ก๊ทธ์ธ, ํ์๊ฐ์ ๋ฐ ์์ ๋ก๊ทธ์ธ (์นด์นด์คํก) ๊ตฌํ
- JWT(Access Token, Refresh Token) ๊ด๋ฆฌ
- ์์ธ ํ์ด์ง
- ๊ฒ์๊ธ ์กฐํ, '๋๋ ๋ถํธํด์', 'ํด๊ฒฐํ์ด์' ๊ธฐ๋ฅ, ๊ฒ์๊ธ ์ญ์ ๊ธฐ๋ฅ, ๋ฐ์ํ UI
- ๋๊ธ ๋ฐ์ํ UI ์์
- 'ํด๊ฒฐํ์ด์' ๋์ ์ ์๋ฃ ๊ฒ์๊ธ ๊ตฌํ
- ๋ง์ดํ์ด์ง
- ์ ์ ์ ๋ณด ์กฐํ, ์ด๋ฉ์ผ ์ถ๊ฐ ๋ฑ๋ก, ๋๋ค์ ๋ณ๊ฒฝ, ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ, ํ์ ํํด ๊ธฐ๋ฅ, ๋ฐ์ํ UI
- 404 ํ์ด์ง ๊ตฌํ
- ๋ฒ์ฉ์ ์ผ๋ก ์ฌ์ฉ๋๋ api instance ๋ฐ interceptor, ๋ชจ๋ฌ, ๋ฒํผ, Media Query, Global Theme, Toast ์๋, confetti HOC ๋ฑ ๊ตฌํ
๐ ์ด์คํธ ๐
์ญํ :
-
AWS ํ๋ก ํธ ๋ฐฐํฌ(https), CI / CD ๊ตฌ์ถ
-
๊ฒ์๊ธ CRUD ๊ธฐ๋ฅ ๊ตฌํ
-
๋๊ธ ๊ธฐ๋ฅ: ์กฐํ, ์์ฑ, ์ญ์
-
Kakao ์ง๋ API๋ฅผ ํ์ฉํ ์๋, ๊ฒฝ๋, ์ฃผ์ ์ ์ก ๋ฐ ๋ถ๋ฌ์ค๊ธฐ ๊ธฐ๋ฅ
-
FormData ์ด๋ฏธ์ง ์ ์ก ์์ฒญ ๊ธฐ๋ฅ
-
SSE ์ฐ๊ฒฐ์ ํตํ ์ค์๊ฐ ์๋ฆผ ๊ธฐ๋ฅ
-
๋๋ฉํ์ด์ง, ๊ฒ์๊ธ ์์ฑ, ์์ ํ์ด์ง, 404ํ์ด์ง์ ๋ํ PC, Tablet, Mobile ๋ฐ์ํ UI ๊ตฌํ
์์ ์ด ๋๋ ๋๋ค์ ๋ถํธ์ฌํญ์ ๋ํด ๊ฒ์๊ธ๊ณผ ๋๊ธ์ ์์ฑํ๋ฉฐ, ์ด๋ฅผ ํตํด ์ปค๋ฎค๋ํฐ ๋ด์์ ์์ ๋กญ๊ฒ ์๊ฒฌ์ ๊ณต์ ํ ์ ์์ต๋๋ค.
ํน์ ์ง์ญ์ ์ ๋ณด์ ๊ด์ฌ์ด ์๋ค๋ฉด "์ง์ญ ๊ฒ์" ๊ธฐ๋ฅ์ ํตํด ํด๋น ์ง์ญ์ผ๋ก ์ฝ๊ฒ ์ด๋ํ ์ ์์ต๋๋ค. ์์ ์ด ์ด๊ณ ์๋ ์ง์ญ ์ธ์ ๋ค๋ฅธ ์ง์ญ์์ ๋ฐ์ํ๋ ๋ถํธ์ฌํญ๋ค๋ ํ์ธํ๊ณ ๊ณต๊ฐํ ์ ์์ต๋๋ค.
์ง๋ ์์์ ์ง๊ด์ ์ผ๋ก ์ฃผ๋ณ์ ๋ถํธ์ฌํญ๋ค์ ํ๋์ ํ์ ํ ์ ์์ต๋๋ค. ํ์ฌ ์์น ๊ทผ๋ฐฉ์์ ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ ์๋์ง ์ฝ๊ฒ ์์๋ณผ ์ ์์ต๋๋ค.
๋ง์ด ํ์ด์ง์์๋ ์ฌ์ฉ์๊ฐ ์์ฑํ ๊ฒ์๊ธ, '๋๋ ๋ถํธํด์'๋ก ์์ํ ๊ฒ์๊ธ, ๊ทธ๋ฆฌ๊ณ ์์ฑํ ๋๊ธ์ ๋ํ ๊ฒ์๊ธ ๋ฑ์ ํ๋ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ถ๊ฐ๋ก, ํ์์ ๋ณด ์์ , ํ์ํํด ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ ๊ฐ์ธ ์ ๋ณด ๊ด๋ฆฌ๋ฅผ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
์ผ์์์ ๊ฒช๋ ๋ถํธํจ์ ๊ณต๊ฐํ๊ณ , ์ด๋ฅผ ์ปค๋ฎค๋ํฐ์ ๊ณต์ ํ์ฌ ์ธ์ง๋๋ฅผ ๋์ด๋ "์ฌ์ด๋ " ์ญํ ์ ํฉ๋๋ค. ๋ฐ๋ฉด, 'ํด๊ฒฐ๋์ด์' ๋ฒํผ์ ํน์ ๋ฌธ์ ์ ์ ๋ํ ํด๊ฒฐ ์ํฉ์ ์ฌ์ฉ์๋ค๊ณผ ๊ณต์ ํ ์ ์๊ฒ ํ๋ ๊ธฐ๋ฅ์ ๋๋ค. ์ด๋ฅผ ํตํด ์ฌ์ฉ์๋ค์ ๊ฒ์๊ธ์ ์ต์ ์ํ๋ฅผ ์ฝ๊ฒ ํ์ ํ๊ณ , ๋ฌธ์ ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ํด๊ฒฐ๋์์์ ํ์ธํ ์ ์์ต๋๋ค. ์ผ์ ๊ฐ์ ์ด์์ โํด๊ฒฐ๋์ด์โ ๊ฐ ๋์ ๋๋ฉด, ๊ฒ์๊ธ์ด ํด๊ฒฐ ์๋ฃ ์ฒ๋ฆฌ๋ฉ๋๋ค.
์ฌ์ฉ์๋ ๋ณธ์ธ์ด ์์ฑํ ๊ฒ์๋ฌผ์ ํ๋(๋๊ธ, ๋๋ ๋ถํธํด์, ํด๊ฒฐ๋์ด์ ๋ฑ)์ด ๋ฐ์ํ๋ฉด ๊ฐ๋ตํ ๋ด์ฉ๊ณผ ์๊ฐ์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์์ต๋๋ค.
๋ํ ์ฝ์ ์๋ฆผ๊ณผ ์ฝ์ง ์์ ์๋ฆผ์ ๊ตฌ๋ถํ ์ ์๊ณ , ๊ฐ๊ฐ์ ์๋ฆผ์ ํด๋ฆญํ๋ฉด ํด๋น ๊ฒ์๋ฌผ๋ก ์ด๋ํด ์์ธํ ๋ด์ฉ์ ํ์ธํ ์ ์์ต๋๋ค.
๊ฒ์๋ฌผ์ด โํด๊ฒฐ์๋ฃโ ์ฒ๋ฆฌ๋ ๊ฒฝ์ฐ์๋ ์นํ์ด์ง ๋ด์์์ ์๋ฆผ ๋ฟ๋ง ์๋๋ผ ์ด๋ฉ์ผ๋ ๋ฐ์๋ณผ ์ ์์ต๋๋ค.
access token, refresh token์ ํตํ ์ผ๋ฐ ๋ก๊ทธ์ธ ๋ฐ ์นด์นด์ค ์์ ๋ก๊ทธ์ธ์ ๊ตฌํํ์์ต๋๋ค.
์๊ตฌ์ฌํญ | ์ ํ | ๊ธฐ์ ์ ์ ํํ ์ด์ ๋ฐ ๊ทผ๊ฑฐ |
---|---|---|
์์ ์ ์ธ ์ฝ๋ | TypeScript | ํ์
์์ ์ฑ ์ ๊ณต: TypeScript์ ์ ์ ํ์ดํ ๊ธฐ๋ฅ์ ํตํด ๋ฐํ์ ์ค๋ฅ๋ฅผ ์ค์ผ ์ ์์ ์ฝ๋ ๊ฐ๋ ์ฑ ํฅ์: ๋ช ์์ ์ธ ํ์ ํ๊ธฐ๋ก ์ธํด ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ์ดํดํ๊ธฐ ์ฌ์์ง ๊ฐ๋ฐ ํจ์จ์ฑ ์ฆ๋: ์๋์์ฑ, ์ธํฐํ์ด์ค ํ์ธ ๋ฑ์ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์์ ํ์ฅ์ฑ ๋ฐ ์ ์ง๋ณด์ ์ฉ์ด: ํฐ ๊ท๋ชจ์ ํ๋ก์ ํธ์์ ๊ตฌ์กฐ์ ์ด๊ณ ๋ช ํํ ์ฝ๋๋ก ์ธํด ์ ์ง๋ณด์ ๋ฐ ํ์ฅ์ด ์ฉ์ดํจ |
๊ฐ๋ฒผ์ด ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | Recoil | ๋ณด์ผ๋ฌ ํ๋ ์ดํธ ์ฝ๋๋ฅผ ์ค์ผ ์ ์์ ์ง๊ด์ ์ด๊ณ ์ฌ์ด ์ฌ์ฉ๋ฒ์ผ๋ก ํ์ ์ ์ ๋ฆฌ redux ๋ณด๋ค ์๋์ ์ผ๋ก ๊ฐ๋ฒผ์ด recoil ์ ํ |
์๋ฒ ์ํ ์ฝ๋๋ฅผ ์ผ๊ด๋๊ฒ ๊ด๋ฆฌ | ReactQuery v4 | ์๋ฒ์ชฝ์ ๋ฐ์ดํฐ๋ฅผ ์ข๋ ์ฝ๊ฒ ๊ด๋ฆฌํ๊ธฐ ์ฌ์ ๋ฐ์ดํฐ ํจ์นญ, ์บ์ฑ, ๋๊ธฐ์ ์๋ฒ์ ์ํ์ ์
๋ฐ์ดํธ์ ์ฉ์ด ๋ณ๋์ ์ต์ ์ ์ง์ํ์ฌ ๋ณต์กํ ์ฝ๋๋ฅผ reactQuery ๋ก์ง์ ํตํด ์งง์ ์ฝ๋๋ก ๋์ฒด reactQuery์์ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ฌ ์ฝ๊ฒ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๊ฐ๋ฅ(infinity query, InvalidateQueries) ํ๋ก์ ํธ ๊ตฌ์กฐ๊ฐ ๊ธฐ์กด๋ณด๋ค ๋จ์ํด์ ธ ์ ์ง๋ณด์ํ๊ธฐ ์ฝ๊ณ ์๋ก์ด ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ตฌ์ถ ์ต์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ ํด ๋ด์ผ๋ก์จ ์ต์ ๊ธฐ์ ๋์ ์ ์ต์ํด์ง๊ธฐ ์ํจ |
๋น ๋ฅธ ํ๋ก์ ํธ ์์ | CRA | ๋ณต์กํ ํ๊ฒฝ์ค์ ์ ๊ฑด๋๋ฐ๊ณ ๋ฐ๋ก ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ ์ง์คํ ์ ์์(์นํฉ, ๋ฐ๋ฒจ ๋ฑ์ ๋ณต์กํ ์ค์ ์ CRA๊ฐ ๋ฏธ๋ฆฌ ํด์ค) CRA๋ TS๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ๊ธฐ ๋๋ฌธ์ ๋ณต์กํ ์ค์ ์์ด ์ฆ์ TS์ React์ ์กฐํฉ์ผ๋ก ๊ฐ๋ฐ์ ์์ํ ์ ์์ |
instance์ interceptor ๊ธฐ๋ฅ | Axios | ์ ํ๋ฆฌ์ผ์ด์
๋ด์ ๋ชจ๋ HTTP ์์ฒญ๊ณผ ์๋ต์ ์ผ๊ด๋๊ฒ ๊ด๋ฆฌํ ์ ์์ด, ์ฝ๋์ ๋ณต์ก์ฑ์ ์ค์ Axios interceptor๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ด๋ ์๋ต์ ๊ฐ๋ก์ฑ์ ์ถ๊ฐ์ ์ธ ์์ ์งํ. ์ด๋ฅผ ํตํด ํ ํฐ ๊ฐฑ์ ์์ ์ ์ฝ๊ฒ ์ฒ๋ฆฌ Axios instance๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ๋ณต์ ์ธ ์ฝ๋๋ฅผ ์ค์ด๊ณ , ํ๋ก์ ํธ ์ ๋ฐ์ ๊ฑธ์ณ ์ผ๊ด๋ HTTP ํด๋ผ์ด์ธํธ ๊ตฌ์ฑ์ ์ ๊ณต ์์ฒญ์ด๋ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก JSON์ผ๋ก ๋ณํํด ์ฃผ์ด ๋ฐ์ดํฐ ํ์์ ๋ํ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ ์์ด๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ๊ด๋ฆฌ |
์นํ์ด์ง ๋ก๋ฉ ์๋ ํฅ์ | CloudFront | CDN(Content Delivery Network)๋ฅผ ํ์ฉํด ์นํ์ด์ง ๋ก๋ฉ ์๋ ํฅ์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์บ์ฑํ์ฌ ์น์ฌ์ดํธ ์ต์ ํ, ์ปจํ ์ธ ์ ๊ณต ์๋ ํฅ์ |
์ต์ ํ๋ ๋ฆฌ๋ ๋๋ง | react-hook-form | ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ์ค์ด๊ณ ์ฑ๋ฅ์ ํฅ์์ํด ์ ๋ ฅ ์ปจํธ๋กค๊ณผ ํผ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ์ปดํฌ๋ํธ์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ ๋ค์ํ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ํตํฉ์ด ์ฉ์ดํ์ฌ, ์ ์ฐํ UI ๊ตฌํ ๊ฐ๋ฅ |
ํ์ ์ ์ ๋ฆฌํ style tool | styled-component | ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๊ฐ๋ฐ์ด ๊ฐ๋ฅ JS์ CSS ์ฌ์ด์ ์์์ ํจ์๋ฅผ ์ฝ๊ฒ ๊ณต์ media query๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ด ๋ฐ์ํ UI ๊ฐ๋ฐ ๊ฐ๋ฅ |
์ด๋ฏธ์ง ๋ฐ ๋น๋์ค ๊ด๋ฆฌ | Cloudinary | ์๋ณธ ๋ฏธ๋์ด๋ฅผ ์์ ํ๊ฒ ๋ณด๊ดํ๊ณ ํ์์ ๋ฐ๋ผ ์ฌ๋ฌ ๋ฒ์ ์ ๋ณํ๋ ๋ฏธ๋์ด๋ฅผ ์์ฑํ์ฌ ์ ์ฅ ๊ณต๊ฐ์ ์ ์ฝํ ์ ์์ ์๋์ผ๋ก ์ด๋ฏธ์ง์ ๋น๋์ค๋ฅผ ์ต์ ํํ์ฌ ์ฌ์ฉ์์๊ฒ ๋ ๋น ๋ฅด๊ฒ ์ ๊ณตํ๋ฉฐ ๋์ญํญ ๋น์ฉ์ ์ ๊ฐํจ |
์ง๋ API | Kakao ์ง๋ API | ์๋์ ์ผ๋ก ๋ ์ ํํ๊ณ ์์ธํ ๊ตญ๋ด ์ง๋ ๋ฐ์ดํฐ ๊ฐ๋ฐ์๋ฅผ ์ํ API ์ง์๊ณผ ๋ฌธ์ํ๊ฐ ์ ๋์ด์์ด ๊ฐ๋ฐ ์๊ฐ์ ๋จ์ถํ ์ ์์ ์นด์นด์ค ์๋น์ค์ ์ต์ํ ๊ตญ๋ด ์ฌ์ฉ์๋ค์๊ฒ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํจ |
์ค์๊ฐ ํผ๋๋ฐฑ | react-toastify | ์ ์ ์๊ฒ ์ค์๊ฐ ํผ๋๋ฐฑ์ ์ ๊ณตํด ์ ์ ์๊ฒ ํ์ฌ ์ํ๋ ์งํ ์ํฉ์ ์ฝ๊ฒ ์๋ฆผ ํ์ํ ๋๋ง ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ์ฌ ๋ฆฌ์์ค๋ฅผ ์ ์ฝํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ ์ง ์์ฒด์ ์ผ๋ก ์๋ฆผ์ ์๋ ์๋ฉธ, ์์น ์ง์ , ์ ๋๋ฉ์ด์ ํจ๊ณผ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ง์ํด ๊ฐ๋ฐ ๊ณผ์ ์ ๋จ์ํํ๊ณ ์๊ฐ์ ์ ์ฝ |
๐ ๋ฌธ์ 1: ๋ฉ์ธํ์ด์ง์์ ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋ํ ํ ๋ค์ ๋์์์ ๋ ์์น๊ฐ ์ด๊ธฐ๋ก ๋์๊ฐ๋ ๋ฌธ์
์์ฑ์: ์ด์ํ
๋ฌธ์ : ๋ฉ์ธํ์ด์ง์์ ์์น๋ฅผ ์ด๋ํ ํ ๋ค๋ฅธ ํ์ด์ง์ ๊ฐ๋ค๊ฐ ๋ค์ ํ ์์น๋ก ์ฌ ๊ฒฝ์ฐ ์ง๋๊ฐ ์๋กญ๊ฒ ์ด๊ธฐํ๋๊ธฐ ๋๋ฌธ์ ๋ฉ์ธํ์ด์ง๋ก ์ฌ ๋๋ง๋ค ํ์ฌ ์์น์์ ์ง๋๊ฐ ์์ํ๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ์:
- ์ง๋ ์์น์ ์ค ๋ ๋ฒจ์ ์ธ์ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ์ฌ ๋ฉ์ธํ์ด์ง์ ์ฌ ๋๋ง๋ค ์ธ์ ์คํ ๋ฆฌ์ง์ ๊ฐ์ด ์๋์ง ํ์ธํ์ฌ ์ต์ ์ ์ธํ ํ์ฌ ์ง๋ ์์น์ ์ค ๋ ๋ฒจ์ ๋ง์ถฐ์ค๋๋ค.
- ๋ก์ปฌ ์คํ ๋ฆฌ์ง๋ฅผ ์ฌ์ฉํ๋ ๋์ ์ธ์ ์คํ ๋ฆฌ์ง๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๊ฐ ํ์ด์ง๋ฅผ ๋๊ฐ๋ฉด ์ค์ ์ด ์ด๊ธฐํ ๋๋๋ก ํ์์ต๋๋ค.
useEffect(() => {
if (!map) {
const saveMapCenter = sessionStorage.getItem('mapCenter');
const saveMapLevel = sessionStorage.getItem('mapLevel');
saveMapCenter && setMapCenter(JSON.parse(saveMapCenter));
saveMapLevel && setZoomLevel(JSON.parse(saveMapLevel));
const options = {
center: new window.kakao.maps.LatLng(mapCenter.lat, mapCenter.lng),
level: zoomLevel,
};
setMap(new window.kakao.maps.Map(mapRef.current, options));
}
}, []);
์์ฑ์: ์ด์ํ
๋ฌธ์ : ๊ฒ์์ ๊ตฌํํ๋ค ๋ณด๋ ์ฌ๋ฌ ๊ฐ์ง ์ด์๋ฅผ ๋ง๋ฌ์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ์:
- ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ ๋ ์๋ฒ์์ ์ค๋ฅ๋ฅผ ๋ณด๋ด๋ ๋์ ๋น ๋ฐฐ์ด๋ก ์๋ต์ ๋ณด๋ด๋๋ก ๋ณ๊ฒฝํฉ๋๋ค.
- ๊ฒ์์ ์ํ์ฌ API๋ฅผ ๋๋ฒ ํธ์ถํด์ผํด์ UX๋ฅผ ์ ํดํ๋ ๋ฌธ์ ๊ฐ ์์๋๋ฐ, ๊ฒ์ ๋ชฉ๋ก๊ณผ ์ขํ๋ฅผ ํ ๋ฒ์ ๋ฐ๋ ํ๋์ API๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค.
- ๊ฒ์์ด ์ ๋ ฅ ์ด๋ฒคํธ๊ฐ ์์ฃผ ๋ฐ์ํ์ฌ ๋ถํ์ํ ๋ ๋๋ง์ ๋ฐฉ์งํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๊ธฐ ์ํด ๋๋ฐ์ด์ฑ์ ์ฌ์ฉํฉ๋๋ค.
const [inputValue, setInputValue] = useState<string>('');
const [keyword, setKeyword] = useState<string>('');
const { data } = useSearchListQuery(keyword);
const debouncedSetKeyword = useCallback(
useDebouncedCallback((value: string) => {
setKeyword(value);
}, 500),
[],
);
useEffect(() => {
debouncedSetKeyword(inputValue);
}, [inputValue]);
์์ฑ์: ์ด์คํธ
๋ฌธ์ : ๋๊ธ์ ์์ฑ ๋๋ ์ญ์ ํ ๋ ์นํ์ด์ง์ ์ฆ์ ๋ฐ์๋์ง ์๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ์:
- ํ์ฌ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด ์๋ฒ์์ ๋ณ๊ฒฝ๋ ๊ฐ์ useState๋ก ๋ค์ ๋ด์ ๊ฐ์ ๋ณ๊ฒฝํ ๋๋ง๋ค ์นํ์ด์ง์ ๋ฐ์ํฉ๋๋ค.
- ๋ ๋ฐ์ ๋ ๋ฐฉ๋ฒ์ผ๋ก react-query์ useQueryClient์ invalidateQueries๋ฅผ ํ์ฉํ์ฌ ์บ์๋ฅผ ๋ฌดํจํํ์ฌ ์ต์ ๊ฐ์ผ๋ก ๊ฐฑ์ ํฉ๋๋ค.
const queryClient = useQueryClient();
const deleteCommentMutation = useMutation<void, unknown, string>(
deleteComment,
{
onSuccess: () => {
queryClient?.invalidateQueries(['postDetail', postId]);
},
},
);
const createCommentMutation = useMutation<void, unknown, CreateCommentArgs>(
createComment,
{
onSuccess: () => {
queryClient?.invalidateQueries(['postDetail', postId]);
},
});
์์ฑ์: ์ด์คํธ
๋ฌธ์ : ์ฌ๋ฌ ์ฅ์ ์ด๋ฏธ์ง์ ๋ค๋ฅธ ํ์์ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ์ ์กํด์ผ ํ์ต๋๋ค.
ํด๊ฒฐ๋ฐฉ์:
- ์ด๋ฏธ์ง ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํ์ผ ์์ฒด๋ฅผ ๋ฐฐ์ด๋ก FormData์ appendํ๊ณ ์๋ฒ์ ์ ์กํฉ๋๋ค.
- ์ด๋ฏธ์ง์ ํจ๊ป ๋ค๋ฅธ ํ์(string, number)์ ๋ฐ์ดํฐ๋ฅผ JSON.stringify๋ก ์ ์กํ๊ณ , ๊ตฌ๋ณํ๊ธฐ ์ฝ๋๋ก blob ํํ๋ก JSON ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ธ ์ ์กํฉ๋๋ค.
const sendPostRequest = async () => {
const formData = new FormData();
const postJSON = JSON.stringify({
title: post.title,
content: post.content,
latitude: post.latitude,
longitude: post.longitude,
address: post.address,
});
const blob = new Blob([postJSON], { type: 'application/json' });
formData.append('post', blob);
post.images.forEach((image) => {
formData.append(`images`, image);
});
return axios.post(`${process.env.REACT_APP_API_URI}/api/posts`, formData, {
headers: {
Authorization: `Bearer ${token}`,
},
});
};
์์ฑ์: ์ํ๋ฃจ
๋ฌธ์
ํ์๊ฐ์ ๋ชจ๋ฌ์์ step์ด ๋ณํ ๋๋ง๋ค ๊ฐ์ ๋ชจ๋ฌ ์์์ ๋ค๋ฅธ ํ๋ฉด์ ๋ณด์ฌ์ฃผ์ด์ผ ํ์ง๋ง, ๋ฐ์ดํฐ๋ ์ด๊ธฐํ๋์ง ์๊ณ ์ ์ฅ๋์ด ์๋ค๊ฐ ๋ง์ง๋ง ์คํ ์์ ํ ๋ฒ์ post ์์ฒญํ๋ ๊ฒ์ ๋ํ ๋ฌธ์
์๋
step๋ณ๋ก ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ๋ ๋ชจ๋ ์ ๋ณด๋ฅผ,
- ๋ก์ปฌ state๋ก ๊ด๋ฆฌ
- ๋ถํ์ํ ๋ ๋๋ง์ด ๊ณผ๋คํ๊ฒ ๋ง์์ง
- step์ ์ด๋ํ ๋๋ง๋ค ์ ์ฅ๋์ด์๋ state๊ฐ ์ฌ๋ผ์ง
- ์ ์ญ ์ํ ๊ด๋ฆฌ (recoil)์ ์ ์ฅํ์๋ค๊ฐ, ๋ง์ง๋ง step์์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํฉ์ณ post ํด์ฃผ๊ธฐ
- step์ ์ด๋ํด๋ ์ด์ ์คํ ์์ ์ ๋ ฅํ ์ ๋ณด๋ ๋จ์์์ง๋ง, ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌ๋๋ ๋ฐ์ดํฐ ์์ด ๊ณผ๋ํ๊ฒ ๋ง์์ง๋ ๋ฌธ์
ํด๊ฒฐ
react-hook-form์ ์ฌ์ฉํด์ ๊ด๋ฆฌ
- step์ด ๋ฌ๋ผ์ง๋๋ผ๋ ์ฌ์ฉ์๋ค์ด ์ ๋ ฅํ๋ ์ ๋ณด๋ฅผ ํ๋์ form ์ผ๋ก ๊ด๋ฆฌํ ์ ์์
- react-hook-form์ด ์์ฒด ์ ๊ณตํ๋ ์ต์ ํ๋ฅผ ํตํด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ํผํจ
// ์ด๊ธฐ form ์ ์
export type LoginModalFormData = {
emailId: string;
emailDomain: string;
password: string;
};
export const useLoginModalForm = () => {
const defaultValues = {
emailId: '',
emailDomain: '',
password: '',
};
const form = useForm({
defaultValues: {
...defaultValues,
},
mode: 'all',
});
return { form };
};
// form controller ์ฌ์ฉ
export const useLoginModalFormController = () => {
const { control, watch } = useFormContext<LoginModalFormData>();
const {
field: { value: passwordValue, onChange: onChangePassword },
} = useController({
control: control,
name: 'password',
});
// ์ดํ ์ค๋ต..
};
์์ฑ์: ์ํ๋ฃจ
๋ฌธ์
- โํด๊ฒฐ๋ ๋ฏผ์์ด์์โ ๊ฒ์๊ธ์ ํด๋ฆญํ๋ ์๊ฐ, ๋ชจ๋ฌ์ ๋์ ํด๊ฒฐ๋ ๊ฒ์๋ฌผ์์ ์๋ ค์ผ ํ๋ ์ํฉ
- ํด๋น ๋ชจ๋ฌ์ ๊ฒ์๊ธ๋ง๋ค ๋ชจ๋ฌ ์ฐฝ์ โ๋จ ํ ๋ฒ๋งโ ๋์์ ์๋ ค์ฃผ์ด์ผ ํ์
์๋
์ฒ์์๋ ๋ชจ๋ฌ ์ฐฝ์ด alert๋์๋์ง ์ฌ๋ถ๋ฅผ useState๋ก ๊ด๋ฆฌ
๊ทธ๋ฌ๋, useState๋ก alert์ฌ๋ถ๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋๋ฉด ํด๋น ํ์ด์ง๊ฐ ์ธ๋ง์ดํธ๋ ๋ state์ ๊ฐ์ด ์ด๊ธฐํ๋์ด, ๋จ ํ ๋ฒ๋ง ๋ชจ๋ฌ์ฐฝ์ ๋์ฐ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํ์
๋ ๋ฒ์งธ ์๋์์๋ ํ์ด์ง๊ฐ ์ธ๋ง์ดํธ๋์ด๋ alert์ ์ฌ๋ถ๊ฐ recoil๋ก ๊ด๋ฆฌ๋๊ณ ์์ด์ ๋ชจ๋ฌ ์ฐฝ์ด ๋ ๋ฒ ๋์ค๋ ์ํฉ์ ํด๊ฒฐ
๊ทธ๋ฌ๋ ์ด ๋ฐฉ์์๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ์ ์ด ์์๋๋ฐ,
- ๊ฐ๊ฐ์ ๊ฒ์๋ฌผ๋ง๋ค alert ์ฌ๋ถ๋ฅผ ์ ์ฅํด์ผ ํ๋๋ฐ, ๋จ์ง ํ ๋ฒ ์ด์ alert ๋๋ฉด atom๊ฐ์ด true๋ก ๋ฐ๋์ด ๊ฒ์๋ฌผ๋ค์ ๊ตฌ๋ถํ์ง ๋ชปํ๋ ๋ฌธ์
- ๋ก๊ทธ์ธ, ๋ก๊ทธ์์ ๊ธฐ๋ฅ ๊ตฌํ์ window.location.reload()๋ก ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ฐ, ์ด ๊ฒฝ์ฐ recoil ๊ฐ์ด ์ด๊ธฐํ๋๋ ๋ฌธ์
์ฆ โ๊ฒ์๋ฌผ๋ง๋คโ alert๋ฅผ ๋์ ๋์ง ์ ๋์ ๋์ง ์ฌ๋ถ๋ฅผ ๊ฐ๊ฐ ์ ์ฅํ๊ณ , โ์๋ก๊ณ ์นจ๋์ด๋โ ๊ฐ์ด ๋จ์์์ด์ผ ํ์
ํด๊ฒฐ
- ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๋ก๊ณ ์นจํ๋ ๋ก์ง์ด ๋ง์ด ์์ด recoil๋ง์ผ๋ก๋ ์ํ๋ ์ํ๋ฅผ ์์์ ์ผ๋ก ์ ์ฅํ ์ ์์
- ์ด๋ฅผ atomFamily์ ํจ๊ป ์ฌ์ฉํ์ฌ ๊ฒ์๊ธ ID๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํด ๋์ ์ผ๋ก atom์ ์์ฑ
- sessionStorage๋ฅผ ์ฌ์ฉํ ์ด์
- ๊ฒ์๊ธ ID๋น ๋ชจ๋ฌ์ด ์ด๋ ธ๋์ง ํ์ธํ๋ ๋ฐ์ดํฐ๋ ์ฅ๊ธฐ์ ์ผ๋ก ์ ์ฅ๋ ํ์๊ฐ ์์. ์ฆ ์ด๋ ์์์ ์ธ ๋ฐ์ดํฐ์ด๋ฏ๋ก sessionStorage๊ฐ ์ ํฉํ๋ค๊ณ ์๊ฐ
- ์ฌ์ฉ์๊ฐ ํญ์ ๋ซ์ผ๋ฉด ์ ์ฅ๋ ๋ฐ์ดํฐ๊ฐ ์ด๊ธฐํ๋๋ ๊ฒ์ด ๊ตฌํ ๋ชฉ์ ์ ๋ ์ ํฉํ๋ค๊ณ ์๊ฐ
// ์ ์
import { atomFamily } from 'recoil';
import { recoilPersist } from 'recoil-persist';
const { persistAtom } = recoilPersist({
key: 'isDoneAlerted',
storage: sessionStorage,
});
export const $isDoneAlertedFamily = atomFamily({
key: 'isDoneAlertedFamily',
default: false,
effects_UNSTABLE: (postId) => [
({ setSelf, onSet }) => {
const storedValue = sessionStorage.getItem(
`isDoneAlertedFamily_${String(postId)}`,
);
if (storedValue != null) {
setSelf(JSON.parse(storedValue));
}
onSet((newValue) => {
sessionStorage.setItem(
`isDoneAlertedFamily_${String(postId)}`,
JSON.stringify(newValue),
);
});
},
],
});
-----
//์ฌ์ฉ
const [isDoneAlerted, setisDoneAlerted] = useRecoilState(
$isDoneAlertedFamily(postId),
);
...
useEffect(() => {
if (localDoneCount === 5 && !isDoneAlerted) {
setIsReallyDone(true);
setisDoneAlerted(true);
openModal(EModalType.POP_UP, {
title: 'ํด๊ฒฐ ์๋ฃ ์ฒ๋ฆฌ๋ ๊ฒ์๋ฌผ์
๋๋ค',
cancelButton: false,
functionButton: {
label: '๋ซ๊ธฐ',
onClick: () => {
closeModal();
},
theme: 'emptyBlue',
},
});
}
}, [localDoneCount, isReallyDone]);