Skip to content

feat:채팅방 검색 및 추가 기능 구현#274

Merged
ParkSungju01 merged 7 commits intodevelopfrom
266-feat-채팅방검색-및-추가-기능추가
Apr 16, 2026

Hidden character warning

The head ref may contain hidden characters: "266-feat-\ucc44\ud305\ubc29\uac80\uc0c9-\ubc0f-\ucd94\uac00-\uae30\ub2a5\ucd94\uac00"
Merged

feat:채팅방 검색 및 추가 기능 구현#274
ParkSungju01 merged 7 commits intodevelopfrom
266-feat-채팅방검색-및-추가-기능추가

Conversation

@ParkSungju01
Copy link
Copy Markdown
Contributor

@ParkSungju01 ParkSungju01 commented Apr 12, 2026

✨ 요약

- 채팅방 검색 페이지, 채팅방 추가 페이지 구현
- 채팅이나 채팅방 이름을 통해 채팅방 검색 구현
- 채팅으로 검색  채팅방 이동  해당 채팅 위치로 이동
- 그룹 채팅 생성 기능 추가
- 그룹 채팅 생성  초대할 인원을 동아리 또는 이름 별로 정렬 기능 추가



😎 해결한 이슈




image
image
image

Summary by CodeRabbit

릴리스 노트

  • 새 기능
    • 채팅탭에 채팅방 검색 페이지와 채팅방 추가(초대) 페이지를 추가하여 경로 내에서 이동 가능
    • 키워드 기반 채팅·메시지 검색 및 검색 결과 내 키워드 강조 표시
    • 여러 사용자 선택으로 그룹 채팅 생성 및 초대 대상 조회(클럽별/이름별 정렬)
    • 검색·초대 화면용 헤더 및 UI 개선 (타이틀·레이아웃 반영)

@ParkSungju01 ParkSungju01 linked an issue Apr 12, 2026 that may be closed by this pull request
4 tasks
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

채팅 검색 및 채팅방 추가 기능을 추가했습니다. 보호된 라우트 아래에 /chats/search/chats/add를 lazy 등록하고, 검색/초대 관련 API 타입·엔드포인트(getSearchChat, getInvitableFriends, postChatRoomsGroup)를 도입했습니다. 채팅 검색/추가 페이지와 헤더 컴포넌트(ChatSearchHeader, ChatAddHeader), 채팅방 리스트 아이템 분리(ChatRoomListItem), 쿼리/뮤테이션 키·훅 확장(useCreateChatRoomGroupMutation, useChat 메시지Id 지원) 및 관련 유틸(formatTime)도 추가했습니다.

Possibly related PRs

  • PR #213: src/App.tsx의 채팅 관련 라우팅 구조를 변경한 PR로, /chats 하위 라우트 등록이 겹칠 가능성이 있어 직접적인 코드 레벨 연관이 있습니다.
  • PR #137: src/pages/Chat/ChatRoom.tsx를 수정한 PR로, 동일 파일의 메시지 렌더링/동작(예: 스크롤·레이아웃)과 충돌하거나 상호작용할 가능성이 높습니다.
  • PR #252: 헤더 시스템(HEADER_CONFIGS, HEADER_RENDERERS, HeaderType 등)을 수정한 PR로, 본 PR의 헤더 타입/구성 추가와 직접적으로 관련됩니다.

Suggested reviewers

  • ff1451
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항(채팅방 검색 및 추가 기능)을 명확하게 요약하고 있으며, 변경 범위와 의도가 명확합니다.
Linked Issues check ✅ Passed PR이 이슈 #266의 모든 요구 사항을 충족합니다: 채팅방 검색 화면, 검색 기능, 채팅방 생성 화면, 그룹 채팅 생성 기능이 모두 구현되어 있습니다.
Out of Scope Changes check ✅ Passed 변경 사항이 모두 이슈 #266의 요구 범위 내에 있으며, 불필요한 리팩토링이나 범위 외 기능이 없습니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 266-feat-채팅방검색-및-추가-기능추가

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

API 모듈 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 패턴 적용을 확인해주세요.

신규 페이지는 LayoutshowBottomNav, 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.css and Use 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 utilities and Reference 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6576748 and 2c37aac.

⛔ Files ignored due to path filters (1)
  • src/assets/svg/check_color.svg is excluded by !**/*.svg, !src/assets/** and included by **
📒 Files selected for processing (13)
  • src/App.tsx
  • src/apis/chat/entity.ts
  • src/apis/chat/index.ts
  • src/apis/chat/mutations.ts
  • src/apis/chat/queries.ts
  • src/components/layout/Header/components/ChatAddHeader.tsx
  • src/components/layout/Header/components/ChatListHeader.tsx
  • src/components/layout/Header/components/ChatSearchHeader.tsx
  • src/pages/Chat/AddChatRoom.tsx
  • src/pages/Chat/ChatSearch.tsx
  • src/pages/Chat/hooks/useChat.ts
  • src/pages/Chat/hooks/useChatMutations.ts
  • src/pages/Chat/index.tsx

Comment thread src/apis/chat/entity.ts Outdated
Comment thread src/apis/chat/queries.ts Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment on lines +24 to +25
<div className="flex py-4" onClick={() => onToggle(user.userId)}>
<img src={user.imageUrl} alt="프로필" className="size-10 rounded-xl" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

선택 행을 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.

Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/ChatSearch.tsx Outdated
Comment thread src/pages/Chat/index.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2c37aac and b165437.

📒 Files selected for processing (6)
  • src/apis/chat/entity.ts
  • src/apis/chat/queries.ts
  • src/components/layout/Header/components/ChatRoomMoreHeader.tsx
  • src/pages/Chat/AddChatRoom.tsx
  • src/pages/Chat/ChatRoomMore.tsx
  • src/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

Comment thread src/components/layout/Header/components/ChatRoomMoreHeader.tsx Outdated
Comment thread src/components/layout/Header/components/ChatRoomMoreHeader.tsx Outdated
Comment thread src/components/layout/Header/components/ChatRoomMoreHeader.tsx Outdated
Comment on lines +17 to +30
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

스위치 접근성 속성을 추가해주세요.

현재 버튼은 시각적으로만 토글이고, 스크린리더에서 상태를 알기 어렵습니다(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.

Suggested change
<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.

Comment thread src/pages/Chat/ChatRoomMore.tsx Outdated
Comment on lines +7 to +14
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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat src/pages/Chat/ChatRoomMore.tsx

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 2813


🏁 Script executed:

find . -name "*.ts" -o -name "*.tsx" | xargs grep -l "const useChat" | head -5

Repository: 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.ts

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 2370


🏁 Script executed:

cat src/styles/theme.css | head -100

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 216


🏁 Script executed:

rg "FF4E4E|error|danger|red" src/styles/theme.css

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 50


🏁 Script executed:

wc -l src/styles/theme.css

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 90


🏁 Script executed:

cat src/styles/theme.css | tail -200

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 216


🏁 Script executed:

rg "red|danger|error|alert" src/styles/ -t css -i

Repository: 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 -10

Repository: 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 -20

Repository: 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.tsx

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 2039


🏁 Script executed:

head -50 src/pages/Chat/index.tsx

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 1799


🏁 Script executed:

cat src/apis/chat/queries.ts

Repository: 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.tsx

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 3087


🏁 Script executed:

grep -r "Layout" src/pages/Chat/*.tsx | head -5

Repository: BCSDLab/KONECT_FRONT_END

Length of output: 50


🏁 Script executed:

grep -r "showBottomNav\|contentClassName" src/pages/ | head -10

Repository: 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.

Comment thread src/pages/Chat/ChatRoomMore.tsx Outdated
Comment on lines +20 to +37
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 });
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

채팅방 나가기 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).

Comment thread src/pages/Chat/ChatRoomMore.tsx Outdated
Comment on lines +40 to +72
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>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

페이지 루트를 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.

Comment thread src/pages/Chat/ChatRoomMore.tsx Outdated
Copy link
Copy Markdown
Collaborator

@ff1451 ff1451 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰량이 좀 많은데 천천히 읽어보면서 확인 부탁드려요

디자인과 다른 부분도 많이 보여서 피그마와 한번 대조해보시는것도 좋을 것 같아요

Comment thread src/components/layout/Header/components/ChatAddHeader.tsx Outdated
Comment thread src/components/layout/Header/components/ChatAddHeader.tsx
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어디에 사용되는 헤더인가요?

Comment thread src/components/layout/Header/components/ChatSearchHeader.tsx
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx
Comment thread src/pages/Chat/AddChatRoom.tsx
Comment thread src/pages/Chat/ChatSearch.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 from src/styles/theme.css Tailwind @theme configuration".

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between b165437 and 77b1cf4.

📒 Files selected for processing (12)
  • src/components/common/Dropdown.tsx
  • src/components/layout/Header/components/ChatAddHeader.tsx
  • src/components/layout/Header/components/SubpageHeader.tsx
  • src/components/layout/Header/headerConfig.ts
  • src/components/layout/Header/index.tsx
  • src/components/layout/Header/routeTitles.ts
  • src/components/layout/Header/types.ts
  • src/pages/Chat/AddChatRoom.tsx
  • src/pages/Chat/ChatSearch.tsx
  • src/pages/Chat/components/ChatRoomListItem.tsx
  • src/pages/Chat/index.tsx
  • src/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

Comment thread src/components/layout/Header/components/SubpageHeader.tsx Outdated
Comment thread src/pages/Chat/AddChatRoom.tsx
@ParkSungju01 ParkSungju01 requested a review from ff1451 April 13, 2026 16:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 36efb1f and 2a4e983.

📒 Files selected for processing (5)
  • src/apis/chat/entity.ts
  • src/apis/chat/queries.ts
  • src/pages/Chat/ChatRoom.tsx
  • src/pages/Chat/ChatSearch.tsx
  • src/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

Comment on lines +109 to +115
useEffect(() => {
if (!targetMessageId) return;
const el = document.querySelector(`[data-message-id="${targetMessageId}"]`);
if (el) {
el.scrollIntoView({ block: 'center' });
}
}, [chatMessages, targetMessageId]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

스크롤 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.

Copy link
Copy Markdown
Collaborator

@ff1451 ff1451 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다!

@ParkSungju01 ParkSungju01 merged commit f7ddb5c into develop Apr 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 채팅방 검색 및 추가 기능 추가

2 participants