TPS 175 → 1,568
로그인 처리량 +796%
부하 테스트로 지연의 97%가 외부 IdP 호출임을 규명하고 Redis 토큰 캐시를 도입
A4 세로 기준으로 조판되어 있습니다. 인쇄 대화상자에서 여백 없음 · 배경 그래픽 켜기를 선택하면 화면과 같게 저장됩니다.
Backend Developer
다양한 개발 경험을 바탕으로, 정량적으로 설계하고 합리적으로 구현합니다.
TPS 175 → 1,568
로그인 처리량 +796%
부하 테스트로 지연의 97%가 외부 IdP 호출임을 규명하고 Redis 토큰 캐시를 도입
38.1% → 76.2%
RAG 정답 핵심내용 포함률
청크 1,500토큰이 컨텍스트 윈도우를 초과하던 문제를 골든셋 평가로 특정
lock 1.5s · stmt 5s
운영 DB 경합 차단 설계
advisory lock 으로 중복 실행을 막고 짧은 timeout 으로 fast-fail 하도록 설계
| Auto Translator | 2026 · 실무 | LLM 다국어 번역을 "검수 가능한 운영 작업"으로 다루는 번역 콘솔입니다. AI 초안을 바로 DB 에 쓰지 않고, 검증·검수·이관 단계를 거쳐 반영합니다. |
|---|---|---|
| First Ticket | 2026.04 - 2026.05 · 팀 프로젝트 | 매크로·동시 접속·중복 예매 문제를 해결하는 MSA 티켓 예매 플랫폼입니다. 6인 백엔드 팀에서 User/Auth 도메인과 API Gateway, Eureka Service Discovery 를 설계·구현했습니다. |
| Personal RAG | 2026.02 - 2026.03 · 개인 프로젝트 | 각종 문서를 개인 PC 안에서 처리하는 로컬 RAG 챗봇. 로컬 모델의 성능 측정 및 개선을 진행했습니다. |
| 오하아사 | 2026.01 - 운영 중 · 개인 프로젝트 | 일본 아사히 TV「오하요 아사히」의 매일 별자리 운세를 한국어·일본어·영어로 제공하는 웹 서비스입니다. 스크래핑부터 번역·배포·운영까지 구축했습니다. |
2026. 09. 27. 기준 · 최신 내용은 djpark.work 에서 확인할 수 있습니다.
딥라이트에서 LLM을 활용한 자동화 번역 프로그램 제작 경험이 있습니다.
스파르타클럽 내일배움캠프 자바단기심화 부트캠프를 수료하고, MSA 아키텍처 설계 및 구현을 경험했습니다.
KH정보교육원 Java 웹 개발과정 수료 후 (주)빅팀아이앤씨에서 웹사이트 유지보수부터 LLM 챗봇 개발까지 다양한 경험을 쌓았습니다.
건축·디자인에서 개발로 이어진 이력을 바탕으로 폭넓은 도메인 지식을 갖추고 있으며, 협업을 위한 의사소통과 프로젝트의 주안점을 항상 신경 쓰고 있습니다.
LLM 다국어 번역을 "검수 가능한 운영 작업"으로 다루는 번역 콘솔입니다. AI 초안을 바로 DB 에 쓰지 않고, 검증·검수·이관 단계를 거쳐 반영합니다.
PythonPostgreSQLSQLiteOpenAIGemini APIDocker
한국어 원문을 포르투갈어(브라질)·일본어·중국어 간체/번체로 번역해 운영 DB에 반영합니다. 확장성을 고려해 번역 대상 테이블과 필드 계약을 설정파일로 관리할 수 있게 구성했습니다.
핵심 기술적 과제는 `운영 DB의 부담을 최소화한다` 였습니다. 해당 주문에 맟춰 설계 및 구성했습니다.
핵심 전제는 "LLM 번역 결과값을 바로 저장하지 않는다" 였습니다. 응답 ID·필수값 필드를 먼저 검증하고 통과한 건만 검토/UPSERT 로 보냅니다. UNIQUE 충돌·lock 대기·statement timeout·검증 실패는 버리지 않고 수동 처리 큐로 이관해 다시 볼 수 있게 남깁니다. 수동 콘솔은 AI 초안을 자동 저장하지 않고, 검수 담당자가 필드별로 확인·수정해야 (원본 FK, locale) 기준 UPSERT 가 실행됩니다.
운영 중인 서비스 DB에 접근하는 작업이므로 경합 상황에 대한 차단을 먼저 설계했습니다. 번역 Worker 는 PostgreSQL advisory lock 으로 인스턴스 간 중복 실행을 막고, 연결 풀은 최소 1 최대 2 로 묶었습니다. 자동 배치는 사람이 이미 검토한 항목과 이전 실패 항목을 제외하며, 다른 작업이 먼저 needs_translation 을 해제한 행은 덮어쓰지 않습니다.
번역 배치 작업이 운영DB 를 오래 붙잡고 있을 위험 확인
원인 LLM 응답은 번역 분량에 따라 수초에서 수십 초가 걸리는데, 저장용 커넥션을 미리 잡고 응답을 기다리면 그 시간만큼 운영 트래픽이 쓸 커넥션이 묶임
해결 LLM 호출 구간과 DB 저장 구간을 분리해 대기 중에는 커넥션을 점유하지 않도록 조치 - 연결 풀은 최소 1 최대 2 로 묶고, lock_timeout 1.5초 · statement_timeout 5초를 걸어 오래 버티는 대신 빨리 포기하도록 설정
여러 인스턴스가 동시에 뜰 경우 같은 행을 중복 번역하는 이슈
원인 기존 배치 Worker 가 이미 실행 중인지 확인할 방법이 없어, 프로세스가 둘 이상일 경우 같은 대상을 각자 처리했습니다.
해결 PostgreSQL advisory lock 을 통해 Worker 진입을 직렬화해 인스턴스 간 중복 실행을 차단했습니다.
저장 단계에서 실패한 건이 기록 없이 사라지는 현상
원인 UNIQUE 충돌 · lock 대기 · statement timeout · 응답 검증 실패가 모두 예외로 끝나면서, 어떤 항목이 왜 빠졌는지 남지 않았습니다.
해결 실패 유형을 구분해서 수동 처리 큐로 이관하고 작업 이력에 기록했습니다. 풀 대기가 1초를 넘는 건도 실패로 처리하지 않고 같은 큐로 보내 다시 검토할 수 있게 했습니다.
기여도 · 1인 개발 - CLI 배치 · 웹 콘솔 Front-end · DB 연동 Back-end
매크로·동시 접속·중복 예매 문제를 해결하는 MSA 티켓 예매 플랫폼입니다. 6인 백엔드 팀에서 User/Auth 도메인과 API Gateway, Eureka Service Discovery 를 설계·구현했습니다.
Java 21Spring BootSpring Cloud GatewayKeycloakPostgreSQLRedisKafkaQueryDSLAWS ECS FargateDockerGrafanaGitHub Actions
대기열로 트래픽을 흡수하고, 분산락으로 선점 좌석을 직렬화하고, Kafka Saga 로 결제 실패를 보상하는 구조입니다. 6개 서비스가 Spring Cloud Gateway · Eureka · Config Server 위에 올라가고, 모든 요청의 진입점인 Gateway 에서 인증·인가·보안을 단일 흐름으로 처리합니다. 배포는 AWS ECS Fargate 에 GitHub Actions CI/CD 로 이어집니다.
Keycloak 이 발급한 JWT 는 만료 전 강제 무효화가 불가능합니다. 로그아웃·비밀번호 변경 후에도 탈취된 토큰이 유효한 문제를 Redis JTI 블랙리스트로, 탈취된 Refresh Token 재사용을 Rotation 으로 해결했습니다. 짧은 AT TTL 만 쓰는 안과 서버 세션 안은 각각 UX 저하와 Stateless 이점 상실로 기각했고, 로그아웃 후 최대 5초(L1 TTL) 지연을 감수하는 쪽을 택했습니다.
요청 1건마다 Redis 조회 - vuser 70 구간에서 1,400 TPS 스파이크
원인 모든 요청이 Gateway 에서 Redis 내 blacklist:{jti} 를 조회하다 보니, 초당 처리량이 그대로 Redis 호출량이 됐습니다. 실제로 블랙리스트에 걸리는 토큰은 1% 미만임에도 전량을 조회하고 있었습니다.
해결 Caffeine L1 캐시(TTL 5s)를 우선 조회하고 Cache Miss 일 때만 Redis 로 내려가게 바꿔 조회 24회 → 1회 (-95.8%). 로그아웃 후 최대 5초 지연은 AT TTL(15분) 대비 0.3% 라 수용했습니다.
vuser 50 구간에서 503 에러 및 TPS 가 0 으로 떨어지는 현상
원인 Resilience4j Bulkhead 의 maxConcurrentCalls 가 25 로 묶여 있었습니다. 동시 호출이 한도를 넘자 BulkheadFullException 이 즉시(elapsed 0ms) 발생했고, 이 예외를 Circuit Breaker 가 실패로 집계해 sliding window 실패율이 50% 를 넘겨 OPEN 으로 전환됐습니다.
해결 한도를 200 으로 올려 BulkheadFullException 4,917건 → 0건, Circuit Breaker 는 CLOSED 를 유지했습니다.
로그인 TPS 175 에서 정체, 응답 135ms
원인 부하테스트 분석 결과 지연의 97% 가 Keycloak ROPC 호출이었습니다. Gateway 1ms 미만, User Service 5ms 미만인데 Keycloak 왕복만 약 130ms 였습니다.
해결 at-cache:{SHA-256(lower(email))} 키로 Redis AT 캐시를 도입해 TPS 175 → 1,568 (+796%), 응답 135ms → 63ms (-53%). 이메일을 해시로 써서 PII 평문 저장을 피했습니다.
DB 커넥션 대기로 응답이 지연되는 현상
원인 HikariCP 풀 기본값 10 인데 vuser 는 50 이라서 구조적으로 부족했습니다. 게다가 connection-timeout 이 30초라 대기가 그대로 응답 지연으로 쌓였습니다.
해결 Little's Law(L = λ × W)로 필요 커넥션을 산정해 풀을 50 으로, timeout 은 3초 fast-fail 로 조정했습니다. GET /users/me TPS 1,720 으로 예측 대비 오차 1%, 응답 37ms → 11ms (-70%).
기여도 · 6인 백엔드 팀 - User/Auth 도메인 · API Gateway · Eureka 설계 및 구현
각종 문서를 개인 PC 안에서 처리하는 로컬 RAG 챗봇. 로컬 모델의 성능 측정 및 개선을 진행했습니다.
Java 17Spring BootSpring AIOllamapgVectorPostgreSQLReactTypeScriptDockerGitHub Actions
ChatGPT, Gemini 등의 상용 LLM API는 품질과 생성 속도를 보장하지만 기본적으로 인터넷 연결이 필요하고, 문서 본문 또한 외부로 전송되고 사용량만큼 과금됩니다. 정부 공고문·계약서·사내 매뉴얼 등 외부 유출에 민감한 문서를 다루기 위해 로컬에서 끝나는 파이프라인이 필요했습니다. 과거 Java 8 · eGovFramework 기반이던 LLM 챗봇을 Java 17 + Spring Boot 3.5 + Spring AI 로 다시 설계하고, Ollama(qwen3:8b)와 pgVector 를 붙여 로컬에서 구동할 수 있도록 했습니다.
RTX 4060 Ti 8GB 기준으로 qwen3:8b(5.6GB)와 bge-m3(0.7GB)를 함께 사용하는 상황에서의 적절 청크 크기, 재정렬 방식, GPU 성능 까지 잡는것이 메인 포인트였습니다.
개선에 앞서 먼저 측정 체계부터 만들었습니다. LLM은 같은 질문에도 매번 다른 답을 내놓기 때문에, 한 번의 테스트 결과를 그대로 믿지 않고 모든 비교 실험을 3회 이상 반복해 재현되는지 확인하는 걸 원칙으로 삼았습니다. 실제로 로컬 모델과 상용 모델을 처음 비교했을 땐 62.5% 대 62.5%로 정확히 동률이 나와 "차이가 없다"고 결론 낼 뻔했지만, 반복하자 50.0% 대 58.3%로 격차가 드러났습니다. 게다가 결과가 들쭉날쭉했던 쪽은 오히려 상용 모델이었습니다(편차 12.5%p 대 0.0%p) — 한 번의 우연한 결과만 믿었다면 정반대 결론을 냈을 것입니다.
콜드스타트 37.2초, 부하 테스트 에러율 100%
원인 설정 파일에는 올바른 값이 적혀 있었지만 요청에 실리지 않았습니다. 응답 유지시간(keepAlive)을 YAML 정수 -1 로 쓰자 문자열 "-1" 로 직렬화됐고, 단위가 없는 값이라 Ollama측에서 거부하고 있었습니다.
해결 단위를 명시(-1s)해 콜드스타트 37.2s → 1.2s (-97%), 에러율 100% → 0% 로 회복했습니다.
100 ~ 450자 답변시간 평균 88.9초, p95 188.6초
원인 모델이 답하기 전 내부적으로 추론하는 과정(thinking mode)을 끄는 설정이 적용되지 않고 있었습니다. application.yaml 에는 있었지만 ChatClientFactory 가 defaultOptions 를 통째로 지정하면서 덮어쓰고 있었습니다.
해결 전달 경로를 고쳐 평균 88.9s → 33.0s (-63%), p95 188.6s → 74.5s (-60%). 다만 사실 포함률이 77.8% → 57.1% 로 함께 떨어져, 트레이드오프를 규명하고 기본값은 품질 우선으로 되돌렸습니다.
검색은 정답을 찾아오는데 모델이 그 내용을 쓰지 못함
원인 청크 1500토큰이 한국어 공고문에서 평균 5,425자 청크를 만들었고, topK=3 근거만으로 약 4,521토큰이 되어 검색 단계에서 이미 num_ctx 4,096 을 초과하고 있었습니다.
해결 청크를 800토큰으로 낮춰 문항 통과율 0.0% → 50.0%, 정답 핵심내용 포함률 38.1% → 76.2% 로 올렸습니다.
기여도 · 개인 프로젝트 1인 개발 - 기획 · 백엔드 · 프론트엔드 · 인프라
일본 아사히 TV「오하요 아사히」의 매일 별자리 운세를 한국어·일본어·영어로 제공하는 웹 서비스입니다. 스크래핑부터 번역·배포·운영까지 구축했습니다.
ReactTypeScriptViteTailwind CSSCloudflareDeepL APIi18next
Cloudflare Workers 가 매일 원본 운세를 수집하고 DeepL API 로 일본어 원문을 한국어·영어로 번역한 뒤 Cloudflare KV 에 캐싱합니다. 프론트엔드는 Cloudflare Pages 에 배포되어 KV 캐시만 읽으므로, 방문자가 몰려도 원본 사이트에 부하가 가지 않습니다.
방송 시간(오전 7시)을 기준으로 당일/전날 운세를 구분해 노출하고, API 갱신이 늦어지면 TV Asahi 사이트를 직접 조회하는 fallback 을 둬 빈 화면이 뜨지 않도록 했습니다. 다크 모드 전용 배경 애니메이션처럼 서비스 성격에 맞는 연출도 함께 다듬었습니다.
초기에는 Gemini API 로 번역했으나, 별자리 운세처럼 짧고 정형화된 문장에서는 범용 LLM보다 전용 번역 엔진인 DeepL이 더 안정적인 품질을 보여 전환했습니다. i18next 를 적용해 3개 국어를 실시간 전환할 수 있습니다.
오전 이른 시간에 접속하면 빈 화면이 뜨거나 전날 운세가 나오는 현상
원인 원본 방송이 오전 7시에 갱신되는데 그 시각을 기준으로 삼지 않아, 갱신 전 접속과 갱신 후 접속이 같은 데이터를 보고 있었습니다.
해결 방송 시간을 기준으로 당일/전날 운세를 구분해 노출하고, API 갱신이 늦어지면 TV Asahi 사이트를 직접 조회하는 fallback 을 둬 빈 화면이 뜨지 않도록 했습니다.
방문자가 늘수록 원본 사이트로 요청이 그대로 전달됨
원인 초기 구조에서는 방문 시점마다 원본을 조회해, 트래픽이 그대로 외부 사이트 부하로 이어졌습니다.
해결 Cloudflare Workers 가 하루 한 번 설정한 시간에 수집·번역한 결과를 KV 에 캐싱하고 프론트엔드는 캐시만 읽도록 바꿨습니다. GitHub Actions 로 매일 캐시를 미리 채워 첫 방문자도 지연을 겪지 않게 했습니다.
기여도 · 1인 개발 - 기획 · 프론트엔드 · Workers 백엔드 · 배포 및 운영