지난 편(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

이번에는 Reactive Stream을 이용해서 실질적으로 현업에서 사용할 만한 코드를 작성해보겠다. 

 

 요구사항 

 

   - Oracle DB , OJdbc, DBCP2

   - org.reactivesreams

   - ExecutorService

 

  A, B라는 Oracle DB User에 대해서 TB_USER로 부터 USER_ID를 가져와 출력하라는 요구 사항을 접수 받았다. 하지만 이 DB User의 수는 상황에 따라서 증가하거나 줄어들 수 있으며 그에 따라 고객이 빠른 응답을 받을 수 있도록 구성이 필요하다고 하자. 이와 같은 요구사항을 ReactiveStream(Java8)을 이용해서 구현해본다. 


1번 : Main 진입점 

 

 DataSource를 생성한뒤 각각의 Command 객체 생성시 전달하여 DB Select시에 사용하려고 한다. 

 해당 부분은 Controller 를 통해서 전달받은 파라미터 및 구분자에 의해서 동적으로 변경될 수 있는 부분이라고 가정한다. 

즉 commandHadlerList의 배열이 요청 갯수에 따라서 증가하거나 감소할 수 있다. 

public static void main(String[] args) {

        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
        dataSource.setUrl("jdbc:oracle:thin:@***********:****/****");
        dataSource.setUsername("*****");
        dataSource.setPassword("*****");

        BasicDataSource dataSource2 = new BasicDataSource();
        dataSource2.setDriverClassName("oracle.jdbc.driver.OracleDriver");
        dataSource2.setUrl("jdbc:oracle:thin:@***********:****/****");
        dataSource2.setUsername("*****");
        dataSource2.setPassword("*****");

        CommandHandler commandHandler1 = new CommandHandler(1);

        commandHandler1.add(new SelectDBCommand(dataSource));
        commandHandler1.add(new SelectDBCommand(dataSource2));

        List<CommandHandler> commandHandlerList = new ArrayList<>();

        commandHandlerList.add(commandHandler1);

        Publisher<CommandHandler> publisher = new SamplePublisher(commandHandlerList);

        Subscriber<CommandHandler> subscriber = new SampleSubscriber(commandHandlerList.size());

        publisher.subscribe(subscriber);
        
}

2번 : Publisher 

 

 Observer 패턴의  Observable과 같은 역할을 하며, Subscriber를 구독할 수 있게 설정하여 실행시 Publihser에서 Subscriber로 onNext, onComplete, onError를 처리할 수 있다. 

public class SamplePublisher implements Publisher<CommandHandler> {

    private final List<CommandHandler> commandHandlerList;

    public SamplePublisher(List<CommandHandler> commandHandlerList){
        this.commandHandlerList = commandHandlerList;
    }

    @Override
    public void subscribe(Subscriber<? super CommandHandler> subscriber) {
        subscriber.onSubscribe(new Subscription() {
            @Override
            public void request(long n) {
                commandHandlerList.forEach(subscriber::onNext);

                // 데이터 전송 처리 완료
                subscriber.onComplete();
            }

            @Override
            public void cancel() {

            }
        });
    }
}

3번 : Subcriber - Publisher 내부에서 호출한 각기 함수(onNext, onComplete, onError)에 맞게 진행된다. 

 

 SampleSubscriber가 생성되는 시점에 만들어진 ExectorService에 대해서  Java의 Future를 활용하여 onComplete 시점에 ExecutorService를 Shutdown 시킬 수 있게 처리하였다. 

public class SampleSubscriber implements Subscriber<CommandHandler> {

    private final ExecutorService executorService;

    List<Future<?>> futures = new ArrayList<>();

    public SampleSubscriber(int executeCount){
        this.executorService = Executors.newFixedThreadPool(executeCount);
    }

    @Override
    public void onSubscribe(Subscription subscription) {
        subscription.request(1);
    }

    @Override
    public void onNext(CommandHandler commandHandler) {
        futures.add(executorService.submit(commandHandler::StartAPICall));
    }

    @Override
    public void onError(Throwable t) {

    }

    @Override
    public void onComplete() {
        for(Future<?> future : futures) {
            try {
                future.get();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }
}

4번 : Command Handler 

 

3번 항목과 처리되는 방식은 거의 유사하며, Command Handler에서 각각의 Command를 ExecutorService를 이용하여  실행시키는 구조이다. 

public class CommandHandler {

    private final List<Command> commandList = new ArrayList<>();
    List<Future<?>> futures = new ArrayList<>();

    private final ExecutorService executorService;

    public CommandHandler(int threadCount){
        executorService = Executors.newFixedThreadPool(threadCount);
    }

    public void StartAPICall() {

        commandList.forEach(command -> {
            futures.add(executorService.submit(command::execute, "Success"));
        });

        for(Future<?> future : futures) {
            try {
                future.get();
            }catch(Exception e){
                e.printStackTrace();
            }
        }

        executorService.shutdown();
    }

    public synchronized void add(Command command) {
        commandList.add(command);
    }
}

5번 : Command 

public interface Command {
    public void execute();
}

6번 : Command 구현 - SelectDBCommand 

 

 실질적으로 데이터를 조회하고, 이를 용도에 맞게 처리하는 Command의 구현체이다. SelectDBCommand 객체가 

생성되는 시점에 필요한 정보를 모두 합성 방식을 이용하여 미리 준비하고, execute가 호출 되는 시점에 활용한다. 

아래와 같은 방식으로 구성했을 때 ThreadSafe하게 구현되었다고 할 수 있다. 

public class SelectDBCommand implements Command {

    private final DataSource dataSource;

    public SelectDBCommand(DataSource dataSource){
        this.dataSource =  dataSource;
    }

    @Override
    public void execute() {

        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        List<User> list =  jdbcTemplate.query("SELECT USER_ID FROM *********", new CustomMapper());

        System.out.println("START" + Thread.currentThread().getName());
        list.stream().forEach(item -> {
            System.out.print(item.getUSER_ID() + ",");
        });
        System.out.println("END" + Thread.currentThread().getName());
    }
}

7번 : CustomMapper - JDBC Rowmapper 

 

JdbcTemplate의 RowMapper를 구현하여 필요에 맞게 값을 바인딩 처리한다. 

public class CustomMapper implements RowMapper<User> {
    @Override
    public User mapRow(ResultSet resultSet, int i) throws SQLException {
        User user = new User();
        user.setUSER_ID(resultSet.getString("USER_ID"));
        return user;
    }
}

'알아보기' 카테고리의 다른 글

0006 WebJars  (0) 2019.12.18
0002 Reactive Programming 3  (0) 2019.12.08
0004 Reactive Programming 2  (0) 2019.12.05
0004 Reactive Programming 1  (0) 2019.12.04
0002 Realm 활용하기 2  (0) 2019.11.22

+ Recent posts