node.js

express

$ npm i express $ npm i -D nodemon
JavaScript
복사
nodemon

passport

$ npm i passport passport-local passport-kakao bcrypt
Bash
복사
// app.js const passport = require('passport'); const passportConfig = require('./passport'); const app = express(); passportConfig(); app.set('port', process.env.PORT || 3001); app.use(session({ resave: false, saveUninitialized: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, secure: false, }, })); app.use(passport.initialize()); app.use(passport.session());
JavaScript
복사
passport.initialize 미들웨어: 요청(req 객체)에 passport 설정
passport.session 미들웨어: req.session 객체에 passport 정보 저장
req.session 객체는 express-session에서 생성 → passport 미들웨어는 express-session 미들웨어보다 뒤에 연결해야 함
// passport/index.js const passport = require('passport'); const kakao = require('./kakaoStrategy'); const User = require('../models/user'); module.exports = () => { passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((id, done) => { User.findOne({ where: { id } }) .then(user => done(null, user)) .catch(err => done(err); }); kakao(); };
JavaScript
복사
passport.serializeUser
로그인 시 실행, req.session(세션) 객체에 어떤 데이터를 저장할지 정하는 메서드
매개변수로 user를 받고 done 함수에 두 번째 인수로 user.id를 넘기고 있음
done 함수의 첫 번째 인수: 에러가 발생했을 대 사용/ 두 번째 인수: 저장하고 싶은 데이터 넣음
로그인 → 사용자 데이터를 세션에 저장, 사용자 정보 모두 저장 시 용량 ↑ + 데이터 일관성 문제 발생 → 사용자 아이디만 저장
⇒ 사용자 정보 객체에서 아이디만 추려 세션에 저장
passport.deserializeUser
각 요청마다 실행, passport.session 미들웨어가 이 메서드 호출
serializeUserdone의 두 번째 인수로 넣었던 데이터 == deserializeUser의 매개변수(사용자 아이디)
serializeUser에서 세션에 저장했던 아이디 → 데이터베이스에서 사용자 정보 조회 → 조회한 정보를 req.user에 저장 ⇒ req.user ~> 로그인한 사용자의 정보 가져올 수 있음
⇒ 세션에 저장한 아이디 ~> 사용자 정보 객체 불러옴
→ 세션에 불필요한 데이터 담아두지 않기 위한 과정

전체 과정

1.
/auth/login 라우터 ~> 로그인 요청 들어옴
2.
라우터에서 passport.authenticate 메서드 호출
3.
로그인 전략(kakaoStrategy) 수행
4.
로그인 성공 시 사용자 정보 객체와 함께 req.login 호출
5.
req.login 메서드가 passport.serializeUser 호출
6.
req.session에 사용자 아이디만 저장해서 세션 생성
7.
express-session에 설정한 대로 브라우저에 connect.sid 세션 쿠키 전송
8.
로그인 완료

로그인 이후 과정

1.
요청이 들어옴
2.
라우터에 요청이 도달하기 전 passport.session 미들웨어가 passport.deserializeUser 메서드 호출
3.
connect.sid 세션 쿠키를 읽고 세션 객체를 찾아서 req.session으로 만듦
4.
req.session에 저장한 아이디로 데이터베이스에서 사용자 조회
5.
조회된 사용자 정보를 req.user에 저장
6.
라우터에서 req.user 객체 사용 가능
로그인 시 동작 → 전략(strategy): 로그인 과정을 어떻게 처리할지 설명하는 파일

미들웨어

라우터에 접근 권한을 제어
// middlewares/index.js exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send('로그인 필요'); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { next(); } else { const message = encodeURIComponent('로그인한 상태입니다.'); res.redirect(`/?error=${message}`); } };
JavaScript
복사

시퀄라이즈(sequelize)

ORM(Object-relational Mapping): 자바스크립트 객체와 데이터베이스의 릴레이션 매핑해주는 도구
자바스크립트 구분을 알아서 SQL로 바꿔줌
⇒ SQL 언어를 직접 사용 X 자바스크립트만으로 MySQL 조작 O
$ npm i express morgan sequelize sequelize-cli mysql2
Bash
복사
sequelize-cli: 시퀄라이즈 명령어를 실행하기 위한 패키지
mysql2: MySQL과 시퀄라이즈를 이어주는 드라이버
데이터베이스 프로그램 X
$ npx sequelize init
Bash
복사
npx: 전역 설치 X 명령어로 사용
⇒ config, models, migrations, seeders 폴더 생성
// models/index.js const Sequelize = require('sequelize'); const env = process.env.NODE_ENV || 'development'; const config = requrie('../config/config')[env]; const db = {}; const sequelize = new Sequelize(config.database, config.username, config.password, config); db.sequelize = sequelize; module.exprts = db;
JavaScript
복사
Sequelize: 시퀄라이즈 패키지이자 생성자
config/config.json에서 데이터베이스 설정을 불러온 후 new Sequelize ~> MySQL 연결 객체 생성
연결 객체 나중에 재사용 → db.sequelize에 넣어둠

MySQL 연결

// app.js const express = require('express'); const path = require('path'); const { sequelize } = require('./models'); const app = express(); app.set('port', process.env.PORT || 3001); sequelize.sync({ force: false }) .then(() => { console.log('데이터베이스 연결 성공'); }) .catch((err) => { console.log(err); }); app.use(morgan('dev'); app.use(express.static(path.join(__dirname, 'public'))); app.use(express.json()); app/use(express.urlencoded({ extended: false })); app.use((req, res, next) => { }); app.use((err,req, res,next) => { res.locals.message = err.message; res.locals.error = process.env.NODE_ENV !== 'production' > err : {}; res.status(err.status || 500); res.render('error'); }); app.listen(app.get('port'), () => { console.log(app.get('port'), '번 포트에서 대기중'); });
JavaScript
복사
// config/config.json { "development": { "username": "root", "password": "root 비밀번호", "database": "database 이름", "host": "127.0.0.1", "dialect": "mysql" }, ... }
JavaScript
복사
test, production: 테스트용, 배포용
process.env.NODE_ENVdevelopment일 때 적용

모델 정의

const Sequelize = require("sequelize"); class User extends Sequelize.Model { static initiate(sequelize) { User.init( { user_id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, }, nickname: { type: Sequelize.STRING(15), }, email: { type: Sequelize.STRING(255), unique: true, }, kakao_id: { type: Sequelize.STRING(30), unique: true, }, }, { sequelize, modelName: "User", tableName: "users", timestamps: true, charset: "utf8", collate: "utf8_general_ci", } ); } static associate(db) { User.hasMany(db.UpbitAccounts, { foreignKey: "user_id", as: "upbitAccounts", }); User.hasMany(db.BithumbAccounts, { foreignKey: "user_id", as: "bithumbAccounts", }); } } module.exports = User;
JavaScript
복사
static initiate 메서드: 테이블에 대한 설정
모델.init의 첫 번째 인수: 테이블 컬럼에 대한 설정
알아서 id를 기본키로 연결 → id 컬럼 적어줄 필요 X
여러 데이터베이스 처리 → MySQL 자료형과 다름
VARCHAR → STRING/ INT → INTEGER/ TINYINT → BOOLEAN/ DATETIME → DATE
INT UNSIGNED → INTEGER.UNSIGNED/ NOT NULL → allowNull: false/ UNIQUE → unique: true/ DEFAULT now() → defaultValue: Sequelize:NOW
두 번째 인수: 테이블 자체의 설정
sequelize: static initiate 메서드의 매개변수와 연결되는 옵션 / db.sequelize 객체 넣어야 함/ model/index.js에서 연결
timestamps: true → 시퀄라이즈는 createdAtupdatedAt 컬럼 추가
underscored: 테이블명과 컬럼명을 camel case로 만듦 → snake case로 바꾸는 옵션
ex. createdAt → created_at
modelName: 모델 이름 설정
tableName: 실제 데이터베이스의 테이블 이름/ 모델 이름: 소문자 및 복수형
ex. 모델 이름: User → 테이블 이름: users
paranoid: truedeletedAt 컬럼 추가/ 로우 삭제 → 완전히 지우지 X 지운 시각 기록 / 로우 조회 → deletedAt: null(삭제되지 X) 조회 ⇒ 로우 복원
charsetcollate: 각각 utf8utf8_general_ci로 설정 → 한글 입력 이모티콘까지 입력 → utf8mb4, utf8mb4_general_ci
static associate 메서드: 다른 모델들과의 관계

NoSQL vs. SQL

SQL(MySQL)
NoSQL(몽고디비)
규칙에 맞는 데이터 입력
자유로운 데이터 입력
테이블 간 JOIN 지원
컬렉션 간 JOIN 미지원
안정성, 일관성
확장성, 가용성
용어(테이블 ,로우, 컬럼)
용어(컬렉션, 다큐먼트, 필드)