<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"> <channel><title>노갬성</title><link>https://nogamsung.github.io/</link><description>백엔드 개발자 안검성(nogamsung)의 기술 블로그. Spring Boot, JPA, Querydsl, Redis, Kong API Gateway, Airflow 등 실무에서 겪은 문제와 해결 과정을 기록합니다.</description> <language>ko-kr</language> <lastBuildDate>Thu, 05 Nov 2026 11:48:17 +0900</lastBuildDate> <generator>Jekyll 4.4.1</generator> <atom:link href="https://nogamsung.github.io/rss.xml" rel="self" type="application/rss+xml" /> <item><title>온프레미스 서버에 Kamal로 무중단 배포 도입하기</title><link>https://nogamsung.github.io/posts/kamal-zero-downtime/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/kamal-zero-downtime/</guid><pubDate>Wed, 04 Nov 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>devops</category> <category>deployment</category> <description>Watchtower로 컨테이너를 다시 만들던 배포가 재배포 한 번에 서비스 대부분을 503으로 만든 일을 계기로, 직접 블루그린·Kamal·Coolify·Docker Swarm을 비교하고 Kamal을 고른 과정과 Tailscale SSH·서비스 간 호출·한 서버의 dev/prd·SMTP·예약 작업처럼 옮기면서 만난 문제를 정리합니다.</description> </item> <item><title>권한을 바꿨는데 30분 동안 옛 권한으로 동작한다, JWT에 권한 버전 심기</title><link>https://nogamsung.github.io/posts/jwt-permission-version/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/jwt-permission-version/</guid><pubDate>Tue, 03 Nov 2026 18:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>stateless JWT의 roles 클레임은 발급 시점 값이라 관리자가 권한을 바꿔도 토큰 수명(30분) 동안 옛 권한으로 메뉴와 API가 동작했습니다. 블랙리스트 대신 토큰에 권한 계산 시각(pv)을 넣고 Redis의 변경 시각과 비교해 401로 끊는 설계, 커밋 전 캐시 재적재 레이스, fail-open 선택까지 정리합니다.</description> </item> <item><title>무중단 배포에서 두 번 도는 예약 작업, ShedLock으로 막기</title><link>https://nogamsung.github.io/posts/shedlock-scheduled-lock/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/shedlock-scheduled-lock/</guid><pubDate>Tue, 03 Nov 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>무중단 배포로 Spring Boot 배치 서버가 잠깐 두 개 뜨는 동안 @Scheduled 작업이 두 번 실행되는 문제를, 분산 락·Spring Batch·Quartz와 비교한 뒤 ShedLock(JDBC)과 lockAtLeastFor로 해결하고 graceful shutdown·테스트까지 갖춘 과정을 정리합니다.</description> </item> <item><title>내부 서비스 호출에 서킷브레이커를 넣으며 권한 조회를 fail-open에서 fail-closed로 바꾼 이야기</title><link>https://nogamsung.github.io/posts/circuit-breaker-fail-closed/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/circuit-breaker-fail-closed/</guid><pubDate>Mon, 02 Nov 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>서비스 API 9개가 account 서비스에 권한을 묻는 구조에서, 조회 실패 시 &quot;허용&quot;하던 fail-open 설계는 장애가 곧 권한 우회였습니다. Resilience4j로 호출부 단위 회로 4개를 두고, 무엇을 실패로 셀지(4xx 제외), 회로 OPEN을 liveness에 연결하지 않는 이유, 권한 조회를 503 fail-closed로 바꾼 결정과 그 직후 드러난 배포 공백 문제까지 정리합니다.</description> </item> <item><title>Spring Boot 4 업그레이드, 그리고 final 엔티티 때문에 LAZY가 사실 EAGER였다</title><link>https://nogamsung.github.io/posts/spring-boot-4-kotlin-lazy-loading/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/spring-boot-4-kotlin-lazy-loading/</guid><pubDate>Tue, 27 Oct 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>Spring Boot 3.4에서 4.1로, Kotlin 1.9에서 2.3(K2)으로 18개 모듈을 하루에 올리며 알게 된 것들. Kotlin 2.x의 plugin.jpa가 all-open을 켜면서 final이던 엔티티가 open이 되고, 그제서야 @ManyToOne(LAZY) 67곳이 지금까지 EAGER로 돌고 있었다는 사실과 가짜 fetch join 179곳이 드러났습니다. Jackson 3 때문에 전 요청이 39ms 만에 408이 난 사고도 함께 정리합니다.</description> </item> <item><title>next start는 next.config를 어디서 읽는가, Docker 런타임 이미지에서 세 번 데인 기록</title><link>https://nogamsung.github.io/posts/next-start-runtime-config-docker/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/next-start-runtime-config-docker/</guid><pubDate>Tue, 20 Oct 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>frontend</category> <category>nextjs</category> <description>빌드 스테이지에서 .next만 복사한 Next.js 런타임 이미지가 두 달 사이 세 번 발목을 잡았습니다. images.remotePatterns가 무시돼 이미지가 400, basePath가 사라져 전 라우트 404, 비루트 사용자로 바꾸자 corepack이 pnpm을 다시 받으려다 멈추고 로거의 mkdir이 EACCES로 모든 BFF가 HTML 500. Next 소스에서 어떤 설정이 빌드 산출물에 들어가고 어떤 설정을 next start가 런타임에 읽는지 확인하고, 비루트 컨테이너 점검 목록을 정리합니다.</description> </item> <item><title>서브도메인 6개를 한 호스트의 basePath로 옮기기, 무중단 4단계와 Next가 접두를 안 붙여 주는 곳</title><link>https://nogamsung.github.io/posts/nextjs-basepath-migration/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/nextjs-basepath-migration/</guid><pubDate>Sun, 04 Oct 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>frontend</category> <category>nextjs</category> <description>각자 서브도메인에 살던 Next.js 앱 6개를 포털 호스트 아래 경로 접두(example.com/svc)로 옮겼습니다. 앱 배포와 프록시 반영이 같은 순간일 필요가 없게 만든 4단계 순서, Next가 basePath를 붙여 주지 않는 fetch·문자열 src·window.location을 전역 패치로 처리한 방법, 누락을 찾아내는 &quot;접두 밖은 404&quot; 프록시, 쿠키를 host-only로 바꾼 이유, 그리고 서비스워커 4개가 FCM 토큰을 서로 지우던 문제까지 정리합니다.</description> </item> <item><title>Refresh 토큰 회전을 넣었다가 하루 만에 뺀 이유</title><link>https://nogamsung.github.io/posts/refresh-token-rotation-rollback/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/refresh-token-rotation-rollback/</guid><pubDate>Fri, 04 Sep 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>보안 리뷰 권고대로 refresh 토큰 회전과 재사용 탐지를 넣었더니 30분마다 전 사용자가 모든 서비스에서 동시에 로그아웃됐습니다. 데스크톱 셸이 한 refresh 토큰을 웹앱 5개 쿠키와 자기 저장소에 복제하는 구조에서 &quot;재사용 = 탈취&quot; 판정이 어떻게 오판으로 바뀌는지, 그리고 회전을 걷어내고 무엇을 남겼는지 정리합니다.</description> </item> <item><title>LiveKit 통화 종료 정산 경합, REST leave와 webhook이 같은 상태를 바꿀 때</title><link>https://nogamsung.github.io/posts/livekit-call-settlement-race/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/livekit-call-settlement-race/</guid><pubDate>Thu, 03 Sep 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>사내 메신저에 LiveKit SFU로 음성 통화를 붙인 뒤 사흘 동안 겪은 세 가지 문제를 정리합니다. 인증 필터가 LiveKit 서명 JWT를 유저 JWT로 검증해 webhook이 전량 401이 되던 것, 동기 REST leave와 비동기 webhook이 같은 통화 상태를 바꾸며 생긴 정산 경합을 멱등 정산으로 푼 것, 유저 단위 STOMP 토픽으로는 응답자의 다른 기기 벨을 끌 수 없던 것입니다.</description> </item> <item><title>안읽음 배지는 왜 세 번이나 틀렸나, 클라이언트 누적 카운터에서 서버 집계로</title><link>https://nogamsung.github.io/posts/unread-badge-server-count/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/unread-badge-server-count/</guid><pubDate>Tue, 28 Jul 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>frontend</category> <category>realtime</category> <description>&quot;받은 프레임마다 +1&quot;로 만든 안읽음 배지가 6주 동안 세 번 틀렸습니다. 새로고침하면 0이 되는 설계 한계, 서버가 두 토픽으로 팬아웃한 DM을 두 번 세는 문제, 그리고 메일 1통이 배지 4로 보인 사건까지. StrictMode를 의심했다가 소스로 결백을 확인하고, 좀비 소켓을 찾아낸 뒤, 결국 &quot;숫자는 서버가 세고 이벤트는 무효화만 한다&quot;로 바꾼 과정을 정리합니다.</description> </item> <item><title>백그라운드 탭에서 WebSocket이 조용히 죽는 이유, 같은 배너 뒤의 네 가지 원인</title><link>https://nogamsung.github.io/posts/websocket-background-tab-reconnect/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/websocket-background-tab-reconnect/</guid><pubDate>Fri, 24 Jul 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>frontend</category> <category>realtime</category> <description>웹 메신저의 &quot;연결이 끊겼습니다. 재연결 중...&quot; 배너가 6주 동안 네 번 다른 이유로 떴습니다. deactivate()가 재연결을 다시 부르는 플래핑, Spring SimpleBroker가 0,0으로 협상해 버린 하트비트, 숨긴 탭의 타이머 스로틀링, 그리고 직결 WebSocket이 BFF 경유 REST보다 먼저 도착해 상태가 유실되는 첫 입장 레이스까지. stompjs와 Spring 소스를 따라가며 정리합니다.</description> </item> <item><title>로그인은 200인데 쿠키가 사라진다, NextResponse.cookies는 이름으로만 키잉된다</title><link>https://nogamsung.github.io/posts/nextresponse-cookies-keyed-by-name/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/nextresponse-cookies-keyed-by-name/</guid><pubDate>Tue, 16 Jun 2026 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>frontend</category> <category>nextjs</category> <description>같은 이름의 쿠키가 Domain만 다르게 두 개 있을 때 Next.js BFF에서 벌어진 세 가지 문제를 하루 안에 따라갑니다. 401 무한 루프, 발급 직후 쿠키가 사라지는 자기 삭제, 그리고 진짜 원인인 ResponseCookies의 &quot;이름만으로 키잉되는 Map&quot;까지. 모킹한 테스트가 왜 버그를 숨겼는지도 함께 적습니다.</description> </item> <item><title>엔티티를 웹 앱에서 꺼내기, 도메인 모듈과 API 모듈 분리</title><link>https://nogamsung.github.io/posts/gradle-domain-api-module-split/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/gradle-domain-api-module-split/</guid><pubDate>Mon, 08 Dec 2025 18:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>예약 배치 작업이 웹 애플리케이션 모듈 안에 살게 되자 구조가 잘못됐다는 게 드러났습니다. 엔티티·리포지토리·QueryDSL Q클래스를 API 모듈에서 도메인 모듈로 꺼내 Gradle 모듈 4개를 6개로 나눈 313개 파일 변경을, 왜 이 시점이었는지, scanBasePackages와 kapt·테스트 유틸은 어떻게 처리했는지, 그리고 아직 못 한 것은 무엇인지 정리합니다.</description> </item> <item><title>아무도 true로 바꾼 적 없는 locked_yn, 마감을 플래그 대신 날짜로 계산하기</title><link>https://nogamsung.github.io/posts/date-based-period-lock/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/date-based-period-lock/</guid><pubDate>Mon, 08 Dec 2025 10:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>수주·판매·입금·재고 보고의 월 마감을 위해 첫 커밋부터 있던 locked_yn 컬럼을 191일 만에 지웠습니다. 운영 코드 어디에서도 true로 바꾼 적이 없는 플래그 옆에서 날짜 규칙이 세 번 바뀌며 실제 마감을 맡고 있었기 때문입니다. &quot;상태를 저장하는 플래그&quot;와 &quot;날짜에서 계산하는 규칙&quot; 사이의 선택과 그 비용을 정리합니다.</description> </item> <item><title>PDFBox로 거래명세서 PDF 만들기, 좌표는 왼쪽 아래에서 시작한다</title><link>https://nogamsung.github.io/posts/pdfbox-transaction-statement/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/pdfbox-transaction-statement/</guid><pubDate>Fri, 05 Dec 2025 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>판매 건마다 종이 양식과 같은 모양의 거래명세서 PDF가 필요했습니다. Apache PDFBox 3으로 한글 폰트를 임베딩하고, 셀 병합이 있는 표를 직접 그리고, 첫 페이지 18행·이후 25행으로 미리 쪼개는 페이지네이션과 도장 이미지 합성까지 구현한 과정과, 도장 하나 추가하다 깨진 테스트 이야기를 정리합니다.</description> </item> <item><title>지우는 게 없다면 삭제라고 부르지 말자, 상태 컬럼으로 소프트 삭제 설계하기</title><link>https://nogamsung.github.io/posts/soft-delete-status-column/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/soft-delete-status-column/</guid><pubDate>Mon, 16 Jun 2025 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>backend</category> <category>spring</category> <description>새 ERP 백엔드를 시작하고 16일 동안 상태 enum이 세 번 바뀌었습니다. 하드 삭제, deleted_at, 상태 컬럼, Hibernate @SQLRestriction 중 상태 컬럼을 고른 이유와, &quot;삭제&quot; API가 실제로는 아무것도 지우지 않는다는 사실을 이름에 드러내기로 한 결정, 그리고 그 대가로 안게 된 고유성·조회 필터 문제를 정리합니다.</description> </item> <item><title>당근 2024 테크 밋업</title><link>https://nogamsung.github.io/posts/daangn-tech-meetup-2024/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/daangn-tech-meetup-2024/</guid><pubDate>Mon, 07 Oct 2024 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>etc</category> <category>conference</category> <description>당근 2024 테크 밋업 후기. 당근알바 초기 엔지니어링 전략, 다중 서버 채팅 시스템 설계, Istio 네트워크 장애 해결 사례와 당근페이 네트워킹 세션에서 들은 내용을 정리했습니다.</description> </item> <item><title>Airflow 설치하기</title><link>https://nogamsung.github.io/posts/airflow-install/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/airflow-install/</guid><pubDate>Tue, 24 Sep 2024 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>data</category> <category>airflow</category> <description>Spring Batch, Spark, AWS Batch 등 배치 처리 선택지를 비교한 뒤, macOS에서 Conda 가상환경으로 Apache Airflow 2.10을 설치하고 MySQL, AWS provider까지 세팅해 실행하는 절차를 정리합니다.</description> </item> <item><title>SLASH24 토스 컨퍼런스</title><link>https://nogamsung.github.io/posts/slash24-toss-conference/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/slash24-toss-conference/</guid><pubDate>Thu, 12 Sep 2024 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>etc</category> <category>conference</category> <description>토스 SLASH 24 서버 세션 후기. 리플레이 검증으로 레거시 원장을 안전하게 전환한 사례, MySQL 기반 코어뱅킹 전환, SAGA 패턴과 보상 트랜잭션으로 환전을 처리하는 방법을 정리했습니다.</description> </item> <item><title>API Gateway가 있다면 인증 로직을 넘기자</title><link>https://nogamsung.github.io/posts/api-gateway-authentication/</link><guid isPermaLink="true">https://nogamsung.github.io/posts/api-gateway-authentication/</guid><pubDate>Wed, 11 Sep 2024 00:00:00 +0900</pubDate> <dc:creator>Geomseong Ahn</dc:creator> <category>devops</category> <category>api-gateway</category> <description>여러 Spring Boot 서버의 JWT 토큰 인증을 Auth 서버에 몰아줄지 API Gateway에서 처리할지 비교하고, Kong JWT 플러그인의 한계를 커스텀 플러그인으로 넘어서 인증을 통합한 설계 과정을 공유합니다.</description> </item> </channel> </rss>
