백엔드 기초 6-6 ~ 7-2 (핸들러, 예외처리 고도화, 미니 프로젝트)

Develop Kim·2024년 9월 7일

programmers

목록 보기
10/40

6 Express와 진하게 친해지기

6-6 핸들러, if(객체) : if 긍정문, route

1️⃣ 핸들러(Handler)란?

  • Request로 인해 호출되는 Controller의 "메소드"를 뜻함.
  • 개발 세계에서, 핸들러란?
    • 안드로이드에 굉장히 필수적인 개념으로 알려져 있는 용어
    • 사실 프로그래밍 언어 모든 곳에서 사용하는 용어
    • 넓은 의미로 사용되는 단어라, 쓰는 환경에 따라 뜻하는 의미가 조금씩 다름.
    • 학문적으로 아직 논쟁이 있다고 보고 있음.
    • 일반적인 큰 의미는, 요청에 의해 호출되는 메소드를 뜻함
  • URL 뒤에 입력되는 콜백함수가 핸들러에 해당됨
  • 즉, HTTP request가 날아오면 자동으로 호출되는 method
  • node.js : app.HTTPMETHOD(path, 핸들러)
    cf.스프링 : controller(ex. testController.java)

2️⃣ if 긍정문

  • 동료에게 내 코드(핸들러)를 주면 설명 없이 알아들을 수 있어야 함
  • 코드를 조금 더 알아보기 쉽게 바꿔보자(if부정문을 긍정문으로 변경해좀)
app.delete('/youtubers/:id', (req, res) => {
  let {id} = req.params
  id = parseInt(id)

  let youtuber = db.get(id)

  if (youtuber) { // TRUE에 해당하는 값을 넣어주면 됨
    const channelTitle = youtuber.channelTitle
    db.delete(id)
    res.json({
      message : `${channelTitle}님, 아쉽지만 다음에 뵙겠습니다.`
    })
  } else {
    res.json({
      message : `요청하신 ${id}의 정보를 찾을 수 없습니다`
    })
  }
})

3️⃣ path는 route?

  • path자리의 설명을 보면 적힌 그대로인 것을 볼 수 있음
  • 새롭게 작성해서 확인해 보면 path는 string(문자열)인 것을 알 수 있고
  • 다시 GET 메서드로 확인해 보면 path는 route라는 객체로 확인됨
  • 즉, Express는 string으로 받아서 객체로 들어가게 만들어줌

6-7 json array, find(), 예외 처리

1️⃣ 배열과 find()를 배워보자

  • 배열은 단순히 정수나 문자열만 넣을 수 있는 게 아니라 json형태도 넣을 수 있음
const express = require('express')
const app = express()
app.listen(3000)

const fruits = [
  {id : 1, name : 'apple'},
  {id : 2, name : 'orange'},
  {id : 3, name : 'strawberry'},
  {id : 4, name : 'blueberry'}
] // 이렇게 작성하면 배열 형태로 저장이 됨

// 과일 전체조회
app.get('/fruits', (req, res) => {

  res.json(fruits)
}) // 배열로 저장된 것을 확인할 수 있음

  • 과일 개별조회를 위해 fruits.id를 불러왔지만 id값대로가 아닌 배열의 순서대로 불러오게 됨
// 과일 개별조회
app.get('/fruits/:id', (req, res) => {
  let id = req.params.id
  let fruit = fruits[id]
  res.json(fruit)
})

  • 아래와 같이 id에서 1을 빼면 정상적으로 불러오는 것을 확인할 수 있지만 근본적인 방법은 아니니 다른 방법을 시도
app.get('/fruits/:id', (req, res) => {
  let id = req.params.id

  let fruit = fruits[id-1]

  res.json(fruit)  
})
  • 이전에 객체를 찾는데 사용하는 forEach문을 이용해보자
app.get('/fruits/:id', (req, res) => {
  let id = req.params.id
  
  // foreach문을 이용해서 id가 1인 것을 찾아내보자
  fruits.forEach((fruit) => { // fruit을 하나씩 꺼내서 확인 해보기
    if (fruit.id == id) { // 만약 fruit의 id값이 req.params로 불러온 id가 같으면
      findfruit = fruit // findefruit에 fruit을 담아줌
    }
  })
  res.json(findfruit) // 담은 fruite을 보냄
})
  • forEach문 보다 더욱 간단하게 찾을 수 있는 find() 메서드가 있음
app.get('/fruits/:id', (req, res) => {
  let id = req.params.id
  let findfruit = // 찾아내는 것을 한줄로 바로 적을 수도 있음
    fruits.find( f => (f.id == id))
  // fruits 배열에서 find 메서드로 객체를 찾을 것, f(객체)의 id값이 req.params로 불러온 id와 같은 객체
  // 즉, fruits 배열 안 객체 중, id값이 req.params.id랑 같은 객체를 찾음
  res.json(findfruit)
})

2️⃣ 예외처리 제대로 배우기

  • 없는 과일을 보냈더니 상태코드가 성공(?)된 것으로 나옴, 이렇게 되면 에러를 잡지 못하기 때문에 예외를 터트려줘야 함 👉 예외를 터트리는 이유는 프론트엔드가 데이터를 받을 때 성공했다는 뜻으로 받을까봐
app.get('/fruits/:id', (req, res) => {
  
  let id = req.params.id
  let findfruit = fruits.find( f => (f.id == id))

  if (findfruit) {
    res.json(findfruit) // 담은 fruite을 보냄
  } else { // 예외를 터트린다 = http status code가 성공이 아닌 실패로 전달할 것
    res.json(
      res.status(404).send( // 중간에 상태 메시지를 작성하여 찾는 것이 없다는 404에러를 보내줌
        "전달 주신 파일이 없어요" // 그냥 보내면 프론트엔드는 모르니까 메시지도 보냄
      )
    )
  }

✅ TIP : 위에서 볼 수 있듯 HTTP 상태코드는 클라이언트와 소통을 정확하게 하기 위함


6-8 '== vs ==='

1️⃣ 이 둘은 어떤 차이가 있을까?

if (1 == "1"){ // 자료형은 상관없이, 값만 비교함
  console.log('같아')
} else {
  console.log('다름')
}

if (1 === "1"){ // 자료형과 값 모두 동일해야 함
  console.log('같아')
} else {
  console.log('다름')
}

6-9 YouTuber demo 예외 고도화 : map은 undefined가 아님, POST

1️⃣ 전체조회에서 예외 고도화 해보기

  • 전체조회를 했을 때
app.get("/youtubers", (req, res) => {

    let youtubers = {} // 변수를 json 형태로 변환

    if (db) {
      db.forEach((value, key) => { // 매개변수로 value, key를 각각 넣어줌
        youtubers[key] = value
      })
      res.json(youtubers)
    } else {
      res.status(404).json({
        message : "조회할 유튜버가 없어요"
      })
    }
  })
  • 유튜버 전체 지우기 후 결과를 보지만, 요소들은 다 지워졌지만 객체가 존재하기 때문에 404가 안 뜸

  • 요소를 아무리 지워도 db에는 객체가 남아있기 때문에 "db.size가 0이 아닐 때"로 코드를 수정

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

    let youtubers = {}

    if (db.size !== 0) { // db.size가 0이 아닐 떄로 수정
      db.forEach((value, key) => {
        youtubers[key] = value
      })
      res.json(youtubers)
    } else {
      res.status(404).json({
        message : "조회할 유튜버가 없어요"
      })
    }
  })
  • else가 작동하는 것을 확인할 수 있었고 map객체는 요소가 없어도 undifind(null값)가 되지 않는다는 것을 알 수 있었음

2️⃣ get/delete/put 에도 예외처리 고도화를 해줌

  • 개별조회에 status 추가
app.get('/youtubers/:id', function (req, res){
  let {id} = req.params
  id = parseInt(id)

  const youtuber = db.get(id)
  if (youtuber == undefined) {
    res.status(404).json({ // 여기다 추가
      message : "정보를 찾을 수 없습니다"
    })
  } else {
    res.json(youtuber)
  }

})
  • 개별삭제에 status 추가
app.delete('/youtubers/:id', (req, res) => {
  let {id} = req.params
  id = parseInt(id)
  let youtuber = db.get(id)

  if (youtuber == undefined) {
    res.status(404).json({ // 여기에 추가
      message : `요청하신 ${id}의 정보를 찾을 수 없습니다`
    })
  } else {
    const channelTitle = youtuber.channelTitle
    db.delete(id)
    res.json({
      message : `${channelTitle}님, 아쉽지만 다음에 뵙겠습니다.`
    })
  }
})
  • 전체삭제에 status 추가
app.delete('/youtubers', (req, res) => {
  if (db.size >= 1) { 
    db.clear() 
    
    res.json({
      message : "모든 유튜버가 삭제되었습니다."
    })
    
  } else {
    res.status(404).json({ // 이전에 사용한 msg를 빼고 상태코드 입력
      message : "삭제할 유튜버가 없습니다."
    })
  }

})
  • 수정에도 status 추가
app.put("/youtubers/:id", (req, res) => {
  let {id} = req.params
  id = parseInt(id)

  let youtuber = db.get(id)
  let oldTitle = youtuber.channelTitle

  if (youtuber == undefined) { 
      res.status(404).json({ // 여기도 상태코드를 추가
      message : `요청하신 ${id}의 정보를 찾을 수 없습니다`
    })
  } else {
    let newTitle = req.body.channelTitle

    youtuber.channelTitle = newTitle

    db.set(id, youtuber)
    
    res.json({
      message : `${oldTitle}님, 채널명이 ${newTitle}님으로 변경되었습니다.`
    })
  }
})

3️⃣ post의 예외처리

  • post의 예외처리는 body값에 아무것도 없다면을 적용시킬 수 있음
  • body값에서 타이틀을 빼버렸는데도 환영과 함께, 200이 뜨는 것을 확인할 수 있음
app.use(express.json()) 
app.post('/youtubers', (req, res) => {
  
  const channelTitle = req.body.channelTitle
  
  if(channelTitle){ // body에 채널타이틀이 있으면 등록을 진행
    db.set(id++, req.body)
    res.status(201).json({message : `${db.get(id-1).channelTitle}님, 유튜브 가입을 축하합니다`}) // 200은 그냥 성공이니 201(등록성공)로 설정해줌
    
  } else { // 아니라면
    res.status(400).json({
    // body값을 잘못 보낸 것은 클라이언트의 잘못이므로 400(서버가 요청의 구문을 인식하지 못함)을 써줌
      message : '요청 값을 제대로 보내주세요'
    })
  }
})
  • 클라이언트가 잘못 입력하는 경우 400번대의 상태코드를 보내주는 예외처리를 해주면 됨
  • 또, 성공시 어떤 성공인지에 따라 달라질 수 있는 부분을 바꿔줄 수 있음


7 미니 프로젝트

7-1 프로젝트 시작

1️⃣ 진짜 유튜브를 운영하는 것처럼 만들어보자!: API 기획

  • 회원
    1) 로그인
    2) 회원 가입
    3) 회원 정보 조회
    4) 회원 탈퇴
    5) 채널 : 회원은 계정 1개당 채널 100개를 가질 수 있음(회원이 채널을 관리함)
    • 채널 생성
    • 채널 수정
    • 채널 삭제

2️⃣ 웹페이지 기획 시각화

  • 로그인 페이지

    • 화면을 완성하는데는 API가 필요 없음
    • 하지만, 로그인 버튼을 클릭하게 되면 id, pwd를 받아와 로그인을 승인하기 때문에 API가 필요함
  • 회원가입 페이지

    • 화면을 완성하는데는 API가 필요 없음
    • 회원가입 버튼 클릭 시 id, pwd, 이름을 가지고 회원가입을 시켜줄 API가 필요함
  • 마이 페이지

    • 화면 생성 시 회원정보를 조회하는 API(id, 이름 데이터)가 필요함
    • 회원탈퇴 버튼 클릭 시 원탈퇴를 시켜줄 API가 필요함

3️⃣ 회원 API 설계

  • 1) 로그인 : POST/login(body가 필요한 api는 POST임! GET과 POST의 차이점은 body)
    • req : body(id, pwd)
    • res : ${name}님 환영합니다 👉 메인 페이지를 출력
  • 2) 회원 가입 : POST/join
    • req : body(id, pwd, name) : 개인정보니까 body에 넣어서 보내줌
    • res : ${name}님 환영합니다 👉 로그인 페이지를 출력
  • 3) 회원 개별 정보 조회 : GET/users/:id
    • req : URL(id)
    • res : id, name
  • 4) 회원 개별 탈퇴 : DELETE/users/:id
    • req : URL(id)
    • res : ${name}님 다음에 또 뵙겠습니다. OR 메인페이지 출력

7-2 API 설계대로 코드 구현하기

1️⃣ 회원 API 코드 틀

const express = require('express')
const app = express()
app.listen(7777)


// 로그인
app.post('/login', (req, res) => {

})


// 회원가입
app.post('/join', (req, res) => {

})



// 회원 개별 조회
app.get('/users/:id', (req, res) => {

})



// 회원 개별 탈퇴
app.delete('/users/:id', (req, res) => {
  
})

2️⃣ 회원가입 구현/회원 개별 조회, 회원 개별 삭제

const express = require('express')
const app = express()
app.listen(7777)
app.use(express.json()) //http 외 모듈 사용 'json 모듈'

let db = new Map()
var id = 1 // 하나의 객체를 유니크 하게 구별하기 위함, 


// 로그인
app.post('/login', (req, res) => {
  let id = req.params.id
})


// 회원가입
app.post('/join', (req, res) => {
  console.log(req.body)

  if (req.body == {}) {
    res.status(400).json({
      message : `입력 값을 확인해주세여`
    })
  } else {
    db.set(id++, req.body)

    res.status(201).json({
      message : `${db.get(id-1).name}님 환영합니당`
    })
  }
})


app
  .route('/users/:id') // 루트를 활용헤서 url이 같은 것을 묶을 수 있음

  // 회원 개별 조회
  .get((req, res) => {
    let {id} = req.params // {}는 비구조화, id 값을 따로 빼서 사용
    id = parseInt(id)
  
    const user = db.get(id)
    if (user == undefined) {
      res.status(404).json({
        message : '회원정보가 없어요'
      })
  
    } else {
      res.json({
        userId : user.userId,
        name : user,name
      })
    }
  })

  // 회원 개별 탈퇴
  .delete((req, res) => {
    let {id} = req.params // {}는 비구조화, id 값을 따로 빼서 사용
    id = parseInt(id)
  
    const user = db.get(id)
  
    if (user == undefined) {
      res.status(404).json({
        message : '회원정보가 없어요'
      })
  
    } else {
      db.delete(id)
  
      res.status(200).json({
        message : `${user.name}님 다음에 볼께요`
      })
    }
  })

profile
김개발의 개발여정

0개의 댓글