근태 서비스에 이벤트 기반 아키텍처를 적용한다면 - Polling Adapter + Kafka

우리는 매일 아침 출근을 한다. 근태관리에서 출퇴근은 결국 돈과 관련되기 때문에 고용주/고용인 양쪽 입장에서 모두 중요하며 민감하게 생각한다. 과거에는 종이에 구멍을 뚫어(punch) 출퇴근 관리를 하였다. 언어란 신기하게도 현대 사회에서는 더 이상 사용하게 되지 않은 사물이나 행위를 나타내는 용어를 관습적으로 사용하는 경우가 많다. 여기서 얘기하는 펀치(punch)도 유사하게 출퇴근을 나타내는 용어로 여전히 사용 중이다. 펀치 인은 출근, 펀치 아웃은 퇴근. 아마 일부 외국 e-HR 시스템에서는 여전히 이러한 용어들을 사용하지 않을까? ...

September 5, 2025 · 7 min · 1441 words · Gukin Han
REDIS_SCHED 모드 아키텍처

트랜잭션에서 이벤트로 - Sync / Async / Redis 성능 비교와 TTV 분석

문제 @Transactional public void like(String loginId, Long productId) { // 1. 유저 조회 User user = userService.getByLoginId(loginId); // 2. 상품-좋아요 Insert boolean isInserted = productLikeRepository .insertIgnoreDuplicateKey(user.getUserId(), productId); if (!isInserted) return; // 3. 좋아요 수 집계 Update productRepository.incrementLikeCount(productId); } 좋아요 기능에서 처음엔 모든 로직을 하나의 트랜잭션 안에서 동기적(Sync)으로 처리 안정적이지만 트랜잭션 크기가 커지고, 상품 핫키에 경합으로 인한 병목이 발생 그래서, 이벤트 기반 설계를 통해 결합도를 낮추고(loosely coupled), 책임을 분리하려는 시도 Github PR : https://github.com/gukin-han/commercial-service/pull/7 ...

August 29, 2025 · 4 min · 743 words · Gukin Han
OFFSET 임계점에서 플랜 전환이 발생하는 그래프

정규화부터 캐싱까지 - 상품 목록 페이지네이션 최적화 과정

요약 정규화 쿼리(집계 + 정렬) → 풀스캔 + filesort로 ~17초 소요. 역정규화 + 정렬 포함 인덱스 → ~227ms (약 76배 향상). OFFSET 페이지네이션 → 인덱스 순차 스캔 시작 → 임계점에서 Table Scan + Filesort로 전환 → 이후 응답시간 급등. 전환 전 일정한 속도는 InnoDB 버퍼 풀/캐시 히트율 덕분. Keyset(Seek) 페이지네이션 → 깊은 페이지에서도 일정 성능 (421ms → 31ms, 92.6% 단축). 인덱스 설계는 핫 트래픽 패턴(브랜드 + 인기순/최신순/최저가순)에 맞춘 3개의 복합 인덱스 유지가 가성비 최적. 설계 원칙: 필터 선두 → 정렬키 → tie-breaker(id) 캐시(Cache-Aside) 적용 시 200RPS에서도 DB I/O 부하 없이 ms 단위 응답 가능. 단, TTL·Evict 정책·실시간성 요구사항·정합성 유지·스탬피드 방지 등 고려 필수. 결론: 정규화/역정규화 → 인덱스 설계 → 페이지네이션 전략 → 캐싱이 단계적으로 맞물려 성능을 좌우함. 문제 정의와 가설 웹 서비스에서는 쓰기 보다 읽기 트래픽이 압도적으로 많다는 글을 읽거나 실제로 경험해봤을 것이다. ...

August 15, 2025 · 16 min · 3283 words · Gukin Han

MySQL InnoDB에서 읽기·쓰기 충돌부터 deadlock 로그 분석까지

요약 MVVC는 트랜잭션 시작점을 기준으로 데이터 버저닝을 한다 락 종류에 따라 충돌 상황을 테스트 공유락은 말그대로 트랜잭션끼리 공유할 수 있다 베타락은 말그대로 하나의 트랜잭션만 가진다 공유락을 가진 상태로 업데이트, 삭제 등을 하면 베타락을 획득하려는 시도를 한다 락 획득 대기 사이클이 생기면 데드락이 발생한다 데드락을 최소화하기 위한 락 설계 방법: 락을 잡는 순서를 트랜잭션 마다 동일하게 유지 락 범위를 축소 첫 쿼리 부터 for update로 가져오는 방법 배경 이번 주차는 낙관적 락과 비관적 락을 학습하게 되었다. 비관적 락은 흔히 느리지만 정합성이 중요할때 사용하며, 낙관적 락은 충돌 가능성이 낮을 때 상대적으로 빠른 상황을 요구할때 사용한다고 한다. ...

August 8, 2025 · 15 min · 3089 words · Gukin Han

멱등성 키(Idempotency Key)의 이해와 기업 사례 정리

배경 멱등성(idempotency)은 단순히 “두 번 호출해도 한 번만 처리된다(Exactly-Once)“를 벗어나 시스템에 큰 이점을 제공한다. 우리 서비스에는 급여계산을 위한 근무시간을 산출할 때, 반복적으로 수행되어도 괜찮게 설계되어 있다. 반복적으로 요청이 들어왔을 때 결괏값이 addUp 하는 게 아니라 refresh 된다. 휴가계산도 동일하게 적용된다. 회사의 연차규정과 직원의 입사일에 따라 연차가 계산되는데 새로운 요구사항이 전달되어 수정하게 되었을때 계산결과가 잘못되어도 올바르게 계산되는 로직으로 수정해서 실행시키면 덮어씌워지게 된다. 물론 돈과 관련된 일이라 항상 조심하고 테스트를 잘 작성해야 한다. ...

July 25, 2025 · 4 min · 654 words · Gukin Han

좋은 테스트는 무엇인가? 레거시 시스템에 테스트 환경 구축

요약 반복적인 수동테스트로 불편함을 느껴서 테스트 환경을 구축하였습니다 구축하고 테스트를 작성하는 과정에서 구성 방식, 데이터 클린업 등에 대한 고민을 하였습니다 결과적으로 어떻게 설계하고 테스트를 작성할지 자신만의 기준을 정할 수 있게 되었습니다 수동 테스트의 반복 비용 마이클 페더는 “레거시 코드 활용 전략”에서 레거시 코드를 다음과 같이 정의했다: “테스트가 없는 코드는 곧 레거시 코드다” 테스트 코드가 없는 조직은 신기능 개발이나 이슈로 인한 코드 수정이 발생하면 일부 기능에 대한 수동 테스트를 진행하게 된다. 예를 들어, 연차를 생성하는 옵션에 요구사항이 바뀌면 연차 생성을 위한 옵션 설정을 하고 여러 직원, 근무형태 케이스에 대해 모두 테스트를 진행하게 된다. 아무래도 다양한 경우의 수를 확인하기 어렵기 때문에 숨어있는 모든 버그를 찾아내기란 쉽지 않았다. ...

July 18, 2025 · 6 min · 1251 words · Gukin Han

Java 스레드의 메모리 관리와 할당에 대한 이해

배경 Spring Boot에서는 Async 어노테이션으로 비동기 메서드를 쉽게 작성할 수 있다. 우리 팀에서 운영 중인 서비스에는 알림 생성 로직을 비동기로 실행하고 있다. 그 알림들 자체는 미래에 발생하기 때문에 동기적으로 작성할 필요가 없고, 유저들에게 응답속도를 높이기 위한 결정으로 보였다. Spring Boot의 코어 기능인 AutoConfiguration 덕분에 ThreadPoolTaskExecutor 빈도 자동으로 등록되지만, 실무에서는 IO-Bound, CPU-Bound 등 목적에 맞는 스레드 풀을 만들어 운영할 필요가 있다. 서로 다른 특성을 가진 작업을 혼합해서 사용하면 비효율이 발생하기 때문이다. CPU 사용이 낮은 IO-Bound 작업은 코어보다 많은 수의 스레드를 풀에 담아 사용하고, CPU 사용률이 높아 컨텍스트 스위칭 비용이 높은 CPU-Bound 작업은 코어 수에 비례해서 제한해야 한다. ...

May 4, 2025 · 5 min · 965 words · Gukin Han

Java enum을 활용한 전략 패턴(Strategy Pattern) 실무 적용기

B2B 서비스에서 새로운 고객사가 들어오거나 기존 기능을 수정 및 이슈 픽스 해야하는 상황일 때 복잡한 if-else 레거시가 유지보수 효율성 문제를 자주 발생시켰다. enum 방식의 전략 패턴을 도입했고, 각 로직에는 단위테스트를 쉽게 도입할 수 있었다. 결과적으로 회귀테스트 비용이 제로로 떨어졌고, 나중에 팀원이 동일한 작업을 진행하면서 추가 작업이 쉬워졌음을 느꼈다고 공유해주었다. 이 글에 사용된 코드는 실제 프로덕션 코드가 아닌, 핵심 구조만 재구성한 예시입니다. 유지보수 관점에서 if-else의 문제점 모든 if-else 구조가 나쁜 것은 아니다. 때론 간결하고 단순한 코드 방식이 더욱 유지보수에 유리한 경우가 있다. ...

April 25, 2025 · 4 min · 847 words · Gukin Han

DELETE-INSERT 패턴에서 발생하는 InnoDB Deadlock 분석

배경 최근 우리 서비스는 Sentry를 통해 데드락 알람을 자주 받고 있다 처음에는 중복 인덱스를 발견해서 제거하는 작업을 진행했다 그럼에도 불구하고 동일한 로직에서 데드락이 발생하는 중이다 조금은 빈도가 감소했을 수 있는데, 데드락 모니터링과 수집 및 통계화를 다른 회사에서는 어떻게 하는지 리서치가 필요해 보인다 아무튼 데드락을 분석해보니, 동일한 레코드의 DELETE-INSERT가 하나의 트랜잭션 내 수행되는 API에서 발생하였다 명확한 원인을 파악하고 전략을 세우기 위해 분석을 시도하였다 InnoDB Deadlock 분석 InnoDB 엔진 내부 상태를 확인하는 방법 RDMBS 클라이언트에서 위 명령어를 입력: ...

April 21, 2025 · 6 min · 1106 words · Gukin Han

ECS Auto Scaling의 파라미터 피팅: 목표값 설정, 부하 테스트, 운영 회고

1. 서비스 오픈 대비하여 Auto Scaling 켜기 팀에서 담당하는 서비스 오픈이 임박하게 되었고, 가용성 확보를 위해 ECS에 Auto Scaling을 적용해야 했다. 백엔드 팀 내에는 인프라 작업 경험자가 없었고 주니어 입장에서 서비스를 이해하는데 도움될 것으로 판단하여 자원하였다. 요청된 작업은 기존에 작성된 IaC를 수정해서 Auto Scaling을 적용하는 것이었다. 개인적으로 인프라 자체도 익숙하지 않았지만, IaC를 먼저 학습하고 적용하는 과정이 이 작업의 가장 큰 병목점이라고 생각했다. 하지만 적용하는 과정에서 해야 했던 고민들과 경험들은 단순하지 않았기 때문에 정리해서 공유한다. ...

January 15, 2025 · 11 min · 2194 words · Gukin Han