대상 직무: 시니어 Java 백엔드 (재사용 가능한 공통 자료) 가장 가까운 면접: CJ 올리브영 커머스플랫폼유닛 Back-End (경력) — 2026-04-21 근거:
resume/2603_김병태_이력서_v4.md,task/nsc-slot/**,task/ai-service-team/**,interview/cj-oliveyoung-wellness-backend.md
NHN에서 4년간 Java 백엔드 개발을 해온 김병태입니다. 소셜 카지노 슬롯팀에서는 Spring Boot 멀티모듈 MSA 환경에서 신규 슬롯 게임 5종 이상과 RTP 캐시 시스템(RCC)을 만들었고, 다중 서버 인메모리 캐시 정합성 문제를 RabbitMQ Fanout과 StampedLock으로 직접 풀었습니다. 이후 AI 서비스팀으로 이동해 Confluence 문서를 OpenSearch에 벡터 색인하는 Spring Batch 파이프라인을 11개 Step과 AsyncItemProcessor로 처음부터 설계·구현했고, 최근에는 12일 동안 혼자 AI 웹툰 제작 도구 MVP를 Next.js와 Gemini, Claude Code 하네스 기반 에이전트 팀으로 199 plan·760 커밋까지 밀어냈습니다. 설계부터 운영, AI 도구 도입·확산까지 전 과정을 주도한 경험을 살려 기여하고 싶습니다.
NHN에서 4년간 Java 백엔드 개발을 해온 김병태입니다. 슬롯팀에서는 Spring Boot 멀티모듈 MSA 환경에서 신규 슬롯 게임 5종 이상을 개발했고, 성능·동시성 이슈를 직접 풀었습니다. 다중 서버 인메모리 캐시 정합성 문제에서는 Hibernate
PostCommitUpdateEventListener로 커밋 후에만 RabbitMQ Fanout Exchange로 변경 ID를 발행하고, 갱신 중 조회 충돌은 StampedLock + 2.5초 tryReadLock 타임아웃으로 해결했습니다. Kafka 쪽에서는 즉시 응답이 필요한 흐름과 후처리 흐름을 분리하고,@TransactionalEventListener(AFTER_COMMIT)+Propagation.REQUIRES_NEW기반 Transactional Outbox 패턴으로 메시지 유실을 차단했습니다.AI 서비스팀으로 옮긴 뒤에는 Confluence → OpenSearch RAG 파이프라인을 11개 Step으로 분리해 처음부터 설계·구현했습니다. I/O 바운드인 임베딩 호출은
AsyncItemProcessor로 병렬화하고, 메타데이터 차이는 전략 패턴(EmbeddingMetadataProvider)으로 흡수해 OCP를 지켰습니다. 최근에는 12일 동안 혼자 Next.js 16 · React 19 · Prisma 7 · Gemini 기반 AI 웹툰 제작 도구 MVP를 만들었는데, Claude Code 하네스 위에서 planner·critic·executor·docs-verifier 4인 에이전트 팀을 조율해 199 plan / 760 커밋을 소화했습니다. Gemini Pro 우선 + 429 fallback 전략, 전역 Rate Limit Tracking, Project 단위 Context Cache, Promise.allSettled 기반 60컷 부분 성공 생성, 글콘티 Grounding 재주입으로 환각을 구조적으로 차단한 게 핵심이었습니다.강점은 두 가지입니다. 하나는 기능 구현에 그치지 않고 추상 템플릿·전략 패턴·테스트 인프라까지 구조를 다져 팀 속도를 높이는 것이고, 다른 하나는 AI 도구를 단순히 쓰는 게 아니라 파이프라인으로 설계·운영하는 관점입니다. 대규모 커머스 트래픽 환경에서 그동안의 캐시·이벤트·대용량 배치 경험을 빠르게 적용하고 싶습니다.
| 기간 | 소속 | 역할 | 대표 기술 결정 |
|---|---|---|---|
| 2026.04 (12일) | NHN AI 서비스팀 | 단독 풀스택 MVP 리드 | Next.js 16 + Gemini + Claude Code 하네스 기반 4인 에이전트 팀(planner/critic/executor/docs-verifier)으로 199 plan/760 커밋. Gemini Pro 기본 + 429 fallback, 전역 Rate Limit Tracking, Project Context Cache, Grounding 재주입, Container/Presenter + 파일 소유권 매트릭스 |
| 2026.01 ~ 2026.03 | NHN AI 서비스팀 | RAG 배치 파이프라인 설계·구현 | Spring Batch 11 Step + AsyncItemProcessor 병렬 임베딩, @JobScope 인메모리 홀더, ADF → Markdown 변환, 전략 패턴 기반 EmbeddingMetadataProvider, 삭제 동기화 (status=DELETED,TRASHED 재사용) |
| 2025.12 ~ | NHN AI 서비스팀 | 백엔드 | OCR 서버 Graceful Shutdown 503 수정, 임베딩 메타데이터 blocklist → allowlist 전환 |
| 2025.07 ~ 2025.10 | NHN NSC 슬롯팀 | RCC·엔진 추상화 리드 | RTP Cache Control 6종 대응, SlotTemplate/BaseSlotService/ExtraConfig 분리, StampedLock 도입으로 refresh 중 NPE 제거, Alias 테이블 IN절 일괄 조회 |
| 2025.02 ~ 2025.08 | NHN NSC 슬롯팀 | 신규 슬롯 5종 | Slot 36/38/41/44/47, Cursor Rules 20+로 AI 에이전트 단독 구현 3종, 스핀 최적화(AliasMethod O(1), SecureRandom → ThreadLocalRandom), 시뮬레이터 OOM 해결(Welford's Online Algorithm) |
| 2024.06 ~ 2024.12 | NHN NSC 슬롯팀 | 합류 첫 해 | Slot 21/33 신규 게임, Admin Alpha↔Real 비교/복사, BuyFeature 티켓·시나리오 스핀 플랫폼 기능, 다중 서버 캐시 정합성 (RabbitMQ Fanout + StampedLock), Kafka Transactional Outbox |
PostCommitUpdateEventListener로 커밋 후에만 RabbitMQ Fanout Exchange로 변경 ID 발행 → 각 인스턴스가 자신의 큐에서 수신 후 해당 항목만 선택 갱신. 갱신/조회 경합은 StampedLock writeLock + tryReadLock(2500ms) 타임아웃으로 흡수. StaticDataManager 인터페이스로 init/refresh/clear 책임 분리 → 새 캐시 타입 추가해도 기존 코드 무변경.resume/2603_김병태_이력서_v4.md 문항 1, task/nsc-slot/slot-engine-abstraction.md "StaticDataLoader 개선".@TransactionalEventListener(AFTER_COMMIT)으로 커밋 후 발행 보장. 전송 실패 시 Propagation.REQUIRES_NEW 별도 트랜잭션으로 실패 메시지를 DB에 저장하고 스케줄러가 재전송. traceId 동반 저장으로 실패 원인 추적.resume/2603_김병태_이력서_v4.md 문항 1.AsyncItemProcessor + AsyncItemWriter로 병렬화. 청크 내 문서를 스레드풀에서 동시 처리.ItemStream 구현으로 커서 위치를 ExecutionContext에 저장 → 중간 실패 후에도 마지막 처리 지점에서 재시작.@JobScope 홀더(ConfluenceJobDataHolder) 도입으로 JobExecutionContext 직렬화 부하 회피 (경량 커서 상태 vs 도메인 데이터 분리 판단).task/ai-service-team/rag-vector-search-batch.md.AliasMethod로 가중치 샘플링을 O(n)→O(1)로, SecureRandom → ThreadLocalRandom 전환.List<Long> winmoneyList 누적 구조를 Welford's Online Algorithm 기반 1-pass 통계로 교체해 메모리 상수화.task/nsc-slot/slot-spin-performance.md, task/nsc-slot/slot-simulator-oom.md, task/ai-service-team/webtoon-maker-ai-pipeline.md (ADR-059, ADR-069)./planning → /plan-and-build → /build-with-teams → /integrate-ux로 vibe 코딩을 spec 기반 코딩으로 단계적 전환.task/nsc-slot/ai-tool-adoption.md(목차), task/ai-service-team/webtoon-maker-ai-pipeline.md "하네스 진화".AbstractSlotTest로 게임 타입별 초기화 자동화, 총 447개 테스트 파일에서 핵심 비즈니스 로직 / AOP 검증 / Kafka 이벤트 발행 / Redis 통합 테스트까지 커버.@BatchComponentTest 커스텀 애노테이션으로 외부 API만 모킹하고 Spring 컨텍스트에서 실제 빈을 엮어 테스트해 @Qualifier 충돌·@StepScope 빈 중복 같은 실수를 빌드 타임에 차단.resume/2603_김병태_이력서_v4.md 문항 1, task/ai-service-team/rag-vector-search-batch.md "테스트 전략".data class·null safety·coroutine 관련 패턴을 집중 학습 중입니다. 기존 Java 설계 원칙(불변성, 단일 책임, 도메인 모델링)이 동일하게 적용되기 때문에, 초기 1~2주 안에 실무 생산성 수준으로 붙일 수 있다고 봅니다.SlotTemplate/BaseSlotService는 신규 슬롯 5종을 만든 후에야 정돈).기본 원칙: "싼 단가가 아니라 총비용", "추상화는 반복을 충분히 경험한 뒤", "API/모델 경계와 논리 경계를 분리해서 본다".
Rate Limit Tracking(429 맞은 모델을 일정 시간 skip 대상으로 마킹)으로 다른 요청이 같은 모델을 또 두드리지 않게.task/ai-service-team/webtoon-maker-ai-pipeline.md.Promise.allSettled 구조 전환Promise.allSettled로 병렬 실행.
AbortController로 전체 취소, 실패 컷만 같은 엔드포인트 재호출.Partial<XxxFields>) → Prisma의 DbNull·connect/create/disconnect semantic을 외부 타입으로 흉내 내며 추상화 누수.Prisma.XxxCreate/UpdateInput, 경계는 actions/mappers/의 작은 변환 함수. "단일 소스" 도그마를 포기하고 레이어 본질에 맞는 타입 소스를 각자 사용.JobExecutionContext vs @JobScope 홀더JobExecutionContext에 저장. 청크 커밋마다 BATCH_JOB_EXECUTION_CONTEXT 테이블에 직렬화되는 부하.JobExecutionContext는 재시작용 경량 커서 상태 전용으로 제한하고, 도메인 데이터는 @JobScope 빈 ConfluenceJobDataHolder로 분리. allowStartIfComplete(true) 세팅으로 재시작 시에도 상태 로더가 반드시 재실행되게 해서 NPE 방지.EmbeddingMetadataProvider 인터페이스 기반 allowlist 방식으로 뒤집어 명시적으로 허용한 필드만 포함. OCP 준수(새 스페이스 포맷은 새 Provider 추가).AbstractPlayService 단일 템플릿으로 통합하고, 타입별 가변 동작은 SpinOperationHandler 인터페이스로 위임. 여러 타입에 흩어진 계산 로직은 Decorator 패턴으로 도메인 레이어에 응집, static 의존을 DI로 전환해 테스트 가능성 확보.StepConti.tsx) 동시 수정으로 conflict가 빈발.docs/collaboration.md에 파일 소유권 매트릭스 명시(디자이너: globals.css, components/common/layout/, components/**/components/ / 백엔드: actions/, lib/, components/**/hooks/, components/**/containers/)./integrate-ux 스킬화 — 디자이너의 vibe 코드를 로컬 state → Server Action, 인라인 카드 → 공통 컴포넌트, 인라인 색상 → semantic 토큰으로 정해진 변환 룰에 따라 흡수.SlotTemplate/BaseSlotService) 도입 시에도 "반복을 충분히 경험한 뒤 공통점을 뽑았다"는 근거를 문서화. 후임이 맥락 없이도 의도 파악 가능.CompositeItemProcessor로 ChangeFilter → Enrichment → BodyConvert → Embedding 단계를 체이닝, I/O 바운드 임베딩은 AsyncItemProcessor + AsyncItemWriter로 병렬화. @JobScope 홀더로 Step 간 경량 공유, JobExecutionContext는 재시작 커서로만 사용. 전략 패턴(ConfluenceDocumentMetadataProvider)으로 스페이스별 메타데이터 포맷 차이 흡수. 삭제 동기화는 status=DELETED,TRASHED 재사용으로 별도 ID 집합 비교 없이 처리.version 비교)로 불필요한 임베딩 API 호출 제거.AsyncItemProcessor, @JobScope 프록시, OpenSearch bulk, 전략 패턴, @StepScope 빈 충돌 해결(@Qualifier 정리).@google/genai로 단일 코드베이스 + 타입 안전성 극대화. Claude Code 하네스 위에 planner / critic / executor / docs-verifier 4인 에이전트 팀을 조립하고, /planning → /plan-and-build → /build-with-teams → /integrate-ux로 vibe 코딩 → spec 기반 코딩 전환.Promise.allSettled 기반 60컷 병렬 생성 (AbortController 전체 취소, 실패 컷별 재호출) (ADR-073).types/, templates/, blocks/) (ADR-132).CharacterSheet.isDefault + 기본 시트 이미지 자동 prepend + mode 분기(default/outfit) (ADR-133/134).PostCommitUpdateEventListener → RabbitMQ Fanout Exchange 발행 → 각 인스턴스가 자신의 큐에서 수신해 해당 슬롯만 선택 갱신. 갱신/조회 경합은 StampedLock writeLock + tryReadLock(2500ms). StaticDataManager 인터페이스로 init/refresh/clear 책임 분리.IN절 일괄 조회로 init/refresh 쿼리 수 감소.@TransactionalEventListener(AFTER_COMMIT)으로 커밋 이후 발행 보장, 전송 실패 시 Propagation.REQUIRES_NEW 별도 트랜잭션으로 실패 메시지 + traceId를 DB에 저장 → 스케줄러 재전송.AliasMethod로 가중치 샘플링 O(n)→O(1), SecureRandom → ThreadLocalRandom. 시뮬레이터는 List<Long> winmoneyList 누적 → Welford's Online Algorithm 기반 1-pass 평균/분산으로 상수 메모리.SecureRandom 경합 병목 이해, 수치 안정성(Welford).SlotTemplate, BaseSlotService, ExtraConfig 분리)와 함께 진행해 확장 비용 최소화.AFTER_COMMIT + Outbox로 정리한 것과 동일합니다.공통 프레이밍: 맥락(왜 문제였는지) → 트레이드오프 → 결정 → 측정 결과 → 회고. 한 답변 60~90초 기준.
Promise.allSettled: 부분 실패 상태 기계가 복잡해진 지점에서 "N개 독립 생성"의 본질에 맞게 클라이언트 병렬로 뒤집음. 브라우저 connection 제한을 의도적으로 throttler로 활용. (ADR-073)ExecutionContext에 저장 → 실패 지점부터 재시작.REQUIRES_NEW 별도 트랜잭션 + traceId로 저장, 재처리 큐로 흘려보냄.AsyncItemProcessor + 커서 재시작(task/ai-service-team/rag-vector-search-batch.md), REQUIRES_NEW 기반 Outbox(resume 문항 1).must (병합 차단) / should (논의 후 결정) / nit (취향). 이유 없는 must는 금지.AbstractSlotTest 같은 추상 테스트 클래스로 테스트를 쓰는 게 쉬운 구조를 먼저 만드는 것이 리뷰보다 앞섭니다.PostCommitUpdateEventListener → RabbitMQ Fanout (커밋 이후만 발행, ID만 전달해 각 서버가 선택 갱신).StampedLock writeLock + tryReadLock(2500ms). 짧고 예측 가능한 대기.@TransactionalEventListener(AFTER_COMMIT)에서 발행. 발행 실패 시 Propagation.REQUIRES_NEW로 실패 기록 + traceId 저장 → 스케줄러 재시도.EXPLAIN + 인덱스 구조 확인 → 3) @EntityGraph 또는 fetch join으로 평탄화 vs 프로젝션(DTO)로 우회 → 4) 컬렉션 여러 개를 동시 fetch join하는 MultipleBagFetchException은 batch size + fetch join 혼합으로 우회 → 5) 벌크 연산 후 영속성 컨텍스트 clear 누락 주의.IN절 일괄 조회로 묶어 쿼리 수를 크게 줄인 사례가 있습니다.lastGenerationStatus, lastGenerationError, lastGeneratedAt을 DB에 박아 UI에 구체적 사유를 노출했습니다.must/should/nit 같은 태그 문화가 있는지, 없다면 리뷰 갈등은 어떻게 중재되나요?gradle init 상태 준비 (필요 시 빠른 시작).AsyncItemProcessor, @TransactionalEventListener(AFTER_COMMIT))는 또박또박."저는 기능을 만드는 것보다 팀이 같은 기능을 더 빠르고 안전하게 다시 만들 수 있는 구조를 남기는 데 동기부여되는 사람입니다. 오늘 이야기한 경험들을 이 팀에서 상품·전시·주문 도메인에 다시 적용해 보고 싶습니다."