fromis_9/backend/src/services/member.js
caadiq 3f27b1f457 refactor(backend): 19단계 Redis 캐시 확대 - 멤버 목록 캐싱
- utils/cache.js 생성: getOrSet, invalidate, invalidatePattern, cacheKeys
- services/member.js: getAllMembers에 Redis 캐시 적용 (10분 TTL)
- services/member.js: invalidateMemberCache 함수 추가
- routes/members: 캐시 사용 및 수정 시 캐시 무효화

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 16:01:12 +09:00

112 lines
3.1 KiB
JavaScript

/**
* 멤버 서비스
* 멤버 관련 비즈니스 로직
*/
import { getOrSet, invalidate, cacheKeys } from '../utils/cache.js';
/**
* 전체 멤버 목록 조회 (별명 포함, 캐시 적용)
* @param {object} db - 데이터베이스 연결
* @param {object} redis - Redis 클라이언트 (캐시용, 선택적)
* @returns {array} 멤버 목록
*/
export async function getAllMembers(db, redis = null) {
const fetchMembers = async () => {
const [members] = await db.query(`
SELECT
m.id, m.name, m.name_en, m.birth_date, m.instagram, m.image_id, m.is_former,
i.original_url as image_original,
i.medium_url as image_medium,
i.thumb_url as image_thumb
FROM members m
LEFT JOIN images i ON m.image_id = i.id
ORDER BY m.is_former ASC, m.id ASC
`);
// 별명 조회
const [nicknames] = await db.query(
'SELECT member_id, nickname FROM member_nicknames'
);
// 멤버별 별명 매핑
const nicknameMap = {};
for (const n of nicknames) {
if (!nicknameMap[n.member_id]) {
nicknameMap[n.member_id] = [];
}
nicknameMap[n.member_id].push(n.nickname);
}
// 멤버 데이터에 별명 추가
return members.map(m => ({
...m,
nicknames: nicknameMap[m.id] || [],
image_url: m.image_thumb || m.image_medium || m.image_original,
}));
};
// Redis가 있으면 캐시 사용
if (redis) {
return getOrSet(redis, cacheKeys.members, fetchMembers, 600); // 10분 캐시
}
return fetchMembers();
}
/**
* 멤버 캐시 무효화
* @param {object} redis - Redis 클라이언트
*/
export async function invalidateMemberCache(redis) {
await invalidate(redis, cacheKeys.members);
}
/**
* 이름으로 멤버 조회 (별명 포함)
* @param {object} db - 데이터베이스 연결
* @param {string} name - 멤버 이름
* @returns {object|null} 멤버 정보 또는 null
*/
export async function getMemberByName(db, name) {
const [members] = await db.query(`
SELECT
m.id, m.name, m.name_en, m.birth_date, m.instagram, m.image_id, m.is_former,
i.original_url as image_original,
i.medium_url as image_medium,
i.thumb_url as image_thumb
FROM members m
LEFT JOIN images i ON m.image_id = i.id
WHERE m.name = ?
`, [name]);
if (members.length === 0) {
return null;
}
const member = members[0];
// 별명 조회
const [nicknames] = await db.query(
'SELECT nickname FROM member_nicknames WHERE member_id = ?',
[member.id]
);
return {
...member,
nicknames: nicknames.map(n => n.nickname),
image_url: member.image_original || member.image_medium || member.image_thumb,
};
}
/**
* ID로 멤버 기본 정보 조회 (수정용)
* @param {object} db - 데이터베이스 연결
* @param {string} name - 멤버 이름
* @returns {object|null} 멤버 기본 정보 또는 null
*/
export async function getMemberBasicByName(db, name) {
const [members] = await db.query(
'SELECT id, image_id FROM members WHERE name = ?',
[name]
);
return members.length > 0 ? members[0] : null;
}