카테고리 없음

[오픈소스코드리뷰] cookie

유지남 2022. 8. 12. 09:28

들어가며

Cookie는 대부분의 웹사이트에서 사용되는 강력한 브라우저 API 이며
key=value로 쌍을 이루고 ;(Semicolon)으로 구분합니다.
브라우저마다 조금씩 다르지만 한 사이트 당 20개 정도로 한정되어 있고,
key=value 를 인코딩 이후 4kb 가 넘지 않아야 하고,
모든 문자가 허용되기 때문에 저장할 때는 반드시 encode 하여 저장하는 것이 좋습니다.
작은 단위 정보로만 사용할 수 있기 때문에, 주로 사용자 정보, 통계 수치, 토큰 저장과 같은 기능 개발에 사용됩니다.

현재 리뷰 버전은 0.4.1 입니다.

리뷰

...
.travis.yml
eslintrc.yml
...

특별한 설정은 없네요. Dayjs와 마찬가지로 Travis CI를 사용하고 있습니다.

rules:
  eol-last: error
  indent: ["error", 2, { "SwitchCase": 1 }]
  no-trailing-spaces: error

eslint 설정 역시 특별한 것은 없습니다..

{
  "scripts": {
    "bench": "node benchmark/index.js",
    "lint": "eslint --plugin markdown --ext js,md .",
    "test": "mocha --reporter spec --bail --check-leaks --ui qunit test/",
    "test-ci": "nyc --reporter=text npm test",
    "test-cov": "nyc --reporter=html --reporter=text npm test",
    "version": "node scripts/version-history.js && git add HISTORY.md"
  }
}

Mocha 로 테스트를 하고 있으며, 퍼포먼스 테스트 도구인 benchmark 도 사용하는 것을 확인할 수 있네요.

본격적으로 index.js 를 살펴볼까요?
주석을 제외하면 약 100여줄 정도밖에 되지 않는데,
정말 많은 사람들이 사용을 하고 있는 라이브러리입니다.

// index.js
// 생략 ...
// 1)
/**
 * RegExp to match field-content in RFC 7230 sec 3.2
 *
 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
 * field-vchar   = VCHAR / obs-text
 * obs-text      = %x80-FF
 */
 
 // 2)
 var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
 // 생략 ...
  1. 사용자가 많은 오픈소스일수록 JSDoc 을 잘 작성합니다.
    JSDoc 작성 시 @example 도 추가로 작성하면 타입 스크립트를 사용하지 않는 환경에서도 협업하기 좋습니다.

    최근 프론트엔드 개발 입사 지원 시 과제를 주는 기업이 상당히 많아졌는데,
    JSDoc 작성 가산점이 있을 정도로 개발 문서화에 대한 관심이 많아지고 있습니다.
  2. Unicode 로 작성된 내용은 \u0009(탭), \u0020(공백), 그 뒤에는 특정한 라틴/특수 문자에 대한 내용이 포함되어 있는지를 확인 하는 정규식입니다.

    https://www.fileformat.info/info/unicode/char/a.htm 사이트에서 Unicode의 대한 내용을 확인 할 수 있습니다.

// index.js
// 생략 ...
function parse(str, options) {
  // 생략 ...
  // 1)
  var pairs = str.split(pairSplitRegExp);

  // 생략 ...
  for (var i = 0; i < pairs.length; i++) {
    // 2
    var pair = pairs[i];
    var eq_idx = pair.indexOf('=');

    // skip things that don't look like key=value
    if (eq_idx < 0) {
      continue;
    }

    var key = pair.substr(0, eq_idx).trim()
    var val = pair.substr(++eq_idx, pair.length).trim();

    // quoted values
    if ('"' == val[0]) {
      val = val.slice(1, -1);
    }

    // only assign once
    if (undefined == obj[key]) {
      obj[key] = tryDecode(val, dec);
    }
  }

  return obj;
}
  1. ;(Semicolon) 을 기준으로 split 하여 배열로 만든 후에
  2. =(Equal)의 포함 여부로 { key: value } 와 같은 Object 만들기 위한 반복문인데,
    이 부분을 정규식으로 만들면 더 간결하게 할 수 있을 것 같네요..
// e.g
const params = arr.map(str => /^(\S+)=(\S+)$/g.exec(str)).reduce((a,b) => {
    const [,key, value] = b;
    return !!key && !!value ? {...a, [key]:value} : a;
}, {});

참고로 \S 는 공백을 제외한 문자에만 해당되는 표현식입니다..

// index.js
// ...생략
switch (sameSite) {
  case true:
    str += '; SameSite=Strict';
    break;
  case 'lax':
    str += '; SameSite=Lax';
    break;
  case 'strict':
    str += '; SameSite=Strict';
    break;
  case 'none':
    str += '; SameSite=None';
    break;
  default:
  throw new TypeError('option sameSite is invalid');
}


이렇게 Case 별로 문자열을 만드는 경우 object 형식으로 개발을 하는 방법도 괜찮을 것 같습니다.

// e.g
let sameSite = 'none';
let str = '';
const mode = {
    "true": "Strict",
    "strict": "Strict",
    "lax": "Lax",
    "none": "None",
}[String(sameSite).toLowerCase()] || null;

str += !!mode ? `SameSite=${mode}` : '';
console.log(str);

쿠키의 사용이 어렵지 않아 코드가 길지 않은 것 같습니다.


마치며

en/de code 방법으로 encodeURIComponent, decodeURIComponent 이 외에
base64 API인 atob, btoa 많이 사용 하니 참고 하시면 좋습니다.

https://github.com/jshttp/cookie

jshttp/cookie

HTTP server cookie parsing and serialization. Contribute to jshttp/cookie development by creating an account on GitHub.

github.com