문제 상황

react-native-draggable-flatlist 라이브러리를 사용하는데 해당 데이터가 모두 잘 넘어옴에도 불구하고,
renderItem에서 item을 사용하려고 하면 모든 아이템이 렌더되지 않는 상황이 발생했다.

ex) the FlatList does not render all 100 items on initial load.

 

 

 

 

 

 

해결 과정

하나하나 디버깅을 해보자.

item을 찍어봤을 때 처음에는 반 정도 렌더링이 되고, 드래그를 하니 나머지 아이템이 렌더링 되는 상황을 목격했다.

음, 이건 해당 라이브러리 문제라고 생각해서 해당 라이브러리 이슈를 살펴보았다.

 

역시 나랑 똑같은 문제를 겪고 있는 사람이 있었다.

해당 이슈: https://github.com/computerjazz/react-native-draggable-flatlist/issues/453

 

Performance Issue on long lists · Issue #453 · computerjazz/react-native-draggable-flatlist

Hi, I'm facing a huge performance issue with my list. Currently, I'm rendering around 100 fields and the ui just freezes on drag.

github.com

 

 

 

 

문제 해결

처음 렌더링이 될 때 아이템의 갯수를 세팅해준다. 

해당 라이브러리에 initialNumToRender 속성을 사용하자.

initalNumToRender 속성

다시 확인해보면, 모든 아이템이 렌더링 되는 것을 확인할 수 있다.

뿌-듯 👍🏻

 

며칠 전 xCode를 14.1로 업데이트했는데,
 
error Could not get the simulator list from Xcode. Please open Xcode and try running project directly from there to resolve the remaining issues.
Error: Command failed: xcrun simctl list --json devices
xcrun: error: unable to find utility "simctl", not a developer tool or in PATH
에러가 났다.



 
원인
xCode 신규버전을 설치하고 나서 command line tool이 초기화되지 않아서 생기는 문제


2가지 해결법
1. Xcode - Preferences - Locations에서 "Command Line Tools"를 설정해주면 됨.

2. 터미널에서 해당 명령어 사용

$ sudo xcode-select --reset


해결했다!

앱을 launch 하자마자 error Failed to launch emulator. Reason: No emulators found as an output of `emulator -list-avds`.

에러가 발생했음. 

에뮬레이터가 없다는 에러인데, 안드로이드 스튜디오에 들어가보면 에뮬이 잘 동작되고 있음.

 

따라서 앱도 안켜짐.

 

확인해보자.

$  react-native doctor

sdk 경로 문제인 것을 확인

 

터미널에서 zshrc 파일을 편집해준다.

$ vi ~/.zshrc

위와 같이 파일을 열고 아래의 안드로이드 관련 부분 코드를 적어준다.

export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
export PATH=$PATH:$ANDROID_SDK_ROOT/tools
export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools

 

업데이트 내용을 적용시킨다.

$ source ~/.zshrc

 

앱이 잘 실행된다!

앱을 유지보수하는 데 가장 많이 도움되는 것은 디자인 패턴인 것 같다.

처음 만들 때 제대로 잡아놔야 유지보수가 쉽고, 다른 사람도 보기 편-안하다.

 

오늘은 React에 어떤 디자인 패턴이 있는지 알아보고, 스스로 어떻게 쓰고 있는지 얘기해보고자 한다.

 

React 디자인 패턴

1️⃣ Presentational and Container Component Pattern

가장 유명한 패턴이기도 하고, 이제는 구식에 가까운(?) 아무튼 나는 잘 쓰고 있는 패턴이다.

 

Presentaitional 컴포넌트는 화면에 보여지는 것 (즉, UI) 부분만 담당하는 컴포넌트이다.

데이터는 항상 Container 단에서 받아서, 사용자에게 보여질 것만 presenter 단에서 처리한다.

Container 컴포넌트는 동작과 데이터를 담당하는 컴포넌트이다. 따라서 DOM 마크업 구조나 스타일을 가지지 않는다.

 

ex)

// ViewContainer
const ViewContainer = () => {
	<ViewPresenter />
}


// ViewPresenter.tsx
const default () => {
	return (
    	<View>
         <Text>ViewPresenter</Text>
        </View>
    )
}

위의 코드처럼 Container와 Presenter의 관심사를 따로 분리하는 디자인 패턴이다.

 

 

장점

1. 관심사 분리를 잘할 수 있다.

데이터와 스타일이 분리되어 있기 때문에 UI를 더 쉽게 만들 수 있다.

2. 재사용성을 높일 수 있다.

다른 화면에서도 해당 화면이 필요할 때 이를 재사용할 수 있다.

 

 

이제는 추천하지 않는 이유

해당 패턴은 Dan Abramov가 소개한 패턴으로, 더 이상은 자신이 소개한 패턴으로 컴포넌트를 나누는 것을 추천하지 않는다고 한다.

왜냐하면 이제는 이 패턴이 아니여도 hook을 사용해서 로직을 분리할 수 있기 때문이다.

하지만! 나는 아직 여전히 잘쓰고 있다. hook을 사용해서 로직을 분리한 후, container에서 해당 로직들과 동작들을 시켜주고 있기 때문이다.

어느 정도 각자 수용할 것은 수용하고, 버릴 것은 버리면 된다고 생각한다.

 

 

2️⃣ Atomic Design Pattern

- 해당 패턴은 원채 디자인 시스템을 위한 구조로 컴포넌트들을 효율적으로 구성하는 방식을 의미한다.

Atomic Design Pattern은 컴포넌트를 뷰가 아닌 기능 단위로 나눈다. 

 

<UI 구성 요소>

Atom -> Molucule -> organisms -> template -> page

각각 살펴보기로 하자.

 

Atom

- 하나의 구성 요소

ex) form label, inputs, buttons과 같은 basic html elements

 

Molecules

- Atom이 모여서 만들어진 하나의 구성 요소

ex) input label + button

 

Organisms

- 분자들(Molecules)의 모음으로 서로 동일하거나 다른 분자들로 구성된다.

 

Templates

- 유기체(Organisms)를 모아 템플릿으로 생성. 페이지의 기본 내용 구조에 초점을 맞춘다.

 

Pages

- 실제 콘텐츠가 배치된 UI 모습을 보여주는 인스턴스이다.

Pages 단위에서 상태 관리가 이뤄져야 한다. ex) redux, input onChagne를 useState로 관리


 

기존에는 Presentational and Container Component Pattern만 사용하다가 디자인 시스템에 고안된 Atomic Design Pattern도 같이 사용하는 중인데 디자인 관점에서 하나 하나 보게되어 조금 더 재사용성을 생각하며 View를 짜게 된다.

하지만, 하나하나 Atom으로 분리하기에는 너무 많은 컴포넌트를 만들어야 할지도 모른다.. ㅎ

Atomic Design Pattern을 사용하면서 조금 더 깔끔하게 UI를 구성하기 위해 노력하게 되는 것 같아서 다른 사람에게도 알리고 싶은 디자인 패턴이다. 

익숙해지는 데에는 시간이 조금 걸릴지 몰라도 도입하면 뷰가 깔끔해져 기분이 좋아지는 마법 🌷

 

 

 

Reference

React Design Pattern 🎨 https://velog.io/@holim0/React-Design-Pattern

현재 상황

Screen에서 Modal로 이동할 때 해당 데이터 값을 넘겨줘야 하는 상황

 


이때 사용되는 state는 총 2개로

1. 모달을 열어주는 state

2. 데이터 값을 받는 state



이런 상황에서 값을 넘겨주는 방식은 2가지가 있다.

1. props로 전달

2. DeviceEventEmitter 사용

 

 

 

첫 번째 방식은 모달이 mount 됐을 때 상태 값을 2개를 관리해줘야해서 번거로운 반면,

두 번째 방식은 항상 모달이 listen중이니 2개의 state 관리가 필요 없고, 값을 전달 받을 수 있다.

 


 

Screen

import { DeviceEventEmitter } from 'react-native';

DeviceEventEmitter.emit('aaaa', data); // 넘기는 data

 

 

Modal

  import { DeviceEventEmitter } from 'react-native';

  useEffect(() => {
    const subscription = DeviceEventEmitter.addListener(
      'aaaa',
      (data: string) => {
        console.log(data); // data 넘겨 받기
      },
    );

    return () => {
      subscription.remove(); // 컴포넌트가 화면에서 사라지면 listener remove까지 해주자
    };
  }, []);

 

 

문제
문제의 화근은 안드로이드 기기 릴리즈 모드에서만 소셜 로그인이 안되는 것이었다.

도대체 왜!!??!?!?! 
문제의 원인을 모르는 게 정말 큰 문제였다. 

해당 버튼을 눌렀을 때 서버 로그에 안찍히는 걸로 보아 아예 서버로 통신이 안가는 것을 발견했다.

 

해결

/android/app/src/main/AndroidManifest.xml 파일에 

<manifest ...>
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ...
        android:usesCleartextTraffic="true"  // 이 부분을 추가해보자
        ...>
        ...
    </application>
</manifest>

원인은 안드로이드 API 28 이상부터는 Cleartext HTTP를 비활성화하는 것으로 정책이 변경되어,
Http로 접근하기 위해서는 Cleartext HTTP를 활성해주어야 한다고 한다.

 

 

이것 때문에 골치 아팠던 내 시간들이여 ,,, 😄

 

 

 

 

 

 

 

 

 

react-native 앱 종료 방법에 대해 포스팅 해보기로 한다.

 


react-native 앱 종료 방법은 2가지가 있다. 


1. react-native에서 제공해주는 BackHandler를 사용하는 법

import React, {useEffect} from 'react';
import {Text, View, StyleSheet, BackHandler, Alert} from 'react-native';

const App = () => {
  useEffect(() => {
    const backAction = () => {
      Alert.alert('Hold on!', 'Are you sure you want to go back?', [
        {
          text: 'Cancel',
          onPress: () => null,
          style: 'cancel',
        },
        {text: 'YES', onPress: () => BackHandler.exitApp()},
      ]);
      return true;
    };

    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      backAction,
    );

    return () => backHandler.remove();
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.text}>Click Back button!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  text: {
    fontSize: 18,
    fontWeight: 'bold',
  },
});

export default App;

주의해야할 점.
1. iOS는 적용이 안되고, Android에서만 작동한다. 
2. 앱을 완전히 kill 하는 것이 아닌 백그라운드 상태로 보낸다.

 


2. react-native-exit-app 라이브러리

  • iOS, Android에서 모두 작동한다.
  • 앱을 백그라운드 상태가 아닌 완전히 kill 한다.

 

설치

npm install react-native-exit-app --save
react-native link react-native-exit-app

Native 코드 수정 - Android

  • In the settings.gradle
      include ':react-native-exit-app', ':app'
      project(':react-native-exit-app').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exit-app/android')
    

 

  • In the build.gradle
      implementation project(':react-native-exit-app')
    주의. react-native는 compile이 아닌 implementation을 쓴다.

 

  • In MainApplication.java
      import com.github.wumke.RNExitApp.RNExitAppPackage;
      ...
      @Override
      protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
          ...
          new RNExitAppPackage(),
          ...
        );
      }
      ...

사용법

import RNExitApp from 'react-native-exit-app';
...
RNExitApp.exitApp();
...

 

 

참고 https://github.com/wumke/react-native-exit-app

그라데이션을 적용할 수 있는 라이브러리 react-native-linear-gradient를 사용해보자.

Installation

Using Yarn

yarn add react-native-linear-gradient

Using npm

npm install react-native-linear-gradient --save

Linking

만약 react-native version이 0.60 아래라면 Linking 필요!

react-native link react-native-linear-gradient


후에는 https://github.com/react-native-linear-gradient/react-native-linear-gradient 에 들어가 메뉴얼을 확인해보자.

설치가 다 되었다면 사용해보자.

	const linearColors = [startBgColor, endBgColor];
    
        return <LinearGradient
                  colors={linearColors}
                  style={{
                    width: '100%',
                    height: '100%',
                    position: 'absolute',
                    zIndex: 100,
                    alignItems: 'center',
                    justifyContent: 'center',
                  }}/>

startBgColor와 endBgColor에 RGBA 색상이나 Hex 값만 정해주면 된다!
ex) ['#FFFFFF', '#0D0D0D']

 

이렇게 지정해주면 배경색이 그라데이션으로 적용된 것을 볼 수 있다.

+ Recent posts