자스왕조실록 (innerHTML편)

Hugo Kim·2021년 4월 5일
3

자스왕조실록

목록 보기
1/1
post-thumbnail

처음이자 마지막이 될 수 있는 자스왕조실록 시리즈 📚

서론

이 여정의 시작은 테스트 코드 작성이었다.
@testing-libraryjest와 함께 컴포넌트 테스트 코드를 작성하던 와중에 아래와 같이 엘리먼트 내부 텍스트를 확인하는 테스트 코드를 작성하게 되었다.

const likeButton = screen.getByRole("button");
expect(likeButton.innerText).toBe("normal");

버튼 내부 텍스트가 normal인지 확인하는 단순한 로직이었으나, 테스트가 계속해서 원하는 값이 아니라며 화를 낸다. 😠

Expected: "normal"
Received: undefined

   7 |     const likeButton = screen.getByRole("button");
>  8 |     expect(likeButton.innerText).toBe("normal");
     |                                  ^

컴포넌트 내부에 innerText의 값이 undefined라는 사실은 이해할 수 없었다.
예제 코드를 따라서 작성했기 때문에 잘못된 엘리먼트를 가져온 건 아니라고 예상했다.
innerHTMLHTML 표준 스펙에도 존재하는 마당에,, 무엇이 문제일까?

처음엔 @testing-library의 문제인가 싶었지만, jest기본 테스팅 환경 jsdom의 문제였다.

이 문제의 원인을 파악하려면 innerText의 작동방식을 파악해야 한다.

InnerText 스펙

Returns the element's text content "as rendered".

위 문장은 HTML 표준 스펙에 명시된 innerText 속성의 설명이다. 다시 말해, innerText 속성은 렌더되지 않은 엘리먼트의 텍스트는 리턴되지 않는다는 말이다.

그렇다면 렌더 여부는 어떻게 결정되는 것일까? 이 또한 HTML 표준 스펙에 정의되어 있다.

An element is being rendered if it has any associated CSS layout boxes, SVG layout boxes, or some equivalent in other styling languages.

엘리먼트의 렌더 여부는 엘리먼트가 연관된 CSS 레이아웃 박스나 SVG 레이아웃 박스같은 것을 가지고 있어야 한다. 문제는 여기서 드러나게 된다. 이렇게 innerText을 구현하기 위해서는 레이아웃 엔진이 필요한데 jsdom은 레이아웃 엔진이 없어 구현할 수 없다.

TextContent와의 차이

innerText는 렌더된 텍스트만 처리할 수 있는 반면에 textContent는 렌더되지 않는 텍스트도 반환한다. 또한 innerText<br>태그를 인식할 수 있다.

<p id="source">
  <style>#source { color: red; }</style>
아래에서<br>이 글을<br>어떻게 인식하는지 살펴보세요.
  <span style="display:none">숨겨진 글</span>
</p>
<h3>textContent 결과:</h3>
<textarea id="textContentOutput" rows="6" cols="30" readonly>...</textarea>
<h3>innerText 결과:</h3>
<textarea id="innerTextOutput" rows="6" cols="30" readonly>...</textarea>

<script>
  const source = document.getElementById('source');
  const textContentOutput = document.getElementById('textContentOutput');
  const innerTextOutput = document.getElementById('innerTextOutput');

  textContentOutput.innerHTML = source.textContent;
  innerTextOutput.innerHTML = source.innerText;
</script>


위 예시를 참고하면 확실하게 두 속성간 차이를 이해할 수 있다.

후기

컴포넌트 단위 테스트 코드를 작성하다가 innerText 속성을 이렇게 찾아보게 될 줄은 몰랐지만, 어찌됐던 재밌는 경험이었다. 😋

참고 자료

profile
ts와 react를 사랑하는 프론트엔드 개발자입니다.

0개의 댓글