Skip to content

Conversation

@Insung-Jo
Copy link

@Insung-Jo Insung-Jo commented Jun 11, 2025

📌 변경 사항 개요

  • Input 컴포넌트 구현

✨ 요약

  • Input 컴포넌트 구현
  • 일부 커스텀 클래스가 추가 되었습니다.
  • 테스트 페이지는 AI로 간단하게 작성 되어 있으니 동작이나 react-hook-form에 대한 사용법을 확인할 수 있습니다. (어떤 건지 궁금하시면 리뷰로 남겨주세용)

📝 상세 내용

  • Text-error, Border-error 커스텀 클래스를 추가하였습니다.

props

  • props는 아래 표와 같이 넘기고 있습니다!
  • ref...rest는 react-hook-form과 관련이 있기 때문에 아래에 자세하게 작성했습니다!
Props Required (입력 필수 여부) 연결 지점들 Description
labelName <label>{labelName}</label> 인풋 상단 라벨 텍스트
name ✅(register 사용 시 입력 X) <input name={name} />, label htmlFor={name}, register() 식별자 폼 필드 식별자
type ❌ (기본값 존재) <input type={type} /> 기본값 text, password 사용시 비밀번호 보기 토글 지원
autoComplete <input autoComplete={autoComplete} /> 브라우저 자동완성 설정 (미지정 시 기본적으로 자동완성 활성화됨)
placeholder <input placeholder={placeholder} /> placeholder 텍스트
hasError className 제어 에러 여부 (border 색상 변경)
errorMessage <p>{errorMessage}</p> 에러 발생 시 출력 메시지
ref ✅ (forwardRef가 자동 처리) <input ref={ref} /> react-hook-form 연결 (DOM 접근용)
...rest ✅ (register spread 시 필수) onChange, onBlur, value, required react-hook-form이 넘기는 이벤트 핸들러 및 값
  • 에러 처리가 없을 경우
<Input
  labelName="닉네임"
  placeholder="닉네임 입력"
/>
  • 별도의 에러 처리가 있을 경우 ex)이메일
      <Input
        labelName="이메일"
        type="email"
        placeholder="이메일 입력"
        autoComplete="email"
        hasError={!!errors.email}
        errorMessage={errors.email?.message}
        {...register("email", { // 이 부분에서 name, ref가 전달 됩니다.
          required: "이메일은 필수 입력입니다.",
          pattern: {
            value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
            message: "유효한 이메일을 입력해주세요."
          }
        })}
      />
  • ...register는 기존 react-hook-form 사용법과 동일 합니다.
  • 자세한 동작은 /auth에서 확인 가능합니다.

발생했던 문제점

이번에 구현하면서 react-hook-form을 사용하는 것을 고려 했을 때 발생한 상황을 말씀드리겠습니다.

요약

  1. ref를 넘기는 이유 (forwardRef)
  • react-hook-form이 DOM 접근을 위해 ref를 사용하므로 꼭 필요합니다.
  • ref는 일반 props처럼 전달되지 않아 forwardRef로 명시적으로 넘겨줘야 합니다.
  1. ...rest를 사용하는 이유
  • register()가 반환하는 onChange, onBlur, name, value 등을 한 번에 전달하기 위해 사용합니다.

1. ref를 넘기는 이유?(forwardRef) [공식 문서 (v19 부터 지원 종료)]

react-hook-form 에서는 내부적으로 DOM 노드에 직접 접근을 하게 되는데 이를 위해 register()에서 ref를 넘겨줘야 해당 컴포넌트가 정상적으로 동작하게 됩니다. 그래서 props로 넘겨줄 수 있지 않을까? 라는 생각을 할 수 있지만 ref는 다르게 동작합니다.
ref는 React에서 특별히 처리하는 예약 속성으로 React가 별도로 관리하기 때문에 컴포넌트에서 자동으로 전달 되지 않는다고 합니다!

function MyInput(props: any) {
  return <input {...props} />;
}

<MyInput ref={myRef} /> // 호출 시 값 전달 X

그래서 해당 문제를 해결하기 위해 나온 것이 forwardRef()라는 커스텀 훅입니다. 해당 훅은 ref를 props 처럼 전달할 수 있는 역할을 해주는 훅입니다. v19에서는 해당 문제가 해결 되면서 사용이 중지 되었지만 지금 사용하는 버전은 v18이기 때문에 해당 훅을 사용하여 ref를 넘기게 되었습니다. (19 버전 부터는 props로 넘기는 것이 가능해졌습니다.)

// 사용 예시
const Input = forwardRef<HTMLInputElement, CustomInputProps>(
  function Input(props, ref) {
//...
)

Input.displayName = 'Input // 개발자 도구로 열었을 때 보이게 되는 이름

2. ...rest를 사용한 이유 [참고 문서]

react-hook-form에 register()에서 반환 되는 값은 onChange, onBlur, ref, name, value 등의 이벤트 헨들러와 값이 포함이 되어 있습니다. 그렇기 때문에 해당 값들을 그대로 전달해주어야 react-hook-form이 정상적으로 동작하게 됩니다.

06-12 추가

공식 문서를 살펴 보니 내부적으로도 이미 타입이 지정이 되어 있어서 따로 타입을 명시하지 않아도 되는 것을 확인했습니다.
내부 코드(1094줄)

🔗 관련 이슈

#38

🖼️ 스크린샷

image
image

✅ 체크리스트

  • 브랜치 네이밍 컨벤션을 준수했습니다
  • 커밋 컨벤션을 준수했습니다
  • 코드가 프로젝트의 스타일 가이드라인을 준수합니다

💡 참고 사항

  • 로그인/회원가입 말고도 다른 부분에서도 사용하는 곳이 있었기 때문에 공통으로 만들게 되었습니다. 하지만 모든 Input을 고려 하지 않았기 때문에 이 부분에서 추가 하고 싶으신 것이 있을 경우에는 자유롭게 말씀해주세요!
  • JSDoc는 추후에 작성해서 올리도록 하겠습니다.
  • 참고 사항은 아니지만 확실히 라이브러리도 고민 상황에 있다 보니까 작성하는데 생각보다 고민하는데 있어서 구현이 좀 늦춰진 거 같습니다.

Summary by CodeRabbit

  • New Features

    • 이메일과 비밀번호 입력 필드를 포함한 사용자 인증 폼이 추가되었습니다.
    • 입력값 유효성 검사 및 오류 메시지 표시 기능이 도입되었습니다.
    • 비밀번호 입력 시 가시성 토글 기능이 제공됩니다.
  • Style

    • 오류 상태를 위한 텍스트 및 테두리 색상 유틸리티 클래스가 추가되었습니다.

@Insung-Jo Insung-Jo added this to the 1차 구현 기간 milestone Jun 11, 2025
@Insung-Jo Insung-Jo self-assigned this Jun 11, 2025
@Insung-Jo Insung-Jo added the ✨Feat 기능 개발 label Jun 11, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 11, 2025

"""

Walkthrough

사용자 인증 폼과 커스텀 Input 컴포넌트가 새롭게 추가되었습니다. 이메일과 비밀번호 입력 및 유효성 검증을 지원하며, 전역 CSS에 에러 스타일 클래스가 도입되었습니다. 비밀번호 입력란은 가시성 토글 기능을 포함합니다.

Changes

파일/경로 변경 요약
src/app/auth/page.tsx 이메일 및 비밀번호 입력, 유효성 검사, 제출 로그 기능의 인증 폼 컴포넌트 추가
src/app/shared/components/Input.tsx ref 전달 지원, 에러 상태, 비밀번호 가시성 토글, 커스텀 props의 Input 컴포넌트 신설
src/app/globals.css 에러 텍스트(.Text-error), 에러 테두리(.Border-error) 스타일 클래스 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MyForm
    participant Input
    User->>MyForm: 이메일/비밀번호 입력
    MyForm->>Input: 입력값 전달 및 에러 상태 표시
    Input-->>MyForm: 입력값/에러 반환
    User->>MyForm: 폼 제출
    MyForm->>MyForm: 유효성 검사
    alt 유효성 통과
        MyForm->>Console: 데이터 로그 출력
    else 에러 발생
        MyForm->>Input: 에러 메시지 표시
    end
Loading

Possibly related issues

Poem

🐇
새로운 입력창에 봄바람 솔솔,
에러는 붉게, 비밀번호는 살짝 볼 수 있죠.
폼은 단단히, 유효성도 꼼꼼히,
로그인 길목에 토끼가 춤춰요!

(입력창 너머로, 변화의 발자국—똑똑!)

"""

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-11T19_13_38_357Z-debug-0.log


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e631ab6 and 611e1ce.

📒 Files selected for processing (1)
  • src/app/shared/components/Input.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/shared/components/Input.tsx
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @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.

@Insung-Jo Insung-Jo added the 🎨Style UI, 스타일 관련 수정 label Jun 11, 2025
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: 4

🧹 Nitpick comments (1)
src/app/globals.css (1)

34-39: 다크 모드 대응 누락

.Text-error, .Border-error는 밝은 테마 색상만 지정돼 있습니다. 다크 모드에서도 일관된 접근성을 유지하려면 dark: 변형을 추가해주세요.

 .Text-error {
-  @apply text-[#D6173A];
+  @apply text-[#D6173A] dark:text-[#FF667F];
 }
 .Border-error {
-  @apply border border-[#D6173A];
+  @apply border border-[#D6173A] dark:border-[#FF667F];
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 70eba7b and e631ab6.

⛔ Files ignored due to path filters (2)
  • public/images/visibility-off.svg is excluded by !**/*.svg
  • public/images/visibility-on.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • src/app/auth/page.tsx (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/shared/components/Input.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/app/shared/components/Input.tsx (1)
src/app/shared/lib/cn.ts (1)
  • cn (4-6)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: eslint-check
🔇 Additional comments (1)
src/app/shared/components/Input.tsx (1)

31-34: 비밀번호 토글 시 type 결정 로직 단순화 가능

불필요한 삼항 중첩 대신 inputType 계산식을 분리하면 가독성이 향상됩니다. (선택 사항)

Comment on lines +20 to +22
const onSubmit = (data: FormData) => {
console.log(data)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

민감 데이터 console.log 출력 자제

비밀번호가 그대로 브라우저 콘솔에 노출됩니다. 배포 전 반드시 제거하거나 서버 전송 로직으로 교체하세요.

🤖 Prompt for AI Agents
In src/app/auth/page.tsx around lines 20 to 22, the onSubmit function currently
logs the entire form data including sensitive information like passwords to the
browser console. Remove the console.log statement to prevent exposing sensitive
data, and replace it with logic that securely sends the form data to the server
for processing.

Comment on lines +32 to +41
{...register('email', {
required: '이메일을 입력해 주세요.',
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: '유효한 이메일 주소를 입력해주세요',
},
})}
hasError={touchedFields.email && !!errors.email}
errorMessage={touchedFields.email ? errors.email?.message : ''}
/>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

에러 표시 조건이 제출 후에는 작동하지 않을 수 있음

touchedFields만 의존하면, 사용자가 필드를 건드리지 않고 제출했을 때 오류 메시지가 안 보일 수 있습니다.
isSubmitted 또는 errors만을 활용하여 제출 후에도 오류가 표시되도록 조건을 보완하는 것을 권장합니다.

Also applies to: 47-56

🤖 Prompt for AI Agents
In src/app/auth/page.tsx around lines 32 to 41 and similarly in lines 47 to 56,
the error display condition relies solely on touchedFields, which may prevent
error messages from showing after form submission if the user hasn't interacted
with the field. To fix this, update the error display condition to also consider
isSubmitted or directly check errors, ensuring that error messages appear after
submission regardless of field interaction.

Copy link
Contributor

@dkslel1225 dkslel1225 left a comment

Choose a reason for hiding this comment

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

인풋 컴포넌트 구현 수고하셨습니다!!

Copy link
Contributor

Choose a reason for hiding this comment

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

const Input = forwardRef<HTMLInputElement, CustomInputProps>(function Input(props, ref) {...}) 이 코드 구조가,
forwardRef에 컴포넌트를 통째로 넘기고, 호출되는 건 const Input인 건가요?

Copy link
Author

@Insung-Jo Insung-Jo Jun 12, 2025

Choose a reason for hiding this comment

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

네 맞습니다! 공식 문서 내용에서도 확인해보시면

const SomeComponent = forwardRef(render)

이처럼 forwardRef 안에 render 함수를 인자로 받아서 ref를 지원하는새로운 컴포넌트로 반환합니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

처음 알았네용!!

Copy link

@LeeCh0129 LeeCh0129 left a comment

Choose a reason for hiding this comment

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

Input 컴포넌트 구현 수고많으셨어요~ 유효성 검사까지 완벽하게 구현해주셨네요!
특히 react-hook-form의 forwardRef 관련 설명 덕분에 명확하게 이해가 되었네요 👍

@Insung-Jo Insung-Jo merged commit 0b6f504 into develop Jun 12, 2025
2 checks passed
Copy link
Contributor

@yuj2n yuj2n left a comment

Choose a reason for hiding this comment

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

인성님 Input 컴포넌트 구현 수고하셨습니다!!

Copy link
Contributor

Choose a reason for hiding this comment

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

처음 알았네용!!

Comment on lines +41 to +75
{isPassword ? (
<div className="relative w-full">
<input
id={name}
className={cn(
'Text-black h-50 w-full rounded-8 px-16 py-12 text-base font-normal',
hasError ? 'Border-error' : 'Border-btn',
)}
type={inputType}
placeholder={placeholder}
name={name}
autoComplete={autoComplete}
{...rest}
ref={ref}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-16 top-1/2 translate-y-[-50%]"
>
<Image
src={`/images/visibility-${showPassword ? 'on' : 'off'}.svg`}
alt={showPassword ? '비밀번호 보기' : '비밀번호 숨기기'}
width={24}
height={24}
/>
</button>
</div>
) : (
<input
id={name}
className={cn(
'Text-black h-50 w-full rounded-8 px-16 py-12 text-base font-normal',
hasError ? 'Border-error' : 'Border-btn',
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

비밀번호 존재 유무에 따라 조건부 렌더링으로 보여주는 로직이군요!!
이 부분은 조금 코드가 길어서 가독성이 떨어져 보이는데 따로 분리해서 컴포넌트로 불러오는 방식은 어떨까용?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feat 기능 개발 🎨Style UI, 스타일 관련 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants