개발일지
개발일지 4주차 WIL
index.ys
2023. 4. 30. 16:22
ORM
- ORM이란 Object Relational Maping의 약자이다 즉, 객체와 관계형 데이터베이스(RDBMS)를 자동으로 연결해주는 것
- 객체 지향 프로그래밍은 클래스를 사용하고 관계형 DB는 테이블을 사용하므로 객체모델과 관계형 DB간에 불일치가 존재함, 이를 해결하기 위해 ORM을 사용하여 SQL을 생성하고 불일치를 해결함.
- DB를 변경할때 모든 Raw Query를 ORM으로 빠르게 변경가능
- DB Table 수정시 빠르게 수정, 변경가능
- 대표적인 ORM:Type ORM , Prisma, Sequelize 등이 있음
ORM의 장점
- 객체지향적인 코드, 직관적인 코드, 데이터조작을 편리하게함, 생산성을 높혀줌
- 코드의 가독성을 올려줌
- 코드의 재사용 및 유지보수 편리
ORM의 단점
ORM 예시 코드
Type ORM
app.js
- 라우터 설정, 모델 설정
- 연결 실행
import { AppDataSource } from "./data-source";
import * as express from "express";
import userRouter from "./routers/UserRouter";
const app = express();
app.use(express.json());
AppDataSource.initialize()
.then(async () => {})
.catch((error) => console.log(error));
app.use("/api", userRouter);
app.listen(3000, () => {
console.log("Server running");
});
data-source.ts
- 데이터 베이스 연결 정보를 설정
import "reflect-metadata";
import { DataSource } from "typeorm";
import { Chat } from "./entity/Chat";
import { User } from "./entity/User";
export const AppDataSource = new DataSource({
type: "mysql",
host: "localhost",
port: 3000,
username: "ID",
password: "PW",
database: "DBname",
synchronize: true,
logging: false,
entities: [User, Chat],
migrations: [],
subscribers: [],
});
모델 생성
- 컬럼의 타입, 관계설정 ,제약조건 등을 추가함
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
import { Chat } from "./Chat";
@Entity({ name: "users" })
export class User {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 10 })
firstName: string;
@Column({ nullable: false })
lastName: string;
@Column()
age: number;
@OneToMany(() => Chat, (chat) => chat.user)
chats: Promise<Chat[]>;
}
데이터 삽입 create()
- firstName, lastName, age 세개의 인자를 body로부터 전달받음
- userRepo 변수에 User모델을 할당함
- User테이블에 body로부터 전달받은 정보를 create(info)로 생성함
- 생성이 완료되면 save(user)메서드로 정보를 저장하고 저장한 정보를 응답으로 반환함
addUser = async (req: Request, res: Response) => {
let info = {
firstName: req.body.firstName,
lastName: req.body.lastName,
age: req.body.age,
};
const userRepo = AppDataSource.getRepository(User);
const user = userRepo.create(info);
await userRepo
.save(user)
.then((data) => {
res.json(data);
})
.catch((err) => console.log(err));
};
데이터 조회 findOne
- 파라미터로 id를 전달받음
- User테이블을 userRepo에 저장
- userRepo에서 파라미터에 해당하는 id를 가진 데이터를 검색
- 파라미터에 해당하는 데이터 발견시 찾은 데이터를 응답으로 반환하고 콘솔에도 찍음.
getUser = async (req: Request, res: Response) => {
let name = req.params.firstName;
const userRepo = AppDataSource.getRepository(User);
await userRepo
.findOne({ where: { firstName: name } })
.then((data) => {
res.json(data);
console.log("Get User: ", data);
})
.catch((err) => console.log(err));
};
Sequelize
config.json
- 데이터 베이스 연결 정보를 설정
{
"development": {
"username": "유저이름",
"password": "패스워드",
"database": "데이터베이스이름",
"host": "localhost",
"dialect": "mysql"
}
}
app.js
- 포트 설정 , 라우터 설정
const express = require("express");
const cookieParser = require("cookie-parser");
const usersRouter = require("./routes/users.route");
const postsRouter = require("./routes/posts.route");
const app = express();
const PORT = 3018;
app.use(express.json());
app.use(cookieParser());
app.use('/api', [usersRouter, postsRouter]);
app.listen(PORT, () => {
console.log(PORT, '포트 번호로 서버가 실행되었습니다.');
})
테이블 생성
- 명령어를 통해 DB에 테이블을 생성함
npx sequelize migration:create --name 테이블이름
생성된 테이블 예시
- 각 컬럼별로 타입설정, 참조할 외래키설정 등 컬럼의 속성을 정의함
'use strict';
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Users', {
userId: {
allowNull: false, // NOT NULL
autoIncrement: true, // AUTO_INCREMENT
primaryKey: true, // Primary Key (기본키)
type: Sequelize.INTEGER,
},
email: {
allowNull: false, // NOT NULL
type: Sequelize.STRING,
unique: true,
},
password: {
allowNull: false, // NOT NULL
type: Sequelize.STRING,
},
createdAt: {
allowNull: false, // NOT NULL
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
updatedAt: {
allowNull: false, // NOT NULL
type: Sequelize.DATE,
defaultValue: Sequelize.fn('now'),
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Users');
},
};
마이그레이트 하여 변경사항을 DB에 적용함
sequelize db:migrate
생성된 모델 예시
- 마이그레이션 파일과 동일하게 속성을 동일하게 작성함.
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Users extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
// 1. Users 모델에서
this.hasOne(models.UserInfos, { // 2. UserInfos 모델에게 1:1 관계 설정을 합니다.
sourceKey: 'userId', // 3. Users 모델의 userId 컬럼을
foreignKey: 'UserId', // 4. UserInfos 모델의 UserId 컬럼과 연결합니다.
});
// 1. Users 모델에서
this.hasMany(models.Posts, { // 2. Posts 모델에게 1:N 관계 설정을 합니다.
sourceKey: 'userId', // 3. Users 모델의 userId 컬럼을
foreignKey: 'UserId', // 4. Posts 모델의 UserId 컬럼과 연결합니다.
});
// 1. Users 모델에서
this.hasMany(models.Comments, { // 2. Comments 모델에게 1:N 관계 설정을 합니다.
sourceKey: 'userId', // 3. Users 모델의 userId 컬럼을
foreignKey: 'UserId', // 4. Comments 모델의 UserId 컬럼과 연결합니다.
});
// 1. Users 모델에서
this.hasMany(models.UserHistories, { // 2. UserHistories 모델에게 1:N 관계 설정을 합니다.
sourceKey: 'userId', // 3. Users 모델의 userId 컬럼을
foreignKey: 'UserId', // 4. UserHistories 모델의 UserId 컬럼과 연결합니다.
});
}
}
Users.init(
{
userId: {
allowNull: false, // NOT NULL
autoIncrement: true, // AUTO_INCREMENT
primaryKey: true, // Primary Key (기본키)
type: DataTypes.INTEGER,
},
email: {
allowNull: false, // NOT NULL
type: DataTypes.STRING,
unique: true,
},
password: {
allowNull: false, // NOT NULL
type: DataTypes.STRING,
},
createdAt: {
allowNull: false, // NOT NULL
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
updatedAt: {
allowNull: false, // NOT NULL
type: DataTypes.DATE,
defaultValue: DataTypes.NOW,
},
},
{
sequelize,
modelName: 'Users',
}
);
return Users;
};
데이터 삽입 create
- body로 title과, content를 인자로 전달받음
- Posts테이블에 전달 받은 id와 title, content를 생성함
- 생성한 데이터를 응답으로 반환
// 게시글 생성
router.post("/posts", authMiddleware, async (req, res) => {
const { userId } = res.locals.user;
const { title, content } = req.body;
const post = await Posts.create({
UserId: userId,
title,
content,
});
return res.status(201).json({ data: post });
});
데이터 조회 findAll, findOne, findeById
- findAll메서드로 Posts테이블에 있는 모든 데이터를 조회
- 생성된 시간을 기준으로 오름차순으로 데이터를 조회하는 조건을 검
- 조회한 모든 데이터를 응답으로 반환
router.get("/posts", async (req, res) => {
const posts = await Posts.findAll({
attributes: ["postId", "title", "createdAt", "updatedAt"],
order: [['createdAt', 'DESC']],
});
return res.status(200).json({ data: posts });
});
Prisma
Schema 설정
- Datasource: 연결할 디비를 말한다.
- Generator: Prisma Client 를 생성하고 싶다는 것을 가리킨다.
- Data model: 애플리케이션 모델을 정의한다.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
데이터 생성
- user테이블에 데이터를생성
const newUser = await client.user.create({
data: {
name: "kim",
email: "1234@naver.com",
},
});
res.status(200).json({ newUser });
요약
- 대표적인 ORM인 sequelize, TypeORM, prisma의 전체적인 동작 방식은 모두 흡사하지만, 각각의 ORM의 특성이 조금씩 다르기 때문에 개발환경에 맞게 어떤 ORM을 사용할지 고려하고 ORM을 적용해야함
NoSQL vs SQL
NoSQL ( not only SQL )이란
- 비관계형 데이터 베이스를 뜻함, 관계형 테이블과 다른 형식으로 데이터를 저장함
- Schema가 존재하지 않음
- 대표적인 NoSQL: MongoDB, Redis, Cassandra
- 사용 서비스: 실시간 웹 애플리케이션, 빅 데이터, 대형 커뮤니티 사이트 등 대량의 데이터를 빠른시간안에 처리 할 수 있는 서비스에 적합.
SQL 이란
- SQL은 구조화된 쿼리언어 (Structured Query Language)의 약자이다
- 데이터는 엄격하게 정해진 데이터 구조를 따라 데이터베이스 테이블에 저장됨
- 대표적인 SQL: MySQL, Orcle, SQLlite, MariaDB
- 사용 서비스: 온라인 쇼핑몰, 소셜 네트워크, 블로그 , 뉴스 사이트 등 정해진 테이블에 데이터를 삽입하고 관리할 수 있는 환경에 적합함
요약
- NoSQL과 SQL은 어떤 서비스를 개발할지에 따라 어떤 DB를 사용하는 것이 적합한지 판단해보고 사용해야함.