...
Mysql 테이블 생성 구문
create schema `nodejs` default character set utf8;
use nodejs;
drop table if exists comments;
drop table if exists users;
create table nodejs.users (
id int not null primary key auto_increment,
name varchar(20) not null,
age smallint unsigned not null,
married tinyint not null, -- tinyint는 0과 1 불리언 용
comment text null, -- 자기 소개
created_at datetime not null default now(),
unique index name_unique (name asc)
)
comment = "사용자 정보"
default character set = utf8
engine = InnoDB;
create table nodejs.comments (
id int not null primary key auto_increment,
commenter int not null,
comment varchar(100) not null, -- 댓글
created_at datetime not null default now(),
index commenter_idx(commenter ASC),
constraint commenter foreign key(commenter) references nodejs.users(id) on delete cascade on update cascade
)
comment = "댓글"
default charset = utf8mb4 -- mb4는 이모티콘도 넣을 수 있음
engine = InnoDB;
insert into users(name, age, married, comment) values('zero', 24, 0, '자기소개1');
insert into users(name, age, married, comment) values('nero', 32, 1, '자기소개2');
Sequelize 모델 정의하기
이제 MySQL에서 정의한 테이블을 시퀄라이즈에서도 정의해야 한다.
MySQL의 테이블은 시퀄라이즈의 모델과 대응된다.
시퀄라이즈는 모델과 MySQL의 테이블을 연결해주는 역할을 한다.
위에서 테이블을 생성했으니,
User 와 Comment 모델을 만들어 users 테이블과 comments 테이블에 연결해보자.
시퀄라이즈는 기본적으로 모델 이름은 단수형 (User), 테이블 이름은 복수형 (users) 으로 사용한다.
models/user.js
const Sequelize = require('sequelize');
class User extends Sequelize.Model {
// 스태틱 메소드
// 테이블에 대한 설정
static init(sequelize) {
return super.init(
{ // 첫번째 객체 인수는 테이블 필드에 대한 설정
name: {
type: Sequelize.STRING(20),
allowNull: false,
unique: true,
},
age: {
type: Sequelize.SMALLINT,
allowNull: false,
},
married: {
type: Sequelize.BOOLEAN,
allowNull: false,
},
comment: {
type: Sequelize.TEXT,
allowNull: true,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
},
{ // 두번째 객체 인수는 테이블 자체에 대한 설정
sequelize, /* static init 메서드의 매개변수와 연결되는 옵션으로, db.sequelize 객체를 넣어야 한다. */
timestamps: false, /* true : 각각 레코드가 생성, 수정될 때의 시간이 자동으로 입력된다. */
underscored: false, /* 카멜 표기법을 스네이크 표기법으로 바꾸는 옵션 */
modelName: 'User', /* 모델 이름을 설정. */
tableName: 'users', /* 데이터베이스의 테이블 이름. */
paranoid: false, /* true : deletedAt이라는 컬럼이 생기고 지운 시각이 기록된다. */
charset: 'utf8', /* 인코딩 */
collate: 'utf8_general_ci'
}
);
}
// 다른 모델과의 관계
static associate(db) { // 인자로 index.js에서 만든 여러 테이블이 저장되어있는 db객체를 받을 것이다.
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', onDelete: 'cascade', onUpdate: 'cascade' });
// db.User (hasMany) db.Comment = 1:N 관계 이다.
// db.User는 가지고있다. 많이. db.Comment를
}
};
module.exports = User;
models/comment.js
const Sequelize = require('sequelize');
class Comment extends Sequelize.Model {
static init(sequelize) {
return super.init(
{
comment: {
type: Sequelize.STRING(100),
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: true,
defaultValue: Sequelize.NOW,
},
},
{
sequelize,
timestamps: false,
modelName: 'Comment',
tableName: 'comments',
paranoid: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci'
});
}
static associate(db) {
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id', onDelete: 'cascade', onUpdate: 'cascade'});
// db.Comment (belongTo) db.User = N:1 관계 이다.
// db.Comment는 속해있다. db.User에게
}
};
module.exports = Comment;
모델은 Sequelize.Model을 확장한 클래스로 선언한다.
모델은 static init 메서드와 static associate 메서드로 나뉘는데,
init 메서드에서는 테이블에 대한 설정을 하고,
associate 메서드에는 다른 모델과의 관계(1:1, 1:N)를 적는다.
init 메서드의 부모 콜백 super.init 메서드는
첫 번째 인수는 테이블 컬럼에 대한 설정이고,
두 번째 인수는 테이블 자체에 대한 설정이다.
시퀄라이즈는 알아서 id를 기본 키로 연결하므로, id 컬럼은 따로 적어줄 필요는 없다.
시퀄라이즈의 자료형
시퀄라이즈의 자료형은 MySQL과 조금 다르다.
아래 비교를 통해 맞는 옵션을 입력하자.
MySQL | Sequelize |
VARCHAR(100) | STRING(100) |
INT | INTEGER |
TINYINT | BOOLEAN |
DATETIME | DATE |
INT UNSIGNED | INTEGER.UNSIGNED |
NOT NULL | allowNull: false |
UNIQUE | unique: true |
DEFAULT now() | defaultValue: Sequelize.NOW |
ZEROFILL | INTEGER.ZEROFILL |
* 참고
https://sequelize.org/v5/manual/data-types.html
시퀄라이즈 옵션
super.init의 두 번째 인수는 테이블 옵션이다.
- sequelize:
static init 메서드의 매개변수와 연결되는 옵션으로, db.sequelize 객체를 넣어야 한다.
나중에 model/index.js에서 연결한다. - timestamps:
이 속성 값이 true면 시퀄라이즈는 createdAt과 updatedAt 컬럼을 추가하며, 각각 로우가 생성될 때와 수정될 때의 시간이 자동으로 입력된다.
(그러나 예제에선 직접 created_at 컬럼을 만들었으므로 지금은 timestamps 속성이 필요없다. 나중엔 꼭 true로 하고 하자) - underscored:
시퀄라이즈는 기본적으로 테이블명과 컬럼명을 카멜 표기법 (camel case) 으로 만든다.
이를 스네이크 표기법 (snake case) 으로 바꾸는 옵션이다 (예를들어 updatedAt 을 updated_at 으로). - modelName:
모델 이름을 설정할 수 있다. 노드 프로젝트에서 사용한다. - tableName:
실제 데이터베이스의 테이블 이름.
기본적으로 모델 이름의 소문자 및 복수형으로 만든다.
예를 들어 모델 이름이 User 라면 테이블 이름은 users 이다. - paranoid:
true로 설정하면 deletedAt이라는 컬럼이 생긴다.
로우를 삭제할 때 완전히 지우지 않고, deletedAt에 지운 시각이 기록된다.
로우를 조회하는 명령을 내렸을 경우 deletedAt의 값이 null인 로우를 조회한다.
이렇게 하는 이유는 후에 로우를 복원하기 위해서다. 로우를 복원해야 할 상황이 생길 것 같다면 미리 true로 설정해두자. - charset / collate:
각각 utf8 과 utf8_general_ci 로 설정해야 한글이 입력된다.
이모티콘까지 입력할 수 있게 하고 싶다면 utf8mb4 와 utf8mb4_general_ci 를 입력한다.
이제 models/index.js에 두 테이블 파일을 연결시켜주자.
const Sequelize = require('sequelize');
// 클래스를 불러온다.
const User = require('./user')
const Comment = require('./comment')
const env = process.env.NODE_ENV || 'development';
// config/config.json 파일에 있는 설정값들을 불러온다.
// config객체의 env변수(development)키 의 객체값들을 불러온다.
// 즉, 데이터베이스 설정을 불러온다고 말할 수 있다.
const config = require("../config/config.json")[env]
const db = {};
// new Sequelize를 통해 MySQL 연결 객체를 생성한다.
const sequelize = new Sequelize(config.database, config.username, config.password, config)
// 연결객체를 나중에 재사용하기 위해 db.sequelize에 넣어둔다.
db.sequelize = sequelize;
// 모델 클래스를 넣음.
db.User = User;
db.Comment = Comment;
// 모델과 테이블 종합적인 연결이 설정된다.
User.init(sequelize);
Comment.init(sequelize);
// db객체 안에 있는 모델들 간의 관계가 설정된다.
User.associate(db);
Comment.associate(db);
// 모듈로 꺼낸다.
module.exports = db;
db라는 객체에 User과 Comment 모델을 담았다.
앞으로 db 객체를 require하여 User과 Comment 모델에 접근할 수 있을 것이다.
또한 User.init과 Comment.init은 각각의 모델의 static.init 메서드를 호출하며, init이 실행되어야 테이블이 모델로 연결된다.
다른 테이블과의 관계를 연결하는 associate 메서드 역시 실행해둔다.
Sequelize 관계 정의하기
이번엔 users 테이블과 comments 테이블 간의 관계를 정의해보자.
사용자 한 명은 댓글을 여러 개 작성할 수 있지만, 댓글 하나에 작성자가 여러 명일 수는 없다.
이러한 관계를 일대다 (1:N) 관계라고 한다. 위 관계에서는 사용자가 1이고 댓글인 N이다.
다른 관계로는 일대일 (1:1), 다대다 (N:M) 관계가 있다.
일대일 관계로는 사용자와 사용자의 대한 정보 테이블을 예로 들 수 있으며,
다대다 관계로는 게시글 테이블과 해시태그 (#) 테이블 관계를 예로 들 수 있겠다.
한 게시글에 여러 해시태그가 달릴 수 있으며, 한 해시태그 또한 여러 게시글에 달릴 수 있다.
↓데이터베이스 관계 모델에 더 알고 싶다면 ?
MySQL에서는 JOIN이라는 기능으로 여러 테이블 간의 관계를 파악해 결과를 도출한다.
시퀄라이즈는 JOIN 기능도 알아서 구현한다.
대신 테이블 간에 어떠한 관계가 있는지 시퀄라이즈에 알려줘야 한다.
1:N 관계 (hasMany, belongsTo)
시퀄라이즈에서는 1:N 관계를 hasMany 메서드로 표현한다.
users 테이블의 로우 하나를 불러올 때 연결된 comments 테이블의 로우들도 같이 불러올 수 있다.
반대로 belongsTo 메서드도 있다.
이는 comments 테이블의 로우를 불러올 때 연결된 users 테이블의 로우를 가져온다.
보통 외래키가 붙은 테이블이 belongTo가 된다.
이제 모델 각각의 static associate 메서드에 정의를 해줘야 한다.
/* user.js */
static associate(db) {
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', onDelete: 'cascade', onUpdate: 'cascade' });
// db.User (hasMany) db.Comment = 1:N 관계 이다.
// 남(db.Comment)의 컬럼 commenter가 내(db.User) 컬럼 id를 참조 하고 있다.
}
};
// comment.js
static associate(db) {
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id', onDelete: 'cascade', onUpdate: 'cascade'});
// db.Comment (belongTo) db.User = N:1 관계 이다.
// 내(db.Comment)의 컬럼 commenter는 남(db.User) 컬럼 id에 속해 있다.
}
};
간단히 User가 많은 댓글을 가질 수 있으니 User.hasMany가 되는 것이고,
Comment는 한 User에 속할 수 있으니 Comment.belongsTo가 되는 것이다.
둘이 소통하는 키는 foreignKey인 commenter이며,
User의 sourceKey는 곧 Commenter의 targetKey가 된다 (hasMany에서는 sourceKey, belongsTo에서는 targetKey).
foreignKey를 따로 지정하지 않는다면 이름이 모델명+기본 키인 컬럼이 모델에 생성된다.
즉, 예를 들어 위 예제에서 commenter를 foreignKey로 설정해 주지 않았다면, 제약조건에 따라 모델명인 User과 기본 키인 id가 합쳐진 UserId가 foreignKey로 따로 생성된다.
1:1 관계 (hasOne, belongsTo)
1:1 관계에서는 hasMany 대신 hasOne을 사용한다.
foriegnKey, sourceKey, targetKey의 사용법은 1:N 관계와 같다.
hasOne도 1:1, belongsTo도 1:1이면, 누가 기준으로 될지 애매할 때가 있다.
이때 외래키가 붙은 모델은 belongsTo로 기준을 잡아주면 된다.
N:M 관계 (belongsToMany)
시퀄라이즈에는 N:M 관계를 belongsToMany 메서드로 표현한다.
이 경우엔 어느 한 테이블이 어느 다른 테이블에 종속되는 관계가 아니다.
예를 들어 Post 모델과 Hashtag 모델이 있다고 할 때, 다음과 같이 표현할 수 있다.
// Post
db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
// Hashtag
db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });
N:M 관계의 특성상 새로운 모델이 다음과 같이 생성되며, through 속성에 그 이름을 적으면 된다.
새로 생성된 PostHashtag 모델에는 게시글과 해시태그의 아이디가 저장된다.
N:M에서는 데이터를 조회할 때 여러 단계를 거쳐야 한다.
예를 들어 #노드 해시태그를 사용한 게시물을 조회하는 경우, 먼저 #노드 해시태그를 Hashtag 모델에서 조회하고, 가져온 태그의 아이디인 '1'을 바탕으로 PostHashtag 모델에서 hashtagId가 1인 postId들을 찾아 Post 모델에서 해당 정보를 가져와야 한다.
또한, 자동으로 만들어진 모델들도 다음과 같은 접근할 수 있다.
db.sequelize.models.PostHashtag
Reference
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.