로그 모니터링 시스템은 서비스 운영을 위해서 반드시 필요한 시스템입니다. 로그 모니터링 시스템 구축에는 인덱스 기반의 빠른 검색을 제공하는 검색 엔진인 Elasticsearch가 널리 사용됩니다. 네이버도 Elasticsearch 기반의 로그 모니터링 시스템을 구축했으며, 수천 대의 서버로 수 페타바이트 규모의 로그 데이터를 저장하고 있습니다. 최근 들어 서비스 규모가 확장되고 저장해야 하는 로그 데이터의 규모와 트래픽 양이 급속도로 증가하면서 Elasticsearch 기반의 로그 모니터링 시스템은 비용 문제와 더불어 확장성 문제에 직면하게 됐습니다. 네이버는 저비용으로 대용량의 로그 데이터를 수집할 수 있도록 Apache Iceberg(이하 Iceberg)를 도입한 신규 컴포넌트 Alaska를 개발해 네이버의 로그 모니터링 시스템 플랫폼 NELO에 적용했습니다. 이 글에서는 기존 로그 모니터링 시스템의 문제와 Iceberg의 특징을 소개하고, Alaska의 작동 방식과 Alaska를 NELO에 적용한 이후의 변화를 소개합니다. Elasticsearch 기반 기존 로그 모니터링 시스템의 한계 Elasticsearch를 기반으로 구축된 기존 로그 모니터링 시스템의 구조를 간략하게 도식화하면 다음 그림과 같습니다. 클라이언트로부터 수신된 로그 데이터는 Kafka에 적재된 후 Elasticsearch에 저장됩니다. Elasticsearch는 SSD 타입의 스토리지로 구성된 Hot 계층(Hot Tier)과 HDD 타입의 스토리지로 구성된 Warm 계층(Warm Tier)으로 구분되어 있습니다. 로그 데이터는 Hot 계층에 3일간 저장된 후 Warm 계층으로 이동되어 최대 90일까지 저장됩니다. 이렇게 Hot 계층과 Warm 계층의 두 단계로 나누어 데이터를 저장하면 검색이 빈번하게 일어나지 않는 데이터를 효율적이면서 저비용으로 저장할 수 있습니다. 기존 로그 모니터링 시스템은 수년간 이와 같은 구조로 운영되었습니다. 그동안 데이터가 증가함에 따라 Warm 계층에 저장된 데이터의 크기도 급증했습니다. Elasticsearch는 단일 클러스터로 저장할 수 있는 데이터의 크기에 제한이 있기 때문에 다수의 Elasticsearch 클러스터를 구성해 클러스터 수준에서 확장을 진행했습니다. 그 과정에서 모든 클러스터가 한계 수준까지 도달해 운영 장애를 빈번하게 겪었습니다. 연간 수십억 원의 인프라 사용 비용도 부담이 되었습니다. 두 계층으로 구성된 Elasticsearch 클러스터는 더 이상 로그를 효율적으로 저장할 수 있는 구조가 아니게 되었습니다. 새로운 타입의 데이터 저장 스토리지의 필요성 Warm 계층에 저장된 데이터의 크기가 급증하는 이유에는 장기간 로그의 저장에 대한 요구 사항도 있습니다. 기존 로그 모니터링 시스템은 Elasticsearch에 최대 90일까지 로그 데이터를 저장할 수 있게 허용했습니다. 하지만 서비스의 법적 요구 사항 등의 이유로 예외로 1년 이상의 로그 데이터 저장을 허용하고 있었습니다. 기존 로그 모니터링 시스템이 한계에 도달하면서 이러한 장기간 로그 데이터 저장에 대한 요구 사항을 Elasticsearch로 수용하는 것이 적합한지 검토하게 되었습니다. 이를 확인하기 위해 실제로 로그 데이터의 사용자들이 어느 시점의 데이터에 관심이 많은지 분석해 보았습니다. 다음은 한 달 동안 사용자들의 검색 요청 로그를 분석한 그래프입니다. 그래프에서 X축인 Data age는 데이터가 저장되고 지난 시간을 의미합니다. Y축은 해당 Data age에 속한 데이터를 검색하는 쿼리의 비율입니다. 분석 결과, 전체 검색 쿼리 중 95%의 쿼리가 당일에 발생한 데이터에 대한 것이었으며, 99%의 쿼리가 일주일 이내의 데이터를 위한 것이었습니다. 단 0.5%의 쿼리만이 2주 이상 지난 데이터를 요청하는 쿼리였습니다. Elasticsearch는 일반적으로 데이터 저장과 쿼리 계산을 위한 컴퓨팅을 같은 노드에서 담당하고 있기 때문에 이렇게 거의 검색되지 않는 데이터를 저장하는 것은 효율적인 일이 아닙니다. 최신 버전의 Elasticsearch는 원격 스토리지에 데이터를 저장하고 검색하는 기능을 제공합니다. 하지만 Elasticsearch의 규모가 한계 크기에 도달한 로그 모니터링 시스템에 적용할 수는 없었습니다. Elasticsearch는 마스터 노드가 관리할 수 있는 메타데이터 규모에 한계가 있기 때문입니다. 이러한 문제를 해결하려면 Elasticsearch에는 검색이 자주 일어나는 단기간의 데이터 저장만 허용하고, 장기간 데이터를 저장할 새로운 스토리지가 필요하다는 판단이 들었습니다. Elasticsearch를 대체하는 신규 스토리지에서는 데이터 저장을 위한 스토리지와 검색을 위한 컴퓨팅을 분리한다 아이디어를 기본으로 설계를 시작했습니다. 또한 Elasticsearch처럼 특정 쿼리 엔진에 제한(lock-in)되지 않는 오픈 데이터 포맷을 중요한 요구 사항 중 하나로 설정했습니다. 저비용의 스토리지에 검색이 가능한 데이터 포맷으로 데이터를 저장할 수 있는 여러 방식을 비교하고 분석했습니다. 여러 방식으로 시뮬레이션을 실행한 결과, Iceberg로 실행한 시뮬레이션에서 현재 수준에서 기존 로그 모니터링 시스템보다 최소 50% 이상 비용을 절감할 수 있다는 결론을 얻었습니다. 최종적으로 Iceberg를 선택해 로그 데이터 저장을 위한 새로운 타입의 스토리지를 구현한 컴포넌트인 Alaska를 개발해 적용했습니다. Iceberg의 특징 기존 로그 모니터링 시스템의 구조와 규모 때문에 새로운 타입의 스토리지에는 다음과 같은 요구 사항이 있었습니다. 데이터 쓰기/읽기가 동시에 가능해야 한다. 데이터 쓰기/읽기가 발생하는 상황에서 동시에 스키마 변경이 가능해야 한다. 단일 테이블로 페타바이트 규모의 데이터를 저장할 수 있어야 한다. 수십만 개의 테이블 운영이 가능해야 한다. 데이터 포맷으로 인한 쿼리 엔진 제한이 없어야 한다. 데이터 저장소와 쿼리 컴퓨팅 노드가 분리되어 있어야 한다. 데이터 압축 효율이 우수해야 한다. 이 요구 사항을 구현할 수 있는 기술로는 오픈 테이블 포맷을 사용하는 Iceberg와 Delta Lake, Apache Hudi가 있습니다. 그 중에 Iceberg의 커뮤니티가 가장 활발하게 업데이트되고 있었습니다. 또한 Databricks, Snowflake 등 여러 회사가 Iceberg를 두고 벌이던 기술 경쟁이 Databricks가 Iceberg를 만든 Tabular 회사를 인수하면서 일단락 되었고, Iceberg가 오픈 테이블 포맷 기술의 주도권을 갖게 되었습니다. 여러 사정을 고려해서 Alaska 컴포넌트에 Iceberg의 오픈 테이블 포맷을 적용하기로 결정했습니다. Iceberg는 '데이터', '메타데이터', '카탈로그'의 세 부분으로 나누어 데이터를 저장합니다. 데이터는 칼럼 스토리지 데이터 포맷인 Parquet로 관리되고, zstd 형식으로 압축됩니다. 이러한 데이터는 오브젝트 스토리지에 저장됩니다. 메타데이터는 하나의 테이블을 구성하기 위한 데이터 파일의 집합 관계와 스키마 정보를 JSON, Avro와 같은 형태로 저장합니다. 데이터와 마찬가지로 메타데이터도 오브젝트 스토리지에 저장됩니다. 카탈로그는 메타데이터의 메타라고 볼 수 있습니다. 가장 최신의 메타데이터의 위치 정보와 같은 최소한의 정보만 카탈로그에서 관리됩니다. 일반적으로 카탈로그 데이터는 데이터베이스에 저장됩니다. 이와 같이 테이블을 구성한 파일에 대한 메타데이터까지 함께 관리하기 때문에 Iceberg를 단순한 데이터 포맷이 아니라 테이블 포맷이라고 부릅니다. Iceberg는 ACID 트랜잭션을 지원하며 schema evolution, hidden partitioning 등 데이터를 다루는 데 유용한 기능을 제공합니다. 신규 로그 모니터링 시스템의 구조 Iceberg를 기반으로 개발한 새로운 로그 모니터링 시스템은 기존 Elasticsearch 기반의 로그 모니터링 시스템을 대체하는 것이 아닙니다. Elasticsearch에는 실시간 모니터링이 필요한 짧은 기간의 로그를 저장하고, 장기간 보관이 필요한 데이터는 새로운 스토리지를 활성화해 저장하도록 설계했습니다. 기존 로그 모니터링 시스템에서는 Kafka에 적재된 로그 데이터를 Elasticsearch에 인덱싱하는 방식을 사용했습니다. 신규 로그 모니터링 시스템도 동일한 Kafka 토픽으로부터 데이터를 읽어 Iceberg 테이블 포맷으로 저장합니다. Elasticsearch의 Warm 계층에서 데이터를 읽어 저장하는 방식을 택하지 않은 이유는 다음과 같습니다. 실시간 검색/모니터링이 필요하지 않은 데이터는 Elasticsearch에 저장하지 않고 직접 Iceberg로 저장할 수 있습니다. Elasticsearch와 Iceberg에 중복 데이터가 저장되더라도 Iceberg 기반 시스템 비용이 매우 저렴합니다. Elasticsearch의 Warm 계층으로부터 데이터를 읽으면 HDD 기반의 클러스터에 큰 부하가 발생합니다. 신규 로그 모니터링 시스템의 아키텍처는 다음 그림처럼 크게 데이터 적재 부분(Data ingestion & optimization)과 데이터 쿼리 부분(Data query)으로 나눌 수 있습니다. 데이터 적재 부분은 다음과 같은 요소로 구성되어 있습니다. Orca: Kafka의 데이터를 Iceberg 테이블 포맷으로 변환해 오브젝트 스토리지에 저장하는 컴포넌트 Polarbear: Iceberg 테이블 데이터를 최적화하고 데이터 라이프사이클을 관리하는 컴포넌트 Puffin: Iceberg 카탈로그 컴포넌트 데이터 쿼리 부분은 다음과 같은 요소로 구성되어 있습니다. Trino: Icerbeg 테이블 조회를 위한 쿼리 컴퓨팅 엔진 API Server: Alaska 데이터 조회를 위한 NELO Open API 제공 Frontend: Alaska 쿼리 UI 제공(웹 UI) 신규 로그 모니터링 시스템은 데이터 프로세싱을 위해서 Kappa Architecture를 따르고 있습니다. 즉, 실시간으로 저장되고 있는 로그 데이터 테이블에 사용자가 접근해 데이터를 조회할 수 있는 구조입니다. 전통적인 Lambda Architecture처럼 여러 개의 테이블을 운용해 데이터 변환 과정을 거쳐 사용자에게 제공하는 방식은 로그 저장 목적으로 사용하기에는 너무 복잡하고 비용 측면에서 효율적이지 않은 구조입니다. Iceberg의 오픈 테이블 포맷은 ACID 트랜잭션을 지원하기 때문에 실시간으로 쓰기가 발생하는 테이블을 동시에 사용자가 읽어도 데이터 정합성을 보장하며 서비스할 수 있습니다. 이러한 구조를 통해서 사용자는 짧은 지연 시간(데이터 동기화 주기 5분) 안에 데이터를 조회할 수 있습니다. 데이터 저장을 위해 사용하는 사내 오브젝트 스토리지 서비스인 Nubes는 MinIO라는 S3 게이트웨이를 활용해 S3 인터페이스를 기반으로 Iceberg와 연동되어 있습니다. 신규 로그 모니터링 시스템의 아키텍처에서 설명한 Orca, Polarbear, Puffin은 모두 Iceberg Java SDK를 기반으로 직접 개발한 컴포넌트입니다. 프로젝트 초기에 오픈 소스를 활용해 PoC(Proof of Concep)를 진행했지만 여러 이유로 오픈 소스를 사용할 수 없었습니다. 신규 로그 모니터링 시스템 개발 초기에 검토한 오픈 소스와 사용 불가 이유는 다음과 같습니다. 데이터 적재 kafka-connect: 기능적 요구 사항은 충족했습니다. 하지만 지원하는 동기화 대상 테이블의 수가 적었습니다. 동기화 대상 테이블의 수가 수십만이었지만, kafka-connect는 테이블의 수가 수백 개의 수준에만 도달해도 OOM(Out of Memory)이 발생했습니다. flink: Kafka의 데이터를 Iceberg로 저장하는 기능은 제공하지만 단일 테이블에 대해서만 작동합니다. 즉, 테이블 fan-out 기능이 존재하지 않습니다. 동기화해야 하는 테이블의 수만큼 flink 애플리케이션을 실행해야 하는 경우가 있어, 현실적으로 운영에 어려움이 있는 문제가 있습니다. 데이터 최적화 Trino, Spark, Hive 등 Iceberg 테이블을 지원하는 쿼리 엔진: 데이터 최적화 및 라이프사이클을 관리하는 것이 기능적으로는 가능합니다. 그러나 요구하는 테이블의 규모를 지원하려면 비용 부담이 커집니다. 또한 세부적인 스케줄링 및 스로틀링 설정이 어렵기 때문에 오브젝트 스토리지에 과한 부담이 발생할 수 있습니다. 카탈로그 Hive metastore, Nessie, Polaris, Unity 등 Iceberg 테이블을 지원하는 카탈로그: 최초 설계에서는 Hive metastore를 사용했으나 Hive lock 버그로 인해 경합이 심할 때에는 데드락에 빠지는 이슈가 발생했습니다. 또한 장기적으로 Iceberg REST 카탈로그를 표준으로 만들고, 다른 카탈로그를 직접적으로 사용하는 것을 중단할 계획이 있다는 것을 Iceberg 커뮤니티를 통해서 확인했습니다. REST 카탈로그는 표준 스펙만 존재하며 공식적인 구현체가 존재하지 않습니다. Snowflake에서 최근에 Polaris라는 REST 카탈로그 스펙에 준한 카탈로그를 공개했지만 특정 카탈로그에 제한될 우려가 있습니다. 또한 카탈로그를 사용자에게 공개해 데이터 연동(data federation)을 제공할 계획이 있어, 컴포넌트를 직접 개발하는 것이 효율적이라고 판단했습니다. 데이터 적재 다음 그림은 Orca 컴포넌트가 Kafka의 데이터를 Iceberg 테이블 포맷으로 변환해 저장하는 과정을 도식화한 그림입니다. Kafka에 쌓여 있는 로그 데이터를 Iceberg 테이블 포맷으로 변환해 저장할 때에는 다음과 같은 단계로 데이터를 처리합니다. Kafka 데이터 수신 Kafka 토픽으로부터 데이터를 읽습니다. 다중 컨슈머 구성을 통해서 I/O 병목 문제를 해결하고 처리량을 극대화했습니다. 로그 데이터 관리 및 전달 데이터를 수신한 후 데이터를 내부 메모리 큐에 적재합니다. 메모리 큐에 적재된 데이터는 레코드 리포지토리를 통해 각 Iceberg 테이블에 대응하는 Writer로 분배됩니다. 데이터 포맷 변환 및 저장 각 Writer는 데이터를 Parquet 형식으로 변환한 뒤 Writer 내부 메모리 버퍼에 저장합니다. Flush Manager가 특정 주기로 오브젝트 스토리지에 데이터를 저장하고 Iceberg 테이블에 커밋합니다. 간단해 보이는 구조이지만 다음과 같은 여러 가지 상황을 고려해 설계되었습니다. 테이블 fan-out 기능:Kafka 토픽에 저장되어 있는 로그는 tenant별로 분리되어 각 Iceberg 테이블에 저장됩니다. 그렇기 때문에 단일 데이터 스트림에서 다수의 테이블로 데이터를 전송하는 fan-out 기능이 필요합니다. 테이블 데이터가 처음 인입되는 시점에 동적으로 Writer가 생성되고 flush가 실행되는 시점에 메모리가 해제되도록 설계했습니다. 효율적인 메모리 관리:초당 수십만 건에 이르는 로그 데이터를 실시간으로 처리하려면 메모리 사용량 최적화가 필수입니다. 실시간으로 유입되는 데이터를 변환해 메모리에 적재하고 5분 단위로 flush를 진행해 메모리를 주기적으로 확보하도록 설계했습니다. 특정 테이블에 데이터가 많이 유입될 경우에는 해당 테이블에 해당하는 데이터를 파일로 먼저 내보내는 롤오버 동작을 수행합니다. 메모리 사용량이 급증할 경우에는 전체 Writer에서 강제 flush를 실행해 OOM을 예방합니다. Kafka 오프셋 관리:Kafka로부터 읽은 데이터의 Iceberg 테이블 커밋이 완료된 이후에 Kafka 오프셋 커밋이 가능합니다. Kafka로부터 읽어 온 batch 단위로 Iceberg 테이블에 커밋을 하면 너무 작은 파일 단위로 커밋이 실행되기 때문에 처리량 측면에서 성능이 저하될 수 있습니다. 그래서 Kafka로부터 읽은 데이터가 충분히 메모리에 쌓였을 때 커밋을 실행해야 하는데, 이럴 경우 Kafka에서 제공하는 자동 오프셋 커밋 기능을 사용할 수 없어 수동으로 오프셋을 관리해야 합니다.내부 메모리에 오프셋을 저장하고 실제로 Iceberg 테이블에 커밋이 성공한 위치까지의 오프셋만 다시 Kafka에 커밋되도록 구현했습니다. 데이터 손실은 발생하지 않지만 중복 데이터가 Iceberg 테이블에 저장될 수 있는 구조(at-least-once)로 설계했습니다.Iceberg의 equality delete 기능을 사용하면 중복 데이터를 방지할 수 있지만 Iceberg 테이블 운용 비용이 비싸지기 때문에 채택하지 않았습니다. 로그 데이터 유실은 중요한 문제가 될 수 있지만, 중복 발생은 대부분 크게 문제가 되지 않습니다. 또한 모든 로그에 유니크 아이디를 부여하고 있어서, 필요시 사용자가 쿼리를 통해서 중복 데이터를 제거할 수 있도록 안내하고 있습니다. 데이터 변환:기본적으로 신규 필드가 유입될 경우 시스템에서 해당 필드를 String으로 취급해 스키마를 자동으로 업데이트합니다(사용자는 UI와 API를 통해서 신규 필드를 원하는 타입으로 생성할 수 있습니다). 신규 필드가 유입되면 해당 테이블에 대해서 메모리에 쌓여 있던 데이터에 강제 flush를 실행한 이후에 스키마 업데이트를 진행하고 다시 메모리에 데이터를 쌓기 시작합니다.특정 필드에 대해서 변환이 불가능한 경우 에러 필드에 원본 데이터와 이유를 함께 저장합니다. Iceberg 테이블은 칼럼 이름의 대소문자 구분을 지원하지만, 쿼리 엔진이 대소문자 구분을 지원하지 않기 때문에 칼럼 이름을 대소문자를 구분하지 않게(case-insensitive) 설정해야 합니다. 대소문자만 다른 이름을 가진 중복되는 필드가 유입되면 에러 필드에 저장합니다. 또한 String이 아닌 다른 타입으로 생성된 필드에 대해서 지원되지 않는 값으로 데이터가 유입될 경우(예: 숫자 타입에 문자열 유입) 에러 필드에 저장합니다. 사용자는 에러 필드를 조회해 누락된 데이터 값과 누락된 사유를 확인할 수 있습니다.알 수 없는 이유로 데이터 변환에 실패하면 DLQ(dead-letter queue)에 전송해 후처리를 실행할 수 있도록 합니다. 트래픽이 증가해 데이터 적재 컴포넌트를 많은 수로 확장(scale-out)하면 단일 Iceberg 테이블에 대해서 여러 노드가 동시에 Write를 실행하게 됩니다. 이럴 경우 다음과 같은 문제가 발생할 수 있습니다. Iceberg 테이블에 대해 동시에 발생한 커밋이 충돌해 실패 가능성 높아집니다. 여러 노드에 데이터가 분산되어 작은 파일로 쪼개져서 Write가 일어납니다. 이 때문에 오히려 처리량이 저하될 수 있으며 오브젝트 스토리지에도 작은 파일로 인해 부담이 발생할 수 있습니다. 또한 추후 데이터 최적화를 위한 Rewriting 과정에서도 문제가 될 수 있습니다. 위와 같은 문제 때문에 데이터 적재 컴포넌트가 단일 노드에서 최대한의 성능을 낼 수 있도록 최적화에 많은 신경을 써서 개발을 진행했습니다. 추후 Kafka 토픽 커스텀 파티셔너를 통해서 개선할 계획도 있습니다. 데이터 최적화 데이터 최적화 컴포넌트는 다음과 같은 두 가지 역할을 수행합니다. Iceberg 테이블 데이터 최적화 및 라이프사이클 관리 Iceberg 테이블 관련 API 제공 데이터 최적화를 진행하지 않으면 Iceberg 테이블에 쌓이는 파일이 너무 많아져서 전체적인 성능이 저하될 수 있습니다. 이러한 데이터 최적화 및 라이프사이클 관리를 위한 태스크를 주기적으로 실행해 테이블의 상태를 최적의 상태로 유지합니다. 또한 API 서버로부터 Iceberg 테이블에 관련된 메타데이터 정보 및 스키마 업데이트 등을 요청받아 처리하는 역할도 수행합니다. 데이터 최적화 컴포넌트는 임베디드 분산 캐시를 내장하고 있으며, 해당 캐시를 통해서 노드의 리더를 선출합니다. 리더로 선출된 노드는 수행해야 할 테스크를 주기적으로 스케줄링해 내부 시스템 테이블로 생성합니다. 이때 시스템 테이블 또한 Iceberg 테이블을 기반으로 생성됩니다. 나머지 팔로워 노드는 시스템 테이블이 업데이트될 때 자신에게 할당된 태스크를 읽어 실행합니다. Iceberg 테이블 최적화 및 라이프사이클 관리를 위해 실행하는 배치 잡(batch job)은 다음과 같습니다. Rewriting data:같은 시간 파티션 안에 있는 파일을 병합하는 작업입니다.데이터 적재 시 5분 주기로 데이터 flush를 실행하기 때문에 실제 테이블에 쓰인 데이터는 작은 파일로 나누어져 있습니다. 예를 들어 데이터 적재 노드가 1개라면 하루에 최소 288개의 파일이 생성(5분당 최소 1개 파일 생성)됩니다. fan-out 대상 테이블의 수가 10,000개라면 하루에 최소 288만 개의 파일이 생성됩니다. 이 상태로 오랜 시간 데이터 적재를 진행하면 파일의 수가 많아져 메타데이터가 거대해지고 커밋 성능이 저하됩니다. 그리고 작은 파일에 대한 I/O가 증가해 쿼리 성능이 저하됩니다. 지속적인 작은 파일 쓰기는 오브젝트 스토리지에도 부담이 됩니다. 그래서 파일을 병합하는 작업을 주기적으로 실행합니다.테이블은 시간 파티션으로 나누어져 있는데, 같은 시간 파티션 안에 존재하는 파일을 병합하는 작업을 매시간 실행합니다. 목표 병합 파일의 크기는 128MiB로 설정되어 있습니다. 트래픽이 많은 일부 테이블을 제외하고 대부분 1시간 로그 데이터가 1개의 파일로 병합됩니다.시간 순서로 유입되지 않고 과거 시간의 로그와 뒤섞인 데이터(disorder data)가 인입되는 테이블의 경우에는 긴 시간 범위에 대해서 병합 작업을 진행합니다. 로그 모니터링 시스템이 네이버 모바일 앱의 로그 수집도 지원하기 때문에 disorder data가 발생합니다. 운영체제의 정책 등 모바일 기기의 특성상 네트워크, 배터리 상태에 따라 로그를 전송하는 시점이 늦어질 수 있습니다. 그래서 최대 3일 전의 로그까지 수집하는 것을 정책상 허용합니다. 이때 3일 이내의 시간 파티션에 계속 작은 파일이 생성되는 문제가 발생하고, 이러한 테이블에 대해서는 매시간마다 3일 내의 모든 파티션을 병합하는 작업을 실행합니다.데이터 적재 지연이 발생하거나 제대로 된 정보가 수신되지 않을 경우에는 해당 작업 스케줄링을 중단합니다. 데이터 지연을 고려하지 않으면 이미 병합이 종료된 시간 파티션에 다시 작은 파일이 생성되어 문제를 유발할 수 있기 때문입니다. Expire snapshot: 주기적으로 스냅숏을 삭제하는 작업입니다. Iceberg 테이블은 매 커밋마다 스냅숏 정보를 남깁니다. 스냅숏을 관리하지 않으면 무한대로 스냅숏이 생성되어 메타데이터 파일이 매우 커지고, 작은 파일이 쌓이는 문제가 발생합니다. 주기적으로 테이블마다 최근 10개의 스냅숏만 남기고 삭제합니다. Optimize table: Rewriting data와 Expire snapshot을 하나의 잡(job)으로 구성해 파일 병합이 종료된 이후에 스냅숏을 삭제하는 작업입니다. Retention: 보존 기간이 지난 로그를 삭제하는 작업입니다. 각 테이블마다 설정된 로그 보존 기간이 있습니다. 보존 기간이 지난 로그를 하루에 한 번씩 삭제합니다. Delete table: 삭제 요청이 있는 Iceberg 테이블을 물리적으로 삭제하는 작업입니다. 삭제 요청이 있은 시점으로부터 3일(데이터 복구 가능 기간)이 지난 뒤에 실행합니다. Iceberg SDK가 제공하는 삭제 API 실행 이후에도 실제 스토리지에 가비지 데이터가 남아 있을 수 있습니다. S3 API를 사용해 해당 테이블 디렉터리 하위에 존재하는 모든 파일에 대해 다시 한번 삭제를 실행합니다. Delete orphan files: 가비지 데이터를 삭제하는 작업입니다. Iceberg 테이블에 데이터 커밋 시 충돌이 발생하면 메타데이터, 데이터 영역에 모두 가비지 데이터가 발생할 수 있습니다. 메타파일과 실제 오브젝트 스토리지에 존재하는 파일을 주기적으로 대조해 가비지 데이터를 삭제합니다. 작업 실행 중 신규 파일이 커밋되면 신규 파일도 삭제될 위험이 있어서 생성된 지 7일 이상 지난 파일에 대해서만 가비지 데이터 분류를 실행합니다. 리더 노드는 주기적으로 배치 잡을 실행합니다. 태스크는 각 테이블의 평균 사이즈를 기준으로 빈 패킹(bin packing) 방식으로 모든 노드에 할당됩니다. 할당된 결과는 Iceberg 시스템 테이블로 저장되고, 각 노드는 해당 시스템 테이블을 주기적으로 읽어 자신에게 할당된 태스크를 실행합니다. 실행이 완료된 태스크는 시스템 테이블에서 삭제됩니다. 태스크 스케줄링 상태가 Iceberg 테이블로 저장되어 있기 때문에 노드가 다시 시작되어도 하던 작업을 이어서 실행할 수 있습니다. 카탈로그와 데이터 연동 신규 로그 모니터링 시스템은 Iceberg REST 카탈로그를 사용합니다. REST 카탈로그의 핸들러 등의 구현체는 Iceberg SDK에 포함되어 있습니다. SDK를 기반으로 Spring Boot로 래핑해 서버로 작동하게 만든 것이 Puffin입니다. REST 카탈로그를 사용하려면 실제 메타데이터가 저장될 저장소를 지정해야 하는데, MySQL을 백엔드 카탈로그로 지정해 사용합니다. Alaska의 초기 설계부터 카탈로그를 사용자에게 제공해 데이터 연동을 지원하려 했습니다. 로그에 포함되어 있는 데이터를 분석하려는 사용자가 많은데, 기존 로그 모니터링 시스템 환경에서는 Open API를 사용해 로그를 다운로드해 분석하는 사용자가 대부분이었습니다. 그렇기 때문에 카탈로그를 사용자에게 제공하면 사용자는 데이터 다운로드 없이 자신의 쿼리 엔진과 직접 연동해 바로 SQL 쿼리를 실행해 쉽게 데이터 분석을 실행할 수 있게 됩니다. 데이터 연동을 위해서 카탈로그에 다음과 같은 기능을 구현했습니다. 기존 로그 모니터링 시스템에서 발급받은 access key 기반으로 인증 시스템과 연동합니다(authentication). 인증된 정보를 기반으로 권한이 있는 테이블에만 접근할 수 있도록 제어합니다(authorization). 데이터 연동 시 read-only API에만 접근을 허용해 테이블에 커밋 및 삭제 등을 실행할 수 없게 합니다. 인증 기능은 Iceberg REST 카탈로그 표준 스펙에 맞춰 구현했고, iceberg.rest-catalog.oauth2.token 설정의 access key 값을 통해 사용자가 권한이 있는 테이블에 읽기 전용으로 접근할 수 있게 했습니다. 데이터 쿼리 신규 로그 모니터링 시스템의 쿼리 엔진으로는 Trino를 채택했습니다. Trino에 의존성을 가지지 않도록 내부 구조를 설계했기 때문에 필요하다면 언제든지 Spark와 같은 다른 쿼리 엔진으로 교체할 수 있습니다. 사용자는 웹 UI 혹은 Open API를 통해서 쿼리를 실행할 수 있습니다. 신규 로그 모니터링 시스템에서는 기본적으로 다음과 같이 쿼리를 크게 Main query와 Sub query로 구분합니다. Main query는 원본 로그 데이터 테이블을 대상으로 실행하는 쿼리입니다. 기본적으로 비동기로 실행됩니다(non-interactive query). CTAS(Create Table As Select) 쿼리로 실행되며, 쿼리 결과는 또 다른 테이블로 저장됩니다. Sub query는 메인 쿼리에 의해서 생성된 쿼리 결과 테이블을 대상으로 실행하는 쿼리입니다. 동기 방식으로 실행됩니다(interactive query). 이렇게 Main query를 비동기 방식으로 실행해 쿼리 결과를 테이블로 저장하는 이유는 대용량의 데이터를 검색할 때 실행 시간이 매우 길어질 수 있기 때문입니다. 일반적으로 인덱스가 없기 때문에 Elasticsearch보다 응답 속도가 느립니다. 장기간 검색을 허용하기 때문에 검색하는 데이터의 양과 쿼리 형태에 따라서 결과를 얻는 데 수시간이 소요될 수도 있습니다. 이러한 상황에서 동기 방식으로 쿼리를 실행하면 사용자는 응답이 올 때까지 웹브라우저가 종료되지 않도록 유지하고 대기해야 합니다. 또한 Main query를 통해 최대한 관심 있는 데이터 영역만 필터링해 쿼리 결과 테이블을 만들면 그 이후부터는 빠른 속도로 관심 있는 데이터 영역을 탐색할 수 있게 됩니다. 이러한 사용자 경험을 고려해 위와 같이 쿼리 방식을 설계했습니다. 새로운 타입의 데이터 저장 스토리지의 필요성에서 살펴본 것처럼 장기 보관 데이터에 대해서는 쿼리가 발생하는 비율이 낮습니다. 그렇기 때문에 Trino 클러스터를 적은 리소스로 제공하고 있습니다. 다만 신규 SQL 쿼리 기능의 도입으로 이전에 없던 쿼리 패턴이 등장해 쿼리 리소스가 과도하게 소모될 가능성이 생겼습니다. 이에 따라 다음과 같이 쿼리에 제약 사항을 두었습니다. 테이블마다 Main query는 동시에 최대 한 개만 실행합니다. Sub query의 실행 속도를 사용자마다 15queries/min로 제한합니다. 쿼리를 ANTLR 4 기반으로 분석해 SQL 문법을 제한합니다. SELECT 쿼리만 허용합니다. WITH, JOIN, UNION, INTERSECT, EXCEPT 연산자를 사용할 수 없습니다. 중첩 쿼리(nested query)를 사용할 수 없습니다. FROM 절에는 반드시 한 개의 대상 테이블만 명시합니다. SQL 쿼리 실행 시 사용되는 리소스를 제한합니다. 사용자 쿼리 요청 시 바로 실행하지 않고 쿼리 플래닝을 통해서 리소스를 예측합니다. 예측된 리소스가 제한 값을 초과하면 사용자에게 에러를 반환합니다. 이와 같은 제약 사항이 없다면 무거운 데이터 분석 쿼리가 많이 유입되어 쿼리 엔진 비용이 급속도로 증가할 가능성이 있습니다. 제약 사항을 넘어서는 쿼리 실행이 필요할 경우에는 카탈로그 데이터 연동을 통해서 사용자의 쿼리 엔진 리소스를 사용하도록 안내하고 있습니다. 신규 로그 모니터링 시스템 적용 결과 Iceberg 기반의 신규 로그 모니터링 시스템을 오픈하면서 기존 Elasticsearch 기반의 로그 데이터는 최대 데이터 보관 기간을 14일로 단축했습니다. 이러한 정책을 통해서 2,000대 이상의 Elasticsearch 노드를 줄일 수 있었으며, 데이터 용량도 수 페타바이트 규모에서 수백 테라바이트 규모로 감소했습니다. 대신 기존에 90일로 제한한 최대 로그 보관 기간을 신규 로그 모니터링 시스템을 활성화할 경우 최대 5년까지로 늘였습니다. 이를 통해서 예상되는 인프라 비용이 매년 수 십억 원까지 절감되었습니다. 늘어나는 트래픽 추세를 감안하면 절감되는 비용은 매년 그 이상이 될 것이라 예상합니다. 이렇게 비용을 절감할 수 있는 이유는 다음과 같습니다. 상대적으로 비용이 저렴한 오브젝트 스토리지에 데이터를 저장합니다. Parquet 데이터 포맷에 zstd 압축을 적용해 데이터 압축률이 높습니다. 다음 그래프에 나타난 것처럼 전체 평균 원본 데이터 대비 약 6% 수준으로 압축됩니다. 쿼리 엔진 리소스를 분리해 최소한의 규모로 운영합니다. 기존 Elasticsearch 기반 모니터링 시스템의 Warm 계층의 데이터 노드와 비교해 Trino 클러스터 규모가 더 작습니다. 다음 그래프는 데이터 적재 이후 데이터 최적화 과정의 파일 병합을 통해서 감소된 파일 비율입니다. 평균적으로 약 7.5%의 수준으로 감소했습니다. 파일 병합 작업을 통해 테이블을 최적화하지 않는다면 데이터 쓰기/읽기 측면서 시스템이 정상적으로 작동할 수 없습니다. 다음은 신규 로그 모니터링 시스템의 UI입니다. 원하는 테이블을 선택해 쿼리(Main query)를 실행하면 그 결과가 다시 Iceberg 테이블로 저장됩니다. 그 이후에 해당 결과에 여러 가지 필터를 적용해 실시간으로 데이터를 탐색할 수 있습니다. 마치며 네이버의 기존 로그 모니터링 시스템은 Elasticsearch를 기반으로 구성되었으며, 수 천대의 서버로 수 페타바이트 규모의 로그 데이터를 저장했습니다. 데이터 쿼리 패턴을 분석한 결과, 대규모 데이터 중에서 70%의 데이터는 검색이 거의 이루어지지 않는 콜드 데이터였습니다. 이런 데이터를 고비용, 고성능 저장소인 Elasticsearch에 저장해야 할지 검토하게 되었습니다. 검색이 거의 이루어지지 않지만 법적 요구 사항과 사후 분석을 위해 장기간 로그 저장에 대한 요구 사항이 많았기 때문에 새로운 저비용의 로그 검색 시스템을 구축하기로 결정했습니다. 새로운 로그 모니터링 시스템은 Iceberg라는 오픈 테이블 포맷을 기반으로 구성됩니다. 오브젝트 스토리지에 로그를 저장하는 기술을 개발하고, Trino 쿼리 엔진에 기반해 로그 검색 시스템을 구축했습니다. 새로운 저비용의 로그 모니터링 시스템으로 연간 수십억 원의 인프라 비용을 절감할 수 있는 기반을 마련할 수 있게 되었고, 새로운 방식의 SQL 로그 검색/분석 기능을 사용자에게 제공할 수 있게 되었습니다. Iceberg의 오픈 테이블 포맷을 사용한 데이터 저장은 데이터 분석 플랫폼에서는 흔하게 쓰이는 방식입니다. 하지만 로그 모니터링(observability) 측면에서 기존 로그 모니터링 시스템의 요구 사항을 만족하는 신규 시스템을 구축하는 것은 쉽지 않은 일이었습니다. 특히나 데이터 적재와 최적화를 위한 오픈 소스의 활용이 어려워 Iceberg SDK를 사용해 직접 컴포넌트를 개발해야 했습니다. Iceberg SDK에 대한 레퍼런스가 부족해 데이터를 시간 단위로 나누어 저장하고 다시 병합하며 메타데이터를 관리하는 부분의 개발은 초기 단계에서부터 많은 어려움이 있었습니다. 또한 신규 시스템의 트래픽이 사내 오브젝트 스토리지 시스템에 부하를 발생시켜, 해당 문제를 해결하는 것도 쉽지 않은 일이었습니다. 하지만 컴포넌트를 자체 개발함으로써 특정 엔진에 제한되지 않고, 최신의 Iceberg 버전을 적용할 수 있다는 점은 매우 큰 장점입니다. 신규 로그 모니터링 시스템 오픈 이후에 사용자들은 단순히 장기 보관 데이터에 대한 검색뿐만 아니라 최신 데이터에 대해서도 기존 Elasticsearch의 Lucene 쿼리로 분석하기 힘든 것을 SQL 기반으로 분석하기 시작했습니다. NELO라는 사내 로그 모니터링 플랫폼은 데이터 분석을 위한 시스템은 아니지만 Iceberg 기반의 신규 로그 모니터링 시스템이 데이터 레이크(data lake)의 데이터 소스 중 하나로 활용될 수 있기를 기대하고 있습니다. 해당 글은 N INNOVATION AWARD 2024 특집편으로 수상작 '대용량/장기간 데이터를 위한 저비용 로그 검색 시스템 : NELO Alaska'의 수상팀에서 작성해주셨습니다. N INNOVATION AWARD는 2008년부터 이어진 네이버의 대표적인 사내 기술 어워드로 매년 우수한 영향력과 성과를 보여준 기술을 선정하여 축하와 격려를 이어오고 있습니다.
기술 블로그 모음
국내 IT 기업들의 기술 블로그 글을 한 곳에서 모아보세요


AWS Blue/Green 배포의 개념과 장점, 절차에 대해 자세히 알아보겠습니다. The post Amazon RDS Blue/Green 배포란? appeared first on NDS Cloud Tech Blog.

얼마 전 저희 리멤버 의 DB서버 이전이 있었습니다. 기존엔 AWS RDS에서 MySQL을 사용하고 있었으나 AuroraDB로 서버 이전을 하였고, 손쉽게(?) 작업을 마무리 할 수 있었습니다. 이전을 할 때 데이터 소실없이 이전 하는 것이 첫 번째로 중요했고, 두 번째로 중요했던 건 서비스의 다운타임을 최소화 하는 것 이었습니다. 첫 번째로 중요했던...
DB Connection과 Garbage Collector의 관계를 중심으로 mysql-connector-j 사용 시 발생할 수 있는 메모리 누수를 탐지하고 해결한 경험을 공유합니다.
JdbcPagingItemReader와 MySqlPagingQueryProvider를 사용할 때 주의사항

안녕하세요. HBase 팀 이욱입니다. 저는 16년간 데이터베이스 엔지니어로 일해 왔습니다. DBA(database administrator)의 업무에는 다양한 작업이 있으며, 그...

※ 팀/직무 소개&인터뷰 문안 요청 해당 내용은 기술블로그에 오픈되어, 미래 구성원이 될 당사의 지원자 대상으로 공개될 내용으로 각각의 질문에 대하여 지원자 관점에서 이해도를 높이기 위하여 제작됩니다. Q. 매니저님, 팀과 자기소개 부탁드립니다. A. 안녕하세요! 데이터엔지니어링팀의 이치호 매니저입니다. 오늘은...

 # 들어가며 이번 글에서는 멀티 테넌트 서비스에서 테넌트 데이터 격리 방법과 격리 수준을 높일 수 있는 몇 가지 방법에 대해 알아봅니다. # 1. 테넌트 데이터 격리 **테넌트(tenant)**는 하나의 플...
스프링부트 버전을 업그레이드하는 과정에서 발견된 버그 해결기

안녕하세요 저는 VI Engineering 팀 김윤제입니다.Gmarket Mobile Web Vip(View Item Page = 상품 상세)를 담당하고 있는 Backend Engineer 입니다. 이번 블로그에서는 개인적으로 상품 상세 페이지에 넣고 싶었던현재 이 상품 몇 명이 보고 있어요 기능을 혼자 공부하며 개발해보는데 있어서 어떻게 설계를 해야...

전통적인 관계형 데이터베이스의 한계를 뛰어넘어, 빅데이터 분석, 확장성, 유연성을 제공하는 NoSQL의 매력을 소개합니다. 빠른 속도와 다양한 데이터 모델로 현대 비즈니스의 요구를 충족시키는 NoSQL 솔루션들을 함께 알아보세요.

(feat. WebSocket)안녕하세요. 네이버페이 내자산&회원FE 팀의 김원용입니다.저희 팀은 네이버페이 포인트는 물론, 마이데이터를 통한 다양한 금융 기관의 자산 정보를 한 곳에서 손쉽게 확인하고 관리할 수 있는, 네이버페이 내자산 서비스를 개발하고있습니다.이 글에서는 네이버페이 내자산 서비스에 대한 간략한 소개와 함께, 마이데이터 자산 조회 개발 과정을 단계별로 설명해보려고 합니다!네이버페이 내자산 서비스: https://new-m.pay.naver.com/mydata/home네이버페이 내자산 서비스 (이하 “내자산”)는 네이버페이 자산과 마이데이터 자산을 한 곳에서 조회 가능한 서비스 입니다.네이버페이 자산: - 네이버페이 포인트 - 네이버페이 머니(하나 통장) - 미래에셋 네이버통장 - 후불결제 - 우리집 - 마이카마이데이터 자산: - 은행 - 카드 - 보험 - 증권 - 대출 - 연금/IRP마이데이터는 고객이 본인의 개인신용정보를 금융회사로부터 마이데이터사업자에게 전송하도록 요구할 수 있는 권리를 의미합니다.이를 통해 사용자는 네이버페이에서 다양한 금융회사에 등록된 자신의 개인신용정보를 한눈에 조회하고 효율적으로 관리할 수 있습니다.여러 금융회사로부터 데이터를 가져온다는 개념을 바탕으로, 사용자의 모든 은행 계좌 잔액을 조회할 수 있는 은행 총 잔액 컴포넌트를 구현해보겠습니다.단순한 은행 총 잔액 컴포넌트1. 하나의 API로 모든 은행 잔액 확인하기은행 총 잔액을 표시하는 가장 간단한 방법은 한 번의 BE API 호출을 통해 데이터를 가져오는 것입니다.BE 에서는 모든 금융회사의 잔액을 병렬로 조회한 후, 이를 합산하여 응답합니다.// 은행 계좌의 총 잔액을 조회하는 함수const fetchBankTotalBalance = async () => { const bankTotalBalance = await fetch('/api/bank/total-balance'); return bankTotalBalance; // 예: 999,999,999}/* ================================================================== */// 총 잔액 조회 및 화면에 표시const bankTotalBalance = await fetchBankTotalBalance();render(bankTotalBalance);그러나, 만약 어떤 특정 금융기관의 데이터를 받아오는 데 시간이 많이 소요된다면, 이는 사용자 경험에 어떤 영향을 미칠까요?금융회사C 응답이 60초가 걸린다면 … ?2. 병렬 요청 방식마이데이터 자산 데이터는 여러 금융기관으로부터 가져오기 때문에, 특정 기관의 응답 지연은 전체 데이터 처리 속도에 영향을 미칠 수 있습니다. 예를 들어, 금융회사 C의 응답이 지연될 경우, 사용자는 전체 잔액 정보를 볼 수 없게 됩니다.이를 개선하기 위해 FE 에서는 금융회사별로 병렬 요청을 보내는 방식을 도입했습니다.FE 에서 금융회사별로 병렬 요청합니다.// 각 금융회사의 잔액 정보를 저장하는 객체const bankBalanceInfo = { 금융회사A: null, 금융회사B: null, 금융회사C: null,}// 개별 은행 계좌의 잔액을 조회하는 함수const fetchBankBalance = async (bankCode) => { const bankBalance = await fetch(`/api/bank/balance/${bankCode}`); return bankBalance; // 예: 100,000,000}// 금융사별 잔액 업데이트 및 화면에 표시const updateAndRender = (bankCode, balance) => { bankBalanceInfo[bankCode] = balance; render(sum(bankBalanceInfo));}/* ================================================================== */// 금융회사 병렬 요청Object.keys(bankBalanceInfo).forEach(async (bankCode) => { const bankBalance = await fetchBankBalance(bankCode); updateAndRender(bankCode, bankBalance);})이 방식을 통해 빠르게 응답을 받은 금융회사의 잔액 정보부터 합산하여 화면에 표시하게 되므로, 사용자는 신속하게 전체 정보를 확인할 수 있습니다.그러나, 만약 여러 금융기관에서 지연이 생기면, 이는 사용자 경험에 어떤 영향을 미칠까요?금융회사A 만 빠르게 온다면 … ?3. 캐시된 정보 활용여러 금융기관에서 데이터를 가져오는 과정에 지연이 생길 때, 병렬 요청 방식을 사용해도 전체 잔액 정보에 일시적인 오차가 생길 수 있습니다. 예를 들어, 전체 잔액이 999,999,999원인데 금융회사 A의 100,000,000원만 먼저 오면, 화면에는 100,000,000원만 표시되고, 지연 후에 서서히 999,999,999원으로 업데이트됩니다. 이것은 사용자에게 좋지 않은 경험을 줄 수 있습니다.이를 개선하기 위해 BE 에서 마지막으로 조회한 금융회사 데이터를 저장하고, FE 에서는 이 저장된 잔액을 먼저 보여줌과 동시에 최신 잔액으로 업데이트하는 방식을 도입했습니다.캐시된 잔액을 우선적으로 화면에 노출시키고, 병렬 요청 방식으로 금융회사의 최신 잔액으로 갱신합니다.// 각 금융회사의 잔액 정보를 저장하는 객체const bankBalanceInfo = { 금융회사A: null, 금융회사B: null, 금융회사C: null,}// 캐시된 총 잔액 정보를 먼저 가져오는 함수const fetchCachedBankTotalBalance = async () => { const cachedBankTotalBalance = await fetch('/api/bank/cached-total-balance'); return cachedBankTotalBalance; // 예: { 금융회사A: 100,000,000원, 금융회사B: 200,000,000원, ... }}// 개별 은행 계좌의 잔액을 조회하는 함수const fetchBankBalance = async (bankCode) => { const bankBalance = await fetch(`/api/bank/balance/${bankCode}`); return bankBalance; // 예: 100,000,000}// 금융사별 잔액 업데이트 및 화면에 표시const updateAndRender = (bankCode, balance) => { bankBalanceInfo[bankCode] = balance; render(sum(bankBalanceInfo));}/* ================================================================== */// 먼저 캐시된 총 잔액 정보를 화면에 표시const cachedBankTotalBalance = await fetchCachedBankTotalBalance();render(sum(cachedBankTotalBalance));// 금융회사 병렬 요청Object.keys(bankBalanceInfo).forEach(async (bankCode) => { const bankBalance = await fetchBankBalance(bankCode); updateAndRender(bankCode, bankBalance);})이 방법을 통해 사용자는 더욱 신속하고 안정적으로 은행 잔액 정보를 확인할 수 있게 되었습니다!4. WebSocket 적용으로 더 나아가기지금까지 3개 금융회사의 은행 잔액 조회 예시를 살펴봤습니다. 그러나 “내자산” 서비스는 은행뿐만 아니라 카드, 증권 등 다양한 금융회사의 데이터 조회도 지원합니다. 이는 갱신을 위한 HTTP API 요청의 증가를 의미합니다.자산이 100개라면 … ?이러한 상황을 개선하기 위해, Socket.IO 기반의 사내 플랫폼을 활용하여 WebSocket을 적용하였습니다.WebSocket 과 Socket.io 에 대한 자세한 내용은 NAVER D2 포스팅에서 확인 가능합니다.WebSocket은 한 번의 연결 설정으로 지속적인 데이터 교환을 가능하게 합니다. 이는 기존의 HTTP 요청 방식과 비교할 때 큰 이점을 제공합니다. HTTP 방식에서는 각 요청마다 헤더와 추가 데이터를 보내야 하지만, WebSocket은 초기 연결 설정 후에는 이러한 오버헤드 없이 데이터를 전송할 수 있습니다. 이는 특히 많은 양의 데이터를 실시간으로 주고받아야 하는 경우, 더욱 효율적입니다.한 번의 갱신 요청 API를 통해 전체 금융회사에 대한 데이터 요청을 하고, 응답은 조회가 완료된 금융회사 데이터부터 WebSocket을 통해 받습니다. 이 방식은 사용자가 신속하고 원활하게 실시간 데이터를 수신할 수 있게 해줍니다. 이 접근법은 네트워크 트래픽과 서버 부하를 줄이는 동시에 사용자에게 빠른 반응 속도를 제공하는 이점이 있습니다.한 번의 갱신 API 호출 후, Socket.IO 를 통하여 여러개의 응답을 받습니다.// 각 금융회사의 잔액 정보를 저장하는 객체const bankBalanceInfo = { 금융회사A: null, 금융회사B: null, 금융회사C: null,}// 캐시된 총 잔액 정보를 먼저 가져오는 함수const fetchCachedBankTotalBalance = async () => { const cachedBankTotalBalance = await fetch('/api/bank/cached-total-balance'); return cachedBankTotalBalance; // 예: { 금융회사A: 100,000,000원, 금융회사B: 200,000,000원, ... }}// 전체 은행 잔액을 갱신 요청const updateRequestBankTotalBalance = async () => { await fetch('/api/update-request/bank/total-balance');}// 금융사별 잔액 업데이트 및 화면에 표시const updateAndRender = (bankCode, balance) => { bankBalanceInfo[bankCode] = balance; render(sum(bankBalanceInfo));}/* ================================================================== */// WebSocket을 사용하여 갱신된 은행 잔액을 실시간으로 받을 준비const socket = io();socket.on("updateComplete", (bankCode, balance) => { updateAndRender(bankCode, balance);})// 먼저 캐시된 총 잔액 정보를 화면에 표시const cachedBankTotalBalance = await fetchCachedBankTotalBalance();render(sum(cachedBankTotalBalance));// 전체 은행 잔액을 갱신 요청updateRequestBankTotalBalance();이 방법을 통해 사용자는 더욱 신속하고 안정적으로 은행 잔액 정보를 확인할 수 있게 되었습니다!“내자산” 에서 WebSocket을 단방향 통신에만 사용하여, 응답 결과를 수신하는 데에 한정하고 있습니다. 이는 WebSocket의 양방향 통신 능력을 완전히 활용하지 못하고 있음을 나타냅니다. 실제로, 단방향 통신 상황에서는 Server-Sent Events(SSE)를 도입하는 것이 더 적합한 대안이 될 수 있습니다. 또한, WebSocket을 통한 데이터 통신의 효율성을 높이긴 했지만, 일부 데이터는 여전히 별도의 HTTP 요청을 통해 수집되는 상황이 발생하고 있습니다. 이는 향후 개선이 필요한 영역입니다.단순하지 않은 은행 총 잔액 컴포넌트지금까지 은행 총 잔액 컴포넌트 구현을 위해 기본 API 요청부터 WebSocket 통신에 이르기까지 단계별 과정을 살펴보았습니다. 단순한 기술적 최적화를 넘어서, 사용자 경험의 지속적인 향상에도 주의 깊게 노력하고 있습니다.이러한 점진적 개선 방식과 사용자 중심의 접근이 데이터 처리와 최적화를 고민하는 다른 개발자분들에게 조금이나마 도움이 되었기를 바랍니다.이 글을 읽어주셔서 감사합니다! 네이버페이 내자산&회원FE 팀은 긍정적인 에너지와 열정을 가진 팀원들이 모여, 지속적으로 성장하고 발전하는 환경을 조성하고 있습니다.이런 동기부여되는 분위기에서 함께 일할 새로운 팀원을 항상 찾고 있습니다. 저희 팀과 함께 성장하고 싶으신 분들은 언제나 환영합니다!채용: https://recruit.naverfincorp.com/내자산: 마이데이터 자산 조회 was originally published in NAVER Pay Dev Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

쿠키런: 오븐브레이크 팀에서 MySQL을 사용하면서 있었던 사례에 대해 공유합니다
안녕하세요, 뱅크샐러드 Core Infra 팀의 Data engineer 김문수 입니다. 점점 커지는 이력성 데이터를 MySQL에서 더 저렴한 저장소인 S3로 옮기면서도, 서비스에서 호출할 때 딱 필요한 데이터만 읽을 수 있도록 bucketing…
컬리 데이터 파이프라인의 BigQuery 도입 결과 및 효과
컬리 데이터 파이프라인의 BigQuery 도입 배경과 그 주안점

DB를 만든 회사에서 복구는 불가능하다고 얘기하는 극한의 상황 속에서 CTO가 커리어를 걸고 비트 레벨까지 내려가서 DB를 해킹했던 이야기를 소개합니다.

쿠키런: 킹덤 런칭 후 4일 만에 기술적인 문제로 인해 발생했던 약 36시간의 장애에 대해 전해드리고자 합니다.

2017년에 시작된 올리브영 온라인몰 서비스는 매년 급격한 성장세를 보이며 국내 H&B…

CockroachDB의 여러 가지 고유한 특성을 알아보고, 왜 CockroachDB를 메인 데이베이스로 선택했는지, Production 서비스에서 CockroachDB를 운용하기 위해 어떤 점들을 주의하고 신경써야 하는지에 대해 다룹니다.

2021년 12월 9일 MariaDB Corporation Ab와 MariaDB Foundation은 MariaDB Community Server의 새로운 릴리즈 모델을 발표했습니다. 발표된 릴리즈 모델은 MariaDB Community Server 10.8 버전부터 적용되며 변경된 정책의 가장 중요한 내용은 앞으로 분기별로 새로운 버전의 MariaD...

엔터프라이즈 데이터 웨어하우스(EDW)는 전사적으로 모든 역사적 데이터를 저장하는 통합 데이터베이스로 분석에 최적화돼 있습니다. 최근, 데이터 웨어하우스를 구축하는 기업은 온프레미스보다 클라우드에 데이터 웨어하우스를 구축하는 경우가 많은데요. 또한, 전통적인 데이터 웨어하우스 대신 쿼리를 지원하는 데이터 레이크를 활용합니다. 이밖에 역사적 데이터와 스...
DynamoDB를 소개하며 RDBMS ERD기반 설계에서 DynamoDB로 Reverse Modeling하는 방법을 공유합니다. 1. DynamoDB 도입 배경 현재 스타트줌(https://start.zum.com/)에서 개인 별 배경화면 / 레이아웃 / 위치설정 / 사이트 바로가기를 설정할 수 있습니다. 해당 데이터들은 브라우저 로컬 스토리지에 저장...

아마존에서는 데이터를 잘 관리하는 방법을 많이 고민하는데요. 회사 사업규모가 커지면서 데이터가 방대하게 쌓였습니다만. 이를 관리하는 데 어려움이 있었다고 합니다. 워너 보겔스 아마존닷컴 최고기술책임자(CTO)는 미국 IT 매체 ‘실리콘 앵글’ 기고 글에서 이렇게 설명했는데요. 아마존에서는 데이터 사일로(Data Silo), 다양한 데이터 세트 분석, ...
DB에 저장을 했는데, 조회가 안 돼요
DDD를 위한 첫걸음. Event Storming

많은 기업들이 Hadoop과 Cloud기반의 빅데이터 기술을 사용해 Data Lake의 확장형 아키텍처로 전환하고 있다. 그러나 Data Lake와 빅데이터 기술에 대한 투자 증가에도 불구하고 기업의 일부만이 PoC를 거친 후 운영계에 배치되고 있다. 이런 불일치 원인 중 하나는 많은 기업이 빅데이터 기술과 인프라에 대한 초기 투자 수익을 회수하지 못...

우리는 일상 생활에서 무수히 많은 데이터를 생산하고 있습니다, 길을 걸으며 누군가와 메시지를 주고받거나 통화할 때, 쇼핑몰에서 물건을 살 때나, 지하철을 탈 때, 그리고 휴대폰을 통해 드라마를 볼 때도 연관된 많은 데이터가 만들어지고 저장되고 분석됩니다. 시장 조사 기관인 IDC (International Data Corporation)에 따르면 매년...
![[성과를 창출하는 데이터 품질 관리] ② 데이터 품질관리 시스템 구축 사례](https://static.teamblind.com/img/channel/logo/kr/6d491226862341.png)
기업이 관리하는 데이터의 규모가 무한하다고 느껴질 만큼 방대해지고 있어, 적합한 품질관리 대상 데이터를 선정하는 것은 비즈니스 성패를 결정 짓는 중요한 요소로 작용합니다. 전사에서 운영하는 모든 정보시스템과 데이터를 대상으로 품질관리를 한다면 정보를 분석하고 나열하는 것만으로도 수많은 자원과 시간이 필요하게 됩니다. 이는 결국 실행 불가능한 이상적 계...
![[성과를 창출하는 데이터 품질 관리] ① 데이터 품질 고도화를 위한 관리 체계](https://static.teamblind.com/img/channel/logo/kr/6d491226862341.png)
업무 자동화를 위한 애플리케이션 세분화, 모바일 디바이스와 정보시스템 간 연계 등으로 기업이 관리하는 데이터 볼륨은 매년 급속도로 증가하고 있으며, 데이터의 소스나 형태도 신기술을 반영하여 다양해지고 있습니다. 이에 따라 기업의 데이터 관리정책도 변화를 거듭하고 있습니다. 과거에는 '방대한 데이터 확보'가 우선시 되었지만, 근래 들어서는 '기업 경쟁력...