최근 회사 프로젝트에서는 URL 대부분이 query string 기반으로 상태를 관리하고 있었습니다.
페이지 번호, 필터 조건, 정렬 방식, 검색어 등 다양한 값들이 URL에 그대로 드러나 있었고, 그 구조를 파악하고 유지하는 것이 꽤 중요한 과제가 되었습니다.
초기에는 API 호출 없이 프론트엔드에서만 필터링을 처리하고 있었기 때문에, 상태 유지를 위해 query string을 자연스럽게 사용했습니다.
하지만 SEO 작업을 진행하면서 query string을 제거해야 하는 상황이 되었고, 그에 따라 서버 API를 통해 데이터를 다시 가져오는 방식으로 변경하게 되었습니다.
이 경험을 계기로
“어떤 경우에 query string을 사용해야 하고, 어떤 경우에는 숨기는 것이 좋을까?”
라는 질문을 스스로에게 던지게 되었고, 저만의 기준을 정리해보고자 이 글을 작성하게 되었습니다.
🔍 1. URL 쿼리 파라미터 사용
// 예시: https://example.com/products?page=2&size=20&keyword=셔츠
const searchParams = new URLSearchParams(window.location.search);
const page = searchParams.get('page');
const keyword = searchParams.get('keyword');
✅ 장점
- 공유 가능: 사용자가 검색이나 필터를 적용한 상태를 그대로 URL로 공유 가능
- 브라우저 히스토리 연동: 뒤로 가기/앞으로 가기 시 상태 복원 쉬움
- SSR/SEO 대응 가능: Next.js 같은 SSR 프레임워크에서는 URL 기반 쿼리가 데이터 prefetch에 유리함
- 디버깅 쉬움: URL만 봐도 현재 상태 파악 가능
❌ 단점
- 길어지고 복잡해질 수 있음: 쿼리 파라미터가 많아질 경우 URL이 난잡해짐
- 비민감 데이터만 노출 가능: 민감한 필터 조건(예: 유저 ID, 내부 키값)은 URL에 직접 쓰기 부적절
- 초기 상태 로직 복잡: URL을 기준으로 상태를 초기화해야 해서 컴포넌트 진입 시 처리가 복잡할 수 있음
🔐 2. API 호출 시 쿼리 전달
// 예시: axios.get(`/api/products`, { params: { page: 2, size: 20, keyword: '셔츠' } });
✅ 장점
- 보안 측면에서 안전: URL 노출 없이 서버에 필요한 값만 전송 가능
- 더 많은 유연성: URL은 그대로 유지하면서 내부 상태로 다양한 필터 조건 구성 가능
- 복잡한 구조 전달 가능: 배열, 객체 형태의 조건 등도 쉽게 전송 가능
❌ 단점
- 공유 어려움: 상태가 URL에 반영되지 않아 "링크 공유"나 "새로고침 후 복원"이 불가능
- SEO에 불리: 클라이언트 사이드에서만 처리되기 때문에 크롤러가 상태를 파악 못함
- 브라우저 히스토리 관리 번거로움: 상태 변경 시 별도로 pushState 등 사용 필요
🧠 언제 어떤 방식이 적절할까?
저는 프로젝트 경험을 바탕으로, 아래와 같은 기준을 세워 사용하고 있습니다.
상황 | 적절한 방식 |
검색, 필터, 페이지네이션 등 공유 가능한 상태가 필요한 경우 | URL 쿼리 |
사용자가 뒤로가기/앞으로가기로 상태를 복원하길 원하는 경우 | URL 쿼리 |
비공개 조건, 복잡한 구조(예: 필터 다중 배열) | API 쿼리만 사용 |
내부 설정 등 외부에 노출하지 않아야 할 정보 | API 쿼리만 사용 |
SEO가 중요한 페이지 (Next.js SSR 등) | URL 쿼리 |
상태 공유보다 사용자 경험 중심의 인터랙션에 집중 | API 쿼리만 사용 또는 상태 관리 라이브러리 활용 |
💡 참고: 새로고침 시 API 쿼리 상태 유지하는 방법은 없을까?
API 요청에만 의존할 경우 새로고침 시 상태가 초기화되지만, 다음과 같은 방법으로 보완할 수 있습니다:
- localStorage / sessionStorage 사용
→ 조건 값을 저장하고, 페이지 진입 시 복원 - 전역 상태 관리 라이브러리 활용 (Redux-persist, Zustand 등)
→ 상태를 유지하고 브라우저 재시작에도 대응 가능 - URL 일부만 쿼리로 남기고, 나머지는 내부 상태로 혼합 사용
→ 필수 조건만 URL에 반영하여 UX와 보안 절충
🧩 두 가지를 조합하자!
실무에서는 두 방식을 혼합해서 사용하는 경우가 많다고 합니다.
예를 들어, 사용자 필터 상태는 URL에 반영하고, 검색 조건은 API 호출로만 전달하는 식이죠.
// React 예시: URL은 필터에만, 내부 조건은 따로 관리
const [keyword, setKeyword] = useState('');
const router = useRouter();
const { page, size } = router.query;
// API 호출 시
useEffect(() => {
fetch(`/api/products?page=${page}&size=${size}&keyword=${keyword}`);
}, [page, size, keyword]);
✍️ 마무리: 쿼리를 다루는 건 단순하지 않다
프론트엔드에서 쿼리(Query) 를 어떻게 처리하느냐는 단순히 데이터 페칭의 문제가 아닙니다.
그 결정은 UX, 유지보수성, 나아가서는 SEO나 보안까지 영향을 미칠 수 있습니다!!!
저는 개인적으로 다음과 같은 기준으로 접근하고 있습니다:
- URL에 query string을 사용하는 것은 새로고침 시 상태가 유지되고, 데이터 페칭 로직이 간단해진다는 장점이 있지만
→ 사용자가 굳이 몰라도 되는 정보까지 URL로 노출되기 때문에 무분별하게 사용하진 않습니다. - 따라서 민감하지 않은 데이터(예: 페이지 번호, 정렬 기준, 검색 키워드)나,
새로고침/링크 공유 등에서 상태가 유지되어야 할 경우에만 query string을 활용하고 있습니다. - 반대로 사용자에게 감춰져야 할 정보나 복잡한 구조는 URL에 드러내지 않고, 내부 상태로 관리하거나 API 파라미터로만 전달합니다.
'React.js' 카테고리의 다른 글
[React] React에서 ref란? 정말 꼭 써야 할까? (0) | 2025.04.22 |
---|---|
[React]React useImperativeHandle Hook 깊게 이해하기 (0) | 2025.02.01 |
[React]React useActionState Hook에 대해 알아보기 (0) | 2024.12.31 |
[React] react-beautiful-dnd를 이용한 Drag and Drop 구현 (3) | 2024.12.01 |
[React] 서버와 통신하며 임시저장 기능 구현하기 (2) | 2024.10.10 |