EDA(Event-Driven Architecture)

  • 소프트웨어 아키텍처의 한 형태로, 이벤트의 발생에 기반하여 시스템, 서브시스템 또는 컴포넌트가 통신하는 방식을 말합니다. EDA는 비동기적인 이벤트 간의 통신에 중점을 두며, 이를 통해 느슨하게 결합된 시스템을 구축할 수 있습니다. 이 아키텍처는 다양한 시스템이나 애플리케이션에서 유연성, 확장성 및 반응성을 증대시키는데 유용합니다.
  • EDA의 주요 구성 요소와 특징을 살펴보겠습니다:
    1. 이벤트 프로듀서(Event Producer):
      • 이벤트를 생성하고 발행하는 엔티티입니다. 예를 들어 사용자 인터페이스의 특정 행동, 시스템 모니터링 도구, 또는 센서 등이 여기에 해당합니다.
    2. 이벤트 컨슈머(Event Consumer):
      • 발행된 이벤트를 수신하고 반응하는 엔티티입니다. 이벤트 컨슈머는 이벤트에 따라 특정 작업을 수행합니다.
    3. 이벤트 채널(Event Channel):
      • 이벤트 프로듀서와 컨슈머 사이에서 이벤트를 전달하는 매개체입니다. 이는 메시지 큐, 이벤트 버스 등이 될 수 있습니다.
    4. 이벤트(Event):
      • 시스템 내에서 발생하는 상태 변화를 나타내는 신호입니다. 이벤트는 보통 메시지 형태로 표현되며, 중요한 데이터를 포함할 수 있습니다.
  • EDA의 주요 장점은 다음과 같습니다:
    • 느슨한 결합(Loose Coupling): 시스템의 다양한 부분들이 서로 직접적으로 의존하지 않고 이벤트를 통해 통신하기 때문에, 한 부분의 변경이 다른 부분에 미치는 영향을 최소화할 수 있습니다.
    • 확장성(Scalability): 컴포넌트들이 독립적으로 작동하고, 동시에 여러 이벤트를 처리할 수 있기 때문에 시스템을 수평적으로 확장하기가 용이합니다.
    • 유연성과 반응성(Flexibility and Responsiveness): 새로운 이벤트 타입이나 프로세싱 로직을 추가하거나 수정하기 쉽고, 시스템이 실시간으로 이벤트에 반응할 수 있습니다.
  • EDA는 특히 실시간 데이터 처리, 마이크로서비스 아키텍처, 분산 시스템과 같은 환경에서 강력한 아키텍처 패턴으로 자리 잡고 있습니다.

아키텍처

  • 기술 시스템의 전체적인 구조와 구성 요소, 그리고 이들 간의 관계를 설명하는 개념입니다.
  • 소프트웨어, 하드웨어, 네트워크와 같은 다양한 기술 요소들이 어떻게 통합되어 시스템의 목표를 달성하는지에 대한 청사진을 제공합니다. 아키텍처는 시스템의 설계 원칙, 가이드라인, 표준을 포함하여, 시스템을 구성하는 데 필요한 정보와 규칙을 명시합니다.
  • IT 아키텍처는 크게 세 가지 주요 범주로 나눌 수 있습니다:
  1. 소프트웨어 아키텍처: 애플리케이션의 구조를 정의하며, 컴포넌트들이 어떻게 상호작용하는지, 시스템의 비즈니스 요구사항을 어떻게 충족시키는지에 대한 설계를 포함합니다. 이는 시스템의 품질 속성(성능, 보안, 확장성 등)을 결정하는 데 중요한 역할을 합니다.
  2. 데이터 아키텍처: 데이터 자산의 구성, 관리, 저장 방법을 설명합니다. 이는 데이터베이스 설계, 데이터 모델링, 데이터 통합, 데이터 보안 등을 포함하며, 조직이 데이터를 효율적으로 처리하고 사용할 수 있도록 합니다.
  3. 네트워크 아키텍처: 통신 네트워크의 물리적 및 논리적 구조를 정의합니다. 이는 네트워크 구성요소(라우터, 스위치, 게이트웨이 등), 연결 방식, 데이터 전송 프로토콜 등을 포함하여, 시스템 간의 통신 및 데이터 교환을 가능하게 합니다.
  • 아키텍처는 시스템의 설계 초기 단계에서 중요한 역할을 하며, 시스템 개발과 운영 전반에 걸쳐 지침을 제공합니다. 또한, 변경 관리, 기술 전략, 리스크 관리 등의 프로세스에서도 중요한 역할을 합니다.

Null 안정성(Null Safety)

  • 프로그래밍 언어 또는 프로그램에서 null 참조를 허용하지 않거나, 이를 통제하는 기능을 말합니다. null 참조는 프로그래밍에서 많은 오류의 원인이 되며, 이러한 오류를 방지하기 위해 null 안정성을 제공하는 언어와 도구가 개발되었습니다.

Null 안정성의 중요성

  • 런타임 오류 방지: 가장 흔한 오류 중 하나는 null 참조를 통한 접근 시도로 인한 NullPointerException 또는 유사한 오류입니다. Null 안정성 기능을 갖춘 언어나 도구는 이러한 종류의 오류를 컴파일 시점에 감지하고 방지하는 데 도움을 줍니다.
  • 보다 안전한 코드: null을 적절히 처리하면 프로그램의 안정성이 향상되며, 예상치 못한 오류로 인한 시스템 중단을 예방할 수 있습니다.

Null 안정성을 지원하는 언어의 특징

  • 명시적 Null 표현: 일부 언어는 null이 될 수 있는 변수와 그렇지 않은 변수를 구분합니다. 예를 들어, Kotlin 언어에서는 String?null을 포함할 수 있는 문자열 타입을 나타내고, Stringnull을 포함할 수 없는 타입을 나타냅니다.
  • Null 체크 강제: null을 허용하는 변수에 접근하기 전에 null 여부를 확인하도록 강제합니다. 이는 프로그래머가 null 체크를 간과하지 않도록 도와줍니다.
  • 안전한 호출 연산자: 예를 들어 Kotlin과 Swift에서는 ?. 연산자를 사용하여 null이 아닐 경우에만 메소드나 프로퍼티에 접근할 수 있게 합니다.
  • 엘비스 연산자(Elvis Operator): ?: 연산자를 사용해 null일 경우 대체 값을 제공할 수 있습니다.

예시: Kotlin에서의 Null 안정성

Kotlin은 null 안정성을 매우 중요하게 다루는 언어 중 하나입니다. 예를 들어, Kotlin에서 null을 허용하는 변수는 명시적으로 타입 뒤에 ?를 붙여 선언해야 합니다.

kotlinCopy codevar a: String = "Hello World"
a = null // 컴파일 오류

var b: String? = "Hello World"
b = null // 허용

이처럼 null 안정성은 프로그래밍 언어와 도구를 통해 null로 인한 오류를 예방하고, 더 안전하고 견고한 소프트웨어를 개발하는 데 도움을 줍니다.

러닝 커브(Learning Curve)

  • 새로운 지식이나 기술을 습득하는 과정에서 학습자의 성취도가 시간이나 경험의 증가에 따라 어떻게 변화하는지를 시각적으로 나타낸 그래프입니다. 일반적으로 세로축에는 학습 성과(예: 생산성, 숙련도, 정확도 등)를, 가로축에는 학습에 투입된 시간이나 경험의 양(예: 학습 시간, 생산된 단위 수 등)을 나타냅니다.

    러닝 커브의 기본 개념은 다음과 같습니다:

    • 초기 학습 속도: 대부분의 경우, 학습 초기에는 빠른 진전이 이루어지며, 이는 학습자가 새로운 지식이나 기술의 기초를 빠르게 습득하기 때문입니다.
    • 학습의 효율성 증가: 시간이나 경험이 쌓이면서 학습자는 더 효율적으로 학습하게 되며, 이는 러닝 커브가 점점 완만해지는 형태로 나타납니다. 이는 학습자가 기존에 습득한 지식을 바탕으로 새로운 정보를 더 쉽게 이해하고 통합할 수 있기 때문입니다.
    • 학습의 한계: 하지만, 어느 정도 지점 이후에는 학습의 추가적인 이득이 줄어들기 시작하며, 이는 학습자가 특정 수준의 숙련도에 도달했음을 의미합니다. 이 시점에서는 더 많은 시간과 노력을 투입해도 성과의 상당한 증가를 기대하기 어렵습니다.

    러닝 커브는 개인의 학습 효율성뿐만 아니라 조직이나 프로젝트 팀 내에서도 적용될 수 있습니다. 예를 들어, 제조 공정에서는 작업자의 숙련도가 증가함에 따라 생산 효율성이 향상되고 단위 제품당 생산 시간이 감소하는 것을 러닝 커브로 나타낼 수 있습니다.

    러닝 커브는 학습자 개인의 학습 전략을 개발하고, 교육 프로그램을 설계하며, 조직의 효율성을 개선하는 데 유용한 도구입니다. 이를 통해 학습자나 조직은 학습 과정을 최적화하고, 자원을 효과적으로 배분하며, 전반적인 학습 효과를 극대화할 수 있습니다.

non-blocking, blocking

Blocking 호출

Blocking 호출은 호출자가 API 작업의 완료를 기다리는 동안 다른 어떤 작업도 수행하지 않고 대기하는 방식입니다. 이 방식에서는 API 호출이 반환될 때까지 현재 실행 흐름이 막히게 되며, 결과가 반환되어야만 다음 작업으로 진행할 수 있습니다. 즉, 호출한 작업이 완료될 때까지 프로그램의 실행이 정지됩니다.

  • 장점: 구현이 단순하고 이해하기 쉽다. 실행 흐름이 순차적이기 때문에 로직을 따라가기가 용이합니다.
  • 단점: 리소스 활용이 비효율적일 수 있으며, 특히 네트워크 지연이나 긴 처리 시간이 필요한 작업을 호출할 때 응답성이 떨어질 수 있습니다.

Non-Blocking 호출

Non-Blocking 호출은 호출자가 작업의 완료를 기다리는 동안 다른 작업을 계속 진행할 수 있는 방식입니다. 이는 비동기(asynchronous) 방식으로도 알려져 있으며, API 호출이 즉시 반환되고, 실제 작업은 백그라운드에서 계속 처리됩니다. 작업 완료 여부는 콜백 함수, 프로미스(Promises), 미래(Futures), 이벤트 등을 통해 나중에 알림을 받을 수 있습니다.

  • 장점: 리소스를 효율적으로 활용할 수 있으며, 특히 I/O 작업이 많거나, 응답 시간이 중요한 애플리케이션에서 높은 응답성을 유지할 수 있습니다.
  • 단점: 구현이 복잡할 수 있으며, 비동기 로직의 관리가 필요합니다. 콜백 지옥(callback hell)과 같은 문제가 발생할 수 있으며, 상태 관리와 오류 처리가 더 어려울 수 있습니다.

요약

  • Blocking: 호출자가 결과를 기다리는 동안 다른 작업을 수행하지 않음. 구현이 단순하지만 리소스 활용이 비효율적일 수 있음.
  • Non-Blocking: 호출자가 결과를 기다리는 동안 다른 작업을 계속 수행할 수 있음. 리소스를 효율적으로 활용할 수 있지만 구현이 복잡할 수 있음.

Project Reactor

  • Project Reactor는 자바(JVM)를 위한 비동기, 논블로킹 프로그래밍을 위한 라이브러리입니다. 이는 리액티브 스트림(Reactive Streams) 사양을 구현하여, 데이터 스트림의 비동기 처리 및 백프레셔(Backpressure)를 지원하는 효율적인 방법을 제공합니다. Project Reactor는 Spring Framework 5와 통합되어 Spring WebFlux와 같은 비동기 웹 애플리케이션을 구축하는 데 사용됩니다.

    핵심 개념

    • 리액티브 프로그래밍: 이벤트 기반의 비동기 프로그래밍 패러다임으로, 데이터 스트림과 변화의 전파에 중점을 둡니다. 이를 통해 응답성이 높고, 탄력적이며, 유지보수가 용이한 시스템을 구축할 수 있습니다.
    • 리액티브 스트림: 비동기 데이터 스트림 처리를 위한 표준으로, 네 가지 기본 인터페이스(Publisher, Subscriber, Subscription, Processor)를 제공합니다. 이 사양은 데이터 스트림의 생성, 전송 및 소비 과정에서 백프레셔를 지원합니다.
    • 백프레셔(Backpressure): 생산자(Producer)와 소비자(Consumer) 간의 데이터 처리 속도 차이를 관리하는 메커니즘으로, 소비자가 처리할 수 있는 속도를 초과하는 데이터의 전송을 방지합니다.

    주요 컴포넌트

    • Flux: 0개에서 N개의 아이템을 비동기적으로 방출할 수 있는 리액티브 타입입니다. 다수의 데이터 아이템 처리에 적합합니다.
    • Mono: 0개 또는 1개의 아이템을 비동기적으로 방출할 수 있는 리액티브 타입입니다. 단일 값 또는 비동기 작업의 결과를 처리하는 데 사용됩니다.

    사용 사례

    • 비동기 웹 애플리케이션: Spring WebFlux와 함께 Project Reactor를 사용하여, 높은 동시성과 대규모 트래픽을 처리할 수 있는 비동기 웹 애플리케이션을 구축할 수 있습니다.
    • 데이터 스트림 처리: 다양한 데이터 소스(데이터베이스, 메시지 큐 등)에서 발생하는 데이터 스트림을 효율적으로 처리하고, 변환, 필터링, 집계 등의 연산을 비동기적으로 수행할 수 있습니다.
    • 마이크로서비스 간의 비동기 통신: 서비스 간의 비동기 메시징 패턴을 구현하여, 시스템의 전체적인 응답성과 탄력성을 향상시킬 수 있습니다.

    결론

    Project Reactor는 리액티브 프로그래밍을 자바 애플리케이션에 적용하기 위한 강력하고 유연한 도구입니다. 비동기적이고, 논블로킹인 데이터 스트림 처리를 통해 고성능 및 고응답성 애플리케이션 개발을 가능하게 합니다. Spring Framework와의 긴밀한 통합을 통해, 현대적인 웹 애플리케이션과 마이크로서비스 아키텍처를 구축하는 데 있어 선택할 수 있는 주요 옵션이 되었습니다.

Project Reactor의 배압

  • Project Reactor는 자바(JVM)를 위한 비동기 프로그래밍 라이브러리로, 리액티브 프로그래밍 패러다임을 따릅니다. 이 라이브러리는 Spring WebFlux와 같은 스프링 프레임워크의 일부분으로 통합되어, 비동기 스트림 처리를 위한 강력한 기능을 제공합니다. Project Reactor에서의 배압(Backpressure)은 중요한 개념 중 하나로, 리액티브 스트림에서 데이터 흐름을 제어하는 메커니즘을 말합니다.

    배압(Backpressure)이란?

    배압은 리액티브 시스템에서 생산자(Producer)와 소비자(Consumer) 간의 데이터 흐름을 조절하는 방법입니다. 생산자가 소비자보다 빠르게 데이터를 생성할 때, 소비자가 처리할 수 있는 속도를 초과하지 않도록 조절하는 것이 중요합니다. 배압은 소비자가 현재 처리할 수 있는 데이터의 양을 생산자에게 알려줌으로써, 시스템 내에서의 과부하를 방지하고, 리소스를 효율적으로 사용할 수 있도록 돕습니다.

    배압의 작동 원리

    Project Reactor에서는 FluxMono와 같은 리액티브 타입을 통해 데이터 스트림을 처리합니다. 배압은 소비자가 request(n) 메서드를 통해 “나는 현재 n 개의 데이터만 처리할 수 있다”고 생산자에게 알리는 방식으로 작동합니다. 이 정보를 바탕으로 생산자는 소비자가 처리할 수 있는 만큼의 데이터만을 전송하게 됩니다.

    배압의 중요성

    • 리소스 보호: 배압 없이 데이터가 무제한으로 소비자에게 전송될 경우, 소비자는 과부하 상태에 빠질 수 있습니다. 이는 시스템의 안정성과 성능에 심각한 영향을 줄 수 있습니다.
    • 유연한 데이터 흐름 제어: 배압 메커니즘을 통해 소비자는 현재의 처리 능력에 맞춰 데이터를 받을 수 있으며, 생산자와 소비자 간의 속도 차이를 유연하게 관리할 수 있습니다.
    • 효율적인 리소스 활용: 배압을 통한 흐름 제어는 네트워크 대역폭, 메모리 사용량 등 시스템 리소스의 효율적인 사용을 가능하게 합니다.

    결론

    Project Reactor와 같은 리액티브 프로그래밍 라이브러리에서 배압은 데이터 스트림의 안정적이고 효율적인 처리를 위해 필수적인 메커니즘입니다. 이를 통해 애플리케이션은 높은 부하 상황에서도 높은 성능과 안정성을 유지할 수 있습니다.

참고

  • ChatGPT-4.0

보완/복습

  • 2024.01.10 생성
  • 2024.02.06 보완