참여 이벤트 | Debug Fundamentals #639
Replies: 5 comments
-
|
실시간 차트 페이지 만들다가 메모리 터져서 브라우저 죽은 이야기. 1. 진단하기주식 실시간 차트 페이지 만들었는데, QA팀에서 "30분만 켜놓으면 브라우저 죽어요"라고 제보가 들어왔어요. 직접 켜놓고 밥 먹고 왔더니 진짜 탭이 먹통이 되어 있더라구요. 2. 재현하기Memory Profiler로 스냅샷 찍어보니 5분만 지나도 detached DOM nodes랑 event listeners가 늘어나고 있었어요 useEffect(() => {
const ws = new WebSocket('wss://api.example.com/realtime');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setChartData(prev => [...prev, data]); // 데이터 계속 쌓임...
};
ws.connect();
// return 빼먹음...
}, []);
useEffect(() => {
const interval = setInterval(() => {
fetchMarketData().then(setMarketData);
}, 5000);
// clearInterval 어디갔어...
}, []);웹소켓이랑 인터벌 정리 안하고 냅뒀더니 컴포넌트 언마운트되도 계속 돌고 있었던거예요. 게다가 차트 데이터는 계속 쌓이기만 하궁 3. 수정하기cleanup 추가하고 데이터도 100개로 제한했어요. useEffect(() => {
const ws = new WebSocket('wss://api.example.com/realtime');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setChartData(prev => {
const updated = [...prev, data];
return updated.slice(-100); // 최근 100개만
});
};
ws.connect();
return () => {
ws.close(); // 이거 추가
};
}, []);
useEffect(() => {
const interval = setInterval(() => {
fetchMarketData().then(setMarketData);
}, 5000);
return () => {
clearInterval(interval); // 이것도 추가
};
}, []);AbortController로 API 요청도 취소되게 처리하고, React.memo로 쓸데없는 리렌더링도 막았어요. 4. 재발방지하기useEffect에서의 "끄기 행동"은 중요하다
ESLint에 실시간 데이터 다루는 페이지는 꼭 30분은 켜놓고 테스트해보세요. 안 그러면 또 QA팀한테 혼나요! |
Beta Was this translation helpful? Give feedback.
-
증상React Hook Form과 ZodResolver를 사용하여 작성한 상품 생성 페이지에서 “Submit” 버튼 클릭 시 아무런 반응이 없었으며, 필드 에러, 콘솔 에러도 출력되지 않아 동작이 멈춰보였습니다. 첫 시도(와 여러 시도..)
찾아낸 원인“상품 생성” 영역의 prizeConfig에서 전달된 값의 타입이 스키마에서 기대한 타입과 일치하지 않아, Zod의 transform 로직이 실행되기 이전 단계에서 검증이 실패하였습니다. 이로 인해 transform 내부에서 ctx.addIssue로 정의해 둔 커스텀 에러 로직이 수행되지 못했고, 결국 제가 의도한 필드 단위의 에러 메시지가 생성되지 않았습니다. 이 때문에 UI에서도 해당 에러 메시지가 표시되지 않는 현상으로 이어져 에러를 확인하기 어렵게 되었습니다. 해결책React Hook Form의 handleSubmit에 onError 콜백을 추가하여 유효성 검증 실패 시 에러를 핸들링 할 수 있도록 조치하였습니다. <form onSubmit={handleSubmit(onSubmit, onError)}>
<button type="submit">생성</button>
</form>문제 필드인 prizeConfig의 값 형태를 스키마와 일치하도록 조정했습니다. 재발 방지를 위한 대책유효성 검증 실패 시 무반응이 아닌, 사용자에게 명확한 에러 안내가 노출되도록 공통 처리 로직을 만들어 두기로 했습니다. Zod 스키마와 실제 form value 타입이 불일치하지 않도록 설계 단계에서 타입 정의를 명확히 하고, select 등 object 형태를 반환하는 UI 컴포넌트 사용 시 스키마와의 매핑 규칙을 사전에 정해두는 것이 재발 방지를 위해서도 좋을 것 같다고 생각했습니다. submit 로직 디버깅 시 기본적으로 onSubmit과 onError 두 경로의 콘솔 로깅을 함께 두어, 어디에서 종료되고 있는지 즉시 파악할 수 있도록 해두는것도 좋을 것 같습니다. |
Beta Was this translation helpful? Give feedback.
-
Radix UI Dialog 내 Select 컴포넌트 ESC 키 충돌 버그 사례증상
첫 시도
찾아낸 원인: 보이지 않는 두 개의 '레이어 관리자'문제의 핵심을 파고들자 GitHub 이슈에서 결정적인 단서를 찾을 수 있었어요. 원인은 Radix UI의 내부 동작 방식과 패키지 매니저의 의존성 관리 방식이 맞물려 발생하고 있었어요.
해결책: '레이어 관리자'를 하나로 통일하기원인을 알았으니 해결책은 명확해요. 우리 프로젝트에 존재하는 여러 명의 '레이어 관리자'를 해고하고, 단 한 명의 유능한 관리자만 남기는 거예요. 즉,
{
"pnpm": {
"overrides": {
"@radix-ui/react-dismissable-layer": "1.0.5"
}
}
}설정을 추가한 뒤, rm -rf node_modules pnpm-lock.yaml
pnpm install이제 모든 Radix UI 컴포넌트들이 단일 버전의 재발방지를 위한 대책
|
Beta Was this translation helpful? Give feedback.
-
Dialog가 켜지면 TV 리모콘이 작동하지 않는 문제 사례부제: 에러메세지가 없는 문제의 디버깅 사례 증상
첫 시도
찾아낸 원인발견 과정
원인 설명
해결책
|
Beta Was this translation helpful? Give feedback.
-
THREE.Cache.enabled로 인한 메모리 누수증상
첫 시도모바일에서 강제 새로고침은 보통 메모리 부족 때문인 경우가 많았어서 메모리 누수를 의심했어요.
dispose 로직은 분명 제대로 작동하고 있었는데 메모리가 안 줄어드니까, Three.js 관련 옵션들을 하나씩 살펴보기 시작했어요. GLTFLoader 설정부터 시작해서 렌더러 옵션, 그리고 프로젝트 전역에 설정된 Three.js 관련 코드들을 전부 뒤지면서 의심되는 부분들을 체크했죠. Three.js 공식 docs도 정독하고, Discourse 커뮤니티에서 "memory leak", "dispose", "cache" 같은 키워드로 비슷한 사례들을 찾아보면서 단서를 찾아갔어요. 찾아낸 원인그렇게 찾다 보니 프로젝트 초기에 성능 최적화를 위해 전역으로 설정해둔 THREE.Cache.enabled = true가 눈에 들어왔어요. 보안상 3D 모델 파일들이 pre-signed URL을 사용하고 있었는데, 한 번 다운로드되면 URL이 즉시 만료되고 새 URL로 교체되는 구조였어요. 같은 모델이라도 접근할 때마다 완전히 다른 URL을 갖게 되어 Three.js 캐시에는 '서로 다른 파일'로 인식되어 계속 쌓이기만 하고 재사용되지 않았던 거죠. 캐시 기능이 오히려 독이 되었던거였습니다. 해결책
useEffect(() => {
return () => {
// ...
THREE.Cache.remove(fbxUrl);
THREE.Cache.remove(glbUrl);
};
}, []);수정 후 메모리 프로파일링을 해보니 페이지를 이동할 때마다 JSArrayBufferData가 깔끔하게 정리되었고, 모바일에서도 10개 이상의 모델을 연속으로 로드해도 안정적으로 동작했어요. 재발방지를 위한 대책
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
참여 이벤트 | Debug Fundamentals
프론트엔드 접근성의 모든 것
https://frontend-fundamentals.com/debug/pages/event.html
Beta Was this translation helpful? Give feedback.
All reactions