지금까지 배운 보안(jwt , 쿠키), 비동기 통신 (ajax), DB(mysql, pool) 을 활용해
간단한 회원가입과 로그인, 게시판 기능이 있는 웹 서버를 만들어보자.
1. 우선 DB에 게시글과 회원들의 정보 틀 (db, table)을 만들어 줄 것이고,
2. 사용자가 회원 가입을 하면 아이디가 중복되는지 등을 검사하고, 문제가 없을 경우,
사용자가 입력한 정보를 DB에 추가할 것이다.
3. 가입한 사용자가 로그인을 시도할 경우, 입력한 id, pw와 일치하는 db 내의 정보를 찾고
일치하는 정보가 있다면 로그인에 성공, 그렇지 않으면 실패하게 된다.
4. 로그인에 성공할 경우, jwt를 이용해 암호화를 거친 쿠키를 생성해 주고,
이 후 게시판에 관련된 웹 페이지를 만들어 줄 때 쿠키를 검증 한 후, 접속이 가능하도록 미들웨어를 추가해줄 예정이다.
이 포스팅에서는 우선 그 첫 단계로 사용자에 관련된 작업 (회원가입, 로그인) 을 다룰 것이다.
1. intro
우선 프론트, 백 엔드 서버를 나눠 준 후, 각각의 서버가 기본적으로 필요한 라이브러리, 코드를 설치, 작성해주자.
(필요 외부 라이브러리 : express, nunjucks)
/* front/server.js */
const express = require('express')
const nunjucks = require('nunjucks')
const app = express()
app.set('view engine', 'html')
nunjucks.configure('views', {express: app})
app.get('/', (req, res) => {
res.render('main.html')
})
app.get('/user/join', (req, res) => {
res.render('join.html')
})
app.get('/user/login', (req, res) => {
res.render('/user/login')
})
app.listen( 3001 ,() => {
console.log('front server 3001 running')
})
/* front/views/main.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<h1><a href='/'>main page</a></h1>
<br>
<a href="/user/login">로그인</a>
<a href="/user/join">회원가입</a>
</body>
</html>
백 엔드 서버도 다음과 같이 작성한다.
(필요 외부 라이브러리 : express, cors)
/* back/server.js */
const express = require('express')
const cors = require('cors')
const app = express()
app.use(express.json()) // 이 후, json 데이터를 받을 때 필요
app.use(express.urlencoded({extended:true})
app.use(cors({
origin:true,
credentials:true
})
app.listen(4001, () => {
console.log('back server 4001 running')
})
나중에 프론트 서버와 데이터를 주고 받아야 하므로 미리 cors를 셋 업 해준다.
서버가 문제 없이 구동되는 것을 확인했으면 DB schema를 작성해보자.
/* /back/SQL/table.sql */
create database home;
use home;
CREATE TABLE user(
userlevel INT NOT NULL,
userid VARCHAR(10) NOT NULL UNIQUE,
userpw VARCHAR(10) NOT NULL,
name VARCHAR(10) NOT NULL,
nickname VARCHAR(10) NOT NULL,
birth DATE NOT NULL,
gender CHAR(2) NOT NULL,
phone VARCHAR(11) NOT NULL,
mobile VARHAR(11) NOT NULL,
active INT NOT NULL DEFAULT 1,
PRIMARY KEY(userid)
);
CREATE TABLE board(
idx INT NOT NULL AUTO_INCREMENT,
subject VARCHAR(40) NOT NULL,
nickname VARCHAR(10) NOT NULL,
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
content TEXT,
hit INT_DEFAULT 0 NOT NULL,
PRIMARY KEY(idx)
);
sql 파일을 mysql에서 실행하면 지정한대로 db와 table을 만들어줄 것이다.
mysql 사용법에 대해 참고하려면 아래 링크로..
https://liferesetbutton.tistory.com/27?category=922856
이제 백 엔드 서버와 DB를 연결하는 작업이 필요하다.
이 역할을 담당한 js 파일을 하나 더 만들어주자.
(필요 외부 라이브러리 : mysql2, dotenv)
/* back/db.js */
require('dotenv').config()
const mysql = require('mysql2')
const host = process.env.DB_HOST || 'localhost'
const user = process.env.DB_USER || [본인의 id 입력]
const password = process.env.DB_PASSWORD || [본인의 pw 입력]
const database = process.env.DB_database || 'home'
const config = { host, user, password, database }
const pool = mysql.createPool(config)
const promisePool = pool.promise()
exports.pool = promisePool
// 변수명을 바꾸지 말 것. DB에 접근하는데 변수명이 그대로 사용된다.
dotenv는 민감한 정보들을 따로 분리해 필요할 경우 다른 사람과 작업물을 공유할 때
원치 않게 정보들이 노출되는 것을 방지해준다.
.env 파일도 다음과 같이 작성해준다.
/* back/.env */
DB_HOST = localhost
DB_USER = [각자의 아이디]
DB_PASSWORD = [각자의 비밀번호]
DB_DATABASE = home
다시 백엔드의 server.js로 돌아와 db.js에서 pool을 inport 해준다.
/* back/server.js */
const pool = require('./db.js').pool
여기까지 하면 DB와 백 엔드 서버간의 연결이 확보된 것이다.
파일들의 위치 관계는 다음과 같다. (utils 폴더는 일단 무시)
2. 회원 가입 기능 구현
이제 회원 가입 기능을 구현해보자.
여기서 회원 가입은 다음과 같은 단계들을 거쳐 이루어진다.
1. 사용자가 필요한 정보를 입력해 제출 한다.
2. 프론트에서 비동기로 전달된 정보를 백 엔드 서버로 보낸다.
3. 백 엔드에서 DB와 요청, 응답을 주고 받아 가입 (데이터의 추가)의 성공 여부 결정
4. 성공 여부를 사용자 에게 전달 (프론트에 렌더링)
이 순서대로 코드를 작성해보자.
2.1 회원 가입 form 작성 후 제출
join.html을 다음과 같이 작성한다.
/* front/join.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<form method="POST" id="join_frm" action="/">
<ul class="join_frm">
<li>
<span class="join_label">아이디</span>
<span class="join_input">
<input type="text" name="userid" id="userid">
</span>
</li>
<li>
<span class="join_label">패스워드</span>
<span class="join_input">
<input type="text" name="userpw" id="userpw">
</span>
</li>
<li>
<span class="join_label">이름</span>
<span class="join_input">
<input type="text" name="name" id="name">
</span>
</li>
<li>
<span class="join_label">별명</span>
<span class="join_input">
<input type="text" name="nickname" id="nickname">
</span>
</li>
<li>
<span class="join_label">생년월일 (YYYYMMDD 형식으로 입력) </span>
<span class="join_input">
<input type="text" name="birth" id="birth">
</span>
</li>
<li>
<span class="join_label">성별</span>
<span class="join_input">
<input type="radio" name="gender" value="남자" id="gender1" checked>
<label for="gender1">남자</label>
<input type="radio" name="gender" value="여자" id="gender2" >
<label for="gender2">여자</label>
</span>
</li>
<li>
<span class="join_label">전화번호</span>
<span class="join_input">
<input type="text" name="phone" id="phone">
</span>
</li>
<li>
<span class="join_label">핸드폰</span>
<span class="join_input">
<input type="text" name="mobile" id="mobile">
</span>
</li>
</ul>
<input type="submit" id="btn" value="회원가입">
</form>
</body>
</html>
DB의 schema대로 회원가입 form을 작성하면 된다.
2.2 입력한 정보를 프론트에서 백 앤드 서버로 전달
이제 정보를 입력한 form을 제출 할 때, 이를 백엔드 서버로 전달하게끔 script를 추가해준다.
/* front/views/join.html */
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> // head 마지막에 추가
//
<script type="text/javascript">
const frm = document.querySelector('#join_frm')
frm.addEventListener('submit', async (e) => {
e.preventDefault()
const btn = document.querySelector('#btn')
btn.value='회원 가입 진행 중'
btn.disabled = true
const data = {
userid:document.querySelector('#userid').value,
userpw:document.querySelector('#userpw').value,
name:document.querySelector('#name').value,
nickname:document.querySelector('#nickname').value,
birth:document.querySelector('#birth').value,
gender:document.querySelector('input[name="gender"]:checked').value,
phone:document.querySelector('#phone').value,
mobile:document.querySelector('#mobile').value
}
const option = {
'Content-type' : 'application/json',
withCredentials : true
}
const response = await axios.post('http://localhost:4001/user/join', data, option)
})
여기까지 작성 후, 백엔드 쪽으로 가서 이를 받아줄 라우터를 작성하고 돌아오자.
2.3 백 엔드 서버에서 DB와 요청, 응답을 통해 회원 가입 성공 여부 결정
(설치할 라이브러리 : cors, cookie-parser)
/* back/server.js */
app.post('/api/user/join', async (req, res) => {
const { userid, userpw, name, nickname, birth, gender, phone, mobile } = req.body
const sql = `INSERT INTO user(userlevel, userid, userpw, name, nickname, birth, gender, pone, mobile)
values(?, ?, ?, ?, ?, ?, ?, ?, ?)`
const param = [ 1, userid, userpw, name, nickname, birth, gender, phone, mobile ]
try {
const [ result ] = await pool.execute(sql, param)
const response = {
errno:0
}
res.json(response)
}
catch (e) {
console.log(e.message)
const response = {
errno : 1
}
res.json(response)
}
})
프론트 서버에서 요청으로 들어온 req.body를 파라미터로 넣어준 후, sql구문과 함께 DB에 요청을 보낸다.
그러면 sql을 구문이 DB에서 실행 된 후, 백 엔드 서버에 응답을 줄 것이다.
그걸 result라는 변수에 배열화해 담는다.
이 result에서 일부의 정보와 지금 새로 만들어준 errno라는 key를 response 변수에 담아 프론트로 전송한다.
DB에 성공적으로 정보가 추가되었을 경우 errno에 0 이라는 값을,
그렇지 않을 경우 (아이디가 중복되면) errno에 1이라는 value를 담아 응답으로 프론트 서버에 전송한다.
다시 프론트의 join.html로 돌아와 각 response에 대한 코드를 이어서 작성하자.
2.4 성공 여부를 프론트, 사용자에게 전달
/* front/views/join.html */
<script type='text/javascript'>
frm.addEventListener('submit', async (e) => {
// ...중략...
// 아래에 이어서 작성
if (response.data.errno === 0 ) {
alert('회원 가입이 완료되었습니다.')
location.href='/'
}
else {
alert('아이디가 중복되었습니다.')
btn.value='회원가입'
btn.disabled = false
}
})
</script>
여기까지 하면 회원 가입 기능 구현이 완료되었다.
다음으로는 등록된 사용자가 로그인을 시도할 경우 DB의 데이터와 대조해 로그인 여부를 결정하는
로그인 기능을 구현해보도록 하자.
그건 다음 포스팅에서..
'Nodejs > Server' 카테고리의 다른 글
게시판 서버 만들기 #3 - 라우터 분리 (0) | 2022.03.20 |
---|---|
게시판 서버 만들기 #2 - 로그인 (0) | 2022.03.19 |
파일 업로드 #1 (0) | 2022.03.16 |
비동기 통신 - AJAX #1 (0) | 2022.03.16 |
Session #2 - 미들웨어 (0) | 2022.02.13 |