백앤드 기초 5-2 ~ 6-5(API설계하며 method 기능 알기)

Develop Kim·2024년 9월 6일

programmers

목록 보기
9/40

5 Express와 친해지기

5-2 지난 학습을 복습하고 실습해보자

1️⃣ API설계 수정: 전체 조회를 API에 적용해보기

  • API 설계(URL, method)에서 전체 유튜버 조회를 추가

    1. 전체 유튜버 "조회" GET/youtubers 👉 URL 설계시에는 가능한 복수형이 좋음
      • req : 필요 없음
      • res : map을 전체조회
    2. 개별 유튜버 "조회" GET/youtubers/:id 👉 id로 map에서 객체를 찾아서, 그 객체의 정보를 뿌려줌
      • req : params.id 👉 map에 저장된 key값을 전달
      • res : map에서 id로 객체를 조회해서 전달
    3. 유튜버 "등록" 👉 POST/youtubers
      • req : body 👉 channelTitle, sub = 0, videoNum = 0 , 신규 유튜버 정보를 전달
      • res : "channelTitle님, 유튜브 가입을 축하합니다"
  • 이전에 URL+메서드 실습에 사용한 내용을 참고
  • 전체조회를 위해 app.get 추가
app.get("/youtubers", (req, res) => {
    res.json(db)
    console.log(db)
})
  • 전체조회를 추가해봤지만 postman에서 res을 못 받아옴, 하지만 콘솔로그에서는 잘 표기가 됨

  • 콘솔로그에서 나오는 값을 확인하면 map형태가 json과 동일하지 않은 것을 확인할 수 있음, 메소드를 활용하여 json으로만 가져오도록 코드를 수정해야 됨

  • db.values 를 사용해봤는데도 중괄호로 둘러쌓여 있어서 json으로 인식하지 못함

app.get("/youtubers", (req, res) => {
    res.json(db.values)
    console.log(db.values)
})

  • forEach문을 사용하여 순서대로 데이터를 나열하는 방법으로 하니 하나씩 결과값을 보여줌
    • 하지만, json으로 response 할 수 없음
app.get("/youtubers", (req, res) => {
	db.forEach((youtuber) => {
	    console.log(youtuber)
    })
})

  • 구글링을 하여 map을 json으로 가져오는 법을 배움
app.get("/youtubers", (req, res) => {

  let youtubers = {} // 변수를 json 형태로 변환
  
  db.forEach((value, key) => { // 매개변수로 value, key를 각각 넣어줌
    youtubers[key] = value
  })
  
    res.json(youtubers)

})
  • 이 방법은 이전에 해본 방법에서 대괄호를 찾아볼 수 있음

    app.get('/:id', function (req, res) { 
    
    let {id} = req.params
    id = parseInt(id) // "숫자" -> 정수
    
    if (db.get(id) == undefined){
      res.json({
        message : "없는 상품이네영"
      })
    } else {
      product = db.get(id)
      product.id = id // 객체를 이런식으로 추가할 수 있음
      product["id"] = id // 배열처럼 대괄호로 추가함, 배열 중 "id"를 id 로 바꿈
      res.json(product) // 위에서 db에서 가져오는 id를 product로 선언하여 간편해짐
    }
    })
  • 아래와 같이 수정하여 사용할 수도 있음

app.get("/youtubers", (req, res) => {

  let youtubers = {} // 변수를 json 형태로 변환
  
  db.forEach((youtuber) => { //  value, key를 각각 넣어주는 대신 변수를 그대로 넣어도 됨
    youtubers[youtuber.channel] = youtuber // 아래도 마찬가지고 변경해줄 수 있음
  
    res.json(youtubers)

})


5-3 자바스크립트 forEach

1️⃣ forEach문이란?

  • for + each, 향상된/개선된 for문으로도 불림, forEach는 처음 개발 이유가 배열에 사용하기 위함이었음
  • 즉 인덱스 값을 순서대로 for문을 한번에 해결하기 위해 사용됨
const arr = [1, 2, 3, 4, 5]
arr.forEach(
  (a) => {
    console.log(a)
  }
  // 선택한 객체(arr)또는 배열에서 요소를 하나 꺼낸 다음
  // 매개변수(a)로 그 요소를 전달하여 호출되는 콜백함수
)

arr.forEach(
  (a, b, c) => {
  // 매개변수의 첫번째는 value, 두번째는 index, 세번째는 값 전부인 것을 알 수 있음
    console.log(`a : ${a}, b : ${b}, c : ${c}`)
  }
  // a값과 b값을 템플릿 문자열을 사용해서 출력하면
  // a : 1, b : 0, c : 1,2,3,4,5 로 a는 값, b는 인덱스, c는 통째로 출력함
  
)

2️⃣ 그렇다면 map 객체에서는 어떻게 꺼내 쓰는 것일까?

let map = new Map()
map.set(7, "seven")
map.set(9, 'nine')
map.set(8, 'eight')

map.forEach((a, b, c) => {
  console.log(`a : ${a}, b : ${b}, c : ${c}`)
// a는 value, b는 key, c는 객체 가 출력되는 것을 알 수 있음 
})

// 아래와 같이 결과가 나오는 것을 볼 수 있음
// a : seven, b : 7, c : [object Map]
// a : nine, b : 9, c : [object Map]
// a : eight, b : 8, c : [object Map]


// map을 json방식으로 가져올 때 key, value의 위치가 왜 이런 지 알 수 있게 됨
app.get("/youtubers", (req, res) => {
  let youtubers = {}
  db.forEach((value, key) => {//  value, key를 순서대로 넣어줌
    youtubers[key] = value
  })
    res.json(youtubers)
})

🧐 추가적으로 내가 궁금한 것: youtubers의 요소 key를 value로 선언하는 것일까?

  • 아래와 같이 map이 있다고 하면 forEach 루프는 다음 단계를 거침
db.set("pewdiepie", { subscribers: 110000000 });
db.set("h3h3", { subscribers: 6000000 });
  1. 첫 번째 반복에서, key는 “pewdiepie”이고 value는 { subscribers: 110000000 }임, youtubers["pewdiepie"] = { subscribers: 110000000 }; 라인이 실행됨
  2. 두 번째 반복에서, key는 “h3h3”이고 value는 { subscribers: 6000000 }임, youtubers["h3h3"] = { subscribers: 6000000 }; 라인이 실행됨
  • 최종적으로 youtubers 객체는 다음과 같이 되고 res.json(youtubers) 호출은 이 객체를 JSON 형식으로 변환하여 클라이언트에 응답으로 보냄
{
  pewdiepie: { subscribers: 110000000 },
  h3h3: { subscribers: 6000000 }
}
  • 즉, youtubers의 key는 youtuber1, youtuber2, youtuber3 등 이며, 우리가 필요한 것은 그 안의 또다른 key:value인 것임

5-4 자바스크립트 map 함수(메서드)

1️⃣ map함수란?

  • 지금까지 썼던 map객체가 아닌 함수에 대한 설명임
  • map 함수는 배열의 각 요소에 대해 주어진 함수를 실행하고, 그 결과를 새로운 배열로 반환하는 JavaScript의 메서드임
  • 간단히 말해서, map은 배열의 각 요소를 변환하여 새로운 배열을 만드는 데 사용됨
const numbers = [1, 2, 3, 4];
const squared = numbers.map(num => num * num);
console.log(squared); // 출력: [1, 4, 9, 16]
// 이 코드에서 map 함수는 numbers 배열의 각 숫자를 제곱하고, 그 결과를 squared 배열에 저장함

2️⃣ forEach VS map

  • 이 둘의 차이는 리턴하는가에서 있음
const arr = [1, 2, 3, 4, 5]

const forarr = arr.forEach(
  (a, b, c) => { 
    return a *2
  } 
)
console.log(forarr) // undefined 라는 결과가 나오고 forEach는 리턴을 하지 않음


const maparr = arr.map(
  (a, b, c) => { 
    return a *2
  } 
) 
console.log(maparr) // [ 2, 4, 6, 8, 10 ], map은 return을 하고 새로운 arr를 만들어줌
  • map에서는 콜백 함수의 반환값들로 구성된 새로운 배열을 반환한다!
  • 즉, forEach 메서드는 단순히 반복문을 대체하기 위한 함수이고, map 메서드는 요소값을 다른 값으로 mapping한 새로운 배열을 생성하기 위한 고차함수다. 

5-5 자바스크립트 delete

1️⃣ API설계 수정: delete(삭제)를 API에 적용해보기

  • API 설계(URL, method)에서 유튜버 삭제를 추가

    1. 전체 유튜버 "조회" GET/youtubers 👉 URL 설계시에는 가능한 복수형이 좋음
      • req : 필요 없음
      • res : map을 전체조회
    2. 개별 유튜버 "조회" GET/youtubers/:id 👉 id로 map에서 객체를 찾아서, 그 객체의 정보를 뿌려줌
      • req : params.id 👉 map에 저장된 key값을 전달
      • res : map에서 id로 객체를 조회해서 전달
    3. 유튜버 "등록" 👉 POST/youtubers
      • req : body 👉 channelTitle, sub = 0, videoNum = 0 , 신규 유튜버 정보를 전달
      • res : "channelTitle님, 유튜브 가입을 축하합니다"
    4. 유튜버 "삭제" 👉 DELETE/youtubers/:id URL이 개별 조회와 같지만 메서드가 다르기 떄문에 괜찮음
      • req : params.id 👉 map에 저장된 key값을 전달
      • res : "channelTitle님, 아쉽지만 다음에 뵙겠습니다."

2️⃣ 코드에 delete를 적용해보자

// express 모듈 세팅
const express = require('express')
const app = express()
app.listen(2223)


// 데이터 세팅
let youtuber1 = {
  channelTitle : "조코딩",
  sub : "609k",
  videonum : "111개"
}

let youtuber2 = {
  channelTitle : "침착맨",
  sub : "2270k",
  videonum : "6600개"
}

let youtuber3 = {
  channelTitle : "아무개",
  sub : "300k",
  videonum : "50개"
}

let db = new Map() // key : value 쌍으로 생김, json과 비슷한 형태
let id = 1
db.set(id++, youtuber1) // 하나씩 더해주는 값으로 등록을 하게 됨
db.set(id++, youtuber2)
db.set(id++, youtuber3)


// 전체조회
app.get("/youtubers", (req, res) => {
  let youtubers = {} // 변수를 json 형태로 변환
    
  db.forEach((value, key) => { // 매개변수로 value, key를 각각 넣어줌
    youtubers[key] = value
  })
  res.json(youtubers)
})


// 개별조회
app.get('/youtubers/:id', function (req, res){
  let {id} = req.params
  id = parseInt(id) // id를 그냥 쓰면 숫자가 아닌 문자열로 받기 때문에 정수로 바꿔줌

  const youtuber = db.get(id) // youtuber 로 db.get을 정리하여 중복을 없앰(가독성 증가)
  if (youtuber == undefined) {
    res.json({
      message : "정보를 찾을 수 없습니다"
    })
  } else {
    res.json(youtuber)
  }
})


// 등록
app.use(express.json()) // http 외 모듈인 '미들웨어' 중 json 설정
app.post('/youtubers', (req, res) => {
  console.log(req.body)

  db.set(id++, req.body) // id를 1이라 선언하고 등록할 때마다 하나씩 더하게 만들면 중복되지 않고 저장이 됨
  
  res.json({message : `${db.get(id-1).channelTitle}님, 유튜브 가입을 축하합니다`})
  // id를 그대로 불러오면 위에서 id++로 더한 값을 res하기 때문에 1을 빼줘야 정상적으로 불러옴
})


// 삭제
app.delete('/youtubers/:id', (req, res) => {
  let {id} = req.params
  id = parseInt(id)

  const name = db.get(id).channelTitle // db에서 id(의 title)를 가져옴

  db.delete(id)
  res.json({
    message : `${name}님, 아쉽지만 다음에 뵙겠습니다.`
  })
})


## 6 Express와 진하게 친해지기

6-1 예외처리는 안정성을 증가시킴

1️⃣ 예외처리로 안정성을 확보해보자

  • 조회를 먼저 하고 없다면 있다면 경우를 나누어 코드를 작성, 있다면을 먼저 내보내면 삭제 후 재조회를 하는 번거로움 때문에 if else문은 없다면을 먼저 쓰는 것이 좋음
app.delete('/youtubers/:id', (req, res) => {
  let {id} = req.params
  id = parseInt(id)

  let youtuber = db.get(id)
      // 어제 const channelTitle를 여기다 적으니 오류가 남 왜일까?
      // 블록 스코프 때문이었음, 선언 블록과 실행 블록이 맞지 않았기 때문임


  if (youtuber == undefined) { // db에서 id를 꺼내 있는지 확인
    res.json({
      message : `요청하신 ${id}의 정보를 찾을 수 없습니다`
    })
  } else {
    const channelTitle = youtuber.channelTitle // 앞에서 db.get(id)를 하기 때문에 유튜버 변수 만듬

    db.delete(id)
    
    res.json({
      message : `${channelTitle}님, 아쉽지만 다음에 뵙겠습니다.`
    })
  }
})

6-2 리팩토링: 수정 후 추가

1️⃣ 리-팩토링(소프트웨어의 코드 구조를 변경하는 것)의 목적

  1. 이해하기 쉽게
  2. 성능 향상
  3. 안정성 향상
    👉 내 코드에 나쁜 부분이 있다면, 리팩토링으로 "클린 코드"를 가질 수 있음

2️⃣ 리팩토링은 언제 해야 하나?

  1. 에러(문제점)이 n회 발견되었을 때, 리팩토링을 진행
  2. 반대로, 리팩토링을 하면서 에러(문제점)을 발견할 수 있음
  3. 그러므로 기능을 추가하기 전에 리팩토링을 하면 좋음(기존 기능과 코드 구조의 문제점이 없는 지 볼 수 있음)
    ex. API URL"설계" 수정 시
  4. 코드 리뷰할 때도 진행하면 좋음(설명을 하게 되면 더 나은 코드가 보여짐)

❌ 그럼 언제 하면 안되는가? : 배포 및 운영 직전에는 절대로 코드 수정이 일어나선 안됨!

6-3 전체 delete + 전체 조회(없다 메시지)

1️⃣ API설계 수정: 전체 유튜버 delete

  • API 설계(URL, method)에서 유튜버 삭제를 추가

    1. 전체 유튜버 "조회" GET/youtubers 👉 URL 설계시에는 가능한 복수형이 좋음
      • req : 필요 없음
      • res : map을 전체조회
    2. 개별 유튜버 "조회" GET/youtubers/:id 👉 id로 map에서 객체를 찾아서, 그 객체의 정보를 뿌려줌
      • req : params.id 👉 map에 저장된 key값을 전달
      • res : map에서 id로 객체를 조회해서 전달
    3. 유튜버 "등록" 👉 POST/youtubers
      • req : body 👉 channelTitle, sub = 0, videoNum = 0 , 신규 유튜버 정보를 전달
      • res : "channelTitle님, 유튜브 가입을 축하합니다"
    4. 유튜버 "삭제" 👉 DELETE/youtubers/:id URL이 개별 조회와 같지만 메서드가 다르기 떄문에 괜찮음
      • req : params.id 👉 map에 저장된 key값을 전달
      • res : "channelTitle님, 아쉽지만 다음에 뵙겠습니다."
    5. **전체 유튜버 "삭제" 👉 DELETE/youtubers
      • req : 필요 없음
      • res : "전체 유튜버가 삭제되었습니다."

2️⃣ 전체 삭제 기능을 추가

app.delete('/youtubers', (req, res) => {
  // db에 값이 1개 이상이면 삭제하고 값이 없다면 안내문 표출

  let msg = ""
  // 메시지를 json으로 보내는 것도 반복되니 변수로 따로 만들어 줄 수 있음

  if (db.size >= 1) { // db의 값이 1보다 크면 이라는 것은 데이터의 수를 나타냄
    db.clear() // db의 모든 것을 깨끗히 지워줌  
    msg = "모든 유튜버가 삭제되었습니다."
  } else {
    msg = "삭제할 유튜버가 없습니다."
  }
  res.json({
    message : msg // msg가 최종 결정 및 보내지는 위치와 변수의 선언 위치가 같은 블록이므로 에러 안남
  })
})
  • TIP : VSCODE에서 정육면체 아이콘은 함수(method), 직육면체는 변수(property)

6-4 자바스크립트 PUT(수정)

1️⃣ API설계 수정: 개별 유튜버 수정

  • API 설계(URL, method)에서 유튜버 삭제를 추가

    1. 전체 유튜버 "조회" GET/youtubers 👉 URL 설계시에는 가능한 복수형이 좋음
      • req : 필요 없음
      • res : map을 전체조회
    2. 개별 유튜버 "조회" GET/youtubers/:id 👉 id로 map에서 객체를 찾아서, 그 객체의 정보를 뿌려줌
      • req : params.id 로 map에 저장된 key값을 받아옴
      • res : map에서 id로 객체를 조회해서 전달
    3. 유튜버 "등록" 👉 POST/youtubers
      • req : body 에서 channelTitle, sub = 0, videoNum = 0 , 신규 유튜버 정보를 받아옴
      • res : "channelTitle님, 유튜브 가입을 축하합니다"
    4. 유튜버 "삭제" 👉 DELETE/youtubers/:id URL이 개별 조회와 같지만 메서드가 다르기 떄문에 괜찮음
      • req : params.id 로 map에 저장된 key값을 받아옴
      • res : "channelTitle님, 아쉽지만 다음에 뵙겠습니다."
    5. 전체 유튜버 "삭제" 👉 DELETE/youtubers
      • req : 필요 없음
      • res : "전체 유튜버가 삭제되었습니다."
    6. **개별 유튜버 "수정" 👉 PUT/youtubers/:id
      • req : params.id, body에서 channelTitle 을 받아옴
      • res : "(이전 이름)channelTiltle님, 채널명이 (신규)channeTitle님으로 변경되었습니다."

2️⃣ PUT으로 수정을 추가해보자

app.put("/youtubers/:id", (req, res) => { // put은 덮어쓰기를 함
  let {id} = req.params
  id = parseInt(id)

  let youtuber = db.get(id)
  let oldTitle = youtuber.channelTitle // 현재 블록에서 채널타이틀을 old로 선언해야 변경 이후에도 사용할 수 있음

  if (youtuber == undefined) { 
      res.json({
      message : `요청하신 ${id}의 정보를 찾을 수 없습니다`
    })
  } else { // 해당 블록 내에서 채널타이틀 수정
    let newTitle = req.body.channelTitle
    // 뉴타이틀을 가져온 body의 채널 타이틀(postman의 put에서 body의 raw에 json으로 입력된 값)로 선언

    youtuber.channelTitle = newTitle // 뉴타이틀을 현재 블록 내의 채널 타이틀로 선언(현재 채널타이틀이 변경됨)

    db.set(id, youtuber) // key값으로 id를 정해주고 객체인 youtuber 지정하여 db에 덮어쓰기
    
    res.json({
      message : `${oldTitle}님, 채널명이 ${newTitle}님으로 변경되었습니다.`
    })
  }
})
  • body의 raw에서 채널타이틀을 request하여 다시 response하는 것을 확인할 수 있음

6-5 http 상태 code

1️⃣ HTTP(인터넷 통신시 사용하는 규약)안에 작성되어 들어가는 "상태"

  1. 정보 응답 (100-199)
  • 100 Continue: 클라이언트는 요청을 계속 진행할 수 있음
  • 101 Switching Protocols: 서버는 클라이언트의 프로토콜 전환 요청을 수락함
  • 102 Processing (WebDAV; RFC 2518): 요청을 처리 중이나 아직 완료되지 않았음
  1. 성공 응답 (200-299)
  • **200 OK: 요청이 성공적으로 처리됨 👉 조회/수정/삭제 성공
  • **201 Created: 요청이 성공적이고 새로운 리소스가 생성됨 👉 등록 성공
  • 202 Accepted: 요청을 수락했으나 처리는 완료되지 않음
  • 204 No Content: 요청은 성공적이지만 전송할 내용이 없음
  1. 리다이렉션 메시지 (300-399)
  • 301 Moved Permanently: 요청한 리소스가 영구적으로 새 위치로 이동함
  • 302 Found: 요청한 리소스가 일시적으로 다른 URI에 있음
  • 304 Not Modified: 이전에 캐시된 버전을 다시 사용할 수 있음
  1. 클라이언트 오류 응답 (400-499)
  • **400 Bad Request: 서버가 요청을 이해할 수 없음
  • 401 Unauthorized: 인증이 필요함
  • **403 Forbidden: 서버가 요청을 거부함 👉 로그인할 떄 주로 사용
  • **404 Not Found: 서버가 요청한 리소스를 찾을 수 없음 👉 URL에 맞는 API가 없음
  • 405 Method Not Allowed: 요청된 메소드는 허용되지 않음
  1. 서버 오류 응답 (500-599)
  • **500 Internal Server Error: 서버에 오류가 발생하여 요청을 수행할 수 없음 👉 서버터짐
  • 501 Not Implemented: 서버가 요청 메소드를 지원하지 않음
  • 502 Bad Gateway: 서버가 게이트웨이나 프록시 역할을 하며, 상위 서버로부터 잘못된 응답을 받음
  • **503 Service Unavailable: 서버가 일시적으로 요청을 처리할 준비가 되어 있지 않음. 주로 서버가 과부하이거나 유지보수 중일 때 발생
  • 504 Gateway Timeout: 게이트웨이가 상위 서버로부터 시간 내에 응답을 받지 못함
profile
김개발의 개발여정

0개의 댓글