본문 바로가기
JavaScript & TypeScript/JavaScript

console.log를 사용하는 당신의 디버깅은 안녕하신가요?

by 감중에홍시 2024. 3. 22.

자바스크립트를 사용하며 개발을 하다 보면, 코드의 실행 상태를 확인하기 위해 console.log()를 사용하여 로깅을 자주 사용합니다. 특히 변수의 값이나 객체의 상태를 확인할 때, 대부분의 개발자(저 포함)는 console.log()를 자주 사용합니다. 

그러나 console.log()를 사용하여 객체의 상태를 확인하는 것은 몇 가지 이유로 바람직하지 않을 수 있습니다. 이 글에서는 그 이유와 함께, 제 경험을 예시로 들어, 객체의 상태를 보다 정확하게 확인하기 위한 대안으로 console.table()의 사용을 제안하고자 합니다.

 

console.log의 한계

console.log() 메서드는 자바스크립트 개발에서 가장 흔히 사용되는 디버깅 도구 중 하나 입니다. 변수의 값이나 코드의 실행 결과를 신속하게 확인할 수 있기 때문에 매우 유용합니다. 그러나 객체 같은 참조 타입의 데이터를 찍어볼 때는 주의가 필요합니다.

console.log()로 객체 같은 참조 타입을 다룰 때, console.log()는 객체의 실시간 참조를 출력합니다.

즉, console.log()로 확인하고 싶은 딱 그 시점에서의 객체의 "스냅샷"을 출력하는 것이 아니라 객체 자체에 대한 참조를 출력합니다.

이로 인해, 나중에 객체의 상태가 변경되면, 이미 콘솔에 출력된 결과 또한 변경된 상태를 반영하게 됩니다. 이는 디버깅 과정에서 혼란을 줄 수 있으며 코드의 실행 당시 정확한 객체의 상태를 파악하기 어렵게 만듭니다.

 

디버깅 어려움을 초래하는 예시

const user = {
  id: 1,
  name: "John Doe",
};

console.log("시점 A", user); // 시점 A

setTimeout(() => {
  user.email = "john.doe@example.com";
  console.log("시점 B", user); // 시점 B
}, 100);

아래는 크롬 브라우저에서의 결과입니다.

user 객체에 대한 console.log() 출력값. user 객체를 펼쳐서 확인하지 않은 경우(좌), user 객체를 펼쳐서 확인한 경우(우)

위 코드에서는 user 객체를 먼저 콘솔에 찍어본 다음(시점 A), 1초 후에 user 객체에 email 속성을 추가하고 다시 콘솔에 찍어봅니다(시점 B). 왼쪽 이미지처럼 얼핏 봤을 때는 user 객체가 콘솔을 출력하는 그 시점의 객체 그 자체를 잘 출력해주는 것처럼 보입니다. 하지만 오른쪽 이미지처럼 user 객체에 대한 상세한 결과를 펼쳐봤을 경우, '시점 A'에서의 user 객체에서 email 속성이 추가된 상태로 로그가 찍힌 것을 볼 수 있습니다. 이는 개발자가 '시점 A'에서의 정확한 객체 상태를 파악하는 것을 어렵게 만듭니다.

 

console.table()  예시

앞선 예시를 console.table()을 사용하여 다시 작성해보겠습니다.

객체의 속성이 추가되었음에도 console을 출력하는 그 시점의 객체의 형태를 출력해준다

분명 아까와 같은 코드에 console.log()를 console.table()로 바꿔줬을 뿐인데 결과값이 다르게 나옴을 볼 수 있습니다.

console.table()을 사용하면 `시점 A`와 `시점 B`에서의 `user` 객체 상태를 테이블 형태로 정확하게 확인할 수 있습니다.

console.table()을 사용함으로써, 개발자는 객체의 상태 변화를 명확하게 파악할 수 있으며, 이는 특히 비동기적인 동작이 많은 자바스크립트 개발에서 디버깅을 훨씬 용이하게 만들어 줄 수 있습니다.

 

실제 내가 겪은 경험

회사 업무를 하면서 객체의 속성을 조건에 따라 특정 조건문을 탈 지, 말 지에 대한 코드를 작성하고 있었습니다. 해당 내용의 workflow는 다음과 같습니다.

// this.m_agGridOptions.api.getRenderedNodes(): 각 요소가 객체인 배열
// Ex: this.m_agGridOptions.api.getRenderedNodes() = [객체A, 객체B, 객체C];
// 초기상태: 객체A = { selected: false }, 객체B = { selected: true }, 객체C = { selected: false }

// 1
console.log(this.m_agGridOptions.api.getRenderedNodes());
console.table(this.m_agGridOptions.api.getRenderedNodes());

// 배열의 0번 index 객체의 "selected"라는 속성이 true라면
// 즉, 초기 상태에서는 해당 조건문을 타지 말아야 합니다!
if (this.m_agGridOptions.api.getRenderedNodes()[0]?.isSelected()) {
	/**
    * 내용과 무관한 로직 수행
    */
}

// 2
// A의 selected = true, B의 selected = false로 C의 selected = false로 변환하는 코드
// 즉, 객체의 속성을 변화시켜주는 코드
const rowLength = this.m_agGridOptions.api.getRenderedNodes().length;
if (rowLength < this.m_nCursorIndex + 1) {
    this.m_agGridOptions.api.forEachNode((row, idx) => {
        if (rowLength === idx + 1) row.setSelected(true);
    });
}

이러한 상황에서 저는 처음에 해당 if 조건문을 잘 타는지, 잘 탄다면 그 시점에서 배열의 각 요소들의 상태는 어떠한 지 확인하고 싶어서 console.log()를 통해 객체의 상태를 출력했습니다. 그리고 그 결과는 다음과 같습니다.

0번 index 객체의 selected 속성이 true인 모습

 

1번 index의 selected 속성이 false인 모습

분명히 초기 상태에서 0번 index의 selected 속성은 false, 1번 index의 selected 속성은 true인데, console.log()로 객체의 상태를 출력해 본 결과, 제 예상과 다르게 0번 index의 selected 속성이 true, 1번 index의 selected 속성이 false인 모습입니다.

이는 앞서 설명드린 console.log()는 그 시점에서의 객체의 "스냅샷"을 출력하는 것이 아니라 객체 자체에 대한 참조를 출력하기 때문에 일어난 현상입니다. 코드를 보면 1에서 객체의 상태에 대한 console.log()를 출력하고, 2에서 객체의 상태를 바꾸는 로직을 수행합니다.

1 시점에 console.log()를 작성한다고 해도 2 시점에 객체의 참조가 바뀌었기 때문에 1 시점의 console.log()는 바뀐 참조를 출력하게 됩니다. 이것 때문에 디버깅에 꽤나 고생을 했었는데요. 이제 같은 코드를 console.table()로 메서드만 바꿔서 출력해보겠습니다.

의도한대로&nbsp; 0번 index의 selected 속성이 false, 1번 index의 selected 속성이 true인 모습

보시는 바와 같이 2 시점에서 객체의 속성을 바꾸기 전 상태 그대로의 모습을 잘 출력해주는 것을 확인할 수 있습니다.

 

console.table()로 해결하기

위와 같은 문제 해결을 위해 저는 'console.table()' 메서드의 사용을 권장 및 추천합니다.

console.table()은 객체나 배열의 데이터를 테이블 형태로 콘솔에 출력해줍니다. 이 메서드는 데이터를 시각적으로 구조화하여 보여주며, 복잡한 객체의 내부 상태를 한눈에 파악하기 쉽습니다. 또한 console.table()은 해당 시점에서의 데이터 스냅샷을 제공하기 때문에, 데이터가 나중에 변경되더라도 출력된 테이블의 내용은 변하지 않습니다.

 

 

결론

`console.log()`는 자바스크립트 디버깅에 있어서 강력한 도구이지만, 참조 타입 데이터의 실시간 상태를 파악하는 데에는 한계가 있습니다. 

이러한 한계로 인해 발생할 수 있는 혼란을 방지하기 위해, 객체의 상태를 확인하고자 할 때는 `console.table()`의 사용을 적극적으로 고려해보는 것을 추천합니다

결론적으로 console.log()를 사용하는 것 대비 console.table()을 사용하는 것의 장점은 다음과 같습니다.

1. console.table() 메서드는 데이터를 표의 형태로 나타내주기 때문에 복잡한 객체의 상태를 한눈에 파악할 수 있게 해줍니다.

2. console.table() 메서드는 시점에 따른 데이터의 스냅샷을 제공하기 때문에, 코드의 실행 상태를 보다 정확하고 명확하게 이해할 수 있도록 도와줍니다.

3. 개발자 경험(DX)의 관점에서, 본인이 짠 코드가 잘 동작하고 있음을 효율적으로 확인할 수 있기 때문에 개발자 본인의 심신에 안정을 줍니다.

따라서, 효과적인 자바스크립트 디버깅을 위해 `console.log()`와 `console.table()`을 상황에 맞게 적절히 활용하는 것이 중요하다고 생각합니다.

 

 

[출처]

console.table() MDN 문서