Fastats
통계 검색 플랫폼
KOSIS 통계표 26만 건을 Elasticsearch에 색인하고 Nori 형태소 분석기를 적용해, 검색 응답 시간을 3,200ms에서 42ms로 줄였습니다.

Backend
Engineer
느린 구간을 찾아,
수치로 개선합니다.
Java와 Spring Boot를 중심으로 성능 최적화, 실시간 데이터 파이프라인, 검색, AI · RAG를 다루는 백엔드 개발자입니다.

public SearchHits<StatisticsDoc> search(String keyword) { NativeQuery query = NativeQuery.builder() .withQuery(q -> q.match(m -> m .field("title").query(keyword) .analyzer("nori_analyzer"))) .build(); return elasticsearchTemplate .search(query, StatisticsDoc.class);}
검색, 실시간 처리, RAG. 세 프로젝트에서 맡은 역할과 핵심 구현, 측정 결과를 정리했습니다.
통계 검색 플랫폼
KOSIS 통계표 26만 건을 Elasticsearch에 색인하고 Nori 형태소 분석기를 적용해, 검색 응답 시간을 3,200ms에서 42ms로 줄였습니다.
지역별 주식 투자 플랫폼
KIS 실시간 시세를 Kafka로 처리해 WebSocket으로 전달하고, 시·구·동별 종목 순위를 지도 위에 표시했습니다.
AI 자기소개서 어시스턴트
저장된 경험을 RAG로 검색해 자기소개서 초안에 반영하고, 생성 단계별 진행 상태를 SSE로 전달합니다.
통계 검색 플랫폼
계속 스크롤하면 상세가 열립니다
문제 · 통계 검색26만 건을 LIKE로 조회하니 풀 테이블 스캔이 발생해 응답이 최대 3,200ms까지 늘고, 조사가 붙은 검색어는 처리하지 못했습니다.
검색 응답 시간98.7% 단축
색인 문서 수신규 구축
조사 검색 · "통계청의"Nori 형태소 분석
검색 데이터를 Elasticsearch에 색인해 풀 스캔을 없애고, Nori 분석기로 조사가 붙은 검색어도 처리했습니다.
−public List<Statistics> search(String keyword) {− String sql = "SELECT * FROM statistics− WHERE title LIKE ?";− return jdbc.query(sql, "%" + keyword + "%", ...);−}+public SearchHits<StatisticsDoc> search(String keyword) {+ NativeQuery query = NativeQuery.builder()+ .withQuery(q -> q.match(m -> m+ .field("title").query(keyword)+ .analyzer("nori_analyzer")))+ .build();+ return elasticsearchTemplate+ .search(query, StatisticsDoc.class);+}별도 인프라는 필요 없지만 한국어 형태소 분석을 지원하지 않아 조사 검색 문제를 해결할 수 없었습니다.
도입은 비교적 쉽지만, 3주 안에 팀원 모두가 참고할 만한 한국어 자료와 사례가 부족했습니다.
Nori로 한국어 형태소를 처리할 수 있고, Spring 연동 자료도 충분했습니다.
별도 검색 서버를 운영해야 하지만, 한국어 검색과 팀의 개발 기간을 고려해 Elasticsearch를 선택했습니다.
'통계청', '일반 가구' 등 실제 검색어로 요청해 응답 시간을 비교했습니다. StatSurveyJdbcRepositoryIntegrationTest에서도 LIKE와 FULLTEXT 쿼리 실행 시간을 확인했습니다. 3,200ms와 42ms는 동시 부하가 아닌 단일 요청 기준입니다.
지역별 주식 투자 플랫폼
계속 스크롤하면 상세가 열립니다
문제 · 실시간 주식 데이터시·구·동별 종목 순위를 갱신하려고 클라이언트가 5초마다 API를 호출했고, 같은 데이터를 확인하는 요청이 분당 600건을 넘었습니다.
서버 API 요청 / 분폴링 제거
업데이트 지연실시간화
응답 시간 (동시 1,000명)Gatling 측정
KIS WebSocket으로 시세를 한 번 수신해 Kafka로 비동기 전달하고, 처리된 데이터를 연결된 클라이언트에 WebSocket으로 브로드캐스트했습니다.
−// Before: REST endpoint polled every 5s−@GetMapping("/api/v1/stocks/ranking")−public ResponseEntity<List<StockRankDto>> getRanking() {− return ResponseEntity.ok(− stockService.getRegionalRanking());−}+public CompletableFuture<SendResult<String, String>>+ sendRealTimeStockData(String stockCode, ...) {+ return kafkaTemplate.send(+ STOCK_REALTIME_TOPIC, stockCode, message);+}+@KafkaListener(topics = "stock-realtime-data",+ groupId = "wts-consumer-group")+public void consumeRealTimeStockData(String msg) {+ realTimeStockCache.put(stockCode, stockData);+}기존 Redis를 그대로 쓸 수 있지만, 구독자가 없을 때 발행된 메시지는 사라져 장애 후 재처리할 수 없습니다.
메시지 전달은 안정적이지만, 소비가 끝난 과거 데이터를 다시 처리하는 용도에는 맞지 않았습니다.
운영할 구성 요소는 늘지만, 토픽에 남은 메시지를 장애 복구 후 다시 처리할 수 있습니다.
1인 프로젝트에는 다소 무겁지만, 장애 후 재처리가 필요해 Kafka를 선택했습니다.
로그인 후 지역 정보를 조회하는 시나리오를 Gatling으로 측정했습니다. DB 인덱스, 커넥션 풀, JPA 배치, Redis 캐시를 차례로 적용한 뒤 95% 응답 시간과 처리량을 다시 확인했습니다.
AI 자기소개서 어시스턴트
계속 스크롤하면 상세가 열립니다
문제 · 자기소개서 생성저장된 경험을 전부 프롬프트에 넣어 초안을 만들었습니다. 경험이 쌓일수록 입력이 길어져 응답이 30초를 넘겼고, 문항과 관계없는 경험까지 함께 들어갔습니다.
초안 생성 시간직접 작성 대비
생성 파이프라인SSE 스트리밍
초안 자동 검증미달 시 재작성
질문과 관련된 경험을 벡터 유사도로 찾아 프롬프트에 넣고, 생성 과정을 다섯 단계로 나눠 SSE로 전달했습니다.
−// Step 1: RAG Context Search−List<Experience> experiences =− experienceRepository.findAll();−String context = experiences.stream()− .map(Experience::getRawContent)− .collect(Collectors.joining("\n---\n"));+sendProgress(emitter, STAGE_ANALYSIS,+ "문항 의도와 v3 답변 설계안을 생성하고 있습니다.");+QuestionDraftPlanV3 plan = questionDraftPlannerService+ .planV3(company, position, questionTitle, ...);+sendProgress(emitter, STAGE_RAG,+ "v3 설계안에 맞는 검증 경험을 검색하고 있습니다.");+String experienceContext =+ buildExperienceContext(questionId, plan, storyIds);사용자별로 모델을 다시 학습해야 하고, 경험 데이터가 바뀔 때마다 학습을 반복해야 합니다.
구현은 단순하지만, 저장된 경험이 많아질수록 입력 토큰과 응답 시간이 함께 늘어납니다.
재학습 없이 경험을 추가할 수 있고, 질문과 관련된 항목만 프롬프트에 넣을 수 있습니다.
경험을 바로 추가·수정할 수 있고 입력 토큰을 제한할 수 있어 RAG를 선택했습니다.
같은 문항을 직접 작성했을 때 걸린 시간과 서비스가 초안을 만드는 시간을 비교했습니다. 서버 처리량을 측정한 수치는 아닙니다. 초안 품질은 사람 평가 점수가 아니라 코드가 판정합니다 — 경험 밀도 58점, 진위 위험 48점, 면접 방어력 58점을 기준으로 미달하면 재작성합니다.
Java 17/21에서 요청·이벤트·장기 작업의 실행 경계를 나눴습니다. Future와 Executor로 I/O를 병렬화하고, concurrent collection·atomic counter·record DTO로 공유 상태와 결과 타입을 구체화했습니다.
구현: Executor/Future 기반 병렬 처리 · 다음 검증: executor lifecycle·queue·backpressure 정책 중앙화
Spring Boot 3.2–3.4에서 MVC와 WebFlux를 같은 부하 조건으로 비교하고, Security filter·PBAC·Gateway·SSE를 별도 실행 경계로 구현했습니다. 기능 조립을 넘어 요청이 어디서 인증되고 대기하며 복구되는지까지 설계했습니다.
구현: 자체 JWT·RoleHierarchy·PBAC · 다음 검증: 외부 IdP·서명 키 회전·분산 세션
MySQL을 정형 데이터의 원본 저장소로 두고, 실제 필터·정렬 순서에 맞춰 복합 인덱스와 연관 조회를 설계했습니다. JPQL로 부족한 계층·좌표 조회는 native query로 내리고, 전문 검색은 별도 엔진으로 분리했습니다.
구현: 복합 인덱스·연관/좌표 조회·검색 분리 · 다음 검증: Flyway/Liquibase 기반 schema 이력 관리
Redis를 단순 조회 캐시가 아니라 인증 토큰, 초단기 시세 데이터, 비동기 LLM 작업 상태의 수명 관리 계층으로 사용했습니다. 데이터 성격별로 1초부터 7일까지 TTL을 나누고, 연결 실패 시 DB·기본값으로 기능을 격리했습니다.
구현: standalone 캐시·토큰·작업 상태 관리 · 다음 검증: Cluster/Sentinel과 hit·miss·eviction 계측
Kafka로 시세 수신과 클라이언트 전송을 분리하고, stockCode key·consumer group·offset 정책을 코드와 설정에 명시했습니다. 같은 producer/consumer 패턴을 STOMP 채팅 전달에도 적용해 메시징 경계를 두 흐름에서 구현했습니다.
구현: keyed producer·consumer group·WebSocket 전달 · 다음 검증: 수동 commit·DLQ·consumer lag 관측
컴퓨터공학 전공으로 CS 기초를 쌓았습니다. 졸업 프로젝트로 서비스를 처음 끝까지 만들어봤고, 재학 중 KB 과정을 병행했습니다.
컴퓨터공학 학사 졸업 · CS 전공 기초
금융 도메인 상식과 Java · Spring Boot · JSP 기반 웹 개발 기초를 쌓으며 첫 팀 프로젝트에 도전했습니다.
금융 도메인 상식과 Java · JSP 웹 개발 기초, 첫 팀 프로젝트
알고리즘 집중 훈련과 실무형 팀 프로젝트 3회. Kafka · Redis · MongoDB · Elasticsearch 등 미들웨어를 직접 도입하며 백엔드 전문성을 쌓았습니다.
알고리즘 교육 · 백엔드 중심 팀 프로젝트 3회
하나금융그룹 연계 과정으로 AI · 데이터 분석 심화 교육을 받고 실전 프로젝트를 수행했습니다.
AI · 데이터 분석 교육 · 하나금융그룹 연계 프로젝트
기획부터 배포까지 혼자 하였습니다. 자소서 작성과 코딩 테스트 공부를 에이전트로 만들었고, 모델이 틀릴 것을 전제로 검증과 재작성을 붙였습니다. 만드는 과정에서도 Claude Code · Codex가 따를 규칙을 문서로 정의했고, 반복 작업은 예약 루틴으로 넘겼습니다.
에이전트 2개 · Claude Code · Codex 루틴 운영
화면에 나온 수치는 전부 아래 저장소에서 확인하실 수 있습니다.