6. 익스프레스 웹 서버 만들기(2)

Bor·2022년 1월 4일
0

Node.js

목록 보기
9/11

6.3 Router 객체로 라우팅 분리하기

4.2 절에서 라우터를 만들 때는 요청 메서드와 주소별로 분기 처리를 하느라 코드가 매우 복잡. if문으로 분기하고 코딩하여 보기에도 좋지 않고 확장하기도 어렵다. 익스프레스를 사용하는 이유 중 하나는 바로 라우팅을 깔끔하게 관리할 수 있다는 점.

app.js에서 app.get 같은 메서드가 라우터 부분.

  • 라우터를 많이 연결하면 app.js 코드가 매우 길어지므로 익스프레스에서는 라우터를 분리할 수 있는 방법을 제공.
  • routes 폴더를 만들고 그 안에 index.js와 user.js를 작성한다.



만들었던 index.js와 user.js를 app.use를 통해 app.js에 연결해야. 또한, 에러 처리 미들웨어 위에 404 상태 코드를 응답하는 미들웨어를 하나 추가한다.

indexRouter를 ./routes로 require할 수 있는 이유는 index.js는 생략할 수 있기 때문에. require('./routes/index.js')와 require('./routes')는 같다.

index.js와 user.js는 모양이 거의 비슷하지만 다른 주소의 라우터 역할을 하고 있다. app.use로 연결할 때의 차이 때문. indexRouter는 app.use('/')에 연결했고, userRouter는 app.use('/user')에 연결했다. indexRouter는 use의 '/'와 get의 '/'가 합쳐져 GET / 라우터가 되었고, userRouter는 use의 '/user'와 get의 '/'가 합쳐져 GET / user 라우터가 되었다. 이렇게 app.use로 연결할 때 주소가 합쳐진다는 것을 염두에 두면 된다.

위처럼 라우터를 여러 개 만들어도 된다. 라우터가 몇 개든 next()를 호출하면 다음 미들웨어가 실행된다. 첫 번째 라우터의 첫 번째 미들웨어에서 next() 대신 next('route')를 호출. 이 경우 두 번째, 세 번째 미들웨어가 실행되지 않음. 대신 주소와 일차하는 다음 라우터로 넘어감.

  • 유용한 팁! :id

    라우터 주소에는 정규표현식을 비롯한 특수패턴을 사용할 수 있다.
router.get('/user/:id', function(req,res){
    console.log(req.params, req.query)
});

주소에 :id가 있음 => 문자 그대로 :id를 의미하는 것이 아니라 다른 값을 넣을 수 있다. /user/1 이나 /users/123 등의 요청도 이 라우터 처리. 이 방식의 장점은 :id에 해당하는 1이나 123을 조회할 수 있다는 점이며, req.params 객체 안에 들어 있다.

  • :id면 req.params.id로
  • :type이면 req.params.type으로 조회할 수 있다

단 위 패턴을 사용할 때 주의점이 있다. 일반 라우터보다는 뒤에 위치해야. 다양한 라우터를 아우르는 와일드 카드 역하을 하므로 일반 라우터보다는 뒤에 위치해야 다른 라우터를 방해하지 않는다.

router.get('/user/:id', function(req,res){
    console.log('얘만 실행된다~')
});
router.get('/user/like', function(req,res){
    console.log('전혀 실행되지 않는다');
});

/user/like 같은 라우터는 /user/:id 같은 라우트 매개변수를 쓰는 라우터보다 위에 위치해야 한다. 주소에 쿼리 스트링을 쓸 때도 있다. 쿼리스트링의 키-값 정보는 req.query 객체 안에 들어있다. 예를 들어 /users/123?limit=5&skip=10이라는 주소의 요청이 들어왔을 때 req.params와 req.query 객체는 다음과 같다.

{id : '123'} {limit:'5', skip:'10'}

app.js에서 에러 처리 미들웨어 위에 넣어둔 미들웨어는 일치하는 라우터가 없을 때 404 상태 코드를 응답하는 역할. 미들웨어가 존재하지 않아도 익스프레스가 자체적으로 404 에러를 처리해주기는 하지만 웬만하면 404 응답 미들웨어와 에러 처리 미들웨어를 연결해주는 것이 좋다.


6.4 req, res 객체 살펴보기

  • 익스프레스의 req, res 객체는 http 모듈의 req, res 객체를 확장한 것
  • 기존 http 모듈의 메서드도 사용할 수도, 익스프레스가 추가한 메서드나 속성을 사용할 수도!

예를 들어, res.writeHead, res.write, res.end 메서드를 그대로 사용할 수 있으면서 동시에 res.send나 res.sendFile 같은 메서드도 사용할 수 있다. 다만 익스프레스의 메서드가 워낙 편리해서 기존 http 모듈의 메서드는 잘 쓰이지 않는다!

자주 사용하는 req객체 메서드

• req.app: req 객체를 통해 app 객체에 접근할 수 있습니다. req.app.get('port')와 같은 식으로 사용할 수 있습니다.
• req.body: body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체입니다.
• req.cookies: cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체입니다.
• req.ip: 요청의 ip 주소가 담겨 있습니다.
• req.params: 라우트 매개변수에 대한 정보가 담긴 객체입니다.
• req.query: 쿼리스트링에 대한 정보가 담긴 객체입니다.
• req.signedCookies: 서명된 쿠키들은 req.cookies 대신 여기에 담겨 있습니다.
• req.get(헤더 이름): 헤더의 값을 가져오고 싶을 때 사용하는 메서드입니다.

res 객체도 살펴보자

• res.app: req.app처럼 res 객체를 통해 app 객체에 접근할 수 있습니다.
• res.cookie(키, 값, 옵션): 쿠키를 설정하는 메서드입니다.
• res.clearCookie(키, 값, 옵션): 쿠키를 제거하는 메서드입니다.
• res.end(): 데이터 없이 응답을 보냅니다.
• res.json(JSON): JSON 형식의 응답을 보냅니다.
• res.redirect(주소): 리다이렉트할 주소와 함께 응답을 보냅니다.
• res.render(뷰, 데이터): 다음 절에서 다룰 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드입니다.
• res.send(데이터): 데이터와 함께 응답을 보냅니다. 데이터는 문자열일 수도 있고 HTML일 수도 있으며, 버퍼일 수도 있고 객체나 배열일 수도 있습니다.
• res.sendFile(경로): 경로에 위치한 파일을 응답합니다.
• res.set(헤더, 값): 응답의 헤더를 설정합니다.
• res.status(코드): 응답 시의 HTTP 상태 코드를 지정합니다.

req나 res 객체의 메서드는 다음과 같이 메서드 체이닝을 지원하는 경우가 많다. 메서드 체이닝을 활용하면 코드양을 줄일 수 있다.

res
  .status(201)
  .cookie('test', 'test')
  .redirect('/admin');

6.5 템플릿 엔지 사용하기

HTML은 정적인 언어. 사용자가 기능을 직접 추가할 수 없다. 물론 JS 등장 이전.
이번 절에서는 대표적인 템플릿 엔진인 퍼브(pug)와 넌적스(Nunjucks)를 살펴봄. 앞으로의 예제는 넌적스로 사용.

EJS를 사용하지 않는 이유
노드 생태계에서 EJS나 Handlebars 같은 템플릿 엔진도 많이 사용하지만, 레이아웃 기능이 없으므로 개인적으로는 추천하지 않는다

6.5.1 퍼그(제이드)


제이드로 더 유명한 퍼그는 꾸준한 인기. 문법이 간단하므로 코드의 양이 줄어들기 때문. 단 HTML과는 문법이 많이 달라 호불호가 갈린다.

6.5.2 넌적스

넌적스는 퍼그의 HTML 문법 변화에 적응하기 힘든 분에게 적합한 템플릿 엔진이며, 파이어폭스를 만든 모질라에서 만들었다. HTML 문법을 그대로 사용하되 추가로 자바스크립트 문법을 사용할 수 있고 파이썬의 Twig와 문법이 유사

퍼그와는 연결 방법이 다소 다르다. configure의 첫번째 인수로 views 폴더의 경로를 넣고, 두 번째 인수로 옵션을 넣는다. 이 때 익스프레스 속성에 app 객체를 연결. watch 옵션이 true이면 HTML 파일이 변경될 때 템플릿 엔진을 다시 렌더링한다.

파일은 퍼그와 같은 특수한 확장자 대신 html을 그ㅐㄷ로 사용해도 된다. 넌적스임을 구분하려면 확장자 njk를 쓰면 된다. 이때는 view engine도 njk로 바꿔야!

6.5.2.1 변수

res.render 호출 시 보내는 변수를 넌적스가 처리한다. routes/index.js의 코드를 보면 다음 부분이 있다.

router.get('/', function(req, res, next) {
    res.render('index', {title:'Express'})
})

넌적스에서 변수는 {{ }}로 감싼다.

내부에 변수를 사용할 수도 있다. 변수를 선언할 때는 {% set 변수 = '값' %}를 사용한다

HTML을 이스케이프하고 싶지 않다면 {{변수 | safe}}를 사용한다

6.5.2.2 반복문

넌적스에서는 특수한 구문을 {%%}안에 쓴다. 따라서 반복문도 이 안에 넣으면 된다. for in문과 endfor 사이에 위치하면 된다.

반복문에서 인덱스를 사용하고 싶다면 loop.index라는 특수한 변수를 사용할 수 있다.

6.5.2.3 조건문


case 문은 없지만 elif를 통해서 분기 처리할 수 있다.

6.5.2.4 include

다른 HTML 파일을 넣을 수 있다. 헤더나 푸터, 네비게이션처럼 웹 제작 시 공통되는 부분을 따로 관리할 수 있어 매 페이지마다 동일할 HTML을 넣어야 하는 번거로움을 없앤다. include 파일 경로로 사용한다

6.5.2.5 extends와 block

레이아웃을 정할 수 있다. 공통되는 레이아웃 부분을 따로 관리할 수 있어 좋다. include와도 함께 사용하곤 한다. 레이아웃이 될 파일에는 공통된 마크업을 넣되, 페이지마다 달라지는 부분을 block으로 비워둔다. block은 여러개 만들어도 된다. block을 선언하는 방법은 {% block [블록명] %}이다. {% endblock %}로 블록을 종료한다.

block이 되는 파일에서는 {% extends 경로 %} 키워드로 레이아웃 파일을 지정하고 block 부분을 넣는다. 나중에 익스프레스에서 res.render('body')를 사용해 하나의 HTML로 합친 후 렌더링을 할 수 있다. 같은 이름의 block이 서로 함쳐진다.

0개의 댓글