이번 글에서는 간단한 UI를 적용해 보겠습니다.

 

React native 설치 및 프로젝트 세팅 방법은 앱 개발 - 01 을 참고하시기 바랍니다.

 

앱 개발 - 01 에서 생성한 firstApp 생성된 프로젝트가 있다고 가정한 상태로 설명하겠습니다. 프로젝트가 생성되지 않았다면 앱 개발 - 01를 참고해서 우선 프로젝트를 생성하고 다시 이 글로 돌아와 주세요.

 

이 글에서는 UI를 다룰 때 필요한 주요 기능을 예제를 통해 정리해 보겠습니다. 순서는 아래와 같습니다. 

 

  • React Native Elements 로 UI 구현
  • Layout 설정
  • React Native Navigation 사용하기
  • Popup 구현

React Native Elements 로 UI 구현

 

UI 객체 구현은 React Native Elements (이하 RNE) 을 사용했으며, 설정 절차와 사용 방법만 간단히 언급하겠습니다.

 

생성한 프로젝트의 root에서 RNE를 아래와 같이 설치해 주세요.

# react native elements를 설치합니다.
npm i react-native-elements --save

# vector icons을 설치합니다.
npm i --save react-native-vector-icons

# 사용하는 react native version은 0.61.5 버전이라 link 과정은 생략합니다.

 

처음으로 버튼을 화면에 하나 그려보겠습니다. 

 

App.js 파일을 열어 아래와 같이 고쳐보세요.

import React from 'react';

import {
  SafeAreaView,
} from 'react-native';

import { Button } from 'react-native-elements';

const App: () => React$Node = () => {
  return (
      <SafeAreaView>
         <Button title="Hello World!!" />
      </SafeAreaView>
  );
};

export default App;

SafeAreaView 키워드는 어떤 디바이스에서든 기본 화면 안쪽에 출력할 수 있는 영역을 의미합니다. 그 외 여러 키워드들이 앞으로 쓰여질텐데 이 글에서는 Interface를 사용하는데 필요한 부분만 설명을 하도록 하겠습니다. 그 외 키워드들은 공식 문서를 참고하시기 바랍니다.

 

실행하면 아래와 같은 결과를 볼 수 있습니다.

 

공식 문서 중 Customization 부분은 꼭 확인해 보시고 다음으로 넘어가세요.

 

Layout 설정

다음에는 화면을 1:1:2로 삼 분할 하고 중앙 레이어에 버튼을 넣어보겠습니다. 

 

일단 테스트 코드를 먼저 복사해서 테스트 해보시기 바랍니다. 

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

import { Button } from 'react-native-elements';

const App: () => React$Node = () => {
  return (
      <SafeAreaView style={{marginTop : 50, flex: 1}}> // (1)
        <View style={{flex: 1, backgroundColor:'#EE2C38'}}>  // (2-1)
        </View>

        <View style={{flex: 1, backgroundColor:'#FAA030'}}>  // (2-2)
          <Button raised title="Hello World!!" /> 
        </View>

        <View style={{flex: 2, backgroundColor:'#32B76C'}} /> // (2-3)
      </SafeAreaView>
  );
};

export default App;

flex는 아이템을 어떻게 화면을 채울지를 결정하는 키워드입니다. 값은 화면의 비율을 설정합니다. 2-1/2/3을 보면, 같은 depth에 세 개의 flex가 있고 각각이 1, 1, 2 라고 설정되어 있다면 해당 영역을 1:1:2의 화면 비율로 나누겠다는 설정입니다.

 

레이아웃에 대한 보다 상세 내용은 이 링크 를 참고하세요.

 

화면 레이아웃을 잡아 봤으니 이제 페이징 처리를 해보겠습니다. 

 

React Native Navigation 사용하기

모바일은 보통 단일 화면으로 구성되기 때문에, React Native 에서는 화면 전환을 위해 React Native Navigation이라는 모듈을 주로 사용합니다. 

 

우선 필요한 모듈을 설치합니다. 

npm install @react-navigation/native @react-navigation/stack

npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

# ios일 경우 cocoapods로 필요한 라이브러리를 설치해야 합니다.
cd ios
pods install
cd ..

추가 모듈을 설치할 때 에뮬레이터는 끄고 설치 후 재실해 하시기 바랍니다. 

 

App.js에 아래 코드를 복사한 후 재실행 해보시기 바랍니다. 

import 'react-native-gesture-handler';
import * as React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import {
  SafeAreaView,
  View,
  Text
} from 'react-native';
import { Button } from 'react-native-elements';

const Stack = createStackNavigator();

const App: () => React$Node = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={{title: 'Welcome'}}
        />
        <Stack.Screen name="Second" component={SecondScreen} options={{title: 'SecondPage'}}/>
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;

function HomeScreen({navigation}) {
  return (
    <Button
      title="Go to Second Page"
      onPress={() => navigation.navigate('Second', {name: 'Parameter_1'})}
    />
  );
}

function SecondScreen({navigation}, param) {
  return (
    <SafeAreaView style={{marginTop:50, flex:1}}>
      <View style={{flex:2}} />
      <View style={{flex:2}} />
      <View style={{flex:1, justifyContent:'flex-end'}}>
        <Button title="Back"  onPress={() => navigation.goBack()} />
      </View>
    </SafeAreaView>
  );
}

 

우선 App 코드를 NavigationContainer로 전체를 감쌉니다. 그리고 Stack.Navigator 안에 사용할 스크린 화면 내용을 정의 합니다. 

 

Stack.Screen의 속성 중 name은 코드에서 지정자처럼 사용할 이름이며, component는 해당 화면에 대한 모듈명을, option의 title에는 각 페이지의 타이틀 명으로 사용됩니다. option에 대한 상세 정보는 이 링크를 참고하세요.

 

마지막 두 개의 함수는 세부 화면을 정의한 코드입니다. 

 

Popup 구현

이 예제에서는 이전에 다루지 않은 몇 가지 중요 개념이 나오기 때문에 우선 샘플 코드(원본 링크)로 테스트를 해 보시고 아래 설명을 읽어보시기 바랍니다.

import React, { Component } from 'react';
import {
  Modal,
  View,
  Text,
  TouchableHighlight,
  StyleSheet
} from 'react-native';
import { Button } from 'react-native-elements';

testModalVisible = false;

const App: () => React$Node = () => {
  return (
    <ModalExample />
  )
}
export default App;

class ModalExample extends Component {
  state = {
    modalVisible: false
  }
  toggleModal(visible) {
      this.setState({ modalVisible: visible });
  }
  render() {
    return (
       <View style = {styles.container}>
          <Modal animationType = {"slide"} transparent = {false}
             visible = {this.state.modalVisible}
             onRequestClose = {() => { console.log("Modal has been closed.") } }>
             
             <View style = {styles.modal}>
                <Text style = {styles.text}>Modal is open!</Text>
                
                <TouchableHighlight onPress = {() => {
                   this.toggleModal(!this.state.modalVisible)}}>
                   
                   <Text style = {styles.text}>Close Modal</Text>
                </TouchableHighlight>
             </View>
          </Modal>
          
          <TouchableHighlight onPress = {() => {this.toggleModal(true)}}>
             <Text style = {styles.text}>Open Modal</Text>
          </TouchableHighlight>
       </View>
    )
  }
}

const styles = StyleSheet.create ({
   container: {
      alignItems: 'center',
      backgroundColor: '#ede3f2',
      padding: 100
   },
   modal: {
      flex: 1,
      alignItems: 'center',
      backgroundColor: '#f7021a',
      padding: 100
   },
   text: {
      color: '#3f2949',
      marginTop: 10
   }
})

 

React 공식 페이지에는 "A JavaScript library for building user interfaces"라는 문구가 있습니다. 그리고 중앙에 "Component-Based"라는 문구가 떡 하니...!

 

위 예제의 정확한 이해를 위해 Component와 state 그리고 props에 대한 정확한 이해가 필요한데, 예제 중심의 이 글에서 이들을 설명하기는 너무 길어지기 때문에 제가 찾았던 글들 중 가장 잘 설명이 되어 있다고 생각하는 링크들를 아래에 기록했으니 여러분도 꼭 읽어보시기 바랍니다. 

 

절대 설명하기 귀찮아서 그런건 아니...

 

지금까지 React-Native 에 필요한 UI 중요 요소 네 가지를 예제로 알아보았습니다. 

 

다음은 유용한 UI 객체들을 살펴보겠습니다.

https://yuddomack.tistory.com/entry/1React-Native-%EC%84%A4%EC%B9%98%EC%99%80-%EC%8B%A4%ED%96%89hello-world?category=754156 (이하 링크 문서) - 이 링크에 너무 잘 정리되어 있어 따라하면서 배우는 중입니다. (글쓴이님. 감사합니다. :)


https://wix.github.io/react-native-navigation/#/docs/Installing (이하 공식 문서)


아래 내용은 다음과 같은 환경에서 진행된 내용입니다. 

OS 

 macOS Mojave 10.14.1

 Android Studio 

 3.3.1

 XCode 

 10.1

 nodejs 

 10.13.0

 react 

 16.6.3

 react-native

 0.58.6

 react-native-navigation

 2.13.1



일단 링크문서의 1~5 chapter까지는 좋은 가이드가 되니 꼭 참고하시기 바랍니다.


chapter 6에서는 react native navigation을 다루는데, 최신 버전에서 변경된 내용들이 있어 별도로 정리를 해둡니다.


react-native-navigation을 설치하고 나면 low severity vulnerabilities에 대한 경고를 출력하고 npm audit fix(npm v6)를 실행하라는 안내가 나올 수 있습니다.


이를 실행하면 react-native-navigation을 사용하기 위해 필요한 몇몇 module의 버전을 조정하는 작업이 진행됩니다.


Android/iOS 세팅은 공식문서의 내용 중 수동 링크를 기준으로 정리하며 부족한 샘플 코드를 추가하였습니다. 왜 수동 링크 작업을 해야 하는지는 링크 문서에 잘 나와 있으니 참고하시기 바랍니다.


[RNN iOS 세팅]


우선 링크 문서 6장 의 내용을 참고하여 xcode에 react native navigation을 추가합니다. 공식문서보다 이 링크가 좀 더 명확히 정리되어 있습니다.


AppDelegate.m을 수정할 때 아래와 같은 에러가 발생할 수 있습니다.


 React/RCTBundleURLProvider.h file not found

 Use of undeclared identifier 'rootView'


이와 관련된 해결책은 공식 문서에 있으며, XCode에서 프로젝트를 선택 후, 메뉴 Product >> Scheme 에서 react native navigation을 선택하면 사라집니다.


링크 문서에서는 공식 문서와는 달리 RCCManager.h를 사용하도록 코드가 되어 있는데, 이전 버전의 내용이니, 아래 내용을 참고하시면 됩니다.


** 중간에 여러 코드 및 세팅을 테스트를 위해 수정하면서 Systrace.setEnable  관련된 에러가 ios 에뮬레이터에서 발생. 검색해보니 방법없으니 다시 설치하라는 내용만 있어 프로젝트 삭제 후 다시 설치. -0-a


관련 코드들은 공식 문서의 내용으로 적용하면 됩니다. 


(( AppDelegate.m ))


#import "AppDelegate.h" #import <React/RCTBundleURLProvider.h> #import <React/RCTRootView.h> #import <ReactNativeNavigation/ReactNativeNavigation.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; return YES; } @end


XCode를 위한 세팅은 이것으로 끝입니다.


[RNN Android 세팅]


** 간간히 이런 작업들을 할 때 느끼는 거지만 아래 링크 문서의 샘플 코드에서 rootProject.projectDir 처럼, 저런 정의된 값을 어디서 찾아야 하는지 난감할 때가 많다. 기존에 android studio를 많이 다뤄 보신 분들은 쉽게 알 수도 있을 듯 한데, 처음 볼 때는 뜬금없어서.. 특히 javascript 프로젝트 할 때는 predefined keyword가 아닌 것들이 (intelligence를 지원하지 않는 ide에서) class의 public 맴버처럼 접근하는걸 볼 때마다 이게 어디에 정의되어 있는지, 올바르게 사용된건지 확인하기가 만만치 않은 듯 합니다. 자꾸 하다보니 그러려니 하긴 하지만...


아래 네 개의 gradle 파일을 수정할 때는 공식 문서를 참고하고, 자신의 android studio 버전에 따라 문서의 내용과 다를 수 있으니, 그에 맞추어 수정해야 합니다.

(( android/settings.gradle ))

(( android/gradle/wrapper/gradle-wrapper.properties ))

(( android/build.gradle ))

(( android/app/build.gradle ))


아래 두 코드를 공식 문서를 참고하여 수정 합니다.


(( MainActivity.java ))

(( MainApplication.java ))


그 다음으로 React Navtive 의 버전을 타겟팅하는 설정이 android/app/build.gradle에 추가해야 합니다. 별도 항목으로 제외된 것을 보면 중요한 항목인 듯 하네요. package.json 파일을 확인하니 제가 사용하는 버전은 0.58.6인 상태입니다.


저는 missingDimensionStrategy "RNN.reactNativeVersion", "reactNative57_5"로 설정을 추가 했습니다. 


공식 문서의 내용 1~8가지 따라하면서 주의할 점은 app/build.gradle의 dependencies를 추가할 때 특정 버전 대신 "${rootProject.ext.supportLibVersion}"을 설정하세요. 버전이 맞지 않아 빌드 실패 하는 경우가 종종 있었습니다. 

(https://github.com/wix/react-native-navigation/issues/3419 를 참고하세요.)







+ Recent posts