서비스 개발에 적용된 복잡한 도메인문제를 해결하기 위해 적용한 액터모델 사용사례를 소개합니다.

이미 작동중인 액터모델로 작성된 코드를 LLM에게 분석의뢰를 시도해 코드의 가치를 다시 발견하기 위해 시도되었습니다.

액터모델 적용된 서비스 간략소개

  • 3천개 이상의 롱테일 구간 마케터담당자 분들이 이용중입니다. 
  • 카페24앱을 통해 출시되었으며~ 카카오 광고발송 매체를 이용합니다.


사용 기술을 소개하기전 이 기술이 이용된 주요 도메인 단어를 먼저 소개하고 이어가보겠습니다.

우리는 도메인이 부족한 상태에서  그것을 함께 학습해가며 완성해 나갔으며 작성된 코드를 설명하기전~ 도메인의 이해를 위해 관련

광고플랫폼에 사용되는 도메인 용어를 먼저 알아보겠습니다. 


선거 캠페인과 광고캠페인 차이점 설명해

  • GPT에 의뢰한 사용된 프롬프트는 이 Style이 적용되었습니다.

캠페인

미국의 선거 캠페인광고 캠페인은 매우 다른 목적을 가지고 있지만, 사용하는 전략이나 기법에서는 많은 유사점이 있습니다. 미국에서는 이 두 영역이 서로 영향을 주고받으며 진화해왔고, 특히 광고 마케팅 기법이 정치 캠페인에 깊이 스며들면서 현대 정치의 얼굴을 바꾸었다고 할 수 있습니다.


🎯 1. 목적(Purpose)


항목선거 캠페인광고 캠페인
목표유권자의 투표 행동 유도소비자의 구매 행동 유도
핵심 결과득표수판매량 또는 브랜드 인지도

🧠 2. 전략(Strategy)


항목선거 캠페인광고 캠페인
타겟팅유권자 세분화 (스윙보터, 지지층 등)소비자 페르소나, 시장 세그먼트
메시지슬로건, 공약 중심 감정 호소 ("Hope", "Make America Great Again")제품 가치 제시, 라이프스타일 연결
감정 활용공포, 희망, 분노, 공감 등욕망, 공감, 소속감 등
채널TV, 라디오, 소셜미디어, 직접 유세TV, 디지털, 옥외광고, 스폰서십 등
데이터 활용유권자 등록 데이터, 여론조사, 디지털 행동 분석CRM 데이터, 구매 이력, 디지털 행동 분석

🧬 3. 유사점(Similarities)

  • 브랜드 구축: 정치인도 하나의 브랜드처럼 관리됨 (캐릭터, 이미지, 슬로건)

  • 스토리텔링: 캠페인의 서사 구조가 중요 (출신 배경, 투쟁 스토리 등)

  • 심리적 접근: 모두 감성적 설득에 의존하는 경우가 많음

  • A/B 테스트와 실험: 메시지나 타이밍을 반복 실험


🧨 4. 차이점(Differences)


항목선거 캠페인광고 캠페인
기간 제한선거 일정에 맞춰 진행 (짧고 집중적)지속적 마케팅 활동
규제 강도선거법, 기부 제한, 공정성 요구기업 윤리 기준, 광고 심의 수준
신뢰 이슈사실 여부와 공약 이행이 주요 이슈과장 허용 범위 다소 넓음 (비교 광고 등)
승패 구조승자독식 구조 (1등만 의미 있음)다수 브랜드가 공존 가능

🕰️ 5. 유례(Historical Influence)

미국 정치 캠페인은 광고 산업과 밀접하게 진화해 왔습니다.


시대정치 캠페인 혁신광고 마케팅과의 연결
1952년아이젠하워 TV 광고 ("I Like Ike")광고 대행사 Rosser Reeves가 직접 제작
1960년케네디 vs 닉슨 TV 토론"보는 이미지의 힘"을 각인시킴
1984년레이건 캠페인 “Morning in America”희망적 내러티브로 브랜드화
2008년오바마 디지털 캠페인SNS + 빅데이터 마케팅 기법 도입
2016년트럼프의 트위터 전략브랜드화된 정치인의 SNS 활용 극대화

🔍 요약 한 줄 정리

미국의 선거 캠페인은 마치 브랜드 마케팅처럼 정교하고 감성적으로 설계되며, 광고 산업과의 융합을 통해 더욱 강력한 설득력을 갖추게 되었다.


어트리뷰션

이 광고 플랫폼을 만들때 어려웠던 파트중 하나를 뽑으라면 어트리뷰션이지 않았을가? 광고플랫폼이  성과가 좋았는지?

ROI 지표를 고객에게 제공하지 못한다면 지속이용이 불가능합니다. ROI지표를 만들때 주로 사용되는 기법이 직접/간접 기여도를 구분해 알고리즘을 만드는것으로

기여도라는 한글단어보다 어트리뷰션이라는 용어를 보편적으로 사용합니다.

  • 특정 결과의 원인을 다른 집단이나 요소에 귀속시키는 것을 의미함.

  • 예시로 BTS가 상을 받았을 때, 팬클럽 '아미'에게 그 영광을 돌리는 발언: 논란이기도하고 유행이기도한 BTS 수상장면을 지브리풍으로 그림

    “We’d like to attribute this glory to all armies"

    📌 디지털 마케팅에서의 어트리뷰션

    • 하나의 결과(예: 구매)에 여러 원인이 영향을 줄 수 있음.

    • 각각의 원인에 기여도를 나눠서 분배하는 것이 핵심.

    • 예를 들어, 한 고객이 상품을 구매했다면, 그 구매에 영향을 준 여러 마케팅 채널들(광고, 이메일, 검색 등)에 기여도를 정량적으로 부여함.


    🧩 요약 키포인트

    • BTS 사례: 팬들에게 영광을 귀속시키는 어트리뷰션.

    • 마케팅 적용: 결과에 영향을 준 요소에 기여도 기반 분배


이것을 런칭하고 런칭회고를 하고나니 비단 광고플랫폼에서만 활용되는것이 아닌 성숙한 개발문화가 되기위해서도 사용되어야할 중요 단어인것같습니다.

A : 당신(팀) 덕분에 우리는 함께 성공할수 있었습니다.

B : 당신(팀) 때문에 우리는 런칭일이 연기되었습니다.

전자와 후자는 개발성숙도와 관련된 케이스로 팀 사일로가 없고 성숙한 개발문화를 가진 팀은

주로 전자적인 이야기를 하는것에 익숙합니다. 내부경쟁이 심하고  성숙하지 못한 개발문화가 작동되는곳은 주로 B를 이야기합니다. 

어트리뷰션의 영향이 팀또는 협업자에게 있을때  ~덕분에 가 아닐까?


활용 IDE

  • 코파일럿 플러그인이 이용되었으며 전체를 분석하기위해 MSA로 구성된 프로젝트를 멀티모듈로 모았습니다.  ( InteliJ + 코파일럿 )


📋 프로젝트 구성 요약과 MSA로 도전

서비스명

역할 요약

service

사용자 프론트에 필요한 기능을 제공하는 API

CommandCenter

발송 스케줄링, 도메인 명령 트리거 처리

collector

회원정보, 장바구니 등의 원시 데이터 수집

webhook

외부 이벤트 수신 (광고조회, 클릭 등)

upload

수집 데이터를 안전하게 저장

recoder

이벤트를 저널 형태로 DB 기록

analyst

사전 세그먼트 분석 (배치 기반)

processor

Elasticsearch 기반 실시간 세그먼트 필터전처리기

distributor

발송 대상을 적절히 분배

sender

발송 실행, 실패 재시도, 비용 정산 처리

postman

단순 채널별 전송 전담 (DB 미사용)

admin

내부 운영툴 (지표 확인, 정산 관리 등)

  • 생각보다 많은 기능서비스가 존재합니다. ( 이 기능분류 역시 GPT에 의뢰후 정리시도)
  • 로컬 올인작동및 디버깅이 모놀리식에서 원래 가능했던 부분이지만 MSA 구성을 시도하면서 파편화가 되고 한방작동 방법을 잃게되는 순간 MSA가 아닌 파편화 시스템이 되버립니다. 
  • 도커의 도움을 받아 Local All In 디버깅환경을  먼저 구성하였습니다. - MSA구성이 파편화시스템이 안되게 하기위해서는 IaC와 함께 중요한부분입니다. 
  • MSA 아키텍처를 통해 클린아키텍처로 구현한다와~ MSA의 구성요소로 분리되었다는 다른개념으로 가끔 이것을 구분하지 못해 MSA를 잘못해석하는 경우가 있습니다. 여기서 소개되는 어플리케이션 구조는 Kafka 중심으로 MSA 요구하는 적합한 구성요소로 분리구성 시도가 되었다로 설명하는것이 맞으며 Java코드로 작성되었습니다. 이것이 MSA냐 아니냐?의 논쟁은 소모적일수 있으며 MSA는 아키텍처는 완성된 그자체가 아니라~ 디벨롭된 공간과 개발문화가 함께 지속 성장하면서 지속도전해야하는 여지로 남겨둡니다. 그러한 관점에서 모놀리식에서 MSA로 한방에 가는것이 아닌 중간 단계인 SOA가 존재하고 이것을 이해하는것은 중요하며 초기 국내IT 대부분은 MSA보다는 전자방식을 통해 성공해왔으며 국내 써밋에서의 MSA의 성공사례는 나이스하지만~ 알려지지 않은 실패사례가 훨씬더 많을것으로 추정해봅니다.



광고플랫폼으로 각 저장소별기능은 그림과 같습니다. 이것은 MSA의 구성요소관점에서 적합한지? 또는 개선사항이 없는지 분석해?

✅ 현재 구조의 장점 요약


항목설명
역할 분리 우수각 서비스는 단일 책임 원칙(SRP)을 따르며, 기능별로 잘 분리되어 있음
ETL/분석 흐름 있음collector → recoder → analyst → processor 라는 전형적인 데이터 파이프라인 흐름이 존재
확장성 고려distributor, sender, postman 등 발송단을 세분화하여 부하 분산에 유리
Elastic 기반 필터 처리실시간 타겟팅/세그먼트 처리를 위해 processor에 Elasticsearch 도입은 적절함
비용 처리까지 고려

sender에 비용 정산 로직 포함되어 있어 SaaS형 광고 플랫폼에 

✅ 개선 요약


항목현재 상태개선 제안
이벤트 흐름일부 동기 호출 가능성Kafka 등 메시징 기반 비동기 구조
명명 규칙recoder 등 오탈자 존재recorder 등 명확한 명칭 정비
세그먼트 분석배치/실시간 나뉨명확한 소스/목적 구분 필요
운영툴내부 확인 용도만 명시관측/로깅/보안 기능 분리 고려
워크플로우 관리CommandCenter 단일복잡도 따라 Workflow Engine 고려 가능



액터모델이  다양한 요소에 적용되었지만 다음 3가지 요소에 적용된 액터모델을 분석 의뢰 해보았습니다.

작업자는 팀에서 부르는 영어닉을 함께 언급하였습니다.

  • 쿠폰생성기
  • 수집처리기
  • 분산저장 처리기


쿠폰생성기

  • 작업자 : 주디

  • 소개 : 커머스 API의 쿠폰처리부분의 제공 TPS가 극단적으로 적은수준으로 제공되 실패없이 호출하려고 적용되었습니다. 커머스  API중 금액관련 API는 대부분 몰별 TPS가 1이하의 제약이 있습니다. 이 제약을 고려하지 않으면 서비스 API는 장애를 뿜어내게 됩니다. 동시성이란? 한꺼번에 여러개를 처리할수 있는 처리능력도 중요하지만 그것보다 오류없이 안정적으로 제어를 하는영역이 더많은 기술스킬을 요구하게됩니다.



CreateCouponActor.java 와 CreateCouponRouterActor.java를 분석해 이 기능을 요약설명한후, 활용된부분중에서 액터모델의 특징도 함께 설명하고

다이어그램도 그려



🎯 기능 개요

CreateCouponActorCreateCouponRouterActorAkka Actor 모델을 기반으로 쿠폰 생성과 메타데이터 업데이트를 비동기 병렬 처리 방식으로 수행합니다. 이 구조는 확장성과 성능을 고려하여 설계되었습니다.


🧩 주요 컴포넌트

🧱 CreateCouponActor

  • 역할: 단일 쿠폰 생성 및 메타데이터 저장 처리

  • 동작 흐름:

    1. CouponMessage 수신

    2. Cafe24CouponService를 통해 쿠폰 생성 DTO 획득

    3. 쿠폰 생성 → 결과 정보 업데이트

    4. CouponMetaDao를 통해 쿠폰 메타데이터 저장 (유효기간, 사용자 정보 등)


🧱 CreateCouponRouterActor

  • 역할: 여러 쿠폰 생성 요청을 라우팅하여 병렬 처리

  • 동작 흐름:

    1. CouponListMessage 수신

    2. 각 버튼의 쿠폰 정보를 파악

    3. 적절한 액터(createCouponRouter, updateCouponMetaRouter)로 메시지 분배

    4. RoundRobinPool을 이용한 액터 간 균등 분배


⚙️ 아키텍처 특징 및 이점

✅ 비동기 메시지 기반 처리

  • 모든 메시지는 비동기로 처리되며, 액터 간 직접 상태 공유 없이 동작

  • CreateCouponRouterActor는 버튼별 메시지를 생성하여 하위 액터에게 전달

✅ 병렬 처리 및 부하 분산

  • RoundRobinPool로 액터 인스턴스들을 구성해 균등한 작업 분배 수행

  • 쿠폰 생성 요청이 많을수록 병렬 처리의 이점이 극대화됨

✅ 상태 캡슐화

  • 각 액터는 자체 상태만을 관리하고 외부로부터 직접 접근을 차단

  • 예: CreateCouponActorCafe24CouponService만을 통해 로직 수행

✅ 수평 확장 용이

  • 액터 수 조정만으로 부하 대응 가능 → 시스템의 확장성 강화

  • 별도의 락(lock)이나 동기화 없이도 안정적으로 확장 가능


🧾 요약

CreateCouponActorCreateCouponRouterActor는 Akka의 액터 모델을 활용해 비동기 처리, 병렬성, 상태 격리, 확장성을 모두 만족시키는 구조로 쿠폰 생성 및 메타데이터 관리를 효과적으로 수행합니다. 이 구조는 높은 트래픽 환경에서도 안정적인 성능을 보장합니다.


Api 호출의 제약하는 AkkaStream Throttle기능에대해서도 분석해서 특징요약

🚦 Akka Stream - Throttle 기능 개요

Akka Stream의 Throttle데이터 스트림의 처리 속도를 제어하는 데 사용되며, 외부 시스템(API 등)과의 연동 시 요청 과부하를 방지하고 시스템 안정성을 높이는 데 유용합니다.


🧩 주요 특징 요약

⏱️ 1. 처리 속도 제한

  • 초당 또는 분당 요청 수를 제한할 수 있습니다.

  • ✅ 예: 초당 10건의 API 호출만 허용

throttle(elements = 10, per = 1.second)


⚡ 2. 버스트 처리 (Burst)

  • 일정 시간 내 순간적으로 허용할 수 있는 최대 요청 수를 설정할 수 있습니다.

  • ✅ 예: 초당 10건 허용 + 최대 20건까지 순간 처리 허용

throttle(10, 1.second, 20, ThrottleMode.Shaping)


📉 3. Backpressure 지원

  • Throttle은 백프레셔(Backpressure) 메커니즘을 통해 처리 속도가 제한될 경우 자동으로 흐름을 늦춥니다.

  • 이로 인해 다운스트림 소비자가 처리 가능한 속도에 맞춰 과부하 없이 처리 가능


⚙️ 4. 정책 모드 설정 (ThrottleMode)

  • Shaping: 요청을 자동으로 지연시켜 목표 속도에 맞춥니다 (성능 최적화에 적합)

  • Enforcing: 제한을 초과하면 스트림을 실패 처리합니다 (보안이나 트래픽 엄수 시 적합)

ThrottleMode.Shaping // 유연하게 조절 ThrottleMode.Enforcing // 엄격히 제한


🧾 요약

Akka Stream의 Throttle은 다음과 같은 상황에서 필수적인 기능입니다:

  • 외부 API 호출 시 속도 제어가 필요한 경우

  • 버스트 요청을 일정 한도로만 허용하고 싶은 경우

  • 스트림 기반 시스템에서 과부하 방지 및 안정성 확보가 필요한 경우

Throttle은 Akka Stream의 백프레셔와 결합되어, 유연하고 확장 가능한 데이터 흐름 제어를 가능하게 합니다.


카페24 수집처리기

  • 작업자 : 크리스(서포트:쌤)

  • 소개 : 세그멘테이션을 구하기위해서는 카페24에서 발생하는 다양한 이벤트를 사전수집하게 됩니다. 7천개의 업체가 가입을 했으며 , 단 3대의 수집처리기로 액터모델을 이용 분산처리를 합니다. 이러한 분산처리를 제어하지 못하면 엄청난 수의 장비가 필요로 할수도 있습니다. - total : 수집처리기 3대 미만의 장치서버로만 운영


Cafe24ProcessorActor,ApiWorkActor,MallWorkActor 을 요약설명하고 어떻게 상호작용하는지 다이어그램으로 그리고 여기서 활용된 Akka액터의 기능도 소개해



☕ Cafe24 데이터 수집 액터 구조 요약

Cafe24ProcessorActor, MallWorkActor, ApiWorkActorAkka 기반 계층형 액터 모델을 활용하여 Cafe24 API 데이터 수집을 병렬적이고 효율적으로 처리합니다.


🧩 구성 요소별 역할

🏗️ Cafe24ProcessorActor

  • 전체 수집 흐름을 총괄하는 상위 액터

  • MallWorkActor, ApiWorkActor를 생성 및 관리

  • 데이터 수집 요청 분배 및 결과 통합 처리

📌 주요 책임: 수집 워크플로우 관리, 액터 간 메시지 중계


🏬 MallWorkActor

  • 쇼핑몰 단위 Mall ID 별로 데이터를 수집

  • 내부적으로 ApiWorkActor를 활용해 API 요청 생성

  • 수집된 데이터를 Kafka로 전송하거나 후속 액션 수행

📌 주요 책임: Mall별 API 호출 분배, 데이터 수집 로직 실행


🌐 ApiWorkActor

  • 실제로 Cafe24 API를 호출하는 실행 단위 액터

  • 에러를 처리하고 응답 데이터를 MallWorkActor로 전달

  • 일부 에러에 대해 재시도 로직 수행

📌 주요 책임: API 호출, 에러 감지 및 재시도, 결과 반환


⚙️ Akka 기능 활용 포인트

📬 비동기 메시지 기반 통신

  • 액터 간 메시지는 비동기 전송으로 병렬 처리 효율 증대

throttler.tell(apiRequestMessage, ActorRef.noSender())


🔁 라우팅 (RoundRobinGroup)

  • MallWorkActorApiWorkActor 인스턴스에 요청을 라운드로빈 방식으로 분산

RoundRobinGroup(apiWorkerPaths)



⏱️ 스로틀링 및 스케줄링

  • API 호출 빈도 제어를 위해 초당 처리량 제한 설정

throttle(processCountPerSec, 1.second)



🔄 에러 처리 및 재시도

  • ApiWorkActor는 특정 에러에 대해 조건부 재시도

handleErrorResponse(apiResponse)



🧱 계층적 액터 모델

  • Cafe24ProcessorActorMallWorkActorApiWorkActor로 이어지는 명확한 책임 분리

  • 유지보수성과 확장성 확보에 유리한 구조


🧾 요약

이 구조는 Akka의 강력한 비동기 메시징, 병렬성, 스로틀링, 에러 복원력, 계층적 구조를 적극 활용하여 Cafe24 API 기반 데이터 수집을 안정적이고 효율적으로 수행할 수 있도록 설계되었습니다.


전체 작동 Flow

  • 최초 한땀한땀 설계한 시퀀스 다이어그램과 작동 코드를 일치시킴

위 그림을 다시 가독성이 좋게 그리고 구성별 역할별 분석

✅ 전체 시나리오 요약

Kafka에서 수집 명령(CollectorCMD)이 발행되면, 이를 ProcessorActor가 수신하여 작업 단위를 그룹핑 및 분배하고, MallWorkActorApiWorkActor를 통해 API 호출을 처리하며, 작업 완료 여부를 검증 후 상태 업데이트 및 결과 업로드까지 담당하는 비동기 분산 작업 처리 흐름입니다.


🧱 구성 요소별 역할 분석

1. Kafka

  • 역할: 이벤트 발송 트리거

  • 기능: CollectorCMD 메시지를 발행하여, 수집/처리 작업을 시작하는 기점

  • 특징: 분산 메시지 브로커로서 ProcessorActor에 메시지를 전달


2. ProcessorActor

  • 역할: 중심 컨트롤러 역할의 액터 (집중 분배 및 상태 제어)

  • 기능:

    • CollectorCMD 수신

    • 채널/작업 단위로 그룹핑 (e.g. Mall 단위)

    • 다음 작업 존재 여부 확인 (HasNext)

    • 작업 시도 (Retry 최대 3회까지)

    • 결과 수신 후 StateUpdate작업결과 UPLOAD

  • 특징:

    • 상태기반 흐름 제어

    • Stream + Retry 패턴 내장

    • 분산 병렬 처리를 위한 분배기능 수행


3. MallWorkActor

  • 역할: 중간 그룹 단위 분배 및 Throttle 조절

  • 기능:

    • 메시지 발행기(MessagePublisherService)를 통해 각 API 작업을 라우팅

    • TPS 제한 설정 (Throttler 3TPS)

    • 라운드로빈 방식으로 ApiWorkActor에 작업 분배

  • 특징:

    • 속도 제한 및 부하 분산의 핵심 지점

    • TPS 조절로 백엔드 API 부하 보호


4. ApiWorkActor

  • 역할: 실제 API 호출 담당

  • 기능:

    • 외부/내부 API 호출 처리

    • 결과를 다시 MallWorkActor로 반환

  • 특징:

    • 단순 호출 전담 (Stateless)

    • TPS 3에 맞춰 분배된 요청만 처리


5. MessagePublisherService

  • 역할: 내부 메시지 브로커(로컬 또는 서드파티)

  • 기능: MallWorkActorApiWorkActor로 메시지를 라우팅할 때 사용

  • 특징:

    • Actor 간 직접 메시지 전달이 아닌 메시지 큐/버퍼링을 활용



보너스 아티컬 - 동일주제로 다른언어로 코드생성 시도

코드생성이 이 아티컬의 주목적은 아니였으나 시도해보았습니다.

코드구현 자체가 학습구현이 높아 도입하지 못한 고급패턴들의 학습및 도입에 가속화를 가져올것으로 기대합니다. - 언제까지 반복적인 CRUD만 우아하게 개발하는 패턴만을 한평생 할것인가?

위 그림만 첨부해 다음을 시도해보았습니다. - 아키텍처입장에서 코그생성을 요청


위 다이어그램을 분석해 ~ 그리고 나는 이것을 Pyhon 액터프레임워크인 Ray를 이용하고 싶음 핵심 코드만 구현하고 설명해봐 도입할지는 내가 판단함

import ray
import asyncio
import random

ray.init()

@ray.remote
class ApiWorkActor:
    async def call_api(self, data):
        await asyncio.sleep(0.5)
        return {"status": "success", "data": data}

@ray.remote
class MallWorkActor:
    def __init__(self, api_actors):
        self.api_actors = api_actors
        self.retry_limit = 3
        self.tps = 3
        self.current_index = 0

    async def process_group(self, task_group):
        results = []
        sem = asyncio.Semaphore(self.tps)

        async def call_with_throttle(task, attempt=0):
            async with sem:
                try:
                    actor = self.api_actors[self.current_index]
                    self.current_index = (self.current_index + 1) % len(self.api_actors)
                    result = await actor.call_api.remote(task)
                    return await result
                except Exception as e:
                    if attempt < self.retry_limit:
                        return await call_with_throttle(task, attempt + 1)
                    else:
                        return {"status": "failed", "data": task}

        for task in task_group:
            result = await call_with_throttle(task)
            results.append(result)

        return results

@ray.remote
class ProcessorActor:
    def __init__(self, mall_actor):
        self.mall_actor = mall_actor

    async def handle_collector_cmd(self, cmd):
        print(f"📦 수신한 CollectorCMD: {cmd}")
        task_group = cmd.get("tasks", [])
        results = await self.mall_actor.process_group.remote(task_group)
        print(f"✅ 전체 결과: {results}")
        return "완료"

# 액터 인스턴스 생성
api_actors = [ApiWorkActor.remote() for _ in range(3)]
mall_actor = MallWorkActor.remote(api_actors)
processor = ProcessorActor.remote(mall_actor)

# 실행
async def main():
    cmd = {"tasks": [f"task-{i}" for i in range(10)]}
    await processor.handle_collector_cmd.remote(cmd)

asyncio.run(main())
  • 닷넷과 자바가 컴파일기반 유사한 언어로 분류하기도하는데 비동기 병렬처리 파트에서 async/tpl 과 같은 개념이 자바에서는 completeFuture/ForkJoin ThreadPool 대체되는 부분으로 개발스타일이 크게 다르게됩니다. 오히려 닷넷의 async와 코틀린/파이썬의 코루틴기반 async와 사용법은 유사하며 자바만 독자적인 체제를 가지고 있습니다.

  • 참고로 필자도 ray를 python기반에 구현을 못해보았으며~액터모델이 복잡한 동시성 문제를 언어구분없이 동일컨셉으로 해결해 가는지를 확인해보기위해 시도 되었습니다.


🧠 핵심 포인트 설명

구성요소

설명

ApiWorkActor

외부 API 호출 처리, 실패 시 예외를 통해 MallWorkActor가 Retry 수행

MallWorkActor

TPS 제한 (Semaphore)과 RoundRobin 방식으로 ApiActor 선택 후 호출

ProcessorActor

Kafka에서의 메시지를 수신한 것처럼 동작하며, 그룹 작업을 Mall에 위임

asyncio + ray

Throttle + 비동기 메시지 전달 구현을 자연스럽게 표현 가능


분산 적재처리기

  • 작업자 :이안

  • 소개 : 다양한 처리기에서 발생시킨 이벤트를 DB에 안전하게 적재하고 NextStep이 진행될수 있게 유도하며 SpringBoot와 통합해 배치처리기의 표준 SptringBatch와 상호작용해 데이터를 긴기주기의 배치가 아닌 부분즉각 처리를 시도합니다. - 이러한 NearRealTime 패턴을 사용하는경우 긴주의 배치가 요구되는 최상급 DB가 필요로 없게되며 DB를 CPU 버스터없이 운영할수 있기때문에 DB가 무한대로 스케일업해서 비용이 증가하는것을 방지할수 있습니다.

DistributorRecordActor DistributorRecordRouterActor MainJobActor EndProcessActor EndProcessRouterActor StartEndProcessMessageMaker 를 분석해서 기능을 설명해~

이용된 컨셉도 설명하고 다이어그램도 그려



  • 액터모델은 계층형으로 설계할수 있으며 다이어그램이 설명하려는 계층과도 일치해 작동하는 특징이 있습니다.

    • 스프링부트의 자동 DI에 익숙하다고하면 어색한 영역이 될수도 있습니다. DI는 보통 라이프사이클만 정의하고 계층형으로는 구성할수 없습니다.

      • 라이프사이클이 긴객체(싱글톤)가 ~ 더 짤은 수명(트랜잭션Scop)을 가진 사이클을 멤버에 가질수(Has) 없습니다. -스프링부트를 이용하다보면 사소한것처럼 보이지만 잘 헛갈리는 AutoWired영역역

        • 더 짧은 수명을 가진 객체를 이용하려면 DI가 아닌 함수호출시점 객체를 생성하거나 생성된 객체를 전달해야합니다.


📦 Akka 기반 작업 분산 처리 구조 요약

이 시스템은 Akka Actor 모델Spring Batch, Kafka를 통합하여 대규모 작업 처리와 종료 프로세스를 안정적으로 수행할 수 있도록 설계되었습니다.


🧩 클래스별 역할 정리

🎯 DistributorRecordActor

  • HistoryDetailMessage를 수신하여 다음 동작 수행:

    • HistoryDetailDistributor 타입 여부 확인

    • DB에 삽입 → insertHistoryDetailDistributor

    • Kafka 메시지 전송 → sendKafkaMessage

    • 작업 완료 후 부모에게 FinishDistributorRecord 메시지 전달

📌 주요 책임: 히스토리 기록 + Kafka 발행


🔁 DistributorRecordRouterActor

  • DistributorRecordActor라우팅 전담

  • RoundRobinRoutingLogic을 사용하여 메시지를 균등 분산

  • FinishDistributorRecord 메시지를 수신 → 작업 종료 트리거

📌 주요 책임: 라우팅 처리 + 종료 이벤트 감지


🧪 MainJobActor

  • Kafka의 Topic/Partition 정보를 기반으로 Spring Batch 작업 실행

    • startJob, endJob 메서드로 작업 관리

  • 실행 상태(Shutdown 등) 확인 → 중복 실행 방지

📌 주요 책임: 배치 작업 스케줄링 및 상태 제어


🛑 EndProcessActor

  • NotificationEnd 메시지 수신 시 작업 종료 로직 실행

  • 완료 후 부모에게 FinishEndProcess 메시지 전달

📌 주요 책임: 종료 시나리오 실행


🚦 EndProcessRouterActor

  • EndProcessActor 라우팅 전담

  • RoundRobinRoutingLogic 사용하여 작업 종료 메시지를 분산

  • 작업 실행 여부를 검사 → 실행 중인 작업은 스킵

📌 주요 책임: 종료 메시지 분산 + 중복 실행 방지


🧾 StartEndProcessMessageMaker

  • 다양한 작업 종류(Send, Analyst, Collector 등)에 대해 StartEndProcessMessage를 생성

  • 생성된 메시지를 EndProcessRouterActor로 전달

📌 주요 책임: 종료 프로세스 시작 메시지 생성 및 발송


🔧 사용된 핵심 개념

🎭 Akka Actor 모델

  • 비동기 메시지 처리 기반 병렬 아키텍처

  • 각 액터는 상태를 캡슐화하고 독립적으로 동작


🌱 Spring + Akka 통합

  • Spring의 DI를 활용하여 액터 생명주기 및 의존성 관리

  • Akka의 유연한 메시지 처리와 결합해 확장성과 테스트 용이성 확보


🔄 라우팅 (Routing)

  • RoundRobinRoutingLogic 사용 → 작업 부하 균등 분산

  • DistributorRecordRouterActor, EndProcessRouterActor가 주요 라우터로 작동


📡 Kafka 연동

  • Kafka를 통해 외부 시스템과의 데이터 송수신 처리

  • 메시지 기반 분산 처리에 적합


🧵 Spring Batch

  • MainJobActor에서 대량 데이터 처리 및 ETL 수행

  • 배치 작업 상태 확인과 중복 실행 방지 포함


🚨 장애 복구 & 로깅

  • preRestart를 활용한 액터 재시작 시 로깅 및 장애 원인 추적

  • 안정적인 장애 복원력 확보


🧾 요약

이 구조는 Akka의 메시지 기반 분산 처리 능력Spring의 관리 기능, Kafka의 이벤트 스트리밍을 결합해 대규모 데이터 흐름과 작업 종료 프로세스를 유연하게 처리하도록 설계되었습니다.

  • 병렬성 + 확장성 + 안정성을 모두 고려한 구조

  • 작업 흐름과 종료 시나리오를 각각 라우터-워커 구조로 분리

어떠한 개발패러다임을 알게되면 그 기원이 무엇일까? 궁금하게되는데 마지막으로 우리가 공기처럼 사용하고 있는 동시성처리 영역이지만 이미 그 컨셉을 알게모르게 이용하고 있는 ReactiveStream과 관련한 질문을 마지막으로 하고 정리합니다.


넷플릭스가 도입해 크게 성공한 Ractive에 출발해, 라이트벤드의 주력활동인 ReactiveStream활동과 자바진영에 영향을 준 Webflux및 OpenAI가 채택하게된 Ray까지 설명해죠


🌀 1. Reactive 시작 - 넷플릭스의 혁신

  • 넷플릭스는 2010년대 초반부터 글로벌 사용자 증가에 따라 대규모 비동기 처리와 내결함성, 탄력성이 필수적이었음.

  • 이를 위해 RxJava를 도입해 Reactive Programming을 실무에 본격 적용함.

  • 주요 특징:

    • Pull 방식 대신 Push 기반 데이터 흐름

    • Observer 패턴 + Backpressure (과부하 조절)

  • 넷플릭스가 Reactive 상용 성공 사례로 꼽히며, 이후 업계가 주목하게 됨.



2. Lightbend Reactive Streams 표준 제안

  • **Lightbend (구 Typesafe)**는 Scala 기반 Akka를 개발한 회사로, 진정한 Reactive 시스템 구축을 위한 4대 원칙을 정의함:

    1. Responsive (반응성)

    2. Resilient (회복력)

    3. Elastic (탄력성)

    4. Message-driven (메시지 중심)

  • 2013~2015년 사이, Lightbend는 Reactive Streams 사양을 주도하여 발표

    • 자바 진영 표준 인터페이스: Publisher, Subscriber, Subscription, Processor

    • Java 9부터 Flow API 채택됨

  • 주요 기여:

    • Akka Streams

    • Play Framework

    • Alpakka (Streaming Connector)

    • Lagom Framework (Reactive Microservice)

3. 자바 진영: Spring WebFlux

  • **Spring 5 (2017)**부터 Spring WebFlux 등장

    • 기존 Spring MVC는 Servlet 기반 → 블로킹

    • WebFlux는 Reactor Core (Project Reactor) 기반 → 논블로킹 / 이벤트 루프 기반

  • 주요 특징:

    • Reactive Streams 호환 (Mono, Flux)

    • Netty 기반 비동기 웹 서버

    • Spring Security, Data, WebSocket 모두 Reactive 확장

  • 영향:

    • 자바 생태계에서 표준 Reactive API로 자리잡음

    • MSA와 API Gateway, 이벤트 기반 아키텍처에 적극 활용됨



4. OpenAI가 채택한 분산 컴퓨팅: Ray

🧬 Ray의 탄생

  • UC Berkeley RISELab에서 시작.

  • OpenAI와 같은 AI 연구소/기업이 요구하는 대규모 분산 처리를 위한 프레임워크.


⚙️ 특징

  • Python 기반이지만, 내부적으로 고성능 C++로 동작.

  • Actor 모델, Task 기반 병렬 처리, 스케일아웃 학습/추론, 파이프라인 병렬화 등 지원.

  • Dask, Apache Spark와 달리 실시간, 상태 유지, 재시도/복구까지 포함한 고신뢰성 분산 시스템.


✅ OpenAI와 Ray

  • GPT 학습·파인튜닝·배포에 필요한 분산 파이프라인, 클러스터 자원관리, 추론 백엔드로 채택됨.

  • HuggingFace, Anyscale, Uber 등도 채택 중.



🔄 결론

  • 넷플릭스가 실무에서 검증한 Reactive Paradigm은, Lightbend를 통해 JVM 표준이 되었고,

  • Java 진영에서는 WebFlux로 실용화되었으며,

  • 대규모 분산 AI 시대에는 Ray 같은 새로운 Reactive Actor 기반 시스템이 주목받고 있습니다.


👉 이 흐름은 비동기 · 스트림 · 상태 유지 · 분산 · 탄력성 이라는 일관된 철학으로 이어지고 있습니다.


반전

자바진영에서 크게 성공한 Reactive Stream 의 인터페이스의 원조는 사실 닷넷 진영이란점

자바/닷넷 개발자도 잘 모르는 사실로 자바/닷넷 진영이 상호발전했으며 동일한 컨셉을 각각의 언어에서 이용할수 있습니다.

자바진영에 영향을 준 .NET 진영 Reactive 을 추가로 조사해

🧩 .NET 진영 Reactive 흐름 요약

📌 1. Rx.net (Reactive Extensions for .NET)

🎯 시작 배경

  • Microsoft 내부 프로젝트로 2009년 개발 시작.

  • C#의 LINQ(Language Integrated Query) 개념을 비동기 스트림 데이터 처리로 확장.

  • .NET에 Rx 핵심 추상화인 Observable 패턴을 도입.


🔑 주요 특징

  • IObservable<T> / IObserver<T> 인터페이스 제공.

  • LINQ 스타일의 연산자 지원 (Select, Where, Throttle, Merge, CombineLatest, etc.)

  • 병렬/비동기 이벤트 처리에 강력함.

  • 다양한 플랫폼(.NET, JavaScript(RxJS), Java(RxJava), Python 등)으로 확장됨.


🔄 자바에 준 영향

  • 자바의 RxJava는 Rx.NET의 철학과 API 스타일을 거의 그대로 포팅한 것.

  • RxJava → Netflix 채택 → 자바 Reactive 패러다임의 대중화로 이어짐.



🧠 2. System.Reactive (Rx.net 공식 라이브러리)

🧰 구성 요소

  • System.Reactive NuGet 패키지로 제공.

  • 주요 네임스페이스: System.Reactive.Linq, System.Reactive.Subjects

  • 다양한 Scheduler (NewThreadScheduler, ThreadPoolScheduler, EventLoopScheduler)로 스레드 제어 가능.


항목

.NET 진영

자바 진영

Reactive 초석

Rx.net

RxJava (Rx.net 영향)

표준화 시도

IObservable<T> (비표준)

Reactive Streams → Java 9 Flow API

대표 프레임워크

ReactiveUI, UniRx

Project Reactor, Akka Streams

비동기 스트림 처리

IAsyncEnumerable<T>

Kotlin Flow / Reactor Flux

UI 통합

ReactiveUI (MVVM 기반)

Spring WebFlux (서버 사이드 중심)

Backpressure 제어

제한적 (Rx.net 2에서 실험)

Reactive Streams에서 표준화됨


액터모델을 학습하다보면 기본언어가 채택한 동시성및 병렬처리(멀티스레드)를 이용하기때문에 근간이 되는 기본기를 함께 학습할수 있는 효과도 있으며 기본기는 중요합니다.

액터모델과 스레드모델을 비교하는것도 좋은 주제가 됩니다. 


액터모델과 스레드모델의 장단점을 요약해


액터모델 VS 스레드모델

🧠 1. 개념 비교

항목

액터 모델 (Actor Model)

스레드 모델 (Thread-based Model)

단위

Actor (행위자)

Thread (실행 흐름)

상태

자기만의 상태 보유
외부 접근 불가

공유 메모리 기반 상태

메시지

비동기 메시지 전달

직접 함수 호출 / 공유 메모리 접근

실행 방식

이벤트 루프 기반 단일 스레드 (내부 동기)

OS 스레드 혹은 사용자 정의 스레드

동시성 처리

메시지 큐 처리 순서 보장

락(lock), 세마포어, 뮤텍스 등 동기화 필요

모델 구조

분산/병렬에 적합한 설계

싱글머신 중심의 동기화 설계

대표 기술

Akka, Erlang, Ray, Orleans, Pekko

Java Thread, .NET Task, POSIX Threads 등

⚖️ 2. 장단점 비교

✅ 액터 모델의 장점

장점

설명

✔ 상태 캡슐화

각 액터는 자기 상태를 숨기고 메시지로만 통신하므로 데이터 경쟁 없음

✔ 고장 격리

하나의 액터가 죽어도 전체 시스템에 영향 없음 (Supervisor Pattern 등 활용)

✔ 분산 확장성

네트워크 위에서도 동일한 방식으로 메시지 주고받기 가능

✔ Backpressure 친화적

메시지 큐 기반 → 처리량 제어 및 흐름 조절 유리

✔ 동시성 추론 쉬움

락 없이 동시성 문제 해결 가능 (락 프리 모델)

❌ 액터 모델의 단점

단점

설명

❗ 디버깅 어려움

메시지 흐름 기반 → 상태 추적 및 디버깅 어려움

❗ 학습 곡선

전통적인 프로그래밍 방식과 사고방식 차이

❗ 메시지 순서 보장 비용

메시지 순서 보장과 재전송 처리 시 성능 저하

❗ 너무 많은 액터 → 관리 어려움

수십만 액터 운영 시 GC/메모리 튜닝 필요


✅ 스레드 모델의 장점

장점

설명

✔ 직관적인 프로그래밍

순차적 사고에 기반한 구현이 가능

✔ 빠른 성능 (단일 작업)

스레드 간 공유 메모리 → 복잡한 처리를 빠르게 수행

✔ 널리 사용됨

대부분의 라이브러리/프레임워크가 스레드 기반 설계

❌ 스레드 모델의 단점

단점

설명

❗ 동기화 어려움

락, 데드락, 레이스 컨디션 등 문제 자주 발생

❗ 자원 소모 많음

스레드당 메모리/컨텍스트 스위칭 비용 큼

❗ 디버깅 어려움

공유 메모리 상태 변경 추적 어려움

❗ 확장성 제한

수천 개 이상의 스레드는 시스템에 부담

🧩 선택 기준

조건

추천 모델

복잡한 상태 공유와 동기화 필요

❌ 스레드 모델은 리스크 큼 → 액터 모델 추천

단순하고 고속 처리 필요

✅ 스레드 모델

대규모 동시성 (예: 채팅, 게임 서버, AI 워크로드)

✅ 액터 모델

낮은 레이턴시 요구 (소수 작업자)

✅ 스레드 모델

장애 격리, 복구가 중요한 서비스

✅ 액터 모델 (Supervisor, Restart 전략)

📌 대표 기술 비교

영역

액터 모델

스레드 모델

Java

Akka, Pekko, Quarkus Actor

Thread, ExecutorService, ForkJoinPool

.NET

Orleans, Proto.Actor

Thread, Task, TPL

Python

Ray, Thespian

threading, asyncio

Erlang

OTP Actor System

N/A

  • Erlang의 액터모델이 기원이자 원조이며 프레임워크 형태가 아니라~ 기본언어에서 액터모델 방식을 채택합니다.

    • Kotlin도 자바(22) Virtual에 대응하는 경량화된 액터모델을 도입

  • 나머지 언어는 툴킷또는 프레임워크를 채택해 이용가능합니다.


동시성 처리를 하기위해 함수형 개발방법도 비교하는것도 도움이 됩니다. 액터모델은 이벤트를 수신받은후 절차형으로 개발도 가능하지만 Stream을 이용 함수형 개발도 가능한 하이브리드 형태의 개발을 이용할수 있습니다.

함수형 개발 비교 WebFlux VS AkkaStream

WebFlux와 AkkaStream 함수형 개발관점에서 비교하고 샘플코드도 작성

✅ WebFlux vs Akka Stream - 함수형 개발 관점 비교

항목

WebFlux (Spring)

Akka Stream (Lightbend/Pekko)

패러다임 기반

Reactor 기반의 리액티브 스트림 (Publisher-Subscriber 패턴)

액터 기반의 back-pressure 처리 스트림

주요 추상화

Mono<T>, Flux<T> (단일/다중 스트림)

Source, Flow, Sink (명확한 데이터 흐름 단계 분리)

함수형 조합 방식

map, flatMap, filter, zip 등 연산자 체이닝 중심

via, map, grouped, async, throttle, merge 등 파이프라인 단계 명시적 구성

Backpressure 처리

Reactive Streams 명세 기반 (Publisher-Subscriber 간 처리 조절)

Actor 기반 메시지 큐 + materializer로 세밀한 backpressure 제어

성능 튜닝 포인트

운영자 순서, subscribeOn/publishOn, thread-pool 조정

병렬도(async, mapAsync), 버퍼 크기, supervision 설정

에러 처리

onErrorResume, onErrorMap 등 체이닝 내 포함

recover, recoverWithRetries, supervisionStrategy 통한 흐름 제어

Materialization 시점

.subscribe() 호출 시 실행

run() 또는 to(Sink).run() 호출 시 실행

구성 단위

단일 연산 중심, 전체 흐름이 코드에 압축됨

스트림 정의와 실행 분리, 재사용성과 테스트 용이

적용 예시

REST API 응답, DB 비동기 처리, WebSocket 처리

대용량 이벤트 스트리밍, 실시간 처리 파이프라인

동시성 모델

리액티브 스레드 모델 (non-blocking)

액터 기반 메시징 모델 + 스트림 컨트롤 (high-level 동시성)

🎯 개발 스타일 요약


WebFlux 함수형 스타일

Akka Stream 함수형 스타일

느낌

자바스러운 연산 체이닝의 함수형

선언형 데이터 흐름 조립식 함수형

제어력

체이닝으로 표현은 간결하지만, 복잡한 흐름은 어려움

각 단계를 분리해서 유연하게 조합 가능

코드 복잡도

간단한 I/O에는 적합, 복잡한 제어에는 다소 어려움

복잡한 흐름/조건/재시도/루프 등에 유리

도입 난이도

Spring에 익숙한 개발자에게는 빠름

Akka 배우는 데 러닝커브 있음, 특히 materializer 개념

✳️ 요약 문장

  • WebFlux간단한 비동기 처리에 강한 함수형 체이닝 기반이고,

  • Akka Stream복잡한 흐름 제어와 병렬 처리에 특화된 함수형 선언형 파이프라인입니다.


샘플코드 비교는 긴 문서를 LLM이 Rag를 하기위해 학습문서를 Junk단위 분해하고 LLM사용 제약이 있을수 있기때문에 속도제어를 함께 요청해보았습니다.


//Webflux
fun main() {
    val largeText = "A".repeat(10_000) // 예시용 1만자 문자열
    val chunks = largeText.chunked(500) // 500자 단위로 자르기
    Flux.fromIterable(chunks)
        .delayElements(Duration.ofMillis(333)) // 초당 3개 = 333ms 간격
        .doOnNext { chunk ->
            println("Saved chunk (size=${chunk.length}): ${chunk.take(30)}...") // 앞 30자만 표시
        }
        .blockLast() // Flux 종료까지 대기

  • chunked(500) → 문자열을 500자 단위로 분할

  • delayElements(Duration.ofMillis(333)) → 초당 최대 3개 제한 (1000ms / 3)

  • blockLast() → 프로그램 종료 방지 (테스트 환경에 필요)



//AkkaStream
object ChunkLimiterApp extends App {
  implicit val materializer = ActorMaterializer()
  val largeText = "A" * 10000
  val chunks = largeText.grouped(500).toList // 500자 단위로 분할
  Source(chunks)
    .throttle(elements = 3, per = 1.second, maximumBurst = 0, ThrottleMode.Shaping)
    .runForeach { chunk =>
      println(s"Saved chunk (size=${chunk.length}): ${chunk.take(30)}...")
    }
  • grouped(500) → 500자씩 문자열 자르기

  • throttle(3, 1.second) → 초당 최대 3개만 실행 (rate limit)

  • runForeach → 저장/출력용 sink로 실행


프로그래밍 Style이 전반적으로 유사하지만 한가지 큰 차이점은 초당3을 수행하기위해 webflux는 코드수행중 비동기지연을 하지만, AkkaStream의 경우 throllte장치를 앞단에 이용해 초당3개가 진행될수 있게 흘려보내는 컨셉을 가지고 있습니다. TPS의 정밀도및 처리량 가속도 적용은 AkkaStream이 더 유연하게 지원하기때문에 처리량을 제어할때 조금더 유리합니다.

팀의 Next도전

  • Kotlin with Actor - 팀의 다음 도전언어는 코틀린기반 코프링으로 작성된 서비스로 오픈소스인 peeko를 연구하고 적용사례를 만드는데 도전준비하고 있습니다.
    • pekko는 akka의 2.6x를 커버하는 오픈소스버전입니다.
  • https://code.webnori.com - 개인 활동으로 동일컨셉을 닷넷으로도 연구되고 있습니다. 닷넷개발팀이 필요하면 전파 예정


NEXT 아티컬 - 오버엔지니어링을 방지하기 ( 데니아 )





  • No labels