리액티브 시스템은 말 그대로 반응을 잘하는 시스템이다. 반응을 잘한다는 의미는 클라이언트의 요청에 머뭇거리지 않고 즉시 응답해 주는 것을 의미한다.
다시 말해서 클라이언트의 요청에 즉각적으로 응답함으로써 지연 시간을 최소화한다고도 볼 수 있다.
리액티브 선언문(The Reactive Manifesto)은 리액티브라는 용어의 의미를 올바르게 정의하기 위해 노력하는 사람들이 만든 리액티브 시스템 구축을 위한 일종의 설계 원칙이자 리액티브 시스템의 특징이라고 할 수 있다.

- MEANS: 리액티브 시스템에서 주요 통신 수단으로 무엇을 사용할 것인지 표현한 것. 그림에 나와 있는 비동기 메시지 기반의 통신을 통해서 구성요소들 간의 느슨한 결합, 격리성, 위치 투명성을 보장
- FORM: 메시지 기반 통신을 통해서 어떠한 형태를 지니는 시스템으로 형성되는지를 나타냄. 그림에서는 리액티브 시스템이 비동기 메시지 통신 기반하에 탄력성과 회복성을 가지는 시스템이어야 함을 보여줌
- 리액티브 시스템에서 탄력성(Elastic)이란 시스템의 작업량이 변화하더라도 일정한 응답을 유지하는 것을 의미. 즉, 시스템으로 유입되는 입력이 많든 적든 간에 시스템에서 요구하는 응답성을 일정하게 유지하는 것을 말함. 이렇게 일정한 응답성을 유지하기 위해서 입력을 처리하기 위한 시스템 자원을 그때 그때 적절하게 추가하거나 감소시켜서 작업량의 변화에 대응하는 것이라고 이해하면 됨
- 리액티브 시스템에서 회복성(Resilient)이란 시스템에 장애가 발생하더라도 응답성을 유지하는 것을 의미. 회복성이 없다면 장애 발생 시 시스템이 응답하지 못하는 심각한 문제에 직면하기 때문에 이를 방지하기 위해서 회복성은 리액티브 시스템의 중요한 설계 원칙이라고 볼 수 있음. 이러한 회복성을 확보하기 위해서 리액티브 시스템의 구성요소들은 비동기 메시지 기반 통신을 통해 느슨한 결합과 격리성을 보장. 시스템의 구성요소들이 독립적으로 분리되기 때문에 장애가 발생하더라도 전체 시스템은 여전히 응답 가능하고 장애가 발생한 부분만 복구하면 된다는 의미
- VALUE: 비동기 메시지 기반 통신을 바탕으로 한 회복성과 예측 가능한 규모 확장 알고리즘을 통해 시스템의 처리량을 자동으로 확장하고 축소하는 탄력성을 확보함으로써 즉각적으로 응답 가능한 시스템을 구축할 수 있음을 의미. 리액티브 시스템의 핵심 가치를 보여 준다고 볼 수 있음
리액티브 시스템은 리액티브 시스템의 설계 원칙에 따라 대규모 분산 시스템 또는 멀티코어 기반의 클라우드 시스템, 모바일 시스템 등 빠른 응답성을 바탕으로 유지보수와 확장이 용이한 시스템을 구축하는 데 활용할 수 있다.
리액티브 프로그래밍은 리액티브 시스템을 구축하는 데 필요한 프로그래밍 모델이다. 리액티브 시스템에서의 비동기 메시지 통신은 Blocking I/O 방식이 아닌 Non-Blocking I/O 방식의 통신이다.
Blocking I/O 방식의 통신에서는 해당 스레드가 작업을 처리할 때까지 남아 있는 작업들은 해당 작업이 끝날 때까지 차단되어 대기한다. 그렇기 때문에 뒤에 남아 있는 작업들을 대기 없이 처리하려면 별도의 추가 스레드를 할당해야 해서 그만큼의 비용이 더 들게 된다. 반면 Non-Blocking I/O 방식의 통신에서는 말 그대로 스레드가 차단되지 않는다.
결론적으로 리액티브 프로그래밍은 리액티브 시스템의 설계 원칙에 잘 부합하는 비동기 Non-Blocking 통신을 위한 프로그래밍 모델이라고 볼 수 있다.
declarative programming은 선언형 프로그래밍이라는 의미이다. 우리가 흔히들 사용하는 전통적인 프로그래밍 방식은 C 언어나 JAVA 같은 명령형 프로그래밍 방식이다. 즉, 실행할 동작을 구체적으로 명시하는 프로그래밍 코드 형태라고 볼 수 있다.
반면에 선언형 프로그래밍 방식은 실행할 동작을 구체적으로 명시하지 않고 목표만 선언하는 방식이다.
data streams라는 것은 데이터 흐름, 즉 데이터가 지속적으로 발생한다는 의미이고, the propagation of change는 지속적으로 데이터가 발생할 때마다 이것을 변화하는 이벤트로 보고, 이 이벤트를 발생시키면서 데이터를 계속적으로 전달하는 것을 의미한다.
public class ImperativeExample {
public static void main(String[] args) {
// 정수 배열 초기화
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("짝수 숫자 출력:");
// 명령형 프로그래밍 방식으로 짝수만 출력
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 == 0) { // 짝수인지 확인
System.out.println(numbers[i]); // 짝수일 경우 출력
}
}
}
}
명령형 프로그래밍 방식은 어떤 작업을 처리하기 위해 실행할 동작을 코드에 구체적으로 명시한다. 위의 코드는 numbers라는 List에 포함된 숫자들 중에서 짝수만 출력한다. 이 작업을 처리하기 위해서 여러 개의 동작을 코드에 명시한다.
- 먼저 for문을 돌아서 numbers에서 각각의 숫자들에 접근하는 동작을 한다.
- 다음으로 if문을 사용해서 짝수인 숫자만 선택하기 위한 동작을 한다.
- 마지막으로 이렇게 조건문으로 선택된 숫자들을 출력한다.
import java.util.Arrays;
public class DeclarativeExample {
public static void main(String[] args) {
// 정수 배열 초기화
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("짝수 숫자 출력:");
// 선언형 프로그래밍 방식으로 짝수만 출력
Arrays.stream(numbers) // 스트림 생성
.filter(n -> n % 2 == 0) // 짝수 필터링
.forEach(System.out::println); // 출력
}
}
리액티브 프로그래밍은 아니지만 선언형 프로그래밍 방식의 예를 설명하기 적절한 Java의 스트림을 사용했다.
코드를 보면 numbers List에 포함된 각각의 숫자에 접근하는 동작을 하는 for문이 사라졌다. for문이 사라진 이유는 for문에서 하는 구체적인 동작을 Java의 스트림이 내부에서 직접 해 주기 때문이다.
그리고 if문 대신에 filter 메서드를 사용해서 조건에 맞는 데이터를 필터링하며, 필터링된 숫자들을 출력한다.
정리하자면, 다음과 같다.
- 선언형 프로그래밍 방식에서는 동작을 구체적으로 명시하지 않고 목표만 선언한다.
- 선언형 프로그래밍에서는 여러 가지 동작을 각각 별도의 코드로 분리하지 않고 각 동작에 대해서 메서드 체인을 형성해서 한 문장으로 된 코드로 구성한다. 이렇게 연결하면 입력으로 들어오는 데이터를 어떤 식으로 처리할지 한눈에 알아볼 수 있기 때문에 코드가 간결해지고 가독성도 좋아진다.
- 선언형 프로그래밍 방식은 함수형 프로그래밍으로 구성된다.
Publisher는 발행인, 발행자 정도로 해석할 수 있다. 리액티브 프로그래밍에서는 발행자, 게시자, 생산자, 방출자 등 여러 가지 용어를 사용하지만 이 용어들의 공통점은 입력으로 들어오는 데이터를 제공하는 역할을 한다는 것이다.
Publisher가 데이터를 제공하는 역할을 한다면, Subscriber는 Publisher가 제공한 데이터를 전달받아서 사용하는 주체라고 할 수 있다. Subscriber는 발행자의 반대 의미로 구독자라고도 불리고, 생산자의 반대 의미로 데이터를 소비하는 관점에서 소비자라고도 불린다. 두 용어의 공통점은 Publisher로부터 전달받은 데이터를 사용하는 역할을 한다는 것이다.
Data Source는 Publisher의 입력으로 들어오는 데이터를 대표하는 용어인데, 이 Data Source를 리액티브 프로그래밍에서는 Data Stream이라고도 표현한다. 엄밀히 따지자면 둘의 의미는 조금 다르지만, 둘 다 Publisher의 입력으로 전달되는 데이터라고 알고 있어도 무방하다.
Publisher로부터 전달된 데이터가 순수하게 아무런 처리를 거치지 않고 그대로 Subscriber에 전달되는 경우는 거의 없다. 애플리케이션의 요구사항에 맞게 Publisher와 Subscriber 사이에서 적절한 가공 처리가 이루어지는데, 이 가공 처리를 담당하는 것이 바로 Operator이다.
데이터를 생성하는 Operator부터 시작해서 데이터 필터링, 데이터 변환 등 리액티브 프로그래밍에는 수많은 Operator가 존재한다.