Skip to content

feat: 스토리 조회 구현 (#85)#106

Merged
wkdtnqls0506 merged 11 commits intomainfrom
feature/PRODUCT-182
Jul 31, 2025
Merged

feat: 스토리 조회 구현 (#85)#106
wkdtnqls0506 merged 11 commits intomainfrom
feature/PRODUCT-182

Conversation

@wkdtnqls0506
Copy link
Contributor

@wkdtnqls0506 wkdtnqls0506 commented Jul 30, 2025

✅ 이슈 번호

close #85


🪄 작업 내용 (변경 사항)

  • 고유한 memberId에 맞게 랜덤 아바타 배정
  • 스토리 상세 조회 페이지 구현

📸 스크린샷

2025-07-30.8.10.53.1.mov

💡 설명

랜덤 아바타

  • 고유한 memberId를 기준으로 고정된 색상이 부여되도록 Math.abs(memberId) % N을 사용해 색상 인덱스를 계산했습니다.
  • 색상은 PROFILE_COLORS 상수로 관리하며, 피그마에서 정의된 3가지 색상(yellow, pink, blue)을 기준으로 선언해두었습니다~!
  • 아바타 배경색은 Avatar 컴포넌트에서 해당 인덱스를 기반으로 지정됩니다.
    • 간단한 사용법을 Avatar 컴포넌트의 JSDoc에 남겨놨습니다~!

스토리 상세 조회

  • 스크린샷을 확인해주세요!
  • /story/[id]는 스토리 데이터가 필수인 페이지이기 때문에, 진입 시 서버 컴포넌트 app/story/[id]/page.tsx에서 queryClient.prefetchQuery로 데이터를 사전 fetch 하도록 구성했습니다~!
  • 이렇게 가져온 데이터를 HydrationBoundary로 감싸 클라이언트에 전달하고, 클라이언트 측에서는 useSuspenseQuery를 통해 데이터를 사용할 수 있게 했습니다.

🗣️ 리뷰어에게 전달 사항


📍 트러블 슈팅

Summary by CodeRabbit

Summary by CodeRabbit

  • 신규 기능

    • 스토리 상세 정보를 보여주는 StoryDetailContent 컴포넌트가 추가되었습니다.
    • 스토리 상세 페이지에서 서버 사이드 데이터 프리패칭 및 클라이언트 하이드레이션이 적용되었습니다.
    • 멤버 아바타(Avatar) 컴포넌트가 추가되어, 사용자별 고유 색상과 아이콘이 표시됩니다.
  • 스타일

    • 스토리 상세 콘텐츠 및 아바타에 대한 새로운 CSS 스타일이 적용되었습니다.
  • 버그 수정

    • 일부 애니메이션 라이브러리 의존성이 교체되어 동작이 개선되었습니다.
  • 기타

    • 스토리 상세 조회를 위한 API, 쿼리, 타입 및 유틸리티 함수가 추가되었습니다.
    • 내부 상수 및 유틸리티 함수의 모듈 구조가 개선되었습니다.
    • HTTP 클라이언트 변경으로 일부 API 요청 방식이 수정되었습니다.

@wkdtnqls0506 wkdtnqls0506 added the ✨ feature 새로운 기능 추가 label Jul 30, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 30, 2025

"""

Walkthrough

스토리 상세 조회 기능이 새로 구현되었습니다. 서버에서 스토리 데이터를 미리 패칭하고, 클라이언트에서 하이드레이션 및 Suspense로 상세 내용을 표시합니다. 아바타, 스타일, 유틸, 쿼리, 타입, API 등 관련 파일이 추가·수정되었으며, motion 라이브러리 교체 및 기타 리팩토링이 포함되어 있습니다.

Changes

Cohort / File(s) Change Summary
스토리 상세 API 및 쿼리 도입
src/app/story/[id]/_api/detail.api.ts, src/app/story/[id]/_api/detail.queries.ts, src/app/story/[id]/_api/detail.types.ts, src/app/story/[id]/_api/index.ts
스토리 상세 정보를 가져오는 API, 타입, 쿼리 옵션 및 쿼리 키 정의 파일 추가 및 index로 통합
스토리 상세 컴포넌트 및 페이지
src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx, src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts, src/app/story/[id]/_components/StoryDetailContent/index.ts, src/app/story/[id]/page.tsx
스토리 상세 조회 UI 컴포넌트, 스타일, 인덱스 파일 및 서버 컴포넌트 페이지 구현. 서버에서 데이터 패칭 및 하이드레이션 처리
아바타 컴포넌트 및 프로필 색상 유틸
src/app/member/_components/Avatar/Avatar.tsx, src/app/member/_components/Avatar/Avatar.css.ts, src/app/member/_components/Avatar/index.ts, src/app/member/_constants/profileColors.constants.ts, src/app/member/_constants/index.ts, src/app/member/_utils/profileUtils.ts, src/app/member/_utils/index.ts
아바타 컴포넌트, 프로필 색상 상수, 색상 인덱스 유틸, 스타일 및 관련 인덱스 파일 추가 및 리팩토링
외부 라이브러리 및 import 변경
package.json, src/app/(search)/_components/SearchStoreBottomSheet/SearchStoreBottomSheet.tsx
framer-motion → motion 패키지로 변경, 관련 import 경로 수정
기타 리소스/스타일 import 변경
src/app/member/profile/_components/Profile/ProfileLayout.tsx
SVG 리소스 import 경로 변경 (front-bapurit.svg → symbol.svg)

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant StoryDetailPage (Server)
    participant API
    participant HydrationBoundary
    participant StoryDetailContent (Client)
    participant QueryClient

    User->>StoryDetailPage (Server): 페이지 접속 (스토리 id 포함)
    StoryDetailPage->>API: getStoryDetail(storyId) 호출
    API-->>StoryDetailPage: StoryDetailResponse 반환
    StoryDetailPage->>HydrationBoundary: dehydrated state 전달
    HydrationBoundary->>StoryDetailContent (Client): Suspense로 감싸서 렌더링
    StoryDetailContent->>QueryClient: useSuspenseQuery로 데이터 요청
    QueryClient-->>StoryDetailContent: 하이드레이션된 데이터 반환
    StoryDetailContent-->>User: 상세 화면 렌더링
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
스토리 상세 조회 화면 구현 (#85)

Assessment against linked issues: Out-of-scope changes

(해당 변경사항에서 요구사항과 무관한 기능적 코드 변경은 발견되지 않았습니다.)

Possibly related PRs

  • feat: 가게 검색 컴포넌트 생성 (#98) #99: framer-motion 의존성을 추가하고 검색 하단 시트 컴포넌트를 구현한 PR로, 본 PR과 animation 라이브러리 및 검색 컴포넌트 관련 코드 변경에서 연관성이 있음.

Poem

🐰
스토리 펼치면 빛나는 밤,
아바타와 색상이 어울린다네.
motion으로 부드럽게,
데이터는 서버에서 미리 준비해,
토끼는 기뻐 깡총깡총,
새 화면에서 이야기 꽃을 피운다!

"""

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c379a7c and 85800ae.

⛔ Files ignored due to path filters (1)
  • src/assets/market-fill.svg is excluded by !**/*.svg
📒 Files selected for processing (1)
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx
⏰ 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
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/PRODUCT-182

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 generate unit tests to generate unit tests for 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.

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

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
Copy link

github-actions bot commented Jul 30, 2025

🎨 Storybook Preview: https://685a32a1c0bbd269fdb67af4-jasvxeveay.chromatic.com/
🔗 Chromatic Build: https://www.chromatic.com/build?appId=685a32a1c0bbd269fdb67af4&number=158
🕖 Updated at: 2025년 07월 31일 01시 26분 34초

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

🧹 Nitpick comments (8)
src/app/member/_constants/profileColors.constants.ts (2)

1-14: 숫자 인덱스를 문자열 리터럴로 추론하는 타입 이슈

keyof typeof PROFILE_COLORS"0" | "1" | "2"(문자열) 유니온을 생성합니다. 실제 사용처가 숫자 인덱스를 기대한다면 타입 불일치가 생깁니다. 배열 형태로 선언하면 타입·런타임 모두 간단히 맞출 수 있습니다.

-export const PROFILE_COLORS = {
-  0: { background: "#FFF1AF", name: "yellow" },
-  1: { background: "#FFDDDA", name: "pink" },
-  2: { background: "#C9ECFF", name: "blue" },
-} as const;
+export const PROFILE_COLORS = [
+  { background: "#FFF1AF", name: "yellow" },
+  { background: "#FFDDDA", name: "pink" },
+  { background: "#C9ECFF", name: "blue" },
+] as const;

19-19: PROFILE_COLOR_COUNT는 상수이므로 직접 리터럴을 써도 트리에이킹에 유리

컴파일 타임에 3으로 결정되는 값이므로 const PROFILE_COLOR_COUNT = 3 으로 두면 번들러가 최적화하기 쉽습니다.

src/app/member/_utils/profileUtils.ts (1)

10-12: 타입 캐스팅 대신 더 안전한 방법을 고려하세요.

현재 as ProfileColorIndex로 타입 캐스팅을 사용하고 있는데, 이는 런타임에서 타입 안전성을 보장하지 못합니다.

더 안전한 구현을 위해 다음과 같이 개선할 수 있습니다:

export const getProfileColorIndex = (memberId: number): ProfileColorIndex => {
-  return (Math.abs(memberId) % PROFILE_COLOR_COUNT) as ProfileColorIndex;
+  const index = Math.abs(memberId) % PROFILE_COLOR_COUNT;
+  return index as ProfileColorIndex;
};

또는 런타임 검증을 추가:

export const getProfileColorIndex = (memberId: number): ProfileColorIndex => {
  const index = Math.abs(memberId) % PROFILE_COLOR_COUNT;
+  if (!(index in PROFILE_COLORS)) {
+    throw new Error(`Invalid profile color index: ${index}`);
+  }
  return index as ProfileColorIndex;
};
src/app/member/_components/Avatar/Avatar.tsx (1)

32-39: 스타일링 일관성과 접근성을 개선해보세요.

현재 CSS 모듈과 인라인 스타일을 혼합 사용하고 있고, 접근성을 위한 속성이 누락되었습니다.

다음과 같이 개선할 수 있습니다:

    <div
      className={`${styles.avatar} ${className || ""}`}
      style={{
        backgroundColor: colorConfig.background,
      }}
+      role="img"
+      aria-label={`${memberNickname || '사용자'}의 프로필 아바타`}
    >
      <Bapurit width={23} height={23} />
    </div>

또는 CSS 변수를 사용하여 스타일링을 일관성 있게 관리:

    <div
-      className={`${styles.avatar} ${className || ""}`}
-      style={{
-        backgroundColor: colorConfig.background,
-      }}
+      className={`${styles.avatar} ${styles[colorConfig.name]} ${className || ""}`}
    >
src/app/story/[id]/page.tsx (1)

22-22: 에러 처리 추가를 고려해보세요.

prefetchQuery에서 실패할 경우에 대한 처리가 없습니다. 네트워크 오류나 API 에러 시 사용자 경험을 개선할 수 있습니다.

다음과 같이 에러 처리를 추가할 수 있습니다:

  const queryClient = getQueryClient();

+  try {
    await queryClient.prefetchQuery(storyDetailQueryOptions(storyId));
+  } catch (error) {
+    // 프리페치 실패 시에도 클라이언트에서 재시도할 수 있도록 허용
+    console.warn('Story detail prefetch failed:', error);
+  }

또는 에러 바운더리와 함께 사용:

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
+      <ErrorBoundary fallback={<StoryDetailErrorFallback />}>
        <Suspense>
          <StoryDetailContent storyId={storyId} />
        </Suspense>
+      </ErrorBoundary>
    </HydrationBoundary>
  );
src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts (1)

101-101: CSS 속성값에 불필요한 공백이 있습니다.

background 속성값 앞에 불필요한 공백이 있습니다.

-  background: " rgba(0, 0, 0, 0.28)",
+  background: "rgba(0, 0, 0, 0.28)",
src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx (2)

74-74: 옵셔널 체이닝 사용이 불필요합니다.

이미 useSuspenseQuery를 사용하고 있어 data가 보장되므로 옵셔널 체이닝(?.)이 불필요합니다.

-          {story?.description && (
+          {story.description && (

102-102: TODO 주석이 있습니다.

가게 상세페이지 이동 기능이 아직 구현되지 않았습니다. 이를 추적할 이슈를 생성하는 것이 좋겠습니다.

이 TODO를 해결하는 이슈를 생성해드릴까요?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 227675e and 615fedc.

⛔ Files ignored due to path filters (5)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/assets/location.svg is excluded by !**/*.svg
  • src/assets/logo/symbol.svg is excluded by !**/*.svg
  • src/assets/market.svg is excluded by !**/*.svg
  • src/assets/notification.svg is excluded by !**/*.svg
📒 Files selected for processing (18)
  • package.json (1 hunks)
  • src/app/(search)/_components/SearchStoreBottomSheet/SearchStoreBottomSheet.tsx (1 hunks)
  • src/app/member/_components/Avatar/Avatar.css.ts (1 hunks)
  • src/app/member/_components/Avatar/Avatar.tsx (1 hunks)
  • src/app/member/_components/Avatar/index.ts (1 hunks)
  • src/app/member/_constants/index.ts (1 hunks)
  • src/app/member/_constants/profileColors.constants.ts (1 hunks)
  • src/app/member/_utils/index.ts (1 hunks)
  • src/app/member/_utils/profileUtils.ts (1 hunks)
  • src/app/member/profile/_components/Profile/ProfileLayout.tsx (1 hunks)
  • src/app/story/[id]/_api/detail.api.ts (1 hunks)
  • src/app/story/[id]/_api/detail.queries.ts (1 hunks)
  • src/app/story/[id]/_api/detail.types.ts (1 hunks)
  • src/app/story/[id]/_api/index.ts (1 hunks)
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts (1 hunks)
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx (1 hunks)
  • src/app/story/[id]/_components/StoryDetailContent/index.ts (1 hunks)
  • src/app/story/[id]/page.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
{src/app/**/_components/**/*.tsx,src/components/**/*.tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Component files must use PascalCase naming (e.g., Button.tsx, DomainLayout.tsx).

Files:

  • src/app/member/profile/_components/Profile/ProfileLayout.tsx
  • src/app/(search)/_components/SearchStoreBottomSheet/SearchStoreBottomSheet.tsx
  • src/app/member/_components/Avatar/Avatar.tsx
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

All files must use TypeScript.

Files:

  • src/app/member/profile/_components/Profile/ProfileLayout.tsx
  • src/app/member/_components/Avatar/index.ts
  • src/app/story/[id]/_api/index.ts
  • src/app/member/_utils/index.ts
  • src/app/(search)/_components/SearchStoreBottomSheet/SearchStoreBottomSheet.tsx
  • src/app/member/_components/Avatar/Avatar.css.ts
  • src/app/story/[id]/_api/detail.api.ts
  • src/app/member/_constants/index.ts
  • src/app/member/_utils/profileUtils.ts
  • src/app/story/[id]/_components/StoryDetailContent/index.ts
  • src/app/member/_components/Avatar/Avatar.tsx
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts
  • src/app/story/[id]/page.tsx
  • src/app/member/_constants/profileColors.constants.ts
  • src/app/story/[id]/_api/detail.queries.ts
  • src/app/story/[id]/_api/detail.types.ts
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx
{src/lib/utils/*.ts,src/lib/api/*.ts,src/app/**/_utils/*.ts,src/app/**/_api/*.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Utility and API files must use camelCase naming (e.g., domainUtils.ts, domain.api.ts).

Files:

  • src/app/story/[id]/_api/index.ts
  • src/app/member/_utils/index.ts
  • src/app/story/[id]/_api/detail.api.ts
  • src/app/member/_utils/profileUtils.ts
  • src/app/story/[id]/_api/detail.queries.ts
  • src/app/story/[id]/_api/detail.types.ts
{src/styles/**/*.css.ts,src/app/**/_components/**/*.css.ts,src/components/**/*.css.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Style files must use camelCase naming with the .css.ts extension (e.g., Button.css.ts, theme.css.ts).

Files:

  • src/app/member/_components/Avatar/Avatar.css.ts
  • src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts
src/app/**/_api/*.{api,queries}.ts

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

API calls should be implemented in .api.ts files using ky, and queries in .queries.ts files using TanStack Query, both located in _api folders.

Files:

  • src/app/story/[id]/_api/detail.api.ts
  • src/app/story/[id]/_api/detail.queries.ts
{src/constants/*.ts,src/app/**/_constants/*.ts}

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Constant files must use camelCase naming (e.g., domain.constants.ts).

Files:

  • src/app/member/_constants/index.ts
  • src/app/member/_constants/profileColors.constants.ts
src/app/**/_constants/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/nextjs-folder-structure.mdc)

Domain-specific constants should be defined in _constants folders.

Files:

  • src/app/member/_constants/index.ts
  • src/app/member/_constants/profileColors.constants.ts
🧠 Learnings (9)
src/app/member/profile/_components/Profile/ProfileLayout.tsx (2)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/app//_components//.tsx,src/components/**/.tsx} : Component files must use PascalCase naming (e.g., Button.tsx, DomainLayout.tsx).

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/app/**/_components/.css.ts,src/components/.css.ts} : Component-specific styles should be co-located with the component file.

src/app/story/[id]/_api/index.ts (5)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API calls should be implemented in .api.ts files using ky, and queries in .queries.ts files using TanStack Query, both located in _api folders.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/lib/api/*.ts : Global API client configuration and types should be placed in lib/api/.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/app/**/_types/*.ts : Domain-specific types should be defined in _types folders.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/lib/utils/*.ts : Global utility functions should be placed in lib/utils/ and separated by functionality.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/lib/utils/.ts,src/lib/api/.ts,src/app//_utils/*.ts,src/app//_api/*.ts} : Utility and API files must use camelCase naming (e.g., domainUtils.ts, domain.api.ts).

src/app/member/_utils/index.ts (3)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/lib/utils/*.ts : Global utility functions should be placed in lib/utils/ and separated by functionality.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/lib/utils/.ts,src/lib/api/.ts,src/app//_utils/*.ts,src/app//_api/*.ts} : Utility and API files must use camelCase naming (e.g., domainUtils.ts, domain.api.ts).

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/lib/api/*.ts : Global API client configuration and types should be placed in lib/api/.

src/app/member/_components/Avatar/Avatar.css.ts (3)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/app/**/_components/.css.ts,src/components/.css.ts} : Component-specific styles should be co-located with the component file.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/styles/*.css.ts : Global styles should be placed in the styles/ folder, using vanilla-extract.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/styles//*.css.ts,src/app//_components//*.css.ts,src/components//*.css.ts} : Style files must use camelCase naming with the .css.ts extension (e.g., Button.css.ts, theme.css.ts).

src/app/member/_constants/index.ts (3)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/app/**/_constants/*.ts : Domain-specific constants should be defined in _constants folders.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/constants/.ts,src/app/**/_constants/.ts} : Constant files must use camelCase naming (e.g., domain.constants.ts).

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/lib/api/*.ts : Global API client configuration and types should be placed in lib/api/.

src/app/story/[id]/_components/StoryDetailContent/index.ts (1)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/app/**/_components/.css.ts,src/components/.css.ts} : Component-specific styles should be co-located with the component file.

src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts (3)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/app/**/_components/.css.ts,src/components/.css.ts} : Component-specific styles should be co-located with the component file.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/styles/*.css.ts : Global styles should be placed in the styles/ folder, using vanilla-extract.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/styles//*.css.ts,src/app//_components//*.css.ts,src/components//*.css.ts} : Style files must use camelCase naming with the .css.ts extension (e.g., Button.css.ts, theme.css.ts).

src/app/member/_constants/profileColors.constants.ts (2)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/app/**/_constants/*.ts : Domain-specific constants should be defined in _constants folders.

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to {src/constants/.ts,src/app/**/_constants/.ts} : Constant files must use camelCase naming (e.g., domain.constants.ts).

src/app/story/[id]/_api/detail.queries.ts (1)

Learnt from: CR
PR: YAPP-Github/26th-Web-Team-1-FE#0
File: .cursor/rules/nextjs-folder-structure.mdc:0-0
Timestamp: 2025-07-20T14:21:47.408Z
Learning: Applies to src/app/**/_api/*.{api,queries}.ts : API calls should be implemented in .api.ts files using ky, and queries in .queries.ts files using TanStack Query, both located in _api folders.

🧬 Code Graph Analysis (5)
src/app/story/[id]/_api/detail.api.ts (2)
src/app/story/[id]/_api/detail.types.ts (1)
  • StoryDetailResponse (1-11)
src/lib/api/client.ts (1)
  • http (69-74)
src/app/member/_utils/profileUtils.ts (1)
src/app/member/_constants/profileColors.constants.ts (2)
  • ProfileColorIndex (16-16)
  • PROFILE_COLOR_COUNT (19-19)
src/app/member/_components/Avatar/Avatar.tsx (2)
src/app/member/_utils/profileUtils.ts (1)
  • getProfileColorIndex (10-12)
src/app/member/_constants/profileColors.constants.ts (1)
  • PROFILE_COLORS (1-14)
src/app/story/[id]/_api/detail.queries.ts (1)
src/app/story/[id]/_api/detail.api.ts (1)
  • getStoryDetail (10-16)
src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx (2)
src/app/story/[id]/_components/StoryDetailContent/index.ts (1)
  • StoryDetailContent (1-1)
src/app/story/[id]/_api/detail.queries.ts (1)
  • storyDetailQueryOptions (8-12)
⏰ 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 (22)
package.json (1)

33-33: framer-motion 임포트 잔여 없음 확인

rg -n "from\s+['\"]framer-motion['\"]" --glob '*.[tj]s*' 실행 결과 코드베이스에 from "framer-motion" 임포트가 전혀 남아 있지 않습니다.
package.json에서 "motion": "^12.23.11"로 정상 전환된 상태입니다.

src/app/(search)/_components/SearchStoreBottomSheet/SearchStoreBottomSheet.tsx (1)

4-4: 새 패키지 서브패스(motion/react)가 실제 배포본에 포함되는지 확인 필요

motion/react 서브패스는 ESM 전용 패키지에서만 제공됩니다. Next.js(webpack/RS) 설정, 트리 셰이킹, 타입 정의(@types/motion 미제공) 호환성까지 반드시 로컬 빌드로 검증해 주세요. 문제가 있으면 "motion" 루트에서 import 하거나 framer-motion을 유지하는 방안을 고려해야 합니다.

src/app/member/profile/_components/Profile/ProfileLayout.tsx (1)

5-5: 확인 완료: 레거시 참조 없음
전역 검색 결과 'front-bapurit' 문자열이 더 이상 존재하지 않아, symbol.svg로의 변경이 올바르게 적용된 것을 확인했습니다.

src/app/member/_constants/index.ts (1)

1-1: LGTM – 새 프로필 색상 상수 재-export 정상 추가

재사용성을 높이는 올바른 구성입니다.

src/app/member/_components/Avatar/index.ts (1)

1-1: LGTM! 표준적인 barrel export 패턴이 잘 적용되었습니다.

컴포넌트의 중앙 집중식 import를 위한 깔끔한 구조입니다.

src/app/story/[id]/_components/StoryDetailContent/index.ts (1)

1-1: LGTM! 일관된 barrel export 패턴이 적용되었습니다.

스토리 상세 컴포넌트의 중앙 집중식 export 구조가 적절합니다.

src/app/member/_components/Avatar/Avatar.css.ts (1)

1-13: LGTM! 아바타 스타일이 잘 정의되었습니다.

vanilla-extract를 사용한 깔끔한 스타일 정의와 공통 디자인 시스템(radius) 활용이 우수합니다. 컴포넌트별 스타일 co-location도 가이드라인에 맞게 적용되었습니다.

src/app/story/[id]/_api/index.ts (1)

1-3: LGTM! API 모듈 구조가 훌륭합니다.

API, 쿼리, 타입을 분리한 관심사 분리와 barrel export를 통한 중앙 집중식 접근이 코딩 가이드라인에 완벽히 부합합니다.

src/app/member/_utils/index.ts (1)

1-2: LGTM! 유틸리티 모듈 export가 일관되게 개선되었습니다.

wildcard export 사용으로 일관된 패턴을 적용하고, 새로운 profileUtils 추가로 기능이 확장되었습니다.

src/app/story/[id]/_api/detail.api.ts (1)

5-16: API 함수 구현이 잘 되었습니다.

JSDoc 문서화, 타입 안전성, HTTP 클라이언트 사용이 적절합니다. 코딩 가이드라인의 camelCase 네이밍도 준수되었습니다.

src/app/story/[id]/_api/detail.types.ts (1)

1-11: 타입 정의가 잘 구성되었습니다.

스토리 상세 정보를 나타내는 타입이 명확하게 정의되었고, nullable 필드(description)도 적절히 처리되었습니다. 네이밍 규칙도 잘 지켜졌습니다.

src/app/member/_components/Avatar/Avatar.tsx (1)

14-26: 훌륭한 JSDoc 문서화입니다.

컴포넌트의 목적, 동작 방식, 사용 예제가 명확하게 작성되어 있어 개발자 경험이 향상됩니다.

src/app/story/[id]/page.tsx (1)

15-31: 서버 사이드 프리페칭 패턴이 잘 구현되었습니다.

React Query의 prefetch, HydrationBoundary, Suspense를 활용한 현대적인 Next.js 패턴이 적절히 적용되었습니다.

src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.css.ts (2)

1-3: Import 구문이 올바르게 구성되어 있습니다.

vanilla-extract와 스타일 토큰 import가 적절하게 되어 있습니다.


5-12: 컨테이너 스타일이 적절합니다.

전체 뷰포트 높이와 다크 배경을 사용한 레이아웃이 스토리 상세 페이지에 적합합니다.

src/app/story/[id]/_api/detail.queries.ts (2)

3-6: 쿼리 키 구조가 잘 설계되어 있습니다.

계층적 쿼리 키 구조와 as const 사용으로 타입 안전성을 보장하고 있습니다.


8-12: 쿼리 옵션 설정이 적절합니다.

enabled 옵션으로 storyId가 존재할 때만 쿼리가 실행되도록 하여 불필요한 API 호출을 방지하고 있습니다.

src/app/story/[id]/_components/StoryDetailContent/StoryDetailContent.tsx (5)

1-17: Import 구문이 잘 구성되어 있습니다.

필요한 라이브러리와 컴포넌트들이 적절하게 import되어 있으며, motion/react 라이브러리 사용도 적절합니다.


25-30: 훅 사용이 적절합니다.

useSuspenseQuery를 통한 데이터 페칭과 useState를 통한 상태 관리가 올바르게 구현되어 있습니다.


46-54: 접근성이 잘 고려되어 있습니다.

버튼에 aria-label이 적절하게 설정되어 있어 스크린 리더 사용자를 위한 접근성이 확보되어 있습니다.


76-95: 애니메이션 구현이 우수합니다.

motion.div를 사용한 부드러운 높이 애니메이션과 적절한 easing 설정이 사용자 경험을 향상시킵니다.


121-131: AnimatePresence 사용이 적절합니다.

설명 확장 시 오버레이의 부드러운 페이드 인/아웃 애니메이션이 잘 구현되어 있습니다.

Copy link
Member

Choose a reason for hiding this comment

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

오오우,,!

@wkdtnqls0506 wkdtnqls0506 merged commit dddf245 into main Jul 31, 2025
6 checks passed
@wkdtnqls0506 wkdtnqls0506 deleted the feature/PRODUCT-182 branch July 31, 2025 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PRODUCT-182] 스토리 조회 구현

2 participants