Skip to content

refactor: 슬롯 확장 수정, 유저별 텃밭 조회 기능#135

Merged
xoruddl merged 2 commits into
developfrom
131-슬롯-확장-로직-반영
Aug 27, 2025

Hidden character warning

The head ref may contain hidden characters: "131-\uc2ac\ub86f-\ud655\uc7a5-\ub85c\uc9c1-\ubc18\uc601"
Merged

refactor: 슬롯 확장 수정, 유저별 텃밭 조회 기능#135
xoruddl merged 2 commits into
developfrom
131-슬롯-확장-로직-반영

Conversation

@xoruddl
Copy link
Copy Markdown
Member

@xoruddl xoruddl commented Aug 27, 2025

📝 개요
이번 PR의 핵심 내용을 한 줄로 요약해 주세요.

💻 작업 내용
이번 PR에서 작업한 내용을 상세히 설명해 주세요.

작업 내용 1
작업 내용 2
...

✅ PR 체크리스트
PR을 보내기 전에 아래 체크리스트를 확인해 주세요.

커밋 메시지는 포맷에 맞게 작성했나요?
스스로 코드를 다시 한번 검토했나요?
관련 이슈를 연결했나요?
빌드 및 테스트가 로컬에서 성공했나요?

🔗 관련 이슈
이번 PR과 관련된 이슈 번호를 기재해 주세요. 예: Closes #131

스크린샷 (선택)
UI 변경 사항이 있다면 스크린샷을 첨부해 주세요.

Summary by CodeRabbit

  • New Features
    • 단계 기반 텃밭 슬롯 해제: 현재 단계에서 허용된 최대 개수를 초과하면 안내 메시지로 생성 제한을 알립니다.
    • 새 텃밭 생성 시 기본 배경/아바타가 자동 적용됩니다.
    • 내 텃밭 ID 목록 조회 API 추가: 현재 사용자 기준으로 슬롯 순서대로 정렬된 ID 리스트를 반환합니다.
    • 오류 코드/메시지 확장: 텃밭 슬롯 잠김, 소원나무 미존재, 기본 리소스 미존재 상황을 명확히 안내합니다.
  • Chores
    • 로컬 환경 DB 스키마 전략을 create-drop에서 update로 변경.

@xoruddl xoruddl linked an issue Aug 27, 2025 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 27, 2025

Walkthrough

단계(위시트리 포인트 기반)별 텃밭 슬롯 개방 로직으로 전환하고, 기본 아바타/배경을 사용한 새 텃밭 생성 절차를 GardenService에 구현. 관련 에러 코드 추가/재배치. 현재 사용자 텃밭 ID 목록 조회 API 추가. 로컬 프로필 JPA DDL 전략을 update로 변경.

Changes

Cohort / File(s) Change Summary
Stage 기반 슬롯 개방 및 기본 리소스 생성 (Service)
src/main/java/.../garden/garden/service/GardenService.java
위시트리 포인트→WishTreeStage로 현재 단계 계산, 단계별 허용 최대 텃밭 수 검증. 미충족 시 GARDEN_SLOT_LOCKED 예외. 기본 배경(ID=1), 기본 아바타(ID=1) 조회 실패 시 DEFAULT_RESOURCE_NOT_FOUND. 새 텃밭 생성 및 user.addGarden 연결. USER_NOT_FOUND로 예외 정규화.
에러 코드 정비
src/main/java/.../global/common/ErrorCode.java
GARDEN_SLOT_LOCKED, WISH_TREE_NOT_FOUND, DEFAULT_RESOURCE_NOT_FOUND 추가. 일부 400 코드 재배치(코드값 재할당). 주석/포맷 소폭 수정.
사용자 텃밭 조회 API
src/main/java/.../member/user/presentation/UserController.java
GET /api/v1/users/me/gardens 추가. 인증 사용자 기준 텃밭을 slotNumber로 정렬 후 ID 리스트로 응답.
로컬 설정 변경
src/main/resources/application.yml
local 프로필의 spring.jpa.hibernate.ddl-auto를 create-drop → update로 변경.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant API as GardenController
  participant S as GardenService
  participant UR as UserRepository
  participant WR as WishTreeRepository
  participant SR as Stage (WishTreeStage)
  participant GR as GardenRepository
  participant AR as AvatarRepository
  participant BR as GardenBackgroundRepository

  U->>API: POST /gardens/unlock
  API->>S: unlockNewGardenSlot(userId)
  S->>UR: findById(userId)
  UR-->>S: User or null
  alt user not found
    S-->>API: throw USER_NOT_FOUND
    API-->>U: 404 USER_NOT_FOUND
  end
  S->>WR: findByUserId(userId)
  WR-->>S: WishTree or null
  alt wish tree missing
    S-->>API: throw WISH_TREE_NOT_FOUND
    API-->>U: 404 WISH_TREE_NOT_FOUND
  end
  S->>SR: getStageForPoints(points)
  SR-->>S: WishTreeStage (maxGardens)
  S->>S: compare currentGardens vs maxGardens
  alt exceeds limit
    S-->>API: throw GARDEN_SLOT_LOCKED
    API-->>U: 400 GARDEN_SLOT_LOCKED
  else can create
    S->>BR: findById(1) defaultBackground
    BR-->>S: Background or null
    S->>AR: findById(1) defaultAvatar
    AR-->>S: Avatar or null
    alt missing default resources
      S-->>API: throw DEFAULT_RESOURCE_NOT_FOUND
      API-->>U: 404 DEFAULT_RESOURCE_NOT_FOUND
    else
      S->>GR: save(new Garden with slotNumber = count+1)
      GR-->>S: Garden
      S->>S: user.addGarden(garden)
      S-->>API: success
      API-->>U: 200 OK
    end
  end
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant API as UserController
  participant Sec as SecurityContext

  U->>API: GET /api/v1/users/me/gardens
  API->>Sec: @AuthenticationPrincipal User
  Sec-->>API: User
  API->>API: user.getGardens() -> sort by slotNumber -> map to id
  API-->>U: 200 [gardenId...]
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
슬롯 확장 로직 반영 (#131)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
로컬 JPA DDL 전략을 create-drop→update로 변경 (src/main/resources/application.yml) 슬롯 확장 로직과 직접 관련 없음. 실행 환경 설정 변경으로, 이슈 #131 범위를 벗어남.
현재 사용자 텃밭 ID 목록 조회 API 추가 (src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java) 슬롯 확장 로직 구현과 무관한 신규 엔드포인트 추가로 보임. 이슈 #131 요구사항에 명시되지 않음.

Possibly related PRs

Suggested reviewers

  • c5ln

Poem

당근 귀 쫑긋, 슬롯이 콕-톡 열렸네!
소원나무 잎새 셈하며 단계로 척척—헤이!
배경 하나, 아바타 하나, 새 텃밭 쏙-생성! 🌱
에러코드 정렬하고 길 찾는 발자국.
오늘도 토끼는 릴리즈에 깡충, PR 위에 점프! 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 131-슬롯-확장-로직-반영

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Status, Documentation and Community

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

@xoruddl xoruddl merged commit f9e045f into develop Aug 27, 2025
5 of 6 checks passed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (2)
src/main/resources/application.yml (1)

88-93: 프로덕션에서 SQL 파라미터 TRACE 로그는 PII 노출 위험

org.hibernate.type.descriptor.sql.BasicBinder: TRACE는 바인딩된 파라미터(개인정보 포함 가능)를 그대로 로그에 남깁니다. prod 프로필에서 제거하거나 최소 INFO 이하로 낮추는 것이 안전합니다.

적용 예:

-logging:
-  level:
-    org.hibernate.SQL: DEBUG
-    org.hibernate.type.descriptor.sql.BasicBinder: TRACE
+logging:
+  level:
+    org.hibernate.SQL: INFO
+    # prod에서는 파라미터 로그 비활성화(필요 시 local/dev 전용 문서에만 유지)
+    # org.hibernate.type.descriptor.sql.BasicBinder: OFF
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java (1)

49-58: PathVariable/RequestParam 사용 불일치 버그

@PatchMapping("/me/{avatarId}")인데 파라미터는 @RequestParam("avatarId")로 선언되어 매핑 실패합니다. @PathVariable로 교체하세요.

-  @PatchMapping("/me/{avatarId}")
+  @PatchMapping("/me/{avatarId}")
   public ResponseEntity<ApiResponse<Void>> updateAvatar(
       @AuthenticationPrincipal User user,
       @RequestBody @Valid AvatarChangeRequest request,
-      @RequestParam("avatarId") Long avatarId) {
+      @PathVariable("avatarId") Long avatarId) {
🧹 Nitpick comments (6)
src/main/resources/application.yml (1)

58-58: 로컬 DDL 전략을 update로 전환 — 테스트/초기화 영향 점검 필요

H2(in‑mem) 환경에서도 update는 스키마 차이를 누적시키므로, 통합테스트가 “완전 초기화”를 전제로 하면 불안정할 수 있습니다. 테스트 전용 프로필(test)에 create-drop을 유지하거나, 마이그레이션 스크립트만으로 부트스트랩하도록 합의가 필요합니다.

권장: test 프로필을 추가해 테스트 시에는 항상 깨끗한 스키마를 보장.

+---
+spring:
+  config:
+    activate:
+      on-profile: test
+  jpa:
+    hibernate:
+      ddl-auto: create-drop
+  datasource:
+    url: jdbc:h2:mem:testdb;MODE=PostgreSQL
+    username: sa
+    password:
src/main/java/com/example/cp_main_be/global/common/ErrorCode.java (1)

18-20: GARDEN_SLOT_MAXED_OUT 주석 최신화

현재 슬롯 확장 로직이 단계(위시트리) 기반으로 전환되었습니다. “최종 3개 도달 시 사용 가능” 주석은 혼동 소지가 있어 최신 정책으로 정리하거나 Deprecated 표기를 권장합니다.

-  GARDEN_SLOT_MAXED_OUT(
-      HttpStatus.BAD_REQUEST, "E40006", "더 이상 텃밭을 추가할 수 없습니다."), // 최종 3개 도달 시 사용 가능
+  GARDEN_SLOT_MAXED_OUT(
+      HttpStatus.BAD_REQUEST, "E40006", "더 이상 텃밭을 추가할 수 없습니다."), // 단계 기반 슬롯 정책으로 대체(legacy)
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java (1)

91-100: 응답 스키마의 확장성

ID만 노출하는 것은 간결하지만, 추후 슬롯 번호나 라벨 추가 시 API 변경이 필요합니다. GardenSummaryResponse{id, slotNumber} 같은 DTO로의 확장을 고려하세요.

src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java (3)

38-38: 미사용 상수 제거

단계 기반 슬롯 정책으로 전환되어 MAX_GARDEN_COUNT는 사용되지 않습니다. 혼동 방지를 위해 제거하세요.

-  private static final int MAX_GARDEN_COUNT = 3;

195-210: 기본 리소스 ID 하드코딩(1L) 제거 권장

데이터 의존성이 높고 이식성이 낮습니다. 설정 값 또는 “기본 여부 플래그” 조회로 치환하세요.

환경설정/서비스 예시:

+import org.springframework.beans.factory.annotation.Value;
...
+  @Value("${garden.default.background-id:1}")
+  private Long defaultBackgroundId;
+  @Value("${avatar.default.id:1}")
+  private Long defaultAvatarId;
...
-    gardenBackgroundRepository.findById(1L)
+    gardenBackgroundRepository.findById(defaultBackgroundId)
...
-    avatarRepository.findById(1L)
+    avatarRepository.findById(defaultAvatarId)

application.yml(프로필별 오버라이드 가능):

+garden:
+  default:
+    background-id: 1
+avatar:
+  default:
+    id: 1

필요하시면 리포지토리에 findDefault() API 형태로의 전환도 도와드리겠습니다.


212-219: 양방향 연관관계 일관성 유지 방식 통일

Garden.builder().user(user)user.addGarden(newGarden)가 동시에 호출됩니다. 도메인 규칙상 한쪽만 진입점으로 삼아 양쪽 세팅을 내부에서 보장하도록 통일하는 것이 안전합니다(중복 추가/누락 방지).

- Garden newGarden = Garden.builder()
-     .user(user)
-     ...
-     .build();
- gardenRepository.save(newGarden);
- user.addGarden(newGarden);
+ Garden newGarden = Garden.builder()
+     ...
+     .build();
+ user.addGarden(newGarden); // addGarden 내부에서 newGarden.setUser(this) 수행
+ gardenRepository.save(newGarden);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1d52477 and f786cff.

📒 Files selected for processing (4)
  • src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java (4 hunks)
  • src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java (3 hunks)
  • src/main/java/com/example/cp_main_be/global/common/ErrorCode.java (2 hunks)
  • src/main/resources/application.yml (1 hunks)
🔇 Additional comments (4)
src/main/java/com/example/cp_main_be/global/common/ErrorCode.java (2)

41-42: 새 404 코드 추가 적합

WISH_TREE_NOT_FOUND, DEFAULT_RESOURCE_NOT_FOUND 추가는 서비스 로직과 일치하며 의미도 명확합니다. 별도 우려 없습니다.


21-27: E40008~E40011 에러 코드 변경에 따른 하드코딩 의존성 없음 확인 & 외부 소비자 검증 요청

레포 전역 검색 결과, “E40008”~“E40011” 리터럴은 enum 정의부(src/main/java/com/example/cp_main_be/global/common/ErrorCode.java) 외에는 발견되지 않았습니다. Java 코드에서는 모두 enum 상수(ErrorCode.GARDEN_SLOT_LOCKED, FILE_SIZE_EXCEEDED, INVALID_MISSION_TYPE_FOR_QUIZ_OPTION, INVALID_REQUEST)로만 참조 중이므로 내부 로직에는 추가 수정이 필요 없습니다.

아래 영역에 대해서만 추가 검증을 권장드립니다:

  • API 명세(Swagger/OpenAPI) 및 README 등 문서
  • 통합/단위 테스트(응답 JSON 내 errorCode 비교)
  • 프론트엔드 코드(문자열 기반 error code 매핑)
  • 외부 클라이언트(Postman 컬렉션, SDK 등)
src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java (2)

170-176: 사용자/위시트리 존재 검증 전환 적절

USER_NOT_FOUND, WISH_TREE_NOT_FOUND로 명확하게 분기하는 변경은 의미적으로 타당합니다.


178-186: 단계 기반 슬롯 한도 계산 로직 적합

포인트→단계→최대 텃밭 수 파이프라인이 명확합니다. 도메인 규칙과 정합적입니다.

Comment on lines +188 to +193
if (currentGardenCount >= maxGardensForStage) {
// 현재 단계에서는 더 이상 텃밭을 생성할 수 없음
// ErrorCode에 GARDEN_SLOT_LOCKED 추가가 필요할 수 있습니다.
throw new CustomApiException(
ErrorCode.GARDEN_SLOT_LOCKED, "현재 단계에서는 더 이상 텃밭을 만들 수 없습니다. 소원나무를 성장시켜주세요.");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

슬롯 확장 동시성 취약 — 중복 슬롯/초과 생성 가능성

동시 요청에서 currentGardenCount 기반 계산은 경쟁 조건을 유발합니다. 사용자별 슬롯 행 잠금 또는 고유 제약 + 비관적 잠금으로 방어하세요.

DB/리포지토리/서비스 제안:

-- DB: 사용자별 슬롯번호 유니크 제약
ALTER TABLE garden ADD CONSTRAINT uq_garden_user_slot UNIQUE (user_id, slot_number);
// GardenRepository
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select coalesce(max(g.slotNumber),0) from Garden g where g.user.id = :userId")
int findMaxSlotNumberByUserIdForUpdate(Long userId);
- int currentGardenCount = user.getGardens().size();
+ int currentMaxSlot = gardenRepository.findMaxSlotNumberByUserIdForUpdate(userId);
...
- if (currentGardenCount >= maxGardensForStage) {
+ if (currentMaxSlot >= maxGardensForStage) {
     throw new CustomApiException(
         ErrorCode.GARDEN_SLOT_LOCKED, "현재 단계에서는 더 이상 텃밭을 만들 수 없습니다. 소원나무를 성장시켜주세요.");
   }
...
- .slotNumber(currentGardenCount + 1)
+ .slotNumber(currentMaxSlot + 1)
🤖 Prompt for AI Agents
In
src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java
around lines 188 to 193, the current check using currentGardenCount can race
under concurrent requests allowing duplicate/over-limit garden creation; fix by
enforcing a DB-level unique constraint on (user_id, slot_number) and performing
the count/slot selection inside a transactional method with a pessimistic lock
query (or select max(slot_number) FOR UPDATE) on the user's garden rows so
concurrent threads serialize; adjust GardenRepository to provide a
PESSIMISTIC_WRITE locked accessor and wrap creation in a single @Transactional
unit that rechecks limits and handles unique-constraint violation to return a
proper ErrorCode.GARDEN_SLOT_LOCKED response.

Comment on lines +87 to +100
@Operation(
summary = "내 텃밭 ID 목록 조회",
description = "현재 로그인한 유저와 연결된 모든 텃밭의 ID 목록을 조회합니다. 텃밭 슬롯 번호 순으로 정렬됩니다.")
@GetMapping("/me/gardens")
public ResponseEntity<ApiResponse<List<Long>>> getMyGardenIds(
@AuthenticationPrincipal User user) {
List<Long> gardenIds =
user.getGardens().stream()
.sorted(Comparator.comparing(Garden::getSlotNumber))
.map(Garden::getId)
.collect(Collectors.toList());

return ResponseEntity.ok(ApiResponse.success(gardenIds));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

컨트롤러에서 LAZY 컬렉션 접근 위험 및 N+1 가능성

user.getGardens()가 LAZY면 트랜잭션 밖에서 LazyInitializationException이 날 수 있습니다. 또한 정렬/매핑을 애그리거트 외부에서 수행 중입니다. 서비스 계층에서 fetch join으로 ID만 조회하는 리포지토리 메서드를 도입하세요.

권장 리포지토리/서비스 예시:

// GardenRepository
@Query("select g.id from Garden g where g.user.id = :userId order by g.slotNumber asc")
List<Long> findIdsByUserIdOrderBySlot(Long userId);
-List<Long> gardenIds =
-    user.getGardens().stream()
-        .sorted(Comparator.comparing(Garden::getSlotNumber))
-        .map(Garden::getId)
-        .collect(Collectors.toList());
+List<Long> gardenIds = gardenService.findMyGardenIds(user.getId());

검증 스크립트(매핑 확인):


🏁 Script executed:

#!/bin/bash
# User 엔티티에서 gardens 컬렉션 fetch 타입/매핑 확인
rg -n -C3 'class\s+User\b'
rg -n -C2 '@OneToMany[^\n]*gardens'

Length of output: 750


이전 스크립트가 @OneToMany 어노테이션과 gardens 필드를 정확히 찾지 못해 매핑 정보를 확인할 수 없습니다. User 엔티티에서 해당 필드 선언과 페치 전략을 명확히 보기 위해 아래 스크립트를 실행해주세요.


🏁 Script executed:

#!/bin/bash
# User 엔티티에서 @OneToMany 어노테이션 위치와 속성 확인
rg -n -C3 '@OneToMany' src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java

# User 엔티티에서 gardens 필드 선언부 확인
rg -n -C3 'gardens' src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java

Length of output: 1488


컨트롤러에서 LAZY 컬렉션 직접 접근 위험 – 서비스 계층으로 조회 로직 이동 필요

현재 User 엔티티의 gardens 컬렉션은 @OneToMany(mappedBy="user", cascade=ALL, orphanRemoval=true)로 선언되어 있어 JPA 기본 페치 전략인 LAZY를 사용합니다. 컨트롤러(getMyGardenIds)에는 트랜잭션이 걸려 있지 않으므로, 요청 처리 시점에 세션이 닫혀 LazyInitializationException이 발생할 수 있습니다. 또한, 정렬·매핑 로직이 프레젠테이션 계층에 흩어져 있어 책임 분리가 미흡합니다.

  • User 엔티티 매핑 확인

    • @OneToMany(mappedBy="user", …) 에서 fetch 속성 미설정 → 기본 LAZY 페치
    • private List<Garden> gardens = new ArrayList<>();
  • 컨트롤러 문제

    • @GetMapping 메서드에 @Transactional 미적용
    • user.getGardens() 호출 시점에 트랜잭션/세션 미존재 → LazyInitializationException 리스크
    • 프레젠테이션 계층에서 정렬·매핑 로직 수행 → SRP 위반

제안하는 리팩터링

  1. Repository에 사용자별 텃밭 ID만 조회하는 JPQL 메서드 추가
    public interface GardenRepository extends JpaRepository<Garden, Long> {
      @Query("select g.id from Garden g where g.user.id = :userId order by g.slotNumber asc")
      List<Long> findIdsByUserIdOrderBySlot(Long userId);
    }
  2. Service 계층에 트랜잭셔널 메서드 구현
    @Service
    public class GardenService {
      private final GardenRepository gardenRepo;
      @Transactional(readOnly = true)
      public List<Long> getMyGardenIds(Long userId) {
        return gardenRepo.findIdsByUserIdOrderBySlot(userId);
      }
    }
  3. 컨트롤러에서 직접 엔티티 컬렉션 대신 서비스 호출로 교체
    - List<Long> gardenIds =
    -     user.getGardens().stream()
    -         .sorted(Comparator.comparing(Garden::getSlotNumber))
    -         .map(Garden::getId)
    -         .collect(Collectors.toList());
    + List<Long> gardenIds = gardenService.getMyGardenIds(user.getId());

이렇게 하면

  • 불필요한 엔티티 그래프 로딩 방지
  • 트랜잭션 경계 내에서 안전하게 쿼리 수행
  • 프레젠테이션 계층의 역할이 명확해집니다.
🤖 Prompt for AI Agents
In
src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java
lines 87-100, the controller directly accesses the LAZY User.gardens collection
(user.getGardens()) which can trigger LazyInitializationException and mixes
presentation logic with data access; replace this by adding a GardenRepository
method to select garden IDs by userId ordered by slot, implement a @Service
GardenService with a @Transactional(readOnly=true) method getMyGardenIds(Long
userId) that calls the repository query, and update the controller to call that
service method (passing the authenticated user's id) and return the result
instead of iterating over user.getGardens(). Ensure no direct traversal of the
LAZY collection in the controller.

@xoruddl xoruddl deleted the 131-슬롯-확장-로직-반영 branch August 27, 2025 14:33
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