{"id":44786077,"url":"https://github.com/nooose/reactive-streams","last_synced_at":"2026-02-16T09:38:49.039Z","repository":{"id":192108414,"uuid":"615930223","full_name":"nooose/reactive-streams","owner":"nooose","description":"내가 보려고 만든 Reactive Streams 정리 저장소 ","archived":false,"fork":false,"pushed_at":"2023-10-01T07:34:27.000Z","size":188,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2023-12-19T16:05:58.498Z","etag":null,"topics":["reactive","reactive-programming","reactive-streams","reactor","rxjava3","webflux"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nooose.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-03-19T05:02:13.000Z","updated_at":"2023-09-05T15:53:51.000Z","dependencies_parsed_at":"2023-09-02T16:28:27.843Z","dependency_job_id":"cb62e75a-632f-4068-aa93-423c29f9932c","html_url":"https://github.com/nooose/reactive-streams","commit_stats":{"total_commits":55,"total_committers":1,"mean_commits":55.0,"dds":0.0,"last_synced_commit":"d479e9fb12a62c4d2d1e6a59d6ba610b214036aa"},"previous_names":["nooose/rxjava"],"tags_count":0,"template":null,"template_full_name":null,"purl":"pkg:github/nooose/reactive-streams","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooose%2Freactive-streams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooose%2Freactive-streams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooose%2Freactive-streams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooose%2Freactive-streams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nooose","download_url":"https://codeload.github.com/nooose/reactive-streams/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooose%2Freactive-streams/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29505011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T09:05:14.864Z","status":"ssl_error","status_checked_at":"2026-02-16T08:55:59.364Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["reactive","reactive-programming","reactive-streams","reactor","rxjava3","webflux"],"created_at":"2026-02-16T09:38:47.481Z","updated_at":"2026-02-16T09:38:49.022Z","avatar_url":"https://github.com/nooose.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RxJava\n\n[Reactive Programming](https://nooose.notion.site/Reactive-Programming-37908bb1a6c441d9bc2272cbf3e493ab)\n\n[Reactive Streams](https://nooose.notion.site/Reactive-Streams-044e6cf525df4cda8a894a11431146dd)\n\n[Back Pressure](https://nooose.notion.site/Flowable-Observable-35085f1b096b41e1b3318b4f5dc869df)\n\n[Single, Maybe, Completable](https://nooose.notion.site/Single-Maybe-Completable-bde309de18004caebe38313fc240a19e)\n\n[Operators](https://nooose.notion.site/Operator-db0644e736704020910b9fe0a395ee34)\n\n# Reactor\n\n- Publisher\n    - Mono[0|1]: 데이터를 한 건도 emit하지 않거나 단 한 건만 emit하는 단발성 데이터에 특화된 Publisher\n    - Flux[N]: 0개부터 N개, 즉 무한대의 데이터를 emit할 수 있는 Publisher\n- Subscriber\n\n## Backpressure 전략\n\n- `IGNORE`: Backpressure를 적용하지 않음\n- `ERROR`: Downstream으로 전달할 데이터가 버퍼에 가득 찰 경우, Exception을 발생시킴\n    - OverflowException(IllegalStateExeption)이 발생\n    - Error 시그널을 Subscriber에게 전송하고 삭제한 데이터는 폐기\n- `DROP`: Downstream으로 전달할 데이터가 버퍼에 가득 찰 경우, 버퍼 밖에서 대기하는 먼저 emit된 데이터부터 Drop 시킴\n    - 버퍼 바깥쪽에 있는 데이터를 폐기\n- `LATEST`: Downstream으로 전달할 데이터가 버퍼에 가득 찰 경우, 버퍼 밖에서 대기하는 가장 최근에(나중에) emit된 데이터부터 버퍼에 채움\n    - 새로운 데이터가 버퍼에 들어오는 시점에 가장 최근의 데이터만 남겨 두고 나머지 데이터를 폐기\n    - 버퍼 바깥쪽에 있는 데이터를 폐기\n- `BUFFER`: Downstream으로 전달할 데이터가 버퍼에 가득 찰 경우, 버퍼 안에 있는 데이터부터 Drop시킴\n\n## Sinks\n\n프로그래밍 방식으로 Signal을 전송할 수 있으며, 멀티 스레드 환경에서 스레드 안전성을 보장받을 수 있다.\n\n- `Sinks.one`: 한 건의 데이터를 프로그래밍 방식으로 emit\n- `Sinks.Many`: 여러 건의 데이터를 프로그래밍 방식으로 emit\n    - `UnicastSpec`: 단 하나의 Subscriber에게만 데이터를 emit\n    - `MulticastSpec`: 하나 이상의 Subscriber에게 데이터를 emit\n    - `MulticastReplaySpec`: emit된 데이터 중에서 특정 시점으로 되돌린(replay) 데이터부터 emit\n\n## Scheduler\n\n비동기 프로그래밍을 위해 사용되는 스레드를 관리해 주는 역할\n\u003e 물리적인 스레드는 병렬성과 관련이 있으며, 논리적인 스레드는 동시성과 관련이 있다.\n\n### Operator\n\n- `subscribeOn()`: 구독이 발생한 직후 실행될 스레드를 변경\n- `publishOn()`: Downstream으로 Signal을 전송할 때 실행되는 스레드를 제어하는 역할\n- `parallel()`: 라운드 로빈 방식으로 CPU 코어 개수만큼의 스레드를 병렬로 실행\n\n### Scheduler 종류\n\n- `Schedulers.immediate()`: 별도의 스레드를 추가적으로 생성하지 않고, 현재 스레드에서 작업을 처리\n- `Schedulers.single()`: 스레드 하나만 생성해서 Scheduler가 제거되기 전까지 재사용\n- `Schedulers.boundedElastic()`\n    - ExecutorService 기반의 스레드 풀을 생성한 후, 그 안에서 정해진 수만큼의 스레드를 사용하여 자업을 처리하고 작업이 종료된 스레드는 반납하여 사용함\n    - 별도의 스레드 풀을 사용하므로 Blocking I/O 작업에 최적화됨\n- `Schedulers.parallel()`: Non-Blocking I/O에 최적화되어 있는 Scheduler로서 CPU 코어 수만킁믜 스레드를 생성\n- `Schedulers.newXXXX()`: 새로운 커스텀 스레드 풀인 Scheduler 인스턴스를 생성할 수 있다.\n\n## Context\n\n- Context는 구독이 발생할 때마다 하나의 Context가 해당 구독에 연결된다.\n- Context는 Operator 체인의 아래에서 위로 전파된다.\n- 동일한 키의 값이라면 Operator 체인상에서 가장 위쪽에 위치한 `contextWrite()` 메서드에서 저장한 값으로 덮어쓴다.\n- Inner Sequence 내부에서는 외부 Context에 저장된 데이터를 읽을 수 있다.\n- 반대로 Inner Sequence 외부에서는 Inner Sequence 내부 Context에 저장된 데이터를 읽을 수 없다.\n- **Context는 인증 정보 같은 독립성을 가지는 정보를 전송하는 데 적합하다.**\n\n## Debugging\n\n- `Hooks.onOperatorDebug()`: 모든 Operator의 스택트레이스를 캡처하므로 프로덕션 환경에서는 사용하면 안 된다.\n- `checkPoint()`: 특정 Operator 체인 내의 스택트레이스만 캡처한다.\n- `log()`: 추가 지점의 Reactor Signal을 출력, 사용 개수에 제한이 없어 1개 이상 메서드 사용이 가능\n\n## Operator\n\n### 생성\n\n- `just()`: Hot Publisher, 구독 여부와는 상관없이 데이터를 emit하고, 구독이 발생하면 emit된 데이터를 다시 재생(replay)해서 전달한다.\n- `defer()`: 구독이 발생하기 전까지 데이터의 emit을 지연시킨다.\n- `using()`: 파라미터로 전달받은 resource를 emit하는 Flux 생성\n- `generator()`: 프로그래밍 방식으로 Signal 이벤트를 발생시키며, 동기 순차적으로 emit한다.\n- `create()`: generator()와 달리 한 번에 여러 건의 데이터를 비동기적으로 emit할 수 있다.\n\n# WebFlux\n\nSpring MVC 서블릿 기반 Blocking I/O 방식의 단점을 해결하기 위해 탄생\n\n- 적은 수의 스레드로 대량의 요청을 안정적으로 처리\n- Non-Blocking I/O 방식으로 처리\n    - 스레드가 차단되지 않기 때문에 적은 수의 고정된 스레드 풀을 사용해서 더 많으 요청 처리 가능\n    - **이벤트 루프** 방식 사용\n        1. 클라이언트로부터 들어오는 요청을 요청 핸들러가 전달받음\n        2. 전달받은 요청을 **이벤트 루프**에 푸시\n        3. 이벤트 루프는 네트워크, 데이터베이스 연결 작업 등 비용이 드는 작업에 대한 콜백 등록\n        4. 작업이 완료되면 완료 이벤트를 이벤트 루프에 push\n        5. 등록한 콜백을 호출해 처리 결과를 전달\n\n## WebFlux의 기술 스택\n\n|         |                 Servlet                  |                               Reactive                               |\n|---------|:----------------------------------------:|:--------------------------------------------------------------------:|\n| 서버      |            Servlet Containers            |                    Netty, Servlet 3.1+ Containers                    |\n| 서버 API  |               Servlet API                |                       Reactive Streams Adapter                       |\n| 보안      |             Spring Security              |                       Spring Security Reactive                       |\n|         |                Spring MVC                |                            Spring WebFlux                            |\n| 데이터 액세스 | Spring Data Repositories JDBC, JPA NoSQL | Spring Data Reactive Repositories R2DBC, Mongo, Cassandra, Couchbase |\n\n## WebFlux 요청 처리 흐름\n\n1. 클라이언트로부터 요청이 들어오면 Netty 등의 서버 엔진을 거쳐 `HttpHandler`가 들어오는 요청을 전달\n   받고, `ServerWebExchange(ServerHttpRequest, ServerHttpResponse)`를 생성한 후 `WebFliter` 체인으로 전달\n2. `WebFilter` 체인에서 전처리 과정을 거친 후, `WebHandler` 구현체인 `DispatcherHandler`에게 전달\n3. `DispatcherHandler`는 HandlerMapping List를 Flux의 소스로 전달 받음\n4. `ServerWebExchange`를 처리할 수 있는 핸들러를 조회\n5. 핸들러의 호출을 `HandlerAdapter`에게 위임\n6. `Controller` 또는 `HandlerFunction` 형태의 핸들러에서 요청을 처리한 후, 응답 데이터 리턴\n7. 리턴 받은 응답 데이터를 처리할 `HandlerResultHandler`를 조회\n8. `HandlerResultHandler`가 응답 데이터를 적절하게 처리 후, response로 리턴\n\n### HttpHandler\n\nrequest와 response를 처리하기 위해 추상화된 인터페이스\n\n```java\npublic interface HttpHandler {\n    public abstract void handle(HttpExchange exchange) throws IOException;\n}\n```\n\n```java\npublic class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler {\n        ...\n\n    @Override\n    public Mono\u003cVoid\u003e handle(ServerHttpRequest request, ServerHttpResponse response) {\n        ...\n        ServerWebExchange exchange = createExchange(request, response);\n        ...\n    }\n}\n```\n\n### WebFilter\n\n핸들로가 요청을 처리하기 전에 천처리 작업을 할 수 있게 해줌\n\n```java\npublic interface WebFilter {\n    Mono\u003cVoid\u003e filter(ServerWebExchange exchange, WebFilterChain chain);\n}\n```\n\n### HandlerFilterFunction\n\n함수형 기반의 요청 핸들러에 적용할 수 있는 Filter\n\n```java\n\n@FunctionalInterface\npublic interface HandlerFilterFunction\u003cT extends ServerResponse, R extends ServerResponse\u003e {\n    Mono\u003cR\u003e filter(ServerRequest request, HandlerFunction\u003cT\u003e next);\n    ...\n}\n```\n\nWebFilter의 구현체는 Bean으로 등록되는 반면, HandlerFilterFunction 구현체는 함수형 기반의 요청 핸들러에서 함수 형태로 사용\n\n```java\n\n@Configuration\npublic class LogRouterFunction {\n    // @RequestMapping 어노테이션과 같은 역할을 한다\n    @Bean\n    public RouterFunction routerFunction() {\n        return RouterFunctions.route(GET(\"/v1/router/books/{bookId}\"), this::getBookName)\n                .filter(new LogFunctionFilter());\n    }\n    ...\n}\n```\n\n### DispatcherHandler\n\nSpring MVC의 DispatcherServlet 처럼 중앙에서 먼저 요청을 전달받은 후에 다른 컴포넌트에 요청 처리를 위임\n\n- `HandlerMapping` Bean, `HandlerAdapter` Bean, `HandlerResultHandler` Bean을 검색 후 `List\u003cT\u003e` 객체를 생성\n- `handle(ServerWebExchange exchange)`: `List\u003cHandlerMapping\u003e`을 입력받아 매치되는 Handler 중 첫 번째 핸들러를 사용\n- `invokeHandler(ServerWebExchange exchange, Object handler)`: 핸들러 호출을 위임(Handler 객체와 매핑되는 HanlderAdapter를 통해 이루어짐)\n- `handleResult(ServerWebExchange exchange, HandlerResult)`: 응답 처리를 위임\n\n### HandlerMapping\n\n- request와 handler object에 대한 매핑을 정의 하는 인터페이스\n- 구현체로는 `RequestMappingHandlerMapping`, `RouterFunctionMapping` 등이 있다.\n\n```java\npublic interface HandlerMapping {\n    ...\n\n    Mono\u003cObject\u003e getHandler(ServerWebExchange exchange);\n}\n```\n\n### HandlerAdapter\n\n- HandlerMapping을 통해 얻은 핸들러를 직접적으로 호출하는 역할\n- 구현체로는 `RequestMappingHandlerAdapter`, `HandlerFunctionAdapter`, `SimpleHandlerAdapter`, `WebSocketHandlerAdapter`가 있다.\n- 두 개의 추상 메서드를 정의\n    1. `supports(Object handler)`\n    2. `handle(ServerWebExchange exchange, Object handler)`: 파라미터로 전달받은 handler object를 통해 핸들러 메서드를 호출\n\n## R2DBC(Reactive Relational Database Connectivity)\n\n관계형 데이터베이스에 리액티브 프로그래밍 API를 제공하기 위한 개방형 사양\n\n- R2DBC 이전에는 몇몇 NoSQL 벤더만 리액티브 데이터베이스 클라이언트를 제공했음\n  - Node.js에서 MongoDB와 찰떡인 이유 중 하나인 것 같음 \n\n### Spring Data R2DBC\nR2DBC 기반 Repository를 쉽게 구현하게 해 준다.\n- 기존 JPA 같은 ORM 프레임워크에서 제공하는 캐싱, 지연 로딩 등 특징들이 제거되어 단순하고 심플한 방법으로 사용 가능\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnooose%2Freactive-streams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnooose%2Freactive-streams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnooose%2Freactive-streams/lists"}