๐Ÿค” How to use Compound Components Pattern

Compound Components Pattern

์กฐํ•ฉ ์ปดํฌ๋„ŒํŠธ ํŒจํ„ด์€ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ง‘ํ•ฉ์ฒด๋กœ ๋ถ„๋ฆฌํ•œ ๋’ค, ๋ถ„๋ฆฌ๋œ ๊ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ ์กฐํ•ฉํ•ด ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด๋‹ค. ์ด๋Š” props drilling์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋Œ€์•ˆ์ฑ…์œผ๋กœ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Prop drilling์€ React ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์—์„œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ, ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ๋Œ€์‹  prop drilling์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฅผ ์ถ”์ ํ•˜๊ธฐ๊ฐ€ ์‰ฝ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ฃผ๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ prop drilling์ด ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋„ ์กด์žฌํ•œ๋‹ค. ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋ณต์žกํ•ด์ง€๊ณ  ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•  ๋•Œ, ์ปดํฌ๋„ŒํŠธ์˜ ๊ณ„์ธต์ด ๋ณต์žกํ•ด์ง€๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ „๋‹ฌํ•ด์•ผ ํ•˜๋Š” props๋„ ๋งŽ์•„์ง€๋ฉฐ ์ด๋Š” ์ƒํƒœ ๊ด€๋ฆฌ์™€ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์–ด๋ ต๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์กฐํ•ฉ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ์ ˆํžˆ ํŒ๋‹จํ•˜์—ฌ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์ด ํŒจํ„ด์„ ํ†ตํ•ด ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๊ณ , ํ•œ ๊ฐ€์ง€์˜ ๊ธฐ๋Šฅ์„ ์ฑ…์ž„์ง€๋Š” ์ปดํฌ๋„ŒํŠธ ์กฐ๊ฐ๋“ค๋กœ ๋‚˜๋ˆ„์–ด ์˜๋ฏธ ์žˆ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋ณต์žก์„ฑ์„ ํ•ด๊ฒฐํ•˜๊ณ  ์ง๊ด€์ ์ธ ํŒŒ์•…๊ณผ ๊ฐ€๋…์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๋ฉฐ ๋ณ€ํ™”์— ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

Compound Form Components

Compound Form Component ๊ธฐ๋ณธ์ ์œผ๋กœ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ์— ๊ทผ๊ฑฐํ•ด์•ผํ•œ๋‹ค. form์˜ ์ž…๋ ฅ๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ„์˜ ์˜์กด์„ฑ์„ ๋‚ฎ์ถ˜๋‹ค. ์–ด๋–ค ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ค๋“ ์ง€ ๊ทธ์— ๊ทผ๊ฑฐํ•˜์ง€์•Š๊ณ  ์ฒ˜๋ฆฌํ• ์ˆ˜์žˆ๋‹ค. ์˜์กด์„ฑ์„ ๋‚ฎ์ถ”๋ฉด ๋„๋ฉ”์ธ๊ณผ์˜ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ”์šฉ์ ์œผ๋กœ form์ด ํ•„์š”ํ•œ ๋ชจ๋“ ๊ณณ์— customํ•ด ์‚ฌ์šฉํ• ์ˆ˜์žˆ๊ฒŒ๋œ๋‹ค. Form Component์˜ ๊ฒฝ์šฐ ์ฑ…์ž„์„ form์˜ ๊ธฐ๋Šฅ, ์ฃต๋ฅ˜์— ๋”ฐ๋ผ ๋‚˜๋ˆŒ์ˆ˜์žˆ๋‹ค. ์ ์ ˆํ•œ ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„๋Š”๊ฒƒ์ด ์ค‘์š”ํ•œ๋ฐ, ์ด๋˜ํ•œ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํˆด์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ์ˆ˜์žˆ๋‹ค. ํ˜„์žฌ ์‚ฌ์šฉํ•˜๋Š” ant design ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด - input - text area - checkbox - button - select - upload - modal ๋“ฑ UI ๋‹จ์œ„๋กœ ๋‚˜๋ˆŒ์ˆ˜์žˆ๋‹ค.

๊ตฌํ˜„

  • ์„œ๋ธŒ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„

    • antd์˜ UI๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค.
  • input component

function CustomInput({
  label, name, rules, disabled, ...args
}: FormItemProps & InputProps) {
  return (
    <S.FormItem label={label} name={name} rules={rules}>
      <S.StyledInput disabled={disabled} {...args} />
    </S.FormItem>
  );
}
  • checkbox component
function CustomCheckbox({
  name,
  children,
  disabled,
}: CheckboxProps) {
  return (
    <S.FormItemCheckbox name={name} valuePropName="checked">
      <Checkbox disabled={disabled}>
        {children}
      </Checkbox>
    </S.FormItemCheckbox>
  );
}
  • button component
function CustomButton({
  children,
  danger,
  icon,
  onClick,
  htmlType,
}: ButtonProps) {
  return (
    <S.FormItem>
      <S.StyledButton
        htmlType={htmlType}
        type="primary"
        danger={danger}
        icon={icon}
        onClick={onClick}
      >
        {children}
      </S.StyledButton>
    </S.FormItem>
  );
}
  • ์„œ๋ธŒ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฌถ์–ด export ํ•˜๊ธฐ
    • ์„œ๋ธŒ ์ปดํฌ๋„ŒํŠธ๋“ค์„ CutomForm์˜ ๊ฐ์ฒด๋กœ ์ง€์ •ํ•ด์ฃผ์–ด export ํ•œ๋‹ค.
    • ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ CustomForm์œผ๋กœ ํ†ต์ผ๋˜๊ธฐ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์—๋„ ๋„์›€์„ ์ค„์ˆ˜์žˆ๋‹ค.
//exapmle

const CustomForm = Object.assign(Form, {
  GridRow,
  Button: CustomButton,
  Input: CustomInput,
  InputNumber: CustomInputNumber,
  TextArea: CusctomTextArea,
  MultipleUpload: CustomMultipleUpload,
  SingleUpload: CustomSingleUpload,
  Checkbox: CustomCheckbox,
  Select: CustomSelect,
  Switch: CustomSwitch,
  Modal: CustomModal,
});

export default CustomForm;
  • Usage
    • ์ง๊ด€์ ์œผ๋กœ CustomForm ์œผ๋กœ ํ†ต์ผ๋˜์–ด ์ผ๊ด€์„ฑ์žˆ๊ฒŒ ์งค์ˆ˜์ž‡์Œ
    • ์ค‘๊ฐ„์— ์ถ”๊ฐ€๋ฅผ ํ•ด์•ผํ•œ๋‹ค๋ฉด ์ถ”๊ฐ€ํ•˜๊ธฐ์— ์šฉ์ดํ•จ.
<CustomForm>
      <CustomForm.Input label="๋ฐฉ์ด๋ฆ„" name="name" />
      <CustomForm.GridRow gridColumns="1fr 1fr">
        <CustomForm.Input label="๋ฐฉ์ข…๋ฅ˜" name="room_type" />
        <CustomForm.InputNumber label="๋ฐฉํฌ๊ธฐ" name="size" />
      </CustomForm.GridRow>
      <CustomForm.GridRow gridColumns="1fr 1fr">
        <CustomForm.InputNumber label="์œ„๋„" name="latitude" />
        <CustomForm.InputNumber label="๊ฒฝ๋„" name="longitude" />
      </CustomForm.GridRow>
      <CustomForm.Input label="์ฃผ์†Œ" name="address" />
      <CustomForm.TextArea
        label="์„ค๋ช…"
        name="description"
        maxLength={200}
      />
      
      <Divider orientation="left" style={{ marginTop: '40px' }}>
        ์˜ต์…˜
      </Divider>
      <S.CheckboxWrap>
        {ROOM_OPTION.map(
          (optionData) => (
            <CustomForm.Checkbox
              key={optionData.name}
              name={optionData.data}
            >
              {optionData.name}
            </CustomForm.Checkbox>
          ),
        )}
      </S.CheckboxWrap>
  
      <Divider orientation="left">์‚ฌ์ง„</Divider>
      <S.UploadWrap>
        <CustomForm.MultipleUpload domain="lands" name="image_urls" form={form} />
      </S.UploadWrap>
      <CustomForm.Button icon={<UploadOutlined />} htmlType="submit">
        ์™„๋ฃŒ
      </CustomForm.Button>
      <CustomForm.Button danger icon={<DeleteOutlined />} onClick={deleteRoom}>
        ์‚ญ์ œ
      </CustomForm.Button>
    </CustomForm>

conclusion

  • ํ•จ์„ฑ ์ปดํฌ๋„ŒํŠธ ํŒจํ„ด์„ ์ด์šฉํ•˜๋ฉด
    • ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ• ์ˆ˜์žˆ๋‹ค.
    • ๋ณด๋‹ค ์œ ์—ฐ์„ฑ์ด ๋†’์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋‚ผ์ˆ˜์žˆ๋‹ค.

Reference