-
[Node.js] Express와 jwt 토큰(Json Web Token)을 이용한 회원가입 / 로그인 구현하기 [2]Backend 2019. 9. 13. 18:10
지난 번에 Express와 MongoDB로 간단하게 백엔드 서버를 만들었으니
이제 그걸 이용해서 프론트엔드 구현을 마저 해보려고 한다.
client/src/index.js
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './components/App'; import * as serviceWorker from './serviceWorker'; import { BrowserRouter as Router } from 'react-router-dom'; import { CookiesProvider } from 'react-cookie'; ReactDOM.render( <Router> <CookiesProvider> <App /> </CookiesProvider> </Router> , document.getElementById('root'));
react-cookie와 react-router 모듈을 사용하기위해
npm install한 후 index.js에 꽂아넣었다.
client/src/Components/App.js
import React, { useState, useEffect } from 'react'; import { Route, Switch, Redirect } from "react-router-dom"; import { withCookies, useCookies } from 'react-cookie'; import Login from './Login'; import Join from './Join'; import Todo from './Todo'; import './css/App.css'; const App = () => { const [ cookies, removeCookie ] = useCookies([ 'user' ]); const [ hasCookie, setHasCookie ] = useState(false); useEffect(() => { if (cookies.user && cookies.user !== 'undefined') { setHasCookie(true); } }, [ cookies ]); return ( <div className="App"> <h1>Todo App</h1> {!hasCookie ? <Redirect to="/login" /> : <Redirect to="/todo" />} <Switch> <Route exact path="/login" render={routerProps => { return ( <Login {...routerProps} setHasCookie={setHasCookie} /> ); }} /> <Route exact path="/join" component={Join} /> <Route exact path="/todo" render={routerProps => { return ( <Todo {...routerProps} setHasCookie={setHasCookie} removeCookie={() => { removeCookie('user'); setHasCookie(false); }} /> ); }} /> </Switch> </div> ); }; export default withCookies(App);
서버로부터 받아온 토큰 정보가 쿠키에 저장되어있지 않다면 Login 컴포넌트를 띄우고,
저장되어있다면 Todo 컴포넌트를 띄운다.
client/src/Components/Login.js
import React, { useState } from 'react'; import { Link } from "react-router-dom"; const Login = ({ setHasCookie }) => { const [ userId, setUserId ] = useState(''); const [ userPw, setUserPw ] = useState(''); const loginApi = (user) => { return fetch('/users/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(user) }).then(response => response.json()); }; const handleSubmit = async (e) => { e.preventDefault(); if (!userId || !userPw) { return; } try { const response = await loginApi({ user_id: userId, user_pw: userPw }); if (response.result === 'ok') { setHasCookie(true); } else { throw new Error(response.error); } } catch (err) { alert('로그인에 실패했습니다.'); setUserId(''); setUserPw(''); console.error('login error', err); } }; return ( <div> <h2>Login</h2> <form onSubmit={handleSubmit} > <input type="text" name="user_id" value={userId} onChange={e => setUserId(e.target.value)} placeholder="id" /> <input type="password" name="user_pw" value={userPw} onChange={e => setUserPw(e.target.value)} placeholder="pw" /> <button type="submit" > Login </button> </form> <Link to="/join" > 회원가입 </Link> </div> ); }; export default Login;
로그인 페이지에서 id와 pw를 사용자가 입력하고 Login 버튼을 클릭하면
handleSubmit 메소드가 실행된다.
이 때 서버에 HTTP POST 요청을 보내는데,
만약 valid한 user라면 token을 생성한 후 쿠키에 저장된다.
로그인에 실패할 경우 alert 메시지가 뜬다.
client/src/Components/Join.js
import React, { useState } from 'react'; import { Link } from "react-router-dom"; const Join = () => { const [ userId, setUserId ] = useState(''); const [ userPw, setUserPw ] = useState(''); const [ userName, setUserName ] = useState(''); const [ isJoinSuccess, setJoinSuccess ] = useState(false); const createUserApi = (user) => { return fetch('/users/new', { method: 'POST', body: JSON.stringify(user), headers: { 'Content-Type': 'application/json' } }).then(response => response.json()); }; const handleSubmit = async (e) => { e.preventDefault(); try { const response = await createUserApi({ user_id: userId, user_pw: userPw, user_name: userName }); if (response.result === 'ok') { setJoinSuccess(true); } } catch (err) { console.error('login error', err); alert('회원가입에 실패하였습니다. 잠시 후 다시 시도해주세요.') } }; return ( <div> {!isJoinSuccess && ( <> <h2>Join</h2> <form onSubmit={handleSubmit} > <input type="text" name="user_id" value={userId} onChange={e => setUserId(e.target.value)} placeholder="id" /> <input type="password" name="user_pw" value={userPw} onChange={e => setUserPw(e.target.value)} placeholder="pw" /> <input type="text" name="user_name" value={userName} onChange={e => setUserName(e.target.value)} placeholder="name" /> <button type="submit" > 제출 </button> </form> </> )} {isJoinSuccess && ( <div> <p>회원가입을 축하합니다!</p> <Link to="/login">로그인</Link> </div> )} </div> ); }; export default Join;
아주 아주 간단한 회원가입 페이지이다.
/users/new 경로로 POST 요청을 보내 DB에 회원 정보를 새로 생성한다.
성공하면 isJoinSuccess 변수를 true로 만들어주고
회원가입을 축하한다는 메시지를 보여주는 페이지를 렌더링한다.
실패하면 alert 메시지를 보여준다.
회원가입이 완료된 후에 Robo 3T를 살펴보면
아까 넣은 데이터가 저장되어있는 것을 확인할 수 있다.
client/src/Components/Todo.js
import React, { useState, useEffect } from 'react'; const Todo = ({ setHasCookie, removeCookie }) => { const [ todos, setTodos ] = useState(null); useEffect(() => { const abortController = new AbortController(); const signal = abortController.signal; const getAllTodoApi = () => { return new Promise((resolve, reject) => { fetch('/todos', { signal: signal, method: 'GET', headers: { 'Content-Type': 'application/json' } }) .then(res => resolve(res.json())) .catch(err => reject(err)); }); }; const onTodoLoad = async () => { try { const response = await getAllTodoApi(); if (response.error === 'token expired') { setHasCookie(false); } else { setTodos(response.todos); } } catch (err) { console.log(err); } }; if (!todos) { onTodoLoad(); } return () => { abortController.abort(); } }, [ todos, setHasCookie ]); return ( <div> <h2> Todo <button type="button" onClick={removeCookie} > logout </button> </h2> <ul> {todos && ( todos.map(todo => ( <li key={todo._id}> <span>{todo.content}</span> <span>{todo.created_at}</span> <input type="checkbox" value={todo.complete} /> </li> )) )} </ul> </div> ); }; export default Todo;
회원가입과 로그인이 모두 성공하면 todo 페이지로 이동한다.
반응형'Backend' 카테고리의 다른 글
[Node.js] 웹 소켓으로 실시간 랜덤 채팅 구현 중 메시지 중복 버그 해결과정 (WebSocket Random Chat - clients rendering duplicate message) (1684) 2019.10.05 [Node.js] Express와 jwt 토큰(Json Web Token)을 이용한 회원가입 / 로그인 구현하기 [1] (1314) 2019.09.11 [Node.js] Express를 backend로 하는 Create-react-app 시작하기 (609) 2019.09.05 [C언어/Day3] C언어 수업정리 - 배열 기초 (정보처리기사, 실기) (0) 2019.05.22 [C언어/Day2] C언어 수업정리 (정보처리기사, 실기) / 자바스크립트와 산술연산 결과가 다르게 나오는 이유 (0) 2019.05.13 COMMENT