인터넷에서 자바스크립트 관련 정보를 찾다가 다음과 유사한 코드를 봤다.
const something = document.createElement('something');
console.log(something);
something.innerHTML = 'hello';
console.log(something);
something.innerHTML += ' my world!';
console.log(something);
코드의 요점은 웹 브라우저에서 엘리먼트를 생성한 후 innerHTML을 수정, 엘리먼트를 출력하는 것이다. 코드 자체는 단순하고 결과도 쉽게 예상할 수 있을 만했는데, 의외로 예상하지 못한 결과가 나타났다.
아마 이 글을 보는 대부분의 사람들은 다음과 같은 결과를 예상했을 것이다.
""
"hello"
"hello my world!"
그런데, 글을 작성한 당일 기준으로 chrome 및 firefox 브라우저는 다음과 같은 결과를 출력한다.
기대와는 달리 3개의 console.log에서 동일한 결과를 출력하고 있다. 이렇듯 오류같은 현상이 발생하는 이유가 뭘까.
다른 언어들처럼 자바스크립트의 객체 역시 인자로 전달할 때 전체를 복사해서 넘겨주는 대신 해당 객체에 대한 주소(레퍼런스)를 제공한다. 인자로 전달된 객체에 접근하는 경우, 주어진 주소를 기준으로 원본 객체가 존재하는 위치로 찾아가는 방식이기 때문에 항상 최신 상태의 객체를 사용하게 된다.
따라서 console.log를 이용하여 객체를 출력하는 경우 로깅이 시작된 시점의 객체가 아니라 로깅이 "완료된" 시점의 최신 상태의 객체를 출력할 가능성이 존재한다. 대부분의 경우 이런 동작은 프로그래머의 의도와는 거리가 멀다.
stackoverflow의 과거 글을 보면, 이와 관련된 사례들이 꽤 많이 보인다.
- https://stackoverflow.com/questions/4057440/is-chrome-s-javascript-console-lazy-about-evaluating-objects
- https://stackoverflow.com/questions/13444596/why-is-google-chromes-console-delayed
- https://stackoverflow.com/questions/7389069/how-can-i-make-console-log-show-the-current-state-of-an-object
- https://stackoverflow.com/questions/23392111/console-log-async-or-sync
이런 현상이 왜 발생하는지는 사실 알기 어렵다. 브라우저 자체적인 버그일 수도 있고, 프로세스 간 통신(IPC) 등에 의해 발생하는 딜레이 때문일 수도 있으며, 코드 최적화 과정에서 console을 고려하지 못한 것일 수도 있다. 개인적으로 마지막일 가능성이 높다고 생각하는 것이, 글 초반부에서 제시한 코드를 "한 문장씩" 콘솔 창에 입력하는 경우에는 이러한 현상이 발생하지 않기 때문이다.
코드에 발생한 현상이 의도된 것인지 ( DOM에 의한 특별 조작? ) 버그인지는 알 수 없지만, 분명한 것은 console.log에 원 객체가 아닌 스냅샷 또는 문자열 등 원시 타입으로 변환하여 제공함으로써 console.log가 제대로 수행되지 않는 현상을 막을 수 있다는 것이다. 예를 들어 현재 글에서 예시로 들고 있는 코드의 경우, 다음과 같은 형태로 코드를 수정하여 각 시점에서 someting에 대한 스냅샷을 제공함으로써 결과를 의도대로 출력할 수 있다.
const something = document.createElement('something');
console.log(something.cloneNode(true));
something.innerHTML = 'hello';
console.log(something.cloneNode(true));
something.innerHTML += ' my world!';
console.log(something.cloneNode(true));
Node.cloneNode 메서드는 현재 노드와 동일한 노드를 복사하여 생성하는데, true을 인자로 전달하면 해당 노드의 자식들에 대해서도 깊은 복사를 수행한다. 복사한 노드는 원본 노드와 다른 객체로 취급되므로 스냅샷을 남기는데 도움이 된다.
DOM Element가 아닌 일반적인 객체에 대해서는 structuredClone 의 사용을 고려할 수 있다.
structuredClone은 깊은 복사에 사용되는 메서드로, 일부 타입에 대해서는 move(소유권을 이동, 이전 소유권을 가진 객체를 무효화) 동작이 가능하다고 한다. 이 방법은 기존 JSON.parse 및 JSON.stringify을 사용하는 방법을 대체할 수 있다.
'WEB&서버' 카테고리의 다른 글
[부가정보] https 로컬에서 사용하기 (0) | 2023.07.28 |
---|---|
[WEB] MutationObserver API (0) | 2023.03.08 |
브라우저 동작 관련 읽어볼만한 내용들 (0) | 2023.02.07 |
[WEB] 웹 상의 전역변수 length (0) | 2023.01.30 |
[WEB] 데이터 속성 (0) | 2023.01.25 |