브라우저 개발자 도구 활용
Elements·Console·Sources·Network·Application·Performance 패널을 증상별로 골라 클라이언트 문제를 추적합니다.
브라우저 개발자 도구(browser developer tools)는 웹 개발에서 디버깅, 성능 분석, UI 문제 해결에 꼭 필요한 도구입니다. 복잡한 Next.js 애플리케이션일수록 개발자 도구를 얼마나 잘 다루는지가 문제 해결 속도와 개발 효율을 크게 좌우합니다. 이 글에서는 Chrome 개발자 도구를 중심으로 주요 패널의 기능과, Next.js 애플리케이션을 디버깅할 때의 활용 방법을 살펴봅니다.
개발자 도구 열기
Chrome, Firefox, Edge, Safari 같은 대부분의 최신 웹 브라우저는 개발자 도구를 내장하고 있습니다. Chrome에서는 웹 페이지에서 마우스 오른쪽 버튼을 눌러 검사(Inspect)를 고르거나, Windows와 Linux에서는 Ctrl + Shift + I 또는 F12를, macOS에서는 Cmd + Option + I를 눌러 엽니다. 개발자 도구는 보통 브라우저 창의 아래쪽이나 옆쪽에 붙은 형태로 나타납니다.
브라우저 도구는 현재 페이지에서 무슨 일이 일어났는지 확인하는 창입니다. Elements에서 DOM을 바꿔 화면이 달라졌다고 해서 서버 코드나 DB를 고친 것은 아닙니다. 브라우저에서 본 요청 결과와 서버 내부의 처리도 서로 다른 자료이므로, 재현한 요청과 동작을 기준으로 관련 로그를 연결해 봅니다.
주요 패널 활용법
Chrome 개발자 도구는 기능이 다른 여러 패널로 구성되어 있습니다. Next.js 개발에서 자주 쓰는 패널들을 중심으로 살펴봅니다.
Elements 패널
Elements 패널은 웹 페이지의 HTML과 CSS 구조를 실시간으로 확인하고 수정하는 곳입니다. 렌더링된 DOM 트리를 탐색하고, 각 요소에 적용된 스타일을 검사하며, 스타일을 바로 바꿔 시각적인 효과를 확인할 수 있습니다. 왼쪽의 DOM 트리에서 요소를 클릭하거나, 페이지 위에서 요소 선택 도구(마우스 포인터 아이콘)로 특정 요소를 고를 수 있습니다.
선택한 요소의 정보는 여러 탭으로 나뉘어 보입니다.
- Styles 탭: 선택한 요소에 적용된 모든 CSS 규칙과 각 규칙의 출처(파일, 줄 번호)를 보여 줍니다. 속성 값을 클릭해 바로 고치거나, 새 규칙을 더하거나, 기존 규칙을 껐다 켤 수 있습니다.
- Computed 탭: 브라우저가 최종적으로 계산한 요소의 모든 스타일 속성(상속된 속성 포함)을 보여 줍니다.
- 박스 모델과 Layout 탭: Styles와 Computed 영역의 박스 모델에서 margin, border, padding, content를 확인하고, Layout 탭에서는 Grid와 Flexbox 같은 레이아웃의 오버레이를 켜고 배치를 검사합니다. 위치와 표시 방식은 Chrome 버전에 따라 달라질 수 있습니다.
- Event Listeners 탭: 선택한 요소에 연결된 JavaScript 이벤트 리스너를 보여 주고, 소스 코드로 이동할 수 있게 합니다.
Next.js에서는 Tailwind CSS나 CSS Modules의 스타일이 예상대로 적용되지 않을 때, Elements 패널에서 실제로 적용된 클래스와 스타일을 확인해 원인을 찾습니다. 하이드레이션 오류(Hydration failed...)를 진단할 때도 쓰지만, Elements가 보여 주는 것은 현재의 DOM이지 초기 응답을 그대로 보존한 화면이 아닙니다. 그래서 Network의 문서 응답과 현재 DOM을 비교하고, React가 알려 준 오류 위치를 함께 확인합니다.
Elements의 DOM 트리에서 텍스트나 속성을 직접 바꾸는 작업은 현재 페이지에서의 실험입니다. 새로고침하거나 React가 다시 렌더하면 원래 데이터에 따른 값으로 돌아갈 수 있으며, 소스에 반영하려면 프로젝트 파일을 고쳐야 합니다. Sources의 Local overrides나 Workspaces로 저장하는 흐름과도 구분해야 하며, Local overrides는 Elements DOM 트리의 편집을 저장하지 않습니다.
Console 패널
Console 패널은 JavaScript 코드가 실행되며 생기는 오류 메시지, 경고, console.log와 console.error 같은 디버그 메시지를 보여 줍니다. 클라이언트에서 발생한 JavaScript 오류와 경고가 실시간으로 표시되고, 메시지를 클릭하면 해당 코드의 소스 위치로 이동할 수 있습니다. console.log()의 출력은 현재 브라우저 실행 컨텍스트의 로그이며, 메시지 레벨이나 필터 설정에 따라 숨겨질 수 있고, 서버 로그 전체가 이 패널에 나타나지는 않습니다. 콘솔 아래쪽의 명령 프롬프트에 JavaScript 코드를 직접 입력해 변수 값을 확인하거나 함수를 불러 볼 수도 있습니다.
Next.js에서는 로그의 실행 위치를 먼저 구분합니다. 서버 컴포넌트와 Route Handler의 로그는 개발 터미널이나 배포 환경의 서버 로그에서 확인합니다. 클라이언트 컴포넌트도 서버에서 사전 렌더링될 때 렌더 본문의 로그가 터미널에 나타날 수 있으며, 하이드레이션 뒤의 렌더 로그는 브라우저 콘솔에도 나타납니다. useEffect와 이벤트 핸들러처럼 브라우저에서만 실행되는 코드의 로그는 브라우저 콘솔에서 확인합니다. 클라이언트 컴포넌트의 오류나 예상하지 못한 동작을 추적할 때, React 컴포넌트의 상태나 props 값을 console.log로 출력해 흐름을 따라가는 방법도 자주 씁니다.
Sources 패널
Sources 패널에서는 웹 페이지를 구성하는 JavaScript, CSS, HTML 파일을 확인하고, JavaScript 코드에 중단점(breakpoint)을 두어 실행을 멈추고 디버깅할 수 있습니다. 왼쪽 탐색기에는 브라우저에 제공된 스크립트와 쓸 수 있는 Source Map이 보이며, 서버의 로컬 .next 폴더 전체나 서버 전용 소스가 브라우저에 공개되지는 않습니다.
코드의 줄 번호를 클릭하면 중단점이 생깁니다. 실행이 그 지점에 이르면 멈추고, 변수 값을 검사하거나 Step over와 Step into로 코드를 한 줄씩 실행할 수 있습니다. 멈춘 상태에서는 Scope 영역이 현재 스코프의 모든 변수 값을, Watch 영역이 미리 지정한 변수의 값을 보여 주며, Call Stack은 현재 실행 중인 함수의 호출 경로를 보여 주어 실행 흐름을 파악하게 돕습니다.
클라이언트 컴포넌트 안의 복잡한 로직이나 이벤트 핸들러에서 문제가 생겼다면 중단점을 두고 깊이 디버깅합니다. Next.js 개발 모드는 Source Map을 제공하므로, DevTools의 파일 검색에서 원본 TypeScript나 JavaScript 파일 이름을 찾습니다. Sources 패널의 가상 경로 표시는 Turbopack과 Webpack, 브라우저 버전에 따라 달라지므로 특정 webpack:// 경로를 전제로 하지 않습니다.
Network 패널
Network 패널은 DevTools를 열고 기록하는 동안 해당 페이지가 보낸 네트워크 요청을 분석합니다. 서버가 DB나 외부 API로 보내는 내부 요청은 브라우저의 Network에 개별 행으로 나타나지 않습니다. 패널에서 확인할 수 있는 정보는 다음과 같습니다.
- 요청 목록: 페이지 로딩에 필요한 모든 자원(HTML, CSS, JS, 이미지, 폰트, API 호출)이 시간 순서대로 표시됩니다.
- 상태 코드: 각 요청의 HTTP 상태 코드(200 OK, 404 Not Found, 500 Internal Server Error 등)를 확인합니다.
- 유형과 크기: 요청한 자원의 유형(JS, CSS, Img, Fetch/XHR 등)과 크기를 보여 줍니다.
- 시간: 각 자원의 로딩 시간과 대기 시간(TTFB, Time to First Byte) 등을 자세히 보여 줍니다.
- Preview와 Response 탭: API 요청의 응답 데이터(JSON, HTML 등)를 미리 보거나 전체 응답 내용을 확인합니다.
- Headers 탭: 요청과 응답의 헤더 정보를 보여 주며, 특히
Cache-Control같은 캐시 관련 헤더를 확인할 때 유용합니다.
Next.js에서는 Route Handler나 서버 액션 요청의 상태 코드, 응답 데이터, 소요 시간을 확인해 서버와 주고받는 단계의 실패를 좁힙니다. 과도하게 큰 이미지나 JavaScript 파일, 느린 API 응답 등을 찾아 성능 병목을 발견하는 데도 씁니다. Disable cache는 DevTools가 열린 동안 브라우저 HTTP 캐시를 재사용하지 않게 할 뿐, Service Worker, CDN, Next.js 서버의 캐시까지 지우는 옵션은 아닙니다. 캐시를 확인할 때는 Cache-Control과 실제 응답, 반복 요청을 함께 봅니다. 헤더는 캐시 정책의 단서일 뿐이며, 이것만으로 서버 내부의 Data Cache나 모든 계층의 적중 여부를 판정하지 않습니다.
Timing과 Size로 알 수 있는 범위도 구분합니다. Network의 Waiting(TTFB)은 요청을 보낸 뒤 첫 응답 바이트를 기다린 구간입니다. 서버가 응답을 준비하는 시간과 네트워크 왕복이 함께 들어가므로, 이 값을 DB 실행 시간으로 그대로 읽지 않습니다. Content Download는 본문을 받는 단계입니다. Waiting이 800ms, Download가 20ms라면 첫 바이트 전의 지연을 먼저 조사할 수 있지만, DB가 800ms였는지는 서버 내부의 측정으로 확인해야 합니다.
압축 전 자원이 600KiB이고 전송한 본문이 120KiB라면 크기 비율은 5:1입니다. 이것이 JavaScript 실행 시간도 5분의 1이라는 뜻은 아닙니다. 브라우저 캐시에서 가져온 자원은 새 본문을 전송하지 않아도 파싱, 실행, 렌더링 작업이 남을 수 있으므로 Performance 기록과 함께 봅니다.
Network의 Replay XHR은 선택한 요청을 다시 보냅니다. 주문 생성처럼 상태를 바꾸는 요청이라면 재전송이 새로운 업무 실행이 될 수 있습니다. 실제 주문을 반복해서 만들지 말고 통제된 테스트 데이터에서 재현하며, 중복 요청을 한 작업으로 처리해야 하는 기능이라면 서버의 멱등성 키 처리 같은 중복 방지 규칙을 따로 확인합니다.
Application 패널
Application 패널은 웹 애플리케이션의 클라이언트 쪽 저장소(Local Storage, Session Storage, IndexedDB, 쿠키)와 Service Worker, Cache Storage 등을 검사하고 관리합니다. Local Storage, Session Storage, Cookies에서는 클라이언트에 저장된 데이터를 확인하고 고치거나 지울 수 있어, 인증 토큰이나 사용자 설정을 디버깅할 때 유용합니다. Cache Storage에서는 Cache API로 저장한 응답 목록을 확인하고 관리합니다. Service Worker뿐 아니라 페이지 코드가 기록한 항목도 여기에 나타날 수 있으며, PWA를 개발할 때 특히 유용합니다.
Next.js에서는 Auth.js 같은 인증 도구를 쓸 때 쿠키에 저장되는 세션 토큰의 상태를 확인해 로그인 문제를 진단합니다. PWA 기능이나 Service Worker를 쓰는데 캐시된 데이터가 예상과 다르다면, 이 패널에서 캐시 내용을 확인하거나 비울 수 있습니다.
Performance 패널
Performance 패널은 웹 페이지의 런타임 성능을 기록하고 자세히 분석하며, 프레임 드롭, 레이아웃 스래싱(layout thrashing), 긴 JavaScript 작업 등을 찾는 데 쓰입니다. 기록(Record) 버튼을 눌러 사용자 상호작용이나 페이지 로딩 과정을 기록하면, 기록이 끝난 뒤 자세한 타임라인 그래프와 요약 정보를 보여 줍니다. CPU 사용량, 네트워크 활동, 렌더링 과정이 함께 표시되고, Flame Chart는 JavaScript 함수의 호출 스택을 시각적으로 나타내 어떤 함수가 많은 시간을 쓰는지 바로 보여 줍니다.
React 컴포넌트의 성능을 분석할 때는 React Developer Tools의 Profiler와 함께 써서 렌더링 주기와 다시 렌더링의 원인을 살피고 불필요한 렌더링을 줄입니다. 이때 개발 빌드나 프로파일링을 지원하는 빌드를 씁니다. 일반 프로덕션 빌드는 기본적으로 React 프로파일링이 꺼져 있어 DevTools 설정만으로는 켤 수 없으며, Next.js에서는 next build --profile로 프로파일링을 켠 프로덕션 빌드를 만들 수 있습니다. 복잡한 애니메이션이나 스크롤 중에 버벅거림(jank)이 생긴다면, 레이아웃 재계산이나 페인트 작업이 지나치게 일어나는지 이 패널에서 확인합니다.
디버깅 흐름 예시
Next.js 애플리케이션에서 문제를 발견했을 때 패널을 고르는 순서를 예로 들어 봅니다. 특정 기능이 동작하지 않거나, 페이지 로딩이 느리거나, UI가 깨지는 문제를 발견했다면 먼저 Console과 Network로 초기 진단을 합니다. Console에서 클라이언트 쪽 오류 메시지나 경고가 있는지 확인하고, Network에서 API가 약속한 상태 코드와 응답 본문을 받았는지, 지연은 없는지 확인합니다. 이때 200만으로 업무 처리가 성공했다고 단정하지 않습니다. 서버 쪽 로그가 필요하다면 로컬 개발 환경의 터미널이나 Vercel 대시보드의 Logs 탭을 확인합니다.
UI 레이아웃이 깨지거나 스타일이 적용되지 않는다면 Elements 패널에서 해당 요소를 선택해 적용된 CSS 규칙을 검사하고, 스타일을 바로 고쳐 보며 원인을 좁힙니다. JavaScript 로직이나 상태 관리에서 문제가 생겼다면 Sources 패널로 이동해 의심되는 줄에 중단점을 두고, 페이지를 새로고침하거나 해당 기능을 실행합니다. 실행이 멈추면 Scope와 Watch 영역에서 변수 값을 확인하고, Call Stack으로 함수 호출 흐름을 파악하며, Step over와 Step into로 한 줄씩 실행하며 동작을 분석합니다.
페이지 로딩이 느리거나 상호작용이 버벅거린다면 Performance 패널에서 기록을 시작해 병목을 시각적으로 분석하고, Network 패널에서 큰 자원이나 느린 요청을 다시 확인한 뒤 필요하다면 next/image, next/script 등으로 최적화를 시도합니다. 로그인 상태가 유지되지 않거나 캐시된 데이터가 잘못되었다면 Application 패널에서 쿠키, Local Storage, Cache Storage 등을 확인하고, 필요하면 데이터를 지운 뒤 다시 테스트합니다.
각 패널의 기능을 익히고 여러 문제 상황에 적용해 보면, 어떤 Next.js 프로젝트에서도 문제의 위치를 빠르게 좁히고 애플리케이션의 품질을 높일 수 있습니다.
정리
- 브라우저 도구는 현재 페이지에서 일어난 일을 보여 주며, Elements에서 바꾼 DOM은 소스나 DB를 바꾸지 않습니다.
- Elements는 현재 DOM과 적용된 스타일을, Console은 브라우저 실행 컨텍스트의 로그와 오류를 보여 줍니다.
- Sources에서는 중단점, Scope, Watch, Call Stack으로 클라이언트 코드를 한 줄씩 추적합니다.
- Network는 브라우저가 보낸 요청만 기록하며, TTFB는 서버 준비와 네트워크 왕복을 합친 값이고 전송 크기 비율은 실행 시간 비율이 아닙니다.
- Application은 쿠키, 저장소, Cache Storage를, Performance는 긴 작업과 렌더링 비용을 확인하는 곳입니다.
연습 문제
1. HTTP 200과 업무 실패
가상의 장바구니 Route Handler가 HTTP 200과 함께 응답 본문에 success: false와 재고 부족 안내를 보냈다고 합시다. 화면에서 상품이 추가되지 않았을 때, 200이라는 상태만 보고 저장에 성공했다고 결론 낼 수 있을까요? 응답과 DB 변경 여부는 각각 어디에서 확인해야 할까요?
해설 보기
먼저 Network의 Response에서 업무 실패 값과 안내를 확인합니다. HTTP 200은 이 가상 핸들러가 고른 전송 상태일 뿐, 저장 성공을 뜻하지 않습니다. 서버의 DB 조회와 변경은 브라우저의 요청 목록에 따로 나타나지 않으므로, 서버에서 확인한 처리 결과와 안전한 로그를 그 요청에 연결해 봅니다. 원인을 확인하지 않고 Elements에서 성공 문구만 바꾸어도 저장 결과는 바뀌지 않습니다.
이 연습은 원래 앱에 없는 응답 형식을 가정했습니다. 실제 앱에서는 구현된 상태 코드, 본문, 오류 처리를 먼저 읽고 같은 확인 방법을 적용합니다.
2. Elements에서 바꾼 가격
Local overrides와 Workspaces를 쓰지 않고 Elements의 DOM 트리에서 상품 가격을 29에서 19로 바꿨습니다. 새로고침하면 29로 돌아옵니다. 서버 코드나 DB의 가격이 바뀐 것일까요?
해설 보기
바뀌었다고 볼 이유가 없습니다. 현재 DOM의 표시만 바꾸었고, 새로고침은 원래 응답과 앱 데이터로 화면을 다시 만듭니다. 원본의 수정은 프로젝트 파일에서, DB 값의 변경은 서버 처리와 저장 결과에서 확인해야 합니다.
3. TTFB와 DB 시간
요청의 Waiting(TTFB)은 800ms, Content Download는 20ms입니다. 이 결과로 DB 조회가 정확히 800ms였다고 보고해도 될까요?
해설 보기
안 됩니다. TTFB에는 서버의 응답 준비와 네트워크 왕복 등이 함께 들어갑니다. 해당 요청의 서버 로그나 추적에서 DB 조회의 시작과 종료, 다른 처리 구간을 확인해 원인을 좁힙니다. Download가 짧다는 사실만으로 서버 내부의 어떤 작업이 긴지 알 수는 없습니다.
4. 전송 크기와 처리 비용
자원 본문의 압축 전 크기는 600KiB, 전송 크기는 120KiB입니다. 두 크기의 비율을 구하세요. 다음 실행에서 본문을 브라우저 캐시에서 가져와 새 전송량이 0이어도 JavaScript 처리 비용이 0일까요?
해설 보기
600 ÷ 120 = 5이므로 크기 비율은 5:1입니다. 이 비율은 시간 비율이 아닙니다. 새 본문을 전송하지 않아도 스크립트가 실행되거나 화면이 다시 렌더될 수 있으므로, 실제 처리 비용은 Performance 기록으로 확인합니다.
5. Replay XHR과 중복 주문
주문 생성 XHR은 요청을 받을 때마다 새 주문을 만들며, 중복 처리를 막는 키나 정책은 없습니다. 한 번 성공한 요청에 Replay XHR을 한 번 더 실행하면 같은 주문을 조회하는 것일까요? 테스트와 실제 중복 방지의 역할을 나누어 설명하세요.
해설 보기
아닙니다. 주어진 서버는 요청마다 새 주문을 만들므로 재전송으로 두 번째 주문이 생길 수 있습니다. 재현은 통제된 테스트 데이터에서 합니다. 재시도까지 같은 업무로 처리해야 한다면, 서버가 같은 멱등성 키를 원래 결과와 연결하는 등의 중복 방지 규칙을 구현하고 검증해야 하며, Replay XHR 자체가 그 규칙을 제공하지는 않습니다.
패널별 기능은 Chrome DevTools 문서에서, React 컴포넌트의 렌더링 분석은 React Developer Tools 안내에서 확인할 수 있습니다.
다음 글 서버 사이드 로깅 및 모니터링에서는 서버에서 일어나는 일을 기록하고 운영 중인 애플리케이션을 모니터링하는 방법을 알아봅니다.