๐ ๋ฐฐํฌ URL
์ด๋ฉ์ผ ๋ก๊ทธ์ธ ํ ์คํธ ๊ณ์
- ID :
[email protected]
- Password :
daengnyang2022
๊ฐ์ ธ๋๋๋ฅ์ ๋ฐ๋ ค๋๋ฌผ์ ์ฌ๋ํ๋ ํปํจ์กฑ์ ์ํ SNS/์ปค๋ฎค๋ํฐ ์๋น์ค์ ๋๋ค.
- ๋ฐ๋ ค๋๋ฌผ์ด ์ฐ๋ ๋ฌผ๊ฑด์ ์ค๊ณ ๋ก ํ๋งค/๊ตฌ๋งคํ ์ ์์ต๋๋ค.
- ์ํ์ ํ๋งค/๊ตฌ๋งคํ์ง ์์๋ ์ผ์์ ๊ณต์ ํ๋ฉฐ ์ฆ๊ฑฐ์ด SNS ํ๋์ ํ ์ ์์ต๋๋ค.
- ๋ฐ๋ ค๋๋ฌผ ์ปค๋ฎค๋ํฐ ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์์ต๋๋ค.
FE ๊น๋ฏผ์น | FE ๊น์ํธ | FE ๋ฐฐ์น์ฐ | FE ์ด๊ด๋ ฌ |
---|---|---|---|
๐ GitHub ๋์์ธ ๋ฆฌ๋ |
๐ GitHub ๊ธฐํ ๋ฆฌ๋ |
๐ GitHub ํ๋ก์ ํธ ๋งค๋์ |
๐ GitHub ๊ฐ๋ฐ ๋ฆฌ๋ |
- IDE : Visual Studio Code 1.74.2
- OS : macOS Monterey, Windows 10
- FE : React v18, Styled-components v5, Axios v1.2.1
- BE : ์ ๊ณต๋ API ์ฌ์ฉ
- ๋ฒ์ ๊ด๋ฆฌ : Git, GitHub
- ์งํ ์ํฉ ๊ด๋ฆฌ(์นธ๋ฐ ๋ณด๋) : GitHub Projects
- ์ด์ ๊ด๋ฆฌ : GitHub Issues
- ๋ฌธ์ ๊ด๋ฆฌ : Notion
- ๋ฉ์ ์ : Discord
- API ํ ์คํธ : Postman
public/favicon/
: ํ๋น์ฝsrc/assets/
: ์๋น์ค์์ ์ฌ์ฉํ๋ ์์ ํ์ผ (ํฐํธ, ์์ด์ฝ, ์ด๋ฏธ์ง)src/components/
: ์๋น์ค์์ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ (์บ๋ฌ์ , ๊ณตํต ์ปดํฌ๋ํธ, ๊ณตํต ๋ ์ด์์)src/context/
: ์ ์ญ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๊ธฐ ์ํด ์ ์ํ Context ํ์ผsrc/hooks/
: ์ฌ์ฌ์ฉ์ ์ํด ๋ถ๋ฆฌํ Custom Hooksrc/pages/
: ๊ณตํต ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด ๋ง๋ ํ์ด์งsrc/routes/
: ํ์ด์ง ๋ผ์ฐํ ์ ์ํ ํ์ผsrc/styles/
: ์ ์ญ ์คํ์ผ ํ์ผsrc/utils/
: ์ฌ์ฌ์ฉ์ ์ํด ๋ถ๋ฆฌํ ์ ํธ ํ์ผ
๐ฆ ๊ฐ์ ธ๋๋๋ฅ
โโ ๐ฆ public
โย ย โโย ๐ favicon
โย ย โโย ๐ index.html
โโย ๐ฆ src
ย ย ย โโย ๐ assets
ย ย ย โย ย โโย ๐ fonts
ย ย ย โย ย โโย ๐ icons
ย ย ย โย ย โโย ๐ images
ย ย ย โโย ๐ components
ย ย ย โย ย โโย ๐ carousel
ย ย ย โย ย โโย ๐ common
ย ย ย โย ย โโย ๐ layout
ย ย ย โโย ๐ context
ย ย ย โโย ๐ hooks
ย ย ย โโย ๐ pages
ย ย ย โย ย โโย ๐ ChatPage
ย ย ย โย ย โโย ๐ CommunityPage
ย ย ย โย ย โโย ๐ FeedPage
ย ย ย โย ย โโย ๐ FollowListPage
ย ย ย โย ย โโย ๐ JoinPage
ย ย ย โย ย โโย ๐ LoginPage
ย ย ย โย ย โโย ๐ NotFoundPage
ย ย ย โย ย โโย ๐ PostPage
ย ย ย โย ย โโย ๐ ProductPage
ย ย ย โย ย โโย ๐ ProfileModificationPage
ย ย ย โย ย โโย ๐ ProfilePage
ย ย ย โย ย โโย ๐ SearchPage
ย ย ย โย ย โโ ๐ย SplashScreen
ย ย ย โโย ๐ routes
ย ย ย โโย ๐ styles
ย ย ย โโย ๐ utils
โโย ๐ App.jsx
โโย ๐ index.jsx
- ์๊ท๋ชจ ํ๋ก์ ํธ์ ๋ง๊ฒ Main, Develop, Feature ์ธ Branch๋ฅผ ์ฌ์ฉํ๋ ์ ๋ต ์ฌ์ฉ
- ์๊ตฌ์ฌํญ ํ์ ๋ฐ ํ๋ก์ ํธ ๊ท์น ์ค๋ฆฝ : 2022-11-29 ~ 2022-12-09
- ๊ณตํตUI ์ปดํฌ๋ํธ ๊ฐ๋ฐ : 2022-12-09 ~ 2022-12-13
- ํ์ด์ง ํผ๋ธ๋ฆฌ์ฑ : 2022-12-13 ~ 2022-12-17
- ๊ธฐ๋ฅ ๊ฐ๋ฐ : 2022-12-16 ~ 2022-12-27
- ๋ฒ๊ทธ ์์ ๋ฐ ์ ์ง๋ณด์ : 2022-12-26 ~ 2023-01-05
- ํ๋ก์ ํธ ์์ ์ , ๊ณตํต๋ ํ ๋ชฉํ๋ฅผ ์ธ์ฐ๊ณ ์๋์ง๋ฅผ ๊ฐํํ๊ธฐ ์ํด ๋ค์ด๋ฒํผ ์ค๋ฌธ ์งํ
- ๊ณต๋ ์์ ํด์ธ Figzam์ ์ด์ฉํ ํ์
- ๋ฑ๋ฑํ ๋ถ์๊ธฐ์ ํ์๊ฐ ์๋๋ผ ๋ชจ๋๊ฐ ์ฐธ์ฌํ ์ ์๋ ๊ฐ๋ฒผ์ด ๋ถ์๊ธฐ์์ ํ์๋ฅผ ์งํ
- ๋งค์ผ 9์ 20๋ถ ๋ฐ์ผ๋ฆฌ ์คํฌ๋ผ์ ํตํด ์ ๋ฌด ๊ณต์ ๋ฐ ์งํ ์ํฉ ํ์
- GitHub Wiki์ ๊ฐ๋ฐ ๊ท์น ๋ฑ๋ก
- GitHub Milestones์ ์ด์ฉํ ๋จ๊ณ๋ณ ๋ชฉํ ๊ด๋ฆฌ
- ํ๋ก์ ํธ ๋จ๊ณ๋ณ ๋ชฉํ๋ฅผ ๋ช ํํ๊ฒ ํ๊ธฐ ์ํด ๋ง์ผ์คํค ๋ฑ๋ก
- GitHub ์ด์ ๋ฑ๋ก ์ ๊ด๋ จ๋ ๋ง์ผ์คํค ์ ํ
- ์์ ์ GitHub Issues ๋ฑ๋ก
- ์๋ฌด๋ฆฌ ์์ ์์ ์ด๋ผ๋ ์์ํ ์ด์ ์ถ์ ์ ์ํด ์ด์ ๋ฐ๋์ ๋ฑ๋ก ํ ์์ ์งํ (์์ ํ๋๋น ์ด์ ํ๋)
- ์ปจ๋ฒค์ ํต์ผ์ ์ํด ์ด์ ํ ํ๋ฆฟ ์ฌ์ฉ
- ์ด์ ํด๊ฒฐ ํ Pull Request ์์ฑ
- ์ปจ๋ฒค์ ํต์ผ์ ์ํด PR ํ ํ๋ฆฟ ์ฌ์ฉ
- ํ์ 2๋ช ์ด์์ ์น์ธ์ ๋ฐ์์ผ ๋จธ์ง ๊ฐ๋ฅ
- GitHub Projects๋ฅผ ์ด์ฉํ ์นธ๋ฐ ๋ณด๋
- ์ด์ ์งํ ์ํฉ์ ํ ๋์ ๋ณผ ์ ์๋๋ก ์นธ๋ฐ ๋ณด๋ ํํ๋ก ์๊ฐํ
- ๋ ธ์ ๊ธฐ๋ฅ๋ณ ์์ ์ํฉ ๊ด๋ฆฌ ํ์ด์ง๋ฅผ ์์ฑํด ์์ ์งํ ์ํฉ ๊ด๋ฆฌ
- ์ ์ฒด์ ์ธ ์์ ์งํ ์ํฉ์ ํ ๋์ ๋ณผ ์ ์๋๋ก ๊ธฐ๋ฅ๋ณ ์งํ ์ํฉ ํ ์ ์
- ์งํ ์ , ์งํ ์ค, ์งํ ์๋ฃ, ์์ ์ค ํ๊ทธ๋ฅผ ์ด์ฉํด ์งํ ์ํฉ ์ฒดํฌ
- ๋ ธ์ ๋ฒ๊ทธ ๋ฆฌํฌํธ ํ์ด์ง๋ฅผ ์์ฑํด ๋ฐ๊ฒฌ๋ ๋ฒ๊ทธ ๊ด๋ฆฌ
- ๋ฒ๊ทธ ๋ฐ๊ฒฌ์๊ฐ ๋ฒ๊ทธ ์ ํ, ๋ฒ๊ทธ, ๋ฒ๊ทธ ์์ฑ์, ๊ฐ๋ฐ ๋ด๋น์ ๋ฑ๋ก
- ๋ฒ๊ทธ ์์ฑ์๊ฐ ๊ฐ๋ฐ ๋ด๋น์์ธ ๊ฒฝ์ฐ : ๋ณธ์ธ ๋ฑ๋ก
- ๋ฒ๊ทธ ์์ฑ์๊ฐ ๊ฐ๋ฐ ๋ด๋น์๊ฐ ์๋ ๊ฒฝ์ฐ : ํ์ ๋ฉ์ ์ ๋ก ๋ด์ฉ ๊ณต์ ํ ๊ฐ๋ฐ ๋ด๋น์ ์ค์
- ๊ฐ๋ฐ ๋ด๋น์๋ ๋ฒ๊ทธ ํ์ธ ํ ํ์ธ ๊ฒฐ๊ณผ ๋ฑ๋ก
- ๊ฑฐ์ : ๋ฒ๊ทธ๊ฐ ์๋ ๊ฒฝ์ฐ
- ์น์ธ : ๋ฒ๊ทธ
- ์น์ธ๋ ๋ฒ๊ทธ๋ ๊ฐ๋ฐ ๋ด๋น์๊ฐ ์ด์ ๋ฑ๋ก ํ ๋ฒ๊ทธ ์์ ์งํ
- ์ปค๋ฐ ๋ฉ์์ง ์ปจ๋ฒค์ , ์ฝ๋ ์ปจ๋ฒค์ , ๋ค์ด๋ฐ ์ปจ๋ฒค์ , ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ์ปจ๋ฒค์ ์ค๋ฆฝ
- GitHub Wiki์ ์ปจ๋ฒค์ ๊ธฐ๋ก
- ์ด์ ๊ด๋ฆฌ
- ์ด์ ์งํ ์ํฉ ๊ด๋ฆฌ
- ์์ ์งํ ์ํฉ ๊ด๋ฆฌ
- ๋ฒ๊ทธ ๊ด๋ฆฌ
- README ์์ฑ
- ๊ธฐ๋ก์ ์ํย ํ์๋กย ์์ฑ ๋ด๋น
- ํ ๋ด๋น๊ฒ์ด์ 5์ข , ํญ ๋ฉ๋ด, ๋๊ธ ์ปดํฌ๋ํธ ์ ์
- Splash, ํผ๋, ํ๋กํ, 404 ํ์ด์ง ํผ๋ธ๋ฆฌ์ฑ
- ํ ํผ๋ ํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ
- ํผ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ๊ทธ๋ ค์ฃผ๋ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ๋ก๊ทธ์ธ ์ฌ๋ถ์ ๋ฐ๋ฅธ ํ์ด์ง ๋ก๋ ๊ธฐ๋ฅ ๊ตฌํ
- ๋ก๊ทธ์์ ์ํ์ผ๋๋ ๋ก๊ทธ์ธ ํ์ด์ง / ๋ก๊ทธ์ธ ์ํ์ผ๋๋ ํ๋ก์ ํผ๋ ๋ฐ์ดํฐ ๋ฐ์์ค๋๋ก
- ํผ๋ ๋ฐ์ดํฐ ์ ํ ์์ด ๋ฐ์์ฌ ์ ์๋๋ก ํ์ด์ง ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ํ๋กํ ํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ (์ฌ์ฉ์ ์ ๋ณด, ๋ฑ๋ก๋ ์ํ, ์์ฑ๊ธ ์กฐํ ๊ธฐ๋ฅ)
- ์ ์ ์ ๋ณด ๋ฐ ์ํ, ํฌ์คํธ ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ๊ทธ๋ ค์ฃผ๋ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ํฌ์คํธ ๋ชฉ๋ก ๋ ์ด์์ ๋ฆฌ์คํธ ํ์ / ์จ๋ฒ ํ์์ผ๋ก ์ ํํด์ ๋ณผ ์ ์๋๋ก ๊ธฐ๋ฅ ์ถ๊ฐ
- ์นด์นด์คํก ๊ณต์ ํ๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐํ์ฌ ๊ณต์ ๊ธฐ๋ฅ ๊ตฌํ
- ์ํ ๋ฆฌ์คํธ๋ฅผ ๊น๋ํ๊ฒ ๋ณผ ์ ์๋๋ก Swiper ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํ์ฌ ์บ๋ฌ์ ๋ก ๊ตฌํ
- ๊ฒ์๋ฌผ ๋ชฉ๋ก์ ์ ํ ์์ด ๋ณผ ์ ์๋๋ก ํ์ด์ง ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ๊ฒ์๊ธ ๋ชจ๋ฌ ๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ
- ๊ฒ์๊ธ ์ญ์ / ์ ๊ณ ๊ธฐ๋ฅ
- ๋์ ๊ฒ์๋ฌผ์ผ๋๋ ์์ ๋ฐ ์ญ์ ๊ธฐ๋ฅ / ๋ค๋ฅธ ์ฌ์ฉ์์ ๊ฒ์๋ฌผ์ผ๋๋ ์ ๊ณ ๊ธฐ๋ฅ ๋ณด์ด๋๋ก ๊ตฌํ
- ๊ฒ์๊ธ ์ญ์ / ์ ๊ณ ๊ธฐ๋ฅ
- ๊ฒ์๋ฌผ ์ข์์ ๊ธฐ๋ฅ ๊ตฌํ
- ๊ฒ์๊ธ, ์ํ ์ปดํฌ๋ํธ ์ ์
- ๋ก๊ทธ์ธ ๋ฉ์ธ, ํ๋กํ ์ค์ , ํ๋กํ ์์ , ์ํ ๋ฑ๋ก, ์ํ ์์ ํ์ด์ง ํผ๋ธ๋ฆฌ์ฑ
- ํ์๊ฐ์
๊ธฐ๋ฅ ๊ตฌํ
- ํ๋กํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ธฐ๋ฅ
- ์ด๋ฏธ์ง๋ฆฌ์ฌ์ด์ง์ ํตํ 10MB ์ด์์ ํ๋กํ ์ด๋ฏธ์ง๋ ์ ๋ก๋ ๊ฐ๋ฅ / ์ฑ๋ฅ ํฅ์
- ํ์๊ฐ์
์ ๋ณด ์ ํจ์ฑ ๊ฒ์ฌ
- API ๋ช ์ธ์ ๋ฐ๋ฅธ ๊ณ์ ID ์ค๋ณต ๊ฒ์ฌ
- ์ ๊ทํํ์์ ํตํ ๊ณ์ ID ์ ํจ์ฑ ๊ฒ์ฌ
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํตํ ์์ํ๊ธฐ ๋ฒํผ ํ์ฑํ
- ๋ชจ๋ ์ ํจ์ฑ ๊ฒ์ฌ ํต๊ณผ ํ, Enter ์ ๋ ฅ ์ ์์ํ๊ธฐ ๋ฒํผ ํด๋ฆญ๊ณผ ๋์ผํ ๊ธฐ๋ฅ
- API ๋ช ์ธ์ ๋ฐ๋ผ ํ๋กํ ์ค์ ์ด์ ์ ๋ฐ์์จ email, password์ ํจ๊ป ์๋ฒ์ ๋ฐ์ดํฐ ์ ์ก
- ํ๋กํ ์์ ๊ธฐ๋ฅ ๊ตฌํ
- ๊ธฐ์กด ํ๋กํ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ (ํ๋กํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํฌํจ)
- ์ ํจ์ฑ ๊ฒ์ฌ
- API ๋ช ์ธ์ ๋ฐ๋ฅธ ๊ณ์ ID ์ค๋ณต ๊ฒ์ฌ (๊ธฐ์กด ID ์ ์ง ์ ์ค๋ณต ๊ฒ์ฌ X)
- ์ ๊ทํํ์์ ํตํ ๊ณ์ ID ์ ํจ์ฑ ๊ฒ์ฌ
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํตํ ์ ์ฅ ๋ฒํผ ํ์ฑํ
- ํ์๊ฐ์ ์ ํ๋กํ ์ธํ ๊ณผ ๋์ผํ ๊ธฐ๋ฅ ํฌํจ
- ์ํ ๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ
- ์ํ ๋ฑ๋ก
- ์ด๋ฏธ์ง๋ฆฌ์ฌ์ด์ง์ ํตํ 10MB ์ด์์ ์ํ ์ด๋ฏธ์ง๋ ์ ๋ก๋ ๊ฐ๋ฅ / ์ฑ๋ฅ ํฅ์
- ๊ฐ ์ํ ์ ๋ณด์ ์ ํจ์ฑ ๊ฒ์ฌ
- ์ํ ๊ฐ๊ฒฉ์ ์ฒ ๋จ์ ์ฝค๋ง ์๋ ์์ฑ & ์ญ์
- ์ญ์
- ์ํ ์ญ์ ํด๋ฆญ ์ ์ํ์ด ์ญ์ ๋๊ณ , ํ๋งค ์ค์ธ ์ํ๋ง ๋ฆฌ๋ ๋๋ง
- ์์
- ๊ธฐ์กด ์ํ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ (์ํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํฌํจ)
- ์ํ ๋ฑ๋ก๊ณผ ๋์ผํ ๊ธฐ๋ฅ ํฌํจ
- ์น์ฌ์ดํธ์์ ์ํ ๋ณด๊ธฐ
- ๋ด ์ํ์ ๋ํด ์น์ฌ์ดํธ์์ ์ํ ๋ณด๊ธฐ ํด๋ฆญ ์, ํด๋น ํ์ด์ง๊ฐ ์ ์ฐฝ์ผ๋ก ์ด๋ฆผ
- ์ํ ๋ฑ๋ก
- ์ปค๋ฎค๋์ผ์ด์
- ํ ๋ชฉํ๋ฅผ ์ธ์ฐ๊ณ ์๋์ง๋ฅผ ๊ฐํํ๊ธฐ ์ํด ๋ค์ด๋ฒํผ ์ค๋ฌธ์ง ์์ฑ
- ์์ ๋ก์ด ํ์๋ฅผ ์ํด Figzam ํ์ ๊ณต๊ฐ ์ ์
- ํ๋ก์ ํธ ๊ด๋ฆฌ
- ์๊ตฌ์ฌํญ ์ ๋ฆฌ ๋ฌธ์ ์์ฑ
- ๊ธฐ๋ฅ ๋ฆฌ์คํธ ๋ฐ ๊ธฐ๋ฅ๋ณ ์์ ์ํฉ ๊ด๋ฆฌ ๋ฌธ์ ์์ฑ
- ๋ฒ๊ทธ ๋ฆฌํฌํธ ์์ฑ ๋ฐ ๋ฒ๊ทธ ๊ด๋ฆฌ ํ๋ก์ธ์ค ์ค๋ฆฝ
- Netlify๋ฅผ ์ด์ฉํด ํ๋ก์ ํธ ๋ฐฐํฌ
- ์์ํ ํ๋ก์ ํธ ์งํ์ ์ํ ์์
- ์ด์ ๊ด๋ฆฌ ํ๋ก์ธ์ค ๋์
- GitHub ์ด์ ํ ํ๋ฆฟ, PR ํ ํ๋ฆฟ ๋ฑ๋ก
- ์๋ชจ์ ์ธ ์ปค๋ฎค๋์ผ์ด์ ์ ์ค์ด๊ธฐ ์ํด ํ์ ๋ฉ์ ์ (Discord)์ GitHub ์๋ฆผ ์ฐ๋
- ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ
์์
- ํด๋ ํธ๋ฆฌ ๊ตฌ์ฑ ๋ฐ ๊ธฐ๋ณธ ํ์ผ ํฌํจ
- ํ ์ปจ๋ฒค์ ์ ๋ง์ถฐ ESLint & Prettier ์ ์ฉ
- ์์ํ ํผ๋ธ๋ฆฌ์ฑ ์์ ์ ์ํด ๋ฉ์ธ ๋ ์ด์์ ์ ์ฉ
- ๋ผ์ฐํฐ ์ค๊ณ ๋ฐ ๊ตฌ์ถ
- ์ง์ ๊ณต์
- ์ฐ๋ฆฌ ํ์ Git Branch ์ ๋ต์ ๋ง๋ ํ์ ์๋๋ฆฌ์ค ์ค๋ฆฝ ํ ํ ๋ด ์ ํ
- Postman์ ์ด์ฉํ API ํ ์คํธ ๋ฐฉ๋ฒ ๋ฐ Axios๋ฅผ ์ด์ฉํ ์๋ฒ ํต์ ๋ฐฉ๋ฒ ํ ๋ด ์ ํ
- ๊ฐ์ ธ๋๋๋ฅ ๋ก๊ณ ,ย ๋ง์ค์ฝํธ ์บ๋ฆญํฐย ๋ฑ ๋์์ธ ์์ ์ ์
- ์ง์ฌ์ํ ๋ฉ์ธ, ์ฐ์ฑ ๋์ด๋, ๋๋ฌผ๋ณ์ ํ์ด์ง Figma ์์ ์ ์
- ์ ๋๋ฉ์ด์ , ๋ก๋ฉ, ๊ฒ์, ํ๋ก์ฐ, ๋ชจ๋ฌ, ๋ฉ์ธ ๋ ์ด์์ ์ปดํฌ๋ํธ ์ ์
- ์ด๋ฉ์ผ ๋ก๊ทธ์ธ, ํ๋ก์ฐ ํ์ด์ง, ์ง์ฌ์ํ ๋ฉ์ธ, ์ฐ์ฑ ๋์ด๋, ๋๋ฌผ๋ณ์ ํ์ด์ง ํผ๋ธ๋ฆฌ์ฑ
- ์ฌ๋ฌ ํ์ด์ง์์ ๋ฐ๋ณต ์ฌ์ฉ ๋๋ ๋ ์ด์์์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ปจํ ์ธ ๋ ์ด์์ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌ
- ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช
์ธ์ ๋ฐ๋ฆ)
- ์ด๋ฉ์ผ ๋ฐ ๋น๋ฐ๋ฒํธ ์ ํจ์ฑ ๊ฒ์ฆ
- ์ ํจ์ฑ ๊ฒ์ฆ์ ํต๊ณผํด์ผ ๋ก๊ทธ์ธ ๋ฒํผ ํ์ฑํ
- ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ํ ํฐ ์ ์ฅ
- ๋ก๊ทธ์์ ๊ธฐ๋ฅ ๊ตฌํ
- ๋ก์ปฌ ์คํ ๋ฆฌ์ง์ ์ ์ฅ๋ ํ ํฐ ์ญ์
- ํ๋ก์ฐ ๊ธฐ๋ฅ ๊ตฌํ
- ํ๋ก์ / ํ๋ก์ ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ํ์ํ๋ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ํ๋ก์ฐ / ์ธํ๋ก์ฐ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ์ง์ฌ์ํ ๋ฉ์ธ ํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ
- Swiper ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ ํ์ด์ง๋ค์ด์ ์บ๋ฌ์ ๊ตฌํ
- ์ถ์ฒ๊ธ ๋ชฉ๋ก
- ์ถ์ฒ๊ธ API๊ฐ ๋ฐ๋ก ์ฃผ์ด์ง์ง ์์์ ๋ฐ๋ผ ๊ฒ์๊ธ๋ค์ด ์ถ์ฒ๊ธ ๋ชฉ๋ก์ฒ๋ผ ๋ณด์ด๋๋ก ์๋ต ๋ฐ์ดํฐ ๊ฐ๊ณต
- ์ฌ์ฉ์๊ฐ ๋ง์ ์์ ๊ฒ์๊ธ์ ํธ๋ฆฌํ๊ฒ ๋ณผ ์ ์๋๋ก ๋ฌดํ์คํฌ๋กค ์ ์ฉ
- ์ฐ์ฑ
๋์ด๋ ํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ
- ํ์ ๊ตฌ, ๋ ์จ, ๋ฏธ์ธ๋จผ์ง ์กฐํ ๊ธฐ๋ฅ
- Geolocation API๋ฅผ ์ด์ฉํด ์ฌ์ฉ์์ ํ์ฌ ์ขํ๋ฅผ ๊ฐ์ ธ์ด
- Kakao API๋ฅผ ์ด์ฉํด ํ์ ๊ตฌ์ญ ์ ๋ณด๋ฅผ ๋ฐ์์ด
- OpenWeatherMap API๋ฅผ ์ด์ฉํด ๋ ์จ, ๋ฏธ์ธ๋จผ์ง ์ ๋ณด๋ฅผ ๋ฐ์์ด
- ์ฐ์ฑ ๋์ด๋ ์ฑ ์
- ํ์ ๊ตฌ, ๋ ์จ, ๋ฏธ์ธ๋จผ์ง ์กฐํ ๊ธฐ๋ฅ
- ๋๋ฌผ๋ณ์ ํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ
- Kakao API๋ฅผ ์ด์ฉํด ๊ทผ์ฒ ๋๋ฌผ๋ณ์ ์ ๋ณด๋ฅผ ๊ฐ๊น์ด ์์ผ๋ก ๋ฐ์์ด
- ์ฌ์ฉ์๊ฐ ์ํ๋ ์ ๋ณด๋ง ์ ๋ณ์ ์ผ๋ก ๋ณผ ์ ์๋๋ก ๋๋ณด๊ธฐ ๋ฒํผ ์ ์ฉ
- ์ฌ์ฉ์ ์ธ์ฆ ์ฌ๋ถ์ ๋ฐ๋ฅธ ๋ผ์ฐํฐ ์ ๊ทผ์ ํ ๊ธฐ๋ฅ ๊ตฌํ
- ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ฝ๋๋ Custom Hook์ผ๋ก ๋ถ๋ฆฌ
- ์ ์ญ์์ ํ์ํ ๋ฐ์ดํฐ๋ Context ๊ฐ์ฒด๋ก ๊ด๋ฆฌ
- ๋ฒํผ ์ปดํฌ๋ํธ(S, MS, M, L ์ฌ์ด์ฆ) ์ ์
- ํ์๊ฐ์ , ๊ฒ์, ๊ฒ์๊ธ ์์ธ, ๊ฒ์๊ธ ๋ฑ๋ก, ๊ฒ์๊ธ ์์ , ์ฑํ ๋ฐฉ ๋ชฉ๋ก, ์ฑํ ํ์ด์ง ํผ๋ธ๋ฆฌ์ฑ
- ์์ด์ฝ, ์ด๋ฏธ์ง๋ฅผ ์ ์ญ์์ ์ฌ์ฉํ ์ ์๋๋ก ๊ณตํต ํ์ผ ์ ์
- ํ์๊ฐ์
๊ธฐ๋ฅ ๊ตฌํ
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋กํ ์ค์ ํ์ด์ง์ props๋ก ๋ด๋ ค์ค
- ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํต๊ณผํ ์ด๋ฉ์ผ๊ณผ ๋น๋ฐ๋ฒํธ๊ฐ ์กด์ฌํ๋ฉด, ๋ฒํผ์ด ํ์ฑํ ๋๋๋ก ๊ตฌํ
- ์ ์ ๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ
- ์ฌ์ฉ์ ์ ๋ ฅ๊ฐ์ด ๋ฐ๋ ๋ ๋ง๋ค ์ ๋ ฅ๊ฐ๊ณผ ์ผ์นํ๋ ์ ์ ๊ฒ์ ๊ฒฐ๊ณผ ๊ตฌํ
- ๊ฒ์๋ ์ฌ์ฉ์ ํด๋ฆญ ์ ํด๋น ์ฌ์ฉ์์ ํ๋กํ๋ก ์ด๋ํ๋ ๋งํฌ ๊ตฌํ
- ์ฌ์ฉ์ ID๊ฐ ์๋, ์ฌ์ฉ์ ์ด๋ฆ ๊ณผ
input
์ฐฝ์ ์ ๋ ฅํ ํค์๋๊ฐ ๋์ผํ ๋ถ๋ถ์ด ์๋ ๊ฒฝ์ฐ์๋ ์ ๋ ฅํ ํค์๋ ๋ถ๋ถ๋ง ๋ค๋ฅธ ์คํ์ผ ์ ์ฉํ์ฌ ๊ฐ์กฐ
- ๊ฒ์๊ธ ๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ ( ์กฐํ, ๋ฑ๋ก, ์์ , ์ญ์ , ์ ๊ณ )
- ์ฌ์ฉ์ ์ ๋ ฅ ํ ์คํธ์ ์ด๋ฏธ์ง ํ์ผ ๊ฒ์๋ฌผ ์ ๋ก๋ ๊ธฐ๋ฅ ๊ตฌํ
- ํ ์คํธ์ ์ด๋ฏธ์ง๊ฐ ์์๊ฒฝ์ฐ, ๋ฒํผ ๋นํ์ฑํ
- ์ด๋ฏธ์ง ํ์ผ 3๊ฐ ์ด๊ณผ ์ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋ alert ๊ตฌํ
- ํฌ์คํธ ํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์์ ์ญ์ ๊ธฐ๋ฅ ๊ตฌํ
- ํฌ์คํธ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ๊ทธ๋ ค์ฃผ๋ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ๊ฒ์๋ฌผ ์์ ๊ธฐ๋ฅ์ ๊ตฌํ
- ๋๊ธ ๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ ( ์กฐํ, ๋ฑ๋ก, ์ญ์ , ์ ๊ณ )
- ๋๊ธ ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ๋ฉด์ ๊ทธ๋ ค์ฃผ๋ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ๋๊ธ ์ ๋ก๋,์ญ์ ๋ฐ ์ ๊ณ ๊ธฐ๋ฅ ๊ตฌํ (API ๋ช ์ธ์ ๋ฐ๋ฆ)
- ์ฑํ
๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ
-
์ฑํ ๋ชฉ๋ก ์กฐํ ๊ธฐ๋ฅ
์ฑํ ๊ธฐ๋ฅ
- ์ฑํ ๊ธฐ๋ฅ ๋ฐฉ์
๋ก๊ทธ์ธ๋ ์ฌ์ฉ์
โย์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ๊ฒ์๊ธ
โย์ฑํ ํ ์๋ ์ฌ์ฉ์
โ ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ์์ด๋๋ ์ ์ ๊ฒ์์ ํตํ์ฌ ์ฐพ์ ์ ์๋๋ก ์ค์
< ์ฑํ ๋ฐฉ ์์ฑ >
- ์ฑํ ํ ์๋ ์ฌ์ฉ์์ ํ๋กํ์์ ์ฑํ ์ด๋ฏธ์ง ๋ฒํผ์ ํด๋ฆญํ๋ฉด, ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ๊ฒ์๊ธ์ด ์์ฑ๋๋ค.
// ์ 3์์ ๊ฒ์๊ธ์ ๋ค๋ฅธ ์ ์ ๊ฐ ์์ฑํ ์ ์๋๋ก ํ ํฐ๊ฐ์ ์ง์ const CHAT_TOKEN = process.env.REACT_APP_CHAT_SERVER_TOKEN;
- ์ 3์์ ๊ฒ์๊ธ์ ์ ์ก๋๋ ์ปจํ
์ธ ์ธ ์ฑํ
๋ฐ์ดํฐ โ
โ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ accountname,์ฑํ ํ ์๋ ์ฌ์ฉ์์ accountnameโ
const createChatroom = () => { axios .post( `https://mandarin.api.weniv.co.kr/post`, { post: { content: `${userAccountname},${profileUserAccountname}`, image: '', }, }, { headers: { Authorization: `Bearer ${CHAT_TOKEN}`, 'Content-type': 'application/json', }, }, ) .then((res) => { navigate(`/chat/${res.data.post.id}`); }); };
โ ์ ์ก๋ ๋ฐ์ดํฐ๋ ์ฑํ ๋ฆฌ์คํธ๋ฅผ ๋ถ๋ฌ์ฌ๋์ ์ฑํ ๋ฐฉ ์ด๋ฆ์ ๋ํ๋ผ๋ ์ฌ์ฉํ๋ค.
- ์ฑํ ๋ฐฉ ์์ฑ์, ์ 3์์ ๊ฒ์๊ธ์ content ๋ด์ฉ๊ณผ ์์ฑํ content ๋ด์ฉ์ด ์ค๋ณต๋๋ค๋ฉด alert ์ฐฝ์ ๋์์ ์ด๋ฏธ ์กด์ฌํ๋ ์ฑํ ๋ฃธ์ด๋ผ๋ ์ฌ์ค์ ์ฌ์ฉ์์๊ฒ ์๋ฆฐ๋ค.
< ์ฑํ ๋ฐฉ ๋ฆฌ์คํธ >
- ์ 3์์ ๊ฒ์๊ธ์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์, ์ ์ก๋ ์ปจํ
์ธ ๋ฐ์ดํฐ ์ ์ฌ์ฉ์์
accountname
์ด ํฌํจ๋ ๊ฒ์๊ธ๋ง ๋ณด์ฌ์ค๋ค.
< ์ฑํ ๋ฐฉ >
- useParams() ๋ฅผ ์ฌ์ฉํ์ฌ, ์ ํํ url์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ธ์จ ํ, ๊ทธ ํ๋ผ๋ฏธํฐ(postid) ์ ํด๋นํ๋ ๊ฒ์๊ธ์ ๋ถ๋ฌ์จ๋ค. ์ฌ๊ธฐ์ ์์ฑ๋ ๋๊ธ์ด ๋ณธ์ธ์ ๊ฒ์ด๋ผ๋ฉด, ๋ณธ์ธ์ด ๋ ๋ฆฐ ์ฑํ ์ผ๋ก ๋ณด์ฌ์ง๊ณ , ์๋๋ผ๋ฉด ํ ์ฌ์ฉ์๊ฐ ๋ณด๋ธ ์ฑํ ์ฒ๋ผ ๋ณด์ฌ์ง๋ค.
< ์ฑํ >
- ์ฑํ ๋ฐฉ์ ์ ์ฅํ๋ฉด, ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ๊ฒ์๋ฌผ์ ๋๊ธ ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ค.
-
์์ ํ๋ฉด | ํ์๊ฐ์ ํ์ด์ง | ๋ก๊ทธ์ธ ํ์ด์ง |
---|---|---|
ํผ๋ ํ์ด์ง | ๊ฒ์ ํ์ด์ง | 404 ํ์ด์ง |
---|---|---|
์ฑํ ๋ชฉ๋ก ํ์ด์ง | ์ฑํ ๋ฐฉ ํ์ด์ง | ์ฑํ ๋ฐฉ ๋๊ฐ๊ธฐ |
---|---|---|
๊ฒ์๊ธ ์์ธ ํ์ด์ง | ๊ฒ์๊ธ ์์ฑ ํ์ด์ง | ๊ฒ์๊ธ ์์ ํ์ด์ง |
---|---|---|
๊ฒ์๊ธ ์ญ์ | ๊ฒ์๊ธ ์ ๊ณ | ๋๊ธ ๊ธฐ๋ฅ |
---|---|---|
๋ง์ด ํ๋กํ ํ์ด์ง | ์ ์ด ํ๋กํ ํ์ด์ง | ๋ฆฌ์คํธํ/์จ๋ฒํ ๋ณด๊ธฐ |
---|---|---|
ํ๋กํ ์์ ํ์ด์ง | ํ๋ก์/ํ๋ก์ ํ์ด์ง | ๋ก๊ทธ์์ ๊ธฐ๋ฅ |
---|---|---|
์ํ ๋ฑ๋ก ํ์ด์ง & ์ํ ๋งํฌ ์ด๋ | ์ํ ์์ ํ์ด์ง | ์ํ ์ญ์ ํ์ด์ง |
---|---|---|
์ง์ฌ์ํ ๋ฉ์ธ ํ์ด์ง | ์ฐ์ฑ ๋์ด๋ ํ์ด์ง | ๋๋ฌผ๋ณ์ ํ์ด์ง |
---|---|---|
์ฌ์ฉ์์ ํ๋กํ ํ์ด์ง
useEffect(() => {
if (location.pathname === `/profile/${userAccountname}`) {
navigate('/profile');
}
}, [location, userAccountname, navigate]);
- ํ์ฌ url์ด
/profile/userAccountname
์ผ ๊ฒฝ์ฐ ๋์ ํ๋กํ ํ์ด์ง์ด๋ฏ๋ก url ์ฃผ์๋ฅผ/profile
๋ก ๋ณ๊ฒฝํ๋๋ก ํจ
useEffect(() => {
const getUserProfileInfo = () => {
axios({
url: url + `/profile/${accountname ? accountname : userAccountname}`,
method: 'GET',
headers: {
Authorization: `Bearer ${userToken}`,
'Content-type': 'application/json',
},
})
.then((res) => {
setUserProfileInfo(res.data.profile);
})
.catch((err) => {
console.error(err);
});
};
getUserProfileInfo();
}, [url, accountname, userAccountname, userToken]);
์ ์ ํ๋กํ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ getUserProfileInfo() ํจ์
-
accountname(ํ์ฌ ํ๋กํ url์ ํ์๋ ์์ด๋)์ userAccountname(ํ์ฌ ๋ก๊ทธ์ธ๋์ด์๋ ์ฌ์ฉ์์ ์์ด๋) ๊ฐ์ ๊ฒฝ์ฐ ๋ด ํ๋กํ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ํ์ฌ URL์ ์ฌ์ฉ์ ์์ด๋์์ ๊ทธ ์ฌ์ฉ์์ ํ๋กํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ฒ ํจ.
ํ ํผ๋ ํ์ด์ง, ํ๋กํ ํฌ์คํธ ์์ญ ๋ฌดํ์คํฌ๋กค ๊ธฐ๋ฅ
// ๋ฌดํ ์คํฌ๋กค ๊ตฌํ์ ํ์ํ useState
const [numFeed, setNumFeed] = useState(0);
const [loading, setLoading] = useState(false);
const [done, setDone] = useState(false);
const [ref, inView] = useInView();
// ์๋ฒ์์ ํผ๋๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์
const getUserFeed = useCallback(async () => {
const option = {
url: url + `/post/feed/?limit=10&skip=${numFeed}`,
...
},
};
setLoading(true);
await axios(option)
.then((res) => {
// ๊ธฐ์กด์ ๋ฐ์ดํฐ์ ์๋ก์ด ๋ฐ์ดํฐ ๋ฐฐ์ด ํฉ์น๊ธฐ
setIsFollowingPost(isFollowingPost.concat(res.data.posts));
setLoading(false);
setIsLoading(false);
})
.catch((err) => {
setIsLoading(false);
console.error(err);
});
}, [numFeed]);
useEffect(() => {
// ์ฌ์ฉ์๊ฐ ๋ง์ง๋ง ์์๋ฅผ ๋ณด๊ณ ์๊ณ (inview === true), ๋ก๋ฉ์ค์ด ์๋๋ผ๋ฉด
if (inView && !loading) {
setNumFeed((current) => current + 10);
}
}, [inView, loading]);
return (
<div>
{isFollowingPost.map((post, i) =>
// isFollowingPost์ ๋ง์ง๋ง ์์๋ผ๋ฉด ref์ถ๊ฐ
isFollowingPost.length - 1 === i ? (
<div key={post.id} ref={ref} />
) : (
<div key={post.id}>
<Post post={post} />
</div>
),
)}
</div>
)
- ๊ฒ์๋ฌผ์ ๊ฐ์ ์ ํ์์ด ๋ณด๊ธฐ ์ํด
'react-intersection-observer'
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํ์ฌ ๋ฌดํ์คํฌ๋กค ๊ตฌํํจ. - ์๋ฒ์์ ํผ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ํจ์๋ฅผ
axios
๋ก ๋ถ๋ฌ์ด. ๋ฐ์ดํฐ๋ฅผ ๋์ ํด์ ์์ฒญํ๋ ๋ถํ์ํ ๊ณผ์ ์ ๋ง๊ธฐ ์ํดskip
์กฐ๊ฑด ์ถ๊ฐํ ํconcat
๋ฉ์๋๋ก ๋ฐฐ์ด ํฉ์ณ์ ๋ธ๋ผ์ฐ์ ์ ๋ณด์ฌ์ค. - ๋ถ๋ฌ์จ ํฌ์คํธ๋ค์ ๋ง์ง๋ง ์์๋ผ๋ฉด, ๋ธ๋ผ์ฐ์ ํ๋จ๋ถ๋ถ์ ์๋ฏธํ๋
ref
๊ฐ ์ถ๊ฐํจ. ํด๋น ์์๊ฐ ๋ณด์ด๋ฉดinView
๊ฐ์ด ยtrue
ย ๋ก, ์ ๋ณด์ด๋ฉดยfalse
๋ก ์๋์ผ๋ก ๋ณ๊ฒฝ๋จ.inView
๊ฐtrue
์ผ ๊ฒฝ์ฐ ์๋ฒ์ ๊ฒ์๋ฌผ ์ถ๊ฐ๋ก 10๊ฐ์ฉ ์์ฒญํจ.
axios(option).then
...
if (res.data.posts.length < 10) {
setDone(true);
}
useEffect(() => {
// ์๋ก ๋ฐ์์จ ๋ฐ์ดํฐ ๋ฐฐ์ด ๊ฐ์๊ฐ 10๊ฐ ๋ฏธ๋ง์ผ๋ ์คํฌ๋กค ๋ฉ์ถ๊ธฐ
if (!done) {
getUserFeed();
}
}, [numFeed]);
-
๋ถ๋ฌ์ค๋ ๋ฐ์ดํฐ์ ๊ฐ์ฅ ๋ง์ง๋ง ์์๋ฅผ ๋ณด๊ณ ์์ผ๋ฉด ๊ณ์ ์์ฒญ๋๋ ํ์์ ๋ง๊ธฐ ์ํด, ์๋ก ๋ฐ์์ค๋ ๋ฐ์ดํฐ ๊ฐ์๊ฐ 10๊ฐ ๋ฏธ๋ง์ผ๋
done
state๋ฅผtrue
๋ก ๋ณ๊ฒฝ.done
์ดtrue
์ผ ๊ฒฝ์ฐ ๊ณ์ํด์ ์์ฒญ์ ๋ฐ์์ค๊ณ ,false
์ผ๊ฒฝ์ฐ ์์ฒญ์ ๋ฉ์ถ๊ฒ ํจ.
๊ฒ์๋ฌผ ์ด๋ฏธ์ง / ์ํ ๋ชฉ๋ก ์บ๋ฌ์ ๊ธฐ๋ฅ
{imageFile[0] ? (
<SwiperWrapper>
<Swiper
style={swiperStyle}
spaceBetween={30}
pagination={{
clickable: true,
}}
modules={[Pagination]}
className='mySwiper'
>
{imageFile ? (
imageFile.map((img, i) => (
<SwiperSlide key={i}>
<ContentImg src={img} alt='' />
</SwiperSlide>
))
) : (
<></>
)}
</Swiper>
</SwiperWrapper>
- ๊ฒ์๋ฌผ์ ์ ๋ก๋๋ ์ด๋ฏธ์ง๋ค์ Swiper ์บ๋ฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํ์ฌ ๊ตฌํํจ. ์ผํญ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ์ง ํ์ผ์ด ์์ ๊ฒฝ์ฐ Swiper๋ก ์ด๋ฏธ์ง๋ค์ ํ์ํด์ฃผ๊ณ ์์.
{itemList.map((item) => (
<SwiperSlide key={item.id}>
<Product
productid={item.id}
productImg={item.itemImage}
productName={item.itemName}
productPrice={`${item.price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} ๋ฅ`}
/>
</SwiperSlide>
- ์ํ ๋ชฉ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์์ ์บ๋ฌ์ ๋ก ๋๊ฒจ ํ์ธํ ์ ์๊ฒ ๊ตฌํํจ.
ํ์๊ฐ์ ๊ธฐ๋ฅ (ํ๋กํ ์ค์ )
- ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง์ ์ํ browser-image-compression ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
-
10MB ์ด์์ ์ด๋ฏธ์ง๋ ์ ๋ก๋ ๊ฐ๋ฅ
-
์ด๋ฏธ์ง ๋ก๋ฉ ์๋ ๊ฐ์ ๋ฑ ์ฑ๋ฅ ํฅ์
// ์๋์ ๊ฐ์ด ์ต์ ๊ฐ์ ๋ถ์ฌํ ์ ์์ { maxSizeMB: 0.08, // ํ์ผ ์ต๋ ํฌ๊ธฐ maxWidthOrHeight: 320, // ๋๋น or ๋์ด ์ต๋๊ฐ }
-
๊ธฐ์กด์๋ ์๋์ ๊ฐ์ด 102400B (100KB)๋ก ์ ํ๋ ํฌ๊ธฐ๋ณด๋ค ์์ฒญ์ด ํฌ๋ค๋ ๋ฉ์์ง๋ฅผ ์๋ต๋ฐ์, ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ๊ธฐ์กด ์ด๋ฏธ์ง ํฌ๊ธฐ์ 1% ์์ค์ผ๋ก ํฌ๊ธฐ๋ฅผ ์ค์ฌ ํด๊ฒฐํ์์
-
๋ฆฌ์ฌ์ด์ง ์ / ํ ์ด๋ฏธ์ง ํ์ผ ํฌ๊ธฐ
-
- Blob to Base64
-
์๋์ ๊ฐ์ด ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง ๊ณผ์ ์์ ๋ง๋ค์ด์ง Blob์ Web API๋ฅผ ํตํด Base64 ํํ๋ก ๋ณํํ์ฌ ์ด๋์์๋ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฌธ์์ด ํํ๋ก ๋ง๋ค์ด ์ค
// Blob to Base64 const readerBlob = new FileReader(); readerBlob.readAsDataURL(compressedFile); readerBlob.onloadend = () => { if (imageFunction) { imageFunction(readerBlob.result); // ์๋ฒ๋ก ์ ์ก๋ ์ด๋ฏธ์ง } else { setThumbnailImg(readerBlob.result); // ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ } };
- ํ์ง๋ง Base64 ํน์ฑ์ ๋ฌธ์์ด์ด ๊ธธ์ด์ ธ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๋ฉฐ, ์ฉ๋ ์ด์๊ฐ ์๊ธธ ์ ์์
-
-
๊ธฐ๋ณธ ํ๋กํ ์ด๋ฏธ์ง
- ์ฌ์ฉ์๊ฐ ํ๋กํ ์ด๋ฏธ์ง๋ฅผ ๋ฐ๋ก ์ค์ ํ์ง ์์ผ๋ฉด, ๋ฏธ๋ฆฌ Base64๋ก ์ธ์ฝ๋ฉ ํด๋์ ์ด๋ฏธ์ง๊ฐ ์๋์ ๊ฐ์ด ์๋ฒ๋ก ์ ์ก๋จ
- ์ฌ์ฉ์ ์ด๋ฆ, ๊ณ์ ID, ์๊ฐ input ์ฐฝ ์ํ๊ด๋ฆฌ
- ๊ฐ input ์ฐฝ์ ๋ง๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํตํด ์ํ๊ด๋ฆฌ๋ฅผ ํ์์
- ์ฌ์ฉ์ ์ด๋ฆ : 2~10์ ์ ๋ ฅ ๊ฐ๋ฅ
- ๊ณ์ ID : ์๋ฌธ, ์ซ์, ํน์๋ฌธ์(
.
,_
)๋ง ์ฌ์ฉ ๊ฐ๋ฅ - ์๊ฐ : 1~100์ ์ ๋ ฅ ๊ฐ๋ฅ
- ๊ฐ input ์ฐฝ์ ๋ง๋ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํตํด ์ํ๊ด๋ฆฌ๋ฅผ ํ์์
- ์์ํ๊ธฐ ๋ฒํผ ์ํ๊ด๋ฆฌ
-
์๋์ ๊ฐ์ด ๋ชจ๋ input ์ฐฝ์ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ๋ง์กฑํ์ ๋, ๋ฒํผ์ด ํ์ฑํ ๋๋๋ก ํจ
useEffect(() => { if (userName && accountName && intro) { setDisabledButton(false); } else { setDisabledButton(true); } }, [userName, accountName, intro]);
-
Enter ์ ๋ ฅ ์ ์์ํ๊ธฐ ๋ฒํผ ํด๋ฆญ๊ณผ ๋์ผํ ํจ๊ณผ๋ฅผ ์ค
const onCheckEnter = (e) => { disabledButton === false && e.key === 'Enter' && onClickStartButtonHandler(); };
-
- ํ์ ๊ฐ์
-
API ๋ช ์ธ์ ๋ฐ๋ผ ํ๋กํ ์ค์ ์ด์ ์ ๋ฐ์์จ email, password์ ํจ๊ป ์๋ฒ์ ๋ฐ์ดํฐ ์ ์ก
// JoinMembershipInput.jsx์์ ์ ๋ฌ๋ฐ์ const location = useLocation(); const email = location.state.email; const password = location.state.password;
const onClickStartButtonHandler = () => { const option = { url: 'https://mandarin.api.weniv.co.kr/user', method: 'POST', headers: { 'Content-type': 'application/json' }, data: { user: { username: userName, email: email, password: password, accountname: accountName, intro: intro, image: image, }, }, }; };
-
ํ๋กํ ์์ ๊ธฐ๋ฅ
-
๊ธฐ์กด ํ๋กํ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ (ํ๋กํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํฌํจ)
const getInfo = () => { const option = { url: 'https://mandarin.api.weniv.co.kr/user/myinfo', method: 'GET', headers: { Authorization: `Bearer ${userToken}` }, }; axios(option) .then((res) => { setUserName(res.data.user.username); setDefaultAccountName(res.data.user.accountname); setAccountName(res.data.user.accountname); setIntro(res.data.user.intro); setImage(res.data.user.image); }) .catch((err) => { console.error(err); }); };
-
์ด๋ฏธ์ง๋ฆฌ์ฌ์ด์ง์ ํตํ 10MB ์ด์์ ํ๋กํ ์ด๋ฏธ์ง๋ ์ ๋ก๋ ๊ฐ๋ฅ (ํ๋กํ ์ธํ ๊ณผ ๋์ผ)
-
์ ํจ์ฑ ๊ฒ์ฌ
-
์ ๊ทํํ์์ ํตํ ๊ณ์ ID ์ ํจ์ฑ ๊ฒ์ฌ
// ์๋ฌธ, ์ซ์, ํน์๋ฌธ์(.), (_)๋ง ์ฌ์ฉ ๊ฐ๋ฅ const regex = /*^*[a-z0-9A-Z_.]{0,}*$*/;
-
API ๋ช ์ธ์ ๋ฐ๋ฅธ ๊ณ์ ID ์ค๋ณต ๊ฒ์ฌ
- defaultAcconutName์ ๋์ด ๊ธฐ์กด ID ์ ์ง ์ ์ค๋ณต ๊ฒ์ฌ X
-
-
์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํตํ ์ ์ฅ ๋ฒํผ ํ์ฑํ (ํ๋กํ ์ธํ ๊ณผ ๋์ผ)
์ํ ๊ด๋ จ ๊ธฐ๋ฅ
-
์ํ ๋ฑ๋ก
-
์ด๋ฏธ์ง๋ฆฌ์ฌ์ด์ง์ ํตํ 10MB ์ด์์ ์ํ ์ด๋ฏธ์ง๋ ์ ๋ก๋ ๊ฐ๋ฅ (ํ๋กํ ์ค์ ๊ณผ ๋์ผ)
-
๊ฐ ์ํ ์ ๋ณด์ ์ ํจ์ฑ ๊ฒ์ฌ
-
์ํ ์ด๋ฏธ์ง (ํ์)
-
์ํ๋ช (2~15์ ์ด๋ด)
-
๊ฐ๊ฒฉ (์ซ์๋ง ์ ๋ ฅ ๊ฐ๋ฅ)
-
ํ๋งค๋งํฌ
const linkFunction = (value) => { const urlRegex = /(http(s)?:\/\/)([a-z0-9\w]+\.*)+[a-z0-9]{2,4}/gi; if (urlRegex.test(value)) { setLink(value); } else { setLink(''); } };
-
-
์ํ ๊ฐ๊ฒฉ์ ์ฒ ๋จ์ ์ฝค๋ง ์๋ ์์ฑ & ์ญ์
// ์ฝค๋ง ์ฐ๊ธฐ, ์ฝค๋ง ์์ ๊ธฐ const commaFunction = (value) => { const comma = (value) => { value = String(value); return value.replace(/(\d)(?=(?:\d{3})+(?!\d))/g, '$1,'); }; const uncomma = (value) => { value = String(value); return value.replace(/[^\d]+/g, ''); }; return comma(uncomma(value)); };
-
-
์ญ์
-
์ํ ์ญ์ ํด๋ฆญ ์ ์ํ์ด ์ญ์ ๋๊ณ , ํ๋งค ์ค์ธ ์ํ๋ง ๋ฆฌ๋ ๋๋ง
const deleteProduct = () => { const option = { url: `https://mandarin.api.weniv.co.kr/product/${productid}`, method: 'DELETE', headers: { Authorization: `Bearer ${userToken}`, 'Content-type': 'application/json', }, }; axios(option) .then(() => { updateProductList(); // ์ํ ๋ฆฌ์คํธ ๋ฆฌ๋ ๋๋ง }) .catch((err) => { console.error(err); }); closeModal(); };
// getProduct()๋ก ์ํ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ด const updateProductList = () => { getProduct(); };
-
-
์์
- ๊ธฐ์กด ์ํ ์ ๋ณด ๋ถ๋ฌ์ค๊ธฐ (์ํ ์ด๋ฏธ์ง ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํฌํจ)
- ์ํ ๋ฑ๋ก๊ณผ ๋์ผํ ๊ธฐ๋ฅ ํฌํจ
const params = useParams(); ... const onClickProductModificationHandler = () => { const option = { url: `https://mandarin.api.weniv.co.kr/product/${params.productid}`, method: 'PUT', headers: { Authorization: `Bearer ${userToken}`, 'Content-type': 'application/json', }, data: { product: { itemName: itemNameMod, price: priceMod, link: linkMod, itemImage: itemImageMod, }, }, }; axios(option) .then((res) => { console.log(res); }) .catch((err) => { console.error(err); }); }; ...
-
์น์ฌ์ดํธ์์ ์ํ ๋ณด๊ธฐ
-
๋ด ์ํ์ ๋ํด ์น์ฌ์ดํธ์์ ์ํ ๋ณด๊ธฐ ํด๋ฆญ ์, ํด๋น ํ์ด์ง๊ฐ ์ ์ฐฝ์ผ๋ก ์ด๋ฆผ
<a rel='noopener noreferrer' target='_blank' href={productLink}>์น์ฌ์ดํธ์์ ์ํ ๋ณด๊ธฐ</a>
-
React Router 6๋ฅผ ์ด์ฉํด ์ธ์ฆ(๋ก๊ทธ์ธ) ์ฌ๋ถ์ ๋ฐ๋ฅธ ์ ๊ทผ ์ ํ ๊ตฌํ
- ์ธ์ฆ ์ฌ๋ถ์ ์๊ด ์์ด ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง
- ์ธ์ฆ ์ ๋ ๊ฒฝ์ฐ๋ง ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง
- ์ธ์ฆ ๋ ๊ฒฝ์ฐ๋ง ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง
์ธ์ฆ ๋ ๊ฒฝ์ฐ๋ง ์ ๊ทผ ๊ฐ๋ฅํ ๊ฒฝ์ฐ / ์ ๋ ๊ฒฝ์ฐ๋ง ์ ๊ทผ ๊ฐ๋ฅํ ๊ฒฝ์ฐ, ๋ ์ผ์ด์ค์ ๋ํ ์ ๊ทผ ์ ํ์ ๊ตฌํํ๊ธฐ ์ํด ์ค์ฒฉ ๋ผ์ฐํ ์ฌ์ฉ
/* Router.jsx ์ผ๋ถ */
import { AuthContextStore } from '../context/AuthContext';
const Router = () => {
const { userToken } = useContext(AuthContextStore);
return (
<Routes>
<Route path='*' element={<Error404Page />} />
<Route path='/notfound' element={<Error404Page />} />
<Route element={<NonAuthRoute authenticated={userToken} />}>
<Route path='/' element={<SplashScreen />} />
<Route path='/login' element={<EmailLoginPage />} />
...
</Route>
<Route element={<AuthRoute authenticated={userToken} />}>
<Route path='/home' element={<FeedPage />} />
<Route path='/search' element={<SearchPage />} />
...
</Route>
</Routes>
);
};
์๋ ์์๋ ์ธ์ฆ ๋ ๊ฒฝ์ฐ๋ง ์ ๊ทผ ๊ฐ๋ฅํ ํ์ด์ง๋ฅผ ๊ฐ์ธ๊ณ ์๋ AuthRoute ์ปดํฌ๋ํธ ์์ธ ์ฝ๋
/* AuthRoute.jsx */
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
const AuthRoute = ({ authenticated, redirectPath = '/' }) => {
if (!authenticated) {
return <Navigate to={redirectPath} />;
}
return <Outlet />;
};
export default AuthRoute;
- ์ ๋ฌ ๋ฐ๋ ๋ฐ์ดํฐ ์ค๋ช
- authenticated : Context์ ์ ์ฅ๋ ์ฌ์ฉ์ token ๋ฐ์ดํฐ
- redirectPath : ์ธ์ฆ์ด ์ ๋ ๊ฒฝ์ฐ ๋ฆฌ๋ค์ด๋ ํธ ๋ ๊ฒฝ๋ก(
/
)๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๊ฐ๊ณ ์์
- ๋์ ์ค๋ช
- ์ ์์ ์ผ๋ก ์ธ์ฆ์ด ๋์ง ์์ ๊ฒฝ์ฐ(์ฌ์ฉ์ token์ด Falsy ๊ฐ์ ๊ฐ๋ ๊ฒฝ์ฐ)
/
๊ฒฝ๋ก๋ก ๋ฆฌ๋ค์ด๋ ํธ ๋จ - ์ ์์ ์ผ๋ก ์ธ์ฆ์ด ๋ ๊ฒฝ์ฐ
Outlet
์์ฑ์ ์ด์ฉํด ์ฌ์ฉ์๊ฐ ์ ๊ทผํ๋ ค๋ ํ์ด์ง๊ฐ ๋ ๋๋ง ๋จ
- ์ ์์ ์ผ๋ก ์ธ์ฆ์ด ๋์ง ์์ ๊ฒฝ์ฐ(์ฌ์ฉ์ token์ด Falsy ๊ฐ์ ๊ฐ๋ ๊ฒฝ์ฐ)
์ค์๊ฐ ๋ ์จ๋ฅผ ๋ฐํ์ผ๋ก ๊ฐ์์ง ์ฐ์ฑ ๋์ด๋ ๊ณ์ฐ
1. ์ฌ๋ฌ ๊ฐ์ API ์์ฒญ๊ณผ ์๋ต์ ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ Axios multiple request ์ฌ์ฉ
-
์ฐ์ฑ ๋์ด๋(๋ ์จ) ํ์ด์ง ๊ตฌํ ์ ๋ ์จ API์ ๋ฏธ์ธ๋จผ์ง API์ ๊ฐ๊ฐ ์์ฒญ์ ๋ณด๋ด๊ณ , ์๋ต์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ์ด ํ์ํ์
-
์ด๋ Axios์ multiple request ๊ธฐ๋ฅ์ ์ด์ฉํด์ ๋์์ ๋ค์ค ์์ฒญ์ ๋ณด๋ด๋๋ก ์ฒ๋ฆฌํจ
/* CommunityWeatherPage.jsx ์ผ๋ถ */ const CommunityWeatherPage = () => { const { longitude, latitude, error } = useContext(UserLocationContextStore); const OPEN_WEATHER_MAP_API = process.env.REACT_APP_OPEN_WEATHER_MAP_API; useEffect(() => { const getFetch = async () => { await axios .all([ axios.get( `https://api.openweathermap.org/data/2.5/weather ?lat=${latitude}&lon=${longitude}&appid=${OPEN_WEATHER_MAP_API}&units=metric`, ), axios.get( `https://api.openweathermap.org/data/2.5/air_pollution ?lat=${latitude}&lon=${longitude}&appid=${OPEN_WEATHER_MAP_API}`, ), ]) .then( axios.spread((weatherRes, dustRes) => { updateWeatherInfo(weatherRes); updateDustInfo(dustRes); }), ); }; getFetch(); }, [longitude, latitude]);
axios.all()
์ ์ด์ฉํด์ ์ฌ๋ฌ ๊ฐ์ ์์ฒญ์ ๋ฌถ์ด์ ํ ๋ฒ์ ๋ณด๋ด๋๋ก ํจ. ์ ์์์์๋ 2๊ฐ์ ์์ฒญ๋ง ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ณ ์์ผ๋ 3๊ฐ ์ด์๋ ๊ฐ๋ฅaxios.spread()
๋ฅผ ์ด์ฉํด์ ์์ฒญ์ ๋ํ ์๋ต์ ๊ฐ๊ฐ ๋ฐ์์ค๋๋ก ํจ
-
WeatherDescription ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ด์
- ์ฌ์ฉํ
๋ ์จ API
(OpenWeatherMap)์ ๊ฒฝ์ฐ ํด์ธ์์ ์ ๊ณตํ๋ API๋ผ ๋ด๋ถ์ ์ผ๋ก ์ ๊ณตํ๊ณ ์๋ ๋ฒ์ญ ๊ธฐ๋ฅ์ ์ฑ๋ฅ์ด ๋ฏธํกํ์์ - ๋ํ ์ฐ์ฑ ๋์ด๋ ๊ณ์ฐ ์ ๋ ์จ ์ ์๋ฅผ ์ฑ ์ ํ ํ์๊ฐ ์์์
/* WeatherDescription.jsx ์ผ๋ถ */ const WeatherDescription = { 202: { title: 'ํญ์ฐ๋ฅผ ๋๋ฐํ ์ฒ๋ฅ๊ตฌ๋ฆ', score: 10 }, 210: { title: '์ฝํ ์ฒ๋ฅ๊ตฌ๋ฆ', score: 2 }, 211: { title: '์ฒ๋ฅ๊ตฌ๋ฆ', score: 2 }, 212: { title: '๊ฐํ ์ฒ๋ฅ๊ตฌ๋ฆ', score: 10 }, 221: { title: '๋ถ๊ท์น์ ์ฒ๋ฅ๊ตฌ๋ฆ', score: 2 }, 230: { title: '์ฝํ ์ค๋ชจ๊ทธ๋ฅผ ๋๋ฐํ ์ฒ๋ฅ๊ตฌ๋ฆ', score: 2 }, ... };
- WeatherDescription ๊ฐ์ฒด ์ค๋ช
๋ ์จ API
์ ์๋ต์ผ๋ก ์ค๋๋ ์จ id
๋ฅผ ๊ฐ์ฒด์ key๋ก ์ฌ์ฉํจ- value๋ก
title(ํ๊ตญ์ด๋ก ๋ฒ์ญํ ๋ ์จ)
์score(๋ ์จ ์ ์)
๋ฅผ ๊ฐ์ง
- ์ฌ์ฉํ
-
์๋ ์์๋ ์ค์ ์ฌ์ฉ ์์
/* CommunityWeatherPage.jsx ์ผ๋ถ */ import WeatherDescription from '../../../utils/WeatherDescription'; const CommunityWeatherPage = () => { const { longitude, latitude, error } = useContext(UserLocationContextStore); const [weatherInfo, setWeatherInfo] = useState({}); useEffect(() => { const updateWeatherInfo = (res) => { const weatherData = res.data; setWeatherInfo({ weather: WeatherDescription[weatherData.weather[0].id].title, weatherScore: WeatherDescription[weatherData.weather[0].id].score, ... }); }; getFetch(); }, [longitude, latitude]); }
- weatherData.weather[0].id : ๋ ์จ API์ ์๋ต์ผ๋ก ์ค๋ ๋ ์จ id
* ์ฐ์ฑ
๋์ด๋ ๊ทธ๋ฃน : ๊ธฐ์จ / ๋ ์จ / ๊ณต๊ธฐ ์ง
* ๊ฐ ๊ทธ๋ฃน์ ์ํ๋ ์ต์ / ์ / ์ค / ํ๋ก ํ๊ฐํ๋ค.
* ์ต์ / ์์ ์ฐ์ฑ
์ด๋ ค์, ์ค์ ๋ณดํต, ํ๋ ์ฐ์ฑ
์ฌ์์ด๋ค.
* ์ต์์ 10์ , ์์ 2์ , ์ค์ 1์ , ํ๋ 0์ ์ ์ ์๋ฅผ ๊ฐ๋๋ค.
* ์ต์์ด ํ๋๋ผ๋ ๊ปด์์ผ๋ฉด ์ฐ์ฑ
์ด๋ ค์์ผ๋ก ์ฑ
์ ๋๋ค.
* ๊ธฐ์จ
์ํ๊ฒฌ์ ๊ธฐ์ค์ผ๋ก ํจ
๊ธฐ์จ ์, ์ค, ํ๋ฅผ ๋๋๋๋ TACC ์ค์ผ์ผ์ ์ฐธ๊ณ
> ๊ฒจ์ธ ๋ ์จ ์ฐธ๊ณ : https://www.k-health.com/news/articleView.html?idxno=57536
> ์ฌ๋ฆ ๋ ์จ ์ฐธ๊ณ : https://purplejam.kr/hot-summer-dog/
- ์ต์ : -9๋ ์๋, 35๋ ์ (+10)
- ์ : -8๋ ~ -2๋, 27๋ ~ 34๋ (+2)
- ์ค : -1๋ ~ 6๋, 23๋ ~ 26๋ (+1)
- ํ : 7๋ ~ 22๋ (0)
* ๋ ์จ
OpenWeatherMap API๊ฐ ์๋ตํด์ฃผ๋ ๊ฐ ์ค ๋ ์จ ์์ด๋ ๊ฐ์ธ weather.id๋ก ํ๋จ
๋ ์จ์ ๋ฐ๋ฅธ ์ ์ ๋ถ์ฌ
> utils/WeatherDescription.jsx์ scroe ์ถ๊ฐ
- ์ต์ : ํญ์ค, ํญ์ฐ, ํํ, ๊ณ ์จ, ํ๋ญ, ๋ํ, ์ฐ๋ฐ, ์ค๋ชจ๊ทธ, ํฉ์ฌ ๋ฑ (+10)
- ์ : ๊ฐํ ๋น, ์ผ ๋ฐ๋, ์ฒ๋ฅ ๊ตฌ๋ฆ ๋ฑ (+2)
- ์ค : ์ ์~์ค๊ฐ ๋น, ์ ์~์ค๊ฐ ๋, ์ฝํ~์ค๊ฐ ์ธ๊ธฐ ๋ฐ๋, ์๊ฐ ๋ฑ (+1)
- ํ : ๋ฐ๋ ๊ฑฐ์ ์์, ๋ง์ ํ๋, ์๊ฒ ๋ ์๊ฐ ๋ฑ (0)
* ๋๊ธฐ ์ง
OpenWeatherMap API๊ฐ ์๋ตํด์ฃผ๋ ๊ฐ ์ค aqi ๊ฐ์ผ๋ก ํ๋จ
aqi ๊ฐ์ ๋๊ธฐ ์ง์ 1~5๋ก ํ๊ฐํ ๊ฐ์
> API ๋งํฌ : https://openweathermap.org/api/air-pollution
- ์ต์ : 5 (+10)
- ์ : 4 (+2)
- ์ค : 2~3 (+1)
- ํ : 1 (0)
* ์ฐ์ฑ
๋์ด๋๋ฅผ ๊ตฌํ๊ธฐ ์ํด ์ธ ๊ทธ๋ฃน์ ์ ์๋ฅผ ๋ํ๋ค.
* ๋์ด๋ ์ฑ
์ ๊ธฐ์ค
- ์ด๋ ค์ : 5 ์ด์ (์ต์์ด ํ๋๋ผ๋ ๊ปด์์ผ๋ฉด ์ฐ์ฑ
์ด๋ ค์์ผ๋ก ์ฑ
์ ๋จ)
- ๋ณดํต : 2, 3, 4
- ์ฌ์ : 0, 1
์๋ฒ ๊ฐ๋ฐ์์ ์ํต์ด ์ด๋ ค์ด ์คํ API์ ํ๊ณ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํ ๋๋ฌผ๋ณ์ ์์ธ๋ณด๊ธฐ ํ์ด์ง URL ๊ตฌ์ฑ
๋๋ฌผ๋ณ์ ์์ธ๋ณด๊ธฐ ํ์ด์ง์์ ์ฅ์ ์ ๋ณด๋ฅผ ์ด๋ป๊ฒ ๊ฐ์ ธ์ฌ๊ฒ์ธ์ง์ ๋ํ ๊ณ ๋ฏผ์ด ์๊น
- ์ฅ์ id๋ฅผ ์ด์ฉํด ํด๋น ์ฅ์์ ๋ํ ์์ธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ํ์ผ๋, ํ์ธ ๊ฒฐ๊ณผ ์นด์นด์ค๋งต API๋ ๊ทธ๋ฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๊ณ ์์์
- ๋ฐ๋ผ์ URL์ ์ฅ์์ ๋ํ ์์ธ ์ ๋ณด๋ฅผ ๋ด์ ๋ณด๋ด๊ธฐ๋ก ํจ
/* HospitalItem.jsx ์ผ๋ถ */
const HospitalItem = ({ hospitalInfo }) => {
const [detailUrl, setDetailUrl] = useState(null);
useEffect(() => {
if (Object.keys(hospitalInfo).length > 0) {
const url =
'?road_address=' +
hospitalInfo.road_address_name +
'&place_name=' +
hospitalInfo.place_name +
'&phone=' +
hospitalInfo.phone +
'&x=' +
hospitalInfo.x +
'&y=' +
hospitalInfo.y;
const encodeResult = encodeURI('/community/hospital' + url);
setDetailUrl(encodeResult);
}
}, [hospitalInfo]);
return (
<HospitalItemWrapper>
<HospitalLink to={detailUrl} aria-label={`${hospitalInfo.place_name} ์์ธ ์ ๋ณด`}>
...
</HospitalLink>
</HospitalItemWrapper>
);
};
- ๋๋ก๋ช ์ฃผ์, ์ฅ์๋ช , ์ ํ๋ฒํธ, x์ขํ, y์ขํ๋ฅผ URL์ ํฌํจํจ
- ์ด๋ ์ค์ ๋ก๋ ๋จ์ํ URL์ ์๋ ์ ๋ณด๋ฅผ ๊บผ๋ด์์ ์์ธ๋ณด๊ธฐ ํ์ด์ง๋ฅผ ๊ตฌํํ ์์ ์ด์์ผ๋, ๋ณดํธ์ ์ธ ํํ์ URL๋ก ๊ตฌ์ฑํ๊ธฐ ์ํด ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ๊ฒ๊ณผ ๊ฐ์ ํํ(
์ฟผ๋ฆฌ์คํธ๋ง
)๋ก URL์ ๊ตฌ์ฑ encodeURI()
๋ฅผ ์ด์ฉํด์ ์์ฝ๋ ๋ฌธ์๋ฅผ ์ ์ธํ ๋ฌธ์๋ฅผ ์ธ์ฝ๋ฉ ์ฒ๋ฆฌ-
๋ฆฌ์คํธ ์์ดํ UI
-
๋ฆฌ์คํธ ์์ดํ ๊ฐ๋ฐ์๋๊ตฌ ํ์ธ ์ ์ธ์ฝ๋ฉ๋ URI ํ์ธ ๊ฐ๋ฅ
-
์ค์ ์ฃผ์์ฐฝ์๋ ๋์ฝ๋ฉ๋ ํํ๋ก ํ์๋์ด ํ์ธ ๊ฒฐ๊ณผ React Router ์ฌ์ฉ์ ์๋ ๋์ฝ๋ฉ๋จ
-
/* CommunityHospitalDetailPage.jsx ์ผ๋ถ */
const CommunityHospitalDetailPage = () => {
const [searchParams, setSearchParams] = useSearchParams();
const [hospitalInfo, setHospitalInfo] = useState({});
useEffect(() => {
setHospitalInfo({
address: searchParams.get('road_address'),
name: searchParams.get('place_name'),
phone: searchParams.get('phone'),
x: searchParams.get('x'),
y: searchParams.get('y'),
});
}, []);
return (
<CommunityLayout padding='0' navType='titleNav' currentMenuId={2}
isViewTabMenu={false} fillHeight={true} title={hospitalInfo.name}>
<HospitalDetailWrapper>
...
<HospitalDetail hospitalInfo={hospitalInfo} />
</HospitalDetailWrapper>
</CommunityLayout>
);
}
useSearchParams()
ํ ์ ์ด์ฉํด์ ์ฟผ๋ฆฌ์คํธ๋ง์ ๋ค๋ฃธsearchParams.get(key)
๋ฉ์๋๋ฅผ ์ด์ฉํด์ ํด๋น key์ value๋ฅผ ๊ฐ์ ธ์ด
- ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ hospitalInfo ๊ฐ์ฒด์ ์ ์ฅํ ํ ํ๋ฉด์ ํ์
์ด๋ฏธ์ง ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ์ ์ฐํ ํ์ด์ง๋ค์ด์ ์บ๋ฌ์ ์ปดํฌ๋ํธ
/* PaginationCarousle.jsx ์ผ๋ถ */
const PaginationCarousel = ({ itemList }) => {
return (
<>
<Swiper
modules={[Autoplay, Pagination]}
pagination={{
dynamicBullets: true,
}}
loop='true'
autoplay={{ delay: 2500, disableOnInteraction: false }}
>
{itemList.map(({ id, src, alt }) => (
<SwiperSlide key={id}>
<img src={src} alt={alt} />
</SwiperSlide>
))}
</Swiper>
</>
);
};
- ์ฝ๊ฒ ํ์ฅํ ์ ์๋๋ก ๊ฐ์ฒด ๋ฐฐ์ด ํํ์ธ itemList๋ฅผ props๋ก ์ ๋ฌ๋ฐ๋๋ก ํจ. ์ ๋ฌ๋ฐ์ itemList๋ ๋ฐ๋ณต์ ๋๋ฉฐ ์บ๋ฌ์ ์์ดํ (SwiperSlide)์ผ๋ก ์ถ๊ฐ๋จ
- ์บ๋ฌ์ ๊ตฌํ์ Swiper ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด์ฉ
/* CommunityMainPage ์ผ๋ถ */
import { ADVERTISING1_IMAGE, ADVERTISING2_IMAGE,
ADVERTISING3_IMAGE, ADVERTISING4_IMAGE } from './../../../styles/CommonImages';
const CommunityMainPage = () => {
const advertisingImageList = [
{ id: 0, src: ADVERTISING1_IMAGE, alt: '์ฐ์ฑ
๋์ด๋ ์๋น์ค ์คํ! ์ค์๊ฐ ๋ ์จ ํ์ธํ๊ณ ๋๋์ด๋ ์ฐ์ฑ
๊ฐ์!' },
{ id: 1, src: ADVERTISING2_IMAGE, alt: '์ง์ฌ์ํ ํ์ด์ง ๋ฐ์นญ! ์ฐ๋ฆฌ์ง ๋๋ฅ์ด๋ฅผ ์ํ ํ๋ฅญํ ์ง์ฌ ๋๊ธฐ' },
{ id: 2, src: ADVERTISING3_IMAGE, alt: 'ํน๋ณ ์ด๋ฒคํธ! ์น๊ตฌ ์ด๋ํ๊ณ ์ ๊ฒฌํธํ
๋ฌด๋ฃ๋ก ๊ฐ๊ธฐ' },
{ id: 3, src: ADVERTISING4_IMAGE, alt: 'ํ์ฌ ์์น์์ ๊ฐ์ฅ ๊ฐ๊น์ด ๋๋ฌผ๋ณ์ ์ฐพ๊ธฐ ์๋น์ค ์คํ!' },
];
return (
<CommunityLayout currentMenuId={0} fillHeight={isEmpty}>
<PaginationCarousel itemList={advertisingImageList} />
<PopularPosts isEmpty={isEmpty} changeEmptyState={changeEmptyState} />
</CommunityLayout>
);
};
id
,src
,alt
๊ฐ์ ๊ฐ๋ ์๋ ๊ฐ์ฒด ๋ฐฐ์ด์ ๋ง๋ ํ PaginationCrousel ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ฉด ํ์ด์ง๋ค์ด์ ์บ๋ฌ์ ์ด ํ๋ฉด์ ์ถ๋ ฅ๋จ- ๋ง์ฝ ์ด๋ฏธ์ง๋ฅผ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด ๊ฐ์ฒด ๋ฐฐ์ด ์ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๋ฉด ๊ฐ๋จํ๊ฒ ์บ๋ฌ์ ์ด๋ฏธ์ง ์ถ๊ฐ ๊ฐ๋ฅ
ํ์๊ฐ์ ๊ธฐ๋ฅ ๊ตฌํ (email, password)
-
email ๊ณผ password ์
input
์ฐฝ์ text ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์, ๊ฐ๊ฐ์ ์ ํจ์ฑ ๊ฒ์ฌ ํ ์คํธํ์ฌ ํต๊ณผํ๋ฉด ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ํตํ์ฌ,useNavigate()
๋ฅผ ํ์ฉํด ํด๋น ํ์ด์ง์state
์ ๋ณด๋ฅผ ๋๊ธด๋ค.... navigate('/join/setprofile', { state: { email: email, password: password, }, }); ...
๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ
input
์ฐฝ์ ๊ฒ์ํ ํค์๋์ ๋์ผํ ์ ๋ณด ( ์ฌ์ฉ์ ID ์ ์ฌ์ฉ์ ์ด๋ฆ ) ๋ฅผ GET ์์ฒญ์ ํตํ์ฌ ๋ฐ์์ด- ์ฌ์ฉ์ ID๊ฐ ์๋, ์ฌ์ฉ์ ์ด๋ฆ ๊ณผ
input
์ฐฝ์ ์ ๋ ฅํ ํค์๋๊ฐ ๋์ผํ ๋ถ๋ถ์ด ์๋ ๊ฒฝ์ฐ์๋ ์ ๋ ฅํ ํค์๋ ๋ถ๋ถ๋ง ๋ค๋ฅธ ์คํ์ผ ์ ์ฉํ์ฌ ๊ฐ์กฐ
// ํค์๋๊ฐ ์ฌ์ฉ์ ์ด๋ฆ ๊ณผ ๋ถ๋ถ์ ์ผ๋ก ๊ฒน์น๋์ง, ์๋๋ฉด ์ฌ์ฉ์ ID ์ ๋ถ๋ถ์ ์ผ๋ก ๊ฒน์น๋์ง ํ๋จํ๊ธฐ ์ํด์ indexOf ์ฌ์ฉ
const usernameValidate = ~username.indexOf(keyword);
const accountnameValidate = ~accountname.indexOf(keyword);
// username ์ ๊ฒ์ ํค์๋๋ฅผ ์ค์ฌ์ผ๋ก ์๋ผ์, 3๊ฐ์ ๋ฌธ์์ด์ ๋ฐฐ์ด๋ก ์ ์ฅ
const COMMA_APPEND_USERNAME = username.replace(keyword, `,${keyword},`);
const arrayKeyword = COMMA_APPEND_USERNAME.split(',');
<span>
// ํค์๋ ๋ถ๋ถ๋ง ๋ฐ๋ก styled-component ๋ฅผ ์์ฑํ์ฌ, ๊ฐ์กฐ
{arrayKeyword[0]}
<Keyword>{arrayKeyword[1]}</Keyword>
{arrayKeyword[2]}
</span>
๊ฒ์๊ธ ๊ด๋ จ ๊ธฐ๋ฅ ๊ตฌํ
-
๊ฒ์๊ธ ์กฐํ
useParams()
๋ฅผ ํตํ์ฌ, ํด๋น ํ์ด์ง์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ธ์, ํด๋นํ๋ ๊ฒ์๋ฌผ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ด
-
๊ฒ์๊ธ ๋ฑ๋ก
- ๊ฐ ์ด๋ฒคํธ๋ค๋ก ํ์ฌ๊ธ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ๋์์ ์คํํ์ง ์๋๋ก
preventDefault
๋ก ๋ง์ - ์ด๋ฏธ์ง - ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ ํ๊ธฐ์ํด, ์์์ ๋ง๋๋ก ์ด๋ฏธ์ง url ์ ์์ ํ๋ ์์ ์ ๊ฑฐ์ณ์ ์๋ฒ์ ์ ์ก ์ค๋น
- ๊ฒ์๊ธ ํ
์คํธ -
event.target.value
๋ฅผ ํตํ์ฌ, ํ ์คํธ๋ฅผ ์ค์๊ฐ ์ ์ฅํ์ฌ ์ํ๊ด๋ฆฌ ํ ์๋ฒ์ ์ ์ก ์ค๋น- ๊ฒ์๊ธ ๊ธธ์ด์ ๋ง๊ฒ,
input
์ฐฝ ํฌ๊ธฐ ๋ณ๊ฒฝ
// ํ ์คํธ์ ๊ธธ์ด์ ๋ง์ถ์ด ๋ฐ์คํฌ๊ธฐ ์กฐ์ const textRef = useRef(); const handleResizeHeight = useCallback(() => { textRef.current.style.height = textRef.current.scrollHeight + 'px'; }, []);
- ๊ฒ์๊ธ ๊ธธ์ด์ ๋ง๊ฒ,
- ์ด๋ฏธ์ง์ ๊ฒ์๊ธ์ด ์ค๋น๊ฐ ๋๋ฉด, ๋ฒํผ ํด๋ฆญ์ ํตํ ์ด๋ฒคํธ๋ก ๋ช
์ธ์ ๋ง๋๋ก ์๋ฒ๋ก ๋ฐ์ดํฐ ์ ์ก
- ์ด๋ฏธ์ง ์ ๋ก๋ ๋ฒํผ์ ์คํ์ผ์ด ๊ฒ์๋ฌผ ๋ฑ๋ก๋ง ๋ฌ๋ผ์, styled-component ์ค๋ฒ๋ผ์ด๋๋ฅผ ํตํ์ฌ ์์
/* ImageUploadButton.jsx */ ... const ImageUploadButton = ({ className, setUploadImg, uploadImg, inputRef }) => { ...
... return ( ... <ImgUploadButton uploadImg={postImages} setUploadImg={setUploadImg} // ์ค๋ฒ๋ผ์ด๋๋ฅผ ์ํ์ฌ className ์ ๋ถ์ฌ ( ๋ถ์ฌํ ์ด์ ๋ styled-component ๊ฐ ์คํ์ผ์ ์ ์ฉํ๋ ๋ฐฉ์์ ์ดํดํ๋ค๋ฉด ์ ์ ์๋ค. ) className={className} inputRef={inputRef} /> ... ... const ImgUploadButton = styled(ImageUploadButton)` position: fixed; margin-left: 26.6rem; bottom: 1.6rem; width: 5rem; height: 5rem; background-image: url(${UPLOAD_FILE_ICON}); background-position: center; background-size: cover; cursor: pointer; z-index: 100; `;
- ๊ฐ ์ด๋ฒคํธ๋ค๋ก ํ์ฌ๊ธ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ๋์์ ์คํํ์ง ์๋๋ก
-
๊ฒ์๊ธ ์์
useParams()
๋ฅผ ํตํ์ฌ, ํด๋น ํ์ด์ง์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ธ์, ํด๋นํ๋ ๊ฒ์๋ฌผ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ด- ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์๊ธ ๋ฑ๋กํ์ด์ง์ ๋ฟ๋ ค์ค
- ๋ฟ๋ ค์ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ์ฌ, ๋ค์ PUT ์ ํตํ์ฌ ์๋ฒ์ ์ ์ก
๋๊ธ ๊ธฐ๋ฅ ๊ตฌํ
-
์กฐํ -
props
๋ก ๊ฒ์๊ธ ์ ๋ณด๋ฅผ ๋ฐ์์, ๊ทธ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ์คcomments
์ ๋ณด๋ง์ ์ ์ ํ์ฌ ์ํ๊ด๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฟ๋ ค์ค๋ค. -
๋ฑ๋ก -
props
๋ก ๋ฐ์์จpostid
๋ฅผ ํ์ฉํ์ฌ, ์ค๋น๋text
์ ๋ณด๋ฅผ ์๋ฒ์ ์ ์กํ๋ค.const sendCommentData = () => { axios .post( `https://mandarin.api.weniv.co.kr/post/${postData.id}/comments`, { comment: { content: `${commentData}`, }, }, { headers: { Authorization: `Bearer ${userToken}`, 'Content-type': 'application/json', }, }, ) .then(() => { window.location.reload(false); }); };
-
์ญ์ ๋ฐ ์ ๊ณ
-
ํด๋น ๋๊ธ์ด ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ ๋๊ธ์ธ์ง๋ฅผ ํ๋จํ์ฌ, ๋ชจ๋ฌ์ ๋์ด๋ค
- ๋ก๊ทธ์ธ ๋ ์ฌ์ฉ์์ผ ๊ฒฝ์ฐ - ์ญ์
const deletePost = () => { axios({ url: url + `/post/${postID}`, method: 'DELETE', headers: { Authorization: `Bearer ${userToken}`, 'Content-type': 'application/json', }, }) .then((res) => { window.location.replace('/profile'); }) .catch((err) => console.error(err)); };
- ๋ค๋ฅธ ์ฌ์ฉ์์ ๊ฒฝ์ฐ - ์ ๊ณ
const reportPost = () => { axios({ url: url + `/post/${postID}/report`, method: 'POST', headers: { Authorization: `Bearer ${userToken}`, 'Content-type': 'application/json', }, }) .then((res) => { setIsReport(true); if (postID === res.data.report.post) { setIsReportSuccess(true); } else { setIsReportSuccess(false); } }) .catch((err) => console.error(err)); };
-
์ฑํ ๊ธฐ๋ฅ ๊ตฌํ
-
์ฑํ ๊ธฐ๋ฅ ๋ฐฉ์
๋ก๊ทธ์ธ๋ ์ฌ์ฉ์
โย์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ๊ฒ์๊ธ
โย์ฑํ ํ ์๋ ์ฌ์ฉ์
โ ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ์์ด๋๋ ์ ์ ๊ฒ์์ ํตํ์ฌ ์ฐพ์ ์ ์๋๋ก ์ค์
< ์ฑํ ๋ฐฉ ์์ฑ >
-
์ฑํ ํ ์๋ ์ฌ์ฉ์์ ํ๋กํ์์ ์ฑํ ์ด๋ฏธ์ง ๋ฒํผ์ ํด๋ฆญํ๋ฉด, ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ์ 3์์ ๊ฒ์๊ธ์ด ์์ฑ๋๋ค.
// ์ 3์์ ๊ฒ์๊ธ์ ๋ค๋ฅธ ์ ์ ๊ฐ ์์ฑํ ์ ์๋๋ก ํ ํฐ๊ฐ์ ์ง์ const CHAT_TOKEN = process.env.REACT_APP_CHAT_SERVER_TOKEN;
-
์ 3์์ ๊ฒ์๊ธ์ ์ ์ก๋๋ ์ปจํ ์ธ ์ธ ์ฑํ ๋ฐ์ดํฐ โ
โ๋ก๊ทธ์ธ๋ ์ฌ์ฉ์์ accountname,์ฑํ ํ ์๋ ์ฌ์ฉ์์ accountnameโ
const createChatroom = () => { axios .post( `https://mandarin.api.weniv.co.kr/post`, { post: { content: `${userAccountname},${profileUserAccountname}`, image: '', }, }, { headers: { Authorization: `Bearer ${CHAT_TOKEN}`, 'Content-type': 'application/json', }, }, ) .then((res) => { navigate(`/chat/${res.data.post.id}`); }); };
โ ์ ์ก๋ ๋ฐ์ดํฐ๋ ์ฑํ ๋ฆฌ์คํธ๋ฅผ ๋ถ๋ฌ์ฌ๋์ ์ฑํ ๋ฐฉ ์ด๋ฆ์ ๋ํ๋ผ๋ ์ฌ์ฉํ๋ค.
-
์ฑํ ๋ฐฉ ์์ฑ์, ์ 3์์ ๊ฒ์๊ธ์ content ๋ด์ฉ๊ณผ ์์ฑํ content ๋ด์ฉ์ด ์ค๋ณต๋๋ค๋ฉด alert ์ฐฝ์ ๋์์ ์ด๋ฏธ ์กด์ฌํ๋ ์ฑํ ๋ฃธ์ด๋ผ๋ ์ฌ์ค์ ์ฌ์ฉ์์๊ฒ ์๋ฆฐ๋ค.
< ์ฑํ ๋ฐฉ ๋ฆฌ์คํธ >
- ์ 3์์ ๊ฒ์๊ธ์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์, ์ ์ก๋ ์ปจํ
์ธ ๋ฐ์ดํฐ ์ ์ฌ์ฉ์์
accountname
์ด ํฌํจ๋ ๊ฒ์๊ธ๋ง ๋ณด์ฌ์ค๋ค.
< ์ฑํ ๋ฐฉ >
- useParams() ๋ฅผ ์ฌ์ฉํ์ฌ, ์ ํํ url์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ ธ์จ ํ, ๊ทธ ํ๋ผ๋ฏธํฐ(postid) ์ ํด๋นํ๋ ๊ฒ์๊ธ์ ๋ถ๋ฌ์จ๋ค. ์ฌ๊ธฐ์ ์์ฑ๋ ๋๊ธ์ด ๋ณธ์ธ์ ๊ฒ์ด๋ผ๋ฉด, ๋ณธ์ธ์ด ๋ ๋ฆฐ ์ฑํ ์ผ๋ก ๋ณด์ฌ์ง๊ณ , ์๋๋ผ๋ฉด ํ ์ฌ์ฉ์๊ฐ ๋ณด๋ธ ์ฑํ ์ฒ๋ผ ๋ณด์ฌ์ง๋ค.
< ์ฑํ >
- ์ฑํ ๋ฐฉ์ ์ ์ฅํ๋ฉด, ์ฑํ ๋ฐฉ์ผ๋ก ์ฌ์ฉ๋ ๊ฒ์๋ฌผ์ ๋๊ธ ํ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ค.
-
์ด๋ฒ ํํ๋ก์ ํธ๋ ์ ๊ฒ๋ ๋๋ฌด ํ๋ณตํ๊ณ ๊ทํ ๊ฒฝํ์ด์์ต๋๋ค. ๊ฒฝํ ๋ง์ ํ์ฅ๋์ด ํ์๋ค์ ์ํด ์ ๊ฐ ์๊ฐ์น๋ ๋ชปํ๋ ๋ถ๋ถ์ ๋ฏธ๋ฆฌ ์ค๋นํด์ฃผ์๋ ๋ชจ์ต๋ ๋๋ฌด ์ธ์๊น์๊ณ ๊ฐ๋ฐํ๋ฉด์ ๊ทธ๊ฒ์ ์ ๋ง ํธํ๊ฒ ํ์ฉํ์ต๋๋ค. ๋ฏธ๋์ ํ๋ก์ ํธ ํ์ฅ์ ๋งก๊ฒ ๋๋ค๋ฉด ๋ฐฐ์ฐ๊ณ ์ถ์๋ ๋ถ๋ถ์ด ๋ง์์ต๋๋ค. ๋ฅ๋ ฅ์๋ ํ์๋ถ๋ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉฐ ๋๊ธฐ๋ถ์ฌ๋ ๋ง์ด ๋๊ณ ์ ๊ฐ ์ ์์ง ๋ชปํ๋์ ์ ์บ์นํ๊ณ ์ผ์ค์๊ฒ ์๋ ค์ฃผ๋ ๋ชจ์ต์๋ ํฐ ์ฉ๊ธฐ๋ฅผ ์ป์์ต๋๋ค. ํ์๋ค๊ณผ ๋ถ์กฑํ ๋ถ๋ถ์ ์ฑ์์ฃผ๋ฉฐ ํ๋ก์ ํธ๋ฅผ ์ ๋ง๋ฌด๋ฆฌํ๊ฒ ๊ฐ์ ๊ธฐ์ฉ๋๋ค. ๊ธ์ ์ ์ธ ํ์ ๋ถ์๊ธฐ์ ์์ข์ ์ํฅ์ ๋ผ์น์ง ์์ผ๋ ค๊ณ ํ์๋ณด๋ค ๋ช๋ฐฐ๋ก ๋ ์ด์ฌํ ํ ์ ์์์ผ๋ฉฐ ๊ทธ๋ก์ธํด ๊ฐ์ธ์ ์ผ๋ก๋ ํฐ ์ฑ์ฅ์ ์ด๋ฃฌ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๋ฒ ํํ๋ก์ ํธ๋ฅผ ํตํด ์ ๊ฐ ์ด๋ค ๋ฐฉํฅ์ผ๋ก ๋์๊ฐ๋์ง, ๊ฐ๋ฐ๊ณผ ํ๋ก์ ํธ๋ ์ด๋ค์์ผ๋ก ํด์ผํ๋์ง์ ๋ํ ํฐ ์๊ฐ์ ์ป์์ต๋๋ค. ํจ๊ป ๊ณ ์ํ ํ์๋ค๊ป ๋๋ฌด๋๋ฌด ๊ฐ์ฌํฉ๋๋ค!!
ํ๋ก์ ํธ ๊ฒฝํ์ด ๋ถ์กฑํด์ ์ค๋ ๊ธฐ๋ ํ๊ณ ๊ฑฑ์ ๋ ๋ง์ด ๋์๋๋ฐ, ์ด์ ์๋ ํ์๋ค ๋๋ถ์ ํ์ ํ๋ฉด์ ๋ง์ ๋ถ๋ถ์ ๋ฐฐ์ฐ๊ณ ํ๋ก์ ํธ๋ฅผ ์ ๋ง์น ์ ์์์ต๋๋ค. ๊ธฐ์ ์ ์ธ ์ฑ์ฅ๋ฟ๋ง ์๋๋ผ ์๋ก์ ๋ฌธ์ ํด๊ฒฐ์ ์ํด ๋์์์ด ๋ชฐ๋ํ๋ ํ์๋ค์ ๋ชจ์ต์ ๋ณด๋ฉด์, ํฐ ๋๊ธฐ๋ถ์ฌ๊ฐ ๋์ต๋๋ค. ์์ผ๋ก ์๋ง์ ํ๋ก์ ํธ๋ฅผ ๋ง์ฃผํ๊ฒ ๋ ๋ ์ด๋ฒ ํ๋ก์ ํธ ๊ฒฝํ์ด ํฐ ์์ฐ์ด ๋ ๊ฒ์ด๋ผ ํ์ ํฉ๋๋ค. ์ค์ค๋ก ๋ถ์กฑํจ์ด ๋ง์์ง๋ง ํญ์ ๊ฒฉ๋ ค์ ์์์ ๋ณด๋ด์ค ํ์ ๋ถ๋ค์ด ๊ณ์ ์ ์ด๊ฒจ๋ผ ์ ์์์ต๋๋ค. ๋๋ฌด ์์คํ ์๊ฐ๋ค ํจ๊ปํ ์ ์์ด์ ์ข์์ต๋๋ค! ๊ฐ์ฌํฉ๋๋ค!!
ํ๋ก ํธ์๋์ค์ฟจ ๊ธฐ๊ฐ ๋์ ์ด๋ฃจ๊ณ ์ถ์๋ ๊ฐ์ธ์ ์ธ ๋ชฉํ๊ฐ 3๊ฐ ์์๋๋ฐ์. ์ฒซ ๋ฒ์งธ๊ฐ ํ์ ๊ฐ๋ฅํ ๋งํผ GitHub ์ตํ๊ธฐ, ๋ ๋ฒ์งธ๊ฐ ํ ํ๋ก์ ํธ ์ ESLint&Prettier ์ ์ฉํด๋ณด๊ธฐ, ์ธ ๋ฒ์งธ๊ฐ ํ ํ๋ก์ ํธ ์ ๋ง๋ฌด๋ฆฌํ๊ธฐ์์ต๋๋ค. ์ด๋ฒ ๊ฐ์ ธ๋๋๋ฅ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์ ๊ฐ ์ด๋ฃจ๊ณ ์ถ์๋ ๋ชฉํ 3๊ฐ๋ฅผ ๋ชจ๋ ๋ฌ์ฑํ๊ฒ ๋์ด ํ๋ณตํฉ๋๋ค.
์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํตํด ํ์ด๊ธฐ์ ํ ์ ์๋ ๊ฒฝํ์ ๋ง์ด ํ์ต๋๋ค. ์ด์ ๊ด๋ฆฌ ํ๋ก์ธ์ค์ ๋ฒ๊ทธ ๊ด๋ฆฌ ํ๋ก์ธ์ค๋ฅผ ์ธ์ ์ฐ๋ฆฌ ํ์ ๋ฌธํ๋ก ๋ง๋ ๊ฒ, ๋ค๋ฅธ ๋ถ๋ค์ ์ฝ๋๋ฅผ ์ฝ๋ ๊ฒ์ ๋๋ ค์์ด ์์๋ ์ ๊ฐ ๋ค๋ฅธ ๋ถ๋ค์ PR์ ๋ณด๋ฉฐ ๋ฆฌ๋ทฐ๋ฅผ ๋๋ฆฐ ๊ฒ, ๋ชจ๋ ํฉ์ฌํ์ฌ ์ฝ๋ฉ ์ปจ๋ฒค์ ์ ๋ง๋ค๊ณ ์ ํด์ง ์ปจ๋ฒค์ ๋๋ก ํ๋ก์ ํธ ์ด๊ธฐ ์ธํ ์ ํด๋ณธ ๊ฒ. ๋ชจ๋ ํผ์์์ผ๋ฉด ํ์ง ๋ชปํ ์์คํ ๊ฒฝํ๋ค์ด์์ต๋๋ค.
ํ์ฅ ์๋ฆฌ๋ฅผ ๋งก๊ฒ ๋์ด ๋ถ๋ด์ด ๋ง์ด ๋์๋๋ฐ, ์คํ๋ ค ์ด ๋ถ๋ด์ด ์ ์๊ฒ ์ข์ ๋ฐฉํฅ์ผ๋ก ์์ฉํ ๊ฒ ๊ฐ์ต๋๋ค! ๋ง์ง๋ง์ผ๋ก ๋์์๋ ์นญ์ฐฌ์ผ๋ก ์ ๋ฅผ ์ถค์ถ๊ฒ ํด์ฃผ์ จ๋ ํ์๋ถ๋ค๊ป ์ ๋ง ๊ฐ์ฌ๋๋ฆฝ๋๋ค. ์ ํผ์์๋ค๋ฉด ํ ์ ์์์ํ ๋ฐ ์ค๋ฅ๊ฐ ๋๋ฉด ๋ค ํจ๊ป ํด๊ฒฐํ๊ณ , ์๋๋ ๊ฒ ์์ผ๋ฉด ๋ฐค์ ์๊ฐ๋ฉฐ ์ด๋ป๊ฒ๋ ํด๋ด๋ ํ์๋ถ๋ค์ ์๋์ง์ ์ด์ ๋๋ถ์ ์์ฃผํ ์ ์์์ต๋๋ค.
ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ํ์๋ถ๋ค์ ์๋์๋ ์์์์์ ๋คํจ๊ป ์ฑ์ฅํ๋ ๊ธฐ๋ถ์ ๋๋ ์ ์์์ต๋๋ค. ์ฒ์์๋ ๋จ์ํ ํผ๋ธ๋ฆฌ์ฑ๋ ๋จธ๋ญํ ์ ์๋๋ฐ, ํผ์ ๊ณ ๋ฏผํ๋ ๊ณผ์ ์ ๊ฑฐ์น๊ณ ํ์๋ถ๋ค๊ณผ ์๋ก ์ํต์ ํตํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ๋๊ฐ๋ค ๋ณด๋ ์ด์ ๋ ํ๋์ ๊ธฐ๋ฅ์ ๋ฌผ๋ก , ์ฌ๋ฌ ๊ธฐ๋ฅ์ ๊ตฌํํ๋๊ฒ์ ์์ด ์์ ๊ฐ์ด ์๊ธด ๊ณ๊ธฐ๊ฐ ๋์์ต๋๋ค. ์๋ก ๊ถ๊ธํ ๋ถ๋ถ์ด ์์ผ๋ฉด, ๊ฐ์ด ๊ณ ๋ฏผํด์ฃผ๊ณ ํด๊ฒฐํ๋ ํ ๋ถ์๊ธฐ๊ฐ ๋๋ฌด ๋ฐ๋ฏํ๊ณ ํ๋ณตํ์ต๋๋ค. ์๊ฒ๋, styled-component ๋ฅผ ์ด๋ ๋จ์๋ก ์ชผ๊ฐ์ผ ํ๋ ์ง ์ ํฌ ํ์ ๋ชจ๋ ๊ณ ๋ฏผํ์ฌ ์ ํฌ์๊ฒ ์ ํฉํ ๋ฐฉ๋ฒ์ ์ฐฉ์์ ํ์๊ณ , ํฌ๊ฒ๋ ๋ฌดํ๋ ๋์ ๊ด๋ จ๋ ๋ถ๋ถ๋ ๋คํจ๊ป ์นจ์ฐฉํ๊ฒ ํ๋ฉด๊ณต์ ๋ฅผ ํ๋ฉฐ ํด๊ฒฐ์ ํด ๋๊ฐ์ต๋๋ค. ์ด๋ฌํ ์๋ก ํ์ ํ๊ณ ํด๊ฒฐํด ๋๊ฐ๋ ๊ณผ์ ์ ํ๋ฌ๋์ ์ง์ํ๋ค๋ณด๋ ํ์ ์ ์ค์์ฑ์ ๋๋ผ๋ฉฐ, ์ฑ์ฅํด ๋์๊ฐ์ผํ ๋ฐฉ๋ฒ์ ์๊ฒ ๋์์ต๋๋ค. ๊ฐ๋์ ์ง์น๊ณ ํ๋ ๋ ๋ ์์์ง๋ง, ํ์๋ค์ ์ด์ ์ ์ ๋ ๋ชจ๋ฅด๊ฒ ์์ผ๋ฉด์ ๋ค์ ํ๋ฒ ํ์ ์ป์ด ํ๋ก์ ํธ๋ฅผ ์งํํ์์ต๋๋ค.
๋ค๋ฅธ ๋ณต์ ๋ชฐ๋ผ๋ ํญ์ ์ฃผ๋ณ ์ธ๋ณต์ ๋ง๋ค๊ณ ์๊ฐํ๋ฉด์ ์ด์์๋๋ฐ, ์ด๋ฒ ํ๋ก์ ํธ ์ญ์ ํ๊ณ ๋ ์ธ๋ณต ๋๋ถ์ ์ข์ ํ์๋ถ๋ค๊ณผ ๋ฐ๋ฏํ ๋ถ์๊ธฐ ์์ ์๋ก ์ฑ์ฅํ๊ณ ์ข์ ์๊ฐ์ ๋ณด๋์ต๋๋ค! ์ด ์์คํ๋ ์๊ฐ์ ๊ผญ ๊ธฐ์ตํ์ฌ, ํผ์ ์ฑ์ฅํ๋ ๊ฐ๋ฐ์๊ฐ ์๋ ํจ๊ป ์ฑ์ฅํ๋ ๊ฐ๋ฐ์๊ฐ ๋๋๋ก ๋ ธ๋ ฅํ๊ฒ ์ต๋๋ค! ๊ทธ๋์ ํจ๊ป ๊ณ ์ํด์ค ํ์๋ค ๋๋ฌด ๊ฐ์ฌํฉ๋๋ค~!!!๐ธ