2026/01/17~18

김기훈·2026년 1월 17일

TIL

목록 보기
114/194

Union Project - Code - 2주

Union Project - learning - 2주


# Keep (이번 주에 잘한 일)
- 1. daily scrum을 진행하여 전날 / 오늘 할 작업에 대해서 공유함
- 2. exception_handler을 사용하여 에러메세지 기본 형식을 통일함
- 3. swagger에 기획서와 작업물 2가지를 추가하여 프론트에게 제공함
- 4. 구현 완료된 기능은 기능의 결과값을 기반으로 api명세를 수정함
- 5. 내가 맡은 파트에서 프론트구현에 대해서 궁금한 점은 직접 물어봤음

# Problem (고쳐야 할 점)
- 1. 프론트와 현재 진행상황은 지속적으로 공유하지 않았음 

# Try (Keep과 Problem을 기반으로 시도해볼 것)
- 1. 기능개발은 지금처럼 진행하되 프론트와 소통을 좀 늘려야 할 듯 

AI

AI 모델(OpenAI API 등)을 연동

  • APIView의 post 또는 get 메서드 내에서 AI 모델을 호출하고 응답을 반환하는 구조
    • pip install djangorestframework openai python-dotenv 설치 필요
  • views.py

import os
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from openai import OpenAI
from dotenv import load_dotenv

# .env 파일에서 환경 변수 로드
load_dotenv()

# OpenAI 클라이언트 초기화
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

class AICompletionView(APIView):
    def post(self, request):
        user_prompt = request.data.get('prompt')
        
        if not user_prompt:
            return Response({"error": "Prompt is required"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            # OpenAI API 호출
            response = client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=[{"role": "user", "content": user_prompt}]
            )
            ai_message = response.choices[0].message.content
            
            # 결과 반환
            return Response({"result": ai_message}, status=status.HTTP_200_OK)
        
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
  • urls.py

from django.urls import path
from .views import AICompletionView

urlpatterns = [
    path('ai/generate/', AICompletionView.as_view(), name='ai-generate'),
]

작동 방식 요약

  • POST 요
    • 클라이언트가 {"prompt": "안녕?"}과 같은 JSON 데이터를 보냄
  • APIView 처리
    • view(ex. AICompletionView)가 이를 받아 OpenAI API에 데이터를 전달
  • AI 응답
    • OpenAI가 텍스트 응답을 반환
  • JSON 응답
    • APIView가 최종적으로 {"result": "안녕하세요!..."} 형식으로 클라이언트에게 응답함

추가 고민

  • 비동기 처리
    • AI 모델 응답이 길어질 경우, APIView 대신 Celery 같은 백그라운드 작업을 활용하는 것 고민
  • 랑체인(LangChain) 연동
    • 복잡한 AI 워크플로우가 필요하면, APIView 내부에서 LangChain을 연동하여 사용

제미나이

- 1. 라이브러리
pip install google-generativeai python-dotenv djangorestframework

- 2. 제미나이 API Key 설정 
  - Google AI Studio에서 API Key를 발급받고
  - Django 프로젝트 루트 디렉토리에 .env 파일을 생성하여 Key를 저장
  
# .env 파일
GOOGLE_API_KEY=your_gemini_api_key_here

- 3. Django 프로젝트 설정 (Settings)
  - settings.py에서 .env 파일을 로드하고 제미나이 API Key를 설정

# settings.py
import os
from dotenv import load_dotenv

load_dotenv()
GEMINI_API_KEY = os.getenv("GOOGLE_API_KEY")

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'your_app_name', # 앱 이름
]

- 4. APIView 구현 및 URL 매핑
  - APIView를 상속받아 POST 요청을 처리하는 뷰를 생성하고, URL 패턴에 연결

- 5. 테스트 (Postman/cURL)
  - 서버 실행 후, http://127.0.0.1 엔드포인트로 POST 요청을 보내며,
  - 요청 본문에는 "prompt" 필드를 포함하여 보내야 함

- 

코드 대기

# service
from django.conf import settings
from apps.community.models.reviews import Review
# openai 라이브러리가 설치되어 있다고 가정합니다 (pip install openai)
import openai 

def get_review_summary(game_id: int) -> str:
    """
    특정 게임의 리뷰들을 조회하여 AI를 통해 요약본을 생성합니다.
    """
    # 1. 요약할 리뷰 데이터 조회 (최신 50개 등 제한을 두는 것이 좋습니다)
    reviews = Review.objects.filter(game_id=game_id, is_deleted=False).order_by("-created_at")[:30]
    
    if not reviews:
        return "작성된 리뷰가 없습니다."

    # 2. 리뷰 텍스트 합치기
    reviews_text = "\n".join([f"- {r.content}" for r in reviews])

    # 3. AI 프롬프트 작성
    prompt = f"""
    아래는 특정 게임에 대한 유저들의 리뷰 모음입니다. 
    이 게임에 대한 유저들의 전반적인 평가를 3줄로 요약해주세요.
    
    [리뷰 목록]
    {reviews_text}
    """

    # 4. OpenAI API 호출 (예시)
    # 실제 사용 시 settings.OPENAI_API_KEY 등을 설정해야 합니다.
    try:
        client = openai.OpenAI(api_key=settings.OPENAI_API_KEY)
        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=[
                {"role": "system", "content": "당신은 게임 리뷰 요약 전문가입니다."},
                {"role": "user", "content": prompt}
            ]
        )
        summary = response.choices[0].message.content
        return summary
    except Exception as e:
        # 로그 기록 등이 필요할 수 있습니다.
        return "요약 서비스를 일시적으로 사용할 수 없습니다."

# view
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers

from apps.community.services.review.review_summary_service import get_review_summary

class ReviewSummaryAPIView(APIView):
    permission_classes = [IsAuthenticatedOrReadOnly]

    @extend_schema(
        tags=["리뷰"],
        summary="리뷰 AI 요약 API",
        responses={
            200: inline_serializer(
                name="ReviewSummaryResponse",
                fields={
                    "game_id": serializers.IntegerField(),
                    "summary": serializers.CharField(),
                },
            ),
        },
    )
    def post(self, request, game_id: int):
        """
        특정 게임의 리뷰 요약을 요청합니다.
        (요약 비용 등을 고려하여 POST 요청으로 설계하거나, 캐싱을 적용한 GET으로 설계할 수 있습니다.)
        """
        # 서비스 레이어 호출
        summary_text = get_review_summary(game_id=game_id)

        return Response(
            {
                "game_id": game_id,
                "summary": summary_text
            },
            status=status.HTTP_200_OK
        )

# urls
from django.urls import path

from apps.community.views.review_api import ReviewAPIView
from apps.community.views.review_like_api import ReviewLikeAPIView
from apps.community.views.review_update_api import ReviewUpdateAPIView
from apps.community.views.review_summary_api import ReviewSummaryAPIView # 추가

urlpatterns = [
    path("<int:game_id>/reviews", ReviewAPIView.as_view(), name="game_review_create"),
    path(
        "reviews/<int:review_id>/like", ReviewLikeAPIView.as_view(), name="review_like"
    ),
    path(
        "reviews/<int:review_id>", ReviewUpdateAPIView.as_view(), name="review_update"
    ),
    # 신규 추가된 URL
    path(
        "<int:game_id>/reviews/summary", ReviewSummaryAPIView.as_view(), name="game_review_summary"
    ),
]

chatGPT

  • openai
    • 최소 5달러는 결제해야 시작 가능

Gemini

기초세팅

    1. API 키 발급
    1. 라이브러리 설치
    • poetry add google-generativeai

참고

외부 API 연동하기

토큰 계산기

재미나이 api

재미나이 api 2

chatGPT

profile
안녕하세요.

0개의 댓글