diff --git a/README.md b/README.md index 53da700f9..ccf11ca28 100644 --- a/README.md +++ b/README.md @@ -1 +1,60 @@ -# ๋•…์ฝฉ ๐Ÿฅœ +
+

๋•…์ฝฉ ๋ฐ”๋กœ๊ฐ€๊ธฐ

+ +

์‹ฌ์‹ฌํ’€์ด ๋•…์ฝฉ์ฒ˜๋Ÿผ ๊ฐ€๋ฒผ์šด ์ฃผ์ œ๋กœ ์นœ๊ตฌ๋“ค๊ณผ ์ฆ๊ธฐ๋Š” ๋‹จ์ฒด ๋Œ€ํ™”์ฃผ์ œ ์ œ๊ณต ์„œ๋น„์Šค ๐Ÿฅœ

+
+ +# ์„œ๋น„์Šค ์†Œ๊ฐœ + +`์‹ฌ์‹ฌํ’€์ด ๋•…์ฝฉ` ์ด๋ผ๋Š” ๋ง์„ ๋“ค์–ด๋ณด์‹  ์  ์žˆ์œผ์‹ ๊ฐ€์š”? ์ด ํ‘œํ˜„์€ ์ผ๋ถ€๋Ÿฌ๋ผ๋„ ๋จน๋Š” ์ผ์„ ๋งŒ๋“ค์–ด ๋ฌด๋ฃŒํ•จ์„ ์žŠ์œผ๋ ค๋Š” ์‹ฌ๋ฆฌ์—์„œ ์œ ๋ž˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ, ๋•…์ฝฉ ์„œ๋น„์Šค๋Š” **๊ฐ€๋ณ๊ฒŒ ๋Œ€ํ™”๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ์ด์–ด๋‚˜๊ฐ€๊ณ ์ž ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค.** + +ํ•จ๊ป˜ ๋ชจ์ธ ์ž๋ฆฌ์—์„œ ๋Œ€ํ™” ์ฃผ์ œ๊ฐ€ ๋ถ€์กฑํ•˜์ง„ ์•Š์œผ์‹ ๊ฐ€์š”? ์นœํ•ด์ง€๊ณ  ์‹ถ์€ ์‚ฌ๋žŒ๋“ค๊ณผ ๋” ๋งŽ์€ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ„๊ณ  ์‹ถ์ง€๋Š” ์•Š์œผ์‹ ๊ฐ€์š”? +๋Œ€ํ™”๋ฅผ ํ•˜๋‹ค๋ณด๋ฉด ์–ด์ƒ‰ํ•œ ์นจ๋ฌต์ด ํ๋ฅด๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋•…์ฝฉ์€ ์Œ์‹, MBTI, ์—ฐ์• , ๋งŒ์•ฝ์— ๋“ฑ `๋‹ค์–‘ํ•œ ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋Œ€ํ™” ์ฃผ์ œ๋ฅผ ์ œ๊ณต` ํ•˜์—ฌ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. + +> [์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์งˆ๋ฌธ ์˜ˆ์‹œ] +> +> `์Œ์‹` : ๊ฐœ๊ตฌ๋ฆฌ ๋ง› ์ดˆ์ฝœ๋ฆฟ vs ์ดˆ์ฝœ๋ฆฟ ๋ง› ๊ฐœ๊ตฌ๋ฆฌ +> +> `MBTI` : ๋‚ด๊ฐ€ '์ด๋ฒˆ์ฃผ ํ† ์š”์ผ์— ๋ญํ•ด?'๋ผ๊ณ  ๋ฌผ์–ด๋ณผ ๋•Œ๋Š” : ์•ฝ์†์„ ์žก์œผ๋ ค๊ณ  ๋ฌผ์–ด๋ณธ๋‹ค vs ๋‹จ์ง€ ๋ญํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ด์„œ ๋ฌผ์–ด๋ณธ๋‹ค +> +> `์—ฐ์• ` : ์™ธ๋ชจ ๋นผ๊ณ  ๋ชจ๋“  ๊ฒƒ์ด ์•ˆ ๋งž๋Š” ์• ์ธ vs ์™ธ๋ชจ ๋นผ๊ณ  ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ๋งž๋Š” ์• ์ธ +> +> `๋งŒ์•ฝ์—` : ์ „์• ์ธย ์นœ๊ตฌ๋ž‘ ์‚ฌ๊ท€๊ธฐ vsย ์นœ๊ตฌย ์ „์• ์ธ์ด๋ž‘ ์‚ฌ๊ท€๊ธฐ + +์ด ์ฃผ์ œ๋“ค์„ ํ†ตํ•ด ์„œ๋กœ์˜ ์ƒ๊ฐ์„ ๊ณต์œ ํ•˜๊ณ , ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฆ๊ฑฐ์šด ๋Œ€ํ™”๋ฅผ ์ด์–ด๋‚˜๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋•…์ฝฉ๊ณผ ํ•จ๊ป˜๋ผ๋ฉด, ๋” ์ด์ƒ ๋Œ€ํ™”์˜ ์‹œ์ž‘์„ ๊ณ ๋ฏผํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋‚˜ ์‰ฝ๊ณ  ์ฆ๊ฒ๊ฒŒ ์†Œํ†ตํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋•…์ฝฉ, **์ง€๊ธˆ ๋‹น์žฅ ํšŒ์›๊ฐ€์ž… ์—†์ด [๋•…์ฝฉ](https://ddangkong.kr/)์„ ์‚ฌ์šฉํ•ด๋ณด์„ธ์š”.** + +# ์ฃผ์š” ๊ธฐ๋Šฅ ์†Œ๊ฐœ + +### 1. ๋‹ค์–‘ํ•œ ๋Œ€ํ™” ์ฃผ์ œ ์ œ๊ณต + +![แ„ƒแ…ขแ„’แ…ชแ„Œแ…ฎแ„Œแ…ฆ_แ„Œแ…ฆแ„€แ…ฉแ†ผ](https://github.com/user-attachments/assets/cfe966ea-698f-42b6-94f5-628d4e15e0e3) + +### 2. `์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํ†ต์‹ ` ์„ ํ†ตํ•ด ์‹œ๋„๋Ÿฌ์šด ์˜คํ”„๋ผ์ธ ๊ณต๊ฐ„์—์„œ๋„ `๋ฉ€ํ‹ฐ ํ”Œ๋ ˆ์ด` ๋กœ ์žฌ๋ฐŒ๊ฒŒ ์ฆ๊ธธ ์ˆ˜ ์žˆ์Œ + +![แ„†แ…ฅแ†ฏแ„แ…ตแ„‘แ…ณแ†ฏแ„…แ…ฆแ„‹แ…ต](https://github.com/user-attachments/assets/046422a7-a389-4469-b8f0-cb72339946e9) + +### 3. ๊ฒŒ์ž„ ๋‚ด์—์„œ ๋‚˜์™€ ๊ฐ™์€ ์„ ํƒ์„ ํ•œ ์‚ฌ๋žŒ๊ณผ์˜ `๋งค์นญ๋„ ์ˆœ์œ„` ๋ฅผ ํ†ตํ•ด ๊ณต๊ฐ๋Œ€ ํ˜•์„ฑ + +![แ„†แ…ขแ„Žแ…ตแ†ผแ„ƒแ…ฉ](https://github.com/user-attachments/assets/019f0779-df57-444b-842a-37fe6d970e07) + +# ์ธํ”„๋ผ + + + +# ํŒ€์› ์†Œ๊ฐœ + +### ํ”„๋ก ํŠธ์—”๋“œ + +| | | | +| :----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------: | +| [๋งˆ๋ฃจ](https://github.com/rbgksqkr) | [์ฌ๋ฐ์ด](https://github.com/useon) | [ํฌ๋ฉ”](https://github.com/novice0840) | + +### ๋ฐฑ์—”๋“œ + +| | | | | +| :----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------: | +| [์ด๋“ ](https://github.com/PgmJun) | [์ปค์ฐฌ](https://github.com/leegwichan) | [ํƒ€์นธ](https://github.com/jhon3242) | [ํ”„๋ฆฐ](https://github.com/GIVEN53) | + +# ๊ทธ๋ผ์šด๋“œ ๋ฃฐ + +image diff --git a/backend/src/main/java/ddangkong/aop/logging/DevRequestLoggingAspect.java b/backend/src/main/java/ddangkong/aop/logging/DevRequestLoggingAspect.java new file mode 100644 index 000000000..0572cde47 --- /dev/null +++ b/backend/src/main/java/ddangkong/aop/logging/DevRequestLoggingAspect.java @@ -0,0 +1,18 @@ +package ddangkong.aop.logging; + + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Aspect +@Component +@Profile({"dev", "local"}) +public class DevRequestLoggingAspect extends RequestLoggingAspect { + @Before("allController()") + public void logController(JoinPoint joinPoint) { + super.logController(joinPoint); + } +} diff --git a/backend/src/main/java/ddangkong/aop/logging/ProdRequestLoggingAspect.java b/backend/src/main/java/ddangkong/aop/logging/ProdRequestLoggingAspect.java new file mode 100644 index 000000000..ab8f0ddb8 --- /dev/null +++ b/backend/src/main/java/ddangkong/aop/logging/ProdRequestLoggingAspect.java @@ -0,0 +1,18 @@ +package ddangkong.aop.logging; + + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Aspect +@Component +@Profile("prod") +public class ProdRequestLoggingAspect extends RequestLoggingAspect { + @Before("allControllerWithoutPolling()") + public void logController(JoinPoint joinPoint) { + super.logController(joinPoint); + } +} diff --git a/backend/src/main/java/ddangkong/aop/logging/RequestLoggingAspect.java b/backend/src/main/java/ddangkong/aop/logging/RequestLoggingAspect.java index 45836d6f6..27622ae63 100644 --- a/backend/src/main/java/ddangkong/aop/logging/RequestLoggingAspect.java +++ b/backend/src/main/java/ddangkong/aop/logging/RequestLoggingAspect.java @@ -6,19 +6,14 @@ import java.lang.reflect.Parameter; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -@Aspect -@Component @Slf4j -public class RequestLoggingAspect { +abstract class RequestLoggingAspect { @Pointcut("execution(* ddangkong.controller..*Controller.*(..))") public void allController() { @@ -32,8 +27,11 @@ public void polling() { public void allControllerWithoutPolling() { } - @Before("allController()") - public void logController(JoinPoint joinPoint) { + protected void logController(JoinPoint joinPoint) { + logRequest(joinPoint); + } + + private void logRequest(JoinPoint joinPoint) { HttpServletRequest request = getHttpServletRequest(); String uri = request.getRequestURI(); String httpMethod = request.getMethod(); diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index 37beb7652..ff7eef976 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -12,7 +12,7 @@ spring: mode: never jpa: - database-platform: org.hibernate.dialect.MySQL8Dialect + database-platform: org.hibernate.dialect.MySQLDialect hibernate: ddl-auto: none properties: diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index 8550cb1e5..e2cc0f747 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -1,5 +1,6 @@ import React from 'react'; import type { Preview } from '@storybook/react'; +import { RecoilRoot } from 'recoil'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { MemoryRouter } from 'react-router-dom'; import { Global, ThemeProvider } from '@emotion/react'; @@ -33,14 +34,14 @@ const preview: Preview = { decorators: [ (Story) => ( - - - - + + + + - - - + + + ), ], diff --git a/frontend/src/components/SelectContainer/SelectContainer.hook.ts b/frontend/src/components/SelectContainer/SelectContainer.hook.ts index 34416b9c6..f670ed445 100644 --- a/frontend/src/components/SelectContainer/SelectContainer.hook.ts +++ b/frontend/src/components/SelectContainer/SelectContainer.hook.ts @@ -3,9 +3,9 @@ import { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { fetchRoundVoteIsFinished } from '@/apis/balanceContent'; +import { POLLING_DELAY } from '@/constants/config'; import { QUERY_KEYS } from '@/constants/queryKeys'; import { ROUTES } from '@/constants/routes'; -import { POLLING_DELAY } from '@/constants/time'; interface UseRoundIsFinishedQueryProps { contentId?: number; diff --git a/frontend/src/components/Timer/Timer.hook.ts b/frontend/src/components/Timer/Timer.hook.ts index b35a09007..3696d5482 100644 --- a/frontend/src/components/Timer/Timer.hook.ts +++ b/frontend/src/components/Timer/Timer.hook.ts @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'; import { convertMsecToSecond } from './Timer.util'; -import { ONE_SECOND } from '@/constants/time'; +import { POLLING_DELAY } from '@/constants/config'; import useBalanceContentQuery from '@/hooks/useBalanceContentQuery'; const INITIAL_WIDTH = 100; @@ -32,7 +32,7 @@ const useRoundTimer = (roomId: number) => { timeout.current = setInterval(() => { setLeftRoundTime((prev) => prev - 1); setBarWidthPercent((prevWidth) => (prevWidth > 0 ? prevWidth - DECREASE_RATE : 0)); - }, ONE_SECOND); + }, POLLING_DELAY); return () => { clearInterval(timeout.current); diff --git a/frontend/src/components/Timer/Timer.util.ts b/frontend/src/components/Timer/Timer.util.ts index d4cd88a57..6d6f1727c 100644 --- a/frontend/src/components/Timer/Timer.util.ts +++ b/frontend/src/components/Timer/Timer.util.ts @@ -1,4 +1,4 @@ -import { ONE_SECOND } from '@/constants/time'; +import { POLLING_DELAY } from '@/constants/config'; export const formatLeftRoundTime = (leftRoundTime: number) => { const minutes = Math.floor(leftRoundTime / 60); @@ -11,6 +11,6 @@ export const formatLeftRoundTime = (leftRoundTime: number) => { }; export const convertMsecToSecond = (msec: number) => { - const UNIT_MSEC = ONE_SECOND; + const UNIT_MSEC = POLLING_DELAY; return msec / UNIT_MSEC; }; diff --git a/frontend/src/components/common/RoomSettingModal/RoomSettingModal.test.tsx b/frontend/src/components/common/RoomSettingModal/RoomSettingModal.test.tsx index 8f74cfc1a..90f2c94a2 100644 --- a/frontend/src/components/common/RoomSettingModal/RoomSettingModal.test.tsx +++ b/frontend/src/components/common/RoomSettingModal/RoomSettingModal.test.tsx @@ -3,7 +3,7 @@ import { userEvent } from '@testing-library/user-event'; import RoomSettingModal from './RoomSettingModal'; -import { ONE_SECOND } from '@/constants/time'; +import { POLLING_DELAY } from '@/constants/config'; import { useGetRoomInfo } from '@/hooks/useGetRoomInfo'; import ROOM_INFO from '@/mocks/data/roomInfo.json'; import { customRender, wrapper } from '@/utils/test-utils'; @@ -88,7 +88,7 @@ describe('RoomSettingModal ๋ฐฉ ์„ค์ • ๋ชจ๋‹ฌ ํ…Œ์ŠคํŠธ', () => { expect(result.current.roomSetting).toEqual(ROOM_INFO.roomSetting); }); - await clickButton(`${TIME_LIMIT / ONE_SECOND}์ดˆ`); + await clickButton(`${TIME_LIMIT / POLLING_DELAY}์ดˆ`); await clickButton('์ ์šฉ'); await waitFor(() => { diff --git a/frontend/src/components/common/RoomSettingModal/RoomSettingModal.tsx b/frontend/src/components/common/RoomSettingModal/RoomSettingModal.tsx index 007b68490..300fa7208 100644 --- a/frontend/src/components/common/RoomSettingModal/RoomSettingModal.tsx +++ b/frontend/src/components/common/RoomSettingModal/RoomSettingModal.tsx @@ -9,7 +9,7 @@ import { } from './RoomSettingModal.styled'; import Modal from '../Modal/Modal'; -import { ONE_SECOND } from '@/constants/time'; +import { POLLING_DELAY } from '@/constants/config'; const TOTAL_ROUND_LIST = [5, 7, 10]; const TIMER_PER_ROUND_LIST = [5000, 10000, 15000]; @@ -58,7 +58,7 @@ const RoomSettingModal = ({ isOpen, onClose }: RoomSettingModalProps) => { onClick={handleClickTimeLimit} value={timeLimit} > - {timeLimit / ONE_SECOND}์ดˆ + {timeLimit / POLLING_DELAY}์ดˆ ))} diff --git a/frontend/src/constants/config.ts b/frontend/src/constants/config.ts new file mode 100644 index 000000000..af490ba5f --- /dev/null +++ b/frontend/src/constants/config.ts @@ -0,0 +1,2 @@ +export const NICKNAME_MAX_LENGTH = 12; +export const POLLING_DELAY = 1000; diff --git a/frontend/src/constants/time.ts b/frontend/src/constants/time.ts deleted file mode 100644 index 379bf3026..000000000 --- a/frontend/src/constants/time.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const ONE_SECOND = 1000; - -export const POLLING_DELAY = ONE_SECOND; diff --git a/frontend/src/hooks/useGetRoomInfo.ts b/frontend/src/hooks/useGetRoomInfo.ts index 0ef16c34d..f76fa4ff7 100644 --- a/frontend/src/hooks/useGetRoomInfo.ts +++ b/frontend/src/hooks/useGetRoomInfo.ts @@ -3,9 +3,9 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { getRoomInfo } from '@/apis/room'; +import { POLLING_DELAY } from '@/constants/config'; import { QUERY_KEYS } from '@/constants/queryKeys'; import { ROUTES } from '@/constants/routes'; -import { ONE_SECOND } from '@/constants/time'; export const useGetRoomInfo = () => { const { roomId } = useParams(); @@ -18,8 +18,9 @@ export const useGetRoomInfo = () => { if (query.state.error && query.state.fetchFailureCount >= 3) { return false; } - return ONE_SECOND; + return POLLING_DELAY; }, + gcTime: 0, }); diff --git a/frontend/src/hooks/useMyGameStatusQuery.ts b/frontend/src/hooks/useMyGameStatusQuery.ts index c9e78de48..7f3b6dac1 100644 --- a/frontend/src/hooks/useMyGameStatusQuery.ts +++ b/frontend/src/hooks/useMyGameStatusQuery.ts @@ -1,8 +1,8 @@ import { useQuery } from '@tanstack/react-query'; import { checkMyGameStatus } from '@/apis/balanceContent'; +import { POLLING_DELAY } from '@/constants/config'; import { QUERY_KEYS } from '@/constants/queryKeys'; -import { ONE_SECOND } from '@/constants/time'; interface useMyGameStatusQueryProps { currentRound: number | undefined; @@ -22,7 +22,7 @@ const useMyGameStatusQuery = ({ roomId, currentRound }: useMyGameStatusQueryProp }); }, enabled: !!currentRound, - refetchInterval: ONE_SECOND, + refetchInterval: POLLING_DELAY, gcTime: 0, }); diff --git a/frontend/src/mocks/handlers/balanceContentHandler.ts b/frontend/src/mocks/handlers/balanceContentHandler.ts index 9c49c39f2..641d4acfe 100644 --- a/frontend/src/mocks/handlers/balanceContentHandler.ts +++ b/frontend/src/mocks/handlers/balanceContentHandler.ts @@ -4,7 +4,7 @@ import BALANCE_CONTENT from '../data/balanceContent.json'; import MY_GAME_STATUS from '../data/myGameStatus.json'; import ROUND_VOTE_IS_FINISHED from '../data/roundVoteIsFinished.json'; -import { ONE_SECOND } from '@/constants/time'; +import { POLLING_DELAY } from '@/constants/config'; import { MOCK_API_URL } from '@/constants/url'; import { BalanceContent } from '@/types/balanceContent'; @@ -15,10 +15,10 @@ const fetchBalanceContentHandler = () => { const fetchIsFinishedHandler = () => { setTimeout(() => { ROUND_VOTE_IS_FINISHED.isFinished = true; - }, 10 * ONE_SECOND); + }, 10 * POLLING_DELAY); setTimeout(() => { ROUND_VOTE_IS_FINISHED.isFinished = false; - }, 12 * ONE_SECOND); + }, 12 * POLLING_DELAY); return HttpResponse.json(ROUND_VOTE_IS_FINISHED); }; diff --git a/frontend/src/pages/GameResultPage/hooks/useCheckRoomReset.ts b/frontend/src/pages/GameResultPage/hooks/useCheckRoomReset.ts index 87f94e01c..0c9762398 100644 --- a/frontend/src/pages/GameResultPage/hooks/useCheckRoomReset.ts +++ b/frontend/src/pages/GameResultPage/hooks/useCheckRoomReset.ts @@ -3,9 +3,9 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { isRoomInitial } from '@/apis/room'; +import { POLLING_DELAY } from '@/constants/config'; import { QUERY_KEYS } from '@/constants/queryKeys'; import { ROUTES } from '@/constants/routes'; -import { ONE_SECOND } from '@/constants/time'; export const useIsRoomInitial = () => { const { roomId } = useParams(); @@ -13,7 +13,7 @@ export const useIsRoomInitial = () => { const { data } = useQuery({ queryKey: [QUERY_KEYS.isRoomInitial, Number(roomId)], queryFn: async () => await isRoomInitial(Number(roomId)), - refetchInterval: ONE_SECOND, + refetchInterval: POLLING_DELAY, gcTime: 0, }); diff --git a/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.styled.ts b/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.styled.ts new file mode 100644 index 000000000..f5055c113 --- /dev/null +++ b/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.styled.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; + +import { Theme } from '@/styles/Theme'; + +export const nicknameInputContainer = css` + display: flex; + align-items: center; + width: 100%; + height: 4.8rem; + padding: 0 1rem; + border-radius: ${Theme.borderRadius.radius10}; + + background-color: ${Theme.color.gray200}; +`; + +export const nicknameInput = css` + width: 100%; + border: 0; + + background-color: ${Theme.color.gray200}; + outline: none; +`; + +export const nicknameLengthText = css` + color: ${Theme.color.gray500}; +`; diff --git a/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.tsx b/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.tsx new file mode 100644 index 000000000..27b7693db --- /dev/null +++ b/frontend/src/pages/NicknamePage/NicknameInput/NicknameInput.tsx @@ -0,0 +1,38 @@ +import { RefObject } from 'react'; + +import useNicknameInput from './hooks/useNicknameInput'; +import { nicknameInput, nicknameInputContainer, nicknameLengthText } from './NicknameInput.styled'; +import createRandomNickname from '../createRandomNickname'; + +import { NICKNAME_MAX_LENGTH } from '@/constants/config'; + +interface NicknameInputProps { + nicknameInputRef: RefObject; + handleMakeOrEnterRoom: () => void; +} + +const NicknameInput = ({ nicknameInputRef, handleMakeOrEnterRoom }: NicknameInputProps) => { + const { nickname, handleChangeInput, handleKeyDown } = useNicknameInput({ + handleMakeOrEnterRoom, + }); + const randomNickname = createRandomNickname(); + + return ( +
+ + + {nickname.length}/{NICKNAME_MAX_LENGTH} + +
+ ); +}; + +export default NicknameInput; diff --git a/frontend/src/pages/NicknamePage/NicknameInput/hooks/useNicknameInput.ts b/frontend/src/pages/NicknamePage/NicknameInput/hooks/useNicknameInput.ts new file mode 100644 index 000000000..587a73092 --- /dev/null +++ b/frontend/src/pages/NicknamePage/NicknameInput/hooks/useNicknameInput.ts @@ -0,0 +1,27 @@ +import { ChangeEvent, useState } from 'react'; + +import { NICKNAME_MAX_LENGTH } from '@/constants/config'; + +interface UseNicknameInputProps { + handleMakeOrEnterRoom: () => void; +} + +const useNicknameInput = ({ handleMakeOrEnterRoom }: UseNicknameInputProps) => { + const [nickname, setNickname] = useState(''); + + const handleChangeInput = (e: ChangeEvent) => { + if (e.target.value.length <= NICKNAME_MAX_LENGTH) { + setNickname(e.target.value); + } + }; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + handleMakeOrEnterRoom(); + } + }; + + return { nickname, handleChangeInput, handleKeyDown }; +}; + +export default useNicknameInput; diff --git a/frontend/src/pages/NicknamePage/NicknamePage.styled.ts b/frontend/src/pages/NicknamePage/NicknamePage.styled.ts index ba430df72..073c91515 100644 --- a/frontend/src/pages/NicknamePage/NicknamePage.styled.ts +++ b/frontend/src/pages/NicknamePage/NicknamePage.styled.ts @@ -18,10 +18,15 @@ export const profileImg = css` width: 60%; `; -export const nicknameBox = css` +export const nicknameContainer = css` + display: flex; + flex-direction: column; + gap: 1.6rem; width: 26.8rem; margin: 2rem 0; +`; +export const nicknameTitle = css` font-weight: 600; font-size: 1.6rem; `; @@ -44,6 +49,11 @@ export const nicknameInput = css` background-color: ${Theme.color.gray200}; outline: none; `; + +export const nicknameLengthText = css` + color: ${Theme.color.gray500}; +`; + export const noVoteTextContainer = css` display: flex; flex-direction: column; @@ -61,4 +71,4 @@ export const noVoteText = css` export const angryImage = css` width: 16rem; height: 14rem; -`; \ No newline at end of file +`; diff --git a/frontend/src/pages/NicknamePage/NicknamePage.tsx b/frontend/src/pages/NicknamePage/NicknamePage.tsx index 475ff1fc7..98fe45c7d 100644 --- a/frontend/src/pages/NicknamePage/NicknamePage.tsx +++ b/frontend/src/pages/NicknamePage/NicknamePage.tsx @@ -1,19 +1,19 @@ import { useQuery } from '@tanstack/react-query'; import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; +import NicknameInput from './NicknameInput/NicknameInput'; import { - nicknameBox, - nicknameInputWrapper, - nicknameInput, profileWrapper, profileImg, noVoteTextContainer, noVoteText, angryImage, + nicknameTitle, + nicknameContainer, } from './NicknamePage.styled'; -import { useMakeOrEnterRoom } from './useMakeOrEnterRoom'; +import useMakeOrEnterRoom from './useMakeOrEnterRoom'; import { isJoinableRoom } from '@/apis/room'; import AngryDdangkong from '@/assets/images/angryDdangkong.png'; @@ -26,15 +26,15 @@ import { memberInfoState, roomUuidState } from '@/recoil/atom'; const NicknamePage = () => { const { isOpen, show, close } = useModal(); - const { randomNickname, nicknameInputRef, handleMakeOrEnterRoom, isLoading } = - useMakeOrEnterRoom(show); + const { nicknameInputRef, handleMakeOrEnterRoom, isLoading } = useMakeOrEnterRoom(show); const { isMaster } = useRecoilValue(memberInfoState); const { roomUuid } = useParams(); - const [, setRoomUuidState] = useRecoilState(roomUuidState); + const setRoomUuidState = useSetRecoilState(roomUuidState); - const { data } = useQuery({ + const { data, isLoading: isJoinableLoading } = useQuery({ queryKey: ['isJoinable', roomUuid], queryFn: async () => isJoinableRoom(roomUuid || ''), + enabled: !!roomUuid, }); useEffect(() => { @@ -43,7 +43,7 @@ const NicknamePage = () => { } }, [roomUuid, setRoomUuidState]); - if (roomUuid && !data?.isJoinable) + if (!isJoinableLoading && roomUuid && !data?.isJoinable) return (
ํ™”๋‚œ ๋•…์ฝฉ @@ -56,25 +56,23 @@ const NicknamePage = () => {
์‚ฌ์šฉ์ž ํ”„๋กœํ•„
-
๋‹‰๋„ค์ž„
-
- + ๋‹‰๋„ค์ž„ +