Skip to content

Conversation

@HopeFullee
Copy link
Member

@HopeFullee HopeFullee commented Dec 14, 2025

📝 변경 사항

[작업]

  • 모임상세 페이지 정보 바인딩

[수정]

  • 참여자 목록 기존 flex-wrap 날먹에서 grid 튜닝으로 디자인에 맞게 수정.

민서님이? 만든 formatDateTime.ts 유틸 함수 수정함 (나도 같이 쓸거라 손대버림)

  • formatDateTime() 수정 사항

  • formatTimeAgo() 수정 사항

    • Math.ceil -> Math.floor로 변경
      • 얼마 전 (time-ago)는 이미 지난 시간만 말해야 함
      • ceil 적용 시 -> 아직 안 지난 시간 포함 ❌
      • floor 적용 시 -> 확정된 시간만 표현 ✅
    • diffPerDay = Math.floor(diffPerHour / 30) -> Math.floor(diffPerHour / 24)로 변경
      5252.. 오마에.. 마지카요.. 하루는 24시간 이라구 wwwww
    • const dateInput = new Date(isoString) 에서 -> new Date(isoString.endsWith('Z') ? isoString : ${isoString}Z) 변경
      • 모임생성으로 서버에 보내는 date는 접미어 "Z" (zulu time)이 포함된 UTC 임
        하지만, 서버에서 받는 createdAt는 "Z"가 빠진 상태로 오기 때문에 Locale 기반으로 시간을 잡아버림.
        이러한 이유로 모임 생성 1분 전이라 할 지라도 UTC 타임과 Locale 타임의 차이로 수시간의 오차가 생김.
        • [솔루션]
          • 서버에서 "Z"를 포함한 createdAt로 달라고 요청하기.
          • 그냥 클라단에서 수동으로 Zulu Time 넣기 (위에 보셈)

[wip]

  • 상황별 버튼 작업 해야함 (참여자, 생성자 등등)

🔗 관련 이슈

Closes #


🧪 테스트 방법

  • 수동 테스트 검증(로컬 환경)
  • 유닛 테스트 검증
  • 통합 테스트 검증

📸 스크린샷 (선택)


📋 체크리스트

  • 관련 문서를 업데이트했습니다 (필요한 경우)
  • 테스트를 추가/수정했습니다 (필요한 경우)
  • Breaking change가 있다면 명시했습니다

💬 추가 코멘트


CodeRabbit Review는 자동으로 실행되지 않습니다.

Review를 실행하려면 comment에 아래와 같이 작성해주세요

@coderabbitai review

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 그룹 참여, 취소, 삭제 기능 추가
    • 모임 상세 정보에 실시간 데이터 표시
  • 개선사항

    • 날짜 및 시간 표시 형식 개선
    • 모임 참가자 목록 UI 및 상호작용 개선
    • 그룹 작업 모달 사용자 환경 향상
    • 프로필 및 그룹 기본 이미지 지원

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

워크스루

풀 요청은 더미 데이터 기반의 밋업 페이지를 실제 데이터 구조로 전환합니다. 그룹 서비스에 참여, 취소, 삭제 작업을 추가하고, 관련 훅과 타입을 업데이트하며, 에러 처리 로직을 동적 임포트 기반으로 리팩터링합니다.

변경 사항

코호트 / 파일(들) 변경 요약
기본 설정 및 상수
constants/default-images.ts, next.config.ts
기본 프로필/그룹 이미지 상수 추가; S3 버킷 호스트명 업데이트 (sprint-fe-project → we-go-bucket)
API 및 서비스 계층
src/api/core/index.ts, src/api/service/group-service/index.ts
에러 처리를 동적 임포트 기반으로 리팩터링; 그룹 서비스에 attendGroup, cancelGroup, deleteGroup 메서드 추가; GetGroupDetailsPayload를 GroupIdPayload로 변경
페이지 및 레이아웃
src/app/meetup/[groupId]/page.tsx
DUMMY_MEETUP_DATA 제거 및 실제 데이터 바인딩 적용; 컴포넌트에 실제 데이터 전달
밋업 배너 및 버튼
src/components/pages/meetup/meetup-banner-images/index.tsx, src/components/pages/meetup/meetup-buttons/index.tsx
배너의 이미지 길이 확인 로직 개선; 버튼 컴포넌트의 Props 구조 변경 (progress/ownerInfo → conditions/groupId); 호스트 상태 검증 로직 추가
밋업 설명 섹션
src/components/pages/meetup/meetup-descriptions/description-sections/*
DescriptionContent 삭제 및 DescriptionDetail 추가; 모든 설명 하위 컴포넌트를 GetGroupDetailsResponse 타입으로 통일; Props 구조 간소화
밋업 설명 컨테이너
src/components/pages/meetup/meetup-descriptions/index.tsx, src/components/pages/meetup/meetup-members/index.tsx
description 프로퍼티를 descriptions로 변경; 모든 자식 컴포넌트에 실제 데이터 전달 구조 업데이트; MeetupMembers에서 더미 데이터 제거 및 실제 멤버 목록 사용
밋업 모달
src/components/pages/meetup/meetup-modal/index.tsx
groupId 프로퍼티 추가; type 값을 'join'
유틸리티 함수
src/lib/formatDateTime.ts, src/components/pages/post-meetup/fields/date-field/index.tsx
formatTimeAgo/formatDateTime 함수 리팩터링 (floor 반올림, 커스텀 포맷 지원); date-field에서 formatDateTime 사용
그룹 액션 훅
src/hooks/use-group/use-group-attend/index.ts, src/hooks/use-group/use-group-cancel/index.ts, src/hooks/use-group/use-group-delete/index.ts, src/hooks/use-group/use-group-get-details/index.ts
세 가지 새로운 그룹 액션 훅 추가 (attend, cancel, delete); useGetGroupDetails의 payload 타입 업데이트
타입 정의
src/types/service/group.ts
GroupIdPayload 타입 신규 추가; GetGroupDetailsPayload 구조 변경; PreUploadGroupImageResponse 및 CreateGroupResponse 이미지 타입 업데이트

시퀀스 다이어그램

sequenceDiagram
    participant User
    participant MeetupPage as Meetup Page
    participant MeetupButtons as MeetupButtons
    participant MeetupModal as MeetupModal
    participant Hook as useAttendGroup/<br/>useCancelGroup/<br/>useDeleteGroup
    participant API as API Service
    participant Server as Server
    participant Cache as React Query Cache

    User->>MeetupPage: 그룹 상세 페이지 방문
    MeetupPage->>API: getGroupDetails(groupId)
    API->>Server: GET /groups/{groupId}
    Server-->>API: GroupDetails 반환
    API-->>MeetupPage: 데이터 전달
    MeetupPage->>MeetupButtons: conditions, groupId 전달

    User->>MeetupButtons: 참여/취소/삭제 버튼 클릭
    MeetupButtons->>MeetupModal: 모달 열기 (type, groupId)
    User->>MeetupModal: 확인 클릭
    
    alt 참여 액션
        MeetupModal->>Hook: useAttendGroup 실행
        Hook->>API: attendGroup({groupId})
        API->>Server: POST /groups/{groupId}/attend
    else 취소 액션
        MeetupModal->>Hook: useCancelGroup 실행
        Hook->>API: cancelGroup({groupId})
        API->>Server: POST /groups/{groupId}/cancel
    else 삭제 액션
        MeetupModal->>Hook: useDeleteGroup 실행
        Hook->>API: deleteGroup({groupId})
        API->>Server: DELETE /groups/{groupId}
    end

    Server-->>API: 응답 반환
    API->>Cache: 그룹 상세 쿼리 무효화
    Cache->>MeetupPage: 캐시 업데이트
    MeetupPage->>MeetupPage: 상태 갱신
    MeetupModal->>User: 모달 닫음
    
    alt 삭제의 경우
        MeetupModal->>User: 홈페이지로 리다이렉트
    end
Loading

예상 코드 리뷰 난이도

🎯 4 (복잡함) | ⏱️ ~45분

검토 시 특별히 주의할 부분:

  • src/api/core/index.ts: 에러 처리 로직이 동적 임포트 기반으로 리팩터링되었으므로, 서버/클라이언트 환경에서의 동작이 올바른지 확인 필요
  • src/components/pages/meetup/meetup-buttons/index.tsx: isHost 상태 관리와 userId 비교 로직의 정확성 검증
  • src/components/pages/meetup/meetup-modal/index.tsx: 세 가지 액션 타입(attend/cancel/delete)에 따른 훅 분기 로직과 성공 후 네비게이션 흐름 확인
  • 설명 섹션 컴포넌트들: GetGroupDetailsResponse 타입으로 통일된 Props 구조가 모든 하위 컴포넌트에서 일관되게 적용되었는지 확인
  • src/lib/formatDateTime.ts: floor 반올림 방식 변경이 기존 UI에 미치는 영향 (특히 상대 시간 표시 정확성)
  • src/types/service/group.ts: PreUploadGroupImageResponse.images의 배열 구조 변경이 기존 사용처에서 호환되는지 확인

제안 리뷰어

  • wooktori

🐰 깡충깡충

더미 데이터여 안녕, 실제 데이터 반가워!
그룹 액션 세 개를 슈욱 추가하고,
타입도 맞춰 정렬했지 우리는,
에러 핸들링도 똑똑해졌어,
밋업 페이지는 이제 참 쌩쌩하네! 🚀

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive PR 설명은 변경사항 섹션에 작업 내용과 수정사항을 상세히 기술하고 있으나, 관련 이슈 연결, 테스트 검증 체크, 그리고 스크린샷이 미완성 상태입니다. 관련 이슈 번호를 'Closes #' 에 작성하고, 수동 테스트 검증 완료 여부를 체크하며, UI 변경사항에 대한 스크린샷을 추가하여 PR 설명을 완성해주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 주요 변경사항(모임상세 정보 바인딩, date 유틸함수 refactor, 디폴트 이미지 상수 파일 생성)을 명확하게 요약하고 있으며, 실제 변경 내용과 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

@github-actions
Copy link

github-actions bot commented Dec 14, 2025

🎭 Playwright Report

E2E Test가 성공적으로 완료되었습니다.

Test 요약 내용을 확인해주세요.

Status Build Log Updated (UTC)
✅ Ready View Build 2025-12-14 14:22:20

📊 Test Summary

  • ✅ Passed: 9
  • ❌ Failed: 0
  • ⏱️ Duration: 34.1s

📜 Test Details

✅ Passed Tests (9)
  • profile.test.ts (9)
    • [chromium] 비로그인 상태에서 /mypage 접속 시 /login으로 redirect 되는 지 테스트
    • [chromium] 나의 프로필 페이지로 접속 시 /mypage로 /redirect 되는 지 테스트
    • [chromium] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트
    • [firefox] 비로그인 상태에서 /mypage 접속 시 /login으로 redirect 되는 지 테스트
    • [firefox] 나의 프로필 페이지로 접속 시 /mypage로 /redirect 되는 지 테스트
    • [firefox] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트
    • [webkit] 비로그인 상태에서 /mypage 접속 시 /login으로 redirect 되는 지 테스트
    • [webkit] 나의 프로필 페이지로 접속 시 /mypage로 /redirect 되는 지 테스트
    • [webkit] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트

@github-actions
Copy link

github-actions bot commented Dec 14, 2025

📊 Coverage Report

Status Build Log Updated (UTC)
✅ Ready View Build 2025-12-14 14:21:08

📉 #180main에 병합하면 coverage가 0.92% 감소합니다.

Coverage 요약

@@             Coverage Diff             @@
##             main     #180       +/-   ##
===========================================
- Coverage   35.89%   34.97%    -0.92%     
===========================================
  Files         143      147        +4     
  Lines        5850     6016      +166     
  Branches      231      235        +4     
===========================================
+ Hits         2100     2104        +4     
+ Misses       3750     3912      +162     

영향받은 파일

파일 Coverage 변화
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/api/core/index.ts 46.00% (-7.33%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/api/service/group-service/index.ts 37.93% (+1.93%) ⬆️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/lib/formatDateTime.ts 22.22% (-6.35%) ⬇️

@github-actions
Copy link

github-actions bot commented Dec 14, 2025

🎨 Storybook Report

변경 사항이 없습니다

모든 Story가 이전 빌드와 동일합니다.

Status Storybook Build Log Updated (UTC)
✅ Unchanged View Storybook View Build 2025-12-14 14:21:29

@github-actions
Copy link

github-actions bot commented Dec 14, 2025

🚀 PR Preview Report

Build가 성공적으로 완료되었습니다.

Preview에서 변경사항을 확인하세요.

Status Preview Build Log Updated (UTC)
✅ Ready Visit Preview View Logs 2025-12-14 14:22:18

@HopeFullee HopeFullee added the Ready For Review! 리뷰 받을 준비가 되었습니다. label Dec 14, 2025
Copy link
Member

Choose a reason for hiding this comment

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

저만 쓸줄알고 어디 굴러다니던거 대충 넣어논거였는데 생각해보니까 쓰는 곳이 많았군요 😅
수정 감사합니다

@claudia99503
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/meetup/[groupId]/page.tsx (1)

23-23: 디버그용 console.log 제거 필요.

프로덕션 코드에 console.log가 남아 있습니다. 배포 전 제거해 주세요.

-  console.log(data);
🧹 Nitpick comments (14)
src/components/pages/meetup/meetup-banner-images/index.tsx (1)

31-31: objectFit prop은 deprecated 되었습니다.

Next.js Image 컴포넌트에서 objectFit prop은 deprecated 되었습니다. style prop이나 className을 사용하세요.

-              <Image alt='image' draggable={false} fill objectFit='cover' src={imageUrl440x240} />
+              <Image alt='image' draggable={false} fill className='object-cover' src={imageUrl440x240} />

Line 37의 default 이미지에도 동일하게 적용해주세요.

src/components/pages/meetup/meetup-descriptions/description-sections/description-tags/index.tsx (1)

8-10: 더 명시적인 조건 검사를 고려해보세요.

Boolean(tags.length)보다 tags.length > 0이 의도를 더 명확하게 표현합니다.

-  const hasTags = Boolean(tags.length);
+  const hasTags = tags.length > 0;
src/hooks/use-group/use-group-cancel/index.ts (1)

15-18: 프로덕션 코드에서 console.log 제거를 권장합니다.

성공/실패 로깅을 위해 console.log를 사용하고 있습니다. 프로덕션 환경에서는 적절한 로깅 라이브러리를 사용하거나, 사용자에게 피드백을 제공하는 토스트 메시지 등으로 대체하는 것이 좋습니다.

src/hooks/use-group/use-group-attend/index.ts (1)

15-18: 프로덕션 코드에서 console.log 제거를 권장합니다.

성공/실패 로깅을 위해 console.log를 사용하고 있습니다. 프로덕션 환경에서는 적절한 로깅 라이브러리를 사용하거나, 사용자에게 피드백을 제공하는 토스트 메시지 등으로 대체하는 것이 좋습니다.

src/hooks/use-group/use-group-delete/index.ts (1)

10-14: 프로덕션 코드에서 console.log 제거를 권장합니다.

성공/실패 로깅을 위해 console.log를 사용하고 있습니다. 프로덕션 환경에서는 적절한 로깅 라이브러리를 사용하거나, 사용자에게 피드백을 제공하는 토스트 메시지 등으로 대체하는 것이 좋습니다.

src/app/meetup/[groupId]/page.tsx (1)

21-21: 로딩 상태 UI 고려.

data가 없을 때 null을 반환하면 레이아웃 시프트가 발생할 수 있습니다. 로딩 스피너나 스켈레톤 UI를 추가하는 것을 권장합니다.

src/components/pages/meetup/meetup-members/index.tsx (2)

41-41: 리스트 keynickName 대신 userId 사용 권장.

nickName은 고유하지 않을 수 있습니다. userId가 고유 식별자이므로 key로 사용하는 것이 더 안정적입니다.

-            key={nickName}
+            key={userId}

55-55: objectFit prop은 Next.js Image에서 deprecated.

Next.js 13+ 에서 objectFit prop은 deprecated되었습니다. className 또는 style로 대체해 주세요. 현재 className에 이미 rounded-full이 있으므로 object-cover를 추가하면 됩니다.

-                    className='h-16 w-16 rounded-full'
+                    className='h-16 w-16 rounded-full object-cover'
                     // ...
-                    objectFit='cover'
src/components/pages/meetup/meetup-descriptions/description-sections/description-profile/index.tsx (2)

4-4: Import 경로 불일치.

다른 import들은 @/ alias를 사용하지만 이 import는 상대 경로 형식입니다. 일관성을 위해 수정해 주세요.

-import { DEFAULT_PROFILE_IMAGE } from 'constants/default-images';
+import { DEFAULT_PROFILE_IMAGE } from '@/constants/default-images';

22-22: objectFit prop은 Next.js Image에서 deprecated.

classNameobject-cover를 추가하고 objectFit prop을 제거해 주세요.

-          className='h-10 w-10 shrink-0 rounded-full'
+          className='h-10 w-10 shrink-0 rounded-full object-cover'
           // ...
-          objectFit='cover'
src/api/service/group-service/index.ts (1)

72-86: 새 API 엔드포인트들이 잘 구현되었습니다.

attendGroup, cancelGroup, deleteGroup 엔드포인트가 일관된 패턴으로 추가되었습니다. deleteGroup의 반환 타입이 명시되지 않았는데, api.deletePromise<T>를 반환하므로 타입 안전성을 위해 명시적으로 지정하는 것이 좋습니다.

  deleteGroup: (payload: GroupIdPayload) => {
-   return api.delete(`/groups/${payload.groupId}`);
+   return api.delete<void>(`/groups/${payload.groupId}`);
  },
src/components/pages/meetup/meetup-modal/index.tsx (2)

62-80: MODAL_MESSAGEonConfirm 속성이 정의되었지만 사용되지 않습니다.

onConfirm 함수가 각 타입별로 정의되어 있지만 handleConfirmClick에서는 if-else 체인을 사용하고 있습니다. 사용하지 않는 코드는 제거하거나, 일관성을 위해 onConfirm을 활용하도록 리팩토링하세요.

옵션 1: 사용하지 않는 onConfirm 제거

 const MODAL_MESSAGE = {
   attend: {
     title: '모임에 참여하시겠어요?',
     description: '참여 후 바로 그룹채팅에 참여할 수 있어요!',
     confirm: '참여하기',
-    onConfirm: (attendMutate: () => void) => attendMutate(),
   },
   cancel: {
     title: '모임을 정말 탈퇴하시겠어요?',
     description: '탈퇴 시 그룹채팅과 모임 활동이 종료돼요.',
     confirm: '탈퇴하기',
-    onConfirm: (cancelMutate: () => void) => cancelMutate(),
   },
   delete: {
     title: '모임을 정말 취소하시겠어요?',
     description: '취소 후에는 다시 복구할 수 없어요.',
     confirm: '취소하기',
-    onConfirm: (deleteMutate: () => void) => deleteMutate(),
   },
 };

40-56: 버튼 너비 스타일링 개선 제안

w-[50%]gap-2를 함께 사용하면 gap으로 인해 버튼들이 컨테이너를 넘칠 수 있습니다. flex-1을 사용하면 gap을 고려하여 균등하게 분배됩니다.

-        <Button
-          className='!text-text-sm-semibold w-[50%]'
+        <Button
+          className='!text-text-sm-semibold flex-1'
           ...
-        <Button
-          className='!text-text-sm-bold w-[50%]'
+        <Button
+          className='!text-text-sm-bold flex-1'
src/components/pages/meetup/meetup-buttons/index.tsx (1)

46-57: isHostnull일 때 렌더링 처리 개선 제안

isHostnull인 초기 상태에서 버튼이 "모임 탈퇴"로 표시될 수 있습니다. 호스트 여부가 결정될 때까지 로딩 상태를 표시하거나 버튼을 숨기는 것을 고려하세요.

+ if (isHost === null) {
+   return <div className='sticky bottom-[56px] border-t-1 border-gray-200 bg-white px-4 py-3' />;
+ }
+
  return (
    <div className='sticky bottom-[56px] border-t-1 border-gray-200 bg-white px-4 py-3'>
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8cdb47 and 9d6465e.

📒 Files selected for processing (25)
  • constants/default-images.ts (1 hunks)
  • next.config.ts (1 hunks)
  • src/api/core/index.ts (1 hunks)
  • src/api/service/group-service/index.ts (2 hunks)
  • src/app/meetup/[groupId]/page.tsx (1 hunks)
  • src/components/pages/meetup/meetup-banner-images/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-buttons/index.tsx (2 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-content/index.tsx (0 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-detail/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-profile/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-progress/index.tsx (2 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-setting/index.tsx (2 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-tags/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-title/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-descriptions/description-sections/index.ts (1 hunks)
  • src/components/pages/meetup/meetup-descriptions/index.tsx (1 hunks)
  • src/components/pages/meetup/meetup-members/index.tsx (3 hunks)
  • src/components/pages/meetup/meetup-modal/index.tsx (1 hunks)
  • src/components/pages/post-meetup/fields/date-field/index.tsx (2 hunks)
  • src/hooks/use-group/use-group-attend/index.ts (1 hunks)
  • src/hooks/use-group/use-group-cancel/index.ts (1 hunks)
  • src/hooks/use-group/use-group-delete/index.ts (1 hunks)
  • src/hooks/use-group/use-group-get-details/index.ts (1 hunks)
  • src/lib/formatDateTime.ts (2 hunks)
  • src/types/service/group.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • src/components/pages/meetup/meetup-descriptions/description-sections/description-content/index.tsx
🧰 Additional context used
🧬 Code graph analysis (16)
src/components/pages/meetup/meetup-descriptions/description-sections/description-detail/index.tsx (1)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/description-sections/description-title/index.tsx (2)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/description-sections/index.ts (1)
  • DescriptionTitle (6-6)
src/hooks/use-group/use-group-delete/index.ts (2)
src/types/service/group.ts (1)
  • GroupIdPayload (163-165)
src/api/index.ts (1)
  • API (22-22)
src/api/service/group-service/index.ts (2)
src/types/service/group.ts (2)
  • GroupIdPayload (163-165)
  • GetGroupDetailsResponse (125-161)
src/api/core/index.ts (1)
  • api (74-100)
src/components/pages/meetup/meetup-descriptions/description-sections/description-profile/index.tsx (3)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/description-sections/index.ts (1)
  • DescriptionProfile (2-2)
constants/default-images.ts (1)
  • DEFAULT_PROFILE_IMAGE (2-3)
src/app/meetup/[groupId]/page.tsx (3)
src/components/pages/meetup/meetup-descriptions/index.tsx (1)
  • MeetupDescriptions (26-49)
src/components/pages/meetup/meetup-members/index.tsx (1)
  • MeetupMembers (19-96)
src/components/pages/meetup/meetup-buttons/index.tsx (1)
  • MeetupButtons (22-77)
src/hooks/use-group/use-group-cancel/index.ts (3)
src/types/service/group.ts (1)
  • GroupIdPayload (163-165)
src/api/index.ts (1)
  • API (22-22)
src/lib/query-key/query-key-group/index.ts (1)
  • groupKeys (1-10)
src/hooks/use-group/use-group-get-details/index.ts (1)
src/types/service/group.ts (1)
  • GroupIdPayload (163-165)
src/components/pages/meetup/meetup-members/index.tsx (3)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/index.ts (1)
  • MeetupMembers (4-4)
src/components/icon/index.tsx (1)
  • Icon (55-61)
src/components/pages/meetup/meetup-descriptions/description-sections/description-tags/index.tsx (1)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/index.tsx (7)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/description-sections/description-profile/index.tsx (1)
  • DescriptionProfile (12-33)
src/components/pages/meetup/meetup-descriptions/description-sections/description-title/index.tsx (1)
  • DescriptionTitle (7-13)
src/components/pages/meetup/meetup-descriptions/description-sections/description-tags/index.tsx (1)
  • DescriptionTags (7-21)
src/components/pages/meetup/meetup-descriptions/description-sections/description-detail/index.tsx (1)
  • DescriptionDetail (7-13)
src/components/pages/meetup/meetup-descriptions/description-sections/description-setting/index.tsx (1)
  • DescriptionSetting (9-24)
src/components/pages/meetup/meetup-descriptions/description-sections/description-progress/index.tsx (1)
  • DescriptionProgress (12-37)
src/components/pages/meetup/meetup-modal/index.tsx (3)
src/hooks/use-group/use-group-attend/index.ts (1)
  • useAttendGroup (7-22)
src/hooks/use-group/use-group-cancel/index.ts (1)
  • useCancelGroup (7-22)
src/hooks/use-group/use-group-delete/index.ts (1)
  • useDeleteGroup (6-18)
src/hooks/use-group/use-group-attend/index.ts (3)
src/types/service/group.ts (1)
  • GroupIdPayload (163-165)
src/api/index.ts (1)
  • API (22-22)
src/lib/query-key/query-key-group/index.ts (1)
  • groupKeys (1-10)
src/components/pages/meetup/meetup-descriptions/description-sections/description-setting/index.tsx (3)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-descriptions/description-sections/index.ts (1)
  • DescriptionSetting (4-4)
src/lib/formatDateTime.ts (1)
  • formatDateTime (33-54)
src/components/pages/meetup/meetup-buttons/index.tsx (2)
src/types/service/group.ts (1)
  • GetGroupDetailsResponse (125-161)
src/components/pages/meetup/meetup-modal/index.tsx (1)
  • MeetupModal (16-60)
src/components/pages/post-meetup/fields/date-field/index.tsx (2)
src/components/ui/modal/index.tsx (1)
  • useModal (18-22)
src/lib/formatDateTime.ts (1)
  • formatDateTime (33-54)
🔇 Additional comments (17)
next.config.ts (1)

13-13: LGTM!

S3 버킷 hostname 변경이 적절합니다. 새로운 이미지 호스팅 소스와 일치합니다.

src/components/pages/meetup/meetup-banner-images/index.tsx (1)

20-20: LGTM!

Boolean() 변환을 통한 명시적 타입 처리가 좋습니다.

src/lib/formatDateTime.ts (3)

10-10: UTC 가정에 대한 주의가 필요합니다.

'Z' 접미사를 자동으로 추가하는 방식은 입력 문자열이 항상 UTC라고 가정합니다. 만약 로컬 타임존 문자열이 전달되면 잘못된 시간 차이가 계산될 수 있습니다. 백엔드에서 항상 UTC ISO 문자열을 반환하는지 확인이 필요합니다.


13-23: LGTM!

Math.ceil에서 Math.floor로 변경하여 경과 시간만 표시하도록 수정한 점, 그리고 diffPerDay 계산을 /30에서 /24로 올바르게 수정한 점이 좋습니다.


46-53: LGTM!

커스텀 포맷 지원이 잘 구현되었습니다. yyyyyy보다 먼저 처리되어 이중 치환 문제가 방지됩니다.

src/components/pages/meetup/meetup-descriptions/description-sections/description-setting/index.tsx (2)

5-9: LGTM!

GetGroupDetailsResponse에서 Pick을 사용하여 타입을 명확하게 정의한 점이 좋습니다. Props 구조가 일관성 있게 개선되었습니다.


19-19: LGTM!

formatDateTime 유틸리티를 활용한 시간 포맷팅이 적절합니다.

src/api/core/index.ts (1)

62-67: 클라이언트 측 404 처리가 누락되었습니다.

서버 측에서는 notFound()를 호출하지만, 클라이언트 측에서는 404 에러가 별도 처리 없이 throw errorResponse로 전달됩니다. 의도된 동작인지 확인이 필요합니다.

클라이언트에서도 404 페이지로 이동해야 한다면:

    if (status === 404) {
      if (isServer) {
        const { notFound } = await import('next/navigation');
        notFound();
+     } else {
+       window.location.href = '/404';
+       return;
      }
    }
src/types/service/group.ts (1)

83-87: 타입 정의가 명확하고 일관성 있게 개선되었습니다.

PreUploadGroupImageResponse의 images 타입이 명시적인 배열 형태로 변경되었고, GroupIdPayload 인터페이스 추가로 그룹 ID 관련 페이로드의 타입 안정성이 향상되었습니다.

Also applies to: 122-122, 163-165

src/hooks/use-group/use-group-get-details/index.ts (1)

5-7: 타입 변경이 올바르게 적용되었습니다.

GetGroupDetailsPayload에서 GroupIdPayload로의 타입 변경이 일관성 있게 적용되었습니다.

src/components/pages/meetup/meetup-descriptions/description-sections/index.ts (1)

1-1: 컴포넌트 export 변경이 올바르게 적용되었습니다.

DescriptionContent에서 DescriptionDetail로의 export 변경이 리팩토링 의도에 맞게 적용되었습니다.

src/app/meetup/[groupId]/page.tsx (1)

28-30: LGTM!

더미 데이터에서 실제 API 데이터로의 바인딩 변경이 올바르게 적용되었습니다. 각 컴포넌트의 Props 인터페이스와 일치합니다.

src/components/pages/meetup/meetup-descriptions/description-sections/description-title/index.tsx (2)

1-4: LGTM!

GetGroupDetailsResponse['title'] 인덱스 접근 타입 사용으로 타입 안전성이 향상되었습니다.


10-10: line-clamp 제거로 인한 긴 제목 오버플로우 가능성 확인 필요.

line-clamp-2가 제거되고 break-keep만 적용되었습니다. 매우 긴 제목의 경우 레이아웃을 벗어날 수 있으니, 최대 제목 길이가 API에서 제한되는지 확인해 주세요.

src/components/pages/meetup/meetup-descriptions/description-sections/description-detail/index.tsx (1)

1-13: LGTM!

DescriptionDetail 컴포넌트가 깔끔하게 구현되었습니다. 타입 안전성과 한글 텍스트를 위한 break-keep 스타일링이 적절합니다.

src/components/pages/meetup/meetup-descriptions/index.tsx (1)

11-47: 타입 안전성과 컴포넌트 구조가 잘 설계되었습니다.

Pick<GetGroupDetailsResponse, ...>를 사용한 타입 정의는 API 응답 타입과의 일관성을 유지하면서 필요한 필드만 명시적으로 요구합니다. 자식 컴포넌트들로의 props 매핑도 명확합니다.

src/components/pages/meetup/meetup-buttons/index.tsx (1)

59-65: 이미 참여한 사용자의 채팅 입장 버튼 비활성화 로직 확인 필요

이미 모임에 참여한 사용자(isJoined === true)가 채팅에 입장하는 버튼인데, participantCount >= maxParticipants 조건으로 비활성화됩니다. 모임이 꽉 찬 후에도 기존 참여자는 채팅에 들어갈 수 있어야 하지 않나요?

Comment on lines +1 to +6
// 나중에 회원 프로필, 모임 디폴트 시안 나오면 각각 '/public/default-images/' 경로에서 지정
export const DEFAULT_PROFILE_IMAGE =
'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';

export const DEFAULT_GROUP_IMAGE =
'https://images.unsplash.com/photo-1705599359461-f99dc9e80efa?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

외부 URL 의존성에 대한 리스크를 추적하세요.

현재 Unsplash 외부 URL을 사용하고 있어, 해당 이미지가 삭제되거나 변경될 경우 문제가 발생할 수 있습니다. 주석에서 언급한 대로 로컬 이미지로 교체하는 작업을 이슈로 추적하는 것을 권장합니다.

이 작업을 추적하기 위한 이슈를 생성해 드릴까요?

🤖 Prompt for AI Agents
In constants/default-images.ts lines 1-6, the file uses external Unsplash URLs
which creates a runtime dependency risk if those images are removed/changed;
create an issue to replace these with local assets under /public/default-images
and update the constants to point to those local paths (e.g.,
/public/default-images/default-profile.png and
/public/default-images/default-group.png), and add a fallback strategy (serve a
bundled placeholder or log a warning) if local files are missing; include the
issue description, acceptance criteria, and assign to the frontend/assets owner
so the images are added and the constants updated.

Comment on lines +50 to +61
if (status === 401) {
if (isServer) {
const { redirect } = await import('next/navigation');
redirect('/login');
} else {
if (window.location.pathname === '/login') {
throw errorResponse;
}
const currentPath = window.location.pathname + window.location.search;
window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

클라이언트 측 401 처리 시 코드 흐름 문제가 있습니다.

window.location.href 할당 후에도 코드가 계속 실행되어 line 69의 throw errorResponse가 실행됩니다. 리다이렉트가 비동기적으로 발생하기 때문에, 불필요한 에러가 throw될 수 있습니다.

      } else {
        if (window.location.pathname === '/login') {
          throw errorResponse;
        }
        const currentPath = window.location.pathname + window.location.search;
        window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
+       return; // 리다이렉트 후 추가 실행 방지
      }
    }
📝 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
if (status === 401) {
if (isServer) {
const { redirect } = await import('next/navigation');
redirect('/login');
} else {
if (window.location.pathname === '/login') {
throw errorResponse;
}
const currentPath = window.location.pathname + window.location.search;
window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
}
}
if (status === 401) {
if (isServer) {
const { redirect } = await import('next/navigation');
redirect('/login');
} else {
if (window.location.pathname === '/login') {
throw errorResponse;
}
const currentPath = window.location.pathname + window.location.search;
window.location.href = `/login?error=unauthorized&path=${encodeURIComponent(currentPath)}`;
return; // 리다이렉트 후 추가 실행 방지
}
}
🤖 Prompt for AI Agents
In src/api/core/index.ts around lines 50 to 61, the client-side 401 branch
assigns window.location.href but execution continues and later reaches the throw
at line 69; stop further execution after initiating the redirect by immediately
returning (or using window.location.replace and then returning) right after
setting window.location.href so the subsequent throw/error logic is not executed
on the client.

Comment on lines +40 to +44
useEffect(() => {
const sessionId = Number(Cookies.get('userId'));
// eslint-disable-next-line react-hooks/set-state-in-effect
setIsHost(sessionId === createdBy.userId);
}, [createdBy]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

isHost 상태 초기화 및 쿠키 파싱 관련 문제가 있습니다.

  1. Cookies.get('userId')undefined를 반환할 수 있으며, Number(undefined)NaN이 됩니다.
  2. isHostnull인 초기 렌더링 시점에 Line 54의 isHost ? 'delete' : 'cancel''cancel'로 평가됩니다.
  useEffect(() => {
-   const sessionId = Number(Cookies.get('userId'));
-   // eslint-disable-next-line react-hooks/set-state-in-effect
-   setIsHost(sessionId === createdBy.userId);
+   const userIdCookie = Cookies.get('userId');
+   if (userIdCookie) {
+     const sessionId = Number(userIdCookie);
+     setIsHost(sessionId === createdBy.userId);
+   } else {
+     setIsHost(false);
+   }
  }, [createdBy]);
🤖 Prompt for AI Agents
In src/components/pages/meetup/meetup-buttons/index.tsx around lines 40-44, the
effect blindly does Number(Cookies.get('userId')) which can produce NaN and you
currently initialize isHost to null causing the early render to treat the
ternary as 'cancel'; fix by parsing the cookie safely (e.g. read cookie string,
use parseInt and check isNaN) and only setIsHost when the parsed sessionId is a
valid number, and also initialize isHost to false (or compute synchronously from
the cookie) so the initial render doesn't treat null as falsy; ensure the effect
compares the numeric sessionId with createdBy.userId (or createdBy.userId as
Number) and update the dependency list appropriately.

<p className='text-text-md-semibold text-gray-800'>{name}</p>
<p className='text-text-xs-regular text-gray-600'>{bio}</p>
<p className='text-text-md-semibold text-gray-800'>{nickName}</p>
<p className='text-text-xs-regular text-gray-600'>some dummy bio text</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

하드코딩된 더미 bio 텍스트.

"some dummy bio text"가 하드코딩되어 있습니다. API 응답에 bio 필드가 없다면, 해당 UI를 제거하거나 TODO 주석을 추가하여 추후 작업을 명시해 주세요.

🤖 Prompt for AI Agents
In
src/components/pages/meetup/meetup-descriptions/description-sections/description-profile/index.tsx
around line 28, there is a hardcoded bio string ("some dummy bio text"); remove
the hardcoded text and replace it with conditional rendering of the actual bio
prop (e.g., render user.bio when present), otherwise render nothing or a small
placeholder; if you do not implement the API hookup now, replace the string with
an explicit TODO comment indicating to wire up the bio field and remove the
placeholder later.

progress: { maxParticipants, participantCount },
createdAt,
}: Props) => {
const progressRate = Math.ceil((participantCount / maxParticipants) * 100);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

maxParticipants가 0일 때 division by zero 오류 가능성이 있습니다.

maxParticipants가 0인 경우 NaN 또는 Infinity가 발생할 수 있습니다. 방어적 처리가 필요합니다.

- const progressRate = Math.ceil((participantCount / maxParticipants) * 100);
+ const progressRate = maxParticipants > 0 
+   ? Math.ceil((participantCount / maxParticipants) * 100) 
+   : 0;
📝 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
const progressRate = Math.ceil((participantCount / maxParticipants) * 100);
const progressRate = maxParticipants > 0
? Math.ceil((participantCount / maxParticipants) * 100)
: 0;
🤖 Prompt for AI Agents
In
src/components/pages/meetup/meetup-descriptions/description-sections/description-progress/index.tsx
around line 16, the calculation const progressRate = Math.ceil((participantCount
/ maxParticipants) * 100); can divide by zero when maxParticipants is 0; change
it to first guard against maxParticipants <= 0 (treat as no capacity) and
compute a safe progressRate: if maxParticipants <= 0 set progressRate to 0 (or
an appropriate sentinel), otherwise compute the percentage and clamp the result
to the 0–100 range (also ensure participantCount is non-negative) before
applying Math.ceil so you never produce NaN or Infinity.

Comment on lines +25 to +27
const defaultProfileImageUrl =
'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

기본 프로필 이미지 상수 중복.

DEFAULT_PROFILE_IMAGE 상수가 constants/default-images.ts에 이미 정의되어 있습니다. 중복 코드를 제거하고 상수를 import하여 사용해 주세요.

+import { DEFAULT_PROFILE_IMAGE } from 'constants/default-images';
 import { GetGroupDetailsResponse } from '@/types/service/group';
 
 // ...
 
-  const defaultProfileImageUrl =
-    'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';
-
   // ...
   
-  src={profileImage ?? defaultProfileImageUrl}
+  src={profileImage ?? DEFAULT_PROFILE_IMAGE}
📝 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
const defaultProfileImageUrl =
'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?q=80&w=717&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D';
🤖 Prompt for AI Agents
In src/components/pages/meetup/meetup-members/index.tsx around lines 25 to 27,
remove the locally defined defaultProfileImageUrl constant and instead import
DEFAULT_PROFILE_IMAGE from constants/default-images.ts; update the file's import
section to add: import { DEFAULT_PROFILE_IMAGE } from 'constants/default-images'
(adjust relative path if needed), replace all uses of defaultProfileImageUrl
with DEFAULT_PROFILE_IMAGE, and delete the now-unused local constant.

Comment on lines +19 to +20
const hasValue = Boolean(field.state.value);
const formattedDate = formatDateTime(field.state.value);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

field.state.value가 비어있을 때 Invalid Date 발생 가능성이 있습니다.

formatDateTimehasValue 체크 전에 무조건 호출됩니다. field.state.value가 빈 문자열이나 undefined일 경우 new Date('')는 Invalid Date를 생성하여 "NaN. NaN. NaN - NaN:NaN" 형태의 문자열이 반환됩니다.

  const hasValue = Boolean(field.state.value);
-  const formattedDate = formatDateTime(field.state.value);
+  const formattedDate = hasValue ? formatDateTime(field.state.value) : '';
🤖 Prompt for AI Agents
In src/components/pages/post-meetup/fields/date-field/index.tsx around lines
19-20, formatDateTime is called unconditionally on field.state.value which can
be empty/undefined and produce "Invalid Date"; guard the call by checking
hasValue first (e.g., compute formattedDate only when hasValue is true) or
update formatDateTime to return an empty string/null for falsy inputs, and use
that guarded value in the render so you never call new Date('') or render NaN
output.

Comment on lines +6 to +17
export const useDeleteGroup = (payload: GroupIdPayload, callback: () => void) => {
const query = useMutation({
mutationFn: () => API.groupService.deleteGroup(payload),
onSuccess: async () => {
console.log('모임 삭제 성공.');
callback();
},
onError: () => {
console.log('모임 삭제 실패.');
},
});
return query;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

모임 삭제 후 쿼리 무효화가 누락되었습니다.

useAttendGroupuseCancelGroup은 성공 시 queryClient.invalidateQueries를 호출하여 상세 정보를 갱신하지만, useDeleteGroup은 쿼리 무효화를 수행하지 않습니다. 모임 삭제 후에는 해당 모임의 상세 쿼리를 무효화하거나 사용자를 다른 페이지로 리다이렉트해야 합니다.

-import { useMutation } from '@tanstack/react-query';
+import { useMutation, useQueryClient } from '@tanstack/react-query';

 import { API } from '@/api';
+import { groupKeys } from '@/lib/query-key/query-key-group';
 import { GroupIdPayload } from '@/types/service/group';

 export const useDeleteGroup = (payload: GroupIdPayload, callback: () => void) => {
+  const queryClient = useQueryClient();
+
   const query = useMutation({
     mutationFn: () => API.groupService.deleteGroup(payload),
     onSuccess: async () => {
+      await queryClient.invalidateQueries({ queryKey: groupKeys.detail(payload.groupId) });
       console.log('모임 삭제 성공.');
       callback();
     },
🤖 Prompt for AI Agents
In src/hooks/use-group/use-group-delete/index.ts around lines 6 to 17, the hook
is missing query invalidation after a successful delete; update the onSuccess
handler to obtain the react-query QueryClient (via useQueryClient), call
queryClient.invalidateQueries for the group's detail/list queries (e.g. the key
used for group detail like ['group', payload.groupId] and any list queries),
then call the existing callback; alternatively, if you want to redirect instead
of invalidating, invoke a navigation callback (e.g. router.push) in onSuccess to
send the user away from the deleted group's page.

Copy link
Member

@claudia99503 claudia99503 left a comment

Choose a reason for hiding this comment

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

👏🏻

@HopeFullee HopeFullee merged commit bc2118c into main Dec 14, 2025
7 checks passed
@HopeFullee HopeFullee deleted the somang-feat/group-details branch December 14, 2025 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready For Review! 리뷰 받을 준비가 되었습니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants