All case studies
GAIS — Government Advertising Integrated Support SystemKorea Press Foundation (KPF)Aug 2020 - Dec 2025 (5y 4m)

CI/CD

Automating the Build and Deploy Pipeline

I replaced a fully manual dev-server deploy — build, transfer, restart, all done by hand — with a Jenkins pipeline. Push to GitLab and the build-and-deploy runs on its own.

Deploy time
20 min → 4 min

~80% reduction

Automation scope
3 stages

Checkout · Build · Deploy

Trigger
GitLab Webhook

Branch-based auto-detection

Infrastructure
Docker + Jenkins

Declarative Pipeline

JenkinsGitLabDockerMavenDeclarative PipelineSSH

Problem

15–20 minutes per deploy, zero traceability

Dev-server deployments were entirely manual — build, transfer, restart, all done by hand. A single deploy took 15–20 minutes, during which other developers were stuck waiting, and there was no record anywhere of who deployed what and when. With frequent deploys, a mistyped command or a wrong-environment slip would creep in from time to time, and each one added a rollback-and-redeploy overhead on top.

  • 15–20 min per deploy on average

    Build, transfer, and restart all run manually in sequence

  • No way to trace deploy history

    No record of who deployed what and when

  • Blocking wait time

    Other work would stall while a deploy was in progress

  • Human errors during manual work

    Typos or wrong-environment mix-ups occasionally caused incidents

Approach

A CI/CD pipeline built on Jenkins

Using a Jenkins Declarative Pipeline, I defined the whole build-and-deploy procedure in a single Jenkinsfile. A GitLab Webhook detects pushes to a specific branch, and the Checkout → Build → Deploy stages run in sequence.

Trigger

Developer

git push devtest

GitLab

Webhook dispatch

Jenkins Pipeline

01

Checkout

git clone

02

Build

mvn clean package

03

Deploy

SSH + deploy.sh

Target

Staging Server

WAR deploy + WAS restart

Average time per deploy

min

Before · manual

15 – 20 min

After · automated

4 min

▼ ~80% reduction

Process

Implementation steps

  1. 01

    Run Jenkins in a Docker container

    To avoid messing up the host environment, I ran Jenkins inside a Docker container. Jobs, plugins, and credentials were split out to volumes, so the configuration survives restarts intact.

  2. 02

    Connect the GitLab Webhook

    Push events from the GitLab repository are forwarded to Jenkins, and a regexpFilterExpression filters them so the build only runs for pushes to the devtest branch.

  3. 03

    Write the Jenkinsfile

    I split the pipeline into Checkout → Build → Deploy stages, so if something fails it is immediately obvious which stage stopped. Deploy ships the WAR file over SSH and then runs the server-side deploy.sh to handle the WAS restart — all in one go.

    Jenkinsfile
    groovy
    // Jenkins Declarative Pipeline - GitLab Webhook 연동 자동 배포
    pipeline {
    agent any
     
    triggers {
    GenericTrigger(
    genericVariables: [
    [key: 'BRANCH_NAME', value: '$.ref'],
    [key: 'EVENT_TYPE', value: '$.event_name']
    ],
    regexpFilterExpression: '^refs/heads/devtest push$'
    )
    }
     
    stages {
    stage('Checkout') {
    steps {
    git branch: 'devtest',
    credentialsId: 'gitlab-ssh-key',
    url: 'ssh://git@server/gitlab/goad.git'
    }
    }
     
    stage('Build') {
    steps {
    sh 'mvn clean package -P staging'
    }
    }
     
    stage('Deploy') {
    steps {
    sshPublisher(publishers: [
    sshPublisherDesc(
    configName: 'staging-server',
    transfers: [sshTransfer(
    sourceFiles: 'target/issga.war',
    execCommand: '/home/deploy/deploy.sh'
    )]
    )
    ])
    }
    }
    }
    }
  4. 04

    Surface build status on GitLab commits

    Build results are reported back as GitLab commit statuses, so when you look at a commit in GitLab you can immediately see whether the build succeeded, failed, or is still running.

Outcome

Results and takeaways

  • Deploy time: 20 min → 4 min

    Work that used to take 15–20 minutes manually dropped to around 4 minutes with the automated pipeline.

  • Manual mistakes disappeared

    The kinds of errors that came from manual steps — command typos, wrong-environment mix-ups — are now virtually gone.

  • No more blocking wait time

    Once a push goes out, developers can move straight on to the next task while the pipeline runs.

  • Pipeline beat Freestyle

    I first tried a Freestyle Job, but Pipeline turned out to be far better for traceability, reuse, and extensibility. It really drove home why capturing the procedure as code matters.

MORE

Explore other cases

Badabom

AUTH / SSO

Building an SSO Provider for Partner Sites

Implemented an SSO Provider so external partner sites (e.g., OTT) could sign in with Badabom accounts. Single-use UUID tokens stored in the database support multiple WAS nodes, and CI (Connecting Information) auto-maps accounts across both sides.

View detail

Badabom

DEVOPS / OBSERVABILITY

SSE + Cross-WAS Real-Time Log Viewer

The WAS lived in the Daejeon IDC, but network-segregation policy meant only Busan-office PCs could reach it — so pulling a log effectively meant flying to Busan. I built an SSE-based viewer inside the admin web and added a cross-WAS relay so logs from both WAS nodes stream into a single screen.

View detail

Badabom

LEGACY MIGRATION

Migrating the OTT Technology-Trade System into Badabom

Moved an Oracle + MyBatis technology-trade platform (OTT) onto PostgreSQL + iBATIS. Rewrote 87 URLs, 34 JSPs, 80+ SQL queries, and 14 tables.

View detail

GAIS — Government Advertising Integrated Support System

INFRA / SESSION

Redis-Backed Session Clustering

JEUS Standard doesn't support native session clustering, so I put Redis in front as an external session store. That unlocked rolling restarts across WAS nodes.

View detail

GAIS — Government Advertising Integrated Support System

SECURITY / NETWORK

Applying TLS 1.3 via an Nginx Reverse Proxy

Touching the shared WebtoB SSL felt risky, so I put Nginx in front and terminated TLS there instead. Existing services kept running untouched while TLS 1.3 was rolled out.

View detail

Freelance · Side Projects

CLIENT WORK / WEB

Pitched and Built a Postpartum Care Center Site Renewal

My wife had stayed at a postpartum care center whose website felt dated, so I mocked up a UI sample and pitched it myself. I built an Astro static site with a 192-frame scroll animation, Kakao Map, and SEO — then shipped it to their production domain.

View detail

Freelance · Side Projects

SIDE PROJECT / AI

Family-Driven Baby Naming with AI + Tournament-Style Voting

Existing naming services are designed for solo use, so I built a way for the whole family to join in. GPT-4o suggests names aligned with Saju (birth-chart) and Ohaeng (Five-Element) rules, and the family votes tournament-style to pick the final name.

View detail
CI/CD Pipeline Build-out | Case Study