8. Combining with Boards & Auth module
1. 인증된 유저만 게시물을 보고 쓸 수 있게 만들기
이번에는 인증된 유저만 게시물을 보고 쓸 수 있게 구현해 보자. Auth
모듈을 Boards
모듈에서 사용할 수 있어야 하기 때문에 Boards
모듈의 imports
배열에 넣어준다. 이렇게 하면 Auth
모듈의 exports
배열에 있는 모든 것을 사용할 수 있게 된다.
src/boards/boards.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { AuthModule } from "src/auth/auth.module";
import { Board } from "./board.entity";
import { BoardsController } from "./boards.controller";
import { BoardsService } from "./boards.service";
@Module({
imports: [TypeOrmModule.forFeature([Board]), AuthModule],
controllers: [BoardsController],
providers: [BoardsService],
})
export class BoardsModule {}
컨트롤러 레벨로 @UseGuards
데코레이터를 달아준다.
src/auth/auth.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
UseGuards,
UsePipes,
ValidationPipe,
} from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";
import { BoardStatusValidationPipe } from "./pipes/board-status-validation.pipe";
@Controller("boards")
@UseGuards(AuthGuard())
export class BoardsController {
constructor(private boardsService: BoardsService) {}
@Get("/")
getAllBoards(): Promise<Board[]> {
return this.boardsService.getAllBoards();
}
@Post("/")
@UsePipes(ValidationPipe)
createBoard(@Body() createBoardDto: CreateBoardDto): Promise<Board> {
return this.boardsService.createBoard(createBoardDto);
}
@Get("/:id")
getBoardById(@Param("id", ParseIntPipe) id: number): Promise<Board> {
return this.boardsService.getBoardById(id);
}
@Delete("/:id")
deleteBoard(@Param("id", ParseIntPipe) id: number): Promise<void> {
return this.boardsService.deleteBoard(id);
}
@Patch("/:id/status")
updateBoardStatus(
@Param("id", ParseIntPipe) id: number,
@Body("status", BoardStatusValidationPipe) status: BoardStatus
): Promise<Board> {
return this.boardsService.updateBoardStatus(id, status);
}
}
2. 유저와 게시물의 관계 형성하기
현재는 유저나 게시물을 생성할 때 이 둘의 관계에 대해 설정해 주지 않았다. 게시물을 생성할 때도 어떤 유저가 생성해 줬는지에 대한 정보를 같이 넣어줘야 한다.
먼저 User
엔터티에 관계를 추가한다.
src/auth/user.entity.ts
import { Board } from "src/boards/board.entity";
import {
Column,
Entity,
OneToMany,
PrimaryGeneratedColumn,
Unique,
} from "typeorm";
@Entity()
@Unique(["username"])
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
@OneToMany((type) => Board, (board) => board.user, { eager: true })
boards: Board[];
}
반대로 Board
엔터티에 관계를 추가한다.
src/boards/board.entity.ts
import { User } from "src/auth/user.entity";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { BoardStatus } from "./board-status.enum";
@Entity()
export class Board {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
status: BoardStatus;
@ManyToOne((type) => User, (user) => user.boards, { eager: false })
user: User;
}
3. 게시물을 생성할 때 유저 정보 넣어주기
게시물 생성 요청이 오면 headers
안에 있는 토큰으로 유저 정보를 확인하고, 게시물을 생성할 때 이 유저 정보를 넣어줄 수 있도록 구현해 보자.
import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "src/auth/user.entity";
import { Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(Board) private boardRepository: Repository<Board>
) {}
async getAllBoards(): Promise<Board[]> {
const boards = await this.boardRepository.find();
return boards;
}
async createBoard(
createBoardDto: CreateBoardDto,
user: User
): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.boardRepository.create({
title,
description,
status: BoardStatus.PUBLIC,
user,
});
await this.boardRepository.save(board);
return board;
}
async getBoardById(id: number): Promise<Board> {
const found = await this.boardRepository.findOneBy({ id });
if (!found) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
async deleteBoard(id: number): Promise<void> {
const result = await this.boardRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
}
async updateBoardStatus(id: number, status: BoardStatus): Promise<Board> {
const board = await this.getBoardById(id);
board.status = status;
await this.boardRepository.save(board);
return board;
}
}
import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
UseGuards,
UsePipes,
ValidationPipe,
} from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/user.entity";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";
import { BoardStatusValidationPipe } from "./pipes/board-status-validation.pipe";
@Controller("boards")
@UseGuards(AuthGuard())
export class BoardsController {
constructor(private boardsService: BoardsService) {}
@Get("/")
getAllBoards(): Promise<Board[]> {
return this.boardsService.getAllBoards();
}
@Post("/")
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoardDto: CreateBoardDto,
@GetUser() user: User
): Promise<Board> {
return this.boardsService.createBoard(createBoardDto, user);
}
@Get("/:id")
getBoardById(@Param("id", ParseIntPipe) id: number): Promise<Board> {
return this.boardsService.getBoardById(id);
}
@Delete("/:id")
deleteBoard(@Param("id", ParseIntPipe) id: number): Promise<void> {
return this.boardsService.deleteBoard(id);
}
@Patch("/:id/status")
updateBoardStatus(
@Param("id", ParseIntPipe) id: number,
@Body("status", BoardStatusValidationPipe) status: BoardStatus
): Promise<Board> {
return this.boardsService.updateBoardStatus(id, status);
}
}
4. 해당 유저의 게시물만 가져오기
현재는 게시물을 가져올 때 어떠한 유저인지에 상관없이 모든 게시물을 가져온다. 해당 유저의 게시물만 가져올 수 있도록 구현해 보자.
import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "src/auth/user.entity";
import { Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(Board) private boardRepository: Repository<Board>
) {}
async getAllBoards(user: User): Promise<Board[]> {
const query = this.boardRepository.createQueryBuilder("board");
query.where("board.userId = :userId", { userId: user.id });
const boards = await query.getMany();
return boards;
}
async createBoard(
createBoardDto: CreateBoardDto,
user: User
): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.boardRepository.create({
title,
description,
status: BoardStatus.PUBLIC,
user,
});
await this.boardRepository.save(board);
return board;
}
async getBoardById(id: number): Promise<Board> {
const found = await this.boardRepository.findOneBy({ id });
if (!found) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
async deleteBoard(id: number): Promise<void> {
const result = await this.boardRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
}
async updateBoardStatus(id: number, status: BoardStatus): Promise<Board> {
const board = await this.getBoardById(id);
board.status = status;
await this.boardRepository.save(board);
return board;
}
}
import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
UseGuards,
UsePipes,
ValidationPipe,
} from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/user.entity";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";
import { BoardStatusValidationPipe } from "./pipes/board-status-validation.pipe";
@Controller("boards")
@UseGuards(AuthGuard())
export class BoardsController {
constructor(private boardsService: BoardsService) {}
@Get("/")
getAllBoards(@GetUser() user: User): Promise<Board[]> {
return this.boardsService.getAllBoards(user);
}
@Post("/")
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoardDto: CreateBoardDto,
@GetUser() user: User
): Promise<Board> {
return this.boardsService.createBoard(createBoardDto, user);
}
@Get("/:id")
getBoardById(@Param("id", ParseIntPipe) id: number): Promise<Board> {
return this.boardsService.getBoardById(id);
}
@Delete("/:id")
deleteBoard(@Param("id", ParseIntPipe) id: number): Promise<void> {
return this.boardsService.deleteBoard(id);
}
@Patch("/:id/status")
updateBoardStatus(
@Param("id", ParseIntPipe) id: number,
@Body("status", BoardStatusValidationPipe) status: BoardStatus
): Promise<Board> {
return this.boardsService.updateBoardStatus(id, status);
}
}
5. 자신이 생성한 게시물 삭제하기
자신이 생성한 게시물만을 삭제할 수 있게 구현해 보자.
import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "src/auth/user.entity";
import { Repository } from "typeorm";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { CreateBoardDto } from "./dto/create-board.dto";
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(Board) private boardRepository: Repository<Board>
) {}
async getAllBoards(user: User): Promise<Board[]> {
const query = this.boardRepository.createQueryBuilder("board");
query.where("board.userId = :userId", { userId: user.id });
const boards = await query.getMany();
return boards;
}
async createBoard(
createBoardDto: CreateBoardDto,
user: User
): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.boardRepository.create({
title,
description,
status: BoardStatus.PUBLIC,
user,
});
await this.boardRepository.save(board);
return board;
}
async getBoardById(id: number): Promise<Board> {
const found = await this.boardRepository.findOneBy({ id });
if (!found) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
return found;
}
async deleteBoard(id: number, user: User): Promise<void> {
const result = await this.boardRepository.delete({ id, user });
if (result.affected === 0) {
throw new NotFoundException(`Can't find Board with id ${id}`);
}
}
async updateBoardStatus(id: number, status: BoardStatus): Promise<Board> {
const board = await this.getBoardById(id);
board.status = status;
await this.boardRepository.save(board);
return board;
}
}
import {
Body,
Controller,
Delete,
Get,
Param,
ParseIntPipe,
Patch,
Post,
UseGuards,
UsePipes,
ValidationPipe,
} from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/user.entity";
import { BoardStatus } from "./board-status.enum";
import { Board } from "./board.entity";
import { BoardsService } from "./boards.service";
import { CreateBoardDto } from "./dto/create-board.dto";
import { BoardStatusValidationPipe } from "./pipes/board-status-validation.pipe";
@Controller("boards")
@UseGuards(AuthGuard())
export class BoardsController {
constructor(private boardsService: BoardsService) {}
@Get("/")
getAllBoards(@GetUser() user: User): Promise<Board[]> {
return this.boardsService.getAllBoards(user);
}
@Post("/")
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoardDto: CreateBoardDto,
@GetUser() user: User
): Promise<Board> {
return this.boardsService.createBoard(createBoardDto, user);
}
@Get("/:id")
getBoardById(@Param("id", ParseIntPipe) id: number): Promise<Board> {
return this.boardsService.getBoardById(id);
}
@Delete("/:id")
deleteBoard(
@Param("id", ParseIntPipe) id: number,
@GetUser() user: User
): Promise<void> {
return this.boardsService.deleteBoard(id, user);
}
@Patch("/:id/status")
updateBoardStatus(
@Param("id", ParseIntPipe) id: number,
@Body("status", BoardStatusValidationPipe) status: BoardStatus
): Promise<Board> {
return this.boardsService.updateBoardStatus(id, status);
}
}