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) 삭제 실패
![]() |
![]() |
'TIL with Programmers' 카테고리의 다른 글
[TIL] 9/11 데이터베이스, DBMS, RDBMS (0) | 2024.09.11 |
---|---|
[TIL] 9/10 라우팅, 예외처리 (0) | 2024.09.10 |
[TIL] 9/6 핸들러, 예외 처리, HTTP 상태코드 (1) | 2024.09.08 |
[회고록] 풀 사이클 개발 데브코스 4주차 회고 (1) | 2024.09.08 |
[TIL] 9/5 api GET/DELETE/PUT, 리팩터링, forEach, map, HTTP 상태코드 (0) | 2024.09.05 |