728x90
반응형
express
$ npm i express
$ npm i -D nodemon
- nodemon
passport
$ npm i passport passport-local passport-kakao bcrypt
// 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());
- `passport.initialize` 미들웨어: 요청(req 객체)에 `passport` 설정
- `passport.session` 미들웨어: `req.session` 객체에 `passport` 정보 저장
- `req.session` 객체는 `express-session`에서 생성
→ `passport` 미들웨어는 `express-session` 미들웨어보다 뒤에 연결해야 함
- `req.session` 객체는 `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();
};
- `passport.serializeUser`
- 로그인 시 실행, `req.session`(세션) 객체에 어떤 데이터를 저장할지 정하는 메서드
- 매개변수로 user를 받고 done 함수에 두 번째 인수로 user.id를 넘기고 있음
- done 함수의 첫 번째 인수: 에러가 발생했을 대 사용/ 두 번째 인수: 저장하고 싶은 데이터 넣음
- 로그인 → 사용자 데이터를 세션에 저장, 사용자 정보 모두 저장 시 용량 ↑ + 데이터 일관성 문제 발생 → 사용자 아이디만 저장
⇒ 사용자 정보 객체에서 아이디만 추려 세션에 저장
- `passport.deserializeUser`
- 각 요청마다 실행, `passport.session` 미들웨어가 이 메서드 호출
- `serializeUser`의 done의 두 번째 인수로 넣었던 데이터 == `deserializeUser`의 매개변수(사용자 아이디)
- `serializeUser`에서 세션에 저장했던 아이디 → 데이터베이스에서 사용자 정보 조회 → 조회한 정보를 `req.user`에 저장
⇒ `req.user` ~> 로그인한 사용자의 정보 가져올 수 있음
⇒ 세션에 저장한 아이디 ~> 사용자 정보 객체 불러옴
→ 세션에 불필요한 데이터 담아두지 않기 위한 과정
전체 과정
- /auth/login 라우터 ~> 로그인 요청 들어옴
- 라우터에서 `passport.authenticate` 메서드 호출
- 로그인 전략(`kakaoStrategy`) 수행
- 로그인 성공 시 사용자 정보 객체와 함께 `req.login` 호출
- `req.login` 메서드가 `passport.serializeUser` 호출
- `req.session`에 사용자 아이디만 저장해서 세션 생성
- `express-session`에 설정한 대로 브라우저에 `connect.sid` 세션 쿠키 전송
- 로그인 완료
로그인 이후 과정
- 요청이 들어옴
- 라우터에 요청이 도달하기 전 `passport.session` 미들웨어가 `passport.deserializeUser` 메서드 호출
- `connect.sid` 세션 쿠키를 읽고 세션 객체를 찾아서 `req.session`으로 만듦
- `req.session`에 저장한 아이디로 데이터베이스에서 사용자 조회
- 조회된 사용자 정보를 `req.user`에 저장
- 라우터에서 `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}`);
}
};
시퀄라이즈(sequelize)
ORM(Object-relational Mapping): 자바스크립트 객체와 데이터베이스의 릴레이션 매핑해주는 도구
자바스크립트 구분을 알아서 SQL로 바꿔줌
⇒ SQL 언어를 직접 사용 X 자바스크립트만으로 MySQL 조작 O
$ npm i express morgan sequelize sequelize-cli mysql2
- `sequelize-cli`: 시퀄라이즈 명령어를 실행하기 위한 패키지
- mysql2: MySQL과 시퀄라이즈를 이어주는 드라이버
- 데이터베이스 프로그램 X
$ npx sequelize init
- 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;
- 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'), '번 포트에서 대기중');
});
// config/config.json
{
"development": {
"username": "root",
"password": "root 비밀번호",
"database": "database 이름",
"host": "127.0.0.1",
"dialect": "mysql"
},
...
}
- test, production: 테스트용, 배포용
- `process.env.NODE_ENV`가 development일 때 적용
모델 정의
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;
- `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` → 시퀄라이즈는 createdAt과 updatedAt 컬럼 추가
- `underscored`: 테이블명과 컬럼명을 camel case로 만듦 → snake case로 바꾸는 옵션
- ex. createdAt → created_at
- `modelName`: 모델 이름 설정
- `tableName`: 실제 데이터베이스의 테이블 이름/ 모델 이름: 소문자 및 복수형
- ex. 모델 이름: User → 테이블 이름: users
- `paranoid: true` → deletedAt 컬럼 추가/ 로우 삭제 → 완전히 지우지 X 지운 시각 기록
/ 로우 조회 → deletedAt: null(삭제되지 X) 조회 ⇒ 로우 복원 - `charset`과 `collate`: 각각 utf8과 utf8_general_ci로 설정 → 한글 입력 이모티콘까지 입력
→ utf8mb4, utf8mb4_general_ci
- `sequelize`: `static initiate` 메서드의 매개변수와 연결되는 옵션
- 모델.init의 첫 번째 인수: 테이블 컬럼에 대한 설정
- `static associate` 메서드: 다른 모델들과의 관계
SQL vs. NoSQL
SQL(MySQL) NoSQL(몽고디비)
규칙에 맞는 데이터 입력 | 자유로운 데이터 입력 |
테이블 간 JOIN 지원 | 컬렉션 간 JOIN 미지원 |
안정성, 일관성 | 확장성, 가용성 |
용어(테이블 ,로우, 컬럼) | 용어(컬렉션, 다큐먼트, 필드) |
728x90
반응형
'BE > NodeJS' 카테고리의 다른 글
[Node.js 교과서 - 개정 3판] 4장 http 모듈로 서버 만들기 (0) | 2024.08.19 |
---|---|
[Node.js 교과서 - 개정 3판] 1장 노드 시작하기 (0) | 2024.08.16 |