2018년 3월 31일 토요일

Node.js - 스터디 2주차

https://opentutorials.org/course/2136/
생활코딩 강의를 이용해 진행하는 스터디 2주차.

4/1(일) :: 12강 '웹페이지를 표현하는 방법' 부터 14강 'URL을 이용한 정보의 전달'까지.


Express-웹페이지를 표현하는 방법


지난 강의에 이어서... 프로그래밍 기법을 이용해 동적인 컨텐츠를 제공할 수 있다. 서버 애플리케이션이 미리 작성된 html 파일을 그대로 읽어 던지는 것이 아니라 클라이언트인 브라우저가 해석 가능한 형식(주로 html)으로 컨텐츠를 새롭게 '생성하여' 클라이언트에게 전달하는 방식이다. 정적인 서비스 방식과 동적인 서비스 방식은 서로 장단점을 갖는다. 매 요청마다 컨텐츠를 새롭게 생성하는 것은 서버 자원이 그만큼 소모되는 것이기 때문에 정적인 요소는 정적인 방식으로 서비스 하는 것이 맞다. Node.js를 사용하는 일정 규모 이상의 서비스에선 Node.js 앞단에 정적인 리소스만 serving하는 전용 웹서버를 두기도 하는 것으로 알고 있다.

기존 Javascript에선 multi-line 문자열을 표현하거나 동적인 문자열을 만들기 꽤 번거로웠는데 이를 해소하기 위해 Commented text라는 기능이 추가되었다. Grave accent 또는 백틱이라(` `) 불리는 문자로 문자열을 감싸주는 방법인데 multi-line 문자열을 쉽게 표현할 수 있고, 플레이스 홀더 '${ }'를 이용한 문자열 생성도 지원한다. (template literal. ES2015에선 'template strings'라 했다고 함.)

아래 예시처럼 읽기에도, 쓰기에도 편리하다.


Express-템플릿 엔진 (Jade)


템플릿 엔진을 사용하면 html을 좀 더 편하게 만들 수 있다. Node.js에선 'jade'라 하는 템플릿 엔진을 많이 쓰는 듯 하다. jade를 사용하려면 당연히 jade를 설치해야 하고 jade의 문법을 이해하고 있어야 한다.

설치와 의존성 추가 방법은 다른 모듈과 동일한 방법으로 제공된다.
$ npm install jade --save

서버 코드에 jade를 사용하겠다는 선언과 함께 jade 파일의 위치를 지정해 준다. 그리고 작성된 jade 파일을 사용자에게 전달할 땐 'render' 메서드를 이용한다.
app.set('view engine', 'jade');
app.set('views', './view');

tip> app.locals.pretty = true;로 해주면 jade가 생성하는 html 코드를 예쁘게(?) 줄바꿈 해준다. 브라우저에서 생성된 코드를 살펴볼 때 유용한 옵션이다.

jade에서 줄바꿈은 새로운 문법의 시작을 뜻하고, 들여쓰기는 앞선 태그 선언의 안 쪽에 위치하는 것을 가리키는 용도로 쓰인다. 프로그래밍 키워드 앞엔 '-'를 붙여 jade에게 프로그래밍 표현임을 알려주어야 한다. 변수의 전달은 render 함수의 두번째 인자로 key-value object로 작성된 변수 묶음을 던져주는 방식으로 동작한다. jade에선 '=' 할당식을 이용해 전달받은 key를 그대로 쓰면 된다.

html
  head
    title= title
  body
    h1 Hello Jade
    ul
      -for (var i = 0; i < 5; i++)
        li coding
    div= time


Express-URL을 이용한 정보의 전달


쿼리 스트링. http://a.com/topic?id=1
쿼리 스트링을 이용하면 topic이란 하나의 path로 보다 풍부한 요청식을 만들어낼 수 있다.

/topic?id=0 이라는 표현에서. 'id=0'이라는 값은 req 객체의 query dictionary에 기록된다. 따라서 '/topic' 핸들링 함수에서 req.query.id와 같은 표현식으로 값을 얻을 수 있다.


그런데.. topic?id=0 보다는 topic/0로 표현하는 것이 구조적으로 좀 더 깔끔하고 직관적이란 걸 알 수 있다. 이러한 표현방식을 Semantic(의미론적) URL이라 하는데 현대적인 웹-애플리케이션에서는 쿼리 스트링 방식보다 Semantic 방식을 선호하므로 알고 넘어갈 필요가 있다.
https://en.wikipedia.org/wiki/Clean_URL

전 예제와 다르게 end-point가 '/topic/:id'로 변경된 것에 유의하자. 그리고 ':id'에 매칭된 값이 이전 예제와 다르게 query 프로퍼티에 담기지 않고 params에 기록된다.



쿼리 스트링에 대한 예제는 아래에..
https://expressjs.com/en/api.html#req.query
// GET /search?q=tobi+ferret
req.query.q
// => "tobi ferret"

// GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
req.query.order
// => "desc"

req.query.shoe.color
// => "blue"

req.query.shoe.type
// => "converse"

4/3(화) :: 15강 'Express-POST 요청을 처리하는 방식에 대해' 부터 17강 '웹 애플리케이션 제작'까지.


Express-웹페이지를 표현하는 방법


GET 요청과 POST 요청에 대해..
GET은 정보에 대한 조회를 위해, POST는 정보에 대한 입력을 위해 주로 쓴다. 사용자 입력 데이터는 일반적으로 <form> 태그를 이용해 전달한다. form 태그를 jade 방식으로 선언하려면 대략 다음과 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    form(action='/form_receiver' method='post')
      p
        input(type='text' name='title')
      p
        textarea(name='description')
      p
        input(type='submit')

위의 예시와 같이 jade에서 들여쓰기는 매우 중요하다. 속성은 '( )' 괄호를 이용해 지정하는데, form 태그의 경우 데이터가 전달되어야 하는 엔드포인트를 'action' 속성에 지정하게끔 되어 있다. 그리고 'method' 속성을 이용해 요청 타입을 구분하게 된다. 위 예시에서는 method 값이 post로 되어있고, 그렇게 하는 것이 맞지만 get으로 지정할 수도 있다. (그렇지만 form 데이터는 반드시 POST로 처리하자. GET으로 처리할 경우 form 데이터가 URL에 모두 드러나게 되어 보안에 취약하다. 또한 URI를 표현하는 길이에 대한 제한이 있으므로 긴 길이의 데이터 포함시 이를 적절히 처리하지 못하는 문제가 발생할 수 있다.)

일반적인 form 데이터는 request의 Content-Type이 기본적으로 'application/x-www-form-urlencoded'로 지정된다. (application/x-www-form-urlencoded는 key=value&key=value... 형식으로 데이터를 다룬다.) POST 요청에 대해선 추가로 body를 인코딩해서 전달하는데 express는 기본적으로 이러한 POST 요청에 대한 body 파싱을 해주지 않는다. 이를 처리하기 위해선 'body-parser'라는 미들웨어를 사용해야 한다. body-parser 미들웨어는 요청 body를 파싱해서 req.body 객체에 값을 넣어주는 역할을 한다. 사용자는 req.body에서 필요한 값을 꺼낼 수 있다.

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended : false }));

extended 옵션이 false면 querystring 라이브러리로 body를 파싱하고, true면 qs라는 라이브러리로 body를 파싱한다. 자세한 건 잘 모르겠다.

팁 - Nodejs를 자동으로 재시작


npm install supervisor -g 로 supervisor 모듈을 설치하면,

node.js로 작성한 서버 코드 변경에 대해 서버 재시작 없이 변경점을 바로 확인할 수 있다.
일종의 watcher인 셈인데 사용하는 입장에서 매우 편리하다고 할 수 있다.

웹 애플리케이션 제작


본격적인(?) 웹 애플리케이션 제작. 강의 예제는 파일 read/write를 이용한 간단한 작성/조회 애플리케이션이다. 여기까지만 공부해도 배울 점이 꽤 많다. 완성된 형태는 아래와 같다.


앞선 강의에서 익힌 기본적인 라우터 설정과 jade 템플릿 사용법을 이용해서 위와 같은 애플리케이션을 제작하게 된다.

다른 부분보다 jade 표현 방식이 재미있는 것 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    h1
      a(href='/topic/') Server Side JavaScript
    ul
      each topic in topics
        li
          a(href='/topic/' + topic)= topic
    article
      h2= title
      = description
    br
    div
      a(href='/topic/new') new

서버 코드에선 특징적인 내용은 없고, 여러 개의 요청을 하나의 엔드포인트 핸들링 메서드에서 처리하는 방식이 인상적이다.


위 사진과 같이 요청에 대한 처리 로직이 비슷한 경우 배열을 이용해 하나의 함수로 처리할 수 있다. ['/topic', '/topic/:id']


4/4(수) :: 18강 '파일 업로드' 부터 19강 '데이터베이스'까지.


파일 업로드


파일 업로드를 구현하는 방법엔 여러가지가 있겠지만 그 중 multer 모듈을 사용하기로 한다. multer 모듈은 파일 업로드를 지원하는데 설치와 사용 방법은 다른 모듈과 같다.

npm install --save multer
var multer = require('multer');

업로드를 지원하는 폼을 준비해보자. 파일의 경우 input 타입을 file로 지정해야 하고, form 태그의 enctype을 multipart/form-data라 지정해 주어야 한다.
강의에서 나온 예제 jade 뷰는 아래와 같다.

doctype html
html
  head
    meta(charset='utf-8')
  body
    form(action='upload' method='post' enctype='multipart/form-data')
      input(type='file' name='userfile')
      input(type='submit')

남은 것은 전송한 파일을 서버에서 취득하는 방법과 특정 위치에 저장하는 방법에 대한 것인데 multer가 바로 이러한 기능을 지원한다.

var upload = multer({ dest: 'uploads/' });

multer는 함수인데 multer를 호출하면 업로드를 지원하는 미들웨어를 반환해준다. multer는 옵션 객체를 인자로 받는데 옵션엔 파일이 업로드될 위치와 파일 필터 등을 지정할 수 있게 구성되어 있다. multer의 경우 핸들링 메서드의 두번째 인자로 upload.single()과 같은 코드를 삽입해서 처리하는 것이 특징적인 부분이다. single 함수의 인자엔 file 타입을 갖는 input 태그의 name 속성을 지정해주어야 multer가 처리할 수 있다.

multer는 사용자가 요청한 데이터에서 파일이 포함되어 있다면 파일을 가공해서 req.file 프로퍼티에 암시적으로 추가하도록 되어있다. file 객체를 콘솔에 찍어보면 다음과 같다.


multer의 고급 기능으로 프로그래밍 방식으로 파일의 위치나 형식을 변경할 수 있다. 강의에 나온 예제는 아래와 같은데. filename 함수를 통해 파일의 이름을 지정해줄 수 있고, destination 함수를 통해 파일이 기록되는 경로를 지정해 줄 수 있다.



데이터베이스

별 내용은 없어서 패스. (문제 유형에 맞는 데이터베이스를 선정하는 능력을 기르려면 다양한 데이터베이스 환경에서의 경험이 필요하다. 최근 백업, 업그레이드, 하드웨어 안정성 부분을 클라우드 사업자가 잘 지원해주고 있기 때문에 러닝 커브는 확실히 줄어든 편이다.)


댓글 없음:

댓글 쓰기