team logo icon

react에 대해

react 상태관리, props drilling

React에 대해

  • 좋은 상태관리란 무엇일까?

    • props drilling은 무엇일까?

    • 개선할 수 있는 방법은?

  • 렌더링을 효과적으로 관리하려면??


React에서 상태 관리가 필요한 이유

: 단방향 데이터 흐름이라는 특징을 갖는 React는 컴포넌트가 복잡해지고 state가 다양해지면 props가 어디서부터 시작된건지 파악하기 쉽지 않아지고, 유지 보수 또한 어려워지는 현상인 props drilling이 발생하기 쉽다. 이러한 props drilling을 피하기 위해, 의미 없는 리렌더를 피하기 위해 상태 관리가 필요하다. 상태 관리에 따라 유지보수 관점에서 코드의 라이프 사이클 또한 결정될수 있으므로 어떤 상태 라이브러리를 쓰며 어떤 구조로 상태를 설계해서 다루느냐가 중요하다.

상태 관리 라이브러리 비교


  1. Redux

  • 가장 처음 라이브러리로, 자바스크립트 앱에서 상태(state)를 효율적으로 관리할 수 있게 도와준다. Redux를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜서 더욱 효율적으로 관리할 수 있다. 또한 컴포넌트끼리 똑같은 상태를 공유해야 할 때도 여러 컴포넌트를 거치지 않고 손쉽게 상태 값을 전달하거나 업데이트할 수 있다.

  • Redux의 등장 이전, 초기의 상태 관리는 MVC(Model-View-Controller) 아키텍처으로 관리했었다. MVC 아키텍처는 모델 상태가 바뀌면 뷰가 바뀌고, 뷰에서 변경이 일어나면 다시 모델 상태가 바뀌어 컨트롤러가 이를 조작하는 양방향 데이터 흐름을 가지고 있어 프로젝트의 규모가 커지고 상태가 많아질수록 관리가 어려웠다. 이를 개선하기 위해 페이스북은 단방향 데이터 흐름의 Flux 아키텍처를 내놓았다. Redux는 Flux 아키텍처를 따르나, Flux에서는 store가 여러 개였지만, Redux에서는 하나의 store에 여러 개의 reducer가 존재한다는 차이점이 있다.


🏁 장점

  • 단방향 모델링이기 때문에 데이터 흐름을 예측하기 쉬워 디버깅에 유리한다.

  • action을 dispatch 할때마다 기록이 남아 이전 상태로 돌아갈수 있다.

  • 스토어라는 이름의 전역 자바스크립트 변수를 통해 상태를 한 곳에서 관리하는 상태의 중앙화라는 특징을 지녀, 전역 상태를 관리할 때 효과적이다.


    🏁 단점

  • 액션 등을 미리 만들어놔야 하기 때문에 아주 작은 기능이여도 리덕스로 구현하는 순간 몇 개의 파일들을 필수로 만들어야하여 코드량이 늘어난다.

  • Redux는 상태를 읽기 전용으로 취급할 뿐, 실제 읽기 전용으로 만들어주지는 않는다. 때문에 상태를 실수로 직접 변경하지 않도록 항상 주의해야 한다.


    🏳️ Redux를 사용하면 좋은 때

    • 앱의 여러 위치에서 필요한 많은 양의 상태들이 존재하여 전역 상태가 필요하다고 느낄 때

    • 상태들이 자주 업데이트 되고 업데이트 로직이 복잡할 때

    • 상태가 업데이트되는 시점을 관찰할 필요가 있을 때

    • Flux 패턴을 이용한 선언적이고 안정적인 상태 운용을 원할 때


    🏳️ MobX

  • Redux의 단점을 보완하여 나온 또 다른 상태 관리 라이브러리로, 객체 지향적 느낌이 더 강하다. 또한 Immutable.js와 같은 불변성을 유지하기 위한 라이브러리를 사용할 필요가 없다는 점에서 Redux와 차이를 보인다.

    🦴 장점

    • 객체 지향적이고 캡슐화를 지원하기에 개발자 친화적이다.

    • Redux에서 제공하지 않는 반응형 메커니즘을 제공하기에 조금 더 쉽게 동적 웹앱 제작이 가능하다.

    🦴 단점

    • 웹앱 규모가 커지면서 로직이 MobX의 자동 업데이트에 의존하기에 디버깅이 조금 더 어려워질 수 있다.

    • Validation 구현에 있어 코드가 번잡스럽다.

    🥕 MobX를 사용하면 좋을 때

    • 보다 객체 지향적인 설계를 원할 때

    • 불변성을 신경쓰지 않고 최적화를 원할 때


    🦴 Recoil

  • Redux, MobX 등의 서드파티 라이브러리와 다르게 오직 리액트 만을 위해 생겨난 라이브러리로, Facebook사에서 개발하였다. 각각의 전역 상태에 대한 작은 데이터 조각인 atom이 생성하고 해당 상태를 구독하는 구성 요소만 리렌더링 하는 단순한 로직이 특징으로, 불필요한 리렌더링을 방지할 수 있다.

  • 장점

    • 가장 간단한 구조를 가지고 있어 초심자에게 좋다.

    • componenet가 렌더링 되는 시기, 상태 등을 세밀하게 제어할 수 있어 성능 최적화 등에도 사용 가능하다.

    • 동적이 기능을 더 쉽게 구현 가능하다.

  • 단점

    • 가장 최신의 라이브러리로 커뮤니티가 활성화되지 않아 이슈 혹은 버그를 해결하기 어려우며, 실험적인 API들이 많고 아직 안정성에 우려가 있다.

    • 상태 관리 자체가 세분화되어 있어 디버깅과 테스트가 어렵다.


      🥕 Recoil을 사용하면 좋을 때 🥕

    • 작고 가벼운 프로젝트에서.

    • 글로벌한 내용들은 전역에서 redux를 사용하여 관리하고, 그 외 데이터들은 관심사의 분리로서 atom에서 관리하는 방식을 사용하기도 한다.


렌더링을 효과적으로 관리하는 방법은 무엇이 있을까?

렌더링이란 코드로 정의된 내용이 실제 브라우저 화면에 그려지는 과정을 의미한다. React에서는 state,props의 교체, 부모 컴포넌트의 재렌더링, 중앙 상태값 변화, render() 함수 호출 등에 의해 렌더링이 촉발된다.

React에서 이러한 렌더링을 효과적으로 관리하기 위해서, 다음과 같은 방법을 사용할수 있다.

1. State 선언 고려하기

  • state 선언 위치

    • React에서는 특정 state가 변경되면 그 state가 선언된 컴포넌트와 하위 컴포넌트가 모두 리렌더링 된다. 따라서 state가 선언되는 위치는 리렌더링 횟수에 영향을 끼치므로 중요하다. 효과적인 렌더링 관리를 위해, state는 해당 state를 사용하는 컴포넌트들 중 가장 최상위 컴포넌트에 선언한다.

  • state 분할 선언

    • 복잡한 객체로 선언된 state를 분할하지 않으면, 하위 컴포넌트가 사용하지 않는 다른 프로퍼티의 값이 업데이트 될 때에도 리렌더링이 발생한다. 따라서 복잡하고 큰 객체 구조인 경우 최대한 분할하는 것이 좋다.

2. React.memo 함수를 이용한 컴포넌트 메모이제이션

  • cf) shouldComponentUpdate() : 클래스형 컴포넌트에서 리렌더링 여부를 결정하는 로직을 만드는 생명주기 메서드.

  • React.memo()

    • 생명주기 메서드를 사용할 수 없는 함수형 컴포넌트에서 shouldComponentUpdate()를 대신하여 사용하는, 컴포넌트를 래핑하여 props를 비교하여 리렌더링을 막을 수 있는 메모이제이션 기법을 제공하는 함수. (Hook이 아니라 함수이다)

    • 메모이제이션 기법을 사용해 불필요한 자식 컴포넌트의 렌더링을 방지하며 성능을 향상시킬 수 있다.

      • Memoization : 기존에 수행한 연산의 결과 값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법. 중복 연산을 피하여 성능을 최적화 할 때 사용한다.*

3. 고유 key를 사용한 컴포넌트 매핑

  • 컴포넌트를 매핑할 때 배열의 index가 아닌, 고유 key 값을 부여한다.

  • 인덱스를 사용한 매핑은 중간에 요소가 삽입될 시 그 이후 인덱스부터 key 값의 변경이 일어나 리마운트가 일어날수 있으므로 효과적인 렌더링을 위한 방식이 아니다.

4. useMemo(), useCallback() 등의 hook 사용하기

  • useMemo() : 메모이제이션 된 값을 반환하는 Hook

    • 종속 변수들이 변하지 않으면 함수를 다시 호출하지 않고 이전에 반환한 참조값을 재사용하여 렌더링을 방지하며 함수 호출 시간을 세이브할 수 있다.

    useMemo(() => fn, [deps]);
  • useCallback() : 메모이제이션된 함수를 반환하는 Hook

    • 종속 변수들이 변하지 않으면 함수를 재생성하지 않고 이전에 있던 참조 변수를 그대로 하위 컴포넌트에 props로 전달하여 props가 변경되지 않아 하위 컴포넌트의 리렌더링을 방지할 수 있다.

    useCallback(fn, [deps]);

Props Drilling이란?

  • Props Drilling이란? : 단방향 데이터 흐름이라는 리액트의 특성 상, props를 전달하기 위해서 하위 컴포넌트를 거쳐야 한다. 이때 props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트를 거치며 데이터를 전달하는 과정이 발생하기도 하는데, 이러한 현상을 props Drilling 이라 한다.

    Props Drilling 해결 전략에는 어떤 것이 있을까?

  • 전역 상태관리 라이브러리 사용

    • 3주차 생각과제에서 다뤘던 Redux, Mobx, Recoil 과 같은 상태관리 라이브러리 혹은 React에서 제공하는 Context API를 활용하여 props drilling으로 발생하는 문제를 해결 가능하다.

    • ‘전역 상태’라는 말에서 알 수 있듯이, 전역 상태관리 라이브러리를 사용하면 특정 컴포넌트가 아닌 중앙 State관리소에서 State를 생성하고, State가 필요한 컴포넌트가 어디에 위치하든 상관없이 State를 불러와 사용할 수 있다는 장점이 있다.

  • 2. children을 적극적으로 사용

    • children 이란?

      • 어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올지 미리 예상할 수 없는 경우가 있습니다. 범용적인 '박스'역할을 하는 sidebar혹은 Dialog와 같은 컴포넌트에서 특히 자주 볼 수 있다. (리액트 공식 문서 설명)

      • 태그와 태그 사이의 모든 내용을 표시하기 위해 사용되는 특수한 Porops

    • props.children은 주로 자식 컴포넌트 또는 html 엘리먼트가 어떻게 구성되어있는지 모르는데 화면에 표시해야 하는 경우 사용된다.

최신 아티클
lighthouse에 대해
문성희
|
2024.05.13
lighthouse에 대해
lighthouse에 대해
prettier, eslint, styleLint에 대해
이진
|
2024.05.10
prettier, eslint, styleLint에 대해
4주차 공유과제
Article Thumbnail
박채연
|
2024.05.10
Prettier, ESLint, StyleLint
prettier, eslint, stylelint