[TIL] Express 활용 : Statesairline-server 과제

ㅜㅜ·2022년 10월 17일
2

Today I learn

목록 보기
34/77
post-thumbnail

오늘 해결했어야 하는 문제들은 위와 같았다.
처음에는 내가 서버의 입장이 되어서 코드를 작성해야 한다는 점이 잘 받아들여지지 않아서 한참동안 코드를 읽어봐야 했다.

파일 목록들 중에서 가장 상위에 있는 파일은 app.js라는 파일이고, 그 다음이 라우터들이 담긴 router.js, 그 다음이 controller 함수들이 담긴 js 파일들이었다.
(refactor 과제와 다른 점은 Router라는 개념이 나온다는 건데 이건 결국 파일들을 나눠서 관리하기 위함이다.)



😋 칭찬할만한 점

  • 이번에는 과제 시작 전에 코드를 정말 꼼꼼하게 읽어봤다. 중간에 주말이 끼어있기도 해서 코드를 읽어보며 이 코드가 대체 어디에 사용되는 것이며, 어떻게 작동하고 있는지 찬찬히 살펴봤는데 막힘없이 술술 풀지는 못했어도 최소 스터디원이나 페어와 같이 누군가의 설명을 들었을 때 이해가 안 되어서 설명을 이해할 수 없는 악순환은 없었던 것 같다.

  • 코드스테이츠가 제공해준 API 문서를 잘 활용했다. 막연히 API라고 계속해서 이야기를 들었을 때는 그래서 어쩌라는 거지, 그런 생각이 들었는데 문서를 잘 살펴보니 어떤 형식으로 어떻게 응답을 전달해야하는지 등의 내용들이 다 적혀 있더라.

  • 사실 이번 페어 프로그래밍에서는 설명을 많이 해야했었는데, 확실히 내가 제대로 이해하지 못한 부분들에 대해서 설명하는 것은 어렵다고 느껴졌다. 그래서 좀 더 열심히 했던 것 같다. 누군가에게 기대려는 마음 없이 간만에 이렇게 열심히 한 것 같아서 조금 뿌듯하다.

🤔 아쉬운 점

  • 어쨌든 내 힘으로 문제를 다 풀었다고는 할 수 없을 것 같다. 많은 분들의 도움을 받았다. 감사히 생각하며 나도 더 정진해서 도움을 줄 수 있는 사람이 되어야지.

  • 구조분해할당 이녀석이 요물인 것 같은데 내가 잘 활용하지 못하는 것 같다. 잘만 사용하면 나도 고급스러운 코드를 쓸 수 있을 것 같은데...?!

  • 공식 문서를 좀 더 잘 해석했다면...하는 아쉬움도 있다. 아직 공식 문서들의 화법(?)에 익숙하지 않은 탓인 것 같은데, 공식 문서 해부하기 같은 콘텐츠를 만들어 보고 싶기도 하다. (... 시간 있냐..너? ㅋㅋ...)





🐸 과제 코드 다시보기

과제 목표

: StatesAirline 서버 구현 - Express 프레임워크를 이용해 만들고 + 로컬 호스트와 연결하기.
States Airline Server는 클라이언트의 요청에 따라 항공편과 예약 데이터를 조회, 생성, 수정, 그리고 삭제하는 기능을 수행할 수 있어야 함.

app.js

서비스에 필요한 미들웨어와 웹 서버를 실행하는 코드가 작성되어 있음.

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

const port = 3001;

const flightRouter = require("./router/flightRouter");
const bookRouter = require("./router/bookRouter");
const airportRouter = require("./router/airportRouter");
//router 폴더에 들어있는 Router.js 파일들은 각각 airport API, book API, flight API 요청을 수행하는 라우터가 작성되어 있음.
//작성된 라우터 내용을 통해 API 요청을 받을 수 있음.

app.use(cors());
app.use(express.json());

app.use("/flight", flightRouter);
app.use("/book", bookRouter);
app.use("/airport", airportRouter);

//중략

app.listen(port, () => {
  console.log(`[RUN] StatesAirline Server... | http://localhost:${port}`);
});


airportController.js

정의된 API 요청을 수행하는 코드를 작성해야 함. (이미 작성되어 있으므로 해당 컨트롤러 내용을 발판삼아 다음 flightController와 bookController를 작성하면 됨.)

[GET] / airport?query={query}

airportController는 GET 요청으로, query라는 이름의 쿼리값으로 들어온 값으로 공항 리스트를 담고 있는 airports라는 배열에서 해당 쿼리의 값(모두 대문자로 바꿔준)이 airports 배열의 각 요소들 내에 존재하는지 filter한 후 해당하는 값들만 반환해 200 상태 코드와 함께 응답을 보낸다.

findAll: (req, res) => {
    if (req.query.query !== undefined) {
      //query가 존재할 때
      console.log(req.query.query);
      const filteredAirports = airports.filter((el) => {
        //airportList에서 받아온 airports를 필터링한 배열을 filteredAirports에 할당할 것임
        return el.code.includes(req.query.query.toUpperCase());
      });
      return res.status(200).json(filteredAirports);
    }
    res.json(airports);
  },


flightController.js

  1. findAll 함수 : [GET] /flight
    요청된 파라미터 departure_times, arrival_times/ departure, destination 값과 동일한 값을 가진 항공편 데이터 조회함.
findAll: (req, res) => {
    const { departure_times, arrival_times, destination, departure } =
      req.query;

    let filteredFlight = flights.slice();

    if (req.query.departure_times) {
      filteredFlight = filteredFlight.filter((el) => {
        return el.departure_times === req.query.departure_times;
      });
    }
    if (req.query.arrival_times) {
      filteredFlight = filteredFlight.filter((el) => {
        return el.arrival_times === req.query.arrival_times;
      });
    }
    if (req.query.destination) {
      filteredFlight = filteredFlight.filter((el) => {
        return el.destination === req.query.destination;
      });
    }
    if (req.query.departure) {
      filteredFlight = filteredFlight.filter((el) => {
        return el.departure === req.query.departure;
      });
    }
    return res.status(200).json(filteredFlight);
  },
  1. findById 함수 : [GET] /flight/:uuid
    요청된 uuid값과 동일한 uuid 값을 가진 항공편 데이터를 조회함.
findById: (req, res) => {
    const { uuid } = req.params;
    // TODO:
    let list = flights.slice();
    if (req.params.uuid) {
      list = list.filter((el) => {
        return el.uuid === req.params.uuid;
      });
    }
    return res.status(200).json(list);
  },
  1. advanced! update 함수 : [PUT] /flight/:uuid
update: (req, res) => {
    const { uuid } = req.params;
    const bodyData = req.body;
    // TODO:

    let findIndex; //flights 배열의 요소들 중 uuid가 같은 요소의 인덱스를 담을 변수
    for (i = 0; i < flights.length; i++) {
      if (flights[i].uuid === uuid) {
        findIndex = i;
      }
    }
    flights[findIndex] = { ...flights[findIndex], ...bodyData };
    //원래 flights[findIndex]의 요소였던 객체를 스프레드 연산자를 이용해 풀어서 새로운 객체 안에 넣고,
    // bodyData로 받아온 객체도 풀어서 새로운 객체 안에 넣게 되는데,
    //이렇게 되면 뒤에 들어온 객체에 동일한 key가 있을 때 뒤에 들어온 값을 따르게 될 것임.
    //자연스럽게 수정이 되는 효과...
    return res.status(200).json(flights[findIndex]);
  },
    
 


bookController.js

자주 등장하는 uuid라는 녀석은 서버단에서 고유한 id를 만들기 위해 대중적으로 사용하는 형식으로 bookController에서는 버전 4를 사용해 고유한 id를 만들어주었다.

1.findAll 함수 : [GET] /book

let booking = [];
//전역 변수 

// 전체 예약 데이터를 조회합니다.
  findAll: (req, res) => {
    return res.status(200).json(booking);
  },
  1. findByphone, findByPhoneAndFlightId 함수
    : [GET] /book/:phone, [GET] /book/:phone/:flight_uuid
// 요청 된 phone과 동일한 phone 예약 데이터를 조회합니다.
findByPhone: (req, res) => {
    const { phone } = req.params;
    if (phone) {
      let data = booking.filter((el) => el.phone === phone);
      return res.status(200).json(data);
    }
  },
  
  // 요청 된 id, phone과 동일한 uuid, phone 예약 데이터를 조회합니다.
  findByPhoneAndFlightId: (req, res) => {
    const { phone, flight_uuid } = req.params;
    // TODO:
    if (phone && flight_uuid) {
      let data = booking.filter(
        (el) => el.phone === phone && el.flight_uuid === flight_uuid
      );
      return res.status(200).json(data);
    }
  },

3.create 함수 : [POST] /book

create 함수를 만드는 데 꽤 오랜 시간을 사용했다... 처음에는 API에서 response부분을 제대로 읽지 않아서 booking_uuid라는 변수를 어디에 써야하는지 헤맸다.

response 에는 booking_uuid, flight_uuid, name, phone 네 가지의 항목이 있어야 했고, 요청으로 들어오는 객체 속에는 booking_uuid를 제외한 나머지 값들이 들어온다.

그러면 booking이라는 예약 데이터 객체들을 요소로 가진 배열에 push를 통해 받아온 req.body 객체를 넣어주면 되는데, 그 전에 랜덤한 uuid를 만드는 booking_uuid를 미리 req.body.booking_uuid = bookin_uuid 와 같이 넣어주면 된다.

create: (req, res) => {
    const booking_uuid = uuid();
    req.body.booking_uuid = booking_uuid; 
   
    booking.push(req.body);
    return res.status(201).json(booking);

    // const {flight_uuid, name, phone} = req.body
    // 구조분해할당을 해주면 아래처럼 키 값들을 변수처럼 사용할 수도 있다
  	// 멋지군. 따로 booking_uuid라는 키 값을 만들어주는 과정이 없어서 편하고 보기 좋은듯. 
    // const newBook = {
    //   booking_uuid,
    //   flight_uuid,
    //   name,
    //   phone
    //  }
    //  booking.push(newBook);
    //  res.status(201).json(newBook)},
  1. deleteByBookingId 함수 : [DELETE] /book/:booking_uuid
// 요청 된 id, phone 값과 동일한 예약 데이터를 삭제합니다.
  deleteByBookingId: (req, res) => {
    const { booking_uuid } = req.params;
    // TODO:

    if (booking_uuid) {
      booking = booking.filter((el) => el.booking_uuid !== booking_uuid);
      return res.status(200).json(booking);
    }
    //booking은 배열
    //배열을 순회하면서 booking_uuid가 동일한 객체를 찾는다
    //해당 객체를 삭제하면 되는데...
    //동일하지 않은 객체들만 filter로 담아주면 되긴 할듯
  },
};




종합문제 오답노트

const express = require('express')
const app = express()
const port = 3000

const logger1 = (req, res, next) => {
  console.log("logger1");
  next();
}

const logger2 = (req, res, next) => {
  console.log("logger2");
  next();
}

const codestates = (req, res) => {
  res.send('Hello CodeStates!');
}

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.use(logger1, logger2);

app.get('/codestates', codestates);

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
  • 위 코드에서 app.use(logger1, logger2) 코드가 app.get('/', //생략) 보다 밑에 있기 때문에 미들웨어 적용이 되어있지 않음. 그러므로 GET /요청을 서버에 보내면 "logger1", "logger2"는 콘솔 출력 X

  • app.get('/codestates', codestates) 코드는 app.use(logger1, logger2)보다 아래에 있기 때문에 미들웨어 적용이되며, GET /codestates요청을 서버에 보내면 "logger1", "logger2"가 콘솔에 출력됩니다.

  • 미들웨어의 next()
    :현재의 미들웨어 함수가 요청-응답 주기를 종료하지 않는 경우에는 next()를 호출하여 그 다음 미들웨어 함수에 제어를 전달해야 합니다. 그렇지 않으면 해당 요청은 정지된 채로 방치됩니다. 출처 : 공식문서
profile
다시 일어나는 중

0개의 댓글