blog
wanted
pre-on-boarding

원티드 프리온보딩 과제 - 3일차

useInput

import { ChangeEvent, useState } from 'react';

function useInput<T>(inputGroup: T) {
  const [inputValues, setInputValues] = useState(inputGroup);

  const handleInputChange = (field: keyof T) => {
    return (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
      setInputValues((prev) => ({
        ...prev,
        [field]: e.target.value,
      }));
    };
  };

  const resetAllInput = () => {
    const resetObject: { [key: string]: string } = {};
    setInputValues((prev) => {
      if (prev) {
        Object.keys(prev).forEach((item) => {
          resetObject[item] = '';
        });
      }

      return prev;
    });
  };

  const resetSpecificInput = (field: keyof T) => {
    setInputValues((prev) => ({
      ...prev,
      [field]: '',
    }));
  };

  return { handleInputChange, inputValues, resetAllInput, resetSpecificInput };
}

export default useInput;

학원에서 포폴 만들 때 사용했던 custom hook을 개선했습니다.

import axios from 'axios';
import { useRef, useState } from 'react';
import { client } from '../api/client';
import { CustomButton, CustomInput } from '../components';
import { baseURL } from '../constants/constant';
import { useInput } from '../hooks';
import { checkEmail, checkPassword } from '../utils';

function Signup() {
  const { inputValues, handleInputChange } = useInput<{
    id: string;
    pw: string;
  }>({ id: '', pw: '' });

  const idRef = useRef<HTMLInputElement>(null);

  return (
    <main className="flex h-screen flex-col items-center justify-center gap-4">
      <div className="flex flex-col gap-4">
        <CustomInput
          value={inputValues.id}
          placeholder="user@user.com"
          onChange={handleInputChange('id')}
          inputLabel={{ label: '아이디', id: '아이디' }}
          errorMessage={idFeedback || checkEmail(inputValues.id)}
          testId="email-input"
          type="email"
          customRef={idRef}
        />
        <CustomInput
          value={inputValues.pw}
          placeholder="8자리 이상 입력해주십시오."
          onChange={handleInputChange('pw')}
          inputLabel={{ label: '비밀번호', id: '비밀번호' }}
          errorMessage={checkPassword(inputValues.pw)}
          testId="password-input"
          type="password"
        />
      </div>
    </main>
  );
}

export default Signup;
  • 이전과 다르게 typesafe하게 사용할 수 있게 되었습니다. 다시 자동완성 뽕맛에 취할 수 있습니다.
  • 여전히 아쉽습니다. 대입하는 인자로 알아서 타입 추론이 되게 만들고 싶었습니다. {id: "", pw: ""}만 대입해도 inputValues에 알아서 inputValues.id, inputValues.pw로 접근가능하게 작성하고 싶습니다. 호출하는 사람이 제네릭을 지정해야 한다는 점이 치명적인 단점입니다.
  • 지금도 타입스크립트 기초가 너무 안 되어 있습니다.

조건부 타입

input에 의존성 props를 만들고 싶었습니다. 일단 못찾았습니다.

<CustomInput value="adsf" onChange={(e) => {}} />
<CustomInput value="adsf" onChange={(e) => {}} label="label" id="id" />

원래는 이렇게 label을 지정 id도 같이 입력하게 만들고 싶었습니다.

이거에 오늘 3 ~ 4시간을 들였습니다.

<CustomInput value="adsf" onChange={(e) => {}} inputLabel={{ label: "비밀번호", id: "비밀번호" }} />
<CustomInput value="adsf" onChange={(e) => {}} />

그냥 이렇게 해결할 수 있었습니다.

@example

/**
 * @example
 * <CustomInput value="adsf" onChange={(e) => {}} />
 * <CustomInput value="adsf" onChange={(e) => {}} inputLabel={{ label: "label", id: "id" }} />
 */

함수 hover하면 JSX 예시를 볼 수 있었습니다.