Express

Express를 사용해서 RESTful API를 만들어 보자

익스프레스는 노드를 만든 패키지의 일종으로 웹 서버를 만들기 위한 것이라고 볼수 있습니다.
클라이언트와 서버는 HTTP라는 규칙을 이용해서 서로 통신하게 됩니다. 웹에서도 이 HTTP를 이용해 페이지를 주고 받습니다. 익스프레스가 웹 프레임웍이긴 하지만 똑같은 HTTP 기반의 API 서비스를 개발하는 것이기 때문에 API 서버에서도 사용하는 것입니다.
또한 프레임웍을 사용하게 되면 노드로만 코드를 작성하는것 보다 훨씬 빠른시간에 효율적으로 서버를 개발할수 있는 이점도 있습니다.

주요 개념

익스프레스는 크게 네 가지 부분으로 이해한다.

  • Application

불러온 익스프레스 객체에는 하나의 함수가 할당되는데 그 함수를 실행하면 익스프레스 객체가 생성된다. 익스프레스 클래스를 이용해 익스프레스 객체를 만든다고 생각하면 된다. 이것을 익스프레서 어플리케이션 (Application)이라고 한다.

  • Request

콜백함수에서 전달해 주는 1번째 파라미터 req는 익스프레스 요청 (Request) 객체라고 한다. 요청 객체는 말 그대로 서버로 요청한 클라이언트의 대한 정보를 담고 있다. 하나의 객체 형태로 되어 있고 키와 함수들로 구성되어 있다.

+ req.params: url 파라미터 정보를 조회
+ req.query: 쿼리 문자열을 조회
+ req.body: 요청 바디를 조회
  • Response

콜백함수에서 전달해 주는 2번째 파라미터 res는 익스프레스 응답 객체 (Response)라고 한다. 응답 객체는 요청한 클라이언트에게 응답하기 위한 함수들로 구성된 객체로, 아래 함수들을 사용한다.

+ res.send()
+ res.json()
+ res.status()
  • Routing

어플리케이션을 이용해 라우팅 조직을 만들 수 있지만 익스프레스에는 별도로 Router()* 클래스를 제공한다. 라우터 클래스를 이용하면 라우팅 조직을 좀 더 구조적으로 만들 수 있다.

Restful API

image.png

서버 데이터를 구조적으로 사용하기 위한 API 디자인을 REST API라고 한다.

클라이언트측 REST API

  • GET - 조회하다
  • POST - 생성하다
  • PUT - 갱신하다
  • DELETE - 삭제하다

서버측 REST API

클라이언트측에서 요청에 대한 서버의 응답은 두 부분으로 구분할 수 있다.
헤더와 바디, 바디는 Json 타입으로 응답하는 것이다. 헤더의 상태코드 (Status Code)를 잘 사용하면 다양한 정보를 담아 클라이언트에게 전송할 수 있다.

응답 헤더의 상태코드는 세 자리 정수로 되어있는데 크게 세 분류가 있다.

  • 2XX - 성공

    • 200 - Success 대부분의 성공 응답에 200 코드를 사용
    • 201 - Created POST 메소드로 요청시 서버쪽에서 자원 생성에 성공하면 201 코드를 클라이언트로 응답
    • 204 - No Content 서버에서 성공했는데 응답할 바디가 없는 경우
  • 4XX - 클라이언트 요청 에러

    • 400 - Bad Request 클라이언트에서 파라미터를 포함해 서버 API를 요청하는데 파라미터가 잘못되었을 경우 응답하는 코드
    • 401 - Unauthorized 인증이 필요한 API에 대해 인증되지 않은 요청일 경우
    • 403 - Fobbiden 401과 유사하면서 사용 방법에 대한 해석은 개발자마다 다르다
    • 404 - Not found 조회할 자원이 서버에 없는 경우 응답하는 코드
    • 409 - Conflict 클라이언트에서 POST메소드로 서버에서 자원 추가를 요청했을 때 이미 그 자원이 서버에 있어서 자원을 추가할 수 없는 경우.
  • 5XX - 서버 응답 에러

    • 500 - Internal Server Error 서버에서 클라이언트 요청 처리 중 에러
    • 503 - Service Unavailable 서버가 과부하 상태 / 점검 상태 일시적으로 요청 처리 불가
    • 504 - Gateway Timeout 서버를 통하는 게이트웨이에 문제 발생하여 시간 초과
    • 505 - HTTP Version Not Supported 해당 HTTP 버전에서는 지원되지 않는 요청임을 알림

First server

이제 express를 설치하고 서버를 만들어 보자.

image.png

npm init --yes
npm i express

const express = require("express");
const app = express();

app.get();
app.post();
app.put();
app.delete();
app.get("/api/courses", (req, res) => {
  res.send([1, 2, 3]);
});

app.listen(3000, () => console.log("Listening on port 3000..."));

image.png

node index.js 를 터미널에서 실행시키면 포트 3000서버가 실행된다.

image.png

Hello World가 잘 나타나는 것을 확인할 수 있다.

image.png

/api/courses 로 url을 변경시키면 send() 로 내보낸 배열이 잘 나타난다.

Nodemon

파일을 수정하고 확인할 때 마다 터미널에서 node index.js 명령어를 반복적으로 입력해 확인해야 하는 불편함을 해결해주는 모듈인 nodemon을 설치해준다.

nodemon은 디렉토리의 파일 변경이 감지되면 노드 응용 프로그램을 자동으로 다시 시작하여 node.js 기반 응용 프로그램을 개발하는 데 도움이되는 도구입니다.

npm i -g nodemon

nodemon index.js

image.png

Route Parameters

app.get("/api/posts/:year/:month", (req, res) => {
  res.send(req.params);
});

image.png

GET

const courses = [
  { id: 1, name: "courses1" },
  { id: 2, name: "courses2" },
  { id: 3, name: "courses3" }
];
app.get("/", (req, res) => {
  res.send("Hello World");
});

app.get("/api/courses", (req, res) => {
  res.send(courses);
});

// 해당하는 ID를 찾아서 Respon
app.get("/api/courses/:id", (req, res) => {
  const course = courses.find(c => c.id === parseInt(req.params.id));
  if (!course) res.status(404).send(`ID was not found`);
  res.send(course);
});

// PORT
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));

image.png

POST

app.use(express.json());

app.post("/api/courses", (req, res) => {
  const course = {
    id: courses.length + 1,
    name: req.body.name
  };
  courses.push(course);
  res.send(course);
});

Postman에서 확인

image.png

input Validation

Joi를 사용하여 Express 애플리케이션의 입력을 검증한다.

const Joi = require("joi");

app.post("/api/courses", (req, res) => {
  const schema = {
    name: Joi.string()
      .min(3)
      .required()
  };

  const result = Joi.validate(req.body, schema);
  console.log(result);

  if (result.error) {
    // 400 Bad Request
    res.status(400).send(result.error);
    return;
  }

new one 값을 제출 시

image.png

비어있는 값 제출 시

image.png

PUT

  1. Look up the course

  2. If not existing, return 404

    app.put("/api/courses/:id", (req, res) => {
    const course = courses.find(c => c.id === parseInt(req.params.id));
    if (!course) res.status(404).send(`ID was not found`);

    Schema , Validate, // return 400 error

    const schema = {
     name: Joi.string()
       .min(3)
       .required()
    };
    
    const result = Joi.validate(req.body, schema); // validate
    
    if (result.error) {
     // 400 Bad Request
     res.status(400).send(result.error);
     return;
    }
  3. Update course

  4. Return the update course

    course.name = req.body.name;
    res.send(course);
    });

Delete

app.delete("api/courses/:id", (req, res) => {
  // 1. Look up the course
  const course = courses.find(c => c.id === parseInt(req.params.id));
  // 2. Not existing, return 404
  if (!course) return res.status(404).send(`ID was not found`);
  // 3. Delete
  const index = courses.indexOf(course);
  courses.splice(index, 1);
  // 4. Return the same course
  res.send(course);
});

참조

http://webframeworks.kr/tutorials/nodejs/api-server-by-nodejs-02/