Skip to content

[FEAT] 모임 생성 로직 수정#47

Merged
LimdaeIl merged 4 commits intomainfrom
feat/group-create
Dec 9, 2025
Merged

[FEAT] 모임 생성 로직 수정#47
LimdaeIl merged 4 commits intomainfrom
feat/group-create

Conversation

@LimdaeIl
Copy link
Collaborator

@LimdaeIl LimdaeIl commented Dec 9, 2025

📝 Pull Request

📌 PR 종류

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

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

✨ 변경 내용

! 이전 플로우: 그룹 생성 → groupId로 이미지 업로드

  • 새 플로우: 이미지 선 업로드 → URL 들고 그룹 생성
    위 .http는 이 새 구조에 맞춰서:
  1. userId → 이미지 업로드
  2. 업로드 응답에서 URL을 전역 변수로 저장
  3. 그 변수를 CreateGroupRequest.images 에 넣어서 그룹 생성
    까지 한 번에 이어서 테스트할 수 있게 작성했습니다.

🔍 관련 이슈

🧪 테스트

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

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

🚨 확인해야 할 사항 (Checklist)

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

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

🙋 기타 참고 사항

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

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 그룹 이미지 사전 업로드 API 추가 — 메인/썸네일 URL 반환
    • 그룹 생성 시 이미지 URL 배열로 여러 이미지 등록 지원
    • 그룹 상세 조회 API 추가
  • Bug Fixes

    • 그룹 종료 시간(endTime) 필수 제약 완화 (선택 가능)
  • Tests

    • 그룹 생성 흐름 테스트를 사전 이미지 업로드 후 생성하는 다단계 시나리오로 개편

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

@LimdaeIl LimdaeIl self-assigned this Dec 9, 2025
Copilot AI review requested due to automatic review settings December 9, 2025 12:46
@LimdaeIl LimdaeIl added the ✨enhancement New feature or request label Dec 9, 2025
@LimdaeIl LimdaeIl moved this from Backlog to In progress in WeGo-Together Backend Dec 9, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Caution

Review failed

The pull request is closed.

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

모임 이미지를 먼저 사전 업로드한 뒤 이미지 URL을 사용해 모임을 생성하도록 API, DTO, 서비스 및 컨트롤러 흐름을 변경합니다. 그룹 엔티티의 endTime null 제약을 완화하고 관련 테스트 흐름을 2단계(이미지 업로드 → 모임 생성)로 전환합니다.

Changes

코호트 / 파일(s) 변경 요약
새 요청 DTO
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupImageRequest.java
모임 이미지(정렬순서, 메인/썸네일 URL)를 담는 CreateGroupImageRequest 레코드 추가
사전 업로드 응답 DTO들
src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageItemResponse.java, src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageResponse.java
업로드된 이미지의 메인/썸네일 URL 및 정렬 정보를 담는 레코드 추가
생성/조회 응답 DTO 변경
src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java, src/main/java/team/wego/wegobackend/group/application/dto/response/GetGroupResponse.java, src/main/java/team/wego/wegobackend/group/application/dto/response/CreatedBy.java
CreateGroupResponse: joinedCountList<GroupImageItemResponse> images로 변경 및 오버로드된 from 추가; GetGroupResponse 및 별도 CreatedBy 레코드 추가
CreateGroupRequest 변경
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupRequest.java
endTime@NotNull 제거 및 List<CreateGroupImageRequest> images 컴포넌트 추가 (생성 요청에 이미지 URL 배열 포함)
그룹 이미지 서비스 리팩토링
src/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java
기존 createGroupImage(...) 제거, uploadGroupImages(List<MultipartFile>) 추가해 파일→URL 사전 업로드 흐름 구현 (이미지 리사이즈/URL 반환)
그룹 서비스 로직 변경
src/main/java/team/wego/wegobackend/group/application/service/GroupService.java
create(userId, ...)createGroup(hostUserId, ...)로 변경; 이미지 URL 기반 저장 (saveGroupImagesByUrl) 및 CreateGroupResponse에 이미지 목록 포함하도록 로직 재배치
엔티티 변경
src/main/java/team/wego/wegobackend/group/domain/entity/Group.java
end_time 컬럼의 nullable = false 제약 제거 (endTime nullable 허용)
컨트롤러 변경
src/main/java/team/wego/wegobackend/group/presentation/GroupController.java, src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java
GroupController: create 호출명 변경 반영 및 getGroupResponse 엔드포인트 추가(플레이스홀더); GroupImageController: createGroupImageuploadImages로 단순화, 요청은 이미지 파일들만 수신하여 PreUploadGroupImageResponse 반환
테스트 시나리오 업데이트
src/test/http/group/create.http
단일 단계에서 2단계(이미지 사전 업로드 → 이미지 URL을 사용한 모임 생성)로 테스트 재구성 및 여러 이미지 URL 캡처/재사용 흐름 추가

Sequence Diagram

sequenceDiagram
    participant Client
    participant GroupImageCtrl as GroupImageController
    participant GroupImageSvc as GroupImageService
    participant ImageUploadSvc as ImageUploadService
    participant GroupCtrl as GroupController
    participant GroupSvc as GroupService
    participant DB as Database

    rect rgb(200,220,240)
    note over Client,GroupImageSvc: 단계 1 — 이미지 사전 업로드
    Client->>GroupImageCtrl: POST /upload (multipart images)
    GroupImageCtrl->>GroupImageSvc: uploadGroupImages(images)
    loop 각 이미지
        GroupImageSvc->>ImageUploadSvc: uploadAsWebpWithSizes(file, sizes)
        ImageUploadSvc-->>GroupImageSvc: 반환 (mainUrl 440x240, thumbUrl 100x100)
    end
    GroupImageSvc-->>GroupImageCtrl: PreUploadGroupImageResponse(images: [{sort, main, thumb},...])
    GroupImageCtrl-->>Client: 200 OK (이미지 URL 목록)
    end

    rect rgb(220,240,200)
    note over Client,GroupSvc: 단계 2 — 이미지 URL로 모임 생성
    Client->>GroupCtrl: POST /groups (CreateGroupRequest + images URLs)
    GroupCtrl->>GroupSvc: createGroup(hostUserId, request)
    GroupSvc->>DB: Host 조회, Group 저장
    GroupSvc->>GroupSvc: saveGroupImagesByUrl(request.images) -> GroupImage 엔티티 저장
    GroupSvc->>DB: 태그/참여자 연결 저장
    GroupSvc-->>GroupCtrl: CreateGroupResponse (images 포함)
    GroupCtrl-->>Client: 201 Created (모임 + 이미지 정보)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • 집중 검토 포인트:
    • GroupService.createGroup 및 saveGroupImagesByUrl: 이미지 URL 기반 저장, 정렬과 썸네일 매핑 로직
    • GroupImageService.uploadGroupImages: 멀티파트 처리, 이미지 크기 변환/URL 반환 흐름과 에러 처리
    • DTO 변경(요청/응답)으로 인한 컨트롤러-서비스 계약 일관성 점검
    • Group.endTime nullable 변경에 따른 유효성 검사/비즈니스 로직 영향
    • 테스트(create.http) 업데이트가 실제 API 응답 형식과 일치하는지 검증

Possibly related PRs

  • #36 — Group/GroupImage 도메인 및 이미지 저장 처리 관련 변경과 밀접 연관 (모델/저장 규약 의존)
  • #24 — S3/이미지 업로드 기반 및 관련 DTO들(업로드 흐름)의 기반 구현과 연관
  • #40 — ImageUploadService의 사이즈별 업로드 API(uploadAsWebpWithSizes) 사용과 직접 연결

Poem

🐰 햇살에 반짝이는 URL들
사진 먼저 띄우고 모임을 지어,
순서 바꿔 흐름이 춤추네.
토끼 한 마리 박수 쳐, 찰칵! 📸
함께 걷는 길, 이제 더 가볍네.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% 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의 주요 변경사항을 명확하게 요약하고 있습니다. '모임 생성 로직 수정'은 이 PR의 핵심 목표인 그룹 이미지 업로드 순서 변경을 직접적으로 반영합니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 이슈 #46의 주요 목표를 완전히 충족합니다. 이미지 생성을 그룹 생성 전에 수행하도록 로직이 변경되었으며, API 필요 여부 요구사항도 만족됩니다.
Out of Scope Changes check ✅ Passed PR의 변경사항이 모두 이슈 #46의 범위 내에 있습니다. 그룹 생성 로직 수정과 이미지 업로드 순서 변경과 관련된 변경만 포함되어 있습니다.

📜 Recent 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 299cb3b and 8873c3b.

📒 Files selected for processing (1)
  • src/main/java/team/wego/wegobackend/group/application/dto/response/CreatedBy.java (1 hunks)

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

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/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java (1)

75-140: Remove unused saveGroupImages method

This private method is no longer called anywhere in the codebase. The new flow uses GroupService.saveGroupImagesByUrl instead. Delete this method to reduce code clutter.

🧹 Nitpick comments (6)
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupImageRequest.java (1)

3-7: 요청 DTO에 유효성 검증 어노테이션 추가를 권장합니다.

CreateGroupRequest의 다른 필드들처럼 이 요청 DTO에도 유효성 검증이 필요합니다. 이미지 URL이 필수라면 @NotBlank, 정렬 순서가 필수라면 @NotNull 어노테이션을 추가하는 것이 좋습니다.

 package team.wego.wegobackend.group.application.dto.request;

+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
 public record CreateGroupImageRequest(
+        @NotNull(message = "이미지: 정렬 순서는 필수입니다.")
         Integer sortOrder,
+        @NotBlank(message = "이미지: 440x240 URL은 필수입니다.")
         String imageUrl440x240,
+        @NotBlank(message = "이미지: 100x100 URL은 필수입니다.")
         String imageUrl100x100
 ) {}
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupRequest.java (1)

37-39: 중첩 객체 유효성 검증을 위해 @Valid 어노테이션이 필요합니다.

CreateGroupImageRequest에 유효성 검증 어노테이션을 추가하더라도, 리스트 내부 객체들에 대한 검증이 동작하려면 @Valid 어노테이션이 필요합니다.

+        @Valid
         List<CreateGroupImageRequest> images

jakarta.validation.Valid를 import해야 합니다.

src/main/java/team/wego/wegobackend/group/application/service/GroupService.java (1)

177-196: O(n²) 매칭 로직 개선 고려

현재 sorted 리스트를 순회하며 URL로 매칭하고 있습니다. 이미지 수가 최대 3개로 제한되어 있어 성능 이슈는 없지만, 가독성과 유지보수성을 위해 인덱스 기반 매칭을 고려해 보세요.

-        return images.stream()
-                .map(image -> {
-                    // 요청에서 매칭되는 썸네일 URL 찾아오기
-                    CreateGroupImageRequest matched = sorted.stream()
-                            .filter(req -> Objects.equals(
-                                    req.imageUrl440x240(),
-                                    image.getImageUrl()
-                            ))
-                            .findFirst()
-                            .orElse(null);
-
-                    String thumbUrl = matched != null ? matched.imageUrl100x100() : null;
-
-                    return GroupImageItemResponse.from(
-                            image,
-                            image.getImageUrl(), // 440x240
-                            thumbUrl             // 100x100
-                    );
-                })
-                .toList();
+        List<GroupImageItemResponse> result = new ArrayList<>();
+        for (int i = 0; i < images.size(); i++) {
+            GroupImage image = images.get(i);
+            CreateGroupImageRequest req = sorted.get(i);
+            result.add(GroupImageItemResponse.from(
+                    image,
+                    image.getImageUrl(),
+                    req.imageUrl100x100()
+            ));
+        }
+        return result;
src/main/java/team/wego/wegobackend/group/application/dto/response/GetGroupResponse.java (1)

66-66: TODO 주석 확인 필요

joinedCount가 하드코딩되어 있고 TODO 주석이 있습니다. 이 값이 참여 인원수를 나타낸다면 attendUserCount와 동일하게 계산되어야 할 것 같습니다. 의도한 동작이 무엇인지 확인 후 수정이 필요합니다.

이 부분을 구현하는 데 도움이 필요하시면 말씀해 주세요.

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

36-38: 미완성 주석 정리 필요

"모임 이미지 수정" 주석만 있고 구현이 없습니다. 구현 예정이라면 TODO로 표시하거나, 불필요하다면 제거해 주세요.

src/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java (1)

30-35: 중복된 null/empty 검증

validateCreateGroupImageRequest(images)를 호출한 직후 다시 images == null || images.isEmpty() 체크를 수행합니다. 검증 메서드에서 이미 이 케이스를 처리하므로 중복입니다.

     public PreUploadGroupImageResponse uploadGroupImages(List<MultipartFile> images) {
         validateCreateGroupImageRequest(images);

-        if (images == null || images.isEmpty()) {
-            return new PreUploadGroupImageResponse(List.of());
-        }
+        // validateCreateGroupImageRequest에서 null/empty를 허용하므로 여기서도 처리 필요
+        if (images == null || images.isEmpty()) {
+            return new PreUploadGroupImageResponse(List.of());
+        }

또는 validateCreateGroupImageRequest 내에서 빈 응답을 반환하도록 리팩토링하세요.

📜 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 9198add and 299cb3b.

📒 Files selected for processing (12)
  • src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupImageRequest.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupRequest.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java (3 hunks)
  • src/main/java/team/wego/wegobackend/group/application/dto/response/GetGroupResponse.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageItemResponse.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageResponse.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java (2 hunks)
  • src/main/java/team/wego/wegobackend/group/application/service/GroupService.java (3 hunks)
  • src/main/java/team/wego/wegobackend/group/domain/entity/Group.java (1 hunks)
  • src/main/java/team/wego/wegobackend/group/presentation/GroupController.java (3 hunks)
  • src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java (2 hunks)
  • src/test/http/group/create.http (4 hunks)
⏰ 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 (7)
src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageResponse.java (1)

5-7: LGTM!

사전 업로드 이미지 응답을 위한 간결한 래퍼 레코드입니다. 구조가 적절합니다.

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

3-7: LGTM!

이미지 정렬 순서와 두 가지 크기의 URL을 포함하는 명확한 응답 DTO입니다.

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

33-33: LGTM!

groupService.createGroup 메서드 호출이 올바르게 업데이트되었습니다.

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

31-38: LGTM!

팩토리 메서드 오버로딩이 깔끔하게 구현되었습니다. 이미지가 없는 경우 빈 리스트를 기본값으로 사용하는 것도 적절합니다.

src/main/java/team/wego/wegobackend/group/application/service/GroupService.java (1)

105-138: LGTM!

호스트 조회, 그룹 생성, 그룹 사용자 저장, 태그 저장, 이미지 저장의 흐름이 명확하고 트랜잭션 범위 내에서 일관되게 처리됩니다.

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

34-42: The null safety concern is not applicable. The groupTags and users fields in the Group entity are initialized as new ArrayList<>() at declaration (lines 66 and 69), ensuring they will never be null. The stream operations in lines 34-42 are safe and will return empty streams if no records are loaded, not throw a NullPointerException.

Likely an incorrect or invalid review comment.

src/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java (1)

53-56: This concern is not applicable to this code.

The uploadAsWebpWithSizes method returns a list with size equal to widths.size(). Since GROUP_WIDTHS is defined as List.of(440, 100) (always 2 elements) and the method loops for (int i = 0; i < widths.size(); i++) adding one element per iteration, the returned list is guaranteed to contain exactly 2 elements. Therefore, variants.get(0) and variants.get(1) are always safe.

Likely an incorrect or invalid review comment.

Comment on lines 53 to +57
CreatedBy createdByHost = new CreatedBy(
host.getId(),
host.getNickName(),
host.getProfileImage());
host.getProfileImage()
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for CreatedBy record/class definition
rg -n --type=java 'record CreatedBy|class CreatedBy' -A 5

# Also check imports in the file under review
echo "=== Checking imports in CreateGroupResponse.java ==="
head -30 src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1063


🏁 Script executed:

#!/bin/bash
# Search for any mention of CreatedBy in all Java files
echo "=== Searching for CreatedBy anywhere in Java files ==="
rg -n 'CreatedBy' --type=java

# Get the full CreateGroupResponse.java file
echo -e "\n=== Full CreateGroupResponse.java file ==="
cat src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 3197


🏁 Script executed:

#!/bin/bash
# Search for CreatedBy.java file
echo "=== Searching for CreatedBy.java file ==="
find . -name "*CreatedBy*" -type f

# Search in the response package directory
echo -e "\n=== Files in response package ==="
ls -la src/main/java/team/wego/wegobackend/group/application/dto/response/

# Search for nested record/class in any response file
echo -e "\n=== Checking for nested CreatedBy definition ==="
rg -n 'record CreatedBy|class CreatedBy' --type=java -B 2 -A 10

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 777


🏁 Script executed:

#!/bin/bash
# Check GetGroupResponse.java for comparison
echo "=== GetGroupResponse.java ==="
cat src/main/java/team/wego/wegobackend/group/application/dto/response/GetGroupResponse.java

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 2410


CreatedBy 레코드 정의가 누락되었습니다.

CreatedBy는 이 파일뿐만 아니라 GetGroupResponse.java에서도 참조되고 있지만, 현재 코드베이스 어디에도 정의되어 있지 않습니다. 컴파일 오류를 발생시킵니다. CreatedBy 레코드를 생성해야 합니다:

public record CreatedBy(Long id, String nickName, String profileImage) {}

이 레코드는 같은 패키지(team.wego.wegobackend.group.application.dto.response)에 정의하거나, 다른 위치에 정의 후 import 문을 추가해야 합니다.

🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java
around lines 53 to 57, the code references a CreatedBy record that is not
defined causing compilation errors; add a record declaration public record
CreatedBy(Long id, String nickName, String profileImage) {} in the same package
(team.wego.wegobackend.group.application.dto.response) — either add it to this
file or create a new CreatedBy.java in that package and, if placed elsewhere,
add the appropriate import to CreateGroupResponse.java (and similarly ensure
GetGroupResponse.java can access it).

Comment on lines +46 to 47
@Column(name = "end_time")
private LocalDateTime endTime;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for endTime usage across the codebase (excluding Group.java)
rg -n --type=java -C3 'endTime|end_time' --glob '!**/Group.java' | head -200

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 6290


🏁 Script executed:

#!/bin/bash
# Check CreateGroupRequest definition
cat src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupRequest.java

# Also search for any sorting or filtering operations on endTime
rg -n --type=java 'endTime.*sort|OrderBy.*endTime|endTime.*query|\.endTime' | head -100

Repository: WeGo-Together/WeGo_BackEnd

Length of output: 1928


endTime nullable change introduces NullPointerException risk in validation logic.

endTime in CreateGroupRequest lacks @NotNull annotation (only @Future is present), which means it can be null. However, GroupService.validateCreateGroupRequest() at line 44 calls request.endTime().isAfter(request.startTime()) without null checking. Since @Future validation allows null values, a null endTime will bypass validation and trigger a NullPointerException when the comparison executes.

Add @NotNull annotation to endTime in CreateGroupRequest or implement null-safe handling in the validation method before calling .isAfter().

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/group/domain/entity/Group.java around
lines 46-47, the DTO's endTime can be null and causes NPE in
GroupService.validateCreateGroupRequest() when calling
request.endTime().isAfter(...); fix by adding @NotNull to the endTime field in
CreateGroupRequest (import javax.validation.constraints.NotNull) so Bean
Validation rejects nulls before service logic runs, and run/update any
validation tests; alternatively, if you prefer defensive coding, add a null
check in GroupService.validateCreateGroupRequest() before calling .isAfter() and
return/throw a clear validation exception when endTime is null.

Comment on lines +40 to +48
// 모임 상세 조회
@PostMapping("/{groupId}")
public ResponseEntity<ApiResponse<GetGroupResponse>> getGroupResponse(
@PathVariable Long groupId
) {
// GetGroupResponse response = groupService.getGroup(groupId);

return ResponseEntity.noContent().build();
}
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

HTTP 메서드가 잘못되었습니다.

getGroupResponse는 조회 작업이므로 @PostMapping 대신 @GetMapping을 사용해야 합니다. 또한 204 No Content 응답은 조회 작업에 적합하지 않습니다.

-    // 모임 상세 조회
-    @PostMapping("/{groupId}")
-    public ResponseEntity<ApiResponse<GetGroupResponse>> getGroupResponse(
-            @PathVariable Long groupId
-    ) {
-//        GetGroupResponse response = groupService.getGroup(groupId);
-
-        return ResponseEntity.noContent().build();
-    }
+    // 모임 상세 조회
+    @GetMapping("/{groupId}")
+    public ResponseEntity<ApiResponse<GetGroupResponse>> getGroupResponse(
+            @PathVariable Long groupId
+    ) {
+        GetGroupResponse response = groupService.getGroup(groupId);
+        return ResponseEntity.ok(ApiResponse.success(response));
+    }

미구현 상태라면 해당 엔드포인트를 제거하거나 별도 PR에서 구현하는 것을 권장합니다.

🤖 Prompt for AI Agents
In src/main/java/team/wego/wegobackend/group/presentation/GroupController.java
around lines 40 to 48, the endpoint is declared with @PostMapping and returns
204 No Content but it represents a read operation; change the mapping to
@GetMapping("/{groupId}") and return an appropriate ResponseEntity with the
GetGroupResponse body (e.g., ResponseEntity.ok(response)) after implementing the
service call, or if the handler is not yet implemented remove the method
entirely or move it to a follow-up PR until the retrieval logic is added.

Comment on lines +24 to 29
@PostMapping("/upload")
public ResponseEntity<ApiResponse<PreUploadGroupImageResponse>> uploadImages(
@RequestParam("images") List<MultipartFile> images
) {
CreateGroupImageResponse response = groupImageService.createGroupImage(userId, groupId,
PreUploadGroupImageResponse response = groupImageService.uploadGroupImages(
images);
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

userId 쿼리 파라미터 불일치

테스트 파일(create.http)에서는 ?userId={{userId}}를 전달하지만, 이 컨트롤러에서는 해당 파라미터를 받지 않습니다. 의도적으로 제거한 것이라면 테스트 파일도 업데이트해 주세요. 향후 사용자 인증이 필요하다면 Spring Security나 세션 기반 인증을 고려하세요.

🤖 Prompt for AI Agents
In
src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java
around lines 24-29, the controller method lacks the userId request parameter
while tests send ?userId={{userId}}; either add a request parameter to the
controller signature (e.g., add @RequestParam("userId") Long userId) and forward
it to the service call as required, or update the test create.http to stop
sending userId if it is intentionally unused; choose the approach consistent
with intended behavior (if authentication will be added later, prefer removing
test param or switch tests to authenticate properly).

Comment on lines +155 to +179
### 3. 모임 이미지 선 업로드 (4장 → 예외 발생)
POST http://localhost:8080/api/v1/groups/images/upload?userId={{userId}}
Content-Type: multipart/form-data; boundary=boundary

--boundary--
--boundary
Content-Disposition: form-data; name="images"; filename="test-webp1.webp"
Content-Type: image/webp

< ../image/resources/test-webp1.webp
--boundary--
Content-Disposition: form-data; name="images"; filename="test-webp1.webp"
--boundary
Content-Disposition: form-data; name="images"; filename="test-webp2.webp"
Content-Type: image/webp

< ../image/resources/test-webp1.webp
--boundary--
< ../image/resources/test-webp2.webp
--boundary
Content-Disposition: form-data; name="images"; filename="img1.png"
Content-Type: image/png

< ../image/resources/img1.png
--boundary--
--boundary
Content-Disposition: form-data; name="images"; filename="img2.jpg"
Content-Type: image/jpeg

< ../image/resources/img2.jpg
--boundary--

--boundary-- No newline at end of file
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

멀티파트 boundary 형식 불일치

테스트 1-1과 2-1에서는 --boundary--를 사용하지만, 테스트 3에서는 --boundary를 사용합니다. RFC 2046에 따르면 각 파트는 --boundary로 시작하고, 마지막 파트만 --boundary--로 끝나야 합니다. 현재 테스트 1-1과 2-1의 형식이 잘못되었을 수 있습니다.

올바른 형식 예시:

--boundary
Content-Disposition: form-data; name="images"; filename="img1.png"
...
--boundary
Content-Disposition: form-data; name="images"; filename="img2.jpg"
...
--boundary--
🤖 Prompt for AI Agents
In src/test/http/group/create.http around lines 155-179, the multipart/form-data
examples use inconsistent boundary markers (some parts end with `--boundary--`
prematurely while others use `--boundary`), violating RFC 2046; fix by ensuring
every part separator uses `--boundary` and only the final closing boundary uses
`--boundary--` (update the affected test cases so each part starts with
`--boundary` and append a single `--boundary--` after the last file).

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 refactors the group creation flow from a two-step process (create group → upload images) to a more streamlined approach (upload images → create group with URLs). The change allows clients to first upload images and receive URLs, then include those URLs when creating a group, improving the API design and error handling.

Key Changes:

  • New pre-upload endpoint for group images that returns URLs before group creation
  • Modified group creation to accept image URLs in the request payload
  • Made endTime optional in the Group entity schema

Reviewed changes

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

Show a summary per file
File Description
src/main/java/team/wego/wegobackend/group/presentation/GroupImageController.java Refactored to provide pre-upload endpoint without requiring groupId
src/main/java/team/wego/wegobackend/group/presentation/GroupController.java Added placeholder for group detail retrieval endpoint; renamed service method
src/main/java/team/wego/wegobackend/group/application/service/GroupService.java Integrated image URL handling into group creation logic with new helper method
src/main/java/team/wego/wegobackend/group/application/service/GroupImageService.java Simplified to provide pre-upload functionality without group context
src/main/java/team/wego/wegobackend/group/domain/entity/Group.java Changed endTime to nullable to support optional end times
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupRequest.java Added images field to accept pre-uploaded image URLs
src/main/java/team/wego/wegobackend/group/application/dto/request/CreateGroupImageRequest.java New DTO to represent image URL data in group creation requests
src/main/java/team/wego/wegobackend/group/application/dto/response/CreateGroupResponse.java Updated to include images in response and removed joinedCount field
src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageResponse.java New DTO for pre-upload image response wrapper
src/main/java/team/wego/wegobackend/group/application/dto/response/PreUploadGroupImageItemResponse.java New DTO for individual pre-uploaded image metadata
src/main/java/team/wego/wegobackend/group/application/dto/response/GetGroupResponse.java New response DTO for group detail retrieval
src/test/http/group/create.http Updated test scenarios to follow new flow: upload images first, then create group with URLs

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


// 나의 모임 목록 조회

//
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Empty or incomplete comments like "//" should be removed. If these are placeholders for future implementations, they should contain meaningful descriptions of what will be implemented.

Suggested change
//

Copilot uses AI. Check for mistakes.

// 모임 이미지 수정


Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Empty comment lines serve no purpose and reduce code readability. These should be removed.

Suggested change

Copilot uses AI. Check for mistakes.
LocalDateTime startTime,

@NotNull(message = "모임: 종료 시간은 필수 입니다.")
@Future(message = "모임: 종료 시간은 현재 이후여야 합니다.")
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The validation annotation @NotNull was removed from endTime, but it's still present in the entity with nullable = false in the schema. However, the diff shows the entity changed to @Column(name = "end_time") without nullable = false. This inconsistency should be clarified - if endTime is optional, the validation annotation @Future should also be updated to allow null values using @Future only when the value is present, or remove it entirely if null is acceptable.

Suggested change
@Future(message = "모임: 종료 시간은 현재 이후여야 합니다.")

Copilot uses AI. Check for mistakes.
@Max(value = 12, message = "모임: 최대 인원은 최대 12명 이하이어야 합니다.")
Integer maxParticipants
Integer maxParticipants,

Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The images field in the request DTO lacks validation annotations. Consider adding validation to ensure data integrity:

@Size(max = 3, message = "모임: 이미지는 최대 3개까지 업로드 가능합니다.")
List<CreateGroupImageRequest> images

Additionally, CreateGroupImageRequest itself should have validation on its fields (e.g., @NotBlank for URLs, validation for sortOrder range).

Suggested change
@Size(max = 3, message = "모임: 이미지는 최대 3개까지 업로드 가능합니다.")

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +5

public record CreateGroupImageRequest(
Integer sortOrder,
String imageUrl440x240,
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The CreateGroupImageRequest record lacks validation for its fields. Since these URLs will be stored in the database, add appropriate validation:

public record CreateGroupImageRequest(
        @Min(value = 0, message = "정렬 순서는 0 이상이어야 합니다.")
        Integer sortOrder,
        @NotBlank(message = "메인 이미지 URL은 필수입니다.")
        @Pattern(regexp = "^https?://.*", message = "유효한 URL 형식이어야 합니다.")
        String imageUrl440x240,
        @NotBlank(message = "썸네일 이미지 URL은 필수입니다.")
        @Pattern(regexp = "^https?://.*", message = "유효한 URL 형식이어야 합니다.")
        String imageUrl100x100
) {}
Suggested change
public record CreateGroupImageRequest(
Integer sortOrder,
String imageUrl440x240,
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
public record CreateGroupImageRequest(
@Min(value = 0, message = "정렬 순서는 0 이상이어야 합니다.")
Integer sortOrder,
@NotBlank(message = "메인 이미지 URL은 필수입니다.")
@Pattern(regexp = "^https?://.*", message = "유효한 URL 형식이어야 합니다.")
String imageUrl440x240,
@NotBlank(message = "썸네일 이미지 URL은 필수입니다.")
@Pattern(regexp = "^https?://.*", message = "유효한 URL 형식이어야 합니다.")

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +50
CreatedBy createdByHost = new CreatedBy(
host.getId(),
host.getNickName(),
host.getProfileImage());
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The CreatedBy record class is referenced but not defined. This will cause a compilation error. The class definition appears to have been accidentally removed from this file.

Copilot uses AI. Check for mistakes.
@RequestPart(value = "images", required = false) List<MultipartFile> images
@PostMapping("/upload")
public ResponseEntity<ApiResponse<PreUploadGroupImageResponse>> uploadImages(
@RequestParam("images") List<MultipartFile> images
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The @RequestParam("images") annotation is incorrect for List<MultipartFile>. For multipart file uploads, files are automatically bound from the request without needing to specify the parameter name. The correct approach is to use @RequestPart or simply @RequestParam without a value:

@RequestParam List<MultipartFile> images

or

@RequestPart(value = "images", required = false) List<MultipartFile> images

The current annotation will cause issues with file upload binding.

Copilot uses AI. Check for mistakes.
private final GroupImageService groupImageService;

private void validateCreateGroupRequest(CreateGroupRequest request) {
if (!request.endTime().isAfter(request.startTime())) {
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The validation logic in validateCreateGroupRequest checks if endTime is after startTime, but since endTime is now nullable (based on the entity change), this method will throw a NullPointerException if endTime is null. Add a null check:

if (request.endTime() != null && !request.endTime().isAfter(request.startTime())) {
    throw new GroupException(GroupErrorCode.INVALID_TIME_RANGE);
}
Suggested change
if (!request.endTime().isAfter(request.startTime())) {
if (request.endTime() != null && !request.endTime().isAfter(request.startTime())) {

Copilot uses AI. Check for mistakes.
}

// 모임 상세 조회
@PostMapping("/{groupId}")
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

Using @PostMapping for a retrieval operation is incorrect. This endpoint should use @GetMapping instead since it's fetching group details, not creating or modifying data. POST requests should be used for operations that create or modify resources, while GET should be used for read operations.

Suggested change
@PostMapping("/{groupId}")
@GetMapping("/{groupId}")

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +47
// GetGroupResponse response = groupService.getGroup(groupId);

return ResponseEntity.noContent().build();
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

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

The endpoint returns ResponseEntity.noContent().build() which produces a 204 No Content response, but the return type is ResponseEntity<ApiResponse<GetGroupResponse>>. This is inconsistent. Either:

  1. Remove or comment out this entire method until it's implemented, or
  2. Return a proper response matching the declared return type

The current implementation is misleading and will cause confusion for API consumers.

Suggested change
// GetGroupResponse response = groupService.getGroup(groupId);
return ResponseEntity.noContent().build();
GetGroupResponse response = groupService.getGroup(groupId);
return ResponseEntity.ok(ApiResponse.success(response));

Copilot uses AI. Check for mistakes.
@LimdaeIl LimdaeIl merged commit cc9a74c into main Dec 9, 2025
1 check was pending
@LimdaeIl LimdaeIl deleted the feat/group-create branch December 9, 2025 12:59
@github-project-automation github-project-automation bot moved this from In progress to Done in WeGo-Together Backend Dec 9, 2025
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] 모임 생성 로직 수정

1 participant