← 전체 글로 돌아가기

Next.js

Next.js에서 한글 slug를 쓸 때 URL 복사가 깨지는 이유

한글 slug는 브라우저 주소창에서는 읽기 편하지만, 복사하면 퍼센트 인코딩된 긴 문자열이 된다. canonical URL도 같은 문제가 생긴다.

한글 slug를 쓰면 URL이 /posts/타입스크립트-입문 처럼 나와서 읽기 편하다. 그런데 이 URL을 복사해서 다른 곳에 붙이면 /posts/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8 같은 형태가 된다.

브라우저는 주소창에서 한글을 디코딩해서 보여주지만, 클립보드에는 인코딩된 값이 복사된다.

어디서 문제가 되는가

링크 공유: SNS나 메신저에 붙이면 긴 인코딩 문자열이 노출된다. 일부 플랫폼은 이를 URL로 인식하지 못하기도 한다.

canonical URL: <link rel="canonical"> 태그에 인코딩되지 않은 한글이 그대로 들어가면 검색엔진이 올바르게 처리하지 못할 수 있다.

RSS: RSS 피드의 <link> 요소에 한글이 들어가면 일부 리더에서 파싱 에러가 난다.

curl -s https://example.com/posts/타입스크립트-입문 | grep canonical

실제로 어떤 값이 출력되는지 확인한다.

Next.js에서 처리하는 방법

generateMetadata에서 canonical URL을 만들 때 encodeURIComponent를 써서 slug를 인코딩한다.

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = params;
  const encodedSlug = encodeURIComponent(slug);

  return {
    alternates: {
      canonical: `https://example.com/posts/${encodedSlug}`,
    },
  };
}

encodeURIComponent는 한글을 퍼센트 인코딩으로 바꾸지만, /는 건드리지 않는다.

slug 자체를 영문으로 쓰는 방법

근본적인 해결책은 slug를 처음부터 영문으로 만드는 것이다. 글 제목에서 slug를 자동 생성할 때 한글을 로마자로 변환하거나(한글 → hangeul), 영문 slug를 따로 입력하도록 한다.

import slugify from 'slugify';

// 한글 제목은 직접 영문 slug를 지정
const slug = 'typescript-intro'; // '타입스크립트-입문' 대신

영문 slug를 쓰면 복사 문제, canonical 문제, RSS 문제가 한 번에 해결된다. 단, 이미 한글 slug로 공개된 글이 있다면 redirect 처리가 필요하다.

// next.config.js
redirects: async () => [
  {
    source: '/posts/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%85%EB%AC%B8',
    destination: '/posts/typescript-intro',
    permanent: true,
  },
],

한글 URL이 보기 좋다는 이유만으로 선택하면 이런 복잡함이 뒤따른다는 걸 미리 알고 결정하는 게 낫다.