typescript

왜 타입스크립트는 DOM 요소를 확신하지 못할까?

냠냠맨 2023. 6. 2. 02:24

😂왜 DOM요소는 as로 타입단언해야하나요?

사실 별 생각없이 당연히 그런거아님? 하고 넘어갔던 문제인데

디스코드에 질문이 올라온걸 보고 다른사람도 납득할 수 있게 근거를 들어 설명하려니

꽤나 어렵다는 생각이 들었습니다.

따라서 제 나름대로 접근하여 결론을 내봤습니다.

물론 100% 뇌피셜은 아니고 나름대로 레퍼런스를 찾아보면서 근거를 확인했어요

그래도 제 생각이 틀릴 수 있으니 만약 그런 경우 댓글 남겨주시면 감사하겠습니다.


😉나의 답변

익명보호를 위해 질문자 분 성함은 그림판으로 가렸습니다.

우선 이 답변이 제 결론이며 이 포스트에서는 제가 결론에 도달하기까지의 과정을 서술하겠습니다.


🤗타입스크립트로 작성한 파일은 어떻게 컴파일될까?

1. 타입스크립트 컴파일러에 의한 타입스크립트 -> 자바스크립트 변환

2. 바벨 등 트랜스파일러에 의한 자바스크립트 -> 자바스크립트 변환

 

이 과정에서 바벨 등 트랜스파일러를 통한 변환은 선택적으로 이루어집니다. 


😐왜 타입스크립트는 DOM 요소를 확신할 수 없을까?

이는 DOM 요소가 브라우저에 존재하기 때문입니다.


🙄타입스크립트는 런타임에 영향을 주지않는다?

타입스크립트는 일반적으로 런타임에 영향을 주지 않습니다.

일반적이라 표현하는 이유는 예컨대 enum을 사용하는 경우 번들러에 따라 런타임에 영향을 주는 경우도 있기 때문입니다.

https://yceffort.kr/2022/03/typescript-use-union-types-instead-enum

 

내가 타입스크립트에서 Enum을 잘 쓰지 않는 이유

Table of Contents Introduction 이전 글에서도 언급했던 것 처럼, enum은 트리쉐이킹이 되지 않기 때문에 (정확히는 번들러가 무엇을 트리쉐이킹 해야할지 알 수 없으므로) 잘 사용하지 않는 다고 언급했

yceffort.kr

또 타입스크립트의 기능 중 하나인 추상클래스를 사용하는 경우에도

컴파일 이후에 추상클래스와 관련한 코드가 남아있을 수 있습니다.

 

하지만 이러한 특수한 경우를 제외하면 타입스크립트는 런타임오버헤드를 최소화하는 것을 지향하며

런타임에 타입 체크를 수행하지 않습니다.

 

즉 타입스크립트가 컴파일을 수행하는 시점에서

타입스크립트는 DOM 요소가 있을지 없을지 정확히 추론할 수 없습니다.


😐그러면 그동안 타입단언이 필요했던 이유도..?

import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import './index.css';
import { Provider } from 'react-redux';
import store from './modules/index.ts';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <Provider store={store}>
    <App />
  </Provider>,
);

이 코드는 vite이 제공하는 탬플릿의 엔트리포인트입니다.

읽어보면 'root'라는 아이디를 가진 element를 가져오는 코드인데

document.getElementById() 는 셀렉되는 요소가 있으면 그 HTMLElment를 반환하고

그렇지 않으면 null을 반환합니다.

즉 document.getElementById()함수의 반환값은 null|HTMLElement라고 볼 수 있습니다.

 

그런데 개발자 입장에서는  당연히 Id가 root인 요소를 만들어두었을테니

null일 가능성이 없다는 것을 알고 있습니다.

하지만 타입스크립트 컴파일러는 그 사실을 알 수 없는 것 입니다.

 

따라서 개발자는 as 문법을 통해 타입단언을 하여

확실히 HTMLElement라는 것을 알려줍니다.


🤩그런데 만약 타입스크립트가 아니었으면 문제가 안될까?

이게 헷갈린 이유는 useEffect의 동작 시점이 언제인지 헷갈렸기 때문입니다.

 

다행히 useEffect의 동작 시점은 같은 혼란을 느낀 많은 사람들이

보기좋게 정리를 해둔 것들을 찾아 볼 수 있었습니다.

저는 아래 링크를 참고했습니다.

https://charles098.tistory.com/176

 

[ React ] useEffect와 컴포넌트 생명주기

리액트에서 Hook이 등장한 이후로 함수 컴포넌트 내부에서 state와 컴포넌트의 생명주기를 다룰 수 있게 되었다. state는 'useState'라는 훅을, 컴포넌트는 생명주기는 'useEffect'라는 훅을 사용하면 된

charles098.tistory.com

https://www.howdy-mj.me/react/useEffect-and-useLayoutEffect

 

useEffect와 useLayoutEffect의 차이

예전에 데이터에 따라 DOM 조작이 필요했었는데, 이때 화면의 깜빡임을 없애기 위해 useEffect 대신 useLayoutEffect를 사용했었다. 그러나 정확히 이 둘의 차이가 무엇인지 생각해보면, 대답할 수 없었

www.howdy-mj.me

useEffect는 위 게시물에서도 알 수 있듯이

paint까지 완료된 다음 비동기적으로 실행되게 됩니다.

 

즉 정상적으로 마운트가 되었다면 useRef는 HTMLElement와 연결될 것이고

그런 다음 useEffect가 실행되게 될테니 자바스크립트에서는 문제없이 동작할 것입니다.

이것이 자바스크립트에서는 문제없이 동작할 수 있는 이유라고 생각했습니다.

 

(이전에 쓴 글인데 잘못된 부분이 있어 추가합니다.)

useEffect의 동작시점은 "대체로" paint 이후이지만 항상 paint 이후 실행이 보장되지는 않습니다.

예컨대 useLayoutEffect가 리렌더링을 유발하는 경우 useEffect는 paint 이전에 실행될 수 있습니다.


😥결론

당연하다는 듯이 사용하고 있던 문법도

왜? 그런걸까?를 생각하기 시작하면 꽤나 설명하기 어렵다..

반응형