Skip to content

Feat-10#11

Closed
hongik-luke wants to merge 16 commits intocheckmo2025:mainfrom
hongik-luke:feat-10
Closed

Feat-10#11
hongik-luke wants to merge 16 commits intocheckmo2025:mainfrom
hongik-luke:feat-10

Conversation

@hongik-luke
Copy link

@hongik-luke hongik-luke commented Jan 1, 2026

💡 To Reviewers

⚠️현재 PR의 base가 feat-8로 되어있습니다 반드시 feat-8을 먼저 merge하고 merge해 주시고 feat-8과 관련된 commit은 제 작업물이 아닙니다 이를 참고 바랍니다.⚠️
이 PR은 feat-10 작업입니다.
원본 레포에 push 권한이 없어서 fork에서 PR 올렸습니다
⚠️Figma에 component 모아져있는 곳에 이름을 파일이름과 동일하게 수정해두었습니다. 참고하시면 찾으시기 쉬울 것입니다.⚠️

🔥 작업 내용 (가능한 구체적으로 작성해 주세요)

  1. 파일구조

  2. [global css] 글씨체, 색 호출방법

  3. button_without_img

  4. 각 페이지 별 생성한 ui

  5. 파일구조
    src/components/base_ui
    -> 하위
    /home
    /Auth
    /Group-search
    /Group-create
    /BookStory
    /News
    /Search
    /Profile
    /Settings
    /button_without_img.tsx

  6. [global css] 글씨체, 색 호출방법

ex)


Subhead_4_1 ->

/* Subhead_4.1 /
font-family: "Pretendard Variable";
font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 135%; /
24.3px /
letter-spacing: -0.018px;
가 호출됨
글씨체 상위에 /
Subhead_4.1 */ 이렇게 표기되어 있으니 호출하시고 색만 신경써주시면 됩니다.
호출 규칙

  1. 이름: 대소문자 포함 그대로

  2. [.] 은 _로 대체

  3. 색 토큰은 Gray_7 같은 형식 ( ex text-[color:var(--Gray_7)]

  4. button_without_img

이미지가 없을 시에 사용할 수 있는 버튼 폼
사용 예시 (비어있을 시 기본값이 들어감)

  1. 색 : 배경 테두리 텍스트 설정가능
  2. 호버 시 색도 설정가능
  3. 크기 설정 가능
    아무 내용 없을 시에 기본값이 들어감

// 아래 기본값 겸 사용방법
<ButtonWithoutImg
text="등록"
onClick={() => {} }

bgColorVar="--premary_2"
borderColorVar="--premary_2"
textColorVar="--White"

hoverBgColorVar="--premary_2"
hoverBorderColorVar="--premary_2"
hoverTextColorVar="--White"

width={132}
height={44}
/>

  1. 각 페이지 별 생성한 ui

책모_홈화면

  1. home_bookclub -> 모임갯수에 따른 경우를 모두 하나에 담았습니다.
  2. list_subscribe_element
  3. notification_element
화면 캡처 2026-01-01 061131 화면 캡처 2026-01-01 063708

로그인/회원가입
개별로 만드는게 나아보여 생략하였습니다.

모임 검색 / 모임 생성
-> 제 쪽이라서 일단 보류했습니다.

책이야기

  1. bookstory_choosebook
화면 캡처 2026-01-01 074003 화면 캡처 2026-01-01 074038
  1. bookstory_detail
화면 캡처 2026-01-01 084650
  1. bookstory_text
화면 캡처 2026-01-01 080234 화면 캡처 2026-01-01 0802457

소식

  1. news_list
화면 캡처 2026-01-01 090134 화면 캡처 2026-01-01 090201
  1. recommendbook_element
화면 캡처 2026-01-01 094101 화면 캡처 2026-01-01 094114

검색

  1. recommendbook_element
화면 캡처 2026-01-01 100923
  1. search_bookresult
화면 캡처 2026-01-01 100708

마이페이지&다른사람 프로필

  1. mypage_profile
  2. others_profile
화면 캡처 2026-01-01 104835 화면 캡처 2026-01-01 105907
  1. notification_element
    홈화면에 있는 것과 같습니다 고로 생략합니다 notification_element

  2. subscribe

화면 캡처 2026-01-01 111532 화면 캡처 2026-01-01 111544

설정

  1. setting_news_list
    소식의 news_list와 max크기 빼곤 다르지 않습니다 고로 생략합니다.

  2. setting_report_list

화면 캡처 2026-01-01 112710

🤔 추후 작업 예정

모임 검색 / 모임 생성

📸 작업 결과 (스크린샷)

🔗 관련 이슈

Feat-10

Summary by CodeRabbit

  • New Features

    • Added navigation header with active route indicators
    • Introduced profile management system for users and viewing others
    • Added book story creation and management interface
    • Implemented notification system for user interactions (likes, comments)
    • Added search and book recommendation features
    • Implemented subscribe/follow functionality
  • Style

    • Expanded color palette with new design tokens
    • Added semantic typography utilities for consistent visual hierarchy
    • Updated default font family for improved consistency

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 1, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new Next.js app structure with a main layout system, multiple reusable UI components for displaying books, news, profiles, searches, and settings, a header with navigation, extended CSS with custom properties and typography utilities, and configuration updates. A large set of base-level UI components is added across several functional areas.

Changes

Cohort / File(s) Summary
Configuration & Templates
.github/ISSUE_TEMPLATE/custom.md, tsconfig.json, src/app/layout.tsx
Updated issue template YAML front matter (quoted strings, removed assignee, added issue type option). Modified tsconfig to include Next.js type paths and reformatted arrays to multi-line format for readability.
App Structure & Pages
src/app/(main)/layout.tsx, src/app/(main)/page.tsx, src/app/(main)/ui-test/page.tsx
Added main layout wrapper rendering Header and children. Introduced blank HomePage component and UiTestClient page with test data for Setting_ReportList component.
Global Styling
src/app/globals.css
Extended CSS custom properties with full color palette (White, Black, Grays, Secondary, Subbrown, primary tones). Changed body font to Pretendard Variable. Added @layer utilities with semantic typography classes (Headline, Subhead, Body, Caption variants).
BookStory Components
src/components/base-ui/BookStory/bookstory_*.tsx
Three new book-related components: bookstory_choosebook.tsx (horizontal card with book image and change button), bookstory_detail.tsx (detailed view with author link and subscribe button), bookstory_text.tsx (title/detail editor with tab-to-spaces handling and textarea auto-resize).
News Components
src/components/base-ui/News/news_list.tsx, recommendbook_element.tsx
New news item display with image, title, content, and date. Book cover card with title, author, and interactive like button with heart icon toggle.
Profile Components
src/components/base-ui/Profile/mypage_*.tsx, notification_*.tsx, others_*.tsx, subscribe_*.tsx
Multiple profile UIs: MypageProfile (user header with avatar, counts, action buttons), NotificationElement (notification row with type-based messaging), OthersProfile (read-only profile with subscribe/report), Subscribe_element (simple subscribe toggle row).
Search Components
src/components/base-ui/Search/search_bookresult.tsx, search_recommendbook.tsx
Book search result card with like/pencil buttons. Book cover card component with interactive like state and optional click handler.
Settings Components
src/components/base-ui/Settings/setting_news_list.tsx, setting_report_list.tsx
News item display for settings context. Report list item with badge, reporter avatar/name, date, and content.
General Button Component
src/components/base-ui/button_without_img.tsx
Button component with customizable colors via CSS variables, hover state management, width/height/disabled props, and optional styling.
Home Components
src/components/base-ui/home/home_bookclub.tsx, list_subscribe_element.tsx, notification_element.tsx
Collapsible bookclub groups sidebar with toggle and action buttons. Subscribe element card with profile, counts, and button. Notification row with type-based messaging and latest-state highlighting.
Layout Components
src/components/layout/Header.tsx, NavItem.tsx
Header component with logo, navigation items with active state, and icon links (search, notification, profile). NavItem component for individual nav links with conditional styling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hopping through new components bright,
Building UI blocks, oh what a sight!
Cards and profiles, headers aligned,
Typography styled with custom design,
A bookish tale in code refined! 📚✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
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.
Title check ❓ Inconclusive The PR title "Feat-10" is vague and generic, providing no meaningful information about the changeset beyond a feature ticket number. Replace with a descriptive title that summarizes the main changes, e.g., "Add base UI components and global styling for home, profile, news, and search pages" or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @hongik-luke, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 풀 리퀘스트는 feat-10 브랜치에 대한 작업으로, 애플리케이션의 전반적인 UI 기반을 구축하고 다양한 페이지에 필요한 핵심 컴포넌트들을 구현합니다. 글로벌 스타일 시스템을 정의하고, 여러 페이지에 걸쳐 재사용 가능한 UI 컴포넌트들을 추가하여 일관된 사용자 경험을 제공하는 데 중점을 둡니다. 또한, 새로운 레이아웃 구조를 도입하고 기존 페이지를 재구성하여 확장성을 고려했습니다.

Highlights

  • 글로벌 스타일 시스템 정의: 다양한 색상 변수와 Pretendard 폰트를 기본으로 하는 타이포그래피 유틸리티 클래스를 globals.css에 추가하여 애플리케이션 전반의 일관된 디자인을 위한 기반을 마련했습니다.
  • 핵심 UI 컴포넌트 개발: 홈, 책 이야기, 소식, 검색, 프로필, 설정 등 주요 페이지에 필요한 다양한 UI 컴포넌트들을 구현했습니다. 여기에는 북클럽 목록, 책 상세 정보, 뉴스 목록, 프로필 카드, 신고 목록 등이 포함됩니다.
  • 재사용 가능한 버튼 및 레이아웃 컴포넌트: 이미지 없이 색상, 크기, 호버 효과 등을 유연하게 설정할 수 있는 재사용 가능한 버튼 컴포넌트(button_without_img.tsx)와 애플리케이션의 기본 헤더 및 메인 레이아웃을 정의하는 컴포넌트들을 추가했습니다.
  • 새로운 SVG 아이콘 추가: 애플리케이션의 시각적 요소를 풍부하게 하기 위해 다양한 기능과 상태를 나타내는 여러 SVG 아이콘 파일들을 public 디렉토리에 추가했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

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

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

♻️ Duplicate comments (2)
src/components/base-ui/home/notification_element.tsx (1)

1-47: Duplicate of Profile/notification_element.tsx.

As noted in the Profile variant, consolidate into a single shared component. The implementation logic is correct.

src/components/base-ui/News/recommendbook_element.tsx (1)

5-56: Code duplication: nearly identical to Search/search_recommendbook.tsx.

This component duplicates src/components/base-ui/Search/search_recommendbook.tsx with only minor dimensional differences. See the review comment on that file for a detailed refactoring approach to eliminate this duplication.

🟡 Minor comments (9)
src/components/base-ui/Profile/others_profile.tsx-17-17 (1)

17-17: Unused prop: onSettings is defined but never used.

The onSettings callback is declared in the props type but is never referenced in the component body. Either remove it from the type or wire it to a settings button.

🔎 Proposed fix
 type OthersProfileProps = {
   profileImgSrc?: string; // default: /profile.svg
   name: string;

   followingCount: number; // 구독중
   followerCount: number;  // 구독자
   isSubscribed: boolean;
   onToggleSubscribe: (newState: boolean) => void;

   intro: string;

-  onSettings?: () => void;
   onReportClick?: () => void;

   className?: string;
 };
src/components/base-ui/home/home_bookclub.tsx-72-72 (1)

72-72: Remove duplicate class name Body_1_2.

The className contains Body_1_2 twice, which is redundant.

🔎 Proposed fix
-                <span className="text-[color:var(--Gray_7)] Body_1_2 Body_1_2">전체보기</span>
+                <span className="text-[color:var(--Gray_7)] Body_1_2">전체보기</span>
src/components/base-ui/home/list_subscribe_element.tsx-8-8 (1)

8-8: Fix the comment to match the actual default value.

The comment states the default is "/profile.svg", but Line 17 actually defaults to "/profile2.svg".

🔎 Proposed fix
-  profileSrc?: string;        // 기본: "/profile.svg" (public)
+  profileSrc?: string;        // 기본: "/profile2.svg" (public)
src/components/base-ui/home/list_subscribe_element.tsx-24-33 (1)

24-33: Correct the sizes prop to match the actual container dimensions.

The sizes prop is set to "42px", but the container is w-[32px] h-[32px] (32 pixels). This mismatch can cause Next.js to generate and load unnecessarily large images, impacting performance.

🔎 Proposed fix
           sizes="42px"
+          sizes="32px"
src/components/base-ui/Search/search_bookresult.tsx-69-74 (1)

69-74: Add descriptive alt text for interactive button icons.

Both the heart icon (line 69-74) and pencil icon (line 89) have empty alt attributes. Screen reader users need descriptive text to understand the button's purpose. Add meaningful alt text or use aria-label on the buttons.

🔎 Suggested fix
            <Image
              src={liked ? "/red_heart.svg" : "/gray_heart.svg"}
-             alt=""
+             alt={liked ? "Unlike" : "Like"}
              width={24}
              height={24}
            />
-           <Image src="/pencil_icon.svg" alt="" width={20} height={20} />
+           <Image src="/pencil_icon.svg" alt="Edit" width={20} height={20} />

Also applies to: 89-89

src/components/base-ui/News/recommendbook_element.tsx-46-46 (1)

46-46: Add descriptive alt text for the like button icon.

The heart icon uses an empty alt attribute. Provide descriptive text for screen reader accessibility.

-          <Image src={liked ? "/red_heart.svg" : "/gray_heart.svg"} alt="" width={24} height={24} />
+          <Image src={liked ? "/red_heart.svg" : "/gray_heart.svg"} alt={liked ? "Unlike" : "Like"} width={24} height={24} />
src/components/base-ui/Search/search_recommendbook.tsx-46-46 (1)

46-46: Add descriptive alt text for the like button icon.

The heart icon has an empty alt attribute, which provides insufficient context for screen reader users. Consider adding descriptive text like "Like" or "Unlike" based on the current state, or mark the button itself with aria-label.

🔎 Suggested fix
-          <Image src={liked ? "/red_heart.svg" : "/gray_heart.svg"} alt="" width={24} height={24} />
+          <Image src={liked ? "/red_heart.svg" : "/gray_heart.svg"} alt={liked ? "Unlike" : "Like"} width={24} height={24} />

Or add aria-label to the button:

        <button
          type="button"
+         aria-label={liked ? "Unlike this book" : "Like this book"}
          onClick={(e) => {
src/app/globals.css-70-71 (1)

70-71: Reconsider truncate in .Body_1 utility class.

Line 70 includes truncate in the .Body_1 class, while .Body_1_1 (with identical typography specs) does not. Including truncate in a base typography utility can cause unexpected text clipping when the class is used in various contexts.

Consider removing truncate from .Body_1 and applying it explicitly where text truncation is needed (e.g., via a separate utility class or inline).

🔎 Proposed fix
- .Body_1 { @apply text-[14px] font-semibold leading-[145%] tracking-[-0.014px] truncate; }
+ .Body_1 { @apply text-[14px] font-semibold leading-[145%] tracking-[-0.014px]; }
  .Body_1_1 { @apply text-[14px] font-semibold leading-[145%] tracking-[-0.014px]; }

Apply truncate explicitly where needed: <div className="Body_1 truncate">...</div>

src/components/layout/Header.tsx-36-39 (1)

36-39: Fix potential active state mismatch for nested routes.

The current logic uses pathname.startsWith(item.href) for non-root routes, which will incorrectly mark a navigation item as active for nested or similar paths. For example, "/groups" would remain active when viewing "/groups/123" (which may or may not be intended), and could also match "/groups-archive" (unintended).

🔎 Proposed fix for more precise matching

If you want nav items active only on exact matches:

               const active =
-                 item.href === "/"
-                   ? pathname === "/"
-                   : pathname.startsWith(item.href);
+                 pathname === item.href;

If you want nav items active for their entire section (e.g., "/groups" stays active on "/groups/123"):

               const active =
                 item.href === "/"
                   ? pathname === "/"
-                  : pathname.startsWith(item.href);
+                  : pathname === item.href || pathname.startsWith(item.href + "/");
🧹 Nitpick comments (19)
src/components/base-ui/Profile/mypage_profile.tsx (2)

36-43: Fixed dimensions may limit responsiveness.

The container has fixed w-[734px] h-[244px] dimensions. If this is intentional for a specific design system breakpoint, consider documenting it. Otherwise, consider using responsive utilities or accepting width/height as props for flexibility.


111-140: Consider making button labels configurable.

The CTA button labels ("책 이야기 쓰기", "소식 문의하기") are hardcoded. For reusability, consider accepting these as props with defaults.

🔎 Proposed enhancement
 type MypageProfileProps = {
   // ... existing props
   onLeftButtonClick?: () => void;
   onRightButtonClick?: () => void;
+  leftButtonLabel?: string;
+  rightButtonLabel?: string;
   className?: string;
 };

 export default function MypageProfile({
   // ... existing destructuring
   onLeftButtonClick,
   onRightButtonClick,
+  leftButtonLabel = "책 이야기 쓰기",
+  rightButtonLabel = "소식 문의하기",
   className = "",
 }: MypageProfileProps) {
src/components/base-ui/Profile/others_profile.tsx (1)

34-81: Structural duplication with MypageProfile.

The avatar + name + follower counts + intro section (lines 42-81) is nearly identical to MypageProfile. Consider extracting a shared ProfileHeader component to reduce duplication.

src/components/base-ui/Profile/subscribe_element.tsx (1)

38-52: LGTM with minor note on typography consistency.

The optional chaining on onToggleSubscribe?.(!isSubscribed) is correctly implemented. Consider using the design system typography class (e.g., Subhead_4_1) instead of inline text-[12px] font-semibold for consistency with other components in this PR.

src/components/base-ui/home/list_subscribe_element.tsx (2)

37-37: Remove trailing whitespace in className.

There's a trailing space after Body_1 in the className string.

🔎 Proposed fix
-        <p className="text-[color:var(--Gray_7)] Body_1 ">
+        <p className="text-[color:var(--Gray_7)] Body_1">

49-49: Simplify redundant color declarations in button className.

The button className contains both text-white and text-[color:var(--White,#FFF)], which are redundant. Use one consistent approach for better maintainability.

🔎 Proposed fix
-        className="flex px-[17px] py-[8px] justify-center items-center gap-[10px] rounded-[8px] bg-[#9A7A6B] text-white text-[color:var(--White,#FFF)] text-[12px] font-semibold leading-[100%] tracking-[-0.012px] whitespace-nowrap"
+        className="flex px-[17px] py-[8px] justify-center items-center gap-[10px] rounded-[8px] bg-[#9A7A6B] text-white text-[12px] font-semibold leading-[100%] tracking-[-0.012px] whitespace-nowrap"
src/components/base-ui/home/home_bookclub.tsx (1)

30-34: Use Next.js Image component for consistency and optimization.

Line 32 uses the native <img> tag, which misses Next.js's automatic image optimization (lazy loading, responsive sizes, modern formats). The rest of the codebase consistently uses the Image component from next/image.

🔎 Proposed fix
       {count === 0 && (
         <div className="">
-          <img src="logo2.svg" alt= "로고" className="mx-auto mb-4 mt-[118px]" />
+          <Image src="/logo2.svg" alt="로고" width={100} height={100} className="mx-auto mb-4 mt-[118px]" />
         </div>
       )}

Note: Adjust width and height to match the actual logo dimensions.

src/components/base-ui/Search/search_recommendbook.tsx (1)

35-35: Consider adding priority prop for above-the-fold images.

If this book cover card appears in the initial viewport, adding priority to the Next.js Image component will prevent it from being lazy-loaded, improving Largest Contentful Paint (LCP).

<Image 
  src={coverSrc} 
  alt={title} 
  fill 
  sizes="244px" 
  priority={/* determine if above fold */}
  className="object-cover" 
/>
src/components/base-ui/Search/search_bookresult.tsx (1)

33-33: Extract magic number to a named constant.

The detail truncation limit of 500 characters is a magic number. Consider extracting it to a named constant for better maintainability and documentation of the business rule.

+const MAX_DETAIL_LENGTH = 500;
+
 export default function SearchBookResult({
   // ...
 }: SearchBookResultProps) {
   const coverSrc = imgUrl && imgUrl.length > 0 ? imgUrl : "/booksample.svg";
-  const clippedDetail = detail.length > 500 ? detail.slice(0, 500) + "..." : detail;
+  const clippedDetail = detail.length > MAX_DETAIL_LENGTH 
+    ? detail.slice(0, MAX_DETAIL_LENGTH) + "..." 
+    : detail;
src/app/(main)/ui-test/page.tsx (2)

2-2: Remove redundant React import.

With the modern JSX transform (enabled by default in Next.js 16), explicitly importing React is no longer necessary unless you're using React hooks or other exports. Since you're only using useState (imported separately on line 3), this import can be removed.

-import React from "react";
 import { useState } from "react";

4-4: Remove unused imports and state.

Both Subscribe_elements (line 4) and the subs state variable (line 8) are declared but never used in the component. If these are placeholders for future testing, consider adding a comment; otherwise, remove them to keep the code clean.

🔎 Suggested cleanup
-import Subscribe_elements from "@/components/base-ui/Profile/subscribe_element";
 import Setting_ReportList from "@/components/base-ui/Settings/setting_report_list";

 export default function UiTestClient() {
-  const [subs, setSubs] = useState(false);
-
   return (

Or if these are intentional placeholders:

// TODO: Add Subscribe_elements testing
// const [subs, setSubs] = useState(false);

Also applies to: 8-8

src/components/base-ui/News/news_list.tsx (1)

42-42: Use CSS variable for consistency instead of hardcoded color.

Line 42 uses a hardcoded color value #000, while other text elements consistently use CSS variables like var(--Gray_4,#8D8D8D) (line 43) and var(--Gray_3,#BBB) (line 50). For consistency with the project's color token system, consider using a CSS variable here as well.

-          <p className="text-[#000] Subhead_3 truncate">{title}</p>
+          <p className="text-[color:var(--Gray_7,#2C2C2C)] Subhead_3 truncate">{title}</p>

Or define a --Black variable if pure black is the intended semantic color.

src/components/base-ui/Settings/setting_report_list.tsx (1)

22-28: Consider responsive width for better mobile experience.

The fixed w-[1000px] width may cause horizontal scrolling on smaller screens or tablets. Consider using max-w-[1000px] w-full to allow the component to adapt to narrower viewports while maintaining the maximum width.

🔎 Suggested change
     <div
       className={`
-        flex w-[1000px] p-[20px] items-start gap-[40px]
+        flex w-full max-w-[1000px] p-[20px] items-start gap-[40px]
         rounded-[8px] border border-[color:var(--Subbrown_4,#EAE5E2)]
         bg-[color:var(--White,#FFF)]
         ${className}
       `}
     >
src/components/base-ui/Settings/setting_news_list.tsx (1)

4-10: Consider renaming type to match component naming pattern.

The type NewsListProps doesn't fully match the component name Setting_NewsList. For consistency with the sibling component (which uses SettingReportListProps for Setting_ReportList), consider renaming to SettingNewsListProps.

🔎 Suggested change
-type NewsListProps = {
+type SettingNewsListProps = {
   imageUrl: string;
   title: string;
   content: string;
   date: string;
   className?: string;
 };

 export default function Setting_NewsList({
   imageUrl,
   title,
   content,
   date,
   className = "",
-}: NewsListProps) {
+}: SettingNewsListProps) {
src/app/globals.css (1)

4-33: Consider consistent naming convention for CSS custom properties.

The color tokens mix naming conventions (e.g., --White vs --primary_3). For better maintainability, consider adopting a consistent convention throughout:

  • Option 1: All lowercase with underscores: --white, --gray_1, --primary_3
  • Option 2: kebab-case: --white, --gray-1, --primary-3

Also, lines 10, 23, and 29 contain unnecessary blank lines that could be removed for cleaner formatting.

src/components/base-ui/button_without_img.tsx (1)

58-83: Consider style composition approach.

The component mixes inline styles (for dynamic colors and dimensions) with Tailwind utility classes (for layout and transitions). This works but can lead to specificity conflicts. The current implementation appears intentional for dynamic theming, which is a valid use case.

One minor point: the transition-colors utility may not smoothly transition the inline style properties. Consider using CSS custom properties on the button element if smooth color transitions are required:

style={{
  '--btn-width': `${width}px`,
  '--btn-height': `${height}px`,
  '--btn-bg': bg,
  '--btn-border': border,
  '--btn-text': textColor,
}}
className="... [width:var(--btn-width)] [height:var(--btn-height)] [background-color:var(--btn-bg)] ..."

However, the current approach is simpler and acceptable for most use cases.

src/app/(main)/layout.tsx (1)

1-11: LGTM! Consider adding layout constraints.

The layout structure correctly composes the Header with page content. The implementation is clean and follows Next.js app router conventions.

Optional: Consider adding layout constraints to the <main> element for consistent content width and padding:

<main className="max-w-7xl mx-auto px-4 py-6">
  {children}
</main>

This ensures consistent spacing across pages, but defer to your design system requirements.

src/components/layout/NavItem.tsx (1)

11-16: Consider using clsx or template literals for cleaner conditional className composition.

The array.join(" ") pattern works but is less idiomatic in modern React. Also, the active link should include aria-current="page" for better accessibility.

🔎 Suggested improvements

Option 1: Using template literals

     <Link
       href={href}
-      className={[
-        "flex w-32 items-center justify-center gap-2.5 p-2.5",
-        active ? "border-b-2 border-white" : "border-b-2 border-transparent",
-      ].join(" ")}
+      className={`flex w-32 items-center justify-center gap-2.5 p-2.5 border-b-2 ${
+        active ? "border-white" : "border-transparent"
+      }`}
+      aria-current={active ? "page" : undefined}
     >

Option 2: Using clsx (recommended if already a dependency)

+import clsx from "clsx";
+
 export function NavItem({ href, label, active }: NavItemProps) {
   return (
     <Link
       href={href}
-      className={[
-        "flex w-32 items-center justify-center gap-2.5 p-2.5",
-        active ? "border-b-2 border-white" : "border-b-2 border-transparent",
-      ].join(" ")}
+      className={clsx(
+        "flex w-32 items-center justify-center gap-2.5 p-2.5 border-b-2",
+        active ? "border-white" : "border-transparent"
+      )}
+      aria-current={active ? "page" : undefined}
     >
src/components/layout/Header.tsx (1)

55-91: Consider extracting icon links to reduce duplication.

The three action icons (search, notification, profile) follow identical patterns. Additionally, using priority on all icons may not be necessary — priority should be reserved for above-the-fold critical images like the logo.

🔎 Suggested refactor

Define an array and map over it:

+const ACTION_ICONS = [
+  { href: "/search", icon: "/search.svg", label: "검색" },
+  { href: "/notification", icon: "/notification.svg", label: "알림" },
+  { href: "/profile", icon: "/profile.svg", label: "프로필" },
+];
+
 export default function Header() {
   const pathname = usePathname();
 
   return (
     <header className="w-full bg-[#7B6154]">
       ...
           {/*아이콘*/}
           <div className="flex items-center gap-4">
-            <Link href="/search" aria-label="검색" className="relative h-6 w-6">
-              <Image
-                src="/search.svg"
-                alt="검색"
-                fill
-                className="object-contain"
-                priority
-              />
-            </Link>
-
-            <Link
-              href="/notification"
-              aria-label="알림"
-              className="relative h-6 w-6"
-            >
-              <Image
-                src="/notification.svg"
-                alt="알림"
-                fill
-                className="object-contain"
-                priority
-              />
-            </Link>
-
-            <Link
-              href="/profile"
-              aria-label="프로필"
-              className="relative h-6 w-6"
-            >
-              <Image
-                src="/profile.svg"
-                alt="프로필"
-                fill
-                className="object-contain"
-                priority
-              />
-            </Link>
+            {ACTION_ICONS.map((item) => (
+              <Link
+                key={item.href}
+                href={item.href}
+                aria-label={item.label}
+                className="relative h-6 w-6"
+              >
+                <Image
+                  src={item.icon}
+                  alt={item.label}
+                  fill
+                  className="object-contain"
+                />
+              </Link>
+            ))}
           </div>

Note: Removed priority from action icons as they're not critical for initial page render.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad62f6f and e75da33.

⛔ Files ignored due to path filters (20)
  • package-lock.json is excluded by !**/package-lock.json
  • public/ArrowDown.svg is excluded by !**/*.svg
  • public/ArrowTop.svg is excluded by !**/*.svg
  • public/Edit_icon.svg is excluded by !**/*.svg
  • public/Setting_icon.svg is excluded by !**/*.svg
  • public/booksample.svg is excluded by !**/*.svg
  • public/gray_heart.svg is excluded by !**/*.svg
  • public/icon_plus.svg is excluded by !**/*.svg
  • public/logo.svg is excluded by !**/*.svg
  • public/logo2.svg is excluded by !**/*.svg
  • public/notification.svg is excluded by !**/*.svg
  • public/pencil_icon.svg is excluded by !**/*.svg
  • public/plus.svg is excluded by !**/*.svg
  • public/profile.svg is excluded by !**/*.svg
  • public/profile2.svg is excluded by !**/*.svg
  • public/profile3.svg is excluded by !**/*.svg
  • public/profile4.svg is excluded by !**/*.svg
  • public/profile5.svg is excluded by !**/*.svg
  • public/red_heart.svg is excluded by !**/*.svg
  • public/search.svg is excluded by !**/*.svg
📒 Files selected for processing (27)
  • .github/ISSUE_TEMPLATE/custom.md
  • src/app/(main)/layout.tsx
  • src/app/(main)/page.tsx
  • src/app/(main)/ui-test/page.tsx
  • src/app/globals.css
  • src/app/layout.tsx
  • src/app/page.backup.tsx
  • src/components/base-ui/BookStory/bookstory_choosebook.tsx
  • src/components/base-ui/BookStory/bookstory_detail.tsx
  • src/components/base-ui/BookStory/bookstory_text.tsx
  • src/components/base-ui/News/news_list.tsx
  • src/components/base-ui/News/recommendbook_element.tsx
  • src/components/base-ui/Profile/mypage_profile.tsx
  • src/components/base-ui/Profile/notification_element.tsx
  • src/components/base-ui/Profile/others_profile.tsx
  • src/components/base-ui/Profile/subscribe_element.tsx
  • src/components/base-ui/Search/search_bookresult.tsx
  • src/components/base-ui/Search/search_recommendbook.tsx
  • src/components/base-ui/Settings/setting_news_list.tsx
  • src/components/base-ui/Settings/setting_report_list.tsx
  • src/components/base-ui/button_without_img.tsx
  • src/components/base-ui/home/home_bookclub.tsx
  • src/components/base-ui/home/list_subscribe_element.tsx
  • src/components/base-ui/home/notification_element.tsx
  • src/components/layout/Header.tsx
  • src/components/layout/NavItem.tsx
  • tsconfig.json
💤 Files with no reviewable changes (1)
  • src/app/layout.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
src/app/(main)/layout.tsx (1)
src/components/layout/Header.tsx (1)
  • Header (15-97)
src/components/layout/Header.tsx (1)
src/components/layout/NavItem.tsx (1)
  • NavItem (9-28)
src/app/(main)/ui-test/page.tsx (1)
src/components/base-ui/Settings/setting_report_list.tsx (1)
  • Setting_ReportList (14-65)
🔇 Additional comments (23)
tsconfig.json (2)

4-8: Formatting improvements to TypeScript configuration.

The reformatting of the lib, paths, and exclude sections to multi-line arrays improves readability and is consistent with modern config formatting practices. These changes are purely cosmetic and do not affect functionality.

Also applies to: 26-28, 41-43


37-39: Good: Added Next.js 15 type definitions and ESM support.

The inclusion of .next/types/**/*.ts, .next/dev/types/**/*.ts, and **/*.mts ensures TypeScript has access to Next.js auto-generated type definitions and ESM module support, which aligns with Next.js 15 and modern TypeScript practices.

src/components/base-ui/Profile/mypage_profile.tsx (1)

6-22: Unused onSettings prop in type definition.

The type defines onSettings (line 16), but at line 17 there's also a reference to settings. Verify that onSettings is correctly wired to the settings button at line 100.

src/components/base-ui/home/home_bookclub.tsx (1)

1-11: LGTM!

The imports and type definitions are clean and appropriate.

src/components/base-ui/Search/search_bookresult.tsx (1)

19-95: Good use of event propagation control.

The stopPropagation() calls on the like and pencil buttons (lines 64, 80) correctly prevent triggering the card's onCardClick handler when interacting with nested controls. This is a solid interaction pattern.

src/components/base-ui/News/news_list.tsx (1)

12-55: Well-structured responsive layout.

The component effectively uses flexbox with proper overflow handling (min-w-0, flex-1) and responsive image loading via Next.js Image. The layout gracefully handles text truncation and maintains fixed spacing for the date column.

src/components/base-ui/Settings/setting_news_list.tsx (2)

28-36: LGTM! Proper Next.js Image usage.

The Image component is correctly configured with:

  • fill prop with relative positioned parent container
  • sizes attribute for optimal image loading
  • object-cover for consistent aspect ratio
  • Meaningful alt text using the dynamic title

19-53: LGTM! Well-structured responsive layout.

The layout implementation demonstrates good practices:

  • Uses max-w-[1000px] w-full for responsive width
  • Proper flex patterns with min-w-0 for text truncation
  • line-clamp-6 for content overflow handling
  • Fixed minimum width for date column maintains layout integrity
src/components/base-ui/BookStory/bookstory_choosebook.tsx (2)

1-11: LGTM!

The imports and type definition are well-structured and appropriate for the component's needs.


26-34: LGTM!

The Next.js Image component is configured correctly with the fill prop, explicit sizes attribute, and meaningful alt text from bookName.

src/components/base-ui/BookStory/bookstory_text.tsx (4)

1-10: LGTM!

The "use client" directive is correctly placed, and the component properly uses React hooks for client-side interactivity. The controlled component pattern in the props type is well-structured.


18-27: LGTM!

The auto-resize logic correctly uses useLayoutEffect to synchronously adjust textarea height before browser paint. The technique of resetting to "0px" before measuring scrollHeight ensures accurate height calculation.


29-50: LGTM!

The custom Tab-to-indent implementation is well-designed. Key strengths:

  • preventDefault() correctly overrides default tab navigation
  • requestAnimationFrame() properly defers cursor positioning until after React's re-render
  • Dependencies include both detail and onChangeDetail to avoid stale closure issues

52-93: LGTM!

The render output correctly wires up the controlled inputs with the auto-resize ref and Tab handler. The rows={6} attribute provides a sensible initial size before the useLayoutEffect takes over height management.

src/components/base-ui/BookStory/bookstory_detail.tsx (2)

1-24: LGTM!

The type definition is comprehensive and well-documented. The flexibility of authorId: string | number accommodates different ID systems, and inline comments clearly document default values for optional props.


42-107: LGTM!

The render logic is well-structured with proper use of Next.js Image and Link components. The layout correctly handles text overflow with truncate and line-clamp-2 utilities. Note that this file correctly uses --primary_2 (line 82), unlike the typo in bookstory_choosebook.tsx.

src/app/(main)/page.tsx (1)

1-3: Placeholder implementation noted.

The HomePage component is currently a stub. This is consistent with the PR objectives indicating that base UI components are being established. Consider adding a comment or TODO to track the intended implementation.

.github/ISSUE_TEMPLATE/custom.md (2)

5-7: LGTM!

The frontmatter formatting is now consistent with proper YAML syntax using quoted empty strings.


11-15: LGTM!

Adding the "기타 (설명 추가)" option improves flexibility for issue categorization.

src/components/base-ui/button_without_img.tsx (3)

25-31: LGTM!

The toCssColor helper correctly handles various color input formats (CSS variables, direct colors, and var() syntax).


5-51: LGTM!

The type definition and prop defaults are well-structured. Good practices:

  • Sensible defaults for colors and dimensions
  • Optional onClick accommodates submit buttons
  • Support for HTML button types

52-56: LGTM!

The hover state logic correctly applies hover colors when provided and falls back to default colors appropriately.

src/components/layout/Header.tsx (1)

1-6: LGTM! The directives and imports are correct.

The "use client" directive is necessary for using usePathname() in Next.js App Router, and all imports (next/link, next/navigation, next/image) are valid in Next.js 16.0.1. No breaking changes affect these directives or imports.

background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
font-family: "Pretendard Variable", Pretendard, system-ui, sans-serif;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for Pretendard font references and loading
rg -n "Pretendard" -C 2

# Also search for @font-face declarations
rg -n "@font-face" -C 2

# Check for Next.js font imports
rg -n "next/font" -C 2

# Check the main layout/app files
fd -e "layout\.(tsx|ts|jsx|js)" -e "app\.(tsx|ts|jsx|js)" | head -10 | xargs -I {} echo "=== {} ===" && cat -n "{}"

Repository: checkmo2025/FE

Length of output: 587


🏁 Script executed:

#!/bin/bash
# Read the full layout.tsx file
echo "=== src/app/layout.tsx ===" 
cat -n src/app/layout.tsx

echo -e "\n=== Search for @import statements with Pretendard ==="
rg "@import.*Pretendard" -A 1 -B 1

echo -e "\n=== Search for font-face in CSS files ==="
rg "@font-face" --type=css -A 5

echo -e "\n=== Check globals.css for font imports ==="
head -60 src/app/globals.css | cat -n

echo -e "\n=== Search for Pretendard in package.json ==="
rg "pretendard" -i -C 2

Repository: checkmo2025/FE

Length of output: 3087


🏁 Script executed:

#!/bin/bash
# Check for font files in public folder
echo "=== Checking for font files in public ==="
find public -name "*pretendard*" -o -name "*.ttf" -o -name "*.woff*" 2>/dev/null | head -20

echo -e "\n=== Check next.config.js ==="
find . -name "next.config.*" -type f | head -5 | xargs cat -n 2>/dev/null

echo -e "\n=== Check if there are other CSS files that might load fonts ==="
fd -e "\.css" -e "\.scss" | xargs rg -l "font-family|@font-face|@import" | head -10

echo -e "\n=== Look for Pretendard in HTML or tsx files ==="
rg "Pretendard" --type=tsx --type=ts --type=html

Repository: checkmo2025/FE

Length of output: 522


Add font loading for Pretendard Variable.

The body font-family on line 52 references "Pretendard Variable" but no font loading mechanism exists in the codebase. There is no @font-face declaration, no font files in the public folder, and no import statements. Only the Geist fonts are currently loaded via the Next.js font API.

Add font loading via:

  • A @font-face rule with font files in the public folder, or
  • An @import url() statement to a CDN (e.g., Google Fonts, Noto Sans KR), or
  • A Next.js font import if Pretendard is available on next/font

Without proper loading, users will see a flash of unstyled text (FOUT) or fallback to system fonts.

🤖 Prompt for AI Agents
In src/app/globals.css around line 52, the body references "Pretendard Variable"
but no font is loaded; add a font-loading mechanism before the font-family
usage: either (A) place Pretendard variable font files in public/fonts and add a
corresponding @font-face declaration in globals.css pointing to those files with
font-display: swap and the correct font-weight/format, (B) add an @import
url(...) at the top of globals.css to a reliable CDN that serves Pretendard, or
(C) import Pretendard via next/font in _app or layout and apply the returned
className; ensure the font is loaded prior to usage, include font-display: swap
to avoid blocking, and update the font-family fallback order if needed.

Comment on lines +57 to +68
.Headline_1 { @apply text-[48px] font-bold leading-[135%] tracking-[-0.1%]; }
.Headline_2 { @apply text-[36px] font-bold leading-[135%] tracking-[-0.1%]; }
.Headline_3 { @apply text-[32px] font-bold leading-[135%] tracking-[-0.1%]; }

/* Subhead */
.Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}
.Subhead_3_1 { @apply text-[18px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_3_2 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.1%]; }
.Subhead_4_1 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.018px]; }

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix invalid letter-spacing values in typography utilities.

Several utility classes use tracking-[-0.1%] which is invalid CSS. The letter-spacing property does not accept percentage values.

Lines with invalid tracking:

  • Lines 57-59: tracking-[-0.1%] in Headline classes
  • Lines 62-63, 65-66: tracking-[-0.1%] in Subhead classes

Additionally, there's inconsistency in tracking units across utilities:

  • Some use tracking-[-0.02px], tracking-[-0.018px], tracking-[-0.014px] (px units - valid)
  • Others use tracking-[-0.1%] (percentage - invalid)
🔎 Proposed fix for tracking values

Replace percentage tracking with valid units. If the design intent is -0.1% of font size, use em units instead:

  /* Headline */
- .Headline_1 { @apply text-[48px] font-bold leading-[135%] tracking-[-0.1%]; }
- .Headline_2 { @apply text-[36px] font-bold leading-[135%] tracking-[-0.1%]; }
- .Headline_3 { @apply text-[32px] font-bold leading-[135%] tracking-[-0.1%]; }
+ .Headline_1 { @apply text-[48px] font-bold leading-[135%] tracking-[-0.001em]; }
+ .Headline_2 { @apply text-[36px] font-bold leading-[135%] tracking-[-0.001em]; }
+ .Headline_3 { @apply text-[32px] font-bold leading-[135%] tracking-[-0.001em]; }

  /* Subhead */
- .Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.1%]; }
- .Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1%]; }
+ .Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.001em]; }
+ .Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1em]; }
  .Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}
- .Subhead_3_1 { @apply text-[18px] font-semibold leading-[135%] tracking-[-0.1%]; }
- .Subhead_3_2 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.1%]; }
+ .Subhead_3_1 { @apply text-[18px] font-semibold leading-[135%] tracking-[-0.001em]; }
+ .Subhead_3_2 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.001em]; }
  .Subhead_4_1 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.018px]; }

Verify the intended tracking values with your design team.

📝 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
.Headline_1 { @apply text-[48px] font-bold leading-[135%] tracking-[-0.1%]; }
.Headline_2 { @apply text-[36px] font-bold leading-[135%] tracking-[-0.1%]; }
.Headline_3 { @apply text-[32px] font-bold leading-[135%] tracking-[-0.1%]; }
/* Subhead */
.Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}
.Subhead_3_1 { @apply text-[18px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_3_2 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.1%]; }
.Subhead_4_1 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.018px]; }
/* Headline */
.Headline_1 { @apply text-[48px] font-bold leading-[135%] tracking-[-0.001em]; }
.Headline_2 { @apply text-[36px] font-bold leading-[135%] tracking-[-0.001em]; }
.Headline_3 { @apply text-[32px] font-bold leading-[135%] tracking-[-0.001em]; }
/* Subhead */
.Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.001em]; }
.Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1em]; }
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}
.Subhead_3_1 { @apply text-[18px] font-semibold leading-[135%] tracking-[-0.001em]; }
.Subhead_3_2 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.001em]; }
.Subhead_4_1 { @apply text-[18px] font-medium leading-[135%] tracking-[-0.018px]; }
🤖 Prompt for AI Agents
In src/app/globals.css around lines 57 to 68 the typography utilities use
invalid percentage values for letter-spacing (e.g. tracking-[-0.1%]) which CSS
does not accept; replace those percentage-based tracking values with valid units
(preferably relative units like em for proportional spacing or px for absolute
spacing) and make tracking units consistent across all Headline/Subhead classes
(convert every tracking-[-0.1%] to an appropriate value such as -0.001em or a
matching px value agreed with design), ensuring the small px values (e.g.
-0.02px, -0.018px) are normalized to the chosen unit system.

Comment on lines +53 to +69
<button
type="button"
onClick={onButtonClick}
className="
flex w-[132px] h-[44px]
px-[16px] py-[12px]
justify-center items-center gap-[10px]
rounded-[8px]
border border-[color:var(--premary_2,#9A7A6B)]
bg-[color:var(--background,#F9F7F6)]
text-[color:var(--premary_2,#9A7A6B)]
Body_1_2
whitespace-nowrap
"
>
변경하기
</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix typo in CSS variable name.

Lines 61, 62, and 63 use --premary_2 but should be --primary_2 (note the missing 'i'). This typo will prevent the intended colors from applying. The correct spelling is used in bookstory_detail.tsx line 82.

🔎 Proposed fix
            className="
              flex w-[132px] h-[44px]
              px-[16px] py-[12px]
              justify-center items-center gap-[10px]
              rounded-[8px]
-             border border-[color:var(--premary_2,#9A7A6B)]
-             bg-[color:var(--background,#F9F7F6)]
-             text-[color:var(--premary_2,#9A7A6B)]
+             border border-[color:var(--primary_2,#9A7A6B)]
+             bg-[color:var(--background,#F9F7F6)]
+             text-[color:var(--primary_2,#9A7A6B)]
              Body_1_2
              whitespace-nowrap
            "
📝 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
<button
type="button"
onClick={onButtonClick}
className="
flex w-[132px] h-[44px]
px-[16px] py-[12px]
justify-center items-center gap-[10px]
rounded-[8px]
border border-[color:var(--premary_2,#9A7A6B)]
bg-[color:var(--background,#F9F7F6)]
text-[color:var(--premary_2,#9A7A6B)]
Body_1_2
whitespace-nowrap
"
>
변경하기
</button>
<button
type="button"
onClick={onButtonClick}
className="
flex w-[132px] h-[44px]
px-[16px] py-[12px]
justify-center items-center gap-[10px]
rounded-[8px]
border border-[color:var(--primary_2,#9A7A6B)]
bg-[color:var(--background,#F9F7F6)]
text-[color:var(--primary_2,#9A7A6B)]
Body_1_2
whitespace-nowrap
"
>
변경하기
</button>
🤖 Prompt for AI Agents
In src/components/base-ui/BookStory/bookstory_choosebook.tsx around lines 53 to
69 the CSS variable is misspelled as --premary_2 in three places (border,
bg/text color entries); update each occurrence to --primary_2 so the intended
theme colors apply (replace --premary_2 with --primary_2 in the className
string).

Comment on lines +70 to +75
</div>

</div>

);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove extra closing </div> tag.

Line 72 contains an extraneous closing </div> tag that breaks the JSX structure. The correct structure should close the main container at line 72, but there's an extra </div> before it.

🔎 Proposed fix
          </button>
        </div>

-      </div>
-
   );
 }
📝 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
</div>
</div>
);
}
</div>
);
}
🤖 Prompt for AI Agents
In src/components/base-ui/BookStory/bookstory_choosebook.tsx around lines 70 to
75, there is an extra closing </div> that breaks the JSX hierarchy; remove the
extraneous </div> (the one before the main container's closing tag) so the JSX
tags properly nest and the main container closes only once.

Comment on lines +26 to +40
export default function BookstoryDetail({
imageUrl = "/bookstory_example.svg",
authorName,
authorNickname,
authorId,
profileImgSrc = "/profile2.svg",
subscribeText = "구독",
onSubscribeClick,
bookTitle,
bookAuthor,
bookDetail,
authorHref,
className = "",
}: BookstoryDetailProps) {
const href = authorHref ?? `/profile/${authorId}`;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if public directory exists and find the default image files
find . -type d -name "public" 2>/dev/null

# Search for the specific image files mentioned in the review
echo "=== Searching for bookstory_example.svg ==="
fd -t f "bookstory_example.svg" . 2>/dev/null || echo "Not found"

echo "=== Searching for profile2.svg ==="
fd -t f "profile2.svg" . 2>/dev/null || echo "Not found"

# Also check what's in public directory if it exists
echo "=== Contents of public directory ==="
find ./public -type f 2>/dev/null | head -20 || echo "public directory not found or empty"

Repository: checkmo2025/FE

Length of output: 635


Fix missing default image file.

The imageUrl default prop references /bookstory_example.svg which does not exist in the public directory. This will result in broken images when the component is used without an imageUrl prop. Either add the missing file or use an existing alternative like /booksample.svg.

The profileImgSrc default (/profile2.svg) exists and is correct.

🤖 Prompt for AI Agents
In src/components/base-ui/BookStory/bookstory_detail.tsx around lines 26 to 40,
the default imageUrl points to /bookstory_example.svg which isn't present in
public causing broken images; either add the missing
/public/bookstory_example.svg file or change the default value to an existing
asset (e.g., /booksample.svg) and update any import/path references accordingly
so the component uses a valid image when imageUrl is omitted.

text-[13px] flex items-center justify-center gap-2"
>
<Image src="/icon_plus.svg" alt="icon_plus" width={24} height={24} />
<span className="text-color-white Subhead_4_1">모임 생성하기</span>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use standard Tailwind class text-white instead of text-color-white.

The class text-color-white is not a standard Tailwind utility. Use text-white for white text color.

🔎 Proposed fix
-              <span className="text-color-white Subhead_4_1">모임 생성하기</span>
+              <span className="text-white Subhead_4_1">모임 생성하기</span>
📝 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
<span className="text-color-white Subhead_4_1">모임 생성하기</span>
<span className="text-white Subhead_4_1">모임 생성하기</span>
🤖 Prompt for AI Agents
In src/components/base-ui/home/home_bookclub.tsx around line 97, the span uses a
non-standard Tailwind class "text-color-white"; replace it with the standard
utility "text-white" to ensure the text color applies correctly and aligns with
Tailwind conventions.

Comment on lines +1 to +47
import React from "react";

type NotificationType = "like" | "comment";

type NotificationElementProps = {
nickname: string;
type: NotificationType;
commentContent?: string;
timeLabel: string; // "지금", "2분 전"
isLatest?: boolean;
};

export default function NotificationElement({
nickname,
type,
commentContent,
timeLabel,
isLatest = false,
}: NotificationElementProps) {
const textColorClass = isLatest
? "text-[color:var(--Gray_7,#2C2C2C)]"
: "text-[color:var(--Gray_3,#BBB)]";

const message =
type === "like"
? `${nickname}님이 좋아요를 눌렀습니다.`
: `${nickname}님이 댓글을 남겼습니다.${commentContent ? `: ${commentContent}` : ""}`;

return (
<div className="flex w-[364px] px-[16px] py-[20px] justify-between items-center border-b border-b-[color:var(--Subbrown_4,#EAE5E2)] bg-[color:var(--White,#FFF)]">
{/* left */}
<div className="flex items-center gap-[12px] min-w-0">

{isLatest ? (
<span className="w-[12px] h-[12px] rounded-full bg-red-500 shrink-0" />
) : (<span className="w-[12px] h-[12px] shrink-0" />)}

<p className={`${textColorClass} Body_1_2 truncate`}>{message}</p>
</div>

{/* right */}
<span className="shrink-0 text-[color:var(--Gray_3,#BBB)] Body_1_2">
{timeLabel}
</span>
</div>
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Duplicate component: identical to home/notification_element.tsx.

This file is virtually identical to src/components/base-ui/home/notification_element.tsx. Extract a single shared NotificationElement component to a common location (e.g., src/components/base-ui/common/) and import it where needed.

🔎 Suggested structure
src/components/base-ui/common/notification_element.tsx  # single source of truth
src/components/base-ui/Profile/                         # re-export or import from common
src/components/base-ui/home/                            # re-export or import from common
🤖 Prompt for AI Agents
In src/components/base-ui/Profile/notification_element.tsx lines 1-47, this
component is duplicated from
src/components/base-ui/home/notification_element.tsx; extract a single shared
NotificationElement into src/components/base-ui/common/notification_element.tsx
(export the component and its props/type), update both Profile and home modules
to import that common component (or create small re-export files under Profile/
and home/ that import and re-export the common one), and remove or replace the
duplicate implementation so there is one source of truth; ensure TypeScript
types are exported/imported and update any relative import paths accordingly.

Comment on lines +5 to +56
type BookCoverCardProps = {
imgUrl?: string;
title: string;
author: string;

liked: boolean;
onLikeChange: (next: boolean) => void;

onCardClick?: () => void;
className?: string;
};

export default function Search_BookCoverCard({
imgUrl,
title,
author,
liked,
onLikeChange,
onCardClick,
className = "",
}: BookCoverCardProps) {
const coverSrc = imgUrl && imgUrl.length > 0 ? imgUrl : "/booksample.svg";

return (
<div
onClick={onCardClick}
className={`relative flex w-[332px] h-[436px] p-[16px] flex-col justify-end items-start gap-[12px] overflow-hidden ${
onCardClick ? "cursor-pointer" : ""
} ${className}`}
>
<Image src={coverSrc} alt={title} fill sizes="244px" className="object-cover" />

<div className="relative z-[1] flex flex-col items-start gap-[10px]">
<button
type="button"
onClick={(e) => {
e.stopPropagation();
onLikeChange(!liked);
}}
className="w-[24px] h-[24px] shrink-0"
>
<Image src={liked ? "/red_heart.svg" : "/gray_heart.svg"} alt="" width={24} height={24} />
</button>

<div className="flex flex-col items-start gap-[6px] min-w-0">
<p className="text-[color:var(--White,#FFF)] Subhead_1 truncate">{title}</p>
<p className="text-[color:var(--White,#FFF)] Subhead_4 truncate">{author}</p>
</div>
</div>
</div>
);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Significant code duplication with News/recommendbook_element.tsx.

This component is nearly identical to src/components/base-ui/News/recommendbook_element.tsx, differing only in:

  • Component name (Search_BookCoverCard vs BookCoverCard)
  • Dimensions (w-[332px] h-[436px] vs w-[244px] h-[320px])
  • Gap spacing (gap-[6px] vs gap-[4px])

Consider extracting a shared base component and accepting dimension/spacing props, or creating a single component with variant configurations to eliminate duplication and improve maintainability.

🔎 Suggested approach

Create a shared component:

// src/components/base-ui/shared/BookCoverCard.tsx
type BookCoverCardProps = {
  imgUrl?: string;
  title: string;
  author: string;
  liked: boolean;
  onLikeChange: (next: boolean) => void;
  onCardClick?: () => void;
  className?: string;
  variant?: 'search' | 'news'; // or pass dimensions directly
};

export default function BookCoverCard({ variant = 'news', ...props }: BookCoverCardProps) {
  const dimensions = variant === 'search' 
    ? { width: 'w-[332px]', height: 'h-[436px]', gap: 'gap-[6px]' }
    : { width: 'w-[244px]', height: 'h-[320px]', gap: 'gap-[4px]' };
  // ... rest of implementation
}

Then re-export from Search and News directories with appropriate defaults.

🤖 Prompt for AI Agents
In src/components/base-ui/Search/search_recommendbook.tsx (lines 5-56), this
component duplicates src/components/base-ui/News/recommendbook_element.tsx with
only size/spacing and name differences; extract a shared BookCoverCard component
(e.g., src/components/base-ui/shared/BookCoverCard.tsx) that accepts imgUrl,
title, author, liked, onLikeChange, onCardClick, className and either a variant
prop ('search'|'news') or explicit dimension/spacing props, implement the shared
markup/behavior (preserve Image fallback, stopPropagation in like button, click
handling), then update both Search/search_recommendbook.tsx and
News/recommendbook_element.tsx to import the shared component and re-export thin
wrappers that pass the correct variant or className for their sizes/gaps so
duplication is removed and behavior remains identical.

<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-[12px]">
<div className="flex items-center gap-[8px] min-w-0">
<Image src="/profile5.svg" alt="" width={24} height={24} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hard-coded profile image breaks reporter differentiation.

The profile image is hard-coded to /profile5.svg for all reporters, which prevents visual differentiation between different reporters. Additionally, the empty alt attribute fails to provide context for screen readers.

🔎 Recommended fix

Add a profileImageUrl prop and provide meaningful alt text:

 type SettingReportListProps = {
   badgeText: string;
   reporterName: string;
   reportedAt: string;
   content: string;
+  profileImageUrl?: string;
   className?: string;
 };

 export default function Setting_ReportList({
   badgeText,
   reporterName,
   reportedAt,
   content,
+  profileImageUrl = "/profile5.svg",
   className = "",
 }: SettingReportListProps) {
   return (
     // ...
-    <Image src="/profile5.svg" alt="" width={24} height={24} />
+    <Image src={profileImageUrl} alt={`${reporterName}의 프로필`} width={24} height={24} />

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/base-ui/Settings/setting_report_list.tsx around line 46, the
Image is hard-coded to /profile5.svg and has an empty alt; change the component
to accept a profileImageUrl (or use reporter.profileImageUrl) and render <Image
src={profileImageUrl || '/default-profile.svg'} alt={`${reporter.name ||
'Reporter'} profile`} width={24} height={24} /> so each reporter can show a
unique image and screen readers get meaningful context; also update any parent
callers to pass profileImageUrl or ensure the reporter object includes it.

Comment on lines 35 to 36
"checkmo/.next/types/**/*.ts",
"checkmo/.next/dev/types/**/*.ts",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the tsconfig.json file to see the actual content
cat -n tsconfig.json

Repository: checkmo2025/FE

Length of output: 1157


🏁 Script executed:

# Check if the "checkmo" directory exists in the project
find . -type d -name "checkmo" 2>/dev/null | head -20

Repository: checkmo2025/FE

Length of output: 40


🏁 Script executed:

# Search for "checkmo" references throughout the codebase
rg "checkmo" --type json --type yaml --type ts --type js 2>/dev/null | head -30

Repository: checkmo2025/FE

Length of output: 275


🏁 Script executed:

# Check the project structure, particularly looking for monorepo indicators
ls -la
cat package.json 2>/dev/null | head -50

Repository: checkmo2025/FE

Length of output: 2039


Remove leftover "checkmo/" paths from include section.

Lines 35–36 reference "checkmo/.next/types/**/*.ts" and "checkmo/.next/dev/types/**/*.ts", but the checkmo/ directory does not exist in the project. The project is a single Next.js application with the package name "checkmo", not a monorepo. The correct paths without the checkmo/ prefix already exist on lines 38–39.

Remove lines 35–36 to eliminate dead includes and avoid type resolution confusion.

🤖 Prompt for AI Agents
In tsconfig.json around lines 35 to 36, remove the two leftover include entries
"checkmo/.next/types/**/*.ts" and "checkmo/.next/dev/types/**/*.ts" because the
project is not a monorepo and those paths don't exist; delete those lines so the
existing correct includes (without the "checkmo/" prefix on lines 38–39) remain
and prevent dead includes/type resolution confusion.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

전반적으로 새로운 UI 컴포넌트들을 많이 추가해주셨네요. 고생하셨습니다! 코드 리뷰에서는 주로 CSS 변수명 오타, Tailwind CSS 문법 오류, 하드코딩된 값 사용, 그리고 컴포넌트 중복과 같은 부분을 중심으로 피드백을 드렸습니다. 제안된 수정 사항들을 반영하면 코드의 일관성과 유지보수성이 더 향상될 것입니다.

flex w-[60px] px-[4px] py-[4px]
justify-center items-center gap-[10px] shrink-0
rounded-[4px]
bg-[color:var(--red,#FF5151)]

Choose a reason for hiding this comment

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

critical

정의되지 않은 CSS 변수 --red를 사용하고 있습니다. 이로 인해 배경색이 적용되지 않습니다. globals.css--Red 또는 비슷한 이름의 변수를 추가하거나, #FF5151과 같은 실제 색상 값을 직접 사용해야 합니다.

Suggested change
bg-[color:var(--red,#FF5151)]
bg-[#FF5151]

/* Subhead */
.Subhead_1 { @apply text-[24px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_2 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.1%]; }
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}

Choose a reason for hiding this comment

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

critical

.Subhead_3 클래스 정의에 문법 오류가 있습니다. @apply 지시어 뒤에 세미콜론(;)이 있고, 바로 뒤에 닫는 중괄호(})가 붙어 있어 CSS 파싱 오류를 유발합니다. 올바른 형식으로 수정해주세요.

Suggested change
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px];}
.Subhead_3 { @apply text-[20px] font-semibold leading-[135%] tracking-[-0.02px]; }

buttonText = "구독",
}: ListSubscribeElementProps) {
return (
<div className="flex w-[296px] h-[66px] px-[14px] py-[8px] items-center gap-[8px] rounded-[8px] border border-[var(--Subbrown_4)] bg-white">

Choose a reason for hiding this comment

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

critical

Tailwind CSS에서 CSS 변수를 사용하여 테두리 색상을 지정할 때 border-[color:var(--...)] 형식을 사용해야 합니다. 현재 border-[var(--Subbrown_4)]는 잘못된 문법입니다.

Suggested change
<div className="flex w-[296px] h-[66px] px-[14px] py-[8px] items-center gap-[8px] rounded-[8px] border border-[var(--Subbrown_4)] bg-white">
<div className="flex w-[296px] h-[66px] px-[14px] py-[8px] items-center gap-[8px] rounded-[8px] border border-[color:var(--Subbrown_4)] bg-white">

text-[13px] flex items-center justify-center gap-2"
>
<Image src="/icon_plus.svg" alt="icon_plus" width={24} height={24} />
<span className="text-color-white Subhead_4_1">모임 생성하기</span>

Choose a reason for hiding this comment

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

critical

text-color-white는 유효한 Tailwind 클래스가 아닙니다. text-white를 사용해야 합니다.

Suggested change
<span className="text-color-white Subhead_4_1">모임 생성하기</span>
<span className="text-white Subhead_4_1">모임 생성하기</span>

>
{open ? (
<div className="flex items-center justify-center gap-1">
<span className="text-[color:var(--Gray_7)] Body_1_2 font-weight: 500">접기</span>

Choose a reason for hiding this comment

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

critical

font-weight: 500은 유효한 Tailwind 클래스가 아닙니다. font-medium 클래스를 사용해야 합니다.

Suggested change
<span className="text-[color:var(--Gray_7)] Body_1_2 font-weight: 500">접기</span>
<span className="text-[color:var(--Gray_7)] Body_1_2 font-medium">접기</span>

</div>
) : (
<div className="flex items-center justify-center gap-1">
<span className="text-[color:var(--Gray_7)] Body_1_2 Body_1_2">전체보기</span>

Choose a reason for hiding this comment

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

medium

Body_1_2 클래스가 중복으로 사용되었습니다. 하나는 제거해도 됩니다.

Suggested change
<span className="text-[color:var(--Gray_7)] Body_1_2 Body_1_2">전체보기</span>
<span className="text-[color:var(--Gray_7)] Body_1_2">전체보기</span>

className?: string;
};

export default function Subscribe({

Choose a reason for hiding this comment

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

medium

컴포넌트 이름(Subscribe)과 파일 이름(subscribe_element.tsx)이 일치하지 않습니다. 혼동을 피하기 위해 이름을 통일하는 것이 좋습니다.

Comment on lines +40 to +41
<p className="text-[#000] Subhead_3 truncate">{bookName}</p>
<p className="text-[#757575] Subhead_4_1 truncate">{author}</p>

Choose a reason for hiding this comment

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

medium

하드코딩된 색상 값(#000, #757575)을 사용하고 있습니다. globals.css에 정의된 색상 토큰(예: var(--Black), var(--Gray_5))을 사용하면 디자인 일관성을 유지하고 유지보수하기 용이해집니다.

Suggested change
<p className="text-[#000] Subhead_3 truncate">{bookName}</p>
<p className="text-[#757575] Subhead_4_1 truncate">{author}</p>
<p className="text-[color:var(--Black)] Subhead_3 truncate">{bookName}</p>
<p className="text-[color:var(--Gray_5)] Subhead_4_1 truncate">{author}</p>

<button
type="button"
onClick={onSubscribeClick}
className="flex px-[17px] py-[8px] justify-center items-center gap-[10px] rounded-[8px] bg-[#9A7A6B] text-white text-[color:var(--White,#FFF)] text-[12px] font-semibold leading-[100%] tracking-[-0.012px] whitespace-nowrap"

Choose a reason for hiding this comment

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

medium

하드코딩된 색상 값 bg-[#9A7A6B]을 사용하고 있습니다. globals.css에 정의된 --primary_2 변수를 사용해주세요.

Suggested change
className="flex px-[17px] py-[8px] justify-center items-center gap-[10px] rounded-[8px] bg-[#9A7A6B] text-white text-[color:var(--White,#FFF)] text-[12px] font-semibold leading-[100%] tracking-[-0.012px] whitespace-nowrap"
className="flex px-[17px] py-[8px] justify-center items-center gap-[10px] rounded-[8px] bg-[color:var(--primary_2)] text-white text-[color:var(--White,#FFF)] text-[12px] font-semibold leading-[100%] tracking-[-0.012px] whitespace-nowrap"

</p>
</div>


Choose a reason for hiding this comment

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

medium

이 부분에 비어있는 코드가 있습니다. 의도된 것인지 확인하고, 불필요하다면 제거해주세요.

@hongik-luke hongik-luke closed this Jan 1, 2026
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.

4 participants