주니어 개발자들을 보면, 끊임 없이 성장하고 싶다는 마음으로 최선을 다해 공부를 한다. 나도 자바스크립트의 기초를 탄탄하게 다지기 위해서 '모던 자바스크립트 Deep Dive'와 같은 프론트엔드에 관련된 책들을 읽기도 하고, 기술 블로그들을 보며 지식을 넓히기도 한다. 그리고 좋은 디자인 패턴 혹은 컴포넌트 설계 기법들을 고민하면서 개발하고 있다.
하지만 이런식으로 언어, 프레임워크 및 라이브러리(React, Next, Vue 등), 프론트엔드 개발 방법론 등의 공부만 하게 되면, 중요한 사실을 하나 놓칠 수 있다. 일의 능률을 올리기 위한 방법들도 성장의 한 부분이라는 사실 말이다. 가장 기본적인 예시로는 단축키가 있다.
단축키의 숙지를 미루는 개발자들이 있다. 그래 바로 나였다. 일단 해오던 대로 하고, 단축키는 나중에 익히면 된다는 마인드였다. IDE를 시작할 때, 어떤 단축키들이 있는지 살펴보고, 해당 단축키들을 익히려면 생각보다 시간을 많이 투자해야 하기 때문이다.
하지만 내가 마우스로만 처리하던 것들을, 혹은 반복적으로 하던 작업은 다른 개발자는 단축키를 이용하여 빠르게 처리한다. 이는 당연히 개발 속도에도 영향을 미친다. 개발이 모니터 앞에서 깊은 상념에 빠져, 복잡한 로직이나 효율적인 코드만 구현할 것 같지만 반복적인 일을 하게 되는 경우도 많다.
오늘의 주제는 아니지만, 위의 질문들을 보며 찔리는 개발자들은 단축키도 익혀보는 것을 추천한다.
다양한 스킬들을 사용해보며 라이브러리나 프레임워크에 대해 깊게 공부하고, 테스트 코드 작성을 연습하는 것 등이 매우 중요한 것은 사실이다. 하지만 일을 잘하기 위해 고민하는 것도 성장이다. 지금은 코드를 잘 짜는 것을 말하는 것이 아니다. 일의 능률을 올리는 방법을 말한다.
반복적인 작업들을 빠르게 처리하고, 중요한 기능에 시간을 투자하자.
단축키 외에도 일의 능률을 올리기 위한 방법에는 다양한 것들이 있다. 프로젝트에서 사용하고 있는 라이브러리나 프레임 워크가 제공하는 Dev Tool을 잘 이용하는 것이다. React Dev Tool, Redux Dev Tool,Vue Dev Tool 등 데브 툴에 능숙하다면 버그 수정이나 개발을 효율적으로 할 수 있다.
처음에 잘 익혀두고, 상황에 맞게 사용하는 습관을 들이자. 처음에 습관을 잘못 들여 놓으면, 나중에는 더 고치기 귀찮기 때문이다.
오늘은 위와 같은 이유로 일을 잘하기 위한 방법 중 하나인 개발자 도구를 잘 다루는 방법을 소개하려고 한다. 이 글을 읽은 주니어 개발자들은 추후에 사수에게 이쁨은 받았으면 좋겠다.
나는 그러지 못했으니까.. 여러분이라도..
사수님들이 개발자 도구를 사용하는 것을 보면, 나는 개발자 도구를 사용하는 것이 아닌 사용하는 시늉을 하는 것 같다. 그렇다고 어떻게 사용하는지 물어보는 것도 애매하다. 그 이유는 개발자 도구의 기능은 하나씩 뜯어 보면 매우 많고, 본인들도 많이 사용하다 보니 능숙해진 것이기 때문이다.
하지만 분명 경험에서 우러나오는 노하우나 회사의 프로젝트 특성상 잘 사용하는 기능들도 있을 것이다. 여기서 우리가 취할 수 있는 액션은 하나다. 단축키에 익숙해지기 위해 IDE의 단축키 리스를 쭉 보는 것처럼, 개발자 도구에는 어떤 기능들이 있는지 큼지막하게라도 살펴보는 것이다. 그 후에 딥하게 사용하는 방법들에 대해서는 질문을 해보자. 오늘도 개발자 도구의 모든 기능을 살펴보진 않을 것이다.
Console Tab은 따로 설명하지 않겠다.
이제부터 모든 예시는 취준시절 만들었던 futurama 사이트에서 들겠다. 지금 보면 창피한 작품이지만, 개발자 도구 사용법의 예시로 들고오기 딱 좋아서 가져왔다. 개발자 도구는 'F12' 또는 '마우스 오른쪽 클릭 -> 검사'를 누르면 나타난다는 사실은 모두 알것이라고 믿는다.
프론트엔드를 입문하는 개발자도 많이 사용하는 탭이 있다. 바로 Element Tab이다. 내가 작성한 코드대로 태그
, class
, style
, script
등이 잘 반영되었는지 확인해 볼 수 있다.
개발자 도구의 Dom Tree
에서 원하는 부분에 Hover
를 하면, 해당 부분이 어떤 부분인지 명확한 시각으로 보여준다. 그 뿐만 아니라 내가 선택한 요소를 Drag & Drop을 이용하여 다른 위치로 옮길 수도 있고, 삭제도 가능하다.
클릭한 요소를 자세히 보면, 빨간색 선 부분으로 $0
을 표시해놓았다. 이것은 의미 없는 값이 아니다. 최근에 클릭한 순서대로 $0 ~ $4
와 같이 Node
를 저장하고, console
에서 접근할 수 있게 된다. 위에서는 $0
에 test
라는 class
를 추가해 보았다.
선택된 요소가 호버, 포커스 등의 상태인 경우 화면에서 어떻게 보여지는지 확인하고 싶다면 위의 기능을 이용할 수도 있다. 물론 화면에서 그냥 해당 부분에 마우스를 올려 호버를 확인할 수 있다. 하지만 위의 기능을 이용하면, 선택한 요소에 마우스를 올리지 않고도 호버된 UI를 볼 수 있다.
지금 알아볼 기능은 Element tab을 많이 사용해본 주니어 개발자라도 생소할 수 있는 기능이다. 요소의 ...
을 누르면 Context Menu가 나타나는데, 주목할 부분은 Break on
이다.
특정 이벤트가 발생하거나 속성값이 변경되는 시점에서 중단점을 설정할 수 있다. DOM 요소에 관련된 디버깅을 할 수 있게 된 것이다. 이제 중단점을 이용하여 DOM 요소의 상태를 쉽게 파악하고, 문제가 있는 코드를 발견하고 수정할 수 있다.
- Subtree modifications: 자식 요소가 추가되거나 제거되는 경우 중단점을 설정
- Attribute modifications: 요소의 속성이 변경되는 경우 중단점을 설정
- Node removal: 요소가 삭제되는 경우 중단점을 설정
예를 들어, 문제지의 정답을 표시해주는 버튼이 있다. 첫 번째 문제를 감싸고 있는 li
태그에 정답인 Philip
이 추가 또는 삭제될 때, 디버깅을 걸고 싶다고 가정하자. 이 경우에 해당 li
태그에 Subtree modifications
옵션으로 Break on
을 하면 되는 것이다. 실제로 정답이 추가 및 삭제 되는 경우 디버거가 중단되는 것을 볼 수 있다. (브레이크 된 요소는 왼쪽에 동그라미
가 추가된다.)
이렇게 추가된 DOM Breakpoints는 Element 또는 Sources tab에서 확인할 수 있다.
Event Listeners
패널은 선택한 DOM 요소에 연결된 이벤트 리스너 정보를 보여준다. Open Answer
버튼 요소를 선택하면, click
이벤트 리스너가 바인딩 되었다고 알려주고 있다.
이벤트 리스너 정보를 확인하면서 디버깅을 하는 경우, 해당 이벤트 리스너가 어디서 등록되었는지도 확인할 수 있다. 예를 들어, 이벤트 리스너 함수가 외부 라이브러리에서 등록된 경우에는 그 라이브러리의 소스 코드로 이동하여 디버깅을 진행할 수 있는 것이다.
Ancestors
는 선택한 요소 뿐만 아니라 상위 요소에 바인딩된 이벤트 리스너까지 모두 보여준다.
나는 Network Tab을 사용한 이유중 대부분은, 정상적인 요청이 발생했는지 요청 세부 정보를 파악하는 것이었다. Header에서는 요청 헤더와 응답 헤더를 확인할 수 있고, Preview는 요청과 응답의 형식과 내용을 간단하게 확인, Response는 응답에 대한 상태 코드와 데이터들을 볼 수 있다.
특정 유형의 요청만 표시할 수 있는 필터링, 페이지를 이동해도 기록을 저장하는 Preserve log
, 캐시하지 않고 항상 서버로 요청을 하는 Disable cache
와 같은 기능들도 있으니 자세한 사항이 궁금하면 찾아보자.
그리고 slow 3G
또는 커스텀하게 만든 throttling
으로 네트워크의 요청을 느리게 만들어 애플리케이션의 동작을 확인하기도 했다. 간단한 예시로는 로딩 처리가 있을 것이다.
Waterfall
은 각각의 네트워크 요청에 대한 타이밍 정보를 그래프를 이용하여 시각적으로 표시해준다. 파란색 막대는 요청을 나타내고, 응답을 기다리고 있는 상태이다. 네트워크 타이밍에서 DNS Lookup, Initial Connection, SSL/TLS Handshake 단계를 나타낼 수 있다. 빨간색 막대는 응답으로, 요청이 끝났다는 의미이다. 네트워크 타이밍에서 Time to First Byte (TTFB)와 Content Download 단계를 나타낼 수도 있다.
최 하단을 보면 페이지의 DCL(DOMContentLoaded)와 Load도 확인해 볼 수 있다.
DCL 이벤트는 DOM 트리 구조가 완성되었지만, 외부 리소스 (CSS, 이미지, 스크립트 등)의 로딩이 완료되지 않은 상태이다. 보통 자바스크립트 코드가 DCL 이벤트에 연결되어 해당 이벤트가 발생할 때 추가적인 작업을 수행할 수 있다. Load 이벤트는 웹 페이지의 모든 리소스 (HTML, CSS, 이미지, 스크립트 등)가 로드되고 렌더링이 완료된 시점이다. 보통 자바스크립트 코드가 Load 이벤트에 연결되어 해당 이벤트가 발생할 때 추가적인 작업을 수행하거나 페이지 초기화 등의 로직을 처리한다.
유용하게 사용할 수 있는 Block request URL
도 있다. 특정 URL에 대한 네트워크 요청을 차단한다. 평소에 코드를 작성하면서 catch
문과 같은 에러 처리를 할 것이다. 개발 중 확인해보기 위해 콘솔로 에러 메시지를 띄워볼 수도 있고, 서비스를 이용하는 유저에게 토스트 UI로 어떤 이유로 에러가 발생했는지 친절하게 알려줄 수도 있다. 이렇게 자신이 구현한 애플리케이션의 동작을 테스트하거나 특정 리소스를 제어하기 위해 사용할 수 있다.
한 번이라도 개발자 도구를 사용해본 개발자라면 모두 Console Tab은 알 것이다. 의도적으로 로그를 남기면서 기능을 개발하기에 편하기 때문에 많이 사용해보았을 것이다. 심지어 콘솔 탭은 어딘가에 에러가 발생하면 위와 같이 로그를 남겨주기도 한다. 에러 로그를 클릭하면 Sources Tab의 Page로 이동하고, 에러가 발생하고 있는 코드를 직접 보여준다.
(위는 일부로 charactor detail 페이지에 오류 코드를 넣었다.)
그리고 스크립트나 CSS를 수정할 수 있고, 코드 라인에 브레이크 포인트를 걸어 디버깅도 가능하다. 에러가 발생한 코드를 파악하고, 디버깅으로 원인을 분석하여 해결하기에 매우 좋은 기능이다.
왼쪽의 번호 부분을 클릭하여, 에러를 발생시키고 있는 것 같은 코드에 브레이크 포인트를 걸고 새로 고침을 해보자. 해당 지점을 수행하기 전에 paused in debugger
상태가 되었다. 그리고 Call Stack
을 보면, 파란색 화살표로 CharactorDetail
를 가리키고 있다. 현재 콜 스택의 위치는 동적 라우팅을 하고 있는 CharactorDetail([id].tsx
)라는 의미이다. 이렇게 Breakpoint
시점의 콜 스택을 볼 수 있다.
호출 스택 프레임들은 현재 함수를 호출한 상위 함수들을 나타내며, 실행 흐름을 따라 추적할 수 있다.
resume script execution(F8)
를 눌러 보자. console
를 보면 에러가 발생하고 있고, scope
의 random
은 undefined
이다. 여기서 우리는 random
이라는 상수를 선언 및 할당하는 과정에서 문제가 있다는 사실을 파악할 수 있다. 지금은 간단한 예제이기 때문에 이 작업을 거치지 않고도 문제점을 알 수 있지만, 복잡한 상황일수록 디버깅의 효율은 극대화된다는 점을 명심하자.
특정 상황에 따라 코드의 실행을 중단시키거나 로그를 출력하는 중단점(Breakpoint)도 있다. 컨디셔널 포인트(Conditional Breakpoint)는 특정 조건이 충족되면 중단시킨다. 로그 포인트(Logpoint)를 설정하면, 코드가 실행되는 도중에 멈추는 것이 아니라 로그를 출력한다.
Blackbox Script
는 특정 스크립트 파일을 블랙박스로 처리해서 디버깅 프로세스에 걸리지 않도록 할 수 있다. 외부 라이브러리나 서드파티 코드에 사용하면 효과적이다. 크롬 개발자 도구에서는 Never pause here
라는 이름으로 제공하고 있는 기능이다.
XHR/fetch Breakpoints
는 XHR 요청의 URL 패턴을 입력하거나 fetch 요청의 함수 호출을 선택하여 중단점을 부여하는 기능이다. 요청과 응답에 대한 정보를 분석할 수 있다.
이 외에도 Filesystem
, Snippets
, Overrides
과 같은 기능들도 제공한다. 개인적으로 많이 사용해본 기능은 아니라, 간단하게 소개만 하겠다.
Filesystem는 로컬 파일 시스템에 접근하여 파일을 생성, 편집, 삭제할 수 있는 기능이다. 쉽게 말하면 IDE처럼 사용할 수 있는 것이다. 디버깅중에 코드를 수정하는 경우에 유용할 것 같다.
Snippets은 자주 사용하는 코드 조각을 저장하고, 실행할 수 있는 기능이다. 자신이 작성한 스크립트나 라이브러리 코드를 저장하여 필요할 때, 불러와 실행할 수 있다.
Overrides는 개발자 도구에서 변경한 코드들을 새로고침 이후에도 유지할 수 있는 기능이다. 오버라이드가 적용된 리소스와 실제 리소스를 혼동하지 않도록 주의만 하자.
웹 서비스에 처음 방문하는 유저에게 정보 수집 동의를 위한 모달을 띄우는 경우, 오늘 하루 보지 않기
와 같은 버튼이 있다. 프론트엔드 개발자는 이 기능을 어떻게 구현해야 할까? 여러가지 방법이 있겠지만, 간단한 방법으로 로컬 스토리지를 이용할 수 있다.
다크 모드
도 마찬가지다. 다크모드를 사용하는 유저에게는 다음 방문시에도 다크모드를, 라이트 모드를 사용하는 유저에게는 라이트 모드를 제공해야 한다. 이것도 로컬 스토리지에서 mode: 'dark'
와 같이 관리하는 체계가 있으면 되는 것이다. 주니어 개발자들이 Application Tab에서 가장 많이 사용하는 기능이 이와 같은 스토리지를 확인하는 것이라 예상해본다.
(스토리지 종류에 대해서는 논점을 벗어나므로 설명하지 않겠다.)
PWA를 고려한 Manifest나 웹 애플리케이션에서 사용하고 있는 Service Workers를 확인해 볼 수도 있다. (+ Background service)
웹 애플리케이션의 성능과 관련된 정보를 분석하고 최적화하기 위한 도구이다. 즉, 내가 만든 웹 서비스에서 지연되고 있는 부분을 찾을 수 있고, 해당 지점이 지연시키는 이유를 고민해보며 개선할 수 있는 여지를 준다.
개발자 도구 Performance Tab에서 Record 버튼을 클릭(Ctrl + E)하고, 3초 정도 후에 후에 꺼보자. 어린 시절에 개발자를 떠올렸을 때, 천재 개발자들이 볼 것 같은 화면이 나온다. 3초 사이에 발생한 activity에 따른 Bottom-Up
, Call Tree
, Event Log
를 볼 수 있다.
사실 나는 하단에 나오는 Summary
정도만 보고 넘어갔다. 애플리케이션의 성능에 대한 간략한 개요 정보를 제공하는 섹션으로, Loading
과 같은 작은 정보부터 실행 시간 전체를 나타내는 Total Time
과 같은 정보까지 한 눈에 파악하기 쉽게 제공한다.
Bottom-Up: 가장 시간이 많이 소비되는 함수부터 시작하여 하위 호출까지 내려가는 방식으로 분석한다. 이를 통해 성능 병목 현상을 식별하고, 어떤 함수에서 시간이 소비되는지 확인할 수 있다.
Event Log: 이벤트 기록을 시간에 따라 표시한다. 이벤트는 사용자 입력, 네트워크 요청, 애플리케이션의 내부 동작 등을 포함할 수 있다.
Call Tree: 함수 호출의 계층 구조를 트리 형태로 보여준다. 이것은 어떤 함수에서 다른 함수를 호출하고, 그 호출의 시간과 호출 횟수를 알려준다.
내가 만든 웹 애플리케이션을 평가해주기도 한다. 다양한 측면에서 점수를 매겨 버린다. 자신이 만든 프로젝트들이 있다면, 한 번 이 기능을 사용해보면 재밌을 것이다. 혹시 "뭐야.. 글쓴이가 만든 프로젝트의 퍼포먼스 58점밖에 안돼?"
라고 생각했는가? 왼쪽 위의 URL을 보자. Google이다.
메모리 탭은 어디서 누수가 발생하고 있는지 파악할 수 있는 Heap snapshot
, Allocation instrumentation on timeline
, Allocation sampling
와 같은 기능들일 제공하고 있다.
Heap snapshot은 현재 시점에서 JavaScript 객체의 힙 스냅샷을 찍는다. 힙 스냅샷은 현재 메모리 상태의 스냅샷을 캡처하여 객체의 개수/크기 및 참조 관계 등을 분석할 수 있어, 수거되지 않거나 불필요한 객체/배열의 할당을 파악하기 좋다.
Allocation Instrumentation on Timeline은 JavaScript 코드에서 메모리 할당이 발생하는 시점을 타임라인에 기록하는 기능이다. 이를 통해 객체가 생성되는 시점, 메모리 할당량, 할당된 위치 등을 시각적으로 확인할 수 있다.
Allocation Sampling은 주기적으로 실행되는 샘플링 작업을 통해 객체의 생성 및 할당량, 할당된 스택 정보 등을 수집한다. 어떤 함수가 메모리를 많이 할당하는지, 어떤 시점에서 많은 객체가 생성되는지 등을 알 수 있다.
실제로 위의 기능들을 이용해보면, 위와 같은 화면이 나타난다. 각각의 섹션들이 어떤 의미를 가지고 있는지 알아보자.
Distance는 말 그대로 거리다. 해당 객체와 JavaScript의 루트 객체(일반적으로는 window 객체) 간의 거리다. 즉, 얼마나 많은 중간 객체를 거쳐서 해당 객체에 접근할 수 있는지를 나타내는 것이다.
Shallow Size는 객체 자체가 차지하는 메모리 공간의 크기이고, Retained Size는 특정 객체가 참조하고 있는 다른 객체들과의 연결을 고려한 전체적인 메모리 사용량이다.
오늘은 주니어 개발자들이 기술 스택과 개발 방법론 등에 대한 성장을 할 수 있는 글이 아닌, 개발을 잘 하기 위한 많은 방법 중 한 가지를 소개했다. 사실 입사했을 당시에 프론트 팀장님께서 개발자 도구에 대해 학습해보라고 공유해주셨다. 그 때, 한 번 훑어보고 다음에 정리하겠다는 생각을 했었는데, 이번 기회에 하게 된 것이다.
위에서 소개한 방법들 외에 자신이 근무하고 있는 회사의 프로젝트 또는 도메인에 따라 많이 사용하게 되는 개발자 도구의 기능도 있을 것이다. 또는 위에서 설명하지 않았지만, 기깔나게 사용할 수 있는 꿀팁 기능들도 있을 것이다. 만약에 이런 경험들이 있는 개발자 분들이 댓글로 공유해주신다면 좋을 것 같다.
우상단 톱니바퀴 옆, 점 3개 아이콘을 눌러 "도구 더보기"를 누르면 css 개요, 애니메이션, 레이어, 실시간 성능 모니터링 등을 추가적으로 볼 수 있습니다.
처음에 보이는 요소, 콘솔, 소스, 네트워크는 충분히 유용한 도구들이지만 이러한 부가적인 도구들 또한 가끔씩 사용하면 엣지 케이스들을 찾아낼지도 모릅니다.
특히 css개요는 모든 색상, 대비 문제 표시, 글꼴, 미디어 쿼리, 미사용 선언을 보여줌으로써 스타일에 집중적인 프로파일링을 체크할 수 있습니다.
완전 유용하네요👍🏻
잘 읽고갑니당~