{
    "componentChunkName": "component---node-modules-gatsby-theme-primer-wiki-src-templates-post-query-js",
    "path": "/posts/seo/hreflang-canonical/",
    "result": {"data":{"mdx":{"id":"d21b636f-ecdb-533c-b8a5-6ee9b3e7c8b8","tableOfContents":{"items":[{"url":"#canonical은-자기-자신을-가리킨다","title":"canonical은 자기 자신을 가리킨다"},{"url":"#hreflang-필수-규칙","title":"hreflang 필수 규칙"},{"url":"#canonical과-hreflang의-관계","title":"canonical과 hreflang의 관계"},{"url":"#정규-url에-포함할-것과-제외할-것","title":"정규 URL에 포함할 것과 제외할 것"},{"url":"#url과-언어-코드는-단일-출처에서-파생시킨다","title":"URL과 언어 코드는 단일 출처에서 파생시킨다"},{"url":"#자동-언어-리다이렉트의-색인-함정","title":"자동 언어 리다이렉트의 색인 함정"},{"url":"#검증","title":"검증"},{"url":"#참고","title":"참고"}]},"fields":{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","slug":"/posts/seo/hreflang-canonical/","url":"https://r0k.wiki/posts/seo/hreflang-canonical/","editUrl":"https://github.com/padawanR0k/wiki/blob/master/content/posts/seo/hreflang-canonical.md","lastUpdatedAt":"2026-06-30T12:28:37.000Z","lastUpdated":"6/30/2026","gitCreatedAt":"2026-06-30T12:28:37.000Z","shouldShowTitle":true},"frontmatter":{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","description":null,"imageAlt":null,"tags":["SEO","i18n"],"date":"2026-06-30T00:00:00.000Z","dateModified":null,"language":null,"seoTitle":null,"image":null},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical\",\n  \"date\": \"2026-06-30T00:00:00.000Z\",\n  \"updated\": \"2026-06-30T00:00:00.000Z\",\n  \"tags\": [\"SEO\", \"i18n\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"\\uD55C \\uD398\\uC774\\uC9C0\\uB97C \\uC5EC\\uB7EC \\uC5B8\\uC5B4\\uB85C \\uC11C\\uBE44\\uC2A4\\uD560 \\uB54C, \\uAC80\\uC0C9\\uC5D4\\uC9C4\\uC5D0 \\\"\\uAC19\\uC740 \\uCF58\\uD150\\uCE20\\uC758 \\uB2E4\\uB978 \\uC5B8\\uC5B4 \\uBC84\\uC804\\\"\\uC774\\uB77C\\uB294 \\uC124\\uC815\\uC744 \\uC81C\\uB300\\uB85C \\uD558\\uC9C0 \\uC54A\\uC73C\\uBA74 \\uBC88\\uC5ED\\uBCF8\\uC774 \\uAC80\\uC0C9\\uC5D4\\uC9C4 \\uC0C9\\uC778\\uC5D0\\uC11C \\uC798\\uBABB\\uB420 \\uC218 \\uC788\\uB2E4.\"), mdx(\"h2\", {\n    \"id\": \"canonical은-자기-자신을-가리킨다\"\n  }, \"canonical\\uC740 \\uC790\\uAE30 \\uC790\\uC2E0\\uC744 \\uAC00\\uB9AC\\uD0A8\\uB2E4\"), mdx(\"p\", null, \"\\uB2E4\\uAD6D\\uC5B4 \\uD398\\uC774\\uC9C0\\uC758 \\uC815\\uADDC URL\\uC740 \\uAC01 \\uC5B8\\uC5B4 \\uD398\\uC774\\uC9C0\\uAC00 \\uC790\\uAE30 \\uC790\\uC2E0\\uC744 \\uAC00\\uB9AC\\uD0A4\\uB294 \\uAC83\\uC774 \\uC815\\uC11D\\uC774\\uB2E4. ko/en/ja\\uB97C \\uD558\\uB098\\uC758 \\uB300\\uD45C URL\\uB85C \\uD1B5\\uD569\\uD558\\uBA74 \\uADF8 \\uD55C \\uC5B8\\uC5B4\\uB9CC \\uC0C9\\uC778\\uB418\\uACE0 \\uB098\\uBA38\\uC9C0 \\uBC88\\uC5ED\\uBCF8\\uC740 \\uAC80\\uC0C9 \\uACB0\\uACFC\\uC5D0 \\uB178\\uCD9C\\uB418\\uC9C0 \\uC54A\\uB294\\uB2E4.\"), mdx(\"p\", null, \"\\uC790\\uAE30 \\uCC38\\uC870\\uB9CC\\uC73C\\uB85C\\uB294 \\uBD80\\uC871\\uD558\\uB2E4. \\\"\\uC774 \\uC14B\\uC740 \\uAC19\\uC740 \\uCF58\\uD150\\uCE20\\uC758 \\uBC88\\uC5ED\\uACB0\\uACFC\\\"\\uB77C\\uB294 \\uC2E0\\uD638\\uB97C hreflang\\uC73C\\uB85C \\uB530\\uB85C \\uC918\\uC57C \\uD55C\\uB2E4.\"), mdx(\"h2\", {\n    \"id\": \"hreflang-필수-규칙\"\n  }, \"hreflang \\uD544\\uC218 \\uADDC\\uCE59\"), mdx(\"p\", null, \"\\uACF5\\uC2DD \\uBB38\\uC11C \\uAE30\\uC900\\uC73C\\uB85C \\uB2E4\\uC74C \\uB124 \\uAC00\\uC9C0\\uAC00 \\uBAA8\\uB450 \\uC9C0\\uCF1C\\uC838\\uC57C \\uD0DC\\uADF8\\uAC00 \\uC720\\uD6A8\\uD558\\uB2E4.\"), mdx(\"table\", null, mdx(\"thead\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"thead\"\n  }, mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uADDC\\uCE59\"), mdx(\"th\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uC124\\uBA85\"))), mdx(\"tbody\", {\n    parentName: \"table\"\n  }, mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uC790\\uAE30 \\uC790\\uC2E0 \\uD3EC\\uD568, \\uBAA8\\uB4E0 \\uC5B8\\uC5B4 \\uB098\\uC5F4\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uAC01 \\uD398\\uC774\\uC9C0\\uC5D0 \\uC790\\uAE30 \\uC790\\uC2E0\\uC744 \\uD3EC\\uD568\\uD55C \\uC804\\uCCB4 \\uC5B8\\uC5B4 \\uBC84\\uC804 \\uB9C1\\uD06C \\uC138\\uD2B8\\uAC00 \\uBC15\\uD600 \\uC788\\uC5B4\\uC57C \\uD55C\\uB2E4\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uC0C1\\uD638 \\uCC38\\uC870 \\uB300\\uCE6D\\uC131\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"X\\uAC00 Y\\uB97C \\uAC00\\uB9AC\\uD0A4\\uBA74 Y\\uB3C4 X\\uB97C \\uB418\\uC9DA\\uC5B4 \\uAC00\\uB9AC\\uCF1C\\uC57C \\uD55C\\uB2E4. \\uD55C\\uCABD\\uC774\\uB77C\\uB3C4 \\uAE68\\uC9C0\\uBA74 Google\\uC774 hreflang \\uD0DC\\uADF8 \\uC790\\uCCB4\\uB97C \\uBB34\\uC2DC\\uD55C\\uB2E4\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uC808\\uB300 URL\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, mdx(\"inlineCode\", {\n    parentName: \"td\"\n  }, \"https://\"), \"\\uBD80\\uD130 \\uD3EC\\uD568\\uD55C \\uC808\\uB300 \\uACBD\\uB85C. \\uC0C1\\uB300 \\uACBD\\uB85C\\uB294 \\uBD88\\uD5C8\")), mdx(\"tr\", {\n    parentName: \"tbody\"\n  }, mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"x-default\"), mdx(\"td\", {\n    parentName: \"tr\",\n    \"align\": null\n  }, \"\\uC5B4\\uB5A4 \\uC5B8\\uC5B4\\uC5D0\\uB3C4 \\uB9E4\\uCE6D\\uB418\\uC9C0 \\uC54A\\uB294 \\uC0AC\\uC6A9\\uC790\\uAC00 \\uBCF4\\uAC8C \\uB420 \\uAE30\\uBCF8 \\uD398\\uC774\\uC9C0. \\uBCF4\\uD1B5 \\uC8FC\\uB825 \\uC2DC\\uC7A5 \\uC5B8\\uC5B4 \\uB610\\uB294 \\uC5B8\\uC5B4 \\uC120\\uD0DD \\uD398\\uC774\\uC9C0\\uB85C \\uC9C0\\uC815\")))), mdx(\"h2\", {\n    \"id\": \"canonical과-hreflang의-관계\"\n  }, \"canonical\\uACFC hreflang\\uC758 \\uAD00\\uACC4\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Google\\uC740 hreflang\\uC73C\\uB85C \\uBB36\\uC778 URL \\uC9D1\\uD569\\uC5D0 \\uC18D\\uD55C \\uD398\\uC774\\uC9C0\\uB97C \\uC815\\uADDC URL\\uB85C \\uC120\\uD638\\uD55C\\uB2E4.\"), \" hreflang\\uC744 \\uC81C\\uB300\\uB85C \\uC124\\uC815\\uD574\\uC57C \\uC5B8\\uC5B4 \\uB9E4\\uCE6D \\uC2E0\\uD638\\uBFD0 \\uC544\\uB2C8\\uB77C \\uC815\\uADDC URL \\uC120\\uC815\\uC5D0\\uB3C4 \\uAC00\\uC0B0\\uC810\\uC774 \\uBD99\\uB294\\uB2E4.\"), mdx(\"h2\", {\n    \"id\": \"정규-url에-포함할-것과-제외할-것\"\n  }, \"\\uC815\\uADDC URL\\uC5D0 \\uD3EC\\uD568\\uD560 \\uAC83\\uACFC \\uC81C\\uC678\\uD560 \\uAC83\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\uCD94\\uC801\\uC6A9 \\uD30C\\uB77C\\uBBF8\\uD130(\", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"gclid\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"utm_*\"), \")\"), \": \\uC815\\uADDC URL\\uC5D0\\uC11C \\uC81C\\uC678\\uD55C\\uB2E4. \\uACF5\\uC2DD \\uBB38\\uC11C\\uAC00 \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"gclid\"), \"\\uB97C \\uC608\\uC2DC\\uB85C \\uBA85\\uC2DC.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\uC2DD\\uBCC4\\uC790 \\uD30C\\uB77C\\uBBF8\\uD130(\", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"?exampleId=1\"), \")\"), \": \\uB9AC\\uC18C\\uC2A4\\uB2F9 1\\uAC1C\\uB85C \\uACE0\\uC815\\uC774\\uBA74 \\uCD94\\uC801\\uC6A9\\uBCF4\\uB2E4 \\uB35C \\uC704\\uD5D8\\uD558\\uC9C0\\uB9CC, \\uCFFC\\uB9AC\\uC2A4\\uD2B8\\uB9C1 \\uD615\\uD0DC\\uB294 \\uACBD\\uB85C\\uC5D0 \\uD3EC\\uD568\\uB41C \\uC2DD\\uBCC4\\uC790\\uBCF4\\uB2E4 \\uBE44\\uCD94\\uCC9C.\")), mdx(\"h2\", {\n    \"id\": \"url과-언어-코드는-단일-출처에서-파생시킨다\"\n  }, \"URL\\uACFC \\uC5B8\\uC5B4 \\uCF54\\uB4DC\\uB294 \\uB2E8\\uC77C \\uCD9C\\uCC98\\uC5D0\\uC11C \\uD30C\\uC0DD\\uC2DC\\uD0A8\\uB2E4\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"og:locale\"), \"\\uC740 \\uD398\\uC774\\uC9C0\\uBCC4\\uB85C \\uBD84\\uAE30\\uB418\\uB294\\uB370 \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"product:locale\"), \"\\uC740 \\uD55C \\uAC12\\uC73C\\uB85C \\uACE0\\uC815\\uB3FC \\uC788\\uB2E4\\uBA74, \\uB450 \\uAC12\\uC774 \\uC11C\\uB85C \\uB2E4\\uB978 \\uACF3\\uC5D0\\uC11C \\uC0DD\\uC131\\uB418\\uACE0 \\uC788\\uB2E4\\uB294 \\uC2E0\\uD638\\uB2E4. \\uBA54\\uD0C0\\uB370\\uC774\\uD130 \\uBE4C\\uB354\\uAC00 \\uD1B5\\uD569\\uB418\\uC9C0 \\uC54A\\uC73C\\uBA74 \\uC0C8 \\uC5B8\\uC5B4 \\uCD94\\uAC00 \\uC2DC \\uD55C\\uCABD\\uB9CC \\uC5C5\\uB370\\uC774\\uD2B8\\uB418\\uB294 \\uC0AC\\uACE0\\uAC00 \\uBC18\\uBCF5\\uB41C\\uB2E4.\"), mdx(\"p\", null, \"URL \\uC0DD\\uC131\\uAE30\\uC640 \\uC5B8\\uC5B4 \\uCF54\\uB4DC \\uB9E4\\uD551\\uC744 \\uB2E8\\uC77C \\uBE4C\\uB354\\uC5D0\\uC11C \\uD30C\\uC0DD\\uC2DC\\uD0A8\\uB2E4. Next.js App Router\\uC5D0\\uC11C\\uB294 \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"generateMetadata\"), \"\\uC758 \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"alternates.canonical\"), \"\\uACFC \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"alternates.languages\"), \"\\uB85C \\uD55C \\uBC88\\uC5D0 \\uCC98\\uB9AC\\uD560 \\uC218 \\uC788\\uB2E4.\"), mdx(\"h2\", {\n    \"id\": \"자동-언어-리다이렉트의-색인-함정\"\n  }, \"\\uC790\\uB3D9 \\uC5B8\\uC5B4 \\uB9AC\\uB2E4\\uC774\\uB809\\uD2B8\\uC758 \\uC0C9\\uC778 \\uD568\\uC815\"), mdx(\"p\", null, \"\\uACF5\\uC2DD \\uBB38\\uC11C\\uB294 \\\"\\uD55C \\uC5B8\\uC5B4 \\uBC84\\uC804\\uC5D0\\uC11C \\uB2E4\\uB978 \\uC5B8\\uC5B4 \\uBC84\\uC804\\uC73C\\uB85C \\uC0AC\\uC6A9\\uC790\\uB97C \\uC790\\uB3D9 \\uB9AC\\uB2E4\\uC774\\uB809\\uD2B8\\uD558\\uC9C0 \\uB9D0\\uB77C\\\"\\uACE0 \\uAD8C\\uACE0\\uD55C\\uB2E4. IP \\uAE30\\uBC18 \\uAC15\\uC81C \\uB9AC\\uB2E4\\uC774\\uB809\\uD2B8\\uC758 \\uAC00\\uC7A5 \\uD070 \\uC704\\uD5D8\\uC740 Googlebot\\uC5D0\\uAC8C\\uB3C4 \\uC801\\uC6A9\\uB41C\\uB2E4\\uB294 \\uC810\\uC774\\uB2E4.\"), mdx(\"p\", null, \"Googlebot\\uC740 \\uC8FC\\uB85C \\uBBF8\\uAD6D\\xB7\\uC720\\uB7FD\\uC5D0\\uC11C \\uD06C\\uB864\\uB9C1\\uD55C\\uB2E4. \\uC678\\uAD6D IP\\uB97C \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/en/\"), \"\\uC73C\\uB85C \\uAC15\\uC81C\\uB85C \\uBCF4\\uB0B4\\uBA74 Googlebot\\uC774 \\uD55C\\uAD6D\\uC5B4 \\uD398\\uC774\\uC9C0\\uB97C \\uC601\\uAD6C\\uD788 \\uBCF4\\uC9C0 \\uBABB\\uD558\\uB294 \\uC0C1\\uD669\\uC774 \\uC0DD\\uAE34\\uB2E4. \\uC5B8\\uC5B4 \\uC120\\uD0DD\\uC740 \\uB9AC\\uB2E4\\uC774\\uB809\\uD2B8\\uAC00 \\uC544\\uB2C8\\uB77C \\uC0AC\\uC6A9\\uC790 \\uC120\\uD0DD(\\uBC30\\uB108 \\uC548\\uB0B4 \\uB4F1)\\uC73C\\uB85C \\uCC98\\uB9AC\\uD55C\\uB2E4.\"), mdx(\"h2\", {\n    \"id\": \"검증\"\n  }, \"\\uAC80\\uC99D\"), mdx(\"p\", null, \"\\uBE0C\\uB77C\\uC6B0\\uC800 \\uAC1C\\uBC1C\\uC790\\uB3C4\\uAD6C\\uB294 CDN \\uCE90\\uC2DC\\uB098 JS \\uB3D9\\uC801 \\uC8FC\\uC785\\uC758 \\uC601\\uD5A5\\uC744 \\uBC1B\\uB294\\uB2E4. \\uC5B8\\uC5B4 \\uCFE0\\uD0A4\\uB97C \\uBC14\\uAFD4\\uAC00\\uBA70 curl\\uB85C \\uBA54\\uD0C0\\uB370\\uC774\\uD130\\uB97C \\uC9C1\\uC811 \\uC870\\uD68C\\uD558\\uB294 \\uAC83\\uC774 \\uAC00\\uC7A5 \\uC2E0\\uB8B0\\uC131\\uC774 \\uB192\\uB2E4.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"curl -s -L -b \\\"lang=ko\\\" \\\"URL\\\" | grep -oE '<link[^>]*rel=\\\"(canonical|alternate)\\\"[^>]*>'\\n\")), mdx(\"h2\", {\n    \"id\": \"참고\"\n  }, \"\\uCC38\\uACE0\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://developers.google.com/search/docs/specialty/international/localized-versions\"\n  }, \"Localized versions of your pages\"), \" \\u2014 hreflang \\uADDC\\uCE59, x-default, \\uD754\\uD55C \\uC2E4\\uC218\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls\"\n  }, \"How to specify a canonical URL\"), \" \\u2014 \\uC815\\uADDC URL \\uBC29\\uBC95\\uB860, hreflang \\uC2DC\\uB108\\uC9C0, gclid \\uC608\\uC2DC\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites\"\n  }, \"Managing multi-regional sites\"), \" \\u2014 \\uC790\\uB3D9 \\uB9AC\\uB2E4\\uC774\\uB809\\uD2B8 \\uBE44\\uAD8C\\uC7A5\")));\n}\n;\nMDXContent.isMDXComponent = true;","rawBody":"---\ntitle: 다국어 페이지 작업시 주의해야할 것 - hreflang / canonical\ndate: 2026-06-30\nupdated: 2026-06-30\ntags:\n  - SEO\n  - i18n\n---\n\n한 페이지를 여러 언어로 서비스할 때, 검색엔진에 \"같은 콘텐츠의 다른 언어 버전\"이라는 설정을 제대로 하지 않으면 번역본이 검색엔진 색인에서 잘못될 수 있다.\n\n## canonical은 자기 자신을 가리킨다\n\n다국어 페이지의 정규 URL은 각 언어 페이지가 자기 자신을 가리키는 것이 정석이다. ko/en/ja를 하나의 대표 URL로 통합하면 그 한 언어만 색인되고 나머지 번역본은 검색 결과에 노출되지 않는다.\n\n자기 참조만으로는 부족하다. \"이 셋은 같은 콘텐츠의 번역결과\"라는 신호를 hreflang으로 따로 줘야 한다.\n\n## hreflang 필수 규칙\n\n공식 문서 기준으로 다음 네 가지가 모두 지켜져야 태그가 유효하다.\n\n| 규칙                           | 설명                                                                                                       |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------- |\n| 자기 자신 포함, 모든 언어 나열 | 각 페이지에 자기 자신을 포함한 전체 언어 버전 링크 세트가 박혀 있어야 한다                                 |\n| 상호 참조 대칭성               | X가 Y를 가리키면 Y도 X를 되짚어 가리켜야 한다. 한쪽이라도 깨지면 Google이 hreflang 태그 자체를 무시한다    |\n| 절대 URL                       | `https://`부터 포함한 절대 경로. 상대 경로는 불허                                                          |\n| x-default                      | 어떤 언어에도 매칭되지 않는 사용자가 보게 될 기본 페이지. 보통 주력 시장 언어 또는 언어 선택 페이지로 지정 |\n\n## canonical과 hreflang의 관계\n\n**Google은 hreflang으로 묶인 URL 집합에 속한 페이지를 정규 URL로 선호한다.** hreflang을 제대로 설정해야 언어 매칭 신호뿐 아니라 정규 URL 선정에도 가산점이 붙는다.\n\n## 정규 URL에 포함할 것과 제외할 것\n\n- **추적용 파라미터(`gclid`, `utm_*`)**: 정규 URL에서 제외한다. 공식 문서가 `gclid`를 예시로 명시.\n- **식별자 파라미터(`?exampleId=1`)**: 리소스당 1개로 고정이면 추적용보다 덜 위험하지만, 쿼리스트링 형태는 경로에 포함된 식별자보다 비추천.\n\n## URL과 언어 코드는 단일 출처에서 파생시킨다\n\n`og:locale`은 페이지별로 분기되는데 `product:locale`은 한 값으로 고정돼 있다면, 두 값이 서로 다른 곳에서 생성되고 있다는 신호다. 메타데이터 빌더가 통합되지 않으면 새 언어 추가 시 한쪽만 업데이트되는 사고가 반복된다.\n\nURL 생성기와 언어 코드 매핑을 단일 빌더에서 파생시킨다. Next.js App Router에서는 `generateMetadata`의 `alternates.canonical`과 `alternates.languages`로 한 번에 처리할 수 있다.\n\n## 자동 언어 리다이렉트의 색인 함정\n\n공식 문서는 \"한 언어 버전에서 다른 언어 버전으로 사용자를 자동 리다이렉트하지 말라\"고 권고한다. IP 기반 강제 리다이렉트의 가장 큰 위험은 Googlebot에게도 적용된다는 점이다.\n\nGooglebot은 주로 미국·유럽에서 크롤링한다. 외국 IP를 `/en/`으로 강제로 보내면 Googlebot이 한국어 페이지를 영구히 보지 못하는 상황이 생긴다. 언어 선택은 리다이렉트가 아니라 사용자 선택(배너 안내 등)으로 처리한다.\n\n## 검증\n\n브라우저 개발자도구는 CDN 캐시나 JS 동적 주입의 영향을 받는다. 언어 쿠키를 바꿔가며 curl로 메타데이터를 직접 조회하는 것이 가장 신뢰성이 높다.\n\n```bash\ncurl -s -L -b \"lang=ko\" \"URL\" | grep -oE '<link[^>]*rel=\"(canonical|alternate)\"[^>]*>'\n```\n\n## 참고\n\n- [Localized versions of your pages](https://developers.google.com/search/docs/specialty/international/localized-versions) — hreflang 규칙, x-default, 흔한 실수\n- [How to specify a canonical URL](https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls) — 정규 URL 방법론, hreflang 시너지, gclid 예시\n- [Managing multi-regional sites](https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites) — 자동 리다이렉트 비권장\n","excerpt":"한 페이지를 여러 언어로 서비스할 때, 검색엔진에 \"같은 콘텐츠의 다른 언어 버전\"이라는 설정을 제대로 하지 않으면 번역본이 검색엔진 색인에서 잘못될 수 있다. canonical은 자기 자신을 가리킨다 다국어 페이지의 정규 URL은 각 언어 페이지가…","outboundReferences":[],"inboundReferences":[]},"tagsOutbound":{"nodes":[{"frontmatter":{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","tags":["SEO","i18n"]},"fields":{"slug":"/posts/seo/hreflang-canonical/","title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","lastUpdated":"6/30/2026","lastUpdatedAt":"2026-06-30T12:28:37.000Z","gitCreatedAt":"2026-06-30T12:28:37.000Z"}},{"frontmatter":{"title":"next.js SEO 개선하기","tags":["SEO"]},"fields":{"slug":"/posts/seo/nextjs dynamic sitemap/","title":"next.js SEO 개선하기","lastUpdated":"9/16/2023","lastUpdatedAt":"2023-09-16T09:29:27.000Z","gitCreatedAt":"2022-11-13T14:57:43.000Z"}},{"frontmatter":{"title":"SEO 클로킹과 콘텐츠 게이팅","tags":["SEO"]},"fields":{"slug":"/posts/seo/seo-cloaking-and-content-gating/","title":"SEO 클로킹과 콘텐츠 게이팅","lastUpdated":"4/18/2026","lastUpdatedAt":"2026-04-18T07:10:38.000Z","gitCreatedAt":"2026-04-18T07:10:38.000Z"}}]}},"pageContext":{"tags":["SEO","i18n"],"slug":"/posts/seo/hreflang-canonical/","sidebarItems":[{"title":"","items":[{"title":"Recently Updated","url":"/latest/","collapse":true,"indent":false,"items":[{"title":"06-30: 다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","url":"/posts/seo/hreflang-canonical/"},{"title":"05-24: dynamic-range-limit - HDR 이미지/비디오 밝기 제한 CSS 속성","url":"/posts/web/dynamic-range-limit/"},{"title":"04-18: SEO 클로킹과 콘텐츠 게이팅","url":"/posts/seo/seo-cloaking-and-content-gating/"},{"title":"03-15: useTransition과 Suspense의 내부 상호작용","url":"/posts/frontend/use-transition-suspense/"},{"title":"03-15: 개발 생산성을 위한 내 개발 환경 세팅 기록","url":"/posts/my-setting/"},{"title":"01-29: 내가 써먹으려고 정리한 단축키 & 명령어 모음","url":"/posts/tip/shortcut/"},{"title":"01-13: 키보드 안쓰기","url":"/posts/tip/키보드 안쓰기/"},{"title":"10-25: git 관련 팁 정리","url":"/posts/tip/git/"},{"title":"08-17: 프론트엔드 모니터링 - datadog RUM 알아보기","url":"/posts/monitoring/datadog RUM/"},{"title":"08-16: 프론트엔드 성능 최적화 팁 기록","url":"/posts/frontend/performance-tip/"}]}]},{"title":"Tags","items":[{"title":"Readme","type":"tag","url":"/tags/readme/","items":[{"title":"📝 r0k Wiki","url":"/"}]},{"title":"SEO","type":"tag","url":"/tags/seo/","items":[{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","url":"/posts/seo/hreflang-canonical/"},{"title":"SEO 클로킹과 콘텐츠 게이팅","url":"/posts/seo/seo-cloaking-and-content-gating/"}]},{"title":"Sentry","type":"tag","url":"/tags/sentry/","items":[{"title":"Sentry","url":"/posts/monitoring/sentry/"}]},{"title":"Web API","type":"tag","url":"/tags/web-api/","items":[{"title":"Blob","url":"/Web_API/Blob/"},{"title":"DOMMatrixReadOnly","url":"/Web_API/DOMMatrixReadOnly/"}]},{"title":"cloudflare","type":"tag","url":"/tags/cloudflare/","items":[{"title":"github 블로그에 도메인 연결하기 by cloudflare","url":"/posts/cloud/cloudflare/my-dns/"}]},{"title":"css","type":"tag","url":"/tags/css/","items":[{"title":"dynamic-range-limit - HDR 이미지/비디오 밝기 제한 CSS 속성","url":"/posts/web/dynamic-range-limit/"}]},{"title":"git","type":"tag","url":"/tags/git/","items":[{"title":"git 딥다이브 - git branch, tag, HEAD","url":"/posts/study/codesoom-git/git-deep-dive-01/"},{"title":"git 딥다이브 - history","url":"/posts/study/codesoom-git/git-deep-dive-03/"},{"title":"git 딥다이브 - git merge, rebase","url":"/posts/study/codesoom-git/git-deep-dive-02/"}]},{"title":"i18n","type":"tag","url":"/tags/i-18-n/","items":[{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","url":"/posts/seo/hreflang-canonical/"}]},{"title":"javascript","type":"tag","url":"/tags/javascript/","items":[{"title":"useTransition과 Suspense의 내부 상호작용","url":"/posts/frontend/use-transition-suspense/"},{"title":"프론트엔드 성능 최적화 팁 기록","url":"/posts/frontend/performance-tip/"},{"title":"callback부터 async await까지 - Javascript 비동기","url":"/posts/javascript/asynchronous/"},{"title":"웹팩을 쓰는 이유와 사용법 정리","url":"/posts/javascript/webpack/"},{"title":"자바스크립트 데드 코드 제거","url":"/posts/javascript/자바스크립트 데드 코드 제거/"},{"title":"웹에서\b영상 재생을 하기 위한 주변 지식","url":"/posts/web/video player/"}]},{"title":"my-setting","type":"tag","url":"/tags/my-setting/","items":[{"title":"개발 생산성을 위한 내 개발 환경 세팅 기록","url":"/posts/my-setting/"}]},{"title":"react","type":"tag","url":"/tags/react/","items":[{"title":"useTransition과 Suspense의 내부 상호작용","url":"/posts/frontend/use-transition-suspense/"}]},{"title":"react-query","type":"tag","url":"/tags/react-query/","items":[{"title":"react-query 동작 안하는 이슈 해결","url":"/posts/library/react-query/fail-request/"}]},{"title":"tip","type":"tag","url":"/tags/tip/","items":[{"title":"webstorm 사용 팁","url":"/posts/tip/webstorm/"},{"title":"나만의 업무 습관 정리","url":"/posts/tip/나만의 업무 습관 정리/"},{"title":"내가 써먹으려고 정리한 단축키 & 명령어 모음","url":"/posts/tip/shortcut/"},{"title":"git 관련 팁 정리","url":"/posts/tip/git/"},{"title":"키보드 안쓰기","url":"/posts/tip/키보드 안쓰기/"}]},{"title":"webstorm","type":"tag","url":"/tags/webstorm/","items":[{"title":"webstorm 사용 팁","url":"/posts/tip/webstorm/"},{"title":"git 관련 팁 정리","url":"/posts/tip/git/"}]},{"title":"개선","type":"tag","url":"/tags/개선/","items":[{"title":"eslint에서 biome로","url":"/posts/library/biome/"},{"title":"자바스크립트 데드 코드 제거","url":"/posts/javascript/자바스크립트 데드 코드 제거/"},{"title":"레거시를 더 나은 환경으로 만들기","url":"/posts/tip/레거시를 더 나은 환경으로 만들기/"}]},{"title":"단축키","type":"tag","url":"/tags/단축키/","items":[{"title":"내가 써먹으려고 정리한 단축키 & 명령어 모음","url":"/posts/tip/shortcut/"}]},{"title":"디깅","type":"tag","url":"/tags/디깅/","items":[{"title":"모노레포에서 서로 다른 React 버전 사용시 에러","url":"/posts/error/모노레포에서 서로 다른 react 버전 사용시 에러/"}]},{"title":"모니터링","type":"tag","url":"/tags/모니터링/","items":[{"title":"프론트엔드 모니터링 - datadog RUM 알아보기","url":"/posts/monitoring/datadog RUM/"}]},{"title":"성능최적화","type":"tag","url":"/tags/성능최적화/","items":[{"title":"프론트엔드 성능 최적화 팁 기록","url":"/posts/frontend/performance-tip/"}]},{"title":"오브젝트","type":"tag","url":"/tags/오브젝트/","items":[{"title":"오트젝트 02 객체지향 프로그래밍","url":"/posts/books/object/오브젝트-02/"},{"title":"오트젝트 01 객체, 설계","url":"/posts/books/object/오브젝트-01/"},{"title":"오트젝트 03 역할, 책임, 협력","url":"/posts/books/object/오브젝트-03/"},{"title":"오트젝트 04 설계 품질과 트레이드 오프","url":"/posts/books/object/오브젝트-04/"},{"title":"오트젝트 06 메시지와 인터페이스","url":"/posts/books/object/오브젝트-06/"},{"title":"오트젝트 05 책임 할당하기","url":"/posts/books/object/오브젝트-05/"}]},{"title":"정규언어","type":"tag","url":"/tags/정규언어/","items":[{"title":"정규언어","url":"/posts/regex/regular-language/"}]},{"title":"정규표현식","type":"tag","url":"/tags/정규표현식/","items":[{"title":"정규표현식 - 02. 정규표현식의 역사","url":"/posts/regex/regex_02/"},{"title":"정규표현식 - 01. 입문 정규표현식","url":"/posts/regex/regex_01/"},{"title":"정규표현식 - 03. 프로그래머를 위한 고급 정규표현식","url":"/posts/regex/regex_03/"}]},{"title":"플레이어","type":"tag","url":"/tags/플레이어/","items":[{"title":"웹에서\b영상 재생을 하기 위한 주변 지식","url":"/posts/web/video player/"}]}]}],"tagsGroups":[{"title":"Readme","type":"tag","url":"/tags/readme/","items":[{"title":"📝 r0k Wiki","url":"/"}]},{"title":"SEO","type":"tag","url":"/tags/seo/","items":[{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","url":"/posts/seo/hreflang-canonical/"},{"title":"SEO 클로킹과 콘텐츠 게이팅","url":"/posts/seo/seo-cloaking-and-content-gating/"}]},{"title":"Sentry","type":"tag","url":"/tags/sentry/","items":[{"title":"Sentry","url":"/posts/monitoring/sentry/"}]},{"title":"Web API","type":"tag","url":"/tags/web-api/","items":[{"title":"Blob","url":"/Web_API/Blob/"},{"title":"DOMMatrixReadOnly","url":"/Web_API/DOMMatrixReadOnly/"}]},{"title":"cloudflare","type":"tag","url":"/tags/cloudflare/","items":[{"title":"github 블로그에 도메인 연결하기 by cloudflare","url":"/posts/cloud/cloudflare/my-dns/"}]},{"title":"css","type":"tag","url":"/tags/css/","items":[{"title":"dynamic-range-limit - HDR 이미지/비디오 밝기 제한 CSS 속성","url":"/posts/web/dynamic-range-limit/"}]},{"title":"git","type":"tag","url":"/tags/git/","items":[{"title":"git 딥다이브 - git branch, tag, HEAD","url":"/posts/study/codesoom-git/git-deep-dive-01/"},{"title":"git 딥다이브 - history","url":"/posts/study/codesoom-git/git-deep-dive-03/"},{"title":"git 딥다이브 - git merge, rebase","url":"/posts/study/codesoom-git/git-deep-dive-02/"}]},{"title":"i18n","type":"tag","url":"/tags/i-18-n/","items":[{"title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","url":"/posts/seo/hreflang-canonical/"}]},{"title":"javascript","type":"tag","url":"/tags/javascript/","items":[{"title":"useTransition과 Suspense의 내부 상호작용","url":"/posts/frontend/use-transition-suspense/"},{"title":"프론트엔드 성능 최적화 팁 기록","url":"/posts/frontend/performance-tip/"},{"title":"callback부터 async await까지 - Javascript 비동기","url":"/posts/javascript/asynchronous/"},{"title":"웹팩을 쓰는 이유와 사용법 정리","url":"/posts/javascript/webpack/"},{"title":"자바스크립트 데드 코드 제거","url":"/posts/javascript/자바스크립트 데드 코드 제거/"},{"title":"웹에서\b영상 재생을 하기 위한 주변 지식","url":"/posts/web/video player/"}]},{"title":"my-setting","type":"tag","url":"/tags/my-setting/","items":[{"title":"개발 생산성을 위한 내 개발 환경 세팅 기록","url":"/posts/my-setting/"}]},{"title":"react","type":"tag","url":"/tags/react/","items":[{"title":"useTransition과 Suspense의 내부 상호작용","url":"/posts/frontend/use-transition-suspense/"}]},{"title":"react-query","type":"tag","url":"/tags/react-query/","items":[{"title":"react-query 동작 안하는 이슈 해결","url":"/posts/library/react-query/fail-request/"}]},{"title":"tip","type":"tag","url":"/tags/tip/","items":[{"title":"webstorm 사용 팁","url":"/posts/tip/webstorm/"},{"title":"나만의 업무 습관 정리","url":"/posts/tip/나만의 업무 습관 정리/"},{"title":"내가 써먹으려고 정리한 단축키 & 명령어 모음","url":"/posts/tip/shortcut/"},{"title":"git 관련 팁 정리","url":"/posts/tip/git/"},{"title":"키보드 안쓰기","url":"/posts/tip/키보드 안쓰기/"}]},{"title":"webstorm","type":"tag","url":"/tags/webstorm/","items":[{"title":"webstorm 사용 팁","url":"/posts/tip/webstorm/"},{"title":"git 관련 팁 정리","url":"/posts/tip/git/"}]},{"title":"개선","type":"tag","url":"/tags/개선/","items":[{"title":"eslint에서 biome로","url":"/posts/library/biome/"},{"title":"자바스크립트 데드 코드 제거","url":"/posts/javascript/자바스크립트 데드 코드 제거/"},{"title":"레거시를 더 나은 환경으로 만들기","url":"/posts/tip/레거시를 더 나은 환경으로 만들기/"}]},{"title":"단축키","type":"tag","url":"/tags/단축키/","items":[{"title":"내가 써먹으려고 정리한 단축키 & 명령어 모음","url":"/posts/tip/shortcut/"}]},{"title":"디깅","type":"tag","url":"/tags/디깅/","items":[{"title":"모노레포에서 서로 다른 React 버전 사용시 에러","url":"/posts/error/모노레포에서 서로 다른 react 버전 사용시 에러/"}]},{"title":"모니터링","type":"tag","url":"/tags/모니터링/","items":[{"title":"프론트엔드 모니터링 - datadog RUM 알아보기","url":"/posts/monitoring/datadog RUM/"}]},{"title":"성능최적화","type":"tag","url":"/tags/성능최적화/","items":[{"title":"프론트엔드 성능 최적화 팁 기록","url":"/posts/frontend/performance-tip/"}]},{"title":"오브젝트","type":"tag","url":"/tags/오브젝트/","items":[{"title":"오트젝트 02 객체지향 프로그래밍","url":"/posts/books/object/오브젝트-02/"},{"title":"오트젝트 01 객체, 설계","url":"/posts/books/object/오브젝트-01/"},{"title":"오트젝트 03 역할, 책임, 협력","url":"/posts/books/object/오브젝트-03/"},{"title":"오트젝트 04 설계 품질과 트레이드 오프","url":"/posts/books/object/오브젝트-04/"},{"title":"오트젝트 06 메시지와 인터페이스","url":"/posts/books/object/오브젝트-06/"},{"title":"오트젝트 05 책임 할당하기","url":"/posts/books/object/오브젝트-05/"}]},{"title":"정규언어","type":"tag","url":"/tags/정규언어/","items":[{"title":"정규언어","url":"/posts/regex/regular-language/"}]},{"title":"정규표현식","type":"tag","url":"/tags/정규표현식/","items":[{"title":"정규표현식 - 02. 정규표현식의 역사","url":"/posts/regex/regex_02/"},{"title":"정규표현식 - 01. 입문 정규표현식","url":"/posts/regex/regex_01/"},{"title":"정규표현식 - 03. 프로그래머를 위한 고급 정규표현식","url":"/posts/regex/regex_03/"}]},{"title":"플레이어","type":"tag","url":"/tags/플레이어/","items":[{"title":"웹에서\b영상 재생을 하기 위한 주변 지식","url":"/posts/web/video player/"}]}],"latestPosts":[{"fields":{"slug":"/posts/seo/hreflang-canonical/","title":"다국어 페이지 작업시 주의해야할 것 - hreflang / canonical","lastUpdatedAt":"2026-06-30T12:28:37.000Z","lastUpdated":"6/30/2026"},"frontmatter":{"draft":false,"tags":["SEO","i18n"]}},{"fields":{"slug":"/posts/web/dynamic-range-limit/","title":"dynamic-range-limit - HDR 이미지/비디오 밝기 제한 CSS 속성","lastUpdatedAt":"2026-05-24T04:36:13.000Z","lastUpdated":"5/24/2026"},"frontmatter":{"draft":false,"tags":["css"]}},{"fields":{"slug":"/posts/seo/seo-cloaking-and-content-gating/","title":"SEO 클로킹과 콘텐츠 게이팅","lastUpdatedAt":"2026-04-18T07:10:38.000Z","lastUpdated":"4/18/2026"},"frontmatter":{"draft":false,"tags":["SEO"]}},{"fields":{"slug":"/posts/frontend/use-transition-suspense/","title":"useTransition과 Suspense의 내부 상호작용","lastUpdatedAt":"2026-03-15T09:04:23.000Z","lastUpdated":"3/15/2026"},"frontmatter":{"draft":false,"tags":["react","javascript"]}},{"fields":{"slug":"/posts/my-setting/","title":"개발 생산성을 위한 내 개발 환경 세팅 기록","lastUpdatedAt":"2026-03-15T04:34:39.000Z","lastUpdated":"3/15/2026"},"frontmatter":{"draft":false,"tags":["my-setting"]}},{"fields":{"slug":"/posts/tip/shortcut/","title":"내가 써먹으려고 정리한 단축키 & 명령어 모음","lastUpdatedAt":"2026-01-29T12:10:22.000Z","lastUpdated":"1/29/2026"},"frontmatter":{"draft":false,"tags":["단축키","tip"]}},{"fields":{"slug":"/posts/tip/키보드 안쓰기/","title":"키보드 안쓰기","lastUpdatedAt":"2026-01-13T07:19:49.000Z","lastUpdated":"1/13/2026"},"frontmatter":{"draft":false,"tags":["tip"]}},{"fields":{"slug":"/posts/tip/git/","title":"git 관련 팁 정리","lastUpdatedAt":"2025-10-25T07:02:10.000Z","lastUpdated":"10/25/2025"},"frontmatter":{"draft":false,"tags":["webstorm","tip"]}},{"fields":{"slug":"/posts/monitoring/datadog RUM/","title":"프론트엔드 모니터링 - datadog RUM 알아보기","lastUpdatedAt":"2025-08-17T07:27:35.000Z","lastUpdated":"8/17/2025"},"frontmatter":{"draft":false,"tags":["모니터링"]}},{"fields":{"slug":"/posts/frontend/performance-tip/","title":"프론트엔드 성능 최적화 팁 기록","lastUpdatedAt":"2025-08-16T09:01:01.000Z","lastUpdated":"8/16/2025"},"frontmatter":{"draft":false,"tags":["javascript","성능최적화"]}}]}},
    "staticQueryHashes": ["2320115945","2650345336","3495835395","451533639"]}