[JavaScript] REST API 백엔드에서 만들고 프론트엔드에서 사용하기

MINEW·2023년 4월 13일
3

클라이언트 vs 서버

  1. 클라이언트
    - 서비스에 관한 요청을 보내는 프로그램 or 컴퓨터를 클라이언트라고 합니다.
    - 클라이언트는 서버로 요청을 보냅니다.

  2. 서버
    - 웹 브라우저나 모바일 앱으로 어떤 요청을 보냈을 때, '응답을 해주는 컴퓨터'를 서버라고 합니다.
    - 요청을 받은 서버는 그에 알맞은 응답을 클라이언트에 해줍니다.

  3. 클라이언트 -> 서버 -> 클라이언트 -> 화면
    1) 클라이언트는 서버로 요청을 보냅니다.
    2) 요청을 받은 서버는 그에 알맞은 응답을 (클라이언트에) 해줍니다.
    3) 클라이언트는 응답을 바탕으로 화면을 구성합니다.

  4. 라우팅
    - 백엔드에서 라우팅: 클라이언트의 요청에 대해, 서버가 그 URL을 보고, 적절하게 분기하여 처리하여 주는 것 입니다.
    - 프론트엔드에서 라우팅: 어떤 url로 들어왔을때, 특정 페이지를 보여주는 것 입니다. (url에 따라 알맞은 콘텐츠(UI)를 전달해주는 기능)

  5. Web Server vs API Server
    - Web Server: 화면을 그리는데 필요한 재료를 response의 body에 담아서 보내줍니다.
    - API Server: 클라이언트가 요청한 작업을 수행하고, 그 결과를 JSON 등의 형식으로 response의 body에 담아서 보내줍니다.

REST API

  1. REST API 란?
    - HTTP url을 통해 자원을 명시하고, HTTP method를 이용해서 자원 데이터를 교환하는 형식을 따르는 api를 REST API라고 부릅니다.
    - 성능의 향상 보다는 규칙을 통한 API 이해도와 호환성을 높이기 위해서 사용합니다.
    - 유지보수 및 코드의 재사용성을 높일 수 있습니다.

  2. HTTP method
    - GET, POST, PUT, PATCH, DELETE 메서드를 제공합니다.

  3. GET
    - 서버에서 데이터를 받아오기 위해서 사용되는 메서드 입니다.
    - 데이터를 전송하는 방식은 url주소의 쿼리(스트링)에 데이터(data)를 포함하여 전달하는 방식입니다.
    - 주소에 데이터가 보이기 때문에 보안에 중요한 데이터를 담아 보내면 안 됩니다.
    - GET의 경우, 서버에 동일한 요청을 여러번 보내더라도 항상 동일한 응답이 돌아와야 한다는 것을 의미합니다.
    - 브라우저는 GET으로 받아온 데이터를 캐싱합니다. GET은 URL에 내가 받아올 데이터를 명확하게 적어주기 때문에, 정확한 캐싱이 가능해 GET 방식만 캐싱을 합니다. 때문에 다른 HTTP 메서드에 비해 더 빠르게 작동할 수 있습니다.
    - GET 방식은 URL에 모든 자원 경로를 담아서 전송하기 때문에, 다른 사람에게 url 공유 만으로도 해당 자원을 공유할 수 있습니다.
    - GET 방식은 url 길이의 제한이 있기 때문에 데이터 제한이 있다고 알려져 있습니다. 그러나, 옛날 브라우저에서 제한하고 있던 사항이고 지금 대다수 버전의 브라우저에는 해당 사항이 없습니다.

  4. POST
    - 서버에서 데이터를 수정하거나, 새로 추가할 때 사용하는 메서드 입니다. 주로 추가할 때 사용합니다.
    - 데이터를 전송하는 방식은 데이터(data)를 별도로 첨부하여 전달하는 방식입니다.
    - 데이터는 body에 담아서 보내기 때문에, 데이터가 직접적으로 노출되지않아 GET 방식 보다는 안전합니다.
    - POST의 경우, 서버에 동일한 요청을 여러번 보내더라도 응답 결과가 다를 수 있다는 것을 의미합니다.
    - POST 방식은 바디 부분에 데이터를 넣어서 서버에 전송해야 하기 때문에 url 공유 만으로는 원하는 자원을 공유할 수 없습니다.

  5. PUT
    - 서버에서 데이터를 수정하거나, 새로 추가할 때 사용하는 메서드 입니다. 주로 수정할 때 사용합니다.
    - POST와 겹치기 때문에 PUT을 사용하는 곳도 있고 POST로 통일해서 사용하는 곳도 있습니다.
    - POST와 PUT 모두 서버에서 데이터를 수정하거나 새로 추가할 때 사용하는 메서드 이지만, 똑같은 요청이 POST로 2번 날아오면, POST는 2개의 새로운 자원을 생성하는 반면 (즉, 게시판에 같은 내용의 글쓰기 요청을 2번 날리면, 2개의 게시물이 등록),
    - 똑같은 요청이 PUT으로 2번 날아오면, PUT은 식별자를 포함하기 때문에 존재하지않는 식별자로 요청을 하게되면 이때는 POST와 동일하게 새로운 자원을 생성하지만, 두번째 요청에선 이미 첫번째 요청에서 생성된 자원이 있으므로 자원을 생성하지않고 교체하게 됩니다.

  6. PATCH
    - 서버에서 데이터를 수정할 때 사용하는 메서드 입니다.
    - PUT 과 PATCH 모두 리소스(자원)를 업데이트하는 메서드 이지만, PUT은 data객체의 모든요소를 업데이트 해야하는 반면 PATCH는 data객체의 일부만 업데이트 하는것이 가능합니다. 만약, PUT으로 일부의 값만 전달하면 전달하지 않은 값은 null 등으로 업데이트 됩니다.
    - 그러나, 백엔드에서 PATCH를 사용하지 않고 PUT으로 PATCH 기능을 대신 사용하는 경우가 빈번합니다.

  7. DELETE
    - 특정 데이터를 서버에 삭제 요청을 보낼때 쓰이는 메서드 입니다.

REST API 백엔드

  1. 컨트롤러
// controllers/companyController.js
const db = require('../models');
const { companies } = db;

// SELECT (조회하기)
const getAllCompanies = async (req, res) => {
  const companyLists = await companies.findAll({
    attributes: {
      exclude: ['email', 'password']
    }
  });
  res.send(companyLists);
};

const getCompany = async (req, res) => {
  const { id } = req.params;

  const company = await companies.findOne({
    attributes: {
      exclude: ['email', 'password']
    },
    where: { id }
  });

  if (company) res.send(company);
  else res.status(404).send({ message: '존재하지 않는 회사 ID 입니다' });
};

// INSERT (추가하기)
const postCompany = async (req, res) => {
  const newCompany = req.body;

  const company = await companies.create(newCompany);
  res.send(company);
};

// UPDATE (갱신하기)
const putCompany = async (req, res) => {
  const { id } = req.params;
  const newInfo = req.body;

  const result = await companies.update(newInfo, { where: { id } });
  if (result[0]) {
    const company = await companies.findOne({
      attributes: {
        exclude: ['email', 'password']
      },
      where: { id }
    });
    res.send(company);
  } else res.status(404).send({ message: '존재하지 않는 회사 ID 입니다' });
};

// DELETE (삭제하기)
const deleteCompany = async (req, res) => {
  const { id } = req.params;

  const deleteCompanyCount = await companies.destroy({ where: { id } });
  if (deleteCompanyCount) res.send({ message: `회사 ID_${id} 삭제되었습니다`});
  else res.status(404).send({ message: '존재하지 않는 회사 ID 입니다' });
};

module.exports = {
  getAllCompanies,
  getCompany,
  postCompany,
  putCompany,
  patchCompany,
  deleteCompany,
};
  1. 라우터
// routers/companyRouter.js
const companyController = require('../controllers/companyController');
const {
  getAllCompanies,
  getCompany,
  postCompany,
  putCompany,
  patchCompany,
  deleteCompany,
} = companyController;
const router = require('express').Router();

router.get('/', getAllCompanies);
router.get('/:id', getCompany);
router.post('/', postCompany);
router.put('/:id', putCompany);
router.delete('/:id', deleteCompany);

module.exports = router;
  1. 데이터베이스 연결하기
// models/index.js
const { Sequelize, Op, QueryTypes } = require('sequelize');

const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const {
  username,
  password,
  database,
  host,
  dialect,
} = config;
const sequelize = new Sequelize(database, username, password, { host, dialect });

const db = {};
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.Op = Op;
db.QueryTypes = QueryTypes;

db.companies = require('./companies')(sequelize, Sequelize.DataTypes);

sequelize
  .sync({ force: false })
  .then(() => {
    console.log('성공 ~');
  })
  .catch((error) => {
    console.log('실패 ~');
    throw error;
  });

module.exports = db;
  1. 서버 연결하기
// app.js
const express = require('express');
const cors = require('cors');
const app = express();
require('dotenv').config();
const port = process.env.PORT || 3000;

app.use(express.static('public'));
app.use(cors());
app.use(express.json());
app.use((req, res, next) => {
  console.log(req.query);
  next();
});

const companyRouter = require('./routers/companyRouter');

app.use('/companies', companyRouter);

app.listen(port, () => {
  console.log(port, 'Server is listening...');
});

REST API 프론트엔드

  1. api 호출하기
// apis/companiesApi.js
import axios from 'axios';

const apiRoot = axios.create({
  baseURL: 'http://localhost:3000/companies',
});

export const getAllCompaniesApi = async () => {
  try {
    const { data } = await apiRoot.get('');
    return data;
  } catch (error) {
    throw error;
  }
};

export const getCompanyApi = async (companyId) => {
  try {
    const { data } = await apiRoot.get(`/${companyId}`);
    return data;
  } catch (error) {
    throw error;
  }
};

export const postCompanyApi = async () => {
  try {
    const { data } = await apiRoot.post('', {
      "회사명": "네이버",
      "국가": "한국",
      "지역": "판교",
      "email": "Naver@gmail.com",
      "password": "naver123",
    },
    {
      headers: {
        'Content-Type': 'application/json',
      },
    });
    return data;
  } catch (error) {
    throw error;
  }
};

export const putCompanyApi = async (companyId) => {
  try {
    const { data } = await apiRoot.put(`/${companyId}`, {
      "회사명": "쿠팡",
      "지역": "서울",
    },
    {
      headers: {
        'Content-Type': 'application/json',
      },
    });
    return data;
  } catch (error) {
    throw error;
  }
};

export const deleteCompanyApi = async (companyId) => {
  try {
    await apiRoot.delete(`/${companyId}`,
    {
      headers: {
        'Content-Type': 'application/json',
      },
    });
  } catch (error) {
    throw error;
  }
};
  1. api 데이터 사용하기
export const companyRegionList = selector({
  key: 'companyRegionList',
  get: async () => {
    const data = await getAllCompaniesApi();

    const seoulCompaniesList = data.filter((company) => company.지역 === '서울');
    const busanCompaniesList = data.filter((company) => company.지역 === '부산');

    return {
      seoulCompaniesList,
      busanCompaniesList,
    };
  },
});

profile
JS, TS, React, Vue, Node.js, Express, SQL 공부한 내용을 기록하는 장소입니다

0개의 댓글