[TIL] null과 0은 다르다 — API에서 “데이터 없음”과 “값이 0”을 구분하는 법

API에서 미분석(null·무효 입력)과 분석 완료 후 점수 0을 구분하지 못해 생기는 조용한 버그, 변환 전 유효성 검증·JSON 직렬화·집계 분모 처리까지 정리한 글이다.

📝 TIL (Today I Learned)
null이나 빈 맵을 모두 Map.of()로 흡수하면, 미분석·점수 누락·합계 0인 분석이 한데 섞인다. 변환 전에 isValidScoreMap()으로 걸러내고, 무효면 null을 유지하면 집계·UI에서 의미가 살아난다.

[친구하자] JPQL에서 매핑되지 않은 관계의 LEFT JOIN 한계

Call과 CallEmotionResult를 SQL처럼 LEFT JOIN하고 싶지만 표준 JPQL은 연관관계 기반 JOIN이 기본이라 막히는 문제, Hibernate HQL 확장과의 차이, 네이티브 쿼리·두 쿼리 분리 후 Java 병합, 구현 시 주의점까지 정리합니다.

CallCallEmotionResult를 SQL처럼 LEFT JOIN하고 싶은데, 표준 JPQL은 연관 경로가 없으면 객체 그래프 기반 조인을 쓰기 어렵다. Hibernate HQL은 버전에 따라 엔티티 JOIN … ON이 열리기도 하지만 포터빌리티와 트레이드오프가 있다. SQL·JPQL의 차이와, 우리가 택한 두 쿼리 + 병합, 구현 시 주의점까지 정리한다.

[친구하자] JPA @Modifying(clearAutomatically = true)의 함정

@Modifying(clearAutomatically = true) 사용 시 영속성 컨텍스트가 비워지면서 엔티티가 detached 되어, 상태 변경이 DB에 반영되지 않았던 버그 원인과 해결 과정을 정리합니다.

정기 예약 취소 API에서 CallReservation은 정상적으로 취소되는데 RecurringReservation 상태만 ACTIVE로 남는 버그를 겪었다. 원인은 @Modifying(clearAutomatically = true)로 인해 영속 엔티티가 detached 된 뒤 상태를 변경한 것이었다.

[친구하자] 백분율을 정확히 100%로 만드는 방법 — Largest Remainder Method

감정 분석 API에서 7가지 감정 점수를 소수점 둘째 자리 백분율로 보여주되 합계는 반드시 100.00%가 되게 하는 문제, 반올림 함정과 Largest Remainder Method(최대 잔여 방식) 해법, 구현 시 부동소수점·동점 처리 등을 정리합니다.

감정 분석 API를 만들면서 “각 항목은 소수점 둘째 자리로 보이게 하되, 합계는 반드시 100.00%”라는 요구를 만났다. 단순 반올림과 흔한 보정 방식의 함정, 최대 잔여 방식(Largest Remainder, Hamilton 방식으로도 알려짐)으로 맞춘 과정과 구현 시 체크 포인트를 정리한다.

[TIL] 트랜잭션 이벤트 발행 타이밍 문제 — @TransactionalEventListener vs publishEvent() (#196)

트랜잭션 커밋 전 publishEvent()를 호출했을 때 발생한 타이밍 문제와 @TransactionalEventListener(AFTER_COMMIT)afterCommit() 해결 방식을 정리한 글이다.

📝 TIL (Today I Learned)
@Transactional 안에서 publishEvent()를 호출하면 @Async 리스너가 커밋 전 데이터를 읽으려다 실패할 수 있다. 이 문제는 TransactionSynchronizationManager.afterCommit() 또는 @TransactionalEventListener(AFTER_COMMIT)로 해결한다.

[TIL] 트랜잭션·Redis 정합성, TOCTOU, 보상 트랜잭션 — #150에서 배운 백엔드 포인트

DB 트랜잭션과 Redis 정합성, TOCTOU 경쟁 조건, self-invocation, 보상 트랜잭션, DB UPDATE를 활용한 뮤텍스 등 #150 구현에서 정리한 백엔드 핵심 포인트입니다.

📝 TIL (Today I Learned)
DB 트랜잭션과 외부 시스템(Redis)이 함께 움직일 때, “원자성”과 “정합성”을 어떻게 지켜야 하는지 #150 구현에서 다시 정리했다.

[TIL] Spring Events, @Async, Redis, STOMP — #148·#149에서 배운 백엔드 포인트

Spring Events(@TransactionalEventListener, AFTER_COMMIT), @Async와 LazyInitializationException, Redis ZSET·Lua·TOCTOU, STOMP WebSocket 사용자별 전송, 스케줄러 설계 등 #148·#149 구현에서 정리한 백엔드 포인트입니다.

📝 TIL (Today I Learned)
트랜잭션 경계를 이해하고, Redis에서 원자성을 보장하며, WebSocket 세션 상태를 고려한 분산 환경의 실시간 알림을 다루면서 정리한 내용이다.

[친구하자] @Transactional 안에서 외부 네트워크를 호출하면 안 되는 이유

@Transactional 범위 안에서 외부 네트워크 호출(Gemini WebSocket)을 처리했다가, DB 커넥션 점유 시간이 길어져 커넥션 풀 고갈 위험이 생긴 문제와 해결(TransactionTemplate)을 정리합니다.

@Transactional 범위 안에 외부 네트워크 호출을 끼워 넣으면 DB 커넥션 풀이 고갈될 수 있다. 그리고 “메서드만 분리하면 되겠지”라는 생각은 Spring의 프록시 구조(self-invocation)에 막힌다.

[TIL] 멀티스레드 Race Condition과 AtomicBoolean (volatile의 한계)

멀티스레드 환경에서 volatile의 한계(가시성 vs 원자성)와 check-then-act 레이스 컨디션을 AtomicBoolean.compareAndSet(CAS)으로 해결하는 방법을 정리했습니다.

📝 TIL (Today I Learned) \n+> volatile만으로는 해결되지 않는 문제가 있다. "가시성"과 "원자성"은 다르고, check-then-act 패턴이 섞이면 레이스 컨디션이 터진다. 이걸 AtomicBoolean.compareAndSet으로 정리해 본다.

[TIL] ArrayDeque vs ArrayBlockingQueue — 코드리뷰로 배우는 Java Queue 정리

ArrayDeque와 ArrayBlockingQueue의 차이, 코드리뷰에서 발견한 OOM 위험, 그리고 Reactor Sinks 버퍼로 ArrayBlockingQueue를 선택한 이유를 정리했습니다.

📝 TIL (Today I Learned)
“OOM 방지”라고 주석을 달았는데, 실제로는 OOM이 날 수 있는 코드가 되어 있었습니다. 코드리뷰 한 줄이 Java Queue의 핵심 차이를 알려 준 경험을 정리합니다.

Pagination