Devin.KR

성능 기록과 조건 고정

130분 안팎

학습 목표

성능 지표를 동일 조건에서 반복 측정합니다.

개념

측정할 사용자 행동을 정합니다

행사 목록이 늦다는 제보를 받으면 먼저 어떤 순간인지 묻습니다. 첫 접속에서 제목이 늦게 보이는 문제와 검색어 입력 뒤 카드가 바뀌는 문제는 다른 작업입니다. “느리다”를 “부산의 책 행사를 검색한 뒤 결과를 확인하는 시간이 길다”로 좁힙니다. 이번 목표는 수치를 낮추는 약속보다 같은 행동을 다른 사람이 재현할 수 있는 기록을 만드는 것입니다.

이번 모듈은 m08 미션 solution의 문서·ESM 앱·모의 API·회귀 테스트를 이어받습니다. 공통 환경은 Node 20 이상과 Python 3이며 각 zip 루트에서 npm test를 실행합니다. Node 검사는 추가 설치가 필요 없습니다. 브라우저 검사는 npm ci와 npx playwright install chromium 뒤 npm run test:e2e로 실행합니다. 실제 화면 검사는 외부 환경에서 하고 실행하지 않았다면 PENDING으로 기록합니다.

측정 구간을 먼저 문장으로 정의합니다. 동기 검색 함수 호출 시작부터 반환까지는 계산 시간입니다. renderList 호출부터 반환까지는 DOM 갱신 코드 시간입니다. 클릭부터 눈에 보이는 다음 화면까지에는 이벤트 대기·코드·레이아웃·페인트가 포함됩니다. 세 구간을 모두 “응답 속도”라고 쓰면 원인을 구분할 수 없습니다. 구간의 시작과 끝을 performance.json의 metric에 적습니다.

performance.now는 밀리초 단위의 단조 시간에 기반한 값을 제공합니다. 시작 값을 저장하고 함수 실행 뒤 끝 값에서 뺍니다. 시스템 시각을 사람이 바꾸는 상황에 영향을 받는 날짜 문자열로 짧은 구간을 재지 않습니다. 이 API도 정밀도가 제한될 수 있으므로 작은 소수점 차이를 확실한 사용자 개선으로 해석하지 않습니다. 측정 함수는 원래 반환값을 보존하도록 작성합니다.

제공 timed는 동기 작업 전용입니다. fetch가 반환한 Promise를 그대로 넣으면 응답 완료가 아니라 Promise를 만드는 시간만 측정할 수 있습니다. 비동기 로딩은 시작 값을 잡고 await가 끝난 뒤 차이를 기록하는 별도 구간이 필요합니다. 실패 응답도 끝점을 정해 관찰하되 성공과 섞어 중앙값을 내지 않습니다. 정상·500·연결 실패는 사용자 계약과 측정 경로가 다릅니다.

데이터 크기를 고정합니다. 이번 measure.mjs는 같은 규칙으로 만든 1000개 행사 카드와 같은 제목·지역·날짜를 사용합니다. 실제 서비스 데이터를 내려받지 않으며 행사 개수를 줄여 더 빨라 보이게 만들지 않습니다. 검색 조건도 함께 남깁니다. 빈 검색은 많은 결과를, 없는 단어는 빈 결과를 만들므로 둘을 섞으면 전후 차이가 구현 때문인지 결과 개수 때문인지 알기 어렵습니다.

환경 표에는 실행 시각·운영체제·런타임 또는 브라우저 버전·화면 크기·CPU 제한·네트워크 제한·캐시 정책을 씁니다. 화면을 넓히면 카드 배치가 달라지고 캐시를 켜면 재다운로드가 줄 수 있습니다. 전후 비교에서는 이 조건을 유지합니다. 개발자 도구에서 선택한 CPU 배율은 실제 휴대전화 한 종류의 성능을 정확히 대체하는 값이 아니므로 모의 제한 조건으로 기록합니다.

첫 실행을 포함할지 제외할지도 계약입니다. 모듈 읽기와 초기 데이터 준비가 있는 첫 접속을 재려면 이를 포함합니다. 같은 목록의 반복 갱신을 재려면 각 회 첫 렌더를 준비 단계로 수행한 뒤 두 번째 호출을 잽니다. 이번 Node 비교는 후자입니다. 처음만 제외한 전 값과 전부 포함한 후 값을 비교하지 않습니다. warmup 필드로 이 선택을 다른 사람이 확인하게 합니다.

최소 세 번의 원자료를 남긴 뒤 숫자 오름차순의 가운데 값을 선택합니다. 중앙값은 유난히 큰 한 값에 덜 끌리지만 세 번만으로 모든 환경의 변동을 설명하지는 못합니다. 원자료를 지우고 대표값만 남기면 흔들림을 확인할 수 없습니다. 숫자 배열의 sort에 비교 함수를 빼면 문자열 순서가 되므로 90·7·12 같은 반례로 계산을 검증합니다.

음수·NaN·Infinity·문자열·표본 부족은 정상 기록이 아닙니다. 실습의 median은 유한한 비음수 숫자 세 개만 받도록 정했습니다. 빈 배열을 0으로 바꾸면 미측정이 빠른 실행으로 둔갑합니다. 반면 [0,0,1]의 중앙값 0은 계약상 유효합니다. 짧은 구간의 0은 실제 작업이 없었다는 뜻까지 보장하지 않으므로 해상도와 구간을 함께 읽습니다.

Node DOM 모델은 카드 객체와 문자열을 만듭니다. CSS 배치·이미지 다운로드·페인트를 실행하지 않으며 실제 브라우저 성능 점수도 생성하지 않습니다. npm run measure가 만드는 기록은 모델 범위의 비교 근거입니다. browser.status는 PENDING으로 유지됩니다. 모델 중앙값이 낮아졌다는 이유로 사용자의 모바일 검색이 그만큼 빨라졌다고 리뷰에 적지 않습니다.

브라우저에서는 npm start로 제공 서버를 열고 results.html을 사용합니다. 같은 검색어와 지역을 입력하며 개발자 도구 Performance 기록을 행동 직전 시작하고 결과 표시 뒤 멈춥니다. 첫 로딩과 검색은 별도 기록으로 보관합니다. 화면과 캐시·CPU·네트워크 설정을 적고 같은 절차를 세 번 수행합니다. 서버 종료는 실행한 터미널에서 Ctrl+C로 처리합니다.

계산과 DOM 구간을 좁혀 보고 싶으면 Sources에서 searchEvents와 renderList의 호출 위치를 찾습니다. 시작·끝에 performance.now 구간을 임시로 넣어 ms를 기록하고, 타임라인에서는 그 이후 Layout·Paint 작업을 따로 봅니다. 계측 자체의 로그 비용도 영향을 줄 수 있어 카드마다 console.log를 넣지 않습니다. 임시 계측을 제거한 뒤 회귀 검사를 다시 실행합니다.

performance.json에는 Node 원자료와 브라우저 원자료를 별도 위치에 저장합니다. 브라우저 미실행은 빈 raw와 null 중앙값으로 둡니다. 측정했다면 실제 브라우저 이름·버전·화면·구간·조건을 추가하고 중앙값을 계산합니다. 개선되지 않았으면 그 결과를 그대로 기록합니다. 목표는 특정 수치를 만드는 것이 아니라 다음 수정 후보를 근거로 고르는 것입니다.

오류 메시지는 계산 문제와 실행 문제로 나누어 읽습니다. RangeError가 나면 입력 개수·타입·부호를 확인합니다. ERR_MODULE_NOT_FOUND는 zip 루트가 맞는지와 assets 경로를 확인합니다. Playwright 실행 파일 부재는 성능 실패의 근거가 아니라 환경 미준비입니다. 설치 뒤 재실행하기 전까지 브라우저 통과나 실패를 주장하지 않습니다.

실습에서는 measurement.mjs의 median TODO를 완성합니다. measurement-test.mjs의 숫자 순서·원본 불변·0·동률·잘못된 표본과 timed 반환값 보존 검사를 읽습니다. npm test 통과 후 npm run measure로 본인 실행값을 만들고 기록의 한계를 설명합니다. 더 읽기의 성능 장은 사용자 지표와 다양한 최적화 전략을 넓히는 자료이며 이번 기록을 대신 작성해 주지는 않습니다.

API와 명령 세부 확인: performance.now API를 참고합니다.

따라하기

중앙값과 원본을 확인합니다

독립 예제를 demo.cjs에 저장하고 node demo.cjs로 실행합니다. 출력의 의미를 본문 계약과 비교합니다.

const values=[90,7,12]; console.log([...values].sort((a,b)=>a-b)[1]);console.log(JSON.stringify(values));

실행 결과

12
[90,7,12]

측정 반환값과 구간을 구분합니다

독립 예제를 demo.cjs에 저장하고 node demo.cjs로 실행합니다. 출력의 의미를 본문 계약과 비교합니다.

const {performance}=require('node:perf_hooks');const start=performance.now();const value=[1,2,3].filter(x=>x>1);const ms=performance.now()-start;console.log(JSON.stringify(value));console.log(ms>=0);

실행 결과

[2,3]
true

표본 계약을 구현합니다

measurement.mjs에서 원본을 바꾸지 않고 숫자순 가운데 값을 반환합니다. 잘못된 표본 거절은 유지합니다.

npm test

실행 원자료를 생성합니다

npm run measure로 Node의 실제 원자료를 생성합니다. 브라우저 미실행은 PENDING으로 유지합니다.

확인 문제

실습

measurement.mjs의 median TODO를 구현합니다. 숫자순 정렬·원본 불변·0·동률·입력 거절을 검사하고 npm run measure로 Node 기록을 생성합니다. 실제 브라우저 조건·로딩·검색 시간은 별도 관찰하며 미실행은 PENDING으로 둡니다.

시작 코드·테스트 내려받기

실행 명령

npm test

기대 결과

starter는 해당 주제 assertion 실패, solution은 기존 회귀와 새 모델 검사 전부 통과

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 컴포넌트의 상태 위치를 정한 사례를 설명합니다.