[nextjs 톺아보기]

서론

안녕하세요! 이번에 nextjs 톺아보기 스터디에 들어가게 되었는데요! 이번 주차 파트인 head를 정의부터 시작해서 react에서의 처리법, next의 처리법을 비교하며 nextjs의 head 동작원리에 다가가보겠습니다!

1. 왜 는 중요한가?

와 다르게 컨텐츠로는 표현되지 않습니다.(What is the HTML head?) 대신에 페이지의 metadata를 정의하는 공간입니다.

1.1 head가 중요한 이유

대표적으로 아래와 같습니다

  • SEO(검색 엔진 최적화): 검색 엔진이 HTML을 읽고 인덱싱할 때 참고하는 메타 정보
    검색 엔진들은 title, description, robots, canonical 등을 중요하게 반응합니다.

    <!-- 문서 제목 -->
    <title>프론트엔드 개발자를 위한 Head 완전정복</title>
    
    <!-- 페이지 설명 -->
    <meta name="description" content="Next.js와 HTML의 head 태그를 심층 분석하고 SEO 최적화 방법을 소개합니다." />
    
    <!-- 검색 엔진에 노출 허용 -->
    <meta name="robots" content="index, follow" />
    
    <!-- 작성자 정보 -->
    <meta name="author" content="박동현" />
    
    <!-- 사이트 정규 주소 (중복 방지) -->
    <link rel="canonical" href="https://yourdomain.com/head-guide" />
  • OG(Open Graph) / Social card: SNS공유 시 썸네일, 제목, 설명 등 미리보기 박스에 나타나는 정보
    대표 이미지가 없다면, 링크를 공유해도 썸네일이 나오지 않습니다.

    <!-- 페이스북, 카카오 등 -->
    <meta property="og:title" content="Head 태그 마스터 가이드" />
    <meta property="og:description" content="Next.js와 HTML의 head 태그를 활용해 SEO와 OG를 최적화하는 방법을 설명합니다." />
    <meta property="og:image" content="https://yourdomain.com/images/og-head-guide.png" />
    <meta property="og:url" content="https://yourdomain.com/head-guide" />
    <meta property="og:type" content="article" />
  • 리소스 관리: 브라우저가 리소스를 어떻게 받아올지 힌트를 줌

    <-- preload - 폰트 파일 preload (렌더링 차단 요소 미리 불러오기) -->
    
    <link rel="preload" href="/fonts/Pretendard.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
    
    <-- preconnect - 다른 도메인의 리소스를 미리 연결 (DNS+TLS 핸드셰이크 미리 함) - 외부 리소스 도메인 preconnect -->
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
    
    <-- dns-prefetch - DNS 조회만 먼저 해놓음 (preconnect보다 가벼움) -->
    <link rel="dns-prefetch" href="//fonts.googleapis.com" />

이외에도 보안 & 정책 메타, 다국어 등 다양한 정보를 <head>에 담아둘 수 있습니다.

이러한 강점들을 수치적으로 확인해볼수도 있습니다!

1.2 Lighthouse로 head의 강점 알아보기

meta-description을 확인해보면 head에 meta태그를 활용하여 seo를 높이는 방법을 설명해주고 있습니다.

The element provides a summary of a page's content that search engines include in search results. A high-quality, unique meta description makes your page appear more relevant and can increase your search traffic.


이와 관련하여 seo에 유리하게 작성하는 법을 알려주고 있으며, meta태그가 없을 시 다양한 문구를 노출합니다. 관련 코드

이런 경고등은 lighthouse의 SEO와 Best Practices, OG/social 에서 위 코드의 UIString에 해당하는 경고와 함께 이 페이지의 lighthouse 점수를 떨어트립니다.

이제 각 프레임워크에서 어떻게 대응을 하는지 알아보겠습니다.

2. react의 csr환경에서의 <head>

2.1 react

따로 서버를 세팅하지않고 CSR로 운영되는 react 프로젝트의 경우, 초기렌더링 시 index.html에 들어있는 메타데이터만 즉각 노출됩니다.

즉 모든페이지에 공통으로 쓸 metadata 또는 og등만 기입할 수 있고, 페이지별, 데이터별 meta데이터를 수정할 수 없습니다. 이를 해결하기위해 react-helmet같은 라이브러리는 렌더링시점에 <head>를 조작하여 페이지별로 메타 데이터를 넣어주곤 하는데, 이 방법 또한 크롤러나 sns봇등이 읽지못하여 seo등을 크게 향상시키지는 못합니다.

2.2 react-helmet 동작원리

<Helmet>
  <title>페이지 타이틀</title>
  <meta name="description" content="..." />
</Helmet>
  1. \<Helmet>을 리액트 트리에 넣습니다.
  2. react가 렌더링될때 helmet내부의 render()를 호출합니다.
// HOC로 helmet컴포넌트 정의
const Helmet = Component => class HelmetWrapper extends React.Component {
      ...

      render() {
        const {children, ...props} = this.props;
        let newProps = {...props};

        if (children) {
          newProps = this.mapChildrenToProps(children, newProps);
        }

       return <Component {...newProps} />;
      }
      ...
}

// 사이드 이펙트 수행할 컴포넌트 생성
const HelmetSideEffects = withSideEffect(
    reducePropsToState,
    handleClientStateChange,
    mapStateOnServer
)(NullComponent);

// 내보내기
const HelmetExport = Helmet(HelmetSideEffects);
  1. <Helmet>은 HelmetWrapper의 인스턴스를 생성한것이며, 렌더링 시 HelmetWrapper.render()를 호출하게 됩니다.
  2. 이때 <Component {...newProps} />;에 react에서 주입한 데이터가 들어간 후 사용가능한 데이터로 정제 후 HelmetSideEffects가 실행됩니다.
  3. handleClientStateChange의 commitTagChanges에 의해 태그가 만들어지고, 이를 reducePropsToState를 통해 조합하여 <head>데이터를 수정하게 됩니다. 상세코드

2.3 결론

결국 react-helmet을 써도 렌더링 시점에 head가 변경되어 ssr환경에서 html을 완성한 상태로 전해주는것에 비해 성능이 떨어집니다. 이는 ssr을 적용하면 해소가 되며, 다음은 이 아티클의 주제인 Nextjs에서 <head>를 다루는 방법을 소개하겠습니다.

3. Next.js에서의 <head>

앞서 CSR환경의 React에서의 <head>의 변경은 렌더링 시점에 조작되기 때문에 SEO나 OG에 불리했습니다. 이제 SSR을 지원하는 Next.js에서의 <head>를 알아보겠습니다.

Next.js는 기본적으로 SSR을 지원하므로, <head> 데이터를 서버에서 HTML에 포함시켜 전달할 수 있습니다. 이로인해 크롤러,SNS, 검색엔진 모두 제대로 읽을 수 있습니다.

3.1 Next.js에서의 <head> 구성 방법

App router기준 Next.js에서 <head>를 구성하는 방식은 크게 두 가지로 나뉩니다. 사용 목적에 알맞게 나누어 사용하는것이 좋습니다.

구성 방식 설명 용도
metadata API SEO, title, description, OG, Twitter, favicon 등을 선언형으로 구성 페이지 정보 메타 구성
head.tsx 파일 외부 폰트, 스크립트, SDK 삽입 등 <script>,<link> 구성 리소스 삽입, 커스텀 제어 필요

📎 공식 문서: Metadata API
📎 공식 문서: head.tsx 파일 구성

우선 metadata API 부터 둘러보겠습니다.

3.2 metadata API

metadata는 <script><link>를 제외한 모든 메타 정보를 선언형으로 정의할 수 있는 API입니다.

page나 layout에 예약된 변수명 metadata나 generateMetadata를 내보내면 SSR시 next.js가 자동으로 파싱하여 에 삽입해줍니다.

클라이언트 요청
   ↓
NextServer.getRequestHandler()
   ↓
renderToHTMLOrFlight (app-render.tsx)
   ↓
createComponentTree → getMetadataComponents()
   ↓
resolveMetadata()
   ↓
resolveMetadataElements()
   ↓
MetadataTree (JSX) → <head> 렌더링 시 삽입

핵심 소스 파일

3.3 head.tsx

meta api는 seo/og중심의 정보에 최적화되어있어 외부 리소스 삽입에는 적합하지 않습니다. 이는 head.tsx를 사용하면 됩니다.

export default function Head() {
  return (
    <>
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link
        href="https://fonts.googleapis.com/css2?family=Inter&display=swap"
        rel="stylesheet"
      />
    </>
  )
}

3.4 결론

next는 metadata api와 head를 분리하여, 정적 메타 데이터와 동적 메타데이터, 외부 리소스 등을 명확히 구분할 수 있도록 설계하였으며, 성능 및 유지보수 측면에서 용이합니다.

'개발' 카테고리의 다른 글

Vue Ref 톺아보기  (0) 2025.03.26
빌드시스템은 왜 필요할까?  (0) 2025.03.26
번들사이즈 최적화  (0) 2024.09.30
자바스크립트 이벤트 루프와 비동기 통신의 실행순서  (2) 2024.09.06

+ Recent posts

  1. 서론
  2. 1. 왜 는 중요한가?
  3. 1.1 head가 중요한 이유
  4. 1.2 Lighthouse로 head의 강점 알아보기
  5. 2. react의 csr환경에서의 <head>
  6. 2.1 react
  7. 2.2 react-helmet 동작원리
  8. 2.3 결론
  9. 3. Next.js에서의 <head>
  10. 3.1 Next.js에서의 <head> 구성 방법
  11. 3.2 metadata API
  12. 3.3 head.tsx
  13. 3.4 결론