๐ ๋ฐฐํฌ URL
์ด๋ฉ์ผ ๋ก๊ทธ์ธ ํ ์คํธ ๊ณ์
- ID :
[email protected]
- Password :
susu2023
์์๋ง์ผ์ ์์ ์ํ์ ์ฌ๋ํ๋ ์ฌ๋๋ค์ ์ํ SNS/์ปค๋ฎค๋ํฐ ์๋น์ค์ ๋๋ค.
- ๊ฐ์ธ์ ์์ ํ ๋ฐ ๊ณต์ํ์ ์ฝ๊ฒ ์ฌ๊ณ ํ ์ ์์ต๋๋ค.
- ์ํ์ ํ๋งค/๊ตฌ๋งคํ์ง ์์๋ ์์ ์ ์ํ์ ๊ณต์ ํ๋ฉฐ ์ฆ๊ฑฐ์ด SNS ํ๋์ ํ ์ ์์ต๋๋ค.
- ๋ค๋ฅธ ์ฌ์ฉ์๋ฅผ ํ๋ก์ฐํ์ฌ ์์์ ๊ณต์ ํ ์ ์๊ณ ๋๊ธ๊ณผ ์ข์์๋ฅผ ํตํด ์ํตํ ์ ์์ต๋๋ค.
- ์์ ์ ์์ ํ์ ์์ฝ๊ฒ ์ฌ๊ณ ํ ์ ์๋ ํ๋ซํผ์ ํตํด ์๋ง์ถ์ด๋ค์๊ฒ ๊ธฐํ ์ฐฝ์ถ
- ์ผ๋ฐ ์ฌ๋๋ค๋ ๋ฌธํ๋ฅผ ์ข ๋ ๊ฐ๊น์ด ์ฆ๊ธธ ์ ์๋๋ก ํ์!
FE ๊ฐ์ค์ | FE ๋จ์ข ํ | FE ์๋๋ณ | FE ์์์ง |
---|---|---|---|
๐ GitHub ๋์์ธ ๋ฆฌ๋ |
๐ GitHub ๊ฐ๋ฐ ๋ฆฌ๋ |
๐ GitHub ํ์ฅ |
๐ GitHub ๋ ธ์ /์ ๋ฆฌ ์ฑ ์ |
- IDE : Visual Studio Code 1.74.2
- OS : Windows 10
๊ตฌ๋ถ | ์ค๋ช |
---|---|
FrontEnd | |
BackEnd | ์ ๊ณต๋ API ์ฌ์ฉ |
ํ์ ๋๊ตฌ | |
IDE |
- ๋ฒ์ ๊ด๋ฆฌ : Git, GitHub
- ์งํ ์ํฉ ๊ด๋ฆฌ(์นธ๋ฐ ๋ณด๋) : GitHub Projects
- ์ด์ ๊ด๋ฆฌ : GitHub Issues
- ๋ฌธ์ ๊ด๋ฆฌ : Notion
- ๋์์ธ ํ์ : Figma
- ๋ฉ์ ์ : Discord
react: ^18.2.0
react-router-dom: ^6.12.1
axios: ^1.4.0
styled-components: ^5.3.11
styled-reset: ^4.4.7
react-device-detect: ^2.2.3
lodash: ^4.17.21
prettier: 2.8.8
ํ ๋ฏธํ , ํ๋ก์ ํธ ๊ณํ : 2023-06-02 ~ 2023-06-05
- ์์ด์ค ๋ธ๋ ์ดํน
- ํ๋ก์ ํธ ์ฃผ์ ํ ์
- ์ฌ์ฉ ๊ธฐ์ ์คํ์ ํ๊ธฐ
- ๊ฐ์ธ ๊ณต๋ถ
- ๊น&๊นํ ์ ๋ต ์ดํด
์ปจ๋ฒค์ ๋ฐ apiํ์ต์ ์ํ ์ฌ์ ๊ณผ์ (๋ ์จ์ฑ) ์งํ : 2023-06-08 ~ 2023-06-11
์ฝ๋ ์ปจ๋ฒค์ ์ฐ์ต
API ๋น๋๊ธฐ ์ดํด
styled-component ํ์ต
๋ ์จ์ฑ ๋ ํฌ์งํ ๋ฆฌ https://github.com/24-test-project/weatherapp
์๊ตฌ์ฌํญ ํ์ ๋ฐ ํ๋ก์ ํธ ๊ท์น ์ค๋ฆฝ : 2023-06-08 ~ 2023-06-12
- ํ ๊ท์น
- ๊ธฐํ, ๋์์ธ
- prettier ์ค์
- ์ฝ๋ ์ปจ๋ฒค์ ์ค์
- git ์ปจ๋ฒค์ ์ค์
- ํด๋ ๊ตฌ์กฐ
- ๊ณตํต ์ปดํฌ๋ํธ ์ง์
- GlobalStyle ์ค์
๊ณตํตUI ์ปดํฌ๋ํธ ๊ฐ๋ฐ : 2023-06-12 ~ 2023-06-14
-
ํจ๊ป ๊ฐ์ด ๋ง๋ค์๋ ๊ฒ
- ์๋ฌ ๋ฉ์์ง
- ์คํ๋์ ํ๋ฉด ๊ฐ๋ฐ
- ๋ก๊ทธ์ธ ์์ ํ์ด์ง ๊ฐ๋ฐ
2์ฐจ ๊ฐ๋ฐ : 2023-06-19 ~ 2023-06-24
- ์๊ท๋ชจ ํ๋ก์ ํธ์ ๋ง๊ฒ Main, Develop, Feature ์ธ Branch๋ฅผ ์ฌ์ฉํ๋ ์ ๋ต ์ฌ์ฉ
- Merge ๋์ Rebase๋ฅผ ์ฌ์ฉํ์ฌ ๋ณด๊ธฐ ์ข์ ์ปค๋ฐ ํ์คํ ๋ฆฌ๋ฅผ ์ ์งํจ
- ๐ GitHub Issues
- ๊ฐํธํ ์ด์ ์์ฑ์ ์ํด ์ด์ ํ ํ๋ฆฟ์ ๋ง๋ค์ด ์ฌ์ฉํ์ต๋๋ค.
- ์ด์ ํ ํ๋ฆฟ์ผ๋ก ์ด๋ค ์ด์์ธ์ง, ์ด๋ค ํ์ด์ง์ ํด๋นํ๋ ์ง, ๊ตฌํ ํด์ผ ํ๋ ๋ด์ฉ์ด ๋ฌด์์ธ์ง๋ฅผ ์ ๋๋ก ํ์ต๋๋ค.
- ํ์์ด ํ์ฌ ์ด๋ค ์์ ์ ์งํํ๊ณ ์๋์ง๋ฅผ ๋ฐ๋ก ์ ์ ์์ด ์์ฌ์ํต ๋น์ฉ์ ์ค์ผ ์ ์์์ต๋๋ค.
- ๐๏ธ GitHub Projects
- Create Issue Branch
- ์ด์๋ฅผ ์์ฑํ๋ฉด GitHub Action์ผ๋ก ํด๋น ์ด์์ ํด๋นํ๋ ๋ธ๋์น๊ฐ ์๋์ผ๋ก ์์ฑ๋๋๋ก ์ค์ ํ์ฌ ๋ธ๋์น๋ช
์ ๊ณ ๋ฏผํ๊ณ ๋ธ๋์น๋ฅผ ์์ฑํ๋ ์๊ฐ์ ์ค์์ต๋๋ค.
- ๋ธ๋์น ์๋ํ ์ค์ ์์ธ ๋ด์ฉ
- ์) ์๋ ์์ฑ๋ ๋ธ๋์น๋ฅผ pull ํ๊ณ
git checkout -t origin/feat/issue-81
ํ์ฌ ํด๋น ๋ธ๋์น๋ก ์ด๋ํฉ๋๋ค.
ํ์ ๊ฐ์ ์ํํ ์ํต๊ณผ ํ์ ์ ์ํด ์ปค๋ฐ ์ปจ๋ฒค์ ๊ณผ, ์ฝ๋ ์ปจ๋ฒค์ ์ ๋ง๋ค์ด ์ด๋ฅผ ๋ฐ๋์ต๋๋ค.
-
๋ค์ํ ์ฌ๋ก๋ฅผ ์ฐธ๊ณ ํ์ฌ ํ๋ก์ ํธ์์ ์ฃผ๋ก ์ฐ์ผ ๊ฒ ๊ฐ์ ์ปค๋ฐ ์ ํ์ ๊ฐ์ถ๋ ค ์ปจ๋ฒค์ ์ผ๋ก ์ง์ ํ์ต๋๋ค.
1. ์ปค๋ฐ ์ ํ ์ง์ - ์ปค๋ฐ ์ ํ์ ์์ด๋ก ์์ฑํ๋ฉฐ, ์ฒซ ๊ธ์๋ฅผ ๋๋ฌธ์๋ก ํฉ๋๋ค - ์ปค๋ฐ ์ ํ - Feat : ์๋ก์ด ๊ธฐ๋ฅ, ํน์ง ์ถ๊ฐ - Fix : ์์ , ๋ฒ๊ทธ ์์ - Docs : ๋ฌธ์์ ๊ด๋ จ๋ ๋ด์ฉ, ๋ฌธ์ ์์ - Style : ์คํ์ผ๋ง - Refactor : ๋ฆฌํฉํ ๋ง - Test : ํ ์คํธ ์ฝ๋ ์์ , ๋๋ฝ๋ ํ ์คํธ๋ฅผ ์ถ๊ฐํ ๋, ๋ฆฌํฉํ ๋ง ํ ์คํธ ์ถ๊ฐ - Remove : ํ์ผ์ ์ญ์ ํ๋ ์์ ๋ง ์ํํ ๊ฒฝ์ฐ - Comment : ํ์ํ ์ฃผ์ ์ถ๊ฐ ๋ฐ ๋ณ๊ฒฝ - Rename : ํ์ผ ํน์ ํด๋๋ช ์ ์์ ํ๊ฑฐ๋ ์ฎ๊ธฐ๋ ์์ ๋ง์ธ ๊ฒฝ์ฐ - init : ์ด๊ธฐ ํ์ผ ์ค์ - Chore : ๋น๋ ์ ๋ฌด ์์ , ํจํค์ง ๋งค๋์ ์์ ๐งพ 2. ์ปค๋ฐ ๋ฉ์์ง๋ ์ ๋ชฉ & ๋ณธ๋ฌธ์ผ๋ก ๊ตฌ์ฑํฉ๋๋ค. git commit -m "Feat: ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๊ตฌํ #13 //์ ๋ชฉ - ๋ก๊ทธ์ธ ์ ํจ์ฑ ๊ฒ์ฌ //๋ณธ๋ฌธ - ๋ก๊ทธ์ธ ์ ๋ณด ์๋ฒ๋ก ์ ์ก" //๋ณธ๋ฌธ ๐ 3. ํ ์ปค๋ฐ์๋ ํ ๊ฐ์ง ๋ฌธ์ ๋ง ๋ด์ต๋๋ค.
-
๋ฆฌ์กํธ ์ฝ๋ฉ์ ์ฃผ๋ก ์ฐ์ด๋ ์ปจ๋ฒค์ ์ ์ฐธ๊ณ ํ์ฌ ์ ํฌ ์กฐ๋ง์ ์ฝ๋ ์ปจ๋ฒค์ ์ ๋ง๋ค์์ต๋๋ค.
-
๋ฌธ์์ด ์ฒ๋ฆฌ ์ ์๋ฐ์ดํ/ํ๋ฐ์ดํ์ ์ฌ์ฉ, ํน์ ๋ฌธ์ฅ ๋ ์ธ๋ฏธ์ฝ๋ก ์ ์ฌ์ฉ์ฌ๋ถ์ ๊ฐ์ ๊ฐ์ธ์ ์ทจํฅ์ด ๋ฐ์๋ ์ ์๋ ํญ๋ชฉ๋ค์ ๊ฒฝ์ฐ์๋ ์ฌ์ ์ค๋ฌธ์ ํตํด ๋ค์๊ฒฐ์ ๋ฐ๋ผ ์ง์ ํ์ต๋๋ค.
๐ผ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌ๋ ํ์ผ์ PascalCase์ผ๋ก ์์ฑํฉ๋๋ค. ๐ซ ์ปดํฌ๋ํธ๊ฐ ์๋ ํ์ผ, ํจ์๋ช , ๋ณ์๋ช ์ camelCase๋ก ์์ฑํฉ๋๋ค. ๐ ๋ค๋ฅธ ์คํ์ผ ์ํธ ํ์ผ(Styled-components)์, ์คํ์ผ ์ํธ ์ ์ฉํ ํ์ผ๋ช .style.js๋ฅผ ๋ถ์ฌ์ฃผ๊ณ , ์๊ธ์๋ ์๋ฌธ์๋ก ํฉ๋๋ค. (ํ์ฅ์๋ .js) ๐ซ ํจ์๋ช , ๋ณ์๋ช ์ camelCase๋ก ์์ฑํฉ๋๋ค. ๐ฌ ๋ฌธ์์ด์ ์ฒ๋ฆฌํ ๋๋ ์๋ฐ์ดํ๋ฅผ ์ฌ์ฉํ๋๋ก ํฉ๋๋ค. ๐ ๋ฌธ์ฅ์ด ์ข ๋ฃ๋ ๋๋ ์ธ๋ฏธ์ฝ๋ก ์ ๋ถ์ฌ์ค๋๋ค. ๐ ๊ฐ๋ ์ฑ์ ์ํด ํ ์ค์ ํ๋์ ๋ฌธ์ฅ๋ง ์์ฑํฉ๋๋ค. ๐งฎ ์ฐ์ฐ์ ์ฌ์ด์๋ ๊ณต๋ฐฑ์ ์ถ๊ฐํ์ฌ ๊ฐ๋ ์ฑ์ ๋์ ๋๋ค. ๐ ์ฝค๋ง ๋ค์์ ๊ฐ์ด ์ฌ ๊ฒฝ์ฐ ๊ณต๋ฐฑ์ ์ถ๊ฐํ์ฌ ๊ฐ๋ ์ฑ์ ๋์ ๋๋ค.
๋ ธ์ ์ ์ด์ฉํ์ฌ ํจ๊ป ์๊ฒฌ์ ๋๋๊ณ ์ค์๊ฐ์ผ๋ก ์๊ฒฌ์ ํ ์คํธํ ์์ผ ๋ ํ๋ฐํ ํ์๋ฅผ ์งํ ์๋ก์ ์๊ฒฌ์ด ๋ฌปํ์ง ์๋๋ก ๊ฐ ํ์์ ์๊ฒฌ์ ์ง์คํด์ ๋ค์๊ณ ํ์ ๋ชจ๋๊ฐ ์ดํดํ๋์ง ํญ์ ๋ฌผ์ด๋ด 1์ฐจ๊ฐ๋ฐ, 2์ฐจ๊ฐ๋ฐ์ ๋๋์ด ์งํํ์๋๋ฐ ๊ฐ ๊ฐ๋ฐ๋จ๊ณ์์ ๋๋ด์ง ๋ชปํ ํ์์ด ์๋ค๋ฉด ๋ชจ๋ ํจ๊ป ๋ชจ์ฌ ๋์์ค
- ๋์ค์ฝ๋์ ์ฐ๋ ๋ ๋ฐ ํ๋ฉด๊ณต์ ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌ ๊ฐ์์ ๊ฐ๋ฐ์ง์ ๋์ ๊ตฌํ์ํฉ์ ํธ๋ฆฌํ๊ฒ ๊ณต์ ํ์ต๋๋ค.
- ๋ ธ์ ํ ์คํ์ด์ค ํ์ด์ง๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ฌด ๋ฐ ์๊ตฌ์ฌํญ, ์ผ์ ์ ์์ ํ์ธํ ์ ์์์ต๋๋ค.
- ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ํจ๊ป ์ฑ์ฅํด๋๊ฐ๊ธฐโค๏ธ
- ํ๋ก์ ํธ ๋๋ ๋๊น์ง ๊ธ์ ์ ์ธ ์๊ฐ๋ง ํ๊ธฐโค๏ธ
- ์ฝ๋ ์ปจ๋ฒค์ ์ ๋ง์ถฐ ์์ฑํ๊ธฐโค๏ธ
- ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ํผ์ ๋๋ ์์ง๋ง๊ณ ๊ผญ ํ์๋ค๊ณผ ์์ํ๊ธฐโค๏ธ
- ํ์์ ์ฐธ์ํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด ์ฌ์ ์ ์๊ธฐํ๊ธฐโค๏ธ
- ์ ํฌ ์นํด์ ธ์โค๏ธ
- ์๊ฐ๋๋ ์์ด๋์ด๊ฐ ์์ผ๋ฉด ๋ฐ๋ก๋ฐ๋ก ์ด์ผ๊ธฐํ๊ธฐโค๏ธ
- ์ง๋ฌธํ๋ฉด ์น์ ํ๊ฒ ๋๋ตํด์ฃผ๊ธฐโค๏ธ
- ์ฃผ๊ฐ ๋ง๋ฌด๋ฆฌํ์ ๋ ํ๋ค์๋ ์ , ๊ณ ๋ฏผ ๋๋๋ ์๊ฐ ๊ฐ์ ธ์ !โค๏ธ
- ํ๋ก์ ํธ ๊ด๋ จ ๊ณต์ง์ฌํญ ์ ํ์ธํ๊ธฐโค๏ธ
- ๊ณต์ง์ฌํญ์ด ์์ผ๋ฉด ์ด๋ชจ์ง ๋จ๊ฒจ์ฃผ๊ธฐ
ํ๊ท์น์ ์ ํจ์ผ๋ก ์์๊ฐ ๋ฐ ํ ์ ์ฒด์ฑ์ ๊ฐํํ์ต๋๋ค.
- ์์ ์ GitHub Issues ๋ฑ๋ก
-
์๋ฌด๋ฆฌ ์์ ์์ ์ด๋ผ๋ ์์ํ ์ด์ ์ถ์ ์ ์ํด ์ด์ ๋ฐ๋์ ๋ฑ๋ก ํ ์์ ์งํ (์์ ํ๋๋น ์ด์ ํ๋)
-
์ปจ๋ฒค์ ํต์ผ์ ์ํด ์ด์ ํ ํ๋ฆฟ ์ฌ์ฉ
๐ง ์ด์ ํด๊ฒฐ ํ Pull Request ์์ฑ
- ์ปจ๋ฒค์ ํต์ผ์ ์ํด PR ํ ํ๋ฆฟ ์ฌ์ฉ
- ํ์ 2๋ช ์ด์์ ์น์ธ์ ๋ฐ์์ผ ๋จธ์ง ๊ฐ๋ฅ
- ์ฝ๋๋ฆฌ๋ทฐ ํ์ PR ์น์ธ
- GitHub Projects๋ฅผ ์ด์ฉํ ์นธ๋ฐ ๋ณด๋
- ์ด์ ์งํ ์ํฉ์ ํ ๋์ ๋ณผ ์ ์๋๋ก ์นธ๋ฐ ๋ณด๋ ํํ๋ก ์๊ฐํ
๐์์ ํ๋ฉด | ๐ํ์๊ฐ์ ํ์ด์ง | ๐๋ก๊ทธ์ธ ํ์ด์ง |
---|---|---|
๐ํผ๋ ํ์ด์ง | ๐๊ฒ์ ํ์ด์ง | ๐404 ํ์ด์ง |
---|---|---|
๐์ฑํ ๋ชฉ๋ก ํ์ด์ง |
---|
๐๊ฒ์๊ธ ์์ฑ ํ์ด์ง | ๐๊ฒ์๊ธ ์์ธ ํ์ด์ง | ๐๊ฒ์๊ธ ์์ ํ์ด์ง |
---|---|---|
๐๊ฒ์๊ธ ์ญ์ | ๐๋๊ธ ๊ธฐ๋ฅ |
---|---|
๐๋ง์ด ํ๋กํ ํ์ด์ง | ๐ํ๋กํ ์์ ํ์ด์ง | ๐ํ๋ก์/ํ๋ก์ ํ์ด์ง |
---|---|---|
๐์ํ ๋ฑ๋ก ํ์ด์ง | ๐์ํ ์์ ํ์ด์ง | ๐์ํ ์ญ์ ํ์ด์ง |
---|---|---|
๐์ํ ์์ธํ์ด์ง |
---|
๐์บ๋ฒ์ค |
---|
- CustomAxios : axios๋ฅผ ์ปค์คํ ํ์ฌ ๋ฐ๋ณต๋์ด ๋ค์ด๊ฐ๋ ํค๋ ๊ฐ๊ณผ baseUrl๋ฅผ ์ค์ ํ์ฌ axios ์ฌ์ฉ ์ ํค๋๊ฐ๊ณผ baseUrl๋ฅผ ์๋ตํ๋๋ก ํ์์ต๋๋ค.
- ์ด๋ฅผ ํตํด ์ฝ๋๋ฅผ ๋จ์ถ ์ํฌ์ ์์๊ณ , ํธ๋ฆฌํ๊ฒ axios๋ฅผ ์ฌ์ฉํ ์ ์์์ต๋๋ค.
import axios from "axios";
export const customAxios = axios.create();
customAxios.interceptors.request.use(
(config) => {
const accessToken = localStorage.getItem("accessToken") || "";
config.baseURL = process.env.REACT_APP_BASE_URL;
config.headers.Authorization = accessToken ? `Bearer ${accessToken}` : "";
return config;
},
(error) => {
console.log(error);
return Promise.reject(error);
},
);
- ๊ธฐ์กด ๊ฒ์์ onChange ์ด๋ฒคํธ์์ input์ ๋ณํ๊ฐ ๊ฐ์ง๋ ๋ ๋ง๋ค API์์ฒญ์ด ๋ฐ์ํ์ฌ ๋ถํ์ํ API ์์ฒญ์ด ๋ฐ์ํฉ๋๋ค.
- debunce์ ํตํด ์ค์ ํ ์๊ฐ์ด ๊ฒฝ๊ณผํ ์ดํ ์ด๋ฒคํธ๊ฐ ํธ์ถ๋์ง ์์ ๋ ์ด๋ฒคํธ๋ฅผ ํ ๋ฒ๋ง ํธ์ถํ๊ฒ ํด์ฃผ์ด ๋ถํ์ํ API ํธ์ถ์ ๋ง์์ค๋๋ค.
- lodash debounce๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์คํํ ํจ์, ๋ ๋ฒ์งธ ์ธ์๋ก ์๊ฐ์ ๋ฐ์ต๋๋ค.
// debounceํจํด ์ ์ฉํ ์ ์ ๊ฒ์ ํจ์
const handleSearch = useCallback(
debounce(async (value) => {
try {
const response = await customAxios.get(
`/user/searchuser/?keyword=${value}`,
);
setUserList(response.data);
} catch (error) {
console.error(error);
}
}, 500), // 500ms ๋์ ์
๋ ฅ์ด ์๋ค๋ฉด ํจ์์คํ
[],
);
- imgValidation ํจ์๋ฅผ ๋ง๋ค์ด์ ์ด๋ฏธ์ง ์ฌ์ฉ ํ์ด์ง์ ๊ณตํต์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ์ ์ฉํ์์ต๋๋ค.
- imgValidation์ ํจ์๋ก ๋ง๋ค์ด์ ์ฝ๋ ์ค๋ณต์ฌ์ฉ์ ํผํ๊ณ imgValidation ํ๋๋ก ํต์ผํ์์ต๋๋ค.
- ์ด๋ฏธ์ง ํ์ผ์ด ์๊ฑฐ๋ API์์ ์ ๊ณตํ๋ ์ด๋ฏธ์ง ํ์์ด ์๋๊ฑฐ๋ ํฌ๊ธฐ๊ฐ ์ด๊ณผํ๋ค๋ฉด ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฆฌ์ง ๋ชปํ๋๋ก ์ ํํ๊ณ , ๊ฒฝ๊ณ ์ฐฝ์ด ์ถ๋ ฅ๋๋๋ก ํ์์ต๋๋ค.
export const imgValidation = (file) => {
const reg = /(.*?)\.(jpg|jpeg|png|gif|bmp|tif|heic)$/;
// ํ์ผ ํ์ธ
if (!file) {
return false;
}
// ํ์ผ ์ฌ์ด์ฆ ํ์ธ
if (file.size > 1024 * 1024 * 10) {
alert("์ด๋ฏธ์ง ํ์ผ์ ํฌ๊ธฐ๋ฅผ ์ด๊ณผํ์์ต๋๋ค.(์ต๋ 10MB)");
return false;
}
// ์ด๋ฏธ์ง ์ง์ ํ์ ํ์ธ
if (!reg.test(file.name)) {
alert(
"์ด๋ฏธ์ง ํ์์ ํ์ธํด ์ฃผ์ธ์!\n(์ง์ํ์ : .jpg,.gif, .png,.jpeg, .bmp,.tif, *.heic)",
);
return false;
}
// ๋ชจ๋ ๋ง์กฑ ํ๋ค๋ฉด true ๋ฐํ
return true;
};
- ๊ตฌํ ํ๋ฉด
- ํ๋กํ ์ด๋ฏธ์ง ๋ณ๊ฒฝ์ imgValidation ์ ์ฉ
- ๋ฌดํ์คํฌ๋กค์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ผ๋ถ๋ง ๊ฐ์ ธ์ ์๋ฒ์ ๋ถ๋ด์ ์ค์ด๊ณ ๋ก๋ฉ์๋๋ฅผ ๊ฐ์ ํ์์ต๋๋ค.
- react-intersection-observer ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ๋ฌดํ์คํฌ๋กค์ ๊ตฌํํ์์ต๋๋ค.
- react-intersection-observer ๋ผ์ด๋ธ๋ฌ์ด์ useView()์ ref๊ฐ์ ๊ด์ฐฐ์์ ref๊ฐ์ ๋ฃ์ผ๋ฉด ๊ด์ฐฐ์์๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
- ๋ง์ฝ ๊ด์ฐฐ์์๊ฐ ํ๋ฉด ์ถ๋ ฅ๋๋ฉด inView true๋ก ํ๋ฉด์์ ์ฌ๋ผ์ง๋ค๋ฉด false๋ก ๋ณ๊ฒฝ๋๊ฒ ๋ฉ๋๋ค.
- hasMore๋ฅผ ํตํด ๋ค์ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด api ์์ฒญ์ ์ผ์ด๋์ง ์๊ฒ ์กฐ๊ฑด์ ๊ฑธ์ด ์ฃผ์์ต๋๋ค.
- hasMore๋ ํ์ฌ API์์ ๋ฐ์์จ post์ length์ limt๊ฐ ๊ฐ์์ง ๋น๊ตํ์ฌ ๋ค์ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ๋จํด์ค๋๋ค.
- API ์์ฒญ์ ๋ณด๋ผ ๋ ๋ง๋ค skip ๊ฐ์ limit ๊ฐ๋ง ๋งํผ ์ฆ๊ฐ ์์ผ์ฃผ์ด ๋ค์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ์ ์๋๋ก ํ์์ต๋๋ค.
export default function ProfilePost({
onClickButton,
settingPostModalProps,
closeModal,
userData,
isFeed,
}) {
const [postData, setPostData] = useState([]);
const [isNonePostData, setIsNonePostData] = useState(false);
const [isGallery, setIsGallery] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [ref, inVeiw] = useInView();
const limit = 5;
const [skip, setSkip] = useState(0);
const [hasMore, setHasMore] = useState(true);
const navigate = useNavigate();
// ๊ฒ์๋ฌผ ์ ๋ณด๋ฅผ ๋ฐ์์ด
const fetchPostData = async () => {
try {
const response = await customAxios(
isFeed
? `post/feed?limit=${limit}&skip=${skip}`
: `post/${userData.accountname}/userpost?limit=${limit}&skip=${skip}`,
);
const data = isFeed ? response.data.posts : response.data.post;
setPostData((prev) => [...prev, ...data]);
setHasMore(data.length === limit);
setSkip((prev) => prev + limit);
setIsLoading(false);
if (data.length === 0 && skip === 0) {
setIsNonePostData(true);
} else {
setIsNonePostData(false);
}
} catch (error) {
console.log(error);
}
};
useEffect(() => {
if (skip === 0 && !inVeiw) {
fetchPostData();
}
if (hasMore && inVeiw) {
fetchPostData();
}
}, [inVeiw]);
return (
// isFeed๋ฅผ ํตํด profile ํ์ด์ง์์ ์ถ๋ ฅ๋ ์์์ feed ํ์ด์ง์์ ์ถ๋ ฅ๋ ์์๋ฅผ ๊ตฌ๋ถ
<ProfilePostWrapper>
(์ค๋ต)...
<ProfilePostUl>
{postData.map((post) => (
<ProfilePostList
key={post.id}
onClickButton={onClickButton}
settingPostModalProps={settingPostModalProps}
closeModal={closeModal}
reFetchPostData={fetchPostData}
post={post}
setPostData={setPostData}
isFeed={isFeed}
userData={userData}
/>
))}
<div ref={ref}></div>
</ProfilePostUl>
)}
</>
)}
</ProfilePostWrapper>
);
}
- ๊ตฌํ ํ๋ฉด
- ์ ์ง์ ๋ก๋ฉ ๊ธฐ๋ฒ๋ฅผ ํตํด ์ด๋ฏธ์ง๊ฐ ๋ก๋ฉ๋ ๋ ์๋ณธ ์ด๋ฏธ์ง ๋์ ์ ํ์ง์ ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ค์ผ๋ก์จ UX๋ฅผ ํฅ์ ์์ผฐ์ต๋๋ค.
- react-intersection-observer ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ lazy-loading๋ฅผ ๊ตฌํํ์์ต๋๋ค.
- ์ด๋ฅผ ํตํด ์ด๋ฏธ์ง๊ฐ ํ๋ฉด์์ ๋ํ๋ ๋ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ์ค์ ํ์ฌ ๋ก๋ฉ์๊ฐ์ ๋จ์ถ ์ํฌ์ ์์ต๋๋ค.
- ์ด ๋ ๊ฐ์ง ๊ธฐ๋ฒ์ ์ด๋ฏธ์ง์ ์ ์ฉํ๊ธฐ ์ํด ProgressiveImg ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ณ , ์ด๋ฏธ์ง์ ์ ์ฉ์์ผ ์ฃผ์์ต๋๋ค.
- ProgressvieImg Props
- src : ์๋ณธ ์ด๋ฏธ์ง์ url ์ ๋๋ค.
- ...props : ๊ทธ ์ธ props๋ฅผ ๋ชจ๋ ๋ฐ์์ค๊ธฐ ์ํด ์ฌ์ฉ ํ์์ต๋๋ค.
- ์ด๋ฏธ์ง lazy-loading ์ ์ฉ ํ ์ด๊ธฐ ๋ก๋ฉ ์๋ 1.9์ด์์ 1.5์ด๋ก 0.4์ด ๊ฐ์ ๋ฐ ์ด๋ฏธ์ง ๋ฆฌ์์ค 4.5MB ์์ 2.6MB ๋ก 1.9MB(์ฝ 42%) ๊ฐ์ํ์์ต๋๋ค.
import React, {useEffect, useState } from "react";
import { Img } from "./progressiveImg.styles";
import { useInView } from "react-intersection-observer";
import noImg from "../../../img/no-image.svg";
import noImgWebp from "../../../img/webp/no-image.webp";
import { resolveWebp } from "../../../library/checkWebpSupport";
import placeholderImg from "../../../img/placeholderImg.svg";
import placeholderImgWebp from "../../../img/webp/placeholderImg.webp";
// ProgressvieImg ์ปดํฌ๋ํธ
export default function ProgressiveImg({ src, ...props }) {
const placeholderSrc = resolveWebp(placeholderImgWebp, placeholderImg);
// ์ด๋ฏธ์ง src๋ฅผ ๊ด๋ฆฌ
const [imgSrc, setImgSrc] = useState(placeholderSrc || src);
// ํ์ฌ ๋ก๋ฉ์ด ์ํ
const [isLoading, setIsLoading] = useState(true);
const { ref, inView } = useInView();
useEffect(() => {
// ์ด๋ฏธ์ง๊ฐ ํ๋ฉด์์ ๋ณด์ด๊ณ , imgSrc๊ฐ placholder์ด๋ฏธ์ง ์ผ๋ ์ด๋ฏธ์ง๋ฅผ ๋ฐ์์ด
if (inView && imgSrc === placeholderSrc) {
const img = new Image();
img.src = src;
img.onload = () => {
setImgSrc(src);
setIsLoading(false);
};
img.onerror = () => {
setIsLoading(false);
setImgSrc(resolveWebp(noImgWebp, noImg));
};
}
}, [src, inView]);
return (
<Img
{...{ src: imgSrc, ...props }}
// ๋ก๋ฉ ์ํ์ผ ๋ blurํจ๊ณผ๋ฅผ ์ฃผ๊ธฐ์ํด ์ฌ์ฉ
className={isLoading ? "loading" : "loaded"}
ref={ref}
/>
);
}
- ๊ตฌํํ๋ฉด
-
WebP ์ด๋ฏธ์ง๋ JPEG๋ PNG์ ๋นํด ์์ถ๋ฅ ์ด ๋๊ณ , ๋ ์์ ํ์ผ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๋ฉฐ, ๋์ ํ์ง์ ์ ๊ณตํ๋ ์ด๋ฏธ์ง ํ์์ ๋๋ค.
-
Webp ์ด๋ฏธ์ง ํ์์ ๊ตฌ ๋ธ๋ผ์ฐ์ ๋ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ ์ง์ ํฅ์ ๊ธฐ๋ฒ์ ์ด์ฉํ์ฌ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌํด ์ฃผ์์ต๋๋ค.
-
Webp ์ด๋ฏธ์ง๊ฐ ์ง์์ด ๋๋ค๋ฉด body ํ๊ทธ์ webp๋ผ๋ className๋ฅผ ์ถ๊ฐํด์ฃผ๊ณ , ์ง์๋์ง ์๋๋ค๋ฉด no-webp๋ผ๋ className์ ์ถ๊ฐํด์ฃผ์์ต๋๋ค.
-
body className๋ฅผ ํตํด ์ด๋ฏธ์ง ํ์์ด ๋ค๋ฅด๊ฒ ์ ์ฉ๋๋๋ก ์ฒ๋ฆฌํ์์ต๋๋ค.
-
๊ตฌ ๋ธ๋ผ์ฐ์ ์์๋ svg ์ด๋ฏธ์ง ํ์์ด ์ ์ฉ๋๋๋ก ์ฒ๋ฆฌํ์์ต๋๋ค.
-
Webp๊ฐ ์ง์๋๋ ๋ธ๋ผ์ฐ์ ์์๋ Webp ์ด๋ฏธ์ง๊ฐ ์ ์ฉ๋๋๋ก ์ฒ๋ฆฌํ์์ต๋๋ค.
-
detectWebpSupport, resolveWebp ํจ์๋ฅผ ๋ง๋ค์ด ์ด๋ฅผ ์ ์ฉ์์ผ ์ฃผ์์ต๋๋ค.
-
detectWebpSupport
- webpdata์ 1x1 ํฝ์ ํฌ๊ธฐ์ WebP ํ์์ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ฅผ base64๋ก ์ธ์ฝ๋ฉํ ๋ฌธ์์ด์ ํ ๋นํฉ๋๋ค.
- ์ด๋ฏธ์ง ๋ก๋ฉ์ด ์ฑ๊ณต์ ์ผ๋ก ์๋ฃ๋๊ฑฐ๋ ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ callback ํจ์๊ฐ ์คํ๋ฉ๋๋ค.
- webp ์ด๋ฏธ์ง๊ฐ ๋ก๋ฉ ๋๊ณ , webp์ด๋ฏธ์ง ์ง์์ฌ๋ถ ํ์ธ์ ๊ธฐ๋ค๋ฆฌ๊ธฐ ์ํด Promise๋ฅผ ์ด์ฉํด ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์์ต๋๋ค.
- image.src์ webpdata๋ฅผ ํ ๋นํ์ฌ, ์์ฑํ ๋น ์ด๋ฏธ์ง ๊ฐ์ฒด๊ฐ ํด๋น WebP ์ด๋ฏธ์ง๋ฅผ ๋ก๋ฉํ๋๋ก ํฉ๋๋ค.
- callback ํจ์์์๋ event.type์ด "load"์ธ ๊ฒฝ์ฐ์ ์ด๋ฏธ์ง์ ๋๋น(image.width)๊ฐ 1 ํฝ์ ์ธ ๊ฒฝ์ฐ๋ฅผ ๊ฒ์ฌํ์ฌ ๋ธ๋ผ์ฐ์ ๊ฐ WebP ์ด๋ฏธ์ง๋ฅผ ์ง์ํ๋์ง ์ฌ๋ถ๋ฅผ ํ๋ณํฉ๋๋ค.
- ๋ธ๋ผ์ฐ์ ๊ฐ WebP ์ด๋ฏธ์ง๋ฅผ ์ง์ํ๋ ๊ฒฝ์ฐ document.body ์์์ classList์ "webp"๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- ์ง์ํ์ง ์๋ ๋ธ๋ผ์ฐ์ ๋ผ๋ฉด document.body ์์์ classList์ "no-webp"๋ฅผ ์ถ๊ฐํฉ๋๋ค.
-
resolveWebp
- webpSupported: webp์ง์ ์ ๋ฌด, img : Webp ์ด๋ฏธ์ง ๊ฒฝ๋ก, fallbackExt : Webp ์ด๋ฏธ์ง ๋์ ์ฌ์ฉํ ์ด๋ฏธ์ง ๊ฒฝ๋ก
- ext์ ์ด๋ฏธ์ง ํ์์ ์ ์ฅํฉ๋๋ค.
- webpSupported๊ฐ false์ธ ๊ฒฝ์ฐ, ext์ด webp์ธ ๊ฒฝ์ฐ์ webp์ด๋ฏธ์ง ๊ฒฝ๋ก ๋์ webp ๋์ ์ฌ์ฉํ ์ด๋ฏธ์ง ๊ฒฝ๋ก๋ฅผ ๋ฐํํฉ๋๋ค.
-
Webp ์ด๋ฏธ์ง ์ ์ฉ์ผ๋ก ๋ก์ปฌ ์ด๋ฏธ์ง ํ์ผ ๋ฆฌ์์ค๋ฅผ 350KB์์ 37KB๋ก 313KB ์ค์์ต๋๋ค.
export async function detectWebpSupport() {
// ์ด๋ฏธ์ง๊ฐ webp ์ง์์ ๋ฌด๋ฅผ ํ์
ํ๊ณ ๋ ๋๋ง ๋ ์ ์๋๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ
return new Promise((resolve) => {
const image = new Image();
// 1px x 1px WebP ์ด๋ฏธ์ง
const webpdata =
"data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=";
const callback = (event) => {
// event.type์ด "load"์ธ ๊ฒฝ์ฐ์ ์ด๋ฏธ์ง์ ๋๋น(image.width)๊ฐ 1 ํฝ์
์ธ ๊ฒฝ์ฐ๋ฅผ ๊ฒ์ฌํ์ฌ ๋ธ๋ผ์ฐ์ ๊ฐ WebP ์ด๋ฏธ์ง๋ฅผ ์ง์ํ๋์ง ์ฌ๋ถ๋ฅผ ํ๋ณ
const result = event?.type === "load" && image.width === 1;
if (result) {
resolve(true); // WebP ์ง์๋จ
} else {
resolve(false); // WebP ์ง์๋์ง ์์
}
};
image.onerror = callback;
image.onload = callback;
image.src = webpdata;
});
}
// webpSupported: webp ์ง์ ์ ๋ฌด, img: webp ์ด๋ฏธ์ง ๊ฒฝ๋ก, fallbackExt: webp ์ด๋ฏธ์ง ๋์ฒด ์ด๋ฏธ์ง ํ์
export const resolveWebp = (img, fallbackImg) => {
const webpSupported = document.body.classList.contains("webp");
// ์ด๋ฏธ์ง ํฌ๋งท
const ext = img.split(".").pop();
// webpSupported false, ext๊ฐ webp์ธ ๊ฒฝ์ฐ
if (!webpSupported && ext === "webp") {
return fallbackImg
}
return img;
};
- ์๋ฒ์ ์ด๋ฏธ์ง๋ฅผ ์ ์กํ ์ ํ์ํ ์ด๋ฏธ์ง ๋งํผ๋ง ์ต์๋ก ์์ถํ์ฌ ์ด๋ฏธ์ง ๋ฆฌ์์ค ๋ญ๋น๋ฅผ ์ค์ผ ์ ์๋๋ก ํ์์ต๋๋ค.
- imgCompression ํจ์๋ฅผ ๋ง๋ค์ด ์ด๋ฅผ ์ ์ฉ ํ์์ต๋๋ค.
import imageCompression from "browser-image-compression";
export const uploadImgCompression = async (file) => {
const options = {
maxSizeMB: 10,
maxWidthOrHeight: 304,
useWebWorker:true,
}
const compressedFileBlob = await imageCompression(file, options);
const preview = await imageCompression.getDataUrlFromFile(compressedFileBlob);
return {compressedFileBlob, preview}
}
export const profileImgCompression = async (file) => {
const options = {
maxSizeMB: 10,
maxWidthOrHeight: 220,
useWebWorker:true,
}
const compressedFileBlob = await imageCompression(file, options);
const preview = await imageCompression.getDataUrlFromFile(compressedFileBlob);
return {compressedFileBlob, preview}
}
- ๊ตฌํ ํ๋ฉด
-
์ด๋ฏธ์ง ์์ถ ์
์ด๋ฏธ์ง ํฌ๊ธฐ : 412MB
-
์ด๋ฏธ์ง ์์ถ ํ
์ด๋ฏธ์ง ํฌ๊ธฐ : 412MB => 57MB (355MB ๊ฐ์)
๋ฆฌ์กํธ ํ๋ก์ ํธ๊ฐ ์ฒ์์ด์๊ณ ์ ์ค๋ ฅ์ ๋ํ ๋ถ์๊ฐ์ด ์ปธ๊ธฐ ๋๋ฌธ์ ํ๋ก์ ํธ์ ๋ํ ๋๋ ค์์ด ์ปธ์ต๋๋ค. ํ์ง๋ง ๊ฐ๋ฐ ์์๊ณผ ๋์์ ๊ฑฐ์ ๋งค์ผ ์ค์ ๋ถํฐ ์๋ฒฝ๊น์ง ๋์ค์ฝ๋์ ํจ๊ป ์ ์ํด์ ์ ๋ง ์ด์ฌํ ์ฝ๋ฉํ๊ณ ์๋ก ๋์๋ ์ ํฌ ํ์ ๋ถ๋ค ๋๋ถ์ ์ค๋ ฅ ํฅ์์ ๋ฌผ๋ก ํ๋ก์ ํธ๋ฅผ ์ํ๋ ๋ชฉํ๋๋ก ์ ์งํํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํตํด ํ์ ์ ๋ฌผ๋ก github๋ถํฐ react๊น์ง ์ ๋ง ๋ง์ ๊ฒ์ ๋ฐฐ์๊ฐ๋๋ค. ์ ํฌ ์กฐ๋ฅผ ๋น๋กฏํ์ฌ ๋ชจ๋ ๋ฉ์ฌ ๋ถ๋ค ์๊ณ ํ์ จ์ต๋๋ค!
์ ๋ ํ๋ก์ ํธ๊ฐ ์์ ๋ ๋ ํ์ ์ด ์ฒ์์ด๊ณ git ์ฌ์ฉ์ด ์ํด๋ฌ์ ํ ํ๋ก์ ํธ๋ฅผ ์ ํ ์ ์์์ง ๊ฑฑ์ ์ด ๋ง์๋๋ฐ, ์ข์ ํ์๋ค์ ๋ง๋์ ๋ฌด์ฌํ ํ๋ก์ ํธ๋ฅผ ๋ง๋ฌด๋ฆฌ ํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค. ํ๋ค ๋๋ ์๋ก ์์ํด์ฃผ๊ณ , ๋ชจ๋ฅด๋ ๊ฒ์ด ์์ ๋๋ ํจ๊ป ๊ณต์ ํ๋ฉด์ ๋ ๋ง์ด ์ฑ์ฅ ํ ์ ์์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ด๋ฒ ํํ๋ก์ ํธ๋ฅผ ํตํด์ ํ์ ์ด ์ด๋ป๊ฒ ์งํ๋๊ณ , ์ด๋ป๊ฒ ์ด๋ฃจ์ด์ง๋ ์์ธํ ์ ์ ์์๊ณ , git ์ฌ์ฉ์ ๋ํด์๋ ๋ง์ด ๋ฐฐ์ธ ์ ์์์ต๋๋ค. ์ด๋ฒ ํํ๋ก์ ํธ๋ฅผ ๊ณ๊ธฐ๋ก ๋ค์ ํ ํ๋ก์ ํธ๊ฐ ์๋ค๋ฉด ๋์ฑ ์ฑ์ฅํ ๋ชจ์ต์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ ์ ์๊ฒ ๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ ํฌ 24์กฐ ํ์, ๋ฉ์ฌ ๋ชจ๋ ๊ณ ์ํ์ จ๊ณ , ์ ๋ง ๊ฐ์ฌํฉ๋๋ค.
๋ฆฌ์กํธ์ ๋ฆฌ์๋ ๋ชจ๋ฅด๋ ์ ๊ฐ ํ์ ๋ฆฌ๋๋ฅผ ๋งก๊ฒ๋์ด์ ๊ฑฑ์ ์ด ๋ง์์์ง๋ง ์ข์ ํ์ ๋ถ๋ค๊ณผ ํจ๊ป ํ๋ก์ ํธ๋ฅผ ์งํํด์ ๋ถ๋ด์์ด ๋ค์ํ ํ์ ์๋์ ๊ฐ๋ฐ์ ํด๋ดค๋ ๊ฒ ๊ฐ์ต๋๋ค. ํ๋ก์ ํธ ๊ณผ์ ๋์ ์๋กญ๊ฒ ๋ฐฐ์ด๊ฒ๋ง ๋ช ๊ฐ์ธ์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ํ๋ก์ ํธ ์์ ์ ๋งํด๋ ํ์ ์ ๋ํด ๋๋ ค์์ด ๋ง์๋ ์ ์ง๋ง ์์ผ๋ก๋ ํ๋ก ํธ์๋ ํ์ ์ ๋ํด ๋๋ ต์ง ์์ ๊ฒ ๊ฐ๋ค์.
ํ๋ก์ ํธ ์์ ์ ์๋ ํ์ ์ ๋ํ ๊ฒฝํ์ด ์ ์๊ธฐ์, ์ ๊ฐ ์ํ ์ ์์๊น ํ๋ ์๊ฐ์ด ๋ง์ด ๋ค์์ต๋๋ค. ๊ทธ๋์ ์ํต์ ๋ํด ๊ฐ์ง๊ณ ์๋ ๋ง์ฐํ ๋๋ ค์์ด ์์์ง๋ง, ์ ํฌ ์กฐ์ ๋ถ๋ค์ด ์๋ก ํ๋ ์ ์ ๋ํด ์ดํดํด ์ฃผ์๊ณ , ๋๊ตฐ๊ฐ๊ฐ ์ด๋ ค์ํ๋ค๋ฉด ํจ๊ป ํด๊ฒฐํด์ฃผ๋ ค๊ณ ํ๋ ๋ถ๋ถ ๋๋ฌธ์ ์ ๋ ํฌ๊ฒ ์ฑ์ฅํ ์ ์๋ ๊ธฐํ๊ฐ ๋ ๊ฒ ๊ฐ์์ ์ข์๋ ๊ฒ ๊ฐ์ต๋๋ค. ๋ํ, ๋ฆฌ์กํธ, axois, styled-component์ ๊ฐ์ด ๊ณต๋ถํด๋ณด๊ณ ์ถ์๋ ๋ถ๋ถ์ ๋ํด ๋ช์ฃผ๊ฐ ๋ชฐ์ ํด์ ์ฌ์ฉํด๋ณผ ์ ์์๋ ์ ๋ ์ ๊ฐ๋ฐ์คํฌ ํฅ์์ ๋์์ด ๋ ๊ฒ ๊ฐ์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก ์ ํฌ 24์กฐ ์กฐ์๋ค ํ๋ก์ ํธ๊ธฐ๊ฐ๋์ ํญ์ ๊ฐ์ด ๋ฐค์๋ฉด์ ๊ณ ์ํด์ ์๊ณ ํ๋จ ๋ง ๋๋ฆฌ๊ณ ์ถ๊ณ , ๋ฉ์ฌ 5๊ธฐ ๋ถ๋ค ๋ชจ๋ ์๊ณ ๋๋ฌด ๋ง์ผ์ จ์ต๋๋ค!
src/components/
: ์๋น์ค์์ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ (๊ณตํต ์ปดํฌ๋ํธ, ๊ณตํต ๋ ์ด์์)src/commons/
: ๊ณตํต์ปดํฌ๋ํธ ์ค UI์ ๊ด๋ จ๋ ํ์ผsrc/context/
: ์ ์ญ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๊ธฐ ์ํด ์ ์ํ Context ํ์ผsrc/hooks/
: ์ฌ์ฌ์ฉ์ ์ํด ๋ถ๋ฆฌํ Custom Hooksrc/img/
: ์๋น์ค์์ ์ฌ์ฉํ๋ ์์ ํ์ผ (ํฐํธ, ์์ด์ฝ, ์ด๋ฏธ์ง)src/library/
: ์ฌ์ฉ์ ํธ๋ฆฌํ๊ฒ ํ๊ฑฐ๋ ์ฌ์ฌ์ฉ์ ์ค์ด๊ธฐ ์ํด ๋ง๋ ํจ์src/pages/
: ์ปดํฌ๋ํธ๋ฅผ ์กฐํฉํ์ฌ ๋ง๋ ํ์ด์งsrc/routes/
: ํ์ด์ง ๋ผ์ฐํ ์ ์ํ ํ์ผ
๐ฆsusuMarket
โฃ ๐public
โ ๐index.html
โฃ ๐src
โฃ ๐components
โ โฃ ๐commons
โ โ โฃ ๐button
โ โ โฃ ๐confirmModal
โ โ โฃ ๐dataInput
โ โ โฃ ๐dateFormat
โ โ โฃ ๐errorMessage
โ โ โฃ ๐menuBar
โ โ โฃ ๐newTopHeader
โ โ โฃ ๐postList
โ โ โฃ ๐postModal
โ โ โฃ ๐progressiveImg
โ โ โฃ ๐topButton
โ โ โฃ ๐userInfo
โ โ โ ๐topHeader
โฃ ๐context
โฃ ๐hook
โฃ ๐img
โฃ ๐library
โ โฃ ๐sweetAlert
โ โฃ ๐checkWebpSupport.js
โ โฃ ๐customAxios.js
โ โฃ ๐imgCompression.js
โ โ ๐imgValidation.js
โฃ ๐pages
โ โฃ ๐chat
โ โ โฃ ๐chatList
โ โ โ ๐chatRoom
โ โฃ ๐drawing
โ โฃ ๐feed
โ โ โฃ ๐post
โ โ โ โฃ ๐postDetail
โ โ โ โฃ ๐postEdit
โ โ โ โฃ ๐postUpload
โ โ โฃ ๐product
โ โ โ โฃ ๐productDetail
โ โ โ โฃ ๐productEdit
โ โ โ โ ๐productUpload
โ โ โ ๐search
โ โฃ ๐login
โ โ โฃ ๐loginEmail
โ โฃ ๐notFound
โ โฃ ๐profile
โ โ โฃ ๐follow
โ โ โ โฃ ๐followers
โ โ โ โ ๐following
โ โ โฃ ๐profileEdit
โ โ โ ๐userProfile
โ โ โ โฃ ๐profileInfo
โ โ โ โ ๐profileProduct
โ โฃ ๐signup
โ โ โฃ ๐profileSetting
โ โ โ ๐userAccount
โ โ ๐splash
โฃ ๐routes
โฃ .prettierrc.json
โฃ ๐App.js
โฃ ๐GlobalStyle.js
โ ๐index.js