Super Kawaii Cute Cat Kaoani
본문 바로가기
🏃‍♀️ 대외활동/Codeit Boost-Node.js

6주차 스터디 자바스크립트 백엔드 개발 (1) [코드잇 부스트 백엔드 스터디]

by wonee1 2024. 6. 28.
728x90

 

 

<자바스크립트 백엔드 개발>

 

Express로 API 만들기

 

 

 

 

프론트 엔드 : 웹사이트 화면 + 인터랙티브한 요소들

백엔드: 필요한 데이터 관리+ 프론트엔드에 전성

 

 

API

💡 HTTP 메소드+ URL= 엔드포인트

 

 

 

Node.js

• 웹 브라우저 바깥에서 자바스크립트를 실행하는 환경

• API를 제공하는 프로그램은 웹 브라우저 바깥에서 실행됨

 

Express

• 자바스크립트 백엔드 개발 쪽에서 가장 유명한 라이브러리

• 리퀘스트와 리스폰스를 쉽게 다룰 수 있음

• 다른 라이브러리(파이썬 장고, 자바 스프링)에 비해 특정 구조를 고집하지 않고 최소한의 기능들만 제공

 

 

mongoDB

• 데이터를 테이블에 저장하지 않고 문서 형태로 저장

• 문서 하나= 도큐먼트, 문서의 모음= 컬렉션

• 셋업 과정도 간단하고 도큐먼트를 다루는 방법이 직관적임

 


 

 

Express 라우트

 

import express from 'express';

const app = express();

app.get('/hello',(req, res)=>{}); // /hello 리퀘스트가 들어오면 뒤에 콜백함수가 실행됨 
//첫번째 파라미터는 url 경로 두번째 파라미터는 실행할 콜백함수(리퀘스트 핸들러)

 

• 첫번째 파라미터는 url 경로

•  두번째 파라미터는 실행할 콜백 함수(리퀘스트 핸들러)

 

 

 

 

 

 

Rest Client로 리퀘스트를 보내는 방법

 

• Send Request 버튼을 누르고 확인한다 

• 바꾼 값이 바로 적용되지 않는다 따라서 서버를 재실행해야한다

 

 

💡Nodemon을 사용하면 서버를 재실행하지 않아도 변경을 즉각적으로 확인할 수 있다

 

  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
   }

 

 

 

GET 리퀘스트 처리하기
import express from 'express';
import tasks from './data/mock.js'
const app = express();

app.get('/tasks',(req, res)=>{
    res.send(tasks)

}); // /hello 리퀘스트가 들어오면 뒤에 콜백함수가 실행됨 

app.listen(3000,()=>console.log('Server Stared'))

 

☑️res.send의 아규먼트로 전달하는 것은 자바스크립트 객체 배열

리스폰스로 돌아오는 것은 json 문자열이다 (res.send에서 변환된 것)

 

 

JSON

json은 프로퍼티에 따옴표를 붙인다

data 객체도 문자열로 전달된다

 

 

 

예) Subscription 목록 조회하기 

import express from 'express';
import subscriptions from './data/mock.js';

const app = express();

app.get('/subscriptions',(req, res) => {
  res.send(subscriptions);
});

app.listen(3000, () => console.log('Server Started'));

 

 

 

쿼리스트링 처리하기

 

URL에서 물음표 뒤에 오는 부분을 쿼리스트링이라고 한다

Get 리퀘스트에서 추가 조건을 추가할 때 주로 사용한다

 

 

import express from 'express';
import tasks from './data/mock.js'
const app = express();

app.get('/tasks',(req, res)=>{
/**
 * 쿼리 파라미터
 *  - sort: 'oldest'인 경우 오래된 테스트 기준, 나머지 경우 새로운 테스크 기준
 * -count: 테스크 개수 
 */
const sort=req.query.sort;
const count=Number(req.query.count);

const compareFn =
    sort === 'oldest'
    ? (a,b) => a.createAt - b.createdAt
    : (a,b) => b.createAt - a.createdAt;

let newTasks = tasks.sort(compareFn);

if(count){
    newTasks=newTasks.slice(0,count); 
}

res.send(newTasks); 

}); 

app.listen(3000,()=>console.log('Server Stared'))

 

  • sort === 'oldest':
    • sort 변수가 'oldest'와 같은지 확인한다
  • 삼항 연산자 (? :):
    • 조건 ? 참일 때 실행할 코드 : 거짓일 때 실행할 코드
    • sort 변수가 'oldest'와 같다면 참일 때 실행할 코드 부분이 실행되고, 그렇지 않다면 거짓일 때 실행할 코드 부분이 실행된다
  • 참일 때 실행할 코드:
    • (a, b) => a.createAt - b.createdAt
    • 이 함수는 두 객체 a와 b의 createAt 속성을 비교하여 오름차순으로 정렬한다
    즉, a의 createAt 값이 b의 createAt 값보다 작으면 a가 b보다 앞에 오도록 정렬한다

 

  • 거짓일 때 실행할 코드:
    • (a, b) => b.createAt - a.createdAt
    • 이 함수는 두 객체 a와 b의 createAt 속성을 비교하여 내림차순으로 정렬한다. 즉, b의 createAt 값이 a의 createAt 값보다 작으면 b가 a보다 앞에 오도록 정렬한다

 

 

예) Subscription 목록 쿼리스트링 처리하기

 

import express from 'express';
import subscriptions from './data/mock.js';

const app = express();

app.get('/subscriptions', (req, res) => {
  /** 쿼리 파라미터
   *  - sort: 'price'인 경우 높은 요금순, 그 외의 모든 경우 최신으로 생성된 순서
   */
  const sort = req.query.sort;

  const compareFn =
    sort === 'price'
      ? (a, b) => b.price - a.price
      : (a, b) => b.createdAt - a.createdAt;

  res.send(subscriptions.sort(compareFn));
});

app.listen(3000, () => console.log('Server Started'));

 

 

다이나믹 URL 처리하기

 

url이 일정하지 않고 계속 바뀌는 url

 

다이나믹 URL을 처리하는 방법

• ID 값마다 URL 을 적을 순 없기 때문에 /tasks/:id 이런 식으로 다이나믹으로 처리한다

• URL 파라미터는 리퀘스트의 Params로 전달된다

 

app.get('./tasks/:id', (req,res)=>{
    const id= Number(req.params.id); 
    const task= tasks.find((task)=>task.id===id);
    if(task){
        res.send(task); 
    }else{
        res.status(404).send({message : 'Cannot find given id.'}); 
    }
    
});

 

 

params란?

• params는 Express.js에서 경로 매개변수(Route Parameters)를 포함하는 객체

• 경로 매개변수는 URL의 특정 부분을 동적으로 바꿀 수 있게 해주는 부분 Express.js는 이러한 매개변수를 req.params 객체에 저장하여 사용할 수 있도록한다

 

 

 

예) Subscription 상세 조회하기

 

GET /subscriptions/:id 리퀘스트가 들어오면 id에 해당하는 Subscription 객체를 리스폰스

 

app.get('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);
  if (subscription) {
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});

 

 

POST 리퀘스트 처리하기

 

###
POST http://localhost:3000/tasks
Content-Type: application/json

{
    "title":"강아지 산책",
    "description": "강아지랑 30분 산책하기"

}

 

• 엔드타입 아래 content 타입을 설정해준다

• 그 아래 json 바디를 작성해준다 (이때 한줄을 비우고 써줘야한다)

 

 

JSON을 자바스크립트 객체로 변환하는 과정을 파싱이라고 한다

→ 파싱을 하려면 express.json를 사용해야한다!

 

 

app.post('/tasks', (req,res)=>{
    const newTask= req.body;
    const ids= tasks.map((task)=>task.id);
    newTask.id=Math.max(...ids)+1;
    newTask.isComplete=false;
    newTask.createAt =new Date();
    newTask.updateAt=new Date(); 

    tasks.push(newTask);
    res.status(201).send(newTask);
});

 

 

스프레드 연산자는 세 개의 점(...)으로 이루어져 있으며, 배열이나 객체의 모든 요소나 속성을 개별적으로 분리하여 사용할 수 있게해준다

 

 

예) Subscription 생성하기

 

import express from 'express';
import subscriptions from './data/mock.js';

const app = express();
app.use(express.json());

app.get('/subscriptions', (req, res) => {
  /** 쿼리 파라미터
     *  - sort: 'price'인 경우 높은 요금순, 그 외의 모든 경우 최신으로 생성된 순서
     */
  const sort = req.query.sort;

  const compareFn =
    sort === 'price'
      ? (a, b) => b.price - a.price
      : (a, b) => b.createdAt - a.createdAt;

  res.send(subscriptions.sort(compareFn));
});

app.get('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);
  if (subscription) {
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});

function getNextId(arr) {
  const ids = arr.map((elt) => elt.id);
  return Math.max(...ids) + 1;
}

app.post('/subscriptions', (req, res) => {
  const newSubscription = req.body;
  newSubscription.id = getNextId(subscriptions);
  newSubscription.createdAt = new Date();
  newSubscription.updatedAt = new Date();
  subscriptions.push(newSubscription);
  res.status(201).send(newSubscription);
});

app.listen(3000, () => console.log('Server Started'));

 

 

PATCH 리퀘스트 처리하기

 

###
PATCH  http://localhost:3000/tasks/1
Content-Type: application/json

{
    "isComplete":true

}

 

app.patch('/tasks/:id', (req,res)=>{
    const id= Number(req.params.id); 
    const task= tasks.find((task)=>task.id===id);
    if(task){
        Object.keys(req.body).forEach((key)=>{//리퀘스트 바디에 전송되는 내용을 테스크 객체에 덮어써야할 때 
            task[key]=req.body[key];
        })
        task.updatedAt=new Date(); 
        res.send(task); 
    }else{
        res.status(404).send({message : 'Cannot find given id.'}); 
    }
    
});

 

💡데이터베이스가 없기 때문에 PATCH랑 POST는 서버를 재접속하면 리셋된다 

 

 

 

예) Subscription 수정하기

import express from 'express';
import subscriptions from './data/mock.js';

const app = express();
app.use(express.json());

app.get('/subscriptions', (req, res) => {
  /** 쿼리 파라미터
     *  - sort: 'price'인 경우 높은 요금순, 그 외의 모든 경우 최신으로 생성된 순서
     */
  const sort = req.query.sort;

  const compareFn =
    sort === 'price'
      ? (a, b) => b.price - a.price
      : (a, b) => b.createdAt - a.createdAt;

  res.send(subscriptions.sort(compareFn));
});

app.get('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);
  if (subscription) {
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});

function getNextId(arr) {
  const ids = arr.map((elt) => elt.id);
  return Math.max(...ids) + 1;
}

app.post('/subscriptions', (req, res) => {
  const newSubscription = req.body;
  newSubscription.id = getNextId(subscriptions);
  newSubscription.createdAt = new Date();
  newSubscription.updatedAt = new Date();
  subscriptions.push(newSubscription);
  res.status(201).send(newSubscription);
});

app.patch('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);

  if (subscription) {
    Object.keys(req.body).forEach((key) => {
      subscription[key] = req.body[key];
    });
    subscription.updatedAt = new Date();
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});    

app.listen(3000, () => console.log('Server Started'));

 

 

 

 

DELETE 리퀘스트 처리하기

 

###
DELETE http://localhost:3000/tasks/1

 

app.delete('/tasks/:id', (req,res)=>{
    const id= Number(req.params.id); 
    const idx= tasks.findIndex((task)=>task.id===id);
    if(idx>=0){
        tasks.splice(idx,1); //idx 요소에서 사용해서 1개 삭제 
        res.sendStatus(204);
    }else{
        res.status(404).send({message : 'Cannot find given id.'}); 
    }
    
});

 

• id 1을 가진 테스크를 삭제한다

 

 

예) Subscription 삭제하기

import express from 'express';
import subscriptions from './data/mock.js';

const app = express();
app.use(express.json());

app.get('/subscriptions', (req, res) => {
  /** 쿼리 파라미터
	 *  - sort: 'price'인 경우 높은 요금순, 그 외의 모든 경우 최신으로 생성된 순서
	 */
  const sort = req.query.sort;

  const compareFn =
    sort === 'price'
      ? (a, b) => b.price - a.price
      : (a, b) => b.createdAt - a.createdAt;

  res.send(subscriptions.sort(compareFn));
});

app.get('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);
  if (subscription) {
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});

function getNextId(arr) {
  const ids = arr.map((elt) => elt.id);
  return Math.max(...ids) + 1;
}

app.post('/subscriptions', (req, res) => {
  const newSubscription = req.body;
  newSubscription.id = getNextId(subscriptions);
  newSubscription.createdAt = new Date();
  newSubscription.updatedAt = new Date();
  subscriptions.push(newSubscription);
  res.status(201).send(newSubscription);
});

app.patch('/subscriptions/:id', (req, res) => {
  const id = Number(req.params.id);
  const subscription = subscriptions.find((sub) => sub.id === id);

  if (subscription) {
    Object.keys(req.body).forEach((key) => {
      subscription[key] = req.body[key];
    });
    subscription.updatedAt = new Date();
    res.send(subscription);
  } else {
    res.status(404).send({ message: 'Cannot find given id.' });
  }
});

// 여기에 코드를 작성하세요.
app.delete('/subscriptions/:id', (req,res)=>{
    const id= Number(req.params.id); 
    const idx= subscriptions.findIndex((sub)=>sub.id===id);
    if(idx>=0){
        subscriptions.splice(idx,1); //idx 요소에서 사용해서 1개 삭제 
        res.sendStatus(204);
    }else{
        res.status(404).send({message : 'Cannot find given id.'}); 
    }
    
});



app.listen(3000, () => console.log('Server Started'));

 

 

 

 

 


 

 

 

 

✍️요약 정리

 

Express 뼈대 코드

import express from 'express';

const app = express();

// 라우트 정의

app.listen(3000, () => console.log('Server Started'));

 

 

라우트(Route) 정의

 

Express 라우트는 아래와 같이 정의할 수 있다

 

app.method(path, handler)

 

  • method: HTTP 메소드 이름
  • path: 엔드포인트 경로
  • handler: 리퀘스트 로직을 처리하고 리스폰스를 돌려주는 핸들러 함수. 첫 번째 파라미터로 리퀘스트 객체, 두 번째 파라미터로 리스폰스 객체를 받는다.
// Arrow Function 핸들러

app.get('/some/path', (req, res) => {
  // 리퀘스트 처리
});
// 핸들러 함수 선언

function handler(req, res) {
  // 리퀘스트 처리
}

app.get('/some/path', handler);

 

 

 

리퀘스트 객체

 

req.query


 •쿼리스트링 파라미터를 프로퍼티로 담고 있는 객체입니다. 파라미터는 항상 문자열이라는 점 주의.

 

 

예시: GET /some/path?foo=1&bar=2

app.get('/some/path', (req, res) => {
  console.log(req.query);  // { foo: '1', bar: '2' }
  // ...
});

 

 

 

req.params

URL 파라미터를 프로퍼티로 담고 있는 객체입니다. 파라미터는 항상 문자열이라는 점 주의.

 


예시: GET /some/1/path/james

 

app.get('/some/:id/path/:name', (req, res) => {
  console.log(req.params);  // { id: '1', name: 'james' }
  // ...
});

 

 

req.body

리퀘스트 바디 내용을 담고 있는 객체. 바디 내용을 `req.body`로 접근하려면 `express.json()`이라는 함수를 이용해야 함.

예시: POST /some/path

 

{
  "field1": "value1",
  "field2": "value2"
}

 

app.use(express.json());

app.post('/some/path', (req, res) => {
  console.log(req.body);  // { field1: 'value1', field2: 'value2' }
  // ...
});

 

 

 

리스폰스 객체

 

res.send()

1. 리스폰스를 보낸다. 

2. 아규먼트로 전달되는 값에 따라 `Content-Type` 헤더를 설정하고 적절한 바디 내용으로 변환해준다.

3.  API 서버를 만들 때는 주로 객체나 배열을 전달한다.

4.  그러면 `Content-Type` 헤더를 `application/json`으로 설정하고 객체나 배열을 JSON 문자열로 바꿔서 전달해준다,
5.
디폴트 상태 코드는 `200`(OK)

 

app.get('/some/path', (req, res) => {
  res.send({ field1: 'value1', field2: 'value2' });
});
GET /some/path

HTTP/1.1 200 OK
Content-Type: application/json

{
  "field1": "value1",
  "field2": "value2"
}

 

 

res.status()

 • 리스폰스의 상태 코드를 설정한다

app.get('/some/path', (req, res) => {
  // ...
  res.status(404).send(...);
});

 

 

res.sendStatus()
 • 리스폰스로 바디 없이 상태 코드만 보낸다

 

app.get('/some/path', (req, res) => {
  // ...
  res.sendStatus(204);
});
728x90