컴포넌트를 잘 쓰기위한 삽질의 여정

kich555·2021년 10월 17일
0

요즘 코드를 많이친다.

이론공부할때가 좋았는데..

이젠 매일 나의 불손한 코드를 볼때마다,

이것밖에 안되냐고 스스로에게 후두려 맞는 느낌이다.

컴포넌트

개인적으로 컴포넌트는 거대한 하나의 결과물을 쌓기위한 작은 블록 하나라고 생각한다.

당연히 블록의 가짓수가 적을수록, 결과물의 코스트는 낮아지게 될것이다.

개발자에게 코스트는 시간이고, 그 소리는 잘 만든 컴포넌트들은 나의 생산성을 크게 높혀줄 것이라 생각했다.

잘 만든 컴포넌트 하나 열 컴포넌트 안부럽다 라는 생각으로..프로젝트를 시작하였고,
프로젝트가 끝난 지금,
나는 아직 안전등급 e등급의 언제 무너질지 모르는 프로젝트는 완성할 수 있어도
컴포넌트하나 제대로 못만드는, 작고 귀엽고 하찮은 개발자라는건 뼈저리게 느낄 수 있었다.

컴포넌트 재사용을 위한 발악

회원가입 페이지는 많은 inputlist tag가 있는 페이지이다.

내 첫번째 삽질은 바로 컴포넌트 재사용이란 단어에 미쳐 너무 재사용하려고 애쓴것이다.

<li className="joinList">
{JOIN_LIST.sec.map(input =>
  input.radio ? (
    <label className="radioLabel">
      <Input
        key={input.id}
        className={input.className}
        type={input.type}
        name={input.name}
        value={input.value}
        onClick={onClick}
      />
      {input.radioName}
    </label>
  ) : (
    <Input
      id={input.id}
      className={input.className}
      placeholder={input.placeHolder}
      type={input.type}
      name={input.name}
      onChange={onChange}
    />
  )
)}
</li>
<li className="joinList">
{JOIN_LIST.third.map(input =>
  input.radio ? (
    <label className="radioLabel">
      <Input
        key={input.id}
        className={input.className}
        type={input.type}
        name={input.name}
        value={input.value}
        onClick={onClick}
      />
      {input.radioName}
    </label>
  ) : (
    <Input
      id={input.id}
      className={input.className}
      placeholder={input.placeHolder}
      type={input.type}
      name={input.name}
      onChange={onChange}
    />
  )
)}
</li>
<List data={JOIN_LIST.fou} onChange={onChange} />
</ul>

위 코드가 가독성이 좋지 않으니 조금 쪼개본다면,

원래는 한번의 map을 사용하여 깔끔하게 위 화면을 구현하려 했었다.

하지만 map을 돌리기엔 커다란 변수가 있었는데 바로


이 부분이다. 문제는 <li>안에 input만 있는것이 아니라 <label>도 있다는 것이었는데,

이러한 text inputradio input, label tag 의 예외처리를 하기 위해
내가 사용한 방식은 매우 무식하며, 복잡했다.

바로 list 컴포넌트를 만들어, 모든 예외상황을 list 컴포넌트안에서 조건부 렌더링으로 처리해버리는 것이었다.

class List extends Component {
  render() {
    const { data, onChange, onClick } = this.props;

    const list = data.map(input =>
      input.radio ? (
        <label className="radioLabel">
          <Input
            key={input.id}
            className={input.className}
            placeholder={input.placeHolder}
            type={input.type}
            name={input.name}
            value={input.value}
            onChange={onChange}
            onClick={onClick}
          />
          {input.radioName}
        </label>
      ) : input.single ? (
        <li className={input.classNameA}>
          <Input
            className={input.classNameB}
            key={input.id}
            placeholder={input.placeHolder}
            type={input.type}
            name={input.name}
            onChange={onChange}
          />
        </li>
      ) : input.number ? (
        <>
          <Input
            className={input.className}
            key={input.id}
            name={input.name}
            onChange={onChange}
          />
          <span className="hyphen">-</span>
        </>
      ) : (
        <Input
          key={input.id}
          className={input.className}
          placeholder={input.placeHolder}
          type={input.type}
          name={input.name}
          onChange={onChange}
          onClick={onClick}
          value={input.value}
        />
      )
    );
    return <>{list}</>;
  }
}

물론 위의 list 컴포넌트를 실용적으로 돌리기 위해선 그에 따른 상수 데이터도 필요했다.

모든 input을 하나의 input 컴포넌트로 처리하려하다보니,
중간중간 문득, 이럴거면 그냥 input tag를 쓰지 뭐하러 input 컴포넌트를 만들었을까? 라는 근본적인 의문점이 들기 시작했다.

과연 이랬던 이유가 뭐였을까...?

재사용하기 위한 확장성을 가지고자, 쪼개고 쪼개다보니 결국에 남은 Input 컴포넌트는 그냥 Input tag가 되어버렸다.

프로젝트 기한이 코앞이라, 더이상 리팩토링할 시간이 없었지만, 이대로 넘어가기는 죽기보다 싫어서,

밤을 새고 특정 Input들을 따로 component화 하였다.

그래서 탄생한 것이 아래 코드에 보이는

PhoneInput


BirthdayInput

RadioInput 들이며,

최종적으로 완성한 형태는 아래와 같다.

<ul>
  <List
    data={JOIN_LIST.first}
    onChange={onChange}
    errorMessage={errorMessage}
  />
  <li className="joinList">
    <Input
      className="name"
      placeholder="이름(실명입력)"
      name="name"
      onChange={onChange}
    />
    <RadioInput
      name="foreigner"
      onClick={onClick}
      firstText="외국인"
      secondText="내국인"
    />
  </li>
  <li className="joinList">
    <BirthdayInput onChange={onChange} />
    <RadioInput
      name="gender"
      onClick={onClick}
      firstText="남자"
      secondText="여자"
    />
  </li>
  <List data={JOIN_LIST.fou} onChange={onChange} />
  <li className="joinListLast">
    <PhoneInput
      firstInputName="firstNum"
      secondInputName="secondNum"
      thirdInputName="thirdNum"
      onChange={onChange}
      None=""
    />
  </li>
</ul>;

위 코드에서도 아쉬운 부분이 많다.

가령 리팩토링 할 시간이 전혀 없어, 아직도 통일성이 안보이는 부분이 많고,

PhoneInput 이랑 BirthdayInput등은 형태가 너무나 비슷해 하나로 합칠수도 있을것 같다. (실은 합쳤다가 다듬을 시간이 없어서 분리한거지만...)

하지만...당장 내일 시작하는 2차 프로젝트를 위한 준비가 우선인것 같아 꾹 참고 넘어가려한다..
(마치 집에 가스레인지를 안끄고 문을 나선것만 같은 느낌이지만 참아야한다....)

결론

실은 저 부분만 하더라도 훨씬 많은 시행착오와, 많은 수정들이 지나갔지만, 가독성을 위해, 대충 저런 삽질을 했구나~ 정도로 기억하려한다.

아직도 리액트의 컴포넌트는 어렵다.
클린한 코드의 길을 멀고도 험난하다는걸 느꼈던, 그래서 더욱 도전 욕구를 불러 일으키는 이번 프로젝트는 아쉬움이 컸지만... 이것또한 경험이겠거니 생각한다.

현업에서 지난 스프린트를 돌이킬 수 있는 시간이 주어진다는게, 리팩토링을 위한 시간적 여유가 있다는게 얼마나 큰 행복인지 담담히 말씀해 주시던 한 현직 개발자분의 말씀이,

그때 당시에도, 지금도 너무나도 큰 마음속 울림을 준다.

21/11/28 (추가)
이 글을 쓴지 근 1개월하고도 보름이 지난 이 시점에서 다시한번 곱씹어보는거지만, 나중에 리팩토링을 해야지...에서 나중은 없다.

무엇인가가 끝나면 다른 무언가가 찾아온다.

1차 프로젝트 이후의 2차 프로젝트, 기업에서 다른 여러 사수분들과 진행해보았던 새로운 프로젝트, 그 과정 속 운좋게 오퍼를 받아 들어간 회사에서 시작할 다른 프로젝트까지..

했던 일은 계속 쌓여가고, 그만큼 벌려둔 일들을 다듬으려면 점점 더 많은 시간이 필요할것같다.

profile
const isInChallenge = true; const hasStrongWill = true; (() => { while (isInChallenge) { if(hasStrongWill) {return 'Success' } })();

0개의 댓글