Portfolio
Table of Contents
Introduction #
For nearly five years, I have built operations tools and customer-facing services across gaming, finance, AI, and advertising, working end to end from API backends to operator interfaces. For a live-game administration platform, I brought authorization, large-volume data access, and scheduled operations into a single tool. In advertising, I automated a media integration workflow that had required operations and engineering teams to move repeatedly between an external console and an internal database. These experiences taught me that services often break down not because the code is wrong, but because a handoff between people and systems fails.
Three themes recur throughout my work. First, I connect workflows whose participants and editable states change over time into a coherent state model. Second, when an external API and an internal database cannot share a transaction, I define partial-failure handling, compensation, and the exact point where a person must take over. Third, I investigate incidents using database, network, and infrastructure signals alongside application data.
Before adopting a new technology, I run a proof of concept to determine whether it fits the current environment. I document the reasoning, verification results, and rollback criteria so that the next person can continue with the same context. More recently, I have used AI agents not merely as code generators, but as part of a development system that combines lightweight shared rules, repository-specific context, branch work logs, and human review.
My primary stack is Node.js and TypeScript, and I have also designed and operated services with Java/Spring and Python. I want to help build long-running live-service web applications and operations tools where features, operational context, and recovery boundaries are designed together, and to move further into system design and scalability decisions rather than feature delivery and maintenance alone.
Tech Stack #
Rather than listing every technology in a single row, I group them by depth of experience. Technologies I have designed with and debugged in production should read differently from ones I have only used.
| Level | Technologies |
|---|---|
| Core · 3+ years, production design and troubleshooting | TypeScript · JavaScript · Node.js · NestJS · Java · Spring · PostgreSQL |
| Working · used on delivered projects | Python · FastAPI · Redis · MariaDB · MikroORM · Kysely · MyBatis · AWS (EC2 · S3 · ELB · Route53 · CloudFront) · Docker · GitHub Actions · GitLab CI/CD |
| Familiar · hands-on exposure | Nuxt.js · Vue.js · React · Next.js · Hono.js · Cloudflare Workers · Flask · BullMQ · InfluxDB · Telegraf · Grafana · Prometheus · Jenkins |
| AI & collaboration tooling | AGENTS-based rules · Git Hooks work logs · agent skills · MCP plugins · dry-run, approval, and verification guardrails |
Career Highlights #
Performance by TBWA #
Platform Developer (Associate Research Engineer) · June 2025 - Present
- Improved and extended a Spring/JSP-based B2C advertising campaign manager across the full lifecycle from campaign creation, payment, and operations review to delivery and conversion tracking, including external media integrations, payments and credits, reports, and web/app tracking interfaces
- Automated the previously manual YouTube Demand Gen handoff between operations and engineering with a FastAPI backend covering resource creation, database linkage, and conversion-tracking setup, with explicit compensation and recovery boundaries for partial failures
- Connected Google Authorized Buyer API data into the B2B campaign manager so operators can review creative approval status and policy violations in place, moved target-file downloads to S3 presigned URLs, and refactored the MyBatis persistence layer
- Designed and built a NestJS and BullMQ advertising-budget alert backend with GitHub Actions deployment automation
- Designed an internal NestJS backend template on a DDD and monorepo structure, documented AI-assisted development guidelines, and developed a GTM agent plugin for the engineering team
ALTAVA Group #
Backend Developer · April 2025 - June 2025
- Developed the Node.js and Cloudflare Serverless backend for an AI-powered 3D fashion platform
- Separated SSE real-time streams from weekly ranking and reward batch jobs, optimized queries with Kysely, and incrementally migrated JavaScript code to TypeScript
- Split weekly design and user ranking batch execution by dev/live environment and added manual-run flags with Slack notifications for finer batch operations control
WEBZEN #
Senior Backend Developer · April 2021 - May 2023
- Developed session and authorization flows, large CSV streaming downloads, and separate scheduler and worker services for in-game mail and notices in a Nuxt.js-based live-game administration platform, with Docker and GitLab CI/CD deployment automation
- Designed and built a server-metrics collection and visualization platform for live games, along with a FastAPI alert backend supporting channel-specific delivery
AlphaBridge #
Backend Developer · March 2020 - March 2021
- Designed and built Flask RESTful APIs for the TangoPick investment-idea platform and developed an Express.js/GraphQL administration server
- Built AWS infrastructure, Jenkins/Docker deployment automation, and Grafana/Prometheus dashboards
Featured Projects #
1. Live Game Operations Administration Platform #
WEBZEN · May 2021 - May 2023 · Nuxt.js operations platform and scheduled-job services
Live-game operations teams repeatedly handle tasks with very different characteristics: querying game-server data, performing role-restricted operations, and scheduling in-game mail and notices. These capabilities had been spread across multiple tools, forcing operators to switch interfaces while leaving service and role boundaries unclear. As the number of services and operators grew, authorization and performance risks grew with them.
Instead of placing every capability in one process, I separated execution boundaries by workload. I implemented service- and role-specific authorization, consolidated authentication and authorization sessions in Redis, divided pages and components as the service expanded, and separated global Vuex state from screen-local state. A Nuxt proxy layer isolated the UI from service-specific backend addresses.
For large CSV exports, I replaced the approach of building the complete file in memory with row-by-row generation and a streaming response, while the frontend triggered the browser’s native download flow. This preserved the operator-facing interface while preventing the server from holding the entire file and request in memory. Based on documented project records, average request time decreased from five seconds to one second, and average memory usage fell by more than 40%.
I also separated scheduled in-game mail and notice delivery into a Node.js scheduler daemon. Requests and execution states were stored in Redis; the scheduler determined when work should run, while workers executed the business logic. Based on the same records, average scheduling time decreased from ten seconds to three seconds. I worked across frontend state management, backend streaming, and batch execution, treating the administration platform as a system for controlling authorization, data volume, and execution timing rather than as a collection of CRUD screens.
2. Diagnosing Intermittent Connectivity in an Overseas Live Game #
WEBZEN · April 2021 - May 2023 · Live-service monitoring platform design and implementation
A game running in an overseas cloud environment experienced intermittent connection drops. The issue could not be reproduced consistently and did not cluster around a particular request, so application logs alone could not identify the failing segment. Rather than continuing to chase a one-off reproduction, I separated application and infrastructure behavior through system metrics.
I compared candidate stacks through proofs of concept and selected Telegraf, InfluxDB, and Grafana based on feedback from the server engineers who would operate the system. Instead of deploying every collection agent centrally, I provided Windows and Linux agent builds with installation and operations guides, enabling server owners to deploy them independently under a shared collection standard. Docker-based logical service units supported country-specific installations, while Grafana image rendering and an alert backend routed contextual alerts by channel.
The collected metrics revealed intermittent network-layer latency on the database server and narrowed the cause from the application to a cloud-provider segment. The data gave game operations and engineering teams evidence for an infrastructure change. After the provider was changed, the same dashboards confirmed that the issue no longer occurred.
3. B2C Advertising Campaign Manager Improvements #
Performance by TBWA · June 2025 - Present · Spring/JSP-based advertising campaign manager
An advertisement moves through creation, payment, operations review, approval, delivery, and conversion tracking before it produces measurable results. Each stage has a different owner and a different point after which data can no longer be changed. When screens, database tables, and integrations model these states separately, the advertiser and operations interfaces can disagree about the same campaign, and downstream work has no reliable starting condition.
Campaign approval requires human judgment, so I did not try to remove it. Instead, I separated stages that require an operator’s decision from transitions the system must carry consistently. Inputs remain editable and are validated step by step during campaign creation; after payment, approval records and history drive the remaining state transitions. This reorganized the logic around the campaign lifecycle rather than around individual screens.
I restructured the campaign form into create and edit modes with step-based input for category, media and channel, region, schedule, budget, brand details, creative assets, and links. Payment behavior was divided by payment type, credit and reserved-credit calculation, balance top-up, administrator queries, and callbacks. Successful payment creates the creative-approval mapping and connects review requests, approval, rejection, and resubmission history to the operations interface.
External media approval status and policy violations were brought into the campaign manager through external APIs so operators no longer had to open a separate console. After delivery, I built conversion dashboards and web/app tracking-pixel management, and added reusable spreadsheet generation with multiple sheets and two-level headers so on-screen reports and exports share the same criteria. Because the service is largely legacy code, I improved the state model and structure within the areas touched rather than attempting a full rewrite.
4. YouTube Demand Gen Media Integration Automation #
Performance by TBWA · February 2026 - Present · External media integration API
The original YouTube Demand Gen workflow depended on a manual relay. Operations created resources in an external console and handed the resulting IDs to engineering, which then configured internal database records and conversion-tracking data. Any intermediate error required a person to reconcile both systems, and resources also had to be deleted one by one through the console.
Campaign approval remains an operations review and is deliberately outside the automation boundary. The FastAPI integration backend begins only after an operator approves the campaign, YouTube is confirmed as the media channel, and that choice can no longer change.
On the success path, the service creates assets, audiences, budgets, and campaigns in dependency order. Each returned ID feeds the next request and the internal database linkage, and the service creates the conversion-tracking records required for reporting. Because external API resources and the internal database cannot share a transaction, an intermediate failure triggers a compensating transaction that removes campaigns, budgets, audiences, and assets in reverse dependency order.
If compensation also fails, the service does not retry indefinitely. It records a failed state in the database, notifies Slack, and hands recovery to a person using the external console. The resulting workflow connects external resource creation, internal database setup, and conversion tracking while removing the console work, ID handoff, and manual database configuration previously split between operations and engineering.
5. AI Development Guardrails and Transferable Collaboration Context #
Performance by TBWA · December 2025 - Present · AI-assisted development guidelines and plugins
AI coding practices varied by person and project. A single rigid policy would interfere with individual workflows, while no policy at all would leave the reasoning behind agent-generated changes undocumented. I designed lightweight shared guardrails that can work with different agent platforms and plugins without forcing a particular tool.
The shared layer contains tool-independent Git commit and pull-request guidance, while service and repository layers keep project structure and constraints close to the code. Bash and PowerShell installers include recovery and dry-run modes, a JSON-based agent map, Git Hooks installation, and configuration-file management.
While an orchestration agent works in a worktree or feature branch, it records changed files, decision rationale, and, where needed, flow diagrams and rollback guidance in a branch work log. The log is committed with the code, allowing another teammate and that teammate’s agent to understand and extend the work without reconstructing the original context.
I also turned recurring GTM configuration work into an agent plugin for the engineering team. The workflow distinguishes request, scan, input required, plan ready, approval, policy block, apply, verification, and review required states. It advances only after validating approval binding, an allowlist, approved templates, and resource ownership. Changes are reviewed in a dry run and verified after application, while a person confirms the result in Preview and decides whether to deploy. The engineering team currently uses the plugin, and it continues to evolve.
6. Advertising Budget Alert Backend #
Performance by TBWA · August 2025 - February 2026 · NestJS alert service
Advertising stops being delivered once a budget is exhausted, so every minute the operations team spends unaware of that state costs money. The initial design had the scheduler send Slack messages directly, which meant a failed send was simply lost, and a growing number of alerts could delay the scheduler itself.
I separated deciding when to run from performing the work. The scheduler now publishes events, while BullMQ listeners and processors handle delivery with retries and failure logging. Slack message formatting, channel selection, and block composition live in separate modules so that adding a new notification channel does not change the delivery flow.
Budget reports aggregate ad-group and ad-level figures through Redis pipelines, with defensive handling that returns zero when upstream data arrives as NaN or null, keeping unusable values out of operator screens. Because data is split across the ABI and Addirect databases, I configured multiple MikroORM connections with manual RequestContext management. Deployment runs through GitHub Actions with PM2 restarts, SSH access control, and dynamic AWS Security Group changes, removing the manual deployment steps.
7. Internal Backend Standard Template #
Performance by TBWA · January 2026 - March 2026 · NestJS internal template
Every new service restated the same decisions about configuration, logging, exception handling, and response shape. When each project settles on a different structure, engineers moving between services have to learn the structure before they can read the code, and that cost grows with every additional service.
Instead of writing the conventions down, I made them executable. ConfigModule, Fastify, Swagger, MikroORM, Redis, pino logging, and shared response and exception handling ship as one working project, and a pnpm workspace with apps and packages allows the API and worker runtimes to be separated.
The template later grew into a DDD-based single-service reference combining shared-kernel, contracts, messaging, and persistence packages with an ordering bounded context, including an outbox/inbox example for cases that need message consistency with external systems. The README and extension guide are updated alongside the implementation so the team can see both how to use the template as-is and how to extend it.
8. Personal Schedule-Sharing Platform (Wydt) #
Personal project · August 2024 - November 2024 · Full-stack web service with Next.js and React
Turning an initial UI mock-up into a working schedule-sharing service required authenticated user identity, user-specific schedule queries, filtering and tags, consistent UI updates after create, edit, and delete operations, and a metadata path that could describe a public page before a visitor opened it.
I expanded the application in dependency order rather than adding every feature at once: authentication and user lookup first, then schedule queries, filters, and tags, followed by create, edit, and delete interactions. Because remote server data and client-only state such as modals and filters change differently, I separated React Query query logic from Zustand UI state.
I built authentication and an OAuth callback route with Next.js App Router and Supabase. Public pages generate Open Graph metadata through a dedicated API route that retrieves a user summary instead of depending on rendered UI output. I also configured ESLint, Prettier, Jest, and commit-message validation so that formatting, tests, and change history remained consistent as the service grew.
9. Backend for an AI-Powered 3D Fashion Platform #
ALTAVA Group · April 2025 - June 2025 · Cloudflare Serverless platform APIs
The platform enables users to create and share AI-generated 3D fashion designs. Its existing backend was written in JavaScript, and as the service grew, type safety and query performance became increasingly important. Rather than converting the entire system at once, I wrote new code in TypeScript and migrated existing areas incrementally when work touched them.
On Cloudflare Workers and D1, I introduced the Kysely query builder and optimized execution with the D1 Batch API. I developed user and design rankings, session-based point calculation, multilingual database structures and APIs, news-content APIs, and an SSE real-time stream endpoint.
Real-time events and periodic aggregation were separated into different services. A dedicated scheduler handled weekly design and user ranking jobs, with Slack notifications and environment-specific routing for development and production so operators could immediately inspect batch results.
Open Source & Side Projects #
Projects I built and published outside of work. More detail lives under Projects.
| Project | Description | Stack |
|---|---|---|
| beerjoa/skills | An agent skill overlay pack distributed via npx skills add beerjoa/skills, covering PR review, commit authoring, project contribution extraction, and Bruno API collection generation across seven skills | Node.js · TypeScript |
| express-sequelize-ts | Backend boilerplate with Passport JWT authentication, refresh tokens, routing-controllers, TypeDI, and E2E tests | Node.js · TypeScript · Express · Sequelize |
| fastapi-postgresql-boilerplate | FastAPI boilerplate with async database sessions, token authentication, and x-request-id tracing | Python · FastAPI · PostgreSQL |
| udemy-custom-api | Udemy course lookup with cron/axios, Passport/JWT authentication, and discount status APIs | NestJS · MongoDB · TypeScript |
| wydt | Personal schedule-sharing platform | Next.js · Supabase |
| plgd | Personal project showcase | — |
Education #
Hankuk University of Foreign Studies (Yongin) · Information and Communications Engineering · March 2010 - February 2020
Enterprise Data Analysis with AI and Natural Language Processing · May 2019 - November 2019 · 920 hours