Introduction
나는 null이 아니라 any 형식이 포함된다고 Error가 떴다. React에서의 event를 제대로 이해하지 못한 탓인지 tsx를 화나게 했다. 나처럼 js에서의 Event는 익숙한데, React + TypeScript에서의 Event 타입을 어떻게 다루어야 할 지 이해하지 못하는 분들을 위해 글을 쓴다!
React, TypeScript에서의 Event Type
JavaScript에서 자주 쓴 `addEventListener( )`, `on____`
예시로 `onChange`
change event는 주로 input element의 값이 수정되었을 때 발생하도록쓰인다.
// Syntax
addEventListener("change", (event) => {});
onchange = (event) => {};
// Example
const result = document.querySelector(".result");
selectElement.addEventListener("change", (event) => {
result.textContent = `You like ${event.target.value}`;
});
TSX에서 쓰려하면..?
const App = () => {
const [minutes, setMinutes] = useState(0);
const [flipped, setFlipped] = useState(false);
const onChange = (event) => {
setMinutes(event.target.value);
};
// typescript 오류
// 'event' 매개 변수에는 암시적으로 'any' 형식이 포함됩니다.
return
... 생략
➡️ 이런식으로 당연히 event를 알겠거니 하고 event.target.value를 썼다간 오류를 뱉어낸다. 그렇다고 위험한 any를 쓸 순 없고,,, 어떻게 타입을 명시해주어야 할까?
해결
(event: React.ChangeEvent<HTMLInputElement>)
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setMinutes(Number(event.target.value));
};
React에서 Input element와 onChange 이벤트를 사용하려면 `React.ChangeEvent<HTMLInputElement>`로 타입을 설정해주어야 한다.
✔️ input 엘리먼트는 TypeScript에서 <HTMLInputElement>타입을 사용하고, textarea 엘리먼트의 경우 <HTMLTextAreaElement>타입을 사용한다.
React에서의 엘리먼트와 이벤트 타입에 대해서 공부하다보니 이런 인터페이스들이 전부 리액트 자체적으로 제공하는 안정성 있는 이벤트 핸들링 시스템인 React Event System의 일부였기에 트리 구조를 따라 구조를 알아보고 싶었다.
👀 onChange에 커서를 올리면 IDE가 타입을 알고 말해준다.
Event의 타입! 이벤트 Interface 살펴보기
1. ChangeEvent Interface
// ChangeEvent는 SyntheticEvent를 상속받는다.
interface ChangeEvent<T = Element> extends SyntheticEvent<T> {
target: EventTarget & T;
}
✔️ ChangeEvent 인터페이스는 React에서 사용되는 이벤트 핸들러에 전달되는 합성 이벤트 객체의 구조를 정의한 인터페이스 중 하나로, 주로 DOM에서 발생한 변경 이벤트의 구조를 나타낸다.
✔️ SyntheticEvent 인터페이스를 상속하여 공통적인 속성과 메서드를 제공한다.
✔️ 주요 특징? 변경 이벤트를 발생시킨 요소에 대한 참조를 가지고 있다는 것.
➡️ target이라는 프로퍼티로 표현, 변경 이벤트를 Trigger한 DOM요소를 가리킨다. 이를 통해 입력값의 변경을 쉽게 감지하고 처리할 수 있다.
(1) <T = Element > ? 인터페이스 Element? 끝은 어디일까?
interface Element extends Node, ARIAMixin, Animatable, ChildNode, InnerHTML, NonDocumentTypeChildNode, ParentNode, Slottable {
...
}
// Element가 정의된 부분을 보자.
interface Element {}
interface DocumentFragment {}
interface HTMLElement extends Element {}
interface HTMLAnchorElement extends HTMLElement {}
interface HTMLAreaElement extends HTMLElement {}
interface HTMLAudioElement extends HTMLElement {}
interface HTMLBaseElement extends HTMLElement {}
interface HTMLBodyElement extends HTMLElement {}
interface HTMLBRElement extends HTMLElement {}
interface HTMLButtonElement extends HTMLElement {}
...
// 상속 과정
EventTarget
|
+-- Node
|
+-- Element
|
+-- HTMLElement
Element는 문서의 모든 Element 인터페이스가 참조하는 가장 일반적인 인터페이스이다. 모든 종류의 엘리먼트 인터페이스에 공통적인 메서드와 프로퍼티만 가지고 있다. <T = Element>부터 각각의 상속 인터페이스로 거슬러 올라갔다. (HTMLElement, HTMLInputElement 등은 Element 인터페이스를 확장하는 구체적인 인터페이스)
(2) target : EventTaget?
그림에 있는 EventTarget 인터페이스는 웹 API의 일부로, 이벤트를 수신할 수 있는 모든 객체의 기본 인터페이스이다. EventTarget은 메서드(addEventListener)와 속성을 정의한다. Element(HTMLElement 등), Document(Node) 및 Window와 같은 DOM 객체들은 EventTarget 인터페이스를 구현한다. 즉, 이들의 객체가 Event Listener를 추가하거나 제거하는 EventTarget의 메서드를 사용할 수 있다는 것..!
TypeScript의 Element 인터페이스는 타입 체킹과 객체의 구조를 정의하고, JavaScript의 EventTarget 인터페이스는 이벤트 리스닝 기능을 제공하는 것이다.
2. SyntheticEvent Interface
// SyntheticEvent는 BaseSyntheticEvent를 상속받는다.
interface SyntheticEvent<T = Element, E = Event> extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
SyntheticEvent 인터페이스는 리액트의 이벤트 처리 시스템에서 중요한 역할을 하는 인터페이스이다. 이 인터페이스는 리액트에서 제공하는 모든 이벤트 핸들러에 전달되는 *합성 이벤트 객체의 타입을 정의한다.
(위에서 ChangeEvent가 상속받았던 인터페이스)
*합성 이벤트의 목적?
React에서는 모든 플랫폼에서 동일한 이벤트 처리, 일관성을 제공하기 위해 설계되었다. 브라우저마다 다른 방식으로 작동하는 웹의 원시 이벤트native Event를 Wrapping한다. 추상화하여 일관된 이벤트 처리를 할 수 있는 인터페이스를 제공하는 것이다.
SyntheticEvent와 Native Event의 차이점?
위에서 말했듯 SyntheticEvent는 브라우저의 원시 이벤트를 감싸는 Wrapper이다. native Event의 표준화 및 추상화를 목적으로 한다. 리액트의 이벤트 처리 시스템에 특화된 인터페이스이며 리액트의 컴포넌트 트리 내에서 이벤트를 효율적으로 처리한다.
(이벤트 핸들링 동작에 관해선 따로 글을 써볼까한다.)
SyntheticEvent
|
+-- ChangeEvent
|
+-- FocusEvent
|
+-- KeyboardEvent
|
+-- MouseEvent
|
+-- TouchEvent
|
+-- WheelEvent
3. BaseSyntheticEvent Interface
React Event System의 핵심 인터페이스리액트에서 발생하는 모든 이벤트의 기본적인 구조와 기능을 정의한다.
// BaseSyntheticEvent
// Event System
// ----------------------------------------------------------------------
// TODO: change any to unknown when moving to TS v3
interface BaseSyntheticEvent<E = object, C = any, T = any> {
// 원래의 브라우저 이벤트. React의 합성 이벤트는 브라우저의 원시 이벤트를 감싸고 있으며, nativeEvent를 통해 원래 이벤트에 접근할 수 있다.
nativeEvent: E;
// 이벤트가 발생한 DOM 요소(currentTarget, target)
// 이벤트 핸들러가 부착된 요소
currentTarget: C;
// 이벤트가 실제로 발생한 요소
target: T;
// 이벤트의 생명주기를 관리하는 속성 5개
// 이벤트가 어떻게 전파되고 처리되는지를 나타냄
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
// 이벤트 흐름 제어 메서드 4개
// 이벤트의 기본 동작을 방지하거나 이벤트 전파를 제어하는 데 사용
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
// React17에서 이벤트 객체가 풀링(pooling)되어 재사용됐다. persist() 메서드를 호출하면 이벤트 객체를 풀에서 제거하여 지속적으로 사용할 수 있다.
persist(): void;
// 이벤트가 발생한 시간과 이벤트의 타입을 나타냄
timeStamp: number;
type: string;
}
기본 속성과 메서드에 대한 설명을 주석으로 달았다.
SyntheticEvent와의 관계
모든 SyntheticEvent 객체는 BaseSyntheticEvent를 상속받는다. SyntheticEvent가 BaseSyntheticEvent의 모든 속성과 메서드를 가지고 있다는 것을 의미한다. SyntheticEvent는 이 기본 인터페이스를 확장하여 특정 유형의 이벤트(예: 클릭, 입력 변경 등)에 대한 추가적인 속성과 메서드를 제공한다.
🤔 💭
어제부터 React 기초로 영화 웹사이트 만들기 중이다. 과거의 좋은 강의는 최신 버전으로 스스로 마이그레이션 한다. 스펙을 맞추라고 생각할 수 있으나,,,해당 강의는 jsx로 진행하고 React17버전을 따른다. TypeScript를 잘 쓰고 싶고 바꾸기 전에 React17의 레거시 코드도 확인하고 바꾸는 과정에서 둘 다 익혀지니까 일석이조 아닌가 싶다. 그리고 마이그레이션 꽤 재밌다.
다만 나는 tsx와 React를 제대로 실무에 적용해보지 않은 푸른 떡잎이라는 것~..
이전에 React Router라던가, 몇 개를 포스팅하고 싶었는데 망설여졌다. React에 대한 전문가의 좋은 강의와 책, 콘텐츠는 이미 레드오션이라 생각한다. ChatGPT, Bard 같은 AI에게도 충분히 도움을 받을 수 있기에 내가 배운 기초를 써서 무엇하나 했다.
블로그란 나 스스로 반추하기 위함도 있지만 자고로 다른 사람들에게도 좋은 영향을 줄 수 있어야된다고 생각해서 스스로 떳떳하지 못하면 쓸 수가 없더라. 그럼에도 불구하고 오늘은 쓰려는 이유는 무엇인가?
10년 후까지는 모르겠다만, 근 몇 년간 내가 사용해야될 기술 스택이기 때문이고, 잘하고 싶다는 욕심때문이다. 그리고 단순히 코드 비교하고 "이렇게 쓰면 됩니다" 라는 식의 포스팅을 지양하면 되지 않을까? 떡잎이기 때문에 앞으로도 내가 배우는 건 흔히, 자주 쓰이는 개념일 것이고 깊게 다루고 싶을 것이니.. 일반적인 docs와 다른 글을 쓸 수 있어야겠다.
React Event Type System...인터페이스를 상속하며 타입이 좁혀지는 걸 확인할 수 있었다. lib.d.ts, index.d.ts 파일을 보며 어떻게 설정했는지도 뜯어볼 수 있었다. 브라우저 native event와 흡사하지만 같은 것은 아니라는 것도 꽤나 흥미로웠던 것 같다. 이벤트에 관해서는 특히 잘 적용할 수 있으리라....! tsx가 더이상 화를 내지 않길 바라며 앞으로도 잘 살펴볼 예정이다.
[Type the onChange event of an element in React (TypeScript)]
[리액트 + 타입스크립트 (React + TypeScript): HTML 요소, 폼의 이벤트 처리]
[React 이벤트 처리방식과 SyntheticEvent]
ChatGPT, Bard 🌞
'JavaScript > React' 카테고리의 다른 글
State: A Component’s Memory | 공식 문서, 책으로 useState 알아보기 (2) | 2024.03.10 |
---|---|
React Fiber... 어렵지만 친해지고 싶은 고마운 친구 (14) | 2023.12.18 |