728x90
TypeORM
1. 데이터베이스 연결
Nest.js에서 데이터베이스 연동은 TypeORM을 사용한다.
TypeORM 설치
npm i typeorm@0.3.0 @nestjs/typeorm mysql
TypeORM을 모듈에 임포트!
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BoardModule } from './board/board.module';
@Module({
imports: [
TypeOrmModule.forRoot({
//데이터베이스 설정
type: 'mysql',
host: 'localhost',
port: 3306,
username: '데이터베이스 아이디',
password: '데이터베이스 비밀번호',
database: 'board',
entities: [],
synchronize: true,
}),
BoardModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
기본적인 데이터베이스 설정은 위와 같이한다
하지만 데이터베이스의 정보가 코드에 노출이 된다.
.env를 사용했던 것 처럼 @nestjs/config 패키지를 사용한다
npm i @nestjs/config
config/typeorm.config.service.ts 파일생성
import { Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
// 생성자를 통해서 DI
constructor(private readonly configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.configService.get<string>('DATABASE_HOST'),
port: this.configService.get<number>('DATABASE_PORT'),
username: this.configService.get<string>('DATABASE_USERNAME'),
password: this.configService.get<string>('DATABASE_PASSWORD'),
database: this.configService.get<string>('DATABASE_NAME'),
entities: [],
synchronize: this.configService.get<boolean>('DATABASE_SYNCHRONIZE'),
};
}
}
.env 파일생성
DATABASE_HOST="localhost"
DATABASE_PORT=3306
DATABASE_USERNAME="데이터베이스 아이디"
DATABASE_PASSWORD="데이터베이스 비밀번호"
DATABASE_NAME="board"
DATABASE_SYNCHRONIZE=true
app.module.ts 수정
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config/dist';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BoardModule } from './board/board.module';
import { TypeOrmConfigService } from './config/typeorm.config.service';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRootAsync({
useClass: TypeOrmConfigService,
}),
BoardModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
2. 엔티티 & 리포지토리 생성
엔티티 : 데이터테이블의 스키마를 정의를 하는것
article.entity.ts
import {
Column,
CreateDateColumn,
DeleteDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from "typeorm";
@Entity({ schema: "board", name: "articles" })
export class Article {
@PrimaryGeneratedColumn({ type: "int", name: "id" })
id: number;
@Column("varchar", { length: 10 })
author: string;
@Column("varchar", { length: 50 })
title: string;
@Column("varchar", { length: 1000 })
content: string;
@Column("varchar", { select: false })
password: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@DeleteDateColumn()
deletedAt: Date | null;
}
패스워드에 select: false 가 되어있는데 특별한 경우 아니고는 셀렉트를 할 필요가 없다
그래서 기본적으로 false로 한다.
(필요에 따라 셀렉트 할수있다.)
deletedAt 에 null 이 있는건
해당 엔티티가 삭제되는 순간 실제로 삭제(Hard Delete) 되는게 아니라
논리적으로 삭제(soft Delete)가 되는걸 목적으로 할 경우 사용한다.
리포지토리 : 엔티티와 서비스코드 사이에서 데이터베이스 관련된 기능을 실행
일반 리포지토리와 커스텀 리포지토리가 있다
일반 리포지토리 명세는 TypeORM의 Repository 문서를 참고
데이터베이스 연산이 부족하면 일반 리포지토리를 상속한 커스텀 지포지토리를 작성한다.
일반 리포지토리 사용 예시)
constructor(
@InjectRepository(Article) private articleRepository: Repository<Article>
) {}
를
board.service.ts 에 추가
import {
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import _ from 'lodash';
import { Repository } from 'typeorm';
import { Article } from './article.entity';
@Injectable()
export class BoardService {
constructor(
@InjectRepository(Article) private articleRepository: Repository<Article>,
) {}
private articles = [];
// 게시글 비밀번호를 저장하기 위한 Map 객체입니다.
private articlePasswords = new Map();
getArticles() {
return this.articles;
}
getArticleById(id: number) {
return this.articles.find((article) => {
return article.id === id;
});
}
createArticle(title: string, content: string, password: number) {
// id 먼저 할당
// 1번부터 시작 => 현재 배열의 크기 + 1
const articleId = this.articles.length + 1;
this.articles.push({ id: articleId, title, content });
this.articlePasswords.set(articleId, password);
return articleId;
}
updateArticle(id: number, title: string, content: string, password: number) {
if (this.articlePasswords.get(id) !== password) {
throw new UnauthorizedException(
`Article password is not correct. id: ${id}`,
);
}
const article = this.getArticleById(id);
if (_.isNil(article)) {
throw new NotFoundException(`Article not found. id: ${id}`);
}
article.title = title;
article.content = content;
}
deleteArticle(id: number, password: number) {
if (this.articlePasswords.get(id) !== password) {
throw new UnauthorizedException(
`Article password is not correct. id: ${id}`,
);
}
this.articles = this.articles.filter((article) => {
return article.id !== id;
});
}
}
board.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Article } from "./article.entity";
import { BoardController } from "./board.controller";
import { BoardService } from "./board.service";
@Module({
// 매우 중요: 서비스에서 사용할 리포지토리 사용을 imports에 명시!
imports: [TypeOrmModule.forFeature([Article])],
controllers: [BoardController],
providers: [BoardService],
})
export class BoardModule {}
typeorm.config.service.ts
import { Injectable } from '@nestjs/common';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';
import { Article } from 'src/board/article.entity';
@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
constructor(private readonly configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.configService.get<string>('DATABASE_HOST'),
port: this.configService.get<number>('DATABASE_PORT'),
username: this.configService.get<string>('DATABASE_USERNAME'),
password: this.configService.get<string>('DATABASE_PASSWORD'),
database: this.configService.get<string>('DATABASE_NAME'),
entities: [Article],
// 엔티티에 Article 추가!
synchronize: this.configService.get<boolean>('DATABASE_SYNCHRONIZE'),
};
}
}
'코딩캠프 > 내일배움캠프' 카테고리의 다른 글
[ TIL ] 02.20(월) 69일차 (0) | 2023.02.20 |
---|---|
[ WIL ] 02.13~17 14주차 (0) | 2023.02.19 |
[ TIL ] 02.16(목) 67일차 (0) | 2023.02.16 |
[ TIL ] 02.15(수) 66일차 (0) | 2023.02.15 |
[ TIL ] 02.14(화) 65일차 (0) | 2023.02.14 |