Skip to content

[Feat] TextField 컴포넌트 제작#21

Merged
YeBeenChoi merged 32 commits into
mainfrom
14-feat/TextField-컴포넌트-제작
Jan 16, 2026

Hidden character warning

The head ref may contain hidden characters: "14-feat/TextField-\ucef4\ud3ec\ub10c\ud2b8-\uc81c\uc791"
Merged

[Feat] TextField 컴포넌트 제작#21
YeBeenChoi merged 32 commits into
mainfrom
14-feat/TextField-컴포넌트-제작

Conversation

@YeBeenChoi

@YeBeenChoi YeBeenChoi commented Jan 14, 2026

Copy link
Copy Markdown
Collaborator

✨ 주요 변경사항

  • TextField 컴포넌트 (Char / TextArea / Price) 구현
  • Playground 예시 페이지에 TextField 상태별 (Default / Error / Disabled) 예제 추가

📝 작업 상세 내용

  • Price Field 숫자 입력 제한 및 포맷 (천 단위 콤마, 원 suffix) 적용
  • TextArea Field 최대 10줄까지 자동 확장, 이후 스크롤 처리

✅ 체크리스트

  • PR 본문에 Close #번호 추가
  • 불필요한 주석, 디버깅 코드 제거
  • 기능 테스트 및 정상 동작 확인
  • 커밋컨벤션 / 코드컨벤션 준수

📸 스크린샷 (선택)

image image

🔍 기타 참고사항

  • Price Field에서 텍스트와 suffix와의 간격은 임의로 30픽셀로 지정해 놓았습니다!

🔗 관련 이슈

@github-actions
github-actions Bot requested a review from jwj0620gcu January 14, 2026 12:20
@github-actions github-actions Bot added the ✨Feature 새로운 기능 label Jan 14, 2026
@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 2MB

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 2MB

@KyeongJooni KyeongJooni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

고생하셨습니다! :) 코멘트 남겼으니까 확인해주시면 좋을 것 같아요 👍

Comment thread src/shared/ui/TextField/TextField.tsx Outdated
Comment on lines +92 to +94
// 표시값(1,000) -> 숫자만(1000)
next = next.replace(/\D/g, '');
// consumer가 e.target.value 읽는 경우를 위해 숫자로 맞춰줌

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

주석을 정리해서 달아주시거나 불필요한 주석은 삭제해도 좋을 것 같아요!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

삭제했습니다!

Comment thread src/shared/ui/TextField/TextField.tsx Outdated
Comment on lines +119 to +121
<textarea
ref={textareaRef}
value={digitsValue}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

digitsValue 라는 변수명이 살짝 모호한거 같아요

const normalizedValue = isPrice ? rawValue.replace(/\D/g, '') : rawValue;

이런식으로 수정하셔도 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

넵 수정했습니다!

Comment on lines +58 to +63
const styles = textFieldVariants({
error,
disabled,
filled,
price: isPrice,
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

현재 variant에 적용되어있는 char는 정의하고 사용은 안하는 걸까요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

다시 확인해 보니까 현재 UI에서는 counter가 header 영역에 있어 char variant가 사용되지 않네요...!! 필요하지 않을 것 같아 제거하였습니다!

Comment on lines +46 to +53
'resize-none',
'text-[16px] leading-[24px] font-medium',
'placeholder:text-[var(--color-gray-300)]',
'hover:border-[var(--color-green-700)]',
'focus:border-[var(--color-green-700)]',
'focus:outline-none',
'transition-colors',
'resize-none',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

중복 정의하신 이유가 있을까요? 없다면 삭제하는게 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

리뷰해 주신 대로 input / textarea에 중복되던 스타일을 fieldBase로 공통화하고, resize-none, text / placeholder / borer / transition 등은 중복 정의 제거하였습니다!

Comment on lines +107 to +114
<div className={styles.labelRow()}>
{label && <label className={styles.label()}>{label}</label>}
{showCounterInHeader && (
<span className={styles.counter()}>
{currentLength}/{maxLen}
</span>
)}
</div>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

char를 정의하고 사용하려고 해도 char가 counter를 absolute로 만드는 속성을 가진 것 같은데 fieldWrapper랑 counter 가 분리가 되어 있는거 같아서 혹시 작동하는 확인 부탁드립니다! 😄

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

위의 답변과 마찬가지로 char variant를 제거하였습니다!

Comment thread src/shared/ui/TextField/TextField.tsx Outdated
if (!isControlled) {
setUncontrolledValue(next);
}
onChange?.(e as React.ChangeEvent<HTMLInputElement>);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

이미 타입을 정의할 때 유니온을 받아서 assertion이 필요가 없을 것 같습니다!

Suggested change
onChange?.(e as React.ChangeEvent<HTMLInputElement>);
onChange?.(e);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다!!

@@ -0,0 +1,149 @@
import { textFieldVariants } from '@shared/ui/TextField/TextField.variants';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { TextFieldProps } from '@shared/ui/TextField/TextField.types';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TextFieldType 은 정의하고 사용 안하고 있는 것 같습니다! 확인해보시면 좋을 것 같아요

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

TextFieldTypeTextFieldPropstype에 사용 중이라 내부 정의만 유지하고, 외부 사용처가 없어 export만 제거했습니다!

(ControlledTextFieldProps | UncontrolledTextFieldProps) &
Omit<ComponentPropsWithoutRef<'input'>, 'type' | 'value' | 'defaultValue' | 'onChange'> &
Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'defaultValue' | 'onChange'> & {
onChange?: (e: CommonChangeEvent) => void;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

여기도 ChangeEventHandler 사용해서 타입 정의하는건 어떨까요?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

넵 수정했습니다!

Comment thread src/shared/ui/TextField/index.ts Outdated
Comment on lines +1 to +2
export { TextField } from './TextField';
export type { TextFieldProps } from './TextField.types';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

타입 임포트가 누락된거 가타요!

Suggested change
export { TextField } from './TextField';
export type { TextFieldProps } from './TextField.types';
export { TextField } from './TextField';
export type { TextFieldProps, TextFieldType } from './TextField.types';

Copy link
Copy Markdown
Collaborator 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
Member

Choose a reason for hiding this comment

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

현재 모든 텍스트 필드를 한 파일이 담당하고 있어 덩어리가 큰 것 같습니다 기본 래퍼는 거의 동일해서

TextField/
├── TextField.tsx          # 공통 로직
├── CharField.tsx          # type="char" 전용
├── TextAreaField.tsx      # type="textarea" 전용
├── TextField.types.ts   # 타입정의
├── TextField.variants.ts  #베리언트 파일
├── PriceField.tsx         # type="price" 전용
└── index.ts               # 통합 export

이런식으로도 분리 고려해볼 수도 있을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

말씀해 주신 구조로 CharField / TextAreaField / PriceField 래퍼를 분리해서 적용했습니다! 공통 로직은 TextField.tsx에 유지했습니다 추후에 전용 로직이 커지면 더 세분화하겠습니다!

@jwj0620gcu

Copy link
Copy Markdown
Collaborator

예빈님 textfield 구현 힘드실텐데 수고하셨습니다!
전 Controlled / Uncontrolled를 타입으로 분리해 충돌 막은 게 인상적이었습니다~!

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 2MB

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 포맷 필요
Test: 실패
Build: 실패

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 2MB

@KyeongJooni KyeongJooni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

수정사항 반영된거 확인했고 추가로 코멘트 좀 더 달았습니다 확인부탁드려요! :)

Comment thread src/shared/ui/TextField/TextField.tsx Outdated
type="text"
value={displayValue}
disabled={disabled}
maxLength={maxLen}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maxLen이 콤마도 포함해서 displayValue에 적용될 것 같은데

<input
  maxLength={isPrice ? undefined : maxLen}
  // ...
/>

이런식으로 수정하시면 더 좋을 것 같습니다!
아니면 handleChange에서 숫자 길이 제한하는 방법도 있을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

말씀해 주신 대로 price 입력은 콤마 포함 길이로 제한되지 않도록 숫자만 추출한 값에 maxLength를 적용하도록 수정했습니다!

Comment thread src/shared/ui/TextField/TextField.tsx Outdated
Comment on lines +46 to +48
const maxLen = isChar
? CHAR_MAX_LENGTH
: (maxLength ?? (isTextarea ? TEXTAREA_MAX_LENGTH : undefined));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

중첩 삼항 연산자때문에 가독성이 쪼금 떨어지는 것 같아요 객체매핑을 통해 정의하거나 함수로 분리 고려해보시면 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

중첩 삼항 대신 getMaxLength 함수로 분리했습니다!

@vercel

vercel Bot commented Jan 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
loopit Ready Ready Preview, Comment Jan 16, 2026 0:46am

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 측정 불가

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 측정 불가

@KyeongJooni KyeongJooni left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM 🥇

@github-actions

Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Test: 통과
Build: 성공
Build Size: 2MB

@YeBeenChoi
YeBeenChoi merged commit 51e4027 into main Jan 16, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feature 새로운 기능

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] #14 - TextField 컴포넌트 제작

3 participants