Today I Learned

날짜별 업무 및 학습 회고입니다. 매일 무엇을 했고, 무엇을 배웠는지 기록합니다.

2026년 10월

2026-10-01

'관련 테스트만 돌린다'는 그래프를 못 믿을 때 전체로 돌아가는 규칙이 있어야 안전하다

병렬 작업마다 전체 단위 테스트를 반복하던 푸시 훅과 PR CI를 변경 기반 선택 실행으로 바꾸면서, 기준점을 추측하지 않는 법과 삭제·전역 설정 변경을 전체 실행으로 보내는 규칙을 정리한 기록

  • 변경 기반 테스트 선택은 import 그래프의 정확도에 기대는 최적화라서, 그래프를 못 믿는 변경(삭제·이름 변경·전역 설정)은 전체 실행으로 흘려보내는 규칙이 같이 있어야 한다
  • 기준점은 추측하지 않는다. CI는 PR의 base 커밋, 푸시 훅은 Git이 넘겨 주는 이전 원격 SHA를 쓰고, 새 브랜치는 사람이 명시하게 한다

2026년 9월

2026-09-25

두 키를 같이 두 번 눌렀더니 한 방향으로만 대시했다 — 첫 이벤트에서 확정하지 않기

브라우저가 동시에 누른 키도 keydown 이벤트로 따로 보내기 때문에, 첫 키에서 방향을 확정하면 두 번째 키가 합쳐지지 못한다는 것과 50ms 수집창으로 푼 방법

  • 브라우저는 동시에 누른 키도 키마다 keydown 이벤트를 따로, 순서를 가지고 보낸다
  • 첫 키 이벤트에서 결과를 확정하면 이벤트 사이에 프레임이나 시뮬레이션 스텝이 끼는 순간 두 번째 키가 합쳐지지 못한다
2026-09-18

잠금으로 한 줄씩만 처리하는 서버 앞에는 큐보다 워커 안의 지터 재시도가 먼저다

동시에 제출된 폼 몇 건이 '지금 바쁨'으로 실패하던 문제를 큐와 워커로 분리하면서, 큐 재배달만으로는 경합이 안 풀리고 예외를 ack하면 조용히 유실된다는 걸 확인한 기록

  • 상대가 잠금으로 한 줄씩만 처리하면 큐의 재배달만으로는 경합이 안 풀린다. 재배달도 같은 시각에 몰려 다시 충돌하므로, 워커 안의 무작위 지터 재시도가 먼저고 큐의 백오프는 장애용이다
  • 이미 '접수 완료'라고 답한 뒤에는 워커가 예상 못 한 예외를 ack하면 안 된다. ack는 곧 조용한 유실이다

2026년 8월

2026-08-23

재사용 가능한 표 컴포넌트를 타입으로 잠그기

컬럼 배열에서 정렬 키 union을 파생하고, 도메인마다 다른 행 타입을 합성 컴포넌트 조각들까지 전달하면서 배운 것 — 그리고 그 과정에서 계속 튀어나온 변성 이야기

  • 값에서 타입을 파생하는 문법과 const 타입 파라미터는 세트다. 하나가 빠지면 나머지도 에러 없이 조용히 무의미해진다
  • const 선언에는 타입 파라미터를 못 붙이므로, 제네릭 context는 함수 클로저 안에서 만들어야 한다
2026-08-14

구현보다 정답 기준을 먼저 잠그기

코드 생성 도구에게 작업을 맡길 때 판정 기준을 문서로 먼저 못 박고, 코드·화면·리뷰 의견을 증거로 강등하며, 확신이 부족하면 멈추게 만드는 설계를 정리했다

  • 판정 기준의 출처를 사람과 승인된 명세로 제한하면 구현을 스펙으로 오해하는 실패가 사라진다
  • 완료의 정의를 테스트 통과 뒤로 옮기면 assertion을 느슨하게 만들 이유가 없어진다
2026-08-09

브라우저 확장의 요청 응답과 변경 알림을 분리해 추적하기

분리된 확장 실행 공간에서 메시지 기반 RPC로 저장을 요청하고, 변경 신호와 쿼리 무효화로 다른 화면이 단일 원본을 다시 읽게 한 흐름을 정리했다

  • Chrome 메시지 API는 전송 수단이고 요청 검증·라우팅·오류 응답은 애플리케이션이 정의한다
  • 명령 응답과 다른 화면의 변경 알림은 목적이 다르므로 분리한다
2026-08-07

중간 계층은 무엇을 통과시키고 무엇을 추측하는가 — 프록시·입력 확정·이미지 비율

rewrite가 업스트림 3xx를 그대로 흘려 만든 무한 리다이렉트, 조합이 끝나기 전 확정된 한글 입력, 16:9로 추측하던 이미지 비율 — 중간 계층의 통과와 추측을 점검하며 배운 것

  • rewrite는 리다이렉트가 아니라 프록시라 업스트림의 3xx를 그대로 브라우저에 전달한다
  • 한글 입력 버그는 값 정규화가 아니라 커밋 경계 문제로 먼저 분리한다

2026년 7월

2026-07-31

구형 브라우저 호환은 babel 하나가 아니다 — 문법·폴리필·조건 로드·스모크의 다층 계약

5년 넘은 구형 브라우저에서 앱이 통째로 죽는 버그를 고치며 배운 것 — 여러 기능이 동시에 죽으면 하이드레이션을 의심하고, 문법 다운레벨과 런타임 폴리필을 분리하고, 폴리필은 feature-detect로 옛 브라우저에만 로드하고, 스모크는 실제 인터랙션을 실행한다

  • 여러 UI 기능이 동시에 죽으면 개별 버그가 아니라 하이드레이션/번들 파싱 같은 공통 근본원인을 먼저 의심한다
  • 문법 다운레벨(babel)과 런타임 API(폴리필)는 별개 층이다 — 트랜스파일러는 문법은 바꿔도 없는 함수는 만들지 않는다
2026-07-25

첫 페인트 스플래시와 오프라인 퍼스트 — ISR을 지키며 즉시 반영하기

네이티브 앱 같은 로딩 스플래시를 첫 페인트부터 띄우되 봇엔 숨기는 인라인 스크립트 기법, 그리고 오프라인 퍼스트에서 '내 쓰기 즉시 반영'을 staleTime이 아니라 invalidate와 durable 큐로 만드는 법

  • 첫 페인트 전 노출 + 봇 은닉은 인라인 블로킹 스크립트 주입 — UA 게이트는 headers()로 ISR을 dynamic으로 깨뜨린다
  • react-query에서 '내 쓰기 즉시 반영'은 staleTime을 낮추는 게 아니라 쓰기 후 invalidateQueries로 만든다(invalidate는 staleTime을 무시)
2026-07-24

LCP 병목이 이미지가 아니라 hydration일 때, 그리고 절제된 스크롤 모션

랜딩 성능을 개선하다 배운 것 — LCP가 느린데 이미지가 작으면 render delay(hydration)를 의심하고 클라이언트 JS를 빼는 것, 화려한 스크롤 모션보다 진입 스태거 리빌, 구조화 데이터는 화면과 1:1

  • LCP가 느린데 TBT가 0이고 이미지가 작으면 element render delay(=클라 경계 hydration)를 의심하고 그 서피스의 클라이언트 JS를 뺀다
  • 화려한 스크롤 종속 모션(scroll-jacking)보다 짧은 뷰포트 진입 스태거 리빌이 범용 정답인 경우가 많다
2026-07-23

간헐적으로 깨지는 이미지 — objectURL 레이스, WebP 폴백, 서비스워커 셸

새로고침마다 보였다 안 보였다 하는 이미지 버그를 추적하다 배운 세 가지 — objectURL 수명 레이스, 브라우저 전용 lib의 테스트 hang 방지, 서비스워커 셸 폴백을 navigation 요청으로 한정하기

  • 서버가 URL로 주는 이미지는 objectURL blob으로 감싸지 말고 직접 렌더한다 — createObjectURL 수명 레이스를 원천 제거
  • 브라우저 전용(canvas) lib를 테스트 경로에 넣을 땐 실행 전 동기 capability 게이트로 jsdom hang을 막는다
2026-07-22

무한 리스트 기능을 코드 없이 옮기기 — 클린룸 계약과 keyset 페이지네이션

가상화 무한 리스트+스크롤 복원 기능을 다른 스택으로 옮기며 배운 것 — 구현 코드가 아니라 겉보기 행동을 계약으로 적는 클린룸 방식, 그리고 무한 스크롤의 안정성을 좌우하는 offset vs keyset 페이지네이션

  • 기능을 옮기는 문서는 "우리가 쓴 라이브러리"가 아니라 "겉으로 어떻게 행동하고 실패 시 무엇을 보장하나"를 계약으로 적는다
  • 무한 스크롤 리스트의 안정성은 대부분 페이지네이션 방식에서 온다 — offset은 삽입/삭제 시 중복·누락, keyset은 값 기준이라 안정
2026-07-20

공개 표면·분석 파사드·모션 게이트에서 배운 것

구조화 데이터와 가시 FAQ 일치, auth 해소 후 fetch 게이트, 분석 이벤트 팬아웃 파사드, prefers-reduced-motion JS 전환 게이트를 범용 개념으로 정리한 TIL

  • 구조화 데이터(FAQPage 등)는 화면에 보이는 텍스트와 같은 소스에서 나와야 한다
  • 보호 API는 세션 로딩이 끝난 뒤에만 켜고, 로딩 중 !user만으로 익명·fetch를 결정하지 않는다
2026-07-14

개발 환경 정비에서 만난 범용 개념들: 셸 인용·모노레포 배포·CI 러너·가드 설계

도구·인프라를 정비하며 마주친 셸 단어 분리, workspace 배포 재작성, CI 러너 불일치, fail-open 가드를 범용 개념으로 정리한 TIL

  • 셸 변수에 담긴 경로는 공백이 있으면 인용 없이 쓸 때 여러 인자로 쪼개져 명령이 조용히 깨진다
  • 모노레포 내부 의존은 workspace 프로토콜로 링크하고 배포 시 실제 버전으로 치환하는 매니저로 올려야 한다
2026-07-13

별빛 그래프·SW 캐시·Playwright triage·ESLint guardrail

Canvas 별 노드, Service Worker SWR·generation 보안, CI flaky 분류, 디자인시스템 ESLint guardrail을 하루에 정리한 TIL

  • Canvas 노드 장식은 ID 해시로 안정화하고 globalAlpha·shadowBlur는 그리기 직후 반드시 복구한다
  • Playwright CI 빨간색은 retry 결과로 flaky와 deterministic failed를 먼저 갈라야 한다
2026-07-12

JSON-LD·3국어 i18n·문서 그래프 — 하루에 맞춘 콘텐츠 파이프라인

검색 노출용 JSON-LD 리치화, .{locale}.md 파이프라인, next-intl Link 수정, BFS 로컬 그래프, knowledge publish까지 한 축으로 정리한 TIL

  • 다국어는 번역뿐 아니라 id=`type:slug:locale` 포맷을 DB·검색·sitemap 전 구간에서 grep해야 한다
  • localePrefix always에서는 내부 이동에 plain next/link를 쓰면 bare href가 /ko로 301된다 — 예외 없이 i18n Link
2026-07-11

홈 히어로 scroll-driven 내러티브 연출

GSAP 없이 sticky scrolly·CSS keyframes·view-timeline으로 TIL→Knowledge 승격 데모와 파이프라인 섹션을 구현한 하루

  • sticky scrolly는 래퍼 높이만큼 스크롤 시간축을 만들고, phase는 진행도에서 파생하면 JS가 얇아진다
  • 히어로 라이브 데모는 clip-path steps 와이프 + animation-delay만으로 루프를 만들 수 있다
2026-07-10

Modal peek 스택과 CSS layer 패키지

단일 dialog 안 document push와 @layer 기반 editor CSS 분리로 peek UX·테마 커스터마이징을 맞춘 하루

  • gallery→peek는 dialog를 새로 열기보다 같은 shell에서 documentId만 바꾸는 편이 연속적이다
  • async resolve는 (newId, oldContent) 중간 프레임을 render-time reset으로 막아야 한다
2026-07-09

계층형 문서 네비와 viewer 번들 분리

page push/peek navigator와 read-only viewer entry 분리로 포트폴리오 데모 깊이와 번들을 동시에 맞춘 하루

  • 문서 그래프는 page 블록 + push/peek navigator 계약으로 flat editor와 분리하는 편이 낫다
  • peek·embed는 editor와 DOM은 같아도 import graph가 달라야 viewer 번들이 작아진다
2026-07-08

모바일 포인터 제스처와 서버 이미지 프록시

터치 드래그·리사이즈 안정화와 unoptimized 환경에서 이미지 프록시로 전송 바이트를 줄인 하루

  • 터치로 끌어도 페이지가 같이 스크롤되지 않게 드래그 동작을 손봤다
  • 이미지는 원본 통째가 아니라 필요한 크기만 보내도록 프록시를 붙였다