1. express 시작하기
패키지 설치
$ npm install express
설치하고 app.js 파일을 만들어보자.
외부 클라이언트로부터 받은 url path 부분이 '/hello' 라면, 그 함수가 실행된다.
이렇게 특정 조건이 만족되었을 때 실행되는 함수가 콜백이라고 했다.
특정 path에 대응하는 콜백을 route handler 라고도 한다.
route handler : 특정 path를 대상으로 한, 특정 메소드를 가진 리퀘스트를 처리해주는 함수
route : 서버가 각 req의 path 부분을 보고 알맞은 작업을 수행하는 것
handler : 그 작업을 담당하는 존재
request : 이 객체를 통해 클라이언트가 보낸 객체를 다룰 수 있다.
response : 이 객체를 통해 적절한 response를 보낼 수 있다.
listen : 외부에서 req가 오는 것을 기다리도록 하는 메쏘드. 이것을 써주어야 프로그램이 request를 받을 수 있다.
이렇게 app.js 파일을 만들고나서
$ node app.js
로 실행하면 서버 프로그램이 실행된다.
그 후 브라우저(localhost:3000/hello)로 들어가면 실행되는 것을 확인할 수 있다.
2. Route Parameter
여기서 members 라는 js 파일에 여러명의 정보가 들어있다.
: 가 붙어있으면 : 다음에 오는 것을 id라는 변수에 대입하라는 뜻이다.
여기서 :id 를 route parameter 라고 한다.
route parmeter는 path 부분에서 가변적인 값을 전달할 때 사용된다.
roure parameter는 req 객체의 params 라고 하는 객체의 프로퍼티로 설정되게 된다.
이렇게 함으로써 id 값을 가져올 수 있다.
모던 자바스크립트에서는 다른 형식으로 쓴다.
params 객체에서 id라는 프로퍼티의 값만 id 변수에 담는 것이다.
이제 해당 id를 가지는 member를 가져오도록 코드를 짜보자.
find 함수를 이용하였다.
find는 그 인자로 들어오는 콜백함수가 True를 리턴하게하는 여러 요소들 중에서 첫번째 요소를 리턴한다.
여기서 주의할 점은 members 객체의 id는 Number 타입인데 route parameter의 id값을 string 타입이라서 타입을 맞추어 주어야 한다. 그래서 스트링 값인 id를 Number함수에 넣어서 숫자형으로 바꾸어서 숫자형으로 변환했다.
그리고 해당 아이디를 가진 직원이 존재한다면 그 직원의 정보를 response에 담아서 보내준다.
그렇지 않다면 없는 것인데, 이렇게 response의 상태코드를, 요청한 정보가 없을 때에는 404를 많이 쓴다.
상태코드 지정 함수 : status
그리고 확장하기 편하게 json 파일의 형태로 메시지를 보내주어야 한다.
정리
- path 부분에서 가변적인 값이 담길 때, : 을 붙여서 route parameter로 표시한다.
- 그리고 이 값을 req 객체의 params 객체에서 찾으면 된다.
3. Query
서버에 있는 리소스(데이터)를 조회할 때 필터링 기준이나 정렬 기준을 설정하기 위해서 많이 사용한다.
즉, 원하는 것만 보고 싶을 때 사용한다.
url의 쿼리에서 team 이라는 파라미터의 값을 가져온다.
query라는 객체에는 url의 쿼리에 표시한 여러 파라미터의 값이 담겨있다.
url에 쿼리가 있고 team 이라는 파라미터가 있을 경우 해당 팀에 대한 리소스만 보여주게 response에 담아보자.
url에 team이라는 파라미터가 있는 경우에는
전체 직원 정보(members) 중에서 해당 팀에 속한 직원만 추리면 된다.
filter() 메쏘드를 사용했다.
이렇게 작성하고나서 url에
localhost:3000/api/members?team=engineering
이라고 치면 team 파라미터에 engineering 이라는 값을 주게 되면서 해당 팀에 대한 리소스만 나오게 된다.
4. 새 정보 추가하기
필요한 조건
request의 메쏘드가 get이어야만 받을 수 있다.
app 객체의 get() : 특정 path로 오는 get메쏘드를 가진 request를 처리하는 route handler를 등록한다.
아무리 같은 path로 request를 보낸다고 해도 다른 메쏘드를 가진 request를 보내면 실행되지 않는다.
보통 서버에 존재하는 리소스를 조회하고자 할 때, request의 get메쏘드를 설정하고 보낸다.
이런 request를 보통 부르기 편하게 get request라고 한다.
app 객체의 get()은 get request에 대응하는 route handler를 등록하는 함수인 것이다.
이외에도 post request, put request, 그리고 기존 리소스를 삭제하는 delete request도 존재한다.
이중에서도 새로운 리소스를 추가하는 post request에 대해 알아보자.
post request를 처리하려면 app 객체의 post 함수를 사용하면 된다.
추가하게될 새로운 리소스는 request의 body에 들어있다.
POST request에 body를 담아 보내보자.
body에 있는 데이터의 타입이 json
이렇게 보내면 서버에서 body를 읽을 수 없다.
어떤 코드를 추가해주어야 할까?
서버로 온 request의 body에 json 데이터가 존재할 경우에, 이것을 추출해서 request 객체의 body 프로퍼티로 설정해준다.
이런식으로 request가 route handler에 의해 처리되기 전에 추가적으로 필요한 전처리를 수행하는 함수를 middleware 라고 한다.
이때 express의 json 메소드는 미들웨어를 리턴하는 메소드이고, 미들웨어란 리퀘스트를 처리하는 기능을 하는 함수이다.
request가 들어오면 위에서 설정한 middleware에 의해서
body에 있는 json 데이터가 request 객체의 body 프로퍼티로 설정되고
그 다음 request의 path와 메쏘드를 보고 알맞은 route handler가 호출되는 것이다.
이처럼 middleware는 서버로 오는 모든 request에 관해 필요한 처리를 해주는 함수이고 app 객체의 use 메쏘드를 이용해 설정할 수 있다.
이제 정보를 배열에 추가해보자.
body에 해당하는 정보를 newMember 객체로 받았고
기존의 정보 배열인 members에 추가해주고 있다.
이렇게 처리해주고 난 후 get 메쏘드로 정보를 불러오면 새로운 정보가 추가된 것을 확인할 수 있다.
미들웨어
미들웨어는 형식적으로는 req, res, next 이렇게 3개의 파라미터를 가진 함수이고, 기능적으로는 라우트 핸들러 이전에 리퀘스트에 관한 처리를 해주는 함수이다. 미들웨어는 app 객체의 use 메소드로 설정할 수 있다. 미들웨어 안에서 원하는 처리를 모두 수행한 후에는 마지막에 next를 호출하여 리퀘스트를 그 다음 미들웨어 또는 라우트 핸들러로 넘겨줘야 한다.
매 리퀘스트(req) 객체의 query 객체의 내용을 출력하는 두 번째 미들웨어를 추가해보자.
app.use((req, res, next) => {
console.log(req.query);
next();
})
5. nodemon
$ npm install nodemon --save-dev
패키지 실행 명령어
$ npx nodemon app.js
nodemon은 개발할 때 불필요한 시간을 줄여준다.
5.1. nodemon을 설치할 때 옵션을 주는 것과 그냥 설치하는 것은 무슨 차이일까?
--save -dev 옵션을 주고 설치하면 dependencies 필드가 아니라 devDependencies 필드에 nodemon 패키지의 정보가 있다는 것을 확인할 수 있는데, nodemon 패키지의 정보가 dependencies에 있는 것과 devDependencies에 있는 것은 어떤 차이가 있는 걸까?
사실 우리가 어떤 프로젝트의 코드를 실행하는 경우는 크게 다음과 같은 2가지 경우로 나눌 수 있다.
(1) 개발/테스트 목적의 코드 실행: 첫 개발부터 시작해서 중간중간 코드를 제대로 작성해가고 있는 건지를 테스트하기 위한 목적에서의 코드 실행
(2) 실제 서비스 제공을 위한 코드 실행: 충분한 검증을 거친 코드를 서비스 제공 목적으로 배포해서 실행하기 위한 목적에서의 코드 실행
이렇게 2가지이다.
앞으로 (1)의 용도는 '개발 용도', (2)의 용도는 '배포 용도'라고 줄여서 말하겠다. 그런데 하나의 프로젝트에서, 개발 용도로 코드를 실행할 때 필요한 패키지와 배포 용도로 코드를 실행할 때 필요한 패키지들이 꼭 같은 것은 아니다. 즉, 각 용도마다 의존 패키지들이 달라질 수 있다는 것이다.
예를 들어, 우리가 설치해서 사용한 nodemon 패키지는 배포 용도로 쓴다기보다는 주로 개발 용도로 쓴다. nodemon 패키지는 특정 파일 내용을 수정하고 저장할 때마다 이를 감지하고 프로그램을 재실행하는 기능이 있었다. 그런데 어차피 배포 용도로 실행되고 있는 코드는 이상이 있거나 추가할 부분이 있으면 아예 실행되던 코드를 수정하고 서버에 다시 직접 배포해야 한다. 그래서 배포 시에는 nodemon 패키지의 기능이 굳이 필요하지 않다.
바로 이렇게 배포 용도로는 필요하지 않고 개발 용도로만 필요한 패키지를 설치할 때 --save-dev 옵션을 사용하는 것이다.
자, 그럼 이제 하나의 프로젝트에서
- 배포 용도로 필요한 패키지들은 dependencies 필드에,
- 개발 용도로만 필요한 패키지들은 devDependencies 필드에
그 정보가 기재되어야 한다는 것은 알았다.(배포 용도와 개발 용도에서 모두 필요한 패키지라면 당연히 dependencies 필드에 적혀야할 것이다.) 그리고 방금 위에서 본 것처럼 옵션없이 npm install로 설치한 패키지들은 dependencies 필드에 적히지만, --save-dev 옵션으로 설치한 패키지들은 devDependencies 필드에 적힌다는 것도 알았다.
그런데 사실 옵션을 쓰나 안 쓰나 node_modules 디렉토리에 nodemon 패키지가 설치된다는 사실은 둘다 똑같다. 그럼 왜 이런 구분이 필요한 걸까?
Node.js 프로젝트를 다른 개발자와 공유할 때 node_modules 디렉토리는 용량이 너무 크기 때문에 굳이 공유할 필요가 없고, package.json 파일만 공유해주고, 프로젝트를 공유받은 개발자가
$ npm install
을 실행해서 package.json 파일에 적혀있던 패키지들을 설치하는 것이 일반적이라고 했다. 이때는 dependencies 필드와 devDependencies에 있던 모든 패키지들이 설치되는데, 그런데 바로 이때
$ npm install --production
이런 식으로 --production이라는 옵션을 주고 실행하면, 이때는 devDependencies에 있는 패키지들(개발 용도로만 필요하고 배포 용도로는 필요하지 않은 패키지들)은 제외하고 dependencies에 있는 패키지들만 node_modules 디렉토리에 설치하게 된다.
사실 프로젝트의 코드를 배포 용도로 실행하기 전에는 보통 이런 순서의 작업을 거친다.
- 실제 개발 중이던 프로젝트 디렉토리를 하나 더 복사
- 새 디렉토리에서 node_modules 디렉토리 삭제
- npm install --production를 실행해서 devDependencies 필드에 있던 패키지들은 제외하고, dependencies 필드에 있던 패키지들만 node_modules 디렉토리에 재설치
- 실제 서비스를 위해 코드 실행(npm start 등 실행)
이런 식으로 배포를 위해 필요한 패키지들만 딱 설치할 수 있는 것이다. 그리고 나중에 이것을 하기 위해 개발 용도로만 사용할 패키지들은 --save-dev 옵션을 주고 설치해서 package.json 파일의 devDependencies 필드에 그 정보가 적히도록 하는 것이다.
우리는 nodemon 패키지를 실제 서비스를 위한 프로젝트 디렉토리의 node_modules 디렉토리에는 설치하지 않을 것이었기 때문에 --save-dev 옵션을 주고 설치했던 것이다.
이제 --save-dev 옵션의 의미를 깨달았다.
이대로 끝내기 전에 참고로 알아두면 좋을 두 가지만 더 배워보자.
5.2. npm install 옵션 차이
우리가 옵션 없이 썼던 npm install은 사실 npm install --save-prod와 같은 의미이다.
즉, --save-prod 옵션이 생략된 것과 같은 의미라는 것인데, --save-dev를 배웠으니까 --save-prod의 의미도 유추할 수 있을 것이다.
--save-prod는 패키지를 설치하고 그 패키지의 정보를 dependencies 필드에 저장하는 옵션이다. 그러니까 npm install을 할 때 아무런 옵션도 주지 않으면 기본적으로는 --save-prod 옵션이 적용되어서 보통은 설치된 패키지 정보가 dependencies 필드에 적히는 것이다. 이 사실도 기억해두자.
그리고 하나 더! devDependencies 필드의 패키지들은 제외하고 dependencies 필드에 있는 패키지들만 설치하기 위해 npm install --production 이렇게 --production이라는 옵션을 주는 방법 말고도
$ NODE_ENV=production npm install
이런 식으로 옵션을 주는 방법도 있다. 사실 맨 앞의 NODE_ENV=production은 NODE_ENV라는 환경 변수의 값을 production으로 주라는 뜻이다.
6. 정보 수정하기
수정해야할 직원 정보의 파라미터 = :id
request의 body에 담겨있을 새로운 직원 정보 = newInfo 객체로 설정
해당 id값을 가진 직원의 정보 객체를 배열에서 찾고, 해당 객체의 모든 프로퍼티의 값을 수정해준다.
Object 객체의 keys 메쏘드를 사용하면 특정 개체의 모든 프로퍼티를 조회할 수 있다.
위의 코드는 모든 객체를 순회하면서 각각의 프로퍼티의 값을, member 객체의 같은 이름을 가진 프로퍼티의 값으로 대입하는 코드이다.
그리고 바뀐 직원의 정보를 response에 담아서 보낸다.
이렇게 put request를 만들고 다시 직원을 조회해보면 정보가 바뀐 것을 확인할 수 있다.
$ npm start
Express 기반의 프로젝트를 실행할 때 뿐만 아니라 패키지 형태의 프로젝트를 실행할 때는 보통 이 명령어를 사용한다. package.json 파일의 scripts 필드에 이 명령어가 실제로 실행할 명령을 적어주면 된다.
Express 기반의 프로젝트 뿐만 아니라 패키지의 코드를 실행할 때는 직접 구체적인 명령어를 써서 하기도 하지만 package.json 파일의 scripts 객체에 "start"라는 프로퍼티를 두고 그 값으로 구체적인 명령어를 적어둔 후, 실제로 실행할 때는 그냥 npm start만 입력하고 실행하는 것이 일반적이다.
이렇게 하는 이유는 나중에 실제로 입력해야할 명령어가 길어졌을 때 편리하기도 하고, 이렇게 하는 것이 일반적인 관행이기도 하기 때문이다. 많은 외부의 서비스들이 특정 프로젝트를 실행할 때 npm start로 실행해야 할 것으로 가정하고 만들어져 있으니까 이 관행을 잘 따르도록 하자.
'🚦 Server > Node.js' 카테고리의 다른 글
ORM으로 DB 작업하기 (0) | 2021.10.31 |
---|---|
서드파티 모듈, npm 이해하기 (1) | 2021.10.26 |
Node js 정리 (0) | 2021.10.19 |
Node - 화살표 함수 (0) | 2021.08.10 |
Node - const, let 는 var를 대체한다. (0) | 2021.08.10 |