반복을 줄이는게 정말 좋은가요?

햄햄·2022년 10월 23일
109
post-thumbnail

코딩 입문의 국룰, 생활코딩에서는 무시무시한 예제를 들며 반복은 나쁘다고 알려준다.

"만약 똑같은 데이터가 1억개가 있다면? 그리고 이 데이터들을 한꺼번에 변경해야 한다면?"

1억이란 숫자에 압도된 코딩 입문자의 머릿 속에는 하나의 원칙이 강렬하게 아로새겨진다.

반복은 나쁜 것!

그 유명한 DRY(Don't Repeat Yourself)원칙을 접하며 다시 한번 다짐한다. '절대 똑같은 코드를 반복하지 않을거야!'

이후 나는 절대 반복되는 코드를 작성하지 않기 위해 눈에 불을 켰다. 반복을 줄이고 재사용이 많이 되도록 코드를 작성하였다. 하지만 어째 상황이 순탄하게 흘러가지만은 않았다. '반복 없이' '재사용'이 많이 되도록 코드를 작성하던 나는 다음과 같은 문제들에 봉착하였다.

🤯

1. 변경하기 어려워졌다

처음 입사한 회사에서 거의 새로 시작하는 신규 프로젝트를 맡게 되었다. 프로젝트에서는 다음 그림과 같은 컴포넌트가 있었다.


작업의 순서를 알려주는 UI이다. 작업 단계들을 순서대로 나열하고 어떤 단계가 작업 전인지, 중인지, 후인지를 표시하고 각 단계에 대한 세부설명을 보여주어야 했다.

작업 단계를 나타내는 UI(노란색 박스)가 반복되었기에, 비슷한 코드를 여러번 작성하는 대신 Work라는 하나의 컴포넌트로 만들어 재사용하였다. 전달받은 디자인에서는 여러 개의 Work 컴포넌트에서 달라지는건 타이틀, 세부설명 텍스트 뿐이었기에 타이틀과 세부설명 텍스트를 prop으로 받도록 컴포넌트를 만들었다.

<Work 
  title="냉장고를 연다"
  inProgressMessage="냉장고 종류: 김치 냉장고" 
  afterProgressMessage="냉장고 종류: 열린 김치 냉장고"
/>

비슷한 UI를 사용하는 다른 페이지의 디자인을 전달받았을 때, Work 컴포넌트를 재사용할 수 있겠군 ^-^ 하고 뿌듯해하던건 잠시였다.

고려하지 못했던 작업 중지 버튼, 테이블 디테일이 생겼다. 제가 만든 Work 컴포넌트는 텍스트만 변경할 수 있게 만들어졌는데요..? 결국 변경된 UI를 적용하기 위해 Work 컴포넌트의 코드를 수정하고 새로운 prop을 추가해야 했다.

<Work 
  title="코끼리를 넣는다"
  titleButton={<Button>작업 중지</Button>}
  inProgressComponent={<Table />}  
  afterProgressComponent={...}
/>

뭔가.. 찜찜하다..

예상치 못한 UI 요소가 추가될 때마다 Work 컴포넌트를 수정하고 새로운 prop을 만들어야 하는걸까? 차라리 Work라는 컴포넌트를 만들지 않고 코드를 작성했다면 더 변경하기 쉬웠을텐데.. 뒤늦게 후회했지만 되돌리기에는 이미 먼 길을 와버렸다.

2. 복잡해졌다

반복을 줄이다 복잡도가 매우 올라가는 문제도 겪게 되었다. 필터링 옵션의 값을 입력해서 리스트를 필터링하는 컴포넌트를 생성해야 했다. 그런데 비슷한 필터링 옵션이 여러 페이지에서 사용되어 공통으로 사용될 필터 컴포넌트를 다음과 같이 만들었다

<BasicFilter
  searchKeywordOptions={}
  stateOptions={}
  searchValue={}
  selectedSearchItem={}
  selectedState={}
  onChangeSearchValue={}
  onSelectSearchKeyword={}
  onSelectState={}
  onSubmit={}
  warehouseOptions={}
  warehouseSubOptions={}
  selectedWarehouse={}
  onSelectWarehouse={}
  onInit={}
/>

여러 개의 input을 하나의 컴포넌트로 묶으니 매우 많은 props가 생기게 되었다. 이 props에 전달할 코드에 대한 반복을 줄이기 위해 또 이와 관련된 커스텀 훅을 만들게 되었다.

const {
  searchQuery,
  setSearchQuery,
  selectedState,
  setSelectedState,
  selectedSearchItem,
  setSelectedSearchItem,
  searchValue,
  setSearchValue,
  selectedWarehouse,
  setSelectedWarehouse,
  handleSearch,
} = useBasicFilter();

이렇게 만든 BasicFilter라는 컴포넌트를 사용하려면 각각의 prop이 어떤 의미인지를 파악해야하고 useBasicFilter라는 훅을 함께 사용한다는 맥락을 파악하고 있어야 한다. 코드를 작성한 당사자인 나는 잘 알지 몰라도, 이 코드를 보고 써야하는 다른 개발자는 어땠을까? 사실, 다른 개발자까지 가지 않아도 되었다. 코드를 작성하고 한달 후 내가 다시 보았을 때 "미친거 아냐?"라는 소리가 절로 나왔었다.

3. 하나의 함수가 너무 많은 일을 하게 되었다

반복을 줄이기 위해 초반에 만든 함수를 계속 재사용하려다보니, 비슷한 기능에 작은 변주가 생길때마다 if 구문으로 분기를 계속 추가하게 되었다. 그렇게 여러가지 기능이 뒤섞인 함수는 매우 뚱뚱하고 전지전능한, 복잡한 함수가 되었다. 버그가 한번 발생할 때마다 디버깅 하느라 시간이 다 갔다는 것은 말해 뭐할까.

왜 반복을 줄여야 하나요?

나는 이 모든 문제들을 '반복을 줄이기 위한' 코드를 작성하는 과정에서 만들었지만, 정말 반복을 줄여서 생긴 문제들이었을까? 사실 문제의 원인은 '반복을 줄여서'가 아니라 '반복을 줄이지 못해서' 였다. 코드 모양이 비슷하다고 무조건 반복이라고 할 수 있을까? 반복은 왜 줄여야하는 것일까? 무작정 반복을 만들지 않겠다고 결심하기 전에 나는 이 고민을 해야 했을지 모른다. 그렇다면, 다시 한번 생활코딩의 말을 잘 들어보자.

"만약 똑같은 데이터가 1억개가 있다면? 그리고 이 데이터들을 한꺼번에 변경해야 한다면?"

그렇다. 반복은 결국 코드를 쉽게 변경하기 위해서 줄이는 것이다. 코드를 쉽게 변경하려면 복잡도가 낮아야 하고 버그에서 안전해야 한다. 코드의 모양만 보고 섣불리 반복을 줄이겠다고 작성한 코드는 정반대로 작동하게 된 것이다.

반복을 잘 줄이는 법

그렇다면 어떻게 해야 반복을 잘 줄일 수 있을까? '변경'에 초점을 맞추어 내가 만든 레거시를 리팩토링 해보았다.

잘 변경되는 것과 잘 변경되지 않는 것 구분하기

코드의 모양이 비슷하다고 하나의 함수로 추출하는 것은 위험하다. 각 코드가 어떤 목적을 가지고 어떤 기능을 수행하는지 잘 고려한 다음 잘 변경되는 것과 잘 변경되지 않는 것을 구분해야 한다. 그리고 잘 변경되지 않는 코드를 추상화하여 재사용해야 한다.

그렇다면 잘 변경되는 것은 무엇이고 잘 변경되지 않는 것은 무엇일까? UI로직에 비해 UI스타일은 잘 변경된다. 나는 컴포넌트를 말로 설명해야할 때 나오는 내용은 UI로직이고 HTML, CSS가 변경되어야 하는 것은 UI스타일이라고 구분하였다.

위에서 예시를 든 Work 컴포넌트를 다시 보자. Work 컴포넌트가 무엇인지 말로 설명하자면 "작업 전, 작업 중, 작업 후 일때 다른 UI를 보여주는 컴포넌트"라고 할 수 있다. 이는 UI로직이다. 작업 전, 작업 중, 작업 후일 때 UI가 어떻게 보여지는지는 HTML, CSS를 통해 작업해야 한다. 이는 UI스타일이다. 이를 토대로 다음과 같이 리팩토링 하였다.

<Work
  beforeProgressComponent={
  	<Title>코끼리를 넣는다</Title>
  }
  inProgressComponent={
  	<>
      <BoldTitle>코끼리를 넣는다</BoldTitle>
      <Table />
    </>
    
  }
  afterProgressComponent={
    <>
      <GrayTitle>코끼리를 넣는다</GrayTitle>
      <Message />
    </>
 }
/>

작업 전, 작업 중, 작업 후일 때 다른 UI가 보여져야 한다는 로직은 Work 컴포넌트 내부에 숨겨 추상화하였다. 대신 어떤 UI들이 보이는지는 Work를 사용하는 컴포넌트가 props로 자유롭게 주입할 수 있도록 만들었다. 비슷한 스타일이 반복되면 비슷한 코드도 반복되지만 스타일 변경에는 좀 더 유연한 컴포넌트가 되었다.

변경이 쉬운 패턴과 아키텍처를 선택하기

코드를 잘 작성하고 싶다고 골방에서 머리만 싸매고 있는다고 답이 나오지는 않는다. 커뮤니티 참여, 검색, 독서 등을 통해 어떤 좋은 아키텍처와 디자인 패턴이 있는지 내 머리에 입력해주자. 개인적으로는 컴파운드 컴포넌트 패턴과 MVI 아키텍처를 유용하게 사용하였다. 이 중 컴파운드 컴포넌트 패턴을 사용하여 BasicFilter 컴포넌트를 리팩토링 해보았다.

<Filter>
  <Filter.SearchWithSelect
    label="검색어"
    searchOptions={}
  />
  <Filter.Select
    label="상태"
    field="status"
    options={}
  />
  ...
</Filter>

이전 BasicFilter에 비해 훨씬 명료하고 복잡도가 낮은 컴포넌트로 만들 수 있었다. 이와 관련해 얼마 전 동료 개발자에게 "그때 햄님이 만드신 컴포넌트 유용하더라구요!" 라는 피드백을 받기도 했다.

반복 작업을 줄이기

어쨌든 비슷한 모양의 코드를 작성하는 것은 귀찮다. 그렇다고 모든 비슷한 코드의 반복을 줄이면 다시 한번 위와 같은 대참사를 겪게 될게 뻔했다. 그래서 '반복 코드'가 아닌 '반복 작업'을 줄이고자 하였다. 바로 IDE의 유용한 기능을 활용해서 말이다. vscode에서는 snippet, webstorm에서는 live template이라는 기능을 활용해서 몇가지 단축키로 많은 코드를 자동완성시킬 수 있다. 이를 통해 간단하게 지루한 코드 작성 시간을 줄일 수 있다. 내가 만든 snippet 혹은 template을 팀에게 공유하여 팀 전체의 생산성을 제고해볼 수도 있다.

마무리

여기까지, 신입개발자로서 어떤 삽질을 했는지, 그리고 어떻게 수습하려 했는지에 대한 기록이었다. 이런 몇달간의 삽질을 통해 "반복을 줄이는 이유는 코드를 변경하기 쉽게 하기 위해, 복잡도를 낮추기 위해서라는 걸 상기해야 한다"는 것과 "잘 줄이지 못한 반복은 줄이지 않느니만 못할수도 있다"라는 깨달음을 얻게 되었다. 이런 경험을 공유하여 주니어 개발자와의 공감대를 형성하고 혹시나 잘못될 수 있는 내용에 대해 피드백을 얻고자 사내 세미나에서 발표하였다. 그리고 CTO님이 다음과 같은 조언을 첨언해주셨다.

  • 우연한 중복이란 것이 있다. 코드가 세번 이상 중복되어도 우연한 중복일 수 있다. 중복 제거 시점을 늦춰 변경되는 시점에 같이 변경되는지 따로 변경되는지 판단하여야 한다.
  • 중복을 판별할 때 변경을 요구하는 주체도 고려하여야 한다. 동일한 로직이어도 변경을 요구하는 주체가 다르고 요구하는 변경 내용이 다르면 코드를 나누어야 한다.
  • 미리 재사용될 것을 고려하여 코드를 작성하는 것은 immotal의 영역이다. 평범한 사람이 미리 고려해야할 내용은 아니다. 한 로직을 두군데 이상에서 재사용되게만 만들어도 좋다.
profile
@Ktown4u 개발자

15개의 댓글

comment-user-thumbnail
2022년 10월 23일

잘보고 갑니다..^^

1개의 답글
comment-user-thumbnail
2022년 10월 24일

공감합니다. 무조건 코드(컴포넌트)를 재사용 하는 것이 옳은 것은 아니죠ㅎㅎ 잘 읽었습니다~

1개의 답글
comment-user-thumbnail
2022년 10월 26일

UI로직의 통합은 개발자가 판단하기 애매한 부분들이 있어서,
제 경험으로는 본격적인 개발 전 디자이너와 공통으로 활용 할만한 컴포넌트들을 같이 정의해서 활용하면
나중에 개발할 때 편하더라구요..!

1개의 답글
comment-user-thumbnail
2022년 10월 28일

한 로직을 두군데 이상에서 재사용되게만 만들어도 좋다.
저도 항상 이런 방향으로 코딩하려고 노력하네요 좋은내용 잘봤습니다.

1개의 답글
comment-user-thumbnail
2022년 10월 29일

work 컴포넌트 이야기는 제 경험이랑 소름돋게 똑같네요... ㄷㄷ
좋은 글 감사합니다 😆

1개의 답글
comment-user-thumbnail
2022년 10월 31일

아직 공부 중인 취준생인데 무슨 얘긴지 80% 정도 알것 같아요...!
경험해보면 완전히 이해하겠죠 ㅎㅎ 좋은 글 감사합니다.

1개의 답글
comment-user-thumbnail
2022년 10월 31일

와우 ... 제 생각은 눈앞에서 for문을 치워버리는 수준 밖에 되지 않았습니다 ㅠㅠ 앞으로 코드 작성할 때 << 몇달간의 삽질을 통해 "반복을 줄이는 이유는 코드를 변경하기 쉽게 하기 위해, 복잡도를 낮추기 위해서라는 걸 상기해야 한다"는 것과 "잘 줄이지 못한 반복은 줄이지 않느니만 못할수도 있다"라는 깨달음을 얻게 되었다. >> 이 부분을 꼭 생각해야겠습니다. 덕분에 좋은 깨달음을 얻고 갑니다 👍

1개의 답글
comment-user-thumbnail
2023년 7월 10일

진짜 똑같은 내용에서 의문 가지는중에 이 글 보네요 ㅋㅋㅋ 심득을 얻었습니다. 고마워요

답글 달기