Repository navigation
feat(chatbot): 마이페이지 고객문의 챗봇 구현 #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| import { NextRequest, NextResponse } from 'next/server' | ||
| import { getServerSession } from 'next-auth' | ||
| import { authOptions } from '@/lib/auth' | ||
| import { getSessionUserId } from '@/lib/auth/session' | ||
| import { getChatbotResponse, ChatbotTimeoutError, ChatbotGeminiError } from '@/lib/ai/chatbot' | ||
| import { | ||
| getOrCreateSession, | ||
| getSessionMessages, | ||
| saveMessage, | ||
| clearSession, | ||
| } from '@/domains/common/chatbot.service' | ||
|
|
||
| function mapRole(role: string): 'user' | 'bot' { | ||
| return role.toLowerCase() === 'user' ? 'user' : 'bot' | ||
| } | ||
|
|
||
| export async function POST(req: NextRequest) { | ||
| try { | ||
| const session = await getServerSession(authOptions) | ||
| const userId = getSessionUserId(session) | ||
| if (!userId) { | ||
| return NextResponse.json( | ||
| { success: false, error: '인증이 필요합니다.' }, | ||
| { status: 401 } | ||
| ) | ||
| } | ||
|
|
||
| const body = await req.json() | ||
| const { message } = body as { message: string } | ||
|
|
||
| if (!message || typeof message !== 'string' || message.trim().length === 0) { | ||
| return NextResponse.json( | ||
| { success: false, error: '메시지를 입력해주세요.' }, | ||
| { status: 400 } | ||
| ) | ||
| } | ||
|
|
||
| if (message.length > 500) { | ||
| return NextResponse.json( | ||
| { success: false, error: '메시지는 500자 이내로 입력해주세요.' }, | ||
| { status: 400 } | ||
| ) | ||
| } | ||
|
|
||
| const chatSession = await getOrCreateSession(userId) | ||
| const dbMessages = await getSessionMessages(chatSession.id) | ||
|
|
||
| const history = dbMessages.map((m) => ({ | ||
| role: mapRole(m.role), | ||
| content: m.content, | ||
| })) | ||
|
|
||
| await saveMessage(chatSession.id, 'user', message.trim()) | ||
|
|
||
| const reply = await getChatbotResponse(message.trim(), history) | ||
|
|
||
| await saveMessage(chatSession.id, 'bot', reply) | ||
|
|
||
| return NextResponse.json({ success: true, data: { reply } }) | ||
| } catch (error) { | ||
| console.error('[Chatbot API Error]', error instanceof Error ? error.message : error) | ||
|
|
||
| if (error instanceof ChatbotTimeoutError) { | ||
| return NextResponse.json( | ||
| { success: false, error: '응답 시간이 초과되었습니다. 잠시 후 다시 시도해주세요.' }, | ||
| { status: 504 } | ||
| ) | ||
| } | ||
| if (error instanceof ChatbotGeminiError) { | ||
| return NextResponse.json( | ||
| { success: false, error: 'AI 서비스에 일시적인 문제가 발생했습니다. 잠시 후 다시 시도해주세요.' }, | ||
| { status: 502 } | ||
| ) | ||
| } | ||
|
|
||
| return NextResponse.json( | ||
| { success: false, error: '답변 생성에 실패했습니다. 잠시 후 다시 시도해주세요.' }, | ||
| { status: 500 } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const session = await getServerSession(authOptions) | ||
| const userId = getSessionUserId(session) | ||
| if (!userId) { | ||
| return NextResponse.json( | ||
| { success: false, error: '인증이 필요합니다.' }, | ||
| { status: 401 } | ||
| ) | ||
| } | ||
|
|
||
| const chatSession = await getOrCreateSession(userId) | ||
| const messages = await getSessionMessages(chatSession.id) | ||
|
|
||
| const data = messages.map((m) => ({ | ||
| role: mapRole(m.role), | ||
| content: m.content, | ||
| timestamp: m.createdAt.toLocaleTimeString('ko-KR', { | ||
| hour: '2-digit', | ||
| minute: '2-digit', | ||
| }), | ||
| })) | ||
|
|
||
| return NextResponse.json({ success: true, data: { messages: data } }) | ||
| } catch (error) { | ||
| console.error('[Chatbot API Error]', error instanceof Error ? error.message : error) | ||
| return NextResponse.json( | ||
| { success: false, error: '대화 내역을 불러오지 못했습니다.' }, | ||
| { status: 500 } | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| export async function DELETE() { | ||
| try { | ||
| const session = await getServerSession(authOptions) | ||
| const userId = getSessionUserId(session) | ||
| if (!userId) { | ||
| return NextResponse.json( | ||
| { success: false, error: '인증이 필요합니다.' }, | ||
| { status: 401 } | ||
| ) | ||
| } | ||
|
|
||
| await clearSession(userId) | ||
|
|
||
| return NextResponse.json({ success: true }) | ||
| } catch (error) { | ||
| console.error('[Chatbot API Error]', error instanceof Error ? error.message : error) | ||
| return NextResponse.json( | ||
| { success: false, error: '대화 초기화에 실패했습니다.' }, | ||
| { status: 500 } | ||
| ) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
오류 유형 분기가 없어 JSON 파싱 실패/ Gemini 실패/타임아웃이 모두 500으로 처리됩니다.
Line 24와 Line 51의 실패를 Line 56-61에서 단일 500으로 처리하면, 클라이언트 복구 동작과 운영 진단이 어렵습니다. 최소한 파싱 실패(400), Gemini 업스트림 실패(502), 타임아웃(504)로 분리해주세요.
As per coding guidelines, "src/app/api/**/*.{ts,tsx}: ... Distinguish between Gemini API failures, JSON parsing failures, and timeouts."
Also applies to: 51-51, 56-61
🤖 Prompt for AI Agents
Source: Coding guidelines