for 속성은 HTML의 label 태그에서 사용되는 속성으로, 해당 label이 어떤 폼 요소를 설명하는지를 지정합니다. 이 속성은 레이블과 폼 요소를 연결하는 데 사용됩니다.
for 속성의 값으로는 연결하려는 폼 요소의 id 속성을 사용합니다. 예를 들어, 아래는 label과 input 요소를 연결하는 예시입니다:
html
Copy code
사용자 이름:
여기서 for="username"은 해당 label이 id가 "username"인 input 요소를 설명한다는 것을 나타냅니다. 이렇게 하면 사용자가 label을 클릭하면 연결된 input 요소가 자동으로 활성화됩니다.
또한, HTML5에서는 label 요소 내부에 포함된 폼 요소에 대한 설명을 위해 label 요소의 for 속성 없이도 사용할 수 있습니다. 이 경우 label 요소는 폼 요소를 직접 감싸면 됩니다:
html
Copy code
사용자 이름:
이때 label 요소의 for 속성은 필요하지 않습니다. 이 방식도 화면 리더 등의 보조 기술에서는 제대로 작동합니다.
파일인풋은 해킹의 위험이 있어 value prop을 지정할 수 없고 비제어 컴포넌트로 만들어야 한다
e.target.value// 에러발생
e.target.files[0] //이거로 파일 정보 불러올 수 있음
const handleChange = (name, value) => {
setValues((prev) => ({ ...prev, [name]: value }));
};
const handleInputChange = (e) => {
handleChange(e.target.name, e.target.value); //file input은 e.target.value가 아닌 e.target.files[0]를 사용해야하기 때문에 구분지어 만들어줘야 함
};
함수도 구분해주어야 함
ref는 React의 선언적인 접근 방식과는 달리, 명령형 프로그래밍의 요소를 도입하여 DOM을 직접 조작하거나 특정 컴포넌트의 메서드에 접근할 때 유용
그래서 ref 객체가 존재하나를 확인하는 조건문이 필요하다
if(inputRef.current)
import { useRef } from 'react';
// ...
const ref = useRef();
const ref = useRef();
// ...
const node = ref.current;
if (node) {
// node 를 사용하는 코드
}
const [preview, setPreview] = useState();
useEffect(() => {
if (value) {
//ObjectURL (사이드 이펙트)
const newPreview = URL.createObjectURL(value);
setPreview(newPreview);
return () => {
setPreview();
//ObjectURL 해제
URL.revokeObjectURL(newPreview);
};
}
}, [value]);
return (
<div>
<img src={preview} alt="이미지 미리보기"></img>
<input type="file" onChange={handleChange} ref={inputRef}></input>
{value && <button onClick={handleClearClick}>X</button>}
</div>
);
}
ObjectURL의 경우 사이드 이펙트에 해당해서 useEffect 내에 작성을 해주어야 한다
useEffect 는 리액트 컴포넌트 함수 안에서
사이드 이펙트를 실행하고 싶을 때 사용하는 함수입니다.
예를들면 DOM 노드를 직접 변경한다거나,
브라우저에 데이터를 저장하고,
네트워크 리퀘스트를 보내는 것처럼 말이죠.
주로 리액트 외부에 있는 데이터나 상태를 변경할 때 사용하는데요.
간단한 예시들을 보면서 어떤 식으로 활용할 수 있는지 가볍게 살펴봅시다.
페이지 정보 변경
useEffect(() => {
document.title = title; // 페이지 데이터를 변경
}, [title]);
네트워크 요청
useEffect(() => {
fetch('https://example.com/data') // 외부로 네트워크 리퀘스트
.then((response) => response.json())
.then((body) => setData(body));
}, [])
데이터 저장
useEffect(() => {
localStorage.setItem('theme', theme); // 로컬 스토리지에 테마 정보를 저장
}, [theme]);
참고: localStorage 는 웹 브라우저에서 데이터를 저장할 수 있는 기능입니다.
타이머
useEffect(() => {
const timerId = setInterval(() => {
setSecond((prevSecond) => prevSecond + 1);
}, 1000); // 1초마다 콜백 함수를 실행하는 타이머 시작
return () => {
clearInterval(timerId);
}
}, []);
참고: setInterval 이라는 함수를 쓰면 일정한 시간마다 콜백 함수를 실행할 수 있습니다.
여기서 한 가지 의문점이 들 수도 있습니다.
앞에서 우리는 데이터 불러오는 기능 만들 때
처음에는 onclick 이벤트 핸들러에서 네트워크 리퀘스트를 보냈고,
페이지가 열리자마자 데이터를 불러오기 위해서 useEffect 를 사용하는 걸로 바꿨습니다.
하지만 만약 둘 다 가능한 경우라면 핸들러 함수를 써야 하는 걸까요?
아니면 useEffect 를 써야 하는 걸까요?
여기에 정해진 규칙은 없습니다.
하지만 useEffect 는 쉽게 말해서 '동기화'에 쓰면 유용한 경우가 많은데요.
여기서 동기화는 컴포넌트 안에 데이터와 리액트 바깥에 있는 데이터를 일치시키는 걸 말합니다.
이게 어떤 의미인지 간단한 예시를 통해 살펴봅시다.
아래 App 컴포넌트는 인풋 입력에 따라 페이지 제목을 바꾸는 컴포넌트인데요.
import { useState } from 'react';
const INITIAL_TITLE = 'Untitled';
function App() {
const [title, setTitle] = useState(INITIAL_TITLE);
const handleChange = (e) => {
const nextTitle = e.target.value;
setTitle(nextTitle);
document.title = nextTitle;
};
const handleClearClick = () => {
const nextTitle = INITIAL_TITLE;
setTitle(nextTitle);
document.title = nextTitle;
};
return (
<div>
<input value={title} onChange={handleChange} />
<button onClick={handleClearClick}>초기화</button>
</div>
);
}
export default App;
handleChange 함수와 handleClearClick 함수가 있습니다.
모두 title 스테이트를 변경한 후에 document.title 도 함께 변경해주고 있는데요.
여기서 document.title 값을 바꾸는 건 외부의 상태를 변경하는 거니까 사이드 이펙트입니다.
만약 새로 함수를 만들어서 setTitle 을 사용하는 코드를 추가할 때마다
document.title 값도 변경해야 한다는 걸 기억해뒀다가 관련된 코드를 작성해야 한다는 점이 아쉽습니다.
동료 개발자가 나중에 컴포넌트를 수정하거나 1년 뒤의 내가 컴포넌트를 수정할 때 빠뜨리기 좋겠죠?
import { useEffect, useState } from 'react';
const INITIAL_TITLE = 'Untitled';
function App() {
const [title, setTitle] = useState(INITIAL_TITLE);
const handleChange = (e) => {
const nextTitle = e.target.value;
setTitle(nextTitle);
};
const handleClearClick = () => {
setTitle(INITIAL_TITLE);
};
useEffect(() => {
document.title = title;
}, [title]);
return (
<div>
<input value={title} onChange={handleChange} />
<button onClick={handleClearClick}>초기화</button>
</div>
);
}
export default App;
useEffect 를 사용한 예시에서는 document 를 다루는 사이드 이펙트 부분만 따로 처리하고 있는데요.
이렇게 하니 setTitle 함수를 쓸 때마다 document.title 을 변경하는 코드를 신경 쓰지 않아도 되니까 편리합니다.
게다가 처음 렌더링 되었을 때 'Untitled'라고 페이지 제목을 변경하는 효과까지 낼 수 있죠.
그리고 이 코드를 본 사람이라면 누구든 이 컴포넌트는 title 스테이트 값을 가지고 항상 document.title 에 반영해줄 것이라고 쉽게 예측할 수 있는데요.
이렇게 useEffect 는 리액트 안과 밖의 데이터를 일치시키는데 활용하면 좋습니다.
useEffect 를 사용했을 때 반복되는 코드를 줄이고, 동작을 쉽게 예측할 수 있는 코드를 작성할 수 있기 때문이죠.
useEffect(() => {
// 사이드 이펙트
return () => {
// 사이드 이펙트에 대한 정리
}
}, [dep1, dep2, dep3, ...]);
useEffect 의 콜백 함수에서 사이드 이펙트를 만들면 정리가 필요한 경우가 있습니다.
이럴 때 콜백 함수에서 리턴 값으로 정리하는 함수를 리턴할 수 있었는데요.
리턴한 정리 함수에서는 사이드 이펙트에 대한 뒷정리를 합니다.
예를 들면 이미지 파일 미리보기를 구현할 때 Object URL을 만들어서 브라우저의 메모리를 할당(createObjectURL) 했는데요. 정리 함수에서는 이때 할당한 메모리를 다시 해제(revokeObjectURL)해줬었죠.