NODE HTTP 서버를 만들어보자
생명주기
node app.js 실행
|
코드 해석, 변수 및 함수 등록
|
[이벤트 루프] ----> 노드 어플리케이션
|
이벤트 리스너가 등록되는 한 반복
시작하기
http.createServer
https://nodejs.org/api/http.html#http_http_createserver_options_requestlistener
서버 객체를 생성
const http = require('http')
const server = http.createServer((req, res) => {
...
});
server.listen
HTTP 서버가 끊어지지 않게 유지하는 역할을 수행. 사용하지 않는다면 실행 후 종료되버린다.
server.listen(3000)
response.write
https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback
HTTP body의 날 것이라고 한다. 테스트 할 때 말고는 딱히 쓸 일은 없다.
res.write('<html>')
res.write('<head><title>제목</title></head>')
res.write('<body><h1> 힘들게써야되지 ? </h1></body>')
res.write('</html>')
response.end
https://nodejs.org/api/http.html#http_response_end_data_encoding_callback
서버에 응답 헤더와 바디를 보냈음을 알리고 처리. 주로 응답 하기 직전 써주어야 함.
요청 (request) 해석하기
express 를 사용하면 아래와 같은 코드를 쓰지 않아도 된다.
그러나, 어떻게 동작하는지 간단하게 알아둘 필요가 있다.
예를 들어, 폼에서 이름이 name인 input 필드에 'yuu2dev' 를 입력한 후 전송해보자.
const server = (req, res) => {
const buffer = []
// 버퍼에 데이터가 채워질 때마다 실행되는 이벤트
req.on('data', (chunk) => {
buffer.push(chunk)
}),
// 요청이 마무리 되었을 때 실행되는 이벤트
req.on('end', () => {
// chunk가 담긴 배열을 하나로 합쳐 새로운 버퍼를 만들어 문자열로 변환
const parsed = Buffer.concat(buffer).toString()
// name=yuu2dev
const [k, v] = parsed.split('=')
console.log(k, v)
})
}
디버깅 (Debugging)
많은 상품을 크롤링 한다던가 스택 큐가 실행 도중에 알 수 없는 에러로 인해 기대하는 결과 값이 나오지 않는다면 어쩔 수 없이 많은 시간을 할애해서 코드를 수정 해야 할 것이다.
조금이라도 시간을 낭비하지 않기 위해서 콜스택 (Call Stack)에서 지점마다 어떤 값이 들어 있는지 확인 할 수만 있다면 내가 자주 사용하는 VsCode의 기능을 조금이나마 알 필요가 있을 것이다.
https://code.visualstudio.com/docs/nodejs/nodejs-debugging
그리고 에러에 대한 설명을 잠깐 하자면 ...
- 문법 에러
- 실행 에러
- 논리 에러
에러는 세가지로 구분 할 수 있다. 위에서 말한 예시는 논리 에러이다.
이 논리에러는 간단하게 말하면 우리가 프로그램을 짜고 의도한대로 동작하지 않은 경우를 뜻한다.
이 경우 에러 메시지를 출력하지 않아 발견하기 어렵다. vscode를 이용한다면 디버깅을 사용 할 수 있다. Run -> Start Debugging -> Node.js 를 통해서 실행 할 수 있다.
만약 디버깅이 끝나고 exit 처리 되어 곤란하다면 설정 값을 추가해서 해소 할 수 있다.
프로젝트/.vsocde/launch.json 에 아래와 같은 값을 추가해야 한다.
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
// 진입점 설정
"program": "${workspaceFolder}/app.js",
"restart": true,
// 노드몬을 사용한다면 설정
"runtimeExecutable": "nodemon",
// 디버그 콘솔이 아닌 터미널에서 볼 수 있음
"console":"integratedTerminal",
}
...
]
Express
https://expressjs.com/en/4x/api.html (4.x API)
앞 문서에서 node.js http 모듈을 통해 요청데이터를 받아올 때 data 와 end 이벤트를 건 후 청크를 바이트 배열로 담아 문자열로 파싱하는 일련의 과정들을 앞으로 이 웹프레임워크가 담당 할 것이다.
설치 및 시작하기
npm install --save express
모듈을 설치 한 후 아래의 express 객체를 받아온다.
const express = require('express')
const app = express()
기본적인 사용법은 아래와 같다. use 는 미들웨어 함수이다.
아래는 여러가지 사용법들을 나열해두었다.
app.use((req, res, next) => {
console.log('미들웨어1')
next()
})
app.get((req, res, next) => {
console.log('미들웨어2')
res.send('<h1>Hello Express</h1>')
})
express.urlencoded(options)
아래와 같은 미들웨어가 있고 form.html 문서에는 myname 이라는 필드를 가진 폼이 있으며 POST 로 데이터를 전송 할 것이라 가정해보자.
app.get('/', (req, res, next) => {
const path = require('path')
res.sendFile(path.join(__dirname, 'views', 'form.html'))
})
app.post('/', (req, res) => {
let myname = req.body.myname
if (myname === undefined) {
console.log('정상적으로 보냈음에도 불구하고 왜 undefined가 떳을까?')
}
const data = { myname }
res.send(data)
})
정상적으로 보냈음에도 불구하고 Content-Type 등을 비롯하여 여러가지 이유로 매칭되지 않아 {} 또는 undefined 를 뱉어내거나 할 때 요청 데이터를 파싱해준다.
// body-parser 라이브러리는 구식화 (deprecated) 되어버렸다.
app.use(express.urlencoded({ extended: true }))
express.Router()
웹 서버에서 담당하는 기능이 많으면 많아질 수록 미들웨어 역시 비례해서 코드가 길어진다.
하나의 파일에 수 많은 미들웨어가 있다면 어떻게 유지보수를 할 것인가. 기능 및 서비스 별로 분할 하고 싶다면 라우터를 이용해보자.
// shopRoutes.js
const router = express.Router()
router.get('/main', (req, res) => {
res.send({ title: 'shop' })
})
module.exports = router
// adminRoutes.js
const router = express.Router()
router.get('/main', (req, res) => {
res.send({ title: 'admin' })
})
module.exports = router
// server.js
const adminRoutes = require('./routes/adminRoutes')
/* http:호스트/main */
app.use(myRoutes)
/* http:호스트/admin/main */
app.use('admin', myRoutes)
express.static()
소스파일 외에 이미지나 html 문서는 자원주소를 통해서 접근 가능해야 할 것이다.
// app.js
const public = path.join(__dirname, 'public'));
app.use(express.static(public);
진입점을 app.js 라고 한다면 public 아래의 문서파일은 누구나 접근가능하도록 처리한다.