마무리 작업 중 Trouble Shooting 정리

Ethan·2026년 1월 26일

막바지에 다다른 FPS Aim Test의 마무리 작업으로,
최근에는 새로운 기능 추가보다는 주석 등 컨벤션 점검과
기존 기능을 안정화하고, 예상치 못한 버그를 해결하는 작업이 많았다.

특히 resize, fullscreen 전환, Canvas 렌더링과 관련된 문제들은
각각 독립적인 이슈처럼 보였지만,
실제로는 하나의 문제에서 파생된 연쇄적인 트러블에 가까웠다.

기억이 비교적 선명할 때,
최근에 겪었던 트러블슈팅 과정을 순서대로 정리해 보려고 한다.


Trouble 1. Resize 중 화면이 깜빡이는 문제

증상

  • 브라우저 창 크기를 조절할 때
  • Canvas가 연속적으로 점멸(flicker)하는 현상 발생

해상도 변경이나 모드 전환에서는 크게 티가 나지 않았지만,
마우스로 뷰포트를 직접 드래그해 resize할 때는
문제가 비교적 명확하게 드러났다.

원인

원인은 resize 시점에 실행되던 dpr(devicePixelRatio) 조정 로직이었다.
이 로직은 브라우저 해상도와 Canvas 해상도를 픽셀 단위로 일치시켜
화면 품질을 유지하는 역할을 한다.

화면이 변할 때마다 즉각적으로 실행되어야 한다고 판단해 구현했었는데,

  • resize 이벤트가 발생할 때마다
  • 프레임 단위로 dpr 계산 및 canvas 재설정이 실행되었고
  • 그 결과 렌더 결과가 계속 흔들리고 있었다

문제는 연산의 무거움이 아니라,
너무 자주 실행되고 있다는 점이었다.

해결

  • resize 처리 로직에
    • throttling
    • trailing 옵션
      을 추가
  • dpr 조정 빈도를 줄여
    연속 resize 중 불필요한 재계산이 발생하지 않도록 수정했다

결과적으로 점멸 현상은 크게 줄었지만,
뷰포트를 빠르게 드래그할 경우
맵과 타겟 컨테이너의 비율 조정이 한 타이밍 늦게 반응하는 경우가 생겼다.

하지만 눈에 띄는 점멸보다
간헐적인 지연이 UX 리스크가 더 낮다고 판단해
현재 상태를 유지하기로 했다.

배운 점

성능 문제는 항상 “무거운 연산”에서만 발생하지 않는다.
빈도가 높은 가벼운 연산도 충분히 UX를 망칠 수 있다.


Trouble 2. 게임 모드 변경 시 카메라 기준점이 튀는 문제

증상

  • 전체화면 ↔ 창 모드 전환 직후
  • 카메라가 순간적으로 잘못된 위치(좌측 상단)로 이동
  • 이후 몇 프레임 뒤 정상 위치로 복귀

육안으로도 “한 번 튄다”는 느낌이 확실히 보였다.

원인

원인을 추적해보니, 모드 변경 직후 아주 짧은 타이밍 동안
drawSize가 실제 canvas 크기보다 작은 값으로 계산되는 구간이 존재했다.

이 상태에서

  • 카메라 clamp 로직이 실행되며
  • 화면 중앙을 원점으로 재구성한 좌표계가 아닌
  • 기본 평면 좌표계의 원점(좌측 상단)을 기준으로
    초기 위치가 설정되고 있었다

정확히는 drawSize가 0에 가까운 값으로 계산되는 첫 프레임에
클램프 결과가 잘못 확정되고 있었던 것이다.

계산식 자체는 맞았지만,
계산이 실행되는 타이밍이 문제였다.

해결

  • clamp 계산 전에 가드를 추가해
    • drawSize가 canvas 크기보다 작을 경우
    • 초기 위치 계산을 건너뛰도록 수정
  • 동시에 카메라 이동 애니메이션의 duration을 줄여
    레이아웃 계산이 더 빠르게 안정되도록 조정했다

배운 점

렌더링 문제는 로직 문제만이 아니라
타이밍 문제로도 충분히 발생할 수 있다는 점을 다시 체감했다.

그동안 애니메이션은
미리 계산된 결과를 따라 움직이는 것이라 생각했지만,
실제로는 매 프레임 계산을 통해 결과에 도달한다는 점을
이번 경험을 통해 명확히 이해하게 되었다.


Trouble 3. 전체화면 종료 후 맵 비율이 고정되는 문제

증상

  • 전체화면 모드에서 게임 실행 후
  • 창 모드로 돌아오면
  • 맵 비율이 이전 상태로 고정되어
    새 resize 발생 전까지 복구되지 않는 현상 발생

매번 발생하는 문제는 아니었고, 대략 재현 시도 10번 중 1~2번,
아주 빠른 모드 변경을 반복할 때 드물게 발생했다.

못 알아챘다면 모를까 한 번 인지한 이상,
그대로 두고 넘어갈 수는 없는 문제였다.

원인

문제의 핵심은
Canvas의 실제 픽셀 크기 변경
React 리렌더 타이밍 사이에
명확한 선후관계가 보장되지 않는다는 점이었다.

Canvas 크기는 이미 변경되었지만,

  • React 컴포넌트는 아직 리렌더되지 않은 상태에서
  • aspect 비율 계산 로직이 실행되고
  • 드물게 처리 순서가 꼬이면
    이전 값을 기준으로 계산 결과가 고정되고 있었다

앞서 살펴본 카메라 클램프 문제와 유사했다.
즉, 값은 바뀌었지만
그 값을 기준으로 다시 계산하는 과정에 빈틈이 있는 상태였다.

해결

  • Canvas 크기 변경 시점을 기준으로
  • 명시적인 state를 추가해 리렌더를 트리거
  • 비율 계산 로직이 항상 최신 상태를 기준으로 실행되도록 수정했다

배운 점

Canvas처럼 React 외부에서 상태가 변하는 요소
React 상태 변경과 자동으로 동기화될 것이라 기대하면 안 된다.

특히 useRef를 의존성 배열로 사용할 때는
참조값과 원시값을 명확히 구분해야 한다는 점을 다시 인식했다.


Trouble 4. 훅이 불필요하게 재생성되던 문제

증상

  • 커스텀 훅 디버깅 중
  • useFullscreen, useResize, useBorderFade 의 내부 로직이
    반복적으로 재생성되고 있는 것을 확인

UX 상 눈에 띄는 오류는 없었지만,
의미 없는 반복 재생성 자체가
성능을 갉아먹고 있다는 점은 분명했다.

원인

원인은 크게 두 가지였다.

  • 부모 컴포넌트에서 콜백을 전달하며
    렌더마다 새로운 함수가 생성되고 있었고
    그 참조 변경이 fullscreen 관련 훅 재생성으로 이어지고 있었다
  • 훅의 반환값이 객체임에도
    useMemo 없이 반환되고 있어
    매 렌더마다 새 객체가 생성되고 있었다

훅 내부 로직이 문제가 아니라,
훅의 props와 반환값의 참조 안정성이 깨진 상태였다.

해결

  • 새로운 함수를 전달하는 대신
    기존 setter를 직접 전달
  • 객체를 반환하는 커스텀 훅에
    누락된 useMemo 적용
  • 참조를 안정화해 불필요한 재생성을 제거했다

배운 점

훅 최적화에서는
로직의 안정성뿐 아니라
입력값과 반환값의 참조 안정성도 함께 고려해야 한다는 점을 실감했다.


마무리

이번 트러블슈팅들을 겪으며 느낀 점은 비교적 명확하다.

기능 추가보다
이런 안정화 작업
체감 난이도는 훨씬 높게 느껴진다는 점이다.

resize, fullscreen, canvas처럼
브라우저 환경과 밀접한 요소들은
한 번 고쳤다고 끝나는 문제가 아니었다.

앞으로 비슷한 구조의 프로젝트를 다시 만든다면,

  • 렌더 타이밍
  • 상태 동기화 지점
  • resize / fullscreen 전환 시나리오

를 훨씬 더 먼저 고려하게 될 것 같다.

profile
"Actions speak louder than words"

0개의 댓글