Skip to content

Conversation

@stoneTiger0912
Copy link
Member

@stoneTiger0912 stoneTiger0912 commented Feb 9, 2026

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 그룹 정보 조회 기능 추가: 그룹 ID를 통해 그룹의 상세 정보를 조회할 수 있습니다.
    • 그룹 정보 수정 기능 추가: 그룹명, 카테고리, 설명, 공개 범위, 최대 멤버 수 등 그룹 정보를 수정할 수 있습니다.

@stoneTiger0912 stoneTiger0912 self-assigned this Feb 9, 2026
@stoneTiger0912 stoneTiger0912 reopened this Feb 9, 2026
@stoneTiger0912 stoneTiger0912 changed the title Feat: [FN-297] 그룹 생성 API 구현 Feat: [FN-297] 그룹 조회 API 구현 Feb 9, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2026

Warning

Rate limit exceeded

@stoneTiger0912 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 34 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.

Walkthrough

그룹 관리 기능 확장: 그룹 변경 및 조회 엔드포인트 추가, 새로운 UseCase 및 Service 구현, CreateGroupCommand에 userId 포함, 도메인 모델에 타임스탬프 필드 및 메서드 추가.

Changes

Cohort / File(s) Summary
Web Controller
src/main/java/flipnote/group/adapter/in/web/GroupController.java
PUT /{groupId}와 GET /{groupId} 엔드포인트 추가, ChangeGroupUseCase 및 FindGroupUseCase 주입, CreateGroupCommand에 userId 포함하여 생성 흐름 조정.
Use Cases & Interfaces
src/main/java/flipnote/group/application/port/in/ChangeGroupUseCase.java, src/main/java/flipnote/group/application/port/in/FindGroupUseCase.java, src/main/java/flipnote/group/application/port/in/CreateGroupUseCase.java
새로운 ChangeGroupUseCase, FindGroupUseCase 인터페이스 정의, CreateGroupUseCase 시그니처 변경 (userId 제거).
Commands
src/main/java/flipnote/group/application/port/in/command/ChangeGroupCommand.java, src/main/java/flipnote/group/application/port/in/command/FindGroupCommand.java, src/main/java/flipnote/group/application/port/in/command/CreateGroupCommand.java
새로운 ChangeGroupCommand, FindGroupCommand 레코드 추가, CreateGroupCommand에 userId 필드 추가.
Results
src/main/java/flipnote/group/application/port/in/result/ChangeGroupResult.java, src/main/java/flipnote/group/application/port/in/result/FindGroupResult.java
새로운 ChangeGroupResult, FindGroupResult 레코드 추가.
DTOs
src/main/java/flipnote/group/api/dto/request/ChangeGroupRequestDto.java, src/main/java/flipnote/group/api/dto/response/ChangeGroupResponseDto.java, src/main/java/flipnote/group/api/dto/response/FindGroupResponseDto.java
요청/응답 DTO 추가, 검증 제약사항 포함, from() 정적 팩토리 메서드 구현.
Service Implementations
src/main/java/flipnote/group/application/service/ChangeGroupService.java, src/main/java/flipnote/group/application/service/FindGroupService.java, src/main/java/flipnote/group/application/service/CreateGroupService.java
새로운 ChangeGroupService, FindGroupService 구현, CreateGroupService 시그니처 업데이트.
Domain Model
src/main/java/flipnote/group/domain/model/group/Group.java
createdAt, modifiedAt 필드 추가, getGroup() → restore() 메서드명 변경, @Setter 제거.
Persistence Layer
src/main/java/flipnote/group/adapter/out/entity/GroupEntity.java, src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java, src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMapper.java, src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.java
GroupEntity에 change() 메서드 추가, GroupRepositoryAdapter에 findById(), update() 메서드 추가, GroupMapper에 toDomain() 메서드 추가, GroupRepositoryPort에 새 메서드 정의.

Sequence Diagram

sequenceDiagram
    participant Client
    participant GroupController
    participant FindGroupService
    participant GroupRepositoryPort
    participant GroupMapper
    
    Client->>GroupController: GET /{groupId}
    GroupController->>FindGroupService: findGroup(FindGroupCommand)
    FindGroupService->>GroupRepositoryPort: findById(groupId)
    GroupRepositoryPort-->>FindGroupService: Optional<GroupEntity>
    FindGroupService->>GroupMapper: toDomain(GroupEntity)
    GroupMapper-->>FindGroupService: Group
    FindGroupService-->>GroupController: FindGroupResult
    GroupController-->>Client: ResponseEntity<FindGroupResponseDto>
Loading
sequenceDiagram
    participant Client
    participant GroupController
    participant ChangeGroupService
    participant GroupRepositoryPort
    participant GroupMapper
    
    Client->>GroupController: PUT /{groupId}
    GroupController->>ChangeGroupService: change(ChangeGroupCommand)
    ChangeGroupService->>GroupRepositoryPort: findById(groupId)
    GroupRepositoryPort-->>ChangeGroupService: Optional<GroupEntity>
    ChangeGroupService->>GroupMapper: toDomain(GroupEntity)
    GroupMapper-->>ChangeGroupService: Group
    ChangeGroupService->>GroupRepositoryPort: update(Group, GroupEntity)
    GroupRepositoryPort-->>ChangeGroupService: Group
    ChangeGroupService-->>GroupController: ChangeGroupResult
    GroupController-->>Client: ResponseEntity<ChangeGroupResponseDto>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • Feat: [FN-297] 그룹 생성 API 구현  #4: 동일 컨트롤러, 엔티티, 리포지토리 어댑터, 매퍼에 대한 확장 수정으로, find/change 작업 추가 및 create use-case 시그니처 변경을 포함하고 있어 코드 레벨에서 강하게 관련됨.

Poem

🐰 그룹을 찾고 바꾸는 길,
새로운 엔드포인트들이 춤을 춘다네.
타임스탐프는 이제 기억하고,
도메인 모델이 더욱 강해지고.
각 계층이 조화롭게 노래 부르니,
플립노트의 그룹 관리가 한층 빛난다! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목 '그룹 조회 API 구현'은 Pull Request의 주요 변경 사항과 부분적으로 관련이 있습니다. FindGroup 기능 구현을 포함하고 있지만, 실제 변경 사항은 그룹 조회(Find) 뿐만 아니라 그룹 수정(Change) 기능도 함께 포함하고 있어 제목이 전체 범위를 완전히 반영하지 못합니다.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/find-group

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

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

🤖 Fix all issues with AI agents
In `@src/main/java/flipnote/group/adapter/out/entity/GroupEntity.java`:
- Around line 81-94: The change() method on GroupEntity currently accepts and
assigns memberCount which can unintentionally overwrite the persisted count;
remove memberCount from the change(String name, Category category, String
description, JoinPolicy joinPolicy, Visibility visibility, Integer maxMember,
Long imageRefId) parameter list and stop assigning this.memberCount inside
GroupEntity.change so the existing memberCount remains unchanged; update all
callers (places invoking GroupEntity.change and any tests) to stop passing
memberCount (they should use ChangeGroupRequestDto/ChangeGroupCommand which do
not include memberCount) or adapt to the new signature.

In
`@src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java`:
- Around line 31-34: The adapter currently exposes persistence type by returning
Optional<GroupEntity> from GroupRepositoryAdapter.findById; change this method
to return Optional<Group> (the port's expected type) and map the result inside
the adapter by transforming GroupEntity to the domain Group (e.g.,
groupRepository.findById(id).map(entity -> entity.toDomain()) or via a
GroupMapper.toDomain(entity)); do not change the GroupRepositoryPort
signature—perform the mapping within GroupRepositoryAdapter and return
Optional.empty()/mapped Optional<Group> as appropriate.

In `@src/main/java/flipnote/group/api/dto/response/FindGroupResponseDto.java`:
- Around line 11-29: FindGroupResponseDto is missing the group's id and
memberCount fields; add Long id and Integer memberCount to the record signature
and update the static factory/mapper method from(...) to populate these new
fields from the Group entity (use Group.getId() and Group.getMemberCount() or
equivalent), and then update any constructor/usage sites to pass the new values;
ensure names are FindGroupResponseDto and from so callers compile.

In `@src/main/java/flipnote/group/application/port/out/GroupRepositoryPort.java`:
- Around line 5-13: The port interface GroupRepositoryPort currently depends on
adapter layer entity GroupEntity; change its API to use only the domain model by
making findById return Optional<Group> (not Optional<GroupEntity>) and remove
the GroupEntity parameter from update so update accepts only a Group; move any
entity-to-domain mapping and entity lookup/update responsibility into the
adapter (e.g., GroupRepositoryAdapter) where you should call
GroupMapper.toDomain() and perform entity retrieval/modification internally.

In `@src/main/java/flipnote/group/application/service/ChangeGroupService.java`:
- Around line 30-41: change(ChangeGroupCommand cmd) 현재 domain 객체를 그대로 update에 넘겨
cmd의 변경값이 반영되지 않으므로, GroupMapper.toDomain(groupEntity)로 만든 domainGroup에
ChangeGroupCommand의 필드(예: name, category, description 등)를 적용한 후
groupRepository.update를 호출하도록 수정하세요; 구체적으로는 ChangeGroupCommand의 각
nullable/optional 필드를 확인해 domainGroup의 변경 메서드(예: setName/changeName, setCategory
등)나 새로운 도메인 생성자를 사용해 값을 덮어쓰고 나서 groupRepository.update(domainGroup,
groupEntity)을 호출하도록 변경하여 변경사항이 DB에 반영되게 만드세요.
- Around line 8-9: ChangeGroupService is importing adapter types (GroupEntity,
GroupMapper) because GroupRepositoryPort exposes adapter types; refactor the
port to expose only domain types (replace Optional<GroupEntity> with
Optional<Group>, change update(Group, GroupEntity) to update(Group) returning
Group or id), then update the adapter implementation to perform mapping between
Group and GroupEntity internally (use GroupMapper inside the adapter, not the
service). After that remove GroupEntity and GroupMapper imports from
ChangeGroupService and adjust its calls to use the revised GroupRepositoryPort
methods (findById(Long) -> Optional<Group>, update(Group) -> Group or id).

In `@src/main/java/flipnote/group/application/service/FindGroupService.java`:
- Around line 7-8: FindGroupService currently depends on adapter types because
GroupRepositoryPort returns Optional<GroupEntity> and the service imports
GroupEntity and GroupMapper; change the port signature so GroupRepositoryPort
returns Optional<Group> (domain model) and move the mapping from
GroupEntity->Group into the adapter implementation (GroupRepositoryAdapter) so
FindGroupService no longer imports GroupEntity/GroupMapper; also mark the
service read-only method with `@Transactional`(readOnly = true) on the method (or
class) that performs the find operation. Ensure the unique symbols updated are
GroupRepositoryPort (return type), GroupRepositoryAdapter (implement mapping),
and FindGroupService (remove adapter imports and add `@Transactional`(readOnly =
true)).
🧹 Nitpick comments (9)
src/main/java/flipnote/group/application/port/in/command/ChangeGroupCommand.java (1)

7-17: maxMember 타입이 ChangeGroupRequestDtoInteger와 불일치합니다.

ChangeGroupRequestDto에서는 Integer (wrapper)를 사용하지만, 이 커맨드에서는 int (primitive)를 사용합니다. DTO에서 @NotNull 검증이 있어 실제 NPE 가능성은 낮지만, 일관성을 위해 타입을 맞추는 것을 권장합니다. CreateGroupCommand도 동일한 불일치가 있습니다.

src/main/java/flipnote/group/adapter/out/persistence/mapper/GroupMapper.java (1)

10-12: @Componentstatic 메서드가 혼용되어 있습니다.

모든 메서드가 static이므로 @Component 어노테이션과 Spring 빈 등록이 불필요합니다. 유틸리티 클래스로 사용하려면 @Component를 제거하고, Spring 빈으로 관리하려면 static을 제거하고 인스턴스 메서드로 변경하세요.

♻️ 유틸리티 클래스로 통일하는 경우
-@Component
 `@NoArgsConstructor`(access = AccessLevel.PRIVATE)
 public class GroupMapper {
src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java (1)

40-54: update 메서드: 명시적 save() 호출 없이 JPA dirty checking에 의존하고 있습니다.

groupEntity.change(...) 호출 후 groupRepository.save()를 명시적으로 호출하지 않고 JPA dirty checking에 의존하고 있습니다. @Transactional 범위 내에서 동작하긴 하지만, 명시적인 save() 호출이 의도를 더 명확하게 전달합니다.

또한, update(Group group, GroupEntity groupEntity) 시그니처에서 GroupEntity를 파라미터로 받는 것은 port 계층의 추상화를 약화시킵니다. 내부적으로 ID 기반 조회 후 변경하는 방식으로 리팩토링을 고려해 주세요.

src/main/java/flipnote/group/application/service/FindGroupService.java (2)

27-37: 읽기 전용 트랜잭션 어노테이션 누락 및 userId 미사용.

  1. 조회 서비스이므로 @Transactional(readOnly = true)를 추가하면 DB 커넥션 최적화(읽기 전용 라우팅, 플러시 모드 비활성화 등)에 도움이 됩니다.
  2. FindGroupCommand에 포함된 userId가 메서드 내에서 전혀 사용되지 않습니다. 그룹 조회 시 가시성(Visibility) 검증이나 권한 확인이 필요하다면 반영이 필요하고, 불필요하다면 커맨드에서 제거하는 것이 좋습니다.
제안: `@Transactional`(readOnly = true) 추가
+import org.springframework.transaction.annotation.Transactional;
+
 `@Service`
 `@RequiredArgsConstructor`
 public class FindGroupService implements FindGroupUseCase {
 
 	private final GroupRepositoryPort groupRepository;
 
 	`@Override`
+	`@Transactional`(readOnly = true)
 	public FindGroupResult findGroup(FindGroupCommand cmd) {

30-32: IllegalArgumentException 대신 도메인 전용 예외 사용을 권장합니다.

IllegalArgumentException은 범용 예외로, 글로벌 예외 핸들러에서 HTTP 상태 코드 매핑 시 다른 유효성 검증 실패와 구분하기 어렵습니다. GroupNotFoundException 같은 도메인 전용 예외를 정의하면 404 응답과 명확하게 매핑할 수 있습니다.

src/main/java/flipnote/group/api/dto/response/ChangeGroupResponseDto.java (1)

11-45: 응답 DTO에 groupId가 포함되어 있지 않습니다.

그룹 수정 응답에 groupId가 빠져 있으면 클라이언트가 어떤 그룹이 수정되었는지 식별하기 어렵습니다. id 필드를 추가하는 것을 고려해 주세요.

제안: groupId 필드 추가
 public record ChangeGroupResponseDto(
+	Long groupId,
+
 	String name,
 	// ... 기존 필드
 ) {
 	public static ChangeGroupResponseDto from(ChangeGroupResult result) {
 		Group group = result.group();
 		return new ChangeGroupResponseDto(
+			group.getId(),
 			group.getName(),
 			// ... 기존 매핑
 		);
 	}
 }
src/main/java/flipnote/group/domain/model/group/Group.java (2)

66-92: restore() 메서드의 파라미터가 11개로, 가독성과 유지보수성이 낮아질 수 있습니다.

향후 필드가 추가되면 파라미터가 더 늘어날 가능성이 높습니다. 파라미터 객체 패턴(예: GroupSnapshot record)이나 Builder 패턴 도입을 검토해 보시면 좋겠습니다.


28-29: create()createdAt/modifiedAt가 설정되지 않습니다.

JPA 엔티티의 @CreatedDate/@LastModifiedDate로 관리된다면 동작상 문제는 없지만, create() 직후 도메인 객체의 타임스탬프가 null인 점은 인지해 주세요. 필요 시 create() 내에서 LocalDateTime.now()를 설정하면 도메인 모델의 자기 완결성이 높아집니다.

Also applies to: 36-51

src/main/java/flipnote/group/adapter/in/web/GroupController.java (1)

42-61: 그룹 생성 API 응답 코드 200 OK201 Created 변경을 권장합니다.

기존 코드이긴 하나, 리소스 생성 시에는 REST 관례상 ResponseEntity.status(HttpStatus.CREATED).body(res) 또는 ResponseEntity.created(uri).body(res)가 더 적절합니다.

Comment on lines 31 to 34
@Override
public Optional<GroupEntity> findById(Long id) {
return groupRepository.findById(id);
}
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

findByIdOptional<GroupEntity>를 반환하여 port 인터페이스를 통해 adapter 타입이 노출됩니다.

이 메서드의 반환 타입이 GroupRepositoryPort를 통해 application 계층에 GroupEntity 의존성을 전파합니다. ChangeGroupService에서 언급한 대로 port 인터페이스는 Optional<Group>을 반환하고, 매핑은 adapter 내부에서 처리하는 것이 바람직합니다.

🤖 Prompt for AI Agents
In
`@src/main/java/flipnote/group/adapter/out/persistence/GroupRepositoryAdapter.java`
around lines 31 - 34, The adapter currently exposes persistence type by
returning Optional<GroupEntity> from GroupRepositoryAdapter.findById; change
this method to return Optional<Group> (the port's expected type) and map the
result inside the adapter by transforming GroupEntity to the domain Group (e.g.,
groupRepository.findById(id).map(entity -> entity.toDomain()) or via a
GroupMapper.toDomain(entity)); do not change the GroupRepositoryPort
signature—perform the mapping within GroupRepositoryAdapter and return
Optional.empty()/mapped Optional<Group> as appropriate.

@stoneTiger0912 stoneTiger0912 merged commit 26dcc9c into main Feb 11, 2026
@stoneTiger0912 stoneTiger0912 deleted the feat/find-group branch February 11, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant