Skip to content

[FEAT] V2 모임 승인 허가 및 거절 구현#151

Merged
LimdaeIl merged 6 commits intomainfrom
feat/approve-reject
Dec 22, 2025
Merged

[FEAT] V2 모임 승인 허가 및 거절 구현#151
LimdaeIl merged 6 commits intomainfrom
feat/approve-reject

Conversation

@LimdaeIl
Copy link
Collaborator

@LimdaeIl LimdaeIl commented Dec 22, 2025

📝 Pull Request

📌 PR 종류

해당하는 항목에 체크해주세요.

  • 기능 추가 (Feature)
  • 버그 수정 (Fix)
  • 문서 수정 (Docs)
  • 코드 리팩터링 (Refactor)
  • 테스트 추가 (Test)
  • 기타 변경 (Chore)

✨ 변경 내용

그룹 호스트 또는 관리자가 보류 중인 사용자 가입 요청을 승인하거나 거부할 수 있는 기능을 구현합니다.

잘못된 사용자 상태 또는 권한 부족과 같은 다양한 승인/거부 시나리오에 대한 새로운 오류 코드를 도입합니다.

대상 사용자의 멤버십 정보를 나타내는 TargetMembership DTO를 추가합니다.

GroupV2AttendanceServiceapprovereject 메서드를 포함하도록 업데이트합니다.

기본 그룹 가입 정책을 FREE로 변경하고 INSTANT를 FREE로 이름을 바꿉니다.

🔍 관련 이슈

🧪 테스트

변경된 기능에 대한 테스트 범위 또는 테스트 결과를 작성해주세요.

  • 유닛 테스트 추가 / 수정
  • 통합 테스트 검증
  • 수동 테스트 완료

🚨 확인해야 할 사항 (Checklist)

PR을 제출하기 전에 아래 항목들을 확인해주세요.

  • 코드 포매팅 완료
  • 불필요한 파일/코드 제거
  • 로직 검증 완료
  • 프로젝트 빌드 성공
  • 린트/정적 분석 통과 (해당 시)

🙋 기타 참고 사항

리뷰어가 참고하면 좋을 만한 추가 설명이 있다면 적어주세요.

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 승인제 모임에서 호스트·권한 보유자가 가입 요청을 승인하거나 거절할 수 있는 기능이 추가되었습니다.
    • 가입 처리 결과를 포함한 상세 응답이 반환되어 상태 확인이 쉬워졌습니다.
  • Bug Fixes / Improvements

    • 가입 정책 기본값 및 명칭이 정리되어 가입 흐름이 일관되게 동작합니다.
    • 가입/승인 관련 오류 메시지가 더 구체적이고 구분되도록 개선되었습니다.
  • Tests

    • 승인/거절 워크플로우와 정책 변화를 반영한 통합 테스트 시나리오가 추가/갱신되었습니다.

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

그룹 호스트 또는 관리자가 보류 중인 사용자 가입 요청을 승인하거나 거부할 수 있는 기능을 구현합니다.

잘못된 사용자 상태 또는 권한 부족과 같은 다양한 승인/거부 시나리오에 대한 새로운 오류 코드를 도입합니다.

대상 사용자의 멤버십 정보를 나타내는 `TargetMembership` DTO를 추가합니다.

`GroupV2AttendanceService`에 `approve` 및 `reject` 메서드를 포함하도록 업데이트합니다.

기본 그룹 가입 정책을 FREE로 변경하고 INSTANT를 FREE로 이름을 바꿉니다.
@LimdaeIl LimdaeIl self-assigned this Dec 22, 2025
Copilot AI review requested due to automatic review settings December 22, 2025 14:24
@LimdaeIl LimdaeIl added the ✨enhancement New feature or request label Dec 22, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Warning

Rate limit exceeded

@LimdaeIl has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e10e49f and 08d7af8.

📒 Files selected for processing (2)
  • src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java
  • src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

이 PR은 V2 모임의 승인/거절 워크플로우를 구현합니다. 새로운 에러 코드들을 추가하고 도메인 엔티티에 승인·거절 상태전환 메서드를 추가했으며, 참여 정책 상수를 INSTANT에서 FREE로 변경하고 승인/거절용 DTO, 서비스 메서드, 컨트롤러 엔드포인트와 HTTP 테스트를 추가합니다.

Changes

Cohort / File(s) Summary
에러 핸들링
src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java
승인/거절 흐름용 8개 새 에러 코드 상수 추가(상태/권한/정책/중복 처리 관련)
도메인 엔티티 (멤버)
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java
approveJoin()rejectJoin() 메서드 추가 — PENDING 검증 후 상태 전환 및 시간 필드 업데이트
도메인 엔티티 (그룹)
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2.java
create() 기본 joinPolicy가 null일 때 기본값을 INSTANT→FREE로 변경
도메인 열거형
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2JoinPolicy.java
enum 상수명 INSTANTFREE로 변경
응답 DTO
src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java
승인/거절 결과를 담는 새 레코드 추가(그룹 상태, 참여자 수, 대상 멤버십 등)
공통 DTO
src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java
대상 멤버십 정보를 담는 새 레코드 추가(userId, groupUserId, status, joinedAt, leftAt)
서비스
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java
approve(approverUserId, groupId, targetUserId)reject(...) 메서드 추가 — ID/정책/상태/권한 검증, 대상 조회, 상태전환, 용량 검사 및 응답 생성
컨트롤러
src/main/java/team/wego/wegobackend/group/v2/presentation/GroupV2Controller.java
POST /{groupId}/attendance/{targetUserId}/approve.../reject 엔드포인트 추가
테스트(HTTP)
src/test/http/group/v2/v2-group-approve-reject.http
새 HTTP 시나리오 추가: 승인/거절 전체 워크플로우(등록, 요청, 승인/거절, 권한/중복 케이스 등)
테스트(데이터 업데이트)
src/test/http/group/v2/*.http
V2 테스트의 그룹 생성 페이로드에서 joinPolicy 값을 "INSTANT""FREE"로 변경

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as GroupV2Controller
    participant Service as GroupV2AttendanceService
    participant GroupRepo as GroupV2Repository
    participant UserRepo as GroupUserV2Repository
    participant Entity as GroupUserV2

    Client->>Controller: POST /{groupId}/attendance/{targetUserId}/approve
    Controller->>Service: approve(approverUserId, groupId, targetUserId)

    Service->>GroupRepo: 그룹 조회/상태 검증
    Service->>Service: 참여정책 검증 (APPROVAL_REQUIRED)
    Service->>Service: 승인자 권한 검증 (호스트/권한자)
    Service->>UserRepo: 대상 GroupUserV2 조회
    UserRepo->>Service: GroupUserV2 반환
    Service->>Entity: entity.approveJoin()
    Entity->>Entity: PENDING 검증 → ATTEND, joinedAt 갱신
    Service->>GroupRepo: 참여자 수 확인 → 필요시 그룹 상태 → FULL로 전환
    Service->>Controller: ApproveRejectGroupV2Response
    Controller->>Client: HTTP 200 + 응답
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 주의 필요 영역:
    • GroupV2AttendanceService.approve() / reject()의 검증 및 예외 매핑 (새 에러 코드와 정확히 매칭되는지)
    • 동시성/용량 검사 부분(참여자 증가 시 FULL 전환 타이밍)
    • GroupUserV2.approveJoin() / rejectJoin()의 상태 전환 조건 및 시간 필드 처리
    • 테스트 스크립트(v2-group-approve-reject.http)가 권한/중복/예외 케이스를 충분히 검증하는지

Possibly related PRs

Poem

🐰 방문한 모두를 살피며 뛰노는 토끼
PENDING 문턱에서 한숨 고르고
승인엔 박수, 거절엔 고개 숙여
FREE 바람에 정책은 바뀌었고
새 멤버들 맞이하네, 깡총 깡총 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 "[FEAT] V2 모임 승인 허가 및 거절 구현"으로 변경 사항의 핵심을 정확하게 설명합니다.
Linked Issues check ✅ Passed PR은 #150 이슈의 모든 코딩 요구사항을 충족합니다: 승인/거절 기능, 권한 검사, 상태 검증, 오류 코드 도입.
Out of Scope Changes check ✅ Passed INSTANT에서 FREE로의 정책 이름 변경은 기본 동작 변경 외에 범위 내입니다.

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.

@LimdaeIl LimdaeIl moved this from Backlog to In progress in WeGo-Together Backend Dec 22, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements approve/reject functionality for group join requests in the V2 group system. It allows group hosts or managers to approve or reject pending user join requests. Additionally, it renames the INSTANT join policy to FREE to better reflect its semantics.

Key changes include:

  • New approve and reject endpoints for managing pending join requests
  • Introduction of error codes for various approval/rejection scenarios
  • Addition of TargetMembership DTO to represent target user's membership information
  • Renaming of INSTANT join policy to FREE across all test files and production code

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/main/java/team/wego/wegobackend/group/v2/presentation/GroupV2Controller.java Adds approve and reject endpoints for attendance management
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2JoinPolicy.java Renames INSTANT to FREE for join policy
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2.java Updates default join policy from INSTANT to FREE
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java Implements approveJoin and rejectJoin domain methods with status validation
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java Implements approve and reject service methods with permission and state validation
src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java New response DTO for approve/reject operations
src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java New DTO representing target user's membership details
src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java Adds new error codes for approval/rejection scenarios
src/test/http/group/v2/v2-group-approve-reject.http New comprehensive HTTP test scenarios for approve/reject functionality
src/test/http/group/v2/*.http Updates test files to use FREE instead of INSTANT join policy

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// 승인제 모임만 가능
if (group.getJoinPolicy() != GroupV2JoinPolicy.APPROVAL_REQUIRED) {
throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The error code GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED expects two parameters in its message format ("모임 ID: %s 참여 방식: %s"), but it is being called with only one parameter (groupId). This will cause a MissingFormatArgumentException at runtime. You need to either add the join policy as a second parameter or update the error message format to only include the groupId.

Suggested change
groupId);
groupId,
group.getJoinPolicy().name());

Copilot uses AI. Check for mistakes.
Comment on lines +278 to +279
throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The error code GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED expects two parameters in its message format ("모임 ID: %s 참여 방식: %s"), but it is being called with only one parameter (groupId). This will cause a MissingFormatArgumentException at runtime. You need to either add the join policy as a second parameter or update the error message format to only include the groupId.

Suggested change
throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId);
throw new GroupException(
GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId,
group.getJoinPolicy().name()
);

Copilot uses AI. Check for mistakes.
Comment on lines 196 to 321
@Transactional
public ApproveRejectGroupV2Response approve(Long approverUserId, Long groupId,
Long targetUserId) {
if (approverUserId == null || targetUserId == null) {
throw new GroupException(GroupErrorCode.USER_ID_NULL);
}

GroupV2 group = groupV2Repository.findById(groupId)
.orElseThrow(
() -> new GroupException(GroupErrorCode.GROUP_NOT_FOUND_BY_ID, groupId));

// 승인제 모임만 가능
if (group.getJoinPolicy() != GroupV2JoinPolicy.APPROVAL_REQUIRED) {
throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId);
}

// 모임 상태 정책
if (group.getStatus() != GroupV2Status.RECRUITING
&& group.getStatus() != GroupV2Status.FULL) {
throw new GroupException(
GroupErrorCode.GROUP_CANNOT_APPROVE_IN_STATUS,
groupId,
group.getStatus().name()
);
}

// 권한 체크: HOST 또는 (그룹 내 MANAGER/… 정책)만 승인 가능
// host는 group.getHost()로 체크
boolean isHost = group.getHost().getId().equals(approverUserId);
boolean canApprove = isHost;

if (!isHost) {
GroupUserV2 approverMembership = groupUserV2Repository.findByGroupIdAndUserId(groupId,
approverUserId)
.orElseThrow(() -> new GroupException(GroupErrorCode.GROUP_USER_NOT_FOUND,
approverUserId));

canApprove = approverMembership.getGroupRole() != null
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER;
}

if (!canApprove) {
throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
approverUserId);
}

GroupUserV2 target = groupUserV2Repository.findByGroupIdAndUserId(groupId, targetUserId)
.orElseThrow(() -> new GroupException(GroupErrorCode.GROUP_USER_NOT_FOUND,
targetUserId));

// PENDING만 승인 가능 (도메인에서 검증)
target.approveJoin();

long attendCount = groupUserV2Repository.countByGroupIdAndStatus(groupId,
GroupUserV2Status.ATTEND);

if (attendCount > group.getMaxParticipants()) {
throw new GroupException(GroupErrorCode.GROUP_IS_FULL, groupId);
}

// FULL 자동 전환
if (attendCount == group.getMaxParticipants()
&& group.getStatus() == GroupV2Status.RECRUITING) {
group.changeStatus(GroupV2Status.FULL);
}

return ApproveRejectGroupV2Response.of(group, attendCount, target);
}

@Transactional
public ApproveRejectGroupV2Response reject(Long approverUserId, Long groupId,
Long targetUserId) {
if (approverUserId == null || targetUserId == null) {
throw new GroupException(GroupErrorCode.USER_ID_NULL);
}

GroupV2 group = groupV2Repository.findById(groupId)
.orElseThrow(
() -> new GroupException(GroupErrorCode.GROUP_NOT_FOUND_BY_ID, groupId));

if (group.getJoinPolicy() != GroupV2JoinPolicy.APPROVAL_REQUIRED) {
throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
groupId);
}

if (group.getStatus() != GroupV2Status.RECRUITING
&& group.getStatus() != GroupV2Status.FULL) {
throw new GroupException(
GroupErrorCode.GROUP_CANNOT_REJECT_IN_STATUS,
groupId,
group.getStatus().name()
);
}

boolean isHost = group.getHost().getId().equals(approverUserId);
boolean canReject = isHost;

if (!isHost) {
GroupUserV2 approverMembership = groupUserV2Repository.findByGroupIdAndUserId(groupId,
approverUserId)
.orElseThrow(() -> new GroupException(GroupErrorCode.GROUP_USER_NOT_FOUND,
approverUserId));

canReject = approverMembership.getGroupRole() != null
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER; // 예시
}

if (!canReject) {
throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
approverUserId);
}

GroupUserV2 target = groupUserV2Repository.findByGroupIdAndUserId(groupId, targetUserId)
.orElseThrow(() -> new GroupException(GroupErrorCode.GROUP_USER_NOT_FOUND,
targetUserId));

target.rejectJoin();

long attendCount = groupUserV2Repository.countByGroupIdAndStatus(groupId,
GroupUserV2Status.ATTEND);

// reject는 ATTEND 수가 바뀌지 않는 게 일반적이지만(대상은 PENDING),
// 혹시라도 상태 정책이 바뀌더라도 count는 최신으로 내려가도록 유지
return ApproveRejectGroupV2Response.of(group, attendCount, target);
}
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

There is significant code duplication between the approve and reject methods. Both methods share identical validation logic for checking join policy, group status, and user permissions. Consider extracting the common validation logic into a private helper method to improve maintainability and reduce duplication. This would make the code easier to maintain and reduce the risk of inconsistencies if validation logic needs to change in the future.

Copilot uses AI. Check for mistakes.
);
}
this.status = GroupUserV2Status.ATTEND;
this.joinedAt = LocalDateTime.now();
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The joinedAt timestamp is being set when approving a join request, but this is semantically incorrect. The joinedAt timestamp was already set when the user initially made the PENDING request. Approving should only change the status to ATTEND and should not modify joinedAt. Only leftAt should be cleared. This could cause confusion when tracking when a user actually requested to join versus when they were approved.

Suggested change
this.joinedAt = LocalDateTime.now();

Copilot uses AI. Check for mistakes.
approverUserId));

canReject = approverMembership.getGroupRole() != null
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER; // 예시
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The inline comment "예시" (example) on line 301 suggests that this is example code rather than production-ready logic. This indicates that the role-based permission logic may not be finalized. Consider removing this comment if the logic is correct, or if it's still under consideration, add a TODO comment to clarify what needs to be decided or implemented.

Suggested change
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER; // 예시
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER; // TODO: 권한 정책 확정 후, REJECT 가능 역할 조건(현재: MEMBER 외 모든 역할)을 재검토할 것

Copilot uses AI. Check for mistakes.
}

if (!canReject) {
throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

The error code NO_PERMISSION_TO_APPROVE_JOIN is used in the reject method, but this error message says "참여 승인/거절 권한이 없습니다" (no permission to approve/reject join). While the message is technically correct for both operations, using an error code named specifically for approving in the reject method is inconsistent and could cause confusion during debugging or error tracking. Consider creating a separate NO_PERMISSION_TO_REJECT_JOIN error code or renaming the existing one to NO_PERMISSION_TO_MANAGE_JOIN_REQUEST to better represent both operations.

Copilot uses AI. Check for mistakes.
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: 5

Caution

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

⚠️ Outside diff range comments (1)
src/test/http/group/v2/v2-group-attend-free.http (1)

52-66: 테스트 설명과 실제 값의 불일치를 수정하세요.

Line 52의 주석과 Line 58의 제목, Line 65의 설명에서 여전히 "INSTANT"를 언급하고 있지만, Line 59에서는 실제로 "FREE"를 사용하고 있습니다. 테스트의 명확성을 위해 모든 참조를 "FREE"로 업데이트해야 합니다.

🔎 제안하는 수정
-### 1-2. 모임 V2 생성 (즉시 참여 INSTANT)
+### 1-2. 모임 V2 생성 (즉시 참여 FREE)
 POST http://localhost:8080/api/v2/groups/create
 Content-Type: application/json
 Authorization: Bearer {{host2AccessToken}}
 
 {
-  "title": "V2 즉시참여(INSTANT) - attend/left/reAttend 테스트",
+  "title": "V2 즉시참여(FREE) - attend/left/reAttend 테스트",
   "joinPolicy": "FREE",
   "location": "서울 서초구",
   "locationDetail": "교대역 1번 출구 근처 카페",
   "startTime": "2026-12-20T19:00:00",
   "endTime": "2026-12-20T21:00:00",
   "tags": ["v2", "attendance", "left", "instant"],
-  "description": "INSTANT: attend 즉시 ATTEND, left는 ATTEND->LEFT, 재참여 가능",
+  "description": "FREE: attend 즉시 ATTEND, left는 ATTEND->LEFT, 재참여 가능",
   "maxParticipants": 5,
🧹 Nitpick comments (4)
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java (1)

182-194: approveJoin 시 joinedAt 타임스탬프 의미 재검토 권장

Line 192에서 joinedAtLocalDateTime.now()로 갱신합니다. 그러나 사용자가 PENDING 상태로 생성될 때 이미 joinedAt이 설정되어 있습니다(Line 132 참조).

비즈니스 요구사항 확인이 필요합니다:

  • 승인 시점을 기록하려면 현재 구현이 적절합니다.
  • 최초 신청 시점을 유지하려면 Line 192를 제거하거나 별도 필드(예: approvedAt)를 추가해야 합니다.

신청 이력 추적이나 분석을 위해 최초 신청 시점 정보가 필요할 수 있습니다.

src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java (1)

22-36: serverTime 타임존 처리 고려 권장

Line 34에서 LocalDateTime.now()를 사용하여 서버 시간을 기록합니다. LocalDateTime은 타임존 정보를 포함하지 않으므로, 다양한 지역의 클라이언트와 통신할 때 혼란을 야기할 수 있습니다.

명시적으로 UTC를 사용하거나 ZonedDateTime을 사용하는 것을 고려하세요:

Instant.now() // UTC 타임스탬프
// 또는
ZonedDateTime.now(ZoneId.of("UTC"))
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java (1)

196-264: approve 메서드의 정원 체크 타이밍 이슈

Line 248에서 target.approveJoin()을 호출하여 상태를 ATTEND로 변경한 후, Line 250-255에서 정원 초과를 확인합니다. 만약 정원이 초과되면 예외가 발생하지만, 이미 상태가 변경되었습니다.

@Transactional로 인해 롤백되지만, 정원 확인을 approveJoin() 호출 전에 수행하는 것이 더 명확하고 안전합니다:

🔎 권장 수정안
+        // 정원 체크를 먼저 수행
+        long currentAttendCount = groupUserV2Repository.countByGroupIdAndStatus(groupId,
+                GroupUserV2Status.ATTEND);
+        if (currentAttendCount >= group.getMaxParticipants()) {
+            throw new GroupException(GroupErrorCode.GROUP_IS_FULL, groupId);
+        }
+
         // PENDING만 승인 가능 (도메인에서 검증)
         target.approveJoin();
 
         long attendCount = groupUserV2Repository.countByGroupIdAndStatus(groupId,
                 GroupUserV2Status.ATTEND);
-
-        if (attendCount > group.getMaxParticipants()) {
-            throw new GroupException(GroupErrorCode.GROUP_IS_FULL, groupId);
-        }
src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java (1)

39-42: 선택적 에러 코드의 필요성을 명확히 해주세요.

주석에 "필요하면(선택)"이라고 명시되어 있어, 이 에러 코드의 필요성이 불명확합니다.

다음 중 하나를 선택해 주세요:

  1. 이 에러 코드가 명확히 필요한 시나리오가 있다면, 주석을 구체적인 사용 사례로 업데이트
  2. 기존 GROUP_USER_NOT_PENDING_STATUS로 충분하다면, 이 에러 코드 제거

불확실한 상태로 에러 코드를 추가하면 향후 일관성 없는 사용으로 이어질 수 있습니다.

이 에러 코드의 사용 시나리오를 명확히 하는 데 도움이 필요하시면 말씀해 주세요.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 505c0a0 and 5bc1b5c.

📒 Files selected for processing (17)
  • src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java
  • src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java
  • src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java
  • src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java
  • src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java
  • src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2.java
  • src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2JoinPolicy.java
  • src/main/java/team/wego/wegobackend/group/v2/presentation/GroupV2Controller.java
  • src/test/http/group/v2/V2-group-left.http
  • src/test/http/group/v2/v2-group-approve-reject.http
  • src/test/http/group/v2/v2-group-attend-free.http
  • src/test/http/group/v2/v2-group-create.http
  • src/test/http/group/v2/v2-group-delete.http
  • src/test/http/group/v2/v2-group-get-list.http
  • src/test/http/group/v2/v2-group-get-me.http
  • src/test/http/group/v2/v2-group-get.http
  • src/test/http/group/v2/v2-group-update.http
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CodeQL analysis (java)
  • GitHub Check: Agent
🔇 Additional comments (16)
src/test/http/group/v2/V2-group-left.http (1)

61-61: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.

src/test/http/group/v2/v2-group-get-list.http (1)

63-63: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.

src/test/http/group/v2/v2-group-get.http (2)

64-64: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.


105-105: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.

src/test/http/group/v2/v2-group-get-me.http (1)

60-60: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.

src/test/http/group/v2/v2-group-delete.http (2)

60-60: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.


155-155: LGTM!

joinPolicy를 "FREE"로 올바르게 업데이트했습니다.

src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2.java (1)

99-99: 이 리뷰 의견은 정정이 필요합니다.

코드베이스 검증 결과, 원본 리뷰의 기본 전제가 정확하지 않습니다:

  • GroupV2JoinPolicy 열거형에 INSTANT 값이 존재하지 않습니다. 현재 열거형은 FREE, APPROVAL_REQUIRED, INVITE_ONLY만 포함합니다.
  • 코드 히스토리에서도 INSTANT 참조가 없습니다.
  • CreateGroupV2Request에서 joinPolicy는 @NotNull로 표시되어 있어 필수 항목이므로 null이 전달될 수 없습니다.

따라서 "INSTANT에서 FREE로의 기본값 변경"은 실제로 발생하지 않았습니다. 99번 줄의 null 체크는 방어적 프로그래밍일 뿐입니다.

Likely an incorrect or invalid review comment.

src/test/http/group/v2/v2-group-create.http (1)

62-62: 테스트 데이터 업데이트 확인

INSTANT에서 FREE로의 joinPolicy 변경이 모든 테스트 케이스에 일관되게 적용되었습니다.

Also applies to: 129-129, 190-190, 281-281

src/test/http/group/v2/v2-group-update.http (1)

63-63: 테스트 데이터 업데이트 확인

joinPolicy 필드가 모든 모임 생성 테스트에 올바르게 추가되었습니다.

Also applies to: 215-215, 300-300

src/test/http/group/v2/v2-group-approve-reject.http (1)

1-202: 승인/거절 워크플로우 테스트 커버리지 우수

새로운 테스트 파일이 다음을 포괄적으로 검증합니다:

  • 정상 승인/거절 플로우
  • 중복 승인/거절 시도
  • 권한 검증
  • 상태별 나가기 동작
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java (1)

196-207: rejectJoin 메서드 구현 확인

거절 로직이 올바르게 구현되었습니다. PENDING 상태만 거절 가능하며, 적절한 타임스탬프 업데이트가 수행됩니다.

src/main/java/team/wego/wegobackend/group/v2/presentation/GroupV2Controller.java (1)

184-194: reject 엔드포인트 구현 확인

거절 엔드포인트가 승인 엔드포인트와 일관된 패턴으로 구현되었습니다.

src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java (2)

86-86: joinPolicy 변경 확인

INSTANT에서 FREE로의 변경이 올바르게 적용되었습니다.


266-321: reject 메서드 전반적 구현 확인

에러 코드 이슈를 제외하면, 거절 로직의 전반적인 구조와 흐름은 적절합니다.

src/main/java/team/wego/wegobackend/group/domain/exception/GroupErrorCode.java (1)

12-42: 미사용 에러 코드를 삭제해주세요.

GROUP_USER_NOT_PENDING_STATUS는 코드에서 정의되었으나 어디서도 사용되지 않습니다. 불필요한 복잡도를 줄이기 위해 제거하거나, 실제로 사용할 장소가 있다면 해당 메서드에서 활용해주세요.

참고: GROUP_USER_STATUS_NOT_ALLOWED_TO_APPROVEGROUP_USER_STATUS_NOT_ALLOWED_TO_REJECT는 승인/거절 메서드에서 각각 사용되고 있으므로 유지가 필요합니다.

Likely an incorrect or invalid review comment.

Comment on lines 14 to 22
public static TargetMembership from(GroupUserV2 gu) {
return new TargetMembership(
gu.getUser().getId(),
gu.getId(),
gu.getStatus(),
gu.getJoinedAt(),
gu.getLeftAt()
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find GroupUserV2 class and check @ManyToOne mapping for user field
fd GroupUserV2 --type f --extension java | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 397


🏁 Script executed:

# Read GroupUserV2 entity class to check the @ManyToOne mapping
cat -n src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupUserV2.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 8454


🏁 Script executed:

# Read GroupUserV2Repository to check for fetch join queries
cat -n src/main/java/team/wego/wegobackend/group/v2/domain/repository/GroupUserV2Repository.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1441


🏁 Script executed:

# Search for usages of TargetMembership.from() method
rg -n "TargetMembership\.from|\.from\(" src/main/java/team/wego/wegobackend/group/v2 -A 3 -B 3

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 18154


🏁 Script executed:

# Check ApproveRejectGroupV2Response to see how target is obtained
cat -n src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1538


🏁 Script executed:

# Check TargetMembership class definition
cat -n src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 918


🏁 Script executed:

# Search for where ApproveRejectGroupV2Response is instantiated
rg -n "ApproveRejectGroupV2Response\." src/main/java/team/wego/wegobackend/group/v2/application -B 5 -A 5

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2674


🏁 Script executed:

# Check GroupV2AttendanceService approve and reject methods to see how target is obtained
cat -n src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java | head -330 | tail -100

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 4790


🏁 Script executed:

# Check the complete approve method
sed -n '230,265p' src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1441


🏁 Script executed:

# Search for all usages of findByGroupIdAndUserId (which doesn't have fetch join)
rg -n "findByGroupIdAndUserId" src/main/java/team/wego/wegobackend/group/v2 -B 5 -A 10

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 14619


🏁 Script executed:

# Check if there are other places where TargetMembership.from() is called
rg -n "TargetMembership\.from" src/main/java/team/wego/wegobackend/group/v2 -B 3 -A 3

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1035


🏁 Script executed:

# Check if there's any way these GroupUserV2 objects could be accessed outside transaction
rg -n "@Transactional|\.findByGroupIdAndUserId" src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 767


GroupUserV2.user 지연 로딩 방지를 위해 fetch join 쿼리 사용 필요

findByGroupIdAndUserId()는 fetch join이 없어서 user 필드가 지연 로딩되며, 16번 줄의 gu.getUser().getId() 호출 시 추가 쿼리가 발생합니다. 현재는 @Transactional 범위 내에서 실행되어 작동하지만, 트랜잭션 외부에서 접근하면 LazyInitializationException이 발생할 수 있습니다.

findByGroupIdAndUserId(Long groupId, Long userId) 메서드에 fetch join을 추가하거나, user를 함께 로드하는 별도 메서드를 생성하세요.

Comment on lines +223 to +241
// 권한 체크: HOST 또는 (그룹 내 MANAGER/… 정책)만 승인 가능
// host는 group.getHost()로 체크
boolean isHost = group.getHost().getId().equals(approverUserId);
boolean canApprove = isHost;

if (!isHost) {
GroupUserV2 approverMembership = groupUserV2Repository.findByGroupIdAndUserId(groupId,
approverUserId)
.orElseThrow(() -> new GroupException(GroupErrorCode.GROUP_USER_NOT_FOUND,
approverUserId));

canApprove = approverMembership.getGroupRole() != null
&& approverMembership.getGroupRole() != GroupUserV2Role.MEMBER;
}

if (!canApprove) {
throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
approverUserId);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

권한 검증 로직 중복

approve와 reject 메서드에서 동일한 권한 검증 로직이 반복됩니다(lines 223-241, 291-307). 이를 별도 메서드로 추출하여 중복을 제거하고 유지보수성을 향상시키세요.

🔎 리팩터링 제안
private void validateApprovalPermission(Long userId, Long groupId, GroupV2 group) {
    boolean isHost = group.getHost().getId().equals(userId);
    if (isHost) {
        return;
    }
    
    GroupUserV2 membership = groupUserV2Repository.findByGroupIdAndUserId(groupId, userId)
            .orElseThrow(() -> new GroupException(
                GroupErrorCode.GROUP_USER_NOT_FOUND, userId));
    
    boolean hasPermission = membership.getGroupRole() != null
            && membership.getGroupRole() != GroupUserV2Role.MEMBER;
    
    if (!hasPermission) {
        throw new GroupException(
            GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId, userId);
    }
}

그런 다음 approve/reject 메서드에서:

validateApprovalPermission(approverUserId, groupId, group);

Also applies to: 291-307

🤖 Prompt for AI Agents
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java
lines 223-241 and 291-307: duplicate permission-check logic should be extracted
into a private helper method; implement a method (e.g.,
validateApprovalPermission(Long userId, Long groupId, GroupV2 group)) that
returns early if user is host, otherwise loads membership via
groupUserV2Repository.findByGroupIdAndUserId(...).orElseThrow(GROUP_USER_NOT_FOUND
with userId), then verifies membership.getGroupRole() is not null and not
GroupUserV2Role.MEMBER and throws NO_PERMISSION_TO_APPROVE_JOIN with groupId and
userId if not; replace the duplicated blocks in both approve and reject with a
single call to this new helper.


public enum GroupV2JoinPolicy {
INSTANT, // 참여 버튼 누르면 즉시 ATTEND
FREE, // 참여 버튼 누르면 즉시 ATTEND
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "GroupV2JoinPolicy.java" 2>/dev/null | head -20

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 153


🏁 Script executed:

# Search for any references to INSTANT in Java files (excluding the main file itself)
rg -n '\bINSTANT\b' --type=java 2>/dev/null | head -50

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 52


🏁 Script executed:

# Search for references to "INSTANT" in test HTTP files
rg -n '"INSTANT"' src/test 2>/dev/null | head -50

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 52


🏁 Script executed:

cat -n ./src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2JoinPolicy.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 367


🏁 Script executed:

# Check for INSTANT in configuration files, JSON, YAML, properties
rg -n 'INSTANT' --type-add 'config:*.{properties,yml,yaml,json,xml}' -tcfg 2>/dev/null || echo "No config files with INSTANT found"

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 104


🏁 Script executed:

# Search for INSTANT in all non-Java files (broader search)
rg -n '\bINSTANT\b' --type-list | grep -v java | head -20
# Then search those types
rg -n '\bINSTANT\b' -g '!*.java' 2>/dev/null | head -50

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 798


🏁 Script executed:

# Verify the enum is correctly used in other classes
rg -n 'GroupV2JoinPolicy\.' --type=java -A 2 2>/dev/null | head -100

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2389


enum 상수 이름 변경 확인

INSTANT에서 FREE로 enum 상수명이 변경되었습니다. Java 코드베이스의 모든 참조가 올바르게 업데이트되었으나, 테스트 파일 src/test/http/group/v2/v2-group-attend-free.http (라인 52, 58, 65)의 주석에서 "INSTANT"에 대한 설명이 여전히 남아있습니다. 이 문서 주석도 "FREE"로 업데이트하세요.

🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/group/v2/domain/entity/GroupV2JoinPolicy.java
line 4 the enum constant was renamed from INSTANT to FREE; update the related
test documentation comments in src/test/http/group/v2/v2-group-attend-free.http
(around lines 52, 58, 65) to replace any remaining "INSTANT" mentions with
"FREE" so the comments match the current enum name.

`TargetMembership` 생성 시 사용자 ID를 입력받도록 수정합니다.

이 변경 사항은 `ApproveRejectGroupV2Response`에서 멤버십 정보와 올바른 사용자 ID가 연결되도록 하여, 그룹 승인 또는 거부 시 사용자 ID가 제대로 설정되지 않던 문제를 해결합니다.
그룹 내에서 사용자가 자신의 가입 요청을 승인하거나 거부하는 것을 방지하는 검사 기능을 구현합니다.

이 개선 사항은 사용자가 자신의 상태를 조작할 수 없도록 하여 그룹 관리 프로세스의 무결성을 유지합니다.
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

♻️ Duplicate comments (5)
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java (5)

223-241: 권한 검증 로직 중복 제거를 권장합니다.

approvereject 메서드 간에 동일한 권한 검증 로직이 중복됩니다 (lines 223-241, 291-307). 별도 헬퍼 메서드로 추출하면 유지보수성이 향상됩니다.

🔎 리팩터링 제안
private void validateApprovalPermission(Long userId, Long groupId, GroupV2 group) {
    boolean isHost = group.getHost().getId().equals(userId);
    if (isHost) {
        return;
    }
    
    GroupUserV2 membership = groupUserV2Repository.findByGroupIdAndUserId(groupId, userId)
            .orElseThrow(() -> new GroupException(
                GroupErrorCode.GROUP_USER_NOT_FOUND, userId));
    
    boolean hasPermission = membership.getGroupRole() != null
            && membership.getGroupRole() != GroupUserV2Role.MEMBER;
    
    if (!hasPermission) {
        throw new GroupException(
            GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId, userId);
    }
}

208-211: 오류 코드 파라미터 누락으로 런타임 예외 발생.

GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED 오류 코드는 메시지 포맷에 2개의 파라미터를 요구하지만 (모임 ID, 참여 방식), Line 210에서는 groupId 하나만 전달하고 있습니다. 이는 런타임에 MissingFormatArgumentException을 발생시킵니다.

🔎 수정 제안
         if (group.getJoinPolicy() != GroupV2JoinPolicy.APPROVAL_REQUIRED) {
             throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
-                    groupId);
+                    groupId,
+                    group.getJoinPolicy().name());
         }

277-280: 오류 코드 파라미터 누락으로 런타임 예외 발생.

approve 메서드와 동일한 문제입니다. GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED 오류 코드는 2개의 파라미터를 요구하지만 Line 279에서는 groupId 하나만 전달하고 있습니다.

🔎 수정 제안
         if (group.getJoinPolicy() != GroupV2JoinPolicy.APPROVAL_REQUIRED) {
             throw new GroupException(GroupErrorCode.GROUP_JOIN_POLICY_NOT_APPROVAL_REQUIRED,
-                    groupId);
+                    groupId,
+                    group.getJoinPolicy().name());
         }

300-301: 권한 정책 확정 여부 확인이 필요합니다.

Line 301의 "예시" 주석은 역할 기반 권한 로직이 아직 최종 확정되지 않았음을 시사합니다. 현재 로직이 정확하다면 주석을 제거하고, 추가 검토가 필요하다면 TODO 주석으로 명확히 해주세요.

🔎 제안
             canReject = approverMembership.getGroupRole() != null
-                    && approverMembership.getGroupRole() != GroupUserV2Role.MEMBER; // 예시
+                    && approverMembership.getGroupRole() != GroupUserV2Role.MEMBER;
+            // TODO: 권한 정책 확정 후, REJECT 가능 역할 조건 재검토

304-306: reject 메서드에서 잘못된 오류 코드 사용.

Line 305에서 NO_PERMISSION_TO_APPROVE_JOIN 오류 코드를 사용하고 있으나, 이는 거절(reject) 작업에 적합하지 않습니다. 별도의 NO_PERMISSION_TO_REJECT_JOIN 오류 코드를 사용하거나, 두 작업 모두에 적용 가능한 일반적인 오류 코드(예: NO_PERMISSION_TO_MANAGE_JOIN_REQUEST)로 변경해야 합니다.

🔎 수정 제안

옵션 1: 별도의 reject 전용 오류 코드 생성

         if (!canReject) {
-            throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
+            throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_REJECT_JOIN, groupId,
                     approverUserId);
         }

옵션 2: 일반적인 오류 코드 사용

         if (!canReject) {
-            throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_APPROVE_JOIN, groupId,
+            throw new GroupException(GroupErrorCode.NO_PERMISSION_TO_MANAGE_JOIN_REQUEST, groupId,
                     approverUserId);
         }

참고: 선택한 오류 코드가 GroupErrorCode에 정의되어 있는지 확인하세요.

🧹 Nitpick comments (1)
src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java (1)

22-37: 서버 시간에 타임존 정보 포함을 고려하세요.

LocalDateTime.now() (Line 35)는 타임존 정보를 포함하지 않아 클라이언트가 다른 시간대에 있을 경우 혼란을 초래할 수 있습니다. API 응답에는 타임존 정보를 포함하는 것이 권장됩니다.

🔎 개선 제안

다음 중 하나를 고려하세요:

방법 1: Instant 사용 (권장)

-import java.time.LocalDateTime;
+import java.time.Instant;

 public record ApproveRejectGroupV2Response(
         Long groupId,
         GroupV2Status groupStatus,
         GroupV2JoinPolicy joinPolicy,
         long participantCount,
         int maxParticipants,
         TargetMembership targetMembership,
-        LocalDateTime serverTime
+        Instant serverTime
 ) {
     public static ApproveRejectGroupV2Response of(
             GroupV2 group,
             long participantCount,
             Long targetUserId,
             GroupUserV2 target
     ) {
         return new ApproveRejectGroupV2Response(
                 group.getId(),
                 group.getStatus(),
                 group.getJoinPolicy(),
                 participantCount,
                 group.getMaxParticipants(),
                 TargetMembership.of(targetUserId, target),
-                LocalDateTime.now()
+                Instant.now()
         );
     }
 }

방법 2: ZonedDateTime 사용

-import java.time.LocalDateTime;
+import java.time.ZonedDateTime;

-        LocalDateTime serverTime
+        ZonedDateTime serverTime

-                LocalDateTime.now()
+                ZonedDateTime.now()
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5bc1b5c and e10e49f.

📒 Files selected for processing (3)
  • src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java
  • src/main/java/team/wego/wegobackend/group/v2/application/dto/response/ApproveRejectGroupV2Response.java
  • src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/team/wego/wegobackend/group/v2/application/dto/common/TargetMembership.java
🔇 Additional comments (1)
src/main/java/team/wego/wegobackend/group/v2/application/service/GroupV2AttendanceService.java (1)

86-86: 정책 이름 변경이 적절합니다.

INSTANT에서 FREE로의 정책 이름 변경이 PR 목표와 일치하며, 즉시 참여를 나타내는 더 명확한 이름입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[FEAT] V2 모임 승인 허가 및 거절 구현

1 participant