본문 바로가기
Node.js

12/13 express.js ==과 ===의 차이, 예외처리 고도화

by 케이리케리 2023. 12. 18.

 

핸들러(handler)?

HTTP request가 오면 자동으로 호출되는 메서드

노드 : 콜백함수로 핸들러를 사용한다.  app.HTTPMETHOD(path, 핸들러)

cf. 스프링에서는 컨트롤러라는 파일 안에서 사용한다.

 

 

과일 개별 조회 : fruits 객체 중에 id값이 url에 입력한 id 값(params.id)과 같은 객체를 찾는다.

 

fruits 배열 인덱스로 접근해서 객체 찾기

const fruits = [
    { id: 1, name: "apple" },
    { id: 2, name: "banana" },
    { id: 3, name: "blueberry" },
    { id: 4, name: "mango" },
];

// 방법 1 - fruits 배열 인덱스로 접근해서 객체 찾기
app.get("/fruits/:id", (req, res) => {
    let id = req.params.id;
    let fruit = fruits[id - 1];
    res.json(fruit);
});

// 방법 2 - forEach 이용
app.get("/fruits/:id", (req, res) => {
    let id = req.params.id;
    let findFruit = "";
    fruits.forEach((fruit) => {
        if (fruit.id == id) {
            findFruit = fruit;
        }
    });
    res.json(findFruit);
});

// 방법 3 - find 함수 이용
app.get("/fruits/:id", (req, res) => {
    let id = req.params.id;
    let findFruit = fruits.find((f) => f.id == id);
 
    res.json(findFruit);
});

 

 

방법 1, 2, 3을 각각 실행시켜 postman에 출력되는 결과

 

 

예외처리

const fruits = [
    { id: 1, name: "apple" },
    { id: 2, name: "banana" },
    { id: 3, name: "blueberry" },
    { id: 4, name: "mango" },
];

app.get("/fruits/:id", (req, res) => {
    let id = req.params.id;
    let findFruit = fruits.find((f) => f.id == id);
 
    if (findFruit) {
        res.json(findFruit);
    } else {
    	// 예외 처리
        // 예외를 터트린다 = http status code를 성공이 아니라 실패로 바꿔서 보냄 -> 실패로 인식하도록 한다
        res.status(404).send("전달주신 id로 저장된 과일이 없습니다.");
        
        // http 상태 코드를 사용하는 이유?
		// 클라이언트(사용자, 화면)와 소통을 정확하기 하기 위함이다
    }
});

 

fruits 배열 안의 객체에 있는 id 값을 입력하면 일치하는 객체가 출력된다.

 

fruits에 없는 id 값이 입력되면 예외처리한 결과가 나온다.

 


 

== vs ===

// ==, === 차이

if (1 == "1") {
    console.log("같다");
} else {
    console.log("같지 않다");
}
//같다 가 출력됨 ==> 자료형은 상관없이, 값만 비교

if (1 === "1") {
    console.log("같다");
} else {
    console.log("같지 않다");
}
//같지 않다 가 출력됨 ===> 자료형이 상관있음. 값도 상관있음

 

 

req.params으로 받아오는 값을 무조건 string(문자열)이기때문에 id의 자료형은 문자열이다.

id - 1 => 문자열 - 1인 셈이다. 
자바스크립트는 암묵적 형변환이 되어 (문자열 - 숫자)연산이 가능해 fruit을 출력할 수 있었지만
자료형을 통일 시키지 않고 연산을 하게된다면 나중에 원하는 결과가 도출되지 않을 수 있기때문에 
꼭 명시적 형변환을 통해 자료형을 통일 시켜줘야한다.
 

자바스크립트의 암묵적 형변환으로 인해 ==의 사용이 가능했다. 

 

하지만 ===으로 변경하게된다면 

app.get("/fruits/:id", (req, res) => {
    let id = req.params.id;
    console.log(`id의 자료형은 : ${typeof id}`);

    let findFruit = fruits.find((f) => f.id === id);
    console.log(findFruit);

    if (findFruit) {
        res.json(findFruit);
    } else {
        res.status(404).json({
            message: "전달주신 id로 저장된 과일이 없습니다.",
        });
    }
});

 

콘솔창에 findFruit의 값은 undefined로 나오고

 

postman에는 예외처리 결과가 나온다.

 

그래서 자바스크립트의 자료형에 항상 신경을 써서 코드를 작성해야한다.

 

예외 처리 고도화

postman에서 전체 유튜버 삭제를 시킨 후 유튜버 전체 조회를 했다.

예상 결과는 db가 존재하지 않으면 http 상태 코드로 404를 보내고 "조회할 유튜버가 없습니다"라는 메세지가 담긴 json이 출력되야한다.

app.get("/youtubers", (req, res) => {
  	console.log(db); 

    let youtubers = {};
    if (db) {
        db.forEach(function (value, key) {
            youtubers[key] = value;
        });

        res.json(youtubers);
    } else {
        res.status(404).json({
            message: "조회할 유튜버가 없습니다.",
        });
    }
});

 

하지만 http 상태 코드가 정상인 200이 나왔고 예상한 메세지가 뜨지 않았다.

 

그래서 콘솔창으로 db를 확인해봤다.

map에 있는 내용 즉, db에 있는 내용을 다 없애긴 했지만 db가 빈 것이 아니다. 

객체가 들어있다. 객체가 있고 안에 있는 요소 갯수가 0일뿐이다.

=> map 객체는 아무리 안에 요소가 없어도 undefined 값이 되지 않는다.

if(db)의 조건에 부합하기때문에 db가 존재할 때 실행문이 작동한 것이다.

 

그래서 if의 조건문을 "db가 존재한다면" -> "db의 요소가 존재한다면"으로 바꿔주었다.

 

다시 postman에서 유튜버 전체를 삭제 후 전체 조회를 했다. 제대로 예외 처리가 된 것을 확인할 수 있었다.

 

예외 처리 고도화를 위해 아래와 같이 개별 유튜버 조회, 개별 유튜버 삭제, 전체 유튜버 삭제, 개별 유튜버 수정 부분에서 각각 status(404)를 추가 작성했다.

const express = require("express");
const app = express();
app.listen(3000);

let youtuber1 = {
    channelTitle: "십오야",
    sub: "600만명",
    videoNum: "1천개",
};
let youtuber2 = {
    channelTitle: "침착맨",
    sub: "228만명",
    videoNum: " 6.7천개",
};
let youtuber3 = {
    channelTitle: "테오",
    sub: "56.9만명",
    videoNum: " 737개",
};

const db = new Map();
let id = 1;
db.set(id++, youtuber1);
db.set(id++, youtuber2);
db.set(id++, youtuber3);

// REST API 설계
//전체 유튜버 조회
app.get("/youtubers", (req, res) => {
   if (db.size !== 0) {
        db.forEach(function (value, key) {
            youtubers[key] = value;
        });

        res.json(youtubers);
    } else {
        res.status(404).json({
            message: "조회할 유튜버가 없습니다.",
        });
    }
});

// 개별 유튜버 조회
app.get("/youtubers/:id", function (req, res) {
    let { id } = req.params;
    id = parseInt(id);

    const youtuber = db.get(id);
    if (youtuber == undefined) {
        res.status(404).json({ message: "유튜버 정보를 찾을 수 없습니다." });
    } else {
        res.json(youtuber);
    }
});

// 유튜버 등록
app.use(express.json());
app.post("/youtubers", (req, res) => {
    db.set(id++, req.body);

    res.json({
        message: `${db.get(id - 1).channelTitle}님의 유튜브 시작을 응원합니다!`,
    });
});

//개별 유튜버 삭제
app.delete;
app.delete("/youtubers/:id", (req, res) => {
    let { id } = req.params;
    id = parseInt(id);

    let youtuber = db.get(id);
    if (youtuber) {
        const channelTitle = youtuber.channelTitle;
        db.delete(id);
        res.json({
            message: `${channelTitle}님, 아쉽지만 다음에 또 뵙겠습니다.`,
        });
    } else {
        //존재하지 않는 유튜버를 삭제한 경우
        res.status(404).json({
            message: `요청하신 ${id}번은 없는 유튜버입니다.`,
        });
    }
});


// 전체 유튜버 삭제
app.delete("/youtubers", function (req, res) {
    if (db.size >= 1) {
        db.clear();
        res.json({
            message: "전체 유튜버가 삭제되었습니다.",
        });
    } else {
        res.status(404).json({
            message: "삭제할 유튜버가 없습니다.",
        });
    }
});

// 개별 유튜버 수정
app.put("/youtubers/:id", function (req, res) {
    let { id } = req.params;
    id = parseInt(id);

    let youtuber = db.get(id);
    let oldTitle = youtuber.channelTitle;
    if (youtuber == undefined) {
        //존재하지 않는 유튜버인 경우
        res.status(404).json({
            message: `요청하신 ${id}번은 없는 유튜버입니다.`,
        });
    } else {
        let newTitle = req.body.channelTitle;
        // console.log(newTitle);
        youtuber.channelTitle = newTitle;
        // db.set(id, youtuber);
        res.json({
            message: `${oldTitle}님, 채널명이 ${newTitle}로 수정되었습니다.`,
        });
    }
});

 

 

post 예외 고도화 - body값에 들어가야할 요소를 다 입력하지 않고 수정을 요청한 경우

 

원래 body에 channelTitle, sub, videoNum에 각각 value값을 입력하고 수정 요청해야한다.

 

현재는 위의 예외를 고려하지 않고 코드를 작성했기 때문에 하나라도 빠트리고 입력해도 수정 요청이 실행된다.

 

이런 예외가 정상으로 인식되어 실행되면 안된다!

 

이 코드에서

app.use(express.json());
app.post("/youtubers", (req, res) => {
    db.set(id++, req.body);

    res.json({
        message: `${db.get(id - 1).channelTitle}님의 유튜브 시작을 응원합니다!`,
    });
});

 

⬇️ 아래와 같이 수정해주었다.

app.use(express.json());
app.post("/youtubers", (req, res) => {
    const channelTitle = req.body.channelTitle;
    if (channelTitle) {
        db.set(id++, req.body);
		
        // status(201) : 등록 성공
        res.status(201).json({
            message: `${db.get(id - 1).channelTitle}님의 유튜브 시작을 응원합니다!`,
        });
    } else {
    	// status(400) : 요청한 연산(처리)를 할 때 필요한 데이터(req)가 덜 왔을 때
        res.status(400).json({
            message: "입력을 완성해주세요.",
        });
    }
});

 

HTTP 상태 코드
2** (성공) 200 조회/수정/삭제 성공
  201 등록 성공
4** (클라이언트 잘못) 400 요청한 연산(처리)을 할 때 필요한 데이터(req)가 덜 왔을 때
  404 찾는 페이지(리소스) 없음(= url에 맞는 api 없음)
5** (서버 잘못) 500 서버가 죽었을 때 (서버가 크리티컬한 오류를 맞았을 때)

 

좌: 등록 성공한 경우, 우: 필요한 입력을 하지 않은 경우