소개
안녕하세요! 이 블로그를 운영하고 있는 개발자입니다.
개발하면서 배운 것들을 기록하고 공유하기 위해 이 블로그를 시작했습니다.
기술 스택
- 이 블로그는 Astro와 Tailwind CSS로 만들어졌습니다.
- GitHub Pages에서 호스팅됩니다.
연락처
- GitHub: github.com/dj258255
- Email: dj258255@naver.com
프로젝트
최신 스토리
훔친 refresh token은 두 번째 사용에서 들킨다 - 회전·재사용 감지·family 무효화 직접 구현
별찌 Identity 도메인에서 외부 인증 SaaS 없이 직접 구현한 JWT 인증의 핵심 — Access는 stateless 15분, Refresh는 stateful 7일로 비대칭을 두고, 회전(rotation)·재사용 감지(reuse detection)·token family 무효화로 토큰 탈취를 막은 과정을 실제 코드로 정리합니다. refresh token 원본을 저장하지 않고 SHA-256 해시만 두는 이유, family_id·parent_token_id로 회전 계보를 추적하는 스키마, 죽은 토큰이 다시 오면 family 전체를 끄는 JPQL, 그리고 표준(RFC 9700·Auth0)이 알려주지 않는 가장 어려운 부분 — 정상 사용자가 토큰 만료 직후 동시에 갱신할 때 발생하는 race condition과 family 오탐(멀쩡한 사용자 강제 로그아웃)을 PG 행 잠금 + 10초 grace + noRollbackFor로 풀고, 20스레드 동시 갱신 오탐 0을 testcontainers 회귀 테스트로 검증하기까지 담았습니다.
별찌 - 완벽한 설계보다 1주 POC, 빠르게 만들며 짓는 중
이미 보고 있는 상품을 기준으로 여러 쇼핑몰의 가격과 중고 대안을 한 화면에서 비교해 주는 AI 패션 쇼핑 어시스턴트 별찌를, 백엔드 2인으로 만들고 있는 현재진행형 기록입니다. 완벽한 설계보다 1주 POC로 빠르게 검증하고 짓는 방식(ADR), 데이터는 PostgreSQL 18 + pgvector로 시작하고 Elasticsearch + Nori는 Phase 2로 미룬 이유(ADR), 쇼핑몰 여러 곳을 동시에 호출하는 워크로드라 Java 25 가상 스레드 + Spring Boot 4를 고른 이유 (ADR), 2인이 도메인 경계를 지키려고 택한 Spring Modulith 모듈러 모놀리스(ADR), 인증 SaaS 대신 자체 OAuth(ADR), OCI Always Free로 인프라 비용을 0원에 맞춘 선택(ADR), Gemini Flash 무료 티어 + 모델 라우팅 + 캐싱으로 AI 비용을 통제하는 전략, 그리고 첫 POC smoke(무신사 100개 표본 비교 매칭 89%, POC 게이트 80% 통과)까지, 결정과 막 시작된 실측을 정직하게 적었습니다.
Balruno MVP 후기
게임 밸런싱 스프레드시트 + 문서 워크스페이스 Balruno의 백엔드 설계와 운영을 한 글에 정리합니다. PostgreSQL JSONB 채택(50,000 시트 환경에서 MySQL/PG/Mongo 직접 측정 — Sheet GET p95: PG 16ms / MySQL 25ms / Mongo 45ms, Name UPDATE p95: Mongo 37ms / PG 40ms / MySQL 63ms. 쓰기만 보면 MongoDB가 조금 빨랐지만, 한정된 인프라 안에서 DB를 둘로 나누지 않고 하나로 운영하는 편이 더 합리적이라고 판단해 PostgreSQL 선택), 시트 셀 + 시트 트리 + 문서 트리 3영역 통합 동기화 알고리즘(Baserow + Linear + Outline 합본), OCI Always Free 4대 + Ansible 자동화 + Cloudflare R2 3-2-1 백업으로 매니지드 대비 예상 회피 비용 연 약 $1,860, OAuth-only + 자체 발급 JWT(Auth0 대비 연 약 $2,880), Grafana + Loki + Alloy + Prometheus + InfluxDB 셀프 호스트 모니터링(Datadog 대비 연 약 $720), nginx blue/green 무중단 배포(첫 cutover 21초 → 두 번째부터 0초), 시트 도메인 100% 서버 진실원 전환(약 80,000 라인 정리)까지 포함합니다.
데이터베이스 인덱스 ⑥: 운영과 한계
인덱스 시리즈 마무리. 운영 환경의 인덱스 작업은 DDL이 DB를 멈출 수 있다는 사실에서 출발해요. CREATE INDEX CONCURRENTLY의 4단계 phase와 제약, 장기 트랜잭션이 클러스터 전반의 VACUUM/IOS에 미치는 영향, 인덱스 bloat과 REINDEX, 수십억 행을 위한 파티셔닝/샤딩, Bloom Filter, 그리고 6가지 안티패턴까지 1차 자료 기준으로 정리합니다.
데이터베이스 인덱스 ⑤: 클러스터형 인덱스와 DBMS별 차이
PostgreSQL의 heap-organized 모델과 MySQL InnoDB의 clustered index 모델은 근본적으로 다른 세계관이에요. 같은 SQL이라도 저장 구조에 따라 plan과 비용이 전혀 달라지고, secondary index 동작·PK 선택 전략·DBMS 이전 시 함정이 모두 달라집니다. PG/MySQL/SQL Server/Oracle 비교를 1차 자료 기준으로 정리.
데이터베이스 인덱스 ④: 복합 인덱스와 좌측 컬럼 규칙
복합 인덱스는 컬럼들의 정렬 순서를 그대로 B-tree에 반영한 자료구조예요. 같은 세 컬럼이라도 순서를 바꾸면 전혀 다른 인덱스가 되고, leftmost prefix rule이 활용 방식을 결정합니다. ESR Rule(Equality·Sort·Range) 가이드라인, PG18 skip scan, AND vs OR, INCLUDE와의 차이까지 1차 자료 기준으로 정리.
데이터베이스 인덱스 ③: Covering Index와 Index-Only Scan
plan에 Index Only Scan이 잡혔다고 진짜 IOS는 아니에요. PostgreSQL의 IOS는 covering(쿼리 컬럼이 인덱스에) + visibility(VM all-visible) 두 단계 조건을 모두 만족해야 Heap Fetches가 0이 됩니다. INCLUDE 절은 covering을, VACUUM은 visibility를 충족시키는 도구. INCLUDE의 leaf-only 저장 메커니즘과 인덱스 타입별 IOS 지원, 그리고 PG12 이전 insert-only Mandrill 함정까지.
데이터베이스 인덱스 ②: 스캔의 종류와 옵티마이저의 선택
인덱스가 있다고 모든 쿼리가 같은 방식으로 그 인덱스를 쓰는 건 아니에요. PostgreSQL의 Sequential / Index / Index-Only / Bitmap Scan 4가지 전략과, 옵티마이저가 통계 기반 셀렉티비티 추정으로 그 중 하나를 고르는 메커니즘을 1차 자료 기준으로 정리합니다. correlation, work_mem, BitmapAnd, Index Cond vs Filter까지.