초기화 함수 init(2)

김대웅·2021년 6월 30일
0

express 분석

목록 보기
6/14

Etag

  • ETag(Entity Tag)는 브라우저의 캐시에 저장되어 있는 구성요소와 원본 서버의 구성요소가 일치하는지 판단하는 방법중 하나이다. 구성요소를 나타내는 값을 고유한 문자열로 응답헤더의 ETag에 지정한다.
  • ETag의 문제점은 해당 값이 서버당 하나이기 때문에 여러 서버로 운영되는 서비스일 경우 값이 일치하지 않게 된다. 이러한 문제는 또한 프록시 캐시의 효율 또한 떨어뜨린다.

https://joochang.tistory.com/37 참고

  • 수정일자를 저장하기 힘든 상황, 지속적으로 저장하고 있기 힘든 상황이 있을수 있기 때문에 ETag를 확인하는것이 수정일자를 확인하는것보다 신뢰할 수 있다.
  • weak 또는 string validator가 존재하며, string validator가 기본값이다. strong validator의 성질(characteristics)을 만족하는 entity-tag를 만들수 없는 경우 weak validator를 설정하여야하며 "W/"를 프리픽스로 붙여야 한다.

RFC7232 2.3 ETag
https://datatracker.ietf.org/doc/html/rfc7232#section-2.1

defaultConfiguration

app.defaultConfiguration = function defaultConfiguration() {
  var env = process.env.NODE_ENV || "development";

  // default settings
  this.enable("x-powered-by");
  this.set("etag", "weak");
  /**
  *
  * 현재까지 settings변수에 저장되어있는 프로퍼티
  *
  * x-powered-by : true
  * etag : "weak"
  * etag fn : ETag생성 함수
  */
  this.set("env", env);
  this.set("query parser", "extended");
  this.set("subdomain offset", 2);
  this.set("trust proxy", false);

  // trust proxy inherit back-compat
  Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
    configurable: true,
    value: true,
  });

  debug("booting in %s mode", env);

  this.on("mount", function onmount(parent) {
    // inherit trust proxy
    if (
      this.settings[trustProxyDefaultSymbol] === true &&
      typeof parent.settings["trust proxy fn"] === "function"
    ) {
      delete this.settings["trust proxy"];
      delete this.settings["trust proxy fn"];
    }

    // inherit protos
    setPrototypeOf(this.request, parent.request);
    setPrototypeOf(this.response, parent.response);
    setPrototypeOf(this.engines, parent.engines);
    setPrototypeOf(this.settings, parent.settings);
  });

  // setup locals
  this.locals = Object.create(null);

  // top-most app is mounted at /
  this.mountpath = "/";

  // default locals
  this.locals.settings = this.settings;

  // default configuration
  this.set("view", View);
  this.set("views", resolve("views"));
  this.set("jsonp callback name", "callback");

  if (env === "production") {
    this.enable("view cache");
  }

  Object.defineProperty(this, "router", {
    get: function () {
      throw new Error(
        "'app.router' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app."
      );
    },
  });
};

set

/**
 * 1. 첫번째 인자만 들어오는 경우 : 첫번째 인자를 key로하여 value를 리턴한다.
 * 2. 두번째 인자도 들어오는 경우 : 첫번째 인자를 key 두번째 인자를 value로 하여 settings개체에 저장한다.
 * 3. 첫번째 인자의 값이 etag, query parser, trust proxy인경우 : etag fn, query parser fn, trust proxy fn을 key 컴파일한값을 value로 하여 값을 저장한다.
 * 4. 첫번째 인자의 값이 trust proxy인경우 : @@symbol:trust_proxy_default를 key, false를 value로 하여 값을 저장한다.
 */

app.set = function set(setting, val) {
  /**
   * setting : settings의 key 값으로 사용
   * val : settings 변수의 value 값으로 사용
   */

  if (arguments.length === 1) {
    // app.get(setting)
    return this.settings[setting];
    /**
     * set함수에 입력된 인자의 개수가 하나인경우 입력된 인자를 키로하여 저장된 값을 리턴한다.
     */
  }

  debug('set "%s" to %o', setting, val);

  // set value
  this.settings[setting] = val;
  /**
   * settings에 값을 설정한다.
   */

  // trigger matched settings
  switch (setting) {
    case "etag":
      this.set("etag fn", compileETag(val));
      break;
    case "query parser":
      this.set("query parser fn", compileQueryParser(val));
      break;
    case "trust proxy":
      this.set("trust proxy fn", compileTrust(val));

      // trust proxy inherit back-compat
      Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
        configurable: true,
        value: false,
      });

      break;
  }

  /**
   * setting값이 etag, query parser, trust proxy인경우
   * val값을 기반으로 etag fn, query parser fn, truest proxy fn값을 설정한다.
   * 이때 setting값이 trust proxy인경우 프로퍼티를 하나 더 추가한다.
   * "@@symbol:trust_proxy_default" : true
   *
   * 이때 @@symbol:trust_proxy_default의 경우 Object.defineProperty로 추가하였기 때문에 enumerable이 기본값인 false로 설정되어있다.
   */

  return this;
};

compileETag

/**
 * val의 타입이 함수인경우 해당 함수를 그대로 리턴하고,
 * true 또는 "weak"인경우 wetag 함수를 리턴,
 * "strong"인 경우 etag 함수를 리턴,
 * false인경우 val자체 리턴,
 * 그 외의 값이 들어오는 경우 타입에러를 발생시킨다.
 */

exports.compileETag = function (val) {
  var fn;

  if (typeof val === "function") {
    return val;
  }

  switch (val) {
    case true:
      fn = exports.wetag;
      break;
    case false:
      break;
    case "strong":
      fn = exports.etag;
      break;
    case "weak":
      fn = exports.wetag;
      break;
    default:
      throw new TypeError("unknown value for etag function: " + val);
  }

  return fn;
};

exports.etag = createETagGenerator({ weak: false });

exports.wetag = createETagGenerator({ weak: true });

createETagGenerator

/**
 * body가 Buffer인지 확인하여 Buffer가 아닌경우 Buffer로 만들어 etag함수를 호출한다.
 */
function createETagGenerator(options) {
  return function generateETag(body, encoding) {
    var buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;

    return etag(buf, options);
  };
}

Node.js etag 모듈

/*!
 * etag
 * Copyright(c) 2014-2016 Douglas Christopher Wilson
 * MIT Licensed
 */

"use strict";

/**
 * Module exports.
 * @public
 */

module.exports = etag;

/**
 * Module dependencies.
 * @private
 */

var crypto = require("crypto");
var Stats = require("fs").Stats;

/**
 * Module variables.
 * @private
 */

var toString = Object.prototype.toString;

/**
 * Generate an entity tag.
 *
 * @param {Buffer|string} entity
 * @return {string}
 * @private
 */

function entitytag(entity) {
  if (entity.length === 0) {
    // fast-path empty
    return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
  }

  // compute hash of entity
  var hash = crypto
    .createHash("sha1")
    .update(entity, "utf8")
    .digest("base64")
    .substring(0, 27);

  /**
   * createHash : 매개변수로 전달한 알고리즘에 해당하는 Hash 클래스 반환
   *
   * update : 해싱할 데이터를 매개변수로 전달
   *
   * digest : 매개벼수로 전달한 인코딩형식으로 해싱된 값을 가져온다.
   */

  // compute length of entity
  var len =
    typeof entity === "string"
      ? Buffer.byteLength(entity, "utf8")
      : entity.length;

  /**
   * entity의 길이를 가져온다.
   */

  return '"' + len.toString(16) + "-" + hash + '"';
}

/**
 * Create a simple ETag.
 *
 * @param {string|Buffer|Stats} entity
 * @param {object} [options]
 * @param {boolean} [options.weak]
 * @return {String}
 * @public
 */

/**
 * entity : Stats or string or Buffer만 가능
 *
 * options.weak의 타입이 boolean인경우 해당값을 기준으로 strong weak을 결정한다.
 *
 * options개체가 없거나 options.weak의 데이터 타입이 boolean이 아닌경우 entity가 Stats개체이면 weak 아니면 strong이다.
 *
 */

function etag(entity, options) {
  if (entity == null) {
    throw new TypeError("argument entity is required");
  }

  // support fs.Stats object
  var isStats = isstats(entity);
  var weak =
    options && typeof options.weak === "boolean" ? options.weak : isStats;
  /**
   * options값이 존재하고 options.weak 프로퍼티의 타입이 boolean인경우 options.weak에 설정되어 있는 값을 weak값으로 설정한다.
   * 그 이외의 경우 entity 데이터 타입의 Stats개체 여부에 따라 결정된다.
   */

  // validate argument
  if (!isStats && typeof entity !== "string" && !Buffer.isBuffer(entity)) {
    throw new TypeError("argument entity must be string, Buffer, or fs.Stats");
  }
  /**
   * entity값이 Stats개체가 아닌경우 entity의 타입은 string 또는 Buffer가 되어야한다.
   *
   * Buffer : Node.js에서 제공하는 Binary의 데이터를 담을 수 있는 Object
   */

  // generate entity tag
  var tag = isStats ? stattag(entity) : entitytag(entity);

  return weak ? "W/" + tag : tag;
}

/**
 * Determine if object is a Stats object.
 *
 * @param {object} obj
 * @return {boolean}
 * @api private
 */

/**
 * isstats
 * 입력받은 인자의 타입이 Stats인지 확인하는 함수
 * 인자가 Stats이거나 Stats의 조건을 가지고 있는 경우 true를 리턴한다.
 */

/**
 * typeof와 instanceof의 차이점
 *
 * typeof : 피연산자의 데이터 타입을 반환하는 연산자
 *
 * instanceof : 개체가 특정 클래스의 인스턴스인지 여부를 나타내는 boolean값으로 반환하는 비교 연산자
 */

/**
 *
 * fs.statSync : 파일 경로에 대한 정보(Stats개체)를 리턴하는 함수
 *
 * Stats개체 예시
 *
 * Stats {
 *  dev: 16777220,
 *  mode: 33188,
 *  nlink: 1,
 *  uid: 502,
 *  gid: 20,
 *  rdev: 0,
 *  blksize: 4096,
 *  ino: 17169940,
 *  size: 255,
 *  blocks: 8,
 *  atimeMs: 1624407156504.2395,
 *  mtimeMs: 1624407155094.901,
 *  ctimeMs: 1624407155094.901,
 *  birthtimeMs: 1624320820424.4988,
 *  atime: 2021-06-23T00:12:36.504Z,
 *  mtime: 2021-06-23T00:12:35.095Z,
 *  ctime: 2021-06-23T00:12:35.095Z,
 *  birthtime: 2021-06-22T00:13:40.424Z
 *}
 */

function isstats(obj) {
  // genuine fs.Stats
  if (typeof Stats === "function" && obj instanceof Stats) {
    return true;
  }

  // quack quack
  return (
    obj &&
    typeof obj === "object" &&
    "ctime" in obj &&
    toString.call(obj.ctime) === "[object Date]" &&
    "mtime" in obj &&
    toString.call(obj.mtime) === "[object Date]" &&
    "ino" in obj &&
    typeof obj.ino === "number" &&
    "size" in obj &&
    typeof obj.size === "number"
  );
}

/**
 * Generate a tag for a stat.
 *
 * @param {object} stat
 * @return {string}
 * @private
 */

/**
 * entity가 Stats개체인경우 etag를 생성하는 함수
 */

function stattag(stat) {
  var mtime = stat.mtime.getTime().toString(16);
  var size = stat.size.toString(16);

  return '"' + size + "-" + mtime + '"';
}
profile
42seoul cadet

0개의 댓글