🗝️ useId
React 18에서 도입된 useId는 접근성(A11y)과 SSR(서버 사이드 렌더링) 환경에서 안전하게 고유 ID를 생성하기 위한 훅입니다. 단순히 랜덤 ID르 만드는 것처럼 보이지만, 더 중요한 역할을 담당합니다.
1️⃣ useId가 필요한 이유
id가 필요한 상황은 많습니다.
<label for="input-id">와 폼 요소 연결- 접근성(A11y) 속성 (
aria-labelledby,aria-describedby) - 여러 컴포넌트 인스턴스에서 고유 ID 생성
하지만 기존 방식에는 문제가 있습니다.
❌ 기존 방식의 문제점
- 랜덤 ID 생성 → SSR에서 hydration mismatch 발생
const id = Math.random(); // ❌ 서버와 클라이언트에서 서로 다른 값
- 서버 렌더링 시 생성된 ID와 클라이언트 hydration 시 생성된 ID가 달라짐
- React는 이를 경고하고 렌더링 효율이 떨어짐
- 전역 카운터 방식도 안전하지 않음
- CSR(Client-Side Render)에서는 동작하나, SSR에서는 순서가 달라질 수 있음
- 컴포넌트 재사용이 많을수록 ID 충돌 위험 증가
➡️ React는 이 문제를 해결하기 위해 useId를 도입했습니다.
2️⃣ useId 기본 사용법
import { useId } from 'react';
function InputField() {
const id = useId();
return (
<div>
<label htmlFor={id}>Name</label>
<input id={id} type="text" />
</div>
);
}
✔ 특징
- 컴포넌트마다 고유 ID 생성
- 렌더 간 ID가 변하지 않음
- SSR + hydration 환경에서도 서버/클라이언트 ID가 일치함
3️⃣ useId가 SSR에서 안전한 이유
React는 컴포넌트 트리 구조를 기반으로 ID를 결정합니다.
- 서버에서 렌더링될 때 생성한 ID
- 클라이너트 hydration 시 동일한 트리 구조를 기반으로 같은 ID 생성
즉, 랜덤이 아니라 트리 기반 결정적 ID입니다.
➡️ 그래서 hydration mismatch 문제를 근본적으로 해결합니다.
4️⃣ 다중 ID 생성 시: 접두사(prefix) 활용
복잡한 컴포넌트에서는 ID 여러 개가 필요할 수 있습니다.
function ProfileForm() {
const id = useId();
return (
<>
<label htmlFor={`${id}-name`}>Name</label>
<input id={`${id}-name`} />
<label htmlFor={`${id}-email`}>Email</label>
<input id={`${id}-email`} />
</>
);
}
✔ 장점
- 같은 기본 ID를 기반으로 여러 개의 하위 ID 생성 가능
- 충돌 없이 안전
5️⃣ useId가 특히 필요한 상황
✔ 접근성(A11y) 연결이 필요할 때
const id = useId();
<label htmlFor={id}>Email</label>
<input id={id} aria-describedby={`${id}-desc`} />
<span id={`${id}-desc`}>We never share your email.</span>
✔ 반복 렌더링되는 리스트 + 각 요소에 ID 필요할 때
- key와는 다른 목적 (key는 렌더링 식별용 / id는 DOM 요소 식별용)
- 고유한 폼 ID를 유지해야 한다면 useId 적합
✔ 컴포넌트 라이브러리 제작 시
- tooltip, dropdown, modal 등 A11y 요구하는 컴포넌트에 필수
✔ SSR 환경(Next.js 등)에서 안정적인 ID 필요할 때
- hydration mismatch를 100% 방지
6️⃣ useId를 사용하면 안 되는 경우
useId는 "고유하지만 특정 데이터의 identity와는 무관한 ID"를 만드므로, 이런 곳에서는 쓰면 안 됩니다.
❌ 리스트의 key로 사용 (절대 금지)
items.map(item => <Item key={useId()} />); // ❌ 매 렌더마다 달라짐
- key는 stable하고 데이터 기반이어야 함
- useId는 렌더마다 새로 호출되므로 key에 적합하지 않음
❌ 서버와 클라이언트가 다른 순서로 렌더링될 수 있는 비결정적 구조
- 조건부 렌더링이 복잡해 구조가 달라질 수 있다면 useId 안정성 떨어짐
❌ 사용자가 구분해야 하는 데이터의 ID
- 유저/글/상품 ID 등느 useId가 아닌 서버 데이터의 진짜 ID 사용해야 함
7️⃣ 정리: useId는 언제 쓰는가?
✔ 써야 한다
- label + input 연결
- aria-labelledby / aria-describedby
- a11y 컴포넌트 만들 때
- 폼 요소를 여러 개 렌더링하는 컴포넌트
- SSR에서 ID 안정성이 필요할 때(Next.js)
❌ 쓰면 안 된다
- key로 사용
- 데이터 식별용 ID 필요할 때
- 매 렌더마다 새 ID가 필요한 상황 (timestamp, random 등)