백엔드 엔지니어 포트폴리오

9개 검색 소스를 격리하고, Kafka 9종의 복구를 좁히며, 오류 데이터 약 200만 건을 바로잡았습니다

Java·Kotlin·Spring으로 중단 뒤 다시 시작할 수 있고, 데이터가 맞는지 끝까지 검증할 수 있는 백엔드를 개발합니다. 채용 피드·이벤트 복구·포인트 원장·JPA 보정 사례를 문제–설계–검증 결과 순서로 정리했습니다.

JOBKOREA·Albamon 개인화 피드의 9개 검색 소스를 독립 변경 단위로 분리하고, 캐시 비가용에도 후보 응답 경로를 유지했습니다

9개 검색 소스의 정책·실패 처리와 OpenSearch→Redis 경계를 분리했습니다. Hit·Empty·Unavailable 3개 Snapshot 상태에 따라 저장 결과 반환·새 Snapshot 저장·저장 생략 후 재계산 경로를 각각 구현했습니다.

  1. 01 · Problem

    검색 소스마다 정책과 실패 조건이 달랐습니다

    9개 검색 소스별 필터·정렬·벡터 규칙과 타임아웃·실패 처리를 독립적으로 바꿀 수 있어야 했습니다.

  2. 02 · Decision

    후보 처리 단계를 명시적인 계약으로 나눴습니다

    OpenSearch 후보 조회 → 중복 제거 → 점수화 → 재정렬 → Redis 캐시 저장의 5단계로 입력과 출력을 구분하고, 캐시 우회 경로를 분리했습니다.

  3. 03 · Boundary

    3개 Snapshot 상태로 캐시 경계를 드러냈습니다

    Hit은 저장된 결과를 반환하고, Empty는 후보 흐름을 실행해 새 Snapshot을 저장합니다. Unavailable은 캐시 비가용 상태로 보고 저장을 건너뛴 채 같은 후보 랭킹 흐름을 캐시 없이 실행합니다.

  4. 04 · Verification

    검색 구현과 도메인 경계를 함께 지켰습니다

    OpenSearch 쿼리를 어댑터 모듈로 분리하고, ArchUnit으로 모듈 간 순환 의존을 검출했습니다.

도식의 실선은 기본 조회·저장 경로, 점선은 캐시를 사용할 수 없을 때 저장만 건너뛰는 우회 경로를 뜻합니다.

개인화 피드 처리 흐름 보기
9개 검색 소스에서 Snapshot 응답까지 Snapshot 상태가 달라도 후보 조회·점수화·재정렬의 경계는 유지됩니다.
Feed API가 후보를 모아 랭킹하고 Snapshot으로 저장하는 처리 흐름

Kafka 9종에서 미완료 업무만 선택 복구했습니다

담당
Kafka 9종·상태 6개·복구 경로 8개·15분 정체 알림 설계
구현
Kafka 발행·처리 실패를 6개 상태로 구분
저장
PostgreSQL에 처리 상태 기록 · JSONB에 복구용 payload 저장
알림
15분 정체된 IN_PROGRESS 이벤트를 유형별 Slack 알림

같은 실패 상태로는 복구 지점을 정할 수 없었습니다

발행 전에 멈췄는지, 발행 후 업무 처리에서 멈췄는지에 따라 확인할 데이터와 재실행 범위가 달랐지만 하나의 실패 상태만 남았습니다.

실패 단계와 업무를 분리해 복구 범위를 좁혔습니다

이벤트 9종을 6개 상태로 기록하고 payload를 보존했습니다. 8개 복구 경로에서는 차감·회수·만료 알림 중 미완료 업무만 재실행했습니다.

토스랩 JANDI에서 실제 영속성 컨텍스트로 JPA 저장 오류를 재현하고 약 200만 건을 보정했습니다

Mock 테스트의 사각지대와 실제 영속성 컨텍스트 재현을 구분했습니다. 데이터 보정과 실제 저장 회귀 검증도 각각 독립된 근거로 남겼습니다.

  • Mock blind spot

    Mock 테스트는 실제 저장 경계를 지나지 않았습니다

    Mock 객체만 확인한 테스트에서는 실제 영속성 컨텍스트의 변경 감지와 저장 시점에 나타나는 오류가 드러나지 않았습니다.

  • Persistence reproduction

    실제 영속성 컨텍스트에서 저장 과정을 재현했습니다

    토스랩 JANDI의 실제 저장 흐름을 실행해 JPA 변경 감지 오류가 발생하는 조건을 확인했습니다.

  • Correction boundary

    영향 데이터 식별과 보정 실행을 분리했습니다

    잘못 저장된 값을 먼저 식별하고, 검토 가능한 보정 스크립트로 대상만 바로잡았습니다.

  • Corrected data

    오류 데이터 약 200만 건을 바로잡았습니다

    식별한 오류 데이터 약 200만 건을 보정해 이후 업무가 저장 값을 다시 사용할 수 있게 했습니다.

  • Storage regression

    실제 저장 동작을 회귀 검증에 포함했습니다

    실제 저장을 확인하는 회귀 테스트를 보강해 같은 변경 감지 오류가 다시 지나가지 않도록 검증했습니다.

JANDI 저장 오류 근거 보기
Mock 사각지대 · 영속성 재현 · 데이터 보정 · 저장 회귀 검증 확인된 네 근거의 관계를 나타내며 정확한 수행 순서를 뜻하지 않습니다.
토스랩 JANDI의 Mock 테스트 사각지대, 실제 영속성 컨텍스트 재현, 데이터 보정, 실제 저장 회귀 검증의 관계

10개 테이블 배치를 재개하고, 9개 API·13개 시나리오로 포인트 정합성을 검증했습니다

10개 테이블 배치 재시작, 9개 API·13개 시나리오의 동시성 검증, Kafka 8개 선택 복구 경로를 서로 독립된 운영 단위로 나눴습니다.

  1. Lane A · Batch restart

    배치별 진행 위치로 중단 지점부터 재개했습니다

    포인트 소멸 대상을 페이지 단위로 처리하고, 만료 알림 대상은 별도로 수집했습니다.

    Archive boundary
    10개 아카이브 테이블을 참조 순서대로 옮겼습니다.
    Restart checkpoint
    10개 테이블의 진행 위치를 배치별로 기록해 중단 지점부터 이어 갔습니다.
  2. Lane B · Invariant check

    동시 요청의 결과 불변식을 확인했습니다

    9개 API의 13개 동시성 시나리오에서 3·5·10개 요청을 같은 락 키 또는 같은 예산 조건으로 실행하고, 종료 뒤 잔액·원장·예산·후속 이벤트를 함께 비교했습니다.

  3. Lane C · Selective recovery

    Kafka 상태와 payload로 선택 복구 범위를 정했습니다

    Kafka 9개 이벤트를 6개 상태로 구분하고 PostgreSQL 상태와 JSONB payload를 보존했습니다. 8개 복구 경로에서는 완료되지 않은 업무 가운데 포인트 차감·회수·만료 알림만 골라 재실행했습니다.

    15분 넘긴 정체를 유형별로 묶었습니다

    15분 넘게 완료되지 않은 이벤트는 유형별로 묶어 Slack 알림으로 확인할 수 있게 했습니다.

세 레인은 앞 단계에서 다음 단계로 흐르는 파이프라인이 아니라, 각각 다시 시작하고 검증하고 복구하는 독립 경계입니다.

Point의 독립된 복구·검증 경계 보기
배치 재시작 · 동시성 검증 · Kafka 선택 복구 서로 연결된 실행 순서가 아니라 각각 독립적으로 확인하는 세 운영 경계입니다.
웍스피어 JOBKOREA와 Albamon Point의 배치 재시작, 동시성 결과 검증, Kafka 선택 복구를 독립된 레인으로 나타낸 도식

발표·글·코드로 남긴 공개 기록

기술 글 156편과 YouthCon 발표, Spring 오픈소스 개선 제안, 개인 프로젝트를 한곳에 모았습니다.

YouthCon 2025 발표

‘우리팀을 위한 Spring Test 만들기’ 발표에서 반복되는 테스트 준비 코드를 공통화하고, 테스트 유형에 맞는 설정과 작성 기준을 정리한 과정을 다뤘습니다.

기술 글 156편 · 2026.07 기준

Java·JPA·테스트 관련 문제와 SQL 튜닝 경험을 글로 기록했습니다. 글또 10기에서는 준비위원과 연사로 참여했습니다.

Spring 오픈소스 · 개선 제안 3건 반영

Spring Kafka, Spring Boot, Spring Data JPA에 불변 컬렉션 처리, 하드코딩 제거, 자원 정리에 관한 개선을 제안했고 모두 반영됐습니다.