πAPI & β¨Feat : μ±λ΄ api μ°κ²°#124
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded@KongMezu has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 24 seconds before requesting another review. β How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. π¦ How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. π Files selected for processing (1)
Walkthroughμ±λ΄ API ν΄λΌμ΄μΈνΈμ μΈμ
κ΄λ¦¬ ν
(useChatbot)μ΄ μΆκ°λκ³ , μ±λ΄ νμ΄μ§κ° ν
κΈ°λ° μΈμ
Β·λ©μμ§ νλ¦μΌλ‘ 리ν©ν°λ§λμμ΅λλ€. ChattingInputμ Changes
Sequence Diagram(s)sequenceDiagram
participant User as μ¬μ©μ
participant Input as ChattingInput
participant Page as ChatPage
participant Hook as useChatbot
participant API as BackendAPI
User->>Input: ν
μ€νΈ μ
λ ₯ ν Send
alt Input.disabled == true
Input-->>User: μ
λ ₯/μ μ‘ λ¬΄μ
else
Input->>Page: onSend(message)
Page->>Page: messages += {role: "user", content}
Page->>Hook: mutate(message)
Hook->>API: fetchChatResponse(message, sessionId)
alt SESSION_EXPIRED (404)
API-->>Hook: 404 / SESSION_EXPIRED
Hook->>API: createChatSession()
API-->>Hook: new sessionId
Hook->>Hook: sessionId κ°±μ & localStorage μ μ₯
Hook->>API: fetchChatResponse(message, new sessionId)
end
API-->>Hook: answer
Hook-->>Page: mutation success (answer)
Page->>Page: messages += {role: "bot", content: answer}
Page->>Input: render μ
λ°μ΄νΈ λ° μλ μ€ν¬λ‘€
end
Estimated code review effortπ― 4 (Complex) | β±οΈ ~45 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touchesβ Failed checks (1 warning)
β Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
π·οΈ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
|
π·οΈ Labeler has automatically applied labels based on your PR title, branch name, or commit message. |
There was a problem hiding this comment.
Actionable comments posted: 1
π§Ή Nitpick comments (4)
src/pages/chatbot/components/ChattingInput.tsx (1)
11-21:disabledμ²λ¦¬ λ°©ν₯μ μ μ νμ§λ§, input μ체 λΉνμ±νλ κ³ λ €ν΄λ³Ό λ§ ν©λλ€.
onKeyDown/onClickλ₯Όdisabledλ‘ κ°λν΄μ μ€μ μ μ‘μ΄ λ§νλ ꡬ쑰λ μ μ 리λ κ² κ°μ΅λλ€.- λ€λ§ UX/μ κ·Όμ± κ΄μ μμ βμ λ ₯μ°½μ΄ λΉνμ±νλλ€βλ μλλ₯Ό λ λΆλͺ ν νλ €λ©΄, μλμ²λΌ inputμλ λΉνμ± μνλ₯Ό λ°μνλ κ²μ κ³ λ €ν΄λ³Ό μ μμ΅λλ€.
disabled={disabled}λλreadOnly={disabled}+aria-disabled={disabled}μΆκ°- λΉνμ± μ μ€νμΌ(μμ/컀μ λ±)λ ν¨κ» μ‘°μ νλ©΄ μ¬μ©μμκ² μνκ° λ λͺ νν μ λ¬λ©λλ€.
νμ¬ κ΅¬νλ κΈ°λ₯μ μΌλ‘λ λ¬Έμ μμ΄μ μΆν 리ν©ν°λ§μΌλ‘ μ²μ²ν λ΄λ λ κ² κ°μ΅λλ€.
Also applies to: 39-52
src/pages/chatbot/index.tsx (1)
1-5: μΈμ μ°κ³ λ©μμ§ μ μ‘ νλ‘μ°λ μ ꡬμ±λμ΄ μμ΅λλ€. λ€λ§ μλ¬/λκΈ° μν UXλ₯Ό μ‘°κΈ λ 보μν μ μμ΅λλ€.
useChatbotμmutateAsyncμsessionIdλ₯Ό μ΄μ©νhandleSendλ‘μ§ κ΅¬μ±μ μμ°μ€λ½κ³ , μΈμ μμ λ early return νλ κ²λ λ°©μ΄μ μΌλ‘ μ’μ΅λλ€.- λ€λ§ νμ¬λ:
- μλ² μ€λ₯(500/E600 λ±) μ
console.errorλ§ λ¨κΈ°κ³ UIμλ μ무 νΌλλ°±μ΄ μμ΄ μ¬μ©μκ° βμ λ΅μ΄ μ μ€λμ§β μκΈ° μ΄λ ΅κ³ ,- μλ΅ λκΈ° μ€μλ μΆκ° λ©μμ§λ₯Ό κ³μ λ³΄λΌ μ μμ΄, λΉ λ₯΄κ² μ¬λ¬ λ² μ μ‘ μ μλ΅ μμκ° λ€μμΌ μ μμ΅λλ€.
- React Queryκ° μ 곡νλ
isPending/isErrorλ±μ μνλ₯Ό νμ©ν΄μ:
const { mutateAsync: sendChat, sessionId, isPending, isError } = useChatbot();<ChattingInput onSend={handleSend} disabled={!sessionId || isPending} />- μλ¬ μ μ±λ΄μ μμ€ν λ©μμ§(μ: βμ§κΈμ μλ² μνκ° μ’μ§ μμμ. μ μ ν λ€μ μλν΄ μ£ΌμΈμ.β)λ₯Ό
messagesμ μΆκ°
κ°μ ννλ‘ UXλ₯Ό κ°μ νλ©΄ μ¬μ©μκ° μνλ₯Ό λ λͺ νν μΈμ§ν μ μμ κ² κ°μ΅λλ€.λν μλ¨μ
'use client';λ Pages Router νκ²½μμ λμμ λ¬Έμ λ μμ§λ§, μ΄ λ ν¬ μ»¨λ²€μ (νμ΄μ§λ κΈ°λ³Έμ μΌλ‘ ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈ) κΈ°μ€μΌλ‘ λΆνμνλ―λ‘ μ κ±°ν΄λ μ’κ² μ΅λλ€.
Based on learningsAlso applies to: 23-27, 30-37, 39-59, 107-107
src/shared/hooks/chatbot/useChatbot.ts (1)
1-44: μΈμ κ΄λ¦¬ νλ¦μ μμ μ μ λλ€. μΈμ μμ± μ€ν¨ μ μ¬μλ/μλ¬ λ ΈμΆ κ²½λ‘λ§ μΆκ°λλ©΄ λ μ’κ² μ΅λλ€.
SESSION_KEYμΊμ,initedrefλ‘ StrictMode 2ν μ€νμ λ§λ ν¨ν΄,createChatSession/fetchChatResponseμ°κ³ λͺ¨λ μμ°μ€λ½μ΅λλ€.- λ€λ§
createChatSessionμ΄ ν λ² μ€ν¨νλ©΄:
sessionIdλ κ³μnullμ΄κ³ ,inited.currentκ° trueλΌμ λμΌ νμμ λ μ΄μ μ¬λΆνΈμ€νΈλ©μ΄ λ°μνμ§ μμ,- μμ μ»΄ν¬λνΈ μ μ₯μμ μ λ ₯μ°½μ΄ μꡬμ μΌλ‘
disabledμνκ° λ μ μμ΅λλ€.μΆκ°λ‘ κ³ λ €ν΄λ³Ό λ§ν μ μ λλ€.
- μ΄ hookμμ
errorμν(λΆνΈμ€νΈλ© μ€μ μλ¬/μΈμ λ―Έμμ±)λ₯Ό λ³λλ‘ κ΄λ¦¬ν΄μ λ°ννκ±°λ,- μμμμ βλ€μ μλβ λ²νΌμ λλ μ λ
localStorage.removeItem(SESSION_KEY)ν λΆνΈμ€νΈλ© ν¨μλ₯Ό μ¬νΈμΆν μ μλ APIλ₯Ό λ ΈμΆνλ λ°©μλ μμ΅λλ€.- μ΄λ―Έ mutation κ°μ²΄ μ 체λ₯Ό λ°ννκ³ μμΌλ, μμ μ»΄ν¬λνΈμμ
isPending,isErrorλ₯Ό νμ©ν΄ UXλ₯Ό νλΆνκ² λ§λλ κ²λ κΆμ₯λ©λλ€.ν° κ΅¬μ‘°λ μ μ‘ν μκ³ , μ λ΄μ©μ μ΄ν μλ¬ μ²λ¦¬λ₯Ό κ°μ ν λ μ°Έκ³ μ©μΌλ‘ 보μλ©΄ λ κ² κ°μ΅λλ€.
src/shared/api/chatbot/chatbot.ts (1)
1-92: API ν΄λΌμ΄μΈνΈλ λ°©μ΄μ μΌλ‘ μ μμ±λμμ΅λλ€. μλ¬ μ 보 ꡬ쑰νμ νμμμλ§ μΆκ°λλ©΄ λμ± νμ©λκ° λμ κ² κ°μ΅λλ€.μ’μ μ :
validateStatus: () => trueλ‘ λͺ¨λ μλ΅μ μ§μ κ²μ¬νκ³ , 4xx/5xxμμ λͺ μμ μΌλ‘ μλ¬λ₯Ό λ°μμν€λ ν¨ν΄μ λͺ νν©λλ€.- μΈμ /μ± μλ΅μμ
result/data/λ£¨νΈ νλλ₯Ό λͺ¨λ νμν΄ sessionId/answerλ₯Ό μ»λ λΆλΆλ μ€μ λ°±μλ μλ΅ λ³νμ μ΄λ μ λ μ μ°νκ² λμνλλ‘ μ€κ³λμ΄ μμ΅λλ€.NEXT_PUBLIC_BACKEND_URLμμ trailing slashλ₯Ό μ κ±°νλ κ²λ URL κ²°ν© μ μ€μλ₯Ό μ€μ¬μ€λλ€.κ°μ μ μ:
νμ¬λ
new Error(\μ±λ΄ μλ΅ μ€ν¨: ${res.status}`)μ²λΌ HTTP statusλ§ ν¬ν¨ν΄ μλ¬λ₯Ό λμ§κΈ° λλ¬Έμ, λ°±μλμμ λ΄λ €μ£Όλ λλ©μΈ μλ¬ μ½λ(μ:code: 'E600'`)λ₯Ό ν΄λΌμ΄μΈνΈμμ κ΅¬λΆ μ²λ¦¬νκΈ° μ΄λ ΅μ΅λλ€.
μλ₯Ό λ€μ΄ μλμ²λΌ 컀μ€ν μλ¬ κ°μ²΄λ₯Ό λ§λ€μ΄
status,code,messageλ± λ©νλ°μ΄ν°λ₯Ό λ΄μ λλ©΄, μμ λ 벨μμ βE600μ΄λ©΄ μ΄λ° μλ΄ λ©μμ§λ₯Ό λμ΄λ€β κ°μ λΆκΈ° μ²λ¦¬κ° μ¬μμ§λλ€.const apiError = new Error(`μ±λ΄ μλ΅ μ€ν¨: ${res.status}`); (apiError as any).status = res.status; (apiError as any).code = (res.data as any)?.code; throw apiError;μ₯κΈ°μ μΌλ‘λ axiosμ
timeoutμ΅μ μ μΆκ°ν΄, μλ²κ° μλ΅μ μμ£Ό μ€λ λκ³ κ°λ μν©μμ UIκ° λ¬΄κΈ°ν λκΈ°νμ§ μλλ‘ νλ κ²λ κ³ λ €ν΄λ³Ό λ§ν©λλ€.νμ¬ λμ μ체μλ λ¬Έμ μμ΄ λ³΄μ΄κ³ , μ λ΄μ©μ μΆν μλ² μλ¬(E600 λ±)λ₯Ό νλ‘ νΈμμ λ μΈλ°νκ² μ²λ¦¬ν΄μΌ ν λ λμμ΄ λ κ°μ ν¬μΈνΈμ λλ€.
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (1)
public/assets/chatbot.svgis excluded by!**/*.svg
π Files selected for processing (4)
src/pages/chatbot/components/ChattingInput.tsx(2 hunks)src/pages/chatbot/index.tsx(4 hunks)src/shared/api/chatbot/chatbot.ts(1 hunks)src/shared/hooks/chatbot/useChatbot.ts(1 hunks)
π§° Additional context used
π§ Learnings (1)
π Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.
Applied to files:
src/pages/chatbot/index.tsx
𧬠Code graph analysis (3)
src/shared/hooks/chatbot/useChatbot.ts (1)
src/shared/api/chatbot/chatbot.ts (2)
createChatSession(28-56)fetchChatResponse(59-92)
src/pages/chatbot/components/ChattingInput.tsx (1)
src/shared/hooks/useChattingInput.ts (1)
useChattingInput(8-49)
src/pages/chatbot/index.tsx (3)
src/shared/hooks/chatbot/useChatbot.ts (1)
useChatbot(9-44)src/pages/chatbot/components/ChattingBubble.tsx (1)
Chatting(32-43)src/pages/chatbot/components/ChattingInput.tsx (1)
ChattingInput(16-60)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
| <main | ||
| role='log' | ||
| aria-label='μ±ν λ΄μ©' | ||
| aria-live='polite' | ||
| className={cn(mainStyle())} | ||
| > | ||
| {/* λ‘κ³ + κΈ°λ³Έ λ©νΈ */} | ||
| <div className={cn(introStyle())}> | ||
| {/* λ‘κ³ μ리 (μμ) */} | ||
| <div | ||
| className='w-[6rem] h-[6rem] rounded-full bg-gray-200 flex-shrink-0' | ||
| aria-hidden="true" | ||
| {/* λ‘κ³ μ리*/} | ||
| <Image | ||
| src='/assets/chatbot.svg' | ||
| alt='μ±λ΄ νλ‘ν μμ΄μ½' | ||
| width={60} | ||
| height={60} | ||
| className='rounded-full flex-shrink-0' | ||
| priority | ||
| /> | ||
| <Chatting | ||
| message='μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' | ||
| variant='received' | ||
| aria-label="κΈλ€μ λ©μμ§: μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ." | ||
| aria-label='κΈλ€μ λ©μμ§: μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' | ||
| /> |
There was a problem hiding this comment.
π§© Analysis chain
ARIA μ€μ μ μ λ°μ μΌλ‘ μ’μ§λ§, 첫 Chattingμ aria-label propμ μ€μ λ‘ μ¬μ©λμ§ μμ κ°λ₯μ±μ΄ ν½λλ€.
-
<main>μrole='log',aria-live='polite',aria-label='μ±ν λ΄μ©'μ λΆμ¬ν μ ,bottomRefμaria-hidden='true'λ₯Ό μ€ μ μ SR κ΄μ μμ μ μ‘ν μμ΅λλ€. -
λ€λ§ 첫 λ²μ§Έ
Chattingμ»΄ν¬λνΈμ λκΈ΄aria-label:<Chatting message='...' variant='received' aria-label='κΈλ€μ λ©μμ§: ...' />
μ νμ¬
Chattingꡬν(λ©μμ§/variantλ§ μ¬μ©, λλ¨Έμ§ propsλ₯Ό DOMμ μ λ¬νμ§ μμ) μ μ€μ DOMκΉμ§ μ λ¬λμ§ μμ κ°λ₯μ±μ΄ λμ΅λλ€. νμ μ μκ°message/variantλ§ νμ©νλ€λ©΄ TS μλ¬λ λ°μν μ μμ΅λλ€. -
μ νμ§λ λ κ°μ§ μ λκ° μμ΄ λ³΄μ λλ€.
aria-labelμ μ¬κΈ°μ μ κ±°νκ³ , νμνλ©΄Chattingλ΄λΆ λ‘μ§/문ꡬλ₯Ό μ‘°μ ν΄μ μ κ·Όμ±μ ν΅μΌμ± μκ² κ΄λ¦¬νλ€.Chattingμ propsλ₯ΌChattingProps & React.HTMLAttributes<HTMLDivElement>μ²λΌ νμ₯νκ³ , λ΄λΆμμ...restλ₯Ό bubbleμ μ€νλ λν΄ μΈλΆμμ μ λ¬νaria-*λ₯Ό κ·Έλλ‘ λ΄λ €λ³΄λΈλ€.
νμ¬ κ΅¬μ‘°μμ β μ΄ κ°λ¨νκ³ , β‘λ Chattingμ λ λ²μ©μ μΌλ‘ μ°κ³ μΆμ λ κ³ λ €ν λ§ν λ°©ν₯μ λλ€.
Also applies to: 98-104
---
π Script executed:
```shell
#!/bin/bash
# Find the Chatting component definition
fd -t f -e tsx -e ts | head -20 | xargs grep -l "export.*Chatting" 2>/dev/null | head -5
Length of output: 94
π Script executed:
#!/bin/bash
# Search for Chatting component definition with ast-grep
ast-grep --pattern 'export $_ Chatting'Length of output: 0
π Script executed:
#!/bin/bash
# Also search more broadly for Chatting
rg -n "component.*Chatting|function Chatting|const Chatting" --type=tsx --type=ts -A 5 | head -50Length of output: 27
π Script executed:
#!/bin/bash
# Check the current file to see both sections mentioned
cat src/pages/chatbot/index.tsx | head -110 | tail -50Length of output: 1309
π Script executed:
#!/bin/bash
# Look for ChattingProps type definition
rg -n "type ChattingProps|interface ChattingProps" --type=tsx --type=ts -A 10Length of output: 27
---
π Script executed:
```shell
#!/bin/bash
# Find Chatting component files
fd "Chatting" --type f
Length of output: 131
π Script executed:
#!/bin/bash
# Search for Chatting in components directory
fd "." src/pages/chatbot/components/ --type fLength of output: 94
π Script executed:
#!/bin/bash
# Search for ChattingProps definition - use grep directly
grep -r "ChattingProps" src/pages/chatbot/ 2>/dev/nullLength of output: 238
π Script executed:
#!/bin/bash
# Search for Chatting component definition - broader search
grep -r "function Chatting\|const Chatting\|export.*Chatting" src/pages/chatbot/ 2>/dev/null | grep -v node_modules | head -20Length of output: 206
π Script executed:
#!/bin/bash
# Look at ChattingBubble since it was found
cat src/pages/chatbot/components/ChattingBubble.tsxLength of output: 884
aria-label propμ΄ Chatting μ»΄ν¬λνΈμ μ λ¬λμ§ μμ΅λλ€.
κ²μ¦ κ²°κ³Ό, 리뷰 μ§μ μ΄ μ νν©λλ€. src/pages/chatbot/components/ChattingBubble.tsxμ ChattingProps μΈν°νμ΄μ€λ messageμ variantλ§ νμ©νλ©°, μ»΄ν¬λνΈλ μΆκ° propsλ₯Ό DOMμ μ λ¬νμ§ μμ΅λλ€.
λΌμΈ 85-88μμ μ λ¬ν aria-label='κΈλ€μ λ©μμ§: ...'μ 무μλκ³ , Chatting μ»΄ν¬λνΈμ κΈ°λ³Έ μ 곡 aria-label={variant === 'sent' ? 'λ΄ λ©μμ§' : 'μλλ°© λ©μμ§'}λ§ μ¬μ©λ©λλ€. λ ꡬ체μ μΈ μ κ·Όμ± μ 보λ₯Ό λ΄μΌλ €λ μλκ° μ€νλμ§ μμ΅λλ€.
ν΄κ²°μ:
- λΌμΈ 85-88μ
aria-labelpropμ μ κ±°νκ³ μ»΄ν¬λνΈ κΈ°λ³Έκ° μ¬μ© - λλ
ChattingPropsλ₯ΌReact.HTMLAttributes<HTMLDivElement>μ ν©μΉκ³ λ΄λΆμμ...restμ€νλ λ μ²λ¦¬
νμ¬ κ΅¬μ‘°μμ β μ΄ κ°λ¨ν©λλ€.
π€ Prompt for AI Agents
In src/pages/chatbot/index.tsx around lines 69 to 90, the aria-label prop passed
to the Chatting component is ignored because ChattingProps only allows message
and variant and the component doesn't forward extra props; remove the explicit
aria-label from lines 85-88 so the Chatting component's built-in aria-label
logic is used, or alternatively update ChattingBubble.tsx to extend
React.HTMLAttributes<HTMLDivElement> and spread ...rest onto the root element to
accept and forward custom aria attributes (option 1 is the simpler fix in this
context).
There was a problem hiding this comment.
Actionable comments posted: 2
π§Ή Nitpick comments (2)
src/shared/api/chatbot/chatbot.ts (2)
78-83: API ν΄λΌμ΄μΈνΈμμ localStorageλ₯Ό μ§μ μ‘°μνλ κ²μ κ΄μ¬μ¬ λΆλ¦¬ μΈ‘λ©΄μμ κ°μ μ΄ νμν©λλ€.
fetchChatResponseν¨μ λ΄μμ localStorageλ₯Ό μ§μ μ κ±°νκ³ μμ΅λλ€. μ΄λ API ν΄λΌμ΄μΈνΈμ μ± μ λ²μλ₯Ό λ²μ΄λλ©°, νΈμΆνλ μͺ½(hook)μμ SESSION_EXPIRED μλ¬λ₯Ό λ°μ μ²λ¦¬νλλ‘ νλ κ²μ΄ λ κΉλν©λλ€.λ€μκ³Ό κ°μ΄ 리ν©ν λ§μ κ³ λ €ν΄λ³΄μΈμ:
if (res.status === 404 && res.data?.code === 'E602') { - if (typeof window !== 'undefined') { - localStorage.removeItem('chatbot:sessionId'); - } throw new Error('SESSION_EXPIRED'); }localStorage μ 리λ
useChatbot.tsμ μλ¬ νΈλ€λ¬λ‘ μ΄λ:// useChatbot.tsμ catch λΈλ‘μμ if (err.message === 'SESSION_EXPIRED') { if (typeof window !== 'undefined') { localStorage.removeItem(SESSION_KEY); } // ... μ μΈμ μμ± }
78-78: λ€μ¬μ°κΈ° μ΄μ.
ifλ¬Έ μμ λΆνμν κ³΅λ°±μ΄ μμ΅λλ€.- if (res.status === 404 && res.data?.code === 'E602') { + if (res.status === 404 && res.data?.code === 'E602') {
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (2)
src/shared/api/chatbot/chatbot.ts(1 hunks)src/shared/hooks/chatbot/useChatbot.ts(1 hunks)
π§° Additional context used
𧬠Code graph analysis (1)
src/shared/hooks/chatbot/useChatbot.ts (1)
src/shared/api/chatbot/chatbot.ts (2)
createChatSession(28-56)fetchChatResponse(59-99)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
π Additional comments (5)
src/shared/api/chatbot/chatbot.ts (3)
3-19: API μλ΅ μ€ν€λ§κ° λΆνμ€ν©λλ€.
SessionSuccessμChatSuccessνμ μμ μ¬λ¬ μ νμ νλ(result?,data?,sessionId?,answer?)λ₯Ό μ μνμ¬ λ€μν μλ΅ νμμ μ²λ¦¬νκ³ μμ΅λλ€. μ΄λ λ°±μλ API κ³μ½μ΄ λͺ ννμ§ μκ±°λ μΌκ΄μ±μ΄ μμμ μμ¬ν©λλ€.λ°±μλ API λ¬Έμλ₯Ό νμΈνμ¬ μ νν μλ΅ μ€ν€λ§λ₯Ό νμ νκ³ , κ°λ₯νλ€λ©΄ λ¨μΌ μλ΅ νμμΌλ‘ νμ€ννλ κ²μ κΆμ₯ν©λλ€. μ΄λ κ² νλ©΄ μ½λκ° λ λͺ νν΄μ§κ³ μμμΉ λͺ»ν μλ΅ νμ μ²λ¦¬ λ‘μ§μ μ€μΌ μ μμ΅λλ€.
21-25: νκ²½ λ³μ κ²μ¦μ΄ μ μ ν©λλ€.
getBaseUrlν¨μκ° νκ²½ λ³μλ₯Ό νμΈνκ³ trailing slashλ₯Ό μ κ±°νμ¬ μΌκ΄λ URLμ 보μ₯ν©λλ€.
27-56: μΈμ μμ± λ‘μ§μ΄ κ²¬κ³ ν©λλ€.λ€μν μλ΅ νμμ μ²λ¦¬νκ³ μ μ ν μλ¬ λ‘κΉ μ μ 곡ν©λλ€.
validateStatus: () => trueλ₯Ό μ¬μ©νμ¬ μλμΌλ‘ μν μ½λλ₯Ό νμΈνλ λ°©μλ μ μ ν©λλ€.src/shared/hooks/chatbot/useChatbot.ts (2)
9-11:inited.currentν¨ν΄μ μ μ νμ§λ§ μ νμ¬νμ΄ μμ΅λλ€.React 18 Strict Modeμ μ΄μ€ λ λλ§μ λ°©μ§νκΈ° μν μΌλ°μ μΈ ν¨ν΄μ λλ€. νμ§λ§ μ»΄ν¬λνΈκ° μΈλ§μ΄νΈ ν μ¬λ§μ΄νΈλλ©΄
inited.currentκ° μ¬μ νtrueμ΄λ―λ‘ bootstrapμ΄ μ€νλμ§ μμ΅λλ€.μ΄κ²μ΄ μλλ λμμΈμ§ νμΈνμΈμ. λ§μ½ μ¬λ§μ΄νΈ μ μ μΈμ μ μμ±ν΄μΌ νλ€λ©΄, cleanupμμ
inited.current = falseλ‘ μ¬μ€μ ν΄μΌ ν©λλ€.
1-7: 'use client' μ§μμ΄μ μμ μ μκ° μ μ ν©λλ€.Next.js ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈμμ λͺ μνκ³ localStorage ν€λ₯Ό μμλ‘ μ μνμ¬ μ¬μ¬μ©μ±κ³Ό μ μ§λ³΄μμ±μ λμμ΅λλ€.
| useEffect(() => { | ||
| if (inited.current) return; | ||
| inited.current = true; | ||
|
|
||
| const bootstrap = async () => { | ||
| try { | ||
| const cached = typeof window !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null; | ||
| if (cached) { | ||
| setSessionId(cached); | ||
| return; | ||
| } | ||
| const id = await createChatSession(); | ||
| setSessionId(id); | ||
| if (typeof window !== 'undefined') localStorage.setItem(SESSION_KEY, id); | ||
| } catch (e) { | ||
| console.error('μΈμ μμ± μ€ν¨:', e); | ||
| setSessionId(null); | ||
| } | ||
| }; | ||
|
|
||
| bootstrap(); | ||
| }, []); |
There was a problem hiding this comment.
μ μ¬μ λ©λͺ¨λ¦¬ λμ: μ»΄ν¬λνΈ μΈλ§μ΄νΈ ν μν μ λ°μ΄νΈ κ°λ₯μ±.
bootstrap ν¨μκ° λΉλκΈ°λ‘ μ€νλλ λμ μ»΄ν¬λνΈκ° μΈλ§μ΄νΈλλ©΄, setSessionIdκ° μΈλ§μ΄νΈλ μ»΄ν¬λνΈμμ νΈμΆλμ΄ κ²½κ³ κ° λ°μν μ μμ΅λλ€.
cleanup ν¨μλ₯Ό μΆκ°νμ¬ μ΄λ₯Ό λ°©μ§νμΈμ:
useEffect(() => {
if (inited.current) return;
inited.current = true;
+ let mounted = true;
const bootstrap = async () => {
try {
const cached = typeof window !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null;
if (cached) {
+ if (mounted) {
setSessionId(cached);
+ }
return;
}
const id = await createChatSession();
+ if (mounted) {
setSessionId(id);
if (typeof window !== 'undefined') localStorage.setItem(SESSION_KEY, id);
+ }
} catch (e) {
console.error('μΈμ
μμ± μ€ν¨:', e);
+ if (mounted) {
setSessionId(null);
+ }
}
};
bootstrap();
+ return () => {
+ mounted = false;
+ };
}, []);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| if (inited.current) return; | |
| inited.current = true; | |
| const bootstrap = async () => { | |
| try { | |
| const cached = typeof window !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null; | |
| if (cached) { | |
| setSessionId(cached); | |
| return; | |
| } | |
| const id = await createChatSession(); | |
| setSessionId(id); | |
| if (typeof window !== 'undefined') localStorage.setItem(SESSION_KEY, id); | |
| } catch (e) { | |
| console.error('μΈμ μμ± μ€ν¨:', e); | |
| setSessionId(null); | |
| } | |
| }; | |
| bootstrap(); | |
| }, []); | |
| useEffect(() => { | |
| if (inited.current) return; | |
| inited.current = true; | |
| let mounted = true; | |
| const bootstrap = async () => { | |
| try { | |
| const cached = typeof window !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null; | |
| if (cached) { | |
| if (mounted) { | |
| setSessionId(cached); | |
| } | |
| return; | |
| } | |
| const id = await createChatSession(); | |
| if (mounted) { | |
| setSessionId(id); | |
| if (typeof window !== 'undefined') localStorage.setItem(SESSION_KEY, id); | |
| } | |
| } catch (e) { | |
| console.error('μΈμ μμ± μ€ν¨:', e); | |
| if (mounted) { | |
| setSessionId(null); | |
| } | |
| } | |
| }; | |
| bootstrap(); | |
| return () => { | |
| mounted = false; | |
| }; | |
| }, []); |
π€ Prompt for AI Agents
In src/shared/hooks/chatbot/useChatbot.ts around lines 13 to 34, the async
bootstrap may call setSessionId after the component unmounts causing a
memory-leak/warning; add a mounted flag (or use a ref) that is true while
mounted and set to false in the effect cleanup, then check that flag before any
setSessionId or localStorage writes (and before console.error handling) so state
updates are only performed when mounted; ensure the flag is closed over by
bootstrap and the effect returns the cleanup that flips it to false.
There was a problem hiding this comment.
Actionable comments posted: 0
β»οΈ Duplicate comments (1)
src/pages/chatbot/index.tsx (1)
93-101: νλ‘ν μ΄λ―Έμ§ μΆκ°λ μ μ νλ,Chattingμ λκΈ΄aria-labelμ μ¬μ ν 무μλ©λλ€
next/imageλ‘ μ±λ΄ νλ‘ν SVGλ₯Ό λ λλ§νκ³ , μ μ νaltν μ€νΈλ₯Ό λΆμ¬ν λΆλΆμ μ’μ΅λλ€.λ€λ§ 첫 λ²μ§Έ
Chattingμ»΄ν¬λνΈμ λκΈ΄aria-label:<Chatting message='μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' variant='received' aria-label='κΈλ€μ λ©μμ§: μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' />μ νμ¬
Chattingꡬν(ChattingBubble.tsxμChattingPropsκ°message/variantλ§ νμ©, λλ¨Έμ§ propsλ DOMμΌλ‘ μ λ¬νμ§ μμ) μ μ€μ DOMκΉμ§ μ λ¬λμ§ μμ΅λλ€. μ΄μ PRμμ μ§μ λ κ²κ³Ό λμΌν μ΄μμ λλ€.μ΄ PRμμλ κ°λ¨νκ² μ΄
aria-labelpropμ μ κ±°νκ³ ,Chattingμ»΄ν¬λνΈ λ΄λΆμ κΈ°λ³Έaria-labelλ‘μ§μ λ§‘κΈ°λ μͺ½μ΄ 무λν΄ λ³΄μ λλ€.μμ diff:
- <Chatting - message='μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' - variant='received' - aria-label='κΈλ€μ λ©μμ§: μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' - /> + <Chatting + message='μλ νμΈμ, κΈλ€μμ! λΆμ²μ μ¬νμ λν μ 보λ₯Ό μ½κ² μλ €λ릴κ²μ.' + variant='received' + />Also applies to: 102-106
π§Ή Nitpick comments (4)
src/pages/chatbot/components/ChattingBubble.tsx (1)
35-41: λ©ν°λΌμΈ λ©μμ§ νμμ©whiteSpace: 'pre-line'μΆκ° μ’μ΅λλ€API μλ΅μ ν¬ν¨λ μ€λ°κΏμ κ·Έλλ‘ μ΄λ¦΄ μ μμ΄μ μ€μ λνμ²λΌ 보μ¬μ€ μ μλ λ³κ²½μ΄λΌ μ’μ 보μ λλ€. μ€νμΌ μΌκ΄μ±μ μν΄ λμ€μ μ¬μ κ° μμΌλ©΄ Tailwind μ νΈ(
whitespace-pre-line)μ΄λchatBubbleStyleμ ν΄λμ€λ‘ λ Ήμ¬ λλ κ²λ κ³ λ €ν΄ λ³Ό λ§ν©λλ€.src/pages/chatbot/index.tsx (3)
3-12: λΌμ°ν°/ν /μ»΄ν¬λνΈ import κ΅¬μ± μ μ ν©λλ€
next/routerλ‘useRouterλ₯Ό κ°μ Έμ€κ³ ,useChatbotν κ³ΌChattingΒ·ChattingInputμ»΄ν¬λνΈλ₯Ό μ΄ νμ΄μ§μμλ§ μ¬μ©νλλ‘ λ¬Άμ κ΅¬μ‘°κ° Pages Router 컨벀μ κ³Ό μ λ§μ΅λλ€.
μΆκ°λ‘, μ΄ νμΌμ Pages Routerμ page μ»΄ν¬λνΈλΌ'use client'μ§μλ¬Έμ μ¬μ€μ μλ―Έκ° μμΌλ―λ‘ μ€νμΌ μΈ‘λ©΄μμ μ κ±°ν΄λ λ©λλ€.
34-45:formatAnswerνμ²λ¦¬ λ‘μ§μ μ μ©νμ§λ§, λΈλΌμ°μ νΈνμ±κ³Ό μ μ§λ³΄μμ μ μνλ©΄ μ’κ² μ΅λλ€
- λΆλ¦Ώ(
Β·,β’)κ³Ό λ¬Έμ₯λΆνΈ λ€μ μ€λ°κΏμ λ£κ³ , μ΄λͺ¨μ§ μ£Όλ³ κ³΅λ°±/μ€λ°κΏμ μ 리νλ κ·μΉμ μ±λ΄ λ΅λ³μ μ½κΈ° μ’κ² λ§λλ λ° λμμ΄ λ κ² κ°μ΅λλ€.- λ€λ§
\p{Emoji_Presentation},\p{Emoji}κ°μ μ λμ½λ νλ‘νΌν° μ κ·μμ μΌλΆ ꡬν λΈλΌμ°μ μμλ μ§μμ΄ μ νλ μ μμΌλ, νκ² λΈλΌμ°μ λ²μλ₯Ό κ³ λ €ν΄λλ νΈμ΄ μ’μ΅λλ€.- κ·μΉμ΄ κ½€ λ§μμ‘μΌλ, λμ€μ ν¬λ§·μ΄ λ 볡μ‘ν΄μ§ κ²½μ° μ£ΌμμΌλ‘ μλλ₯Ό κ°λ¨ν λ¨κΈ°κ±°λ λ³ μ νΈ ν¨μ νμΌλ‘ λΆλ¦¬νλ κ²λ κ³ λ €ν΄λ³Ό λ§ν©λλ€.
51-75:handleSendνλ¦μ λ§μ§λ§, μ μ‘ μ€ μν λ° μλ¬ UXλ₯Ό μ‘°κΈ λ 보μν μ¬μ§κ° μμ΅λλ€
- 곡백 λ¬Έμμ΄ μ°¨λ¨,
sessionIdλ―Έμ‘΄μ¬ μ early return, μ¬μ©μ λ©μμ§ β μ±λ΄ μλ΅ μμΌλ‘messagesλ₯Ό μ λ°μ΄νΈνλ νλ¦μ μμ°μ€λ½μ΅λλ€.- νμ¬ μ€ν¨ μμλ μ½μ λ‘κ·Έλ§ λ¨κ³ μ¬μ©μμκ²λ μ무 νΌλλ°±μ΄ μμ΄, μ€μ μλΉμ€ μμλ βλ΅λ³μ κ°μ Έμ€μ§ λͺ»νμ΅λλ€β κ°μ λ©μμ§λ₯Ό λ΄ λ©μμ§λ‘ μΆκ°ν΄ μ£Όλ κ²λ μ’κ² μ΅λλ€.
useChatbotμμ React QueryuseMutationμ μ°κ³ μμΌλ―λ‘,isPendingλ±μ μ΄ μ»΄ν¬λνΈλ‘ λμ΄μChattingInputμdisabledμ ν¨κ» λ°μνλ©΄ μ°μ ν΄λ¦/μ€λ³΅ μ μ‘μ λ°©μ§ν μ μμ΅λλ€. μ:disabled={!sessionId || isPending}.Also applies to: 123-123
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (2)
src/pages/chatbot/components/ChattingBubble.tsx(1 hunks)src/pages/chatbot/index.tsx(4 hunks)
π§° Additional context used
π§ Learnings (1)
π Learning: 2025-10-31T17:34:04.352Z
Learnt from: jjangminii
Repo: geulDa/FE PR: 64
File: src/pages/map/result/components/ResultMap.tsx:3-3
Timestamp: 2025-10-31T17:34:04.352Z
Learning: The geulDa/FE project uses Next.js Pages Router (not App Router), so components should import useRouter from 'next/router' and should not use the 'use client' directive.
Applied to files:
src/pages/chatbot/index.tsx
𧬠Code graph analysis (2)
src/pages/chatbot/index.tsx (4)
src/shared/hooks/chatbot/useChatbot.ts (1)
useChatbot(9-63)src/shared/lib/utils.ts (1)
cn(71-73)src/pages/chatbot/components/ChattingBubble.tsx (1)
Chatting(32-44)src/pages/chatbot/components/ChattingInput.tsx (1)
ChattingInput(16-60)
src/pages/chatbot/components/ChattingBubble.tsx (1)
src/shared/lib/utils.ts (1)
cn(71-73)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
π Additional comments (2)
src/pages/chatbot/index.tsx (2)
23-33: Message νμ μ μμ λ©μμ§ λ λλ§ νλ¦μ΄ κΉλν©λλ€
Messageλ₯Όtextμvariant('received' | 'sent')λ‘ λͺ νν νμ νν΄μmessages: Message[]μνμmessages.map(...)λ λλ§μ μ¬μ¬μ©ν μ μ΄ μ μ§λ³΄μ μΈ‘λ©΄μμ μ’μ΅λλ€. λ©μμ§ νν°λ§ μμ΄ λμΌ λ°°μ΄μ κ·Έλλ‘ νλ©΄μ λ§΅ννλ ꡬ쑰λΌ, μ΄ν μμ€ν λ©μμ§λ μν λ©μμ§λ₯Ό μΆκ°ν λλ νμ₯νκΈ° μμν΄ λ³΄μ λλ€.Also applies to: 114-116
85-90:role='log'+aria-live+ μ€ν¬λ‘€ μΌν°λ¬ μ‘°ν©μ΄ μ κ·Όμ± μΈ‘λ©΄μμ μ μ‘ν μμ΅λλ€
<main role='log' aria-live='polite' aria-label='μ±ν λ΄μ©'>κ³ΌbottomRefλ₯Ό μ΄μ©ν μλ μ€ν¬λ‘€ ν¨ν΄μ μ€ν¬λ¦°λ¦¬λ μ¬μ©μμκ²λ μλ‘μ΄ λ©μμ§κ° μμ°μ€λ½κ² μ½νλλ‘ νλ κ΅¬μ‘°λΌ μ’μ 보μ λλ€.aria-hidden='true'λ‘ μΌν°λ¬λ₯Ό μ¨κΈ΄ κ²λ μ μ ν©λλ€.Also applies to: 119-119
There was a problem hiding this comment.
Actionable comments posted: 2
β»οΈ Duplicate comments (2)
src/shared/hooks/chatbot/useChatbot.ts (2)
64-93: μ΄μ 리뷰μμ μ§μ λ λ©λͺ¨λ¦¬ λμ λ¬Έμ κ° μ¬μ ν μ‘΄μ¬ν©λλ€.λΉλκΈ°
bootstrapμ€ν μ€ μ»΄ν¬λνΈκ° μΈλ§μ΄νΈλλ©΄setSessionIdμsetMessagesνΈμΆμ΄ μΈλ§μ΄νΈλ μ»΄ν¬λνΈμμ λ°μνμ¬ λ©λͺ¨λ¦¬ λμ κ²½κ³ κ° λ°μν μ μμ΅λλ€. μ΄μ 리뷰μμ μ μλmountedνλκ·Έλ₯Ό μ¬μ©ν cleanup ν¨μ μΆκ°κ° νμν©λλ€.λν, Lines 76, 85μμ
localStorageμ κ·Ό μtypeof window !== 'undefined'체ν¬κ° λλ½λμ΄ SSR νκ²½μμ λ¬Έμ κ° λ°μν μ μμ΅λλ€.λ€μκ³Ό κ°μ΄ μμ νμΈμ:
useEffect(() => { if (inited.current) return; inited.current = true; + let mounted = true; // νμ€ν 리 λ‘λ (λ‘κ·ΈμΈ μ μ λ§) if (isLoggedIn) { const prev = loadHistory(); + if (mounted) { setMessages(prev); + } } const bootstrap = async () => { try { - const cached = localStorage.getItem(SESSION_KEY); + const cached = typeof window !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null; if (cached) { + if (mounted) { setSessionId(cached); + } return; } const id = await createChatSession(); + if (mounted) { setSessionId(id); - localStorage.setItem(SESSION_KEY, id); + if (typeof window !== 'undefined') { + localStorage.setItem(SESSION_KEY, id); + } + } } catch (e) { console.error('μΈμ μμ± μ€ν¨:', e); + if (mounted) { setSessionId(null); + } } }; bootstrap(); + return () => { + mounted = false; + }; }, []);
96-113: μ΄μ 리뷰μμ μ§μ λ μ¬μλ μ€λ₯ μ²λ¦¬κ° μ¬μ ν λλ½λμμ΅λλ€.Line 104:
createChatSession()μ΄ μ€ν¨νλ©΄ μλ¬κ° κ·Έλλ‘ μ νλμ΄ μ¬μ©μμκ² λͺ νν νΌλλ°±μ΄ μ 곡λμ§ μμ΅λλ€.λν, Line 106μμ
localStorage.setItemνΈμΆ μtypeof window !== 'undefined'체ν¬κ° μμ΅λλ€.λ€μκ³Ό κ°μ΄ μμ νμΈμ:
} catch (err: any) { if (err.message === 'SESSION_EXPIRED') { - const newId = await createChatSession(); - setSessionId(newId); - localStorage.setItem(SESSION_KEY, newId); - - return await fetchChatResponse(body.message, newId); + try { + const newId = await createChatSession(); + setSessionId(newId); + if (typeof window !== 'undefined') { + localStorage.setItem(SESSION_KEY, newId); + } + + return await fetchChatResponse(body.message, newId); + } catch (retryErr) { + console.error('μ¬μλ μ€ μ€λ₯:', retryErr); + throw new Error('μΈμ μ¬μμ± μ€ν¨. λμ€μ λ€μ μλν΄μ£ΌμΈμ.'); + } } throw err; }
π§Ή Nitpick comments (1)
src/shared/hooks/chatbot/useChatbot.ts (1)
22-25: μ±λ₯ μ΅μ ν κ³ λ €: memberId κ³μ°μ λ©λͺ¨μ΄μ μ΄μ ν μ μμ΅λλ€.
getMemberIdFromToken()μ΄ λ§€ λ λλ§λ€ νΈμΆλμ΄ localStorage μ κ·Ό λ° JWT λμ½λ©μ΄ λ°λ³΅λ©λλ€. μ»΄ν¬λνΈκ° μμ£Ό 리λ λλ§λλ κ²½μ°useMemoλ‘ μ΅μ νν μ μμ΅λλ€.λ€μκ³Ό κ°μ΄ μμ ν μ μμ΅λλ€:
+ const memberId = useMemo(() => getMemberIdFromToken(), []); - const memberId = getMemberIdFromToken(); const isLoggedIn = !!memberId;
π Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π Files selected for processing (2)
src/pages/chatbot/index.tsx(4 hunks)src/shared/hooks/chatbot/useChatbot.ts(1 hunks)
π§ Files skipped from review as they are similar to previous changes (1)
- src/pages/chatbot/index.tsx
π§° Additional context used
𧬠Code graph analysis (1)
src/shared/hooks/chatbot/useChatbot.ts (2)
src/shared/utils/token.ts (1)
getMemberIdFromToken(25-39)src/shared/api/chatbot/chatbot.ts (2)
createChatSession(29-57)fetchChatResponse(60-99)
β° Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-and-deploy
skyblue1232
left a comment
There was a problem hiding this comment.
μ±λ΄ ꡬνμ΄ UX μ μΌλ‘ μ’μ κ² κ°κ³ , μ 체μ μΌλ‘ μ μλνλ κ² κ°λ€μ! μκ³ νμ ¨μ΅λλ€!
π₯ μμ λ΄μ©
π€ μΆν μμ μ¬ν
πΒ μ΄μ
PR Point (To Reviewer)
μ±λ΄ api μ°κ²°
μ±λ΄ api μ°κ²° νμ΅λλ€.
λ‘κ·ΈμΈ μ μ : λ€μ μ±λ΄ μ μμ 12μκ° λμ μ΄μ μ±ν κΈ°λ‘ νμΈ κ°λ₯
λΉλ‘κ·ΈμΈ μ μ : μλ‘ μ μμ 리μ
κ·Έλ¦¬κ³ api μμ λ°μμ€λ κΈμλ μ΄λͺ¨ν°μ½, λ§μΉ¨ν(.) μ λ§κ² μ€λ°κΏμ΄ κ°λ₯νλλ‘ ν΄λ¨μ΅λλ€.
+) instance axios κ²½λ‘ μμ
πΈΒ νΌκ·Έλ§ μ€ν¬λ¦°μ· or κΈ°λ₯ GIF
λΉλ‘κ·ΈμΈ
-.Clipchamp.4.mp4
λ‘κ·ΈμΈ
-.Clipchamp.5.mp4
Summary by CodeRabbit
μλ‘μ΄ κΈ°λ₯
λ²κ·Έ μμ / κ°μ
UI