[10분 테코톡] 닉의 Spring vs Spring Boot

- 영상: https://www.youtube.com/watch?v=6h9qmKWK6Io

 

 

내용 정리

 

 

 

 

"Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

스프링 부트는 독립적이고 상용 수준의 스프링 기반 애플리케이션을 쉽게 만들 수 있게 해준다.

 

 

<비교>

  Spring  Spring Boot
Dependency 설정 모든 의존성을 버전까지 정확하게 설정 간략해지고 버전 관리도 권장 버전으로 자동 설정
Configuration 설정 설정량이 많다 설정량이 적다
서버 구동 시간 외부에서 다운 받아 설치과정 필요 - 톰캣과 jetty를 내장 서버(embedded server)로 갖추고 있어서 절반 가까이 단축
- 내장 서블릿 컨테이너 덕분에 jar 파일로 간단 배포 가능

 

- application.properties vs application.yml   =>  application.yml이 더 읽기 쉽다. 

 

- yml의 의미: YAML Ain't Markup Lanuage

 

 

<Spring Boot의 특징 정리>

1. 간편한 설정

2. 편리한 의존성 관리 & 자동 권장 버전 관리

3. 내장 서버로 인한 간단한 배포 서버 구축

4. 스프링 Security, Data JPA 등의 다른 스프링 프레임워크 요소를 쉽게 사용

'IT > Spring Framework' 카테고리의 다른 글

AtomicInteger와 Integer 비교  (0) 2020.04.25
@Transactional Rollback 조건  (0) 2020.03.19
@Transactional 사용 목적  (0) 2019.10.24
@Component, @Service, @Repository, @Controller의 차이  (0) 2019.06.10
Netty 개념  (0) 2019.01.19

공통점: 둘 다 int형을 wrapping한 클래스

차이점: AtomicInteger는 thread-safe하여 동시성(Concurrency)을 보장한다.

'IT > Spring Framework' 카테고리의 다른 글

Spring vs Spring Boot  (0) 2020.05.29
@Transactional Rollback 조건  (0) 2020.03.19
@Transactional 사용 목적  (0) 2019.10.24
@Component, @Service, @Repository, @Controller의 차이  (0) 2019.06.10
Netty 개념  (0) 2019.01.19

참고:

https://offbyone.tistory.com/405

- https://dzone.com/articles/spring-transactional-amp-exceptions

- https://cheese10yun.github.io/checked-exception/

 

@Transactional의 rollback 조건

Unchecked Exception인 경우에만 rollback 처리된다.
예를 들어,
(1) RuntimeException 발생한 경우 (handling해도 마찬가지)
(2) Checked Exception을 handling하지 않은 경우

'IT > Spring Framework' 카테고리의 다른 글

Spring vs Spring Boot  (0) 2020.05.29
AtomicInteger와 Integer 비교  (0) 2020.04.25
@Transactional 사용 목적  (0) 2019.10.24
@Component, @Service, @Repository, @Controller의 차이  (0) 2019.06.10
Netty 개념  (0) 2019.01.19

- 일련의 로직 실행 과정에서 예외 또는 오류 발생 시에 관련 쿼리들을 모두 Rollback 시키기 위함.

'IT > Spring Framework' 카테고리의 다른 글

AtomicInteger와 Integer 비교  (0) 2020.04.25
@Transactional Rollback 조건  (0) 2020.03.19
@Component, @Service, @Repository, @Controller의 차이  (0) 2019.06.10
Netty 개념  (0) 2019.01.19
ORM, JPA, Hibernate란?  (0) 2018.02.14

출처: https://m.blog.naver.com/PostView.nhn?blogId=platinasnow&logNo=220032855280&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

@Component, @Service, @Repository, @Controller는 bean으로 등록하고 싶은 것에 사용되는 annotaion이다. 역할을 확실하게 명시하기 위해 사용되며, AOP의 Pointcut에서 annotaion 단위로 지정할 수 있기 때문에 유용하게 사용될 수 있다.

 

@Component Presentation Layer에서 Controller를 명시하기 위해 사용.
@Service Business Layer에서 Service를 명시하기 위해 사용.
@Repository Persistence Layer에서 DAO를 명시하기 위해 사용.
@Component 위의 용도 외에 bean으로 등록하고 싶은 것에 사용.

'IT > Spring Framework' 카테고리의 다른 글

@Transactional Rollback 조건  (0) 2020.03.19
@Transactional 사용 목적  (0) 2019.10.24
Netty 개념  (0) 2019.01.19
ORM, JPA, Hibernate란?  (0) 2018.02.14
서블릿(Servlet)과 JSP(Java Sever Pages)  (0) 2018.02.14

참고: 

- 『네티 인 액션』 (노먼 마우러, 마빈 알렌 울프탈 지음. 최민석 옮김.)

 

 

Netty: 

- 비동기식 이벤트 기반의 네트워크 애플리케이션 프레임워크

- 모든 구현이 Channel, ChannelPipeline, ChannelHandler 인터페이스를 기준으로 정의된다.

- 네티가 제공하는 전송:

  ㄴ NIO: 논블로킹 입출력. selector 기반 방식.

  ㄴ Epoll: 논블로킹 입출력. 리눅스에서만 이용. NIO 전송보다 빠르고 완전한 논블로킹.

  ㄴ OIO: 블로킹 스트림 이용.

  ㄴ 로컬(Local): VM에서 파이프를 통해 통신하는 데 이용.

  ㄴ 임베디드(Embedded): 실제 네트워크 기반 전송 없이 ChannelHandler를 이용할 수 있게 해줌. ChannelHandler 구현을 테스트하는 데 유용.

 

 

Selector 클래스:

- 자바의 논블로킹 입출력 구현의 핵심으로서, 논블로킹 Socket의 집합에서 입출력에서 입출력이 가능한 항목을 지정하기 위해 이벤트 통지 API를 이용한다.

- 적은 수의 스레드로 더 많은 연결을 처리할 수 있으므로 메모리 관리와 컨텍스트 전환에 따르는 오버헤드가 감소한다. (적은 수의 스레드로 여러 연결에서 이벤트를 모니터링할 수 있게 해준다.)

- 입출력을 처리하지 않을 때는 스레드를 다른 작업에 활용할 수 있다.

 

 

Channel:

- 자바 NIO(비동기 입출력 방식)의 기본 구조.

- 정의: 하나 이상의 입출력 작업을 수행 할 수 있는 하드웨어 장치, 파일, 네트워크 소켓, 프로그램 컴포넌트와 같은 엔티티에 대한 열린 연결.

- Socket에 해당.

 

- Channel의 메소드:

  ㄴ eventLoop()

  ㄴ pipeline()

  ㄴ isActive()

  ㄴ localAddress()

  ㄴ remoteAddress()

  ㄴ write()

  ㄴ flush()

  ㄴ writeAndFlush()

 

 

 

EventLoop: 

- 용도: 제어 흐름, 멀티스레딩, 동시성 제어

- 하나 이상의 Channel에 할당 가능

 

 

ChannelFuture: 

- 용도: 비동기 알림

- 미래에 실행될 작업의 겨로가를 위한 자리표시자라고 생각할 수 있다.

- ChannelFutureListener 인스턴스를 하나 이상 등록할 수 있는 추가 메소드가 있다. 작업이 완료되면 리스너의 콜백 메소드인 operationComplete()이 호출되며, 이 시점에 리스너는 작업이 정상적으로 완료됐는지, 아니면 오류가 발생했는지 확인할 수 있다.

- 네티의 모든 아웃바운드 입출력 작업은 ChannelFuture를 반환하며 진행을 블로킹하는 작업은 없다.

 

 

ChannelPipeline:

- 하나 이상의 ChannelHandler를 포함.

- 인바운드와 아웃바운드 핸들러를 동일한 파이프라인에 설치할 수 있다.

 

 

ChannelHandler:

- 인바운드와 아웃바운드 데이터의 처리에 적용되는 모든 애플리케이션의 비즈니스 로직의 컨테이너 역할을 하는 컴포넌트.

- 크게 ChannelInboundHandler, ChannelOutboundHandler로 나뉨.

- 4가지 이벤트 유형 포함.

 

 

ChannelHandlerContext:

- 이벤트를 현재 체인의 다음 핸들러로 전달할 수 있다.

 

 

EventLoopGroup:

- 하나 이상의 EventLoop를 포함.

 

 

Adapter:

- 해당 인터페이스에 정의된 모든 메소드의 기본 구현을 제공. 

 

 

Bootstrap:

- 프로세스를 지정된 포트로 바인딩하거나 프로세스를 지정된 호스트의 지정된 포트에서 실행 중인 다른 호스트로 연결하는 등의 일을 하는 애플리케이션의 네트워크 레이어를 구성하는 컨테이너.

- 서버 부트스트랩(ServerBootstrap): 프로세스를 지정된 포트로 바인딩하는 애플리케이션의 네트워크 레이어를 구성. 들어오는 연결을 수신하는 동작.

- 클라이언트 부트스트랩(Bootstrap): 프로세스를 지정된 호스트의 지정된 포트에서 실행 중인 다른 호스트로 연결. 하나 이상의 프로세스로 연결하는 동작.

 

 

Bootstrap vs ServerBootstrap

- 네트워크 기능: 원격 호스트와 포트로 연결 | 로컬 포트로 바인딩

- EventLoopGroup의 수: 1 | 2

 

 

Unpooled.wrappedBuffer() vs Unpooled.copiedBuffer()

ㄴ참고: https://m.blog.naver.com/PostView.nhn?blogId=lunajina&logNo=220125068423&proxyReferer=https%3A%2F%2Fwww.google.com%2F

ㄴ참고: https://koda93.github.io/Netty%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88(ByteBuf)

- Unpooled.wrappedBuffer() : 새 복합 버퍼(composite buffer)를 생성하며, 인자로 받은 content를 실제 복사하지는 않는다. 지정한 데이터를 래핑하는 ByteBuf 반환.

- Unpooled.copiedBuffer​() : 새 버퍼를 생성하여 인자로 받은 content를 복사한다. 지정한 데이터를 복사하는 ByteBuf 반환.

 

* CompositeByteBuf:

  ㄴ참고:  https://netty.io/4.0/api/io/netty/buffer/CompositeByteBuf.html

  ㄴ여러 버퍼가 병합된 가상의 단일 버퍼를 제공하는 ByteBuf의 하위 클래스.

  ㄴA virtual buffer which shows multiple buffers as a single merged buffer. It is recommended to use ByteBufAllocator.compositeBuffer() or Unpooled.wrappedBuffer(ByteBuf...) instead of calling the constructor explicitly.

 

 

 

애플리케이션을 위한 최적의 전송

 애플리케이션의 요건

 권장되는 전송

 논블로킹 코드 기반 또는 일반적 출발점

 NIO(또는 리눅스의 경우 epoll)

 블로킹 코드 기반

 OIO

 동일한 JVM 내의 통신

 로컬(Local)

 ChannelHandler 구현 테스트

 임베디드(Embeded)

 

 

전송과 네트워크 프로토콜 지원 (2016년 기준)

전송 

TCP 

UDP 

SCTP

(Stream Control Trasmission Protocol) 

UDT 

 NIO

 O

 O

 O

 O

 Epol(리눅스 전용)

 O

 O

 X

 X

 OIO

 O

 O

 O

 O

 

 

ByteBuffer:

- ByteBuffer: 자바 NIO의 데이터 컨테이너. 사용법이 복잡.

 

 

ByteBuf:

- 네티의 데이터 컨테이너.

- 읽기와 쓰기를 위한 고유한 두 인덱스를 제공한다. ByteBuf에서 데이터를 읽으면 읽은 바이트 수만큼 readerIndex가 증가하고, ByteBuf에 데이터를 기록하면 기록한 바이트 수만큼 writerIndex가 증가한다.

- 기본 읽기와 쓰기 작업 외에도 데이터를 수정하는 데 이용할 수 있는 다양한 메소드를 제공한다.

- ByteBuf는 readerIndex 위치, writerIndex 위치에 따라, '폐기할 수 있는 바이트' 영역, '읽을 수 있는 바이트 (내용)' 영역, '기록할 수 있는 바이트' 영역으로 분할될 수 있다.

- discardReadByte()를 호출하면 readerIndex는 0이되고, writerIndex는 감소하며, '폐기할 수 있는 바이트' 영역은 폐기 및 회수되고, '읽을 수 있는 바이트 (내용)' 영역은 버퍼의 시작 부분으로 옮겨지며(옮겨지기 위해 메모리 복사 필요), '기록할 수 있는 바이트' 영역은 확장된다.

- clear()를 호출하면 readerIndex와 writerIndex는 0이 되고, 메모리의 내용은 지우지 않으며, 전체 영역이 '기록할 수 있는 바이트' 영역이 된다. clear()는 인덱스만 재설정하므로 discardReadBytes()에 비해 실행 비용이 훨씬 낮다.

- 읽기/쓰기 작업은 두 가지 범주가 있다:

  ㄴget() 및 set() 작업은 지정한 인덱스에서 시작하며 인덱스를 변경하지 않는다.

  ㄴread() 및 write() 작업은 지정한 인덱스에서 시작하며 접근한 바이트 수 만큼 인덱스를 증가시킨다.

 

 

ByteBuf 사용 패턴:

- 힙 버퍼(heap buffer): 

  ㄴByteBuf에 보조 배열(backing array)이 있으면 힙 버퍼. 

  ㄴJVM에 힙 공간에 데이터를 저장. 

  ㄴ이 패턴은 풀링이 사용되지 않는 경우, 빠른 할당과 해제 속도를 보여줌.

  ㄴ레거시 데이터를 처리하는 경우에 적합.

- 다이렉트 버퍼(direct buffer): 

  ㄴByteBuf에 보조 배열이 없으면 다이렉트 버퍼.

  ㄴ다이렉트 버퍼의 내용은 일반적인 가비지 컬렉션(GC)이 적용되는 힙 바깥에 위치한다. 다이렉트 버퍼가 네트워크 전송에 이상적이기 때문이다. 데이터가 힙 할당 버퍼에 있는 경우에는 JVM은 소켓을 통해 전송하기 전에 내부적으로 버퍼를 다이렉트 버퍼로 복사해야 한다.

  ㄴ힙 기반 버퍼보다 할당과 해제의 비용 부담이 약간 더 크다.

  ㄴ레거시 데이터를 이용하는 경우에는 데이터가 힙에 있지 않기 때문에 복사본을 만들어야 할 수 있다.

- 복합 버퍼(composite buffer): 

  ㄴ여러 ByteBuf의 집합적 뷰에 해당. 

  ㄴ네티는 여러 버퍼가 병합된 가상의 단일 버퍼를 제공하는 ByteBuf의 하위 클래스인 CompositeByteBuf를 이용해 이 패턴을 구현한다.

  ㄴ헤더와 본문의 두 부분으로 구성되는 메세지를 http를 통해 전송하는 경우에 CompositeByteBuf를 이용하면 메세지마다 두 버퍼를 다시 할당할 필요가 없어 편리하며, 공통 ByteBuf API를 노출할 때 불필요하게 복사할 필요가 없게 해준다.

  ㄴCompositeByteBuf는 보조 배열에 대한 접근을 허용하지 않을 수 있으므로 CompositeByteBuf의 데이터에 접근하려면 다이렉트 버퍼와 비슷한 방법을 이용한다.

  ㄴ네티는 CompositeByteBuf를 이용하는 소켓 입출력 작업을 최적화해 JDK의 버퍼 구현이 이용될 때 발생하는 성능과 메모리 소비 패널티를 최소화한다.

  ㄴCompositeByteBuf API는 ByteBuf로부터 상속받은 메소드 외에도 다양한 추가 기능을 제공한다.

 

 

파생 버퍼:

- ByteBuf의 내뇽을 특수한 방법으로 나타내는 뷰를 제공.

- 파생 버퍼의 내부 저장소는 JDK ByteBuffer와 마찬가지로 공유된다. 즉, 생성하는 비용은 낮지만 파생 버퍼의 내용을 수정하면 원본 인스턴스까지 수정된다는 데 주의해야 한다.

 

 

ByteBufHolder 인터페이스:

- 네티는 실제 데이터 payload와 함께 다양한 속성값을 저장해야 하는 경우에 대해 ByteBufHolder를 제공한다.

- ByteBufHolder는 ByteBuf를 풀에서 가져오고 필요할 때 자동으로 해제할 수 있는 버퍼 풀링과 같은 네티의 고급 기능도 지원한다.

- ByteBufHolder는 ByteBuf 안에 Payload를 저장하는 메세지 객체를 구현하려고 할 때 좋은 선택이다.

 

 

ByteBufAllocator 인터페이스:

- 네티는 메모리 할당과 해제 시 발생하는 오버헤드를 줄이기 위해 ByteBufAllocator 인터페이스를 통해 모든 종류의 ByteBuf 인스턴스를 할당하는 데 이용할 수 있는 풀링을 구현한다.

- ByteBufAllocator의 참조는 Channel에서 얻거나(각각 고유 인스턴스를 가짐) ChannelHandler에 바인딩된 ChannelHandlerContext를 통해 얻을 수 있다.

- 네티는 ByteBufAllocator의 구현을 PooledByteBufAllocator와 UnpooledByteBufAllocator로 제공한다. 네티는 기본적으로 PooledByteBufAllocator를 이용하지만 ChannelConfig API를 통하거나 애플리케이션을 부트스트랩할 때 다른 할당자를 지정할 수 있다.

  ㄴPooledByteBufAllocator: ByteBuf 인스턴스를 풀링해 성능을 개선하고 메모리를 단편화를 최소화하며, 여러 최신 운영체제에 도입된 jemalloc라는 고효율 메모리 할당 방식을 이용한다.

  ㄴUnpooledByteBufAllocator: ByteBuf 인스턴스를 풀링하지 않고 호출될 때마다 새로운 인스턴스를 반환한다.

- 네티는 ByteBufAllocator의 참조가 없는 상황에 대해 풀링되지 않는 ByteBuf 인스턴스를 생성하는 정적 도우미 메소드를 제공하는 Unpooled라는 유틸리티 클래스를 제공한다. Unpooled 클래스는 다른 네티 컴포넌트가 필요없는 네트워킹과 무관한 프로젝트에 ByteBuf를 제공해 확장성 높은 고성능 버퍼 API를 이용할 수 있게 해준다.

  

 

ByteBufUtil 클래스:

- ByteBuf를 조작하기 위한 정적 도우미 메소드를 제공한다. 이 API는 범용이며 풀링과는 관계가 없으므로 이러한 메소드는 할당 클래스와 무관하게 구현된다.

 

 

참조 카운팅(reference counting):

- 다른 객체에서 더이상 참조하지 않는 객체가 보유한 리소스를 해제해 메모리 사용량과 성능을 최적화하는 기법.

- 특정 객체에 대한 활성 참조의 수를 추적하는 데 바탕을 둔다.

- ReferenceCounted 인터페이스를 구현한 인스턴스는 일반적으로 참조 카운트 1을 가지고 시작하며, 참조 카운트가 1 이상이면 객체가 해제되지 않지만, 0으로 감소하면 인스턴스가 해제된다. 해제된 객체는 더이상 이용할 수 없게 된다.

- 참조 카운팅은 PooledByteBufAllocator와 같은 풀링 구현에서 메모리 할당의 오버헤드를 줄이는 데 반드시 필요하다.

- 특정한 객체가 각자의 고유한 방법으로 해제 카운팅 계약을 정의할 수 있다. 일반적으로 객체에 마지막으로 접근하는 측에 해제할 책임이 있다.

 

 

실시간 웹:

- 사용자 또는 사용자의 소프트웨어가 주기적으로 업데이트를 확인하지 않고도 저자가 정보를 게시하는 즉시 정보를 받을 수 있는 기술과 방식을 적용한 네트워크 웹을 의미한다.

 

 

웹소켓:

- 웹소켓 프로토콜은 웹의 양방향 데이터 전송 문제에 대한 실용적인 솔루션을 제공하기 위해 클라이언트와 서버가 언제든지 메세지를 전송할 수 있게 허용하고, 결과적으로 메세지 수신을 비동기적으로 처리하게 요구하도록 설계되었다.

 

ORM, JPA, Hibernate란?

 출처 : http://www.libqa.com/wiki/769

ORM (Object Relational Mapping) 무엇인가?
RDB 테이블을 객체지향적으로 사용하기 위한 기술입니다. RDB 은 객체지향적 (상속, 다형성, 레퍼런스, 오브젝트 등)으로 접근하기 쉽지 않습니다.
때문에 ORM을 사용해 오브젝트와 RDB 사이에 객체지향적으로 다루기 위한 기술입니다.

 

JPA (Java Persistence API) 무엇인가?
ORM 전문가가 참여한 EJB 3.0 스펙 작업에서 기존 EJB ORM이던 Entity Bean JPA라고 바꾸고 JavaSE, JavaEE를 위한 영속성(persistence) 관리와 ORM을 위한 표준 기술입니다. JPA ORM 표준 기술로 Hibernate, OpenJPA, EclipseLink, TopLink Essentials과 같은 구현체가 있고 이에 표준 인터페이스가 바로 JPA입니다.

 

HIBERNATE 무엇인가?
Boss에서 개발한 ORM(Object Relational Mapping) 프레임워크 입니다.

장점
Hibernate는 특정 클래스에 매핑되어야 하는 데이터베이스의 테이블에 대한 관계 정의가 되어 있는 XML 파일의 메타데이터로 객체관계 매핑을 간단하게 수행시킵니다.
Hibernate를 사용하면 데이터베이스가 변경되더라도 SQL 스크립트를 수정하는등의 작업을 할 필요가 없습니다.
애플리케이션에서 사용되는 데이터베이스를 변경시키고자 한다면 설정파일의 dialect 프로퍼티를 수정함으로서 쉽게 처리할 수 있습니다.
Hibernate MySQL, Oracle, Sybase, Derby, PostgreSQL를 포함한 많은 데이터베이스를 지원하며 POJO기반의 모델과도 원활하게 동작합니다.

 
 JPA를 쓰는가?
1. 기존 SQL 중심적인 개발시 불편하다
- 쿼리가 변경되면 이에따른 프로그램 소스 DTO객체의 변경도 불가피하게 일어난다
- 데이터를 가져와 객체지향적으로 관계를 Mapping하는 일이 매번 일어난다!! SQL의존적인 개발이 이루어진다.

 
2. 객체-관계 간 모델 불일치
관계형 데이터베이스에는 로우와 컬럼의 2차원 형태로 데이터가 저장된다. 데이터 관계는 외래키foreign key 형태로 표현된다. 문제는 도메인 객체를 관계형 데이터 베이스로 저장할 때 발생한다. 애플리케이션의 객체는 로우와 컬럼 형태가 아니다. 도메인 객체는 객체의 상태를 속성(변수)으로 가지고 있다. 그래서 도메인 객체 그대로 관계형 데이터베이스에 저장할 수가 없다. 이러한 불일치를 객체-관계 간 임피던스 불일치object-relational impedance mismatch라고 합니다.



3. 상속 불일치
상속은 객체 세계에서는 지원하지만, 관계형 스키마에서는 지원하지 않는다. 상속은 모든 객체지향 언어, 특히 자바에서 바늘과 실처럼 뗄 수 없는 특징입니다. 안타깝게도 관계형 스키마에는 상속 개념이 없습니다. 회사에서 임원과 직원의 예를 들어보면. 임원 개인도 회사의 직원이죠. 이 관계를 데이터베이스에서 표현하는 것은 테이블 간 관계 수정이 필요해서 쉽지 않습니다. 상속 없이 현실 세계의 문제 상황을 표현하는 것은 매우 복잡한 일입니다. 그런데 데이터베이스는 상속 관계와 같은 형태를 알지 못하지요. 이것을 해결할 간단한 방법은없지만, 문제를 풀 수 있는 몇 가지 접근법이 있습니다. 이 접근법은 다양한 클래스-테이블class-to-table 전략을 사용합니다.

4. 관계와 연관 관계의 불일치
1. SQL 중심적인 개발의 문제점
 - field 하나추가시  쿼리도 바꿔야하고  VO도 바꿔야되고 ...
 - SQL에 의존적인 개발을 피하기 어렵다.
 - 객체답게 모델링 할수록 매핑 작업만 늘어난다


장점
- 객체지향적으로 데이터를 관리할 수 있기 때문에 비즈니스 로직에 집중 할 수 있으며객체지향 개발이 가능합니다.
- 테이블 생성, 변경, 관리가 쉽다. (JPA를 잘 이해하고 있는 경우) 로직을 쿼리에 집중하기 보다는 객체자체에 집중 할수 있습니다.
- 빠른 개발이 가능합니다.


단점
- 어렵다. 장점을 더 극대화 하기 위해서 알아야 할게 많습니다.
- 잘 이해하고 사용하지 않으면 독이될수도 있습니다.
- 성능상 문제가 있을 수 있다.(이 문제 또한 잘 이해해야 해결이 가능합니다.


JPA, HIBERNATE  Architecture






서블릿(Servlet)과 JSP(Java Sever Pages)

출처 : 

- 서블릿과 JSP 의미 : http://anster.tistory.com/128

- Model1, Model2, Struts 개발 방식의 비교 분석 : http://www.javajigi.net/pages/viewpage.action?pageId=73

 

<서블릿>

  서블릿이란, 요약하자면, java 파일이 WAS(Web Application Server)를 통해 class 파일로 컴파일된 후, 메모리에 적재되어 클라이언트의 HTTP Get, Post 등의 요청을 처리하는 자바 프로그램. (톰캣 위에서 동작하는 java 프로그램.) (웹페이지를 동적으로 생성하는 서버측 프로그램.)

[IMG-1]





[IMG-2]





  톰캣(Tomcat)과 같은 WAS가 java 파일을 컴파일(Compile)해서 class로 만들고 메모리에 올려 servlet 객체를 만들게 되고, servlet 객체는 doPost(), doGet()을 통해 요청에 응답한다.
  [IMG-2]에서 Init, Service, Destroy 등의 이벤트들이 각 시점에 불려지는 것을 볼 수 있다.
  - Init 이벤트는 서블릿이 메모리에 로드될때 실행된다.
  - Service 이벤트는 HTTP Method 타입에 따라 doGet() 또는 doPost()를 호출한다.
  - Destroy는 서블릿이 메모리로부터 언로드되기 전에 실행된다.

  기억해야할 점은, 초기화된 서블릿은 클라이언트의 요청이 있을 때마다 쓰레드(Thread)를 생성하여 병렬적으로 Service를 수행한다는 것이다. 서블릿 객체는 여러개로 생성되지 않는다.



<JSP>

[IMG-3]





[IMG-4]





  서블릿 샘플 소스[IMG-3]를 보면 알겠지만, HTML을 넣기 불편하다. 그래서, [IMG-4]처럼 HTML 내부에 java 코드를 삽입하는 형식인 JSP가 나왔다.
하지만, 로직과 디자인이 한 파일내에 섞여 있어 유지보수하기 어렵다는 단점이 있다. 클래스 단위로 분해해서 객체지향 프로그래밍(Object-Oriented Programming)적으로 코드를 짜기도 어렵다.
  JSP라는 새로운 개발 방법이 나왔지만, 사실 JSP도 내부적으로는 톰캣이 서블릿으로 바꾸어서 돌린다.
  
  JSP 개발 방식으로는 Model 1, Model 2가 있다.


<Model 1>
[IMG-5]





  Model 1은 클라이언트의 요청 발생 시, JSP가 Java Bean(DTO, DAO)을 호출하여 처리한다.
 
  장점으로는
  - 개발 속도가 빠르다.
  - 개발자가 배우기 쉽다.
 
  단점으로는
  - 프리젠테이션 로직과 비즈니스 로직이 혼재되어 있어 코드가 복잡해져 유지보수가 어려워지고 개발자와 디자이너의 분업이 어렵워진다.
 
 
<Model 2>
[IMG-6]





  Model 2는 JSP, 서블릿 양쪽의 장점을 모두 취해 JSP를 뷰(View)로서, 서블릿을 컨트롤러(Controller)로서 사용한다.
  모델(Model) 영역에서는 DTO(Data Type Object), DAO(Data Access Object)를 통해 Mysql DB와 같은 데이터 저장소(Data Storage)에 접근합니다.

 


(시청일 : 20180114)









■ 예제 1
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<Event> hello(@PathVariable long id) {
        return Mono.just(new Event(id, "event" + id));
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}


<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/event/123
{"id":123,"value":"event123"}




■ 예제 2
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<Event> hello(@PathVariable long id) {
        return Mono.just(new Event(id, "event" + id));
    }
    @GetMapping("/events")
    Flux<Event> events() {
        return Flux.just(new Event(1L, "event1"), new Event(2L, "event2"));
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}




<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
[{"id":1,"value":"event1"},{"id":2,"value":"event2"}]



■ 예제 3
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping("/events")
    Flux<Event> events() {
        return Flux.just(new Event(1L, "event1"), new Event(2L, "event2"));
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}




<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/event/1
[{"id":1,"value":"event1"},{"id":2,"value":"event2"}]
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
[{"id":1,"value":"event1"},{"id":2,"value":"event2"}]





■ 예제 3
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Flux.fromIterable(list);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}





<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1,"value":"event1"}
data:{"id":2,"value":"event2"}




<Flux>






<Flux.fromIterable()>






■ 예제 4
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        return Flux
                .fromStream(Stream.generate(() -> new Event(System.currentTimeMillis(), "value")))
                .delayElements(Duration.ofSeconds(1))
                .take(10);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}







<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1516531806083,"value":"value"}
data:{"id":1516531806192,"value":"value"}
data:{"id":1516531806194,"value":"value"}
data:{"id":1516531806195,"value":"value"}
data:{"id":1516531806210,"value":"value"}
data:{"id":1516531806213,"value":"value"}
data:{"id":1516531806215,"value":"value"}
data:{"id":1516531806216,"value":"value"}
data:{"id":1516531806217,"value":"value"}
data:{"id":1516531806219,"value":"value"}


<Flux.take()>








■ 예제 5
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        return Flux
                .<Event>generate(sink -> sink.next(new Event(System.currentTimeMillis(), "value")))
                .delayElements(Duration.ofSeconds(1))
                .take(10);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}





<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1516533395000,"value":"value"}
data:{"id":1516533396018,"value":"value"}
data:{"id":1516533397019,"value":"value"}
data:{"id":1516533398021,"value":"value"}
data:{"id":1516533399023,"value":"value"}
data:{"id":1516533400023,"value":"value"}
data:{"id":1516533401024,"value":"value"}
data:{"id":1516533402025,"value":"value"}
data:{"id":1516533403026,"value":"value"}
data:{"id":1516533404027,"value":"value"}






■ 예제 6
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        return Flux
                .<Event, Long>generate(()->1L, (id, sink) -> {
                    sink.next(new Event(id, "value" + id));
                    return id+1;
                })
                .delayElements(Duration.ofSeconds(1))
                .take(10);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}



<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1,"value":"value1"}
data:{"id":2,"value":"value2"}
data:{"id":3,"value":"value3"}
data:{"id":4,"value":"value4"}
data:{"id":5,"value":"value5"}
data:{"id":6,"value":"value6"}
data:{"id":7,"value":"value7"}
data:{"id":8,"value":"value8"}
data:{"id":9,"value":"value9"}
data:{"id":10,"value":"value10"}




■ 예제 7
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        Flux<Event> es = Flux
                .<Event, Long>generate(()->1L, (id, sink) -> {
                    sink.next(new Event(id, "value" + id));
                    return id+1;
                });
        Flux<Long> interval = Flux.interval(Duration.ofSeconds(1));
        return Flux.zip(es, interval).map(tu->tu.getT1()).take(10);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}



<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1,"value":"value1"}
data:{"id":2,"value":"value2"}
data:{"id":3,"value":"value3"}
data:{"id":4,"value":"value4"}
data:{"id":5,"value":"value5"}
data:{"id":6,"value":"value6"}
data:{"id":7,"value":"value7"}
data:{"id":8,"value":"value8"}
data:{"id":9,"value":"value9"}
data:{"id":10,"value":"value10"}





■ 예제 8
package toby.tobytv014;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@SpringBootApplication
@Slf4j
@RestController
public class Tobytv014Application {
    @GetMapping("/event/{id}")
    Mono<List<Event>> hello(@PathVariable long id) {
        List<Event> list = Arrays.asList(new Event(1L, "event1"), new Event(2L, "event2"));
        return Mono.just(list);
    }
    @GetMapping(value="/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Event> events() {
        Flux<String> es = Flux.generate(sink -> sink.next("Value"));
        Flux<Long> interval = Flux.interval(Duration.ofSeconds(1));
        return Flux.zip(es, interval).map(tu -> new Event(tu.getT2()+1, tu.getT1())).take(10);
    }
    public static void main(String[] args) {
        SpringApplication.run(Tobytv014Application.class, args);
    }
    @Data    @AllArgsConstructor
    public static class Event {
        long id;
        String value;
    }
}



<Terminal>
C:\Users\Soohyeon\Desktop\curl_7_53_1_openssl_nghttp2_x64>curl localhost:8080/events
data:{"id":1,"value":"Value"}
data:{"id":2,"value":"Value"}
data:{"id":3,"value":"Value"}
data:{"id":4,"value":"Value"}
data:{"id":5,"value":"Value"}
data:{"id":6,"value":"Value"}
data:{"id":7,"value":"Value"}
data:{"id":8,"value":"Value"}
data:{"id":9,"value":"Value"}
data:{"id":10,"value":"Value"}




<Flux.generate()>





<Flux.interval()>





<Flux.zip()>





(시청일 : 20170114)



스프링 5.0 WebFlux에서 사용되는 Reactor의 Mono의 기본 동작방식을 살펴봅니다.



■ 예제 1
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        return Mono.just("Hello WebFlux").log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}


<결과>
2018-01-21 17:15:04.761  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onSubscribe([Synchronous Fuseable] Operators.ScalarSubscription)
2018-01-21 17:15:04.764  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(1)
2018-01-21 17:15:04.764  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onNext(Hello WebFlux)
2018-01-21 17:15:04.787  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(1)
2018-01-21 17:15:04.787  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(31)
2018-01-21 17:15:04.787  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onComplete()
2018-01-21 17:15:05.264  INFO 8148 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | cancel()



■ 예제 2
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        Mono m = Mono.just("Hello WebFlux").log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        log.info("pos2");
        return m;
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}




<결과>
2018-01-21 17:18:46.265  INFO 13388 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:18:46.268  INFO 13388 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2
2018-01-21 17:18:46.284  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onSubscribe([Synchronous Fuseable] Operators.ScalarSubscription)
2018-01-21 17:18:46.285  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(1)
2018-01-21 17:18:46.286  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onNext(Hello WebFlux)
2018-01-21 17:18:46.307  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(1)
2018-01-21 17:18:46.308  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | request(31)
2018-01-21 17:18:46.308  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | onComplete()
2018-01-21 17:18:46.746  INFO 13388 --- [ctor-http-nio-2] reactor.Mono.Just.1                      : | cancel()




■ 예제 3
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        Mono m = Mono.just("Hello WebFlux").doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        log.info("pos2");
        return m;
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}




<결과>
2018-01-21 17:20:50.096  INFO 13748 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:20:50.099  INFO 13748 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2
2018-01-21 17:20:50.115  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:20:50.117  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:20:50.117  INFO 13748 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello WebFlux
2018-01-21 17:20:50.117  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello WebFlux)
2018-01-21 17:20:50.138  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:20:50.138  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(31)
2018-01-21 17:20:50.139  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:20:50.229  INFO 13748 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | cancel()




■ 예제 4
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        Mono m = Mono.just(generateHello()).doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        log.info("pos2");
        return m;
    }
    private String generateHello() {
        log.info("method generateHello()");
        return "Hello Mono";
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}




<결과>
2018-01-21 17:26:20.531  INFO 14112 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:26:20.531  INFO 14112 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:26:20.534  INFO 14112 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2
2018-01-21 17:26:20.552  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:26:20.553  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:26:20.553  INFO 14112 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:26:20.553  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:26:20.575  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:26:20.575  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(31)
2018-01-21 17:26:20.575  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:26:20.755  INFO 14112 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | cancel()


■ 예제 5
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        Mono m = Mono.fromSupplier(() -> generateHello()).doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        log.info("pos2");
        return m;
    }
    private String generateHello() {
        log.info("method generateHello()");
        return "Hello Mono";
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}





<결과>
2018-01-21 17:29:04.571  INFO 13256 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:29:04.575  INFO 13256 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2
2018-01-21 17:29:04.589  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:29:04.591  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:29:04.591  INFO 13256 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:29:04.591  INFO 13256 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:29:04.591  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:29:04.622  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:29:04.622  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(31)
2018-01-21 17:29:04.623  INFO 13256 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()



■ 예제 6
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        Mono m = Mono.fromSupplier(() -> generateHello()).doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        m.subscribe();
        log.info("pos2");
        return m;
    }
    private String generateHello() {
        log.info("method generateHello()");
        return "Hello Mono";
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}





<결과>
2018-01-21 17:33:30.588  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:33:30.596  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:33:30.598  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(unbounded)
2018-01-21 17:33:30.598  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:33:30.598  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:33:30.598  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:33:30.599  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:33:30.599  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2
2018-01-21 17:33:30.612  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:33:30.612  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:33:30.612  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:33:30.612  INFO 13316 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:33:30.612  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:33:30.633  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:33:30.633  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(31)
2018-01-21 17:33:30.634  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:33:31.249  INFO 13316 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | cancel()




■ 예제 7
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        String msg = generateHello();
        Mono<String> m = Mono.just(msg).doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        String msg2 = m.block();
        log.info("pos2: " + msg2);
        return m;
    }
    private String generateHello() {
        log.info("method generateHello()");
        return "Hello Mono";
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}






<결과>
2018-01-21 17:39:20.356  INFO 11348 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:39:20.356  INFO 11348 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:39:20.362  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:39:20.364  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(unbounded)
2018-01-21 17:39:20.364  INFO 11348 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:39:20.364  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:39:20.364  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:39:20.365  INFO 11348 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2: Hello Mono
2018-01-21 17:39:20.381  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:39:20.381  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:39:20.381  INFO 11348 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:39:20.381  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:39:20.407  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(1)
2018-01-21 17:39:20.407  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(31)
2018-01-21 17:39:20.407  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:39:20.579  INFO 11348 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | cancel()


- block : publisher가 제공하는 결과값을 꺼내서 Mono나 Flux 같은 컨테이너를 제거하고 값을 넘겨주는 것이 목적.


■ 예제 8
package toby;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@SpringBootApplication
@RestController
@Slf4j
public class Toby013Application {
    @GetMapping("/")
    Mono<String> hello() {
        log.info("pos1");
        String msg = generateHello();
        Mono<String> m = Mono.just(msg).doOnNext(c->log.info(c)).log(); // Publisher -> (Publisher) -> (Publisher) -> Subscriber
        String msg2 = m.block();
        log.info("pos2: " + msg2);
        return Mono.just(msg2);
    }
    private String generateHello() {
        log.info("method generateHello()");
        return "Hello Mono";
    }
    public static void main(String[] args) {
        SpringApplication.run(Toby013Application.class, args);
    }
}




<결과>
2018-01-21 17:48:23.009  INFO 13012 --- [ctor-http-nio-2] toby.Toby013Application                  : pos1
2018-01-21 17:48:23.009  INFO 13012 --- [ctor-http-nio-2] toby.Toby013Application                  : method generateHello()
2018-01-21 17:48:23.015  INFO 13012 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
2018-01-21 17:48:23.016  INFO 13012 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | request(unbounded)
2018-01-21 17:48:23.016  INFO 13012 --- [ctor-http-nio-2] toby.Toby013Application                  : Hello Mono
2018-01-21 17:48:23.017  INFO 13012 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onNext(Hello Mono)
2018-01-21 17:48:23.017  INFO 13012 --- [ctor-http-nio-2] reactor.Mono.PeekFuseable.1              : | onComplete()
2018-01-21 17:48:23.018  INFO 13012 --- [ctor-http-nio-2] toby.Toby013Application                  : pos2: Hello Mono




+ Recent posts