WHOAMI: Revi![]()
분석을 즐기는 개발자, 이경학입니다.
분석을 즐기는 개발자, 이경학입니다.
안녕하세요! Spring 기반 백엔드 개발자를 희망하는 이경학입니다.
디버거와 같은 다양한 분석도구로 원리를 분석하고, 얻어낸 지식을 시각적으로 나타내는 것을 즐깁니다.
데이터 정합성과 성능 개선에 관심이 많습니다.
Tech:Java · Spring Boot · MySQL · Redis · AWS · Docker
Projects
OnSquad
2024.04 - 현재취미/운동 모임 플랫폼의 백엔드 API 서버. 크루(커뮤니티)와 스쿼드(소규모 모임)의 가입·권한 관리, 리더보드, 실시간 알림을 제공합니다.
JavaSpring BootJPAQueryDSLMySQLRedisSSES3CloudFrontDockerGitHub ActionsPrometheusGrafana
Team: 3인 (디자이너 1 · 프론트 1 · 백엔드 1)Role: 백엔드 100% (설계/구현/테스트/배포)
Achievements
트랜잭션 내 S3 업로드 분리로 DB 커넥션 점유 78% 단축, Read/Write 처리량 21% 개선
증상외부 IO로 인해 길어진 DB 커넥션 점유시간이 Write뿐 아니라 같은 풀을 공유하는 Read 요청까지 오염시킬 수 있다고 의심
검증HikariCP hikaricp_connections_usage_seconds 지표 실측·k6로 Read:Write=7:3 비율의 동시 부하 재현
원인@Transactional 안에서 S3 업로드(외부 I/O)가 실행돼, 업로드가 끝날 때까지 DB 커넥션을 점유
설계
- LazyConnectionDataSourceProxy로 첫 쿼리 전 S3 업로드 [배제] 개발자가 쿼리 순서에 의존하는게 직접적인 이유
- S3 업로드를 Facade로 분리해 트랜잭션 밖에서 실행 [채택]
배운 점
- 트랜잭션 안에 외부 I/O 의존성이 들어오면 그 시간만큼 커넥션 반납이 늦어지므로, 트랜잭션은 DB 작업만 감싸도록 경계를 설계해야 함
- 트랜잭션과 외부 I/O를 분리하면 원자성이 깨지므로, DB 실패 시 업로드된 파일을 삭제하는 보상 처리가 필요함을 알게 됨
커넥션 점유 시간:66.9ms14.5ms (-78.3%)
Write 처리량:20,938건25,455건 (+21.4%)
Read 처리량:48,945건59,359건 (+21.1%)
랭커 집계 쿼리 90.5% 개선, 대규모(1M건)에서 전체 서비스 런타임 29.7% 단축
증상
- 리더보드 갱신 쿼리의 실행시간이 데이터 규모에 따라 비선형적으로 악화
- 10K → 100K: 24.7ms → 309ms
- 100K → 1M: 309ms → 6,444ms
검증EXPLAIN ANALYZE로 실행계획을 구간별로 분해해 각 구간의 소요시간 비중 측정·서비스 메서드 시간측정
원인집계를 윈도우 함수 2회+DISTINCT로 처리해, 행이 줄지 않은 43.8만 건을 그대로 두 번 집계함 — 전체 실행시간의 33.2% 차지
설계
- 1. 윈도우 함수+DISTINCT를 GROUP BY로 교체해 불필요한 중간 행 부풀림 제거
- 2. 4개 테이블 UNION ALL을 활동 로그 단일 테이블로 통합 → 3. UPSERT 사전집계로 GROUP BY 제거 → 4. 정합성 결함을 벌크 연산으로 해결
배운 점
- 쿼리 개선이 서비스 개선은 아님. 쿼리는 10K에서도 빨라졌지만(24.7→4.66ms), 서비스는 소규모에서 오히려 느려짐
쿼리 실행시간 (1M건, 크루당 상위 5위 기준):6,444ms611ms (-90.5%)
서비스 전체 런타임 (1M건, 크루당 상위 5위 기준):6,655ms4,680ms (-29.7%)
서비스 전체 런타임 (10K건, 크루당 상위 5위 기준, 소규모에선 역전):47ms88ms (+87.2%)
분리된 '조회 → 계산 → 저장' 연산을 Lua 원자화로 리더보드 점수 갱신 Lost Update 차단
증상점수와 활동시각을 하나의 ZSET score로 저장, '조회 → 계산 → 저장' 갱신이 동시에 겹치면 Lost Update 가능함을 확인
검증동일 사용자에 대해 두 스레드의 조회 시점만 겹치게 해 비원자적 3단계에서 Lost Update가 실제로 재현됨을 먼저 증명
원인두 스레드가 같은 시점의 점수를 읽고 각자 독립적으로 계산 → 저장 구조 = 나중에 쓴 값이 먼저 쓴 값을 그대로 덮어써 한쪽의 증분이 소실
설계'조회 → 계산 → 저장' 전 과정을 Lua 스크립트 내부로 이동해 Redis 서버에서 원자적으로 실행
배운 점Lua 스크립트는 롤백이 없으므로, 쓰기 커맨드를 하나만 두어 중간 실패 시 일부만 반영되는 상황을 차단해야 함
같은 사용자의 동시 갱신 정합성:Lost Update 발생점수 손실 0건
AOP 순서 제어 + Redis 장애 격리 체인으로 중복 요청 차단 및 불필요한 커넥션 점유 방지
증상크루 가입 신청이 더블클릭/재시도로 같은 사용자에게서 연속 유입되면, 중복 요청마다 트랜잭션이 열려 DB 커넥션을 불필요하게 점유
원인쓰로틀링 체크가 트랜잭션 시작 이후 실행되면, 차단될 요청도 커넥션을 이미 점유한 뒤에야 걸러짐
설계
- 1. Redis SET NX + TTL(1초)로 요청 키(crewId+memberId)를 선점해 중복 판별, 이미 선점됐으면 429로 차단
- 2. ThrottlingAspect를 TransactionInterceptor보다 먼저 실행되도록 순서 배치, 커넥션 점유 전에 차단
- 3. 쓰로틀링 전용 Redis 템플릿을 분리(200ms Timeout)해 다른 Redis 사용처 보호
- 4. CircuitBreaker로 Redis 호출을 차단(Open)하고 Caffeine fallback으로 연결
검증
- 1. Spring AOP 인터셉터 체인에서 ThrottlingAspect가 TransactionInterceptor보다 먼저 실행됨을 디버거로 확인
- 2. 거부된 요청에서 DataSource.getConnection()이 호출되지 않음을 Mockito로 검증
배운 점보호 장치(쓰로틀링)가 새로운 장애 지점이 되지 않도록, 타임아웃과 fallback까지 함께 설계해야 함
동시성 제어 전략 차등 설계(비관적 락/낙관적 락/Atomic Update + 수동 버저닝)로 크루 데이터 정합성 확보
증상
- 여러 운영진이 동시에 가입 승인/추방을 처리하면 crew의 인원수가 레이스 컨디션으로 꼬일 수 있음을 확인
- 한 명의 방장이 여러 운영자에게 동시에 방장 위임을 요청하면 crew에 방장이 중복으로 발생할 수 있음을 확인
검증
- 승인/추방: 락 없이 가입 승인·추방을 동시 실행해 인원수 유실을 증명 (가입 승인 기대 4 → 실제 3, 추방 후 기대 잔여 인원 1 → 실제 2)
- 방장 위임: 락 없이 방장 위임을 동시 실행해 두 후보 모두 방장으로 승격되는 중복을 증명
원인
- 승인/추방: 두 스레드가 같은 시점에 crew를 읽고 각자 수정/저장 → 나중 커밋이 먼저 커밋을 덮어써 증감분 소실
- 추방: Crew 프록시 객체 초기화로 불필요한 SELECT 쿼리가 추가로 발생
- 방장 위임: 두 스레드가 같은 시점에 방장이 아직 유효하다고 보고 각자 후보를 승격 → 두 운영자가 동시에 방장으로 남음
설계
- 승인: 낙관적 락+재시도를 먼저 실측한 뒤 crew row 비관적 락 채택 [채택]
- 추방: Crew를 로드하지 않는 Atomic Update로 SELECT를 제거하고, version을 쿼리에서 증가시켜 방장 위임과의 충돌 감지 유지 [채택]
- 방장 위임: 경합 주체가 현재 방장 한 명뿐이라 @Version + 재시도 [채택]
배운 점
- 락 하나가 정답이 아니라, 경합 주체의 수와 충돌 빈도에 따라 전략을 달리해야 함
- 낙관적 락의 재시도 실패를 실측하고 나서야, 비관적 락으로 바꿀 근거를 얻음
승인: 60건 동시 가입승인 처리시간 (6명 운영진×10건):341ms (낙관적 락, 재시도 176회)122ms (비관적 락, -64.2%)
승인: 재시도 소진으로 인한 영구 실패:18건 / 60건 (30.0%)0건 (-100%)
추방: 동시 추방 시 인원수 유실 (2명 동시 추방):1건 유실 (기대 1, 실제 2)0건 유실 (기대/실제 모두 1)
추방: 추방 1건당 Crew 관련 쿼리:2회 (SELECT 1+UPDATE 1, lazy 초기화 발생)1회 (UPDATE 단독, Crew 미조회)
방장 위임: 동시 위임 시 방장 중복 발생:2건 (두 후보 모두 방장)0건 (한 명만 방장)
Education & Certifications
학력백석대학교 졸업 (4년제) 2017.03 - 2023.02 (졸업)
자격
- 정보처리기사 · 한국산업인력공단2025.06.13
- SQLD · 한국데이터산업진흥원2024.09.20
- 정보보안산업기사 · 한국인터넷진흥원2021.06.25
- 리눅스마스터 · 한국정보통신진흥협회2019.04.05
