donghakim.dev — zsh
[03]
dnghkm@donghakim.dev ~/blog $ ls -t ./blog/*.md

Blog / 블로그

백엔드 · 인프라 · 커리어에 대한 1년차의 기록

8 / 8 posts
2026.05.16·4분·ai

딸깍 개인 블로그 제작기

Claude Design과 Claude Code의 이어달리기

디자인 시안부터 미니 PC 자동 배포까지 한 줄도 사람이 안 짠 사이드 프로젝트 기록. AI 가 코드를 짜기 시작한 뒤로 사람의 자리는 어디에 남는지에 대한 작은 답입니다.

#ai#claude-code#devops#docker#nextjs#self-hosting#side-project#spring
딸깍 개인 블로그 제작기 · cover
2026.05.08·3분·backend

mysql2 의 timezone 과 dateStrings 가 같이 켜져 있으면 저장할 때마다 9 시간이 쌓입니다

두 옵션이 따로는 합리적인데 합치면 round-trip 마다 +9h. KST 환경의 Node 백엔드에서 자주 마주치는 함정

사용자가 18:01 을 입력해 저장하면 새로고침에 18:10, 다시 저장하면 19:10. 매 저장 round-trip 마다 9 시간이 쌓이는 원인을 찾고 보니 mysql2 풀의 timezone + dateStrings 두 옵션의 상호작용이었습니다.

#datetime#kst#mysql2#nodejs#timezone#troubleshooting#utc
mysql2 의 timezone 과 dateStrings 가 같이 켜져 있으면 저장할 때마다 9 시간이 쌓입니다 · cover
2026.05.07·3분·infra

.dockerignore 한 줄이 mvn test 를 통째로 사라지게 했습니다

CodeBuild 에서 "No tests were executed!" 가 나오면 의존성·JUnit 보다 먼저 봐야 할 곳

로컬 mvn test 도 통과하고 의존성도 정상인데 CodeBuild 만 "No tests were executed!" 로 실패. 컴파일·surefire·JUnit 가설을 한 시간씩 잡고 뒤지다, 결국 범인은 .dockerignore 한 줄이었던 이야기.

#ci#codebuild#docker#dockerignore#maven#surefire#troubleshooting
.dockerignore 한 줄이 mvn test 를 통째로 사라지게 했습니다 · cover
2026.05.07·3분·backend

Lettuce cluster 의 SCAN cursor 는 값이 아니라 인스턴스였습니다

cursor 객체를 새로 만들면 라우팅 정보가 통째로 사라져 첫 shard 만 보입니다

Redis cluster 에서 SCAN 루프가 dev 에선 멀쩡한데 prod 에서만 매칭 0건. spring-data-redis 를 우회해 native Lettuce 로 내려간 코드에서 cursor 를 새 인스턴스로 복사한 한 줄이 만든, 한 픽스가 다른 함정을 깐 케이스 정리.

#cluster#java#lettuce#redis#scan#spring-data-redis#troubleshooting#valkey
Lettuce cluster 의 SCAN cursor 는 값이 아니라 인스턴스였습니다 · cover
2026.05.07·5분·note

팀 지식 저장소를 중앙화한 이야기

AI 부하직원에게 일을 잘 시키는 법

흩어져 있던 팀의 암묵지를 한 곳에 모으고, Claude Code 가 작업 시작과 커밋 시점 양쪽에서 자동으로 참조·기록하도록 강제하는 사내 공유 저장소를 만들었습니다.

#ai#claude-code#devops#hooks#knowledge-management
팀 지식 저장소를 중앙화한 이야기 · cover
2026.05.07·7분·backend

Valkey Serverless의 SCAN cursor가 Java long 범위를 넘어서 NPE까지 가는 길

spring-data-redis가 cursor를 long으로 다루는 한 줄이 만든 결제·주문 분기 사고

캐시 무효화에 쓰던 SCAN의 cursor 값이 Java long 범위를 초과하면서 NumberFormatException으로 깨졌고, scanKeys가 null을 반환하면서 호출자의 addAll(null)이 NPE로 이어졌습니다. 결제는 떨어졌는데 주문이 안 잡히는 사고로 보였던 한 줄의 함정, 두 겹의 방어로 막은 핫픽스, 그리고 그 너머에 있는 Redis SCAN cursor의 정체에 대한 정리.

#lettuce#npe#redis#scan#spring-data-redis#troubleshooting#valkey
Valkey Serverless의 SCAN cursor가 Java long 범위를 넘어서 NPE까지 가는 길 · cover
2026.05.06·5분·backend

JVM 배치 서비스의 14일 메모리 누수 — JSch SFTP가 Session을 흘리고 있었다

heap dump 분석으로 connection thread 누적을 찾기까지의 진단 보고서

운영 배치 컨테이너의 메모리가 14일 동안 +351 MiB 단조 증가해 OOM Kill로 끝났습니다. CloudWatch 메트릭 → ECS Exec → jcmd로 heap dump → Eclipse MAT 분석으로 따라간 끝에, JSch SFTPUtils.destroy()가 Channel만 닫고 Session(과 그 connection thread)을 살려둔 한 줄짜리 누수를 찾았습니다. 부수적으로 AspectJ ShadowMatchCache와 JVM reflection inflation도 같이 부풀고 있었던 이야기.

#aws-ecs#eclipse-mat#heap-dump#jsch#jvm#memory-leak#sftp#troubleshooting
JVM 배치 서비스의 14일 메모리 누수 — JSch SFTP가 Session을 흘리고 있었다 · cover
2026.05.06·4분·backend

항공권 검색 캐시 전략 — 무엇을 캐시하고, 언제 비우는가

외부 GDS 호출 비용을 줄이면서 stale 좌석 정보는 피하는 무효화 설계

항공권 조회는 외부 GDS·NDC 호출 단가가 비싸기 때문에 검색 결과를 Valkey에 캐시해 두고 재사용합니다. 다만 좌석은 살아 움직이는 데이터라서 캐시가 stale해지면 곧장 사용자 불만으로 돌아옵니다. 캐시 키를 어떻게 설계했고, 어떤 트리거로 어떤 범위를 비우는지, 그리고 무효화 단계를 본 흐름과 어떻게 묶거나 떼어낼지에 대한 정리.

#airline#architecture#cache#cache-invalidation#redis#valkey
항공권 검색 캐시 전략 — 무엇을 캐시하고, 언제 비우는가 · cover