Skip to main content
Glama

FSS Pension MCP Server

by ddoriboo
main.html50.3 kB
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>AI 연금 전문가 | 맞춤형 연금 진단 및 상담</title> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- Font Awesome --> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> <style> :root { --primary-color: #0d6efd; --success-color: #198754; --info-color: #0dcaf0; --warning-color: #ffc107; --danger-color: #dc3545; --bg-primary: #f8f9fa; --bg-secondary: #ffffff; --text-primary: #212529; --text-secondary: #6c757d; --border-color: #dee2e6; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; } .app-container { max-width: 430px; margin: 0 auto; min-height: 100vh; background: var(--bg-secondary); box-shadow: 0 0 20px rgba(0,0,0,0.1); } /* 헤더 */ .header { background: linear-gradient(135deg, var(--primary-color), #0056b3); padding: 20px 16px; text-align: center; color: white; position: relative; } .header-title { font-size: 24px; font-weight: 700; margin-bottom: 8px; } .header-subtitle { font-size: 14px; opacity: 0.9; } .nav-links { position: absolute; top: 20px; right: 16px; } .nav-links a { color: white; text-decoration: none; font-size: 14px; padding: 8px 12px; border-radius: 20px; background: rgba(255,255,255,0.2); transition: all 0.3s ease; } .nav-links a:hover { background: rgba(255,255,255,0.3); } /* 진단 섹션 */ .diagnosis-section { padding: 24px 16px; background: var(--bg-secondary); } .section-title { font-size: 20px; font-weight: 700; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; } .diagnosis-card { background: var(--bg-secondary); border: 2px solid var(--border-color); border-radius: 16px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); transition: all 0.3s ease; } .diagnosis-card:hover { border-color: var(--primary-color); transform: translateY(-2px); } .form-row { display: flex; gap: 12px; margin-bottom: 16px; } .form-group { flex: 1; } .form-label { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px; display: block; } .form-control { width: 100%; padding: 12px 16px; border: 2px solid var(--border-color); border-radius: 12px; font-size: 16px; transition: all 0.3s ease; } .form-control:focus { border-color: var(--primary-color); outline: none; box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.1); } .btn-diagnose { width: 100%; padding: 16px; background: linear-gradient(135deg, var(--success-color), #146c43); color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; margin-top: 16px; } .btn-diagnose:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(25, 135, 84, 0.3); } /* 채팅 섹션 */ .chat-section { background: var(--bg-primary); padding: 0; flex: 1; } .chat-header { background: var(--bg-secondary); padding: 16px; border-bottom: 1px solid var(--border-color); text-align: center; } .chat-container { height: 500px; display: flex; flex-direction: column; background: var(--bg-secondary); } .chat-messages { flex: 1; padding: 16px; overflow-y: auto; background: #fafbfc; } .ai-message, .user-message { margin-bottom: 16px; animation: fadeInUp 0.3s ease; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .message-content { display: inline-block; max-width: 85%; padding: 12px 16px; border-radius: 18px; font-size: 15px; line-height: 1.4; } .ai-message { text-align: left; } .ai-message .message-content { background: linear-gradient(135deg, #e3f2fd, #f3e5f5); color: #333; border: 1px solid #e1f5fe; } .user-message { text-align: right; } .user-message .message-content { background: linear-gradient(135deg, var(--primary-color), #0056b3); color: white; } .message-timestamp { font-size: 12px; color: var(--text-secondary); margin-top: 4px; } .chat-input-container { padding: 16px; background: white; border-top: 1px solid var(--border-color); } .input-group { display: flex; gap: 8px; } .chat-input { flex: 1; padding: 12px 16px; border: 2px solid var(--border-color); border-radius: 25px; font-size: 16px; } .chat-input:focus { border-color: var(--primary-color); outline: none; } .send-btn { padding: 12px 20px; background: var(--primary-color); color: white; border: none; border-radius: 25px; cursor: pointer; transition: all 0.3s ease; } .send-btn:hover { background: #0056b3; transform: scale(1.05); } /* 빠른 질문 버튼 - 중장년층 친화적 */ .question-interface { padding: 20px 16px; background: var(--bg-secondary); border-top: 1px solid var(--border-color); } .question-category { margin-bottom: 20px; } .category-label { font-size: 16px; font-weight: 600; color: var(--text-primary); margin-bottom: 12px; display: flex; align-items: center; gap: 8px; } .category-select { width: 100%; padding: 14px 16px; border: 2px solid var(--border-color); border-radius: 12px; font-size: 16px; background: white; cursor: pointer; transition: all 0.3s ease; } .category-select:focus { border-color: var(--primary-color); outline: none; box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.1); } .quick-questions-grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 16px; } .quick-question-btn { padding: 16px 20px; background: linear-gradient(135deg, #f8f9fa, #e9ecef); border: 2px solid var(--border-color); border-radius: 12px; font-size: 15px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-align: left; display: flex; align-items: center; gap: 12px; min-height: 60px; } .quick-question-btn:hover { border-color: var(--primary-color); background: linear-gradient(135deg, #e3f2fd, #f3e5f5); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); } .question-icon { font-size: 24px; width: 32px; text-align: center; } .question-text { flex: 1; line-height: 1.4; } /* 후속 질문 버튼들 */ .follow-up-questions { margin-top: 16px; padding: 16px; background: #f8f9fa; border-radius: 12px; border-left: 4px solid var(--primary-color); } .follow-up-title { font-size: 14px; font-weight: 600; color: var(--text-secondary); margin-bottom: 12px; display: flex; align-items: center; gap: 6px; } .follow-up-btn { display: inline-block; padding: 8px 12px; margin: 4px 6px 4px 0; background: white; border: 1px solid var(--border-color); border-radius: 20px; font-size: 13px; cursor: pointer; transition: all 0.3s ease; text-decoration: none; color: var(--text-primary); } .follow-up-btn:hover { background: var(--primary-color); color: white; transform: translateY(-1px); } /* 타이핑 인디케이터 */ .typing-indicator { display: flex; align-items: center; padding: 12px 16px; margin-bottom: 16px; } .typing-dots { display: flex; gap: 4px; margin-left: 8px; } .typing-dot { width: 8px; height: 8px; background-color: var(--text-secondary); border-radius: 50%; animation: typing 1.4s infinite ease-in-out; } .typing-dot:nth-child(1) { animation-delay: -0.32s; } .typing-dot:nth-child(2) { animation-delay: -0.16s; } @keyframes typing { 0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; } 40% { transform: scale(1); opacity: 1; } } /* 중장년층 친화적 UI 개선 */ .form-control, .category-select { font-size: 17px; /* iOS에서 확대 방지 */ -webkit-appearance: none; -moz-appearance: none; appearance: none; } .category-select { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 12px center; background-size: 16px 12px; } /* 접근성 개선 */ .quick-question-btn:focus, .follow-up-btn:focus, .btn-diagnose:focus, .send-btn:focus { outline: 3px solid #4fc3f7; outline-offset: 2px; } /* 터치 친화적 크기 */ .quick-question-btn, .follow-up-btn, .btn-diagnose, .send-btn, .category-select { min-height: 48px; /* 터치하기 쉬운 최소 크기 */ } /* 로딩 상태 표시 */ .btn-loading { opacity: 0.7; cursor: not-allowed; position: relative; } .btn-loading::after { content: ''; position: absolute; width: 16px; height: 16px; margin: auto; border: 2px solid transparent; border-top-color: #ffffff; border-radius: 50%; animation: spin 1s ease infinite; top: 50%; left: 50%; transform: translate(-50%, -50%); } @keyframes spin { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } } /* 메시지 스타일 개선 */ .message-list { padding-left: 20px; margin-bottom: 16px; } .message-list li { padding: 4px 0; line-height: 1.5; } .message-heading { border-bottom: 1px solid #e9ecef; padding-bottom: 8px; } .code-block { font-family: 'Courier New', monospace; white-space: pre-wrap; overflow-x: auto; } /* 반응형 */ @media (min-width: 768px) { .app-container { max-width: 800px; } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .quick-questions-grid { grid-template-columns: repeat(2, 1fr); } .question-text { font-size: 16px; } .follow-up-btn { font-size: 14px; padding: 10px 16px; } } @media (min-width: 1024px) { .quick-questions-grid { grid-template-columns: repeat(2, 1fr); } } /* 다크모드 대응 */ @media (prefers-color-scheme: dark) { :root { --bg-primary: #1a1a1a; --bg-secondary: #2d2d2d; --text-primary: #ffffff; --text-secondary: #b0b0b0; --border-color: #404040; } } /* 고대비 모드 대응 */ @media (prefers-contrast: high) { .quick-question-btn, .follow-up-btn { border-width: 3px; } .form-control:focus, .category-select:focus { border-width: 3px; } } /* 애니메이션 줄이기 선호 시 */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } /* 숨김/표시 클래스 */ .section-hidden { display: none; } .section-visible { display: block; } </style> </head> <body> <div class="app-container"> <!-- 헤더 --> <div class="header"> <div class="nav-links"> <a href="/dashboard"><i class="fas fa-chart-line me-1"></i>대시보드</a> </div> <div class="header-title">🤖 AI 연금 전문가</div> <div class="header-subtitle">맞춤형 연금 진단 및 전문 상담 서비스</div> </div> <!-- 진단 섹션 --> <div class="diagnosis-section" id="diagnosisSection"> <div class="section-title"> <i class="fas fa-stethoscope text-success"></i> 연금 진단 및 분석 </div> <div class="diagnosis-card"> <div class="form-row"> <div class="form-group"> <label class="form-label">나이</label> <input type="number" class="form-control" id="userAge" placeholder="35" min="20" max="80"> </div> <div class="form-group"> <label class="form-label">월소득 (만원)</label> <input type="number" class="form-control" id="userIncome" placeholder="500" min="100" max="10000"> </div> </div> <div class="form-row"> <div class="form-group"> <label class="form-label">투자 성향</label> <select class="form-control" id="userRisk"> <option value="conservative">안정형 (원금보장 중시)</option> <option value="moderate" selected>균형형 (적정 수익 추구)</option> <option value="aggressive">적극형 (고수익 추구)</option> </select> </div> <div class="form-group"> <label class="form-label">목표 은퇴나이</label> <input type="number" class="form-control" id="userRetirement" placeholder="65" min="50" max="80"> </div> </div> <div class="form-group"> <label class="form-label">현재 연금 준비 상황</label> <select class="form-control" id="currentPreparation"> <option value="none">아직 준비하지 않음</option> <option value="national">국민연금만 가입</option> <option value="some">일부 연금상품 가입 중</option> <option value="well">충분히 준비 중</option> </select> </div> <button class="btn-diagnose" onclick="startDiagnosis()"> <i class="fas fa-search me-2"></i>AI 진단 시작하기 </button> </div> </div> <!-- 채팅 섹션 --> <div class="chat-section section-hidden" id="chatSection"> <div class="chat-header"> <div class="section-title"> <i class="fas fa-comments text-primary"></i> AI 전문가와 실시간 상담 </div> <div style="font-size: 14px; color: var(--text-secondary);"> 개인 맞춤형 연금 상담을 받아보세요 </div> </div> <div class="chat-container"> <div class="chat-messages" id="chatMessages"> <!-- 초기 AI 메시지 --> <div class="ai-message"> <div class="message-content"> <i class="fas fa-robot me-2"></i> 안녕하세요! 저는 AI 연금 전문가입니다. 진단 결과를 바탕으로 맞춤형 연금 상담을 도와드리겠습니다. 🎯 </div> <div class="message-timestamp">방금 전</div> </div> </div> <div class="chat-input-container"> <div class="input-group"> <input type="text" class="chat-input" id="chatInput" placeholder="연금 관련 질문을 입력해주세요..." maxlength="500"> <button class="send-btn" id="sendChatBtn"> <i class="fas fa-paper-plane"></i> </button> </div> </div> </div> <!-- 질문 인터페이스 - 중장년층 친화적 --> <div class="question-interface"> <!-- 질문 카테고리 선택 --> <div class="question-category"> <div class="category-label"> <i class="fas fa-list-ul text-primary"></i> 질문 주제를 선택해주세요 </div> <select class="category-select" id="questionCategory"> <option value="">-- 궁금한 주제를 선택하세요 --</option> <option value="products">🏷️ 연금상품 추천</option> <option value="fees">💰 수수료 비교</option> <option value="planning">📊 은퇴 계획</option> <option value="tax">🧾 세제 혜택</option> <option value="process">📝 가입 방법</option> <option value="general">❓ 기타 문의</option> </select> </div> <!-- 카테고리별 질문 옵션들 --> <div class="quick-questions-grid" id="categoryQuestions"> <!-- 기본 추천 질문들 --> <button class="quick-question-btn" data-question="내 상황에 맞는 연금 상품을 추천해주세요"> <span class="question-icon">🎯</span> <span class="question-text">내 상황에 맞는<br>연금 상품 추천받기</span> </button> <button class="quick-question-btn" data-question="수수료가 가장 낮은 연금 상품 5개를 알려주세요"> <span class="question-icon">💸</span> <span class="question-text">수수료 최저가<br>상품 찾아보기</span> </button> <button class="quick-question-btn" data-question="은퇴 후 월 300만원으로 생활하려면 얼마나 준비해야 하나요?"> <span class="question-icon">🏠</span> <span class="question-text">은퇴 후 생활비<br>계산해보기</span> </button> <button class="quick-question-btn" data-question="연금저축과 IRP의 차이점을 쉽게 설명해주세요"> <span class="question-icon">📚</span> <span class="question-text">연금 상품 종류<br>차이점 알아보기</span> </button> </div> <!-- 텍스트 입력 대안 --> <div style="margin-top: 20px; text-align: center; color: var(--text-secondary); font-size: 14px;"> <i class="fas fa-keyboard me-1"></i> 또는 아래에 직접 질문을 입력하세요 </div> </div> </div> </div> <!-- JavaScript --> <script> class PensionAIApp { constructor() { this.apiBase = '/api'; this.userId = this.generateUserId(); this.userProfile = null; this.init(); } generateUserId() { return 'user_' + Math.random().toString(36).substr(2, 9); } init() { this.setupEventListeners(); } setupEventListeners() { // 채팅 이벤트 const chatInput = document.getElementById('chatInput'); const sendBtn = document.getElementById('sendChatBtn'); const categorySelect = document.getElementById('questionCategory'); // 전송 버튼 클릭 sendBtn.addEventListener('click', () => { this.sendMessage(); }); // Enter 키로 메시지 전송 chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this.sendMessage(); } }); // 카테고리 선택 이벤트 categorySelect.addEventListener('change', (e) => { this.updateQuestionsByCategory(e.target.value); }); // 빠른 질문 버튼들 (동적으로 생성되므로 이벤트 위임 사용) document.addEventListener('click', (e) => { if (e.target.closest('.quick-question-btn')) { const btn = e.target.closest('.quick-question-btn'); const question = btn.getAttribute('data-question'); this.askQuestion(question); } if (e.target.closest('.follow-up-btn')) { const btn = e.target.closest('.follow-up-btn'); const question = btn.getAttribute('data-question'); this.askQuestion(question); } }); } // 카테고리별 질문 옵션 정의 getQuestionsByCategory(category) { const questions = { 'products': [ { icon: '🎯', text: '내 나이와 소득에 맞는<br>상품 추천받기', question: '내 나이와 월소득에 맞는 연금상품을 추천해주세요' }, { icon: '🏆', text: '인기 연금상품<br>TOP 5 보기', question: '가장 인기있는 연금상품 5개를 추천해주세요' }, { icon: '🛡️', text: '안전한 원금보장<br>상품 찾기', question: '원금이 보장되는 안전한 연금상품을 알려주세요' }, { icon: '📈', text: '고수익 연금상품<br>알아보기', question: '수익률이 높은 연금상품들을 비교해주세요' } ], 'fees': [ { icon: '💸', text: '수수료 최저가<br>상품 TOP 5', question: '수수료가 가장 낮은 연금상품 5개를 알려주세요' }, { icon: '🔍', text: '수수료 비교<br>상세 분석', question: '연금상품 수수료를 상세히 비교 분석해주세요' }, { icon: '💰', text: '수수료 절약<br>방법 알아보기', question: '연금 가입 시 수수료를 절약하는 방법을 알려주세요' }, { icon: '📊', text: '회사별 수수료<br>순위 보기', question: '운용사별 수수료 순위를 보여주세요' } ], 'planning': [ { icon: '🏠', text: '은퇴 후 생활비<br>계산하기', question: '은퇴 후 월 300만원으로 생활하려면 얼마나 준비해야 하나요?' }, { icon: '📅', text: '연령별 은퇴<br>준비 전략', question: '내 나이에 맞는 은퇴 준비 전략을 알려주세요' }, { icon: '💎', text: '목표 은퇴자금<br>설정하기', question: '적정 은퇴자금을 계산하는 방법을 알려주세요' }, { icon: '⏰', text: '은퇴 시기별<br>준비 방법', question: '은퇴까지 남은 기간별 준비 방법을 알려주세요' } ], 'tax': [ { icon: '🧾', text: '연금저축<br>세제혜택', question: '연금저축의 세제혜택을 자세히 알려주세요' }, { icon: '💳', text: 'IRP 세액공제<br>한도 확인', question: 'IRP 세액공제 한도와 혜택을 알려주세요' }, { icon: '📋', text: '연말정산<br>신고 방법', question: '연금저축 연말정산 신고 방법을 알려주세요' }, { icon: '⚖️', text: '일반형 vs 세액공제형<br>비교', question: '일반형과 세액공제형의 차이점을 알려주세요' } ], 'process': [ { icon: '📝', text: '연금 가입<br>절차 안내', question: '연금상품 가입 절차를 단계별로 알려주세요' }, { icon: '📄', text: '필요 서류<br>확인하기', question: '연금 가입 시 필요한 서류를 알려주세요' }, { icon: '🏪', text: '가입 장소<br>추천받기', question: '연금상품을 어디서 가입하는 것이 좋은지 알려주세요' }, { icon: '🔄', text: '기존 연금<br>이전 방법', question: '다른 회사 연금을 이전하는 방법을 알려주세요' } ], 'general': [ { icon: '❓', text: '연금저축 vs IRP<br>차이점', question: '연금저축과 IRP의 차이점을 쉽게 설명해주세요' }, { icon: '🔮', text: '연금 트렌드<br>최신 정보', question: '최신 연금 시장 트렌드를 알려주세요' }, { icon: '⚠️', text: '연금 가입 시<br>주의사항', question: '연금 가입 시 주의해야 할 점들을 알려주세요' }, { icon: '🎓', text: '연금 기초<br>용어 설명', question: '연금 관련 기본 용어들을 쉽게 설명해주세요' } ] }; return questions[category] || []; } updateQuestionsByCategory(category) { const questionsContainer = document.getElementById('categoryQuestions'); if (!category) { // 기본 추천 질문들 표시 questionsContainer.innerHTML = ` <button class="quick-question-btn" data-question="내 상황에 맞는 연금 상품을 추천해주세요"> <span class="question-icon">🎯</span> <span class="question-text">내 상황에 맞는<br>연금 상품 추천받기</span> </button> <button class="quick-question-btn" data-question="수수료가 가장 낮은 연금 상품 5개를 알려주세요"> <span class="question-icon">💸</span> <span class="question-text">수수료 최저가<br>상품 찾아보기</span> </button> <button class="quick-question-btn" data-question="은퇴 후 월 300만원으로 생활하려면 얼마나 준비해야 하나요?"> <span class="question-icon">🏠</span> <span class="question-text">은퇴 후 생활비<br>계산해보기</span> </button> <button class="quick-question-btn" data-question="연금저축과 IRP의 차이점을 쉽게 설명해주세요"> <span class="question-icon">📚</span> <span class="question-text">연금 상품 종류<br>차이점 알아보기</span> </button> `; return; } const questions = this.getQuestionsByCategory(category); questionsContainer.innerHTML = questions.map(q => ` <button class="quick-question-btn" data-question="${q.question}"> <span class="question-icon">${q.icon}</span> <span class="question-text">${q.text}</span> </button> `).join(''); } askQuestion(question) { const chatInput = document.getElementById('chatInput'); chatInput.value = question; this.sendMessage(); } async startDiagnosis() { // 사용자 입력 수집 const age = parseInt(document.getElementById('userAge').value); const income = parseInt(document.getElementById('userIncome').value); const risk = document.getElementById('userRisk').value; const retirement = parseInt(document.getElementById('userRetirement').value); const preparation = document.getElementById('currentPreparation').value; // 입력 검증 if (!age || !income || !retirement) { alert('모든 필수 정보를 입력해주세요.'); return; } if (age < 20 || age > 80) { alert('나이는 20세 ~ 80세 사이로 입력해주세요.'); return; } // 사용자 프로필 저장 this.userProfile = { age: age, monthly_income: income, risk_preference: risk, target_retirement_age: retirement, current_preparation: preparation }; // 진단 결과 생성 const diagnosisMessage = this.generateDiagnosisMessage(); // 채팅 섹션으로 전환 this.showChatSection(); // AI 진단 메시지 추가 setTimeout(() => { this.addMessageToChat(diagnosisMessage, 'ai'); }, 500); } generateDiagnosisMessage() { const { age, monthly_income, risk_preference, target_retirement_age, current_preparation } = this.userProfile; const yearsToRetirement = target_retirement_age - age; const riskMap = { 'conservative': '안정형', 'moderate': '균형형', 'aggressive': '적극형' }; const prepMap = { 'none': '준비 없음', 'national': '국민연금만', 'some': '일부 준비', 'well': '충분히 준비' }; return `📋 **연금 진단 결과** **기본 정보** • 나이: ${age}세 (은퇴까지 ${yearsToRetirement}년) • 월소득: ${monthly_income}만원 • 투자성향: ${riskMap[risk_preference]} • 현재 준비상황: ${prepMap[current_preparation]} **맞춤형 분석** ${this.getPersonalizedAnalysis()} 이제 구체적인 질문을 해주시면 더 자세한 상담을 도와드리겠습니다! 💪`; } getPersonalizedAnalysis() { const { age, monthly_income, target_retirement_age, current_preparation } = this.userProfile; const yearsToRetirement = target_retirement_age - age; let analysis = ''; // 나이대별 분석 if (age < 30) { analysis += '• 젊은 나이의 장점을 활용해 장기 투자 전략이 유리합니다.\n'; } else if (age < 40) { analysis += '• 안정적인 자산 형성이 중요한 시기입니다.\n'; } else if (age < 50) { analysis += '• 은퇴 준비를 본격적으로 시작해야 하는 중요한 시기입니다.\n'; } else { analysis += '• 보수적인 투자와 함께 빠른 준비가 필요합니다.\n'; } // 소득별 분석 if (monthly_income < 300) { analysis += '• 소득 대비 효율적인 연금 상품 선택이 중요합니다.\n'; } else if (monthly_income < 600) { analysis += '• 적절한 연금 분산 투자를 고려해보세요.\n'; } else { analysis += '• 다양한 연금 상품을 활용한 포트폴리오 구성을 권장합니다.\n'; } // 준비 상황별 분석 if (current_preparation === 'none') { analysis += '• 연금저축부터 시작하여 단계적으로 확대해나가세요.\n'; } else if (current_preparation === 'national') { analysis += '• 개인연금 추가 가입을 통한 보완이 필요합니다.\n'; } return analysis; } showChatSection() { document.getElementById('diagnosisSection').classList.add('section-hidden'); document.getElementById('chatSection').classList.remove('section-hidden'); document.getElementById('chatSection').classList.add('section-visible'); } async sendMessage() { const chatInput = document.getElementById('chatInput'); const message = chatInput.value.trim(); if (!message) return; // 사용자 메시지 표시 this.addMessageToChat(message, 'user'); chatInput.value = ''; // 타이핑 인디케이터 표시 this.showTypingIndicator(); try { // API 호출 const response = await fetch(`${this.apiBase}/ai-chat-with-profile`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: message, user_id: this.userId, user_profile: this.userProfile }) }); const result = await response.json(); // 타이핑 인디케이터 제거 this.hideTypingIndicator(); if (result.success) { this.addMessageToChat(result.response, 'ai'); } else { const errorMsg = result.error || '서버 오류가 발생했습니다'; const detailedError = `죄송합니다. 오류가 발생했습니다: ${errorMsg}`; this.addMessageToChat(detailedError, 'ai', true); console.error('AI Chat Error:', result); } } catch (error) { console.error('메시지 전송 실패:', error); this.hideTypingIndicator(); this.addMessageToChat('네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요.', 'ai', true); } } addMessageToChat(message, sender, isError = false) { const chatMessages = document.getElementById('chatMessages'); const messageDiv = document.createElement('div'); const timestamp = new Date().toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' }); messageDiv.className = sender === 'user' ? 'user-message' : 'ai-message'; if (isError) messageDiv.classList.add('error-message'); const icon = sender === 'user' ? '<i class="fas fa-user me-2"></i>' : '<i class="fas fa-robot me-2"></i>'; // AI 메시지인 경우 후속 질문 버튼들 생성 let followUpButtons = ''; if (sender === 'ai' && !isError) { const followUpQuestions = this.generateFollowUpQuestions(message); if (followUpQuestions.length > 0) { followUpButtons = ` <div class="follow-up-questions"> <div class="follow-up-title"> <i class="fas fa-lightbulb"></i> 이런 것도 궁금하지 않으세요? </div> ${followUpQuestions.map(q => `<button class="follow-up-btn" data-question="${q}">${q}</button>` ).join('')} </div> `; } } messageDiv.innerHTML = ` <div class="message-content"> ${sender === 'ai' ? icon : ''} ${this.formatMessage(message)} ${sender === 'user' ? icon : ''} </div> <div class="message-timestamp">${timestamp}</div> ${followUpButtons} `; chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } // AI 응답에 따른 후속 질문 자동 생성 generateFollowUpQuestions(aiMessage) { const message = aiMessage.toLowerCase(); const followUpQuestions = []; // 키워드 기반 후속 질문 생성 if (message.includes('추천') || message.includes('상품')) { followUpQuestions.push('이 상품들의 수수료는 얼마인가요?'); followUpQuestions.push('가입 방법을 알려주세요'); followUpQuestions.push('다른 연령대 추천상품도 보고 싶어요'); } if (message.includes('수수료') || message.includes('비용')) { followUpQuestions.push('수수료 외에 다른 비용도 있나요?'); followUpQuestions.push('수수료를 줄이는 방법이 있나요?'); followUpQuestions.push('이 회사들의 서비스 품질은 어때요?'); } if (message.includes('은퇴') || message.includes('계획') || message.includes('준비')) { followUpQuestions.push('구체적인 투자 방법을 알려주세요'); followUpQuestions.push('중간에 돈이 필요하면 어떻게 하나요?'); followUpQuestions.push('인플레이션도 고려해야 하나요?'); } if (message.includes('세제') || message.includes('세금') || message.includes('공제')) { followUpQuestions.push('연말정산 시 주의사항이 있나요?'); followUpQuestions.push('세액공제 한도를 늘리는 방법은?'); followUpQuestions.push('일반형과 세액공제형 중 뭐가 좋나요?'); } if (message.includes('가입') || message.includes('신청')) { followUpQuestions.push('온라인으로도 가입할 수 있나요?'); followUpQuestions.push('필요한 서류가 뭐가 있나요?'); followUpQuestions.push('가입 후 변경할 수 있는 것들이 있나요?'); } if (message.includes('차이') || message.includes('비교')) { followUpQuestions.push('내 상황에는 어느 것이 더 좋나요?'); followUpQuestions.push('둘 다 가입해도 되나요?'); followUpQuestions.push('나중에 바꿀 수 있나요?'); } // 기본 후속 질문들 (항상 포함) if (followUpQuestions.length === 0) { followUpQuestions.push('더 자세히 알려주세요'); followUpQuestions.push('다른 방법도 있나요?'); followUpQuestions.push('실제 사례를 들어주세요'); } // 최대 3개까지만 표시 return followUpQuestions.slice(0, 3); } formatMessage(message) { let formatted = message; // 테이블 마크다운 처리 formatted = this.parseMarkdownTable(formatted); // 헤딩 처리 (### ## #) formatted = formatted.replace(/^### (.*$)/gm, '<h3 class="message-heading h6 mt-3 mb-2 text-primary">$1</h3>'); formatted = formatted.replace(/^## (.*$)/gm, '<h2 class="message-heading h5 mt-3 mb-2 text-primary">$1</h2>'); formatted = formatted.replace(/^# (.*$)/gm, '<h1 class="message-heading h4 mt-3 mb-2 text-primary">$1</h1>'); // 리스트 처리 (- 또는 *) formatted = formatted.replace(/^[\-\*] (.+$)/gm, '<li class="mb-1">$1</li>'); formatted = formatted.replace(/(<li.*<\/li>)/s, '<ul class="message-list mb-2">$1</ul>'); // 번호 리스트 처리 (1. 2. 3.) formatted = formatted.replace(/^\d+\. (.+$)/gm, '<li class="mb-1">$1</li>'); formatted = formatted.replace(/(<li.*<\/li>)/s, '<ol class="message-list mb-2">$1</ol>'); // 코드 블록 처리 formatted = formatted.replace(/```(.*?)```/gs, '<div class="code-block bg-light p-2 rounded mb-2"><code>$1</code></div>'); formatted = formatted.replace(/`([^`]+)`/g, '<code class="bg-light px-1 rounded">$1</code>'); // 링크 처리 formatted = formatted.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" class="text-primary">$1</a>'); // 강조 처리 formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong class="text-dark">$1</strong>'); formatted = formatted.replace(/\*(.*?)\*/g, '<em>$1</em>'); // 줄바꿈 처리 formatted = formatted.replace(/\n/g, '<br>'); return formatted; } parseMarkdownTable(text) { const tableRegex = /^\|(.+)\|\s*\n\|[\s\-\|]+\|\s*\n((?:\|.+\|\s*\n?)*)/gm; return text.replace(tableRegex, (match, headerRow, bodyRows) => { // 헤더 파싱 const headers = headerRow.split('|').map(h => h.trim()).filter(h => h); // 바디 행 파싱 const rows = bodyRows.trim().split('\n').map(row => row.split('|').map(cell => cell.trim()).filter(cell => cell) ).filter(row => row.length > 0); // HTML 테이블 생성 let tableHtml = '<div class="table-responsive mt-2 mb-3">'; tableHtml += '<table class="table table-sm table-striped">'; // 헤더 if (headers.length > 0) { tableHtml += '<thead class="table-light"><tr>'; headers.forEach(header => { tableHtml += `<th class="fw-bold">${header}</th>`; }); tableHtml += '</tr></thead>'; } // 바디 if (rows.length > 0) { tableHtml += '<tbody>'; rows.forEach(row => { tableHtml += '<tr>'; row.forEach(cell => { tableHtml += `<td>${cell}</td>`; }); tableHtml += '</tr>'; }); tableHtml += '</tbody>'; } tableHtml += '</table></div>'; return tableHtml; }); } showTypingIndicator() { const chatMessages = document.getElementById('chatMessages'); const typingDiv = document.createElement('div'); typingDiv.id = 'typingIndicator'; typingDiv.className = 'typing-indicator'; typingDiv.innerHTML = ` <i class="fas fa-robot me-2"></i> <span class="typing-dots"> <span class="typing-dot"></span> <span class="typing-dot"></span> <span class="typing-dot"></span> </span> `; chatMessages.appendChild(typingDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } hideTypingIndicator() { const typingIndicator = document.getElementById('typingIndicator'); if (typingIndicator) { typingIndicator.remove(); } } } // 전역 함수 function startDiagnosis() { window.pensionApp.startDiagnosis(); } // 앱 초기화 document.addEventListener('DOMContentLoaded', () => { window.pensionApp = new PensionAIApp(); }); </script> </body> </html>

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ddoriboo/pension-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server