미들웨어란 무엇인가라는 질문에는 하나의 제품 목록보다 문맥이 먼저 필요하다. distributed system에서는 application 사이의 통신·messaging·transaction 같은 공통 기능을 제공하는 software를, web framework에서는 request와 response 처리 pipeline에 끼어드는 component를 middleware라고 부른다.
미들웨어는 하나의 제품 종류가 아니다
middleware라는 말은 서로 다른 시대와 architecture에서 넓게 사용됐다. 공통점은 business logic이 매번 직접 구현하기 어려운 통신과 integration 책임을 중간 layer가 맡는다는 점이다. 그러나 모든 중간 component를 같은 방식으로 운영할 수는 없다.
| 역할 | 대표 책임 | 실패 때 먼저 볼 것 |
|---|---|---|
| reverse proxy | request forwarding, TLS termination, buffering, routing | upstream timeout, header, retry, connection |
| message broker | producer·consumer decoupling, queueing, delivery state | ack, confirm, redelivery, ordering, backlog |
| ORM | object와 relational data mapping, query generation | transaction, N+1 query, mapping, connection pool |
| web framework middleware | request 전처리와 response 후처리 | ordering, short circuit, exception path |
| RPC·service middleware | serialization, discovery, deadline, retry | compatibility, timeout budget, idempotency |
“middleware를 도입하면 안정성과 확장성이 보장된다”는 말은 성립하지 않는다. broker의 durable queue, proxy의 retry, ORM의 transaction처럼 제품별 설정과 application contract가 맞아야 원하는 성질을 얻는다.
web middleware는 request·response pipeline이다
Django 공식 문서는 middleware를 request와 response processing에 연결되는 hook framework로 설명한다. request는 등록 순서대로 안쪽 handler를 향해 들어가고 response는 반대 순서로 나온다.
request
-> request ID
-> authentication
-> rate limit
-> application handler
<- rate limit
<- authentication
<- request ID
response
middleware는 다음 middleware나 handler를 호출하지 않고 response를 바로 반환할 수도 있다. 인증 실패, cache hit, rate limit 같은 short circuit이 그 예다. 그래서 등록 순서가 단순 취향이 아니라 behavior다.
- request ID는 logging보다 먼저 만들어야 log 전체에 포함된다.
- authentication보다 authorization이 먼저 실행되면 principal이 없다.
- exception handler가 감싸지 못한 바깥 layer의 오류는 다른 형식으로 노출될 수 있다.
- response compression과 cache의 순서에 따라 cache key와 저장 representation이 달라질 수 있다.
framework middleware는 같은 process의 call chain인 경우가 많다. network를 건너는 standalone server라는 뜻은 아니다.
NGINX는 middleware인가, web server인가
NGINX는 web server이자 reverse proxy로 사용할 수 있다. NGINX ngx_http_proxy_module은 request를 다른 server로 전달하고 response buffering, header 변환, timeout 같은 동작을 설정한다.
architecture 그림에서 client와 application 사이에 있으므로 넓은 의미의 infrastructure middleware라고 부를 수는 있다. 하지만 운영 문서에서는 “middleware”보다 reverse proxy, ingress, web server처럼 실제 책임을 적는 편이 낫다. 그래야 다음 질문에 답할 수 있다.
- TLS를 어디에서 종료하는가?
- client disconnect 뒤에도 upstream request를 계속 처리하는가?
- request와 response body를 buffering하는가?
- retry할 method와 failure condition은 무엇인가?
- client, proxy, upstream timeout budget이 어떻게 이어지는가?
Apache Tomcat도 단순히 “web server middleware”라고 묶기보다 Java Servlet container와 HTTP server라는 구체적인 역할을 적어야 NGINX와의 차이를 설명할 수 있다.
message broker는 무엇을 중개하나
RabbitMQ 같은 broker는 producer와 consumer 사이에서 message를 route하고 보관하며 delivery state를 관리한다. producer가 publish call을 성공적으로 마쳤다는 사실만으로 consumer의 business processing 완료까지 보장되지는 않는다.
RabbitMQ Reliability Guide는 network-level TCP delivery와 application-level acknowledgement를 구분한다.
- publisher confirm: broker가 published message에 대한 책임을 인수했는지 producer에 알린다.
- consumer acknowledgement: consumer가 delivery를 처리했음을 broker에 알린다.
- redelivery: connection failure나 negative acknowledgement 뒤 message가 다시 전달될 수 있다.
- idempotency: at-least-once delivery에서 duplicate processing을 견디게 한다.
queue를 선언하고 basic_publish를 한 짧은 예제만으로 “reliable messaging”을 구현했다고 말할 수 없는 이유다. durability, persistence, confirm, acknowledgement, retry, dead letter, ordering requirement를 system contract에 맞게 결정해야 한다.
ORM은 database middleware인가
Hibernate 같은 ORM은 application object와 relational schema 사이의 mapping, query generation, unit of work와 persistence context를 제공한다. application과 database 사이에 있으므로 data-access middleware로 분류할 수 있지만 message broker나 reverse proxy와 같은 failure semantics를 갖지는 않는다.
ORM을 쓰면 SQL이 사라지는 것도 아니다. transaction boundary, fetch strategy, generated query, index, connection pool을 이해하지 않으면 N+1 query나 과도한 load를 만들 수 있다. 제품 category 이름보다 어떤 query가 언제 실행되고 transaction이 어디에서 끝나는지를 관측해야 한다.
middleware를 선택할 때 묻는 질문
없애려는 결합은 무엇인가
producer와 consumer의 시간 결합을 줄이려면 broker가 후보가 된다. client와 upstream topology를 분리하려면 reverse proxy가 후보가 된다. object mapping 반복을 줄이려면 ORM이 후보가 된다. 문제를 쓰지 않고 제품부터 고르면 새 운영 layer만 늘어난다.
failure semantics는 무엇인가
timeout 뒤 작업이 실제로 실패했는지, 처리됐지만 response만 잃었는지 구분해야 한다. retry가 duplicate side effect를 만들 수 있다면 idempotency key나 deduplication이 필요하다. broker라면 at-most-once, at-least-once, ordering 범위를 명시한다.
ownership은 누구에게 있는가
schema migration, certificate, queue policy, retry, dead letter, plugin upgrade, observability dashboard의 owner를 정한다. managed service를 써도 application contract와 incident response owner는 사라지지 않는다.
무엇을 관측할 것인가
latency 하나만 보지 말고 layer별 signal을 나눈다.
- proxy: upstream latency, timeout, reset, active connection
- broker: publish confirm latency, queue depth, consumer lag, redelivery
- ORM: query count, slow query, pool wait, transaction duration
- request middleware: short-circuit rate, error mapping, per-layer latency
NGINX를 포함한 web server 비교는 Apache·IIS·NGINX 차이, service 사이 protocol 선택은 HTTP부터 gRPC까지에서 이어서 볼 수 있다.
정리
middleware는 application 사이의 공통 통신·integration 기능 또는 web request·response pipeline component를 가리키는 넓은 말이다. NGINX, RabbitMQ, ORM, framework middleware를 한 제품군처럼 비교하기보다 reverse proxy, broker, data mapping, in-process hook이라는 실제 책임으로 구분해야 한다. 도입 판단은 결합도, failure semantics, ordering, ownership, observability를 기준으로 한다.
참고 자료
'배움과 성장 > 백엔드·데이터' 카테고리의 다른 글
| Spring Boot 시작이 느릴 때: CPU 제한보다 먼저 측정할 것 (5) | 2024.09.22 |
|---|---|
| BigQuery 기본 구조와 비용 관리: dataset·partition·bytes processed (0) | 2024.08.16 |
| JPA 엔티티에 protected 기본 생성자가 필요한 이유 (0) | 2023.01.30 |
| MongoDB macOS 설치와 로컬 실행: Homebrew·mongosh·보안 확인 (0) | 2023.01.03 |
| JPA 영속성 컨텍스트: 엔티티 상태·flush·commit 구분하기 (0) | 2022.12.28 |
댓글