Skip to content

Conversation

@dbswl701
Copy link
Contributor

@dbswl701 dbswl701 commented Feb 19, 2025

📝 주요 작업 내용

  • input text 컴포넌트 개발
  • input password 컴포넌트 개발

📺 스크린샷

image image

🔗 참고 사항

  • preview/input에서 미리보기가 가능합니다.
  • 비밀번호의 눈 아이콘 색상 부분이 아직 완료되지 않았습니다. 해당 부분의 상위 컴포넌트에서 focus, 타이핑 등 여러 고려해야 할 부분이 많아 이 부분은 로그인, 회원가입 페이지 개발에서 수정하겠습니다.
  • react-hook-form을 쓰는 것을 가정하고 작성하였습니다.
  • 접근성을 위해 id가 필수적으로 필요합니다.

💬 리뷰 요구사항

인풋 컴포넌트를 가져다 쓰는 곳에서의 사용성이 어떨지 고려해서 작성하였는데, 제가 생각치 못한 부분이 있다면 말씀 부탁드립니다.

📃 관련 이슈

DEVING-34

Summary by CodeRabbit

  • 새로운 기능
    • 다양한 입력 상태(일반, 비밀번호, 맞춤 너비)를 시연하는 예제 컴포넌트가 추가되었습니다.
    • 입력 필드가 텍스트와 비밀번호 모드를 지원하며, 기본, 입력 중, 완료, 에러, 비활성화 상태에 따른 시각적 피드백과 에러 메시지가 제공됩니다.
    • 패스워드 입력에 대한 가시성 토글 기능과 반응형 레이아웃이 적용되어 사용자 경험이 향상되었습니다.

@vercel
Copy link

vercel bot commented Feb 19, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
moim-fe ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 19, 2025 4:03am
moim-fe-1kc6 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 19, 2025 4:03am

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2025

Walkthrough

이번 변경 사항은 두 개의 컴포넌트 업데이트를 포함합니다. 첫 번째로, ButtonExamples라는 새로운 React 컴포넌트가 추가되어 다양한 상태(기본, 입력 중, 완료, 오류, 비활성화)에서의 Input 컴포넌트 예시를 렌더링합니다. 두 번째로, Input 컴포넌트가 업데이트되어 상태 및 크기에 따라 CSS 클래스 변형을 적용하고, 비밀번호 입력 시 가시성을 전환할 수 있는 토글 기능이 도입되었습니다.

Changes

파일 변경 요약
src/app/preview/input/page.tsx 새로운 React 컴포넌트 ButtonExamples 추가, 다양한 Input 상태 예시 렌더링
src/components/ui/Input.tsx Input 컴포넌트 업데이트: 상태/크기 따른 스타일 변형, 비밀번호 가시성 토글 기능 포함

Sequence Diagram(s)

sequenceDiagram
    participant U as 사용자
    participant I as Input 컴포넌트
    participant V as 가시성 토글 아이콘

    U->>I: 비밀번호 입력 필드 활성화
    U->>V: 토글 아이콘 클릭
    V->>I: 토글 이벤트 전달
    I->>I: 입력 타입 상태 업데이트 (password ↔ text)
    I-->>U: 업데이트된 입력 렌더링
Loading

Poem

토끼가 뛰노는 코드 숲에서,
버튼 예시와 입력이 춤을 추네 🐰
반짝이는 토글 아이콘, 사용자의 눈을 사로잡고,
깔끔한 변화로 코드가 빛나네,
즐거운 발자국 소리와 함께 개발길을 달리네!

— CodeRabbit Inc.가 전하는 기쁨!


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ❌ failure
  • Tests: ✅ success
  • Build: ❌ failure

❌ 일부 검사가 실패했습니다.

@codecov
Copy link

codecov bot commented Feb 19, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

@vercel vercel bot temporarily deployed to Preview – moim-fe February 19, 2025 02:09 Inactive
@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ✅ success
  • Tests: ✅ success
  • Build: ✅ success

✅ 모든 검사가 통과되었습니다.

Copy link

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

🧹 Nitpick comments (2)
src/app/preview/input/page.tsx (1)

8-137: 코드 중복을 제거하여 유지보수성을 개선해야 합니다.

반복되는 컨테이너 너비 정의를 상수로 추출하고, 반복되는 Input 컴포넌트 렌더링을 map 함수를 사용하여 리팩토링하는 것이 좋습니다.

다음과 같이 수정하는 것을 제안합니다:

const CONTAINER_WIDTH = 'w-[460px]';

const INPUT_VARIANTS = [
  { title: 'normal', props: { placeholder: 'Default' } },
  { title: 'typing', props: { placeholder: 'Default', defaultValue: 'Typing' } },
  { title: 'done', props: { defaultValue: 'Done', state: 'success' } },
  { title: 'error', props: { defaultValue: 'Error', errorMessage: '필수 입력 사항입니다.' } },
  { title: 'disable', props: { placeholder: 'Disabled', disabled: true } },
] as const;

// 사용 예:
{INPUT_VARIANTS.map(({ title, props }) => (
  <React.Fragment key={title}>
    <h2>large {title}</h2>
    <div className={`mb-4 ${CONTAINER_WIDTH}`}>
      <Input {...props} />
    </div>
  </React.Fragment>
))}
src/components/common/Header.tsx (1)

96-135: 접근성 개선 필요

NavLinks 컴포넌트의 접근성을 개선할 필요가 있습니다. 현재 구현에는 ARIA 레이블이나 역할이 정의되어 있지 않습니다.

다음과 같이 수정하는 것을 제안합니다:

     <ul
+      role="navigation"
+      aria-label="메인 메뉴"
       className={`${!isMobile ? 'hidden items-center text-Cgray700 lg:flex' : 'text-Cgray400'}`}
     >
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 048c040 and 2a76416.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • src/assets/icon/logo.svg is excluded by !**/*.svg
  • src/assets/icon/profile.svg is excluded by !**/*.svg
  • src/fonts/PretendardVariable.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (12)
  • .coderabbit.yaml (0 hunks)
  • next.config.mjs (1 hunks)
  • package.json (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/app/preview/dropdown/page.tsx (1 hunks)
  • src/app/preview/gnb/page.tsx (1 hunks)
  • src/app/preview/input/page.tsx (1 hunks)
  • src/components/common/Header.tsx (1 hunks)
  • src/components/ui/Input.tsx (1 hunks)
  • src/types/global.d.ts (1 hunks)
  • tailwind.config.ts (3 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • .coderabbit.yaml
🧰 Additional context used
🪛 GitHub Check: check
src/components/common/Header.tsx

[warning] 30-30:
Unexpected console statement


[warning] 69-69:
Unexpected console statement

🔇 Additional comments (6)
src/types/global.d.ts (1)

1-5: SVG 모듈 선언이 적절하게 구현되었습니다!

React 컴포넌트로 SVG를 임포트할 수 있도록 타입이 정확하게 정의되어 있습니다.

next.config.mjs (1)

2-10: Webpack 설정이 적절하게 구현되었습니다!

SVG 파일을 React 컴포넌트로 처리하기 위한 webpack 설정이 정확하게 구현되어 있습니다.

src/app/layout.tsx (1)

12-17: 폰트 설정이 적절하게 구현되었습니다!

Pretendard 폰트의 variable font 설정과 display swap 옵션이 성능 최적화를 위해 올바르게 구성되어 있습니다.

tailwind.config.ts (1)

13-16: 브레이크포인트 값을 검토해주세요.

현재 설정된 브레이크포인트 값이 일반적인 모바일/태블릿 기준과 다릅니다:

  • md: 376px (일반적으로 768px)
  • lg: 745px (일반적으로 1024px)

디자인 시스템과 일치하는지 확인이 필요합니다.

tsconfig.json (1)

34-40: 설정이 적절해 보입니다!

src 디렉토리를 포함하도록 설정을 추가한 것이 적절합니다. TypeScript 컴파일러가 올바르게 소스 파일을 찾을 수 있게 됩니다.

package.json (1)

17-18:

Details

✅ Verification successful

의존성 추가가 적절해 보입니다!

@svgr/webpack과 @radix-ui/react-slot의 추가가 적절합니다. 다만, 보안을 위해 최신 버전인지 확인이 필요합니다.


🌐 Web query:

What is the latest version of @svgr/webpack and @radix-ui/react-slot?

💡 Result:

The latest versions of the packages based on current information are:

@svgr/webpack: Version 8.1.0
@radix-ui/react-slot: Version 1.1.2

Details:

  • @svgr/webpack: The npm registry lists @svgr/webpack at v8.1.0 as its latest stable release[10]. This aligns with usage examples in projects like Gatsby[7].
  • @radix-ui/react-slot: The npm registry shows v1.1.2 as the latest stable release (published 11 days ago)[2][8]. Projects like PasteBarApp also use this version[3].

For production environments, ensure compatibility with peer dependencies like Webpack (v5+ for @svgr/webpack)[1][7].

Citations:


의존성 버전 검증 완료 – 모든 것이 최신 상태입니다.

  • 현재 사용 중인 버전:
    • @svgr/webpack: ^8.1.0
    • @radix-ui/react-slot: ^1.1.2

웹 상의 최신 정보와 일치하므로, 해당 의존성 추가는 보안 및 기능 면에서 적절합니다.

export default function Home() {
return (
<div className="mt-40">
<Header isLogIn />
Copy link

@coderabbitai coderabbitai bot Feb 19, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

isLogIn prop의 상태 관리 개선이 필요합니다.

하드코딩된 isLogIn 값은 실제 로그인 상태와 동기화되지 않을 수 있습니다. 전역 상태 관리나 Context를 통해 실제 로그인 상태를 관리하는 것이 좋습니다.

-      <Header isLogIn />
+      <Header isLogIn={useAuth().isLoggedIn} />

Copy link
Contributor Author

Choose a reason for hiding this comment

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

로그인 구현 후 추후 연동 예정

Copy link

Choose a reason for hiding this comment

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

@dbswl701 알겠습니다. 로그인 기능 구현 후 연동하시면 되겠습니다.


🧠 Learnings used
Learnt from: dbswl701
PR: MoimService/Moim-FE#13
File: src/app/preview/gnb/page.tsx:8-8
Timestamp: 2025-02-18T07:37:44.563Z
Learning: The hardcoded `isLogIn` prop in the GNB preview page (`src/app/preview/gnb/page.tsx`) is temporary and will be replaced with actual authentication state when login functionality is implemented.

<body>{children}</body>
<html lang="ko" className={pretendard.variable}>
<body>
<Header />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Header 컴포넌트의 props 검토가 필요합니다.

layout에서 사용되는 Header 컴포넌트에 isLogIn prop이 전달되지 않았습니다. 이는 preview/gnb 페이지의 구현과 일관성이 없습니다.

-        <Header />
+        <Header isLogIn={useAuth().isLoggedIn} />
📝 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 />
<Header isLogIn={useAuth().isLoggedIn} />


export default function Home() {
const [selectedFilter, setSelectedFilter] = useState<string | null>(null);
const [selectedFilter] = useState<string | null>(null);
Copy link

@coderabbitai coderabbitai bot Feb 19, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

상태 업데이트 로직이 누락되었습니다.

selectedFilter의 상태를 업데이트할 수 있는 setSelectedFilter 함수가 제거되어 필터 기능이 동작하지 않을 수 있습니다.

다음과 같이 수정하는 것을 제안합니다:

-const [selectedFilter] = useState<string | null>(null);
+const [selectedFilter, setSelectedFilter] = useState<string | null>(null);
📝 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
const [selectedFilter] = useState<string | null>(null);
const [selectedFilter, setSelectedFilter] = useState<string | null>(null);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

불필요한 로직이라 판단

Copy link

Choose a reason for hiding this comment

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

@dbswl701 네, 이해했습니다. preview 페이지에서 실제 필터링 기능이 구현되지 않아 상태 업데이트가 불필요하다는 판단이 맞습니다.


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/app/preview/dropdown/page.tsx:0-0
Timestamp: 2025-02-19T02:36:43.049Z
Learning: In preview/example pages where components are only displayed for demonstration purposes without actual functionality, state update functions can be omitted if they're not being used to update the UI.

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.

Comment on lines 116 to 126
<button
onClick={() => setIsVisible((prev) => !prev)}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}
`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성 개선이 필요합니다.

비밀번호 표시/숨기기 버튼에 대한 접근성 개선이 필요합니다:

다음과 같이 수정하는 것을 제안합니다:

 <button
   onClick={() => setIsVisible((prev) => !prev)}
+  type="button"
+  aria-label={isVisible ? "비밀번호 숨기기" : "비밀번호 표시"}
   className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}`}
 >
📝 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
<button
onClick={() => setIsVisible((prev) => !prev)}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}
`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>
<button
onClick={() => setIsVisible((prev) => !prev)}
+ type="button"
+ aria-label={isVisible ? "비밀번호 숨기기" : "비밀번호 표시"}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>

Comment on lines 104 to 115
<input
type={
type === 'password' ? (isVisible ? 'text' : 'password') : 'text'
}
className={cn(
inputVariants({ state, inputSize }),
!!errorMessage &&
'border border-warning text-warning caret-warning',
)}
ref={ref}
{...props}
/>
Copy link

@coderabbitai coderabbitai bot Feb 19, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

입력 필드의 접근성을 개선해야 합니다.

입력 필드에 aria-invalidaria-describedby 속성이 누락되었습니다.

다음과 같이 수정하는 것을 제안합니다:

 <input
   type={type === 'password' ? (isVisible ? 'text' : 'password') : 'text'}
+  aria-invalid={state === 'error' || !!errorMessage}
+  aria-describedby={errorMessage ? `${props.id}-error` : undefined}
   className={cn(
     inputVariants({ state, inputSize }),
     !!errorMessage && 'border border-warning text-warning caret-warning',
   )}
   ref={ref}
   {...props}
 />

그리고 에러 메시지에 ID를 추가합니다:

 {errorMessage && (
-  <p className={cn(errorTextVariants({ inputSize, className }))}>
+  <p id={`${props.id}-error`} className={cn(errorTextVariants({ inputSize, className }))}>
     {errorMessage}
   </p>
 )}
📝 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
<input
type={
type === 'password' ? (isVisible ? 'text' : 'password') : 'text'
}
className={cn(
inputVariants({ state, inputSize }),
!!errorMessage &&
'border border-warning text-warning caret-warning',
)}
ref={ref}
{...props}
/>
<input
type={
type === 'password' ? (isVisible ? 'text' : 'password') : 'text'
}
aria-invalid={state === 'error' || !!errorMessage}
aria-describedby={errorMessage ? `${props.id}-error` : undefined}
className={cn(
inputVariants({ state, inputSize }),
!!errorMessage &&
'border border-warning text-warning caret-warning',
)}
ref={ref}
{...props}
/>
{errorMessage && (
<p id={`${props.id}-error`} className={cn(errorTextVariants({ inputSize, className }))}>
{errorMessage}
</p>
)}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

aria 라벨 중에서 label, invalid, describedby 는 어떤 역할을 하기에 웹 접근성을 위한 속성이라는거야?

Copy link

Choose a reason for hiding this comment

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

@dbswl701 각 ARIA 속성의 역할을 설명해드리겠습니다:

  1. aria-label: 시각적으로 보이지 않는 요소에 대한 설명을 제공합니다. 예를 들어, 아이콘 버튼에 "비밀번호 보기" 같은 레이블을 제공할 수 있습니다.

  2. aria-invalid: 입력값이 유효하지 않음을 스크린 리더에 알립니다. 에러 상태일 때 스크린 리더가 "유효하지 않은 입력"이라고 읽어줍니다.

  3. aria-describedby: 입력 필드와 관련된 추가 설명(예: 에러 메시지)을 연결합니다. 스크린 리더가 입력 필드에 포커스될 때 연결된 설명을 함께 읽어줍니다.

이러한 속성들은 시각 장애인이 스크린 리더를 통해 웹 페이지를 더 잘 이해하고 사용할 수 있도록 도와줍니다.

(`・ω・´)ゞ


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:38:34.072Z
Learning: ARIA 속성들의 주요 역할:
- aria-label: 시각적으로 보이지 않는 요소에 대한 설명 제공
- aria-invalid: 입력값의 유효성 상태를 스크린 리더에 전달
- aria-describedby: 입력 필드와 관련 설명(에러 메시지 등)을 연결하여 스크린 리더가 함께 읽도록 함

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

에러 메시지에 id를 추가하는 이유가 뭐야?

Copy link

Choose a reason for hiding this comment

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

@dbswl701 에러 메시지에 ID를 추가하는 이유는 aria-describedby 속성과 함께 작동하기 위해서입니다. 입력 필드의 aria-describedby가 에러 메시지의 ID를 참조하면, 스크린 리더는 이 두 요소가 연결되어 있다는 것을 인식합니다. 그래서 사용자가 입력 필드에 포커스할 때, 스크린 리더가 입력 필드와 함께 관련된 에러 메시지도 읽어줄 수 있게 됩니다.


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:43:36.851Z
Learning: HTML 접근성에서 `aria-describedby`와 요소 ID의 관계:
- 에러 메시지와 같은 설명 텍스트에는 고유한 ID가 필요함
- 입력 필드의 `aria-describedby` 속성은 이 ID를 참조하여 스크린 리더가 두 요소를 연결하여 읽도록 함

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

그럼 id를 props로 꼭 받아야하는거네?

Copy link

Choose a reason for hiding this comment

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

@dbswl701 네, 접근성을 위해서는 id가 필요합니다. 하지만 컴포넌트를 더 사용하기 쉽게 만들기 위해, id가 제공되지 않았을 때는 내부적으로 고유한 ID를 생성하도록 개선할 수 있습니다. 예를 들어:

const Input = React.forwardRef<HTMLInputElement, IInputProps>(
  ({ className, type, state = 'default', errorMessage, inputSize = 'l', id, ...props }, ref) => {
    const [isVisible, setIsVisible] = React.useState(false);
    const uniqueId = React.useId(); // 내부적으로 고유한 ID 생성
    const inputId = id || uniqueId;

    return (
      <div className={`w-full ${className}`}>
        <div className="relative focus-within:text-Cgray700">
          <input
            id={inputId}
            type={type === 'password' ? (isVisible ? 'text' : 'password') : 'text'}
            aria-invalid={state === 'error' || !!errorMessage}
            aria-describedby={errorMessage ? `${inputId}-error` : undefined}
            // ... 나머지 코드
          />
          {errorMessage && (
            <p id={`${inputId}-error`} className={cn(errorTextVariants({ inputSize, className }))}>
              {errorMessage}
            </p>
          )}
        </div>
      </div>
    );
  }
);

✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:46:56.222Z
Learning: Input 컴포넌트에서 접근성을 위한 ID 처리:
- 에러 메시지와 입력 필드를 연결하기 위해 ID가 필요함
- ID가 제공되지 않은 경우 React.useId()를 사용하여 내부적으로 고유한 ID를 생성할 수 있음

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.

const menu = [
{ label: '내 모임', onSelect: () => router.push('/my-meeting') },
{ label: '마이페이지', onSelect: () => router.push('/my-page') },
{ label: '로그아웃', onSelect: () => console.log('로그아웃') },
Copy link

@coderabbitai coderabbitai bot Feb 19, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

콘솔 로그 제거 필요

프로덕션 환경에서 콘솔 로그를 사용하는 것은 바람직하지 않습니다. 로그아웃 기능 구현 시 실제 로그아웃 로직으로 대체해야 합니다.

다음과 같이 수정하는 것을 제안합니다:

-    { label: '로그아웃', onSelect: () => console.log('로그아웃') },
+    { label: '로그아웃', onSelect: () => handleLogout() },
-          onClick={() => console.log('로그아웃')}
+          onClick={handleLogout}

Also applies to: 69-69

🧰 Tools
🪛 GitHub Check: check

[warning] 30-30:
Unexpected console statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

임시로 작성한 내용. 추구 제거 예정

Copy link

Choose a reason for hiding this comment

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

@dbswl701 알겠습니다. 추후 로그아웃 기능 구현 시 제거하시면 되겠습니다.


✏️ Learnings added
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/common/Header.tsx:0-0
Timestamp: 2025-02-19T02:47:47.477Z
Learning: The console.log statements in Header.tsx for logout functionality are temporary placeholders that will be replaced with actual logout logic in a future update.

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.

<div className="flex items-center">
<Dropdown items={menu} trigger={<Profile />} />
<span className="typo-head3 m-auto w-[77px] text-center text-white">
김밤식
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

하드코딩된 사용자 이름

사용자 이름이 하드코딩되어 있습니다. 실제 사용자 정보를 props로 전달받아 사용하도록 수정해야 합니다.

다음과 같이 수정하는 것을 제안합니다:

-          <span className="typo-head3 m-auto w-[77px] text-center text-white">
-            김밤식
-          </span>
+          <span className="typo-head3 m-auto w-[77px] text-center text-white">
+            {userName}
+          </span>

Also applies to: 76-76

<Logo />
</Link>
<NavLinks />
{isLogIn ? <BeforeLogin /> : <AfterLogin />}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

로그인 상태 렌더링 로직 오류

isLogIn prop의 사용이 반대로 되어 있습니다. 현재 로직은 로그인된 상태에서 BeforeLogin 컴포넌트를, 로그아웃된 상태에서 AfterLogin 컴포넌트를 보여주고 있습니다.

다음과 같이 수정하는 것을 제안합니다:

-          {isLogIn ? <BeforeLogin /> : <AfterLogin />}
+          {isLogIn ? <AfterLogin /> : <BeforeLogin />}
📝 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
{isLogIn ? <BeforeLogin /> : <AfterLogin />}
{isLogIn ? <AfterLogin /> : <BeforeLogin />}

@dbswl701 dbswl701 force-pushed the feat/component/input/DEVING-34 branch from 2a76416 to 344177e Compare February 19, 2025 02:21
@vercel vercel bot temporarily deployed to Preview – moim-fe February 19, 2025 02:22 Inactive
@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ✅ success
  • Tests: ✅ success
  • Build: ✅ success

✅ 모든 검사가 통과되었습니다.

Copy link

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

♻️ Duplicate comments (1)
src/components/ui/Input.tsx (1)

104-126: ⚠️ Potential issue

접근성 개선이 여전히 필요합니다.

입력 필드와 비밀번호 표시/숨기기 버튼에 대한 접근성 속성이 누락되어 있습니다.

이전 리뷰에서 제안된 다음과 같은 수정사항을 적용해주세요:

  1. 입력 필드에 aria-invalidaria-describedby 속성 추가
  2. 비밀번호 표시/숨기기 버튼에 type="button"aria-label 속성 추가
🧹 Nitpick comments (1)
src/components/ui/Input.tsx (1)

8-28: 타입 안전성 개선이 필요합니다.

stateinputSize 속성에 대해 리터럴 유니온 타입을 별도로 정의하면 코드의 재사용성과 유지보수성이 향상될 것 같습니다.

다음과 같이 수정하는 것을 제안합니다:

+type InputState = 'default' | 'success' | 'error';
+type InputSize = 'l' | 's';
+
 interface IInputProps
   extends React.InputHTMLAttributes<HTMLInputElement>,
     VariantProps<typeof inputVariants> {
   /**
    * 입력 필드의 상태 (기본값: `'default'`)
    * - `'default'` : 기본 상태 (테두리 없음)
    * - `'success'` : 성공 상태 (메인 컬러 테두리 적용)
    * - `'error'` : 에러 상태 (경고 컬러 테두리 및 텍스트 적용)
    */
-  state?: 'default' | 'success' | 'error';
+  state?: InputState;
   /**
    * 입력 필드의 크기 (기본값: `'l'`)
    * - `'s'` : 작은 크기 (36px)
    * - `'l'` : 기본 크기 (50px)
    */
-  inputSize?: 'l' | 's';
+  inputSize?: InputSize;
   /**
    * 에러 메시지 (에러 상태일 때 표시)
    */
   errorMessage?: string;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a76416 and 344177e.

📒 Files selected for processing (1)
  • src/components/ui/Input.tsx (1 hunks)

Comment on lines +50 to +60
const errorTextVariants = cva('px-[10px] text-warning', {
variants: {
inputSize: {
s: 'typo-caption2 mt-[8px]',
l: 'typo-caption1 mt-[10px]',
},
defaultVariants: {
inputSize: 'l',
},
},
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

오류 텍스트 스타일의 기본값 설정에 버그가 있습니다.

errorTextVariantsdefaultVariants 설정이 잘못된 위치에 있습니다. 현재는 variants 객체 내부에 있어 작동하지 않습니다.

다음과 같이 수정하는 것을 제안합니다:

 const errorTextVariants = cva('px-[10px] text-warning', {
   variants: {
     inputSize: {
       s: 'typo-caption2 mt-[8px]',
       l: 'typo-caption1 mt-[10px]',
     },
-    defaultVariants: {
-      inputSize: 'l',
-    },
   },
+  defaultVariants: {
+    inputSize: 'l',
+  },
 });
📝 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
const errorTextVariants = cva('px-[10px] text-warning', {
variants: {
inputSize: {
s: 'typo-caption2 mt-[8px]',
l: 'typo-caption1 mt-[10px]',
},
defaultVariants: {
inputSize: 'l',
},
},
});
const errorTextVariants = cva('px-[10px] text-warning', {
variants: {
inputSize: {
s: 'typo-caption2 mt-[8px]',
l: 'typo-caption1 mt-[10px]',
},
},
defaultVariants: {
inputSize: 'l',
},
});

Comment on lines +108 to +112
className={cn(
inputVariants({ state, inputSize }),
!!errorMessage &&
'border border-warning text-warning caret-warning',
)}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 상태 처리 로직이 중복됩니다.

state가 'error'일 때와 errorMessage가 있을 때 모두 동일한 에러 스타일이 적용되고 있습니다. 이는 일관성 없는 상태를 초래할 수 있습니다.

다음과 같이 수정하는 것을 제안합니다:

 className={cn(
-  inputVariants({ state, inputSize }),
-  !!errorMessage &&
-    'border border-warning text-warning caret-warning',
+  inputVariants({ 
+    state: !!errorMessage ? 'error' : state,
+    inputSize 
+  }),
 )}
📝 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
className={cn(
inputVariants({ state, inputSize }),
!!errorMessage &&
'border border-warning text-warning caret-warning',
)}
className={cn(
inputVariants({
state: !!errorMessage ? 'error' : state,
inputSize
}),
)}

clyde-yoonjae
clyde-yoonjae previously approved these changes Feb 19, 2025
@github-actions
Copy link

CI Status Report

검사 결과

  • Lint: ✅ success
  • Format: ✅ success
  • Type Check: ✅ success
  • Tests: ✅ success
  • Build: ✅ success

✅ 모든 검사가 통과되었습니다.

Copy link

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

♻️ Duplicate comments (1)
src/components/ui/Input.tsx (1)

104-116: ⚠️ Potential issue

ARIA 속성 설정이 잘못되었습니다.

  1. aria-describedby가 입력 필드 대신 버튼에 설정되어 있습니다.
  2. 에러 상태 처리가 중복됩니다.
 <input
   type={type === 'password' ? (isVisible ? 'text' : 'password') : 'text'}
   aria-invalid={!!errorMessage}
+  aria-describedby={errorMessage ? `${id}-error` : undefined}
   className={cn(
-    inputVariants({ state, inputSize }),
-    !!errorMessage && 'border border-warning text-warning caret-warning',
+    inputVariants({ 
+      state: errorMessage ? 'error' : state,
+      inputSize 
+    }),
   )}
   ref={ref}
   {...props}
 />
🧹 Nitpick comments (1)
src/components/ui/Input.tsx (1)

8-27: 인터페이스 정의를 개선해야 합니다.

다음과 같은 개선사항을 제안합니다:

  1. state 타입에 'error' 상태가 누락되었습니다 (예제에서 사용됨).
  2. inputSize 대신 size를 사용하면 더 간결할 것 같습니다.
-  state?: 'default' | 'success';
+  state?: 'default' | 'success' | 'error';

-  inputSize?: 'l' | 's';
+  size?: 'l' | 's';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 344177e and ee0b644.

📒 Files selected for processing (1)
  • src/components/ui/Input.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
src/components/ui/Input.tsx (3)
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:46:56.460Z
Learning: Input 컴포넌트에서 접근성을 위한 ID 처리:
- 에러 메시지와 입력 필드를 연결하기 위해 ID가 필요함
- ID가 제공되지 않은 경우 React.useId()를 사용하여 내부적으로 고유한 ID를 생성할 수 있음
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:43:36.967Z
Learning: HTML 접근성에서 `aria-describedby`와 요소 ID의 관계:
- 에러 메시지와 같은 설명 텍스트에는 고유한 ID가 필요함
- 입력 필드의 `aria-describedby` 속성은 이 ID를 참조하여 스크린 리더가 두 요소를 연결하여 읽도록 함
Learnt from: dbswl701
PR: MoimService/Moim-FE#15
File: src/components/ui/Input.tsx:104-115
Timestamp: 2025-02-19T02:38:34.203Z
Learning: ARIA 속성들의 주요 역할:
- aria-label: 시각적으로 보이지 않는 요소에 대한 설명 제공
- aria-invalid: 입력값의 유효성 상태를 스크린 리더에 전달
- aria-describedby: 입력 필드와 관련 설명(에러 메시지 등)을 연결하여 스크린 리더가 함께 읽도록 함
🔇 Additional comments (1)
src/components/ui/Input.tsx (1)

48-58: 오류 텍스트 스타일의 기본값 설정에 버그가 있습니다.

errorTextVariantsdefaultVariants 설정이 잘못된 위치에 있어 작동하지 않습니다.

Comment on lines +117 to +130
<button
onClick={() => setIsVisible((prev) => !prev)}
type="button"
aria-label={isVisible ? '비밀번호 숨기기' : '비밀번호 표시'}
aria-describedby={errorMessage ? `${id}-error` : undefined}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}
`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

비밀번호 토글 버튼의 ARIA 속성을 수정해야 합니다.

aria-describedby가 버튼에서 제거되어야 합니다.

 <button
   onClick={() => setIsVisible((prev) => !prev)}
   type="button"
   aria-label={isVisible ? '비밀번호 숨기기' : '비밀번호 표시'}
-  aria-describedby={errorMessage ? `${id}-error` : undefined}
   className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${
     type !== 'password' && 'hidden'
   }`}
 >
📝 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
<button
onClick={() => setIsVisible((prev) => !prev)}
type="button"
aria-label={isVisible ? '비밀번호 숨기기' : '비밀번호 표시'}
aria-describedby={errorMessage ? `${id}-error` : undefined}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}
`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>
<button
onClick={() => setIsVisible((prev) => !prev)}
type="button"
aria-label={isVisible ? '비밀번호 숨기기' : '비밀번호 표시'}
className={`absolute right-[16px] top-1/2 -translate-y-1/2 text-Cgray500 focus-within:text-Cgray700 ${type !== 'password' && 'hidden'}
`}
>
{!isVisible ? (
<EyeOff className={`size-5 `} />
) : (
<Eye className={`size-5 `} />
)}
</button>

Comment on lines +86 to +98
const Input = React.forwardRef<HTMLInputElement, IInputProps>(
(
{
className,
type,
state = 'default',
errorMessage,
inputSize = 'l',
id,
...props
},
ref,
) => {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

접근성을 위한 ID 처리를 개선해야 합니다.

id가 필수적으로 필요하지만 제공되지 않았을 때의 처리가 누락되었습니다.

 const Input = React.forwardRef<HTMLInputElement, IInputProps>(
   (
     {
       className,
       type,
       state = 'default',
       errorMessage,
       inputSize = 'l',
-      id,
+      id: providedId,
       ...props
     },
     ref,
   ) => {
+    const uniqueId = React.useId();
+    const id = providedId || uniqueId;
📝 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
const Input = React.forwardRef<HTMLInputElement, IInputProps>(
(
{
className,
type,
state = 'default',
errorMessage,
inputSize = 'l',
id,
...props
},
ref,
) => {
const Input = React.forwardRef<HTMLInputElement, IInputProps>(
(
{
className,
type,
state = 'default',
errorMessage,
inputSize = 'l',
id: providedId,
...props
},
ref,
) => {
const uniqueId = React.useId();
const id = providedId || uniqueId;
// ... rest of the component implementation

@dbswl701 dbswl701 merged commit eb63f02 into feat/component/DEVING-9 Feb 19, 2025
8 checks passed
@dbswl701 dbswl701 deleted the feat/component/input/DEVING-34 branch February 19, 2025 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants