프로젝트 일지/Unity

[Unity/TIL] Firebase Cloud Functions - 서버에서 뽑기 로직 돌리기(1)

톰마토 2025. 5. 19. 23:32
728x90

프로젝트 '오집마'에서는 가챠 시스템이 존재한다. 그런데 가챠 로직을 클라에서 돌리고 있었는데, 이는 실제 출시하는 게임에서는 있을 수 없는 일이다! 그래서 우리도 Cloud Functions를 사용하여 서버에서 가챠를 돌리고 클라에서는 값만 받아서 보여주도록 만들었다. 

이미 프로젝트에서 Firebase를 사용하고 있었기 때문에 Firebase Functions를 사용했다. 분명 사용법을 파이어베이스에서도 순서대로 제공하는데 척척 따라가지지 않는다. 그래서 미래에 또 헤맬지 모르는 나를 위해 작성해둠.


준비단계

1. 파베 프로젝트 만들어놓기 & Firebase Functions SDK 추가하기 

2. 콘솔에서 Functions에 들어가서 '안내' 버튼 누르면 아래 화면 나옴

3. 하라는대로 Node.js 설치 > Firebase 도구 설치 

파이어베이스 콘솔의 안내 1

4. 또 하라는대로 하기

인데 여기부터는 그냥 믿고 따라하기가 힘들더라. 그래서 내가 증거(?)를 다 찍어놨다!! 

파이어베이스 콘솔의 안내 2


여기부터 순서대로 보여드릴게요

1. firebase init functions 실행 후 자기 프로젝트에 맞게 선택 (아래 사진 참고) 

CMD(관리자권한으로 실행)에서 프로젝트 루트 위치에서 'firebase init functions' 를 실행하면 된다. 

내가 선택한건 하늘색글씨로 보임

 

2. 함수 작성

나는 Clout Functions를 뭐로 작성할거냐는 질문에 JavaScript를 선택했기 때문에 JS로 함수를 작성했다. 문법을 배운적이 없으므로 로직은 챗GPT와 함께 작성했다. 물론 한 번에 되지 않았고, 여러차례 수정하고 재배포하면서 완성됐다.

const functions = require("firebase-functions");

exports.gachaDrawWithGuarantees = functions.https.onCall((data, context) => {
    const payload = data.data || data;

    const gradeRanges = payload.gradeRanges;
    const drawCount = Number(payload.drawCount) || 10;

    console.log("🔥 gradeRanges:", gradeRanges);
    console.log("🔥 count:", drawCount);

    const weights = [70, 20, 8, 2]; // Normal, Rare, Epic, Legend
    const guaranteeEpicAt = 10;
    const guaranteeLegendAt = 100;

    if (!gradeRanges || gradeRanges.length !== weights.length) {
        throw new functions.https.HttpsError(
            'invalid-argument',
            'gradeRanges의 길이는 고정된 weights와 일치해야 합니다.'
        );
    }

    // 누적 확률 테이블
    const totalWeight = weights.reduce((a, b) => a + b, 0);
    const cumulative = [];
    let acc = 0;
    for (let i = 0; i < weights.length; i++) {
        acc += weights[i];
        cumulative.push({ gradeIndex: i, threshold: acc });
    }

    const drawOne = () => {
        const rand = Math.random() * totalWeight;
        const selected = cumulative.find(e => rand < e.threshold);
        const maxId = gradeRanges[selected.gradeIndex];
        const itemId = Math.floor(Math.random() * maxId);
        return { grade: selected.gradeIndex, id: itemId };
    };

    const results = [];
    let hasEpic = false;
    let hasLegend = false;

    // 일반 뽑기
    for (let i = 0; i < drawCount - 1; i++) {
        const result = drawOne();
        if (result.grade === 2) hasEpic = true;
        if (result.grade === 3) hasLegend = true;
        results.push(result);
    }

    let finalDraw;
    // Epic 보장 (10회 이상이고 에픽 없음)
    if (drawCount >= guaranteeEpicAt && !hasEpic) {
        const maxId = gradeRanges[2];
        const itemId = Math.floor(Math.random() * maxId);
        finalDraw = { grade: 2, id: itemId };
    }
    // Legend 보장 (100회 이상이고 레전드 없음)
    else if (drawCount >= guaranteeLegendAt && !hasLegend) {
        const maxId = gradeRanges[3];
        const itemId = Math.floor(Math.random() * maxId);
        finalDraw = { grade: 3, id: itemId };
    }
    else
    {
        finalDraw = drawOne(); // 일반 뽑기
    }

    results.push(finalDraw);
    return { results };
});

3. 함수 배포

CMD(관리자권한으로 실행)에서 'firebase deploy --only functions'를 실행하면 된다. 

1번에서 ESLint 어쩌구를 사용할거냐했을 때 Yes를 해서그런지 firebase.json 또는 기본 설정상 배포 전에 lint를 하려고 시도하는데, functions/package.json 안에 "lint" 스크립트가 정의되어 있지 않기 때문에 오류가 난다.

deploy 실패 에러

그래서 firebase.json에서 "predeploy" 부분을 제거하고 다시 실행했다.

배포 성공

 

배포가 정상적으로 완료되면 파이어베이스의 콘솔에서 Functions에 작성한 함수가 등록된 것을 볼 수 있다. 

함수 등록됨

일단 배포는 이렇게하면 된거다. !!

아 그리고 함수를 수정하면 재배포해야한다.


가장 어려웠던 것은 디버깅 과정

그런데 이 과정에서 예상한대로 바로 작동하지 않아서 오류 원인 찾고 해결하는 것이 어려웠다. 직접 콘솔로그 찍어서 봤음..

firebase functions:log 명령어로 js에서 찍어둔 로그를 확인할 수 있다. 

콘솔창에서 로그 확인하는 모습

 


2025.06.24 - [프로젝트 일지/Unity] - [Unity/TIL] Firebase Cloud Functions - 서버에서 뽑기 로직 돌리기(2)

 

[Unity/TIL] Firebase Cloud Functions - 서버에서 뽑기 로직 돌리기(2)

2025.05.19 - [프로젝트 일지/Unity] - [Unity/TIL] Firebase Cloud Functions - 서버에서 뽑기 로직 돌리기(1) [Unity/TIL] Firebase Cloud Functions (1) - 함수 배포하기프로젝트 '오집마'에서는 가챠 시스템이 존재한다. 그

ramenkirby.tistory.com

 

728x90