게시일: 2025년 10월 14일
다음 페인트에 대한 상호작용 (INP)은 응답성을 측정하는 데 중요한 Core Web Vitals 측정항목입니다. Fotocasa에서는 2024년에 INP가 첫 입력 지연 (FID)을 대체했을 때 Google Search Console에서 '개선 필요' 및 '나쁨'으로 전환된 페이지가 상당수 강조 표시되었습니다. 이 사례 연구에서는 이러한 문제를 진단하고 해결하는 데 사용된 도구와 전략을 설명하며, 이를 통해 INP가 크게 개선되었습니다.
Fotocasa팀이 시작된 곳
FID에서 INP로 전환되기 전에는 데스크톱과 모바일의 거의 모든 페이지가 '좋음' 기준치 내에 있었으므로 당시의 모든 코어 웹 바이탈 (LCP, CLS, FID)이 양호한 실적을 보였습니다. 하지만 INP로 전환된 후 대부분의 사용자 상호작용에서 INP 값이 200밀리초를 일관되게 초과하여 거의 모든 페이지가 '개선 필요'로 전환되었고 일부는 '나쁨'으로 전환되었습니다.
이 변화로 인해 Fotocasa팀은 사용자 경험의 중요한 측면을 간과하고 있다는 사실을 깨달았습니다. FID는 첫 번째 상호작용의 지연만 측정했지만 INP는 입력 처리 및 프레젠테이션 지연을 고려하여 모든 상호작용의 반응성을 평가합니다. 이 광범위한 측정은 Google에서 언급한 것처럼 실제 상호작용을 훨씬 더 잘 나타내며 누락된 기회를 강조합니다.
Google Search Console은 필드 실적 데이터를 제공하지만 실시간 통계는 제공하지 않습니다. 데이터는 28일 동안 집계되므로 현재 문제를 일으키는 상호작용을 정확히 파악하기 어렵습니다.
가장 느리고 사용자가 가장 자주 사용하는 상호작용을 파악하고 팀의 개발 환경에서 안정적으로 재현하려면 실시간으로 INP를 추적하는 방법이 필요했습니다. 어떤 수정사항이 도움이 되었는지뿐만 아니라 어떤 조정으로 인해 의도치 않게 상황이 악화되었는지 등 변경사항의 영향을 이해하는 것도 마찬가지로 중요했습니다.
이러한 이유로 문제를 진단하고 해결하는 데 도움이 되는 도구 세트가 사용되었습니다. 가장 중요한 사항은 다음과 같습니다.
- Google Chrome DevTools, 특히 성능 탭
- Fotocasa팀이 Datadog에서 web-vitals 라이브러리를 사용하여 빌드한 맞춤 RUM (실제 사용자 모니터링) 시스템
- React 개발자 도구
문제를 진단하는 도구
INP 성능 문제를 진단하고 디버그하기 위해 다음 도구가 사용되었습니다.
Google Chrome DevTools
웹 애플리케이션에서 핵심 웹 바이탈과 관련된 문제를 감지하고 재현하는 좋은 방법은 Google Chrome DevTools의 성능 탭을 사용하는 것입니다. '성능' 탭은 Core Web Vitals 측정항목을 자동으로 측정하여 로드, 상호작용, 레이아웃 변경 측정항목에 관한 즉각적인 의견을 제공합니다. 이 측정항목은 다른 Google 도구에 보고되는 방식과 전반적으로 일치합니다.
Fotocasa팀은 INP 문제를 식별하고 해결하기 위해 일반적으로 CPU를 제한하여 저가형 및 중급 기기의 성능을 시뮬레이션했습니다. 이를 통해 Fotocasa팀은 더 제한적인 조건에서 페이지가 어떻게 작동하는지 관찰할 수 있었습니다. 그런 다음 프로파일러를 사용하여 세션을 기록하고 사용자 상호작용에 중점을 두어 트레이스를 주의 깊게 분석하여 성능 문제를 정확히 파악했습니다.
병목 현상을 식별할 때는 INP 하위 부분과 브라우저가 각 부분 내에서 실행하는 작업을 검사하는 것이 특히 유용합니다. 예를 들어 다음 이미지에서는 문서 본문의 스타일 변경으로 인해 스타일이 두 번 다시 계산되어 INP가 상당히 높은 것을 확인할 수 있습니다.
Fotocasa는 INP 및 기타 핵심 웹 바이탈 측정항목을 추적하는 시스템을 설정하여 성능 문제가 신속하게 식별되고 해결되도록 했습니다. 측정항목이 Google에서 정의한 범위를 기준으로 특정 기준점을 초과하면 문제가 분석되고 해결될 수 있도록 기여도가 기록됩니다.
이 시스템에서는 web-vitals 라이브러리를 사용하여 Chrome에서 측정되고 다른 Google 도구 (예: Chrome 사용자 환경 보고서, PageSpeed Insights, Search Console의 속도 보고서 등)에 보고되는 방식과 정확히 일치하는 방식으로 실제 사용자로부터 이러한 측정항목을 캡처했습니다.
Fotocasa는 포괄적인 보기와 중앙 집중식 추적을 위해 Datadog를 사용하여 데이터를 수집하고 시각화하여 팀이 정보에 입각한 데이터 기반 결정을 내릴 수 있도록 했습니다. 맞춤 측정항목을 사용하면 비용 효율성을 유지하면서 Fotocasa 웹사이트의 거의 모든 사용자를 더 잘 추적할 수 있습니다.
이 시스템을 통해 Fotocase팀은 수정사항이 측정항목에 영향을 미치는지 또는 예기치 않은 변경사항이 발생하여 측정항목이 손상될 수 있는지 신속하게 모니터링할 수 있습니다. 그런 다음 INP 측정항목을 입력 지연, 처리 시간, 프레젠테이션 지연과 같은 부분으로 분석하여 긴 상호작용 시간의 주요 원인이 되는 상호작용 부분을 정확하게 파악할 수 있습니다.
Fotocasa는 그림 7과 8에 표시된 것과 같은 이상을 감지하자마자 즉시 대응하고 변경이 발생할 수 있는 위치를 정확히 파악하는 데 도움이 되는 또 다른 도구인 OpenSearch를 사용했습니다. web-vitals 라이브러리에서 제공하는 데이터는 타겟 (메트릭 값이 높아지는 원인이 될 수 있는 DOM 요소)을 식별하는 데 도움이 되었으며, 팀이 문제 해결에 더 집중할 수 있도록 지원합니다.
또한 페이지 유형, 기기, 로드 상태와 같은 다양한 필터를 정의하여 시나리오를 간소화하고 INP에 미치는 영향을 더 정확하게 파악할 수 있습니다.
React 개발자 도구
React 개발자 도구는 Fotocasa의 디버깅 기능을 개선하는 데 사용되었으며, 이를 통해 다시 렌더링된 구성요소를 시각적으로 강조 표시할 수 있는 강력한 기능을 제공합니다.
이 기능은 프로파일러 탭으로 이동하여 사용 설정할 수 있습니다. 여기에서 상단 바 오른쪽에 있는 톱니바퀴를 클릭하고 일반 탭으로 이동한 다음 구성요소가 렌더링될 때 업데이트 강조 표시 체크박스를 선택합니다. 이 기능을 활성화하면 구성요소가 다시 렌더링될 때 강조 표시되어 동적인 시각적 표현을 제공합니다.
리렌더링의 원인 파악
다시 렌더링된 구성요소를 식별한 후에는 '왜 발생했지?'라는 질문이 이어집니다. React DevTools는 프레임 그래프 뷰에 유용한 도움말을 표시하여 이 질문에 답합니다.
이 정보에 액세스하려면 프로파일러 세션을 기록하세요. 프로파일러 출력을 분석하면 다음과 같은 유용한 정보를 확인할 수 있습니다.
- 오른쪽 상단에 React 커밋 수가 표시됩니다.
- 플레임 그래프는 구성요소 트리를 시각적으로 나타내며, 회색은 다시 렌더링되지 않은 구성요소를 나타냅니다. 각 막대는 React 구성요소 트리가 변경된 시점과 해당 변경사항이 DOM에 커밋된 시점을 나타냅니다.
- 플레임 그래프의 각 구성요소 위로 마우스를 가져가면 이 렌더링이 발생한 이유 하위 제목 아래에 리렌더링 이유가 표시됩니다.
구성요소 리렌더링의 이유는 다음과 같습니다.
- 구성요소가 처음 렌더링된 경우
- 컨텍스트가 변경됨
- 후크가 변경됨
- 속성이 변경됨
- 주(도)가 변경됨
- 렌더링된 상위 구성요소
렌더링 시간 확인
플레임 그래프의 색상은 의미 있는 정보를 전달합니다. 다양한 파란색 음영과 같은 색상은 다른 구성요소에 비해 구성요소의 렌더링 시간이 비교적 짧았음을 나타냅니다. 반대로 주황색과 빨간색은 구성요소의 렌더링 시간이 더 길다는 것을 의미합니다.
Fotocasa팀에서 문제를 해결한 방법
불필요한 리렌더링 삭제
리렌더링은 React가 새 데이터로 UI를 업데이트해야 할 때마다 발생합니다. 이는 일반적으로 사용자 작업, API 응답 또는 사용자 인터페이스 업데이트가 필요한 기타 중요한 이벤트에서 비롯됩니다. 각 리렌더링은 JavaScript를 실행하므로 한 번에 너무 많은 리렌더링이 발생하면 특히 큰 구성요소 트리에서 기본 스레드가 차단되고 성능 문제가 발생할 수 있습니다.
리렌더링에는 두 가지 종류가 있습니다.
- 필요한 리렌더링: 구성요소가 최신 데이터를 소유하거나 사용하므로 업데이트해야 하는 경우입니다.
- 불필요한 리렌더링: 구성요소가 의미 있는 변경사항 없이 업데이트되는 경우로, 비효율적인 상태 관리 또는 부적절한 속성 처리로 인해 발생하는 경우가 많습니다.
일반적으로 React는 충분히 빠르기 때문에 사용자가 알아차리지 못하므로 몇 번의 불필요한 리렌더링은 큰 문제가 되지 않습니다. 하지만 이러한 작업이 너무 자주 발생하거나 무거운 구성요소 트리에서 발생하면 사용자 환경에 해를 끼치고 페이지의 INP에 부정적인 영향을 미칠 수 있습니다.
Fotocasa팀의 경우가 그랬습니다. 웹사이트에 불필요한 리렌더링이 많이 발생한다는 사실을 알게 되었습니다. 이러한 문제는 다음 두 가지 주요 시나리오에서 발생했습니다.
- 페이지 로드 중: 기본 스레드의 긴 작업 수가 증가하고 첫 번째 상호작용이 지연되어 페이지의 INP에 부정적인 영향을 미쳤습니다.
- 사용자 상호작용 중: 대부분의 상호작용 처리 시간이 증가하여 INP에도 영향을 미쳤습니다.
Fotocasa 웹사이트에서 불필요한 리렌더링이 많이 최적화되었습니다. 가장 큰 최적화는 검색 페이지에서 이루어졌습니다. 페이지 로드 시 불필요한 리렌더링이 3번 있었습니다. 이러한 항목을 삭제한 후 다음과 같은 결과가 관찰되었습니다.
- 긴 작업의 수가 적음 (다음 그림 참고)
- 총 차단 시간 감소 (그림 14와 15 비교)
상태 공동 배치
React 상태를 잘못 배치하면 애플리케이션 속도가 느려지고 UI가 응답하지 않는 것처럼 느껴질 수 있습니다. 구성요소의 상태가 변경되면 이스케이프 해치 (예: memo)를 사용하지 않는 한 하위 구성요소가 기본적으로 다시 렌더링됩니다.
이전 섹션에서 설명한 것처럼 리렌더링이 본질적으로 나쁜 것은 아니지만 특정 상태 업데이트로 인해 전체 페이지를 리렌더링하면 렌더링 후에 DOM 업데이트가 발생하므로 상호작용이 느려질 수 있습니다.
예를 들어 검색 페이지에는 버튼을 클릭하면 모든 필터를 보여주는 대화상자가 있습니다.
이 경우 대화상자의 열림 상태를 제어하는 상태는 검색 페이지에 배치되었습니다. 이 상태가 변경되면 전체 페이지가 다시 렌더링되어 특히 느린 기기에서 INP가 나빠졌습니다. 이는 DevTools에서 CPU 제한을 사용했을 때 확인할 수 있습니다.
변경을 트리거하는 구성요소에 최대한 가까운 상태로 변경하면 이 문제가 해결됩니다. 이 경우 상태를 필터의 버튼 구성요소에 배치하여 상태가 변경될 때 버튼만 다시 렌더링되도록 할 수 있습니다.
불필요한 상태 삭제
상태에는 중복되거나 중복된 정보가 포함되어서는 안 됩니다. 이 경우 불필요한 리렌더링이 발생하여 문제가 발생할 수 있습니다.
예를 들어 Fotocasa 필터 표시줄에는 특정 검색에 적용된 필터 수를 나타내는 텍스트가 있습니다.
적용된 필터 수는 애플리케이션의 상태에서 계산됩니다. 하지만 이로 인해 전체 구성요소가 불필요하게 다시 렌더링되었을 뿐만 아니라 이 구성요소가 서버 측에서 렌더링되므로 특정 경우에는 레이아웃 이동도 발생했습니다.
const [filtersCount, setFiltersCount] = useState(DEFAULT_COUNTER)
useEffect(() => {
const counter = filters
? Object.keys(filters)
?.reduce(reducerCounter, [])
?.filter((param) => searchParams?.[param]).length
: DEFAULT_COUNTER
setFiltersCount(counter)
}, [searchParams]);
이 문제를 해결하기 위해 상태를 사용하는 대신 변수를 사용하여 필터 객체에서 값을 파생했습니다.
const counter = filters
? Object.keys(filters)
?.reduce(reducerCounter, [])
?.filter((param) => searchParams?.[param]).length
: DEFAULT_COUNTER;
비용이 많이 드는 렌더링 줄이기
React 애플리케이션에서 상호작용이 발생하면 일반적으로 상태 변경이 트리거됩니다. 앞서 설명한 것처럼 구성요소의 상태가 변경되면 구성요소와 모든 하위 요소가 다시 렌더링됩니다.
이러한 구성요소 렌더링 함수 중 하나가 느리면 긴 작업이 생성되고 DOM이 업데이트되는 데 시간이 더 오래 걸리므로 페이지의 INP에 부정적인 영향을 미칩니다.
Fotocasa팀은 구성요소의 렌더링 함수 내에서 시간이 많이 걸리는 계산을 최대한 최소화하려고 노력했습니다. Chrome DevTools와 React Developer Tools는 렌더링 작업이 느린 것을 감지하는 데 매우 유용했습니다.
코드 실행 지연
구성요소의 렌더링 함수를 최적화하는 것 외에도 긴 작업이 최대한 최소화되도록 다른 함수도 최적화되었습니다. 하지만 일부 작업은 서드 파티 코드를 사용하므로 최적화할 수 없습니다.
분석이 그 한 예입니다. 이 경우 사용자 상호작용이 발생하면 분석 코드 실행을 지연하고 DOM 업데이트를 우선시하기로 결정했습니다. 이를 위해 Fotocasa팀은 idlefy라는 라이브러리를 사용했으며, 이 라이브러리는 브라우저가 바로 닫히더라도 분석 코드가 계속 실행되도록 했습니다.
실적 문화
성능 작업은 일회성 노력이 아니라 프로덕션에 출시되는 모든 기능과 함께 고려해야 하는 사항입니다. 팀의 모든 구성원이 일치해야 합니다. 그렇지 않으면 코어 웹 바이탈이 회귀할 수밖에 없습니다.
이를 해결하기 위해 Fotocasa팀은 팀 내에서 적극적으로 지식을 공유하고, 재현 방법을 포함하여 Fotocasa의 Datadog RUM 데이터를 기반으로 성능 문제를 식별하기 위한 명확한 프레임워크를 구축했습니다. RUM 시스템에서 코어 웹 바이탈(특히 INP)에 대한 알림이 설정되었으며, Slack에서 Fotocasa팀에 직접 알리도록 구성되었습니다. 이 접근 방식을 통해 성능을 최우선으로 고려하고 회귀로 이어지기 전에 문제를 포착할 수 있었습니다.
결과
Fotocasa에서 INP를 개선하려면 기술적 최적화와 문화적 변화를 결합해야 했습니다. Fotocasa팀은 불필요한 리렌더링을 없애고, 상태 배치를 최적화하고, 비용이 많이 드는 렌더링을 줄이고, 중요하지 않은 코드를 지연시켜 모든 데스크톱 페이지를 '개선 필요'에서 '양호'로 이동하고, '불량' 및 '개선 필요' 페이지를 거의 모두 '양호'로 업그레이드하여 모바일 페이지를 크게 개선했습니다.
이러한 변경사항으로 Fotocasa의 전반적인 사용자 환경이 개선되었으며, 다른 이니셔티브와 함께 문의 및 전화 리드 광고가 27% 증가하여 회사의 주요 비즈니스 측정항목이 직접적으로 강화되었습니다.
Datadog을 사용한 실시간 모니터링을 통해 Fotocasa팀은 INP 개선사항을 검증하고, 이상을 신속하게 감지하고, 회귀를 방지할 수 있었습니다. 이러한 성과 외에도 Fotocasa는 웹 성능을 개발 문화에 포함하여 모든 출시에서 INP와 Core Web Vitals가 우선순위로 유지되도록 했습니다.