diff --git a/README.md b/README.md index 553e9d9..d008910 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,84 @@ ## ๐Ÿง‘โ€๐Ÿ’ป ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ -### ์šฐ๋ฆฌ๋“ค์˜ ํ–‰๋ณตํ•œ ์‹œ๊ฐ„ โฐ + +### ์šฐ๋ฆฌ๋“ค์˜ ํ–‰๋ณตํ•œ ์‹œ๊ฐ„ โฐ + > ์šฐ๋ฆฌFIS ์•„์นด๋ฐ๋ฏธ ๊ต์œก์ƒ๋“ค์„ ๋Œ€์ƒ์œผ๋กœ ๊ณต๋ถ€ํ•œ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜์—ฌ ํ•™์Šต ๊ธฐ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค -[์šฐํ–‰์‹œ์— ์ ‘์†ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ํด๋ฆญํ•ด์ฃผ์„ธ์š”! ๐Ÿ‘€](https://woohangshi.vercel.app/) -- ํ…Œ์ŠคํŠธ ์œ ์ € ID: test@test.com +[์šฐํ–‰์‹œ์— ์ ‘์†ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ํด๋ฆญํ•ด์ฃผ์„ธ์š”! ๐Ÿ‘€](https://woohangshi.vercel.app/) + +- ํ…Œ์ŠคํŠธ ์œ ์ € ID: test@test.com - ํ…Œ์ŠคํŠธ ์œ ์ € PW: test1234!
## ๐Ÿ‘ป ํŒ€์›๊ตฌ์„ฑ + | | | | -|:---:|:---:|:---:| -| [๊ฐ•์žฌ์—ฐ](https://github.com/riverkite0708) | [๊ณต์†Œ์—ฐ](https://github.com/Kong-E) | [์ด๋„์ด](https://github.com/doyi0107) | -| ํ”„๋ก ํŠธ์—”๋“œ | ํ”„๋ก ํŠธ์—”๋“œ | ํ”„๋ก ํŠธ์—”๋“œ | -| UI ๊ฐ€์ด๋“œ๋ผ์ธ,
๊ธฐ๋กํ™•์ธ, ๋งˆ์ดํŽ˜์ด์ง€ ๊ตฌํ˜„ | ์ธ์ฆ/์ธ๊ฐ€, ๊ณต๋ถ€ํ•˜๊ธฐ ๊ตฌํ˜„,
API ์„ค์ • | ๋žœ๋”ฉ ํŽ˜์ด์ง€, ๊ณผ๋ชฉ์„ ํƒ,
์ˆœ์œ„์กฐํšŒ ๊ตฌํ˜„ | +| :------------------------------------------------------------: | :-----------------------------------------------------: | :-------------------------------------------------------: | +| [๊ฐ•์žฌ์—ฐ](https://github.com/riverkite0708) | [๊ณต์†Œ์—ฐ](https://github.com/Kong-E) | [์ด๋„์ด](https://github.com/doyi0107) | +| ํ”„๋ก ํŠธ์—”๋“œ | ํ”„๋ก ํŠธ์—”๋“œ | ํ”„๋ก ํŠธ์—”๋“œ | +| UI ๊ฐ€์ด๋“œ๋ผ์ธ,
๊ธฐ๋กํ™•์ธ, ๋งˆ์ดํŽ˜์ด์ง€ ๊ตฌํ˜„ | ์ธ์ฆ/์ธ๊ฐ€, ๊ณต๋ถ€ํ•˜๊ธฐ ๊ตฌํ˜„,
API ์„ค์ • | ๋žœ๋”ฉ ํŽ˜์ด์ง€, ๊ณผ๋ชฉ์„ ํƒ,
์ˆœ์œ„์กฐํšŒ ๊ตฌํ˜„ | | | | | | -|:---:|:---:|:---:|:---:| -| [๊ฐ•ํ˜„์šฐ(ํŒ€์žฅ)](https://github.com/khwoowoo) | [๊ธธ๊ฐ€์€](https://github.com/rlfrkdms1) | [๊น€ํ˜œ๋นˆ](https://github.com/qbobl5) | [๊ณต์˜ˆ์ง„](https://github.com/yaejinkong) | -| ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | -| ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ ๊ตฌ์ถ•,
๋žญํ‚น API ๊ฐœ๋ฐœ | ์ธ์ฆ/์ธ๊ฐ€, ์ด๋ฉ”์ผ ์ธ์ฆ ๊ธฐ๋Šฅ,
๊ณต๋ถ€์‹œ๊ฐ„ ๊ธฐ๋ก API ๊ฐœ๋ฐœ | CI ํ™˜๊ฒฝ ๊ตฌ์ถ•,
ํƒ€์ด๋จธ ์กฐํšŒ ๋ฐ ์บ˜๋ฆฐ๋” API ๊ฐœ๋ฐœ | ๊ณผ๋ชฉ ๋ฐ ํšŒ์› ์ •๋ณด API ๊ฐœ๋ฐœ | +| :-------------------------------------------------------: | :--------------------------------------------------------: | :-----------------------------------------------------: | :---------------------------------------------------------: | +| [๊ฐ•ํ˜„์šฐ(ํŒ€์žฅ)](https://github.com/khwoowoo) | [๊ธธ๊ฐ€์€](https://github.com/rlfrkdms1) | [๊น€ํ˜œ๋นˆ](https://github.com/qbobl5) | [๊ณต์˜ˆ์ง„](https://github.com/yaejinkong) | +| ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | ๋ฐฑ์—”๋“œ | +| ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ ๊ตฌ์ถ•,
๋žญํ‚น API ๊ฐœ๋ฐœ | ์ธ์ฆ/์ธ๊ฐ€, ์ด๋ฉ”์ผ ์ธ์ฆ ๊ธฐ๋Šฅ,
๊ณต๋ถ€์‹œ๊ฐ„ ๊ธฐ๋ก API ๊ฐœ๋ฐœ | CI ํ™˜๊ฒฝ ๊ตฌ์ถ•,
ํƒ€์ด๋จธ ์กฐํšŒ ๋ฐ ์บ˜๋ฆฐ๋” API ๊ฐœ๋ฐœ | ๊ณผ๋ชฉ ๋ฐ ํšŒ์› ์ •๋ณด API ๊ฐœ๋ฐœ |
## โญ ํ”„๋กœ์ ํŠธ ์ฃผ์š” ๊ธฐ๋Šฅ + ์ค€๋น„์ค‘ ์ž…๋‹ˆ๋‹ค.
## โš™๏ธ ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜ -### ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋„ -![ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋„](https://github.com/user-attachments/assets/97c151ee-4875-4c52-b446-0f5b88dabaaa) ---- -### ์ธํ”„๋ผ ๊ตฌ์กฐ๋„ + +### ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋„ + +## ![ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋„](https://github.com/user-attachments/assets/97c151ee-4875-4c52-b446-0f5b88dabaaa) + +### ์ธํ”„๋ผ ๊ตฌ์กฐ๋„ + ![์ธํ”„๋ผ ๊ตฌ์กฐ๋„](https://github.com/user-attachments/assets/81017061-93c3-4720-9abc-eafc10874017)
## ๐Ÿ“š ๊ธฐ์ˆ  ์Šคํƒ -### Common + +### Common + ![Notion](https://img.shields.io/badge/Notion-eeeeee.svg?style=flat-square&logo=notion&logoColor=000000) ![GitHub](https://img.shields.io/badge/Github-%23121011.svg?style=flat-square&logo=github&logoColor=white) ![Postman](https://img.shields.io/badge/Postman-FF6C37?style=flat-square&logo=postman&logoColor=white) -### UI/UX +### UI/UX + ![Radix UI](https://img.shields.io/badge/Radix%20ui-161618.svg?style=flat-square&logo=radix-ui&logoColor=white) ![figma](https://img.shields.io/badge/Figma-f24e1e?style=flat-square&logo=figma&logoColor=ffffff) -### Frontend +### Frontend + ![Next JS](https://img.shields.io/badge/Next.js-black?style=flat-square&logo=next.js&logoColor=white) ![Zustand](https://img.shields.io/badge/Zustand-orange?style=flat-square&logo=zustand&logoColor=white) ![TypeScript](https://img.shields.io/badge/TypeScript-%23007ACC.svg?style=flat-square&logo=typescript&logoColor=white) ![authjs](https://img.shields.io/badge/Auth.js-1eabf4?style=flat-square&logo=nextauth&logoColor=black) -![SWR](https://img.shields.io/badge/SWR-black.svg?style=flat-square&logo=swr&logoColor=white) +![SWR](https://img.shields.io/badge/SWR-black.svg?style=flat-square&logo=swr&logoColor=white) ![ESLint](https://img.shields.io/badge/ESLint-4B3263?style=flat-square&logo=ESLint&logoColor=white) ![Prettier](https://img.shields.io/badge/Prettier-%23F7B93E.svg?style=flat-square&logo=prettier&logoColor=black) ![Vercel](https://img.shields.io/badge/Vercel-000000?style=flat-square&logo=Vercel&logoColor=white) -### Backend +### Backend + -### Infra & DB +### Infra & DB + -### CI/CD +### CI/CD + Amazon Elastic Beanstalk Status
@@ -77,18 +92,18 @@ โ”‚ย ย  โ”œโ”€โ”€ easteregg/ โ–ถ๏ธ ์ด์Šคํ„ฐ์—๊ทธ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ icons/ โ–ถ๏ธ ์•„์ด์ฝ˜ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ imgs/ โ–ถ๏ธ ์ด๋ฏธ์ง€ ํด๋” -โ”‚ย ย  โ””โ”€โ”€ fonts/ ํฐํŠธ ํด๋” +โ”‚ย ย  โ””โ”€โ”€ fonts/ ํฐํŠธ ํด๋” โ”œโ”€โ”€ app/ โ–ถ๏ธ ์•ฑ ๋ผ์šฐํŒ… ํด๋” -โ”‚ย ย  โ”œโ”€โ”€ (auth)/ โ–ถ๏ธ ์ธ์ฆ ์ธ๊ฐ€ ํด๋” -โ”‚ย ย  โ”œโ”€โ”€ actions/ โ–ถ๏ธ auth.js ํ•จ์ˆ˜ ํ˜ธ์ถœ ํด๋” +โ”‚ย ย  โ”œโ”€โ”€ (auth)/ โ–ถ๏ธ ์ธ์ฆ ์ธ๊ฐ€ ํด๋” +โ”‚ย ย  โ”œโ”€โ”€ actions/ โ–ถ๏ธ auth.js ํ•จ์ˆ˜ ํ˜ธ์ถœ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ api/auth/[...nextauth]/ โ–ถ๏ธ auth.js ์„ค์ • ํด๋” โ”‚ย ย  โ”œโ”€โ”€ mypage/ โ–ถ๏ธ ๋งˆ์ดํŽ˜์ด์ง€ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ ranking/ โ–ถ๏ธ ์ˆœ์œ„์กฐํšŒ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ record/ โ–ถ๏ธ ๊ธฐ๋กํ™•์ธ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ study/ โ–ถ๏ธ ๊ณต๋ถ€์‹œ์ž‘ ํด๋” -โ”‚ย ย  โ”œโ”€โ”€ page.tsx โ–ถ๏ธ root ๊ฒฝ๋กœ ํŽ˜์ด์ง€ -โ”‚ย ย  โ””โ”€โ”€ layout.tsx โ–ถ๏ธ root ๊ฒฝ๋กœ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐ -โ”œโ”€โ”€ components/ โ–ถ๏ธ ์ปดํฌ๋„ŒํŠธ ํด๋” +โ”‚ย ย  โ”œโ”€โ”€ page.tsx โ–ถ๏ธ root ๊ฒฝ๋กœ ํŽ˜์ด์ง€ +โ”‚ย ย  โ””โ”€โ”€ layout.tsx โ–ถ๏ธ root ๊ฒฝ๋กœ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐ +โ”œโ”€โ”€ components/ โ–ถ๏ธ ์ปดํฌ๋„ŒํŠธ ํด๋” โ”‚ย ย  โ”œโ”€โ”€ common/ โ–ถ๏ธ ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ํด๋” โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ Header/ โ”‚ย ย  โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ Header์ปดํฌ๋„ŒํŠธ.tsx @@ -99,7 +114,7 @@ โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ ์ปดํฌ๋„ŒํŠธ.tsx โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ ์ปดํฌ๋„ŒํŠธ.module.css โ”‚ย ย  โ”œโ”€โ”€ ๋ผ์šฐํŒ…ํด๋”๋ช…/์ปดํฌ๋„ŒํŠธ.tsx -โ”‚ย ย  โ””โ”€โ”€ ๋ผ์šฐํŒ…ํด๋”๋ช…/์ปดํฌ๋„ŒํŠธ.module.css +โ”‚ย ย  โ””โ”€โ”€ ๋ผ์šฐํŒ…ํด๋”๋ช…/์ปดํฌ๋„ŒํŠธ.module.css โ”œโ”€โ”€ constants/ โ”‚ย ย  โ””โ”€โ”€ ์ƒ์ˆ˜๋ช….ts โ”œโ”€โ”€ hooks/ @@ -107,7 +122,7 @@ โ”œโ”€โ”€ apis/ โ”‚ย ย  โ”œโ”€โ”€ instancs.ts โ–ถ๏ธ api ์š”์ฒญ ๊ธฐ๋ณธ ์„ค์ • ํŒŒ์ผ โ”‚ย ย  โ””โ”€โ”€ ๋„๋ฉ”์ธApi.ts -โ”œโ”€โ”€ stores/ โ–ถ๏ธ Zustand Store ํด๋” +โ”œโ”€โ”€ stores/ โ–ถ๏ธ Zustand Store ํด๋” โ”‚ย ย  โ””โ”€โ”€ ๋„๋ฉ”์ธStore.ts โ”œโ”€โ”€ types/ โ–ถ๏ธ TypeScript Interface ์„ค์ • ํด๋” โ”‚ย ย  โ””โ”€โ”€ ๋„๋ฉ”์ธType.ts @@ -121,7 +136,8 @@ โ””โ”€โ”€ package.json โ–ถ๏ธ NPM ํ”„๋กœ์ ํŠธ ์„ค์ • ํŒŒ์ผ ``` -## ๐ŸŽˆ Commit ๋ฐฉ๋ฒ• +## ๐ŸŽˆ Commit ๋ฐฉ๋ฒ• + ๊ผญ ๋‹ค์Œ์˜ ๋ฐฉ๋ฒ•์„ ๋”ฐ๋ผ์„œ ์ปค๋ฐ‹ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ, ์•Œ์•„๋ณด๊ธฐ ์‰ฝ๊ฒŒํ•˜๊ธฐ ์œ„ํ•จ. \ ์ปค๋ฐ‹์˜ ์ œ๋ชฉ์€ ํƒ€์ž…์„ ๊ธฐ์žฌ ํ›„ ๊ฐ„๋‹จํ•œ ์š”์•ฝ(๋ช…๋ น์กฐ)์„ ๊ธฐ์žฌ ํ•จ. @@ -129,19 +145,20 @@ ๋ณธ๋ฌธ ์ž‘์„ฑ์‹œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ๋ˆ„๊ตฌ๋“  ์•Œ์•„๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๊ธฐ์žฌ ํ•จ(์–ด๋–ป๊ฒŒ ๋ณด๋‹ค **์™œ**์— ์ดˆ์ ์„ ๋งž์ถฐ ์ž‘์„ฑ). \ **ํƒ€์ž…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Œ.** -* feat : ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ -* fix : ๋ฒ„๊ทธ ์ˆ˜์ • -* docs : ๋ฌธ์„œ ์ˆ˜์ • -* style : ์ฝ”๋“œ formatting, ์„ธ๋ฏธ์ฝœ๋ก (;) ๋ˆ„๋ฝ, ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ์—†๋Š” ๊ฒฝ์šฐ -* refactor : ์ฝ”๋“œ ๋ฆฌํŒฉํ„ฐ๋ง -* test : ํ…Œ์ŠคํŠธ ์ฝ”๋“œ, ๋ฆฌํŒฉํ„ฐ๋ง ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ถ”๊ฐ€(ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ X) -* chore : ๋นŒ๋“œ ์—…๋ฌด ์ˆ˜์ •, ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ˆ˜์ •(ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ X) -* design : CSS ๋“ฑ ์‚ฌ์šฉ์ž UI ๋””์ž์ธ ๋ณ€๊ฒฝ -* comment : ํ•„์š”ํ•œ ์ฃผ์„ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ -* rename : ํŒŒ์ผ ํ˜น์€ ํด๋”๋ช…์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์˜ฎ๊ธฐ๋Š” ์ž‘์—…๋งŒ์ธ ๊ฒฝ์šฐ -* remove : ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๋Š” ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•œ ๊ฒฝ์šฐ -* !BREAKING CHANGE : ์ปค๋‹ค๋ž€ API ๋ณ€๊ฒฝ์˜ ๊ฒฝ์šฐ -* !HOTFIX : ๊ธ‰ํ•˜๊ฒŒ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ๋ฅผ ๊ณ ์ณ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ + +- feat : ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€ +- fix : ๋ฒ„๊ทธ ์ˆ˜์ • +- docs : ๋ฌธ์„œ ์ˆ˜์ • +- style : ์ฝ”๋“œ formatting, ์„ธ๋ฏธ์ฝœ๋ก (;) ๋ˆ„๋ฝ, ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ์—†๋Š” ๊ฒฝ์šฐ +- refactor : ์ฝ”๋“œ ๋ฆฌํŒฉํ„ฐ๋ง +- test : ํ…Œ์ŠคํŠธ ์ฝ”๋“œ, ๋ฆฌํŒฉํ„ฐ๋ง ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ถ”๊ฐ€(ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ X) +- chore : ๋นŒ๋“œ ์—…๋ฌด ์ˆ˜์ •, ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ์ˆ˜์ •(ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ X) +- design : CSS ๋“ฑ ์‚ฌ์šฉ์ž UI ๋””์ž์ธ ๋ณ€๊ฒฝ +- comment : ํ•„์š”ํ•œ ์ฃผ์„ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ +- rename : ํŒŒ์ผ ํ˜น์€ ํด๋”๋ช…์„ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์˜ฎ๊ธฐ๋Š” ์ž‘์—…๋งŒ์ธ ๊ฒฝ์šฐ +- remove : ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๋Š” ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•œ ๊ฒฝ์šฐ +- !BREAKING CHANGE : ์ปค๋‹ค๋ž€ API ๋ณ€๊ฒฝ์˜ ๊ฒฝ์šฐ +- !HOTFIX : ๊ธ‰ํ•˜๊ฒŒ ์น˜๋ช…์ ์ธ ๋ฒ„๊ทธ๋ฅผ ๊ณ ์ณ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์˜ˆ์‹œ `[feat/#์ด์Šˆ๋ฒˆํ˜ธ]: ํƒ€์›Œ ์ถ”๊ฐ€` diff --git a/apis/instance.ts b/apis/instance.ts index 2874d58..0fb83dc 100644 --- a/apis/instance.ts +++ b/apis/instance.ts @@ -7,7 +7,9 @@ import { redirect } from 'next/dist/server/api-utils'; interface RequestOptions { headers?: Record; - [key: string]: string | Record | undefined; + isMultipart?: boolean; // ๋ฉ€ํ‹ฐํŒŒํŠธ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์†์„ฑ ์ถ”๊ฐ€ + body?: any; // ์š”์ฒญ ๋ณธ๋ฌธ์— ๋Œ€ํ•œ ํƒ€์ž… ์ •์˜ + [key: string]: any; // ๋‹ค๋ฅธ ์†์„ฑ ํ—ˆ์šฉ } const fetchInstance = async (url: string, options: RequestOptions = {}) => { @@ -15,10 +17,16 @@ const fetchInstance = async (url: string, options: RequestOptions = {}) => { const accessToken = session?.user?.accessToken; const headers: RequestOptions['headers'] = { - 'Content-Type': 'application/json', ...options.headers, }; + // FormData์ธ ๊ฒฝ์šฐ Content-Type ์„ค์ • ์ œ๊ฑฐ + if (options.body instanceof FormData) { + delete headers['Content-Type']; + } else { + headers['Content-Type'] = 'application/json'; + } + if (accessToken) { headers.Authorization = `Bearer ${accessToken}`; } @@ -35,7 +43,7 @@ const fetchInstance = async (url: string, options: RequestOptions = {}) => { console.error('Token Expired'); } console.error('Fetch Error:', errorResponse); - return { error: errorResponse }; + return response; } if (response.headers.get('Content-Type')?.includes('application/json')) { diff --git a/apis/memberApi.ts b/apis/memberApi.ts index a579bb8..544f362 100644 --- a/apis/memberApi.ts +++ b/apis/memberApi.ts @@ -1,3 +1,4 @@ +import { revalidatePath } from 'next/cache'; import { instance } from './instance'; // ์œ ์ €์ •๋ณด ์กฐํšŒ @@ -9,6 +10,16 @@ export const getUserInfo = async () => { return response; }; +// ์œ ์ €์ •๋ณด ์ˆ˜์ • +export const patchUserInfo = async ({ name, course }: { name: string; course: string }) => { + const response = await instance(`members`, { + method: 'PATCH', + body: JSON.stringify({ name, course }), + }); + + return response; +}; + // ๋น„๋ฐ€๋ฒˆํ˜ธ ์—…๋ฐ์ดํŠธ export const postPwUpdate = async ({ oldPassword, newPassword }: { oldPassword: string; newPassword: string }) => { const response = await instance('members', { diff --git a/apis/rankingApi.ts b/apis/rankingApi.ts index a577585..b28c22d 100644 --- a/apis/rankingApi.ts +++ b/apis/rankingApi.ts @@ -1,6 +1,6 @@ import { instance } from './instance'; import { ApiResponse } from '../types/rankingType'; -import rankingImg from '../assets/icons/ranking_profile_img.png'; +import rankingImg from '@/public/imgs/ranking/ranking_profile_img.png'; //๋žญํ‚น ์กฐํšŒ api export const getMemberRanking = async ({ diff --git a/app/font.css b/app/font.css index acc086e..5a3c3bf 100644 --- a/app/font.css +++ b/app/font.css @@ -4,15 +4,15 @@ font-weight: 900; font-display: swap; src: - url('/font/Pretendard-Black.subset.woff2') format('woff2'), - url('/font/Pretendard-Black.subset.woff') format('woff'); + url('/fonts/Pretendard-Black.subset.woff2') format('woff2'), + url('/fonts/Pretendard-Black.subset.woff') format('woff'); } @font-face { font-family: 'Pretendard'; font-weight: 700; font-display: swap; - src: url('/font/Pretendard-Bold.subset.woff') format('woff'); + src: url('/fonts/Pretendard-Bold.subset.woff') format('woff'); } @font-face { @@ -20,8 +20,8 @@ font-weight: 500; font-display: swap; src: - url('/font/Pretendard-Medium.subset.woff2') format('woff2'), - url('/font/Pretendard-Medium.subset.woff') format('woff'); + url('/fonts/Pretendard-Medium.subset.woff2') format('woff2'), + url('/fonts/Pretendard-Medium.subset.woff') format('woff'); } @font-face { @@ -29,8 +29,8 @@ font-weight: 400; font-display: swap; src: - url('/font/Pretendard-Regular.subset.woff2') format('woff2'), - url('/font/Pretendard-Regular.subset.woff') format('woff'); + url('/fonts/Pretendard-Regular.subset.woff2') format('woff2'), + url('/fonts/Pretendard-Regular.subset.woff') format('woff'); } @font-face { @@ -38,6 +38,6 @@ font-weight: 100; font-display: swap; src: - url('/font/Pretendard-Thin.subset.woff2') format('woff2'), - url('/font/Pretendard-Thin.subset.woff') format('woff'); + url('/fonts/Pretendard-Thin.subset.woff2') format('woff2'), + url('/fonts/Pretendard-Thin.subset.woff') format('woff'); } diff --git a/app/mypage/infoupdate/page.tsx b/app/mypage/infoupdate/page.tsx new file mode 100644 index 0000000..a553972 --- /dev/null +++ b/app/mypage/infoupdate/page.tsx @@ -0,0 +1,67 @@ +'use client'; + +import ClassRadioGroup from '@/components/auth/ClassRadioGroup'; +import InputField from '@/components/auth/InputField'; +import CommonButton from '@/components/common/CommonButton'; +import { Box } from '@radix-ui/themes'; +import { patchUserInfo } from '@/apis/memberApi'; +import { useState, useTransition } from 'react'; + +export default function InfoUpdate() { + const [isPending, startTransition] = useTransition(); + const [userInfo, setUserInfo] = useState({ + name: '', + course: '', + }); + + const handleNameChange = (name: string) => { + setUserInfo({ ...userInfo, name }); + }; + + const handleCourseChange = (value: string) => { + setUserInfo({ ...userInfo, course: value }); + }; + + const submitHandler = (e: React.FormEvent) => { + e.preventDefault(); + + if (userInfo.name.trim() === '') { + alert('์ˆ˜์ •ํ•  ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'); + return; + } + if (userInfo.course === '') { + alert('์ˆ˜์ •ํ•  ๊ณผ์ •์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”.'); + return; + } + + // Transition ์•ˆ์—์„œ ๋น„๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘ + startTransition(() => { + patchUserInfo(userInfo); // ์œ ์ € ์ •๋ณด ํŒจ์น˜ + localStorage.removeItem('userInfo'); // ๋กœ์ปฌ ์Šคํ† ๋ฆฌ์ง€์—์„œ ์œ ์ € ์ •๋ณด ์ œ๊ฑฐ + }); + }; + + return ( + +
+ + ) => handleNameChange(e.target.value)} + /> + + + + + + {isPending ? '์ˆ˜์ • ์ค‘...' : '์ˆ˜์ •ํ•˜๊ธฐ'} + + + +
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index 069e358..1a64e8c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,24 +1,29 @@ -import { Box, Flex, Text } from '@radix-ui/themes'; +import { Box } from '@radix-ui/themes'; import Image from 'next/image'; +import Landing from '@/components/landing/Landing'; export default function Page() { - const rand = Math.floor(Math.random() * 3); + // const rand = Math.floor(Math.random() * 3); return ( - - -
- ์ด์Šคํ„ฐ์—๊ทธ -
- - ์—…๋ฐ์ดํŠธ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค ^^ - -
+ + + + // + // + //
+ // ์ด์Šคํ„ฐ์—๊ทธ + //
+ // + // ์—…๋ฐ์ดํŠธ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค ^^ + // + //
+ //
); } diff --git a/app/ranking/page.tsx b/app/ranking/page.tsx index 3970ea0..fb9663c 100644 --- a/app/ranking/page.tsx +++ b/app/ranking/page.tsx @@ -4,7 +4,7 @@ import TopRankings from '@/components/ranking/topRanking'; import FullRankingList from '@/components/ranking/fullRankingList'; import { Box, Flex } from '@radix-ui/themes'; import styles from './page.module.css'; -import rankingImg from '@/assets/icons/ranking_profile_img.png'; +import rankingImg from '@/public/imgs/ranking/ranking_profile_img.png'; import { getMemberRanking } from '@/apis/rankingApi'; import { Student, ApiResponse } from '@/types/rankingType'; diff --git a/assets/icons/ranking_1.png b/assets/icons/ranking_1.png deleted file mode 100644 index 5673bc3..0000000 Binary files a/assets/icons/ranking_1.png and /dev/null differ diff --git a/assets/icons/ranking_2.png b/assets/icons/ranking_2.png deleted file mode 100644 index a8cf46f..0000000 Binary files a/assets/icons/ranking_2.png and /dev/null differ diff --git a/assets/icons/ranking_3.png b/assets/icons/ranking_3.png deleted file mode 100644 index abcfe68..0000000 Binary files a/assets/icons/ranking_3.png and /dev/null differ diff --git a/assets/icons/subject_edit_close_btn.png b/assets/icons/subject_edit_close_btn.png deleted file mode 100644 index 8a7a587..0000000 Binary files a/assets/icons/subject_edit_close_btn.png and /dev/null differ diff --git a/components/auth/ClassRadioGroup.tsx b/components/auth/ClassRadioGroup.tsx new file mode 100644 index 0000000..86704c7 --- /dev/null +++ b/components/auth/ClassRadioGroup.tsx @@ -0,0 +1,39 @@ +import { Flex, RadioGroup, Text } from '@radix-ui/themes'; + +interface ClassRadioGroupProps { + course: string; + onChange: (value: string) => void; +} + +export default function ClassRadioGroup({ course, onChange }: ClassRadioGroupProps) { + return ( + <> + + ํด๋ž˜์Šค + + + + + AI ์—”์ง€๋‹ˆ์–ด๋ง + + + ํด๋ผ์šฐ๋“œ ์—”์ง€๋‹ˆ์–ด๋ง + + + ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ๊ฐœ๋ฐœ + + + + + ); +} diff --git a/components/auth/InputField.tsx b/components/auth/InputField.tsx index 1a49982..ed3ace8 100644 --- a/components/auth/InputField.tsx +++ b/components/auth/InputField.tsx @@ -11,13 +11,13 @@ interface InputFieldProps { export default function InputField({ label, id, type = 'text', placeholder, value, onChange }: InputFieldProps) { return ( - + <> {label} - + ); } diff --git a/components/auth/JoinForm.tsx b/components/auth/JoinForm.tsx index b739d81..809f26d 100644 --- a/components/auth/JoinForm.tsx +++ b/components/auth/JoinForm.tsx @@ -7,6 +7,7 @@ import AuthFormLayout from './AuthFormLayout'; import { useJoinStore } from '@/stores/authStore'; import { useState } from 'react'; import { isValidPassword } from '@/utils/validateUtils'; +import ClassRadioGroup from './ClassRadioGroup'; interface JoinFormProps { onJoin: (e: React.FormEvent) => void; @@ -78,84 +79,61 @@ export default function JoinForm({ onJoin }: JoinFormProps) {
- ) => handleEmailChange(e.target.value)} - /> + + ) => handleEmailChange(e.target.value)} + /> + {error.email && ( ์ด๋ฉ”์ผ ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. )} - ) => handleNameChange(e.target.value)} - /> - ) => handlePasswordChange(e.target.value)} - /> + + ) => handleNameChange(e.target.value)} + /> + + + ) => handlePasswordChange(e.target.value)} + /> + {error.password && ( ์˜์–ด, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ํฌํ•จํ•œ ์ตœ์†Œ 8์ž ์ด์ƒ, ์ตœ๋Œ€ 20์ž ์ดํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. )} - ) => handlePasswordCheckChange(e.target.value)} - /> + + ) => handlePasswordCheckChange(e.target.value)} + /> + {error.passwordCheck && ( ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. )} - - ํด๋ž˜์Šค - - - - - AI ์—”์ง€๋‹ˆ์–ด๋ง - - - ํด๋ผ์šฐ๋“œ ์—”์ง€๋‹ˆ์–ด๋ง - - - ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ๊ฐœ๋ฐœ - - - +
diff --git a/components/auth/LoginForm.tsx b/components/auth/LoginForm.tsx index 50a8f0b..d0fb11a 100644 --- a/components/auth/LoginForm.tsx +++ b/components/auth/LoginForm.tsx @@ -40,21 +40,25 @@ export default function LoginForm({ onLogin }: LoginFormProps) {
- setEmail(e.target.value)} - /> - setPassword(e.target.value)} - /> + + setEmail(e.target.value)} + /> + + + setPassword(e.target.value)} + /> +
diff --git a/components/common/CommonButton.tsx b/components/common/CommonButton.tsx index c3e332b..683784d 100644 --- a/components/common/CommonButton.tsx +++ b/components/common/CommonButton.tsx @@ -6,19 +6,20 @@ import Link from 'next/link'; interface CommonButtonProps { type: 'submit' | 'button' | 'link'; href?: string; + disabled?: boolean; style: 'dark_purple' | 'light_purple' | 'dark_gray' | 'light_gray'; children: React.ReactNode; } -export default function CommonButton({ type, href, style, children }: CommonButtonProps) { +export default function CommonButton({ type, href, disabled, style, children }: CommonButtonProps) { return ( <> {type == 'submit' ? ( - ) : type == 'button' ? ( - ) : ( diff --git a/components/common/Header/Header.module.css b/components/common/Header/Header.module.css index 6190e0e..16bdd9f 100644 --- a/components/common/Header/Header.module.css +++ b/components/common/Header/Header.module.css @@ -2,21 +2,34 @@ background-color: #fff; border-bottom: 2px solid var(--main-color-3); } + +.header_landing { + position: sticky; + top: 0; + border-bottom: none; + background-color: #fff; + transition: background-color 0.3s ease; + z-index: 100; +} + .header_inner { position: relative; } + .logo { position: absolute; left: 0; top: 50%; transform: translateY(-50%); } + .logo p { font-size: 28px; font-weight: 300; line-height: 1; color: var(--main-color-3); } + .logo p i { display: inline-block; font-weight: 700; @@ -26,9 +39,11 @@ height: 75px; line-height: 75px; } + .gnb ul li { position: relative; } + .gnb ul li:before { content: ''; display: block; @@ -41,6 +56,7 @@ border-radius: 100%; background-color: var(--main-color-2); } + .gnb ul li:first-of-type:before { display: none; } @@ -51,19 +67,23 @@ top: 50%; transform: translateY(-50%); } + .user_name { background-size: cover; background-position: 50% 50%; border-radius: 100%; overflow: hidden; } + .user_txt { line-height: 1; } + .user_txt strong { display: block; font-weight: 500; } + .user_txt p { line-height: 1; color: #bab2bb; @@ -83,9 +103,11 @@ line-height: 60px; gap: 32px; } + .gnb ul li:before { left: -17px; } + .gnb ul li p { font-size: 16px; } @@ -94,9 +116,11 @@ width: 30px; height: 30px; } + .user_txt strong { font-size: 14px; } + .user_txt p { font-size: 12px; } @@ -109,13 +133,16 @@ height: 60px; text-align: center; } + .logo p { font-size: 20px; line-height: 60px !important; } + .gnb { display: none; } + .user_info { display: none; } diff --git a/components/common/Header/Header.tsx b/components/common/Header/Header.tsx index eda4efb..6064033 100644 --- a/components/common/Header/Header.tsx +++ b/components/common/Header/Header.tsx @@ -4,22 +4,49 @@ import Link from 'next/link'; import { Box, Flex, Heading, Strong, Text } from '@radix-ui/themes'; import styles from './Header.module.css'; import HeaderNav from './HeaderNav'; -import rankingImg from '@/assets/icons/ranking_profile_img.png'; +import rankingImg from '@/public/imgs/ranking/ranking_profile_img.png'; import Image from 'next/image'; import { useUserInfoStore } from '@/stores/memberStore'; import { useSession } from 'next-auth/react'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { getUserInfo } from '@/apis/memberApi'; +import { usePathname } from 'next/navigation'; +import { gsap } from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); export default function Header() { const { userInfo, setUserInfo } = useUserInfoStore(); - const { data: session, update } = useSession(); + const { data: session } = useSession(); const accessToken = session?.user?.accessToken; const refreshToken = session?.user?.refreshToken; + // ๋ฃจํŠธ ๊ฒฝ๋กœ์—์„œ๋งŒ ํŠน์ • ์ด๋ฒคํŠธ ์ถ”๊ฐ€ + const pathname = usePathname(); + const isRootPath = pathname === '/'; + + useEffect(() => { + if (isRootPath) { + const header = document.querySelector(`.${styles.header_landing}`); + + gsap.to(header, { + backgroundColor: 'rgba(255, 255, 255, 0.5)', + backdropFilter: 'blur(8px)', + scrollTrigger: { + trigger: document.body, + start: '2% 0%', + end: '10% 20%', + scrub: true, + markers: false, + }, + }); + } + }, [isRootPath]); + // update(); useEffect(() => { @@ -42,11 +69,11 @@ export default function Header() { } } })(); - }, [accessToken, refreshToken, setUserInfo]); + }); return ( -
+
diff --git a/components/landing/Landing.module.css b/components/landing/Landing.module.css new file mode 100644 index 0000000..701cfd2 --- /dev/null +++ b/components/landing/Landing.module.css @@ -0,0 +1,295 @@ +@font-face { + font-family: 'yg-jalnan'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_four@1.2/JalnanOTF00.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'SUIT-Regular'; + src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_suit@1.0/SUIT-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + +.landing_wrap { + position: relative; + width: 100%; + height: 100%; + top: 0; +} + +/* ์ธํŠธ๋กœ ์„น์…˜ */ +.intro { + position: relative; + top: 0; + left: 0; + height: 95vh; + background: linear-gradient(to bottom, #fff 3%, #f9f9ff 10%, #e7dcfd 50%, #d0d4ff 100%); +} + +.intro_section { + display: flex; + justify-content: space-around; + align-items: center; +} + +.timer_icon_wrap { + width: 35%; + height: auto; +} + +.timer_icon { + animation: swing 2s infinite ease-in-out; +} + +@keyframes swing { + 0% { + transform: translateX(-10px) rotate(-5deg); + } + + 50% { + transform: translateX(10px) rotate(5deg); + } + + 100% { + transform: translateX(-10px) rotate(-5deg); + } +} + +.intro_text_wrap { + padding: 0px 50px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.intro_text { + font: 90px 'yg-jalnan'; + color: rgb(68, 68, 68); +} + +.intro_btn_wrap { + margin-top: 30px; +} + +.intro_text_detail { + font-family: 'SUIT-Regular'; + color: rgb(68, 68, 68); +} + +.intro_btn { + font-weight: bold; + background-color: white; + padding: 12px 15px; + margin-right: 10px; + font-size: 16px; + cursor: pointer; + transition: + background-color 0.3s ease, + color 0.3s ease; + border-radius: 8px; +} + +.intro_btn:hover { + background-color: var(--main-color-2); + color: white; +} + +.inquiry_icon { + width: 27px; + padding-right: 8px; +} + +.notion_icon { + width: 25px; + padding-right: 10px; +} + +.intro_arrow_wrap { + position: absolute; + bottom: 100px; + left: 50%; + transform: translateX(-50%); +} + +.arrow { + width: 50px; + animation: blink 1.5s infinite ease-in-out; +} + +@keyframes blink { + 0% { + opacity: 1; + transform: translateY(0); + } + + 50% { + opacity: 0.4; + transform: translateY(10px); + } + + 100% { + opacity: 1; + transform: translateY(0); + } +} + +/* ๋ถ€๊ฐ€์„ค๋ช… */ + +.add_explane { + width: 100%; + background-color: #fdfdfd; + text-align: center; + padding: 150px 0px; +} + +.add_explane_text { + font-size: 30px; + line-height: 55px; + font-weight: 500; +} + +/* ํƒ€์ด๋จธ์†Œ๊ฐœ์„น์…˜ */ + +/* animation ์„ค์ •*/ +.timer_detail_wrap, +.timer_img_wrap, +.ranking_detail_wrap, +.ranking_img_wrap, +.cal_detail_wrap, +.cal_img_wrap { + opacity: 0; + transform: translateY(150px); + transition: all 0.5s; +} + +.timer_detail, +.ranking_detail, +.cal_detail { + display: block; + margin-top: 15px; +} + +.timer_detail_wrap { + padding: 100px 0px 50px 0px; + text-align: center; +} + +.timer_title { + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; +} + +.timer_title_icon { + width: 35px; + padding-right: 10px; +} + +.timer_img_wrap { + padding-bottom: 100px; +} + +.timer_img_desktop, +.timer_img_mobile { + position: relative; + width: 45%; + border-radius: 20px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); +} + +.timer_img_desktop { + left: 50%; + transform: translate(-50%, 0%); +} + +.timer_img_mobile { + width: 15%; + left: 25%; + transform: translate(-30%, 20%); +} + +/* ๋žญํ‚น ์†Œ๊ฐœ, ๊ธฐ๋ก ํ™•์ธ*/ + +.ranking, +.cal { + width: 100%; + display: flex; + justify-content: center; +} + +.ranking_inner, +.cal_inner { + width: 80%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; +} + +.ranking_img_wrap, +.cal_img_wrap { + width: 50%; + display: flex; +} + +.ranking_img_wrap { + justify-content: flex-end; +} + +.cal_img_wrap { + justify-content: flex-start; +} + +.ranking_img_desktop, +.cal_img_desktop { + border-radius: 20px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); + width: 80%; +} + +.cal_img_desktop { + width: 85%; +} + +.ranking_title { + display: flex; + align-items: center; + font-weight: bold; +} + +.cal_title { + font-weight: bold; +} + +.cal_title_icon { + width: 42px; +} + +.cal_detail_wrap { + padding-left: 100px; + text-align: left; +} + +.cal_title_icon { + height: 40px; + padding-right: 5px; +} + +/* ๊ธฐ๋กํ™•์ธ */ + +.cal_img_desktop, +.cal_record_img { + position: relative; + border-radius: 20px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); +} + +.cal_record_img { + width: 50%; + height: 80%; + right: 20%; + transform: translate(-40%, 60%); +} diff --git a/components/landing/Landing.tsx b/components/landing/Landing.tsx new file mode 100644 index 0000000..a9bd3db --- /dev/null +++ b/components/landing/Landing.tsx @@ -0,0 +1,216 @@ +'use client'; +import { Box, Text } from '@radix-ui/themes'; +import styles from './Landing.module.css'; +import { useEffect, useRef } from 'react'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; +import Image from 'next/image'; + +gsap.registerPlugin(ScrollTrigger); + +export default function Landing() { + const timerRef = useRef(null); + const rankingRef = useRef(null); + const calRef = useRef(null); + + const timerDetailRef = useRef(null); + const timerImgWrapRef = useRef(null); + const rankingDetailRef = useRef(null); + const rankingImgWrapRef = useRef(null); + const calDetailRef = useRef(null); + const calImgWrapRef = useRef(null); + + useEffect(() => { + const commonGsapAni = { + y: 0, + opacity: 1, + ease: 'circ.out', + }; + + const tl = gsap.timeline({ + scrollTrigger: { + trigger: timerRef.current, + start: '0% 80%', + end: '50% 50%', + scrub: true, + markers: false, + }, + }); + + tl.to(timerDetailRef.current, commonGsapAni).to(timerImgWrapRef.current, commonGsapAni); + + const t2 = gsap.timeline({ + scrollTrigger: { + trigger: rankingRef.current, + start: '60% bottom', + end: 'bottom 70%', + scrub: true, + markers: false, + }, + }); + + t2.to(rankingDetailRef.current, commonGsapAni, 0).to(rankingImgWrapRef.current, commonGsapAni, 0); + + const t3 = gsap.timeline({ + scrollTrigger: { + trigger: calRef.current, + start: '0% 50%', + end: '50% 30%', + scrub: true, + markers: false, + }, + }); + + t3.to(calDetailRef.current, commonGsapAni, 0).to(calImgWrapRef.current, commonGsapAni, 0); + }, []); + + return ( + +
+
+
+ ๋žœ๋”ฉํƒ€์ด๋จธ์•„์ด์ฝ˜ +
+
+ ์šฐํ–‰์‹œ + + ์šฐ๋ฆฌ๋“ค์˜ ํ–‰๋ณตํ•œ ์‹œ๊ฐ„์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜์ž! +
+ ์‰ฝ๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ณต๋ถ€ ๊ธฐ๋ก์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. +
+ +
+
+
+ ์Šคํฌ๋กค ์œ ๋„ ์•„์ด์ฝ˜ +
+
+
+ + ์‹œ๊ฐ„ ์ธก์ •๋ถ€ํ„ฐ ๊ณผ๋ชฉ ์„ ํƒ, ๋žญํ‚น ํ™•์ธ๊นŒ์ง€ ํ•œ ๋ฒˆ์—!
+ ๊ต์œก์ƒ์˜ ํ•™์Šต ์‹œ๊ฐ„์„ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค,
+ ์šฐํ–‰์‹œ์™€ ํ•จ๊ป˜๋ผ๋ฉด ์„ฑ์žฅ์„ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
+
+ {/* ํƒ€์ด๋จธ ์†Œ๊ฐœ ์„น์…˜ */} +
+
+ + ํƒ€์ด๋จธ์•„์ด์ฝ˜ + ๊ณต๋ถ€์‹œ์ž‘ + + + ๋‚ด๊ฐ€ ์„ ํƒํ•œ ๊ณผ๋ชฉ์œผ๋กœ ๊ณต๋ถ€ ์‹œ๊ฐ„์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.
+ ์–ธ์ œ ์–ด๋””์„œ๋“  ๊ณต๋ถ€๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ์‹œ๊ฐ„์„ ๊ธฐ๋กํ•ด๋ณด์„ธ์š”. +
+
+
+ ๋žœ๋”ฉํƒ€์ด๋จธ๋ฐ์Šคํฌํƒ‘ + ๋žœ๋”ฉํƒ€์ด๋จธ๋ชจ๋ฐ”์ผ +
+
+ {/* ์ˆœ์œ„์กฐํšŒ ์†Œ๊ฐœ ์„น์…˜ */} +
+
+
+ + ํŠธ๋กœํ”ผ์•„์ด์ฝ˜ + ์ˆœ์œ„์กฐํšŒ +
+
+ + ์ผ, ์ฃผ, ์›”๋ณ„๋กœ ๋‚˜์˜ ํ•™์Šต ์ˆœ์œ„๋ฅผ ํ™•์ธํ•˜๊ณ ,
+ ๋‹ค๋ฅธ ๋ฐ˜ ํ•™์ƒ๋“ค๊ณผ ๊ฒฝ์Ÿํ•  ์ˆ˜ ์žˆ์–ด์š”.
+ ๋งค์ผ ๊พธ์ค€ํžˆ ๊ณต๋ถ€ํ•œ ์‹œ๊ฐ„๋งŒํผ ๋†’์€ ์ˆœ์œ„์— ์˜ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! +
+
+
+ ๋žœ๋”ฉ๋žญํ‚น๋ฐ์Šคํฌํƒ‘ +
+
+
+ {/* ๊ธฐ๋กํ™•์ธ ์†Œ๊ฐœ ์„น์…˜ */} +
+
+
+ ๋žœ๋”ฉ์บ˜๋ฆฐ๋”๋ชจ๋‹ฌ + ๋žœ๋”ฉ์บ˜๋ฆฐ๋”๊ธฐ๋กํ™•์ธ +
+
+ + ํŠธ๋กœํ”ผ์•„์ด์ฝ˜ + ๊ธฐ๋กํ™•์ธ +
+
+ + ํ•œ๋ˆˆ์— ๋‚˜์˜ ํ•™์Šต ๊ธฐ๋ก์„ ํ™•์ธํ•ด๋ณด์„ธ์š”. +
+ ์ผ๋ณ„ ๊ณต๋ถ€ ์‹œ๊ฐ„์„ ๋‹ฌ๋ ฅ์— ํ‘œ์‹œํ•˜๊ณ ,
+ ๊ฐ ๋‚ ์งœ์— ๊ณต๋ถ€ํ•œ ๊ณผ๋ชฉ์„ ์‰ฝ๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”. +
+ ํ•ด๋‹น ๋‚ ์งœ๋ฅผ ํด๋ฆญํ•˜๋ฉด,
+ ๋ชจ๋‹ฌ์ฐฝ์—์„œ ๋‚˜๋งŒ์˜ ํšŒ๊ณ ๋ก์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
+
+
+
+
+ ); +} diff --git a/components/mypage/MypageTabMenu.tsx b/components/mypage/MypageTabMenu.tsx index e54bc6f..27b28e4 100644 --- a/components/mypage/MypageTabMenu.tsx +++ b/components/mypage/MypageTabMenu.tsx @@ -18,6 +18,7 @@ export default function MypageTabMenu() { { link: '/mypage', title: '๋‚ด ๋ฐฐ์ง€ ์กฐํšŒ' }, { link: '/mypage/subjectedit', title: '๊ณผ๋ชฉ ํŽธ์ง‘' }, { link: '/mypage/pwupdate', title: '๋น„๋ฐ€๋ฒˆํ˜ธ ์ˆ˜์ •' }, + { link: '/mypage/infoupdate', title: 'ํšŒ์›์ •๋ณด ์ˆ˜์ •' }, ]; const [pathTabTitle] = tabMenu.filter((tab) => { diff --git a/components/mypage/PwUpdateForm.tsx b/components/mypage/PwUpdateForm.tsx index 429cc9d..f544d46 100644 --- a/components/mypage/PwUpdateForm.tsx +++ b/components/mypage/PwUpdateForm.tsx @@ -66,7 +66,7 @@ export default function PwUpdateForm({ onUpdate }: IUpdateForm) { }; return ( - + diff --git a/components/mypage/UserProfile.module.css b/components/mypage/UserProfile.module.css index 51da37e..410326b 100644 --- a/components/mypage/UserProfile.module.css +++ b/components/mypage/UserProfile.module.css @@ -2,17 +2,22 @@ font-size: 0; text-align: center; } + .img_box { position: relative; display: inline-block; } + .back_img { background-repeat: no-repeat; background-position: 50% 50%; border-radius: 100%; background-size: cover; overflow: hidden; + width: 180px; + height: 180px; } + .back_img span { width: 180px; height: 180px; @@ -21,6 +26,7 @@ .btn_file input[type='file'] { display: none; } + .btn_file button { position: absolute; right: 0px; diff --git a/components/ranking/fullRankingList.tsx b/components/ranking/fullRankingList.tsx index b81d058..4277a8e 100644 --- a/components/ranking/fullRankingList.tsx +++ b/components/ranking/fullRankingList.tsx @@ -4,7 +4,7 @@ import Image from 'next/image'; import styles from './fullRankingList.module.css'; import InfiniteScroll from 'react-infinite-scroller'; import { Student } from '@/types/rankingType'; -import rankingImg from '@/assets/icons/ranking_profile_img.png'; +import rankingImg from '@/public/imgs/ranking/ranking_profile_img.png'; import { formatTime } from '@/utils/formatTimeUtils'; interface FullRankingListProps { diff --git a/components/ranking/topRanking.tsx b/components/ranking/topRanking.tsx index 7d1abd1..44abd52 100644 --- a/components/ranking/topRanking.tsx +++ b/components/ranking/topRanking.tsx @@ -4,7 +4,7 @@ import styles from './topRankingList.module.css'; import rankingOne from '@/public/imgs/medal_first_on.svg'; import rankingTwo from '@/public/imgs/medal_second_on.svg'; import rankingThird from '@/public/imgs/medal_third_on.svg'; -import rankingImg from '@/assets/icons/ranking_profile_img.png'; +import rankingImg from '@/public/imgs/ranking/ranking_profile_img.png'; import Image from 'next/image'; import { Student } from '@/types/rankingType'; diff --git a/middleware.ts b/middleware.ts index 1a29c67..21285a1 100644 --- a/middleware.ts +++ b/middleware.ts @@ -31,5 +31,5 @@ export default async function middleware(request: NextRequest, response: NextRes // ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์ ์šฉ๋  ๊ฒฝ๋กœ ์„ค์ • export const config = { - matcher: ['/', '/ranking', '/record', '/study', '/mypage'], + matcher: ['/', '/ranking', '/record', '/study', '/mypage/:path*'], }; diff --git a/package-lock.json b/package-lock.json index dc16933..49913b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.1", "@vercel/analytics": "^1.3.1", + "gsap": "^3.12.5", "js-cookie": "^3.0.5", "lodash": "^4.17.21", "next": "14.2.5", @@ -8018,6 +8019,11 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/gsap": { + "version": "3.12.5", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.5.tgz", + "integrity": "sha512-srBfnk4n+Oe/ZnMIOXt3gT605BX9x5+rh/prT2F1SsNJsU1XuMiP0E2aptW481OnonOGACZWBqseH5Z7csHxhQ==" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", diff --git a/package.json b/package.json index e0599fa..d291a80 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^3.1.1", "@vercel/analytics": "^1.3.1", + "gsap": "^3.12.5", "js-cookie": "^3.0.5", "lodash": "^4.17.21", "next": "14.2.5", diff --git a/public/imgs/landing/arrow.png b/public/imgs/landing/arrow.png new file mode 100644 index 0000000..9edef51 Binary files /dev/null and b/public/imgs/landing/arrow.png differ diff --git a/public/imgs/landing/cal.png b/public/imgs/landing/cal.png new file mode 100644 index 0000000..e5bb1f3 Binary files /dev/null and b/public/imgs/landing/cal.png differ diff --git a/public/imgs/landing/cal_timer.png b/public/imgs/landing/cal_timer.png new file mode 100644 index 0000000..51cda5a Binary files /dev/null and b/public/imgs/landing/cal_timer.png differ diff --git a/public/imgs/landing/clock.png b/public/imgs/landing/clock.png new file mode 100644 index 0000000..71837c7 Binary files /dev/null and b/public/imgs/landing/clock.png differ diff --git a/public/imgs/landing/inquiry.png b/public/imgs/landing/inquiry.png new file mode 100644 index 0000000..5496934 Binary files /dev/null and b/public/imgs/landing/inquiry.png differ diff --git a/public/imgs/landing/landing_cal_desktop.png b/public/imgs/landing/landing_cal_desktop.png new file mode 100644 index 0000000..8c96f8e Binary files /dev/null and b/public/imgs/landing/landing_cal_desktop.png differ diff --git a/public/imgs/landing/landing_cal_record.png b/public/imgs/landing/landing_cal_record.png new file mode 100644 index 0000000..d14c4a3 Binary files /dev/null and b/public/imgs/landing/landing_cal_record.png differ diff --git a/public/imgs/landing/landing_ranking_desktop.png b/public/imgs/landing/landing_ranking_desktop.png new file mode 100644 index 0000000..9ad1dc4 Binary files /dev/null and b/public/imgs/landing/landing_ranking_desktop.png differ diff --git a/public/imgs/landing/landing_timer.png b/public/imgs/landing/landing_timer.png new file mode 100644 index 0000000..066072f Binary files /dev/null and b/public/imgs/landing/landing_timer.png differ diff --git a/public/imgs/landing/landing_timer_desktop.png b/public/imgs/landing/landing_timer_desktop.png new file mode 100644 index 0000000..0a0ae2f Binary files /dev/null and b/public/imgs/landing/landing_timer_desktop.png differ diff --git a/public/imgs/landing/landing_timer_icon.png b/public/imgs/landing/landing_timer_icon.png new file mode 100644 index 0000000..95d792d Binary files /dev/null and b/public/imgs/landing/landing_timer_icon.png differ diff --git a/public/imgs/landing/landing_timer_mobile.png b/public/imgs/landing/landing_timer_mobile.png new file mode 100644 index 0000000..288866b Binary files /dev/null and b/public/imgs/landing/landing_timer_mobile.png differ diff --git a/public/imgs/landing/notion.png b/public/imgs/landing/notion.png new file mode 100644 index 0000000..2ebabb7 Binary files /dev/null and b/public/imgs/landing/notion.png differ diff --git a/public/imgs/landing/timer_caleander.png b/public/imgs/landing/timer_caleander.png new file mode 100644 index 0000000..a77a140 Binary files /dev/null and b/public/imgs/landing/timer_caleander.png differ diff --git a/public/imgs/landing/trophy.png b/public/imgs/landing/trophy.png new file mode 100644 index 0000000..3a74c44 Binary files /dev/null and b/public/imgs/landing/trophy.png differ diff --git a/assets/icons/profile_img_file.png b/public/imgs/mypage/profile_img_file.png similarity index 100% rename from assets/icons/profile_img_file.png rename to public/imgs/mypage/profile_img_file.png diff --git a/assets/icons/ranking_profile_img.png b/public/imgs/ranking/ranking_profile_img.png similarity index 100% rename from assets/icons/ranking_profile_img.png rename to public/imgs/ranking/ranking_profile_img.png diff --git a/assets/icons/subject_delete.png b/public/imgs/subject/subject_delete.png similarity index 100% rename from assets/icons/subject_delete.png rename to public/imgs/subject/subject_delete.png diff --git a/assets/icons/subject_edit_back.png b/public/imgs/subject/subject_edit_back.png similarity index 100% rename from assets/icons/subject_edit_back.png rename to public/imgs/subject/subject_edit_back.png diff --git a/public/timeout.png b/public/timeout.png index 7803f0d..558f239 100644 Binary files a/public/timeout.png and b/public/timeout.png differ diff --git a/stores/authStore.ts b/stores/authStore.ts index d99d7b6..33fadfc 100644 --- a/stores/authStore.ts +++ b/stores/authStore.ts @@ -5,11 +5,13 @@ interface joinState { name: string; password: string; passwordCheck: string; + image: File | null; course: string; setEmail: (email: string) => void; setName: (name: string) => void; setPassword: (password: string) => void; setPasswordCheck: (passwordCheck: string) => void; + setImage: (image: File | null) => void; setCourse: (course: string) => void; setAllEmpty: () => void; } @@ -19,11 +21,13 @@ export const useJoinStore = create((set) => ({ name: '', password: '', passwordCheck: '', + image: null, course: '', setEmail: (email: string) => set({ email }), setName: (name: string) => set({ name }), setPassword: (password: string) => set({ password }), setPasswordCheck: (passwordCheck: string) => set({ passwordCheck }), + setImage: (image: File | null) => set({ image }), setCourse: (course: string) => set({ course }), - setAllEmpty: () => set({ email: '', name: '', password: '', passwordCheck: '', course: '' }), + setAllEmpty: () => set({ email: '', name: '', password: '', passwordCheck: '', image: null, course: '' }), }));