"Figma 이미지와 스크린샷 크기가 안 맞아서 비교가 안 돼요." 문제의 시작pixelDiff는 디자인(Figma)과 실제 구현(스크린샷)을 픽셀 단위로 비교하는 도구다. 그런데 개발 초기부터 골치 아픈 문제가 있었다.Figma에서 내보낸 이미지: 5760×3600px크롬 익스텐션으로 캡처한 스크린샷: 2880×1800px사용자가 직접 업로드한 이미지: 1440×900px세 이미지 모두 "1440×900 프레임"을 캡처한 건데, 실제 픽셀 크기는 전부 다르다. 왜 이런 일이 발생하는가Figma API의 고해상도 내보내기Figma API로 이미지를 요청할 때 scale 파라미터를 지정할 수 있다. scale=4로 요청하면 원래 크기의 4배 해상도로 내보내진다.// Figma API 이미지 요청const ..
Turborepo가 해결하려는 문제Turborepo 공식 문서를 보면 이런 말이 나온다. "Turborepo is a high-performance build system for JavaScript and TypeScript codebases." 핵심 기능은 세 가지다. 기능 설명 로컬/리모트 캐싱 변경 없는 패키지는 빌드 스킵 병렬 실행 의존성 없는 태스크 동시 실행 증분 빌드 변경된 패키지만 다시 빌드 대규모 모노레포에서 빌드 시간이 대폭 단축되는 사례가 많다. 팀원 간 리모트 캐시를 공유하면 CI 비용도 절감된다. 근데 나는 그 규모가 아니다pixelDif..
상태 관리 라이브러리를 고를 때 Redux, Recoil, Jotai, Zustand 사이에서 고민하는 경우가 많다. 하나의 전역 스토어에 모든 상태를 넣을지, 아토믹하게 쪼갤지, 아니면 그 중간 어딘가를 선택할지는 프로젝트 규모와 팀 상황에 따라 다르다. pixelDiff를 개발하면서 Zustand를 선택하고, "도메인 기반 복합 스토어" 패턴을 적용했다. 단순한 투두 앱이 아니라 캔버스 에디터 수준의 복잡도를 가진 프로젝트에서 상태 관리를 어떻게 설계했는지 정리한다. 왜 Zustand인가상태 관리 라이브러리 선택지를 비교해보면 이렇다.라이브러리특징적합한 상황Redux단일 스토어, 보일러플레이트 많음대규모 팀, 엄격한 구조 필요Recoil아토믹, React 종속Facebook 생태계, Suspense ..
canvas 위에서 레이어를 비교할 때 opacity 조절이 핵심이다. 문제는 이 UI가 원래 좌측 하단에 고정되어 있었다는 것.canvas 작업은 상호작용이 많다. 레이어를 드래그하고, 확대/축소하고, 위치를 맞추는 동안 시선은 canvas 중앙에 머문다. 그런데 opacity 값을 확인하려면 시선을 좌측 하단으로 옮겨야 했다. 작업 흐름이 끊긴다.해결책은 opacity 숫자를 슬라이더 thumb 바로 위에 표시하는 것이었다. 드래그하는 손가락 근처에서 값을 바로 확인할 수 있다. 화면도 덜 가린다.그런데 단순히 숫자만 띄우면 밋밋했다. 드래그하는 방향으로 살짝 기울어졌다가 손을 떼면 다시 원위치로 돌아오는 애니메이션을 추가했다. 오뚜기처럼. 실용적인 이유도 있지만, 솔직히 만들면서 재밌었다. 슬라이더..
런타임에 터진 버그"타입 정의 완벽하게 했는데 왜 undefined 에러가...?"type User = { name: string; age: number;};// 컴파일은 통과하지만...const data: User = JSON.parse(response);// response가 { name: 123, age: "스물" }이면? API 응답이 예상과 달랐다. TypeScript는 아무 경고 없이 통과시켰다.TypeScript는 컴파일 타임에만 동작한다. API 응답, 폼 입력, 외부 데이터는 런타임에 들어오기 때문에 타입을 아무리 정교하게 정의해도 런타임에는 무력하다.결국 if (!data.name || typeof data.age !== 'number') 같은 검증 코드를 직접 작성하게 되는데, 이..
React에서 탭이나 모드를 전환할 때 컴포넌트가 느리게 뜨는 경험을 해본 적이 있을 것이다. 특히 iframe이나 Canvas처럼 초기화 비용이 큰 컴포넌트가 매번 리마운트되면 사용자 경험이 확연히 나빠진다. 문제: 모드 전환마다 컴포넌트가 리셋된다pixelDiff에는 세 가지 비교 모드가 있다:iframe 모드: 웹페이지를 iframe으로 띄우고 Figma 이미지와 오버레이 비교opacity 모드: 투명도 조절로 차이점 확인devices 모드: 여러 기기 해상도에서 동시에 비교초기 구현은 단순한 조건부 렌더링이었다: {comparisonMode === 'devices' && }{comparisonMode !== 'devices' && } 문제는 모드를 전환할 때마다 발생했다:DevicesMode 리마..
React UI 라이브러리를 고르는 건 늘 고민이다. MUI처럼 완성된 디자인 시스템을 쓰면 빠르게 시작할 수 있지만, 커스터마이징할 때마다 기본 스타일과 싸워야 한다. 직접 만들자니 접근성, 키보드 네비게이션, 포커스 관리까지 신경 쓸 게 너무 많다.pixelDiff를 만들면서 이 딜레마를 다시 마주했다. 결론부터 말하면 shadcn/ui를 선택했고, 7000줄이 넘는 UI 컴포넌트를 쌓아가면서 이게 맞는 선택이었다는 확신이 들었다. UI 라이브러리 선택지검토했던 세 가지 선택지다.라이브러리스타일링 방식커스터마이징번들 크기특징MUIEmotion (CSS-in-JS)테마 오버라이드큼완성된 디자인 시스템, Google Material DesignChakra UIEmotion테마 토큰중간직관적인 props,..
캔버스 기반 앱에서 빈 공간을 드래그해 여러 객체를 한 번에 선택하는 기능, Marquee Selection을 구현했다. Figma나 Photoshop에서 익숙하게 쓰던 기능인데, 막상 직접 만들려니 생각보다 고려할 게 많았다. 문제 정의요구사항은 명확했다:빈 공간 드래그 → 반투명 선택 영역 표시드래그 영역과 겹치는 레이어 모두 선택Shift+드래그 → 기존 선택에 추가실시간 피드백 → 드래그 중에도 선택 상태 업데이트제약 조건도 있었다:캔버스는 Pixi.js로 렌더링 중줌/팬이 적용된 상태에서도 정확하게 동작해야 함기존 Hand 모드(패닝)와 Move 모드(레이어 이동)와 충돌 없이 공존해야 함 핵심 구현1. 좌표 변환: 화면 → 캔버스가장 먼저 해결해야 할 문제는 좌표 변환이다. 마우스 이벤트는 화..