Sequelize
- node.js.에서 mysql 등 RDBMS를 쉽게 다룰 수 있도록 도와주는 라이브러리
npm i sequelize
https://sequelize.org/
MongoDB
- MongoDB는 NoSQL 데이터베이스로 문서 기반 데이터 저장 방식을 채택한 오픈소스 DBMS
- 관계형 데이터베이스와는 달리 스키마가 없으며, BSON(Binary JSON) 형태로 데이터를 저장
- 유연성이 좋고, 대규모 데이터 처리에 용이
MongoDB Atlas
- MongoDB의 관리형 클라우드 데이터베이스 서비스
- MongoDB 데이터베이스를 클라우드에서 호스팅 하고 관리하는 것을 중심으로 하며, 개발자 및 기업이 손쉽게 애플리케이션을 빌드하고 배포할 수 있도록 지원
// db/database.js
import mysql from 'mysql2';
import { config } from '../config.js';
import SQ from 'sequelize';
const { host, user, database, password } = config.db;
export const sequelize = new SQ.Sequelize(database, user, password, {
host,
dialect: 'mysql',
logging: false
})
// app.js
// app.js
import express from "express";
import morgan from "morgan";
import tweetsRouter from './router/tweets.js'
import authRouter from './router/auth.js'
import { config } from "./config.js";
import { sequelize } from "./db/database.js";
const app = express();
app.use(express.json()); // json로 연결
app.use(morgan("dev"));
//tweetsRouter 미들웨어 등록
app.use('/tweets', tweetsRouter);
//authsRouter 미들웨어 등록
app.use('/auth', authRouter);
app.use((req, res, next) => {
res.sendStatus(404);
});
// DB 연결 테스트!
// db.getConnection().then(Connection => console.log(Connection));
sequelize.sync().then(() => {
app.listen(config.host.port);
});
// app.listen(config.host.port);
// data/tweet.js
// data/tweet.js
// import { db } from '../db/database.js';
import SQ from 'sequelize';
import { sequelize } from '../db/database.js';
import { User } from './auth.js';
const DataTypes = SQ.DataTypes;
const Sequelize = sequelize;
const INCLUDE_USER = {
attributes: [
'id',
'text',
'createdAt',
'userId',
[Sequelize.col('user.name'), 'name'],
[Sequelize.col('user.username'), 'username'],
[Sequelize.col('user.url'),'url']
],
include: {
model: User,
attributes : [],
}
}
const ORDER_DESC = {
order: [['createdAt', 'DESC']]
}
// const SELECT_JOIN = 'select tw.id, tw.text, tw.createdAt, tw.userId, us.username, us.email, us.url from tweets as tw join users as us on tw.userId = us.id';
// const ORDER_DESC = 'order by tw.createdAt desc';
const Tweet = sequelize.define(
'tweet',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
text: {
type: DataTypes.TEXT,
allowNull: false
}
},
{ timestamps: false }
);
Tweet.belongsTo(User);
// 모든 트윗을 리턴
export async function getAll() {
return Tweet.findAll({ ...INCLUDE_USER, ...ORDER_DESC });
// return db.execute(`${SELECT_JOIN} ${ORDER_DESC}`).then ((result) => {
// console.log(result);
// return result;
// });
}
// 해당 아이디에 대한 트윗을 리턴
export async function getAllByUsername(username){
return Tweet.findAll({ ...INCLUDE_USER, ...ORDER_DESC, include: {
...INCLUDE_USER.include, where: {username}
} });
// return db.execute(`${SELECT_JOIN} where username = ? ${ORDER_DESC}`, [username]).then ((result) => {
// console.log(result);
// return result;
// });
}
// 글번호에 대한 트윗을 리턴
export async function getById(id){
return Tweet.findOne({ where: {id}, ...INCLUDE_USER });
// return db.execute(`${SELECT_JOIN} where tw.id = ? ${ORDER_DESC}`, [id]).then ((result) => {
// console.log(result);
// return result;
// });
}
// 트윗을 작성
export async function create(text, userId){
return Tweet.create({ text, userId }).then((data) => this.getById(data.dataValues.id));
// return db.execute('insert into tweets (text, userId) values (?, ?)', [text, userId]).then ((result) => {
// console.log(result);
// return getById(result[0].insertId);
// });
}
// 트윗을 변경
export async function update(id, text){
return Tweet.findByPk(id, INCLUDE_USER).then((tweet) => {
tweet.text = text;
return tweet.save();
})
// return db.execute('update tweets set text = ? where id =?', [text, id]).then ((result) => {
// console.log(result);
// return getById(id);
// });
}
// 트윗을 삭제
export async function remove(id){
return Tweet.findByPk(id).then((tweet) => {
tweet.destroy();
})
// return db.execute('delete from tweets where id = ?', [id]);
}
// data/auth.js
// data/auth.js
//import { db } from '../db/database.js';
import SQ from 'sequelize';
import { sequelize } from '../db/database.js';
const DataTypes = SQ.DataTypes;
export const User = sequelize.define(
'user',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true
},
username: {
type: DataTypes.STRING(50),
allowNull: false
},
password: {
type: DataTypes.STRING(150),
allowNull: false
},
name: {
type: DataTypes.STRING(50),
allowNull: false
},
email: {
type: DataTypes.STRING(50),
allowNull: false
},
url: DataTypes.STRING(1000)
},
{ timestamps: false }
);
// 아이디(username) 중복 검사
export async function findByUsername(username){
return User.findOne({where: {username}});
}
// id 중복 검사
export async function findById(id){
return User.findByPk(id);
}
export async function createUser(user){
return User.create(user).then((data) => data.dataValues.id)
}
// export async function login(username){
// return users.find((users) => users.username === username);
// }
// {
// "username":"orange",
// "password":"12345",
// "name":"오렌지",
// "email":"orange@orange.com",
// "url":"https://img.freepik.com/premium-vector/banana-cute-kawaii-style-fruit-character-vector-illustration_787461-1772.jpg"
// }
----------------------------------------------------------------------------------------------------
// .env
# DB
# DB_HOST=127.0.0.1
DB_HOST=mongodb+srv://zmwaexp:<password>@cluster0.ul4snii.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
DB_PORT=3306
DB_USER=root
DB_PASSWORD=1234
DB_DATABASE=kdt
# JWT
JWT_SECRET=abcd1234%^&*
JWT_EXPORES_SEC=172800
# BCRYPT
BCRYPT_SALT_ROUNDS=10
# SERVER
PORT=8080
//app.js
// app.js
import express from "express";
import morgan from "morgan";
import tweetsRouter from './router/tweets.js'
import authRouter from './router/auth.js'
import { config } from "./config.js";
// import { sequelize } from "./db/database.js";
import { connectDB } from "./db/database.js";
const app = express();
app.use(express.json()); // json로 연결
app.use(morgan("dev"));
//tweetsRouter 미들웨어 등록
app.use('/tweets', tweetsRouter);
//authsRouter 미들웨어 등록
app.use('/auth', authRouter);
app.use((req, res, next) => {
res.sendStatus(404);
});
// DB 연결 테스트!
// db.getConnection().then(Connection => console.log(Connection));
// sequelize.sync().then(() => {
// app.listen(config.host.port);
// });
connectDB().then((db) => {
console.log('몽고DB 연결 성공!!');
app.listen(config.host.port);
}).catch(console.error);
// app.listen(config.host.port);
// db/database.js
import { config } from '../config.js';
import MongoDB from 'mongodb';
let db;
export async function connectDB(){
return MongoDB.MongoClient.connect(config.db.host).then((client) => db = client.db());
}
export function getUsers(){
return db.collection('users'); //데이터를 넣을 때 users객체에 넣겠다
}
export function getTweets(){
return db.collection('tweets');
}
// data/auth.js
// data/auth.js
import MongoDB from 'mongodb';
import { getUsers } from '../db/database.js';
const ObjectID = MongoDB.ObjectId;
//import { db } from '../db/database.js';
// import MongoDB from 'mongodb';
// // import SQ from 'sequelize';
// // import { sequelize } from '../db/database.js';
// const DataTypes = SQ.DataTypes;
// export const User = sequelize.define(
// 'user',
// {
// id: {
// type: DataTypes.INTEGER,
// autoIncrement: true,
// allowNull: false,
// primaryKey: true
// },
// username: {
// type: DataTypes.STRING(50),
// allowNull: false
// },
// password: {
// type: DataTypes.STRING(150),
// allowNull: false
// },
// name: {
// type: DataTypes.STRING(50),
// allowNull: false
// },
// email: {
// type: DataTypes.STRING(50),
// allowNull: false
// },
// url: DataTypes.STRING(1000)
// },
// { timestamps: false }
// );
// // 아이디(username) 중복 검사
export async function findByUsername(username){
return getUsers().find({username}).next().then(mapOptionalUser);
}
// export async function findByUsername(username){
// return User.findOne({where: {username}});
// }
// // id 중복 검사
export async function findById(id){
return getUsers().find({_id: new ObjectID(id)}).next().then(mapOptionalUser);
}
// export async function findById(id){
// return User.findByPk(id);
// }
export async function createUser(user){
return getUsers().insertOne(user).then((result) => console.log(result.insertedId.toString()));
}
// export async function createUser(user){
// return User.create(user).then((data) => data.dataValues.id)
// }
function mapOptionalUser(user){
return user ? { ...user, id: user._id.toString() } : user;
}
// export async function login(username){
// return users.find((users) => users.username === username);
// }
// {
// "username":"orange",
// "password":"12345",
// "name":"오렌지",
// "email":"orange@orange.com",
// "url":"https://img.freepik.com/premium-vector/banana-cute-kawaii-style-fruit-character-vector-illustration_787461-1772.jpg"
// }
// data/tweet.js
// data/tweet.js
// import { db } from '../db/database.js';
// import SQ from 'sequelize';
//import { sequelize } from '../db/database.js';
import MongoDB from 'mongodb';
import { getTweets, getUsers } from '../db/database.js';
import * as authRepository from './auth.js';
const ObjectID = MongoDB.ObjectId;
// import { User } from './auth.js';
// 모든 트윗을 리턴
export async function getAll() {
return getTweets().find().sort({ createdAt: -1 }).toArray().then(mapTweets);
}
// 해당 아이디에 대한 트윗을 리턴
export async function getAllByUsername(username){
return getTweets().find({username}).sort({createdAt: -1 }).toArray().then(mapTweets);
}
// 글번호에 대한 트윗을 리턴
export async function getById(id){
return getTweets().find({ _id: new ObjectID(id)}).next().then(mapOptionalTweet);
}
// 트윗을 작성
export async function create(text, userId){
return authRepository.findById(userId).then((user) => getTweets().insertOne({
text,
userId,
username: user.username,
url: user.url
})).then((result) => getById(result.insertedId)).then(mapOptionalTweet);
}
// 트윗을 변경
export async function update(id, text){
return getTweets().findOneAudUpdate({_id: new ObjectID(id)}, {$set: {text}}, {returnDocument: 'after'}).then((result) => result).then(mapOptionalTweet);
}
// 트윗을 삭제
export async function remove(id){
return getTweets().deleteOne({_id: new ObjectID(id)});
}
function mapTweets(tweets){
return tweets.map(mapOptionalTweet);
}
function mapOptionalTweet(tweet){
return tweet ? { ...tweet, id: tweet.insertedId} : tweet;
}
🤍
ChatGPT에게 물어본 오늘의 공부-
✔️ Sequelize
Sequelize는 Node.js 어플리케이션을 위한 ORM(Object-Relational Mapping) 라이브러리입니다. 이 라이브러리는 주로 SQL 데이터베이스를 다룰 때 사용되며, JavaScript로 쿼리를 작성하는 대신에 객체 지향적인 방식으로 데이터베이스와 상호작용할 수 있도록 도와줍니다. Sequelize를 사용하면 데이터베이스의 테이블을 JavaScript 클래스로 모델링할 수 있으며, 이를 통해 데이터를 쉽게 생성, 조회, 수정 및 삭제할 수 있습니다.
Sequelize의 주요 기능 및 장점은 다음과 같습니다:
- 객체 지향적인 모델링: 데이터베이스 테이블을 JavaScript 클래스로 표현하여 코드를 보다 직관적으로 만듭니다.
- 데이터 유효성 검사: 모델에 데이터를 삽입하기 전에 유효성을 검사하고 오류를 방지합니다.
- 쿼리 생성 및 실행: Sequelize는 쿼리를 생성하고 실행하는 다양한 방법을 제공하여 SQL 작성을 간소화합니다.
- 데이터베이스 간 이식성: Sequelize는 다양한 SQL 데이터베이스를 지원하여 코드의 이식성을 높입니다.
- 마이그레이션 지원: 데이터베이스 스키마의 변경 사항을 관리하고 적용할 수 있는 마이그레이션 도구를 제공합니다.
이러한 기능들을 통해 Sequelize는 Node.js 기반 어플리케이션의 데이터베이스 관련 작업을 보다 쉽고 효율적으로 처리할 수 있도록 도와줍니다.
이 형식은 Sequelize 쿼리에서 사용되는 옵션 객체입니다. Sequelize는 Node.js에서 사용되는 ORM(Object-Relational Mapping) 라이브러리로, 데이터베이스 테이블과 JavaScript 객체 간의 매핑을 도와줍니다.
위의 코드에서 INCLUDE_USER는 Sequelize에서 쿼리를 수행할 때 사용되는 옵션 객체로, 특정 모델에 대한 쿼리를 실행할 때 해당 모델과 연결된 다른 모델의 데이터도 함께 가져올 수 있도록 설정합니다.
해당 코드에서 사용된 옵션은 다음과 같습니다:
- attributes: 가져올 열(column)을 지정합니다. 여기서는 트윗(tweets) 테이블의 id, text, createdAt, userId 및 사용자(user) 테이블의 name, username, url을 지정합니다. Sequelize.col을 사용하여 사용자 모델의 특정 열에 대한 별칭을 설정합니다.
- include: 연결된 모델의 데이터를 가져오도록 지정합니다. 여기서는 User 모델을 가져오도록 설정합니다.
이렇게 함으로써 Sequelize는 트윗 테이블과 사용자 테이블을 조인하여 필요한 데이터를 함께 가져올 수 있게 됩니다
✔️ sequelize.define()
sequelize.define() 함수는 Sequelize에서 모델을 정의할 때 사용됩니다. 이 함수를 사용하여 데이터베이스 테이블의 모델을 JavaScript 객체로 정의하고 Sequelize 모델을 생성합니다. 이를 통해 JavaScript 코드를 사용하여 데이터베이스 테이블에 대한 작업을 수행할 수 있습니다.
일반적으로 다음과 같은 방식으로 sequelize.define() 함수를 사용합니다:
이와 같이 정의된 모델은 Sequelize 모델로 사용될 수 있으며, 이를 통해 해당 테이블에 대한 쿼리 및 데이터 조작 작업을 수행할 수 있습니다.
✔️ belongsTo()
belongsTo() 함수는 Sequelize에서 관계를 정의할 때 사용되는 함수 중 하나입니다. 이 함수는 다른 모델에 대한 외부 키(foreign key)가 현재 모델에 속함을 나타내는 일대일 또는 일대다 관계를 정의할 때 사용됩니다.
예를 들어, 사용자(User) 모델과 게시물(Post) 모델이 있다고 가정해봅시다. 각각의 게시물은 특정 사용자에게 속하므로(Post 모델은 User 모델에 속함), 게시물(Post) 모델은 사용자(User) 모델과 일대다 관계를 가집니다.
이러한 관계를 정의하기 위해 belongsTo() 함수를 사용할 수 있습니다. 예를 들어:
위의 코드에서 Post.belongsTo(User, { foreignKey: 'userId' })는 "게시물(Post) 모델은 사용자(User) 모델에 속한다"는 의미입니다. foreignKey 옵션은 외부 키의 이름을 정의하며, 위의 예시에서는 userId가 사용되었습니다. 이것은 게시물 테이블에서 사용자를 식별하기 위한 외부 키입니다.
belongsTo() 함수를 사용하면 두 모델 사이의 관계를 설정할 수 있고, 이를 통해 Sequelize가 적절한 SQL 쿼리를 생성하여 데이터베이스에서 관련된 데이터를 가져오거나 조작할 수 있습니다.
✔️ { include: }
{ include: }는 Sequelize에서 쿼리 실행 시 관련된 모델의 데이터를 함께 가져오기 위한 옵션입니다. 이 옵션을 사용하면 주요 모델과 연결된 다른 모델의 데이터를 함께 조회할 수 있습니다.
예를 들어, 사용자(User)와 사용자가 작성한 게시물(Post)이라는 두 모델이 있다고 가정해보겠습니다. 사용자 모델에는 많은 게시물이 연결되어 있습니다. 이때 User.findAll()로 사용자 목록을 가져오면서 각 사용자의 게시물도 함께 가져오고 싶을 수 있습니다. 이때 { include: Post }를 사용하여 게시물 정보를 가져올 수 있습니다.
예를 들어:
위의 코드에서 User.findAll()로 모든 사용자를 가져오면서 { include: Post }로 각 사용자의 게시물 정보를 함께 가져옵니다. 이렇게 하면 Sequelize가 내부적으로 조인(join)을 수행하여 연결된 모델의 데이터를 가져올 수 있습니다.
{ include: } 옵션을 사용하여 연결된 모델의 데이터를 함께 가져오면 데이터베이스에서 여러 테이블을 조인하여 한 번의 쿼리로 모든 데이터를 효율적으로 가져올 수 있습니다.
✔️ { where: }
{ where: } 옵션은 Sequelize에서 데이터베이스 쿼리를 수행할 때 조건을 지정하는 데 사용됩니다. 이 옵션을 사용하면 특정 조건을 만족하는 레코드만 검색하거나 업데이트할 수 있습니다.
예를 들어, 다음과 같이 where 옵션을 사용하여 사용자 테이블에서 특정 조건을 만족하는 사용자를 검색할 수 있습니다
위의 예제에서는 User 모델에서 id가 1인 사용자를 검색하고 있습니다. 이렇게 하면 데이터베이스에서 id가 1인 사용자를 검색하여 해당 사용자 객체를 반환합니다.
where 옵션은 여러 가지 비교 연산자를 사용하여 더 복잡한 조건을 지정할 수도 있습니다. 예를 들어, Op.gt, Op.lt 등의 Sequelize의 연산자를 사용하여 범위 검색을 수행할 수 있습니다.
위의 예제에서는 age가 18보다 크고 30보다 작은 사용자를 검색합니다. 이러한 방식으로 where 옵션을 사용하여 특정 조건을 만족하는 레코드를 검색할 수 있습니다.
✔️ dataValues
dataValues는 Sequelize 모델 인스턴스에서 실제로 저장된 데이터를 포함하는 객체입니다. Sequelize는 모델 인스턴스를 표현하는데, 이 인스턴스는 데이터베이스의 레코드를 나타냅니다. dataValues 속성은 이 레코드의 각 열(column)과 해당 값에 대한 키-값 쌍을 포함합니다.
예를 들어, 다음과 같이 Sequelize를 사용하여 데이터베이스에서 사용자를 검색한 후 결과로 반환된 인스턴스의 dataValues를 확인할 수 있습니다.
위의 코드에서 user.dataValues는 데이터베이스에서 검색된 사용자의 정보를 포함하는 객체입니다. 이 객체에는 사용자의 각 열에 대한 정보가 키-값 쌍으로 포함되어 있습니다.
주의할 점은 dataValues는 실제 데이터를 포함하는 객체이므로 수정된 내용은 dataValues에 반영되지만 원본 모델 인스턴스에는 반영되지 않습니다. 따라서 Sequelize에서 데이터를 조작할 때 주로 dataValues를 사용하여 데이터를 읽거나 수정합니다.
🤍