목록 복귀 시 검색 조건 유지: URL 쿼리와 return_to

이 글의 목차 9개 항목
  • 분야: FE
  • 기술 스택: React · TypeScript(TSX) · Next.js 호환 라우팅

문제와 원인

필터 목록 URL에는 q·category·topic이 있었지만 상세 링크에서 복귀 주소가 누락됐다. 돌아가기 링크는 document.listPath만 가리켜 상세를 다녀오면 기본 목록으로 돌아갔다.

수정 전 코드

PublicTheoryDirectory

href={article.href}

목록 복귀·이전·다음 단원

href={document.listPath}
{previous && <Link className="outline-button" href={previous.href}>← 이전 단원</Link>}
{next && <Link className="primary-button" href={next.href}>다음 단원 →</Link>}

수정 후 코드

PublicTheoryDirectory

href={theoryDetailHref(article.href, theoryListHref(listPath, { q: search, category, topic: activeTopic }))}

목록 복귀·이전·다음 단원

const returnHref = safeTheoryReturnHref(document.listPath, returnTo);

href={returnHref}

{previous && <Link className="outline-button" href={theoryDetailHref(previous.href, returnHref)}>← 이전 단원</Link>}
{next && <Link className="primary-button" href={theoryDetailHref(next.href, returnHref)}>다음 단원 →</Link>}

주소 생성·검증

theoryListHref가 세 필터를 포함한 목록 주소를 만들고, theoryDetailHref가 상세 링크의 기존 쿼리를 보존하면서 return_to를 추가한다. 상세와 이전·다음 단원 이동에서도 같은 복귀 주소를 전달한다.

복귀 시 safeTheoryReturnHref는 같은 origin과 정확한 listPath를 검사한 뒤 세 필터만 다시 구성한다. 필터별 값은 200자로 제한한다.

type Filters = { q?: string; category?: string; topic?: string };

export function theoryListHref(listPath: string, filters: Filters) {
  const query = new URLSearchParams();
  for (const key of ["q", "category", "topic"] as const) {
    if (filters[key]) query.set(key, filters[key]!.slice(0, 200));
  }
  return listPath + (query.size ? "?" + query : "");
}

export function theoryDetailHref(href: string, returnTo: string) {
  const [path, query] = href.split("?");
  const params = new URLSearchParams(query);
  params.set("return_to", returnTo);
  return path + "?" + params;
}

export function safeTheoryReturnHref(listPath: string, value?: string) {
  if (!value || !value.startsWith("/") || value.startsWith("//")) {
    return listPath;
  }
  try {
    const url = new URL(value, "https://modumunje.com");
    if (url.origin !== "https://modumunje.com" || url.pathname !== listPath) {
      return listPath;
    }
    return theoryListHref(listPath, {
      q: url.searchParams.get("q") ?? undefined,
      category: url.searchParams.get("category") ?? undefined,
      topic: url.searchParams.get("topic") ?? undefined,
    });
  } catch {
    return listPath;
  }
}

검증 결과

원본 전후 함수와 링크 표현식에 같은 7개 입력을 넣었다. 정상 필터 4개에서 누락이 4개에서 0개로 줄고, 잘못된 복귀 주소 3/3개는 기본 목록으로 이동했다.

입력 조건수정 전수정 후
검색어 지정검색어 누락q 유지
과목 지정과목 누락category 유지
단원 지정단원 누락topic 유지
검색어·과목·단원 함께 지정세 필터 누락세 필터 유지
//로 시작하는 다른 출처 주소기본 목록기본 목록
다른 출처의 절대 주소기본 목록기본 목록
다른 경로 /admin기본 목록기본 목록

검색어 정규화, 과목 데이터 모델링의 이해, 단원 정규화를 각각 지정하거나 함께 지정한 네 입력과 잘못된 주소 세 입력을 로컬 Node에서 각 1회 다시 실행했다. 네 필터 입력은 쿼리를 보존했고 잘못된 주소는 기본 목록을 반환했다.

정상 필터 4개 보존과 잘못된 복귀 주소 3개 차단원본 보기 (새 창)

정상 필터 4개 보존과 잘못된 복귀 주소 3개 차단

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.