[Javascript] URLSearchParams를 활용한 QueryString 추출 및 세팅
코드 리팩토링을 진행하다가 기록하면 도움 될 것 같아 남긴다.
https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
1. 쿼리스트링 추출하기
var QueryCollection = function () {
this.collection = [];
};
QueryCollection.prototype.parseQuery = function (url) {
if (!url) {
return this;
}
var index = url.indexOf("?");
if (index < 0) {
return this;
}
var query = url.substr(index + 1);
var queryPairs = query.split("&");
for (var i = 0; i < queryPairs.length; i++) {
var pair = queryPairs[i];
var keyValue = pair.split("=");
if (keyValue.length < 2) {
continue;
}
var key = keyValue[0];
var value = keyValue[1];
if (!key || !value) {
continue;
}
this.collection[decodeURIComponent(key)] = decodeURIComponent(value);
}
return this;
};
기존 소스는 위와 같은 방법으로 쿼리스트링을 parsing 했다.
parseQuery(url) {
const queryPairs = url.slice(url.indexOf('?') + 1).split('&');
queryPairs.map(param => {
const [key, value] = param.split('=');
if (value) this.collection[decodeURIComponent(key)] = decodeURIComponent(value);
})
다음과 같이 map을 이용하면 이렇게 간결화할 수 있다.
str.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
obj[decodeURIComponent(key)] = decodeURIComponent(value);
});
정규식을 이용하면 이렇게 표현할 수도..
this.collection = {};
parseQuery(url) {
const queryObj = Object.fromEntries(new URLSearchParams(new URL(url).search));
return Object.assign(this.collection, queryObj);
};
es6에서는 애초에 배열이 아니라 Object로 선언해 다음과 같이 간결하게 나타낼 수 있다. (URL.searchParams이 ts문법)
Object.assign의 반환 값이 object 객체이기 때문에 리턴으로 해주면 간단하다.
중복 key값은 덮어 씌우고 신규는 이어 붙인다.
여기서 new URL(url).searchParams (Only Typescript) 대신에 new URLSearchParams(url)을 쓰면 다음과 같이 첫 파라미터가 딸려온다.
usp2
{https://www.naver.com/search?test: '', q: 'javascript query string parse', newwindow: '1', rlz: '1C1GCEU_koKR1006KR1006', ei: 'j3FDY6DUI4b9hwOL9KMo', …}
그래서 new URL.search를 사용하는 편이 낫다. (TypeScript에서는 new URL(url).searchParams 가능)
2. 쿼리 스트링 세팅하기
QueryCollection.prototype.setQueryTo = function (url, skipEncoding) {
var pairs = [];
for (var key in this.collection) {
var value = this.collection[key];
var newKey = skipEncoding ? key : encodeURIComponent(key);
var newValue = skipEncoding ? value : encodeURIComponent(value);
pairs.push(newKey + "=" + newValue);
}
var queryString = pairs.join("&");
var newUrl = url;
var queryIndex = newUrl.indexOf("?");
if (queryIndex < 0) {
newUrl += "?" + queryString;
} else {
newUrl = newUrl.substr(0, queryIndex + 1) + queryString;
}
return newUrl;
};
이것도 중복 코드를 없애고 map, entries를 사용하면 다음과 같이 표현이 가능하다.
const pairs = Object.entries(this.collection).map(e => e.join('=')).join('&');
URLSearchParams은 Object를 바로 묶어준다. +기호를 공백으로 치환하는 RFC3986을 따르고 있으니 다음과 같이 사용하여야 한다.
setQueryTo(url, isSkipEncoding) {
const pairs = isSkipEncoding ?
new URLSearchParams(this.collection).toString()
: Object.keys(this.collection).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(this.collection[key])
}).join('&');
return url.split('?')[0] + '?' + pairs;
};
전체 기존 코드
var QueryCollection = function () {
this.collection = [];
};
QueryCollection.prototype.parseQuery = function (url) {
if (!url) {
return this;
}
var index = url.indexOf("?");
if (index < 0) {
return this;
}
var query = url.substr(index + 1);
var queryPairs = query.split("&");
for (var i = 0; i < queryPairs.length; i++) {
var pair = queryPairs[i];
var keyValue = pair.split("=");
if (keyValue.length < 2) {
continue;
}
var key = keyValue[0];
var value = keyValue[1];
if (!key || !value) {
continue;
}
this.collection[decodeURIComponent(key)] = decodeURIComponent(value);
}
return this;
};
QueryCollection.prototype.set = function (key, value) {
this.collection[key] = value;
};
QueryCollection.prototype.get = function (key) {
return this.collection[key];
};
QueryCollection.prototype.setQueryTo = function (url, skipEncoding) {
var pairs = [];
for (var key in this.collection) {
var value = this.collection[key];
var newKey = skipEncoding ? key : encodeURIComponent(key);
var newValue = skipEncoding ? value : encodeURIComponent(value);
pairs.push(newKey + "=" + newValue);
}
var queryString = pairs.join("&");
var newUrl = url;
var queryIndex = newUrl.indexOf("?");
if (queryIndex < 0) {
newUrl += "?" + queryString;
} else {
newUrl = newUrl.substr(0, queryIndex + 1) + queryString;
}
return newUrl;
};
전체 코드 리팩토링
export class QueryCollection {
constructor() {
this.collection = {};
}
set(key, value) {
this.collection[key] = value;
};
get(key) {
return this.collection[key];
};
parseQuery(url) {
// const queryObj = Object.fromEntries(new URLSearchParams(new URL(url).search));
Object.assign(this.collection, queryObj)
return this;
};
setQueryTo(url, isSkipEncoding) {
const pairs = isSkipEncoding ?
new URLSearchParams(this.collection).toString()
: Object.keys(this.collection).map((key) => {
return encodeURIComponent(key) + '=' + encodeURIComponent(this.collection[key])
}).join('&');
return url.split('?')[0] + '?' + pairs;
};
}
아직 테스트 안해봐서 오류 지적 환영합니다!
'Front-end' 카테고리의 다른 글
Pixar에서 만든 3D 포맷, USD (0) | 2024.08.01 |
---|---|
[Javacsript] 클립보드에 복사하기 (0) | 2022.11.24 |
[Webpack] Loader (0) | 2022.07.06 |
[Webpack] Webpack 기초 (0) | 2022.07.03 |
[Intellij / React] 무작정 React Project 만들어보기 -1 (0) | 2022.03.05 |