프로젝트 목록
Case Study2025.12 - 2026.01

StoLink

웹소설 작가용 에디터 프론트엔드

SVG→Canvas 마이그레이션으로 INP 16배 개선, 번들 최적화로 초기 로딩 60% 단축

Project

StoLink

Role

Frontend Developer

Period

2025.12 - 2026.01

Team

5인 팀 (FE 전담)

Overview

5인 개발팀의 웹소설 작가용 플랫폼에서 프론트엔드 전체를 단독 담당했습니다. 노드 30개에서 멈추던 관계도 그래프를 600개 이상 처리 가능하도록 Canvas로 전환하고, Vite 번들 최적화로 초기 로딩을 60% 단축했습니다.

핵심 성과 (Chrome DevTools 측정)

INP 16.3배 향상

SVG→Canvas 마이그레이션으로 응답 속도 1,048ms → 64ms 개선. Style Recalculation 935ms/frame을 0ms로 완전 제거했습니다.

초기 로딩 60% 감소

Vite manualChunks로 Export/Graph 라이브러리를 Lazy Load 처리. 초기 JS 450KB → 187KB(gzip) 달성했습니다.

Technical Challenges

5 Cases
01

렌더링 성능 16배 개선

SVG → Canvas 마이그레이션

Impact

INP 1,048ms → 64ms

Challenge

D3.js 기반 캐릭터 관계도에서 노드가 30개를 넘어서자 줌/팬 조작 시 FPS가 10 미만으로 하락했습니다. Chrome DevTools 프로파일링 결과, 매 프레임마다 수백 개 DOM 요소의 Style Recalculation에 935ms가 소요되고 있었습니다.

Solution

SVG(DOM) 기반 렌더링을 Canvas API(react-force-graph-2d)로 전면 전환했습니다. 단일 Canvas 레이어에서 일괄 드로잉하여 브라우저의 스타일 재계산 부하를 완전히 제거했습니다.

Single Layer Rendering: 개별 DOM 노드 대신 Canvas 컨텍스트에서 Batch Drawing

Image Caching (useImageCache): 캐릭터 이미지를 텍스처로 메모리에 캐싱하여 드로잉 성능 최적화

Color-based Hit Detection: Canvas에서 클릭 이벤트 감지를 위한 O(1) 히트맵 구현 - 각 노드를 고유 색상으로 칠한 히든 캔버스에서 픽셀 색상으로 노드 ID 역추적

렌더링 파이프라인 비교

graph TB subgraph Before[Before: SVG/DOM] A[Node 1] --> S1[Style Recalc] B[Node 2] --> S1 C[Node N] --> S1 S1 --> R1[Reflow 935ms/frame] end subgraph After[After: Canvas] D[All Nodes] --> C1[Single Canvas] C1 --> R2[GPU Rasterization ~0ms] end
02

Vite Code Splitting

번들 최적화로 초기 로딩 60% 개선

Impact

초기 JS 450KB → 187KB

Challenge

프로덕션 빌드 후 Lighthouse 측정 시, 사용하지 않는 Export 라이브러리(docx 714KB, jspdf 341KB, epub-gen)가 초기 번들에 포함되어 로딩 지연 발생. 초기 JS 크기가 450KB(gzip)에 달했습니다.

Solution

Vite의 manualChunks 설정으로 라이브러리를 용도별로 분리하고 Lazy Loading을 적용했습니다.

vendor-export (714KB): docx, jspdf, epub-gen → Export 페이지에서만 로드

vendor-graph (61KB): D3, react-force-graph → 관계도 페이지에서만 로드

vendor-editor-extensions (33KB): Tiptap 확장 → 에디터 페이지에서만 로드

캐시 효율성: 세분화된 청크로 코드 수정 시 변경된 청크만 재다운로드 (40% → 85%)

03

useRef 패턴으로 재생성 방지

Tiptap 에디터 인스턴스 안정화

Impact

인스턴스 재생성 0회, 커서 튐 완전 제거

Challenge

Tiptap(ProseMirror) 에디터에서 부모 컴포넌트가 리렌더링될 때마다 onUpdate 콜백이 새로 생성되어 에디터 인스턴스가 파괴/재생성되었습니다. 이로 인해 타이핑 도중 커서 위치가 초기화되고, 입력이 씹히는 치명적인 UX 문제가 발생했습니다.

Solution

Stable Callback Ref 패턴을 적용하여 에디터의 의존성(Dependencies)과 콜백의 최신성(Freshness)을 분리했습니다. 함수 자체가 아닌 함수의 참조(ref)를 구독하게 하여 리렌더링 사이클을 끊었습니다.

Before: useEditor({ onUpdate }, [onUpdate]) → 콜백 변경 시 에디터 전체 재생성

After: onUpdateRef = useRef(callback) → ref.current만 갱신, 에디터는 최초 1회만 생성

useEffect로 ref.current를 동기화하여 항상 최신 로직에 접근 가능

04

O(n log n) → O(1)

트리 변환 메모이제이션

Impact

연산 비용 제거 (O(n log n) → O(1)), 반응성 극대화

Challenge

문서 트리 사이드바에서 노드 클릭/호버마다 Flat Array → Nested Tree 변환 함수(buildTree)가 매번 실행되었습니다. Map 생성 O(n) + 관계 연결 O(n) + 정렬 O(n log n) 과정으로, 문서 수백 개일 때 단순 클릭에도 15~20ms 연산이 발생하여 UI 반응성이 저하되었습니다.

Solution

useMemo와 참조 동등성(Referential Equality)을 활용하여 데이터가 실제로 변경되었을 때만 트리 변환을 수행하도록 최적화했습니다.

Before: 렌더링마다 buildTree(documents) 실행 → 매번 15~20ms 소요

After: useMemo(() => buildTree(documents), [documents]) → 참조 변경 시에만 재연산

불변성(Immutability) 원칙 덕분에 내용이 같으면 참조도 같음이 보장됨

05

개발 워크플로우 자동화

AI 에이전트 워크플로우 자동화

Impact

개인 도구로 시작하여 4팀 중 3팀(15/20명)으로 확산, 기수 전체 생산성 견인

Challenge

5인 팀 개발 중 Git 숙련도 격차로 인한 브랜치 충돌, 이슈 트래킹 누락, 구두 합의된 컨벤션 미준수 등 협업 비효율이 발생했습니다.

Solution

이슈 생성부터 PR까지의 수명주기를 자동화하는 'Smart-Commit' CLI와, 작업 크기에 따라 최적의 프롬프트를 주입하는 'Supervisor Agent' 시스템을 구축했습니다.

Smart-Commit: Staging된 변경사항을 분석하여 Conventional Commits 메시지 및 이슈 연결 자동화

Supervisor Agent: 작업 명세를 분석하여 Stylist/Architect 등 전문 에이전트에게 최소한의 Context만 동적 주입

Quality Gates: 'Design Compliance'를 포함한 3단계 자동 감사를 통해 코드와 디자인 품질 집중 검증

Key Results

1048 → 64ms

드래그 응답 시간

SVG→Canvas 전환으로 DOM 조작 제거, Chrome DevTools Performance 탭에서 측정

450 → 190KB

초기 번들 크기

Vite manualChunks로 Export/Graph 라이브러리 Lazy Loading 적용 (gzip 기준)

인스턴스 유지

에디터 안정화

useRef 패턴으로 부모 리렌더링에도 Tiptap 인스턴스 유지, 커서 튐 현상 해결

O(n log n) → O(1)

트리 변환 최적화

useMemo + 참조 동등성으로 데이터 변경 시에만 트리 재구성

100%

표준 준수

자동화된 워크플로우 도입으로 온보딩 비용 제거 및 코드 품질 표준화

Tech Stack

React 19TypeScript 5.7ViteZustand (17 stores)TanStack QueryCanvas APID3.jsreact-force-graph-2dTiptap (16개 커스텀 익스텐션)ProseMirrorIndexedDB (idb-keyval)

Next Project

Pintos Kernel

커널 프로그래밍 및 운영체제 실습