박민정

[docs] Change the folder structure

Showing 36 changed files with 1177 additions and 468 deletions
1 -# Getting Started with Create React App
2 -
3 This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 1 This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 2
5 ## Available Scripts 3 ## Available Scripts
...@@ -8,23 +6,23 @@ In the project directory, you can run: ...@@ -8,23 +6,23 @@ In the project directory, you can run:
8 6
9 ### `npm start` 7 ### `npm start`
10 8
11 -Runs the app in the development mode.\ 9 +Runs the app in the development mode.<br>
12 Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 10 Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 11
14 -The page will reload if you make edits.\ 12 +The page will reload if you make edits.<br>
15 You will also see any lint errors in the console. 13 You will also see any lint errors in the console.
16 14
17 ### `npm test` 15 ### `npm test`
18 16
19 -Launches the test runner in the interactive watch mode.\ 17 +Launches the test runner in the interactive watch mode.<br>
20 See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 18 See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 19
22 ### `npm run build` 20 ### `npm run build`
23 21
24 -Builds the app for production to the `build` folder.\ 22 +Builds the app for production to the `build` folder.<br>
25 It correctly bundles React in production mode and optimizes the build for the best performance. 23 It correctly bundles React in production mode and optimizes the build for the best performance.
26 24
27 -The build is minified and the filenames include the hashes.\ 25 +The build is minified and the filenames include the hashes.<br>
28 Your app is ready to be deployed! 26 Your app is ready to be deployed!
29 27
30 See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 28 See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
...@@ -35,7 +33,7 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d ...@@ -35,7 +33,7 @@ See the section about [deployment](https://facebook.github.io/create-react-app/d
35 33
36 If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 34 If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 35
38 -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 36 +Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 37
40 You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 38 You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 39
...@@ -47,24 +45,24 @@ To learn React, check out the [React documentation](https://reactjs.org/). ...@@ -47,24 +45,24 @@ To learn React, check out the [React documentation](https://reactjs.org/).
47 45
48 ### Code Splitting 46 ### Code Splitting
49 47
50 -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 48 +This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
51 49
52 ### Analyzing the Bundle Size 50 ### Analyzing the Bundle Size
53 51
54 -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 52 +This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
55 53
56 ### Making a Progressive Web App 54 ### Making a Progressive Web App
57 55
58 -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 56 +This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
59 57
60 ### Advanced Configuration 58 ### Advanced Configuration
61 59
62 -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 60 +This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
63 61
64 ### Deployment 62 ### Deployment
65 63
66 -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 64 +This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
67 65
68 ### `npm run build` fails to minify 66 ### `npm run build` fails to minify
69 67
70 -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 68 +This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
......
This diff could not be displayed because it is too large.
...@@ -3,21 +3,22 @@ ...@@ -3,21 +3,22 @@
3 "version": "0.1.0", 3 "version": "0.1.0",
4 "private": true, 4 "private": true,
5 "dependencies": { 5 "dependencies": {
6 - "@testing-library/jest-dom": "^5.12.0", 6 + "antd": "^3.24.1",
7 - "@testing-library/react": "^11.2.7", 7 + "axios": "^0.19.2",
8 - "@testing-library/user-event": "^12.8.3", 8 + "formik": "^1.5.8",
9 - "antd": "^4.16.1", 9 + "moment": "^2.24.0",
10 - "axios": "^0.21.1", 10 + "react": "^16.8.6",
11 - "http-proxy-middleware": "^2.0.0", 11 + "react-dom": "^16.8.6",
12 - "react": "^17.0.2", 12 + "react-icons": "^3.7.0",
13 - "react-dom": "^17.0.2", 13 + "react-redux": "^7.1.0-rc.1",
14 - "react-redux": "^7.2.4", 14 + "react-router-dom": "^5.0.1",
15 - "react-router-dom": "^5.2.0", 15 + "react-scripts": "3.4.1",
16 - "react-scripts": "4.0.3", 16 + "redux": "^4.0.0",
17 - "redux": "^4.1.0",
18 "redux-promise": "^0.6.0", 17 "redux-promise": "^0.6.0",
19 "redux-thunk": "^2.3.0", 18 "redux-thunk": "^2.3.0",
20 - "web-vitals": "^1.1.2" 19 + "yup": "^0.27.0",
20 + "core-js": "^3.6.4",
21 + "react-app-polyfill": "^1.0.6"
21 }, 22 },
22 "scripts": { 23 "scripts": {
23 "start": "react-scripts start", 24 "start": "react-scripts start",
...@@ -26,10 +27,7 @@ ...@@ -26,10 +27,7 @@
26 "eject": "react-scripts eject" 27 "eject": "react-scripts eject"
27 }, 28 },
28 "eslintConfig": { 29 "eslintConfig": {
29 - "extends": [ 30 + "extends": "react-app"
30 - "react-app",
31 - "react-app/jest"
32 - ]
33 }, 31 },
34 "browserslist": { 32 "browserslist": {
35 "production": [ 33 "production": [
...@@ -42,5 +40,8 @@ ...@@ -42,5 +40,8 @@
42 "last 1 firefox version", 40 "last 1 firefox version",
43 "last 1 safari version" 41 "last 1 safari version"
44 ] 42 ]
43 + },
44 + "devDependencies": {
45 + "http-proxy-middleware": "^1.0.3"
45 } 46 }
46 } 47 }
......
...@@ -2,14 +2,9 @@ ...@@ -2,14 +2,9 @@
2 <html lang="en"> 2 <html lang="en">
3 <head> 3 <head>
4 <meta charset="utf-8" /> 4 <meta charset="utf-8" />
5 - <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> 5 + <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
6 <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1" />
7 <meta name="theme-color" content="#000000" /> 7 <meta name="theme-color" content="#000000" />
8 - <meta
9 - name="description"
10 - content="Web site created using create-react-app"
11 - />
12 - <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13 <!-- 8 <!--
14 manifest.json provides metadata used when your web app is installed on a 9 manifest.json provides metadata used when your web app is installed on a
15 user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ 10 user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
......
...@@ -6,16 +6,6 @@ ...@@ -6,16 +6,6 @@
6 "src": "favicon.ico", 6 "src": "favicon.ico",
7 "sizes": "64x64 32x32 24x24 16x16", 7 "sizes": "64x64 32x32 24x24 16x16",
8 "type": "image/x-icon" 8 "type": "image/x-icon"
9 - },
10 - {
11 - "src": "logo192.png",
12 - "type": "image/png",
13 - "sizes": "192x192"
14 - },
15 - {
16 - "src": "logo512.png",
17 - "type": "image/png",
18 - "sizes": "512x512"
19 } 9 }
20 ], 10 ],
21 "start_url": ".", 11 "start_url": ".",
......
...@@ -9,12 +9,16 @@ import { ...@@ -9,12 +9,16 @@ import {
9 import LandingPage from './components/views/LandingPage/LandingPage' 9 import LandingPage from './components/views/LandingPage/LandingPage'
10 import LoginPage from './components/views/LoginPage/LoginPage' 10 import LoginPage from './components/views/LoginPage/LoginPage'
11 import RegisterPage from './components/views/RegisterPage/RegisterPage' 11 import RegisterPage from './components/views/RegisterPage/RegisterPage'
12 +import UploadPage from './components/views/UploadPage/UploadPage'
12 import auth from './hoc/authentication' 13 import auth from './hoc/authentication'
14 +import Navigation from './components/views/NavBar/NavBar'
13 15
14 function App() { 16 function App() {
15 return ( 17 return (
16 <Router> 18 <Router>
19 + <Navigation />
17 <div> 20 <div>
21 +
18 <Switch> 22 <Switch>
19 {/* null, false에 대한 옵션 설명은 auth로 가서 확인*/} 23 {/* null, false에 대한 옵션 설명은 auth로 가서 확인*/}
20 24
...@@ -24,6 +28,7 @@ function App() { ...@@ -24,6 +28,7 @@ function App() {
24 28
25 <Route exact path="/register" component={auth(RegisterPage, false)}/> 29 <Route exact path="/register" component={auth(RegisterPage, false)}/>
26 30
31 + <Route exact path="/product/upload" component={auth(UploadPage, true)}/>
27 {/* 32 {/*
28 <Route exact path="/" component={LandingPage} /> 33 <Route exact path="/" component={LandingPage} />
29 34
......
1 // type들만 관리하는 곳 1 // type들만 관리하는 곳
2 2
3 -export const LOGIN_USER = "login_user";
4 -export const REGISTER_USER = "resgier_user";
5 -export const AUTH_USER = "auth_user";
...\ No newline at end of file ...\ No newline at end of file
3 +export const LOGIN_USER = 'login_user';
4 +export const REGISTER_USER = 'register_user';
5 +export const AUTH_USER = 'auth_user';
6 +export const LOGOUT_USER = 'logout_user';
......
1 +import axios from 'axios';
2 +import {
3 + LOGIN_USER,
4 + REGISTER_USER,
5 + AUTH_USER,
6 + LOGOUT_USER,
7 +} from './types';
8 +import { USER_SERVER } from '../components/Config.js';
9 +
10 +export function loginUser(logInfo){
11 + const request = axios.post(`${USER_SERVER}/login`,logInfo)// logInfo를 post로 전달
12 + .then(response => response.data); // 서버에서 받은 데이터를 request에 저장
13 +
14 +return { // return을 통해 Reducer로 보냄
15 + // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
16 + // request를 reducer로 보내는 작업
17 +
18 + // action은 type과 response을 넣어줘야 함
19 + type: LOGIN_USER,
20 + payload: request // payroad == response
21 +}
22 +}
23 +
24 +export function registerUser(dataToSubmit){
25 + const request = axios.post(`${USER_SERVER}/register`,dataToSubmit)
26 + .then(response => response.data);
27 +
28 + return {
29 + type: REGISTER_USER,
30 + payload: request
31 + }
32 +}
33 +
34 +
35 +
36 +export function auth(){
37 + const request = axios.get(`${USER_SERVER}/auth`)
38 + .then(response => response.data);
39 +
40 + return {
41 + type: AUTH_USER,
42 + payload: request
43 + }
44 +}
45 +
46 +export function logoutUser(){
47 + const request = axios.get(`${USER_SERVER}/logout`)
48 + .then(response => response.data);
49 +
50 + return {
51 + type: LOGOUT_USER,
52 + payload: request
53 + }
54 +}
55 +
1 -// Redux에 있는 Store에 Reducer들이 여러가지 있을 수 있다.
2 -// -> why? : Reducer 안에서 하는 일은
3 -// state가 어떻게 변하는지를 보여준 다음, 변한 마지막 값을 return 해주는 것.
4 -// 웹서비스를 제작하면서 user state, comment state ... 등등 다양한 기능에 대한 state들이 존재할 수 있고
5 -// 각각 state마다 reducer가 있어서 user reducer, comment reducer ... 등등 다양한 reducer들이 존재할 수 있음.
6 -// ------------------------------------
7 -// 이렇게 나눠진 다양한 reducer을 combineReducers을 통해 rootReducer에서 하나로 합쳐주는 기능을 만들 것임.
8 import { combineReducers } from 'redux'; 1 import { combineReducers } from 'redux';
9 -import user from './user_reducer'; // user(회원가입, 로그인, 인증, 로그아웃 기능이 있음) reducer 2 +import user from './user_reducer';
10 -// import comment from './comment_reducer'; // comment기능이 있을 때 reducer
11 3
12 -const rootReducer = combineReducers( { 4 +const rootReducer = combineReducers({
13 - user 5 + user,
14 -}) 6 +});
15 7
16 -// 다른 곳에서도 rootReducer을 쓸 수 있도록
17 export default rootReducer; 8 export default rootReducer;
...\ No newline at end of file ...\ No newline at end of file
......
1 import { 1 import {
2 LOGIN_USER, 2 LOGIN_USER,
3 REGISTER_USER, 3 REGISTER_USER,
4 - AUTH_USER 4 + AUTH_USER,
5 + LOGOUT_USER,
5 } from '../_actions/types'; 6 } from '../_actions/types';
7 +
6 8
7 - 9 +export default function(state={},action){
8 -// reducer은 (previousState, action) => (nextState)로 10 + switch(action.type){
9 -export default function (prevState = {}, action) {
10 - switch (action.type) {
11 - case LOGIN_USER:
12 - return {...prevState, loginSuccess:action.payload} // 위의 prevState를 그대로 가져오고,
13 - // user_action.js에 있는 payload를 그대로 가져와서 return.
14 - // loginSuccess는 server/index.js 에서 login에 성공하면 json type으로 loginSuccess: true를 전달하라고 했기 때문
15 - break;
16 case REGISTER_USER: 11 case REGISTER_USER:
17 - return {...prevState, success:action.payload} 12 + return {...state, register: action.payload }
18 - // success는 server/index.js 에서 register에 성공하면 json type으로 success: true를 전달하라고 했기 때문 13 + case LOGIN_USER:
19 - break; 14 + return { ...state, loginSucces: action.payload }
20 -
21 case AUTH_USER: 15 case AUTH_USER:
22 - return {...prevState, user:action.payload} 16 + return {...state, userData: action.payload }
23 - break; 17 + case LOGOUT_USER:
18 + return {...state }
24 default: 19 default:
25 - return prevState; 20 + return state;
26 } 21 }
27 } 22 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +import React, { Suspense } from 'react';
2 +import { Route, Switch } from "react-router-dom";
3 +import Auth from "../hoc/auth";
4 +// pages for this product
5 +import LandingPage from "./views/LandingPage/LandingPage.js";
6 +import LoginPage from "./views/LoginPage/LoginPage.js";
7 +import RegisterPage from "./views/RegisterPage/RegisterPage.js";
8 +import NavBar from "./views/NavBar/NavBar";
9 +import Footer from "./views/Footer/Footer"
10 +
11 +//null Anyone Can go inside
12 +//true only logged in user can go inside
13 +//false logged in user can't go inside
14 +
15 +function App() {
16 + return (
17 + <Suspense fallback={(<div>Loading...</div>)}>
18 + <NavBar />
19 + <div style={{ paddingTop: '69px', minHeight: 'calc(100vh - 80px)' }}>
20 + <Switch>
21 + <Route exact path="/" component={Auth(LandingPage, null)} />
22 + <Route exact path="/login" component={Auth(LoginPage, false)} />
23 + <Route exact path="/register" component={Auth(RegisterPage, false)} />
24 + </Switch>
25 + </div>
26 + <Footer />
27 + </Suspense>
28 + );
29 +}
30 +
31 +export default App;
1 +//SERVER ROUTES
2 +export const USER_SERVER = '/api/users';
1 import React from 'react' 1 import React from 'react'
2 +import {Icon} from 'antd';
2 3
3 function Footer() { 4 function Footer() {
4 return ( 5 return (
5 - <div> 6 + <div style={{
6 - Footer 7 + height: '80px', display: 'flex',
8 + flexDirection: 'column', alignItems: 'center',
9 + justifyContent: 'center', fontSize:'1rem'
10 + }}>
11 + <p> 2018110650 박민정</p>
7 </div> 12 </div>
8 ) 13 )
9 } 14 }
10 15
11 export default Footer 16 export default Footer
12 -Footer
...\ No newline at end of file ...\ No newline at end of file
......
1 -import React, {useEffect} from 'react' 1 +import React from 'react'
2 -import axios from 'axios' 2 +import { FaCode } from "react-icons/fa";
3 -import { withRouter } from 'react-router-dom';
4 -
5 -function LandingPage(props) {
6 -
7 - // 로그아웃 버튼 클릭 됐을 때
8 - const onLogoutClickedEvent = () => {
9 - axios.get('/api/users/logout')
10 - .then(response => {
11 - // 만약 success:true이면 로그인 페이지로 가기
12 - if(response.data.success)
13 - props.history.push("/login");
14 - else
15 - alert("Fail to logout.")
16 - })
17 - }
18 - // 랜딩페이지에 들어오자마자
19 - useEffect(() => {
20 - axios.get('/api/hello') // get request를 서버로 보냄 (endpoint는 /api/hello)
21 - .then(response => console.log(response.data)) // 서버로부터 응답 받은 내용을 콘솔에 출력
22 - }, [])
23 3
4 +function LandingPage() {
24 return ( 5 return (
25 - <div style={{justifyContent:'center', alignItems: 'center', display:'flex', width:'100%'}}> 6 + <>
26 - <h1>시작 페이지</h1> 7 + <div className="app">
27 - <button onClick ={onLogoutClickedEvent}> Logout </button> 8 + ..
28 - </div> 9 + </div>
29 - 10 +
30 - 11 + </>
31 ) 12 )
32 } 13 }
33 14
34 -
35 -export default withRouter(LandingPage)
...\ No newline at end of file ...\ No newline at end of file
15 +export default LandingPage
......
1 -import axios from 'axios'; 1 +import React, { useState } from "react";
2 -//import { response } from 'express'; 2 +import { withRouter } from "react-router-dom";
3 -import React from 'react' 3 +import { loginUser } from "../../../_actions/user_actions";
4 -import {useState} from 'react' 4 +import { Formik } from 'formik';
5 -import {useDispatch} from 'react-redux'; 5 +import * as Yup from 'yup';
6 -import {loginUser} from '../../../_actions/user_action' 6 +import { Form, Icon, Input, Button, Checkbox, Typography } from 'antd';
7 -import { withRouter } from 'react-router-dom'; 7 +import { useDispatch } from "react-redux";
8 +
9 +const { Title } = Typography;
8 10
9 function LoginPage(props) { 11 function LoginPage(props) {
10 - 12 + const dispatch = useDispatch();
13 + //const rememberMeChecked = localStorage.getItem("rememberMe") ? true : false;
14 +
15 + const [Error, setError] = useState('')
16 + //const [rememberMe, setRememberMe] = useState(rememberMeChecked)
11 17
12 - // 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용. 18 + // const handleRememberMe = () => {
13 - // 1-1. state을 사용하기 위해 state 만들어줌. 19 + // setRememberMe(!rememberMe)
14 - const [Email, setEmail] = useState(""); // 1-2. email을 위한 state 20 + // };
15 - const [Password, setPassword] = useState(""); // 1-2. password를 위한 state
16 - //1-3. 아래 input value에 넣어줌
17 21
18 - // 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌 22 + //const initialEmail = localStorage.getItem("rememberMe") ? localStorage.getItem("rememberMe") : '';
19 - const emailEvent = (event) => {
20 - setEmail(event.currentTarget.value)
21 - }
22 - const passwordEvent = (event) => {
23 - setPassword(event.currentTarget.value)
24 -
25 - }
26 23
27 - const dispatch = useDispatch(); 24 + return (
28 - const submitEvent = (event) => { 25 + <Formik
29 - event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음 26 + initialValues={{
30 - 27 + email: '',
31 - //console.log('Email', Email); // 잘 나오는지 확인 28 + password: '',
32 - //console.log('Password', Password); // 잘 나오는지 확인 29 + }}
30 + // validationSchema={Yup.object().shape({
31 + // email: Yup.string()
32 + // .email('Email is invalid')
33 + // .required('Email is required'),
34 + // password: Yup.string()
35 + // .min(6, 'Password must be at least 6 characters')
36 + // .required('Password is required'),
37 + // })}
38 + onSubmit={(values, { setSubmitting }) => {
39 + setTimeout(() => {
40 + let dataToSubmit = {
41 + email: values.email,
42 + password: values.password
43 + };
33 44
34 - let logInfo = { // 보내주기 위해 저장 45 + dispatch(loginUser(dataToSubmit))
35 - email: Email,
36 - password: Password
37 - }
38 -
39 - dispatch(loginUser(logInfo)) // _actions폴더 user_action.js에 있음
40 .then(response => { 46 .then(response => {
41 - if(response.payload.loginSuccess) 47 + if (response.payload.loginSuccess) {
42 - props.history.push('/'); 48 + window.localStorage.setItem('userId', response.payload.userId);
43 - 49 + // if (rememberMe === true) {
44 - else 50 + // window.localStorage.setItem('rememberMe', values.id);
45 - alert('Error'); 51 + // } else {
46 - 52 + // localStorage.removeItem('rememberMe');
53 + // }
54 + props.history.push("/");
55 + } else {
56 + setError('이메일 또는 비밀번호가 올바르지 않습니다.')
57 + }
58 + })
59 + .catch(err => {
60 + setError('이메일 또는 비밀번호가 올바르지 않습니다.')
61 + setTimeout(() => {
62 + setError("")
63 + }, 3000);
64 + });
65 + setSubmitting(false);
66 + }, 500);
67 + }}
68 + >
69 + {props => {
70 + const {
71 + values,
72 + touched,
73 + errors,
74 + dirty,
75 + isSubmitting,
76 + handleChange,
77 + handleBlur,
78 + handleSubmit,
79 + handleReset,
80 + } = props;
81 + return (
82 + <div className="app">
83 +
84 + <Title level={2}>로그인</Title>
85 + <form onSubmit={handleSubmit} style={{ width: '350px' }}>
86 +
87 + <Form.Item required>
88 + <Input
89 + id="email"
90 + prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
91 + placeholder="Enter your email"
92 + type="email"
93 + value={values.email}
94 + onChange={handleChange}
95 + onBlur={handleBlur}
96 + className={
97 + errors.email && touched.email ? 'text-input error' : 'text-input'
98 + }
99 + />
100 + {errors.email && touched.email && (
101 + <div className="input-feedback">{errors.email}</div>
102 + )}
103 + </Form.Item>
47 104
48 - }) 105 + <Form.Item required>
49 - 106 + <Input
50 - } 107 + id="password"
51 - return ( 108 + prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
52 - <div style={{ 109 + placeholder="Enter your password"
53 - justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh' 110 + type="password"
54 - }}> 111 + value={values.password}
55 - <form onSubmit={submitEvent}> 112 + onChange={handleChange}
56 - <label>Email</label> 113 + onBlur={handleBlur}
57 - <input type="email" value={Email} onChange={emailEvent} /> 114 + className={
58 - {/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */} 115 + errors.password && touched.password ? 'text-input error' : 'text-input'
59 - <label>Password</label> 116 + }
60 - <input type="password" value={Password} onChange={passwordEvent} /> 117 + />
61 - <br/> 118 + {errors.password && touched.password && (
62 - <button> 119 + <div className="input-feedback">{errors.password}</div>
63 - Login 120 + )}
64 - </button> 121 + </Form.Item>
122 +
123 + {Error && (
124 + <label ><p style={{ color: '#508DFF', fontSize: '0.8rem' }}>{Error}</p></label>
125 + )}
126 +
127 + <Form.Item>
128 + {/* <Checkbox id="rememberMe" onChange={handleRememberMe} checked={rememberMe} >Remember me</Checkbox>
129 + <a className="login-form-forgot" href="/reset_user" style={{ float: 'right' }}>
130 + forgot password
131 + </a> */}
132 + <div>
133 + <Button type="primary" htmlType="submit" className="login-form-button" style={{ minWidth: '100%' }} disabled={isSubmitting} onSubmit={handleSubmit}>
134 + 로그인
135 + </Button>
136 + </div>
137 + <a href="/register" style={{ minWidth: '100%' }}>회원가입</a>
138 + </Form.Item>
65 </form> 139 </form>
66 - </div> 140 + </div>
67 - ) 141 + );
68 -} 142 + }}
143 + </Formik>
144 + );
145 +};
146 +
147 +export default withRouter(LoginPage);
148 +
69 149
70 -export default withRouter(LoginPage)
......
1 -import React from 'react' 1 +import React, { useState } from 'react';
2 +import LeftMenu from './Sections/LeftMenu';
3 +import RightMenu from './Sections/RightMenu';
4 +import { Drawer, Button, Icon } from 'antd';
5 +import './Sections/Navbar.css';
2 6
3 function NavBar() { 7 function NavBar() {
4 - return ( 8 + const [visible, setVisible] = useState(false)
5 - <div> 9 +
6 - NavBar 10 + const showDrawer = () => {
11 + setVisible(true)
12 + };
13 +
14 + const onClose = () => {
15 + setVisible(false)
16 + };
17 +
18 + return (
19 + <nav className="menu" style={{ position: 'fixed', zIndex: 5, width: '100%' }}>
20 + <div className="menu__logo">
21 + <a href="/">Logo</a>
22 + </div>
23 + <div className="menu__container">
24 + <div className="menu_left">
25 + <LeftMenu mode="horizontal" />
26 + </div>
27 + <div className="menu_rigth">
28 + <RightMenu mode="horizontal" />
7 </div> 29 </div>
8 - ) 30 + <Button
31 + className="menu__mobile-button"
32 + type="primary"
33 + onClick={showDrawer}
34 + >
35 + <Icon type="align-right" />
36 + </Button>
37 + <Drawer
38 + title="Basic Drawer"
39 + placement="right"
40 + className="menu_drawer"
41 + closable={false}
42 + onClose={onClose}
43 + visible={visible}
44 + >
45 + <LeftMenu mode="inline" />
46 + <RightMenu mode="inline" />
47 + </Drawer>
48 + </div>
49 + </nav>
50 + )
9 } 51 }
10 52
11 -export default NavBar 53 +export default NavBar
...\ No newline at end of file ...\ No newline at end of file
......
1 +import React from 'react';
2 +import { Menu } from 'antd';
3 +const SubMenu = Menu.SubMenu;
4 +const MenuItemGroup = Menu.ItemGroup;
5 +
6 +function LeftMenu(props) {
7 + return (
8 + <Menu mode={props.mode}>
9 + <Menu.Item key="mail">
10 + <a href="/">Home</a>
11 + </Menu.Item>
12 + <SubMenu title={<span>Blogs</span>}>
13 + <MenuItemGroup title="Item 1">
14 + <Menu.Item key="setting:1">Option 1</Menu.Item>
15 + <Menu.Item key="setting:2">Option 2</Menu.Item>
16 + </MenuItemGroup>
17 + <MenuItemGroup title="Item 2">
18 + <Menu.Item key="setting:3">Option 3</Menu.Item>
19 + <Menu.Item key="setting:4">Option 4</Menu.Item>
20 + </MenuItemGroup>
21 + </SubMenu>
22 + </Menu>
23 + )
24 +}
25 +
26 +export default LeftMenu
...\ No newline at end of file ...\ No newline at end of file
1 +@import "~antd/dist/antd.css";
2 +
3 +.menu {
4 + padding: 0 20px;
5 + border-bottom: solid 1px #e8e8e8;
6 + overflow: auto;
7 + box-shadow: 0 0 30px #f3f1f1;
8 + background-color: white;
9 +}
10 +
11 +.menu__logo {
12 + width: 150px;
13 + float: left;
14 +}
15 +
16 +.menu__logo a {
17 + display: inline-block;
18 + font-size: 20px;
19 + padding: 19px 20px;
20 +}
21 +
22 +.menu__container .ant-menu-item {
23 + padding: 0px 5px;
24 +}
25 +
26 +.menu__container .ant-menu-submenu-title {
27 + padding: 10px 20px;
28 +}
29 +
30 +.menu__container .ant-menu-item a,
31 +.menu__container .ant-menu-submenu-title a {
32 + padding: 10px 15px;
33 +}
34 +
35 +.menu__container .ant-menu-horizontal {
36 + border-bottom: none;
37 +}
38 +
39 +.menu__container .menu_left {
40 + float: left;
41 +}
42 +
43 +.menu__container .menu_rigth {
44 + float: right;
45 +}
46 +
47 +.menu__mobile-button {
48 + float: right;
49 + height: 32px;
50 + padding: 6px;
51 + margin-top: 8px;
52 + display: none !important; /* use of important to overwrite ant-btn */
53 + background: #3e91f7;
54 +}
55 +
56 +.menu_drawer .ant-drawer-body {
57 + padding: 0 !important;
58 +}
59 +
60 +/* align header of Drawer with header of page */
61 +.menu_drawer .ant-drawer-header {
62 + padding: 14px 24px !important;
63 +}
64 +
65 +@media (max-width: 767px) {
66 + .menu__mobile-button {
67 + display: inline-block !important;
68 + }
69 +
70 + .menu_left,
71 + .menu_rigth {
72 + display: none;
73 + }
74 +
75 + .menu__logo a {
76 + margin-left: -20px;
77 + }
78 +
79 + .menu__container .ant-menu-item,
80 + .menu__container .ant-menu-submenu-title {
81 + padding: 1px 20px;
82 + }
83 +
84 + .menu__logo a {
85 + padding: 10px 20px;
86 + }
87 +}
1 +
2 +import React from 'react';
3 +import { Menu } from 'antd';
4 +import axios from 'axios';
5 +import { USER_SERVER } from '../../../Config';
6 +import { withRouter } from 'react-router-dom';
7 +import { useSelector } from "react-redux";
8 +
9 +function RightMenu(props) {
10 + const user = useSelector(state => state.user)
11 +
12 + const logoutHandler = () => {
13 + axios.get(`${USER_SERVER}/logout`).then(response => {
14 + if (response.status === 200) {
15 + props.history.push("/login");
16 + } else {
17 + alert('Log Out Failed')
18 + }
19 + });
20 + };
21 +
22 + //console.log(user.userData);
23 + //console.log(!user.userData.isAuth);
24 +
25 + if (user.userData && !user.userData.isAuth) {
26 + return (
27 + <Menu mode={props.mode}>
28 + <Menu.Item key="mail">
29 + <a href="/login">로그인</a>
30 + </Menu.Item>
31 + <Menu.Item key="app">
32 + <a href="/register">회원가입</a>
33 + </Menu.Item>
34 + </Menu>
35 + )
36 + } else {
37 + return (
38 + <Menu mode={props.mode}>
39 + <Menu.Item key="logout">
40 + <a onClick={logoutHandler}>로그아웃</a>
41 + </Menu.Item>
42 + </Menu>
43 + )
44 + }
45 +}
46 +
47 +export default withRouter(RightMenu);
48 +
1 +import React from 'react';
2 +import { Menu } from 'antd';
3 +const SubMenu = Menu.SubMenu;
4 +const MenuItemGroup = Menu.ItemGroup;
5 +
6 +function LeftMenu(props) {
7 + return (
8 + <Menu mode={props.mode}>
9 + <Menu.Item key="mail">
10 + <a href="/">Home</a>
11 + </Menu.Item>
12 + <SubMenu title={<span>Blogs</span>}>
13 + <MenuItemGroup title="Item 1">
14 + <Menu.Item key="setting:1">Option 1</Menu.Item>
15 + <Menu.Item key="setting:2">Option 2</Menu.Item>
16 + </MenuItemGroup>
17 + <MenuItemGroup title="Item 2">
18 + <Menu.Item key="setting:3">Option 3</Menu.Item>
19 + <Menu.Item key="setting:4">Option 4</Menu.Item>
20 + </MenuItemGroup>
21 + </SubMenu>
22 + </Menu>
23 + )
24 +}
25 +
26 +export default LeftMenu
...\ No newline at end of file ...\ No newline at end of file
1 +/* eslint-disable jsx-a11y/anchor-is-valid */
2 +import React from 'react';
3 +import { Menu } from 'antd';
4 +import axios from 'axios';
5 +//import { USER_SERVER } from '../../../Config';
6 +import { withRouter } from 'react-router-dom';
7 +import { useSelector } from "react-redux";
8 +
9 +function RightMenu(props) {
10 + const user = useSelector(state => state.user)
11 +
12 + const logoutHandler = () => {
13 + axios.get('/api/users/logout').then(response => {
14 + if (response.status === 200) {
15 + props.history.push("/login");
16 + } else {
17 + alert('Log Out Failed')
18 + }
19 + });
20 + };
21 +
22 + //console.log(user.userData)
23 + //console.log(!user.userData.isAuth)
24 + if (user.userData && !user.userData.isAuth) {
25 + return (
26 + <Menu mode={props.mode}>
27 + <Menu.Item key="mail">
28 + <a href="/login">Signin</a>
29 + </Menu.Item>
30 + <Menu.Item key="app">
31 + <a href="/register">Signup</a>
32 + </Menu.Item>
33 + </Menu>
34 + )
35 + } else {
36 + return (
37 + <Menu mode={props.mode}>
38 + <Menu.Item key="logout">
39 + <a onClick={logoutHandler}>Logout</a>
40 + </Menu.Item>
41 + </Menu>
42 + )
43 + }
44 +}
45 +
46 +export default withRouter(RightMenu);
...\ No newline at end of file ...\ No newline at end of file
1 -import React from 'react' 1 +import React from "react";
2 -import {useState} from 'react' 2 +import moment from "moment";
3 -import {useDispatch} from 'react-redux'; 3 +import { Formik } from 'formik';
4 -import {RegisterUser} from '../../../_actions/user_action' 4 +import * as Yup from 'yup';
5 -import { withRouter } from 'react-router-dom'; 5 +import { registerUser } from "../../../_actions/user_actions";
6 +import { useDispatch } from "react-redux";
6 7
7 -function RegisterPage(props) { 8 +import {
8 - 9 + Form,
9 - // 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용. 10 + Input,
10 - // 1-1. state을 사용하기 위해 state 만들어줌. 11 + Button,
11 - const [Name, setName] = useState(""); 12 +} from 'antd';
12 - const [Email, setEmail] = useState(""); // 1-2. email을 위한 state
13 - const [Password, setPassword] = useState(""); // 1-2. password를 위한 state
14 - const [Password2, setPassword2] = useState("");
15 13
16 - //1-3. 아래 input value에 넣어줌 14 +const formItemLayout = {
15 + labelCol: {
16 + xs: { span: 24 },
17 + sm: { span: 8 },
18 + },
19 + wrapperCol: {
20 + xs: { span: 24 },
21 + sm: { span: 16 },
22 + },
23 +};
24 +const tailFormItemLayout = {
25 + wrapperCol: {
26 + xs: {
27 + span: 24,
28 + offset: 0,
29 + },
30 + sm: {
31 + span: 16,
32 + offset: 8,
33 + },
34 + },
35 +};
17 36
18 - // 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌 37 +function RegisterPage(props) {
19 - const emailEvent = (event) => { 38 + const dispatch = useDispatch();
20 - setEmail(event.currentTarget.value) 39 + return (
21 - }
22 - const passwordEvent = (event) => {
23 - setPassword(event.currentTarget.value)
24 -
25 - }
26 - const password2Event = (event) => {
27 - setPassword2(event.currentTarget.value)
28 -
29 - }
30 - const NameEvent = (event) => {
31 - setName(event.currentTarget.value)
32 -
33 - }
34 40
35 - const dispatch = useDispatch(); 41 + <Formik
36 - const submitEvent = (event) => { 42 + initialValues={{
37 - event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음 43 + email: '',
38 - 44 +
39 - //console.log('Email', Email); // 잘 나오는지 확인 45 + name: '',
40 - //console.log('Password', Password); // 잘 나오는지 확인 46 + password: '',
47 + confirmPassword: ''
48 + }}
49 + validationSchema={Yup.object().shape({
50 + name: Yup.string()
51 + .required('이름이 올바르게 입력되지 않았습니다.'),
52 +
53 + email: Yup.string()
54 + .required('이메일이 올바르게 입력되지 않았습니다.'),
55 + password: Yup.string()
56 + .min(3, '비밀번호는 3자리 이상이어야 합니다.')
57 + .required('비밀번호를 입력해주세요.'),
58 + confirmPassword: Yup.string()
59 + .oneOf([Yup.ref('password'), null], '비밀번호를 다시 확인해주세요.')
60 + .required('비밀번호 확인이 올바르게 입력되지 않았습니다.')
61 + })}
62 + onSubmit={(values, { setSubmitting }) => {
63 + setTimeout(() => {
41 64
42 - // 비밀번호 두개가 같아야 회원가입이 되도록 65 + let dataToSubmit = {
43 - if(Password !== Password2) 66 + email: values.email,
44 - return alert('비밀번호가 일치하지 않습니다.') 67 + password: values.password,
68 + name: values.name,
69 +
70 + //image: `http://gravatar.com/avatar/${moment().unix()}?d=identicon`
71 + };
45 72
73 + dispatch(registerUser(dataToSubmit)).then(response => {
74 + if (response.payload.success) {
75 + props.history.push("/login");
76 + } else {
77 + alert(response.payload.err.errmsg)
78 + }
79 + })
46 80
47 - let regiInfo = { // 보내주기 위해 저장 81 + setSubmitting(false);
48 - name : Name, 82 + }, 500);
49 - email: Email, 83 + }}
50 - password: Password 84 + >
51 - } 85 + {props => {
52 - 86 + const {
53 - dispatch(RegisterUser(regiInfo)) // _actions폴더 user_action.js에 있음 87 + values,
54 - .then(response => { 88 + touched,
55 - if(response.payload.success) 89 + errors,
56 - props.history.push('/login'); 90 + dirty,
57 - 91 + isSubmitting,
58 - else 92 + handleChange,
59 - alert('Fail to sign up'); 93 + handleBlur,
60 - }) 94 + handleSubmit,
61 - 95 + handleReset,
62 - } 96 + } = props;
97 + return (
98 + <div className="app">
99 + <h2>회원가입</h2>
100 + <Form style={{ minWidth: '375px' }} {...formItemLayout} onSubmit={handleSubmit} >
63 101
64 - return ( 102 + <Form.Item required label="Name">
65 - <div style={{ 103 + <Input
66 - justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh' 104 + id="name"
67 - }}> 105 + placeholder="이름을 입력하세요."
68 - <form onSubmit={submitEvent} style={{display: 'flex', flexDirection: 'column'}}> 106 + type="text"
107 + value={values.name}
108 + onChange={handleChange}
109 + onBlur={handleBlur}
110 + className={
111 + errors.name && touched.name ? 'text-input error' : 'text-input'
112 + }
113 + />
114 + {errors.name && touched.name && (
115 + <div className="input-feedback">{errors.name}</div>
116 + )}
117 + </Form.Item>
69 118
70 - <label>Name</label> 119 + <Form.Item required label="Email" hasFeedback>
71 - <input type="text" value={Name} onChange={NameEvent} /> 120 + <Input
121 + id="email"
122 + placeholder="이메일을 입력하세요."
123 + type="email"
124 + value={values.email}
125 + onChange={handleChange}
126 + onBlur={handleBlur}
127 + className={
128 + errors.email && touched.email ? 'text-input error' : 'text-input'
129 + }
130 + />
131 + {errors.email && touched.email && (
132 + <div className="input-feedback">{errors.email}</div>
133 + )}
134 + </Form.Item>
72 135
73 - <label>Email</label> 136 + <Form.Item required label="Password" hasFeedback>
74 - <input type="email" value={Email} onChange={emailEvent} /> 137 + <Input
75 - {/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */} 138 + id="password"
139 + placeholder="비밀번호를 입력하세요."
140 + type="password"
141 + value={values.password}
142 + onChange={handleChange}
143 + onBlur={handleBlur}
144 + className={
145 + errors.password && touched.password ? 'text-input error' : 'text-input'
146 + }
147 + />
148 + {errors.password && touched.password && (
149 + <div className="input-feedback">{errors.password}</div>
150 + )}
151 + </Form.Item>
76 152
153 + <Form.Item required label="Confirm" hasFeedback>
154 + <Input
155 + id="confirmPassword"
156 + placeholder="비밀번호를 다시 한 번 입력해주세요."
157 + type="password"
158 + value={values.confirmPassword}
159 + onChange={handleChange}
160 + onBlur={handleBlur}
161 + className={
162 + errors.confirmPassword && touched.confirmPassword ? 'text-input error' : 'text-input'
163 + }
164 + />
165 + {errors.confirmPassword && touched.confirmPassword && (
166 + <div className="input-feedback">{errors.confirmPassword}</div>
167 + )}
168 + </Form.Item>
77 169
78 - <label>Password</label> 170 + <Form.Item {...tailFormItemLayout}>
79 - <input type="password" value={Password} onChange={passwordEvent} /> 171 + <Button onClick={handleSubmit} type="primary" disabled={isSubmitting}>
172 + Submit
173 + </Button>
174 + </Form.Item>
175 + </Form>
176 + </div>
177 + );
178 + }}
179 + </Formik>
180 + );
181 +};
80 182
81 - <label>Confirm Password</label>
82 - <input type="password" value={Password2} onChange={password2Event} />
83 -
84 - <br/>
85 - <button>
86 - Sign In
87 - </button>
88 - </form>
89 - </div>
90 - )
91 -}
92 183
93 -export default withRouter(RegisterPage) 184 +export default RegisterPage
......
1 +import React from 'react'
2 +
3 +function UploadPage() {
4 + return (
5 + <div>
6 + uploadpage
7 + </div>
8 + )
9 +}
10 +
11 +export default UploadPage
1 +/* eslint-disable react-hooks/exhaustive-deps */
2 +import React, { useEffect } from 'react';
3 +import { auth } from '../_actions/user_actions';
4 +import { useSelector, useDispatch } from "react-redux";
5 +
6 +export default function (SpecificComponent, option, adminRoute = null) {
7 + function AuthenticationCheck(props) {
8 +
9 + let user = useSelector(state => state.user);
10 + const dispatch = useDispatch();
11 +
12 + useEffect(() => {
13 + //To know my current status, send Auth request
14 + dispatch(auth()).then(response => {
15 + //Not Loggined in Status
16 + if (!response.payload.isAuth) {
17 + if (option) {
18 + props.history.push('/login')
19 + }
20 + //Loggined in Status
21 + } else {
22 + //supposed to be Admin page, but not admin person wants to go inside
23 + if (adminRoute && !response.payload.isAdmin) {
24 + props.history.push('/')
25 + }
26 + //Logged in Status, but Try to go into log in page
27 + else {
28 + if (option === false) {
29 + props.history.push('/')
30 + }
31 + }
32 + }
33 + })
34 +
35 + }, [])
36 +
37 + return (
38 + <SpecificComponent {...props} user={user} />
39 + )
40 + }
41 + return AuthenticationCheck
42 +}
43 +
44 +
1 body { 1 body {
2 margin: 0; 2 margin: 0;
3 - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 3 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
4 - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 4 + "Droid Sans", "Helvetica Neue", sans-serif;
5 - sans-serif;
6 -webkit-font-smoothing: antialiased; 5 -webkit-font-smoothing: antialiased;
7 -moz-osx-font-smoothing: grayscale; 6 -moz-osx-font-smoothing: grayscale;
8 } 7 }
9 8
10 code { 9 code {
11 - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 10 + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
12 - monospace; 11 +}
12 +
13 +body {
14 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji",
15 + "Segoe UI Emoji", "Segoe UI Symbol";
16 + font-size: 14px;
17 + line-height: 1.5;
18 + color: #24292e;
19 + background-color: #fff;
20 +}
21 +
22 +.app {
23 + flex-direction: column;
24 + display: flex;
25 + justify-content: center;
26 + align-items: center;
27 + height: 100vh;
28 +}
29 +
30 +input.error {
31 + border-color: red;
32 +}
33 +
34 +
35 +.input-feedback {
36 + color: red;
37 + height: 5px;
38 + margin-top: -12px;
13 } 39 }
......
1 +import 'react-app-polyfill/ie9';
2 +import 'react-app-polyfill/ie11';
3 +import 'core-js';
4 +
1 import React from 'react'; 5 import React from 'react';
2 import ReactDOM from 'react-dom'; 6 import ReactDOM from 'react-dom';
3 import './index.css'; 7 import './index.css';
4 -import App from './App'; 8 +import App from './components/App';
5 -import reportWebVitals from './reportWebVitals'; 9 +import * as serviceWorker from './serviceWorker';
6 -import { Provider } from 'react-redux' // app에 redux를 연결시켜주기 위해 redux에서 제공하는 provider 사용 10 +import { BrowserRouter } from "react-router-dom";
7 -import { createStore } from 'redux'; // redux에서 createStore 가져옴. 11 +
8 -import { applyMiddleware } from 'redux'; // object만 받는 store가 promise나 functions도 받기 위해 middleware을 사용함 12 +import Reducer from './_reducers';
13 +import { Provider } from 'react-redux';
14 +import { createStore, applyMiddleware } from 'redux';
9 import promiseMiddleware from 'redux-promise'; 15 import promiseMiddleware from 'redux-promise';
10 import ReduxThunk from 'redux-thunk'; 16 import ReduxThunk from 'redux-thunk';
11 -import Reducer from './_reducers';
12 17
13 -import 'antd/dist/antd.css'; 18 +const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore);
14 -
15 -
16 -const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore)
17 19
18 ReactDOM.render( 20 ReactDOM.render(
19 - // App에 Redux를 연결 21 + <Provider
20 - <Provider 22 + store={createStoreWithMiddleware(
21 - store={createStoreWithMiddleware(Reducer, 23 + Reducer,
22 window.__REDUX_DEVTOOLS_EXTENSION__ && 24 window.__REDUX_DEVTOOLS_EXTENSION__ &&
23 window.__REDUX_DEVTOOLS_EXTENSION__() 25 window.__REDUX_DEVTOOLS_EXTENSION__()
24 )} 26 )}
25 > 27 >
26 - <App /> 28 + <BrowserRouter>
29 + <App />
30 + </BrowserRouter>
27 </Provider> 31 </Provider>
28 - , document.getElementById('root') 32 + , document.getElementById('root'));
29 -); 33 +// If you want your app to work offline and load faster, you can change
30 - 34 +// unregister() to register() below. Note this comes with some pitfalls.
31 -// If you want to start measuring performance in your app, pass a function 35 +// Learn more about service workers: https://bit.ly/CRA-PWA
32 -// to log resu lts (for example: reportWebVitals(console.log)) 36 +serviceWorker.unregister();
33 -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
34 -reportWebVitals();
......
1 +// This optional code is used to register a service worker.
2 +// register() is not called by default.
3 +
4 +// This lets the app load faster on subsequent visits in production, and gives
5 +// it offline capabilities. However, it also means that developers (and users)
6 +// will only see deployed updates on subsequent visits to a page, after all the
7 +// existing tabs open on the page have been closed, since previously cached
8 +// resources are updated in the background.
9 +
10 +// To learn more about the benefits of this model and instructions on how to
11 +// opt-in, read https://bit.ly/CRA-PWA
12 +
13 +const isLocalhost = Boolean(
14 + window.location.hostname === 'localhost' ||
15 + // [::1] is the IPv6 localhost address.
16 + window.location.hostname === '[::1]' ||
17 + // 127.0.0.1/8 is considered localhost for IPv4.
18 + window.location.hostname.match(
19 + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 + )
21 +);
22 +
23 +export function register(config) {
24 + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 + // The URL constructor is available in all browsers that support SW.
26 + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 + if (publicUrl.origin !== window.location.origin) {
28 + // Our service worker won't work if PUBLIC_URL is on a different origin
29 + // from what our page is served on. This might happen if a CDN is used to
30 + // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 + return;
32 + }
33 +
34 + window.addEventListener('load', () => {
35 + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 +
37 + if (isLocalhost) {
38 + // This is running on localhost. Let's check if a service worker still exists or not.
39 + checkValidServiceWorker(swUrl, config);
40 +
41 + // Add some additional logging to localhost, pointing developers to the
42 + // service worker/PWA documentation.
43 + navigator.serviceWorker.ready.then(() => {
44 + console.log(
45 + 'This web app is being served cache-first by a service ' +
46 + 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 + );
48 + });
49 + } else {
50 + // Is not localhost. Just register service worker
51 + registerValidSW(swUrl, config);
52 + }
53 + });
54 + }
55 +}
56 +
57 +function registerValidSW(swUrl, config) {
58 + navigator.serviceWorker
59 + .register(swUrl)
60 + .then(registration => {
61 + registration.onupdatefound = () => {
62 + const installingWorker = registration.installing;
63 + if (installingWorker == null) {
64 + return;
65 + }
66 + installingWorker.onstatechange = () => {
67 + if (installingWorker.state === 'installed') {
68 + if (navigator.serviceWorker.controller) {
69 + // At this point, the updated precached content has been fetched,
70 + // but the previous service worker will still serve the older
71 + // content until all client tabs are closed.
72 + console.log(
73 + 'New content is available and will be used when all ' +
74 + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 + );
76 +
77 + // Execute callback
78 + if (config && config.onUpdate) {
79 + config.onUpdate(registration);
80 + }
81 + } else {
82 + // At this point, everything has been precached.
83 + // It's the perfect time to display a
84 + // "Content is cached for offline use." message.
85 + console.log('Content is cached for offline use.');
86 +
87 + // Execute callback
88 + if (config && config.onSuccess) {
89 + config.onSuccess(registration);
90 + }
91 + }
92 + }
93 + };
94 + };
95 + })
96 + .catch(error => {
97 + console.error('Error during service worker registration:', error);
98 + });
99 +}
100 +
101 +function checkValidServiceWorker(swUrl, config) {
102 + // Check if the service worker can be found. If it can't reload the page.
103 + fetch(swUrl)
104 + .then(response => {
105 + // Ensure service worker exists, and that we really are getting a JS file.
106 + const contentType = response.headers.get('content-type');
107 + if (
108 + response.status === 404 ||
109 + (contentType != null && contentType.indexOf('javascript') === -1)
110 + ) {
111 + // No service worker found. Probably a different app. Reload the page.
112 + navigator.serviceWorker.ready.then(registration => {
113 + registration.unregister().then(() => {
114 + window.location.reload();
115 + });
116 + });
117 + } else {
118 + // Service worker found. Proceed as normal.
119 + registerValidSW(swUrl, config);
120 + }
121 + })
122 + .catch(() => {
123 + console.log(
124 + 'No internet connection found. App is running in offline mode.'
125 + );
126 + });
127 +}
128 +
129 +export function unregister() {
130 + if ('serviceWorker' in navigator) {
131 + navigator.serviceWorker.ready.then(registration => {
132 + registration.unregister();
133 + });
134 + }
135 +}
1 const { createProxyMiddleware } = require('http-proxy-middleware'); 1 const { createProxyMiddleware } = require('http-proxy-middleware');
2 2
3 -module.exports = function(app) { 3 +module.exports = function (app) {
4 - app.use( 4 + app.use(
5 - '/api', 5 + '/api',
6 - createProxyMiddleware({ 6 + createProxyMiddleware({
7 - target: 'http://localhost:5000', 7 + target: 'http://localhost:5000',
8 - changeOrigin: true, 8 + changeOrigin: true,
9 - }) 9 + })
10 - ); 10 + );
11 }; 11 };
...\ No newline at end of file ...\ No newline at end of file
......
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 { 1 {
2 - "name": "boiler-plate", 2 + "name": "react-boiler-plate",
3 "version": "1.0.0", 3 "version": "1.0.0",
4 - "description": "", 4 + "description": "react boiler plate",
5 "main": "index.js", 5 "main": "index.js",
6 + "engine": {
7 + "node": "10.16.0",
8 + "npm": "6.9.0"
9 + },
6 "scripts": { 10 "scripts": {
7 "start": "node server/index.js", 11 "start": "node server/index.js",
8 "backend": "nodemon server/index.js", 12 "backend": "nodemon server/index.js",
9 - "test": "echo \"Error: no test specified\" && exit 1", 13 + "frontend": "npm run start --prefix client",
10 "dev": "concurrently \"npm run backend\" \"npm run start --prefix client\"" 14 "dev": "concurrently \"npm run backend\" \"npm run start --prefix client\""
11 }, 15 },
12 - "author": "mindyeoi", 16 + "author": "John ahn",
13 "license": "ISC", 17 "license": "ISC",
14 "dependencies": { 18 "dependencies": {
15 - "bcrypt": "^5.0.1", 19 + "bcrypt": "^3.0.6",
16 - "body-parser": "^1.19.0", 20 + "body-parser": "^1.18.3",
17 - "concurrently": "^6.2.0", 21 + "cookie-parser": "^1.4.3",
18 - "cookie-parser": "^1.4.5", 22 + "cors": "^2.8.5",
23 + "debug": "^4.1.1",
19 "express": "^4.17.1", 24 "express": "^4.17.1",
20 "jsonwebtoken": "^8.5.1", 25 "jsonwebtoken": "^8.5.1",
21 - "mongoose": "^5.12.12" 26 + "moment": "^2.24.0",
27 + "mongoose": "^5.4.20",
28 + "react-redux": "^5.0.7",
29 + "saslprep": "^1.0.3",
30 + "supports-color": "^7.1.0"
22 }, 31 },
23 "devDependencies": { 32 "devDependencies": {
24 - "nodemon": "^2.0.7" 33 + "concurrently": "^4.1.0",
34 + "nodemon": "^1.19.1"
25 } 35 }
26 } 36 }
......
1 -const express = require('express') 1 +const express = require("express")
2 const app = express() 2 const app = express()
3 -const port = 5000 3 +const port = process.env.PORT || 5000
4 4
5 -// User.js에서 만든 model을 가져옴 5 +const path = require("path");
6 -const { User } = require('./models/User') 6 +const cors = require('cors')
7 -//const { User } = require('./')
8 7
9 // body-parser 가져옴 8 // body-parser 가져옴
10 const bodyParser = require('body-parser') 9 const bodyParser = require('body-parser')
11 // bodyParser option 10 // bodyParser option
12 app.use(bodyParser.urlencoded({extended: true})) //application/x-www-form-urlencoded로 된 데이터를 분석해서 가져옴 11 app.use(bodyParser.urlencoded({extended: true})) //application/x-www-form-urlencoded로 된 데이터를 분석해서 가져옴
13 app.use(bodyParser.json()) // application/json 타입으로 된 데이터를 분석해서 가져옴 12 app.use(bodyParser.json()) // application/json 타입으로 된 데이터를 분석해서 가져옴
13 +const cookieParser = require("cookie-parser");
14 +app.use(cookieParser());
15 +const config = require("./config/key");
14 16
15 -const config = require('./config/key')
16 17
17 -const cookieParser = require('cookie-parser') 18 +const mongoose = require("mongoose");
18 -app.use(cookieParser())
19 19
20 -const mongoose = require('mongoose')
21 -
22 -const { auth } = require('./middleware/auth')
23 -
24 -//이 정보는 비밀임..! 몽고DB아이디랑 비밀번호를 감춰야해..!
25 -mongoose.connect(config.mongoURI, {
26 - useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, useFindAndModify: false
27 -}).then(()=>console.log('MongoDB Connected...'))
28 -.catch(err => console.log(err))
29 -
30 -app.get('/', (req, res) => {
31 - res.send('민정이짱짱맨최고최고!')
32 -})
33 -
34 -// 회원가입 구현
35 -// route의 endpoint는 register
36 -app.post('/api/users/register', (req, res) => {
37 - // 회원가입할 때 필요한 정보들을 client에서 가져오면
38 - // 그것들을 데이터베이스에 넣어준다.
39 -
40 - const user = new User(req.body) // req.body에 User의 정보를 저장
41 -
42 - // 비밀번호 암호화
43 20
21 +const connect = mongoose.connect(config.mongoURI,
22 + {
23 + useNewUrlParser: true, useUnifiedTopology: true,
24 + useCreateIndex: true, useFindAndModify: false
25 + })
26 + .then(() => console.log('MongoDB ---> Connected'))
27 + .catch(err => console.log(err));
44 28
45 - // mongoDB에서 오는 메서드. 정보들이 user model에 저장 29 +app.use(cors())
46 - user.save((err, userInfo) => {
47 - // 만약 에러가 나면, json형식으로 success:false를 보내주고, 에러메시지를 보내줌
48 - if(err) return res.json({success: false, err})
49 - // 성공하면 status(200) (status(200)은 성공했다는 뜻)
50 - return res.status(200).json({
51 - success: true
52 - })
53 - })
54 30
55 -})
56 31
57 -// 로그인 구현 -> 로그인 하면 토큰 생성 32 +app.use('/api/users', require('./routes/users'));
58 -app.post('/api/users/login', (req, res) => {
59 - // 1. 요청된 이메일이 데이터베이스에 있는지 찾기
60 - User.findOne({ email: req.body.email }, (err, user) => {
61 - if(!user)
62 - {
63 - return res.json({
64 - loginSuccess: false,
65 - message: "There is no user with that email."
66 - })
67 - }
68 - // 2. email과 비밀번호가 맞는지 확인 (User.js에 comparePassword 함수 정의되어 있음)
69 - user.comparePassword(req.body.password, (err, isMatch) => {
70 - if(!isMatch)
71 - return res.json({loginSuccess: false, message: "Password is not match."})
72 - // 3. 비밀번호까지 맞다면 유저를 위한 토큰 생성 (User.js에 generateToken 함수 정의)
73 - user.generateToken((err, user) => { // err가 없으면 user에 정보 받아옴
74 - if(err)
75 - return res.status(400).send(err);
76 - // 4. 생성한 토큰을 저장함 -> 쿠키나 로컬 스토리지 등에 저장할 수 있는데 여기선 쿠키에 저장
77 - res.cookie("loginCookie", user.token)
78 - .status(200) //성공했다는 표시
79 - .json({loginSuccess: true, userId: user._id})
80 - })
81 - })
82 - })
83 -})
84 33
85 34
86 -// 인증 구현 (이 사람이 일반유저인지 관리자인지) 35 +// 이미지 가져오려고
87 -app.get('/api/users/auth', auth ,(req,res) => { 36 +app.use('/uploads', express.static('uploads'));
88 - // 여기까지 미들웨어(auth) 통과했으면 authentication == true 라는 뜻
89 - res.status(200).json({
90 - _id: req.user._id,
91 - isAdmin: req.user.role === 0 ? false : true, // role이 0이면 일반 유저, role이 0이 아니면 관리자.
92 - isAuth: true,
93 - email: req.user.email,
94 - lastname: req.user.lastname,
95 - role: req.user.role,
96 - image: req.user.image
97 - })
98 -})
99 -
100 -// 로그아웃 구현 (로그인 때 만든 토큰을 지워버림)
101 -app.get('/api/users/logout', auth, (req, res) => {
102 - User.findOneAndUpdate({_id: req.user._id}, // id로 User를 찾아서 업데이터 시킴
103 - { token: "" }, (err, user) => {
104 - if(err) return res.json({success: false, err});
105 - return res.status(200).send({
106 - success: true
107 - })
108 - })
109 -})
110 37
111 -// test 38 +if (process.env.NODE_ENV === "production") {
112 -app.get('/api/hello', (req, res) => { 39 + app.use(express.static("client/build"));
113 - 40 + app.get("*", (req, res) => {
114 - res.send("hello world"); 41 + res.sendFile(path.resolve(__dirname, "../client", "build", "index.html"));
115 -}) 42 + });
43 +}
116 44
117 app.listen(port, () => { 45 app.listen(port, () => {
118 - console.log(`Example app listening at http://localhost:${port}`)
119 -})
...\ No newline at end of file ...\ No newline at end of file
46 + console.log(`Server ---> http://localhost:${port}`)
47 +});
...\ No newline at end of file ...\ No newline at end of file
......
1 -const { User } = require("../models/User"); 1 +const { User } = require('../models/User');
2 2
3 let auth = (req, res, next) => { 3 let auth = (req, res, next) => {
4 - // 인증 처리 4 + // 인증 처리
5 - // 1. client 쿠키에서 토큰을 가져옴. 5 + // 1. client 쿠키에서 토큰을 가져옴.
6 - let token = req.cookies.loginCookie; 6 + let token = req.cookies.w_auth;
7 7
8 - // 2. 토큰을 복호화한 후 유저를 찾는다. (User.js에 findByToken(); 있음) 8 + // 2. 토큰을 복호화한 후 유저를 찾는다. (User.js에 findByToken(); 있음)
9 - User.findByToken(token, (err, user)=>{ 9 + User.findByToken(token, (err, user)=>{
10 - // 에러가 있으면 10 + // 에러가 있으면
11 - if(err) throw err; 11 + if(err) throw err;
12 - // 유저가 없으면 12 + // 유저가 없으면
13 - if(!user) return res.json({ isAuth:false, error: true}) 13 + if(!user) return res.json({ isAuth:false, error: true})
14 - // 에러도 없고 유저도 있으면 14 + // 에러도 없고 유저도 있으면
15 - req.token = token; // token과 user를 request에 넣어줌으로써 index.js에서 request 사용할 수 있음 15 + req.token = token; // token과 user를 request에 넣어줌으로써 index.js에서 request 사용할 수 있음
16 - req.user = user; 16 + req.user = user;
17 - next(); 17 + next();
18 - }); 18 +});
19 19
20 - // 3. 유저가 있으면 인증OK, 유저가 없으면 인증No! 20 +// 3. 유저가 있으면 인증OK, 유저가 없으면 인증No!
21 } 21 }
22 22
23 // 이 auth를 다른 파일에서도 쓸 수 있도록 23 // 이 auth를 다른 파일에서도 쓸 수 있도록
......
...@@ -5,35 +5,36 @@ const bcrypt = require('bcrypt') ...@@ -5,35 +5,36 @@ const bcrypt = require('bcrypt')
5 // bcrypt 사용하기 위해 salt를 생성하고 그걸 이용해 암호화 시킴 5 // bcrypt 사용하기 위해 salt를 생성하고 그걸 이용해 암호화 시킴
6 const saltRounds = 10 // salt를 몇글자 할 건지 6 const saltRounds = 10 // salt를 몇글자 할 건지
7 7
8 -// 8 +const jwt = require('jsonwebtoken');
9 -const jwt = require('jsonwebtoken') 9 +const moment = require("moment");
10 10
11 const userSchema = mongoose.Schema({ 11 const userSchema = mongoose.Schema({
12 - name:{ 12 + name: {
13 - type: String, 13 + type:String,
14 - maxlength: 50 14 + maxlength:50
15 }, 15 },
16 - email:{ 16 + email: {
17 - type: String, 17 + type:String,
18 - trim: true, // 'minjeong park'처럼 space가 있는 문자에 space가 없어지도록 함 18 + trim:true,
19 - unique: 1 // 똑같은 이메일은 쓸 수 없도록 19 + unique: 1
20 }, 20 },
21 password: { 21 password: {
22 type: String, 22 type: String,
23 - minlength: 5 23 + minglength: 3
24 }, 24 },
25 lastname: { 25 lastname: {
26 - type: String, 26 + type:String,
27 maxlength: 50 27 maxlength: 50
28 }, 28 },
29 - role: { // Number==1 이면 관리자, number==0 이면 일반 유저 29 + role : {
30 - type: Number, 30 + type:Number, // Number==1 이면 관리자, number==0 이면 일반 유저
31 - default: 0 // default는 0 31 + default: 0 // default는 0
32 }, 32 },
33 + image: String,
33 token : { 34 token : {
34 - type : String 35 + type: String,
35 }, 36 },
36 - tokenExp: { //토큰의 유효기간 37 + tokenExp :{
37 type: Number 38 type: Number
38 } 39 }
39 }) 40 })
...@@ -41,11 +42,11 @@ const userSchema = mongoose.Schema({ ...@@ -41,11 +42,11 @@ const userSchema = mongoose.Schema({
41 // index.js의 app.post('/register', (req, res)에 있는 42 // index.js의 app.post('/register', (req, res)에 있는
42 // user model에 user 정보를 저장하기 전에 무엇을 한다는 것 43 // user model에 user 정보를 저장하기 전에 무엇을 한다는 것
43 // function( next )를 해서 얘네가 끝난 다음에 다음걸 실행해라~ 44 // function( next )를 해서 얘네가 끝난 다음에 다음걸 실행해라~
44 -userSchema.pre('save', function( next ){ 45 +userSchema.pre('save', function( next ) {
45 - var user = this 46 + var user = this;
46 - 47 +
47 - if(user.isModified('password')) // password를 변경할 때만 적용되도록.. 48 + if(user.isModified('password')){ // password를 변경할 때만 적용되도록..
48 - { 49 +
49 // 비밀번호 암호화 (https://www.npmjs.com/package/bcrypt 에서 가져옴) 50 // 비밀번호 암호화 (https://www.npmjs.com/package/bcrypt 에서 가져옴)
50 bcrypt.genSalt(saltRounds, (err, salt) => // salt를 만드는 함수 51 bcrypt.genSalt(saltRounds, (err, salt) => // salt를 만드는 함수
51 { 52 {
...@@ -53,18 +54,15 @@ userSchema.pre('save', function( next ){ ...@@ -53,18 +54,15 @@ userSchema.pre('save', function( next ){
53 bcrypt.hash(user.password, salt, (err, hash) => { // bcrypt.hash(암호화되지 않은 pw, salt, function(err, 암호화된 비밀번호)) 54 bcrypt.hash(user.password, salt, (err, hash) => { // bcrypt.hash(암호화되지 않은 pw, salt, function(err, 암호화된 비밀번호))
54 if(err) return next(err) // 에러 나면 return err 55 if(err) return next(err) // 에러 나면 return err
55 user.password = hash // 성공하면 user.password를 hash로 교체 56 user.password = hash // 성공하면 user.password를 hash로 교체
56 - next() 57 + next()
57 - }); 58 + })
58 - }); 59 + })
59 - } 60 + } else {
60 - else
61 - {
62 next() 61 next()
63 } 62 }
63 +});
64 64
65 -}) 65 +userSchema.methods.comparePassword = function(plainPassword,cb){
66 -
67 -userSchema.methods.comparePassword = function(plainPassword, cb){
68 66
69 // 1. plainPassword가 1234567 암호화된 비밀번호 가 같은지 체크해야함 67 // 1. plainPassword가 1234567 암호화된 비밀번호 가 같은지 체크해야함
70 // 그러면 plainPassword도 암호화해서 비교해야함. (복호화 할 수 없기 때문에) 68 // 그러면 plainPassword도 암호화해서 비교해야함. (복호화 할 수 없기 때문에)
...@@ -75,39 +73,35 @@ userSchema.methods.comparePassword = function(plainPassword, cb){ ...@@ -75,39 +73,35 @@ userSchema.methods.comparePassword = function(plainPassword, cb){
75 }) 73 })
76 } 74 }
77 75
78 -userSchema.methods.generateToken = function(cb) 76 +userSchema.methods.generateToken = function(cb) {
79 -{
80 var user = this; 77 var user = this;
81 // jsonwebtoken을 이용해서 token 생성 78 // jsonwebtoken을 이용해서 token 생성
82 var token = jwt.sign(user._id.toHexString(), 'secretToken') //database에 있는 id라서 _id 79 var token = jwt.sign(user._id.toHexString(), 'secretToken') //database에 있는 id라서 _id
80 + var oneHour = moment().add(1, 'hour').valueOf();
83 81
84 - user.token = token 82 + user.tokenExp = oneHour;
85 - user.save(function(err, user){ 83 + user.token = token;
86 - if(err) 84 + user.save(function (err, user){
87 - return cb(err) // 에러가 있다면 callback으로 에러 전달 85 + if(err) return cb(err)// 에러가 있다면 callback으로 에러 전달
88 cb(null, user) // 에러가 없다면 err는 없고 user정보만 전달 86 cb(null, user) // 에러가 없다면 err는 없고 user정보만 전달
89 }) 87 })
90 -
91 } 88 }
92 89
93 -userSchema.statics.findByToken = function(token, cb) 90 +userSchema.statics.findByToken = function (token, cb) {
94 -{
95 var user = this; 91 var user = this;
96 92
97 // 1. 토큰을 decoding 93 // 1. 토큰을 decoding
98 jwt.verify(token, 'secretToken', function(err, decoded) { 94 jwt.verify(token, 'secretToken', function(err, decoded) {
99 - // 2. 유저 아이디를 이용해서 유저를 찾은 다음에 클라이언트에서 가져온 토큰과 DB에 보관된 토큰이 일치하는지 확인. 95 + // 2. 유저 아이디를 이용해서 유저를 찾은 다음에 클라이언트에서 가져온 토큰과 DB에 보관된 토큰이 일치하는지 확인.
100 - user.findOne({"_id": decoded, "token": token}, function(err, user){ // findOne :: mongoDB에 이미 있는 method 96 + user.findOne({"_id": decoded, "token": token}, function(err, user){ // findOne :: mongoDB에 이미 있는 method
101 - // 에러가 나면 97 + // 에러가 나면
102 - if(err) return cb(err); 98 + if(err) return cb(err);
103 - // 에러가 안나면 99 + // 에러가 안나면
104 - cb(null, user) 100 + cb(null, user)
105 - }) 101 + })
106 - }) 102 +})
107 } 103 }
108 104
109 -// 만든 스키마를 모델로 감싸줌 105 +const User = mongoose.model('User', userSchema);
110 -const User = mongoose.model('User', userSchema)
111 106
112 -// 이 모델을 다른 파일에서도 쓰고싶으면 아래와 같이 해주면 됨
113 -module.exports = {User}
...\ No newline at end of file ...\ No newline at end of file
107 +module.exports = { User }
...\ No newline at end of file ...\ No newline at end of file
......
1 +const express = require('express');
2 +const { User } = require("../models/User");
3 +const { auth } = require("../middleware/auth");
4 +const router = express.Router();
5 +
6 +router.get("/auth", auth, (req, res) => {
7 + res.status(200).json({
8 + _id: req.user._id,
9 + isAdmin: req.user.role === 0 ? false : true,
10 + isAuth: true,
11 + email: req.user.email,
12 + name: req.user.name,
13 + lastname: req.user.lastname,
14 + role: req.user.role,
15 + image: req.user.image,
16 + });
17 +});
18 +
19 +router.post("/register", (req, res) => {
20 +
21 + const user = new User(req.body);
22 +
23 + user.save((err, doc) => {
24 + if (err) return res.json({ success: false, err });
25 + return res.status(200).json({
26 + success: true
27 + });
28 + });
29 +});
30 +
31 +router.post("/login", (req, res) => {
32 + User.findOne({ email: req.body.email }, (err, user) => {
33 + if (!user)
34 + return res.json({
35 + loginSuccess: false,
36 + message: "Auth failed, email not found"
37 + });
38 +
39 + user.comparePassword(req.body.password, (err, isMatch) => {
40 + if (!isMatch)
41 + return res.json({ loginSuccess: false, message: "Wrong password" });
42 +
43 + user.generateToken((err, user) => {
44 + if (err) return res.status(400).send(err);
45 + res.cookie("w_authExp", user.tokenExp);
46 + res
47 + .cookie("w_auth", user.token)
48 + .status(200)
49 + .json({
50 + loginSuccess: true, userId: user._id
51 + });
52 + });
53 + });
54 + });
55 +});
56 +
57 +router.get("/logout", auth, (req, res) => {
58 + User.findOneAndUpdate({ _id: req.user._id }, { token: "", tokenExp: "" }, (err, doc) => {
59 + if (err) return res.json({ success: false, err });
60 + return res.status(200).send({
61 + success: true
62 + });
63 + });
64 +});
65 +
66 +module.exports = router;