<Typography variant='body2' sx={{ mb: 2 }}>
<div
dangerouslySetInnerHTML={{ __html: record.content }}
style={{ whiteSpace: 'pre-wrap', wordWrap: 'break-word' }}
/>
</Typography>
위와 같이 코드를 입력했을 때 아래와 같은 에러 발생
In HTML, ‹div> cannot be a descendant of ‹p>.
This will cause a hydration error.
Q1. 에러의 원인이 뭐야?
A1. 코드를 살펴보니 dangerouslySetInnerHTML을 사용하는 부분이 <p> 태그 내부에서 <div>를 렌더링하려고 시도하고 있습니다. HTML 명세에 따르면 <p> 태그는 블록 레벨 요소인 <div>를 자식으로 가질 수 없습니다.
이 문제를 해결하기 위한 가장 간단한 방법은 <p> 태그를 <div>로 변경하는 것입니다.
이제 hydration 에러가 해결될 것입니다. 변경사항을 설명하자면:
1. <Typography> 컴포넌트를 일반 <div>로 변경했습니다.
2. Material-UI의 sx prop 대신 일반 style 객체를 사용하여 margin을 설정했습니다.
3. dangerouslySetInnerHTML을 포함하는 <div>는 그대로 유지했습니다.
이렇게 수정하면 HTML 명세를 준수하면서도 원하는 스타일링과 기능을 유지할 수 있습니다. 블록 레벨 요소인 <div>는 다른 블록 레벨 요소를 자식으로 가질 수 있기 때문에 hydration 에러가 발생하지 않을 것입니다.
Q2. p태그 내부에 div와 같은 블록 레벨 요소를 포함하면 React에서 hydration 과정에 문제를 일으킨다는게 어떤 말인지 조금 더 자세히 설명해줘
A2.
1. HTML 명세의 제약사항
- HTML 명세에 따르면 <p> 태그는 "phrasing content"만 포함할 수 있습니다.
- Phrasing content는 텍스트나 <span>, <a>, <em> 같은 인라인 요소들을 의미합니다.
- <div>는 "flow content"로 분류되는 블록 레벨 요소이므로 <p> 태그 내부에 올 수 없습니다.
2. React Hydration 과정
- React의 hydration은 서버에서 렌더링된 HTML을 클라이언트에서 React 컴포넌트와 연결하는 과정입니다.
- 이 과정에서 React는 서버에서 생성된 HTML 구조와 클라이언트에서 생성하려는 HTML 구조가 정확히 일치해야 합니다.
3. 문제 발생 과정
// 문제가 되는 코드
<p>
<div dangerouslySetInnerHTML={{ __html: content }} />
</p>
// 브라우저의 HTML 파서가 자동으로 교정하는 결과
<p></p>
<div dangerouslySetInnerHTML={{ __html: content }}></div>
<p></p>
- 서버에서: React는 코드를 그대로 HTML로 변환하려 합니다.
- 브라우저에서: HTML 파서가 유효하지 않은 구조를 발견하고 자동으로 교정합니다.
- 결과: 서버의 HTML 구조와 클라이언트의 HTML 구조가 달라져 hydration 에러가 발생합니다.
4. 해결 방법
// 올바른 코드
<div>
<div dangerouslySetInnerHTML={{ __html: content }} />
</div>
- <p> 대신 <div>를 사용하면 HTML 명세를 준수하면서도 원하는 구조를 만들 수 있습니다.
- 이렇게 하면 서버와 클라이언트의 HTML 구조가 일치하므로 hydration 에러가 발생하지 않습니다.
이러한 이유로 React에서 dangerouslySetInnerHTML을 사용할 때는 특히 HTML 구조에 주의를 기울여야 합니다. 블록 레벨 요소를 포함할 가능성이 있는 HTML 콘텐츠를 주입할 때는 <div>와 같은 컨테이너 요소를 사용하는 것이 안전합니다.
[Next.js] App Router - Building Your Application (0) | 2023.11.16 |
---|---|
[Next.js] Getting Started - Project Structure (0) | 2023.11.15 |
[Next.js] Getting Started - Installation (0) | 2023.11.13 |
[Next.js] Getting Started - Introduction (0) | 2023.11.12 |
Vue.js v-html 사용하여 style 적용하는 방법 (0) | 2023.10.27 |