Skip to content

[DEV] main으로 pr#14

Merged
GirimNam merged 70 commits into
mainfrom
dev
May 22, 2026
Merged

[DEV] main으로 pr#14
GirimNam merged 70 commits into
mainfrom
dev

Conversation

@GirimNam

@GirimNam GirimNam commented May 22, 2026

Copy link
Copy Markdown
Member

📌 작업 내용

  • auth 공통 레이아웃 생성
  • 회원가입 페이지 ui 구현
  • 로그인 페이지 ui 구현
  • 비밀번호 재설정 페이지 ui 구현
  • auth 공통 컴포넌트 구현
  • deploy, preview yaml dev 포함하게 수정

✅ 변경 사항

  • 새로운 기능 추가
  • 버그 수정
  • 코드 개선 및 수정
  • 문서 작성 및 업데이트
  • 배포 관련 수정
  • 브랜치 관련 수정
  • 기타:

🔗 관련 이슈

Closes #10
Closes #4
Closes #12

🧪 체크리스트

  • 제가 작성한 코드를 리뷰했습니다.
  • 문서에 변경 사항을 반영했습니다.
  • 새로운 경고를 생성하지 않습니다.
  • 새로운 기능이나 수정 사항이 기존 테스트와 충돌하지 않음을 확인했습니다.

📸 스크린샷

없음

💬 리뷰 요청 사항

기타

Summary by CodeRabbit

  • 새로운 기능

    • 이메일 기반 로그인·회원가입(인증 코드·프로필) 및 비밀번호 재설정 전체 흐름 추가
    • 인증 코드 입력, 새 비밀번호 입력, 완료 화면, Google 로그인 버튼 등 인증 관련 UI 컴포넌트 제공
    • 플레이그라운드에서 버튼/입력 등 UI 컴포넌트 상태 미리보기 가능
  • 기타

    • 배포·미리보기 워크플로우가 개발 브랜치도 트리거하도록 확장
    • SVG를 컴포넌트로 임포트하는 지원 및 전역 타이포그래피 변수·관련 의존성 추가

Review Change Stack

girimNam and others added 30 commits May 19, 2026 02:49
[Feat] 회원가입/로그인 공통 컴포넌트 구현
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c0076e1c-a2b0-4101-98df-dc5eab11f3ee

📥 Commits

Reviewing files that changed from the base of the PR and between 447e918 and f71d1b0.

📒 Files selected for processing (2)
  • src/components/auth/AgreementItem.tsx
  • src/components/auth/CodeInput.tsx

📝 Walkthrough

Walkthrough

회원가입·로그인·비밀번호 재설정용 레이아웃과 UI 컴포넌트, SVGR 기반 SVG 설정 및 타입 선언, 의존성 추가, 그리고 CI(Deploy/Preview) 트리거 확장이 포함됩니다.

Changes

Authentication Pages and Components

Layer / File(s) Summary
UI component library and styling foundation
src/lib/cn.ts, src/components/ui/Button.tsx, src/components/ui/Radio.tsx, src/components/ui/TextField.tsx, src/components/ui/Divider.tsx, src/app/globals.css
Core UI primitives, Tailwind class merge util, CVA-based Button variants, TextField/Radio/Divider, and global typography CSS variables.
Auth-specific reusable components
src/components/auth/CodeInput.tsx, src/components/auth/PasswordField.tsx, src/components/auth/GoogleButton.tsx, src/components/auth/AgreementItem.tsx
Authentication helpers: multi-cell CodeInput with paste/backspace focus logic, PasswordField visibility toggle, GoogleButton, AgreementItem for terms consent.
Auth page shared layout
src/app/(auth)/layout.tsx
Two-column auth layout rendering left branding image and right content wrapper for auth flows.
Signup flow pages
src/app/(auth)/signup/page.tsx, src/app/(auth)/signup/verify/page.tsx, src/app/(auth)/signup/profile/page.tsx, src/app/(auth)/signup/complete/page.tsx
Four-step signup: email → 5-minute code verification → profile + password + terms (react-hook-form + zod) → completion.
Login and password reset pages
src/app/(auth)/login/page.tsx, src/app/(auth)/reset-password/page.tsx, src/app/(auth)/reset-password/verify/page.tsx, src/app/(auth)/reset-password/new-password/page.tsx, src/app/(auth)/reset-password/complete/page.tsx
Login form with zod validation; password-reset flow with timed code verification, new-password validation, and completion page.
Component playground page
src/app/playground/page.tsx
Playground rendering components in various states for development/visual checks.
SVG component handling
next.config.ts, src/svg.d.ts
SVGR loader added for turbopack/webpack with SVGO options and TypeScript *.svg module declaration.
Package dependencies
package.json
Added form, validation, styling and icon dependencies (react-hook-form, zod, @hookform/resolvers, class-variance-authority, clsx, tailwind-merge, lucide-react, @svgr/webpack adjustments).
CI/CD workflow updates
.github/workflows/deploy.yaml, .github/workflows/preview.yaml
Deploy and preview workflows now trigger on pushes/PRs to both main and dev; deploy action pushes to the triggering branch.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 "당근 밭에 컴포넌트 쌓아두고,
zod로 검증은 반짝이며,
타이머는 똑딱 5분 춤추고,
SVG는 깔끔히 변환되어,
인증 흐름 한 바퀴 완성했네."

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목이 매우 모호하고 구체적인 변경 사항을 설명하지 않습니다. '[DEV] main으로 pr'은 의미 있는 정보를 전달하지 않습니다. 제목을 '[DEV] 인증 UI 및 공유 컴포넌트 추가' 또는 '[DEV] 회원가입/로그인/비밀번호 재설정 페이지 UI 구현'으로 변경하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed 모든 연결된 이슈의 요구사항이 충족되었습니다. #4의 인증 레이아웃, #10의 회원가입 4단계 플로우 및 공유 컴포넌트, #12의 로그인 및 비밀번호 재설정 페이지가 구현되었습니다.
Out of Scope Changes check ✅ Passed 배포 워크플로우 수정(deploy.yaml, preview.yaml), SVGR 설정(next.config.ts), 패키지 의존성 추가는 모두 인증 UI 구현을 지원하기 위한 필요한 변경입니다.

✏️ 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 dev

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

🧹 Nitpick comments (3)
src/components/ui/Radio.tsx (1)

13-16: ⚡ Quick win

키보드 포커스 가시성 스타일을 추가해주세요.

현재 라디오에 focus-visible 스타일이 없어 키보드 내비게이션 접근성이 떨어집니다. 최소한 포커스 링/보더 변화를 넣는 게 좋습니다.

♿ 제안 코드
 className={cn(
-  "peer size-5 cursor-pointer appearance-none rounded-full border border-gray-70 bg-gray-90",
+  "peer size-5 cursor-pointer appearance-none rounded-full border border-gray-70 bg-gray-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-50 focus-visible:ring-offset-2",
   className
 )}
🤖 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/ui/Radio.tsx` around lines 13 - 16, The radio input in the
Radio component lacks keyboard focus-visible styling which harms accessibility;
update the className passed to the element (the string built via cn in
Radio.tsx) to include visible focus styles (e.g., a focus-visible ring or border
change and outline-none to avoid double outlines) so keyboard users see focus;
modify the class string within the cn(...) call that currently contains "peer
size-5 cursor-pointer appearance-none rounded-full border border-gray-70
bg-gray-90" to append appropriate focus-visible classes (using your design
tokens, e.g. focus-visible:ring-{color} and focus-visible:ring-{width} or
focus-visible:border-{color}) while preserving existing classes and the
className prop merging.
src/components/auth/PasswordField.tsx (1)

43-50: ⚡ Quick win

토글 버튼 상태를 aria-pressed로 노출해 주세요.

Line 43-50의 버튼은 상태 토글형 컨트롤이라 aria-pressed를 함께 주는 편이 스크린리더 맥락에 더 정확합니다.

접근성 보강 예시
         <button
           type="button"
+          aria-pressed={visible}
           aria-label={visible ? "비밀번호 숨기기" : "비밀번호 보기"}
           onClick={() => setVisible((v) => !v)}
           className="ml-2 flex shrink-0 cursor-pointer items-center justify-center text-gray-50"
         >
🤖 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/auth/PasswordField.tsx` around lines 43 - 50, The toggle
button in PasswordField is a pressed-state control but lacks an aria-pressed
attribute; update the button element (the one using visible and setVisible,
rendering EyeIcon/EyeOffIcon) to include aria-pressed={visible} so the screen
reader receives the current toggle state (keep the existing onClick handler
setVisible((v) => !v) and aria-label as-is).
src/app/(auth)/layout.tsx (1)

7-36: ⚡ Quick win

소형 뷰포트 대응(반응형 + 세로 스크롤)을 보강해 주세요.

현재 고정 2열(w-1/2)과 h-screen 조합은 작은 화면에서 인증 폼이 답답하거나 잘릴 수 있습니다. 좌측 비주얼은 큰 화면에서만 노출하고, 우측은 overflow-y-auto를 주는 구성이 더 안전합니다.

레이아웃 보강 예시
-    <div className="flex h-screen w-full">
-      <div className="relative w-1/2 overflow-hidden rounded-[20px] my-5 ml-5">
+    <div className="flex min-h-screen w-full">
+      <div className="relative my-5 ml-5 hidden w-1/2 overflow-hidden rounded-[20px] lg:block">
...
-      <div className="flex w-1/2 flex-col">
+      <div className="flex w-full flex-col overflow-y-auto lg:w-1/2">
🤖 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/`(auth)/layout.tsx around lines 7 - 36, Replace the rigid two-column
h-screen layout in layout.tsx with a responsive structure: change the root div
class from "flex h-screen w-full" to "flex min-h-screen w-full" (use
min-h-screen to allow vertical scrolling), make the left visual panel
(containing Image and LogoIpxFigure) hidden on small screens by replacing
"relative w-1/2 ..." with "hidden lg:block lg:relative lg:w-1/2 ..." so it only
shows on large viewports, and update the right panel (containing LogoIpxChar and
{children}) from "flex w-1/2 flex-col" to "flex w-full lg:w-1/2 flex-col" and
add "overflow-y-auto" (e.g., "flex w-full lg:w-1/2 flex-col overflow-y-auto") to
allow vertical scrolling of the auth form; keep LogoIpxFigure, LogoIpxChar and
children usage unchanged.
🤖 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 @.github/workflows/deploy.yaml:
- Line 5: The workflow currently includes both "main" and "dev" in the branches
trigger but always uses a fixed target-branch: main, which causes dev pushes to
be deployed to production; update the workflow to either split the triggers into
two jobs or make the deployment job conditional on the ref so dev and main map
to different targets—modify the branches array and the deployment job that
contains the target-branch setting (look for the keys "branches:" and
"target-branch:" in the workflow) to either restrict triggers to only main for
the production deploy or add an if: condition (e.g., check github.ref or
github.ref_name) to set target-branch dynamically or skip deploy when the ref is
refs/heads/dev.

In `@next.config.ts`:
- Around line 32-36: 현재 webpack(config)에 추가된 /\.svg$/i 룰이 svgrLoader로 모든 SVG(심지어
?url 쿼리 포함)를 처리해 향후 `.svg?url` import가 깨질 수 있습니다; 수정 방법은 svgrLoader가 적용되는 규칙(현재
/\.svg$/i 룰)을 resourceQuery: { not: [/url/] }로 제한해 `?url` 쿼리가 붙은 요청은 제외하고, 별도의
asset/url 처리 규칙을 추가해 `.svg?url`은 Next의 asset/url 방식(asset/resource 또는
asset/inline 등)을 따르도록 분기하세요 (참조 심볼: webpack(config), svgrLoader, /\.svg$/i,
resourceQuery).

In `@src/app/`(auth)/login/page.tsx:
- Around line 39-41: The routing after successful login is incorrect: the
handler currently calls setErrors({}) then
router.push(`/signup/verify?email=${encodeURIComponent(email)}`); update the
success branch (the function containing setErrors and router.push) to redirect
to the correct post-login destination (e.g., the service main or dashboard route
such as '/dashboard' or '/app') instead of '/signup/verify', ensuring any query
params needed are adjusted or removed accordingly.

In `@src/app/`(auth)/reset-password/new-password/page.tsx:
- Around line 46-48: The heading text in the reset-password new-password page
("h2" with className "text-body-19 text-gray-30") is incorrect for the current
step; replace the sentence about an email link with a message appropriate to the
"enter new password" step (e.g., instruct the user to set and confirm their new
password or inform them that they are creating a new password for their account)
by updating the string inside that h2 in
src/app/(auth)/reset-password/new-password/page.tsx.

In `@src/app/`(auth)/reset-password/verify/page.tsx:
- Around line 69-71: The Link element currently uses href="#" which causes a
meaningless navigation; replace it with a non-navigating, accessible disabled
control until resend is implemented—either swap the <Link> with a <button>
(preserve className "text-label-15 font-normal text-gray-30 underline") set
disabled and aria-disabled="true" and remove any onClick behavior, or keep the
Link but remove href, add role="button" aria-disabled="true" and an onClick that
prevents default; also add a short helper text near the control indicating the
resend is temporarily unavailable (e.g., "재전송 준비 중") so users understand it’s
intentionally inactive.

In `@src/app/`(auth)/signup/profile/page.tsx:
- Around line 20-21: The validation message for agreedPrivacy is incorrect —
update the zod schema in page.tsx so agreedTerms keeps the message "*가입약관에
동의해주세요." and agreedPrivacy uses a privacy-specific message (e.g. "*개인정보 수집·이용에
동의해주세요.") by changing the refine message on the agreedPrivacy field; keep the
boolean/refine usage and only modify the message string for the agreedPrivacy
symbol.

In `@src/app/`(auth)/signup/verify/page.tsx:
- Around line 68-70: The Link with href="#" (the Link element rendering "새 코드
받기") should not be a no-op anchor; change it to a semantic button (replace Link
with a <button> or a Button component) and either disable it or wire the resend
action; update the element rendered in page.tsx (the Link that contains "새 코드
받기") to use a button, add a disabled prop and aria-disabled/aria-label when
resend is unimplemented, or call the existing resend handler when available so
clicks do not only jump to top of page.
- Around line 17-21: The SignupVerify component calls useSearchParams() directly
in a client component which can trigger Next.js
missing-suspense-with-csr-bailout warnings; wrap the minimal subtree that uses
useSearchParams (the SignupVerify component or its parent render of that
subtree) in a React <Suspense fallback={...}> boundary, or convert the page to a
server component and accept searchParams via props instead; locate the
SignupVerify function and the useSearchParams() call and either add a Suspense
wrapper around the JSX that depends on those params or refactor page.tsx to a
server component to receive searchParams and pass them down.

In `@src/components/auth/AgreementItem.tsx`:
- Around line 28-35: The detail button is rendered even when onDetail is
undefined causing a focusable, inert control; update AgreementItem to only
render the <button> (the element containing ArrowIcon and aria-label) when the
onDetail prop exists (e.g., conditionally render the button using onDetail &&
...), and keep the existing aria-label/props and className but remove the button
when onDetail is falsy so no focusable inert element appears.

In `@src/components/auth/CodeInput.tsx`:
- Around line 44-49: The current handleChange uses a space " " as a placeholder
and trimEnd(), which leaves internal blanks when deleting middle digits and
breaks backspace/focus logic; change the behavior to maintain an explicit
fixed-length array of characters using empty-string placeholders (e.g., '' for
empty slots), avoid trimEnd(), and call onChange with the joined string that
preserves internal empties (or a canonical representation of length-controlled
value) so that checks like !value[i] work correctly; update the same approach in
the related keydown/handlers referenced around lines 52-54 (the other input
handlers) and ensure refs.current[i+1]?.focus() logic remains the same.
- Around line 18-23: CodeInput 컴포넌트의 각 <input ref={ref} ... /> 요소가 스크린리더에서 구분되지
않으니 각 입력칸에 고유한 접근성 레이블을 추가하세요: 예를 들어 컴포넌트 내부에서 인덱스(index)와 전체 길이(예: total=6)를 받아
<input ... aria-label={`인증 코드 자리 ${index + 1} / ${total}`} /> 또는
aria-labelledby로 연결된 숨김 레이블을 설정하도록 수정하고 같은 변경을 파일 내 두 군데(현재 diff에 보이는 입력 블록과
67-79라인에 해당하는 다른 입력 블록) 모두 적용하세요.

In `@src/components/auth/GoogleButton.tsx`:
- Around line 20-21: The button in GoogleButton.tsx is missing explicit
disabled-state styling so when the disabled prop is true it still shows
hover/active and pointer cursor; update the class composition where the array
contains the base classes to branch on the disabled prop (e.g., in the
GoogleButton component) and when disabled add classes like cursor-not-allowed,
remove/avoid hover:bg-*/active:bg-* classes and instead apply muted
bg/text/border classes (e.g., bg-gray-95, text-gray-40, border-gray-60) so the
button visually appears disabled and does not respond to hover/active.

---

Nitpick comments:
In `@src/app/`(auth)/layout.tsx:
- Around line 7-36: Replace the rigid two-column h-screen layout in layout.tsx
with a responsive structure: change the root div class from "flex h-screen
w-full" to "flex min-h-screen w-full" (use min-h-screen to allow vertical
scrolling), make the left visual panel (containing Image and LogoIpxFigure)
hidden on small screens by replacing "relative w-1/2 ..." with "hidden lg:block
lg:relative lg:w-1/2 ..." so it only shows on large viewports, and update the
right panel (containing LogoIpxChar and {children}) from "flex w-1/2 flex-col"
to "flex w-full lg:w-1/2 flex-col" and add "overflow-y-auto" (e.g., "flex w-full
lg:w-1/2 flex-col overflow-y-auto") to allow vertical scrolling of the auth
form; keep LogoIpxFigure, LogoIpxChar and children usage unchanged.

In `@src/components/auth/PasswordField.tsx`:
- Around line 43-50: The toggle button in PasswordField is a pressed-state
control but lacks an aria-pressed attribute; update the button element (the one
using visible and setVisible, rendering EyeIcon/EyeOffIcon) to include
aria-pressed={visible} so the screen reader receives the current toggle state
(keep the existing onClick handler setVisible((v) => !v) and aria-label as-is).

In `@src/components/ui/Radio.tsx`:
- Around line 13-16: The radio input in the Radio component lacks keyboard
focus-visible styling which harms accessibility; update the className passed to
the element (the string built via cn in Radio.tsx) to include visible focus
styles (e.g., a focus-visible ring or border change and outline-none to avoid
double outlines) so keyboard users see focus; modify the class string within the
cn(...) call that currently contains "peer size-5 cursor-pointer appearance-none
rounded-full border border-gray-70 bg-gray-90" to append appropriate
focus-visible classes (using your design tokens, e.g. focus-visible:ring-{color}
and focus-visible:ring-{width} or focus-visible:border-{color}) while preserving
existing classes and the className prop merging.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 17e10f82-fd80-4ffb-a661-02e9d1e0d210

📥 Commits

Reviewing files that changed from the base of the PR and between 8c39cb5 and e17af9e.

⛔ Files ignored due to path filters (23)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/img/img-auth-graphic.png is excluded by !**/*.png
  • src/components/icons/icon-Union.svg is excluded by !**/*.svg
  • src/components/icons/icon-arrow.svg is excluded by !**/*.svg
  • src/components/icons/icon-back.svg is excluded by !**/*.svg
  • src/components/icons/icon-calender.svg is excluded by !**/*.svg
  • src/components/icons/icon-cancel.svg is excluded by !**/*.svg
  • src/components/icons/icon-create.svg is excluded by !**/*.svg
  • src/components/icons/icon-expand.svg is excluded by !**/*.svg
  • src/components/icons/icon-eye-off.svg is excluded by !**/*.svg
  • src/components/icons/icon-eye.svg is excluded by !**/*.svg
  • src/components/icons/icon-filter.svg is excluded by !**/*.svg
  • src/components/icons/icon-folder.svg is excluded by !**/*.svg
  • src/components/icons/icon-google_Icon.svg is excluded by !**/*.svg
  • src/components/icons/icon-help.svg is excluded by !**/*.svg
  • src/components/icons/icon-lightbulb.svg is excluded by !**/*.svg
  • src/components/icons/icon-more_vert.svg is excluded by !**/*.svg
  • src/components/icons/icon-plus.svg is excluded by !**/*.svg
  • src/components/icons/icon-search.svg is excluded by !**/*.svg
  • src/components/icons/icon-stop.svg is excluded by !**/*.svg
  • src/components/icons/icon-view_sidebar.svg is excluded by !**/*.svg
  • src/components/icons/logo-ipx-char.svg is excluded by !**/*.svg
  • src/components/icons/logo-ipx-figure.svg is excluded by !**/*.svg
📒 Files selected for processing (26)
  • .github/workflows/deploy.yaml
  • .github/workflows/preview.yaml
  • next.config.ts
  • package.json
  • src/app/(auth)/layout.tsx
  • src/app/(auth)/login/page.tsx
  • src/app/(auth)/reset-password/complete/page.tsx
  • src/app/(auth)/reset-password/new-password/page.tsx
  • src/app/(auth)/reset-password/page.tsx
  • src/app/(auth)/reset-password/verify/page.tsx
  • src/app/(auth)/signup/complete/page.tsx
  • src/app/(auth)/signup/page.tsx
  • src/app/(auth)/signup/profile/page.tsx
  • src/app/(auth)/signup/verify/page.tsx
  • src/app/globals.css
  • src/app/playground/page.tsx
  • src/components/auth/AgreementItem.tsx
  • src/components/auth/CodeInput.tsx
  • src/components/auth/GoogleButton.tsx
  • src/components/auth/PasswordField.tsx
  • src/components/ui/Button.tsx
  • src/components/ui/Divider.tsx
  • src/components/ui/Radio.tsx
  • src/components/ui/TextField.tsx
  • src/lib/cn.ts
  • src/svg.d.ts

Comment thread .github/workflows/deploy.yaml
Comment thread next.config.ts
Comment on lines +32 to +36
webpack(config) {
config.module.rules.push({
test: /\.svg$/i,
use: [svgrLoader],
});

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
# SVG import 패턴과 현재 충돌 가능성 확인 (read-only)
set -e

echo "[1] next.config.ts에서 svg exclude 처리 여부"
fd -a "next.config.ts" | xargs -I{} sh -c 'echo "--- {}"; rg -n "exclude\\s*=\\s*/\\\\\\.svg\\$/" "{}" || true'

echo
echo "[2] 코드베이스 SVG import 형태 확인 (?url vs 컴포넌트)"
rg -n --type=ts --type=tsx --type=js --type=jsx 'from\s+["'\''][^"'\'']+\.svg(\?url)?["'\'']|import\s+["'\''][^"'\'']+\.svg(\?url)?["'\'']'

Repository: CEOS-IPX/IPX-FE

Length of output: 225


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== next.config.ts (상대적으로 작은 범위만) =="
ls -la
if [ -f next.config.ts ]; then
  sed -n '1,140p' next.config.ts | nl -ba | sed -n '1,140p'
else
  echo "next.config.ts not found in repo root"
  fd -a "next.config.ts" -t f || true
fi

echo
echo "== webpack svg 관련 규칙이 있는지 (exclude/resourceQuery 포함) =="
rg -n "svgrLoader|webpack\\(config\\)|\\.svg\\$|exclude\\s*=|resourceQuery|issuer|file-loader|url-loader" next.config.ts || true

echo
echo "== 코드베이스에서 SVG import 형태 (?url vs 컴포넌트) =="
rg -n -g'*.ts' -g'*.tsx' -g'*.js' -g'*.jsx' \
  'import\s+["'\''][^"'\'']+\.svg(\?url)?["'\'']|from\s+["'\''][^"'\'']+\.svg(\?url)?["'\'']' . || true

echo
echo "== svg에 ?url이 붙은 경우만 =="
rg -n -g'*.ts' -g'*.tsx' -g'*.js' -g'*.jsx' \
  '["'\''][^"'\'']+\.svg\?url["'\'']' . || true

echo
echo "== svg 컴포넌트(= ?url 없는 .svg) 후보 =="
rg -n -g'*.ts' -g'*.tsx' -g'*.js' -g'*.jsx' \
  '["'\''][^"'\'']+\.svg["'\''](?!\\?url)' . || true

Repository: CEOS-IPX/IPX-FE

Length of output: 1597


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== next.config.ts (전체) =="
cat -n next.config.ts

echo
echo "== next.config.ts에서 svg 관련 규칙/로더 확인 =="
rg -n "svgrLoader|webpack\\(config\\)|\\.svg\\$|exclude\\s*=|resourceQuery|issuer|file-loader|url-loader" next.config.ts || true

echo
echo "== 코드베이스 SVG import 형태 (?url vs 컴포넌트) =="
# import "xxx.svg" 또는 import xxx from "xxx.svg?url" 형태만
rg -n --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
  'import\s+["'\''"][^"'\''"]+\.svg(\?url)?["'\''"]|from\s+["'\''"][^"'\''"]+\.svg(\?url)?["'\''"]' . || true

echo
echo "== svg import 중 ?url만 =="
rg -n --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
  '["'\''"][^"'\''"]+\.svg\?url["'\''"]' . || true

echo
echo "== svg import 중 ?url 없는 .svg만 =="
rg -n --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
  '["'\''"][^"'\''"]+\.svg["'\''"]' . | rg -v '\.svg\?url' || true

Repository: CEOS-IPX/IPX-FE

Length of output: 2746


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== src/svg.d.ts =="
cat -n src/svg.d.ts

echo
echo "== PasswordField.tsx (svg 사용 형태) =="
sed -n '1,200p' src/components/auth/PasswordField.tsx | nl -ba | head -n 200 || true

echo
echo "== GoogleButton.tsx =="
sed -n '1,120p' src/components/auth/GoogleButton.tsx | nl -ba | head -n 120 || true

echo
echo "== AgreementItem.tsx =="
sed -n '1,120p' src/components/auth/AgreementItem.tsx | nl -ba | head -n 120 || true

echo
echo "== src/app/(auth)/layout.tsx =="
sed -n '1,200p' src/app/(auth)/layout.tsx | nl -ba | head -n 200 || true

Repository: CEOS-IPX/IPX-FE

Length of output: 586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== PasswordField.tsx (상위 200줄) =="
sed -n '1,200p' src/components/auth/PasswordField.tsx | cat -n

echo
echo "== GoogleButton.tsx (상위 200줄) =="
sed -n '1,200p' src/components/auth/GoogleButton.tsx | cat -n

echo
echo "== AgreementItem.tsx (상위 200줄) =="
sed -n '1,200p' src/components/auth/AgreementItem.tsx | cat -n

echo
echo "== src/app/(auth)/layout.tsx (상위 200줄) =="
sed -n '1,200p' 'src/app/(auth)/layout.tsx' | cat -n

Repository: CEOS-IPX/IPX-FE

Length of output: 6525


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json =="
cat package.json

echo
echo "== next.config.ts에서 사용하는 패키지(의존성) 체크: `@svgr/webpack` 있는지 =="
rg -n "\"`@svgr/webpack`\"|svgr" package.json pnpm-lock.yaml next.config.ts || true

Repository: CEOS-IPX/IPX-FE

Length of output: 5033


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] next.config.ts에서 svg exclude 처리 여부"
rg -n "exclude\s*=\s*/\\\.svg\$|resourceQuery\s*[:{].*url|test:\s*\\/\\\\\.svg\\/$|\\.svg\\$\\/" next.config.ts || true

echo
echo "[2] 코드베이스 SVG import 형태 (?url vs 컴포넌트)"
rg -n --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' \
  'import\s+["'\''"][^"'\''"]+\.svg(\?url)?["'\''"]|from\s+["'\''"][^"'\''"]+\.svg(\?url)?["'\''"]' src || true

Repository: CEOS-IPX/IPX-FE

Length of output: 947


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Next webpack 기본에서 svg/ ?url 관련 규칙 찾기 (node_modules/next) =="

# 1) svg 라우팅(파일/데이터) 관련 키워드 탐색
rg -n --hidden --no-ignore-vcs \
  '(\\.svg\\b|test:\\s*/\\\\\\.svg\\$|resourceQuery.*url|\\?url|svgr|`@svgr/webpack`|asset/resource|oneOf)' \
  node_modules/next/dist/build -S | head -n 80 || true

echo
echo "== svg asset rule이 어디에 있는지 더 좁혀서 확인 =="
rg -n --hidden --no-ignore-vcs \
  '(test:\\s*/\\\\\\.svg\\$|\\.svg\\$\\)|resourceQuery|issuer|oneOf|asset/resource|url\\-loader)' \
  node_modules/next/dist/build -S | rg -n 'svg|resourceQuery|oneOf|asset' | head -n 80 || true

Repository: CEOS-IPX/IPX-FE

Length of output: 50627


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# next 16.2.6 기본 webpack에서 svg 관련 규칙이 들어있는 파일/코드만 좁혀서 찾기
rg -n --hidden --no-ignore-vcs --glob='*.js' --glob='*.mjs' --glob='*.ts' \
  '(\\.svg\\b|test:\\s*\\/\\\\\\.svg\\$|resourceQuery.*url|\\?url)' \
  node_modules/next/dist/build \
  | head -n 200 || true

echo
echo "---- svg 처리 로직 후보를 더 좁혀서: asset/file loader/oneOf/asset/resource ----"
rg -n --hidden --no-ignore-vcs --glob='*.js' \
  '(oneOf|asset/resource|file-loader|url-loader|resourceQuery)' \
  node_modules/next/dist/build \
  | rg -n 'svg|\\.svg' \
  | head -n 200 || true

echo
echo "---- svg 관련 구체 키워드(예: 'svg' literals) ----"
rg -n --hidden --no-ignore-vcs --glob='*.js' \
  "svg" node_modules/next/dist/build | head -n 120 || true

Repository: CEOS-IPX/IPX-FE

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

TARGET="node_modules/next/dist/build/webpack-config.js"

echo "== Next webpack-config.js에서 .svg 관련 리터럴/패턴 찾기 =="
rg -n --no-mmap "\\.svg" "$TARGET" | head -n 80 || true

echo
echo "== Next webpack-config.js에서 asset/file/url 관련과 함께 .svg 주변 찾기 =="
rg -n --no-mmap "svg|resourceQuery|\\?url|oneOf|asset/resource|file-loader|url-loader" "$TARGET" \
  | head -n 120 || true

echo
echo "== .svg 규칙의 정확한 블록을 더 보기: test: /\\.svg/ 또는 규칙 내 test:.svg 근처 =="
rg -n --no-mmap "test:\\s*/\\\\\\.svg\\$?/?|test:\\s*/\\\\\\.svg\\//|test:\\s*\\/\\\\\\.svg\\$\\/" "$TARGET" -C 3 | head -n 80 || true

echo
echo "== webpack-config.js의 helper 중 하나에서 svg asset 처리 정의가 있을 수 있어: assets/ 관련 파일 탐색 =="
fd -t f -a "svg" node_modules/next/dist/build -e js -e mjs 2>/dev/null | head -n 50 || true

Repository: CEOS-IPX/IPX-FE

Length of output: 2854


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="node_modules/next/dist/build/webpack-config.js"
echo "== $FILE : lines 2060-2220 =="
sed -n '2060,2220p' "$FILE" | cat -n

Repository: CEOS-IPX/IPX-FE

Length of output: 8845


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="node_modules/next/dist/build/webpack-config.js"

echo "== webpack-config.js: test:/\\.svg/ 주변 =="
rg -n --no-mmap "test:\\s*\\/?\\\\\\.svg\\/?|\\\\\\.svg\\$\\/i|\\\\\\.svg\\$\\/'|\\\\\\.svg\\$\\)/" "$FILE" -n -C 3 | head -n 200 || true

echo
echo "== webpack-config.js: resourceQuery.*url 또는 ?url 문자열 =="
rg -n --no-mmap "(resourceQuery[^\\n]*(url)|\\?url|WEBPACK_RESOURCE_QUERIES[^\\n]*url)" "$FILE" -C 3 | head -n 200 || true

echo
echo "== webpack-config.js: 'svg'가 등장하는 규칙 블록들(근처 20줄) =="
rg -n --no-mmap "svg" "$FILE" -C 5 | head -n 120 || true

echo
echo "== (추가) Next build의 다른 규칙 파일에서 svg/asset/url 관련 =="
fd -t f -e js -e mjs "svg" node_modules/next/dist/build -x sh -c 'rg -n --no-mmap "test:\\s*/\\\\\\.svg|\\\\\\.svg\\$|\\?url|resourceQuery.*url|asset/resource" "$1" -C 2 | head -n 40' sh {} \; 2>/dev/null | head -n 120 || true

Repository: CEOS-IPX/IPX-FE

Length of output: 2829


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="node_modules/next/dist/build/webpack-config.js"

echo "== nextImageLoaderRegex 사용 위치(문맥 포함) =="
rg -n --no-mmap "nextImageLoaderRegex" "$FILE" -n
echo
# Show a few nearby occurrences
rg -n --no-mmap "nextImageLoaderRegex" "$FILE" | head -n 20 | cut -d: -f1 | head -n 5 | while read -r line; do
  start=$((line-20)); end=$((line+60))
  echo "--- around line $line ($start-$end) ---"
  sed -n "${start},${end}p" "$FILE" | head -n 140
done

Repository: CEOS-IPX/IPX-FE

Length of output: 2226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="node_modules/next/dist/build/webpack-config.js"
line=$(rg -n --no-mmap "nextImageLoaderRegex" "$FILE" | awk -F: 'NR==3{print $1}' || true)

# 위에서 3번째 매치가 없을 수 있으니, 전체 매치를 기준으로 주변을 모두 출력
rg -n --no-mmap "nextImageLoaderRegex" "$FILE" -n | cut -d: -f1 | while read -r line; do
  start=$((line-20))
  if [ "$start" -lt 1 ]; then start=1; fi
  end=$((line+80))
  echo "--- around line $line ($start-$end) ---"
  sed -n "${start},${end}p" "$FILE" | head -n 200
done

Repository: CEOS-IPX/IPX-FE

Length of output: 26495


SVGR .svg 규칙은 Next 내장 처리와 충돌 가능성은 낮지만, ?url은 안전장치가 필요

  • Next 16.2.6은 config.webpack에서 커스텀 .svg loader(예: @svgr/webpack)를 감지하면, 기본 next-image-loader.svg 매칭을 자동으로 제외합니다. 그래서 “기존 SVG 에셋 규칙 때문에 SVGR이 무시/충돌”하는 위험은 현재로서는 과장되어 있습니다.
  • 다만 현재 webpack 룰은 resourceQuery: /url/ 같은 분기가 없어 .svg?url까지 모두 SVGR 컴포넌트로 처리될 수 있습니다. 코드베이스에서는 .svg?url import를 찾지 못했지만, 향후 사용 시 깨질 수 있으니 ?url일 때는 SVGR을 피하도록 룰을 분리하는 게 안전합니다.

예: SVGR 룰에 resourceQuery: { not: [/url/] }를 추가하고, 필요하다면 ?url에는 asset/url 처리 룰을 따로 두세요.

🤖 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 `@next.config.ts` around lines 32 - 36, 현재 webpack(config)에 추가된 /\.svg$/i 룰이
svgrLoader로 모든 SVG(심지어 ?url 쿼리 포함)를 처리해 향후 `.svg?url` import가 깨질 수 있습니다; 수정 방법은
svgrLoader가 적용되는 규칙(현재 /\.svg$/i 룰)을 resourceQuery: { not: [/url/] }로 제한해 `?url`
쿼리가 붙은 요청은 제외하고, 별도의 asset/url 처리 규칙을 추가해 `.svg?url`은 Next의 asset/url
방식(asset/resource 또는 asset/inline 등)을 따르도록 분기하세요 (참조 심볼: webpack(config),
svgrLoader, /\.svg$/i, resourceQuery).

Comment on lines +39 to +41
setErrors({});
router.push(`/signup/verify?email=${encodeURIComponent(email)}`);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

로그인 성공 후 라우팅 경로가 잘못되었습니다.

검증 성공 시 /signup/verify로 이동하고 있어 로그인 플로우와 맞지 않습니다. 로그인 성공 목적지(예: 서비스 메인/대시보드)로 분기해 주세요.

🤖 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/`(auth)/login/page.tsx around lines 39 - 41, The routing after
successful login is incorrect: the handler currently calls setErrors({}) then
router.push(`/signup/verify?email=${encodeURIComponent(email)}`); update the
success branch (the function containing setErrors and router.push) to redirect
to the correct post-login destination (e.g., the service main or dashboard route
such as '/dashboard' or '/app') instead of '/signup/verify', ensuring any query
params needed are adjusted or removed accordingly.

Comment on lines +46 to +48
<h2 className="text-body-19 text-gray-30">
이메일로 새 비밀번호를 설정할 수 있는 링크가 전송됩니다.
</h2>

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

현재 단계와 맞지 않는 안내 문구입니다.

새 비밀번호 설정 단계인데 “이메일로 링크가 전송됩니다” 문구가 남아 있습니다. 현재 단계에 맞는 설명으로 교체해 주세요.

🤖 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/`(auth)/reset-password/new-password/page.tsx around lines 46 - 48,
The heading text in the reset-password new-password page ("h2" with className
"text-body-19 text-gray-30") is incorrect for the current step; replace the
sentence about an email link with a message appropriate to the "enter new
password" step (e.g., instruct the user to set and confirm their new password or
inform them that they are creating a new password for their account) by updating
the string inside that h2 in
src/app/(auth)/reset-password/new-password/page.tsx.

Comment on lines +69 to +71
<Link href="#" className="text-label-15 font-normal text-gray-30 underline">
새 코드 받기
</Link>

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

href="#"는 실제 재전송 동작 전까지 제거/대체가 필요합니다.

현재 클릭 시 의미 없는 이동만 발생합니다. 임시로 비활성 버튼 + 안내 문구로 처리하는 쪽이 UX에 더 안전합니다.

🤖 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/`(auth)/reset-password/verify/page.tsx around lines 69 - 71, The Link
element currently uses href="#" which causes a meaningless navigation; replace
it with a non-navigating, accessible disabled control until resend is
implemented—either swap the <Link> with a <button> (preserve className
"text-label-15 font-normal text-gray-30 underline") set disabled and
aria-disabled="true" and remove any onClick behavior, or keep the Link but
remove href, add role="button" aria-disabled="true" and an onClick that prevents
default; also add a short helper text near the control indicating the resend is
temporarily unavailable (e.g., "재전송 준비 중") so users understand it’s
intentionally inactive.

Comment on lines +68 to +70
<Link href="#" className="text-label-15 font-normal text-gray-30 underline">
새 코드 받기
</Link>

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

동작 없는 href="#" 링크는 버튼/비활성 상태로 바꿔주세요.

현재 클릭 시 페이지 상단 이동만 발생합니다. 재전송이 미구현 상태라면 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/app/`(auth)/signup/verify/page.tsx around lines 68 - 70, The Link with
href="#" (the Link element rendering "새 코드 받기") should not be a no-op anchor;
change it to a semantic button (replace Link with a <button> or a Button
component) and either disable it or wire the resend action; update the element
rendered in page.tsx (the Link that contains "새 코드 받기") to use a button, add a
disabled prop and aria-disabled/aria-label when resend is unimplemented, or call
the existing resend handler when available so clicks do not only jump to top of
page.

Comment thread src/components/auth/AgreementItem.tsx Outdated
Comment thread src/components/auth/CodeInput.tsx
Comment on lines +44 to +49
const handleChange = (i: number) => (e: ChangeEvent<HTMLInputElement>) => {
const digit = e.target.value.replace(/\D/g, "").slice(-1);
const arr = value.padEnd(length, " ").split("");
arr[i] = digit || " ";
onChange(arr.join("").trimEnd());
if (digit && i < length - 1) refs.current[i + 1]?.focus();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

중간 자리 삭제 시 공백 문자가 상태에 남는 로직 버그가 있습니다.

" "를 placeholder로 넣고 trimEnd()하는 방식 때문에 중간 삭제 시 값에 내부 공백이 남습니다. 이 상태는 Backspace 포커스 이동 조건(!value[i])도 깨뜨릴 수 있습니다.

🐛 제안 코드
 export function CodeInput({ value, onChange, length = 6, error, autoFocus }: CodeInputProps) {
   const refs = useRef<(HTMLInputElement | null)[]>([]);
+  const normalized = value.replace(/\D/g, "").slice(0, length);

   const handleChange = (i: number) => (e: ChangeEvent<HTMLInputElement>) => {
     const digit = e.target.value.replace(/\D/g, "").slice(-1);
-    const arr = value.padEnd(length, " ").split("");
-    arr[i] = digit || " ";
-    onChange(arr.join("").trimEnd());
+    const arr = Array.from({ length }, (_, idx) => normalized[idx] ?? "");
+    arr[i] = digit;
+    onChange(arr.join("").slice(0, length));
     if (digit && i < length - 1) refs.current[i + 1]?.focus();
   };

   const handleKeyDown = (i: number) => (e: KeyboardEvent<HTMLInputElement>) => {
-    if (e.key === "Backspace" && !value[i] && i > 0) {
+    if (e.key === "Backspace" && !normalized[i] && i > 0) {
       refs.current[i - 1]?.focus();
     }
   };

Also applies to: 52-54

🤖 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/auth/CodeInput.tsx` around lines 44 - 49, The current
handleChange uses a space " " as a placeholder and trimEnd(), which leaves
internal blanks when deleting middle digits and breaks backspace/focus logic;
change the behavior to maintain an explicit fixed-length array of characters
using empty-string placeholders (e.g., '' for empty slots), avoid trimEnd(), and
call onChange with the joined string that preserves internal empties (or a
canonical representation of length-controlled value) so that checks like
!value[i] work correctly; update the same approach in the related
keydown/handlers referenced around lines 52-54 (the other input handlers) and
ensure refs.current[i+1]?.focus() logic remains the same.

Comment on lines +20 to +21
"flex h-14 w-full cursor-pointer items-center justify-center gap-2.5 rounded-lg border border-gray-70 bg-white px-4 py-3 text-label-emphasis-17 text-gray-10 hover:bg-gray-90 active:bg-gray-80",
className

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

disabled 상태 스타일을 명시해 주세요.

disabled일 때도 hover/active/cursor 스타일이 유지되어 비활성 버튼처럼 보이지 않습니다.

🎨 제안 코드
 className={cn(
-  "flex h-14 w-full cursor-pointer items-center justify-center gap-2.5 rounded-lg border border-gray-70 bg-white px-4 py-3 text-label-emphasis-17 text-gray-10 hover:bg-gray-90 active:bg-gray-80",
+  "flex h-14 w-full cursor-pointer items-center justify-center gap-2.5 rounded-lg border border-gray-70 bg-white px-4 py-3 text-label-emphasis-17 text-gray-10 hover:bg-gray-90 active:bg-gray-80 disabled:cursor-not-allowed disabled:bg-gray-80 disabled:text-gray-30 disabled:border-transparent",
   className
 )}
📝 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
"flex h-14 w-full cursor-pointer items-center justify-center gap-2.5 rounded-lg border border-gray-70 bg-white px-4 py-3 text-label-emphasis-17 text-gray-10 hover:bg-gray-90 active:bg-gray-80",
className
"flex h-14 w-full cursor-pointer items-center justify-center gap-2.5 rounded-lg border border-gray-70 bg-white px-4 py-3 text-label-emphasis-17 text-gray-10 hover:bg-gray-90 active:bg-gray-80 disabled:cursor-not-allowed disabled:bg-gray-80 disabled:text-gray-30 disabled:border-transparent",
className
🤖 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/auth/GoogleButton.tsx` around lines 20 - 21, The button in
GoogleButton.tsx is missing explicit disabled-state styling so when the disabled
prop is true it still shows hover/active and pointer cursor; update the class
composition where the array contains the base classes to branch on the disabled
prop (e.g., in the GoogleButton component) and when disabled add classes like
cursor-not-allowed, remove/avoid hover:bg-*/active:bg-* classes and instead
apply muted bg/text/border classes (e.g., bg-gray-95, text-gray-40,
border-gray-60) so the button visually appears disabled and does not respond to
hover/active.

@github-actions

Copy link
Copy Markdown

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

♻️ Duplicate comments (2)
src/app/(auth)/signup/profile/page.tsx (1)

21-21: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

개인정보 동의 에러 메시지를 수정하세요.

agreedPrivacy 필드의 에러 메시지가 여전히 "*가입약관에 동의해주세요."로 되어 있습니다. 개인정보 처리방침 동의에 맞는 메시지(예: "*개인정보 처리방침에 동의해주세요.")로 변경해 주세요.

제안된 수정
    agreedTerms: z.boolean().refine((v) => v, { message: "*가입약관에 동의해주세요." }),
-    agreedPrivacy: z.boolean().refine((v) => v, { message: "*가입약관에 동의해주세요." }),
+    agreedPrivacy: z.boolean().refine((v) => v, { message: "*개인정보 처리방침에 동의해주세요." }),
🤖 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/`(auth)/signup/profile/page.tsx at line 21, Update the zod schema's
agreedPrivacy field to use a privacy-specific error message: locate the
agreedPrivacy definition (z.boolean().refine(...)) in the signup profile schema
and change the refine message from "*가입약관에 동의해주세요." to a privacy-appropriate
string such as "*개인정보 처리방침에 동의해주세요." so the validation message matches the field
purpose.
src/app/(auth)/signup/verify/page.tsx (1)

69-71: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

동작 없는 href="#" 링크는 버튼으로 교체하고 재전송 기능을 연결하세요.

"새 코드 받기" 링크가 여전히 href="#"로 되어 있어 클릭 시 페이지 상단으로만 이동합니다. <button> 또는 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/app/`(auth)/signup/verify/page.tsx around lines 69 - 71, Replace the
inert <Link href="#"> "새 코드 받기" element in the signup verify page with a
<button> (or your app's Button component) and wire it to a resend handler (e.g.,
onClick calling handleResendCode) declared in the page's component; if the
resend API isn't implemented yet, render the button disabled (or aria-disabled)
and show a tooltip/inline "준비 중" message to users instead of a no-op link so the
element no longer just jumps to the top of the page. Ensure the button text "새
코드 받기" and any loading/error states are handled by the handler
(handleResendCode) and that accessibility attributes (aria-busy, aria-disabled)
are set when appropriate.
🤖 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.

Duplicate comments:
In `@src/app/`(auth)/signup/profile/page.tsx:
- Line 21: Update the zod schema's agreedPrivacy field to use a privacy-specific
error message: locate the agreedPrivacy definition (z.boolean().refine(...)) in
the signup profile schema and change the refine message from "*가입약관에 동의해주세요." to
a privacy-appropriate string such as "*개인정보 처리방침에 동의해주세요." so the validation
message matches the field purpose.

In `@src/app/`(auth)/signup/verify/page.tsx:
- Around line 69-71: Replace the inert <Link href="#"> "새 코드 받기" element in the
signup verify page with a <button> (or your app's Button component) and wire it
to a resend handler (e.g., onClick calling handleResendCode) declared in the
page's component; if the resend API isn't implemented yet, render the button
disabled (or aria-disabled) and show a tooltip/inline "준비 중" message to users
instead of a no-op link so the element no longer just jumps to the top of the
page. Ensure the button text "새 코드 받기" and any loading/error states are handled
by the handler (handleResendCode) and that accessibility attributes (aria-busy,
aria-disabled) are set when appropriate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 58deb03e-1bee-42d2-b159-771b6d723b38

📥 Commits

Reviewing files that changed from the base of the PR and between e17af9e and fb390ae.

📒 Files selected for processing (4)
  • src/app/(auth)/signup/complete/page.tsx
  • src/app/(auth)/signup/page.tsx
  • src/app/(auth)/signup/profile/page.tsx
  • src/app/(auth)/signup/verify/page.tsx

@github-actions

Copy link
Copy Markdown

@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/(auth)/login/page.tsx (1)

14-16: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Zod v4 문법 수정: z.email("...")z.email({ message: "..." })

  • src/app/(auth)/login/page.tsxz.email("올바른 이메일 형식인지 확인해주세요")는 Zod v4에서 string 인자를 직접 받지 않으므로 z.email({ message: "올바른 이메일 형식인지 확인해주세요" })로 변경하세요.
🤖 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/`(auth)/login/page.tsx around lines 14 - 16, The loginSchema uses
invalid Zod v4 syntax: replace the z.email("...") call inside loginSchema with
the v4 form by using z.string().email({ message: "올바른 이메일 형식인지 확인해주세요" }) so the
email validator is applied to a string; update the email line within loginSchema
accordingly (look for the loginSchema definition and the z.email usage).
♻️ Duplicate comments (1)
src/app/(auth)/login/page.tsx (1)

40-41: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

로그인 성공 후 라우팅 구현이 필요합니다.

이전 리뷰에서 지적된 잘못된 라우팅(/signup/verify)이 주석 처리되어 버그는 방지되었으나, 로그인 성공 시 실제 목적지(예: 대시보드, 메인 페이지)로 이동하는 로직이 아직 구현되지 않았습니다.

TODO 코멘트로 향후 작업이 표시되어 있으니, 백엔드 연동 시 적절한 라우팅을 추가해 주세요.

🤖 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/`(auth)/login/page.tsx around lines 40 - 41, The login flow currently
leaves routing unimplemented (the commented-out router.push to /signup/verify),
so after a successful sign-in you should call the router to navigate the user to
the intended post-login page (e.g., router.push('/dashboard') or
router.push('/')). Update the success branch of the login submit handler (the
function that processes form submission / onSubmit / handleLogin in page.tsx) to
call router.push with the chosen destination, and ensure this runs only after
authentication succeeds; remove or replace the commented router.push line and
keep any TODO comment indicating backend-dependent destination.
🤖 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.

Outside diff comments:
In `@src/app/`(auth)/login/page.tsx:
- Around line 14-16: The loginSchema uses invalid Zod v4 syntax: replace the
z.email("...") call inside loginSchema with the v4 form by using
z.string().email({ message: "올바른 이메일 형식인지 확인해주세요" }) so the email validator is
applied to a string; update the email line within loginSchema accordingly (look
for the loginSchema definition and the z.email usage).

---

Duplicate comments:
In `@src/app/`(auth)/login/page.tsx:
- Around line 40-41: The login flow currently leaves routing unimplemented (the
commented-out router.push to /signup/verify), so after a successful sign-in you
should call the router to navigate the user to the intended post-login page
(e.g., router.push('/dashboard') or router.push('/')). Update the success branch
of the login submit handler (the function that processes form submission /
onSubmit / handleLogin in page.tsx) to call router.push with the chosen
destination, and ensure this runs only after authentication succeeds; remove or
replace the commented router.push line and keep any TODO comment indicating
backend-dependent destination.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d257f7c-02eb-491e-ae28-9c76b8d45d8d

📥 Commits

Reviewing files that changed from the base of the PR and between fb390ae and 447e918.

📒 Files selected for processing (2)
  • .github/workflows/deploy.yaml
  • src/app/(auth)/login/page.tsx

@GirimNam GirimNam merged commit 3c88ee2 into main May 22, 2026
2 of 3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 22, 2026
11 tasks
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.

[FEAT] AUTH 전용 레이아웃 설정

2 participants