3-2 : React 기초 (엘리스 AI 트랙)

[엘리스 AI 트랙] 3주차 - 2

  • 온라인 강의, 실습 내용 정리본
  • 10/8 금요일

React 기초

1. props와 State

01. Props (Properties)

1) Props란?

  • 컴포넌트에 원하는 값을 넘겨줄 때 사용
  • 변수, 함수, 객체, 배열 등 제한 없이 JS 요소를 넘겨줄 수 있다.
  • 주로 컴포넌트의 재사용을 위하여 사용
// 컴포넌트 생성
const Greeting = (props) => {
	return <h1>Hi, My name is {props.name}</h1>;
}
// 컴포넌트 사용
const App =() => {
	return <div>
		<Greeting name="IceBear" />
		<Greeting name="Panda" />
		<Greeting name="Greezly" />
	</div>;
}

2) Props는 읽기 전용

: props 값 임의 변경 사용하면 X. 변경 원할 시 새로운 변수 사용하기.

// 잘못된 코드!!!!!!!!!!!! 오류는 아니지만 잘못된 사용임!!
const Greeting = (props) => {
	props.name = props.name + "곰";
	return <h1>Hi, My name is {props.name}</h1>;
}
// 올바른 사용법
const Greeting = (props) => {
	const bear = props.name + "곰";
	return <h1>Hi, My name is {bear}</h1>;
}

3) DOM Element의 Attributes

  • 기본적인 DOM Element(div, span 등)들의 Attribute
    : camel case로 작성. (예: tabIndex, className 등)
  • ‘data-’ 또는 ‘aria-’ 로 시작하는 Attribute는 예외!
  • HTML의 Attribute와 다른 이름을 가지는 Attribute 주의하기. (class → className, for → htmlFor 등)
  • HTML의 Attribute와 다른 동작 방식을 가진 Attribute 주의하기. (checked(defaultChecked), value(defaultValue), style 등)
  • React에서만 쓰이는 새로운 Attribute 주의하기. (key 등)

4) HTML과 다른 방식의 React Attribute (checked, value)

  • HTML에서 checked 또는 value는 해당 값이 초기값 vs React 내에서는 현재 값을 의미
  • 즉, checked 값이 false로 설정돼있으면 체크박스 클릭해도 값의 변화 없다.
  • 초기값의 의미로 checked 또는 value를 사용하려면 defaultChecked, defaultValue속성으로 설정!
(<input type="checkbox" checked={false} />)

5) React에서만 쓰이는 새로운 Attribute(key)

  • Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.
  • Key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 Element에 지정해야 함
  • Key는 배열 내부 형제 사이에서 고유해야 하고 전체에서 고유할 필요 X. → 다른 배열을 만들 때는 key 같아도 O.
const Bears = () => {
	const bears = [
		{key: '3', value: 'iceBear'}, 
		{key: '1', value: 'greezly'}, 
		{key: '2', value: 'panda'}
	]
	return (
		<div>
			{bears.map((item) => (
				<li key={item.key}>{item.value}</li>
			))}
		</div>
	);
}



02. State

1) State란?

: State는 컴포넌트 내부, 지속적으로 변경이 일어나는 값을 관리하기 위해 사용.

const App = () => {
    const [value, setValue] = useState(초기값); // value: state명, setValue: set+State명
    return ...
}
  • useState import 하기
    import { useState } from "react";
  • State 값 변경시 꼭 setState 함수로! state 값을 임의로 변경하지 말자.
    const [value, setValue] = useState("");
    ...
    setValue("안녕하세요");
  • State는 Component 내에서 유동적인 값을 저장. 개발자 의도나 사용자 입력에 의해 변할 수 있다. State 값 변경되고 재렌더링이 필요하면 React가 자동으로 변경된 부분 렌더링. (이러기 위해서는 setState 함수 사용 필수. 임의 변경시 리액트가 알아차리지 못함!)
    import {useState} from 'react';
    
    function DoorBell(){
    	const [cnt, setCnt] = useState(0);
    	return (
    		<div>
    			<button onClick={() => setCnt(cnt + 1)}>딩동</button>
    			<p>초인종을 {count}번 눌렀다!</p>
    		</div>
    	);
    }

2) State를 변경하는 두 가지 방법

  • setState 내에 변경할 값 넣기
    const [count, setCount] = useState(0);
    setCount(count+1);
  • setState에 함수를 넣기 : 함수가 반환(return)하는 값으로 State가 변경
    const [count, setCount] = useState(0);
    setCount((current) => {
    	return current + 1;
    });
    현재 값을 기반으로 State를 변경할 땐 함수를 넣는 방법을 권장

3) 배열이나 객체를 갖는 State 만들 때 주의사항

: 예시의 경우 React가 State의 변경을 감지하지 못함! 내용물만 바뀌고 bear 객체 자체가 변경되지 않았기 때문! 리액트는 객체와 배열은 그 자체가 변경될 경우에만 다시 렌더링 함!!!!!!!!!

(예를 들어 마치 슈뢰딩거의 고양이.. 고양이가 박스 안에서 살아있는 상태에서 죽은 상태로 바뀌어도 박스 밖에선 모른다. 리액트는 우리처럼 모른다.)

// 잘못된 사용
const [bear, setBear] =
useState({name: "iceBear", age: 5})
setBear((current) => {
	current.age = 6;
	return current;
})
// 올바른 사용
const [bear, setBear] =
useState({name: "iceBear", age: 5})
setBear((current) => {
	const newBear = {...current};
	newBear.age = 6;
	return newUser;
})




2. 이벤트 처리

01. 이벤트 소개

1) 이벤트란?

: 이벤트(event)란 웹 브라우저가 알려주는 HTML 요소에 대한 사건의 발생. 유저의 행동, 개발자 의도한 코드에 의해 발생할 수도 있다. Element가 로딩되었을 때, Element를 클릭, 마우스 오버, 더블 클릭, 키보드 입력 등 다양한 이벤트가 존재. 이벤트 핸들러 함수는 이벤트 생겼을 때 처리하는 방법이다.


2) 이벤트 처리(핸들링) 방법

: React에서 이벤트를 처리하는 방법은 크게 두 가지! 별도의 핸들링 함수를 선언하고 Element에 넘겨주는 방법과 이벤트를 할당하는 부분에서 익명 함수를 작성하는 방법으로 나뉜다.

  • 핸들링 함수 선언
    const App = () => {
    	const handleClick = () => {
    		console.log("눌렀다!");
    	}
    	return (
    		<div>
    			<button onClick={handleClick}>눌러보자</button>
    		</div>
    	);
    }
  • 익명 함수로 처리
    const App = () => {
    	return (
    		<div>
    			<button onClick={() => {console.log('눌렀다!')}}>눌러보자</button>
    		</div>
    	)
    }

3) 이벤트 객체

  • DOM Element의 경우 핸들링 함수에 event object를 매개변수로 전달.
  • event object를 이용하여 이벤트 발생 원인, 이벤트가 일어난 Element에 대한 정보 접근 가능.
  • 이벤트 형태와 DOM 종류(button, form, input 등)에 따라 전달되는 이벤트 object의 내용 다름.
  • 참고: https://developer.mozilla.org/ko/docs/Web/API/Event
const App = () => {
	const handleChange = (event) => {
		alert(event.target.value + '라고 입력했어요!'); // event.target은 이벤트가 일어난 엘리먼트를 뜻함.
	}
	return (
		<div>
			<input onChange={handleChange} />
		</div>
	);
}

4) 많이 쓰이는 DOM 이벤트

onClick : Element를 클릭했을 때
onChange : Element의 내용이 변경되었을 때(input의 텍스트를 변경, 파일 선택 등)
onKeyDown, onKeyUp, onKeyPress : 키보드 입력이 일어났을 때
onDoubleClick : Element를 더블 클릭했을 때
onFocus : Element에 Focus되었을 때
onBlur : Element가 Focus를 잃었을 때
onSubmit : Form Element에서 Submit 했을 때



02. 컴포넌트 내 이벤트 처리

1) DOM Input 값을 State에 저장하기

: event object의 target은 이벤트의 원인이 되는 Element를 가리킨다. 현재 event의 target은 input element이므로 입력된 value를 가져와 setState를 하는 예시.

const App = () => {
	const [inputValue, setInputValue] = 
useState("");

	const handleChange = (event) => {
		setInputValue(event.target.value);
	}
	return (
		<div>
			<input onChange={handleChange} defaultValue={inputValue} />
			<br />
			입력한 값: {inputValue}
		</div>
	);
}

2) 여러 Input 동시에 처리하기

: 한 개의 이벤트 핸들링 함수를 재사용해 여러 input element에서 값을 입력받는 방법

  • object 값 변경 시 보통 object.key = "value"와 같이 작성하는데

  • 그 대신, object["key"] = "value" 처럼 작성할 수 있고 "key" 자리에 변수를 대입하여 다이나믹하게 값 변경 가능.

  • Dynamic key 예시

const obj = {
    a: "example1",
    b: "example2",
    c: "example3"
}
const key = "b";
obj[key] = "수정"
console.log(obj);// { a: "example11", b: "수정", c: "example3" }
  • 이런 Object 특성을 이용 → 한 개의 이벤트 핸들링 함수로 여러 element의 이벤트를 처리 가능!

    : 여러 State 선언 대신 object를 활용하여 여러 개의 input을 state로 관리하는 방법이 있다. target으로부터 name을 받아와 해당 name의 key에 해당하는 value를 변경하여 state에 반영하기!

const App = () => {
	const [bearInfo, setBearInfo] = useState({bearName: "아이스베어", home: "동굴"});

	const handleChange = (event) => {
		const {name, value}
		const newBearInfo = {...bearInfo};
		newBearInfo[name] = value;
		setUser(newBearInfo);
	};
	
	return (
		<div>
			<input name="bearName" onChange={handleChange} value={bearInfo.bearName} />
			<br />
			<input name="home" onChange={handleChange} value={bearInfo.home} />
			<p>
				내 친구 {bearInfo.bearName}{bearInfo.home}에 살고 있어요.
			</p>
		</div>
	);
}



03. 다른 컴포넌트로 이벤트 전달

1) 컴포넌트간 이벤트 전달하기

: 입력 받은 정보를 부모 컴포넌트에서 활용해야 하는 경우 이벤트를 Props로 전달하여 처리

const MyForm = ({onChange}) => {
	return (
		<div>
			<span>곰 이름: </span>
			<input onChange={onChange} />
		</div>
	);
}
const App = () => {
	const [bearname, setBearname] = useState('');
	return (
		<div>
			<h1>{bearname} 곰이에요.</h1>
			<MyForm 
				onChange={(event) => {
					setBearname(event.target.value)
				}} 
			/>
		</div>
	);
}

2) 커스텀 이벤트

const RingDingDong = ({onRingDingDong}) => {
	const [count, setCount] = useState(0);
	const handleClick = () => {
		if(count >= 2) {
			onRingDingDong();
		}
		setCount(count + 1);
	}
	return <button onClick={handleClick}>
		똑똑똑 버튼 세번 누르면 문이 열립니다.({count})
	</button>
}
const App = () => {
	return (
		<div>
			<RingDingDong
				onSOS={() => {
					alert("문 열어주기!");
				}}
			/>
		</div>
	);
};

3) 이벤트 명명법

  • 직접 이벤트 제작 시 이름 자유 설정 가능. 보통은 관습적으로 “on” + 동사 or “on” + 명사 + 동사 형태로 작성. (예: onClick, onInputChange)
  • 핸들링 함수의 경우, “handle” + 동사 or “handle” + 명사 + 동사 의 형태로 작성. “handle” 대신 이벤트명과 동일한 “on”을 앞에 붙여도 O. (예. handleClick)




3. Hooks

01. Hooks 개요

1) Hook이란?

: 함수 컴포넌트에서 데이터를 관리(State)하고 데이터가 변경될 때 상호작용(Effect)을 하기 위해 사용

2) 유의사항

  • Hook은 React 함수(컴포넌트, Hook) 내에서만 사용 가능
  • Hook의 이름은 반드시 **use**로 시작
  • 최상위 Level에서만 Hook을 호출할 수 있다. (if문, for문 안 쪽 또는 콜백함수 내에서 호출 불가)
const App = () => {
	const [name, setName] = useState('')
		return (
			<div>
				<h1>{name}님 접속하셨네요!</h1>
			</div>
		);
}



02. State Hook과 Effect Hook

1) State Hook

  • useState는 컴포넌트 내 동적인 데이터를 관리할 수 있는 hook.
  • 최초에 useState가 호출될 때 초기값으로 설정되고 이후 재 렌더링이 되면 초기값 무시.
  • state는 읽기 전용이므로 직접 수정 불가!! (사실 가능한데 이렇게 하면 재 렌더링이 안 됨.)
  • state를 변경하기 위해서는 setState를 이용.
  • state가 변경되면 자동으로 컴포넌트가 재 렌더링 됨.
const App = () => {
	// 일반적인 useState 사용법
	const [state이름, setState이름] = useState(초기값);
}
  • state 변경 방법 두 가지 :
      1. setState 함수에 직접 값을 입력
      1. 현재 값을 매개변수로 받는 함수를 전달 : 함수에서 return되는 값이 state에 반영 됨.
const App = () => {
	const [lyrics, setLyrics] = useState("");
	
	// State를 변경할 값을 직접 입력
	setLyrics("안녕하세요. 선생님~")
	
	// 또는 현재 값을 매개변수로 받는 함수 선언
	// return 값이 state에 반영됨
	setLyrics((current) => {
		return current + "안녕? 친구야";
	})
}

2) Effect Hook

  • Effect Hook을 사용하면 함수 컴포넌트에서 side effect 수행 가능.
  • 컴포넌트가 최초로 렌더링될 때 & 지정한 State나 Props가 변경될 때마다 effect 콜백 함수가
    호출 됨.
  • Deps : 변경을 감지할 변수들의 집합(배열)
  • EffectCallback : Deps에 지정된 변수가 변경될 때 실행할 함수
const App = () => {
	useEffect(EffectCallback, Deps?)
}
const App = () => {
	const [count, setCount] = useState(0);
	
	useEffect(() => {
	console.log('버튼 클릭 : ${count} 번')
	}, [count])

	return (
		<div>
			<button onClick={() => setCount(count + 1)}>
				클릭!
			</button>
		</div>
	)
}
  • useEffect의 이펙트 함수 내에서 다른 함수를 return할 경우
    : state가 변경되어 컴포넌트가 다시 렌더링되기 전과 컴포넌트 소멸 시(Destroy) 호출할 함수를 지정.
const App = () => {
	useEffect(() => {
		// State가 변경될 때, 컴포넌트를 렌더링할 때
			const intervalId = setInterval(()=>{
				console.log("State가 변경될 때, 컴포넌트를 렌더링할 때 실행!");
			}, 3000);
		
		// 컴포넌트를 재 렌더링 하기 전에, 컴포넌트가 없어질 때만 실행될 함수 지정
		return () => {
			clearInterval(intervalId);
		}
	}, []) 
...



03. 이외의 Hooks

  • useMemo
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
    • 메모이제이션 된 반환

    • 지정한 State나 Props가 변경될 경우 해당 값을 활용해 계산된 값을 메모이제이션하여 재렌더링 시 불필요한 연산을 줄인다.

    • useMemo의 연산은 렌더링 단계에서 이루어짐 → 시간이 오래 걸리는 로직을 작성하지 않는 것이 권장됨.

      const App = () => {
      	const [mammal, setMammal] = useState('bear')
      	const [animalName, setAnimalName] = useState('ice')
      	
      	// 포유류 또는 동물 이름이 바뀔 때마다 메모이제이션
      	// useMemo 내부 함수에서 리턴된 값이 animalFullName 변수에 들어간다.
      	const animalFullName = useMemo(() => {
      		return ′${animalName} ${mammal}}, [animalName, mammal])
      }

  • useCallback

    const memoizedCallback = useCallback(
    	() => {
    		doSomething(a, b);
    	}, 
    	[a, b]
    );
    • 메모이제이션 된 콜백 함수 반환

    • 컴포넌트가 재렌더링될 때 불필요하게 함수가 다시 생성되는 것 방지

    • useMemo(() => fn, deps) 와 useCallback(fn, deps) 는 같다. (useMemo에서 함수를 리턴해주는 것과 useCallback은 일치) → 단지 개발자의 편의를 위해 분리했다고 생각하자

      const App = () => {
      	const [mammal, setMammal] = useState('bear')
      	const [animalName, setAnimalName] = useState('ice')
      	
      	// 포유류 또는 동물 이름이 바뀔 때마다
      	// 풀네임을 return하는 함수를 메모이제이션
      	const getAnimalFullName = useCallback(() => {
      		return ′${animalName} ${mammal}}, [animalName, mammal])
      	
      	return <>{getAnimalFullName()}</>
      }

  • useRef
    const refContainer = useRef(initialValue);
    • 컴포넌트 생애 주기 내에서 유지할 ref 객체를 반환

    • ref 객체는 .current라는 프로퍼티를 가지며 이 값을 자유롭게 변경할 수 있다.

    • 일반적으로 React에서 DOM Element에 접근 할 때 사용(DOM Element의 ref 속성
      이용)

    • useRef에 의해 반환된 ref 객체가 변경되어도 컴포넌트가 재렌더링되지 않는다.

      const App = () => {
      	const inputRef = useRef(null)
      	const onButtonClick = () => {
      		inputRef.current.focus()
      	}
      	return (
      		<div>
      			<input ref={inputRef} type="text" />
      			<button onClick={onButtonClick}>
      				input으로 포커스 이동하기
      			</button>
      		</div>
      	)
      }



04. 커스텀 Hook

1) Custom Hook

: UI 요소의 재사용성을 높이기 위해 컴포넌트를 만드는 것처럼, 로직의 재사용성을 높이기 위해 Custom Hook을 제작. ⇒ 커스텀 훅은 그냥 훅을 사용하는 함수임!

function useMyHook(args) {
	const [status, setStatus] = useState(undefined);
	// ...
	return status;
}
  • 한 로직이 여러 번 사용될 경우 함수를 분리하는 것처럼 Hook을 만드는 것
  • Hook의 이름은 ‘use’로 시작해야 한다.
  • 한 Hook 내의 state는 공유되지 않는다.

좋은 웹페이지 즐겨찾기