* 퀴즈셋 http api 호출 vs 서비스주입 방식 비교
분석
- Service 주입 방식의 장점:
- 단일 책임 원칙(SRP) 준수
- 테스트 용이성 향상
- 캐싱 전략 구현 가능
- 에러 처리 통합
- 의존성 역전 원칙(DIP) 준수
- HTTP 직접 호출 방식의 단점:
- 코드 중복 가능성
- 테스트 어려움
- 에러 처리 분산
- 캐싱 전략 부재
CTO들의 견해
- Martin Fowler (ThoughtWorks):"서비스 계층 패턴은 비즈니스 로직을 캡슐화하고 재사용성을 높이는 핵심 패턴입니다."
- Sam Newman (Author of Building Microservices):"마이크로서비스 환경에서 서비스 추상화는 시스템 유연성의 핵심입니다."
Best Practices
- 인터페이스 정의
- 의존성 주입 활용
- 캐싱 전략 구현
- 환경 설정 분리
- 에러 처리 통합
추천사항
QuizService를 주입받아 사용하는 방식을 강력히 추천합니다. 이유는:
- 테스트 용이성
- 코드 재사용
- 관심사 분리
- 확장성
- 결론 : 퀴즈서비스를 주입받아 사용하고자 함.
- 문제 : createRoom에서 quizSetId, quizCount를 주지않음. 그래서 하드코딩 되있구나.
- updateRoomQuizset여기서 풀 퀴즈셋을 정하는것 같다.
- 그러면 기본값으로 풀 퀴즈를 정해놓으면 되겠군.
- vs 퀴즈 스타트할때 퀴즈셋아이디, 카운트가 있는지 검사해도 될것같다.
- bug? : setId기본값이 왜 2가 들어가는거지??
- 다시 서버 restart하니까 해결되었다. dev -watch의 버근인듯하다.
* room에 퀴즈셋 title 추가
- 추가 요구사항이 생겼다.
- 퀴즈셋의 title을 여기서 가지고 있어야 한다.
- 값 주입시점은 언제가 좋을까?
- updateRoomQuizSet 여기가 좋겠다. -> quizSet API를 호출하는 startGame 밖에 없는듯.
* redis 캐시 사용
- 기존 : 캐시 확인없이 무조건 API 호출
- 개선 : 캐시가 있으면 캐시활용 ( 로컬캐시 -> 레디스 캐시 -> DB)
import { QuizSetData } from '../../InitDB/InitDB.Service';
import { Injectable, Logger } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import { Redis } from 'ioredis';
import { QuizService } from '../../quiz/quiz.service';
import { mockQuizData } from '../../../test/mocks/quiz-data.mock';
@Injectable()
export class QuizCacheService {
private readonly quizCache = new Map<string, any>();
private readonly logger = new Logger(QuizCacheService.name);
private readonly CACHE_TTL = 1000 * 60 * 30; // 30분
constructor(
@InjectRedis() private readonly redis: Redis,
private readonly quizService: QuizService
) {}
/**
* 캐시키 생성
*/
private getCacheKey(quizSetId: number): string {
return `quizset:${quizSetId}`;
}
/**
* Redis 캐시에서 퀴즈셋 조회
*/
private async getFromRedisCache(quizSetId: number): Promise<QuizSetData | null> {
const cacheKey = this.getCacheKey(quizSetId);
const cachedData = await this.redis.get(cacheKey);
if (cachedData) {
return JSON.parse(cachedData);
}
return null;
}
/**
* Redis 캐시에 퀴즈셋 저장
*/
private async setToRedisCache(quizSetId: number, data: QuizSetData): Promise<void> {
const cacheKey = this.getCacheKey(quizSetId);
await this.redis.set(cacheKey, JSON.stringify(data), 'EX', this.CACHE_TTL);
}
/**
* 퀴즈셋 데이터 조회 (캐시 활용)
*/
async getQuizSet(quizSetId: number) {
// 1. 로컬 메모리 캐시 확인
const localCached = this.quizCache.get(this.getCacheKey(quizSetId));
if (localCached) {
this.logger.debug(`Quiz ${quizSetId} found in local cache`);
return localCached;
}
// 2. Redis 캐시 확인
const redisCached = await this.getFromRedisCache(quizSetId);
if (redisCached) {
this.logger.debug(`Quiz ${quizSetId} found in Redis cache`);
// 로컬 캐시에도 저장
this.quizCache.set(this.getCacheKey(quizSetId), redisCached);
return redisCached;
}
// 3. DB에서 조회
const quizData = quizSetId === -1 ? mockQuizData : await this.quizService.findOne(quizSetId);
// 4. 캐시에 저장
await this.setToRedisCache(quizSetId, quizData);
this.quizCache.set(this.getCacheKey(quizSetId), quizData);
return quizData;
}
/**
* 캐시 무효화
*/
async invalidateCache(quizSetId: number): Promise<void> {
const cacheKey = this.getCacheKey(quizSetId);
this.quizCache.delete(cacheKey);
await this.redis.del(cacheKey);
}
}
- 로컬캐시를 주기적으로 비워주는 알고리즘을 또 만들어야하네? -> 일단 기본 TTL을 제공하는 레디스 캐시만사용
- 퀴즈셋은 자주 변경되지않는데이터 -> 캐시에 적절함.
- redis에는 아래와 같이 저장된다.
- 먼저 로컬캐시를 뒤진후에 그래도 없으면 DB에 요청을한다.
* test code 실행
- e2e test에서 의존성주입 엄청난 에러..
- 원인 : gameService에서 quizCacheService를 사용하고, 이것은 quizService를 사용하고, 이것은 TypeORM을 사용하기 때문으로 추정
- 해결 : TypeORM 등 모든 관련 의존성주입
- test가 안끝나고 대기중인 문제
- 원인 : import에 있던 실제 redis가 안닫히는 걸로 추정
- 해결 : 기존의 test는 beforeALL에서 실제 redis말고 redisMock을 사용하게 되어있음. -> 사용하지않는 실제 redis import를 제거
- 제거후에도 test가 잘된다!
* test code 추가
- 게임 시작시 quizSetTitle이 올바르게 설정되어야 한다
- 캐시에 없는 퀴즈셋의 경우 DB에서 조회하고 캐시에 저장해야 한다.
- 캐시에 있는 퀴즈셋의 경우 DB 조회 없이 캐시에서 가져와야 한다
- 캐시가 만료되면 DB에서 다시 조회해야 한다
'개발일지' 카테고리의 다른 글
24. 11. 20. 개발일지 // redis 메모리 관리 (0) | 2024.11.21 |
---|---|
24. 11. 19. 개발일지 // test-code 의존성 수정, redis 구독 리팩토링, 진행중게임에 들어올수없음 (0) | 2024.11.20 |
24. 11. 17. 개발일지 redis 탐색 (0) | 2024.11.18 |
24.11.14. 발표자료 // n+1문제해결, 변경감지, redis 설계 (0) | 2024.11.14 |
24.11.14. 개발일지 // 자동배포, 깃헙액션에서 db test, ssh 터널링 db 연결 (1) | 2024.11.14 |