신입의 발견

10

발행일: 2025년 06월 03일

팀은 전쟁에 돌입했다. 사무실은 밤늦도록 불이 꺼지지 않았고, 화이트보드는 각 시스템 구간별로 측정된 지연 시간 값들로 가득 찼다. 하지만 원인은 오리무중이었다.

“네트워크 자체는 문제가 없어. 데이터센터 간 핑(ping)은 수 밀리초에 불과해.”
“경매 로직? 단순한 최고가 비교 알고리즘인데, 마이크로초 단위에서 끝나.”
“데이터베이스 조회? 알렉스가 만든 모듈도 캐싱(caching)을 적용하면 병목이 될 리가 없어.”

모든 시니어 엔지니어들은 자신들이 구축한 복잡한 시스템 아키텍처와 네트워크 토폴로지(topology) 사이를 헤매고 있었다. 그들은 더 거대하고, 더 복잡한 곳에 문제가 있을 거라 확신했다. 마치 거대한 댐의 누수 원인을 찾기 위해 댐의 구조 설계 도면만 들여다보는 것과 같았다.

알렉스는 회의에서 한 발짝 떨어져 있었다. 그는 아직 전체 시스템을 속속들이 알지 못했고, 시니어들의 대화에 끼어들기 어려웠다. 대신 그는 가장 기본적인 것을 다시 들여다보기로 했다. 바로 ‘데이터’ 그 자체였다.

‘애드 익스체인지와 DSP 사이를 오가는 데이터는 어떤 모습일까?’

그는 자신이 작성했던 DSP 연동 규격서를 다시 열었다. 거기에는 DSP에게 보내는 입찰 요청(Bid Request)에 담겨야 할 수많은 정보들이 정의되어 있었다.

  • user_id: 사용자를 식별하는 암호화된 ID
  • ip_address: 사용자의 IP 주소
  • url: 사용자가 보고 있는 페이지의 주소
  • ad_unit_id: 광고 지면의 ID
  • size: 광고 지면의 크기 (e.g., “300x250”)
  • … 등등 수십 개의 필드.

시스템은 이 정보들을 ‘XML(Extensible Markup Language)’ 형식으로 변환하여 DSP로 전송하고 있었다. XML은 당시 웹 서비스 간 데이터 교환의 표준과도 같은 방식이었다. 사람이 읽기 쉽고, 구조가 명확하다는 장점이 있었다.

알렉스는 실제 테스트에서 생성된 XML 데이터를 살펴보았다.

<BidRequest>
  <user_id>a1b2c3d4e5f6</user_id>
  <ip_address>123.45.67.89</ip_address>
  <url>http://globalherald.com/sports/main</url>
  ...
</BidRequest>

데이터 자체에는 문제가 없어 보였다. 하지만 알렉스의 머릿속에 작은 의문이 피어올랐다.

‘0.1초 안에 수십만 건을 처리해야 하는 시스템이다. 그런데 기계가 읽을 데이터를, 굳이 사람이 읽기 편한 형태로 만들 필요가 있을까?’

그는 <BidRequest>, </BidRequest>, <user_id>, </user_id> 같은 태그(tag)들을 유심히 보았다. 이 태그들은 데이터의 구조를 설명하기 위해 존재했지만, 정작 실제 데이터 값(a1b2c3d4e5f6)보다 훨씬 더 많은 자릿수를 차지하고 있었다. 데이터의 본질보다 포장지가 더 큰 셈이었다.

이 거대한 XML 텍스트를 생성하는 과정(직렬화, Serialization)과, DSP 측에서 다시 이 텍스트를 해석하여 데이터를 추출하는 과정(역직렬화, Deserialization)에서 보이지 않는 시간 낭비가 일어나고 있을지도 모른다는 생각이 들었다.

그는 조심스럽게 사라에게 다가갔다.

“사라, 혹시… 데이터 직렬화 과정에서 소요되는 시간을 측정해 본 적이 있습니까?”

사라는 복잡한 네트워크 다이어그램에서 눈을 떼고 의아한 표정으로 알렉스를 보았다.

“직렬화? 그건 CPU 연산이라 거의 시간이 걸리지 않아. 병목은 네트워크 I/O(입출력)나 디스크 I/O에서 생기는 거지, CPU 내부에서 생기는 게 아니야.”

경험 많은 엔지니어의 일반적인 답변이었다. 하지만 알렉스는 포기하지 않았다.

“하지만 초당 수십만 건을 처리해야 한다면, 그 작은 시간들이 모여서 큰 차이를 만들 수도 있지 않을까요? 특히 XML처럼 텍스트 기반의 비효율적인 포맷을 사용한다면요.”

그의 끈질긴 태도에 사라는 잠시 생각에 잠겼다. 그녀는 신입의 엉뚱하지만 논리적인 의심을 무시하지 않았다.

“좋아, 알렉스. 네 말이 맞는지 한번 보자. 네가 직접 증명해봐.”

알렉스는 즉시 작은 테스트 코드를 작성했다. 동일한 데이터를 XML 형식과, 구글 내부에서 사용하던 훨씬 간결한 이진(binary) 데이터 포맷인 ‘프로토콜 버퍼(Protocol Buffers)’ 형식으로 각각 백만 번씩 생성하고 파싱하는 데 걸리는 시간을 측정하는 코드였다.

결과는 충격적이었다.

XML 방식 평균 처리 시간: 780 마이크로초
프로토콜 버퍼 방식 평균 처리 시간: 25 마이크로초

무려 30배 이상의 성능 차이.

개별적으로는 1밀리초도 안 되는 미미한 시간이었지만, 이 연산이 경매 요청 한 건당 여러 번 반복되고, 그것이 초당 수만 건씩 쌓인다면, 그 차이는 수백 밀리초에 달하는 거대한 눈덩이가 될 수 있었다.

모두가 댐의 구조적 결함을 찾고 있을 때, 신입사원 알렉스는 댐의 수문을 통과하는 물 분자 자체의 비효율성을 발견한 것이다. 그는 떨리는 손으로 결과 데이터를 정리해 데이비드와 사라에게 보냈다.