You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

액터모델의 메일박스로 통해 전달되는 이벤트들은 다양한 DB장치를 이용해 영속화가 가능하며 상태프로그래밍을 할수 있습니다.

RDB,MongoDB,ElasticSearch등 다양한 특징을 가진 전통강호 영속장치를 이용해 각각 특화된 기능을  이용할수 있겠지만

이러한 기능을 보편적으로 모두이용할수 있는 장치가 없을까 조사하다 발견한 DB로 Akka를 연구하는 NetCorelabs에 변종실험 대상으로 추가하였습니다.

데이터베이스는 지루하면 안된다라는 슬로건을 내건 모던 DB


전통 데이터베이스의 문제는 전문화된 기능을 각각 가지고 안정적으로 발전해온 그자체가 아니라 서비스가 확장되고 다양한 분석기능으로 확장되고

AI시대 특히 RAG를 강화 하기위해 다음과 같은 특화된 DB를 모두 필요로 하게됩니다. 

  • RDB
  • Elastic Search
  • Mongo Document DB
  • Neo4J Graph DB
  • Vector DB

전통적 DB를포함 특화된 NoSQL 모두 다뤄 필연적으로 발생하는 불가피한 작업을 Boring 이라 표현하는것같습니다.

다양한 DB를 관리함으로 발생하는 관리비용뿐만 아니라~ 데이터가 이동됨에 따른 가공과정의 파이프라이닝을 다루는  높은수준의 ETL 능력을 가진 팀을 필요로하게됩니다. 

필요로하는 ETL 파이프라이닝

[RDB / Mongo]     [File Upload]        [API]
     │                │                  │
     ▼                ▼                  ▼
 ┌─────────┐    ┌──────────┐     ┌─────────────┐
 │ Debezium│    │ Fluent Bit│     │ REST Events │
 └─────────┘    └──────────┘     └─────────────┘
       ▼                ▼                 ▼
               ┌─────────────────────┐
               │    Apache Kafka     │
               └─────────────────────┘
                     ▼
         ┌─────────────────────┐
         │  Flink / Beam / NiFi│  ← 변환, 정제
         └─────────────────────┘
        ▼      ▼         ▼       ▼
  [Elastic] [Mongo] [Neo4j] [Vector DB]


데이터 엔지니어팀이 이미 존재하다면  이러한 파이프라인을 이미 구축하고 그 기반으로 성장을 가속화를 했을테지만

소규모로 구성된 스타트업은 핵심 서비스도 개발하면서 동시에 고도화된 데이터파이프라이닝을 구축할 여력이 없습니다.

대안으로 클라우드가 제공하는 파이프라인툴은 Nosql과 연동되어 ETL플랫폼을 제공하며 그 자체로  휼륭하지만

트래픽에 따른 인프비용 증가를 통제하기 쉽지 않습니다. 


AI OPS 등장

AI 를 활용하는 AI OPS의 등장으로 MSA의 API GateWay는 Context Gateway로 전환을 준비하고 기중중심에서 문맥중심으로 활용해야할 DB와 인프라는 더 복잡해자고

활용 인프라아키텍처는 더 복잡해지고 더 많은 이벤트를 처리할것이 분명해 보입니다. 


✅ 1. Next 아키텍처의 키워드

핵심 키워드

설명

AI-NativeAI가 모든 서비스 흐름에 기본 내장됨 (예: 추천, 예측, 분류)
Context-Aware사용자, 시스템 상태에 따라 유동적으로 동작 (MCP 적용)
Event-Driven모든 변화가 이벤트로 감지/처리됨 (Kafka, NATS)
Composable기능을 블록처럼 재조립 (Low-code, Function as a Service)
Autonomous Ops스스로 모니터링, 복구, 확장하는 인프라
Multi-Agent Collaboration

여러 AI Agent가 분산되어 협력 작업 수행


전통RDB진영 또한 Nosql기능을 탑재해가며 발전해 나가겠지만~ 스몰비즈니스 개발팀이 각각 특화된 모든 DB를 다루고 복잡한 파이프라인을 다룰 역량을 가지기 까지

너무오랜 시간이 걸리기때문에  다양한 데이터베이스를 다룸으로 발생하는 복잡성및 비용을 줄이기위해  하루가 다르게 변화하는 AI의 인기에는 가려질수 있겠지만

조용하게 이용이 될것으로 예상해봅니다. ( 전통DB를 필수로 사용해야하다는 인식의 전환)



서론이 길었으며~ 다양한 하이브리드 모던 DB중 하나인 RavenDB의 특징과 사용법 그리고 Akka.net에서의 확장 사용법을 간단하게 알아보겠습니다.

✅ RavenDB 특징

항목설명
Document StoreJSON 기반의 문서 저장 (MongoDB처럼)
Full-Text Search 내장Lucene 기반 검색엔진 포함 (Elasticsearch 대체 가능)
Graph-Like Traversal 지원Include, Load, RelatedDocuments 로 Graph traversal 흉내 가능
벡터 검색 (Vector Search)6.0 이상 버전에서 Vector search 지원 (Preview → Stable 예정)
ACID 트랜잭션 지원NoSQL 중 드물게 단일 DB 내 ACID 지원
자동 인덱싱/쿼리 최적화쿼리 기반으로 자동 인덱싱 생성
Change Vector / ETL 기능 내장다른 Raven 클러스터 또는 외부 시스템으로 데이터 복제 가능
클라우드 + 온프렘 지원다양한 배포 환경 대응
Sharding + Replication분산 구조 대응 가능 (Sharded DB)


✅ 기존 DB 구성 중 대체 가능한 역할


기존 시스템RavenDB로 대체 가능 여부설명
MongoDB (Document DB)✅ 완전 대체JSON 기반 문서 저장, 컬렉션 → 문서 분리 모델
Elasticsearch✅ 부분 대체Full-text 검색 지원, 복잡한 분석쿼리는 제한적이나 일반 검색에는 충분
Neo4j (Graph DB)⚠️ 간단한 관계 트래버설은 가능명시적 Graph 모델링은 어려움 (복잡한 네트워크 분석에는 부적합)
Vector DB (예: Weaviate, Milvus)✅ 단순 벡터 검색은 대체 가능다차원 벡터 검색 API 제공, 모델링+쿼리 결합 쉬움
RDB (CRUD/정형)⚠️ 단순 CRUD는 가능, 복잡한 조인과 트랜잭션은 제한적정형 테이블 기반보다는 문서 중심 모델 필요



RabenDB Docker StandAlone 구동

version: '3.8'

services:
  ravendb:
    image: ravendb/ravendb:ubuntu-latest
    container_name: ravendb
    ports:
      - "9000:8080"
    environment:
      - RAVEN_Setup_Mode=None
      - RAVEN_License_Eula_Accepted=true
    volumes:
      - ravendb_data:/ravendb/data
      - ravendb_logs:/ravendb/logs

volumes:
  ravendb_data:
  ravendb_logs:


  • 클라우드로도 이용가능하며~ 로컬또는 온프레미스로도 운영가능합니다. 


IDE 환경

  • Docker와 통합된 IDE환경으로 RavenDB 구동


RavenDB 웹관리툴

  • DB를 관리할 웹툴을 포함하며 기본적인 관리가 가능합니다.
  • IDE내에서 연동돠는 플러그인은 아직 없어보입니다.


언어별 지원 클라이언트

  • Akka.net을  연계해 사용예정이기때문에 .NET 기반실험이 진행되었습니다.


RabenClient for .NET

  • 설치된 서버 Mazor버전과 맞춰서 패키지 설치


Database생성


몽고DB와 유사하게 스키마리스 DB이기때문에 DDL코드가 필요로 하지 않습니다. 


Repository 코드

public class Member
{
    public string Id { get; set; } // RavenDB는 기본적으로 Id를 문서 키로 사용
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

public class MemberRepository
{
    private readonly IDocumentStore _store;

    public MemberRepository(IDocumentStore store)
    {
        _store = store ?? throw new ArgumentNullException(nameof(store));
    }

    public void AddMember(Member member)
    {
        using (var session = _store.OpenSession())
        {
            session.Store(member);
            session.SaveChanges();
        }
    }

    public Member GetMemberById(string id)
    {
        using (var session = _store.OpenSession())
        {
            return session.Load<Member>(id);
        }
    }

    public void UpdateMember(Member member)
    {
        using (var session = _store.OpenSession())
        {
            var existingMember = session.Load<Member>(member.Id);
            if (existingMember != null)
            {
                existingMember.Name = member.Name;
                existingMember.Email = member.Email;
                existingMember.Age = member.Age;
                session.SaveChanges();
            }
        }
    }

    public void DeleteMember(string id)
    {
        using (var session = _store.OpenSession())
        {
            var member = session.Load<Member>(id);
            if (member != null)
            {
                session.Delete(member);
                session.SaveChanges();
            }
        }
    }
}


CRUD TEST 코드

public class MemberRepositoryTest : TestKitXunit
{
    private readonly IDocumentStore _store;
    private readonly MemberRepository _repository;
    
    public MemberRepositoryTest(ITestOutputHelper output) : base(output)
    {
        // RavenDB 임베디드 서버 초기화
        _store = new DocumentStore
        {
            Urls = new[] { "http://localhost:9000" }, // 로컬 RavenDB URL
            Database = "net-core-labs"
        };
        _store.Initialize();

        // MemberRepository 초기화
        _repository = new MemberRepository(_store);
    }
    
    [Fact]
    public void AddMember_ShouldAddMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "John Doe",
            Email = "john.doe@example.com",
            Age = 30
        };

        // Act
        _repository.AddMember(member);

        // Assert
        var retrievedMember = _repository.GetMemberById(member.Id);
        Assert.NotNull(retrievedMember);
        Assert.Equal("John Doe", retrievedMember.Name);
    }

    [Fact]
    public void UpdateMember_ShouldUpdateMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "Jane Doe",
            Email = "jane.doe@example.com",
            Age = 25
        };
        _repository.AddMember(member);

        // Act
        member.Age = 26;
        _repository.UpdateMember(member);

        // Assert
        var updatedMember = _repository.GetMemberById(member.Id);
        Assert.NotNull(updatedMember);
        Assert.Equal(26, updatedMember.Age);
    }

    [Fact]
    public void DeleteMember_ShouldDeleteMemberSuccessfully()
    {
        // Arrange
        var member = new Member
        {
            Name = "Mark Smith",
            Email = "mark.smith@example.com",
            Age = 40
        };
        _repository.AddMember(member);

        // Act
        _repository.DeleteMember(member.Id);

        // Assert
        var deletedMember = _repository.GetMemberById(member.Id);
        Assert.Null(deletedMember);
    }
    
}
  • 처음 사용하는 DB사용 자체를 연구하고 테스트하는 코드이기때문에 Mocking을 이용하지 않고 로컬구축 DB직접 Access방식이용


테스트 수행및 확인

  • 비교적 심플한 코드작성으로 CRUD 테스트 수행이 완료되었습니다.


Next - CQRS로의 여정

ActorModel에 Persist는 CRUD만 파악되면 커스텀하게 디벨롭할수도 있겠지만, akka.net진영과 콜라보로 공식 지원을 합니다.






  • No labels