Thief of Wealth

react를 처음시작할 때에는 컴포넌트는 UI기준으로만 분리하면되고,

사용하는 측에서 컴포넌트를 사용을 하면, 해당 컴포넌트가 다 알아서 해주는 것.

즉, 똑똑한 컴포넌트가 좋은 컴포넌트라고 생각했다.

 

하지만, react 철학으로나 어느 경로로든, "상태끌어올리기"와 "컴포넌트는 멍청해야한다" 등의 어휘를 들어보았을 것이다.

지금까지 그에 대한 이유를 나름 정의하고 있었는데,

"상태끌어올리기"는 상태의 single of truth 를,

"컴포넌트는 멍청해야한다"는 외부에서 제어하기 쉽게하기 위함이라고 생각하는 것이었다.

 

내 머릿속에는 이 2가지의 어휘가 따로따로 존재하고 있었다.

그리고 오늘 이 2가지가 가지는 공통적인 결을 발견했다.

 

바로 "테스터블한 컴포넌트가 되기 위한 조건"이었다.

 

이것을 깨닫기전에는 아래와같은 경험이 있었다.

 

-

나는 며칠전부터 테스트 코드 커버리지를 높이기 위해서,

테스트 코드를 싹 갈아엎고 작은 컴포넌트부터 차근차근 테스트코드를 작성해나가고 있다.

 

atom급 컴포넌트는 사실 rendering만 하는 기능들이 대다수이기 때문에 테스터블하다고 생각되었다.

하지만 molecule에 무려 370line에 달하는 Comment 컴포넌트가 있었고, 커스텀훅으로 엄청난 비동기로직들을 감당하고 있었다.

 

과연 이 Comment 컴포넌트는 테스터블한가?

테스트 코드를 작성하는데 바로 거부감이 드는데 어떻게 테스터블한 컴포넌트라고 할 수 있을까 ㅋㅋ 절대 아니다.

 

물론, use~~ 비동기 로직을 담당하는 커스텀 훅들을 mocking하여 테스트는 가능할것이지만, 그 테스트 코드는 작성 시간과 유지보수 시간은 끔찍할 것이다.

 

일단, 컴포넌트를 더 작고 1가지의 역할만을 담당하게 쪼개었다.

그 과정에서 컴포넌트가 최대한 렌더링 로직만 수행하도록 많은 상태부분들을 Props로 넘겨주었다.

 

즉, 상태끌어올리기가 수행되었고 컴포넌트는 할 수 있는 기능들이 적어져서 멍청해졌다.

 

아래 2개의 컴포넌트가 있다.

무엇이 더 테스터블한 컴포넌트일까?

 

1.

const Comment = ({
  user,
  projectOwnerId,
  comment,
  isVisibleCommentOption,
  iAmAdmin,
  iAmGuestUser,
  thisCommentIsWrittenByAdmin,
  thisCommentIsWrittenByGuest,
  thisCommentIsMine,
  isSubComment,
  alreadyLiked,
  hasSubComments,
  hasLikingUser,
  canIEdit,
  canIDelete
}: Props) => {
   ...
   return <>...</>
}

 

2.

const Comment = ({
  comment,
}: Props) => {
   ...
  const { user } = useUser(); // useQuery
  const { projectOwnerId } = useGetProjectOwnerId(); // useQuery
  
  const isVisibleCommentOption = ... ;
  const iAmAdmin = ... ;
  const iAmGuestUser = ... ;
  const thisCommentIsWrittenByAdmin = ... ;
  const thisCommentIsWrittenByGuest = ... ;
  const thisCommentIsMine = ... ;
  const isSubComment = ... ;
  const alreadyLiked = ... ;
  const hasSubComments = ... ;
  const hasLikingUser = ... ;
  const canIEdit = ... ;
  const canIDelete = ... ;
   return <>...</>
}

 

렌더링에 관여할 수 있는 요소들이 굉장히 많다.

1은 그 요소들을 외부에서 평가하여 Props으로 내려보내준것을 기반으로 렌더링을 하는 것이고,

2는 그 요소들을 내부에서 스스로 평가 및 비동기 요청을 하여 렌더링을 한다.

 

나는 1번이 테스터블한 컴포넌트가 될것이라고 생각한다.

그 이유는 다음과 같다.

- 해당 컴포넌트의 동작을 선언적으로 테스트할 수 있고, 어떤 인자를 true, false로 했을때 어떤 상태가 될 것인지 예측가능하다.

- mocking? 할거없이 Props만 조작하면 된다.

- 하지만 Props가 많아진다. => 객체로 한번에 묶거나 커스텀훅이 해당 로직 담당하게해도 되므로 리팩터링의 문제

 

2번이 테스터블한 컴포넌트가 될 것이라고 생각하지 않는 이유는 다음과 같다.

- useUser의 결과가 어떤 user를 반환할것인지 mocking해야하고 + comment의 정보를 조작해서 각 true, false를 조절해야한다.

저렇게 true, false flag가 많은데, user, comment 객체를 조절하면서 맞추려면 해당 flag값이 어떤 논리식으로 이루어져있는지 검증하고 이해까지 해야한다.

- Props가 적긴하다.

 

만약 테스트를 작성하지 않는다면, 작성하고 comment 객체 1개만 내려보내주면 알아서 다해주는 2번이 가장 선언적으로 보이고 매력적으로 느껴진다.

하지만 테스트를 작성한다면...? 기존에 그런 생각들은 이제 테스트 코드 작성하는데 발목을 잡는 요소가 될뿐이다.

 

즉, 테스터블한 컴포넌트는

- 사이드 이펙트가 적고,

- 최대한 렌더링 로직 위주를 담당하고 (상태끌어올리기 + 멍청한 컴포넌트),

- 테스트 코드를 작성하는 사람과 유지보수를 위해서 가독성이 높은 코드를 작성하고 (클린코드)

- 매번 testId로 컴포넌트를 selecting하기보단 aria-label같은 요소로 selecting하는게 좋으므로, 접근성을 고려한

 

컴포넌트이다.

 

즉, 좋은코드 <= 클린코드 <<<<= 테스터블한코드인것이고

우리가 코드를 작성할때 목표를 해야할 최종적인 코드형태인것이다.

 

앞으로도 코드를 작성할때, 어떻게 해야 테스터블한 코드를 작성할 수 있을지에 대한 고민을 함께한다면,

클린코드 및 접근성들은 자동으로 따라오게 될 것이다.

 

 

Q. Props가 많아지는게 좋은 컴포넌트인가요?

Props가 많아지는 것은, Props를 객체로 내리는 등의 방법으로 충분히 대체할 수 있고, 나는 많다고 생각해서 나쁜 컴포넌트라고 생각하지 않는다.

 

 

=> 10/04에 생각이 바뀌었다.

컴포넌트에 Props가 많아지는 것은, 상태와 관련된 값들을 모두 커스텀훅으로 빼면서,

Props를 줄이면서, 테스트로 상태와 관련된 인자를 조작하기 용이하게 짤 수 있다는 것을 깨달았다.

Props를 줄이고, 커스컴훅으로 위임하자.

 

 

 

 

profile on loading

Loading...