백엔드 기초 8(미니프로젝트 고도화, route)

Develop Kim·2024년 9월 10일

programmers

목록 보기
12/40

8 미니프로젝트 고도화

8-1 router : user-demo와 channel-demo를 합쳐보자

1️⃣ Server와 Router의 역할

  • Express를 쓰는 최고 장점 중 하나가 route 등의 모듈을 깔끔하게 정해주는 것(HTTP에서 모듈을 만들 때는 복잡했었던 것이 쉬워짐, 모듈을 따로 만들지 않아도 됨)

2️⃣ Node.js에서의 라우팅이란?

  • 네트워크에서 경로를 정해주는 역할을 함
  • request(요청)가 날라왔을 때, 원하는 경로에 따라 적절한 방향으로 경로를 알려주는 것
    • URL, method에 따라 "콜백 함수"를 호출해줌

3️⃣ 서버를 담당하는 app.js 만들기

// 서버역할을 담당함
const express = require('express')
const app = express()
app.listen(1234)

const userRouter = require('./routes/user-demo') // 모듈화된 user-demo 소환하기
app.use("/", userRouter) // app.use를 사용하여 매개변수에 내가 만든 모듈을 추가해줌, "/"는 ? 

4️⃣ user-demo.js를 모듈화하기

const express = require('express')
 // 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌


router.use(express.json()) // http 외 모듈 사용 'json 모듈' -> app.을 router.로 수정

let db = new Map()
var id = 1
function isExist(obj){
  if (Object.keys(obj).length === 0) {
    return true
  } else {
    return false
  }
}

// 로그인
router.post('/login', (req, res) => {
  console.log(req.body) // userid, pwd

  const {userId, password} = req.body
  let loginUser = {} 

  db.forEach((user, id) => {
    if (user.userId === userId){
      loginUser = user 
    }
  })

  if(isExist(loginUser)) {
    console.log("아이디 같음!!")

    if (loginUser.password === password) {
      console.log("비번 같아")
    } else {
      console.log("비번 틀려")
    }
  } else {
    console.log('입력한 아이디를 다시 확인해 주세요')
  }
})


// 회원가입
router.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}님 환영합니당`
    })
  }
})


router
  .route('/users/:id')

  // 회원 개별 조회
  .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 = 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}님 다음에 볼께요`
      })
    }
  })

module.exports = router // 모듈화를 해주기 위해 router를 반환함 -> 외부모듈로 사용 가능

8-2 rename, channel-demo 연결, 모듈 이름 변경, use URL 설명

1️⃣ 코드 편집이 쉽도록 명령어를 한번에 다 바뀌보자

  • VSCODE의 설명을 보면 rename으로 이름을 한번에 쉽게 바꿔주는 기능이 있음


  • rename symbol로 쉽게 한번에 변경해줄 수 있으니 애용하면 됨

2️⃣ channel-demo도 모듈화하여 연결

  • user-demo에서 했던 것 처럼 적용
const express = require('express')
 // 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌


router.use(express.json()) //http 외 모듈 사용 'json 모듈'


let db = new Map()
let id = 1

router
  .route('/channels') // route로 URL 묶어주기

// 채널 개별 생성
  .post((req, res) => {
    if (req.body.channelTitle){
      db.set(id++, req.body) // db에 값을 줄 때 id+1씩 적용

      res.status(201).json({
        message : `${db.get(id-1).channelTitle}채널을 응원합니다.₩`
      })
    } else {
      res.status(400).json({
        message : '요청 값을 제대로 보내주세요'
      })
    }

})


// 채널 전체 조회
  .get((req, res) => { 
    let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌

    if(db.size){ // db에 무엇이라도 있다면
      db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
        // channels[key] = value channels에 키값을 전달하면서 value를 추가
        channels.push(value) // 리스트에 value를 하나씩 추가(push)해줌
      })
      // console.log(channels[0]) 배열의 인덱스를 활용해서 선택하여 가져올 수 있음
      // console.log(channels[1]) 
      // console.log(channels[2]) 
      res.status(200).json(channels) // 여기서 이렇게 보내면 배열 안에 json형태로 잘 보내줌
    } else {
      res.status(404).json({
        message : '아무것도 없는 걸요'
      })
    }
  })


router
  .route('/channels/:id')

// 채널 개별 조회
.get((req, res) => {
  let {id} = req.params // req.params 객체에서 URL 경로에 포함된 id 값을 추출하여 id 변수에 저장
  id = parseInt(id) // URL에서 문자열로 추출된 id를 정수로 변환
  let channel = db.get(id) // 해당 id를 키로 하는 채널 정보를 channel 변수에 저장

  if(channel){
    res.status(200).json(channel)
  } else {
        res.status(404).json({
      message : `채널정보를 찾을 수 없습니다.`
    })
  }
})


// 채널 개별 수정
  .put((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    let channel = db.get(id)
    let oldTitle = channel.channelTitle // 올드 타이틀을 만들어줘야 차별화가 가능함

    if(channel){
      let newTitle = req.body.channelTitle // 뉴타이틀을 req.body의 channelTitle에서 가져옴

      channel.channelTitle = newTitle // 가져온 것을 newTitle로 바꿔줌
      db.set(id, channel) // 바꾼 것을 같은 id값에 channel을 덮어쓰기 해줌 

      res.status(200).json({
        message : `${oldTitle}님이 ${newTitle}로 수정 되었습니다.` // 삭제되기 전 객체에서 꺼내와야 함
      })
    } else {
          res.status(404).json({
        message : `채널정보를 찾을 수 없습니다.`
      })
    }
})


// 채널 개별 삭제
  .delete((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    let channel = db.get(id) // 해당 id를 키로 하는 채널 정보를 channel 변수에 저장

    if(channel){
      db.delete(id) // db에서 id를 삭제
      res.status(200).json({
        message : `${channel.channelTitle}님 정상적으로 삭제 되었습니다.` // 삭제되기 전 객체에서 꺼내와야 함
      })
    } else {
          res.status(404).json({
        message : `채널정보를 찾을 수 없습니다.`
      })
    }

})


module.exports = router // 모듈화를 해주기 위해 router를 반환함

3️⃣ 모듈 이름 변경하기

  • 실제 모듈을 사용할 때 파일 이름은 demo를 쓰진 않음 파일 이름을 바꿔보자
  • 파일 이름을 변경하니 코드에서 자동으로 이름이 변경된 것을 확인할 수 있음
const express = require('express')
const app = express()
app.listen(1234)

const userRouter = require('./routes/users')// users(자동으로 이름이 변경됨) 소환하기
app.use("/", userRouter) // app.use를 사용하여 매개변수에 내가 만든 모듈을 추가해줌 

const channelRouter = require('./routes/channel-demo')// channels(자동으로 이름이 변경됨) 소환하기
app.use("/", channelRouter) // "/"는 URL을 정해줄 수 있음!

4️⃣ use URL을 이용하여 공통 URL 골라주기

  • 공통된 URL을 묶어서 정리해줄 수 있음
const express = require('express')
const app = express()
app.listen(1234)

const userRouter = require('./routes/users')// users 소환하기
app.use("/", userRouter) // app.use를 사용하여 매개변수에 내가 만든 모듈을 추가해줌 

const channelRouter = require('./routes/channels')// channels 소환하기
app.use("/channels", channelRouter) // 공통되는 URL을 여기서 설정해주면 불필요한 코드를 생략해줄 수 있음(통일된 URL이 있을 때 유용함)
  • 아래와 같이 수정이 가능함


8-3 회원마다 채널 가지게 ERD 그려보기

1️⃣ 웹페이지 보고 ERD 생각해보기

2️⃣ 간단하게 ERD 고려해보기

  • 회원에서 index id가 없어도 user_id로도 raw를 구별할 수 있으니 굳이 고려하지 않아도 됨

  • 채널은 한 유저가 여러개를 만들 수 있으므로 아래와 같이 설계해 볼 수 있음

  • 위와 같이 ERD를 설계한다면 API 수정이 필요해짐

    • 채널 전체조회를 했을 경우 내가 가진 채널만 뿌려야 하는데 다른 유저의 채널까지 뿌리게 되어 API 수정 필요

8-4 채널 API 설계 수정, 채널 생성 테스트

1️⃣ 채널 API 설계 수정

  • ERD 설계시 문제가 되었던 URL(/channels)을 수정, "채널 생성"과 "채널 전체조회" 를 어떻게 수정하면 좋을까?
  • 유저를 구분하기 위해 userId를 body에 넣어보자(원래는 header에 숨겨서 token으로 가져옴...이부분은 심화 때 알아보자)
// 채널 개별 생성
  .post((req, res) => {
    if (req.body.channelTitle){
      let channel = req.body // 1. json 형태인 req.body의 형태를 바꾸기 위해 변수 선언
      // 2. 생각해보니 body에 userId를 추가만 해주면 되는 것인데 이건 코드를 바꿀 것이 아니라 body에서 추가해야 하는 것임

      db.set(id++, channel) // db에 값을 줄 때 id+1씩 적용

      res.status(201).json({
        message : `${db.get(id-1).channelTitle}채널을 응원합니다.₩`
      })
    } else {
      res.status(400).json({
        message : '요청 값을 제대로 보내주세요'
      })
    }
})
  • 결국 채널 개별 생성은 코드를 건들 필요 없음

2️⃣ 채널 생성 테스트

  • 먼저 그동안 작성한 postman의 이름을 정리해주자
  • 없는 유저 아이디에 채널등록이 가능하고 개별조회도 가능함 이부분은 추후 데이터 베이스를 배울 때 다룰 것이고 지금은 신경쓰지 말자

8-5 회원 채널 조회, id없으면 예외 처리

1️⃣ 채널 전체 조회를 수정해보자


// 채널 전체 조회
  .get((req, res) => { 

    if(db.size){ // db에 무엇이라도 있다면
      let {userId} = req.body //  1. req.body에 있는 userId를 뽑아옴
      let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌

      if (userId == undefined) { // 6. 5-1 예외처리를 이렇게 만들어 줌
        res.status(404).json({
          message : "로그인이 필요합니다."
        })
      } else {
        db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
        // channels[key] = value channels에 키값을 전달하면서 value를 추가
          if(value.userId === userId) // 2. value의 userId와 req.body에서 가져온 userId와 비교해서 같다면
            channels.push(value) // 3. 리스트에 value를 하나씩 추가(push)해줌, 같지 않다면 push 안하면 됨
            // 4. channels에 push가 아무것도 안되어 있을 수 있는데 예외없이 res를 해주면 안됨
        })

        if (channels.length == 0) { // 7. 5-2 예외처리 는 이렇게 만들어 줌
          res.status(404).json({
            message : "조회할 채널이 없습니다."
          })
        } else (
          res.status(200).json(channels) // 여기서 이렇게 보내면 배열 안에 json형태로 잘 보내줌
        )
      }
      // 5. 예외처리 2가지
      // 1) userId가 body에 없으면 -> 6로 이동 / 이 경우는 포인트를 돌릴필요도 없음, 그치만 채널 관리페이지를 들어가기 위해선 마이페이지에서 들어가기 때문에 필요없지만, URL로 접속할 경우를 대비해 로그인하라는 알림을 띄워주자
      // 2) userId가 가진 채널이 없으면 -> 7로 이동

    } else {
      res.status(404).json({
        message : "조회할 채널이 없습니다."
      })
    }
  })
  • "조회할 채널이 없습니다." 메시지가 잘못나온다. 어떤 문제가 있을까?

2️⃣ id없으면 예외 처리 if문 고도화

  • db가 비어있었기 때문에 해당 메시지가 표출된 것이지 아무런 문제는 없음
  • if문이 너무 많다보니 가독성을 높이기 위해 간략화할 필요가 있다. 우선 모든 if문을 긍정문으로 바꿔보자
  .get((req, res) => { 

    if(db.size){ // db에 무엇이라도 있다면
      let {userId} = req.body 
      let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌

      if (userId) {
        db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
            if(value.userId === userId)
              channels.push(value) 
          })
  
          if (channels.length) { 
            res.status(200).json(channels) 
          } else {
            res.status(404).json({
              message : "조회할 채널이 없습니다."
            })
          }

      } else {
        res.status(404).json({
          message : "로그인이 필요합니다."
        }) 
      }

    } else {
      res.status(404).json({
        message : "조회할 채널이 없습니다."
      })
    }
  })
  • 더 가독성을 좋게 하기 위해 큰 기능이 없고 중복이 가능한 if끼리 합쳐줌
  .get((req, res) => { 
      let {userId} = req.body 
      let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌
      
    if(db.size && userId){ // db에 무엇이라도 있고 userId가 있으면 으로 합칠 수 있음

        db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
            if(value.userId === userId)
              channels.push(value) 
          })
  
          if (channels.length) { 
            res.status(200).json(channels) 
          } else {
            res.status(404).json({
              message : "조회할 채널이 없습니다."
            })
          }

//      } else { -> 위 if문을 하나 줄였기 때문에 여기도 줄여줌
//        res.status(404).json({message : "로그인이 필요합니다."}) 
//      }

    } else {
      res.status(404).json({
        message : "조회할 채널이 없습니다."
      })
    }
  })
  • 중복되는 메시지도 함수로 만들어 간단하게 만들어 보면 코드가 훨씬 깔끔해진 것을 알 수 있다.
  .get((req, res) => { 
      let {userId} = req.body 
      let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌
      
    if(db.size && userId){ // db에 무엇이라도 있고 userId가 있으면 으로 합칠 수 있음

        db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
            if(value.userId === userId)
              channels.push(value) 
          })
  
          if (channels.length) { 
            res.status(200).json(channels) 
          } else {
			notFoundChannel()
          }

    } else {
	  notFoundChannel()
    }
  })

function notFoundChannel() {
  res.status(404).json({
	message : "조회할 채널이 없습니다."
  })
}

8-6 백엔드 기초 마무리

1️⃣ API 설계 다듬어 보기

  • ERD대로 회원 API가 잘 작동할 수 있도록 수정해보자

  • 이전 설계에서 수정할 점

    • 로그인에서 id를 userId로 변경
    • 회원 개별 조회에서도 id가 사라졌기 때문에 URL(id)에서 body(userId)로 변경
    • 회원 개별 탈퇴도 마찬가지로 URL(id)에서 body(userId)로 변경

2️⃣ 변경 사항을 코드에 적용해보자

서버

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

const userRouter = require('./routes/users')// users 소환하기
app.use("/", userRouter) // app.use를 사용하여 매개변수에 내가 만든 모듈을 추가해줌 

const channelRouter = require('./routes/channels')// channels 소환하기
app.use("/channels", channelRouter) // 

유저 API

const express = require('express')
 // 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌


router.use(express.json()) //http 외 모듈 사용 'json 모듈'



let db = new Map()
function isExist(obj){
  if (Object.keys(obj).length === 0) {
    return true
  } else {
    return false
  }
}

// 로그인
router.post('/login', (req, res) => {
  console.log(req.body) // userid, pwd

  const {userId, password} = req.body
  let loginUser = {} 

  db.forEach((user, id) => {
    if (user.userId === userId){
      loginUser = user // user가 있다면 userfmf loginUser에 user를 담음
    }
  })

  // 위 코드에서 userId 값을 못 찾았다면
  if(isExist(loginUser)) { // loginuser로도 user의 존재 여부를 알 수 있게 됨

    if (loginUser.password === password) {

      res.status(200).json({
        message : `${loginUser.name}님 로그인 되었습니다.`
      })
    } else {
      res.status(400).json({
        message : `비밀번호가 틀렸습니다.`
      })
    }
  } else {
    res.status(404).json({
      message : `회원정보가 없습니다.`
    })
  }
})


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

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

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





router
  .route('/users')

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

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

module.exports = router

채널 API

const express = require('express')
 // 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌


router.use(express.json()) //http 외 모듈 사용 'json 모듈'


let db = new Map()
let id = 1

router
  .route('/') // route로 URL 묶어주기

// 채널 개별 생성
  .post((req, res) => {
    if (req.body.channelTitle){
      let channel = req.body 
      db.set(id++, channel) // db에 값을 줄 때 id+1씩 적용

      res.status(201).json({
        message : `${db.get(id-1).channelTitle}채널을 응원합니다.`
      })
    } else {
      res.status(400).json({
        message : '요청 값을 제대로 보내주세요'
      })
    }
  })


// 채널 전체 조회
.get((req, res) => { 
  let {userId} = req.body 
  let channels = [] // json array(배열 또는 리스트라 부름)를 만들어줌
  
  if(db.size && userId){ // db에 무엇이라도 있고 userId가 있으면 으로 합칠 수 있음

    db.forEach((value, key) => {// Map에서 하나씩 꺼내서 channels에 더해 줌
        if(value.userId === userId)
          channels.push(value) 
    })

    if (channels.length) { 
      res.status(200).json(channels) 
    } else {
      notFoundChannel()
    }

  } else {
    notFoundChannel()
  }
})

function notFoundChannel() {
  res.status(404).json({
    message : "조회할 채널이 없습니다."
  })
}






router
  .route('/:id')

// 채널 개별 조회
.get((req, res) => {
  let {id} = req.params // req.params 객체에서 URL 경로에 포함된 id 값을 추출하여 id 변수에 저장
  id = parseInt(id) // URL에서 문자열로 추출된 id를 정수로 변환
  let channel = db.get(id) // 해당 id를 키로 하는 채널 정보를 channel 변수에 저장

  if(channel){
    res.status(200).json(channel)
  } else {
        res.status(404).json({
      message : `채널정보를 찾을 수 없습니다.`
    })
  }
})



// 채널 개별 수정
  .put((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    let channel = db.get(id)
    let oldTitle = channel.channelTitle // 올드 타이틀을 만들어줘야 차별화가 가능함

    if(channel){
      let newTitle = req.body.channelTitle // 뉴타이틀을 req.body의 channelTitle에서 가져옴

      channel.channelTitle = newTitle // 가져온 것을 newTitle로 바꿔줌
      db.set(id, channel) // 바꾼 것을 같은 id값에 channel을 덮어쓰기 해줌 

      res.status(200).json({
        message : `${oldTitle}님이 ${newTitle}로 수정 되었습니다.` // 삭제되기 전 객체에서 꺼내와야 함
      })
    } else {
          res.status(404).json({
        message : `채널정보를 찾을 수 없습니다.`
      })
    }
})


// 채널 개별 삭제
  .delete((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    let channel = db.get(id) // 해당 id를 키로 하는 채널 정보를 channel 변수에 저장

    if(channel){
      db.delete(id) // db에서 id를 삭제
      res.status(200).json({
        message : `${channel.channelTitle}님 정상적으로 삭제 되었습니다.` // 삭제되기 전 객체에서 꺼내와야 함
      })
    } else {
          res.status(404).json({
        message : `채널정보를 찾을 수 없습니다.`
      })
    }

})


module.exports = router // 모듈화를 해주기 위해 router를 반환함
profile
김개발의 개발여정

0개의 댓글