데브코스 웹풀스택 과정/TIL

0924 로그인 처리 및 고도화, 자바스크립트 Object.keys(), 채널 API 설계구현

thinktank911 2025. 9. 24. 15:56

로그인 기본 로직

  • req.body 안에서 구조분해 할당으로 userId, pwd, name 꺼내기
  • db foreach문 돌려서 db value값의 userId와 req.body에서 보내 온 userId를 비교
  • hasUserId 변수를 false 처리 한 다음 db의 userId와 요청 userId가 같은 경우 true로 변경
  • userId를 찾으면 db 패스워드와 요청 패스워드도 비교한다. 
  • 패스워드가 일치하면 200 코드 보내주고 name 출력
  • 일치하지 않으면 404 코드 보내주고 message 출력
const loginInfo = req.body;
    console.log(loginInfo);     // userId, pwd

    // userId가 DB에 저장된 회원인지 확인
    const {userId, pwd, name} = loginInfo
    var hasUserId = false;

    db.forEach((user, id)=>{
        // a : 데이터, value
        // b : 인덱스, key
        // c : 전체 객체, Map
        if(user.userId === userId){
            hasUserId = true;

            // pwd도 맞는지 비교
            if(user.pwd === pwd){
                res.status(200).json({
                    message : `${name}님 환영합니다.`
                })
            }else{
                res.status(404).json({
                    message : "비밀번호가 일치하지 않습니다."
                })
            }
        }
    })

    if(hasUserId){
        console.log("찾았다");
           
    }
    // userId 값을 못 찾았으면
    else{
        res.status(404).json({
            message : "없는 아이디입니다."
        })
    }

 

로그인 예외 처리

  • 패스워드 비교 구문 또한 hasUserId 조건 구문으로 빼주려 한다.
    이때, user.userId에서 user는 forEach문 안에서만 쓸 수 있는 변수로 밖으로 뺄 수가 없다.
  • loginUser라는 객체 변수를 만들어 forEach문에서 조건값이 맞는 user 데이터를 loginUser에 넣는다.
  • 그러면 패스워드 비교 구문을 forEach문 밖으로 빼내서 hasUserId if문으로 이동할 수 있다.
var hasUserId = false;
    var logInUser = {}

    db.forEach((user, id)=>{
        // a : 데이터, value
        // b : 인덱스, key
        // c : 전체 객체, Map
        if(user.userId === userId){
            hasUserId = true;
            logInUser = user;   // 찾은 user 변수에 넣기
        }
    })

    if(hasUserId){
        console.log("찾았다");
           
        // pwd도 맞는지 비교
        if(logInUser.pwd === pwd){
            res.status(200).json({
                message : `${name}님 환영합니다.`
            })
        }else{
            res.status(404).json({
                message : "비밀번호가 일치하지 않습니다."
            })
        }
    }
  • hasUserId로 조건을 걸지 않고, loginUser로 조건을 걸어도 충분할 것 같다.
  • hasUserId 자리에 loginUser로 대체
var logInUser = {}

    db.forEach((user, id)=>{
        // a : 데이터, value
        // b : 인덱스, key
        // c : 전체 객체, Map
        if(user.userId === userId){
            logInUser = user;   // 찾은 user 변수에 넣기
        }
    })

    if(logInUser){
        console.log("찾았다");
  • 이때 loginUser는 빈 객체로서 if(loginUser) 시 객체가 비었음에도 true값이 된다.
  • 이에 빈 객체를 확인하는 방법을 알아봤다.

 

자바스크립트 Object.keys()

* 빈 객체 확인하는 방법 3가지 : {}
- 객체.keys()
- for
- lodash(라이브러리) : isEmpty

 

  • Object.keys() 활용하여 isEmpty 함수 만들어서 사용해볼 수 있다.
    => 유지보수성에도 좋음. 다른 사람 이해 가능하도록 함수명 지어서 확인할 수 있으니 가독성 좋다.
  • 주의 사항 : 객체인지 먼저 확인이 필요하다.
if(obj.constructor === Object)

 

로그인 고도화

  • 객체 값이 존재하는지 확인하는 함수를 분리해서 조건문에 사용
  • 함수명 : isEmpty() => isExist()로 긍정문 사용 (클린코드 관점)
// 객체 값 존재하는지 확인 함수
function isExist(obj){
    if(Object.keys(obj).length){
        return true;
    }else{
        return false;
    }
}
if(isExist(loginUser)){
        console.log("찾았다");
           
        // pwd도 맞는지 비교
        if(loginUser.pwd === pwd){
            res.status(200).json({
                message : `${name}님 환영합니다.`
            })
        }else{
            res.status(404).json({
                message : "비밀번호가 일치하지 않습니다."
            })
        }
    }

 


채널 API 설계 1탄 - API 명세

  • 설계 목표 : URL, http method/status, req/res
    회원은 계정 1개당 채널 100개를 가질 수 있다.
    채널
    - 채널생성
    - 채널수정
    - 채널삭제

1) 채널 생성 : POST /channels

  • req : body(channelTitle)
  • res 201 : ${channelTitle}님 채널을 응원합니다.
    => 다른 페이지 띄워주고 싶어 ex. 채널관리 페이지

2) 채널 개별 수정 : PUT /channels/:id

  • req : URL(id), body(channelTitle)
  • res 200 : 채널명이 성공적으로 수정되었습니다. 기존 : ${} => 수정 : ${}

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

  • req : URL(id)
  • res 200 : 채널이 삭제되었습니다. => 메인페이지 이동

4) 채널 전체 조회 : GET /channels

  • req : x
  • res 200 : 채널 전체 데이터 list, json array

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

  • req : URL(id)
  • res 200 : 채널 개별 데이터 -> channelTitle

 

채널 API 설계 2탄 - 화면 설계

 

채널 생성 페이지

  • 채널 생성 버튼 클릭 시 => 입력받은 채널명을 받아서 채널 생성(등록) API

채널 관리 페이지(마이페이지 안에 버튼 존재)

  • 생성/수정/삭제 버튼 존재
    (1) 화면 출력 => 이 회원이 소유한 전체 채널 조회 API
    (2) 삭제 버튼 클릭 시 => 개별 채널 삭제 API

개별 채널 수정 페이지
(1) 기존 개별 채널 정보 조회 API
(2) 수정완료 버튼 클릭 시 => 개별 채널 수정 API

 

채널 API 코드 틀 route

  • route로 중복 url 합치기
  • 전체 조회/개별 생성 => '/channels'로 합치기
  • 개별 조회/개별 수정/개별 삭제 =>'/channels/:id'로 합치기
app
    .route('/channels')
    .get()      // 채널 전체 조회
    .post()     // 채널 개별 생성

app
    .route('/channels/:id')
    .get()      // 채널 개별 조회
    .put()      // 채널 개별 수정
    .delete()   // 채널 개별 삭제

 

채널 생성, 채널 개별 조회

 

채널개별조회

 

<이슈>

TypeError: Assignment to constant variable. 는 간단히 말해서 const 로 선언한 변수에 재할당을 시도했을 때 발생하는 오류

 

  • 채널 개별 조회 시 위 에러가 났는데 const 변수를 재할당해줘서 생기는 문제였다. 생각보다 자주 나는 에러라 기억을 해둘 필요가 있다.
  • const를 let으로 변경하니 에러 해결
// 변경 전
const {id} = req.params;
id = parseInt(id);
        
// 변경 후
let {id} = req.params;
id = parseInt(id);

 

채널개별생성

 

<이슈>

Cannot read properties of undefined (reading 'channelTitle')
  • 예외처리를 확인하기 위해 req.body값을 지우고 post 요청을 보냈는데 서버(500)에러가 났다.
  • 예외처리 해줬는데 왜 서버 에러가 나는건지 궁금했다.
// 변경 전
.post((req,res)=>{
        if(req.body.channelTitle){
            // db 등록
            db.set(id++, req.body);

            res.status(201).json({
                message : `${db.get(id-1).channelTitle} 채널이 생성되었습니다.`
            })
        }
        else{
            res.status(400).json({
                message : "입력 값을 다시 확인해주세요."
            })
        }
  • if (req.body.channelTitle) 처리했는데 req.body 가 아예 없는 경우에는 그 줄에서 터진다.
  • 조건문을 req.body 존재 여부부터 확인하도록 req.body를 넣어주니 잘 되었다.
  • body값을 아예 비워버리면 json 파싱부터 실패하므로 req.body가 없으면 {} 객체로 대체해서 보내주도록 처리하는 방법도 있다.
// 변경 후
post((req,res)=>{
        if(req.body && req.body.channelTitle){
            // db 등록
            db.set(id++, req.body);

 

채널 개별 삭제, 개별 수정

 

채널개별수정

.put((req,res)=>{
        let {id} = req.params;
        id = parseInt(id);
        const channel = db.get(id); // db에서 채널 찾기
        var oldTitle = channel.channelTitle;    // db 채널 old타이틀 변수 보관

        if(req.body && channel){
            var newTitle = req.body.channelTitle;   // req.body에서 new타이틀 보관
            channel.channelTitle = newTitle;
            // db에 덮어쓰기
            db.set(id, channel);

            res.status(200).json({
                message : `${oldTitle} -> ${newTitle} 으로 수정되었습니다.`
            })
        }
        else{
            res.status(404).json({
                message : "존재하지 않는 채널입니다."
            })
        }
    })      // 채널 개별 수정

 

<이슈>

db is not a function
  • put 요청 시 에러
  • 오타 : db(id, channel) => db.set(id, channel)

 

채널 전체 조회

  • ` var channels = [] ` (json array) : json을 배열 형태로 보낸다.
  • channels가 배열이므로 값을 넣어줄 때 channels.pusth(value)로 넣어줄 수 있다.
.get((req,res)=>{
        // json array(여러개 json의 배열) 로 보내기
        var channels = []
       
        if(db.size !== 0){
            db.forEach((value, key) => {
                channels.push(value)
            })
            // 꺼낼 때 channels[0]
            res.status(200).json(channels)
        } else{
            res.status(404).json({
                message : "채널이 존재하지 않습니다."
            })
        }

    })      // 채널 전체 조회

 


Object.keys()관련하여 깊이 공부해보고 싶어서 map, object, json을 비교해봤었는데 잘한 선택이었다. 본격적으로 API 설계하고 구현하는 과정에서 에러가 나기 시작하는데 평소에는 그냥 에러가 해결되면 넘기다가 블로그를 쓰기 시작하면서 정리를 하기 시작하니 좀 더 공부가 되는 느낌이다. 좋은 변화다.

흥미로웠던 지점은 함수명을 지을 때 isEmpty()를 관념적으로 많이 썼기에 그냥 넘어갔었는데 긍정문이 가독성이 좋기 때문에 클린코드 관점에서 isExist()로 고쳤던 게 인상적이었다. 의문이 가는 지점은 자바에선 isEmpty() 함수가 따로 있는 걸로 알고 있는데 자바를 아는 사람의 관점에선 isEmpty()로 명명하는 편이 좀 더 이해하기 편하지 않을까 하는 생각을 했다.