동일한 키에 대한 쿼리 편집하기

jh_leitmotif·2022년 2월 8일
1

Frontend 개인 공부

목록 보기
20/24

개요

http://someUrl/?category=blahblah&tag=blahblah...

URLSearchParams()는 key=value의 형태로 query를 편집할 수 있게끔 만들어주는 좋은 친구입니다.

여기에서 저는 key는 항상 unique해야한다! 라고 생각하고 있다보니

http://someUrl/?category=a&tag=b&subtag=c&subSubtag=d

이런식으로, 모든 쿼리의 키는 고유해야 한다고 전제했었습니다.

그리고 해당 키에 여러가지 값이 있다면?

http://someUrl/?category=a,b,c

queryString을 list 형태로 넘기면 이런 방식으로 넘어갈 수가 있습니다.

실제로 쿠팡, 네이버 등의 사이트가 이것을 채용해 사용하고 있습니다.

그런데 만약 '똑같은' key가 계속 &가 걸리는 경우가 있다면 어떻게 처리해야할까? 라는 고민이 계속 있었습니다.


👉 사건의 발단(?)

??? : 쿼리를 이렇게 보내주세요! 
'/v0/api/category=blahblah&subcategory=a&subcategory=b&subcategory=c'

사실 이전에도 이런 문제가 있었습니다만,

백엔드 입장에서 '하드코딩' 스럽다는 견해가 있었지만, 필터될 것이 그렇게 많지는 않아서 일단 넘어갔던 기억이 있습니다.

다행스럽게도 지금 작업 중인 프로젝트에서는 시간적인 여유가 조금 있어 구현해보기로 마음먹고 구글을 뒤져보기 시작했습니다.

🥺 검색 키워드 :: searchParams remove by value

!!! react-router-dom은 query params를 다룰 수 있게끔 'useSearchParams()'라는 hook을 제공한다.

URLSearchParams 형태를 이용한 useSearchParams라는 hook이 존재합니다.

사용법은 아래와 같습니다.

const [searchParams] = useSearchParams();

searchParams.append(key,value) :: 쿼리스트링을 추가한다.
searchParams.delete(key) :: 해당 키의 쿼리 스트링을 삭제한다.
searchParams.set(key,value) :: 해당 키의 쿼리 스트링을 갱신한다.
searchParams.toString() :: 쿼리 스트링을 문자열로 변환하여 반환한다.
..... 등등등

여기서 고민했던 것은 delete에 대한 동작입니다.

append의 경우, 직접 테스트해보니 동일한 키에 대한 다른 값이 의도된 대로 잘 추가됩니다.

다만, delete(key) 동작을 진행하면 해당 key에 해당하는 쿼리가 모두 날아갑니다....

searchParams.append('tag','a')
searchParams.append('tag','b')
searchParams.append('tag','c')
 //?tag=a&?tag=b&tag=c 로 조합됐고.
searchParams.delete('tag')
 // 난 마지막 tag=c만 지우고 싶었는데
 // ? 로 그냥 초기화되버린다 ㅜㅜ
이거.. key로는 지우면 안되겠다.
value를 기준으로 지울 수 있는 방법은 없을까?

라는 생각으로 검색 신공을 펼쳤지만

따로 URLSearchParams, 또는 useSearchParams hook에서 제공해주는 것은 없어보이고 대부분 따로 함수를 만들어서 적용한다는 이야기가 많았습니다.

링크 : https://stackoverflow.com/questions/70221831/remove-a-url-search-parameter-when-there-is-duplicate-names

🙇‍♂️ 그러면 값은 어떻게 받아올 수가 있을까...

searchParams.get(key)
searchParams.getAll(key)

사용할 수 있는 내장된 함수 중, get과 getAll이 있었습니다.

두 함수 모두 key를 기준으로 조회한다는 것은 동일한데, get은 단순히 해당 키에 해당하는 '첫 번째' 값만 반환하지만 getAll은 값을 Array 형태로 반환합니다.

아? 그렇다면..

#1. 만약 들어온 쿼리 스트링 값이 이미 존재하는 경우.
#2. 일단 getAll로 현재 쿼리 스트링의 값을 배열로 받아온다. 
#3. Array.splice(index,1)을 통해 배열에서 해당 값을 지운다.
#4. forEach를 돌리기 전에 이미 존재하는 params에 대해서 초기화를 한 뒤
#5. forEach를 돌려 params를 세팅하면 되지 않겠는가?

📋 코드로 옮겨보자.

스케치는 다 했으니, 색만 입혀보았습니다.

const [queryString,setQueryString] = useState('');
const [searchParams] = useSearchParams();
    
const handleParams = (name) =>{
	switch(name){
    	case :'all'
        case :'initialize'
        	searchParams.delete(key);
            break;
        default:
        	!decodeURI(queryString).includes(name) ?
            	searchParams.append(key,name)
                :
                removeParams(name);
                	// #1. 이미 해당 쿼리 스트링이 존재하는 경우.
    }
    
    setQueryString(searchParams.toString());
    
}

	// 선택가능한 필터 버튼이 여러가지일 것을 가정합니다.
    // 그러므로, 전체 선택 또는 초기화에 해당하는 arg가 아니라면 기본적으로 append하거나 지웁니다.
    // 그 뒤 queryString을 업데이트합니다.

const removeParams = (name) =>{
	const allParams = searchParams.getAll(key);
    	// #2. 현재 쿼리 스트링에 저장된 Params를 배열로 가져온 뒤 
    allParams.splice(allParams.indexOf(name),1));
    	
    	// #3. 이미 존재하는 쿼리 스트링에 대한 값을 배열에서 지운다.
    searchParams.delete(key);
    	// #4. params를 새로 세팅해야하니 이미 존재하는 값들을 초기화하고
    allParams.forEach(item=>{
    	searchParams.append(key,item);
    });
    	// #5. 반복문을 이용해 새로운 Params를 세팅한다.
}

🎯 마무리

사실 아직도 어떻게 쿼리를 날리는 것이 정답인지 모르겠습니다.

리스트 형태로 넘겨야하는지..모든 키를 고유하게 설정해서 넘겨야하는지...

혹은 이 포스트에서 다룬 것과 같이 키가 중복되어도 되는지...

어쨌든, 전에 하루 정도를 날렸음에도 해결이 되지 않았던 이 중복된 키에 대한 문제가

잠깐 10분 정도 생각하고, 짰더니 바로 해결되어 버린.. 이 상황에 대해 기분이 이상하기도 하고

세 가지 경우에 대한 코드를 모두 가지고 있으니, 이제 난 무적이다!!! 란 생각도 합니다.

profile
Define the undefined.

0개의 댓글