원문 링크 - https://medium.com/@chrlschn/is-this-the-easiest-2-min-refactoring-to-improve-your-react-code-5a5d9e0547f0
이 글은 위의 원문을 번역한 글입니다.
혹시 리액트 코드를 작성하면서, 삼항 연산자를 엄청나게 사용되는 코드를 작성한 적이 있나. 삼항 연산자는 분명 유용 하지만 남발하게 되면 가독성이 매우 떨어지게 된다. 나의 경우로 말을 하면 사실 삼항 연산자가 2개 이상 중첩되면 바로 의미 이해가 어렵다. 하지만 분명 의미적으로 삼항 연산자가 여러개여야 하는 경우, 즉 분기가 조건의 조건의 조건 등을 타고 가야하는 경우는 무조건 있기 때문에 그러한 상황에서 어떻게 코드를 작성하면 유지보수에 용이한 코드를 작성할 수 있는지 설명하겠다.
예제 코드
우선 위에 코드가 어떤 플로우를 가지고 있는지 생각해보자.
- signUpState가 default 이면 null을 아니면 A를 반환한다.
- A는 signUpState가 warning이면 를 아니면 B를 반환한다.
- B는 signUpState가 info이면 를 아니면 C를 반환한다.
- C는 signUpState가 success이면 D를 아니면 를 반환한다.
- D는 user.Internal이 참이면 을 아니면 을 반환한다.
이걸 좀 풀어서 생각하면 결국, signUpStater를 판단해서 그에 맞는 컴포넌트를 반환하고, 특별하게 signUpState가 success인 경우에 내부폼인지 외부폼인지를 판단하겠다는 뜻이다.
자 이걸 바꿔보자.
이 방식은 간단하다. 삼항 연산자로 판단하던 조건을 컴포넌트 안에서 판단하도록 구현하는 것이다.
위의 설명에서 5번 로직인 user.Internal 조건을 가지고 null을 리턴할지, 컴포넌트를 리턴할 지를 컴포넌트 내부에서 결정하도록 구조를 바꾸었다. 놀랍게도 이것이 OOP의 캡슐화 개념이다. 물론 위의 컴포넌트 역시 2개의 컴포넌트로 분리하는 것도 가능하지만 현재 상황에서 그렇게 작업하는 것은 더 지저분하게만 할 뿐이다. 현재 방식이 모듈화에 더 가까운 코드 형태이다.
이제 전체 코드는 이렇게 바뀌었다.
이러한 방식의 장점은 컴포넌트의 구성을 볼때 어떤 조건들이 있고 각 컴포넌트에 영향을 미치는지 파악하기 어렵다는 문제를 해결해준다. 그리고 위에 코드는 아직 signUpState 라는 조건에 대한 로직을 옮기진 않았다. 다시한번 코드 구조를 바꿔볼 수 있겠다. 과정은 생략하고 결과 코드를 아래 첨부하겠다.
처음보다는 낫지만, signUpState
를 컴포넌트 안으로 넣지 않는 한 마지막 남아있는 삼항연산자 한 개는 제거할 수 없다. 또 만약 signUpState
를 컴포넌트 안으로 넣게 된다면 의존성이 바인딩 된다. 우리는 과 이 signUpState
상태를 알아야 하는지에 대한 확신이 없는한 의존성을 분리시키는 것이 더 깔끔하다. 그렇다면 어떻게 해야할까
무슨 말일까? 기껏 컴포넌트 안으로 조건에 대한 로직을 옮겨서 삼항연산자를 제거해놓고서는 다시 레이아웃에 로직을 나두라니? 당연히 이전 상태로 돌리라는 뜻은 아니다. 위에 방식으로 바꾼 구조의 결과를 보면 코드가 어떤 로직을 원하는지 이해하기 어렵진 않다. 하지만 어떤 동작을 하는지 정확하게 파악하기 위해서는 각각의 컴포넌트를 하나하나 확인해야 한다. signUpState가 어떤 String을 가질때 해당 컴포넌트가 렌더링 되는지 등을 파악하기 위해선 말이다. 그래서 조건을 기준으로 분기하는 로직은 컴포넌트 안으로 넣 돼, 어떤 조건인지는 최종코드에서 볼 수 있도록 바꾸라는 말이다.
이렇게 코드 구조를 바꾸면 엄청나게 코드를 유지보수하기 쉬워진다.
이러한 방식을 이용하면 Generic한 컴포넌트를 아래와 같이 구성할 수도 있다.
<Conditional
when="() => {...}"
>
<CardContent>...</CardContent>
</Conditional>
이렇게 하면 코드가 복사되는 양을 줄일 수 있다는 장점은 있지만, 레이아웃에 복잡도는 조금 더 올라가긴 한다. 상황에 맞게 잘 사용하자.
또 만약 useEffect
를 props로 넘겨야하는 상황에는 지금까지 방식은 작동하지 않는다. 그런 상황엔 아래 방식을 활용하자.
{condition && <CardContent />}
여담으로 위의 코드 방식이 뭔가 익숙하다고 느끼는 사람이 있을 수 있다. 아마 Vue.js를 공부하고 사용해본 사람이면 비슷하다고 느낄 수 있다. 아래는 Vue.js 방식을 보여주겠다.
Vue.js는 기본적으로 무조건 이렇게 하도록 되어있다.