Nest.js

[Nest.js] 본인의 게시물만 보고 쓰고 삭제하기

Hoo_Dev 2023. 1. 30. 17:01

유저(OneToMany)와 게시물(ManyToOne)의 관계 형성 해주기

 

user.entity.ts

import { type } from 'os';
import { Board } from 'src/boards/board.entity';
import {
  BaseEntity,
  Column,
  Entity,
  OneToMany,
  PrimaryGeneratedColumn,
  Unique,
} from 'typeorm';

@Entity()
@Unique(['username'])
export class User extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  @OneToMany((type) => Board, (board) => board.user, { eager: true })
  boards: Board[];
}

board.entity.ts

import { User } from 'src/auth/user.entity';
import {
  BaseEntity,
  Column,
  Entity,
  ManyToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';
import { BoardStatus } from './board-status.enum';

@Entity()
export class Board extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  description: string;

  @Column()
  status: BoardStatus;

  @ManyToOne((type) => User, (user) => user.boards, { eager: false })
  user: User;
}

유저, 보트 엔티티에 OneToMany, ManyToOne 관계를 설정해준다.

eager → eager옵션을 true로 두게 된다면 상위 엔티티를 로드 했을 때, 그 하위 엔티티까지 모두 로드되게 한다.

 

게시물 생성 할 때 유저 정보 넣어주기

 

boards.controller.ts

@Controller('boards')
@UseGuards(AuthGuard())
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

 
  @Post()
  @UsePipes(ValidationPipe)
  createBoard(
    @Body() createBoardDto: CreateBoardDto,
    @GetUser() user: User,
  ): Promise<Board> {
    return this.boardsService.createBoard(createBoardDto, user);
  }

}

boards.controller 안에 토큰값 안에 들어있는 유저의 정보를 가져와서(커스텀 데코레이터를 이용하여 가져올 수 있다.) 보드를 생성 할 때 유저를 포함해서 생성하게 해준다.

 

boards.service.ts

@Injectable()
export class BoardsService {
  constructor(
    @InjectRepository(BoardRepository)
    private boardRepository: BoardRepository,
  ) {}

  createBoard(createBoardDto: CreateBoardDto, user: User): Promise<Board> {
    return this.boardRepository.createBoard(createBoardDto, user);
  }
}

service 파일로 가서 user 정보를 넣어준다.(레포지토리에 접근하여 넣을 수 있게)

 

board.repository.ts

@Injectable()
export class BoardRepository extends Repository<Board> {
  constructor(private dataSource: DataSource) {
    super(Board, dataSource.createEntityManager());
  }

  async createBoard(
    createBoardDto: CreateBoardDto,
    user: User,
  ): Promise<Board> {
    const { title, description } = createBoardDto;
    const board = this.create({
      title,
      description,
      status: BoardStatus.PUBLIC,
      user,
    });

    await this.save(board);
    return board;
  }
}

이후 repository로 접근해서 DB에 저장될 때 유저의 정보를 포함하여 저장할 수 있게 코드를 수정해준다.

이후 생성 요청을 보내게 되면

{
    "title": "new board(add user)",
    "description": "new description(add user)",
    "status": "PUBLIC",
    "user": {
        "id": 10,
        "username": "GHGHgh",
        "password": "$2a$10$JQYn2m5zYOaybzYUMw8BteEb3TWEkFYILU8sPyw2JtPhLS37jZnPm",
        "boards": []
    },
    "id": 8
}

위와 같이 유저의 정보가 담긴 json이 리턴된다.

실제 DB에는 어떻게 저장이 돼 있는지 확인을 해보면(pgAdmin)

이와 같이 userId를 가진 컬럼이 추가가 된다.

 

해당 유저의 게시물만 가져오기(getAllBoards) - QueryBuilder 사용하기

 

boards.controller.ts

@Controller('boards')
@UseGuards(AuthGuard())
export class BoardsController {
  constructor(private boardsService: BoardsService) {}

  @Get()
  getAllBoard(@GetUser() user: User): Promise<Board[]> {
    return this.boardsService.getAllBoards(user);
  }

}

컨트롤러에서 커스텀 데코레이터를 통해 유저 정보를 가져오고 유저 정보를 service에 넘겨준다.

 

boards.service.ts

@Injectable()
export class BoardsService {
  constructor(
    @InjectRepository(BoardRepository)
    private boardRepository: BoardRepository,
  ) {}

  async getAllBoards(@GetUser() 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;
  }

}

이후 쿼리빌더를 통해 보드 안의 유저 아이디가 넘겨받은 유저의 아이디와 같으면, 해당되는 객체들을 boards 변수 안에 담아서 리턴해준다.

결과(user2가 가진 게시물은 3, 4번 게시물).

[
    {
        "id": 3,
        "title": "board3",
        "description": "board3",
        "status": "PUBLIC"
    },
    {
        "id": 4,
        "title": "board4",
        "description": "board4",
        "status": "PUBLIC"
    }
]

 

자신이 생성한 게시물만 삭제 할 수 있는 기능 구현

방식은 위와 같다(삭제 함수들 안에 user객체와 타입을 넣어준다) 하지만 delete 또한 쿼리셀렉터를 만들어 줘서 해결해야 한다.

https://orkhan.gitbook.io/typeorm/docs/delete-query-builder

 

Delete using Query Builder - typeorm

Alternatively, You can recover the soft deleted rows by using the restore() method:

orkhan.gitbook.io

boards.service.ts

@Injectable()
export class BoardsService {
  constructor(
    @InjectRepository(BoardRepository)
    private boardRepository: BoardRepository,
  ) {}

 
  async deleteBoard(id: number, user: User): Promise<void> {
    const result = await this.boardRepository
      .createQueryBuilder('board')
      .delete()
      .from(Board)
      .where('userId = :userId', { userId: user.id })
      .andWhere('id = :id', { id })
      .execute();

    if (result.affected == 0) {
      throw new NotFoundException(`Can't find Board with id ${id}`);
    }
  }
}

위와 같이 토큰에서 가져온 유저 정보의 id와 생성한 게시물 유저의 id가 일치하고, 해당 게시물의 id와 삭제하고자 하는 게시물의 id가 일치한다면 삭제가 가능하다.

 

결과.(user2의 정보로 board3을 지운 결과)