feat:채팅방 검색 및 추가 기능 구현#274
Hidden character warning
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough채팅 검색 및 채팅방 추가 기능을 추가했습니다. 보호된 라우트 아래에 Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/apis/chat/index.ts (1)
1-13:⚠️ Potential issue | 🟡 MinorAPI 모듈 import도 alias 경로로 통일해주세요.
상대 경로(
../client,./entity) 대신@/*경로를 사용해 일관성을 맞춰주세요.As per coding guidelines:
Use import alias@/* instead of relative paths.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/apis/chat/index.ts` around lines 1 - 13, Replace the relative imports in src/apis/chat/index.ts with alias imports: change "import { apiClient } from '../client';" to use the project alias (e.g., import { apiClient } from '@/apis/client') and change the type import from "./entity" to the alias path (e.g., from '@/apis/chat/entity'); keep the imported symbols (apiClient, ChatMessage, ChatMessageRequestParam, etc.) unchanged so only the module specifiers are updated.
🧹 Nitpick comments (7)
src/components/layout/Header/components/ChatAddHeader.tsx (1)
9-18:ringSlot네이밍을rightSlot으로 맞추는 게 좋습니다.동작에는 문제 없지만 의미가 정확하지 않아 읽을 때 혼동될 수 있습니다.
diff 제안
- const ringSlot = ( + const rightSlot = ( <button type="button" onClick={onConfirm}> <span className="text-primary-500">확인</span> </button> ); @@ - rightSlot={ringSlot} + rightSlot={rightSlot}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/layout/Header/components/ChatAddHeader.tsx` around lines 9 - 18, Rename the local JSX variable ringSlot to rightSlot in the ChatAddHeader component and update its usage where passed into the BackTitleHeader prop (replace ringSlot with rightSlot) so the variable name matches the prop name; ensure the const declaration (previously const ringSlot = ...) and the BackTitleHeader rightSlot={...} reference are both updated and no other references remain to ringSlot.src/apis/chat/queries.ts (1)
10-11: 새 query key도as const로 고정해 타입 일관성을 맞춰주세요.기존
rooms/messages와 동일하게 readonly tuple로 맞추면 invalidate/queryKey 타입 안정성이 좋아집니다.변경 예시
- search: (keyword: string) => [...chatQueryKeys.all, 'search', keyword], - invite: (query: string, sortBy: SortBy) => [...chatQueryKeys.all, 'invite', query, sortBy], + search: (keyword: string) => [...chatQueryKeys.all, 'search', keyword] as const, + invite: (query: string, sortBy: SortBy) => [...chatQueryKeys.all, 'invite', query, sortBy] as const,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/apis/chat/queries.ts` around lines 10 - 11, New query keys returned by chatQueryKeys.search and chatQueryKeys.invite must be fixed to readonly tuple types for consistency; update the return expressions for the functions named search and invite (which currently build arrays using [...chatQueryKeys.all, 'search', keyword] and [...chatQueryKeys.all, 'invite', query, sortBy]) to assert them as const (i.e., append as const) so they produce readonly tuple types and restore queryKey/invalidate type safety consistent with rooms/messages.src/pages/Chat/AddChatRoom.tsx (2)
94-135: 페이지 루트는Layout패턴 적용을 확인해주세요.신규 페이지는
Layout에showBottomNav,contentClassName를 전달하는 컨벤션을 맞추는 게 좋습니다.As per coding guidelines:
src/pages/**/*.tsx: Pass showBottomNav and contentClassName props to Layout component for navigation and styling control.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/AddChatRoom.tsx` around lines 94 - 135, This page's root currently renders a bare div; wrap the page JSX in the app Layout component and pass the required props: add an import for Layout and replace the outer <div className="flex h-full flex-col items-center pt-6"> wrapper by rendering <Layout showBottomNav={false} contentClassName="flex h-full flex-col items-center pt-6"> ...children...</Layout>, keeping inner components like ChatAddHeader, the search label, and the list sections unchanged (ensure ChatAddHeader, Dropdown, InvitableSectionList/InvitableUserList usage remains the same).
21-21: 하드코딩 색상/타이포보다 토큰 사용으로 통일해주세요.
text-#344352,text-[14px],text-[15px]대신 테마/타이포 토큰(text-text-*,text-body*등)으로 맞추는 편이 유지보수에 안전합니다.As per coding guidelines:
Prioritize color tokens from src/styles/theme.cssandUse typography tokens (text-h1 ... text-body3 ...).Also applies to: 114-114
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/AddChatRoom.tsx` at line 21, Replace hardcoded utility classes in the AddChatRoom component with theme color and typography tokens from src/styles/theme.css: change occurrences of className strings like "text-#344352 text-[14px]" (the span rendering clubName) and the similar class at line ~114 to use the project tokens (e.g., text-text-*, text-body1/2/3, text-h1... etc.) so color and font-size come from the theme; locate the spans rendering clubName and the other text nodes in AddChatRoom.tsx and swap the hardcoded classes for the appropriate typography and color tokens according to the coding guidelines.src/pages/Chat/ChatSearch.tsx (3)
162-163: 리스트 key에 index 결합은 가능하면 피해주세요.검색 결과 재정렬 시 재사용 이슈가 생길 수 있어, 가능한 안정적인 고유값 기반 key로 바꾸는 편이 안전합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 162 - 163, The list key currently combines message.roomId with the loop index which can cause item reuse bugs when search results reorder; update the key on the <MessageListItem> to use a stable unique identifier from the message object (e.g., message.id, message.messageId, message.uuid, or a combination like `${message.roomId}-${message.messageId}`) instead of `${message.roomId}-${index}`; locate the map rendering that returns <MessageListItem key=... message={message} keyword={debouncedQuery} /> and replace the key expression with the chosen stable id property.
61-71: 텍스트 스타일은 타이포/컬러 토큰 우선으로 통일해주세요.
text-[16px],text-[12px],text-black같은 하드코딩 대신 토큰 기반 클래스 사용이 일관성에 유리합니다.As per coding guidelines:
Use semantic typography utilitiesandReference design/color tokens from src/styles/theme.css.Also applies to: 101-114
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 61 - 71, The spans and paragraph around room.roomName, the timestamp rendered via formatTime(room.lastSentAt), and the message preview paragraph use hardcoded classes like text-[16px], text-[12px], and color tokens like text-black; replace these with the project's semantic typography utility classes and color/design tokens from src/styles/theme.css so styles are token-driven and consistent (update the span containing room.roomName, the timestamp span using formatTime, and the <p> message preview to use the appropriate typography and color token classes). Ensure you remove any raw pixel/hex classes and map them to the established token names used across the codebase.
145-168: 신규 페이지는Layout사용 패턴 적용을 확인해주세요.
showBottomNav,contentClassName전달 컨벤션을 맞추는 게 좋습니다.As per coding guidelines:
src/pages/**/*.tsx: Control layouts using Layout, showBottomNav, and contentClassName props.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 145 - 168, This page component (ChatSearch) must declare the layout metadata used by the app-level layout system; add the conventional static props to the ChatSearch component: set ChatSearch.Layout to the appropriate Layout component used by other pages, set ChatSearch.showBottomNav to true/false per design, and set ChatSearch.contentClassName to the page content wrapper classes (e.g., the same container classes other pages use) so the Layout system can render consistent chrome; locate the ChatSearch function/component in this file and attach those three properties after the component definition and before the default export.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/apis/chat/entity.ts`:
- Line 1: Replace the relative import in src/apis/chat/entity.ts so it uses the
project alias instead of a relative path: change the import of PaginationParams
and PaginationResponse (currently from '../common/pagination') to the alias path
'@/apis/common/pagination' so the statement that imports PaginationParams and
PaginationResponse uses the `@/`* convention.
In `@src/apis/chat/queries.ts`:
- Around line 2-3: Update the imports in src/apis/chat/queries.ts to use the
project alias instead of relative paths: replace the relative imports of types
and functions (ChatMessagesResponse, SortBy, and the functions getChatMessages,
getChatRooms, getSearchChat, getInvitableFriends) with alias imports starting
from "@/..." so they consistently follow the codebase convention (e.g., import
types from "@/apis/chat/entity" and functions from "@/apis/chat" or the
appropriate aliased module).
In `@src/pages/Chat/AddChatRoom.tsx`:
- Around line 24-25: In AddChatRoom, the clickable row currently uses a div with
onClick (e.g., <div className="flex py-4" onClick={() =>
onToggle(user.userId)}>), which prevents keyboard users from activating the row;
replace the div with a semantic <button> (styled reset) or, if you must keep a
non-button, add role="button", tabIndex={0} and an onKeyDown handler that
triggers onToggle(user.userId) for Enter and Space, and ensure proper accessible
attributes (aria-pressed/aria-label as appropriate); apply the same change to
the other occurrence around lines 41–42 so both rows are keyboard-accessible.
- Around line 97-100: Wrap the createRoomGroup call in a try/catch inside the
onConfirm handler (which currently calls
createRoomGroup(Array.from(selectedUserIds)) and then
navigate(`/chats/${result.chatRoomId}`)); on success keep the navigate behavior,
but in the catch inspect the thrown error for a server 5xx (e.g.
error.response?.status >= 500 && error.response?.status < 600) and call
redirectToServerErrorPage() for that case; for non-5xx failures rethrow or
surface a user-friendly error as appropriate.
- Line 11: Replace the relative import in AddChatRoom.tsx for the useChat hook
with the project alias import: change the line importing useChat (currently
import useChat from './hooks/useChat';) to use the @ alias (e.g., import useChat
from '@/pages/Chat/hooks/useChat';) so it conforms to the project's import alias
rule.
- Line 2: The component currently imports and uses useQuery from
'@tanstack/react-query' but project convention requires useSuspenseQuery for
server state reads; replace the import of useQuery with useSuspenseQuery in
src/pages/Chat/AddChatRoom.tsx and update any calls (e.g., the hook invocation
where useQuery(...) is used) to the useSuspenseQuery API (remove or adjust any
suspense/enabled/options that conflict with suspense behavior), ensuring the
hook returns the same data shape the component expects and that
Suspense/ErrorBoundary wrappers are present where necessary.
- Line 116: The className string in AddChatRoom.tsx contains a Tailwind typo
"text=[13px]"—replace that token with the semantic typography token (either
"text-sub3" or "text-body2", both equal to 13px) instead of fixing it to
"text-[13px]"; update the className (on the element in the AddChatRoom component
where you see className="text=[13px] h-7.25 w-19 font-medium") to use
"text-sub3" (or "text-body2") while keeping the other utility classes intact.
In `@src/pages/Chat/ChatSearch.tsx`:
- Line 2: 현재 ChatSearch.tsx가 React Query의 useQuery를 사용하고 있는데 프로젝트 컨벤션상 검색 데이터
조회는 useSuspenseQuery를 사용해야 합니다; import 문에서 useQuery를 useSuspenseQuery로 교체하고 파일
내에서 호출 중인 useQuery(...) (특히 기존에 위치한 검색 훅 호출 — 대략 라인 138-141 부근) 를 동일한 인수로
useSuspenseQuery(...)로 바꿔 사용하며, 기존에 suspense 옵션을 별도 설정하고 있으면 중복되지 않도록 정리하고 에러/로딩
처리는 Suspense 경계와 에러 바운더리로 위임되었는지 확인하세요.
In `@src/pages/Chat/index.tsx`:
- Line 227: Remove the debug statement console.log(rooms) in the Chat component
(src/pages/Chat/index.tsx) so chat room data is not printed on each render; if
you need runtime diagnostics instead, replace it with a conditional or
environment-guarded debug log (e.g., use a logger.debug or process.env.NODE_ENV
check) referencing the same variable name rooms.
---
Outside diff comments:
In `@src/apis/chat/index.ts`:
- Around line 1-13: Replace the relative imports in src/apis/chat/index.ts with
alias imports: change "import { apiClient } from '../client';" to use the
project alias (e.g., import { apiClient } from '@/apis/client') and change the
type import from "./entity" to the alias path (e.g., from '@/apis/chat/entity');
keep the imported symbols (apiClient, ChatMessage, ChatMessageRequestParam,
etc.) unchanged so only the module specifiers are updated.
---
Nitpick comments:
In `@src/apis/chat/queries.ts`:
- Around line 10-11: New query keys returned by chatQueryKeys.search and
chatQueryKeys.invite must be fixed to readonly tuple types for consistency;
update the return expressions for the functions named search and invite (which
currently build arrays using [...chatQueryKeys.all, 'search', keyword] and
[...chatQueryKeys.all, 'invite', query, sortBy]) to assert them as const (i.e.,
append as const) so they produce readonly tuple types and restore
queryKey/invalidate type safety consistent with rooms/messages.
In `@src/components/layout/Header/components/ChatAddHeader.tsx`:
- Around line 9-18: Rename the local JSX variable ringSlot to rightSlot in the
ChatAddHeader component and update its usage where passed into the
BackTitleHeader prop (replace ringSlot with rightSlot) so the variable name
matches the prop name; ensure the const declaration (previously const ringSlot =
...) and the BackTitleHeader rightSlot={...} reference are both updated and no
other references remain to ringSlot.
In `@src/pages/Chat/AddChatRoom.tsx`:
- Around line 94-135: This page's root currently renders a bare div; wrap the
page JSX in the app Layout component and pass the required props: add an import
for Layout and replace the outer <div className="flex h-full flex-col
items-center pt-6"> wrapper by rendering <Layout showBottomNav={false}
contentClassName="flex h-full flex-col items-center pt-6">
...children...</Layout>, keeping inner components like ChatAddHeader, the search
label, and the list sections unchanged (ensure ChatAddHeader, Dropdown,
InvitableSectionList/InvitableUserList usage remains the same).
- Line 21: Replace hardcoded utility classes in the AddChatRoom component with
theme color and typography tokens from src/styles/theme.css: change occurrences
of className strings like "text-#344352 text-[14px]" (the span rendering
clubName) and the similar class at line ~114 to use the project tokens (e.g.,
text-text-*, text-body1/2/3, text-h1... etc.) so color and font-size come from
the theme; locate the spans rendering clubName and the other text nodes in
AddChatRoom.tsx and swap the hardcoded classes for the appropriate typography
and color tokens according to the coding guidelines.
In `@src/pages/Chat/ChatSearch.tsx`:
- Around line 162-163: The list key currently combines message.roomId with the
loop index which can cause item reuse bugs when search results reorder; update
the key on the <MessageListItem> to use a stable unique identifier from the
message object (e.g., message.id, message.messageId, message.uuid, or a
combination like `${message.roomId}-${message.messageId}`) instead of
`${message.roomId}-${index}`; locate the map rendering that returns
<MessageListItem key=... message={message} keyword={debouncedQuery} /> and
replace the key expression with the chosen stable id property.
- Around line 61-71: The spans and paragraph around room.roomName, the timestamp
rendered via formatTime(room.lastSentAt), and the message preview paragraph use
hardcoded classes like text-[16px], text-[12px], and color tokens like
text-black; replace these with the project's semantic typography utility classes
and color/design tokens from src/styles/theme.css so styles are token-driven and
consistent (update the span containing room.roomName, the timestamp span using
formatTime, and the <p> message preview to use the appropriate typography and
color token classes). Ensure you remove any raw pixel/hex classes and map them
to the established token names used across the codebase.
- Around line 145-168: This page component (ChatSearch) must declare the layout
metadata used by the app-level layout system; add the conventional static props
to the ChatSearch component: set ChatSearch.Layout to the appropriate Layout
component used by other pages, set ChatSearch.showBottomNav to true/false per
design, and set ChatSearch.contentClassName to the page content wrapper classes
(e.g., the same container classes other pages use) so the Layout system can
render consistent chrome; locate the ChatSearch function/component in this file
and attach those three properties after the component definition and before the
default export.
🪄 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: CHILL
Plan: Pro
Run ID: c2c32297-7aee-432d-b995-2fe65988d055
⛔ Files ignored due to path filters (1)
src/assets/svg/check_color.svgis excluded by!**/*.svg,!src/assets/**and included by**
📒 Files selected for processing (13)
src/App.tsxsrc/apis/chat/entity.tssrc/apis/chat/index.tssrc/apis/chat/mutations.tssrc/apis/chat/queries.tssrc/components/layout/Header/components/ChatAddHeader.tsxsrc/components/layout/Header/components/ChatListHeader.tsxsrc/components/layout/Header/components/ChatSearchHeader.tsxsrc/pages/Chat/AddChatRoom.tsxsrc/pages/Chat/ChatSearch.tsxsrc/pages/Chat/hooks/useChat.tssrc/pages/Chat/hooks/useChatMutations.tssrc/pages/Chat/index.tsx
| <div className="flex py-4" onClick={() => onToggle(user.userId)}> | ||
| <img src={user.imageUrl} alt="프로필" className="size-10 rounded-xl" /> |
There was a problem hiding this comment.
선택 행을 div 클릭으로만 처리하면 키보드 접근이 막힙니다.
행 루트를 button(또는 role="button" + 키보드 핸들러)로 바꿔 접근성을 보장해주세요.
Also applies to: 41-42
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/AddChatRoom.tsx` around lines 24 - 25, In AddChatRoom, the
clickable row currently uses a div with onClick (e.g., <div className="flex
py-4" onClick={() => onToggle(user.userId)}>), which prevents keyboard users
from activating the row; replace the div with a semantic <button> (styled reset)
or, if you must keep a non-button, add role="button", tabIndex={0} and an
onKeyDown handler that triggers onToggle(user.userId) for Enter and Space, and
ensure proper accessible attributes (aria-pressed/aria-label as appropriate);
apply the same change to the other occurrence around lines 41–42 so both rows
are keyboard-accessible.
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/layout/Header/components/ChatRoomMoreHeader.tsx`:
- Around line 17-30: The toggle button in ChatRoomMoreHeader.tsx (the button
that calls toggleMute(numericRoomId) and uses isMuted / isTogglingMute) lacks
accessibility attributes; update that button to include role="switch", set
aria-checked to the boolean isMuted, and add an appropriate aria-label that
reflects the current action/state (e.g., "Mute room" / "Unmute room") so screen
readers announce state and purpose; ensure these attributes are applied to the
same button element and keep existing disabled/onclick behavior intact.
- Line 4: The import in ChatRoomMoreHeader.tsx uses a relative path for
BackTitleHeader; change the import of BackTitleHeader in ChatRoomMoreHeader.tsx
to use the project path alias (e.g., start with `@/`) instead of
'./BackTitleHeader' so it matches the codebase convention for alias imports.
- Line 16: Replace the invalid Tailwind class on the span in
ChatRoomMoreHeader.tsx by using the design token and typography utility: swap
`text-#5A6B7F px-1` for `text-[--color-text-500] text-body3 px-1` and keep the
existing isMuted conditional string logic; then refactor the template-literal
class construction used in the block around lines 21-28 into the shared cn()
utility (use cn(...) with conditional arguments) to simplify dynamic class
composition and ensure consistent class ordering.
- Around line 7-10: chatRoomId is being converted with Number(...) into
numericRoomId which can be NaN and is then passed to useChat(numericRoomId) and
toggleMute, causing find comparisons to fail; fix by validating numericRoomId
immediately after conversion (e.g., Number.isFinite or !Number.isNaN) and only
call useChat and any toggleMute handlers when the id is valid — if invalid,
return a safe fallback UI or set numericRoomId to undefined/null and ensure
useChat and toggleMute accept that (or short-circuit their invocation); update
references in this file to use the validated id (chatRoomId, numericRoomId,
useChat, toggleMute, and the find usage) so no NaN is passed.
In `@src/pages/Chat/ChatRoomMore.tsx`:
- Around line 40-72: The ChatRoomMore component currently returns its JSX root
directly instead of wrapping with the app Layout and passing the required props;
update the ChatRoomMore component (the exported function/component named
ChatRoomMore) to render <Layout showBottomNav={...} contentClassName={...}> as
the root wrapper and move the current returned div into Layout's children,
passing appropriate boolean for showBottomNav and the styling string to
contentClassName so the bottom navigation and content styling are controlled by
the Layout; keep existing props/state like isClubGroup and handleLeaveChatRoom
unchanged and ensure the Layout import is added if missing.
- Around line 20-37: The handleLeaveChatRoom flow lacks error handling for
deleteChatRoom and kickMember: wrap the deleteChatRoom(numericRoomId) and
kickMember({ chatRoomId: numericRoomId, targetUserId: currentUserId }) calls in
try/catch blocks (or a single try covering the branch) so errors are caught; on
catch call showToast with an error message (and optional error.message) and do
not call navigate('/chats', { replace: true }) unless the API call succeeds;
keep existing checks for chatRoom.chatType, currentUserId, and use the same
symbols (handleLeaveChatRoom, deleteChatRoom, kickMember, showToast, navigate,
numericRoomId).
- Line 66: The button in ChatRoomMore.tsx currently hardcodes the color with
text-[`#FF4E4E`]; replace that with the theme color token from
src/styles/theme.css (e.g., use a CSS variable or existing token class) so dark
mode and theming work correctly — update the className to use the token (for
example text-[var(--color-danger)] or the project’s equivalent token class)
instead of text-[`#FF4E4E`], and ensure any related hover/disabled styles also
reference the theme token.
- Around line 7-14: Validate the URL param before passing it into useChat: check
that chatRoomId exists and parse it with parseInt, ensure it's a positive
integer (numericRoomId > 0); if invalid, call navigate to a safe route (or
showToast then navigate) and do not invoke useChat with the invalid value so the
component won't hang on a failed query. Replace the inline Tailwind hex class
text-[`#FF4E4E`] with the project danger color token from src/styles/colors.css
(e.g., danger-500 or danger-600) to use the established color tokens. Ensure
references: useParams/chatRoomId, numericRoomId, useChat, navigate, showToast,
and the inline class text-[`#FF4E4E`] are updated accordingly.
🪄 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: CHILL
Plan: Pro
Run ID: d296e2ed-9286-456d-9927-c95d91ddccd8
📒 Files selected for processing (6)
src/apis/chat/entity.tssrc/apis/chat/queries.tssrc/components/layout/Header/components/ChatRoomMoreHeader.tsxsrc/pages/Chat/AddChatRoom.tsxsrc/pages/Chat/ChatRoomMore.tsxsrc/pages/Chat/ChatSearch.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- src/pages/Chat/AddChatRoom.tsx
- src/apis/chat/queries.ts
- src/pages/Chat/ChatSearch.tsx
- src/apis/chat/entity.ts
| <button | ||
| type="button" | ||
| disabled={isTogglingMute} | ||
| onClick={() => void toggleMute(numericRoomId)} | ||
| className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${ | ||
| isMuted ? 'bg-primary-500' : 'bg-gray-300' | ||
| } disabled:cursor-not-allowed disabled:opacity-60`} | ||
| > | ||
| <span | ||
| className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${ | ||
| isMuted ? 'translate-x-6' : 'translate-x-1' | ||
| }`} | ||
| /> | ||
| </button> |
There was a problem hiding this comment.
스위치 접근성 속성을 추가해주세요.
현재 버튼은 시각적으로만 토글이고, 스크린리더에서 상태를 알기 어렵습니다(role="switch", aria-checked, aria-label 권장).
수정 예시
<button
type="button"
+ role="switch"
+ aria-checked={isMuted}
+ aria-label={isMuted ? '채팅방 알림 켜기' : '채팅방 알림 끄기'}
disabled={isTogglingMute}
onClick={() => void toggleMute(numericRoomId)}As per coding guidelines, src/components/**: 접근성(aria-*, role, 키보드 탐색)이 적절히 처리되는지.
📝 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.
| <button | |
| type="button" | |
| disabled={isTogglingMute} | |
| onClick={() => void toggleMute(numericRoomId)} | |
| className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${ | |
| isMuted ? 'bg-primary-500' : 'bg-gray-300' | |
| } disabled:cursor-not-allowed disabled:opacity-60`} | |
| > | |
| <span | |
| className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${ | |
| isMuted ? 'translate-x-6' : 'translate-x-1' | |
| }`} | |
| /> | |
| </button> | |
| <button | |
| type="button" | |
| role="switch" | |
| aria-checked={isMuted} | |
| aria-label={isMuted ? '채팅방 알림 켜기' : '채팅방 알림 끄기'} | |
| disabled={isTogglingMute} | |
| onClick={() => void toggleMute(numericRoomId)} | |
| className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${ | |
| isMuted ? 'bg-primary-500' : 'bg-gray-300' | |
| } disabled:cursor-not-allowed disabled:opacity-60`} | |
| > | |
| <span | |
| className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${ | |
| isMuted ? 'translate-x-6' : 'translate-x-1' | |
| }`} | |
| /> | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/layout/Header/components/ChatRoomMoreHeader.tsx` around lines
17 - 30, The toggle button in ChatRoomMoreHeader.tsx (the button that calls
toggleMute(numericRoomId) and uses isMuted / isTogglingMute) lacks accessibility
attributes; update that button to include role="switch", set aria-checked to the
boolean isMuted, and add an appropriate aria-label that reflects the current
action/state (e.g., "Mute room" / "Unmute room") so screen readers announce
state and purpose; ensure these attributes are applied to the same button
element and keep existing disabled/onclick behavior intact.
| const { chatRoomId } = useParams(); | ||
| const numericRoomId = Number(chatRoomId); | ||
| const navigate = useNavigate(); | ||
| const { showToast } = useToastContext(); | ||
| const user = useAuthStore((s) => s.user); | ||
|
|
||
| const { chatRoomList, clubMembers, deleteChatRoom, isDeletingChatRoom, kickMember, isKickingMember } = | ||
| useChat(numericRoomId); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat src/pages/Chat/ChatRoomMore.tsxRepository: BCSDLab/KONECT_FRONT_END
Length of output: 2813
🏁 Script executed:
find . -name "*.ts" -o -name "*.tsx" | xargs grep -l "const useChat" | head -5Repository: BCSDLab/KONECT_FRONT_END
Length of output: 145
🏁 Script executed:
find . -path "*/theme.css" -o -path "*/theme.ts"Repository: BCSDLab/KONECT_FRONT_END
Length of output: 90
🏁 Script executed:
cat src/pages/Chat/hooks/useChat.tsRepository: BCSDLab/KONECT_FRONT_END
Length of output: 2370
🏁 Script executed:
cat src/styles/theme.css | head -100Repository: BCSDLab/KONECT_FRONT_END
Length of output: 216
🏁 Script executed:
rg "FF4E4E|error|danger|red" src/styles/theme.cssRepository: BCSDLab/KONECT_FRONT_END
Length of output: 50
🏁 Script executed:
wc -l src/styles/theme.cssRepository: BCSDLab/KONECT_FRONT_END
Length of output: 90
🏁 Script executed:
cat src/styles/theme.css | tail -200Repository: BCSDLab/KONECT_FRONT_END
Length of output: 216
🏁 Script executed:
rg "red|danger|error|alert" src/styles/ -t css -iRepository: BCSDLab/KONECT_FRONT_END
Length of output: 580
🏁 Script executed:
find . -name "Layout.tsx" -o -name "Layout.ts"Repository: BCSDLab/KONECT_FRONT_END
Length of output: 50
🏁 Script executed:
find . -type f -iname "*layout*" | head -10Repository: BCSDLab/KONECT_FRONT_END
Length of output: 413
🏁 Script executed:
ls -la src/pages/Chat/Repository: BCSDLab/KONECT_FRONT_END
Length of output: 685
🏁 Script executed:
find . -name "queries.ts" | xargs rg "messages" | head -20Repository: BCSDLab/KONECT_FRONT_END
Length of output: 500
🏁 Script executed:
ls -la src/layouts/ 2>/dev/null || echo "No layouts directory"Repository: BCSDLab/KONECT_FRONT_END
Length of output: 88
🏁 Script executed:
head -50 src/pages/Chat/ChatRoom.tsxRepository: BCSDLab/KONECT_FRONT_END
Length of output: 2039
🏁 Script executed:
head -50 src/pages/Chat/index.tsxRepository: BCSDLab/KONECT_FRONT_END
Length of output: 1799
🏁 Script executed:
cat src/apis/chat/queries.tsRepository: BCSDLab/KONECT_FRONT_END
Length of output: 1845
🏁 Script executed:
ls -la src/components/layout/Repository: BCSDLab/KONECT_FRONT_END
Length of output: 547
🏁 Script executed:
cat src/components/layout/index.tsxRepository: BCSDLab/KONECT_FRONT_END
Length of output: 3087
🏁 Script executed:
grep -r "Layout" src/pages/Chat/*.tsx | head -5Repository: BCSDLab/KONECT_FRONT_END
Length of output: 50
🏁 Script executed:
grep -r "showBottomNav\|contentClassName" src/pages/ | head -10Repository: BCSDLab/KONECT_FRONT_END
Length of output: 50
URL 파라미터 검증을 추가하고 색상 토큰을 사용해주세요.
Number(chatRoomId) 결과가 유효한 양수 정수인지 검증 없이 쿼리에 전달되고 있습니다. 잘못된 파라미터가 들어올 경우 데이터 로드 실패 상태가 지속되므로, 라우트 진입 시점에 유효성 체크 후 필요시 리다이렉트를 추가하세요. 또한 Line 65의 text-[#FF4E4E]를 src/styles/colors.css의 danger 토큰(danger-500, danger-600 등)으로 변경해주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/ChatRoomMore.tsx` around lines 7 - 14, Validate the URL param
before passing it into useChat: check that chatRoomId exists and parse it with
parseInt, ensure it's a positive integer (numericRoomId > 0); if invalid, call
navigate to a safe route (or showToast then navigate) and do not invoke useChat
with the invalid value so the component won't hang on a failed query. Replace
the inline Tailwind hex class text-[`#FF4E4E`] with the project danger color token
from src/styles/colors.css (e.g., danger-500 or danger-600) to use the
established color tokens. Ensure references: useParams/chatRoomId,
numericRoomId, useChat, navigate, showToast, and the inline class text-[`#FF4E4E`]
are updated accordingly.
| const handleLeaveChatRoom = async () => { | ||
| if (!chatRoom) return; | ||
|
|
||
| if (chatRoom.chatType === 'DIRECT') { | ||
| await deleteChatRoom(numericRoomId); | ||
| navigate('/chats', { replace: true }); | ||
| return; | ||
| } | ||
|
|
||
| if (chatRoom.chatType === 'GROUP') { | ||
| const currentUserId = clubMembers.find((m) => m.studentNumber === user?.studentNumber)?.userId; | ||
| if (!currentUserId) { | ||
| showToast('사용자 정보를 찾을 수 없습니다.', 'error'); | ||
| return; | ||
| } | ||
| await kickMember({ chatRoomId: numericRoomId, targetUserId: currentUserId }); | ||
| navigate('/chats', { replace: true }); | ||
| } |
There was a problem hiding this comment.
채팅방 나가기 API 실패 처리가 없어 UX가 끊깁니다.
deleteChatRoom/kickMember 실패 시 try/catch가 없어 토스트 안내 없이 함수가 중단됩니다. 실패 시 에러 토스트를 노출하고 성공 시에만 이동하도록 분기해 주세요.
🔧 제안 수정안
const handleLeaveChatRoom = async () => {
if (!chatRoom) return;
- if (chatRoom.chatType === 'DIRECT') {
- await deleteChatRoom(numericRoomId);
- navigate('/chats', { replace: true });
- return;
- }
-
- if (chatRoom.chatType === 'GROUP') {
- const currentUserId = clubMembers.find((m) => m.studentNumber === user?.studentNumber)?.userId;
- if (!currentUserId) {
- showToast('사용자 정보를 찾을 수 없습니다.', 'error');
- return;
- }
- await kickMember({ chatRoomId: numericRoomId, targetUserId: currentUserId });
- navigate('/chats', { replace: true });
- }
+ try {
+ if (chatRoom.chatType === 'DIRECT') {
+ await deleteChatRoom(numericRoomId);
+ navigate('/chats', { replace: true });
+ return;
+ }
+
+ if (chatRoom.chatType === 'GROUP') {
+ const currentUserId = clubMembers.find((m) => m.studentNumber === user?.studentNumber)?.userId;
+ if (!currentUserId) {
+ showToast('사용자 정보를 찾을 수 없습니다.', 'error');
+ return;
+ }
+ await kickMember({ chatRoomId: numericRoomId, targetUserId: currentUserId });
+ navigate('/chats', { replace: true });
+ }
+ } catch {
+ showToast('채팅방 나가기에 실패했습니다.', 'error');
+ }
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/ChatRoomMore.tsx` around lines 20 - 37, The
handleLeaveChatRoom flow lacks error handling for deleteChatRoom and kickMember:
wrap the deleteChatRoom(numericRoomId) and kickMember({ chatRoomId:
numericRoomId, targetUserId: currentUserId }) calls in try/catch blocks (or a
single try covering the branch) so errors are caught; on catch call showToast
with an error message (and optional error.message) and do not call
navigate('/chats', { replace: true }) unless the API call succeeds; keep
existing checks for chatRoom.chatType, currentUserId, and use the same symbols
(handleLeaveChatRoom, deleteChatRoom, kickMember, showToast, navigate,
numericRoomId).
| return ( | ||
| <div className="bg-text-100 flex min-h-full flex-col items-center px-4 py-6"> | ||
| <div className="w-89.5 rounded-2xl bg-white"> | ||
| <p className="text-sub2 px-4 py-3 text-gray-500">친구 ({clubMembers.length})</p> | ||
| <ul> | ||
| {clubMembers.map((member) => ( | ||
| <li key={member.userId} className="flex items-center gap-3 px-4 py-2"> | ||
| {member.imageUrl ? ( | ||
| <img src={member.imageUrl} alt={member.name} className="h-10 w-10 rounded-full object-cover" /> | ||
| ) : ( | ||
| <div className="text-sub2 flex h-10 w-10 items-center justify-center rounded-full bg-gray-200 text-gray-600"> | ||
| {member.name[0]} | ||
| </div> | ||
| )} | ||
| <span className="text-body1 text-gray-800"> | ||
| {member.name} ({member.studentNumber}) | ||
| </span> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </div> | ||
| {!isClubGroup && ( | ||
| <button | ||
| type="button" | ||
| disabled={isLeaving} | ||
| onClick={() => void handleLeaveChatRoom()} | ||
| className="mt-6 mb-[25px] flex h-13.25 w-89.5 shrink-0 items-center justify-center rounded-3xl bg-white text-[#FF4E4E] disabled:cursor-not-allowed disabled:opacity-60" | ||
| > | ||
| 채팅방 나가기 | ||
| </button> | ||
| )} | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
페이지 루트를 Layout으로 감싸고 showBottomNav, contentClassName를 전달해주세요.
현재 페이지 컴포넌트가 Layout 규약을 따르지 않아 내비게이션/컨텐츠 영역 제어 일관성이 깨질 수 있습니다.
As per coding guidelines, src/pages/**/*.{ts,tsx}: Pass showBottomNav and contentClassName props to Layout component for navigation and styling control.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/ChatRoomMore.tsx` around lines 40 - 72, The ChatRoomMore
component currently returns its JSX root directly instead of wrapping with the
app Layout and passing the required props; update the ChatRoomMore component
(the exported function/component named ChatRoomMore) to render <Layout
showBottomNav={...} contentClassName={...}> as the root wrapper and move the
current returned div into Layout's children, passing appropriate boolean for
showBottomNav and the styling string to contentClassName so the bottom
navigation and content styling are controlled by the Layout; keep existing
props/state like isClubGroup and handleLeaveChatRoom unchanged and ensure the
Layout import is added if missing.
ff1451
left a comment
There was a problem hiding this comment.
리뷰량이 좀 많은데 천천히 읽어보면서 확인 부탁드려요
디자인과 다른 부분도 많이 보여서 피그마와 한번 대조해보시는것도 좋을 것 같아요
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/pages/Chat/AddChatRoom.tsx (2)
27-27:⚠️ Potential issue | 🟠 Major선택 행을
div클릭으로만 처리하면 키보드 접근이 막힙니다.행 루트를
button으로 바꾸거나role="button",tabIndex,onKeyDown(Enter/Space)를 추가해주세요. 현재는 키보드 사용자 선택이 불가합니다.변경 예시
- <div className="flex py-4 pr-3" onClick={() => onToggle(user.userId)}> + <button + type="button" + className="flex w-full py-4 pr-3 text-left" + onClick={() => onToggle(user.userId)} + aria-pressed={selectedUserIds.has(user.userId)} + > ... - </div> + </button>As per coding guidelines:
접근성(aria-*, role, 키보드 탐색)이 적절히 처리되는지.Also applies to: 44-45
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/AddChatRoom.tsx` at line 27, The clickable row currently implemented as <div className="flex py-4 pr-3" onClick={() => onToggle(user.userId)}> blocks keyboard access; update the row (in AddChatRoom.tsx where onToggle(user.userId) is used, and the similar instance at the 44-45 area) to be keyboard-accessible by either converting the root element to a <button> or adding role="button", tabIndex={0}, and an onKeyDown handler that triggers onToggle(user.userId) for Enter and Space keys; ensure focus styles and aria attributes are preserved and no visual layout regression occurs.
123-123:⚠️ Potential issue | 🟡 Minor
text-#344352는 유효한 Tailwind 클래스가 아니라 색상이 적용되지 않습니다.토큰 기반 텍스트 컬러 클래스로 교체해주세요(예:
text-text-700).변경 예시
- <span className="text-#344352 flex-1 translate-y-2 text-[15px]">친구 선택({data?.currentCount})</span> + <span className="text-text-700 flex-1 translate-y-2 text-[15px]">친구 선택({data?.currentCount})</span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/AddChatRoom.tsx` at line 123, In the AddChatRoom component replace the invalid Tailwind class name "text-#344352" on the span (the label showing 친구 선택({data?.currentCount})) with a design-token text color class (for example "text-text-700"); update the className on that span to use the tokenized color so Tailwind can apply the color correctly.
🧹 Nitpick comments (5)
src/components/common/Dropdown.tsx (1)
50-53: 트리거 배경색 하드코딩 대신 디자인 토큰을 사용해주세요.
bg-[#69BFDF]는 테마 일관성 유지가 어렵습니다. 토큰 클래스(예:bg-primary계열)로 치환하는 게 안전합니다.변경 예시
- 'inline-flex h-7.25 items-center justify-evenly overflow-hidden rounded-full bg-[`#69BFDF`] px-2.5 text-[13px] leading-[1.6] font-medium text-white transition-opacity active:opacity-90', + 'inline-flex h-7.25 items-center justify-evenly overflow-hidden rounded-full bg-primary px-2.5 text-[13px] leading-[1.6] font-medium text-white transition-opacity active:opacity-90',As per coding guidelines:
Prioritize color tokens from src/styles/theme.css (e.g., indigo-*, blue-*, background, primary) over hardcoded colors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/Dropdown.tsx` around lines 50 - 53, Replace the hardcoded background color in the trigger class list (the string containing 'bg-[`#69BFDF`]' in Dropdown.tsx) with the appropriate design token from src/styles/theme.css (for example a 'bg-primary' or 'bg-indigo-*' / 'bg-blue-*' token), updating the class string inside the same classnames call that builds the trigger classes so theme tokens are used instead of the hex literal; ensure triggerClassName and isOpen logic remain unchanged.src/pages/Chat/index.tsx (1)
10-10: import는 alias 경로(@/*)로 맞춰 주세요.Line 10은 상대경로 대신 alias import를 사용하는 컨벤션과 다릅니다.
diff 제안
-import { ChatRoomListItem } from './components/ChatRoomListItem'; +import { ChatRoomListItem } from '@/pages/Chat/components/ChatRoomListItem';As per coding guidelines, "
src/**/*.{ts,tsx,js,jsx}: Use path alias@/*for imports instead of relative paths".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/index.tsx` at line 10, The import on line importing ChatRoomListItem uses a relative path; update it to the project path-alias style by replacing the relative import with the alias import for the ChatRoomListItem component (referencing ChatRoomListItem from the components folder) so it follows the "@/..." convention used across src/**/*.{ts,tsx,js,jsx}.src/pages/Chat/ChatSearch.tsx (2)
35-35: 하이라이트 색상은 토큰 사용을 권장합니다.
text-black대신 테마 토큰 기반 색상 클래스로 맞추는 게 일관성에 더 좋습니다.As per coding guidelines, "
src/{pages,components}/**/*.tsx: Reference design tokens fromsrc/styles/theme.cssTailwind@themeconfiguration".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` at line 35, The highlighted span in the ChatSearch component (the JSX that renders {keyword} when index < parts.length - 1) uses the hardcoded class "text-black"; update that class to use the theme token-based Tailwind class from your design tokens (the token defined in src/styles/theme.css via the Tailwind `@theme` config) so the highlight color follows the theme—replace "text-black" with the appropriate token-based class for highlight text (use the token name from theme.css).
8-9: 상대경로 import를 alias import로 바꿔 주세요.Line 8-9는
@/*alias 규칙과 다릅니다.diff 제안
-import { ChatRoomAvatar, ChatRoomListItem } from './components/ChatRoomListItem'; -import { formatTime } from './utils/formatTime'; +import { ChatRoomAvatar, ChatRoomListItem } from '@/pages/Chat/components/ChatRoomListItem'; +import { formatTime } from '@/pages/Chat/utils/formatTime';As per coding guidelines, "
**/*.{ts,tsx}: Use import alias@/*instead of relative paths".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 8 - 9, Change the relative imports in ChatSearch.tsx to use the project alias pattern (`@/`*) instead of "../" paths: replace the imports that reference './components/ChatRoomListItem' and './utils/formatTime' so they import ChatRoomAvatar and ChatRoomListItem and formatTime via the alias (e.g., '@/pages/Chat/components/ChatRoomListItem' and '@/pages/Chat/utils/formatTime' or the correct alias path for these modules in your repo); ensure the imported symbols ChatRoomAvatar, ChatRoomListItem, and formatTime stay unchanged and that TypeScript path aliases are configured so the file compiles.src/pages/Chat/components/ChatRoomListItem.tsx (1)
50-67: 타이포는 디자인 토큰 클래스로 통일해 주세요.현재
text-[16px],text-[12px],text-[10px]같은 임의 값이 섞여 있습니다.text-sub*,text-body*,text-cap*토큰으로 맞추면 페이지 간 일관성이 좋아집니다.As per coding guidelines, "
src/{pages,components,layouts}/**/*.tsx: Use semantic typography utilities (text-h1,text-body1, etc.) before custom CSS".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/components/ChatRoomListItem.tsx` around lines 50 - 67, The component ChatRoomListItem mixes raw pixel typography classes (e.g., text-[16px], text-[12px], text-[10px]) which breaks design-token consistency; update the JSX nodes that render room.roomName, the timestamp (formatTime), the previewMessage paragraph, and the unread badge text to use the project's semantic typography tokens (e.g., text-h*, text-body*, text-cap* / text-sub* as appropriate) instead of the inline pixel classes so they match the coding guideline for semantic typography utilities; keep other utility classes (truncate, leading, font-*) intact while swapping only the text-[] classes to the corresponding token names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/layout/Header/components/SubpageHeader.tsx`:
- Around line 20-22: 현재 headerStyle 선언의 삼항 연산자 줄바꿈 때문에 Prettier/CI가 실패합니다;
headerStyle, headerClassName, CSSProperties 식을 한 줄로 포맷해 삼항식을 단일 라인으로 변경하세요 (예:
const headerStyle = headerClassName ? undefined : ({ minHeight:
'var(--subpage-header-height)' } as CSSProperties);) so Prettier 규칙을 만족하도록 포맷만
조정하면 됩니다.
In `@src/pages/Chat/AddChatRoom.tsx`:
- Around line 81-85: The catch block inside onConfirm currently swallows non-5xx
errors; update the error handling in onConfirm so that if isApiError(error) &&
isServerErrorStatus(error.status) you call redirectToServerErrorPage(), but for
all other errors you either rethrow the error or surface user feedback (e.g.,
set an error state/notification) so the error reaches the UI/error boundary;
locate the catch handling around onConfirm and adjust behavior for errors that
do not match isServerErrorStatus to avoid silent failures.
---
Duplicate comments:
In `@src/pages/Chat/AddChatRoom.tsx`:
- Line 27: The clickable row currently implemented as <div className="flex py-4
pr-3" onClick={() => onToggle(user.userId)}> blocks keyboard access; update the
row (in AddChatRoom.tsx where onToggle(user.userId) is used, and the similar
instance at the 44-45 area) to be keyboard-accessible by either converting the
root element to a <button> or adding role="button", tabIndex={0}, and an
onKeyDown handler that triggers onToggle(user.userId) for Enter and Space keys;
ensure focus styles and aria attributes are preserved and no visual layout
regression occurs.
- Line 123: In the AddChatRoom component replace the invalid Tailwind class name
"text-#344352" on the span (the label showing 친구 선택({data?.currentCount})) with
a design-token text color class (for example "text-text-700"); update the
className on that span to use the tokenized color so Tailwind can apply the
color correctly.
---
Nitpick comments:
In `@src/components/common/Dropdown.tsx`:
- Around line 50-53: Replace the hardcoded background color in the trigger class
list (the string containing 'bg-[`#69BFDF`]' in Dropdown.tsx) with the appropriate
design token from src/styles/theme.css (for example a 'bg-primary' or
'bg-indigo-*' / 'bg-blue-*' token), updating the class string inside the same
classnames call that builds the trigger classes so theme tokens are used instead
of the hex literal; ensure triggerClassName and isOpen logic remain unchanged.
In `@src/pages/Chat/ChatSearch.tsx`:
- Line 35: The highlighted span in the ChatSearch component (the JSX that
renders {keyword} when index < parts.length - 1) uses the hardcoded class
"text-black"; update that class to use the theme token-based Tailwind class from
your design tokens (the token defined in src/styles/theme.css via the Tailwind
`@theme` config) so the highlight color follows the theme—replace "text-black"
with the appropriate token-based class for highlight text (use the token name
from theme.css).
- Around line 8-9: Change the relative imports in ChatSearch.tsx to use the
project alias pattern (`@/`*) instead of "../" paths: replace the imports that
reference './components/ChatRoomListItem' and './utils/formatTime' so they
import ChatRoomAvatar and ChatRoomListItem and formatTime via the alias (e.g.,
'@/pages/Chat/components/ChatRoomListItem' and '@/pages/Chat/utils/formatTime'
or the correct alias path for these modules in your repo); ensure the imported
symbols ChatRoomAvatar, ChatRoomListItem, and formatTime stay unchanged and that
TypeScript path aliases are configured so the file compiles.
In `@src/pages/Chat/components/ChatRoomListItem.tsx`:
- Around line 50-67: The component ChatRoomListItem mixes raw pixel typography
classes (e.g., text-[16px], text-[12px], text-[10px]) which breaks design-token
consistency; update the JSX nodes that render room.roomName, the timestamp
(formatTime), the previewMessage paragraph, and the unread badge text to use the
project's semantic typography tokens (e.g., text-h*, text-body*, text-cap* /
text-sub* as appropriate) instead of the inline pixel classes so they match the
coding guideline for semantic typography utilities; keep other utility classes
(truncate, leading, font-*) intact while swapping only the text-[] classes to
the corresponding token names.
In `@src/pages/Chat/index.tsx`:
- Line 10: The import on line importing ChatRoomListItem uses a relative path;
update it to the project path-alias style by replacing the relative import with
the alias import for the ChatRoomListItem component (referencing
ChatRoomListItem from the components folder) so it follows the "@/..."
convention used across src/**/*.{ts,tsx,js,jsx}.
🪄 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: CHILL
Plan: Pro
Run ID: 07726903-bade-4ef1-b74c-95f8d430e457
📒 Files selected for processing (12)
src/components/common/Dropdown.tsxsrc/components/layout/Header/components/ChatAddHeader.tsxsrc/components/layout/Header/components/SubpageHeader.tsxsrc/components/layout/Header/headerConfig.tssrc/components/layout/Header/index.tsxsrc/components/layout/Header/routeTitles.tssrc/components/layout/Header/types.tssrc/pages/Chat/AddChatRoom.tsxsrc/pages/Chat/ChatSearch.tsxsrc/pages/Chat/components/ChatRoomListItem.tsxsrc/pages/Chat/index.tsxsrc/pages/Chat/utils/formatTime.ts
✅ Files skipped from review due to trivial changes (4)
- src/components/layout/Header/types.ts
- src/components/layout/Header/routeTitles.ts
- src/pages/Chat/utils/formatTime.ts
- src/components/layout/Header/components/ChatAddHeader.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/pages/Chat/ChatSearch.tsx (2)
48-57: 검색 결과 없음 상태 처리 고려검색 결과가 없을 때 빈 화면이 표시됩니다. 사용자 경험을 위해 "검색 결과가 없습니다" 메시지 추가를 고려해보세요.
💡 제안
return ( <div className="mt-6 h-full w-87.5 overflow-hidden rounded-t-2xl bg-white py-4"> + {!data?.roomMatches?.rooms?.length && !data?.messageMatches?.messages?.length && ( + <p className="text-text-400 py-8 text-center">검색 결과가 없습니다</p> + )} {data?.roomMatches?.rooms?.map((room) => (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 48 - 57, ChatSearch currently renders nothing when there are no room or message matches, causing an empty screen; update the JSX in ChatSearch.tsx to detect when both data?.roomMatches?.rooms and data?.messageMatches?.messages are empty or undefined and render a user-facing placeholder (e.g., a centered "검색 결과가 없습니다" message) instead of the blank div; ensure the placeholder is shown while preserving existing rendering of ChatRoomListItem and MessageListItem and keep the current key/props (room.roomId, `${message.roomId}-${index}`, keyword) logic intact.
45-58:ChatSearchResults를 Suspense 경계로 감싸서 검색 중 로딩 상태 표시라우트 레벨의 Suspense가 있지만,
ChatSearchResults주변에 로컬 Suspense 경계를 추가하여 검색 중에만 로딩 상태를 표시하는 것이 더 나은 UX를 제공합니다.{debouncedQuery && ( <Suspense fallback={<div className="mt-6">로딩 중...</div>}> <ChatSearchResults keyword={debouncedQuery} /> </Suspense> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatSearch.tsx` around lines 45 - 58, Wrap ChatSearchResults in a local React Suspense boundary so only the search area shows a loading state while useSuspenseQuery resolves: where debouncedQuery is used to conditionally render the results, replace the direct <ChatSearchResults keyword={debouncedQuery} /> with a Suspense fallback (for example a simple div with "로딩 중...") that renders <ChatSearchResults keyword={debouncedQuery} /> inside it; this keeps the existing ChatSearchResults component and its useSuspenseQuery call unchanged while adding the local Suspense wrapper around that render.src/pages/Chat/ChatRoom.tsx (1)
85-91:searchParams.get('messageId')중복 호출 개선동일한 값을 두 번 파싱하고 있습니다. 변수로 추출하면 더 깔끔합니다.
♻️ 제안
const [searchParams] = useSearchParams(); - const targetMessageId = searchParams.get('messageId') ? Number(searchParams.get('messageId')) : undefined; + const messageIdParam = searchParams.get('messageId'); + const targetMessageId = messageIdParam ? Number(messageIdParam) : undefined;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/Chat/ChatRoom.tsx` around lines 85 - 91, The code calls searchParams.get('messageId') twice; extract it once into a local variable (e.g., const messageIdParam = searchParams.get('messageId')) then compute targetMessageId from that single variable and pass it to useChat; update the targetMessageId assignment and the useChat call to use messageIdParam to avoid duplicate parsing and improve readability (refer to searchParams, messageIdParam, targetMessageId, and useChat).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/Chat/ChatRoom.tsx`:
- Around line 109-115: The effect scrolls to targetMessageId every time
chatMessages changes, causing unwanted repeated scrolling; add a ref flag (e.g.,
hasScrolledToTargetRef) to ensure the scroll happens only once per
targetMessageId: create const hasScrolledToTargetRef = useRef(false), in the
scrolling useEffect (watching targetMessageId and chatMessages) return early if
hasScrolledToTargetRef.current is true, perform scroll and then set
hasScrolledToTargetRef.current = true, and add a separate useEffect that resets
hasScrolledToTargetRef.current = false whenever targetMessageId changes so each
new target only scrolls once.
---
Nitpick comments:
In `@src/pages/Chat/ChatRoom.tsx`:
- Around line 85-91: The code calls searchParams.get('messageId') twice; extract
it once into a local variable (e.g., const messageIdParam =
searchParams.get('messageId')) then compute targetMessageId from that single
variable and pass it to useChat; update the targetMessageId assignment and the
useChat call to use messageIdParam to avoid duplicate parsing and improve
readability (refer to searchParams, messageIdParam, targetMessageId, and
useChat).
In `@src/pages/Chat/ChatSearch.tsx`:
- Around line 48-57: ChatSearch currently renders nothing when there are no room
or message matches, causing an empty screen; update the JSX in ChatSearch.tsx to
detect when both data?.roomMatches?.rooms and data?.messageMatches?.messages are
empty or undefined and render a user-facing placeholder (e.g., a centered "검색
결과가 없습니다" message) instead of the blank div; ensure the placeholder is shown
while preserving existing rendering of ChatRoomListItem and MessageListItem and
keep the current key/props (room.roomId, `${message.roomId}-${index}`, keyword)
logic intact.
- Around line 45-58: Wrap ChatSearchResults in a local React Suspense boundary
so only the search area shows a loading state while useSuspenseQuery resolves:
where debouncedQuery is used to conditionally render the results, replace the
direct <ChatSearchResults keyword={debouncedQuery} /> with a Suspense fallback
(for example a simple div with "로딩 중...") that renders <ChatSearchResults
keyword={debouncedQuery} /> inside it; this keeps the existing ChatSearchResults
component and its useSuspenseQuery call unchanged while adding the local
Suspense wrapper around that render.
🪄 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: CHILL
Plan: Pro
Run ID: 6aa9a246-6807-4131-813b-0456d559ecf0
📒 Files selected for processing (5)
src/apis/chat/entity.tssrc/apis/chat/queries.tssrc/pages/Chat/ChatRoom.tsxsrc/pages/Chat/ChatSearch.tsxsrc/pages/Chat/hooks/useChat.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/pages/Chat/hooks/useChat.ts
- src/apis/chat/queries.ts
| useEffect(() => { | ||
| if (!targetMessageId) return; | ||
| const el = document.querySelector(`[data-message-id="${targetMessageId}"]`); | ||
| if (el) { | ||
| el.scrollIntoView({ block: 'center' }); | ||
| } | ||
| }, [chatMessages, targetMessageId]); |
There was a problem hiding this comment.
스크롤 effect가 반복 실행될 수 있습니다.
chatMessages가 변경될 때마다 effect가 실행되어, 새 메시지 도착 시 사용자가 다른 곳을 보고 있어도 다시 targetMessageId 위치로 스크롤될 수 있습니다. 최초 1회만 실행되도록 ref 플래그 사용을 고려해보세요.
🛠️ 제안
+ const hasScrolledToTarget = useRef(false);
+
useEffect(() => {
- if (!targetMessageId) return;
+ if (!targetMessageId || hasScrolledToTarget.current) return;
const el = document.querySelector(`[data-message-id="${targetMessageId}"]`);
if (el) {
el.scrollIntoView({ block: 'center' });
+ hasScrolledToTarget.current = true;
}
}, [chatMessages, targetMessageId]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/pages/Chat/ChatRoom.tsx` around lines 109 - 115, The effect scrolls to
targetMessageId every time chatMessages changes, causing unwanted repeated
scrolling; add a ref flag (e.g., hasScrolledToTargetRef) to ensure the scroll
happens only once per targetMessageId: create const hasScrolledToTargetRef =
useRef(false), in the scrolling useEffect (watching targetMessageId and
chatMessages) return early if hasScrolledToTargetRef.current is true, perform
scroll and then set hasScrolledToTargetRef.current = true, and add a separate
useEffect that resets hasScrolledToTargetRef.current = false whenever
targetMessageId changes so each new target only scrolls once.
✨ 요약
😎 해결한 이슈
Summary by CodeRabbit
릴리스 노트