
| 구성 요소 | 파일 위치 | 역할 |
|---|---|---|
| View | ai/views/review_summary_view.py | 클라이언트의 요약 요청을 받아 서비스를 호출 |
| Service | ai/services/review_summary_service.py | 요약 생성 여부 판단, 리뷰 필터링, AI 호출 등 핵심 로직 수행 |
| Signal | ai/signals/review_summary.py | 리뷰가 저장될 때마다 요약 갱신 조건을 체크하여 백그라운드 작업 실행 |
| Task | ai/tasks/review_summary.py | Celery를 이용한 비동기 백그라운드 요약 생성 |
| Model | ai/models/game_review_summary.py | 생성된 요약 데이터를 DB에 저장 (JSON 형태) |
| Utils | ai/utils.py | 욕설 필터링 및 리뷰 유효성 검사 |
_update_and_parse )| 구성 요소 | 파일 위치 | 역할 |
|---|---|---|
| View | ai/views/user_tendency_view.py | 성향 분석 요청 API. 분석 중일 경우 '진행 중' 상태 반환 |
| Service | ai/services/user_tendency_service.py | 캐시를 이용한 중복 요청 방지 및 AI 분석 로직 관리 |
| Task | ai/tasks/user_tendency.py | Celery를 이용한 비동기 성향 분석 실행 |
| Model | ai/models/user_tendency.py | 분석된 성향 문구를 유저와 1:1 매핑하여 저장 |
def get_or_create_tendency(self, user) -> dict:
"""
API View에서 호출: DB 데이터를 우선 반환하고, 없으면 분석 요청
"""
# 1. DB 데이터가 이미 있다면 바로 반환 (Persistent Cache)
if hasattr(user, "ai_tendency"):
return {"status": "completed", "tendency": user.ai_tendency.tendency}
# ---------------------------------------------------------
# [캐시 로직 시작] 중복 분석 요청 방지 (Cache Lock)
# ---------------------------------------------------------
# 유저별 고유 캐시 키 생성 (예: "tendency_analysis_lock_15")
cache_key = f"tendency_analysis_lock_{user.id}"
"""
- `f"tendency_analysis_lock_{user.id}"`
- 유저 ID를 포함하여 유저별로 독립적인 락(Lock)을 관리합니다.
- 즉, A 유저가 분석 중이어도 B 유저는 분석을 요청할 수 있습니다.
"""
# 2. 캐시 조회: 이미 누군가(혹은 본인이) 요청해서 분석 중인지 확인
if cache.get(cache_key):
# 이미 Task가 돌고 있다면 API는 기다리라는 메시지만 반환
"""
- `cache.get(cache_key)`가 존재하면
- `run_user_tendency_analysis.delay()`를 호출하지 않고 즉시 리턴
- 이는 사용자가 버튼을 연타하거나, 새로고침을 반복해도 AI API가 중복으로 호출되는 것을 막아줍니다.
"""
return {
"status": "processing",
"message": "성향 분석이 진행 중입니다. 잠시만 기다려주세요.",
"tendency": None,
}
# 3. 캐시 설정 (Locking): "지금부터 이 유저의 분석을 시작함" 표시
# - value: "processing" (상태값)
# - timeout: 60 * 5 (5분 뒤 자동 만료, 혹시 모를 데드락 방지)
cache.set(cache_key, "processing", timeout=60 * 5)
"""
- `timeout=60 * 5` (300초)
- 만약 분석 작업(Task)이 알 수 없는 에러로 죽어서 `cache.delete()`가 호출되지 않더라도,
- 5분이 지나면 자동으로 락이 풀려 유저가 다시 요청할 수 있게 하는 안전장치입니다.
"""
# ---------------------------------------------------------
# [캐시 로직 끝]
# ---------------------------------------------------------
# 4. Celery 비동기 작업 호출
run_user_tendency_analysis.delay(user.id)
return {
"status": "processing",
"message": "성향 분석 요청이 접수되었습니다.",
"tendency": None,
}
logger = logging.getLogger(__name__)
@shared_task
def run_user_tendency_analysis(user_id: int):
# 서비스와 동일한 규칙으로 캐시 키 생성
cache_key = f"tendency_analysis_lock_{user_id}"
try:
user = User.objects.get(id=user_id)
# AI 분석 서비스 호출 (시간이 오래 걸리는 작업)
service = UserTendencyService()
service.analyze_and_save(user)
logger.info(f"Successfully finished analysis for User ID: {user_id}")
except Exception as e:
logger.error(f"Error in User Tendency Task: {e}", exc_info=True)
finally:
# ---------------------------------------------------------
# [캐시 로직] 락 해제 (Unlock)
# ---------------------------------------------------------
# 작업이 성공하든(try), 에러가 나든(except) 무조건 실행되어야 함
cache.delete(cache_key)
"""
- `cache.delete(cache_key)`
- 서비스 레이어에서 걸어두었던 "processing" 상태를 제거합니다.
- 이제 해당 유저는 다시 `get_or_create_tendency`를 호출했을 때
- 캐시가 없으므로 새로운 요청을 하거나 DB 결과를 받을 수 있습니다.
- finally 블록의 중요성
- AI 분석 중 예외(API 오류, DB 오류 등)가 발생하더라도 락은 반드시 해제되어야 합니다.
- `finally` 블록에 넣음으로써, 작업 실패 시 유저가 영원히 "분석 중" 상태에 갇히는 것(Deadlock)을 방지합니다.
"""
| 기능 | API / Method | 주요 로직 및 특징 | 관련 주요 파일 |
|---|---|---|---|
| 리뷰 등록 | ReviewAPIView (POST) | - 유효성 검사: 평점(Rating)은 1~5점 사이의 정수만 허용 (Validators 적용)- 관계 매핑: 작성자(User)와 대상 게임(Game)을 연결 - 예외 처리: 존재하지 않는 게임 ID 요청 시 에러 반환 | models/reviews.pyreview_api.pyreview_create_service.py |
| 리뷰 목록 조회 | ReviewAPIView (GET) | - 페이지네이션: ReviewPageNumberPagination을 통한 목록 분할 전송- 필터링: 특정 Game ID에 종속된 리뷰만 조회 | review_api.pyreview_list_service.py |
| 내 리뷰 조회 | MyReviewListAPIView (GET) | - 본인 확인: 로그인한 유저(request.user)가 작성한 리뷰만 필터링하여 조회 | review_api.pyreview_list_service.py |
| 좋아요 (투표) | ReviewLikeAPIView | - 중복 방지: 유저당 1개의 리뷰에 한 번만 좋아요 가능 (UniqueConstraint)- 카운팅: Review 모델에 like_count 필드를 두어 집계 성능 최적화 | models/review_like.pyreview_like_service.py |
| 데이터 관리 | Model Definition | - Soft Delete: is_deleted 필드를 사용하여 실제 데이터 삭제 대신 플래그 처리 (데이터 보존)- 인덱싱: 조회 성능 향상을 위한 DB Index 설정 ( game, like_count) | models/reviews.py |
| 기능 | API / Method | 주요 로직 및 특징 | 관련 주요 파일 |
|---|---|---|---|
| 댓글 등록 | ReviewCommentAPIView (POST) | - 부모 검증: 댓글이 달릴 리뷰가 존재하는지, 삭제(is_deleted=True)되지는 않았는지 확인- 작성자 매핑: 로그인한 유저 정보를 자동으로 작성자로 할당 | models/comments.pycomment_api.pycomment_create_service.py |
| 댓글 조회 | ReviewCommentAPIView (GET) | - 통합 조회: 단순히 댓글만 가져오는 것이 아니라, 리뷰 상세 정보 + 해당 리뷰의 댓글 목록을 함께 반환하는 구조로 보임 - 정렬: 인덱스를 활용하여 작성일 순 등의 정렬 효율화 | comment_api.pycomment_list_service.py |
| 데이터 관리 | Model Definition | - Soft Delete: 리뷰와 동일하게 is_deleted 필드를 통한 논리적 삭제 구현- 관계: Review와 1:N 관계 (FK) | models/comments.py |