Sequelize + MSSQL
1. Sequelize란?
Node 에서 자주 사용되는 ORM 라이브러리다. ORM(Object-Relational Mapping)이란 관계형 데이터베이스와 자바스크립트 객체를 서로 연결해주는 도구로 좀 더 DB작업을 쉽게 처리할 수 있도록 도와준다.
타입스크립트를 이용하여 구현할 거기 때문에 sequelize-typescript를 설치한다.
$npm install sequelize-typescript
2. sequelize 연결 설정
DB 정보를 저장할 .env파일을 root에 생성하고 DB_HOST, DB_PORT 등 사용할 변수들을 선언해 놓고 값을 입력해 놓으면, process.env.[변수명] 으로 접근 가능하다.
여러 파일들 에서도 하나의 instance에 접근가능하도록 하기위해 ORM 클래스를 만들고 singleton 패턴으로 구현하였다.
참조 : https://sequelize.org/docs/v6/
import { Sequelize } from "sequelize-typescript";
class ORM {
private static instance: Sequelize
private constructor () {
}
// 메소드 이름은 달라도 상관없다.
public static getInstance () {
return this.instance || (this.instance = new Sequelize({
host: process.env.DB_HOST,
database: process.env.DB_NAME,
dialect: "mssql",
timezone: "Asia/Seoul",
dialectOptions: {
options: {
charset: 'utf8mb4',
dateStrings: true,
typeCast: true,
encrypt: false,
},
},
define : {
timestamps : true,
},
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
storage: ":memory:",
port: process.env.DB_PORT as unknown as number,
models: [__dirname + "/models"],
modelMatch: (filename, member) => {
return (
filename.substring(0, filename.indexOf(".model")) === member.toLowerCase()
);
}
}))
}
}
export default ORM;
그런 다음 서버에서 sequelize를 불러와 아래와 같이 인증하고 싱크를 맞춰준다.
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import depthLimit from 'graphql-depth-limit';
import { createServer } from 'http';
import compression from 'compression';
import cors from 'cors';
import loadAllSchema from './schema';
import ORM from './sequelize';
require("dotenv").config();
const app = express();
const sequelize = ORM.getInstance();
(async () => {
await sequelize.authenticate().then(() => {
console.log("Authentication successful.");
});
await sequelize.sync().then(() => {
console.log("Sync ok!")
});
const schema = await loadAllSchema();
const server = new ApolloServer({
schema,
validationRules: [depthLimit(7)],
});
await server.start();
app.use('*', cors());
app.use(compression());
server.applyMiddleware({ app, path: '/graphql' });
const httpServer = createServer(app);
httpServer.listen(
{ port: 8000 },
(): void => console.log(`server Start`)
);
})();
sequelize는 연동만 해주고 그 다음 코드들은 ApolloServer와 만들어진 schema를 연결해주고 8000번 포트를 통해 주고 받는다.
3. Model 생성하기
Member라는 DB테이블과 연동하기 위해 member.model.ts파일을 만들어 준다. 해당 파일에 아래와 같이 작성해주면 된다.
interface로 생성할때 사용될 MemberDto Type을 선언해준다. @Table 데코레이터에선 테이블 공통 설정에 대한 정보를 넣어준다. 't_member'는 실제 DB에 연결된 테이블 명을 뜻한다. createdAt/updatedAt은 false로 하지않으면 자동으로 insert된다.
@Column 데코레이터를 이용해 ColumnAttribute를 설정할 수 있는데 이 때, 컬럼 속성은 DB에서 설정된 속성과 일치해야하므로 주의해야한다. 특히 DataType은 아래 링크를 참조해 각 DB유형마다 다를 수 있으므로 체크해야한다.
https://sequelize.org/docs/v7/other-topics/other-data-types/
import moment from "moment";
import { DataTypes } from "sequelize";
import { Table, Column, Model } from "sequelize-typescript";
export interface MemberDto {
user_id: string;
password: string;
member_type: string;
reg_type: string;
reg_channel: string;
member_status : string;
info_period : string;
sms_receive : boolean;
email_receive : boolean;
}
@Table({
tableName: "t_member",
createdAt: false,
updatedAt: false,
})
export class Member extends Model<Member, MemberDto> {
@Column({ type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true })
member_id!: number; //회원id
@Column({ type: DataTypes.STRING(20) })
user_id!: string; //아이디
@Column({ type: DataTypes.STRING(20) })
password!: string; //비밀번호
@Column({ type: DataTypes.STRING(), defaultValue: moment(new Date()).format('YYYY-MM-DD HH:mm:ss.SSS'), allowNull: true })
password_date!: string; //비밀번호 변경일 : 6개월 주기로 변경 필요
@Column({ type: DataTypes.CHAR(1) })
member_type!: string; //회원유형 : 개인/학원
@Column({ type: DataTypes.DATEONLY, defaultValue: DataTypes.NOW })
reg_date!: Date; //가입일
@Column({ type: DataTypes.CHAR(1) })
reg_type!: string; //가입유형 - 1 : 쌤잡, 2 : 네이버, 3 : 카카오, 4 : 구글
@Column({ type: DataTypes.CHAR(1) })
reg_channel!: string; //가입채널 - 1 : 웹, 2 : 핸드폰, 3 : 탭
@Column({ type: DataTypes.DATEONLY, allowNull: true })
rest_date!: Date; //휴면일
@Column({ type: DataTypes.DATEONLY, allowNull: true })
out_date!: Date; //탈퇴일
@Column({ type: DataTypes.CHAR(1), defaultValue: '0' })
member_status!: string; //회원상태 - 0 : Live, 1 : 휴면, 2 : 탈퇴
@Column({ type: DataTypes.CHAR(1) })
info_period!: string; //개인정보 보유기간 - 1년/3년/5년
@Column({ type: DataTypes.BOOLEAN })
sms_receive!: boolean; //문자 동의
@Column({ type: DataTypes.BOOLEAN })
email_receive!: boolean; //이메일 동의
}
mssql을 사용하는 분들은 그냥 DateTime을 넣을 시 에러가 날 수 있으므로 string DataType으로 넣어주되 string format을 위와 같이 해주면 된다. 'YYYY-MM-DD HH:mm:ss.SSS'
4. Controller 작성하기
만든 Model을 Create/Read/Update/Delete 할 수 있도록 Controller를 작성해 보도록 하자
- getItems() : 전체 Member list 가져오기
- getItemByUserId() : user_id로 해당하는 member 가져오기
- insertItem() : new member 만들기
- updateItem() : 기존 member 정보 업데이트하기
- deleteItem() : user_id로 해당 member 삭제하기
import { Member, MemberDto } from "../models/member.model";
const getItems = async () => {
const members = await Member.findAll();
return {res : true, msg : `All members found.`, data : members};
};
const getItemByUserId = async (user_id : string) => {
const member = await Member.findOne({where : { user_id : user_id}});
if (!member)
{
throw Error(`member not existed. id: ${user_id}`);
}
return {res : true, msg : `${user_id} found.`, data : [member]};
}
const insertItem = async (args : any) => {
const newMember : MemberDto = {
user_id : args.user_id,
password : args.password,
member_type : args.member_type,
reg_type: args.reg_type,
reg_channel: args.reg_channel,
member_status : args.member_status,
info_period : args.info_period,
sms_receive : args.sms_receive,
email_receive : args.email_receive
}
try {
const member : Member = await Member.create(newMember);
return {res : true, msg : `created ${args.user_id}.`, data : [member]};
} catch(error) {
return {res : false, msg : error};
}
}
const updateItem = async (user_id : string, args : any) => {
const member = await Member.findOne({where: {user_id : user_id}});
if (!member) {
throw Error(`member not updated. user_id: ${user_id}`);
}
member.password = args.password;
member.member_type = args.member_type;
member.reg_type = args.reg_type;
member.reg_channel = args.reg_channel;
member.member_status = args.member_status;
member.info_period = args.info_period;
member.sms_receive = args.sms_receive;
member.email_receive = args.email_receive;
try {
await member.save();
return {res : true, msg : `updated ${user_id}.`, data : [member]};
} catch (error) {
return {res : false, msg : error};
}
}
const deleteItem = async(user_id : string) => {
const member = await Member.findOne({where: {user_id : user_id}});
if (!member) {
throw Error(`member not existed. user_id: ${user_id}`);
}
try {
await Member.destroy({where : {user_id : user_id}});
return {res : true, msg : `delete ${user_id}.`};
} catch (error) {
return {res : false, msg : error};
}
}
export default { getItems, getItemByUserId, insertItem, updateItem, deleteItem };
각 함수들은 return 값을 객체 형태로 반환하며 data에 DB로 부터 받은 데이터를 넣는다.
해당 함수들은 추후 GraphQL의 resolver에서 호출되어 사용된다.
2022.09.21 - [개발 Study/Node, js, react] - [GraphQL] GraphQL과 REST
2022.09.27 - [개발 Study/Node, js, react] - [GraphQL] GraphQL 사용법 - (1) Schema 생성 후 ApolloServer 연동하기
'개발 Study > GraphQL' 카테고리의 다른 글
[GraphQL/Typescript] GraphQL로 이미지 파일 업로드하기 (1) | 2022.12.14 |
---|---|
[GraphQL] GraphQL 사용법 - (4) ThunderClient로 API 테스트하기 (0) | 2022.10.04 |
[GraphQL] GraphQL 사용법 - (3) Sequelize 사용하기(1:1, ForeignKey, Join) (1) | 2022.09.30 |
[GraphQL] GraphQL 사용법 - (1) Schema 생성 후 ApolloServer 연동하기 (2) | 2022.09.27 |
[GraphQL] GraphQL과 REST (2) | 2022.09.21 |
댓글