Backoffice setup#5
Conversation
📝 WalkthroughWalkthrough백오피스 영역을 신설했습니다: 로그인/회원가입 페이지 추가, 메인 레이아웃과 사이드바 내비게이션 도입, 대시보드 홈과 5개 섹션의 임시 페이지 생성, 헤더는 백오피스 경로 인식으로 브랜드 텍스트를 분기합니다. 로그인/회원가입은 클라이언트 상태와 기본 검증/토글을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant L as Backoffice Login Page
participant R as Router
U->>L: Submit adminId/password
L->>L: preventDefault()
L-->>U: alert(adminId)
Note over L: TODO: 실제 로그인 API 연동
U-->>R: Navigate 링크(회원가입/찾기)
sequenceDiagram
participant U as User
participant S as Backoffice Signup Page
U->>S: 입력 변경 (phone/email/password/닉네임)
U->>S: 약관 체크/전체동의 토글
U->>S: 인증하기 클릭
S-->>U: alert(인증 코드 전송 스텁)
U->>S: 가입하기 제출
S->>S: 클라이언트 검증(비번일치/닉네임/필수동의)
S-->>U: alert(가입 시도)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (15)
src/app/backoffice-main/customer/page.tsx (2)
1-1: Next 13+/App Router에서는 React import가 불필요합니다 (사소한 정리)JSX 자동 런타임을 사용한다면
import React from 'react'는 제거 가능합니다.적용 예:
-import React from 'react';
3-9: 중복된 임시 페이지 패턴을 공용 컴포넌트로 추출하면 유지보수가 수월합니다동일한 "임시 페이지입니다" 뷰가 여러 경로에 반복됩니다. 간단한 Placeholder 컴포넌트를 만들고 텍스트/크기를 props로 받게 하면 이후 메시지 변경 시 한 곳에서 관리할 수 있습니다.
예시(참고용):
// 예: src/components/backoffice/Placeholder.tsx export function Placeholder({ children }: { children?: React.ReactNode }) { return ( <div className="flex items-center justify-center h-full text-2xl font-bold"> {children ?? '임시 페이지입니다'} </div> ); } // 사용 export default function TemporaryPage() { return <Placeholder />; }원하시면 공용 컴포넌트와 각 페이지 적용 PR 패치를 만들어드릴게요.
src/app/backoffice-main/permission/page.tsx (1)
1-1: React import 제거 가능 (사소한 정리)자동 JSX 변환을 사용한다면 다음 라인은 생략 가능합니다.
-import React from 'react';src/app/backoffice-main/settings/page.tsx (1)
1-1: React import 제거 가능 (사소한 정리)Next App Router 기본 설정이라면 JSX 자동 런타임이므로 불필요한 import입니다.
-import React from 'react';src/app/backoffice-main/product/page.tsx (1)
1-1: React import 제거 가능 (사소한 정리)같은 패턴으로 불필요한
import React는 제거해도 됩니다.-import React from 'react';src/app/backoffice-main/page.tsx (2)
7-25: 대시보드 홈 스캐폴딩 적절함레이아웃과 카드 배치가 간결하고 명확합니다. 이후 실제 지표 연동 시, 숫자 포맷터(예: Intl.NumberFormat)로 통화/숫자 표기를 일관화하면 좋겠습니다.
1-1: 불필요한 React 임포트 제거 제안Next.js(App Router)에서는 JSX를 위해 React 임포트가 필요 없습니다. 코드 경량화를 위해 제거하는 것을 고려해주세요.
-import React from 'react';src/app/backoffice-main/auction/page.tsx (1)
3-8: 접근성/시맨틱 향상: 페이지 제목 추가 권장임시 페이지라도 h1 등 시맨틱 제목을 제공하면 스크린 리더 및 SEO에 유리합니다. 간단한 h1을 상단에 추가해 주세요.
export default function TemporaryPage() { return ( - <div className="flex items-center justify-center h-full text-2xl font-bold"> - 임시 페이지입니다 - </div> + <div className="flex h-full flex-col items-center justify-center"> + <h1 className="mb-4 text-2xl font-bold">경매</h1> + <p className="text-lg">임시 페이지입니다</p> + </div> ); }src/app/backoffice-login/page.tsx (3)
66-81: 비밀번호 토글 아이콘/레이블 의미 뒤바뀜 및 접근성 개선 필요현재 showPassword가 true일 때 입력 타입이 password로 설정되어 “숨김 상태”인데, 아이콘/alt는 “숨기기”로 표기되어 의미가 어긋납니다. 또한 inline SVG에 alt는 접근성에 사용되지 않으므로 버튼에 aria-label을 제공해야 합니다.
- <button - className='absolute top-[39px] right-[16px]' - type='button' - onClick={() => setShowPassword((prev) => !prev)} // 클릭 시 showPassword 상태를 반전시킵니다. - > - {showPassword ? ( - <EyeClose alt='비밀번호 숨기기' height={24} width={24} /> - ) : ( - <EyeOpen alt='비밀번호 보이기' height={24} width={24} /> - )} - </button> + <button + className='absolute top-[39px] right-[16px]' + type='button' + aria-label={showPassword ? '비밀번호 보이기' : '비밀번호 숨기기'} + onClick={() => setShowPassword((prev) => !prev)} + > + {showPassword ? ( + <EyeOpen height={24} width={24} /> + ) : ( + <EyeClose height={24} width={24} /> + )} + </button>
96-107: 임시 링크의 의도 명시 및 오작동 방지href='#'는 최상단 점프 등 예기치 않은 동작을 유발합니다. 준비중이라면 명시적으로 비활성화 처리해 주세요.
- <Link className='font-medium' href='#'> + <Link + className='font-medium cursor-not-allowed opacity-60' + href='#' + aria-disabled='true' + onClick={(e) => e.preventDefault()} + > 아이디 찾기 </Link> <span className='text-[12px]/[12px] text-[#E0E0E0]'>|</span> - <Link className='font-medium' href='#'> + <Link + className='font-medium cursor-not-allowed opacity-60' + href='#' + aria-disabled='true' + onClick={(e) => e.preventDefault()} + > 비밀번호 찾기 </Link>
18-23: TODO 알림에 민감 정보 노출 주의alert로 관리자 아이디를 노출하는 패턴은 개발 중에도 캡처/로그 유출 우려가 있습니다. 임시로 console.info 정도로 낮추거나 토스트 메시지로 대체를 권장합니다.
- alert('관리자 로그인 시도: ' + adminId); + console.info('[DEV] 관리자 로그인 시도', { adminId });src/app/backoffice-main/layout.tsx (1)
33-36: 활성 링크 판별 로직 보강 및 aria-current 추가정확 일치 비교는 하위 경로에서 비활성 처리되는 문제가 있습니다. 홈은 정확 일치, 그 외는 하위 경로까지 활성으로 취급하도록 보강하고, 접근성을 위해 aria-current를 부여하세요.
- {navLinks.map((link) => { - // 현재 경로와 링크의 경로가 일치하는지 확인 - const isActive = pathname === link.href; + {navLinks.map((link) => { + // 홈은 정확 일치, 그 외는 하위 경로까지 활성 처리 + const isActive = + link.href === '/backoffice-main' + ? pathname === link.href + : pathname === link.href || pathname.startsWith(link.href + '/'); return ( <li key={link.href} className='mb-2'> <Link href={link.href} + aria-current={isActive ? 'page' : undefined} // cn 유틸리티를 사용하여 조건부 스타일링 적용 // isActive가 true이면 bg-[#6C918B] 클래스를, 아니면 bg-transparent를 적용 className={cn( 'block p-3 transition-colors', { 'bg-[#F4FEFC] text-gray-800 border-r-4 border-[#6C918B]': isActive, 'bg-transparent rounded-md': !isActive, }, )} > {link.label} </Link> </li> ); })}Also applies to: 40-51
src/app/backoffice-login/signup/page.tsx (3)
174-185: 비밀번호 토글 버튼 접근성 보강(aria-label) 및 SVG alt 제거inline SVG의 alt는 접근성에 반영되지 않습니다. 버튼에 동적 aria-label을 추가하고 아이콘 alt는 제거하세요.
- <button - className='absolute top-[39px] right-[16px]' - type='button' - onClick={() => setShowPassword((prev) => !prev)} - > - {showPassword ? ( - <EyeOpen alt='비밀번호 숨기기' height={24} width={24} /> - ) : ( - <EyeClose alt='비밀번호 보기' height={24} width={24} /> - )} - </button> + <button + className='absolute top-[39px] right-[16px]' + type='button' + aria-label={showPassword ? '비밀번호 숨기기' : '비밀번호 보이기'} + onClick={() => setShowPassword((prev) => !prev)} + > + {showPassword ? ( + <EyeOpen height={24} width={24} /> + ) : ( + <EyeClose height={24} width={24} /> + )} + </button> @@ - <button - className='absolute top-[17px] right-[16px]' - type='button' - onClick={() => setShowPasswordConfirm((prev) => !prev)} - > - {showPasswordConfirm ? ( - <EyeOpen alt='비밀번호 숨기기' height={24} width={24} /> - ) : ( - <EyeClose alt='비밀번호 보기' height={24} width={24} /> - )} - </button> + <button + className='absolute top-[17px] right-[16px]' + type='button' + aria-label={showPasswordConfirm ? '비밀번호 숨기기' : '비밀번호 보이기'} + onClick={() => setShowPasswordConfirm((prev) => !prev)} + > + {showPasswordConfirm ? ( + <EyeOpen height={24} width={24} /> + ) : ( + <EyeClose height={24} width={24} /> + )} + </button>Also applies to: 197-208
111-121: 자동완성 힌트 추가로 UX 개선브라우저 자동완성 힌트를 지정하면 입력 편의성과 정확도가 올라갑니다.
<input required + autoComplete='tel' className='w-full flex-grow border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='phone' name='phone' placeholder='전화번호 입력' type='tel' value={formData.phone} onChange={handleChange} /> @@ <input required + autoComplete='one-time-code' className='mt-[4px] w-full border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='verificationCode' name='verificationCode' placeholder='인증번호 입력' type='text' value={formData.verificationCode} onChange={handleChange} /> @@ <input required + autoComplete='email' className='w-full border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='email' name='email' placeholder='abd@email.com' type='email' value={formData.email} onChange={handleChange} /> @@ <input required + autoComplete='new-password' className='w-full border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='password' name='password' placeholder='비밀번호 입력' type={showPassword ? 'text' : 'password'} value={formData.password} onChange={handleChange} /> @@ <input required + autoComplete='new-password' className='mt-[8px] w-full border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='passwordConfirm' name='passwordConfirm' placeholder='비밀번호 재입력' type={showPasswordConfirm ? 'text' : 'password'} value={formData.passwordConfirm} onChange={handleChange} /> @@ <input required + autoComplete='nickname' className='w-full border border-[#E0E0E0] px-[16px] py-[10px] text-[16px]/[22px] placeholder-[#9E9E9E]' id='nickname' name='nickname' placeholder='별명 (2~20자)' type='text' value={formData.nickname} onChange={handleChange} />Also applies to: 130-138, 147-155, 165-173, 187-196, 217-225
229-332: 중복 약관 UI/로직의 컴포넌트화 제안일반 회원가입 페이지와 거의 동일한 약관 동의 UI/상태 관리가 반복됩니다. 재사용 가능한 TermsAgreement 컴포넌트로 분리하면 유지보수성이 크게 향상됩니다.
원하시면 공용 컴포넌트를 설계(Props: required/optional 항목 정의, 상태 바인딩 콜백 등)해 드릴게요.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
src/app/backoffice-login/page.tsx(1 hunks)src/app/backoffice-login/signup/page.tsx(1 hunks)src/app/backoffice-main/auction/page.tsx(1 hunks)src/app/backoffice-main/customer/page.tsx(1 hunks)src/app/backoffice-main/layout.tsx(1 hunks)src/app/backoffice-main/page.tsx(1 hunks)src/app/backoffice-main/permission/page.tsx(1 hunks)src/app/backoffice-main/product/page.tsx(1 hunks)src/app/backoffice-main/settings/page.tsx(1 hunks)src/components/layout/Header.tsx(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/app/backoffice-main/permission/page.tsx (4)
src/app/backoffice-main/auction/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/customer/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/product/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/settings/page.tsx (1)
TemporaryPage(3-9)
src/app/backoffice-main/settings/page.tsx (4)
src/app/backoffice-main/auction/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/customer/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/permission/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/product/page.tsx (1)
TemporaryPage(3-9)
src/app/backoffice-main/auction/page.tsx (4)
src/app/backoffice-main/customer/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/permission/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/product/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/settings/page.tsx (1)
TemporaryPage(3-9)
src/app/backoffice-main/customer/page.tsx (4)
src/app/backoffice-main/auction/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/permission/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/product/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/settings/page.tsx (1)
TemporaryPage(3-9)
src/app/backoffice-main/product/page.tsx (4)
src/app/backoffice-main/auction/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/customer/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/permission/page.tsx (1)
TemporaryPage(3-9)src/app/backoffice-main/settings/page.tsx (1)
TemporaryPage(3-9)
src/app/backoffice-main/page.tsx (2)
src/app/page.tsx (1)
Home(5-15)src/app/layout.tsx (1)
RootLayout(16-28)
src/app/backoffice-main/layout.tsx (2)
src/lib/utils.ts (1)
cn(4-6)src/app/layout.tsx (1)
RootLayout(16-28)
src/app/backoffice-login/page.tsx (1)
src/app/login/page.tsx (3)
LoginPage(9-95)e(14-18)e(56-56)
src/app/backoffice-login/signup/page.tsx (2)
src/app/signup/page.tsx (3)
e(79-96)e(50-72)e(37-47)src/app/login/page.tsx (2)
LoginPage(9-95)e(14-18)
🔇 Additional comments (5)
src/components/layout/Header.tsx (2)
4-4: 클라이언트 훅 도입 적절합니다
usePathname사용을 위해 클라이언트 컴포넌트 선언이 이미 되어 있어 일관성 있습니다. 별도 이슈 없습니다.
8-9: 브랜드 전환 로직 검증 결과:
현재pathname.startsWith('/backoffice')는/backoffice-main,/backoffice-login경로에서도 true를 반환하므로, 별도 PREFIX 배열을 정의하지 않아도 백오피스 페이지를 올바르게 감지합니다.
따라서 제안된 다중 prefix 처리 코드는 중복이며 불필요합니다.다른 이유로 브랜드 전환이 동작하지 않는다면, 해당 로직 외부(예: 라우터 설정 또는 상태 관리)에서 문제를 확인해 주세요.
Likely an incorrect or invalid review comment.
src/app/backoffice-main/customer/page.tsx (1)
5-5: h-full은 상위 컨테이너 높이에 의존합니다부모 레이아웃이
min-h-screen또는flex-1등으로 높이를 보장하지 않으면 수직 중앙 정렬이 깨질 수 있습니다. 상위 레이아웃의 height 정책을 한 번 확인해 주세요.src/app/backoffice-main/layout.tsx (2)
60-61: 메인 스크롤 컨테이너 유지 적절내부 콘텐츠만 스크롤되도록 overflow-y-auto를 main에 둔 구성은 유지하는 것이 좋습니다. 위의 섹션 overflow-hidden 변경과 조합 시 이중 스크롤이 해소됩니다.
24-29: 레이아웃 헤더-메인 스크롤 중첩 여부 확인 요청아래 두 위치에서
pt-32(헤더 패딩)와h-screen(본문 높이)가 동시에 적용되고 있어 이중 스크롤이 발생할 수 있습니다.
실제 페이지를 렌더링해 스크롤바가 중첩되어 보이는지 확인해 주세요.• src/app/layout.tsx:22
<div className='pt-32'>{children}</div>• src/app/backoffice-main/layout.tsx:28
<section className='flex h-screen bg-gray-100'>이중 스크롤이 보인다면, section의 높이를 헤더 높이(8rem)를 제외하도록
h-[calc(100vh-8rem)]등으로 조정해 주시기 바랍니다.
| <section className='flex h-screen bg-gray-100'> | ||
| <aside className='w-52 flex-shrink-0 bg-gray-100 p-4 text-gray-800'> | ||
|
|
||
| <nav> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
헤더 패딩(pt-32)와 h-screen 중복으로 인한 이중 스크롤/오버플로우 가능성
RootLayout에서 children에 pt-32(8rem)을 주는 상태에서 섹션에 h-screen을 고정하면, 헤더+패딩+섹션 높이가 100vh를 초과해 페이지 스크롤과 내부(main) 스크롤이 중첩될 수 있습니다. 가용 높이에서 헤더 패딩을 차감하고 섹션에서 overflow를 관리하도록 조정하세요.
- <section className='flex h-screen bg-gray-100'>
- <aside className='w-52 flex-shrink-0 bg-gray-100 p-4 text-gray-800'>
-
- <nav>
+ <section className='flex h-[calc(100vh-8rem)] overflow-hidden bg-gray-100'>
+ <aside className='w-52 flex-shrink-0 overflow-y-auto bg-gray-100 p-4 text-gray-800'>
+ <nav aria-label='백오피스 사이드바 내비게이션'>📝 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.
| <section className='flex h-screen bg-gray-100'> | |
| <aside className='w-52 flex-shrink-0 bg-gray-100 p-4 text-gray-800'> | |
| <nav> | |
| <section className='flex h-[calc(100vh-8rem)] overflow-hidden bg-gray-100'> | |
| <aside className='w-52 flex-shrink-0 overflow-y-auto bg-gray-100 p-4 text-gray-800'> | |
| <nav aria-label='백오피스 사이드바 내비게이션'> |
📌 변경 사항 개요
📝 backoffice 홈페이지를 일단 만들어봤습니다
🔗 최대한 비슷하게 만들었고, 서로 들어가는 링크와 페이지 구조를 확립했어요
💡 참고 사항
잘 못하지만 일단 계속 업로드해보겠습니다.
Summary by CodeRabbit