Post

Toss Makers Conference 25 Engineering Day 후기

Toss Makers Conference 25 Engineering Day 후기

운좋게도 토스 메이커스 2025 컨퍼런스 엔지니어링 데이에 참석했습니다.
들었던 세션 중 인상깊었던 내용, 들으면서 작성했던 내용들을 공유하고자 합니다.
Image

기억에 남는 것

현장결제 서비스의 분산 트랜잭션 관리학 개론

  • 비즈니스 로직 구현 시 “표현 모델”을 작성하는 것

수천 개의 API/BATCH 서버를 하나의 설정 체계로 관리하기

  • 수천개의 배치 서버를 추가/관리하기 위해 젠킨스를 극한으로 사용하는 것이 인상적
  • job 생성/관리 자동화를 위해 오버레이 아키텍쳐에서 템플릿 패턴까지 발전시켜 휴먼에러를 최소화 해낸 과정

가맹점은 변함없이, 결제창 시스템 전면 재작성하기

  • 개선을 위한 코드 분석 시 코드를 뜯어보는 방법 : Entry / Prepare / Confirm => 이런식으로.. 패턴을 관찰해서 공통적으로 드러나는 패턴 분석하기
  • 관심사 분리가 아주아주 중요하구나~

당신은 이미 팀을 최적화 하고 있다

  • 신뢰의 3요소 : 역량 신뢰(이 사람이 이 일을 잘 해낼 수 있을거란 믿음) / 소통 신뢰(이 사람이 어떤 상황에서도 진심으로, 솔직하게, 맥락있게 소통할거란 믿음) / 계약 신뢰(약속을 지킬 사람이란 믿음)

중앙에서 Data Mesh로: Data Ownership 강화를 위한 변화

  • 토스의 데이터 엔지니어링 팀 분리 방식이 신기했음 (DEA/Data Mesh)

레거시 정산 개편기: 신규 시스템 투입 여정부터 대규모 배치 운영 노하우까지

  • 한방 쿼리를 분해하는 방법 -> JOIN, UNION ALL 등을 분해해서 문제를 작은 단위로 분해 => 새로운 요구사항 들어왔을 때 요구사항 충족 가능
    • 쿼리 의존적인 로직이 아니라 코드 의존적인 로직이 될 수 있도록
  • 자주 사용하는 조회 데이터는 조건 기반으로 조회 전용 테이블을 분리하여 성능 개선
  • 배치에서도 결국 IO를 최소화 하는게 중요하다
  • 외부 API 를 호출하는 경우 무조건 지연이 발생(API 자체의 호출 시간이 짧더라도 네트워크 타면 시간 지연이 걸림)
    • 이 경우 API 호출을 병렬처리함
    • Thread Safe 하지 않아 중복정산이나 누락 발생하는 경우 방지하기 때문에 동기화 적용 && 모듈러 연산으로 id를 나눠 스레드 별로 처리할 데이터를 명확히 나눠줌
  • 신규 시스템 투입 방법 : 카나리 투입
    • 레거시도 연산 진행하고 신규도 연산 진행
    • 레거시 == 신규 -> 신규 적용 / 레거시 != 신규 -> 레거시 적용 && 신규 에러 케이스 로깅
  • 안정성 면에서 젠킨스가 뛰어나므로 불편한 점은 젠킨스를 최대한 뜯어고쳐보자

더 빠르고, 더 정교한 이상금융거래탐지 시스템

  • 도메인에서 중요하게 여기는 데이터와 FDS에서 중요하게 여기는 데이터 성질의 차이가 발생할 때 => 카프카 이벤트 기반으로 거래 완료 이벤트 발생 시 FDS에서 필요한 데이터 가공하는 방식으로 처리
  • 새로운 룰을 추가할 때 일일히 코드로 구현해야 하는게 비효율적 => JSON을 이용한 룰 명세를 해서 Admin에서도 룰을 생성할 수 있도록 함
  • 룰 추가 시 Boolean처리 이슈(일부 정보만 가져오는데 성공했을 때, 가져오는데 실패한 데이터가 False로 처리되어 전체 탐지가 안되는 이슈 발생) => Unknown 상태를 추가하여 Unknown 상태값인 룰은 무시하는 방향으로 해결
  • 0빼기 이슈 : 휴먼 에러로 임계치 잘못 설정했을 때 => 전체의 xx%를 초과하면 해당 룰을 off

Spring 서버 애플리케이션 구동시간 줄이기

  • FlameGraph에서 병목구간 찾아내어 개선해낸것

+ Action Item

  • 회사 배치 서버에 오버레이 아키텍쳐 & 템플릿 패턴 적용해서 신규 job 생성 해 보기
  • 회사 젠킨스에 확인 후 JOB DSL 어댑터 적용해보기
  • 신뢰의 3요소에 맞게 행동하기
  • 로그 기반 어뷰징 탐지 배치 만들어보기(어뷰징이면 특정 기능 제한)
    • 배치 만들 때 성능 유의해서 만들어보기 : IO 최소화 / 멀티스레딩으로 데이터 병렬처리
    • 멀티스레딩 적용 시 모듈러 연산 활용해보기
    • FDS 세션 참고해서 Boolean 처리, 과탐방지 로직 적용하기
  • 외부 API 호출하는 로직을 병렬적으로 처리할 수 있을지 고민해보기 -> 이걸로 데이터독에서 Flame Graph 보고 Wall-Clock 분석해서 성능 좋아졌는지 확인해보기
  • 오래 걸리는 API의 Wall-Clock 분석하기
  • 신규 시스템 투입 방법 카나리로 배포할 수 있도록 하기
    • 레거시 고친 이후 카나리 투입으로
  • 배치 모니터링 도구 적용해보기 (Thread Dump / Async Profiler)
  • 이벤트 기반으로 어뷰징 탐지에 필요한 데이터 가공 시도해보기
  • 과탐지 자동 모니터링 로직 적용해서 어뷰징 탐지해보기 (전체의 xx% 넘어가면 해당 룰 off)
  • 스프링 프로젝트에서 인텔리제이 JFR 확인해서 Timeline wall-clock profiling 확인해보기
  • 스프링 프로젝트에 Executable JAR startup 사용중인지 확인해보기

작성한 내용

20년 레거시를 넘어 미래를 준비하는 시스템 만들기

  • 레거시 시스템 및 인프라 제약에 영향받지 않는 환경 만들기
  • 인프라 -> AWS 사용
  • 단계적 시스템 개선

빌드 표준화 -> 신뢰할 수 있는 환경에서 빌드하는게 아니라, 개발자 pc에서 테스트를 하는식으로 함…

  • 어느 개발자의 pc던, 어떤 서버던 빌드 가능하게 하고 빌드 결과물 동일하도록 작업합

고가용성 인프라 구성

  • MSA로 전환하고 쿠버네티스 도입
  • 쿠버네티스로 초 고가용성 인프라 구현

자동화된 코드 업데이트 체계 확보

  • 표준화와 자동화가 중요함
  • 수백여개에 대한 MSA를 동일하게 처리하기 위해서는..
  • 로깅과 모니터링같이 전사에 작용해야 하는 코드는 Common Library에 적용함.
  • 문제가 되는 코드 패턴이 보이면 pr을 올리는… 최신버전 라이브러리가 빠르게 적용되는걸 가능하게 함
  • JVM 50% 이상의 서비스가 java 17,21 버전 사용하는것에도 영향 -> 다음 업데이트도 빠르게 적용 가능

서비스 안정성 및 성능 고도화

  • 서비스 트래픽 가능하도록 함(카나리 방식)
  • 트래픽을 새로운 버전에 조금씩 흘려보냄
  • 1%단위로 트래픽을 조절해서 보내는게 가능합
  • 성능 최적화

대규모 데이터 조회 기술 확보

  • 쿼리 결과가 1분에서 1초 이내로, 3개월 데이터만 조회 가능하던게 최근 5년 내 데이터 조회 가능하도록함
  • 검색 기간 제어 없이 빠른 데이터 조회가 가능하도록 함
  • 긴 범위에 대한 데이터 조회 니즈가 상당히 많음 -> 개선으로 가맹점에서 며칠씩 걸리던게 몇분이면 끝남

보안 강화

  • 경계 / 내부망 / 업무망 / 런타임 / 컨테이너 보안
  • 서버 호스트들의 보안성을 강화

어플리케이션 개편

  • 어플리케이션 레이어 대규모 개편
  • 2004 년의 struts 1.2 를 boot3 + kotlin + react
  • 쿼리중심 -> 로직중심
  • OnPrem, -> 클라우드

현장결제 서비스의 분산 트랜잭션 관리학 개론

가맹점 -> 결제서버 -> (송금서버, 포인트서버, 프로모션 서버)

1
2
3
4
5
6
/**
* Task A = Transaction A + Transaction B
*/
fun taskA {

}
  • transaction 하나를 API call 이라고 했을때
    • API 호출 결과를 단정할 수 없는 경우 -> Read Timeout, 네트워크 유실, 정의되지 않은 에러
    • 성공 / 실패 / 알 수 없음까지 고려해야함. => 사실 4개 (알 수 없음 -> 성공/실패)
    • 트랜잭션이 하나씩 더 추가될수록… 트랜잭션간 불일치 상태에 따라 무수한 경우의 수가 발생함

문제 해결을 위해 필요한 것

  • 복잡한 상황을 나타낼 수 있는 “표현모델”
    • 상황을 트리모델로 표현 -> 여기에 상태 표현
  • 결제 정합성을 성공 또는 실패로 맞추는 방법 => 보정 방안(성공, 실패)을 결정하는 주체가 필요함 => 보정 정책도 각 노드가 맞춰야 함. 보정 정책을 가지고 있으면 leaf-> root를 맞출 수 있다.
  • 각각의 노드는 사가 오케스트레이터와 다르게 이중 보정 전략을 가지고 있음

언제까지 일관성이 유지되어야 할까 -> 분산 환경에서는 최종적인 일관성을 유지하는게 필연적

  • 복잡한 상황을 나타낼 수 있는 표현모델 : 트리구조 / 일관성은 어떻게 보장? : 각 노드가 / 일관성을 언제까지 보장해야할까 : ASAP이지만 최종적 일관성 추구

다양한 정책

  • 정책 수립 시 고려해야 할 점 -> 결제 수단별 심리적 가치 차이 / 보상 트랜잭션 난이도(입금이 출금보다 쉬움) / 결제 수단별 처리 난의도(내부 서비스가 외부보다 쉬움 등…)
  • 결제수단 승일/환불 순서
  • 각 결제수단의 필수 성공 여부
  • UNKNOWN 상태의 전파 바운더리 설정
  • 금원이동 서버와 결제 서비스의 분리
  • api 트랜잭션 내에서의 최소한의 보상 트랜잭션 실행

수천 개의 API/BATCH 서버를 하나의 설정 체계로 관리하기

하나의 설정 체게로 수천개의 실시간 API서버와 배치서버를 다룬다 -> 설정 하나로 문제를 해결한 과정

API 설정

  • 개발자의 요구사항
    • 모든 서버에 공통 환경변수를 넣고싶다
    • 특정 서버에서만 Java Heap 메모리를 올려달라
    • 특정 빌드에서만 스크립트를 실행하고 싶다

초기에는 복붙으로 해결했지만 한계가 있음…

실시간 API 설정의 두가지 축

  1. 오버레이 아키텍쳐 : 설정을 계층으로 나누고, 적용하는 방식.
    • 오버레이가 만능은 아님!! -> 값이 문자열인 경우나 긴 경우.. 중복이 발생함
  2. 템플릿 패턴
    • 중괄호를 활용해서 값을 끠워넣을 수 있도록 함!!
    • 특정 설정을 조건부로 줄 수도 있도록 함

모든 요구사항을 만족시킬 수 있는 구조

  • 핵심 : 계속 진화할 수 있는 구조
  • 복붙 -> 오버레이 아키텍쳐 -> 템플릿패턴 -> (그 이후는?)

배치 서버

  • 단순할수록 안전하다 -> 여러 배치 솔루션을 검토했지만 젠킨스를 선택
  • 완벽하지는 않지만 페이먼츠에서 필요한 배치 프로비저닝에 적절함.
  • 스크립트도 잡마다 조금씩 다르고, 자바 경로나 환경구조도 어렵고… -> 이걸 개선함
    • 워커에 몇 개의 자바 프로세스를 다뤄야 하냐~ 메모리 이슈로 배치 비정상 종료되는 것도

JOB DSL 플러그인 어댑터

  • 반복 설정 줄여줌
  • 젠킨스 오픈소스 플러그인 => 플러그인을 한 번 감싸서 개발자가 사용하기 편하게 개선함.
  • 어댑터 : 빌더패턴으로 만든 groovy 코드
  • 배치에 기대하는 설정을 하나의 파일에 설정 -> 반복, 조건문을 통해 설정 찝어낼 수 있도록도함
  • 실행 / 빌드 / 배포 / 젠킨스 파이프라인 실행 / shell 커맨드 실행 등 모든 커멘드를 빌더패턴으로 만들어줌

Dynamic Provisioning 어댑터

  • 메모리 부족 현상을 줄여줌
  • 어떤 노드에 몇 개의 자바 프로세스를 띄울지 애매함..
  • 전용 노드를 붙여주면 해결할 수는 있지만 비용 문제가 있기 때문에, 실행 요청에 따라 자동으로 늘어나고 줄어드는 배치 컴퓨팅 리소스를 만들어줌
  • 필요한 경우 배치를 동적으로 늘려주는 방식으로….

핵심 : 계속 진화할 수 있는 구조

  • 복붙 -> JOB-DSL -> Dynamic Provisioning -> (그 이후는?)

설정도 일종의 소프트웨어다

가맹점은 변함없이, 결제창 시스템 전면 재작성하기

  • 결제창 레거시 -> 절차지향적… 코드 끼워서 분기처리 해주는 방식. 도일한 코드가 재작성됨
  • 개발생산성이 너무 낮음. 웹로직과 JSP… 레거시에 대한 학습비용
  • 백엔드가 강결합되어 -> 직접 커넥션 받아오고 쿼리 로직.. 화면 로직이 다 같이 개발되어 있었음
  • 20년된 레거시 파라미터 연동 방식 & OpenAPI 파라미터 연동방식
    • 레거시 파라미터 : flat한 형태… 사실상 전역변수처럼 사용됨
    • 외부 인터페이스와 결제창의 Core로직이 강결합된 형태

개편 전략

  • 중복으로 존재하는 설정을 Core로직에 묶어줌
  • OpenAPI 파라미터 연동 방식 -> 변환 Adapter -> 20년된 레거시 파라미터 연동 방식 -> 토스페이먼츠 pg
    • 불필요한 변환 Adapter, 레거시 파라미터 변환 삭제
    • feature 단위로…

단일 JSP 에서 FE-BE가 강결합되어 있는 이슈가 있었음… -> FE와 BE가 서로의 관심사에 맞는 일을 하도록 수정해야함.

  • 패턴을 관찰해서 공통적으로 드러나는 3가지 스택을 발견
  • Entry : FE가 BE에 데이터를 받아옴
  • Prepare : 토스페이에 필요한 데이터를 내려줘
  • Confirm : 인증 마친 후 결제 성공으로 이동

프론트엔드는 비즈니스 로직을 모르도록 함. 백엔드는 결제인증에 필요한 통신 방식만 지시해주면 됨. 브라우저는 관심이 없음. 카드사 / FE / BE 가 분리됨

당신은 이미 팀을 최적화하고있다

위임, 분배, 책임감

  • 할 일을 주변에 위임해라 -> 단순히 할 일을 쪼개서 주는게 아니다…

신뢰의 3요소

  • 역량 신뢰(이 사람이 이 일을 잘 해낼 수 있을거란 믿음)
  • 소통 신뢰(이 사람이 어떤 상황에서도 진심으로, 솔직하게, 맥락있게 소통할거란 믿음)
  • 계약 신뢰(약속을 지킬 사람이란 믿음)

자율성을 존중하면서 퍼미션 얻기 -> 한 번 기다렸다가 얘기하기… 수용성이 올라감. 사전동의를 얻고 하면 방어력이 떨어짐. “잠시 한 마디 해도 될까요?”

매니징업

  • 변화, 한 사람, 버드뮤, 알아주기
  • 그 사람의 변화를 알아차려줄 수 있는 상위 리더가 필요함. 미세한 변화를 캐치하고 알려줄 사람..

중앙에서 Data Mesh로: Data Ownership 강화를 위한 변화

DAE(Data Analytics Engineer)

  • 적재하는걸 넘어 비즈니스 로직에 맞게 표준화하고 정리하는 역할

Data Mesh

  • 각 도메인이 데이터 소유권을 분산화해서 가지는 구조
  • 각 도메인이 자기 데이터에 대한 책임을 명확히 하고
  • 데이터를 제품으로 생각하고 -> 퀄리티있는 데이터를 책임지고 제공할 수 있도록 함
  • 도메인에 국한되지 않는 데이터를 제공함.
  • 도메인간 협업을 위해 거버넌스팀이 관리해줘야 함

토스가 중앙집중화된 데이터팀에서 데이터매쉬로 변경된 과정 각 제품에 대한 도메인이 명확해지면서, 각 도메인에 맞는 데이터가 필요….

23년까지의 DA는 하나의 조직에서 여러 업무를 함 -> 업무 범위가 넓다보니 고도화의 한계가 생김

  • 팀을 둘로 나눔
    • 전사 파이프라인 운영/데이터 툴 개발/ DW지원
    • 데이터 마트 구축 마트 팀
  • 조직의 데이터 리터러시를 올리고 신뢰할 수 있는 데이터를 제공하자
  • 더 많은 리소스를 분석 마트에 할애
  • 데이터 문서화에 대한 여러 시도 끝에도 원하는만큼의 데이터 자산화는 쉽지 않은 일..

표준화 마트

  • 데이터 사일로화 방지 => 데이터 가시성 향상 및 데이터 maturity 개선
  • 서비스의 주요 개념 정의(제품팀과의 협업) -> 기본 개념 마트 생성 -> 지표 기본 마트 생성 -> 문서화 -> 정합성 배치 생성

DW 파이프라인

  1. 입수단계
  2. 기본 마트
  3. 전사 집계
  4. 서브단계 & 백필

테이블센서

  • 디펜던시가 직접 이어져있지 않아도 태스크가 수행될 수 있도록 하는 거…

레거시 정산 개편기: 신규 시스템 투입 여정부터 대규모 배치 운영 노하우까지

오랫동안 정상 기능한 정산 기능을 개편한 이유

  1. 비즈니스 로직이 코드가 아니라 쿼리에 종속되어 있다
    • 거래 한 건을 위해 다양한 기능(환전, 수수료계산, 계약조회 등등) => 기존은 레거시 쿼리를 통해 수많은 도메인이 엮임… UNION ALL, 중첩된 DECODE, CASE WHEN 등을 사용함
    • 변경에 대한 영향비용도 거치고 개발 유지보수 비용도 커짐
    • -> 쿼리를 분석해서 한방 쿼리를 분해함(분할정복에 집중)
      • JOIN, UNION ALL 등을 분해함 -> 분리한 카테고리에서 또 분할하여 문제를 작은 단위로 분해함
      • 분할 정복 방식으로 -> 코드 분석하고… 새로운 요구사항이 들어왔을 대에도 요구사항 충족할 수 있도록 개편 가능해짐
  2. 데이터 모델링의 한계
    • 한덩어리의 결과이기 때문에 특정 거래에 대한 계산된 결과를 얻기 어려움 -> 데이터의 최소단위를 확보하게 되어 최소단위 데이터를 조합하여 제공할 수 있게 되어 요구사항에 유연하게 대응할 수 있게 됨
    • 개선 결과에 계산 당시의 스냅샷을 저장함
    • -> 정상 계산에 대한 재처리를 개선 할 수 있게 되는등, 분할하게 되어 세부적인 대응이 가능하게 됨
    • 신규에서 거래마다 처리 상태를 분리하여 이슈가 발생한 단계에 대해 대응이 용이해짐
    • 데이터 고해상도 문제 -> 파티셔닝과 인덱스 / 조회 전용 테이블을 명확하게 분리
    • => 기존 시스템에서는 집계해서 저장… 같은 조회 요청에 대해 신규 시스템은 실시간 데이터 문제발생(조회 느려지는 문제) 자주 사용하는 조회 조건 기반으로 조회 젼용 테이블을 분리하여 효율적인
  3. 배치 시스템 성능 문제
    • 거레 데이터가 실제 수행까지 실행 시간이 무한히 증가하는 이슈.. 신규 시스템에서는 Spring Batch 기반으로 개편했지만 모든걸 해결해주지는 않음 -> 많은 IO 발생, 단일 스레드 기반 처리로 인한 처리량의 한계…
    • 배치를 수행하고 거래를 실행할 때 마다 DB에서 조회하는 비효율적인 이슈 -> 설정 정보를 메모리에 캐시해두고, 설정 정보를 앱에서 조회하여 IO를 줄임
    • 처리해야하는 대상 객체를 한 건식 전달받음…
    • 대상 객체를 하나의 래퍼클래스에 묶어서 전달하는 구조로 바꿈 -> 아이템 프로세서가 객체를 하나만 전달받아도 여러 건을 전달하는 방식과 같아짐 -> 하나의 bulk로 조회하여 IO를 줄여줌
    • 정산 결과를 DB에 저장하는 과정 => IO 처리 횟수를 줄임
    • 병렬성 개선 -> 여러 외부 API를 호출해야 하는 경우가 생김… 다만 서로 다른 API에 존재하는 데이터를 조회해야 하다보니 여러 건을 순차처리하면 누적 시간이 수시간의 지연을 발생시킴(건건이 적더라도)
    • => API 호출을 병렬적으로 호출하여 시간을 단축시켜줌
    • 멀티 스레딩을 적용하여 배치를 병렬처리함 =-> 다만 Thread Safe 하지 않아서 중복정산, 누락 발생 할 수 있음…. => 동기화 적용 다만 동기화를 통해 데이터 읽는 시점에 대기가 발생하여 병렬처리 장점이 사라짐 -> 모듈러 연산을 이용해 각 스레드가 처리할 연산을 명확히 나눠줌 (ID%4=0인 데이터만, 스레드2는 ID%4=1인 데이터만… 스레드별로 처리할 데이터를 명확히 나눠줌)

신규시스템에 확신을 갖는 방법

  • 경우의 수? => 테스트 자동화 플랫폼을 적극 활용함. 시스템 내에서 원하는 테스트 방식을 표준화하여 제공. 수만개 이상의 테스트케이스 관리에 용이
  • 모든 데이터를 취함하여 테스트 자동화를 함. 테스트 계산 API를 별도로 만들어 계산 모듈을 검증함

신규 시스템 투입 방법

  • 고입 지점
    • 빠른 시간 안에 이슈 복구가 가능한가
    • 문제가 발생해도 충분히 대응할 수 있는 감당 가능한 규모인가
  • 배치 레이어에서의 카나리 투입진행
    • 레거시 시스템으로도 정산건을 수행하고, 신규에서도 정산건을 수행.
    • 정산건이 일치하면 신규를 투입 / 불일치하면 기존 건을 투입(추후 이슈 트래킹) => 적용해볼만 할듯

정산 배치를 어떻게 관리할까요 -> 다량의 정산시스템 모니터링을 어떻게 발전시킬까

  • 많은 배치가 흩어져 정의됨 => 관리가 불편하고 장애에 취약함. 서버에 이슈 발생 시 즉각 정상화가 어려움. 배치 실행 정보가 crontab으로 정의되어있기 때문에 관리되지 못하는 상황
  • 하드웨어 장애에 취약한 환경에 벗어나자 => AWS K8S CronJob
    • AWS 데이터센터간 RTT 이슈로 배치 성능 저하
    • K8S CronJob 한계로 정확히 한 번 실행을 보정하지 않아 배치가 중복 실행되는 이슈가 발생
    • 배치가 안전하게 실행될 수 있도록 젠킨스 선택
  • 젠킨스 : 정산은 미션크리티컬한 배치가 많아… “안정성” 부분에서 젠킨스가 뛰어남
    • 다른 배치관리 시스템과 비교했을 때 Stateful한 인프라
    • 파이프라인 선언을 통해 배치 워크플로우 정의 가능
    • Jenkins Plugin 생태계 -> 원하는 기능 충분히 구현 가능

Dynamic Provisioning

  • 배치 실행 장비가 고정적으로 할당하면 같은 시점에 실행되는 배치를 유동적으로 감당하기 어렵고 / 그렇다고 서버 자원을 과도하게 할당하면 서버 자원 낭비가 됨
  • 다이나믹 프로그래밍 => 실행 노드를 동적으로 관리 / 필요할 경우 서버 노드를 동적으로 할당받고, 배치 없으면 회수함
  • Jenkins 파이프라인 노드 잘못 설정해서 배치가 터지거나, 환경 변수를 일괄로 바꿔야 하는데 Job이 수십개일때….. 파라미터를 하나하나 다 추가해야하거나…
  • Job 내에 여러 선언을 일괄 변경하기 번거로움 -> 쏟아지는 비즈니스 요구사항 개발에 허들
  • 코드화되어있는 프로젝트로 커밋하면 JOB..

배치 모니터링 도구 강화

  • Thread Dump / Async Profiler
  • Prometheus / Pinpoint

고객은 절대 기다려주지 않는다: 빠른 데이터 서빙으로 고객 만족도를 수직 상승 시키는 법

MSA 전환 이후에도 DB는 분리하지 못해… 검색에 대한 니즈가 커지자 ES에 색인해서 해결 -> CQRS 도입 (RDB에서 집계 힘든 부분을 아파치 드루이드를 도입)

Apache Druid

  • 거래 내역을 시계열. OLAP성 쿼리에 유리
  • 개발자가 SQL로 쉽게 개발
  • 자동 index로 어느정도 보장되는 성능
  • 아키텍쳐

읽기/쓰기가 분리되면서 -> 실시간 매출 확인이 가능해짐…

데이터 조회 비즈니스 요구사항

  • 최근 n개월 데이터 조회를 하고싶음
  • 특정 키가 아닌 범위 조회를 하고싶음
  • 범위 조건 없이 특정 컬럼에 대한 like 검색
  • 정렬, 페이징, 실시간 집계 연산 들어가면서 응답속도…
  • 테이블 n개에 대한 조인을 원함

=> 조회 성능 저하.. -> 빠른 응답속도 충족이 어려워 효율적인 성능 최적화 전략이 필요함

데이터 결합을 위한 데이터 가공 방식

  • CDC
  • 메세지 발행 -> 메세지만 잘 발행되면 druid에 전잘하여 성능 보장 가능

아키텍쳐 특징 이용해서 비용, 성능 한번에

  • 비싼 EBS 안쓰고 S3를 쓰는것만으로 비용과 고가용성 한번에… 데이터 유실도 없음. 일부는 스팟인스턴스같은 휘발성 인스턴스 이용 가능
  • 다만
    • 20개가 넘는 원장을 기반으로 과거 기간 역정규화 테이블 만들어야 함
    • 멱등성 처리 어려움
    • 데이터 파편화
    • 아키텍쳐 복잡성

검색 기능

  • 검색은 elastic으로 해서 키/파티션을 획득하고 -> 해당 건을 druid에서 조인하는 방식으로 해결

druid에서의 집계 최적화 방식

  • roll up : 행 압축으로 조회해야 하는 데이터 스캔이 줄어들어 조회 성능 향상됨 -> 시간 훨씬 줄어들었다~ 최적화 되었다~

더 큰 범위의 통합 원장의 필요성 -> 신규 플랫폼(starrocks)

  • 멱득성 문제 해결
  • 분석 시스템과의 통일성
  • 간결한 아키텍쳐

대용량 테이블 조인 성능 향상을 위한 starrocks 에서의 데이터 저장 방식 -> Colocation Group

더 빠르고, 더 정교한 이상금융거래탐지 시스템

  • 거래란? : 뱅킹 서비스를 사용하는 유저의 모든 행동(입출금, 카드결제, 대출 등…)
  • 이상거래란? : 정상적인 사용 패턴과 다르게 의심스러운 패턴으로 이루어진 거래 -> 소액 송금만 하다가 갑자기 큰 금액을 이체하거나, 사용하지 않던 곳에서 카드를 사용하거나….
    • 이상거래 사례 : 대포통장 개설, 보이스피싱을 통한 출금, 기기 도용 로그인, 불법 대출 수수료 편취, 도용/복제 카드 사용 등….

이상 금융 거라 탐지 시스템(FDS, Fraud Detection System)

  • 어떻게 탐지? : 과거에 발생했던 사기 패턴을 분석하여 비슷한 패턴이 감지되었을 시 감지 -> ex: 특정 나이대 이상에서 특정 금액 이상 거래했던 것…
    • 수십개의 패턴 중 일치하는 패턴(룰)이 있느가를 형상화 하여 -> 차단
    • 여러 데이터를 조합하여 패턴을 감지

기존 FDS 룰 엔진 단점 -> 데이터 추가 시 불편함

  • 원인 : 도메인과 FDS간 데이터 성질간 차이점 존재 -> 일반 서버(사용자) 입장에서는 “언제” 결제했는지가 중요했는지 반해, FDS에서는 그 결제가 “어떤” 방식으로 결제했는지가 중요(근데 이건 고객은 안궁금해함) => 이런식으로 FDS와 일반 사용자가 중요하게 생각하는 데이터가 다름
  • 새로운 요구사항 : 실시간으로 FDS응답을 받아보고 싶음 -> 거래 시도 시 FDS 검증 요청 -> 이렇게 되면 순환참조 발생할 수 있음( 도메인 서버에 또 FDS에 필요한 데이터 요청해야 하니까….)
  • 기존 룰 엔진 활용 시 타임아웃 요건 미부합
  • 룰 명세 -> 새로 룰을 추가할 때 마다 일일히 코드로 구현하는게 비효율적…. and or과 같은 방식
    • 룰이 기존에는 줄글 형태로 …. -> 조건에 눈에 잘 안보임.. ㅠ
    • JSON 을 이용한 룰 명세 => Jankson 이용해서 룰을 설계. Admin으로도 룰을 생성할 수 있도록도 함

룰 엔진 개선한 -> 동기화 방식을 통한 데이터 수집

  • 카프카에서 거래 완료 이벤트가 발생하면 FDS에서 필요한 데이터 가공하는 방식으로 진행
  • 동기화 시 데이터가 많은 인원에 대해 마킹
  • 데이터가 적은 경우 개별 쿼리 호출 / 데이터가 많은 경우 쿼리 분할, 병렬 호출(한번에 많이 요청하면 타임아웃 발생하니까)
    • 거래가 많은 유저는 병렬쿼리 보내는 방식을 적용함
    • 데이터 가용성/성능이 받쳐주면 가능함
  • 신규 룰 엔진 장점 : 데이터 추가 시 용이
  • DB 용량이 커지는 문제 -> 오래된 데이터는 clean up 하도록 설정
    • 정해진 기간의 데이터만 가지고 있음
    • 오래된 데이터는 정말 필요 없을까? => 정교해지는 이상금융 거래들을 잡아낼 수 있을까? => 아님.. 오래된 데이터도 필요… 이상거래와 무관하지 않음.
    • Feature Store 서버

운영 이슈

  • Boolean 처리 -> 송금 정보는 정상적으로 가져왔는데 유저 정보는 제대로 가져오지 않는다면…(일부 정보만 가져오는데 성공했다면)
    • 전체 룰을 수행하지 않거나 / 가져온 정보에 대해서만 판별할 수 있는건 다 판별한다
    • 여기서 발생한 문제 : 데이터가 없음 false로 처리 했는데 -> 후자로 해결하려면 룰을 작동시킬 때 무조건 False가 되게 함
    • 해결 방법 : Unknown 상태를 추가 -> Unknown이 있으면 해당 상태값을 포함한 룰은 무시함.
  • 0빼기 이슈 -> 테스트 환경과 라이브에 휴먼에러로 임계치를 잘못 했을때…
    • 휴먼 에러 방지를 위해 과탐지 자동 모니터링을 적용 : 탐지량이 전체의 xx%를 초과하면 해당 룰을 off함

FDS가 나아갈 길

  • ML연동….

Spring 서버 애플리케이션 구동시간 줄이기

문제

  • 긴 시간 실행되는 배치는 이중화된 DC 환경에서 전환시 문제
  • 배포시간 단축으로 생산성 증가

Async-profiler

  • 자바 어플리케이션에서 성능 분석하는 샘플링 툴. 성능 이슈 없이 샘플링 가능
  • wall-clock profiling : 스레드 상태 상관 없이 모든 애플리케이션 샘플링 가능. 스타트업 타임 분석 가능.
  • Agent 방식 / 코드 방식 두 방식 중 하나로 프로파일링 가능
  • FlameGraph
    • 샘플을 수집할 때 어떤 function이 호출되었는지, 몇 번 호출되었는지, 얼마나 수행시간이 걸렸는지
  • Filtering options : 너무 많은 스레드를 보지 않기 위해, 내가 보고싶은 스레드만 살펴볼 수 있음

지연 구간을 확인할 수 있음 -> 스레드 옵션을 제거하고 확인하는게 분석에 도움됨

wall-clock 분석

  • refresh global cache 구간에서 많은 시간이 사용되는걸 해당 wall-clock에서 확인함 -> batch에서 불필요한 의존성 제거해서 실행시간 단축

Application Startup tracking by SpringApplication

  • 스프링 프레임워크에서도 자체적으로 분석 가능
  • JFR 확인 -> 인텔리제이에서 Timeline에서 오래걸린 부분에서 확인 가능
  • wall-clock profiling을 intellij에서도 확인 가능함
  • 로컬 환경에서도 병목 구간 확인 가능함

개선한걸 실제 프로덕션 반영했을때는 개선 효과가 많이 미미했음

  • 로컬에서는 개선했으니 K8S에서 다시 분석하기 -> 띄울 때 에이전트 같이 띄워서 Wall-Clock 확인함
  • 이슈 보고
    • 문제1 : 3.2.0 업그레이드 후 Executable JAR startup 시간이 매우 느려짐
    • 문제2 : static path 하위에 정적리소스 추가 후 startup 시간이 느려짐
  • 해당 이슈와 현 환경 비고하여 Executable Jar를 다른 Jar로 변경
    • 로컬 환경에서 테스트 했을 때 구동 시간이 훨신 줄어들었음
    • 배포 환경에 적용했을때에도 개선 폭이 아주 컸던 걸 확인 가능했음
    • 수정 후 확인한 월클락 결과에서도 유의미한 결과
    • jar 풀어넣었을 때 docker size 커지지 않는지 -> 미미한 차이라서 무리없이 적용 가능한 최적화였다
  • Jar 풀어서 구동시간 줄였으니… Layerd Jar 적용?

Layerd Jar

  • 도커 이미지 빌드 효율적으로 도와.
  • Jar 파일을 여러 레이어로 나눠 구성 -> 코드가 살짝 바뀌었을 때 전체 이미지 빌드하지 않아도 되어서 빌드 성능이 좋아짐
  • JAR를 레이어드로 분리하고 나니까 네트워크 bandwidth가 대폭 줄어들음.
  • 어플리케이션 구동시간 뿐만 아니라 배포 시간도 대폭줄일 수 있어서 유의미한 결과

CDS

  • (Class Data Sharing) : JDK12부터 지원. 구동시간, 메모리 풋프린트 줄이기 위한 기능.
  • 클래스 정보 저장한 jsa 파일 설정을 해 둬야 함 -> 이후 클래스 로딩 정보 확인해서 애플리캐이션 로딩 될 때… 캐시에서 이미 저장된 클래스를 끌오는 방식으로 개선이 됨.
  • metaspace 비교해봤을 때 클래스 영역 메모리 사용량 줄어든 것 확인함.
This post is licensed under CC BY 4.0 by the author.