Showing
16 changed files
with
197 additions
and
47 deletions
This diff is collapsed. Click to expand it.
... | @@ -6,9 +6,17 @@ | ... | @@ -6,9 +6,17 @@ |
6 | "@testing-library/jest-dom": "^4.2.4", | 6 | "@testing-library/jest-dom": "^4.2.4", |
7 | "@testing-library/react": "^9.5.0", | 7 | "@testing-library/react": "^9.5.0", |
8 | "@testing-library/user-event": "^7.2.1", | 8 | "@testing-library/user-event": "^7.2.1", |
9 | + "antd": "^4.3.4", | ||
10 | + "axios": "^0.19.2", | ||
11 | + "http-proxy-middleware": "^1.0.4", | ||
9 | "react": "^16.13.1", | 12 | "react": "^16.13.1", |
10 | "react-dom": "^16.13.1", | 13 | "react-dom": "^16.13.1", |
11 | - "react-scripts": "3.4.1" | 14 | + "react-redux": "^7.2.0", |
15 | + "react-router-dom": "^5.2.0", | ||
16 | + "react-scripts": "3.4.1", | ||
17 | + "redux": "^4.0.5", | ||
18 | + "redux-promise": "^0.6.0", | ||
19 | + "redux-thunk": "^2.3.0" | ||
12 | }, | 20 | }, |
13 | "scripts": { | 21 | "scripts": { |
14 | "start": "react-scripts start", | 22 | "start": "react-scripts start", | ... | ... |
1 | -import React from 'react'; | 1 | +import React from "react"; |
2 | -import logo from './logo.svg'; | 2 | +import { |
3 | -import './App.css'; | 3 | + BrowserRouter as Router, |
4 | + Switch, | ||
5 | + Route, | ||
6 | + Link | ||
7 | +} from "react-router-dom"; | ||
4 | 8 | ||
9 | +import LandingPage from './components/views/LandingPage/LandingPage'; | ||
10 | +import LoginPage from './components/views/LoginPage/LoginPage'; | ||
11 | +import RegisterPage from './components/views/RegisterPage/RegisterPage'; | ||
5 | function App() { | 12 | function App() { |
6 | return ( | 13 | return ( |
7 | - <div className="App"> | 14 | + <Router> |
8 | - <header className="App-header"> | 15 | + <div> |
9 | - <img src={logo} className="App-logo" alt="logo" /> | 16 | + |
10 | - <p> | 17 | + |
11 | - Edit <code>src/App.js</code> and save to reload. | 18 | + {/* |
12 | - </p> | 19 | + A <Switch> looks through all its children <Route> |
13 | - <a | 20 | + elements and renders the first one whose path |
14 | - className="App-link" | 21 | + matches the current URL. Use a <Switch> any time |
15 | - href="https://reactjs.org" | 22 | + you have multiple routes, but you want only one |
16 | - target="_blank" | 23 | + of them to render at a time |
17 | - rel="noopener noreferrer" | 24 | + */} |
18 | - > | 25 | + <Switch> |
19 | - Learn React | 26 | + <Route exact path="/" component={LandingPage}/> |
20 | - </a> | 27 | + |
21 | - </header> | 28 | + <Route exact path="/login" component={LoginPage}/> |
22 | - </div> | 29 | + |
30 | + <Route exact path="/register" component = {RegisterPage} /> | ||
31 | + | ||
32 | + </Switch> | ||
33 | + </div> | ||
34 | + </Router> | ||
23 | ); | 35 | ); |
24 | } | 36 | } |
25 | 37 | ||
26 | -export default App; | 38 | +export default App |
39 | + | ||
40 | + | ... | ... |
client/src/_actions/types.js
0 → 100644
1 | +export const LOGIN_USER = "login_user"; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
client/src/_actions/user_actions.js
0 → 100644
1 | +import axios from 'axios'; | ||
2 | + | ||
3 | +import { | ||
4 | + LOGIN_USER | ||
5 | +} from './types'; | ||
6 | + | ||
7 | +export function loginUser(dataTosubmit){ | ||
8 | + const request = axios.post('/api/users/login', dataTosubmit) | ||
9 | + .then(response => response.data) | ||
10 | + | ||
11 | + return { | ||
12 | + type: LOGIN_USER, | ||
13 | + payload: request | ||
14 | + } | ||
15 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
client/src/_reducers/index.js
0 → 100644
client/src/_reducers/user_reducer.js
0 → 100644
1 | +import { | ||
2 | + LOGIN_USER | ||
3 | +} from '../_actions/types'; | ||
4 | + | ||
5 | +export default function (state={}, action){ | ||
6 | + switch (action.type) { | ||
7 | + case LOGIN_USER: | ||
8 | + return { ...state, loginSuccess: action.payload} | ||
9 | + break; | ||
10 | + | ||
11 | + default: | ||
12 | + return state; | ||
13 | + } | ||
14 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -import React from 'react' | 1 | +import React, {useEffect} from 'react' |
2 | +import axios from 'axios'; | ||
2 | 3 | ||
3 | -function LandingPage(){ | 4 | +function LandingPage() { |
4 | - return( | 5 | + useEffect(() => { |
5 | - <div> | 6 | + axios.get('/api/hello') |
7 | + .then(response => {console.log(response)}) | ||
8 | + }, []) | ||
6 | 9 | ||
7 | - </div> | ||
8 | 10 | ||
11 | + return ( | ||
12 | + <div style = {{ | ||
13 | + display: 'flex', justifyContent: 'center', alignItems: 'center' | ||
14 | + , width: '100%', height: '100vh' | ||
15 | + }}> | ||
16 | + <h2> 시작 페이지 </h2> | ||
17 | + </div> | ||
9 | ) | 18 | ) |
10 | } | 19 | } |
11 | 20 | ||
12 | -export default LandingPage | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
21 | +export default LandingPage | ... | ... |
1 | -import React from 'react' | 1 | +import React,{useState} from 'react' |
2 | +import Axios from 'axios' | ||
3 | +import { useDispatch} from 'react-redux'; | ||
4 | +import {loginUser} from '../../../_actions/user_actions'; | ||
2 | 5 | ||
3 | function LoginPage(){ | 6 | function LoginPage(){ |
7 | + const dispatch = useDispatch(); | ||
8 | + const [Email, setEmail] = useState("") | ||
9 | + const [PassWord, setPassWord] = useState("") | ||
10 | + | ||
11 | + const onEmailHandler = (event) => { | ||
12 | + setEmail(event.currentTarget.value) | ||
13 | + } | ||
14 | + | ||
15 | + const onPassWordHandler = (event) => { | ||
16 | + setPassWord(event.currentTarget.value) | ||
17 | + } | ||
18 | + | ||
19 | + const onSubmitHandler = (event) => { | ||
20 | + event.preventDefault(); | ||
21 | + | ||
22 | + | ||
23 | + let body = { | ||
24 | + email: Email, | ||
25 | + password: PassWord | ||
26 | + } | ||
27 | + | ||
28 | + dispatch(loginUser(body)) | ||
29 | + | ||
30 | + | ||
31 | + } | ||
32 | + | ||
4 | return ( | 33 | return ( |
5 | - <div> | 34 | + <div style = {{ |
6 | - LoginPage | 35 | + display: 'flex', justifyContent: 'center', alignItems: 'center' |
7 | - </div> | 36 | + , width: '100%', height: '100vh' |
37 | + }}> | ||
38 | + <form style = {{display :'flex', flexDirection: 'column'}} | ||
39 | + onSubmit= {onSubmitHandler} | ||
40 | + > | ||
41 | + <label>Email</label> | ||
42 | + <input type = "email" value = {Email} onChange={onEmailHandler} /> | ||
43 | + <label>PassWord</label> | ||
44 | + <input type = "password" value= {PassWord} onChange = {onPassWordHandler} /> | ||
45 | + <br /> | ||
46 | + <button type = "submit"> | ||
47 | + Login | ||
48 | + </button> | ||
49 | + | ||
50 | + </form> | ||
51 | + </div> | ||
8 | ) | 52 | ) |
9 | 53 | ||
10 | } | 54 | } | ... | ... |
... | @@ -3,12 +3,30 @@ import ReactDOM from 'react-dom'; | ... | @@ -3,12 +3,30 @@ import ReactDOM from 'react-dom'; |
3 | import './index.css'; | 3 | import './index.css'; |
4 | import App from './App'; | 4 | import App from './App'; |
5 | import * as serviceWorker from './serviceWorker'; | 5 | import * as serviceWorker from './serviceWorker'; |
6 | +import {Provider} from 'react-redux'; | ||
7 | +import 'antd/dist/antd.css'; | ||
8 | +import { applyMiddleware, createStore} from 'redux'; | ||
9 | +import promiseMiddleware from 'redux-promise'; | ||
10 | +import ReduxThunk from 'redux-thunk'; | ||
11 | +import Reducer from './_reducers/index'; | ||
6 | 12 | ||
13 | +const creatStoreWithMiddleware = applyMiddleware(promiseMiddleware,ReduxThunk)(createStore) | ||
7 | ReactDOM.render( | 14 | ReactDOM.render( |
8 | - <React.StrictMode> | 15 | + <Provider |
9 | - <App /> | 16 | + store={creatStoreWithMiddleware(Reducer, |
10 | - </React.StrictMode>, | 17 | + window.__REDUX_DEVTOOLS_EXTENTION__&& |
11 | - document.getElementById('root') | 18 | + window.__REDUX_DEVTOOLS_EXTENTION__() |
19 | + | ||
20 | + )} | ||
21 | + > | ||
22 | + <App /> | ||
23 | + </Provider> | ||
24 | + | ||
25 | + | ||
26 | + | ||
27 | + ,document.getElementById('root') | ||
28 | + | ||
29 | + | ||
12 | ); | 30 | ); |
13 | 31 | ||
14 | // If you want your app to work offline and load faster, you can change | 32 | // If you want your app to work offline and load faster, you can change | ... | ... |
client/src/setupProxy.js
0 → 100644
This diff is collapsed. Click to expand it.
... | @@ -5,8 +5,9 @@ | ... | @@ -5,8 +5,9 @@ |
5 | "main": "index.js", | 5 | "main": "index.js", |
6 | "scripts": { | 6 | "scripts": { |
7 | "start": "node index.js", | 7 | "start": "node index.js", |
8 | - "backend": "nodemon index.js", | 8 | + "backend": "nodemon server/index.js", |
9 | - "test": "echo \"Error: no test specified\" && exit 1" | 9 | + "test": "echo \"Error: no test specified\" && exit 1", |
10 | + "dev": "concurrently \"npm run backend \" \"npm run start --prefix client\"" | ||
10 | }, | 11 | }, |
11 | "repository": { | 12 | "repository": { |
12 | "type": "git", | 13 | "type": "git", |
... | @@ -17,6 +18,7 @@ | ... | @@ -17,6 +18,7 @@ |
17 | "dependencies": { | 18 | "dependencies": { |
18 | "bcrypt": "^4.0.1", | 19 | "bcrypt": "^4.0.1", |
19 | "body-parser": "^1.19.0", | 20 | "body-parser": "^1.19.0", |
21 | + "concurrently": "^5.2.0", | ||
20 | "cookie-parser": "^1.4.5", | 22 | "cookie-parser": "^1.4.5", |
21 | "express": "^4.17.1", | 23 | "express": "^4.17.1", |
22 | "jsonwebtoken": "^8.5.1", | 24 | "jsonwebtoken": "^8.5.1", | ... | ... |
... | @@ -22,7 +22,7 @@ mongoose.connect(config.mongoURI,{ | ... | @@ -22,7 +22,7 @@ mongoose.connect(config.mongoURI,{ |
22 | 22 | ||
23 | 23 | ||
24 | 24 | ||
25 | -app.get('/', (req,res) => res.send('Hello world!! 오늘도 지식이 쌓였당!!')) | 25 | +app.get('/api/hello', (req,res) => res.send('Hello world!! 오늘도 지식이 쌓였당!!')) |
26 | 26 | ||
27 | app.post('/api/users/register', (req, res) => { | 27 | app.post('/api/users/register', (req, res) => { |
28 | // 회원 가입시 필요한 정보들을 client에서 가져오면 | 28 | // 회원 가입시 필요한 정보들을 client에서 가져오면 | ... | ... |
... | @@ -23,4 +23,4 @@ let auth = (req,res,next) => { | ... | @@ -23,4 +23,4 @@ let auth = (req,res,next) => { |
23 | 23 | ||
24 | } | 24 | } |
25 | 25 | ||
26 | -module.exports = {auth}; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
26 | +module.exports = {auth} | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -2,6 +2,7 @@ const mongoose = require('mongoose'); | ... | @@ -2,6 +2,7 @@ const mongoose = require('mongoose'); |
2 | const bcrypt = require('bcrypt'); | 2 | const bcrypt = require('bcrypt'); |
3 | const saltRounds = 10; | 3 | const saltRounds = 10; |
4 | const jwt = require('jsonwebtoken'); | 4 | const jwt = require('jsonwebtoken'); |
5 | + | ||
5 | const userSchema = mongoose.Schema({ | 6 | const userSchema = mongoose.Schema({ |
6 | name : { | 7 | name : { |
7 | type : String, | 8 | type : String, |
... | @@ -71,16 +72,23 @@ userSchema.methods.generateToken = function(cb){ | ... | @@ -71,16 +72,23 @@ userSchema.methods.generateToken = function(cb){ |
71 | if(err) return cb(err) | 72 | if(err) return cb(err) |
72 | cb(null, user) | 73 | cb(null, user) |
73 | }) | 74 | }) |
74 | - | ||
75 | - | ||
76 | - | ||
77 | - | ||
78 | } | 75 | } |
79 | 76 | ||
77 | +userSchema.statics.findByToken = function(token, cb){ | ||
78 | + var user = this; | ||
79 | + // 토큰을 복호화한다. | ||
80 | + | ||
81 | + jwt.verify(token,'secretToken', function(err, decoded){ | ||
82 | + // 유저 아이디를 이용해서 유저를 찾고 | ||
83 | + // 클라이언트에서 가져온 토큰과 데이터베이스에 보관된 토큰이 | ||
84 | + //일치하는지 확인 | ||
85 | + user.findOne({"_id": decoded, "token" : token}, function(err, user){ | ||
86 | + if(err) return cb(err); | ||
87 | + cb(null, user) | ||
88 | + }) | ||
89 | + }) | ||
90 | +} | ||
80 | 91 | ||
81 | const User = mongoose.model('Users', userSchema) | 92 | const User = mongoose.model('Users', userSchema) |
82 | 93 | ||
83 | - | 94 | +module.exports = {User} |
84 | - | ||
85 | -module.exports = {User} | ||
86 | - | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or login to post a comment