Reactive Programming 2 편에서 다루었던 개발건에 추가적인 요구사항에 생겼다고 해보자, 

 

 

 요구사항

 

 ( 2편의 요구사항 )

   일정한 시간을 주기로 2000건의 정수 배열을 받은 후 해당 각각의 결과 값을 합하는 요구사항이 발생하였다.

   생성해야할 정수 배열은 난수로 이루어진 값으로 1000개의 아이템을 가지고 있다. 

 

 추가 요구사항 

    난수로 생성되는 2000건의 정수 배열에 대해서 각 배열의 총합이  음수인 값은 제외하고, 양수인 값을 추출하여하 출력한다.  

 

 ( 사실, 자바8의 Stream API를 사용한다면 이런 힘들고 복잡한 작업을 전혀할 필요가 없다. 우리는 Reactive Stream의 기본이 되는 개념을 익히고 이를 실질적으로 활용하기 위한 Akka, RXJava, Java9 Flow API 등등을 쉽게 이해하기 위해 진행 중인 것이다. )

 


2편에서 다룬 코드 샘플을 아래와 같이 변경하였다. 

아래의 코드에서 중요한 부분은 Publisher 1, Publisher 2를 연결하면서 처리되는 Data 흐름이다.  

데이터 전달 순서 : CustomPublisher -> FilterPublisher -> CustomSubscriber  

 

1번 : Main 진입점

public static void main(String[] args) {
	// 1000개의 난수 배열을 가진 아이템 생성 - 각각을 합쳐

	BlockingQueue<Command> blockingQueue = new ArrayBlockingQueue<Command>(500);

	for (int i = 0; i < 500; i++) {

	IntStream intStream = new Random().ints();

	List<Integer> integerList = intStream.limit(1000).boxed().collect(Collectors.toList());

	Command command = new IntegerSumCommand(integerList);

	blockingQueue.add(command);
}

// Publisher 1
Publisher<Command> publisher = new CustomPublisher(blockingQueue);

// Publisher 2
Publisher<Command> publisher1 = new FilterPublisher(publisher);

// Subscriber
Subscriber subscriber = new CustomSubscriber();

// Start !
publisher1.subscribe(subscriber);
}


2번 : Publisher 1 : Publisher를 구현한 CustomPublisher 

public class CustomPublisher implements Publisher<Command> {

    private final BlockingQueue<Command> blockingQueue;

    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

    public CustomPublisher(BlockingQueue<Command> blockingQueue){
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void subscribe(Subscriber subscriber) {

        subscriber.onSubscribe(new Subscription() {
            @Override
            public void request(long n) {
                while (!blockingQueue.isEmpty()){
                    try {
                        Command command = blockingQueue.take();

                        subscriber.onNext(command);

                    } catch (InterruptedException e) {

                        e.printStackTrace();
                    }
                }

                scheduledExecutorService.schedule(() -> {
                    if(Counter.get() == 500){
                        subscriber.onComplete();

                        if(!scheduledExecutorService.isShutdown()){
                            scheduledExecutorService.shutdown();
                        }
                    }
                }, 1, TimeUnit.SECONDS);

            }

            @Override
            public void cancel() {

            }
        });
    }
}

 

3번 : Publisher 2 : Publisher 1을 전달 받아 값을 필터링하는 FilterPublisher

public class FilterPublisher implements Publisher<Command> {

    private Publisher<Command> publisher;

    public FilterPublisher(Publisher<Command> publisher){
        this.publisher = publisher;
    }

    @Override
    public void subscribe(Subscriber<? super Command> subscriber) {
        this.publisher.subscribe(new Subscriber<Command>() {
            @Override
            public void onSubscribe(Subscription subscription) {
                subscriber.onSubscribe(subscription);
            }

            @Override
            public void onNext(Command command) {
                if(!command.isMinus()){
                    subscriber.onNext(command);
                }
            }

            @Override
            public void onError(Throwable t) {
                subscriber.onError(t);
            }

            @Override
            public void onComplete() {
                subscriber.onComplete();
            }
        });
    }
}

4번 : Subscriber : 최종적으로 Publisher 2의 데이터 처리를 전달받아 최종값을 출력한다.

public class CustomSubscriber implements Subscriber<Command> {

    private ExecutorService executorService = Executors
            .newFixedThreadPool(1000);

    private Subscription subscription;

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

    @Override
    public void onNext(Command command) {
        executorService.execute(() -> {
            int total = command.getTotal();

            System.out.println(String.format("총 값은 %s", total));
        });
    }

    @Override
    public void onError(Throwable throwable) {
        subscription.cancel();
    }

    @Override
    public void onComplete() {
        if(!executorService.isShutdown()){
            executorService.shutdown();
        }
    }
}

 

5번 : Command Interface 

public interface Command {
    public int getTotal();
    public boolean isMinus();
}

 

6번 : Command Implementation 

public class IntegerHandlerCommand implements Command {

    private Optional<Integer> optionalInteger;

    public IntegerHandlerCommand(List<Integer> integerList){

        Counter.addAndGet();

        this.optionalInteger = integerList.stream().reduce(Integer::sum);
    }

    @Override
    public int getTotal() {
        return this.optionalInteger.orElse(0);
    }

    @Override
    public boolean isMinus() {
        return this.optionalInteger.orElse(0) < 0;
    }
}

 

7번 : Counter Class - Executor Service 종료를 위한 Counter 서비스 

public class Counter {
    private static int count = 0;

    public synchronized static int addAndGet(){
        return count ++;
    }

    public synchronized static int get(){
        return count;
    }
}

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

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

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

Reactive Stream을 다루기 위해서 아래의 예제를 바탕으로 요구조건을 변경해가면서 코드 위주로 알아보겠다. 

 

 요구사항 

 

   일정한 시간을 주기로 2000건의 정수 배열을 받은 후 해당 각각의 결과 값을 합하는 요구사항이 발생하였다.

   생성해야할 정수 배열은 난수로 이루어진 값으로 1000개의 아이템을 가지고 있다. 

 

 

Reactive Stream Java 8 버전 기준 


1번, Main :  진입점

public class Sample05_reactiveStream04 {

    public static void main(String[] args) {

        BlockingQueue<Command> blockingQueue = new ArrayBlockingQueue<Command>(2000);

        for (int i = 0; i < 2000 ; i++) {
            IntStream randomValueList = new Random().ints();

            List<Integer> integerList = randomValueList.limit(1000).boxed().collect(Collectors.toList());

            Command command = new InsertToDataBaseCommand(integerList);

            blockingQueue.add(command);
        }

        Publisher publisher = new CustomPublisher(blockingQueue);

        Subscriber subscriber = new CustomSubscriber();

        publisher.subscribe(subscriber);
    }
}

 

2번, Publisher : Publisher를 구현한 CustomPublisher 

public class CustomPublisher<Command> implements Publisher<Command> {

    private final BlockingQueue<Command> blockingQueue;

    private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    public CustomPublisher(BlockingQueue<Command> blockingQueue){
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void subscribe(Subscriber<? super Command> subscriber) {
        subscriber.onSubscribe(new Subscription() {
            @Override
            public void request(long n) {
                while (!blockingQueue.isEmpty()) {
                    try {
                        Command command = blockingQueue.take();

                        subscriber.onNext(command);

                    } catch (InterruptedException e) {
                        // Exception handling.
                    }
                }

                executorService.schedule(() -> {
                    if(Counter.get() == 2000){
                        System.out.println("onComplete");
                        subscriber.onComplete();

                        executorService.shutdown();
                    }
                }, 1, TimeUnit.SECONDS);
            }

            @Override
            public void cancel() {

            }
        });
    }
}

 

3번, Subscriber : Subscriber를 구현한 CustomSubscriber 

public class CustomSubscriber implements Subscriber<Command> {

    private Subscription subscription;

    private final ExecutorService executorService = Executors.newFixedThreadPool(1000);

    @Override
    public void onSubscribe(Subscription subscription) {
        this.subscription = subscription;

        this.subscription.request(1);
    }

    @Override
    public void onNext(Command command) {
        executorService.execute(command::work);
    }

    @Override
    public void onError(Throwable t) {
        System.out.println("We Error");
    }

    @Override
    public void onComplete() {
        System.out.println("We Finished");

        executorService.shutdown();
    }
}

 

4번, Command Interface

public interface Command {
    public void work();
}

 

5번, Command Implementation : 실제 구현 객체 

public class InsertToDataBaseCommand implements Command {

    private final List<Integer> integerList;

    public InsertToDataBaseCommand(List<Integer> listInteger){
        integerList = listInteger;
    }

    @Override
    public void work() {
        Optional<Integer> sum = integerList.stream().reduce(Integer::sum);

        System.out.println(Thread.currentThread().getName() +  " Final Result : " + sum);
        System.out.println(Thread.currentThread().getName()  + " " + Counter.incrementAndGet());

    }
}

 

6번, Counter Class :  수행된 횟수를 저장하고, 가져온다. 

public class Counter {

    private static final AtomicInteger atomicInteger = new AtomicInteger();

    public static int incrementAndGet(){
        return atomicInteger.incrementAndGet();
    }

    public static int get(){
        return atomicInteger.get();
    }
}

 


 

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

0002 Reactive Programming 4  (0) 2019.12.09
0002 Reactive Programming 3  (0) 2019.12.08
0004 Reactive Programming 1  (0) 2019.12.04
0002 Realm 활용하기 2  (0) 2019.11.22
0002 Realm 활용하기 3  (0) 2019.11.22

+ Recent posts