내일배움캠프 TIL

본캠프 12/05 TIL

parkcw0325 2024. 12. 5. 19:25

오늘 구현한 풋살온라인 프로젝트 API

 

오늘은 사용자들이 특정 비용을 지불하고 카드를 뽑을 수 있는 API를 작성하였다

단일 뽑기와, 5장 뽑기 두 가지 API를 구현하였다

 

단일뽑기 API 

사용자는 500 골드를 소모하여 랜덤으로 카드를 뽑는다

뽑은 카드는 UserCard 테이블에 저장되며, 유저의 cash는 자동으로 차감된다.

확률 

노말 70%

레어 25%

에픽 4.5%

레전더리 0.5%

 

router.post('/users/:user_id/cards', authMidWare, async (req, res, next) => {
  const userId = req.params.user_id; // URL 경로에서 user_id 가져오기
  const gachaCost = 500; // 가챠 비용

  try {
    await stringSchema.validateAsync(req.user.id);
    // 사용자 정보 조회
    const user = await prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      return res.status(404).json({ message: '로그인 후 이용해주세요.' });
    }

    // 가챠 비용 확인
    if (user.cash < gachaCost) {
      return res
        .status(400)
        .json({ message: '뽑기를 하기 위한 골드가 부족합니다.' });
    }

    // 랜덤 숫자 생성 (0~100 사이)
    const random = Math.random() * 100; // 0.0 ~ 100.0
    let selectedGrade;

    // 등급 확률에 따른 결정
    if (random < 70) {
      selectedGrade = 'NORMAL'; // 70%
    } else if (random < 95) {
      selectedGrade = 'RARE'; // 25% (70 ~ 95)
    } else if (random < 99.5) {
      selectedGrade = 'EPIC'; // 4.5% (95 ~ 99.5)
    } else {
      selectedGrade = 'LEGENDARY'; // 0.5% (99.5 ~ 100)
    }

    // 해당 등급의 카드 중 랜덤 선택
    const cardsByGrade = await prisma.card.findMany({
      where: { grade: selectedGrade },
    });

    const randomCard =
      cardsByGrade[Math.floor(Math.random() * cardsByGrade.length)];

    // UserCard 테이블에 카드 추가
    const userCard = await prisma.userCard.create({
      data: {
        user_id: userId,
        card_idx: randomCard.idx,
      },
    });

    // 유저 캐시 차감//
    await prisma.user.update({
      where: { id: userId },
      data: { cash: user.cash - gachaCost },
    })

    // 가챠 결과 반환
    return res.status(200).json({
      message: '선수카드를 뽑았습니다!',
      data: {
        grade: selectedGrade,
        card: randomCard,
      },
    });
  } catch (error) {
    next(error);
  }
});

 

5번 뽑기 API 

한 번에 5장의 카드를 뽑는 기능으로. 각 카드당 400 골드로 할인되여, 총 2000골드가 소모된다.

5번의 뽑기 과정을 반복하여 결과로 배열을 반환한다,

 

 

느낀 점과 개선할 점

API 작성 과정에서 확률 분배와 DB 작업이 결합된 로직을 구현하는 것은 재밌으면서도 도전적이었다.

특히 가챠 시스템은 사용자 경험에 큰 영향을 미치는 만큼 확률의 설계와 밸런스 조정이 중요하다고 느꼈다

 

유효하지 않은 사용자 요청(잔액 부족, 로그인 필요 등)에 대한 예외 처리 코드를 꼼꼼히 작성했다.

try-catch 블록으로 오류를 잡고 사용자에게 적절한 에러 메시지를 반환하는 로직을 강화했다.

 

단일 뽑기와 5번 뽑기 로직을 통합하거나, 공통 기능을 다른 함수로 분리하여 중복 코드를 줄이면 좋겠다는 생각이 들었다.