저장을 습관화
NestJS - TypeORM CRUD 본문
상황은 사용자 로그인 이후 블로그에 게시글을 작성, 조회, 수정, 삭제하는 API를 가정하며,
CRUD에 필수적인 패키지 외 가드 등은 생략한다.
사용하는 DB는 MySQL이다.
0. 사용자 정보를 가져오는 데코레이터
- user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
// 세션 방식 로그인
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
1. Create
- posting.request.dto.ts
import { PickType } from '@nestjs/swagger';
import { Posts } from '../posts.entity';
export class PostingRequestDto extends PickType(Posts, [
'title',
'content',
] as const) {}
엔티티 Posts에서 PickType을 이용하여 title, content 컬럼만을 가져오며 둘 모두 string 타입이다.
- posts.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { PostsService } from './posts.service
import { PostingRequestDto } from './dto/posting.request.dto';
import { User } from '../common/decorators/user.decorator';
import { Users } from '../users/users.entity';
export class PostsController {
constructor(private postsService: PostsService) {}
// 글 작성
@Post()
async createPost(@Body() body: PostingRequestDto, @User() user: Users) {
await this.postsService.createPost(body.title, body.content, user.id);
return `'${body.title}' 게시글 작성 완료`;
}
}
- posts.service.ts
import {
ForbiddenException,
Injectable,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Posts } from './posts.entity';
import { Repository } from 'typeorm';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Posts) private postsRepository: Repository<Posts>,
) {}
// 글 작성
async createPost(title: string, content: string, id: number) {
await this.postsRepository.save({
title: title,
content: content,
userId: id,
});
}
}
2. Read
- main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 모든 컨트롤러에 class-validation, class-transformer 사용
app.useGlobalPipes(new ValidationPipe({ transform: true }));
}
URL에 들어간 내용이 파라미터로 쓰일 경우
이를 string이 아닌 number로써 사용하기 위함이다.
이에 대한 설명
TypeScript - class-transformer 데이터 타입 변환
nest.js를 사용할때 유용한 패키지 데이터 타입을 변환할때 사용한다 설치 방법 $ npm install class-transformer 예를 들어, 영화의 정보 등을 작성하는 API를 만들었다고 가정하자. body에는 "title", "year", "ge
ctrs.tistory.com
- posts.controller.ts
// 게시글 개수 세기 - 완료
@Get('count')
countPost() {
return this.postsService.countPost();
}
// 전체 글 가져오기
@Get()
getAllPost() {
return this.postsService.getAllPost();
}
// 특정 글 가져오기
@Get(':id')
getSpecificPost(@Param('id') postId: number) {
return this.postsService.getSpecificPost(postId);
}
- posts.service.ts
// 게시글 개수 세기 - 완료
async countPost() {
return await this.postsRepository.count();
}
// 전체 글 가져오기 - 완료
async getAllPost() {
return await this.postsRepository.find({});
}
// 특정 글 가져오기 - 완료
async getSpecificPost(postId: number) {
// 게시글이 존재하는지 확인
const post = await this.postsRepository.findOne({ where: { id: postId } });
if (!post) {
throw new NotFoundException('게시글이 존재하지 않습니다.');
}
return post;
}
3. Update
HTTP 메소드는 PUT이 아닌 PATCH를 사용한다.
- updatePost.request.dto.ts
import { PartialType } from '@nestjs/swagger';
import { PostingRequestDto } from './posting.request.dto';
export class UpdatePostRequestDto extends PartialType(PostingRequestDto) {}
Create에 쓰였던 PostingRequestDto의 내용을 PartialType을 사용하여 상속받았다.
이를 통해 string 타입의 'title', 'content' 속성을 사용하면서, 둘 중 하나의 데이터만 서버에 전송하더라도
에러없이 처리가 가능하다. (PUT이 아닌 PATCH)
- posts.controller.ts
// 글 수정
@Patch(':id')
async updatePost(
@Param('id') postId: number,
@Body() body: UpdatePostRequestDto,
@User() user: Users,
) {
await this.postsService.updatePost(
postId,
body.title,
body.content,
user.id,
);
return `${postId} 게시글이 수정되었습니다.`;
}
- posts.service.ts
// 글 수정
async updatePost(
postId: number,
title: string,
content: string,
userId: number,
) {
// 게시글이 존재하는지 확인
const post = await this.postsRepository.findOne({ where: { id: postId } });
if (!post) {
throw new NotFoundException('게시글이 존재하지 않습니다.');
}
// 현재 로그인한 사용자가 글 작성자인지 확인
if (post.userId !== userId) {
throw new ForbiddenException('수정 권한이 없습니다.');
}
await this.postsRepository.update(postId, {
title: title,
content: content,
});
}
4. Delete
- posts.controller.ts
// 글 삭제
@Delete(':id')
async deletePost(@Param('id') postId: number, @User() user: Users) {
await this.postsService.deletePost(postId, user.id);
return `${postId} 게시글이 삭제되었습니다.`;
}
- posts.service.ts
// 글 삭제
async deletePost(id: number, userId: number) {
// 게시글이 존재하는지 확인
const post = await this.postsRepository.findOne({ where: { id: id } });
if (!post) {
throw new NotFoundException('게시글이 존재하지 않습니다.');
}
// 현재 로그인한 사용자가 글 작성자인지 확인
if (post.userId !== userId) {
throw new ForbiddenException('삭제 권한이 없습니다.');
}
await this.postsRepository.delete(id);
return true;
}
'공부 > node.js' 카테고리의 다른 글
NestJS - Swagger 데코레이더 정리 (0) | 2024.01.09 |
---|---|
NestJS - TypeORM OneToMany, ManyToOne (0) | 2024.01.02 |
NestJS - TypeORM 트랜잭션 (0) | 2023.12.28 |
에러 기록 - passport를 이용하여 로그인 시도 중 발생하는 401 Unauthorized 에러 (0) | 2023.12.22 |
NestJS의 라이프 사이클 (0) | 2023.12.20 |