본문 바로가기
TIL with Programmers

[TIL] 9/9 API 설계 - 회원 API, 채널 API

by 보먀 2024. 9. 9.
728x90
반응형

1. 미니미니 프로젝트 (실습)

 

지난번에 했던 미니미니 프로젝트를 이어서 해보려고 한다. 지난번에는 회원 API 의 회원가입 / 회원 개별 조회 / 회원 개별 탈퇴까지 했으니, 로그인 API 를 만들어보자.

 

 

이전 내용은 여기에

 

[TIL] 9/6 핸들러, 예외 처리, HTTP 상태코드

1. 핸들러 (Handler)HTTP request 가 날아오면 자동으로 호출되는 메소드 노드에서는 콜백함수로, 콜백함수를 핸들러라고 생각하면 된다. (cf. 스프링에서는 컨트롤러라고 불린다)즉, HTTPMETHOD 를 사용

everydayc0ding.tistory.com

 

 

1.1. 로그인 코드

 

여기서 로그인 하려는 유저의 정보를 담기 위한 변수 loginUser 의 초기값은 빈 객체 ({}) 이다. 

그래서 단순히 if (loginUser){ ... } 이렇게 if 문을 사용해서는 빈 객체가 걸러지지 않기 때문에 다른 방법을 사용해줘야 한다. (빈 객체지만 객체가 존재하기 때문) 

 

Object.keys() 라는 것을 사용해서 빈 객체를 거르는 함수를 만들어 사용하였다. 

if 문에 바로 쓸 수도 있지만, 함수를 만들어 사용하는 것이 가독성이 높기 때문에 함수로 만들어 사용하였다. 

// 로그인
app.post('/login', (req, res) => {
    const {userId, pwd} = req.body
    let loginUser = {}
    db.forEach(user => {
        if (user.userId === userId) {
            loginUser = user
        }
    })
    if (isExisted(loginUser)) {
        if (loginUser.pwd === pwd) {
            res.status(201).json({
                message: `${loginUser.name}님 로그인 성공`
            })
        } else {
            res.status(400).json({
                message: '비밀번호가 틀립니다'
            })
        }
    } else {
        res.status(400).json({
            message: '아이디가 존재하지 않습니다'
        })
    }
})

function isExisted(obj) {
    if (Object.keys(obj).length) {
        return true
    } else {
        return false
    }
}

 

 

 

1.2. 빈 객체 확인하는 방법 3 가지

  • 객체.keys() -> 키 값을 가져오기 때문에 키가 없는 것을 확인
  • for ... in-> for 문을 돌면서 안에 프로퍼티가 있는지 없는지 확인
  • lodash (라이브러리) -> isEmpty 

 

가장 추천하는 방법은 객체.keys() 

const obj1 = {}
const obj2 = { message: '안 빔' }

console.log(Object.keys(obj1).length === 0) // 0 이기 때문에 true
console.log(Object.keys(obj2).length === 0) // 0 이 아니기 때문에 false

 

만약 매번 길게 치기 번거롭다면 함수로 만들어서 사용하는 것도 추천한다.

const obj1 = {}
const obj2 = { message: '안 빔' }

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

console.log(isEmpty(obj1)) // true
console.log(isEmpty(obj2)) // false

 

Object.keys() 를 사용할 때 주의해야 할 점은 이름에서도 느낄 수 있겠지만 객체에만 사용할 수 있다는 것이다. 

객체가 아닌 숫자에 사용하면 이상한 값이 나온다. 추가로 문자열은 거슬러 올라가면 결국 객체이기 때문에 사용이 가능하다. 

const num = 1
const str1 = "one"
const str2 = "" 

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

console.log(isEmpty(num)) // true -> 이상한 값

// 문자열도 결국 객체
console.log(isEmpty(str1)) // true
console.log(isEmpty(str2)) // false

 

 

1.3. 페이지 구성

핑크색은 버튼

 

로그인 페이지

(1) 화면 생성 : API X

(2) 로그인 버튼 클릭 : id, pwd -> 로그인 시켜줄 API

 

회원 가입 페이지

(1) 화면 생성: API X

(2) 회원가입 버튼 클릭: id, pwd, 이름 -> 회원가입 시켜줄 API

 

마이페이지 

(1) 화면 생성: 회원 정보 조회 API

(2) 회원 탈퇴 클릭: 회원 탈퇴를 시켜줄 API

(3) 채널 생성 클릭: API X

 

채널 관리 페이지

(1) 화면 생성: 이 회원이 소유한 전체 채널 조회 API

(2) 수정 버튼 클릭: API X

(3) 삭제 버튼 클릭: 개별 채널 삭제 API

 

채널 수정 페이지

(1) 화면 생성: 기존 개별 채널 정보 조회

(2) 수정 완료 버튼 클릭: 개별 채널 수정 API

 

 

1.4. 채널 API 설계

 

1) 채널 생성 -> POST /channels

- req: body(channelTitle)

- res 201: `${channelTitle} 채널을 응원합니다` 👉 다른 페이지 띄우고 싶음 (ex. 채널 관리 페이지)

 

2) 채널 수정 -> PUT /channels/:id

- req: URL(id), body(channelTitle)

- res 200: `채널명이 성공적으로 수정되었습니다. 기존: ${oldTitle} -> 수정: ${newTitle}`

 

3) 채널 개별 삭제 -> DELETE /channels/:id

- req: URL(id), body

- res 200: `삭제 되었습니다` 👉 메인 페이지

 

4) 채널 전체 조회 GET /channels

- req: X

- res 200: 채널 전체 데이터를 list, json array 등으로 돌려줌

 

5) 채널 개별 조회 GET /channels/:id

- req: URL(id)

- res 200: 채널 개별 데이터

 

 

회원 파트의 API 를 구현할 때와 다르게 route 를 사용하여 코드를 짜 보았다. route 는 같은 URL 을 사용하는 메서드를 모았다가 필요한 곳으로 분기 시키면 된다.

// 예시 코드

app
    .route('/주소')
    .get((req, res) => { ... })
    .post((req, res) => { ... })
    .delete((req, res) => { ... })
    .put((req, res) => { ... })

 

 

 

URL: '/channels'

app
    .route('/channels')

 

- 채널 개별 생성

 

1) 수정할 채널명이 정상적으로 입력된 경우

  • ${channelTitle} 채널을 응원합니다
  • status 201 (POST 성공)

2) 수정할 채널명이 정상적으로 입력되지 않은 경우 (예외처리)

  • 요청 값을 제대로 보내주세요
  • status 400 -> 서버가 요청의 구문을 인식하지 못함 = 사용자가 채널명을 제대로 넣지 않음
.post((req, res) => {
    if (req.body.channelTitle) {
        db.set(id++, req.body)
        res.status(200).json({
            message: `${db.get(id-1).channelTitle} 채널을 응원합니다`
        })
    } else {
          res.status(400).json({
              message: '요청 값을 제대로 보내주세요'
          })
      }
 }) // 채널 개별 생성

 

 

- 채널 전체 조회

 

1) 조회할 채널이 있는 경우

  • json array 에 존재하는 json 객체들을 담아서 보여줌
  • status 200 (요청 성공)

2) 조회할 채널이 없음 =  db 가 빈 경우 (예외처리)

  • 조회할 채널이 없습니다
  • status 404 -> 서버가 요청한 리소스를 찾을 수 없음
.get((req, res) => {
        let channels = [] // json array

        if (db.size) {
            db.forEach((channel) => {
                channels.push(channel)
            })
            res.status(200).json(channels)
        } else {
            res.status(404).json({
                message: '조회할 채널이 없습니다.'
            })
        }
    }) // 채널 전체 조회

 

 

 

URL: '/channels/:id'

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

 

- 채널 개별 조회

 

1) 채널이 존재하는 경우 =  db 에 id 를 키 값으로 갖는 채널이 존재함

  • 채널 정보 돌려줌
  • status 200 (요청 성공)

2) 채널 존재하지 않음 (예외처리)

  • 채널 정보를 찾을 수 없습니다
  • status 404 -> 서버가 요청한 리소스를 찾을 수 없음
.get((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    let channel = db.get(id)
        
    if (channel) {
        res.status(200).json(channel)
    } else {
          res.status(404).json({
              message: '채널 정보를 찾을 수 없습니다.'
          })
      }
}) // 채널 개별 조회

 

 

- 채널 개별 수정

 

1) 수정할 채널명이 제대로 온 경우

  • 채널이 존재하는 경우 -> 채널명이 성공적으로 수정되었습니다. 기존: ${oldTitle} -> 수정: ${newTitle}
  • 채널이 존재하지 않는 경우 -> 채널 정보를 찾을 수 없습니다
  • status 201 (PUT 성공)

2) 수정할 채널명이 제대로 오지 않은 경우 (예외처리)

  • 올바른 채널명이 아닙니다
  • status 400 ->  서버가 요청의 구문을 인식하지 못함 = 사용자가 채널명을 제대로 넣지 않음
.put((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    const newTitle = req.body.channelTitle
    let channel = db.get(id)

    if (newTitle) {
        if (channel) {
            const oldTitle = channel.channelTitle
            channel.channelTitle = newTitle
            db.set(id, channel)
            res.status(201).json({
                message: `채널명이 성공적으로 수정되었습니다. 기존 ${oldTitle} -> 수정 ${newTitle}`
            })
        } else {
              res.status(404).json({
                  message: '채널 정보를 찾을 수 없습니다.'
              })
          }
    } else {
          res.status(400).json({
              message: '올바른 채널명이 아닙니다'
          })
      }
}) // 채널 개별 수정

 

 

- 채널 개별 삭제

 

1) 삭제할 채널이 존재하는 경우

  • ${channelTitle}이 정상적으로 삭제되었습니다.
  • status 200 (요청 성공)

2) 삭제할 채널이 존재하지 않는 경우 (예외처리)

  • 채널 정보를 찾을 수 없습니다.
  • status 404 -> 서버가 요청한 리소스를 찾을 수 없음
.delete((req, res) => {
    let {id} = req.params
    id = parseInt(id)
    const channel = db.get(id)

    if (channel) {
        db.delete(id)
        console.log(db)
        res.status(200).json({
            message: `${channel.channelTitle}이 정상적으로 삭제되었습니다.`
        })
    } else {
          res.status(404).json({
              message: '채널 정보를 찾을 수 없습니다.'
          })
      }
}) // 채널 개별 삭제

 

 

 

1.5. 실습 화면

 

- 채널 개별 생성

 

1) 채널 생성 성공

2) 채널 생성 실패

 

 

 

 

- 채널 전체 조회

 

1) 전체 조회 성공

2) 전체 조회 실패 (조회할 데이터 없음)

 

 

- 채널 개별 조회

 

1) 개별 조회 성공

2) 개별 조회 실패 (조회할 데이터 없음)

 

 

- 채널 개별 수정

 

1) 개별 수정 성공

2) 개별 수정 실패

 

 

- 채널 개별 삭제

 

1) 삭제 성공

2) 삭제 실패

 

 

728x90
반응형