All Posts

27 articles
주간 cron이 PR을 올리는 구조를 완성했다
기타 · DevOps

주간 cron으로 미사용 키를 탐지·삭제하고 PR을 자동 생성하는 파이프라인. 안전 가드, 스모크 체크리스트, 향후 모니터링까지.

April 13, 2026
I18N_KEYS를 직접 읽는 스크립트를 만들었다
기타 · DevOps

I18N_KEYS를 직접 읽는 스크립트를 만들었다

I18N_KEYS를 진실원으로 삼는 AST 수집기의 설계. namespace 해석, t() 키 수집, 화이트리스트, --apply와 30% 안전 가드까지.

April 13, 2026
공식 추천 플러그인이 네임스페이스를 통째로 지우려 했다
기타 · DevOps

공식 추천 플러그인이 네임스페이스를 통째로 지우려 했다

i18next-parser 아카이브 이후 공식 추천 플러그인 i18next-cli를 검토했지만, I18N_KEYS 간접 참조에서 네임스페이스를 통째로 삭제하려는 실측 오판을 확인하고 커스텀 스크립트로 전환한 과정.

April 13, 2026
네임스페이스를 상수 객체로 관리하자
기타 · DevOps

네임스페이스를 상수 객체로 관리하자

react-i18next에서 네임스페이스 문자열을 I18N_KEYS 상수 객체로 관리하는 이유. JSON·i18n.ts와의 정합성, 오타 방지, 그리고 공식 추천 플러그인과의 트레이드오프.

April 13, 2026
v0에서 v1, 번역 키가 쌓일 때
기타 · DevOps

v0에서 v1, 번역 키가 쌓일 때

v0에서 v1으로 넘어가면서 미사용 키와 새 키가 동시에 불어난 상황과, 자동 삭제를 시도하기 전에 팀에서 "안전하게 지운다"를 먼저 정의한 과정.

April 13, 2026
React Router는 자체 ErrorBoundary를 갖고 있다
프론트엔드 · React

React Router는 자체 ErrorBoundary를 갖고 있다

ErrorBoundary를 감쌌는데 에러가 안 잡히는 상황. React Router가 내부에서 먼저 에러를 잡고 있었고, errorElement로 해결한 과정을 공유한다.

April 9, 2026
모바일 · Flutter

시뮬레이터에선 되던 앱이 실기기에서 터진 이유

Flutter 시뮬레이터에서 잘 되던 API 호출이 실기기에서 Connection refused, localhost가 가리키는 곳이 달랐다. 원인을 찾고 device_info_plus로 자동 감지까지 구현한 과정.

April 9, 2026
Orval 코드 생성 시 500개 파일 diff가 생긴다면
설계 · 설계와 구조

Orval 코드 생성 시 500개 파일 diff가 생긴다면

Orval이 생성하는 파일 헤더의 OpenAPI spec version 때문에 500개 파일에 불필요한 diff가 생기는 문제를, 커스텀 header 함수로 해결한 경험.

April 1, 2026
Claudian으로 블로그 글 리뷰를 맡기기까지
기타 · 블로그 개발

Claudian으로 블로그 글 리뷰를 맡기기까지

Obsidian 안에서 Claude Code를 쓸 수 있는 Claudian 플러그인을 발견하고, 설치부터 블로그 글 리뷰 워크플로우까지 녹여낸 경험을 정리했습니다.

March 27, 2026
글쓰기 도구와 코드가 섞여 있던 블로그를 정리한 이야기
기타 · 블로그 개발

글쓰기 도구와 코드가 섞여 있던 블로그를 정리한 이야기

Next.js 블로그에서 콘텐츠를 app 폴더 밖으로 분리하고, Obsidian vault로 열어 글 전용 환경을 만든 과정을 정리했습니다.

March 27, 2026
Obsidian 이미지를 Next.js 블로그에서 렌더링하기 - 커스텀 remark 플러그인
기타 · 블로그 개발

Obsidian 이미지를 Next.js 블로그에서 렌더링하기 - 커스텀 remark 플러그인

Obsidian의 ![[이미지.png]] 문법이 MDX에서 깨지는 문제를 커스텀 remark 플러그인으로 해결한 과정

March 25, 2026
파일 업로드 리팩토링기 #5. 50MB를 올리고 나서야 비율이 안 맞는 걸 알았다
프론트엔드 · 사용자 경험

파일 업로드 리팩토링기 #5. 50MB를 올리고 나서야 비율이 안 맞는 걸 알았다

메타데이터 검증으로 충분하다고 생각했지만, PDF 페이지 비율 문제를 만나면서 콘텐츠 검증이라는 새로운 레이어가 필요해졌습니다. 함수 주입 vs 설정 기반, 동기 vs 비동기 경계를 설계한 과정을 다룹니다.

March 25, 2026
파일 업로드 리팩토링기 #4. 에러가 나도 파일이 남는 훅
설계 · 설계와 구조

파일 업로드 리팩토링기 #4. 에러가 나도 파일이 남는 훅

3편에서 설계한 인터페이스를 구현합니다. 에러 복구, 업로드 진행률, 동시성 제어, 취소, 확정 통합까지 v2의 핵심 구현을 다룹니다.

March 24, 2026
파일 업로드 리팩토링기 #3. 훅의 책임을 어디까지 넓힐 것인가
설계 · 설계와 구조

파일 업로드 리팩토링기 #3. 훅의 책임을 어디까지 넓힐 것인가

v1이 업로드만 래핑했던 한계를 넘어, 어떤 API를 훅에 포함하고 어떤 API는 제외할지 판단 기준을 세우고, v2의 인터페이스와 상태 모델을 설계합니다.

March 24, 2026
파일 업로드 리팩토링기 #2. 에러 나면 파일이 사라지는 훅
설계 · 설계와 구조

파일 업로드 리팩토링기 #2. 에러 나면 파일이 사라지는 훅

업로드 상태를 통합하기 위해 만든 커스텀 훅이, 에러 시 파일을 Map에서 바로 삭제해버리는 구조였습니다. v1의 설계를 분석하고 한계를 정리합니다.

March 24, 2026
파일 업로드 리팩토링기 #1. 같은 API, 다른 에러 처리 5곳
설계 · 설계와 구조

파일 업로드 리팩토링기 #1. 같은 API, 다른 에러 처리 5곳

하나의 파일 API를 5개 컴포넌트에서 각각 useMutation으로 정의하고, 에러 처리가 제각각인 문제를 정리합니다.

March 23, 2026
배포했는데 왜 안 바뀌죠 - Version Polling으로 SPA 업데이트 알림 배너 만들기
프론트엔드 · 사용자 경험

배포했는데 왜 안 바뀌죠 - Version Polling으로 SPA 업데이트 알림 배너 만들기

SPA에서 배포 후에도 사용자 화면이 갱신되지 않는 문제를, Service Worker 없이 version.json 폴링과 Cache API로 해결한 과정.

March 20, 2026
백엔드 에러 메시지가 화면에 그대로 나왔다 - i18n 키 매핑과 fallback으로 에러 UI 설계하기
설계 · 에러 다루기

백엔드 에러 메시지가 화면에 그대로 나왔다 - i18n 키 매핑과 fallback으로 에러 UI 설계하기

API 에러 응답의 detail 메시지가 사용자 화면에 그대로 노출되는 걸 발견하고, HTTP 상태 코드 기반 i18n 키 매핑과 배열 키 fallback으로 에러 화면을 선언적으로 관리한 과정.

March 13, 2026
에러 타입을 지정하니 인터셉터의 빠진 경로 3개가 드러났다
설계 · 에러 다루기

에러 타입을 지정하니 인터셉터의 빠진 경로 3개가 드러났다

Register로 에러 타입을 ApiError로 지정한 뒤 인터셉터를 다시 보니, 4개 에러 경로 중 3개가 AxiosError를 그대로 reject하고 있었다. 타입을 제대로 설계했기 때문에 보이게 된 구멍을 메운 과정.

March 10, 2026
매번 instanceof를 쓰고 있다면 — TanStack Query Register로 에러 타입 지정하기
설계 · 에러 다루기

매번 instanceof를 쓰고 있다면 — TanStack Query Register로 에러 타입 지정하기

useQuery의 error가 항상 Error로 추론되어 매번 instanceof 타입 가드를 쓰고 있었다. TanStack Query v5의 Register 인터페이스로 프로젝트 전체의 에러 타입을 바꾼 과정.

March 9, 2026
Zustand scoped store #3. 유틸리티, 직접 만들어 쓰기
설계 · 설계와 구조

Zustand scoped store #3. 유틸리티, 직접 만들어 쓰기

zustand/context가 사라진 v5에서, GitHub Discussion과 TkDodo 블로그를 참고해 ~40줄의 createZustandContext 유틸리티를 설계한 과정과 각 결정의 이유

March 13, 2026
Zustand scoped store #2. React Context로 지역 store 만들기
설계 · 설계와 구조

Zustand scoped store #2. React Context로 지역 store 만들기

create()와 createStore()의 차이를 이해하고, React Context와 조합해 컴포넌트 단위로 격리된 store를 직접 구현합니다.

March 13, 2026
Zustand scoped store #1. 글로벌 store로는 안 되는 것들
설계 · 설계와 구조

Zustand scoped store #1. 글로벌 store로는 안 되는 것들

전역상태를 남용하지 말라는 이야기는 많이 들어봤지만, 정작 왜 그런지를 Zustand 맥락에서 구체적으로 짚은 글은 드뭅니다. 글로벌 store의 세 가지 한계에서 출발해 scoped store가 필요한 이유를 정리합니다.

March 13, 2026
F12 누르면 비밀번호가 보인다 - 프론트엔드에서 SHA-256 해싱으로 로그인 평문 노출 막기
기타 · 보안

F12 누르면 비밀번호가 보인다 - 프론트엔드에서 SHA-256 해싱으로 로그인 평문 노출 막기

로그인 보안 태스크를 받고, SHA-256 해싱을 처음 접하며 하나씩 질문하고 답을 찾아간 과정을 공유합니다.

March 11, 2026
런타임에 변하는 한글 - i18next에서 조사를 자동으로 선택하기
프론트엔드 · 사용자 경험

런타임에 변하는 한글 - i18next에서 조사를 자동으로 선택하기

{{name}}에 어떤 단어가 올지 모르는데, 을/를은 어떻게 정하지? i18next post-processor로 번역 키 하나만으로 조사를 자동 선택한 과정.

February 27, 2026
프론트엔드 · 타입과 언어

TypeScript는 왜 내 코드를 의심할까

TypeScript는 왜 내 코드를 의심할까

April 12, 2024
프론트엔드 · 타입과 언어

if문 하나로 TypeScript가 똑똑해지는 이유

if문 하나로 TypeScript가 똑똑해지는 이유

April 9, 2024