Skip to content

Conversation

@lvalentine6
Copy link
Member

@lvalentine6 lvalentine6 commented Jul 18, 2025

✨ 개요

  • ERD 자동 생성 워크플로를 수정하여 변경시에만 ERD가 생성되도록 수정하였습니다.
  • 스토리 상세 조회 기능을 구현했습니다.
  • 스토리 목록의 size를 클라이언트에게 받도록 수정하였습니다.
  • 기존 등록된 더미 데이터에 실제 이미지들이 나가도록 수정하였습니다. (마지막 DB 초기화가 되길...)
  • 소나 클라우드 관련 버그를 수정하였습니다.

🧾 관련 이슈

#84

🔍 참고 사항 (선택)

  • 스토리 관련 마지막 PR 입니다~
  • 테스트가 벌써 190개가 되었군요...

Summary by CodeRabbit

  • 신규 기능

    • 특정 스토리의 상세 정보를 조회할 수 있는 API 엔드포인트가 추가되었습니다.
    • 스토리 목록 조회 시, 반환 개수를 지정할 수 있는 size 쿼리 파라미터가 도입되었습니다(1~50, 기본값 5).
  • 버그 수정

    • 존재하지 않는 스토리 조회 시 404 오류와 정확한 에러 메시지가 반환됩니다.
  • 데이터베이스

    • 스토리 테이블의 주소 필드가 단일 주소에서 도로명 주소와 지번 주소로 분리되었습니다.
    • 더 구체적인 이미지 경로로 시드 데이터의 image_key 값이 수정되었습니다.
  • 테스트

    • 스토리 상세 조회 API와 관련된 단위 및 문서화 테스트가 추가 및 개선되었습니다.
    • 스토리 등록 및 조회 테스트가 구조화되고 상세 검증이 강화되었습니다.
  • CI/CD

    • 워크플로우가 변경되어 불필요한 실행이 줄고, 코드 품질 분석 및 인프라 변경 감지가 더욱 정확해졌습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jul 18, 2025

"""

Walkthrough

이 변경사항은 Story API에 스토리 상세 조회 기능을 추가하고, 스토리 목록 조회에 페이지 크기(size) 파라미터를 도입하여 유연하게 조회할 수 있도록 했습니다. 또한, 관련 서비스 및 예외처리를 확장하고, 워크플로우 트리거 조건과 시드 데이터의 이미지 경로를 개선했습니다.

Changes

파일/경로 요약 변경 내용 요약
.github/workflows/automatic-erd.yml
.github/workflows/terraform-plan.yml
워크플로우 트리거 조건에 브랜치 및 경로 필터 추가/수정, 불필요한 실행 방지
.github/workflows/sonarcloud.yml checkout 단계에 fetch-depth: 0 추가하여 전체 Git 히스토리 fetch
src/main/java/eatda/controller/story/StoryController.java 스토리 목록 조회에 size 파라미터 추가, 스토리 상세 조회 엔드포인트 추가
src/main/java/eatda/service/story/StoryService.java getPagedStoryPreviews 메서드에 size 파라미터 추가, getStory 메서드 신설 및 예외처리 구현
src/main/java/eatda/exception/BusinessErrorCode.java STORY_NOT_FOUND에 HttpStatus.NOT_FOUND(404) 명시적으로 지정
src/main/resources/db/seed/dev/V2__dev_init_data.sql
src/main/resources/db/seed/local/V2__local_init_data.sql
cheer, article 테이블의 image_key 값을 구체적인 경로로 변경
src/main/resources/db/seed/dev/V4__dev_add_story_data.sql
src/main/resources/db/seed/local/V4__local_add_story_data.sql
story 테이블의 image_key 경로를 story/dummy/로 변경
src/main/resources/db/migration/V3__add_story_table.sql story 테이블의 store_address 컬럼을 store_road_address, store_lot_number_address로 분리
src/main/java/eatda/domain/story/Story.java Story 엔티티의 storeAddress 필드를 storeRoadAddress, storeLotNumberAddress로 분리 및 관련 메서드 추가
src/main/java/eatda/controller/story/FilteredSearchResult.java address 필드를 roadAddress, lotNumberAddress 두 필드로 분리
src/main/java/eatda/controller/story/StoryResponse.java storeAddress 필드를 storeDistrict, storeNeighborhood 두 필드로 분리
src/test/java/eatda/controller/story/StoryControllerTest.java 테스트 클래스 리팩토링 및 스토리 상세/목록/등록 테스트 추가 및 예외 상황 테스트 강화
src/test/java/eatda/document/story/StoryDocumentTest.java 스토리 상세 조회 API 문서화 및 테스트 추가, 목록 조회 size 파라미터 반영
src/test/java/eatda/service/story/StoryServiceTest.java 스토리 상세 조회 및 예외 테스트 추가, 목록 조회 테스트 size 파라미터 반영 및 검증 로직 개선
src/test/java/eatda/domain/story/StoryTest.java Story 테스트에서 storeAddress 대신 storeRoadAddress, storeLotNumberAddress 필드로 변경 및 검증 분리

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller
    participant Service
    participant Repository
    participant ImageService

    Client->>Controller: GET /api/stories?size=5
    Controller->>Service: getPagedStoryPreviews(size)
    Service->>Repository: findStories(PageRequest.of(0, size))
    Repository-->>Service: List<Story>
    Service->>ImageService: getPresignedUrl(imageKey)
    ImageService-->>Service: imageUrl
    Service-->>Controller: StoriesResponse
    Controller-->>Client: 200 OK + StoriesResponse

    Client->>Controller: GET /api/stories/{storyId}
    Controller->>Service: getStory(storyId)
    Service->>Repository: findById(storyId)
    alt Story exists
        Repository-->>Service: Story
        Service->>ImageService: getPresignedUrl(imageKey)
        ImageService-->>Service: imageUrl
        Service-->>Controller: StoryResponse
        Controller-->>Client: 200 OK + StoryResponse
    else Story not found
        Repository-->>Service: null
        Service-->>Controller: throws BusinessException(STORY_NOT_FOUND)
        Controller-->>Client: 404 Not Found + ErrorResponse
    end
Loading

Possibly related PRs

  • #74: 자동 ERD 생성 워크플로우의 초기 구현을 도입한 PR로, 본 PR의 워크플로우 트리거 조건 변경과 직접적으로 연관됨.
  • #84: 스토리 목록 조회 기능의 초기 구현 PR로, 본 PR에서 상세 조회 및 페이지네이션 확장 등 직접적으로 기능을 확장함.
  • #80: 스토리 등록 등 스토리 도메인 및 서비스 구조를 도입한 PR로, 본 PR의 상세 조회/목록 확장과 직접적으로 연결됨.

Suggested labels

released on @beta

Suggested reviewers

  • leegwichan

Poem

🐰
새로운 이야기, 한 땀 한 땀,
상세 조회로 빛나는 밤.
페이지 크기 조절도 척척,
오류엔 404로 딱!
토끼는 기뻐 깡충깡충,
코드의 숲에 봄바람이 솔솔~
🌱✨
"""

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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.

Documentation and Community

  • 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.

@github-actions github-actions bot added the feat label Jul 18, 2025
@github-actions github-actions bot requested a review from leegwichan July 18, 2025 15:16
@lvalentine6 lvalentine6 changed the title Feat/product 185 [Feat] 스토리 상세 조회 기능 구현 Jul 18, 2025
@github-actions
Copy link

📄 Terraform Plan Summary

🛡️ Common Infrastructure


No plan summary

Status: ✅ No Changes


🛠️ Development Environment


No plan summary

Status: ✅ No Changes


📋 Full Results: View in Actions

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

🔭 Outside diff range comments (1)
src/test/java/eatda/service/story/StoryServiceTest.java (1)

31-41: 중복된 서비스 초기화

StoryService@Autowired로 주입받고 있으면서 동시에 @BeforeEach에서 수동으로 생성하고 있습니다. 이는 혼란을 야기할 수 있으므로 하나의 방식만 사용해야 합니다.

Spring 컨텍스트를 사용하는 경우:

-    @BeforeEach
-    void setUp() {
-        storyService = new StoryService(storeService, imageService, storyRepository, memberRepository);
-    }

또는 수동 초기화를 사용하는 경우:

-    @Autowired
     private StoryService storyService;
🧹 Nitpick comments (2)
.github/workflows/sonarcloud.yml (1)

22-23: 전체 히스토리 체크아웃으로 Sonar 정확도 향상, 속도 영향 검토 필요
fetch-depth: 0을 추가해 전체 git 히스토리를 내려받도록 변경하신 점 👍 SonarCloud에서 blame 정보와 커버리지 히스토리를 제대로 잡는 데 도움이 됩니다.
다만 저장소가 커질수록 체크아웃 시간이 늘어날 수 있으니, 필요 시 lfs: false 설정이나 캐시 최적화도 함께 고려해 보세요.

.github/workflows/terraform-plan.yml (1)

5-6: 경로 필터로 불필요한 Terraform Plan 실행 방지—👍
paths 조건을 추가해 terraform/**, terraform-bootstrap/** 변경 시에만 실행되도록 한 점이 CI 비용 절감에 효과적입니다.
추가로, 직접 main 브랜치에 push 되는 경우에도 Plan 확인이 필요하다면 push: 트리거에 동일한 필터를 넣는 방안도 검토해 주세요.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 153a16c and f006ada.

📒 Files selected for processing (13)
  • .github/workflows/automatic-erd.yml (1 hunks)
  • .github/workflows/sonarcloud.yml (1 hunks)
  • .github/workflows/terraform-plan.yml (1 hunks)
  • src/main/java/eatda/controller/story/StoryController.java (2 hunks)
  • src/main/java/eatda/exception/BusinessErrorCode.java (1 hunks)
  • src/main/java/eatda/service/story/StoryService.java (3 hunks)
  • src/main/resources/db/seed/dev/V2__dev_init_data.sql (1 hunks)
  • src/main/resources/db/seed/dev/V4__dev_add_story_data.sql (1 hunks)
  • src/main/resources/db/seed/local/V2__local_init_data.sql (1 hunks)
  • src/main/resources/db/seed/local/V4__local_add_story_data.sql (1 hunks)
  • src/test/java/eatda/controller/story/StoryControllerTest.java (3 hunks)
  • src/test/java/eatda/document/story/StoryDocumentTest.java (3 hunks)
  • src/test/java/eatda/service/story/StoryServiceTest.java (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
src/main/resources/db/seed/dev/V2__dev_init_data.sql (1)
Learnt from: lvalentine6
PR: YAPP-Github/26th-Web-Team-1-BE#76
File: src/main/java/eatda/domain/store/Cheer.java:50-61
Timestamp: 2025-07-15T09:42:54.091Z
Learning: Cheer 엔티티의 생성자는 데이터베이스 시딩을 위해 의도적으로 ID 매개변수를 받도록 설계되었습니다. 시드 데이터에서 명시적으로 ID를 설정하기 때문에 이 패턴이 필요합니다.
src/main/resources/db/seed/local/V2__local_init_data.sql (1)
Learnt from: lvalentine6
PR: YAPP-Github/26th-Web-Team-1-BE#76
File: src/main/java/eatda/domain/store/Cheer.java:50-61
Timestamp: 2025-07-15T09:42:54.091Z
Learning: Cheer 엔티티의 생성자는 데이터베이스 시딩을 위해 의도적으로 ID 매개변수를 받도록 설계되었습니다. 시드 데이터에서 명시적으로 ID를 설정하기 때문에 이 패턴이 필요합니다.
src/main/java/eatda/controller/story/StoryController.java (1)
Learnt from: leegwichan
PR: YAPP-Github/26th-Web-Team-1-BE#60
File: src/main/java/eatda/controller/store/StoreController.java:18-21
Timestamp: 2025-07-09T08:05:53.497Z
Learning: Spring Boot 컨트롤러에서 LoginMember 등의 인증 파라미터는 메서드 내에서 직접 사용되지 않더라도 접근 제어(인증된 사용자만 접근 가능)를 위해 필요할 수 있다. 이는 보안상 유효한 패턴이다.
src/test/java/eatda/controller/story/StoryControllerTest.java (1)
Learnt from: leegwichan
PR: YAPP-Github/26th-Web-Team-1-BE#60
File: src/test/java/eatda/controller/store/StoreControllerTest.java:10-32
Timestamp: 2025-07-09T07:56:50.612Z
Learning: 컨트롤러 테스트에서 MockitoBean으로 의존성을 모킹한 경우, 상세한 비즈니스 로직 검증보다는 컨트롤러 계층의 동작(라우팅, 파라미터 처리, 응답 구조 등)을 검증하는 것이 더 적절합니다. 모킹된 데이터에 대한 상세 검증은 의미가 없기 때문입니다.
🧬 Code Graph Analysis (1)
src/main/java/eatda/controller/story/StoryController.java (1)
src/main/java/eatda/controller/web/auth/AuthMemberArgumentResolver.java (1)
  • RequiredArgsConstructor (14-36)
🔇 Additional comments (12)
.github/workflows/automatic-erd.yml (1)

5-6: DB 마이그레이션 변경 시에만 ERD 생성—구현 적절
src/main/resources/db/migration/** 경로로 한정하여 워크플로우 빈번 실행을 줄인 점 좋습니다.
특정 브랜치 외 타깃으로 ERD가 필요한 경우(예: hotfix)도 있을 수 있으니, 향후 필요 시 브랜치 배열에 추가만 하면 될 것 같습니다.

src/main/resources/db/seed/dev/V2__dev_init_data.sql (2)

28-34: 이미지 경로 구조 개선이 잘 적용되었습니다

cheer 테이블의 image_key 값들이 'default.jpg'에서 'cheer/dummy/N.jpg' 형태로 체계적으로 변경되어 카테고리별 이미지 관리가 가능해졌습니다. 각 레코드마다 고유한 이미지 파일을 참조하도록 개선되었습니다.


37-39: article 테이블의 이미지 경로 구조화가 완료되었습니다

article 테이블의 image_key 값들이 일관되게 'article/dummy/N.jpg' 형태로 변경되어 cheer 테이블과 동일한 구조를 따르고 있습니다. 체계적인 이미지 관리를 위한 좋은 변경사항입니다.

src/main/resources/db/seed/local/V2__local_init_data.sql (1)

28-34: 로컬 환경 시드 데이터의 일관성 유지가 완료되었습니다

개발 환경과 동일한 이미지 경로 구조로 변경되어 환경 간 일관성이 유지되었습니다. 이는 개발 및 테스트 과정에서 예상치 못한 차이를 방지하는 좋은 접근 방식입니다.

Also applies to: 37-39

src/main/java/eatda/exception/BusinessErrorCode.java (1)

50-50: HTTP 상태 코드가 의미론적으로 적절하게 수정되었습니다

STORY_NOT_FOUND 에러에 HttpStatus.NOT_FOUND를 명시적으로 지정한 것은 RESTful API 설계 원칙에 부합하는 개선사항입니다. 존재하지 않는 리소스 조회 시 404 상태 코드를 반환하는 것이 표준적인 접근 방식입니다.

src/main/resources/db/seed/local/V4__local_add_story_data.sql (1)

3-9: 스토리 이미지 경로 구조가 일관되게 정리되었습니다

'story/preview/'에서 'story/dummy/'로 변경되어 다른 테이블들과 동일한 네이밍 컨벤션을 따르게 되었습니다. 모든 스토리 레코드가 일관되게 업데이트되어 체계적인 이미지 관리가 가능해졌습니다.

src/main/resources/db/seed/dev/V4__dev_add_story_data.sql (1)

3-5: 개발 환경 스토리 데이터의 일관성이 유지되었습니다

로컬 환경과 동일한 이미지 경로 구조로 변경되어 환경 간 일관성이 확보되었습니다. 'story/dummy/' 경로 사용으로 다른 테이블들과 통일된 네이밍 컨벤션을 따르고 있습니다.

src/main/java/eatda/service/story/StoryService.java (1)

84-97: 구현이 적절합니다!

스토리 상세 조회 메서드가 표준 패턴을 잘 따르고 있으며, 에러 처리도 적절합니다.

src/main/java/eatda/controller/story/StoryController.java (2)

34-38: 페이지네이션 파라미터 추가가 적절합니다!

size 파라미터의 검증 조건과 기본값 설정이 적절하며, 공개 엔드포인트로 인증이 필요 없는 것도 타당합니다.


40-44: 스토리 상세 조회 엔드포인트가 잘 구현되었습니다!

RESTful 규칙을 잘 따르고 있으며, 공개 엔드포인트로 구현한 것이 적절합니다.

src/test/java/eatda/service/story/StoryServiceTest.java (1)

121-161: 스토리 상세 조회 테스트가 잘 작성되었습니다!

성공 케이스와 실패 케이스를 모두 포함하여 포괄적인 테스트 커버리지를 제공합니다.

src/test/java/eatda/controller/story/StoryControllerTest.java (1)

82-130: GetStory 테스트 클래스가 잘 구성되었습니다!

성공 케이스와 404 에러 케이스를 모두 다루며, 컨트롤러의 실제 동작과 일치하는 테스트를 제공합니다.

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

🧹 Nitpick comments (2)
.github/workflows/terraform-plan.yml (1)

3-7: Terraform 코드로 한정된 트리거 필터 👍 — 루트 위치 .tf 파일 여부만 재확인 권장

paths 필터로 워크플로우 실행이 Terraform 디렉터리 변경 시에만 일어나도록 제한한 점은 빌드 자원 절약에 도움이 됩니다. 다만 레포 루트나 다른 위치에 .tf 파일이 존재할 가능성이 있다면 추가 패턴을 포함할지 검토해 주세요.

.github/workflows/automatic-erd.yml (1)

3-7: ERD 자동 생성 워크플로우 범위를 마이그레이션 스크립트 변경으로 한정

필요한 경우에만 실행되도록 최적화된 점 좋습니다. 다만 PR 단위로만 실행되므로 main/develop에 직접 커밋되는 경우 ERD 페이지가 갱신되지 않을 수 있습니다. 병합 이후 최신 ERD를 보장하려면 push 트리거(같은 경로 필터 포함) 추가를 고려해 보세요.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 153a16c and f006ada.

📒 Files selected for processing (13)
  • .github/workflows/automatic-erd.yml (1 hunks)
  • .github/workflows/sonarcloud.yml (1 hunks)
  • .github/workflows/terraform-plan.yml (1 hunks)
  • src/main/java/eatda/controller/story/StoryController.java (2 hunks)
  • src/main/java/eatda/exception/BusinessErrorCode.java (1 hunks)
  • src/main/java/eatda/service/story/StoryService.java (3 hunks)
  • src/main/resources/db/seed/dev/V2__dev_init_data.sql (1 hunks)
  • src/main/resources/db/seed/dev/V4__dev_add_story_data.sql (1 hunks)
  • src/main/resources/db/seed/local/V2__local_init_data.sql (1 hunks)
  • src/main/resources/db/seed/local/V4__local_add_story_data.sql (1 hunks)
  • src/test/java/eatda/controller/story/StoryControllerTest.java (3 hunks)
  • src/test/java/eatda/document/story/StoryDocumentTest.java (3 hunks)
  • src/test/java/eatda/service/story/StoryServiceTest.java (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
src/main/resources/db/seed/local/V2__local_init_data.sql (1)
Learnt from: lvalentine6
PR: YAPP-Github/26th-Web-Team-1-BE#76
File: src/main/java/eatda/domain/store/Cheer.java:50-61
Timestamp: 2025-07-15T09:42:54.091Z
Learning: Cheer 엔티티의 생성자는 데이터베이스 시딩을 위해 의도적으로 ID 매개변수를 받도록 설계되었습니다. 시드 데이터에서 명시적으로 ID를 설정하기 때문에 이 패턴이 필요합니다.
src/main/resources/db/seed/dev/V2__dev_init_data.sql (1)
Learnt from: lvalentine6
PR: YAPP-Github/26th-Web-Team-1-BE#76
File: src/main/java/eatda/domain/store/Cheer.java:50-61
Timestamp: 2025-07-15T09:42:54.091Z
Learning: Cheer 엔티티의 생성자는 데이터베이스 시딩을 위해 의도적으로 ID 매개변수를 받도록 설계되었습니다. 시드 데이터에서 명시적으로 ID를 설정하기 때문에 이 패턴이 필요합니다.
src/main/java/eatda/controller/story/StoryController.java (1)
Learnt from: leegwichan
PR: YAPP-Github/26th-Web-Team-1-BE#60
File: src/main/java/eatda/controller/store/StoreController.java:18-21
Timestamp: 2025-07-09T08:05:53.497Z
Learning: Spring Boot 컨트롤러에서 LoginMember 등의 인증 파라미터는 메서드 내에서 직접 사용되지 않더라도 접근 제어(인증된 사용자만 접근 가능)를 위해 필요할 수 있다. 이는 보안상 유효한 패턴이다.
src/test/java/eatda/controller/story/StoryControllerTest.java (1)
Learnt from: leegwichan
PR: YAPP-Github/26th-Web-Team-1-BE#60
File: src/test/java/eatda/controller/store/StoreControllerTest.java:10-32
Timestamp: 2025-07-09T07:56:50.612Z
Learning: 컨트롤러 테스트에서 MockitoBean으로 의존성을 모킹한 경우, 상세한 비즈니스 로직 검증보다는 컨트롤러 계층의 동작(라우팅, 파라미터 처리, 응답 구조 등)을 검증하는 것이 더 적절합니다. 모킹된 데이터에 대한 상세 검증은 의미가 없기 때문입니다.
🧬 Code Graph Analysis (1)
src/main/java/eatda/controller/story/StoryController.java (1)
src/main/java/eatda/controller/web/auth/AuthMemberArgumentResolver.java (1)
  • RequiredArgsConstructor (14-36)
🔇 Additional comments (21)
.github/workflows/sonarcloud.yml (1)

22-24: SonarCloud 분석을 위한 fetch-depth: 0 설정 환영

전체 Git 히스토리를 가져오면 Sonar 추적(블레임)·커버리지 기준선 계산이 정확해집니다. CI 시간/트래픽 증가가 미미하다면 그대로 유지해도 무방합니다.

src/main/resources/db/seed/dev/V4__dev_add_story_data.sql (1)

3-5: 이미지 경로 통일로 더 나은 개발 환경 제공

image_key 값을 'story/dummy/' 경로로 통일하여 더 실제적인 dummy 이미지를 사용하도록 개선하였습니다. 이는 스토리 상세 조회 기능과 함께 더 나은 개발 환경을 제공할 것으로 보입니다.

src/main/resources/db/seed/local/V4__local_add_story_data.sql (1)

3-9: 환경간 이미지 경로 일관성 확보

로컬 환경의 시드 데이터도 개발 환경과 동일하게 'story/dummy/' 경로로 통일하여 환경간 일관성을 확보했습니다. 이는 개발 과정에서 환경간 차이로 인한 혼란을 줄이는 좋은 접근입니다.

src/main/resources/db/seed/dev/V2__dev_init_data.sql (2)

28-34: 응원(Cheer) 데이터의 이미지 경로 개선

'default.jpg'에서 'cheer/dummy/' 경로로 변경하여 더 구체적이고 실제적인 이미지 경로를 제공합니다. 이는 개발 환경에서 더 나은 시각적 피드백을 제공할 것입니다.


37-39: 기사(Article) 데이터의 이미지 경로 개선

기사 데이터의 이미지 경로도 'article/dummy/' 경로로 통일하여 전체적인 이미지 경로 구조를 일관성 있게 개선했습니다.

src/main/resources/db/seed/local/V2__local_init_data.sql (2)

28-34: 로컬 환경 응원 데이터의 이미지 경로 통일

개발 환경과 동일하게 'cheer/dummy/' 경로로 통일하여 환경간 일관성을 확보했습니다. 이는 개발 과정에서 환경 전환 시 발생할 수 있는 혼란을 줄입니다.


37-39: 로컬 환경 기사 데이터의 이미지 경로 통일

기사 데이터의 이미지 경로도 개발 환경과 동일하게 'article/dummy/' 경로로 통일했습니다. 전체적인 이미지 경로 구조의 일관성을 유지하는 좋은 접근입니다.

src/main/java/eatda/exception/BusinessErrorCode.java (1)

50-50: 스토리 미발견 에러의 HTTP 상태 코드 개선

STORY_NOT_FOUND 에러 코드에 명시적으로 HttpStatus.NOT_FOUND(404)를 설정하여 REST API 표준을 준수하도록 개선했습니다. 존재하지 않는 리소스에 대한 요청 시 적절한 HTTP 상태 코드를 반환하여 클라이언트가 더 명확하게 에러 상황을 파악할 수 있습니다.

src/main/java/eatda/controller/story/StoryController.java (3)

5-6: validation 어노테이션 import가 적절합니다.

Min, Max 어노테이션을 사용하여 size 파라미터의 범위를 적절히 제한했습니다.


35-38: pagination 기능 구현이 잘 되었습니다.

size 파라미터에 적절한 validation (최소 1, 최대 50)과 기본값 5를 설정했습니다. 서비스 레이어로 파라미터를 전달하는 것도 올바릅니다.


40-44: 새로운 상세 조회 엔드포인트가 잘 구현되었습니다.

PathVariable을 사용하여 storyId를 받고, 서비스 레이어의 getStory 메소드를 호출하는 구조가 적절합니다.

src/main/java/eatda/service/story/StoryService.java (2)

70-71: 동적 페이지 크기 지원이 잘 구현되었습니다.

하드코딩된 PAGE_SIZE 대신 파라미터로 받은 size를 사용하여 유연성이 향상되었습니다.


84-97: 새로운 스토리 상세 조회 기능이 잘 구현되었습니다.

  • @Transactional(readOnly = true) 어노테이션 적절히 사용
  • Optional을 사용한 예외 처리가 올바름
  • StoryResponse 객체 생성 시 필요한 모든 필드 포함
  • imageService를 통한 presigned URL 생성 로직 적절함
src/test/java/eatda/service/story/StoryServiceTest.java (3)

38-41: 테스트 초기화가 개선되었습니다.

@beforeeach 메소드를 사용하여 중복된 초기화 코드를 제거하고 테스트 구조를 개선했습니다.


106-117: 기존 테스트 업데이트가 적절합니다.

  • size 파라미터(5) 추가로 새로운 메소드 시그니처에 맞춤
  • 이미지 URL 변경사항 반영
  • containsExactlyInAnyOrder 사용으로 순서에 무관한 검증 수행

121-161: 새로운 GetStory 테스트 클래스가 잘 구현되었습니다.

  • 성공 시나리오: 모든 필드를 검증하여 완전성 확보
  • 실패 시나리오: 존재하지 않는 ID에 대한 예외 처리 검증
  • 테스트 데이터가 현실적이고 의미있음
  • 이미지 서비스 모킹 적절함
src/test/java/eatda/document/story/StoryDocumentTest.java (2)

163-171: 기존 문서화 테스트 업데이트가 적절합니다.

getPagedStoryPreviews 메소드에 size 파라미터(5)를 추가하고 요청에 queryParam을 포함시켜 새로운 API 스펙을 올바르게 반영했습니다.


179-253: 새로운 스토리 상세 조회 API 문서화가 완벽합니다.

  • 요청/응답 메타데이터 완전함
  • 모든 응답 필드에 대한 설명 포함
  • 성공 시나리오에서 모든 필드 검증
  • 실패 시나리오에서 적절한 에러 응답 검증
  • RestDocs 문서 생성을 위한 설정 완료
src/test/java/eatda/controller/story/StoryControllerTest.java (3)

29-53: 테스트 구조 개선이 잘 되었습니다.

nested 클래스로 테스트를 구성하여 가독성과 유지보수성이 향상되었습니다.


55-80: 스토리 목록 조회 테스트가 적절합니다.

  • size 파라미터(5) 추가로 새로운 API 스펙 반영
  • 모킹된 응답 데이터 검증
  • 응답 구조와 데이터 검증 적절함

82-130: 새로운 스토리 상세 조회 테스트가 완벽합니다.

  • 성공 시나리오: 모든 응답 필드 검증
  • 실패 시나리오: 404 응답과 에러 메시지 검증
  • pathParam 사용으로 RESTful API 패턴 올바르게 테스트
  • 모킹과 검증 로직 적절함

Copy link
Member

@leegwichan leegwichan left a comment

Choose a reason for hiding this comment

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

/noti 정말 API 공장처럼 찍어내고 계시네요 ㅋㅋ
제안 사항, 수정 사항 남겼으니 확인 부탁드릴께요!


@GetMapping("api/stories")
public ResponseEntity<StoriesResponse> getStories() {
public ResponseEntity<StoriesResponse> getStories(@RequestParam(defaultValue = "5") @Min(1) @Max(50) int size) {
Copy link
Member

Choose a reason for hiding this comment

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

👍

Comment on lines +70 to +71
public StoriesResponse getPagedStoryPreviews(int size) {
Pageable pageable = PageRequest.of(PAGE_START_NUMBER, size);
Copy link
Member

Choose a reason for hiding this comment

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

[제안] Pageable에 이런 게 있더라구요!

Suggested change
public StoriesResponse getPagedStoryPreviews(int size) {
Pageable pageable = PageRequest.of(PAGE_START_NUMBER, size);
public StoriesResponse getPagedStoryPreviews(int size) {
Pageable pageable = PageRequest.of(Pageable.ofSize(size));

Copy link
Member Author

Choose a reason for hiding this comment

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

좋은 제안 감사합니다!
컴파일 에러가 나는데 어엄.. 이 방식을 말씀하신거겠죠??
Pageable pageable = Pageable.ofSize(size);

1차 배포후에 다른 페이지가 필요하다고 할수도 있으니... 지금은 이대로 가겠습니다!

Copy link
Member

Choose a reason for hiding this comment

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

  • V2 파일 바뀌었으니, 머지하지 전에 개발 DB 리셋 한 번 부탁드려요!
  • 그리고 개발 DB 리셋하면 FE 한테도 알려주면 좋을 것 같아요!

Comment on lines 38 to 41
@BeforeEach
void setUp() {
storyService = new StoryService(storeService, imageService, storyRepository, memberRepository);
}
Copy link
Member

Choose a reason for hiding this comment

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

해당 부분 없어도 테스트 정상 작동 하지 않나요?

Comment on lines 215 to 227
Response response = given(document)
.pathParam("storyId", storyId)
.when()
.get("/api/stories/{storyId}");

response.then()
.statusCode(200)
.body("storeKakaoId", equalTo("123456"))
.body("category", equalTo("한식"))
.body("storeName", equalTo("진또곱창집"))
.body("storeAddress", equalTo("서울특별시 성동구 성수동1가"))
.body("description", equalTo("곱창은 여기"))
.body("imageUrl", equalTo("https://s3.bucket.com/story1.jpg"));
Copy link
Member

Choose a reason for hiding this comment

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

[제안] Document Test는 문서 작성을 위해서 만드는 테스트여서 statusCode(200) 까지만 검증해도 좋을 것 같은데, 승로님은 어떻게 생각하시나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

좋은 의견 감사합니다! 저도 문서 테스트의 검증 범위에 대해 다시 한번 생각해볼 수 있었는데요.

현재 컨트롤러 테스트가 별도로 존재하긴 하지만, 문서 테스트에서 응답 필드에 대한 상세 검증을 제거하면,
API 명세가 변경되었을 때 컨트롤러 테스트만 수정되고 문서는 갱신되지 않아
실제 구현과 문서 간 불일치가 생길 수 있어 추가를 해보았습니다ㅋㅋㅋ

약간의 중복은 있더라도, 문서 신뢰성과 구현 일치성을 동시에 보장할 수 있다는 점에서 지금 구조도 나쁘지 않은 선택인 것 같아요 😄

Comment on lines 69 to 79
Response response = given()
.queryParam("size", 5)
.when()
.get("/api/stories");

response.then()
.statusCode(200)
.body("stories.size()", equalTo(2))
.body("stories[0].storyId", equalTo(1))
.body("stories[0].imageUrl", equalTo("https://s3.bucket.com/story/dummy/1.jpg"));
}
Copy link
Member

Choose a reason for hiding this comment

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

이런 방식도 있어요!

Suggested change
Response response = given()
.queryParam("size", 5)
.when()
.get("/api/stories");
response.then()
.statusCode(200)
.body("stories.size()", equalTo(2))
.body("stories[0].storyId", equalTo(1))
.body("stories[0].imageUrl", equalTo("https://s3.bucket.com/story/dummy/1.jpg"));
}
StoriesResponse response = given()
.queryParam("size", 5)
.when()
.get("/api/stories")
.then().statusCode(200)
.extract().as(StoriesResponse.class);
assertAll(
() -> assertThat(response.stories()).hasSize(2),
....
);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

오 DTO로 역직렬화 해서 사용하는 방식이군요!
이게 더 깔끔하고 좋은것 같아요!!

Comment on lines 187 to 195
RestDocsResponse responseDocument = response()
.responseBodyField(
fieldWithPath("storeKakaoId").description("가게의 카카오 ID"),
fieldWithPath("category").description("가게 카테고리"),
fieldWithPath("storeName").description("가게 이름"),
fieldWithPath("storeAddress").description("가게 주소"),
fieldWithPath("description").description("스토리 내용"),
fieldWithPath("imageUrl").description("스토리 이미지 URL")
);
Copy link
Member

Choose a reason for hiding this comment

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

[수정 필요] Address 풀 텍스트 보다는 "district", "neighborhood"로 나누어 주는게 좋지 않을까요?
Image

Copy link
Member Author

Choose a reason for hiding this comment

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

반...여.ㅇ했스빈다...

@github-actions
Copy link

📌 최신 ERD가 자동 생성되었습니다.

👉 ERD 보러가기

@sonarqubecloud
Copy link

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

🧹 Nitpick comments (2)
src/main/resources/db/migration/V3__add_story_table.sql (1)

1-14: 스토리 테이블 스키마가 주소 분리 요구사항에 맞게 잘 설계되었습니다.

도로명 주소와 지번 주소를 분리한 스키마 설계가 적절하며, 모든 필드의 데이터 타입과 제약 조건이 합리적입니다. 다만 member_id에 대한 외래키 제약 조건 추가를 고려해보시기 바랍니다.

외래키 제약 조건을 추가하여 데이터 무결성을 보장하는 것을 고려해보세요:

ALTER TABLE `story` ADD CONSTRAINT `fk_story_member_id` 
FOREIGN KEY (`member_id`) REFERENCES `member` (`id`) ON DELETE CASCADE;
src/main/java/eatda/domain/story/Story.java (1)

148-162: 주소 파싱 메서드의 견고성을 개선해주세요.

현재 구현은 기본적인 경계 검사를 포함하고 있지만, 다음과 같은 개선사항을 고려해보세요:

  1. 중복 코드 제거: 두 메서드 모두 동일한 문자열 분할 로직을 사용합니다.
  2. 매직 넘버 제거: 하드코딩된 인덱스(1, 2)를 상수로 정의해주세요.
  3. 반환값 개선: 빈 문자열 대신 Optional<String>을 사용하는 것을 고려해보세요.
  4. 주소 형식 검증: 예상되는 주소 형식에 대한 문서화나 검증을 추가해주세요.
+    private static final int DISTRICT_INDEX = 1;
+    private static final int NEIGHBORHOOD_INDEX = 2;
+    
+    private String[] parseAddressParts() {
+        return storeLotNumberAddress.split("\\s+");
+    }
+    
     public String getAddressDistrict() {
-        String[] addressParts = storeLotNumberAddress.split(" ");
-        if (addressParts.length < 2) {
+        String[] addressParts = parseAddressParts();
+        if (addressParts.length <= DISTRICT_INDEX) {
             return "";
         }
-        return addressParts[1];
+        return addressParts[DISTRICT_INDEX];
     }

     public String getAddressNeighborhood() {
-        String[] addressParts = storeLotNumberAddress.split(" ");
-        if (addressParts.length < 3) {
+        String[] addressParts = parseAddressParts();
+        if (addressParts.length <= NEIGHBORHOOD_INDEX) {
             return "";
         }
-        return addressParts[2];
+        return addressParts[NEIGHBORHOOD_INDEX];
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1547759 and a65f46b.

📒 Files selected for processing (11)
  • src/main/java/eatda/controller/story/FilteredSearchResult.java (1 hunks)
  • src/main/java/eatda/controller/story/StoryResponse.java (1 hunks)
  • src/main/java/eatda/domain/story/Story.java (5 hunks)
  • src/main/java/eatda/service/story/StoryService.java (4 hunks)
  • src/main/resources/db/migration/V3__add_story_table.sql (1 hunks)
  • src/main/resources/db/seed/dev/V4__dev_add_story_data.sql (1 hunks)
  • src/main/resources/db/seed/local/V4__local_add_story_data.sql (1 hunks)
  • src/test/java/eatda/controller/story/StoryControllerTest.java (3 hunks)
  • src/test/java/eatda/document/story/StoryDocumentTest.java (3 hunks)
  • src/test/java/eatda/domain/story/StoryTest.java (8 hunks)
  • src/test/java/eatda/service/story/StoryServiceTest.java (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/resources/db/seed/dev/V4__dev_add_story_data.sql
  • src/test/java/eatda/service/story/StoryServiceTest.java
  • src/main/java/eatda/service/story/StoryService.java
  • src/test/java/eatda/document/story/StoryDocumentTest.java
  • src/test/java/eatda/controller/story/StoryControllerTest.java
⏰ 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). (1)
  • GitHub Check: test
🔇 Additional comments (10)
src/main/resources/db/seed/local/V4__local_add_story_data.sql (1)

1-22: 주소 필드 분리 및 더미 데이터 업데이트가 적절하게 구현되었습니다.

단일 주소 필드를 도로명 주소와 지번 주소로 분리한 스키마 변경에 맞춰 시드 데이터가 정확하게 업데이트되었습니다. 이미지 키도 story/preview/에서 story/dummy/로 일관되게 변경되어 실제 이미지 파일과의 매핑이 개선되었습니다.

src/main/java/eatda/controller/story/StoryResponse.java (1)

3-12: 주소 필드를 의미론적으로 명확한 구/동 단위로 분리한 개선사항입니다.

단일 storeAddress 필드를 storeDistrictstoreNeighborhood로 분리하여 클라이언트에서 위치 정보를 더 세밀하게 활용할 수 있도록 개선되었습니다. 이는 UI에서 지역별 필터링이나 표시에 유용할 것입니다.

src/main/java/eatda/controller/story/FilteredSearchResult.java (1)

3-10: 도메인 모델과 일관성 있는 주소 필드 분리가 적용되었습니다.

단일 address 필드를 roadAddresslotNumberAddress로 분리하여 Story 엔티티의 구조와 일치하도록 개선되었습니다. 이는 검색 결과에서 더 상세한 주소 정보를 제공할 수 있게 합니다.

src/test/java/eatda/domain/story/StoryTest.java (3)

21-35: 도메인 모델 변경에 맞춰 테스트가 적절히 업데이트되었습니다.

Story 생성 테스트에서 분리된 주소 필드(storeRoadAddress, storeLotNumberAddress)를 정확히 사용하고 있으며, 실제 한국 주소 형식을 사용한 테스트 데이터가 현실적입니다.


97-128: 주소 필드 분리에 따른 검증 로직이 완전히 테스트되었습니다.

기존 단일 주소 검증 테스트를 도로명 주소와 지번 주소 각각에 대한 별도 테스트로 분리하여 두 필드 모두 필수값임을 확인하고 있습니다. 테스트 메서드명도 명확하게 작성되어 테스트 의도를 잘 표현하고 있습니다.


42-56: 모든 검증 테스트가 새로운 주소 필드 구조에 맞게 업데이트되었습니다.

회원 검증, 가게 정보 검증, 스토리 검증 등 모든 테스트 케이스에서 분리된 주소 필드를 일관되게 사용하고 있어 테스트 커버리지가 잘 유지되고 있습니다.

Also applies to: 63-94, 131-184

src/main/java/eatda/domain/story/Story.java (4)

41-45: 주소 필드 분리가 적절하게 구현되었습니다.

도로명 주소와 지번 주소를 별도 필드로 분리하여 더 세밀한 주소 정보 관리가 가능해졌습니다. 컬럼 매핑과 필수 제약 조건 설정이 올바르게 적용되었습니다.


57-79: 생성자 업데이트가 올바르게 구현되었습니다.

새로운 주소 필드들을 포함하도록 생성자가 적절히 수정되었으며, 매개변수 순서와 필드 할당이 논리적으로 구성되었습니다.


87-99: 검증 메서드 리팩토링이 깔끔하게 수행되었습니다.

두 개의 주소 필드를 각각 검증하도록 메서드가 적절히 업데이트되었으며, 기존 검증 패턴을 일관되게 유지하고 있습니다.


118-128: 주소 검증 메서드들이 일관되게 구현되었습니다.

두 주소 유형에 대한 별도 검증 메서드가 동일한 패턴으로 구현되었으며, 기존 검증 메서드들과 일관성을 유지하고 있습니다.

@lvalentine6 lvalentine6 merged commit 65390cc into develop Jul 18, 2025
10 checks passed
@lvalentine6 lvalentine6 deleted the feat/PRODUCT-185 branch July 18, 2025 18:39
@github-actions
Copy link

🎉 This PR is included in version 1.4.0-develop.24 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions
Copy link

🎉 This PR is included in version 1.5.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants