백엔드 기초 7-3 ~ 7-6 (미니 프로젝트)

Develop Kim·2024년 9월 9일

programmers

목록 보기
11/40

7 미니 프로젝트

7-1 프로젝트 시작

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

  • 회원
    1) 로그인
    2) 회원 가입
    3) 회원 정보 조회
    4) 회원 탈퇴
  • 채널 : 회원은 계정 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}님 다음에 볼께요`
      })
    }
  })


7-3 로그인 로직

1️⃣ 로그인 기본 로직 구현

  • 로그인 로직은 아이디와 비밀번호를 확인하는 것이 핵심이므로 forEach문을 통해 만들 수 있음
app.post('/login', (req, res) => {
  console.log(req.body) // userid, pwd

  //userId가 db에 저장된 회원인지 확인해야
  const {userId, password} = req.body
  db.forEach((user, id) => {
    if (user.userId/*user 안에 있는 id*/ === userId/*req.body 통해서 받은 값*/){
      console.log("같음!!")
      
      // pwd도 맞는지 비교
      if (user.password === password) {
        console.log("비번 같아")
      } else {
        console.log("비번 틀려")
      }
    }
  })
})
  • forEach문을 사용하면서 다시 복습하자면 a는 밸류(요소), b는 키, c는 맵(객체)

  • forEach문으로 등록된 유저를 로그에 찍어보면 정상적으로 작동하는 것을 확인

  • postman의 body에서 찍은 아이디와 db의 아이디가 같은지 확인해보면 정상작동하는 것을 알 수 있음

2️⃣ 로그인 예외 처리

  • userId가 같지 않을 때는 어떻게 해야 할까?
app.post('/login', (req, res) => {
  console.log(req.body) // userid, pwd

  //userId가 db에 저장된 회원인지 확인해야
  const {userId, password} = req.body
  let hasUserId = false
  // hasUserId를 기본적으로 false로 선언하고 userId를 찾았을 때 true로 바꿔주면 됨

  db.forEach((user, id) => {
    if (user.userId/*user 안에 있는 id*/ === userId/*req.body 통해서 받은 값*/){
      console.log("아이디 같음!!")
      hasUserId = true
      
      // pwd도 맞는지 비교
      if (user.password === password) {
        console.log("비번 같아")
      } else {
        console.log("비번 틀려")
      }
    }
    
  })

  // 위 코드에서 userId 값을 못 찾았다면
  if(!hasUserId) {
    console.log('입력한 아이디를 다시 확인해 주세요')
  }
})


  • 클린 코드를 위해 if문을 합쳐보자
app.post('/login', (req, res) => {
  console.log(req.body) // userid, pwd

  //userId가 db에 저장된 회원인지 확인해야
  const {userId, password} = req.body

  let loginUser = {} // forEach에서만 사용하던 user를 다른 블록에서도 사용하기 위해 선언
  // 또, 주의해야 할 점은 {}를 사용하면 전역변수가 되기 떄문에 비어있는 값도 값이 있는 것으로 인식하기 때문에 아이디가 없어도 있는 것으로 인지

  db.forEach((user, id) => {
    if (user.userId/*user 안에 있는 id*/ === userId/*req.body 통해서 받은 값*/){
      // hasUserId = true loginUser를 만들고 나니 필요 없어짐
      loginUser = user // user가 있다면 userfmf loginUser에 user를 담음
    }
  })

  // 위 코드에서 userId 값을 못 찾았다면
  if(loginUser) { // loginuser로도 user의 존재 여부를 알 수 있게 됨
    console.log("아이디 같음!!")

    if (loginUser.password === password) {
      console.log("비번 같아")
    } else {
      console.log("비번 틀려")
    }
  } else {
    console.log('입력한 아이디를 다시 확인해 주세요')
  }
})
  • 위 코드가 정상적으로 작동하려면 let loginUser가 빈 객체인지 구별을 먼저 해줘야 하는데 이때 Object.keys()를 활용해주면 좋음

7-4 자바스크립트 Object.keys()

1️⃣ 빈 객체를 확인하는 방법 3가지
1. 객체: keys() // key값을 가져오게 되면
2. for in // for문은 안에 프로퍼티가 있나 없나 확인하는 법으로 객체 전체를 반복 순회하는 거라 비효율적
3. lodash: isEmpty // 누군가 만들어둔 라이브러리를 활용하는 법

2️⃣ Object.keys() 활용해보기

const obj1 = {}
const obj2 = {message : "use"}

console.log(Object.keys(obj1)) // length === 0, 배열길이가 0
console.log(Object.keys(obj2)) // length === 1, 배열길이가 1

// 배열길이가 있는지로 객체가 비어있는지 확인이 가능함
console.log(Object.keys(obj1).length === 0) // true로 반환
console.log(Object.keys(obj2).length === 0) // false로 반환

// 숫자나 문자열도 될까?
const num = 1
const str1 = 'one'
const str2 = '' // 문자열도 객체임!

console.log(Object.keys(num).length === 0) // true로 반환, 말이 안됨
console.log(Object.keys(str1).length === 0) // false로 반환, 문자열이 있다는 것이 적용이 됨
console.log(Object.keys(str2).length === 0) // true로 반환, 문자열이 없다는 것이 적용이 됨



// 함수를 모듈처럼 사용해본다면 아래와 같이 만들 수 있음
function isEmpty(obj) {
  // 번외 if(obj.constructor === Object) 자바스크립트의 기본문법으로 객체인지 확인하는 방법

  if(Object.keys(obj).length === 0){
    return true;
  } else {
    return false;
  }
}
console.log(isEmpty(obj1))

3️⃣ 로그인 고도화에 적용하기

  • 로그인에 적용하게 되면 정상적으로 작동하는 것을 확인할 수 있음
  if(Object.keys(loginUser).length) { // loginuser로도 user의 존재 여부를 알 수 있게 됨
    console.log("아이디 같음!!")

    if (loginUser.password === password) {
      console.log("비번 같아")
    } else {
      console.log("비번 틀려")
    }
  } else {
    console.log('입력한 아이디를 다시 확인해 주세요')
  }
  • isExist 함수를 만들어 적용하면 여러군데에 적용할 수 있어 코드가 깔끔해짐
const express = require('express')
const app = express()
app.listen(7777)
app.use(express.json()) //http 외 모듈 사용 'json 모듈'

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

// 존재한다는 의미로 exist를 사용한 함수를 만듦
function isExist(obj){
  if (Object.keys(obj).length === 0) {
    return true
  } else {
    return false
  }
}

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

  //userId가 db에 저장된 회원인지 확인해야
  const {userId, password} = req.body

  let loginUser = {} // forEach에서만 사용하던 user를 다른 블록에서도 사용하기 위해 선언
  // 또, 주의해야 할 점은 {}를 사용하면 전역변수가 되기 떄문에 비어있는 값도 값이 있는 것으로 인식하기 때문에 아이디가 없어도 있는 것으로 인지

  db.forEach((user, id) => {
    if (user.userId/*user 안에 있는 id*/ === userId/*req.body 통해서 받은 값*/){
      // hasUserId = true loginUser를 만들고 나니 필요 없어짐
      loginUser = user // user가 있다면 userfmf loginUser에 user를 담음
    }
  })

  // 위 코드에서 userId 값을 못 찾았다면
  if(isExist(loginUser)) { // loginuser로도 user의 존재 여부를 알 수 있게 됨
    console.log("아이디 같음!!")

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

7-5 채널 API 설계

1️⃣ 채널 API 설계(URL, http method/status, req/res)

  • 1) 채널 개별 "생성" : POST/channels
    • req : body(channelTitle)
    • res :(201) ${channelTitle}님 채널을 응원합니다 👉 채널 관리페이지 등 다른 페이지 띄워주기
  • 2) 채널 개별 "수정" : PUT/channels/:id
    • req : URL(id), body(channelTitle) 👉 URL도 수정은 물론 채널 이름도 수정할 수 있도록 가져옴
    • res :(200) 채널명이 성공적으로 수정되었습니다. 기존: ${}-> 수정: ${}
  • 3) 채널 개별 "삭제": DELETE/channels/:id
    • req : URL(id)
    • res :(200) 삭제되었습니다. 👉 메인페이지로 이동
  • 3) 채널 전체 "조회": GET/channels
    • req : 필요 없음
    • res :(200) 채널 전체 데이터 list, json array
  • 3) 채널 개별 "조회": GET/channels/:id
    • req : URL(id)
    • res :(200) 채널 개별 데이터

2️⃣ 채널 생성페이지 기획 시각화

  • '마이 페이지'에 채널 관리 페이지 추가

  • 채널 관리 페이지

    • 유저가 가지고 있는 모든 채널들을 보여주도록 하고 각 채널별 [수정]으로 채널 수정 페이지로 연결
    • 화면 출력 시 유저가 소유한 전체 채널 조회 API가 필요함
    • 삭제 버튼 클릭 시 개별 채널 삭제 API가 필요함
    • 채널 생성 버튼 클릭 시 생성 페이지로 이동
  • 채널 생성 페이지

    • 채널 생성 버튼 클릭 시 입력 받을 채널명 받아서 채널 등록을 해줘야 함!
  • 채널 개별 수정 페이지

    • 개별 채널명 수정을 할 수 있는 페이지로 기존 채널의 개별 정보 조회를 해야 함
    • 개별채널 정보조회개별채널 수정 API가 필요함

7-6 채널 API 코드 작성

1️⃣ 채널 API 코드 틀

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


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

// 채널 개별 생성
  .post((req, res) => {
    res.json("개별생성")
})

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


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

// 채널 개별 수정
  .put((req, res) => {
    res.json("개별수정")
})

// 채널 개별 삭제
  .delete((req, res) => {
    res.json("개별삭제")
})

// 채널 개별 조회
  .get((req, res) => {
    res.json("개별조회")
})

2️⃣ 채널 생성/전체조회/개별조회/개별수정/개별삭제

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


let db = new Map()
let id = 1

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

// 채널 개별 생성
  .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 : '아무것도 없는 걸요'
      })
    }
  })





app
  .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 : `채널정보를 찾을 수 없습니다.`
      })
    }

})
  • 전체조회를 했을 때 배열에 json형태로 객체들이 담긴 것을 확인할 있음
profile
김개발의 개발여정

0개의 댓글