pile·
프론트엔드·티몬티몬·

응답없는 페이지가 되지 않게 하는 법 (feat. setTimeout)

오래 걸리는 자바스크립트 작업 때문에 브라우저가 멈추고 "응답 없는 페이지" 경고창이 뜨는 문제를, 타이머로 작업을 잘게 나눠 해결한 기록이다. 분할 단위를 바꿔 가며 전체 수행 시간과 UI 응답성이 어떻게 맞바뀌는지 실측까지 보여준다.

핵심 포인트
  • 브라우저는 UI 업데이트와 자바스크립트 실행을 동시에 못 한다. 스크립트가 도는 동안 화면은 정지 상태다.
  • Windows Chrome 기준 약 30초 이상 실행되면 경고창이 뜨고, 사용자는 30초마다 계속 '대기' 를 눌러야 한다. 개발자는 이 창이 뜨게 만들면 안 된다.
  • `document.body.appendChild(div)` 로 DOM 을 바꿔도 함수가 끝나기 전에는 화면에 나타나지 않는다. 싱글 스레드이기 때문이다.
  • 해결은 `setTimeout` 으로 일정 횟수마다 실행을 끊어 주는 것이다. 쉬는 동안 브라우저가 UI 업데이트를 처리한다.
  • 분할 단위가 성능과 응답성을 맞바꾼다. 50회씩 쪼개면 3분 24초, 1000회씩이면 1분 38초가 걸렸다.
상세 정리
  • 배경: 서버와 주고받는 데이터가 매우 많거나 서버 부하가 심한 기능은 클라이언트에서 처리하는 편이 효율적인데, 그 대가로 UI 사용성이 끊긴다.
  • 문제 재현 코드: 버튼 클릭 시 div 를 추가하고 3만 번 루프를 도는 예제로, 클릭 표시와 "Clicked!" 출력이 루프가 끝난 뒤에야 나타나는 것을 보인다.
  • 실제 케이스: 티몬 어드민에서 화면 결과를 보여주기 전에 오래 수행되는 기능이 있어 30초마다 경고창이 반복됐다. 종료를 누르면 기능 자체를 못 쓴다.
  • 테스트 코드 구성: 10×2000 짜리 딜 목록을 만들고 13개 대상 키워드를 3중 루프로 찾는 `searchDeal()` 을 10만 번 반복하며 progress 바를 갱신하는 구조다.
  • 수정 전 결과: 30초간 브라우저가 멈추고, '진행확인' 버튼을 눌러도 반응이 없으며, progress 바도 움직이지 않는다. 로딩바 이미지조차 정지한다.
  • 사용자 입장에서 프로그램이 동작 중인지 언제 끝나는지 전혀 알 수 없다는 점을 문제의 본질로 짚는다.
  • 수정 방식: `searchDeal()` 을 정해진 횟수만큼 호출한 뒤 `setTimeout` 으로 25ms 쉬고 다시 `execute` 를 호출하는 식으로, 총 호출 수를 채울 때까지 반복한다.
  • 수정 후 결과: 실행 중에도 '진행확인' 버튼이 즉시 반응하고 progress 바가 정상 동작하며, 30초 경고창이 한 번도 뜨지 않고 완주한다. 완료 시 콜백으로 알림을 띄운다.
  • 트레이드오프 실측: 타이머 주기마다 수행하는 양을 늘리면 쉬는 횟수가 줄어 전체는 빨라지지만 브라우저가 UI 를 갱신할 기회가 줄어 응답성이 나빠진다.
  • 판단 기준: 이 사례처럼 수행 시간이 길고 즉각적인 응답성이 꼭 필요하지 않다면 50회보다 1000회씩 처리하는 쪽이 효율적이라고 결론 낸다.
왜 읽나무거운 클라이언트 작업 때문에 화면이 얼어붙는 상황을 겪은 프론트엔드 개발자에게 분할 실행 패턴과 단위 선택의 근거를 준다.
티몬
티몬 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2