Skip to content

인사이트 스타일링 개선, 페이지네이션 적용#464

Merged
dongjooyun merged 5 commits into
developfrom
feat/insights
Mar 31, 2026
Merged

인사이트 스타일링 개선, 페이지네이션 적용#464
dongjooyun merged 5 commits into
developfrom
feat/insights

Conversation

@dongjooyun

@dongjooyun dongjooyun commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

🌱 연관된 이슈

  • 아티클이 누적될수록 전체 목록에서 누락됨(페이지네이션 미구현)
  • 아티클 상세 > 가독성이 떨어지고 글이 짧아보임. 수동으로 줄바꿈(
    )하여 자체 스타일링해옴

☘️ 작업 내용

  • 페이지네이션 적용 - 한 페이지당 아티클 10개
  • 아티클 상세 본문 스타일링 개편
    • 반응형 적용
    • 한줄요약 문구 발행일 위로 이동
    • 마크다운 요소 스타일링 개선
    • 이미지 캡션 파일명 디폴트 제거

🍀 참고사항

스크린샷 (선택)

Summary by CodeRabbit

  • New Features

    • 인사이트 목록에 페이지네이션 추가(페이지 쿼리 지원, 페이지 이동 UI).
    • 글 목록 조회가 페이지 및 페이지크기 파라미터를 지원(기본값 page=1, pageSize=10).
  • Style

    • 상세 페이지 미디어 렌더링 통일(이미지·동영상 공통 래퍼) 및 마크다운 타이포그래피·코드 블록 스타일 개선.
    • 이미지 슬라이더 및 상세 레이아웃, 저자 표시 스타일 조정.
  • Chores

    • 사이트맵 생성 시 더 많은 기사(대량 요청) 포함하도록 조정.

@vercel

vercel Bot commented Mar 31, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
study-platform-client-dev Ready Ready Preview, Comment Mar 31, 2026 8:27pm

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

fetchArticles가 객체 매개변수(카테고/페이지/페이지크기)로 리팩터링되어 Strapi pagination 쿼리를 추가하고, 인사이트 페이지에 서버사이드 페이징이 도입되며 블로그 상세의 미디어·마크다운 렌더링과 레이아웃이 광범위하게 변경되었습니다.

Changes

Cohort / File(s) Summary
API 리팩터링
src/api/strapi/api/fetch-articles.ts
fetchArticles 시그니처를 FetchArticlesParams 객체로 변경(categorySlug?, page?, pageSize?), 기본값 page=1, pageSize=10 적용 및 Strapi pagination 쿼리(pagination[page], pagination[pageSize]) 추가.
인사이트 페이지 변경
src/app/(service)/insights/page.tsx
searchParamspage를 수용하고 currentPage/totalPages 계산, 서버 페이징으로 fetchArticles({ categorySlug, page, pageSize: PAGE_SIZE }) 호출, 최근 카테고리는 별도 큰 pageSize로 페칭.
페이지네이션 UI 추가
src/app/(service)/insights/ui/insights-pagination.tsx
클라이언트 컴포넌트 InsightsPagination 추가 — 현재 쿼리 유지, 페이지 변경 시 page 파라미터 설정/삭제 후 /insights?{query}로 라우팅.
블로그 상세 UI 리팩터링
src/app/(service)/insights/ui/blog-detail-page.tsx
MediaFigure 도입, 이미지/비디오/캡션 렌더링 통합, 마크다운 렌더러(타이포·코드·블록 등) 및 레이아웃/스타일 대규모 변경, 슬라이더·미디어 선택 흐름 정리.
호출부 업데이트
src/app/(service)/insights/weekly/page.tsx, src/app/sitemap.ts
fetchArticles 호출을 새 객체 시그니처로 변경 ({ categorySlug }, { pageSize: 1000 } 등).

Sequence Diagram

sequenceDiagram
    participant User as "User"
    participant UI as "InsightsPagination\n(Component)"
    participant Router as "Router"
    participant Page as "InsightsPage"
    participant Fetch as "fetchArticles"
    participant Strapi as "Strapi API"

    User->>UI: 페이지 선택
    UI->>Router: push /insights?page=N
    Router->>Page: 페이지 요청 (쿼리 포함)
    Page->>Fetch: fetchArticles({ categorySlug, page: N, pageSize })
    Fetch->>Strapi: GET /articles?pagination[page]=N&pagination[pageSize]=...
    Strapi-->>Fetch: articles + meta.pagination
    Fetch-->>Page: { data, meta.pagination }
    Page-->>User: 렌더된 아티클 + Pagination
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

  • Strapi CMS 1차 개선 #445: fetchArticles 구현 변경 관련 — 이 PR은 정렬 파라미터 추가, 본 PR은 객체 시그니처 및 페이지네이션 추가로 코드 수준 연관성 있음.

Poem

🐰 깡총, 페이지가 숫자대로 줄을 서네.
API는 쿼리를 세고, 토끼는 춤을 추네.
미디어는 포근히 감싸 안고, 캡션은 속삭이네.
매개변수는 정리되어 깔끔히 모이고,
당근 하나로 축하해요, 콩닥콩닥!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 변경사항의 핵심을 명확하게 요약하고 있으며, 페이지네이션 적용과 스타일링 개선이라는 두 가지 주요 변경사항을 간결하게 표현하고 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/insights

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/app/(service)/insights/page.tsx (3)

86-92: cn() 유틸리티 사용 일관성 개선

Line 88에서 조건부 클래스에 템플릿 리터럴을 사용하고 있으나, Line 100에서는 cn()을 사용하고 있습니다. 코딩 가이드라인에 따라 className 조합 시 항상 cn() 유틸리티를 사용해야 합니다.

♻️ cn() 사용으로 리팩터링
           <Link
             href="/insights"
-            className={`shrink-0 whitespace-nowrap px-300 pb-200 transition-colors ${
-              !selectedCategorySlug
-                ? 'font-designer-18b border-b-2 border-text-strong text-text-strong'
-                : 'font-designer-18r text-text-subtle hover:text-text-strong'
-            }`}
+            className={cn(
+              'shrink-0 whitespace-nowrap px-300 pb-200 transition-colors',
+              !selectedCategorySlug
+                ? 'font-designer-18b border-b-2 border-text-strong text-text-strong'
+                : 'font-designer-18r text-text-subtle hover:text-text-strong',
+            )}
           >

As per coding guidelines: "Always use cn() utility for className composition. Never use template literal className strings."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/page.tsx around lines 86 - 92, Replace the
template-literal className on the Link component that references
selectedCategorySlug with a call to the cn() utility so class composition is
consistent; specifically, import/use cn and pass the static classes ("shrink-0
whitespace-nowrap px-300 pb-200 transition-colors") plus a conditional
object/ternary for selectedCategorySlug to apply either 'font-designer-18b
border-b-2 border-text-strong text-text-strong' or 'font-designer-18r
text-text-subtle hover:text-text-strong' — e.g., update the Link's className to
use cn(...) and keep the same conditional logic around selectedCategorySlug.

76-76: Tailwind 임의 값 사용

max-w-[1280px]는 Tailwind 임의 값입니다. 프로젝트 학습 내용에 따르면 w-7xl이 80rem(1280px)에 해당하는 유효한 Tailwind v4 유틸리티입니다. max-w-7xl 사용을 권장합니다.

Based on learnings: "In this project, treat 'w-7xl' as a valid Tailwind CSS v4 width utility (from the container scale) equivalent to 80rem (1280px)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/page.tsx at line 76, Replace the arbitrary
Tailwind value in the container div's className: locate the div in
src/app/(service)/insights/page.tsx that currently contains "max-w-[1280px]" and
change it to the semantic utility "max-w-7xl" (i.e., update the className on
that div, preserving the other classes like mx-auto, w-full, px-200, py-400,
etc.).

162-162: Tailwind 임의 값 사용

h-[80px], w-[80px], h-[120px], w-[120px]는 Tailwind 임의 값입니다. 프로젝트 디자인 토큰 사용을 권장합니다.

As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/page.tsx at line 162, The div using className
"rounded-100 relative h-[80px] w-[80px] flex-shrink-0 overflow-hidden
sm:h-[120px] sm:w-[120px]" uses Tailwind arbitrary values; replace h-[80px],
w-[80px], sm:h-[120px], sm:w-[120px] with the project's design token utility
classes (e.g., avatar/spacing tokens or predefined h-/w- classes) so the
component in src/app/(service)/insights/page.tsx uses approved tokens; update
the className on that div accordingly and verify responsive sizes map to the
project's sm-token equivalents.
src/app/(service)/insights/weekly/page.tsx (1)

57-57: 하드코딩된 색상 및 임의 값 사용 지양 필요

이 파일의 기존 코드에서 코딩 가이드라인 위반이 발견됩니다:

  • 하드코딩된 색상: #181D27, #535862, #D5D7DA, #9CA3AF, #252B37
  • Tailwind 임의 값: max-w-[1280px], h-[120px], w-[120px]
  • 조건부 클래스에 템플릿 리터럴 대신 cn() 유틸리티 사용 필요

현재 PR 범위는 아니지만, 향후 리팩터링 시 프로젝트 디자인 토큰(text-text-strong, text-text-subtle, border-border-default 등)으로 통일하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/weekly/page.tsx at line 57, Replace hardcoded hex
colors and arbitrary Tailwind values in the JSX container and related elements
(e.g., the div with className "mx-auto w-full max-w-[1280px] px-400 py-600" and
any elements using h-[120px], w-[120px]) with project design tokens such as
text-text-strong, text-text-subtle, border-border-default and standard Tailwind
spacing/max-width classes; also swap any conditional className template literals
for the cn() utility to compose classes. Locate usages in page.tsx (search for
max-w-[1280px], `#181D27`, `#535862`, `#D5D7DA`, `#9CA3AF`, `#252B37`, h-[120px],
w-[120px]) and replace them with the appropriate token or token-based Tailwind
class and use cn(...) for conditional class construction.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/`(service)/insights/ui/blog-detail-page.tsx:
- Around line 359-363: The returned file descriptor currently prefers
fileData.name over fileData.alternativeText, causing file names like
"hero-banner.webp" to be exposed to assistive tech; update the return object in
the function that builds the file descriptor so the display text uses
alternativeText first (e.g., set name to fileData.alternativeText ??
fileData.name ?? ''), and make the same change for the other occurrence
referenced around the second return (the similar block at lines ~390-392) so alt
text is prioritized consistently.
- Around line 468-470: The time element currently shows article.createdAt but
should show the actual publication timestamp; update the UI to use
article.publishedAt (formatted via formatDate) instead of article.createdAt so
the 발행일 label displays the Article.publishedAt value; locate the time element
and replace the reference to createdAt with publishedAt (e.g., use
formatDate(article.publishedAt)) and ensure Article.publishedAt is available
where the component reads article.
- Around line 490-493: The author avatar URL is always prefixed with STRAPI_URL,
which breaks when article.author.avatar.url is already an absolute URL; update
the Image src logic used where article.author.avatar?.url is rendered to detect
absolute URLs (e.g., starts with "http://" or "https://" or protocol-relative
"//") and only prepend STRAPI_URL when the value is a relative path, so the
Image component receives a correct single absolute URL.
- Around line 127-130: 파일의 arbitrary Tailwind 값(text-[13px], rounded-[4px],
text-[`#333d4b`], border-[var(--color-rose-500)], max-w-[740px], px-[24px] 등)을
프로젝트 디자인 토큰으로 교체하세요: blog-detail-page.tsx의 figure/figcaption과 관련된 클래스들을 찾아(예: 해당
<figure> 블록 및 주석된 범위 150-171, 178-287, 295-345, 440-469, 489-506) text-[13px] →
텍스트 토큰(e.g., text-text-*), rounded-[4px] → rounded-150/비슷 토큰, 색상/경계/spacing 직접값
→ `@theme` inline 토큰 또는 p-200/px-200/ max-w-* 커스텀 클래스로 대체하도록 변경하고, 모든 하드코딩된 hex,
arbitrary spacing/width 값을 글로벌 토큰 클래스로 매핑하여 일관된 디자인 토큰만 사용하도록 리팩토링하세요.
- Around line 266-287: The fenced-code renderers (the inline code renderer
"code: ({ children, className }) => { ... }" and the "pre: ({ children }) =>
..." renderer) currently duplicate block styling causing nested boxes and use
forbidden arbitrary Tailwind values and hardcoded hex colors; fix by making
block styles the responsibility of only the pre renderer (detect block via
className?.includes('language-') in the code renderer and for block cases return
a plain <code> without background/padding/overflow), keep inline styling only
for non-block inline code, and replace all arbitrary classes (rounded-[4px],
px-[16px], py-[12px], bg-[`#F6F8FA`], text-[`#333`], etc.) with the project design
tokens / custom tailwind classes (use `@theme` tokens and classes like bg-*,
text-text-*, spacing/rounded token classes) so the pre renderer owns
bg/padding/overflow and the code renderer only handles inline text styling.

---

Nitpick comments:
In `@src/app/`(service)/insights/page.tsx:
- Around line 86-92: Replace the template-literal className on the Link
component that references selectedCategorySlug with a call to the cn() utility
so class composition is consistent; specifically, import/use cn and pass the
static classes ("shrink-0 whitespace-nowrap px-300 pb-200 transition-colors")
plus a conditional object/ternary for selectedCategorySlug to apply either
'font-designer-18b border-b-2 border-text-strong text-text-strong' or
'font-designer-18r text-text-subtle hover:text-text-strong' — e.g., update the
Link's className to use cn(...) and keep the same conditional logic around
selectedCategorySlug.
- Line 76: Replace the arbitrary Tailwind value in the container div's
className: locate the div in src/app/(service)/insights/page.tsx that currently
contains "max-w-[1280px]" and change it to the semantic utility "max-w-7xl"
(i.e., update the className on that div, preserving the other classes like
mx-auto, w-full, px-200, py-400, etc.).
- Line 162: The div using className "rounded-100 relative h-[80px] w-[80px]
flex-shrink-0 overflow-hidden sm:h-[120px] sm:w-[120px]" uses Tailwind arbitrary
values; replace h-[80px], w-[80px], sm:h-[120px], sm:w-[120px] with the
project's design token utility classes (e.g., avatar/spacing tokens or
predefined h-/w- classes) so the component in
src/app/(service)/insights/page.tsx uses approved tokens; update the className
on that div accordingly and verify responsive sizes map to the project's
sm-token equivalents.

In `@src/app/`(service)/insights/weekly/page.tsx:
- Line 57: Replace hardcoded hex colors and arbitrary Tailwind values in the JSX
container and related elements (e.g., the div with className "mx-auto w-full
max-w-[1280px] px-400 py-600" and any elements using h-[120px], w-[120px]) with
project design tokens such as text-text-strong, text-text-subtle,
border-border-default and standard Tailwind spacing/max-width classes; also swap
any conditional className template literals for the cn() utility to compose
classes. Locate usages in page.tsx (search for max-w-[1280px], `#181D27`, `#535862`,
`#D5D7DA`, `#9CA3AF`, `#252B37`, h-[120px], w-[120px]) and replace them with the
appropriate token or token-based Tailwind class and use cn(...) for conditional
class construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5306602d-acaf-4d7b-b3aa-618afa40f26e

📥 Commits

Reviewing files that changed from the base of the PR and between 41c6dcd and 90e1b28.

📒 Files selected for processing (6)
  • src/api/strapi/api/fetch-articles.ts
  • src/app/(service)/insights/page.tsx
  • src/app/(service)/insights/ui/blog-detail-page.tsx
  • src/app/(service)/insights/ui/insights-pagination.tsx
  • src/app/(service)/insights/weekly/page.tsx
  • src/app/sitemap.ts

Comment thread src/app/(service)/insights/ui/blog-detail-page.tsx Outdated
Comment thread src/app/(service)/insights/ui/blog-detail-page.tsx
Comment thread src/app/(service)/insights/ui/blog-detail-page.tsx
Comment thread src/app/(service)/insights/ui/blog-detail-page.tsx Outdated
Comment thread src/app/(service)/insights/ui/blog-detail-page.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/app/(service)/insights/ui/blog-detail-page.tsx (1)

60-60: ⚠️ Potential issue | 🟡 Minor

alt 텍스트 우선순위 수정 필요

resolveMedia 함수에서는 alternativeText를 우선하도록 수정되었지만, ImageSlider에서는 여전히 name을 먼저 사용하고 있습니다. 접근성을 위해 설명 텍스트인 alternativeText가 파일명보다 우선되어야 합니다.

🔧 제안 코드
                   <Image
                     src={imageUrl}
-                    alt={file.name || file.alternativeText || 'Slider Image'}
+                    alt={file.alternativeText || file.name || 'Slider Image'}
                     width={800}
                     height={500}
                     className="h-auto w-full object-contain"
                   />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/ui/blog-detail-page.tsx at line 60, The
ImageSlider alt prop currently prefers file.name over descriptive text; update
the alt value in the ImageSlider render to prefer file.alternativeText first
(matching resolveMedia behavior), then fall back to file.name and finally a
default like 'Slider Image' so accessibility uses the descriptive
alternativeText when present.
🧹 Nitpick comments (1)
src/app/(service)/insights/ui/blog-detail-page.tsx (1)

142-159: react-markdown 컴포넌트에 타입 정의 추가 권장

Static analysis에서 any 타입 사용이 감지되었습니다. react-markdown의 Components 타입을 활용하면 타입 안전성을 높일 수 있습니다.

♻️ 타입 개선 예시
import type { Components } from 'react-markdown';

const MarkdownComponents: Components = {
  video: ({ src, children }) => {
    // ...
  },
  img: ({ src, alt }) => {
    // ...
  },
  // ...
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/`(service)/insights/ui/blog-detail-page.tsx around lines 142 - 159,
Replace the ad-hoc any typing for the react-markdown custom renderer by
declaring a typed Components object from react-markdown and use it for the
components prop; specifically, import the Components type and create e.g. const
MarkdownComponents: Components = { video: ({ src, children }) => { ... }, img:
({ src, alt }) => { ... } } and then pass MarkdownComponents to ReactMarkdown's
components prop so the video renderer (the video: (...) => { ... }
implementation) no longer uses any and gains proper typing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/app/`(service)/insights/ui/blog-detail-page.tsx:
- Line 60: The ImageSlider alt prop currently prefers file.name over descriptive
text; update the alt value in the ImageSlider render to prefer
file.alternativeText first (matching resolveMedia behavior), then fall back to
file.name and finally a default like 'Slider Image' so accessibility uses the
descriptive alternativeText when present.

---

Nitpick comments:
In `@src/app/`(service)/insights/ui/blog-detail-page.tsx:
- Around line 142-159: Replace the ad-hoc any typing for the react-markdown
custom renderer by declaring a typed Components object from react-markdown and
use it for the components prop; specifically, import the Components type and
create e.g. const MarkdownComponents: Components = { video: ({ src, children })
=> { ... }, img: ({ src, alt }) => { ... } } and then pass MarkdownComponents to
ReactMarkdown's components prop so the video renderer (the video: (...) => { ...
} implementation) no longer uses any and gains proper typing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6e71b36c-223d-4e50-8865-85543833fc24

📥 Commits

Reviewing files that changed from the base of the PR and between 90e1b28 and b7aa657.

📒 Files selected for processing (1)
  • src/app/(service)/insights/ui/blog-detail-page.tsx

@dongjooyun dongjooyun merged commit 8bb1b43 into develop Mar 31, 2026
8 of 9 checks passed
@dongjooyun dongjooyun deleted the feat/insights branch March 31, 2026 20:30
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