서버가 작동하던 홈페이지였을 때, 사용자의 토큰여부에 따라 작동하던 기능들이 였으나 서버가 작동하지 않아 토큰 여부와 상관없이 작동하도록 임시대체하였습니다.
| api 이름 | url | 대체 여부 | |
|---|---|---|---|
| Pick on/off | /api/pick/{보드게임id} | ✅ | token 기능 생략 |
| MyPick 리스트 | /api/pick | ✅ | token 기능 생략 |
| 추천보드게임 | /api/boardgames/recs | ✅ | token 기능 생략 |
| 오늘의 Pick 순위 | /api/boardgames/today-pick | ❌ | 보드픽 회원들의 pick 데이터를 기반으로 추천하는 기능으로, 현재는 사용자들의 정보를 불러올 수 없어 랜덤데이터로 대체하여 작동 중 |
| 제안 게임 | /api/boardgames/suggestion | ❌ | 보드픽 회원들의 pick 데이터를 기반으로 추천하는 기능으로, 현재는 사용자들의 정보를 불러올 수 없어 랜덤데이터로 대체하여 작동 중 |
| user | /api/user | ❌ | 로그인한 회원의 정보를 조회하는 서버 전용 API로, 현재 프론트엔드에서는 호출이 불가능한 상태 |
사용자가 보드게임을 '마이픽(Mypick)'으로 선택하면 해당 게임의 id를 localStorage에 배열로 저장하고,
다시 불러올 때는 이 배열을 기준으로 boardGameData에서 해당 게임 정보를 필터링하여 보여주는 구조였습니다.
//Pick on/off API 대체 코드
export const togglePick = async (id, token) => {
try {
const picks = JSON.parse(localStorage.getItem("pick")) || [];
const isPicked = picks.includes(id);
const updatedPicks = isPicked
? picks.filter((pickId) => pickId !== id)
: [id, ...picks];
localStorage.setItem("pick", JSON.stringify(updatedPicks));
return updatedPicks;
} catch (error) {
throw error;
}
};
//MyPick API 대체 코드
export const getMyPick = async () => {
try {
const data = JSON.parse(localStorage.getItem("pick")) || [];
const pickedData = boardGameData.filter((game) => data.includes(game.id));
return pickedData;
} catch (error) {
throw error;
}
};
사용자가 저장한 순서와 다르게 게임 id 값 기준으로 정렬되어 리스트가 출력되는 문제가 발생했습니다.
boardGameData는 mock data이기에, 데이터가 배열로 저장된 순서대로 반환되어짐 (API가 아니기 때문에 서버에서 순서를 맞춰주지 않음)filter 는 조건에 해당하는 요소들을 걸러내지만 원본 배열의 순서 반영 ❌⇒includes()를 사용한 filter()는 id 기준의 순서만 반영 (사용자가 저장한 순서는 반영되지 않음)
map(id => find(...)) 을 활용하면
사용자가 선택한 순서를 기준으로 하나씩 게임 내용을 가져오기 때문에 반환하는 배열의 순서가 사용자 선택 순서 배열과 동일해 순서를 유지할 수 있습니다.
export const getMyPick = async () => {
try {
const data = JSON.parse(localStorage.getItem("pick")) || [];
const pickedData = data.map((id) =>
boardGameData.find((game) => game.id === id)
);
return pickedData;
} catch (error) {
throw error;
}
};
filter()는 조건에 맞는 요소를 전부 반환하지만 순서 조정 불가하므로 원하는 순서대로 데이터를 정렬하고 싶다면 map(id => find(...))형식을 활용해야 한다.백엔드에서 제공받았던 API 설명에
현재 pickedData는 보드게임의 id 값만 저장되어 있기 때문에 전체 데이터에서 해당 아이디의 정보를 가져와야합니다.
const pickedGames = boardGameData.filter((game) =>
pickedGameIds.includes(game.id)
);
const categoryScores = {};
pickedGames.forEach((game) => {
game.boardGameCategories.forEach((category) => {
categoryScores[category] = (categoryScores[category] || 0) + 1;
//해당 카테고리가 존재하면 그 값을 가져오고 없으면 0 -> +1 누적
});
});
console.log("Category Scores:", categoryScores);
//Category Scores: {전략게임: 2, 롤플레잉: 1, 카드게임: 1, 기억력: 1, 배팅게임: 1}
reduce()함수 활용
arr.reduce( (acc, cur) => acc + cur, 초기값, );
const scoredGames = boardGameData
.filter((game) => !pickedGameIds.includes(game.id)) //pick되어 있는 보드게임 제외
.map((game) => {
const score = game.boardGameCategories.reduce(
(acc, category) => acc + (categoryScores[category] || 0),
0
);
console.log(`${game.name} → ${score}점`);
return { ...game, score };
});

조건 1 : 점수 높은 순으로 정렬해서 추천
조건 2 : 10개 미만시 랜덤
조건 3 : pick한 보드게임 없을 시 랜덤
조건 2와 조건3이 모두 랜덤 데이터를 반환해야했기에 처음에는 두 조건을 하나로 묶고 싶었지만 문제가 발생하였습니다.
if (!pickedGameIds || pickedGameIds.length === 0 || topGames.length < 10) {
return boardGameData.sort(() => Math.random() - 0.5).slice(0, 10);
}
→ 이유 : 조건이 발생하는 시점과 맥락이 너무 다르기 때문에 같이 작동할 수 없기 때문이다..
if (!pickedGameIds || pickedGameIds.length === 0) {
return boardGameData.sort(() => Math.random() - 0.5).slice(0, 10);
}
const ScoreGameCount = scoredGames.filter((game) => game.score > 0);
if (ScoreGameCount.length >= 10) {
scoredGames.sort((a, b) => b.score - a.score);
let topGames = scoredGames.slice(0, 10);
return topGames;
} else {
return boardGameData.sort(() => Math.random() - 0.5).slice(0, 10);
}
export const getRecsGame = (pickedGameIds) => {
//pick한 게임이 없을 때 랜덤렌더링
if (!pickedGameIds || pickedGameIds.length === 0) {
return boardGameData.sort(() => Math.random() - 0.5).slice(0, 10);
}
// Pick한 게임의 id와 일치하는 게임데이터 불러오기
const pickedGames = boardGameData.filter((game) =>
pickedGameIds.includes(game.id)
);
//1. pick한 게임 카테고리별 점수 누적표
const categoryScores = {};
pickedGames.forEach((game) => {
game.boardGameCategories.forEach((category) => {
categoryScores[category] = (categoryScores[category] || 0) + 1;
});
});
// 2. 점수표를 기준으로 나머지 보드게임들의 점수표
const scoredGames = boardGameData
.filter((game) => !pickedGameIds.includes(game.id))
.map((game) => {
const score = game.boardGameCategories.reduce(
(acc, category) => acc + (categoryScores[category] || 0),
0
);
return { ...game, score };
});
const ScoreGameCount = scoredGames.filter((game) => game.score > 0);
if (ScoreGameCount.length >= 10) {
scoredGames.sort((a, b) => b.score - a.score);
let topGames = scoredGames.slice(0, 10);
return topGames;
} else {
return boardGameData.sort(() => Math.random() - 0.5).slice(0, 10);
}
};
