▶️ JavaScript
[▶️Back-end] 사용자 입력의 Prompt를 다듬는 기능 구현 5차_LLM
Back-end 기능 구현은 끝났으니까, 이제는 Front-end 작업을 해봅시다!
시작하기 전에 JS와 DRF 연동하는 것에 대해 공부했어요.
보안을 위해서 CORS를 설치해야 하는군요!
settings.py에도 앱 등록 및 도메인 주소 설정도 해줘야겠어요.
이전 : [🎨Front-end] HTML/CSS/JS, JavaScript 통신
최근 : [🎨Front-end] DRF와 JS 연동 방법
수정 전 Miravelle 서버

현재 서버의 메인 화면이에요.

메인 화면에 프롬프트 생성하는 버튼은 만들어져 있지만,

버튼을 누르면 이렇게 표시되기 때문에 프런트 작업을 해주려고 해요.
작업 시작
core/settings.py

지금 허용된 Host는 3개가 있어요.(모자이크 처리했답니다 🤗)
프런트들이 다 localhost:8000으로 돼 있기 때문에 JS 작성할 때 url을 8000으로 해줄게요.

이미 INSTALLED_APP에 coresheaders가 등록돼 있어서 따로 추가해주진 않았어요.
MIDDLEWARE = [
...
# JS와 연결하기
"corsheaders.middleware.CorsMiddleware",
]
CORS_ALLOW_ALL_ORIGINS = True # 개발 중에는 허용
대신 MIDDLEWARE와 CORS_ALLOW_ALL_ORIGINS를 추가해 주었어요.
prompts/views.py
더보기
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from openai import AsyncOpenAI
import asyncio
import openai
from drf_yasg.utils import swagger_auto_schema
from prompts.serializers import GeneratePromptSerializer
from rest_framework.renderers import TemplateHTMLRenderer # 20250324 추가
from django.utils.decorators import method_decorator # 20250324 추가
from django.contrib.auth.decorators import login_required # 20250324 추가
from django.http import JsonResponse # 20250324 추가
from utils.azure_key_manager import AzureKeyManager
# OpenAI 프롬프트 생성 함수
async def generate_3d_prompt(user_input):
azure_keys = AzureKeyManager.get_instance()
api_key = azure_keys.openai_api_key
if not api_key:
raise ValueError("Missing OpenAI API Key")
aclient = AsyncOpenAI(api_key=api_key)
system_prompt = (
"너는 3D 모델을 생성하기 위한 최적의 프롬프트를 만드는 AI야."
"사용자의 요청을 분석하여 디테일한 프롬프트를 제공해야 해."
"사용자가 제공한 정보를 존중하며, 부족한 경우 일반적인 추천을 추가해."
"3D 모델링에 적합한 키워드를 선정하고, 문장은 직관적이며 이해하기 쉽게 작성해."
"프롬프트 내의 개행을 삭제하고, 자연스럽게 이어지도록 구성해."
"필요한 정보를 압축해 500자 이내로 한국어로 작성해."
)
try:
response = await aclient.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input}
]
)
return response.choices[0].message.content
except openai.APIConnectionError as e:
print(f"OpenAI 서버 연결 오류: {e}")
return "OpenAI 서버 연결에 실패했습니다. 잠시 후 다시 시도해주세요."
except openai.RateLimitError as e:
print(f"OpenAI API Rate Limit 초과: {e}")
return "OpenAI API Rate Limit이 초과되었습니다. 잠시 후 다시 시도해주세요."
except Exception as e:
print(f"OpenAI API 호출 중 오류 발생: {e}")
return "OpenAI API 호출 중 오류가 발생했습니다."
# Django REST Framework API 엔드포인트
# ⬇️ 20250324 추가
@method_decorator(login_required(login_url='users:login'), name='dispatch')
class GeneratePromptAPI(APIView):
renderer_classes = [TemplateHTMLRenderer] # 20250324 추가
template_name = 'prompt.html' # 20250324 추가
def get(self, request):
message = "API is running"
# ⬇️ 20250324 추가
return Response({"status": message}, template_name=self.template_name, status=status.HTTP_200_OK)
@swagger_auto_schema(
request_body=GeneratePromptSerializer,
operation_description="3D 모델 생성을 위한 프롬프트 생성 API",
)
def post(self, request):
serializer = GeneratePromptSerializer(data=request.data)
if serializer.is_valid():
user_input = serializer.validated_data['user_input']
user_request = "Create a 3D model based on 3D Model Prompt: {}".format(user_input)
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
optimized_prompt = loop.run_until_complete(generate_3d_prompt(user_request))
loop.close()
return Response({"Miravelle": optimized_prompt}, status=status.HTTP_200_OK) #kmj 20250324
# return Response({"Miravelle": optimized_prompt}, status=200) #kmj 20250328
except Exception as e:
return Response(
{"error": f"API 호출 중 오류 발생: {str(e)}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
prompts/templates/prompt.html
더보기
{% extends "base.html" %}
{% load static %}
{% block content %}
{% if user.is_authenticated %}
<div class="container mt-5">
<h2>Generate 3D Model Prompt</h2>
<input type="hidden" id="csrf_token" value="{{ csrf_token }}">
<input type="text" id="userInput" class="form-control" placeholder="생성하고 싶은 모델을 설명해주세요." maxlength="500"> {# maxlength 추가 #}
<button id="submitBtn" class="btn btn-dark mt-3">Generate</button>
<div id="output" class="mt-4"></div>
<div id="loading" class="mt-3" style="display:none;">loading...</div> {# 로딩 표시 #}
<div id="error" class="mt-3 text-danger" style="display:none;"></div> {# 오류 표시 #}
</div>
{% else %}
<div class="container mt-5">
<h2>Login required</h2>
<p>Please <a href="{% url 'users:login' %}">log in</a> to use this page.</p>
</div>
{% endif %}
<script src="{% static 'prompt_js/prompt.js' %}"></script>
{% endblock %}
core/static/prompt_js/prompt.js
더보기
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.startsWith(name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
document.addEventListener('DOMContentLoaded', function() {
const submitBtn = document.getElementById('submitBtn');
const userInput = document.getElementById('userInput');
const output = document.getElementById('output');
const loading = document.getElementById('loading');
const error = document.getElementById('error');
const csrfToken = getCookie('csrftoken'); // CSRF 토큰 가져오기
console.log("csrfToken: ", csrfToken);
if (!submitBtn || !userInput || !output || !loading || !error) {
console.error("필수 요소가 존재하지 않습니다. HTML 구조를 확인하세요.");
return;
}
submitBtn.addEventListener('click', function() {
const inputText = userInput.value;
if (!inputText) {
error.textContent = "프롬프트를 입력해주세요.";
error.style.display = "block";
return;
}
if (inputText.length > 500) {
error.textContent = "입력은 500자를 초과할 수 없습니다.";
error.style.display = "block";
return;
}
loading.style.display = "block";
output.style.display = "none";
error.style.display = "none";
fetch('/api/prompts/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken // CSRF 토큰 추가
},
body: JSON.stringify({ user_input: inputText })
})
.then(response => {
loading.style.display = "none";
output.style.display = "block";
if (!response.ok) {
return response.json().then(err => {
throw new Error(err.error || 'API 요청 실패');
});
}
return response.json();
})
.then(data => {
output.textContent = data.Miravelle || "결과를 가져올 수 없습니다.";
})
.catch(err => {
error.textContent = err.message || "서버 오류가 발생했습니다.";
error.style.display = "block";
});
});
});
변경된 Create to 3D Model Prompt 화면

이렇게 외형 변경이 완료된 상태예요.
하지만 JSON 형태로 Response이 되지 않아서 작동되지 않고 있어요. 🤔
내일은 Generate를 눌렀을 때, 프롬프트 결과가 보이도록 수정할 거예요.
읽어주셔서 감사합니다 :)
'👥 최종 팀 프로젝트(250227~250331) > 구현 과정 ▶️' 카테고리의 다른 글
[▶️Back-end] 메인 페이지의 페이지네이션(pagination) 구현 (0) | 2025.03.26 |
---|---|
[▶️Front-end] 사용자 요청 결과의 Prompt를 보여주기 2차 完 (1) | 2025.03.25 |
[▶️Back-end] 사용자 입력의 Prompt를 다듬는 기능 구현 5차_LLM 完 (0) | 2025.03.21 |
[▶️Back-end] 사용자 입력의 Prompt를 다듬는 기능 구현 4차_LLM (5) | 2025.03.20 |
[▶️Back-end] 사용자 입력의 Prompt를 다듬는 기능 구현 3차_LLM (1) | 2025.03.19 |