이번엔 React를 이용해 로그인 과정을 구현해보겠다.
참조한 링크는 이 곳이다.
소스는 기존 포스팅, React-Node-Mysql 통합환경구축을 기반으로 시작한다.
전체 파일 트리와 package.json은 다음과 같다.
// File tree
(project_name)
└ (app)
└ App.js
└ Hello.js
└ Home.js
└ LoginPanel.js
└ UserInfo.js
└ (public)
└ index.html
└ login.html
└ style.css
└ (server)
└ (config)
└ db-config.json
└ main.js
└ package.json
└ webpack.config.js
// Package.json
{
"name": "third_react_comp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"clean": "rm -rf build public/bundle.js",
"build": "./node_modules/.bin/babel server --out-dir build && ./node_modules/.bin/webpack --progress",
"start": "rm -rf build public/bundle.js && ./node_modules/.bin/babel server --out-dir build && ./node_modules/.bin/webpack --progress && node ./build/main.js"
},
"author": "refgjin",
"license": "ISC",
"devDependencies": {
"babel-core": "~6.7.*",
"babel-cli": "~6.9.*",
"babel-loader": "~6.2.*",
"babel-plugin-react-transform": "^2.0.2",
"babel-preset-es2015": "~6.6.*",
"babel-preset-react": "~6.5.*",
"webpack": "^1.12.*",
"webpack-dev-server": "^1.10.*"
},
"dependencies": {
"babel-polyfill": "~6.7.*",
"body-parser": "^1.17.1",
"express": "^4.14.1",
"mysql": "^2.11.1",
"react": "^0.13.*",
"react-addons-css-transition-group": "^15.4.2",
"react-alert": "^1.0.14",
"react-cookie": "^1.0.5",
"react-dom": "^15.0.0",
"whatwg-fetch": "^0.11.0"
}
}
변경된 App.js 파일이다.
//App.js
import React from 'react';
import ReactDOM from 'react-dom';
import Home from './Home';
const root = document.getElementById("root");
React.render(<Home />, root);
기존 코드에서 Hello.js를 직접 참조했던 것과 다르게 Home.js를 먼저 참조한다.
이유는 다음 코드를 보며 설명하겠다.
// Home.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import cookie from 'react-cookie';
import Hello from './Hello';
import LoginPanel from './LoginPanel';
class Home extends Component {
componentWillMount(){
this.state = {
userId: ''
};
//adminId: cookie.load('adminId'),
}
onLogin(adminId){
this.setState({
userId:userId
});
//cookie.save('adminId',adminId, { path: '/'});
}
onLogout(){
this.setState({
userId:''
});
//cookie.remove('adminId', { path: '/'});
}
render(){
if(!this.state.userId){
return <LoginPanel
onSuccess={this.onLogin.bind(this)}
/>;
}
return <Hello
userId={this.state.userId}
onLogout={this.onLogout.bind(this)}
/>;
}
}
export default Home;
Home 컴포넌트는 userId가 존재한다면 Hello 컴포넌트를 불러오며,
존재하지 않을 경우 LoginPanel 컴포넌트를 부른다.
// LoginPanel.js
import React, { Component , PropTypes } from 'react';
import ReactDOM from 'react-dom';
import 'whatwg-fetch';
import AlertContainer from 'react-alert';
class LoginPanel extends Component {
constructor(){
super(...arguments);
this.state ={
requestID:'',
requestPW:''
};
this.alertOptions = {
offset: 14,
position: 'top right',
theme: 'dark',
time: 5000,
transition: 'scale'
};
this.requestIDChange = this.requestIDChange.bind(this);
this.requestPWChange = this.requestPWChange.bind(this);
}
onSubmit(){
let userInfo={
'user_id':this.state.requestID,
'user_pw':this.state.requestPW
};
fetch('/login',{
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfo)
}).then((response)=> response.json())
.then((responseData)=>{
if(responseData.loginresult){
this.props.onSuccess(this.state.requestID);
}
else{
msg.show(`일치하는 ID와 PW가 없습니다.`
, {
time: 2000,
type: 'error'
});
this.setState({
requestID:'',
requestPW:''
});
}
});
}
requestIDChange(event){
this.setState({requestID: event.target.value});
}
requestPWChange(event){
this.setState({requestPW: event.target.value});
}
render(){
return (
<div className="loginpanel">
<div className="loginwindow">
<AlertContainer ref={ (a) => global.msg = a} {...this.alertOptions} />
<ul>
<li className="title">Login Template</li>
<li><input type="text" name="requestID" placeholder="Admin Id" value={this.state.requestID} onChange={this.requestIDChange}/></li>
<li><input type="password" name="requestPW" placeholder="Password" value={this.state.requestPW} onChange={this.requestPWChange}/></li>
<li><button className="loginwindowbutton" onClick={this.onSubmit.bind(this)}>로그인</button></li>
</ul>
</div>
</div>
);
}
}
LoginPanel.propTypes={
onSuccess: PropTypes.function
};
export default LoginPanel;
LoginPanel 컴포넌트는 fetch를 이용해 API 서버에서 아이디-비밀번호 검사를 하고 존재할 경우,
Home 컴포넌트에서 받은 onSuccess함수로 userId를 전달하며,
Home 컴포넌트의 state.userId가 갱신되는 순간 바로 unmount 된다.
// Hello.js
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import 'whatwg-fetch';
import UserInfo from './UserInfo';
// Parent Component
class Hello extends Component {
constructor(){
super(...arguments);
this.state = {
mans:[]
};
}
componentDidMount(){
fetch('/man',{
method: 'get',
dataType: 'json',
headers:{
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then((response) => response.json())
.then((responseData) => {
this.setState({mans: responseData});
})
.catch((error)=>{
console.log('Error fetching man',error);
});
}
render() {
let mans = this.state.mans.map( (man) => {
return <Guy
name={man.name}
age={man.age}
createDateTime={man.create_datetime}
{...man}/>
});
let userPanel = {
return <UserInfo
userId={this.props.userId}
onLogout={this.props.onLogout}
{...UserInfo}/>
};
return (
<div>
{userPanel}
<h1>CalyFactory Developers</h1>
<ul>
{mans}
</ul>
</div>
);
}
}
// Child Component
class Guy extends Component {
render() {
return (
<li>
{this.props.name}, age is {this.props.age}. create time : {this.props.createDateTime}
</li>
);
}
}
export default Hello;
변경된 Hello 컴포넌트다. 부모 컴포넌트로부터 userId와 onLogout을 받아서 UserInfo 컴포넌트에서 전달하며,
기존 기능은 동일하다.
// UserInfo.js
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
class UserInfo extends Component {
render(){
return (
<div className="userinfo">
<li className="title">CalyFactory</li>
<li>{''}{this.props.userId} 님 안녕하세요 ! <input type="button" className="userinfologoutbutton" value="로그아웃" onClick={() => this.props.onLogout()} /></li>
</div>
);
}
};
UserInfo.propTypes={
userId: PropTypes.string,
onLogout: PropTypes.function
};
export default UserInfo;
UserInfo 컴포넌트는 Home 컴포넌트로부터 받은 userId를 렌더링하고,
onLogout함수를 로그아웃 버튼에 binding한다.
// main.js
import express from 'express';
import bodyParser from 'body-parser';
import mysql from 'mysql';
import path from 'path';
let dbconfig = require(__dirname+'/../server/config/db-config.json');
let connection = mysql.createConnection(dbconfig);
const app = express();
const port = 3000;
app.use('/', express.static(__dirname + "/../public"));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.get('/man', (req, res) =>{
connection.query("SELECT * FROM man", (err, rows) => {
if(err) throw err;
res.send(rows);
});
});
app.get('/login', (req, res) =>{
connection.query("SELECT * FROM user WHERE user_id=\'"+req.body.user_id+"\' and user_pw=\'"+req.body.user_pw+"\'", (err, rows) => {
if(err) throw err;
if(rows.length>0)
return res.send({loginresult:true,name:rows[0].user_name});
else
return res.send({loginresult:false});
});
});
const server = app.listen(port, () => {
console.log('Express listening on port', port);
});
마지막으로 변경된 server/main.js다.
app/loginPanel.js에서 요청한 /login에 대해 아이디-비밀번호 체크를 제공한다.
Comments