1회차가 끝난 후 다른 분들과 코드를 비교하면서 내가 몰랐던 부분, 놓쳤던 부분을 알게 되어 저에겐 너무너무 유용한 시간이었습니다!!
응집도를 높히고 추상화하는 것이 포인트였는데 그것이 실제로 구현된 코드를 보니 그제서야 이해가 됐습니다!
2회차도 열심히 고민해보고 다른 분들과 비교해볼 것을 생각하면 벌써 기대가 됩니다! 😆
[2회차] 단단한 컴포넌트 만들기
- 다양한 유즈케이스에 탄력적으로 대응할 수 있는 구조에 대해서 고민해봅니다.
- Storybook을 사용해 누구나 확인할 수 있도록 배포합니다.
탄력적인 컴포넌트란?
아래 세가지를 기준으로 생각해보기
- Headless 기반의 추상화하기
: hooks을 이용해서 변하는 데이터와 UI를 분리하는 예시를 보여주심- 한가지 역할만 하기
: composition을 이용해서 하나의 역할을 하는 컴포넌트들의 조합해서 복잡한 컴포넌트를 보다 단순하게 구성하는 예시를 보여주심- 도메인 분리하기
: 🚧👷♂️🚧
참고 | 토스 유튜브
to
에 따라 버튼 또는 링크가 반환되도록 했습니다.interface IButton extends ButtonHTMLAttributes<HTMLButtonElement> {
title: string;
to?: string;
colored?: boolean;
}
const Button = ({ to, title, colored, ...props }: IButton) => (
<Wrapper colored={colored}>
{to ? (
<Link href={to} passHref>
<a>{title}</a>
</Link>
) : (
<button {...props}>{title}</button>
)}
</Wrapper>
);
icon
, isValid
만 심플하게 받습니다.interface IIput extends InputHTMLAttributes<HTMLInputElement>, IMessage {
icon?: string;
isValid?: boolean;
}
const Input = forwardRef<HTMLInputElement, IIput>(
({ icon, isValid, message, validations, ...props }, ref) => (
<Wrapper error={!!message}>
<label>
<span>{icon || '✉️'}</span>
<input ref={ref} {...props} />
{isValid && <span className='valid-icon'>✓</span>}
</label>
<Messages message={message} validations={validations} />
</Wrapper>
)
);
message
에러메세지를 보여줍니다.validations
조건리스트를 보여줍니다. 조건마다 완수했는지 하나씩 검사 가능합니다.interface IMessage {
message?: string;
validations?: { type: string; invalid: boolean; message: string }[];
}
const Messages = ({ message, validations }: IMessage) => (
<Box>
{validations
? validations.map(({ invalid, message }) => (
<ValidMessages invalid={invalid}>
<span>{invalid ? 'X' : '✓'}</span>
{message}
</ValidMessages>
))
: message && <span>{message}</span>}
</Box>
);
props 이름 정하기 제일 어려웠습니다.
description
이 있다면 button을 통해 더 자세한 정보를 볼 수 있습니다.interface ICheckbox extends InputHTMLAttributes<HTMLInputElement> {
title: string;
isChild?: boolean;
bold?: boolean;
description?: {};
}
const Checkbox = forwardRef<HTMLInputElement, ICheckbox>(
({ title, description, bold, isChild, ...props }, ref) => (
<Wrapper bold={bold} isChild={isChild}>
<label>
<input type='checkbox' ref={ref} {...props} />
<span>{title}</span>
</label>
{description && <button>〉</button>}
</Wrapper>
)
);
fields
와 register
를 심플하게 받도록 구성했습니다. interface IProps<T> {
fields: ICheck<T>[];
register: UseFormRegister<T>;
}
const CheckboxGroup = <T extends unknown>({ fields, register }: IProps<T>) => {
...
return (
<Wrapper>
<Checkbox
title='모두 동의합니다.'
onChange={selectAll}
checked={isCheckAll}
bold
/>
<p>
동의에는 필수 및 선택 목적(광고성 정보 수신 포함)에 대한 동의가 포함되어
있으며, 선택 목적의 동의를 거부하시는 경우에도 서비스 이용이 가능합니다.{' '}
</p>
<ul>
{fields.map((i, index) => (
<li key={index}>
<Checkbox
{...register(i.name)}
title={i.title}
description={i.description}
isChild={i.isChild}
required={i.required}
checked={isCheck.includes(i.name)}
onChange={selectOne}
/>
</li>
))}
</ul>
</Wrapper>
);
};
이후에 할 일
- 가이드를 주시면서 밑에 참고할 만한 컨텐츠를 제공해주셨는데 그게 너무 유익했습니다. 2회차 제출 후에 다시 복습하면서 제 코드와 다시 비교해보겠습니다.
→ 제출기한이 늘어서 다시 복습하고 코드를 수정해보았는데 더 많이 시도해봐야할 것 같습니다.- 데이터 흐름을 중단하지 말라는 자료도 그동안 제가 한 실수는 없는지 조마조마했지만 흥미롭게 읽었습니다.
- 3회차 가이드라인도 너무 도움이 되는 주제고 할 수 있는 만큼 열심히 해보겠습니다!