[34] Sequelize, cookie, session을 이용한 회원가입 + 로그인 + 게시판 예제(1)

minjeong·2024년 2월 16일
0
post-thumbnail

맥북 고장이슈로 회원가입(2)는 추후에 작성하겠다... 수정본을 github에 올리기 전에 맡기게 되어서 말이다😂

게시판에 접근하려면 회원이어야만 하고, 회원정보 수정 및 탈퇴, 게시물을 직접 작성할 수 있는 예제를 만들어보겠다.

📌 sequelize 패키지

우선, Sequelize 란?
• 자바스크립트 구문을 알아서 SQL로 변환해준다.
• DB 작업을 쉽게 할 수 있도록 도와주는 ORM 라이브러리 중 하나
Sequelize 쿼리문
findAll() - select
findOne() - select
create() - insert
update() - update
destroy() -delete

Sequelize 설치

npm i sequelize sequelize-cli

• sequelize : 시퀄라이즈 패키지
• sequelize-cli : 시퀄라이즈 명령어 실행

npx sequelize init

-> 프로젝트 처음 시작할 때 유용(필수는 아니지만 추천)

-> 자동으로 4개의 폴더 생성

cookie : 웹 브라우저(클라이언트) 에 저장되는 키와 값이 들어있는 작은 데이터 파일
• 이름, 값, 만료일, 경로 정보로 구성되어 있다.
session : 웹 서버에 저장되는 쿠키
• 사용자가 웹 브라우저를 통해 접속한 시점부터 연결을 끝내는 시점까지의 시간 동안 일련의 요구를 하나의 상태로 보고 그 상태를 유지 시킨다.
• Ex) 로그인 유지

npm i cookie-parser express-session

1. config.js

  • 기존 방식은 모델 폴더 안에 파일이 여러 개 있다면, 아래 데이터베이스 연결 코드를 모든 파일에 적어줘야 한다.

=> 데이터 베이스 명을 바꾸게 된다면 모든 파일을 다 바꿔줘야해서 매우 번거롭다. so, config.js 파일로 한번에 처리하기!

require('dotenv').config();

module.exports = {
    development: {
        username: 'min',
        password: process.env.PASSWORD,
        database: process.env.DATABASE,
        host: process.env.HOST,
        dialect: 'mysql',
        port: process.env.PORT,
    },
    test: {
        username: 'root',
        password: null,
        database: 'database_test',
        host: '127.0.0.1',
        dialect: 'mysql',
    },
    production: {
        username: 'root',
        password: null,
        database: 'database_production',
        host: '127.0.0.1',
        dialect: 'mysql',
    },
};

➡️ 개발시 테스트용으로 할 때는 development, test용을 자주 쓴다. 나중에 서버를 시작할때 package-json에서 정의했던 값으로 시작하면 된다. npm run [정의한 값] 으로 말이다.(start:dev...) 아래는 예시이다.

"scripts": {
    "start:dev": "cross-env NODE_ENV=development nodemon app.js",
    "start:prod": "cross-env NODE_ENV=production nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

중요 정보는 .env 파일에 언급하고(직접 생성), .gitignore*.env 추가하면 git에 올라가지 않는다.

2. app.js

const express = require('express');
const session = require('express-session'); //설치 완료 후 2,3행 식이 반드시 필요함
const cookieParser = require('cookie-parser');
const db = require('./models');
const app = express();
const PORT = 8000;

//미들웨어
app.set('view engine', 'ejs');
app.use(express.json()); //위아래 식도 쿠키와 세션을 사용하려면 필수적
app.use(cookieParser());
app.use(
    session({
        secret: 'my-board', //세션 암호화을 위한 비밀키
        resave: false, //항상 세션을 저장할지 여부
        saveUninitialized: true, //초기화되지 않는 세션을 스토어에 저장
        cookie: {
            httpOnly: true, //클라이언트에서 쿠키를 확인하지 못하도록 함
            secure: false, //https에서만 사용
        },
    })
);

//라우터
const indexRouter = require('./routes');
app.use('/', indexRouter);
const postRouter = require('./routes/post');
app.use('/api/post', postRouter);
const memberRouter = require('./routes/member');
app.use('/api/member', memberRouter);

//404
app.get('*', (req, res) => {
    res.status(404).render('404');
});

//테이블싱크
//force:true 새로고침마다 항상 테이블을 삭제 후 재생성
//force:false(기본값) 테이블이 존재하면 패쓰, 없으면 생성
//DB에서 따로 테이블을 생성하지 않아도 된다.
db.sequelize.sync({ force: false }).then(() => {
    app.listen(PORT, () => {
        console.log(`http://localhost:${PORT}`);
    });
});

3. controller/index.js


const main = (req, res) => {
    res.render('index');
};

const post = (req, res) => {
    res.render('post'); //전체 글 페이지
};

const detail = (req, res) => {
    res.render('detail'); //상세페이지
};

const write = (req, res) => {
    res.render('write'); //글쓰기 페이지
};

const signup = (req, res) => {
    res.render('signup'); //회원가입 페이지
};

const login = (req, res) => {
    res.render('login'); //로그인 페이지
};

const profile = (req, res) => {
    res.render('profile'); //정보조회 페이지
};

module.exports = { main, post, detail, write, signup, login, profile };

4. controller/member.js

  • 하나의 코드가 실행될때 다른 코드가 실행되지 않기 위해 비동기 사용.
const { Member, Profile } = require('../models');
const SAVEID = 'saveId'; //상수값으로 만들어서 저장

//회원가입
exports.signup = async (req, res) => {
    const { userId, pw, username, age, email } = req.body;
    //존재여부확인
    const find = await Member.findOne({ where: { userId } });
    console.log('find', find);

    if (find) {
        res.json({ success: false, message: '이미 존재하는 회원' });
    } else {
        //생성 create
        const result = await Member.create({ userId, password: pw });
        console.log('signup', result);
        const result2 = await Profile.create({ username, age, email, memberId: result.id });
        console.log('profile', result2);
        res.json({ success: true });
    }
};
//로그인
exports.login = async (req, res) => {
    const { userId, pw, save } = req.body;
    //검색 findOne
    const result = await Member.findOne({ where: { userId, password: pw } });
    console.log('login', result);
    if (result) {
        req.session.member = result; //로그인한 사람에 대해 저장함

        if (save === 'save') {
            res.cookie(SAVEID, result.id, { maxAge: 100000, httpOnly: true });
        } else {
            res.clearCookie(SAVEID);
        }

        res.json({ success: true, result });
    } else {
        res.json({ success: false });
    }
};
//회원조회
exports.find = async (req, res) => {
    console.log(req.session.member);
    const { id } = req.session.member;
    //findByPk : PK를 기반으로 데이터를 찾는 메서드
    const result = await Member.findByPk(id, {
        attributes: ['userId', 'password'], // 두개의 필드만 반환
        include: [{ model: Profile, attributes: ['username', 'age', 'email'] }],
    }); //Profile 모델의 정보도 가져오고, 'username', 'age', 'email' 필드만 가져온다.
    console.log('result', result);
    res.json({ success: true, result });
};
//정보수정
exports.update = async (req, res) => {
    const { pw, username, age, email } = req.body;
    const { id } = req.session.member; //이때 id는 프론트가 아니라 백엔드에서 가져옴
    const result = await Member.update({ password: pw }, { where: { id } });
    console.log('update', result);
    const result2 = await Profile.update({ username, age, email }, { where: { memberId: id } });
    res.json({ success: true });
};
//회원탈퇴
exports.delete = async (req, res) => {
    const { id } = req.body;
    const result = await Member.destroy({ where: { id } });
    console.log('delete', result);
    res.json({ success: true });
};
//로그아웃
exports.logout = (req, res) => {
    if (req.session.member) {
        //세션제거
        req.session.destroy(() => {
            res.clearCookie(SAVEID);
            res.json({ success: true });
        });
    } else {
        res.json({ success: false, message: '로그인 상태가 아닙니다.' });
    }
};
//쿠키 보내기
exports.getCookie = (req, res) => {
    if (req.cookies[SAVEID]) {
        res.json({ isLoggedIn: true });
    } else {
        res.json({ isLoggedIn: false });
    }
};

➡️ 회원의 전반적인 로그인, 정보 관련한 로직이다.
➡️ 주석도 포함해서 같이 확인하자.


이번 글은 예제를 실행하기 위한 모든 코드를 올리진 않고, 새로 배웠거나 중요하다고 생각한 부분 위주로 올려보았다.
그런데도 일부 개념설명이 들어가서 그런지, 예제코드를 얼마 쓰지도 못했는데 너무 길어져버렸다..! 다음 글에 이어서 작성해보겠다!

profile
중요한 건 꺾여도 다시 일어서는 마음

0개의 댓글