Pie_Archive

[React] 리액트에서 멋지게 클래스 사용하기 본문

Front End

[React] 리액트에서 멋지게 클래스 사용하기

코딩파이 2022. 5. 28. 02:30

들어가기 전에

 

글 전에 자극적인 제목 죄송합니다.

이 글은 정답이 아닙니다.

최근 공부하면서 여러 프론트엔드 개발자분들께 여쭤본 내용과 생각을 바탕으로 적은 글입니다.

한 사람의 의견이며, 피드백은 언제나 환영합니다.


0. 개요

 

최근 리액트로 프로젝트를 구성하면서

아키텍쳐, 테스트코드 등 여러 부분에 대해 공부하고 고민하는 중이었다.

특히 최근에 비즈니스 로직을 분리하여 코드 가독성과 재사용성을 높이는 방향에 고민중인데,

그러다보니 자연스럽게 함수형 프로그래밍과 메서드를 활용하게 되었다.

 

거기에 초안 작성을 끝낸 코드를 최적화 하기 위해 useCallback / memo를 활용하여 재랜더링을 막으려는데,

useCallback과 memo를 정확히 사용했음에도 계속 컴포넌트가 재랜더링되는 문제가 발생하였다.

 

결국 재랜더링 문제의 원인은 인스턴스 생성 때문이었다.

프로그램의 가장 상위 파일인 Index에서 클래스의 인스턴스를 생성하지 않고 필요한 컴포넌트 중간부터 생성, 하위 컴포넌트로 전달해주는 형태로 사용했었는데, 인스턴스들은 memoization 되지 않기 때문에 발생한 문제로 생각된다.

 

테스트로 작성한 구조. 이러한 구조를 예시로 들 것이다.

이런식으로 사용했었는데, 계속 Component가 재랜더링되는 문제가 있었다.

 

결국 원인을 찾아서 문제는 해결했지만

"그럼 클래스의 인스턴스는 Index에서 하위 컴포넌트로 전달해야 되는가?"

혹은

"각 필요한 컴포넌트에서 인스턴스를 생성/사용해도 되는가?"

 

라는 React에서 클래스의 근본적인 사용법에 대해 의문이 생겼다.


1. 혼자 고민하기

 

예시용 프로젝트 구조

위와 같은 구조를 예시로 두 가지의  타입의 코드를 짰다.

 

1. Index에서 인스턴스를 생성하여 마지막 Component까지 전달해주기.

class Awesome {
  print() {
    console.log("Awesome!")
  }
}
//인스턴스 생성
const awesome = new Awesome();

const App1 = ( { awesome } ) => {
  awesome.print() // 여기서 한번 사용되고
  return(
    <Page awesome={awesome}/>
  )
}

const Page = ( { awesome } ) => {
  return(
    <Component  awesome={awesome} />
  )
}

const Component = ( { awesome }) => {
  awesome.print() // 여기서 두번째 사용됩니다.
  return(
    <></>
  )
}

 

이 방법은 성능을 우선고려한 방법이다.

전체 프로젝트 구조에서 인스턴스가 단 한번만 생성되기 때문에 불필요한 인스턴스 생성을 줄이고, 성능을 최적화 시킬 수 있다.

그러나, 사용하지 않는 컴포넌트에서도 하위 컴포넌트에서 사용하기 위해 props를 전달해주는 방법은 여간 번거로운 일이 아닐 수 없다.

 

2. 각 클래스가 필요한 컴포넌트에서 인스턴스 생성해주기

class Print {
  do() {
    console.log("Print!")
  }
}

const App1 = ( ) => {
  const print = new Print();
  print.do() // 여기서 한번 사용되고
  return(
    <Page print={print}/>
  )
}

const Page = ( ) => {
  return(
    <Component />
  )
}

const Component = () => {
  const print = new Print();
  print.do() // 여기서 두번째 사용됩니다.
  return(
    <></>
  )
}

클래스가 필요한 부분만 따로 인스턴스를 생성 한 방법이다.

사실 위 방법보다 좀 덜 최적화 된 부분은 있겠지만,

Props 전달이 번거로워서 Redux / Mobx 같은 상태관리 라이브러리로 쓰는 상황

이 방법 또한 오답이라고 할 순 없다고 생각했다.

 

아무래도 검색결과도 시원찮고, 원하는 답을 얻지 못했기에 결국 프론트엔드 개발자 분들께 질문드려봤다.


2. 같이 고민하기 (물어보기)

 

분명 회사에서 맞닥드린 문제지만 아쉽게 우리 회사엔 리액트 시니어분이 계시지 않는다.

때문에 종종 도움을 받는 프론트엔드 오픈채팅방에 관련된 고민과 테스트코드를 질문드렸으며,

답변 결과는...

 

두 방법에 대해 명확한 정답은 없으며,

프로젝트 디자인 패턴에 따라 1, 2번의 방법을 선택하여 사용하면 된다는게 결론이었다.
또 정답을 기대했는가? 그런건 없다 ! 하하!

 

관련되어 추가로 들은 몇 가지 조언이 있었는데,

  •  로직을 담당하는 ContainerUI를 담당하는 Presenter로 나누는 Container/Presenter 디자인 패턴에선 Container에서만 따로 인스턴스를 생성하는 방식을 자주 사용한다.
  • 비즈니스 로직을 분리하는 것 (인스턴스를 따로 생성하는 것)은 의존성과 복잡성을 낮추기 때문성능 이슈보다 우선적으로 고려할 만 하다.
  • 결국 각 디자인 패턴과 프로젝트 구조에 맞게 적절한 것을 선택해라 (항상 이런 결론이 나온다) (...)

 

위 조언 사항을 고려하여 결국 나는

이번 프로젝트에서 2번 방법으로, 각 필요한 컴포넌트마다 인스턴스를 따로 생성 하기로 했다.

이렇게 결론을 내린 이유는,

  • 이번 프로젝트 설계에서 컴포넌트의 재사용성을 위해  Atomic Design 패턴으로 프로젝트를 구성하였다.
  • 때문에 컴포넌트 Depth가 꽤 깊은 편이기 때문에 유지보수를 위해 간결한 방식을 고려했다.
  • 추가로 핑계를 대자면, 프로젝트 편의성을 위해 Mobx를 사용했는데 인스턴스 전달에서 편의성을 포기하고 싶지 않았다.

3. 결론

 

정답을 기대하고 오신 분들께는 죄송하지만, 그나마 제가 내린 답을 드리자면

좀 더 성능적인, 특별한 디자인 패턴을 공부하기 전까진 편하게 인스턴스를 생성하는 방식으로 코드를 짜는게

더 나을것 같다는 생각이다.

 

또 성능도 성능이지만, 코드를 잘 짜기 위해선 의존성이나 복잡성 등 프론트엔드 개발자가 우선적으로 고려 할 부분들이 꽤 많다는 것도 느끼게 되었고,

오늘도 물음의 정답을 찾지 못하여  React의 자유도가 괜히 미워졌지만, 어떤 코드를 짜든 천천히 공부하면서  꼭 이유있는 코드를 짜야된다고 좀 더 느끼는 순간이었다.