박민정

[merge] Merge client into develop

Showing 38 changed files with 400 additions and 61 deletions
...@@ -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": {
......
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 );
......
1 +// type들만 관리하는 곳
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
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
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
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)
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
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();
1 -import React from 'react'
2 -
3 -function LoginPage() {
4 - return (
5 - <div>
6 - LoginPage
7 - </div>
8 - )
9 -}
10 -
11 -export default LoginPage
1 -import React from 'react'
2 -
3 -function RegisterPage() {
4 - return (
5 - <div>
6 - RegisterPage
7 - </div>
8 - )
9 -}
10 -
11 -export default RegisterPage
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();