Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { ControlBar, Header } from '@/shared/components';
import { Icon } from '@/shared/icons';

export default function Home() {
return (
<>
<div className='flex flex-col items-center justify-center min-h-screen py-2 bg-gray-50'>
<main className='flex flex-col items-center justify-center w-full flex-1 px-4 sm:px-20 text-center'>
<Header onClick={() => {}} className='' title={'글다'} />
<ControlBar isLoggedIn={false} onLogin={() => {}} userName={''} />
<ControlBar isLoggedIn={true} userName='홍길동' onLogin={() => {}} />
<h1 className='text-4xl sm:text-6xl font-extrabold text-gray-900 mb-4'>
초기 세팅 완료
<Icon name='User' color='mint-600' />
</h1>
<Icon name='CalendarBlank' size={200} color='gray-900' />
<Icon name='User' size={200} color='blue-400' />
<Icon name='User' size={200} color='red-300' />
<Icon name='User' size={200} color='red-300' />

<p className='mt-3 text-lg sm:text-2xl text-gray-600'>
Next.js(Page Router) + TS + Tailwind + Axios
</p>
Expand Down
71 changes: 71 additions & 0 deletions src/shared/components/header/ControlBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Icon } from '@/shared/icons';
import { cn } from '@/shared/lib';
import { cva } from 'class-variance-authority';

interface ControlBarProps {
isLoggedIn: boolean;
onLogin: () => void;
userName: string;
className?: string;
}
Comment on lines +5 to +10
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 | 🟡 Minor

인터페이스와 기본값 불일치를 수정하세요.

isLoggedIn이 인터페이스에서는 필수 prop(boolean)으로 정의되어 있지만, 컴포넌트에서는 기본값(false)을 제공합니다. 인터페이스에서도 선택적으로 변경해야 일관성이 유지됩니다.

다음과 같이 수정하세요:

 interface ControlBarProps {
-  isLoggedIn: boolean;
+  isLoggedIn?: boolean;
   onLogin?: () => void;
   userName?: string;
   className?: string;
 }
📝 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
interface ControlBarProps {
isLoggedIn: boolean;
onLogin?: () => void;
userName?: string;
className?: string;
}
interface ControlBarProps {
isLoggedIn?: boolean;
onLogin?: () => void;
userName?: string;
className?: string;
}
🤖 Prompt for AI Agents
src/shared/components/header/ControlBar.tsx around lines 5 to 10: the
ControlBarProps interface declares isLoggedIn as a required boolean but the
component provides a default value (false); make the interface consistent by
changing isLoggedIn to an optional prop (isLoggedIn?: boolean) so the prop
signature matches the component's defaulting behavior.


const rightStyle = cva('flex items-center gap-[0.6rem] transition w-[7.8rem]', {
variants: {
state: {
loggedIn: 'text-mint-600',
guest: 'text-gray-400 hover:text-gray-600',
},
},
defaultVariants: { state: 'guest' },
});

const ControlBar = ({
onLogin,
isLoggedIn,
userName = '글다',
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Oct 17, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

userName 기본값이 의미상 적절하지 않습니다.

사용자 이름의 기본값으로 '글다'를 사용하는 것은 의미상 적절하지 않습니다. 서비스 이름을 사용자 이름 기본값으로 사용하면 혼란을 야기할 수 있습니다.

다음 중 하나로 수정하는 것을 권장합니다:

-  userName = '글다',
+  userName = '사용자',

또는 기본값 없이 빈 문자열로 처리:

-  userName = '글다',
+  userName = '',
📝 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
userName = '글다',
userName = '사용자',
Suggested change
userName = '글다',
userName = '',
🤖 Prompt for AI Agents
In src/shared/components/header/ControlBar.tsx at line 25, the default prop
userName is set to '글다' which is semantically misleading; change the default to
a neutral value (e.g., 'Guest') or an empty string/undefined so the service name
isn't used as a username, and update any related render logic to gracefully
handle the empty/undefined value (show placeholder text or omit the name).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

이 부분은 우리 서비스명이 '글다'여서 기본 사용자이름이 글다면 좋을것 같아 설정해두었습니다

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.

@jjangminii, 이해했습니다! 서비스명을 기본 사용자이름으로 사용하는 것이 의도된 디자인이었군요. 설명해주셔서 감사합니다. 제안을 철회하겠습니다. 😊

   🐰
  (^.^)
 (     )
  |   |
 ~~~ ~~~

✏️ Learnings added
Learnt from: jjangminii
PR: geulDa/FE#32
File: src/shared/components/header/ControlBar.tsx:25-25
Timestamp: 2025-10-18T07:48:47.811Z
Learning: In the geulDa/FE project, the service name '글다' is intentionally used as the default userName in the ControlBar component (src/shared/components/header/ControlBar.tsx). This is a deliberate design decision to display the service name as the default username.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

className,
}: ControlBarProps) => {
const rightState = isLoggedIn ? 'loggedIn' : ('guest' as const);
const iconColor = isLoggedIn ? 'mint-600' : ('gray-400' as const);

return (
<header className={cn('bg-none w-full h-[5.4rem]', className)} role='group'>
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 | 🟡 Minor

role='group' 속성을 제거하세요.

<header> 요소는 이미 landmark 역할을 가지고 있어 추가적인 role='group'은 불필요하며 접근성을 오히려 저해할 수 있습니다.

다음과 같이 수정하세요:

-    <header className={cn('bg-none w-full h-[5.4rem]', className)} role='group'>
+    <header className={cn('bg-none w-full h-[5.4rem]', 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
<header className={cn('bg-none w-full h-[5.4rem]', className)} role='group'>
<header className={cn('bg-none w-full h-[5.4rem]', className)}>
🤖 Prompt for AI Agents
In src/shared/components/header/ControlBar.tsx around line 32, remove the
redundant role='group' attribute from the <header> element (leave the className
and other props intact) so the header retains its native landmark semantics and
does not override or harm accessibility.

<div className='h-full grid grid-cols-[auto_1fr_auto] items-center gap-[1.2rem]'>
<div
className='w-[7.8rem] h-[5.4rem] rounded-[0.6rem] bg-gray-200'
aria-hidden
/>

<h1 className='justify-self-center text-headline-sm-serif font-[400] leading-[2.4rem] tracking-[0.015rem] text-gray-900'>
글다
</h1>
{isLoggedIn ? (
<div className={cn(rightStyle({ state: rightState }), 'min-w-0')}>
<Icon name='User' size={24} color={iconColor} />
<span
className='text-body-md w-[5.3rem] truncate block'
aria-label={`${userName}님`}
>
{userName}님
</span>
</div>
) : (
<button
type='button'
onClick={(e) => {
e.stopPropagation();
onLogin?.();
}}
className={rightStyle({ state: rightState })}
aria-label='Log In'
>
<Icon name='User' size={24} color={iconColor} />
<span className='text-body-md'>Log In</span>
</button>
)}
</div>
</header>
);
};

export default ControlBar;
55 changes: 55 additions & 0 deletions src/shared/components/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Icon } from '@/shared/icons';
import { cn } from '@/shared/lib';
import { cva, type VariantProps } from 'class-variance-authority';

interface HeaderProps {
title: string;
onClick: () => void;
className?: string;
color?: VariantProps<typeof headerStyle>['color'];
}

const headerStyle = cva(
'w-full h-[11.8rem] px-[1.6rem] pb-[1.8rem] pt-[7.6rem] fixed top-0 left-0 right-0 z-[100]',
{
variants: {
color: {
mint300: 'bg-mint-300',
mint50: 'bg-mint-50',
},
},
defaultVariants: {
color: 'mint300',
},
},
);
Comment on lines +12 to +25
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

고정 헤더로 인한 콘텐츠 겹침 문제가 발생할 수 있습니다.

헤더가 fixed 포지션으로 top-0에 고정되어 있고 높이가 11.8rem이므로, 페이지 콘텐츠가 헤더 아래에 가려질 수 있습니다. 페이지 본문에 헤더 높이만큼의 padding-top 또는 margin-top을 추가해야 합니다.

예를 들어, 이 Header를 사용하는 페이지의 main 컨텐츠에 pt-[11.8rem] 클래스를 추가하거나, body 레이아웃 컴포넌트에서 처리하는 것을 권장합니다.

🤖 Prompt for AI Agents
In src/shared/components/header/Header.tsx around lines 12 to 25, the header is
positioned fixed at top-0 with a height of 11.8rem which will overlap page
content; update the layout so main page content gets a top offset equal to the
header height — either add pt-[11.8rem] (or margin-top: 11.8rem) to the main
container where Header is used, or export a headerHeight constant from this
module and apply that value to the body/main wrapper (or a layout component) so
all pages consistently receive the correct padding-top to avoid content being
hidden under the fixed header.


const Header = ({ title, onClick, color, className }: HeaderProps) => {
return (
<header className={cn(headerStyle({ color }), className)}>
<div className='mx-auto h-[2.4rem] grid grid-cols-[auto_1fr_auto] items-center text-mint-900'>
<button
type='button'
aria-label='뒤로가기'
onClick={onClick}
className='grid place-items-center w-[2.4rem] h-[2.4rem]'
>
<Icon name='Caret' size={24} color='mint-900' />
</button>

<h1
className={cn(
'justify-self-center',
'text-title-md font-[600] leading-[2.4rem] tracking-[0.015rem]',
)}
>
{title}
</h1>

<div aria-hidden className='w-[2.4rem] h-[2.4rem]' />
</div>
</header>
);
};

export default Header;
2 changes: 2 additions & 0 deletions src/shared/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Header } from './header/Header';
export { default as ControlBar } from './header/ControlBar';
Loading