Backend Developer

가 준 영

Ka Jun Young

Kotlin & Spring 기반의 서버 백엔드 개발자로, 스타트업 환경에서 레거시 시스템을 개선하고 신규 서비스를 구축하는 일을 중심으로 경험을 쌓아왔습니다.

단순히 기존 기능을 이전하는 데 그치지 않고, 복잡하게 얽힌 로직을 구조적으로 단순화하여 유지보수성과 가독성을 높이는 것에 집중했습니다.

서비스의 도메인이 코드로 자연스럽게 드러날 수 있도록 설계하는 것을 중요하게 생각하며, 팀 내 공유를 통해 함께 성장하는 개발 문화를 지향합니다.

가 준 영
PAGE 01 / INTRODUCTION
© 2025CRAFTED FOR EXCELLENCE

Professional Journey

Experience

Backend Focus

Robust Architectures

Mobility42

Mobility42

2023.11 - 2025.10

(1년 11개월)

주임 / AI 기반 자동화 및 백엔드 아키텍처 리딩

2025.04 - 2025.10

• AI 코드 리뷰 시스템 구축: GitLab/n8n/LM Studio 연동으로 Zero-Budget AI 리뷰 환경을 구축하여 리뷰 병목 해결 및 코드 품질 상향 평준화 달성.

사원 / 글로벌 도메인 설계 및 프레임워크 트러블슈팅

2024.05 - 2025.04

• B2B 해외 배송 SOP 엔진 개발: JSON 스냅샷 기반 데이터 모델링과 동적 상태 추론 로직을 도입하여 글로벌 배송 추적의 데이터 무결성과 유연성 확보.

인턴 / 레거시 성능 최적화 및 비동기 전환

2023.11 - 2024.04

• 비동기 메일 발송 리팩터링: Coroutine async/awaitAll 기반 병렬 처리를 도입하여 동기식 SMTP I/O 병목을 제거하고 API 응답 속도 80% 이상 개선.

PAGE 02 / CAREER HISTORY

Will Done

100% AI 에이전트로 구축한 타임 트래커 & 성과 리포트 자동화 데스크탑 앱

코드 한 줄 직접 작성하지 않고, 오직 AI 에이전트 오케스트레이션(Vibe Coding)만으로 구축한 풀스택 데스크톱 생산성 도구입니다. 흩어진 내 업무 시간을 커리어로 연결하는 타임 트래커 역할을 하며, 특히 백엔드 관점에서의 동시성 제어와 폴백 엔진 구현에 집중했습니다.

React 19TypeScriptTauri v2 (Rust)SQLite (sqlx)Tailwind CSSGemini CLIGoogle Antigravityoh-my-opencode
Will Done
PAGE 03 / PROJECT OVERVIEW

Will Done

Core Logic: Time-Shift Engine

Technical Deep Dive

The Problem

기존 TODO 앱은 고정된 일정 기반이라 긴급 업무 발생 시 이후 모든 일정을 사용자가 수동으로 조정해야 하는 불편함이 있었습니다. 또한 점심시간이나 회의 등 '언플러그드 타임'을 고려하지 못해 일정이 겹치는 문제가 발생했습니다.

Strategic Solution

Rust 백엔드에서 모든 태스크를 타임 블록으로 관리하고, 특정 지점의 시간 변화가 발생하면 이후 모든 블록을 재귀적으로 밀어내거나 당기는(Shift) 엔진을 구현했습니다. 특히 고정된 금지 시간대를 만날 경우 태스크를 자동으로 분할(Split)하여 유연한 스케줄링을 보장합니다.

긴급 업무 대응을 위한 수학적 타임라인 시프트 및 자동 분할 로직
impl.rs
#[tauri: :command]
pub async fn shift_timeline(
    state: State<'_, AppState>,
    start_from: i64,
    delta_minutes: i64
) -> Result<(), String> {
    let mut tasks = state.db
        .get_tasks_after(start_from)
        .await?;
    
    for task in tasks.iter_mut() {
        let new_start = task.planned_start + delta_minutes;
        
        // 언플러그드 타임(식사, 회의) 확인
        if let Some(conflict) = 
            check_unplugged_conflict(new_start, task.duration) 
        {
            // 충돌 시 태스크 분할 및 시프트
            split_and_reschedule(task, conflict).await?;
        } else {
            // 단순 시간 이동
            task.planned_start = new_start;
            state.db.update_task(task).await?;
        }
    }
    Ok(())
}
PAGE 04 / PROJECT DEEP DIVE

Will Done

Reliability: AI Fallback Strategy

Technical Deep Dive

The Problem

Gemini API의 무료 티어 사용 시 빈번한 Rate Limit(429 에러)과 일시적 서버 불안정으로 인해 AI 성과 보고서 생성 기능이 중단되는 사용자 경험 저하 문제가 있었습니다.

Strategic Solution

에러 발생 시 캐싱된 마지막 성공 모델을 우선 시도한 후, API에서 사용 가능한 모델 목록을 가져와 flash-lite > flash > pro 순서로 시도하는 폴백 엔진을 구축했습니다. 성공한 모델명을 캐싱하여 다음 요청 시 우선 시도함으로써 생성 성공률을 90%까지 끌어올렸습니다.

Architecture Flow

1
Last Successful Model

DB에 캐싱된 마지막 성공 모델 우선 시도

2
Flash-Lite (동적)

API에서 가져온 모델 중 flash-lite 계열 우선

3
Flash (동적)

표준 성능 flash 계열 모델로 재시도

4
Pro (동적)

최종 고성능 pro 계열 모델로 재시도

API 제약(429) 극복을 위한 다중 모델 폴백 및 자동 재시도 시스템
impl.rs
// Rust 백엔드: 동적 모델 폴백 엔진 (retrospective.rs)
let mut models_to_try = Vec::new();

// 1. 마지막 성공 모델 캐시 우선 시도
if let Some(ref cached_model) = user.last_successful_model {
    models_to_try.push(cached_model.clone());
}

// 2. API에서 사용 가능한 모델 동적 조회 및 우선순위 정렬
let available = internal_fetch_available_models(&api_key).await?;
available.sort_by(|a, b| {
    // flash-lite(3) > flash(2) > pro(1) 우선순위
    let score = |name: &str| match () {
        _ if name.contains("flash-lite") => 3,
        _ if name.contains("flash") => 2,
        _ if name.contains("pro") => 1,
        _ => 0,
    };
    score(&b.name).cmp(&score(&a.name))
});

for model in models_to_try {
    match try_generate_content(&client, &model, &prompt).await {
        Ok(text) => {
            // 성공 모델 DB 캐싱
            database::user::save_last_model(&pool, &model.name).await?;
            return Ok(text);
        }
        Err(e) => { eprintln!("Model {} failed: {}", model.name, e); continue; }
    }
}
Err("All Gemini models failed".into())
PAGE 05 / PROJECT DEEP DIVE

Will Done

Outcomes & Retrospective

Achievements

  • 01

    Tauri v2를 활용하여 단일 코드베이스로 macOS(ARM/Intel) 및 Windows 크로스 플랫폼 배포 지원

  • 02

    다중 모델 폴백 엔진 도입 후 AI 생성 기능 성공률 99% 달성

  • 03

    GitHub Actions를 통한 바이너리 서명 및 자동 업데이트 파이프라인 구축

Lessons Learned

AI가 코드를 작성하는 시대에 개발자의 역할이 '구현자'에서 '오케스트레이터'로 변화하고 있음을 체감했습니다. 특히 하네스(Harness) 문제 해결을 위한 Planning Loop와 구조화된 레퍼런스 문서 시스템 설계는 AI 협업의 핵심임을 깨달았습니다. 향후에는 사용자 패턴을 학습하여 일정을 최적화하는 온디바이스 학습 기능을 추가해보고 싶습니다.

가 준 영

가 준 영

Backend Developer

PAGE 06 / PROJECT CONCLUSION
Reflective Summary

Profanity Filter Library

Aho-Corasick 알고리즘 기반의 고성능 비속어 필터링 라이브러리

정규식(Regex) 기반 탐지의 성능 병목을 해결하고, 숫자나 공백을 섞은 변칙 우회 패턴을 O(N) 시간 복잡도로 처리하는 Kotlin/Java 라이브러리입니다. 우아한형제들 기술 블로그의 금칙어 관리 전략을 참고하여 설계했으며, 실시간 트래픽 환경에서도 안정적으로 동작하도록 최적화했습니다.

Kotlin 1.9Java 21Aho-Corasick (PayloadTrie)Gradle (Version Catalog)Kotest 5
Profanity Filter Library
PAGE 07 / PROJECT OVERVIEW

Profanity Filter Library

Efficiency: Aho-Corasick & Normalization Pipeline

Technical Deep Dive

The Problem

'시 1발'과 같이 숫자나 공백을 섞은 변칙 패턴을 정규식으로 탐지하려면, 모든 문자 사이에 선택적 패턴을 삽입해야 하므로 정규식 복잡도가 기하급수적으로 증가합니다. 10만 자 기준으로 Complex Regex는 85.89ms가 소요되어 실시간 서비스에 적용이 어렵습니다.

Strategic Solution

ProfanityFilterRegex 정책에 따라 입력 문자열을 정규화한 뒤, ahocorasick 라이브러리의 PayloadTrie를 활용해 단 한 번의 스캔(O(N))으로 모든 금칙어를 탐색합니다. 정규화 + Trie 탐색 + 허용 단어 검증까지 포함하고도 3.15ms로 처리되어, Complex Regex 대비 약 27배 빠른 성능을 확보했습니다.

Architecture Flow

1
applyPolicies

정책 기반 정규화 (숫자/공백 제거)

2
trie.parseText

PayloadTrie 기반 Aho-Corasick 탐색

3
Overlap Filtering

허용 단어 구간과의 중첩 판별

정책 기반 정규화와 O(N) 다중 패턴 탐색 파이프라인
impl.kt
fun validate(sentence: String) {
    if (sentence.isBlank()) return

    // 1. 정책 기반 전처리 (공백, 숫자 제거 등)
    val filtered = applyPolicies(sentence, defaultPolicies)

    // 2. 일차 탐지
    val detectedBans = trie.parseText(filtered)
    if (detectedBans.isEmpty()) return

    // 3. 허용 단어(WhiteList) 제외 처리
    val allowTrie = getAllowTrie(defaultPolicies)
    val detectedAllows = allowTrie.parseText(filtered)

    val remains = detectedBans.asSequence()
        .filterNot { ban ->
            detectedAllows.any { allow ->
                overlaps(ban.start, ban.end, allow.start, allow.end)
            }
        }
        .map { it.payload.word }
        .distinct()
        .toList()

    if (remains.isNotEmpty()) {
        throw ProfanityDetectedException(remains)
    }
}
PAGE 08 / PROJECT DEEP DIVE

Profanity Filter Library

Precision: False Positive Prevention

Technical Deep Dive

The Problem

'시발점'과 같이 정상적인 단어 안에 금칙어가 포함된 경우, 단순 매칭 방식은 이를 비속어로 오판하여 서비스 경험을 해치는 문제가 발생합니다.

Strategic Solution

동일한 정규화 정책이 적용된 '허용 단어 PayloadTrie'를 ConcurrentHashMap으로 캐싱하여 별도로 구축합니다. 금칙어 탐지 구간이 허용 단어 구간과 겹치는지(overlaps) 판단하는 구간 중첩 로직을 통해 오탐지를 필터링합니다.

Architecture Flow

1
detectedBans

금칙어 탐지 구간 추출

2
getAllowTrie

정책별 허용 단어 Trie 캐시 조회

3
overlaps Check

구간 중첩 여부 판별 후 필터링

구간 중첩(Overlaps) 알고리즘을 활용한 지능형 오탐지 방지
impl.kt
// 구간 중첩 판별: 두 구간이 겹치면 true
private fun overlaps(s1: Int, e1: Int, s2: Int, e2: Int) =
    s1 <= e2 && s2 <= e1

// 허용 단어 Trie를 정책 조합별로 캐싱
private fun getAllowTrie(
    policies: Set<ProfanityFilterRegex>
): PayloadTrie<String> {
    return allowTrieCache.computeIfAbsent(policies) { key ->
        val builder = PayloadTrie.builder<String>()
        allowWords.forEach { word ->
            val normalized = applyPolicies(word, key)
            if (normalized.isNotBlank())
                builder.addKeyword(normalized, normalized)
        }
        builder.build()
    }
}
PAGE 09 / PROJECT DEEP DIVE

Profanity Filter Library

Outcomes & Retrospective

Achievements

  • 01

    Complex Regex 대비 약 27배 빠른 처리 속도 달성 (10만 자 기준 3.15ms vs 85.89ms)

  • 02

    O(N) 시간 복잡도를 유지하여 금칙어 수 증가와 무관한 안정적 성능 보장

  • 03

    숫자/공백 혼용 등 다양한 변칙 우회 패턴 방어 및 구간 중첩 기반 오탐지 방지

Lessons Learned

알고리즘의 효율성이 실제 서비스의 사용자 경험과 비용에 얼마나 큰 영향을 미치는지 체감한 프로젝트였습니다. 특히 외부 라이브러리(org.ahocorasick)의 PayloadTrie를 활용하되, 정규화 정책과 허용 단어 캐싱 시스템을 직접 설계하여 단순한 라이브러리 래핑이 아닌 문제 해결 중심의 아키텍처를 구현했습니다. 향후에는 한글 자모 분리 탐지(ㅅㅂ → 씨발) 정책을 추가하여 더욱 강력한 변칙 방어 체계를 구축하고 싶습니다.

가 준 영

가 준 영

Backend Developer