[Day 31] Express + EJS + Web Form + 클래스(1)
Express
- Express Official Manual
- Node.js 생태계에서 가장 널리 쓰이는 웹 프레임워크
- 내장하고 있는 기능은 매우 적으나, 미들웨어를 주입하는 방식으로 기능을 확장하는 생태계를 가지고 있다.
Express 앱의 기본 구조
// Express 인스턴스 생성
const app = express()
// 미들웨어 주입
app.use(sessionMiddleware())
app.use(authenticationMiddleware())
// 라우트 핸들러 등록
app.get('/', (request, response) => {
response.send('Hello express!')
})
// 서버 구동
app.listen(3000, () => {
console.log('Example app listening on port 3000!')
})
Routing
// HTTP 요청 메소드(GET, POST, ...)와 같은 이름의 메소드를 사용
app.get('/articles', (req, res) => {
res.send('Hello Routing!')
})
// 특정 경로에만 미들웨어를 주입하는 것도 가능
app.post('/articles', bodyParserMiddleware(), (req, res) => {
database.articles.create(req.body)
.then(() => {
res.send({ok: true})
})
})
// 경로의 특정 부분을 함수의 인자처럼 입력받을 수 있음
app.get('/articles/:id', (req, res) => {
database.articles.find(req.params.id) // `req.params`에 저장됨
.then(article => {
res.send(article)
})
})
Request 객체
- 요청(request)의 구성요소
- 메소드
- 주소
- 헤더
- 바디
- req.body
- 요청 바디를 적절한 형태의 자바스크립트 객체로 변환하여 이곳에 저장한다. (body-parser 미들웨어에 의해 처리된다.)
- req.ip
- 요청한 쪽의 IP
- req.params
- route parameter
- req.query
- query string이 객체로 저장된다.
- req.get(…)
- 요청에 표함된 특정 헤더의 값을 가져온다.
- ex)
req.get('X-Custom-Header')
Response 객체
- 응답(response)의 구성요소
- 상태코드
- 헤더
- 바디
- res.status(…)
- 응답의 상태 코드를 지정하는 메소드
- res.append(…)
- 응답의 헤더를 지정하는 메소드
- res.send(…)
- 응답의 바디를 지정하는 메소드
- 인자가 텍스트면 text/html, 객체면 application/json 타입으로 응답한다.
- res.set(…)
- 응답에 새로운 헤더를 지정한다.
- ex)
res.set('X-Custom-Header', value)
- res.end()
- 응답을 보낸다.
- res.send(…)와 비슷하지만, 아무런 인자를 받지 않고 그냥 보낸다.
Template Language
Template Engine
- 템플릿과 데이터를 결합해 문서를 생성하는 프로그램, 혹은 라이브러리
- 템플릿을 작성할 때 사용하는 언어를 템플릿 언어라고 한다.
EJS
-
Node.js 생태계에서 가장 많이 사용되는 템플릿 엔진
-
JavaScript 코드를 템플릿 안에서 그대로 쓸 수 있음
-
EJS 사용 예시
<%# index.ejs %> <html> <head> <title><%= title %></title> </head> <body> <div class="message"> <%= message %> </div> <% if (showSecret) { %> <div>my secret</div> <% } %> </body> </html>
-
EJS는 서버에서 HTML을 만져서 프론트로 응답을 넘기는 방식이다.
- 템플릿과 데이터를 합쳐서 HTML을 만들어낸 뒤 응답으로 보낸다.
- react, angular 등 프론트엔드 라이브러리(프레임워크)의 등장으로 이러한 방식의 변화가 일어난다. (서버가 아닌 브라우저에서 HTML을 만지는 방식으로 변화한다.)
Web Form
HTML form의 기본 동작
-
HTML form을 전송하면, 입력된 정보가 기본적으로 percent encoding 되어 요청된다.
-
GET Method : 입력된 정보가 query string 으로 날라간다.
GET /search?query=%EA%B0%9C&sort=latest HTTP/1.1 ...
-
POST Method : 입력된 정보가 요청 바디에 포함되서 날라간다.
POST /form HTTP/1.1 Content-Type: application/x-www-form-urlencoded // Content-Type은 요청 바디에 포함되어 있는 정보가 어떤 형식인지 보여준다. ... home=Cosby&favorite+flavor=flies
-
-
전송된 문서의 타입(MIME 타입)
UUID
-
일종의 난수 형태를 띄는 식별자로 사용하기 위해 고안된 수 형식
-
node에서 UUID를 사용하기 위해서는 npm 패키지
uuid
를 사용하면 된다.const uuidv4 = require('uuid/v4') const todos = [ { id: uuidv4(), // 함수를 실행하면 난수 문자열이 return 된다. title: 'sample todo' } ]
Redirect after submission (전통적인 웹 개발에서)
- form을 submit 하고난 뒤 redirect 시켜주지 않으면 새로고침 할 경우 이전과 같은 요청이 한번 더 가게된다.
- form에서 post 요청을 보냈을 경우 redirect 시켜주지 않고 새로고침 하면 계속 post 요청을 보내는 결과를 만든다.
- redirect를 통해 post 요청을 완료시키고 get 요청으로 우회해서 페이지를 보여주는 것이 가능하다.
클래스 (Class)
Class (ES2015)
-
ES2015 이전에는 비슷한 종류의 객체를 만들어내기 위해 생성자(Constructor)를 사용했었다.
-
구버전 브라우저에서도 클래스를 사용할 수 있도록 하기 위해 클래스는 생성자를 기반으로 만들어졌고, 생성자의 기능을 대체한다. (Transpiler를 통해 클래스는 모두 생성자로 변경된다.)
-
클래스 사용 예제
// 클래스 class Person { // 이전에서 사용하던 생성자 함수는 클래스 안에 `constructor`라는 이름으로 정의합니다. constructor({name, age}) { this.name = name; this.age = age; } // 객체에서 메소드를 정의할 때 사용하던 문법을 그대로 사용하면, 메소드가 자동으로 `Person.prototype`에 저장됩니다. introduce() { return `안녕하세요, 제 이름은 ${this.name}입니다.`; } } const person = new Person({name: '윤아준', age: 19}); console.log(person.introduce()); // 안녕하세요, 제 이름은 윤아준입니다. console.log(typeof Person); // function console.log(typeof Person.prototype.constructor); // function console.log(typeof Person.prototype.introduce); // function console.log(person instanceof Person); // true
-
class
블록을 정의할 때에는 기존의 JS와는 다른 별도의 문법으로 코드를 작성해야 한다.// 클래스는 함수가 아닙니다! class Person { console.log('hello'); } // 에러: Unexpected token
// 클래스는 객체가 아닙니다! class Person { prop1: 1, prop2: 2 } // 에러: Unexpected token
-
생성자와 클래스의 동작방식의 차이점
- 클래스는 함수로 호출될 수 없다.
- 클래스 선언은
let
과const
처럼 블록 스코프에 선언되며, 호이스팅이 일어나지 않는다. - 클래스의 메소드 안에서
super
키워드를 사용할 수 있다.
메소드 정의하기
class Calculator {
add(x, y) {
return x + y;
}
subtract(x, y) {
return x - y;
}
}
-
임의의 표현식을 대괄호로 둘러싸서 메소드의 이름으로 사용할 수도 있다.
const methodName = 'introduce'; class Person { constructor({name, age}) { this.name = name; this.age = age; } // 아래 메소드의 이름은 `introduce`가 됩니다. [methodName]() { return `안녕하세요, 제 이름은 ${this.name}입니다.`; } } console.log(new Person({name: '윤아준', age: 19}).introduce()); // 안녕하세요, 제 이름은 윤아준입니다.
-
Getter / Setter 도 정의할 수 있다.
class Account { constructor() { this._balance = 0; } get balance() { return this._balance; } set balance(newBalance) { this._balance = newBalance; } } const account = new Account(); account.balance = 10000; account.balance; // 10000
-
static
키워드를 메소드 이름 앞에 붙여주면 해당 메소드는 정적 메소드가 된다.class Person { constructor({name, age}) { this.name = name; this.age = age; } // 이 메소드는 정적 메소드입니다. static sumAge(...people) { return people.reduce((acc, person) => acc + person.age, 0); } } const person1 = new Person({name: '윤아준', age: 19}); const person2 = new Person({name: '신하경', age: 20}); Person.sumAge(person1, person2); // 39
-
Generator 메소드를 정의하려면 메소드 이름 앞에
*
를 붙여주면 된다.-
Symbol.iterator
메소드를 generator로 정의하면 클래스의 인스턴스를 iterable로 만들 수 있다.class Gen { *[Symbol.iterator]() { yield 1; yield 2; yield 3; } } // 1, 2, 3이 차례대로 출력됩니다. for (let n of new Gen()) { console.log(n); }
-
클래스 필드 (Class Field)
-
클래스 블록 안에서 할당 연산자
=
를 이용해 인스턴스 속성을 지정할 수 있는 문법.class Counter { static initial = 0; // static class field count = Counter.initial; // class field inc() { return this.count++; } } const counter = new Counter(); console.log(counter.inc()); // 0 console.log(counter.inc()); // 1 Counter.initial = 10; console.log(new Counter().count); // 10
-
클래스 필드는 아직 정식 표준 기능은 아니지만 많이 사용하고 있다. 현재는 트랜스파일러를 이용해 이 기능을 사용할 수 있다.
클래스 필드와 this
-
class
블록은 새로운 블록 스코프를 만든다. 그리고 이 내부에서 사용된this
는 인스턴스 객체를 가리키게 된다.class MyClass { a = 1; b = this.a; } new MyClass().b; // 1
-
이러한 성질을 이용해서 화살표 함수를 통해서 메소드를 정의할 수 있다.
- 메소드를 다른 함수의 인수로 넘겨줘야 하는 경우 화살표 함수를 사용하는 것이 좋다.
class MyClass { a = 1; getA = () => { return this.a; } } new MyClass().getA(); // 1
- 일반적인 메소드는 클래스의
prototype
속성에 저장되지만, 클래스 필드는 인스턴스 객체에 저장된다. - 화살표 함수의
this
는 호출 형태에 관계없이 항상 인스턴스 객체를 가리키게 된다.- 이러한 성질 때문에 메소드를 값으로 다뤄야 할 경우 화살표 함수를 사용하는 경우가 있다.
- 클래스 필드를 통해 정의한 메소드는 인스턴스를 생성할 때마다 새로 생성되기 때문에 메모리를 많이 차지한다.
댓글남기기