Hyperconnect Tech Blog 최신 글
hyperconnect.github.io에서 발행한 기술 글을 테크곳간이 수집해 최신순으로 보여드립니다.
최신 글
2부: 정책을 따르는 평가자, LLM-as-a-Judge
이 글은 1부: 데이터도 정답도 없다에서 이어집니다. 1부에서는 Tinder의 AI-enabled Discovery 기능에서, “당신은 왜 이 사람과 잘 맞을까요?”라는 질문에 답하는 설명을 만들기 위한 정책 수립 과정을 다뤘습니다. 이번 글에서는 그 정책을 실제 평가 시스템으로 바꾸는 방법을 다룹니다. 들어가며 지난 글에서 Tinder와 MGAI 팀이 합작한 Tinder AI-enabled Discovery 서비스의 “소개팅 주선자” 모듈을 소개했습니다. 즉 LLM을 활용해 “당신은 이래서 이 사람과 잘 맞습니다”를 설득하는 문장을 만들어내는 모듈이죠. 예를 들어 자전거와 술을 좋아하는 유저 A를 따릉이를 좋아하는 B 유저에게 소개할 때 “A는 자전거를 좋아해요!”라고 소개합니다. 위스키를 좋아하는 C 유저에게 소개할 때는 “A는 술을 즐겨요”라고 소개하고요. 이런 LLM 모델을 만들기 위해선 엄밀한 “설명 정책”이 필요합니다. 정책에는 제품적 의사결정 (어느 상황에서 어떤 설명이 좋은지), 그리고 기술적 의사결정 (LLM이 잘 이해할 수 있는지)을 잘 녹여내야 합니다. 두 조건을 모두 만족하는 정책을 만드려면 반복 평가 과정을 통해 다듬어야 하고, PM과 MLE의 끈끈한 협력 과정이 필요함을 지난 1부에서 설명 드렸습니다. 이런 깔끔한 설명 정책을 확보하면, 사람은 LLM이 생성한 설명의 pass/fail 여부를 충분히 일관되게 판단할 수 있습니다. 정책만 제대로 익혔다면, 전문 평가자로서 모델의 성능을 일관된 기준으로 평가하는 능력을 얻는 것입니다. 그리고 일관된 평가를 바탕으로 모델의 성능을 정량화 하고, 모델 배포를 결정할 수 있습니다. 그러나 모델은 한번 배포되고 끝이 아닙니다. 배포된 모델이 잘 동작하는지 지속적으로 모니터링 해야 합니다. 또한 문제가 발견되면 모델을 재학습하고, 배포할만한 수준인지 평가해야 합니다. 그때마다 전문 평가자가 나설 순 없습니다. 왜냐하면 전문가의 시간은 큰 비용입니다. 또한 전문가도 사람인지라 실수할 가능성이 있습니다. 그래서 결국 평가가 모델 개선 사이클의 병목이 됩니다. 하지만 안심하십시오. 다행히도 우리는 LLM의 시대에 살고 있습니다. 올바른 정책이 세워져 있다면 품질을 일관되게 평가할 수 있는 자동화된 평가자, 즉 LLM-as-a-Judge를 만들 수 있습니다. LLM Judge는 인간 전문가를 대신해 수 만 건의 설명 문장을 빠르고 정확하게 평가할 수 있습니다. 다만 인간 전문가와 LLM Judge 간의 높은 평가 일치율을 만들기는 쉽지 않습니다. 단순히 LLM Judge Prompt에 설명 정책을 넣는다고 LLM Judge가 인간 전문가 수준의 안정된 평가를 보여주지 않기 때문입니다. 여러분이 직접 LLM Judge를 만든다면 어떤 방식으로 만드실 건가요? 만든 Judge는 어떻게 평가하면 좋을까요? 그리고 LLM Judge는 자동화된 평가를 넘어서서 어디에 활용될 수 있을까요? 이 글에서는 하이퍼커넥트 MGAI팀이 LLM Judge를 잘 만들고 사용하는 과정에서 얻은 꿀팁을 소개합니다. 1부에서와 마찬가지로 여러 예시와 함께 프롬프트를 개선하는 과정을 소개합니다. 이 팁들을 여러분의 프로젝트에 적용한다면, 낮은 가격으로, 빠른 시간 안에, 인간 평가자와 높은 일치율을 보이는 LLM Judge를 만드실 수 있습니다. 여러분의 LLM Product는 LLM Judge의 도움으로 빠르게 개선될 것입니다. LLM-as-a-Judge LLM-as-a-Judge란, LLM이 평가자가 되어 생성 결과의 품질을 자동으로 평가하는 시스템입니다. 단순히 점수만 예측하는 것이 아니라, 사람처럼 이유를 들고 판단을 내리도록 하여 LLM의 강력한 추론 능력을 최대한 활용할 수 있습니다. 사람이 직접 설명의 품질을 매번 평가하려면 많은 시간과 비용이 들지만, LLM-as-a-Judge를 활용하면 평가를 자동화할 수 있어 빠르고 효율적인 반복 실험이 가능합니다. LLM Judge를 만들기 위해서 먼저 문헌조사를 진행했습니다. 문헌 조사1를 통해 LLM-as-a-Judge의 rule o
2026년 4월 22일
1부: 데이터도 정답도 없다: 하이퍼커넥트가 LLM을 길들이는 법
이 글은 2부작 중 첫 번째 글입니다. 이번 글에서는 2025년 초 수행되었던 Tinder의 AI-enabled Discovery 프로젝트를 예시로, 정답 데이터가 없는 open-ended generation 문제에서 “좋은 설명이란 무엇인가?”를 먼저 정의하고, 그것을 사람이 일관되게 평가할 수 있는 정책으로 정리해 나간 과정을 소개합니다. 2부에서는 이렇게 만든 정책을 바탕으로 LLM-as-a-Judge를 설계한 과정을 다룹니다. 들어가며 “얘 진짜 성격 좋아, 너랑 취미도 잘 맞고. 한번 만나봐.” 누군가에게 소개팅을 주선하거나 받아본 경험, 다들 한 번쯤 있으시죠? 연애 상대를 러닝 크루, 클라이밍 동호회, 와인 모임 등에서 찾는 것보다, 주변 사람이 소개해주는 상대가 더 잘 맞는 경우가 많습니다. 그 이유 중 하나는 노련한 소개팅 주선자의 설명 덕분이죠. 노련한 주선자는 다짜고짜 사진부터 들이밀며 소개하지 않습니다. 이 사람이 어떤 사람인지, 왜 당신과 잘 어울릴 것 같은지를 먼저 설명합니다. 그리고 마지막에 사진을 보여주죠. 빌드업이 지나치면 오히려 불안감이 커지기도 하지만, 많은 경우 이런 설명이 호감도 형성에 큰 영향을 줍니다. 사진만 봤을 땐 그냥 시큰둥했을 상대에게도, 설명 덕분에 매력적으로 느껴지기도 하니까요. 심리학에서는 이런 현상을 초두효과(Primacy effect)라고 부릅니다. 사람은 처음 접한 정보에 더 큰 영향을 받는 경향이 있으며, 이후의 정보보다 먼저 주어진 정보가 인상 형성에 더 큰 역할을 합니다. 예를 들어, “이 사람 성격 정말 좋아, 너랑 잘 맞을 거야”라는 말을 먼저 들으면, 그 다음에 보는 사진이나 행동도 그 설명을 떠올리며 더 긍정적으로 해석하게 되는 것이죠. 그렇다면 이런 인지적 특성을 데이팅 앱에도 적용해볼 수 있지 않을까요? 사진을 먼저 보여주는 대신, 이 상대와 내가 왜 잘 맞을지를 먼저 설명해주는 거죠. 마치 소개팅 주선자처럼요. 유저가 “아, 이 사람이랑 나 잘 맞을지도?”라는 생각을 먼저 하게 만들고, 그 다음 사진을 보여주는 방식입니다. 이런 “소개팅 주선자”를 데이팅앱에 넣으려면 어떻게 해야 될까요? 이 기능을 간단히 설명 기능(Explanation Module)이라고 불러볼게요. 이 기능을 쉽게 구현하는 방법으로 LLM 활용을 떠올려볼 수 있습니다. 그렇다면 여러분의 팀은 어떤 방식으로 이 모듈을 만들 것 같으신가요? 예를 들어 이런 조건이 있다고 해봅시다. 처음 출시하는 기능이라 Ground Truth 데이터는 없다 (즉, “이 상황에서는 이 설명이 나와야 해” 같은 정답이 없다) 서빙을 위해 8B 이하의 LLM만 사용 가능하다 (즉, 프롬프트 엔지니어링 만으로는 원하는대로 동작하지 않을 수 있다) 배포 수준 품질을 만족시켜야 한다 그렇다면 보통은 프로덕트 매니저(PM)가 “소개 기능 제안서”를 만들고, 거기에 어떤 말을 Explanation Module이 출력 해야 할지 예시를 붙이겠죠. 그걸 머신러닝 엔지니어(MLE)가 받아서, 프롬프트 엔지니어링을 하거나, synthetic data로 소량 파인튜닝하거나 등등 알려진 레시피들을 시도할 것입니다. 하지만, 막상 그렇게 해봐도 잘 안 됩니다. 이런 프로젝트를 진행해 보신 분들이라면 아실 겁니다. 아무리 예쁘게 프롬프트를 짜고 모델을 고쳐도, LLM이 내놓는 설명이 기대한 만큼 만족스럽지 않다는 것을요. PM은 “이건 너무 밋밋하고 비논리적이야”라고 실망하고, MLE는 “데이터도 없고 모델도 작은데, 이 이상 뭘 더 하라는 거야?”라며 답답해합니다. 이렇게 LLM이 PM과 MLE의 기대치와 다르게 동작하는 문제는 현업에서 쉽게 발생합니다. 사실 하이퍼커넥트 MGAI 팀도 비슷한 벽을 마주쳤습니다. PM과 MLE 모두가 만족할 수 있는 설명을 만들기란 생각보다 훨씬 어려웠습니다. 하지만 팀은 이 문제를 해결할 수 있는 “이터레이션 프로세스”를 찾아냈습니다. 정책을 세우고, 직접 평가하고, 개선해나가는 이터레이션을 반복하며 결국 출시 가능한 수준의 Explanation Module을 만들어낼 수 있었습
2026년 4월 22일
온디바이스 AI 얼굴 식별 파이프라인 최적화
하이퍼커넥트의 Match Group AI 팀은 다양한 도메인의 머신러닝 모델을 연구하고, 이를 글로벌 서비스에 적용하고 있습니다. 그중 하나로, 사용자의 갤러리에 저장된 수천 장의 사진 중 프로필에 적합한 사진을 추천하는 AI 기술을 연구하고 있습니다. 이번 포스트에서는 안드로이드 온디바이스(on-device) 사진 추천 기술에 사용되는 얼굴 식별(Face Verification) 파이프라인을 최적화하는 과정에서의 주요 경험을 공유하고자 합니다. Motivation Tinder에서 진행한 연구에 따르면, 싱글 사용자의 85%는 데이팅 앱에서 프로필 사진이 매우 중요하다고 생각하고 있으며, 52%는 적절한 사진을 고르는 데 어려움을 느끼고 있습니다. 실제로 젊은 세대는 프로필 사진을 고르는 데 평균 33분을 사용한다고 응답했습니다. 저희는 이러한 사용자 경험의 어려움을 해소하기 위해 AI 추천 기술을 도입하여 사용자 경험을 혁신하고자 했습니다. 사용자의 갤러리에 있는 사진 중에서 좋은 이미지를 찾기 위해서는, AI가 갤러리 전체에 접근하여 분석을 수행해야 합니다. 하지만 갤러리는 매우 민감한 개인 정보이기 때문에, 추천 과정에서 생성되는 데이터나 분석 결과가 네트워크를 통해 외부로 전송되어서는 안 됩니다. 이러한 보안과 프라이버시 요구를 충족하기 위해, 모든 데이터 처리는 기기 내에서 시작되고 종료되어야 합니다. 저희는 이러한 기술적 제약과 사용자 보호 원칙을 바탕으로, Match Group의 다른 브랜드들과 함께 온디바이스 ML 기술 기반의 AI 사진 추천 기술을 연구하고 개발해 왔습니다. 기술의 전반적인 동작 방식에 대해 더 알고 싶으시다면, Tinder 기술 블로그를 참고해 보셔도 좋습니다. AI 사진 추천은 크게 두 단계로 구성됩니다. 첫 번째는 얼굴 인식 파이프라인으로, 전체 사진 중 사용자 본인의 얼굴이 포함된 이미지를 식별합니다. 다음으로 추천 파이프라인은 이들 중에서 품질이 좋은 사진을 선별하고, 프로필로 사용하기에 부적절한 사진을 걸러 최종 결과를 제공합니다. 얼굴 인식 파이프라인은 사용자의 전체 사진을 대상으로 하기 때문에 실행 속도가 매우 중요합니다. 모델을 개선함으로써 속도 향상을 기대할 수도 있지만, 실제 온디바이스 ML 프로젝트를 수행하다 보면 모델 외적인 병목 요소를 많이 마주하게 됩니다. 저희는 모델을 변경하지 않고도 각 연산의 배치 방식과 실행 구조를 개선함으로써, 안드로이드 얼굴 식별 파이프라인에서 응답시간 37% 단축, 처리량 530% 개선이라는 성능 향상을 이끌어낼 수 있었습니다. 이제 저희가 최적화 문제에 어떤 전략으로 접근했는지를 단계별로 설명드리고자 합니다. Understanding the Face Verification Pipeline 최적화 내용을 소개하기 전에, 우선 얼굴 식별 기술이 무엇인지를 빠르게 살펴보겠습니다. AI 사진 추천에서 얼굴 식별 기술은 두 이미지에 동일 인물이 포함되어 있는지를 확인하는 목적으로 사용되고 있습니다. 얼굴 식별은 얼굴 탐지(Face Detection) 단계와 얼굴 인식(Face Recognition) 단계로 나뉘는데, 각각 도식을 중심으로 간단하게 설명드리겠습니다. 얼굴 탐지(Face Detection) 파이프라인 얼굴 탐지는 사진에서 얼굴을 식별해내 좌표를 뽑아내는 단계입니다. ML 모델을 이용하여 (1) 얼굴이 있는 직사각형 영역(bounding box)을 찾아내고, (2) 눈/코/입술/귀 등 중요한 이목구비 위치(landmark)를 찾아냅니다. 이후, NMS 알고리즘 등 후처리를 적용하여 얼굴을 알맞게 오려내고, 적절한 변환(similarity transform)을 통해 이목구비를 정면으로 정렬합니다. 정렬된 얼굴 이미지는 이후 얼굴 인식 파이프라인에서 활용합니다. 저희는 프로필 사진에서 얼굴을 탐지하는데 특화된 자체 경량 모델을 양자화(quantization)하여 사용하고 있습니다. 얼굴 인식(Face Recognition) 파이프라인 얼굴 인식은 두 얼굴이 동일인인지를 예측하는 단계입니다. ML 모델을 이용하여 (1) 얼굴 이미
2026년 1월 23일
비즈니스 문제를 AI 문제로 정렬하는 방법
AI 조직은 비즈니스 문제를 AI 문제로 정렬해서 푸는 조직입니다. AI 문제란 무엇일까요? 우리가 현업에서 정말 다양한 업무들을 하지만, 본질은 효용 함수(또는 손실 함수)를 최적화하는 문제입니다. 명시적이든 암시적이든, 우리는 문제를 최적화 식(optimization form)으로 쓰게 됩니다. ML 알고리즘을 통해 최적화 식의 해(solution)를 찾고, 이 해를 이용해 비즈니스 문제를 풉니다. 결국, AI 조직은 수학적 해를 통해 비즈니스의 해를 찾아가는 조직입니다. 비즈니스 문제는 본질적으로 어렵습니다. 그렇다면 우리는 이런 어려운 문제를 어떻게 풀어야 할까요? 최적화 이론에서는 이런 어려운 문제를 풀기 쉬운 문제로 완화(relaxation)해서 풉니다. 특히, 여러 개의 지역(local) 최솟값에 갇힐 수 있는 비볼록(non-convex) 문제를 볼록(convex) 문제로 바꾸어 해결하는 방법들을 중요하게 다룹니다. 볼록 문제는 지역 해가 곧 전역 해인 아름다운 성질을 가지는 덕분에 우리는 경사 하강(gradient descent) 같은 단순한 방법으로도 전역 최적해를 찾을 수 있습니다. 그래서 최적화 이론에서는 비볼록 문제를 볼록 문제로 바꾸는 변환 과정을 볼록 완화(convex relaxation)라고 부르며, 어려운 문제를 풀 수 있는 형태로 재정의하는 방법 중 하나로 중요하게 다룹니다. 직접적으로 풀기 어려운 비즈니스 문제도, 최적화 이론에서 제시하는 것처럼 풀 수 있는 형태로 완화(relaxation)하면 훨씬 다루기 쉬워집니다. 완화된 문제를 잘 설계하면, 비록 원래의 복잡한 비즈니스 문제를 직접 푸는 것은 아니더라도, 그 해가 원래 문제(original problem)의 해와 충분히 유사한 결과를 만들어낼 수 있습니다. 실제로 AI 조직에서 다루는 문제의 대부분은 의식적으로든 무의식적으로든 이러한 완화된 형태를 띱니다. 예를 들어, 어떤 AI 기능을 개발하기 위해 분류기(classifier)를 만든다고 해봅시다. 우리가 진정으로 개선하고자 하는 것은 특정 비즈니스 지표일 것입니다. 하지만 현실적으로 그 지표를 직접 최적화하기는 어렵기 때문에, 대신 우리는 “분류기를 잘 학습시키는 문제”로 바꾸어 풉니다. 이 분류 문제는 원래의 비즈니스 목표를 근사적으로 대변하는 완화된 문제(relaxed problem)라고 볼 수 있습니다. 결국, AI 조직은 비즈니스 문제를 바로 푸는 대신, 그 문제를 AI 문제로 적절히 완화하고, 그 완화된 문제를 최적화함으로써 비즈니스 임팩트를 만들어내는 조직이라 할 수 있습니다. Preliminary: Convex Relaxation 원래의 문제를 “잘” 완화한다는 것은 무엇일까요? 이 질문에 대한 좋은 힌트는 convex relaxation에서 찾을 수 있습니다. 우리는 머신러닝을 처음 배울 때 거의 예외 없이 Lasso regression을 접하게 됩니다. 그 이유는 단순합니다. Lasso는 “풀기 어려운 문제를 풀기 쉬운 형태로 바꾸는” convex relaxation의 가장 대표적인 예이기 때문입니다. 우선, 기본이 되는 선형회귀(linear regression)를 생각해봅시다. 이 문제는 모든 변수(feature)를 그대로 사용하는 회귀식입니다. 하지만 우리는 종종 모델이 너무 복잡해지는 것을 피하고 싶습니다. 일부 계수(\(\beta_i\))만 남기고 나머지는 0으로 만들어, sparse한 해를 얻고 싶은 거죠. 그렇게 하려면 다음과 같은 제약식을 추가해야 합니다. 여기서 \(\|\beta\|_0\)은 0이 아닌 계수의 개수를 세는 항으로, “전체 변수 중 최대 \(k\)개만 사용할 수 있다”는 제약을 뜻합니다. 하지만 이 문제는 모든 feature subset을 탐색해야하는 조합 최적화(combinatorial optimization) 문제이기에 해를 찾는 데 지수적 탐색이 필요합니다. 다시 말해, 현실적으로 풀기 어렵습니다. 그래서 등장한 것이 Lasso regression입니다. Lasso는 원래의 \(\ell_0\) 제약 조건을 \(\ell_1\)
2026년 1월 12일
왜 막상 배포하면 효과가 없지? 타겟 지표에 맞는 ML모델 train/eval 설계하기
ML 벤치마크 태스크에서는 타겟 메트릭이 정해져 있고 모델링과 최적화에 집중하는 경우가 많습니다. 하지만 실제 서비스에 ML 모델을 적용할 때는, 무엇을 목표로 삼고 어떤 지표에 초점을 맞춰야 할지부터 정하는 과정이 매우 중요합니다. 비즈니스 목표가 아닌 잘못된 지표를 최적화 하는 경우, 모델 성능을 계속 높여도 서비스에서 아무런 효과가 발생하지 않는 상황을 겪기도 됩니다. 이번 포스트에서는 하이퍼커넥트 AI 조직이 매치그룹 내 데이팅 브랜드와 협업한 프로젝트를 각색한 사례를 소개합니다. 문제 정의에서 시작해 모델 학습 목표 설정과 데이터셋 구축, 오프라인 평가, 온라인 A/B 테스트, 실제 배포까지의 흐름을 따라가며, 비즈니스 문제를 ML 문제로 재구성하고 이를 실제 서비스 성과와 어떻게 연결했는지 살펴봅니다. 문제 상황 특정 서비스에 아이템과 각 아이템을 나타내는 속성(attribute)들이 있는 상황을 살펴봅시다. 만약 광고 추천이라면 아이템은 특정 상품, 속성은 상품별 광고 카피가 될 수 있습니다. 넷플릭스, 유튜브 등의 영상 플랫폼에서는 썸네일이 속성에 해당될 수 있습니다. 일반적으로 유저들이 아이템들을 살펴볼 땐 처음부터 모든 정보들이 노출되지 않습니다. 광고라면 플랫폼에서 노출시키는 아이템별 광고 카피 중 하나를 보게 되고, 영상을 볼 땐 썸네일이 우선적으로 나타나게 됩니다. 처음 노출된 속성은 유저의 아이템에 대한 첫인상을 결정하고, 나아가 유저가 해당 아이템에 대해 구매·소비 등의 전환으로 이어질지에 대해서도 중요한 역할을 하게 됩니다. 즉, 사용자에게 특정 아이템의 여러 속성 중 어떤 것을 처음에 노출시킬지 잘 선택하는 것만으로도, 전환율을 늘릴 수 있습니다. 이후에는 처음에 노출될 속성을 대표 속성(primary attribute)이라고 표현하겠습니다. 그렇다면 유저들에게 전환율을 높일 수 있는 대표 속성을 어떻게 찾을 수 있을까요? 아이템에 포함된 여러개의 속성 중에서, 각 속성을 랜덤하게 대표 속성으로 선택해 유저들에게 노출시켰을 때 전환율을 비교해볼 수 있습니다. 문제를 multi-armed bandit 문제로 바라보는 관점인데요, 문제점이 있습니다. 바로 새로운 아이템이 생기거나 속성이 업데이트 되면 처음부터 다시 exploration을 해야한다는 점입니다. 전환율이 더 높은 속성을 노출시킬 수 있던 기회비용이 드는 것이기 때문에 공짜가 아니고, 최적의 대표 속성을 찾기까지 시간도 걸립니다. 만약 아이템에 포함된 여러 속성 중, 가장 높은 전환율을 보일 대표 속성을 미리 예측해주는 AI 모델이 있다면 어떨까요? 새로 업로드 된 아이템이나 속성이라도 별도의 exploration 단계 없이 아이템의 전환율을 극대화할 수 있을 것입니다. 이후 글에서는 이 문제를 AI 모델이 풀 수 있는 문제로 변환하기 위해 어떤 과정들을 거쳤는지를 소개드리겠습니다. 데이터로 문제 구체화하기 이번 프로젝트의 목표는 한 아이템의 여러 속성 중에서 유저에게 가장 높은 전환율을 보일 속성을 고르는 모델을 학습하는 것 입니다. 이 모델을 학습하기 위해서는, 학습 및 평가를 위한 데이터를 구축하는 것이 첫번째 작업입니다. 유저가 아이템을 보고나면, 전환이 발생할 수도, 발생하지 않을 수도 있습니다. 이 때 대표 속성이 무엇이었는지가 남아있다면, 각 속성에 대한 전환 여부(0/1) 데이터를 구축할 수 있습니다. 전환 데이터 예시: user item Item feature 1 Item feature 2 primary attribute conversion User A Item X Category 1 2.3 Attribute 1 1 User B Item X Category 1 2.3 Attribute 1 0 User C Item X Category 1 2.3 Attribute 2 1 User D Item Y Category 2 3.5 Attribute 3 0 … … … … … … 학습 방식을 고안하고 그 방식이 망가지는 상황은 없을지 이론적으로 검토하기 위해, 위 데이터의 주요 column을 서로 종속관계가 있는 확률변수로 다
2025년 11월 28일
클릭 한 번으로 실험 시작! 이터레이션 사이클을 단축하는 추천 실험 시스템 개발기
들어가며 아자르는 1:1 비디오 채팅을 통해 매일 전세계의 사용자들을 연결하고 있습니다. 비디오 채팅에서 즐거운 경험을 하려면 자신과 잘 맞는 사람을 만나야 하기에 추천 알고리즘의 역할이 매우 중요합니다. 지난 테크블로그에서는 하이퍼커넥트가 AI 기반 추천 시스템을 개발하여 어떻게 매치 경험을 향상시키고, 아자르의 성장을 견인하고 있는지에 대해 다루었습니다. AI Lab에서는 추천 알고리즘의 성능을 향상시키기 위해 끊임없이 더 향상된 알고리즘을 개발하고 있는데, 이런 알고리즘의 성능을 검증하려면 사용자 대상 실험을 해야 합니다. 예를 들어, 신규 가입자의 리텐션을 높이기 위해 새로운 추천 알고리즘을 개발했다고 가정해봅시다. 이 알고리즘이 기존보다 더 효과적인지를 확인하려면, 실험을 통해 실제 사용자의 반응을 비교해야 합니다. 이를 위해 신규 사용자 중 일부를 실험군으로 무작위 할당하여 새로운 알고리즘을 적용하고, 나머지 대조군에게는 기존 알고리즘을 유지한 채로 리텐션을 비롯한 주요 지표를 관찰합니다. 만약 실험군의 성과가 유의하게 더 좋다면, 새로운 알고리즘을 전체 사용자에게 적용하고, 그렇지 않으면 실험을 종료합니다. 이렇게 두 집단에 오직 ‘추천 알고리즘’만 다르게 적용하고, 그 외의 조건은 최대한 동일하게 유지한 상태에서 결과를 비교하는 실험 방식을 A/B 테스트라고 합니다. 추천 알고리즘에 대해 A/B 테스트를 하는 과정은 다음과 같이 정리할 수 있습니다. 추천 시스템을 도입하던 초창기에, 저희는 신규 알고리즘 A/B 테스트 로직 구현 단계가 주요 병목 지점이라는 것을 파악했습니다. 왜냐하면, 신규 알고리즘을 적용하기까지는 여러 번의 이터레이션을 거치면서 알고리즘을 보완하고 실험군 비율을 조정해야 해서, A/B 테스트 로직의 변경이 자주 일어나기 때문입니다. 이 때마다 코드를 수정하고 테스트한 뒤 배포를 하려면 시간이 오래 걸리고, 실험 담당자가 소프트웨어 엔지니어의 스케줄에 맞춰야 해서 실험을 원하는 시점에 시작하지 못할 수도 있습니다. 빠른 추천 실험을 위한 로우 코드(Low Code) 실험 시스템 위와 같은 병목을 방지하고 빠르게 이터레이션을 돌며 추천 실험을 하기 위해, AI Lab에서는 추천 시스템 개발 초기에 코드 변경 및 배포 없이 설정 파일로 실험을 켤 수 있는 로우 코드 실험 시스템을 함께 개발했습니다. 이 시스템에서는 JSON 형식의 실험 명세를 작성하여, 런타임에 실험군을 자동으로 할당하고 신규 알고리즘을 적용할 수 있습니다. 예를 들어, 기존에 v1 알고리즘을 쓰고 있는 상태에서 한국의 신규 가입자를 대상으로 v2 알고리즘을 검증하고 싶다면 다음과 같이 JSON 설정을 작성하면 됩니다. { # 실험 대상 조건("AND" 형식으로 나열) "target_condition": { "country": "kr", "is_newbie": true, }, # 실험 설정 "treatments": [ { "percent": 95, # 95%는 대조군으로 할당 "name": "신규 가입자 리텐션 향상 실험 [대조군]", "algorithm": "v1" # 기존 알고리즘 } }, { "percent": 5, # 5%는 실험군으로 할당 "name": "신규 가입자 리텐션 향상 실험 [실험군]", "algorithm": "v2" # 신규 알고리즘 } ] } 이처럼 배포 없이 설정 파일만으로 추천 알고리즘과 실험 로직을 바꿀 수 있는 시스템을 도입한 덕분에, 저희는 AI 추천 시스템 도입 초기부터 빠른 이터레이션 사이클을 유지하며 실험 로직을 고도화할 수 있었습니다. 그러나 추천 시스템이 고도화되면서 새로운 문제가 생겨났습니다. 조직이 성장하며 더 많은 추천 실험을 진행하게 되었고, 이로 인해 설정 파일의 사이즈가 빠르게 불어났습니다. 또 단순히 알고리즘의 버전만 바꾸는 수준을 넘어, 알고리즘의 세부 동작(임계치나 가중치, 특정 기능의 on/off 여부 등)을 조절해야 하는 실험들이 등장했고, 이로 인해 설정 파일의 구조도 복잡해졌습니다. 이렇게 실험 설정이 길고 복잡해지다 보니 설정을 잘못 건드려서 잘못된 로
2025년 8월 26일
1:1 비디오 채팅 서비스는 E2E 회귀 테스트를 어떻게 자동화할까?
안녕하세요? 하이퍼커넥트에서 Software Development Engineer in Test 팀(이하 SDET팀) 에서 근무중인 Nick.Y, Liam.O 입니다. 많은 서비스에서는 매 버전 업데이트를 할 때마다 기존 기능에 문제가 없는지 반복적으로 검사해야 합니다. 이것을 회귀 테스트(Regression Test) 라고 하는데요, 아자르도 예외 없이 버전마다 QA 팀에서 수행 중입니다. 저희 SDET팀에서는 반복적으로 수행되는 회귀 테스트 케이스에 대해 Pytest와 Appium을 이용하여 자동화하는 작업을 하고 있는데요, 다른 서비스에서는 경험하기 어려운 아자르의 특별한 기능을 테스트하기 위해 많이 고민했습니다. 이번 포스트에서는 테스트를 자동화할 때 겪은 어려움과 그 해결 방법, 그리고 자동화를 통해 얻는 이점 등을 소개하고자 합니다. 아자르의 자동 회귀 테스트 구성 1:1 비디오 채팅 이라는 아자르의 특별한 기능은 테스트 자동화 초기 단계에서 가장 큰 허들이었습니다. 팀 내부에서 열심히 토론한 결과, 일반적인 형태의 테스트 자동화로는 구현이 어렵다는 결론에 이르러, 테스트 구성을 크게 Non-interaction 과 Interaction 이라는 2개의 형태로 나누기로 했습니다. 이 구조는 실행하고자 하는 테스트가 다른 유저와의 상호작용(interaction)이 없는지, 또는 비디오 콜, 메시지, 매칭 등 다른 유저와의 상호작용을 수행해야 하는 테스트인지에 따라 구분했습니다. 이는 다시 하나의 코드 베이스에서 driver 생성 전략을 구분하게 되었습니다. Non-interaction 테스트의 경우 일반적인 UI 자동 테스트와 동일합니다. 물론 Non-interaction 테스트에서도 소개해 드릴 만한 부분이 정말 많지만, 이번 포스트에서는 Interaction 테스트를 소개해 드리고자 합니다. 그러면 1:1 Interaction을 어떻게 자동화할 수 있을까? 그러면 다른 유저와의 1:1 인터랙션을 어떻게 자동화할 수 있을까요? 바로 하나의 테스트에서 2개의 driver를 생성해서 조작하면 됩니다. 그러나 driver 를 2개 생성해서 테스트를 제대로 수행한다는 것은 그리 간단한 문제는 아니었습니다. Interaction 테스트를 자동화하면서 했던 고민들 실행 단말기 구성 1:1 인터랙션을 한다고 해도 단순히 Android 단말기끼리, iOS 단말기끼리 인터랙션을 수행할 수는 없었습니다. iOS에서 Android로 비디오 채팅을 실행했을 때, 테스트 케이스의 확인 관점에 따라 그 반대의 경우 등 크로스 플랫폼의 경우를 확인해야 했습니다. 따라서 기본적으로는 아래 4가지 조건으로 테스트를 실행할 수 있어야 했습니다. 주로 테스트하는 플랫폼 테스트를 도와주는 상대 플랫폼 Android Android Android iOS iOS Android iOS iOS 다만 때에 따라 특정 플랫폼에서 지원하지 않는 기능이 있거나, 테스트 케이스를 분석했을 때 테스트 시간을 단축하고자 굳이 크로스 플랫폼까지 확인할 필요가 없는 때도 있어 조합을 선택적으로 실행할 수도 있어야 했습니다. 또한 위 조건에 더하여, 하나의 테스트에 대해 모든 실행 가능한 조합별 병렬 실행할 수 있어야 했습니다. 이러한 조건으로 인해 하나의 테스트 케이스에서 여러 플랫폼의 driver를 생성하는 것은 단일 플랫폼만 생성해서 단말기를 조작할 때와는 다른 접근이 필요했습니다. 원하는 테스트 상대와의 매칭 아자르의 1:1 비디오 채팅은 기본적으로 무작위(랜덤) 상대를 만나게 되어 있습니다. 그러나 이로 인해 인터랙션 테스트 케이스들의 병렬 실행 시도 시, 시나리오상 만나야 하는 상대와 만날 수 없는 문제가 발생했습니다. 예를 들어 A 테스트와 B 테스트가 동시에 실행될 때, A 테스트에서는 a 유저와 b 유저가, B 테스트에서는 c 유저와 d 유저가 매칭되어야 합니다. 그러나 병렬 실행 시 A 테스트의 a가 B 테스트의 c와 매칭되거나, b 유저가 d 유저와 매칭되는 등 의도치 않은 매칭이 발생할 수 있습니다. 또한 테스트 실행 중 매칭 기능을 이용해 테스
2025년 6월 12일
AI 실시간 추천 시스템을 위한 Flink 기반 스트림 조인 서비스 구축기
안녕하세요. Matching Dev Team의 Milo입니다. Azar는 전 세계 유저들을 24/7 연결하고 있으며, 즐거운 연결 경험을 제공하기 위해 세션 기반 추천 시스템1을 운영하고 있습니다. 이 추천 시스템은 유저의 행동이나 선호도 같은 정보를 바탕으로 맞춤형 추천을 제공하는 방식입니다. 추천 품질을 높이려면 유저의 최신 정보를 실시간 반영하는 것이 중요합니다. 유저의 최신 정보는 서로 다른 시점에 실시간으로 생성된 여러 이벤트로 존재했고, 파편화된 이벤트를 조합하여 추천에 활용할 수 있는 데이터로 변환하는 것이 필요했습니다. 뒤에 설명드리겠지만, 실시간으로 여러 이벤트를 조합하는 규칙이 단순하지 않았기 때문에 새로운 스트림 조인 서비스를 구축하게 되었습니다. 이번 포스트에서는 Azar의 AI 기반 실시간 매칭 시스템을 위해 유저의 이벤트를 어떻게 실시간으로 조합하고, 결과를 안정적으로 제공할 수 있었던 경험을 소개합니다. 1. 요구사항 기능 요구사항 다른 시점에 발생하는 유저 이벤트를 조합할 수 있어야 합니다. 유저 이벤트가 일부 유실되더라도 정의된 조건에 따라 조합할 수 있어야 합니다. 유저 이벤트는 다양한 조합이 가능해야 합니다. 예시: 매치 요청 → 대화 완료 (성공적인 매칭) 예시: 매치 요청 → 요청 취소 (매칭 취소) 비기능 요구사항 이벤트 조합과 결과 생성을 최소한의 지연으로 처리해야 합니다. 조합된 데이터는 유실 없이 정확히 한 번(Exactly Once)만 제공되어야 합니다. 시스템 배포나 업데이트 시에도 서비스 중단 없이(Zero downtime) 지속적으로 데이터를 제공해야 합니다. 2. 기술 탐색 및 결정 아래 그림과 같은 실시간 스트림 조인 파이프라인을 구성하고자 했습니다. 그림 1. 실시간 스트림 조인 파이프라인 구성 실시간 이벤트 조합을 위한 스트리밍 플랫폼 선정 실시간 이벤트 조합을 위한 스트리밍 플랫폼을 선정하기 위해 여러 기술을 분석했습니다. 실시간성과 복잡한 이벤트 조합이 요구되므로, 다음과 같은 기술 스택을 고려했습니다. 플랫폼 장점 단점 적합성 Spark Streaming 배치/스트림 통합, 풍부한 Spark API 생태계 마이크로배치 기반으로 인한 지연 ❌ 밀리초 단위 지연이 중요한 실시간 처리에 부적합 Kafka Streams 간단한 API, Kafka와 자연스러운 통합 복잡한 시간 처리에 제한적 (Schedule 기반 시간만 지원) ❌ Event Time 기반 타임아웃 등 정밀 제어의 한계 Apache Flink 낮은 지연, 정밀한 시간 처리 지원 높은 학습 곡선, 초기 진입 장벽 ✅ 실시간 처리 요구사항(상태, 시간, 지연 제어 등)에 가장 적합 조금 더 자세한 Apache Flink의 특징은 다음과 같습니다. 낮은 지연: Flink는 Record-by-Record 단위의 데이터 처리 방식을 기반으로 하여, 데이터를 받는 즉시 연산을 수행합니다. 배치처럼 데이터를 모아서 처리하지 않고, 흐름 속에서 연산이 일어나므로 밀리초 단위의 지연으로도 실시간 처리가 가능합니다. 상태 관리: Flink는 애플리케이션 수준에서 상태를 안전하게 저장하고 관리할 수 있도록 내장된 상태 백엔드(예: RocksDB)를 제공합니다. 이를 통해 이벤트 누적, 집계, 중복 제거, 타임아웃 처리 등 복잡한 상태 기반 로직을 안정적으로 구현할 수 있습니다. 또한, Exactly-once 처리 보장 및 장애 복구도 지원됩니다. 시간 관리: Flink는 Processing Time, Event Time, Ingestion Time을 모두 지원하며, 특히 Event Time을 기반으로 한 시간 제어가 강력한 특징입니다. Watermark를 통해 out-of-order 이벤트도 올바르게 처리할 수 있고, 시간 기반 윈도우 연산을 수행할 수 있습니다. 실시간 이벤트 조합 처리로 Kafka Streams와 Apache Flink 둘 다 서비스를 구성하기 위한 좋은 선택이었지만, Flink의 정밀한 시간 제어 기능이 Azar의 복잡한 요구사항을 충족하는 데 더 적합하다고 판단했습니다. 하지만 Flink를
2025년 6월 11일
Building Resilient, High Performance ScyllaDB Clusters with Super Disk
안녕하세요, SRE - Database Platform Unit(DBP)의 stewart입니다. Hyperconnect는 전사 NoSQL 데이터베이스로 ScyllaDB를 약 4년 동안 운영하며, ML feature store[2] 등 전사의 매우 다양한 서비스의 고성능 요구사항을 충족해왔습니다. 하지만 최근 장애 모의 훈련에서 cluster rolling update 시 node 복구 시간이 지나치게 길다는 문제를 발견했습니다. 본 글에서는 이 문제를 해결하기 위해 도입한 Super Disk (Write-mostly RAID) 기능이 어떻게 cluster rolling update 시간을 기존 대비 최대 10배 이상 단축시켰으며 , 성능상에는 어떤 차이가 있는지, 이러한 결론을 바탕으로 해당 기능을 Kubernetes 환경에서 더욱 효율적으로 관리하기 위해 도입한 Windmill 기반 자동화 과정까지 단계별로 소개합니다. 1. ScyllaDB 란? 먼저 DBP에서 전사 NoSQL DB로 사용하고 있는 ScyllaDB에 대해 소개하려고 합니다. ScyllaDB는 Cassandra와 호환되는 wide-column Database로, 다음과 같은 특징과 장점을 지닙니다. Cassandra Compatible 기존 Cassandra의 데이터 모델과 쿼리 방식을 그대로 호환하며, 내부적으로 C++와 Seastar 프레임워크를 사용해 자원을 효율적으로 관리합니다. 이를 통해 높은 성능과 낮은 지연시간을 제공합니다. Far less infrastructure ScyllaDB는 Cassandra 대비 훨씬 효율적으로 동작해 cluster 크기를 크게 축소할 수 있습니다. Comcast나 Discord 같은 기업은 ScyllaDB로 마이그레이션 후 Cassandra cluster를 최대 10분의 1로 줄여 비용 절감과 향상된 ROI를 달성했습니다.[3] Low latency, high throughput 공식 Benchmark 자료에 따르면, 동일한 3-Node cluster에서 ScyllaDB(Open Source 4.4 버전)는 Cassandra(4.0 버전) 대비 2~3배 높은 처리량(throughput)을 보이며, p90, p99 지연시간 또한 10ms 미만으로 유지합니다.[4] Discord, Zillow 등 여러 글로벌 기업이 ScyllaDB를 활용해 성공 사례를 공유하고 있으며, Hyperconnect 또한 지난 4년 동안 Production 환경에서 안정적으로 운영하고 있습니다. 2. 문제 상황: ScyllaDB node 장애 시 node 롤링 시간이 너무 느리다! 최근 SRE 팀에서 진행한 장애 모의 훈련[5]에서는 ScyllaDB node 장애 상황도 함께 테스트했습니다. 테스트에서는 ScyllaDB가 운영 중인 EC2 인스턴스를 강제로 종료한 후 복구 시간을 측정하였으며, disk 용량에 따라 평균 3~4시간, 데이터 양이 많은 경우에는 최대 12시간 정도 소요되는 것으로 나타났습니다. 이 경우, 일반적인 read/write 성능에는 문제가 없지만, 여러 node가 동시에 장애가 발생하면 cluster 전체 복구에 상당한 지연이 발생합니다. 이는 ScyllaDB가 Local SSD 타입 인스턴스(i4i)를 사용해야 하기 때문입니다. Cassandra와 달리 ScyllaDB는 Local NVMe SSD 사용을 권장[6]]하는데, 이는 ScyllaDB가 Cassandra와는 전혀 다른 caching 및 disk I/O 전략을 사용하기 때문입니다. 이 전략을 제대로 이해하려면 먼저 ScyllaDB의 caching 방식에 대해 알아볼 필요가 있습니다. ScyllaDB 의 WhitePaper 7 Reasons Not to Put an External Cache in Front of Your Database를 참고해 보면, ScyllaDB의 Embedded Cache 설계에 대해 상세하게 알 수 있습니다. Cassandra의 Cache 접근 방식 Cassandra는 OS 차원의 Linux page cache와 자체 key
2025년 5월 15일
Apache Flink 어플리케이션의 End-to-End Latency 병목 찾아내기
어플리케이션을 운영하다 보면, 트래픽 증가나 사용자 경험 개선, 비용 절감 등 다양한 요인으로 인한 성능 개선 요구가 꾸준히 제기됩니다. 저희 팀에서 운영하고 있는 Flink 어플리케이션도 비즈니스 요구사항을 만족시키기 위한 지속적인 성능 튜닝이 필요했습니다. 하이퍼커넥트의 대표 Product인 Azar의 핵심이 되는 1:1 매칭 서비스는 Flink 어플리케이션으로 구현되어 있는데, 특히 스와이프 직후 매칭이 이뤄져야 하는 Azar의 특성상, end-to-end latency는 사용자 만족도와 직결되므로 매우 중요합니다. 이에 따라 성능상 병목 구간을 정확히 찾아내고, 찾아낸 문제를 해결하는 과정이 지속적으로 필요했습니다. 이 글에서는 이러한 Flink 어플리케이션의 end-to-end latency를 낮추기 위해 병목을 진단하고 개선 포인트를 도출해나가는 과정을 소개하고자 합니다. Flink 어플리케이션의 end-to-end latency 개선 포인트를 찾아내는 과정은 크게 두 단계로 나눌 수 있습니다. Application level: 전체 어플리케이션에 걸쳐 Flink operator 단위로 지표를 상세하게 수집하고 관찰하여 비정상적으로 느린 부분을 찾아냅니다. Operator level: 찾아낸 Flink operator에 대한 프로파일링을 진행하고, 이후 코드 레벨의 inspection을 수행합니다. 지금부터 각 단계에 대해 상세히 다뤄보겠습니다. 1. Application Level: Operator 단위의 지표 수집 및 관찰 End-to-end latency를 개선하기 위해서는 먼저 Flink의 각 operator 별로 처리에 소요되는 시간을 정확히 파악해야 할 필요가 있습니다. 이렇게 파악만 하더라도 기대와 다르게 느린 지점을 단번에 찾아낼 가능성이 있습니다. 이를 위해서 Flink 어플리케이션의 각 operator에 2종류의 히스토그램 지표를 추가하는 작업이 필요합니다. 처리 시간: map(), processElement()와 같은 어플리케이션 코드가 input을 처리해서 output을 생성하기까지 걸리는 시간 처리 외 시간: 처리 시간 밖의 모든 시간. Flink에서 데이터를 de/serialize하거나 네트워크 I/O를 하는 시간 등이 포함됩니다. 지표를 처리 시간과 처리 외 시간의 두 종류로 분리하였는데, 이는 둘 중 어느 곳이 병목이냐에 따라 해결 방식이 완전히 달라지게 되기 때문입니다. 만약 처리 시간이 병목이라면 어플리케이션의 로직을 점검하거나, DB I/O 시간 등을 확인해야 할 수 있습니다. 반대로 처리 외 시간이 병목이라면 네트워크 I/O와 관련된 트러블슈팅을 진행하거나 Flink 내부 코드와 관련된 작업을 해야 할 것입니다. 그림 1. 처리 시간과 처리 외 시간 지표가 각각 커버하는 구간의 예시 코드 작업을 통해 지표를 추가하고, 운영 환경에 배포하여 각 operator의 처리 시간과 처리 외 시간을 다음과 같이 수집할 수 있습니다. (이 글 끝에 부록으로 지표 추가 코드의 예시를 실어 놓았습니다) 그림 2. 위 예시에서 수집한 지표를 모아 파이 차트로 나타낸 것 처리 시간은 Operator 1, 처리 외 시간은 Operator 1 Non-Process의 형태로 수집하고 있다. 위와 같이 2종류의 지표를 추가하게 되면 end-to-end latency를 잘게 쪼개서 확인할 수 있습니다. 모니터링 체계가 갖춰지면 어느 구간이 예상했던 것보다 시간이 오래 걸리는지 파악할 수 있고, 파악한 병목 operator를 가지고 다음 단계로 넘어갈 수 있습니다. 참고로 Flink 자체적으로도 end-to-end latency를 측정할 수 있는 LatencyMarker라는 기능을 제공합니다. 이 기능을 활성화하면 source로부터 각 operator에서 처리가 끝나기까지 걸리는 시간을 지표로 확인할 수 있습니다. 다만 이 기능은 성능 저하가 상당히 커서 실서버 사용이 어렵고, 처리 시간과 처리 외 시간을 분리해서 볼 수 없으며, timer / aggregation / windowing 등을 사용하
2025년 3월 28일