Routing은 어플리케이션이 특정 엔드포인트(URI, 또는 경로)와 특정 HTTP 요청 메서드(get
, post
등)에 의한 클라이언트 요청에 어떻게 응답할지를 결정하는 것을 의미합니다.
(공식문서 상의 Routing의 정의)
app.METHOD(PATH, HANDLER)
PATH
: 서버 상에서의 PathHANDLER
: Path가 매치되었을 때 실행할 함수위에서 app.get의 첫번째 파라미터로 경로를 넘겨줬는데, 이러한 요청(ex. get
, post
, delete
, put
)메소드와 결합된 Route Path는 요청이 이루어질 수 있는 엔드포인트를 정의합니다.
이 Route Path는 문자열, 문자열 패턴, 정규식의 형태를 허용합니다.
?
, +
, *
, ()
와 같은 문자는 정규식에 사용됩니다.hyphen(-)
, dot(.)
은 문자열 기반의 path에 사용됩니다.$
를 문자열 기반의 path 내부에 사용하고 싶다면, ([
와 ])
사이에 escaped된 형태로 사용해야 합니다."data/$book"
으로 라우트하고 싶으면 "data/([\$])book"
으로 작성합니다.]app.get('/', (req, res) => {
res.send('root')
}
app.get('/about', (req, res) => {
res.send('about')
}
//ab?cd는 acd 또는 abcd로 매칭됩니다.
app.get('/ab?cd', (req, res) => {
res.send('ab?cd')
}
//ab+cd는 abcd, abbcd, abbbcd, ... 로 매칭됩니다.
app.get('/ab+cd', (req, res) => {
res.send('ab+cd')
}
//ab*cd는 abcd, abxcd, abRANDOMcd, ab123cd, ... 로 매칭됩니다.
app.get('/ab*cd', (req, res) => {
res.send('ab*cd')
}
//ab(cd)?e는 abe 또는 abcde로 매칭됩니다.
app.get('/ab(cd)?e', (req, res) => {
res.send('ab(cd)?e')
}
///a/는 내부에 a를 포함하고 있는 모든 경로와 매칭됩니다.
app.get('/a/', (req, res) => {
res.send('/a/')
}
///,*fly$/는 butterfly, dragonfly와 같이 fly로 끝나는 모든 경로와 매칭됩니다.
app.get('/.*fly$/', (req, res) => {
res.send('/.*fly$/')
}
다음과 같이 Route Parameter를 사용하면 파라미터를 경로에 전달할 수 있으며, 전달된 파라미터를 캡처할 수 있습니다. 캡처된 값은 경로에 지정된 Route Parameter 각 변수의 이름을 key로 사용하여 req.params
객체에 저장됩니다.
Route Path: /users/:userId/books/:bookId
Request URL: http://localhost:8080/users/34/books/8989
req.params: { "userId": 34, "bookId": 8989 }
app.get('users/:userId/books/:bookId', (req, res) => {
res.send(req.params)
}
hyphen(-
)과 dot(.
)은 문자 그대로 해석되므로 다음과 같이 Route Parameter와 연결해서 사용할 수 있습니다.
-
) 사용 예시Route Path: /flights/:from-:to
Request URL: http://localhost:8080/flights/LAX-SFO
req.params: { "from": "LAX", "to": "SFO" }
.
) 사용 예시Route Path: /plantae/:genus.:species
Request URL: http://localhost:8080/plantae/Prunus.persica
req.params: { "genus": "Prunus", "species": "persica" }
클라이언트의 Request를 처리하기 위해 미들웨어처럼 동작하는 여러개의 Callback 함수를 전달할 수 있습니다.
콜백함수에는 총 3가지의 파라미터가 들어갑니다.
Router Handlers는 함수, 함수들의 배열, 혹은 앞의 두 형태의 혼합형식으로 사용 가능합니다.
app.get('/example/a', (req, res) => {
res.send('Hello!')
}
app.get('/example/b', (req, res, next) => {
console.log('response는 next function이 가져다 줄 거에요~')
next()
}, (req, res) => {
res.send('이제서야 response를 보냅니다!')
})
const cb0 = function(req, res, next) {
console.log('CB0')
next()
}
const cb1 = function(req, res, next) {
console.log('CB1')
next()
}
const cb2 = function(req, res) {
console.log('CB2')
res.send('response 전달!')
}
app.get('/example/c', [cb0, cb1, cb2])
const cb0 = function(req, res, next) {
console.log('CB0')
next()
}
const cb1 = function(req, res, next) {
console.log('CB1')
next()
}
app.get('/example/d', [cb0, cb1], (req, res, next) => {
console.log('the response will be sent by the next function')
next()
}, (req, res) => {
res.send('Hello from D!')
})
아래 테이블에 나열되어 있는 메서드들을 응답 객체(res
)에 사용함으로써 클라이언트에게 응답을 보낼 수 있으며, 클라이언트에게 응답함으로써 request-response cycle을 종료시키게 됩니다. 만약에 아래 테이블에 있는 Response Methods들을 한 개도 호출하지 않는다면 클라이언트의 요청은 계속 보류(대기)될 것입니다.
Method | Description |
---|---|
res.download() | 파일을 다운로드 할 것인지 묻습니다 |
res.end() | Response 프로세스를 종료시킵니다 |
res.json() | JSON 형태로 응답을 전달합니다. |
res.jsonp() | JSONP를 지원하는 JSON 응답을 전달합니다. |
res.redirect() | Request를 redirect 시킵니다. |
res.render() | view template을 렌더해준다. |
res.send() | 다양한 type으로 응답을 전달해준다. |
res.sendFile() | octet 스트림으로 파일을 전달해준다. |
res.sendStatus() | Response status code를 설정하고 해당 status를 문자열로 표현하여 response body에 실어보낸다. |
우리가 실제로 웹사이트를 개발할 때는 app.get( ~ ), app.post( ~ ) 이런 코드를 엄청나게 많이 쓰게 될 것이 분명하다..!
예를 들어, 온라인 쇼핑몰을 만들었다고 치면 다음과 같은 코드가 셀 수 없이 많을 것이다.
app.get('/phone/apple', (req, res) => {
res.send(iPhone정보)
}
app.get('/phone/samsung', (req, res) => {
res.send(Galaxy정보)
}
...
그러면 가독성도 그만큼 떨어지고 작업의 효율이 매우 낮아질 것이다. 그래서 각각 다른 파일로 관리하고 싶을 때 우리는 express의 Router를 사용한다!
server.js
와 같은 디렉토리에 routes
라는 폴더를 만들고, 그 안에 phone.js
라는 파일을 만들어줍니다.
phone.js
에는 다음과 같이 입력해줍니다.
const express = require('express')
const router = express.Router() //express가 제공하는 Router라는 기능 사용
router.get('/phone/apple', (req, res) => {
res.send(iPhone정보)
}
router.get('phone/samsung', (req, res) => {
res.send(Galaxy정보)
}
module.exports = router; //파일 최하단에서 지금 만든 라우터를 export해준다.
//이제 이 phone.js라는 라우터를 server.js에 적용하는 일만 남았다!
💡
require()
와module.exports
의 용도에 대해서 알아보자!
→ Node.js 환경에서 JavaScript 파일들을 볼러와서 쓰기 위함입니다.
modules.exports = 변수명
과 같이 쓰면 해당 변수명을 다른 곳에서 쓸 수 있게 해줍니다.
(함수도 가능해요!)
require('파일경로')
와 같이 쓰면 다른 파일을 불러올 수 있습니다.
즉, 다른 파일에서export
해준 변수를 현재 파일에서require
해서 사용할 수 있는 것입니다.
제일 처음에 우리가require
를 접한 것은require('express')
였는데, 이처럼 파일 경로가 아니라npm
으로 설치한 라이브러리 명을 써도 됩니다!하지만 이렇게 쓰지 않고, 자바스크립트에 새로 도입된 import, export 문법으로도 사용 가능하니 따로 더 공부하면 될 것 같습니다.
server.js
에 다음과 같이 입력합니다.
//사용자가 /phone 경로로 요청하면 실행할 미들웨어 정하기
app.use('/phone', require('./routes/phone.js'))
app.use()는 미들웨어를 사용하고 싶을 때 사용하는 함수입니다.
위의 코드를 이해해보자면, 아까 phone.js
에서 export
해준 라우터를 어떠한 사용자가 /phone
이라는 경로로 요청했을 때 미들웨어로 실행시킨다는 뜻입니다.
이렇게 routes폴더 안에서 라우터들을 관리하면 훨씬 가독성이 올라갈 것 같네요!