[ 앱 개발자 도전기 : 크로스플랫폼_ReactNative ] React Native 공부 3-4.

0

App_Dev : RN

목록 보기
9/17
post-thumbnail

[ 앱 개발자 도전기 : 크로스플랫폼_ReactNative ] React Native 공부 3-4.

▽React Native 공부 3-4.

목  차

1. 리엑트 네이티브(RN)의 동작 원리.
2. 리엑트 네이티브(RN)의 플랫폼 파트 ( 구성 요소 )
3. 리엑트 네이티브(RN)의 애플리케이션 동작 순서 및 뉴 아키텍처.
4. 리엑트 네이티브에서 상태관리는 어떻게?
------------
5. 리엑트 네이티브에서 에러 핸들링과 디버깅은 어떻게?
6. 네이티브 모듈을 사용하여 기기의 하드웨어 기능에 어떻게 접근하는가?

Ⅳ. 리엑트 네이티브에서 상태관리는 어떻게?


목  차

1. 리엑트 네이티브 상태 관리 개요.
2. 상태 관리의 필요성
3. 내장 상태 관리 방법들.
    3-1. useState
    3-2. this.setState
4. Context API
5. 외부 라이브러리 활용.
    5-1. Redux.
    5-2. Recoil.
    5-3. MobX.
    5-4. Zustand.
6. 상태 관리 모범 사례
7. 결론 및 요약

1. 리엑트 네이티브 상태 관리 개요.


  • 리액트 네이티브에서 상태 관리는 앱의 데이터를 효율적으로 관리하고,
    UI를 동적으로 업데이트하는 데 중요한 역할을 합니다.

  • 특히, 복잡한 앱을 개발할 때 상태 관리가 더욱 중요해집니다.
    상태 관리에는 여러 가지 방법과 라이브러리가 있으며, 각기 다른 장점과 사용 사례가 있습니다.

  • 리액트 네이티브는 내장된 상태 관리 방법과
    외부 라이브러리를 통해 다양한 상태 관리 요구를 충족시킬 수 있습니다.

  • 내장된 방법으로는 useState와 this.setState가 있으며,
    외부 라이브러리로는 Redux, Recoil, MobX, Zustand 등이 있습니다.

2. 상태 관리의 필요성.


상태 관리는 리액트 네이티브 앱에서 필수적인 요소입니다.

  • 상태 관리가 필요한 이유는 다음과 같습니다.

    • 동적 UI 업데이트:

      • 사용자의 인터랙션에 따라 UI를 실시간으로 업데이트해야 하는 경우가 많습니다.
      • 상태 관리를 통해 이러한 동적 업데이트를 효율적으로 처리할 수 있습니다.
    • 데이터 일관성 유지:

      • 여러 컴포넌트 간에 데이터를 공유할 때,
        상태 관리를 통해 데이터의 일관성을 유지할 수 있습니다.
    • 복잡한 로직 처리:

      • 복잡한 비즈니스 로직을 처리할 때, 상태 관리가 필수적입니다.
      • 상태를 통해 다양한 조건과 규칙을 적용하여 로직을 구현할 수 있습니다.
    • 성능 최적화:

      • 상태 관리를 통해 불필요한 렌더링을 방지하고, 성능을 최적화할 수 있습니다.

3. 내장 상태 관리 방법들.


'리액트 네이티브'는 내장된 상태 관리 방법을 제공합니다.

이 방법은 리액트의 기본 기능을 활용하여 '상태'를 관리하는 데 사용됩니다.
내장 상태 관리 방법에는 'useState', 'this.setState', 'useReducer' 가 있습니다.

3-1. useState.

작동 방식.

  • useState의 역할 :
    • useState는 함수형 컴포넌트에서 상태를 관리하는 데 사용됩니다.
    • 이 훅은 초기 상태를 설정하고, 상태를 업데이트하는 함수를 반환합니다.
  • 구조 분해 할당 :
    • useState는 구조 분해 할당을 통해 상태 값과 상태를 업데이트하는 함수를 제공합니다.
      • ex) const [count, setCount] = useState(); 와 같이 사용.
  • 작동 방식 :
    • useState는 컴포넌트가 렌더링 될 때 리엑트의 내무 메모리에 상태를 저장합니다.
    • 상태를 업데이트하는 setState 함수를 호출하면,
      리액트는 해당 컴포넌트를 다시 렌더링하여 화면을 업데이트합니다.

장점 및 한계.

  • 장점:
    • 간단하고 사용하기 쉽습니다.
    • 함수형 컴포넌트에서 유용합니다.
    • 상태를 업데이트할 때 자동으로 UI를 갱신합니다.
  • 단점:
    • 복잡한 상태 관리에는 적합하지 않을 수 있습니다.
    • 여러 컴포넌트 간의 상태 공유가 어렵습니다.

실무 예시.

import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';

const CounterApp = () => {
  const [count, setCount] = useState(0);

  return (
    <View>
      <Text>{count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
    </View>
  );
};

3-2. this.setState.

작동 방식.

  • this.setState의 역할 :
    • this.setState는 클래스 컴포넌트에서 상태를 업데이트하는 데 사용됩니다.
    • 이 메서드는 상태를 변경하고, 컴포넌트를 재렌더링합니다.
  • 작동 방식 :
    • this.setState는 상태를 업데이트할 때,
      컴포넌트의 render 메서드를 호출하여 UI를 갱신합니다.

장점 및 한계.

  • 장점:

    • 클래스 컴포넌트에서 유용합니다.

    • 상태를 업데이트할 때 자동으로 UI를 갱신합니다.

  • 한계:

    • 함수형 컴포넌트에서는 사용할 수 없습니다.

    • 복잡한 상태 관리에는 적합하지 않을 수 있습니다.

실무 예시.

import React, { Component } from 'react';
import { View, Text, Button } from 'react-native';

class CounterApp extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <View>
        <Text>{this.state.count}</Text>
        <Button title="Increment" onPress={() => this.setState({ count: this.state.count + 1 })} />
      </View>
    );
  }
}

3-3. useReducer.

작동 방식.

  • useReducer의 역할 :
    • useReducer는 복잡한 상태 논리를 관리하는 데 사용됩니다.
    • 이는 useState와 유사하지만, 상태를 업데이트하는 로직이 더 복잡할 때 유용합니다.
  • 작동 방식 :
    • 'useReducer'는 상태를 업데이트하는 함수인 '리듀서'와 초기 상태를 받습니다.
    • 상태를 업데이트할 때, 리듀서 함수가 호출되어 상태를 변경합니다.

장점 및 한계.

  • 장점:

    • 복잡한 상태 논리를 쉽게 관리할 수 있습니다.

    • 상태의 흐름을 명확히 파악할 수 있습니다.

  • 한계:

    • 초기 설정이 복잡할 수 있습니다.

    • 상태 관리가 간단한 경우에는 불필요할 수 있습니다.

실무 예시.

import React, { useReducer } from 'react';
import { View, Text, Button } from 'react-native';

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const CounterApp = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <View>
      <Text>{state.count}</Text>
      <Button title="Increment" onPress={() => dispatch({ type: 'increment' })} />
      <Button title="Decrement" onPress={() => dispatch({ type: 'decrement' })} />
    </View>
  );
};

4. Context API.


context API는 여러 컴포넌트 간에 데이터를 공유할 때 유용합니다.

이는 prop driling을 피하고, 전역적으로 데이터를 관리하는 데 적합합니다.

4-1. Context API의 작동 방식.

  • createContext : context를 생성하는 함수입니다.
    • 이 함수는 Context의 기본값을 설정할 수 있습니다.
  • Provider : Context의 값을 하위 컴포넌트에 전달하는 역할을 합니다.
    • Provider 컴포넌트는 value 속성을 통해 Context의 값을 설정합니다.
  • Consumer : Context의 값을 사용하는 컴포넌트입니다.
    • Consumer는 Provider가 제공하는 값을 사용하여 데이터를 렌더링합니다.
  • useContext : Consumer 대신 사용할 수 있는 훅으로,
    • Context의 값을 더 쉽게 사용할 수 있도록 합니다.

4-2. 실무 예시.

import React, { createContext, useState } from 'react';
import { View, Text } from 'react-native';

const ThemeContext = createContext();

const App = () => {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
};

const Toolbar = () => {
  const { theme } = useContext(ThemeContext);

  return (
    <View>
      <Text>Current theme: {theme}</Text>
    </View>
  );
};

5. 외부 라이브러리 활용.


5-1. Redux.

개요.

  • Redux는 JS 애플리케이션의 상태를 예측 가능한 방식으로 관리하는 라이브러리입니다.
    • 특히, 복잡한 상태 관리에 적합하며, 리엑트 네이티브와도 잘 통합됩니다.

작동 방식

  • 중앙화된 상태 :

    • Redux는 애플리케이션의 모든 상태를 하나의 JS 객체인 '스토어'에 저장합니다.
      • 이는 상태를 관리하고 디버깅하는 데 유용합니다.
  • 액션과 리듀서 :

    • 상태를 업데이트하려면 '액션'을 디스패치해야 하며,
      '리듀서' 함수가 이 액션을 처리하여 상태를 업데이트합니다.

      • 액션.

        • 액션의 역할 :
          • '액션'은 상태를 업데이트하는 요청을 나타냅니다.
          • 예를 들어,
            버튼을 클릭하면 'INCREMENT'라는 액션이 디스패치 될 수 있습니다.
        • 액션의 구조 :
          • '액션'은 일반적으로 'type' 필드를 가지며,
            이는 리듀서에서 어떤 작업을 수행할지 결정합니다.
      • 리듀서.

        • 리듀서의 역할 :

          • '리듀서'는 액션을 처리하여 상태를 업데이트합니다.
            • 리듀서는 불변성을 유지해야 하며, 상태를 직접 수정하지 않습니다.
        • 리듀서의 구조 :

          • '리듀서'는 현재 상태와 액션을 입력으로 받아 새로운 상태를 반환합니다.

장점 및 한계.

  • 장점:

    • 복잡한 상태 관리에 적합합니다.

    • 상태의 흐름을 명확히 파악할 수 있습니다.

    • 문서화가 잘 되어 있고 커뮤니티가 활발합니다.

  • 한계:

    • 초기 설정이 복잡할 수 있습니다.

    • 보일러플레이트 코드가 많을 수 있습니다.

    • 성능이 다른 라이브러리보다 떨어질 수 있습니다.

실무 예시.

import { createStore } from 'redux';
import { Provider } from 'react-redux';

const initialState = { count: 0 };

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    default:
      return state;
  }
};

const store = createStore(reducer);

const App = () => {
  return (
    <Provider store={store}>
      <CounterApp />
    </Provider>
  );
};

5-2. Recoil.

개요.

  • Recoil은 React의 Context API를 기반으로 구축된 상태 관리 라이브러리입니다.
  • 가벼운 구조와 인상적인 성능을 자랑하며, 사용자 친화적인 특성이 특징입니다.

작동 방식.

  • atoms: 상태의 기본 단위로, 업데이트와 구독이 가능합니다.

  • selectors: atoms나 다른 selectors를 입력으로 받아 파생된 상태를 제공하는 함수입니다.

atoms
  • atom의 역할: atom은 상태의 기본 단위로, 업데이트와 구독이 가능합니다.

  • atom의 구조: atom은 key와 default 값을 가지며, 이는 상태의 식별자와 초기 값을 정의합니다.

selectors
  • selector의 역할:

    • selector는 atoms나 다른 selectors를 입력으로 받아 파생된 상태를 제공하는 함수입니다.
  • selector의 구조: selector는 get 함수를 통해 상태를 계산하고,

    • set 함수를 통해 상태를 업데이트할 수 있습니다.

장점 및 한계.

  • 장점:

    • 간단하고 유연한 API를 제공합니다.

    • 성능 최적화에 적합합니다.

    • 기존 코드와 원활하게 통합됩니다.

  • 한계:

    • 상대적으로 새로운 라이브러리라서 커뮤니티가 작을 수 있습니다.

    • 문서가 일부 경쟁 라이브러리만큼 포괄적이지 않을 수 있습니다.

실무 예시.

import { atom, useRecoilState } from 'recoil';

const countState = atom({
  key: 'countState',
  default: 0,
});

const CounterApp = () => {
  const [count, setCount] = useRecoilState(countState);

  return (
    <View>
      <Text>{count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
    </View>
  );
};

5-3. MobX.

개요.

  • MobX는 반응형 프로그래밍을 지원하는 상태 관리 라이브러리입니다.
  • 상태 변경 시 자동으로 UI를 업데이트합니다.

작동 방식.

  • observable:

    • 상태를 관찰 가능한 객체로 만듭니다.
  • action:

    • 상태를 수정하는 함수입니다.
  • reaction:

    • 상태 변경에 반응하는 사이드 이펙트입니다.
observable.
  • observable의 역할 :
    • observable은 상태를 관찰 가능한 객체로 만듭니다.
  • observable의 구조 :
    • observable은 @observable 데코레이터를 사용하여 상태를 정의합니다.
action.
  • action의 역할 :
    • action은 상태를 수정하는 함수입니다.
  • action의 구조 :
    • action은 @action 데코레이터를 사용하여 상태를 업데이트하는 함수를 정의합니다.

장점 및 한계.

  • 장점:

    • 반응형 프로그래밍을 지원합니다.
    • 간단하고 확장 가능합니다.
    • 상태 변경 구독이 용이합니다.
  • 한계:

    • Redux만큼 잘 문서화되어 있지 않습니다.
    • 소규모 커뮤니티와 덜 광범위한 third-party 라이브러리 생태계입니다.

실무 예시.

import { observable, action } from 'mobx';
import { observer } from 'mobx-react';

class CounterStore {
  @observable count = 0;

  @action
  increment() {
    this.count++;
  }
}

const store = new CounterStore();

const CounterApp = observer(() => {
  return (
    <View>
      <Text>{store.count}</Text>
      <Button title="Increment" onPress={store.increment} />
    </View>
  );
});

5-4. Zustand.

개요.

Zustand는 Context API를 기반으로 하는 가벼운 상태 관리 라이브러리입니다.
특히 중소 규모의 애플리케이션에 적합하도록 설계되었습니다.

작동 방식.

  • create 함수:

    • Zustand 스토어를 생성하는 함수입니다.
  • useStore 훅:

    • Zustand 스토어의 상태와 함수를 사용할 수 있게 해줍니다.
create 함수.
  • create 함수의 역할:
    • create 함수는 Zustand 스토어를 생성하는 데 사용됩니다.
  • create 함수의 구조:
    • create 함수는 상태와 함수를 정의하는 콜백 함수를 받습니다.
useStore 훅.
  • useStore 훅의 역할:
    • useStore 훅은 Zustand 스토어의 상태와 함수를 사용할 수 있게 해줍니다.
  • useStore 훅의 구조:
    • useStore 훅은 스토어의 상태와 함수를 반환합니다.

장점 및 한계.

  • 장점:

    • 간단하고 빠릅니다.
    • 최소한의 보일러플레이트 코드가 필요합니다.
    • 포괄적인 문서와 강력한 지원을 제공합니다.
  • 한계:

    • 대규모 애플리케이션을 디버깅할 때 문제가 발생 가능.
    • 다른 라이브러리의 강력한 기능이 부족할 수 있습니다.

실무 예시.

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}));

const CounterApp = () => {
  const { count, increment } = useStore();

  return (
    <View>
      <Text>{count}</Text>
      <Button title="Increment" onPress={increment} />
    </View>
  );
};

5-5. Jotai.

개요.

  • Jotai는 Recoil과 유사한 원자적 상태 관리를 제공하는 라이브러리입니다.
  • 매우 가벼우며, 간단한 API를 제공합니다.

작동 방식.

  • atom:

    • 상태의 기본 단위로, 업데이트와 구독이 가능합니다.
  • useSetAtom:

    • 상태를 업데이트하는 훅입니다.
atom
  • atom의 역할 :
    • atom은 상태의 기본 단위로, 업데이트와 구독이 가능합니다.
  • atom의 구조 :
    • atom은 atom 함수를 사용하여 상태를 정의합니다.
useSetAtom
  • useSetAtom의 역할 :
    • useSetAtom은 상태를 업데이트하는 훅입니다.
  • useSetAtom의 구조 :
    • useSetAtom은 atom의 상태를 업데이트하는 함수를 반환합니다.

장점 및 한계.

  • 장점:

    • 매우 가벼운 라이브러리입니다.
    • 간단한 API를 제공합니다.
    • 성능이 뛰어납니다.
  • 한계:

    • Recoil보다 커뮤니티가 작을 수 있습니다.
    • 일부 기능이 부족할 수 있습니다.

실무 예시.

import { atom, useSetAtom } from 'jotai';

const countAtom = atom(0);

const CounterApp = () => {
  const [count, setCount] = useSetAtom(countAtom);

  return (
    <View>
      <Text>{count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
    </View>
  );
};

5-6. Valtio.

개요.

-Valtio는 Recoil과 MobX의 영향을 받은 반응형 상태 관리 라이브러리입니다.

  • 간단하고 유연한 API를 제공합니다.

작동 방식.

  • proxy: 상태를 프록시로 감싸 반응형으로 만듭니다.

  • useProxy: 상태를 사용하는 훅입니다.

proxy
  • proxy의 역할:

    • proxy는 상태를 프록시로 감싸 반응형으로 만듭니다.
  • proxy의 구조:

    • proxy는 proxy 함수를 사용하여 상태를 정의합니다.
useProxy
  • useProxy의 역할:

    • useProxy는 상태를 사용하는 훅입니다.
  • useProxy의 구조:

    • useProxy는 proxy의 상태를 반환합니다.

장점 및 한계.

  • 장점:

    • 간단하고 유연한 API를 제공합니다.
    • 성능이 뛰어납니다.
  • 한계:

    • 상대적으로 새로운 라이브러리라서 커뮤니티가 작을 수 있습니다.
    • 일부 기능이 부족할 수 있습니다.

실무 예시

import { proxy, useProxy } from 'valtio';

const state = proxy({ count: 0 });

const CounterApp = () => {
  const snap = useProxy(state);

  return (
    <View>
      <Text>{snap.count}</Text>
      <Button title="Increment" onPress={() => state.count++} />
    </View>
  );
};

5-7. MobX State Tree.

개요.

  • MobX State Tree는 MobX의 간단함과 Redux의 구조적 접근 방식을 결합한 라이브러리입니다. - 복잡한 상태 구조를 관리하는 데 적합합니다.

작동 방식.

  • models: 상태의 구조를 정의하는 모델입니다.

  • actions: 상태를 수정하는 함수입니다.

models
  • model의 역할:
    • model은 상태의 구조를 정의하는 데 사용됩니다.
  • model의 구조: model은 types.model 함수를 사용하여 상태를 정의합니다.
    • model은 상태의 구조를 정의하는 데 사용됩니다.
actions
  • action의 역할:

    • action은 상태를 수정하는 함수입니다.
  • action의 구조:

    • action은 actions 함수를 사용하여 상태를 업데이트하는 함수를 정의합니다.

장점 및 한계.

  • 장점:

    • 복잡한 상태 구조를 쉽게 관리할 수 있습니다.
    • MobX의 반응형 프로그래밍을 활용합니다.
  • 한계:

    • 초기 설정이 복잡할 수 있습니다.
    • 상태 구조가 복잡해지면 관리가 어려울 수 있습니다.

실무 예시.

import { types } from 'mobx-state-tree';

const CounterModel = types.model('Counter', {
  count: types.number,
}).actions(self => ({
  increment() {
    self.count++;
  },
}));

const store = CounterModel.create({ count: 0 });

const CounterApp = observer(() => {
  return (
    <View>
      <Text>{store.count}</Text>
      <Button title="Increment" onPress={store.increment} />
    </View>
  );
});

5-8. Redux Toolkit.

개요

  • Redux Toolkit은 Redux의 복잡성을 줄이고 개발 효율성을 높이는 도구 세트입니다.
  • Redux의 보일러플레이트 코드를 줄여줍니다.

작동 방식.

  • createSlice: 상태와 리듀서를 한 번에 생성하는 함수입니다.

  • configureStore: 스토어를 쉽게 설정하는 함수입니다.

createSlice
  • createSlice의 역할:

    • createSlice는 상태와 리듀서를 한 번에 생성하는 데 사용됩니다.
  • createSlice의 구조:

    • createSlice는 name, initialState, reducers를 정의하여 상태와 리듀서를 생성합니다.
configureStore
  • configureStore의 역할:

    • configureStore는 스토어를 쉽게 설정하는 데 사용됩니다.
  • configureStore의 구조:

    • configureStore는 reducer를 입력으로 받아 스토어를 생성합니다.

장점 및 한계.

  • 장점:

    • Redux의 보일러플레이트 코드를 줄여줍니다.
    • Redux DevTools와 통합되어 디버깅이 용이합니다.
  • 한계:

    • 여전히 Redux의 기본 개념을 이해해야 합니다.
    • 일부 기능이 제한될 수 있습니다.

실무 예시.

import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
    increment(state) {
      state.count++;
    },
  },
});

const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});

const CounterApp = () => {
  const { count, increment } = useSelector((state) => state.counter);
  const dispatch = useDispatch();

  return (
    <View>
      <Text>{count}</Text>
      <Button title="Increment" onPress={() => dispatch(increment())} />
    </View>
  );
};

6. 상태 관리 모범 사례.


1.로컬 상태 사용:

  • 전역 상태를 최소화하고, 필요한 경우에만 사용합니다.
  • 로컬 상태는 컴포넌트 간의 복잡성을 줄이고 디버깅을 용이하게 합니다.

2.불변 데이터 구조 사용:

  • 상태 변경 시 불변 데이터 구조를 사용하여 예측 가능한 상태 변화를 유지합니다.
  • 이는 버그를 줄이고 코드의 안정성을 높입니다.

3.중복 상태 피하기:

  • 상태를 중복하지 않고, 필요한 경우 파생 상태를 사용합니다.
  • 중복 상태는 일관성을 유지하기 어렵게 만들 수 있습니다.

4.테스트 가능성 고려:

  • 상태 관리 방법이 테스트 가능성을 고려하여 설계되어야 합니다.
  • 이는 코드의 신뢰성을 높이고 유지보수성을 개선합니다.

5.최적화 기법 활용:

  • 불필요한 렌더링을 방지하기 위해 useMemo, useCallback 같은 최적화 기법을 활용합니다.

6.상태 구조를 단순화:

  • 깊게 중첩된 상태 구조는 관리하기 어렵습니다.
  • 상태를 평평하게 유지하여 업데이트와 디버깅을 용이하게 합니다.

7.명확한 명명 규칙 사용:

  • 상태 변수와 액션의 명명 규칙을 명확히 정의하여 코드의 가독성을 높입니다.

7. 결론 및 요약.


리액트 네이티브에서 상태 관리는

  • 앱의 데이터를 효율적으로 관리하고, UI를 동적으로 업데이트하는 데 중요한 역할을 합니다.

  • 다양한 상태 관리 방법과 라이브러리가 있으며, 각기 다른 장점과 사용 사례가 있습니다.

  • 내장 상태 관리 방법:

    • useState, this.setState, useReducer는 기본적인 상태 관리 요구를 충족시킬 수 있습니다.
  • 외부 라이브러리:

    • Redux, Recoil, MobX, Zustand, Jotai, Valtio, MobX State Tree, Redux Toolkit 등은
      복잡한 상태 관리에 적합합니다.

상태 관리를 선택할 때는 앱의 복잡성, 성능 요구 사항, 개발자 팀의 경험 등을 고려해야 합니다.

0개의 댓글