상품 목록 페이지 SEO 최적화: 카테고리 페이지 트래픽을 3배 높이는 전략

이커머스

SEO상품 목록 페이지카테고리 SEO이커머스 검색트래픽 최적화

이 글은 누구를 위한 것인가

  • 카테고리 페이지 트래픽이 없어 상품 상세 페이지만 의존하는 팀
  • 필터 선택 시 URL이 바뀌어 구글에 중복 페이지가 색인되는 팀
  • SEO 없이 만든 PLP(Product Listing Page)를 개선하려는 개발자

들어가며

상품 상세 페이지(PDP)만 SEO를 신경 쓰는 팀이 많다. 하지만 "남성 청바지", "겨울 패딩 추천" 같은 카테고리성 키워드는 목록 페이지(PLP)가 타겟이다. PLP SEO를 잡으면 고구매 의도 트래픽이 들어온다.

이 글은 bluefoxdev.kr의 이커머스 SEO 전략 를 참고하여 작성했습니다.


1. PLP SEO 문제 진단

[PLP SEO 실패 패턴]

1. 중복 페이지 문제:
   /category/jeans
   /category/jeans?color=blue
   /category/jeans?color=blue&size=M
   → 필터 조합마다 별도 URL = 중복 콘텐츠

2. 얕은 콘텐츠:
   카테고리 페이지에 상품만 있고 텍스트 없음
   → 구글이 "내용 없는 페이지"로 인식

3. 페이지네이션 오류:
   page=2, page=3이 rel="canonical"로 page=1 가리킴
   → 2페이지 이후 상품은 색인 안됨

4. 내부 링크 부족:
   서브 카테고리 간 링크 없음
   → Link Equity 전달 안됨

[올바른 URL 전략]
색인 대상: /category/청바지/남성
색인 제외: /category/청바지/남성?sort=price_asc (noindex)
색인 포함: /category/청바지/남성/슬림핏 (인기 필터는 별도 페이지)

2. 동적 메타 태그 생성

interface CategoryPageMeta {
  title: string;
  description: string;
  h1: string;
  intro: string;
}

function generateCategoryMeta(
  category: string,
  filters: Record<string, string>,
  productCount: number,
): CategoryPageMeta {
  const filterText = Object.entries(filters)
    .map(([key, val]) => `${val}`)
    .join(" ");

  const fullCategory = [filterText, category].filter(Boolean).join(" ");

  return {
    title: `${fullCategory} 추천 ${productCount}개 | 최저가 무료배송`,
    description: `${fullCategory} 인기 상품 ${productCount}개를 한눈에 비교하세요. 오늘 주문 시 내일 도착, 무료반품 보장.`,
    h1: `${fullCategory} (${productCount}개 상품)`,
    intro: generateCategoryIntro(category, filters, productCount),
  };
}

function generateCategoryIntro(
  category: string,
  filters: Record<string, string>,
  count: number,
): string {
  const season = getCurrentSeason();
  const filterDesc = Object.values(filters).join(", ");

  return `${season} ${filterDesc ? filterDesc + " " : ""}${category} 트렌드 상품 ${count}개를 모았습니다. 
가격 비교, 리뷰, 배송 정보를 한 페이지에서 확인하고 최적의 상품을 선택하세요.`;
}

function getCurrentSeason(): string {
  const month = new Date().getMonth() + 1;
  if (month >= 3 && month <= 5) return "봄";
  if (month >= 6 && month <= 8) return "여름";
  if (month >= 9 && month <= 11) return "가을";
  return "겨울";
}

// Next.js generateMetadata
export async function generateMetadata({ params, searchParams }: PageProps) {
  const category = await getCategory(params.slug);
  const filters = extractSeoFilters(searchParams);
  const products = await getProducts({ category: params.slug, ...filters });

  const meta = generateCategoryMeta(category.name, filters, products.total);

  return {
    title: meta.title,
    description: meta.description,
    alternates: {
      canonical: buildCanonicalUrl(params.slug, filters),
    },
    robots: shouldIndex(searchParams) ? "index,follow" : "noindex,follow",
  };
}

function shouldIndex(searchParams: Record<string, string>): boolean {
  // 정렬, 페이지는 noindex
  const noIndexParams = ["sort", "page"];
  return !noIndexParams.some((p) => searchParams[p]);
}

3. 패싯 필터 URL 전략

// 패싯 필터: SEO 대상 vs 비대상 분리
const SEO_FACETS = ["gender", "style", "material"]; // 색인 대상
const SESSION_FACETS = ["sort", "color", "size"];    // noindex (너무 많음)

function buildCanonicalUrl(
  categorySlug: string,
  filters: Record<string, string>,
): string {
  const seoFilters = Object.entries(filters)
    .filter(([key]) => SEO_FACETS.includes(key))
    .sort(([a], [b]) => a.localeCompare(b)); // 순서 정규화

  const base = `/category/${categorySlug}`;
  if (!seoFilters.length) return base;

  const path = seoFilters.map(([, val]) => val).join("/");
  return `${base}/${path}`;
}

// 인기 필터 조합은 별도 SEO 페이지로
const POPULAR_FILTER_PAGES = [
  { slug: "청바지/남성/슬림핏", title: "남성 슬림핏 청바지" },
  { slug: "패딩/여성/롱패딩", title: "여성 롱패딩" },
  { slug: "운동화/남성/러닝화", title: "남성 러닝화" },
];

마무리

PLP SEO의 핵심은 두 가지다: 중복 페이지 제거와 얕은 콘텐츠 보완. 필터 URL을 canonical로 정규화하고, 카테고리 페이지에 200자 이상의 소개 텍스트와 내부 링크를 추가하는 것만으로도 3개월 내 카테고리 페이지 트래픽이 눈에 띄게 증가한다.