브라우저 멀티 스레드로 UX 개선하기

badahertz52·2025년 10월 21일
1

browser

목록 보기
2/2
post-thumbnail

상황: 페이지 분기 처리

헤더 메뉴에 있는 배송 관리 버튼을 클릭했을 때, 로그인한 사용자가 배송 그룹 관리 대상인지 여부에 따라 페이지를 분기 처리해야 하는 요구사항을 구현해야했습니다.

원래는 두 가지 방식 중 하나를 선택할 수 있었는데,

  • 하나는 로그인 후 사용자 프로필에 “관리 대상 여부” 정보를 포함시키는 방식,
  • 다른 하나는 버튼 클릭 전에 헤더 메뉴 부분에서 별도의 API를 호출하여 분기 여부를 판단하는 방식이었습니다.

프로필 관련 데이터가 복잡하고 여러 의존성이 있었으며, 일정 내 구현해야 하는 제약이 있었습니다. 그래서 헤더에서 API를 호출하여 분기 처리하는 방식으로 결정했습니다

트러블 슈팅 : UX개선하기

문제

버튼 클릭 전/후 상태 관리가 필요해, API 호출 전/중에는 버튼을 비활성화하여 권한 없는 사용자가 잘못된 페이지로 가는 오류를 방지했습니다.

하지만, API 응답이 느려서 버튼이 한참 비활성화 상태로 남아 있어 UX가 좋지 않았습니다.

특히 리소스가 많거나 초기 페이지 로딩이 복잡한 경우, 버튼 활성화까지의 지연이 매우 눈에 띄었습니다.

원인 파악

초기 구현에서는 페이지 렌더링이 끝난 후에 API를 호출했기 때문에, 버튼이 그동안 비활성 상태로 남아 UX가 떨어졌습니다.

브라우저 구조상 네트워크 요청은 별도 스레드에서 처리되지만, DOM 조작과 렌더링은 메인 스레드가 담당하므로 JS에서 API 호출 시점이 렌더링에 의해 지연될 수 있었습니다.

해결 방법

해결책은 브라우저의 멀티 스레드를 활용한 페이지 로드와 API 호출을 병렬 처리하는 방식이었습니다. HTML 파싱과 동시에 헤더 메뉴 분기 API를 비동기로 요청하고, DOMContentLoaded 이벤트 시점에 API 결과를 배송 관리 버튼에 반영했습니다.

관련 코드를 간단하게 표현한 예시 코드입니다.


  let isDeliveryGroupPromise = null;

  (async function() {
    isDeliveryGroupPromise = fetchIsDeliveryGroup();
  })();

  document.addEventListener('DOMContentLoaded', async function () {
    try {
      const result = await isDeliveryGroupPromise;
      // 분기 처리: 배송 관리 버튼 링크 또는 메뉴 아이템 설정
    } catch (e) {
      // 오류 처리
    }
  });

  async function fetchIsDeliveryGroup() {
    const response = await fetch('/api/check-delivery-group');
    return response.json();
  }

렌더링 스레드가 렌더링 관련 작업을 진행하는 동안 네트워크 스레드가 API 호출을 하면, DOM이 로드될 때 API 응답이 준비되어 있습니다.

이렇게 하면 보다 빨리 버튼 설정을 진행할 수 있어, 사용자에게 보여지는 UI 준비가 더 빨라지져 버튼이 “비활성 → 활성화” 되는 지연이 줄어듭니다.

⏰ 타임라인 정리

시간: 0ms
├── HTML 파싱 시작
└── JS 파일 실행: API 요청 시작 (네트워크 스레드)

시간: 500ms
├── HTML 파싱 계속
├── CSS 파싱
├── DOM 생성
└── API 요청 진행 중

시간: 1000ms
├── DOMContentLoaded 이벤트 발생
├── JavaScript 실행 (DOM 조작)
└── API 응답 완료 (이미 진행 중)

시간: 1500ms
├── UI 업데이트 완료
└── 사용자에게 최종 화면 표시

성과

개선 전에는 버튼 링크 분기처리를 위한 API 호출 완료까지 평균 약 1600ms 걸렸습니다. 개선 후에는 약 500ms 수준으로 단축되었습니다. UX 측면에서 버튼이 거의 곧바로 활성화되는 인상을 주었고, 기다리는 느낌이 줄었습니다.

한발 더 나아가기: 크롬의 멀티 프로세스와 멀티 스레드

브라우저마다 정책이 달라, 많이 사용하는 크롬 기준으로 살펴보겠습니다.

크롬 공식 문서에 따르면 크롬 브라우저는 성능·안정성·보안 확보를 위해 멀티 프로세스+ 멀티 스레드 구조를 채택하고 있습니다.

우선, 성능 개선 작업에 활용한 멀티 스레드부터 살펴보겠습니다.

크롬의 멀티 스레드

브라우저의 탭에서는 다양한 기능이 동작합니다. 만약 이를 단일 스레드로 처리하면 병목 현상이 발생할 수 있습니다.

그래서 브라우저는 멀티 스레드를 사용해, 속도를 높이고(스레드가 메모리/자원 공유로 )비용을 줄이고 있습니다. 위의 성능 개선 작업도 메인 스레드와 네트워크 스레드,렌더링 스레드는 병렬적으로 동작하는 것을 이용했습니다.

크롬의 멀티 프로세스

크롬은 멀티 프로세스를 지원하며 사이트 별로 별도의 렌더러 프로세스를 실행하는 사이트 격리를 사용하고 있습니다.

왜 크롬은 사이트별로 다른 렌더러 프로세스를 할당할까요?
멜트다운, 스펙터와 같은 보안 취약점에 대응하기 위해서입니다.

프로세스 레벨에서 격리하면, 만약 하나의 프로세스가 악의적 코드나 교차 사이트 공격 등에 취약하더라도 다른 사이트의 렌더링 데이터나 쿠키·저장된 정보 등이 동일 프로세스에서 노출되는 위험이 줄어듭니다.

사이트 격리는 위험을 크게 줄이지만 100% 완전한 해결책은 아닙니다. 사이트 격리는 멜트다운과 스펙터 같은 추측·캐시 기반 공격의 공격 표면(공격이 닿을 수 있는 범위)을 줄여 줍니다. 크롬 자체는 이를 “추가 방어선(second line of defense)”이라고 설명하고 있습니다.

되돌아보며

브라우저의 멀티 스레드를 활용해, API 호출과 렌더링을 병렬로 처리해 UX를 개선했습니다.

이 과정을 통해 브라우저의 멀티 스레드 동작, 프로세스 격리 및 보안 개념 등이 단순한 CS 지식이 아니라 실무에서 “왜 이 방법이 더 나은가”의 근거가 되고 성능 개선의 열쇠였음을 깨달았습니다. 작은 성능 개선 작업이었지만, 흩어져 있던 기술 지식들이 하나의 트러블슈팅 경험으로 엮여 정리가 되었다는 것이 가장 큰 수확이었습니다.


📄 참고 자료

profile
세상과 사람을 잇는 개발을 꿈꾸는 프론트엔드 개발자

0개의 댓글