IT_Study/Web

Back-End (6) : REST API, HTTP Method, Postman, Morgan, Multer를 이용한 서버 다루기

__Vivacé__ 2023. 3. 3. 12:07

REST API(Representational State Transfer API)

클라이언트와 서버 간의 통신을 위한 인터페이스

  • 서버 측에서 데이터와 기능을 제공
  • 클라이언트 측에서 이를 요청하여 사용

 

*인터페이스 : 사용자가 쉽게 동작 및 사용하는데 도움을 주는 시스템

*RESTful API : REST API 설계 방식에 맞게 설계한 API


HTTP METHOD

 

1.GET : 데이터를 읽거나 검색

  • 웹사이트를 불러오는 경우도 GET : [네트워크] - [All] 에서 보면 GET으로 대부분 가져오는 걸 볼 수 있음

 

2. POST : 새로운 리소스를 생성할 때 사용

  • 로그인, 게시글 작성 등
  • 규정하기 애매한 것들은 POST를 활용한다고 보면 됨

 

3. PATCH : 일부 데이터를 수정할 때

 

4. PUT : 보통 전체 데이터를 수정할 때 사용

 

5. DELETE : 삭제 요청 시 사용

 

 

위 METHOD대로 요청을 안 해도 동작은 하지만, RESTful해지지는 않음

 


올바른 API 설계 방식 (in CSR)

GET, POST, PATCH, DELETE가 전부 사용가능한 환경(CSR)에서의 RESTful API 디자인 방식

 

1. HTTP METHOD → 동사
2. 설계 URL은 명사 (동작을 안 넣는 것이 좋다)

 

1. GET : 유저 전체 정보 가져오기

  • O : GET /users
  • X : GET /user/all/get

 

2. GET : 특정 유저 조회

  • O : GET /users/:id → id에 해당되는 user를 가져온다.
  • X : GET /users/:id/get-information

 

3. POST : 유저 등록

  • O : POST /users
  • X : POST /user/adduser

 

4. PATCH : 특정 유저 수정

  • O : PATCH /users/:id
  • X : PATCH /users/:id/update

 

5. DELETE : 특정 유저 삭제

  • O : DELETE /users/:id
  • X : DELETE /users/:id/delete

 


GET, POST만 가능한 SSR 환경 - HTML 요소 사용

  • GET : 링크<a tag>를 클릭하여 URL로 이동하면 GET 요청
  • POST : 서버로 데이터를 제출<form tag>하여 처리

 


GET, POST 이용하기

 

Pre-setting

1. EC2 인스턴스 활성화

 

2. local에 DB 연결정보 세팅

const mysql = require('mysql2/promise');

const pool = mysql.createPool({
    //AWS EC2 IP
    host: "12.34.56.78",
    //Mysql 유저 이름
    user: "my_sql_id",
    // 비밀번호
    password: "my_sql_password",
    // DB 및 schema 이름
    database: "my_database",
    
    // Basic options
    waitForConnections: 10,
    connectionLimit: 10,    
    queueLimit: 0
})

module.exports = pool;

 

3. Express를 이용, EC2에 있는 MySQL 서버 연결

const express = require('express');
const app = express();
const cors = require('cors');

// index 생략 가능
const pool = require('./db/index');

const PORT = 8080;

// app.use 전역 (모든 요청마다 적용)
app.use(cors());

// body에 데이터를 받아오기 위해서는 setting이 꼭 필요함
// json 형식의 데이터를 받아오기 위해 필요한 세팅
app.use(express.json())

// GET /api/menus
// MySQL에 query를 날려 값을 가져올 것
app.get("/api/menus", async(req, res) => {
    // db
    try {
        // pool 가져온 후 진행
        const data = await pool.query("select * from menus");
        return res.json(data[0]);        
    } 
    
    catch (error) {
        console.log(error);
        return res.json(error);
    }

})

// POST /api/menus
// request.body 안에 있는 menu_name, menu_description을 DB에 query로 날릴 것
app.post("/api/menus", async(req, res) => {
    try {
        // 1st method : [?] 부분은 변수로 넣을 수 있다. <-- 추천하는 방식

        const data = await pool.query(`INSERT INTO menus (menu_name, menu_description)
        VALUES (?, ?)`, [req.body.menu_name, req.body.menu_description])
        
        return res.json(data[0]);

        // 2nd method : [?] 대신 바로 변수로 넣기

        // const data = await pool.query(`
        // INSERT INTO menus (menu_name, menu_description)
        // VALUES ("${req.body.menu_name}", "${req.body.menu_description}")
        // `)
        
        // return res.json(data[0]);

    }
    
    catch (error) {
        console.log(error);
        return res.json(error);    
    }

});

app.listen(PORT, ()=> console.log(`${PORT} 서버 기동 중`));

 

4. 임의의 client 생성

		// 비동기 처리를 하기 위한 axios 소스 가져오기

        const button = document.querySelector("button");
        button.addEventListener('click', async function(){
            
            // text 값 받아오는 법
            // 1. input_data -> value
            // 2. 그 외 html -> textContent

            const menu_name = document.querySelector("#menu_name").value;
            const menu_description = document.querySelector("#menu_description").value;

            // post 요청 보내기

            const result = await axios.post("<http://localhost:8080/api/menus>", {
                menu_name : menu_name,
                // key랑 value가 같으면 생략이 가능
                menu_description
            })
        })
        
        // GET TEST : result가 서버의 log에 찍힘

        async function getTest(){
            const result = await axios.get("<http://localhost:8080/api/menus>");
            console.log(result);
        }

        getTest();

        // POST TEST : result가 서버의 log에 찍힘
        
        async function getPostTest(){
            const result = await axios.post("<http://localhost:8080/api/menus>");
            console.log(result);
        }

        getPostTest();

    

 


URL 분석

 

1. Path - params : 로 시작

// id 1 ~ 10까지 다 받아오고 싶은데 일일이 다 작성할 수가 없다.
// ":" 이용 <-- : 로 들어오는 값들은 req.params로 조회가 가능

// GET <http://localhost:8080/api/menus/4> 를 했을 시

app.get("/api/menus/:id", async(req, res) => {
    console.log(req.params);  // {id : '4'}
    console.log(req.params.id);  // 4 

    try {
        const data = await pool.query("select * from menus where menu_id = ?",
        [req.params.id]);
        return res.json(data[0][0]); // menu_id가 4인 record를 가져 옴
    } 

    catch (error) {
        console.log(error);
        return res.json(error);
    }
})

 

2. Query - query는 ?로 시작

 

Query vs Params vs Body

query, params

GET, POST, PUT, DELETE, PATCH 때 사용 가능

body  // 뒤 쪽에서 다룸

POST PUT PATCH 에서만 가능

 


 

Postman

API 개발 및 테스트를 위한 협업 플랫폼, RESTful API 테스트를 위한 다양한 기능들을 포함

 

사용법

1. GET

// index.js 코드 일부

app.get("/user/:id", async (req, res) => {
    try {
        
        return res.json({get_:true, query : req.query, params : req.params});
    }

    catch (error) {
        return res.json({get_:false});
    }
})

  1. 주소창에 local 서버 주소 입력
  2. 위 화면처럼 SEND 시,
    • req.query : Qeury로 넣어 준 KEY, VALUE 값이 저장되어 있음
    • req.params : 주소에서 얻은 id의 값이 저장되어 이음

2. POST

app.post("/user", async (req, res) => {
    try {
        if (req.body.id && req.body.password)
            return res.json({signup: true, id: req.body.id, password: req.body.password})
        else
            return res.json({signup:false});
        } 
    catch (error) {
        return res.json({signup:false});
    }
})

 

 

 

 

 

**[POST]**로 변경 - [Body] 탭 - [raw] 클릭 - [JSON] 으로 변경

이후에 json 형식대로 값을 넣고, send하면 req.body에서 값을 받을 수 있음

 

 


Morgan

Node.js 애플리케이션에서 HTTP 요청 로그를 기록하는 데 사용되는 미들웨어

HTTP 요청 시 Morgan이 나타내는 log

 

HTTP Response Status Code

  • 1xx(Information)
    • 요청을 받았으며 프로세스를 진행하는 상태
    • 100(요청 후 대기중)

 

  • 2xx(Success)
    • 요청을 성공적으로 받았으며 인식했고 진행한 상태
    • 200(요청 성공), 201(생성 완료)

 

  • 3xx(Redirection)
    • 클라이언트는 요청을 마치기 위해 추가적으로 동작을 취해줘야 한다.
    • 301(새위치로 영구적으로 이동), 302(임시이동)

 

  • 4xx(Client Error)
    • 클라이언트 단에서 에러가 발생한 경우 해당 오류가 발생한다.
    • 400(잘못된 요청), 401(권한 없음), 404(찾을 수 없음)

 

  • 5xx(Server Error)
    • 서버 측에 에러가 발생 했을 경우 해당 상태코드가 나타난다. (보통 critical한 issue)
    • 500(내부 서버 오류), 503(서버를 사용 할 수 없음), 504(시간 초과)

 

 


정적 파일

직접 변화시키지 않는이상 변하지 않는 파일

 

multer

Node.js에서 파일 업로드를 처리하는 데 사용되는 미들웨어

npm i express morgan multer

 


클라이언트에서 정적 파일을 서버에 저장하기

// index.js 내용

const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const multer = require('multer');

const PORT = 8080;
const app = express();

app.use(cors());
app.use(morgan("dev"));
app.use(express.json());

// 정적 서비스
**// '/public' router로 오게 되면, 'public' folder에 있는 정적 파일을 보여 준다는 의미**
// app.use("/public", express.static("public"));

const upload = multer({
    // 저장경로 설정
    storage: multer.diskStorage({
        **// public 경로에 저장할 예정**
        destination: (req, file, done) => {
            done(null, "public/")
        },
        **// 저장할 파일 이름 설정**
        filename : (req, file, done) => {
            done(null, file.originalname); // 원래 이름으로 가져옴
        }
    }),

    // limits: { fileSize: 5 * 1024 * 1024 },
});

// file upload는 무조건 POST

// 무조건 app.use를 거쳐간다. 전역적용 app.use 부분적용
// upload 전 middleware를 거치고 여기로 온다는 의미 

// upload.single() : 1개의 파일을 받는다.
app.post("/api/file", upload.single('file'), (req, res) => {
    console.log(req.files);
    console.log(req.file);
    return res.json({test:"OK"});
})

app.listen(PORT, () => console.log("this server listening on " + PORT));

 

Postman에서 localhost로 file post 요청 보내는 예시

[Body][form-data][KEY][File] 눌러서 진행

 

 

 

중요) KEY 내용이랑, index.js 파일 내 upload.single()의 argument랑 값이 똑같아야 함

 

성공 시 화면