프론트엔드/크롬확장 코드에 박힌 매직 넘버가 버그가 되는 순간: 사이드 프로젝트 MAX_RECENT=25 제거기 아무도 기억 못 하는 상수 `MAX_RECENT = 25`가 사용자에겐 "사라지는 탭" 버그였습니다. 왜 25였는지 추적했고, 답이 "그냥"이어서 지웠습니다. 임의의 제한이 코드 속에서 자라나 버그로 바뀌는 과정을 정리합니다.
프론트엔드/크롬확장 단일 크롬 확장에서 멀티테넌트 구현하기: Space Isolation 패턴과 30개 메시지 핸들러 스코핑기 하나의 크롬 확장 안에 여러 격리된 데이터 공간을 두려면 메시지 핸들러마다 "현재 공간" 개념을 주입해야 합니다. 30개 가까운 핸들러에 Space Isolation 패턴을 녹이고, cross-page sync를 깨지 않으면서, 리팩토링 부수 효과로 dead code까지 정리한 경험을 정리했습니다.
백엔드/인증-보안 Hono + Cloudflare Workers + D1로 라이선스 서버 만들기: Lemon Squeezy 웹훅 완전 가이드 사이드 프로젝트에 Pro 라이선스를 붙이려면 서버가 필요합니다. Cloudflare Workers + Hono + D1로 주말에 구축한 라이선스 서버, 그리고 Lemon Squeezy 웹훅 이벤트 순서 함정으로 하루를 날린 이야기를 공유합니다.
프론트엔드/크롬확장 크롬 확장에 위장 공간 만들기: AES-GCM과 티어드 패딩으로 Plausible Deniability 구현 단순 암호화는 내용만 숨기고 존재는 드러냅니다. 크롬 확장에서 Plausible Deniability를 달성하기 위해 AES-GCM 고정 슬롯, 티어드 패딩, CSPRNG, 그리고 Auto-Timeout UX까지 네 레이어를 쌓은 실전 사례를 공유합니다.
도구/GitHub 머지와 배포 분리하기: main push 대신 tag push로 production 배포하기 태그를 release artifact로 취급하고 머지 이벤트와 릴리스 이벤트를 분리한 GitHub Actions CI/CD 구성을 정리했습니다.
백엔드/API설계 Subscription 모델링: 왜 planId는 불변이어야 하는가 (trialPlanId 분리 패턴) Trial은 구독의 한 종류가 아니라 구독 위에 얹힌 임시 레이어입니다. planId를 불변으로 유지하고 trialPlanId로 기능을 덧칠한 설계 이야기입니다.
백엔드/API설계 분산 트랜잭션이 없을 때: Payment Provider와 DB의 cancelFailed 롤백 패턴 DB + PG의 다단계 연산은 원자적이지 않습니다. Saga 보상 대신 마커를 저장하고 cron이 자동 복구하는 패턴으로 해결했습니다.
백엔드/API설계 스케줄 결제의 숨겨진 덫: PENDING Invoice를 먼저 만들어야 하는 이유 webhook을 Invoice 생성 트리거로 쓰면 세 가지 문제가 터집니다. 스케줄 등록 시점에 PENDING Invoice를 미리 만드는 구조로 해결한 방법을 정리했습니다.
백엔드/인증-보안 OAuth와 이메일 가입을 하나의 온보딩 플로우로 통합하기 계정 생성과 조직 설정을 분리하면 OAuth·이메일 두 가입 경로가 하나의 온보딩 페이지로 모입니다. 미들웨어 한 줄이 이 통합을 가능하게 합니다.
백엔드/인증-보안 NextAuth v5 JWT 콜백: OAuth와 Credentials 사용자 한 곳에서 분기하기 OAuth 경로에서는 user 객체에 회사 관계가 없어 토큰 초기화에 실패합니다. account.type 분기와 추가 DB 조회로 해결한 방법을 정리했습니다.
백엔드/인증-보안 NextAuth PrismaAdapter + 암호화된 이메일: Blind Index 커스텀 Adapter OAuth 추가 후 getUserByEmail이 null을 반환하는 조용한 실패를 추적해, Prisma Extension과 Blind Index를 활용한 커스텀 Adapter로 해결한 사례입니다.
프론트엔드/Next.js OG 이미지 구현의 두 전환점: 단순한 답이 이긴 두 번의 리팩토링 OG 이미지를 구현하면서 같은 영역을 두 번 리팩토링했습니다. 두 번 모두 "영리해 보이는" 첫 설계를 버리고 "덜 특별한" 두 번째 설계를 택했고, 두 번 모두 그게 맞았습니다. 공통 패턴과 의사결정 기준을 정리합니다.
프론트엔드/Next.js Next.js i18n 미들웨어에서 OG 이미지가 안 보이는 이유: redirect vs rewrite 완벽 가이드 다국어 사이트에서 루트 경로의 OG 이미지가 안 보이는 이유는 next-intl 미들웨어의 307 redirect 때문입니다. 크롤러 UA 감지 대신 모든 요청을 rewrite하여 해결하는 방법을 다룹니다.
프론트엔드/Next.js Next.js opengraph-image에서 한글이 깨지는 이유: Satori 폰트 로딩 완벽 가이드 Next.js `ImageResponse`가 한글을 빈 사각형으로 렌더링하는 이유는 Satori가 OS 폰트를 읽지 않기 때문입니다. `fonts` 배열에 Noto Sans KR을 주입하는 정확한 방법과, 정적 PNG로 전환하는 프래그매틱 대안을 다룹니다.
도구/Obsidian window.confirm() 대신 두 번 클릭 패턴: Obsidian 플러그인에서 안전한 삭제 UX 만들기 Obsidian 플러그인의 Settings 탭에서 window.confirm()을 쓰다가 Linux에서 dialog가 차단되는 문제를 만났습니다. 그리고 credential 삭제는 확인 없이 즉시 삭제되는 더 큰 문제도 있었죠. 두 문제를 한 번에 해결하는 two-step delete 패턴과 destructive action UX의 일반 원칙을 정리했습니다.
도구/GitHub Obsidian 플러그인 릴리스에서 styles.css가 사라지는 이유: GitHub Actions 워크플로우의 빠진 한 줄 커스텀 summary card 디자인을 새로 도입하면서 styles.css가 생겼는데, release.yml의 gh release upload 커맨드에 파일명을 빠뜨려서 GitHub Release asset에서 누락됐습니다. 사용자에게는 "스타일이 안 보인다"는 버그로 나타났고, 2줄 fix로 해결했습니다. Obsidian 플러그인 release workflow 체크리스트도 함께 정리.
AI/Claude Code 소스와 테스트를 grep 한 줄로 연결하기: @tested / @covers 양방향 마커 시스템 코드베이스가 커지면 "이 파일에 테스트가 있나?" "이 테스트가 뭘 커버하나?"를 추적하는 비용이 커집니다. JSDoc에 @tested와 @covers 마커를 넣어 소스·테스트 간 양방향 링크를 구축하면, grep 한 줄로 커버리지 맵을 얻을 수 있습니다. 실제 Obsidian 플러그인 프로젝트에 적용한 사례.
도구/Obsidian E2E 스위트가 하나로 부족해진 날: Obsidian 플러그인에 두 번째 CDP 테스트 추가하기 첫 번째 CDP E2E 스위트 글의 속편. multi-config 리팩토링에서 Settings UI를 검증하기 위해 두 번째 E2E 스위트를 추가했고, 그 과정에서 51줄의 CDP 인프라 코드를 공유 모듈로 추출했습니다. 두 스위트를 하나의 Obsidian 인스턴스에서 돌리는 격리 규칙과 공유 경계 설계까지.
도구/Obsidian Singleton을 Per-Instance로 바꾼 뒤 찾아온 메모리 누수: 16줄로 잡은 Orphaned RateLimiter Obsidian 플러그인의 multi-config 리팩토링 중 발견한 조용한 메모리 누수: config를 삭제해도 그 config가 쓰던 RateLimiter가 공유 Map에 그대로 남았던 버그의 원인과 3파일 16줄짜리 fix. Per-instance 서비스 전환 시 주의해야 할 4가지 cleanup 함정을 정리했습니다.
도구/Obsidian Obsidian 플러그인을 Multi-Database로 리팩토링하기: DatabaseProvider 추상화와 FieldType 정규화 Airtable에 강결합돼 있던 Obsidian 플러그인 서비스 레이어를 provider-agnostic DatabaseProvider 인터페이스로 추상화한 2단계 리팩토링(PR #49, #50) 기록. 런타임 capability flag, 5-way discriminated union credential, 29→11 StandardFieldType 정규화까지 395/395 테스트를 유지하며 진행한 설계 결정을 공유합니다.
도구/Obsidian 하나의 Obsidian Vault에서 여러 Airtable 동시 싱크: Multi-Config 아키텍처 구현기 Airtable 플러그인을 단일 config에서 multi-config 멀티테넌트 구조로 확장한 경험. ConfigInstance와 ConfigManager의 역할 분리, credential-scoped RateLimiter 공유, defensive + idempotent 마이그레이션, 폴더 겹침 검증, Obsidian command palette의 dynamic re-registration까지.
자동화/블로그 Ghost MCP v1.0.1 업데이트기: 프로토타입에서 오픈소스 도구로 — 무엇이 달라졌나 v1.0.0 공개 후 2개월. "내 블로그용 스크립트"를 "오픈소스 도구"로 끌어올린 변화 — 테스트, 보안, 유연성, 온보딩 네 가지 영역의 성숙화를 한눈에 정리합니다.
AI/도구활용 양방향 링크 시스템을 테스트 커버리지에 확장하기: @tested / @covers 마커 패턴 문서↔코드 양방향 링크 시스템을 테스트 영역에 확장한 후속편. @tested / @covers 마커 설계, 3계층 테스트 커버리지 선언, CI 검증 스크립트까지 정리합니다.
백엔드/API설계 마크다운 3포맷 자동 감지 파서 설계: legacy, frontmatter, plain을 한 파서로 기존 사용자의 레거시 포맷을 깨지 않으면서 표준 frontmatter와 일반 마크다운까지 지원해야 했습니다. 감지 순서, fallback 설계, 그리고 strategy 패턴을 포기한 이유를 정리합니다.
백엔드/인증-보안 MCP 서버 보안: LLM이 생성한 파일 경로를 18줄로 막는 Path Traversal 방어 MCP 서버는 로컬 권한과 LLM 입력이 만나는 새로운 공격 표면입니다. Path Traversal, prefix 우회, symlink 공격을 18줄의 검증 함수로 막는 방법과 그 뒤의 설계 원칙을 정리합니다.