[NestJS] 인증(Auth) - 권한

soyeon·2023년 4월 14일

Nest

목록 보기
8/10
post-thumbnail

유저에게 게시물 접근 권한 주기

  1. 인증에 관한 모듈을 board 모듈에서 쓸 수 있어야 하기에 board module에서 인증 모듈 imports 하기
import { Module } from '@nestjs/common';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardRepository } from './board.repository';
import { Board } from './board.entity';
import { AuthModule } from 'src/auth/auth.module';

@Module({
  imports: [TypeOrmModule.forFeature([Board]), AuthModule],
  controllers: [BoardsController],
  providers: [BoardsService, BoardRepository],
})
export class BoardsModule {}
  1. UseGuards(AuthGuard())를 이용해서 올바른 토큰을 가지고 요청을 주는지 체크한다. AuthGuard()는 각각 라우트 별로 줄 수 있고 한번에 하나의 컨트롤러안에 들어있는 모든 라우트에 줄 수도 있다.(여기서는 board 컨트롤러 안의 모든 라우트에 적용)
@Controller('boards')
@UseGuards(AuthGuard()) // 모든 게시물 요청에 토큰값으로 유저 정보를 가져온다.
export class BoardsController {
  constructor(private boardsService: BoardsService) {}
  ...

이제부터는 토큰값이 없거나 유효하지 않으면 게시물에 대한 요청을 보낼때 에러가 내려간다.

유저와 게시물 데이터의 관계 형성

관계를 형성하기 위해서는 엔티티에 서로간의 필드를 넣어줘야한다.

한 유저는 여러 개의 게시글을 작성할 수 있고,
한 게시글은 한명의 유저만을 가진다.
위와 같은 관계는 유저에서는 OneToMany, 게시글에서는 ManyToOne 관계를 가진다.

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

@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;
}
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[];
}

각 파라미터는 다음과 같다.
1. Type: 타입 정의
2. InverseSide (접근 경로)
(board) => board.user:
board.user를 통해서 board에서 user를 접근하는 방법을 명시
3. Option
{ eager: true }:
user를 가져올때 board를 가져온다.

게시물 생성 시 유저 정보도 같이 저장하기

  1. 생성하는 board 레포지토리의 메소드 createBoard에서 user 를 받아서 저장하도록 추가한다.
// BoardRepository
async createBoard(
    createBoardDto: CreateBoardDto,
    user: User,
  ): Promise<Board> {
    const { title, description } = createBoardDto;

    const board = this.create({
      title,
      description,
      status: BoardStatus.PUBLIC,
      user, // board.user 를 통해 접근 가능
    });

    await this.save(board);

    return board;
  }
  1. board service와 board controller 에서 유저 정보를 받아와 넘겨주도록 추가하기
// BoardsService
createBoard(createBoardDto: CreateBoardDto, user: User): Promise<Board> {
    return this.boardRepository.createBoard(createBoardDto, user);
  }
  
// BoardsController
@Post()
@UsePipes(ValidationPipe)
createBoard(
  @Body() createBoardDto: CreateBoardDto,
  @GetUser() user: User, // 유저정보를 받아왔다.
): Promise<Board> {
  return this.boardsService.createBoard(createBoardDto, user);
}

이제 게시글을 생성해보면 다음과 같은 쿼리로 처리되는 것을 볼 수 있다.
query: INSERT INTO `board`(`id`, `title`, `description`, `status`, `userId`) VALUES (DEFAULT, ?, ?, ?, ?) -- PARAMETERS: ["new board","new board description","PUBLIC",6]

위에서 마지막에 들어가는 6이라는 데이터는 작성자 유저의 ID값을 의미한다.

해당 유저의 게시글만 가져오기

기존에 모든 게시글을 가져올때 this.boardRepository.find();를 통해서 모든 게시글을 가져왔다.

이번에는 게시글을 가져오는 요청을 보낸 유저의 게시글만 가져오도록 수정해보자.

createQueryBuilder() 사용해서 검색 조건 추가하기

async getAllBoard(user: User): Promise<Board[]> {
    const query = this.boardRepository.createQueryBuilder('board'); // 검색할 객체를 'board'라고 하겠다.
    query.where('board.userId = :userId', { userId: user.id }); // 조건 추가.

    const boards = await query.getMany();
    return boards;
  }

해당 유저의 게시글만 삭제하기

// controller
@Delete('/:id')
  deleteBoard(
    @GetUser() user: User,
    @Param('id', ParseIntPipe) id: number,
  ): Promise<void> {
    return this.boardsService.deleteBoard(id, user);
  }
  
  // service
async deleteBoard(id: number, user: User): Promise<void> {
    const result = await this.boardRepository
      .createQueryBuilder()
      .delete()
      .from(Board)
      .where('id = :id and user = :userId', { id, userId: user.id })
      .execute();

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

원래 const result = await this.boardRepository.delete({id, user}); 로 작성하려고 했는데 어째서인지 user에서 에러가 나더라...

왜 때문인지 모르겠는데 다음에 다시 알아보려구 한다.
이에 대해서 알게 되면 따로 포스팅하도록 하자...

profile
사부작 사부작

0개의 댓글