Express.js : REST API 구현

김종현·2023년 4월 5일
0

Node.js

목록 보기
8/8

REST API

REST + API 합성어
-REST 아키텍쳐를 준수하는 웹 API로 RESTful API라고 부르기도 한다.
-REST : REpresentational State Transfer. 웹에서 자료를 전송하기 위한 표현 방법에 대한 아키텍쳐. REST를 정확하게 구현하기 위해선 많은 제한조건이 있으나 기본적인 가이드를 따르면 조금 더 좋은 구조의 API 구성이 가능
-API : 어플리케이션 프로그래밍 인터페이스, 서비스나 프로그램 간에 미리 정해진 기능을 실행 할 수 있도록 하는 규약.

1. 기본 가이드 - HTTP Method의 사용

-REST API는 API의 동작을 HTTP method+명사형 URL로 표현함
-/post라는 URL은 '게시글'이라는 자원을 가리킨다고 할 때, get-가져오기, post-새로 만들기, put-수정하기(덮어씌움-다른 데이터 삭제될 가능성 존재), delete-삭제하기 등 HTTP 메소드와 결합하여 API 동작을 정의해야함.
*참고로 patch는 부분수정임.

2. 기본 가이드 - URL 표현법

-REST API URL의 자원은 '복수형으로 표현'되며 하나의 자원에 대한 접근은 '복수형+아이디'를 통해 특정 자원에 접근함
-/posts/는 '게시글 전체'를 칭하는 URL 이라 할 때 /posts/1은 '1번 게시글'을 의미.

3. 기본 가이드 - 계층적 자원

-REST API는 URL을 통해 자원을 계층적으로 표현함
-/users/1/posts는 '1번 유저의 게시글 전체'라는 자원을 의미.

REST API의 구현

목표 : DB 없이 Node.js 모듈 활용, 간단한 메모 작성/수정/삭제/확인기능 API 구현, express-generator 사용하지 않고 MVC 패턴 구현

MVC 패턴
-웹 서비스의 가장 대표적인 프로젝트 구성 패턴
-프로젝트의 기능들을 어떻게 분리할지에 대한 하나의 구성 방법
-Model-View-Controller를 구분하여 프로젝트 구조를 구성
-Node.js의 모듈화를 이용하여 MVC 패턴을 구현할 수 있음
-JSON API를 구현하는 경우, Node.js는 기본적으로 JSON을 처리하는 방법을 가지고 있기 때문에 View는 생략될수있음

(1) Model
-'데이터에 접근하는 기능 혹은 데이터 그 자체'
-'데이터의 읽기, 쓰기는 모델을 통해서만' 이루어지도록 구성

(2) View
-'데이터를 표현하는 기능'
-Controller에 의해 데이터를 전달받고 화면에 표시함

(3) Controller
-Model을 통해 데이터에 접근하여, 처리 결과를 View로 전달하는 기능
-웹서비스에선 주로 라우팅 함수가 해당 기능을 수행

(4) ★구현 사항★
-JavaScript의 'Array 함수 사용하여 데이터 처리 구현'
-router와 route handler를 사용하여 HTTP 요청, 응답처리 구현
-오류처리 미들웨어를 사용하여, 오류를 처리하는 방법 구현
-정의되지 않은 라우팅에 대해 404 오류처리구현

  1. 메모 목록 API 구현
---models/note.js
// 배열 사용하여 노트가 담기는 목록 구현.
// 목록 생성, 수정, 삭제 기능을 여기서 구현.(데이터 처리)
let notes = [
  { 
    id: 1, 
    title: 'first note', 
    content: 'My first note is here.'
  }
];
//메모 목록을 보내주는 함수 'list'
//메모 목록 안에서 id와 title만 object로 만들어 만들어 보내는 함수
exports.list= () =>{
  return notes.map(({ id, title })=>(
    {
    id,
    title,
  }
  ));
}
---routes/notes.js
const { Router } = require('express');
const Note = require('../models/note');
const router = Router();

//메모 목록을 가져옴 : get
router.get('/', (req, res, next) => {
  //리퀘스트 핸들러로 Note.list함수를 써서 노트 목록을 notes라는 이름으로 받음
  const notes = Note.list();
  // 노트 목록을 json으로 전달
  res.json(notes);
});
  1. 메모 상세 API 구현
---models/note.js
// id를 받아 메모 목록안에 전달받은 id와 같은 id가 있는지 찾음
exports.get = (id) =>{
  const note = notes.find(
    (note) => note.id === id
    );
  if (!note) {
    throw new Error('No note found');
  }
  return note;
}
// http Method get임.
router.get('/:id', (req, res, next) =>{
  const id = Number(req.params.id);
  
  try{
    //변환해준 id를 Note.get으로 받아 사용
    const note = Note.get(id);
    res.json(note);
  } catch(e) {
    next(e);
  }
});
  1. 메모 작성 API 구현
---models/note.js
// 메모의 제목과 내용을 인자로 받아서 새로운 메모를 생성, 
//위에 있는 메모 목록의 가장 마지막 요소로 추가하는 함수
exports.create = (title, content) =>{
  // 노트의 가장 마지막 요소를 받아서 그 요소의 id를 lastId라는 변수로 디스트럭팅.
  // 노트 목록은 배열이므로 length-1이 마지막 요소.
  const { id : lastId} = notes[notes.length-1];
  // 새 노트는 마지막 id에 1을 더하고 전달 받은 제목과 내용을 가짐
  const newNote= { 
    id: lastId+ 1, 
    title, 
    content,
  };
  notes.push(newNote);
  return newNote;}
---routes/notes.js
// 작성 API는 자원을 생성하므로 POST 메소드를 사용
router.post('/', (req, res, next) =>{
  // post 메소드는 req.body로 요청값을 받는데 req.body 안에서 title, content로 디스트럭팅,
  const { title, content } = req.body;
  // 디스트럭팅한 값을 Note.create로 넘겨서 그 값을 토대로 메모를 작성하여 목록 마지막에 넣게됨.
  const note = Note.create(title, content);
  // 이렇게 새로 생성 된 노트를 json으로 응답처리함.
  res.json(note);
});
  1. 메모 수정 API 구현
---models/note.js
exports.update = (id, title, content) =>{
  //전달받은 id 값과 id가 일치하는 노트를 index로 지정
  const index = notes.findIndex(
    (note) => note.id === id
  );
  if(index < 0) {
    throw new Error('Note not found for update');
  }
  // 요소 검사 통과시 요소를 객체로 전달 받고(노트 목록의 요소는 객체형태) 이 객체의 제목과 내용을 변경하고 수정 대상의 인덱스에 마지막으로 그 내용을 저장
  const note = notes[index];
  note.title = title;
  note.content = content;
  notes[index] = note;
  return note;
}
---routes/notes.js
// 자원 수정 -> put 메소드 사용.
// path param : 특정 자원 나타냄.
router.put('/:id', (req, res, next)=> {
  const id = Number(req.params.id);
  // body에 전달된 title과 content를 디스트럭팅 해서
  const{ title, content } = req.body;
  
  try{
    const note = 
          // 노트의 업데이트 함수에 전달하고 반환해서
          Note.update(id, title, content);
    // 반환 받은 값을 json화
    res.json(note);
    // update에서 에러를 던지면 여기서 잡아서 에러 미들웨어 실행
  } catch(e) {
    next(e);
  }
});
  1. 메모 삭제 API 구현
---models/note.js
exports.delete = (id) => {

  // some : 특정 조건에 맞는 요소가 있는지 찾아 t/f 반환
  // 요소가 없다면 에러 투척
  if(!notes.some((note) => note.id === id)) {
    throw new Error (
      `Note not found for delete`
      );
  }
  // 삭제 대상 외의 것들을 모아서 노트목록 재구성
  notes = notes.filter(not => note.id !== id);
  
  //삭제시 리턴값 필요 없음
  return;
}
---routes/notes.js
router.put('/:id', (req, res, next)=> {
  const id = Number(req.params.id);
  
  try{
    Note.update(id, title, content);
    res.json(note);
  } catch(e) {
    next(e);
  }
});
  1. JSON 데이터 처리 미들웨어 사용
    -위에서 작성한 API들은 바로 사용하지는 못하므로 JSON 처리 필요.
    (post와 put에서 res.send를 json으로 했음)
    -express.js는 기본적으로 HTTP body에 전달되는 JSON 데이터를 처리하지 못함
    -express에서 기본적으로 제공해주는 express.json() 미들웨어를 app에 연결해야 제이슨 데이터 사용 가능
---index.js
app.use(express.json());
  1. 오류처리 미들웨어 구현
    -가장 마지막 미들웨어로 오류 처리 미들웨어 적용해 모든 라우팅에 공통적인 오류처리 로직을 적용.
---index.js
app.use((err, req, res, next) =>{
  res.status(500);
  
  res.json({
    result: 'fail',
    error: err.message,
   });
});
  1. 정의되지 않은 라우팅에 404 오류 처리하기
    -모든 라우팅이 적용된 이후에 사용되는 미들웨어는 설정된 경로가 없는 요청을 처리하는 라우트 핸들러로 동작함.
    -express는 기본적인 404 페이지를 가지나 직접 json 처리등이 필요한 경우 이와 같이 핸들러를 추가해야함.
---index.js
app.use((req, res, next) =>{
  res.status(404);
  res.send({ result: 'fail', 
            error: `Page not found ${req.path}`
           });
});

Postman 사용하기

-API를 테스트할 수 있는 도구로 HTTP 요청을 손쉽게 작성하여 테스트 할 수 있게 도움.
-또한 API를 문서화할 수 있는 기능 및 다양한 도구를 제공함.

  1. API 문서화
    collection 만들기
    api request 만들기
    api와 collection에 document 작성
    전체 문서 확인

  2. API 테스트
    HTTP method 설정
    query param 사용
    path variable 사용
    body 사용

profile
나는 나의 섬이다.

0개의 댓글