2024-09-25 CH3 풋살 온라인 팀 프로젝트 6 최종 프로젝트 제출

MOON·2024년 9월 26일
0

내일배움캠프 과제

목록 보기
13/36

후 어찌저찌 최종적으로 팀프로젝트를 마무리하면서 깃 허브에 팀원들이 했던 작업을 합치는 과정을 겨처 프로젝트를 테스트해보고 또 수정하고 테스트하고를 반복하여 제출에 성공하였습니다.

마무리 하면서 일어난 내용을 정리해보겠습니다.

최종 ERD 테이블 구조입니다.

테스트 중 문제

테스트 과정에서 선수 선발 등록 API가 약간 애매모하다라는 점이 좀 있다는 의견이 있었습니다. 선수 선발 등록 API를 실행 할때 새로운 팀ID를 params로 받으면 새롭게 팀을 생성하고 선수가 선발 등록이 되게 구현하였습니다.

그래서 발생한 문제는 서순에 뭔가 잘못 기획이 된것이였습니다.

하..근데 제출하기 하루전 밤에 모든기능들을 테스트하면서 이상함을 다같이 느꼈고 그래서 수정을 해야 됐었습니다. 고민중에 아 그럼 그냥 API를 추가해서 현재 기능을 나누는 것을 선택하였습니다. 팀을 먼저 생성하고 -> 해당 팀이 없으면 선수등록X, 있으면 선수 등록O 이러한 형태 수정하여 다시 구현하면서, 추가로 팀 목록 조회 API, 팀삭제 API, 후보등록 API 수정 등의 구현 작업을 빠르게 진행하였습니다.

팀 생성 API

router.post("/team", authMiddleware, async (req, res, next) => {
  try {
    const userId = req.user.userId;

    // 한 계정 당 3개의 팀만을 가집니다. 3팀이 넘어가면 에러를 처리합니다.
    let isTeamsCountCheck = await userPrisma.teams.findMany({
      where: {
        userId: +userId,
      },
    });
    if (Object.keys(isTeamsCountCheck).length >= 3)
      return res.status(400).json({
        message: "이미 3개의 팀을 가지고 있습니다.",
        yourTeamId1: isTeamsCountCheck[0].teamId,
        yourTeamId2: isTeamsCountCheck[1].teamId,
        yourTeamId3: isTeamsCountCheck[2].teamId,
      });

    // 팀 생성
    const team = await userPrisma.teams.create({
      data: {
        userId: userId,
      },
    });

    // 다시 조회
    isTeamsCountCheck = await userPrisma.teams.findMany({
      where: {
        userId: +userId,
      },
    });

    return res.status(201).json({
      message: `${team.teamId}팀이 새로 등록되었습니다.`,
      yourTeamId1: isTeamsCountCheck[0]
        ? isTeamsCountCheck[0].teamId
        : "비어있음",
      yourTeamId2: isTeamsCountCheck[1]
        ? isTeamsCountCheck[1].teamId
        : "비어있음",
      yourTeamId3: isTeamsCountCheck[2]
        ? isTeamsCountCheck[2].teamId
        : "비어있음",
    });
  } catch (err) {
    next(err);
  }
});

선수 선발 등록 API

router.post("/team/:teamId", authMiddleware, async (req, res, next) => {
  try {
    const userId = req.user.userId;
    const { teamId } = await teamIdParamValidate(req.params);
    const { userPlayerId, position } = await teamInfoBodyValidate(req.body);
    // userPlayerId는 유저가 가지고 있는 선수 등록할 userPlayerId를 요청받습니다.
    // position는 team컬럼 중 userPlayerId1, userPlayerId2, userPlayerId3 등 어느 위치(실제 축구라면 포지션)에 등록할 것인지를 요청받습니다.

    let team = await userPrisma.teams.findFirst({
      where: {
        teamId: +teamId,
      },
    });
    // 팀이 없으면 새로 팀을 생성하고 선발할 선수를 해당 자리에 등록합니다.
    if (!team) throw new NotFoundError("해당 팀이 존재하지 않습니다.");

    const userPlayer = await userPrisma.userPlayers.findFirst({
      where: {
        userPlayerId: userPlayerId,
      },
    });
    if (!userPlayer || userPlayer.count <= 0)
      throw new BadRequestError("현재 소유하고 있는 선수가 아닙니다.");
    if (+userId !== userPlayer.userId)
      throw new ConflictError("현재 계정의 선수가 아닙니다.");

    const player = await gamePrisma.players.findFirst({
      where: { playerId: userPlayer.playerId },
    });
    if (!player) throw new NotFoundError("해당 선수 정보가 없습니다.");

    // 현재 계정의 팀이 아닐때 현재 계정이 가지고 있는 팀을 응답으로 보여줍니다.
    if (+userId !== team.userId) {
      const isTeamCountCheck = await userPrisma.teams.findMany({
        where: {
          userId: +userId,
        },
      });
      return res.status(409).json({
        message: "현재 계정의 팀이 아닙니다.",
        yourTeamId1: isTeamCountCheck[0]
          ? isTeamCountCheck[0].teamId
          : "비어있음",
        yourTeamId2: isTeamCountCheck[1]
          ? isTeamCountCheck[1].teamId
          : "비어있음",
        yourTeamId3: isTeamCountCheck[2]
          ? isTeamCountCheck[2].teamId
          : "비어있음",
      });
    }

    if (team[`userPlayerId${position}`] === userPlayerId) {
      throw new ConflictError("해당 자리에 같은 선수가 이미 있습니다.");
    }

    // 해당 userPlayerId 선수가 이미 팀에 존재하고 선발 위치만을 변경해 주기위한 코드들입니다. 그리고 선발할 위치에 다른 선수가 있으면 다른 선수의 userPlayerId를 서로 위치를 변경하기 위해 작성해 보았습니다.
    let originPosition = null; // 선발할려던 userPlayerId가 이미 존재하여 위치를 알기위해 사용합니다. 그래야 이 위치에 다른 userPlayerId를 넣어주기 위해서 입니다.
    let otherUserPlayerId = null; // 선발할 위치에 있던 다른 선수의 userPlayerId를 가집니다.
    for (let i = 1; i < 4; i++) {
      // 선발 선수가 이미 존재하는 위치를 찾아서 지정합니다.
      if (userPlayerId === team[`userPlayerId${i}`]) {
        originPosition = i;
        break;
      }
    }
    // 해당 선발할려는 위치에 선수가 존재하면 그 선수의 userPlayerId를 가져옵니다.
    if (team[`userPlayerId${position}`]) {
      otherUserPlayerId = team[`userPlayerId${position}`];
    }
    // 다른 선수의 위치를 원래 선발할려는 선수의 위치를 변경해 줍니다.
    if (originPosition) {
      await userPrisma.teams.update({
        data: {
          [`userPlayerId${originPosition}`]: otherUserPlayerId,
        },
        where: { teamId: +teamId },
      });
    }

    // 해당 자리에 선수 선발 등록
    await userPrisma.teams.update({
      data: {
        [`userPlayerId${position}`]: userPlayerId,
      },
      where: { teamId: +teamId },
    });

    return res.status(201).json({
      message: `${player.name} 선수를 ${team.teamId}번 팀에 등록했습니다.`,
    });
  } catch (err) {
    next(err);
  }
});

팀 목록 조회 API

추가한 API 입니다.

  • 유저가 갖고있는 팀ID를 조회합니다.
router.get("/team", authMiddleware, async (req, res, next) => {
  try {
    const userId = req.user.userId;

    const isTeamsCountCheck = await userPrisma.teams.findMany({
      where: {
        userId: +userId,
      },
    });

    return res.status(200).json({
      yourTeamId1: isTeamsCountCheck[0]
        ? isTeamsCountCheck[0].teamId
        : "비어있음",
      yourTeamId2: isTeamsCountCheck[1]
        ? isTeamsCountCheck[1].teamId
        : "비어있음",
      yourTeamId3: isTeamsCountCheck[2]
        ? isTeamsCountCheck[2].teamId
        : "비어있음",
    });
  } catch (err) {
    next(err);
  }
});

음 하다 보니깐 후보 등록 API도 유저의 팀에서 선수를 전부 후보등록하고 팀에 아무런 선수가 없다면 자동으로 팀레코드를 삭제하는 형식으로 했는데 이것도 나누어야 될 것 같아 팀 삭제 API를 만들고 후보 등록 API는 수정해 보았습니다.

후보 등록 API

router.patch("/team/:teamId", authMiddleware, async (req, res, next) => {
  try {
    const userId = req.user.userId;
    const { teamId } = await teamIdParamValidate(req.params);
    const { position } = await positionBodyValidate(req.body);

    const isTeam = await userPrisma.teams.findFirst({
      where: { teamId: +teamId },
    });
    if (!isTeam) throw new NotFoundError("팀이 존재 하지 않습니다.");

    // 현재 계정의 팀이 아닐때 현재 계정이 가지고 있는 팀을 응답으로 보여줍니다.
    if (+userId !== isTeam.userId) {
      const isTeamCountCheck = await userPrisma.teams.findMany({
        where: {
          userId: +userId,
        },
      });
      return res.status(409).json({
        message: "현재 계정의 팀이 아닙니다.",
        yourTeamId1: isTeamCountCheck[0]
          ? isTeamCountCheck[0].teamId
          : "비어있음",
        yourTeamId2: isTeamCountCheck[1]
          ? isTeamCountCheck[1].teamId
          : "비어있음",
        yourTeamId3: isTeamCountCheck[2]
          ? isTeamCountCheck[2].teamId
          : "비어있음",
      });
    }
    if (!isTeam[`userPlayerId${position}`])
      throw new BadRequestError("해당위치에 선수가 존재하지 않습니다.");

    const userPlayer = await userPrisma.userPlayers.findFirst({
      where: { userPlayerId: isTeam[`userPlayerId${position}`] },
    });
    if (!userPlayer)
      throw new BadRequestError("현재 소유하고 있는 선수가 아닙니다.");

    const player = await gamePrisma.players.findFirst({
      where: { playerId: userPlayer.playerId },
    });
    if (!player) throw new NotFoundError("해당 선수 정보가 없습니다.");

    // 팀 위치의 userPlayerId를 null로 변경 후보로 변경
    await userPrisma.teams.update({
      data: {
        [`userPlayerId${position}`]: null,
      },
      where: { teamId: +teamId },
    });

    // // 변경된 팀의 선수 명단을 확인합니다.
    // const renewalTeamUserPlayerIds = await userPrisma.teams.findFirst({
    //   select: {
    //     userPlayerId1: true,
    //     userPlayerId2: true,
    //     userPlayerId3: true,
    //   },
    //   where: { teamId: +teamId },
    // });

    // const userPlayerIds = Object.values(renewalTeamUserPlayerIds);

    // let isUserPlayerIdCheck = false;
    // for (let i = 0; i < userPlayerIds.length; i++) {
    //   // 팀에 선수 한명이라도 있으면 isUserPlayerIdCheck = true;
    //   if (userPlayerIds[i]) {
    //     isUserPlayerIdCheck = true;
    //     break;
    //   }
    // }

    // // 모든 위치에 선수가 없으면 팀 레코드 삭제
    // if (!isUserPlayerIdCheck) {
    //   await userPrisma.teams.delete({
    //     where: { teamId: +teamId },
    //   });
    // }

    return res
      .status(200)
      .json({ message: `${player.name} 선수를 후보로 등록 되었습니다.` });
  } catch (err) {
    next(err);
  }
});

팀 삭제 API

router.delete("/team/:teamId", authMiddleware, async (req, res, next) => {
  try {
    const userId = req.user.userId;
    const { teamId } = await teamIdParamValidate(req.params);

    const team = await userPrisma.teams.findFirst({
      where: { teamId: +teamId },
    });
    if (!team) throw new NotFoundError("팀이 존재하지 않습니다.");
    if (team.userId !== userId) {
      const isTeamCountCheck = await userPrisma.teams.findMany({
        where: { userId: +userId },
      });
      return res.status(409).json({
        message: "현재 계정의 팀이 아닙니다.",
        yourTeamId1: isTeamCountCheck[0]
          ? isTeamCountCheck[0].teamId
          : "비어있음",
        yourTeamId2: isTeamCountCheck[1]
          ? isTeamCountCheck[1].teamId
          : "비어있음",
        yourTeamId3: isTeamCountCheck[2]
          ? isTeamCountCheck[2].teamId
          : "비어있음",
      });
    }

    await userPrisma.teams.delete({
      where: { teamId: +teamId },
    });

    return res.status(200).json({ message: `${teamId}팀이 삭제 되었습니다.` });
  } catch (err) {
    next(err);
  }
});

다시 한번 생각해 보니깐 그냥 함수로 묶어서 관리를 했다면 좀 더 깔끔하고 읽기 편했을거란 생각이 드네요. 이번에 여러 튜터님들의 피드백이야기도 들어보면서 여러 파일을 만들어 따로 함수를 관리하는 형식으로 하는게 좋다고 하시더라고요. 그래서 다음 프로젝트에선 그렇게 해봐야겠다고 생각했습니다.

팀프로젝트 회고 & 느낀점

휴 우여곡절이 조금씩 있었지만 잠을 많이 줄여가면서 하니 어찌저찌 끝이났습니다. 굿~ 이번 팀원분들이 맡은신 업무에 대해 책임감 있게 해주셔서 서로서로 힘이되면서 좋은 경험이 되었고 잊지못할 팀원들도 있을 것 같네요 마지막에 다들 종종 연락하기로 마무리하면서 좋은 팀프로젝트를 경험 한것 같아 좋았습니다.

발표자료 준비하는 것도 생각이상으로 오래 걸리면서 아까 코드 수정과 더불어 제출전날은 그냥 밤을 새버렸습니다. 다음엔 미리 프로젝트를 끝내고 발표준비하는 것도 빠르게 시작해야 겠다는 생각도 했습니다.
그럼 지금까지 수고했고 오늘은 푹쉬고 일찍 잘려고 합니다. 화이팅~

API 명세서
https://documenter.getpostman.com/view/38272046/2sAXqnf4zT

깃허브 주소
https://github.com/Moonb7/OnlineFutsalProject

프로젝트 시연 영상
https://youtu.be/VfDu9MlhwWo

profile
안녕하세요

0개의 댓글