Selected cases across payments, apps, roaming, internal AI agents, pricing, membership, promotions, DX, and personal products

Project Portfolio

This page highlights projects such as multi-vendor payments, app/WebView bridge work, roaming reliability, internal AI-agent routing and log diagnosis, pricing APIs, membership migration, point-wallet design, DX automation, and Commit Map as a personal product built from a real planning problem.

Projects

13

Primary Stack

Kotlin Spring Boot MySQL

Focus

Architecture, state transitions, operational resilience

01

Backend development experience centered on Kotlin and Spring Boot

02

Experience integrating MSAs with OAuth2, PGs, gateways, and external certificate services

03

Experience connecting U+ VIP partnership coupons, external membership gateways, and Admin tooling

04

Hybrid app and app-validation tooling experience with Flutter/WebView

05

Operational stabilization of public roaming integrations using events, retries, and monthly resync

06

Kept homepage statistics updated under public traffic by caching prior-month cumulative totals in Redis for 24h, upserting daily deltas into cumulative DB state, and protecting same-day sums with Redis/local cache plus distributed locks

07

Operational stabilization with Pub/Sub DLQ, Athena batches, monthly partitions, and circuit-breaker patterns

08

Chat-based AI agents, LLM routing, tool calling, WebSocket streaming, and production-log diagnosis automation

09

DX improvements using LLM review workflows, Docusaurus, Firebase App Distribution, capture/replay extensions, and Jenkins/TestFlight

Project

01

LG Uplus VoltUp / Oct 2024 - Present

Multi-Vendor Payment System and Arrears Recovery

Owned the service from initial design to implementation

Owned the payment service from initial design through implementation, expanding a single payment flow into a modular structure that supports multiple payment vendors and building DLQ-based unpaid-event processing with automatic recovery. Later hardened operational paths such as DLQ retry stuck prevention, PG not-found repayment continuity, and cancellation-message branching.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Cloud SQL

Design Context

The service needed to move beyond a single-payment structure into one that can absorb multiple payment vendors through the same extension point, while also handling unpaid-processing flows.

Key Point

A project for explaining both PG expansion and unpaid-event retry strategy.

Core Implementation

  • Built a PG integration architecture with an abstract-class-based vendor strategy pattern.
  • Implemented a GCP Pub/Sub-based DLQ pattern that isolates failed events and keeps arrears-processing targets traceable.
  • Implemented retries, DLQ, and NACK-based recovery paths so unpaid events could flow into follow-up payment recovery.
  • Hardened FAILOVER transitions and retry timestamp recording to prevent dead-letter retries from getting stuck, while keeping unpaid-payment recovery flows intact on PG not-found responses.
  • Separated AlimTalk contexts for full, partial, and roaming payment cancellations and normalized amount formatting to improve user-facing communication accuracy.

Engineering Lens

  • Centered request orchestration, point hold/confirm, and success/failure transitions around PaymentProcessor so state changes remain traceable in one place.
  • Protected point holds from concurrent payment attempts by keeping both hold and payment-READY creation inside a user-scoped lock.
  • Split PG-network uncertainty into repair, consumer retries, and failover batches so immediate recovery and operator intervention remain clearly separated.
  • Treated retries themselves as observable operations, leaving latestRetriedAt and state transitions behind so operators can tell where a failed event stopped.

Architecture Snapshot

Mermaid View

Multi-vendor orchestration: lock, hold, and state transitions

Keeps request data, lock key, point hold, payment READY creation, internal `PG Vendor` approval steps, and success/failure transitions in one integrated diagram.

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

Multi-vendor system: mandatory contracts and optional extensions

Places `VendorChecker.select()` on top of the `VendorType` extension point, separates contracts required for every vendor from features needed by only some vendors, and uses `@RequiredVendor` plus `VendorRequirementsValidator` to catch missing mandatory implementations at startup.

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;

Operational Outcomes

  • Built a payment architecture that integrates multiple PG vendors behind one interface and remains extensible as new vendors are added.
  • Applied DLQ-based unpaid-event processing and automatic recovery paths in operation.
  • Improved trust in unpaid-payment recovery and user-facing cancellation messages by hardening external PG exception handling and cancellation-message branching.
Project

02

LG Uplus VoltUp / Jul 2025 - Present

KakaoT Account Linking and Payment Method Registration

Unified member identity, linked AuthMethod records, and designed card-registration state around the partner customer token

Designed the flow that links KakaoT external accounts to existing VoltUp members through encrypted CI and carries payment-method registration from the mobile-gateway one-step API through payment-service READY-state and ACTIVE-state transitions. Later promoted the partner customer token as the key between external payment methods and internal user context, stabilizing lookup, unlink validation, and app-callback activate flows.

Resume Link Points

User-side Backend

The resume entry for User-side Backend maps here as the broader area around KakaoT integration, vehicle/PnC (Plug & Charge) flows, and user-facing stability work.

Kotlin Spring Boot OAuth2 Flyway T Partner API

Reference Views

Preview

Linked auth methods: connecting KakaoT under the same member

A screen showing how multiple auth methods were unified under one member, then connected into the KakaoT payment-registration flow.

VoltUp linked auth-method screen

Linked auth methods: connecting KakaoT under the same member

A screen showing how multiple auth methods were unified under one member, then connected into the KakaoT payment-registration flow.

Payment registration: KakaoT, KakaoPay, and card options

A bottom-sheet entry that exposes KakaoT, KakaoPay, and normal card registration in one place to simplify the user choice flow.

VoltUp payment-method registration bottom sheet

Payment registration: KakaoT, KakaoPay, and card options

A bottom-sheet entry that exposes KakaoT, KakaoPay, and normal card registration in one place to simplify the user choice flow.

Design Context

The system had to merge existing VoltUp members with KakaoT users without creating duplicate identities, then keep card-registration sessions and final payment-method state under the same user context.

Key Point

A strong project for explaining both identity unification and payment-method state transitions.

Core Implementation

  • Designed the linking flow that resolves the existing VoltUp member from KakaoT OAuth account data and encrypted CI, then adds the linked auth method.
  • Built the one-step mobile-gateway flow that uses the current user encrypted CI to create the KakaoT payment-link session.
  • In payment-service, implemented the state transition that stores the `session_key` in the payment payload and finalizes `pgPayKey` plus the partner customer token during confirm.
  • Added partner-customer-token lookup filters plus a composite index, and hardened unlink validation so KakaoT teardown checks whether the payment-method record belongs to the current user.
  • Separated the app-callback-specific activate API and added DTO aliases, `@JsonProperty`, and search logs to absorb external schema drift and improve operational traceability.

Engineering Lens

  • Split responsibilities so auth-domain owns external account acquisition, identity-service owns identity resolution and AuthMethod ownership, and payment-service owns payment-method state.
  • Prevented identity mismatches by not starting card registration until account linking is complete, then creating sessions and activate calls only afterward.
  • Grouped the `session_key`, partner customer token, and `pgPayKey` around the same subscription row during the `READY -> ACTIVE` transition so later approve/cancel calls can reuse them.
  • Treated the partner customer token not as a response field but as an operational key for user-to-external-payment consistency, so lookup and unlink validation share the same basis.

Architecture Snapshot

Mermaid View

Card registration after VoltUp-to-KakaoT account linking

Summarizes identity linking, encrypted-CI based account matching, link-session creation, and the READY-state to ACTIVE-state transition in one pass.

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 transition"]
  Ready --> Active["confirm success<br/>ACTIVE transition"]

Operational Outcomes

  • Established the user flow that links existing VoltUp members to KakaoT accounts before continuing into card registration.
  • Made subscriptions stay in the ACTIVE state after registration so approve, cancel, and lookup operations can reuse the same identity context.
  • Kept the current user and payment method matching basis stable across mixed app-callback, web-login, and unlink-validation flows.
Project

03

LG Uplus VoltUp / Jul 2025 - Present

Unified Promotion Platform for Coupons and Points Buildout and Enhancement

Extended promotion-service policy, payment-vendor restrictions, and point-wallet structure

In VoltUp `promotion-service`, handled code issuance, code registration, direct user assignment without codes, and expiry reminder batches based on coupon-pack registration and usage windows, while applying partner-coupon payment-vendor restrictions across issuance, lookup, usage, and Admin creation flows. Separately designed point wallets and redemption order for per-accrual expiration.

Kotlin Spring Boot Spring Batch MySQL JPA QueryDSL JDBC Distributed Lock

Design Context

The project had to support partner-specific promotion requirements while handling both code-based coupon issuance and direct coupon assignment without codes, and some coupon packs needed different allowed payment vendors such as KakaoT, normal cards, or KakaoPay. The point model also had to handle per-accrual expiration reliably.

Key Point

A good project for explaining promotion-service issuance, direct assignment, expiry reminders, and point-wallet redemption structure together.

Core Implementation

  • Managed coupons with separate `registerStartAt/registerEndAt` and `usableStartAt/usableEndAt` windows on `couponPack`, separating registration timing from usage timing.
  • Issued code-based coupons through `batchIssue`, converting Snowflake IDs into 10-character base36 codes after SHA-256 hashing, bulk-saving them first and falling back to individual saves on conflicts.
  • For direct assignment without coupon codes, created coupon rows through `mapping(code=null)` or `batchMapping(userIds)`, while code registration linked the user inside a `coupon-mapping:{code}` lock.
  • Added allowed payment methods as a coupon-pack policy, treating an empty value as allowing all methods to preserve compatibility while applying the same restriction across issuance, lookup, and usage.
  • Added an allowed-payment-vendor multiselect and Encoded ID exposure to the Admin coupon-pack creation flow so operators can verify the policy from creation time.
  • In `addBulk`, created a new `PointWallet` whenever `expiredAt` exists, while merging into the same `type + chargeType` wallet when it does not, so accrual and expiration units stay aligned.
  • When points are used, paged through active wallets and sorted them by `FREE -> earliest expiredAt`, holding across multiple wallets sequentially and restoring only the wallets that participated in the hold on failure.
  • Handled coupon expiry through an expiry reminder batch that reads a `usableEndAt` window and publishes `EXPIRED` events, while point expiration is reflected by the next-month history batch.

Engineering Lens

  • Split coupon responsibilities so `couponPack` owns timing and discount policy while each `coupon` owns user mapping and usage state, allowing both code-based and code-less issuance within the same model.
  • Protected code registration with `coupon-mapping:{code}` locks, usage with `coupon-process:{userId}` locks, and reinforced both with `unique(code)` plus `unique(userId,couponPackId)` constraints to prevent duplicate registration and duplicate issuance.
  • Handled expiry without adding another online coupon state: the batch reads coupon packs by `usableEndAt` and emits `EXPIRED` events only for unused assigned coupons.
  • Promoted payment-vendor restrictions from a UI condition into a coupon-pack domain policy, so policies created in Admin carry the same meaning through user issuance, lookup, and usage.
  • Split points into `PointWallets` entities instead of one balance, creating new wallets for expiring accruals and merging non-expiring ones into the same `type + chargeType` wallet so expiration rules are visible in the data model.
  • During usage, distributed holds across active wallets in `FREE -> expiredAt asc` order, and made `releaseHold(order)` restore only the actually held `pointWalletId`s so deduction and recovery follow the same sequence.

Architecture Snapshot

Mermaid View

promotion-service: code issuance, direct assignment, and expiry reminders

Shows the actual `promotion-service` flow from coupon-pack based code issuance and code registration to direct assignment without codes and `usableEndAt`-based expiry reminder batches.

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

Point wallets: partner accrual and expiration-ordered deduction

Separates point handling from coupon flow and shows, with concrete example data, how partner points split into wallets and move through active-wallet scan, ordering, hold, and 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/>new wallet if expiredAt exists<br/>merge by same type+chargeType if null"]
  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/>page query by createdAt asc"]
  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 excluded from active<br/>expiringSoon queried separately"]
  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;

Operational Outcomes

  • Organized code issuance, code registration, direct assignment without codes, and expiry reminder batches under the same `promotion-service` model.
  • Absorbed partner-promotion payment-vendor requirements into coupon-pack policy, reducing the chance that discount policy drifts from payment and settlement conditions.
  • Kept point wallets, expiration-order redemption, and next-month history batching in the same backend so coupons and points can be explained together as one promotion platform.
Project

04

LG Uplus VoltUp / Dec 2024 - Present

VoltUp Hybrid App: WebView Bridge and Native Features

Flutter hybrid launch, JSBridge, and QR/permission/push/forced-update flows

Built the Flutter-based Android/iOS hybrid app for the VoltUp 2.0 launch and designed JSBridge plus core app flows so WebView surfaces can call native capabilities reliably. Continued improving production quality through QR scanning, camera permission, FCM, forced-update handling, and Crashlytics-driven stabilization.

Flutter Dart Kotlin Swift WebView JSBridge ML Kit FCM Crashlytics

Design Context

The app had to ship quickly on Android and iOS while keeping service screens flexible through WebView. At the same time, app-only capabilities such as QR scanning, camera permission, new-window handling, push notifications, and forced updates needed reliable native support.

Key Point

A strong project for explaining rapid user-app delivery together with WebView-native bridge design and app-specific flows such as QR, permissions, push, and updates.

Core Implementation

  • Defined the Flutter hybrid structure and designed the boundary between WebView screens and native capability calls for a two-month Android/iOS launch.
  • Implemented the JSBridge contract that lets the frontend call native features such as new-window handling, external URLs, QR scanning, camera permission, app messages, and forced updates.
  • Built a custom ML Kit-based QR scanner page with responsive scan UI to control the QR recognition experience and reduce dependency on the previous scanner package.
  • Connected Crashlytics for Dart/native error collection and stabilized camera and FCM flows.

Engineering Lens

  • In the hybrid app, WebView owns fast surface iteration while the native layer owns OS permissions and hardware capabilities. I treated JSBridge as the product contract between them and organized callable frontend features into explicit message flows.
  • Because QR scanning and camera permission are key entry points for starting a charge, I focused on controlling device layout, lifecycle, and permission states inside the app UX rather than just wrapping a scanner package.
  • Production stability improvements were driven by Crashlytics signals. I grouped camera exceptions and repeated FCM calls that affected user entry flows, improving reliability around those paths.

Architecture Snapshot

Mermaid View

App bridge between WebView surfaces and native features

Shows how WebView surfaces call native features such as new-window handling, QR scanning, camera permission, FCM, and forced updates through JSBridge, then feed stability improvements through Crashlytics.

flowchart TD
  Web["VoltUp WebView surface"] --> Bridge["JSBridge contract<br/>frontend -> native"]
  Bridge --> Window["new-window / external URL handling"]
  Bridge --> QR["QR scanning<br/>ML Kit custom scanner"]
  Bridge --> Camera["camera permission / lifecycle"]
  Bridge --> Push["FCM push token"]
  Bridge --> Version["forced update / 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 stabilization"]
  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;

Operational Outcomes

  • Launched the 2.0 app quickly across Android and iOS and established an operational contract for WebView surfaces to call native features.
  • Stabilized core app-owned capabilities such as QR scanning, camera permission, push notifications, and forced-update handling in the native layer.
  • Used Crashlytics to track and fix production crashes, continuously improving stability around core app entry points.
Project

05

LG Uplus VoltUp / May 2026 - Present

VoltUp App Validation and Ops Correction Extension

App-free validation, API capture/replay, and Admin-unsupported ops corrections

Reduced validation time by recreating app-dependent flows such as new-window handling, QR scanning, camera permission, and forced-update version branches inside a browser extension instead of requiring a real app build every time. The same capture/replay structure was then extended to single-API operational corrections not directly supported by the Admin UI.

TypeScript Chrome Extension API Replay WebView Debugging

Design Context

App feature validation had high setup cost. Even simple API flows or WebView-app bridge behavior required attaching the app, while operations sometimes had cases such as charge-zone correction where the Admin UI lacked a feature but the issue could be corrected through a single API.

Key Point

A tooling project for speeding up app development and operations response rather than an app feature itself. It is useful for showing a working style of spotting bottlenecks and turning them into small internal tools.

Core Implementation

  • Built Chrome Extension flows that can adjust or recreate app-provided behaviors such as new windows, QR scanning, camera permission, and forced-update version conditions.
  • Implemented API capture plus row-based replay so repeated QA and API-flow checks can be performed quickly without attaching the app.
  • Added variable templates, row parsing, and an executor so single-API correction work beyond the Admin UI can run through Bulk Replay.

Engineering Lens

  • This tool did not start as ops automation alone. It first targeted app-attachment validation delays, then expanded once the same capture/replay structure proved useful for operational corrections.
  • After handling a charge-zone creation issue with a hand-written JS `fetch` script, I turned that pattern into a row-based execution tool the team can reuse instead of writing one-off scripts every time.
  • Because app/admin hosts coexist, I separated host-specific UI to avoid exposing the wrong operation in the wrong context.

Architecture Snapshot

Mermaid View

From app-validation bottlenecks to ops-correction replay

Shows how the extension recreates app-dependent flows without app attachment, then turns captured API requests into row-based replay for both development QA and Admin-unsupported operational corrections.

flowchart TD
  Pain["app-attachment bottleneck<br/>new window / QR / camera / version"] --> Extension["Chrome Extension<br/>app-like controls"]
  Extension --> Sim["recreate app-dependent flows in browser"]
  Extension --> Capture["API request capture"]
  Capture --> Template["row parser<br/>variable template"]
  Template --> Replay["Bulk Replay executor"]
  Replay --> QA["shorter repeated dev QA"]
  Replay --> Ops["single-API ops correction<br/>beyond Admin UI"]
  Ops --> Share["one-off JS fetch -> team tool"]
  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;

Operational Outcomes

  • Reduced waiting and repeated interactions by making app-dependent flows quickly verifiable from the browser without the app.
  • Turned Admin-unsupported single-API correction work from one-off scripts into a repeatable internal tooling procedure.
  • Became a concrete example of spotting bottlenecks and sharing small tools that improve real app development and operations workflows.
Project

06

LG Uplus VoltUp / Feb 2026 - Present

Roaming Reliability: Public-Integration Resync and Retry

Public roaming card-state redesign, API retries, and monthly full resync

Redesigned the Ministry of Climate, Energy and Environment public roaming integration so member-card state does not drift long-term from the external system, moving card-state updates from payment responses to payment-arrears events. Added public API retry handling and a monthly full-resync scheduler so baseline data can recover after missed events or transient failures.

Kotlin Spring Boot Spring Batch GCP Pub/Sub Scheduler

Design Context

Public roaming data needs to stay aligned with the external system, but online events alone cannot reliably recover after missed events or transient failures. Treating baseline-like member-card data and more tolerably lossy charger-status data with the same priority could also waste retry capacity.

Key Point

An operational reliability project that combines online events, retries, and full resync to keep internal state aligned with an external public system over time.

Core Implementation

  • Moved card-state updates from payment-response-driven logic to payment-arrears-event-driven logic, updating state selectively only for arrears cases.
  • Simplified the roaming card-state processing path and consolidated payment-state lookups to reduce transformation and lookup overhead.
  • For public API retries, prioritized baseline-like member-card data and separated more tolerably lossy charger-status data into a lower-priority path.
  • Added a monthly full-resync scheduler and task seed for public roaming member cards so differences missed by online events can be periodically restored.

Engineering Lens

  • Keeping state updates tied to payment responses could make normal payment flows a cause of roaming-state changes, so I narrowed the trigger to arrears events where correction is actually needed.
  • I treated retries as an operational design problem rather than “try everything again.” Member cards recover first because they are baseline data, while charger status is lower priority to control retry cost.
  • The monthly full resync complements online event handling. Instead of trying to eliminate every missed event, it creates a recovery path that prevents long-term drift from accumulating.

Architecture Snapshot

Mermaid View

Event handling, retries, and monthly resync for public roaming data

Shows how payment-arrears-based updates, priority-based public API retries, and monthly full resync work together to reduce long-term drift from the external system.

flowchart TD
  Source["public roaming API<br/>member cards / charger status"] --> Online["online event handling<br/>card state update"]
  Online --> Arrears["payment-arrears events<br/>update only required cases"]
  Source --> Retry["public API error retry<br/>priority by importance"]
  Retry --> Member["member-card retry first<br/>recover baseline data"]
  Retry --> Charger["charger status later<br/>separate tolerable loss"]
  Source --> Monthly["monthly full resync<br/>dynamic scheduler + seed"]
  Monthly --> Baseline["prevent long-term external drift"]
  Arrears --> Stable["operational data accuracy"]
  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;

Operational Outcomes

  • Reduced unnecessary state-change risk and improved data accuracy by redesigning the card-state update basis.
  • Established an operational retry path where important data recovers first after public API errors.
  • Added a monthly full-resync safety net so member-card baseline data realigns with the external system after missed events or transient failures.
Project

07

LG Uplus VoltUp / Jun 2026 - Present

Voltbot: Internal Work-Agent Platform and Log Diagnosis Automation

Designed and implemented the chat-based work-agent platform, automatic specialist-agent routing, and log-diagnosis agent

Voltbot is an internal work platform where employees use specialized agents for code policy, BigQuery data lookup, legal support, operations, and log diagnosis through chat. My core contributions were two connected parts inside that service: first, automatic specialist-agent routing (`Voltbot Crew`/`AgentRouter`), which selects the needed specialist based on question intent and permissions without requiring manual agent selection; second, a shared-context workflow where log lookup, BigQuery lookup, and code-policy findings accumulate in the same conversation context so the next agent can continue from that evidence.

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

Product Preview

Internal work agents in a chat surface

The Agent Router classifies the request first, then the right specialized agent traverses logs, guides, and code.

Role-based access PII masking Token/cost display Session sharing
VOLTBOT
Customer 224514 failed payment yesterday. Can you find the cause?

Agent Router

Auto-routed

Payment failure / evidence request → Log Diagnosis Agent

Permission check · agent availability · session context handoff

Execution Trail

  1. 1 Classify intent and route agent
  2. 2 Search production logs
  3. 3 Read diagnosis guides
  4. 4 Find code evidence
  5. 5 Build linked flow

Diagnosis

The failure is likely a temporary rejection from an external PG during card or bank maintenance.

Evidence Logs

  • svc=payment-service-worker | code=4902
  • order_number=260706170149MSGD
  • Re-query by traceId, user_id, then order_number

Recommended Action

  • Ask the customer to retry after maintenance
  • Check PG schedule if failures continue
  • Mask natural-person data at display time
Usage 61.2K tokens · Context 23.6K · Estimated cost $0.09

Design Context

When customer payment or charging failures arrived, operators could not easily inspect error codes, cross-service correlation keys, or whether a case was expected policy behavior without asking engineers for manual triage. The internal AI tool also needed to expose multiple agents safely through one chat surface and reduce the decision cost of choosing between code policy, log diagnosis, and data analysis.

Key Point

This project groups my contributions inside the single Voltbot service: automatic specialist-agent routing and shared-context operations diagnosis. It is less about replacing internal work with AI and more about turning developer-dependent triage into a productized work tool with permissions, evidence, guides, and code/data exploration.

Core Implementation

  • Designed and implemented `Voltbot Crew` as a single user-facing entry point, where `AgentRouter` automatically assigns a specialist such as log diagnosis, data analysis, legal support, or code policy based on the request, conversation context, agent descriptions, and user permissions.
  • Connected `AgentRunner`, `AgentRouter`, and `ToolHandler` on top of the shared `Agent`/`Tool` contract so sessions, context, permissions, quotas, interruptions, tool calls, and tool results flow through WebSocket responses.
  • First validated a `TEAM` prototype where two agents could share context inside one conversation, then evolved it into a simpler `AUTO_ROUTING` model so users would not need to understand a complex session structure.
  • Split `LogDiagnosisAgent` into customer and operations modes, so it can investigate from time windows, symptoms, and service patterns even without a userId.
  • Implemented `searchUserLogs` for GCP production logs using `services`, `severity`, `excludeIstio`, `range/from/to`, `pageToken`, and raw LQL, with retries for widening ranges or strengthening queries.
  • Connected guide upload/search/edit screens with `listLogDiagnosisGuides` and `readLogDiagnosisGuide`, so operational knowledge can be consulted directly at runtime.
  • Designed the code-policy agent to add expected conditions, state transitions, and error codes; the BigQuery agent to add aggregate, history, and pattern evidence; and the log-diagnosis agent to add runtime logs into the same context. The next agent can then continue from prior evidence to narrow services, time ranges, and correlation keys before classifying a first-pass cause.
  • Added correlation-key pivot rules across traceId, user_id, and order_number, plus answer rules that render a Mermaid sequence diagram when a case spans multiple services.
  • Defined a display policy that masks natural-person identifiers in quoted logs and diagnosis text while keeping system identifiers such as user_id, order_number, and traceId traceable.

Engineering Lens

  • The Agent Router was designed as more than a menu shortcut: it lets internal users describe the problem they want to solve instead of knowing which agent to choose. Clear intent is routed automatically, while ambiguous cases still allow explicit agent selection.
  • `Voltbot Crew` acts as a router rather than a direct answering agent. The selected specialist still owns the actual response, keeping existing tool permissions and system-prompt boundaries intact while simplifying the entry point.
  • The important part was not just a chatbot that answers well, but an agent that calls work tools and leaves evidence. Tool trails, evidence logs, and guide/code basis therefore remain visible before the final answer is trusted.
  • Log diagnosis cannot rely on a single key across the whole system, so I paired traceId with user_id and order_number views. The key point is keeping the causal chain traceable across generic services such as `payment-service`, `order-service`, and `mobile-gateway`, even when traces break at async worker boundaries.
  • When multiple agents share one chat surface, operational states such as permissions, developing status, pending approval, and usage need to be part of the product UI.
  • For ops VoC diagnosis, the system chooses the needed agents among code policy, logs, and BigQuery based on the question, then compares each agent result in the same context to distinguish expected policy blocks, external API or PG failures, and internal state mismatches.

Architecture Snapshot

Mermaid View

Automatic specialist-agent routing: question-based selection and shared context

Shows how `Voltbot Crew` receives customer context, selects the needed work among authorized agents, accumulates code-policy, log, and BigQuery findings into shared context, and continues through next-agent decisions and first-pass triage.

flowchart TD
  Voc["Ops VoC<br/>customer context / time range / identifiers"] --> Crew["automatic specialist-agent routing<br/>intent-based handoff"]
  Crew --> Auth["candidate agents<br/>limited by user permission"]
  Auth --> Policy["Code-policy agent<br/>expected behavior / exception rules"]
  Auth --> Log["Log agent<br/>trace / order / user search"]
  Auth --> Data["BigQuery agent<br/>aggregation / history / pattern checks"]
  Policy --> Shared["shared context<br/>policy / logs / data results"]
  Log --> Shared
  Data --> Shared
  Shared --> Next["decide next needed agent<br/>carry context forward"]
  Next --> Policy
  Next --> Log
  Next --> Data
  Shared --> Triage{"first-pass triage"}
  Triage --> Expected["expected policy block"]
  Triage --> External["external API / PG failure"]
  Triage --> Internal["internal state mismatch"]
  Triage --> Reply["ops response draft<br/>shorter developer wait"]
  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 platform structure for chat-based multi-agent work

Shows how a chat request passes through permissions and agent selection, then AgentRunner and ToolHandler execute log, guide, and code tools before streaming tool trails and the final answer over WebSocket.

flowchart TD
  User["Internal users<br/>CS / operations / engineers"] --> Chat["Voltbot Web Chat<br/>sessions / attachments / sharing"]
  Chat --> Auth["Google OAuth + permissions<br/>role/user based agent access"]
  Auth --> Router["Intent-based AgentRouter<br/>classify request / choose agent"]
  Router --> Select["AgentSelector<br/>manual choice / routing result"]
  Select --> Runner["AgentRunner<br/>context / quota / interruption"]
  Runner --> Tools["ToolHandler<br/>tool_call / approval / answer"]
  Tools --> Log["GCP production-log search"]
  Tools --> Guide["log-diagnosis guides"]
  Tools --> Code["GitHub code evidence"]
  Runner --> Stream["WebSocket streaming<br/>tool trail + final answer"]
  Stream --> Ops["diagnosis / evidence / actions"]
  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

Agent execution loop: routing, tool calls, and next-action decisions

Shows the runtime loop where AgentRouter chooses a specialist, AgentRunner lets the LLM decide the next action, and ToolHandler results are appended back into context until the final answer is ready.

flowchart TD
  Input["User message<br/>question / file / session context"] --> Guard["session, permission,<br/>and quota checks"]
  Guard --> Route{"AgentRouter<br/>auto routing needed?"}
  Route -->|auto| Pick["classify intent<br/>choose authorized agent candidate"]
  Route -->|manual| Selected["selected specialist agent"]
  Pick --> Selected
  Selected --> Runner["AgentRunner<br/>system prompt + history + context"]
  Runner --> Turn{"LLM turn<br/>decide next action"}
  Turn -->|tool_call| ToolHandler["ToolHandler<br/>schema validation / approval check"]
  ToolHandler --> Approval{"user approval needed?"}
  Approval -->|yes| Wait["wait for approval<br/>stream status over WebSocket"]
  Wait -->|approved| Execute["execute tool<br/>logs / guides / code / files"]
  Approval -->|no| Execute
  Execute --> Append["append tool result<br/>to conversation context"]
  Append --> Budget{"context / token limit?"}
  Budget -->|compress| Summary["create compressed context"]
  Summary --> Runner
  Budget -->|continue| Runner
  Turn -->|ask_user| Clarify["ask follow-up<br/>request missing info"]
  Clarify --> Input
  Turn -->|final_answer| Answer["final answer<br/>diagnosis / evidence / action"]
  Answer --> Stream["WebSocket streaming<br/>tool trail + cost + answer"]
  Guard --> Block["interrupted / unauthorized / quota exceeded"]
  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

Log diagnosis agent: joining logs, guides, and code evidence

Splits customer and operations modes, pivots across traceId/user_id/order_number, then combines logs with guide and code evidence to produce diagnosis and recommended actions.

flowchart TD
  Ask["Operations question<br/>payment failure / service error"] --> Mode{"customer mode<br/>or operations mode"}
  Mode -->|userId exists| Timeline["user_id timeline search"]
  Mode -->|pattern search| Pattern["service/error-pattern search"]
  Timeline --> Signal["extract error codes, exceptions, correlation keys"]
  Pattern --> Signal
  Signal --> Pivot["multi-angle pivot<br/>traceId / user_id / order_number"]
  Pivot --> Flow["cross-service flow<br/>Mermaid sequenceDiagram"]
  Signal --> Guide["compare diagnosis guides"]
  Signal --> Github["check GitHub code evidence"]
  Flow --> Answer["final answer<br/>diagnosis / evidence logs / actions"]
  Guide --> Answer
  Github --> Answer
  Answer --> Mask["PII masking<br/>keep system identifiers"]
  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;

Operational Outcomes

  • Lowered the entry barrier for the internal AI tool by letting users reach the right work agent from the question itself, even when they do not know the available agent types in advance.
  • Created a workflow where operations and CS can ask about customer payment failures, service errors, or incident patterns in chat and receive evidence logs plus recommended actions.
  • Turned manual triage across Cloud Logging, internal guides, and GitHub code search into an agent tool flow, creating a foundation for reducing repeated developer-dependent diagnosis work.
  • Organized specialized agents such as code policy, data analysis, legal, operations, and log diagnosis into a permission-aware multi-agent platform.
  • Validated the path from a team-agent prototype to the final `AUTO_ROUTING` model and shaped multi-agent collaboration into a product fit for the single Voltbot work platform.
Project

08

Kakao Style / Dec 2023 - Sep 2024

Pricing Platform: Product Management System and Promotion Service

Designed the customer-facing best-price flow across PIM and Promotion

Split the boundary so PIM owns internal/external product matching, dynamic pricing, and shopping-catalog Engine Pages while Promotion owns membership and Final Pricing, allowing the user-facing best price to be chosen by comparing promotion-calculated benefit prices against external market prices.

Resume Link Points

Internal/External Identical Product Matching

This covers product matching that stabilizes comparable groups through image similarity, same-shop exact matches, and winner scores.

Resume Link Points

Final Pricing (Unified API for Service-Level Pricing Logic)

This covers the Final Pricing API that standardizes membership, coupon, promotion, and shipping-adjusted benefit prices.

Resume Link Points

Shopping Catalog Engine Page & Lowest-Price Updates (Naver Shopping / YouTube Shopping)

This covers shopping integration that generates Engine Page outputs, feed CSVs, and sync datasets from changed items only.

Kotlin Spring Boot AWS Athena

Design Context

Price drivers such as external market prices, internal optimization signals, and membership or coupon benefits were spread across multiple services, while operating policies kept changing. The system needed a structure that separated PIM from Promotion yet still produced a consistent and rational best price for users.

Key Point

A strong project for explaining the boundary where PIM combines external product values with Promotion Final Pricing to expose a rational best price to users.

Core Implementation

  • Used versioned product-matching caches in PIM to resolve `productId -> matchingId`, then grouped exact same-shop matches with winner scores as the basis for pricing comparison.
  • Ran price-optimization batches that read Athena-applied targets, rebuilt internal/external comparison sets, and upserted price scores with rules such as `SUPERIOR / EQUAL = 100` and `UNKNOWN = 50`.
  • Built a shared shopping-catalog path that consumes product and price update events, filters only changed items, and generates Engine Pages, feed CSVs, and sync datasets.
  • Separated membership eligibility and `product / item / order final price` APIs in Promotion, then composed shipping fees through a `MappedBatchLoader` into the final benefit price.

Engineering Lens

  • Split the boundary so PIM owns internal/external matching, dynamic pricing, and shopping catalogs while Promotion owns membership and Final Pricing, allowing PIM to determine the user-facing best price by comparing promotion-calculated benefit prices against external product values.
  • Stabilized comparable product groups first through versioned caches and same-shop exact matching, while exposing winner-score context for operational decisions.
  • Replaced the flow that re-read the entire catalog on every run with a shared path that consumes product and price update events, filters only changed items, and generates the Engine Page plus the Naver Shopping feed CSV and sync dataset, meeting the CPS 2-hour refresh interval and making it quick to extend the same structure to Google Engine Page (YouTube Shopping).
  • Separated final pricing by `product / item / order` boundary and combined membership, coupons, promotions, and shipping in one response while controlling shipping-query cost through DataLoader.

Architecture Snapshot

Mermaid View

Pricing flow from the product-management system to the promotion service

Shows in one diagram how PIM owns internal/external matching, dynamic pricing, and shopping catalogs while Promotion owns membership and Final Pricing, then how PIM combines promotion-calculated benefit prices with external product values to expose the best user-facing price.

flowchart TD
  Req["request<br/>product=421 user=3001 site=KR"] --> Match
  Req --> Member
  subgraph PIMSYS["Product Management System (PIM)"]
    direction TD
    Match["Internal/external product matching<br/>matchingId / same-shop / winner score"]
    Optimize["Dynamic pricing<br/>price score / compare set"]
    Catalog["Shopping catalog Engine Page<br/>Naver / YouTube feed sync"]
    External["External product values<br/>lowest price / sync dataset"]
    Match --> Optimize --> Catalog --> External
  end
  subgraph PROMO["Promotion Service"]
    direction TD
    Member["Membership<br/>grade / eligibility"]
    Final["Final Pricing API<br/>coupon / promotion / shipping"]
    Member --> Final
  end
  External --> Expose
  Final --> Expose
  Expose["PIM user-facing price<br/>promotion final + external price"] --> Resp["response<br/>show best reasonable price"]
  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;

Operational Outcomes

  • Structured the system so PIM can combine Promotion Final Pricing values with external product prices to calculate the user-facing best price.
  • Instead of depending on a full-catalog refresh that took about 6 hours, added an event-driven path for changed items so the Naver Shopping feed CSV and sync dataset can be generated within an hour.
  • Commonized the Engine Page and lowest-price update structure built for Naver Shopping so Google Engine Page (YouTube Shopping) could be added quickly on top of the same foundation.
  • Standardized final-pricing responses across product, item, and order boundaries so membership, coupon, and promotion-adjusted prices can be reused under one contract across surfaces and operational batches.
  • Made it possible to change policies inside the product-management system while keeping the user-facing response contract in the promotion service stable.
Project

09

Kakao Style / Apr 2023 - Jun 2023

Membership & Mileage Migration

Migrated the legacy service to Spring Boot through API-response parity verification

Redesigned membership tiers for retention, migrated the legacy Node.js-based membership service to Spring Boot through a 1:1 DB migration with zero downtime, and did the cutover by collecting real request/response sets from the legacy membership API, turning them into test cases, replaying them through the Spring implementation, and comparing output before gradually switching the gateway. The monthly tier calculation was also rebuilt around a partitioned Athena source.

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

Reference Views

Preview

Zigzag membership: tier-benefit screen

A reference view showing how the expanded membership-tier system and tier benefits were exposed in the actual user-facing UI.

Zigzag membership benefit screen

Zigzag membership: tier-benefit screen

A reference view showing how the expanded membership-tier system and tier benefits were exposed in the actual user-facing UI.

Design Context

The project required a 1:1 DB migration from the Node.js legacy service while keeping real user-facing responses unchanged, while also preventing monthly tier batches from widening their scan scope as both users and months accumulated.

Key Point

A strong project for explaining zero-downtime migration through legacy-response parity checks together with Athena-based membership-batch optimization.

Core Implementation

  • Collected real request/response sets from the legacy membership API, organized them into test cases across query, body, and edge conditions, and replayed the same inputs through the Spring Boot implementation to compare output diffs.
  • Expanded the membership tier calculation period from 3 to 6 months.
  • After passing response-parity verification, gradually switched the gateway so the Spring Boot service could be opened without breaking user responses.
  • Queried a monthly payment partition in Athena by target date, paged results with a continuation token, and converted them into monthly payment snapshots as batch input.
  • Calculated tiers from the six-month confirmed cumulative amount, batch-upserted the result, and bounded reads to the needed period with a recent-month set plus monthly range partitioning.

Engineering Lens

  • Treated zero-downtime migration as a response-parity problem first, turning real legacy API request/response sets into reusable test assets that were repeatedly replayed against the Spring implementation.
  • Built the monthly batch to read only the required `stamp_date` from a partitioned Athena source and page through results with `queryExecutionId + nextToken`, avoiding a full in-memory load for large target sets.
  • Stored both six-month confirmed totals and current-month-inclusive totals in a monthly payment snapshot, then persisted them as cumulative fields for tier calculation so later reads could rely on the model directly.
  • Grouped writes into JDBC batch insert/update, limited reads to recent month sets, and managed history data with monthly range partitions so only the needed months are touched as data grows.

Architecture Snapshot

Mermaid View

Zero-downtime migration through legacy API response-parity checks

Shows the zero-downtime migration flow that collects request/response sets from the legacy membership API, replays the same inputs through the Spring Boot implementation, compares output diffs, and then gradually switches the gateway.

flowchart TD
  Legacy["legacy membership API<br/>request / response set capture"] --> Cases["test case conversion<br/>query / body / edge case"]
  Cases --> Replay["replay the same input<br/>into Spring Boot logic"]
  Replay --> Compare{"same as legacy response?"}
  Compare -->|yes| Ready["ready for rollout"]
  Compare -->|no| Fix["fix logic / serializer diff"]
  Fix --> Replay
  Ready --> Switch["gradual gateway switch"]
  Switch --> Open["zero-downtime release"]

Mermaid View

Membership batch with monthly cumulative sums and monthly partitioning

Shows the flow from an Athena monthly payment partition by target date, through monthly payment snapshots and membership batch upserts, into recent-month-scoped lookups and monthly history partitioning.

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/>latest 6-month cumulative sum"]
  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

Operational Outcomes

  • Reduced DB load from a 70% threshold to under 30% through monthly cumulative sums and monthly partitioning.
  • Achieved zero-downtime deployment by passing request/response-set-based parity tests between the legacy API and the Spring implementation, then gradually switching the gateway.
  • Combined a partitioned Athena source, paged reader, JDBC batch upsert, and recent-month-scoped lookups to keep monthly tier-calculation performance stable as customer data accumulated.
Project

10

Personal Project / Ongoing

Commit Map

Designed an authoring flow that turns natural-language travel routes into structured map content

A map-based travel planning service built to share personal itineraries with friends. Natural-language destinations and routes are turned into a rough itinerary draft through an AI workflow, then refined manually in Markdown.

Astro React Leaflet TypeScript Markdown GitHub Pages Antigravity

Reference Views

Preview

Home screen: world map and trip cards

A reference view showing the country filters, world map, and trip-planning cards in one continuous screen.

Reference image of the Commit Map home screen

Home screen: world map and trip cards

Open service

A reference view showing the country filters, world map, and trip-planning cards in one continuous screen.

Design Context

Travel plans tend to scatter across chat messages, map links, and notes, making them hard to share and revise, while manually structuring locations and coordinates from scratch also costs too much effort.

Key Point

Although it started as a hobby project, it became a real product example of natural-language input flowing into a structured draft that a human refines further.

Core Implementation

  • Built a static web service with Astro, React, and Leaflet that combines travel cards, detailed maps, and timelines, visualizing routes by place type, order, and visit date.
  • Managed trip posts through Markdown frontmatter and a location schema so dates, coordinates, notes, and links remain structured and easy to refine manually later.
  • Added an AI workflow so natural-language destinations and routes can quickly produce a draft post, candidate places, and a starter itinerary, after which I refine the plan and content manually.
  • Deployed it statically on GitHub Pages so travel plans can be shared instantly, with each trip post addressable by its own URL.

Engineering Lens

  • Treated AI not as the final planner but as a draft generator for the first route pass, while keeping the final plan data in Markdown.
  • Viewed travel content as more valuable when maps, timelines, and location data are shown together rather than as text-only blog posts, so visualization and data structure were designed as one flow.
  • Kept operating cost low with static deployment and file-based content so the personal project stays lightweight and can grow only where needed.

Operational Outcomes

  • Operates as a personal service where itineraries can be shared through a single link instead of scattered messages.
  • Built an authoring flow where just listing destinations and routes is enough for the AI workflow to produce a starter plan that can later be refined in detail.
  • Unified maps, timelines, and location metadata under one content model so both past trips and future plans can be operated in the same way.
Project

11

LG Uplus VoltUp / Jul 2025 - Present

Vehicle Registration, Vehicle Identifier Safe Mapping, and Plug & Charge Authorization

Designed vehicle information lookup hardening, vehicle reference tree, and safe vehicle identifier mapping

Normalized vehicle information lookup results into a `brand > category > model` reference tree used across registration, selection UI, warning display, and targeted notifications, then safely connected vehicle info and vehicle identifiers into the same user-vehicle context even when they arrive independently.

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

Reference Views

Preview

Vehicle management: registered car and Plug & Charge entry

A user-facing screen that leads from vehicle registration into Plug & Charge, showing how vehicle context and charging authorization meet in the product flow.

VoltUp vehicle registration and Plug & Charge screen

Vehicle management: registered car and Plug & Charge entry

A user-facing screen that leads from vehicle registration into Plug & Charge, showing how vehicle context and charging authorization meet in the product flow.

Design Context

Because external plate-number/owner-name lookup results, user-selected vehicle models, vehicle identifiers, and brand/category/model-level warning notices arrive through different paths, the system needed a consistent vehicle reference model plus conservative deduplication and auto-linking rules.

Key Point

A good project for explaining how external vehicle information lookup results become internal reference data, then when vehicle info and vehicle identifiers should auto-link or fall back to manual choice.

Core Implementation

  • Mapped vehicle information lookup values such as brand name, vehicle category, model name, release year, fuel type, and representative image into internal vehicle-registration data, while storing the original response with sensitive owner-name values masked as trace metadata.
  • Hardened vehicle information lookup calls with forced token refresh on token expiry, active-certificate iteration, and fallback-result-code filtering so external lookup failures do not easily block the full vehicle-registration flow.
  • Promoted vehicle information lookup results into a `brand > category > model` reference tree, automatically filling missing nodes at registration time so vehicle selection, user-vehicle registration, and brand/category/model warning notifications share the same reference.
  • Applied a bidirectional auto-mapping rule that checks whether exactly one unmatched counterpart exists from both the vehicle-info and PnC (Plug & Charge) registration sides.
  • Stored vehicle info around the plate number and PnC vehicles around the vehicle identifier, keeping charging authorization focused on the user and auth tag while plate-number context is preserved through the mapped app/admin vehicle info.

Engineering Lens

  • Treated the vehicle tree as operational reference data rather than a simple selection list, so one `Hyundai > electric SUV > Ioniq 5` structure supports user-vehicle registration, UI selection, ancestor-notice collection, and descendant-user expansion.
  • Combined parent-scoped uniqueness with a distributed lock so concurrent registrations do not create duplicate reference nodes for the same vehicle.
  • Auto-linking is convenient but risky when wrong, so vehicle info and PnC entities were stored separately and linked only under the conservative exactly-one-unmatched-counterpart rule.

Architecture Snapshot

Mermaid View

Vehicle reference tree connecting registration, selection, warnings, and notifications

Shows how vehicle information lookup results from plate number and owner name become brand, category, and model nodes, then the same tree is reused as the shared reference for user-vehicle registration, selection UI, optional brand/category/model notice-warning exposure, and targeted notifications. Dotted edges indicate conditional exposure or delivery.

flowchart TD
  Lookup["vehicle information lookup<br/>plate=12GA3456 owner=K**"] --> LookupResult["brand name=Hyundai<br/>category=electric SUV<br/>model name=Ioniq 5"]
  LookupResult --> Normalize["look up or create<br/>vehicle reference tree"]
  Normalize --> Brand["brand node<br/>Hyundai"]
  Brand --> Category["category node<br/>electric SUV"]
  Category --> ModelNode["model node<br/>Ioniq 5"]
  ModelNode --> Model["vehicle-model reference<br/>image / registration count"]
  Model --> VehicleInfo["user vehicle info<br/>plate + model link"]
  Brand --> Selection["vehicle selection UI<br/>shared reference"]
  Category --> Selection
  ModelNode --> Selection
  Notice["operational notice<br/>optional exposure"] -.-> Brand
  Notice -.-> Category
  Notice -.-> ModelNode
  Warning["vehicle warning<br/>attention exposure"] -.-> Brand
  Warning -.-> Category
  Warning -.-> ModelNode
  Brand -.-> Display["vehicle detail screen<br/>conditionally shown"]
  Category -.-> Display
  ModelNode -.-> Display
  Brand -.-> Notify["targeted notifications<br/>expand when matched"]
  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

Safe auto/manual mapping flow between vehicle info and vehicle identifiers

Shows how either vehicle-info or PnC registration can trigger auto-linking only when exactly one unmatched counterpart exists, with all other cases falling back to user confirmation.

flowchart TD
  VehicleInfoReg["vehicle info registration<br/>plate=12GA3456"] --> CheckA["try auto-link<br/>from vehicle info"]
  PncReg["PnC registration<br/>vehicle identifier=VID-7K3Q9M"] --> CheckB["try auto-link<br/>from vehicle identifier"]
  CheckA --> Match{"exactly one<br/>unlinked counterpart?"}
  CheckB --> Match
  Match -->|yes| Link["link vehicle info<br/>and identifier"]
  Match -->|no| Manual["switch to user selection"]
  Manual --> Confirm["user-confirmed link"]
  Link --> Auth["charging authorization<br/>user / auth tag"]
  Confirm --> Auth
  Auth --> Context["vehicle context<br/>for app/admin"]
  Auth --> Charge["start charging"]

Operational Outcomes

  • Connected manufacturer, vehicle category, detailed model, release year, fuel type, and image data from vehicle information lookup into the vehicle reference tree, then used the same tree for brand/category/model warning display and targeted notification delivery.
  • Enabled auto-mapping from either vehicle-info or PnC registration when conditions match, while keeping charging authorization stable around the vehicle identifier and user auth context.
Project

12

LG Uplus VoltUp / Jun 2026 - Present

U+ VIP Partnership Coupon

Designed U+ VIP coupon-pack policy, external membership approval, and issue/compensation flows

Connected monthly coupon-pack policy, external membership approval, issue/cancel compensation, and issue-history search end to end so LGU+ VIP/VVIP customers can receive a monthly U+ VIP benefit coupon. Admin assisted issue and status-check screens were positioned as supporting features for safely operating that coupon flow.

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

Design Context

External membership approval and internal coupon issuance had to feel like one user flow while satisfying pre-registered benefit-month coupon packs, once-per-user and once-per-card monthly limits, birthday verification, and compensating approval cancellation on coupon failure. The work also included supporting Admin tools for payment-vendor-restricted coupons, assisted issue, and scheduled customer messages.

Key Point

A strong project for explaining U+ VIP partnership coupons through external membership approval, internal coupon issue/compensation, and supporting Admin operations tooling.

Core Implementation

  • Added the U+ VIP coupon-pack policy to `promotion-service`, blocked overlapping active packs within the same benefit month, and kept allowed-payment-method rules consistent through preview, search, use, and Admin creation.
  • Extracted core issuing logic into a U+ VIP issue service, tying phone-based card lookup, member birthday verification, once-per-user/card monthly limits, LGU+ approval, coupon issue, and compensating approval cancellation into one flow.
  • Added diagnostics for LGU+ response codes and limit-exceeded branches while keeping card numbers and keys behind masking and encryption boundaries so sensitive values do not leak into operational logs.
  • Added Admin APIs for phone-based card lookup and assisted benefit issue from member detail, pre-checks for identity mismatches, and searchable issue/coupon mapping history.
  • Implemented the member-detail U+ VIP assisted-issue panel, issue/coupon mapping history page, U+ VIP coupon-pack option, benefit-month autofill, fixed-discount minimum-amount correction, and allowed-payment-method multiselect in Admin UI.
  • Built a one-time customer SMS/push/AlimTalk Admin tool so immediate and scheduled sends are processed from the same send record, backed by a scheduled dispatch batch and send-history search.

Engineering Lens

  • U+ VIP benefits were not just another coupon type; they were a state-alignment problem between external approval and internal coupon issuance. I called LGU+ approval only after local pre-checks and attached compensating cancellation when internal coupon issuance failed.
  • I treated Admin assisted issue as a faster execution path for the same rules, not as a bypass. The member-detail panel and history search let operations see current status, failure causes, and retry feasibility in one place.
  • Coupon-pack creation rules stayed as promotion-service domain policy instead of scattered frontend conditions. Admin became the surface for entering and reviewing policy, while user issue/search/use flows read the same policy values.
  • For customer messages, I stored send requests as data instead of creating bespoke code for each campaign, letting immediate sends and scheduled dispatches share the same send ledger.

Architecture Snapshot

Mermaid View

U+ VIP: operational flow connecting external membership approval and internal coupon issue

Shows the U+ VIP coupon flow from Admin coupon-pack pre-registration through phone-based card lookup, identity check, LGU+ approval, promotion-service issue, failure compensation, and issue-history search.

flowchart TD
  Pack["Admin pre-registers coupon pack<br/>U+ VIP benefit month + usable window"] --> Policy["promotion-service policy<br/>payment-method restriction<br/>no overlapping active pack in same month"]
  Policy --> UserFlow["Customer issue flow<br/>phone + birthday<br/>card lookup"]
  Policy --> AdminFlow["Admin assisted issue<br/>member detail panel<br/>phone-based card lookup"]
  UserFlow --> Guard["Pre-checks<br/>member birthday match<br/>once per user per month<br/>once per card per month"]
  AdminFlow --> Guard
  Guard --> Approve["external membership gateway<br/>approval request"]
  Approve --> Issue["promotion-service issue<br/>U+ VIP coupon"]
  Issue --> History["Issue/coupon mapping history<br/>Admin list search"]
  Issue --> Fail{"Coupon issue failed?"}
  Fail -->|yes| Cancel["Compensating LGU+ cancel<br/>response-code diagnostics"]
  Fail -->|no| Complete["Customer benefit granted"]
  History --> Ops["Customer inquiry check<br/>less developer manual lookup"]
  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

Customer-message Admin: send ledger for immediate and scheduled sends

Shows how SMS, push, and AlimTalk send requests are stored in a send ledger and processed by both immediate sends and scheduled dispatch batches.

flowchart TD
  Admin["Admin message send<br/>SMS / push / AlimTalk"] --> Template["Custom template<br/>targets + variables"]
  Template --> Source["Send ledger<br/>same record for immediate/scheduled"]
  Source --> Immediate["Immediate send<br/>send orchestrator"]
  Source --> Reserved["Scheduled send<br/>customerMessageDispatchJob"]
  Reserved --> Dispatch["Dispatch tasklet<br/>queries sendable time"]
  Immediate --> Provider["Message provider call"]
  Dispatch --> Provider
  Provider --> History["Send history / failure status<br/>Admin search"]
  History --> Ops["Operations campaign response<br/>fewer repeated dev requests"]
  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;

Operational Outcomes

  • Connected U+ VIP/VVIP benefit issuing end-to-end across customer flow, coupon policy, external LGU+ approval, failure compensation, and issue history.
  • Moved developer-dependent manual log and policy checks into Admin search and pre-check surfaces, creating a foundation for faster customer-inquiry checks.
  • Aligned promotion-policy consistency across promotion-service and Admin for payment-vendor restrictions, coupon preview, U+ VIP coupon-pack creation, and reissue after soft deletion.
  • Extended operations tooling with customer-message Admin sends and scheduled dispatch batches so repeated campaign or notice sends can be handled without developer intervention.
Project

13

LG Uplus VoltUp / Oct 2024 - Present

Voltup Workflow: CI Automation and DevOps Standardization

Org-wide PR automation workflow, repo-local operational context, Vault-local sync, and CI/CD standardization

Voltup Workflow turns repeated work between development and operations, including PR review, PR description writing, change summaries, local environment setup, internal API integration, and service/app delivery, into reusable workflows and CI automation tools. The `/voltup-review` code/security review and `/voltup-pr` PR body generation flows read repo-local `project-context`, `review-template`, and docs, while `pr-changes-detector`, Vault-local sync, and Jenkins/ArgoCD standardization improve review quality and release reliability.

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 starts from PR comments

A developer leaves a command on a PR, then the GitHub Actions bot writes change analysis, code review, and PR-body updates back into the same timeline.

Open

feat(admin): clean up partner settlement APIs and prepare release

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 Change Analysis

Flyway migrations (4)

OK
  • V28__settlement_category_index.sql - Add category index to settlement_config
  • V29__partner_ticket_id.sql - Add partner_ticket_id to support_ticket
  • V30__notice_pin_flag.sql - Add pinned flag and composite index to admin_notice

Admin API controllers (16)

review
  • SettlementCommandController.kt - Remove [POST] /admin/settlements/{settlementId}/retry
  • StoreAdminController.kt - Add channel-level QR mapping to store settlement responses
  • CustomerNoticeController.kt - Move notice-template lookup behind a notification bridge
GH

github-actions Bot

/voltup-review · review posted

AI Code Review Result
Critical 0
High 0
Medium 6
Low 1
  • Reviews settlement retry endpoint removal and caller impact in one cluster
  • Flags null/empty cases in the store QR response enrichment path
  • Separates low-risk deletions into Low priority comments

StoreAdminController.kt

If channel-level QR codes are missing, the admin response can drift from settlement status and store mapping data. Add an explicit fallback response.

Security agent loop

Find Context expand Refute
DV

developer commented

/voltup-pr
GH

github-actions Bot

/voltup-pr · marker block update

PR Body Generated

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

Summary

Clean up partner settlement APIs and prepare release merge

Changes

Settlement migrations, Admin API removals/refinements, store QR response updates

Validation

Check Flyway order and settlement retry caller paths before deployment

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

Manual PR notes are preserved; only the voltup-pr marker block is replaced.

Design Context

As the number of services grew, review rules, microservice-level conventions, recurring task patterns, local secret delivery, internal API invocation, and delivery steps were drifting per person. In a development/operations split, this drift can turn into missed reviews, environment mismatches, release failures, and unclear operator-tool trust boundaries, so the team needed shared workflows and automation.

Key Point

A workflow automation project that improves operational efficiency and release reliability by reducing repeated review work, environment drift, delivery failures, and ambiguity at internal-tool boundaries.

Core Implementation

  • Built a reusable GitHub Actions workflow in a shared workflow hub triggered by `/voltup-review`, connecting an LLM with per-repo `project-context`, `review-template`, and docs so PR diffs are automatically reviewed against repository-specific context.
  • Designed `/voltup-review -t security` as a security-agent loop with Find, context expansion, and Refute stages, so it can inspect authentication, caller, and configuration context outside the diff and reduce false positives.
  • Built `/voltup-pr` to generate and update PR descriptions from commit logs, changed files, and diffs while replacing only the `voltup-pr` marker block to preserve manual content. `pr-changes-detector` upserts PR change summaries for Flyway, controller, entity, and authorization changes on PR open/push.
  • Added `.agent/workflows`, `.github/skills`, `.github/prompts`, and `copilot-instructions.md` to MSA repositories, turning service conventions, API-first development flow, security rules, and recurring operational task shapes into reusable repo-local context for generative LLM tools.
  • Added root `build.gradle.kts` logic that replaces base-yaml placeholders from Vault and generates a local config yaml, checking both service-specific Vault paths and shared dev paths. It also verifies Vault CLI login, handles non-interactive environments, and fills the DB user from the current `gcloud` account so local environments stay automatically aligned across developers even when new keys are added.
  • Added a sensitive-certificate refresh task in an app-domain repository that Base64-encodes certificates and app-config fields into Vault, so developers can refresh sensitive assets themselves without sharing files through the repo or chat.
  • Documented and applied a shared internal API client pattern plus a caller-identity header convention so growing operator-tool integrations keep a consistent trust boundary.
  • Standardized delivery on top of a shared Jenkins library so each service `Jenkinsfile` routes API/BATCH/CONSUMER/APP targets by job name and continues into Docker build/push plus ArgoCD deploy. The Android app pipeline follows the same pattern with cache restore/save, release track selection, and notifications.
  • Reduced long-lived key dependency in Android delivery and improved release stability with clearer logs, Slack notifications, and token-exposure guards.
  • Hardened iOS delivery conditions and authentication flow so releases are not unnecessarily blocked by commit text or external dependency failures.

Engineering Lens

  • Treated AI adoption as an operating-system design problem rather than just attaching a model. `/voltup-review`, `/voltup-pr`, and `pr-changes-detector` each handle a different PR operations problem while reading per-repo context and templates, keeping the workflow shared without losing service-specific operational context.
  • Approached local setup as “sync Vault directly into local environments so authenticated developers receive the same baseline automatically” rather than “who hands secrets out.” By automating yaml generation and certificate refresh behind Vault CLI authentication, the workflow keeps developer environments aligned even as new keys are added.
  • Converged delivery onto a shared-library model with job-name-based target routing so operational steps stay standardized while app/backend differences appear only at the target layer.
  • Reduced long-lived key dependency in mobile delivery authentication and made deployment failures easier to trace from Slack messages and logs.
  • When repetitive work started becoming an operational risk, I did not stop at documentation alone; I turned it into Gradle tasks, reusable workflows, and Jenkins pipelines the team could use immediately.

Architecture Snapshot

Mermaid View

Voltup Workflow: /voltup-review and /voltup-pr PR automation loop

Shows how `/voltup-review` and `/voltup-pr` start from PR comments, invoke org-wide workflows, read repo-local context plus the PR diff, and update inline reviews, PR descriptions, and change summaries.

flowchart TD
  ReviewComment["PR comment<br/>/voltup-review"] --> ReviewWorkflow["AI code/security review<br/>voltup-review workflow"]
  PrComment["PR comment<br/>/voltup-pr"] --> PrWorkflow["PR body generation/update<br/>voltup-pr.yml"]
  AutoDetect["PR open/push<br/>pr-changes-detector"] --> Summary["change summary comment<br/>Flyway / controller / entity / auth"]
  ReviewWorkflow --> LLM["LLM-backed review engine<br/>automated code/security review"]
  ReviewWorkflow --> RepoCtx["repo-local context<br/>project-context / review-template / docs"]
  PrWorkflow --> RepoCtx
  ReviewWorkflow --> Diff["PR diff + changed files"]
  PrWorkflow --> Diff
  PrWorkflow --> Marker["voltup-pr marker block<br/>manual body preserved"]
  LLM --> ReviewEngine["change-cluster analysis<br/>risk / gap / improvement candidates"]
  RepoCtx --> ReviewEngine
  Diff --> ReviewEngine
  ReviewEngine --> ReviewBack["inline review + summary<br/>risks / gaps / suggestions"]
  Diff --> PrDoc["PR description<br/>summary / changes / validation / review points"]
  Marker --> PrDoc
  ReviewBack --> Human["developer review<br/>human owns final judgment"]
  PrDoc --> Human
  Summary --> Human
  Human --> Update["follow-up commit or discussion"]
  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 review, Vault-to-local sync, and deployment standardization

Summarizes how repeated work was turned into three tracks: AI review workflows, Vault-to-local sync based yaml generation, and Jenkins/ArgoCD deployment standardization.

flowchart TD
  Pain["Repeated operational work<br/>PR review / PR docs / local env / deploy"] --> Review["voltup-workflow<br/>/voltup-review + /voltup-pr<br/>org-wide PR automation"]
  Review --> Context["project-context + prompts + skills<br/>repo-specific ops context injected"]
  Pain --> Local["Gradle local-config task<br/>local config yaml"]
  Local --> Vault["Vault CLI login<br/>service path + shared path<br/>no secret commits"]
  Local --> IAM["cloud account -><br/>DB user auto-fill"]
  Pain --> Internal["internal API client<br/>caller identity convention"]
  Pain --> Deploy["Jenkins shared library<br/>job name -> target routing"]
  Deploy --> Build["docker/app build<br/>cache / track / notifications"]
  Deploy --> Android["Android release<br/>less key burden"]
  Deploy --> IOS["iOS release<br/>fewer avoidable failures"]
  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;

Operational Outcomes

  • Established the org-wide `voltup-workflow` plus repo-local context patterns, allowing new repositories and workstreams to onboard under the same PR review, PR description, change-summary, and operational conventions.
  • Kept sensitive values out of the repository while syncing Vault directly into local environments, so developer configs stay aligned automatically even as new keys are introduced.
  • Simplified service and app delivery with Jenkins shared-library plus ArgoCD deployment patterns, reducing manual branching in release operations.
  • Standardized Admin internal APIs and mobile delivery authentication, reducing recurring security and operational risks around operator-tool expansion and app releases.