결제, 앱, 로밍, 사내 AI 에이전트, 프라이싱, 멤버십, 프로모션, DX, 개인 서비스의 주요 사례

프로젝트 포트폴리오

경력기술서와 이력서에 정리한 프로젝트 가운데, 멀티 벤더 결제, 앱/WebView 브릿지, 로밍 안정화, 사내 AI 에이전트 라우팅과 로그 진단, 프라이싱 플랫폼, 멤버십 이관, 포인트 지갑 설계, DX 자동화, 그리고 Commit Map처럼 개인 문제를 제품으로 풀어본 사례를 골라 정리했습니다.

Projects

13

Primary Stack

Kotlin Spring Boot MySQL

Focus

아키텍처, 상태 전이, 운영 복원력

01

Kotlin/Spring Boot 중심의 백엔드 개발 경험

02

OAuth2, PG, 게이트웨이, 외부 인증서 서비스가 포함된 MSA 연동 경험

03

U+ VIP콕 제휴 쿠폰, 외부 멤버십 G/W, 운영 어드민 연동 경험

04

Flutter/WebView 기반 하이브리드 앱과 앱 검증 도구 개발 경험

05

공공 연계 로밍 데이터의 이벤트 처리, 재처리, 월간 재동기화 기반 운영 안정화 경험

06

전월까지의 누적합은 Redis 24h 캐시, 전일까지의 일간 delta는 DB upsert로 누적, 당일 합산은 Redis/LocalCache와 분산 락으로 보호해 홈페이지 공개 트래픽에서도 원본 부하를 줄이며 통계를 지속 갱신한 경험

07

Pub/Sub DLQ, Athena 배치, 월간 파티션, 서킷브레이커 기반 운영 안정화 경험

08

채팅 기반 AI 에이전트, LLM 라우팅, Tool Calling, WebSocket 스트리밍, 운영 로그 진단 자동화 경험

09

LLM 리뷰 workflow, Docusaurus, Firebase App Distribution, capture/replay 익스텐션, Jenkins/TestFlight 기반 DX 개선 경험

Project

01

LG유플러스 볼트업 / 2024.10 - 현재

멀티 벤더 결제 시스템 및 미수 복구 안정화

서비스 초기 설계부터 구현까지 전담

결제 서비스 초기 설계부터 구현까지 전담하며, 단일 결제 구조를 멀티 벤더사를 수용하는 모듈 구조로 확장하고, DLQ 기반 미수 이벤트 처리와 자동 복구 체계를 구성했습니다. 이후 DLQ retry stuck 방지, PG not-found 후속 재결제, 취소 알림톡 분기까지 운영 안정화 영역을 보강했습니다.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Cloud SQL

설계 배경

단일 결제 중심 구조에서 복수 벤더를 같은 방식으로 수용하고, 이후 새로운 벤더가 늘어나더라도 같은 확장 지점으로 붙일 수 있는 구조가 필요했습니다.

강조 포인트

PG 확장과 미수 이벤트 재처리 기준을 함께 설명할 수 있는 프로젝트입니다.

핵심 구현

  • 추상 클래스 기반 벤더 전략 패턴으로 PG사 통합 아키텍처를 구성했습니다.
  • GCP Pub/Sub 기반 DLQ 패턴을 구현하고 실패 이벤트를 별도 큐로 격리해 미수 처리 대상이 추적 가능하도록 구성했습니다.
  • 재시도, DLQ, NACK 기반 자동 복구 경로를 구현해 미수 이벤트가 결제 보완 처리 흐름으로 이어지도록 구성했습니다.
  • FAILOVER 상태 전이와 retry timestamp 기록을 보강해 dead-letter 재시도가 stuck 되지 않도록 만들고, PG not-found 응답에서도 미수 재결제 플로우가 끊기지 않도록 수정했습니다.
  • 전액 취소, 부분 취소, 로밍 결제 취소 알림톡 context를 분리하고 금액 포맷팅을 정리해 사용자 커뮤니케이션 정확도를 높였습니다.

엔지니어링 관점

  • 결제 요청, 포인트 hold/confirm, 성공·실패 업데이트를 PaymentProcessor 중심으로 묶어 상태 전이를 한 곳에서 추적할 수 있게 설계했습니다.
  • 같은 사용자의 중복 결제 시도가 포인트 hold를 동시에 건드리지 않도록, 유저 단위 락 안에서 hold와 payment READY 생성을 함께 처리했습니다.
  • PG 네트워크 불확실성은 repair, 컨슈머 재시도, failover 배치로 층을 나눠 즉시 복구와 운영 개입 경계를 분리했습니다.
  • 재시도 자체도 운영 관찰 대상이라고 보고, 실패 이벤트가 어디에서 멈췄는지 latestRetriedAt과 상태 전이로 남겨 후속 보정 판단이 가능하게 했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

멀티 벤더 결제 오케스트레이션: 락, hold, 상태 전이

PaymentProcessor 기준으로, 요청 데이터와 락 키, 포인트 hold, payment READY, `PG Vendor` 내부 승인 단계, 성공/실패 상태 전이가 한 장 안에서 자연스럽게 이어지도록 정리했습니다.

flowchart TD
  Req["pay / rePay<br/>order=ORD-240915-001<br/>user=421 method=17 point=2000"] --> Lock["distributed lock<br/>payment-user-process:421"]
  Lock --> Hold["PointUpdater.hold()<br/>wallet -> HOLD 2000P"]
  Hold --> Ready["createWithReady()<br/>payment READY"]
  Ready --> Sub["resolve subscription<br/>methodId=17 or primary"]
  Sub --> Vendor["PG Vendor Router<br/>supports: KakaoPay / TossPayments / KakaoT<br/>selected: KakaoT"]
  subgraph PGV["PG Vendor Layer"]
    direction TD
    Vendor --> Keys["read vendor keys<br/>pgPayKey + token"]
    Keys --> Api["vendor client.pay(...)"]
    Api --> Tx["save pgTransactionId<br/>paymentId / tid / paymentKey"]
  end
  Tx --> Result{"approval result"}
  Result -->|success| Done["updateSuccess<br/>payment PAID<br/>point HOLD->CONFIRM"]
  Result -->|fail| Fail["updateFailed<br/>releaseHold(order)"]
  Fail --> Recovery["repair / retry / failover"]
  classDef vendor fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  class Vendor,Keys,Api,Tx vendor;
  style PGV fill:#eef7fb,stroke:#0f4c81,stroke-width:2px,color:#0f172a;

Mermaid View

멀티 Vendor 시스템: 필수 계약과 선택 확장

`VendorType` 확장 지점 위에 `VendorChecker.select()`를 두고, 모든 벤더에 필요한 계약과 특정 벤더에만 필요한 기능을 분리했습니다. `@RequiredVendor`와 `VendorRequirementsValidator`가 앱 시작 시 필수 구현 누락을 막고, `VendorPaymentOnceProcessor(KakaoPay)`나 repair 서비스는 필요한 벤더에만 붙도록 구성했습니다.

flowchart TD
  Vendors["VendorType<br/>KakaoPay / TossPayments / KakaoT"] --> Select["VendorChecker.select(vendorType)"]
  Select --> Required["Required on all vendors<br/>VendorPaymentProcessor<br/>VendorMethodProcessor"]
  Select --> Partial["Required on some vendors<br/>VendorPaymentOnceProcessor<br/>(KakaoPay only)"]
  Select --> Optional["Optional extensions<br/>RepairService / vendor hooks"]
  Required --> Validate["@RequiredVendor<br/>+ VendorRequirementsValidator"]
  Partial --> Validate
  Validate --> Boot{"startup validation"}
  Boot -->|missing| Error["application start fail"]
  Boot -->|ok| Route["route to concrete impl"]
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef optional fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef error fill:#fff1f2,stroke:#be123c,stroke-width:2px,color:#0f172a;
  class Vendors,Select,Required,Partial,Validate,Route core;
  class Optional optional;
  class Error error;

운영 결과

  • 복수 PG를 단일 인터페이스로 통합하고, 새로운 벤더가 추가돼도 같은 구조로 확장 가능한 결제 아키텍처를 만들었습니다.
  • DLQ 기반 미수 이벤트 처리와 자동 복구 경로를 운영에 적용했습니다.
  • 외부 PG 응답 예외와 취소 알림 분기를 보강해 미수 복구 흐름과 사용자 안내 메시지의 신뢰도를 높였습니다.
Project

02

LG유플러스 볼트업 / 2025.07 - 현재

카카오T 계정 링크 및 결제수단 등록

회원 식별 통합, AuthMethod 연결, 카드 등록 상태와 제휴사 고객 토큰 기준 설계

VoltUp 회원가입 이후 카카오T 외부 계정을 암호화된 CI 기준으로 연결하고, mobile-gateway의 한 스텝 API에서 결제수단 등록 세션 생성부터 payment-service의 READY 상태 전환, ACTIVE 상태 전환까지 이어지는 흐름을 설계했습니다. 이후 제휴사 고객 토큰을 외부 결제수단과 내부 사용자 컨텍스트를 잇는 기준 키로 정리해 검색, 해지 검증, 앱 콜백 activate 흐름을 안정화했습니다.

이력서 연결 지점

유저 사이드 백엔드

이력서의 유저 사이드 백엔드 항목은 카카오T 연동을 중심으로 차량/PnC(Plug & Charge), 인증서 안정화 등 사용자 경험 핵심 흐름을 함께 다룬 이 영역으로 연결됩니다.

Kotlin Spring Boot OAuth2 Flyway T Partner API

참고 화면

Preview

로그인 수단 연결: 동일 회원 아래 카카오T 계정 연결

동일 사용자 기준으로 여러 로그인 수단을 묶고, 카카오T 계정 연결 이후 결제수단 등록 흐름으로 이어지도록 설계한 화면입니다.

VoltUp 로그인 수단 연결 화면

로그인 수단 연결: 동일 회원 아래 카카오T 계정 연결

동일 사용자 기준으로 여러 로그인 수단을 묶고, 카카오T 계정 연결 이후 결제수단 등록 흐름으로 이어지도록 설계한 화면입니다.

결제수단 등록: 카카오T / 카카오페이 / 일반 카드 분기

추가하기 한 번으로 카카오T, 카카오페이, 일반 카드 등록 경로를 한 바텀시트에서 노출해 사용자 선택 흐름을 단순화한 화면입니다.

VoltUp 결제수단 등록 바텀시트 화면

결제수단 등록: 카카오T / 카카오페이 / 일반 카드 분기

추가하기 한 번으로 카카오T, 카카오페이, 일반 카드 등록 경로를 한 바텀시트에서 노출해 사용자 선택 흐름을 단순화한 화면입니다.

설계 배경

기존 VoltUp 회원과 KakaoT 유저를 중복 계정 없이 합쳐야 했고, 그 위에서 카드 등록 세션과 최종 결제수단 상태가 같은 사용자 컨텍스트를 공유해야 했습니다.

강조 포인트

회원 식별 통합과 카드 등록 상태 전이를 함께 설명하기 좋은 프로젝트입니다.

핵심 구현

  • KakaoT OAuth 결과의 `externalId`, `ci`를 기준으로 기존 VoltUp 회원을 찾고 연결된 인증 수단을 추가하는 흐름을 설계했습니다.
  • mobile-gateway에서 현재 사용자의 암호화된 CI를 기준으로 카카오T 결제수단 연동 세션을 생성하는 한 스텝 API 흐름을 정리했습니다.
  • payment-service에서는 payment payload에 `session_key`를 저장하고, confirm 시 `pgPayKey`와 제휴사 고객 토큰을 확정하는 상태 전이를 구현했습니다.
  • 제휴사 고객 토큰 검색 필터와 복합 인덱스를 추가하고, 카카오T 해지 시 현재 사용자의 결제수단인지 검증하는 경로를 보강했습니다.
  • 앱 콜백 전용 activate API를 분리하고 DTO alias, `@JsonProperty`, 검색 로그를 보강해 외부 스키마 차이와 운영 추적성을 흡수했습니다.

엔지니어링 관점

  • auth-domain은 외부 계정 정보 수집, identity-service는 동일인 판단과 AuthMethod 소유권, payment-service는 결제수단 상태를 맡도록 경계를 분리했습니다.
  • 회원 연결이 끝나기 전에는 카드 등록을 진행하지 않고, 연결 완료 후에만 session 생성과 activate를 진행해 사용자 식별 불일치를 막았습니다.
  • `READY -> ACTIVE` 전이에서 필요한 `session_key`, 제휴사 고객 토큰, `pgPayKey`를 같은 subscription 행에 모아 이후 승인/취소 호출도 재사용 가능하게 했습니다.
  • 제휴사 고객 토큰을 단순 응답 필드가 아니라 사용자-외부 결제수단 정합성을 확인하는 운영 키로 보고, 조회와 해지 검증이 같은 기준을 공유하도록 정리했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

VoltUp 회원과 카카오T 계정 연결 후 카드 등록

회원 식별, 암호화된 CI 기준 계정 연결, link session 생성, READY 상태 전환과 ACTIVE 상태 전환 흐름을 한 번에 따라갈 수 있게 정리했습니다.

flowchart TD
  User["VoltUp member<br/>encrypted CI"] --> OAuth["KakaoT OAuth<br/>external account + encrypted CI"]
  OAuth --> Link["identity-service<br/>linked auth method"]
  Link --> Session["mobile-gateway link session<br/>ACCOUNT + PAYMENT"]
  Session --> Ready["payment-service init<br/>READY 상태 전환"]
  Ready --> Active["confirm success<br/>ACTIVE 상태 전환"]

운영 결과

  • 기존 VoltUp 회원과 KakaoT 계정을 동일인 기준으로 연결한 뒤 카드 등록을 이어가는 사용자 흐름을 정리했습니다.
  • 결제수단 등록 완료 후 subscription이 ACTIVE 상태를 유지하며 승인, 취소, 조회가 같은 식별 컨텍스트를 재사용하도록 만들었습니다.
  • 앱 콜백, 웹 로그인, 해지 검증이 섞이는 상황에서도 현재 사용자와 결제수단의 매칭 기준이 흔들리지 않도록 만들었습니다.
Project

03

LG유플러스 볼트업 / 2025.07 - 현재

통합 프로모션(쿠폰/포인트) 플랫폼 구축 및 고도화

promotion-service 정책 확장, 결제수단 제한, 포인트 지갑 구조 설계

VoltUp `promotion-service`에서는 쿠폰팩의 등록 기간과 사용 기간을 기준으로 코드 발급, 코드 등록, 코드 없이 유저 직접 할당, 만료 알림 배치를 처리했고, 제휴 쿠폰별 허용 결제수단 정책까지 발급/조회/사용/Admin 생성 흐름에 반영했습니다. 별도로 포인트는 accrual 단위 만료를 다루기 위해 지갑 구조와 차감 순서를 설계했습니다.

Kotlin Spring Boot Spring Batch MySQL JPA QueryDSL JDBC Distributed Lock

설계 배경

넥센, 도요타, 블루멤버스 등 제휴사별 요구사항을 수용하면서도, 쿠폰은 코드형 발급과 무코드 직접 할당을 같이 지원해야 했고, 일부 쿠폰팩은 카카오T/일반 카드/카카오페이처럼 허용 결제수단이 달라야 했습니다. 포인트는 적립 건마다 다른 만료일을 가진 구조를 안정적으로 처리해야 했습니다.

강조 포인트

쿠폰 서비스의 코드 발급/무코드 할당/만료 알림과 포인트 지갑 차감 구조를 함께 설명하기 좋은 프로젝트입니다.

핵심 구현

  • 쿠폰은 `couponPack`에 `registerStartAt/registerEndAt`, `usableStartAt/usableEndAt`를 두고, 등록 가능 기간과 사용 가능 기간을 분리해 관리했습니다.
  • 코드형 쿠폰은 `batchIssue`에서 Snowflake 기반 ID를 SHA-256 후 base36 10자리 코드로 변환해 bulk 저장하고, 충돌 시 개별 저장 fallback으로 마무리했습니다.
  • 코드 없이 유저에게 바로 주는 경우에는 `mapping(code=null)` 또는 `batchMapping(userIds)`로 쿠폰 행을 직접 만들고, 코드 등록은 `coupon-mapping:{code}` 락 안에서 사용자와 연결했습니다.
  • 허용 결제수단을 쿠폰팩 정책으로 추가하고, 빈 값은 전체 허용으로 해석해 기존 쿠폰과의 호환성을 유지하면서 발급/조회/사용 단계에 같은 제한을 적용했습니다.
  • Admin 쿠폰팩 생성 폼에는 허용 결제수단 멀티셀렉과 Encoded ID 노출을 추가해 운영자가 정책을 생성 시점부터 확인할 수 있게 했습니다.
  • 포인트는 `addBulk`에서 `expiredAt`이 있으면 새 `PointWallet`을 만들고, 없으면 같은 `type + chargeType` 지갑에 합산해 적립 단위와 만료 단위를 함께 관리했습니다.
  • 포인트 사용 시에는 활성 지갑을 페이지 조회한 뒤 `FREE -> expiredAt 빠른 순`으로 정렬해 여러 wallet을 순차 hold하고, 실패 시에는 hold에 참여한 지갑만 정확히 복원했습니다.
  • 쿠폰 만료는 만료 알림 배치에서 `usableEndAt` 기준 윈도우를 읽어 `EXPIRED` 이벤트를 발행하도록 했고, 포인트는 당월 만료분을 다음 달 히스토리 배치에 반영했습니다.

엔지니어링 관점

  • 쿠폰은 `couponPack`이 기간과 할인 정책을 소유하고 개별 `coupon`이 유저 매핑과 사용 상태를 가지도록 나눠, 코드형/무코드형 발급을 같은 모델 안에서 처리했습니다.
  • 코드 등록은 `coupon-mapping:{code}` 락, 사용 처리는 `coupon-process:{userId}` 락, DB는 `unique(code)`와 `unique(userId,couponPackId)`로 보강해 중복 등록과 중복 발급을 동시에 막았습니다.
  • 만료는 별도 상태를 추가하기보다 `usableEndAt` 기준으로 만료 대상 쿠폰팩을 읽고, 미사용 쿠폰 사용자에게 `EXPIRED` 이벤트를 발행하는 배치 경로로 분리했습니다.
  • 결제수단 제한은 화면 조건으로만 두지 않고 쿠폰팩 도메인 정책으로 끌어올려, Admin에서 만든 정책이 사용자 발급/조회/사용 단계까지 같은 의미로 흐르도록 했습니다.
  • 포인트를 단일 잔액이 아닌 `PointWallets` 엔티티로 분리하고, `expiredAt`이 있는 적립은 새 wallet, 없는 적립은 동일 `type + chargeType` 지갑에 합산해 만료 규칙이 데이터 구조에 직접 드러나게 했습니다.
  • 사용 시에는 활성 wallet을 읽어 `FREE -> expiredAt asc` 순으로 hold를 배분하고, `releaseHold(order)`는 실제 hold된 `pointWalletId`만 다시 찾아 복원하게 해서 순차 차감과 복구 기준을 일치시켰습니다.

Mermaid로 보는 핵심 구조

Mermaid View

promotion-service: 코드 발급, 무코드 할당, 만료 알림

쿠폰팩 기준으로 코드 발급, 유저 코드 등록, 코드 없이 직접 할당, `usableEndAt` 기반 만료 알림 배치까지 실제 `promotion-service` 흐름을 한 장으로 정리했습니다.

flowchart TD
  Pack["couponPack 71<br/>register 10/01~10/31<br/>usable 10/01~11/30"] --> Code["batchIssue(size=1000)<br/>Snowflake -> SHA-256/base36<br/>code=37PRPT85WA"]
  Pack --> Direct["mapping(code=null) / batchMapping<br/>user=421 or [421,422]"]
  Pack --> VendorPolicy["allowed payment methods<br/>partner / card / wallet"]
  Code --> Claim["mapping(user=421, code=37PRPT85WA)<br/>lock coupon-mapping:37PRPT85WA"]
  Claim --> Guard["DB unique guard<br/>code / (userId,couponPackId)"]
  Direct --> Guard
  Guard --> Ready["coupon row<br/>userId=421 status=READY"]
  Ready --> Process["process(price=32000, user=421)<br/>lock coupon-process:421<br/>READY -> PROCESSING"]
  VendorPolicy --> Process
  Process --> Finish["complete -> COMPLETE<br/>rollback -> READY"]
  Pack --> Expire["expiry reminder batch<br/>usableEndAt D+3 window"]
  Expire --> Scan["getAllByCouponPackId<br/>completeAt is null"]
  Scan --> Event["publish promotion event<br/>eventType=EXPIRED"]

Mermaid View

포인트 지갑: 제휴별 적립과 만료일 순차 차감

쿠폰 흐름과 분리해서, 여러 제휴 포인트가 wallet으로 쪼개지고 활성 지갑 조회 -> 정렬 -> hold -> confirm/release로 어떻게 관리되는지 예시 데이터와 함께 보여줍니다.

flowchart TD
  Grant["addBulk / partner accrual<br/>BASE 1200P exp 10-18<br/>TOYOTA 3000P exp 10-20<br/>BLUEMEMBERS 800P exp 10-22<br/>NEXEN 5000P exp 10-31<br/>EVENT 700P exp 11-15<br/>BASE 900P exp null"] --> Rule["wallet rule<br/>expiredAt 있으면 new wallet<br/>null이면 same type+chargeType merge"]
  Rule --> Wallets["wallet #11 BASE/FREE 1200 exp 10-18<br/>wallet #12 TOYOTA/FREE 3000 exp 10-20<br/>wallet #13 BLUEMEMBERS/FREE 800 exp 10-22<br/>wallet #14 NEXEN/CHARGE 5000 exp 10-31<br/>wallet #15 EVENT/FREE 700 exp 11-15<br/>wallet #16 BASE/FREE 900 exp null"]
  Wallets --> Active["active wallet scan<br/>expiredAt > now only<br/>createdAt asc page 조회"]
  Active --> Order["deduction order<br/>FREE first<br/>then expiredAt asc"]
  Order --> Hold["hold 4500P<br/>#11 -1200<br/>#12 -3000<br/>#13 -300"]
  Hold --> Usage["point_usage rows<br/>order=ORD-240915-001<br/>walletId=11,12,13<br/>status=HOLD"]
  Usage --> Result{"payment result"}
  Result -->|success| Confirm["confirm<br/>HOLD -> CONFIRM<br/>wallet amount final"]
  Result -->|fail| Release["releaseHold(order)<br/>wallet 11 +1200<br/>wallet 12 +3000<br/>wallet 13 +300<br/>usage -> FAILED"]
  Wallets --> Expire["expiry handling<br/>expired wallet = active 제외<br/>expiringSoon 별도 조회"]
  classDef wallet fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef state fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Wallets,Active,Order,Hold,Usage wallet;
  class Confirm,Release,Expire state;

운영 결과

  • 코드형 쿠폰 발급, 코드 등록, 무코드 직접 할당, 만료 알림 배치를 `promotion-service` 안에서 같은 모델로 운영할 수 있게 정리했습니다.
  • 제휴 프로모션의 결제수단 제한 요구를 쿠폰팩 정책으로 흡수해 할인 정책과 실제 결제/정산 조건이 어긋날 가능성을 줄였습니다.
  • 포인트 지갑, 만료 순서 차감, 다음 달 히스토리 배치 반영도 함께 운영해 쿠폰과 포인트를 하나의 프로모션 백엔드에서 설명할 수 있게 했습니다.
Project

04

LG유플러스 볼트업 / 2024.12 - 현재

볼트업 하이브리드 앱: WebView 브릿지와 네이티브 기능

Flutter 하이브리드 앱 런칭, JSBridge, QR/권한/푸시/강제 업데이트 흐름 설계

VoltUp 2.0 런칭을 위해 Flutter 기반 Android/iOS 하이브리드 앱을 빠르게 구축하고, WebView 화면이 네이티브 기능을 안정적으로 호출할 수 있도록 JSBridge와 앱 핵심 흐름을 설계했습니다. 이후 QR 스캔, 카메라 권한, FCM, 강제 업데이트, Crashlytics 기반 안정화까지 운영 중인 앱의 품질 개선을 이어갔습니다.

Flutter Dart Kotlin Swift WebView JSBridge ML Kit FCM Crashlytics

설계 배경

짧은 일정 안에 Android/iOS 앱을 런칭해야 했고, 서비스 화면은 WebView로 빠르게 확장하면서도 QR 스캔, 카메라 권한, 새창 처리, 푸시, 강제 업데이트처럼 앱만이 처리할 수 있는 기능은 네이티브 계층에서 안정적으로 제공해야 했습니다.

강조 포인트

사용자 앱을 빠르게 런칭한 경험과 WebView-네이티브 브릿지, QR/권한/푸시/업데이트 같은 앱 고유 기능 설계를 함께 설명하기 좋은 프로젝트입니다.

핵심 구현

  • Flutter 기반 하이브리드 앱 구조를 잡고 2개월 내 Android/iOS 런칭을 목표로 WebView 중심 화면과 네이티브 기능 호출 경계를 설계했습니다.
  • JSBridge를 통해 프론트엔드가 새창, 외부 URL, QR 스캔, 카메라 권한, 앱 메시지, 강제 업데이트 같은 네이티브 기능을 호출하는 규약을 구현했습니다.
  • QR 인식 경험을 제어하기 위해 ML Kit 기반 커스텀 QR 스캐너 페이지와 반응형 스캔 UI를 구현하고 기존 스캐너 의존성을 줄였습니다.
  • Crashlytics 기반으로 Dart/native 오류 수집 경로를 연결하고, 카메라와 FCM 흐름을 안정화했습니다.

엔지니어링 관점

  • 하이브리드 앱에서 WebView는 빠른 화면 확장성을 맡고, 네이티브 계층은 OS 권한과 하드웨어 기능을 맡도록 경계를 분리했습니다. JSBridge는 이 둘 사이의 제품 계약으로 보고, 프론트엔드가 호출할 수 있는 기능을 명시적인 메시지 흐름으로 정리했습니다.
  • QR 스캔과 카메라 권한은 충전 시작의 핵심 진입점이라 단순 패키지 적용보다 기기별 레이아웃, lifecycle, 권한 상태를 앱 UX 안에서 제어할 수 있게 만드는 데 초점을 뒀습니다.
  • 운영 중 앱 안정성은 Crashlytics 신호를 기준으로 개선했습니다. 카메라 예외와 FCM 중복 호출처럼 사용자 진입 흐름을 흔드는 문제를 묶어 안정성을 높였습니다.

Mermaid로 보는 핵심 구조

Mermaid View

WebView 화면과 네이티브 기능을 잇는 앱 브릿지

WebView 화면에서 JSBridge를 통해 새창, QR 스캔, 카메라 권한, FCM, 강제 업데이트 같은 네이티브 기능으로 이어지고, Crashlytics 신호로 안정화하는 흐름을 정리했습니다.

flowchart TD
  Web["VoltUp WebView 화면"] --> Bridge["JSBridge 계약<br/>frontend -> native"]
  Bridge --> Window["새창 / 외부 URL 처리"]
  Bridge --> QR["QR 스캔<br/>ML Kit custom scanner"]
  Bridge --> Camera["카메라 권한 / lifecycle"]
  Bridge --> Push["FCM push token"]
  Bridge --> Version["강제 업데이트 / version branch"]
  QR --> Native["Android / iOS native layer"]
  Camera --> Native
  Push --> Native
  Version --> Native
  Native --> Observe["Crashlytics<br/>Dart + native error tracking"]
  Observe --> Fix["camera / FCM 안정화"]
  classDef web fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef native fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef ops fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Web,Bridge web;
  class Window,QR,Camera,Push,Version,Native native;
  class Observe,Fix ops;

운영 결과

  • 서비스 2.0 앱을 Android/iOS 양쪽에 빠르게 런칭하고, WebView 화면에서 네이티브 기능을 호출하는 공통 규약을 운영 기준으로 만들었습니다.
  • QR 스캔, 카메라 권한, 푸시, 강제 업데이트처럼 앱이 담당해야 하는 핵심 기능을 네이티브 계층에서 안정적으로 처리하도록 정리했습니다.
  • Crashlytics 기반으로 실제 운영 크래시를 추적하고 수정해 앱 핵심 진입점의 안정성을 지속적으로 개선했습니다.
Project

05

LG유플러스 볼트업 / 2026.05 - 현재

볼트업 앱 검증/운영 보정 익스텐션

앱 연결 없는 기능 검증, API capture/replay, Admin 미지원 운영 보정

앱 개발 중 매번 실제 앱 빌드를 연결해야 검증할 수 있던 새창, QR 스캔, 카메라 권한, 강제 업데이트 버전 분기 등을 브라우저 익스텐션에서 재현해 검증 시간을 줄였습니다. 이후 같은 capture/replay 구조를 충전존 생성 오류 대응처럼 Admin 화면에서 직접 지원하지 않는 단일 API 보정 작업까지 확장했습니다.

TypeScript Chrome Extension API Replay WebView Debugging

설계 배경

앱 기능 검증은 준비 비용이 컸습니다. 간단한 API 흐름이나 WebView-앱 브릿지 동작을 확인하려 해도 앱을 연결해야 했고, 운영에서는 충전존 생성 오류처럼 Admin 화면에 기능이 없지만 단일 API로는 보정 가능한 상황이 반복될 수 있었습니다.

강조 포인트

앱 기능 자체가 아니라 앱 개발과 운영 대응을 빠르게 만드는 도구성 프로젝트입니다. 병목을 발견하고 작은 내부 도구로 구체화하는 일하는 방식을 보여주기에 좋습니다.

핵심 구현

  • Chrome Extension에서 앱이 제공하는 새창, QR 스캔, 카메라 권한, 강제 업데이트 버전 조건을 조정/재현할 수 있는 검증 흐름을 만들었습니다.
  • API 요청을 캡처하고 row 기반 입력으로 replay할 수 있는 구조를 만들어, 앱 연결 없이도 반복 QA와 API 흐름 확인을 빠르게 수행할 수 있게 했습니다.
  • Admin 화면에서 직접 지원하지 않는 단일 API 보정 작업을 위해 variable template, row parser, executor를 구성하고 Bulk Replay로 실행할 수 있게 했습니다.

엔지니어링 관점

  • 이 도구는 처음부터 운영 자동화만을 목표로 한 것이 아니라, 앱 연결이 필요한 개발 검증 병목을 먼저 줄이는 데서 출발했습니다. 이후 같은 capture/replay 구조가 운영 보정에도 유효하다는 점을 확인하고 범위를 넓혔습니다.
  • 충전존 생성 오류 대응 때 JS `fetch` 스크립트를 직접 세팅해 처리했던 경험을, 매번 새로 짜는 임시 스크립트가 아니라 팀이 다시 쓸 수 있는 row 기반 실행 도구로 바꿨습니다.
  • app/admin 호스트가 섞여 있는 환경에서는 잘못된 화면에 잘못된 조작을 노출하지 않도록 호스트별 UI를 분리했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

앱 검증 병목에서 운영 보정 replay까지

앱 연결 없이 앱 의존 흐름을 재현하고, 캡처한 API 요청을 row 기반 replay로 바꿔 개발 QA와 Admin 미지원 운영 보정을 같은 도구 구조로 다루는 흐름입니다.

flowchart TD
  Pain["앱 연결 검증 병목<br/>새창 / QR / 카메라 / 버전"] --> Extension["Chrome Extension<br/>app-like controls"]
  Extension --> Sim["브라우저에서 앱 의존 흐름 재현"]
  Extension --> Capture["API request capture"]
  Capture --> Template["row parser<br/>variable template"]
  Template --> Replay["Bulk Replay executor"]
  Replay --> QA["개발 QA 반복 시간 단축"]
  Replay --> Ops["Admin 미지원 단일 API<br/>운영 보정"]
  Ops --> Share["일회성 JS fetch -> 팀 도구"]
  classDef pain fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef tool fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Pain pain;
  class Extension,Sim,Capture,Template,Replay tool;
  class QA,Ops,Share result;

운영 결과

  • 앱 없이도 앱 의존 흐름을 브라우저에서 빠르게 확인할 수 있어 개발 검증의 대기 시간과 반복 조작을 줄였습니다.
  • Admin 미지원 단일 API 보정 작업을 일회성 스크립트가 아니라 반복 가능한 내부 도구 절차로 다룰 수 있게 했습니다.
  • 병목이 보이면 작은 도구로 만들어 공유하는 작업 방식을 실제 앱 개발/운영 맥락에서 보여주는 사례가 됐습니다.
Project

06

LG유플러스 볼트업 / 2026.02 - 현재

로밍 서비스 안정성: 공공 연계 상태 재동기화와 재처리

환경부 로밍 카드 상태 재설계, 공공 API 재처리, 월간 전체 재동기화

기후에너지환경부 공공 로밍 연계에서 회원카드 상태가 외부 시스템과 장기적으로 어긋나지 않도록 카드 상태 갱신 기준을 결제 응답에서 결제 미수 이벤트 중심으로 재설계했습니다. 공공 API 오류 재처리와 월 1회 전체 재동기화 스케줄러를 더해 이벤트 누락이나 일시 장애 이후에도 기준 데이터를 회복할 수 있게 했습니다.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Scheduler

설계 배경

공공 로밍 연계 데이터는 외부 시스템의 상태와 계속 맞아야 하지만, 온라인 이벤트만으로는 누락이나 일시 장애 뒤의 불일치를 자연스럽게 회복하기 어려웠습니다. 회원카드처럼 기준 데이터에 가까운 항목과 충전기 상태처럼 일부 누락을 감내할 수 있는 항목도 같은 우선순위로 처리하면 재처리 비용이 커질 수 있었습니다.

강조 포인트

외부 공공 시스템과 내부 상태를 장기적으로 맞추기 위해 온라인 이벤트, 재처리, 전체 재동기화를 함께 설계한 운영 안정화 프로젝트입니다.

핵심 구현

  • 카드 상태 업데이트 기준을 결제 응답 중심에서 결제 미수 이벤트 중심으로 바꾸고, 미수 발생 건에 대해서만 선별적으로 상태를 갱신하도록 정리했습니다.
  • 로밍 카드 상태 처리 경로를 단순화하고 결제 상태 조회를 통합해 변환/조회 오버헤드를 줄였습니다.
  • 공공 API 오류 재처리는 회원카드처럼 기준 데이터 성격이 강한 항목을 우선 처리하고, 충전기 상태처럼 누락 허용 가능한 항목은 후순위로 분리했습니다.
  • 환경부 회원카드 월 1회 전체 재동기화 스케줄러와 task seed를 추가해 온라인 이벤트가 놓친 차이를 주기적으로 복구할 수 있게 했습니다.

엔지니어링 관점

  • 상태 갱신 기준을 결제 응답에 묶어두면 정상 결제 흐름까지 로밍 상태 변경의 원인이 될 수 있어, 실제 보정이 필요한 미수 이벤트로 기준을 좁혔습니다.
  • 재처리는 “무조건 다시 시도”가 아니라 데이터 중요도에 따라 우선순위를 나누는 운영 설계로 봤습니다. 회원카드는 기준 데이터라 먼저 회복하고, 충전기 상태는 후순위로 두어 비용을 조절했습니다.
  • 월간 전체 재동기화는 온라인 이벤트 처리의 보완재로 두었습니다. 이벤트 누락을 완전히 없애려 하기보다, 누락이 생겨도 장기 drift가 누적되지 않는 회복 경로를 만든 것입니다.

Mermaid로 보는 핵심 구조

Mermaid View

공공 로밍 데이터의 이벤트 처리, 재처리, 월간 재동기화

결제 미수 이벤트 기준 상태 갱신, 중요도별 공공 API 재처리, 월간 전체 재동기화를 함께 두어 외부 시스템과의 장기 drift를 줄이는 구조입니다.

flowchart TD
  Source["환경부 로밍 API<br/>회원카드 / 충전기 상태"] --> Online["온라인 이벤트 처리<br/>card state update"]
  Online --> Arrears["결제 미수 이벤트 기준<br/>필요 건만 상태 갱신"]
  Source --> Retry["공공 API 오류 재처리<br/>중요도별 우선순위"]
  Retry --> Member["회원카드 우선 재처리<br/>기준 데이터 회복"]
  Retry --> Charger["충전기 상태 후순위<br/>누락 허용 항목 분리"]
  Source --> Monthly["월 1회 전체 재동기화<br/>dynamic scheduler + seed"]
  Monthly --> Baseline["외부 시스템과 장기 drift 방지"]
  Arrears --> Stable["운영 데이터 정확성"]
  Member --> Stable
  Baseline --> Stable
  classDef external fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef process fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Source external;
  class Online,Arrears,Retry,Member,Charger,Monthly process;
  class Baseline,Stable result;

운영 결과

  • 카드 상태 업데이트 기준을 재설계해 불필요한 상태 변경 가능성을 줄이고 데이터 정확성을 높였습니다.
  • 공공 API 오류 이후에도 중요 데이터가 우선 복구되는 재처리 경로를 운영 기준으로 만들었습니다.
  • 월간 전체 재동기화로 이벤트 누락이나 일시 장애 이후에도 회원카드 기준 데이터가 외부 시스템과 다시 맞춰지는 안전망을 확보했습니다.
Project

07

LG유플러스 볼트업 / 2026.06 - 현재

Voltbot: 사내 업무 에이전트 플랫폼과 로그 분석 자동화

채팅 기반 업무 에이전트 플랫폼, 개별 에이전트 자동 라우팅, 로그 진단 에이전트 설계·구현

Voltbot은 사내 구성원이 코드 정책, BigQuery 데이터 조회, 법률 지원, 운영, 로그 진단 같은 전문 에이전트를 채팅으로 사용하는 업무 플랫폼입니다. 이 안에서 제가 만든 핵심 축은 두 가지였습니다. 첫째, 사용자가 에이전트를 직접 고르지 않아도 질문 의도와 권한을 기준으로 필요한 전문 에이전트를 자동 선택하는 개별 에이전트 자동 라우팅(`Voltbot Crew`/`AgentRouter`)을 설계·구현했습니다. 둘째, 운영/CS가 개발자에게 요청하던 원인 분석 병목을 줄이기 위해 로그 조회, BigQuery 조회, 코드 정책 확인 결과가 같은 대화 컨텍스트에 쌓이고 다음 에이전트가 그 컨텍스트를 이어받아 판단하는 흐름을 구성했습니다.

Kotlin Spring Boot React TypeScript WebSocket GCP Cloud Logging LLM GitHub API Google OAuth MySQL Multi-Agent

Product Preview

채팅으로 쓰는 사내 업무 에이전트 플랫폼

사용자 요청을 Agent Router가 먼저 분류하고, 적절한 전문 에이전트가 로그, 가이드, 코드를 오가며 답을 구성합니다.

권한 기반 접근 PII 마스킹 토큰/비용 표시 세션 공유
VOLTBOT
고객 224514가 어제 결제 실패했다는데 원인 확인해줘

Agent Router

자동 연결

결제 실패 / 로그 근거 요청 → 로그 분석 에이전트

권한 확인 · 에이전트 상태 확인 · 세션 컨텍스트 전달

실행 내역

  1. 1 의도 분류 및 에이전트 라우팅
  2. 2 운영 로그 조회
  3. 3 가이드 확인
  4. 4 코드 근거 검색
  5. 5 연계 플로우 생성

진단

카드사/은행 시스템 점검 시간대에 외부 PG가 결제를 반려한 일시적 실패로 판단했습니다.

근거 로그

  • svc=payment-service-worker | code=4902
  • order_number=260706170149MSGD
  • traceId -> user_id -> order_number 순서로 재조회

권장 조치

  • 고객에게 점검 종료 후 재시도 안내
  • 반복 실패 시 PG 점검 일정 확인
  • 자연인 정보는 표시 단계에서 마스킹
토큰 사용량 61.2K · 컨텍스트 23.6K · 예상 비용 $0.09

설계 배경

고객 결제 실패나 충전 오류 문의가 들어오면 운영자가 에러 코드, 서비스 간 상관키, 정책상 정상 차단 여부를 직접 찾기 어려워 개발자에게 수동 분석을 요청해야 했습니다. 동시에 사내 AI 도구는 여러 에이전트를 같은 채팅 표면에서 권한별로 안전하게 제공해야 했고, 사용자가 코드 정책 조회, 로그 진단, 데이터 분석 중 무엇을 골라야 하는지 판단하는 비용도 줄여야 했습니다.

강조 포인트

하나의 Voltbot 서비스 안에서 제가 맡은 기여를 개별 에이전트 자동 라우팅과 컨텍스트 공유 기반 운영 진단 흐름으로 묶어 보여주는 프로젝트입니다. 사내 반복 업무를 AI로 대체했다기보다, 개발자 의존 트리아지 흐름을 권한·근거·가이드·코드/데이터 탐색이 있는 제품형 업무 도구로 바꾼 사례입니다.

핵심 구현

  • `Voltbot Crew`를 사용자가 선택할 수 있는 하나의 진입점으로 두고, 내부에서는 `AgentRouter`가 자연어 요청, 대화 맥락, 에이전트 설명, 사용자 권한을 기준으로 로그 분석·데이터 분석·법률 지원·코드 정책 같은 전문 에이전트를 자동 배정하도록 설계·구현했습니다.
  • 공통 `Agent`/`Tool` 계약 위에서 `AgentRunner`, `AgentRouter`, `ToolHandler` 흐름을 연결해 세션, 컨텍스트, 권한, 쿼터, 중단 요청, Tool 호출/결과가 WebSocket으로 이어지도록 구성했습니다.
  • 초기에는 하나의 대화 세션에서 2개 에이전트를 조합하고 같은 컨텍스트를 공유하는 `TEAM` 구조를 프로토타입으로 검증했고, 이후 제품 적용이 더 단순한 `AUTO_ROUTING` 구조로 전환해 복잡한 세션 모델을 사용자에게 노출하지 않도록 정리했습니다.
  • `LogDiagnosisAgent`에는 고객 모드와 운영 모드를 나누고, userId가 없어도 기간·증상·서비스 패턴만으로 조사할 수 있도록 검색 전략을 설계했습니다.
  • `searchUserLogs` 도구를 통해 GCP 운영 로그를 `services`, `severity`, `excludeIstio`, `range/from/to`, `pageToken`, raw LQL 조합으로 조회하고, 결과가 비거나 모호하면 범위 확대와 query 보강을 반복하도록 만들었습니다.
  • 로그 분석 가이드 업로드/조회/수정 화면과 `listLogDiagnosisGuides`, `readLogDiagnosisGuide` 도구를 연결해 운영 지식이 에이전트 런타임에 직접 참조되도록 했습니다.
  • 코드 정책 에이전트가 정상 조건·상태 전이·에러 코드를, BigQuery 조회 에이전트가 집계·이력·패턴 정보를, 로그 진단 에이전트가 실제 실행 로그를 같은 컨텍스트에 남기도록 구성했습니다. 이후 다음 에이전트는 앞선 결과를 이어받아 서비스·시간 범위·상관키를 좁히고 1차 원인을 분류할 수 있게 했습니다.
  • traceId, user_id, order_number를 상황에 맞게 갈아타는 상관키 pivot 규칙과, 2개 이상 서비스가 얽히면 Mermaid sequenceDiagram으로 연계 플로우를 표현하는 답변 기준을 추가했습니다.
  • 로그 인용과 진단문에는 자연인 식별 정보를 마스킹하되 user_id, order_number, traceId 같은 시스템 식별자는 추적 가능하도록 남기는 개인정보 표시 정책을 정리했습니다.

엔지니어링 관점

  • Agent Router는 단순 메뉴 선택 기능이 아니라, 사내 사용자가 “무엇을 물어봐야 하는지”보다 “어떤 문제를 해결하고 싶은지”만 말해도 맞는 업무 에이전트로 보내주는 진입점으로 설계했습니다. 명확한 의도는 자동 라우팅하고, 애매한 경우에는 사용자가 직접 에이전트를 선택할 수 있게 했습니다.
  • `Voltbot Crew`는 직접 답변하는 에이전트가 아니라 라우터 역할을 맡도록 분리했습니다. 실제 답변은 선택된 전문 에이전트가 담당하므로, 기존 도구 권한과 시스템 프롬프트 경계를 유지하면서 진입점만 단순화할 수 있었습니다.
  • 이 프로젝트는 “답변을 잘하는 챗봇”보다 “업무 도구를 호출해 근거를 남기는 에이전트”가 중요했습니다. 그래서 최종 답변보다 먼저 Tool 실행 내역, 근거 로그, 가이드/코드 근거가 확인 가능한 형태로 남도록 설계했습니다.
  • 로그 분석은 한 키로 전체 흐름을 잇기 어렵기 때문에 traceId에만 의존하지 않고 user_id와 order_number 관점을 병행했습니다. `payment-service`, `order-service`, `mobile-gateway`처럼 여러 서비스 경계나 비동기 worker에서 trace가 끊겨도 다른 상관키로 인과 사슬을 이어갈 수 있게 한 점이 핵심입니다.
  • 여러 에이전트를 같은 채팅 표면에 올릴 때 권한, 개발 중 상태, 승인 대기, 사용량 같은 운영 상태를 제품 UI에 드러내야 한다고 봤습니다.
  • 운영 VoC 진단에서는 질문에 따라 코드 정책, 로그, BigQuery 조회 중 필요한 에이전트를 고르고, 각 에이전트의 결과를 같은 컨텍스트에서 비교해 정책상 정상 차단인지 외부 API/PG 오류인지 내부 상태 불일치인지 구분하도록 구성했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

개별 에이전트 자동 라우팅: 질문별 에이전트 선택과 컨텍스트 공유

`Voltbot Crew`가 운영팀의 고객 상황을 받아 권한 있는 에이전트 중 필요한 작업을 고르고, 코드 정책·로그 조회·BigQuery 조회 결과를 같은 컨텍스트에 쌓아 다음 에이전트 판단과 1차 원인 분류까지 이어가는 구현 흐름입니다.

flowchart TD
  Voc["운영팀 VoC<br/>고객 상황 / 시간대 / 식별값"] --> Crew["개별 에이전트 자동 라우팅<br/>요청 의도 기반 연결"]
  Crew --> Auth["권한 있는 에이전트만 후보화"]
  Auth --> Policy["코드 정책 에이전트<br/>정상 조건 / 예외 규칙"]
  Auth --> Log["로그 조회 에이전트<br/>trace / order / user 검색"]
  Auth --> Data["BigQuery 조회 에이전트<br/>집계 / 이력 / 패턴 확인"]
  Policy --> Shared["공유 컨텍스트<br/>정책 / 로그 / 데이터 조회 결과"]
  Log --> Shared
  Data --> Shared
  Shared --> Next["다음 필요 에이전트 판단<br/>컨텍스트 이어받기"]
  Next --> Policy
  Next --> Log
  Next --> Data
  Shared --> Triage{"1차 원인 분류"}
  Triage --> Expected["정상 정책에 의한 차단"]
  Triage --> External["외부 API / PG 오류"]
  Triage --> Internal["내부 상태 불일치"]
  Triage --> Reply["운영팀 답변 초안<br/>개발자 확인 대기 단축"]
  classDef ops fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef ai fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Voc,Crew,Auth ops;
  class Policy,Log,Data,Shared,Next ai;
  class Triage,Expected,External,Internal,Reply result;

Mermaid View

멀티 에이전트를 채팅으로 사용하는 Voltbot 플랫폼 구조

사용자가 채팅으로 요청하면 권한과 에이전트 설정을 확인하고, AgentRunner와 ToolHandler가 로그·가이드·코드 도구를 실행한 뒤 WebSocket으로 실행 내역과 최종 답변을 돌려주는 구조입니다.

flowchart TD
  User["사내 사용자<br/>CS / 운영 / 개발"] --> Chat["Voltbot Web Chat<br/>세션 / 파일첨부 / 공유"]
  Chat --> Auth["Google OAuth + 권한<br/>role/user 기반 agent access"]
  Auth --> Router["Intent-based AgentRouter<br/>요청 의도 분류 / agent 결정"]
  Router --> Select["AgentSelector<br/>수동 선택 / 라우팅 결과 표시"]
  Select --> Runner["AgentRunner<br/>context / quota / interruption"]
  Runner --> Tools["ToolHandler<br/>tool_call / approval / answer"]
  Tools --> Log["GCP 운영 로그 조회"]
  Tools --> Guide["로그 분석 가이드"]
  Tools --> Code["GitHub 코드 근거"]
  Runner --> Stream["WebSocket streaming<br/>tool trail + 최종 답변"]
  Stream --> Ops["진단 / 근거 / 권장 조치"]
  classDef user fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef tool fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class User user;
  class Chat,Auth,Router,Select,Runner,Stream core;
  class Tools,Log,Guide,Code tool;
  class Ops result;

Mermaid View

에이전트 실행 루프: 라우팅, Tool 호출, 재판단

채팅 요청이 들어온 뒤 AgentRouter가 전문 에이전트를 정하고, AgentRunner가 LLM의 다음 행동을 판단하며, ToolHandler 실행 결과를 다시 컨텍스트에 넣어 최종 답변까지 반복하는 루프입니다.

flowchart TD
  Input["사용자 메시지<br/>질문 / 파일 / 세션 컨텍스트"] --> Guard["세션·권한·쿼터 확인"]
  Guard --> Route{"AgentRouter<br/>자동 라우팅 필요?"}
  Route -->|자동| Pick["의도 분류<br/>권한 있는 agent 후보 선택"]
  Route -->|수동| Selected["선택된 전문 에이전트"]
  Pick --> Selected
  Selected --> Runner["AgentRunner<br/>system prompt + history + context"]
  Runner --> Turn{"LLM turn<br/>다음 행동 판단"}
  Turn -->|tool_call| ToolHandler["ToolHandler<br/>schema 검증 / 승인 필요 여부"]
  ToolHandler --> Approval{"사용자 승인 필요?"}
  Approval -->|yes| Wait["승인 대기<br/>WebSocket 상태 전송"]
  Wait -->|approved| Execute["도구 실행<br/>logs / guides / code / files"]
  Approval -->|no| Execute
  Execute --> Append["tool result를<br/>대화 컨텍스트에 추가"]
  Append --> Budget{"컨텍스트 / 토큰 한계?"}
  Budget -->|compress| Summary["요약 컨텍스트 생성"]
  Summary --> Runner
  Budget -->|continue| Runner
  Turn -->|ask_user| Clarify["추가 질문<br/>필요 정보 요청"]
  Clarify --> Input
  Turn -->|final_answer| Answer["최종 답변<br/>진단 / 근거 / 조치"]
  Answer --> Stream["WebSocket streaming<br/>tool trail + 비용 + 답변"]
  Guard --> Block["중단 / 권한 없음 / 쿼터 초과"]
  classDef input fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef core fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef loop fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef stop fill:#fff1f2,stroke:#be123c,stroke-width:2px,color:#0f172a;
  class Input input;
  class Guard,Route,Pick,Selected,Runner core;
  class Turn,ToolHandler,Approval,Wait,Execute,Append,Budget,Summary,Clarify loop;
  class Answer,Stream result;
  class Block stop;

Mermaid View

로그 분석 에이전트: 로그, 가이드, 코드 근거를 묶는 진단 흐름

고객 모드와 운영 모드를 나누고, traceId/user_id/order_number 상관키를 갈아타며 로그를 재조회한 뒤, 가이드와 코드 근거를 결합해 진단과 권장 조치를 만드는 흐름입니다.

flowchart TD
  Ask["운영 질문<br/>결제 실패 / 서비스 에러"] --> Mode{"고객 모드<br/>or 운영 모드"}
  Mode -->|userId 있음| Timeline["user_id 타임라인 조회"]
  Mode -->|패턴 조사| Pattern["서비스·에러 패턴 조회"]
  Timeline --> Signal["에러코드·예외·상관키 추출"]
  Pattern --> Signal
  Signal --> Pivot["다관점 pivot<br/>traceId / user_id / order_number"]
  Pivot --> Flow["서비스 간 연계 플로우<br/>Mermaid sequenceDiagram"]
  Signal --> Guide["로그 분석 가이드 대조"]
  Signal --> Github["GitHub 코드 근거 확인"]
  Flow --> Answer["최종 답변<br/>진단 / 근거 로그 / 권장 조치"]
  Guide --> Answer
  Github --> Answer
  Answer --> Mask["개인정보 마스킹<br/>시스템 식별자는 유지"]
  classDef ask fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef search fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef evidence fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Ask,Mode ask;
  class Timeline,Pattern,Signal,Pivot search;
  class Flow,Guide,Github evidence;
  class Answer,Mask result;

운영 결과

  • 사용자가 에이전트 종류를 미리 알지 못해도 질문 내용만으로 적절한 업무 에이전트에 진입할 수 있게 만들어, 사내 AI 도구의 첫 사용 장벽을 낮췄습니다.
  • 운영/CS가 고객 결제 실패, 서비스 에러, 특정 기간의 장애 패턴을 채팅으로 질의하고, 로그 근거와 권장 조치를 함께 받을 수 있는 업무 흐름을 만들었습니다.
  • Cloud Logging, 사내 가이드, GitHub 코드 검색을 오가던 수동 진단 절차를 에이전트 Tool 흐름으로 묶어 개발자 의존적인 반복 트리아지 비용을 줄일 수 있는 기반을 만들었습니다.
  • 코드 정책, 데이터 분석, 법률, 운영, 로그 분석처럼 성격이 다른 사내 에이전트를 권한에 따라 같은 채팅 UI에서 사용할 수 있는 멀티 에이전트 플랫폼 형태로 정리했습니다.
  • 팀 에이전트 프로토타입에서 최종 `AUTO_ROUTING` 구조까지 검증하며, 다중 에이전트 협업 경험을 Voltbot이라는 하나의 업무 플랫폼 안에 맞는 형태로 정리했습니다.
Project

08

카카오스타일 / 2023.12 - 2024.09

프라이싱 플랫폼: 상품 관리 시스템과 프로모션 서비스

PIM과 프로모션을 분리해 고객 노출 최적가 흐름 설계

상품 관리 시스템(PIM)은 외·내부 상품 매칭, 다이나믹 프라이싱, 쇼핑 카탈로그 Engine Page를 담당하고 프로모션 서비스는 멤버십과 파이널 프라이싱을 담당하도록 경계를 나눠, 프로모션의 최종 혜택가와 외부 상품 값을 함께 비교해 고객에게 노출할 합리적 최적가를 계산하도록 정리한 프로젝트입니다.

이력서 연결 지점

내/외부 동일 상품 매칭

이미지 유사도, same-shop exact match, winner score를 기반으로 비교 가능한 상품군을 안정적으로 만드는 상품 매칭 영역입니다.

이력서 연결 지점

파이널 프라이싱 (각 서비스별 가격 계산 로직 통합 API)

멤버십·쿠폰·프로모션·배송비를 포함한 혜택가를 하나의 파이널 프라이싱 API로 표준화한 영역입니다.

이력서 연결 지점

쇼핑 카탈로그 Engine Page 및 최저가 갱신 (네이버쇼핑 / 유튜브쇼핑)

변경 상품만 추려 Engine Page, 쇼핑 피드 CSV, 동기화 데이터셋을 빠르게 생성하는 쇼핑 연동 영역입니다.

Kotlin Spring Boot AWS Athena

설계 배경

외부 상품 가격, 내부 최적화 점수, 멤버십·쿠폰 혜택처럼 가격 결정 요소가 여러 서비스에 흩어져 있던 상태에서, 운영 정책은 자주 바뀌고 고객에게는 일관된 합리적 최적가를 보여줘야 했기 때문에 PIM과 프로모션의 책임을 나누면서도 한 흐름으로 연결할 구조가 필요했습니다.

강조 포인트

PIM이 외부 상품 값과 프로모션의 파이널 프라이싱 값을 함께 받아 고객에게 보여줄 합리적 최적가를 노출하도록 만든 서비스 경계를 설명하기 좋은 프로젝트입니다.

핵심 구현

  • 상품 관리 시스템의 상품 매칭은 version cache 기준으로 `productId -> matchingId`를 조회하고, 같은 shop의 exact match와 winner score를 묶어 프라이싱 기준 상품군을 정리했습니다.
  • 가격 최적화는 Athena 적용 대상을 읽어 내부/외부 상품을 다시 구성하고, `SUPERIOR / EQUAL = 100`, `UNKNOWN = 50` 규칙으로 price score를 upsert하는 배치 흐름을 운영했습니다.
  • 쇼핑 카탈로그 영역은 상품 업데이트·가격 업데이트 이벤트를 받아 변경 상품만 추려 Engine Page, 쇼핑 피드 CSV, 동기화 데이터셋을 생성하는 공통 경로로 운영했습니다.
  • 프로모션 서비스에서는 멤버십 혜택 조건과 `product / item / order final price` API를 나누고, shipping fee는 `MappedBatchLoader`로 묶어 최종 혜택가를 조합했습니다.

엔지니어링 관점

  • 상품 관리 시스템은 외·내부 상품 매칭, 다이나믹 프라이싱, 쇼핑 카탈로그를 운영하고 프로모션 서비스는 멤버십과 파이널 프라이싱을 담당하도록 경계를 나눠, PIM이 프로모션의 최종 혜택가와 외부 상품 값을 함께 비교해 고객 노출 최적가를 결정하도록 했습니다.
  • 상품 매칭은 version cache와 same-shop exact match 기준을 사용해 비교 가능한 상품군을 먼저 안정화했고, winner score를 함께 노출해 운영 판단 근거도 남겼습니다.
  • 전체 상품을 매번 다시 읽어 변경값을 보내던 흐름 대신, 상품 업데이트와 가격 업데이트 이벤트를 받아 변경 상품만 추려 Engine Page와 네이버 쇼핑이 읽는 CSV·동기화용 데이터셋을 만드는 공통 경로로 바꿔 CPS 2시간 갱신 기준을 맞추고, 같은 구조를 구글 Engine Page(유튜브 쇼핑)에도 빠르게 확장할 수 있게 했습니다.
  • 파이널 프라이싱은 `product / item / order` 경계를 분리하고, 멤버십·쿠폰·프로모션·배송비를 한 응답 안에서 조합하면서도 shipping 조회 비용은 DataLoader로 제어했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

상품 관리 시스템에서 프로모션 서비스까지 이어지는 프라이싱 흐름

PIM은 외·내부 상품 매칭, 다이나믹 프라이싱, 쇼핑 카탈로그를 담당하고 프로모션 서비스는 멤버십과 파이널 프라이싱을 담당한 뒤, PIM이 프로모션의 최종 혜택가와 외부 상품 값을 함께 비교해 고객 노출 최적가를 만드는 구조를 한 장으로 정리했습니다.

flowchart TD
  Req["request<br/>product=421 user=3001 site=KR"] --> Match
  Req --> Member
  subgraph PIMSYS["상품 관리 시스템 (PIM)"]
    direction TD
    Match["외·내부 상품 매칭<br/>matchingId / same-shop / winner score"]
    Optimize["다이나믹 프라이싱<br/>price score / compare set"]
    Catalog["쇼핑 카탈로그 Engine Page<br/>Naver / YouTube feed sync"]
    External["외부 상품 값<br/>lowest price / sync dataset"]
    Match --> Optimize --> Catalog --> External
  end
  subgraph PROMO["프로모션 서비스 (Promotion)"]
    direction TD
    Member["Membership<br/>grade / eligibility"]
    Final["Final Pricing API<br/>coupon / promotion / shipping"]
    Member --> Final
  end
  External --> Expose
  Final --> Expose
  Expose["PIM 고객 노출 가격<br/>promotion final + external price"] --> Resp["response<br/>합리적 최적가 노출"]
  classDef pim fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef promo fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef expose fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Match,Optimize,Catalog,External pim;
  class Member,Final promo;
  class Expose,Resp expose;

운영 결과

  • 상품 관리 시스템이 프로모션의 파이널 프라이싱 값과 외부 상품 값을 함께 받아 고객 노출 최적가를 계산하도록 구조를 정리했습니다.
  • 전체 상품 갱신에 기대면 약 6시간이 걸리던 구조에서, 변경 상품만 이벤트 기반으로 반영하는 경로를 추가해 네이버 쇼핑용 CSV와 동기화 데이터셋을 1시간 이내에 생성할 수 있게 했습니다.
  • 네이버 쇼핑 기준으로 만든 Engine Page·최저가 갱신 구조를 공통화해 구글 Engine Page(유튜브 쇼핑)도 빠르게 반영할 수 있는 확장 기반을 마련했습니다.
  • 상품, 아이템, 주문 단위의 파이널 프라이싱 응답을 표준화해 멤버십·쿠폰·프로모션 혜택가를 여러 지면과 운영 배치에서 같은 계약으로 재사용할 수 있게 했습니다.
  • 상품 관리 시스템 쪽 정책이 바뀌어도 그쪽 입력과 운영 로직만 조정하고, 프로모션 서비스의 사용자 응답 계약은 안정적으로 유지할 수 있게 했습니다.
Project

09

카카오스타일 / 2023.04 - 2023.06

멤버십/마일리지 서비스 이관 및 고도화

레거시 API 응답 동등성 검증 기반 Spring Boot 무중단 이관

리텐션 강화를 위해 멤버십 등급 체계를 재설계하고, 레거시 Node.js 기반 멤버십 서비스를 Spring Boot로 1:1 DB 마이그레이션 및 무중단 이관했습니다. 특히 기존 멤버십 API에서 실제 request·response 셋을 수집해 테스트케이스를 만들고, 이를 Spring 로직에 직접 재주입해 응답 차이를 비교한 뒤 게이트웨이를 점진 전환하는 방식으로 오픈했으며, 월간 등급 산정도 Athena partition source 기반으로 다시 정리했습니다.

Kotlin Spring Boot Spring Batch DGS Framework(GraphQL) JPA MySQL Kafka AWS Athena

참고 화면

Preview

지그재그 멤버십: 등급 혜택 노출 화면

확장된 멤버십 등급 체계와 등급별 혜택이 실제 사용자 화면에서 어떻게 노출되는지 보여주는 예시입니다.

지그재그 멤버십 혜택 화면

지그재그 멤버십: 등급 혜택 노출 화면

확장된 멤버십 등급 체계와 등급별 혜택이 실제 사용자 화면에서 어떻게 노출되는지 보여주는 예시입니다.

설계 배경

기존 Node.js 기반 서비스를 1:1 DB 마이그레이션으로 옮기면서도 실제 사용자 응답이 달라지지 않게 유지해야 했고, 월간 등급 산정 배치가 사용자 수와 월 수가 늘어날수록 더 넓은 범위를 재조회하는 구조가 되지 않도록 막아야 했습니다.

강조 포인트

실제 레거시 API 응답 셋을 수집해 Spring 구현과 동등성 비교를 거친 뒤 점진 오픈한 무중단 이관과, Athena 기반 멤버십 배치 최적화를 함께 설명하기 좋은 프로젝트입니다.

핵심 구현

  • 기존 멤버십 API에서 실제 request·response 셋을 수집하고, query·body·경계 케이스까지 테스트케이스로 정리한 뒤 Spring Boot 구현에 같은 입력을 직접 넣어 응답 차이를 비교했습니다.
  • 멤버십 등급 산정 기간을 3개월에서 6개월로 확대했습니다.
  • 응답 동등성 검증을 통과한 뒤 게이트웨이를 점진적으로 전환해, 사용자 응답을 깨지 않고 Spring Boot 서비스로 무중단 오픈했습니다.
  • Athena의 월별 결제 파티션을 기준일로 조회하고, continuation token으로 페이지 처리한 뒤 월간 결제 스냅샷으로 변환해 배치 입력으로 사용했습니다.
  • 최근 6개월 누적 확정금액으로 등급을 계산하고 결과를 batch upsert했으며, 조회는 최근 월 집합과 월별 RANGE PARTITION으로 필요한 범위만 다루도록 정리했습니다.

엔지니어링 관점

  • 무중단 이관은 기능 추가보다 응답 동등성 확보를 먼저 두고, 실제 레거시 API request·response 셋을 테스트 자산으로 바꿔 Spring 구현에 반복 재주입하는 방식으로 검증했습니다.
  • 월간 배치는 Athena partitioned source에서 필요한 `stamp_date`만 읽고, `queryExecutionId + nextToken`으로 잘게 나눠 가져오도록 구성해 대량 대상도 한 번에 메모리로 끌어오지 않게 했습니다.
  • 월간 결제 스냅샷에 최근 6개월 확정금액과 이번 달 포함 누적값을 분리해 담고, 이를 등급 산정용 누적 컬럼으로 저장해 이후 조회 기준이 데이터 모델에 직접 남도록 했습니다.
  • 배치 쓰기는 JDBC batch insert/update로 묶고, 조회 쪽은 최근 월 집합만 보게 하며 이력 데이터는 월별 RANGE PARTITION으로 관리해 데이터가 쌓여도 필요한 월만 다루게 했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

기존 API 응답 동등성 검증 기반 무중단 이관

기존 멤버십 API에서 request·response 셋을 수집해 테스트케이스로 만들고, Spring Boot 구현에 같은 입력을 재주입해 응답 차이를 비교한 뒤 게이트웨이를 점진 전환하는 무중단 이관 흐름을 보여줍니다.

flowchart TD
  Legacy["기존 멤버십 API<br/>request / response set 수집"] --> Cases["테스트 케이스화<br/>query / body / edge case"]
  Cases --> Replay["Spring Boot 로직에<br/>동일 입력 재주입"]
  Replay --> Compare{"legacy 응답과 동일?"}
  Compare -->|yes| Ready["배포 후보 확정"]
  Compare -->|no| Fix["로직 / serializer diff 수정"]
  Fix --> Replay
  Ready --> Switch["게이트웨이 점진 전환"]
  Switch --> Open["무중단 오픈"]

Mermaid View

월간 누적합과 월간 파티셔닝 기반 멤버십 배치

Athena의 월별 결제 파티션을 기준일로 읽고, 월간 결제 스냅샷을 거쳐 멤버십을 batch upsert한 뒤 최근 월 집합 조회와 월별 이력 파티셔닝으로 범위를 제한하는 흐름을 보여줍니다.

flowchart TD
  Source["Athena source<br/>monthly paid partition<br/>target date=2023-10-08"] --> Reader["paged query<br/>continuation token"]
  Reader --> Paid["monthly payment snapshot<br/>user=421 confirmed=330000<br/>predicted=350000"]
  Paid --> Calc["level calc<br/>최근 6개월 누적합 기준"]
  Calc --> Upsert["membership upsert<br/>dateAppliedYm=202310"]
  Upsert --> Current["current membership data<br/>batch insert / update"]
  Upsert --> Archive["monthly history partition<br/>RANGE(applied month)"]
  Current --> Query["recent Ym lookup<br/>202310, 202309, 202308"]
  Archive --> Query

운영 결과

  • 월간 누적합과 월간 파티셔닝 구조로 DB 부하를 임계치 70%에서 30% 이내로 낮췄습니다.
  • 기존 API request·response 셋 기반 테스트케이스와 Spring 응답 비교를 통과한 뒤 게이트웨이를 점진 전환해 무중단 배포를 성공시켰습니다.
  • Athena partitioned source, page reader, JDBC batch upsert, 최근 월 집합 조회를 조합해 대량 고객 데이터가 누적돼도 월별 등급 산정 성능을 안정적으로 유지했습니다.
Project

10

개인 프로젝트 / 운영 중

Commit Map

자연어 여행 루트를 구조화된 지도 콘텐츠로 바꾸는 작성 플로우 설계

개인 여행 계획을 지인과 공유하려고 만든 지도 기반 여행 계획 서비스입니다. 여행지와 이동 루트를 자연어로 적으면 AI 워크플로우가 일정 초안을 만들고, 이후 Markdown으로 직접 세부 동선을 고도화할 수 있게 구성했습니다.

Astro React Leaflet TypeScript Markdown GitHub Pages Antigravity

참고 화면

Preview

메인 화면: 세계 지도와 여행 계획 카드

국가 필터, 세계 지도, 여행 계획 카드가 한 화면에서 이어지는 구성을 참고 이미지로 정리했습니다.

Commit Map 메인 화면 참고 이미지

메인 화면: 세계 지도와 여행 계획 카드

서비스 보기

국가 필터, 세계 지도, 여행 계획 카드가 한 화면에서 이어지는 구성을 참고 이미지로 정리했습니다.

설계 배경

여행 계획은 메신저 대화, 지도 링크, 메모가 흩어지기 쉬워 공유와 수정이 번거롭고, 처음부터 장소 좌표와 일정 구조를 모두 수작업으로 넣는 것도 비용이 컸습니다.

강조 포인트

취미 프로젝트이지만, 자연어 입력 → 구조화된 초안 → 사람이 고도화하는 워크플로우를 실제 서비스 형태로 풀어본 사례입니다.

핵심 구현

  • Astro + React + Leaflet으로 여행 카드, 상세 지도, 타임라인을 결합한 정적 웹 서비스를 만들고, 장소 타입·순서·방문일 기준으로 동선을 시각화했습니다.
  • 여행 포스트를 Markdown frontmatter와 location 스키마로 관리해, 일정·좌표·노트·링크를 구조화된 데이터로 다루고 추후 수동 수정이 쉬운 형태로 유지했습니다.
  • 프로젝트에 포함한 AI 워크플로우로 여행지와 이동 루트를 자연어로 입력하면 초안 포스트, 장소 후보, 기본 일정 구성을 빠르게 만들고, 이후 제가 직접 세부 계획과 콘텐츠를 고도화하는 플로우로 설계했습니다.
  • GitHub Pages 기반 정적 배포로 운영해 여행 계획 링크를 바로 공유할 수 있게 하고, 특정 여행 포스트를 URL 단위로 바로 전달할 수 있게 구성했습니다.

엔지니어링 관점

  • AI는 완성본을 대신 쓰게 하기보다, 처음 루트를 잡아주는 초안 생성기로 두고 최종 계획의 진실은 Markdown 데이터에 남기도록 설계했습니다.
  • 여행 콘텐츠는 글만 있는 블로그보다 지도, 타임라인, 장소 데이터가 함께 보여야 공유 가치가 높다고 보고, 시각화와 데이터 구조를 한 흐름으로 묶었습니다.
  • 개인 프로젝트여도 운영 부담이 커지지 않도록 정적 배포와 콘텐츠 파일 기반 운영으로 유지비를 낮추고, 필요한 부분만 점진적으로 확장할 수 있게 했습니다.

운영 결과

  • 여행 계획을 메신저 조각 대신 링크 하나로 공유할 수 있는 개인 서비스로 운영하고 있습니다.
  • 여행지와 이동 루트만 적어도 AI 워크플로우가 초안 계획을 빠르게 만들어주고, 이후 직접 세부 동선을 고도화할 수 있는 작성 흐름을 만들었습니다.
  • 지도, 타임라인, 장소 메타데이터를 같은 콘텐츠 모델로 묶어, 여행 기록과 향후 여행 계획을 같은 방식으로 운영할 수 있게 했습니다.
Project

11

LG유플러스 볼트업 / 2025.07 - 현재

차량 등록 / 차량식별자 안전 매핑 / Plug & Charge 인증

차량정보 조회 안정화, 차량 기준 트리 설계, 차량식별자 안전 매핑

차량정보 조회 결과를 `브랜드 > 차량군 > 모델` 기준 트리로 정규화해 차량 등록, 선택 UI, 경고 표시, 대상자 알림의 공통 기준으로 쓰고, 차량 정보와 차량식별자가 서로 다른 시점에 들어와도 안전하게 같은 사용자 차량 컨텍스트로 이어지도록 구성했습니다.

Kotlin Spring Boot JPA Redis 차량정보 조회 API

참고 화면

Preview

차량 관리: 등록 차량과 바로충전 진입 화면

차량 등록 이후 바로충전(PnC) 기능으로 이어지는 사용자 화면 예시로, 차량 컨텍스트와 충전 인증 흐름이 서비스 안에서 어떻게 만나는지 보여줍니다.

VoltUp 차량 등록 및 바로충전 화면

차량 관리: 등록 차량과 바로충전 진입 화면

차량 등록 이후 바로충전(PnC) 기능으로 이어지는 사용자 화면 예시로, 차량 컨텍스트와 충전 인증 흐름이 서비스 안에서 어떻게 만나는지 보여줍니다.

설계 배경

차량번호/소유자명 기반 외부 조회 결과, 사용자가 직접 선택하는 차량 모델, 차량식별자, 브랜드/차량군/모델 대상 경고 공지가 서로 다른 경로로 들어오기 때문에, 차량 기준 데이터를 일관되게 만들면서 중복과 잘못된 자동 연결을 막는 기준이 필요했습니다.

강조 포인트

외부 차량정보 조회 결과를 내부 기준 데이터로 정규화한 뒤, 차량 정보와 차량식별자가 언제 자동 연결되고 언제 수동 선택으로 넘어가야 하는지 설명하기 좋은 프로젝트입니다.

핵심 구현

  • 차량정보 조회 응답의 브랜드명, 차량군, 모델명, 연식, 연료, 대표 이미지를 내부 차량 등록 정보로 변환하고, 소유자명 등 필요한 민감 필드를 마스킹한 원본 응답은 추적용 부가 데이터로 보관했습니다.
  • 차량정보 조회 호출에는 토큰 만료 시 강제 갱신, 활성 인증서 순회, fallback 대상 오류 코드 분리를 적용해 외부 조회 실패가 차량 등록 흐름 전체를 쉽게 막지 않도록 보강했습니다.
  • 차량정보 조회 결과를 `브랜드 > 차량군 > 모델` 기준 트리로 승격하고, 등록 시점에 없는 노드는 조회·생성 흐름에서 자동 보강해 차량 선택, 사용자 차량 등록, 브랜드/차량군/모델 단위 경고·알림이 같은 기준을 공유하도록 구성했습니다.
  • 차량 정보 등록과 PnC(Plug & Charge) 등록 양쪽에서 모두 “매핑 안 된 대상이 정확히 1개인지”를 검사하는 양방향 자동 매핑 규칙을 적용했습니다.
  • 차량 정보는 차량번호, PnC 차량은 차량식별자를 중심으로 따로 저장하고, 충전 인증은 사용자와 인증 태그 확인에 집중하도록 두어 차량번호 컨텍스트는 앱/운영 화면의 매핑 정보에서 이어보게 했습니다.

엔지니어링 관점

  • 차량 트리를 단순 선택값이 아니라 운영 기준 데이터로 두어, `현대 > 전기 SUV > 아이오닉 5` 구조 하나가 사용자 차량 등록, UI 선택, 상위 공지 수집, 하위 대상자 전개를 함께 담당하게 했습니다.
  • 브랜드/차량군/모델 노드는 parent 기준 unique 제약과 분산 락을 함께 사용해, 동시에 같은 차량이 등록되어도 기준 데이터가 중복 생성되지 않도록 설계했습니다.
  • 자동 매핑은 편의 기능이지만 잘못 연결되면 위험하므로, 차량 정보와 PnC 엔티티를 분리 저장하고 “정확히 1개일 때만 연결”하는 보수적 규칙으로 설계했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

차량 기준 트리로 등록·선택·경고·알림을 연결한 구조

차량번호와 소유자명으로 조회한 차량정보 결과를 브랜드, 차량군, 모델 노드로 승격하고, 같은 트리를 사용자 차량 등록과 선택 UI, 특정 브랜드·차량군·모델 공지/경고의 선택 노출 및 대상 사용자 알림 발송 기준으로 재사용하는 흐름입니다. 점선은 조건이 맞을 때만 노출되거나 발송되는 선택 연결을 의미합니다.

flowchart TD
  Lookup["차량정보 조회<br/>차량번호=12가3456 소유자=강**"] --> LookupResult["브랜드명=현대<br/>차량군=전기 SUV<br/>모델명=아이오닉 5"]
  LookupResult --> Normalize["차량 기준 트리<br/>조회 또는 생성"]
  Normalize --> Brand["브랜드 노드<br/>현대"]
  Brand --> Category["차량군 노드<br/>전기 SUV"]
  Category --> ModelNode["모델 노드<br/>아이오닉 5"]
  ModelNode --> Model["차량 모델 기준 데이터<br/>이미지 / 등록 수"]
  Model --> VehicleInfo["사용자 차량 정보<br/>차량번호 + 차량 모델 연결"]
  Brand --> Selection["차량 선택 UI<br/>공통 기준"]
  Category --> Selection
  ModelNode --> Selection
  Notice["운영 공지<br/>선택 노출"] -.-> Brand
  Notice -.-> Category
  Notice -.-> ModelNode
  Warning["차량 경고<br/>주의 노출"] -.-> Brand
  Warning -.-> Category
  Warning -.-> ModelNode
  Brand -.-> Display["앱 차량 상세<br/>조건에 따라 노출"]
  Category -.-> Display
  ModelNode -.-> Display
  Brand -.-> Notify["대상자 알림 발송<br/>조건 충족 시 전개"]
  Category -.-> Notify
  ModelNode -.-> Notify
  classDef notice fill:#fef3c7,stroke:#d97706,stroke-width:2px,color:#78350f;
  classDef warning fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d;
  class Notice notice;
  class Warning warning;

Mermaid View

차량 정보와 차량식별자를 안전하게 자동/수동 매핑하는 흐름

차량 정보 등록과 PnC 등록 어느 쪽이 먼저 들어와도 매핑 안 된 상대가 정확히 1개일 때만 자동 연결하고, 그 외에는 사용자 확인으로 넘기는 흐름입니다.

flowchart TD
  VehicleInfoReg["차량 정보 등록<br/>차량번호=12가3456"] --> CheckA["차량 정보 기준<br/>자동 매핑 시도"]
  PncReg["PnC 등록<br/>차량식별자=VID-7K3Q9M"] --> CheckB["차량식별자 기준<br/>자동 매핑 시도"]
  CheckA --> Match{"매핑 안 된 상대가<br/>정확히 1개?"}
  CheckB --> Match
  Match -->|yes| Link["차량 정보와 식별자 연결<br/>중복 연결 방지"]
  Match -->|no| Manual["사용자 선택으로 전환"]
  Manual --> Confirm["사용자 확인 후 연결"]
  Link --> Auth["충전 인증<br/>사용자 / 인증 태그"]
  Confirm --> Auth
  Auth --> Context["앱/운영 화면<br/>차량 컨텍스트"]
  Auth --> Charge["충전 시작"]

운영 결과

  • 차량정보 조회로 얻은 제조사, 차량군, 상세 모델, 연식, 연료, 이미지를 차량 기준 트리에 연결하고, 같은 트리에서 특정 브랜드·차량군·모델 경고 표시와 대상 사용자 알림 발송까지 처리했습니다.
  • 차량 정보 등록과 PnC 등록 어느 쪽을 먼저 하더라도 조건이 맞으면 자동 매핑하고, 충전 인증은 차량식별자와 사용자 인증 정보를 기준으로 안정적으로 이어지게 했습니다.
Project

12

LG유플러스 볼트업 / 2026.06 - 현재

U+ VIP콕 제휴 쿠폰

U+ VIP콕 쿠폰팩 정책, 외부 멤버십 승인, 발급/보상 흐름 설계

LGU+ 멤버십 VIP/VVIP 고객에게 월 1회 U+ VIP콕 쿠폰을 지급하기 위해 월별 쿠폰팩 정책, 외부 멤버십 승인 연동, 발급/취소 보상, 발급 이력 조회까지 end-to-end로 연결했습니다. 고객 문의 대응을 위한 Admin 보조 발급과 상태 확인 화면은 이 쿠폰 발급 흐름을 운영에서 안전하게 다루기 위한 부가 기능으로 정리했습니다.

Kotlin Spring Boot Spring Batch React TypeScript MySQL JPA QueryDSL Feign Flyway

설계 배경

외부 멤버십 승인과 내부 쿠폰 발급이 하나의 사용자 경험으로 보여야 했지만, 혜택 월 기준 쿠폰팩 사전 등록, 사용자/카드 단위 월 1회 제한, 생년월일 본인확인, 쿠폰 발급 실패 시 외부 승인 취소 보상 기준을 동시에 맞춰야 했습니다. 여기에 결제수단 제한 쿠폰 정책, Admin 보조 발급, 고객 메시지 예약 발송처럼 운영 안정성을 높이는 도구도 함께 정리해야 했습니다.

강조 포인트

U+ VIP콕 제휴 쿠폰을 중심으로, 외부 멤버십 승인과 내부 쿠폰 발급/보상, Admin 운영 보조 기능까지 한 흐름으로 설명하기 좋은 프로젝트입니다.

핵심 구현

  • `promotion-service`에 U+ VIP 쿠폰팩 정책과 혜택 월 기준 활성 기간 중복 제한을 추가하고, 허용 결제수단 정책이 미리보기/조회/사용/Admin 생성까지 같은 의미로 흐르도록 정리했습니다.
  • 사용자 발급 API의 핵심 발급 로직을 U+ VIP 발급 서비스로 분리해 휴대폰번호 카드조회, 회원 생일 검증, 사용자/카드 월 1회 제한, LGU+ 승인, 쿠폰 발급, 실패 시 승인 취소 보상을 한 흐름으로 묶었습니다.
  • LGU+ 응답코드별 진단 로그와 한도 초과 분기를 보강하고, 카드번호/키는 마스킹과 암호화 경계를 지켜 운영 로그에 민감값이 남지 않도록 했습니다.
  • Admin API에는 회원 상세에서 휴대폰번호로 카드조회 후 혜택 발급을 보조하는 경로, 본인확인 불일치 사전 점검, 발급/쿠폰 매핑 이력 조회를 추가했습니다.
  • Admin UI에는 회원 상세 U+ VIP콕 보조 발급 패널, 발급/쿠폰 매핑 이력 페이지, U+ VIP 쿠폰팩 생성 옵션, 혜택 월 자동 입력, 정액 할인 최소사용금액 보정, 허용 결제수단 멀티셀렉을 구현했습니다.
  • 고객 대상 문자/푸시/알림톡 1회 발송 어드민을 만들고, 즉시/예약 발송이 같은 발송 기록을 기준으로 처리되도록 예약 디스패치 배치와 발송 이력 조회를 구성했습니다.

엔지니어링 관점

  • U+ VIP콕은 단순 쿠폰 타입 추가가 아니라 외부 승인 상태와 내부 쿠폰 상태를 맞추는 문제였습니다. 그래서 사전 검증을 통과한 뒤에만 LGU+ 승인을 호출하고, 내부 쿠폰 발급 실패 시 외부 승인 취소를 보상 트랜잭션처럼 붙였습니다.
  • Admin 보조 발급은 규칙 우회가 아니라 운영자가 같은 검증 기준을 더 빠르게 실행하는 화면으로 봤습니다. 회원 상세 패널과 이력 조회를 붙여 고객 문의의 현재 상태, 실패 원인, 재시도 가능성을 한 자리에서 확인하게 했습니다.
  • 쿠폰팩 생성 정책은 화면 조건으로 흩어두지 않고 promotion-service 도메인 정책으로 유지했습니다. Admin은 그 정책을 입력하고 확인하는 표면이고, 사용자 발급/조회/사용은 같은 정책 값을 읽는 구조로 맞췄습니다.
  • 고객 메시지 발송은 캠페인 요청마다 별도 코드를 만드는 방식 대신 발송 요청 자체를 데이터로 남기고, 즉시 발송과 예약 디스패치가 같은 발송 원장을 공유하도록 설계했습니다.

Mermaid로 보는 핵심 구조

Mermaid View

U+ VIP콕: 외부 멤버십 승인과 내부 쿠폰 발급을 잇는 운영 흐름

Admin 쿠폰팩 사전 등록부터 휴대폰번호 카드조회, 본인확인, LGU+ 승인, promotion-service 발급, 실패 보상, 발급 이력 조회까지 U+ VIP콕 쿠폰 흐름을 한 장으로 정리했습니다.

flowchart TD
  Pack["Admin 쿠폰팩 사전 등록<br/>U+ VIP 혜택 월 + 사용 기간"] --> Policy["promotion-service 정책<br/>결제수단 제한<br/>동일 월 활성 기간 중복 차단"]
  Policy --> UserFlow["고객 발급 플로우<br/>휴대폰번호 + 생년월일<br/>카드 조회"]
  Policy --> AdminFlow["Admin 보조 발급<br/>회원 상세 패널<br/>휴대폰번호 카드조회"]
  UserFlow --> Guard["사전 검증<br/>회원 생일 일치<br/>유저 월 1회<br/>카드 월 1회"]
  AdminFlow --> Guard
  Guard --> Approve["외부 멤버십 게이트웨이<br/>승인 요청"]
  Approve --> Issue["promotion-service 발급<br/>U+ VIP coupon"]
  Issue --> History["발급/쿠폰 매핑 이력<br/>Admin 목록 조회"]
  Issue --> Fail{"쿠폰 발급 실패?"}
  Fail -->|yes| Cancel["LGU+ 승인 취소 보상<br/>응답코드 진단 로그"]
  Fail -->|no| Complete["고객 혜택 지급 완료"]
  History --> Ops["고객 문의 확인<br/>개발자 수동 조회 의존 감소"]
  classDef policy fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef flow fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Pack,Policy policy;
  class UserFlow,AdminFlow,Guard,Approve,Issue,History flow;
  class Cancel,Complete,Ops result;

Mermaid View

고객 메시지 발송 어드민: 즉시/예약 발송 원장

문자/푸시/알림톡 발송 요청을 발송 원장으로 남기고 즉시 발송과 예약 디스패치 배치가 같은 데이터를 처리하는 구조를 보여줍니다.

flowchart TD
  Admin["Admin 메시지 발송<br/>문자 / 푸시 / 알림톡"] --> Template["커스텀 템플릿<br/>대상자 + 변수"]
  Template --> Source["발송 원장<br/>즉시 / 예약 동일 기록"]
  Source --> Immediate["즉시 발송<br/>send orchestrator"]
  Source --> Reserved["예약 발송<br/>customerMessageDispatchJob"]
  Reserved --> Dispatch["Dispatch tasklet<br/>발송 가능 시각 조회"]
  Immediate --> Provider["메시지 provider 호출"]
  Dispatch --> Provider
  Provider --> History["발송 이력 / 실패 상태<br/>Admin 조회"]
  History --> Ops["운영 캠페인 대응<br/>반복 개발 요청 감소"]
  classDef admin fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef process fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class Admin,Template admin;
  class Source,Immediate,Reserved,Dispatch,Provider process;
  class History,Ops result;

운영 결과

  • U+ VIP/VVIP 혜택 쿠폰 발급을 고객 화면, 쿠폰 정책, 외부 LGU+ 승인, 실패 보상, 발급 이력까지 end-to-end로 운영 가능한 상태로 연결했습니다.
  • 고객 문의 중 개발자에게 수동 로그/정책 확인을 요청하던 지점을 Admin 조회와 사전 점검 화면으로 옮겨 운영 확인을 빠르게 할 수 있는 기반을 만들었습니다.
  • 결제수단 제한, 쿠폰 미리보기, U+ VIP콕 쿠폰팩 생성, 소프트삭제 후 재발급 제약 같은 프로모션 정책 정합성을 promotion-service와 Admin 양쪽에서 맞췄습니다.
  • 고객 메시지 발송 어드민과 예약 발송 배치를 통해 반복 캠페인/공지성 발송을 개발자 작업 없이 운영팀이 처리할 수 있는 방향으로 확장했습니다.
Project

13

LG유플러스 볼트업 / 2024.10 - 현재

Voltup Workflow: CI 자동화와 DevOps 표준화

조직 공통 PR 자동화 workflow, repo-local 운영 문맥, Vault-local sync, CI/CD 표준화

Voltup Workflow는 개발과 운영 사이에서 반복되던 PR 리뷰, PR 본문 작성, 변경사항 요약, 로컬 환경 셋업, 내부 API 연동, 서비스/앱 배포 작업을 재사용 가능한 workflow와 CI 자동화 도구로 묶은 프로젝트입니다. `/voltup-review` 코드/보안 리뷰와 `/voltup-pr` PR 본문 자동 생성은 저장소별 `project-context`, `review-template`, `docs`를 읽어 repo-local 개발 문맥을 반영하고, `pr-changes-detector`, Vault-local sync, Jenkins/ArgoCD 표준화와 함께 리뷰 품질과 릴리즈 안정성을 높이는 방향으로 정리했습니다.

Vault CLI Gradle Kotlin DSL GitHub Actions Jenkins ArgoCD Workload Identity Firebase CLI LLM GitHub Copilot Claude Code gcloud CLI

Example PR Run

명령으로 시작되는 Voltup Workflow 실행 예시

PR 댓글에 명령을 남기면 GitHub Actions bot이 컨텍스트를 읽고, 변경사항 분석·코드 리뷰·PR 본문 갱신 결과를 같은 타임라인에 남깁니다.

Open

feat(admin): 파트너 정산 관리 API 정리 및 배포 준비

sprint/feature -> deploy/prod · 64 commits · 27 files changed

DV

developer commented

/voltup-review -t security
GH

github-actions Bot

Workflow accepted
Command /voltup-review -t security
Context project-context, review-template, docs
Diff scope 27 files · 3 review clusters
GH

github-actions Bot

pr-changes-detector · comment upsert

PR 변경사항 분석 결과

Flyway 마이그레이션 (4개)

OK
  • V28__settlement_category_index.sql - settlement_config 테이블 category 인덱스 추가
  • V29__partner_ticket_id.sql - support_ticket 테이블에 partner_ticket_id 컬럼 추가
  • V30__notice_pin_flag.sql - admin_notice 상단 고정 플래그 및 복합 인덱스 추가

Admin API 컨트롤러 (16개)

review
  • SettlementCommandController.kt - [POST] /admin/settlements/{settlementId}/retry 제거
  • StoreAdminController.kt - 매장 정산 QR 조회 응답에 채널별 매핑 정보 보강
  • CustomerNoticeController.kt - 고객 공지 템플릿 조회 API 제거 후 알림 브리지로 이전
GH

github-actions Bot

/voltup-review · review posted

AI 코드 리뷰 결과
Critical 0
High 0
Medium 6
Low 1
  • 정산 재시도 endpoint 제거 범위와 caller 영향도를 같은 클러스터에서 검토
  • 매장 QR 응답 보강 로직의 null/empty 케이스를 리뷰 포인트로 표시
  • 운영 영향이 낮은 단순 삭제는 Low priority로 분리

StoreAdminController.kt

채널별 QR 코드가 누락될 경우 관리자 화면 응답의 정산 상태와 매장 매핑 정보가 어긋날 수 있어 fallback 응답을 명시하는 편이 안전합니다.

Security agent loop

Find Context expand Refute
DV

developer commented

/voltup-pr
GH

github-actions Bot

/voltup-pr · marker block update

PR 본문 자동 생성

<!-- voltup-pr:begin -->

Summary

파트너 정산 관리 API 정리와 배포 브랜치 병합 준비

Changes

정산 마이그레이션, Admin API 삭제/보강, 매장 QR 조회 응답 정리

Validation

배포 전 Flyway 적용 순서와 정산 재시도 호출 경로 확인

<!-- voltup-pr:end -->

기존 수기 본문은 보존하고, voltup-pr marker block만 교체했습니다.

설계 배경

MSA가 늘수록 코드 리뷰 기준, 마이크로서비스별 작업 컨벤션, 반복 작업 방식, 로컬 환경값 전달, 내부 API 호출 방식, 배포 절차가 사람마다 달라지기 쉬웠습니다. 개발과 운영이 분리된 상황에서는 이런 drift가 리뷰 누락, 환경 불일치, 배포 실패, 운영자 도구 호출 경계 불명확성으로 이어질 수 있어 공통 workflow와 자동화 체계가 필요했습니다.

강조 포인트

개발/운영 사이에서 반복되는 리뷰, 환경 drift, 배포 실패, 내부 도구 호출 경계를 workflow와 자동화로 줄여 운영 효율과 릴리즈 안정성을 높인 프로젝트입니다.

핵심 구현

  • 조직 공통 workflow 허브에 `/voltup-review` 댓글 트리거형 GitHub Actions reusable workflow를 만들고, LLM과 저장소별 `project-context`, `review-template`, `docs`를 연계해 PR diff를 repo 문맥에 맞춰 자동 코드/보안 리뷰하도록 구성했습니다.
  • `/voltup-review -t security`는 Find, 컨텍스트 확장, Refute 흐름의 보안 에이전트 루프로 구성해 diff 밖 인증/호출/설정 문맥까지 확인하고 오탐을 줄이도록 설계했습니다.
  • `/voltup-pr`은 커밋 로그, 변경 파일, diff를 근거로 PR 본문을 자동 생성/갱신하고, `voltup-pr` 마커 블록만 교체해 수기 본문을 보존하도록 만들었습니다. `pr-changes-detector`는 PR open/push 시 Flyway, 컨트롤러, 엔티티, 권한 변경을 요약 댓글로 upsert하도록 정리했습니다.
  • MSA 저장소에는 `.agent/workflows`, `.github/skills`, `.github/prompts`, `copilot-instructions.md`를 배치해 각 서비스의 작업 컨벤션, API 우선 개발 흐름, 보안 규칙, 반복 운영 작업 형상을 여러 생성형 LLM 도구에서 공유 가능한 repo-local context로 만들었습니다.
  • 루트 `build.gradle.kts`에는 base yaml의 placeholder를 Vault에서 치환해 local config yaml을 생성하는 로직을 넣고, 서비스별 Vault path와 shared dev path를 순차 조회하도록 만들었습니다. Vault CLI 로그인 확인, 비대화형 환경 대응, `gcloud` 계정 기반 DB 사용자명 치환까지 포함해 새 키가 추가돼도 개발자별 local 환경이 자동으로 같은 기준을 유지하도록 했습니다.
  • 앱 도메인 저장소에는 인증서와 앱 설정 필드를 Base64 인코딩해 Vault에 반영하는 민감 인증서 갱신 태스크를 만들어, 민감 파일을 저장소나 메신저로 공유하지 않고도 필요한 개발자가 스스로 갱신할 수 있게 했습니다.
  • 운영자 도구 내부 연동 API가 늘어나는 상황에서 공통 내부 API 클라이언트 패턴과 호출 주체 식별 헤더 규약을 문서화하고 적용해 신뢰 경계를 일관되게 관리했습니다.
  • 배포는 공통 Jenkins shared library 위에서 서비스별 `Jenkinsfile`이 job name으로 API/BATCH/CONSUMER/APP target을 분기하고, Docker build/push 후 ArgoCD 배포로 이어지도록 통일했습니다. Android 앱은 cache, track 선택, 알림까지 같은 패턴으로 자동화했습니다.
  • Android 앱 배포에서 오래 보관되는 인증 키 의존을 줄이고, 배포 로그·Slack 알림·토큰 노출 방지를 보강해 릴리즈 흐름을 안정화했습니다.
  • iOS 배포가 커밋 문구나 외부 의존 문제 때문에 불필요하게 멈추지 않도록 배포 조건과 인증 흐름을 보강했습니다.

엔지니어링 관점

  • AI 도입을 “모델 하나 붙이기”가 아니라 운영 체계 설계 문제로 봤습니다. `/voltup-review`, `/voltup-pr`, `pr-changes-detector`가 각자 다른 PR 운영 문제를 맡되 저장소별 context와 template을 읽도록 만들어, 공통 workflow는 유지하면서 서비스별 운영 문맥은 잃지 않게 했습니다.
  • 로컬 환경 셋업은 “누가 비밀값을 전달하느냐”보다 “Vault와 local 환경을 직접 동기화해 인증된 개발자가 같은 기준의 설정을 자동으로 받게 하자”는 방향으로 풀었습니다. 공개 저장이나 수동 배포 대신 Vault CLI 인증을 전제로 yaml 생성과 인증서 갱신을 자동화해, 키가 늘어나도 개발자 간 동기화가 흐트러지지 않도록 만들었습니다.
  • 배포 파이프라인은 서비스별로 완전히 다르게 두지 않고 job name 기반 target 분기와 shared library 위로 수렴시켜, 운영 절차를 공통화하면서도 앱/백엔드 차이는 target 수준에서만 드러나게 했습니다.
  • 모바일 배포 인증은 오래 보관되는 키 의존을 줄이는 방향으로 정리했고, 배포 실패 원인은 Slack 메시지와 로그에서 바로 추적할 수 있게 했습니다.
  • 반복 작업이 팀 운영 리스크가 된다고 느낀 지점에서는 문서만 남기지 않고 직접 Gradle 태스크, workflow, Jenkins 파이프라인으로 만들어 팀이 바로 쓸 수 있게 바꿨습니다.

Mermaid로 보는 핵심 구조

Mermaid View

Voltup Workflow: /voltup-review, /voltup-pr PR 자동화 루프

PR 댓글에서 시작된 `/voltup-review`와 `/voltup-pr`가 조직 공통 workflow를 호출하고, 저장소별 context와 변경 diff를 함께 읽어 인라인 리뷰, PR 본문, 변경사항 요약을 갱신하는 구조입니다.

flowchart TD
  ReviewComment["PR 댓글<br/>/voltup-review"] --> ReviewWorkflow["AI 코드/보안 리뷰<br/>voltup-review workflow"]
  PrComment["PR 댓글<br/>/voltup-pr"] --> PrWorkflow["PR 본문 자동 생성/갱신<br/>voltup-pr.yml"]
  AutoDetect["PR open/push<br/>pr-changes-detector"] --> Summary["변경사항 요약 댓글<br/>Flyway / controller / entity / auth"]
  ReviewWorkflow --> LLM["LLM 연계 리뷰 엔진<br/>코드/보안 리뷰 자동화"]
  ReviewWorkflow --> RepoCtx["저장소별 context<br/>project-context / review-template / docs"]
  PrWorkflow --> RepoCtx
  ReviewWorkflow --> Diff["PR diff + 변경 파일"]
  PrWorkflow --> Diff
  PrWorkflow --> Marker["voltup-pr marker block<br/>수기 본문 보존"]
  LLM --> ReviewEngine["변경 클러스터 기반 분석<br/>위험 / 누락 / 개선 후보 추출"]
  RepoCtx --> ReviewEngine
  Diff --> ReviewEngine
  ReviewEngine --> ReviewBack["인라인 리뷰 + 전체 요약<br/>위험 / 누락 / 개선 제안"]
  Diff --> PrDoc["PR 설명 문서<br/>요약 / 변경 내용 / 검증 / 리뷰 포인트"]
  Marker --> PrDoc
  ReviewBack --> Human["개발자 검토<br/>최종 판단은 사람"]
  PrDoc --> Human
  Summary --> Human
  Human --> Update["수정 커밋 또는 논의"]
  Update --> ReviewComment
  Update --> PrComment
  classDef trigger fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef workflow fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  classDef context fill:#eef7fb,stroke:#3b556b,stroke-width:2px,color:#0f172a;
  classDef result fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  class ReviewComment,PrComment,AutoDetect trigger;
  class ReviewWorkflow,PrWorkflow,LLM,ReviewEngine workflow;
  class RepoCtx,Diff,Marker context;
  class ReviewBack,PrDoc,Summary,Human,Update result;

Mermaid View

AI 리뷰, Vault-로컬 동기화, 배포 표준화

반복 작업을 발견한 뒤 AI 리뷰 workflow, Vault-local 동기화 기반 yaml 생성, Jenkins/ArgoCD 배포 표준화로 나눈 구조를 한 장에 정리했습니다.

flowchart TD
  Pain["반복 운영 작업<br/>PR 리뷰 / PR 본문 / 로컬 ENV / 배포"] --> Review["voltup-workflow<br/>/voltup-review + /voltup-pr<br/>조직 공통 PR 자동화"]
  Review --> Context["project-context + prompts + skills<br/>repo별 운영 문맥 주입"]
  Pain --> Local["Gradle local-config task<br/>local config yaml"]
  Local --> Vault["Vault CLI login<br/>service path + shared path<br/>secret commit 없음"]
  Local --> IAM["cloud account -><br/>DB 사용자명 자동 치환"]
  Pain --> Internal["internal API client<br/>호출 주체 식별 규약"]
  Pain --> Deploy["Jenkins shared library<br/>job name -> target 분기"]
  Deploy --> Build["docker/app build<br/>cache / track / notifications"]
  Deploy --> Android["Android release<br/>키 관리 부담 축소"]
  Deploy --> IOS["iOS release<br/>불필요한 실패 방지"]
  Build --> Argo["ArgoCD deploy"]
  Android --> Argo
  IOS --> Argo
  classDef ai fill:#fff4db,stroke:#9a6700,stroke-width:2px,color:#0f172a;
  classDef sec fill:#edf9f3,stroke:#2f6f57,stroke-width:2px,color:#0f172a;
  classDef ops fill:#dff2ff,stroke:#0f4c81,stroke-width:2px,color:#0f172a;
  class Review,Context ai;
  class Local,Vault,IAM,Internal sec;
  class Deploy,Build,Android,IOS,Argo ops;

운영 결과

  • 조직 공통 `voltup-workflow`와 마이크로서비스별 repo-local context 체계를 만들어, 신규 저장소나 신규 작업도 같은 PR 리뷰, PR 본문, 변경사항 요약 기준과 운영 컨벤션으로 빠르게 온보딩할 수 있게 했습니다.
  • 민감한 환경값을 저장소에 두지 않으면서도 Vault와 local 환경을 바로 동기화해, 키가 추가될 때도 개발자 간 설정 sync가 자동으로 유지되도록 만들었습니다.
  • Jenkins shared library와 ArgoCD 중심 배포 패턴으로 서비스/앱 배포 절차를 단순화하고 수작업 분기를 줄였습니다.
  • Admin internal API와 모바일 배포 인증 방식을 표준화해 운영자 도구 확장과 앱 릴리즈에서 반복되는 보안/운영 리스크를 줄였습니다.