Skip to content

랜딩페이지#520

Open
Bangdayeon wants to merge 2 commits into
mainfrom
feat/#512-landing-page
Open

랜딩페이지#520
Bangdayeon wants to merge 2 commits into
mainfrom
feat/#512-landing-page

Conversation

@Bangdayeon
Copy link
Copy Markdown
Member

관련 이슈

PR 설명

  • 랜딩 페이지 피그마 디자인 적용하여 제작

@Bangdayeon Bangdayeon self-assigned this Jun 3, 2026
@Bangdayeon Bangdayeon linked an issue Jun 3, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Walkthrough

이 PR은 기존의 모놀리식 LandingPage.tsx 파일을 제거하고, 랜딩 페이지 구조를 재사용 가능한 섹션 기반 컴포넌트로 리팩토링합니다. page.tsx의 진입점을 LandingHeader, HeroSection, FeatureSection, LandingFooter의 조합으로 변경하며, 각 섹션을 구성하는 세부 UI 컴포넌트들(ExperienceItem, FAQItem, FeatureCard, HowUseItem, ScrollStackedCards 등)을 추가합니다. Google OAuth 로그인 및 Chrome 웹스토어 링크 기능은 GoogleLoginButton, ChromeButton 등의 독립적인 컴포넌트로 분리되어 각 섹션에서 활용됩니다. 아이콘 크기에 '2xl' 옵션을 추가하고 레이아웃 오버플로우 처리를 조정합니다.

Possibly related PRs

  • Team-SoFa/linkiving#335: 동일한 src/app/LandingPage.tsx 파일의 로그인 UI/구글 로그인 버튼 디자인을 수정한 이전 PR로, 이 PR의 LandingPage 전체 삭제로 인해 직접적으로 대체되는 변경 사항입니다.
  • Team-SoFa/linkiving#293: Google OAuth 로그인 흐름에서 NEXT_PUBLIC_BASE_API_URL 환경 변수 확인 및 /oauth2/authorization/google 리다이렉트 로직을 다루는 PR로, 이 PR의 GoogleLoginButton, LandingHeader, LandingFooter 컴포넌트에서 동일한 패턴이 사용됩니다.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive PR 제목이 너무 모호하고 일반적이어서 주요 변경사항을 명확하게 전달하지 못합니다. "랜딩페이지 UI 컴포넌트로 재구성" 또는 "LandingPage를 섹션 컴포넌트로 분리하여 재구현"과 같이 구체적인 변경 내용을 포함하는 제목으로 수정해주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 템플릿을 따르고 있으나 구체적인 변경 사항에 대한 상세 설명이 부족합니다.
Linked Issues check ✅ Passed PR 변경사항이 #512 이슈의 "피그마에 올라온 랜딩 페이지 디자인 적용"이라는 목표를 충족합니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경사항이 랜딩 페이지 구현 범위 내에 있으며, 범위 외의 변경사항은 없습니다.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#512-landing-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
src/app/page.tsx (1)

70-76: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

불필요한 searchParams 의존 제거로 페이지 정적 최적화 복구 필요

  • src/app/page.tsx (70-76)에서 searchParamsawait하지만 이후 params를 사용하지 않습니다.
  • Next.js App Router에서는 searchParams를 사용하는 것만으로도 라우트가 동적 렌더링으로 옵트인되어 정적 생성이 디옵트됩니다.
  • Page 시그니처에서 searchParamsconst params = await searchParams;를 제거하세요. 쿼리값이 필요하다면 useSearchParams(Client 컴포넌트) 또는 동적 라우트로 분리하는 방식이 맞습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/page.tsx` around lines 70 - 76, The Page function currently declares
and awaits searchParams (function Page and const params = await searchParams)
but never uses params, which forces dynamic rendering; remove the searchParams
parameter from the Page signature and delete the line "const params = await
searchParams;" to restore static optimization, and if query values are required
later convert to a client component using useSearchParams or a dedicated dynamic
route instead.
🧹 Nitpick comments (1)
src/components/layout/Landing/LandingHeader.tsx (1)

14-23: ⚡ Quick win

Google OAuth 리다이렉트 로직을 공통화해주세요.

동일한 ${baseUrl}/oauth2/authorization/google 조립/이동 로직이 LandingHeader, LandingFooter, GoogleLoginButton에 중복되어 있고, 누락 env 처리도 이미 다르게 동작합니다. 공통 함수(또는 GoogleLoginButton 재사용)로 모아 드리프트를 막는 게 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Landing/LandingHeader.tsx` around lines 14 - 23,
Duplicate Google OAuth redirect assembly/redirect logic across
LandingHeader.handleStart, LandingFooter, and GoogleLoginButton should be
centralized; create a shared helper (e.g., getGoogleOAuthUrl or
redirectToGoogleOAuth) that reads NEXT_PUBLIC_BASE_API_URL once, throws or
returns a Result/error when missing, and returns the full URL
(`${baseUrl}/oauth2/authorization/google`) or performs the window.location.href
change. Replace handleStart and other inline implementations to call this helper
(or reuse GoogleLoginButton) so env missing handling is consistent and the
redirect URL assembly is not duplicated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/layout/Landing/FAQItem.tsx`:
- Around line 10-30: The root element of the FAQItem component should be changed
from a <div> to an <li> to preserve list semantics when this component is
rendered inside a <ul>; update the JSX root element (the element wrapping the
button and the answer container in the FAQItem component) to <li> and keep all
existing attributes and className values (including aria-expanded on the button
and the grid/overflow structure) so behavior and styling remain unchanged;
ensure there are no invalid nested list/interactive element rules (button stays
as a child of the new <li>) and run the app’s linter/HTML validator to confirm
semantics are fixed.

In `@src/components/layout/Landing/FeatureCard.tsx`:
- Line 23: The Image components using the next/image "fill" prop should include
a responsive sizes prop to avoid oversized image selection; update the Image
usages in FeatureCard (component FeatureCard), ExperienceItem (ExperienceItem),
HowUseItem (HowUseItem), LinkThumbnailTitleSection (component
LinkThumbnailTitleSection) and AddMultiLinks (AddMultiLinks) to add a sizes
string matching the layout (for example: mobile ~100vw, tablet ~50vw, desktop
~33vw) so the browser can pick appropriately sized images for each breakpoint.

In `@src/components/wrappers/ChromeButton.tsx`:
- Around line 18-25: The ChromeButton component is showing google-icon.png while
its text and behavior target the Chrome Web Store; update the Image usage in
ChromeButton (the Image component instance) to use the correct Chrome icon asset
(e.g., change src from "google-icon.png" to the Chrome asset name such as
"chrome-icon.png"), keep a matching alt like "Chrome 로고", and ensure the
referenced chrome icon file exists in the public/images assets so the button
shows the proper Chrome icon.

In `@src/components/wrappers/GoogleLoginButton.tsx`:
- Around line 16-18: The button in the GoogleLoginButton component is missing an
explicit type which can cause it to act as a form submitter; update the JSX for
the button that calls handleGoogleLogin to include type="button" so it won't
trigger form submission (i.e., modify the <button ...> in GoogleLoginButton.tsx
that uses handleGoogleLogin to add the type attribute).

---

Outside diff comments:
In `@src/app/page.tsx`:
- Around line 70-76: The Page function currently declares and awaits
searchParams (function Page and const params = await searchParams) but never
uses params, which forces dynamic rendering; remove the searchParams parameter
from the Page signature and delete the line "const params = await searchParams;"
to restore static optimization, and if query values are required later convert
to a client component using useSearchParams or a dedicated dynamic route
instead.

---

Nitpick comments:
In `@src/components/layout/Landing/LandingHeader.tsx`:
- Around line 14-23: Duplicate Google OAuth redirect assembly/redirect logic
across LandingHeader.handleStart, LandingFooter, and GoogleLoginButton should be
centralized; create a shared helper (e.g., getGoogleOAuthUrl or
redirectToGoogleOAuth) that reads NEXT_PUBLIC_BASE_API_URL once, throws or
returns a Result/error when missing, and returns the full URL
(`${baseUrl}/oauth2/authorization/google`) or performs the window.location.href
change. Replace handleStart and other inline implementations to call this helper
(or reuse GoogleLoginButton) so env missing handling is consistent and the
redirect URL assembly is not duplicated.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 235d7e0e-4f81-4b90-a3ce-c024fc387977

📥 Commits

Reviewing files that changed from the base of the PR and between e155395 and 03b9c1d.

⛔ Files ignored due to path filters (14)
  • public/images/landing-1.png is excluded by !**/*.png
  • public/images/landing-2.png is excluded by !**/*.png
  • public/images/landing-3.png is excluded by !**/*.png
  • public/images/landing-4.png is excluded by !**/*.png
  • public/images/landing-5.png is excluded by !**/*.png
  • public/images/landing-6.png is excluded by !**/*.png
  • public/images/landing-7.png is excluded by !**/*.png
  • public/images/landing-add.png is excluded by !**/*.png
  • public/images/landing-ai.png is excluded by !**/*.png
  • public/images/landing-chatbot.png is excluded by !**/*.png
  • public/images/landing-extension.png is excluded by !**/*.png
  • public/images/landing_icon_bookmark.png is excluded by !**/*.png
  • public/images/landing_icon_questionmark.png is excluded by !**/*.png
  • public/images/landing_icon_searchoff.png is excluded by !**/*.png
📒 Files selected for processing (15)
  • src/app/LandingPage.tsx
  • src/app/layout-client.tsx
  • src/app/page.tsx
  • src/components/Icons/icons.ts
  • src/components/layout/Landing/ExperienceItem.tsx
  • src/components/layout/Landing/FAQItem.tsx
  • src/components/layout/Landing/FeatureCard.tsx
  • src/components/layout/Landing/FeatureSection.tsx
  • src/components/layout/Landing/HeroSection.tsx
  • src/components/layout/Landing/HowUseItem.tsx
  • src/components/layout/Landing/LandingFooter.tsx
  • src/components/layout/Landing/LandingHeader.tsx
  • src/components/layout/Landing/ScrollStackedCards.tsx
  • src/components/wrappers/ChromeButton.tsx
  • src/components/wrappers/GoogleLoginButton.tsx
💤 Files with no reviewable changes (1)
  • src/app/LandingPage.tsx

Comment on lines +10 to +30
<div className="w-full rounded-[20px] bg-[linear-gradient(180deg,#F5F6FA_0%,#EAEDFF_100%)] px-13 py-12">
<button
type="button"
aria-expanded={isOpen}
onClick={() => setIsOpen(prev => !prev)}
className="flex w-full cursor-pointer items-center justify-between text-left"
>
<span className="text-[20px] font-semibold md:text-[32px]">Q. {title}</span>
<SVGIcon icon={isOpen ? 'IC_Up' : 'IC_Down'} size="2xl" />
</button>
<div
className={`grid transition-[grid-template-rows] duration-300 ease-in-out ${
isOpen ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'
}`}
>
<div className="overflow-hidden">
<span className="block pt-4 text-[20px] leading-[160%] md:text-[32px]">{content}</span>
</div>
</div>
</div>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

FAQ 항목 루트 태그를 <li>로 바꿔 목록 시맨틱을 맞춰주세요

현재 컴포넌트가 <ul> 내부에서 렌더링되는데 루트가 <div>라서 HTML 시맨틱이 깨집니다. 루트를 <li>로 바꾸는 게 안전합니다.

제안 수정안
-    <div className="w-full rounded-[20px] bg-[linear-gradient(180deg,`#F5F6FA_0`%,`#EAEDFF_100`%)] px-13 py-12">
+    <li className="w-full rounded-[20px] bg-[linear-gradient(180deg,`#F5F6FA_0`%,`#EAEDFF_100`%)] px-13 py-12">
@@
-    </div>
+    </li>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Landing/FAQItem.tsx` around lines 10 - 30, The root
element of the FAQItem component should be changed from a <div> to an <li> to
preserve list semantics when this component is rendered inside a <ul>; update
the JSX root element (the element wrapping the button and the answer container
in the FAQItem component) to <li> and keep all existing attributes and className
values (including aria-expanded on the button and the grid/overflow structure)
so behavior and styling remain unchanged; ensure there are no invalid nested
list/interactive element rules (button stays as a child of the new <li>) and run
the app’s linter/HTML validator to confirm semantics are fixed.

className={`${bgClassName} flex h-[60vh] flex-col items-center gap-10 rounded-[20px] px-[55px] py-18 shadow-xl md:flex-row`}
>
<div className="relative aspect-700/337 w-full max-w-[700px]">
<Image src={image} alt={imageAlt} fill />
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# fill 사용 Image 중 sizes 누락 위치 점검
rg -nP --type=tsx '<Image(?:(?!>).)*\bfill\b(?:(?!>).)*(?=>)' -g '!**/*.test.tsx'
rg -nP --type=tsx '<Image(?:(?!>).)*\bfill\b(?:(?!>).)*(?=>)' -g '!**/*.test.tsx' | rg -v '\bsizes='

Repository: Team-SoFa/linkiving

Length of output: 119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) FeatureCard.tsx 주변 확인
echo "---- FeatureCard.tsx (context around line 23) ----"
nl -ba src/components/layout/Landing/FeatureCard.tsx | sed -n '15,35p' || true

# 2) src 전체에서 <Image ... fill ...> 이지만 같은 태그에 sizes= 없는 경우(단일 라인 기준)
echo "---- <Image ... fill ...> without sizes= (single-line elements, src/**.tsx) ----"
rg -n --glob '!**/*.test.tsx' --glob '*.tsx' -S '<Image[^>]*\bfill\b' src \
  | rg -v 'sizes=' || true

# 3) 혹시 fill/sizes가 여러 줄로 나뉘는 경우를 대비해, fill 라인만 먼저 수집
echo "---- Lines containing fill in TSX (for manual spot check) ----"
rg -n --glob '!**/*.test.tsx' --glob '*.tsx' -S '\bfill\b' src \
  | head -n 50 || true

Repository: Team-SoFa/linkiving

Length of output: 2189


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) FeatureCard.tsx 주변 확인
echo "---- FeatureCard.tsx (context around line 23) ----"
nl -ba src/components/layout/Landing/FeatureCard.tsx | sed -n '15,35p' || true

# 2) src 전체에서 <Image ... fill ...> 이지만 같은 태그에 sizes= 없는 경우(단일 라인 기준)
echo "---- <Image ... fill ...> without sizes= (single-line elements, src/**.tsx) ----"
rg -n --glob '!**/*.test.tsx' --glob '*.tsx' -S '<Image[^>]*\bfill\b' src \
  | rg -v 'sizes=' || true

# 3) 혹시 fill/sizes가 여러 줄로 나뉘는 경우를 대비해, fill 라인만 먼저 수집
echo "---- Lines containing fill in TSX (for manual spot check) ----"
rg -n --glob '!**/*.test.tsx' --glob '*.tsx' -S '\bfill\b' src \
  | head -n 50 || true

Repository: Team-SoFa/linkiving

Length of output: 2189


fill 사용하는 next/imagesizes를 추가해주세요

fill만 있고 sizes가 없으면 과도한 크기의 이미지를 선택해 랜딩/썸네일 성능에 불리할 수 있습니다. (다음 위치에 동일 패턴이 보입니다)

  • src/components/layout/Landing/FeatureCard.tsx:23
  • src/components/layout/Landing/ExperienceItem.tsx:15
  • src/components/layout/Landing/HowUseItem.tsx:15
  • src/components/layout/SideNavigation/components/MenuSection/AddLink/LinkThumbnailTitleSection.tsx:46
  • src/components/layout/SideNavigation/components/MenuSection/AddLink/AddMultiLinks.tsx:236
제안 수정안
-        <Image src={image} alt={imageAlt} fill />
+        <Image
+          src={image}
+          alt={imageAlt}
+          fill
+          sizes="(min-width: 1024px) 700px, 100vw"
+        />
📝 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
<Image src={image} alt={imageAlt} fill />
<Image
src={image}
alt={imageAlt}
fill
sizes="(min-width: 1024px) 700px, 100vw"
/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Landing/FeatureCard.tsx` at line 23, The Image
components using the next/image "fill" prop should include a responsive sizes
prop to avoid oversized image selection; update the Image usages in FeatureCard
(component FeatureCard), ExperienceItem (ExperienceItem), HowUseItem
(HowUseItem), LinkThumbnailTitleSection (component LinkThumbnailTitleSection)
and AddMultiLinks (AddMultiLinks) to add a sizes string matching the layout (for
example: mobile ~100vw, tablet ~50vw, desktop ~33vw) so the browser can pick
appropriately sized images for each breakpoint.

Comment on lines +18 to +25
<Image
src="/images/google-icon.png"
alt="Chrome 로고"
width={20}
height={20}
className="mr-2 inline-block"
/>
<span>Chrome 웹스토어 추가</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Chrome CTA에 Google 아이콘이 노출되고 있습니다.

버튼 문구/동작은 Chrome 웹스토어인데 아이콘이 google-icon.png라 의미가 어긋납니다. Chrome 아이콘 에셋으로 교체하는 게 맞습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/wrappers/ChromeButton.tsx` around lines 18 - 25, The
ChromeButton component is showing google-icon.png while its text and behavior
target the Chrome Web Store; update the Image usage in ChromeButton (the Image
component instance) to use the correct Chrome icon asset (e.g., change src from
"google-icon.png" to the Chrome asset name such as "chrome-icon.png"), keep a
matching alt like "Chrome 로고", and ensure the referenced chrome icon file exists
in the public/images assets so the button shows the proper Chrome icon.

Comment on lines +16 to +18
<button onClick={handleGoogleLogin} className="">
Google 계정으로 계속하기
</button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

버튼 type을 명시해주세요.

type이 없으면 폼 컨텍스트에서 기본 submit으로 동작할 수 있습니다. type="button"을 명시해 의도치 않은 제출을 방지하는 편이 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/wrappers/GoogleLoginButton.tsx` around lines 16 - 18, The
button in the GoogleLoginButton component is missing an explicit type which can
cause it to act as a form submitter; update the JSX for the button that calls
handleGoogleLogin to include type="button" so it won't trigger form submission
(i.e., modify the <button ...> in GoogleLoginButton.tsx that uses
handleGoogleLogin to add the type attribute).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

랜딩페이지 디자인 적용

1 participant