Juni_Dev_log

(node.js) [Part.5] 웹 서버 만들기 - 요청 라우팅하기 본문

Theorem (정리)/node.js

(node.js) [Part.5] 웹 서버 만들기 - 요청 라우팅하기

Juni_K 2020. 12. 10. 22:02

앞에서 로그인 페이지를 열고 버튼을 눌렀을 때, 로그인 처리를 하는 간단한 예를 살펴보았다.

그런데 로그인이 아니라 사용자 리스트 등 다른 요청이 들어왔을 때도 use() 메소드로 설정한 미들웨어 함수가 항상 호출되기 때문에 요청 url이 무엇인지 일일이 확인해야 하는 번거로움이 생긴다.

요청 url 을 일일이 확인해야하는 번거로운 문제를 해결하는 것이 라우터 미들웨어이다.

라우터 미들웨어 사용하기

라우터 미들웨어는 익스프레스에 포함되어 있다. 이 라우터를 사용하면 사용자가 요청한 기능이 무엇인지 패스를 기준으로 구별하기 때문에 아주 중요하다.

라우터 미들웨어를 사용하려면 다음과 같이 익스프레스 객체에서 라우터 객체를 참조해서 사용한다. 그리고 라우팅 함수를 등록하면 app 객체에 설정한다.

 

// 라우터 객체 참조
var router = express.Router();

// 라우팅 함수 등록
router.route('/process/login').get(...);
router.route('/process/login').post(...);
...

// 라우터 객체를 app 객체에 등록
app.use('/', router);

클라이언트에서 요청한 요청 패스에 따라서, 실행될 함수는 라우터 객체를 사용해 등록한다.

router 객체에는 route() 메소드가 있어 요청패스를 지정할 수 있으며, get() 이나 post() 메소드를 호출하면 실행될 함수를 등록할 수 있다.

 

router.route(요청 패스).get(실행될 함수);
router.route(요청 패스).post(실행될 함수);

요청 패스를 라우터 객체에 등록할 때, 사용하는 메소드는 다음과 같다.

메소드 이름 설명
get(callback) GET 방식으로 특정 패스 요청이 발생했을 때,
사용할 콜백 함수를 지정합니다.
post(callback) POST 방식으로 특정 패스 요청이 발생했을 때,
사용할 콜백 함수를 지정합니다.
put(callback) PUT 방식으로 특정 패스 요청이 발생했을 때,
사용할 콜백 함수를 지정합니다.
delete(callback) DELETE 방식으로 특정 패스 요청이 발생했을 때,
사용할 콜백 함수를 지정합니다.
all(callback) 모든 요청 방식을 처리하며, 
특정 패스 요청이 발생했을 때, 사용할 콜백 함수를 지정한다.

클라이언트에서 특정 패스로 요청할 경우, GET 방식으로 요청할 때는 get() 메소드를 사용해 함수를 등록해야하며, POST 방식으로 요청할 때는 post() 메소드를 사용해 등록해야한다.

라우팅 기능을 확인해 보기 위해서 먼저 앞에 만든 [public] 폴더 안의 login.html 파일을 복사하여, login2.html 을 만든 후 <form> 태그에 action 속성을 추가한다.

 

1
2
3
...
<form method="post" action="/process/login">
...
cs

app7.js 파일을 복사해서 만든 app8.js 파일에는 이제 더 이상 use() 메소드로 함수를 추가하지 않겠다.

그 대신 라우터 객체에 라우팅 함수를 등록하여, /process/login 요청 패스를 처리하도록 만든다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
// 라우터 객체 설정
var router = express.Router();
 
// 라우터 함수 등록
router.route('/process/login').post(function(req,res){
    console.log('/process/login 처리함.');
    
    var paramId = req.body.id || req.query.id;
    var paramPassword = req.body.password || req.query.password;
    
    res.writeHead('200',{'Content-Type':'text/html;charset=utf8'});
    res.write('<h1>Express 서버에서 응답한 결과입니다.</h1>');
    res.write('<div><p>Param id : ' + paramId +  '</p></div>');
    res.write('<div><p>Param password : ' + paramPassword + '</p></div>');
    res.end();
})
 
// 라우터 객체를 app 객체에 등록
app.use('/',router);
 
...
cs

로그인 페이지에서 전송 버튼을 누르면 /process/login 패스로 요청하므로 post() 메소드로 등록한 콜백 함수가 호출된다. app8.js 를 실행한 후 브라우저에서 아이디와 비밀번호를 입력한다.

 

login2.html 파일에서 아이디와 비밀번호 입력

localhost 대신에 내 PC의 실제 IP나 호스트 이름을 입력해도 된다.

위의 테스트 화면처럼 localhost 대신에,. 192.168.0.5와 같은 내 PC의 실제 IP를 입력한 경우가 많다.
이것은 앞에서 ipconfig 명령을 사용해 확인했던 내 PC의 IP이며 웹 서버의 IP이기도 하다.
이렇게 웹 브라우저에서 웹 서버에 접속할 때는 localhost 또는 여러분 PC의 IP를 입력하기 바란다.

전송 버튼을 누르면 서버에서 처리한 후 다음 응답 페이지가 표시된다.

 

post() 메소드로 추가한 콜백 함수 처리 결과

다음은 클라이언트가 요청한 패스인 /process/login 을 라우팅하는 과정을 보여준다.

라우터 미들웨어를 등록하면 app 객체에서 get() 또는 post() 메소드를 호출할 수 있으므로 먼저 app.post() 메소드를 호출하여 요청 패스를 라우팅하도록 등록한다.

그 다음 클라이언트에서 /process/login 으로 요청이 들어오면 이 라우팅 정보에 따라서 해당 콜백 함수가 실행된다.

 

콜백 함수에서는 로그인에 필요한 기능을 실행한 후 클라이언트로 응답을 보내줄 수 있다.

URL 파라미터 사용하기

클라이언트에서 요청할 때 URL 뒤에 ? 기호를 붙이면 요청 파라미터를 추가하여 보낼 수 있다.

클라이언트에서 서버로 데이터를 전달하는 방식은 이것 이외에도 URL 파라미터를 사용하기도 한다.

URL 파라미터는 요청 파라미터와 달리 URL 주소의 일부로 들어간다. URL 파라미터를 어떻게 전달하는지 알아보기 위해 app8.js파일을 복사하여 app8_02.js 를 만든다. 그 다음 app.post 메소드 호출 부분을 수정한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
 
// 라우터 객체 설정
var router = express.Router();
 
// 라우터 함수 등록
router.route('/process/login/:name').post(function(req,res){
    console.log('/process/login/:name 처리함.');
    
    var paramName = req.params.name;
    
    var paramId = req.body.id || req.query.id;
    var paramPassword = req.body.password || req.query.password;
    
    res.writeHead('200',{'Content-Type':'text/html;charset=utf8'});
    res.write('<h1>Express 서버에서 응답한 결과입니다.</h1>');
    res.write('<div><p>Param name : '  + paramName + '</p></div>');
    res.write('<div><p>Param id : ' + paramId +  '</p></div>');
    res.write('<div><p>Param password : ' + paramPassword + '</p></div>');
    res.write("<br><br><a href='/public/login3.html'>로그인 페이지로 돌아가기</a>");
    res.end();
})
 
// 라우터 객체를 app 객체에 등록
app.use('/',router);
 
...
cs

app.post 메소드를 호출할 때 전달하는 첫 번째 파라미터의 값이 /process/login 에서  /process/login/:name 으로 변경되었다. 이것은 /process/login/ 뒤에 오는 값을 파라미터로 처리하겠다는 의미이다.

이렇게 지정한 파라미터는 req.params 객체 안에 들어간다. 따라서, :name 으로 표시된 부분에 넣어 전달된 값을 req.params.name 속성으로 접근할 수 있다.

이를 토큰(Token)이라고 부른다.

이렇게 /process/login/:name 형태를 가진 URL 을 처리하도록 변경했으므로 사용자가 웹 페이지에서 요청할 때 사용하는 URL도 변경해야한다. [public] 폴더 안에 있는 login2.html 파일을 복사하여 login3.html 파일을 만든 후 다음과 같이 요청URL을 수정한다.

 

1
2
3
...
    <form method="post" action="/process/login/juni">    
...
cs

action 속성 값으로 /process/login/juni 를 넣었으므로 juni 라는 문자열이 URL 파라미터로 전달된다.

/process/login/juni
/process/login/:name

위에서 빨간색으로 표시한 부분이 서로 매칭되어서 처리된다. 정상적으로 처리되는지 확인하기 위해서 app8_02.js 파일을 실행한 후 웹 브라우저에서 login3.html 파일을 연다.

 

URL 파라미터를 전달하기 위한 웹 페이지

아이디와 비밀번호를 입력한 후, [전송] 버튼을 누르면 다음과 같이 아이디와 비밀번호 외에 juni라는 값도 파라미터로 전달받은 것을 알 수 있다.

 

서버에서 URL 파라미터를 전달받아 보여 준 결과

클라이언트가 보낸 URL에 포함된 문자열 중 일부를 서버에 파라미터로 처리할 수 있다는 것을 알게되었다.

URL 파라미터를 사용해 데이터를 전달하는 경우가 자주 있기 때문에 이 방식을 잘 기억하도록 한다.

오류 페이지 보여 주기

로그인을 위해 웹 브라우저에 주소를 입력할 때 주소가 입력되는 부분을 보면 /process/login 패스로 처리된 것을 알 수 있다. 이 패스는 웹 서버에서 라우터 미들웨어로 등록했기 때문에 등록된 콜백 함수에서 요청을 전달받아 처리할 수 있다.

따라서 웹 서버에 등록되지 않은 패스인 /login 과 같은 패스를 웹 브라우저에 입력해 보면 문서를 찾을 수 없다는 디폴트 메시지가 나타난다. 그런데 문서를 찾을 수 없다는 디폴트 메시지가 나타난다.

그런데, 문서를 찾을 수 없다는 메세지를 우리가 직접 만든 페이지로 바꾸고 싶다면 어떻게 해야할까?

지정한 패스이외의 모든 패스로 요청이 들어왔을 때, 오류 페이지로 처리해주어야한다.

 

라우터 미들웨어는 특정 패스가 등록되어 있는지 순서대로 확인하여 처리한다.

따라서 위에서 추가한 post() 메소드 아래쪽에 다음과 같이 all() 메소드 호출 부분을 추가한다.

 

1
2
3
4
5
6
7
8
...
 
// 등록되지 않은 패스에 대해 페이지 오류 응답
app.all('*'function(req,res){
    res.status(404).send('<h1>ERROR - 페이지를 찾을 수 없습니다.</h1>')
})
 
...
cs

이제 다시 웹 브라우저에서 /login 패스를 입력하면 찾으려는 페이지가 없어서 서버에서 전송한 오류 페이지가 화면에 표시된다.

 

 all() 메소드를 사용해 '페이지를 찾을 수 없다.'는 메시지를 보여 준 경우

 

이와 같이 페지를 찾을 수 없다는 오류 메세지를 응답으로 보내는 코드를 직접 만드는 대신에 다른 사람이 미리 만들어둔 미들웨어를 사용할 수도 있다.

express-error-handler 미들웨어로 오류 페이지 보내기

예상하지 못한 오류가 발생했을 때, 그 오류를 처리할 수 있는 미들웨어를 사용할 수 있다.

다음은, express-error-handler 미들웨어를 사용해 404.html 페이지를 응답으로 보내주는 예제이다.

app8.js 파일을 복사하여 app9.js 파일을 만든 후 파일을 수정한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
 
// 오류 핸들러 모듈 사용
var expressErrorHandler = require('express-error-handler');
 
// 모든 router 처리 끝난 후 404 오류 페이지 처리
var errorHandler = expressErrorHandler({
    static:{
        '404''./public/404.html'
    }
});
 
app.use(expressErrorHandler.httpError(404));
app.use(errorHandler);
 
...
cs

express-error-handler 모듈은 특정 오류 코드에 따라 클라이언트로 응답을 보내 줄 때 미리 만들어놓은 웹 문서를 보내줄 수 있다.

이 모듈은 외장 모듈이므로 코드의 위쪽에서 먼저 require() 세모드를 호출하여 모듈을 불러온다.

오류 페이지는 모든 라우터 처리가 끝난 후 처리되어야한다.

따라서 서버를 시작하기 위해 호출하는 코드 위쪽에 미들웨어로 추가한다. 오류 페이지를 미리 만들어 둔 파일의 위치를 지정할 수 있으므로  ./public/404.html 로 지정한다.

이제 프로젝트 안에 있는 [public] 폴더 안에 404.html 파일을 만들고 그 안에 오류 표시를 입력한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>오류 페이지</title>
</head>
<body>
   <h3>ERROR - 페이지를 찾을 수 없습니다.</h3>
   <hr/>
   <p>/public/404.html 파일의 오류 페이지를 표시한 것입니다.</p>
</body>
</html>
cs

express-error-handler 모듈을 설치한다.

 

npm install express-error-handler --save

 

서버를 실행하고 아무 주소나 치면 다음 페이지가 나온다.

 

오류 처리 미들웨어로 표시한 오류 페이지

문서를 찾을 수 없다는 오류 페이지를 표시하기까지의 과정은 다음과 같다.

 

오류 페이지를 표시하기까지의 과정

웹 브라우저에서 요청한 패스가 라우터에 등록한 함수 중에 없다면 404 오류가 발생한다.

이 오류가 발생하면 미리 만들어 둔 404.html 파일을 읽어 응답으로 보낸다.

 

지금까지 라우팅 미들웨어를 사용하는 방법에 대해 알아보았다. 사용자가 요청하는데 패스는 매우 다양하다.

그래서 각각의 요청을 담당하는 함수를 각각 만들 수 있는 라우팅 기능은 매우 편리하면서도 중요한 기능이다.

토큰과 함께 요청한 정보 처리하기

앞에서 요청한 URL에 포함된 URL 파라미터 사용방법을 알아보았다.

여기에서는 다시 한 번 코드를 만들어 테스트해 볼 것이다. app9.js 를 복사해서 app10.js 를 만들고 다음과 같이 입력한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
// 라우터 객체 설정
var router = express.Router();
 
// 라우터 함수 등록
router.route('/process/users/:id').get(function(req,res){
    console.log('/process/users/:id 처리함.');
    
    var paramId = req.params.id;
    
    console.log('/process/users와 토큰 %s 를 이용해 처리함.', paramId);
    
    res.writeHead('200',{'Content-Type':'text/html;charset=utf8'});
    res.write('<h1>Express 서버에서 응답한 결과입니다.</h1>');
    res.write('<div><p>Param id : ' + paramId +  '</p></div>');
    res.end();
});
 
// 라우터 객체를 app 객체에 등록
app.use('/',router);
 
 
...
cs

get() 메소드를 호출하면서 동시에 /process/users/:id 패스를 처리하도록 코드를 입력했다.

여기에서 콜론(:)을 붙인 id값이 토큰이며, 일반적인 요청 파라미터처럼 파라미터 객체의 속성으로 확인할 수 있다.

 

따라서 req.params.id 코드를 사용하면 id 속성에 접근할 수 있다.

 

이 파일을 실행하고 웹 브라우저에 다음 주소를 입력한 후 조회하면 다음 결과를 볼 수 있다.

 

토큰을 포함하여 GET 요청을 했을 때 보여주는 응답 페이지

이렇게 토큰을 사용하면 사용자 리스트 중에서 특정 사용자 정보를 id 값으로 조회하기에 편리하다.

 

Comments