Skip to content

[Chore] #79 - Next.js 전환 구조 정리 및 Vite 제거#89

Merged
sebeeeen merged 22 commits into
devfrom
feat/next-app-router
Apr 5, 2026
Merged

[Chore] #79 - Next.js 전환 구조 정리 및 Vite 제거#89
sebeeeen merged 22 commits into
devfrom
feat/next-app-router

Conversation

@sebeeeen
Copy link
Copy Markdown
Member

@sebeeeen sebeeeen commented Apr 2, 2026

🔎 What is this PR?

  • Next.js 전환 구조를 정리하고, 로그인 라우트를 기준으로 App Router 구조를 명확히 했습니다.

📝 Changes

  • 로그인 페이지를 src/app/(auth)/login/page.tsx로 이동해 route group 구조
  • Vite 직접 의존성과 레거시 엔트리(src/main.tsx, index.html, vite.config.ts, src/firebase.js, public/index.html)를 제거하고 env 접근을 NEXT_PUBLIC_* 기준으로 정리
  • Storybook을 @storybook/nextjs로 전환하고 관련 패키지 버전을 10.3.3으로 정렬
  • src/pages/ 레이어를 src/views/로 이름 변경하여 FSD 레이어 컨벤션을 정리
  • Modal, Tabs, Navigation, Input 등 인터랙티브 컴포넌트에 "use client" 지시자를 명시
  • Modal은 SSR hydration 안전성을 위해 isMounted 패턴을 추가했습니다.
  • 누락된 공용 아이콘 export를 보강해 앱 빌드/Storybook 빌드가 통과하도록 정리
  • src/shared/config/firebase.ts에 env 누락 시 명시적 에러를 던지는 검증 로직을 추가
  • README, docs/CONTRIBUTING.md, docs/VERSIONS.md, docs/page-rendering-strategy.md를 현재 전환 상태에 맞게 업데이트
  • out/ 빌드 아티팩트를 git 추적에서 제거 (.gitignore에 명시되어 있으나 이전에 커밋된 파일 정리)

📚 Background / Context

  • SEO 대응을 위한 Next.js 전환 이슈의 1~3단계를 먼저 진행했습니다.
  • 이번 PR은 공개 SEO 페이지 구현보다 먼저, 현재 혼재된 Vite/Next 구조를 정리하고 전환 기준을 문서화하는 데 목적이 있습니다.
  • Firebase Hosting 배포 방식 마이그레이션은 후속 작업이 필요합니다.

✔ Checklist

  • 코드는 로컬에서 정상적으로 빌드됩니다 (pnpm build)
  • ESLint / Prettier 통과 (pnpm lint)
  • 네이밍/레이어 컨벤션 준수 (camelCase/PascalCase, is·has 불린 접두사, alias 계층 규칙)
  • 관련 문서/주석 반영 (필요 시)
  • 주요 로직에 테스트 또는 검증 완료

Summary by CodeRabbit

Release Notes

  • New Features

    • Migrated build system from Vite to Next.js with App Router architecture.
    • Implemented static site generation and client-side rendering strategy for improved performance.
    • Added support for server components and optimized metadata handling.
  • Build & Deployment

    • Updated development and production builds to use Next.js toolchain.
    • Configured static export for Firebase Hosting deployment.
    • Generated optimized production bundles to out/ directory.
  • Developer Experience

    • Updated Storybook integration to use Next.js framework support.

@sebeeeen sebeeeen self-assigned this Apr 2, 2026
@sebeeeen sebeeeen linked an issue Apr 2, 2026 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

This PR executes a comprehensive migration from Vite to Next.js, replacing the build toolchain and adopting Next.js App Router, static export deployment to out/ directory, and updating all dependencies, configurations, and documentation accordingly. The change removes Vite/Vitest tooling, updates Storybook to @storybook/nextjs, converts environment variables from VITE_* to NEXT_PUBLIC_*, adds "use client" directives to interactive components, and includes extensive pre-rendered static artifacts in the out/ directory.

Changes

Cohort / File(s) Summary
Build Configuration & Toolchain
vite.config.ts, next.config.ts, tsconfig.json, tsconfig.app.json, tsconfig.node.json, eslint.config.js, tailwind.config.js
Removed Vite config, added Next.js config with output: "export" and trailingSlash: true for static export; updated TypeScript configs to support Next.js plugins and paths (@views/*, @widgets/*, etc.); adjusted ESLint ignores to include .next and storybook-static; removed Vite type references.
Entry Points & HTML
index.html, public/index.html, src/main.tsx
Removed SPA entry point files as Next.js App Router handles bootstrap; deleted Firebase initialization from public HTML.
Next.js App Structure
src/app/layout.tsx, src/app/page.tsx, src/app/(auth)/login/page.tsx, src/app/globals.css, src/app/index.tsx, src/app/index.css, next-env.d.ts
Added Next.js root layout, metadata, login route under auth group, and global styles; removed legacy App component; added Next.js type definitions file.
Firebase & Environment Configuration
src/firebase.js, src/shared/config/firebase.ts, .env* patterns`
Removed legacy Firebase initialization file; updated to use NEXT_PUBLIC_FIREBASE_* environment variables with runtime validation before initialization.
Package & Dependencies
package.json
Updated scripts from Vite (vite, vite build, vite preview) to Next.js equivalents (next dev, next build --webpack, HTTP server preview); added next@^16.2.1; removed Vite/Vitest/Playwright packages; updated Storybook to @storybook/nextjs; added eslint-config-next.
.gitignore & Configuration
.gitignore
Added .next, out, .vercel to ignore list for Next.js build outputs.
Storybook Migration
.storybook/main.ts, .storybook/preview.ts, .storybook/vitest.setup.ts
Changed Storybook framework from @storybook/react-vite to @storybook/nextjs; removed Vitest setup code including accessibility addon annotations; updated type imports across all story files.
Storybook Story Files (Type Updates)
src/shared/ui/{badge,button,card,checkbox,dropdown,inputBox,navigation,pagination,spinner,tabs,tag,badge/status-badge,modal,lists,empty_states,file-uploader,form,avatars}.stories.tsx, src/views/{home,login}/...stories.tsx
Updated Storybook type imports from @storybook/react-vite to @storybook/nextjs across ~20 story files; updated checkbox stories to use useArgs hook for Storybook integration; updated modal stories to remove controlled state args and improve uncontrolled behavior detection.
Storybook New Story Files
src/shared/ui/{avatars,empty_states,file-uploader,form,icons,lists}.stories.tsx
Added comprehensive Storybook documentation for Avatar, EmptyState, FileUploader, Form, and List/Table components with multiple usage examples and interactive states.
Shared UI Components - Client Directives
src/shared/ui/{inputBox,modal,navigation,tabs}/index/...tsx
Added "use client" directive to interactive components (InputBox, Modal, Navigation, Tabs); Modal includes hydration-safe rendering with isMounted state and deferred Escape-key binding.
Shared UI Components - Styling
src/shared/ui/icons/index.tsx, src/views/login/{index.tsx,login.module.css}
Added 8 new icon components (UserSolidIcon, FileTextAltIcon, BellIcon, AlertCircleIcon, ClockIcon, ImageIcon, UploadIcon, InboxIcon); converted LoginPage to use CSS modules instead of global CSS; renamed CSS class from global BEM convention to module-scoped names.
Entity & Feature Modules
src/entities/user/index.ts, src/features/auth/index.ts
Changed placeholder comments to explicit empty exports (export {};), converting them to valid TypeScript modules.
Documentation
README.md, docs/CONTRIBUTING.md, docs/VERSIONS.md, docs/page-rendering-strategy.md
Updated tech stack badges (removed Vite/React Router/React Query/Zustand, added Next.js); revised dev/build commands for Next.js workflow; moved CONTRIBUTING.md and expanded it with FSD layer changes (pagesviews), Next.js App Router structure, environment variable updates (NEXT_PUBLIC_FIREBASE_*), and 화면 개발 패턴 guidelines; rewrote VERSIONS.md as operational reference with current stack baseline; added new page-rendering-strategy.md defining SSG/CSR/ISR rules for Firebase static export deployment.
Deployment Configuration
firebase.json
Changed hosting public directory from "public" to "out"; removed SPA rewrites (catch-all "source": "**""/index.html") as static export does not require routing fallback.
Build Artifacts (Pre-rendered HTML & Metadata)
out/index.html, out/login/index.html, out/404.html, out/404/index.html, out/_not-found/index.html, out/login/__next.*.txt, out/_not-found/__next*.txt, out/__next*.txt
Added pre-built Next.js static export outputs including HTML pages, RSC metadata files, and routing tree descriptions for home, login, 404, and not-found routes; includes stylesheets and JavaScript chunk references.
Build Artifacts (JavaScript Chunks)
out/_next/static/chunks/{app,main,webpack,page,layout}-*.js, out/_next/static/chunks/app/{_global-error,_not-found,layout,page}-*.js
Added compiled Next.js chunk files for page/layout modules, Webpack runtime, and app-level error handling; includes minified module definitions and chunk registration/loading logic.
Build Artifacts (Stylesheets)
out/_next/static/css/{729202eae8491218,a031dbc3d4ab787a}.css
Added Tailwind v4.1.18 generated stylesheet with full utility classes and CSS variables; added login panel animation CSS module stylesheet.
Build Artifacts (Build Manifests)
out/_next/static/wJA63Lu7_pm4CosKUSIC_/{_buildManifest,_ssgManifest}.js
Added Next.js build metadata scripts defining rewrite rules, route filters, and empty SSG manifest for static export build.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

  • [Chore] SEO 대응을 위한 Next.js 전환 구조 설계 #79: The migration directly implements the Next.js transition architecture (next.config.ts with static export, App Router structure, environment variable updates) described in this issue for achieving SEO-friendly static rendering on Firebase Hosting.

Possibly related PRs

Suggested labels

dependencies, documentation, migration, next.js, storybook

Suggested reviewers

  • jaeu5325
  • kimsman06

Poem

🐰 Vite waves goodbye, Next.js takes the reins,
App Router dances through digital lanes,
Static exports bloom in the out/ directory,
Firebase Hosting celebrates the victory! ✨
From SPA dreams to SSG streams we bound!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main objective: transitioning to Next.js structure and removing Vite. It is concise, specific, and directly related to the primary changes in the changeset.
Description check ✅ Passed The PR description follows the required template structure with all critical sections completed: title, objectives, changes, background context, and checklist.

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

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

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
Contributor

@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: 17

Caution

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

⚠️ Outside diff range comments (1)
docs/CONTRIBUTING.md (1)

44-59: ⚠️ Potential issue | 🟡 Minor

Inconsistent .env file reference.

Line 46 mentions ".env 파일 설정이 필요합니다" but line 50 instructs to create .env.local. For Next.js, .env.local is the correct choice for local development secrets. Consider updating line 46-48 to reference .env.local for consistency.

📝 Suggested fix
 ### 환경 변수 설정 (.env)

-Firebase 연동을 위해 `.env` 파일 설정이 필요합니다.
+Firebase 연동을 위해 `.env.local` 파일 설정이 필요합니다.

-> `.env` 파일은 절대 GitHub에 커밋하지 마세요. `.gitignore`에 등록되어 있습니다.
+> `.env.local` 파일은 절대 GitHub에 커밋하지 마세요. `.gitignore`에 등록되어 있습니다.

 환경 변수 값은 **팀 노션**을 참고해서 프로젝트 루트에 `.env.local` 파일을 생성하세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/CONTRIBUTING.md` around lines 44 - 59, Update the CONTRIBUTING docs to
consistently reference `.env.local` instead of `.env`: change the heading and
the sentence "`.env` 파일 설정이 필요합니다" to mention `.env.local`, and ensure the
warning about not committing env files and the example variables remain
unchanged but clearly labeled as for `.env.local`; look for the existing text
that mentions ".env" and the example block around NEXT_PUBLIC_FIREBASE_* to make
these edits so the doc consistently guides Next.js developers to use
`.env.local`.
🧹 Nitpick comments (4)
src/shared/ui/file-uploader/file-uploader.stories.tsx (1)

5-11: Consider adding a component reference to meta for better autodocs.

The meta object doesn't specify a component, which means autodocs won't automatically generate prop tables. Since this file demonstrates multiple components (ThumbnailUploader, FileListItem, FileUploader), you might want to either:

  1. Split into separate story files per component, or
  2. Add a primary component reference
💡 Option: Add primary component for autodocs
 const meta = {
   title: "Shared/UI/FileUploader",
+  component: ThumbnailUploader,
   parameters: {
     layout: "centered",
   },
   tags: ["autodocs"],
 } satisfies Meta;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/ui/file-uploader/file-uploader.stories.tsx` around lines 5 - 11,
The meta object for the storybook file doesn't set a component, so autodocs
can't auto-generate prop tables; update the meta object (named meta) to include
a primary component reference (e.g., component: FileUploader) or split the
stories into separate files and set each file's meta.component to its respective
exported component (ThumbnailUploader, FileListItem, FileUploader) so Storybook
autodocs can produce proper prop tables.
src/shared/ui/form/form.stories.tsx (1)

82-86: onUpload callback signature mismatch.

Per ThumbnailUploaderProps (in file-uploader.tsx:15-20), onUpload receives a File parameter: onUpload: (file: File) => void. The current implementation ignores this parameter. While acceptable for a demo story, consider using the file to generate a proper preview URL for better demonstration fidelity.

💡 Suggested improvement for realistic preview
 <ThumbnailUploader
   previewUrl={preview}
-  onUpload={() => setPreview("https://picsum.photos/800/400")}
+  onUpload={(file) => setPreview(URL.createObjectURL(file))}
   onRemove={() => setPreview(undefined)}
 />

Note: If using URL.createObjectURL, remember to revoke the URL on remove to avoid memory leaks in real implementations.

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

In `@src/shared/ui/form/form.stories.tsx` around lines 82 - 86, The story's
ThumbnailUploader usage ignores the File parameter expected by
ThumbnailUploaderProps; update the onUpload prop passed to ThumbnailUploader to
accept the File argument (e.g., onUpload={(file) => ...}) and generate a preview
with URL.createObjectURL(file) instead of a fixed URL, storing that value via
setPreview; also update onRemove to revoke the object URL (URL.revokeObjectURL)
when clearing the preview to avoid a memory leak, referencing ThumbnailUploader,
onUpload, setPreview and the ThumbnailUploaderProps signature in
file-uploader.tsx.
tsconfig.json (1)

58-66: Consider impact on IDE support for excluded files.

Excluding *.stories.ts(x) and *.test.ts(x) from the main tsconfig means these files won't get full TypeScript support from the Next.js plugin. This is typically fine since:

  1. Stories usually have their own tsconfig via Storybook
  2. Tests often use a separate config

However, if developers report IntelliSense issues in story/test files, you may need to add a tsconfig.storybook.json that extends this config but includes those patterns.

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

In `@tsconfig.json` around lines 58 - 66, The tsconfig currently excludes story
and test files via the "exclude" array entries ("src/**/*.stories.ts",
"src/**/*.stories.tsx", "src/**/*.test.ts", "src/**/*.test.tsx",
"src/**/*.spec.ts", "src/**/*..spec.tsx"), which can remove IDE/Next.js
TypeScript support for those files; create a companion tsconfig (e.g.,
tsconfig.storybook.json) that extends the base tsconfig but overrides the
"exclude" to omit those story/test globs so editors and Storybook get proper
IntelliSense, or alternatively remove only the specific entries if you want the
base config to include them—update the project setup docs to note the new
tsconfig for Storybook/tests.
package.json (1)

8-8: Clarify the --webpack flag usage in build script.

The next build --webpack flag is unusual. As of Next.js 15, Turbopack is available for production builds. The --webpack flag explicitly opts out of Turbopack. If this is intentional (e.g., for stability or compatibility reasons), consider adding a comment in the documentation. Otherwise, you might want to use just next build and let Next.js use the default bundler.

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

In `@package.json` at line 8, The build script in package.json currently uses the
explicit flag "next build --webpack" which forces webpack instead of the default
bundler; decide whether to keep opting out of Turbopack or remove the flag:
either remove "--webpack" from the "build" script (script key "build") so
Next.js uses the default bundler, or if webpack is required, add a short comment
in the repo docs/README explaining why "next build --webpack" is intentionally
used for compatibility/stability and reference the "build" script name so
reviewers know the reason.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@next-env.d.ts`:
- Line 3: The dev-only explicit import import "./.next/dev/types/routes.d.ts" in
next-env.d.ts should be removed because it references a non-existent,
build-generated file; delete that import line and rely on the existing tsconfig
includes (e.g. ".next/dev/types/**/*.ts" and ".next/types/**/*.ts") to provide
the types so CI/clean builds no longer fail due to the missing .next file.

In `@src/shared/config/firebase.ts`:
- Around line 6-8: Replace the dynamic env lookup in getPublicEnv by using
static NEXT_PUBLIC_FIREBASE_* references (e.g.
process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, etc.) instead of process.env[key];
update your .env keys from VITE_FIREBASE_* to NEXT_PUBLIC_FIREBASE_* so they
match; and add a validation block before calling initializeApp that checks each
required NEXT_PUBLIC_FIREBASE_* value and throws/returns an explicit error if
any are undefined to fail fast (reference getPublicEnv usage sites and the
initializeApp call to locate where to change).

In `@src/shared/ui/avatars/avatars.tsx`:
- Around line 114-125: The avatar currently renders a non-interactive <div> even
when onClick is provided, so replace the outer wrapper with a semantic
interactive element when clickable: if onClick exists render a <button
type="button"> using the same avatarClasses, onClick handler and props (instead
of the <div>), otherwise keep the non-interactive wrapper; ensure you reference
AvatarProps, getAvatarClasses and avatarClasses so the class/name logic is
preserved and avoid nesting additional interactive descendants when switching to
a button.

In `@src/shared/ui/checkbox/checkbox.stories.tsx`:
- Around line 45-50: The DisabledChecked Story is passing a controlled prop
(checked: true) without an onChange handler which triggers React's
controlled-input warning; update the DisabledChecked.args to include readOnly:
true (or alternatively provide an onChange) so the component is treated as a
static checked input and the warning is suppressed—modify the DisabledChecked
Story args to add readOnly: true alongside checked: true and disabled: true.
- Around line 18-21: The InteractiveCheckbox story initializes component state
from args using useState(args.checked || false) which only captures the initial
value and doesn't update when Storybook controls change; update the
InteractiveCheckbox component to sync its local state with changes to
args.checked (e.g., inside InteractiveCheckbox use an effect that watches
args.checked and calls setChecked(args.checked ?? false)) so the Checkbox prop
checked stays in sync with Storybook controls while still updating via the
onChange handler.

In `@src/shared/ui/checkbox/checkbox.tsx`:
- Around line 11-20: The wrapper always includes "cursor-pointer" which makes
disabled checkboxes appear clickable; update the class construction so cursor
classes are conditional based on the disabled prop instead of hardcoding in
containerBaseClasses: remove "cursor-pointer" from the static
containerBaseClasses and, inside the Checkbox component (where container classes
are assembled, also referenced around the other affected block at lines ~39-45),
append "cursor-pointer" when !disabled and "cursor-not-allowed" or no pointer
class when disabled so disabled checkboxes do not show the pointer cursor.

In `@src/shared/ui/file-uploader/file-uploader.tsx`:
- Around line 190-196: The file input and drop handlers (e.g., handleFileChange
and the drop handler around lines 205-210) forward files without validating
against the component props; update both handlers to filter the incoming
FileList by the component's accept prop (supporting MIME types and extensions)
and enforce the multiple prop (if multiple is false, only keep the first
accepted file), then call onFileSelect with the filtered array and clear the
input; ensure the same accept-filtering logic is shared or extracted to a small
helper (e.g., isAcceptedFile / filterAcceptedFiles) so both handleFileChange and
the drop handler use identical validation.
- Around line 62-67: The file input change handler (handleFileChange) currently
calls onUpload for any selected file; mirror the drag-and-drop image guard by
validating the file is an image before invoking onUpload: inside
handleFileChange, check the same predicate used in the drag-drop path (e.g.,
file.type.startsWith('image/') or the existing is-image check used in the drop
handler) and only call onUpload(file) when it passes; if it fails, skip calling
onUpload (optionally console.warn or surface a user error) and still reset
e.currentTarget.value = "" to allow re-selection.
- Around line 1-3: This module uses React hooks (useRef, useState) and
interactive handlers so it must be a Next.js Client Component; add the directive
"use client" as the very first line of
src/shared/ui/file-uploader/file-uploader.tsx (above the imports) so
functions/components in this file like the FileUploader component, and
references to useRef/useState and Button/UploadIcon are executed on the client.

In `@src/shared/ui/form/form.stories.tsx`:
- Line 35: Replace the deprecated String.prototype.substr usage when generating
the id in form.stories.tsx: in the expression that currently uses
Math.random().toString(36).substr(2, 9) (the id generation), switch to
String.prototype.substring (e.g. use substring(2, 11) to preserve the same
9-character slice) or use slice(2, 11); update the id assignment inside the
story to use Math.random().toString(36).substring(2, 11) so substr is no longer
used.

In `@src/shared/ui/form/form.tsx`:
- Around line 56-61: The required indicator in the FormLabel component is
visual-only; update the JSX in FormLabel to make the star accessible by adding
aria-hidden="true" to the visual <span> (the '*' marker) and append a
screen-reader-only text node reading "required" (e.g., a span with
visually-hidden/SR-only class) so assistive tech announces the requirement;
modify the FormLabel render (the const content in FormLabel) to include these
changes while keeping the existing required prop handling.

In `@src/shared/ui/lists/lists.tsx`:
- Line 145: The row-level click targets (the div using getListItemClasses(...)
with handleItemClick and the similar divs around lines 293-297) are not keyboard
accessible; update the implementation so that when onClick/onRowClick is
provided you either render a native interactive element (e.g., a <button> or <a>
with proper href) or enhance the divs by adding role="button", tabIndex={0}, and
keyDown handler that calls the same handler on Enter/Space; ensure
handleItemClick and any onRowClick callbacks are invoked from both onClick and
the keyboard handler, and preserve disabled behavior (do not focus or respond to
keyboard when isDisabled is true).
- Around line 147-166: The checkbox inputs in lists.tsx lack accessible names;
update the input elements used when hasCheckbox (and the header checkbox) to
include proper accessibility attributes: for row checkboxes in the list item
component add aria-labelledby pointing to the item/title element's id (ensure
the title cell has a stable id or generate one from the item id), and for the
header checkbox add aria-label="Select all" (or aria-labelledby to a header
label) so screen readers announce intent; modify the input in the block where
handleCheckClick/onCheck/isChecked/isDisabled are used and mirror the same
change in the other checkbox occurrences (lines referenced around the header and
other rows) to ensure every checkbox is labeled.

In `@src/shared/ui/modal/modal.stories.tsx`:
- Around line 22-31: The trigger rendering currently checks !propsIsOpen so a
controlled isOpen={false} still shows the internal open button; change that
logic to detect uncontrolled mode by checking propsIsOpen === undefined (or
typeof propsIsOpen === "undefined") instead. Update FilterModalExample to use
propsIsOpen === undefined when deciding to render the internal trigger and when
wiring setInternalIsOpen, and replace any other occurrences of !propsIsOpen in
the modal examples (the blocks that reference propsIsOpen/propsOnClose,
internalIsOpen, isOpen, onClose) with the uncontrolled-mode check so the
internal trigger only appears when the component is actually uncontrolled.

In `@src/shared/ui/modal/modal.tsx`:
- Around line 10-16: The Modal implementation must complete the accessibility
contract: ensure the dialog has a reliable label and proper focus management.
Update the Modal/ModalHeader flow so that Modal uses aria-labelledby referencing
either the public titleId prop (if provided) or a generated id produced when
ModalHeader renders a title, and ensure ModalHeader sets that id on the title
element; also ensure the Modal does not hardcode a generated id that may be
absent. Add focus management in Modal: when isOpen becomes true, move focus into
the dialog (e.g., the dialog container or first focusable element), trap focus
inside while open, and restore focus to the previously focused element on close;
call onClose on Escape and clicks outside as before. You can implement this by
enhancing the Modal component (and ModalHeader) or by switching to a vetted
dialog primitive, but make sure to reference Modal, ModalHeader, titleId,
isOpen, and onClose when making the changes.
- Around line 117-124: The close button in the Modal component (the conditional
button rendered when onClose is provided in src/shared/ui/modal/modal.tsx) lacks
an explicit type and will act as type="submit" inside forms; update that button
element to include type="button" so clicking it does not trigger form submission
and only calls onClose (reference the JSX button with onClick={onClose} and
XIcon).
- Around line 84-106: The modal currently calls createPortal(..., document.body)
even during server render which can throw because document is undefined; update
the Modal component to track client mount using useState(false) and useEffect(()
=> setMounted(true), []), return null (or the non-portal render) while mounted
is false or if !isOpen, and only call createPortal when mounted is true and
isOpen is true; update references around isOpen, createPortal and document.body
to guard portal rendering by checking the new mounted state.

---

Outside diff comments:
In `@docs/CONTRIBUTING.md`:
- Around line 44-59: Update the CONTRIBUTING docs to consistently reference
`.env.local` instead of `.env`: change the heading and the sentence "`.env` 파일
설정이 필요합니다" to mention `.env.local`, and ensure the warning about not committing
env files and the example variables remain unchanged but clearly labeled as for
`.env.local`; look for the existing text that mentions ".env" and the example
block around NEXT_PUBLIC_FIREBASE_* to make these edits so the doc consistently
guides Next.js developers to use `.env.local`.

---

Nitpick comments:
In `@package.json`:
- Line 8: The build script in package.json currently uses the explicit flag
"next build --webpack" which forces webpack instead of the default bundler;
decide whether to keep opting out of Turbopack or remove the flag: either remove
"--webpack" from the "build" script (script key "build") so Next.js uses the
default bundler, or if webpack is required, add a short comment in the repo
docs/README explaining why "next build --webpack" is intentionally used for
compatibility/stability and reference the "build" script name so reviewers know
the reason.

In `@src/shared/ui/file-uploader/file-uploader.stories.tsx`:
- Around line 5-11: The meta object for the storybook file doesn't set a
component, so autodocs can't auto-generate prop tables; update the meta object
(named meta) to include a primary component reference (e.g., component:
FileUploader) or split the stories into separate files and set each file's
meta.component to its respective exported component (ThumbnailUploader,
FileListItem, FileUploader) so Storybook autodocs can produce proper prop
tables.

In `@src/shared/ui/form/form.stories.tsx`:
- Around line 82-86: The story's ThumbnailUploader usage ignores the File
parameter expected by ThumbnailUploaderProps; update the onUpload prop passed to
ThumbnailUploader to accept the File argument (e.g., onUpload={(file) => ...})
and generate a preview with URL.createObjectURL(file) instead of a fixed URL,
storing that value via setPreview; also update onRemove to revoke the object URL
(URL.revokeObjectURL) when clearing the preview to avoid a memory leak,
referencing ThumbnailUploader, onUpload, setPreview and the
ThumbnailUploaderProps signature in file-uploader.tsx.

In `@tsconfig.json`:
- Around line 58-66: The tsconfig currently excludes story and test files via
the "exclude" array entries ("src/**/*.stories.ts", "src/**/*.stories.tsx",
"src/**/*.test.ts", "src/**/*.test.tsx", "src/**/*.spec.ts",
"src/**/*..spec.tsx"), which can remove IDE/Next.js TypeScript support for those
files; create a companion tsconfig (e.g., tsconfig.storybook.json) that extends
the base tsconfig but overrides the "exclude" to omit those story/test globs so
editors and Storybook get proper IntelliSense, or alternatively remove only the
specific entries if you want the base config to include them—update the project
setup docs to note the new tsconfig for Storybook/tests.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c505d809-305a-4d68-9f7b-dfa406a6737b

📥 Commits

Reviewing files that changed from the base of the PR and between 37630fd and da991be.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (69)
  • .gitignore
  • .storybook/main.ts
  • .storybook/preview.ts
  • .storybook/vitest.setup.ts
  • README.md
  • docs/CONTRIBUTING.md
  • docs/VERSIONS.md
  • docs/seo-nextjs-migration-plan.md
  • eslint.config.js
  • index.html
  • next-env.d.ts
  • next.config.ts
  • package.json
  • public/index.html
  • src/app/(auth)/login/page.tsx
  • src/app/globals.css
  • src/app/index.tsx
  • src/app/layout.tsx
  • src/app/page.tsx
  • src/entities/user/index.ts
  • src/features/auth/index.ts
  • src/firebase.js
  • src/main.tsx
  • src/shared/config/firebase.ts
  • src/shared/ui/avatars/avatars.stories.tsx
  • src/shared/ui/avatars/avatars.tsx
  • src/shared/ui/badge/badge.stories.tsx
  • src/shared/ui/badge/status-badge.stories.tsx
  • src/shared/ui/button/button.stories.tsx
  • src/shared/ui/card/card.stories.tsx
  • src/shared/ui/checkbox/checkbox.stories.tsx
  • src/shared/ui/checkbox/checkbox.tsx
  • src/shared/ui/checkbox/index.ts
  • src/shared/ui/dropdown/dropdown.stories.tsx
  • src/shared/ui/empty_states/empty-states.stories.tsx
  • src/shared/ui/empty_states/empty-states.tsx
  • src/shared/ui/file-uploader/file-uploader.stories.tsx
  • src/shared/ui/file-uploader/file-uploader.tsx
  • src/shared/ui/file-uploader/index.ts
  • src/shared/ui/form/form.stories.tsx
  • src/shared/ui/form/form.tsx
  • src/shared/ui/form/index.ts
  • src/shared/ui/icons/index.tsx
  • src/shared/ui/inputBox/inputBox.stories.tsx
  • src/shared/ui/inputBox/inputBox.tsx
  • src/shared/ui/lists/lists.stories.tsx
  • src/shared/ui/lists/lists.tsx
  • src/shared/ui/modal/index.ts
  • src/shared/ui/modal/modal.stories.tsx
  • src/shared/ui/modal/modal.tsx
  • src/shared/ui/navigation/navigation.stories.tsx
  • src/shared/ui/navigation/navigation.tsx
  • src/shared/ui/pagination/pagination.stories.tsx
  • src/shared/ui/spinner/spinner.stories.tsx
  • src/shared/ui/tabs/tabs.stories.tsx
  • src/shared/ui/tabs/tabs.tsx
  • src/shared/ui/tag/tag.stories.tsx
  • src/views/home/home.stories.tsx
  • src/views/home/home.tsx
  • src/views/home/index.ts
  • src/views/index.ts
  • src/views/login/index.tsx
  • src/views/login/login.module.css
  • tailwind.config.js
  • tsconfig.app.json
  • tsconfig.json
  • tsconfig.node.json
  • vite.config.ts
  • vitest.shims.d.ts
💤 Files with no reviewable changes (7)
  • .storybook/vitest.setup.ts
  • vitest.shims.d.ts
  • index.html
  • src/main.tsx
  • src/firebase.js
  • public/index.html
  • vite.config.ts

Comment thread next-env.d.ts Outdated
Comment thread src/shared/config/firebase.ts Outdated
Comment thread src/shared/ui/avatars/avatars.tsx
Comment thread src/shared/ui/checkbox/checkbox.stories.tsx
Comment thread src/shared/ui/checkbox/checkbox.stories.tsx
Comment thread src/shared/ui/lists/lists.tsx
Comment thread src/shared/ui/modal/modal.stories.tsx
Comment thread src/shared/ui/modal/modal.tsx
Comment thread src/shared/ui/modal/modal.tsx Outdated
Comment thread src/shared/ui/modal/modal.tsx
Copy link
Copy Markdown
Contributor

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@firebase.json`:
- Around line 8-11: Add a new Next.js app-level not-found route component by
creating src/app/not-found.tsx that exports a default NotFound component (export
default function NotFound()) which renders a simple 404 UI; ensure the file
exists in the build so the static export (with trailingSlash: true) generates
out/404.html and route folders like out/login/index.html after running the next
build.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7aa8ebc8-d3ad-4fd0-a88d-2710a2ef186c

📥 Commits

Reviewing files that changed from the base of the PR and between da991be and 1824e5f.

📒 Files selected for processing (11)
  • .gitignore
  • README.md
  • docs/CONTRIBUTING.md
  • docs/VERSIONS.md
  • docs/page-rendering-strategy.md
  • docs/seo-nextjs-migration-plan.md
  • firebase.json
  • next.config.ts
  • package.json
  • public/index.html
  • src/app/page.tsx
💤 Files with no reviewable changes (1)
  • public/index.html
✅ Files skipped from review due to trivial changes (4)
  • .gitignore
  • docs/page-rendering-strategy.md
  • docs/seo-nextjs-migration-plan.md
  • docs/CONTRIBUTING.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/app/page.tsx
  • next.config.ts
  • README.md
  • package.json
  • docs/VERSIONS.md

Comment thread firebase.json
Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
docs/VERSIONS.md (1)

73-86: 업데이트 이력 섹션의 배치/정렬 규칙을 하나로 통일하면 좋겠습니다.

2026-03-29 블록이 ## 업데이트 이력 헤더 바깥에 있어, 이력 탐색 시 혼동이 생깁니다. 전부 동일 섹션 아래 역순(최신→과거)으로 정리하는 것을 권장합니다.

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

In `@docs/VERSIONS.md` around lines 73 - 86, Move the "### 2026-03-29" release
block so it sits under the existing "## 업데이트 이력" header and ensure all dated
blocks (e.g., "### 2026-03-29" and "### 2026-04-01 — Next.js 전환 1단계 문서 정리") are
consistently ordered in reverse chronological order (latest → past); verify
heading levels remain "##" for the section and "###" for each entry and adjust
spacing/markdown separators so the entire update history is a single,
consistently sorted section.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/VERSIONS.md`:
- Around line 73-86: Move the "### 2026-03-29" release block so it sits under
the existing "## 업데이트 이력" header and ensure all dated blocks (e.g., "###
2026-03-29" and "### 2026-04-01 — Next.js 전환 1단계 문서 정리") are consistently
ordered in reverse chronological order (latest → past); verify heading levels
remain "##" for the section and "###" for each entry and adjust spacing/markdown
separators so the entire update history is a single, consistently sorted
section.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0c06b0c-fa82-42d9-b995-ebba7227cad3

📥 Commits

Reviewing files that changed from the base of the PR and between 1824e5f and d71e713.

📒 Files selected for processing (7)
  • docs/CONTRIBUTING.md
  • docs/VERSIONS.md
  • docs/seo-nextjs-migration-plan.md
  • next-env.d.ts
  • src/app/globals.css
  • src/app/index.css
  • src/app/index.tsx
💤 Files with no reviewable changes (2)
  • src/app/index.css
  • src/app/index.tsx
✅ Files skipped from review due to trivial changes (3)
  • src/app/globals.css
  • next-env.d.ts
  • docs/seo-nextjs-migration-plan.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/CONTRIBUTING.md

sebeeeen and others added 17 commits April 4, 2026 22:25
- out/ 빌드 아티팩트 git 추적 제거 (git rm --cached)
- inputBox: Tailwind JIT 오작동 유발 백틱 제거
- modal: 모듈 레벨 전역 변수 제거 → 클로저 캡처 방식으로 교체, titleId dead prop 제거
- navigation: <a> → next/link, <img> → next/image
- next.config: allowedDevOrigins IP 하드코딩 → env 분리, images.unoptimized 추가
- views/index.ts: 잘못된 주석 수정 (Pages → Views)
- next-env.d.ts: .next/dev/types → .next/types (표준 경로)
- docs/VERSIONS.md: tiptap 패키지 및 문서 갱신 규칙 포함한 HEAD 기준 유지
- pnpm-lock.yaml: remote 기준 채택 후 pnpm install 재조정
머지 후 remote에서 재추적된 out/ 디렉토리 파일 제거
.gitignore에 이미 명시되어 있으나 이전에 커밋된 파일 정리

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
src/shared/ui/modal/modal.tsx (1)

56-58: ⚠️ Potential issue | 🟠 Major

Accessibility contract is still incomplete (label target + focus handling).

Line 107 still always points aria-labelledby to a generated id, but that id is only meaningful when a titled ModalHeader actually renders. Also, the public ModalProps.titleId (Line 17) is not wired in Modal (Line 56), and focus is still not moved/trapped/restored for keyboard users.

Also applies to: 94-99, 105-108

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

In `@src/shared/ui/modal/modal.tsx` around lines 56 - 58, The Modal component
currently always sets aria-labelledby to a generated id and never uses the
public ModalProps.titleId, and it doesn't manage focus; update Modal to accept
and prefer ModalProps.titleId (fallback to the generated useId value only if no
prop supplied), only apply aria-labelledby when a ModalHeader renders (e.g., via
presence of children with role/header or by accepting a hasHeader flag), and
implement keyboard focus management: on open move focus into the dialog (to the
first focusable element or the dialog container), trap focus while open, and on
close restore focus to the previously focused element; reference
ModalProps.titleId, Modal, ModalHeader, titleId, aria-labelledby, and useId when
making these changes.
🧹 Nitpick comments (2)
out/_next/static/chunks/app/(auth)/login/page-a3951dbd4039757d.js (1)

1-1: Please confirm out/ is intentionally versioned.

This is a generated, hash-named Next.js client chunk. Keeping out/_next/static/* in git will add a lot of churn and can easily desync the checked-in export from src/app when someone forgets to rebuild. If deployment already performs the build/export, I'd remove out/ from version control; otherwise document that the checked-in export is the deployable artifact.

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

In `@out/_next/static/chunks/app/`(auth)/login/page-a3951dbd4039757d.js at line 1,
The PR has a generated Next.js client chunk committed under the exported site
(the out/ directory, e.g. files under out/_next/static/*) which should not be
versioned; either remove out/ from git, add out/ (or out/_next/static/) to
.gitignore, and commit the removal, or if you intend to treat the checked-in
export as the deployable artifact, document that clearly (README/CONTRIBUTING)
and add CI validation to ensure the exported files are rebuilt and in sync;
update the repo root .gitignore and project docs accordingly and remove the
generated chunk file(s) from the commit if you choose to unversion them.
package.json (1)

21-21: Pin the Node.js runtime floor for Next.js 16.

Next.js 16 requires Node 20.9+, but this repository does not specify a minimum Node version in package.json (no engines field) or via .nvmrc, Volta, or CI configuration. Contributors on Node 18 or early Node 20 will only discover incompatibility when running next dev or next build. Add an engines field to package.json:

"engines": {
  "node": ">=20.9.0"
}

Alternatively, pin the version with .nvmrc or Volta tooling.

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

In `@package.json` at line 21, Add a Node runtime floor so contributors running
Next.js 16 don't hit runtime incompatibilities: update the project configuration
to declare a minimum Node version (for example add an "engines" field in
package.json with node >=20.9.0) or alternatively add a .nvmrc or Volta config
pin; ensure the change references the package.json manifest (where "next":
"^16.2.1" is declared) so CI and contributors are informed of the Node >=20.9.0
requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@out/_next/static/chunks/app/`(auth)/login/page-a3951dbd4039757d.js:
- Line 1: Tabs and input icon buttons remove native focus outlines (you can find
the tab button class generator inside function B and the input icon buttons
inside component C where buttons use "focus:outline-none"/"outline-none"), so
restore visible keyboard focus by removing the global "outline-none" /
"focus:outline-none" and adding explicit focus-visible styles (e.g.
focus-visible:ring-2 focus-visible:ring-offset-2
focus-visible:ring-[var(--color-primary-500,`#3385DB`)] or a matching
focus-visible shadow class) to those elements' class lists so keyboard users get
a clear focus indicator for the tab buttons (B) and the input icon buttons
(password toggle and clear buttons in C).
- Line 1: The LoginPage export (symbol S / LoginPage) currently renders dead UI
— wire real auth handlers in src/app/(auth)/login/page.tsx: implement
handleGoogleSignIn (call your auth provider/signIn flow) and hook it to the
Google button's onClick, implement handleSubmitCredentials to call your
credential auth API or signIn and replace the form's noop onSubmit with this
handler (symbols: form onSubmit, input component C, submit Button n), change the
signup anchor href from "#signup" to the real signup route or router.push
(replace the anchor with a navigation that uses handleSignupNavigate), and pass
real callbacks into Navigation (symbol L) for
onLogin/onSignup/onLogout/onAdminToggle/onProfileClick/onAdminDashboardClick;
rebuild so the module exports the working LoginPage.

In `@out/index.html`:
- Line 1: The root page currently returns null and only calls
router.replace("/login") inside useEffect (src/app/page.tsx), which yields an
empty HTML shell if the client JS is delayed; update page.tsx so the component
renders a minimal static fallback UI (e.g., visible text and a plain anchor/link
to "/login" or a message like "Redirecting to /login...") instead of null, or
alternatively move the redirect out of the client component into the
hosting/server layer; ensure the change is made where useEffect calls
router.replace and where the component currently returns null so the
server-rendered HTML contains a usable non-JS fallback.

In `@package.json`:
- Line 39: package.json added eslint-config-next but eslint.config.js never
includes it; update eslint.config.js by importing or requiring
"eslint-config-next" and spreading it into the top-level extends array alongside
"@eslint/js", "plugin:`@typescript-eslint/recommended`", and
"plugin:react-hooks/recommended" so Next.js lint rules are applied; locate the
extends array and add the eslint-config-next entry (or spread the imported
config) to the array used by the exported config.

---

Duplicate comments:
In `@src/shared/ui/modal/modal.tsx`:
- Around line 56-58: The Modal component currently always sets aria-labelledby
to a generated id and never uses the public ModalProps.titleId, and it doesn't
manage focus; update Modal to accept and prefer ModalProps.titleId (fallback to
the generated useId value only if no prop supplied), only apply aria-labelledby
when a ModalHeader renders (e.g., via presence of children with role/header or
by accepting a hasHeader flag), and implement keyboard focus management: on open
move focus into the dialog (to the first focusable element or the dialog
container), trap focus while open, and on close restore focus to the previously
focused element; reference ModalProps.titleId, Modal, ModalHeader, titleId,
aria-labelledby, and useId when making these changes.

---

Nitpick comments:
In `@out/_next/static/chunks/app/`(auth)/login/page-a3951dbd4039757d.js:
- Line 1: The PR has a generated Next.js client chunk committed under the
exported site (the out/ directory, e.g. files under out/_next/static/*) which
should not be versioned; either remove out/ from git, add out/ (or
out/_next/static/) to .gitignore, and commit the removal, or if you intend to
treat the checked-in export as the deployable artifact, document that clearly
(README/CONTRIBUTING) and add CI validation to ensure the exported files are
rebuilt and in sync; update the repo root .gitignore and project docs
accordingly and remove the generated chunk file(s) from the commit if you choose
to unversion them.

In `@package.json`:
- Line 21: Add a Node runtime floor so contributors running Next.js 16 don't hit
runtime incompatibilities: update the project configuration to declare a minimum
Node version (for example add an "engines" field in package.json with node
>=20.9.0) or alternatively add a .nvmrc or Volta config pin; ensure the change
references the package.json manifest (where "next": "^16.2.1" is declared) so CI
and contributors are informed of the Node >=20.9.0 requirement.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ee1c45c-9ee3-42dc-b435-6266c9e83ec9

📥 Commits

Reviewing files that changed from the base of the PR and between d71e713 and 4183d3f.

⛔ Files ignored due to path filters (3)
  • out/assets/ajou-logo.svg is excluded by !**/*.svg
  • out/vite.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (50)
  • .gitignore
  • README.md
  • docs/VERSIONS.md
  • out/404.html
  • out/404/index.html
  • out/__next.__PAGE__.txt
  • out/__next._full.txt
  • out/__next._head.txt
  • out/__next._index.txt
  • out/__next._tree.txt
  • out/_next/static/chunks/1f9827ce-f7a0a51790a77a5e.js
  • out/_next/static/chunks/842-9dfc9af168e60700.js
  • out/_next/static/chunks/app/(auth)/login/page-a3951dbd4039757d.js
  • out/_next/static/chunks/app/_global-error/page-b55364013c5cdff5.js
  • out/_next/static/chunks/app/_not-found/page-c46e016a44b33e7a.js
  • out/_next/static/chunks/app/layout-2bd09d57c7f734b0.js
  • out/_next/static/chunks/app/page-d84632cbe95311f8.js
  • out/_next/static/chunks/framework-d6dd631488a05ecd.js
  • out/_next/static/chunks/main-app-713d12a8f2ea8b88.js
  • out/_next/static/chunks/main-fff885e1cc206200.js
  • out/_next/static/chunks/polyfills-42372ed130431b0a.js
  • out/_next/static/chunks/webpack-282e1267345f1a63.js
  • out/_next/static/css/729202eae8491218.css
  • out/_next/static/css/a031dbc3d4ab787a.css
  • out/_next/static/wJA63Lu7_pm4CosKUSIC_/_buildManifest.js
  • out/_next/static/wJA63Lu7_pm4CosKUSIC_/_ssgManifest.js
  • out/_not-found/__next._full.txt
  • out/_not-found/__next._head.txt
  • out/_not-found/__next._index.txt
  • out/_not-found/__next._not-found.__PAGE__.txt
  • out/_not-found/__next._not-found.txt
  • out/_not-found/__next._tree.txt
  • out/_not-found/index.html
  • out/_not-found/index.txt
  • out/index.html
  • out/index.txt
  • out/login/__next.!KGF1dGgp.login.__PAGE__.txt
  • out/login/__next.!KGF1dGgp.login.txt
  • out/login/__next.!KGF1dGgp.txt
  • out/login/__next._full.txt
  • out/login/__next._head.txt
  • out/login/__next._index.txt
  • out/login/__next._tree.txt
  • out/login/index.html
  • out/login/index.txt
  • package.json
  • src/shared/config/firebase.ts
  • src/shared/ui/checkbox/checkbox.stories.tsx
  • src/shared/ui/modal/modal.stories.tsx
  • src/shared/ui/modal/modal.tsx
✅ Files skipped from review due to trivial changes (33)
  • .gitignore
  • out/login/__next._tree.txt
  • out/__next._tree.txt
  • out/_next/static/chunks/app/page-d84632cbe95311f8.js
  • out/login/__next._head.txt
  • out/_not-found/__next._head.txt
  • out/_not-found/__next._not-found.txt
  • out/_not-found/__next._tree.txt
  • out/_not-found/index.html
  • out/next/static/wJA63Lu7_pm4CosKUSIC/_ssgManifest.js
  • out/_not-found/__next._full.txt
  • out/login/__next.!KGF1dGgp.login.PAGE.txt
  • out/login/__next.!KGF1dGgp.login.txt
  • out/_not-found/__next._not-found.PAGE.txt
  • out/__next._index.txt
  • out/404/index.html
  • out/_not-found/__next._index.txt
  • out/index.txt
  • out/__next._full.txt
  • out/_next/static/chunks/app/layout-2bd09d57c7f734b0.js
  • out/__next._head.txt
  • out/login/__next._index.txt
  • out/404.html
  • out/login/index.html
  • out/_next/static/chunks/app/_global-error/page-b55364013c5cdff5.js
  • out/next/static/wJA63Lu7_pm4CosKUSIC/_buildManifest.js
  • out/_next/static/chunks/webpack-282e1267345f1a63.js
  • out/_next/static/chunks/app/_not-found/page-c46e016a44b33e7a.js
  • out/__next.PAGE.txt
  • out/login/index.txt
  • out/login/__next.!KGF1dGgp.txt
  • out/login/__next._full.txt
  • out/_next/static/chunks/main-app-713d12a8f2ea8b88.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/shared/config/firebase.ts
  • src/shared/ui/checkbox/checkbox.stories.tsx
  • README.md
  • docs/VERSIONS.md
  • src/shared/ui/modal/modal.stories.tsx

Comment thread package.json
"@vitest/coverage-v8": "^4.0.17",
"autoprefixer": "^10.4.24",
"eslint": "^9.39.2",
"eslint-config-next": "^16.2.1",
Copy link
Copy Markdown
Contributor

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
set -euo pipefail

sed -n '1,220p' eslint.config.js
echo
rg -n --glob 'eslint.config.*' 'eslint-config-next|@next/eslint-plugin-next|@next/next|core-web-vitals' .

Repository: ajou-industry-matching/aim-frontend

Length of output: 642


Wire eslint-config-next into the config array.

package.json adds eslint-config-next, but eslint.config.js only extends @eslint/js, typescript-eslint, and react-hooks (lines 10-12). The eslint-config-next import and spread into the extends array are missing, which means Next.js linting rules won't run.

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

In `@package.json` at line 39, package.json added eslint-config-next but
eslint.config.js never includes it; update eslint.config.js by importing or
requiring "eslint-config-next" and spreading it into the top-level extends array
alongside "@eslint/js", "plugin:`@typescript-eslint/recommended`", and
"plugin:react-hooks/recommended" so Next.js lint rules are applied; locate the
extends array and add the eslint-config-next entry (or spread the imported
config) to the array used by the exported config.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Vite 기반 SPA 잔재를 제거하고 Next.js(App Router) 중심으로 프로젝트 구조/의존성/문서를 정리해, Firebase Hosting 정적 export(out/) 배포 기준을 명확히 하는 PR입니다.

Changes:

  • Vite 엔트리/설정 제거 및 Next.js(App Router) 엔트리(src/app) 도입 + //login 진입 흐름 정리
  • Storybook을 @storybook/nextjs로 전환하고 스토리 타입 import/패키지 버전을 정렬
  • FSD 레이어 정리(pagesviews), 클라이언트 컴포넌트 "use client" 명시, Firebase env 검증 및 문서 갱신

Reviewed changes

Copilot reviewed 54 out of 63 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
vitest.shims.d.ts Vitest 브라우저 타입 참조 제거
vite.config.ts Vite 설정 제거
tsconfig.node.json Node(tsc) 대상 파일을 next.config.ts로 전환
tsconfig.json Next.js(App Router) 기준 TS 설정/alias로 재정의
tsconfig.app.json Vite client types 제거 및 @views/* 경로 반영
tailwind.config.js Vite index.html 기반 content 경로 제거
src/views/login/login.module.css 로그인 패널 애니메이션을 CSS Module로 전환
src/views/login/index.tsx LoginPage를 client component로 설정, CSS Module 적용
src/views/index.ts views 배럴 export 추가
src/views/home/index.ts HomePage export 추가
src/views/home/home.tsx Storybook용 HomePage 추가
src/views/home/home.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/tag/tag.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/tabs/tabs.tsx "use client" 추가
src/shared/ui/tabs/tabs.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/spinner/spinner.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/pagination/pagination.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/navigation/navigation.tsx "use client" 추가
src/shared/ui/navigation/navigation.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/modal/modal.tsx "use client" + hydration 안전 isMounted 패턴 + button type 보강
src/shared/ui/modal/modal.stories.tsx Storybook 프레임워크 import 전환 + controlled/uncontrolled 예제 정리
src/shared/ui/lists/lists.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/inputBox/inputBox.tsx "use client" 추가
src/shared/ui/inputBox/inputBox.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/index.ts UI 배럴 export 재정리(Input를 새 경로로 export 등)
src/shared/ui/icons/index.tsx 공용 아이콘 export 구성/정렬 업데이트
src/shared/ui/form/form.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/file-uploader/file-uploader.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/empty_states/empty-states.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/dropdown/dropdown.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/checkbox/checkbox.stories.tsx useArgs 기반 인터랙티브 스토리로 전환
src/shared/ui/card/card.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/button/button.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/badge/status-badge.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/badge/badge.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/ui/avatars/avatars.stories.tsx Storybook 프레임워크 import를 Next로 전환
src/shared/config/firebase.ts NEXT_PUBLIC_FIREBASE_* 기반 env 검증 및 설정 적용
src/main.tsx Vite SPA 엔트리 제거
src/firebase.js Vite env 기반 Firebase 초기화 파일 제거
src/features/auth/index.ts 빈 export로 정리
src/entities/user/index.ts 빈 export로 정리
src/app/page.tsx / 진입 시 /login CSR 리다이렉트 추가
src/app/layout.tsx Next root layout + metadata 추가
src/app/index.tsx 레거시 App 엔트리 제거
src/app/index.css 레거시 App 엔트리 스타일 제거
src/app/globals.css Next 글로벌 스타일 진입점에서 src/index.css import
src/app/(auth)/login/page.tsx App Router /login 라우트에서 views LoginPage 렌더
README.md Vite → Next.js로 스택/명령어/문서 링크 갱신
public/index.html Firebase Hosting 기본 fallback 페이지 제거
package.json Next dev/build 중심 스크립트/의존성으로 전환, Storybook 버전 정렬
next.config.ts static export(output: export) 등 Next 설정 추가
next-env.d.ts Next 타입 참조 파일 추가/수정
index.html Vite index.html 엔트리 제거
firebase.json Hosting public을 out/으로 전환, SPA rewrite 제거
eslint.config.js .next, storybook-static ignore 및 Vite 관련 린트 설정 제거
docs/VERSIONS.md 현재 스택/버전/운영 기준 문서 갱신
docs/page-rendering-strategy.md SSG/CSR 중심 렌더링 전략 문서 추가
docs/CONTRIBUTING.md Next.js 전환 기준/구조/실행/배포 가이드 갱신
.storybook/vitest.setup.ts Storybook+Vitest setup 제거
.storybook/preview.ts @storybook/nextjs 기반 Preview 타입으로 전환
.storybook/main.ts @storybook/nextjs framework로 전환
.gitignore .next, out, .vercel ignore 추가

Comment thread src/app/layout.tsx
Comment on lines +9 to +14
type RootLayoutProps = Readonly<{
children: React.ReactNode;
}>;

export default function RootLayout({ children }: RootLayoutProps): React.ReactElement {
return (
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

RootLayoutProps/RootLayout reference React.ReactNode and React.ReactElement, but React isn’t imported in this module. With TS modules + jsx: react-jsx, this will typically fail typechecking (Cannot find name 'React'). Import the needed types from react (or avoid the explicit React.* annotations).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
import { LoginPage } from "@/views/login";

export default function LoginRoute(): React.ReactElement {
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

This file returns React.ReactElement but doesn’t import React (or ReactElement) anywhere. That will usually fail TypeScript compilation in module scope. Import type ReactElement from react (or drop the return type annotation).

Suggested change
import { LoginPage } from "@/views/login";
export default function LoginRoute(): React.ReactElement {
import type { ReactElement } from "react";
import { LoginPage } from "@/views/login";
export default function LoginRoute(): ReactElement {

Copilot uses AI. Check for mistakes.
Comment thread package.json
Comment on lines +7 to +11
"dev": "next dev",
"build": "next build --webpack",
"start": "python3 -m http.server 3000 -d out",
"lint": "eslint .",
"preview": "vite preview",
"preview": "python3 -m http.server 3000 -d out",
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

start/preview scripts depend on python3 being installed on every dev/CI environment. If the team needs cross-platform scripts, consider using a Node-based static server (e.g. npx serve out -l 3000) or a dedicated package (http-server) instead of relying on Python.

Copilot uses AI. Check for mistakes.
@ajou-industry-matching ajou-industry-matching deleted a comment from coderabbitai Bot Apr 5, 2026
@ajou-industry-matching ajou-industry-matching deleted a comment from coderabbitai Bot Apr 5, 2026
@sebeeeen sebeeeen merged commit c4c32f8 into dev Apr 5, 2026
5 checks passed
@sebeeeen sebeeeen deleted the feat/next-app-router branch May 25, 2026 14:33
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.

[Chore] SEO 대응을 위한 Next.js 전환 구조 설계

2 participants