Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import LogIn from '@/pages/logIn';
import Game from '@/pages/play/game';
import LobbyPage, { getLobbyInfo } from '@/pages/play/lobby';
import Mode from '@/pages/play/mode';
import Ranking from '@/pages/ranking';
import Ranking, { getRank } from '@/pages/ranking';
import Result, { getGameResult } from '@/pages/result';
import Settings from '@/pages/settings';
import Tutorial from '@/pages/tutorial';

import { checkToken } from '@/services/auth';
import { getRank } from '@/services/rank';

import { checkToken } from '@/features/auth';
import { ROUTE } from '@/shared/constants';

const router = createBrowserRouter([
Expand Down Expand Up @@ -111,6 +109,8 @@ const router = createBrowserRouter([
{
path: ROUTE.game,
element: <Game />,
loader: checkToken,
errorElement: <Navigate to={ROUTE.main} replace />,
},
]);

Expand Down
6 changes: 4 additions & 2 deletions src/components/frame/with-buttons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ const BackButton = ({ pathname }: { pathname: string }) => {

const DISABLED = {
rightButtons: new Set([ROUTE.error, ROUTE.result]),
menuButton: new Set([ROUTE.tutorial]),
menuButton: [ROUTE.tutorial, ROUTE.error],
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent data structure: rightButtons uses a Set while menuButton uses an array. Consider using Set for both to maintain consistency and improve lookup performance.

Copilot uses AI. Check for mistakes.
};

const BasicContentFrame = ({ children }: { children: ReactNode }) => {
const { pathname } = useLocation();

const rightButtonsDisabled = DISABLED.rightButtons.has(pathname);

const menuButtonDisabled = DISABLED.menuButton.has(pathname);
const menuButtonDisabled = DISABLED.menuButton.some((route) =>
pathname.startsWith(route),
);

return (
<div className={styles.frame}>
Expand Down
35 changes: 25 additions & 10 deletions src/features/auth/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,37 @@ const setToken = (token: string, status: number) => {
return false;
};

const axiosConfig = {
headers: {
'X-Bypass-Authorization': true,
},
};

export const loginAsMember = async (account: Account) => {
const { data, status } = await client.post<string>(`/user/login`, account, {
headers: {
'X-Bypass-Authorization': true,
},
});
const { data, status } = await client.post<string>(
`/user/login`,
account,
axiosConfig,
);
return setToken(data, status);
};

export const loginAsGuest = async () => {
return client
.get<string>('/user/guest', {
headers: {
'X-Bypass-Authorization': true,
},
})
.get<string>('/user/guest', axiosConfig)
.then(({ data, status }) => setToken(data, status))
.catch(() => false);
};

export const checkToken = async () => {
const isTokenValid = await client
.get<boolean>('/user/token')
.then(({ data }) => data);

if (!isTokenValid) {
localStorage.removeItem('token');
throw new Error();
}

return isTokenValid;
};
23 changes: 10 additions & 13 deletions src/features/auth/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { useQueryClient } from '@tanstack/react-query';
import { type AxiosError } from 'axios';
import { useNavigate } from 'react-router-dom';

import { loginAsGuest, loginAsMember } from './api';

import { removeUserQuery } from '@/models/user';
import { userQueryKey } from '@/models/user';
import { ROUTE } from '@/shared/constants';
import { setFullScreen } from '@/shared/utils';

export const useLogout = () => {
const navigate = useNavigate();
const queryClient = useQueryClient();

const logout = () => {
window.localStorage.removeItem('token');
removeUserQuery();
queryClient.removeQueries({ queryKey: userQueryKey });
navigate('/');
};

Expand All @@ -21,9 +23,10 @@ export const useLogout = () => {

export const useLogin = () => {
const navigate = useNavigate();
const queryClient = useQueryClient();

const login = async (account: Account) => {
removeUserQuery();
const memberLogin = async (account: Account) => {
queryClient.removeQueries({ queryKey: userQueryKey });

return loginAsMember(account)
.then((isSuccess) => {
Expand All @@ -42,14 +45,8 @@ export const useLogin = () => {
});
};

return { login };
};

export const useGuestLogin = () => {
const navigate = useNavigate();

const login = async () => {
removeUserQuery();
const guestLogin = async () => {
queryClient.removeQueries({ queryKey: userQueryKey });

const isSuccess = await loginAsGuest();
if (!isSuccess) {
Expand All @@ -59,5 +56,5 @@ export const useGuestLogin = () => {
return navigate(ROUTE.tutorial, { replace: true });
};

return { guestLogin: login };
return { memberLogin, guestLogin };
};
1 change: 1 addition & 0 deletions src/features/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './hooks';
export { checkToken } from './api';
2 changes: 0 additions & 2 deletions src/features/sign-up/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { AxiosError } from 'axios';

import { invalidateUserQuery } from '@/models/user';
import { client } from '@/shared/api';

const axiosConfig = {
Expand All @@ -22,7 +21,6 @@ export const upgradeToMember = async (formData: SignUpForm) => {
.post<string>('/user/guest', formData)
.then(({ data }) => {
console.debug(data);
invalidateUserQuery();
return true;
})
.catch(({ response }: AxiosError) => {
Expand Down
21 changes: 21 additions & 0 deletions src/features/sign-up/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQueryClient } from '@tanstack/react-query';

import { registerMember, upgradeToMember } from './api';

import { userQueryKey } from '@/models/user';

export const useSignUp = (isGuest: boolean) => {
const queryClient = useQueryClient();

if (!isGuest) {
return registerMember;
}

return async (formData: SignUpForm) => {
const isSuccess = await upgradeToMember(formData);
if (isSuccess) {
await queryClient.invalidateQueries({ queryKey: userQueryKey });
}
return isSuccess;
};
};
7 changes: 4 additions & 3 deletions src/features/sign-up/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { checkIdDuplicate, registerMember, upgradeToMember } from './api';
import { checkIdDuplicate } from './api';
import { useSignUp } from './hooks';
import { validateSignUpForm } from './utils';

import ColoredButton from '@/components/button/ColoredButton';
Expand All @@ -17,8 +18,9 @@ interface Props {
isGuest?: boolean;
}

const SignUp = ({ closeModal, isGuest }: Props) => {
const SignUp = ({ closeModal, isGuest = false }: Props) => {
const navigate = useNavigate();
const signUp = useSignUp(isGuest);
const [errorMessage, setErrorMessage] = useState(ERROR_MESSAGE.welcome);
const [form, setForm] = useState<SignUpForm>({
id: '',
Expand Down Expand Up @@ -48,7 +50,6 @@ const SignUp = ({ closeModal, isGuest }: Props) => {
return;
}

const signUp = isGuest ? upgradeToMember : registerMember;
const isSuccess = await signUp(form);
if (!isSuccess) {
return;
Expand Down
9 changes: 5 additions & 4 deletions src/models/user/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { getDefaultStore, useAtomValue, useSetAtom } from 'jotai';

import { getUser } from './api';
import { queryKey } from './constants';
import { updateUserCodeAtom, userCodeAtom, userStaleAtom } from './store';
import { invalidateUserQuery } from './utils';

export const useUserInfo = () => {
const queryClient = useQueryClient();
const setUserCode = useSetAtom(updateUserCodeAtom);
const store = getDefaultStore();
const isStale = store.get(userStaleAtom);
Expand All @@ -18,14 +18,15 @@ export const useUserInfo = () => {
setUserCode(user.userCode);
return user;
},
throwOnError: true,
refetchOnWindowFocus: false,
refetchOnMount: isStale,
});

const refetchUser = () => queryClient.invalidateQueries({ queryKey });

return {
user: data,
refetchUser: invalidateUserQuery,
refetchUser,
};
};

Expand Down
2 changes: 1 addition & 1 deletion src/models/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './hooks';
export * from './utils';
export { queryKey as userQueryKey } from './constants';
8 changes: 0 additions & 8 deletions src/models/user/utils.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/error/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const ERROR_MESSAGE = {
'401': '로그인 정보가 유효하지 않습니다!\n다시 로그인 하시겠습니까?',
'404': '페이지를 찾을 수 없습니다!\n이전 화면으로 돌아가시겠습니까?',
'404': '페이지를 찾을 수 없습니다!\n홈으로 돌아가시겠습니까?',
'500': '서버에 에러가 발생했습니다!\n 개발자에게 문의하세요.',
'400': '네트워크 연결 상태를\n확인해주세요',
};
19 changes: 10 additions & 9 deletions src/pages/error/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import { useNavigate, useParams } from 'react-router-dom';

import * as constants from './constants';
import { ERROR_MESSAGE } from './constants';
import * as styles from './index.css';

import ColoredButton from '@/components/button/ColoredButton';

import type { errorCode } from '@/pages/error/type';

import { ROUTE } from '@/shared/constants';

const Error = () => {
const navigate = useNavigate();
const code = useParams().code as errorCode;
const code = useParams().code as ErrorCode;

const goBack = () => {
if (code === '401') {
navigate(ROUTE.main, { replace: true });
return;
switch (code) {
case '401':
return navigate(ROUTE.main, { replace: true });
case '404':
return navigate(ROUTE.home, { replace: true });
default:
return navigate(-1);
}
navigate(-1);
};

return (
<div className={styles.errorWrapper}>
<pre className={styles.errorMessage}>{constants.ERROR_MESSAGE[code]}</pre>
<pre className={styles.errorMessage}>{ERROR_MESSAGE[code]}</pre>
<ColoredButton text='확인' color='green' size='small' onClick={goBack} />
</div>
);
Expand Down
3 changes: 0 additions & 3 deletions src/pages/error/type.d.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/pages/error/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type ErrorCode = keyof typeof import('./constants').ERROR_MESSAGE;
4 changes: 2 additions & 2 deletions src/pages/landing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import * as styles from './index.css';

import ColoredButton from '@/components/button/ColoredButton/index';

import { useGuestLogin } from '@/features/auth';
import { useLogin } from '@/features/auth';
import { ROUTE } from '@/shared/constants';

const Landing = () => {
const navigate = useNavigate();
const { guestLogin } = useGuestLogin();
const { guestLogin } = useLogin();

const clickLogIn = () => {
navigate(ROUTE.login);
Expand Down
4 changes: 2 additions & 2 deletions src/pages/logIn/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const LogIn = () => {
id: '',
password: '',
});
const { login } = useLogin();
const { memberLogin } = useLogin();

const { modalRef, openModal, closeModal } = useModal();

Expand All @@ -30,7 +30,7 @@ const LogIn = () => {
};

const handleLogInClick = async () => {
const isLoginSuccess = await login(logInInfo);
const isLoginSuccess = await memberLogin(logInInfo);
if (!isLoginSuccess) {
setIsClicked(false);
openModal();
Expand Down
13 changes: 13 additions & 0 deletions src/pages/ranking/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { client } from '@/shared/api';

export const getRank = async () => {
return client
.get<{
refreshTime: string;
players: Profile[];
}>('rank/list')
.then(({ data }) => {
console.debug('rank:', data);
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug console statement should be removed before merging to production. Consider using a proper logging library or removing this entirely.

Suggested change
console.debug('rank:', data);

Copilot uses AI. Check for mistakes.
return data.players;
});
};
Loading