React Native 는 프로젝트 구성 및 효과적인 폴더 구성은은 어떤 방법들이 있을 지 알아보려 한다. 

 


dream$ npx react-native init folderStructure

좌측의 이미지는 React Native를 이용해 처음으로 생성한 폴더&파일이다. 

 

효과적인 폴더 구성을 알아보기 위해서 기본적으로 생성되는 폴더와 파일에 대해서 알아보겠다. 

 

index.js : react native app이 실행될 때, 진입점이 된다.

App.js : App이 로딩 되었을 때 개발자에 의해 개발되어 표시되는 최초화면이다. index.js 파일에서 import하여 App의 진입화면으로 사용한다.

App.json : RunTime 시점에 자바스크립트 코드를 이용해서 접근할 수 있다. 

ios : IOS를 기동하기 위한 프로젝트 폴더이다.

android : Android를 기동하기 위한 프로젝트 폴더이다. 

babel.config.js - React Native Application 을 위한 Preset 설정

metro.config.js - Metro Bundler 설정 파일

node_modules - node module이 저장되는 폴더  

package.json - node modules 종속성 관리 설정 파일 

 

 

 

 

 

React Native 를 활용해서 모바일 App을 만들때 가장 고민되었던 부분이 폴더 구성이었다. 

관련 자료과 내가 구성한 방식들을 고민해봤을 때, 어떤 방식으로 구성하는 것이 가장 효율적이냐에 대한 부분은 정답이 없다는 생각이 들었다. 

 

다만, 다수의 React Native 폴더를 보았을 때는 아래와 같이 구성하는 것이 가장 적합한 것 같았고, 오히려 폴더의 구성 보다 React Native Component 상에서 각각의 폴더 내의 Component들을 가져오는 방식이 더 중요하다고 생각한다.

 

폴더의 구성 방식은 아래와 같이 구성해봤다.

 

src 폴더 구성

위의 폴더 구성을 바탕으로 필요한 Component 를 Import 할 때, 

 

 이전에는 아래와 같이 했다면,

 

import { DataBase, DataBaseWrite } from '../../../components/database';

 

아래와 같이 조정해봤다.

 

import { DataBase, DataBaseWrite } from '_components/database';

 

위와 같이 처리한 이유는 ../../../은 가독성이 유지보수 측면에서 최악이라고 생각했기 때문에 이를 _components 라는 alias를 대신해서 사용한 것이다.

 

 


1. Package.json에 아래 항목 추가 

 

"babel-plugin-module-resolver": "^4.0.0",

"eslint-import-resolver-babel-module": "^5.1.0",

"eslint-plugin-import": "^2.19.1",

 

 

2. 아래의 빨간색으로 표시한 항목에 대해서 파일 추가 또는 내용 추가 

 

react-native init {프로젝트 명} 로 생성한 프로젝트 폴더 경로 안에 아래의 빨간색 표시 박스의 파일을 수정 또는 추가한다. 

 

아래에 설정된 폴더의 경로는 자신이 구성한 프로젝트 폴더의 구성에 따라 변경하여 사용하면 된다. 

 

추가된 파일 

 

 

3. .babelrc 파일 추가

babel-plugin-module-resolver를 설치하고 난 뒤에 .babelrc 파일에 아래와 같이 설정하면 alias를 절대경로로 사용할 수 있다.

root를 사용하면, 내가 원하는 src 폴더에 대해서 프로젝트 root 를 지정할 수 있고, 위에서 봤던 이미지 처럼 

 

 _components/database 와 같이 ../../../ 을 사용하지 않고 직관적으로 이해할 수 있게 경로 설정이 가능하다. 이는 폴더의 depth가 깊어지기 시작하면 정말 필요해진다. 

{
	"plugins": [
		[
			"module-resolver",
			{
				"cwd": "babelrc",
				"root": ["./src"],
				"extensions":[".js",".ios.js",".android.js"],
				"alias": {
					"_assets":"./src/assets",
					"_components":"./src/components",
					"_atoms":"./src/components/atoms",
					"_molecules":"./src/components/molecules",
					"_organisms":"./src/components/organisms",
					"_navigations":"./src/navigations",
					"_scenes":"./src/screens",
					"_services":"./src/services",
					"_styles":"./src/styles",
					"_utils":"./src/utils"
				}
			}
		]
	]
}

 

4. jsconfig.json

Visual Studio Code의 코드 인텔리센스를 위해서 프로젝트 디렉터리에 jsconfig.json 파일을 만든다. 

이는 npm plugin을 통해서 babel-plugin-module-resolver를 import 하여 사용하는 것이기 때문에, VS Code에서 import 시에 

지정한 alias에 맞게 작업시 파일을 불러오지 못하는 경우가 생기는데, 이 경우에 해당 파일슬 생성하여 alias에 맞게 자동완성 기능을

적용할 수 있다.

{
	"compilerOptions": {
		"baseUrl":".",
		"paths": {
			"_assets":["src/assets/*"],
			"_components":["src/components/*"],
			"_atoms":["src/components/atoms/*"],
			"_molecules":["src/components/molecules/*"],
			"_organisms":["src/components/organisms/*"],
			"_navigations":["src/navigations/*"],
			"_scenes":["src/screens/*"],
			"_services":["src/services/*"],
			"_styles":["src/styles/*"],
			"_utils":["src/utils/*"]
		}
	}
}

 

5. .eslintrc.js

eslint가 설정되어 있는 경우에는 eslint에서 사용하는 소스 코드의 경로 상의 문제를 해결하기 위해서 아래와 같이 alias를 지정한다. 

module.exports = {
  root: true,
  extends: '@react-native-community',
    plugins: ['import'],
    settings: {
      'import/resolver': {
        node: {
          paths: ['src'],
          alias: {
            _assets: './src/assets',
            _components: './src/components',
            _atoms: './src/components/atoms',
            _molecules: './src/components/molecules',
            _organisms: './src/components/organisms',
            _navigations: './src/navigations',
            _scenes: './src/screens',
            _services: './src/services',
            _styles: './src/styles',
            _utils: './src/utils',
          },
        },
      },
    },
};

 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-3  (0) 2019.12.13
0001 React Native App 개발기 6-2  (0) 2019.12.13
0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22

Reactive Native App 개발기 6-2 에서 알아본 Component Lifecycle 상에 제공되는 함수들의 용도와 활용방법에 대해서 알아보겠다.

 


Mounting

 

  • constructor

       만약 state를 초기화 하거나 함수를 바인딩 하는 것이 아니라면, 해당 Component를 위한 constructor는 구현할 필요가 없다.   

       constructor는 마운트 되기 전에 호출된다. react component의 constructor를 구현할 때, 다른 문자을 작성하기 전에 super(props)

       를 반드시 호출해야한다. 그렇지 않으면 this.props 는 constructor에서 undefined으로 찾을수 없다. 

 

        constructor는 주로 아래의 2가지를 위해서 사용되는데, 

   

          1. this.state 를 초기화 할 때. 

          2. event handler 함수를 객체에 바인딩할 때. 

 

        constructor 내부에서 setState를 호출해서는 안되고 필요한 경우, this.state를 이용하여 선언시에 초기값으로 직접 값을 설정해야한다. side-effect(외부간섭/부가효과) 또는 구독을 constructor에서 처리하는 것을 피해야한다. 필요한 경우 componentDidMount를 대신 사용해야한다. 

 

  • static getDerivedStateFromProps(props, state)

        해당함수는 render 함수가 호출되고 난 이후에 바로 실행된다. 초기 마운트 시점과 구독 갱신 시점에 모두 영향을 받으며, 상태를 업데이트 하기 위해서 객체를 하거나 아무것도 업데이트 하지 않기 위해서 null을 반환해야 한다. 

 

        1. 만약 부가 효과를 실행해야 한다면, componentDidUpdate를 활용하라. 

        2. prop가 변경되었을 때 일부 데이터에 대해서만 재계산하고 싶다면, 메모제이션을 활용해야한다.

        3. 만약 일부 state를 초기화하고 싶다면, 해당 링크의 방식(fully controlled  , fully uncontrolled)을 활용할수 있다. 

 

  • render()

        render 함수는 class 컴포넌트에서 필요한 함수로써, function 컴포넌트에서는 필요하지 않다. 

 

        render 함수는 shouldComponentUpdate의 반환 값이 false 일 경우 실행되지 않는다.  

 

  • componentDidMount()

        Component가 마운트되고 난후 즉시 componentDidMount가 실행된다. Dom 노드에 대한 초기화가 필요한 경우 여기에서 작업해야한다. 원격의 Endpoint로 부터 데이터를 가져와야하는 경우에도 해당 함수의 위치에서 진행하면 된다. 만약 setState를 이 함수안에서 사용한다면 rendering을 발생시킬 것이고 이는 브라우저가 해당 화면을 갱신하기 전에 발생한다. 이것은 비록 render가 두번 호출되더라도 사용자가 중간과정을 보지 않을 수 있도록 보장한다. 하지만 이 패턴은 성능 상의 이슈를 야기할 수 있기 때문에 주의해야한다. 

 따라서 가능하다면 constructor()에서 초기값을 할당할 수 있도록 해야한다./

 


Updating 

 

  • static getDerivedStateFromProps(props, state) 
  • shouldComponentUpdate(nextProps, nextState)

        해당 함수는 React가 component의 결과아 state나 props의 변화에 의해 영향 받지 않았을 경우 shouldComponetUpdate에 의해 알릴 수 있다. 해당 함수는 신규 props나 state의 변화에 의해 rendering되기 전에 실행된다. 기본 값은 true이며 forceUpdate가 실행된 경우에는 해당 함수를 호출되지 않는다. 

 

        이 함수는 성능 최적화로써 오로지 존재하는데, shouldComponentUpdate를 사용하는 것대신에 PureComponent를 사용하는 것을 고려하라. 

 

       결과값을 false로 반환할 경우, UNSAFE_componentWillUpdate, render, componentDidUpdate를 실행하지 않을 것이다. 

 

  • render()
  • getSnapshotBeforeUpdate(prevProps. prevState)

      가장 최근의 렌더링된 결과가 commit 되었을 때 실행된다. 이 함수는 정보가 잠재적으로 변경될 가능성이 있을때, 컴포넌트가 Dom으로 부터 일부 정보를 보관하는 것을 허용한다. 생명주기 족에서 반환된 모든 값은 파라미터로 componentDidUpdate()로 전달될것이다. 

 

  • componentDidUpdate(prevProps, prevState, snashot)

       해당 함수는 갱신이 일어난 후에 즉시 실행된다. 해당 함수는 초기 render를 위해서 호출되지 않는다. 

 

       해당 함수는 컴포넌트가 갱신되었을 때, DOM에 대해서 처리할 수 있는 기회로 사용하면 된다. 이는 네트워크 요청등의 처리를 하기에 적합하다. 

 


UnMounting

  • componentWillUnMount

        해당함수는 컴포넌트가 언마운트 되기 전이나 제거될 때 호추된다. 해당 함수안에서 초기화 및 정리를 실행할 수 있다. 

 


ErrorHandling 

  • static getDerivedStateFromError()

       해당 생명 주기는 상위 Component(해당 함수를 선언한 컴포넌트)를 감싸고 있는 자식(하위)컴포넌트에서 에러가 발생하였을 경우 실행된다. 파라미터로 전달된 에러를 받을 수 있고 state를 갱신하기 위한 값을 반환해야 한다. 

 

  • componentDidCatch(error, info)

      해당 생명 주기는 상위 Component(해당 함수를 선언한 컴포넌트)를 감싸고 있는 자식(하위)컴포넌트에서 에러가 발생하였을 경우 실행된다. 파라미터로 전달된 에러를 받을 수 있고 state를 갱신하기 위한 값을 반환해야 한다. 

 

     2개의 파라미터를 전달받는데, 

      

       1. error - 전달된 Error

       2. info - 컴포넌트(에러를 던진 컴포넌트에 대한 정보를 포함하는 키) 스택가지고 있는 객체 

 

 


기존의 생명주기 함수들

 

  아래의 함수들은 version 17 까지는 동작할 예정이다. 하지만 16.3 이후로 정립된 생명주기 함수들을 활용하여 개발하는 것을 추천한다. 

이전부터 컴포넌트의 생명주기와 관련된 함수들에 대한 많은 이슈가 있어왔고 이를 해소하고자 16.3 이후 부터 위의 함수들을 제공하고 있다. 

 

 UNSAFE_componentWillMount()

    마운팅이 일어났을 때, 실행된다. 이 것은 render 함수 전에 실행된다. 

 

 UNSAFE_componentReceiveProps()

    해당 함수의 경우, 버그와 불일치성을 초래하고 있다. 따라서 사용하지 않는 것을 권고한다. 

    마운트된 컴포넌트가 새로운 props를 받았을 때 실행된다. 

 

 UNSAFE_componentWillUpdate()

     새로운 props나 state를 받았을 때, 실행된다. 

 

    해당함수는 만약 shouldComponentUpdate가 false를 반환할 경우 실행되지 않을 것이다. 

 

 

3편에 걸쳐서 컴포넌트의 생명주기에 대해서 알아보았다. 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 7  (0) 2019.12.14
0001 React Native App 개발기 6-2  (0) 2019.12.13
0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22

지난 편(React Native App 개발기 6-1)에서 알아본 컴포넌트 생명주기에 대해서 코드로 알아보려 한다. 

 

 아래의 설명한 사항은 React Version 16.4를 기준으로 동작하는 함수 및 절차이다. 


React Native 메인  

메인 화면 ( 클릭버튼 ) 

 - Component의 생명 주기를 알아보기 위해서 Main Component를 Import하여 아래와 같이 사용하였다. 

 - React Hook의 useState를 이용해서 함수 내에서 state를 사용하여 상태를 Main Component로 전달하였다. 

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow
 */

import React, { useState } from 'react';
import { View,  Text, Button } from 'react-native';
import Main from './src/pages/Main';
import ErrorBoundary from './src/pages/Error'

const App = () => {

  const [ value, setValue ] = useState({ data : "" });

  const onPressEvent = () => {
    setValue({ data : "11" });
  }


  return (
    <>
      <ErrorBoundary>
        <View style={{ flex : 1, justifyContent : 'center', alignItems:'center' }} >
          <Text>Component Life Cycle 입니다.</Text>
          <Button title="클릭" onPress={onPressEvent} ></Button>
        </View>
        <Main stateProps={value} propsData={11} />
      </ErrorBoundary>
    </>
  );
};

export default App;

 

React Native ErrorBoundary

import React, { Component } from 'react';


class ErrorBoundary extends Component {
    constructor(props) {
      super(props);
    }

    static getDerivedStateFromError(){
        console.log("getDerivedStateFromError");
    }
  
    render() {
      return this.props.children;
    }
}

export default ErrorBoundary;

 

React Native Main

 

 - Component Lifecycle에 활용되는 모든 함수를 아래와 같이 정의하여 Component 호출 시점, Props 전달 시점, State 전달 시점을 알아보려한다.  

 - 각각의 함수에 console.log를 추가하여 호출 시점을 알아보겠다.

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

class Main extends Component{
    constructor(props){
        super(props);

        console.log("constructor");
    }

    static getDerivedStateFromProps(props, state){
        console.log("getDerivedStateFromProps") ;  
    }

    shouldComponentUpdate(nextProps, nextState){
        console.log("shouldComponentUpdate");
    }

    getSnapshotBeforeUpdate(prevProps, prevState){
        console.log("getSnapshotBeforUpdate");
    }

    componentDidMount(){
        console.log("componentDidMount");
    }
    
    componentWillUnmount(){
    	console.log("componentWillUnmount");
    }

    render(){
        return (
            <View style={{flex : 1, justifyContent : 'center', alignItems:'center' }} >
                <Text>Component 입니다.</Text>
            </View>
        )
    }
}

export default Main;

 - 메인화면이 호출될 때, ( command + R 을 눌러 메인화면을 새로고침 했다. )

메인화면 로딩시

 

호출 순서 : constructor -> getDerivedStateFromProps -> componentDidMount 

 

 - 메인화면에서 버튼을 클릭했을 때, ( React Hook을 이용해 state를 component로 전달했다. )

버튼 클릭 시

 

 호출 순서 : getDerivedStateFromProps -> shouldComponentUpdate 

 

 - 메인화면에서 다른 화면으로 전환될 때

 

 호출 순서 : componentWillUnmount 

 

 - ErrorBoundary를 감싸고 있는 하위 컴포넌트에서 에러가 발생했을 때, 

 

 호출 순서 : getDerivedStateFromError

 

 

Mounting에서 부터 Unmounting 까지의 절차는 아래의 Link에서 가져온 이미지에서 잘 정리되어 있었다. 

http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

 

 

다음편에서 16.3 기점으로 나뉜 컴포넌트 생명주기 함수들에 대해서 자세히 설명할 예정이다. 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 7  (0) 2019.12.14
0001 React Native App 개발기 6-3  (0) 2019.12.13
0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22

( 해당들은 2019년 12월 12일 기준으로 작성되었음 )

 

React Native App을 개발하기 위해서 Component Cycle에 대해서 잘 알아두어야 하는데, 이번에는 Component Cycle 및 Component를 구성하는 방식에 대해서 알아보려 한다. 

 


Component's lifecycle

 

Component Lifecycle

Component의 생명주기는 4가지로 분류할 수 있다. 

 

1. Mounting

 

  컴포넌트 객체가 생성된 후, DOM으로 주입(삽입)될 때

 

  React Native의 실행되는 함수의 순서는 

 

    - constructor

    - static getDerivedStateFromProps ( React v16.3 부터 제공 )  - componentWillReceiveProps ( React v16.3 이전 ) 

    - render

    - componentDidMount

 

2. Updating

 

  브라우저 상에 React 컴포넌트가 표시되거나, 새로운 갱신(변경)이 발생했을 때

 

  React Native의 실행되는 함수 순서는 

 

    - static getDerivedStateFromProps ( React v16.3 부터 제공 )   - componentWillReceiveProps ( React v16.3 이전 ) 

    - shouldComponentUpdate

    - render 

    - getSnapshotBeforeUpdate ( React v16.3 부터 제공 ) - componentWillUpdate ( React v16.3 이전 )

    - componentDidUpdate

 

3. UnMounting

 

  해당 컴포넌트가 더이상 필요하지 않고, 컴포넌트가 해제(분리)될 때

 

  React Native 의 실행되는 함수 순서는 

   

    - componentWillUnmount

 

4. Error Handling

 

  생명주기 함수 내, 생성자 내, 모든 하위 컴포넌트의 생성자 내의 렌더링 과정 상에서 에러가 발생할 때

 

   React Native 의 실행되는 함수 순서는 

 

     - getDerivedStateFromError

     - componentDidCatch ( React v16.3 부터 제공 ) 

 

     > getDerivedStateFromError, componentDidCatch는 동일한 에러를 잡는데, 실제 전달되는 인자가 다르다. 

        ~ getDerivedStateFromError 는 "render" 단계에 호출되며, 외부 함수에 의한 영향 ( SideEffect ) 까지 포함되지 않는다. 

        ~ sideEffect가 포함되어야하는 경우에는 componentDidCatch를 사용해야 한다. 

 

위의 4가지 상태에 따라 React Native(React)에서 제공해주는 함수들이 존재하며, 화면 초기화 시점이나 state가 SetState에 의해서 변경될 때, state에 의해서 갱신이 일어날 경우, 아래에 제공하는 함수를 이용해서 우리가 원하는 기능 또는 초기화 작업 등을 처리할 수 있다. 

 

Component Lifecycle은 React v16.3 이전과 이후로 나뉘고, React v17 부터는 이전에 Deprecated된 함수들은 사용할수 없게 되었다. 

 

다음 편에서 실질적인 코드와 상세한 생명주기에 대해서 알아보겠다. 

 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-3  (0) 2019.12.13
0001 React Native App 개발기 6-2  (0) 2019.12.13
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22
0001 React Native App 개발기 3  (1) 2019.11.17

Flex를 활용한 화면 구성에 어느정도 익숙해졌다면 데이터를 이용해서 목록을 조회하여 이를 화면에 뿌려보자.

 

 


React Native의 List 컨트롤에 대해서 알아보겠다. 

 

  • FlatList

  React Native 에서 제공하는 Flat List를 이용에 데이터를 바인딩 했을 때 아래와 같이 표현된다.

Flat List

<FlatList
          data={[
            {key: 'Devin'},
            {key: 'Dan'},
            {key: 'Dominic'},
            {key: 'Jackson'},
            {key: 'James'},
            {key: 'Joel'},
            {key: 'John'},
            {key: 'Jillian'},
            {key: 'Jimmy'},
            {key: 'Julie'},
          ]}
          renderItem={({item}) => <Text style={styles.tab_1_item}>{item.key}</Text>}
          />

 

  • SectionList

React Native에서 제공하는 Section List를 이용해 데이터를 바이딩 했을 때 아래와 같이 표현할 수 있다. 

Section List

 

<SectionList
   sections={[
   {title: 'D', data: ['Devin', 'Dan', 'Dominic']},
   {title: 'J', data: ['Jackson', 'James', 'Jillian', 'Jimmy', 'Joel', 'John', 'Julie']},
   ]}
   renderItem={({item}) => <Text style={styles.tab_2_item}>{item}</Text>}
   renderSectionHeader={({section}) => <Text style={styles.tab_2_sectionHeader}>{section.title}</Text>}
   keyExtractor={(item, index) => index}
 />

 

  • List 의 아이템을 사용자의 요구사항에 따라 변경하고 싶을 때, 

FlatList나 SectionList 속성의 renderItem을 이용해서 리스트의 아이템에 아이콘을 추가하거나 버튼 외에 화면 이동 등 우리가 원하는 다양한 작업을 진행할 수 있다. 

 <View style={[styles.tab_1_container]} >
        <FlatList
          data={[
            {key: 'Devin'},
            {key: 'Dan'},
            {key: 'Dominic'},
            {key: 'Jackson'},
            {key: 'James'},
            {key: 'Joel'},
            {key: 'John'},
            {key: 'Jillian'},
            {key: 'Jimmy'},
            {key: 'Julie'},
          ]}
          renderItem={({item}) => renderItem(item)}
          />
  </View>
    
 const renderItem = (item) => {
    return (
        <TouchableOpacity style={{flex:1, flexDirection : "row"}} activeOpacity={0.8}  >  
            <View style={{flex:8, padding : 10}} >
                <Text>{item.key} </Text>
            </View>
            <TouchableOpacity style={{flex :2}} >
                 <View style={{flex:2, padding : 10}}>
                    <Image
                        source={require('./icon.png')}
                        style={styles.tab_3_icon} 
                    />  
                </View>
            </TouchableOpacity>
            
        </TouchableOpacity> 
    )
  }

 

 


그렇다면 Flat List를 Realm의 데이터를 이용해서 조회하고자 할 경우,  아래와 같이 사용할 수 있다. 

Realm의 데이터로 조회했을 때,

 

<View>
   <TouchableOpacity style={{flex:1, flexDirection : "row"}} activeOpacity={0.8}  >  
      <View style={{flex:8, padding : 10}} >
          <Text>{item.userId} </Text>
      </View>
      <TouchableOpacity style={{flex :2}} >
          <View style={{flex:2, padding : 10}}>
            <Image
            source={require('./icon.png')}
            style={styles.tab_3_icon} 
            />  
          </View>
      </TouchableOpacity>
  </TouchableOpacity> 
</View>


const LineSchema = {
  name : 'LineUser',
    properties : {
      userId : 'string',
      userPwd : 'string',
      position : 'string?'
  }
}

const realm = new Realm({schema : [ LineSchema ]});

let data = realm.objects("LineUser").map((data, index) => (
  data
));

return (
  <View style={[styles.tab_1_container]} >
    <FlatList
      data={data}
      renderItem={({item}) => renderItem(item)}
    />
  </View>
)

 

기본적인 List를 설정하고, Realm을 이용해서 바인딩하는 방식에 대해서 알아보았다. 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-2  (0) 2019.12.13
0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 4  (0) 2019.11.22
0001 React Native App 개발기 3  (1) 2019.11.17
0001 React Native App 개발기 2  (0) 2019.11.15

화면 구성하기 


 본격적으로 App을 만들기 위해서 구성한 화면이다. Timer App을 아래와 같이 만들어봤다. 

 

Timer 상위 카테고리 
Timer 하위 카테고리 

위의 화면을 구성하기 위해서 React Native에서 기본적으로 배워야할 Layout 구성 방식을 보면, 

 

1. Layout with FlexBox 

 

  Flex는  내가 지정할 View를 지정가능한 공간에 대해서 얼마나 "채울수" 있는지 지정할 수 있다. 즉 비율로 상대적 크기를 설정할 수 있다. 

 

  Flex 값은 각각의 영역을 구분하는데 효과적으로 사용할 수 있는데,

 

  예를 들어 

 

    <View style={{flex : 1}} >

         <View style={{flex : 2}}></View>

         <View style={{flex : 3}}></View>

    </View>

 

   와 같은 레이아웃으로 구성되어 있다고 할 때, 

 

  가장 외부를 감싸고 있는 View의 높이를 기준으로 내부의 View는 각각 2/5 , 3/5의 높이를 가지게 된다. 

 

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



class App extends Component {
  render(){
    return (
      <View style={{flex : 1, backgroundColor:"white"}} >
  
           <View style={{flex : 2, backgroundColor:"red"}}></View>
  
           <View style={{flex : 3, backgroundColor:'blue'}}></View>
  
      </View>
    );
  }
};

export default App;

위의 코드를 실제로 실행해보면 아래와 같이 영역이 구분되는 것을 볼 수 있다.      

2/5 , 3/5

여기에서 부모의 View를 크기 내에서 자식들의 View의 영역을 비율로 지정하는 것인데, 실제로 부모의 View의 크기를 조정하면 아래와 같이 조정할 수 있다. 

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

class App extends Component {
  render(){
    return (
      <View style={{ height:400, backgroundColor:"white" }} >
           <View style={{flex : 2, backgroundColor:"red"}}></View>
           <View style={{flex : 3, backgroundColor:'blue'}}></View>
      </View>
    );
  }
};

export default App;

강제로 View의 높이를 400으로 지정했을 때 내부의 View의 크기가 "상대적"으로 각각의 비율(2/5 , 3/5)에 따라 조정되었다. 

 

2. Flex Direction

 

 flexDirection은 각각의 하위 아이템들의 정렬 방향을 지정할 수 있다. 

 

   flexDirection : 'row' 로 지정되면, 행을 기준으로 정렬된다. 

 

<View style={{ flex :1, flexDirection : 'row', backgroundColor:"white" }} >
   <View style={{flex : 2, height : 60, backgroundColor:"red"}}></View>
   <View style={{flex : 3, height : 60, backgroundColor:'blue'}}></View>
 </View>

   

flexDirection : 'row'

    flexDirection : 'row-reverse' 로 지정되면 내부 아이템의 정렬이 반대로 적용된다. 

 

<View style={{ flex :1, flexDirection : 'row-reverse', backgroundColor:"white" }} >
           <View style={{flex : 2, height : 60, backgroundColor:"red"}}></View>
           <View style={{flex : 3, height : 60, backgroundColor:'blue'}}></View>
      </View>

row-direction : 'row-reverse'

     flexDirection : 'column' 으로 지정되면 내부 아이템이 세로로 위치된다.

 

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column", backgroundColor:"white", marginTop : 100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
      </View>
    );
  }
};

flexDirection : 'column'

     flexDirection : 'column-reverse' 으로 지정되면 아이템의 정렬 순서 및 시작위치가 반대로 위치한다.

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column-reverse", backgroundColor:"white", marginTop : 100, marginBottom :100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
      </View>
    );
  }
};

flexDirection : 'column-reverse'

3. Layout Direction

 

  Justity Content - 내부 아이템을 감싸는 Container를 기준으로 어떻게 정렬할 것인지를 지정할 수 있음 

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

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column-reverse", justifyContent : 'space-around' , backgroundColor:"white", marginTop : 100, marginBottom :100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'yellow'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'green'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'purple'}}></View>
      </View>
    );
  }
};

export default App;

     

  Align Items - 내부 아이템을 감싸는 Continer의 가로축을 기준으로 어떻게 정렬할 것인지를 지정할 수 있음 

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

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column-reverse", alignItems : 'center', backgroundColor:"white", marginTop : 100, marginBottom :100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, alignSelf : 'flex-start', backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'yellow'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'green'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'purple'}}></View>
      </View>
    );
  }
};

export default App;

  Align Self  - 내부 아이템 각각의 정렬 어떻게 정렬할 지를 지정할 수 있고 만약 부모 Container에서 Align Item이 선언되어 있다면 해당 속성은 AlignSelf에 의해 무시된다. 

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

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column-reverse", alignItems : 'center', backgroundColor:"white", marginTop : 100, marginBottom :100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, alignSelf : 'flex-start', backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'yellow'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'green'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'purple'}}></View>
      </View>
    );
  }
};

export default App;

  Align Content - 횡축을 기준으로 분포를 정의하는데, 이는 flexWrap을 사용하는 다중 라인(줄)으로 감쌀때만 적용된다. 

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

class App extends Component {
  render(){
    return (
      <View style={{ flex :1, flexDirection : "column-reverse", flexWrap : 'wrap', alignContent : 'center', backgroundColor:"white", marginTop : 100, marginBottom :100, backgroundColor : '#00ffff' }} >
           <View style={{height : 60, width : 60, backgroundColor:"red"}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'blue'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'yellow'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'green'}}></View>
           <View style={{height : 60, width : 60, backgroundColor:'purple'}}></View>
      </View>
    );
  }
};

export default App;

 Flex Wrap - 내부의 아이템의 Container의 사이즈를 벗어나는 것을 제어하고자 할 때 사용할 수 있다. alignContent는 아이템에 Container 영역을 넘어서서 다음칸으로 채워질 때 어떤 정렬로 채워지게 할 것인가를 정할 수 있다. 

 

 그외에도 

 

 flexGrow, flexShrink, flexBasis 등의 옵션이 존재한다. 

 

 

 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 3  (1) 2019.11.17
0001 React Native App 개발기 2  (0) 2019.11.15
0001 React Native App 개발기 1  (0) 2019.11.15

React Native와 Realm 

 

로그인 정보를 App Data로 저장하기


로그인 정보를 App내에서 사용하기 위해서 모바일 App Database로 활용할 수 있는 다양한 App을 찾아 본 결과 

 

  • SQLite
  • Realm

크게는 SQLite 와 Realm을 주로 많이 사용하는 것으로 보이며, 이전에 SQLite로 개발했을 때 마치 RDB를 설계하는 듯한 느낌이 들어서

별로였었는데, Realm의 심플함에 반해서 Realm으로 넘어왔다. Mobile Database로 Realm을 추천하는 사례들도 많아졌기에 이번 App을 개발시에 적용해 보려한다. 

 

Realm 설치 


$ cd {React Native 프로젝트 경로}

 

$ npm insall --save realm

Realm 설치

 

$ react-native link realm

realm 연동

$ cd ios 

$ pod install

pod install 실행

 

이후 XCode를 실행시켜  Realm을 사용하는 방법을 알아보겠다.

 

 

 

Realm 사용하기 


Realm을 사용하기위한 DataBase Component 작성 

var Realm = require('realm');

const DataBase = ( props ) => {
    
    const LoginSchema = {
        name : 'Login',
        properties : {
            userId : 'string'
        }
    }

    const CategorySchema = {
        name: 'Category',
        properties: {
            categorySeqNo : 'int',
            categoryName  : 'string',
        }
    };

    const CategoryListSchema = {
        name: 'CategoryList',
        properties : {
            categoryList : { type : 'list' , objectType : 'Category' }
        }
    }

    let realm = new Realm({ schema : [LoginSchema, CategoryListSchema, CategorySchema]});

    return realm;
}

export default DataBase;

로그인의 일부 코드 

  saveUserId 함수를 보면, Realm을 이용해서 데이터를 저장하고, 가져오는 방식을 간단하게 작성해봤다.

  좀더 상세한 활용 방법 및 사용법은 https://realm.io/docs/javascript/latest/ 링크를 접속해서 확인할 수 있다. 

import React, {Component} from 'react';
import { View, Text, TextInput, StyleSheet, TouchableHighlight } from 'react-native';
import { widthPercentageToDP as wp } from 'react-native-responsive-screen';
import DataBase from '../../../components/database';

export default class LoginScreen extends Component {
    
    constructor(props) {
      super(props);
      this.state = {
        email   : '',
        password: '',
      }

      this.realm = DataBase();

      console.log(this.realm)
    }
      
    static navigationOptions = {
        header: null,
    }

    saveUserId = async userIdValue => {
      try {
        let login = this.realm.objects('Login');

        if(!login["0"].userId){
          this.realm.write(() =>{
            this.realm.create('Login', { userId :  userIdValue })
          });
        }
        
        console.log(login);
        console.log(login["0"].userId);

      } catch (error) {
        // Error retrieving data
        console.log(error.message);
      }
    }

로그인 화면에서 id를 입력하고 값을 가져오는 방법 위와 같이 작성하면, 아래와 같은 결과를 얻을 수 있었다. 

shj@lines.com을 4번을 입력하고 그 중 첫번째 userId만 가져오도록 한 결과이다. 

 

Realm에 대해서는 추후에 별도로 정리할 예정이다. 

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22
0001 React Native App 개발기 2  (0) 2019.11.15
0001 React Native App 개발기 1  (0) 2019.11.15

작성일 : 2019년 11월 16일 

 

React-Native 기반하에 운동 Timer를 개발해보고자 한다. 

 


개발에 필요한 것

  • XCode ( Version 11.2 ) 
  • Terminal
  • Visual Studio Code 

개발 시 사용할 Modules

  • realm : App 데이터 저장소 
  • react-native-clean-form 
  • react-native-countdown-component 
  • react-navigation, react-navigation-statck, react-navigation-tabs

이외에도 다양한 모듈이 사용되었지만 메인이되었던 항목들을 나열하였다. 

 

App.js 구성 


React Native App이 진입점은 index.js 파일이고, index.js 파일을 보면 아래와 같이 구성되어 있다. 

/**
 * @format
 */

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

AppRegistry.registerComponent(appName, () => App);

AppRegistry

 

Index.js 파일에서 로딩되는 App 파일을 기점으로 하여 간단하게 Timer App을 만들어보겠다.

 


import에 대해서 알고 싶으면 [더보기]를 클릭하세요.

더보기

import 와 export는 ES6(EcmaScript2015)에서 도입되었다. 현재 (2019년 11월 15일 현재) 브라우저에서 기본으로 제공되고 있지 

않습니다. 따라서 별도의 트랜스파일러(Transpiler)에 의해서 동작할 수 있습니다. 

 

아래는 Import와 Export를 사용하는 간단한 예시이며, 이외에도 다양한 방법으로 사용할 수 있다. 

// import 를 사용하는 간단한 예제 
import example from "example-module";
import example as ex from "example-module";
import { example1 } from "example-modules";
import { example1, example2 } from "example-modules";

// export 를 사용하는 간단한 예제 
export { example1 };
export example1;
export default example1; // 각각의 모듈 당 단하나의 default만 가질 수 있습니다. 
export let example1 = ..., example2 = ...;
export { example1, example2 } from ...;
 

 

개발을 위한 Module 설치


react-navigation은 V4를 사용한다.

 

npm을 통해 아래의 명령어를 실행하고, 

$ npm install --save react-navigation react-navigation-tabs react-navigation-stack

 

pod install 실행 

$ pod install 

 

하지만 제대로 실행되지 않고... 아래와같은 메세지가 뜬다.

pod install 시 발생한 에러 

그래서 아래의 모듈을 다시한번 설치해주고,

 

$ npm install --save @react-native-community/cli-platform-ios

 

이래도 pod install 시에 정상적으로 설치가 되지 않는다. 

pod install 시 에러

그래서 다시 한번 react-native app 폴더에서 npm install 을 진행하였다. 그랬더니 그제서야 정상적으로 pod install 이 동작했다. 

pod install 이 정상적으로 설치

 

 

pod install이 정상적으로 진행되고 난 뒤 xcworkspace 프로젝트를 열어 App을 실행했더니...

기동 이후 화면 로딩 시 에러 발생

 

흠... 알고 봤더니 @react-native-community/cli-platform-ios 이걸 설치하던 부분 부터 다시정리해보면,

react-navivation을 사용하기 위해 필요한 Package.json을 다시 아래와 같이 정의하고,

 

[package.json] 파일의 일부 , 

"react-native-gesture-handler": "^1.5.0",

"react-native-reanimated": "^1.4.0",

"react-navigation": "^4.0.10",

"react-navigation-stack": "^1.10.3",

"react-navigation-tabs": "^2.5.6"

 

package.json 파일 

아래와 같은 작업을 다시 실시하였다. 


  1. $ rm -rf node_modules
  2. package.json 재확인하여 정리 
  3. $ npm install 
  4. $ cd ios
  5. $ pod install 진행 
  6. xcworkspace 재오픈 처리 

최종 컴파일 화면


아래의 소스 코드는 로그인화면의 소스 코드이다. 로그인 화면은 구글링을 통해 제공되던 샘플을 공유한다. 

import React, {Component} from 'react';
import { View, Text, TextInput, StyleSheet, TouchableHighlight } from 'react-native';
import { widthPercentageToDP as wp } from 'react-native-responsive-screen';

export default class LoginScreen extends Component {
    
    constructor(props) {
      super(props);
      this.state = {
        email   : '',
        password: '',
      }
    }
      
    static navigationOptions = {
        header: null,
    }

    saveUserId = async userId => {
      try {
        await AsyncStorage.setItem('userId', userId);
      } catch (error) {
        // Error retrieving data
        console.log(error.message);
      }
    }

    onClickListener = (viewId) => {
      this.getMoviesFromApiAsync(() => { this.props.navigation.replace('TabNavigator') })      
    }

    getMoviesFromApiAsync = (executeFunc) => {

      if(this.state.email == '' || this.state.password  == '' ){
        alert("로그인을 위해서 값을 입력해주세요.")
        return;
      }

      const formData = new FormData();
      formData.append('email', this.state.email);
      formData.append('password', this.state.password);

      this.saveUserId(this.state.email);

      return fetch('http://localhost:9090/v1/accounts/login',
        {
          method: 'POST',
          body: formData
        })
        .then((response) => response.json())
        .then((responseJson) => {
          if(responseJson.userStatus.code == "2000"){
            executeFunc()
          }else{
            alert(responseJson.userStatus.message);
          }

          return responseJson.movies;
        })
        .catch((error) => {
          console.error(error);
        })
    }

    serializeJSON = (data) => {
      return Object.keys(data).map(function (keyName) {
        return encodeURIComponent(keyName) + '=' + encodeURIComponent(data[keyName])
      }).join('&');
    }


    render(){
        return (
        <View style={styles.container}>
            <View style={styles.titleArea}>
                <Text style={styles.title}>Excercise Timer</Text>
            </View>
            <View style={styles.inputContainer}>
              <TextInput style={styles.inputs}
                  placeholder="Email"
                  keyboardType="email-address"
                  autoCapitalize = 'none'
                  underlineColorAndroid='transparent'
                  onChangeText={(email) => this.setState({email})}/>
            </View>
            
            <View style={styles.inputContainer}>
              <TextInput style={styles.inputs}
                  placeholder="Password"
                  secureTextEntry={true}
                  underlineColorAndroid='transparent'
                  onChangeText={(password) => this.setState({password})}/>
            </View>
    
            <TouchableHighlight style={[styles.buttonContainer, styles.loginButton]} onPress={() => this.onClickListener('login')}>
              <Text style={styles.loginText}>Login</Text>
            </TouchableHighlight>


            <TouchableHighlight style={styles.buttonContainer} onPress={() => this.onClickListener('restore_password')}>
                <Text>Forgot your password?</Text>
            </TouchableHighlight>
          </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
      flex: 1,
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: '#616155',
    },
    titleArea: {
        width: '100%',
        padding: wp('10%'),
        alignItems: 'center',
    },
    title: {
        fontSize: wp('10%'),
    },
    inputContainer: {
        borderBottomColor: '#F5FCFF',
        backgroundColor: '#FFFFFF',
        borderRadius:30,
        borderBottomWidth: 1,
        width:250,
        height:45,
        marginBottom:20,
        flexDirection: 'row',
        alignItems:'center'
    },
    inputs:{
        height:45,
        marginLeft:16,
        borderBottomColor: '#FFFFFF',
        flex:1,
    },
    inputIcon:{
      width:30,
      height:30,
      marginLeft:15,
      justifyContent: 'center'
    },
    buttonContainer: {
      height:45,
      flexDirection: 'row',
      justifyContent: 'center',
      alignItems: 'center',
      marginBottom:20,
      width:250,
      borderRadius:30,
    },
    loginButton: {
      backgroundColor: "#00b5ec",
    },
    loginText: {
      color: 'white',
    }
  });
  

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22
0001 React Native App 개발기 3  (1) 2019.11.17
0001 React Native App 개발기 1  (0) 2019.11.15

React Native 를 활용한 App 개발에 대해서 설명하기전에 React Native 사이트에서 제공하는 가이드에 따라서 Mobile App 을 만들어 보겠다. 


2019년 11월 14일 기준 ( React Native Cli 기반 개발 ) - MacOS 버전 

  > 필자의 노트북이 MacBook이다보니 양해 바란다. 

 

 

 

 React Native 기반으로 개발하는 방식으로 Expo Cli 또는 React Native Cli 를 이용하여 App Template을 만들 수 있는데, 가급적이면 React Native Cli를 이용하는 방식을 추천한다. 필자 또한 Expo를 이용하여 개발해봤으나 결국 Native 모듈을 사용함에 있어 어려움을 느껴 RN(React Native)로 넘어왔다.

 

 [더보기]를 누르면 expo eject에 대해서 간단하게 정리해봤다.

더보기

 만약 Expo를 이용해서 App을 개발하던 중에 Native Code를 사용하기 위해서 "ejected"를  진행할수 있다.

 실제로 Expo 를 이용해서 진행하지는 않을 예정이지만, 

 

 $ expo eject 

 

eject 명령에 대해서 아래와 같이 선택할 수 있는데, Bare, ExpoKit, Cancel을 선택할 수 있다. 

expo eject 시 진행되는 절차 

 

홈 화면 및 프로젝트 명 설정

빨간색으로 표시한 네모칸이 실제 Android Studio, XCode를 이용해서 React Native를 기동시킬 수 있는 프로젝트 폴더이다.

expo eject 후 폴더 구성 

 

 

 

React Native Cli를 이용한 모바일 프로젝트 생성 


  • Brew가 없다면 HomeBrew 설치 
  • node 설치 - React Native를 실행시키기 위한 필수 모듈이다.
  • cocoapods 설치 - React Native를 ios 에서 돌릴 때, react-native link {모듈명}으로 설치된 모듈에 대한 pod install 시에도 사용된다.
  • react-native 를 이용한 프로젝트 생성 
  • XCode를 통한 프로젝트 Open
  • XCode를 통한 프로젝트 실행

$brew install node

 

$sudo gem install cocoapods

 

$npx react-native init {프로젝트 명}

Example ) npx react-native init workingTimer 

 

생성된 프로젝트의 폴더 내부를 보면, 

 

react-naive 기준으로 생성된 프로젝트

위의 이미지에서 [ios]가 실제로 XCode에서 프로젝트를 열어서 개발을 진행할 폴더이다. 

[android] 폴더는 Android Studio에서 프로젝트를 열어서 개발을 진행할 폴더이다. 

 

npx, gem, cocoapods에 대해 알아보기


npx, gem 에 대해서 알고 싶다면 [더보기]클릭하라. 

더보기

[npx]

npm 5.2.0 이후부터, 

 새로운 바이너리로 npm 과 함께 npx가 소개되었다. 

 

 npm 은 지년 수년간 packages를 로컬과 전역에 설치하는 방식에 대해서 증진을 이뤘다. 

 그중에 기존에 로컬에 package를 설치하기 위해서 사용하던 dev-dependencies가 있었는데, 이를 없애고자 하는 

 움직임도 있었다. 이런 움직임 속에서 제공되기 시작한 것이 npx이다,. 

 

 npx : npm package runner 로 명시적으로 설치하지 않고 패키지를 실행시키는 것을 돕는다. 

 

  • 일회성 명령을 실행 시킬 때,
  • Gist 기반 scripts를 실행 시킬 때, 
  • npm module의 다른 버전을 사용하고 싶을 때,
  • 전역적으로 설치할 수 있는 권한이 없을 때,

즉 

$ npx react-native init workingTimer 

 

 위의 의미는 react-native라는 모듈을 별도의 설치 없이 패키지를 실행시킨 것이라고 생각하면 된다.

 

[gem]

 

 gem은 ruby에서 쓰이는 라이브러리 패키지이다. 

 

 cocoapods가 RubyGems와 Bundler의 조합에 영감을 받아 만들어졌기 때문에 rubygems를 이용해서 라이브러리

를 관리한다. 

 

cocoaPods 에 대해서 알고 싶다면 [더보기]를 클릭하라. 

더보기

[cocoapods]

CocoaPods는 Swift 및 Objective-C 프로젝트의 라이브러리 관리자

Maven이나 Gradle과 느낌이 유사하다. 

 

$ sudo gem install cocoapods

 

Podfile - 종속성 관리를 위한 파일 : pod install, pod update 등 명령어를 사용할 때 필요한 파일이다. 

 

$ pod install

  새로운 pod를 다운로드 받고 설치한다. pod install의 경우에는 

 

$ pod outdated

 모든 pod에 대해서 Podfile.lock 안에 목록화된 것보다 신규 버전일 경우 모드 목록화 한다. 

 이후 pod install을 하게되면 해당 pod들은 모두 업데이트 될 것이다. 

 

$ pod update {PODNAME} 

  설치되어 있는 Pod들을 PODNAME에 따라서 버전 업데이트에 사용할 수 있으며 POSNAME을 빼면 PodFile의 목록을 기준으로 업데이트 처리한다. 

 

install 명령를 update 명령어 처럼 이미 설치된 pod에 대해서는 사용하지 않는다.

 

 

XCode에서 프로젝트 열기 


ios 폴더 내의 파일

위에서 우리는 XCode에서 xcodeproj, xcworkspace 중에  어떤 파일로 열어야 할까?

 

React Native를 이용한 App을 구성할 때 CocoaPods를 사용했기 때문에 HealthTimer.xcworkspace를 이용해서 프로젝트를 열어야 한다. 이는 cocoapods를 이용하면 생성한 프로젝트를 내부에 별도로 구성하면서 xcworkspace로 구성하고 있다. 

 

xcodeproj와 xcworkpace의 차이를 보려면 [더보기]를 눌러주세요. 

더보기

[xcodeproj]

 HealthTimer.xcodeproj 는 실행할 때도 쓰지만, 프로젝트의 설정파일들이 포함되어 있다. 

 제품을 만들기 위한 모든 파일과 리소스, 정보들을 가지고 있다.

 

[xcworkspace] 

 HealthTimer.xcodeproj 를 포함한 프로젝트를 논리적으로 묶어주는 역할을 한다. 

 즉 xcworkspace는 xcodeproj를 큰 틀에서 묶을 수 있다. 

 

 

Health Timer 폴더을 열었을 때의 구성 

위와 같이 xcworkspace을 이용해서 프로젝트를 열어서 표시된 XCode 편집창이다. 

 

 

프로젝트 실행해보기 


XCode 편집창의 삼각형 실행 버튼을 실행한다. 

 

XCode 편집창의 좌측 실행 버튼 

 

실제 XCode를 통해서 실행을 시키면 최종적으로 아래와 같이 실행된다. 

 

XCode와 연동하여 실행한 React Native 기본 화면 

 

실행시 같이 동작되는 MetroBundler와 XCode의 상관 관계를 알고 싶다면 [더보기]를 눌러주세요.

더보기

[MetroBundler]

 React Native App은 자바스크립트를 기반으로 컴파일되는 App이다. React Native Project를 빌드 할 때, Packager가 MetroBundler를 호출한다. 이는 webpack과 비슷하지만 React Native App을 위한 것이다.

 

 Metro는 기본적으로 8081 포트를 이용해서 React Native App과 통신한다. 

 

 즉, Java나 Swift 처럼 번역되는 것이 아닌 우리가 짠 React Native Code를 Native Modules에 의해서 Javascript Thread가 실행시키는 방식이다. 즉 Metro는 React Native App과 내부적으로 통신을 한다. ( 이때 기본 포트가 8081이다 ) 

 

 XCode를 이용해서 React Native App을 실행 시킨 후에 아래의 Link로 들어가보면 조금 이해될 수 있다.

 http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false

 

 기본 컨셉에 대해서는 링크에서 확인할 수 있다. - https://facebook.github.io/metro/docs/en/concepts

'따라해보기' 카테고리의 다른 글

0001 React Native App 개발기 6-1  (0) 2019.12.12
0001 React Native App 개발기 5  (0) 2019.12.08
0001 React Native App 개발기 4  (0) 2019.11.22
0001 React Native App 개발기 3  (1) 2019.11.17
0001 React Native App 개발기 2  (0) 2019.11.15

+ Recent posts