기술 블로그 모음

국내 IT 기업들의 기술 블로그 글을 한 곳에서 모아보세요

전체 프론트엔드 백엔드 데브옵스 AI 아키텍처 DB 기타
[DAN 24] 서치피드: SERP를 넘어 SURF로! 검색의 새로운 물결
네이버 D2
[DAN 24] 서치피드: SERP를 넘어 SURF로! 검색의 새로운 물결

네이버 통합 검색은 2023년에 서치피드를 출시하여 통합 검색 모바일 하단에서 피드 형태로 개인화된 콘텐츠를 제공했습니다. 이 서비스의 내부 프로젝트명은 SURF(Search User Recommendation Feed)로, 검색 결과 페이지(Search Engine Results Page, SERP)의 한계를 뛰어넘어 사용자가 마치 바다에서 서핑을 즐기듯 끊임없이 새로운 콘텐츠를 발견하고 탐색할 수 있는 경험 제공을 목표로 하고 있습니다. 이 글에서는 SURF의 도입 배경과 SURF에 적용된 핵심 기술에 대해 설명하겠습니다. 서핑을 즐기듯 끊임없이 새로운 콘텐츠를 만나는 검색 결과 SURF 기존의 검색 경험(SERP)은 사용자가 검색어를 입력하면 출처 또는 의도 단위로 나열되어, 필요할 때 정확한 정보를 빠르게 찾을 수 있다는 장점이 있었습니다. 하지만 연속적이거나 다양한 정보 탐색 니즈를 충족하며, 발견하고 탐색하는 경험으로 확장하기 위해 새로운 방식으로 접근했습니다. SURF는 사용자의 검색 서핑 경험에 다음 3가지 핵심 기능을 제공합니다. A. 신선하고 다양한 파도 가져오기 SURF는 사용자에게 다층적인 콘텐츠 경험을 제공합니다. 먼저 입력된 검색어와 직접 관련된 콘텐츠를 제공하면서, 동시에 사용자의 취향과 관심사를 반영한 추천 결과를 함께 보여줍니다. 예를 들어, 특정 주제의 인기 카페 글이나 최근에 사용자가 클릭했던 문서와 유사한 글을 자연스럽게 노출합니다. 이를 통해 사용자는 원하는 정보를 찾으면서도 새로운 콘텐츠를 발견하는 즐거움을 경험할 수 있습니다. B. 그라데이션 방식으로 정보 제공하기 SURF의 특징적인 기능 중 하나는 그라데이션 방식의 콘텐츠 제공입니다. 사용자가 검색 결과를 아래로 스크롤할 때, 상단에서는 검색어와 가장 밀접하게 연관된 문서가 노출됩니다. 그러다가 점차 아래로 내려갈수록 주제가 자연스럽게 확장되어, 연관성은 있지만 보다 폭넓은 맥락의 콘텐츠가 제공됩니다. 이러한 그라데이션 구조는 사용자가 자연스럽게 관심 영역을 확장하면서 새로운 정보를 탐색할 수 있도록 돕습니다. C. 실시간 피드백 기반으로 동적 최적화하기 SURF는 사용자의 모든 행동을 실시간으로 분석하고 이를 즉각 검색 결과에 반영합니다. 문서 클릭, 스크롤 패턴, 체류 시간 등 다양한 사용자 행동 데이터를 수집하고, 이를 기반으로 사용자의 현재 관심사와 의도를 정교하게 예측합니다. 예를 들어, 특정 주제의 문서를 연속하여 클릭한 사용자에게는 해당 주제와 관련된 더 많은 콘텐츠를 제공하며, 특정 콘텐츠를 건너뛰는 경향을 보이는 경우에는 다른 주제의 콘텐츠 비중을 높이는 방식으로 작동합니다. 또한, SURF는 단순한 키워드 매칭을 넘어 사용자의 검색 맥락을 깊이 있게 이해하고자 합니다. 같은 검색어라도 사용자의 이전 검색 기록, 클릭 패턴, 관심사 등에 따라 다른 맥락의 결과를 제공할 수 있습니다. 특히 동음이의어나 다양한 맥락이 있는 검색어의 경우, 사용자의 실제 의도에 부합하는 결과를 제공하기 위해 개인화된 맥락 분석을 수행합니다. SURF의 가장 큰 특징은 검색이 단순한 정보 찾기를 넘어 지속적인 탐색 경험으로 확장된다는 점입니다. 사용자가 명시적으로 새로운 검색어를 입력하지 않더라도, SURF는 현재 관심사를 바탕으로 연관된 다양한 콘텐츠를 계속해서 제공합니다. 이는 마치 하나의 관심사가 자연스럽게 다른 관심사로 이어지는 실제 정보 탐색 과정을 모사한 것으로, 사용자의 정보 발견 여정을 더욱 풍부하게 만들어줍니다. SURF의 기술적 구현 SURF는 다양한 출처의 문서와 다양한 방법론을 사용하여 네이버 사용자의 검색 니즈를 충족시키기 위해 노력하고 있습니다. 전통적인 IR(information retrieval)은 물론, 최신성에 집중한 리트리버, 사용자 피드백 통계 기반으로 동작하는 리트리버뿐만 아니라 발견 확장을 위해 검색 질의 카테고리에 해당하는 다양한 출처의 인기 문서 리트리버도 사용합니다. 이러한 다양한 리트리버를 사용자의 니즈에 맞게 개인화하여 다양한 출처의 문서를 순위화할 수 있어야 합니다. SURF는 뉴럴 랭커를 학습하여 순위화의 품질을 높이기 위해 노력하고 있습니다. 이렇게 SURF에 활용하고 있는 다양한 방법 중 LLM(large language model)을 활용한 방법론을 위주로 설명하겠습니다. LLM 시대에 진입하면서, 검색 시스템 또한 새로운 변화가 필요했습니다. LLM의 가장 큰 특징은 고차원의 추론 능력으로 마치 사람처럼 텍스트의 맥락을 이해할 수 있다는 점입니다. 그러나 LLM은 크기가 크고 처리 속도가 느려 실시간 검색에 직접 활용하기는 어려웠습니다. 이를 해결하기 위해 저희는 LLM의 이해 능력을 담은 대량의 데이터셋을 만들고 이를 기반으로 실시간 검색에 활용 가능한 sLM(small language model)을 학습시키는 방식을 채택했습니다. 이는 증류(distillation)라고도 불리는 방식으로, LLM의 이해 능력을 더 작고 빠른 모델에 전수하는 것입니다. 저희는 디코더 기반의 sLM 임베딩 모델을 개발했고, 백본(backbone)으로는 사내 모델인 HCX를, LLM의 이해 능력이 녹아 있는 대량의 학습셋은 내부 RRA(re-ranking agent)를 활용하여 지도 학습 파인튜닝(supervised fine-tuning, SFT)을 진행했습니다. 이렇게 개발된 문맥 이해 임베딩 모델을 기반으로, SURF는 사용자에게 제공하고 싶은 4가지 가치를 구현하기 위해 노력했습니다. 반응형, 연관, 확장성, 그리고 개인화로 대표되는 이 '4대 파도'가 각각 어떻게 구현되었는지 자세히 살펴보겠습니다. 1. 반응형 파도(Reactive Wave): 사용자가 클릭한 문서와 연관된 문서 추천하기 SURF에서 가장 먼저 착수한 것은 반응형 파도였습니다. 반응형 파도는 단순한 키워드 매칭을 넘어, 클릭한 문서의 맥락을 파악해 연관 문서를 추천합니다. 예를 들어, 축구 관련 문서를 클릭했다면 같은 단어를 포함하지 않더라도 맥락에 관련된 다른 축구 소식을 추천할 수 있습니다. 따라서, 반응형 파도는 특정 문서와 연관된 문서를 리트리빙하는 것이 핵심 과제였습니다. 기존의 텀 매칭 기반 기술에서는 단어가 가장 많이 겹치는 문서를 찾는 방식을 사용했지만, 이는 중의성 문제로 인해 전혀 관련 없는 문서가 노출되는 한계가 있었습니다. 반면 임베딩 기반 리트리빙은 시드(seed) 문서의 맥락을 이해하고, 단어가 겹치지 않더라도 연관성 있는 문서를 효과적으로 찾아낼 수 있습니다. 반응형 파도의 실제 구현은 다음과 같습니다. 먼저 문서 풀에 대한 sLM 임베딩을 사전에 생성해 두고, SURF에서 사용자 클릭이 발생하면 실시간으로 해당 문서를 sLM에 통과시켜 임베딩을 얻습니다. 이후 ANN(approximate nearest neighbor) 검색을 수행하여 연관 문서를 가져오는 방식입니다. 2. 연관 파도(Related Wave): 검색어와 관련된 최신 문서 찾아주기 연관 파도는 검색 질의에서도 LLM의 맥락 이해 능력을 활용하고자 했습니다. 기존 텀 매칭 기반의 검색은 중의성이나 동음이의어 처리에 어려움이 있었기 때문에 이 문제를 해결하기 위해 LLM 임베딩을 활용하여 사용자의 실제 의도에 맞는 문서를 보여주고자 했습니다. 즉, '손흥민'을 검색하면 손흥민의 실제 경기 내용을 담은 문서를 제공하는 것이 목표였습니다. 하지만 SURF가 주로 다루는 짧은 숏헤드 질의는 문맥을 파악하기에 정보가 너무 부족했기에, 이를 해결하기 위해 '맥락텍스트'라는 개념을 도입했습니다. 예를 들어 '손흥민'에 대한 맥락텍스트는 "손흥민 맨시티 크리스탈팰리스 선발"과 같이 주요 토큰을 연결하여 생성합니다. 맥락텍스트는 반드시 하나일 필요는 없으며, 여러 개를 생성하여 검색의 다양성을 확보할 수 있습니다. 더 나아가 개인별로 다른 맥락텍스트를 생성함으로써 검색 개인화까지 구현할 수 있습니다. 하지만 ANN 사용 과정에서 새로운 과제가 발견되었습니다. 연관 파도는 '연관성 있는 최신 문서'를 제공하는 것이 핵심인데, ANN은 콘텐츠의 관련도만을 기준으로 검색하기 때문에 ANN으로 1차 검색된 문서 풀 내에서 최신 문서를 찾는 방식으로는 최신 문서를 원하는 만큼 가져올 수 없었습니다. 따라서 반응형 파도와는 다른 구조를 채택했습니다. 새로운 방식은 '손흥민'을 검색했을 때 다음과 같이 동작합니다. 문서 풀에서 '손흥민'으로 색인하여 최신 문서 K개를 최신순으로 가져옵니다. '손흥민' 맥락텍스트에 대한 임베딩과 최신 문서 K개의 임베딩을 구합니다. 맥락텍스트와 각 문서 간 코사인 유사도(cosine similarity)를 계산하여 유사도 기반 필터링(cut-off)과 재순위화(re-ranking)를 수행합니다. 이 방식을 통해 '손흥민'으로 색인된 최신 문서 중에서 실제로 손흥민의 경기와 관련된 문서만을 선별하여, 연관도가 높은 순서로 결과를 제공할 수 있게 되었습니다. 3. 확장형 파도(Expansive Wave): 검색어와 관련된 다른 주제로 확장하기 세 번째로 구현된 확장형 파도는 검색 질의와 유사한 다른 질의의 문서를 추천하는 기능입니다. 예를 들어, '손흥민' 검색 시 이강인, 김민재, 홍명보 등과 관련된 문서를 함께 제공하는 것입니다. 이를 '확장 질의'와 '확장 문서'라고 부릅니다. 확장형 파도에서도 동음이의어 문제는 중요한 과제였습니다. '손흥민' 검색에서 '김민재'가 확장 질의로 선정되었을 때, 축구 선수가 아닌 배우 김민재의 문서가 노출된다면 사용자 경험을 해칠 수 있기 때문입니다. 이를 해결하기 위해 맥락텍스트와 sLM을 다시 한 번 활용했습니다. 확장형 파도는 먼저 고품질 질의 풀을 선정하고, 질의의 메타 정보, 패턴, 로그 등을 활용하여 지식 그래프(Knowledge Graph)를 구축합니다. 이 지식 그래프 내에서 검색 질의와 확장 질의 간 매핑이 이루어지며, 동음이의어의 경우 검색 맥락에 따라 적절한 맥락텍스트가 생성됩니다. 예를 들어 '손흥민' 관련 확장 문서를 찾을 때, 지식 그래프에서 '김민재'가 확장 질의로 매핑되면, 축구 선수 김민재에 관한 맥락텍스트가 생성됩니다. 이를 기반으로 ANN 검색을 수행하면 축구 관련 확장 문서만을 제공할 수 있습니다. 더 나아가 지식 그래프와 사용자의 맥락을 결합하여 개인화된 확장 질의 매핑을 생성할 수도 있습니다. 4. 개인화 파도(Personalized Wave): 사용자 행동을 실시간으로 학습하기 마지막 개인화 파도는 랭커를 통해 구현되었습니다. SURF의 랭커는 사용자를 이해하고 피드 스크롤에 따라 적절한 결과를 제공하도록 학습됩니다. 예를 들어 '캠핑' 검색 시, 장소 탐색 의도가 강한 사용자에게는 캠핑장이나 차박 명소를, 장비 탐색 의도가 강한 사용자에게는 캠핑 장비 정보를 우선 제공합니다. 피드 스크롤 중 사용자의 반응도 즉각 반영됩니다. 등유 난로 관련 문서 클릭 시 다음 피드에서는 관련 문서를 적극 추천하고, 우드 테이블 관련 문서를 건너뛰면 해당 주제는 순위가 하향 조정됩니다. 또한 스크롤이 깊어질수록 다양한 주제와 출처로 자연스럽게 확장되도록 설계되어 있습니다. 현재는 더 발전한 형태의 개인화 추천 패러다임을 연구 중입니다. 사용자의 활동 로그를 분석하여 sLM이 관심사를 멀티 프로필로 생성하고, 이를 기반으로 개인화 추천을 제공하는 방식입니다. 추천된 결과에 대한 사용자 반응이 다시 로그로 쌓이면서 지속적으로 프로필이 발전하는 선순환 구조를 만드는 것이 목표입니다. 이러한 에이전트는 사용자의 멀티 프로필을 sLM 임베딩으로 보유하고, ANN 검색을 통해 문서를 피드에 노출합니다. 노출된 문서를 사용자가 클릭하거나 건너뛰는 등의 반응에 따라 해당 프로필의 임베딩을 업데이트하거나 삭제하면서 지속적으로 사용자의 선호도를 학습해 나갑니다. 이를 통해 더욱 정교한 개인화 검색 결과를 제공할 수 있을 것으로 기대됩니다. SURF의 미래: 개인 맞춤형 검색 에이전트를 향해 SURF는 출시 이후 3개월이라는 짧은 시간 동안에 많은 발전을 이루었고, 빠르게 변화하는 기술 환경에 발맞추어 더 큰 진화를 준비하고 있습니다. 검색 서비스의 미래는 단순히 원하는 정보를 찾아주는 것을 넘어서야 합니다. SURF가 그리는 미래의 검색은 개개인의 맥락을 이해하고 함께 고민하는 '나만의 검색 에이전트'입니다. 이 에이전트는 다음과 같은 역할을 수행하게 될 것입니다. 필요한 문서의 핵심을 요약하여 제공 대화형 인터페이스를 통한 심층 정보 탐색 지원 쇼핑 과정에서 제품 탐색부터 구매 결정까지 통합 지원 상황과 맥락을 고려한 적시의 정보 제공 이처럼 SURF는 검색이라는 행위를 더욱 자연스럽고 풍부한 경험으로 발전시키고자 합니다. 검색이 단순한 정보 찾기를 넘어 사용자의 목적 달성을 위한 종합적인 동반자가 되는 것, 그것이 SURF가 그리는 미래의 모습입니다. 우리는 이러한 미래 검색 경험을 실현하기 위해 끊임없이 연구하고 발전해 나갈 것입니다. SURF를 통해 펼쳐질 새로운 검색의 미래에 많은 관심과 기대 부탁드립니다. 이 글은 TEAM NAVER CONFERENCE 'DAN 24'에서 발표한 내용을 토대로 작성되었으며, 발표 내용과 자료는 DAN 24에서 보실 수 있습니다.

MySqlPagingQueryProvider 살펴보기
마켓컬리
MySqlPagingQueryProvider 살펴보기

JdbcPagingItemReader와 MySqlPagingQueryProvider를 사용할 때 주의사항

올리브영 결제수단 연동, 이렇게만 하면 끝!
올리브영
올리브영 결제수단 연동, 이렇게만 하면 끝!

업무 몰입을 위한 AI 조직 문화 구축
삼성 SDS
업무 몰입을 위한 AI 조직 문화 구축

이 아티클에서는 AI를 활용하여 직원들의 잠재력을 극대화하고, 기하급수적 성장을 이끌어내는 조직 문화를 구축하기 방안을 제안합니다.

Java 가상 스레드, 깊이 있는 소스 코드 분석과 작동 원리 3편 - 고정 이슈와 한계
라인
Java 가상 스레드, 깊이 있는 소스 코드 분석과 작동 원리 3편 - 고정 이슈와 한계

지난 2편에서는 가상 스레드(virtual thread)의 컨텍스트 스위칭(context switching)이 구체적으로 어떤 과정으로 진행되는지 알아봤습니다. 마지막 3편에서는 ...

당근 홈 피드, Server Driven UI로 실험 이터레이션 빠르게 돌리기
당근마켓
당근 홈 피드, Server Driven UI로 실험 이터레이션 빠르게 돌리기

안녕하세요! 당근 피드실 피드인프라팀 카터예요.홈 피드 화면피드실은 당근의 첫 화면을 통해 사용자들과 다양한 서비스를 연결해요. 중고거래, 동네생활 모임, 알바, 부동산 등 당근의 여러 서비스가 만드는 콘텐츠들을 사용자에게 재미있게 전할 수 있도록 홈 피드를 구성하고 있죠.피드 아이템피드 화면에서 볼 수 있는 하나하나의 콘텐츠를 의미하는 피드 아이템은 크게 두 부분으로 이루어져 있어요. 첫 번째로 피드 엔티티는 앱에서 보이는 실제 콘텐츠(중고거래 게시글이나 당근알바 게시글)를 말하고, 뷰타입은 이 콘텐츠를 어떤 모양으로 보여줄지 정하는 방식이에요. 뷰타입은 피드에서 피드 엔티티를 시각적으로 표현하기 위해 정의된 개념이고, UI 디자인과 스키마를 속성으로 가진다는 특징이 있어요.피드인프라팀에서는 Server Driven UI를 통해 새로운 피드 아이템 구성을 앱 업데이트 없이도 홈 피드에 빠르게 적용할 수 있도록 시스템을 구축했어요. 이번 글에서는 이 과정에서 어떤 기술적 고민들을 하고 어떻게 해결했는지 소개해 드리려고 해요.피드 아이템에는 어떤 문제가 있었나요?뷰타입 재사용의 어려움당근 초기에는 중고거래 게시글을 기반으로 다양한 형태의 뷰타입을 만들었어요. 이후 부동산, 중고차, 당근알바와 같은 신규 서비스들이 성장하면서, 중고거래 게시글과 비슷한 형태지만 기능과 노출 방식이 다른 뷰타입을 계속 추가했어요. 예를 들어 중고거래 게시글은 중고거래 피드 엔티티를 활용해서 중고거래 뷰타입을 유저에게 노출하고, 당근알바 게시글은 당근알바 피드엔티티를 활용해서 당근알바 뷰타입을 그리는 형식이었어요.하지만 뷰타입은 클라이언트 배포에 포함되기 때문에 앱 업데이트가 필요해요. 모든 사용자가 앱을 즉시 업데이트하진 않기 때문에, 서버에서는 하위 호환성을 위해 버전 분기 처리를 해야 했죠.실험 유연성과 속도 저하피드의 성장을 위해서는 다양한 형태와 조합으로 UI를 실험해야 했지만, 각 뷰타입별로 노출 가능한 정보들의 제약이 많아 실험 진행에 상당한 어려움이 있었어요. 이러한 제약은 실험의 유연성과 속도를 크게 저해했죠. 예를 들어 부동산 게시글의 거래상태(예약 중, 거래완료)를 표현하려고 했던 실험을 살펴볼까요?부동산 게시글의 거래상태를 표시하기 위해 새로운 컴포넌트가 필요했는데, 아이러니하게도 이미 중고거래 뷰타입에 동일한 기능이 구현되어 있었어요. 하지만 뷰타입 간에 컴포넌트를 재사용할 수 없어서, 부동산 게시글 뷰타입에 같은 기능을 담아 앱 업데이트를 배포해야 했죠. 게다가 뷰타입 배포 후 충분한 사용자 수가 확보될 때까지 기다려야 실험을 시작할 수 있었어요. 이런 복잡한 과정 때문에 간단한 실험 하나를 진행하는 데에만 2주 이상이 걸렸답니다.합의된 명칭 부재로 인한 소통의 어려움불명확한 요소들의 명칭또한 뷰타입의 UI 요소들에 대한 표준화된 명칭이 없어서 팀원들 간 협업에 어려움이 있었어요. 예를 들어 같은 UI 요소를 두고 누군가는 ‘썸네일’이라고 부르고, 다른 사람은 ‘이미지’라고 불렀어요. 또 타이틀 아래 영역을 누군가는 ‘태그그룹’이라 부르고, 다른 사람은 위치상 ‘서브타이틀’이라고 불렀죠. 이런 용어의 불일치로 인해 소통 과정에서 불필요한 시간과 노력이 들었어요.어떻게 해결했을까?이러한 문제점을 해결하기 위해서 우리는 Server Driven UI를 도입하고, 이를 바탕으로 두 가지 핵심 컴포넌트인 ‘피드 아이템 카드’ 와 ‘피드 아이템 제네레이터’를 만들었어요. 시작하기 전에 Server Driven UI를 먼저 설명드릴게요.Server Driven User InterfaceServer Driven User Interface(SDUI)는 서버에서 UI의 구조와 동작을 정의하고 제어하는 방식이에요. 서버에서 UI 명세를 내려주면 클라이언트는 이 명세에 따라 화면을 그리죠. 이렇게 하면 새로운 UI를 실험할 때 클라이언트 앱을 매번 업데이트하지 않아도 된다는 장점이 있어요.예를 들어, 기존에는 홈 피드의 UI를 변경하려면 위에서 설명한 것처럼클라이언트 개발앱 배포사용자들의 앱 업데이트충분한 사용자 수 확보의 과정이 필요했지만, SDUI를 도입하면 서버에서 UI 명세만 변경하면 바로 실험을 시작할 수 있어요.SDUI를 효과적으로 활용하기 위해서는 적절한 수준의 구현이 중요했는데요. 서버에서 모든 구조를 정의할 수 있는 HTML처럼 세밀한 수준의 SDUI도 가능하지만, 우리 팀은 다음 세 가지 원칙에 따라 SDUI를 구현했어요.1. 검증된 레이아웃 기반의 유연성기존 서비스들(중고거래, 부동산, 중고차, 당근알바)의 검증된 레이아웃을 기반으로 유연성을 가져가요. 피드가 중고거래 게시글을 시작으로 성장했기 때문에, 검증된 UX를 해치지 않는 선에서 썸네일 크기, 텍스트 스타일, 섹션 배치 등을 서버에서 제어해요.2. 효율적인 스타일 관리서버에서는 UI 레이아웃과 디자인 시스템의 아이콘 토큰, 컬러 토큰과 같은 넓은 범위의 스타일만 정의하고, 클라이언트는 이에 따라 렌더링해요. margin, border-radius 같은 세부 스타일 값은 실험 영역에서 제외하여 복잡도를 낮췄어요.3. 표준화된 인터페이스클라이언트의 동작을 표준화된 인터페이스로 정의해요. 이는 기존 뷰타입의 문제점을 해결하기 위한 원칙이에요. 예를 들어 특정 도메인에 종속된 뷰타입은 게시글 ID로만 화면 전환이 가능해서 재사용하기 어려웠거든요. 이제는 뷰타입별로 흩어져 있던 클라이언트 동작 처리 방식을 하나로 모으고, 서버에서 UI 관련 동작을 일관되게 제어할 수 있게 됐어요.피드 아이템 카드이러한 SDUI 원칙을 바탕으로 저희 팀은 ‘피드 아이템 카드’를 만들었어요. 피드 아이템 카드는 저희 팀이 정의한 통합 뷰타입으로, 다양한 서비스의 콘텐츠를 일관된 방식으로 보여줘요. 지금부터 피드 아이템 카드가 어떤 구조로 이루어져 있고, 앞서 설명한 뷰타입 재사용과 소통의 문제를 어떻게 해결했는지 소개해드릴게요.Section? Component? Property?Feed Item Card의 구성요소피드 아이템 카드는 Section, Component, Property 세 개의 계층으로 이뤄져요. 각 계층이 어떤 역할을 하는지 하나씩 살펴볼게요.Section, Component, Property 예시가장 상위 계층인 Section은 피드 아이템 카드에서 가장 큰 영역을 차지하는 요소예요. 게시글의 대표 이미지를 보여주는 Thumbnail Section, 게시글의 제목과 내용 등 주요 정보를 보여주는 Info Section이 대표적이죠. 일관된 사용자 경험을 위해 Section의 순서나 위치는 변경할 수 없고, 일부 Section은 필수로 포함해야 해요.중간 계층인 Component는 Section이 의미를 가지도록 돕는 구성요소예요. Component는 독립적으로 동작하는 기본 단위이며, 각각의 Component는 고유한 Property들을 가지고 있어요. 예를 들어 Info Section 안에 있는 Tag Group Component는 텍스트나 배지 같은 Property들로 원하는 정보를 표시해요.Property는 Component의 특성을 결정하는 가장 기본적인 요소예요. Property는 혼자서는 의미를 가질 수 없고, 반드시 Component에 속해 있어야 해요. Tag Group Component를 예로 들면, 텍스트나 배지, 이미지 등의 Property들은 Tag Group이라는 맥락 안에서만 의미를 가져요.Property는 필수 Property와 선택 Property로 나뉘어요. 예를 들어, 게시글의 상태를 나타내는 Status Component에서 ‘예약중’이나 ‘거래완료’ 배지는 선택 Property예요. 반면 가격이나 게시글의 속성을 나타내는 텍스트는 필수 Property로, 항상 표시되어야 하죠.이러한 계층 구조 덕분에 피드 아이템 카드는 높은 유연성과 재사용성을 갖게 되었어요. 예를 들어 동일한 Tag Group Component를 중고거래와 부동산 게시글에서 각각 다른 Property 조합으로 활용할 수 있게 되었죠.또한 모든 구성 요소가 명확한 계층으로 구분되어 있어 디자이너와 개발자 간 소통도 한결 수월해졌어요. 각자의 역할에서 동일한 구조를 바라보며 작업할 수 있게 되었거든요.새로운 기능이나 디자인을 실험할 때도 큰 이점이 있어요. 기존 컴포넌트들을 새롭게 조합하거나 일부 속성만 수정해서, 실험 모수를 확보하지 않고도 빠르게 변화를 줄 수 있게 되었답니다. 처음부터 새로 만들 필요 없이 검증된 컴포넌트들을 활용할 수 있게 된 거예요.피드 액션!기존에는 클라이언트에서 피드의 이벤트를 처리하는 방식이 뷰타입별로 제각각이었어요. 예를 들어, 중고거래 게시글은 클라이언트가 게시글 ID를 파싱해서 상세화면으로 이동했지만, 당근알바 게시글은 정해진 URI로 이동하는 식이었죠.이런 파편화된 액션들을 하나로 모으기 위해 ‘피드 액션’이라는 통합 시스템을 만들었어요. 이를 통해 서버에서 다양한 액션을 일관되게 제어할 수 있어요.구체적인 예시를 들어볼게요. 홈 피드에서 게시글 숨기기 버튼을 누르면:서버에 숨긴 게시글을 저장하는 HTTP 요청을 보내고유저의 피드에서 피드 아이템을 숨기고아이템 숨기기 이벤트를 로깅하는 것을클라이언트에서 한 번에 처리할 수 있어요. 이런 피드 액션 시스템 덕분에 서버에서 피드 아이템 카드의 동작을 더 체계적이고 유연하게 제어하게 됐어요.피드 아이템을 그리기 위한 피드 아이템 제네레이터피드 아이템 카드의 구조를 만들었으니 각 서비스의 정보를 이 카드에 맞게 변환하는 프로젝트가 필요했어요. 이를 위해 ‘피드 아이템 제네레이터’를 만들었죠. 이름 그대로 피드 시스템의 데이터(피드 엔티티)를 클라이언트 화면(피드 아이템)으로 만들어주는 모듈이에요.기존에는 중고거래팀, 알바팀 등 각 서비스팀이 자신만의 방식대로 화면을 구성했어요. 그러다 보니 아래와 같은 문제점들이 발생했죠.비슷한 화면을 여러 팀에서 각각 만들어 리소스가 낭비됐어요.일관된 사용자 경험을 제공하기 어려웠어요.실험이나 신규 기능을 도입할 때 각 팀과 조율이 필요했어요.이제는 피드 인프라팀이 피드 아이템 생성을 전담하면서 여러 장점이 생겼어요.1. 서비스별, 국가별로 다른 UI를 효율적으로 관리할 수 있어요.예를 들어 중고거래에서는 유저와 매물의 거리를 보여주고, 중고차에서는 누적주행거리를 보여줘요.피드 엔티티 서비스에서 각 서비스의 데이터를 독립적으로 관리하기 때문에 이런 차별화가 가능해요.각 서비스의 특성을 살리면서도 일관된 사용자 경험을 제공할 수 있어요.2. 앱 버전에 따른 하위호환을 체계적으로 지원해요.새로운 컴포넌트가 추가되어도 앱 버전에 따라 적절한 컴포넌트를 보여줄 수 있어요.예를 들어 아이콘 형식이 바뀌면 구 버전 사용자에게는 이전 버전 컴포넌트를 보여주고 최신 버전 사용자에게는 새 컴포넌트를 보여줘요.이를 통해 사용자는 앱 업데이트 여부와 관계없이 안정적인 서비스를 이용할 수 있어요.3. 새로운 실험도 안전하고 효율적으로 할 수 있어요.섹션별로 독립적인 구조라 한 부분의 변경이 다른 부분에 영향을 주지 않아요.실험은 최신 버전에서만 진행해 실험 코드가 여러 곳에 흩어지는 것을 방지했어요.중앙화된 관리로 실험 결과를 빠르게 분석하고 적용할 수 있어요.다만 중앙화된 관리 방식에는 단점도 있어요. 각 서비스팀이 원하는 변경사항을 즉시 적용하기 어렵고, 피드 인프라팀의 작업량이 늘어날 수 있죠. 하지만 일관된 사용자 경험과 안정적인 서비스 제공이라는 이점이 더 크다고 판단했어요. 각 서비스팀과 긴밀하게 소통하며 우선순위를 조율하는 방식으로 이런 단점을 최소화하고 있답니다.이렇게 피드 아이템 제네레이터는 단순히 데이터를 변환하는 것을 넘어, 효율적이고 안정적인 서비스 제공의 핵심 역할을 하고 있어요.결과피드 아이템 카드와 피드 아이템 제네레이터 프로젝트를 실제 피드 프로덕트에 적용하면서 큰 변화가 있었어요. 기존에는 새로운 UI를 실험하려면 클라이언트 앱을 업데이트하고 배포하는 과정이 필요했지만, 이제는 서버에서 바로 새로운 UI를 정의하고 실험할 수 있게 되었어요. 실험 준비에 필요한 서버 개발도 1~2주면 충분해 실험까지의 시간이 크게 단축되었어요. 이를 통해 더 많은 아이디어를 빠르게 검증할 수 있는 환경을 만들 수 있었어요.몇 가지 실험 결과를 살펴볼까요?모임 정보노출 실험동네생활 모임은 운동, 독서 등 공통 관심사를 가지고 이웃들과 온오프라인으로 소통하고 만날 수 있는 서비스인데요. 동네생활 모임 활성화 실험에서는 모임의 최근 활동시간을 보여주고 관심 수를 추가하는 변화를 주었어요. 그 결과 클릭수가 대조군 대비 3% 상승했고, 실험 적용까지는 8일(워킹데이 기준)이 걸렸어요. 특히 이 실험에서는 컴포넌트 단위의 코드만으로 변경사항을 적용할 수 있었고, 실험군의 롤아웃도 기존 실험 코드를 실제 코드로 적용하는 것만으로 충분했어요.비즈니스(업체) 소식 UI 실험비즈니스 소식은 지역의 사장님이 작성한 게시글을 홈피드에 노출하는 서비스인데요. 비즈니스 소식 UI 개선 실험에서는 피드 아이템 카드가 지원하는 다양한 컴포넌트를 활용해 비즈니스 소식의 정보를 전달하고자 했어요. 예를 들어 사장님이 작성한 게시글의 본문을 노출하거나, 단골수를 보여주거나, 업체 이름을 보여주는 실험을 진행했어요. 실험 결과 유저들의 단골 맺기 수가 대조군과 5~10% 만큼의 차이를 보였어요. 이런 긍정적인 결과를 바탕으로 유저에게 더 필요한 정보를 전달하기 위한 추가 실험들을 준비하고 있어요.앞으로의 계획당근은 “활발한 교류가 있는 지역사회를 위해 모바일 기술로 가까운 동네 이웃들을 연결”한다는 비전을 향해 나아가고 있어요. 피드 인프라팀은 이를 기술적으로 실현하기 위해 중고거래, 부동산, 중고차, 당근알바 등 당근의 다양한 서비스들을 하나의 홈 피드로 연결하고 있죠. 앞으로도 피드 아이템 카드의 다양한 컴포넌트와 피드 아이템 제네레이터의 실험 기능을 지속적으로 발전시킬 예정이에요.이를 위해 SDUI와 같은 혁신적인 기술로 복잡한 문제를 해결하고 새로운 아키텍처를 설계하며 함께 성장할 백엔드 엔지니어를 찾고 있는데요. 유저들이 좋아하는 콘텐츠를 안정적으로 서빙할 수 있는 더 나은 방식을 고민하고, 직접 작성한 코드로 수많은 이웃들의 일상을 더 풍요롭게 만드는 경험을 해보고 싶지 않으세요? 피드 인프라팀의 문은 활짝 열려 있답니다!👉 피드 인프라팀 엔지니어 지원하기당근 홈 피드, Server Driven UI로 실험 이터레이션 빠르게 돌리기 was originally published in 당근 테크 블로그 on Medium, where people are continuing the conversation by highlighting and responding to this story.

100여개의 대출 기관 API, 자동으로 운영하기
뱅크샐러드
100여개의 대출 기관 API, 자동으로 운영하기

안녕하세요! 뱅크샐러드의 Server Engineer 조성민입니다. 이번 글에서는 제 팀인 금융쇼핑 PA…

[네이버클라우드 아카데미] 건양대 SW중심대학과 성황리에 마무리한 <클라우드 Literacy 과정> 수료식
네이버 클라우드
[네이버클라우드 아카데미] 건양대 SW중심대학과 성황리에 마무리한 <클라우드 Literacy 과정> 수료식

안녕하세요, 누구나 쉽게 시작하는 클라우드 네이버 클라우드 플랫폼 ncloud.com입니다. #네이버클라우드 #네이버클라우드아카데미 #건양대SW중심대학 #NCA클라우드 자격증 교육 지난 11월 29일 금요일, 건양대 SW중심대학과 함께한 첫 번째 과정 수료식이 개최되었습니다. 이번 수료식은 건양대 SW 중심대학과 함께한 네이버클라우드 아카데미의 첫 번...

[술술 읽히는 업무 해설집 - 근태편] 연차 언제까지 쓸 수 있게 하나요?
네이버 클라우드
[술술 읽히는 업무 해설집 - 근태편] 연차 언제까지 쓸 수 있게 하나요?

안녕하세요, 협업과 소통을 위한 필수 기능으로 글로벌 53만 기업의 든든한 협업툴 역할을 해온 네이버웍스(NAVER WORKS)입니다! "업무와 관련된 것이라면 뭐든지 쉽게 풀어드립니다!" 술술 읽히는 업무 해설집 내년도 연차를 올해 미리 당겨 쓸 수 없나요? 올해 남은 연차는 내년에 이어서 사용할 수 있나요? 연말이 다가오면 인사 담당자가 직원들로부...

[고객사례] 로커스, "네이버웍스는 로커스의 온전한 업무 몰입을 위한 임직원 복지입니다."
네이버 클라우드
[고객사례] 로커스, "네이버웍스는 로커스의 온전한 업무 몰입을 위한 임직원 복지입니다."

안녕하세요, 누구나 쉽게 시작하는 클라우드 네이버클라우드 ncloud.com 입니다. 이번 포스팅에서는 네이버웍스를 통해 맞춤형 업무 몰입 환경을 구축한 '로커스'를 소개해 드리려고 해요! 로커스는 극장용 애니메이션부터 광고, 드라마, 미디어아트까지 다양한 분야에서 고품질 콘텐츠를 선보이는 종합 콘텐츠 기업입니다! 로커스 홈페이지 바로 가기 로커스 경...

[네이버클라우드캠프] 2024 네이버클라우드캠프 서포터즈 발대식 현장
네이버 클라우드
[네이버클라우드캠프] 2024 네이버클라우드캠프 서포터즈 발대식 현장

안녕하세요, 누구나 쉽게 시작하는 클라우드 네이버클라우드(ncloud.com)입니다. #네이버클라우드 #네이버클라우드캠프 #네이버클라우드캠프서포터즈 지난 11월 15일 오후, 네이버 그린팩토리에서 '2024 네이버클라우드캠프 서포터즈 발대식'이 진행되었습니다. 이번 모집에서는 치열한 경쟁률을 뚫고, 총 48명의 서포터즈가 최종 선발되었는데요! 서포터즈...

2025년, 생성형 AI 트렌드 전망
삼성 SDS
2025년, 생성형 AI 트렌드 전망

이 아티클에서는 2024년 올해 기업 내에서 생성형 AI 도입이 애초 기대보다 더디게 진전되고 있는 원인과 이를 바탕으로 내년도에 전망하는 트렌드에 대해 자세히 살펴봅니다.

시니어 사용자가 어려워하는 UX 5가지
토스
시니어 사용자가 어려워하는 UX 5가지

시니어 UX 설계 가이드라인을 만들기 위해, 리서치로 시니어 사용자들의 공통된 사용성 패턴을 찾는 과정을 들려드릴게요.

스마트 승급 시스템, 회원 승급 자동화의 혁신 스토리
올리브영
스마트 승급 시스템, 회원 승급 자동화의 혁신 스토리

Introducing Configurable Metaflow
넷플릭스
Introducing Configurable Metaflow

David J. Berg*, David Casler^, Romain Cledat*, Qian Huang*, Rui Lin*, Nissan Pow*, Nurcan Sonmez*, Shashank Srikanth*, Chaoying Wang*, Regina Wang*, Darin Yu**: Model Development Team, Machine Lear...

[소식✨] 클로바 스튜디오와 랭체인(LangChain)으로 AI 개발을 더 쉽게
네이버 클라우드
[소식✨] 클로바 스튜디오와 랭체인(LangChain)으로 AI 개발을 더 쉽게

안녕하세요, 누구나 쉽게 시작하는 클라우드 네이버클라우드 ncloud.com 입니다. 오늘은 AI 개발을 더 쉽게 만들어주는 LangChain에 대해서 소개해 드리겠습니다. LangChain을 쓰면 어떤 점이 좋은가요? HyperCLOVA X를 포함한 여러 언어 모델과 벡터 데이터베이스, 검색 엔진 등의 여러 도구를 사슬(Chain)처럼 엮어 연결할 ...

Java 가상 스레드, 깊이 있는 소스 코드 분석과 작동 원리 2편 - 컨텍스트 스위칭
라인
Java 가상 스레드, 깊이 있는 소스 코드 분석과 작동 원리 2편 - 컨텍스트 스위칭

들어가며 지난 1편에서는 가상 스레드(virtual thread)의 장점을 살펴보고 가상 스레드를 어떻게 생성하고 시작하는지 알아봤습니다. 이어서 이번 글에서는 컨텍스트 스위칭(c...

당근페이 재무 결산 사례로 보는 백엔드와 데이터의 만남
당근마켓
당근페이 재무 결산 사례로 보는 백엔드와 데이터의 만남

안녕하세요. 저는 당근페이 머니서비스팀 백엔드 엔지니어 클로버(Clover)에요! 당근페이에서는 동네에서 쉽고 편하게 쓸 수 있는 금융 서비스를 만들고 있어요. 당근에서 일어나는 거래에는 당근머니가 사용되는데요. 머니서비스팀에서는 당근머니와 관련된 일들에 더해, 동네에서 쓰면 다양한 혜택이 적립되는 당근카드와 관련된 일들을 하고 있어요. 다시 말해 저희 팀은 지역에서 생기는 다양한 거래를 연결하는 게 목표예요!재무 결산의 중요성당근페이에서는 매월 셀 수 없이 많은 거래가 이루어지는데요. 매월 이 거래들을 바탕으로 당근페이 내에서 돈이 어떤 경우에 어디에서 어디로 흘러갔는지 재무 결산을 진행해요. 예를 들면 당근머니를 충전하면 사용자의 계좌에서 당근페이 모계좌로 돈이 이동하는데, 당근머니 충전액의 총합은 모계좌 입금액의 총합과 같아야 해요. 재무 결산을 했는데 단 1원이라도 맞지 않는다면 그 1원을 찾기 위해 모든 업무를 중단해야 해요. 그만큼 핀테크 회사인 당근페이에서는 재무 결산이 중요해요.당근페이 초기에는 거래량이나 거래 종류가 많지 않았기 때문에 데이터를 직접 SQL 쿼리로 뽑았었어요. 그러다가 당근페이가 커지면서…결산 어드민 도입 전 머니 결산 스레드위와 같이 매월 초에 열리는 머니 결산 스레드에서, 백엔드 엔지니어들이 태그되기 시작했어요. 머니서비스팀에 도메인 담당자가 많아질수록 매월 결산 슬랙 스레드에 태그되는 사람도 점점 많아졌죠. 결국 머니서비스팀 백엔드 개발자들은 매월 첫 주에는 정기적으로 코드가 아닌 SQL 쿼리를 치고 있는 상황이 일어났어요.이런 비효율을 어떻게 개선해 볼 수 있을까요? 모두가 SQL 쿼리를 더 정확하고 빠르게 작성하도록 SQLD 자격증 스터디를 열어볼 수도 있을 듯해요. 하지만 저희 팀에서는 이를 시스템적으로 개선해보고자 했어요.Spring Batch와 MySQL기반 결산가장 쉽게 떠오르는 방법은 스프링 배치를 이용하는 방법이에요. 대용량 데이터를 처리할 때 가장 흔하게 사용하는 기술이죠. 처음에는 저희 팀도 이 방법으로 결산을 진행했어요. 대략적인 구조는 다음과 같았어요.당근페이 머니서비스팀은 마이크로서비스로 컴포넌트들이 구성돼 있어서, 각자 다른 데이터베이스를 가지고 있어요. 여러 데이터들을 종합해서 진행하는 결산의 특성상, 배치는 여러 개의 데이터베이스를 바라보고 진행해요.이러한 결산은 크게 두 단계로 구성돼 있어요.원본 DB에서 성공 거래를 바탕으로 원장(raw data)을 구성해 결산 전용 DB에 밀어 넣기위에서 가져온 원장을 기반으로 집계해 일별 결산 리포트를 만들고 결산 전용 DB에 쌓기이런 단계별로 구성된 배치를 매일 00시에 Cron을 통해 트리거해요. 재무 담당자나 결산 이해관계자들은 이렇게 단계별, 일자별로 잘 집계된 데이터들을 스프링 기반 백엔드 및 결산 프론트 화면을 통해 언제든지 조회할 수 있어요. 여기까지는 괜찮은 것 같아요. 그러던 어느 날 문제를 마주했죠.구조의 한계배치 실패 알림 발생어느 날 갑자기 이런 배치 작업 실패 알림을 받게 됐어요. 보통 이렇게 결산 배치를 실패하는 이유는 크게 세 가지가 있어요.결산 코드 비즈니스 로직 상의 문제로 인한 실패원본 테이블 구조(혹은 필드 ENUM 타입) 변경으로 인한 실패인프라나 애플리케이션 자체(JVM, DB 커넥션 등)의 이슈로 인한 실패여기서 첫 번째와 두 번째 이슈가 가장 잦으면서도 중요한 이슈라 조금 더 구체적으로 예시를 들어볼게요. 예를 들어, 당근페이가 “당근머니 송금” 기능만을 제공하다가, “당근머니 결제”라는 기능을 추가적으로 오픈했다고 가정할게요. 이때 당근머니 DB에는 “송금”이라는 거래 타입뿐만 아니라 “결제”라는 거래 타입도 추가될 거예요. 게다가 기존의 “송금”은 단순히 사용자 간의 지갑 잔액이 변한다면, “결제”는 지갑 잔액 변동과 함께 외부로의 정산이 발생해요. 이 경우 테이블 구조뿐만 아니라 결산에 대한 비즈니스 로직도 변경될 수 있어요.물론 당근머니만을 대상으로 결산한다면 이는 큰 문제는 아닐 거예요. 당근머니와 관련된 기능이 추가될 때마다 결산에도 추가적으로 대응하면 되니까요. 하지만 당근페이에서의 결산은 당근머니, 은행 거래, 카드 거래 등 다양한 서비스를 기반으로 진행하게 돼요. 그리고 각 서비스들은 결산에서 긴밀하게 얽혀 있죠. 결국 결산은 언제든 깨질 수 있는, 테이블 끝에 걸친 유리잔이 됐어요. 각 서비스들에 새로운 기능이 생길수록 작은 변화가 결산에 큰 영향을 미치게 되고, 결국 결산 오류가 더 자주 발생하게 된 거예요.이외에도 다른 서비스 컴포넌트에서 기능이 추가될 때 대응이 복잡하다는 문제가 있어요. 결산이 하는 일은 거래들을 일자별로 모아서 더하고 빼는 것뿐이지만, 새 기능이 도입될 때마다 결산에 추가적으로 코틀린 ENUM 타입을 추가하고 코드를 작성해야 하는 건 굉장히 비효율적이죠. 모든 팀원들이 결산의 내부 동작 구조를 이해하고 있는 건 아니니까요.위의 문제를 해결할 수 있는 방법은 없을까요? 조금 더 엄밀히 말하면 테이블 변경이나 비즈니스 로직 변경에 좀 더 유연하게 대응하려면 어떻게 해야 할까요?Airflow와 dbt를 활용한 데이터 기반 결산Airflow와 dbt는 백엔드 개발자에게는 조금은 생소한 개념일 수도 있는데요. 우선 Airflow는 초기에 Airbnb에서 개발한 작업(워크플로)을 코드를 통해 작성하고, 스케쥴링하고, 모니터링할 수 있는 플랫폼이에요.dbt는 원시 데이터(raw data)를 분석 가능한 형태의 데이터로 변환하고, 데이터 중심의 결정을 내리도록 돕는 프레임워크예요. dbt는 Airflow 위에서 실행되면서, 미리 작성해 둔 SQL 쿼리를 바탕으로 데이터를 가공해요. 쿼리 결과에 맞는 형태로 테이블이나 뷰를 만들어주는 툴이라고 볼 수 있어요.기존 구조였던 Spring Batch와 MySQL 기반 결산과 대조되는 개념들로 이해하면 더 쉽게 이해할 수 있어요. 기존의 원장 데이터와 집계된 데이터를 저장하던 MySQL 데이터베이스는 데이터 웨어하우스가 되었고, Argo Workflow가 해주던 작업 스케쥴링은 이제 Airflow가 담당해요. 데이터 변환과 집계 로직을 담고 있던 Spring Batch는 dbt가 대신하게 됐고요!가장 큰 변화는 각 서비스 컴포넌트의 DB 구조를 이해하고 직접 집계해야 했던 기존 결산 구조에서 벗어났어요. dbt를 통해 각 서비스가 오너십을 가지고 집계 쿼리를 작성하고, 결산 서비스는 잘 집계된 데이터를 분류에 맞게 시각화하는 기능을 제공하게 됐어요. 이제 더 이상 각 컴포넌트의 기능 추가로 인해 결산 집계가 실패하는 일은 없어졌어요.큰 그림 말고, 이제 조금 더 깊게 살펴볼까요?dbt 스키마 구조위에서 기존 MySQL 기반 결산을 소개하면서, 결산은 크게 두 단계로 나뉜다고 설명드렸어요. 먼저 거래 원장을 구성하고, 원장을 바탕으로 결산 집계 작업을 진행하죠. 실제 재무 결산에서는 집계된 데이터를 보고 진행하지만, 혹여 결산 과정에서 틀어지거나 올바르지 않은 데이터가 있을 때를 교차검증하기 위해 원장 테이블이 존재해요.예를 들어, “프로모션 지급” 거래의 원장은 다음과 같은 쿼리를 통해 성공한 거래 내역을 추출해요.-- promotion_ledger.sql (프로모션 지급 거래 원장)SELECT 거래ID, 거래타입, 거래금액, 생성일시FROM 거래내역WHERE 거래타입 = '프로모션' AND 거래상태 = '성공'# dbt YAMLmodels: - name: promotion_ledger description: 프로모션 지급 거래 원장 columns: - name: 거래금액 description: 거래에서 발생한 금액 (실제 지급된 금액) data_tests: - not_null - dbt_utils.expression_is_true: expression: &quot;&gt; 0&quot;이러한 형태로 추출된 dbt 스키마는 거래타입별로 존재해요. 모든 거래 타입의 거래 원장을 하나의 스키마로 관리하지 않는 이유는 실제 재무실 니즈에 조금 더 유연하게 대응하기 위함인데요. 예를 들면 같은 “프로모션” 거래타입 일지라도, 당근페이 내에서 진행한 프로모션이 있을 수도 있고 다른 서비스에서 진행한 프로모션이 있을 수 있어요. 원장을 거래타입별로 구분하면서 이러한 구체적인 거래 분기에 조금 더 유연하게 대응할 수 있게 되었어요.data_tests: - not_null - dbt_utils.expression_is_true: expression: &quot;&gt; 0&quot;그리고 이 표현처럼 특정 필드의 값을 테스트할 수 있게 됐어요. 위 경우에서는 거래 금액이 null이 아니고 0보다 커야 함을 테스트하는 표현이에요. 데이터 변환과 함께 원장 데이터의 무결성도 함께 검증할 수 있게 된 셈이에요.이렇게 수집된 원장을 기반으로 다음과 같이 일별 집계를 진행해요.-- daily_report.sql (일별 결산 집계)WITH -- 프로모션 지급daily_promotion AS ( SELECT DATE(생성일시) AS 대상날짜, 거래타입, SUM(거래금액) AS 총거래금액 FROM {{ ref('promotion_ledger') }} GROUP BY 1, 2),-- 머니송금daily_transfer AS ( SELECT DATE(생성일시) AS 대상날짜, 거래타입, SUM(거래금액) AS 총거래금액 FROM {{ ref('transfer_ledger') }} GROUP BY 1, 2)...SELECT * FROM daily_promotionUNION ALLSELECT * FROM daily_transfer...# DBT YAMLmodels: - name: daily_report description: 일별 결산 집계 columns: - name: 총거래금액 description: 일별 거래에서 발생한 총 금액 data_tests: - not_null - dbt_utils.expression_is_true: expression: &quot;&gt; 0&quot;앞서 수집했던 원장 스키마를 바탕으로 일별 거래타입별 거래액을 집계해요. 집계 결과도 마찬가지로 상황에 맞게 테스트를 진행해요.결국 이렇게 정의된 SQL과 dbt YAML들을 바탕으로 Airflow는 dbt Task를 실행시켜 주고, 이 Task는 결국 필요한 데이터를 데이터 웨어하우스에 밀어 넣어줘요. 그렇다면 이 데이터는 어떻게 기존 결산 어드민처럼 재무팀분들에게 보여줄 수 있을까요? 재무팀분들께 DW 접근 권한을 드려야 할까요?Spring Boot에 데이터 웨어하우스 연결하기현재 당근페이에서 사용하고 있는 데이터 웨어하우스는 Redshift인데요. 놀랍게도 Redshift(정확히는 AWS)에서 JDBC 드라이버를 제공하고 있어요. JDBC 드라이버가 존재한다는 것은 Spring Data JPA를 그대로 활용할 수 있다는 것을 의미해요. 결국 실제 백엔드에 새로운 쿼리 전용 라이브러리를 도입할 필요 없이, 기존 기술 스택 그대로 새로운 Airflow와 dbt 기반 결산 데이터를 조회할 수 있게 됐어요.우선 다음과 같이 Gradle에 RedShift JDBC 드라이버를 추가하고,dependencies { runtimeOnly(&quot;org.hibernate.orm:hibernate-community-dialects:6.4.4.Final&quot;) runtimeOnly(&quot;com.amazon.redshift:redshift-jdbc42:2.1.0.30&quot;)}다음과 같이 driver-class-name 과 database-platform 값, 그리고 RedShift 접근 정보를 채워주면 돼요.spring: jpa: driver-class-name: com.amazon.redshift.jdbc.Driver database-platform: org.hibernate.community.dialect.PostgreSQLLegacyDialect datasource: url: jdbc:redshift://{DB_HOSTNAME}:{DB_PORT}/{DB_DATABASE} username: {DB_USERNAME} password: {DB_PASSWORD}기존에 Spring Boot 2.x 에서는 Hibernate에 내장된 Dialect인 org.hibernate.dialect.PostgreSQL9Dialect를 사용할 수 있었지만, Spring Boot 3.x에서 해당 클래스가 사라지면서 org.hibernate.community.dialect.PostgreSQLLegacyDialect 로 지정해야 해요.그 외에는 기존 Spring Boot JPA와 똑같아요. 다음과 같이 Entity를 정의하고 쓰면 돼요. (대신 애플리케이션에서 업데이트하는 것을 방지하기 위해 @Immutable 어노테이션을 추가했어요.)@IdClass(DailyReportEntity.DailyReportEntityKey::class)@Immutable@Entity@Table(name = &quot;daily_report&quot;)abstract class DailyReportEntity( @Id @Column(name = &quot;대상날짜&quot;, nullable = false) val targetDate: LocalDate = LocalDate.now(), @Id @Column(name = &quot;거래타입&quot;, nullable = false) val transactionType: String = &quot;&quot;, @Column(name = &quot;총거래금액&quot;, nullable = false) val totalAmount: Long = 0L,) { data class DailyReportEntityKey( val targetDate: LocalDate = LocalDate.now(), val transactionType: String = &quot;&quot;, ) : Serializable}그 외에 다른 로직들은 기존 Spring JPA 사용법과 전부 동일해요. 다시 말해 과거에 Spring Batch와 MySQL기반으로 결산 데이터를 모으고 쌓는 부분만 Airflow로 이전하고, 그 외의 결산 결과를 확인하던 대시보드와 조회 로직은 그대로 사용할 수 있었어요. 덤으로 기존 MySQL로 되어있던 결산 데이터베이스는 필요 없어졌고요!최종 결산 아키텍처 구조는 다음과 같아요.(데이터팀분들이 구축해 주신 부분) 매 시간마다 운영(Reader 인스턴스) MySQL 데이터베이스에서 Spark를 통해 데이터 웨어하우스로 테이블이 싱크돼요.매일 Airflow는 dbt를 통해 결산에 필요한 원장과 일별 집계 데이터를 테이블로 가공해요.재무 담당자분들은 결산 어드민 프론트 화면을 통해서 데이터 웨어하우스에 적재된 결산 데이터를 볼 수 있어요.이루어낸 성과우선 기존 Spring Batch와 MySQL 기반 결산의 한계였던 구조적 복잡성을 해결했어요. 덕분에 머니서비스팀 내의 백엔드 개발자분들이 각자가 개발한 기능에 대한 결산 대응을 SQL 쿼리 수정만으로 쉽게 해결할 수 있게 됐죠.이는 단순히 작업자의 접근성을 끌어올려줬을 뿐만 아니라, 실제 결산 시 기능 추가 과정에서 필요한 작업들을 효과적으로 줄이기까지 했어요. 이제 더 이상 애플리케이션에서 각 타입을 알고 계산 로직을 추가해야 하는 게 아니라 SQL 쿼리에서만 대응하면 되니까요![Before]Spring Batch + MySQL 아키텍쳐에서 결산 로직 변경시 사례[After]Airflow + dbt 아키텍쳐에서 결산 로직 변경시 사례확실히 기존에 코틀린 코드로 존재하던 결산을 SQL 쿼리로 변경하니 수정 사항을 변경하는 게 간단해지기도 하고 가시성도 높아졌죠?마치며현대 사회에서 데이터의 가치는 회사의 중요한 자산과 경쟁력이 되고 있어요. 특히 당근페이와 같은 핀테크 기업에서 이러한 데이터의 정합성과 투명성은 사용자들의 신뢰를 구축하기 위한 핵심 요소예요. 위 사례에서 살펴본 데이터 기반 결산 아키텍처로의 변화는 단순한 결산 과정 비효율 개선을 넘어서, 장기적으로 당근페이 내 구성원들이 데이터 기반의 의사결정과 소통을 할 수 있도록 도와요. 결국 이를 통해 사용자에게 더 나은 서비스를 제공하는 데 기여할 수 있을 거예요.따라서 당근페이에서는 데이터 중심적 사고를 백엔드 엔지니어에게도 중요한 역량으로 인식해요. 당근페이의 백엔드 엔지니어들은 단순히 코드를 작성하는 것을 넘어, 데이터의 정합성과 무결성을 보장하기 위해 위의 Airflow 같은 다양한 데이터 중심의 아키텍처를 탐구하죠. 또한 데이터 중심적 사고방식을 확산하려고 노력하고 있기도 해요. 조직 전체가 데이터를 적극적으로 이해하고 활용하려는 문화가 정착되면서, 모두가 데이터에 기반해 더 나은 의사결정을 할 수 있도록 변화하고 있어요.지역의 다양한 거래를 연결한다는 팀의 비전을 위해 비효율적인 부분을 개선하고 데이터 관점에서도 끝없이 고민하는 머니서비스팀! 저희 팀이나 오늘의 이야기에 대해 궁금하신 게 있다면 clover@daangnpay.com 으로 연락 주세요! 사소한 궁금증도 괜찮아요 ✌️또 현재 머니서비스팀에선 기술의 장벽 없이 비효율적인 부분을 개선하고 다양한 문제를 해결해 나갈 백엔드 엔지니어도 채용하고 있으니 많은 관심 부탁드려요!https://about.daangn.com/jobs/4511184003/참조[1] https://airflow.apache.org/[2] https://www.getdbt.com/product/what-is-dbt당근페이 재무 결산 사례로 보는 백엔드와 데이터의 만남 was originally published in 당근 테크 블로그 on Medium, where people are continuing the conversation by highlighting and responding to this story.

프론트엔드 서비스 최적화? 토스에서는 '이렇게' 합니다! | EP.9 모닥불
토스
프론트엔드 서비스 최적화? 토스에서는 '이렇게' 합니다! | EP.9 모닥불

프론트엔드 개발자라면 한 번쯤 고민해봤을 성능 최적화! 토스 개발자들이 전하는 최적화의 본질과 실무 노하우를 공개합니다. 초기 로딩부터 런타임 최적화까지, 토스의 박서진, 박건영, 조유성 님이 전하는 최적화 사례와 비법을 만나보세요!

CloudFront의 숨은 힘: 캐싱 없이도 극대화 되는 성능과 비용 효율성
원티드
CloudFront의 숨은 힘: 캐싱 없이도 극대화 되는 성능과 비용 효율성

부제: CloudFront를 동적 콘텐츠에도 사용해보세요~!CloudFront에 대한 고정관념CloudFront에 대해 흔히 가지고 있는 두 가지 오해가 있습니다:정적 콘텐츠만의 영역이라고 생각 하는점.HTML, CSS, JavaScript, 이미지, 미디어 파일과 같은 정적 자원을 빠르게 전송하기 위한 도구로만 인식동적 콘텐츠 처리에는 부적합 하다는...

[24년 12월] 마케팅과 AI가 만난다면
네이버 클라우드
[24년 12월] 마케팅과 AI가 만난다면

지난 월간네클폼이 궁금하다면 아래 링크를 클릭해 확인해 보세요!

AI 에이전트가 바꾸는 이커머스 생태계
삼성 SDS
AI 에이전트가 바꾸는 이커머스 생태계

이 글에서는 사람의 개입 없이 자율적으로 특정 작업을 수행하는 지능형 시스템인 AI 에이전트의 개념과 이를 통한 차세대 이커머스 구축 전략 및 프레임워크를 자세하게 다룹니다.

Part 1: A Survey of Analytics Engineering Work at Netflix
넷플릭스
Part 1: A Survey of Analytics Engineering Work at Netflix

This article is the first in a multi-part series sharing a breadth of Analytics Engineering work at Netflix, recently presented as part of our annual internal Analytics Engineering conference. We k...

Cloud Efficiency at Netflix
넷플릭스
Cloud Efficiency at Netflix

By J Han, Pallavi PhadnisContextAt Netflix, we use Amazon Web Services (AWS) for our cloud infrastructure needs, such as compute, storage, and networking to build and run the streaming platform tha...

Title Launch Observability at Netflix Scale
넷플릭스
Title Launch Observability at Netflix Scale

Part 1: Understanding The ChallengesBy: Varun KhaitanWith special thanks to my stunning colleagues: Mallika Rao, Esmir Mesic, Hugo MarquesIntroductionAt Netflix, we manage over a thousand global co...

2024 DXCO 컨퍼런스: 뜨겁게 마무리한 성장과 교류의 장!
GS리테일
2024 DXCO 컨퍼런스: 뜨겁게 마무리한 성장과 교류의 장!

&nbsp; DX본부 커뮤니티 DXCO 연말 컨퍼런스가 새롭게 오픈한 GS역삼타워 사내행사 공간에서많은 분들의 참여와 호응 속에 성공적으로 마무리되었습니다! 이번 컨퍼런스는 하반기 동안 열심히 달려온 소모임 활동을 돌아보고,함께 성장과 교류의 의미를 되새긴 뜻깊은 자리였는데요~ 지금부터 그 열정 가득했던 현장을 생생하게 소개합니다! ✨ 잠깐, DXCO...

스칼라 컴파일 속도 빠르게 하기
데브시스터즈
스칼라 컴파일 속도 빠르게 하기

스칼라 프로젝트의 고질적인 문제인 컴파일 속도를 향상한 경험을 공유합니다.

Spring Boot MongoDB 트랜잭션 도입 실전 가이드
올리브영
Spring Boot MongoDB 트랜잭션 도입 실전 가이드

👉 들어가기에 앞서 안녕하세요! 올리브영에서 상품 도메인을 책임지고 있는 윤긱스입니다. 혹시 지금.. 이런 고민을 하고 계신가요? 🙋 : MongoDB…

디자인 시스템 중 디자인 토큰을 여러 도구를 이용하여 자동화 하는 방법
올리브영
디자인 시스템 중 디자인 토큰을 여러 도구를 이용하여 자동화 하는 방법

지능형 기술의 새로운 패러다임, AI 에이전트
삼성 SDS
지능형 기술의 새로운 패러다임, AI 에이전트

이 아티클에서는 지능형 기술 가운데 최근에 많이 회자되고 있는 AI 에이전트가 우리의 일상과 비즈니스를 어떻게 혁신하는지 알아봅니다.