댓글을 달면 경험치가 쌓이고 이 값을 활용해서 레벨업을 시켜주는 로직을 구현했다.
처음에는 정말 막막했는데 튜터님 + 팀원분들의 힌트 덕분에 생각보다 오래걸리지 않고 해결할 수 있었다.
supabase에서 제공하는 메소드를 대충 알고 썼는데 이번 기회에 세밀하게 알게된 것 같다.
레벨 업 기능 구현하기 + 약간의 트러블슈팅..? 💥
import { supabase } from "@/supabase/supabase";
// 경험치에 따라 레벨을 반복적으로 업데이트하는 함수
export const updateUserRank = async (userId: string) => {
try {
const { data: userData, error: userError } = await supabase
.from("USER_TABLE")
.select("user_exp, user_rank")
.eq("user_id", userId)
.single();
if (userError || !userData) {
console.error("사용자 데이터 불러오기 실패", userError?.message);
return;
}
let { user_exp, user_rank } = userData;
// 반복적으로 다음 레벨 기준을 가져와 경험치를 업데이트
while (true) {
const { data: rankData, error: rankError } = await supabase
.from("RANK_TABLE")
.select("rank_base")
.order("rank_base", { ascending: true })
.gt("rank_base", user_exp) // 현재 경험치보다 높은 레벨 기준을 가져오기
.limit(1);
if (rankError || !rankData || rankData.length === 0) {
console.log("레벨 기준 데이터 불러오기 실패", rankError?.message);
break; // 다음 레벨이 없으면 종료
}
const nextRankBase = rankData[0].rank_base;
// 경험치가 다음 레벨 기준보다 낮으면 반복 종료
if (user_exp < nextRankBase) {
break;
}
// 다음 레벨 기준을 넘어서면 레벨업
user_rank += 1;
user_exp -= nextRankBase;
}
// 최종적으로 USER_TABLE에 업데이트된 경험치와 레벨을 반영
const { error: updateError } = await supabase
.from("USER_TABLE")
.update({ user_exp, user_rank })
.eq("user_id", userId);
if (updateError) {
console.log("USER_TABLE 업데이트 오류", updateError.message);
} else {
console.log("USER_TABLE 업데이트 성공", { user_exp, user_rank });
}
} catch (error) {
console.error("레벨 업데이트 중 오류 발생:", error);
}
};
난이도 설정하는 코드를 다른 팀원분이 반복문을 사용해서 구현하셨다고 해서 나도 반복문을 써봤다.
while
문을 쓴 이유는 몇 번 반복해야하는지 정해지지 않을 떈 while
문을 쓴다고 들어서 작성해보았다.
랭킹 계산 로직이 전혀 안 되었다… 푸칵칵
랭킹 숫자가 supabase
에 반영이 되지 않고 있었다.
console.log
를 찎어서 확인했을 때 “레벨 기준 데이터 불러오기 실패 undefined” 가 나오고 있었고 비교할 대상이 없으니 당연히 반복문도 실행되지 않았던 것..!
RANK_TABLE
을 기준으로 경험치 값을 비교하고 있었는데 이 부분의 값 설계를 잘못해두었었다..ㅎ
RANK_TABLE
의 rank_base
컬럼이 100,200이 아니라 1,2로 설정되어있어서 발생한 오류였고 이를 고쳐주니 레벨 기준 데이터를 불러오는건 성공했다.
가만 생각해보니 굳이 반복문을 돌릴게 있나? 싶었다.
단순히 데이터 값만 RANK_TABLE
과 비교하면 끝인 문제인데?
다시 생각한 로직
1. USER_TABLE의 user_exp과 RANK_TABLE의 exp 값을 비교한다.
2. USER_TABLE의 user_exp가 RANK_TABLE의 exp 컬럼의 숫자에 도달하면(일치하면)
3. RANK_TABLE의 rank_base의 값이 USER_TABLE의 user_rank에 반영된다.
4. 레벨이 변경되고 브라우저에 보여진다.
rank_base
: 레벨 값 (1, 2, 3 등)exp
: 각 레벨에 도달하는 데 필요한 user_exp
기준 (100, 200, 300 등)import { supabase } from "@/supabase/supabase";
// 경험치에 따라 레벨을 업데이트하는 함수
export const updateUserRank = async (userId: string) => {
try {
const { data: userData, error: userError } = await supabase
.from("USER_TABLE")
.select("user_exp, user_rank")
.eq("user_id", userId)
.single();
if (userError || !userData) {
console.error("사용자 데이터 불러오기 실패", userError?.message);
return;
}
let { user_exp, user_rank } = userData;
// RANK_TABLE에서 현재 경험치에 맞는 레벨 가져오기
const { data: rankData, error: rankError } = await supabase
.from("RANK_TABLE")
.select("rank_base, exp")
.eq("exp", user_exp) // ✅ user_exp와 일치하는 레벨 기준 가져오기
.single();
if (rankError || !rankData) {
console.log("레벨 기준 데이터 불러오기 실패", rankError?.message);
return; // 조건에 맞는 레벨 기준이 없으면 ㅂㅂ
}
const { rank_base } = rankData;
// 현재 레벨과 데이터베이스 레벨 비교 후 업데이트
if (user_rank !== rank_base) {
user_rank = rank_base;
const { error: updateError } = await supabase
.from("USER_TABLE")
.update({ user_rank })
.eq("user_id", userId);
if (updateError) {
console.log("USER_TABLE 업데이트 오류", updateError.message);
} else {
console.log("USER_TABLE 업데이트 성공", { user_exp, user_rank });
}
}
} catch (error) {
console.error("레벨 업데이트 중 오류 발생:", error);
}
};
일단 되긴 하지만..! 정확히 exp
값과 일치하는! 100,200 값이어야만하고 130 같은 값이 있으면 fetch
에러가 생기고 있었다…🫨
ㅇ ㅏ.. 대충 알잘딱해서 비교해줘야지이이읶!!!!!
const { data: rankData, error: rankError } = await supabase
.from("RANK_TABLE")
.select("rank_base, exp")
.lte("exp", user_exp) // user_exp 이하의 exp를 가져오기
.order("exp", { ascending: false }) // exp 기준 내림차순으로 정렬
.limit(1); // 가장 높은 exp 기준 한 개만 가져오기
RANK_TABLE 쿼리 수정:
lte("exp", user_exp)
: user_exp
이하의 경험치를 가진 레벨을 가져오기order("exp", { ascending: false })
: 경험치가 가장 높은 레벨을 먼저 가져오기 위해 내림차순으로 정렬limit(1)
: 상위 한 개의 레벨 기준만 가져오기이렇게 하니까 280 값을 줘도 오류도 나지 않고 바로바로 레벨과 경험치가 브라우저, DB에 반영되었다 👍
order("rank_base", { ascending: true })
:rank_base
컬럼을 오름차순(ascending)으로 정렬하여 가장 낮은 기준부터 높은 기준으로 순서를 정렬
→ 현재 경험치보다 높은 기준 중 가장 낮은 기준을 우선적으로 가져올 수 있게 된다
.gt("rank_base", user_exp)
:현재 user_exp
보다 높은 rank_base
값을 가진 레벨만 가져오도록 설정
→ 현재 경험치를 초과하는 다음 레벨의 기준을 찾아오는 조건
.limit(1)
:조건을 만족하는 다음 레벨 기준 한 개만 가져오도록 제한
rank_base
기준을 오름차순으로 정렬했으므로, 조건에 맞는 가장 낮은 rank_base
가 한 개만 선택
결론 : 공식문서를 아주 아주 아주 꼼꼼히 보고 생각을 잘 하자!!!^^👊🏻👊🏻