-
#Sequelize 2탄: 더 깊이 들어가는 Sequelize 사용 방법TIL (Today I Learned) 2023. 12. 14. 23:52
# Sequelize 1편 이론과 연결 포스팅 이후
이전 포스팅에서는 Sequelize를 사용하는 기초적인 방법을 다뤘습니다. 이번에는 Sequelize를 더 깊이 있게 사용하는 방법을 살펴보겠습니다. 튜터님의 해설 영상을 참고하여 실무에서 바로 활용할 수 있는 내용을 중점적으로 다룰 예정이며, 리마인드 차원에서 다시 공부하고 기억하고자 작성하게 되었습니다.
밑에 첨부한 포스팅 내용은 Sequelize 이론부터 연결에 대한 간략한 내용입니다. 기본적인 내용은 아래의 포스팅을 통해 참고해보시면 좋을 것 같습니다.
※ Sequelize 사용하기(기본 세팅 파일은 생략)
# `sequelize`와 관련된 라이브러리 설치
**VSCode Terminal에서 아래의 명령어를 입력하여 sequelize와 mysql2를 설치하고 package.json을 확인합니다.
npm i sequelize mysql2
**설치가 완료되면 개발할때만 사용하는 `sequelize-cli` 라이브러리를 아래의 명령어를 통해 추가적으로 설치해줍니다.
npm i -D sequelize-cli
`sequelize-cli`은 ` sequelize init`과 같은 명령어를 실행하기 위해서 필수로 설치해야합니다.
** 모든 설치가 정상적으로 완료되었는지 package.json 파일을 통해 확인합니다.
** 그 다음 시퀄라이즈를 사용할 수 있도록 init을 진행합니다.
npx sequelize init
** 그럼 이전의 프로젝트 파일에서 4개의 파일이 자동으로 추가로 생성된 것을 확인할 수 있습니다.
[config, migrations, models, seeders]
* 파일 자동 추가전
* 파일 자동 추가후
** 위 사진처럼 사용해야 할 파일이 자동으로 추가됩니다.
# config 파일 수정 및 환경변수 설정
** 먼저 config 파일에 들어가는 계정정보는 환경변수로 관리해줘야합니다. 파일 자체를 GitHub에서 관리하기 때문에 환경변수로 관리하지 않는다면 계정정보가 그대로 노출되기 때문입니다.
그래서 .env 파일을 통해서 관리를 해줘야하는데 문제는 config.json -> .json 파일 형태의 확장자가 .env 파일을 불러올 수 없습니다. .env파일을 불러내서 사용하기 위해 파일 확장자를 js 파일확장자로 변경을 해서 사용해야 합니다. 저는 es6 module을 사용했지만 commonjs확장자 명인 .cjs로 변경해주었습니다. 이렇게 확장자명을 .cjs로 설정해주면 commonjs 스타일로 작성하겠다고 선언하는 것과 같습니다.
"그럼 es6 module를 사용하는데 ommonjs확장자 명인 .cjs로 변경하였을까요?"
sequelize 라이브러리 같은 경우에 es6 module시스템에 친화적인 라이브러리가 아니기 때문에 init 명령을 통해 만들어주는 파일의 경우 전부다 commonjs 스타일로 작성이 되어있습니다. es6 module 스타일로 바꾼다면 바꿀 수 있겠지만 변경하면서 발생할 수 있는 "사이드 이팩트(side effect)"가 어디까지 영향을 미칠지 알 수 없기 때문에 기존에 제공하는 commonjs 스타일로 사용하는 것이 안전한 방법입니다.
"[config, migrations, models, seeders] 파일 4가지는 기존의 스타일을 따라 작성하는 것이 좋습니다."
그럼 다시 config 파일로 돌아와서 dotenv 파일을 사용하기 위해 require('dotenv').config();을 입력하여 dotenv 파일에서 환경변수들을 사용합니다.
전부 환경 변수에서 가져와서 사용할 예정이기 때문에 아래와 같이 수정합니다.
development 부분만 사용할 예정이기 때문에 기존에 작성되어 있던 코드는 모두 지워주고, development를 export 하기 위해서 module.exports = {development}를 같이 작성합니다.
여기까지 작업한 것을 정리하면 json 파일을 .cjs로 변경하고 환경변수를 사용할 수 있도록 지정했습니다.
그 다음 models > index.js 파일이 지금 수정한 config.cjs파일을 불러내서 사용하기 때문에 같이 수정해줘야합니다.
# models > index.js 파일 수정
** models > index.js 파일을 변경하기전 간단히 짚고 넘어가자면 sequelize라고 하는 라이브러리를 사용해서 models 폴더에 작성된 것을 가져와 사용할 수 있게 해주는 역할을 수행합니다.
sequelize라는 객체를
config라는 파일에서 내용을 읽어와서
config, database, username, password를 이용해 새로운 객체를 생성합니다.
그래서 sequelize 변수에 데이터베이스에 접속할 수 있는 정보가 담겨있습니다.
+++
추가적으로 _dirname는 directory의 name이라고 하면 directory 의 주소인 현재 위치 models를 나타냅니다. models의 파일이 생성되면 model이라는 변수에 model을 사용할 수 있도록 초기화를 시켜줍니다.
db 객체에 해당 model 이름에 해당하는 property로 해당 model을 넘겨줌과 동시에 추가적으로 model과 model, table과 model 관계 설정도 자동으로 해결해줍니다.
그래서 우리가 마지막에 설정했던 db라는 곳에 저장을 했다가 db라는 객체를 export해서 언제든지 model 정보 혹은 database 접근 정보를 사용할 수 있게 해주는 파일이 models의 index.js 파일입니다.
그럼 models에 추가되는 파일들도 전부 확장자를 .cjs로 변경하겠습니다.
밑줄 친 부분인 file.slice(-4) === '.cjs' && 부분도 -3 => -4 변경과 확장자명을 .cjs로 같이 변경했습니다. 추가적인 설명을 덧붙이자면 여기서 나타내는 -4는 .cjs 4자리만 가져와서 가져온 정보가 cjs인 경우에만 사용하겠다고 선언하는 것입니다. 다음으로 환경 변수 설정 후 제대로 접근이 가능한지 테스트를 진행해보겠습니다.
# 환경변수 설정 후 RDS 접근 확인
** Sequelize 공식 문서
현재 작업중인 파일의 package.json에서 sequelize 버전을 확인 한 후 위의 sequelize 공식 사이트에 접속합니다.
저는 현재 6.35.1 버전이기 때문에 6버전으로 진행하겠습니다. (보통 6버전을 많이 사용합니다.)
공식문서에 접속 후 메뉴에서 v6 - stable을 선택합니다. (자신의 버전과 꼭 맞는 버전을 찾아서 진행해야합니다.)
v6 페이지로 이동하고나면 왼쪽에 Getting Started를 선택하여 한번 더 이동합니다. Getting Started를 보게되면 테스팅 방법이 나와있습니다.
그럼 try로 시작하는 저 코드를 복사하여 app.js에 붙여넣고 실행해줍니다. (v6면 아래의 코드를 사용하시면 됩니다.)
try { await sequelize.authenticate(); console.log('Connection has been established successfully.'); } catch (error) { console.error('Unable to connect to the database:', error); }
우선 밑줄 친 "Hello World" 부분을 지워줍니다.
그리고 복사해온 try 코드를 붙여 넣어준 후 한가지 더 설정을 해줍니다. (바로 실행하는 것이 아닙니다.)
models > index.js 파일에 있는 밑줄 친 부분에 접속 정보를 저장하기 때문에 db.sequelize를 이용해야합니다.
import db from './models/index.cjs 을 통해 app.js에서 db를 가져오고 db를 붙여서 db.sequelize.authenticate(); 제대로 불러오는지 확인을 해야합니다. 성공시에는 'Connection has been established successfully.' 출력하고 실패 시에는 'Unable to connect to the database:' 출력합니다. 터미널에서 npm run dev로 바로 실행하여 확인해봅니다.
실제로 실행해보면 데이터가 베이스가 정의되어 있지 않다고 오류가 발생합니다. 이유인 즉슨 config만 정의해놓고
실제로 config를 통해 데이터 베이스를 생성하는 db:create 코드를 실행하지 않았기 때문입니다. 이를 실행하기 위해서는 아래의 명령어를 입력해야하는데 파일 명을 .cjs로 변경하였기 때문에 명령어 뒤에 --config로 옵션을 수동으로 정해주고 config/config.cjs 경로를 지정해줘야합니다.
npx sequelize db:create --config config/config.cjs
위의 명령어를 실행하고 나면 데이터 베이스가 정상적으로 생성된 것을 확인 할 수 있습니다.
그럼 다시 터미널에서 npm run dev를 실행하여 실제로 코드상으로 돌려보고 성공했을 때 출력되는 'Connection has been established successfully.' 메시지를 확인해보겠습니다.
위의 사진을 보면 정상적으로 성공했을 때 출력되는 메시지를 확인할 수 있습니다. 여기까지가 node.js sequelize를 이용해서 aws rds에 정상적으로 접근했는지 확인하는 과정이었습니다. 이후엔 실제로 데이터 베이스가 생성되었는지 확인을 해야합니다.
# 데이터 베이스 생성 확인 ( MySQL Workbench 사용)
MySQL Workbench에 접속하여 새로 만들기를 진행합니다.
(RDS에서 설정한 값을 적어주세요)
Hostname: RDS 엔드 포인트Port: 3306Username: root 혹은 admin
Password: ************* 입력 후 Tset Connection을 클릭하여 successful을 확인합니다.
다음 OK 클릭
한번 더 OK 클릭
새로 생성된 연결 데이터 베이스 클릭
들어와서 express-database2가 있는 것을 확인 할 수 있었습니다. Table에 아무것도 없는건 테이블을 정의한 것이 없기 때문에 빈 공간으로 나타납니다.
여기까지 데이터 베이스를 생성하였고 생성한 데이터 베이스에 연결되는 것까지 확인을 하였습니다. 다음 단계로 직접 사용해야 할 table을 정의해야하는데 sequelize에서 제공하는 model:generate 명령어를 이용하여 models 폴더 안에 model 파일과 migrations 폴더 안에 있는 migrations을 파일을 생성해보겠습니다. 사전에 정의했던 erd model을 바탕으로 생성을 하면되며 저는 생성한 erd model이 있어서 그대로 사용하겠습니다.
# table 정의를 위한 migrations와 models 사용 방법
model을 생성하기 위한 명령어
npx sequelize model:generate --name Users --attributes email:String,password:String,name:String
여기서 --name Users 부분은 model과 table 이름에 적용이됩니다. 그리고 --attributes 우리가 정의해 놓은 field 혹은 column 이름을 생성할 때 사용합니다. model을 생성할 때 기본적으로 id, createAt, updatedAt은 자동으로 생성을 해주기 때문에 제외하고 작성해주시면 됩니다. 그럼 위의 명령어를 실행해보겠습니다.
정상적으로 create되었습니다. 파일쪽을 보면 migration 폴더엔 create-users.js라는 migrations 파일이 생성되었고, models 폴더엔 users.js 파일이 동시에 생성된 것을 확인 할 수 있습니다.
migrations 파일을 먼저 확인해보면 명령어에서 제외하였던 id와 createAt, updatedAt은 자동으로 추가가 된 것을 확인할 수 있습니다.
models 폴더 users.js 파일도 확인해보면 users init해서 아까 입력했었던 필드들이 추가되는 확인할 수 있습니다.
여기에서도 마찬가지로 두 파일 모두 commonjs 형태의 module로 작성되었기 때문에 migrations와 models 전부 .cjs로 확장자 명을 번거롭더라도 변경해줘야합니다.
만약 변경해주지 않는다면 module 시스템이 맞지 않는다는 에러가 발생하기 때문에 es6 module이더라도 눈물을 머금고 seqeulize에서 자동으로 생성해주는 파일들은 전부 .cjs로 확장자 명을 변경해줘야 나중에 문제가 없습니다.
지금까지 User model을 생성했고 다음으로 Products 모델을 생성해보겠습니다. erd를 확인하고 id, createdAt, updatedAt은 자동으로 생성되니 제외하고 진행해주시면 됩니다. 저는 아래의 명령어를 입력하여 진행했습니다.
npx sequelize model:generate --name Products --attributes userId:Integer,title:String,description:String,status:String
이번에도 정상적으로 create 되었습니다.
models > products.js 안에도 Products init해서 아까 입력했던 필드들이 추가되는 확인할 수 있습니다.
이전과 동일하게 migrations 폴더에 create-products.js migrations 파일과 models 폴더에 products.js 파일이 생성된 것을 확인할 수 있습니다. Products로 생성된 파일도 확장자 명을 .cjs로 변경해야합니다.
이렇게 진행하고 나면 이후에 작성하는 코드는 module 시스템과 상관없이 그대로 사용할 수 있습니다. 이제 model 정리를 마치고 다음으로 model을 가지고 query를 날려야할 때 어떻게 날려야하고, 어떻게 model을 가져와야하는지 알아보겠습니다.
# model을 가지고 query를 조작하는 방법
일단 app.js로 돌아와 try catch부분이 더이상 필요가 없기 때문에 지워줍니다.
다음으로 잠시 index.js로 넘어갑니다. 여기에서 .cjs 확장자로 적힌 모든 파일을 읽어서
여기서 model을 만들어 줍니다.
model 인스턴스를 만들어주면 [model.name] 해서 .cjs 확장자 파일안에 정의되어 있는 model name들을
products.cjs에선 return Products;를
users.cjs 에선 Users;를
db 안에 모델 이름에 해당하는 property로 생성된 model 인스턴스를 전부 집어넣어줍니다.
"그럼 이걸 가져다 사용하기 위해 어떻게 해야할까요?"
바로 구조분해 할당을 통해 가져다 사용할 수 있습니다. app.js로 다시 돌아와서 const { Products, Users } = db; 이렇게 사용하면 됩니다. db 안에는 Products, Users가 들어있습니다.
const { Products, Users } = db;
실제로 들어있는지 확인하는 방법으로 아래의 방법을 통해 확인합니다.
const products = await Products.findAll(); const users = await Users.findAll(); console.log({ products, users });
실제로 node app.js를 실행하여 확인해보겠습니다.
그럼 위 사진의 에러가 발생하는데 table을 만들 수 있는 코드는 작성이 되었는데 아직 table 을 만들어 주지 않았기 때문에 발생하는 현상입니다. table을 만든 후에 진행을 하면 migration을 진행해야합니다.
npx sequelize db:migrate 명령을 실행하게되면
npx sequelize db:migrate
migrations 폴더 안에 정의되어있는 create-users.cjs 파일과 products.cjs 파일을 하나씩 읽어가면서
파일안에 정의 되어있는 up 함수를 실행하게 됩니다.
create-users.cjs에선 createTable을 통해 'Users'라는 table을 생성하고 create-products.cjs 에선 createTable을 통해 'Products'라는 table을 생성하게 됩니다.
실행을 하기전 npx sequelize db:migrate에 --config config/config.cjs 옵션을 추가해줘야 정상적으로 작동합니다.
npx sequelize db:migrate --config config/config.cjs
실행하면 파일 이름들이 나오면서 migration이 되었다고 나오는데 실제로 migration이 되었는지 워크벤치를 통해서 한번 더 확인을 해야합니다.
MySQL Workbench로 이동합니다. 기존에는 아무것도 없었는데 새로고침을 통해 데이터를 최신화해줍니다.
최신화해주자 products 테이블과 Users라는 테이블이 생성된 것을 확인할 수 있습니다.
그러면 app.js로 돌아와서 생성된 table이 실제적으로 제대로 된 데이터를 가져올 수 있는지 실행을 해보겠습니다.
node.js를 실행해서 여기에서 모든 데이터를 가져와서 출력을 하겠습니다. 그럼 products에 아무 것도 없고 users에도 아무것도 없는 것으로 확인됩니다.
조금 더 명확하게 알아보기 위해서 실제로 데이터 하나씩을 넣어보고 다시 실행해보겠습니다.
await Users.create({ email: 'test', password: 'test', name: 'test' }); await Products.create({ userId: 1, title: 'testProduct', description: 'testProduct_ing', status: 'SOLD_OUT', });
위의 코드 추가 후 실행
반환을 하긴 했는데 이상하게 나오고 있습니다.
일단 크리에이트 했던 부분은 주석처리하고 다시 진행을 해봅니다.
findAll()이 return을 할때 데이터만 return 해주는 것이니라 인스턴스 형태로 제공해서 return을 합니다. 그래서 아래 친구들
const products = await Products.findAll(); const users = await Users.findAll();
이 친구들이 가지고 있는 method들이 있는데 실행할 수 있는 method까지 전부 출력이 되기 때문에 위와 같은 현상이 발생합니다. 그래서 만약 데이터만 보고 싶다면 .toJSON()을 통해 속에 있는 알맹이만 확인할 수 있게 합니다.
//console.log를 수정해줍니다. console.log({ products: products.toJSON(), users: users.toJSON() });
이렇게 수정한 후 바로 실행을 하면 아래와 같은 현상이 일어납니다.
TypeError: products.toJSON is not a function 오류가 뜨면 map()을 사용해야합니다. 이유인 즉슨 객체를 반환하는게 아니라 배열을 반환한 것이기 때문에 아래와 같이 수정해야합니다.
console.log({ products: products.map((products) => products.toJSON()), users: users.map((users) => users.toJSON()), });
위와 같이 다시 수정해주고 node app.js를 실행시켜줍니다.
아까 입력했던 test 정보들이 쭉 정상적으로 출력되는 것을 확인할 수 있습니다.
여기까지 모델과 마이그레이션을 활용하여 실제로 데이터베이스에 table을 생성하고 해당 table 에서 데이터를 검색하는 것을 확인했습니다. 이제 우리는 이것을 실제로 사용하기 위해 제약조건을 설정해야 합니다. 제약조건은 데이터베이스에서 데이터의 무결성을 유지하고 보호하기 위한 규칙이며, 이를 통해 데이터베이스의 일관성을 유지할 수 있습니다. 그럼 제약조건을 어떻게 걸어줄 것인지 자세하게 알아보도록 하겠습니다.
# 제약조건 migrations 파일에 적용하기
사전에 ERD를 작성하면서 정의했던 제약조건을 마이그레이션스 파일에 적용해보겠습니다. 먼저 ERD와 비교하기 위해 작성해놨던 ERD에 테이블 항목을 확인합니다.
먼저 Users쪽을 살펴보면서 ERD와 비교하면서 테이블에 있는 항목이 있는지 확인합니다.
[ id, email, password, name, createdAt, updatedAt] 필드가 다 정의되어 있는지 확인한 이후에 세부적인 조건들도 적용된거지 확인해야합니다.
id안에 항목을 추가적으로 설명하면 autoIncrement를 true로 설정하면 자동으로 1씩 증가합니다. primaryKey를 true는 Users 아이디를 식별합니다.
[ email, password, name]에 null이 지금 허용된 상태인데 null 값을 허용하지 않기 위해 allowNull:false,를 추가해줍니다.
allowNull:false,
그리고 [ createdAt, updatedAt ]의 default 값을 지정해야합니다. 현재 시간을 default 값으로 사용하려면defaultValue: Sequelize.fn('now') 를 입력해줘야 마이그레이션 코드가 동작할 때 현재 시간을 입력할 수 있도록 default 값을 설정해줍니다. 아래와 같이 작성하면 완료입니다.
defaultValue: Sequelize.fn('now'),
여기서 [ createdAt, updatedAt ] 항목이 있으면 데이터 베이스에 새로운 데이터가 추가될 때마다 자동으로 인식해서 현재 시간을 입력해주고 [updatedAt ]은 수정이 일어나면 수정된 시간을 추가적으로 수정된 시간을 자동으로 기록합니다.
위와 같이 설정하고나면 ERD에서 지정한 것과 동일하게 됩니다. 다음으로 models의 model에 있는 코드를 확인합니다.
[ email: DataTypes.STRING,]
[ password: DataTypes.STRING,]
[ name: DataTypes.STRING,]models 코드는 이렇게 세가지만 있는데 migrations 코드와 동일하게 맞춰줘야합니다.
*** 여기서 migrations 코드와 models 코드의 차이점을 알아보고 넘어가겠습니다.
migrations 코드는 sequelize migrate:up을 하게되면 실행되는 명령어를 지정한 것이고 주로 sequelize migration을 업데이트 할 때 실행되는 명령어를 지정한 것입니다. 이 명령어는 실행되면 실제로 MySQL 데이터 베이스에 해당 명령어를 실행하여 table을 생성합니다. 즉, migration 코드 설정은 table 생성을 위해 정의된다고 보시면 됩니다.
반면에 model 코드는 주로 API 코드를 구현할 때 사용됩니다. 데이터베이스와 통신하면서 findAll이나 create와 같은 메서드를 이용하여 데이터베이스와 상호작용하는 객체를 정의합니다. 여기서 정의된 옵션들은 데이터베이스에 저장되기 전에 validation(주어진 데이터가 특정 조건이나 규칙을 충족하는지 검증하는 과정) 에 사용되는 옵션입니다.
마이그레이션 코드에서는 데이터베이스 자체에서 걸러지는 옵션이 적용되고, 모델 코드에서는 ORM을 이용할 때 걸러지는 옵션이 적용됩니다. 그러므로 두 코드 모두 동일한 옵션으로 지정해야 합니다.
그래서 migrations 코드 작성 부분을 복사해서
models init 쪽에 붙여넣어 주고 확인해 봤을 때 type: Sequelize. 부분에 흰색 ...이 찍혀있습니다.
migration 코드 같은 경우엔 sequelize로 type을 가져왔지만 models에서는 sequelize에서 넘겨받는 것이 아닌 DataTypes에서 type 넘겨받아서 설정하기 때문에 수정을 해줘야합니다.
그리고 기존의 코드 [eamil, password, name] 선택 후 삭제해줍니다.
그럼 이런 형태로 작성이 완료되면
아래와 같이 [createdAt, updatedAt] 을 추가로 설정해줘야 마무리 됩니다. defaultValue: DataTypes.fn('now'),를 defaultValue: DataTypes.NOW, 로 설정해야 사용이 가능합니다. 이렇게 지정을 하면 기존의 migration 코드와 동일하게 적용한 것을 확인 할 수 있습니다. 이어서 Products 쪽도 수정을 진행하겠습니다.
defaultValue: DataTypes.NOW,
Products 쪽도 ERD와 나란히 두고 비교를 해봤고, Users 쪽과 마찬가치로 Null을 허용치 않게 하기 위해 [ userId, title, description, status]에 allowNull: false,를 추가해줍니다.
allowNull:false,
그리고 [createdAt, updatedAt]은 defaultValue: Sequelize.fn('now'), 값을 지정해줘야합니다.
defaultValue: Sequelize.fn('now'),
아래 사진과 같이 변경이 완료되었다면 복사를 해서 models > Products 쪽으로 넘어갑니다.
models > Products로 넘어오면 [id, createdAt, updatedAt] 부분이 추가되어 있지 않는 것을 확인할 수 있습니다.
추가가 안된 부분이 있기 때문에 복사 붙여넣기를 진행하고 migrations 코드와 동일하게 맞춰줍니다.
붙여넣기를 한 후 이상태에서 DataTypes에서 type 넘겨받아서 설정하기 때문에 이번에도 DataTypes를 이용해 type: Sequelize. 부분을 교체해줘야합니다.
type: Sequelize. 부분 교체를 진행합니다.
그리고 밑에 있는 기존 코드는 삭제합니다.
그 다음 defaultValue: DataTypes.fn('now'),를 defaultValue: DataTypes.NOW, 로 설정해야 사용이 가능합니다.
defaultValue: DataTypes.NOW,
이렇게 하면 기본적인 형태는 갖추었고 migrations 코드와 models 코드 동기화까지 완료했습니다.
그리고 userId 부분에 fireignKey 부분을 넘어간 부분인 있는데 fireignKey라고 해서 여기 있는 userId는 userTable에 있는 id를 참고하는 거라고 설정이 되어있습니다. 그래서 이 부분에 대해서 정의를 해줘야합니다.
references 코드를 추가해줍니다.
references: { model: 'Users', key: 'id', },
아래와 같이 users 모델에 key로 id를 참고해서 사용하겠다고 설정해줘야합니다.
migrations에서는 이렇게 추가를 하고나면 끝난 것이 아니라 models쪽에도 역시 추가를 해줘야합니다. models 쪽에서는 associate 부분에서 설정이 가능합니다. 아래 밑줄 친 부분이 설정할 수 있는 부분입니다.
this. belongsTo(models.Users, {as:'user'} ); 이렇게 설정을 해야하는데 this는 class Products 내부에서 자신을 가리킵니다. belongsTo() 메소드를 사용하여 어디에 속하는지를 지정할 수 있는데, 이 경우 models를 통해 우리가 정의한 Users 모델에 속한다고 지정합니다. 즉, Users 모델이 Products 모델을 포함한다는 관계를 정의합니다. 쿼리를 수행할 때, 나중에 데이터를 가져올 때 user 항목으로 지정하겠다고 추가로 명시합니다. 이렇게 하면 이제 쿼리를 실행할 때 user 항목으로 데이터를 가져오도록 지정할 수 있습니다. (this.belongsTo() 나중에 검색해보기)
this. belongsTo(models.Users, {as:'user'} );
유저가 상품을 포함하고 있기 때문에, 반대로 유저 측에서도 관계를 설정해주어야 합니다. 마이그레이션 코드에는 특별한 지정이 필요하지 않지만, 모델 코드로 이동하여 associate 메소드를 사용합니다.
이전에는 상품이 어디에 속해있는지를 지정했었는데, 여기에서는 hasMany를 사용합니다. 유저는 여러 상품을 생성할 수 있으므로, 상품과의 관계를 "하나의 유저가 많은 상품을 가질 수 있다"로 정의합니다.
this.hasMany(models.Products, { foreignKey: 'userId', as: 'Products' });
여기서 foreignKey 개념이 등장합니다. 이전에는 상품에서 나오는 userId를 foreignKey로 사용하기로 결정했습니다. 따라서 userId를 foreignKey로 지정하고, 결과적으로 상품을 Products라는 이름으로 쿼리할 때 결과값을 가져오도록 지정합니다. 이렇게 설정을 마치면 모델 정의가 완료되었습니다.
이 상태에서 바로 사용할 수 있는 것은 아니고 migration을 실행을 해야 변경된 내용을 table에 반영할 수 있습니다.
# 변경된 내용 migrations 실행을 통해 table에 반영하기
그럼 migration을 실행을 해야 변경된 내용을 table에 반영해보겠습니다.
*** migrate:up
npx sequelize db:migrate --config config/config.cjs
*** migrate:down
npx sequelize db:migrate:undo --config config/config.cjs
위의 명령어를 통해 사용가능하지만 자주 사용해야하기 때문에 package.json으로 이동하여 script에 추가를 하겠습니다.
간편한 명령어로 올렸다가 내릴 수 있게 실행 시키는 코드를 script에 설정해줬습니다.
"migrate:up": "npx sequelize db:migrate --config config/config.cjs", "migrate:down": "npx sequelize db:migrate:undo --config config/config.cjs",
그럼 설정한 대로 npm run migrate:up 실행을 하면 작동이 되는데 실제로 터미널에서 실행해보겠습니다.
npm run migrate:up
npm run migrate:up 실행을 하면 마이그레이션 실행을 할게 없다고 메시지가 뜹니다.
No migrations were executed, database schema was already up to date.
이럴 땐 Workbench 툴을 사용해서 migration이 어떻게 관리되는지 짚어보고 넘어가겠습니다. 우선 Workbench를 다시 실행합니다.
Workbench 툴을 이용해서 데이터 베이스에 접속하게되면 우리가 만들었던 products와 users table 이외에 못보던 sequelizeMeta라는 table이 하나 더 있습니다. 여기로 한번 들어가서 데이터를 확인해보겠습니다.
sequelizeMeta table에 들어가서 데이터를 확인해보면 여기에 아까 정의했던 migration 파일 이름이 나와있습니다.
이 기록은 이미 실행된 migration파일이 있기 때문에 "npm run migrate:up"을 실행할 때 해당 파일을 실행하지 말아야 한다는 내용을 기록한 것입니다. migration은 주로 개발 단계가 아닌 프로덕션 환경에서 사용되는 것이 일반적입니다. 초기 정의는 유지되어야 하며 변경 사항은 주의 깊게 관리되어야 하며 데이터베이스 변경을 실행할 적절한 시점을 고려해야 합니다.
원래는 제약 조건을 따로 걸지 않고 migration파일을 만들었습니다. 이 경우 수정이 필요한 경우 초기에 생성된 마이그레이션 파일을 그대로 두고 부족한 부분을 보완하기 위해 추가적인 마이그레이션 파일을 만드는 것이 좋습니다. 그러나 지금 포스팅에서는 연습을 위해 우리는 기존 migration파일을 직접 수정하고, 초기에 수행한 마이그레이션을 되돌리고 다시 실행하는 방식으로 진행할 예정입니다.
아까 script에 정의했던 "migrate:down": "npx sequelize db:migrate:undo --config config/config.cjs" 명령어는db:migrate:undo 명령을 이용하면 가장 최근에 실행했던 마이그레이션 작업을 되돌리는 작업입니다
migration 파일에 정의된 것을 보면업(Up) 함수:
- 업 함수는 마이그레이션을 실행할 때 동작합니다.
- 일반적으로 테이블을 생성하거나 스키마를 변경하는 등의 데이터베이스 작업을 수행합니다.
- 즉, 새로운 스키마를 데이터베이스에 적용하거나 업데이트하는 역할을 합니다.
async up(queryInterface, Sequelize) { // up과
다운(Down) 함수:
- 다운 함수는 마이그레이션 롤백 시 실행됩니다.
- 주로 마이그레이션을 이전 상태로 되돌리는 작업을 수행합니다. 테이블을 삭제하거나 스키마를 이전 상태로 되돌리는 등의 작업이 포함될 수 있습니다.
async down(queryInterface, Sequelize) { // down으로 정의되어있습니다.
업(Up) 함수 다운(Down) 함수 예시를 통한 내용 정리
module.exports = { up: (queryInterface, Sequelize) => { // 테이블 생성 또는 스키마 변경 작업 수행 return queryInterface.createTable('테이블명', { // 테이블 컬럼 정의 등... }); }, down: (queryInterface, Sequelize) => { // 다운 함수는 마이그레이션 롤백 시 실행됩니다. // 여기에서는 테이블을 삭제하거나 이전 상태로 복원하는 등의 작업을 수행합니다. return queryInterface.dropTable('테이블명'); } };
이렇게 migration파일은 테이블 생성, 스키마 변경 등과 관련된 데이터베이스 작업을 정의하는데 사용되며, 업(Up) 함수는 migration을 실행하고 다운 함수는 롤백을 수행합니다.
실제로 아래와 같이 npm run migrate:down 을 실행해보면
npm run migrate:down
Users를 먼저 migrate 했고 그다음 Products를 migrate 했기 때문에 가장 최근에 migrate된 것이 Products인 것을 확인할 수 있었고 이로 인해 Products 하나만 reverting 되었습니다.
Workbench로 다시 이동해서 새로고침해서 확인해보면 [Users, Products] 원래 두개가 있었는데
지금은 [ Products] 가 사라진 것을 확인할 수 있었습니다.
그럼 npm run migrate:down을 한번 더 실행을 해보면
npm run migrate:down
여기에서 create users를 reverting 했다고 메시지가 뜹니다.
그럼 Workbench로 다시 돌아와서 sequelizeMeta table을 새로고침 해주면 reverted되어 migration에 실행된 파일이 없는 것으로 나타납니다.
이상태에서 다시 npx run migrate:up을 실행해보면 방금 전까지만 하더라도 바로 실행 했을 때 migration 할게 없다고 나왔지만 reverting을 두 번 하고 실행했더니 Users와 Products 두 개가 동시에 migrating 되었다고 메시지가 나타납니다.
npx run migrate:up
그럼 또 다시 Workbench로 다시 돌아와서 새로고침하면 실행된 파일명이 추가된 것을 확인 할 수 있습니다.
마지막으로 실제로 변경된 migration 코드들이 제대로 적용되었는지 확인해야하는데 default 혹은 Null과 같은 것을 Users 부터 차근차근 넘어가면서 확인해봐야합니다.property를 먼저 확인하겠습니다. (not Null, createdAt, updatedAt, MySQL에서 제공하는 형태인지 확인)
not Null, createdAt, updatedAt, MySQL에서 제공하는 형태로 설정되어있습니다. 그리고 이렇게 엔티티 관계도도 확인할 수 있습니다
여기까지 진행했으면 본격적으로 개발 준비가 완료된 것입니다. 이다음부터는 API를 하나씩 구현해나가시면 됩니다.
'TIL (Today I Learned)' 카테고리의 다른 글
#TIL_백오피스 프로젝트 회고록(음식 배달 서비스) (1) 2023.12.18 # class 대하여!.! 간단정리 (1) 2023.12.17 #TIL(error)_ERROR: NodeJS address already in use // Window 해결 방법 (0) 2023.12.11 #TIL_Prisma에 대하여!.! (0) 2023.12.08 #TIL_ EC2를 이용해서 프로젝트 배포하기(1편 EC2 생성하기) (1) 2023.12.04