-
Node.js - ORM(sequelize)Node.js 2023. 1. 17. 14:25728x90
sequelize
sequelize는 어떤 DBMS든 상관이 없는 친구임
그렇기 때문에 sequelize는 어떤것을 연결할지 꼭 알아야 함
자 sequelize 세팅에 댛서 좀 알아보자.
server를 돌리기 위해 우리는 config.js를 만들었는데 config.js를 다시 만들어야 할 필요성이 있을 수도 있다.
config.js const config = { exception: { HttpException, }, port: process.env.PORT || 3000, db: { development:{ username:'root', password:'95211010!', database:'ingoo3', host: '127.0.0.1', port:port, dialect: 'mysql' }, text:{ username:'root', password:'95211010!', database:'ingoo3', host: '127.0.0.1', port:port, dialect: 'mysql' } } }
db라는 객체에서 구분값을 놨다고 생각하면 된다.
우리가 기본적으로 넣어야 할것은
host,port, user,password, database
가 있었다. sequelize는 하나가 추가된다.어떤 RDBMS를 사용할 것인가?
또한 알아둬야 할것은 sequelize는 user가 아니라 username을 작성한다는 사실을 기억하자.
속성명이 바뀌었다!
config.js require("dotenv").config() const HttpException = require("./exceptions/HTTPException") const host = process.env.DB_HOST || '127.0.0.1' const port = process.env.DB_PROT || 3306 const user = process.env.DB_USER || "root" const password = process.env.DB_PASSWORD || "95211010!" const database = process.env.DB_DATABASE || "Comments" const config = { exception: { HttpException, }, env: process.env.NODE_ENV || 'development', port: process.env.PORT || 3000, db: { development: { username: user, password: password, database: database, host: host, dialect: 'mysql' }, text: { username: user, password: password, database: database, host: host, dialect: 'mysql' } } } module.exports = config
저렇게 하고 나서 결과를 확인해보자(model에서!)
const config = require("../config") console.log(config)
자 이제 index로 가서 필요없는것들을 정리하고 우리는 다음과 같은것만 추출하고싶다.
db: { development: { username: 'root', password: '95211010!', database: 'Comments', host: '127.0.0.1', dialect: 'mysql' }, }
그럼 우리가 설정해놓은 env를 가져온 후 db를 가져오자.
index.js const config = require("../config") const env = config.env // development const db = config.db[env] console.log(db, env)
이렇게 되면 우리가 원하는 값만 뽑아온 것을 볼 수 있게 된다.
자 이제 우리가 사용할 ORM인 Sequelize를 가져와 보자.
그러면 여러가지 DBMS와 데이터형 (STRING은 VARCHAR임!)들이 보이게 된다.
TEXT: 필드 테이블의 속성들
하지만 제일 중요한것은 Sequelize의 데이터 타입이고, 그러면 function인것을 볼 수 있는데 좀 더 정확히 말하자면 class이다.
이 클래스 안에 여러가지 데이터가 다양하고 사용할때는 new 키워드를 붙여야 하는것을 기억해야 한다.
그럼 클래스로 만들어 보자.
const config = require("../config") const Sequelize = require("sequelize") const env = config.env // development const db = config.db[env] const sequelize = new Sequelize(db.database, db.username,db.password, db) const sequelize = new Sequelize(데이터베이스명, 유저이름, 비밀번호, db)
인스턴스화된것을 살펴보면
커넥션 풀도 등장하고 어떤 메서드를 실행하면 connection을 맺고 통신을 구현한다는 것이다.
하지만 만들 수 있는 상황을 만들었다는것
그리고 config를 보면 우리가 넣어놓은 자료를 볼 수 있고
여기서 제일 중요한 것은 model이다.
그럼 우리가 sequelize를 뭐라고 생각해야 할까?
통신하기 위한 준비! 라고 생각하면 된다
인스턴스화된 정보들은 통신과 연결되어 있고
안배운 것들인 model객체들이 들어갈 것이다.
우리가 만들었던 connection pool과 같다.
그리고 마지막으로는 클래스와 인스턴스를 각각 보내서 쓸일이 있으므로 세팅을 해두자.
자 이제 우리가 바라봐야 할것은 ORM의 목적이다.
결국 ORM은 객체를 만들어서 테이블을 만드는것이 목적이다.
그렇다면 우리는 직접 mysql말고 js코드로 테이블을 만들고 모델을 만드는것이 목적이라는 것이다.
모델은 클래스로 만들거나, 함수형으로 만드는 방법이다.
클래스: 정적 메서드를 사용해서 만듬
함수형으로 ORM 만들어보기
module.exports = (sequelize, Datatypes) =>{ }
우선 이렇게 sequelize와 Datatypes를 받는다.
다음으로 우리가 model/index.js에서 다시 받아보자.
const config = require("../config") const Sequelize = require("sequelize") const env = config.env // development const db = config.db[env] const sequelize = new Sequelize(db.database, db.username, db.password, db) const user = require('./user.model') module.exports = { Sequelize, sequelize }
이런식으로 했을떄 우리가 받는것은 함수의 값이다.(function)
따라서 우리는
user()
를 할 수 있게 된다.그럼
user
함수에는 어떤것이 들어가는가?바로
sequelize
이다. 이 소문자sequelize
에는 왜냐하면 소문자sequelize
에는 모델이 들어가 있다.우리는 모델을 생성해야 하기 떄문에 꼭 필요하다.
그 다음
DataTypes
가 있는데 이것은 무슨 역활을 하는가?우리가 DB를 만들때 데이터 타입이 존재했다. JS는 데이터 타입(VARCHAR등등)이란게 존재하지 않는다. 따라서 그것들을
JS로 구현해놓았다. 구현된 타입들을 가져오기 위해서 인자값을 가져왔다. 이것부터 require가 아니라 인자로 가져왔다.
그런데 그러한 데이터 타입은 Sequelize에 있다. 따라서 우리가 데이터타입을 요구할떄는 Sequelize가 필요한 것이다.
const user = require('./user.model') user(sequelize,Sequelize)
사용방법은 이렇고 이제 우리는 user.model을 구현해보자.
user.model.js module.exports = (sequelize, Datatypes) => { return sequelize.define() }
우리는 sequelize의 define이라는 메서드를 사용하면서 이제 만들어 내는데
이 메서드의 return 값은 바로 model을 생성한다는 것이다.
함수안에 또 하나의 함수를 사용한다는 것이다.
closure를 사용해서 했던것 require를 하지않고 매개변수로 받아서 실행하겠다.
그런데 파일을 따로 만들어서 하는게 편하다. 이런 것들이 의존성 주입이라는 것을 기억하자
자 근데 이제 테이블을 만든다는 것은 어떤 필드명에 필드명에 어떤 속성에 길이는 얼마이고 not null이 되는지 등등등
지정을 해줘야 한다.
그래서 인자값을 정의해 줘야한다.
define은 3가지 인자값을 받는다.
- 객체 속성이름
모델이라는 것은 결국 객체이다. object안에 이름을 정의하는 것이다. (그리고 나중에 테이블명도 User로 할 것) - 테이블 field정보
userid, name, gender등등등이 들어갈 수 있다.
근데 이렇게 넣으면 너무 많이 들어가기 떄문에 우리는 객체로 넣는것이 편할것이다.
- Table Option정보
글자셋, 테이블정보들 많이 알 필요는 없다.
예제를 통해 좀 더 살펴보자.
module.exports = (sequelize, Datatypes) => { return sequelize.define('Users', { userid: { type: Datatypes.STRING(30), allowNull: false, unique : true, }, userpw: {}, username: {}, gender: {}, }, { }) }
자 여기서 먼저 우리는 Datatypes를 통해서 우리가 원하는 데이터 타입들이 담겨있는 객체를 받아왔다.
따라서 그 객체 안에 있는 메서드를 사용한다.
type: Datatypes.STRING(30)
여기서 괄호안의 숫자는 바이트가 아니라 숫자인것을 기억해야 한다.다음으로
allowNull: false,
은 null을 허용을 하는지 하지 않는지를 뜻하며마지막 unique는 정말 unique값을 설정한다는 의미이다.
sequelize의 어려운 점은 우리가 sequelize의 Datatypes 관련 객체들을 알고있어야 한다는 것이다.
자 이러한 sequelize의 좋은점은 코드로 남겨져있기 때문에 github에 올라가고 그것을 통해서
서로의 table이 달라질일이 없다는 것이다.
물론 속도 향상에 저하될 수 있지만 이런것에 유용하다는 것이다.
자 다음으로는 table option이다.
{ freezeTableName:true, timestamps: }
timestamps
는 테이블이 생성될떄 create at, update at, 생성한 시간, 수정한 시간을 적어주는것을 막겠다.freezeTableName
: 테이블명을 객체 속성 이름 그대로 사용하겠다! 라고 쓰는것객체속성명을 유저로 할건데 user라는 속성명을 그대로 테이블명으로 하겠다.
그리고 글자셋도 table option에서 설정이 가능하다.
그러면 모델을 만드는 함수를 정의를 했다.
아직 우리는 실행하지는 않았다.
이제 호출을 해야지 실행이 가능하다는 것을 기억하자.
const user = require('./user.model') user(sequelize, Sequelize)
현재 이렇게 되어있는데 하나로 합칠 수 있다.
const user =require('./user.model')(sequelize,Sequelize) console.log(use)
여기서는 console.log만 칠 경우는 user라는 객체만 보여주며,
console.dir를 쓸 경우에는 모든 객체를 보여준다.
그러면 우리가 썻던 내용들을 다 보여준다.
define을 시켰더니 return 값이 결국 객체이다.
유저라는 것을 찍어봤는데 이것은 굉장히 중요한 것이다.
define 메서드를 사용하면 객체를 만들어줌과 동시에 소문자 sequelize의 models안에 넣어준다는 것을 기억하자.
자 다음으로 이제 실제 서버에서 넣어보는 것을 해보자.
어떻게 진행되냐면
const { sequelize } = require("./models") app.listen(config.port, async () => { await sequelize.sync({ force: true }) console.log(`백 서버 오픈 ${config.port}`) })
이런식으로 3-way-handshake가 진행됐을떄 우리가 pool을 통해서 받은 것들이 실제로 진행되도록 하는 것이다.
맨 처음에 sync는 model객체를 가지고 db에 쿼리문을 때려준다고 생각하자.
나 모델이 있는데 DB에 실제 테이블이 있니,
true
가 있으면 있더라도 지우고,false
면 아무것도 안한다.한번 true로 놓은 다음에 false로 놓자. 덮어 씌울것인지 아닌지
그리고 sync는 promise객체이기 때문에 await으로 받았다.
제가 왜 listen 안에 넣었는가를 보자
어차피
sequelize.sync({ force: true })
는 비동기 코드이다.그리고
app.listen
도 비동기 코드이다. server가 열리고 그 다음에 db에 접속을 하겠다 라는 의미이다.만약 이 코드가 위에 있다면은 server.js를 시작하려면 일단 누가 먼저 시작할지를 모른다고 생각하는 것이 좋다.
언제는 서버가 먼저 켜지고, 언제는 db접속이 먼저 되고, 우리가 생각하는 것과 다른 일이 벌어질 수도 있다는 것을 기억해야 한다.
우리는 순서를 항상 일정하게 해야 오류가 덜 날 수 있다.
서버가 항상 켜지고 나서 실행한다는 것 그리고 console도 그때 찍는다는 것
사용방법
지금까지는 ORM을 Sequelize로 사용한것
이제까지 한 것은 model을 생성해서(객체만들어서)
Table
생성자 근데 DB를 쓴다는 것은 create, insert, update까지는 진행을 해봐야 한다.
자 일단은 user라는 폴더를 하나 만들어 보자.
comment를 참고해서 post를 한번 만들어보자.
자 repository부터 작업할 것이다.
user.repository class userRepository { constructor({ User }) { this.user = User } async addUser(payload) { try { return null } catch (e) { throw new Error(e) } } } module.exports = userRepository
일단은 return 값을
null
로 두고 작업할 것이다.다음은 service 이다.
user.service class userRepository { constructor({ User }) { this.user = User } async addUser(payload) { try { return null } catch (e) { throw new Error(e) } } } module.exports = userRepository
user.Controller class userController { constructor({ userService }) { this.userService = userService } async postSignup(req, res, next) { try { } catch (e) { } } } module.exports = userController
자 이제 마지막으로 module 부분을 만드는데 module 부분은 의존성 주입을 처리하는 곳이다.
이때는 정말로 repository부터 차곡차곡 쌓아 나가야 한다.
자 repository는 의존성 주입을 해줘야 하는데 모델을 넣어주기로 했다.
user 또는 sequelize.models.User를 보낼 수도 있다.
우리는 sequelize를 보내고 있으므로 그것을 통해서 처리해보자.
const { sequelize: { models: { User }, }, } = require('../models') console.dir(User)
마지막것만 찍힌다는 사실을 기억할것
모델의 user를 가져온 이유는 repository에 주입하기 위해서이다.
const { sequelize: { models: { User }, }, } = require('../models') const userRepository = require("./user.repository") const userrepository = new userRepository(User)
그러면 이렇게 짜면 될까?
안된다 왜?
우리가 구조분해할당문으로 처리해서 객체로 넣었기 때문에 의존성 주입을 할때도 객체로 넣어줘야 한다.
const { sequelize: { models: { User }, }, } = require('../models') const userRepository = require("./user.repository") const repository = new userRepository({ User }) console.log(repository)
자 그렇다면 이제 우리는 원래는 repository에서 sql 구문을 사용했었다.
하지만 이제는 좀 더 쉬워진다.
class userRepository { constructor({ User }) { this.User = User } async addUser(payload) { try { const suer = await this.User.create() return null } catch (e) { throw new Error(e) } } } module.exports = userRepository
우리가 insert를 하고싶다면 create 메서드를 사용하면 된다.
User 모델에 create() 메서드가 있는데 insert문을 사용하겠다는 뜻이ㅏㄷ.
insert문을 실행시키고 나온값을 반환받겠다는 뜻이다.
그 다음에는 어떠한 필드에 어떠한 값을 넣을지만 알면 된다.
const user = await this.User.create({ userid: "asdf", userpw: "asdf", username: "asdf" }) return user
먼저 강제로 넣어준다.
그러고 module에서 되는지 살펴본다.
const { sequelize: { models: { User }, }, } = require('../models') const userRepository = require("./user.repository") const repository = new userRepository({ User }) repository.addUser({})
이런식으로 한번 실행을 시켜보면 실제로 쿼리문이 작동하고 들어가는 것을 볼 수 있다.
다음은 findOne을 해보자. 하나만 찾는것이다 모든것을 찾는것은 findAll이다.
sequelize는 where조건을 거는게 조금 귀찮다. 인자값에 {}를받고
일단 findOne을 했을떄는
select * from user
까지 완성된다는 것을 기억하자.where이 추가되는데 where은 값을 넣어주는데 이것들을 전부 객체로 표현하고 속성명은 where이다.
자 근데 여기서 이제 다음과 같이 만들고 작동을 시켰다고 해보자.
async getUserByUserId({ userid }) { const user = await this.User.findOne({ where: { userid: userid, }, }) return user } repository.getUserByUserId({ userid: "asdfasdf" }).then((data) => { console.log(data) })
여기서 이제 중요한 객체는
User { dataValues: { id: 3, userid: 'asdfasdf', userpw: 'czxc', username: 'cxcz', gender: '여자' }, _previousDataValues: { id: 3, userid: 'asdfasdf', userpw: 'czxc', username: 'cxcz', gender: '여자' }, uniqno: 1, _changed: Set(0) {}, _options: { isNewRecord: false, _schema: null, _schemaDelimiter: '', raw: true, attributes: [ 'id', 'userid', 'userpw', 'username', 'gender' ] }, isNewRecord: false }
유저 객체를 통으로 주고 있기 떄문에 빼려면
class userRepository { constructor({ User }) { this.User = User } N async addUser(payload) { try { const user = await this.User.create(payload) return user } catch (e) { throw new Error(e) } } async getUserByUserId({ userid }) { const user = await this.User.findOne({ where: { userid: userid, }, }) return user.dataValues } } module.exports = userRepository
return 값을 바꿔주면 된다
'Node.js' 카테고리의 다른 글
Node.js - Buffer (0) 2023.01.17 Node.js - JWT(Jason Web Token) (0) 2023.01.17 Node.js - ORM (0) 2023.01.17 Node.js - dotenv (0) 2023.01.17 Node.js - API (0) 2023.01.05 - 객체 속성이름