Express.js : 라우팅

김종현·2023년 4월 5일
0

Node.js

목록 보기
7/8

라우팅

-클라이언트가 보낸 HTTP 요청을 분기하는 방법을 제공, URL(endpoint) + HTTP method에 맞춰서 알맞은 응답의 경로를 미리 설정함.
-Express는 내부적으로 path-to-regexp 패키지를 가지고 PATH와 클라이언트가 요청을 보낸 endpoint와 매칭을 시킨다. 그러므로 문자열 형태의 정규 표현식을 사용해서 원하는 endpoint에 유연하게 대응할 수도 있다.

  • app.get('/ab+c', (req,res,next) => { do sth });

-Express에서는 기본적으로 app.REST메소드('path', 콜백함수) 로 구현할 수 있다.

const express = require('express')
const app = express();
app.METHOD(PATH, HANDLER)의 구조 = app.route(PATH).METHOD(HANDLER)의 축약판.

Express.js는 크게 app라우팅과 Express.Router를 통한 라우팅으로 나누어짐

1. app 라우팅

-app 객체에 직접 get/post/put/delete 함수를 사용하여 HTTP method로 라우팅 가능.

  • HTTP 요청에 HTTP 메소드가 get/post/put/delete로 들어왔을때 각각 연결되는 라우팅 생성

-공통적으로 적용되는 경로에 사용.

-HTTP method의 첫 인자가 라우팅을 실행할 URL, 마지막 인자가 라우팅이 실행될 때 작동하는 함수
-all 함수를 사용시 HTTP method에 상관없이 라우팅 가능

app.get('/', (req, res) =>{
  res.send('GET /');
});
app.post('/', (req, res) =>{
  res.send('POST /');
});
app.put('/', (req, res) =>{
  res.send('PUT /');
});
app.delete('/', (req, res) =>{
  res.send('DELETE /');
});
app.all('/all', (req, res) =>{
  res.send('ANY /');
});

2. express 라우팅

-app 라우팅을 통해서는 라우팅의 핵심인 그룹화를 지원하지 않음
-Express.Router를 통해 라우팅을 모듈화 할 수 있음
-세부적인 경로를 지정할 때 보통 사용

(1) Router 객체
-라우팅을 조금 더 모듈화해서 관리할 수 있도록 해주는 객체.
-작은 단위의 Express앱 같은 느낌으로 미들웨어와 동일하게 app.use로 Express 앱에 등록할 수가 있다.
-Router를 사용시 index.js 또는 app.js를 작성할 필요가 없게 된다.
-라우팅은 반복되는 부분이 많아, 모듈로 분리해서, Router()로 불러올 수 있다. ⇒ 계층적 구조의 라우터로 그룹화가 가능하다.
EX ) /users/name , /users/phone , /users/address 등의 경로에 대해 모두 index.js에 처리 로직을 작성하지 않고, users 라는 모듈로 분리한 뒤, users 를 라우터로 가져올 수 있다.

(2) Express.Router 모듈
-라우터 객체도 앱 객체처럼 get/put/post/delete 함수 사용 가능. 동작은 동일함 (router.get/put/post/delete)
-첫 인자가 라우팅 될 URL, 마지막 인자가 라우팅 시 실행될 함수
-라우터는 일반적으로 모듈로 만들어서 사용함.

const express = require('express');
const router = express.Router();

router.get('/', (req, res, next) =>{
  res.send('respond');
});

module.exports = router;

(3) Express.Router 사용
-작성된 라우터 모듈을 app에 use함수로 연결하여 사용할 수 있음 <1>
-router 객체에도 하위 라우터를 use 함수로 연결하여 사용할 수 있음 <2>

---routes/users.js
const { Router } = require('express');

const router = Router();

router.get('/', (req, res) => {
    res.send('GET /users');
});

router.post('/', (req, res) => {
    res.send('POST /users');
});

router.put('/', (req, res) => {
    res.send('PUT /users');
});

router.delete('/', (req, res) => {
    res.send('DELETE /users');
});

module.exports = router;

---index.js
const express = require('express');
const userRouter = require('./routes/users')

const app = express();

app.get('/', (req, res) => {
    res.send("OK");
});

// <1>
// 웹페이지에서 /users 경로로 들어오는 모든 URL은 userRouter에서 먼저 처리하게됨.
app.use('/users', userRouter);

app.listen(8080);

--- ./routes/users.js
const petRouter = require('./pets');
const router = express.Router();

// <2>
// userRouter로 들어온 users 하위 경로 중에 /users/pets 경로는 petRouter로 먼저 처리.
router.use('/pets', petRouter);

module.exports = router;

(4) Request Handler

-라우팅에 적용되는 router나 app의 HTTP 메소드의 가장 마지막 인자로 전달되는 함수.
-클라이언트의 요청 정보가 담긴 request와 그 요청에 응답할 수 있도록 도와주는 response, 다음 핸들러 또는 미들웨어를 부르는데 사용하는 next를 매개변수로 갖는다.
-HTTP 요청과 응답을 다루는 함수로 설정된 라우팅 경로에 해당하는 요청이 들어오면 이 함수가 실행됨.

router.get('/:id', (req, res) =>{
  const id = req.params.id
  res.send(`hi ${id}`);
});
// '/:id' 형태의 path parameter를 포함한 경로로 요청이 들어오면 실행.
// path paramter의 id를 받아서 메시지 출력

ⓐ Request 객체
-HTTP 요청 정보를 가진 객체
-HTTP 요청의 path parameter, query paramter, body, header(어떤 사용자가 어떤 값을 요청하는지 들어있음) 등을 확인 가능

  • req.queries : '?'를 사용해서 뒤에 붙는 값들을 'query paramter'라 하는데 이것은 req.queries 값을 참조해서 사용할 수 있음
  • req.get('헤더의 이름'): 헤더의 이름을 넣어 호출시 헤더 값 가져옴

ⓑ Response 객체
-HTTP 응답을 처리
-HTTP 응답 데이터를 전송하거나 응답 상태 및 헤더를 설정

ⓒ next 파라미터 사용

app.get('/', (requ, res, next) =>{
	console.log('one');
  	next();
}, (req, res, next) => {
  	console.log('two');
  	next();
});

//middleware
app.use((req, res, next) => {
  	console.log('three');
  	next();
});

./route.js
1. index.js에 처리 로직을 작성하던것을 별도의 파일에 작성하였다.
2. app.get이 아닌 router에 연결하였다.
3. 별도의 모듈로 분리하였다.

const express = require('express');
const path = require('path');
const router = express.Router(); // 라우터 분리
router.get('/name', (req, res) => { // app 대신 router에 연결
res.sendFile(path.join(__dirname, 'html', 'main.html'));
});
router.get('/phone', (req, res) => { // app 대신 router에 연결
res.sendFile(path.join(__dirname, 'html', 'phone.html'));
});
router.get('/address', (req, res) => { // app 대신 router에 연결
res.sendFile(path.join(__dirname, 'html', 'address.html'));
});
module.exports = router;

index.js
1. 별도의 모듈로 분리한 모듈을 require를 통해 불러온다.
2. app.use를 통해 /users 경로 아래의 요청은 route 모듈에서 처리하도록 설정한다.

const route = require('./route.js');
...
app.use('/users', route);
....
app.listen(8080, ()=>{
  console.log('connected')
});

path parameter

-Express.js 라우팅은 path paramter를 제공.
-path parameter를 사용하면 주소의 일부를 변수처럼 사용 가능
-Path parameter는 URI 내에 일부 값을 동적으로 설정하고자 할 때 사용된다. 일반적으로 :
을 사용하여 표시하며, 해당 값은 req.params 객체를 통해 얻을 수 있다.

app.get('/post/:id', () => {});
app.get('/post/a', () => {});

위 예제 코드에서 /post/a 요청이 오면, app.get('/post/a', () => {}); 에서 요청을 처리할 것이라고 생각하지만,
app.get('/post/:id', () => {}); 에서 요청을 처리한다.
-익스프레스는 코드써진 순서로 처리하기 때문.

  1. /users/:id -> /users/123, /users/456 등으로 접속시 라우팅 적용
    /messages/:from-:to -> /message/123-456 등으로 접속시 라우팅 적용
app.get('/users/:userId', (req, res) => {
const userId = req.params.userId;
// 유저 정보를 가져와서 응답 처리
});
const express = require('express');

const app = express();

app.get('/', (req, res) => {
    res.send("OK");
});
// 패스 파라미터를 이용하여 /say뒤에 오는 경로에 따라오는 문자열을 통해 출력.
app.get('/say/:anyword', (req, res) => {
    const { anyword } = req.params;
    res.send(anyword);
});

app.listen(8080);

계층적 구조의 라우터 사용

-계층적 구조의 라우터를 사용시 Router({ mergeParams: true })를 사용해야 이전 라우터에서 전달된 path parameter를 사용가능.

---index.js
const express = require('express');
const userRouter = require('./routes/users');
const app = express();

app.get('/', (req, res) => {
  res.send('done');
});

app.use('/users', userRouter);

app.listen(8080)

---routes/users.js
const { Router } = require('express');
const subsRouter = require('./characters');
const router = Router();

router.get('/', (req, res) => {
    res.send('GET /users');
});

// /users 경로 하위에 다음과 같은 하위 라우터를 연결, 해당 경로는 charactersRouter가 처리.
router.use('/:userId/characters', charactersRouter)

module.exports = router;

---characters.js
const { Router } = require('express');

const router = Router({ mergeParams: true });

//하위로 들어가는 라우터이므로 루트패스('/')만 사용해도됨.
// '/users/:userId/characters'를 처리하는 라우터.
router.get('/', (req, res)=>{
  // 아래 두 줄은 Characters of user ${req.params.userId}와 동일.
  // 가독성을 위해 구조 분해 할당.
    const { userId } = req.params;
    res.send(`Characters of user ${userId}`);
})

module.exports = router;

Query String ( Query Parameter )

Query parameter는 URI 뒤에 ? 를 붙이고 key=value 형태로 전달하는 방식이다. 다수의 쿼리 파라미터를 전달할 경우에는 & 를 이용하여 구분
한다. 이 값은 req.query 객체를 통해 얻을 수 있다.
예를 들어, /users?name=john&age=30 와 같은 URI에서 name 과 age 는 query parameter이다.

쿼리 파라미터를 이용한 검색 기능을 구현할 수 있다.

app.get('/users', (req, res) => {
const name = req.query.name;
const age = req.query.age;
// 이름과 나이를 이용한 검색 기능을 구현하여 응답 처리
});

Request Body

req.params/ req.query/ req.body

출처 : https://inpa.tistory.com/entry/EXPRESS-%F0%9F%93%9A-reqparams-reqquery-reqbody-%F0%9F%A4%94-%EC%A0%95%EB%A6%AC

1. req.params

-라우터의 매개변수

  • /:id/:name 경로가 있으면 ":id"속성과 ":name"속성을 req.params.id, req.params.name으로 사용할 수 있다.
// 요청 url : www.example.com/public/100/kim

router.get('/:id/:name', (req, res, next) =>{
  
  // ../100/kim 부분이 담긴다.
  // {id: '100', name 'jun'}
  console.log(req.params)
  
});

2. req.query

-경로의 각 쿼리 문자열 매개 변수에 대한 속성이 포함 된 개체. (주로 GET 요청에 대한 처리)
-www.example.com/post/1/jun?title=hello!을 받으면 ?뒷 부분인 title=hello! 부분을 객체로 매개변수의 값을 가져온다.

---클라이언트
//클라이언트 단에서, Js로 get 요청을 보냄

// 방식 1
await axios.get(`www.example.com/post/1/jun?title=hello!`)

// 방식 2
await axios({
  method: "get",
  url: `www.example.com/post/1/jun`,
  params: { title: 'hello!' },
})

---서버

// 요청온 url : www.example.com/public/100/kim?title=hello!
// urlencoded : url 방식 폼 요청 들어오면 파싱
app.use(express.urlencoded({ extended: false })); 

router.get('/:id/:name', (req, res, next) => {

  //../100/jun 부분이 담기게 된다.
  // { id: '100', name: 'kim' }
  console.log(req.params) 

  
  // url에서 ?의 뒷부분인 title=hello! 부분이 담기게 된다.
  // { title : 'hello!' }
  console.log(req.query) 

});

3. req.body

-HTTP 요청의 본문에 담긴 데이터를 말한다. 주로 POST나 PUT과 같은 HTTP 메소드를 이용하여 유저의 정보 또는 파일 업로드(formdata)를 보냈을 때 사용
-요청 본문에 제출된 키-값 데이터 쌍을 포함한다.
-ex)JSON 등의 바디 데이터를 담을때.
-Request body의 값은 req.body 객체를 통해 얻을 수 있다.
-ex)Request Body를 이용해 새로운 유저를 생성하는 기능을 구현할 수 있다.

app.post('/users', (req, res) => {
const newUser = req.body;
// 새로운 유저 정보를 생성하고 응답 처리
});

-위의 예시 코드에서 newUser 는 HTTP 요청의 본문(body)에 담긴 데이터를 의미한다.

---클라이언트
// 클라이언트 단에서, 자바스크립트로 get요청을 보냄

// 방식 1
await axios.post('www.example.com/post/1/kim', { 
    name: 'kim', // post 로 보낼 데이터
    age: 20,
    married: false
});

// 방식 2
await axios({
  method: "post",
  url: `www.example.com/post/1/kim`,
  data: { // post 로 보낼 데이터
  	name: 'kim',
    age: 20,
    married: false
  },
})

---서버
// json 형식 폼 요청 들어오면 파싱
app.use(express.json()); 

// 요청온 url : www.example.com/public/100/kim
router.post('/:id/:name', (req, res, next) => {

  // public/100 부분이 담기게 된다.
  console.log(req.params) // { id: '100', name: 'jun' }
  
  // post보낼때 담은 객체 부분이 담기게 된다.
  console.log(req.body) // { name: 'kim', age: 20, married: false }
  
});
profile
나는 나의 섬이다.

0개의 댓글