[ 내일배움캠프 - Nest.js ] 트렐로 서비스 팀 프로젝트 | Threello - 카드 작업자 할당

sooyoung choi·2024년 1월 10일
0

내일배움캠프

목록 보기
14/19
post-thumbnail

꽤나 잘 진행됐던 이번 프로젝트, 뷰페이지 구현하지 않고 서버로만 보여줬기로 했던터라 백엔드 구현에 더욱 집중했다.

트렐로 작업자 할당

트렐로를 보면 각 카드별로 작업자를 할당할 수 있는데 그 로직을 우리 프로젝트에도 추가해보았다.

ERD 확인

Entity 설정

  • card 안에서 일어나는 일이니 따로 폴더 생성해주지 않고, card 폴더 안에 entity 파일을 추가해주었다.
import {
  Column,
  Entity,
  JoinColumn,
  ManyToOne,
  PrimaryGeneratedColumn,
} from 'typeorm';
import { Card } from './card.entity';
import { IsNumber } from 'class-validator';
import { User } from 'src/user/entities/user.entity';
import { ApiProperty } from '@nestjs/swagger';

@Entity('card_workers')
export class CardWorker {
  @PrimaryGeneratedColumn({ unsigned: true })
  id: number;

  @IsNumber()
  @Column()
  @ApiProperty({ description: '유저 아이디', example: '1' })
  user_id: number;

  // 유저 테이블과 N:1
  @ManyToOne(() => User, (user) => user.cardWorkers, {
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  @JoinColumn({ name: 'user_id' })
  user: User;

  // 카드 테이블과 N:1
  @ManyToOne(() => Card, (card) => card.cardWorkers, {
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  @JoinColumn({ name: 'card_id' })
  card: Card;
}

api

### 
#CREATE WORKER
POST {{host}}/api/7/card/80/worker/create
Authorization: Bearer {{Token}}
Content-Type: application/json

{
  "userIds": [
    { "id": 4 },
    { "id": 5 }
  ]
}

###
# DELETE WORKER
DELETE {{host}}/api/7/card/80/worker/remove/4
Authorization: Bearer {{Token}}

DTO

create worker dto

import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsNotEmpty } from 'class-validator';

export class CreateWorkerDto {
  @ApiProperty({
    description: '작업자 할당입니다.',
    example: '[{"id":1},{"id":2}]',
  })
  @IsNotEmpty()
  @IsArray()
  userIds: { id: number }[];
}

작업자 조회

  • 처음엔 작업자를 바로 할당해주는 방식이었지만, 초대된 멤버인지 확인하는 가드가 생긴 이후로는 아무 유저를 다 할당해줄 수 없으니 초대된 사람들만 할당될 수 있게 조회 로직을 구현해보았다.
async getAllWorkers(boardId: number) {
    return await this.boardMemberRepository.find({
      where: { board: { id: boardId } },
    });
  }

작업자 할당

  • 작업자 조회 로직을 갖고와서 써보자

    고려해야할 사항

    1. 작업자는 초대된 멤버만 할당될 수 있다.
    2. 중복돼서 할당될 수 없다.
async createWorker(
    boardId: number,
    cardId: number,
    createWorkerDto: CreateWorkerDto,
  ) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      // 초대된 멤버인지 체크
      const getWorkers = await this.getAllWorkers(boardId);

      // 초대된 멤버의 아이디 값만 넣어줄 배열 생성
      const invitedWorkerArr = [];
      
      // 할당된 작업자들만 넣어줄 배열 생성
      const createdWorkers = [];
      
      // 초대된 멤버 아이디 값 넣어주기
      getWorkers.forEach((worker) => {
        invitedWorkerArr.push(worker.userId);
      });
      
      const { userIds } = createWorkerDto;

      for (const user of userIds) {
        // 해당 유저가 멤버인지 확인
        if (!invitedWorkerArr.includes(user.id))
          throw new NotFoundException('초대되지 않은 멤버입니다.');

        // 작업자 중복 체크
        const existingWorker = await this.cardWorkerRepository.find({
          where: { user_id: user.id },
        });

        // 중복된 사람 제외 등록
        if (existingWorker.length > 0) {
          throw new Error('중복된 멤버입니다.');
        }
        
        const newWorker = await queryRunner.manager.save(CardWorker, {
          user_id: user.id,
          card: { id: cardId },
        });
        createdWorkers.push(newWorker);
      }
      await queryRunner.commitTransaction();
      return createdWorkers;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      return { status: 404, message: error.message };
    } finally {
      // 사용이 끝난 후에는 항상 queryRunner를 해제
      await queryRunner.release();
    }
  }

작업자 삭제

  • 트렐로는 단순히 작업자를 할당 했다가 삭제하는 과정을 지녔다
async removeWorker(cardId: number, userId: number) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();
    try {
      
      // 해당되는 사용자가 있는 지 확인
      const existingWorker = await this.cardWorkerRepository.findOne({
        where: { user: { id: userId }, card: { id: cardId } },
      });
      
      // 없다면?
      if (!existingWorker)
        throw new NotFoundException('해당되는 사용자가 없습니다.');
      
      // 있으면 삭제하기
      const deleteWorker = await this.cardWorkerRepository.delete({
        user: { id: userId },
      });
      await queryRunner.commitTransaction();
      return deleteWorker;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      return { status: 404, message: error.message };
    } finally {
      // 사용이 끝난 후에는 항상 queryRunner를 해제
      await queryRunner.release();
    }
  }

0개의 댓글