본문 바로가기

웹개발

게시판-회원가입, 로그인

폴더 구성부터 설명하자면, model 폴더 안에는 users.js라는 파일을 만들고, router 폴더 안에는 auth.js 파일을 만든다.
views 폴더에는 auth 폴더를 만들고 그 안에 login.ejs, signup.ejs 파일을 만든다. 

 

<데이터베이스 테이블>
회원가입, 로그인 기능을 추가하기 위해서 users라는 테이블을 추가하여 사용자들의 정보를 담는다.
model 폴더 안의 users.js 파일이다.

module.exports = function(sequelize, DataTypes){ 
    return sequelize.define('users', {
      idx: {
        type : DataTypes.INTEGER,
        autoIncrement: true,
        primaryKey: true,
        allowNull: false
      },
      username: {
        type: DataTypes.STRING(255),
        allowNull: false
      },
      email: {
        type: DataTypes.STRING(255),
        allowNull: false
      },
      password: {
        type: DataTypes.STRING(255),
        allowNull: false
      }
    });
}

idx 설정은 posts때 만들었던 것과 같고 username, email, password 항목을 만들었다.

db.users = seq.import(__dirname+"/users.js");

 

db.js 파일에 이 코드를 추가해서 테이블 연결을 해준다.

 

 

<session>
회원가입과 로그인 요청에 대한 부분을 작성하기 전에 session을 사용할 수 있게 

npm install express-session

 

이렇게 다운을 받아주고,
app.js 파일에 가서

const session = require('express-session');

app.use(session({
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: false
}));

 

이 코드를 추가시켜 준다.
secret은 세션을 암호화하기 위해 사용되는 비밀 문자열이다.
resave는 세션이 변경되지 않아도 세션을 다시 저장할 것인지에 대한 옵션인데 불필요한 세션 저장 작업을 방지하기 위해서 false로 설정하였다.
saveUninitialized는 새로운 세션을 강제로 생성할 것인지에 대한 옵션인데 사이트를 방문해서 아무 작업도 하지 않았는데도 불필요한 세션을 생성시키는 것을 방지하기 위해 false로 설정하였다.

 

 

<라우팅>
router 폴더에 auth.js 파일을 만든다. auth.js 파일에 있는 링크들은 /auth 시작하는 링크에 할당되게 설정할 것이다.

const express = require('express');
const router = express.Router();
const db = require('../model/db')
const bcrypt = require('bcrypt');

const saltRounds = 10;

router.get('/signup', (req, res) => {
    res.render('auth/signup');
});

router.post('/signup', async (req, res) => {
    try {
        const { username, email, password } = req.body;
        const hashedPassword = await bcrypt.hash(password, saltRounds);
        
        await db.users.create({ username, email, password: hashedPassword });

        res.redirect('/');
        
    } catch (error) {
        res.status(500).json({ message: '회원가입 중 오류 발생', error: error.message });
    }
});

router.get('/login', (req, res) => {
    res.render('auth/login');
});

router.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = await db.users.findOne({ where: { email } });

        if (!user) {
            return res.status(400).json({ message: '유저를 찾을 수 없습니다.' });
        }

        const isMatch = await bcrypt.compare(password, user.password);
        
        if (!isMatch) {
            return res.status(400).json({ message: '비밀번호가 일치하지 않습니다.' });
        }

        req.session.user = {
            id: user.id,
            username: user.username,
            email: user.email
        };

        res.redirect('/');

    } catch (error) {
        res.status(500).json({ message: '로그인 중 오류 발생', error: error.message });
    }
});

router.get('/logout', (req, res) => {
    req.session.destroy();
    res.redirect('/');
})

module.exports = router;

 

위에 기본 설정을 쓸 때 못 보던 bcrypt라는 것이 있는데 이는 비밀번호 암호화를 위해 필요한 것이다.

saltRounds = 10도 bcrypt를 사용할 때 필요한 것이다.

bcrypt를 사용하기 위해 미리

npm install bcrypt

로 설치를 한다.

 

1. signup
get 방식의 /signup 링크는 auth 폴더에 있는 signup.ejs 파일을 렌더링한다. 
post 방식의 /signup 링크는 회원가입을 한다. 
사용자의 입력값 username, email, password를 받아서 저장하고
이 코드를 통해 암호화된 password를 hashedPassword라는 변수에 저장한다. 
bcrypt는 패스워드의 안전한 해싱을 위한 라이브러리이다. 
해싱은 같은 문자열에 대해서 같은 결괏값이 나온다는 한계점이 있다.
salt는 이를 보완하기 위해 문자열에 무작위 데이터를 붙여서 해싱을 한다.
saltRouds를 10으로 설정하고 hash 함수를 돌리면 10번 반복해서 연산을 해준다. 

여기서 await라는 코드와, 처음에 routing을 할 때 async(req, res)라는 못 보던게 있는데 이는 비동기, 동기 처리와 관련된 것이다. 비동기는 병렬적으로 태스크를 수행하고, 동기는 직렬적으로 태스크를 수행한다.
비동기는 한번에 여러 작업, 연산을 하는 것이고 동기는 한번에 하나씩 한다. 시간은 동기가 더 오래 걸린다.
비동기 처리는 여러 콜백 함수들로 인해 가독성이 떨어지고 처리 도중 발생한 에러를 처리하기 힘들다.
promise는 이를 해결하기 위한 비동기 처리 패턴으로, 처리가 성공했는지, 실패했는지 등의 정보를 담고 있다.
bcrypt.hash 함수가 promise를 반환한다. 
await는 async 함수 안에서만 사용이 되는데 promise 작업이 끝날 때까지 기다리게 한다.
hash를 10번 반복해서 연산하려면 시간이 꽤 걸릴 수 있기 때문에 비동기 처리로 구성을 하였고
hashedPassword가 연산 완료 되어야 밑의 코드를 실행할 수 있기 때문에 await로 기다리게 하였다.

이렇게 암호화된 password를 가지고 db.users.create로 데이터를 추가하고 redirect로 다시 돌아갔다.

2. login
get 방식의 /login 링크는 auth 폴더에 있는 login.ejs 파일을 렌더링한다. 
post 방식의 /login 링크는 로그인을 한다.
사용자의 입력으로 email, password를 가져오고, 데이터베이스에서 email로 정보를 가져온다.
유저가 없는 경우에 대한 에러 처리를 하고, bcrypt.compare 함수를 이용해 비밀번호가 맞는지 검사한다.
비밀번호가 일치하는 경우에는 session을 설정하고 redirect로 원래 페이지로 돌아갔다.  

3. logout
get 방식의 /logout 링크는 다른 페이지로 이동하지 않고
그냥 session을 destroy 함수로 붕괴시키고 메인 페이지로 redirect 시킨다.

 

const authRouter = require('./router/auth');
app.use('/auth', authRouter);

마지막으로 app.js 파일 안에 authRouter를 설정해서 auth.js 파일 안에 라우팅된 링크들이 /auth에서 시작하도록 설정한다.

 

 

<view 설정>
views 폴더 안에 auth 폴더를 만들고 그 안에 signup.ejs, login.ejs 파일을 만든다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>회원가입</title>
    <link href="/public/main.css" rel="stylesheet" />
</head>
<body>
    <h1>회원가입</h1>
    <form action="/auth/signup" method="post">
        <div>
            <label for="username">사용자 이름:</label>
            <input type="text" id="username" name="username" required>
        </div>
        <div>
            <label for="email">이메일:</label>
            <input type="email" id="email" name="email" required>
        </div>
        <div>
            <label for="password">비밀번호:</label>
            <input type="password" id="password" name="password" required>
        </div>
        <div>
            <button type="submit">회원가입</button>
        </div>
    </form>
    <a href="/auth/login">이미 계정이 있으신가요? 로그인</a>
</body>
</html>

signup.ejs 파일은 간단하게 username, email, password를 입력할 input 태그를 만들고, 회원가입 버튼을 만들어서
auth/sighnup으로 post 요청을 보내서 회원가입을 처리한다.
아래에는 로그인 창으로 가는 링크를 넣었다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>로그인</title>
    <link href="/public/main.css" rel="stylesheet" />
</head>
<body>
    <h1>로그인</h1>
    <form action="/auth/login" method="post">
        <div>
            <label for="email">이메일:</label>
            <input type="email" id="email" name="email" required>
        </div>
        <div>
            <label for="password">비밀번호:</label>
            <input type="password" id="password" name="password" required>
        </div>
        <div>
            <button type="submit">로그인</button>
        </div>
    </form>
    <a href="/auth/signup">계정이 없으신가요? 회원가입</a>
</body>
</html>

login.ejs 파일도 간단한데, email과 password를 입력하는 태그를 만들고 /auth/login으로 post 요청을 보내서
로그인을 처리한다. 마찬가지로 밑에 회원가입 창으로 가는 링크를 넣었다.


'웹개발' 카테고리의 다른 글

게시판-자기 글 수정, 삭제, 비밀글  (0) 2023.10.25
게시판-이메일 인증, 아이디 찾기  (0) 2023.10.25
게시판-CRUD  (0) 2023.10.24
flask - mySQL 연동  (0) 2023.10.22
flask  (0) 2023.10.20