본문으로 건너뛰기

모든 노트

45편

45편

성능 개선은 어디서 재야 보일까: 로컬에선 그대로, CI에선 25초

DOM을 안 쓰는 테스트 파일 121개가 파일마다 jsdom을 부팅하던 낭비를 Vitest 프로젝트 분리로 걷어냈습니다. 10코어 로컬에서는 wall time이 그대로였는데 4-vCPU CI에서만 실행당 25초가 줄어든 이유를 실측으로 따라갑니다.

2026-09-18 · 20분#vitest

캐시가 hit인데 매번 콜드 빌드였습니다 — GitHub Actions Next.js 캐시 키가 죽어 있던 이유

캐시 복원은 초록불이었고 67MB가 정상적으로 내려왔습니다. 그런데 빌드는 캐시가 아예 없을 때와 같은 4분이었습니다. hit 로그가 보증하지 않는 것 — 캐시가 거치는 두 단계, 그리고 캐시 키에서 구조적으로 빠지는 입력을 대조군 실측으로 확인합니다.

2026-08-16 · 17분#github-actions

매일 쓰던 getByRole 때문에 테스트가 26배 느렸습니다 — RTL·jsdom 성능 분석

매일 쓰던 getByRole이 사실은 트리 전체를 훑고 있었습니다. Meta 디자인 시스템에서 34초 걸리던 테스트 파일의 원인을 찾아 1.3초로 줄이기까지, RTL과 jsdom 내부에서 실제로 벌어지는 일을 따라가 봤습니다.

2026-07-31 · 33분#testing-library

TypeScript 6 업그레이드인 줄 알았는데, 문제는 "설정"이었습니다

TypeScript 6으로 올리자 baseUrl·rootDir·types 기본값이 차례로 빌드를 깨뜨렸다. 각 변경의 "왜"를 microsoft/TypeScript PR diff까지 추적해 보니, 버전을 올리는 이야기가 아니라 "올바른 설정"에 도달하는 이야기였다.

2026-07-02 · 36분#TypeScript

[Typescript로 설계하는 프로젝트] id?: string을 버려라. 상태가 다르면 타입도 달라야 한다.

id?: string 하나가 코드 전체에 옵셔널 체이닝과 느낌표를 퍼뜨립니다. 무분별한 옵셔널이 낳은 안티 패턴에서 벗어나, Tagged Type(Discriminated Union)과 도메인 모델링으로 상태마다 다른 타입을 주는 설계를 알아봅니다.

2026-03-16 · 12분#TypeScript

[누가 시키지도 않았는데 번들러 만들기] 4. 소스맵: 번들된 코드에서 원본의 흔적 찾기

수천 줄로 합쳐진 번들 파일에서 에러가 나면 어떻게 원본의 그 줄을 찾아갈까요? 소스맵의 VLQ 인코딩 원리와 magic-string이 변환 이력을 관리하는 방식을 직접 구현해 보며 이해합니다. 번들러를 손으로 만들어 본 시리즈의 마지막 편.

2026-03-03 · 31분#bundler

[누가 시키지도 않았는데 번들러 만들기] 3. 번들링과 스코프: 파일 합치기의 기술

단순히 파일을 이어붙이는 것과 번들링은 다릅니다. 같은 이름의 변수가 서로를 덮어쓰지 않도록 IIFE로 스코프를 가두는 전략과, 합쳐진 결과물이 브라우저에서 제대로 실행되게 만드는 런타임 구현 과정을 깊이 있게 다룹니다.

2026-02-23 · 15분#bundler

[누가 시키지도 않았는데 번들러 만들기] 2. 코드를 데이터로 보는 법 (AST Graph)

정규표현식으로 import 문을 찾으려다 실패한 경험, 그리고 AST(추상 구문 트리)를 통해 코드를 문자열이 아닌 데이터로 바라보는 관점을 소개합니다. 흩어진 파일들을 하나의 의존성 그래프로 바꾸는 과정을 직접 구현해봅니다.

2026-02-17 · 13분#bundler

[누가 시키지도 않았는데 번들러 만들기] 0. 프롤로그: 질문이 구현이 되는 순간

수많은 소스 코드가 어떻게 하나의 결과물로 합쳐질까요? 매일 쓰면서도 정작 안을 들여다본 적 없던 번들러의 블랙박스를 열고, 라이브러리 없이 직접 손으로 구현해 보며 그 답을 찾아가는 시리즈를 시작합니다. 첫 편은 왜 이걸 만들기로 했는지에 대한 이야기입니다.

2026-02-02 · 5분#bundler

[누가 시키지도 않았는데 번들러 만들기] 1. 개념과 도구: 번들러의 등장 배경

script 태그를 나열하던 시절부터 ESM까지, 코드를 번들링해야 하는 기술적 이유와 자바스크립트 모듈 시스템의 발전 과정을 살펴봅니다. 그리고 앞으로 만들 번들러의 기반이 될 Magic String 라이브러리를 소개합니다.

2026-02-02 · 8분#bundler

결제 시스템 리팩토링: if문 지옥에서 디자인 패턴(Factory, Adapter)으로 탈출하기

국가마다 결제 수단이 다른 시스템의 분기가 if문 지옥으로 자라났습니다. Adapter·Factory·Strategy 패턴으로 그 분기를 걷어낸 적용기와, Zod로 타입 안전성을 확보하고 Code Splitting까지 챙긴 방법.

2026-01-24 · 16분#TypeScript

2025 회고: 글 33개, PR 38회, 발표 3회로 '보여주는 개발자'가 되다

글 33개, PR 38회, 발표 3회. '숨어서 잘하는 개발자'에서 '보여주고 나누는 개발자'로 방향을 튼 한 해를 숫자로 정리했습니다. 조회수 상위 글들의 공통점, 오픈소스 기여가 커리어에 남긴 것, 다음 해에 이어갈 것까지.

2025-12-21 · 9분

2025 Teoconf 발표 후기

40페이지짜리 슬라이드를 다 만들고 나서야 알았습니다. 발표는 기술을 나열하는 자리가 아니라 청중이 가져갈 한 문장을 만드는 과정이라는 것을. 테오콘 신청과 사전 미팅, 혹독했던 사전 녹화 피드백, 그리고 무대 위까지의 기록입니다.

2025-12-11 · 6분

하루 만에 끝날 줄 알았던 디자인 시스템 배포가 3주 걸린 이유

'별거 아니겠지'라고 생각했던 디자인 시스템 NPM 배포가 어떻게 3주간의 삽질로 이어졌는지, 그리고 그 과정에서 배운 타협의 기술. 사내에서만 쓰던 패키지를 세상에 내놓기까지를 다루는 배포 시리즈의 프롤로그입니다.

2025-11-30 · 23분#NPM

[Typescript로 설계하는 프로젝트] 타입 한 줄로 552개 파일을 2주 만에 안전하게 수정한 방법

회원 구조가 바뀌어 552개 파일을 고쳐야 했지만 2주 만에 사이드 이펙트 없이 끝냈습니다. 1년 전에 심어둔 브랜디드 타입 한 줄이 컴파일러를 리팩토링 체크리스트로 바꿔준 이야기와, 같은 안전장치를 지금 프로젝트에 심는 방법.

2025-11-06 · 11분

React 에러 구조 설계: throw만으로 선언적 에러 핸들링 하기

에러가 계층을 따라 전파된다면 에러 클래스도 계층으로 설계할 수 있지 않을까. 커스텀 에러 클래스 상속과 throw만으로 ErrorBoundary가 섹션마다 다르게 반응하는 구조를 만들고, throw와 return 중 무엇을 고를지도 정리했습니다.

2025-10-14 · 26분

구글 개발자가 인정한 성능 최적화 기여 후기 - AI와 함께한 gemini-cli 오픈소스 여정

출시 직후의 gemini-cli 코드베이스를 AI와 함께 뜯어 동기 파일 처리를 병렬로 바꿨고, 408ms를 107ms로 줄였습니다. 기여할 지점을 찾는 프롬프트부터 구글 개발자에게 리뷰받으며 PR을 머지하기까지의 전략.

2025-09-07 · 24분

feconf 2025 발표 후기

마이크를 잡는 순간까지 '내가 말해도 되나' 싶었습니다. FEConf 2025 라이트닝 토크 신청부터 선정, 슬라이드 초안과 세 번의 리허설, 그리고 무대 위까지 — 발표가 처음인 개발자가 남긴 3개월치 준비 기록입니다.

2025-09-02 · 10분

4시간 만에 Node.js PR 승인받기

util.inspect가 음수 소수점을 잘못 포매팅하는 버그를 AI와 페어 프로그래밍으로 파고들어 4시간 만에 PR 승인까지 받았습니다. 이슈를 고르는 기준부터 메인테이너와 주고받은 과정까지, Node.js 코어 기여의 전 과정.

2025-08-10 · 9분

Next.js 오픈소스 기여: 2번 실패 후 성공 후기

새벽 2시에 날아온 Closed 알림을 두 번 받고서야 Next.js Contributors 목록에 이름을 올렸습니다. 두 번의 실패에서 무엇을 잘못 골랐는지, 세 번째에 머지된 PR은 무엇이 달랐는지 솔직하게 적었습니다.

2025-08-05 · 20분

Context API를 활용한 DropZone 구현하기

Mantine Dropzone의 내부를 뜯어보니 Context API와 컴파운드 컴포넌트 패턴의 조합이 있었습니다. 같은 패턴으로 기본·허용·거부·로딩 상태별 UI를 자유롭게 갈아끼울 수 있는 DropZone을 직접 만들어봅니다.

2025-07-30 · 37분

Type Guard를 활용한 여러가지 Button 구현하기

solid에만 있는 assistive, primary만 있는 text. 피그마의 이 불규칙한 조합을 Button 하나로 받으면서도 잘못된 variant는 컴파일 단계에서 막는 Type Guard 설계를 정리했습니다.

2025-06-30 · 20분

15줄에서 2줄로: useSyncExternalStore 기반 React Toast 시스템 설계법

useState와 useEffect로 15줄씩 반복되던 Toast 호출을 2줄로 줄였습니다. useSyncExternalStore로 상태를 컴포넌트 밖 스토어로 빼내, 앱 어디에서든 타입 안전하게 부를 수 있게 만드는 설계.

2025-06-30 · 25분

개발자에서 기여자로: 기술 블로그와 오픈소스가 바꾼 나의 2025년 상반기

나만 알던 지식을 밖으로 꺼내기 시작한 반 년. 첫 글이 일주일 만에 500회를 넘던 순간부터 Mantine에 PR을 보내기까지, 개발자에서 기여자로 옮겨간 2025년 상반기를 글과 오픈소스 두 축으로 정리했습니다.

2025-06-17 · 7분

[Typescript로 설계하는 프로젝트] "같은 로직 또 복사했어요?" Domain 모델로 책임 분리하기

Service에 사용자 관련 함수가 수백 줄씩 쌓이기 시작하면 그게 신호입니다. Type에서 출발해 Domain 모델로 점진적으로 키워가는 과정과, 함수형과 객체지향 중 무엇을 고를지 판단하는 기준을 예제 코드와 함께 정리했습니다.

2025-06-15 · 23분

[Typescript로 설계하는 프로젝트] "원래 있던 기능이니 금방 하시죠?" 당하지 않는 Service Layer 설계 전략

'원래 있던 기능이니 금방 하시죠?' UI 컴포넌트에 눌어붙은 비즈니스 로직을 Service Layer로 떼어내는 이야기입니다. 기획이 갑자기 바뀌어도 화면을 뜯지 않고 재사용으로 대응할 수 있는 구조를 단계별로 만들어봅니다.

2025-06-08 · 26분

[Typescript로 설계하는 프로젝트] "원래 있던 기능이니 금방 하시죠?" 당하지 않는 Service Layer 설계 전략 With Di

같은 Service Layer를 이번엔 의존성 주입으로 다시 짭니다. 서비스가 HTTP 클라이언트를 직접 만들지 않고 밖에서 주입받게 하면 결합도가 내려가고 목 객체만 갈아끼워 서버 없이도 테스트할 수 있습니다. 본편과 같은 문제를 다른 도구로 푸는 편.

2025-06-08 · 39분

[Typescript로 설계하는 프로젝트] Type 설계의 시작: 견고한 서버 API Type 설계하기

'User 스키마에서 name이 빠질 것 같아요.' 이 한마디에 흩어진 타입을 전부 찾아 헤매지 않으려면 서버 API 응답 스키마부터 구조적으로 설계해야 합니다. 타입 설계의 첫 단계를 실제 코드로 하나씩 따라가 봅니다.

2025-06-01 · 21분

[Typescript로 설계하는 프로젝트] Type 설계의 시작: 견고한 서버 API Type 설계하기 With DI

제네릭 HTTP 클래스를 인터페이스 뒤로 감추고 밖에서 주입합니다. API 레이어가 통신 구현을 모르게 되면 fetch든 axios든 갈아끼울 수 있고, 서버가 없어도 목 하나로 테스트가 끝납니다. DI 관점의 API 설계.

2025-06-01 · 20분

[Typescript로 설계하는 프로젝트] 당신의 Type, 어디까지 연결되어 있나요?

타입스크립트로 옮겼는데도 undefined is not a function이 끊이지 않는다면 문제는 문법이 아니라 설계입니다. 서버 응답부터 화면까지 타입이 끊기지 않고 이어지는 구조를, 실제로 겪은 버그와 함께 이야기합니다.

2025-05-05 · 14분

pnpm 10 업그레이드 후 ESLint 설정이 사라졌어요?! (feat. 호이스팅)

pnpm 9에서 10으로 올린 뒤 모노레포의 ESLint가 조용히 멈췄습니다. 원인은 설정 문법이 아니라 호이스팅 정책 변경이었습니다. pnpm 이슈 트래커를 따라가 원인을 확인하고, 설정 한 줄로 되돌리기까지의 기록입니다.

2025-05-01 · 3분

오픈소스 첫 발걸음: Mantine PR 기여 후기

디자이너 시안에 필요한 옵션이 Mantine에 없었습니다. 라이브러리 코드를 통째로 내려받아 Slider를 분석하고 직접 기능을 구현해 PR을 보내고 머지되기까지, 첫 오픈소스 기여의 막막함과 그 과정을 그대로 적었습니다.

2025-04-22 · 10분

🚨3분만에 Next.js 서비스 장애 대응 대응하기: GitHub Actions + AWS ECS + CodeDeploy 롤백 시스템

배포 두 시간 뒤 장애 신고가 들어왔고 되돌리는 데 30분이 걸렸습니다. GitHub Actions와 AWS CodeDeploy로 배포 버전을 골라 3분 만에 롤백하는 파이프라인을 만들어 그 30분을 없앤 과정입니다.

2025-04-03 · 8분

ecs와 code deploy를 활용한 next.js 배포하기

도커라이징한 Next.js를 AWS ECS에 올리고 CodeDeploy로 배포합니다. GitHub Actions 워크플로우의 실행 조건부터 태스크 정의와 ECR 푸시까지, 실제로 쓰는 설정을 하나씩 따라가며 정리했습니다.

2025-04-01 · 13분

Turborepo 에서 next.js 도커라이징 하기

모노레포의 웹 서비스 세 개를 각각의 이미지로 굽습니다. Next.js standalone 모드와 Turborepo의 --docker 옵션을 조합해 node_modules를 걷어내고 이미지를 가볍게 만드는 방법을 정리했습니다.

2025-03-22 · 12분

Next.js로 우아하게 Error 핸들링하기

try-catch와 ErrorBoundary 다음은 Next.js의 error.js입니다. App Router가 경계마다 error.tsx를 심는 방식을 실제 코드로 따라가며 global-error와의 역할 차이를 정리했습니다.

2025-03-03 · 7분

React로 우아하게 Error 핸들링 하기

ErrorBoundary는 렌더링 중에 난 에러만 잡습니다. 클릭 핸들러와 비동기 요청에서 난 에러를 경계까지 끌어올리는 방법, 그리고 의도한 에러와 의도치 않은 에러를 나누어 다루는 기준을 예제 코드와 함께 정리했습니다.

2025-03-02 · 15분

javascript로 우아하게 Error 핸들링하기

try-catch부터 async/await, 에러 래핑과 커스텀 에러 클래스까지. 에러를 여기서 잡을지 상위로 넘길지를 매번 감으로 정하지 않도록, 동기와 비동기 상황별 처리 패턴을 테스트 코드와 함께 정리했습니다.

2025-02-27 · 14분

미들웨어에서 auth 적용하기

레이아웃에서 인증을 확인하면 이미 늦습니다. Next.js Middleware로 요청 단계에서 로그인 여부와 역할을 판별해, 프라이빗 페이지가 잠깐 보였다 사라지는 깜빡임을 없앤 과정과, 쿠키·캐싱 전략까지 정리했습니다.

2025-02-26 · 6분

Panda CSS 1년 사용기

빌드 타임에 CSS를 정적으로 추출하는 Panda CSS를 1년 썼습니다. Tailwind·styled-components와 무엇이 어떻게 다른지, 모노레포 디자인 시스템에 쓸 때의 장단점과 효율적인 학습 순서를 정리했습니다.

2025-02-17 · 19분

컴포넌트에 인터렉션 레이어 이쁘게 씌우기

hover·focus·press마다 색이 정확히 달라져야 하는데 opacity로는 의도한 색이 안 나오고, div를 덮으면 클릭 이벤트가 죽습니다. 가상 요소 ::after로 두 문제를 한 번에 푼 방법을 정리했습니다.

2025-02-10 · 10분

디자인 시스템 시작해보기

디자이너가 제각각이던 다섯 개 서비스의 디자인을 한 벌로 모았습니다. Turborepo·Mantine·Panda CSS로 시스템을 세우고 디자인 토큰부터 컴포넌트 제작, 문서화와 협업 프로세스 확립까지 하나씩 만들어간 기록입니다.

2025-02-02 · 19분

FSD 간단하게 시작해보기

components, apis, utils로 나눈 폴더는 프로젝트가 커지면 서로 엉킵니다. 레이어·슬라이스·세그먼트로 기능 중심으로 다시 나누는 FSD를, 실제 팀 프로젝트에 적용해 본 사람의 눈높이에서 정리했습니다.

2025-01-21 · 7분

서버 의존성을 줄여보자 (Feat : 클린 아키텍처)

페이지 맞춤형 API에 화면이 묶이면 요구사항이 바뀔 때마다 뷰까지 흔들립니다. 클린 아키텍처를 팀이 감당할 만큼만 덜어내 우리 팀이 쓸 수 있는 형태로 다시 설계하고, 실제 코드에 적용해 팀을 설득한 경험을 정리했습니다.

2025-01-13 · 14분

도전과 성장, 그리고 배움으로 가득 찬 2024년 회고

동료들의 공백으로 역할이 커진 1분기부터 연말까지, 2024년을 분기별 Keep·Problem·Try로 돌아봤습니다. TypeScript 전환과 배포 시스템 리팩터링에서 배운 것, 그리고 소통이 없어 놓쳐서 아팠던 것들.

2024-12-23 · 9분