🤔 어떤 작업을 해야하나..
결국, 내 생각에는 모델은 하나만 사용하고, 선택한 상황에 따라 다른 프롬프트를 제공하는게 맞을 것 같다는 생각이 들었다.
현재 사용자가 선택할 수 있는 사항은 4가지로 해놓았는데,
selectedGameType: 어떤 놀이인지(ex. 병원 놀이)
selectedUserRole: 사용자의 역할은 무엇인지(ex. 의사)
selectedAIRole: AI의 역할은 무엇인지(ex. AI)
selectedAIVoice: AI의 음성은 무엇으로 할 것인지(ex. 음성1)
=> 이 4가지 선택지를 기반으로 fastAPI는 LM Studio에 응답을 요청할 때, 다른 prompt를 제공해야한다.
바로, fastAPI에서 # 프롬프트 내용 부분에 다른 요청을 해야된다는 소리이다.
prompt_template = f"""
# 프롬프트 내용
#Question:
{message.question}
#Answer: """
예를 들어 병원 놀이라면, "AI야 너는 지금 병원놀이를 하고 있어. 너의 역할은 환자야. 그리고 사용자는 의사야~"이런식으로 말이다. 이렇게 사용자의 선택지에 따라서 다른 프롬프트를 제공해야한다.
아 음성은 프롬프트와 상관없을수도 있겠지만, 일단은 포함시켰다.
🔶 어떻게 4가지 정보를 전달할 것인가..
찾아본 결과, 서버로 데이터를 보내는 몇가지 방법들을 찾을 수 있었다.
서버로 데이터를 보내는 방법에는 여러가지가 있다. 일반적으로 사용되는 세 가지 방법은 다음과 같다.
(내가 백엔드를 잘 몰라서...정확하지 않을수도 있다.. 문제가 있다면 알려주길!!)
🔸 (1) url parameter
URL 파라미터는 RESTful API에서 자주 사용되는 방법으로, 리소스를 식별하거나 특정 작업을 지정할 때 사용됩니다. URL 경로의 일부분으로 포함되며, 보통 슬래시 /로 구분됩니다.
여기서 123은 특정 사용자 리소스를 식별하는 파라미터입니다.
🔸 (2) query string
쿼리 스트링은 URL의 끝에 ?뒤에 키-값쌍으로 데이터를 포함시키는 방법입니다. 여러 개의 파라미터는 &로 구분됩니다.
GET /search?query=example&limit=10&page=2
위의 예시에서 query, limit, page는 쿼리 스트링 파라미터입니다. 이 방법은 주로 GET요청에서 데이터를 보낼 때 사용됩니다.
🔸 (3) 요청 부문(body)에 데이터를 포함시키기
요청 본문은 POST, PUT, PATCH 등의 요청에서 주로 사용되며, 데이터는 HTTP 메시지의 본문에 포함됩니다. JSON, XML, 폼 데이터 등 다양한 포맷을 사용할 수 있습니다.
POST /users
Content-Type: application/json
{
"name": "John Doe",
"email": "john.doe@example.com"
}
위의 예시에서는 JSON형식으로 사용자 정보를 본문에 포함시켜 보냅니다.
더 궁금하면 해당글참고
🔸 그 외의 방법
사실 그 외에도 HTTP의 Header에 데이터를 포함시켜 보내는 방법, 데이터를 쿠키에 포함시켜 보내는 방법 등 여러 방법들이 있으나, 위에서 언급한 3가지 방법이 일반적입니다.
🔶 어떤 방법을 선택해야할까?
| url parameter |
✔️ 추천할 때
- 리소스를 식별하거나 특정 자원을 참조할 때
- RESTful API에서 리소스의 ID 등을 전달할 때
✔️ 장점
- URL 자체에 의미를 부여할 수 있어 직관적임
- 리소스 식별에 용이함
✔️ 단점
- 데이터 길이가 제한적임
- 민감한 데이터를 포함하기 어려움
|
| 쿼리 스트링 |
✔️ 추천할 때
- 검색 필터, 페이지네이션 등에서 여러 개의 파라미터를 전달할 때
- GET 요청에서 데이터를 전달할 때
✔️ 장점
- 어러 개의 키-값 쌍을 쉽게 전달 가능
- URL에 포함되어 브라우저 히스토리에 저장 가능
✔️ 단점
- 데이터의 길이가 제한적임
- 민감한 데이터를 포함하기 어려움
|
| 요청 본문 |
✔️ 추천할 때
- 데이터를 생성하거나 업데이트할 때 (POST, PUT, PATCH 요청)
- 대량의 데이터 또는 복잡한 데이터 구조를 전달할 때
✔️ 장점
- 데이터 길이에 제한이 거의 없음
- 구조화된 데이터(JSON, XML 등)를 전달하기에 적합함
✔️ 단점
- GET 요청에는 사용되지 않음
- URL에 포함되지 않아, 브라우저 히스토리에 저장되지 않음
|

나는 위와 같은 이유로, 쿼리 스트링 방법이 적합하다고 생각했는데, 다른 방법이 더 적합해 보이면 피드백 해줄것!!!
🔶 쿼리 스트링 방법을 썼을 때, 코드
다음 3개 파일의 코드 변경이 생겼다.
(1) Server1.py
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
from langchain_core.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
app = FastAPI()
# CORS 설정
origins = [
"http://localhost",
"http://localhost:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["POST"],
allow_headers=["Content-Type"],
)
llm = ChatOpenAI(
base_url="http://localhost:5000/v1",
api_key="lm-studio",
model="teddylee777/Llama-3-Open-Ko-8B-gguf",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
class Message(BaseModel):
question: str
@app.post("/chat")
async def chat_with_bot(
message: Message,
selectedGameType: str = Query(...),
selectedUserRole: str = Query(...),
selectedAIRole: str = Query(...),
selectedAIVoice: str = Query(...)
):
try:
prompt_template = f"""
You are the assistant, you are the {selectedAIRole}.
{selectedGameType} will deal with {selectedUserRole}.
{selectedAIRole} must answer {selectedUserRole} in simple expressions and informal language.
#Question:
{message.question}
#Answer: """
prompt = PromptTemplate.from_template(prompt_template)
chain = prompt | llm | StrOutputParser()
response = chain.invoke({"question": message.question})
return {"response": response}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
[ CORS 설정 ]
CORS 설정에서 origins는 특정 출처로부터의 요청을 허용하도록 설정하는 부분입니다.
http://localhost
- 이 출처에서 오는 모든 요청을 허용한다는 의미입니다. 이는 일반적으로 백엔드 서버와 같은 출처에 있는 클라이언트 애플리케이션(ex. 프론트)에서 오는 요청을 허용하는 데 사용됩니다.
http://localhost:3000
- 3000번 포트는 현재 리액트 앱을 의미하며, 즉 프론트 측에서 오는 요청을 허용한다는 의미입니다.
CORS 설정이 없는 경우에 브라우저가 차단하는 것은 "다른 출처"에서 오는 요청입니다. "다른 출처"란 도메인, 프로토콜, 또는 포트가 다른 경우를 말합니다.
[ fastAPI 애플리케이션에 CORS 미들웨어 추가 ]
이 코드는 FastAPI 애플리케이션에 CORS 미들웨어를 추가하여 특정 출처에서 오는 요청을 허용하는 설정을 적용하는 부분입니다. CORS 미들웨어는 웹 브라우저가 서로 다른 출처에서 오는 요청을 차단하지 않고 허용하도록 도와줍니다.
이 코드의 목적은 http://localhost:3000에서 오는 요청을 허용하고, 그 요청이 자격 증명을 포함할 수 있도록 하며, POST 메서드와 Content-Type 헤더를 사용할 수 있게 설정하는 것입니다. 이를 통해 로컬에서 실행되는 프런트엔드 애플리케이션이 FastAPI 백엔드와 원활하게 통신할 수 있도록 합니다.
# CORS 미들웨어 추가
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 허용할 출처 리스트
allow_credentials=True, # 자격 증명(쿠키, 인증 헤더 등) 포함 요청 허용
allow_methods=["POST"], # 허용할 HTTP 메서드 (예: GET, POST 등)
allow_headers=["Content-Type"], # 허용할 HTTP 헤더
)
[ ChatOpenAI라는 클래스를 사용하여 LM을 설정 ]
llm = ChatOpenAI(
base_url="http://localhost:5000/v1",
api_key="lm-studio",
model="teddylee777/Llama-3-Open-Ko-8B-gguf",
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()],
)
이 코드는 ChatOpenAI라는 클래스를 사용하여 Language Model(LM)을 설정하는 부분입니다. 여기서는 ChatOpenAI를 인스턴스화하여 로컬 서버에서 실행되는 언어 모델 API와 상호 작용하도록 설정하고 있습니다. 이 인스턴스를 통해 AI 챗봇의 대화를 처리하는 역할을 합니다.
(2) Chat.jsx
import React, { useState } from 'react';
import Layout from '../../components/Layout/Layout';
import { useNavigate } from 'react-router-dom';
import Alert from '../../components/Alert/Alert';
import styles from './Chat.module.scss';
import { useLocation } from 'react-router-dom';
function Chat() {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const selectedGameType = queryParams.get('selectedGameType');
const selectedUserRole = queryParams.get('selectedUserRole');
const selectedAIRole = queryParams.get('selectedAIRole');
const selectedAIVoice = queryParams.get('selectedAIVoice');
const [messages, setMessages] = useState([]);
const [userMessage, setUserMessage] = useState('');
const [loading, setLoading] = useState(false);
const [showAlert, setShowAlert] = useState(false);
const [isListening, setIsListening] = useState(false);
const navigate = useNavigate();
const handleInputChange = (e) => {
setUserMessage(e.target.value);
};
const speakText = (text) => {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ko-KR';
window.speechSynthesis.speak(utterance);
};
const handleSendMessage = async () => {
if (!userMessage.trim()) return;
const newMessages = [...messages, { sender: '사용자', text: userMessage }];
setMessages(newMessages);
setUserMessage('');
setLoading(true);
try {
const response = await fetch(`http://localhost:8000/chat?selectedGameType=${selectedGameType}&selectedUserRole=${selectedUserRole}&selectedAIRole=${selectedAIRole}&selectedAIVoice=${selectedAIVoice}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ question: userMessage })
});
const data = await response.json();
const computerMessage = { sender: '컴퓨터', text: data.response };
setMessages([...newMessages, computerMessage]);
speakText(computerMessage.text);
} catch (error) {
console.error('Error:', error);
setMessages([...newMessages, { sender: '컴퓨터', text: 'Error: 응답을 가져올 수 없습니다.' }]);
} finally {
setLoading(false);
}
};
const handleStartListening = () => {
if (!('webkitSpeechRecognition' in window)) {
alert('STT를 지원하지 않는 브라우저입니다.');
return;
}
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
recognition.lang = 'ko-KR';
recognition.interimResults = false;
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
setUserMessage(transcript);
handleSendMessage();
};
recognition.onend = () => {
setIsListening(false);
};
recognition.start();
setIsListening(true);
};
const handleStopClick = () => {
setShowAlert(true);
};
const handleCloseAlert = () => {
setShowAlert(false);
};
const handleConfirmExit = () => {
navigate('/result');
};
return (
<Layout>
<div className={styles.chatContainer}>
<div className={styles.chatWrapper}>
<div className={styles.chatHeader}>
<h1>역할놀이 챗봇</h1>
<button className={styles.stopButton} onClick={handleStopClick}>
X
</button>
</div>
<div className={styles.chatBody}>
{messages.map((msg, index) => (
<div key={index} className={`${styles.message} ${msg.sender === '사용자' ? styles.userMessage : styles.computerMessage}`}>
<strong>{msg.sender}:</strong> {msg.text}
</div>
))}
</div>
<div className={styles.userInput}>
<input
type="text"
id="user-message"
value={userMessage}
onChange={handleInputChange}
placeholder="메시지를 입력하세요..."
className={styles.userMessageInput}
/>
<button className={styles.sendButton} onClick={handleSendMessage}>
전송
</button>
<button className={styles.sendButton} onClick={handleStartListening} disabled={isListening}>
{isListening ? '음성 인식 중...' : '음성 입력'}
</button>
</div>
{loading && <div className={styles.loading}>로딩 중...</div>}
</div>
{showAlert && (
<Alert
message="정말로 종료하시겠습니까?"
onConfirm={handleConfirmExit}
onCancel={handleCloseAlert}
/>
)}
</div>
</Layout>
);
}
export default Chat;
(3) Situation.jsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Layout from '../../components/Layout/Layout';
import Alert from '../../components/Alert/Alert';
import styles from './Situation.module.scss';
function Situation() {
const navigate = useNavigate();
const [selectedGameType, setSelectedGameType] = useState(null);
const [selectedUserRole, setSelectedUserRole] = useState(null);
const [selectedAIRole, setSelectedAIRole] = useState(null);
const [selectedAIVoice, setSelectedAIVoice] = useState(null);
const [rightPanelState, setRightPanelState] = useState(false);
const [showAlert, setShowAlert] = useState(false);
const roleOptions = {
'학교 놀이': ['선생님', '학생', '교장선생님'],
'병원 놀이': ['의사', '환자', '간호사'],
'시장 놀이': ['상인', '소비자', '경찰'],
'가족 놀이': ['아빠', '엄마', '아들', '딸'],
'소꿉 놀이': ['너구리', '사자', '토끼'],
'소방관 놀이': ['소방관', '도움이 필요한 사람'],
'연예인 놀이': ['가수', '배우', '메이크업 담당 선생님', '헤어 담당 선생님', '매니저'],
'승무원 놀이': ['승객', '승무원'],
};
const voiceOptions = ['음성A', '음성B', '음성C', '음성D'];
const handleRolePlaySelect = (gameType) => {
if (gameType === selectedGameType) {
setSelectedGameType(null);
setRightPanelState(false);
} else {
setSelectedGameType(gameType);
setRightPanelState(true);
}
};
const handleNextStep = () => {
if (selectedGameType && selectedUserRole && selectedAIRole && selectedAIVoice) {
const queryParams = new URLSearchParams({
selectedGameType,
selectedUserRole,
selectedAIRole,
selectedAIVoice
}).toString();
navigate(`/chat?${queryParams}`);
} else {
setShowAlert(true);
}
};
const handleExit = () => {
navigate('/');
};
const handleCloseAlert = () => {
setShowAlert(false);
};
return (
<Layout>
<div className={styles.situationWrap}>
<div className={styles.header}>
<h1 className={styles.title}>Roleplay With AI</h1>
<button onClick={handleExit} className={styles.exitButton}>돌아가기</button>
</div>
<div className={styles.content}>
<div className={styles.leftPanel}>
<h2 className={styles.subtitle}>놀이를 선택해주세요</h2>
<div className={styles.gameTypeBox}>
{Object.keys(roleOptions).map((gameType) => (
<button
key={gameType}
onClick={() => handleRolePlaySelect(gameType)}
className={`${styles.gameTypeButton} ${gameType === selectedGameType ? styles.selected : ''}`}
>
{gameType}
</button>
))}
</div>
</div>
{rightPanelState && selectedGameType && (
<div className={styles.rightPanel}>
<div className={styles.rightPanelHeader}>
<h3>세부 설정</h3>
<p>선택된 놀이: {selectedGameType}</p>
</div>
<div className={styles.selectionSection}>
<h3>🐻 사용자 역할</h3>
<select onChange={(e) => setSelectedUserRole(e.target.value)} className={styles.selectDropdown}>
<option value="">선택해주세요</option>
{roleOptions[selectedGameType].map((role) => (
<option key={role} value={role}>{role}</option>
))}
</select>
</div>
<div className={styles.selectionSection}>
<h3>🐻 AI 역할</h3>
<select onChange={(e) => setSelectedAIRole(e.target.value)} className={styles.selectDropdown}>
<option value="">선택해주세요</option>
{roleOptions[selectedGameType].map((role) => (
<option key={role} value={role}>{role}</option>
))}
</select>
</div>
<div className={styles.selectionSection}>
<h3>🐻 AI 음성</h3>
<select onChange={(e) => setSelectedAIVoice(e.target.value)} className={styles.selectDropdown}>
<option value="">선택해주세요</option>
{voiceOptions.map((voice) => (
<option key={voice} value={voice}>{voice}</option>
))}
</select>
</div>
<div className={styles.nextButtonContainer}>
<button onClick={handleNextStep} className={styles.nextButton}>다음 단계</button>
</div>
</div>
)}
</div>
{showAlert && (
<Alert
message="모든 항목을 선택해야 합니다."
onConfirm={handleCloseAlert}
/>
)}
</div>
</Layout>
);
}
export default Situation;
🔶 쿼리 스트링으로 짰을 때, 실행 결과이다.
응답을 받는데 시간이 꽤 걸리는데, 참을성을 가지고 보시오!

실행결과가 진행될 때, 상단의 url을 확인해보면, 채팅페이지로 넘어갈때 다음과 같은 url로 설정되는 걸 확인할 수 있다.

http://localhost:3000/chat?selectedGameType=%EC%8B%9C%EC%9E%A5+%EB%86%80%EC%9D%B4&selectedUserRole=%EC%86%8C%EB%B9%84%EC%9E%90&selectedAIRole=%EC%83%81%EC%9D%B8&selectedAIVoice=%EC%9D%8C%EC%84%B1A
상단의 url 보이시나요? 복사 붙혀넣기 했더니 한글 대신 이상한 문자들이 포함되는 걸 확인할 수 있을텐데, 한글이 url에 포함될 때, url 인코딩이라는 과정을 거치게 되기 때문입니다. url 인코딩은 특수 문자 및 비-ASCII 문자를 퍼센트 기호(%)와 16진수 코드로 변환하는 방법입니다. 이로 인해 한글 등 비-ASCII 문자가 포함된 URL이 인코딩된 형태로 보이게 됩니다.
예를 들어, "시장 놀이"는 "%EC%8B%9C%EC%9E%A5+%EB%86%80%EC%9D%B4"로 인코딩됩니다. 이는 브라우저가 한글을 URL에 포함할 때 자동으로 인코딩하기 때문입니다.
암튼 URL 인코딩이 중요한게 아니라, 제가 하고 싶은말은 저렇게 쿼리 스트링 방식으로 다른 프롬프트를 제공할 수 있다 이말입니다.
✏️ 해당 이슈와 관련되서 밸류업 해야할 사항들
- 일단 쿼리스트링 방식을 백엔드를 배운 팀원들에게 물어봐야겠다. 의견을 구해보고, 더 나은 방법이 있는건지, 어떤식으로 전달하면 좋을지 의견을 구해봐야겠다.
- 프롬프트를 어떤식으로 설정해야할지 프롬프트 엔지니어링 부분에서 더 공부해야될 것 같다.
🤔 어떤 작업을 해야하나..
결국, 내 생각에는 모델은 하나만 사용하고, 선택한 상황에 따라 다른 프롬프트를 제공하는게 맞을 것 같다는 생각이 들었다.
현재 사용자가 선택할 수 있는 사항은 4가지로 해놓았는데,
selectedGameType: 어떤 놀이인지(ex. 병원 놀이)selectedUserRole: 사용자의 역할은 무엇인지(ex. 의사)selectedAIRole: AI의 역할은 무엇인지(ex. AI)selectedAIVoice: AI의 음성은 무엇으로 할 것인지(ex. 음성1)=> 이 4가지 선택지를 기반으로 fastAPI는 LM Studio에 응답을 요청할 때, 다른 prompt를 제공해야한다.
바로, fastAPI에서
# 프롬프트 내용부분에 다른 요청을 해야된다는 소리이다.예를 들어 병원 놀이라면, "AI야 너는 지금 병원놀이를 하고 있어. 너의 역할은 환자야. 그리고 사용자는 의사야~"이런식으로 말이다. 이렇게 사용자의 선택지에 따라서 다른 프롬프트를 제공해야한다.
아 음성은 프롬프트와 상관없을수도 있겠지만, 일단은 포함시켰다.
🔶 어떻게 4가지 정보를 전달할 것인가..
찾아본 결과, 서버로 데이터를 보내는 몇가지 방법들을 찾을 수 있었다.
서버로 데이터를 보내는 방법에는 여러가지가 있다. 일반적으로 사용되는 세 가지 방법은 다음과 같다.
(내가 백엔드를 잘 몰라서...정확하지 않을수도 있다.. 문제가 있다면 알려주길!!)
🔸 (1) url parameter
URL 파라미터는 RESTful API에서 자주 사용되는 방법으로, 리소스를 식별하거나 특정 작업을 지정할 때 사용됩니다. URL 경로의 일부분으로 포함되며, 보통 슬래시
/로 구분됩니다.여기서
123은 특정 사용자 리소스를 식별하는 파라미터입니다.🔸 (2) query string
쿼리 스트링은 URL의 끝에
?뒤에키-값쌍으로 데이터를 포함시키는 방법입니다. 여러 개의 파라미터는&로 구분됩니다.위의 예시에서
query,limit,page는 쿼리 스트링 파라미터입니다. 이 방법은 주로 GET요청에서 데이터를 보낼 때 사용됩니다.🔸 (3) 요청 부문(body)에 데이터를 포함시키기
요청 본문은 POST, PUT, PATCH 등의 요청에서 주로 사용되며, 데이터는 HTTP 메시지의 본문에 포함됩니다. JSON, XML, 폼 데이터 등 다양한 포맷을 사용할 수 있습니다.
위의 예시에서는 JSON형식으로 사용자 정보를 본문에 포함시켜 보냅니다.
더 궁금하면 해당글참고
🔸 그 외의 방법
사실 그 외에도 HTTP의 Header에 데이터를 포함시켜 보내는 방법, 데이터를 쿠키에 포함시켜 보내는 방법 등 여러 방법들이 있으나, 위에서 언급한 3가지 방법이 일반적입니다.
🔶 어떤 방법을 선택해야할까?
- 리소스를 식별하거나 특정 자원을 참조할 때
- RESTful API에서 리소스의 ID 등을 전달할 때
✔️ 장점
- URL 자체에 의미를 부여할 수 있어 직관적임
- 리소스 식별에 용이함
✔️ 단점
- 데이터 길이가 제한적임
- 민감한 데이터를 포함하기 어려움
- 검색 필터, 페이지네이션 등에서 여러 개의 파라미터를 전달할 때
- GET 요청에서 데이터를 전달할 때
✔️ 장점
- 어러 개의 키-값 쌍을 쉽게 전달 가능
- URL에 포함되어 브라우저 히스토리에 저장 가능
✔️ 단점
- 데이터의 길이가 제한적임
- 민감한 데이터를 포함하기 어려움
- 데이터를 생성하거나 업데이트할 때 (POST, PUT, PATCH 요청)
- 대량의 데이터 또는 복잡한 데이터 구조를 전달할 때
✔️ 장점
- 데이터 길이에 제한이 거의 없음
- 구조화된 데이터(JSON, XML 등)를 전달하기에 적합함
✔️ 단점
- GET 요청에는 사용되지 않음
- URL에 포함되지 않아, 브라우저 히스토리에 저장되지 않음
나는 위와 같은 이유로, 쿼리 스트링 방법이 적합하다고 생각했는데, 다른 방법이 더 적합해 보이면 피드백 해줄것!!!
🔶 쿼리 스트링 방법을 썼을 때, 코드
다음 3개 파일의 코드 변경이 생겼다.
(1) Server1.py
[ CORS 설정 ]
CORS 설정에서
origins는 특정 출처로부터의 요청을 허용하도록 설정하는 부분입니다.http://localhosthttp://localhost:3000CORS 설정이 없는 경우에 브라우저가 차단하는 것은 "다른 출처"에서 오는 요청입니다. "다른 출처"란 도메인, 프로토콜, 또는 포트가 다른 경우를 말합니다.
[ fastAPI 애플리케이션에 CORS 미들웨어 추가 ]
이 코드는 FastAPI 애플리케이션에 CORS 미들웨어를 추가하여 특정 출처에서 오는 요청을 허용하는 설정을 적용하는 부분입니다. CORS 미들웨어는 웹 브라우저가 서로 다른 출처에서 오는 요청을 차단하지 않고 허용하도록 도와줍니다.
이 코드의 목적은 http://localhost:3000에서 오는 요청을 허용하고, 그 요청이 자격 증명을 포함할 수 있도록 하며, POST 메서드와 Content-Type 헤더를 사용할 수 있게 설정하는 것입니다. 이를 통해 로컬에서 실행되는 프런트엔드 애플리케이션이 FastAPI 백엔드와 원활하게 통신할 수 있도록 합니다.
[ ChatOpenAI라는 클래스를 사용하여 LM을 설정 ]
이 코드는 ChatOpenAI라는 클래스를 사용하여 Language Model(LM)을 설정하는 부분입니다. 여기서는 ChatOpenAI를 인스턴스화하여 로컬 서버에서 실행되는 언어 모델 API와 상호 작용하도록 설정하고 있습니다. 이 인스턴스를 통해 AI 챗봇의 대화를 처리하는 역할을 합니다.
(2) Chat.jsx
(3) Situation.jsx
🔶 쿼리 스트링으로 짰을 때, 실행 결과이다.
응답을 받는데 시간이 꽤 걸리는데, 참을성을 가지고 보시오!

실행결과가 진행될 때, 상단의 url을 확인해보면, 채팅페이지로 넘어갈때 다음과 같은 url로 설정되는 걸 확인할 수 있다.
상단의 url 보이시나요? 복사 붙혀넣기 했더니 한글 대신 이상한 문자들이 포함되는 걸 확인할 수 있을텐데, 한글이 url에 포함될 때,
url 인코딩이라는 과정을 거치게 되기 때문입니다.url 인코딩은 특수 문자 및 비-ASCII 문자를 퍼센트 기호(%)와 16진수 코드로 변환하는 방법입니다. 이로 인해 한글 등 비-ASCII 문자가 포함된 URL이 인코딩된 형태로 보이게 됩니다.예를 들어, "시장 놀이"는 "%EC%8B%9C%EC%9E%A5+%EB%86%80%EC%9D%B4"로 인코딩됩니다. 이는 브라우저가 한글을 URL에 포함할 때 자동으로 인코딩하기 때문입니다.
암튼 URL 인코딩이 중요한게 아니라, 제가 하고 싶은말은 저렇게 쿼리 스트링 방식으로 다른 프롬프트를 제공할 수 있다 이말입니다.
✏️ 해당 이슈와 관련되서 밸류업 해야할 사항들