이번 시간에는 REST API 요청을 테스트하기 위한 API 테스트 툴인 Postman의 기본 사용법을 알아보도록 하겠습니다.

API 요청을 보내는 최소한의 사용법만 살펴볼 예정이므로 Postman의 세부 기능이 궁금하신 분들은 공식 사이트의 매뉴얼을 참고해주세요.

Postman은 웹에서도 사용 가능하지만 여기서는 편의를 위해 실행 파일을 다운로드한 후, 설치하는 것을 기준으로 설명합니다.
이 글의 Postman 실행 파일은 Windows 기준이지만 다른 OS의 경우도 사용법은 크게 다르지 않을 것이라 생각합니다.

실행 파일 다운로드

Postman 사이트에서 Postman 실행 파일을 다운로드 받습니다.

 

실행 파일 설치

다운로드 받은 실행 파일을 실행한 후, 안내에 따라서 설치합니다.

 

설치된 파일 실행

[그림 1-1] Postman 파일 실행 시 첫 화면

설치된 Postman 파일을 실행하면 [그림 1-1]과 같은 화면이 오픈됩니다. 여기서 처음 사용을 하는 분은 [Create Account] 버튼을 눌러서 계정을 생성한 후, [Sign In] 버튼을 눌러 로그인을 진행하면 됩니다.

※ Postman은 특이하게 컴퓨터에 설치된 파일을 실행해도 계정 생성이나 로그인은 웹 브라우저로 자동 이동해서 진행이 되니 이점 참고하시기 바랍니다.

 

[그림 1-2] 로그인 후, 애플리케이션 리다이렉션 메시지

로그인 후, [그림 1-2]와 같이 데스크탑 애플리케이션으로 리다이섹션 할지 묻는 창이 열립니다. [Postman 열기] 버튼을 클릭합니다.

 

워크스페이스(Workspace) 생성

[그림 1-3] 워크 스페이스 생성

[그림 1-3]과 같이 [Workspaces] 탭을 선택한 후, [Create Workspace] 버튼을 클릭한 후, 워크스페이스 이름을 입력해서 워크스페이스를 생성합니다.

 

컬렉션(Collections) 생성

[그림 1-4] 컬렉션 생성

[그림 1-4]와 같이 [+](Create new Collection) 버튼을 눌러서 컬렉션을 생성합니다.

※ 컬렉션은 원하는 API 요청들을 컬렉션 별로 구분하기 위한 일종의 그룹화라고 보면 됩니다. 프로젝트별로 컬렉션을 생성해서 API 요청 목록들을 관리하면 좋을 것 같습니다.

 

API 요청 정보 추가

[그림 1-5] API 요청 정보 추가

생성된 컬렉션의 [...] 메뉴를 클릭한 후, [Add request] 메뉴를 클릭합니다.

 

[그림 1-6] 요청 정보 입력 화면

[그림 1-6]과 같이 요청에 필요한 HTTP Method를 선택한 후, 요청에 필요한 URI를 입력합니다. 

[그림 1-6]에서는 HTTP Method로 POST를 선택했기 때문에 [Body] 탭에서 데이터 타입을 선택할 수 있습니다.

요청에 필요한 정보를 모두 입력했다면 [Send] 버튼을 눌러서 서버로부터 응답 메시지를 전달 받을 수 있습니다.

'Java Backend 개발자 되기 > RESTful API' 카테고리의 다른 글

RESTful API의 URL 작성 규칙  (4) 2022.04.12

Spring Boot에서의 부트스트랩이란?

일반적으로 부트스트랩(Bootstrap)이란 어떠한 과정이 시작되어 알아서 진행되는 일련의 과정을 의미합니다.

컴퓨터의 부팅 과정을 생각해보면 이해가 쉬울것입니다.

우리가 컴퓨터 전원을 켜게 되면 일반적으로 다음과 같은 작업이 내부적으로 진행이 됩니다

  • 전원을 켜면 컴퓨터 전원이 잘 공급되는지를 확인한다.
  • 중앙 처리 장치의 상태를 점검한다.
  • ROM에서 컴퓨터의 입출력 장치 및 램과 같은 주요 기본 장치들에 대한 정보를 읽어들여서 정상 동작 유무를 확인한다.
  • 운영체제를 램으로 읽어 들여서 실행한다.

 

Spring Boot에서의 부트스트랩도 컴퓨터 부팅 과정과 유사합니다. 

Spring Boot 애플리케이션이 실행이 되면, 클라이언트가 사용 가능한 애플리케이션으로 동작하기 위한 사전 작업을 거치게 됩니다.

Spring Boot 애플리케이션을 어떻게 부트스트랩 시킬 수 있을까요?

[코드 1-1]

1
2
3
4
5
6
7
@Configuration
@EnableAutoConfiguration
public class CoffeeApplication  {
    public static void main(String[] args) {
     SpringApplication.run(CoffeeApplication.class, args);
    }
}
 
cs

대부분의 Spring Boot 애플리케이션은 [코드 1-1]과 같이 run() 메서드를 호출해서 부트스트랩을 진행할 수 있습니다.

 

[코드 1-2]

1
2
3
4
5
6
7
8
9
@Configuration
@EnableAutoConfiguration
public class CoffeeApplication  {
    public static void main(String[] args) {
         SpringApplication application = new SpringApplication(CoffeeApplication.class);
        // 애플리케이션 설정 커스터마이징..
        application.run(args)
    }
}
 
cs

[코드 1-2]와 같이 애플리케이션 부트스트랩 전에 애플리케이션 설정을 커스터마이징 할 수도 있습니다.

 

Spring Boot에서는 부트스트랩 과정에서 어떤 일이 일어날까요?

  • 클래스 패스 내에서 ApplicationContext 인스턴스를 생성한다.
  • 커맨드라인 매개 변수들을 Spring의 프로퍼티로 구성하기 위해 CommandLinePropertySource를 등록한다.
  • 모든 싱글톤 bean들을 로드한 후, 애플리케이션 컨텍스트를 갱신한다.
  • CommandLineRunner bean들이 존재한다면 트리거(Trigger) 시킨다.

'Java Backend 개발자 되기 > Spring Boot' 카테고리의 다른 글

@SpringBootApplication 의 역할  (0) 2022.04.12

[코드 1-1]

@SpringBootApplication
public class CoffeeApplication {

    public static void main(String[] args) {
        SpringApplication.run(CoffeeApplication.class, args);
    }

}

[코드 1-1]과 같이 Spring Boot Application 클래스에 @SpringBootApplication 애너테이션을 추가하면 기본적으로 해당 애플리케이션이 Spring Boot 기반의 애플리케이션으로 동작하도록 해줍니다.

 

그렇다면 @SpringBootApplication 은 내부적으로 어떤 기능을 하는 걸까요?

@SpringBootApplication 은 다음과 같은 세 가지 기능을 활성화 하는데 사용됩니다.

  • @EnableAutoConfiguration
    • Spring Boot의 자동 구성 메카니즘을 활성화합니다.
  • @ComponentScan
    • 애플리케이션 내의 패키지에서 @Component 애너테이션이 붙은 클래스들에 대한 스캐닝을 활성화합니다.
  • @SpringBootConfiguration
    • Spring Context에 Bean을 추가적으로 등록하거나 Configuration 클래스를 추가적으로 임포트 하는 기능을 활성화합니다.
    • Spring Boot의 @*Test 애너테이션을 사용해서 테스트를 진행할 경우,  자동으로 @SpringBootConfiguration을 검색합니다.

 

@SpringBootApplication 을 무조건 붙여야 하는것은 아니며, [코드 1-2]와 @SpringBootApplication 이 지원하는 세 가지 기능을 개별적으로 적용할 수 있습니다.

[코드 1-2]

@SpringBootConfiguration(proxyBeanMethods = false)
@EnableAutoConfiguration
@Import({ CoffeeConfiguration.class, MemberConfiguration.class })
public class CoffeeApplication {

    public static void main(String[] args) {
        SpringApplication.run(CoffeeApplication.class, args);
    }

}

 

REST API란?

REST(Representational State Transfer)는 HTTP 네트워크 상의 리소스(Resource, 자원)를 정의하고 해당 리소스를 URI라는 고유한 주소로 접근하는 접근 방식을 의미하며, REST API란 REST 방식을 통해서 리소스에 접근하기 위한 서비스 API를 지칭합니다.

 

REST에서 의미하는 리소스란?

REST에서 의미하는 자원은 데이터베이스에 저장된 데이터, 문서, 이미지, 동영상 등 HTTP 통신을 통해 주고 받을 수 있는 모든 것을 의미합니다.

 

URI(Uniform Resource Identifier)와 URL(Uniform Resource Locator)

URI는 네트워크 상에 있는 특정 리소스를 식별하는 통합 자원 식별자(Uniform Resource Identifier)를 의미합니다.

URL은 인터넷에 있는 리소스를 나타내는 통합 리소스 식별자를 의미하며, 우리가 흔히들 이야기하는 웹 상의 주소를 의미합니다.

URI는 URL의 상위 개념으로 볼 수 있습니다.

URI는 리소스를 식별하는 식별자 역할을 하고, URL은 리소스의 위치를 가리킵니다. 

예) http://www.itivllage.tistory.com/manage? id = 1

위 예에서 'http://www.itivllage.tistory.com/manage'까지는 리소스의 위치를 가리키는 URL이라고 할 수 있고, 'http://www.itivllage.tistory.com/manage? id = 1'는 리소스를 식별하기 위한 'id = 1'이라는 고유 식별자 붙었으므로 URI라고 할 수 있습니다.

 

REST API URI 작성 규칙

그러면 이번에는 HTTP 상에서 REST API 서비스를 만드는 입장에서 REST API URI를 작성하는 규칙들을 살펴보도록 하겠습니다.

URI 작성 기본 규칙

  • URI의 마지막이 '/'로 끝나지 않아야 합니다.
    • better
      • http://www.itivllage.tistory.com/coffees (ㅇ)
    • worse
      • http://www.itivllage.tistory.com/coffees/ (x)
  • 동사 보다는 명사를 사용합니다.
    • better
      • http://www.itivllage.tistory.com/coffees (ㅇ)
    • worse
      • http://www.itivllage.tistory.com/coffees/update (x) 
  • 단수형 보다는 복수형 명사를 사용합니다.
    • better
      • http://www.itivllage.tistory.com/coffees (ㅇ) 
    • worse
      • http://www.itivllage.tistory.com/coffee (x)
  • URI는 기본 소문자로 사용합니다.
  • 언더스코어( _ ) 대신에 하이픈(-)을 사용합니다.
  • 파일 확장자는 URI에 포함하지 않습니다.

 

 

 

URI에서 리소스 간의 관계를 표현하는 방법

REST API를 작성하다보면 특정 리소스 간의 관계를 URI로 표현해야 할 경우가 굉장히 많습니다.

이해를 위해 예시를 살펴보도록 하겠습니다.

  • 'http://www.itivllage.tistory.com/members'는 전체 회원에 대한 리소스를 의미합니다. 
  • 'http://www.itivllage.tistory.com/members/1'는 1이라는 ID를 가지는 회원에 대한 리소스를 의미합니다.. 
  • 'http://www.itivllage.tistory.com/members/1/orders'는 1이라는 ID를 가지는 회원의 주문에 대한 리소스를 의미합니다. 

 

 

 

더 읽을 거리

 

REST - 위키백과, 우리 모두의 백과사전

대한민국의 힙합 음악가에 대해서는 R-EST 문서를 참고하십시오. REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다. 이

ko.wikipedia.org

 

7 Rules for REST API URI Design - DZone Integration

URIs, or Uniform Resource Identifiers, should be designed to be readable and clearly communicate the API resource model. These rules will help you succeed.

dzone.com

 

웹 API 디자인 모범 사례 - Azure Architecture Center

플랫폼 독립성과 서비스 진화를 지원하는 웹 API 설계를 위한 모범 사례를 알아봅니다.

docs.microsoft.com

 

'Java Backend 개발자 되기 > RESTful API' 카테고리의 다른 글

Postman 기본 사용법  (0) 2022.04.18

Handler 용어의 의미

핸들(Handle) 이라고 하면 일반적으로 자동차의 핸들을 제일 먼저 떠올릴 수 있는데, 자동차의 핸들은 운전자가 직접 핸들을 움직이면서 직접적으로 자동차의 주행을 처리하는 역할을 합니다.

Spring MVC에서는 자동차의 핸들과 마찬가지로 클라이언트의 요청을 처리하는 처리자Handler라고 합니다.

그렇다면 Spring MVC에서 Handler는 누구일까요?
Spring MVC에서의 요청 Handler는 바로 여러분들이 작성하는 Controller 클래스를 의미합니다. 그리고 Controller 클래스에 있는 @GetMapping, @PostMapping 같은 애너테이션이 붙어 있는 메서드들을 핸들러 메서드라고 합니다.

HandlerMapping이란 의미는 결국 사용자의 요청과 이 요청을 처리하는 Handler를 매핑해주는 역할을 하는 것입니다.

그렇다면, 사용자의 요청과 Handler 메서드의 매핑은 어떤 기준으로 이루어질까요?
@GetMapping(”/coffee”) 처럼 HTTP Request Method(GET, POST 등)와 Mapping URL을 기준으로 해당 Handler와 매핑이 되는데 Spring 에서는 여러가지 유형의 HandlerMapping 클래스를 제공하고 있습니다.

실무에서는 Spring에서 디폴트로 지정한 ‘RequestMappingHandlerMapping’을 대부분 사용하는데 원한다면 얼마든지 HandlerMapping 전략을 바꿀 수 있습니다. 

 

Adapter의 의미

Java에서 사용하는 클래스나 인터페이스의 이름을 살펴보다 보면 흥미로운 용어들이 많이 나옵니다.
Adapter라는 용어도 그 중에 하나라고 볼 수 있습니다.

우리말로 아답터, 어댑터(Adapter, Adaptor)하면 220V 전압을 110V 전압으로 또는 그 반대로 바꿔주는 어댑터(일명, 돼지코)나 USB 충전기를 떠올릴 수 있습니다.

220V를 5V 전압으로 바꿔주는 USB 충전기 예

 

이처럼 Adapter는 무언가를 다른 형식이나 형태로 바꿔주는 역할을 합니다.

그렇다면 HandlerAdater는 무엇을 바꿔줄까요?

Spring은 객체지향의 설계 원칙을 잘 따르는 아주 유연한 구조를 가지는 프레임워크입니다.
따라서 Spring이 제공하는 Spring MVC에서 지원하는 Handler를 사용해도 되지만 다른 프레임워크의 Handler를 Spring MVC에 통합할 수 있습니다.

이처럼 다른 프레임워크의 핸들러를 Spring MVC에 통합하기 위해서 HandlerAdapter를 사용할 수 있습니다.

 

ViewResolver의 의미

’Resolve’는 무언가를 해석하고, 해결해주다라는 뜻이 있습니다.

ViewResolver는 DispatcherServlet에서 ‘이런 이름을 가진 View를 줘’ 라고 요청하면 DispatcherServlet에서 전달한 View 이름을 해석한 뒤 적절한 View 객체를 리턴해주는 역할을 합니다.

객체 지향 설계 원칙이란?

  • 수많은 사람들이 고민하고 시행착오를 겪으면서 만든 원칙입니다.
  • 좀 더 유지보수하기 쉽고, 유연하고, 확장이 쉬운 소프트웨어를 만들 수 있습니다.
  • 코드 베이스뿐만 아니라 아키텍쳐 설계에 이르기까지 다양하게 적용할 수 있습니다.

 

높은 응집력이란?

  • 비슷한 일을 하는 기능 즉, 하나의 책임에 포함되는 기능들이 잘 뭉쳐있다면 높은 응집력을 가진다고 볼 수 있습니다.
  • 하나의 기능을 변경하는데 여기저기 변경해야 될 곳이 많다면 응집력이 낮다고 볼 수 있습니다.

 

낮은 결합도란?

  • 클래스 간에 의존성이 낮다면 낮은 결합도를 가진다고 볼 수 있습니다.
  • 하나의 클래스를 수정하는데 의존하는 다른 클래스를 모두 수정해야 한다면 결합도가 높다고 할 수 있습니다.

SOLID 원칙

  • SRP, OCP, LSP, ISP, DIP의 첫 글자를 딴것이 SOLID 입니다.

SRP(Single Responsibility Principle, 단일 책임의 원칙)

  • 클래스가 제공하는 기능들은 하나의 책임을 수행하는데 집중해야 한다는 원칙입니다.
  • SRP를 지키지 않는다면?
    • 응집력이 낮아지고, 결합도는 높아집니다.
    • 서로 다른 애플리케이션에 배포될 때 사용하지 않는 기능도 포함됩니다. 즉, 왕따가 발생합니다.
    • 변수 레벨
      • 하나의 속성이 여러 의미를 가지는 경우
    • 메서드 레벨
      • if 문이 많을 경우
  • SRP를 잘 지킨다면?
    • 응집력이 높아지고, 결합도는 낮아집니다.
    • 코드 가독성도 좋아집니다.
    • 테스트 범위도 작아집니다. 즉 유지보수도 편해집니다.
  • AOP는 별도의 부가 기능을 핵심 로직에서 분리했기 때문에 SRP의 원칙을 지킨 방식이라고도 할 수 있습니다.

SRP를 지키는 역할 분리의 예)  Kevin이라는 클래스가 있다면?

  • 남편의 역할
    • 자주 전화해주기
    • 기념일 챙기기
  • 아빠의 역할
    • 놀아주기
    • 씻겨주기
    • 재워주기
    • 먹여주기
    • 책 읽어주기
  • 아들의 역할
    • 건강 체크하기
    • 용돈 주기
    • 안부 전화하기
    • 자주 방문하기
  • 사위의 역할
    • 건강 체크하기
    • 용돈 주기
    • 안부 전화하기
    • 자주 방문하기
  • 친구의 역할
    • 안부 전화하기
    • 술 마시기
    • 고민 들어주기
  • 직원의 역할
    • 출근하기
    • 열심히 일하기
    • 회의하기
    • 퇴근하기

 

OCP(Open Close Principle, 개방폐쇄의 원칙)

  • 확장에는 열려있고(Open), 변경에는 닫혀 있어야 한다(Close)는 원칙입니다.
  • 변하는것과 변하지 않는것을 잘 구분하면 OCP를 지키기 용이합니다. 변하는것은 Open, 변하지 않는 것은 Close에 해당됩니다.
  • 변하지 않는것은 변하는 것의 구현에 의존하지 않고, 인터페이스를 통해 느슨하게 의존하게 만듭니다.
  • 디자인 패턴 중에서 전략패턴의 전략 인터페이스는 OCP의 Open에 해당되고 Context는 OCP의 Close에 해당됩니다.
  • 예)
    • 자동차라는 객체에서 변하지 않는 동작
      • 움직이는 것
    • 자동차 객체에서 변하는 동작
      • 수동 모드, 오토메이션 모드, 크루즈컨트롤 모드

 

LSP(The Liskov Substitution Principle, 리스코브 치환의 원칙)

  • subclass의 객체는 superclass의 참조 변수에 대입해서 superclass의 역할을 수행하는데 문제가 없어야 한다는 원칙입니다.
  • OCP 원칙의 기반이 된다고 생각됩니다.

 

ISP(Interface Segregation Principle, 인터페이스 분리의 원칙)

  • 인터페이스의 단일 책임을 위한 원칙입니다.
  • 즉, 일반적인 하나의 인터페이스를 조금 더 구체적인 인터페이스로 쪼개는것이 낫다는 것입니다.
  • 예)
    • 새의 행동(일반적인 하나의 인터페이스)
    • 날수 있는 행동(구체적인 인터페이스)
    • 울수 있는 행동(구체적인 인터페이스)

 

DIP(Dependency Inversion Principle, 의존성 역전의 원칙)

  • 자주 변경되는 구체 클래스에 의존하지 않고, 추상화(일반화) 된 클래스에 의존하는 것을 의미합니다.
  • 구체 클래스가 추상 클래스에 의존하므로 의존 관계가 역전된 형태입니다.
  • 예)
    • Spring framework의 IOC 컨테이너
      • 직접 객체를 생성하는것이 아니라 프레임워크에서 객체를 제공해줍니다.
    • 느슨한 연결 측면에서의 DIP
      • 추상 클래스에서 구체 클래스의 hook 메서드를 호출하는 것
        • hook 메서드 호출 시, 구체 클래스가 무엇인지 알 필요가 없습니다.
    • 이벤트 드리븐 방식
      • 이벤트 리스너를 등록하고 hook 메서드를 통해 해당 이벤트를 처리합니다.
        • hook 메서드 호훌 시, 구체적인 이벤트 리스너가 무엇인지 알 필요가 없습니다.

: NiFi 서버의 로그인 방식은 LDAP, Kerberos, OpenID Connect, Apache Knox에서 하나를 선택하여 구성할 수 있다(다음 링크 참조: https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#user_authentication). 이 네가지 방식중에서 OpenID Connect(다음 링크 참조: https://openid.net/connect/) 로그인 방식을 택한것은 별도의 추가적인 인증 서버 설치 및 관리가 필요없어서 관리의 부담이 적고, 설정 자체가 네가지 방식 중에 가장 단순하기때문이다.

(1) Google API 프로젝트 생성 : OpenID Connect 방식의 로그인을 사용하기위해서 Google API를 사용할 것이므로 Google API용 프로젝트를 먼저 생성해야 한다.

a. 아래 링크를 통해 Google API로 접속을 한다.

https://console.developers.google.com/apis

b. (그림 1. Google API Dashboard)와 같이 새 프로젝트를 생성하기 위해 1)의 빨간색 박스 영역을 클릭한다.

(그림 1. Google API Dashboard)

c. (그림 2. 새프로젝트 생성)과 같이 1)의 "새 프로젝트"를 클릭한다.

※ NiFi 서버용 프로젝트(nifi-fitogether)가 이미 생성이 되어 있는 상태이지만 새 프로젝트를 생성하는 방법을 보여주기 위함이다.

(그림 2. 새프로젝트 생성)

d. (그림 3. 새 프로젝트 설정)에서 1) 프로젝트명을 입력 2) "만들기" 버튼을 클릭한다.

(2) OAuth 동의 화면 설정

(그림 4.1. OAuth 동의 화면 설정)

1) 좌측 메인 메뉴에서 "OAuth" 동의 화면을 클릭한다.

2) "UserType"에서 "내부"를 선택한 후, "만들기" 버튼을 클릭한다. "만들기" 버튼을 클릭하면 OAuth 동의 화면의 상세 항목 페이지로 이동한다.

 

(그림 4.1. OAuth 동의 화면 상세)

1) 애플리케이션 이름을 적절하게 입력한다.

2) OAuth 인증을 사용하기 위한 승인된 도메인을 입력하고 엔터키를 누른 후, 맨 하단의 저장 버튼을 클릭한다.

 

(3) 클라이언트 ID 생성

(그림 5.1. 사용자 인증 정보 생성)

1) 좌측의 메인 메뉴에서 "사용자 인증 정보"를 클릭한다.

2) 사용자 인증 정보를 생성하기 위해서 상단의 "사용자 인증 정보 만들기"를 클릭한다.

3) "OAuth 클라이언트 ID"를 클릭한다.

 

(그림 5.2. 애플리케이션 유형 선택)

1) 애플리케이션 유형 항목에서 "웹 애플리케이션"을 선택한다.

2) 이름 항목에 적절한 이름을 입력한 후, "생성" 버튼을 클릭한다.

 

(그림 5.3. OAuth 클라이언트 생성 후 팝업)

: OAuth 클라이언트 생성 직 후, 위와 같은 팝업이 뜨는데 "클라이언트 ID"와 "클라이언트 보안 비밀번호"를 copy해서 보관해둔다.

 

(그림 5.4. OAuth 클라이언트 ID 목록)

1) 생성된 OAuth 클라이언트 ID를 설정하기 위해 클라이언트 ID 목록에서 이름 항목을 클릭한다.

 

(그림 5.5. OAuth 클라이언트 ID 설정)

1) 이름 항목에 이름을 적절하게 입력한다.

2) URI 버튼을 클릭해서 아래 URI 주소를 입력한 후, "저장" 버튼을 클릭한다.

https://xxx.xxxx.com/nifi-api/access/oidc/callback

 

(4) nifi.properties 설정 : Google 클라이언트 ID를 이용한 OpenID Connect 방식의 로그인을 위해 nifi.properties(파일 위치: /home/ubuntu/app/nifi-1.11.4/conf)에 아래와 같이 설정을 추가한다.

(그림 6. nifi.properties에 OpenID Connect 설정)

1) OpenID Connect Provider 검색 URL을 "nifi.security.user.oidc.discovery.url" 프로퍼티의 값으로 입력한다.

2) (그림 5.3. OAuth 클라이언트 생성 후 팝업)에서 copy하여 보관한 클라이언트 ID를 "nifi.security.user.oidc.client.id" 프로퍼티의 값으로 입력한다.

3) (그림 5.3. OAuth 클라이언트 생성 후 팝업)에서 copy하여 보관한 클라이언트 보안 비밀번호를 "nifi.security.user.oidc.client.secret" 프로퍼티의 값으로 입력한다.

 

(5) authorizers.xml 파일 설정 : OpenID Connect 로그인을 위한 접근 권한을 부여하기 위해 authorizers.xml(/home/ubuntu/app/nifi-1.11.4/conf) 파일을 아래와 같이 수정한다.

(그림 7. authorizers.xml에 접근 권한 설정)

: 1), 2)에 Google OAuth 클라이언트 ID를 생성할 때 로그인한 Google 이메일 계정을 입력한 후, 파일을 저장한다.

 

(6) 로그인 테스트

a. NiFi 서버를 stop and start 시킨다.(/home/ubuntu/app/nifi-1.11.4/bin/nifi.sh stop, /home/ubuntu/app/nifi-1.11.4/bin/nifi.sh start)

b. NiFi 서버(https://xxx.xxxxx.com/nifi)로 다시 접속을 한다.

c. 아래와 같이 로그인을 위해 구글 계정을 선택 또는 입력하는 화면에서 authorizers.xml에 등록한 구글 계정을 선택 또는 입력한다.

(그림 8. 구글 계정 선택 또는 입력)

d. 아래와 같이 입력한 구글 계정의 비밀번호를 입력한다.

(그림 9. 구글 계정의 비밀번호)

e. 아래와 같이 NiFi 캔버스가 오픈되고, 좌측 상단에 구글 이메일 계정이 보이면 정상적으로 로그인이 된것이다.

(그림 10. OpenID Connect 방식으로 로그인 한 화면)

: 로그인에 성공하면 1)과 같이 구글 계정이 표시된다. 로그 아웃을 하기 위해서는 1)에서 "LOG OUT" 링크를 클릭하면 정상적으로 로그 아웃된다. "LOG OUT" 링크를 클릭했을때 자동으로 다시 로그인이 된다면 구글 사이트로 이동해서 구글 계정 자체를 로그아웃한 다음에 다시 한번 시도한다.

'Open source > Apache NiFi' 카테고리의 다른 글

Apache NiFi에 SSL 적용  (0) 2021.07.09

: NiFi 서버에 로그인 화면을 적용하기 위해서는 SSL 연동이 필수이다. 현재(2020년 5월 12일 기준) EC2에 생성된 NiFi 서버의 SSL 인증서는 Self Signed Certificate을 사용하였으며, OpenSSL 툴을 사용하지 않고 좀 더 빠르게 Self Signed Certificate을 생성하기 위해 TinyCert(https://www.tinycert.org/)를 이용하였다.

(1) 인증서 생성 : NiFi 서버에 SSL 설정을 하기 위해서는 먼저 TinyCert 에서 CA 인증서와 서버 인증서를 생성 및 다운로드해야하는데 절차는 다음과 같다.

a. TinyCert에 접속하여 먼저 Sing Up을 한다. Email Address와 Password, Passphrase를 입력 해야 하는데 Passphrase는 서버 인증서를 NiFi 서버에 구성할때 필요하므로 기억을 해야한다.

b. TinyCert에 로그인이 된 상태에서 아래 (그림 1. CA 생성)에서와 같이 메인 화면에서 CA 인증서를 생성한다.

(그림 1. CA 생성)

1) "Create" 버튼을 클릭하여 CA 인증서를 생성한다. CA 인증서 항목은 적절하게 입력해주면 된다.

 

c. 서버 인증서 생성

(그림2. 서버 인증서 생성)

1) "Create" 버튼을 클릭해서 서버 인증서를 생성한다. 서버 인증서 항목은 적절하게 입력해주면 된다.

 

d. CA 다운로드

(그림3. CA 다운로드)

1) "다운로드" 버튼을 클릭해서 CA 인증서를 다운로드한다.

 

e. 서버 인증서 다운로드

(그림 4.1. 생성된 서버 인증서 목록)

1) 생성된 서버 인증서 목록을 클릭한다.

 

(그림 4.2. 생성된 서버 인증서 상세)

1) "다운로드" 버튼을 클릭해서 서버 인증서를 다운로드 버튼을 클릭한다.

2) 서버 인증서 타입을 "PKCS#12 Archive" 타입으로 선택하면 서버 인증서가 다운로드 된다. 

 

(2) NiFi 서버에 인증서 업로드 : 로컬 PC에 다운로드 받은 CA 인증서와 서버 인증서를 AWS EC2에 설치된 NiFi 서버쪽으로 업로드를 해야하는데 이를 위해서는 EC2 인스턴스에 해당하는 Private Key가 필요하다. Private Key는 개발 담당자(2020년 5월 12일 기준: 황정식)에게 문의한다. 아래 링크의 문서를 참조해서 다운로드 받은 CA 인증서와 서버 인증서를 NiFi 서버쪽으로 업로드한다.

a. SCP 파일 전송 관련 문서 : https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html

b. 현재(2020년 5월 12일 기준) 업로드된 CA 인증서와 서버 인증서의 디렉토리 경로는 (그림 5. CA 인증서와 서버 인증서의 디렉토리 경로)와 같다.

(그림 5. CA 인증서와 서버 인증서의 디렉토리 경로)

1) CA 인증서와 서버 인증서가 위치한 디렉토리 경로이다.

2) 로컬 PC에서 업로드 된 CA 인증서이다.

3) 로컬 PC에서 업로드 된 서버 인증서이다.

(3) Truststore 생성 :  NiFi 서버는 SSL 설정을 위해서 서버 인증서와 추가적으로 Truststore를 생성해야하는데 Truststore는 로컬 PC에서 업로드한 CA 인증서를 이용해서 (그림 6. Truststore 생성)과 같이 생성할 수 있다.

(그림 6. Truststore 생성)

1) <ca_file> 부분은 로컬 PC에서 업로드한 CA 인증서 파일명이며, <password>에는 패스워드를 입력하면 된다. 설정된 패스워드에 대해서는 개발 담당자(2020년 5월 12일 기준: 황정식)에게 문의한다.

(4) nifi.properties 설정 : nifi.properties 파일을 열어서 SSL 포트, KeyStore, Truststore 설정을 해준다.

a. HTTPS 설정 : NiFi 서버는 http와 https를 동시에 운용할 수 없기때문에 (그림7.1. HTTPS 설정)과 같이 프로퍼티 값을 수정한다.

(그림 7.1. HTTPS 설정)

1) "nifi.remote.input.secure" 프로퍼티의 값을 "true"로 수정한다.

2) "nifi.remote.input.http.enabled" 프로퍼티의 값을 "false"로 수정한다.

3) "nifi.web.https.port" 프로퍼티의 값을 "9443"으로 수정한다. NiFi의 https 디폴트 포트는 9443이다.

4) 그림 7.1에는 빨간 박스로 표시 되어 있지만 "nifi.web.https.host" 프로퍼티의 값으로 NiFi 서버와 매핑된 도메인을 입력해야지만 다음 글에서 설명하는 OpenID Connect 방식의 로그인을 사용할 수 있다.

b. KeyStore 및 Truststore 설정: CA 인증서를 이용해서 생성한 Truststore와 서버 인증서 설정을 (그림 7.2. KeyStore 및 Truststore 설정)과 같이 추가한다.

(그림 7.2. KeyStore 및 Truststore 설정)

1) "nifi.security.keystore" 프로퍼티의 값으로 서버 인증서의 경로를 입력한다.

2) "nifi.security.keystoreType" 프로퍼티의 값으로 "PKCS12"를 입력한다.

3) "nifi.security.keystorePasswd" 프로퍼티의 값으로 TinyCert에서 Sign up 시, 등록한 passphrase 값을 입력한다.

4) "nifi.security.truststore" 프로퍼티의 값으로 CA 인증서를 이용해서 생성한 Truststore의 경로를 입력한다.

5) "nifi.security.truststoreType" 프로퍼티의 값으로 "JKS"를 입력한다.

6) "nifi.security.truststorePasswd" 프로퍼티의 값으로 Truststore를 생성할 때 설정한 패스워드를 입력한다.

(5) hosts 파일 설정 : NiFi 서버에 접속하는 사용자는 (6)에서 설명할 도메인을 통해서 NiFi 서버에 접속을 할 수 있는데, 이 도메인은 EC2에서 추가한 외부 IP인 탄력적 IP와 매핑이 되어 있다. 하지만 NiFi 서버 입장에서는 EC2 인스턴스 내부 IP와 매핑이 되어야하므로 hosts 파일에 매핑 정보를 추가해주어야 한다. 

a. hosts 파일의 경로 : /etc/hosts

b. 내부 IP는 "hostname -I" 명령어로 확인 가능하며, NiFi 서버가 설치된 EC2 인스턴스의 내부 IP는 "172.31.34.156" 이다.

b. (그림 8. 도메인과 내부 IP 매핑 정보)와 같이 매핑 정보를 추가해준다.

1) EC2 인스턴스의 내부 IP와 도메인 매핑 정보를 hosts 파일에 추가한다.

(6) 접속 테스트

a. nifi.sh 명령을 사용해 NiFi 서버를 가동 시킨다. nifi.sh 전체 명령어는 아래와 같다.

 ubuntu@ip:~/app/nifi-1.11.4$ pwd
/home/ubuntu/app/nifi-1.11.4
ubuntu@ip:~/app/nifi-1.11.4$ bin/nifi.sh start

 

b. 아래 주소로 접속을 해본다.

https://도메인:9443/nifi

1) NiFi 서버쪽에 정상적으로 접속이 되지 않는 이유는 NiFi 서버는 SSL 설정이 완료되면 추가적으로 로그인 설정을 해주어야 한다. 접속 시, 발생하는 에러는 SSL 설정은 되어 있으나 로그인 설정이 되어 있지 않았기때문에 발생하는 에러이다.

다음 글에서는 NiFi 로그인 인증 방식중에 하나인 OpenID Connect 설정을 해보겠습니다.

'Open source > Apache NiFi' 카테고리의 다른 글

OpenID Connect 방식의 로그인 연동  (3) 2021.07.09

안녕하세요? [Kevin의 알기 쉬운 RxJava] 강의를 진행하고 있는 Kevin입니다.

이번 시간에는 'Hello, Reactor' 코드를 가지고 Reactor의 기본 구조를 들여다보는 시간을 가져보겠습니다.

개발에 처음 입문하실때 대부분 'Hello World!' 코드를 실행해보셨을텐데요. Reactor 역시 마찬가지로 'Hello, Reactor' 메시지를 출력해보면서 Reactor의 기본 구조를 간단하게 살펴보도록 하겠습니다.

어렵지 않으니 가벼운 마음으로 글을 읽어주시면 감사드리겠습니다.^^

먼저 아래의 코드를 보시죠.

[Hello Reactor 코드]

 

여러분들이 아시다 시피 Reactor는 RxJava와 마찬가지로 Reactive Streams라는 표준 사양을 구현한 구현체입니다. 따라서 Reactive Streams에서 정의하고 있는 생산자 즉, Publisher와 소비자 즉, Subscriber를 구현하고 있는데요.

위의 코드에서 보시는것처럼 Reactor에서는 Flux가 대표적인 생산자 중에 하나입니다.
위의 코드에서 소비자라고 되어 있는 부분은 람다 베이스 Subscriber가 되겠습니다.

생산자 쪽에서는 just라는 Operator(연산자)를 사용해서 두 개의 데이터를 소비자 쪽으로 통지를 하는데요. Reactor에서는 생산자 쪽에서 통지할 데이터를 정의하는 과정 자체를 묶어서 'Sequence(시퀀스)'라고 합니다. 이 Sequence라는 용어는 Reactor를 접하다보면 자주 나오는 용어이니 기억을 해두시길 바래볼게요.

생산자 쪽에서 소비자 쪽으로 데이터를 통지하는것이 맞긴하지만 최종 소비자까지 데이터가 전달 되기까지는 중간에 여러 Operator를 만날 수 있기때문에 엄밀히 말하자면 생산자가 소비자에게 데이터를 통지한다라고 말하기보다는 생산자가 Downstream으로 데이터를 내보낸다라는 표현이 더 정확할 것 같네요.

용어에 대한 부분은 아래쪽에서 다시 정의를 해보도록 하구요.

아무튼 생산자 쪽에서 데이터를 통지하게 되면 최종 소비자 쪽에 바로 데이터가 전달되는 것이 아니라 map( )이라는 Operator에서 원본 데이터가 가공 처리 된 후에 최종 소비자 쪽으로 전달이 되는것을 볼 수 있는데요.

여기서는 통지된 String 데이터를 소문자로 변환을 한 후에 최종 소비자쪽으로 전달하고 있네요.

자, RxJava 강의 시간에도 말씀을 드렸지만 리액티브 프로그래밍은 크게 세가지의 step으로 이루어져있습니다.

1 step : 생산자가 데이터를 통지한다.
2 step : 통지된 데이터를 Operator로 가공한다.
3 step : 가공 처리된 데이터를 최종 소비자에게 전달한다.

이 세가지 큰 흐름은 기본적으로 꼭 기억을 해두시고, 나머지는 데이터를 통지하는 다양한 방법, 데이터를 가공하는 다양한 방법, 비동기 프로그래밍을 위한 쓰레드를 할당하는 방법, 에러 처리 방법 등을 단계적으로 적용 하는 식으로 학습을 진행해 나가면 리액티브 프로그래밍을 조금 더 수월하게 이해하실 수 있을거라는 생각이 드네요. ^^;

자, 그럼 마지막으로 Reactor에 대한 에피소드를 계속 진행가기 전에 Reactor에서 사용되는 여러가지 용어를 정의하고 에피소드를 마무리 하도록 하겠습니다.

먼저 아래 용어들을 보시죠.

  • Publisher : 발행자, 게시자, 생산자, 방출자(Emitter)
  • Subscriber : 구독자, 소비자 
  • Emit : Publisher가 데이터를 내보내는 것(방출하다. 내보내다. 통지하다.) 
  • Sequence : Publisher가 emit하는 데이터의 연속적인 흐름. 스트림과 같은 의미라고 보면 됨 
  • Subscribe : SubscriberSequence를 구독하는 것
  • Dispose : Suscriber가 Sequence 구독을 해지 하는 것 
  • Downstream : 현재 Operator 체인의 위치에서 봤을때 데이터가 전달 되는 하위 Operator 및 method 체인
  • Upstream : 현재 Operator 체인의 위치에서 봤을때 상위 Operator 및 method 체인

여태껏 제가 데이터를 통지하는 쪽은 생산자로 지칭했는데요. 보시는대로 생산자 이외에 발행자, 게시자, 방출자 등 다양하게 사용이 되고 있습니다. 모두 같은 의미라고 보시면 되겠지만 앞으로 에피소드를 진행할 때는 그냥 영어로 Publisher라고 지칭하도록 하겠습니다. ^^;

따라서 소비자 역시 영어로 Subscriber라고 지칭하면 되겠구요.

데이터를 통지하는 행위 역시 영어 그대로 Emit 한다라고 하겠습니다.

Sequence는 위에서 잠깐 말씀 드렸죠? Publisher쪽에서 데이터를 emit하는 흐름 자체를 통칭하는 것을 의미하는데, 예를 들어서 Flux라는 Publisher가 데이터의 Emit을 정의하는 부분을 설명할때 Sequence를 생성한다 라고 표현할 수 있겠습니다.

Subscribe와 Dispose는 구독하다, 구독을 해지하다라고 표현하도록 하겠습니다.

마지막으로 Downstream과 Upstream이라는 용어인데요. Reactor를 학습하다보면 종종 나오는 용어인데 개념적으로 이해하기 모호한 면이 있는 용어중에 하나이기도 합니다.

이럴때는 그냥 단순하게 이해를 하는게 제일 좋을거라고 생각을 해보게되네요. ^^;
예를 들어 위의 코드에서 just( )의 위치에서 Downstream은 map( )부터 subscribe( )까지 하위 체인이 되겠습니다. 그리고 subscribe( ) 입장에서는 map( )과 just( )가 Upstream이 되겠구요. 

just( )의 내부를 들여다보면 return 값으로 Flux를 반환하는데요. just( )에서 반환하는 Flux를 원본 Flux가 되겠습니다. 그리고 map( )에서 반환하는 return 값 역시 Flux인데요. 여기서의 Flux는 원본 Flux가 아닌 가공 처리된 데이터를 가지고 있는 새로운 Flux입니다. 

Flux(또는 Mono)의 이러한 처리 흐름때문에 Downstream이나 Upstream이라는 용어가 생겨난것 같은데 위에서 언급드린것처럼 단순하게 생각해주시면 될것 같아요.

자, 오늘은 'Hello, Reactor' 코드를 통해서 Reactor 의 기본 구조를 살펴보았는데요. 
다음 시간에 다른 에피소드를 가지고 다시 찾아뵙도록 하겠습니다. ^^

감사합니다!

[Kevin의 알기 쉬운 RxJava 1부] 강의 바로가기

 

Kevin의 알기 쉬운 RxJava 1부 - 인프런

리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti

www.inflearn.com

[Kevin의 알기 쉬운 RxJava 2부] 강의 바로가기

 

Kevin의 알기 쉬운 RxJava 2부 - 인프런

리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti

www.inflearn.com

 

수강생 여러분 안녕하세요. RxJava 강의를 진행하고있는 Kevin입니다. 오늘은 리액티브 프로그래밍과 밀접한 관련이 있는 이벤트 루프에대해서 잠시 알아보도록하겠습니다.

이벤트 루프에 대해서 먼저 이야기 하기 전에 저희는 서블릿에 대한 언급을 먼저 하고 넘어가야되는데, 그 이유는 이벤트 루프라는 개념이 나오게 된 배경에는 서블릿이 있기때문이에요.

 그럼 서블릿에 대한 이야기를 먼저 해볼까요?

서블릿의 기본 개념

서블릿은 뭘까요? 자바를 사용하는 개발자이거나 자바를 배우고 계신 분들이 웹 애플리케이션을 만들어 보셨다면 이미 서블릿을 사용해보셨을겁니다. 여러분들이 JSP를 사용해보셨거나 아니면 Spring framework을 사용해보셨다면 여러분들은 알게 모르게 서블릿이란 놈을 사용해보셨다는 말씀. ^^

서블릿은 웹에서 클라이언트의 요청을 동적으로 처리하기 위해 서블릿 규약이라는 일정한 규칙에 맞춰서 작성되는 클래스인데요. 이 클래스들이 클라이언트로 부터 들어오는 요청을 받아서 응답으로 되돌려주는 역할을 하죠. 

개발자가 작성한 서블릿 클래스가 HttpServlet을 상속 받고, doGet( ), doPost()를 구현하는 것이 서블릿 클래스의 기본이라는 사실은  서블릿 클래스를 사용해보신분들은 너무나 잘 알고들 계실겁니다.



Spring에서의 서블릿

어쨌든 저희가 HttpServlet 같은 상위 클래스를 상속 받아서 개발을 진행할건 아니기때문에 서블릿에 대해서 더 자세한 이야기를 할 필요는 없을 것을 같네요. 다만, 이것 하나만은 기억을 하셔야 될 것 같습니다. Spring MVC 역시 내부적으로는 Servlet 기술을 사용을 한다는것을요. DispatcherServlet 이라는 녀석을 기억하시죠? Spring MVC에서는 이 DispatchServlet이라는 녀석이 최앞단에서 클라이언트의 요청을 전달 받아서 해당 요청에 매핑되는 Controller 에게 요청을 전달한다는 사실은 Spring MVC로 개발을 해보신분들은 잘 알고 계실겁니다. 

서블릿 방식의 문제점

그런데 이러한 서블릿을 사용하여 클라이언트의 요청을 처리하는 방식에는 큰 문제점이 하나 있는데요. 서블릿 방식은 클라이언트에서 요청이 들어오면 해당 요청을 처리하기 위해서 요청 당 쓰레드 하나씩을 생성해서 처리합니다. 하나의 쓰레드에서 클라이언트의 요청을 다 처리하면 이 쓰레드는 쓰레드풀이라는 쓰레드의 저장소에 다시 반납이 되는데,  참고로 이 쓰레드 풀은 톰캣 같은 서블릿 컨테이너가 관리를 해줍니다. 사실 클라이언트의 요청이 폭주만 하지 않으면 지금과 같은 서블릿 방식이 큰 문제가 되지는 않겠지만 클라이언트의 요청이 쓰레드의 갯수에 비해 기하급수적으로 늘어나는 순간 문제가 시작됩니다. 요청을 처리하기 위한 쓰레드가 쓰레드 풀에 남아있지 않을 경우 기존에 사용되던 쓰레드가 쓰레드 풀에 다시 반납이 되어 다른 요청을 처리하기 위해서 재사용할 수 있 때까지 클라이언트로 부터 들어온 요청들은 대기를 해야되니까요. 이런 성능상의 문제점을 상당 부분 해소할 수 있는 방식이 바로 이벤트 루프 방식인데요. 그럼 이벤트 루프 방식이 무엇인지, 어떻게 클라이언트의 요청을 효과적으로 처리하는지 간단하게 살펴볼까요?

이벤트 루프란

아래 그림을 보면서 이벤트 루프에 대해서 설명 드리겠습니다.

클라이언트가 서버로 요청을 보내면 요청 핸들러가 클라이언트의 요청이 들어왔다는 이벤트를 이벤트 루프에 푸시합니다.

이벤트 루프는 그림과 같이 계속해서 루프를 돌면서(한마디로 무한루프) 요청 핸들러로부터 푸시된 이벤트를 감지합니다. 그리고 나서 해당 이벤트에 대한 콜백을 등록한 후, 클라이언트가 요청한 작업을 처리합니다. 작업이 끝나면 작업이 끝났다는 이벤트를 이벤트 루프에 푸시하고 등록된 콜백을 호출한 후, 요청 핸들러를 다시 거쳐서 클라이언트에게 최종적으로 응답을 되돌려줍니다. 어떠신가요? 이벤트 루프가 무엇인지 대충 감이 오시는지 모르겠네요.

이벤트 루프 방식의 장점은 쓰레드 하나로 굉장히 많은 요청을 병렬로 처리하기때문에 기존의 서블릿 방식의 성능상의 문제를 해결한다는것입니다. 

WebFlux와 이벤트 루프

  Spring WebFlux가 바로 이벤트 루프 방식을 사용하기때문에 대량의 클라이언트 요청을 효과적으로 처리할 수 가 있게되었는데요. Spring WebFlux를 사용해서 대량의 클라이언트 요청을 효과적으로 처리하는 애플리케이션을 한번 만들어보고 싶어지지 않으셨나요? ^^;

Spring MVC냐 Spring WebFlux냐

자, 그러면 무조건적으로 Spring MVC 대신에 Spring WebFlux를 사용하면 좋은것이냐하면 사실 그건 아닌것같습니다. Spring MVC로 개발을 진행해보셨다면 아시겠지만 생산성이 대단하다는것을 느끼실 수가 있을겁니다. 그리고 RxJava 2부 강의에서 비동기 방식의 디버깅에 대한 이야기를 드리겠지만 비동기 방식보다는 동기 방식이 디버깅도 용이하고 여태껏 사용해온 익숙한 명령형 프로그래밍 방식으로 코드를 짜면 되니 코드를 짜는 부담 역시 줄어드는게 사실일테구요. 하지만 분명한것은 사물 인터넷, 자동차, 인공 지능 등등 우리가 생각하는 전통적인 클라이언트 이외에 다양한 클라이언트들이 계속해서 생겨날텐데 이런 종류의 클라이언트들이 계속해서 생겨나면 생겨날수록 서버가 감당해야 되는 요청의 수는 점점 늘어나겠죠. 마이크로 서비스로 인해서 또한 서비스들 간의 요청도 점점 늘어나고 있기도 하구요. 

결론은 지금 당장은 아니더라도 개발자로서 미래를 준비하고 싶으신 분은 리액티브 프로그래밍 기법을 꼭 익혀두셨으면 하는 바램입니다. ^^ 이야기는 길었는데 결론은 꽤 간단하다는.. ^^;;

오늘은 이벤트 루프에 대한 이야기를 간단하게 해보았는데요. Spring WebFlux에 대한 이야기로 또 찾아뵙도록 할게요.

감사합니다~

 

[Kevin의 알기 쉬운 RxJava 1부 강의] 바로 가기

 

Kevin의 알기 쉬운 RxJava 1부 - 인프런

리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti

www.inflearn.com

 

[Kevin의 알기 쉬운 RxJava 2부 강의] 바로 가기

 

Kevin의 알기 쉬운 RxJava 2부 - 인프런

리액티브 프로그래밍이라는 진입 장벽을 넘고 싶으신가요? Kevin의 알기 쉬운 RxJava가 그 벽을 넘을 수 있는 힘을 키워드리겠습니다. 초급 프레임워크 및 라이브러리 함수형 프로그래밍 RxJava Reacti

www.inflearn.com

 

+ Recent posts

출처: http://large.tistory.com/23 [Large]