🍋

마케팅 카탈로그에 대한 글

마케팅 카탈로그(Marketing Catalog)는 마케팅 도메인 내에서 사용하는 카탈로그를 의미한다. 이 마케팅 카탈로그의 탄생부터 현재 시점까지 정리해보고자 한다.

마케팅 카탈로그는 일반적인 카탈로그와 다르다. 일반적인 카탈로그는 상품의 이름, 가격, 이미지뿐만 아니라 상품의 다양한 속성을 가지고 있다. 하지만 마케팅 영역에서는 이 일반적인 카탈로그의 모든 속성을 필요로하지 않는다. 오히려 상품의 이름, 가격, 이미지만으로도 충분할 수 있다. 카탈로그의 데이터를 마케팅 도메인 관점에서 사용하기 위해 마케팅 카탈로그를 만들었다.

마케팅 카탈로그 탄생에는 한 가지 이유가 더 있다. 도메인 영역(scope)이 다르기 때문이다. 일반 카탈로그를 다루는 카탈로그 도메인이 있다면 마케팅에 집중하는 마케팅 도메인이 있다. 서로 다른 도메인에서 하나의 데이터를 사용할 때 어떻게 접근을 해야하는지 많은 의견이 있다. 하지만 해당 시점에서는 마이크로 서비스 구조(MSA)를 옮기고 있었기 때문에 서로 다른 두 도메인 하나의 데이터 스토리지에 강하게 의존하지 않으려고 노력했다.

마케팅 카탈로그 - MySQL

마이크로 서비스 아키텍쳐(MSA) 지향과 마케팅 도메인에서 카탈로그 사용이 증가하면서 마케팅 카탈로그가 탄생했다. 처음 구축하는 과정에 참여하지 않아서 왜 MySQL을 사용했는지는 알 수 없지만, 당시에 자주 사용하고 있던 MySQL(RDS)에 구축한 것으로 믿고 있다.

마케팅 카탈로그는 일반 카탈로그 데이터에 대한 복사본이다. 그렇다면 어떻게 이 복사본의 카탈로그를 만들 수 있을까. 카탈로그 팀에서는 이 카탈로그 데이터를 두 가지 채널로 제공하면서 다른 도메인에서도 사용할 수 있도록 하였다. 카탈로그 팀은 날 마다 카탈로그의 전체 데이터를 하이브(Hive)로 동기화했다. 다른 도메인 팀은 하이브를 통해 전체 데이터를 쉽게 접근할 수 있었다. 하지만 이 전체 데이터를 실시간 데이터를 반영하지 못했다. 그 말은 하이브를 통해 카탈로그 전체 데이터를 접근할 수 있었지만, 실시간으로 변경된 데이터를 조회할 수 없었다. 이 제약사항을 해결하기 위해 카탈로그 팀은 변경된 데이터를 카프카(Kafka)를 통해 실시간으로 제공했다.

마케팅 카탈로그는 하이브를 이용하여 전체 데이터를 가져와 카탈로그 데이터를 저장했다. 이와 동시에 카프카를 통해 변경된 데이터를 수신하여 카탈로그 데이터를 실시간으로 업데이트했다. 필요에 따라 카탈로그 데이터와 복사본인 마케팅 카탈로그 사이의 데이터 검증 작업이 이뤄졌다. 두 카탈로그 사이의 데이터 무결성이 깨지지 않도록 때때로 복구 작업을 진행했다. 

마케팅 카탈로그와 일반 카탈로그를 따로 분리하면서 다른 도메인과의 의존성을 제거할 수 있었다. 그러나 마케팅 카탈로그를 실제 사용 및 운영하면서 몇가지 문제가 발생했다. 데이터의 스키마 변경이 쉽지 않았고 읽기 성능이 크게 떨어졌다.

카탈로그의 구조부터 살펴보자. 카탈로그는 단 하나의 구조, 테이블로 이뤄지지 않았다. 카탈로그는 크게 총 3개의 데이터로 이뤄졌다. 프라덕트(Product), 아이템(Item), 벤더 아이템(Vendor Item)으로 이뤄졌다. 프라덕트와 아이템은 1:N 관계, 아이템과 벤더 아이템도 1:N 관계로 이뤄졌다. 하나의 프라덕트 하위에 다수의 아이템이 존재했고, 하나의 아이템 하위에 다수의 벤더 아이템이 존재했다. 그리고 각각의 프라덕트, 아이템, 벤더 아이템은 상품의 이름, 이미지, 가격 등의 데이터를 가지고 있다.

데이터 구조 변경에 대한 문제

데이터 스키마 변경은 기존에 사용하지 않았던 데이터를 비즈니스 목적에 따라 추가로 사용하면서 필요했다. 예를 들어, 기존에는 마케팅을 위해 상품의 브랜드 데이터를 사용하지 않았다고 생각하자. 이에 따라 마케팅 카탈로그는 일반 카탈로그에서 브랜드 데이터를 따로 저장하지 않는다. 이후에 마케팅을 위해 브랜드 데이터를 추가로 사용한다면 마케팅 카탈로그는 브랜드 데이터를 추가로 저장해야한다. 데이터 구조(Schema) 변경 작업은 자주 있지 않았지만 비즈니스 목적에 따라 반드시 필요했다. 

데이터 스키마 변경이 문제인 이유는 하나의 컬럼을 추가하는데 최소 10시간이 걸렸기 때문이다. 테이블의 데이터가 억 단위로 존재했다. 데이터가 많은 테이블에 컬럼 추가 작업은 상당히 오래 걸렸다. 컬럼을 추가하는 과정에서 락(lock)이 발생했기 때문에 이 시간 동안에는 데이터를 읽기/쓰기가 불가능했다. 컬럼을 추가했더라도 데이터에 대한 백필(Back-fill)작업도 오래걸렸다. 일반 카탈로그 전체를 다시 읽어서 백필 작업을 진행해야 했기 때문이다. 데이터 스키마 변경 과정은 긴 시간 동안 마케팅 카탈로그 시스템 전체에 영향을 주었다.

데이터 읽기에 대한 문제

프라덕트와 그 하위에 있는 모든 아이템과 벤더 아이템을 읽는 작업이 있다. 하나의 프라덕트 밑에 많은 수의 아이템과 벤더 아이템이 존재한 경우, 데이터를 읽는 작업이 너무 오래 걸렸다. 데이터 읽기 작업이 슬로우 쿼리로 마킹되면서 5분이 넘게 수행되면 시스템이 해당 작업을 강제로 중지시켰다. 이렇게 강제로 데이터 읽기 작업이 실패하면 해당 데이터는 마케팅에 활용할 수 없었다.

데이터 읽기 작업이 다른 쿼리에 맞물리면서 블락킹이 되면서 읽기 작업이 실패했다. 키(ID) 기준으로 단 한 건의 데이터를 읽는 작업이라 오래 걸리는 작업이 아니다. 그러나 다른 작업에서 상당히 긴 트랙잭션(Transaction)을 가지고 수행되고 있을때, 해당 읽기 작업이 블락킹되어 실패했다.

마케팅 카탈로그 - Elasticsearch

마케팅 카탈로그에서 데이터 구조 변경과 읽기 작업에 대한 문제를 겪으면서 개선 작업이 필요했다. 최대한 MySQL에서 개선 작업을 진행했지만 시스템이 원하는 기준까지는 미치지 못했다. 그래서 마케팅 카탈로그를 새로운 데이터 스토리지(Storage)를 통해 재구축하는 작업을 진행했다. 마케팅 카탈로그를 재구축하기 전, 시스템의 요구 사항과 이에 부합하는 스토리지를 찾는 작업을 진행했다.

시스템 요구 사항

  • 읽기/쓰기가 키 값으로 진행되어야 하고 상당히 높은 I/O가 보장되어야 함.
  • 스키마 변경이 쉽고 이에 따라 시스템 대응이 쉬어야 함.
  • 읽기 작업이 키 범위(Range) 조회가 가능해야 함.
  • 키 값뿐만 아니라 다른 컬럼에 대해서도 인덱스가 가능해야 함.

기존의 RDS(MySQL)로 문제를 겪었고 NoSQL에 대한 인지도 상승도 한 몫하여 NoSQL 기반의 스토리지를 살펴보았다. Cassandra, HBase, MongoDB, DynamoDB, Elasticsearch 등 다양한 스토리지 중에서 회사에서 지원하는 팀이 있는 Cassandra, HBase에 대해서 먼저 살펴봤다. Cassandra와 HBase의 특징을 간략하게 살펴보자.

카산드라(Cassandra)

  • 키 - 값(Key - Value) 이루어진 데이터 적합하다.
  • 카산드라는 링(Ring) 구조로 이뤄져 있고 복제(Replication)을 지정할 수 있어 가용성이 뛰어나다.
  • 조인(Join)을 할 수 없다.
  • 키가 아닌 다른 컬럼으로 조회하거나 범위(Range) 조회를 할 수 없다.

HBase

  • HDFS로 구성되어 Hive와 통합이 가능하다.

  • 범위(Range) 조회가 가능하다.

  • 카산드라의 일관성(Consistency)보다 더 높은 일관성을 가질 수 있다.

  • 링(Ring)구조가 아니라 마스터-슬레이브(Master-Slave)구조다.

시스템 요구 사항을 고려해볼 때, HBase가 새로운 스토리지로 적합하다고 판단했다. 그러나 팀 내의 의견 외에도 마케팅 도메인 내의 다른 팀에서 Elasticsearch에 대한 추가 제안이 들어왔다. 해당 요구 사항을 만족하면서 다른 팀으로부터 Elasticsearch에 대한 도움을 받을 수 있어 Elasticsearch로 마케팅 카탈로그 재구축 작업을 진행했다.

Elasticsearch의 마케팅 카탈로그는 MySQL 보다 시스템 요구 사항에 더 적합했다. 키뿐만 아니라 다른 컬럼에 대해서도 조회가 가능했고, 범위(Range) 조회도 가능했다. 데이터 스키마 변경 작업도 스키마 변경 후, 백필 작업만 진행하면 되었기에 수월했다. 그러나 항상 완벽한 시스템은 없듯이 Elasticsearch의 마케팅 카탈로그에서도 문제가 드러났다. 

Re-indexing의 필요성

Elasticsearch에서 전체 데이터를 넣는 작업을 인덱싱(Indexing)라 한다. Re-indexing은 말그대로 인덱싱을 작업을 다시 수행하는 것이다. Elasticsearch를 운영하면서 주기적으로 re-indexing 작업이 필요했다. Elasticsearch에서의 delete와 update 작업은 실제 데이터를 삭제하는 것이 아니라 해당 데이터에 delete 마크를 한다. 이를 tombstone라고 부르는데, 실제 데이터를 삭제하는 것이 아니기 때문에 실제 데이터 볼륨을 차지하고 있다. 이 tombsone을 제거하기 위해서 전체 데이터를 re-indexing을 진행했다.

카탈로그 데이터는 계속 증가한다. 상품의 셀렉션이 많아지면 많아 질수록 카탈로그 데이터도 많아진다. Elasticsearch에서 데이터 노드에 대해서 추가가 가능하지만 실제로 해당 데이터 노드를 바로 사용할 수 없었다. 데이터가 많아지면서 인덱싱의 샤드(Shard)도 늘려주어야한다. 샤드를 늘렸을 때, 이를 적용하기 위해서는 re-indexing이 필요하다.

낮은 읽기 성능

Elasticsearch에서 데이터를 읽을 때, 특정 시점에 성능이 급격히 떨어지는 상황이 발생했다. 데이터 무결성을 위해 일반 카탈로그와 마케팅 카탈로그 사이의 데이터 복구 작업이 필요하다. Elasticsearch 마케팅 카탈로그에서도 이 복구 과정이 진행되었는데 실시간으로 진행해되는 작업이 아니라 특정 시점에 데이터를 벌크(Bulk)로 처리하는 배치로 진행했다. 이 벌크 작업이 진행하는 시점에서 다른 애플리케이션에서 데이터를 읽을 때 소요 시간이 오래 걸리는 문제가 발생했다. 읽기 성능에 대해 요구 사항을 달성하지 못하고 있었고 해당 문제가 실제 서비스까지 부정적 영향을 주었다.

마케팅 카탈로그 - Cassandra

Elasticsearch 마케팅 카탈로그에서 낮은 읽기 성능에 대한 문제를 중점적으로 해결하는 것이 목표였다. 그래서 문제의 원인이었던 배치 작업을 없애는 것에 중점을 두었다. 이 배치 처리는 마케팅 카탈로그에 대한 복구 작업이었다. 그렇다면 복구 작업이 필요없게끔 만들면 되지 않을까. 

이전까지는 자바 애플리케이션으로 실시간 데이터인 카프카 이벤트를 처리했다. 그러나 이 이벤트 처리 과정에서 시스템에 문제가 발생했을 때, 데이터 유실이 있었다. 물론 데이터를 완벽하게 처리하고 카프카에 커밋(Commit)을 하면 해당 데이터를 재처리할 수 있지만, 자바 애플리케이션에서 사용한 내부 카프카 클라이언트 라이브러리에서 그 부분이 불가능했다. 카프카 이벤트를 유실없이 처리할 수 있도록 애플케이션 변경 작업을 진행했다. 기존의 스프링 프레임워크 기반의 애플리케이션에서 플링크 프레임워크 기반의 애플리케이션으로 변경했다. 플링크(Flink) 프레임워크에서는 내부적으로 반드시 데이터를 문제없이 처리했다는 것을 보장하는 체크포인트(Checkpoint)를 가지고 있었다. 따라서 플링크 애플리케이션에서 카프카로부터 처리하는 데에 데이터 유실이 없다는 것을 보장할 수 있어 주기적인 데이터 복구 작업을 제거했다. 

이번에 애플리케이션을 변경하면서 스토리지도 변경했다. 기존의 elasticsearch에서의 re-indexing 작업이 시스템 운영 측면에서 부담을 주고 있었기 때문이다. 링(Ring) 구조로 데이터 노드 추가가 쉽고 따로 전체 re-indexing이 필요로 하지 않는 카산드라(Cassandra)를 선택했다. 카산드라 마케팅 카탈로그는 현재까지 큰 문제 없이 운영하고 있다.

마치며

사실 마케팅 카탈로그는 마케팅 도메인의 비즈니스에 따라 바로 탄생하지 못했다. Time to marketing이라는 명목하에 마케팅 카탈로그 없이 일반 카탈로그를 직접 접근했다. 한마디로 마케팅 팀에서 만든 애플리케이션이 카탈로그 팀에서 관리하는 데이터베이스에 직접 접근한 것이다. 이후에 다행히도 마케팅 비즈니스의 성과가 좋았기 때문에 마케팅 카탈로그를 만들 수 있었다. 실제 시장에서 나오기까지의 A-Z까지 완벽한 시스템은 없지만 이처럼 도전적으로 진행했던 것은 큰 경험이었다.

Elasticsearch 마케팅 카탈로그를 운영하면서 re-indexing 작업은 결코 쉬운 작업은 아니었다. 데이터가 커져서 샤드 수를 조정할 때, 데이터 노드를 추가할 때, Tombstone이 많아져 데이터 볼륨을 적게 만들 때의 상황을 주기적으로 살펴봐야 했다. Re-indexing 과정도 데이터가 많을 때에는 수 시간이 소요되었다. 

MySQL에서부터 Elasticsearch, Cassandra까지 마케팅 카탈로그를 구축하고 운영하는 작업을 진행했다. 3개의 데이터 스토리지를 경험하면서 많은 어려움과 도전적인 과제를 진행했다. 아직 이 3개의 스토리지에 대해서 확실히 안다고 자부할 수 없지만, 마케팅 카탈로그를 운영하면서 각각의 특징점은 어느정도 알게 되었다. 그리고 실제 데이터 스토리지를 선택할 때, 단순히 하고 싶어서 선택하는 것이 아니라 시스템 요구 사항과 데이터 특성을 고려해야 한다는 점을 알게 되었다. 이전에도 사용하고 경험이 많다고 해서 항상 그 선택이 최고의 선택은 아니다.