feat:실시간 채팅 기능 구현(#60) - #60
Conversation
📝 WalkthroughWalkthrough워크스페이스 멤버가 채팅 메시지를 조회·전송하고 Supabase Realtime으로 새 메시지를 수신하는 기능을 추가했습니다. 서버 초기 조회, TanStack Query 캐시, 낙관적 전송, RLS 정책, 채팅 UI와 라우트를 구현합니다. Changes워크스페이스 채팅
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ChatView
participant useChatRoom
participant getChatRoom
participant sendChatMessage
participant SupabaseRealtime
ChatView->>useChatRoom: 채팅방 초기화 및 메시지 전송
useChatRoom->>getChatRoom: 초기 채팅방 조회
useChatRoom->>sendChatMessage: 메시지 저장 요청
SupabaseRealtime->>useChatRoom: chat_messages INSERT 이벤트
useChatRoom->>ChatView: 캐시된 메시지와 연결 상태 반환
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/entities/chat/index.ts`:
- Around line 1-4: Update the chat domain entry point alongside getChatRoom to
export sendChatMessage from its API module, preserving the existing query and
type exports so upper layers can use the message-saving action without deep
imports.
In `@src/features/manage-chat/model/use-chat-room.ts`:
- Around line 147-177: The send failure handling in the mutation flow clears the
temporary message without restoring the submitted text. Update both the
!result.ok branch and catch block in the chat send handler to restore content as
the draft when the user has not entered a newer draft, or otherwise preserve the
failed message with retry support, while keeping newer user input intact.
- Around line 16-38: Update appendMessage and replaceMessage so every merge
sorts messages by createdAt in chronological order and retains only the latest
50 messages. Apply the same ordering and limit after duplicate handling, while
preserving temporary-message replacement and existing undefined-data behavior.
- Around line 107-114: Update the realtime message handlers around
chatRoomQueryKey and appendMessage so incoming DB rows are matched to the
optimistic message using the shared clientMessageId. Replace the matching
temporary message with the converted toRealtimeMessage result instead of
appending a separate message, while preserving append behavior for messages with
no matching optimistic entry; apply the same logic to the related handler at the
referenced range.
In `@src/features/manage-chat/ui/ChatMessageList.tsx`:
- Around line 33-35: Update the useEffect in ChatMessageList so new messages
only trigger scrollIntoView when the user is already near the bottom, while
preserving automatic scrolling on initial entry. Track the current scroll
position against the scroll container’s scrollHeight and clientHeight, and avoid
moving users who are reading older messages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 68f6c04f-cb23-4b94-9970-fe7253b8a2b3
📒 Files selected for processing (13)
src/app/workspaces/[workspaceId]/chat/page.tsxsrc/entities/chat/api/chat-actions.tssrc/entities/chat/api/get-chat-room.tssrc/entities/chat/index.tssrc/entities/chat/model/chat-query.tssrc/entities/chat/model/chat.types.tssrc/features/manage-chat/index.tssrc/features/manage-chat/model/use-chat-room.tssrc/features/manage-chat/ui/ChatComposer.tsxsrc/features/manage-chat/ui/ChatMessageList.tsxsrc/views/chat/index.tssrc/views/chat/ui/ChatView.tsxsupabase/migrations/20260715100000_enable_workspace_chat_realtime.sql
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/features/manage-chat/ui/ChatMessageList.tsx`:
- Around line 30-51: Replace useEffect with useLayoutEffect for the
messages.length-driven scroll effect in ChatMessageList, updating the
corresponding React import. Preserve the existing shouldScrollToBottomRef guard,
bottomRef scrolling, 48px threshold, and handleScroll logic unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f24c3f3f-b69e-4619-82c2-0bae80bd7747
📒 Files selected for processing (4)
src/entities/chat/api/chat-actions.tssrc/entities/chat/index.tssrc/features/manage-chat/model/use-chat-room.tssrc/features/manage-chat/ui/ChatMessageList.tsx
| // 메시지 영역 자체의 스크롤 위치를 판단해 과거 메시지 열람 중 자동 이동을 막습니다. | ||
| const scrollContainerRef = useRef<HTMLDivElement>(null); | ||
| // 새 메시지 전송·수신 뒤 최근 대화를 보이게 하는 스크롤 기준 요소입니다. | ||
| const bottomRef = useRef<HTMLDivElement>(null); | ||
| // 최초 진입 또는 하단 근처일 때만 자동 스크롤하도록 기억하는 상태입니다. | ||
| const shouldScrollToBottomRef = useRef(true); | ||
|
|
||
| useEffect(() => { | ||
| if (!shouldScrollToBottomRef.current) return; | ||
|
|
||
| bottomRef.current?.scrollIntoView({ block: 'end' }); | ||
| shouldScrollToBottomRef.current = true; | ||
| }, [messages.length]); | ||
|
|
||
| const handleScroll = () => { | ||
| const container = scrollContainerRef.current; | ||
| if (!container) return; | ||
|
|
||
| // 48px 이내면 사용자가 대화 하단을 보고 있다고 판단합니다. | ||
| const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight; | ||
| shouldScrollToBottomRef.current = distanceFromBottom < 48; | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
최초 마운트 시 스크롤 깜빡임 방지: useLayoutEffect 사용 권장
useEffect는 브라우저 페인트 이후 실행되므로, 메시지가 많은 상태로 최초 마운트될 때 대화 상단이 잠깐 보였다가 하단으로 스크롤되는 깜빡임이 발생할 수 있습니다. 페인트 전에 스크롤 위치를 확정하려면 useLayoutEffect로 교체하는 것이 좋습니다. 나머지 스크롤 판단 로직(48px 임계값, handleScroll)은 과거 지적된 강제 스크롤 문제를 잘 해결했습니다.
💡 제안 diff
- useEffect(() => {
+ useLayoutEffect(() => {
if (!shouldScrollToBottomRef.current) return;
bottomRef.current?.scrollIntoView({ block: 'end' });
shouldScrollToBottomRef.current = true;
}, [messages.length]);(파일 상단의 react import에서 useEffect를 useLayoutEffect로 교체해야 합니다.)
Also applies to: 71-75
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/features/manage-chat/ui/ChatMessageList.tsx` around lines 30 - 51,
Replace useEffect with useLayoutEffect for the messages.length-driven scroll
effect in ChatMessageList, updating the corresponding React import. Preserve the
existing shouldScrollToBottomRef guard, bottomRef scrolling, 48px threshold, and
handleScroll logic unchanged.
Pull Request
작업 내용
작업 결과
chat_messagesRealtime publication 등록 migration을 포함합니다.변경 사항
Added
entities/chat,features/manage-chat,views/chat기반의 채팅 도메인·UI·라우트chat_messages멤버 조회·본인 발송 권한 및 Realtime publication migrationChanged
Fixed
실행화면
https://github.com/user-attachments/assets/af60eaf8-2d92-4981-a3e0-78d4973c911e
테스트
npm run typechecknpm run lintnpm run build리뷰 체크리스트
feature/*->develop, 배포 시develop또는release/*->main)Type/#issue-number/description형식을 따릅니다.console.log, 주석, 임시 코드를 제거했습니다.리뷰 요청사항
관련 이슈
Closes #60
Ref #2
Summary by CodeRabbit
새로운 기능
개선 사항