React

[React] AI 건강 상담 페이지 코드 리뷰

Puft 2024. 10. 7. 21:11

안녕하세요!

 

오늘은 NutriGuide의 AI 건강 상담 페이지의 코드를 리뷰해보려고 합니다.

 

AI 건강 상담 페이지는 Open AI에서는 16개 이상의 LLM (Large Language Model)API 형태로 제공하는데

 

이 API를 활용해서 질문에 대한 답을 받을 수 있는 페이지 입니다. 

 

GPT-4o mini Model은 큰 규모의 파라미터와 저렴한 비용으로 본 AI 건강 상담에 활용하기 적합했습니다.

 

AI 건강 상담 페이지

 

 

가이드라인 보기 버튼을 누르면 효율적인 답변을 얻을 수 있는

 

가이드라인이 표시됩니다.

 

인풋창에 질문을 입력하고 질문하기 버튼을 누르면

 

ChatGPT 모델이 답변을 제공합니다.

 

다음은 서버의 API 코드입니다.

 

 


@app.route('/ask', methods=['POST'])
def ask_question():
    data = request.get_json()
    user_question = data['question']

    # Use the user_question as the prompt for OpenAI Completion
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": user_question}
        ],
        temperature=0.7,
        max_tokens=2000
    )

    # Extract the generated answer from the OpenAI response
    answer = response.choices[0].message['content'].strip()

    return jsonify({'answer': answer})

 

 

기본 적으로 GPT에서 제공하는 코드를 활용했습니다.

 

질문을 클라이언트에서 받아와서

 

답변을 다시 클라이언트로 보내줍니다.

 

import React, { useState } from 'react';
import '../css/QuestionPage.css';
import { Tooltip } from 'antd';
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons';
import yourImage from '../img/robot.png'; // 이미지 경로를 가져옵니다.
import '@fortawesome/fontawesome-free/css/all.min.css'; // FontAwesome CSS 추가

const QuestionPage = () => {
    const [question, setQuestion] = useState('');
    const [answer, setAnswer] = useState('');
    const [showAnswer, setShowAnswer] = useState(false); // 클릭 여부 상태
    const [loading, setLoading] = useState(false); // 로딩 상태
    const [showGuideline, setShowGuideline] = useState(false); // 가이드라인 표시 상태

    const renderTooltipContent = () => (
        <span>
            ChatGPT 모델이 답변을 제공해줍니다!
        </span>
    );

    const handleQuestionChange = (e) => {
        setQuestion(e.target.value);
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        setLoading(true); // 데이터를 가져오는 동안 로딩 상태를 true로 설정

        try {
            const response = await fetch('http://localhost:5000/ask', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ question }),
            });

            if (!response.ok) {
                throw new Error('Network response was not ok');
            }

            const data = await response.json();
            setAnswer(data.answer);
            setShowAnswer(true); // 버튼 클릭 시 답변 표시
        } catch (error) {
            console.error('Error:', error);
        } finally {
            setLoading(false); // 데이터 가져오기가 완료되면 로딩 상태를 false로 설정
        }
    };

    const toggleGuideline = () => {
        setShowGuideline(!showGuideline); // 가이드라인 표시 상태를 토글
    };

    return (
        <div className='question-page'>
            <div className="header">
                <h1>AI에게 건강 조언을 받아보세요!</h1>
            </div>
            <div className='form-and-guideline'>
                <div className='right-section'>
                    <button onClick={toggleGuideline} className='question-button-top'>
                        가이드라인 보기
                    </button>
                    {showGuideline && (
                        <div className='guideline'>
                            <h2>질문 작성 가이드라인:</h2>
                            <ul>
                                <li>
                                    <h3 className='bigtitle'>
                                        명확한 질문
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "몸에 좋은 영양제를 추천해주세요!"
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "저는 자주 피로감을 느끼는데, 피로 회복에 도움이 될 만한 영양제를 추천해주실 수 있나요?"
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        배경 정보 제시
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "스트레스 해소에 도움되는 영양제를 알려주세요."
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "저는 최근 몇 달 동안 스트레스와 과도한 업무로 인해 심한 피로를 느끼고 있습니다. 이런 상태를 개선하기 위해 효과적인 영양제가 있을까요?"
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        단계적인 질문
                                    </h3>
                                    <div className='question-content'>
                                        <span className="number-icon">1</span> {/* 숫자 1 아이콘 */}
                                        <div className='question1'>
                                            "장 건강에 좋은 영양제를 추천해주세요"
                                        </div>
                                    </div>
                                    <div className='question2'>
                                        "락토바실러스(Lactobacillus)나 비피돌박테리움(Bifidobacterium) 등의 프로바이오틱스가 함유된 제품을 선택하시면 좋을 것 같습니다."
                                    </div>
                                    <div className='question-content'>
                                        <span className="number-icon">2</span> {/* 숫자 2 아이콘 */}
                                        <div className='question1'>
                                            "복용 방법을 알려주세요."
                                        </div>
                                    </div>
                                    <div className='question2'>
                                        "하루에 1~2회, 식사 전 또는 식사 후에 물과 함께 섭취하는 것이 좋습니다."
                                    </div>
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        원하는 답변의 예시 제공
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "불면증에 좋은 영양제를 추천해 주세요."
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "불면증에 도움이 되는 영양제를 추천해 주세요. 그런데 어떤 성분이 포함된 제품이 효과적인지, 하루에 얼마만큼 복용해야 하는지와 같은 구체적인 정보를 포함해 주세요."
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                            </ul>
                        </div>
                    )}
                    <div className='form-answer-container'>
                        <form className='question-form' onSubmit={handleSubmit}>
                            <label className='question-label'>
                                질문을 입력하세요:
                            </label>
                            <input
                                className='question-input'
                                type='text'
                                value={question}
                                onChange={handleQuestionChange}
                                placeholder='질문을 입력하세요...'
                                required
                            />
                            <button type='submit' className='question-button'>
                                질문하기
                            </button>
                        </form>
                       
                <div className='under-section'>
                {loading && <div className='loading'>질문을 처리하는 중...</div>}
                        {showAnswer && (
                            <div className='answer-container'>
                                <h3>답변:</h3>
                                <p className='answer'>{answer}</p>
                            </div>
                        )}
                    </div>
                </div>
                    <Tooltip placement='top' title={renderTooltipContent}>
                        <img src={yourImage} className='logo-image' alt='로고 이미지' />
                    </Tooltip>
                </div>
            </div>
        </div>
    );
};

export default QuestionPage;

 

다음은 클라이언트 코드입니다.

 

   const [question, setQuestion] = useState('');
    const [answer, setAnswer] = useState('');
    const [showAnswer, setShowAnswer] = useState(false); // 클릭 여부 상태
    const [loading, setLoading] = useState(false); // 로딩 상태
    const [showGuideline, setShowGuideline] = useState(false); // 가이드라인 표시 상태

 

state를 사용해서 질문과 답변을 설정하고, 열고 닫을 수 있는 상태를 설정합니다.

 

 try {
            const response = await fetch('http://localhost:5000/ask', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ question }),
            });

            if (!response.ok) {
                throw new Error('Network response was not ok');
            }

            const data = await response.json();
            setAnswer(data.answer);
            setShowAnswer(true); // 버튼 클릭 시 답변 표시
        } catch (error) {
            console.error('Error:', error);
        } finally {
            setLoading(false); // 데이터 가져오기가 완료되면 로딩 상태를 false로 설정
        }

 

다음은 API코드입니다.

 

서버에 answer API를 요청해서

 

실패하면 에러를 출력하고 

 

성공하면 답변을 가져옵니다.

 

   <div className='question-page'>
            <div className="header">
                <h1>AI에게 건강 조언을 받아보세요!</h1>
            </div>
            <div className='form-and-guideline'>
                <div className='right-section'>
                    <button onClick={toggleGuideline} className='question-button-top'>
                        가이드라인 보기
                    </button>
                    {showGuideline && (
                        <div className='guideline'>
                            <h2>질문 작성 가이드라인:</h2>
                            <ul>
                                <li>
                                    <h3 className='bigtitle'>
                                        명확한 질문
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "몸에 좋은 영양제를 추천해주세요!"
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "저는 자주 피로감을 느끼는데, 피로 회복에 도움이 될 만한 영양제를 추천해주실 수 있나요?"
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        배경 정보 제시
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "스트레스 해소에 도움되는 영양제를 알려주세요."
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "저는 최근 몇 달 동안 스트레스와 과도한 업무로 인해 심한 피로를 느끼고 있습니다. 이런 상태를 개선하기 위해 효과적인 영양제가 있을까요?"
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        단계적인 질문
                                    </h3>
                                    <div className='question-content'>
                                        <span className="number-icon">1</span> {/* 숫자 1 아이콘 */}
                                        <div className='question1'>
                                            "장 건강에 좋은 영양제를 추천해주세요"
                                        </div>
                                    </div>
                                    <div className='question2'>
                                        "락토바실러스(Lactobacillus)나 비피돌박테리움(Bifidobacterium) 등의 프로바이오틱스가 함유된 제품을 선택하시면 좋을 것 같습니다."
                                    </div>
                                    <div className='question-content'>
                                        <span className="number-icon">2</span> {/* 숫자 2 아이콘 */}
                                        <div className='question1'>
                                            "복용 방법을 알려주세요."
                                        </div>
                                    </div>
                                    <div className='question2'>
                                        "하루에 1~2회, 식사 전 또는 식사 후에 물과 함께 섭취하는 것이 좋습니다."
                                    </div>
                                </li>
                                <li>
                                    <h3 className='bigtitle'>
                                        원하는 답변의 예시 제공
                                    </h3>
                                    <div className='question-content'>
                                        <div className='question1'>
                                            "불면증에 좋은 영양제를 추천해 주세요."
                                        </div>
                                        <CloseCircleOutlined className='icon' />
                                    </div>
                                    <div className='question2'>
                                        "불면증에 도움이 되는 영양제를 추천해 주세요. 그런데 어떤 성분이 포함된 제품이 효과적인지, 하루에 얼마만큼 복용해야 하는지와 같은 구체적인 정보를 포함해 주세요."
                                    </div>
                                    <CheckCircleOutlined className='check-icon' />
                                </li>
                            </ul>
                        </div>

 

가이드 라인의 문구는 하드코딩하였습니다.

 

antD의 체크 아이콘을 활용하여 가이드라인의 올바른 예시와

 

올바르지 않은 예시를 구분하였습니다.

 

.guideline {
    width: 100%; /* 전체 너비 사용 */
    padding-right: 20px; /* 폼과 가이드라인 사이의 간격 추가 */
    background-color: #f2f8f3;
    padding: 40px;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    margin-top: 20px; /* 가이드라인과 다른 요소 사이의 여백 추가 */
    margin-bottom: 50px;
}

/* 가이드라인 수정 */
.guideline ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.guideline li {
    display: flex;
    flex-direction: column;
    margin-bottom: 20px;
}

.bigtitle {
    border-bottom: 2px solid #333; /* Border on the bottom */
    padding-bottom: 10px;
    font-size: 1.25rem; /* Adjusted font size */
    font-weight: 700;
    color: #2c3e50;
    margin-bottom: 10px;
    margin-top: 10px;
}

 

가이드라인의 경우 

 

bigtitle과 내용 구문을 구분하여 디자인했습니다.

 

 <div className='form-answer-container'>
                        <form className='question-form' onSubmit={handleSubmit}>
                            <label className='question-label'>
                                질문을 입력하세요:
                            </label>
                            <input
                                className='question-input'
                                type='text'
                                value={question}
                                onChange={handleQuestionChange}
                                placeholder='질문을 입력하세요...'
                                required
                            />
                            <button type='submit' className='question-button'>
                                질문하기
                            </button>
                        </form>
                       
                <div className='under-section'>
                {loading && <div className='loading'>질문을 처리하는 중...</div>}
                        {showAnswer && (
                            <div className='answer-container'>
                                <h3>답변:</h3>
                                <p className='answer'>{answer}</p>
                            </div>
                        )}
                    </div>
                </div>
                    <Tooltip placement='top' title={renderTooltipContent}>
                        <img src={yourImage} className='logo-image' alt='로고 이미지' />
                    </Tooltip>
                </div>
            </div>

 

질문을 입력받으면 question의 상태를 변경하여 

 

   const handleQuestionChange = (e) => {
        setQuestion(e.target.value);
    };

 

question의 값을 변경합니다.

 

그 후 서버에서 받아 온 answer을 답변 container에 출력합니다.

 

 

 

 

오늘은 AI 건강 상담 페이지의 코드를 리뷰해보았습니다.

 

개발을 하면서 chatGPT의 API를 실제로 서버에서 가져와서

 

답변을 클라이언트로 넘겨 받아서 출력되는 것을

 

눈으로 확인해서 흥미로웠던 것 같습니다.

 

읽어주셔서 감사합니다!