-->

[Javascript] URLSearchParams를 활용한 QueryString 추출 및 세팅

 

코드 리팩토링을 진행하다가 기록하면 도움 될 것 같아 남긴다.

https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams

 

URLSearchParams - Web APIs | MDN

The URLSearchParams interface defines utility methods to work with the query string of a URL.

developer.mozilla.org

 

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

+ Recent posts