어서와, 개발은 처음이지?

6.React Native Navigation(v1) 기초 - 2부 화면 등록, 화면 이동(startapp, push, pop) 본문

React/React Native

6.React Native Navigation(v1) 기초 - 2부 화면 등록, 화면 이동(startapp, push, pop)

오지고지리고알파고포켓몬고 2018. 8. 3. 17:29


이 글은

6.React Native Navigation 기초 - 1부 설치하기

6.React Native Navigation 기초 - 2부 화면 등록, 화면 이동(startapp, push, pop)(현재글)

6.React Native Navigation 기초 - 3부 기능 살펴보기로 구성되어 있습니다.


(19.08.05)react-navigation 예제가 추가되었습니다.



이번 글에서는 React Native Navigation의 화면 등록과 이동 과정에 대해 알아보겠습니다.

근데 매번 풀네임으로 쓰기 너무 힘드네요.. React Native Navigation(이하 rnn)으로 칭하겠습니다. 순환신경망 아니쥬~



1.일단 예제는


rnn의 깃헙에 들어가셔서 클론하면 Wix 형님들이 만들어두신 example을 사용해볼 수 있습니다.

react-native-navigation-master/example 경로에 들어가셔서 npm install을 하고 react-native run-ios 혹은 react-native run-android로 예제를 실행해볼 수 있습니다.


하나씩 실험해보실 분들은 example을 가지고 놀아보세요!



2.RNN의 구조


rnn의 예제를 보면 src/screens와 src/components라는 경로로 화면과 컴포넌트를 관리하고 있습니다.

(리액트 디자인 패턴중에 Atomic Design이라는게 있는데 약간 비슷한 느낌입니다.)


screens와 components에는 index.js가 존재하며 각 경로의 js파일들을 Navigation에 등록시켜주는 역할을 하고 있습니다.(App.js는 따로 만들지 않겠습니다.)

일단 아래처럼 경로를 만들어줍니다.

.
├── App.js
├── android
├── TestComponent.js
├── app.json
├── images
│   └── img.jpg
├── index.js
├── ios
├── package-lock.json
├── package.json
├── src
│   ├── components
│   │   ├── CustomButton.js
│   │   └── index.js
│   └── screens
│       ├── JoinScreen.js
│       ├── LoginScreen.js
│       ├── StartScreen.js
│       └── index.js
└── yarn.lock

TestComponent.js는 이전에 State와 Props를 설명할 때 만든 것으로, 신경쓰지 않으셔도 됩니다.

눈여겨 봐야할 부분은 imagessrc경로입니다.


depth가 깊어질수록 상대경로로 참조하기 어려워지기 때문에 최상단에 images라는 경로를 만들었습니다.

이는 require('TestProject/images/img.jpg')의 형태로 참조 가능합니다.


src는 sources의 줄임말이 되겠죠. 여기에 components와 screens라는 경로를 만들고, screens에 지난 시간에 제작한 화면들을 옮겨줍니다. (직접 만든 아무 화면도 상관없습니다.)

각 화면에 따라서 의미 파악이 용이한 이름으로 파일명을 변경해줍니다.


혹시 모르니 StartScreen.js을 작성해두겠습니다.

import React, {Component} from 'react';
import {StyleSheet, Text, View, Image} from 'react-native';
import CustomButton from '../components/CustomButton';

type Props = {};
export default class StartScreen extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.header} />
        <View style={styles.title}>
          <Text style={{fontSize:35,color:'white'}}>어서와,{'\n'}개발은 처음이지?</Text>
        </View>
        <View style={styles.content}>
          <Image
            style={{height:'100%',width:'100%',resizeMode:'contain'}}
            source={require('TestProject/images/img.jpg')}/>
        </View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#444'}
            title={'회원가입'}
            onPress={() => alert('회원가입 버튼')}/>
          <CustomButton
          buttonColor={'#023e73'}
          title={'로그인'}
          onPress={() => alert('로그인 버튼')}/>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
    backgroundColor: 'black',
  },
  header: {
    width:'100%',
    height:'5%',
    //backgroundColor: '#ff9a9a',
  },
  title: {
    width:'100%',
    height:'18%',
    justifyContent: 'center',
    //backgroundColor: '#9aa9ff',
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingBottom:30,
    //backgroundColor: '#d6ca1a',
  },
  footer: {
    width:'100%',
    height:'20%',
    //backgroundColor: '#1ad657',
  },
});
주의 - CustomButton의 상대경로와 17번 줄 image source의 절대경로를 유의하세요!



3.화면 등록


이 구조에서는 Navigation에 화면 등록을 components/index.jsscreens/index.js가 담당합니다.

우선 screens/index.js를 살펴보겠습니다.

import { Navigation } from 'react-native-navigation';

import StartScreen from './StartScreen';
import LoginScreen from './LoginScreen';
import JoinScreen from './JoinScreen';

export function registerScreens() {
  Navigation.registerComponent('yuddomack.StartScreen', () => StartScreen);
  Navigation.registerComponent('yuddomack.LoginScreen', () => LoginScreen);
  Navigation.registerComponent('yuddomack.JoinScreen', () => JoinScreen);
}


registerScreens() 함수는 각 화면 컴포넌트를 가져와서 Navigation.registerComonent()를 사용하여 화면을 등록합니다.

앞의 'yuddomack.xxx'는 Navigation에 등록될 화면 이름(Screen ID)인데, '시그니처.화면이름' 식으로 사용하도록 합니다.(외부 패키지에서 com.facebook.xxx 이런 네임을 쉽게 볼 수 있죠)


이 함수를 외부에서 호출할 수 있도록 export해줍니다.


다음은 components/index.js입니다.

import { Navigation } from 'react-native-navigation';


export function registerComponents() {
  
}

Navigation 등록이 필요한 컴포넌트는 주로 custom navbar나 drawer(사이드 메뉴)에 관한 컴포넌트입니다.

우리의 CustomButton은 Navigation에 등록하지 않아도 되오니 비워두도록 합니다.


이제 screens와 components를 사용하기위해 다시 /TestProject/index.js로 돌아와서 아래처럼 수정해줍니다.

import {name as appName} from './app.json';
import { Navigation } from 'react-native-navigation';

import { registerScreens } from './src/screens';
import { registerComponents } from './src/components';

registerScreens();
registerComponents();

Navigation.startSingleScreenApp({
  screen: {
    screen: 'yuddomack.StartScreen', // unique ID registered with Navigation.registerScreen
    title: 'Welcome', // title of the screen as appears in the nav bar (optional)
    navigatorStyle: {
      navBarHidden: false,
    }, // override the navigator style for the screen, see "Styling the navigator" below (optional)
    navigatorButtons: {} // override the nav buttons for the screen, see "Adding buttons to the navigator" below (optional)
  }
});

rnn은 탭이 있는 앱(startTabBasedApp)과 탭이 없는 앱(startSingleScreenApp)을 제공하며, 이 예제는 아직 메인 화면 전(세션 할당 전)에 해당하므로 아직 탭 기능이 필요없습니다.

startTabBasedApp이나 세부 옵션에 관한 문서는 이곳을 확인하세요.


Navigation.startSingleScreenApp()을 실행하면 명시된 screen을 시작화면으로 앱이 시작되는데,

registerScreens()와 registerComponent()를 선행하여 화면, 컴포넌트를 먼저 Navigation에 등록해줍니다.

screen: value에는 컴포넌트를 등록하는 것이 아닌 registerScreens()에서 등록한 문자열 이름을 사용하여야 합니다.


이제 앱을 실행하면 아래 화면이 나오는 것을 볼 수 있습니다.

navBarHidden을 true로 설정하시면 상단 title을 안보이게 할 수 있지만 화면 이동이 이루어지는 것을 보기 위해 남겨두겠습니다.




4.화면 이동(push)


Navigation.registerComponent()로 등록된 화면에는 navigator라는 props가 생성됩니다.

this.props.navigator.push() 함수를 사용하여 화면 이동을 사용할 수 있습니다.


StartScreen.js를 수정하겠습니다.

export default class StartScreen extends Component<Props> {

  _pushJoin(){
    this.props.navigator.push({
      screen: 'yuddomack.JoinScreen', // unique ID registered with Navigation.registerScreen
      title: 'Join', // navigation bar title of the pushed screen (optional)
      passProps: {}, // Object that will be passed as props to the pushed screen (optional)
    });
  }

  _pushLogin(){
    this.props.navigator.push({
      screen: 'yuddomack.LoginScreen', // unique ID registered with Navigation.registerScreen
      title: 'Login', // navigation bar title of the pushed screen (optional)
      passProps: {}, // Object that will be passed as props to the pushed screen (optional)
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.header} />
        <View style={styles.title}>
          <Text style={{fontSize:35,color:'white'}}>어서와,{'\n'}개발은 처음이지?</Text>
        </View>
        <View style={styles.content}>
          <Image
            style={{height:'100%',width:'100%',resizeMode:'contain'}}
            source={require('TestProject/images/img.jpg')}/>
        </View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#444'}
            title={'회원가입'}
            onPress={this._pushJoin.bind(this)}/>
          <CustomButton
          buttonColor={'#023e73'}
          title={'로그인'}
          onPress={this._pushLogin.bind(this)}/>
        </View>
      </View>
    );
  }
}

StartScreen에서 _push 함수를 만들고(화면별 push를 만들어 외부 모듈로 관리하셔도 좋습니다.) 각 버튼에 넣어줍니다.

만약 값 전달을 원하시면 passProps에 key value 형태로 넣어주세요.

대상 screen에서 this.props.key로 호출할 수 있습니다. (passProps: {session:false} -> this.props.session)


이제 StartScreen에서 버튼을 누르면 각 화면으로 이동하는 모습을 볼 수 있습니다.

상단의 title을 보시면 Navigator를 통해 화면이 이동되었다는 것을 알 수 있습니다.



5.뒤로가기(pop)


화면 이동이 일어나면 이전 화면이 stack에 쌓이게됩니다.

this.props.navigator.pop() 함수를 사용하면 이전 화면으로 돌아갈 수 있습니다.


이를 LoginScreen.js의 취소 버튼에 넣도록 하겠습니다.

export default class LoginScreen extends Component<Props> {
  _pop(){
    this.props.navigator.pop({
      animated: true, // does the pop have transition animation or does it happen immediately (optional)
      animationType: 'fade', // 'fade' (for both) / 'slide-horizontal' (for android) does the pop have different transition animation (optional)
    });
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.header} />
        <View style={styles.title}>
          <Text style={{fontSize:35,paddingBottom:20}}>로그인</Text>
          <View style={{width:"100%",borderBottomWidth:0.5,borderColor:'#444'}} />
        </View>
        <View style={styles.content}>
          <View style={{flexDirection:'row',justifyContent:'space-between',alignItems:'center',paddingBottom:10}}>
            <Text style={{fontSize:15}}>아이디</Text>
            <TextInput style={{borderColor: '#aaa', width:'70%', height:35, borderWidth: 1, borderRadius: 5, padding:5}}/>
          </View>
          <View style={{flexDirection:'row',justifyContent:'space-between',alignItems:'center',paddingBottom:10}}>
            <Text style={{fontSize:15}}>비밀번호</Text>
            <TextInput style={{borderColor: '#aaa', width:'70%', height:35, borderWidth: 1, borderRadius: 5, padding:5}}/>
          </View>
        </View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#444'}
            title={'취소'}
            onPress={this._pop.bind(this)}/>
          <CustomButton
          buttonColor={'#023e73'}
          title={'확인'}
          onPress={() => alert('확인 버튼')}/>
        </View>
      </View>
    );
  }
}

option을 보면 this.props.navigator.pop() 만으로도 실행가능합니다.

이제 취소 버튼을 누르면 다시 이전 화면으로 돌아가게 됩니다.

실제 서비스라면 fetch를 사용해서 실제 서버에 아이디와 비밀번호 정보를 보내고, 도착한 메세지에 따라서 화면을 넘어가게 하는식으로 구현될 것 입니다.

그럼 이제 확인 버튼을 눌렀을 때 다른 화면이 나오도록 직접 해보시는 것을 추천 드리겠습니다.



6.요약


* src/components, src/screens로 화면과 컴포넌트를 관리할 수 있도록 구조화

* 각 index.js에서 Navigation에 컴포넌트 등록(registerComponent)하는 것을 관리

* Navigation.startTabBasedApp 혹은 Navigation.startSingleScreenApp으로 초기 화면 설정 및 실행

* 화면 이동은 push, 이전 화면은 pop



7.마무리


이번 시간에는 rnn을 사용해서 화면을 제어하는 방법을 알아봤습니다.

기능이 워낙 방대하고 옵션도 다양해서 모든 부분을 설명할 수 없어서 우선 rnn을 사용해서 앱을 실행하고 화면을 이동하는 기초적인 부분에 대해서만 알아봤습니다.


다음 글에서는 화면과 화면 사이의 연결보다는 기능에 초점을 두고, 핵심적인 기능들을 짚어보도록 하겠습니다.


Comming Soon!




7 Comments
댓글쓰기 폼