https://github.com/loy124/express-mongodb-template
해당 게시글을 통해 backend를 구축하고 해당 백엔드와 연동하는 프론트를 React로 구축을한다
https://github.com/loy124/react-user-template
완성된 리액트 페이지
해당 게시글은 git에 올린 react-user-template을 기반으로 설명위주로 글을 쓸 예정이다
먼저 를 사용해서 리액트 기본 상태를 구축해준다.
npx create-react-app frontend
해당 프로젝트에서는 react-router-dom, redux, react-redux(리덕스를 쉽게 사용할수 있게 도와주는 라이브러리)
, redux-promise(리덕스에서 promise 패턴을 사용할수 있게 해주는 라이브러리), redux-thunk(만약에 특정 액션이 몇초뒤에 실행되게 하거나, 현재 상태에 따라 아예 액션이 무시등 해당 기능을 사용하기 위해 사용), axios를 사용하기 때문에
터미널에서 해당 명령어를 실행해서 전부 다운받을 수 있게 한다.
npm i react-router-dom redux react-redux redux-promise redux-thunk axios --save
그리고 폴더구조를 생성해준다
_actions(redux의 action들 모음)
_reducers(redux의 reducers모음)
_components/views(화면을 그려주는 부분)
hoc(auth등 로그인시 접근제어를 위한 컴포넌트 폴더)
utils(라이브러리를 모아둔다, axios등)
먼저 redux에 대한 셋팅을 해준다.
1. Provider 로 react 감싸기 -> store생성하기(reduxThunk, reduxrnt) -> Provider에 생성한 store 바인딩하기
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { applyMiddleware, createStore } from "redux";
import promiseMiddlerware from "redux-promise";
import reduxThunk from "redux-thunk";
const createStoreWidthMiddleware = applyMiddleware(
promiseMiddlerware,
reduxThunk
)(createStore);
ReactDOM.render(
<React.StrictMode>
<Provider
store={createStoreWidthMiddleware(
// 리듀서를 생성후 넣어준다
//
//개발자 도구를 사용하기 위한 설정
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
reducer관련 파일들을 모두 제작 한후 reducer 또한 넣어줄 예정이다.
먼저 axios는 전역적으로 활용하기 때문에 모듈화를 진행시키는게 좋다고 판단되어 모듈화를 진행시켰다.
axios.js
import axios from "axios";
const DOMAIN = "http://localhost:9000";
axios.defaults.withCredentials = true; // 쿠키 데이터를 전송받기 위해
export const request = (method, url, data) => {
return axios({
method,
url: DOMAIN + url,
data,
})
.then((res) => res.data)
.catch((err) => console.log(err));
};
쿠키데이터를 주고받기 위해 axios.default.withCredentials를 true로 지정해주고
request를 export 시켜서 어디서든 import해서 사용할수 있도록 모듈화를 진행하였다.
이제 각각 view들을 만들어준다.
기본적으로 LandingPage(기본화면), LoginPage(로그인 화면), RegisterPage(회원가입 화면) 세가지로 나뉘어있고
회원가입, 로그인, 로그아웃, auth(특정 라우터에 접근을 하지 못하게 만들기) 기능을 구현할 예정이다.
먼저 view들을 만들어준다
RandingPage.js
import React from "react";
import { withRouter } from "react-router-dom";
function LandingPage(props) {
const onClickHandler = () => {};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}>
<h2>시작 페이지</h2>
<button onClick={onClickHandler}>로그아웃</button>
</div>
);
}
export default withRouter(LandingPage);
RegisterPage.js
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
function RegisterPage(props) {
const [Email, setEmail] = useState("");
const [Password, setPassword] = useState("");
const [Name, setName] = useState("");
const [ConfirmPasword, setConfirmPasword] = useState("");
const onEmailHandler = (e) => {
setEmail(e.currentTarget.value);
};
const onNameHandler = (e) => {
setName(e.currentTarget.value);
};
const onPasswordHanlder = (e) => {
setPassword(e.currentTarget.value);
};
const onConfirmPasswordHandler = (e) => {
setConfirmPasword(e.currentTarget.value);
};
const onSubmitHandler = (e) => {
e.preventDefault();
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}>
<form
onSubmit={onSubmitHandler}
style={{ display: "flex", flexDirection: "column" }}>
<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />
<label>Name</label>
<input type="test" value={Name} onChange={onNameHandler} />
<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHanlder} />
<label>ConfirmPasword</label>
<input
type="password"
value={ConfirmPasword}
onChange={onConfirmPasswordHandler}
/>
<br />
<button type="submit">회원 가입</button>
</form>
</div>
);
}
export default withRouter(RegisterPage);
LoginPage.js
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
function LoginPage(props) {
const [Email, setEmail] = useState("");
const [Password, setPassword] = useState("");
const onEmailHandler = (e) => {
setEmail(e.currentTarget.value);
};
const onPasswordHanlder = (e) => {
setPassword(e.currentTarget.value);
};
const onSubmitHandler = (e) => {
e.preventDefault();
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}>
<form
onSubmit={onSubmitHandler}
style={{ display: "flex", flexDirection: "column" }}>
<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />
<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHanlder} />
<br />
<button type="submit">Login</button>
</form>
</div>
);
}
export default withRouter(LoginPage);
useState(https://loy124.tistory.com/232?category=784079 ) 를 활용하여 현재 state와 state를 변경하는 함수를 각각 지정해주었다.
view를 만들었으니 해당 요청들을 모두 라우터들로 만들어줘야한다.
App.js
import React from "react";
import "./App.css";
import { BrowserRouter as Router, Switch, Route } 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";
function App() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={LandingPage} />
<Route exact path="/login" component={LoginPage} />
<Route exact path="/register" component={RegisterPage} />
</Switch>
</div>
</Router>
);
}
export default App;
Router 내에서 Switch를 사용해서 다른 라우터들을 보여준다
view를 만들었으니 이제 회원가입 기능을 redux를 활용해서 구현해 보겠다.
먼저 types.js다 types.js에서는 각각 type들의 형태를 변수로 관리한다 (오타 방지및 관리를 편하게 하기위함)
회원가입 기능이니 REGISTER_USER로 정의해주었다.
type.js
export const REGISTER_USER = "REGISTER_USER";
이를 바탕으로 action 함수를 생성해준다
userAction.js
import { REGISTER_USER } from "./types";
import { request } from "../utils/axios";
const USER_URL = "/api/user";
export function registerUser(dataToSubmit) {
const data = request("post", USER_URL + "/register", dataToSubmit);
return {
type: REGISTER_USER,
payload: data,
};
}
모듈화된 axios로 제작된 request를 사용해서 액션 함수를 생성해준다.
post 요청을 보내고 받은 값을 payload에 실어서
reducer에서 해당 타입을 읽을수 있도록 해야한다.
userReducer.js
import { REGISTER_USER } from "../_actions/types";
export default function (state = {}, action) {
switch (action.type) {
case REGISTER_USER:
return { ...state, loginSuccess: action.payload };
default:
return state;
}
}
REGISTER_USER 일때 loginSuccess에 action의 payload값 을 받아온다 (userAction.js)의 값을 받아온다고 보면된다
해당 reducer을 한곳에서 묶어서 하나의 리듀서처럼 보여지게 하는 작업이 필요한데
_reducers/index.js에서 combinReducers를 사용해서 하나의 리듀서로 만들어준다.
import { combineReducers } from "redux";
import user from "./userReducer";
const rootReducer = combineReducers({
user,
});
export default rootReducer;
reducer가 생성되었으니 해당 reducer를 store에 넣어줘야한다.
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "react-redux";
import { applyMiddleware, createStore } from "redux";
import promiseMiddlerware from "redux-promise";
import reduxThunk from "redux-thunk";
import reducer from "./_reducers";
const createStoreWidthMiddleware = applyMiddleware(
promiseMiddlerware,
reduxThunk
)(createStore);
ReactDOM.render(
<React.StrictMode>
<Provider
store={createStoreWidthMiddleware(
reducer,
//개발자 도구를 사용하기 위한 설정
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
해당 기능까지 완료하였다면 정상적으로 회원가입 기능이 완성이 되었다.
RegisterPage.js에서 사용할 차례이다
useDispatch(useDispatch는 redux store에 설정된 action에 대한 dispatch를 연결하는 훅이다.)와 registerUser를 import 해서
useDispatch 안에 액션 함수를 넣어준다.
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
import { useDispatch } from "react-redux";
import { registerUser } from "../../../_actions/userAction";
function RegisterPage(props) {
const [Email, setEmail] = useState("");
const [Password, setPassword] = useState("");
const [Name, setName] = useState("");
const [ConfirmPasword, setConfirmPasword] = useState("");
const dispatch = useDispatch();
const onEmailHandler = (e) => {
setEmail(e.currentTarget.value);
};
const onNameHandler = (e) => {
setName(e.currentTarget.value);
};
const onPasswordHanlder = (e) => {
setPassword(e.currentTarget.value);
};
const onConfirmPasswordHandler = (e) => {
setConfirmPasword(e.currentTarget.value);
};
const onSubmitHandler = (e) => {
e.preventDefault();
if (Password === ConfirmPasword) {
let body = {
email: Email,
name: Name,
password: Password,
};
dispatch(registerUser(body)).then((res) => {
alert("가입이 정상적으로 완료되었습니다");
props.history.push("/login");
});
} else {
alert("비밀번호가 일치하지 않습니다");
}
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}>
<form
onSubmit={onSubmitHandler}
style={{ display: "flex", flexDirection: "column" }}>
<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />
<label>Name</label>
<input type="test" value={Name} onChange={onNameHandler} />
<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHanlder} />
<label>ConfirmPasword</label>
<input
type="password"
value={ConfirmPasword}
onChange={onConfirmPasswordHandler}
/>
<br />
<button type="submit">회원 가입</button>
</form>
</div>
);
}
export default withRouter(RegisterPage);
test5를 기반으로 회원가입을 진행하였다.
정상적으로 회원가입이 되어 /login 으로 넘어간것을 확인할 수 있다.
해당 글은 위 강의를 바탕으로 작성되었습니다.
'React > react 활용하기' 카테고리의 다른 글
react + redux를 활용한 로그인및 회원가입 만들기 - auth(접근방지, 허용) 기능 구현하기 (1) | 2020.07.01 |
---|---|
react + redux를 활용한 로그인및 회원가입 만들기 - 로그인및 로그아웃 구현하기 (1) | 2020.07.01 |