[Nest.js][TIL] 심화 프로젝트 - 프로젝트 협업 도구 만들기...Guard 만들기

Trippy·2024년 1월 9일
0

Nest.js

목록 보기
12/15
post-thumbnail

심화 팀 프로젝트

개인 프로젝트가 끝나자마자 바로 팀 프로젝트가 시작되었다.
이번 프로젝트는 칸반 보드를 만들 예정이다.

협업을 위한 노션 페이지와, DISCORD를 생성하고 GITHUB 레포지도 새로 생성하고 Nestjs 파일과 typeORM을 연결하고 초기 세팅을 한 채로 만들었다.

와이어프레임

깃 허브 규칙을 정했다.

ERD


프로젝트 초기 세팅을 마치고 내가 맡은 부분인 CARD를 구현하고 있는데,

카드 뿐만 아니라 보드와 컬럼도 에서도 해당 유저가 해당 보드에 "초대된" 유저인지 확인할 수 있는 방법이 필요했다.

그래서 처음에는 각 서비스에서 직접 확인하는 로직을 짜고 있었는데 짜고 보니까 코드가 너무 중복되고 더러워 지는 것 같아서 하나로 깔끔하게 묶어줘서 필요한 곳에서 그때 그때 불러주는 방법은 없을까 하고 생각해보니까 우리가 그동안 JWT할 때 만들어서 썼던 Guard가 있었다.

그래서 가드를 만들고자 했었는데 먼저 nestjs 공식 문서에서 찾아보았다.

NestJS에서는 가드를 만들기 위해서는 @nestjs/common 모듈에서 제공하는 CanActivate라는 인터페이스를 구현하는 클래스를 생성해야 한다. 그리고 canActivate() 메서드를 안에 가드의 로직을 작성할 수 잇다.

예를 들면

import { Injectable, CanActivate } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate() {
    console.log('인증이 성공하였습니다.');
    return true;
  }
}

NestJS는 canActivate()함수가 true또는 promise<true>를 반활했을 때만 해당 요청을 컨트롤러로 전달한다
만약 false가 전달되면 컨트롤러로 넘어가는 것을 차단한다.

// src/auth/guard/board-invitation.guard.ts

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { BoardInvitation } from 'src/board-invitations/entities/board-invitation.entity';
import { Board } from 'src/boards/entities/board.entity';
import { Repository } from 'typeorm';

@Injectable()
export class BoardInvitationGuard implements CanActivate {
  constructor(
    @InjectRepository(BoardInvitation)
    private readonly boardInvitationRepository: Repository<BoardInvitation>,
    @InjectRepository(Board)
    private readonly boardRepository: Repository<Board>,
  ) {}

  // canActivate 메소드는 요청이 허용되는지 여부를 결정하는 역할을 합니다.
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 현재 HTTP 요청을 가져옵니다.
    const request = context.switchToHttp().getRequest();

    // 요청한 사용자의 ID와 보드 ID를 추출합니다.
    const userId = request.user.id;
    const { boardId } = request.params;

    // 요청된 보드가 존재하는지 확인합니다.
    const existingBoard = await this.boardRepository.findOne({
      where: { id: boardId },
    });

    // 보드가 없다면 NotFoundException을 발생시킵니다.
    if (!existingBoard) {
      throw new NotFoundException('보드를 찾을 수 없습니다.');
    }

    // 현재 사용자가 해당 보드에 초대된 사용자인지 확인합니다.
    const existedUser = await this.boardInvitationRepository.findOne({
      where: {
        user: { id: userId },
        status: 'invited',
        board: { id: boardId },
      },
    });

    // 초대된 사용자가 아니라면 UnauthorizedException을 발생시킵니다.
    if (!existedUser) {
      throw new UnauthorizedException('조작할 권한이 없습니다.');
    }

    // 모든 검증이 통과하면 허용합니다.
    return true;
  }
}

해당 가드를 만들고 난 뒤 필요한 컨트롤러에서 가드를 사용하면 된다. 하지만 여기서 중요한 점은 꼭 jwt가드를 통과하고 나서 할 수 있다 왜냐하면 userId를 받아야하기 때문에 jwt가드를 먼저 통과해야 한다.

또한 해당 가드는 BoardInvitationRepositoryBoardRepository를 사용하기 때문에 해당 사용하는 해당 모듈에서는 저 두개의 엔티티를 불러와야 한다.

해당 모듈에서 불러온 모습.

profile
감금 당하고 개발만 하고 싶어요

0개의 댓글