Skip to content

Conversation

@layout-SY
Copy link
Collaborator

@layout-SY layout-SY commented Apr 30, 2025

구현내용

  • 신고하기 컴포넌트 구현
    image

  • 토스트 구현을 통한 실시간 알림 화면에 표시
    image

  • 헤더 알림 전체 조회 스타일 스크롤바 추가
    image

  • 빌드 에러 해결

연관이슈

close #236
close #253
close #254

Summary by CodeRabbit

  • 신규 기능

    • 신고 모달 및 신고 API 연동 기능이 추가되어 댓글, 대댓글, 사용자, 프로젝트 신고가 가능해졌습니다.
    • 실시간 알림(토스트) 시스템이 도입되어 새로운 알림 발생 시 화면 우측 하단에 토스트 알림이 표시됩니다.
    • 알림 벨 아이콘에 실시간 알림 도트 표시가 추가되었습니다.
  • UI/UX 개선

    • 알림, 댓글, 대댓글, 드롭다운, 마이페이지, 프로젝트 관련 스타일 및 레이아웃이 개선되었습니다.
    • 알림 목록이 스크롤 가능 영역으로 변경되고, 구분선 및 비어있음 메시지 스타일이 개선되었습니다.
    • 프로젝트, 댓글, 대댓글 등에서 태그 타입 일관성이 향상되었습니다.
  • 버그 수정

    • 닉네임 중복 확인 시 올바른 값이 전달되도록 수정되었습니다.
    • 콘솔 로그 등 불필요한 디버깅 코드가 제거되었습니다.
  • 기타

    • 내부 타입, 인터페이스, 컨텍스트 등 구조가 리팩토링되어 유지보수성이 향상되었습니다.

@layout-SY layout-SY added ⛏기능 기능 구현에 대한 라벨 🛠개선 성능 개선/리팩토링에 대한 라벨 👀버그 버그 labels Apr 30, 2025
@layout-SY layout-SY self-assigned this Apr 30, 2025
@coderabbitai
Copy link

coderabbitai bot commented Apr 30, 2025

"""

Walkthrough

이번 변경 사항은 실시간 알림 시스템과 신고 기능을 대폭 확장하는 데 초점을 맞췄습니다. 알림(EventSource) 수신 및 토스트(Toast) 알림 표시를 위한 커스텀 훅과 컨텍스트, UI 컴포넌트가 추가되었으며, 신고 모달 및 신고 API가 구현되었습니다. 댓글/대댓글에서 신고 및 삭제/수정 드롭다운이 개선되었고, 프로젝트 관련 타입들이 리팩토링되었습니다. 알림 벨 UI, 알림 목록, 토스트 UI, 신고 모달 등 여러 컴포넌트가 추가 및 수정되어 사용자 경험이 향상되었습니다. 전체 라우팅 구조에도 토스트 프로바이더가 전역적으로 적용되었습니다.

Changes

파일(들) 변경 요약
src/api/alarm.api.ts, src/api/report.api.ts 실시간 알림 테스트 및 신고 전송 API 함수 추가
src/api/projectLists.api.ts, src/api/reply.api.ts 불필요한 공백/디버깅 코드 제거
src/components/common/Toast/* 토스트 알림 컨텍스트, UI, 훅, 테스트 버튼 등 토스트 시스템 전체 신규 구현
src/context/ToastContext.tsx, src/hooks/useToast.ts 토스트 컨텍스트 및 커스텀 훅 신규 도입
src/components/common/header/Header.tsx, src/components/common/header/Header.styled.ts 알림 벨 UI 및 실시간 알림 수신 로직 추가, 문의 링크 수정
src/hooks/useNotification.ts 실시간 알림 수신, 토스트 연동, 쿼리 갱신 등 알림 훅 리팩토링
src/models/alarm.ts 알림 타입 속성명 및 실시간 알림 타입 추가
src/components/common/header/Notification/* 알림 목록 UI, 스크롤, 라인, 메시지 등 스타일 및 렌더링 개선
src/util/routeSelector.ts 알림 라우팅용 경로 선택 함수 신규 추가
src/components/comment/DropDownItem.tsx, src/components/common/dropDown/DropDownItem.tsx 신고 모달 연동, 드롭다운 옵션 개선, reportTitle prop 추가
src/components/reportComponent/ReportModal.styled.ts, src/components/reportComponent/ReportModal.tsx 신고 모달 UI 및 신고 제출 폼 신규 구현
src/constants/reportConstants.ts, src/models/report.ts 신고 사유 상수 및 신고 API 데이터 타입 추가
src/hooks/useModal.ts 신고 모달 전용 open/close 함수 추가
src/components/comment/commentComponent/CommentComponentLayout.tsx, src/components/comment/replyComponent/ReplyComponent.tsx 드롭다운 아이템 import 경로 및 reportTitle prop 전달
src/components/comment/CommentLayout.tsx, src/components/comment/CommentLayout.styled.ts 댓글 없을 때 메시지 처리 및 에러 메시지 스타일 적용
src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts, src/components/comment/replyComponent/ReplyComponent.styled.ts 댓글/답글 스타일 개선, 불필요한 메시지 스타일 제거
src/components/common/dropDown/DropDown.tsx, src/context/DropDownContext.tsx 드롭다운 닫기 컨텍스트 도입 및 적용
src/routes/AppRoutes.tsx 전체 라우트에 ToastProvider 전역 적용, 404 처리 개선
src/pages/main/*.tsx 스타일드 컴포넌트 import 경로 대소문자 정정
src/components/mypage/myProfile/MyProfile.styled.ts TitleWrapper 스타일 컴포넌트 추가
src/components/userPage/joinedProject/UserJoinProject.tsx 타이틀 스타일드 컴포넌트로 변경
src/components/userPage/userProfile/UserProfile.tsx 초보자 아이콘 렌더 조건 변경
src/pages/register/Register.tsx 닉네임 중복체크 변수명 오류 수정
src/components/projectFormComponents/projectInformationInput/* 프로젝트 포지션/스킬 타입 import 및 prop 타입 변경
src/hooks/ProjectHooks/useTagSelectors.ts 프로젝트 태그 타입 import 및 prop 타입 변경
src/models/projectDetail.ts, src/models/userProject.ts 프로젝트/유저 프로젝트 타입 리팩토링

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Header
    participant useNotification
    participant EventSource
    participant ToastProvider
    participant ToastContainer

    User->>Header: 페이지 진입
    Header->>useNotification: 알림 구독 시작
    useNotification->>EventSource: SSE 연결
    EventSource-->>useNotification: "alarm" 이벤트 수신
    useNotification->>ToastProvider: showToast(알림 데이터)
    ToastProvider->>ToastContainer: toasts 상태 업데이트
    ToastContainer-->>User: 토스트 알림 표시
Loading
sequenceDiagram
    participant User
    participant CommentDropDown
    participant ReportModal
    participant postReport API

    User->>CommentDropDown: "신고하기" 클릭
    CommentDropDown->>ReportModal: 모달 open
    User->>ReportModal: 사유 선택 및 내용 입력, 제출
    ReportModal->>postReport API: 신고 데이터 전송
    postReport API-->>ReportModal: 200 OK
    ReportModal-->>User: 신고 완료 안내, 모달 close
Loading

Possibly related PRs

Suggested reviewers

  • hyeongjun6364

Poem

🐇

알림이 땡땡, 토스트가 번쩍!
신고도 척척, 모달이 번쩍!
댓글과 답글, 드롭다운까지,
새로워진 UI, 토끼도 신나지!
실시간 소식에 귀 쫑긋 세우고,
오늘도 개발밭을 깡총깡총~ 🥕✨

"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51065f6 and aafb853.

📒 Files selected for processing (5)
  • src/components/comment/CommentLayout.tsx (1 hunks)
  • src/components/common/Toast/ToastItem.tsx (1 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1 hunks)
  • src/components/reportComponent/ReportModal.tsx (1 hunks)
  • src/util/timeAgo.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/util/timeAgo.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx
  • src/components/common/Toast/ToastItem.tsx
  • src/components/comment/CommentLayout.tsx
  • src/components/reportComponent/ReportModal.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: accessibility-test
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@layout-SY layout-SY changed the title 신고하기 구현 및 토스트 구현 신고하기 구현 및 토스트 구현 (issue #254, issue #253, issue #236) Apr 30, 2025
Copy link

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

🧹 Nitpick comments (19)
src/constants/reportConstants.ts (1)

1-11: 신고 이유 상수 정의가 잘 되었으나, 중복 항목이 있습니다.

신고 이유를 상수로 정의하고 as const를 사용하여 타입 안정성을 보장한 점은 좋습니다. 그러나 "광고/스팸"(4번 줄)과 "도배/스팸"(6번 줄)이 의미상 중복되어 보입니다. 사용자가 혼란스러울 수 있으니 두 항목을 합치거나 더 명확하게 구분하는 것이 좋을 것 같습니다.

export const reasons = [
  '욕설/비속어',
  '성적내용/음란물',
  '광고/스팸',
  '사기/부정행위',
-  '도배/스팸',
+  '도배글',
  '혐오/차별발언',
  '사생활 침해',
  '저작권 침해',
  '기타',
] as const;
src/components/mypage/joinedProject/MyJoinProjects.styled.ts (1)

18-18: 빈 스타일 컴포넌트가 추가되었습니다.

TitleWrapper라는 빈 styled div가 추가되었습니다. 다른 파일(MyProfile.styled.ts)에서도 동일한 패턴이 사용되고 있어 일관성을 유지하기 위한 목적으로 보입니다. 하지만 현재 스타일이 정의되어 있지 않아, 사용 목적에 맞는 스타일을 추가하거나 주석을 통해 용도를 명확히 하는 것이 좋을 것 같습니다.

-export const TitleWrapper = styled.div``;
+// 타이틀 요소를 감싸는 컨테이너로, 일관성을 위해 추가
+export const TitleWrapper = styled.div`
+  /* 필요한 경우 스타일 추가 */
+`;
src/components/comment/CommentLayout.styled.ts (1)

33-35: 테마 변수 사용을 고려해 보세요.

하드코딩된 색상 값 (#333) 대신 테마의 색상 변수를 사용하면 일관된 스타일 관리에 도움이 됩니다.

export const ErrorMessage = styled.p`
-  color: #333;
+  color: ${({ theme }) => theme.color.textPrimary || '#333'};
`;
src/models/report.ts (1)

1-6: 인터페이스 정의가 명확합니다만, 문서화를 추가하면 좋겠습니다.

신고 기능을 위한 인터페이스가 잘 정의되었습니다. 각 필드의 목적을 설명하는 JSDoc 주석을 추가하면 코드의 가독성과 유지보수성이 향상될 것입니다.

+/**
+ * 신고 API 요청을 위한 인터페이스
+ */
export interface ApiPostContent {
+  /** 신고 대상의 고유 ID */
  reportTargetId: number;
+  /** 신고 유형 (카테고리) */
  reportFilter: number;
+  /** 신고 사유 목록 */
  reason: string[];
+  /** 추가 상세 내용 */
  detail: string;
}
src/components/common/header/Header.styled.ts (1)

31-32: 빈 스타일 컴포넌트의 용도 명시

BellButton은 현재 스타일이 없는 빈 컴포넌트입니다. 주석이나 최소한의 스타일을 추가하여 용도를 명확히 하면 좋겠습니다.

-export const BellButton = styled.div``;
+export const BellButton = styled.div`
+  /* 알림 벨 버튼 컨테이너 */
+  position: relative;
+  cursor: pointer;
+`;
src/api/report.api.ts (1)

1-15: 신고 API 구현에 대한 몇 가지 개선 제안

신고 기능을 위한 API 함수 구현이 잘 되었습니다. 다만 몇 가지 개선할 점이 있습니다:

  1. 성공적인 응답으로 200 상태 코드만 확인하고 있는데, 신규 리소스 생성의 경우 201도 성공 상태에 포함될 수 있습니다.
  2. 에러 메시지가 상태 코드만 포함하고 있어 디버깅 시 충분한 정보를 제공하지 않습니다.
export const postReport = async (formData: ApiPostContent) => {
  try {
    const response = await httpClient.post(`/report`, formData);
-    if (response.status !== 200) {
-      throw new Error(`${response.status}`);
+    if (response.status !== 200 && response.status !== 201) {
+      throw new Error(`신고 제출 실패: 상태 코드 ${response.status}`);
    }
    return response.status;
  } catch (error) {
    console.error(error);
    throw error;
  }
};
src/components/common/Toast/ToastTestButton.tsx (1)

1-29: 토스트 테스트 버튼 컴포넌트가 잘 구현되었습니다!

토스트 알림 기능을 테스트할 수 있는 버튼 컴포넌트가 깔끔하게 작성되었습니다. 스타일드 컴포넌트를 사용해 디자인을 적용하고, useToast 훅을 통해 토스트 알림 기능을 호출하는 구조가 명확합니다.

한 가지 고려할 점은 이 컴포넌트가 개발 환경에서만 사용되는 테스트용인지, 아니면 프로덕션 환경에도 포함될 예정인지 명확히 하는 것이 좋을 것 같습니다. 개발 환경에서만 사용할 목적이라면 환경 변수를 체크하여 조건부 렌더링을 적용하는 것을 고려해 보세요.

const ToastTestButton = () => {
  const { showToast } = useToast();

  const handleClick = () => {
    showToast('🛎️ 이건 테스트 토스트 메시지입니다!', 4000);
  };

+  // 개발 환경에서만 렌더링 (필요한 경우)
+  if (import.meta.env.MODE !== 'development') return null;

  return <TestButton onClick={handleClick}>토스트 확인</TestButton>;
};
src/components/common/header/Header.tsx (1)

56-67: 알림 UI가 개선되었지만 접근성 개선이 필요합니다.

알림 메시지 유무에 따라 다른 UI를 보여주는 조건부 렌더링이 잘 구현되었습니다. 그러나 알림 표시(Dot)에 대한 스크린 리더 지원이 부족합니다. 알림이 있을 때 시각적으로만 표시할 것이 아니라, 스크린 리더 사용자에게도 알림 존재 여부를 전달해야 합니다.

<S.BellButton onClick={() => setSignalMessage('')}>
  <img src={bellLogined} alt='알림' />
-  {signalMessage && <S.Dot />}
+  {signalMessage && <S.Dot aria-label="읽지 않은 알림이 있습니다" />}
</S.BellButton>
src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1)

12-17: 알림 항목을 클릭 가능한 링크로 변환한 점이 좋습니다.

각 알림 항목을 클릭하면 해당 공지사항으로 이동하도록 개선되었습니다. 사용자 경험 측면에서 큰 향상입니다.

다만 방어적 코딩을 추가해보는 것을 고려해보세요:

-    <Link to={`${ROUTES.notice}/${item.routingId}`}>
+    <Link to={item.routingId ? `${ROUTES.notice}/${item.routingId}` : ROUTES.notice}>
       <S.Container>
-        <S.Message>{item.content}</S.Message>
-        <S.Time>{item.createdAt}</S.Time>
+        <S.Message>{item.content || '알림'}</S.Message>
+        <S.Time>{item.createdAt || ''}</S.Time>
       </S.Container>
     </Link>
src/components/common/Toast/ToastProvider.tsx (1)

5-30: 토스트 알림 시스템이 잘 구현되었습니다.

React Context API를 활용하여 전역적으로 사용 가능한 토스트 알림 시스템을 구현한 점이 인상적입니다. useCallback을 사용하여 성능 최적화를 고려하고, 최대 3개의 토스트만 표시되도록 관리하는 로직도 좋습니다.

몇 가지 개선 사항을 제안합니다:

  1. ID 생성 방식 개선:
- const id = Date.now().toString() + Math.random();
+ const id = `toast-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
  1. 상수값 분리:
+ const MAX_TOASTS = 3;
+ const DEFAULT_DURATION = 4000;

- const addToast = useCallback((content: string, duration = 4000) => {
+ const addToast = useCallback((content: string, duration = DEFAULT_DURATION) => {

  useEffect(() => {
-   if (toasts.length >= 4) {
+   if (toasts.length > MAX_TOASTS) {
src/components/common/Toast/ToastContainer.tsx (1)

11-24: React Portal을 활용한 구현이 적절합니다.

토스트 컨테이너를 React Portal을 사용하여 document.body에 렌더링하는 방식은 모달 및 토스트와 같은 오버레이 UI 요소에 매우 적합합니다. 컴포넌트 계층 구조와 상관없이 항상 최상위에 표시될 수 있습니다.

단, 극히 드문 경우지만 SSR 환경에서 document.body가 없을 수 있습니다. 조건부 렌더링을 추가하는 것을 고려해보세요:

- return createPortal(
+ return typeof document !== 'undefined' ? createPortal(
    <S.Container>
      {toasts.map((toast) => (
        <ToastItem
          key={toast.id}
          {...toast}
          onDone={() => onRemove(toast.id)}
        />
      ))}
    </S.Container>,
    document.body
-  );
+  ) : null;
src/components/common/Toast/ToastItem.tsx (1)

11-28: 토스트 아이템의 라이프사이클 관리가 잘 구현되었습니다.

타이머를 사용하여 토스트의 표시 시간을 관리하고, 애니메이션 종료 시 콜백을 호출하는 방식이 깔끔합니다. 또한 useEffect 내에서 타이머를 정리하여 메모리 누수를 방지하는 점도 좋습니다.

한 가지 주목할 점은 props에서 id를 받고 있지만 실제로 컴포넌트 내에서 사용하지 않습니다:

- const ToastItem = ({ content, duration, onDone }: ToastItemProps) => {
+ const ToastItem = ({ content, duration, onDone /* id는 사용되지 않음 */ }: ToastItemProps) => {

인터페이스에 정의된 모든 속성을 사용하지 않는다면 코드 가독성을 위해 간단한 주석을 추가하는 것도 좋은 방법입니다. 또는 ToastItemProps에서 id를 Partial로 만들거나, 필요한 경우에만 사용하도록 수정할 수 있습니다.

src/components/common/Toast/Toast.styled.ts (1)

23-34: 토스트 아이템 스타일링에 대한 제안

토스트 아이템 스타일링이 전반적으로 잘 되어 있습니다. 다만 두 가지 개선 사항을 제안합니다:

  1. 현재 white-space: nowrap으로 설정되어 있어 긴 메시지가 잘릴 수 있습니다. 여러 줄로 표시되도록 수정하는 것이 사용자 경험에 더 좋을 수 있습니다.
  2. 접근성을 위해 배경색과 텍스트 색상의 대비를 확인해보세요. 현재 반투명 배경에 흰색 텍스트는 WCAG 접근성 기준을 충족하는지 확인이 필요합니다.
export const Item = styled.div<{ $exiting: boolean }>`
  background-color: rgba(50, 50, 50, 0.9);
  color: ${({ theme }) => theme.color.white};
  padding: 12px 20px;
  border-radius: 8px;
  font-size: 0.95rem;
-  white-space: nowrap;
+  white-space: normal;
+  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  animation: ${fadeInUp} 0.3s ease-out,
    ${({ $exiting }) => $exiting && fadeOut} 0.3s ease-in forwards;
`;
src/components/common/dropDown/DropDownItem.tsx (1)

43-57: 컨테이너 구조와 드롭다운 아이템 구현이 좋습니다

Fragment로 감싸서 모달 컴포넌트를 함께 렌더링하는 구조가 적절합니다. 신고하기 아이템이 모든 사용자에게 표시되고, 수정하기/삭제하기는 작성자에게만 표시되도록 조건부 렌더링이 잘 구현되어 있습니다.

작은 이슈가 하나 있습니다:

            <S.Item onClick={() => onDelete(commentId, recommentId)}>
              삭제하기
-            </S.Item>{' '}
+            </S.Item>
          </>

불필요한 공백 문자({' '})가 있어 제거하는 것이 좋겠습니다.

src/components/common/header/Notification/Notification.styled.ts (1)

12-23: 스크롤 영역 구현이 잘 되었습니다

스크롤 기능 구현과 스크롤바 커스터마이징이 잘 되어 있습니다. 하지만 현재 WebKit 브라우저(Chrome, Safari)에만 적용되는 스크롤바 스타일링이 있습니다. Firefox 등 다른 브라우저에서도 일관된 경험을 제공하기 위한 스타일링을 고려해보세요.

export const ScrollArea = styled.div`
  height: 100%;
  overflow-y: auto;

  &::-webkit-scrollbar {
    width: 6px;
  }
  &::-webkit-scrollbar-thumb {
    border-radius: 3px;
    background-color: rgba(0, 0, 0, 0.2);
  }
+  /* Firefox 스크롤바 스타일링 */
+  scrollbar-width: thin;
+  scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
`;
src/components/reportComponent/ReportModal.tsx (2)

27-32: reportFilter 값이 하드코딩되어 있습니다.

reportFilter 값(type === 'comment' ? 3 : 4)이 하드코딩되어 있습니다. 이런 값은 상수로 정의하여 사용하는 것이 코드 가독성과 유지보수성을 높입니다.

+ // constants/reportConstants.ts에 추가
+ export const REPORT_FILTERS = {
+   COMMENT: 3,
+   OTHER: 4,
+ };

// ReportModal.tsx에서
const postData = {
  reportTargetId: targetId,
- reportFilter: type === 'comment' ? 3 : 4,
+ reportFilter: type === 'comment' ? REPORT_FILTERS.COMMENT : REPORT_FILTERS.OTHER,
  reason: selectedReasons,
  detail: textAreaRef.current?.value ? textAreaRef.current?.value : '',
};

33-33: 운영 환경에서 불필요한 로그가 출력됩니다.

console.log는 디버깅 용도로만 사용하고, 운영 환경에 배포하기 전에 제거하는 것이 좋습니다.

- console.log(postData);
src/components/reportComponent/ReportModal.styled.ts (2)

59-59: 사용하지 않는 스타일 컴포넌트가 있습니다.

ContentContainer는 정의되어 있지만 실제 컴포넌트에서 사용되지 않는 것으로 보입니다. 사용하지 않는 스타일 컴포넌트는 제거하는 것이 코드 유지보수성을 높입니다.

- export const ContentContainer = styled.div``;

72-83: 중복되는 스타일 컴포넌트가 있습니다.

CheckboxGridCheckRow가 거의 동일한 기능을 수행하는 것으로 보입니다. 둘 중 하나만 사용하거나, 차이점이 있다면 명확히 구분하는 것이 좋습니다.

export const CheckboxGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-bottom: 1.5rem;
`;

- export const CheckRow = styled.div`
-   display: grid;
-   grid-template-columns: repeat(3, 1fr);
-   gap: 1.5rem;
- `;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9e51d3 and 80f0aae.

📒 Files selected for processing (42)
  • src/App.tsx (2 hunks)
  • src/api/alarm.api.ts (1 hunks)
  • src/api/projectLists.api.ts (1 hunks)
  • src/api/reply.api.ts (0 hunks)
  • src/api/report.api.ts (1 hunks)
  • src/components/comment/CommentLayout.styled.ts (2 hunks)
  • src/components/comment/CommentLayout.tsx (1 hunks)
  • src/components/comment/commentComponent/CommentComponentLayout.tsx (2 hunks)
  • src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts (1 hunks)
  • src/components/comment/replyComponent/ReplyComponent.styled.ts (1 hunks)
  • src/components/comment/replyComponent/ReplyComponent.tsx (1 hunks)
  • src/components/common/Toast/Toast.styled.ts (1 hunks)
  • src/components/common/Toast/ToastContainer.tsx (1 hunks)
  • src/components/common/Toast/ToastContext.tsx (1 hunks)
  • src/components/common/Toast/ToastItem.tsx (1 hunks)
  • src/components/common/Toast/ToastProvider.tsx (1 hunks)
  • src/components/common/Toast/ToastTestButton.tsx (1 hunks)
  • src/components/common/dropDown/DropDownItem.tsx (4 hunks)
  • src/components/common/header/Header.styled.ts (2 hunks)
  • src/components/common/header/Header.tsx (3 hunks)
  • src/components/common/header/Notification/Notification.styled.ts (1 hunks)
  • src/components/common/header/Notification/Notification.tsx (2 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts (0 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1 hunks)
  • src/components/mypage/joinedProject/MyJoinProjects.styled.ts (1 hunks)
  • src/components/mypage/myProfile/MyProfile.styled.ts (1 hunks)
  • src/components/reportComponent/ReportModal.styled.ts (1 hunks)
  • src/components/reportComponent/ReportModal.tsx (1 hunks)
  • src/components/userPage/joinedProject/UserJoinProject.tsx (1 hunks)
  • src/components/userPage/userProfile/UserProfile.tsx (1 hunks)
  • src/constants/reportConstants.ts (1 hunks)
  • src/hooks/useModal.ts (1 hunks)
  • src/hooks/useNotification.ts (2 hunks)
  • src/hooks/useToast.ts (1 hunks)
  • src/models/alarm.ts (1 hunks)
  • src/models/projectDetail.ts (1 hunks)
  • src/models/report.ts (1 hunks)
  • src/pages/main/Create.tsx (1 hunks)
  • src/pages/main/Manage.tsx (1 hunks)
  • src/pages/main/Project.tsx (1 hunks)
  • src/pages/main/Result.tsx (1 hunks)
  • src/pages/register/Register.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • src/api/reply.api.ts
  • src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts
🧰 Additional context used
🧬 Code Graph Analysis (14)
src/components/mypage/myProfile/MyProfile.styled.ts (1)
src/components/mypage/joinedProject/MyJoinProjects.styled.ts (1)
  • TitleWrapper (18-18)
src/components/mypage/joinedProject/MyJoinProjects.styled.ts (1)
src/components/mypage/myProfile/MyProfile.styled.ts (1)
  • TitleWrapper (17-17)
src/App.tsx (1)
src/components/common/Toast/ToastProvider.tsx (1)
  • ToastProvider (5-30)
src/api/report.api.ts (2)
src/models/report.ts (1)
  • ApiPostContent (1-6)
src/api/http.api.ts (1)
  • httpClient (78-78)
src/hooks/useToast.ts (1)
src/components/common/Toast/ToastContext.tsx (1)
  • ToastContext (14-17)
src/components/common/header/Header.styled.ts (2)
src/components/common/header/Notification/Notification.styled.ts (1)
  • Message (25-27)
src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts (1)
  • Message (8-10)
src/components/comment/commentComponent/CommentComponentLayout.tsx (1)
src/models/comment.ts (1)
  • CommentType (13-15)
src/components/common/Toast/ToastTestButton.tsx (1)
src/hooks/useToast.ts (1)
  • useToast (4-7)
src/components/common/Toast/ToastProvider.tsx (1)
src/components/common/Toast/ToastContext.tsx (2)
  • ToastMessage (3-7)
  • ToastContext (14-17)
src/components/common/header/Notification/Notification.styled.ts (1)
src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts (1)
  • Message (8-10)
src/components/common/Toast/Toast.styled.ts (3)
src/components/common/header/Notification/Notification.styled.ts (1)
  • Container (3-10)
src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts (1)
  • Container (3-6)
src/components/common/header/Header.styled.ts (1)
  • Item (103-106)
src/components/common/dropDown/DropDownItem.tsx (1)
src/hooks/useModal.ts (1)
  • useModal (3-34)
src/hooks/useNotification.ts (2)
src/hooks/useToast.ts (1)
  • useToast (4-7)
src/hooks/queries/keys.ts (1)
  • AlarmList (30-32)
src/components/common/Toast/ToastContainer.tsx (1)
src/components/common/Toast/ToastContext.tsx (1)
  • ToastMessage (3-7)
🪛 Biome (1.9.4)
src/components/common/header/Notification/Notification.tsx

[error] 14-14: There is a suspicious semicolon in the JSX element.

This is usually the result of a typo or some refactor gone wrong.
Remove the semicolon, or move it inside a JSX element.

(lint/suspicious/noSuspiciousSemicolonInJsx)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: accessibility-test
🔇 Additional comments (46)
src/pages/main/Create.tsx (1)

1-1: import 경로 대소문자 일관성 유지
about.styledAbout.styled로 수정하여 실제 파일명과 일치시켰습니다.

src/pages/main/Result.tsx (1)

1-1: import 경로 대소문자 일관성 유지
about.styledAbout.styled로 수정하여 실제 파일명과 일치시켰습니다.

src/pages/main/Manage.tsx (1)

1-1: import 경로 대소문자 일관성 유지
about.styledAbout.styled로 수정하여 실제 파일명과 일치시켰습니다.

src/pages/main/Project.tsx (1)

3-3: import 경로 대소문자 일관성 유지
about.styledAbout.styled로 수정하여 실제 파일명과 일치시켰습니다.

src/api/projectLists.api.ts (1)

14-14: 불필요한 빈 줄 제거 변경이 잘 이루어졌습니다.

코드 가독성을 향상시키기 위한 코드 정리 작업이 잘 되었습니다.

src/hooks/useModal.ts (3)

17-19: 신고 모달 전용 오픈 함수 추가가 적절합니다.

메시지 상태를 변경하지 않고 모달만 열 수 있는 기능이 추가되어 신고 기능 구현에 유용하게 활용될 것 같습니다.


21-23: 신고 모달 전용 닫기 함수 추가가 적절합니다.

메시지 상태를 변경하지 않고 모달을 닫을 수 있는 기능이 추가되어 신고 기능 구현에 유용하게 활용될 것 같습니다.


31-32: 새로 추가된 함수들을 반환 객체에 포함시킨 점이 좋습니다.

새로 구현한 함수들을 반환 객체에 적절히 포함시켜 외부에서 사용할 수 있도록 했습니다.

src/components/mypage/myProfile/MyProfile.styled.ts (1)

17-17: 새로운 TitleWrapper 컴포넌트가 추가되었습니다.

styled.div 컴포넌트가 일관된 UI 패턴을 위해 추가되었네요. 다른 마이페이지 컴포넌트에서도 동일한 패턴을 사용하는 것으로 보입니다.

src/models/alarm.ts (1)

21-21: 네이밍 컨벤션이 개선되었습니다.

AlarmFilterId에서 alarmFilterId로 변경되어 자바스크립트/타입스크립트의 camelCase 네이밍 컨벤션을 더 잘 따르게 되었습니다. 이런 일관성 있는 네이밍은 코드 가독성과 유지보수성을 향상시킵니다.

src/components/userPage/userProfile/UserProfile.tsx (1)

23-23: 초보자 아이콘 표시 조건이 개선되었습니다.

이전에는 userData?.userLevel === 'Beginner'로 문자열을 비교했으나, 이제는 userData?.beginner === true로 boolean 값을 확인하도록 변경되었습니다. 이는 더 명확하고 타입 안전한 접근 방식입니다.

src/components/comment/replyComponent/ReplyComponent.tsx (1)

87-87: 신고 기능이 추가되었습니다.

DropDownItem 컴포넌트에 reportTitle prop이 추가되어 신고 모달에 필요한 정보를 전달합니다. 이는 전체 앱에 걸쳐 구현된 신고 기능의 일부로, 사용자가 부적절한 댓글을 신고할 수 있도록 합니다.

src/pages/register/Register.tsx (1)

276-276: 변수 참조 수정이 제대로 이루어졌습니다!

닉네임 중복 확인 버튼의 onClick 핸들러에서 올바른 상태 변수 nicknameText를 사용하도록 수정되었습니다. 이전에는 undefined 또는 잘못된 변수를 사용하고 있었을 것으로 보입니다. 이 수정으로 사용자가 입력한 닉네임 값이 제대로 중복 확인 함수에 전달됩니다.

src/components/comment/CommentLayout.tsx (3)

21-29: 댓글이 없는 경우 처리 로직이 개선되었습니다.

getCommentList가 없는 경우(빈 배열이거나 undefined)에 대한 명시적 처리가 추가되었습니다. 이제 사용자에게 "댓글 없음" 메시지가 스타일링된 컨테이너 안에 표시됩니다. 이 변경으로 사용자 경험이 향상되고 예외 상황이 더 명확하게 처리됩니다.


37-41: 오류 메시지 표시 방식이 개선되었습니다.

오류 상황에서 일반 텍스트 대신 <S.ErrorMessage> 스타일 컴포넌트를 사용하여 오류 메시지를 표시하도록 개선되었습니다. 이로써 오류 메시지가 시각적으로 더 일관되고 사용자 친화적으로 표시됩니다.


47-47: 코드가 간결해졌습니다.

이전 코드에서는 옵셔널 체이닝을 사용하여 getCommentList가 undefined인 경우를 처리했을 것입니다. 이제 21-29 라인에서 getCommentList가 falsy인 경우를 명시적으로 처리하고 있으므로, 여기서는 옵셔널 체이닝이 불필요해 제거되었습니다. 코드가 더 간결해지고 타입 안전성도 향상되었습니다.

src/components/comment/commentComponent/CommentComponentLayout.tsx (2)

13-14: 타입 정의가 명확해졌습니다.

getCommentList 속성의 타입이 CommentType[] | undefined에서 CommentType[]로 변경되었습니다. 이 변경은 CommentLayout.tsx에서 getCommentList가 falsy인 경우를 명시적으로 처리하는 방식과 일치합니다. 이제 이 컴포넌트에서는 항상 정의된 배열을 받게 되므로 타입 안전성이 향상되었습니다.


65-65: 신고 기능 통합이 적절히 이루어졌습니다.

DropDownItem 컴포넌트에 reportTitle 속성이 추가되어 현재 댓글의 내용(item.content)이 신고 모달의 제목으로 전달됩니다. 이는 PR 목적 중 하나인 신고 기능 구현과 일치하며, 댓글 컴포넌트와 신고 모달 사이의 통합이 적절히 이루어졌습니다.

src/hooks/useToast.ts (1)

1-7: useToast 훅 구현이 깔끔하고 직관적입니다.

Context API를 활용한 토스트 알림 시스템을 훅으로 잘 추상화했습니다. addToastremoveToast 함수를 showToasthideToast로 이름을 바꿔서 더 직관적인 API를 제공한 점이 좋습니다.

src/components/comment/CommentLayout.styled.ts (1)

3-5: Container에 padding-bottom 추가는 적절합니다.

댓글 컨테이너에 하단 여백을 추가하여 UI의 가독성이 향상되었습니다.

src/components/common/header/Header.styled.ts (1)

33-41: 알림 표시 스타일이 명확합니다.

새로운 알림이 있음을 표시하는 작은 빨간 원 스타일이 잘 구현되었습니다.

src/App.tsx (1)

7-7: 토스트 공급자가 성공적으로 통합되었습니다!

ToastProvider를 활용하여 애플리케이션 전체에 토스트 알림 시스템을 구현한 것은 좋은 접근 방식입니다. 이제 앱 전체에서 토스트 알림을 사용할 수 있게 되었습니다.

Also applies to: 24-26

src/components/common/header/Header.tsx (3)

19-21: 필요한 훅과 API 함수가 잘 가져와졌습니다.

실시간 알림 기능을 위한 useNotification 훅과 testLiveAlarm API 함수를 적절히 가져왔습니다.


28-28: 알림 상태 관리가 적절히 구현되었습니다.

useNotification 훅을 통해 signalMessage 상태와 이를 갱신하는 setSignalMessage 함수를 가져와 알림 상태를 관리하는 구조가 잘 구현되었습니다.


95-95: 경로 업데이트가 올바르게 적용되었습니다.

문의하기 링크의 경로가 ROUTES.support에서 ROUTES.inquiry로 적절히 변경되었습니다.

src/components/comment/replyComponent/ReplyComponent.styled.ts (1)

27-31: 댓글 스타일링 개선이 잘 이루어졌습니다.

댓글 컴포넌트의 텍스트 표시 방식이 개선되었습니다. 다음과 같은 변경사항이 적용되었습니다:

  • display: inline-block 속성으로 인라인 요소의 블록 특성 부여
  • max-width: calc(100% - 12px)로 컨테이너 내 최대 너비 설정
  • word-break: break-word로 긴 단어의 적절한 줄바꿈 처리
  • white-space: pre-wrap으로 공백과 줄바꿈 보존
  • 여백이 11px에서 12px로 약간 조정됨

이러한 변경으로 텍스트 레이아웃이 향상되고 긴 댓글이나 단어가 있을 때도 UI가 깨지지 않도록 개선되었습니다.

src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts (1)

24-28: 일관된 댓글 스타일링 적용이 좋습니다.

댓글 컴포넌트의 텍스트 표시 방식이 ReplyComponent와 동일하게 개선되었습니다:

  • display: inline-block 속성 추가
  • max-width: calc(100% - 12px) 설정
  • word-break: break-word 추가
  • white-space: pre-wrap 추가
  • 여백이 11px에서 12px로 조정

댓글과 답글 컴포넌트에 일관된 스타일을 적용함으로써 사용자 경험과 디자인 일관성이 향상되었습니다. 이렇게 관련 컴포넌트들에 동일한 스타일 변경을 적용하는 것은 좋은 접근 방식입니다.

src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1)

1-1: 필요한 의존성들이 적절하게 추가되었습니다.

Link 컴포넌트와 ROUTES 상수를 추가하여 알림 항목을 클릭 가능한 링크로 변환하는 설계가 잘 이루어졌습니다.

Also applies to: 4-4

src/components/common/Toast/ToastContainer.tsx (1)

6-9: 인터페이스 정의가 명확합니다.

props 인터페이스가 명확하게 정의되어 있어 코드의 가독성과 타입 안전성이 좋습니다.

src/components/common/Toast/ToastItem.tsx (1)

4-9: props 인터페이스가 잘 정의되어 있습니다.

ToastItem에 필요한 모든 속성들이 명확하게 정의되어 있습니다.

src/components/common/Toast/ToastContext.tsx (1)

1-17: React Context 패턴을 활용한 깔끔한 토스트 관리 구현!

토스트 알림을 관리하기 위한 Context가 잘 설계되었습니다. ToastMessage 인터페이스는 토스트의 기본 속성을 명확하게 정의하고 있으며, ToastContextProps는 토스트를 추가하고 제거하는 기능을 적절히 제공합니다.

다음 사항을 고려해보세요:

  • 각 인터페이스와 함수에 JSDoc 주석을 추가하면 API 문서화에 도움이 될 수 있습니다.
  • duration의 단위(ms인지 s인지)를 주석으로 명시하면 좋을 것 같습니다.
src/components/common/Toast/Toast.styled.ts (2)

3-10: 애니메이션 효과가 매끄럽게 구현되었습니다!

토스트 컴포넌트의 등장(fadeInUp)과 사라짐(fadeOut) 애니메이션이 자연스럽게 구현되었습니다. 애니메이션 타이밍과 트랜지션 효과가 적절히 설정되어 있습니다.


12-21: 토스트 컨테이너 위치 및 스타일링 적절합니다

고정 위치와 z-index를 사용하여 항상 다른 UI 요소 위에 표시되도록 구현한 점이 좋습니다. 컨테이너 간격(gap)도 적절히 설정되어 있습니다.

src/components/common/dropDown/DropDownItem.tsx (2)

3-4: 신고 기능 추가를 위한 의존성 및 props 설정이 잘 되었습니다

신고 모달을 위한 useModal 훅과 ReportModal 컴포넌트를 적절히 불러오고 있습니다. reportTitle prop을 통해 신고 대상 정보를 유연하게 받을 수 있도록 한 것도 좋은 설계입니다.

Also applies to: 14-14, 28-28, 32-32


58-65: ReportModal 구현이 적절합니다

조건부 렌더링을 통해 isOpen 상태일 때만 ReportModal을 표시하고, 필요한 props를 모두 전달하고 있어 좋습니다. 특히 targetId가 recommentId 또는 commentId로 동적으로 결정되는 부분과 type이 상황에 맞게 설정되는 부분이 잘 구현되었습니다.

src/components/common/header/Notification/Notification.styled.ts (1)

3-10: 알림 컨테이너 스타일 개선이 적절합니다

알림 컨테이너의 크기와 스타일링이 잘 조정되었습니다. 고정 높이와 overflow: hidden을 설정하여 스크롤 영역을 준비한 점이 좋습니다.

src/hooks/useNotification.ts (5)

2-6: 의존성 모듈 추가로 알림 관리 기능이 향상되었습니다.

useToast 훅과 useQueryClient를 사용하여 알림 메시지 표시 및 데이터 갱신 기능을 개선한 것은 좋은 접근입니다.


9-15: EventSource 관리 및 상태 설계가 개선되었습니다.

이전의 isSignal 대신 signalMessage를 사용하여 실제 알림 메시지를 저장하고, useRef를 사용하여 EventSource 인스턴스를 관리하는 방식으로 변경된 것은 리소스 관리 측면에서 효율적입니다.


17-70: EventSource 생명주기 관리 로직이 강화되었습니다.

userId가 있을 때만 EventSource를 생성하고, 컴포넌트 언마운트 시 정리하는 로직이 추가되어 메모리 누수를 방지합니다. 또한 React Query를 활용하여 알림 목록을 갱신하는 것은 좋은 접근법입니다.


72-77: 토스트 알림 표시 기능 통합이 잘 구현되었습니다.

signalMessage 상태가 변경될 때 토스트 알림을 표시하는 로직이 추가되어 사용자에게 실시간 알림을 제공하는 기능이 개선되었습니다.


78-78: 반환 객체가 개선되었습니다.

이제 signalMessagesetSignalMessage를 반환하여 컴포넌트에서 알림 메시지 상태를 더 유연하게 사용할 수 있습니다.

src/components/reportComponent/ReportModal.tsx (2)

7-12: 인터페이스 설계가 명확합니다.

props 인터페이스가 잘 정의되어 있어, 컴포넌트가 필요로 하는 데이터와 콜백을 명확하게 이해할 수 있습니다.


40-88: 모달 구현이 잘 되었습니다.

모달 컴포넌트의 UI와 상호작용이 잘 구현되어 있습니다. 특히 모달 외부 클릭 시 닫히는 기능과 내부 클릭 이벤트 전파 방지 기능이 적절히 구현되어 있습니다.

src/components/reportComponent/ReportModal.styled.ts (3)

3-15: 모달 컨테이너 스타일링이 적절합니다.

모달 컨테이너의 포지셔닝과 배경 효과가 잘 구현되어 있습니다. 특히 backdrop-filter를 사용하여 블러 효과를 추가한 것은 UX 측면에서 좋은 선택입니다.


17-25: 모달 박스 스타일링이 적절합니다.

모달 박스의 크기와 스타일링이 사용자 경험을 고려하여 잘 구현되어 있습니다. 특히 max-width: 90%를 사용하여 모바일 환경에서의 반응형 디자인을 고려한 것이 좋습니다.


114-131: 버튼 스타일 구현이 잘 되었습니다.

버튼의 variant 속성을 사용하여 다양한 스타일을 적용할 수 있도록 구현한 것은 재사용성 측면에서 좋은 접근입니다.

padding: 1rem;
`;

export const Message = styled.p``;
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Message 컴포넌트 중복 및 용도 검토 필요

Message 스타일 컴포넌트가 다른 파일(Notification.styled.ts, NotificationItem.styled.ts)에도 존재하며, 현재는 스타일이 정의되어 있지 않습니다. 중복 컴포넌트를 통합하거나 용도에 맞는 스타일을 추가하는 것이 좋겠습니다.


🏁 Script executed:

#!/bin/bash
# Message 스타일 컴포넌트 사용처 확인
echo "Header.styled.ts의 Message 컴포넌트 사용처 검색:"
rg -A 3 "import.*Message.*from '.*Header\.styled'" --type ts

echo -e "\n다른 Message 스타일 컴포넌트 정의 확인:"
rg "export const Message = styled" --type ts

Length of output: 782


Message 스타일 컴포넌트 중복 정의 확인 및 통합/커스터마이징 필요

중복으로 정의된 Message 스타일 컴포넌트가 아래 5개 파일에 존재하며, 모두 빈 styled.p\`` 형태로만 선언되어 있습니다. 공통 컴포넌트로 통합하거나, 각 용도에 맞게 스타일을 추가해 중복을 해소해 주세요.

  • src/components/common/header/Header.styled.ts
  • src/components/common/header/Notification/Notification.styled.ts
  • src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts
  • src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts
  • src/components/comment/replyComponent/ReplyComponent.styled.ts

Comment on lines 36 to 46
export const testLiveAlarm = async () => {
try {
const response = await httpClient.get<ApiAlarmList>(
`/user/send-alarm?alarmFilter=0`
);

return response;
} catch (e) {
console.log(e);
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

새 함수 testLiveAlarm의 목적을 명확히 해주세요

이 함수의 목적이 테스트인지 실제 기능인지가 명확하지 않습니다. 함수명이 "test"로 시작하는 것은 테스트 코드임을 암시하지만, 실제 기능으로 사용될 예정이라면 더 적절한 이름이 필요합니다.

또한 다른 함수들과 에러 처리 방식이 일관되지 않습니다:

  • getAlarmList는 에러를 다시 throw하지만 이 함수는 에러를 로깅만 합니다
  • 다른 함수들과 달리 에러 메시지가 없는 console.log를 사용합니다
export const testLiveAlarm = async () => {
  try {
    const response = await httpClient.get<ApiAlarmList>(
      `/user/send-alarm?alarmFilter=0`
    );

    return response;
  } catch (e) {
-    console.log(e);
+    console.error('라이브 알람 요청 에러:', e);
+    throw e;
  }
};
📝 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
export const testLiveAlarm = async () => {
try {
const response = await httpClient.get<ApiAlarmList>(
`/user/send-alarm?alarmFilter=0`
);
return response;
} catch (e) {
console.log(e);
}
};
export const testLiveAlarm = async () => {
try {
const response = await httpClient.get<ApiAlarmList>(
`/user/send-alarm?alarmFilter=0`
);
return response;
} catch (e) {
console.error('라이브 알람 요청 에러:', e);
throw e;
}
};

Comment on lines 24 to 31
<S.ScrollArea>
{AlarmData.map((item, index) => (
<React.Fragment key={index}>
<NotificationItem item={item} />
{index !== AlarmData.length - 1 && <S.Line />}
</React.Fragment>
))}
</S.ScrollArea>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

배열 인덱스를 key로 사용하는 것을 피하세요

React에서 목록을 렌더링할 때 인덱스를 key로 사용하는 것은 항목이 재정렬되거나 삭제될 경우 문제를 일으킬 수 있습니다. 가능하다면 item.id와 같은 고유 식별자를 key로 사용하는 것이 좋습니다.

또한 스크롤 영역 구현으로 알림 UI가 개선되었습니다. 이는 많은 알림이 있을 때 사용자 경험을 향상시킬 것입니다.

<S.ScrollArea>
  {AlarmData.map((item, index) => (
-    <React.Fragment key={index}>
+    <React.Fragment key={item.id || index}>
      <NotificationItem item={item} />
      {index !== AlarmData.length - 1 && <S.Line />}
    </React.Fragment>
  ))}
</S.ScrollArea>
📝 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
<S.ScrollArea>
{AlarmData.map((item, index) => (
<React.Fragment key={index}>
<NotificationItem item={item} />
{index !== AlarmData.length - 1 && <S.Line />}
</React.Fragment>
))}
</S.ScrollArea>
<S.ScrollArea>
{AlarmData.map((item, index) => (
<React.Fragment key={item.id || index}>
<NotificationItem item={item} />
{index !== AlarmData.length - 1 && <S.Line />}
</React.Fragment>
))}
</S.ScrollArea>

Comment on lines +30 to +32
useEffect(() => {
testLiveAlarm();
}, []);
Copy link

@coderabbitai coderabbitai bot Apr 30, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

API 호출에 에러 처리가 필요합니다.

컴포넌트 마운트 시 testLiveAlarm API를 호출하는 부분에 에러 처리 로직이 없습니다. API 호출 실패 시 사용자 경험에 영향을 줄 수 있으므로 try-catch 구문을 통한 에러 처리를 추가하는 것이 좋겠습니다.

useEffect(() => {
-  testLiveAlarm();
+  const initializeAlarm = async () => {
+    try {
+      await testLiveAlarm();
+    } catch (error) {
+      console.error('알림 초기화 실패:', error);
+    }
+  };
+  
+  initializeAlarm();
}, []);
📝 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
useEffect(() => {
testLiveAlarm();
}, []);
useEffect(() => {
const initializeAlarm = async () => {
try {
await testLiveAlarm();
} catch (error) {
console.error('알림 초기화 실패:', error);
}
};
initializeAlarm();
}, []);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

어차피 testLiveAlarm 자체가 Promise로 return 되니까 굳이 async/await을 쓰지 않아도 되는 거 아냐?

Copy link

Choose a reason for hiding this comment

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

@layout-SY 네, 맞습니다! testLiveAlarm이 이미 Promise를 반환하고 내부적으로 에러 처리를 한다면 async/await을 사용하지 않고 직접 호출해도 충분합니다. 제 이전 제안은 컴포넌트 레벨에서 명시적인 에러 처리를 추가하기 위한 것이었지만, 실제로는 다음과 같이 간단하게 작성할 수 있습니다:

useEffect(() => {
  testLiveAlarm();
}, []);

이렇게 하면 Promise는 비동기적으로 실행되고 내부적으로 에러 처리가 이루어집니다.


✏️ Learnings added
Learnt from: layout-SY
PR: devpalsPlus/frontend#260
File: src/components/common/header/Header.tsx:30-32
Timestamp: 2025-04-30T06:28:07.959Z
Learning: testLiveAlarm 함수는 이미 내부적으로 에러 처리를 하고 Promise를 반환하므로, 컴포넌트에서 호출할 때 async/await과 try-catch 구문 없이 직접 호출해도 충분하다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines 22 to 38
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const selectedReasons = formData.getAll('reason') as string[];

const postData = {
reportTargetId: targetId,
reportFilter: type === 'comment' ? 3 : 4,
reason: selectedReasons,
detail: textAreaRef.current?.value ? textAreaRef.current?.value : '',
};
console.log(postData);
postReport(postData);

alert('신고 되었습니다.');
onClose();
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

폼 제출 및 API 호출 로직에 개선이 필요합니다.

폼 제출 로직은 잘 구현되었으나, API 호출 후 에러 처리가 없습니다. 또한 alert 대신 앱에서 방금 구현한 토스트 시스템을 사용하는 것이 일관성 있는 UX를 제공할 것입니다.

다음과 같이 개선할 수 있습니다:

- postReport(postData);
- 
- alert('신고 되었습니다.');
- onClose();
+ try {
+   await postReport(postData);
+   showToast('신고가 접수되었습니다.', 3000);
+   onClose();
+ } catch (error) {
+   console.error('신고 제출 중 오류가 발생했습니다:', error);
+   showToast('신고 제출에 실패했습니다. 다시 시도해주세요.', 3000);
+ }

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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 (1)
src/components/reportComponent/ReportModal.tsx (1)

43-49: 🛠️ Refactor suggestion

토스트 알림 시스템을 사용하세요

alert() 대신 앱에서 구현한 토스트 시스템을 사용하면 더 일관된 사용자 경험을 제공할 수 있습니다. 또한 API 호출에 await을 사용하지 않고 있습니다.

-    try {
-      postReport(postData);
-      alert('신고 되었습니다.');
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    try {
+      await postReport(postData);
+      showToast('신고가 접수되었습니다.', 3000);
+      onClose();
+    } catch (error) {
+      console.error('신고 제출 중 오류가 발생했습니다:', error);
+      showToast('신고 제출에 실패했습니다. 다시 시도해주세요.', 3000);
+    }
🧹 Nitpick comments (2)
src/components/reportComponent/ReportModal.tsx (2)

89-93: textarea에 id와 label 추가가 필요합니다

textarea에 대한 레이블이 시각적으로만 존재하고 프로그래밍적으로 연결되어 있지 않습니다. 접근성 향상을 위해 id와 htmlFor 속성을 추가하세요.

-          <S.SectionTitle>상세 작성(생략 가능)</S.SectionTitle>
-          <S.TextArea
-            placeholder='신고 내용의 상세한 설명을 작성해 주세요!'
-            ref={textAreaRef}
-          />
+          <S.SectionTitle htmlFor="detail-textarea">상세 작성(생략 가능)</S.SectionTitle>
+          <S.TextArea
+            id="detail-textarea"
+            placeholder='신고 내용의 상세한 설명을 작성해 주세요!'
+            ref={textAreaRef}
+          />

22-22: isNotExist 상태 변수명 개선이 필요합니다

isNotExist라는 변수명은 무엇이 존재하지 않는지 명확하지 않습니다. isReasonMissing 또는 noReasonSelected와 같이 더 명확한 이름을 사용하세요.

-  const [isNotExist, setIsNotExist] = useState<boolean>(false);
+  const [noReasonSelected, setNoReasonSelected] = useState<boolean>(false);

그리고 이에 따라 다른 부분도 변경해야 합니다:

-      setIsNotExist(true);
-    } else {
-      setIsNotExist(false);
+      setNoReasonSelected(true);
+    } else {
+      setNoReasonSelected(false);
-            {isNotExist && (
+            {noReasonSelected && (
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80f0aae and ee8e78e.

📒 Files selected for processing (16)
  • src/api/alarm.api.ts (1 hunks)
  • src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts (1 hunks)
  • src/components/comment/replyComponent/ReplyComponent.styled.ts (1 hunks)
  • src/components/common/header/Header.styled.ts (1 hunks)
  • src/components/common/header/Notification/Notification.styled.ts (1 hunks)
  • src/components/common/header/Notification/Notification.tsx (2 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts (1 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1 hunks)
  • src/components/projectFormComponents/projectInformationInput/languageComponent/LanguageComponent.tsx (1 hunks)
  • src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx (1 hunks)
  • src/components/reportComponent/ReportModal.styled.ts (1 hunks)
  • src/components/reportComponent/ReportModal.tsx (1 hunks)
  • src/components/userPage/joinedProject/UserJoinProject.tsx (2 hunks)
  • src/hooks/ProjectHooks/useTagSelectors.ts (1 hunks)
  • src/models/projectDetail.ts (2 hunks)
  • src/models/userProject.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/components/projectFormComponents/projectInformationInput/languageComponent/LanguageComponent.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.styled.ts
  • src/components/userPage/joinedProject/UserJoinProject.tsx
  • src/components/common/header/Header.styled.ts
  • src/api/alarm.api.ts
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx
  • src/components/comment/commentComponent/commentComponent/CommentComponent.styled.ts
  • src/components/common/header/Notification/Notification.tsx
  • src/components/common/header/Notification/Notification.styled.ts
  • src/components/reportComponent/ReportModal.styled.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/models/projectDetail.ts (1)
src/models/tags.ts (3)
  • MethodTag (16-20)
  • PositionTag (10-14)
  • SkillTag (3-8)
src/components/reportComponent/ReportModal.tsx (3)
src/api/report.api.ts (1)
  • postReport (4-15)
src/components/reportComponent/ReportModal.styled.ts (1)
  • Avatar (41-43)
src/constants/reportConstants.ts (1)
  • reasons (1-11)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: accessibility-test
🔇 Additional comments (12)
src/models/userProject.ts (1)

30-30: 타입 업그레이드가 적절하게 이루어졌습니다.

ownProjects 속성의 타입을 AppliedProject[]에서 JoinedProject[]로 변경함으로써 사용자 프로젝트에 대한 더 많은 데이터(예: isBeginner, isDone, recruitmentEndDate 등)를 활용할 수 있게 되었습니다. 이는 UI에서 더 풍부한 정보 표시가 가능해집니다.

src/components/projectFormComponents/projectInformationInput/positionComponent/PositionComponent.tsx (1)

12-12: 타입 리팩토링이 일관되게 적용되었습니다.

Position[]에서 PositionTag[]로 타입을 변경한 것은 코드베이스 전체의 타입 표준화 작업의 일환으로 보입니다. 이는 models/tags.ts에서 정의된 중앙화된 태그 타입을 사용하도록 하는 좋은 변경입니다.

src/hooks/ProjectHooks/useTagSelectors.ts (1)

4-4: 타입 임포트 및 인터페이스 업데이트가 일관되게 적용되었습니다.

models/projectDetail에서 정의된 로컬 인터페이스 대신 models/tags에서 중앙화된 태그 타입을 사용하도록 변경하는 리팩토링이 잘 이루어졌습니다. 이 변경은 코드베이스 전체의 타입 일관성을 향상시키고 중복을 줄입니다.

Also applies to: 7-7

src/models/projectDetail.ts (1)

2-2: 모델 타입 중앙화 리팩토링이 잘 이루어졌습니다.

로컬에서 정의된 인터페이스 대신 ./tags에서 중앙화된 타입(MethodTag, PositionTag, SkillTag)을 가져와 사용하도록 변경한 점이 좋습니다. 이러한 리팩토링은:

  1. 코드베이스 전체에서 일관된 타입 정의를 보장합니다
  2. 타입 정의의 중복을 제거합니다
  3. 향후 타입 변경 시 한 곳에서만 수정하면 되므로 유지보수성이 향상됩니다

모든 필요한 타입이 적절히 임포트되고 ProjectDetailPlusExtended 인터페이스에 올바르게 적용되었습니다.

Also applies to: 33-35

src/components/reportComponent/ReportModal.tsx (6)

1-7: 잘 구성된 import 섹션

필요한 컴포넌트, 스타일, 훅, API 함수들을 명확하게 가져오고 있습니다.


8-14: 인터페이스 설계가 적절합니다

ReportModalProps 인터페이스가 필요한 모든 속성을 포함하고 있으며, 타입도 명확하게 정의되어 있습니다. reportTitle의 유니온 타입을 사용하여 유연성을 제공한 점이 좋습니다.


24-34: 폼 유효성 검사 구현이 적절합니다

신고 사유를 최소 하나 이상 선택하도록 유효성 검사를 구현한 것은 좋습니다. isNotExist 상태를 사용하여 에러 메시지를 표시하는 구현도 잘 되었습니다.


52-55: 모달의 기본 구조가 잘 구현되어 있습니다

모달 바깥을 클릭하면 닫히도록 하는 이벤트 처리와 모달 내부 클릭 시 이벤트 전파를 막는 처리가 잘 되어 있습니다.


57-69: 조건부 렌더링이 잘 구현되어 있습니다

reportTitle의 타입에 따라 다른 UI를 표시하는 조건부 렌더링이 잘 구현되어 있습니다.


95-112: Button 컴포넌트 사용이 일관적입니다

Button 컴포넌트의 속성들이 일관되게 사용되어 있으며, 취소/제출 버튼에 적절한 스타일과 타입이 적용되어 있습니다.

src/components/comment/replyComponent/ReplyComponent.styled.ts (2)

26-32: 텍스트 래핑 스타일 개선이 적절합니다

Comment 컴포넌트에 display: inline-block, max-width, word-break, white-space 속성을 추가하여 텍스트 래핑을 개선한 것은 좋은 변경입니다. 특히 긴 텍스트나 URL이 있을 때 UI가 깨지는 것을 방지할 수 있습니다.


31-31:

Details

❓ Verification inconclusive

margin-left 값 일관성 검토가 필요합니다

margin-left를 11px에서 12px로 변경했는데, 다른 관련 컴포넌트(NickName, ReplyContainer, Icon 등)의 margin-left는 여전히 11px입니다. 일관성을 위해 다른 요소의 margin 값도 검토해보세요.

다음 스크립트를 실행하여 관련 스타일드 컴포넌트 파일에서 margin-left 사용 패턴을 확인하세요:


🏁 Script executed:

#!/bin/bash
# 스타일드 컴포넌트 파일에서 margin-left 사용 패턴 검사
echo "컴포넌트별 margin-left 사용 현황:"
rg "margin-left: \d+px" --type ts src/components/comment/ -A 1 -B 1

Length of output: 4304


margin-left 값 일관성 검토 필요

src/components/comment 하위 styled 컴포넌트에서 margin-left 값이 컴포넌트별로 상이합니다:

  • CommentLayout.styled.ts: margin-left: 10px;
  • commentInput/CommentInput.styled.ts: margin-left: 5px;, 10px;
  • commentComponent/CommentComponent.styled.ts: margin-left: 11px;, 12px;, 15px;
  • replyComponent/ReplyComponent.styled.ts: margin-left: 11px;, 12px;, 10px;

최근 ReplyComponent 내 특정 요소를 12px로 변경하셨는데, 디자인 가이드에 따라 spacing scale(theme.spacing) 또는 px 값을 통일해서 적용할지 검토해 주세요.

Comment on lines 74 to 87
<S.CheckboxGrid>
{reasons.map((reason) => (
<S.CheckItem key={reason}>
<input type='checkbox' name='reason' value={reason} />
{reason}
</S.CheckItem>
))}

{isNotExist && (
<S.ErrorMessage>
신고 사유를 하나 이상 선택해주세요.
</S.ErrorMessage>
)}
</S.CheckboxGrid>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

체크박스 접근성 개선이 필요합니다

체크박스에 label 요소가 없어 접근성이 좋지 않습니다. 웹 접근성을 향상시키기 위해 label 태그를 추가하세요.

-              <S.CheckItem key={reason}>
-                <input type='checkbox' name='reason' value={reason} />
-                {reason}
-              </S.CheckItem>
+              <S.CheckItem key={reason}>
+                <input id={`reason-${reason}`} type='checkbox' name='reason' value={reason} />
+                <label htmlFor={`reason-${reason}`}>{reason}</label>
+              </S.CheckItem>
📝 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
<S.CheckboxGrid>
{reasons.map((reason) => (
<S.CheckItem key={reason}>
<input type='checkbox' name='reason' value={reason} />
{reason}
</S.CheckItem>
))}
{isNotExist && (
<S.ErrorMessage>
신고 사유를 하나 이상 선택해주세요.
</S.ErrorMessage>
)}
</S.CheckboxGrid>
<S.CheckboxGrid>
{reasons.map((reason) => (
<S.CheckItem key={reason}>
- <input type='checkbox' name='reason' value={reason} />
- {reason}
+ <input
+ id={`reason-${reason}`}
+ type='checkbox'
+ name='reason'
+ value={reason}
+ />
+ <label htmlFor={`reason-${reason}`}>{reason}</label>
</S.CheckItem>
))}
{isNotExist && (
<S.ErrorMessage>
신고 사유를 하나 이상 선택해주세요.
</S.ErrorMessage>
)}
</S.CheckboxGrid>

return '댓글을 불러오는 중 오류가 발생했습니다. 다시 시도해 주세요.';
return (
<S.ErrorMessage>
댓글을 불러오는 중 오류가 발생했습니다. 다시 시도해 주세요.
Copy link
Collaborator

Choose a reason for hiding this comment

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

QueryErroBoundary 를 통해서 에러나는 경우 감지하여 FallbackUI를 보여주게 되는데, ErrorMessage를 작성하신 이유가 궁금합니다!

Copy link
Collaborator Author

@layout-SY layout-SY Apr 30, 2025

Choose a reason for hiding this comment

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

생각을 못했네요. 제외 하겠습니다.

justify-content: center;
align-items: center;
z-index: 1000;
`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

ModalContainer 같은 경우, modal.styled에 있는 것과 유사하다고 생각이 드는데 기존에 있는 ModalContainer 재사용해도 좋을 것 같다는 생각이 듭니다..!
또한 ScrollProventer 이용해서 스크롤방지를 해도 좋을 것 같은데 어떠실까요?!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

기존 모달과 차이가 큰 컨테이너 자체만 같고, 나머지 구성 사항은 전부 새롭게 추가하고, 수정해야 할 거 같아서 그럴 바엔 새로 만드는 게 낫다고 생각했습니다.
ScrollProventer는 추가 하겠습니다.

Copy link

@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: 0

🧹 Nitpick comments (7)
src/components/comment/DropDownItem.tsx (2)

34-40: 확인 메시지에 오타가 있습니다.

삭제 로직은 잘 구현되어 있지만, 확인 메시지에 오타가 있습니다. "완성히"를 "완전히"로 수정하는 것이 좋겠습니다.

-      if (confirm('답글을 완성히 삭제할까요?')) removeReply(recommentId);
+      if (confirm('답글을 완전히 삭제할까요?')) removeReply(recommentId);
-      if (confirm('댓글을 완성히 삭제할까요?')) removeComment(commentId);
+      if (confirm('댓글을 완전히 삭제할까요?')) removeComment(commentId);

58-65: 신고 모달 연동이 효과적으로 이루어졌습니다.

모달 상태 관리와 신고 기능 구현이 잘 통합되어 있습니다. 다만, 에러 처리 로직이 보이지 않습니다. 신고 제출 시 발생할 수 있는 에러 상황에 대한 처리를 추가하는 것을 고려해보세요.

src/models/alarm.ts (1)

26-31:

Details

❓ Verification inconclusive

'createAt' 속성 이름 검토 필요

AlarmLive 인터페이스의 createAt 속성은 기존 Alarm 인터페이스의 createdAt과 이름이 일관되지 않습니다. 이는 백엔드 API 응답과 일치하는지 확인하고, 가능하면 일관된 이름으로 통일하는 것이 좋습니다.

 export interface AlarmLive {
   alarmFilterId: number;
-  createAt: string;
+  createdAt: string;
   message: string;
   routingId: number;
 }

🏁 Script executed:

#!/bin/bash
# 백엔드 API 응답에서 날짜 필드명 확인
rg -A 10 -B 10 "class.*Controller.*Alarm" --type java
rg "createAt|createdAt" --type java

Length of output: 391


추가로 프론트엔드 모델 정의에서 Alarm 인터페이스와 createdAt/createAt 필드가 어떻게 사용되고 있는지 확인해보겠습니다.


🏁 Script executed:

#!/bin/bash
# Alarm 인터페이스 및 AlarmLive, createdAt/createAt 속성 검색
rg -n "export interface Alarm" --type ts
rg -n "export interface AlarmLive" --type ts
rg -n "createAt" --type ts
rg -n "createdAt" --type ts

Length of output: 1522


AlarmLive.createAt → createdAt로 필드명 일관화 검토 필요

AlarmLive 인터페이스의 createAt 필드가 다른 모델의 createdAt과 일치하지 않습니다. 백엔드 API 응답 스펙이 어떤 이름을 사용하는지 확인한 뒤, 일관된 필드명으로 통일해주세요.

주의할 점: 이름을 변경하면 아래 사용처도 함께 수정해야 합니다.

  • src/models/alarm.ts (AlarmLive 인터페이스)
  • src/components/common/Toast/ToastItem.tsx (content.createAt → content.createdAt)
 export interface AlarmLive {
   alarmFilterId: number;
-  createAt: string;
+  createdAt: string;
   message: string;
   routingId: number;
 }
src/components/common/Toast/ToastItem.tsx (2)

7-12: Props 인터페이스에 사용되지 않는 속성이 있습니다.

ToastItemProps 인터페이스에 id 속성이 정의되어 있지만 컴포넌트 내에서 사용되지 않습니다. 사용되지 않는 속성은 제거하거나 주석으로 그 목적을 명시하는 것이 좋습니다.

 interface ToastItemProps {
-  id: string;
   content: AlarmLive;
   duration: number;
   onRemove: () => void;
 }

14-35: 토스트 아이템 구현이 잘 되었습니다.

토스트 아이템 컴포넌트가 잘 구현되었습니다. 몇 가지 개선점을 제안합니다:

  1. content.createAt을 사용하는 부분에서 날짜 포맷팅이 되어 있지 않습니다. 사용자 친화적인 형식으로 표시하는 것이 좋습니다.
  2. 링크를 클릭할 때 토스트가 즉시 사라지도록 하는 로직이 없습니다. 사용자가 토스트를 클릭하면 즉시 사라지는 것이 UX 측면에서 더 좋습니다.
 return (
-  <Link to={route}>
+  <Link to={route} onClick={onRemove}>
     <S.Item $exiting={exiting} onAnimationEnd={handleAnimationEnd}>
       <S.LiveMessage>{content.message}</S.LiveMessage>
-      <S.LiveDate>{content.createAt}</S.LiveDate>
+      <S.LiveDate>{new Date(content.createAt).toLocaleString()}</S.LiveDate>
     </S.Item>
   </Link>
 );
src/hooks/useNotification.ts (2)

47-48: 콘솔 로그 제거 필요

프로덕션 코드에 불필요한 콘솔 로그가 남아있습니다. 디버깅을 위한 임시 코드로 보이니 제거해 주세요.

 try {
-  console.log(JSON.parse(event.data));
   const eventData: AlarmLive = JSON.parse(event.data);

9-79: 이벤트 소스 생명주기 관리 개선이 잘 되었습니다.

useNotification 훅의 개선 사항:

  1. useRef를 사용한 이벤트 소스 인스턴스 관리
  2. 컴포넌트 언마운트 시 이벤트 소스 정리
  3. userId에 따른 조건부 이벤트 소스 생성
  4. React Query와의 통합으로 알림 데이터 갱신
  5. 토스트 시스템과의 통합

이 모든 변경사항은 코드의 품질과 사용자 경험을 크게 향상시킵니다.

하나 개선할 수 있는 부분:

  • 에러 처리 로직에서 단순히 콘솔에 로깅하는 것 외에도 사용자에게 오류를 알리거나 재시도 메커니즘을 구현하는 것을 고려해 보세요.
 eventSource.onerror = (e) => {
   console.log(e);
+  // 일정 시간 후 재연결 시도 또는 사용자에게 알림
+  setTimeout(() => {
+    // 연결 재시도 로직
+  }, 5000);
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee8e78e and 51065f6.

📒 Files selected for processing (20)
  • src/App.tsx (0 hunks)
  • src/components/comment/DropDownItem.tsx (1 hunks)
  • src/components/comment/commentComponent/CommentComponentLayout.tsx (3 hunks)
  • src/components/comment/replyComponent/ReplyComponent.tsx (2 hunks)
  • src/components/common/Toast/Toast.styled.ts (1 hunks)
  • src/components/common/Toast/ToastContainer.tsx (1 hunks)
  • src/components/common/Toast/ToastItem.tsx (1 hunks)
  • src/components/common/Toast/ToastProvider.tsx (1 hunks)
  • src/components/common/dropDown/DropDown.tsx (2 hunks)
  • src/components/common/header/Header.tsx (3 hunks)
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx (1 hunks)
  • src/components/reportComponent/ReportModal.styled.ts (1 hunks)
  • src/components/reportComponent/ReportModal.tsx (1 hunks)
  • src/context/DropDownContext.tsx (1 hunks)
  • src/context/ToastContext.tsx (1 hunks)
  • src/hooks/useNotification.ts (2 hunks)
  • src/hooks/useToast.ts (1 hunks)
  • src/models/alarm.ts (1 hunks)
  • src/routes/AppRoutes.tsx (3 hunks)
  • src/util/routeSelector.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/App.tsx
✅ Files skipped from review due to trivial changes (3)
  • src/context/DropDownContext.tsx
  • src/context/ToastContext.tsx
  • src/util/routeSelector.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/components/comment/replyComponent/ReplyComponent.tsx
  • src/components/common/header/Header.tsx
  • src/components/comment/commentComponent/CommentComponentLayout.tsx
  • src/hooks/useToast.ts
  • src/components/common/Toast/ToastContainer.tsx
  • src/components/common/header/Notification/NotificationItem/NotificationItem.tsx
  • src/components/common/Toast/ToastProvider.tsx
  • src/components/common/Toast/Toast.styled.ts
  • src/components/reportComponent/ReportModal.tsx
  • src/components/reportComponent/ReportModal.styled.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/components/common/dropDown/DropDown.tsx (1)
src/context/DropDownContext.tsx (1)
  • DropDownContext (3-5)
src/routes/AppRoutes.tsx (1)
src/components/common/Toast/ToastProvider.tsx (1)
  • ToastProvider (6-31)
src/components/comment/DropDownItem.tsx (1)
src/hooks/useModal.ts (1)
  • useModal (3-34)
src/hooks/useNotification.ts (3)
src/models/alarm.ts (1)
  • AlarmLive (26-31)
src/hooks/useToast.ts (1)
  • useToast (4-7)
src/hooks/queries/keys.ts (1)
  • AlarmList (30-32)
src/components/common/Toast/ToastItem.tsx (2)
src/models/alarm.ts (1)
  • AlarmLive (26-31)
src/util/routeSelector.ts (1)
  • routeSelector (1-12)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: accessibility-test
🔇 Additional comments (6)
src/components/common/dropDown/DropDown.tsx (2)

4-4: 컨텍스트 API를 활용한 개선!

DropDownContext를 가져오는 import가 추가되었습니다. 컨텍스트를 사용하면 prop drilling 없이 깊게 중첩된 컴포넌트 간에 데이터를 효과적으로 공유할 수 있어 좋은 접근 방식입니다.


25-36: 컨텍스트 프로바이더 통합이 잘 이루어졌습니다.

DropDownContext.Provider로 컴포넌트를 감싸서 자식 컴포넌트가 close 메소드에 접근할 수 있도록 한 접근이 좋습니다. 이를 통해 NotificationItem 같은 컴포넌트에서 알림 링크를 클릭할 때 드롭다운을 닫는 기능을 구현할 수 있게 되었습니다.

src/components/comment/DropDownItem.tsx (2)

7-17: 인터페이스 속성이 명확히 정의되어 있습니다.

DropdownProps 인터페이스가 잘 설계되어 있어 컴포넌트의 용도를 명확히 이해할 수 있습니다. 다만, 선택적 속성에 대한 타입 안전성을 조금 더 강화하면 좋을 것 같습니다.


47-56: 권한 체크가 잘 구현되어 있습니다.

로그인한 사용자와 댓글 작성자가 동일할 때만 수정 및 삭제 옵션을 보여주는 조건부 렌더링이 잘 구현되어 있습니다. 이는 사용자 권한 관리에 대한 좋은 접근 방식입니다.

src/models/alarm.ts (1)

21-21: 속성 이름 변경이 일관성 있게 적용되었습니다.

PascalCase에서 camelCase로의 변경이 TypeScript 코딩 규칙에 맞게 잘 수정되었습니다.

src/routes/AppRoutes.tsx (1)

315-324: 전역 토스트 시스템 구현이 잘 되었습니다.

모든 라우트를 ToastProvider로 감싸는 방식으로 전역 토스트 알림 시스템을 구현한 것이 좋습니다. 이렇게 하면 어플리케이션의 어느 부분에서든 토스트 알림을 표시할 수 있습니다. 또한 404 페이지를 위한 catch-all 라우트를 추가한 것도 적절합니다.

@layout-SY layout-SY requested a review from hyeongjun6364 April 30, 2025 09:50
@layout-SY layout-SY merged commit 1e52e19 into develop Apr 30, 2025
4 checks passed
@layout-SY layout-SY deleted the feat/#253 branch April 30, 2025 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⛏기능 기능 구현에 대한 라벨 👀버그 버그 🛠개선 성능 개선/리팩토링에 대한 라벨

Projects

None yet

Development

Successfully merging this pull request may close these issues.

유저 신고 컴포넌트 구현 실시간 알림을 위한 토스트 구현 헤더에 추가적인 기능 구현

4 participants