Express로 미니프로젝트 만들기 - 1

wltjd1688·2025년 2월 19일

풀사이클

목록 보기
20/74

핸들러(handler)란?

HTTP request가 날안오면 자동으로 호출되는 메소드, 노드에서는 콜백함수로 app.HTTPMETHOD(path, 핸들러)이다.
cf. 스프링의 컨트롤러

  • 여러 해석이 있지만 요청에 의해 호출되는 메소드이다.
  • 지금까지 코드에서는 api의 콜백함수가 이에 해당한다.

app.get위에 마우스 올리고, 뒤에 콜백함수를 보면 그걸 핸들러라하는게 보인다.

"찐" 예외처리

현재까지 작성한 youtuber-demo에서 다 해결하지 못한 예외처리를 조금 더 해볼려고 한다. 그전에 map뿐만 아니라 array로도 한번 api를 만들어 보자.

app.get('/fruits/:id', (req, res) => {
    let id = req.params.id;
    // let fruit = fruits[id-1]
    let findFruit = fruits.find(f=> f.id == id); // fruits 배열 안에 있는 객체 중, id값이 parmams.id랑 같은 객체를 찾는다.
    // fruits.forEach((v,_)=>{
    //     if (v.id===id){
    //         findFruit = v
    //     }
    // })
    res.json(findFruit)
});

임의로 상태코드 불러오는 코드 => .status()
클라이언트와 명확한 통신을 하기 위해서 사용함

🤔 === 와==의 차이
기본적으로 자바스크립트는 동적으로 타입을 변경해주기에 상관없지만 나중에 ===과 ==을 구분할 줄 알아야한다.
=== -> 타입도 비교
== -> 값만 비교(타입 달라도 됨)

기존 코드로 알아보는 예외처리

기존에 작성한 youtube-demo코드에서 예외처리를 해볼려고 한다.

  1. 전체 조회에서 db에 담겨있는 정보가 없을때는?

    • if(db.size > 0)을 추가함

      // 유투버 전체 조회시, 조회할 데이터가 없을 경우 if문을 통해 처리함
              var youtubers = {};
      
              if(db.size > 0) {
                  db.forEach((value,key)=>{
                      youtubers[key] = value
                  })
                  res.json(youtubers);
              } else {
                  res.status(404).json({
                      message : "조회할 유튜버가 없습니다."
                  })
              }
  2. post에서 body가 비워져 있는경우

    • 400띄워줄거임

      // if을 사용하여 channelTitle이 비어있는 경우 res.status(400)을 띄워줌
      ...
              const channelTitle = req.body.channelTitle;
              if(channelTitle){
                  let newYoutubers ={
                      channelTitle: channelTitle,
                      sub: 0,
                      videoNum: 0,
                  }
      
                  const id = idx++;
                  db.set(id,newYoutubers)
      
                  res.status(201).json({
                      "message": `${db.get(id).channelTitle}님, 유튜버 생활을 응원합니다.`
                  })
              } else {
                  res.status(400).json({
                      "message": "올바른 정보를 입력해주세요"
                  })
              } 
      ...

다시 알아보는 상태코드

  • 40x: 클라이언트 잘못
    • 400: 요청한 연산(처리)를 할 때 필요한 데이터(req)가 덜 왔거나 없을때
  • 50x: 서버 잘못
  • 20x: 성공
    • 201: 요청을 성공적으로 수행함

미니 프로젝트(?)

라우터, 유효성, 예외사항, 리팩토링, 데이터베이스 연동까지 할 예정이다.

회원

  • 로그인
  • 회원 탈퇴

채널

  • 생성(계정 1개당 100개까지)
  • 수정
  • 삭제

이걸 하기전에 화면을 그려주는건 많은 도움이 된다.

회원가입 api

  1. 로그인 => POST "/login"
    • req: id, pwd를 body에 담아서 보낼 예정
    • res: "OO님 환영합니다" => 메인페이지로 보내주기
  2. 회원가입 => POST "/join"
    • req: id, pwd, name을 body에 담아서 보낼 예쩡
    • res: "OO님 회원가입을 환영합니다" => 로그인 페이지로 보내줌
  3. 마이페이지(회원 정보 조회) => GET "/users/:id"
    • req: params.id를 얻음
    • res: id, name
  4. 회원 탈퇴(개별 탈퇴) => DELETE "/users/:id"
    • req: params.id를 얻음
    • res: "${name}님 다음에 또 뵙겠습니다" or 메인페이지

내가 작성한 코드

const express = require("express")
const app = express()

app.listen(7777)

let db = new Map();
let idx = 1;

app.use(express.json())

// 로그인
app.post("/login", (req, res)=>{
    try{
        isExist(req.body);
        const {userId, password} = req.body;
        let userInfo = {};

        if (!userId) return res.status(400).json({message: "아이디를 입력해주세요"})
        if (!password) return res.status(400).json({message: "비밀번호를 입력해주세요"})

        db.forEach((value)=>{
            if (value.userId===userId) return userInfo = value;
        })

        isExist(userInfo,"없는 아이디 입니다");
        if (userInfo.password = password){
            return res.status(200).json({message: `${userInfo.name}님 환영합니다.`})
        } else {
            return res.status(401).json({message: "비밀번호가 틀렸습니다."})
        }
        // 메인 페이지로 이동
    } catch {
        res.status(500).json({message: "서버와의 연결에 실패하였습니다."})
    }
    
})

// 회원가입
app.post("/join", (req, res)=>{
    try {
        isExist(req.body);
        const id = idx++;
        const userInfo = {
            userId: req.body.userId,
            password: req.body.password,
            name : req.body.name
        }

        if (!userInfo.userId) return res.status(400).json({message: "아이디를 입력해주세요"})
        if (!userInfo.password) return res.status(400).json({message: "비밀번호를 입력해주세요"})
        if (!userInfo.name) return res.status(400).json({message: "이름을 입력해주세요"})
        
        db.forEach((value)=>{
            if (value.userId===userInfo.userId) return res.status(401).json({message: "이미 사용중인 아이디 입니다. 다시 입력해주세요"})
        })

        db.set(id, userInfo)
        return res.status(200).json({message: `${userInfo.name}님 회원가입을 환영합니다.`})
        // 로그인 페이지로 이동
    } catch {
        res.status(500).json({message: "서버 연결에 실패했습니다."})
    }
})

app.route('/users/:id')
    .get((req,res)=>{
        try{
            let id = req.params.id;
            id = parseInt(id)

            const userInfo = db.get(id);
            if (userInfo){
                res.status(200).json({ 
                    userId: userInfo.userId,
                    name: userInfo.name
                })
            } else {
                res.status(404).json({ message: "회원 정보가 업습니다."})
            }
        } catch {
            res.status(500).json({message: "서버 연결에 실패했습니다."})
        }
    })
    .delete((req,res)=>{
        try {
            let id = req.params.id;
            id = parseInt(id)
                
            const userInfo = db.get(id);
            if (userInfo){
                const beName = userInfo.name;
                db.delete(id)
                res.status(200).json({message: `${beName}님 다음에 또 뵙겠습니다.`})
            } else {
                res.status(404).json({ message: "회원 정보가 업습니다."})
            }
        } catch {
            res.status(500).json({message: "서버 연결에 실패했습니다."})
        }
    })

app.all("*", (req,res)=>{
    res.status(404).json({ message: "올바른 URL을 입력해주세요."});
});

function isExist(obj, text="입력한 값을 확인해주세요"){
    if (Object.keys(obj).length) {
        return res.status(400).json({ message: text})
    }
};

각 코드마다 리팩토링 하기

  1. 회원가입
    • 회원가입(성공): ✅

    • 회원가입(중복): ✅

    • 회원가입(아이디 누락): ✅

    • 회원가입(비밀전호 누락): ✅

    • 회원가입(이름 누락): ✅

    • 회원가입(body 누락): 🛑 => ✅

      1. body누락시 아이디 누락과 같은 동작이 나옴

      2. 코드중 if (req.body == {}) res.status(401).json({ message: "입력한 값을 확인해주세요"}) 가 동작하지 않음을 확인

      3. req.body 출력값이 {}가 나오지만 {}, "{}", undefined, null, false와 비교했지만 모두 같지 않다고 나옴
        => {}는 자바스크립트에서 객체가 참조형이기 때문에 두 개의 빈 객체라도 서로 다른 메모리 주소를 참조하게 되면 같지 않다고 하며, 그 이외에 "{}", undefined, null, false는 타입 자체가 다르기 때문에 다르다고 한다.

      4. 해결방법으로는 Object.keys(req.body).length 로 아에 길이를 활용하여 객체가 비어있다는 것을 확인해야 한다고한다.

회원가입-body누락(이전)
⬇️
회원가입-body누락(수정)

  1. 로그인

    • 로그인(성공):✅
    • 로그인(유저없음):✅
    • 로그인(아이디 누락):✅
    • 로그인(비밀번호 누락):✅
    • 로그인(body 누락):🛑 => ✅, 회원가입 수정하면서 수정
  2. 마이페이지

    • 마이페이지(성공):✅

    • 마이페이지(유저없음):🛑 => ✅

      1. 유저가 없을때, 바로 catch로 가버리는 에러 확인

      2. 코드에서 userInfo로 db에서 정보를 불러온 다음, 바로 내보낼 변수에서 userInfo.name을 불러오는데 이게 문제가 되었음을 확인

      3. 순서를 변경하여 userInfo를 하고 if문을 통해 먼저 올바른 값이 들어왔는지 확인 후 할당하는 것으로 변경 -> 해결

    • 마이페이지(파라미터 누락):🛑 => ✅

  3. 회원 탈퇴

    • 회원 탈퇴(성공):✅
    • 회원 탈퇴(유저없음):🛑 => ✅, 마이페이지 수정하면서 수정함
    • 마이페이지(파라미터 누락):🛑 => ✅

✚ 마이페이지와 회원 탈퇴의 파라미터 누락
app.router를 통해 "/users" 페이지를 요청하게 되면, URL을 확인하라고 보내달라는 코드를 작성할 려고 했는데, 그냥 api외에 모든 페이지에 대해서 신청할 수는 없는지가 궁금해 찾아봤다.
node.js에서는 app.all이라는 함수를 통해 HTTP메서드에 상관없이 모든 요청 경로에 대한걸 처리할 수 있다는 것을 알게되었다.

app.all("*", (req,res)=>{
  res.status(404).json({ message: "올바른 URL을 입력해주세요."});
});


수정사항

  • 기존에 id(수정 후 userId)를 통해서 조회 및 로그인 하던걸 userId로 따로 관리하면서, 각 객체의 id에 대한걸 넣어줌
  • 위의 수정사항으로 db.has(id)나 db.get(id)하던 로직을 db.forEach를 통해 userId를 찾다록 수정
  • if(Object.keys(---) === 0)을 사용하던 코드를 if(Object.keys(---).length===0)으로 암묵적 타입변환을 방지함
  • 고도화를 위해서 함수를 따로 작성함
profile
일단 해!!!!

0개의 댓글