Skip to content

[FEAT] 인플루언서 추천 피드 - 지현#284

Merged
ljh130334 merged 12 commits into
developfrom
feat/influencer
Oct 28, 2025
Merged

[FEAT] 인플루언서 추천 피드 - 지현#284
ljh130334 merged 12 commits into
developfrom
feat/influencer

Conversation

@ljh130334
Copy link
Copy Markdown
Member

@ljh130334 ljh130334 commented Oct 28, 2025

#️⃣ 연관된 이슈

#283

📝 작업 내용

개요

전체 피드 화면에 "지금 뜨는 추천 글" 섹션을 추가했습니다. 피드 10개마다 인플루언서가 추천하는 게시글 5개를 캐러셀 형태로 보여줍니다.

주요 기능

  1. 추천 섹션 레이아웃

    • 피드 10개마다 추천 글 섹션 반복 표시
    • 각 섹션마다 다른 5개의 추천 게시글 노출 (15개 목업 데이터 순환)
    • 헤더와 서브 텍스트로 섹션 설명 제공
  2. 캐러셀 구현

    • Embla Carousel 라이브러리를 사용한 수평 스크롤
    • 중앙 정렬 방식으로 다음 카드가 살짝 보이는 UX
    • 터치/마우스 드래그 지원
  3. 추천 피드 카드

    • 기존 PostHeader, PostBody, PostFooter 컴포넌트 재사용
    • 인플루언서 전용 lookmore 아이콘 적용 (lookmore-influencer.svg)
    • 북마크 아이콘 숨김 처리
    • 본문 텍스트 3줄 제한 (-webkit-line-clamp: 3)
    • 카드 전체 클릭 시 피드 상세 페이지로 이동
    • 책 카드 클릭 이벤트 무효화 (pointer-events: none)
  4. 데이터 구조

    • 목업 데이터를 /src/mocks/recommendedFeeds.mock.ts로 분리
    • 15개의 여러 장르의 추천 도서 데이터 포함

구현 파일

  • 신규 생성

    • src/components/feed/RecommendedFeedCard.tsx - 추천 피드 카드 컴포넌트
    • src/components/feed/RecommendedFeedSection.tsx - 추천 섹션 컨테이너 (캐러셀 포함)
    • src/mocks/recommendedFeeds.mock.ts - 추천 피드 목업 데이터 (15개)
  • 수정

    • src/components/feed/TotalFeed.tsx - 10개마다 추천 섹션 삽입 로직 추가

스크린샷 (선택)

2025-10-28.9.17.01.mov

💬리뷰 요구사항(선택)

  • 캐러셀 설정 검토: Embla Carousel의 옵션(align: 'center', containScroll: 'trimSnaps')이 모바일에서 자연스러운지 확인 부탁드립니다.

Summary by CodeRabbit

새로운 기능

  • 추천 피드 섹션이 추가되었습니다. 사용자들은 메인 피드에서 10개 포스트마다 수평으로 스크롤 가능한 추천 콘텐츠를 확인할 수 있습니다.

스타일

  • 피드 포스트 하단 테두리의 너비가 조정되었습니다.

@ljh130334 ljh130334 self-assigned this Oct 28, 2025
@ljh130334 ljh130334 added ✨ Feature 기능 개발 🎨 Html&css 마크업 & 스타일링 labels Oct 28, 2025
@vercel
Copy link
Copy Markdown

vercel Bot commented Oct 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
thip Ready Ready Preview Comment Oct 28, 2025 0:17am

@ljh130334 ljh130334 requested review from heeeeyong and ho0010 October 28, 2025 00:17
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 28, 2025

📋 Walkthrough

Embla Carousel 라이브러리를 도입하고, 새로운 추천 피드 컴포넌트(RecommendedFeedCard, RecommendedFeedSection)를 추가하며, TotalFeed에 10개 게시물마다 추천 피드 섹션을 삽입하는 기능을 구현했습니다. 또한 모의 추천 피드 데이터와 스타일 조정을 추가했습니다.

📊 Changes

Cohort / File(s) Summary
의존성 추가
package.json
embla-carousel 및 embla-carousel-react ^8.6.0 버전 추가
스타일 조정
src/components/feed/FeedPost.tsx
BorderBottom 너비를 94.8%에서 100%로 변경
새로운 추천 피드 컴포넌트
src/components/feed/RecommendedFeedCard.tsx
PostHeader, PostBody, PostFooter를 포함한 새 피드 카드 컴포넌트 추가, 클릭 시 새 탭에서 열기 기능 구현
추천 피드 섹션 (Embla Carousel)
src/components/feed/RecommendedFeedSection.tsx
5개 항목의 수평 스크롤 카로셀을 렌더링하는 새 섹션 컴포넌트 추가, Embla Carousel 통합
피드 렌더링 로직 수정
src/components/feed/TotalFeed.tsx
isLast prop 제거, 10개 게시물마다 RecommendedFeedSection 삽입, FeedPost 렌더링 재구성
모의 데이터 추가
src/mocks/recommendedFeeds.mock.ts
15개 항목의 추천 피드 모의 데이터 및 MockPostData 인터페이스 추가

🔀 Sequence Diagram(s)

sequenceDiagram
    participant TotalFeed
    participant GroupLoop as Post Grouping Loop
    participant FeedPost
    participant RecommendedFeedSection
    participant RecommendedFeedCard
    participant Embla as Embla Carousel

    TotalFeed->>GroupLoop: Iterate posts (groups of 10)
    
    loop Every post in current group
        GroupLoop->>FeedPost: Render with isLast=false
        FeedPost-->>GroupLoop: Rendered
    end

    rect rgb(230, 240, 255)
        Note over GroupLoop,Embla: After every 10 posts
        GroupLoop->>RecommendedFeedSection: Insert with sectionIndex
        RecommendedFeedSection->>Embla: Configure carousel (center, single-slide)
        
        loop 5 mock feeds
            Embla->>RecommendedFeedCard: Render in slide
            RecommendedFeedCard-->>Embla: Card rendered
        end
        
        Embla-->>RecommendedFeedSection: Carousel ready
    end
    
    RecommendedFeedSection-->>TotalFeed: Section inserted
Loading

⏱️ Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20-25 minutes

검토 시 주의사항:

  • RecommendedFeedSection.tsx: Embla Carousel 설정(center 정렬, 단일 슬라이드 스크롤, trimSnaps containment) 및 모의 데이터 선택 로직 검증 필요
  • TotalFeed.tsx: 10개 항목 그룹핑 및 RecommendedFeedSection 삽입 위치 확인, 기존 isLast 로직 제거로 인한 영향 검토
  • RecommendedFeedCard.tsx: PostHeader에 aliasName/aliasColor 전달, PostFooter에 isMyFeed=false 설정 검증
  • Mock 데이터: 15개 항목의 데이터 정합성 및 aliasColor 값 일관성 확인

🔗 Possibly related issues

🔀 Possibly related PRs

  • feat: add followlist page, followerlist page #48: 동일한 src/components/feed/TotalFeed.tsx 파일을 수정하며, TotalFeed 렌더링 로직/props 변경(main PR은 isLast 제거 및 10개 항목마다 RecommendedFeedSection 삽입, 조회 PR은 빈 상태 조건 렌더링 추가)

🐰 Poem

캐러셀이 돌아가며 책을 담고,
열 개마다 추천 섹션 반짝반짝,
Embla와 함께 스르르 넘기고,
초록 별처럼 빛나는 별명들,
새로운 피드의 세상이 열렸네! ✨📚

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed PR 제목 "[FEAT] 인플루언서 추천 피드 - 지현"은 changeset의 주요 변경사항을 명확하게 요약하고 있습니다. PR의 핵심은 피드 아이템 10개마다 인플루언서 추천 글 섹션을 삽입하는 기능 추가이며, 제목이 이를 정확히 반영합니다. 제목은 간결하고 명확하며, 새로운 컴포넌트 3개 추가와 기존 컴포넌트 수정이라는 변경사항의 본질을 효과적으로 전달합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/influencer

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

🧹 Nitpick comments (2)
src/components/feed/RecommendedFeedSection.tsx (1)

79-83: 슬라이스 로직 단순화 제안

wrap-around 로직이 작동하지만, 더 명확하게 작성할 수 있습니다.

다음과 같이 리팩토링을 고려해보세요:

-  const startIndex = (sectionIndex * 5) % allMockRecommendedFeeds.length;
-  const selectedFeeds = [
-    ...allMockRecommendedFeeds.slice(startIndex, startIndex + 5),
-    ...allMockRecommendedFeeds.slice(0, Math.max(0, startIndex + 5 - allMockRecommendedFeeds.length)),
-  ].slice(0, 5);
+  const startIndex = (sectionIndex * 5) % allMockRecommendedFeeds.length;
+  const selectedFeeds = Array.from({ length: 5 }, (_, i) => {
+    const index = (startIndex + i) % allMockRecommendedFeeds.length;
+    return allMockRecommendedFeeds[index];
+  });

이 방식이 wrap-around 의도를 더 명확하게 표현합니다.

src/components/feed/RecommendedFeedCard.tsx (1)

49-58: !important 사용 최소화 고려

!important를 사용하면 스타일 우선순위 관리가 어려워지고 향후 유지보수에 문제가 될 수 있습니다. 가능하다면 더 구체적인 선택자나 styled-components의 전역 스타일 재정의를 고려해보세요.

만약 PostBody 컴포넌트가 props를 통해 스타일을 커스터마이징할 수 있다면, 그 방식을 사용하는 것이 더 좋습니다. 예:

<PostBody {...postData} maxLines={3} lookmoreIcon={lookmoreInfluencer} />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira 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 8d5414e and e67e20f.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/assets/feed/lookmore-influencer.svg is excluded by !**/*.svg
📒 Files selected for processing (6)
  • package.json (1 hunks)
  • src/components/feed/FeedPost.tsx (1 hunks)
  • src/components/feed/RecommendedFeedCard.tsx (1 hunks)
  • src/components/feed/RecommendedFeedSection.tsx (1 hunks)
  • src/components/feed/TotalFeed.tsx (1 hunks)
  • src/mocks/recommendedFeeds.mock.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/components/feed/TotalFeed.tsx (1)
src/types/post.ts (1)
  • FeedListProps (21-27)
src/components/feed/RecommendedFeedCard.tsx (2)
src/types/post.ts (1)
  • PostData (1-19)
src/styles/global/global.ts (1)
  • colors (4-53)
src/mocks/recommendedFeeds.mock.ts (2)
src/types/post.ts (1)
  • PostData (1-19)
src/styles/global/global.ts (1)
  • colors (4-53)
src/components/feed/RecommendedFeedSection.tsx (2)
src/styles/global/global.ts (2)
  • colors (4-53)
  • typography (56-77)
src/mocks/recommendedFeeds.mock.ts (1)
  • allMockRecommendedFeeds (9-328)
🔇 Additional comments (6)
package.json (1)

21-22: 캐러셀 라이브러리 추가 확인 완료

Embla Carousel 의존성 추가가 적절합니다. 버전 ^8.6.0이 최신 안정 버전인지 확인하시면 좋을 것 같습니다.

src/components/feed/FeedPost.tsx (1)

20-20: 하단 경계선 너비 조정 확인

100%로 변경하여 일관된 UI를 제공합니다.

src/components/feed/RecommendedFeedSection.tsx (1)

70-74: Embla 옵션 설정 검토

PR 설명에서 요청하신 대로 Embla 옵션을 검토했습니다:

  • align: 'center': 중앙 정렬로 다음 카드를 부분적으로 보여주는 의도된 UX에 적합합니다.
  • containScroll: 'trimSnaps': 마지막 슬라이드가 중앙에 오지 않도록 스냅 포인트를 조정합니다.

모바일 기기에서 터치 및 드래그 동작이 의도대로 작동하는지 테스트하시기 바랍니다.

src/mocks/recommendedFeeds.mock.ts (2)

5-7: MockPostData 인터페이스 확장 적절

PostData를 확장하여 aliasColor를 추가한 방식이 타입 안전성을 유지하면서 기능을 확장하고 있습니다.


15-15: 프로덕션 환경에서 placeholder 이미지 교체 필요

Mock 데이터에서 https://via.placeholder.com/36을 사용하고 있습니다. 프로덕션 배포 전에 실제 이미지 URL 또는 자체 호스팅 이미지로 교체해야 합니다.

src/components/feed/RecommendedFeedCard.tsx (1)

60-62: 북 카드 클릭 비활성화 검증 필요

pointer-events: none을 첫 번째 자식에 적용하여 북 카드 클릭을 비활성화하고 있습니다. 의도한 대로 작동하는지, 그리고 다른 상호작용(예: 링크, 버튼)이 있는 경우 영향을 받지 않는지 확인하세요.

Comment on lines +14 to +16
const handleCardClick = () => {
window.open(`/feed/${postData.feedId}`, '_blank');
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

보안 취약점: window.open에 보안 속성 누락

새 탭에서 링크를 열 때 noopenernoreferrer 속성이 없으면 tabnabbing 공격에 취약합니다. 새로 열린 페이지가 window.opener를 통해 원본 페이지를 조작할 수 있습니다.

다음과 같이 수정하세요:

   const handleCardClick = () => {
-    window.open(`/feed/${postData.feedId}`, '_blank');
+    window.open(`/feed/${postData.feedId}`, '_blank', 'noopener,noreferrer');
   };

또는 React Router의 Link 컴포넌트 사용을 고려하세요:

import { Link } from 'react-router-dom';

// In component:
<Link to={`/feed/${postData.feedId}`} target="_blank" rel="noopener noreferrer">
  <CardContainer>
    {/* ... */}
  </CardContainer>
</Link>
🤖 Prompt for AI Agents
In src/components/feed/RecommendedFeedCard.tsx around lines 14-16, the current
window.open call lacks security attributes and is vulnerable to tabnabbing;
modify the click handler to either open the URL via a proper anchor/React Router
Link with target="_blank" and rel="noopener noreferrer", or if you must use
window.open, capture the returned window object and immediately set
newWindow.opener = null and pass "noreferrer" as the third argument to
window.open to prevent the opened page from accessing window.opener and to avoid
leaking the referrer.

Comment on lines +16 to +30
{posts.map((post, index) => (
<>
<FeedPost
key={`${post.feedId}-${index}`}
showHeader={showHeader}
isMyFeed={isTotalFeed}
isLast={false}
{...post}
/>
{/* 10개마다 추천 섹션 반복 표시 */}
{(index + 1) % 10 === 0 && (
<RecommendedFeedSection sectionIndex={Math.floor((index + 1) / 10) - 1} />
)}
</>
))}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fragment에 key 속성 누락

배열 내에서 Fragment를 사용할 때는 key 속성이 필요합니다. React가 각 항목을 올바르게 식별하지 못하면 콘솔 경고가 발생하고 렌더링 성능에 영향을 줄 수 있습니다.

다음과 같이 수정하세요:

-          {posts.map((post, index) => (
-            <>
+          {posts.map((post, index) => (
+            <React.Fragment key={`${post.feedId}-${index}`}>
               <FeedPost
-                key={`${post.feedId}-${index}`}
                 showHeader={showHeader}
                 isMyFeed={isTotalFeed}
                 isLast={false}
                 {...post}
               />
               {/* 10개마다 추천 섹션 반복 표시 */}
               {(index + 1) % 10 === 0 && (
                 <RecommendedFeedSection sectionIndex={Math.floor((index + 1) / 10) - 1} />
               )}
-            </>
+            </React.Fragment>
           ))}

또한 FeedPost의 key는 Fragment로 이동했으므로 제거하세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{posts.map((post, index) => (
<>
<FeedPost
key={`${post.feedId}-${index}`}
showHeader={showHeader}
isMyFeed={isTotalFeed}
isLast={false}
{...post}
/>
{/* 10개마다 추천 섹션 반복 표시 */}
{(index + 1) % 10 === 0 && (
<RecommendedFeedSection sectionIndex={Math.floor((index + 1) / 10) - 1} />
)}
</>
))}
{posts.map((post, index) => (
<React.Fragment key={`${post.feedId}-${index}`}>
<FeedPost
showHeader={showHeader}
isMyFeed={isTotalFeed}
isLast={false}
{...post}
/>
{/* 10개마다 추천 섹션 반복 표시 */}
{(index + 1) % 10 === 0 && (
<RecommendedFeedSection sectionIndex={Math.floor((index + 1) / 10) - 1} />
)}
</React.Fragment>
))}
🤖 Prompt for AI Agents
In src/components/feed/TotalFeed.tsx around lines 16 to 30, the Fragment
wrapping each mapped item is missing a key which causes React warnings; move the
existing key from the FeedPost to the Fragment (e.g. use the same
`${post.feedId}-${index}` value) and remove the key prop from the FeedPost
component so each array child has a unique key on the Fragment instead.

@ljh130334 ljh130334 changed the title Feat/influencer [FEAT] 인플루언서 추천 피드 - 지현 Oct 28, 2025
@ljh130334 ljh130334 merged commit 154caf7 into develop Oct 28, 2025
3 checks passed
Copy link
Copy Markdown
Collaborator

@heeeeyong heeeeyong left a comment

Choose a reason for hiding this comment

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

LGTM😃

ljh130334 added a commit that referenced this pull request Nov 11, 2025
This reverts commit 154caf7, reversing
changes made to 8d5414e.
heeeeyong added a commit that referenced this pull request Nov 11, 2025
Revert "Merge pull request #284 from THIP-TextHip/feat/influencer"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발 🎨 Html&css 마크업 & 스타일링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants