Engineering Notes

겪은 것들을
기록합니다

삽질, 고민, 결정의 순간들 — 나중의 나와 팀을 위한 메모

Posts
아키텍처 DevOps

@Scheduled 대신 Quartz — 동적 스케줄과 취소가 필요했다

72시간 마감 안에 4번 리마인더, 선생님이 응답하면 즉시 취소. @Scheduled 폴링으로는 개별 취소가 안 된다. Quartz JDBC Store로 스케줄 상태를 외부화하고, 도메인 모델을 깔끔하게 유지한 과정.

읽기 → 약 5분
아키텍처 결정

알림톡, 트랜잭션이 끝난 뒤에 보내야 한다

NCP 장애가 나도 API는 성공해야 한다. 알림은 부가 기능이고, 장애 도메인은 격리되어야 한다. @TransactionalEventListener + @Async 조합을 고른 이유, 그리고 언제 Outbox 패턴이 필요해지는지.

읽기 → 약 4분
JPA Performance

@Basic(fetch = LAZY)가 왜 안 되지? — Hibernate Bytecode Enhancement 적용기

TEXT 컬럼이 상태 변경 쿼리마다 딸려온다. @Basic(fetch = LAZY)를 붙여도 Hibernate는 무시한다. 단순 필드는 프록시를 쓸 수 없기 때문이다. Bytecode Enhancement 플러그인으로 실제 lazy load를 구현한 과정.

읽기 → 약 5분
트러블슈팅 DevOps

NAT Instance를 통해 외부 API를 못 부른다 — iptables FORWARD 체인 함정

App Runner → GCP Cloud Run 호출이 정확히 10초에 timeout. NAT Instance에서 curl은 되는데 왜? FORWARD 체인 policy가 DROP이었다. NAT Instance 자신의 트래픽(OUTPUT)과 라우팅되는 트래픽(FORWARD)은 다른 체인을 탄다.

읽기 → 약 5분
트러블슈팅 DevOps

App Runner에서 앱이 뜨질 않는다 — 로그도 없이

EnvironmentPostProcessor가 아무 로그도 남기지 않고 조용히 실패하고 있었다. bootJar를 열어보니 AWS SDK JAR이 없었다. Gradle implementation vs api의 차이가 만들어낸 삽질 기록.

읽기 → 약 6분
아키텍처

MSA로 시작했다가 Modular Monolith로 바꾼 이야기

7개 레포 MSA로 출발했다. 중복 코드, 다수 레포 동시 변경, 월 20만원 인프라 비용 — 직접 운영하면서 문제들이 쌓였다. 서비스 규모에 맞지 않는 구조라고 판단하고, Domain 추상화 기반 Modular Monolith로 전환한 과정.

읽기 → 약 7분