lib/application/defaultConfiguration
this.set("view", View);
this.set("views", resolve("views"))
- defaultConfiguration 함수에서 View 생성자 함수와 view에대한 기본 디렉토리 경로를 설정한다.
- View 생성자 함수 : 사용할 View 이름과 옵션 입력하면 해당 View를 express에서 사용할 수 있도록 개체화하여 리턴한다.
- 기본 디렉토리 경로 : express가 작동하고 있는 지점의 절대 경로 + /views를 합쳐서 만든다.
* express가 작동하고 있는 지점의 절대 경로 : /Users/user/Documents
- View 기본 디렉토리 경로 : /Users/user/Documents/views
lib/view.js
/*!
* express
* Copyright(c) 2009-2013 TJ Holowaychuk
* Copyright(c) 2013 Roman Shtylman
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict';
/**
* Module dependencies.
* @private
*/
var debug = require('debug')('express:view');
var path = require('path');
var fs = require('fs');
/**
* Module variables.
* @private
*/
var dirname = path.dirname;
var basename = path.basename;
var extname = path.extname;
var join = path.join;
var resolve = path.resolve;
/**
* Module exports.
* @public
*/
module.exports = View;
/**
* Initialize a new `View` with the given `name`.
*
* Options:
*
* - `defaultEngine` the default template engine name
* - `engines` template engine require() cache
* - `root` root path for view lookup
*
* @param {string} name
* @param {object} options
* @public
*/
/**
*
* View가 저장되어있는 경로를 입력 -> 해당 View를 express에서 사용할 수 있도록 개체화
*/
function View(name, options) {
/**
* View는 render함수에서 사용된다.
* render의 첫번째 프로퍼티를 name으로 전달받는다.
* 랜더링할 파일 명을 의미한다.
*/
/**
* options
*
* defaultEngine : app settings 프로퍼티의 "view engine" 값 / 템플릿 엔진을 가져오는 부분 / 예 : app.set("view engine", "pug");
* root : views 폴더 까지의 경로
* engines : app engines 프로퍼티 / app.init() 시점에는 빈 개체({})이다.
*/
var opts = options || {};
this.defaultEngine = opts.defaultEngine;
this.ext = extname(name);
/**
* extname : 경로안에서 확장자를 추출하는 함수
* ext에는 추출한 확장자가 들어온다.
*/
this.name = name;
this.root = opts.root;
if (!this.ext && !this.defaultEngine) {
/**
* 템플릿 엔진과 확장자를 추출하는 함수가 없는 경우 에러를 던진다.
*/
throw new Error('No default engine was specified and no extension was provided.');
}
var fileName = name;
if (!this.ext) {
/**
* 확장자 추출 함수가 없는경우
* defaultEngine을 통해 확장자를 추출후 확장자를 추가한다.
*/
// get extension from default engine name
this.ext = this.defaultEngine[0] !== '.'
? '.' + this.defaultEngine
: this.defaultEngine;
fileName += this.ext;
}
if (!opts.engines[this.ext]) {
/**
* engines 프로퍼티에 해당 템플릿 엔진이 없는경우
* ext를 이용하여 엔진을 설정해준다.
*/
// load engine
var mod = this.ext.substr(1)
debug('require "%s"', mod)
// default engine export
var fn = require(mod).__express
if (typeof fn !== 'function') {
throw new Error('Module "' + mod + '" does not provide a view engine.')
}
opts.engines[this.ext] = fn
}
// store loaded engine
this.engine = opts.engines[this.ext];
// lookup path
this.path = this.lookup(fileName);
/**
* engine 프로퍼티와 path프로퍼티를 설정한다.
*/
}
/**
* Lookup view by the given `name`
*
* @param {string} name
* @private
*/
/**
*
* 파일명 in -> 파일 경로 out
*/
View.prototype.lookup = function lookup(name) {
var path;
var roots = [].concat(this.root);
/**
* this.root프로퍼티를 오염시키지 않도록 새로운 roots 배열을 만든다.
*/
/**
* 근데 왜 굳이 배열을 만들어 반복문을 돌릴까?
*
* express 라이브러리상에서는 this.root의 값이 배열로 들어올 일은 없음
* 따라서 반복문이 작동할 이유는 없다.
*
* 만약 this.settings["views"]의 값을 배열로 바꾼 경우
* 배열의 마지막 값을 가지고 path를 만든다
*/
debug('lookup "%s"', name);
for (var i = 0; i < roots.length && !path; i++) {
var root = roots[i];
// resolve the path
var loc = resolve(root, name);
/**
* views 디렉토리까지 설정된 경로에다가 name을 합친다.
* <views 디렉토리까지의 경로>/<name>
*/
var dir = dirname(loc);
/**
* 합친 경로에서 views디렉토리까지의 경로를 다시 가져온다.
*/
var file = basename(loc);
/**
* 합친 경로에서 파일명을 다시 가져온다.
*/
// resolve the file
path = this.resolve(dir, file);
}
/**
* 굳이 resolve dirname basename 처리를 하는 이유
*
* name에 절대 경로를 넣는경우 처리하기 위해
*
* name에 절대 경로가 들어가면 resolve는 root를 무시한다.
*/
return path;
};
/**
* Render with the given options.
*
* @param {object} options
* @param {function} callback
* @private
*/
View.prototype.render = function render(options, callback) {
debug('render "%s"', this.path);
this.engine(this.path, options, callback);
};
/**
* Resolve the file within the given directory.
*
* @param {string} dir
* @param {string} file
* @private
*/
View.prototype.resolve = function resolve(dir, file) {
var ext = this.ext;
// <path>.<ext>
var path = join(dir, file);
var stat = tryStat(path);
if (stat && stat.isFile()) {
return path;
}
/**
* 해당 경로에 파일이 있는경우 파일 경로를 리턴한다.
*/
// <path>/index.<ext>
path = join(dir, basename(file, ext), 'index' + ext);
stat = tryStat(path);
/**
* 경로에 파일이 없는경우 파일명에 해당하는 디렉토리를 찾고, 해당 디렉토리에 index파일이 있는지 확인한다.
*/
if (stat && stat.isFile()) {
return path;
}
/**
* 있으면 리턴한다.
*/
};
/**
* Return a stat, maybe.
*
* @param {string} path
* @return {fs.Stats}
* @private
*/
function tryStat(path) {
debug('stat "%s"', path);
try {
return fs.statSync(path);
/**
* 주어진 파일 경로에대한 정보를 리턴한다.
*/
} catch (e) {
return undefined;
}
}
View 생성자 함수
- 개체화할 View의 파일명과 옵션 개체를 입력받아 프로퍼티(defaultEngine, ext, name, root, engine, path) 값을 초기화 하고 개체를 리턴한다.
- defaultEngine 프로퍼티(문자열) : 템플릿 엔진의 이름을 저장한다. app개체에 저장된 값을 옵션 개체를 통하여 전달받는다.
* 예: app.set("view engine", "pug"); -> defaultEngine === "pug"
- ext 프로퍼티 : 템플릿의 확장자
개체화할 View의 파일명에 확장자가 있는경우 : 해당 확장자를 가져온다.
개체화할 View의 파일명에 확장자가 없는경우 : defaultEngine으로부터 확장자를 가져온다.
- name 프로퍼티(문자열) : 개체화할 View의 파일명
- root 프로퍼티(문자열) : view에대한 기본 디렉터리 경로. app개체에 저장된 값을 옵션 개체를 통하여 전달받는다.
- engine 프로퍼티(함수) : 템플릿 엔진 함수.
ext프로퍼티를 이용하여 노드 모듈을 가져온다.(const fn = require(this.ext.substr(1)).__express)
view의 engine프로퍼티를 해당 모듈로 초기화한다.
application의 engines프로퍼티에 해당 템플릿 엔진이 없는경우 ext를 key로 하여 프로퍼티로 추가한다.
- path 프로퍼티(문자열) : 입력받은 View파일의 경로를 저장한다.
lookup, resolve 메서드
loopup
- name과 root값을 이용하여 path값을 리턴한다.
- name이 절대 경로로 주어진 경우 root의 값은 사용하지 않는다.
- root의 값이 하나 이상으로 주어지는 경우 가장 마지막 값으로 path를 설정한다.
resolve
- path에 실제 파일이 있는지 확인한다.
- 만약 path에 실제 파일이 없는경우 주어진 path를 디렉토리로 가정하여, 디렉토리 내부에 index파일이 있는지 확인힌다.
/Document/repository/src/views/test.pug -> /Document/repository/src/views/test/index.pug