Showing
38 changed files
with
400 additions
and
61 deletions
File moved
This diff is collapsed. Click to expand it.
... | @@ -6,12 +6,17 @@ | ... | @@ -6,12 +6,17 @@ |
6 | "@testing-library/jest-dom": "^5.12.0", | 6 | "@testing-library/jest-dom": "^5.12.0", |
7 | "@testing-library/react": "^11.2.7", | 7 | "@testing-library/react": "^11.2.7", |
8 | "@testing-library/user-event": "^12.8.3", | 8 | "@testing-library/user-event": "^12.8.3", |
9 | + "antd": "^4.16.1", | ||
9 | "axios": "^0.21.1", | 10 | "axios": "^0.21.1", |
10 | "http-proxy-middleware": "^2.0.0", | 11 | "http-proxy-middleware": "^2.0.0", |
11 | "react": "^17.0.2", | 12 | "react": "^17.0.2", |
12 | "react-dom": "^17.0.2", | 13 | "react-dom": "^17.0.2", |
14 | + "react-redux": "^7.2.4", | ||
13 | "react-router-dom": "^5.2.0", | 15 | "react-router-dom": "^5.2.0", |
14 | "react-scripts": "4.0.3", | 16 | "react-scripts": "4.0.3", |
17 | + "redux": "^4.1.0", | ||
18 | + "redux-promise": "^0.6.0", | ||
19 | + "redux-thunk": "^2.3.0", | ||
15 | "web-vitals": "^1.1.2" | 20 | "web-vitals": "^1.1.2" |
16 | }, | 21 | }, |
17 | "scripts": { | 22 | "scripts": { | ... | ... |
No preview for this file type
1 | - | ||
2 | import React from "react"; | 1 | import React from "react"; |
3 | import { | 2 | import { |
4 | BrowserRouter as Router, | 3 | BrowserRouter as Router, |
5 | Switch, | 4 | Switch, |
6 | Route, | 5 | Route, |
7 | - Link | 6 | + //Link |
8 | } from "react-router-dom"; | 7 | } from "react-router-dom"; |
9 | 8 | ||
10 | import LandingPage from './components/views/LandingPage/LandingPage' | 9 | import LandingPage from './components/views/LandingPage/LandingPage' |
11 | import LoginPage from './components/views/LoginPage/LoginPage' | 10 | import LoginPage from './components/views/LoginPage/LoginPage' |
12 | import RegisterPage from './components/views/RegisterPage/RegisterPage' | 11 | import RegisterPage from './components/views/RegisterPage/RegisterPage' |
13 | - | 12 | +import auth from './hoc/authentication' |
14 | 13 | ||
15 | function App() { | 14 | function App() { |
16 | return ( | 15 | return ( |
17 | <Router> | 16 | <Router> |
18 | <div> | 17 | <div> |
19 | - {/* | ||
20 | - A <Switch> looks through all its children <Route> | ||
21 | - elements and renders the first one whose path | ||
22 | - matches the current URL. Use a <Switch> any time | ||
23 | - you have multiple routes, but you want only one | ||
24 | - of them to render at a time | ||
25 | - */} | ||
26 | <Switch> | 18 | <Switch> |
27 | - <Route exact path="/"> | 19 | + {/* null, false에 대한 옵션 설명은 auth로 가서 확인*/} |
28 | - <LandingPage /> | 20 | + |
29 | - </Route> {/* | 21 | + <Route exact path="/" component={auth(LandingPage, null)} /> |
30 | - <Route exact path="/" component={LandingPage} /> 로 해도 똑같은 결과가 나옴! | 22 | + |
31 | - */} | 23 | + <Route exact path="/login" component={auth(LoginPage, false)}/> |
24 | + | ||
25 | + <Route exact path="/register" component={auth(RegisterPage, false)}/> | ||
26 | + | ||
27 | + {/* | ||
28 | + <Route exact path="/" component={LandingPage} /> | ||
32 | 29 | ||
33 | - <Route path="/login"> | 30 | + <Route exact path="/login" component={LoginPage}/> |
34 | - <LoginPage /> | ||
35 | - </Route> | ||
36 | 31 | ||
37 | - <Route path="/register"> | 32 | + <Route exact path="/register" component={RegisterPage}/> |
38 | - <RegisterPage /> | 33 | + */} |
39 | - </Route> | 34 | + </Switch> |
40 | - </Switch> | ||
41 | </div> | 35 | </div> |
42 | </Router> | 36 | </Router> |
43 | ); | 37 | ); | ... | ... |
We-Shop/client/src/_actions/types.js
0 → 100644
We-Shop/client/src/_actions/user_action.js
0 → 100644
1 | +import axios from 'axios'; | ||
2 | +import { | ||
3 | + LOGIN_USER, | ||
4 | + REGISTER_USER, | ||
5 | + AUTH_USER | ||
6 | +} from './types'; | ||
7 | +export function loginUser(logInfo) { | ||
8 | + const request = axios.post('/api/users/login', logInfo) // logInfo를 post로 전달 | ||
9 | + .then(response => response.data); // 서버에서 받은 데이터를 request에 저장 | ||
10 | + | ||
11 | + return { // return을 통해 Reducer로 보냄 | ||
12 | + // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState | ||
13 | + // request를 reducer로 보내는 작업 | ||
14 | + | ||
15 | + // action은 type과 response을 넣어줘야 함 | ||
16 | + type: "LOGIN_USER", | ||
17 | + payload: request // payroad == response | ||
18 | + } | ||
19 | +} | ||
20 | + | ||
21 | +export function RegisterUser(regInfo) { | ||
22 | + const request = axios.post('/api/users/register', regInfo) // logInfo를 post로 전달 | ||
23 | + .then(response => response.data); // 서버에서 받은 데이터를 request에 저장 | ||
24 | + | ||
25 | + return { // return을 통해 Reducer로 보냄. | ||
26 | + // Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState | ||
27 | + // request를 reducer로 보내는 작업 | ||
28 | + | ||
29 | + // action은 type과 response을 넣어줘야 함 | ||
30 | + type: "REGISTER_USER", | ||
31 | + payload: request // payroad == response | ||
32 | + } | ||
33 | +} | ||
34 | + | ||
35 | +export function auth() { | ||
36 | + const request = axios.get('/api/users/auth') // logInfo를 post로 전달 | ||
37 | + .then(response => response.data); // 서버에서 받은 데이터를 request에 저장 | ||
38 | + | ||
39 | + return { | ||
40 | + | ||
41 | + type: "AUTH_USER", | ||
42 | + payload: request // payroad == response | ||
43 | + } | ||
44 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
We-Shop/client/src/_reducers/index.js
0 → 100644
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'; | ||
9 | +import user from './user_reducer'; // user(회원가입, 로그인, 인증, 로그아웃 기능이 있음) reducer | ||
10 | +// import comment from './comment_reducer'; // comment기능이 있을 때 reducer | ||
11 | + | ||
12 | +const rootReducer = combineReducers( { | ||
13 | + user | ||
14 | +}) | ||
15 | + | ||
16 | +// 다른 곳에서도 rootReducer을 쓸 수 있도록 | ||
17 | +export default rootReducer; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
We-Shop/client/src/_reducers/user_reducer.js
0 → 100644
1 | +import { | ||
2 | + LOGIN_USER, | ||
3 | + REGISTER_USER, | ||
4 | + AUTH_USER | ||
5 | +} from '../_actions/types'; | ||
6 | + | ||
7 | + | ||
8 | +// reducer은 (previousState, action) => (nextState)로 | ||
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: | ||
17 | + return {...prevState, success:action.payload} | ||
18 | + // success는 server/index.js 에서 register에 성공하면 json type으로 success: true를 전달하라고 했기 때문 | ||
19 | + break; | ||
20 | + | ||
21 | + case AUTH_USER: | ||
22 | + return {...prevState, user:action.payload} | ||
23 | + break; | ||
24 | + default: | ||
25 | + return prevState; | ||
26 | + } | ||
27 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +import React, {useEffect} from 'react' | ||
2 | +import axios from 'axios' | ||
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 | + | ||
24 | + return ( | ||
25 | + <div style={{justifyContent:'center', alignItems: 'center', display:'flex', width:'100%'}}> | ||
26 | + <h1>시작 페이지</h1> | ||
27 | + <button onClick ={onLogoutClickedEvent}> Logout </button> | ||
28 | + </div> | ||
29 | + | ||
30 | + | ||
31 | + ) | ||
32 | +} | ||
33 | + | ||
34 | + | ||
35 | +export default withRouter(LandingPage) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | +import axios from 'axios'; | ||
2 | +//import { response } from 'express'; | ||
3 | +import React from 'react' | ||
4 | +import {useState} from 'react' | ||
5 | +import {useDispatch} from 'react-redux'; | ||
6 | +import {loginUser} from '../../../_actions/user_action' | ||
7 | +import { withRouter } from 'react-router-dom'; | ||
8 | + | ||
9 | +function LoginPage(props) { | ||
10 | + | ||
11 | + | ||
12 | + // 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용. | ||
13 | + // 1-1. state을 사용하기 위해 state 만들어줌. | ||
14 | + const [Email, setEmail] = useState(""); // 1-2. email을 위한 state | ||
15 | + const [Password, setPassword] = useState(""); // 1-2. password를 위한 state | ||
16 | + //1-3. 아래 input value에 넣어줌 | ||
17 | + | ||
18 | + // 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌 | ||
19 | + const emailEvent = (event) => { | ||
20 | + setEmail(event.currentTarget.value) | ||
21 | + } | ||
22 | + const passwordEvent = (event) => { | ||
23 | + setPassword(event.currentTarget.value) | ||
24 | + | ||
25 | + } | ||
26 | + | ||
27 | + const dispatch = useDispatch(); | ||
28 | + const submitEvent = (event) => { | ||
29 | + event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음 | ||
30 | + | ||
31 | + //console.log('Email', Email); // 잘 나오는지 확인 | ||
32 | + //console.log('Password', Password); // 잘 나오는지 확인 | ||
33 | + | ||
34 | + let logInfo = { // 보내주기 위해 저장 | ||
35 | + email: Email, | ||
36 | + password: Password | ||
37 | + } | ||
38 | + | ||
39 | + dispatch(loginUser(logInfo)) // _actions폴더 user_action.js에 있음 | ||
40 | + .then(response => { | ||
41 | + if(response.payload.loginSuccess) | ||
42 | + props.history.push('/'); | ||
43 | + | ||
44 | + else | ||
45 | + alert('Error'); | ||
46 | + | ||
47 | + | ||
48 | + }) | ||
49 | + | ||
50 | + } | ||
51 | + return ( | ||
52 | + <div style={{ | ||
53 | + justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh' | ||
54 | + }}> | ||
55 | + <form onSubmit={submitEvent}> | ||
56 | + <label>Email</label> | ||
57 | + <input type="email" value={Email} onChange={emailEvent} /> | ||
58 | + {/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */} | ||
59 | + <label>Password</label> | ||
60 | + <input type="password" value={Password} onChange={passwordEvent} /> | ||
61 | + <br/> | ||
62 | + <button> | ||
63 | + Login | ||
64 | + </button> | ||
65 | + </form> | ||
66 | + </div> | ||
67 | + ) | ||
68 | +} | ||
69 | + | ||
70 | +export default withRouter(LoginPage) |
1 | +import React from 'react' | ||
2 | +import {useState} from 'react' | ||
3 | +import {useDispatch} from 'react-redux'; | ||
4 | +import {RegisterUser} from '../../../_actions/user_action' | ||
5 | +import { withRouter } from 'react-router-dom'; | ||
6 | + | ||
7 | +function RegisterPage(props) { | ||
8 | + | ||
9 | + // 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용. | ||
10 | + // 1-1. state을 사용하기 위해 state 만들어줌. | ||
11 | + const [Name, setName] = useState(""); | ||
12 | + const [Email, setEmail] = useState(""); // 1-2. email을 위한 state | ||
13 | + const [Password, setPassword] = useState(""); // 1-2. password를 위한 state | ||
14 | + const [Password2, setPassword2] = useState(""); | ||
15 | + | ||
16 | + //1-3. 아래 input value에 넣어줌 | ||
17 | + | ||
18 | + // 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌 | ||
19 | + const emailEvent = (event) => { | ||
20 | + setEmail(event.currentTarget.value) | ||
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 | + | ||
35 | + const dispatch = useDispatch(); | ||
36 | + const submitEvent = (event) => { | ||
37 | + event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음 | ||
38 | + | ||
39 | + //console.log('Email', Email); // 잘 나오는지 확인 | ||
40 | + //console.log('Password', Password); // 잘 나오는지 확인 | ||
41 | + | ||
42 | + // 비밀번호 두개가 같아야 회원가입이 되도록 | ||
43 | + if(Password !== Password2) | ||
44 | + return alert('비밀번호가 일치하지 않습니다.') | ||
45 | + | ||
46 | + | ||
47 | + let regiInfo = { // 보내주기 위해 저장 | ||
48 | + name : Name, | ||
49 | + email: Email, | ||
50 | + password: Password | ||
51 | + } | ||
52 | + | ||
53 | + dispatch(RegisterUser(regiInfo)) // _actions폴더 user_action.js에 있음 | ||
54 | + .then(response => { | ||
55 | + if(response.payload.success) | ||
56 | + props.history.push('/login'); | ||
57 | + | ||
58 | + else | ||
59 | + alert('Fail to sign up'); | ||
60 | + }) | ||
61 | + | ||
62 | + } | ||
63 | + | ||
64 | + return ( | ||
65 | + <div style={{ | ||
66 | + justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh' | ||
67 | + }}> | ||
68 | + <form onSubmit={submitEvent} style={{display: 'flex', flexDirection: 'column'}}> | ||
69 | + | ||
70 | + <label>Name</label> | ||
71 | + <input type="text" value={Name} onChange={NameEvent} /> | ||
72 | + | ||
73 | + <label>Email</label> | ||
74 | + <input type="email" value={Email} onChange={emailEvent} /> | ||
75 | + {/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */} | ||
76 | + | ||
77 | + | ||
78 | + <label>Password</label> | ||
79 | + <input type="password" value={Password} onChange={passwordEvent} /> | ||
80 | + | ||
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 | + | ||
93 | +export default withRouter(RegisterPage) |
We-Shop/client/src/hoc/authentication.js
0 → 100644
1 | +// 사용자의 상태를 보고 | ||
2 | +// 해당 페이지에 들어갈 수 있게 할지 안할지 결정해 줌. hoc 이용. | ||
3 | +import axios from 'axios'; | ||
4 | +import React, {useEffect} from 'react'; | ||
5 | +//import {useEffect} from 'react'; | ||
6 | +import {useDispatch} from 'react-redux'; | ||
7 | +import {auth} from '../_actions/user_action' | ||
8 | + | ||
9 | + | ||
10 | +export default function (SpecificComponent, option, adminRoute = null){ | ||
11 | + // ㄴ option 종류 | ||
12 | + // - null 아무나 출입 가능한 페이지 | ||
13 | + // - true 로그인한 유저만 출입 가능 | ||
14 | + // - false 로그인한 유저 출입 불가능 | ||
15 | + | ||
16 | + function AuthenticationCheck(props) { | ||
17 | + //1. backend에 request를 날려서 사용자의 상태를 확인 | ||
18 | + const dispatch = useDispatch(); // 1-1. dispatch 사용 | ||
19 | + useEffect(() => { | ||
20 | + dispatch(auth()) // 페이지가 이동할 때마다 dispatch가 작동해서 backend에 request를 줌 | ||
21 | + .then(response => { // 받은 response | ||
22 | + console.log(response); | ||
23 | + | ||
24 | + | ||
25 | + // 로그인 안했다면 | ||
26 | + if(!response.payload.isAuth) { | ||
27 | + if(option) { // 만약 위 option이 true이면 (로그인한 유저만 출입 가능한 페이지로 가게 하려면) | ||
28 | + props.history.push('/login'); // 로그인 하게 함 | ||
29 | + } | ||
30 | + } | ||
31 | + | ||
32 | + // 로그인 했다면 | ||
33 | + else { | ||
34 | + if(adminRoute && !response.payload.isAdmin) { // admin만 갈 수 있는 페이지를 admin이 false 사람이 들어가려 한다면 | ||
35 | + props.history.push('/'); // 홈페이지로 돌아가게 함 | ||
36 | + } | ||
37 | + else { | ||
38 | + if(option===false) {// 로그인한 유저가 출입 불가능한 곳을 가려고 한다면 | ||
39 | + props.history.push('/'); // 홈페이지로 돌아가게 함 | ||
40 | + | ||
41 | + } | ||
42 | + } | ||
43 | + } | ||
44 | + }) | ||
45 | + },[]) | ||
46 | + | ||
47 | + return ( | ||
48 | + <SpecificComponent /> | ||
49 | + ) | ||
50 | + | ||
51 | + } | ||
52 | + | ||
53 | + return AuthenticationCheck | ||
54 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
We-Shop/client/src/index.js
0 → 100644
1 | +import React from 'react'; | ||
2 | +import ReactDOM from 'react-dom'; | ||
3 | +import './index.css'; | ||
4 | +import App from './App'; | ||
5 | +import reportWebVitals from './reportWebVitals'; | ||
6 | +import { Provider } from 'react-redux' // app에 redux를 연결시켜주기 위해 redux에서 제공하는 provider 사용 | ||
7 | +import { createStore } from 'redux'; // redux에서 createStore 가져옴. | ||
8 | +import { applyMiddleware } from 'redux'; // object만 받는 store가 promise나 functions도 받기 위해 middleware을 사용함 | ||
9 | +import promiseMiddleware from 'redux-promise'; | ||
10 | +import ReduxThunk from 'redux-thunk'; | ||
11 | +import Reducer from './_reducers'; | ||
12 | + | ||
13 | +import 'antd/dist/antd.css'; | ||
14 | + | ||
15 | + | ||
16 | +const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore) | ||
17 | + | ||
18 | +ReactDOM.render( | ||
19 | + // App에 Redux를 연결 | ||
20 | + <Provider | ||
21 | + store={createStoreWithMiddleware(Reducer, | ||
22 | + window.__REDUX_DEVTOOLS_EXTENSION__ && | ||
23 | + window.__REDUX_DEVTOOLS_EXTENSION__() | ||
24 | + )} | ||
25 | + > | ||
26 | + <App /> | ||
27 | + </Provider> | ||
28 | + , document.getElementById('root') | ||
29 | +); | ||
30 | + | ||
31 | +// If you want to start measuring performance in your app, pass a function | ||
32 | +// to log resu lts (for example: reportWebVitals(console.log)) | ||
33 | +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | ||
34 | +reportWebVitals(); |
boiler-plate/client/src/index.js
deleted
100644 → 0
1 | -import React from 'react'; | ||
2 | -import ReactDOM from 'react-dom'; | ||
3 | -import './index.css'; | ||
4 | -import App from './App'; | ||
5 | -import reportWebVitals from './reportWebVitals'; | ||
6 | - | ||
7 | -ReactDOM.render( | ||
8 | - <React.StrictMode> | ||
9 | - <App /> | ||
10 | - </React.StrictMode>, | ||
11 | - document.getElementById('root') | ||
12 | -); | ||
13 | - | ||
14 | -// If you want to start measuring performance in your app, pass a function | ||
15 | -// to log results (for example: reportWebVitals(console.log)) | ||
16 | -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals | ||
17 | -reportWebVitals(); |
-
Please register or login to post a comment