Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

하루에 하나씩

[02] 이벤트 속성 이해하기 본문

React

[02] 이벤트 속성 이해하기

BGK97 2024. 4. 24. 17:31

모든 HTML요소는 onmouseenter나 onmouseover처럼 'on'으로 시작하는 속성을 제공

  • 이를 이벤트 속성이라고 한다.

이벤트 속성을 만들 파일 작성하기

1. src 디렉터리 안에, copy 디렉터리를 만들고 CopyMe.tsx파일을 만들기

 

 

2. 복사용 기본 코드 작성

export default function CopyMe(){
    return <div>CopyMe</div>
}

 

3.  src 디렉터리에 page 디렉터리 생성 후 파일을 복사, 각 이벤트를 넣기

 

4. src/App 파일에 해당 코드 작성하기 (방금 작성한 11개의 컴포넌트 사용)

import DispatchEvent from './pages/DispatchEvent';
import DragDrop from './pages/DragDrop';
import EventBubbling from './pages/EventBubbling';
import EventListener from './pages/EventListener';
import FileDrop from './pages/FileDrop';
import FileInput from './pages/FileInput';
import OnChange from './pages/OnChange';
import OnClick from './pages/OnClick'
import ReactOnClick from './pages/ReactOnClick';
import StopPropagation from './pages/StopPropagation';
import VariousInput from './pages/VariousInput';

export default function App(){
  return(
    <div>
      <FileDrop />
      <DragDrop />
      <FileInput />
      <OnChange />
      <VariousInput />
      <StopPropagation />
      <EventBubbling />
      <DispatchEvent />
      <ReactOnClick />
      <OnClick />
      <EventListener />
    </div>
  )
}

 

5. 실행...

 

이벤트

  • 화면 UI를 다루는 모든 프레임워크는 사용자가 화면 UI에서 버튼을 누르거나 입력 등의 행위가 발생하면
  • UI를 다루는 코드에 알려줘야하는데, 이를 이벤트가 발생했다고 함

이벤트 타입

종류 설명
type 이벤트 이름으로 대소문자 구분 X
isTrusted 이벤트가 웹 브라우저에서 발생한것인지(true)
프로그래밍에서 발생한 것인지 (false) 구분
target 이벤트가 처음 발생한 HTML 요소
currentTarget 이벤트의 현재 대상
이벤트 버블링 중에서 이벤트가 현재 위치한 객체
bubbles 이벤트가 DOM을 타고 버블링될지 여부 결정

 

예시 : click인 이름을 가진 Event 객체를 생성하는 코드

new Event('click', { bubbles : true })

 

EventTarget 타입

  • 모든 HTML요소는 HTMLElement 상속타입을 가짐
  • 최상위 EventTarget을 시작으로, Node, Element 순으로 상속
  • 브라우저 객체 모델인 window도 EventTarget에서 상속하는 타입임

이벤트 처리기

EventTarget가 제공하는 3가지 메서드
1. addEventListner
2. removeEventListener
3. dispatchEvent
- 셋 모두 이벤트 처리기의 일종으로, 이벤트를 귀 기울여 듣는 다는 의미(event Listener)
- 하나의 이벤트에 여러 이벤트 처리기 부착 가능

 

1. addEventListner

DOM_객체.addEventListener(이벤트 이름 : string, 콜백함수: (e: Event) => void)
  • window 객체의 경우 EventTarget 타입을 상속하므로 addEventListener 메서드를 제공함
  • 리액트 프로젝트는 항상 public 디렉터리의 index.html파일에 <div id="root">태그가 포함 되어 있으므로, 다음과 같이 코드 작성이 가능함
document.getElementById('root')?.addEventListener('click', (e: Evet) => {
	const {isTrusted, target, bubbles } = e
    console.log('mouse click occurs', isTrusted, target, bubbles)
})
  • 옵셔널 체이닝이 사용되었음 (?.)
  • 이는 getElementById가 null을 반환할 수도 있기 때문에, 이를 방지하기 위해 사용

EventListener.tsx 파일에 다음과 같이 코드 작성

document.getElementById('root')?.addEventListener('click', (e: Event) =>{
    const {isTrusted, target, bubbles} = e;
    console.log('mouse click occurs.', isTrusted, target, bubbles);
})
document.getElementById('root')?.addEventListener('click', (e: Event) =>{
    const {isTrusted, target, bubbles} = e;
    console.log('mouse click also occurs.', isTrusted, target, bubbles);
})

export default function CopyMe(){
    return <div>EventListener</div>
}
  • 1행, 5행에서 root에 대한 click이벤트 처리기가 붙어있음
  • 해당 코드를 실행하면

 

물리 DOM 객체의 이벤트 속성

  • addEventListener 사용법이 조금 번거로움
  • 이 때문에 window나 대부분의 HTML요소는, onclick 처럼 'on' 뒤에 이벤트 이름을 붙인 속성을 제공
  • 이벤트 속성값에는 항상 이벤트 처리기를 설정해야 함
  • 아까 했던 addEventListener을 onclick으로 바꾸면 다음과 같음
window.onclick = (e:Event) => console.log('mouse click occurs')
  • 또한, <div id='root'> 에서 DOM 객체의 onclick 속성값을 다음과 같이 구현 가능
  • 옵셔널 체이닝 연산자는 document.getElementById('root')?.onclick = 콜백함수 처럼 값 설정 구문에서는 사용 불가능
  • 따라서 다음과 같이 구현
const rootDiv = document.getElementById('root')
if (rootDiv){
	rootDiv.onclick = (e: Event) => console.log('mouse click occurs')
}

 

src/pages 디렉터리에서 Onclick.tsx 파일 열고 다음과 같이 작성

const rootDiv = document.getElementById('root')
if (rootDiv){
	rootDiv.onclick = (e: Event) => {
        const {isTrusted, target, bubbles} = e;
        console.log('mouse click occurs on rootDiv', isTrusted, bubbles)
    }
    rootDiv.onclick = (e: Event) => {
        const {isTrusted, target, bubbles} = e;
        //prettier-ignore
        console.log('mouse click also occurs on rootDiv', isTrusted, bubbles)
    }
}

export default function Onclick(){
    return <div>OnClick</div>
}

 

  • addEventListener와 다르게, onclick은 마지막에 설정한 콜백 함수를 호출하기 때문에 하나만 호출됨

리액트 프레임워크의 이벤트 속성

  • 리액트도 on이벤트 식으로 작성된 HTML요소의 이벤트 속성 제공
  • 차이점이 있다면, 카멜케이스(onClick...)로 사용하는 것
  • 리액트 컴포넌트에서 이벤트 속성에 설정하는 콜백 함수는 매개변수 e의 타입이 Event가 아닌 SyntheticEvent를 설정해야 함
  • Synthetic이라는 단어는 '모든 종류의 이벤트를 종합한' 뜻으로 해석
  • BaseSyntheticEvent를 상속

Base SyntheticEvent 주요내용

interface BaseSyntheticEvent<E = object, C = any, T = any>{
	nativeEvent: E;
    currentTarget: C;
    target: T;
    preventDefault(): void;
    stopPropagation(): void;
}

 

리액트 물리DOM에서 일어나는 이벤트를 '네이티브 이벤트'라고 함

  • BaseSyntheticEvent의 nativeEvent속성은 물리DOM에서 발생하는 Event의 세부 타입인 PointerEvent와 같은 이벤트 객체 저장에 사용
  • currentTarget 속성은 이벤트 버블링과정에서 현재 이벤트를 수신한 DOM 객체를 알고 싶을 때 사용
  • target 속성은 이벤트를 처음 발생시긴 DOM 객체를 알고 싶을 때 사용

src/pages의 ReactOnClick.tsx파일을 다음과 같이 수정

import { SyntheticEvent } from "react"

export default function ReactOnClick() {
    const onClick = (e: SyntheticEvent) => {
        const { isTrusted, target, bubbles } = e;
        console.log('mouse click occurs on <button>', isTrusted, target, bubbles)
    }
    return (
    <div>
        <p>ReactOnclick</p>
        <button onClick={onClick}>Click Me</button>
    </div>
    )
}

 

 

EventTarget의 dispatchEvent 메서드

dispatchEvent(event: Event): boolean;

 

Event 객체는 다음처럼 만들 수 있음

new Event('click', {bubbles: true})

 

이렇게 생성된 Event 타입의 객체는 다음처럼 Target 속성값이 되는 DOM 객체의 dispatchEvent 메서드를 통해 이벤트 발생 가능

DOM.dispatchEvent(new Event('click', {bubbles: true}))

 

모든 DOM 객체의 부모 타입인 HTMLElement는 click 메서드를 제공한다.

  • distpatchEvent 코드와 완전히 똑같이 동작
  • DOM.click(); ...

DistpatchEvent 파일을 다음과 같이 수정

 

export default function DispatchEvent(){
    const onCallDispatchEvent = () => {
        console.log('onCallDispatchEvent')
        document.getElementById('root')?.dispatchEvent(new Event('click', {bubbles: true}))
    }
    const onCallClick = () => {
        console.log('onCallClick')
        document.getElementById('root')?.click()
    }
    return(
        <div>
            <p>DispatchEvent</p>
            <button onClick={onCallDispatchEvent}>call dispatchEvent</button>
            <button onClick={onCallClick}>call click</button>
        </div>
    )
}

  • 이 때, dispatchEvent와 click메서드로 발생한 이벤트가 false인 것을 알 수 있다.
  • isTrusted가 웹 브라우저에서 발생한건지, 프로그래밍에서 발생한건지 확인한 것이므로
  • 저 둘은 프로그래밍에서 발생한 이벤트인 것을 알 수 있음

이벤트 버블링

  1. 자식 요소에서 발생한 이벤트가 가까운 부모 요소에서 가장 먼 부모 요소까지 전달이 계속 되는 현상
  • 이벤트가 지속적으로 호출, 버블링으로 상위 태그 까지 이벤트가 전달이됨
  • 이 때, e.currentTarget의 경우 각각의 DOM 객체가 설정이 됨

EventBubblig에 다음과 같이 코드 작성

import type { SyntheticEvent } from "react";

export default function EventBubbling(){
    const onDivClick = (e: SyntheticEvent) => {
        const {isTrusted, target, bubbles, currentTarget} = e;
        console.log('click event bubbles on <div>', isTrusted, target, bubbles, currentTarget); 
    }

    const onButtonClick = (e: SyntheticEvent) => {
        const {isTrusted, target, bubbles} = e;
        console.log('click event starts at <button>', isTrusted, target, bubbles)
    }
    return(
        <div onClick={onDivClick}>
            <p>EventBubbling</p>
            <button onClick={onButtonClick}> Click Me </button>
        </div>
    );
}

  • 화면을 보면, button에서 발생한 이벤트가 부모 요소인 onDivClick 까지 전달된 것을 볼 수 있음
  • 값을 보면 currentTarget값도 다름 (버블링 중 현재 위치한 객체를 나타내므로)

StopPropagation 메서드와 이벤트 전파 막기

  • 버블링이 큰 문제는 아니나, 중단이 필요한 경우도 있음
  • 이 때는 StopPropagation 메서드를 사용

StopPropagation.tsx에 다음과 같이 코드 작성

import type { SyntheticEvent } from "react"

export default function StopPropagation(){
    const onDivClick = (e: SyntheticEvent) => console.log('click event bubbles on <div>')
    const onButtonClick = (e: SyntheticEvent) => {
        console.log('mouse click occurs on <button> and call stopPropagation')
        e.stopPropagation()
    }
    return(
        <div onClick={onDivClick}>
            <p>Propagation</p>
            <button onClick={onButtonClick}>Click Me and event Stop propagation</button>
        </div>
    )
}

  • 아까와는 다르게 div태그에는 이벤트가 전달되지 않았음.

Input 요소의 이벤트 처리

  • Input은 type 속성값에 따라 화면에 나타나는 모습과, 사용자 입력을 받는법이 다름

VariousInputs 파일에 다음과 같이 작성

export default function VariousInputs(){
    return(
        <div>
            <p>VariousInputs</p>
            <div>
                <input type="text" placeholder="enter some texts"/>
                <input type="password" placeholder="enter your password"/>
                <input type="email" placeholder="enter email address"/>
                <input type="range"/>
                <input type="button" value="I'm button"/>
                <input type="checkbox" value="I'm a checkbox" defaultChecked />
                <input type="radio" value="I'm a radio" defaultChecked />
                <input type="file" />
            </div>
        </div>
    )
}

 

Input의 onChange 이벤트 속성

  • Input의 type이 text일 때 발생하는 이벤트
import type {ChangeEvet} from 'react';

export default function OnChange(){
	const onChange = (e: ChangeEvent<HTMLInputElement>) => {
    	console.log('onChange', e.target.value)
    }
    return <input type="text" onChange={onChange} />
}
  • 이벤트 속성에 onChange라는 이름의 이벤트 처리기 설정
  • 이 때 매개변수의 타입은 ChangeEvent<HTMLInputElement>로 설정
  • 이러면 HTMLInputElement 타입의 물리 DOM 객체 값을 e.target 형태로 획득 가능

Input 요소의 이벤트 관련 속성들

input : React.DetaildHTMLProps<React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement>;
  • 위 코드로, Input 요소가 제공하는 속성은 React.InputHTMLAttributes<HTMLInputElement> 형태로 얻어

InputHTMLAttributes<T> 속성에서 onChange 이벤트와 관련된 속성

  • Check & Radio
    • checked 속성
  • text, email, password, range
    • value 속성
  • file
    • files 속성 값
  • 이 속성들을 통해 사용자가 입력한 구체적인 내용을 알 수 있음

Input의 defaultValue와 defaultChecked 속성

  • check : 사용자가 input에 입력한 값을 얻을 때 사용
  • defaultValut와 defaultChecked : 초깃값 설정에 사용

OnChange 컴포넌트 구현하기 

import type { ChangeEvent, SyntheticEvent } from "react"

export default function OnChange(){
    const onChangeValue = (e: ChangeEvent<HTMLInputElement>) => {
        e.stopPropagation()
        e.preventDefault()
        console.log('onChangeValue', e.target.value)
    }

    const onChangeChecked = (e: ChangeEvent<HTMLInputElement>) => {
        e.stopPropagation()
        console.log('onChangeChecked', e.target.checked)
    }
    const onChangeFiles = (e: ChangeEvent<HTMLInputElement>) => {
        e.stopPropagation()
        console.log('onChangeFiles', e.target.files)
    }

    return(
        <div>
            <p>onChange</p>
            <input type="text" onChange={onChangeValue}
                placeholder="type some text" defaultValue="Hello">
            </input>
            <input type="checkbox" onChange={onChangeChecked} defaultChecked />
            <input type="file" onChange={onChangeFiles} multiple accept="images/*"/>

        </div>
    )
}

multiple과 accept

  • multiple의 경우 파일을 여러개 선택할 수 있도록 하고
  • accept의 경우 파일의 확장자를 제한함

input type="file" 에서의 onChange

  • e.target.files 속성값으로 사용자가 선택한 파일 목록을 얻을 수 있음
  • 이 때 속성의 타입은 FileList로, 리액트가 아니라 웹 브라우저의 자바스크립트 엔진이 제공
  • FileList의 item과 인덱스 연산자 []는 해당하는 item 인덱스에 따라 File 타입의 속성 값 얻기 가능
  • Blob 타입과 Blob 타입을 확장한 File 타입도 제공

FileInput 을 다음과 같이 작성

import type { ChangeEvent, SyntheticEvent } from "react"

export default function FileInput(){
    const onChange = (e: ChangeEvent<HTMLInputElement>) => {
        const files: FileList | null = e.target.files
        if(files){
            for(let i = 0; i < files.length; i++){
                const file: File | null = files.item(i) //or file = files[i];
                console.log(`file[${i}]`, file)
            }
        }
    }
    return(
        <div>
            <p>File Input</p>
            <input type="file" onChange={onChange} multiple accept="image/*" />
        </div>
    )
}

  • 콘솔 창에서 해당 이미지의 세부 내용을 볼 수 있음

Dage & Drop 이벤트 처리

  • 모든 HTMLElement 상속 요소는 draggable 이라는 boolean 타입의 속성을 제공
  • true 이면 드래그 가능 아니면 불가능

Drag & Event 

종류 발생 시기 리액트 이벤트
속성 이름
dragenter 드래그한 요소나 텍스트 블록을 적합한 드롭 대상 위에 올렸을 때 onDragEnter
dragstart 사용자가 요소나 텍스트 블록을 드래그 하기 시작했을 때 onDragStart
drag 요소나 텍스트 블록을 드래그할 때 onDrag
dragover 요소나 택스트 블록을 적합한 드롭 대상 위로 지나갈 때  onDragOver
dragleave 드래그하는 요소나 텍스트 블록이 적합한 드롭 대상 위를 벗어났을 때 onDragLeave
dragend 드래그가 끝났을 때 onDragEnd
drop 요소나 텍스트 블록을 적합한 드롭 대상에 드롭했을 때 onDrop
  • DragEvent 타입에서 dataTransfer이라는 속성이 있음
  • 이 속성은 DataTransfer 타입을 가짐
    • 파일이 드롭 되었을 땐 files 속성으로 드롭한 파일의 정보를 알 수 있음
    • 나머지는 각자에 맞게 알 수 있음

PreventDefault

  • 어떤 사용자 액션에 따라 이벤트가 발생했을 때, 이 이벤트와 관련한 웹 브라우저의 기본 구현을 막는 것
  • 웹 브라우저는 기본으로 drop 이벤트가 발생하지 않도록 설계됨
  • 따라서 실행하려고 하면, dragover 이벤트 처리기에서 preventDefault를 호출해야함

DragDrop 컴포넌트를 다음과 같이 작성

import type { DragEvent } from "react"

export default function DragDrop(){
    const onDragStart = (e: DragEvent<HTMLElement>) => 
        console.log('onDragStart', e.dataTransfer)
    const onDragEnd = (e: DragEvent<HTMLElement>) => 
        console.log('onDragEnd', e.dataTransfer)
    

    const onDragOver = (e: DragEvent) => e.preventDefault()
    const onDrop = (e: DragEvent) => {
        e.preventDefault()
        console.log('onDrop', e.dataTransfer)
    }

    return(
        <div>
            <p>DragDrop</p>
            <div draggable onDragStart={onDragStart} onDragEnter={onDragEnd}>
                <h1>Drag Me</h1>
            </div>
            <div onDrop={onDrop} onDragOver={onDragOver}>
                <h1>Drop over Me</h1>
            </div>
        </div>
    )
}

 

FileDrop 컴포넌트 구현하기

  • File Input과 다른점
    • 웹 브라우저 바깥에서 안쪽으로 떨어지므로 draggable 같은 요소 필요 없음
    • onDrop이 호출되어야 하므로 onDragOver에 preventDefault 메서드 호출 필요
    • FileInput은  e.target.files에서 파일 객체을 얻지만
    • FileDrop은 e.Transfer.files에서 파일 객체를 얻음 (드랍한 파일을 가져와야 하니까)
import { DragEvent } from "react"
export default function FileDrop(){
    const onDragOver = (e: DragEvent) => e.preventDefault()

    const onDrop = (e: DragEvent) => {
        e.preventDefault()
        const files = e.dataTransfer.files
        if(files){
            for(let i = 0; i < files.length; i++){
                const file: File | null = files.item(i)
                console.log(`file[${i}]`, file)
            }
        }
    }

    return (
        <div>
            <p>FileDrop</p>
            <div onDrop={onDrop} onDragOver={onDragOver}>
                <h1>Drop Image Files over Me</h1>
            </div>
        </div>
    )
}

  • 파일에서 onDrop 이벤트가 처리되어 파일 정보가 출력이 됨