멤버십 제한이 있는 WordPress 사이트를 위한 검색 모달 구축하기

대부분의 WordPress 검색 튜토리얼은 헤더에 위젯을 추가하는 단계에서 끝납니다.

유료 강의나 비공개 영상과 같이 접근이 제한된 콘텐츠가 있는 경우에는 이 방식이 통하지 않습니다. 기본 검색 기능은 로그아웃된 방문자에게 비공개 자료의 제목을 노출할 수 있기 때문입니다.

저는 피트니스 멤버십 사이트를 위해 라이브 검색 모달을 구축했습니다. 이 사이트는 WordPress, WooCommerce, Divi, LearnDash, WishList Member를 사용합니다.

페이월(paywall)을 준수하는 검색 기능을 구축하는 방법을 소개합니다.

아키텍처

저는 액세스 권한을 인식하는 필터링 기능이 포함된 통합 인덱스를 사용했습니다. 로그아웃된 사용자가 멤버 전용 게시물의 제목이나 발췌문을 절대 볼 수 없도록 하기 위해 이 방식을 선택했습니다.

UI는 전체 화면 오버레이를 여는 아이콘을 사용합니다. 이는 모바일에서 공간을 절약하며, 좁은 입력창보다 훨씬 깔끔해 보입니다.

백엔드

모든 기능은 자식 테마(child theme) 내의 단일 커스텀 REST 경로를 통해 실행됩니다.

주요 기술 규칙:

  • 의존성 보호: Relevanssi와 같은 검색 플러그인을 사용할 때는 함수 체크를 수행하세요. 플러그인이 없더라도 사이트가 깨지는 대신 WordPress 코어 검색 기능으로 대체(fallback)되어야 합니다.
  • 서버 측 필터링: JavaScript를 사용하여 결과를 필터링하지 마세요. 브라우저에서 결과를 숨기더라도 데이터는 이미 네트워크 응답에 포함되어 있습니다. DevTools를 사용하는 사람이라면 누구나 이를 볼 수 있습니다. 서버가 응답을 보내기 전에 데이터를 필터링해야 합니다.
  • 캐시 제외: 검색 REST 경로를 페이지 캐싱에서 반드시 제외해야 합니다. 검색 결과를 캐싱하면 한 회원의 검색 결과가 게스트에게 제공되어 비공개 콘텐츠가 유출될 수 있습니다.

프론트엔드

클라이언트 측은 vanilla JavaScript를 사용합니다.

UX를 완성하는 세 가지 요소:

  • 디바운스(Debounce): 키 입력 후 요청을 보내기 전 250ms를 기다립니다. 이는 불필요한 서버 부하를 방지합니다.
  • AbortController: 사용자가 계속 타이핑할 때 이전 요청을 취소합니다. 이는 이전 결과가 새로운 결과를 덮어쓰는 것을 방지합니다.
  • 에러 상태: fetch가 실패하면 명확한 메시지를 표시하세요. 멈추지 않고 계속 돌아가는 로딩 스피너는 나쁜 UX입니다.

모바일 교훈

Divi 모바일 메뉴에 검색 버튼을 삽입하려고 시도했습니다. 하지만 테마가 클릭 이벤트를 가로채는 바람에 버튼을 클릭할 수 없었습니다.

해결책은 간단했습니다. 테마와 싸우지 않는 것입니다.

버튼을 메뉴 안에 넣는 대신, 헤더에 독립적인 요소로 삽입했습니다. 이렇게 하면 테마의 제어권 밖에서 작동하며 탭하기도 쉬워집니다.

요약

  • 액세스 제어는 반드시 서버에서 이루어져야 합니다.
  • 검색 엔드포인트를 캐시에서 제외하세요.
  • 요청 처리를 위해 debounce와 AbortController를 사용하세요.
  • XSS를 방지하기 위해 API 데이터를 DOM에 삽입하기 전에 반드시 이스케이프(escape) 처리하세요.
  • 페이지 빌더가 코드를 거부한다면, 요소 안에 넣으려 하지 말고 그 옆에 배치하세요.

Source: https://dev.to/highcenburg/building-a-search-modal-for-a-membership-gated-wordpress-site-b92