w w w. h a n b i t . c o . k r
이것이 프로그래밍이다! 저자 직강 동영상 제공!
이것이 안드로이드다
이것이 C언어다
이것이 자바다
진정한 안드로이드 개발자로 이끌어줍니다.
세상에 없던 새로운 C언어 입문서 탄생!
가장 중요한 프로그래밍 언어를 하나 배워야 한다면, 결론은 자바다!
SDK 5.0 롤리팝 호환!
삼성, LG에서 펼쳐졌던 전설의 명강의를 풀타임 동영상 강좌로!
중급 개발자로 나아가기 위한 람다식, JavaFX, NIO 수록
이보다 더 확실한 방법은 없다, 칠판강의
책만 보고, 동영상 강좌로도 만족하지 못했다면 Daum 카페 '슈퍼드로이드'에서 만나요
전체 동영상 강좌 유투브 전격 공개!
자바의 모든 것을 알려주는 인터넷 강의 궁금한 것은 카페에서!
cafe.daum.net/superdroid
http://goo.gl/tJK3Tu
cafe.naver.com/thisisjava
박성근 저 | 1,164쪽 | 45,000원
서현우 저 | 708쪽 | 25,000원
신용권 저 | 1,224쪽 | 30,000원
w w w. h a n b i t . c o . k r
지금은 모던 웹 시대! 모던 웹 디자인을 위한
모던 웹을 위한
HTML5 + CSS3 입문 HTML5 분야 부동의 1위 도서
JavaScript + jQuery 입문
HTML5 표준안 확정에 맞춘 완전 개정판의 귀환!
자바스크립트에서 제이쿼리, 제이쿼리 모바일까지 한 권으로 끝낸다!
HTML5 권고안과 최신 웹 브라우저 환경 대응
시대의 흐름에 맞춰 다시 쓴 자바스크립트 교과서
윤인성 저 | 624쪽 | 30,000원
윤인성 저 | 980쪽 | 32,000원
모던 웹을 위한
HTML5 + CSS3 정복
Node.js
프로그래밍 페이스북, 월마트, 링크드인은 왜 Node.js를 선택했는가?
필요한 것만 배워 바로 현장에서 쓰는 HTML5
이 물음에 대한 답은 Node.js가 보여주는 빠른 처리 능력 때문이다.
순서대로 읽으며 실습할 수 있는 HTML5 자습서
윤인성 저 | 484쪽 | 25,000원
김상형 저 | 700쪽 | 32,000원
| 표지 설명 | 책의 표지에 있는 동물은 검은종다리Melanocorypha yeltoniensis와 흰날개종다리Melanocorypha leucoptera입니다. 두 새는 철새이며 카자흐스탄과 중앙 러시아 스텝 지대에 주로 서식합니다. 두 새 모두 이곳에서 번식하 지만, 검은종다리 수컷은 겨울에도 카자흐스탄 초원에 머무는 반면 암컷은 남쪽으로 이동합니다. 흰날개종다리는 겨울이 오면 훨씬 먼 북서쪽으로, 흑해 너머까지 이동하곤 합니다. 이 새들 이 목격되는 범위는 점점 넓어지고 있습니다. 흰날개종다리는 유럽의 2~4배 정도 되 는 넓이의 지역에서 목격되지만, 검은종다리는 유럽의 80배는 될 만큼 넓은 범위에 서 목격되곤 합니다. 검은종다리라는 이름이 붙은 이유는, 이 새의 수컷은 거의 몸 전체가 검은색이기 때문입니다. 반면 암컷은 다리와 날개 아 래쪽 깃털만 수컷과 비슷한 검은색이고, 나머지 부분은 회색입니다. 흰날개종다리의 날개 깃털은 검은색, 흰색, 밤색이 섞여 있습니다. 흰날개종다리의 등에는 회색 줄이 있으며 몸 아래쪽은 옅은 흰색입니다. 흰날개종다리 수컷은 암컷과 거의 비슷하지만, 밤색 벼슬이 있다는 점이 다릅니다. 두 새 모두 독특하게 노래하듯 지저귀며, 수백 년이 넘도록 많은 작가와 음악가가 이 새의 노랫소리를 듣고 상상력을 발휘 해왔습니다. 두 새는 모두 벌레와 씨앗을 먹고, 땅에 둥지를 틉니다. 검은종다리는 둥지로 배설물을 가져와 벽을 만들거나 일종의 포장을 하는데, 이런 행동을 하는 이유는 아직 밝혀지지 않았습니다. 오라일리에서 표지에 소개하는 동물 중 상당수는 멸종 위험에 처해 있습니다. 이들은 모두 우리 세계에 중요한 동물입니 다. 이 동물들을 돕고 싶다면 animals.oreilly.com으로 오십시오. 표지 그림은 라이데커의 『Royal Natural History』에서 가져왔습니다.
한 권으로 끝내는 Node & Express : 웹사이트 구축을 위한 서버 사이드 자바스크립트 초판발행 2015년 10월 1일 지은이 이선 브라운 / 옮긴이 한선용 / 펴낸이 김태헌 펴낸곳 한빛미디어 (주) / 주소 서울시 마포구 양화로 7길 83 한빛미디어(주) IT출판부 전화 02 – 325 – 5544 / 팩스 02 – 336 – 7124 등록 1999년 6월 24일 제10 – 1779호 / ISBN 978 – 89 –6848–222–9
93000
총괄 배용석 / 책임편집 최현우 / 기획 조희진, 이상복 / 편집 이상복 디자인 표지 송경선, 내지 여동일, 조판 방유선 영업 김형진, 김진불, 조유미 / 마케팅 박상용, 서은옥, 송경석 / 제작 박성우 이 책에 대한 의견이나 오탈자 및 잘못된 내용에 대한 수정 정보는 한빛미디어(주)의 홈페이지나 아래 이메일로 알려주십시오. 잘못된 책은 구입하신 서점에서 교환해드립니다. 책값은 뒤표지에 표시되어 있습니다. 한빛미디어 홈페이지 www.hanbit.co.kr / 이메일 ask@hanbit.co.kr
Published by HANBIT Media, Inc. Printed in Korea Copyright © 2015 HANBIT Media, Inc. Authorized translation of the English edition of Web Development with Node and Express, ISBN 9781491949306 © 2014 Ethan Brown. This translation is published and sold by permission of O’Reilly Media, Inc., whitch owns or controls all rights to publish and sell the same.
이 책의 저작권은 오라일리와 한빛미디어(주)에 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 복제 및 무단 전재를 금합니다.
지은이·옮긴이 소개
지은이
이선 브라운 Ethan Brown
인터랙티브 마케팅 에이전시인 팝 아트의 시니어 소프트웨어 엔지니어. 팝 아트의 웹사이트 및 웹 서비스를 책임지고 있으며, 그가 담당하는 고객사는 작은 기업부터 세계적 기업까지 다양합니다. 이선은 임베디드부터 웹까지 20년 넘게 프로그래밍을 해왔으며, 자바스크립트 스택이 웹 플랫폼의 미래가 될 거라고 확신합니다.
옮긴이
한선용
웹 표준과 자바스크립트에 관심이 많은 번역가. 2008년부터 웹 관련 일을 했으며, ‘WCAG 2.0 을 위한 일반적 테크닉’ 등의 문서를 번역해 웹에 올렸습니다. 번역서로 『자바스크립트를 말하다』 (2014 ), 『데이터 시각화를 위한 데이터 인사이트』(2014 ), 『모던 웹을 요리하는 초간편 HTML5 Cookbook』(2012 ), 『Head First jQuery』(2012 ), 『jQuery Mobile』(2012 ), 『자바스크립트 성 능 최적화』(2011, 이상 한빛미디어), 『자바스크립트 프로그래밍』(2013 ), 『처음 배우는 jQuery』 (2012 ), 『에릭 마이어의 CSS 노하우』(2011, 이상 인사이트) 등이 있습니다.
4
옮긴이의 말
두말하면 잔소리지만, 현재 웹에서 가장 영향력이 강한 언어는 자바스크립트입니다. 처음 등장 한 후 얼마 동안은 별 볼 일 없다는 평가를 받았지만, ‘어디서나 쓰인다’는 강력한 배경 덕에 수 많은 이들이 자바스크립트를 배울 수밖에 없었습니다. 그런 과정에서 다른 언어의 전문가였던 개발자들이 자신이 사용하던 언어의 장점을 자바스크 립트에서도 살리길 원했고, 자바스크립트는 그렇게 다른 언어의 장점을 하나둘씩 흡수하면서 점차 발전하고 있습니다. 구글을 비롯해 몇몇 회사에서 자바스크립트를 대체하려는 시도를 했 으나 수포로 돌아간 것은, 그들이 준비한 대안에 문제가 있었다기보다는 익숙한 언어를 계속 사용하려는 사람들이 훨씬 많았기 때문일 겁니다. 익숙한 언어를 계속 사용하고 싶다는 생각이 발전하면서 ‘클라이언트 전용으로 생각했던 자바 스크립트를 서버에서도 사용할 수 없을까?’ 하는 아이디어가 나왔고 그 아이디어를 발전시킨 프로젝트가 여럿 등장했습니다. 그중에서도 가장 주목을 받은 것이 바로 노드입니다. 이 책에서 다루는 익스프레스는 노드용 라이브러리입니다. 익스프레스는 기존 라이브러리들이 여러 기능을 한데 모아놓은 형태를 취한 것과 달리, 모듈 개념을 적극 채용해 딱 필요한 요소만 가져다 사용하는 방식을 쓰고 있습니다. 처음에는 조금 어렵게 느껴질 수 있지만, 불필요한 기 능이 없으므로 가볍고, 문제가 생겼을 때도 훨씬 쉽게 원인을 찾을 수 있다는 장점이 있습니다. 역자는 주로 PHP와 자바스크립트를 사용해서 일했고 노드는 아직 경험이 없었는데, 책이 워 낙 쉽게 잘 쓰여서 이 책을 따라 한 것만으로도 상당히 많은 지식을 얻을 수 있었습니다. 독자 여러분도 이 책을 읽으며 노드의 기본을 익히고 익스프레스를 이용해 쉽고 빠르게 노드 웹사이 트를 만들 수 있을 겁니다. 사람의 일은 무엇이든 혼자 할 수는 없는지라 감사 인사를 일일이 하려면 끝이 없겠으나, 누구보 다도 먼저 이 책을 살펴보고 있을 독자 여러분께 감사합니다. 좋은 책을 맡겨주신 한빛미디어, 거 친 번역을 수정하고 여러 부분에서 생각도 못 한 도움을 준 이상복 편집자께도 감사합니다. 늘 그렇지만, 모든 것에 대해 부모님께 감사합니다. 한선용 5
머리말
개발 커뮤니티와 대기업에서 널리 인정받는 강력하면서도 빨리 완성할 수 있는 기술 스택을 원 하는 웹 팀에게는 자바스크립트, 노드, 익스프레스 조합이 이상적 선택입니다. 훌륭한 웹 애플리케이션을 만드는 일이나 뛰어난 웹 개발자를 찾는 일은 모두 쉽지 않습니다. 훌륭한 애플리케이션으로 평가받으려면 기능이나 사용자 경험이 뛰어나면서도 비즈니스에 좋 은 영향을 끼쳐야 합니다. 다시 말해 배포나 지원이 신속하고 저렴하게 이루어질 수 있어야 합 니다. 익스프레스를 사용하면 총 비용을 낮추면서도 시장에 빨리 진입할 수 있어 비즈니스 세 계에 이상적입니다. 웹 개발자라면 자바스크립트를 조금이라도 써야만 합니다. 하지만 조금만 쓰라는 법은 없죠. 이선 브라운은 이 책을 통해 자바스크립트를 아주 많이 쓰는 방법을 알려줄 텐데, 특히 노드와 익스프레스 덕에 쉽게 배울 수 있을 겁니다. 노드와 익스프레스는 마치 자바스크립트가 가진 장점을 마구 쏟아내는 기관총 같습니다. 자바스크립트는 클라이언트 사이드 스크립트 언어로 가장 널리 쓰이는 언어입니다. 플래시와 는 달리, 주요 웹 브라우저는 모두 자바스크립트를 지원합니다. 웹에서 볼 수 있는 각종 애니메 이션은 대개 자바스크립트를 사용합니다. 자바스크립트 없이 클라이언트 쪽의 최신 기능을 이 용하기란 사실 거의 불가능합니다. 자바스크립트에 한 가지 문제가 있다면 엉성한 프로그래밍을 완전히 배제하기 어렵다는 겁니 다. 하지만 노드 생태계에서 제공하는 프레임워크, 라이브러리, 도구를 쓰면 개발 속도도 빨라 지고 좋은 코딩 습관을 익힐 수 있습니다. 덕분에 더 나은 애플리케이션을 시장에 내놓을 수 있 겠죠. 이제 우리에겐 대기업에서 지원하고, 쓰기 쉽고, 최신 브라우저에 맞게 디자인되었으며 클라이 언트와 서버 양쪽에서 훌륭한 프레임워크와 라이브러리로 지원받는 프로그래밍 언어가 생겼습 니다. 혁명이라 부를 일입니다. 스티브 로젠바움 팝 아트Pop Art, Inc. 회장 겸 최고경영자
6
이 책에 대하여
대상 독자 물론 이 책은 자바스크립트, 노드, 익스프레스를 사용해 웹 애플리케이션을 만드는 프로그래머 를 위한 책입니다. 만들고 있는 것이 전통적 웹사이트인지 RESTful API인지, 또는 그 중간의 어떤 것인지는 중요하지 않습니다. 노드 개발에서 신나는 부분은 완전히 새로운 프로그래머들 이 진입하고 있다는 겁니다. 자바스크립트는 배우기 쉽고 유연해서 프로그래밍을 독학한 사람 들도 전 세계에서 참여하고 있습니다. 컴퓨터과학 역사에서 이렇게 부담 없는 프로그래밍 언어 는 처음입니다. 프로그램을 배우며 막혔을 때 도움을 받을 수 있는 온라인 자원도 양에서나 질 에서나 정말 놀랍습니다. 신입 프로그래머 여러분, 환영합니다. 물론 필자처럼 한동안 프로그래머 일을 해온 사람도 있을 겁니다. 우리 세대의 프로그래머들이 대개 그렇지만, 필자도 어셈블러와 베이식으로 시작해 파스칼, C++, 펄, 자바, PHP, 루비, C,
C# 등을 거쳐 자바스크립트에 이르렀습니다. 필자는 대학에서 ML이나 리스프, 프롤로그 같은 언어도 배웠습니다. 이들 언어 중 필자의 마음에 쏙 든 언어도 여럿 있었지만, 자바스크립트만 큼 미래가 밝은 언어는 없었습니다. 그래서 이 책을 쓰면서 경험 많은 프로그래머, 다른 언어를 더 좋아할 수도 있는 프로그래머가 이 책을 읽을 수도 있다는 점을 항상 생각했습니다. 노드 경험은 없어도 되지만, 자바스크립트는 어느 정도 알아야 합니다. 프로그래밍이 처음이라 면 먼저 코드아카데미(http://www.codecademy.com/tracks/javascript )를 권합니다. 경험 많은 프로그래머라면 『더글라스 크락포드의 자바스크립트 핵심 가이드』 (한빛미디어, 2008년) 를 읽어보십시오. 이 책에서 사용한 예제는 노드가 설치된 시스템이라면 윈도우, OS X, 리눅 스를 가리지 않고 실행됩니다. 예제는 명령줄(터미널) 사용자를 생각하고 만들었으므로 시스 템에 설치된 터미널을 어느 정도 쓸 수 있어야 합니다. 무엇보다 이 책은 고양된 프로그래머를 위한 책입니다. 인터넷의 미래에 흥분하고 동참하고자 하는 프로그래머 말입니다. 새로운 것, 새 기술을 배우고 웹 개발의 새로운 방법을 찾는 데 열 정적이어야 합니다. 만약 그렇지 않다면, 이 책을 마칠 때쯤에는 그렇게 되길 바랍니다.
7
이 책의 구성 1장과 2장은 노드와 익스프레스를 소개하고, 책에서 쭉 사용할 도구도 몇 가지 소개합니다. 3 장과 4장에서는 익스프레스를 사용해 책에서 쭉 쓸 샘플 웹사이트의 뼈대를 만듭니다.
5장은 테스트와 QA에 관한 내용이고, 6장은 노드의 중요한 구조와 익스프레스에서 그 구조를 어떻게 확장하고 사용하는지 살펴봅니다. 7장에서는 익스프레스로 유용한 웹사이트를 만드는 초석인 템플릿(핸들바)에 대해 배웁니다. 8장과 9장은 쿠키, 세션, 폼 핸들러에 대해 배웁니 다. 이들은 웹사이트의 기본적인 기능입니다.
10장에서는 익스프레스의 주요 구성 요소이기도 한 커넥트를 중심으로 ‘미들웨어’ 개념을 설명 합니다. 11장에서는 미들웨어를 통해 서버에서 이메일을 보내는 방법, 이메일에 항상 따라오 는 보안 및 레이아웃 문제를 설명합니다.
12장에서는 실무 관심사를 미리 훑어봅니다. 물론 실무 웹사이트를 만드는 데 필요한 것을 다 설명하지는 않지만, 실무에 대해 미리 생각해두면 나중에 찾아올 두통거리를 많이 줄일 수 있 습니다.
13장은 지속성에 관한 내용이며 문서 데이터베이스의 선두주자인 몽고DB에 맞춰 설명합니 다.
14장은 URL과 콘텐츠를 연결하는 라우팅에 대해 자세히 설명하고, 15장에서는 익스프레스로 API를 만드는 방법을 배웁니다. 16장은 정적 콘텐츠를 전송하는 세부 사항과 성능을 최대화하 는 방법에 초점을 맞췄습니다. 17장에서는 인기 있는 모델-뷰-컨트롤러(MVC ) 패러다임을 살펴보고 익스프레스와 어떻게 어울리는지도 살펴봅니다.
18장은 보안에 관한 내용입니다. 타사 인증을 중심으로 애플리케이션에서 인증과 승인을 어떻 게 사용하는지 배우고, 사이트에 HTTPS를 적용하는 방법도 배웁니다.
19장은 타사 서비스와 통합하는 방법을 설명합니다. 예제 서비스는 트위터, 구글 지도, 웨더
8
언더그라운드입니다.
20장과 21장은 사이트 오픈에 관한 내용입니다. 사이트 오픈 전에 버그를 박멸할 디버그, 오픈 과정을 설명합니다. 22장은 중요한지만 종종 빼먹곤 하는 단계인 유지보수에 대한 내용입니 다.
23장은 책의 마지막 장입니다. 노드와 익스프레스를 더 배우기로 결심했다면 이 장에서 소개 하는 자원을 살펴보고 도움을 받으십시오.
예제 웹사이트 3장부터 쭉 ‘메도라크’라는 여행사의 웹사이트를 예제로 쓸 겁니다. 필자는 리스본 여행을 마치 고 돌아온 지 얼마 안 돼서 여행의 기쁨이 아직 마음에 남아 있는 상태라, 고향인 오리건 주의 가상 여행사를 예제 웹사이트로 선택했습니다(메도라크, 즉 들종다리는 오리건 주를 대표하는 새입니다). 메도라크 여행사는 여행자에게 ‘아마추어 지역 여행 가이드’를 소개하는 회사이고, 오토바이와 스쿠터를 대여하는 업체와 협력하고 있습니다. 또한, 메도라크 여행사는 지역 명소 가 담긴 데이터베이스를 운영하고 있으며 위치 기반 서비스도 제공합니다. 학습용 예제가 으레 그렇듯 메도라크 여행사 웹사이트도 부자연스러운 면은 있지만, 이 예제에 는 현실 속 웹사이트에서 마주칠 여러 문제가 들어 있습니다. 타사 구성 요소와의 통합, 지오로 케이션, 전자상거래, 성능, 보안 말입니다. 이 책의 초점은 서버 쪽 기반 구조이므로 예제 웹사이트를 완성하지는 않습니다. 이 사이트는 그저 예제에 깊이와 문맥을 더하기 위해 만든 가상 웹사이트일 뿐입니다. 독자 여러분은 자신 만의 웹사이트가 있을 텐데, 메도라크 여행사 예제를 템플릿으로 삼을 수 있습니다. 장별 예제 코드는 모두 깃허브에서 확인할 수 있습니다. https://github.com/EthanRBrown/web-development-with-node-and-express
9
감사의 말
이 책이 나오기까지는 그동안 살면서 함께했던 수많은 사람의 도움이 있었습니다. 이들이 필자 의 삶을 만들고, 필자를 만들고, 이 책을 만들었습니다. 먼저 팝 아트에 있는 모든 이에게 감사하고 싶습니다. 필자는 팝 아트에서 일하면서 컴퓨터공 학에 관한 열정을 다시 얻을 수 있었고, 함께 일한 사람들로부터 정말 많은 것을 배웠습니다. 이들의 지원이 없었다면 이 책은 존재할 수 없었을 겁니다. 이렇게 멋진 회사를 만들어준 스티 브 로젠바움에게, 필자를 채용하고 따듯하게 환영해준 훌륭한 리더인 델 올즈에게 감사합니다. 폴 인먼은 흔들림 없이 지원해줬고 컴퓨터공학에 관한 열정을 보여줬습니다. 토니 알페레스는 항상 따듯이 지원해줬고, 회사에 영향을 끼치지 않으면서도 책을 쓸 시간을 낼 수 있게 도와줬 습니다. 함께 일한 훌륭한 엔지니어들인 존 스켈턴, 딜런 할스트룀, 그레그 영, 퀸 마이클, CJ 스트리첼에게 감사합니다. 영감을 준 재커리 메이슨에게 감사합니다. 이 책이 『The Lost Books of the Odyssey』에 비할 수는 없겠지만, 그야 이건 내 책이니까요. 당신의 선례가 없었다면 중간에 의지가 꺾였을 겁니다. 가족이 없이는 나 자신도 없었을 겁니다. 더 바랄 수 없을 만큼 사랑으로 충만한 교육을 받았 고, 부모님의 사랑이 여동생에게도 똑같이 전해졌음을 압니다. 책을 쓸 기회를 준 사이먼 생 로랑에게, 한결같이 용기를 준 편집자 브라이언 앤더슨에게 감사 합니다. 오라일리에서 일하는 모든 이의 열정과 헌신에도 감사합니다. 기술 리뷰를 담당한 제 니퍼 피어스, 마이크 윌슨, 레이 비얄로보스, 에릭 엘리엇에게 감사합니다. 필자가 멋대로 보낸 제안을 검토하고 귀중한 피드백을 준 케이티 로버츠와 해나 넬슨에게 감사 합니다. 당신들이 거절했다면 책을 쓸 수 없었겠죠! QA 장에서는 크리스 코웰-샤가 정말 많이 도와줬습니다. 마지막으로 친구들에게도 감사를. 자네들이 없었다면 나는 책을 쓰다가 미쳐버렸을 거야. 바이 런 클레이턴, 마크 부스, 케이티 로버츠, 세라 루이스. 어디 가서 더 좋은 친구를 찾겠어? 비키 와 주디, 있어준 것만으로 고마워. 정말 사랑해.
10
CONTENTS
지은이·옮긴이 소개 ���������������������������������������������������������������������������������������������� 4
옮긴이의 말 ���������������������������������������������������������������������������������������������������������� 5
머리말 ����������������������������������������������������������������������������������������������������������������� 6
이 책에 대하여 ������������������������������������������������������������������������������������������������������ 7
감사의 말 ����������������������������������������������������������������������������������������������������������� 10
CHAPTER
1 익스프레스 소개
1.1.
자바스크립트의 혁명 ��������������������������������������������������������������������������������������������
21
1.2.
익스프레스 소개 ��������������������������������������������������������������������������������������������������
23
1.3.
익스프레스의 간단한 역사 �������������������������������������������������������������������������������������
25
1.4.
익스프레스 4.0으로 업그레이드 �����������������������������������������������������������������������������
25
1.5.
노드: 새로운 타입의 웹 서버 ����������������������������������������������������������������������������������
26
1.6.
노드 생태계 ��������������������������������������������������������������������������������������������������������� 27
1.7.
라이선스 ������������������������������������������������������������������������������������������������������������
CHAPTER
28
2 노드 시작하기 2.1.
노드 설치 �����������������������������������������������������������������������������������������������������������
31
2.2.
터미널 사용 ��������������������������������������������������������������������������������������������������������
32
2.3.
에디터 ���������������������������������������������������������������������������������������������������������������
34
2.4.
npm �����������������������������������������������������������������������������������������������������������������
35
2.5.
노드로 만드는 단순한 웹 서버 ���������������������������������������������������������������������������������
36
2.6.
익스프레스로 출발 ������������������������������������������������������������������������������������������������
41
11
CONTENTS
CHAPTER
3 익스프레스로 시간 절약 3.1.
스캐폴딩 �������������������������������������������������������������������������������������������������������������
43
3.2.
메도라크 여행사 웹사이트 �������������������������������������������������������������������������������������
44
3.3.
초기 단계 ����������������������������������������������������������������������������������������������������������� 44
3.4.
마치며 ��������������������������������������������������������������������������������������������������������������
CHAPTER
CHAPTER
55
4 모양새 갖추기 4.1.
모범 사례 ������������������������������������������������������������������������������������������������������������
57
4.2.
버전 관리 �����������������������������������������������������������������������������������������������������������
58
4.3.
이 책에서 깃을 활용하는 법 �����������������������������������������������������������������������������������
59
4.4.
npm 패키지 �������������������������������������������������������������������������������������������������������
62
4.5.
프로젝트 메타데이터 ��������������������������������������������������������������������������������������������
64
4.6.
노드 모듈 �����������������������������������������������������������������������������������������������������������
64
5 품질보증
5.1.
QA에 가치가 있나? ��������������������������������������������������������������������������������������������� 68
5.2.
논리 vs 표현 �������������������������������������������������������������������������������������������������������
70
5.3.
테스트 타입 ��������������������������������������������������������������������������������������������������������
70
5.4.
QA 테크닉 개관 �������������������������������������������������������������������������������������������������
71
5.5.
서버 운영 ����������������������������������������������������������������������������������������������������������
72
5.6.
페이지 테스트 �����������������������������������������������������������������������������������������������������
72
12
5.7.
교차 페이지 테스트 ����������������������������������������������������������������������������������������������
76
5.8.
논리 테스트 ��������������������������������������������������������������������������������������������������������
80
5.9.
린트 �����������������������������������������������������������������������������������������������������������������
81
5.10. 링크 체크 ����������������������������������������������������������������������������������������������������������
82
5.11. 그런트를 통한 자동화 ������������������������������������������������������������������������������������������
83
5.12. 지속적 통합(CI) ��������������������������������������������������������������������������������������������������
86
CHAPTER
6 요청과 응답 객체
6.1.
URL의 각 부분 ��������������������������������������������������������������������������������������������������� 87
6.2.
HTTP 요청 규칙 ������������������������������������������������������������������������������������������������� 89
6.3.
요청 헤더 �����������������������������������������������������������������������������������������������������������
89
6.4.
응답 헤더 �����������������������������������������������������������������������������������������������������������
90
6.5.
인터넷 미디어 타입 ����������������������������������������������������������������������������������������������
91
6.6.
요청 본문 �����������������������������������������������������������������������������������������������������������
91
6.7.
매개변수 ������������������������������������������������������������������������������������������������������������
92
6.8.
요청 객체 �����������������������������������������������������������������������������������������������������������
92
6.9.
응답 객체 �����������������������������������������������������������������������������������������������������������
95
6.10. 더 많은 정보 얻기 ������������������������������������������������������������������������������������������������
97
6.11. 핵심 기능 �����������������������������������������������������������������������������������������������������������
98
CHAPTER
7 핸들바를 사용한 템플릿
7.1.
절대 규칙은 이것 하나뿐 �������������������������������������������������������������������������������������
107
7.2.
템플릿 엔진 선택 �����������������������������������������������������������������������������������������������
107
7.3.
다른 접근법: 제이드 ��������������������������������������������������������������������������������������������
108
13
CONTENTS
CHAPTER
CHAPTER
7.4.
핸들바 기초 ������������������������������������������������������������������������������������������������������
110
7.5.
마치며 �������������������������������������������������������������������������������������������������������������
125
8.1.
클라이언트 데이터를 서버로 보내기 ����������������������������������������������������������������������
127
8.2.
HTML 폼 ��������������������������������������������������������������������������������������������������������
128
8.3.
인코딩 �������������������������������������������������������������������������������������������������������������
129
8.4.
폼을 처리하는 다른 방법 ��������������������������������������������������������������������������������������
129
8.5.
익스프레스를 이용한 폼 처리 �������������������������������������������������������������������������������
132
8.6.
AJAX 폼 처리 ��������������������������������������������������������������������������������������������������
134
8.7.
파일 업로드 ������������������������������������������������������������������������������������������������������
136
8.8.
제이쿼리 파일 업로드 �����������������������������������������������������������������������������������������
139
8 폼 처리
9 쿠키와 세션
9.1.
인증 위임 ���������������������������������������������������������������������������������������������������������
145
9.2.
쿠키와 익스프레스 ���������������������������������������������������������������������������������������������
146
9.3.
쿠키 살펴보기 ���������������������������������������������������������������������������������������������������
148
9.4.
세션 ����������������������������������������������������������������������������������������������������������������
148
9.5.
세션을 이용한 플래시 메시지 구현 ������������������������������������������������������������������������
151
9.6.
세션을 써야 할 곳 ����������������������������������������������������������������������������������������������
153
14
CHAPTER
10 미들웨어
10.1. 공통 미들웨어 ��������������������������������������������������������������������������������������������������� 161
10.2. 타사 미들웨어 ��������������������������������������������������������������������������������������������������� 165
CHAPTER
11 이메일 보내기
11.1. SMTP, MSA, MTA ������������������������������������������������������������������������������������������ 167
11.2. 이메일 받기 ������������������������������������������������������������������������������������������������������ 168
11.3. 이메일 헤더 ������������������������������������������������������������������������������������������������������ 168
11.4. 이메일 형식 ������������������������������������������������������������������������������������������������������ 169
11.5. HTML 이메일 �������������������������������������������������������������������������������������������������� 170
11.6. 노드메일러 ������������������������������������������������������������������������������������������������������� 171
11.7. 대량 이메일에 더 적합한 옵션 ������������������������������������������������������������������������������ 174
11.8. HTML 이메일 보내기 ���������������������������������������������������������������������������������������� 175
11.9. 이메일을 사이트 모니터링 도구로 사용 ������������������������������������������������������������������ 181
CHAPTER
12 실무 관심사
12.1. 실행 환경 ��������������������������������������������������������������������������������������������������������� 183
12.2. 환경별 설정 ������������������������������������������������������������������������������������������������������ 185
12.3. 웹사이트 확장 ��������������������������������������������������������������������������������������������������� 186
12.4. 웹사이트 모니터링 ��������������������������������������������������������������������������������������������� 195
12.5. 스트레스 테스트 ������������������������������������������������������������������������������������������������ 196
15
CONTENTS
CHAPTER
13 지속성
13.1. 파일시스템 지속성 ��������������������������������������������������������������������������������������������� 199
13.2. 클라우드 지속성 ������������������������������������������������������������������������������������������������ 202
13.3. 데이터베이스 지속성 ������������������������������������������������������������������������������������������ 203
CHAPTER
14 라우팅
14.1. 라우트와 SEO �������������������������������������������������������������������������������������������������� 220
14.2. 서브도메인 ������������������������������������������������������������������������������������������������������� 220
14.3. 라우트 핸들러는 미들웨어입니다 �������������������������������������������������������������������������� 221
14.4. 라우트 경로와 정규 표현식 ���������������������������������������������������������������������������������� 223
14.5. 라우트 매개변수 ������������������������������������������������������������������������������������������������ 224
14.6. 라우트 정리 ������������������������������������������������������������������������������������������������������ 225
14.7. 모듈에서 라우트 선언 ����������������������������������������������������������������������������������������� 226
14.8. 핸들러를 논리적 그룹으로 묶기 ���������������������������������������������������������������������������� 227
14.9. 자동으로 뷰 렌더링 �������������������������������������������������������������������������������������������� 229
14.10. 라우트를 정리하는 다른 방법 ������������������������������������������������������������������������������� 230
CHAPTER
15 REST API와 JSON
15.1. JSON과 XML �������������������������������������������������������������������������������������������������� 232
15.2. API ���������������������������������������������������������������������������������������������������������������� 233
15.3. API 에러 보고 ��������������������������������������������������������������������������������������������������� 234
16
15.4. 크로스 소스 자원 공유(CORS) ����������������������������������������������������������������������������� 235
15.5. 데이터 저장 ������������������������������������������������������������������������������������������������������ 236
15.6. 테스트 ������������������������������������������������������������������������������������������������������������� 237
15.7. 익스프레스를 이용한 API 제공 ����������������������������������������������������������������������������� 238
15.8. REST 플러그인 사용 ����������������������������������������������������������������������������������������� 240
15.9. 서브도메인 사용 ������������������������������������������������������������������������������������������������ 243
CHAPTER
16 정적 콘텐츠
16.1. 성능에 관한 고려 사항 ���������������������������������������������������������������������������������������� 246
16.2. 미래에도 안전한 웹사이트 ����������������������������������������������������������������������������������� 247
16.3. 서버 자바스크립트 속의 정적 자원 ������������������������������������������������������������������������ 252
16.4. 클라이언트 자바스크립트 속의 정적 자원 ��������������������������������������������������������������� 253
16.5. 정적 자원 전송 �������������������������������������������������������������������������������������������������� 255
16.6. 정적 콘텐츠 ������������������������������������������������������������������������������������������������������ 256
16.7. 번들링과 최소화 ������������������������������������������������������������������������������������������������ 257
16.8. 타사 라이브러리에 관한 노트 ������������������������������������������������������������������������������� 263
16.9. QA ����������������������������������������������������������������������������������������������������������������� 263
16.10. 마치며 ������������������������������������������������������������������������������������������������������������� 265
CHAPTER
17 익스프레스에서 MVC 구현
17.1. 모델 ���������������������������������������������������������������������������������������������������������������� 268
17.2. 뷰 모델 ������������������������������������������������������������������������������������������������������������ 269
17
CONTENTS
17.3. 컨트롤러 ���������������������������������������������������������������������������������������������������������� 272
17.4. 마치며 ������������������������������������������������������������������������������������������������������������� 274
CHAPTER
18 보안
18.1. HTTPS ����������������������������������������������������������������������������������������������������������� 275
18.2. 크로스 사이트 요청 위조 ������������������������������������������������������������������������������������� 284
18.3. 인증 ���������������������������������������������������������������������������������������������������������������� 286
18.4. 마치며 ������������������������������������������������������������������������������������������������������������� 305
CHAPTER
19 타사 API와의 통합
19.1. 소셜 미디어 ������������������������������������������������������������������������������������������������������ 307
19.2. 지오코딩 ����������������������������������������������������������������������������������������������������������� 317
19.3. 날씨 데이터 ������������������������������������������������������������������������������������������������������ 327
19.4. 마치며 ������������������������������������������������������������������������������������������������������������� 329
CHAPTER
20 디버그
20.1. 디버그의 첫 번째 원칙 ���������������������������������������������������������������������������������������� 331
20.2. REPL과 콘솔을 활용하십시오 ����������������������������������������������������������������������������� 333
20.3. 노드 내장 디버거 ����������������������������������������������������������������������������������������������� 333
20.4. 노드 인스펙터 ��������������������������������������������������������������������������������������������������� 334
20.5. 비동기 함수 디버그 �������������������������������������������������������������������������������������������� 338
18
CHAPTER
20.6. 익스프레스 디버그 ��������������������������������������������������������������������������������������������� 339
21 사이트 오픈
21.1. 도메인 등록과 호스팅 ������������������������������������������������������������������������������������������ 343
21.2. 마치며 ������������������������������������������������������������������������������������������������������������� 357
CHAPTER
22 유지보수
22.1. 유지보수 원칙 ���������������������������������������������������������������������������������������������������� 360
22.2. 코드 재사용과 리팩토링 �������������������������������������������������������������������������������������� 367
22.3. 마치며 ������������������������������������������������������������������������������������������������������������� 372
CHAPTER
23 추가 자원
23.1. 온라인 문서 ������������������������������������������������������������������������������������������������������ 373
23.2. 정기 간행물 ������������������������������������������������������������������������������������������������������ 374
23.3. 스택 오버플로 ��������������������������������������������������������������������������������������������������� 374
23.4. 익스프레스에 기여하기 ��������������������������������������������������������������������������������������� 377
23.5. 마치며 ������������������������������������������������������������������������������������������������������������� 380
한국어판 부록 ��������������������������������������������������������������������������������������������������������������������������������������������
381
찾아보기 ���������������������������������������������������������������������������������������������������������������������������������������������������
385
19
CHAPTER
1
익스프레스 소개
1.1. 자바스크립트의 혁명 이 책의 주요 주제를 소개하기 전에 역사적 배경을 조금 설명하는 게 좋겠습니다. 즉 자바스크 립트와 노드Node 이야기죠. 이제 정말 자바스크립트의 시대입니다. 자바스크립트는 초라한 클라이언트 스크립트 언어로 시작했지만 이제 클라이언트에는 어디서나 쓰이고, 노드 덕택에 서버 쪽 언어로도 쓰이기 시작 했습니다. 자바스크립트 기반 기술 스택에는 명확한 장점이 있습니다. 생각하는 방식을 계속 바꾸지 않 아도 됩니다. 자바스크립트 방식으로 생각하다가 PHP나 C#, 루비, 파이썬, 기타 서버 쪽 언어 방식으로 생각을 바꾸지 않아도 됩니다. 또한, 프론트엔드 기술자에게 서버 쪽 프로그래밍으 로 이동할 기회가 생겼습니다. 물론 서버 쪽 프로그래밍이 언어만 안다고 끝나는 건 아니고 그 외에도 배워야 할 부분이 많지만, 자바스크립트가 있으니 최소한 언어의 장벽은 해결된 셈입니다. 이 책은 자바스크립트 기술의 미래를 믿는 모든 사람을 위한 책입니다. 서버 측 개발 경험을 쌓 고 싶은 프론트엔드 기술자든, 서버 측 경험이 많지만 복잡하게 얽혀 있는 서버 쪽 언어 세계에 서 자바스크립트를 대안으로 보고 있는 개발자든 상관없습니다. 오랫동안 소프트웨어 기술자로 일했다면 여러 언어, 프레임워크, API가 유행하는 걸 봤을 겁 니다. 일부는 성공했고 일부는 구식이 됐습니다. 당신은 새 언어나 시스템을 빨리 배우는 능력
1장 - 익스프레스 소개
21
에 자신이 있을 겁니다. 언어를 새로 배우는 일이 많아질수록, 처음 접하는 언어에서 친숙한 부 분을 찾는 일이 점점 더 늘어날 겁니다. 이 부분은 대학에서, 저 부분은 직장에서 배웠던 식으 로 말입니다. 언어를 빨리 배우는 건 확실히 좋은 일이지만, 좀 지치기도 합니다. 이따금 그냥 일을 빨리 끝냈으면 좋겠다는 생각이 들 겁니다. 완전히 새로운 기술을 익히거나, 몇 달 혹은 몇 년간 쓰지 않았던 기술을 복습하는 일 없이 말입니다. 처음 자바스크립트를 보면 어떻게 이렇게 성공했는지 의아할 수도 있습니다. 사실 필자도 그랬 습니다. 3년 전에 누군가 내게 자바스크립트를 주요 언어로 선택하고, 거기에 더해 자바스크립 트에 관한 책까지 쓰라고 했다면 필자는 그 얘기를 한 사람이 미쳤다고 생각했을 겁니다. 자바 스크립트에 관한 흔한 편견을 필자 역시 갖고 있었습니다. 즉 자바스크립트를 ‘장난감’ 언어라 고 생각했습니다. 이를테면 아마추어가 쓰는 언어, 악용과 해킹에 무방비한 언어라는 편견 말 입니다. 사실 자바스크립트는 아마추어들의 진입 장벽을 낮췄을 뿐입니다. 그리고 엉성한 자바 스크립트 코드가 너무 많아서 편견이 생긴 겁니다. 적절한 비유가 있습니다. “죄는 미워하되 사 람은 미워하지 말라.” 불행히도 이런 편견이 널리 퍼져 있었으므로 사람들은 자바스크립트가 얼마나 강력하고 유연 하며 우아한 언어인지 알게 될 기회가 없었습니다. 자바스크립트는 1996년부터 존재했지만, 사람들은 이제서야 자바스크립트를 진지하게 바라보기 시작했습니다(자바스크립트의 매력적 인 기능 대다수는 2005년에야 추가되긴 했습니다). 이 책을 집어 든 당신은 자바스크립트에 관한 편견은 거의 없을 겁니다. 나처럼 그 편견에서 벗 어났거나, 아니면 처음부터 편견이 없었겠죠. 어느 쪽이든 당신은 행운아입니다. 그리고 필자 는 자바스크립트라는 즐겁고 놀라운 언어에서 파생한 기술인 익스프레스Express를 소개하려 합 니다. 사람들이 브라우저 스크립트 언어로서 자바스크립트의 힘과 표현력을 깨닫기 시작한 지 몇 년 지나, 2009년 라이언 달Ryan Dahl은 서버 쪽 언어로서 자바스크립트의 가능성을 보았고, 노드가 탄생했습니다. 인터넷 기술이 열매를 맺은 시간이라고 부를 만한 때였습니다. 루비(및 루비 온 레일즈)는 학교에서 가르치는 컴퓨터과학 에서 얻은 훌륭한 아이디어와 고유한 새 아이디어를 결합하여, 웹사이트와 웹 애플리케이션을 더 빨리 만드는 방법을 개발했습니다. 마이크로소프 트 역시 .NET을 훌륭히 완성하기까지 루비와 자바스크립트의 장점을 흡수하고 자바의 실수에 서 배웠으며 학계에서도 여러 가지를 배웠습니다.
22
한 권으로 끝내는 Node & Express
지금은 웹 개발이 아주 신나는 때입니다. 어디에서든 감탄스러운 새 아이디어가 튀어나오고, 오래된 아이디어가 재조명받는 일도 많습니다. 여러 해가 지났지만, 지금처럼 혁신적이고 흥분 되는 때는 없었습니다.
1.2. 익스프레스 소개 익스프레스 웹사이트에서는 익스프레스를 ‘단일, 다중 페이지, 하이브리드 웹 애플리케이션을 만드는 데 필요한 견고한 기능 집합을 제공하는, 최소화되고 유연한 노드 웹 애플리케이션 프 레임워크’라고 설명합니다. 정확히 무슨 뜻일까요? 하나씩 풀어봅시다. | 최소화되고 | 이 점은 익스프레스에서 가장 인상적인 측면입니다. 프레임워크 개발자들은 ‘적을수록 좋다’는 사실을 잊어버리곤 합니다. 익스프레스의 철학은 당신의 두뇌와 서버 사이에 최소한의 계층을 제공하는 겁니다. 최소한이라는 말이 견고하지 않다거나, 유용한 기능이 충분치 않다는 뜻은 아닙니다. 최소한이라는 표현은 익스프레스가 끼어드는 일을 줄여서 당신의 아이디어를 완전 히 표현할 수 있게 하면서도 동시에 유용한 기능을 제공한다는 뜻입니다. | 유연한 | 익스프레스 철학의 다른 핵심은 확장성입니다. 익스프레스는 최소화된 프레임워크만 제공하 며, 필요하다면 다른 익스프레스 기능을 넣고 필요하지 않은 것은 뺄 수 있습니다. 개발자를 숨 막히게 하던 기존 프레임워크와는 다릅니다. 수많은 프레임워크가 모든 기능을 추가함으로써 코드는 단 한 줄도 작성하지 않았는데도 프로젝트를 이미 불가사의하고 복잡하며 비대한 것으 로 만들어버리곤 합니다. 이렇다 보니, 프로젝트를 진행하며 맨 처음 하는 일이 필요 없는 기능 을 잘라내거나 요건에 맞지 않는 기능을 다른 것으로 교체하는 일일 때가 많습니다. 익스프레 스는 반대 방향에서 접근하므로 필요한 것을 필요할 때 추가할 수 있습니다. | 웹 애플리케이션 프레임워크 | 용어가 조금 꼬이는군요. 웹 애플리케이션이 뭘까요? 익스프레스로는 웹사이트나 웹 페이지
1장 - 익스프레스 소개
23
를 만들 수 없다는 뜻일까요? 물론 아닙니다. 웹사이트도 웹 애플리케이션이고, 웹 페이지도 웹 애플리케이션입니다. 하지만 웹 애플리케이션은 더 넓은 범주입니다. 즉 웹 애플리케이션은 다른 웹 애플리케이션(및 기타 등등)에도 기능을 제공할 수 있습니다. 일반적으로 ‘앱’이란 단 순히 콘텐츠를 모아 놓기만 한 것이 아니라, 기능이 있는 어떤 것을 부를 때 쓰는 말입니다. 현 재는 장치 자체에서 동작하는 것을 ‘앱’이라 부르고, 네트워크를 통해 장치에 전달되는 것을 ‘웹 페이지’라 부릅니다만, 폰갭PhoneGap 같은 프로젝트도 있고 마이크로소프트는 HTML5 애플리케 이션을 네이티브 애플리케이션처럼 데스크톱에서 실행하는 등 이 구분이 점차 모호해지고 있 습니다. 몇 년 안에 앱과 웹사이트 사이에 차이가 없어질 거라고 생각합니다. | 단일 페이지 웹 애플리케이션 | 단일 페이지 웹 애플리케이션은 비교적 새로운 아이디어입니다. 일반적인 웹사이트는 사용자 가 다른 페이지로 이동할 때마다 네트워크 요청을 보내지만, 단일 페이지 웹 애플리케이션은 사이트 전체(또는 그중 상당 부분)를 클라이언트 브라우저로 내려받습니다. 일단 초기 다운로 드가 끝나면 서버와 통신할 필요가 없거나 매우 적으므로 더 빨리 이동할 수 있습니다. 앵귤러 나 엠버Ember 같은 프레임워크가 유명해지면서 단일 페이지 애플리케이션 개발도 쉬워졌
Angular
습니다. 익스프레스는 이들 프레임워크와도 잘 어울립니다. | 다중 페이지/하이브리드 웹 애플리케이션 | 다중 페이지 웹 애플리케이션은 전통적인 웹사이트에서 사용하던 방식입니다. 웹사이트의 각 페이지는 서버에 따로 요청됩니다. 이 방법이 더 전통적이라는 말이, 이점이 없다거나 단일 페 이지 애플리케이션이 더 낫다는 뜻은 아닙니다. 선택할 수 있는 옵션일 뿐이며, 콘텐츠 중 어떤 부분을 단일 페이지 앱으로 전송하고 어떤 부분을 각각 페이지로 요청받을지는 자유롭게 정할 수 있습니다. ‘하이브리드’는 두 가지 방법을 섞어 쓰는 사이트를 말합니다.
익스프레스가 정확히 뭔지 아직 혼란스럽더라도 걱정할 필요 없습니다. 일단 사용해보는 게 이 해가 더 빠를 때가 있습니다. 이 책에서도 일단 익스프레스로 웹 애플리케이션을 만들어볼 것 입니다.
24
한 권으로 끝내는 Node & Express
1.3. 익스프레스의 간단한 역사 익스프레스를 만든 TJ 할로웨이척TJ Holowaychuk은 루비에 기반을 둔 프레임워크 시나트라Sinatra에 영감을 받아 익스프레스를 만들었다고 합니다. 익스프레스가 루비 기반 프레임워크에서 영향 을 받은 건 놀랄 일이 아닙니다. 루비는 웹 개발을 빠르고 효율적이며 관리하기 쉽게 만드는 데 공헌을 많이 했으니까요. 익스프레스는 시나트라에 영향을 받기도 했고, 노드의 ‘플러그인’ 라이브러리인 커넥트Connect와 도 깊이 얽혀 있습니다. 웹 요청을 처리하는 노드 플러그인 모듈에 ‘미들웨어'라는 이름이 붙은 건 커넥트 때문입니다. 버전 4.0 이전에는 익스프레스에 커넥트가 포함되어 있었지만, 4.0부터 는 커넥트를 포함해 미들웨어 대부분을 분리해서 따로 업그레이드할 수 있게 했습니다.
NOTE_ 익스프레스는 2.x 버전에서 3.0으로 올라가며 상당히 많이 바뀌었고, 3.x 버전에서 4.0으로 올라 가며 또 많이 바뀌었습니다. 이 책은 4.0을 기준으로 합니다.
1.4. 익스프레스 4.0으로 업그레이드 이미 익스프레스 3.0을 사용해봤다면, 기쁜 소식이 있습니다. 익스프레스 4.0으로 업그레이드 하기는 매우 쉽습니다. 익스프레스가 처음이라면 이 섹션은 읽지 않아도 됩니다. 익스프레스
3.0을 사용해봤다면 다음을 염두에 두십시오. 커넥트가 분리됐습니다. 따라서 static 미들웨어를 제외하고 나머지 패키지(connect 등)는 직접 설치해
●
야 합니다. 커넥트에서도 몇 가지 미들웨어를 독립 패키지로 분리했으므로, 필요한 미들웨어를 찾으려면
npm을 검색해봐야 할 겁니다. body-parser도 독립 패키지이며 주요 보안 문제를 없애기 위해 multipart 미들웨어를 제거했습니다. 이
●
제 body-parser 미들웨어를 써도 안전합니다. 이제 애플리케이션에 익스프레스 라우터router를 연결하지 않아도 됩니다. 따라서 기존 익스프레스 3.0 애플
●
리케이션에서 app.use(app.router)를 제거해야 합니다. app.configure도 제거됐습니다. 이 메서드를 호출하던 부분은 app.get(env)로 대체하면 됩니다
●
(switch 문 또는 if 문을 써서).
1장 - 익스프레스 소개
25
공식 업그레이드 가이드(http://bit.ly/1pkw80L )에서 더 자세한 내용을 알 수 있습니다. 익스프레스는 오픈 소스 프로젝트이며, 개발과 유지보수는 TJ 할로웨이척이 주로 담당합니다.
1.5. 노드: 새로운 타입의 웹 서버 노드는 다른 유명한 웹 서버, 예를 들어 마이크로소프트의 인터넷 정보 서비스Internet Information Services (IIS )
나 아파치Apache와 비슷한 점이 많습니다. 물론 어떤 점이 다른가가 더 흥미롭겠죠. 여
기에서 다른 점을 설명합니다. 익스프레스와 마찬가지로 노드 역시 최소한의 웹 서버로 시작합니다. IIS나 아파치는 완전히 숙달하는 데 몇 년씩 걸릴 수도 있지만, 노드는 설치하고 설정하기가 매우 쉬운 편입니다. 그렇 다고 아무나 노드 서버에서 최대한의 성능을 끌어낼 수 있다는 건 아닙니다. 설정 옵션이 더 단 순하고 이해하기 쉬울 뿐입니다. 다른 주요 차이점은 노드가 단일 스레드로 운영된다는 겁니다. 언뜻 보면 뒤떨어진 것 같지만, 사실 이건 매우 현명한 결정입니다. 단일 스레드로 운영하면 웹 애플리케이션을 만들기가 정말 쉬워집니다. 멀티스레드 서버의 성능을 원한다면 단순히 노드 인스턴스를 더 늘리기만 하면 멀 티스레드로 운영하는 거나 마찬가지입니다. 날카로운 독자라면 이 부분을 읽고 교묘한 속임수 라고 생각할 수도 있습니다. “결국 서버 병렬화를 통한 멀티스레딩은 (앱 병렬화와는 달리) 복 잡한 부분을 없앤 게 아니라 단순히 다른 곳으로 전가한 것뿐이지 않나?” 그럴 수도 있습니다 만, 내 경험에 비춰보면 복잡해야 할 곳이 복잡해진 겁니다. 또한, 클라우드 컴퓨팅이 득세하고 서버를 범용 재화로 취급하게 되면서 이런 접근법이 더 상식적이 되었습니다. IIS나 아파치는 물론 대단히 강력하고, 최근의 고성능 하드웨어를 아주 잘 활용하도록 만들어졌습니다. 하지만 그에 따른 비용도 있습니다. 이들 서버를 설치하고 그만한 성능을 내려면 상당한 전문가가 필 요합니다. 애플리케이션을 만드는 관점에서 본다면 노드 애플리케이션은 .NET이나 자바 애플리케이션 보다는 PHP나 루비 애플리케이션에 더 가깝습니다. 노드가 사용하는 자바스크립트 엔진인 구 글 V8이 자바스크립트를 네이티브 기계 코드로 컴파일하긴 하지만(C나 C++처럼), 워낙 빨
26
한 권으로 끝내는 Node & Express
리1 컴파일하기 때문에 사용자의 관점에서 보면 순수한 인터프리터 언어나 마찬가지입니다. 별 도의 컴파일 단계가 필요 없으면 유지보수나 배포 시 귀찮은 일이 줄어듭니다. 자바스크립트 파일을 업데이트하기만 하면 자동으로 바뀐 점이 반영됩니다. 노드 애플리케이션의 또 다른 특장점은 플랫폼을 가리지 않는다는 겁니다. 물론 노드가 최초의 플랫폼 독립 서버 기술은 아니지만, ‘플랫폼을 가리지 않는다’는 말은 정말 잘 새겨들어야 합니 다. 예를 들어 모노Mono 덕분에 .NET 애플리케이션을 리눅스 서버에서 실행할 수 있긴 하지만, 그건 정말 고역입니다. 마찬가지로 PHP 애플리케이션을 윈도우 서버에서 실행할 수 있긴 하 지만, 리눅스 서버에서 실행하는 것보다 훨씬 어렵습니다. 반면 노드를 윈도우나 OS X, 리눅 스 같은 주요 운영체제에 설치하기는 식은 죽 먹기고, 협업하기도 쉽습니다. 웹사이트 디자인 팀에서는 PC와 맥을 같이 쓰는 일이 매우 흔합니다. .NET 같은 일부 플랫폼은 보통 맥을 쓰는 프론트엔드 개발자나 디자이너가 접근하기 어렵고, 그래서 협업에도 상당한 악영향이 있습니 다. 단 몇 분(심지어 몇 초) 안에 어떤 운영체제에서도 동작하는 서버! 실로 꿈이 이루어진 겁 니다.
1.6. 노드 생태계 노드 생태계 중심에는 물론 노드가 있습니다. 노드는 자바스크립트가 브라우저를 벗어나 서버 에서 동작하게 해준 소프트웨어이며 익스프레스처럼 자바스크립트로 작성된 프레임워크를 쓸 수 있게 해준 주인공입니다. 다른 중요 구성 요소는 데이터베이스이며, 13장에서 더 깊이 다룹 니다. 단순한 웹 애플리케이션을 제외하면 모두 데이터베이스가 필요하며, 노드 생태계에 가장 잘 어울리는 데이터베이스는 따로 있습니다. 물론 주요 관계형 데이터베이스(MySQL, 마리아DB, PostgreSQL, 오라클, SQL 서버)에 사 용할 인터페이스는 만들어져 있습니다. 이미 널리 쓰이는 데이터베이스를 간과할 수 없으니까 요. 하지만 노드가 득세하자 소위 ‘NoSQL’ 데이터베이스라는 새로운 접근법이 힘을 얻었습니 다. 뭔가가 없다고 정의해서는 큰 의미가 없을 수도 있으니, 이들 NoSQL 데이터베이스를 ‘문 서 데이터베이스’ 또는 ‘키/값 쌍 데이터베이스’라고 부르는 게 더 좋겠습니다. 이들은 데이터
1 종종 ‘적시'(JIT) 컴파일이라고 부릅니다.
1장 - 익스프레스 소개
27
스토리지에 더 단순하게 접근합니다. 문서 데이터베이스는 여러 종류가 있지만, 이 책에서는 그중 선두 주자인 몽고DBMongoDB를 사용합니다. 웹사이트를 만들려면 여러 가지 기술을 사용해야 하므로, 사용한 기술 ‘스택’을 두문자어로 나 타내는 약어가 만들어졌습니다. 예를 들어 리눅스, 아파치, MySQL, PHP 스택은 LAMP 스택 이라고 부릅니다. 몽고DB에서 일하는 발레리 카르포프Valeri Karpov는 몽고, 익스프레스, 앵귤러, 노드를 합쳐 MEAN이는 말을 만들어냈습니다. ‘MEAN’은 확실히 기억하기 쉬운 말이긴 하지 만, 이 단어 하나로는 노드 생태계를 이루는 여러 가지 데이터베이스나 애플리케이션 프레임워크 를 모두 표현하지는 못합니다(필자가 중요 구성 요소라고 판단하는 템플릿 엔진도 빠졌습니다). 포괄적인 두문자어를 만드는 것은 재미있는 놀이입니다. 물론 노드는 필수불가결한 구성 요소 입니다. 다른 서버 측 자바스크립트 기술들도 있지만, 노드가 가장 지배적입니다. 익스프레스 역시 유일한 웹 앱 프레임워크는 아니지만, 노드만큼 지배적입니다. 웹 앱 개발에 핵심적인 다 른 두 구성 요소는 데이터베이스 서버와 템플릿 엔진입니다. 템플릿 엔진이 하는 일은 PHP나
JSP, 레이저Razor와 마찬가지로 코드와 마크업을 매끄럽게 연결하는 일입니다. 데이터베이스 서 버와 템플릿 엔진에는 뚜렷한 선두 주자가 없어서 어느 하나를 지목한다면 다른 프로그램에 폐 가 될 것 같습니다. 아무튼, 이들 기술을 한데 아우르는 것은 자바스크립트이므로, 포괄적으로 ‘자바스크립트 스택’ 이라 부르겠습니다. 이 책에서 자바스크립트 스택이란 노드, 익스프레스, 몽고DB입니다.
1.7. 라이선스 노드 애플리케이션을 개발하다 보면 전보다 더 라이선스에 주의를 기울이게 될 겁니다(필자는 확실히 그랬습니다). 노드 생태계의 장점 중에는 방대한 패키지도 있습니다. 하지만 각 패키지 의 라이선스 정책이 다 다르고, 심지어 각 패키지가 다른 패키지에 의존할 수도 있으므로 자신 이 만든 앱의 어느 부분에 어떤 라이선스가 적용되는지 이해하기가 어려울 수 있습니다. 물론 좋은 소식도 있습니다. 노드 패키지에서 가장 널리 쓰이는 라이선스인 MIT 라이선스는 제한이 매우 적어서, 하고 싶은 일은 거의 다 할 수 있으며, 심지어는 그 패키지가 들어 있는 소 프트웨어의 소스를 공개하지 않아도 됩니다. 하지만 모든 패키지가 MIT 라이선스를 쓸 거라고
28
한 권으로 끝내는 Node & Express
단정해서는 안 되겠죠. TIP
npm에는 프로젝트에 사용한 소프트웨어들의 라이선스를 알아내는 패키지도 있습니다. npm에서 licensesniffer나 license-spelunker로 검색해보십시오.
MIT가 가장 많겠지만, 다음 라이선스도 흔히 볼 수 있습니다. | GNU 일반 공중 사용 허가서(GPL) |
GPL은 소프트웨어를 무료로 사용한다는 목적으로 만들어진 라이선스며 오픈 소스 소프트웨 어에서 널리 쓰입니다. GPL 라이선스를 쓰는 코드가 프로젝트에 들어 있다면, 해당 프로젝트 도 반드시 GPL 라이선스를 따라야 합니다. 따라서 소스를 공개할 수 없는 프로젝트에는 쓸 수 없습니다. | 아파치 2.0 | 이 라이선스는 MIT와 마찬가지로 프로젝트에 다른 라이선스를 쓸 수 있게 허가하며, 소스를 공개하지 않는 라이선스도 상관없습니다. 하지만 어떤 구성 요소에서 아파치 2.0 라이선스를 사용하는지 반드시 명시해야 합니다. | BSD 허가서 | 이 라이선스는 아파치와 비슷해서 프로젝트의 어떤 구성 요소에서 BSD 라이선스를 사용하는 지 표시하기만 하면 프로젝트에 다른 어떤 라이선스든 쓸 수 있게 허가합니다.
NOTE_ 2중 라이선스를 사용하는 소프트웨어도 있습니다. 이렇게 하는 이유는 대개 소프트웨어를 GPL 프로젝트나 더 관대한 라이선스를 쓰는 프로젝트에서 모두 사용할 수 있게 하기 위해서입니다(GPL 소프 트웨어에서 사용할 구성 요소는 반드시 GPL 라이선스를 써야 합니다). 필자는 프로젝트를 진행할 때 종종
GPL과 MIT를 함께 사용합니다.
마지막으로, 패키지를 직접 작성했다면 선의를 가지고 적합한 라이선스를 선택한 후 이를 정확 히 명시해야 합니다. 개발자로서 가장 짜증 나는 일은 다른 사람의 패키지를 쓰면서 소스를 여 기저기 뒤져 라이선스 정책을 찾아보는 일이고, 라이선스가 아예 없음을 발견하면 절망스럽기 까지 합니다.
1장 - 익스프레스 소개
29
CHAPTER
2
노드 시작하기
이 장은 노드를 아직 경험해보지 못한 사람을 위한 장입니다. 익스프레스를 이해하고 유용하게 사용하려면 노드의 기본적인 내용을 이해해야 합니다. 이미 노드로 웹 애플리케이션을 만들어 봤다면 이 장은 읽지 않아도 됩니다. 이 장에서는 노드로 매우 기본적인 웹 서버를 만들고, 다 음 장에서는 익스프레스로 같은 서버를 만들어볼 겁니다.
2.1. 노드 설치 노드를 시스템에 설치하기는 매우 쉽습니다. 노드 팀은 주요 플랫폼에 단순하게 설치할 수 있 도록 많이 노력했습니다. 다음과 같이 단순한 3단계만 진행하면 됩니다. 1. 노드 홈페이지(http://nodejs.org)로 갑니다. 2. INSTALL이라 적힌 큰 녹색 버튼을 클릭합니다. 3. 지시하는 대로 따라 합니다.
윈도우와 OS X에서는 설치 파일을 내려받습니다. 리눅스라면 패키지 관리자( http://bit. ly/1GBz8YS )를 쓰는 편이 더 빠를 겁니다.
2장 - 노드 시작하기
31
CAUTION_리눅스 사용자이고 패키지 관리자를 사용한다면 앞에서 언급한 웹 페이지에 있는 지시 사항 을 꼭 따라야 합니다. 적절한 패키지 저장소를 추가하지 않으면 대단히 오래된 버전의 노드를 설치하는 리눅 스 배포판이 많습니다.
회사에 노드를 설치한다면 http://nodejs.org/download에서 독립 설치 파일을 내려받는 편 이 더 편리할 수도 있습니다. 노드 설치 과정에서 막혔거나 어떤 이유로 노드를 처음부터 컴파일하고 싶다면 공식 설치 가이 드(http://bit.ly/node_installation )를 읽어보십시오.
2.2. 터미널 사용 필자는 터미널의 생산성과 힘을 맹신하는 열광적 팬입니다(터미널은 ‘콘솔’ 또는 ‘명령 프롬프 트’라고도 부릅니다). 이 책 전체에서 모든 예제는 여러분이 터미널을 사용한다고 가정합니다. 터미널에 친숙하지 않다면 시간을 좀 투자해서 터미널을 선택하고 거기 친숙해지길 권합니다. 이 책에서 사용하는 유틸리티에는 대개 GUI 인터페이스가 있어서 터미널에 익숙하지 않더라 도 방법은 있지만, 그 방법은 스스로 찾아야 합니다.
OS X나 리눅스 사용자라면 훌륭한 셸shell (터미널 명령어 인터프리터)이 많이 있습니다. zsh 사용자도 많지만, 가장 널리 쓰이는 셸은 배시bash입니다. 오래 사용해서 친숙하기도 하지만, 필 자가 배시에 끌리는 가장 큰 이유는 배시가 어디서나 쓰이기 때문입니다. 유닉스 기반 컴퓨터 를 써보면 그중 99%의 기본 셸이 배시일 겁니다. 윈도우 사용자라면 조금 불편할 수 있습니다. 마이크로소프트는 여태까지 터미널 환경에 별다 른 관심을 보이지 않았으므로 사용자가 할 일이 좀 더 많습니다. 깃Git에는 유닉스 비슷한 터미 널 경험을 제공하는 깃 배시Git bash 셸이 포함되어 있습니다(일반적인 유닉스 명령줄의 유틸리 티 일부만 포함되긴 했지만 유용한 부분집합입니다). 깃 배시가 배시 셸의 기능을 일부 제공하 긴 하지만, 윈도우에 내장된 콘솔 애플리케이션의 한계는 명확해서, 콘솔 창의 크기를 조절한 다거나 텍스트를 선택하고 자르고 붙여넣는 단순한 기능조차 직관적이지 않고 엉망진창입니 다. 따라서 콘솔2Console2 ( http://bit.ly/Console_2 )나 ConEmu ( http://bit.ly/Con-Emu )
32
한 권으로 끝내는 Node & Express
같은 더 세련된 터미널을 설치하길 권합니다. 윈도우 고급 사용자, 특히 .NET 개발자나 윈도 우 시스템/네트워크 관리자라면 마이크로소프트의 파워셸PowerShell을 쓸 수도 있습니다. 파워셸 은 이름값을 합니다. 파워셸로 주목할 만한 일을 해내는 사람들도 있고, 숙련된 파워셸 사용자 는 유닉스 명령줄 전문가 못지않습니다. 하지만 OS X/리눅스와 윈도우를 오간다면 깃 배시를 쓰는 편이 일관성 면에서 낫다고 봅니다. 윈도우 사용자라면 가상화를 생각해볼 수도 있습니다. 최신 컴퓨터는 워낙 성능이 좋아서 가상 머신virtual machine (VM )과 실제 컴퓨터의 차이를 구분하기 어렵습니다. 필자는 오라클의 무료 프 로그램인 버추얼박스VirtualBox를 아주 잘 썼고, 윈도우 8은 VM 지원이 내장돼 있습니다. 드롭박 스Dropbox 같은 클라우드 기반 파일 스토리지도 있고, VM 스토리지에서 호스트 스토리지로 연 결하기도 쉬우므로 가상화의 매력은 점점 더 커질 겁니다. 윈도우의 엉망진창인 콘솔 지원을 깃 배시로 때우는 것보다 아예 리눅스 VM을 설치해 개발에 사용하는 것도 고려할 만합니다. 리눅스 UI에 익숙해지기 어렵다면 PuTTY (http://www.putty.org ) 같은 터미널 애플리케이 션도 좋습니다. 필자도 이 프로그램을 종종 사용합니다. 마지막으로, 시스템에 상관없이 사용할 수 있는 코디오Codio (https://codio.com )가 있습니다. 코디오는 프로젝트별로 IDE와 명령줄이 포함된 새 리눅스 인스턴스를 제공하는 웹사이트이 며, 이 인스턴스에는 노드가 이미 설치돼 있습니다. 코디오는 대단히 쓰기 쉽고, 노드 개발을 매우 빨리 시작할 수 있습니다. TIP
npm 패키지를 설치할 때 -g(전역) 옵션을 사용하면 npm은 윈도우 홈 디렉터리 안의 서브디렉터리에 설치 됩니다. 이들 패키지 중 상당수가, 윈도우 사용자 이름에 공백이 들어 있으면 제대로 동작하지 않습니다. 필자 의 윈도우 사용자 이름은 원래 ‘Ethan Brown’이었지만 이제는 ‘ethan.brown’이라고 씁니다. 알 수 없는 에러 때문에 골치를 썩이기 싫다면 윈도우 사용자 이름에 공백을 쓰지 말길 권합니다. 사용자 이름에 이미 공 백이 들어 있다면 새 사용자를 만들고 파일을 새 계정으로 전송할 수 있습니다. 윈도우 홈 디렉터리의 이름을 바꿔도 가능하긴 하지만 위험합니다.
마음에 드는 셸을 찾았다면 시간을 좀 투자해 기본적인 것들에 익숙해지길 권합니다. 인터넷에 는 훌륭한 가이드가 많이 있고, 미리 조금만 익혀두면 나중에 찾아올 두통거리를 많이 예방할 수 있습니다. 최소한 디렉터리 이동 방법, 파일을 복사하고 이동하고 지우는 방법, 명령줄 프로 그램에서 빠져나오는 방법(보통 Ctrl-C )은 숙지하고 있어야 합니다. 터미널 전문가가 되고 싶 다면 다음과 같은 것들을 검색해보십시오. 파일에 들어 있는 텍스트를 찾는 방법, 파일과 디렉 터리를 검색하는 방법, 명령어를 연속적으로 실행하는 방법(오래된 ‘유닉스 철학’입니다), 출
2장 - 노드 시작하기
33
력 리다이렉션 방법을 알면 터미널 전문가라 할 만합니다.
CAUTION_유닉스 비슷한 시스템 상당수에서 Ctrl-S에는 특별한 의미가 있습니다. 이 키는 터미널을 ‘멈춥니다.’ 원래 이 키는 출력이 매우 빨리 스크롤될 때 화면을 멈춰서 살펴보기 위해 만들어진 키입니다.
Ctrl-S는 저장 단축키로 쓰이기도 하므로 생각 없이 누르기 쉽고, 일단 누른 사람들은 매우 혼란스러운 상 황에 부닥치게 됩니다(인정하긴 싫지만, 필자도 종종 겪는 일입니다). 터미널을 다시 사용하려면 Ctrl-Q를 누르면 됩니다. 즉 터미널이 갑자기 멈춘 것 같다면 Ctrl-Q를 눌러서 다시 동작하는지 보십시오.
2.3. 에디터 프로그래머들 사이에서 에디터 선택만큼 열띤 토론 주제는 별로 없는데, 사실 그럴 만한 일입 니다. 에디터야말로 가장 많이 쓰는 도구니까요. 필자가 주로 쓰는 에디터는 vi1 (또는 vi 모드 가 있는 에디터)입니다. 사실 vi는 진입 장벽이 좀 높기는 합니다. 필자가 동료들에게 지금 하 는 일을 vi에서 얼마나 쉽게 할 수 있는지 찬양할 때면 그들은 항상 불신의 시선을 보내곤 합니 다. 그렇긴 해도, 강력한 에디터를 찾고 사용법을 익히면 생산성이 꽤 많이 올라갈 뿐 아니라, 감히 말하건대 일이 즐거워집니다. 필자가 vi를 특히 좋아하는 이유는 배시와 마찬가지로 아주 흔하기 때문입니다. 유닉스 시스템(Cygwin 포함)에는 항상 vi가 설치돼 있습니다. 인기 있는 에디터에는 대부분(심지어 마이크로소프트 비주얼 스튜디오에도) vi 모드가 들어 있습니다. 일단 vi에 익숙해지면 다른 에디터는 상상도 할 수 없습니다. 처음에는 좀 어렵지만, 분명 그만 한 가치가 있습니다. 어디서든 쓰이는 에디터에 친숙해지는 게 좋다는 데 동의한다면 이맥스Emacs도 좋은 대안입니 다. 필자는 이맥스를 잘 사용하지는 못하지만(보통 이맥스 아니면 vi를 쓰지, 둘 다 쓰는 사람 은 드뭅니다), 이맥스의 유연함과 기능은 정말 훌륭하다고 생각합니다. vi의 모달 편집 패러다 임이 당신에게 맞지 않다면 이맥스를 써보십시오.
vi나 이맥스 같은 콘솔 에디터에 익숙해지면 믿을 수 없을 정도로 편리하지만, 그래도 최신 에 디터를 원하는 사람이 있을 겁니다. 프론트엔드를 담당하는 동료 중 코다Coda를 숭배하는 사람
1 최근에는 vim(vi improved)을 vi라고 부르기도 합니다. 대부분 vi를 입력하면 vim이 실행되지만 필자는 보통 vim이라고 입력합니다.
34
한 권으로 끝내는 Node & Express
CHAPTER
3
익스프레스로 시간 절약
2장에서는 노드만 사용해 단순한 웹 서버를 만드는 방법을 배웠습니다. 이 장에서는 익스프레 스를 사용해 그 서버를 다시 만듭니다. 이 장은 책의 이후 내용의 출발점이 될 것이며, 이 장을 읽음으로써 익스프레스의 기본에 대해 이해하게 될 겁니다.
3.1. 스캐폴딩 스캐폴딩1자체는 새로운 아이디어가 아니지만, 필자를 포함해 많은 사람이 루비를 통해 이 개 념에 익숙해졌습니다. 아이디어는 단순합니다. 프로젝트는 대개 ‘템플릿boilerplate’ 코드가 일정량 필요한데, 새 프로젝트를 시작할 때마다 그 코드를 다시 만들고 싶어 하는 사람은 없지 않겠습 니까? 프로젝트의 엉성한 뼈대를 만들어놓고, 새 프로젝트를 시작할 때마다 이 뼈대, 즉 템플 릿을 복사하는 방법이 훨씬 간단합니다. 루비 온 레일즈에서는 자동으로 스캐폴딩을 생성하는 프로그램을 만들어 이 개념을 한 단계 진 화시켰습니다. 이 접근법의 장점은 그저 템플릿 컬렉션 중에서 선택하는 것이 아니라 더 세련 된 프레임워크를 생성할 수 있다는 겁니다. 익스프레스는 루비 온 레일즈의 아이디어를 받아들여, 프로젝트를 시작할 때 스캐폴딩을 생성 1 역주_ 건물을 처음 시공할 때 그 주위에 강관과 발판을 연결해 작업자들이 이동할 수 있는 엉성한 구조물을 만들어놓는 걸 본 적이 있을 겁 니다. 이게 스캐폴딩이고 한자로는 비계(飛階)라고 쓰는데, 한자 역시 익숙한 단어는 아니라 영어 단어를 그대로 쓰겠습니다.
3장 - 익스프레스로 시간 절약
43
하는 유틸리티를 제공합니다. 익스프레스가 제공하는 스캐폴딩 유틸리티도 쓸 만하긴 하지만, 현재 버전에서 생성하는 프레 임워크는 그다지 권장할 만하진 않습니다. 특히 필자가 선택한 템플릿 언어인 핸들바Handlebars를 지원하지 않고, 필자가 선호하는 명명 규칙naming convention도 따르지 않습니다(후자는 쉽게 고칠 수 있긴 합니다). 이 책에서는 스캐폴딩 유틸리티를 사용하지 않겠지만, 책을 다 읽은 다음에는 한번 살펴보길 권합니다. 책을 다 읽고 나면 스캐폴딩 유틸리티가 생성하는 템플릿이 자신에게 유용한지 직접 판단할 수 있는 지식을 다 갖췄을 겁니다. 클라이언트에 전송될 실제 HTML에도 유용한 템플릿이 있습니다. 필자는 HTML5 보일러플 레이트HTML5 Boilerplate ( http://bit.ly/boiler_plate )를 추천합니다. 이 프로그램은 HTML5 웹사이트에 사용하기에 안성맞춤인 템플릿을 생성합니다. 최근 HTML5 보일러플레이트에는 커스텀 빌드를 생성하는 기능이 생겼습니다. 커스텀 빌드 옵션에는 필자가 강력히 추천하는 프 론트엔드 프레임워크인 트위터 부트스트랩Twitter Bootstrap도 포함됐습니다. 7장에서는 부트스트 랩 기반 커스텀 빌드를 사용해 반응형 모던 HTML5 웹사이트를 만들 겁니다.
3.2. 메도라크 여행사 웹사이트 이 책 전체에서 사용할 예제는 오리건 주를 찾아오는 사람을 대상으로 서비스를 제공하는 가상 의 여행사 메도라크Meadowlark (들종다리)의 웹사이트입니다. REST 애플리케이션을 만드는 데 더 관심이 있더라도 걱정할 필요 없습니다. 메도라크 웹사이트에서는 REST 서비스도 제공합 니다.
3.3. 초기 단계 먼저 프로젝트에 사용할 새 디렉터리를 만듭니다. 이 디렉터리는 프로젝트의 루트 디렉터리입 니다. 이 책에서 ‘프로젝트 디렉터리’, ‘앱 디렉터리’, ‘프로젝트 루트’라고 말하면 그건 이 디렉
44
한 권으로 끝내는 Node & Express
터리를 가리키는 겁니다. TIP
프로젝트를 진행하다 보면 회의록이나 문서 같은 파일이 생기게 마련인데, 이런 파일들과 웹 앱 파일을 분리해 서 보관하고 싶을 겁니다. 따라서 서브디렉터리를 하나 만들어 프로젝트 루트로 지정하길 권합니다. 예를 들어 필자는 메도라크 웹사이트 프로젝트와 관련된 파일을 ~/projects/meadowlark 안에 두되, 프로젝트 루 트(프로젝트 디렉터리)는 ~/projects/meadowlark/site로 지정하겠습니다.
npm은 프로젝트 의존성과 메타데이터를 package.json 파일에 보관해서 관리합니다. 이 파일 을 만드는 가장 쉬운 방법은 npm init입니다. 이 명령을 내리면 몇 가지 질문을 거쳐 package.
json 파일을 생성합니다(‘진입점entry point’을 묻는 질문에는 meadowlark.js 또는 프로젝트 이름 을 쓰면 됩니다). TIP
package.json에 저장소 URL을 지정하지 않거나 README.md 파일을 작성하지 않으면 npm을 실행 할 때마다 경고가 나올 겁니다. 사실 package.json 파일에 들어 있는 메타데이터는 프로젝트를 npm 저 장소에 올리지 않는다면 필요 없는 것이지만, 간단한 작업을 해서 경고를 없애는 것도 나쁘지 않습니다.
첫 번째 단계는 익스프레스 설치입니다. 다음 npm 명령어를 실행합니다. npm install --save express
npm install을 실행하면 지정한 패키지를 node_modules 디렉터리에 설치합니다. --save 플
래그를 지정하면 package.json 파일을 업데이트합니다. node_modules 디렉터리는 언제든
npm에서 다시 생성할 수 있으니 저장소에 저장할 필요는 없습니다. 실수로 node_modules 디렉터리를 저장소에 추가하는 일이 없도록 다음과 같이 .gitignore 파일을 만들어둡시다. # npm에서 설치한 패키지를 무시합니다. node_modules # 그 밖에 .DS_Store(OS X)나 *.bak 등 필요 없는 파일은 # 무엇이든 추가하십시오.
이제 meadowlark.js 파일을 만듭니다. 이 파일이 우리 프로젝트의 진입점입니다. 이 책 전체 에서 이 파일을 ‘앱 파일’이라 부를 겁니다. var express = require('express'); var app = express();
3장 - 익스프레스로 시간 절약
45
app.set('port', process.env.PORT || 3000); // 커스텀 404 페이지 app.use(function(req, res){ res.type('text/plain'); res.status(404); res.send('404 - Not Found'); }); // 커스텀 500 페이지 app.use(function(err, req, res, next){ console.error(err.stack); res.type('text/plain'); res.status(500); res.send('500 - Server Error'); }); app.listen(app.get('port'), function(){ console.log( 'Express started on http://localhost:' + app.get('port') + '; press Ctrl-C to terminate.' ); }); TIP
익스프레스 스캐폴딩 생성기를 포함해 여러 가이드에서는 메인 파일의 이름을 app.js(혹은 index.js나
server.js )라고 정하길 권합니다. 메인 애플리케이션 파일에 정해진 이름을 써야만 하는 호스트 서비스나 배 포 시스템을 사용하는 게 아니라면, 이런 이름을 고집할 필요는 없다고 생각합니다. 필자는 메인 애플리케이션 파일 이름은 프로젝트를 따라 짓습니다. 에디터에 ‘index.html’ 탭을 여러 개 띄워놓고 뭐가 뭔지 헷갈려본 사람은 즉시 이 방법의 장점을 깨달을 겁니다. npm init을 실행하면 기본적으로 index.js를 사용합니다. 애 플리케이션 파일에 다른 이름을 쓰기로 결정했다면 package.json 파일의 main 프로퍼티를 반드시 업데 이트하십시오.
이제 기초적인 익스프레스 서버가 만들어졌습니다. node meadowlark.js로 서버를 시작하고 http://localhost:3000에 접속할 수 있습니다. 하지만 결과는 실망스럽습니다. 아직 익스프
레스에 아무런 라우트route도 지정하지 않았으므로 페이지가 존재하지 않음을 나타내는 범용
404 페이지가 나타날 겁니다. NOTE_ 애플리케이션 포트는 app.set(port, process.env.PORT || 3000)으로 지정했습니다. 이렇 게 하면 서버를 시작하기 전에 환경 값을 설정해 포트를 오버라이드할 수 있습니다. 이 예제를 실행했을 때 앱이 포트 3000에서 실행되지 않는다면 PORT 환경 변수가 설정됐는지 체크하십시오.
46
한 권으로 끝내는 Node & Express
CHAPTER
4
모양새 갖추기
2장과 3장은 단순한 실험이었습니다. 말하자면 물속에 발가락만 살짝 넣어본 격입니다. 더 복 잡한 기능을 배우기 전에 주변 정리를 좀 하고 좋은 습관을 익히는 게 좋겠습니다. 이 장에서부터 메도라크 여행사 프로젝트를 본격적으로 시작할 겁니다. 하지만 웹사이트 자체 를 만들기 전에 먼저 필요한 도구부터 확실히 준비합시다. TIP
여러분이 이 책의 예제를 그대로 따를 필요는 없습니다. 자신만의 웹사이트를 빨리 만들고 싶어 몸이 달았다 면, 예제의 큰 틀은 따르되 필요에 맞게 수정하면서 진행한다면 책을 덮을 때쯤에는 웹사이트가 완성되어 있 을 겁니다.
4.1. 모범 사례 ‘모범 사례’라는 말을 많이 들어봤을 겁니다. 이 말은 일을 ‘이렇게’ 해야 좋으니 대충 하지 말라 는 뜻입니다. IT 분야의 농담도 알고 있겠죠.“일을 빨리 하든, 싸게 하든, 훌륭히 하든, 그중 두 가지만 가능하다.” 그런데 이 농담은 일을 정확히 하는 것의 실제 비용은 생각하지 않은 것 같 습니다. 어떤 일을 처음으로 정확히 하려면, 빠르고 간편한 방법의 다섯 배 정도 시간이 걸릴 겁니다. 하지만 두 번째에는 세 배 정도로 줄어들고, 열 번을 넘어가면 일을 정확히 해도 빠르 고 간편한 방법과 시간 차이가 없을 겁니다.
4장 - 모양새 갖추기
57
필자의 펜싱 코치는 항상 이렇게 말합니다. 연습을 열심히 하면 완벽해지는 게 아니라 영구적 이 되는 것이라고요. 즉 뭔가를 계속 반복하다 보면 결국 자동적, 기계적이 된다는 겁니다. 이 말은 맞지만, 중요한 점이 하나 빠져 있습니다. 나쁜 습관을 반복하면 그 나쁜 습관이 기계적이 됩니다. 완벽한 연습을 통해 완벽해지는 규칙을 따라야 합니다. 이 책의 예제를 따라 할 때는 그런 마음가짐으로, 즉 여러분이 실제 웹사이트를 만들고 있으며 그 결과의 품질에 따라 여러 분의 명성과 보수가 달라진다는 자세로 하길 권합니다. 이 책에서 새 기술만 배우려 하지 말고, 좋은 습관을 들이는 계기로도 활용하십시오. 우리가 집중할 부분은 버전 관리와 품질보증(QA )입니다. 이 장에서는 버전 관리을 설명하고, 다음 장에서는 QA를 설명합니다.
4.2. 버전 관리 버전 관리의 가치에 관해 설명할 필요는 없으리라 봅니다. 그것만으로도 책 한 권이 나오니까 요. 버전 관리의 장점을 요약하면 다음과 같습니다. | 문서 | 프로젝트의 역사를 되짚어보면서 어떤 결정을 내렸는지, 구성 요소를 개발한 순서는 어떻게 되 는지 알 수 있으면 그것만으로도 이미 가치 있는 문서입니다. 프로젝트의 기술적인 역사를 안 다는 것은 매우 유용한 일입니다. | 귀속 | 팀에서 일한다면 누가 무엇을 담당했는지, 즉 귀속이 대단히 중요합니다. 코드에서 뭔가 불투 명하거나 의문스러운 점이 있을 때 누가 그렇게 바꿨는지 알 수 있다면 시간을 많이 절약할 수 있습니다. 주석으로도 충분한 답이 될 수 있지만, 그렇지 못하더라도 최소한 누구에게 물어볼 지는 알 수 있으니까요. | 실험 | 좋은 버전 관리 시스템에서는 실험도 가능합니다. 옆길로 새서 뭔가 새로운 시도를 해보더라도
58
한 권으로 끝내는 Node & Express
프로젝트의 안정성에는 아무 지장도 없습니다. 실험이 성공적이면 프로젝트에 반영하면 되고, 실패라면 그냥 폐기하면 됩니다.
필자는 몇 년 전부터 분산형 버전 관리 시스템distributed version control system (DVCS )을 쓰기 시작했 습니다. 여러 후보를 검토하다가 깃과 머큐리얼Mercurial로 좁혀졌고, 결국 어디서나 쓰이고 유연 한 깃으로 정했습니다. 둘 다 뛰어난 무료 버전 관리 시스템이며 둘 중 하나를 쓰길 권합니다. 이 책에서는 깃을 쓰겠지만, 머큐리얼이나 기타 버전 관리 시스템을 보조로 써도 됩니다. 깃에 익숙지 않다면 존 롤리거의 뛰어난 책 『분산 버전 관리 Git 사용설명서』 (제이펍, 2013 ) 를 권합니다. 또 코드 스쿨Code School에도 훌륭한 깃 입문 코스가 있습니다(http://try.github. io ).
4.3. 이 책에서 깃을 활용하는 법 먼저 git --version 명령으로 깃이 설치돼 있는지 확인합시다. 버전 번호가 뜨지 않으면 깃을 설치해야 합니다. 설치에 관해서는 깃 문서(http://git-scm.com )를 보십시오.1 이 책의 예제를 따라오는 방법은 두 가지입니다. 하나는 예제를 직접 타이핑하고 깃 명령어도 입력하는 방법입니다. 다른 방법은 필자가 이 책의 예제를 담아둔 깃 저장소를 복제clone해서 각 예제의 태그를 체크하는 겁니다. 직접 입력해봐야 더 잘 배우는 사람도 있고, 예제를 입력하지 않고 살펴보기만 하는 걸 좋아하는 사람도 있습니다.
1 역주_ OS X 요시미테 이후 버전 사용자라면 배시에서 git --version 명령을 사용했을 때 xCode를 설치하라는 창이 뜰 수 있습니다. 이는 OS 정책이므로 xCode를 설치할 필요는 없고, http://git-scm.com 에서 내려받은 파일로 깃을 설치한 다음, 다음과 같이 깃 파 일을 옮기고 명령행 창을 재실행하면 됩니다.
sudo mv /usr/bin/git /usr/bin/git-system 이 내용은 내려받은 .dmg 파일에 들어 있는 readme.txt 파일의 내용을 옮긴 것이므로 더 궁금한 점이 있으면 파일을 참조하시기 바 랍니다.
4장 - 모양새 갖추기
59
4.3.1. 직접 입력한다면 우리는 앞에서 이미 뷰, 레이아웃, 로고, 메인 애플리케이션 파일, package.json 파일 등 이미 프로젝트의 프레임워크를 거칠게나마 만들었습니다. 깃 저장소를 만들고 이 파일들을 추가합 시다. 먼저 프로젝트 디렉터리에 깃 저장소를 만듭니다. git init
파일을 추가하기 전에, 실수로 필요 없는 파일을 추가하지 않도록 .gitignore 파일을 만듭시다. 프로젝트 디렉터리에 텍스트 파일 .gitignore를 만들고 깃에서 기본적으로 무시할 파일이나 디 렉터리를 한 행에 하나씩 입력합니다. .gitignore는 와일드카드도 지원합니다. 예를 들어, 만약 에디터에서 백업 파일을 만들 때 meadowlark.js~처럼 끝에 ~를 붙인다면 .gitignore 파일에 *~를 추가해서 ~로 끝나는 파일은 전부 제외할 수 있습니다. 맥을 사용한다면 .DS_Store도 제
외하면 좋습니다. node_modules도 제외하는 게 좋은데, 그 이유는 곧 설명합니다. 따라서 이 시점에서 파일은 다음과 같습니다. node_modules *~ .DS_Store
NOTE_ .gitignore 파일에 들어 있는 항목은 서브디렉터리에도 적용됩니다. 따라서 프로젝트 루트에 있는 .gitignore에 *~를 넣으면 서브디렉터리에 있는 백업 파일도 무시합니다.
이제 기존 파일을 모두 추가할 수 있습니다. 깃에서 파일을 추가하는 방법은 여러 가지입니다. 필자는 일반적으로 모든 파일을 추가하는 git add -A을 씁니다. 깃에 익숙하지 않다면, 커밋 할 파일이 한두 개일 때는 git add meadowlark.js처럼 파일 하나씩 추가하고, 바뀐 부분 전 체(지운 파일도 포함됩니다)를 추가하려면 git add -A를 쓰십시오. 여기서는 이미 한 작업을 모두 추가할 테니 후자를 사용합니다. git add -A
60
한 권으로 끝내는 Node & Express
CHAPTER
5
품질보증
품질보증Quality assurance. 이 구절을 들으면 등줄기를 타고 소름이 돋는 개발자들이 많은데, 참 불 행한 일입니다. 품질 좋은 소프트웨어를 만들고 싶지 않습니까? 당연히 그렇게 하고 싶겠지요. 어려운 점은, 품질보증이 최종 목표가 아니라, 대립하는 견해를 낳는 주제란 겁니다. 필자는 웹 개발이 크게 두 가지 상황에서 이루어진다고 봅니다. | 큰 기업 또는 자금이 풍족한 기업 | 이런 경우는 보통 QA 부서가 있지만, 불행히도 QA 부서와 개발 부서가 대립하곤 합니다. 최 악이죠. 두 부서는 같은 팀에서 같은 목표를 갖고 일하지만, QA 부서는 버그를 더 많이 찾아내 야 일을 잘하는 거고, 개발 부서는 버그가 적어야 일을 잘하는 거니, 갈등과 경쟁의 소지가 처 음부터 들어 있습니다. | 작은 기업이나 자금이 부족한 기업 | 이런 경우는 대개 QA 부서가 없고 개발팀에서 QA와 소프트웨어 개발을 모두 담당합니다. 따 라서 갈등의 소지는 없습니다만, QA는 개발과 매우 다른 분야이며 요구되는 성격과 재능이 다 릅니다. 물론 불가능한 건 아니고 QA적 사고방식을 갖춘 개발자도 분명 존재하지만, 데드라인 이 다가오면 보통 QA는 뒤로 밀리기 마련입니다.
현실적으로는 여러 가지 기술이 필요하며 모든 방면에 전문가가 되는 건 점점 더 어려워집니
5장 - 품질보증
67
다. 하지만 직접 책임지지 않는 분야에 경쟁력이 있다면 당신은 팀에서 더 가치 있는 사람이 되 고, 팀은 더 효율적으로 움직일 수 있습니다. QA 기술을 배우는 개발자들이 좋은 예입니다. 이 들 지식 분야는 대단히 밀접하게 얽혀 있으므로 양쪽을 모두 이해하는 것은 대단히 가치 있는 일입니다.
QA와 개발 역할을 합쳐서 개발자가 QA도 담당하게 하려는 움직임도 있습니다. 이런 패러다 임에서는 QA 분야에 특화된 소프트웨어 기술자가 마치 개발자들의 컨설턴트처럼 일하며 개발 작업 흐름에 QA를 끼워 넣도록 돕습니다. QA 역할을 통합하든 분리하든, QA를 이해하는 것 이 개발자에게 도움이 된다는 건 명확합니다. 이 책은 개발자를 위한 책이지, QA 전문가를 위한 책이 아닙니다. 필자의 목표는 QA 전문가 를 양성하는 것이 아니라 QA 영역에 대해 경험을 조금 제공하는 겁니다. 일하는 곳에 QA 전 담자가 있다면, 이 장을 읽은 후에는 그 사람과 얘기하고 협력하기 쉬워질 겁니다. QA 전담자 가 없다면 프로젝트에 상세한 QA 계획을 세우는 시작 지점이 될 겁니다.
5.1. QA에 가치가 있나? QA는 비용이 듭니다. 이따금 매우 많이 들기도 합니다. 그럴 가치가 있을까요? QA는 인자가 많은 복잡한 공식입니다. 기업은 대부분 ‘투자 대비 수익률’ 모델에 따라 움직입니다. 돈을 쓰면 반드시 쓴 만큼은 벌어야 하고, 더 많이 벌수록 좋은 겁니다. 하지만 QA가 끼면 이 관계가 복 잡해집니다. QA 기간을 주기만 하면 품질 좋은 제품을 만들 수 있다고 칩시다. 분명 형편없는 결과물을 원하는 사람은 없지만, 시기에 맞게 시장에 내보내는 게 정말 중요할 때가 있습니다. 두 달 뒤에 완벽한 결과를 내는 것보다, 조금 부족하더라도 지금 결과를 내는 게 더 좋을 때가 있는 겁니다. 웹 개발에서 품질은 크게 네 가지 범주입니다. | 범위 | 범위란 당신이 제공하는 웹사이트나 서비스를 보는 사람 숫자입니다. 범위와 수익성은 직접 관 련되어 있습니다. 웹사이트를 보는 사람이 많을수록 제품이나 서비스를 사는 사람도 많습니다.
68
한 권으로 끝내는 Node & Express
개발자 관점에서는 검색엔진 최적화search engine optimization (SEO )가 범위에 가장 큰 영향을 미치므 로 QA 계획에는 SEO도 들어 있습니다. | 기능 | 일단 사람들이 사이트나 서비스에 방문한 다음에는 사이트 기능의 품질이 좋아야 사용자를 계 속 유지할 수 있습니다. 사이트가 광고대로 동작한다면 그렇지 않은 사이트보다 재방문자가 많 습니다. 다른 범주와 달리, 기능 테스트는 대개 자동화할 수 있습니다. | 사용성 | 기능은 정확한 동작과 밀접하지만, 사용성은 인간-컴퓨터 상호작용human-computer interaction (HCI ) 을 평가합니다. 근본적인 의문은 “기능이 대상 사용자에게 유용하게 구현됐는가?”입니다. 이 의문은 종종 ‘쓰기 쉬운가?’로 바뀌곤 합니다만, 너무 쉬운 것만 찾다 보면 유연성이나 기능을 잃을 수 있습니다. 또 프로그래머에게 쉬워 보이는 것과 기술자가 아닌 사용자에게 쉬운 것은 다를 수 있습니다. 달리 말하자면, 사용성을 평가할 때는 반드시 대상 사용자를 고려해야 합니 다. 사용성을 따지는 근본 인자는 사용자이므로 사용성을 자동으로 평가할 수는 없습니다. 하 지만 사용자 테스트는 QA 계획에 넣어야 합니다. | 미학 | 미학은 네 가지 범주 중에서 가장 주관적이므로 개발과의 관련성도 가장 적습니다. 사이트의 디자인에 관해서는 개발자가 관심 가질 일이 별로 없긴 하지만, QA 계획에서는 사이트 디자 인을 정기적으로 체크해야 합니다. 사이트를 대상 사용자 중 대표 샘플에 보여주고 요즘 유행 에 맞게 느껴지는지, 혹은 기대한 반응을 이끌어내지 못하는 건 아닌지 확인해보십시오. 미적 기준은 시대에 따라 바뀌며, 어떤 사용자에게 매력적인 디자인이 다른 사용자에겐 아무 느낌도 없을 수 있습니다.
QA 계획을 세울 때는 네 가지 범주를 모두 고려해야 하지만, 기능과 SEO는 개발 과정에서 자 동으로 테스트할 수 있으므로 이 장에서는 이 두 가지에 초점을 맞춥니다.
5장 - 품질보증
69
5.2. 논리 vs 표현 대체로 웹사이트에는 논리(종종 ‘비즈니스 논리’라 부르는데 필자는 상업적 편향이라 생각해 이 용어는 사용하지 않습니다)와 표현 두 가지 ‘영역’이 있습니다. 웹사이트의 논리는 순수한 지적 영역이라 생각해도 됩니다. 예를 들어 메도라크 여행사에서 스쿠터를 빌리려는 고객은 반 드시 유효한 운전면허가 있어야 한다는 규칙이 있다고 합시다. 이 규칙은 매우 단순한 데이터 기반 규칙입니다. 스쿠터를 예약할 때마다 사용자에게 유효한 운전면허가 있어야 합니다. 표 현은 이러한 논리와 직접적인 연결은 없습니다. 주문 페이지의 마지막 폼에 체크박스로 표현할 수도 있고, 고객이 유효한 운전면허 번호를 제공하면 메도라크에서 유효성 검사를 할 수도 있 습니다. 이건 중요한 차이점입니다. 논리 영역은 가능한 한 명확하고 단순해야 하지만, 표현 영 역은 필요에 따라 복잡할 수도, 단순할 수도 있습니다. 또 표현은 사용성과 미학을 고려해야 하 지만, 논리는 그렇지 않습니다. 가능하면 항상 논리와 표현을 명확히 구분해야 합니다. 방법은 여러 가지입니다. 이 책에서는 논리를 자바스크립트 모듈로 캡슐화하는 데 중점을 둡니다. 반면 표현은 HTML과 CSS, 멀티 미디어, 자바스크립트, 제이쿼리jQuery 같은 프론트엔드 라이브러리의 조합입니다.
5.3. 테스트 타입 이 책에서 설명할 테스트 타입은 크게 단위 테스트와 통합 테스트 두 가지 카테고리로 나뉩니 다(필자는 ‘시스템 테스트’를 통합 테스트의 한 가지로 봅니다). 단위 테스트는 각 구성 요소가 제대로 동작하는지 매우 세부적으로 테스트하며 통합 테스트는 여러 구성 요소, 때로는 전체 시스템 상호작용을 테스트합니다. 일반적으로 논리 테스트에는 단위 테스트가 더 유용하고 적절합니다(가끔 표현 부분에 단위 테스트를 적용할 때도 있습니다). 통합 테스트는 논리와 표현 모두에 유용합니다.
70
한 권으로 끝내는 Node & Express
CHAPTER
6
요청과 응답 객체
익스프레스로 웹 서버를 만들다 보면, 대부분의 일은 요청 객체로 시작해서 응답 객체로 끝납 니다. 이들 두 객체는 노드에서 쓰기 시작했고 익스프레스에서는 그 기능을 확장했습니다. 이 들 객체에 대해 자세히 알아보기 전에, 먼저 클라이언트(보통 브라우저)가 어떻게 서버에 페 이지를 요청하며 그 페이지는 어떻게 반환되는지 배경지식을 좀 알아봅시다.
6.1. URL의 각 부분
프로토콜
호스트
포트
경로
쿼리스트링
해시
| 프로토콜 | 프로토콜은 요청이 어떻게 전송될지 결정합니다. 우리는 http와 https만 다룰 겁니다. 그 외에
file과 ftp 프로토콜도 많이 쓰입니다.
6장 - 요청과 응답 객체
87
| 호스트 | 호스트는 서버를 가리킵니다. 당신의 컴퓨터에 있는 서버(localhost )나 로컬 네트워크에 있 는 서버 이름은 한 단어일 수도 있고, 숫자형 IP 주소일 수도 있습니다. 인터넷 호스트 이름은 .com이나 .net 같은 최상위 도메인top-level domain (TLD )으로 끝납니다. 또한 호스트 이름 앞에 서 브도메인이 붙을 수 있습니다. 서브도메인 이름에는 무엇이든 쓸 수 있지만 www가 가장 흔합 니다. 서브도메인은 옵션입니다. | 포트 | 서버마다 여러 포트가 있습니다. 80이나 443 같은 일부 포트 번호는 ‘특별’합니다. 포트를 생략 하면 HTTP는 80, HTTPS는 443이라고 가정합니다. 일반적으로 80이나 443 포트를 쓰지 않 는다면 1023보다 큰 포트 번호를 써야 합니다.1 보통 3000이나 8080, 8088 같은 기억하기 쉬 운 포트 번호를 씁니다. | 경로 | 경로는 일반적으로 URL에서 앱이 중요하게 여기는(읽고 판단하는) 첫 번째 부분입니다(프로 토콜, 호스트, 포트에 따라 결정하는 것도 가능하긴 하지만 모범 사례는 아닙니다). 경로를 통 해 페이지나 기타 자원을 유일하게 식별할 수 있어야 합니다. | 쿼리스트링 | 쿼리스트링은 옵션이며 이름/값 쌍을 모은 형태입니다. 쿼리스트링은 물음표(?)로 시작하고, 각 이름/값 쌍은 앰퍼샌드(&)로 구분합니다. 이름과 값은 모두 URL 인코드해야 합니다. 자바 스크립트에는 URL 인코드용 내장 함수 encodeURIComponent가 있습니다. 예를 들어 공백은 + 기호로 바뀌고 기타 특수문자는 숫자형 문자 참조로 바뀝니다. | 해시 | 해시hash는 프래그먼트fragment라고도 부르며, 서버에는 전혀 전달되지 않습니다. 해시는 오직 브 라우저에서만 사용합니다. 단일 페이지 애플리케이션이나 AJAX를 많이 쓰는 애플리케이션들 이 해시를 이용해 애플리케이션을 컨트롤하는 사례가 점점 더 늘어나고 있습니다. 원래 해시는
1 포트 0-1023은 소위 ‘잘 알려진 포트’라고 합니다. http://ko.wikipedia.org/wiki/TCP/UDP의_포트_목록을 참고하십시오.
88
한 권으로 끝내는 Node & Express
브라우저에서 앵커 태그(<a id="chapter06"> )로 표시한 문서의 특정 부분을 찾아가도록 하는 것이 유일한 목적이었습니다.
6.2. HTTP 요청 규칙 HTTP 프로토콜에는 클라이언트가 서버와 통신하는 요청 규칙request method (HTTP 동사 verb라고 도 부릅니다) 정의가 들어 있습니다. 가장 널리 쓰이는 규칙은 GET과 POST입니다. 브라우저에 URL을 입력하거나 링크를 클릭하면 브라우저는 서버에 HTTP GET 요청을 보냅니 다. 서버로 보내지는 중요한 정보는 URL 경로와 쿼리스트링입니다. 앱은 규칙, 경로, 쿼리스 트링에 따라 어떻게 응답할지 결정합니다. 웹사이트의 대부분 페이지는 GET 요청에 응답합니다. POST 요청은 보통 서버에 정보를 보낼 때 사용합니다. 서버는 요청에 포함된 정보를 처리한 후 응답을 보내는데, 정보가 POST 요청으 로 들어왔든 GET 요청으로 들어왔든 응답 HTML은 대개 같습니다. 브라우저는 서버와 통신할 때 AJAX 또는 GET과 POST만 사용합니다. 반면 웹 서비스는 HTTP 규칙을 좀 더 창의적으로 쓸 때가 많습니다. 예를 들어 API 호출을 통 해 뭔가를 지울 때 유용한 DELETE라는 HTTP 규칙이 있습니다. 노드와 익스프레스를 쓸 때는 어떤 요청에 응답할지가 완전히 자유입니다(개중에는 널리 쓰이 지 않아서 잘 지원되지 않는 것도 물론 있습니다). 익스프레스에서는 보통 핸들러를 통해 요청 에 응답합니다.
6.3. 요청 헤더 페이지로 이동할 때 서버로 URL만 전달되는 건 아닙니다. 브라우저는 웹사이트에 방문할 때 마다 ‘보이지 않는’ 정보를 아주 많이 보냅니다. 개인 정보가 새는 문제를 얘기하는 게 아닙니다 (브라우저가 멀웨어에 감염됐다면 가능한 일이지만요). 먼저 브라우저는 서버에 페이지의 어 떤 언어 버전을 원하는지 전달합니다. 예를 들어 크롬을 스페인에서 내려받는다면 해당 페이지
6장 - 요청과 응답 객체
89
의 스페인어 버전을 요청할 겁니다. 브라우저, 운영체제, 하드웨어 등의 ‘사용자 에이전트’ 정 보와 기타 소소한 정보도 보냅니다. 이 정보는 모두 요청 헤더의 형태로 전달되며, 요청 객체 의 headers 프로퍼티에서 이 정보를 볼 수 있습니다. 아주 단순한 익스프레스 라우트를 만들어 브라우저가 어떤 정보를 보내는지 볼 수 있습니다. app.get('/headers', function(req,res){ res.set('Content-Type','text/plain'); var s = ''; for(var name in req.headers) s += name + ': ' + req.headers[name] + '\n'; res.send(s); });
6.4. 응답 헤더 브라우저가 요청 헤더 형태로 서버에 정보를 보내는 것과 마찬가지로, 서버는 응답할 때 브라 우저가 화면에 렌더링하거나 표시하지 않는 정보를 보냅니다. 응답 헤더에는 일반적으로 메타 데이터와 서버 정보가 포함됩니다. 전송 중인 콘텐츠 종류(HTML, 이미지, CSS, 자바스크립 트 등)를 브라우저에 알리는 Content-Type 헤더는 이미 본 적이 있습니다. 브라우저는 URL 경로보다 Content-Type 헤더를 더 우선시합니다. 즉 /image.jpg 경로로 HTML을 전송하거나, /text.html 경로로 이미지를 전송할 수도 있습니다(물론 이렇게 하라는 건 아닙니다. 단지 경로 는 추상적이며 브라우저는 Content-Type을 이용해 콘텐츠를 어떻게 렌더링할지 결정한다는 것을 이해해야 한다는 뜻입니다). 헤더에는 응답이 압축됐는지 아닌지, 어떤 인코딩을 사용하 는지 같은 정보도 들어 있습니다. 브라우저에서 자원을 얼마나 오래 캐시하고 있을지 결정하는 데 필요한 힌트도 들어 있습니다. 캐시에 관한 힌트는 웹사이트를 최적화할 때 중요하게 고려 해야 하는 부분이며 16장에서 자세히 설명합니다. 응답 헤더에는 서버가 어떤 타입인지 나타 내는 정보가 들어 있을 때도 잦고, 심지어는 운영체제에 관한 세부 정보가 들어 있을 때도 있습 니다. 서버 정보를 보내면 해커가 사이트에 침입할 때 단서로 삼을 수 있다는 단점도 있습니다. 보안에 대단히 민감한 서버에서는 종종 이 정보를 생략하며, 가짜 정보를 제공할 때도 있습니 다. 익스프레스의 기본 X-Powered-By 헤더는 쉽게 비활성화할 수 있습니다. app.disable('x-powered-by');
90
한 권으로 끝내는 Node & Express
CHAPTER
7
핸들바를 사용한 템플릿
그동안 템플릿을 사용하지 않았거나 템플릿이 뭔지 모른다면, 템플릿이야말로 이 책에서 배워 야 할 가장 중요한 주제입니다. PHP를 써왔다면 왜 이렇게 호들갑이냐고 할 수도 있습니다.
PHP는 제대로 된 템플릿 언어라고 부를 수 있는 첫 번째 언어니까요. 웹에 맞게 만들어진 주 요 언어는 거의 모두 어떤 형태로든 템플릿을 지원합니다. 요즘 달라진 것은 ‘템플릿 엔진’이 언 어와 분리되고 있다는 겁니다. 예를 들자면 대단히 인기 있는 언어 독립적 템플릿 엔진으로 머 스태시가 있습니다. 그래서, 대체 템플릿이 뭘까요? 우선 템플릿이란 게 무엇이 아닌지부터 시작해봅시다. 한 언어 로 다른 언어를 생성하는 ‘너무’ 분명하고 단순한 방법을 살펴보죠(구체적으로는 자바스크립트 로 HTML을 만들어보겠습니다). document.write('<h1>Please Don\'t Do This</h1>'); document.write('<p><span class="code">document.write</span> is naughty,\n'); document.write('and should be avoided at all costs.</p>'); document.write('<p>Today\'s date is ' + new Date() + '.</p>');
아마 이 코드가 ‘분명’해 보이는 유일한 이유는, 프로그래밍을 항상 이런 식으로 배워왔기 때문 일 겁니다. 10 PRINT "Hello world!"
명령형 언어에서는 ‘이걸 하고, 다음엔 그걸 하고, 그다음엔 저걸 해라’ 같은 방법이 잘 어울리 는 곳도 있습니다. 500행 정도 되는 자바스크립트로 복잡한 계산을 한 끝에 나오는 결과가 단
7장 - 핸들바를 사용한 템플릿 105
하나의 숫자이고, 매 단계가 이전 단계에 의존한다면 아무 문제도 없습니다. 하지만 상황이 달 라진다면, 예를 들어 순수한 자바스크립트는 단 세 줄인데 자바스크립트로 만들 HTML이 500 행이라면 어떨까요? document.write를 500번 쓰는 게 상식적인가요? 설마요. 문제의 핵심은 이겁니다. 문맥 교환 switching context은 문제가 생기기 마련입니다. 자바스크립트를 많이 써야 할 때, HTML 중간에 섞어 쓰면 불편하고 혼란스럽습니다. 우리는 이미 자바스크립 트를 <script> 블록 안에 쓰는 데 익숙하므로 아주 심각할 정도는 아닙니다. 하지만 여전히 문 맥 교환이 존재합니다. 우리는 HTML을 쓰거나, 아니면 <script> 블록 안에 자바스크립트를 쓰거나 둘 중 하나입니다. 자바스크립트로 HTML을 생성하면 다음과 같이 문제가 아주 많습니 다. 어떤 문자를 이스케이프해야 하는지, 어떻게 이스케이프해야 하는지 끊임없이 신경을 써야 합 니다. 자바스크립트로 생성한 HTML에 자바스크립트가 포함돼 있으면 순식간에 문제가 커집니다.
●
에디터에서 제공하는 문법 강조나 언어별로 제공하는 편리한 기능들이 무색해집니다.
●
잘못된 HTML을 찾기가 훨씬 어렵습니다.
●
한 번 보고는 무슨 뜻인지 알기 어렵습니다.
●
다른 사람들이 당신의 코드를 이해하기 어렵습니다.
●
템플릿을 쓰면 해당 언어를 쓰는 것과 비슷하면서도 동적 데이터를 삽입할 수 있어서 이런 문 제가 해결됩니다. 이전 예제를 머스태시 템플릿으로 고쳐 쓴 결과를 보십시오. <h1>Much Better</h1> <p>No <span class="code">document.write</span> here!</p> <p>Today's date is {{today}}.</p>
이제 {{today}}의 값만 제공하면 됩니다. 이게 템플릿 언어의 핵심입니다.
106 한 권으로 끝내는 Node & Express
7.1. 절대 규칙은 이것 하나뿐1 자바스크립트에서 HTML을 절대 쓰지 말라는 것이 아니라, 가능하면 피하라는 뜻입니다. 특 히, 제이쿼리 같은 라이브러리 덕에 프론트엔드 코드에서는 이렇게 하는 게 더 좋습니다. 예를 들어 이 정도라면 별문제 없을 겁니다. $('#error').html('Something <b>very bad</b> happened!');
하지만 위 코드가 최종적으로는 이렇게 바뀐다면 어떨까요. $('#error').html('<div class="error"><h3>Error</h3>' + '<p>Something <b><a href="/error-detail/' + errorNumber +'">very bad</a></b> ' + 'happened. <a href="/try-again">Try again<a>, or ' + '<a href="/contact">contact support</a>.</p></div>');
템플릿을 쓰는 편이 좋습니다. 요점은, HTML을 문자열로 만드는 게 나은지, 템플릿을 쓰는 편 이 나은지 상황에 따라 잘 판단해야 한다는 겁니다. 필자는 템플릿을 추천하는 편입니다. 정말 단순한 걸 빼면 자바스크립트로 HTML을 생성하는 일은 피하십시오.
7.2. 템플릿 엔진 선택 노드 세계에는 여러 가지 템플릿 엔진이 있는데 그중 뭘 선택해야 할까요? 이건 매우 복잡한 질문이고, 답은 당신의 필요에 따라 매우 다릅니다. 몇 가지 고려할 점이 있습니다. | 성능 | 당연히 가능한 한 빠른 템플릿 엔진을 원할 겁니다. 템플릿 엔진이 웹사이트를 느리게 만들어 서는 안 되겠죠. | 클라이언트용, 서버용? 또는 겸용? | 전부 그런 건 아니지만, 템플릿 엔진은 대부분 서버와 클라이언트 양쪽에서 쓸 수 있습니다. 두 1 친구이자 멘토인 폴 인먼(Paul Inman)의 가르침입니다.
7장 - 핸들바를 사용한 템플릿 107
영역에서 모두 템플릿이 필요하다면(그렇게 될 겁니다) 양쪽에서 다 쓸 수 있는 엔진을 고르길 권합니다. | 추상화 수준 | 꺾쇠(< >) 안에 코드가 들어 있는 일반적인 HTML처럼 친숙한 것을 원하십니까, 아니면 사실
HTML 형식이 싫고 꺾쇠에서 해방되길 원하십니까? 템플릿(특히 서버 쪽 템플릿)을 쓰면 이 런 부분에서 선택할 수 있는 여지가 있습니다.
이들은 템플릿 언어를 선택할 때 고려할 중요한 기준 중 일부일 뿐입니다. 이 주제에 관해 더 상세한 글을 읽고 싶다면 비나 바사바라Veena Basavaraj가 링크드인에 사용할 템플릿 언어를 선정하 면서 그 기준에 관해 쓴 블로그 포스트(http://bit.ly/templating_selection_criteria )를 읽어보길 권합니다. 결국 링크드인에는 더스트Dust (http://akdubya.github.io/dustjs )가 선택됐지만, 필자가 선 호하고 이 책에서 사용할 핸들바(http://handlebarsjs.com ) 역시 마지막까지 남은 선택지 중 하나였습니다. 익스프레스에서는 템플릿 엔진을 자유롭게 선택할 수 있으므로 핸들바가 마음에 들지 않는 다면 쉽게 다른 엔진으로 바꿀 수 있습니다. 다른 템플릿 엔진을 찾아보려면 http://garann. github.io/template-chooser가 재미있고 유용할 겁니다.
7.3. 다른 접근법: 제이드 대부분의 템플릿 엔진이 HTML을 중심에 둔 접근법을 택했지만, 제이드는 HTML 대부분을 추상화했습니다. 익스프레스를 만든 TJ 할로웨이척이 제이드도 만들었으니, 제이드와 익스프 레스가 매우 잘 통합되는 건 당연합니다. 제이드가 채택한 접근법은 매우 우아합니다. 그 핵심 은 HTML이 직접 작성하기에는 몹시 지루하고 군더더기가 많은 언어라는 판단입니다. 제이드 템플릿이 어떤 모양인지, 그 템플릿이 출력하는 HTML과 비교해봅시다(이 코드는 제이드 홈 페이지 http://jade-lang.com에서 가져왔고 책에 맞게 조금 수정했습니다).
108 한 권으로 끝내는 Node & Express
CHAPTER
8
폼 처리
사용자에게서 정보를 받을 때는 보통 HTML 폼을 사용합니다. 브라우저에서 일반적 방법으로 폼을 전송하든, AJAX를 사용하든, 멋진 프론트엔드 컨트롤을 사용하든 간에, 근본적인 메커니 즘은 대부분 HTML 폼입니다. 이 장에서는 폼을 처리하고 유효성을 검사하며 파일을 업로드는 방법을 살펴봅니다.
8.1. 클라이언트 데이터를 서버로 보내기 클라이언트 데이터를 서버에 보내는 방법은 크게 말해 쿼리스트링과 요청 본문입니다. 일반적 으로 쿼리스트링을 쓸 때는 GET 요청을, 요청 본문을 쓸 때는 POST 요청을 사용합니다(HTTP 프로토콜에 어떤 제한이 있는 건 아니므로 두 요청을 반대로 해도 안 될 건 없지만, 그렇게 할 이유는 전혀 없습니다. 표준 관행을 따르는 편이 가장 좋습니다). POST는 안전하고 GET은 그렇지 않다고 오해하는 사람이 많은데 그렇지 않습니다. HTTPS를
쓰면 둘 다 안전하고, 쓰지 않으면 어느 쪽도 안전하지 않습니다. HTTPS를 쓰지 않으면 침입자 는 GET 요청의 쿼리스트링과 똑같이 쉽게 POST 본문의 데이터를 볼 수 있습니다. GET 요청을 쓰 면 숨긴 필드를 포함해 사용자가 입력한 내용이 모두 쿼리스트링에 나타나는데, 보기도 좋지 않 고 문제도 많습니다. 또한 브라우저는 대개 쿼리스트링 길이에 제한을 두지만, POST 요청 본문에 는 길이 제한이 없습니다. 따라서 필자는 일반적으로 폼 전송에 POST를 권합니다.
8장 - 폼 처리 127
8.2. HTML 폼 이 책은 서버 쪽에 초점을 맞추고 있지만, HTML 폼을 만드는 몇 가지 기본은 반드시 이해해야 합니다. 다음 예제 코드를 보십시오. <form action="/process" method="POST"> <input type="hidden" name="hush" val="hidden, but not secret!"> <div> <label for="fieldColor">Your favorite color: </label> <input type="text" id="fieldColor" name="color"> </div> <div> <button type="submit">Submit</button> </div> </form>
사용자가 폼을 전송하면 /process URL이 호출되며 필드 값이 요청 본문에 담겨 서버로 전송됩 니다. 이 예제에서는 <form> 태그에 명시적으로 POST를 지정했는데, 지정하지 않았을 때 기본값은 GET입니다. action 속성은 폼의 정보를 받을 URL입니다. 이 필드를 생략하면 폼이 있는 URL,
즉 자기 자신으로 전송됩니다. 설령 AJAX를 쓰더라도 항상 action 속성에 유효한 값을 지정 하길 바랍니다. 데이터 손실의 위험을 피하기 위해서입니다. 자세한 내용은 22장에서 더 설명 하겠습니다. 서버의 관점에서 <input> 필드에 중요한 속성은 name 속성입니다. 서버는 이 속성을 보고 각 필드를 식별합니다. name 속성과 id 속성은 용도가 다릅니다. id 속성은 스타일을 비롯해 프론 트엔드 기능에 써야 합니다(서버로 전송되지 않습니다). 숨긴 필드는 사용자의 브라우저에 표시되지 않습니다만, 이 필드를 예민한 정보나 기밀 용도로 사용해서는 안 됩니다. 누구든 소스 보기를 누르기만 하면 이 필드의 내용을 볼 수 있습니다.
HTML에 페이지당 폼 개수 제한은 없습니다(초기 서버 프레임워크, 이를테면 ASP에는 제한 이 있긴 했습니다).1 폼은 논리적으로 일관되게, 즉 폼 하나에 전송할 필드가 모두(옵션이나 빈 필드는 상관없습니다) 들어가게 하고, 전송받지 않을 필드는 쓰지 않길 권합니다. 페이지
1 아주 오래된 브라우저에서는 폼을 여러 개 쓰면 문제가 생길 수 있으니, 호환성이 정말 중요하다면 폼을 단 하나만 써야 할 수도 있습니다.
128 한 권으로 끝내는 Node & Express
하나에 두 가지 서버 동작(action 속성)이 들어 있다면 폼을 두 개 쓰십시오. 예를 들어 페이 지에 사이트 검색용 폼이 있고 이메일 뉴스 구독 신청용 폼이 따로 있을 수 있습니다. 모든 필 드를 폼 하나에 넣고 버튼에 따라 달리 동작하게 할 수 있기는 하지만 구현하기도 골치 아프고, 접근성을 중시하는 브라우저들이 폼을 렌더링하는 방식 때문에 장애가 있는 사람들은 이용하 기 어렵습니다.
8.3. 인코딩 브라우저나 AJAX가 전송한 폼은 어떤 형식으로든 인코드되어야 합니다. 인코딩을 명시하지 않 았을 때 기본값은 application/x-www-form-urlencoded입니다(‘URL 인코드’를 길게 썼을 뿐입 니다). 이 인코딩은 기초적이고 쓰기 쉬우며 익스프레스에서도 기본으로 지원합니다. 파일 업로드하기는 좀 복잡합니다. URL 인코딩으로 파일을 보내는 쉬운 방법은 없으므로 multipart/form-data 인코딩 타입을 써야 합니다. 익스프레스 최근 버전은 기본적으로 이 인
코딩을 지원하지 않으므로, 이 인코딩을 대체할 방법에 대해 곧 설명하겠습니다.
8.4. 폼을 처리하는 다른 방법 AJAX를 쓰지 않으면 폼을 전송할 때 페이지를 다시 불러오는 브라우저의 기본 방식을 따를 수 밖에 없습니다. 하지만 페이지를 다시 불러오는 방법은 바꿀 수 있습니다. 폼을 처리할 때 고려 할 것은 두 가지입니다. 첫 번째는 처리할 서버 경로(action )이고 두 번째는 브라우저에 보낼 응답입니다. 폼에서 method='POST' (권장)를 쓸 때는 폼을 표시하는 경로와 처리하는 경로가 같을 때가 대 부분입니다. 전자(표시)는 GET 요청이고 후자(처리)는 POST 요청이므로 이에 따라 구분할 수 있습니다. 이 방법을 택하면 폼에서 action 속성을 생략할 수 있습니다. 폼을 표시하는 경로와 처리하는 경로를 다르게 할 수도 있습니다. 예를 들어 연락처 페이 지에서 /contact 경로를 쓴다면, 폼을 처리할 경로는 /process-contact로 할 수 있습니다
8장 - 폼 처리 129
(action='/process-contact'로 써야겠죠). 이렇게 하면 폼을 전송할 때 GET 요청을 쓸 수 있 지만, URL에 폼 필드가 노출되므로 필자는 권하지 않습니다. 이 방법은 여러 URL에서 같은 폼을 공유할 때, 예를 들어 이메일 가입 양식을 여러 페이지에 올려두고 싶을 때 쓸 만합니다. 어떤 방법을 택했든, 브라우저에 어떤 응답을 보낼지도 정해야 합니다. 다음 중에서 선택할 수 있습니다. | 직접 HTML 응답 | 폼을 처리한 후 브라우저에 HTML (예를 들어 뷰)을 직접 보냅니다. 이 방법을 쓰면 사용자가 페이지를 새로 고칠 때마다 경고가 표시되고, 북마크나 뒤로 가기 버튼에도 영향이 있으므로 권장하지 않습니다. | 302 리다이렉션 | 이 방법은 흔히 쓰이긴 하지만, 사실 302 응답 코드의 원래 의미를 잘못 이해한 겁니다. HTTP
1.1에서 추가된 303 (기타 위치 보기) 응답 코드가 더 적절합니다. 1996년 이전에 만들어진 브라우저를 지원해야 하는 게 아니라면 303을 써야 합니다. | 303 리다이렉션 |
303 (기타 위치 보기) 응답 코드는 HTTP 1.1에서 302 리다이렉션의 오용을 해결하기 위해 추가된 코드입니다. HTTP 명세는 브라우저가 303 리다이렉션을 따라갈 때 원래 요청method과 무관하게 GET 요청을 사용하도록 규정했습니다. 폼 전송 요청에 응답할 때는 이 방법을 따르길 권합니다.
폼 전송에 대해 303 리다이렉션으로 응답하도록 권했으니, 다음 질문은 “어디로 리다이렉트하 는가?”가 될 겁니다. 답은 스스로 택해야 합니다. 널리 쓰이는 방법은 다음과 같습니다. | 전용 성공/실패 페이지로 리다이렉션 | 이 방법을 쓸 때는 성공 또는 실패 메시지에 따라 전용 URL을 준비해야 합니다. 예를 들어 사 용자가 광고 이메일 구독을 수락했지만, 데이터베이스 에러가 있다면 /error/database로 리다
130 한 권으로 끝내는 Node & Express
CHAPTER
9
쿠키와 세션
HTTP는 ‘상태가 없는stateless’ 프로토콜입니다. 즉 브라우저에서 페이지를 불러온 다음, 같은 웹 사이트에 있는 다른 페이지로 이동하더라도, 서버나 브라우저 중 누구도 같은 브라우저가 같은 사이트를 방문했음을 알 수 없다는 뜻입니다. 달리 말하자면, 웹은 원래 모든 HTTP 요청에는 서버에서 그 요청을 수행할 때 필요한 정보가 모두 들어 있다는 방식으로 움직입니다. 하지만 이건 문제입니다. 만약 이야기가 “모든 HTTP 요청에는… 들어 있다”에서 그냥 끝난다 면, 우리는 어디에도 ‘로그인’할 수 없습니다. 스트리밍 미디어도 동작하지 않을 겁니다. 웹사이 트는 페이지를 이동할 때마다 사용자 선호 설정을 전부 잊어버릴 겁니다. 따라서 HTTP 위에 ‘상태’를 전송할 방법이 필요합니다. 쿠키와 세션은 이 목적에 따라 만들어졌습니다. 불행히도, 사람들이 쿠키를 써서 나쁜 일들을 했기 때문에 쿠키도 덩달아 누명을 썼습니다. ‘모 던 웹’이 제대로 동작하려면 쿠키가 꼭 필요한데 이렇듯 악명이 높아졌으니 참 불행한 일입니 다(HTML5에서 이와 같은 목적에 쓸 수 있는 로컬 스토리지 같은 새 기능을 도입하긴 했습니 다). 쿠키의 아이디어는 단순합니다. 서버는 정보를 조금 보내고 브라우저는 일정 시간 그 정보를 저장하는데, 그 시간은 설정할 수 있습니다. 어떤 정보를 보낼지는 온전히 서버의 몫입니다. 보 통은 고유한 ID 숫자 하나만 보내고 이 숫자로 특정 브라우저를 식별해 ‘상태’를 관리합니다. 쿠키에 대해 알아야 할 중요한 점이 있습니다.
9장 - 쿠키와 세션 143
| 사용자가 쿠키에 대해 알 수 있습니다 | 서버가 클라이언트에 보내는 쿠키는 전부 클라이언트에서 볼 수 있습니다. 내용을 볼 수 없도 록 쿠키를 암호화하는 건 가능하긴 하지만, 사실 그럴 필요도 없습니다(범죄에 악용할 게 아니 라면 말이죠!). 잠시 뒤에 서명된 쿠키에 대해 설명할 텐데, 이런 쿠키는 콘텐츠를 조금 가리긴 하지만 해커로부터 안전할 정도로 암호화하는 건 아닙니다. | 사용자는 쿠키를 삭제하거나 차단할 수 있습니다 | 사용자는 쿠키에 대해 모든 권한을 갖고 있고, 브라우저는 쿠키를 하나씩이든, 한꺼번에든 지 울 수 있습니다. 사용자에게 악의가 있지 않은 한 쿠키를 일부러 지울 이유는 없지만, 사이트를 테스트할 때는 편리합니다. 사용자는 쿠키를 차단할 수도 있는데, 이건 더 큰 문제입니다. 쿠키 가 없어도 동작하는 웹 애플리케이션은 정말 단순한 일밖에 못합니다. | 쿠키는 조작할 수 있습니다 | 쿠키의 콘텐츠를 맹목적으로 신뢰한다면, 브라우저가 서버에 연결된 쿠키를 요청할 때마다 공 격에 문을 열어주는 거나 마찬가지입니다. 예를 들어 쿠키에 들어 있는 코드를 실행하는 게 이 런 어리석음의 전형적인 예입니다. 쿠키 조작 가능성을 없애려면 서명된 쿠키를 사용하십시오. | 쿠키를 공격에 사용할 수 있습니다 | 최근 몇 년 사이 크로스 사이트 스크립트 공격cross-site scripting attack (XSS )이란 것이 등장했습니다.
XSS 공격법 중 하나는 악의적 자바스크립트를 써서 쿠키의 콘텐츠를 조작하는 것입니다. 이것 이 서버로 돌아온 쿠키의 콘텐츠를 신뢰해서는 안 되는 또 다른 이유입니다. 서명된 쿠키를 쓰 면, 사용자가 조작했든 악의적 자바스크립트로 조작했든 쿠키 내용이 바뀌었을 경우 분명히 알 수 있습니다. 오직 서버만 쿠키 내용을 수정할 수 있게 제한하는 설정도 있습니다. 이런 쿠키는 활용도가 떨어질 수는 있지만, 확실히 더 안전합니다. | 쿠키를 남용하면 사용자가 알 수 있습니다 | 사용자의 컴퓨터에 쿠키를 너무 많이 저장하거나 데이터를 너무 많이 저장하면 사용자를 거슬 리게 합니다. 이런 일은 피해야 합니다. 쿠키는 최소한으로 유지하십시오.
144 한 권으로 끝내는 Node & Express
| 세션을 쿠키보다 우선시하십시오 | 대부분 세션을 써서 상태를 관리할 수 있으며 일반적으로 세션을 쓰는 편이 더 낫습니다. 세션 은 더 쉽고 사용자 스토리지를 남용할 걱정도 없으며, 보안도 더 낫습니다. 물론 세션도 쿠키에 의존하지만, 세션을 쓰면 익스프레스가 어려운 작업을 많이 덜어줍니다.
NOTE_ 쿠키는 마법이 아닙니다. 서버가 클라이언트에 쿠키를 저장하려 할 때는 이름/값 쌍이 들어 있는 Set-Cookie 헤더를 보냅니다. 클라이언트는 자신이 저장하고 있는 쿠키와 관련된 요청을 보낼 때는 쿠키 값이 들어 있는 요청 헤더를 여러 개 보냅니다.
9.1. 인증 위임 쿠키 보안을 위해서는 쿠키 시크릿이 필요합니다. 쿠키 시크릿은 서버만 알고 있는 문자열이 며 쿠키를 클라이언트에 보내기 전에 암호화할 때 사용합니다. 시크릿은 비밀번호가 아니므로 기억할 필요가 없고, 무작위 문자열을 써도 됩니다. 필자는 보통 xkcd (http://bit.ly/xkcd_ pw_generator )에 착안해 만든 무작위 비밀번호 생성기를 써서 쿠키 시크릿을 만듭니다.
쿠키 시크릿이나 데이터베이스 비밀번호, API 토큰(트위터, 페이스북 등) 같은 타사 인증은 외부로 위임하는 경우가 많습니다. 인증을 위임하면 인증서를 찾고 업데이트하기 쉬워서 유지 보수가 쉬울 뿐 아니라, 버전 관리 시스템에 인증서 파일을 추가하지 않아도 됩니다. 깃허브나 기타 공개 소스 관리 저장소를 사용하는 오픈 소스 저장소에서는 이 점이 굉장히 중요합니다. 이런 목적을 위해 우리는 인증서를 외부 자바스크립트 파일에 보관하겠습니다( JSON이나
XML도 상관없지만 자바스크립트가 제일 쉽습니다). credentials.js 파일을 만들어봅시다. module.exports = { cookieSecret: 'your cookie secret goes here', };
이 파일을 실수로 저장소에 올리지 않도록 credentials.js를 .gitignore 파일에 추가합니다. 애 플리케이션에서 인증서를 불러올 때는 다음 코드만 쓰면 됩니다. var credentials = require('./credentials.js');
9장 - 쿠키와 세션 145
나중에 다른 인증서를 저장할 때도 이 파일을 쓰겠지만, 일단 지금은 이 쿠키 시크릿만 있으면 됩니다.
NOTE_ 저장소 방식을 따라 하고 있다면 credentials.js 파일은 직접 만들어야 합니다. 저장소에는 이 파 일이 들어 있지 않으니까요.
9.2. 쿠키와 익스프레스 앱에서 쿠키를 설정하거나 접근하려면 cookie-parser 미들웨어를 연결해야 합니다. 먼저 npm install --save cookie-parser로 미들웨어를 설치하고, 다음 코드로 쿠시 시크릿을 연결합
니다. app.use(require('cookie-parser')(credentials.cookieSecret));
이 작업을 하고 나면, 응답 객체에 접근할 수 있는 곳이라면 어디서든 쿠키나 서명된 쿠키를 만 들 수 있습니다. res.cookie('monster', 'nom nom'); res.cookie('signed_monster', 'nom nom', { signed: true });
NOTE_ 서명된 쿠키가 서명되지 않은 쿠키보다 우선입니다. 서명된 쿠키에 signed_monster라는 이름을 붙였다면, 서명되지 않은 쿠키에 같은 이름을 쓸 수는 없습니다(undefined가 반환됩니다).
클라이언트가 보낸 쿠키 값을 가져오려면 요청 객체의 cookie나 signedCookie 프로퍼티에 접근하면 됩니다. var monster = req.cookies.monster; var signedMonster = req.signedCookies.signed_monster;
146 한 권으로 끝내는 Node & Express
CHAPTER
10
미들웨어
우리는 이미 몇 가지 미들웨어를 사용해봤습니다. body-parser, cookie-parser, static 같 은 기존 미들웨어도 써봤고, 쿼리스트링에 &test=1이 있는지 확인하는 미들웨어, 404 핸들러 는 직접 만들어봤습니다. 그런데 미들웨어는 정확히 무슨 뜻일까요? 개념적으로 말하면 미들웨어는 기능, 특히 애플리케이션에 대한 HTTP 요청에서 동작하는 기 능을 캡슐화하는 방법입니다. 현실적으로 말하면 미들웨어는 단순히 매개변수 세 가지를 받는 함수입니다. 세 가지 매개변수란 요청 객체와 응답 객체, 그리고 곧 설명할 'next' 함수입니 다. 에러 처리를 위해 네 가지 매개변수를 받는 미들웨어도 있는데, 그건 이 장 마지막에서 설 명할 겁니다. 미들웨어는 파이프라인이라 부르는 것 속에서 실행됩니다. 물이 흐르고 있는 파이프를 생각해 봅시다. 파이프 한쪽 끝에서는 물을 펌프로 끌어올리고, 중간에는 게이지와 밸브도 있습니다. 이 비유에서 중요한 부분은 순서에 의미가 있다는 겁니다. 압력 게이지를 밸브 앞에 두느냐, 뒤 에 두느냐에 따라 게이지에 표시되는 내용이 다를 겁니다. 마찬가지로 물에 무언가를 주입하는 밸브가 있다면, 그 밸브 ‘다음’을 흐르는 물에는 모두 그 재료가 섞여 있을 겁니다. 익스프레스 앱에서는 app.use를 호출해 미들웨어를 삽입합니다. 익스프레스 4.0 이전에는 라우터를 개발자가 명시적으로 연결해야 했기에 파이프라인이 복잡 했습니다. 라우터에서 연결하는 위치에 따라 라우트는 순서를 벗어나서 연결될 수 있고, 미들 웨어와 라우트 핸들러를 섞어 쓴다면 파이프라인 순서가 혼란스러워질 수 있습니다. 익스프레 스 4.0에서는 미들웨어와 라우트 핸들러가 연결한 순서대로 호출되므로 순서를 훨씬 명확히
10장 - 미들웨어 155
알 수 있습니다. 파이프라인 맨 마지막 미들웨어에는 다른 어떤 라우트와도 일치하지 않는 요청을 처리하는 폴 백 핸들러를 두는 게 일반적입니다. 이 미들웨어는 보통 상태 코드 404 (찾을 수 없음)을 반환 합니다. 파이프라인에서 요청은 어떻게 ‘종료’될까요? 각 미들웨어에 전달된 next 함수가 하는 일이 요 청 전달입니다. next ( )를 호출하지 않으면 해당 요청은 그 미들웨어에서 종료됩니다. 미들웨어와 라우트 핸들러에 대해 유연하게 생각할 수 있어야 익스프레스가 어떻게 동작하는 지 이해하기 쉬워집니다. 염두에 두어야 할 것들을 정리했습니다. app.get, app.post 등의 라우트 핸들러는 GET, POST 등 특정한 HTTP 동사만 처리하는 미들웨어라고
●
생각해도 됩니다(app.get, app.post 등을 묶어서 app.VERB라고 부르곤 합니다). 반대로 미들웨어는 모 든 HTTP 동사를 처리하는 라우트 핸들러라고 생각할 수 있습니다(따라서 모든 HTTP 동사를 처리하는 app.all과 본질적으로 같습니다. PURGE 같은 고급 동사에서는 약간의 차이가 있지만 일반적인 동사에서 는 마찬가지입니다). 라우트 핸들러는 첫 번째 매개변수로 경로를 받습니다. 이 경로가 모든 라우트와 일치하게 하려면 /*를 쓰
●
기만 하면 됩니다. 미들웨어 역시 첫 번째 매개변수로 경로를 받을 수 있지만 이는 옵션입니다(생략하면 /* 와 마찬가지로 모든 경로에 일치합니다). 라우트 핸들러와 미들웨어는 매개변수를 두 개, 세 개, 네 개 받는 콜백 함수를 받습니다(매개변수를 아예
●
쓰지 않거나 하나만 써서 안 될 건 없지만 그럴 이유가 없습니다). 콜백 함수의 매개변수가 두 개 또는 세 개 라면, 첫 번째와 두 번째 매개변수는 각각 요청/응답 객체이며 세 번째 매개변수는 next 함수입니다. 매개 변수가 네 개 있으면 그 미들웨어는 에러 처리 미들웨어로 변하며, 이때 첫 번째 매개변수는 에러 객체이고, 그다음에 순서대로 요청, 응답, next 객체입니다. next()를 호출하지 않으면 파이프라인은 종료되고 이후의 라우트 핸들러와 미들웨어는 호출하지 않습니
●
다. next()를 호출하지 않으면 클라이언트에 res.send, res.json, res.render 등 응답을 보내야 합니 다. 응답조차 보내지 않으면 클라이언트는 계속 대기하다가 마침내 타임아웃에 걸릴 겁니다. next()를 호출했다면 일반적으로 클라이언트에 응답을 보내지 말아야 합니다. next()를 호출하고 응답까지 보
●
낸다면, 이후 미들웨어나 라우트 핸들러는 실행은 되지만 이들이 보내는 클라이언트 응답은 모두 무시됩니다.
정말 단순한 미들웨어를 만들어 실험해봅시다. app.use(function(req, res, next){ console.log('processing request for "' + req.url + '"....'); next();
156 한 권으로 끝내는 Node & Express
}); app.use(function(req, res, next){ console.log('terminating request'); res.send('thanks for playing!'); // next()를 호출하지 않았으므로 요청은 여기서 종료됩니다. }); app.use(function(req, res, next){ console.log('whoops, i\'ll never get called!'); });
위 예제에는 미들웨어가 세 개 있습니다. 첫 번째 미들웨어는 메시지를 콘솔에 기록한 다음 next ( )를 호출해서 요청을 다음 미들웨어로 보냅니다. 두 번째 미들웨어는 요청을 실제로 처
리합니다. 하지만 두 번째 미들웨어에 res.send를 생략했으므로 어떤 응답도 클라이언트에게 반환하지 않습니다. 결국 클라이언트는 타임아웃에 걸릴 겁니다. 모든 요청이 이전 미들웨어에 서 종료됐으므로 마지막 미들웨어는 실행되지 않습니다. 이제 더 복잡하고 완전한 예제를 봅시다. 이 예제는 route-example.js라는 이름으로 저장소에 있습니다.1 var app = require('express')(); app.use(function(req, res, next){ console.log('\n\nALLWAYS'); next(); }); app.get('/a', function(req, res){ console.log('/a: route terminated'); res.send('a'); }); app.get('/a', function(req, res){ console.log('/a: never called'); }); app.get('/b', function(req, res, next){ console.log('/b: route not terminated'); next();
1 역주_ 저장소 ch10 폴더에 들어 있지만, 프로젝트에 영향을 미치는 파일은 아니므로 ch10 브랜치에는 들어 있지 않습니다.
10장 - 미들웨어 157
}); app.use(function(req, res, next){ console.log('SOMETIMES'); next(); }); app.get('/b', function(req, res, next){ console.log('/b (part 2): error thrown' ); throw new Error('b failed'); }); app.use('/b', function(err, req, res, next){ console.log('/b error detected and passed on'); next(err); }); app.get('/c', function(err, req){ console.log('/c: error thrown'); throw new Error('c failed'); }); app.use('/c', function(err, req, res, next){ console.log('/c: error detected but not passed on'); next(); }); app.use(function(err, req, res, next){ console.log('unhandled error detected: ' + err.message); res.send('500 - server error'); }); app.use(function(req, res){ console.log('route not handled'); res.send('404 - not found'); }); app.listen(3000, function(){ console.log('listening on 3000'); });
이 예제를 실행하기 전에 결과를 상상해보십시오. 결과가 다른 라우트는 뭘까요? 클라이언트 에는 뭐가 보일까요? 콘솔에는 어떤 메시지가 기록될까요? 위 문제를 모두 정확히 맞혔다면,
158 한 권으로 끝내는 Node & Express
CHAPTER
11
이메일 보내기
이메일은 웹사이트가 세계와 교류하는 기본적인 방법 중 하나입니다. 사용자 등록과 비밀번호 리셋 지침, 광고 이메일, 문제 알림 등, 이메일을 보내는 기능은 아주 중요한 것입니다. 노드나 익스프레스는 기본적으로 이메일 전송을 지원하지 않으므로 타사 모듈을 써야 합니 다. 필자는 안드리스 레인만Andris Reinman의 노드메일러Nodemailer ( https://npmjs.org/package/ nodemailer )를 추천합니다. 노드메일러 설정에 들어가기 전에 이메일의 기본에 대해 알아봅
시다.
11.1. SMTP, MSA, MTA 가장 기본적인 이메일 전송은 단순 메일 전송 프로토콜Simple Mail Transfer Protocol ( SMTP )입니다.
SMTP를 통해 이메일을 직접 수신자의 메일 서버로 보낼 수 있긴 하지만, 일반적으로는 좋은 생각이 아닙니다. 구글이나 야후! 같은 ‘신뢰할 수 있는 발신자’가 아니면 이메일이 스팸함으로 직행할 수 있으니까요. 메일 발송 에이전트Mail Submission Agent (MSA )를 사용하면 신뢰할 수 있는 채널을 통해 메일을 배달하므로 스팸으로 분류될 확률이 줄어듭니다. MSA는 일시적인 정지나 반송 이메일도 처리합니다. 마지막은 이메일을 마지막 목적지로 실제 전송하는 서비스인 메일 전송 에이전트Mail Transfer Agent (MTA )입니다. 이 책에서 MSA, MTA, ‘SMTP 서버’는 사실 동등 합니다.
11장 - 이메일 보내기 167
우선 MSA에 접근할 수 있어야 합니다. 가장 쉬운 방법은 지메일, 핫메일, 아이클라우드, 센 드그리드SendGrid, 야후! 같은 무료 이메일 서비스로 시작하는 겁니다. 하지만 이는 일시적인 해 결책입니다. 무료 서비스에는 제한이 있고(예를 들어 지메일은 24시간에 500개만 허용하고, 이메일당 수신자를 100명 까지만 지정할 수 있습니다), 개인 이메일이 노출됩니다. 발신자가
joe@meadowlarktravel.com로 보이게 지정할 수도 있지만, 이메일 헤더를 슬쩍 보기만 해 도 이 메일이 사실 joe@gmail.com를 통해 전달된 걸 알 수 있으니 아마추어처럼 보이기도 합 니다. 실무 서버로 넘어갈 준비가 되면 센드그리드나 아마존의 단순 이메일 서비스Simple Email Service
(SES ) 같은 전문 MSA로 바꿀 수 있습니다.
기업에서 일한다면 자체 MSA가 있을 수도 있습니다. IT 부서에 연락해서 자동 이메일 전송에 필요한 SMTP 전달relay이 가능한지 물어보십시오.
11.2. 이메일 받기 대부분의 웹사이트는 비밀번호 리셋이나 광고 이메일을 보내기만 하면 됩니다. 하지만 일부 애 플리케이션은 이메일을 받아야 하기도 합니다. 문제 추적issue tracking 시스템이 좋은 예입니다. 누 군가가 문제를 업데이트하면 시스템이 이메일을 발송하고, 그 이메일에 응답하면 문제도 자동 으로 업데이트됩니다. 하지만 이메일 수신은 매우 복잡한 주제이고 이 책의 범위를 넘어섭니다. 이메일 수신 기능 이 필요하다면 안드리스 레인만Andris Reinman의 SimpleSMTP ( http://bit.ly/simplesmtp )나
Haraka (http://haraka.github.com )를 참고하십시오.
11.3. 이메일 헤더 이메일 메시지는 HTTP 요청과 마찬 가지로 헤더와 본문 두 부분으로 구성됩니다. 헤더에는 누가 보냈는지, 누구에게 보냈는지, 언제 받았는지, 주제가 무엇인지 등 이메일에 관한 정보가 들어 있습니다. 일반적으로 이메일 애플리케이션은 이러한 정보를 사용자에게 보여주지만, 헤
168 한 권으로 끝내는 Node & Express
더에는 훨씬 더 많은 정보가 들어 있습니다. 대부분의 이메일 클라이언트는 사용자가 헤더를 볼 수 있게 허용합니다. 아직 본 적이 없다면 한번 보길 권합니다. 헤더에는 이메일이 어떻게 전달됐는지에 관한 정보가 전부 들어 있습니다. 즉 이메일이 배달되면서 거쳐온 서버와 MTA 는 모두 헤더에 표시됩니다. 발신자가 헤더의 ‘from’ 주소를 임의로 설정할 수 있다는 걸 알고 놀라는 사람들이 종종 있습니 다. ‘from’ 주소를 실제 보내는 계정과 다른 것으로 지정하는 행위를 종종 ‘위장’spoofing이라 부르 기도 합니다. 이메일을 보낼 때 ‘from’ 주소를 빌 게이츠 <billg@microsoft.com>로 위장하 지 못하게 막을 수 있는 장치는 없습니다. 여러분에게 직접 위장해보라고 권하는 건 아닙니다. 그저 특정 헤더를 임의로 지정할 수 있다는 사실을 말하는 것뿐입니다. 때로는 이렇게 할 정당 한 이유가 있긴 하지만 악용해서는 안 됩니다. 하지만 이메일을 보낼 때는 반드시 ‘from ’ 주소가 있어야 합니다. 이는 자동으로 이메일 을 보낼 때 문제를 일으킬 수 있습니다. 반환 주소가 DO NOT REPLY <do-not-reply@
meadowlarktravel.com>로 지정된 이메일을 종종 받는 이유는 이 때문입니다. 발신자를 임의 로 지정하든, 메도라크 여행사 <info@meadowlarktravel.com>에서 자동 이메일을 보내든 그 건 여러분이 결정할 일입니다. 하지만 후자를 택했다면 <info@meadowlarktravel.com>에 들 어올 이메일에 응답할 준비는 해야 할 겁니다.
11.4. 이메일 형식 인터넷 초창기에는 모든 이메일이 단순한 ASCII 텍스트였습니다. 하지만 세상은 많이 변했고, 사람들은 이메일을 다른 언어로 보내길 원하며 형식이 지정된 텍스트, 이미지, 첨부 파일 등 상 상만 해도 끔찍한 것들을 같이 보내길 원합니다. 이제 문제가 복잡해집니다. 이메일 형식과 인 코딩에는 기술과 표준이 중구난방으로 뒤섞여 있으니까요. 다행히 이 난국을 우리가 직접 해결 할 필요는 없습니다. 노드메일러 복잡한 일을 처리해줄 겁니다. 이메일에는 평문(유니코드)과 HTML 두 가지가 있습니다. 최신 이메일 애플리케이션은 거의 모두 HTML 이메일을 지원하므로 이메일을 HTML 형식으 로 보내도 대개 안전합니다. 하지만 HTML 이메일을 멀리하는 ‘텍스트 순수주의자’가 여전히
11장 - 이메일 보내기 169
존재하므로 이메일에는 항상 텍스트와 HTML을 모두 쓰길 권합니다. 둘 다 쓰기가 싫다면, 노 드메일러에는 HTML에서 평문 버전을 자동으로 생성하는 기능이 있습니다.
11.5. HTML 이메일 HTML 이메일은 책 한 권을 다 채울 만큼 방대한 주제입니다. 불행히도 HTML 이메일은 사 이트에 사용할 HTML을 작성하는 것만큼 단순하지는 않습니다. 대부분의 메일 클라이언트는
HTML의 극히 일부만 지원합니다. 이메일에 HTML을 쓸 때는 마치 1996년을 사는 듯한 기분 으로 만들어야 합니다. 재미있는 일은 아니죠. 심지어 테이블 레이아웃을 써야 합니다(슬픈 음 악을 좀 틀어야겠네요).
HTML에서 브라우저 호환성 문제를 겪어봤다면 그게 얼마나 두통거리인지 잘 알 겁니다. 이 메일 호환성 문제는 훨씬 더 심각합니다. 다행히 도움이 될 것이 몇 가지 있습니다. 먼저, 메일침프MailChimp에서 HTML 이메일 작성에 관한 훌륭한 글(http://bit.ly/writing_ html_email )을 읽기를 권합니다. 이 글에는 기본이 잘 설명되어 있고 HTML 이메일을 작성할
때 염두에 두어야 할 것들을 다루고 있습니다. 다음은 실제로 시간을 줄여주는 HTML 이메일 보일러플레이트HTML Email Boilerplate ( http:// htmlemailboilerplate.com )입니다. 매우 잘 작성되었고 엄격히 테스트받은 HTML 이메일
템플릿입니다. 마지막으로, 테스트입니다. HTML 이메일을 어떻게 작성하는지 배웠고 HTML 이메일 보일러 플레이트를 사용하더라도, 당신이 보낸 이메일이 로터스 노츠 7Lotus Notes 71 (네, 아직도 쓰는 사 람이 있습니다)에서 망가지지 않는지 확인할 방법은 테스트뿐입니다. 이메일 딱 하나 테스트 하려고 30가지 메일 클라이언트를 설치해야 할까요? 설마요. 다행히 테스트를 대행해주는 훌 륭한 서비스인 리트머스Litmus (https://litmus.com/email-testing )가 있습니다. 리트머스는 결코 저렴한 서비스는 아닙니다. 한 달에 80불로 시작하니까요. 하지만 광고 이메일을 아주 많 이 보낸다면 이 서비스가 최선입니다.
1 역주_ 1989년 로터스가 개발한 이메일 클라이언트로, 7 버전은 2005년에 나왔습니다.
170 한 권으로 끝내는 Node & Express
CHAPTER
12
실무 관심사
이 시점에서 실무에 관해 설명하기 시작하는 게 너무 이르다 싶을 수도 있겠지만, 실무에 관해 일찍 생각해두면 시간을 많이 절약하고 두통거리도 줄일 수 있습니다. 사이트 오픈 날짜는 당 신이 알기도 전에 닥칠 테니까요. 이 장에서는 익스프레스가 여러 가지 실행 환경을 어떻게 지원하는지, 웹사이트를 확장하는 방 법, 웹사이트 모니터링 방법을 살펴봅니다. 테스트와 개발 목적으로 실무 환경을 시뮬레이트하 는 방법, 스트레스 테스트를 통해 실무 문제가 닥치기 전에 알아내는 방법도 배웁니다.
12.1. 실행 환경 익스프레스는 애플리케이션을 실무, 개발, 테스트 모드로 실행하는 실행 환경이란 개념을 지원 합니다. 사실 환경은 원하는 만큼 많이 만들 수 있습니다. 예를 들어 스테이징 환경, 훈련 환경 같은 환경을 만들 수 있습니다. 하지만 개발, 실무, 테스트가 ‘표준’ 환경이라는 건 염두에 두십 시오. 익스프레스, 커넥트, 타사 미들웨어는 환경에 따라 동작 방식이 다를 수 있습니다. 다시 말해 ‘스테이징’ 환경을 만든다고 할 때 실무 환경의 프로퍼티를 자동으로 상속할 수는 없습니 다. 따라서 실무, 개발, 테스트라는 표준에 머물길 권합니다. app.set ('env', 'production')을 호출해서 실행 환경을 지정할 수 있지만, 이렇게 하면 앱
이 상황과 상관없이 항상 그 환경에서 실행되므로 권장하지 않습니다. 더 심한 문제는, 이렇게
12장 - 실무 관심사 183
하면 한 환경에서 시작해 다른 환경으로 넘어갈 수도 있다는 겁니다. 환경 변수 NODE_ENV를 써서 실행 환경을 지정하는 편이 낫습니다. 앱에서 app.get ('env')을 호출해 모드를 보고하게 만듭시다. app.listen(app.get('port'), function() console.log( 'Express started in ' ' mode on http://localhost:' + '; press Ctrl-C to terminate.' });
{ + app.get('env') + app.get('port') + );
지금 서버를 시작한다면 개발 모드(development mode )라고 표시될 겁니다. 개발 모드는 아무것도 지정하지 않았을 때의 기본값입니다. 그럼 실무 모드로 바꿔봅시다.1 $ export NODE_ENV=production $ node meadowlark.js
유닉스/BSD 시스템이나 시그윈Cygwin에는 특정 명령어가 유효한 동안에만 환경을 지정하는 편 리한 문법이 있습니다. $ NODE_ENV=production node meadowlark.js
이 명령은 서버를 실무 모드로 실행하지만, 서버를 종료하면 NODE_ENV 환경 변수는 원래 값으 로 돌아갑니다.
NOTE_ 익스프레스를 실무 모드로 시작하면 구성 요소가 실무 모드에 적합하지 않다는 경고를 볼 수 있습 니다. 책의 예제를 따라왔다면 connect.session이 세션을 메모리에 저장하도록 설정되어 있을 텐데, 이 설정은 실무 환경에 적합하지 않습니다. 13장에서 데이터베이스에 저장하도록 바꾸고 나면 이 경고는 사라 질 겁니다.
1 역주_ OS X에서는 셸에서 export 명령을 쓸 수 없고 홈 디렉터리의 .bash_profile 파일에 export NODE_ENV=production 행을 추 가한 후 배시를 재시작해야 합니다. 윈도우에서는 export 대신 set을 씁니다.
184 한 권으로 끝내는 Node & Express
12.2. 환경별 설정 실행 환경을 바꾸기만 해서 크게 달라지는 건 없습니다. 익스프레스는 실무 모드에서 콘솔에 경고를 더 많이 기록하기는 합니다. 예를 들어 어떤 모듈이 폐기되었으며 이후 제거될 거라는 경고가 기록될 수 있습니다. 뷰 캐시가 기본값으로 활성화되기도 합니다(7장을 보십시오). 실행 환경은 원래 개발자 마음대로 사용하라고 만든 도구입니다. 즉 애플리케이션이 환경에 따 라 다르게 동작하는 방식을 쉽게 지정하라고 만든 겁니다. 한 가지 주의할 점은, 개발/테스트/ 실무 환경의 차이를 최소화하도록 노력해야 합니다. 즉 이 기능은 일종의 옵션으로 간주해야 합니다. 개발이나 테스트 환경이 실무 환경과 너무 많이 다르면 실무에서 다르게 동작할 가능 성이 그만큼 더 커지고, 애플리케이션에 결함이 있을 확률도 따라서 커집니다. 일부 차이는 피 할 수 없습니다. 예를 들어 앱이 데이터베이스에 매우 밀접하다면, 개발 과정에서부터 실무 데 이터베이스를 사용하다가 재앙을 초래하고 싶은 사람은 없을 겁니다. 이런 경우에는 환경별 설 정이 도움이 됩니다. 환경별 설정이 크게 달라도 악영향이 없는 영역 중 하나는 로그 수준 결정 입니다. 개발할 때는 로그를 많이 남겨야 하지만, 이 중 대부분은 실무에서는 불필요합니다. 그럼 애플리케이션에 로그 기능을 추가해봅시다. 개발 환경에서는 모건Morgan ( npm install --save morgan )을 쓸 겁니다. 모건은 로그에 색깔을 입혀 읽기 쉽게 바꿉니다. 실무에서는 익 스프레스 로거express-logger (npm install --save express-logger )를 쓸 겁니다. 로거는 24시 간마다 로그를 복사하고 새로 시작해서 로그가 무한히 커지지 않게 막습니다. 애플리케이션 파 일에 로그 지원을 추가합시다. switch(app.get('env')){ case 'development': app.use(require('morgan')('dev')); break; case 'production': app.use(require('express-logger')({ path: __dirname + '/log/requests.log' })); break; }
로거를 테스트하려면 앞 절에서 설명한 대로 애플리케이션을 실무 모드로 실행하면 됩니다. 로그가 교체되는 것을 직접 보고 싶다면 node_modules/express-logger/logger.js 파일에
12장 - 실무 관심사 185
서 defaultInterval 변수를 24시간 대신 10초나 기타 원하는 시간으로 바꾸면 됩니다. 단,
node_modules에 들어 있는 패키지를 바꾸는 건 실험이나 연구 목적으로 임시로 바꾸는 것만 가능함을 기억하십시오.
NOTE_ 위 예제에서는 __dirname을 써서 요청 로그를 프로젝트 자체의 서브디렉터리에 저장했습니다. 이 방법을 택한다면 .gitignore 파일에 log도 추가하는 게 좋을 겁니다. 아니면 더 유닉스 같은 방법을 택해 서 아파치 기본값처럼 로그를 서브디렉터리 /var/log에 저장해도 됩니다.
환경에 밀접한 설정을 선택할 때는 신중해야 한다고 다시 한 번 강조합니다. 사이트를 오픈하 면 실무 인스턴스는 production 모드에서 동작한다는 점(동작해야 한다는 점)을 항상 염두에 두십시오. 개발 환경에 밀접한 설정을 수정할 때는 항상 그 결과가 실무의 QA에 어떤 영향 을 미칠지 먼저 생각해야 합니다. 13장에서 환경에 밀접한 설정에 관한 더 견고한 예제를 볼 겁니다.
12.3. 웹사이트 확장 최근에는 확장이라고 하면 보통 수직적 확장scaling up 또는 수평적 확장scaling out을 말합니다. 수 직적 확장은 더 빠른 CPU, 더 나은 아키텍처, 더 많은 CPU 코어, 메모리 추가 등 서버 자체를 더 좋은 것으로 바꾸는 걸 말합니다. 수평적 확장은 단순히 서버를 더 늘리는 겁니다. 클라우드 컴퓨팅이 점점 인기를 얻고 어디서나 가상화를 이용하게 되면서 서버의 계산 능력은 점점 의미 를 잃는 반면, 수평적 확장은 웹사이트를 필요에 맞게 확장하려 할 때 가장 비용대비 효과가 좋 은 방법입니다. 노드 웹사이트를 개발할 때는 항상 수평적 확장 가능성을 고려해야 합니다. 설령 애플리케이션 이 항상 매우 제한된 사용자만 사용하는 인트라넷 애플리케이션처럼 아주 작고, 확장해야 할 필요를 전혀 느낄 수 없다 하더라도 항상 대비해두는 게 좋은 습관입니다. 무엇보다도, 당신이 다음에 만들 노드 프로젝트가 트위터를 계승할 만큼 인기를 얻을 수도 있으니, 그럴 때는 수평 적 확장이 필수입니다. 다행히 노드는 수평적 확장을 아주 잘 지원하며 애플리케이션을 개발할 때 확장을 염두에 두는 게 그리 어려운 일은 아닙니다.
186 한 권으로 끝내는 Node & Express
CHAPTER
13
지속성
정말 단순한 형태가 아닌 한, 웹사이트나 웹 애플리케이션에는 일종의 지속성persistence이 필요합 니다. 지속성이란, 데이터를 불안정한 메모리보다 더 영구적인 곳에 저장해서 서버가 충돌하거 나, 전기가 나가거나, 업그레이드되거나, 이전한다 해도 데이터가 유지되도록 하는 겁니다. 이 장에서는 지속성을 유지할 수 있는 옵션을 몇 가지 설명하며, 문서 데이터베이스에 중점을 둡 니다.
13.1. 파일시스템 지속성 데이터를 단순히 ‘플랫 파일’에 보관하기만 해도 지속성을 얻을 수 있습니다. ‘플랫’이라는 표현 은 이런 파일은 내재적 구조가 없고 그저 바이트의 연속이기 때문입니다. 노드는 fs filesystem 모 듈을 통해 파일시스템 지속성을 제공합니다. 파일시스템을 통한 지속성에는 몇 가지 결점이 있습니다. 가장 큰 문제는 확장이 어렵다는 겁 니다. 트래픽이 늘어나면서 서버 하나로는 감당하기 어려워지는 순간, 파일시스템 지속성은 문 제를 일으키기 시작합니다(모든 서버가 파일시스템 하나를 공유한다면 예외입니다). 또한, 플 랫 파일에는 내재적 구조가 없으므로 데이터를 검색하고, 정렬하고, 필터링하는 부담을 모두 애플리케이션이 져야 합니다. 이런 이유로, 데이터를 저장할 때는 파일시스템보다 데이터베이 스를 우선시해야 합니다. 한 가지 예외는 이미지, 오디오, 비디오 파일 같은 바이너리 파일을
13장 - 지속성 199
저장할 때입니다. 이런 데이터 타입을 처리할 수 있는 데이터베이스가 여럿 있긴 하지만, 파일 시스템이 더 효율적입니다. 그럼에도 바이너리 파일에 관한 정보는 보통 데이터베이스에 저장 해서 검색, 정렬, 필터링할 수 있게 합니다. 바이너리 데이터를 저장하더라도, 파일시스템 스토리지에는 여전히 확장성 문제가 있음을 염 두에 두십시오. 호스팅 서비스에서 공유 파일시스템을 제공하지 않는다면(보통 제공하지 않습 니다), 바이너리 파일을 데이터베이스에 저장하거나 아마존 S3나 마이크로소프트 애저 스토리 지 같은 클라우드 스토리지 서비스에 저장해야 합니다. 데이터베이스에 저장할 때는 데이터베 이스가 과부하로 멈추지 않도록 설정을 좀 해야 합니다. 주의할 점은 이 정도로 끝내고 이제 노드의 파일시스템 지원에 대해 알아봅시다. 8장에서 만들 었던 휴가 사진 콘테스트 예제를 다시 쓸 겁니다. 애플리케이션 파일에서 폼을 처리하는 핸들 러를 수정합니다. 이 코드 앞에는 var fs = require (fs )가 반드시 있어야 합니다. // 디렉터리가 존재하는지 확인하고 없으면 만듭니다. var dataDir = __dirname + '/data'; var vacationPhotoDir = dataDir + '/vacation-photo'; fs.existsSync(dataDir) || fs.mkdirSync(dataDir); fs.existsSync(vacationPhotoDir) || fs.mkdirSync(vacationPhotoDir); function saveContestEntry(contestName, email, year, month, photoPath){ // TODO 이 부분은 나중에 완성합니다. } app.post('/contest/vacation-photo/:year/:month', function(req, res){ var form = new formidable.IncomingForm(); form.parse(req, function(err, fields, files){ if(err) return res.redirect(303, '/error'); if(err) { res.session.flash = { type: 'danger', intro: 'Oops!', message: 'There was an error processing your submission. ' + 'Please try again.', }; return res.redirect(303, '/contest/vacation-photo');
200 한 권으로 끝내는 Node & Express
} var photo = files.photo; var dir = vacationPhotoDir + '/' + Date.now(); var path = dir + '/' + photo.name; fs.mkdirSync(dir); fs.renameSync(photo.path, dir + '/' + photo.name); saveContestEntry('vacation-photo', fields.email, req.params.year, req.params.month, path); req.session.flash = { type: 'success', intro: 'Good luck!', message: 'You have been entered into the contest.', }; return res.redirect(303, '/contest/vacation-photo/entries'); }); });
하는 일이 많으니 나눠서 살펴봅시다. 먼저, 올린 파일을 저장할 디렉터리가 존재하는지 확인 하고 없으면 만듭니다. data 디렉터리를 .gitignore 파일에 추가해서 실수로 업로드 파일을 커 밋하지 않게 막는 게 좋습니다. 포미더블의 IncomingForm 인스턴스를 새로 만들고, req 객체 를 넘겨서 parse 메서드를 호출합니다. 콜백에는 필드와 업로드된 파일이 모두 들어 있습니다. 업로드 필드를 photo라고 지정했으므로 업로드된 파일의 정보는 files.photo 객체에 들어 있 습니다. 사용자 두 명이 portland.jpg 파일을 올릴 수도 있으므로, 충돌을 막으려면 파일 이름 을 그대로 써선 안 됩니다. 타임스탬프에 따라 고유한 디렉터리를 만들면 이 문제를 피할 수 있 습니다. 두 사용자가 portland.jpg 파일을 동시에, 그것도 밀리초 단위로 동시에 올릴 일은 없 을 테니까요. 이제 업로드된 파일을 우리가 지정한 이름으로 바꿉니다(옮깁니다). 포미더블에 서 지정한 임시 이름은 path 프로퍼티에 들어 있습니다. 마지막으로, 사용자가 올린 파일과 이메일 주소 및 전송 연월을 연결해야 합니다. 이 정보를 파 일이나 디렉터리 이름에 담을 수도 있지만, 데이터베이스에 담기로 합니다. 그런데 그 방법은 아직 배우지 않았으니, 그 기능은 saveContestEntry 함수에 담기로 하고 이 장 후반에 함수를 완성하겠습니다.
13장 - 지속성 201
NOTE_ 일반적으로 사용자가 올린 것은 아무것도 믿어선 안 됩니다. 사용자를 믿으면 웹사이트 공격에 문 을 열어주게 됩니다. 예를 들어 악의적인 사용자가 해로운 실행 파일을 만들어 확장자만 .jpg로 바꿔 올릴 수 도 있습니다. 나중에 어떻게든 그 파일을 실행할 기회가 생기길 기대하면서 말입니다. 마찬가지로, 브라우저 가 제공한 name 프로퍼티를 그대로 써서 파일 이름을 정하는 것도 조금 위험합니다. 파일 이름에 특수문자 를 써서 악용할 수 있으니까요. 우리는 파일 이름은 무작위로 정하고, 확장자만(영문과 숫자로만 구성된 걸 확인하고) 그대로 써서 이 코드를 완벽히 안전하게 만들겠습니다.
13.2. 클라우드 지속성 클라우드 스토리지는 점점 더 인기를 얻고 있고, 필자 역시 저렴하고 사용하기 쉬운 이 서비스 의 장점을 활용하길 적극 권합니다. 먼저 아마존 S3 계정에 파일을 저장하기가 얼마나 쉬운지 예를 보이겠습니다. var filename = 'customerUpload.jpg'; aws.putObject({ ACL: 'private', Bucket: 'uploads', Key: filename, Body: fs.readFileSync(__dirname + '/tmp/ + filename) });
정보가 더 필요하면 AWS SDK 문서(http://aws.amazon.com/sdkfornodejs )를 보십시오. 마이크로소프트 애저에서는 이렇게 합니다. var filename = 'customerUpload.jpg'; var blobService = azure.createBlobService(); blobService.putBlockBlobFromFile('uploads', filename, __dirname + '/tmp/' + filename);
정보가 더 필요하면 마이크로소프트 애저 문서(http://bit.ly/azure_documentation )를 보 십시오.
202 한 권으로 끝내는 Node & Express
CHAPTER
14
라우팅
라우팅은 웹사이트나 웹 서비스에서 가장 중요한 요소 중 하나입니다. 다행히 익스프레스의 라 우팅은 단순하고 유연하며 견고하게 만들어졌습니다. 라우팅은 URL이나 HTTP에 명시된 요 청을, 그 요청을 처리할 코드로 돌리는 메커니즘입니다. 이미 살펴보았듯, 파일 기반으로 이루 어지는 라우팅은 매우 단순했습니다. 웹사이트에 foo/about.html 파일을 올리면 브라우저에서 /foo/about.html 경로로 접근하는 식이었습니다. 이것은 단순하지만 유연하지는 않습니다. 그 리고 아직 눈치채지 못했다면 말이지만, URL에 ‘HTML’을 쓰는 일 자체가 상당히 구습입니다. 익스프레스 라우팅의 기술적인 측면을 설명하기 전에, 먼저 정보 구조information architecture (IA )라 는 개념에 대해 이해해야 합니다. IA는 콘텐츠의 개념적 구성을 말합니다. 라우팅에 관해 생각 하기 전에 먼저 확장성 있는(지나치게 복잡하지는 않은) IA를 계획하면 이후 과정에 크나큰 도움이 됩니다.
IA에 관해 가장 현명하고, 시간이 지나도 빛이 바래지 않을 고전이 있다면 그건 인터넷을 사실 상 창시한 팀 버너스-리의 에세이입니다. http://www.w3.org/Provider/Style/URI.html에 서 팀 버너스-리의 에세이를 읽을 수 있습니다(꼭 읽으십시오). 이 글은 1998년에 쓰였지만, 시간이 많이 지났다는 사실은 잠시 접어둬도 됩니다. 인터넷 기술에 관해 1998년에 쓰여진 글 이 오늘에 와서도 여전히 진리인 경우는 극히 드뭅니다. 이 에세이 중에서 우리가 감당해야 할 ‘고귀한 책임’ 부분을 발췌했습니다.
14장 - 라우팅 217
2년이 지나도, 20년이 흘러도, 200년 뒤에도 여전히 쓸 수 있는 URI를 만드는 게 웹마스터 의 책임이다. 이렇게 하려면 숙고하고, 조직하고, 헌신해야 한다. 팀 버너스-리
필자는 다른 공학 분야와 마찬가지로 웹 디자인에도 전문가 면허가 있어야 한다고 생각합니다. 결과에 책임을 져야 하는 전문가 말입니다(날카로운 독자라면 이 글의 URL이 .html로 끝난다 는 사실에 쓴웃음을 지을지도 모르겠습니다). 비유하자면(슬프게도 젊은 독자들은 무슨 말인지 잘 모르겠지만), 당신이 좋아하는 도서관에 서 2년마다 듀이의 십진분류법을 완전히 바꾼다고 생각해보십시오. 어느 날 도서관에 가면 원하는 책을 하나도 찾을 수 없게 되었을 겁니다. URL 구조를 재설계하 면 이와 똑같은 일이 벌어집니다.
URL에 대해 진지하게 생각해보십시오. 20년 뒤에도 의미 있는 URL이 있을까요? (200년은 조금 과장일 수 있습니다. 그때까지 URL이 쓰이기나 할지도 아무도 모르니까요. 그래도 그렇 게 먼 미래까지 대비한다는 생각은 존경합니다.) 주의 깊게 콘텐츠 명세를 만들고, 논리적으로 분류하십시오. 그리고 스스로를 코너에 몰지 마십시오. 이 작업은 과학인 동시에 예술입니다. 가장 중요한 일은 URL을 디자인할 때 다른 사람과 협력하는 걸 겁니다. 설령 주변에 당신만 한 정보 전문가가 없다 해도, 사람들이 같은 콘텐츠를 다른 관점으로 보는 걸 알고 놀랄 수도 있 습니다. IA를 모든 사람이 공감하게 만들라는 건 아닙니다. 그건 거의 불가능하니까요. 하지만 여러 관점에서 문제를 보면 더 나은 아이디어를 얻을 수 있고, IA에 있는 흠을 찾기 쉽습니다. 오래 지속될 IA를 만드는 데 도움이 될 만한 몇 가지 제안입니다. | 기술적인 세부 사항을 절대 URL에 노출하지 마십시오 | 웹사이트 URL이 .asp로 끝나는 걸 보고 구제불능으로 구식이라고 생각한 적이 있지 않습니까? 하지만 ASP도 한때는 첨단 기술이었습니다. 이런 말을 하기는 고통스럽지만, 자바스크립트도,
JSON도, 노드도, 익스프레스도 언젠가 사라질 겁니다. 가능하면 오래갔으면 좋겠지만, 시간 은 기술에 관대하지 않습니다. | URL에 무의미한 정보를 두지 마십시오 |
URL의 모든 단어를 주의 깊게 생각하고 의미 없는 단어는 빼버리십시오. 예를 들어 필자는 웹
218 한 권으로 끝내는 Node & Express
사이트에서 URL에 home이란 단어를 볼 때마다 민망해집니다. 루트 URL이 곧 홈페이지입니 다. /home/directions나 /home/contact 같은 URL도 필요 없습니다. | 필요 없이 긴 URL은 피하십시오 | 다른 모든 점이 같다면, 짧은 URL이 긴 URL보다 낫습니다. 하지만 URL을 너무 줄여서 명료 함을 잃거나 SEO를 포기해서는 안 됩니다. 약어를 쓰고 싶겠지만 주의 깊게 생각해야 합니다. 매우 널리 쓰이고 아무나 이해할 수 있는 약어만 쓰십시오. | 단어 구분자는 통일하십시오 | 단어 구분자에는 보통 하이픈(-)을 쓰며, 밑줄을 쓰는 경우는 그보다 적습니다. 하이픈은 일 반적으로 밑줄보다 보기 좋게 느껴지며, 대부분의 SEO 전문가들도 하이픈을 추천합니다. 하 이픈을 택했든 밑줄을 택했든, 일관성 있게 쓰십시오. | 공백 문자나 타이핑할 수 없는 문자는 절대 쓰지 마십시오 |
URL에 공백은 권장할 수 없습니다. 공백은 보통 + 기호로 치환되며 혼란스러워 보입니다. 타 이핑할 수 없는 문자를 쓰지 말아야 하는 건 당연합니다. 필자는 URL에 영/숫자와 하이픈, 밑 줄 이외의 문자는 쓰지 않길 강하게 권합니다. 당시에는 기발해 보일 수 있지만, ‘기발함’이 시 간이 지나도 유지되는 건 아닙니다. 물론 웹사이트 사용자가 영어를 사용하지 않는다면 퍼센트 코드를 써서 알파벳이 아닌 문자를 사용할 수 있겠지만, 지역화하려 할 때는 두통거리가 될 겁 니다. | URL에는 소문자만 쓰십시오 | 이건 논쟁의 소지가 좀 있습니다. URL에 대소문자를 섞어 쓰기를 추천하는 사람들이 있으니까 요. 필자는 이 종교적 논쟁에 휘말리고 싶은 생각은 없지만, 소문자는 코드를 통해 자동으로 생 성할 수 있다는 장점을 들고 싶습니다. 웹사이트 전체를 훑으며 수천 개의 링크를 검사하거나 문자열 비교를 해본 일이 있다면 이 장점의 진가를 알아볼 수 있을 겁니다. 필자 개인적으로는 소문자 URL이 보기도 더 좋다고 생각하지만, 이 문제는 독자 여러분의 결정에 맡깁니다.
14장 - 라우팅 219
14.1. 라우트와 SEO 웹사이트가 검색엔진에 걸리게 하려면(틀림없이 그렇겠죠) SEO에 대해 생각해야 하고, URL 이 SEO에 미치는 영향도 생각해야 합니다. 특히 매우 중요한 키워드가 있고 상식적이기도 하 다면, 그 키워드를 URL에 넣는 것도 좋습니다. 예를 들어 메도라크 여행사에는 오리건 해안 휴 가 코스가 몇 가지 있습니다. 우리는 이 휴가 코스의 검색엔진 순위를 높이기 위해 문자열 ‘오 리건 해안’을 타이틀과 헤더, 본문, 메타 정보에 넣었고 URL은 /vacations/oregon-coast로 시 작하게 만들었습니다. 맨저니터 휴가 패키지는 /vacations/oregon-coast/manzanita에 있습니 다. URL 길이를 줄이려고 /vacations/manzanita로 정했다면 검색엔진 순위가 떨어질 겁니다. 그렇긴 하지만, 검색 순위를 올리려고 키워드들을 URL에 무차별로 남발하는 시도는 하지 마 십시오. 소용없습니다. 예를 들어 맨저니터 휴가 패키지 URL을 /vacations/oregon-coast-
portland-and-hood-river/oregon-coast/manzanita로 바꿔서 ‘오리건 해안’이 한 번 더 나오 고, ‘포틀랜드’나 ‘후드 강’ 키워드에도 동시에 대응하려는 시도는 잘못 생각하는 겁니다. 좋은
IA가 아닐뿐더러, 역효과가 날 겁니다.
14.2. 서브도메인 서브도메인은 경로만큼이나 라우팅에 많이 쓰입니다. 서브도메인은 애플리케이션에서 꽤 많이 다른 부분, 예를 들어 REST API (api.meadowlarktravel.com )나 관리자 인터페이스(admin. meadowlarktravel.com ) 등에 쓰는 게 가장 좋습니다. 기술적인 이유로 서브도메인을 쓰기도
합니다. 예를 들어 사이트 대부분을 익스프레스로 만들지만, 블로그는 워드프레스를 이용하기 로 했다면, 블로그 URL은 blog.meadowlarktravel.com이 더 좋을 겁니다(더 나은 해결책은 엔진X 같은 프록시 서버를 쓰는 겁니다). 보통 서브도메인을 써서 콘텐츠를 분리하면 SEO에 도 영향이 있으므로, 가능하면 서브도메인은 사이트에서 검색과 무관한 영역, 예를 들어 관리 자 영역이나 API 등에 쓰는 게 좋습니다. 이걸 염두에 두고, SEO 계획에 중요한 콘텐츠에 서 브도메인을 쓰기 전에 다른 옵션은 없는지 확인하십시오. 익스프레스의 라우팅 메커니즘은 기본적으로 서브도메인을 고려하지 않습니다. 즉 app.get (/ about ) 에서 http://meadowlarktravel.com/about과 http://www.meadowlarktravel.com/
220 한 권으로 끝내는 Node & Express
CHAPTER
15
REST API와 JSON
여태까지 우리는 브라우저가 ‘소비’하는 웹사이트를 디자인했습니다. 이제 관심을 돌려 데이터 와 기능을 다른 프로그램에게 제공해봅시다. 인터넷은 독야청청하는 웹사이트의 집합에서 벗 어나 점점 더 진정한 웹으로 바뀌고 있습니다. 모든 웹사이트가 자유롭게 서로 통신하며 사용 자에게 풍부한 경험을 제공하는 그런 웹 말입니다. 프로그래머의 꿈이 현실로 이루어진 겁니 다. 사람들이 인터넷을 사용하듯, 당신의 코드도 인터넷을 사용할 수 있게 되었습니다. 이 장에서는 우리가 만든 앱에 웹 서비스를 추가할 겁니다(웹 서버와 웹 서비스가 같은 애플리 케이션에 공존하지 못할 이유는 없습니다). ‘웹 서비스’란 용어는 HTTP를 통해 접근할 수 있 애플리케이션 프로그래밍 인터페이스(API )를 가리키는 일반적인 용어입니다. 웹 서비스라는 아이디어가 나타난 지는 좀 됐지만, 최근까지는 답답하고, 구시대적이고, 지나치게 복잡한 기 술에 의존해야 했습니다. 그런 기술(SOAP나 WSDL )을 사용하는 시스템도 여전히 존재하고, 노드 패키지 중에는 그런 시스템과 상호작용하도록 돕는 패키지도 있습니다. 여기서 그런 패키 지를 설명하지는 않을 겁니다. 대신 소위 RESTful 서비스, 훨씬 더 단순한 서비스를 제공하는 데 초점을 맞추겠습니다. 두문자어 REST는 ‘표현적인 상태 전송 representational state transfer’이며, 문법적으로는 좀 이상해 보이 는 ‘RESTful’이란 단어는 REST 원칙을 지키는 웹 서비스를 가리킬 때 쓰는 말입니다. REST의 공식 설명은 복잡하고 정규 컴퓨터과학 에도 한 다리 걸쳐야 하지만, 기본적으로 REST는 클라 이언트와 서버의 상태 없는 연결을 뜻합니다. REST의 공식 정의에는 서비스가 캐시될 수 있어 야 하며 각 서비스는 계층구조를 이룰 수 있어야 한다는 문구도 있습니다(즉 REST API를 사
15장 - REST API와 JSON 231
용 중이라면 그 바탕에 다른 REST API가 있을 수도 있습니다). 현실적인 관점에서, HTTP의 제약 때문에 사실 RESTful하지 않은 API를 만들기가 더 어렵습 니다. 예를 들어 그런 API를 만들려면 우선 ‘상태’를 직접 구현해야 하니까요. 문자 그대로 시 작이 반이군요. 우리는 메도라크 여행사 웹사이트에 REST API를 추가할 겁니다. 메도라크 여행사에서는 오리 건 여행을 권장하기 위해 오리건의 명소 데이터베이스를 만들어두고 있는데, 이 데이터베이스 에는 흥미로운 역사적 사실들이 많이 들어 있습니다. API를 사용하면 방문자가 오리건을 여행 할 때 가이드가 될 스마트폰 또는 태블릿용 애플리케이션을 만들 수 있습니다. 장치가 현재 위 치를 인식한다면, 흥미로운 장소 가까운 곳에 있을 때 알려줄 수도 있을 겁니다. 데이터베이스 는 계속 늘어날 수 있어야 하므로 API에서는 주요 지형지물과 명소를 추가하는 것도 지원합니 다(악용을 막으려면 검증 과정을 거쳐야 하겠죠).
15.1. JSON과 XML API를 제공하려면 공통 언어가 있어야 합니다. 통신 중 일부분은 이미 정해져 있습니다. 서버 와 통신할 때는 반드시 HTTP를 사용해야 합니다. 하지만 그 외에는 어떤 데이터 언어든 자 유롭게 쓸 수 있습니다. XML은 전통적으로 널리 쓰였고 현재도 중요한 마크업 언어입니다.
XML이 그렇게 복잡한 건 아니지만, 더글러스 크록퍼드는 더 경량화할 수 있다고 보고 자바스 크립트 객체 표기법(JSON )을 만들었습니다. JSON은 자바스크립트와 매우 잘 어울릴 뿐 아 니라, 일반적으로 XML에 비해 하드코딩하기 쉽다는 장점도 있습니다(JSON은 자바스크립트 와 잘 어울리긴 하지만 자바스크립트 전용은 아니며 어떤 언어로든 쉽게 파싱할 수 있는 형식 입니다). 필자는 거의 모든 애플리케이션에서 XML보다 JSON을 사용합니다. 자바스크립트 지원도 더 좋고, 더 단순하며 가벼운 형식이기 때문입니다. JSON을 주로 사용하되, 기존 시스템에서 앱 과 통신하기 위해 XML이 필요할 때만 XML을 쓰길 권합니다.
232 한 권으로 끝내는 Node & Express
15.2. API API에 바로 뛰어들기보다는 먼저 계획을 세워야 합니다. API에는 다음 기능이 필요합니다. GET /api/attractions
명소attraction를 가져옵니다. 쿼리스트링 매개변수로 lat, lng, radius1를 받고 명소 목록을 반환 합니다. GET /api/attraction/:id
ID로 명소를 찾아 반환합니다. POST /api/attraction
요청 본문에서 lat, lng, name, description, email을 받습니다. 새로 추가된 명소는 검증 대 기열에 들어갑니다. PUT /api/attraction/:id
기존 명소를 업데이트합니다. 명소 ID와 lat, lng, name, description, email을 받습니다. 업데이트는 검증 대기열에 들어갑니다. PUT /api/attraction/:id
명소를 삭제합니다. 명소 ID와 email, reason을 받습니다. 삭제는 검증 대기열에 들어갑니다.
API를 만드는 방법은 여러 가지입니다. 여기서는 HTTP 메서드와 경로를 조합해 API 호출을 구별하고, 데이터를 받을 때는 쿼리스트링과 요청 본문 매개변수를 모두 사용합니다. 같은 방 법을 쓰면서 경로만 다르게, 예를 들어 /api/attractions/delete 같은 경로를 쓸 수도 있습니다.2 데이터를 쿼리스트링과 요청 본문 매개변수 모두에서 받지 않고 한쪽에서만 받아도 됩니다. 예를 들어 필요한 정보를 쿼리스트링 대신 GET /api/attractions/:lat/:lng/:radius처럼
URL에서 받을 수도 있습니다. 명소에 관한 설명 등 큰 데이터는 요청 본문으로 받아서 URL이 너무 길어지지 않게 하길 권합니다.
1 역주_ 각각 위도(latitude), 경도(longitude), 반경(radius)을 뜻합니다. 2 클라이언트에서 다른 HTTP 메서드를 사용한다면 https://github.com/expressjs/method-override에서 다른 HTTP 메서드로 ‘속이는’ 방법을 읽어보십시오.
15장 - REST API와 JSON 233
NOTE_ 뭔가 만들 때는 POST, 뭔가 업데이트할 때는 PUT을 쓰는 게 표준 관습입니다. 이들 동사의 영어 의미만으로는 이 차이점을 알 수 없으므로 경로를 통해 두 조작을 구별하는 것도 생각해볼 만합니다.
이 책에서는 이 함수들 중 명소를 추가하고, 가져오고, 나열하는 세 가지만 만들겠습니다. 책 소스를 내려받으면 모든 프로그램을 볼 수 있습니다.
15.3. API 에러 보고 HTTP API의 에러 보고는 보통 HTTP 상태 코드를 통해 이뤄집니다. 요청이 200 (성공)을 반 환하면 클라이언트 요청이 성공했음을 알게 됩니다. 요청이 500 (내부 서버 오류)을 반환하면 요청이 실패한 겁니다. 하지만 대부분의 애플리케이션에서 모든 것을 단순히 ‘성공’이나 ‘실패’ 로 분류할 수는 없습니다. 그럴 필요가 없을 수도 있고요. 예를 들어 ID로 뭔가 요청했는데, 그 런 ID가 존재하지 않는다면? 서버 에러는 아닙니다. 클라이언트에서 존재하지 않는 것을 요청 했을 뿐입니다. 일반적으로 에러는 다음 카테고리로 나눌 수 있습니다. | 심각한 에러 | 서버가 불안정하거나 알 수 없는 상태일 때 일어납니다. 보통 이런 에러는 예외 처리에 실패했 을 때 발생합니다. 심각한 에러에서 복구하는 안전한 방법은 서버 재시작뿐입니다. 이상적인 경우 대기 중인 요청이 모두 500 응답 코드를 받겠지만, 에러가 너무 심각하다면 서버가 전혀 응답할 수 없어서 모든 요청은 타임아웃될 겁니다. | 복구 가능한 서버 에러 | 복구 가능한 에러에는 서버 재시작이나 기타 엄청난 행동이 필요하지는 않습니다. 이런 에러는 데이터베이스 연결이 끊기는 등 서버에 예기치 못한 에러 조건이 생겼을 때 일어납니다. 문제 는 일시적일 수도, 지속적일 수도 있습니다. 이 상황에는 응답 코드 500이 적절합니다.
234 한 권으로 끝내는 Node & Express
CHAPTER
16
정적 콘텐츠
정적 콘텐츠란 앱에서 전송하는 자원 중 요청마다 바뀌지 않는 자원을 말합니다. 다음 콘텐츠 는 정적 콘텐츠라 할 수 있습니다. | 멀티미디어 | 이미지, 비디오, 오디오 파일입니다. 물론 이미지 파일을 즉석에서 생성하는 것도 가능하지만 (비디오나 오디오도 가능하지만 드뭅니다), 멀티미디어 자원은 대부분 정적입니다. | CSS | 설령 레스LESS, 사스Sass, 스타일러스Stylus 같은 추상 CSS 언어를 쓰더라도 브라우저가 받는 건 평 범한 CSS1이며 이는 정적 자원입니다. | 자바스크립트 | 서버에서 자바스크립트를 운영한다 해서 클라이언트 쪽 자바스크립트가 없으란 법은 없습니 다. 클라이언트 쪽 자바스크립트는 정적 자원으로 간주합니다. 물론 구분이 좀 모호해지기는 합니다. 예를 들어 서버와 클라이언트에서 공유하는 코드라면? 이 문제를 해결할 방법도 있긴 하지만, 일단 클라이언트에 전송되는 자바스크립트는 일반적으로 정적입니다.
1 자바스크립트를 써서 컴파일되지 않은 레스 파일을 브라우저에서 쓸 수는 있습니다. 하지만 성능이 떨어지므로 필자는 이 방법을 권하지 않습니다.
16장 - 정적 콘텐츠 245
| 바이너리 파일 | 여기에는 여러 가지가 포함됩니다. PDF, ZIP 파일, 설치 원본 등이죠.
HTML이 목록에 없는 걸 보고 의아할지도 모르겠습니다. 정적 HTML 페이지는 왜 빠진 걸까 요? 그런 파일이 있다면 정적 자원으로 봐도 되지만, 그렇다면 URL이 .html로 끝날 텐데 이건 ‘최신’ 경향이 아닙니다. 정적 HTML 파일을 .html 확장자 없이 전송하는 라우트를 만들 수도 있지만, 일반적으로 뷰를 만드는 편이 더 쉽습니다. 뷰에 꼭 동적 콘텐츠가 있어야 하는 것도 아니니까요.
API만 만들고 있다면 정적 자원이 없을 수도 있습니다. 그런 경우라면 이번 장을 건너뛰어도 좋습니다.
16.1. 성능에 관한 고려 사항 정적 자원을 처리하는 방법에 따라 웹사이트 성능이 크게 달라질 수 있으며, 특히 사이트에 멀 티미디어 파일이 아주 많다면 차이가 더 커질 겁니다. 성능을 생각할 때 먼저 고려해야 할 점은 요청 숫자 줄이기와 콘텐츠 크기 줄이기입니다. 둘 중에서, (HTTP ) 요청 숫자 줄이기가 더 중요하며, 모바일 환경에서는 특히 더 중요합니 다. 무선 통신망에서는 HTTP 요청을 만드는 것도 꽤 부담이 큽니다. 요청 숫자 줄이기에는 두 가지 방법이 있습니다. 하나는 자원을 묶는 것이고 다른 하나는 브라우저 캐싱입니다. 자원 결합은 주로 구조적이며 프론트엔드 관심사입니다. 가능한 한 작은 이미지를 결합해 스 프라이트 하나로 만들어야 합니다. 그리고 CSS에서 오프셋과 크기를 지정해 필요한 이미 지에 해당하는 부분만 표시합니다. 스프라이트를 만들 때는 무료 서비스인 스프라이트패드 SpritePad
( http://wearekiss.com/spritepad )를 적극 추천합니다. 스프라이트패드는 믿을 수
없을 정도로 쉽게 스프라이트를 생성하며, CSS까지 만들어줍니다. 이보다 더 쉬울 수는 없습 니다. 스프라이트패드의 무료 기능으로도 충분하겠지만, 스프라이트를 정말 많이 만들어야 한 다면 유료 계정에 가입해도 아깝지 않을 겁니다.
246 한 권으로 끝내는 Node & Express
브라우저 캐싱은 자주 쓰이는 정적 자원을 클라이언트의 브라우저에 저장해서 HTTP 요청 숫자 를 줄입니다. 브라우저는 캐싱을 가능한 한 자동화하려고 여러 가지로 노력하지만, 마법사는 아 닙니다. 정적 자원을 캐시하도록 하기 위해 할 수 있는 일이 많이 있고, 또 그렇게 해야 합니다. 마지막으로, 정적 자원의 크기를 줄여 성능을 올릴 수 있습니다. 일부 테크닉은 크기를 줄여 도 데이터를 잃지 않는 무손실이고, 일부 테크닉은 크기를 줄이면서 정적 자원 품질이 떨어지 는 손실입니다. 무손실 테크닉에는 자바스크립트와 CSS 최소화minification, PNG 이미지 최적화 등이 있습니다. 손실 테크닉은 대표적으로 JPEG와 비디오의 압축 수준을 올리는 것입니다. 이 장에서는 최소화에 대해 설명합니다. HTTP 요청을 줄이는 번들링에 대해서도 설명합니다.
NOTE_ 일반적으로 CDN을 쓸 때는 크로스 소스 자원 공유(CORS)에 대해 걱정하지 않아도 됩니다.
HTML에서 불러오는 외부 자원은 CORS 정책을 적용받지 않습니다. AJAX를 통해 불러오는 자원에만 CORS를 적용하면 됩니다(15장 참고).
16.2. 미래에도 안전한 웹사이트 웹사이트를 오픈하면 정적 자원을 반드시 인터넷 어딘가에 올려야 합니다. 동적 HTML을 생 성하는 서버와 동일한 한 서버에 올리는 일이 익숙할 겁니다. 우리는 여태까지 이 방법에 따 라 예제를 만들어왔습니다. 노드/익스프레스 서버는 node meadowlark.js 명령으로 시동되고
HTML과 정적 자원을 모두 전송합니다. 하지만 사이트 성능을 최대화(지금이 아니면 나중에 라도)하려면, 정적 자원을 콘텐츠 전송 네트워크content delivery network (CDN )에 올리는 편이 좋습 니다. CDN은 정적 자원 전송에 최적화된 서버입니다. CDN은 특별한 헤더(곧 배웁니다)를 써서 브라우저 캐싱을 활성화합니다. CDN은 지리적 최적화, 즉 정적 콘텐츠를 클라이언트와 지리적으로 가까운 서버에서 전송할 수도 있습니다. 물론 인터넷은 매우 빠르지만(거의 빛의 속도입니다), 그래도 뉴욕에서 수원으로 보내는 것보다는 서울에서 수원으로 보내는 게 더 빠 릅니다. 자원 하나하나로 보면 시간이 크게 줄어들지 않을 수도 있지만, 사용자 수를 곱하고 요 청 수를 곱하고 자원 수를 곱하면 차이가 커집니다. 그럴 만한 때가 됐을 때 정적 콘텐츠를 CDN에 옮겨 웹사이트를 ‘미래에도 안전하게’ 만들기는
16장 - 정적 콘텐츠 247
매우 쉬우므로, 항상 습관을 들이길 권합니다. 정적 자원의 추상 계층을 만들면, 스위치를 껐다 켜듯 쉽게 자원 위치를 바꿀 수 있습니다. 정적 자원은 대부분 HTML 뷰에서 참조합니다. CSS 파일은 <link> 요소, 자바스크립트 파 일은 <script>, 이미지는 <img>, 멀티미디어 파일도 대응하는 요소가 있습니다. CSS 도 background-image 프로퍼티 등에서 보통 정적 참조를 사용합니다. 마지막으로, 자바스크립트
에서도 정적 자원을 참조할 때가 있습니다. 예를 들어 자바스크립트 코드에서 동적으로 <img> 요소나 background-image 프로퍼티를 삽입하거나 수정할 때가 있습니다.
16.2.1. 정적 매핑 정적 자원을 캐싱이나 이동하기 쉽게 하는 전략의 핵심은 매핑 개념입니다. HTML을 작성할 때 정적 자원을 어디에서 서비스할지 밤새도록 치열하게 고민할 사람은 없습니다. 진짜 관심 이 있는 부분은 정적 자원의 논리적 구조입니다. 다시 말해, 중요한 건 후드 강 패키지의 사진 은 /img/vacations/hood-river에 있고, 맨저니터 사진은 /img/vacations/manzanita에 있다 는 겁니다. 따라서 정적 자원의 위치를 명시할 때 이 구조만 사용해도 쉽도록 하는 데 초점을 맞출 겁니다. HTML에 <img src='/img/meadowlark_logo.png' alt='Meadowlark Travel Logo'>이라고만 쓰고 싶지, <img src='//s3-us-west-2.amazonaws.com/meadowlark/img/ meadowlark_logo-3.png' alt='Meadowlark Travel Logo'>이라고 쓰고 싶어 하는 사람은
없을 테니까요. TIP
앞으로 정적 자원을 참조할 때는 ‘프로토콜 상대적 URL’을 쓸 겁니다. URL이 //로 시작할 뿐, http://나
https://로 시작하지 않는다는 뜻입니다. 이렇게 하면 브라우저에서 적절한 프로토콜을 쓸 수 있습니다. 사용 자가 보안 페이지를 보고 있다면 HTTPS를, 그렇지 않다면 HTTP를 쓸 겁니다. 물론 CDN에서는 반드시
HTTPS를 지원해야 하며, 필자는 아직 HTTPS를 지원하지 않는 CDN을 본 적이 없습니다.
결국 이건 매핑 문제입니다. 우리는 덜 고정적인 경로 /img/meadowlark_logo.png를 더 고정 적인 경로 //s3-us-west-2.amazonaws.com/meadowlark/img/meadowlark_logo-3.png로 매 핑하고 싶습니다. 또, 매핑을 자유롭게 바꿀 수 있었으면 합니다. 예를 들어 아마존 S3 계정에 가입하지 않았다면 이미지를 로컬(//meadowlarktravel.com/img/meadowlark_logo.png )에 두고 싶을 겁니다.
248 한 권으로 끝내는 Node & Express
CHAPTER
17
익스프레스에서 MVC 구현
기초에 대해 많이 설명했는데, 좀 벅차다고 느껴지더라도 겁먹지는 마십시오. 여러분만 그런 건 아닐 테니까요. 이 장에서는 여태까지 배운 것들을 정리하는 내용을 설명하겠습니다. 최근 지배적인 개발 패러다임은 모델-뷰-컨트롤러(MVC )입니다. 사실 MVC는 1970년대부 터 있었던 상당히 오래된 개념입니다. 웹 개발에 매우 적합하기에 다시 부활한 겁니다. 필자가 보기에 MVC의 가장 큰 장점은 프로젝트 개발 시간이 짧아진다는 겁니다. 예를 들어
PHP 개발자라도, MVC 프레임워크에 익숙하다면 .NET MVC 프로젝트에 놀랄 만큼 빠르게 적응할 수 있습니다. 어디서 무엇을 찾을지 알기만 하면 프로그래밍 언어는 그리 큰 장벽이 되 지 않습니다. MVC는 기능을 매우 잘 정의된 영역으로 분해해 소프트웨어 개발에 유용한 프레 임워크를 제공합니다.
MVC에서 모델이란 ‘순수한’ 데이터와 로직입니다. 모델은 사용자 상호작용을 전혀 신경 쓰지 않습니다. 뷰는 모델을 사용자에게 전달하며, 컨트롤러는 사용자 입력을 받고 모델을 조작하며 어떤 뷰를 표시할지 결정합니다. 필자는 종종 ‘코디네이터coordinator’가 ‘컨트롤러’보다 더 나은 용 어가 아닐까 생각하곤 합니다. 컨트롤러라는 말에는 사용자 입력을 받는다는 느낌이 들지 않는 데도, 컨트롤러가 MVC에서 수행하는 주요한 역할은 사용자 입력을 받는 것이니까요.
MVC에는 셀 수도 없이 많은 변형이 존재합니다. 그중에서도 특히 마이크로소프트의 ‘모델뷰-뷰 모델’(MVVM )은 가치 있는 개념, 즉 뷰 모델이란 개념을 소개했습니다(컨트롤러가 뷰 안에 포함되기도 하는데, 필자가 보기엔 별로 흥미롭지는 않습니다). 뷰 모델은 기본적으로 모
17장 - 익스프레스에서 MVC 구현 267
델의 변형이지만, 뷰 모델 하나에 모델 여러 개가 결합될 수도 있고, 여러 모델의 부분합이나 단일 모델의 일부가 될 수도 있습니다. 언뜻 보기에는 불필요하게 복잡해 보일 수도 있지만 필 자는 여기서 대단히 가치 있는 개념을 발견했습니다. 그건 바로 모델을 ‘보호’한다는 겁니다. 순 수한 MVC에서는 모델을 변형하거나, 뷰에만 필요한 것을 추가해 모델을 더럽히고 싶어질 때 가 있고, 심지어 그런 일이 필요하기까지 합니다. 뷰 모델에는 ‘외부’가 있습니다. 표현에만 필 요한 뷰가 있어야 한다면 뷰 모델에서 하면 됩니다. 어떤 패턴이든 마찬가지지만, MVC를 따를 때도 얼마나 충실히 지킬지 결정해야 합니다. 원칙 에 너무 충실하면 최신 기술을 ‘올바른’ 방식으로 구현하기 위해 영웅적인 노력이 필요할 겁니 다. 반면 너무 방만하면 유지보수 문제가 생기고 기술적인 부채를 지게 됩니다. 필자는 충실한 쪽을 선호하는 편입니다. 다행히 MVC와 뷰 모델은 자연스럽게 따라야 하는 부분이 있고, 이 패턴으로 적응하기 어려운 상황은 극히 드뭅니다.
17.1. 모델 필자는 모델이 무엇보다도 중요한 요소라고 생각합니다. 모델이 견고하고 잘 디자인되어 있으 면 표현 계층을 만들거나 추가하기는 매우 쉽습니다. 하지만 다른 길로 가기는 어렵습니다. 모 델은 프로젝트의 기반입니다. 표현 계층이나 상호작용 코드로 모델을 어지럽히지 않는 게 정말 중요합니다. 어지럽히는 게 쉽거나 편리한 해결책으로 보이더라도, 결국 두통거리를 예약하는 것일 뿐이라고 장담합니다. 더 복잡하고 논란거리인 문제는 모델과 지속성 계층 사이의 관계입니다. 이상적인 세계에서는 모델과 지속성 계층을 완벽히 분리할 수 있습니다. 확실히 가능한 이야기 이긴 하지만, 상당한 대가를 치러야 합니다. 모델은 대부분 지속성 계층에 깊이 의존하고, 두 계층을 분리하는 일은 투자에 비해 가치가 적을 때가 많습니다. 이 책에서는 몽고DB 전용인 몽구스를 써서 모델을 정의했으므로 큰 문제는 없었습니다. 특정 한 지속성 계층에 얽매이기 싫다면 스키마나 객체 매핑을 전혀 지원하지 않는 몽고DB 네이티 브 드라이버를 쓰고 모델과 지속성 계층을 분리할 수 있습니다. 모델은 데이터만이어야 한다고 주장하는 사람들도 있습니다. 즉 모델에 로직이 있으면 안 된다는
268 한 권으로 끝내는 Node & Express
겁니다. ‘모델’이라는 단어를 들으면 기능보다는 데이터를 더 많이 떠올리기는 하지만, 필자는 이 런 제한이 유용하다고 생각하지는 않으며 모델은 데이터와 로직의 결합이라고 생각합니다. 프로젝트에 models 서브디렉터리를 만들어 모델을 거기 보관하길 권합니다. 구현해야 할 기능 이 있거나 저장할 데이터가 있을 때마다 models 디렉터리에서 작업해야 합니다. 예를 들어 고 객 데이터와 로직을 models/customer.js에 저장한다고 합시다. var mongoose = require('mongoose'); var Order = require('./order.js'); var customerSchema = mongoose.Schema({ firstName: String, lastName: String, email: String, address1: String, address2: String, city: String, state: String, zip: String, phone: String, salesNotes: [{ date: Date, salespersonId: Number, notes: String, }], }); customerSchema.methods.getOrders = function(cb){ return Order.find({ customerId: this._id }, cb); }; var Customer = mongoose.model('Customer', customerSchema); modules.exports = Customer;
17.2. 뷰 모델 모델을 뷰에 바로 보내서는 절대 안 된다고 독단적으로 주장하진 않겠지만, 뷰에 표시할 뭔가 가 필요하다는 이유만으로 모델을 수정하고 싶어진다면 뷰 모델을 만들길 강력히 권합니다. 뷰
17장 - 익스프레스에서 MVC 구현 269
모델을 쓰면 모델의 추상성을 유지하면서도 의미 있는 데이터를 뷰에 제공할 수 있습니다. 이전 예제에서는 Customer 모델을 만들었습니다. 이제 고객 정보와 함께 주문 목록을 보여주 는 뷰를 만들고 싶습니다. 하지만 Customer 모델에는 별로 손댈 필요가 없습니다. 이 모델에 는 판매 노트처럼 고객에게 노출하고 싶지 않은 정보도 있고, 메일 주소나 전화번호 등을 다 른 형식으로 표시하고 싶을 수도 있습니다. 또한, 고객 주문 목록처럼 Customer 모델에는 존 재하지 않는 데이터를 표시하고 싶을 수도 있습니다. 이럴 때 뷰 모델이 유용합니다. 뷰 모델
viewModels/customer.js를 만듭시다. // 필드를 합치는 간편 함수 function smartJoin(arr, separator){ if(!separator) separator = ' '; return arr.filter(function(elt){ return elt!==undefined && elt!==null && elt.toString().trim() !== ''; }).join(separator); } module.exports = function(customer, orders){ return { firstName: customer.firstName, lastName: customer.lastName, name: smartJoin([customer.firstName, customer.lastName]), email: customer.email, address1: customer.address1, address2: customer.address2, city: customer.city, state: customer.state, zip: customer.zip, fullAddress: smartJoin([ customer.address1, customer.address2, customer.city + ', ' + customer.state + ' ' + customer.zip, ], '<br>'), phone: customer.phone, orders: orders.map(function(order){ return { orderNumber: order.orderNumber,
270 한 권으로 끝내는 Node & Express
CHAPTER
18
보안
최근에는 대부분의 웹사이트와 애플리케이션에 일종의 보안 요건이 있습니다. 사람들이 로그인 하거나 개인 식별 정보personally identifiable information (PII )를 저장한다면 사이트 보안이 필요합니다. 이 장에서는 웹사이트 보안의 초석인 보안 HTTPHTTP Secure (HTTPS )를 설명합니다. 인증 메커 니즘도 설명하는데, 주로 타사 인증에 초점을 맞춥니다. 보안은 책 한 권을 다 채울 수 있는 방대한 주제입니다. 따라서 이 책에서는 기존 인증 모듈을 이용하는 데 초점을 맞춥니다. 인증 시스템을 직접 만드는 것도 가능하긴 하지만, 크고 복잡한 작업입니다. 또한, 타사 인증 방법이 더 좋은 이유도 있는데 이 장 후반에서 설명합니다.
18.1. HTTPS 보안 서비스 제공의 첫 단계는 보안 HTTP (HTTPS ) 사용입니다. 인터넷의 특성상 클라이언 트와 서버 사이에 전송되는 패킷을 제3자가 가로챌 수 있습니다. HTTPS는 패킷을 암호화해서 전송되는 정보에 공격자가 접근하기 대단히 어렵게 만듭니다. 매우 어려운 거지, 불가능한 건 아닙니다. 완벽한 보안이란 존재하지 않으니까요. 하지만 HTTPS는 은행 거래나 기업 보안, 건강 관리 등에 써도 될 만큼 충분히 안전하다고 평가받고 있습니다.
HTTPS를 웹사이트 보안의 초석이라고 생각해도 됩니다. HTTPS 자체에 인증 기능이 있는 건
18장 - 보안 275
아니지만, 인증을 위한 기반을 제공합니다. 예를 들어 인증 시스템에서 비밀번호를 전송한다고 할 때, 그 비밀번호가 암호화되지 않은 채 전송된다면 보안 시스템을 아무리 복잡하게 만들었 어도 헛수고입니다. 보안의 강함은 체인의 가장 약한 고리가 결정합니다. 그 첫 번째 고리는 바 로 네트워크 프로토콜입니다.
HTTPS 프로토콜에서는 서버가 공용 키 인증서public key certificate를 가지고 있습니다. 종종 SSL 인증서라고도 부릅니다. 현재 SSL 인증서의 표준 형식은 X.509라고 합니다. 이 인증서 방식은 인증서 발행자certificate authority1 (CA )가 인증서를 발행한다는 개념입니다. 인증서 발행자는 브라 우저 제조사에서 사용할 수 있는 신뢰할 수 있는 루트 인증서를 만듭니다. 브라우저를 설치할 때 이들 신뢰할 수 있는 루트 인증서도 함께 설치되며, 이 인증서를 통해 CA와 브라우저 사이 에 신뢰할 수 있는 체인이 수립됩니다. 이 체인이 동작하려면 반드시 서버에서 CA에서 발행한 인증서를 사용해야 합니다. 요약하면, HTTPS를 제공하려면 CA가 발행한 인증서가 필요하단 얘깁니다. 그럼 어떻게 얻을 수 있을까요? 크게 말해 직접 생성하거나, 무료 CA에서 얻거나, 상업적 CA에서 구입할 수 있 습니다.
18.1.1. 인증서 직접 생성 인증서를 생성하기는 어렵지 않지만, 직접 생성한 인증서는 일반적으로 개발과 테스트 목적에 만 적합합니다(인트라넷이라면 배포해도 될 수도 있습니다). 인증서의 계층적 성격 때문에 브 라우저는 알려진 CA가 생성한 인증서만 신뢰합니다(즉 당신의 인증서는 아니죠). 웹사이트가 브라우저가 모르는 CA의 인증서를 사용하려 하면 브라우저는 매우 겁주는 말투로 알려지지 않 은(따라서 신뢰할 수 없는) 개체와 보안 연결을 수립하려 한다고 경고할 겁니다. 개발과 테스 트 과정에는 상관없습니다. 당신이나 동료들은 직접 생성한 인증서를 사용 중이니 브라우저가 경고하리란 걸 알고 있으니까요. 하지만 이런 상태로 웹사이트를 오픈하면 방문자는 모두 발길 을 돌릴 겁니다.
1 역주_ 어의상 ‘인증서를 관리/감독하는 자’라는 뜻이지만 이 책의 문맥에서는 인증서를 발행(판매)하는 기업/단체라는 뜻으로만 사용하므 로 인증서 발행자라고 옮깁니다.
276 한 권으로 끝내는 Node & Express
NOTE_ 브라우저 배포와 설치를 감독하고 있다면 브라우저를 설치할 때 직접 생성한 루트 인증서도 자동 으로 설치되게 할 수 있고, 이렇게 하면 방문자의 브라우저에 경고가 표시되지 않습니다. 하지만 설치하기가 쉽지도 않고, 어떤 브라우저를 설치할지 선택할 권한이 있는 환경에서나 가능합니다. 이렇게 해야 하는 정말 확고한 이유가 없다면, 이 방법은 일반적으로 득보다 실이 많습니다.
인증서를 직접 생성하려면 OpenSSL 프로그램이 필요합니다. [표 18-1]은 프로그램을 얻는 방법입니다. 표 18-1 OpenSSL 프로그램을 얻는 방법 플랫폼
방법
OS X
brew install openssl
우분투, 데비안
sudo apt-get install openssl
기타 리눅스
http://www.openssl.org/source/에서 내려받아 tarball 압축을 풀고 지시를 따릅니다.
윈도우
http://gnuwin32.sourceforge.net/packages/openssl.htm에서 내려받습니다.
TIP
윈도우 사용자라면 OpenSSL 설정 파일의 위치를 명시해야 하는데, 윈도우의 경로명 때문에 쉽지 않을 겁 니다. 확실한 방법은 openssl.cnf 파일(보통 설치한 디렉터리 아래 share 서브디렉터리에 있습니다)을 찾 고, openssl 명령을 내리기 전에 SET OPENSSL_CONF=openssl.cnf 명령으로 OPENSSL_CNF 환경 변수 를 설정하는 겁니다.
OpenSSL을 설치했으면 개인 키와 공용 인증서를 생성할 수 있습니다. openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout meadowlark.pem -out meadowlark.crt
국가 코드, 도시, 주, 완전한 형태의 도메인 이름(FQDN ), 이메일 주소 등 세부 사항을 물어 볼 겁니다. 이 인증서는 개발/테스트 목적이므로 어떤 값을 제공해도 상관없습니다(사실 이 질 문은 모두 옵션이지만, 이 값을 비워두면 브라우저에서 이 인증서를 더 의심합니다). 공통 이 름(FQDN )은 브라우저에서 도메인을 식별할 때 사용합니다. 따라서 localhost를 사용 중이 라면 그대로 써도 되고, 서버의 IP 주소를 써도 되고, 서버 이름이 있다면 이름을 써도 됩니다. 공통 이름과 URL의 도메인이 일치하지 않아도 암호화는 여전히 동작하지만, 브라우저에서 불 일치에 대해 추가 경고를 표시할 겁니다. 이 명령어의 자세한 내용이 궁금하다면 OpenSSL 문서 페이지( http://www.openssl.org/
18장 - 보안 277
docs/apps/req.html )를 참고하십시오. -nodes 옵션이 노드와는 아무 상관도 없다는 건 알고
넘어갑시다. 이 옵션은 ‘no DES’, 즉 개인 키를 DES로 암호화하지 않는다는 뜻입니다. 이 명령을 실행하면 meadowlark.pem, meadowlark.crt 파일이 생깁니다. PEM (프라이버시 가 강화된 전자 메일) 파일이 개인 키이므로 클라이언트에 보내서는 안 됩니다. CRT 파일은 자체 서명된 인증서이며 브라우저에 보내서 보안 연결을 수립하는 데 사용합니다. 다른 방법으로, http://www.selfsignedcertificate.com처럼 자체 서명된 무료 인증서를 제 공하는 웹사이트도 있습니다.
18.1.2. 무료 인증서 발행자 이용 HTTPS는 신뢰를 바탕으로 움직이는데, 인터넷에서 신뢰를 얻는 가장 쉬운 방법이 돈을 주 고 사는 것이라는 현실은 좀 씁쓸합니다. 그렇다고 인증서 발행자가 약장수인 건 아닙니다. 보안에 필요한 기반 구조를 수립하고, 인증서를 보증하고, 브라우저 제조사와의 관계를 관 리하려면 돈이 필요합니다. 물론 돈을 주고 산 인증서만 실무에 쓸 수 있는 건 아닙니다.
CACert ( http://www.cacert.org )는 포인트를 통해, 당신이 ‘내가 메도라크요’ 하면 당신이 메도라크라고 믿는 ‘신뢰의 웹’을 만들고 있습니다. 인증서를 발행받을 만큼 충분한 포인트를 얻으려면 ‘보증인’으로 평가받는 CACert 멤버 자격을 갖춰야 합니다. 아니면 포인트를 지급하 는 이벤트에 참여해도 됩니다. 하지만 역시 무료로는 한계가 있습니다. 현재 주요 브라우저들은 CACert를 지원하지 않습니 다. 모질라 파이어폭스는 최종적으로 CACert를 지원할 예정이지만, CACert의 비영리적 성격 때문에 구글 크롬이나 인터넷 익스플로러, 애플 사파리에서 지원할 가능성은 낮아 보입니다. 따라서 CACert 인증서는 개발이나 테스트 목적으로만 권할 수 있습니다. 신뢰할 수 없는 인증 서라는 경고를 봐도 크게 두려워하지 않을 오픈 소스 사용자에게만 서비스하는 것도 괜찮을 겁 니다. 코모도Comodo나 시만텍Symantec 같은 주요 인증서 판매사는 모두 30일에서 90일 동안 시험해볼 수 있는 무료 인증서를 제공합니다. 상업용 인증서를 테스트하는 동안에는 무료 인증서도 문제 없지만, 서비스를 계속 유지하려면 시험 사용 기간이 끝나기 전에 구입해야 합니다.
278 한 권으로 끝내는 Node & Express
CHAPTER
19
타사 API와의 통합
완벽히 혼자인 웹사이트가 성공하는 사례는 점점 줄고 있습니다. 기존 사용자를 유지하고 새 사용자를 찾으려면 반드시 소셜 네트워크와 통합해야 합니다. 상점 위치를 제공하거나 기타 위 치 기반 서비스를 제공하려면 지오로케이션과 지도화 서비스가 꼭 필요합니다. 이게 끝은 아닙 니다. 점점 더 많은 기업에서 API를 제공해야 서비스를 확장하고 더 유용하게 만들 수 있다는 사실을 깨닫고 있습니다. 이 장에서는 가장 많이 통합되는 서비스인 소셜 미디어와 지오로케이션과의 통합에 관해 설명 합니다.
19.1. 소셜 미디어 소셜 미디어는 제품이나 서비스를 광고하기에 아주 좋은 방법입니다. 광고가 목적이라면, 제공 하는 콘텐츠를 사용자들이 소셜 미디어 사이트를 통해 쉽게 공유할 수 있게 만들어야 합니다. 이 글을 쓰는 시점에서 지배적 소셜 네트워크 서비스는 페이스북과 트위터입니다. 구글+는 아 직 지배력을 확장하고자 악전고투하고 있지만, 너무 무시하지는 마십시오. 어쨌든 세계에서 가 장 큰 인터넷 회사가 지원하는 서비스니까요. 핀터레스트Pinterest, 인스타그램Instagram, 플리커Flickr 같은 사이트도 나름대로 자신의 영역을 확보하고 있지만, 이들은 특정 분야에 관심이 있는 중 소규모 사용자를 타깃으로 삼고 있습니다. 예를 들어 DIY 제작에 관한 웹사이트를 만들고 있
19장 - 타사 API와의 통합 307
다면 반드시 핀터레스트를 지원하고 싶을 겁니다. 실소할 수도 있겠지만, 필자는 마이스페이스 도 분명 다시 부활할 거라 믿습니다. 마이스페이스는 사이트를 재설계하는 중인데, 노드
MySpace
를 사용해 만들고 있다니 흥미롭습니다.
19.1.1. 소셜 미디어 플러그인과 사이트 성능 소셜 미디어 통합은 대부분 프론트엔드 쪽 작업입니다. 페이지에 있는 자바스크립트가 들어오 는 콘텐츠(예를 들어 페이스북에 뜬 인기 글 세 개)와 나가는 콘텐츠(예를 들어 현재 페이지 를 트윗)를 모두 처리합니다. 이 방법이 소셜 미디어를 통합하는 가장 쉬운 방법이기는 하지만 대가가 있습니다. 필자는 HTTP 요청이 늘어나면서 로딩 시간이 두 배, 세 배로 늘어나는 페이 지를 여러 번 봤습니다. 페이지 성능이 중요하다면(특히 모바일 사용자에게 중요합니다) 소셜 미디어를 어떻게 통합할지 주의 깊게 생각해야 합니다. 그렇긴 하지만, ‘좋아요’ 버튼이나 ‘트윗’ 버튼 구현에 필요한 코드는 브라우저 쿠키를 보내야만 동작합니다. 이 기능을 서버 쪽으로 옮기기란 대단히 어렵고 불가능할 때도 있습니다. 따라서 이 기능이 필요하다면 적절한 타사 라이브러리를 링크하는 게 가장 좋은 옵션이며 페이지 성능 에 영향이 있더라도 감수해야 합니다. 그나마 다행인 것은 페이스북과 트위터 API는 아주 널리 쓰이므로 브라우저가 이미 이 라이브러리를 캐시하고 있을 가능성이 높고, 따라서 성능에 미치 는 영향은 그리 크지 않을 거라는 점입니다.
19.1.2. 트윗 검색 #meadowlarktravel 해시 태그가 들어 있는 트윗을 최근 순서대로 10개 멘션하고 싶다고 합 시다. 프론트엔드 구성 요소를 쓸 수도 있지만, 그렇게 하면 HTTP 요청이 늘어납니다. 서버에 서 그 일을 하면 트윗을 캐시해서 성능을 올릴 수 있습니다. 또한, 서버에서 검색을 담당하면 악성 트윗을 ‘블랙리스트’로 관리할 수 있는데 이런 기능은 프론트엔드에서 구현하기 어렵습니 다. 트위터에서도 페이스북과 마찬가지로 애플리케이션을 만들 수 있습니다. 단어를 약간 잘못 사 용한 느낌이 들기도 합니다. 트위터 앱은 상식적으로 앱이 하는 일을 아무것도 하지 않으니까 요. 트위터 앱은 사이트에 실제 앱을 만드는 데 쓸 수 있는 증명을 모아놓은 집합에 더 가깝습
308 한 권으로 끝내는 Node & Express
니다. 트위터 API에 접근하는 가장 쉽고 재사용성 높은 방법은 앱을 만들어서 접근 토큰을 얻 는 겁니다. 트위터 앱 생성은 http://dev.twitter.com에서 합니다. 화면 하단 ‘Tools’ 메뉴 아래 ‘Manage
Your Apps’를 클릭한 후 ‘Create New App’ 버튼을 클릭하고 지시에 따릅니다. 애플리케이 션을 만들고 나면 사용자consumer 키와 사용자 시크릿을 받습니다. 사용자 시크릿은 이름 그대로 안전하게 보관해야 합니다. 클라이언트에 응답할 때 절대 시크릿이 포함되서는 안 됩니다. 타 사에서 시크릿을 알게 되면 애플리케이션 요청을 위조할 수 있고, 그들이 악의적이라면 불행한 결과를 초래할 겁니다. 사용자 키와 사용자 시크릿이 있으니 이제 트위터 REST API와 통신할 수 있습니다. 코드를 간결하게 유지하도록 트위터 코드를 lib/twitter.js 모듈에 저장하겠습니다. var https = require('https'); module.exports = function(twitterOptions){ return { search: function(query, count, cb){ // TODO } }; };
이제 이런 패턴은 슬슬 익숙하게 느껴져야 합니다. 이 모듈은 함수를 내보내는데, 호출자가 이 함수에 설정 객체를 넘길 겁니다. 반환되는 것은 메서드가 들어 있는 객체입니다. 이런 방식으 로 모듈에 기능을 추가할 수 있습니다. 현재는 search 메서드만 들어 있습니다. 이 라이브러리 는 다음과 같이 사용할 겁니다. var twitter = require('./lib/twitter')({ consumerKey: credentials.twitter.consumerKey, consumerSecret: credentials.twitter.consumerSecret, }); twitter.search('#meadowlarktravel', 10, function(result){ // 트윗은 result.statuses에 들어가게 됩니다. });
19장 - 타사 API와의 통합 309
credentials.js 파일에 consumerKey와 consumerSecret가 들어 있는 twitter프로퍼티를 추가 해두는 걸 잊지 마십시오. search 메서드를 완성하기 전에 트위터에서 인증받을 기능이 반드시 필요합니다. 과정은 단순
합니다. HTTPS 요청을 보내서 사용자 키와 사용자 시크릿에 해당하는 접근 토큰을 받으면 됩 니다. 이 과정은 한 번만 하면 됩니다. 현재 트위터는 접근 토큰을 만료시키지 않고 있습니다 (직접 만료하는 건 가능합니다). 매번 접근 토큰을 요청할 수는 없으니 접근 토큰을 캐시해서 재사용하겠습니다. 우리가 택한 방법대로 모듈을 만들면 호출자는 사용할 수 없는 고유 기능을 만들 수 있습니다. 이 예제에서 호출자는 단지 module.exports만 사용할 수 있습니다. 모듈은 함수를 반환하므 로, 호출자가 사용할 수 있는 건 함수뿐입니다. 그 함수를 호출하면 객체가 반환되며, 호출자 는 그 객체의 프로퍼티만 이용할 수 있습니다. 접근 토큰을 캐시할 accessToken 변수와 접근 토큰을 가져올 getAccessToken 함수를 만들 겁니다. 이 함수를 처음 호출하면 트위터 API에 요청을 보내 접근 토큰을 가져올 겁니다. 다음에 호출하면 트위터 API에 요청을 보내지 않고 accessToken의 값을 반환합니다. var https = require('https'); module.exports = function(twitterOptions){ // 이 변수는 모듈 밖에서는 보이지 않습니다. var accessToken; // 이 함수는 모듈 밖에서는 보이지 않습니다. function getAccessToken(cb){ if(accessToken) return cb(accessToken); // TODO: 접근 토큰 가져오기 } return { search: function(query, count, cb){ // TODO }, }; };
getAccessToken 에서 트위터 API 를 비동기적으로 호출할 수 있으므로 콜백이 필요합니
다. 이 콜백은accessToken 값이 유효할 때 호출될 겁니다. 이제 기본 구조는 만들었으니
310 한 권으로 끝내는 Node & Express
CHAPTER
20
디버그
‘디버그’는 버그와 뜻이 연결됐다는 이유만으로 기피되는 불운한 용어입니다. 사실 우리가 ‘디 버그’라 부르는 행위는 늘 하고 있는 일입니다. 새 기능을 구현하든, 뭔가가 동작하는 방법을 배우든, 실제로 버그를 수정하든 말입니다. ‘탐색exploring’이라는 용어가 더 어울릴 수도 있지만, ‘디버그’라는 단어가 동기와 상관없이 이 행위를 뚜렷이 나타내니 계속 쓰기로 합시다. 디버그는 종종 등한시되는 기술입니다. 가끔은, 프로그래머라면 당연히 태어날 때부터 디버그 하는 법을 알고 있어야 하는 것처럼 보입니다. 컴퓨터과학 교수나 책을 쓰는 사람들에게 디버 그는 너무나 당연한 능력이라 언급되지 않는 건지도 모르겠습니다. 사실 디버그는 배워야 하는 기술이고, 프로그래머는 디버그를 통해 지금 사용 중인 프레임워크 뿐만 아니라 자신 또는 팀이 만든 코드를 제대로 이해할 수 있습니다. 이 장에서는 노드와 익스프레스 애플리케이션을 효과적으로 디버그하는 도구와 테크닉을 몇 가지 설명합니다.
20.1. 디버그의 첫 번째 원칙 이름이 암시하듯 ‘디버그’는 보통 버그를 찾아서 없애는 과정입니다. 도구를 설명하기 전에 먼 저 일반적인 디버그 원칙에 관해 생각해봅시다.
20장 - 디버그 331
불가능한 것들을 제외한 뒤 남는 것이 아무리 어처구니없을지라도 그것이 진실이라고 내가 몇 번이나 말했나? -아서 코난 도일
디버그 과정의 첫 번째 원칙이면서 가장 중요한 원칙은 소거법elimination입니다. 최신 컴퓨터 시 스템은 믿을 수 없을 정도로 복잡합니다. 전체 시스템을 염두에 둔 채 그 방대한 구름 속에서 단 하나의 문제의 원인을 찾으려 한다면 어디서 시작해야 할지도 모를 겁니다. 당장 분명히 파 악할 수 없는 문제에 직면할 때마다 가장 먼저해야 할 생각은 ‘이 문제의 원인이 될 만한 것 중 에서 무엇을 소거할 수 있는가?’입니다. 여러 가지를 제외할수록 생각해야 할 선택지가 줄어듭 니다. 소거법에는 여러 가지 형태가 있습니다. 몇 가지 공통 예제를 봅시다. 코드 블록을 체계적으로 주석 처리하거나 비활성화해봅니다.
●
단위 테스트로 테스트할 수 있게 코드를 작성합니다. 단위 테스트는 그 자체로 소거법을 돕는 프레임워크입
●
니다. 네트워크 트래픽을 분석해서 클라이언트 문제인지 서버 문제인지 판단합니다.
●
첫 번째 문제와 유사한 다른 부분을 테스트합니다.
●
성공했던 입력을 다시 테스트하고, 입력을 조각내어 하나씩 입력하면서 문제가 나타나는지 테스트합니다.
●
버전 관리를 사용해 문제가 없을 때까지 한 단계씩 돌아가봅니다.
●
기능을 ‘무시mock’해서 복잡한 하위 시스템을 단순화합니다.
●
물론 소거법이 모든 악을 일소하는 은탄환은 아닙니다. 둘 이상의 구성 요소가 복잡하게 상호 작용하며 문제가 생길 때도 잦습니다. 구성 요소를 하나씩 소거하거나 무시하다 보니 문제가 사라졌지만, 어느 한 구성 요소의 문제라고 단정 지을 수 없을 때도 있습니다. 설령 정확한 위 치에서 사이렌을 울릴 수는 없더라도, 소거법은 이런 상황에서도 문제를 좁혀나가는 데 도움을 줄 수 있습니다. 소거법은 조심스레, 꼼꼼하게 해야 가장 성공적입니다. 구성 요소가 전체에 어떤 영향을 끼치는 지 꼼꼼히 생각하지 않고 소거를 위한 소거를 반복하다 보면 문제를 놓치기가 매우 쉽습니다. 자 기 자신과 내기를 한다고 생각해보십시오. 어떤 구성 요소를 소거하기 전에, 제외하고 나면 전체 시스템에 어떤 영향이 있을지 생각해보는 겁니다. 이렇게 하다 보면 무엇을 예상해야 할지, 구성 요소를 제거하거나 그냥 두면 어떤 유용한 단서가 드러날지 영감을 얻을 수 있습니다.
332 한 권으로 끝내는 Node & Express
20.2. REPL과 콘솔을 활용하십시오 노드와 브라우저에는 모두 읽고-평가하고-출력하는 루프read–eval–print loop (REPL )가 존재합니 다. 사실 이는 자바스크립트를 대화형으로 작성하는 기본적인 방법입니다. 자바스크립트는 코 드를 조금 입력하고 엔터를 누르면 즉시 결과를 볼 수 있습니다. REPL은 충분히 활용할 만한 훌륭한 방법이며, 작은 코드에서 에러를 찾아내는 가장 빠르고 직관적인 방법이기도 합니다. 브라우저에서는 자바스크립트 콘솔이 곧 REPL입니다. 노드에서는 아무 옵션 없이 node라고 입력하면 REPL 모드에 들어갑니다. 여기서 패키지를 불러올(require ) 수도 있고, 변수나 함 수를 만드는 등, 패키지를 만드는 것만 제외하고 일반적으로 코드에서 할 수 있는 일은 다 할 수 있습니다. 콘솔 로그도 믿음직한 친구입니다. 콘솔 로그는 좀 거친 방법일 수 있지만, 그만큼 이해하기도 쉽고 실행하기도 쉬운 방법입니다. 노드에서 console.log를 호출하면 객체를 읽기 쉬운 형식 으로 보여주므로 쉽게 문제를 찾을 수 있습니다. 물론 일부 객체는 너무 커서 콘솔에 출력해도 유용한 정보를 찾는 데 시간이 너무 많이 걸릴 수 있습니다. 예를 들어 경로 핸들러 아무 것이 든 console.log (req )를 넣어보십시오.
20.3. 노드 내장 디버거 노드에는 내장된 디버거가 있어서 애플리케이션을 단계별로 실행할 수 있습니다. 마치 자바스 크립트 인터프리터가 운전하는 오토바이의 사이드카에 앉은 것처럼 말입니다. 앱을 시작할 때 debug 매개변수를 쓰기만 하면 됩니다. node debug meadowlark.js
실행하면 즉시 몇 가지가 보입니다. 콘솔에 처음 보이는 내용은 debugger listening on port 5858입니다. 노드 디버거는 자신만의 웹 서버를 생성하는 방식으로 동작하므로 이를 통해 디
버그하는 애플리케이션의 실행을 통제할 수 있습니다. 당장은 별로 유용해 보이지 않겠지만, 이 접근법의 장점은 노드 인스펙터를 설명할 때 명확해질 겁니다. 콘솔 디버거에 머무는 동안 help를 입력하면 명령어 목록을 볼 수 있습니다. 가장 자주 사용할
20장 - 디버그 333
명령어는 n (다음), s (진입), o (이탈)입니다. n은 현재 행을 ‘넘깁니다’. 현재 행이 실행되긴 하 지만, 현재 행이 다른 함수를 호출한다면 호출된 함수의 실행이 끝나야 제어권을 반환합니다. 반면 s는 현재 행 안으로 들어갑니다. 현재 행이 다른 함수를 호출한다면 그 함수 안에서 계속 제어권을 갖고 있습니다. o는 현재 실행 중인 함수에서 빠져나옵니다. ‘진입’과 ‘이탈’은 함수에 만 통용됩니다. if나 for 블록, 기타 흐름 제어문에는 진입이나 이탈 개념이 없습니다. 명령줄 디버거에는 더 많은 기능이 있지만, 그리 많이 쓰게 되지는 않을 겁니다. 명령줄에는 훌 륭한 기능이 여럿 있는데 디버그 기능은 거기 낄 정도는 아닙니다. 명령줄 디버그는 궁지에 몰 렸을 때, 예를 들어 서버에 접근할 방법이 SSH 콘솔뿐이거나 서버에 아예 GUI를 설치하지 않 았거나 할 때 쓸 수 있다는 점이 좋은 겁니다. 노드 인스펙터 같은 그래픽 디버거를 더 자주 쓰 게 될 겁니다.
20.4. 노드 인스펙터 노드는 디버그 컨트롤을 웹 서비스로 제공하므로 명령줄 디버거 외에도 다른 옵션이 있습니다. 특히 대니 코츠Danny Coates가 만들고 현재 스트롱루프StrongLoop에서 관리하는 노드 인스펙터Node 는 노드 애플리케이션을 클라이언트 쪽 자바스크립트와 같은 인터페이스에서 디버그할
Inspector
수 있습니다. 노드 인스펙터는 크롬에서도 사용하는 크로미엄 프로젝트의 블링크 엔진을 사용합니다. 크롬 의 디버거에 친숙하다면 노드 인스펙터도 집에 있는 듯 편안할 겁니다. 아, 혹시 디버그가 완전 히 처음이라면 옷매무새를 가다듬으십시오. 디버거를 처음 만져보면 마치 신탁을 받은 기분일 겁니다. 노드 인스펙터에서는 블링크 엔진을 사용하니, 크롬이나 오페라 최신 버전이 필요합니다. 혹시 아직 설치하지 않았다면 지금 바로 설치하십시오. 끝났으면 노드 인스펙터를 설치합니다. sudo npm install -g node-inspector
설치했으면 시작해야죠. 원한다면 별도의 창에서 실행해도 되지만, 유용한 시작 메시지를 빼면 콘솔에 그리 많이 기록하지는 않으므로 필자는 보통 백그라운드에서 실행되게 내버려두고 잊 어버리는 편입니다.
334 한 권으로 끝내는 Node & Express
INDEX 영 문
Expires 헤더 255 express.Router( ) 221
Accepts HTTP 헤더 136
express-handlebars 패키지 50
Access-Control-Allow-Origin 헤더 235
express-logger 185
app.all 156
express-session 미들웨어 149, 163
app.defaultConfiguration 340 app.get 47, 156
<form> 태그 128
app.get(env) 25
fs 모듈 199
app.post 156
fs.readFile 메서드 41, 339
app.render 340 app.set 183
GET 메서드 89, 127
app.use 48, 155, 340 assert 함수 73
hashres
260
headers 프로퍼티 90 basicAuth 미들웨어 162
HTML5 보일러플레이트 44, 122
blocks 111
HTTP 동사 47, 89
body-parser 미들웨어 25, 132, 162
http.IncomingMessage 객체 92
browserify 253
http.ServerResponse 객체 95 HTTPS 275
Cache-Control 헤더 255 cluster.fork 188
json 미들웨어 162
connect-bundle 모듈 261 connect-rest 플러그인 240
Last-Modified 헤더 255
Content-Type 헤더 48, 90
lib/application.js (익스프레스) 98
cookie-parser 미들웨어 146, 163
lib/express.js (익스프레스) 98
cookie-session 미들웨어 163
lib/request.js (익스프레스) 98
createApplication 함수 340
lib/response.js (익스프레스) 98
csurf 미들웨어 163, 285
lib/router/route.js (익스프레스) 98 localhost 36, 335
DELETE 메서드 89 directory 미들웨어 164
method-override 미들웨어 164 morgan 미들웨어 164
errorhandler 미들웨어 164
multipart/form-data 인코딩 129
ETag 헤더 255
찾아보기
385
INDEX NODE_ENV 환경 변수 184
res.status 메서드 48
node_modules 디렉터리 45, 62
res.type 메서드 48
npm 35
response-time 미들웨어 165
npm init
45
REST 함수 242 rest.get 238
OpenSSL 277
rest.put 238 RESTful 서비스 231
package.json 파일 45, 62
restler 패키지 237
pipeline 155 PORT 환경 변수 47
sections 120
POST 메서드 89, 127
serveStaticFile 보조 함수 17
process.nextTick 190
session-mongoose 패키지 214 setTimeout 190
query 미들웨어 165
static 미들웨어 52, 161, 165, 341 static-favicon 미들웨어 164
req.accepts 프로퍼티 136 req.body 101
uncaughtException 이벤트 191
req.cookie 메서드 99
upsert 213
req.cookies 146
URL 인코드된 바디 파싱 342
req.query 93, 99, 101
URL 인코딩 129
req.session 99, 150
urlencoded 미들웨어 162
req.session.authorized 222 req.session.flash 151
vhost 미들웨어 165, 221
req.signedCookies 99 req.xhr 프로퍼티 100, 153
X-Forwarded-Proto 헤더 284
require 함수 64 res.cookie 146 res.locals 객체 73, 97
한 글
res.locals.flash 151 res.locals.partials 객체 119
가상 브라우저 77, 83
res.locals.showTests 프로퍼티 73
그런트 83
res.render 메서드 99, 179
기본 레이아웃 51
res.send 메서드 99, 157, 340 res.set 메서드 48
386 한 권으로 끝내는 Node & Express
난독화 259
노드 22
소거법 332
노드 모듈 64
소셜 미디어 307
노드 인스펙터 334
소스 관리 362
노드메일러 171
수명 계획 360
데이터베이스 서버 28
스코프 변수 337
데이터베이스 지속성 203
스택 오버플로 374
동일 소스 정책 235
스트레스 테스트 196 스프라이트 246
라우트 경로와 정규 표현식 223
실행 환경 183
라우트 핸들러 156 라우팅 38, 217
알림 서비스 196
레스 250
앱 클러스터 187
레이아웃 50, 115
앱 파일 45
링크체커 82
앵귤러 프레임워크 24 에디터 34
메도라크 여행사 예제 웹사이트 44
엠버 프레임워크 24
모델-뷰-뷰 모델 267
웹사이트 확장 186
모델-뷰-컨트롤러 패러다임 49
이미지매직 139
모카 72
이벤트 기반 프로그래밍 37
몽고DB 28, 203, 268, 289
인증서 276
몽고랩 204 몽구스 205, 268
잡히지 않은 예외 처리 190
문제 추적 362
전용 npm 저장소 368
미들웨어 25, 155
좀비 78 중단점 335
버전 관리 58, 362
지도 317
뷰 49, 114
지리 좌표 317
비동기 함수 디버그 338
지오코딩 317
사용자 키/사용자 시크릿 309
차이 어서션 라이브러리 73
상태 없는 프로토콜 143
최소화 257
서명된 쿠키 144, 146 서버 쪽 템플릿 111
커넥트 라이브러리 25
찾아보기
387
코드 리뷰 362
파일시스템 지속성 199
쿠키 기반 세션 149
패스포트 290
쿠키 시크릿 145
페이스북 앱 294
크로스 사이트 HTTP 요청 235
페이스북 인증 전략 294
클라우드 지속성 202
포미더블 136
클라우드 호스팅 349
포트 282
키워드 모니터 195
표현식 관찰 337 프라미스 314
터미널 32
프록시 서버 194
템플릿 엔진 28
플래시 메시지 151
트위터 부트스트랩 44, 133
핑거프린트 256 핸들바 템플릿 엔진 49, 108
파셜 118
호스트 파일 243
파일 업로드 136
호출 스택 337
388 한 권으로 끝내는 Node & Express
w w w. h a n b i t . c o . k r
Hanbit eBook
Realtime w w w. h a n b i t . c o . k r / e b o o k
DRM free! 어떤 디바이스에서도 자유롭게
eBook Oriented! 전자책에 꼭 맞는 최적의 내용과 디자인
Hanbit eBook
Hanbit eBook
Realtime 70
Realtime 89 49
MFC 프로그래밍 주식분석 프로그램 만들기 김세훈 지음
Hanbit eBook
Hanbit eBook
Realtime 90
Realtime 92 49
자바 개발자를 위한
Vert.x JavaScript Promise azu지음 /주우영옮김
애플리케이션 개발 모바일/웹 메시징 STOMP와 MQTT로 개발하는 IoT 모바일/웹 애플리케이션 Mobile and Web Messaging 제프 메스닐 지음 / 조건희 옮김
이연복 지음
w w w. h a n b i t . c o . k r
즐거운 상상이 가득! 2015년 화제의 신간
즐거운 상상이 가득! 2015년 화제의 신간
전자부품 백과사전 vol.1 찰스 플랫 지음 / 배지은 옮김 / 30,000원
취미공학에 필요한 핵심 전자부품을 사전식으로 정리한 안내서.
전자부품 백과사전 vol.1 찰스 플랫 지음 / 배지은 옮김 / 30,000원
처음 시작하는 센서
취미공학에 필요한 핵심 전자부품을 사전식으로 정리한 안내서.
전자부품 백과사전 vol.2
찰스 플랫 지음 / 가격미정
키모 카르비넨, 테로 카르비넨 지음 임지순 옮김 / 13,000원
세상을 수치로 읽어내는
<전자부품 백과사전> 시리즈의 두 번째 도서다.
부품인 센서를 알려주 는 책. 이 책을 통해 자신 만의 프로젝트에 다양한 처음 센서를 사용해보자.
Zero to Maker
: 누구나 메이커가 될 수 있다 데이비드 랭 지음 / 장재웅 옮김 / 14,000원
전자부품 백과사전 vol.2
찰스 플랫 지음 / 가격미정
일반인에서 메이커로. 날백수에서 무인 잠
<전자부품 백과사전> 시리즈의 수정 회사 CEO 가 된 사나이, 데이비드두 번째 도서다. 랭의 메이커 도전기.
Make: 센서
시작하는 센서
키모 카르비넨, 테로 카르비넨 지음 임지순 옮김 / 13,000원
세상을 수치로 읽어내는 부품인 센서를 알려주
키모 카르비넨, 테로 카르비 넨, 빌 발토카리 지음 는 책. 이 책을 통해 자신 / 가격미정
만의 프로젝트에 다양한
필수 전자부품인 센서를
센서를 사용해보자. 마이크로 컨트롤러 보드
Zero to Maker
: 누구나 메이커가 될 수 있다
에 응용하는 방법을 담
데이비드 랭 지음 / 장재웅 옮김 / 14 ,000원 Maker Pro
았다.
베이첼 지음 / 가격미정 일반인에서 메이커로.존날백수에서 무인 잠
메이커라면 반드시 읽어야 할 필수 계발
수정 회사 CEO가 된 사나이, 데이비드 서. 프로 메이커들과의 인터뷰 및 에세이 랭의 메이커 도전기. 수록.
프로젝트로 배우는 라즈베리 파이
도날드 노리스 지음 / 임지순 옮김
다양한 실전 프로젝트를 통해 라즈베리 파이를 쉽고 재미있게 배워본다.
Maker Pro 존 베이첼 지음 / 가격미정
메이커라면 반드시 읽어야 할 필수 계발
Make: 센서 키모 카르비넨, 테로 카르비 넨, 빌 발토카리 지음 / 가격미정
필수 전자부품인 센서를 마이크로 컨트롤러 보드 에 응용하는 방법을 담 았다.