박민정

[merge] Merge client into develop

Showing 38 changed files with 400 additions and 61 deletions
......@@ -6,12 +6,17 @@
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"antd": "^4.16.1",
"axios": "^0.21.1",
"http-proxy-middleware": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"redux": "^4.1.0",
"redux-promise": "^0.6.0",
"redux-thunk": "^2.3.0",
"web-vitals": "^1.1.2"
},
"scripts": {
......
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
//Link
} from "react-router-dom";
import LandingPage from './components/views/LandingPage/LandingPage'
import LoginPage from './components/views/LoginPage/LoginPage'
import RegisterPage from './components/views/RegisterPage/RegisterPage'
import auth from './hoc/authentication'
function App() {
return (
<Router>
<div>
{/*
A <Switch> looks through all its children <Route>
elements and renders the first one whose path
matches the current URL. Use a <Switch> any time
you have multiple routes, but you want only one
of them to render at a time
*/}
<Switch>
<Route exact path="/">
<LandingPage />
</Route> {/*
<Route exact path="/" component={LandingPage} /> 로 해도 똑같은 결과가 나옴!
*/}
{/* null, false에 대한 옵션 설명은 auth로 가서 확인*/}
<Route exact path="/" component={auth(LandingPage, null)} />
<Route exact path="/login" component={auth(LoginPage, false)}/>
<Route exact path="/register" component={auth(RegisterPage, false)}/>
{/*
<Route exact path="/" component={LandingPage} />
<Route path="/login">
<LoginPage />
</Route>
<Route exact path="/login" component={LoginPage}/>
<Route path="/register">
<RegisterPage />
</Route>
</Switch>
<Route exact path="/register" component={RegisterPage}/>
*/}
</Switch>
</div>
</Router>
);
......
// type들만 관리하는 곳
export const LOGIN_USER = "login_user";
export const REGISTER_USER = "resgier_user";
export const AUTH_USER = "auth_user";
\ No newline at end of file
import axios from 'axios';
import {
LOGIN_USER,
REGISTER_USER,
AUTH_USER
} from './types';
export function loginUser(logInfo) {
const request = axios.post('/api/users/login', logInfo) // logInfo를 post로 전달
.then(response => response.data); // 서버에서 받은 데이터를 request에 저장
return { // return을 통해 Reducer로 보냄
// Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
// request를 reducer로 보내는 작업
// action은 type과 response을 넣어줘야 함
type: "LOGIN_USER",
payload: request // payroad == response
}
}
export function RegisterUser(regInfo) {
const request = axios.post('/api/users/register', regInfo) // logInfo를 post로 전달
.then(response => response.data); // 서버에서 받은 데이터를 request에 저장
return { // return을 통해 Reducer로 보냄.
// Reducer에서 previousState, action을 이용해 nextState로 만들기 때문 :: (previousState, action) => nextState
// request를 reducer로 보내는 작업
// action은 type과 response을 넣어줘야 함
type: "REGISTER_USER",
payload: request // payroad == response
}
}
export function auth() {
const request = axios.get('/api/users/auth') // logInfo를 post로 전달
.then(response => response.data); // 서버에서 받은 데이터를 request에 저장
return {
type: "AUTH_USER",
payload: request // payroad == response
}
}
\ No newline at end of file
// Redux에 있는 Store에 Reducer들이 여러가지 있을 수 있다.
// -> why? : Reducer 안에서 하는 일은
// state가 어떻게 변하는지를 보여준 다음, 변한 마지막 값을 return 해주는 것.
// 웹서비스를 제작하면서 user state, comment state ... 등등 다양한 기능에 대한 state들이 존재할 수 있고
// 각각 state마다 reducer가 있어서 user reducer, comment reducer ... 등등 다양한 reducer들이 존재할 수 있음.
// ------------------------------------
// 이렇게 나눠진 다양한 reducer을 combineReducers을 통해 rootReducer에서 하나로 합쳐주는 기능을 만들 것임.
import { combineReducers } from 'redux';
import user from './user_reducer'; // user(회원가입, 로그인, 인증, 로그아웃 기능이 있음) reducer
// import comment from './comment_reducer'; // comment기능이 있을 때 reducer
const rootReducer = combineReducers( {
user
})
// 다른 곳에서도 rootReducer을 쓸 수 있도록
export default rootReducer;
\ No newline at end of file
import {
LOGIN_USER,
REGISTER_USER,
AUTH_USER
} from '../_actions/types';
// reducer은 (previousState, action) => (nextState)로
export default function (prevState = {}, action) {
switch (action.type) {
case LOGIN_USER:
return {...prevState, loginSuccess:action.payload} // 위의 prevState를 그대로 가져오고,
// user_action.js에 있는 payload를 그대로 가져와서 return.
// loginSuccess는 server/index.js 에서 login에 성공하면 json type으로 loginSuccess: true를 전달하라고 했기 때문
break;
case REGISTER_USER:
return {...prevState, success:action.payload}
// success는 server/index.js 에서 register에 성공하면 json type으로 success: true를 전달하라고 했기 때문
break;
case AUTH_USER:
return {...prevState, user:action.payload}
break;
default:
return prevState;
}
}
\ No newline at end of file
import React, {useEffect} from 'react'
import axios from 'axios'
import { withRouter } from 'react-router-dom';
function LandingPage(props) {
// 로그아웃 버튼 클릭 됐을 때
const onLogoutClickedEvent = () => {
axios.get('/api/users/logout')
.then(response => {
// 만약 success:true이면 로그인 페이지로 가기
if(response.data.success)
props.history.push("/login");
else
alert("Fail to logout.")
})
}
// 랜딩페이지에 들어오자마자
useEffect(() => {
axios.get('/api/hello') // get request를 서버로 보냄 (endpoint는 /api/hello)
.then(response => console.log(response.data)) // 서버로부터 응답 받은 내용을 콘솔에 출력
}, [])
return (
<div style={{justifyContent:'center', alignItems: 'center', display:'flex', width:'100%'}}>
<h1>시작 페이지</h1>
<button onClick ={onLogoutClickedEvent}> Logout </button>
</div>
)
}
export default withRouter(LandingPage)
\ No newline at end of file
import axios from 'axios';
//import { response } from 'express';
import React from 'react'
import {useState} from 'react'
import {useDispatch} from 'react-redux';
import {loginUser} from '../../../_actions/user_action'
import { withRouter } from 'react-router-dom';
function LoginPage(props) {
// 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용.
// 1-1. state을 사용하기 위해 state 만들어줌.
const [Email, setEmail] = useState(""); // 1-2. email을 위한 state
const [Password, setPassword] = useState(""); // 1-2. password를 위한 state
//1-3. 아래 input value에 넣어줌
// 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌
const emailEvent = (event) => {
setEmail(event.currentTarget.value)
}
const passwordEvent = (event) => {
setPassword(event.currentTarget.value)
}
const dispatch = useDispatch();
const submitEvent = (event) => {
event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음
//console.log('Email', Email); // 잘 나오는지 확인
//console.log('Password', Password); // 잘 나오는지 확인
let logInfo = { // 보내주기 위해 저장
email: Email,
password: Password
}
dispatch(loginUser(logInfo)) // _actions폴더 user_action.js에 있음
.then(response => {
if(response.payload.loginSuccess)
props.history.push('/');
else
alert('Error');
})
}
return (
<div style={{
justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh'
}}>
<form onSubmit={submitEvent}>
<label>Email</label>
<input type="email" value={Email} onChange={emailEvent} />
{/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */}
<label>Password</label>
<input type="password" value={Password} onChange={passwordEvent} />
<br/>
<button>
Login
</button>
</form>
</div>
)
}
export default withRouter(LoginPage)
import React from 'react'
import {useState} from 'react'
import {useDispatch} from 'react-redux';
import {RegisterUser} from '../../../_actions/user_action'
import { withRouter } from 'react-router-dom';
function RegisterPage(props) {
// 이 로그인페이지 안에서 input에 타이핑을 함으로써 데이터를 변화시켜주므로 state 사용.
// 1-1. state을 사용하기 위해 state 만들어줌.
const [Name, setName] = useState("");
const [Email, setEmail] = useState(""); // 1-2. email을 위한 state
const [Password, setPassword] = useState(""); // 1-2. password를 위한 state
const [Password2, setPassword2] = useState("");
//1-3. 아래 input value에 넣어줌
// 2-1. 타이핑할 때 타이핑 하는 거 보이게 하도록 핸들러를 만들어줌
const emailEvent = (event) => {
setEmail(event.currentTarget.value)
}
const passwordEvent = (event) => {
setPassword(event.currentTarget.value)
}
const password2Event = (event) => {
setPassword2(event.currentTarget.value)
}
const NameEvent = (event) => {
setName(event.currentTarget.value)
}
const dispatch = useDispatch();
const submitEvent = (event) => {
event.preventDefault(); // 이걸 하지 않으면 버튼을 누를 때마다 refresh돼서 데이터 처리를 할 수 없음
//console.log('Email', Email); // 잘 나오는지 확인
//console.log('Password', Password); // 잘 나오는지 확인
// 비밀번호 두개가 같아야 회원가입이 되도록
if(Password !== Password2)
return alert('비밀번호가 일치하지 않습니다.')
let regiInfo = { // 보내주기 위해 저장
name : Name,
email: Email,
password: Password
}
dispatch(RegisterUser(regiInfo)) // _actions폴더 user_action.js에 있음
.then(response => {
if(response.payload.success)
props.history.push('/login');
else
alert('Fail to sign up');
})
}
return (
<div style={{
justifyContent:'center', alignItems: 'center', display:'flex', width:'100%', height:'50vh'
}}>
<form onSubmit={submitEvent} style={{display: 'flex', flexDirection: 'column'}}>
<label>Name</label>
<input type="text" value={Name} onChange={NameEvent} />
<label>Email</label>
<input type="email" value={Email} onChange={emailEvent} />
{/* input type="email"이라서 '이메일 주소에 '@'를 포함해주세요'라는 경고문 뜸. */}
<label>Password</label>
<input type="password" value={Password} onChange={passwordEvent} />
<label>Confirm Password</label>
<input type="password" value={Password2} onChange={password2Event} />
<br/>
<button>
Sign In
</button>
</form>
</div>
)
}
export default withRouter(RegisterPage)
// 사용자의 상태를 보고
// 해당 페이지에 들어갈 수 있게 할지 안할지 결정해 줌. hoc 이용.
import axios from 'axios';
import React, {useEffect} from 'react';
//import {useEffect} from 'react';
import {useDispatch} from 'react-redux';
import {auth} from '../_actions/user_action'
export default function (SpecificComponent, option, adminRoute = null){
// ㄴ option 종류
// - null 아무나 출입 가능한 페이지
// - true 로그인한 유저만 출입 가능
// - false 로그인한 유저 출입 불가능
function AuthenticationCheck(props) {
//1. backend에 request를 날려서 사용자의 상태를 확인
const dispatch = useDispatch(); // 1-1. dispatch 사용
useEffect(() => {
dispatch(auth()) // 페이지가 이동할 때마다 dispatch가 작동해서 backend에 request를 줌
.then(response => { // 받은 response
console.log(response);
// 로그인 안했다면
if(!response.payload.isAuth) {
if(option) { // 만약 위 option이 true이면 (로그인한 유저만 출입 가능한 페이지로 가게 하려면)
props.history.push('/login'); // 로그인 하게 함
}
}
// 로그인 했다면
else {
if(adminRoute && !response.payload.isAdmin) { // admin만 갈 수 있는 페이지를 admin이 false 사람이 들어가려 한다면
props.history.push('/'); // 홈페이지로 돌아가게 함
}
else {
if(option===false) {// 로그인한 유저가 출입 불가능한 곳을 가려고 한다면
props.history.push('/'); // 홈페이지로 돌아가게 함
}
}
}
})
},[])
return (
<SpecificComponent />
)
}
return AuthenticationCheck
}
\ No newline at end of file
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux' // app에 redux를 연결시켜주기 위해 redux에서 제공하는 provider 사용
import { createStore } from 'redux'; // redux에서 createStore 가져옴.
import { applyMiddleware } from 'redux'; // object만 받는 store가 promise나 functions도 받기 위해 middleware을 사용함
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-thunk';
import Reducer from './_reducers';
import 'antd/dist/antd.css';
const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore)
ReactDOM.render(
// App에 Redux를 연결
<Provider
store={createStoreWithMiddleware(Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}
>
<App />
</Provider>
, document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log resu lts (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import React from 'react'
function LoginPage() {
return (
<div>
LoginPage
</div>
)
}
export default LoginPage
import React from 'react'
function RegisterPage() {
return (
<div>
RegisterPage
</div>
)
}
export default RegisterPage
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();