Skip to content
Open
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
12 changes: 5 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import ServicePage from './pages/ServicePage';
import SigninPage from './pages/SigninPage';
import SignupPage from './pages/SignupPage';
import PrivateRoute from './components/common/PrivateRoute';
import { useUserStateValue } from './atoms/userState';
import useAutoLogin from './hooks/useAutoLogin';

const App: FC = () => {
const user = useUserStateValue();

const isAuthenticated = !!user;
const { isLoggedIn } = useAutoLogin();

return (
<ErrorBoundary>
Expand All @@ -25,19 +23,19 @@ const App: FC = () => {
path={Uri.signup}
exact
component={SignupPage}
isAccessible={!isAuthenticated}
isAccessible={!isLoggedIn}
/>
<PrivateRoute
path={Uri.signin}
exact
component={SigninPage}
isAccessible={!isAuthenticated}
isAccessible={!isLoggedIn}
/>
<PrivateRoute
path={Uri.service}
exact
component={ServicePage}
isAccessible={isAuthenticated}
isAccessible={isLoggedIn}
/>
<Route path={Uri.serviceEdit} exact component={ServiceEditPage} />
<Route component={NotFoundPage} />
Expand Down
5 changes: 2 additions & 3 deletions src/components/common/Navbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import React, { FC } from 'react';
import { Link } from 'react-router-dom';

import { useUserState } from '@/atoms/userState';
import useConfirmModal from '@/hooks/useConfirmModal';
import Uri from '@/constants/uri';
import PrivateNavbar from '@/components/nav/PrivateNavbar';
import PublicNavbar from '@/components/nav/PublicNavbar';
import Logo from '@/components/common/Logo';
import useAuth from '@/hooks/useAuth';

import { navStyle } from './style';

const Navbar: FC = () => {
const [user, setUser] = useUserState();
const { user, logout } = useAuth();
const [open] = useConfirmModal();

const logoutHandler = () => {
const logout = () => setUser(null);
open({ message: 'Do you want to logout?', acceptHandler: logout });
};

Expand Down
26 changes: 18 additions & 8 deletions src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { useState } from 'react';
import { useHistory } from 'react-router-dom';

import { authApi } from '@/apis';
import { LoginRequestBody } from '@/types';
import { useSetUserState } from '@/atoms/userState';
import { NETWORK_ERROR, loginError, UNEXPECTED_ERROR } from '@/constants/error';
import { useUserState } from '@/atoms/userState';
import { NETWORK_ERROR, UNEXPECTED_ERROR, loginError } from '@/constants/error';
import Uri from '@/constants/uri';
import { LoginRequestBody, User } from '@/types';

const useAuth = (): {
user: User;
login: ({ userId, password }: LoginRequestBody) => Promise<void>;
logout: () => void;
isLoading: boolean;
error: string;
} => {
const setUserState = useSetUserState();
const [user, setUserState] = useUserState();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const history = useHistory();
Expand All @@ -24,9 +25,13 @@ const useAuth = (): {

await authApi.login({ userId, password });

// @TODO 로그인 성공 시 사용자 정보 가져오는 API 호출 후 상태로 저장
setUserState((prev) => ({ ...prev, userId }));

history.replace(Uri.home);
localStorage.setItem('userId', userId);

setIsLoading(false);

history.replace(Uri.service);
} catch (e) {
if (e.response) {
if (loginError[e.response.status]) {
Expand All @@ -37,20 +42,25 @@ const useAuth = (): {
} else {
setError(NETWORK_ERROR);
}
throw e;
} finally {

setIsLoading(false);

throw e;
Copy link
Member

Choose a reason for hiding this comment

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

에러 throw를 할 필요가 있을까욥??

}
};

const logout = () => {
setUserState(null);

localStorage.removeItem('userId');

authApi.logout().catch(() => {
/* 로그아웃의 경우, 별도의 에러 처리를 하지 않음 */
});
};

return {
user,
login,
logout,
isLoading,
Expand Down
22 changes: 13 additions & 9 deletions src/hooks/useAutoLogin.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
import { useEffect, useState } from 'react';

import { authApi } from '@/apis';
import { useUserStateValue } from '@/atoms/userState';
import { useUserState } from '@/atoms/userState';

const useAutoLogin = (): { isLoading: boolean; isLoggedIn: boolean } => {
const userState = useUserStateValue();
const [user, setUserState] = useUserState();

const [isLoading, setIsLoading] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);

const autoLogin = async () => {
try {
const userId = localStorage.getItem('userId');
if (!userId) return;

setIsLoading(true);

await authApi.refresh();

// @TODO refresh API 성공 시, 사용자 정보 가져오는 API를 호출하여 사용자 상태로 저장

setIsLoggedIn(true);
setUserState({ userId });
} catch (e) {
setIsLoggedIn(false);
/*
따로 에러 핸들링 해야할 것으로 보입니다.
refresh를 실패했다는 것은 곧 refresh token이 만료되었을 경우가 대부분일테니
이에 따라서 다시 로그인 해달라는 창을 보여주는 것이 어떨지.. toastify 처럼..
*/
Comment on lines +22 to +26
Copy link
Member

@pumpkiinbell pumpkiinbell Dec 30, 2021

Choose a reason for hiding this comment

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

개인적으로 저는 refresh token 이 만료되었을 때 모달같은 알림을 띄우는 것을 선호하지 않습니다

그린카라는 앱을 이용하다가 언급해주신 경험을 했는데요,
앱에 오랜만에 들어갔을 때 아무 맥락없이 토큰이 만료되어 ... 같은 알림을 보면 썩 유쾌하진 않더라고요
유저가 비로그인 상태를 인지하면 자연스레 로그인을 시도하지 않을까... 하는 생각이 듭니다!

개인적인 생각이라 다른 방향으로 결정되어도 괜찮습니다!!
다만 굳이 로그인 창을 띄운다면 window.alert API 같이 확인 버튼을 눌러야 사라지는 모달보단 말씀하신 toastify 처럼 시간이 지나면 자연스레 사라지는 모달이 더 좋겠네요 => 저희 useSnackbar 사용하는 것도 좋아보입니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

그 훅이 뭐하는 건지.. 잘 몰라서요.. 어떤거죠 ?!

Copy link
Member

Choose a reason for hiding this comment

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

이 영상 5초 부근 인데... 훔 하단 중간 부분에 보이게 된다는 단점이 있긴 하네요...

Copy link
Member

Choose a reason for hiding this comment

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

로그인 필요 여부는 반드시 사용자에게 보여줘야 하는 부분이지 않을까 싶어요. refresh token이 만료되게 되면 잘 사용하다가 갑자기 새로고침을 했더니 로그아웃이 되는 상황이 발생할 수 있고 이에 대한 피드백이 반드시 사용자에게 필요하다고 생각합니다.

요거에 대해서는 useAutoLogin 외에도 일반 API 요청을 할 때도 처리를 해줘야 하는 부분이라 다른 이슈에서 작업하는 게 좋지 않을까 싶습니다.

} finally {
setIsLoading(false);
}
};

useEffect(() => {
autoLogin();
}, [userState]);
}, []);

return { isLoading, isLoggedIn };
return { isLoading, isLoggedIn: user !== null };
};

export default useAutoLogin;
9 changes: 7 additions & 2 deletions src/pages/SigninPage/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { MemoryRouter, Router } from 'react-router-dom';

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { RecoilRoot } from 'recoil';

import SigninPage from '.';

describe('SigninPage 테스트', () => {
const signupPageContainer = (
<MemoryRouter>
<SigninPage />
<RecoilRoot>
<SigninPage />
</RecoilRoot>
</MemoryRouter>
);

Expand All @@ -34,7 +37,9 @@ describe('SigninPage 테스트', () => {

render(
<Router history={history}>
<SigninPage />
<RecoilRoot>
<SigninPage />
</RecoilRoot>
</Router>,
);

Expand Down
14 changes: 4 additions & 10 deletions src/pages/SigninPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import React, { FC, useCallback } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { Link } from 'react-router-dom';

import { authApi } from '@/apis';
import { useSetUserState } from '@/atoms/userState';
import Jumbotron from '@/components/common/Jumbotron';
import Logo from '@/components/common/Logo';
import Shell from '@/components/shell/Shell';
import { reject, renderProps, resolve } from '@/components/shell/helper';
import { NETWORK_ERROR, UNEXPECTED_ERROR, loginError } from '@/constants/error';
import Uri from '@/constants/uri';
import useAuth from '@/hooks/useAuth';
import { LoginRequestBody } from '@/types';

import { footerStyle, headerStyle, layoutStyle } from './style';

const SigninPage: FC = () => {
const setUserState = useSetUserState();
const { push } = useHistory();
const { login } = useAuth();

const checkIsValueEmpty = useCallback(
(option: {
Expand All @@ -37,11 +35,7 @@ const SigninPage: FC = () => {
if (val !== 'y') return reject(2, 'Access denied');

try {
await authApi.login(body);

setUserState((prev) => ({ ...prev, userId: body.userId }));

push(Uri.service);
await login(body);

return resolve();
} catch (error) {
Expand Down