유저 정보를 캐싱하고, 캐싱된 유저를 조회하는 로직입니다.
const friendsDataMap = {}; // 친구 ID -> 사용자 정보 매핑
const uncachedFriendIds = []; // 캐시에 없는 친구 ID 목록
// 각 친구에 대해 캐시된 정보 확인
for (const friend of friends) {
let cachedUserData;
try {
// 캐시에서 친구 정보 조회
cachedUserData = await UserCacheClient.get(`friendData:${friend.userId}`);
} catch (e) {
logger.error('친구 정보 캐시 조회 중 오류 발생', {
input: friends,
error: e.message,
});
}
if (!cachedUserData) {
// 캐시에 정보가 없으면 나중에 조회할 목록에 추가
uncachedFriendIds.push(friend.userId);
continue;
}
friendsDataMap[friend.userId] = cachedUserData;
}
// 캐시되지 않은 친구 정보를 배치 처리하기 위한 준비
const batches = [];
const batchSize = 100;
for (let i = 0; i < uncachedFriendIds.length; i += batchSize) {
// 배치 크기로 분할
batches.push(uncachedFriendIds.slice(i, i + batchSize));
}
// 캐시에 없는 친구 정보를 데이터베이스에서 조회
let uncachedFriendsData = [];
for (const batch of batches) {
// 일괄 조회로 성능 최적화
const batchQueryResult = await UserDatabase.batchGetItems({
Keys: batch.map((userId) => ({ id: userId })),
// 필요한 속성만 선택적으로 가져오기
ProjectionFields: '#id, nickname, profileUrl, bgImageUrl',
ExpressionAttributeNames: { '#id': 'id' },
});
// 유효한 데이터만 필터링
uncachedFriendsData = uncachedFriendsData.concat(
batchQueryResult.filter((userData) => {
return (userData && userData.nickname);
}),
);
}
// 데이터베이스에서 조회한 정보를 캐시에 저장
for (const friendData of uncachedFriendsData) {
friendsDataMap[friendData.id] = friendData;
try {
// 캐시에 저장하고 TTL 설정
await UserCacheClient.setWithExistingTtl({
key: `friendData:${friendData.id}`,
value: JSON.stringify(friendData),
});
await UserCacheClient.setExpiryIfMissing(
`friendData:${friendData.id}`,
config.friendDataCacheDuration,
);
} catch (e) {
logger.error('친구 정보 캐싱 중 오류', {
input: friends,
error: e.message,
});
}
}
// 최종 결과 구성 - 필요한 정보를 합쳐서 반환
friends = friends.map((f) => {
if (!friendsDataMap[f.userId]) return null;
return {
...friendsDataMap[f.userId],
id: f.userId,
status: f.status,
type: f.type,
};
});
// 유효한 데이터만 필터링하여 최종 결과 반환
friends = friends.filter((f) => f !== null);