본문 바로가기
Web/backend

express (1) : 웹서버 생성, 미들웨어, 라우팅

by yongmin.Lee 2020. 10. 24.

1. 웹서버 생성

yarn add express

 

//src/index.js
const http = require('http');
const express = require('express');

//익스프레스 서버 객체
const app = express();

//웹 서버의 port속성 설정
app.set('port', 3000);

http.createServer(app).listen(app.get('port'), ()=>{
    console.log('express server starts : ' + app.get('port'));
});

express server object의 메소드

- set(name, value) : 서버 설정을 위한 속성 지정

- get(name) : 지정한 속성 반환

- use([path], function) : 미들웨어함수 등록

- get([path], function) : 특정 패스로 요청된 정보 처리

 

 

2. 미들웨어

2-1. 미들웨어등록

//src/index.js
const http = require('http');
const express = require('express');

//익스프레스 서버 객체
const app = express();

//미들웨어 등록
app.use((req,res,next)=>{
    console.log('first middleware');

    //req객체에 user속성을 추가하고 문자열 할당
    req.user = 'mike';

    //다음 미들웨어 호출
    next();
});
app.use('/',(req,res,next)=>{
    console.log('second middleware');
    res.writeHead('200', {'content-type':'text/html;charset=utf-8'});
    res.write('<p>express server 응답 결과 : ' + req.user + '</p>');
    res.end();
})

//웹 서버의 port속성 설정
app.set('port', 3000);

http.createServer(app).listen(app.get('port'), ()=>{
    console.log('express server starts : ' + app.get('port'));
});

- app.use파라미터로 등록된 함수가 하나의 미들웨어

- 미들웨어 함수는 req, res, next 객체를 파라미터로 받음

- req : 요청객체

- res : 응답객체

- next() : 현재처리중인 미들웨어의 다음 미들웨어를 호출하는 함수

 

2-2. 응답객체 메소드

- writeHead(statusCode, statusMessage, headers)

- write(chunk, encoding, callback) : 여러번 작성가능

- end(data, encoding, callback)

+

express에서 추가로 사용 가능한 메소드

- send() : 클라이언트에 모든 종류의 응답데이터를 보낸다 (html, buffer객체, json객체, json배열 등등 -> 만능메서드)

//app.use('/',(req,res,next)=>{
//   res.writeHead('200', {content-type':'text/html;charset=utf-8'});
//	 res.write("<p>server response</p>");
//	 res.end();
//});

app.use('/',(req,res,next)=>{
    res.send('<p>express server response</p>');
});

- status(code) : http 상태코드 반환 -> send()메소드를 연속으로 호출해야 전송

- sendStatus(code) = status(code).send();

app.use('/',(req,res,next)=>{
    res.status(404).send();
})

//app.use('/',(req,res,next)=>{
//    res.sendStatus(404);
//})

- redirect([status], path) : 웹페이지 경로를 강제로 이동

app.use('/',(req,res,next)=>{
	//디폴트 상태값 : 302
    res.redirect(302,'http://www.naver.com');
});

 

2-3. 요청객체의 속성

- req.query : 클라이언트에서 get방식으로 전송한 데이터(querystring) 확인, url query로 전송한 데이터(querystring) 확인

- req.body : 클라이언트에서 post방식으로 전송한 데이터(querystring) 확인, (body-parser 외장모듈 필요)

- req.params : 라우트 파라미터를 확인할 수 있는 객체, 이 객체의 속성을 "토큰"이라고 한다.

- header(name) : header를 확인

 

2-4. static middleware

: 특정 폴더의 파일들을 특정패스로 접근할 수 있도록 만들어준다.

yarn add server-static

public 디렉토리를 만들고 그 안에 index.html파일을 만든다.

app.use('/public', static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

-> http://localhost:3000/public/index.html로 index.html 접근 가능

 

2-5. body-parser middleware

: 클라이언트가 post방식으로 요청할 때 본문 영역에 들어 있는 요청파라미터(querystring)들을 파싱하여

요청객체(req)의 "body" 속성에 넣어준다. ex) req.body.key

<!-- public/index.htrml -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <h1>test page</h1>
    <!-- post방식 -->
    <form method="post">   
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="id"></td>
            </tr>
            <tr>
                <td><label>비밀번호</label></td>
                <td><input type="password" name="pw"></td>
            </tr>
        </table>
        <input type="submit" value="submit" name="">
    </form>
</body>

</html>

 

//src/index.js
const http = require('http');
const express = require('express');
const static = require('serve-static');
const path = require('path');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();
app.set('port', 3000);

//미들웨어 등록

//body-parser를 사용해 application/x-www-from-urlencoded 파싱
app.use(bodyParser.urlencoded({extended:false}));

//body-parser를 사용해 application/json 파싱
app.use(bodyParser.json());

app.use('/public', static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

app.use((req,res)=>{
    const paramId = req.body.id;
    const paramPw = req.body.pw;

    res.send("id : " + paramId + "<br>" + "pw : " + paramPw);
    // res.writeHead('200', {'content-type':'text/html;charset=utf-8'});
    // res.write("<h1>response</h1>");
    // res.write("<p>id : " + paramId + "</p>");
    // res.write("<p>pw : " + paramPw + "</p>");
    // res.end();
})

//웹 서버의 port속성 설정
http.createServer(app).listen(app.get('port'), ()=>{
    console.log('express server starts : ' + app.get('port'));
});

 3.  라우팅

: 다른주소로 요청이 들어올 때, 다른 작업을 처리할수 있도록 라우터 사용 -> 사용자가 요청한 기능이 무엇인지 패스를 기준으로 구별

3-1. 라우터 설정 및 등록

<!-- public/index.htrml -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <!-- GET방식 -->
    <h1>GET</h1>
    <form method="GET" action="../src/index.js">
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="gid"></td>
            </tr>
            <tr>
                <td><label>비밀번호</label></td>
                <td><input type="password" name="gpw"></td>
            </tr>
        </table>
        <input type="submit" value="submit" name="">
    </form>
    <hr>
    <!-- POST방식 -->
    <h1>POST</h1>
    <form method="POST">
        <table>
            <tr>
                <td><label>아이디</label></td>
                <td><input type="text" name="pid"></td>
            </tr>
            <tr>
                <td><label>비밀번호</label></td>
                <td><input type="password" name="ppw"></td>
            </tr>
        </table>
        <input type="submit" value="submit" name="">
    </form>
</body>

</html>

 

// src/index.js
const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();
//라우터 객체 참조
const router = express.Router();
//포트설정
app.set('port', 3000);

//미들웨어 등
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// static 미들웨어 
app.use('/public', express.static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

//라우팅 함수 설정
router.get('/',(req, res) => {
    console.log("/ get방식으로 요청들어옴");
    res.send("<p>HOME</p>");
});
router.get('/src/index.js',(req, res) => {
    console.log("/src/index.js 에서 get방식으로 요청들어옴");
    const paramgId = req.query.gid;
    const paramgPw = req.query.gpw;
    res.writeHead('200', { 'content-type': 'text/html;charset=utf-8' });
    res.write("<h1>GET</h1>");
    res.write("<p>id : " + paramgId + "</p>");
    res.write("<p>pw : " + paramgPw + "</p>");
    res.end();
});
router.post('/public/index.html',(req, res) => {
    console.log("/public/index.html 에서 post방식으로 요청들어옴");
    const paramId = req.body.pid;
    const paramPw = req.body.ppw;
    res.writeHead('200', { 'content-type': 'text/html;charset=utf-8' });
    res.write("<h1>POST</h1>");
    res.write("<p>id : " + paramId + "</p>");
    res.write("<p>pw : " + paramPw + "</p>");
    res.end();
});

//라우터 등록
app.use('/', router);

//웹 서버의 port속성 설정
http.createServer(app).listen(app.get('port'), () => {
    console.log('express server starts : ' + app.get('port'));
});

- 라우팅 함수 설정 (19~43행)

  - GET 방식으로 request 발생시 req.query 객체에서 속성조회가능

  - POST 방식으로 request 발생시 req.body 객체에서 속성조회가능 (body-parser 미들웨어 필수)

- 라우터 등록 (46행)

 

3-2. 클라이언트->서버로 데이터(querystring) 전송 방법 : URL query와 라우트 파라미터, form태그

- URL query(url parameter) : url 뒤에 ?를 붙이고 querysting을 추가하여 보내면 자동으로 객체 형태로 파싱되어 req.query에서 확인 가능

- 라우트파라미터 : 라우트경로 설정 시 맨뒤에 /:parameter? 와 같은 형식으로 라우터 경로 설정하면 req.params 객체에서 속성(토큰)으로 확인 가능. 이 속성을 '토큰'이라고 부른다.

- form 태그 : method속성으로 데이터를 보내는 방식을 설정 가능 (get, post, etc) 

=> get 방식으로 보낸 데이터는 req.query, post방식으로 보낸 데이터는 req.body에서 확인 가능

// src/index.js
//...
router.get('/src/index.js/:name?',(req, res) => {
    console.log("/src/index.js 에서 get방식으로 요청들어옴");
    const {name} = req.params;
    const paramgId = req.query.gid;
    const paramgPw = req.query.gpw;
    res.writeHead('200', { 'content-type': 'text/html;charset=utf-8' });
    res.write("<h1>GET</h1>");
    res.write("<p>id : " + paramgId + "</p>");
    res.write("<p>pw : " + paramgPw + "</p>");
    res.write("<p>name : " + name + "</p>");
    res.end();
});
/...

=> url :  http://localhost:3000/src/index.js/John?gid=123&gpw=456 

=> 결과 : id : 123 pw = 456 name : John (John은 토큰)

 

3-3. request path를 router 객체에 설정시 사용하는 메소드

- get('request path', callback) : get 방식으로 request path 에서 요청이 발생이 콜백함수 실행

- post('request path', callback) : post 방식으로 request path 에서 요청이 발생이 콜백함수 실행

- put('request path', callback) : put 방식으로 request path 에서 요청이 발생이 콜백함수 실행

- delete('request path', callback) : delete 방식으로 request path 에서 요청이 발생이 콜백함수 실행

- patch('request path', callback) : patch 방식으로 request path 에서 요청이 발생이 콜백함수 실행펭

- all('request path', callback) : 모든방식으로 request path 에서 요청이 발생이 콜백함수 실행 - > 페이지 오류 응답 처리하는데 사용

// src/index.js
//...
router.all('*', (req,res)=>{
    res.status(404).send("<h1>404 ERROR - page not found</h1>");
});
/...

 

 

3-4. 라우트 모듈화

- 모듈화 전

// src/index.js
const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();
//라우터 객체 참조
const router = express.Router();

//포트설정
app.set('port', 3000);

//미들웨어 등
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// static 미들웨어 
app.use('/public', express.static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

//라우팅 함수 
router.get('/',(req, res) => {
    console.log("/ get방식으로 요청들어옴");
    res.send("<h1>HOME</h1>");
});
router.get('/about/:name?', (req,res)=>{
    const {name} = req.params;
    if(name){
        res.send(`${name} 소개`);
    }else{
        res.send("소게");
    }
});
router.get('/posts',(req,res)=>{
    const {id} = req.query;
    if(id){
        res.send(`포스트 #${id}`);
    }else{
        res.send("포스트id 없음");
    }
});
router.all('*', (req,res)=>{
    res.status(404).send("<h1>404 ERROR - page not found</h1>");
});

//라우터 등록
app.use('/', router);

//웹 서버의 port속성 설정
http.createServer(app)
.listen(app.get('port'), () => {
    console.log('express server starts : ' + app.get('port'));
});

 

- 모듈화 후

// src/index.js
const http = require('http');
const express = require('express');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();

//라우터 설정
const router = require('./router');

//포트설정
app.set('port', 3000);

//미들웨어 등
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// static 미들웨어 
app.use('/public', express.static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

//라우터 등록
app.use('/', router);

//웹 서버의 port속성 설정
http.createServer(app)
.listen(app.get('port'), () => {
    console.log('express server starts : ' + app.get('port'));
});

 

//라우트 모듈화
// src/router/index.js
const express = require('express');

const router = express.Router();


router.get('/',(req, res) => {
    console.log("/ get방식으로 요청들어옴");
    res.send("<h1>HOME</h1>");
});
router.get('/about/:name?', (req,res)=>{
    const {name} = req.params;
    if(name){
        res.send(`${name} 소개`);
    }else{
        res.send("소게");
    }
});
router.get('/posts',(req,res)=>{
    const {id} = req.query;
    if(id){
        res.send(`포스트 #${id}`);
    }else{
        res.send("포스트id 없음");
    }
});
router.all('*', (req,res)=>{
    res.status(404).send("<h1>404 ERROR - page not found</h1>");
});


module.exports = router;

 

- posts 라우트

//src/api/posts/index.js
const express = require('express');
const posts = express.Router();

const printInfo = (req,res)=> {
    res.send(req.body = {
        method:req.method,
        path : req.originalUrl,
        params: req.params
    })
};

posts.get('/', printInfo);
posts.post('/', printInfo);
posts.get('/:id', printInfo);
posts.delete('/:id', printInfo);
posts.put('/:id', printInfo);
posts.patch('/:id', printInfo);

module.exports = posts;

 

//src/api
const express = require('express');
const api = express.Router();
const posts = require('./posts');

api.use('/posts', posts);

module.exports = api;

 

//src/index.js
const http = require('http');
const express = require('express');
// const static = require('serve-static');
// const path = require('path');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();

//라우터 설정
const api = require('./api');

//포트설정
app.set('port', 3000);

//미들웨어 등
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// static 미들웨어 
app.use('/public', express.static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

//라우터 등록

app.use('/api', api);

//웹 서버의 port속성 설정
http.createServer(app)
.listen(app.get('port'), () => {
    console.log('express server starts : ' + app.get('port'));
});

 

3-5. 컨트롤러 파일

- 컨트롤러 : 라우트 처리 함수만 모아놓은 파일

- 컨트롤러에서는 백엔드 기능을 구현

 

//src/api/posts/posts.ctrl.js
//컨트롤러 파일
let postId = 1; // id 초깃값

const posts = [{ id: 1, title: '제목', body: '내용' }];
//목록조회 GET /api/posts 
exports.list = (req, res) => {
    res.send(posts);
};

//특정포스트 조회 GET /api/posts/:id 
exports.read = (req, res) => {
    const { id } = req.params;

    const post = posts.find(p => p.id.toString() === id);

    //포스트 없으면 오류 반환
    if (!post) {
        res.status(404).send("포스트없음");
        return;
    }
    res.send(post);
};

//포스트 작성 POST /api/posts
exports.write = (req, res) => {
    const { title, body } = req.body;
    postId += 1;
    const post = { id: postId, title, body };
    posts.push(post);
    res.send(post);
};

//특정포스트 제거 DELETE /api/posts/:id
exports.remove = (req, res) => {
    const { id } = req.params;

    const index = posts.findIndex(p => p.id.toString() === id);
    if (index === -1) {
        res.status(404).send("포스트없음");
        return;
    }
    posts.splice(index, 1);
    res.status(204).send("콘텐츠 없음, no content");
};

// 포스트 수정 PUT /api/posts/:id
//put 메소드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용
exports.replace = (req, res) => {
    const { id } = req.params;
    const index = posts.findIndex(p => p.id.toString() === id);
    if (index === -1) {
        res.status(404).send("포스트없음");
        return;
    }
    posts[index] = {
        id,
        ...req.body
    };
    res.send(posts[index]);
};

//포스트 수정 PATCH /api/posts/:id
exports.update = (req, res) => {
    const { id } = req.params;
    const index = posts.findIndex(p => p.id.toString() === id);
    if (index === -1) {
        res.status(404).send("포스트없음");
        return;
    }
    posts[index] = {
        ...posts[index],
        ...req.body
    }
    res.send(posts[index]);
};

 

//src/api/posts/index.js
// posts 라우터
const express = require('express');
const posts = express.Router();
const postsCtrl = require('./posts.ctrl');

posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.read);
posts.delete('/:id', postsCtrl.remove);
posts.put('/:id', postsCtrl.replace);
posts.patch('/:id', postsCtrl.update);

module.exports = posts;

 

//src/api/index.js
//라우트 모듈화
const express = require('express');
const api = express.Router();
const posts = require('./posts');

api.use('/posts', posts);

module.exports = api;

 

//src/index.js
// 백엔드 프로그래밍
const http = require('http');
const express = require('express');
// const static = require('serve-static');
// const path = require('path');
const bodyParser = require('body-parser');

//익스프레스 서버 객체
const app = express();

//라우터 설정
const api = require('./api');

//포트설정
app.set('port', 3000);

//미들웨어 등
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// static 미들웨어 
app.use('/public', express.static("/home/yongmin/Desktop/React/blog/blog-backend/public"));

//라우터 등록

app.use('/api', api);

//웹 서버의 port속성 설정
http.createServer(app)
.listen(app.get('port'), () => {
    console.log('express server starts : ' + app.get('port'));
});



'Web > backend' 카테고리의 다른 글

MVC 패턴  (0) 2020.10.30
express (2) : 요청과 응답, 쿠키와 세션, REST API와 라우팅  (0) 2020.10.24
SNS 로그인 flow  (0) 2020.10.24
AWS django 배포  (0) 2020.10.24
Django - select_related() & prefetch_related()  (0) 2020.10.24