SMALL
안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
초기 세팅
$ yarn add express morgan sequelize sequelize-cli mysql2
$ yarn add typescript
$ yarn add -D nodemon
$ npx sequelize init
$ yarn init -y
$ npx tsc --init
models, seeders, config, migrations 파일 생성이 되고, package.json 수정
package.json
{
"name": "sequelize-test",
"version": "1.0.0",
"description": "시퀄라이즈 연습하기",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "chany",
"license": "MIT",
"dependencies": {
"express": "^4.18.2",
"morgan": "^1.10.0",
"mysql2": "^2.3.3",
"sequelize": "^6.28.0",
"sequelize-cli": "^6.5.2",
"typescript": "^4.9.4"
},
"devDependencies": {
"nodemon": "^2.0.20"
}
}
config/config.json 파일의 development에 username, password, database 등을 수정한 후 데이터베이스 생성
$ npx sequelie db:create
Sequelize CLI [Node: 18.12.1, CLI: 6.5.2, ORM: 6.28.0]
Loaded configuration file "config/config.json".
Using environment "development".
Database nodeBird created.
config/config.json → config/config.ts 로 수정
// 객체 config에 대한 타입 정의
type Config = {
username: string;
password: string;
database: string;
host: string;
[key: string]: string;
};
interface IConfigGroup {
development: Config;
test: Config;
production: Config;
}
const config: IConfigGroup = {
development: {
username: "node",
password: "test123",
database: "nodeBird",
host: "127.0.0.1",
dialect: "mysql",
},
test: {
username: "root",
password: "test123",
database: "database_test",
host: "127.0.0.1",
dialect: "mysql",
},
production: {
username: "root",
password: "test123",
database: "database_production",
host: "127.0.0.1",
dialect: "mysql",
},
};
export default config;
model/index.ts 수정
아래 코드는 참고만!
"use strict";
import { Sequelize } from "sequelize";
import config from "../config/config";
interface dbType {
sequelize?: Sequelize;
}
const env =
(process.env.NODE_ENV as "production" | "test" | "development") ||
"development";
const db: dbType = {};
// config 파일에서 데이터베이스 설정을 불러와 new Sequelize를 통해 MySQL 연결 객체 생성
const { database, username, password } = config[env];
let sequelize = new Sequelize(database, username, password, config[env]);
// 연결 객체를 추후 재사용을 위해 db 객체에 담아 export 처리
db.sequelize = sequelize;
export default db;
models/index.ts
"use strict";
import { Sequelize } from "sequelize";
import config from "../config/config";
import User from "./user";
import Post from "./post";
import Tag from "./tag";
interface IDb {
sequelize: Sequelize;
}
// config 파일에서 데이터베이스 설정을 불러와 new Sequelize를 통해 MySQL 연결 객체 생성
const { database, username, password } = config["development"];
let sequelize = new Sequelize(
database,
username,
password,
config["development"]
);
// 연결 객체를 추후 재사용을 위해 db 객체에 담아 export 처리
const db: IDb = { sequelize };
export default db;
시퀄라이즈를 통해 익스프레스 앱과 MySQL 연결하기
app.ts
아래 코드 중 미들웨어, 에러 렌더링 등등이 필요하지 않으므로 참고만!
import express, { NextFunction, Request, Response } from "express";
import morgan from "morgan";
import path from "path";
// 시퀄라이즈 객체가 담긴 db를 import
import db from "./models";
const app = express();
app.set("port", process.env.PORT || 8001); // 포트 설정
// 시퀄라이즈 초기화
db.sequelize
?.sync({ force: false })
.then(() => {
console.log("데이터베이스 연결 성공!");
})
.catch((error) => {
console.error(error);
});
// 미들웨어
app.use(morgan("dev"));
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
interface Error {
message?: string;
status?: number;
}
app.use((req: Request, res: Response, next: NextFunction) => {
const error: Error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== "production" ? err : {};
res.status(err.status || 500);
res.render("error");
});
// 포트 연결
app.listen(app.get("port"), () => {
console.log(app.get("port"), "번에서 대기 중!");
});
.env
PORT=8002
app.ts
import express, { NextFunction, Request, Response } from "express";
// 시퀄라이즈 객체가 담긴 db를 import
import db from "./models";
import indexRouter from "./routes";
import dotenv from "dotenv"; // .env 파일을 읽어서 process.env로 만듦
dotenv.config();
const app = express();
app.set("port", process.env.PORT || 8001); // 포트 설정
// 시퀄라이즈 초기화
db.sequelize
?.sync({ force: true })
.then(() => {
console.log("데이터베이스 연결 성공!");
})
.catch((error) => {
console.error(error);
});
app.use("/", indexRouter);
// 포트 연결
app.listen(app.get("port"), () => {
console.log(app.get("port"), "번에서 대기 중!");
});
$ npx tsc && yarn start
yarn run v1.22.19
$ nodemon app
[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
8001 번에서 대기 중!
Executing (default): SELECT 1+1 AS result
데이터베이스 연결 성공!
모델 및 관계 정의하기
- User - Post : 1:N 관계
- Post - Tag : M:N 관계
models/index.ts
"use strict";
import { Sequelize } from "sequelize";
import config from "../config/config";
import User from "./user";
import Post from "./post";
import Tag from "./tag";
interface IDb {
sequelize: Sequelize;
}
// config 파일에서 데이터베이스 설정을 불러와 new Sequelize를 통해 MySQL 연결 객체 생성
const { database, username, password } = config["development"];
let sequelize = new Sequelize(
database,
username,
password,
config["development"]
);
const db: IDb = { sequelize };
// 모델 생성 메소드 호출
User.initiate(sequelize);
Post.initiate(sequelize);
Tag.initiate(sequelize);
// 관계 설정 메소드 호출
User.associate();
Post.associate();
Tag.associate();
export default db;
models/user.ts
// User 모델
import { Sequelize, Model, DataTypes } from "sequelize";
import Post from "./post";
interface UserAttributes {
username: string;
password: string;
email?: string;
}
class User extends Model<UserAttributes> {
// id can be undefined during creation when using `autoIncrement`
public readonly id!: number;
public username!: string;
public password!: string;
public email?: string;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
static initiate(sequelize: Sequelize) {
User.init(
{
username: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING(15),
allowNull: false,
},
email: {
type: DataTypes.STRING(50),
allowNull: true,
},
},
{
sequelize,
timestamps: true,
underscored: true, // true
modelName: "User",
tableName: "users",
paranoid: false,
charset: "utf8",
collate: "utf8_general_ci",
}
);
}
static associate() {
User.hasMany(Post);
}
}
export default User;
models/post.ts
import {
Sequelize,
Model,
DataTypes,
BelongsToManyAddAssociationMixin,
} from "sequelize";
import Tag from "./tag";
import User from "./user";
// Post 모델 정의
interface PostAttributes {
title: string;
content: string;
user_id?: number;
}
class Post extends Model<PostAttributes> {
public readonly id!: number;
public title!: string;
public content!: string;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
public addTag!: BelongsToManyAddAssociationMixin<Tag, string>;
static initiate(sequelize: Sequelize) {
Post.init(
{
title: {
type: DataTypes.STRING(30),
allowNull: false,
},
content: {
type: DataTypes.STRING(200),
allowNull: false,
},
},
{
sequelize,
timestamps: true,
underscored: true,
modelName: "Post",
tableName: "posts",
paranoid: false,
charset: "utf8mb4",
collate: "utf8mb4_general_ci",
}
);
}
static associate() {
Post.belongsTo(User);
Post.belongsToMany(Tag, { through: "PostTag" });
}
}
export default Post;
models/tag.ts
import { Sequelize, Model, DataTypes } from "sequelize";
import Post from "./post";
// Tag 모델 정의
interface TagAttributes {
title: string;
}
class Tag extends Model<TagAttributes> {
public readonly id!: number;
public title!: string;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
static initiate(sequelize: Sequelize) {
Tag.init(
{
title: {
type: DataTypes.STRING(15),
allowNull: false,
unique: true,
},
},
{
sequelize,
timestamps: true,
underscored: true,
modelName: "Tag",
tableName: "tags",
paranoid: false,
charset: "utf8mb4",
collate: "utf8mb4_general_ci",
}
);
}
static associate() {
Tag.belongsToMany(Post, { through: "PostTag" });
}
}
export default Tag;
라우터
routes/index.ts
import express from "express";
import index from "../controllers";
const router = express.Router();
router.get("/", index);
export default router;
컨트롤러
controllers/index.ts
import User from "../models/user";
import Post from "../models/post";
import Tag from "../models/tag";
import { NextFunction, Request, Response } from "express";
export default async (req: Request, res: Response, next: NextFunction) => {
try {
// User 생성 전 조회하기
let user = await User.findOne({
where: { username: "chany" },
});
if (!user) {
// 존재하지 않는 경우 User 생성
user = await User.create({
username: "chany",
password: "test1234",
email: "chany@abc.com",
});
}
const tag1 = await Tag.create({ title: "Tag1" });
const tag2 = await Tag.create({ title: "Tag2" });
const tag3 = await Tag.create({ title: "Tag3" });
// 게시글1 추가 및 브릿지 테이블 컬럼 추가
const post1 = await Post.create({
title: "안녕하세요1",
content: "노드 뿌시기!",
user_id: user.id,
});
await post1.addTag(tag1);
await post1.addTag(tag2);
// 게시글2 추가 및 브릿지 테이블 컬럼 추가
const post2 = await Post.create({
title: "안녕하세요2",
content: "내가 뿌셔짐!",
user_id: user.id,
});
await post2.addTag(tag1);
await post2.addTag(tag3);
console.log(
await User.findOne({
include: [
{
model: Post,
include: [{ model: Tag }],
},
],
where: { username: "chany" },
})
);
} catch (error) {
console.error(error);
next(error);
}
};
// const addTagToPost = (post: Post, tagList: Tag[]) => {
// tagList.forEach((tag) => {
// post.addTag(tag);
// });
// };
- 필요 없는 코드는 넣지 않기
- index에서 모델을 db.User 등으로 담아서 사용하지 말고, 그냥 import!
- ID값은 primarykey, autoIncrement 설정
- underscored: true 설정
- 위 코드 중 id 값들에 대해 따로 autoIncrement 설정을 하지 않았는데 어떻게 1씩 증가하며 번호가 매겨지는가?
- id can be undefined during creation when using 'autoIncrement' → 모델로 인해 테이블이 생성될 때 id값이 undefined면 시퀄라이즈에서 autoIncrement 설정을 추가하여 생성한다고 함!
- .env는 dotenv 패키지를 다운 받은 후 dotenv.config()를 하면 .env 파일의 내용을 process.env로 만들어줌
따라서 중요한 정보, 비밀번호 등은 .env 파일에 만들어 불러와서 사용!
LIST
'JavaScript > Node.js' 카테고리의 다른 글
Node, Express, typescript boilerplate (0) | 2023.01.10 |
---|---|
Node) RESTful API 정리 (0) | 2023.01.04 |
Node) 익스프레스로 SNS 서비스 만들기 - multer 패키지로 이미지 업로드 구현하기 (0) | 2023.01.02 |
Node) 익스프레스로 SNS 서비스 만들기 - Passport 모듈로 로그인 구현하기 (0) | 2023.01.02 |
Node) 익스프레스로 SNS 서비스 만들기 - 프로젝트 구조 갖추기, 모델 생성, 데이터베이스 연결 (0) | 2023.01.02 |