라우트 요청과 처리에 대해 알아봅시다.
각 학생은 데이터 생성 시 mongoose 가 할당해준 id
속성값을 가지고 있습니다.
/students/(Student doc의 아이디값)
라는 페이지를 요청하면 그 학생의 정보를 볼 수 있게 해봅시다.
이 때, 학생의 id
값은 학생마다 달라지는 변수
이므로, 변수를 URL 에서 처리해 줄 방법이 필요합니다.
바로 URL 파라미터
를 이용하는 것입니다.
//studentRouter 는 /students 로 이어지는 라우트를 처리
studentRouter.get("/:id", studentInfo);
// http://localhost:4000/videos/1234 에 대한 요청을 studentInfo 컨트롤러가 처리
앞에 :
기호를 붙이면 변수로 취급하게 됩니다.
위에서는 id
앞에 :
를 붙여 id 를 URL 파라미터, 즉 변수로 사용했습니다.
그렇다면 이 id 변수에 대한 정보를 어떻게 가져올 수 있을까요?
컨트롤러에서 URL 파라미터를 사용하는 방법에 대해 알아봅시다.
URL 파라미터는 request
의 params
에 들어가있습니다.
컨트롤러가 기본적으로 넘겨받는 request, response 객체 중 request 를 이용합니다.
import Student from "models/Student"; // 학생 모델 사용하기
const studentInfo = (req, res) => {
const { id } = req.params; // URL 파라미터 id 읽어오기
// const id = req.params.id 와 같음
const student = Student.findById(id);
// 해당 id 를 갖는 학생 정보를 찾음
return res.render("studentInfo.html", { student });
// 찾아온 학생 정보를 뷰에 넘기기
}
위와 같이 활용합니다.
파라미터 값을 얻어와, 모델에서 데이터를 찾고, 그 정보를 뷰에 넘겨 렌더링합니다.
URL 파라미터 사용 시 주의할 점에 대해 알아봅시다.
다음과 같이 라우팅 처리를 구성했다고 합시다.
//studentRouter 는 /students 로 이어지는 라우트를 처리
studentRouter.get("/:id", studentInfo);
studentRouter.get("/logout", logout);
/students/logout 으로 접속하면 logout 컨트롤러가 호출되어야 합니다. 그러나, 실제로는 studentInfo 컨트롤러 가 호출됩니다.
왜일까요...?
이유는 logout 을 파라미터로 취급했기 때문입니다.
studentInfo 컨트롤러에서 req.params.id
를 해보면 "logout" 이라는 결과가 나옵니다.
//studentRouter 는 /students 로 이어지는 라우트를 처리
studentRouter.get("/logout", logout);
studentRouter.get("/:id", studentInfo);
위처럼 순서를 바꿔주면 /logout 라우트에 먼저 도달하므로 정상적으로 처리되지만... 이런 방식으로 하기는 너무 비효율적입니다.
파라미터인 id 의 형식을 지정해준다면(ex. 숫자만 받는다던지) 아무 URL 이나 처리하게 되는 일이 없어질 것입니다.
URL 정규식 패턴을 사용하여 우리가 받는 파라미터의 형식을 지정할 수 있습니다.
//studentRouter 는 /students 로 이어지는 라우트를 처리
studentRouter.get("/:id(\\d+)", studentInfo);
// \d 는 숫자만 받는다는 뜻
// + 는 숫자인 한 계속 받는다는 뜻
studentRouter.get("/logout", logout);
위처럼 작성해주면, id 파라미터는 숫자만 받게 됩니다.
/students/13434 는 studentInfo 가, /students/logout 은 logout 이 처리하게 됩니다.
이를 모델의 id 속성에 맞게 형식을 바꿔줄 수 있습니다.
모델의 id 는 24문자로 구성된 16진수입니다.
정규식으로 나타내면 다음과 같습니다.
studentRouter.get("/:id([0-9a-f]{24})", studentInfo);
이제 req.params.id
로 받아온 id 변수값과 모델을 이용하여 DB 에서 id에 해당하는 학생 정보를 얻을 수 있습니다.
브라우저가 서버에 라우트 처리를 요청하는 방식은 크게 두 가지로 나뉩니다.
바로 GET
과 POST
방식인데, 간단히 설명하면 다음과 같습니다.
- GET 방식 : 서버에 있는 리소스를 불러올 때 (단순 읽기!)
- POST 방식 : 사용자가 입력한 정보를 통해 서버에 새로운 리소스가 생성되거나 변경될 때
GET
방식은 보안이 필요없는 정보 읽기에, POST
방식은 보안 유지가 필요한 정보 처리에 사용합니다.
GET 방식으로 정보를 요청하면, 입력한 정보가 URL 의 query string
을 통해 다 보이기 때문이죠.
로그인 화면의 경우를 예시로 든다면, /login 라우트에 대해 GET 요청을 하면 로그인 페이지 뷰를 렌더링하는 코드가 동작합니다. 하지만, POST 요청을 한다면 form 으로 받은 로그인 정보를 이용하여 DB 에서 유저 정보를 가져옵니다.
같은 라우트라도 어떤 방식으로 요청되냐에 따라서 라우팅 처리를 진행하는 컨트롤러가 달라지고, 처리 방식이 달라집니다.
각 방식의 요청을 어떻게 처리하는지 알아봅시다.
studentRouter.get("/:id([0-9a-f]{24})", studentInfo);
// 0~9 사이 숫자, a~f 사이의 알파벳으로 이루어진 24자의 id 파라미터 라우트
// 해당 라우트를 GET 방식으로 요청하면 studentInfo 컨트롤러가 처리
studentRouter.route("/:id([0-9a-f]{24})/edit").get(getEdit).post(postEdit);
// route( ) 로 URL 지정
// GET 방식 요청은 getEdit 컨트롤러가, POST 방식 요청은 postEdit 컨트롤러가 처리
위와 같은 방식으로 처리할 수 있습니다.
실제 사용자는 form 을 이용해 GET 이나 POST 요청을 하게 됩니다.
GET 방식으로 요청하는 경우에 대해 알아봅시다.
<!-- /search -->
<form method="GET">
<input type="text" name="stuname" placeholder="Student Name" />
<input type="submit" value="Search now" />
</form>
위 form 은 method="GET"
으로, 받아온 input 을 GET
방식으로 처리합니다.
action
에 다른 경로가 지정되지 않았으므로, /search 라우트에서 그대로 쿼리 스트링을 통해 정보를 얻어옵니다.
input 에 Amy 라고 쓰고 submit 버튼을 누르면...
/search?stuname=Amy
위 URL 로 이동합니다. name 이 stuname 인 input 에 입력된 값이 Amy 라는 뜻입니다.
이와 같이 ?name=value&name=value...
형식을 쿼리스트링
이라고 합니다.
입력한 정보가 URL 에 다 나타나므로 보안성이 없습니다.
이 쿼리에 대한 정보는 컨트롤러에서 req.query
를 통해 읽어올 수 있습니다.
import Student from "models/Student";
const search = (req, res) => {
const { stuname } = req.query;
const students = Student.find({name: stuname});
// 이름이 stuname 인 학생을 모두 반환
.
.
.
return res.render("search.html", { pageTitle: "Search", students });
};
req.query 에서 stuname 변수를 가져와 사용합니다.
POST 요청을 하는 경우에 대해 알아봅시다.
// "/join" 라우트
<form action="/join" method="POST">
<input name="userid" type="text" placeholder="ID" maxlength="12" required />
<input
name="username"
type="text"
placeholder="Username"
maxlength="10"
required
/>
<input
name="passwd"
type="password"
placeholder="Password"
maxlength="12"
required
/>
<input type="submit" value="Join" />
</form>
userid, username, passwd 라는 name
의 input
을 받아오고 있습니다.
이렇게 받아온 input 정보는 form 의 action
속성에 지정된 라우트로 request
를 보낼 때 body
에 담겨 전송됩니다.
(action 속성에 아무런 값도 지정하지 않을 경우, form 이 있는 페이지의 라우트로 POST 요청)
컨트롤러에서는 req.body
를 이용하여 정보를 얻어올 수 있습니다.
const postJoin = (req, res) => {
const { userid, username, passwd } = req.body;
.
.
.
return res.redirect("/"); // 루트 페이지로 돌아감
}
req.params
, req.query
, req.body
는 모두 이런 형태입니다.
{ name1: "value1", name2: "value2" ... }
input 의 name
과 그 input 에 보내준 값 value
로 구성됩니다.
각 정보는 방식에 따라 서로 다른 부분에 위치합니다.
- URL 파라미터 정보 :
req.params
에 존재- GET 방식 정보 :
req.query
에 존재- POST 방식 정보 :
req.body
에 존재