소개
안녕하세요! 이 블로그를 운영하고 있는 개발자입니다.
개발하면서 배운 것들을 기록하고 공유하기 위해 이 블로그를 시작했습니다.
기술 스택
- 이 블로그는 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), "번 만큼만 크롤링한다"는 3-path 전략과 법적 안전(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까지.