송용우

Fixed Login&Register Add Homepage

...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
6 "@testing-library/jest-dom": "^4.2.4", 6 "@testing-library/jest-dom": "^4.2.4",
7 "@testing-library/react": "^9.3.2", 7 "@testing-library/react": "^9.3.2",
8 "@testing-library/user-event": "^7.1.2", 8 "@testing-library/user-event": "^7.1.2",
9 + "axios": "^0.19.2",
9 "immer": "^7.0.1", 10 "immer": "^7.0.1",
10 "include-media": "^1.4.9", 11 "include-media": "^1.4.9",
11 "open-color": "^1.7.0", 12 "open-color": "^1.7.0",
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
17 "redux": "^4.0.5", 18 "redux": "^4.0.5",
18 "redux-actions": "^2.6.5", 19 "redux-actions": "^2.6.5",
19 "redux-devtools-extension": "^2.13.8", 20 "redux-devtools-extension": "^2.13.8",
21 + "redux-saga": "^1.1.3",
20 "styled-components": "^5.1.1" 22 "styled-components": "^5.1.1"
21 }, 23 },
22 "scripts": { 24 "scripts": {
...@@ -39,5 +41,6 @@ ...@@ -39,5 +41,6 @@
39 "last 1 firefox version", 41 "last 1 firefox version",
40 "last 1 safari version" 42 "last 1 safari version"
41 ] 43 ]
42 - } 44 + },
45 + "proxy": "http://localhost:4000"
43 } 46 }
......
1 import React from 'react'; 1 import React from 'react';
2 import styled from 'styled-components'; 2 import styled from 'styled-components';
3 import { Link } from 'react-router-dom'; 3 import { Link } from 'react-router-dom';
4 -import Button from '../common/Button';
5 import palette from '../../lib/styles/palette'; 4 import palette from '../../lib/styles/palette';
6 -/* 5 +import Button from '../common/Button';
7 -Display Auth Form(Register, Login)
8 -*/
9 6
10 const AuthFormBlock = styled.div` 7 const AuthFormBlock = styled.div`
11 h3 { 8 h3 {
...@@ -47,9 +44,6 @@ const ButtonWithMarginTop = styled(Button)` ...@@ -47,9 +44,6 @@ const ButtonWithMarginTop = styled(Button)`
47 margin-top: 1rem; 44 margin-top: 1rem;
48 `; 45 `;
49 46
50 -/**
51 - * Show Error message
52 - */
53 const ErrorMessage = styled.div` 47 const ErrorMessage = styled.div`
54 color: red; 48 color: red;
55 text-align: center; 49 text-align: center;
......
...@@ -38,7 +38,7 @@ const AuthTemplate = ({ children }) => { ...@@ -38,7 +38,7 @@ const AuthTemplate = ({ children }) => {
38 <AuthTemplateBlock> 38 <AuthTemplateBlock>
39 <WhiteBox> 39 <WhiteBox>
40 <div className="logo-area"> 40 <div className="logo-area">
41 - <Link to="/">Jaksimsamil</Link> 41 + <Link to="/">작심삼일</Link>
42 </div> 42 </div>
43 {children} 43 {children}
44 </WhiteBox> 44 </WhiteBox>
......
1 +import React from 'react';
2 +import styled from 'styled-components';
3 +import Responsive from './Responsive';
4 +import Button from './Button';
5 +import { Link } from 'react-router-dom';
6 +
7 +const HeaderBlock = styled.div`
8 + position: fixed;
9 + width: 100%;
10 + background: white;
11 + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08);
12 +`;
13 +
14 +const Wrapper = styled(Responsive)`
15 + height: 4rem;
16 + display: flex;
17 + align-items: center;
18 + justify-content: space-between;
19 + .logo {
20 + font-size: 1.125rem;
21 + font-weight: 800;
22 + letter-spacing: 2px;
23 + }
24 + .right {
25 + display: flex;
26 + align-items: center;
27 + }
28 +`;
29 +
30 +const Spacer = styled.div`
31 + height: 4rem;
32 +`;
33 +const UserInfo = styled.div`
34 + font-weight: 800;
35 + margin-right: 1rem;
36 +`;
37 +
38 +const Header = ({ user, onLogout }) => {
39 + return (
40 + <>
41 + <HeaderBlock>
42 + <Wrapper>
43 + <Link to="/" className="logo">
44 + 작심삼일
45 + </Link>
46 + {user ? (
47 + <div className="right">
48 + <UserInfo>{user.username}</UserInfo>
49 + <Button onClick={onLogout}>로그아웃</Button>
50 + </div>
51 + ) : (
52 + <div className="right">
53 + <Button to="/login">로그인</Button>
54 + </div>
55 + )}
56 + </Wrapper>
57 + </HeaderBlock>
58 + <Spacer />
59 + </>
60 + );
61 +};
62 +
63 +export default Header;
1 +import React from 'react';
2 +import styled from 'styled-components';
3 +
4 +const ResponsiveBlock = styled.div`
5 + padding-left: 1rem;
6 + padding-right: 1rem;
7 + width: 1024px;
8 + margin: 0 auto;
9 +
10 + @media (max-width: 1024px) {
11 + width: 768px;
12 + }
13 + @media (max-width: 768px) {
14 + width: 100%;
15 + }
16 +`;
17 +
18 +const Responsive = ({ children, ...rest }) => {
19 + return <ResponsiveBlock {...rest}>{children}</ResponsiveBlock>;
20 +};
21 +
22 +export default Responsive;
...@@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react'; ...@@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react';
2 import { useDispatch, useSelector } from 'react-redux'; 2 import { useDispatch, useSelector } from 'react-redux';
3 import { withRouter } from 'react-router-dom'; 3 import { withRouter } from 'react-router-dom';
4 import { changeField, initializeForm, login } from '../../modules/auth'; 4 import { changeField, initializeForm, login } from '../../modules/auth';
5 -import AuthForm from './AuthForm'; 5 +import AuthForm from '../../components/auth/AuthForm';
6 +import { check } from '../../modules/user';
6 7
7 const LoginForm = ({ history }) => { 8 const LoginForm = ({ history }) => {
8 const dispatch = useDispatch(); 9 const dispatch = useDispatch();
...@@ -59,8 +60,15 @@ const LoginForm = ({ history }) => { ...@@ -59,8 +60,15 @@ const LoginForm = ({ history }) => {
59 console.log(user); 60 console.log(user);
60 } 61 }
61 }, [history, user]); 62 }, [history, user]);
62 - 63 + return (
63 - return <AuthForm type="login"></AuthForm>; 64 + <AuthForm
65 + type="login"
66 + form={form}
67 + onChange={onChange}
68 + onSubmit={onSubmit}
69 + error={error}
70 + ></AuthForm>
71 + );
64 }; 72 };
65 73
66 export default withRouter(LoginForm); 74 export default withRouter(LoginForm);
......
1 import React, { useEffect, useState } from 'react'; 1 import React, { useEffect, useState } from 'react';
2 import { useDispatch, useSelector } from 'react-redux'; 2 import { useDispatch, useSelector } from 'react-redux';
3 +import { changeField, initializeForm, register } from '../../modules/auth';
3 import AuthForm from '../../components/auth/AuthForm'; 4 import AuthForm from '../../components/auth/AuthForm';
5 +import { check } from '../../modules/user';
4 import { withRouter } from 'react-router-dom'; 6 import { withRouter } from 'react-router-dom';
5 7
6 const RegisterForm = ({ history }) => { 8 const RegisterForm = ({ history }) => {
...@@ -32,7 +34,6 @@ const RegisterForm = ({ history }) => { ...@@ -32,7 +34,6 @@ const RegisterForm = ({ history }) => {
32 return; 34 return;
33 } 35 }
34 if (password !== passwordConfirm) { 36 if (password !== passwordConfirm) {
35 - //Todo Handle Error
36 setError('비밀번호가 일치하지 않습니다.'); 37 setError('비밀번호가 일치하지 않습니다.');
37 changeField({ form: 'register', key: 'password', value: '' }); 38 changeField({ form: 'register', key: 'password', value: '' });
38 changeField({ form: 'register', key: 'passwordConfirm', value: '' }); 39 changeField({ form: 'register', key: 'passwordConfirm', value: '' });
...@@ -72,8 +73,15 @@ const RegisterForm = ({ history }) => { ...@@ -72,8 +73,15 @@ const RegisterForm = ({ history }) => {
72 } 73 }
73 } 74 }
74 }, [history, user]); 75 }, [history, user]);
75 - 76 + return (
76 - return <AuthForm type="register" form={form}></AuthForm>; 77 + <AuthForm
78 + type="register"
79 + form={form}
80 + onChange={onChange}
81 + onSubmit={onSubmit}
82 + error={error}
83 + ></AuthForm>
84 + );
77 }; 85 };
78 86
79 export default withRouter(RegisterForm); 87 export default withRouter(RegisterForm);
......
1 +import React from 'react';
2 +import { useSelector, useDispatch } from 'react-redux';
3 +import Header from '../../components/common/Header';
4 +import { logout } from '../../modules/user';
5 +const HeaderContainer = () => {
6 + const { user } = useSelector(({ user }) => ({ user: user.user }));
7 + const dispatch = useDispatch();
8 + const onLogout = () => {
9 + dispatch(logout());
10 + };
11 + return <Header user={user} onLogout={onLogout} />;
12 +};
13 +export default HeaderContainer;
...@@ -5,11 +5,31 @@ import App from './App'; ...@@ -5,11 +5,31 @@ import App from './App';
5 import * as serviceWorker from './serviceWorker'; 5 import * as serviceWorker from './serviceWorker';
6 import { BrowserRouter } from 'react-router-dom'; 6 import { BrowserRouter } from 'react-router-dom';
7 import { Provider } from 'react-redux'; 7 import { Provider } from 'react-redux';
8 -import { createStore } from 'redux'; 8 +import { createStore, applyMiddleware } from 'redux';
9 import { composeWithDevTools } from 'redux-devtools-extension'; 9 import { composeWithDevTools } from 'redux-devtools-extension';
10 -import rootReducer from './modules'; 10 +import createSagaMiddleware from 'redux-saga';
11 +import rootReducer, { rootSaga } from './modules';
12 +import { tempSetUser, check } from './modules/user';
11 13
12 -const store = createStore(rootReducer, composeWithDevTools()); 14 +const sagaMiddleware = createSagaMiddleware();
15 +const store = createStore(
16 + rootReducer,
17 + composeWithDevTools(applyMiddleware(sagaMiddleware)),
18 +);
19 +
20 +function loadUser() {
21 + try {
22 + const user = localStorage.getItem('user');
23 + if (!user) return;
24 +
25 + store.dispatch(tempSetUser(user));
26 + store.dispatch(check());
27 + } catch (e) {
28 + console.log('localStorage is not working');
29 + }
30 +}
31 +sagaMiddleware.run(rootSaga);
32 +loadUser();
13 33
14 ReactDOM.render( 34 ReactDOM.render(
15 <Provider store={store}> 35 <Provider store={store}>
......
1 +import client from './client';
2 +
3 +export const login = ({ username, password }) =>
4 + client.post('api/auth/login', { username, password });
5 +
6 +export const register = ({ username, password }) =>
7 + client.post('api/auth/register', { username, password });
8 +
9 +export const check = () => client.get('api/auth/check');
10 +
11 +export const logout = () => client.post('/api/auth/logout');
1 +import axios from './axios';
2 +const client = axios.create();
3 +export default client;
1 +import { call, put } from 'redux-saga/effects';
2 +import { startLoading, finishLoading } from '../modules/loading';
3 +
4 +export const createRequestActionTypes = (type) => {
5 + const SUCCESS = `${type}_SUCCESS`;
6 + const FAILURE = `${type}_FAILURE`;
7 + return [type, SUCCESS, FAILURE];
8 +};
9 +
10 +export default function createRequestSaga(type, request) {
11 + const SUCCESS = `${type}_SUCCESS`;
12 + const FAILURE = `${type}_FAILURE`;
13 +
14 + return function* (action) {
15 + yield put(startLoading(type));
16 + try {
17 + const response = yield call(request, action.payload);
18 + yield put({
19 + type: SUCCESS,
20 + payload: response.data,
21 + });
22 + } catch (e) {
23 + yield put({
24 + type: FAILURE,
25 + payload: e,
26 + error: true,
27 + });
28 + }
29 + yield put(finishLoading(type));
30 + };
31 +}
1 import { createAction, handleActions } from 'redux-actions'; 1 import { createAction, handleActions } from 'redux-actions';
2 import produce from 'immer'; 2 import produce from 'immer';
3 - 3 +import { takeLatest } from 'redux-saga/effects';
4 -const CHANGE_FIELD = 'auth/CHANGE_FIELD'; 4 +import createRequestSaga, {
5 + createRequestActionTypes,
6 +} from '../lib/createRequestSaga';
7 +import * as authAPI from '../lib/api/auth';
8 +const CHAGE_FIELD = 'auth/CHANGE_FIELD';
5 const INITIALIZE_FORM = 'auth/INITIALIZE_FORM'; 9 const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';
6 10
7 -export const sampleAction = createAction(SAMPLE_ACTION); 11 +const REGISTER = 'auth/REGISTER';
8 -export const cahngeField = createAction( 12 +const REGISTER_SUCCESS = 'auth/REGISTER_SUCCESS';
9 - CHANGE_FIELD, 13 +const REGISTER_FAILURE = 'auth/REGISTER_FAILURE';
10 - ({ form, key, value }) => { 14 +
11 - form, key, value; 15 +const LOGIN = 'auth/LOGIN';
12 - }, 16 +const LOGIN_SUCCESS = 'auth/LOGIN_SUCCESS';
17 +const LOGIN_FAILURE = 'auth/LOGIN_FAILURE';
18 +
19 +export const changeField = createAction(
20 + CHAGE_FIELD,
21 + ({ form, key, value }) => ({
22 + form,
23 + key,
24 + value,
25 + }),
13 ); 26 );
14 export const initializeForm = createAction(INITIALIZE_FORM, (form) => form); 27 export const initializeForm = createAction(INITIALIZE_FORM, (form) => form);
15 28
16 -const initialState = { 29 +const initalState = {
17 register: { 30 register: {
18 username: '', 31 username: '',
19 password: '', 32 password: '',
...@@ -23,20 +36,58 @@ const initialState = { ...@@ -23,20 +36,58 @@ const initialState = {
23 username: '', 36 username: '',
24 password: '', 37 password: '',
25 }, 38 },
39 + auth: null,
40 + authError: null,
26 }; 41 };
27 42
43 +export const register = createAction(REGISTER, ({ username, password }) => ({
44 + username,
45 + password,
46 +}));
47 +export const login = createAction(LOGIN, ({ username, password }) => ({
48 + username,
49 + password,
50 +}));
51 +
52 +const registerSaga = createRequestSaga(REGISTER, authAPI.register);
53 +const loginSaga = createRequestSaga(LOGIN, authAPI.login);
54 +
55 +export function* authSaga() {
56 + yield takeLatest(REGISTER, registerSaga);
57 + yield takeLatest(LOGIN, loginSaga);
58 +}
59 +
28 const auth = handleActions( 60 const auth = handleActions(
29 { 61 {
30 - [CHANGE_FIELD]: (state, { payload: { form, key, value } }) => 62 + [CHAGE_FIELD]: (state, { payload: { form, key, value } }) =>
31 produce(state, (draft) => { 63 produce(state, (draft) => {
32 draft[form][key] = value; 64 draft[form][key] = value;
33 }), 65 }),
34 [INITIALIZE_FORM]: (state, { payload: form }) => ({ 66 [INITIALIZE_FORM]: (state, { payload: form }) => ({
35 ...state, 67 ...state,
36 - [form]: initialState[form], 68 + [form]: initalState[form],
69 + authError: null,
70 + }),
71 + [REGISTER_SUCCESS]: (state, { payload: auth }) => ({
72 + ...state,
73 + authError: null,
74 + auth,
75 + }),
76 + [REGISTER_FAILURE]: (state, { payload: error }) => ({
77 + ...state,
78 + authError: error,
79 + }),
80 + [LOGIN_SUCCESS]: (state, { payload: auth }) => ({
81 + ...state,
82 + authError: null,
83 + auth,
84 + }),
85 + [LOGIN_FAILURE]: (state, { payload: error }) => ({
86 + ...state,
87 + authError: error,
37 }), 88 }),
38 }, 89 },
39 - initialState, 90 + initalState,
40 ); 91 );
41 92
42 export default auth; 93 export default auth;
......
1 import { combineReducers } from 'redux'; 1 import { combineReducers } from 'redux';
2 -import auth from './auth'; 2 +import { all } from 'redux-saga/effects';
3 +import auth, { authSaga } from './auth';
4 +import loading from './loading';
5 +import user, { userSaga } from './user';
3 6
4 const rootReducer = combineReducers({ 7 const rootReducer = combineReducers({
5 auth, 8 auth,
9 + loading,
10 + user,
6 }); 11 });
7 12
13 +export function* rootSaga() {
14 + yield all([authSaga(), userSaga()]);
15 +}
16 +
8 export default rootReducer; 17 export default rootReducer;
......
1 +import { createAction, handleActions } from 'redux-actions';
2 +
3 +const START_LOADING = 'loading/START_LOADING';
4 +const FINISH_LOADING = 'loading/FINISH_LOADING';
5 +
6 +export const startLoading = createAction(
7 + START_LOADING,
8 + (requestType) => requestType,
9 +);
10 +
11 +export const finishLoading = createAction(
12 + FINISH_LOADING,
13 + (requestType) => requestType,
14 +);
15 +
16 +const initialState = {};
17 +
18 +const loading = handleActions(
19 + {
20 + [START_LOADING]: (state, action) => ({
21 + ...state,
22 + [action.payload]: true,
23 + }),
24 + [FINISH_LOADING]: (state, action) => ({
25 + ...state,
26 + [action.payload]: false,
27 + }),
28 + },
29 + initialState,
30 +);
31 +
32 +export default loading;
1 +import { createAction, handleActions } from 'redux-actions';
2 +import { takeLatest, call } from 'redux-saga/effects';
3 +import * as authAPI from '../lib/api/auth';
4 +import createRequestSaga, {
5 + createRequestActionTypes,
6 +} from '../lib/createRequestSaga';
7 +
8 +const TEMP_SET_USER = 'user/TEMP_SET_USER';
9 +const [CHECK, CHECK_SUCCESS, CHECK_FAILURE] = createRequestActionTypes(
10 + 'user/CHECK',
11 +);
12 +const LOGOUT = 'user/LOGOUT';
13 +
14 +export const tempSetUser = createAction(TEMP_SET_USER, (user) => user);
15 +export const check = createAction(CHECK);
16 +export const logout = createAction(LOGOUT);
17 +const checkSaga = createRequestSaga(CHECK, authAPI.check);
18 +function checkFailureSaga() {
19 + try {
20 + localStorage.removeItem('user');
21 + } catch (e) {
22 + console.log('localStroage is not working');
23 + }
24 +}
25 +function* logoutSaga() {
26 + try {
27 + yield call(authAPI.logout);
28 + console.log('logout');
29 + localStorage.removeItem('user');
30 + } catch (e) {
31 + console.log(e);
32 + }
33 +}
34 +export function* userSaga() {
35 + yield takeLatest(CHECK, checkSaga);
36 + yield takeLatest(CHECK_FAILURE, checkFailureSaga);
37 + yield takeLatest(LOGOUT, logoutSaga);
38 +}
39 +
40 +const initialState = {
41 + user: null,
42 + checkError: null,
43 +};
44 +
45 +export default handleActions(
46 + {
47 + [TEMP_SET_USER]: (state, { payload: user }) => ({
48 + ...state,
49 + user,
50 + }),
51 + [CHECK_SUCCESS]: (state, { payload: user }) => ({
52 + ...state,
53 + user,
54 + checkError: null,
55 + }),
56 + [CHECK_FAILURE]: (state, { payload: error }) => ({
57 + ...state,
58 + user: null,
59 + checkError: error,
60 + }),
61 + [LOGOUT]: (state) => ({
62 + ...state,
63 + user: null,
64 + }),
65 + },
66 + initialState,
67 +);
1 import React from 'react'; 1 import React from 'react';
2 +import HeaderContainer from '../containers/common/HeaderContainer';
2 import Button from '../components/common/Button'; 3 import Button from '../components/common/Button';
3 4
4 const HomePage = () => { 5 const HomePage = () => {
5 return ( 6 return (
6 <div> 7 <div>
8 + <HeaderContainer />
7 <Button>test</Button> 9 <Button>test</Button>
8 </div> 10 </div>
9 ); 11 );
......
...@@ -1026,6 +1026,13 @@ ...@@ -1026,6 +1026,13 @@
1026 dependencies: 1026 dependencies:
1027 regenerator-runtime "^0.13.4" 1027 regenerator-runtime "^0.13.4"
1028 1028
1029 +"@babel/runtime@^7.6.3":
1030 + version "7.10.2"
1031 + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
1032 + integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==
1033 + dependencies:
1034 + regenerator-runtime "^0.13.4"
1035 +
1029 "@babel/template@^7.10.1": 1036 "@babel/template@^7.10.1":
1030 version "7.10.1" 1037 version "7.10.1"
1031 resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" 1038 resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
...@@ -1344,6 +1351,50 @@ ...@@ -1344,6 +1351,50 @@
1344 resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" 1351 resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
1345 integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== 1352 integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
1346 1353
1354 +"@redux-saga/core@^1.1.3":
1355 + version "1.1.3"
1356 + resolved "https://registry.yarnpkg.com/@redux-saga/core/-/core-1.1.3.tgz#3085097b57a4ea8db5528d58673f20ce0950f6a4"
1357 + integrity sha512-8tInBftak8TPzE6X13ABmEtRJGjtK17w7VUs7qV17S8hCO5S3+aUTWZ/DBsBJPdE8Z5jOPwYALyvofgq1Ws+kg==
1358 + dependencies:
1359 + "@babel/runtime" "^7.6.3"
1360 + "@redux-saga/deferred" "^1.1.2"
1361 + "@redux-saga/delay-p" "^1.1.2"
1362 + "@redux-saga/is" "^1.1.2"
1363 + "@redux-saga/symbols" "^1.1.2"
1364 + "@redux-saga/types" "^1.1.0"
1365 + redux "^4.0.4"
1366 + typescript-tuple "^2.2.1"
1367 +
1368 +"@redux-saga/deferred@^1.1.2":
1369 + version "1.1.2"
1370 + resolved "https://registry.yarnpkg.com/@redux-saga/deferred/-/deferred-1.1.2.tgz#59937a0eba71fff289f1310233bc518117a71888"
1371 + integrity sha512-908rDLHFN2UUzt2jb4uOzj6afpjgJe3MjICaUNO3bvkV/kN/cNeI9PMr8BsFXB/MR8WTAZQq/PlTq8Kww3TBSQ==
1372 +
1373 +"@redux-saga/delay-p@^1.1.2":
1374 + version "1.1.2"
1375 + resolved "https://registry.yarnpkg.com/@redux-saga/delay-p/-/delay-p-1.1.2.tgz#8f515f4b009b05b02a37a7c3d0ca9ddc157bb355"
1376 + integrity sha512-ojc+1IoC6OP65Ts5+ZHbEYdrohmIw1j9P7HS9MOJezqMYtCDgpkoqB5enAAZrNtnbSL6gVCWPHaoaTY5KeO0/g==
1377 + dependencies:
1378 + "@redux-saga/symbols" "^1.1.2"
1379 +
1380 +"@redux-saga/is@^1.1.2":
1381 + version "1.1.2"
1382 + resolved "https://registry.yarnpkg.com/@redux-saga/is/-/is-1.1.2.tgz#ae6c8421f58fcba80faf7cadb7d65b303b97e58e"
1383 + integrity sha512-OLbunKVsCVNTKEf2cH4TYyNbbPgvmZ52iaxBD4I1fTif4+MTXMa4/Z07L83zW/hTCXwpSZvXogqMqLfex2Tg6w==
1384 + dependencies:
1385 + "@redux-saga/symbols" "^1.1.2"
1386 + "@redux-saga/types" "^1.1.0"
1387 +
1388 +"@redux-saga/symbols@^1.1.2":
1389 + version "1.1.2"
1390 + resolved "https://registry.yarnpkg.com/@redux-saga/symbols/-/symbols-1.1.2.tgz#216a672a487fc256872b8034835afc22a2d0595d"
1391 + integrity sha512-EfdGnF423glv3uMwLsGAtE6bg+R9MdqlHEzExnfagXPrIiuxwr3bdiAwz3gi+PsrQ3yBlaBpfGLtDG8rf3LgQQ==
1392 +
1393 +"@redux-saga/types@^1.1.0":
1394 + version "1.1.0"
1395 + resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204"
1396 + integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg==
1397 +
1347 "@sheerun/mutationobserver-shim@^0.3.2": 1398 "@sheerun/mutationobserver-shim@^0.3.2":
1348 version "0.3.3" 1399 version "0.3.3"
1349 resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25" 1400 resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz#5405ee8e444ed212db44e79351f0c70a582aae25"
...@@ -2312,6 +2363,13 @@ aws4@^1.8.0: ...@@ -2312,6 +2363,13 @@ aws4@^1.8.0:
2312 resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" 2363 resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
2313 integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== 2364 integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
2314 2365
2366 +axios@^0.19.2:
2367 + version "0.19.2"
2368 + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
2369 + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
2370 + dependencies:
2371 + follow-redirects "1.5.10"
2372 +
2315 axobject-query@^2.0.2: 2373 axobject-query@^2.0.2:
2316 version "2.1.2" 2374 version "2.1.2"
2317 resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" 2375 resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
...@@ -3852,7 +3910,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: ...@@ -3852,7 +3910,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
3852 dependencies: 3910 dependencies:
3853 ms "2.0.0" 3911 ms "2.0.0"
3854 3912
3855 -debug@3.1.0: 3913 +debug@3.1.0, debug@=3.1.0:
3856 version "3.1.0" 3914 version "3.1.0"
3857 resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 3915 resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
3858 integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 3916 integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
...@@ -5023,6 +5081,13 @@ flush-write-stream@^1.0.0: ...@@ -5023,6 +5081,13 @@ flush-write-stream@^1.0.0:
5023 inherits "^2.0.3" 5081 inherits "^2.0.3"
5024 readable-stream "^2.3.6" 5082 readable-stream "^2.3.6"
5025 5083
5084 +follow-redirects@1.5.10:
5085 + version "1.5.10"
5086 + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
5087 + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
5088 + dependencies:
5089 + debug "=3.1.0"
5090 +
5026 follow-redirects@^1.0.0: 5091 follow-redirects@^1.0.0:
5027 version "1.10.0" 5092 version "1.10.0"
5028 resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" 5093 resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb"
...@@ -10295,7 +10360,14 @@ redux-devtools-extension@^2.13.8: ...@@ -10295,7 +10360,14 @@ redux-devtools-extension@^2.13.8:
10295 resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" 10360 resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
10296 integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== 10361 integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
10297 10362
10298 -redux@^4.0.5: 10363 +redux-saga@^1.1.3:
10364 + version "1.1.3"
10365 + resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.1.3.tgz#9f3e6aebd3c994bbc0f6901a625f9a42b51d1112"
10366 + integrity sha512-RkSn/z0mwaSa5/xH/hQLo8gNf4tlvT18qXDNvedihLcfzh+jMchDgaariQoehCpgRltEm4zHKJyINEz6aqswTw==
10367 + dependencies:
10368 + "@redux-saga/core" "^1.1.3"
10369 +
10370 +redux@^4.0.4, redux@^4.0.5:
10299 version "4.0.5" 10371 version "4.0.5"
10300 resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" 10372 resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
10301 integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== 10373 integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
...@@ -11866,6 +11938,25 @@ typedarray@^0.0.6: ...@@ -11866,6 +11938,25 @@ typedarray@^0.0.6:
11866 resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 11938 resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
11867 integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 11939 integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
11868 11940
11941 +typescript-compare@^0.0.2:
11942 + version "0.0.2"
11943 + resolved "https://registry.yarnpkg.com/typescript-compare/-/typescript-compare-0.0.2.tgz#7ee40a400a406c2ea0a7e551efd3309021d5f425"
11944 + integrity sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==
11945 + dependencies:
11946 + typescript-logic "^0.0.0"
11947 +
11948 +typescript-logic@^0.0.0:
11949 + version "0.0.0"
11950 + resolved "https://registry.yarnpkg.com/typescript-logic/-/typescript-logic-0.0.0.tgz#66ebd82a2548f2b444a43667bec120b496890196"
11951 + integrity sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==
11952 +
11953 +typescript-tuple@^2.2.1:
11954 + version "2.2.1"
11955 + resolved "https://registry.yarnpkg.com/typescript-tuple/-/typescript-tuple-2.2.1.tgz#7d9813fb4b355f69ac55032e0363e8bb0f04dad2"
11956 + integrity sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==
11957 + dependencies:
11958 + typescript-compare "^0.0.2"
11959 +
11869 uid-number@0.0.6: 11960 uid-number@0.0.6:
11870 version "0.0.6" 11961 version "0.0.6"
11871 resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" 11962 resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
......
...@@ -32,6 +32,6 @@ ...@@ -32,6 +32,6 @@
32 | profile | 추천 문제 조회 | GET | api/profile/recommend:id | 바로가기 | None | 32 | profile | 추천 문제 조회 | GET | api/profile/recommend:id | 바로가기 | None |
33 | notify | 슬랙 메시지 전송 요청 | POST | api/notify/slack | 바로가기 | Jwt Token | 33 | notify | 슬랙 메시지 전송 요청 | POST | api/notify/slack | 바로가기 | Jwt Token |
34 | auth | 로그인 | POST | api/auth/login | 바로가기 | None | 34 | auth | 로그인 | POST | api/auth/login | 바로가기 | None |
35 -| auth | 로그아웃 | GET | api/auth/logout | 바로가기 | JWT Token | 35 +| auth | 로그아웃 | POST | api/auth/logout | 바로가기 | JWT Token |
36 | auth | 회원가입 | POST | api/auth/register | 바로가기 | None | 36 | auth | 회원가입 | POST | api/auth/register | 바로가기 | None |
37 | auth | 로그인 확인 | GET | api/auth/check | 바로가기 | None | 37 | auth | 로그인 확인 | GET | api/auth/check | 바로가기 | None |
......
...@@ -40,7 +40,7 @@ exports.register = async (ctx) => { ...@@ -40,7 +40,7 @@ exports.register = async (ctx) => {
40 ctx.body = user.serialize(); 40 ctx.body = user.serialize();
41 41
42 const token = user.generateToken(); 42 const token = user.generateToken();
43 - ctx.cookies.set("acces_token", token, { 43 + ctx.cookies.set("access_token", token, {
44 //3일동안 유효 44 //3일동안 유효
45 maxAge: 1000 * 60 * 60 * 24 * 3, 45 maxAge: 1000 * 60 * 60 * 24 * 3,
46 httpOnly: true, 46 httpOnly: true,
...@@ -75,7 +75,7 @@ exports.login = async (ctx) => { ...@@ -75,7 +75,7 @@ exports.login = async (ctx) => {
75 } 75 }
76 ctx.body = user.serialize(); 76 ctx.body = user.serialize();
77 const token = user.generateToken(); 77 const token = user.generateToken();
78 - ctx.cookies.set("acces_token", token, { 78 + ctx.cookies.set("access_token", token, {
79 //7일동안 유효 79 //7일동안 유효
80 maxAge: 1000 * 60 * 60 * 24 * 7, 80 maxAge: 1000 * 60 * 60 * 24 * 7,
81 httpOnly: true, 81 httpOnly: true,
...@@ -88,6 +88,7 @@ exports.login = async (ctx) => { ...@@ -88,6 +88,7 @@ exports.login = async (ctx) => {
88 GET api/auth/check 88 GET api/auth/check
89 */ 89 */
90 exports.check = async (ctx) => { 90 exports.check = async (ctx) => {
91 + console.log(ctx.state);
91 const { user } = ctx.state; 92 const { user } = ctx.state;
92 if (!user) { 93 if (!user) {
93 ctx.status = 401; 94 ctx.status = 401;
......
...@@ -2,7 +2,8 @@ const Router = require("koa-router"); ...@@ -2,7 +2,8 @@ const Router = require("koa-router");
2 const auth = new Router(); 2 const auth = new Router();
3 const authCtrl = require("./auth.ctrl"); 3 const authCtrl = require("./auth.ctrl");
4 auth.post("/login", authCtrl.login); 4 auth.post("/login", authCtrl.login);
5 -auth.get("/logout", authCtrl.logout); 5 +auth.post("/logout", authCtrl.logout);
6 auth.post("/register", authCtrl.register); 6 auth.post("/register", authCtrl.register);
7 +auth.get("/check", authCtrl.check);
7 8
8 module.exports = auth; 9 module.exports = auth;
......
1 const jwt = require("jsonwebtoken"); 1 const jwt = require("jsonwebtoken");
2 const User = require("../models/user"); 2 const User = require("../models/user");
3 +
3 const jwtMiddleware = async (ctx, next) => { 4 const jwtMiddleware = async (ctx, next) => {
4 const token = ctx.cookies.get("access_token"); 5 const token = ctx.cookies.get("access_token");
6 + console.log("1");
7 + console.log(token);
5 if (!token) { 8 if (!token) {
6 - //토큰이 없을 때 9 + console.log("1");
7 return next(); 10 return next();
8 } 11 }
9 try { 12 try {
10 - const decoded = jwt.verify(token, process.env.JWT_TOKEN); 13 + console.log("1");
14 + const decoded = jwt.verify(token, process.env.JWT_SECRET);
11 ctx.state.user = { 15 ctx.state.user = {
12 _id: decoded._id, 16 _id: decoded._id,
13 username: decoded.username, 17 username: decoded.username,
14 }; 18 };
15 - //토큰의 남은 유효 기간이 2일 이하라면 재발급 19 + const now = Math.floor(Date.now() / 1000);
16 - if (decoded.exp - Date.now() / 1000 < 60 * 60 * 24 * 2) { 20 + if (decoded.exp - now < 60 * 60 * 24 * 3.5) {
17 const user = await User.findById(decoded._id); 21 const user = await User.findById(decoded._id);
18 const token = user.generateToken(); 22 const token = user.generateToken();
19 ctx.cookies.set("access_token", token, { 23 ctx.cookies.set("access_token", token, {
20 - maxAge: 1000 * 60 * 60 * 24 * 7, 24 + maxAge: 1000 * 60 * 60 * 24 * 7, //7days
21 httpOnly: true, 25 httpOnly: true,
22 }); 26 });
23 } 27 }
28 + console.log(decoded);
24 return next(); 29 return next();
25 } catch (e) { 30 } catch (e) {
26 return next(); 31 return next();
27 } 32 }
28 }; 33 };
29 -
30 module.exports = jwtMiddleware; 34 module.exports = jwtMiddleware;
......