* 프로그래머스, 타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js) 5기 강의 수강 내용을 정리하는 포스팅.
* 원활한 내용 이해를 위해 수업에서 제시된 자료 이외에, 개인적으로 조사한 자료 등을 덧붙이고 있음.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
const express = require('express');
const app = express();
app.get('/', (req, res) => { res.send('Hello World!'); });
app.listen(3000, () => { console.log('Server is running on port 3000'); });
npm install -g express-generator
Express.js 애플리케이션은 어떻게 생성하는가?
공식문서 상의 순서대로, 서버 경로상에 app.js 파일을 생성하고 위에 예시로 작성한 코드를 붙여넣기해주면 기본 구조 상태로 생성할 수 있다.
혹은 Express.js 애플리케이션 생성기를 사용해볼 수 있다.
템플릿을 통해 Express 프로젝트의 디렉터리와 파일 구조를 자동으로 생성하며, 개발자가 초기 설정 작업에 시간을 절약할 수 있다.
템플릿 엔진, 정적 파일 경로, 미들웨어 설정 등을 포함한 기본적인 앱 구조를 제공한다.
npm 패키지를 통해 애플리케이션 생성기를 설치하고, express '어플리케이션이름'
을 입력해주면 된다.
express '어플리케이션이름'
express myapp
Express 애플리케이션의 기본 구조를 자동으로 생성해주는 도구.
초기 설정 작업 최소화 / 일관된 프로젝트 구조 제공 / 기본 기능 및 템플릿 제공 등에 대단히 유용하다.
다만, 작은 프로젝트에서는 사용할 일도 없는 도구들이 너무 많이 생성된다는 문제가 발생한다.
더구나 정해진 프로젝트 구조 때문에 복잡한 대규모 프로젝트에서는 기본 구조가 방해가 되는 경우도 존재.
- 그래서 프로젝트 구조를 입맛에 맞게 조정하는 경우가 많다.
React, Angular, Vue.js와 같은 프론트엔드 도구와 결합이 잘 안맞는 경우도 존재. 따라서 필요한 경우에 알아서 사용하고, 기본 구조가 자기 입맛에 영 맞지 않는다 싶으면 사용하지 않거나 / 구조를 적절히 수정해서 사용하는게 좋다.
myapp/ # 서버 실행 관련 스크립트를 저장하는 디렉터리
├── app.js ├─# Express 앱의 진입 파일
├── bin/
│ └── www └─# 서버 실행 파일, app.js 파일을 불러와 HTTP 서버를 생성하고 포트를 바인딩
├── public/ ├─# 정적 파일 (CSS, JavaScript, 이미지 등)
│ ├── images/
│ ├── javascripts/
│ └── stylesheets/
├── routes/ ├─# 라우터 파일
│ ├── index.js └─# 기본 경로(/)에 대한 요청을 처리
│ └── users.js └─# /users 경로에 대한 요청을 처리
├── views/ ├─# 템플릿 파일
│ ├── error.pug └─# 오류 페이지를 렌더링하는 템플릿
│ ├── index.pug └─# 루트 경로(/)에 대한 기본 HTML 페이지를 정의
│ └── layout.pug └─# 애플리케이션의 기본 레이아웃 파일, 공통 레이아웃(헤더, 푸터 등)을 정의
├── package.json ├─# 프로젝트 의존성과 스크립트 정의
└── node_modules/ ├─# 설치된 의존성 모듈
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
// 라우터 로드
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
// 뷰 엔진 설정
// 템플릿 파일(뷰)의 위치를 지정
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// 미들웨어 설정
// morgan 미들웨어로, 개발 모드에서 요청 정보를 로그로 출력
app.use(logger('dev'));
// JSON 형식의 요청 본문(body)을 파싱
app.use(express.json());
// URL-encoded 데이터를 파싱
app.use(express.urlencoded({ extended: false }));
// 요청의 Cookie를 파싱하여 사용할 수 있도록 해준다
app.use(cookieParser());
// public 디렉터리의 파일들을 정적 파일로 제공하는 미들웨어
app.use(express.static(path.join(__dirname, 'public')));
// 불러온 라우터를 연결
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
// 404 에러 처리
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
// shebang(쉐뱅), 이 파일이 Node.js 환경에서 실행되어야 함을 명시
#!/usr/bin/env node
/** 모듈 의존성 로드
* Module dependencies.
*/
var app = require('../app');
// 디버깅 메시지를 출력하기 위한 모듈입니다. myapp:server 네임스페이스로 디버깅 메시지를 관리
var debug = require('debug')('myapp:server');
// Express 앱은 HTTP 모듈을 사용하여 서버를 생성하고 실행한다
var http = require('http');
/** 포트 설정
* Get port from environment and store in Express.
*/
// normalizePort: 문자열로 전달된 포트를 숫자 또는 유효한 값으로 변환
// 유효하지 않은 값이 전달될 경우 false를 반환
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
// express.js는 결국 HTTP 기반이기 때문에 createServer를 통해 서버를 생성한다.
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
// 지정된 포트에서 서버를 시작
server.listen(port);
// 서버 실행 중 발생한 에러를 처리하는 이벤트 리스너를 등록
server.on('error', onError);
// 서버가 정상적으로 시작된 경우 호출되는 이벤트 리스너를 등록
server.on('listening', onListening);
/** 포트 유효성 검사
* Normalize a port into a number, string, or false.
*/
// 입력된 포트를 유효한 값(숫자, 파이프)으로 변환
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
return val; // 포트가 숫자가 아니면 (named pipe), 그대로 반환
}
if (port >= 0) {
return port; // 포트 번호가 양수면 그대로 반환
}
return false; // 유효하지 않은 포트는 false 반환
}
/** 에러 처리
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error; // listen과 관련 없는 에러는 그대로 던짐
}
var bind = typeof port === 'string'
? 'Pipe ' + port // 포트가 파이프일 경우
: 'Port ' + port; // 포트가 숫자일 경우
// 특정 에러 유형에 따른 메시지 처리
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1); // 권한 문제
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1); // 포트 중복 사용
break;
default:
throw error; // 기타 에러는 그대로 던짐
}
}
/** 서버 시작 완료 처리
* Event listener for HTTP server "listening" event.
*/
// 서버가 정상적으로 시작된 경우 호출
function onListening() {
var addr = server.address();
// 포트 또는 파이프 여부에 따라 메시지 생성
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
ES6(ECMAScript 2015)에서 도입된 간결한 문법.
기존의 함수 선언 방식보다 짧게 함수를 작성할 수 있다.
// 기본 함수 표현식
const add = function(a, b) {
return a + b;
};
// 화살표 함수 표현식
const add = (a, b) => a + b;
const square = x => x * x;
const greet = name => `Hello, ${name}`;
const sayHi = () => 'Hi!';
상세 내용은 이전 포스팅 참조.
일반 함수에서 this는, 호출한 객체를 가르킨다. 다만 호출 방식에 따라 this가 가르키는 대상이 달라진다.
화살표 함수에서 this는 자신이 선언된 위치를 가르킨다.
- 그래서 call, apply, bind로 this가 가르키는 위치를 변경할 수 없다.
// 일반 함수
function sum() {
console.log(arguments); // [1, 2, 3]
}
// 화살표 함수
const sum = (...args) => {
console.log(args); // [1, 2, 3]
};
sum(1, 2, 3);
const Person = (name) => {
this.name = name;
};
const john = new Person('John'); // TypeError: Person is not a constructor
//
//
const obj = {
value: 10,
method: () => console.log(this.value)
};
obj.method(); // undefined (this는 obj가 아닌 상위 컨텍스트를 참조)