Skip to content

Commit

Permalink
feat: Add login handler + auto login handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Jun99uu committed Oct 16, 2023
1 parent 938a6bf commit 16049c6
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 71 deletions.
23 changes: 23 additions & 0 deletions client/src/apis/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { AuthData, AuthResponse } from 'Auth';
import axios, { AxiosResponse } from 'axios';

class AuthApi {
/** 로그인 */
login = async (loginId: string, password: string): Promise<AuthData> => {
const data: AxiosResponse<AuthResponse> = await axios({
method: 'post',
url: `${process.env.NEXT_PUBLIC_BASE_URL}/login`,
headers: {
'Content-Type': 'application/json',
},
data: {
loginId,
password,
},
});

return data.data.data;
};
}

export default AuthApi;
60 changes: 5 additions & 55 deletions client/src/atoms/authInfoState.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,8 @@
import { AuthResponse } from 'Auth';
import { atom } from 'jotai';

export const authInfoState = atom<AuthResponse | null>(null);

export const dummyAuthData: AuthResponse = {
success: true,
code: 'successCode_xyz123',
message: '로그인되었습니다.',
data: {
availableHomepages: [42, 69],
isPrivacyPolicyAgree: true,
privacyPolicyAgreePeriod: 3,
dept: {
id: 456,
code: 'deptCode_abc789',
name: '과학학부',
},
accessToken: 'xyzabc789ghi',
parentDept: {
id: 457,
code: 'parentDeptCode_def456',
name: '자연대학',
},
branch: {
id: 123,
name: '서부도서관',
alias: '서부',
libraryCode: 'libCode_ghi123',
sortOrder: 2,
},
showMobileMain: false,
memberNo: 'member_xyz789',
alternativeId: 'altid_ghi123',
lastUpdated: '2023-07-27 15:23:45',
branchGroup: {
id: 24,
name: '서울대학교',
},
isPortalLogin: false,
patronType: {
id: 34,
name: '교수',
},
disableServices: ['ABC', 'DEF', 'GHI'],
hasFamily: false,
name: '홍길동',
printMemberNo: 'print_xyz456',
patronState: {
id: 789,
name: '퇴직',
},
id: 987,
multiTypePatrons: [],
isExpired: false,
isFamilyLogin: false,
},
export type authInfoType = {
name: string;
sId: string;
};

export const authInfoState = atom<authInfoType | null>(null);
3 changes: 3 additions & 0 deletions client/src/constants/queryKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const QUERY_KEYS = {
login: 'LOGIN',
};
2 changes: 2 additions & 0 deletions client/src/hooks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { default as useVh } from './useVh';
export { default as useHeader } from './useHeader';
export { default as useInput } from './useInput';
export { default as useAuth } from './useAuth';
52 changes: 47 additions & 5 deletions client/src/hooks/useAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,62 @@
import AuthApi from '@/apis/auth';
import { authInfoState } from '@/atoms/authInfoState';
import { getUserInfo, updateUserInfo } from '@/utils/lib/infoHandler';
import { updateAccessToken } from '@/utils/lib/tokenHandler';
import { useAtom } from 'jotai';
import { useRouter } from 'next/router';

const useAuth = () => {
const authApi = new AuthApi();

const router = useRouter();
const [authInfo, setAuthInfo] = useAtom(authInfoState);

const handleLogin = () => {
router.replace('/');
/**
* 로그인 함수
*/
const handleLogin = async (id: string, password: string) => {
try {
const data = await authApi.login(id, password);
console.log(data);
setAuthInfo({
name: data.name,
sId: data.printMemberNo,
});
updateAccessToken(data.accessToken);
updateUserInfo(data.name, data.printMemberNo, id, password);
router.replace('/');
} catch (err) {
console.log(err);
}
};

const checkAuth = () => {
if (!authInfo) router.replace('/landing');
/**
* 자동 로그인 함수
*/
const autoLogin = async () => {
try {
const userInfo = getUserInfo();
if (!userInfo) throw new Error('Empty user info');
const data = await authApi.login(userInfo.loginId, userInfo.password);
console.log(data);
setAuthInfo({
name: data.name,
sId: data.printMemberNo,
});
updateAccessToken(data.accessToken);
updateUserInfo(
data.name,
data.printMemberNo,
userInfo.loginId,
userInfo.password,
);
} catch (err) {
console.log(err);
router.replace('/landing');
}
};

return { authInfo, checkAuth, handleLogin };
return { authInfo, autoLogin, handleLogin };
};

export default useAuth;
17 changes: 11 additions & 6 deletions client/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import { Global, css } from '@emotion/react';
import reset from '@/styles/reset';
import { useRouter } from 'next/router';
import { COLORS } from '@/styles/colors';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export default function App({ Component, pageProps }: AppProps) {
const { checkAuth } = useAuth();
const { autoLogin } = useAuth();
const router = useRouter();

const getBgColor = (pathname: string) => {
Expand All @@ -22,14 +25,16 @@ export default function App({ Component, pageProps }: AppProps) {
};

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

return (
<Frame>
<Global styles={[reset, getBgColor(router.pathname)]} />
<Component {...pageProps} />
</Frame>
<QueryClientProvider client={queryClient}>
<Frame>
<Global styles={[reset, getBgColor(router.pathname)]} />
<Component {...pageProps} />
</Frame>
</QueryClientProvider>
);
}

Expand Down
30 changes: 25 additions & 5 deletions client/src/pages/login.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { RoundButton } from '@/components/Buttons';
import { TextInput } from '@/components/Field';
import { useHeader, useVh } from '@/hooks';
import useAuth from '@/hooks/useAuth';
import { useAuth, useHeader, useInput, useVh } from '@/hooks';
import { COLORS } from '@/styles/colors';
import { flex } from '@/styles/tokens';
import { TYPO } from '@/styles/typo';
import styled from '@emotion/styled';
import { useLayoutEffect } from 'react';

type FormData = {
id: string;
password: string;
};

/**
* 로그인 페이지
*/
const Login = () => {
const { setHeader } = useHeader();
const { fullPageStyle } = useVh();
const { handleLogin } = useAuth();
const { values, handleChange } = useInput<FormData>({ id: '', password: '' });

useLayoutEffect(() => {
setHeader('로그인');
Expand All @@ -25,15 +30,30 @@ const Login = () => {
<InputWrapper>
<InputBox>
<Caption>학번</Caption>
<TextInput placeholder="ex. 20230000" value={''} />
<TextInput
placeholder="ex. 20230000"
value={values.id}
name="id"
onChange={handleChange}
/>
</InputBox>
<InputBox>
<Caption>비밀번호</Caption>
<TextInput placeholder="도서관 비밀번호를 입력하세요." value={''} />
<TextInput
placeholder="도서관 비밀번호를 입력하세요."
type="password"
value={values.password}
name="password"
onChange={handleChange}
/>
</InputBox>
</InputWrapper>
<ButtonWrapper>
<RoundButton title="로그인" theme="primary" onClick={handleLogin} />
<RoundButton
title="로그인"
theme="primary"
onClick={() => handleLogin(values.id, values.password)}
/>
<Link>비밀번호를 재설정하고 싶어요.</Link>
</ButtonWrapper>
</Container>
Expand Down
39 changes: 39 additions & 0 deletions client/src/utils/lib/infoHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type StudentInfo = {
name: string;
sId: string;
loginId: string;
password: string;
};

/**
* 정보 가져오기
*/
export const getUserInfo = (): StudentInfo | null => {
const data = localStorage.getItem('USER_INFO');
return data ? JSON.parse(data) : null;
};

/**
* 정보 업데이트
*/
export const updateUserInfo = (
name: string,
sId: string,
loginId: string,
password: string,
) => {
const data = {
name,
sId,
loginId,
password,
};
localStorage.setItem('USER_INFO', JSON.stringify(data));
};

/**
* 정보 삭제 (로그아웃)
*/
export const removeUserInfo = () => {
localStorage.removeItem('USER_INFO');
};

0 comments on commit 16049c6

Please sign in to comment.