StatesAirline Server 과제
- cors가 무엇인지 알아보고 node.js를 이용하여 서버를 구축한다.
- mini-node server를 공식 문서를 참고하여 직접 만들어보고 express로 리팩토링해 본 후,
지난 스프린트에서 사용한StatesAirline의 API Server를 직접 구현하면서 라우팅하는 방법을 학습한다.
과제 요구 사항
Bare Minimum Requirements
statesairline/controller/flightController.js와 statesairline/controller/bookController.js 에 코드를 작성하세요.
Express 공식문서에서 req.query , req.params를 사용하는 방법을 확인하세요.
Query와 Params를 기준으로 데이터를 필터링하는 코드를 작성해야 합니다.
예약 데이터는 controller/bookController.js 안에 작성된 let booking = []; 배열에 저장해야 합니다.
Flight API -> 항공편 수정은 Advanced Challenges입니다.
Advanced Challenges
Advanced 콘텐츠의 문제를 해결하려면, 충분한 컨텍스트가 필요할 수 있습니다.
Bare minimum requirements를 완료했다면 도전하세요.
생성한 데이터를 수정할 수 없다면, 매번 데이터를 지우고 다시 생성해야 합니다.
이런 불편을 서비스에서 제거하려면, 데이터를 수정할 수 있는 기능을 추가하면 됩니다.
Advanced 콘텐츠에서는 PUT 요청에 따라 항공편을 수정하는 기능을 구현합니다.
1. 목표
Flight API -> 항공편 수정에서 정의한 API 요청을 수행하는 코드를 작성하세요.
Advanced Challenges에 있는 모든 테스트를 통과해야 합니다.
2. 참고 내용
flight.uuid는 변경되면 안 됩니다.flight JSON의 일부만 수정할 수 있어야 합니다.
통과해야 할 테스트
내가 구현한 방법
1. 코드의 흐름을 파악
위의 테스트 목록을 보면 flight Router, Book Router의 파일을 구현해야하는 것으로 보인다.
두 파일을 살펴보자.
flight Router
const { findAll, findById, update } = require('../controller/flightController');
const express = require('express');
const router = express.Router();
router.get('/', findAll);
router.get('/:uuid', findById);
router.put('/:uuid', update);
module.exports = router;
Book Router
const { findAll, findByPhone, findByPhoneAndFlightId, create, deleteByBookingId } = require('../controller/bookController');
const express = require('express');
const router = express.Router();
router.get('/', findAll);
router.get('/:phone', findByPhone);
router.get('/:phone/:flight_uuid', findByPhoneAndFlightId);
router.post('/', create);
//Optional
router.delete('/:booking_uuid', deleteByBookingId);
module.exports = router;
위 두 파일을 보면, 라우터(Router) 모듈을 사용하여 경로에 대한 HTTP GET 및 HTTP PUT 요청을 처리하기 위해 각 함수를 호출하는 라우터를 설정하고 있는 것을 알 수 있다.
즉, 이렇게 설정된 라우터는 Express 애플리케이션에서 해당 URL에 대한 요청이 들어올 경우,
flightController와 bookController모듈의 함수들을 호출하여 요청에 따른 항공편 정보를 처리하는 역할을 하는 것이다.
저 파일에서는 수정해야 할 로직이 없을 것 같으므로 flightController, bookController 파일로 이동해보자.
flightController
const flights = require('../repository/flightList');
const fs = require('fs');
module.exports = {
// [GET] /flight
// 요청 된 파라미터 departure_times, arrival_times 값과 동일한 값을 가진 항공편 데이터를 조회합니다.
// 요청 된 파라미터 departure, destination 값과 동일한 값을 가진 항공편 데이터를 조회합니다.
findAll: (req, res) => {
const { departure_times, arrival_times, destination, departure } = req.query;
// TODO:
return res.json(flights);
},
// [GET] /flight/:uuid
// 요청 된 uuid 값과 동일한 uuid 값을 가진 항공편 데이터를 조회합니다.
findById: (req, res) => {
const { uuid } = req.params;
// TODO:
},
// Advanced
// [PUT] /flight/:uuid 요청을 수행합니다.
// 요청 된 uuid 값과 동일한 uuid 값을 가진 항공편 데이터를 요쳥 된 Body 데이터로 수정합니다.
update: (req, res) => {
const { uuid } = req.params;
const bodyData = req.body;
// TODO:
}
};
bookController
// POST /book에서 사용할 uuid입니다.
const { v4: uuid } = require('uuid');
// 항공편 예약 데이터를 저장합니다.
let booking = [];
module.exports = {
// [GET] /book 요청을 수행합니다.
// 전체 예약 데이터를 조회합니다.
findAll: (req, res) => {
return res.status(200).json(booking);
},
// [GET] /book/:phone 요청을 수행합니다.
// 요청 된 phone과 동일한 phone 예약 데이터를 조회합니다.
findByPhone: (req, res) => {
const {phone} = req.params;
},
// [GET] /book/:phone/:flight_uuid 요청을 수행합니다.
// 요청 된 id, phone과 동일한 uuid, phone 예약 데이터를 조회합니다.
findByPhoneAndFlightId: (req,res) => {
const {phone, flight_uuid} = req.params;
// TODO:
},
// [POST] /book 요청을 수행합니다.
// 요청 된 예약 데이터를 저장합니다.
create: (req, res) => {
// POST /book에서 사용할 booking_uuid입니다.
const booking_uuid = uuid();
// TODO:
},
// Optional
// [DELETE] /book/:booking_uuid 요청을 수행합니다.
// 요청 된 id, phone 값과 동일한 예약 데이터를 삭제합니다.
deleteByBookingId: (req, res) => {
const {booking_uuid} = req.params;
// TODO:
}
};
위의 두 파일을 봤을 때, 각 함수마다 달린 주석에는 구현해야 할 로직들이 의사코드로 작성이 되어있는 것을 볼 수 있다.
따라서, 결론적으로는 순서로만 봤을 때 로직은 이런 순서로 되어있는 것으로 생각하면 쉽다.
app -> flight Router -> flightController
app -> Book Router -> bookController
2. 테스트에 맞게 함수의 로직을 구현
flight Router
- GET /flight?departure_times=2021-12-03T12:00:00&arrival_times=2021-12-03T12:00:00를 입력하면 조건에 해당하는 객체를 응답으로 보냅니다.
- GET /flight?departure=CJU&destination=ICN 을 입력하면 조건에 해당하는 객체를 응답으로 보냅니다.
// [GET] /flight
// 요청 된 파라미터 departure_times, arrival_times 값과 동일한 값을 가진 항공편 데이터를 조회합니다.
// 요청 된 파라미터 departure, destination 값과 동일한 값을 가진 항공편 데이터를 조회합니다.
findAll: (req, res) => {
// req.query : GET 방식으로 넘어오는 데이터,쿼리스트링의 정보를 가져온다.
const { departure_times, arrival_times, destination, departure } =
req.query;
// list 변수에 항공편 데이터를 복사해서 저장
let list = flights;
// 요청받아온 departure_times, arrival_times 데이터가 들어올 때
// 항공편 데이터에 있는 정보와 같다면 같은 값을 필터로 걸러서 list에 저장
if (departure_times && arrival_times) {
list = flights.filter(
(el) =>
departure_times === el.departure_times &&
arrival_times === el.arrival_times
);
}
// 요청받아온 departure, destination 데이터가 들어올 때
// 항공편 데이터에 있는 정보와 같다면 같은 값을 필터로 걸러서 list에 저장
if (departure && destination) {
list = flights.filter(
(el) => departure === el.departure && destination === el.destination
);
}
// list를 json 형식으로 변환하여 리턴
return res.json(list);
}
- GET /flight/:uuid 요청의 응답 객체는 `uuid, departure, destination, departure_times, arrival_times`를 포함해야 합니다.
// [GET] /flight/:uuid
// 요청 된 uuid 값과 동일한 uuid 값을 가진 항공편 데이터를 조회합니다.
findById: (req, res) => {
// 요청받은 파라미터 uuid를 가져온다.
const { uuid } = req.params;
// 항공편 데이터를 list 변수에 저장한다.
let list = flights;
// uuid 파라미터가 들어올 때, 항공편 데이터에 있는 값이랑 동일하다면 같은 값을 찾아서 list에 저장
if (uuid) {
list = flights.filter((el) => uuid === el.uuid);
}
// json으로 변환한 list를 리턴한다.
return res.json(list);
}
Advanced Challenges
- PUT /flight/:uuid 요청의 업데이트 된 객체를 응답으로 보냅니다.
- PUT /flight/:uuid 요청의 일부 데이터만 업데이트 된 객체를 응답으로 보냅니다.
// Advanced
// [PUT] /flight/:uuid 요청을 수행합니다.
// 요청 된 uuid 값과 동일한 uuid 값을 가진 항공편 데이터를 요쳥 된 Body 데이터로 수정합니다.
update: (req, res) => {
// 요청된 파라미터 가져오기
const { uuid } = req.params;
// 요청된 본문 가져오기
const bodyData = req.body;
// 요청된 본문의 uuid 속성만 제외하고 나머지 속성들만 넣어서 새로운 객체를 생성하여 updatedData에 저장
const updatedData = Object.keys(bodyData)
.filter((key) => key !== 'uuid')
.reduce((obj, key) => {
obj[key] = bodyData[key];
return obj;
}, {});
// 요청된 uuid와 모든 항공편 데이터에서 같은 uuid의 위치를 찾아 flightIndex에 저장
const flightIndex = flights.findIndex((flight) => flight.uuid === uuid);
// updatedFlight에 모든 항공편 데이터에서 찾은 uuid와 요청된 수정 값을 저장
const updatedFlight = { ...flights[flightIndex], ...updatedData };
// flights의 동일한 uuid키의 값에 updatedFlight값을 넣어서 수정한다.
flights[flightIndex] = updatedFlight;
// 수정된 값을 json으로 변환하여 리턴
return res.json(updatedFlight);
}
Book Router
- POST /book 요청시, uuid, name, phone 데이터가 booking 배열에 객체 형태로 저장되어야 합니다.
// [POST] /book 요청을 수행합니다.
// 요청 된 예약 데이터를 저장합니다.
create: (req, res) => {
// 요청된 본문을 해석한 객체
const { flight_uuid, name, phone } = req.body;
const booking_uuid = uuid();
// 요청 된 예약 데이터를 객체 형식으로 담아서 예약 정보를 저장할 배열에 넣는다.
const newBooking = { booking_uuid, flight_uuid, name, phone };
booking.push(newBooking);
return res.status(201).json(newBooking);
}
- GET /book/:phone 요청은 특정 예약자 전화번호에 대한 예약 데이터를 응답으로 보냅니다.
// [GET] /book/:phone 요청을 수행합니다.
// 요청 된 phone과 동일한 phone 예약 데이터를 조회합니다.
findByPhone: (req, res) => {
const { phone } = req.params;
// 요청된 phone과 예약정보가 담긴 booking 안에 있는 phone과 같다면 filter를 통해 같은 것만 걸러줌
const result = booking.filter((booking) => booking.phone === phone);
// 응답시 HTTP 상태 코드를 지정하고 phone과 동일한 예약 데이터를 json 형식으로 변환하여 리턴
return res.status(200).json(result);
}
- GET /book/:phone/:flight_uuid 요청은 특정 항공편의 예약자 전화번호에 대한 예약 데이터를 응답으로 보냅니다.
// [GET] /book/:phone/:flight_uuid 요청을 수행합니다.
// 요청 된 id, phone과 동일한 uuid, phone 예약 데이터를 조회합니다.
findByPhoneAndFlightId: (req, res) => {
// 요청 받은 라우트 매개변수에 대한 정보를 가져옴
const { phone, flight_uuid } = req.params;
// 요청된 id와 phone을 가지고 있는 예약 데이터 안에서 같은지 둘다 확인하여 filter로 찾아서 변수에 저장
const result = booking.filter(
(booking) =>
booking.phone === phone && booking.flight_uuid === flight_uuid
);
// 응답시 HTTP 상태 코드를 지정하고 id,phone과 동일한 예약 데이터를 json 형식으로 변환하여 리턴
return res.status(200).json(result);
}
- 테스트 통과 목록은 아니지만 구현하라는 함수 : deleteByBookingId
// Optional
// [DELETE] /book/:booking_uuid 요청을 수행합니다.
// 요청 된 id, phone 값과 동일한 예약 데이터를 삭제합니다.
deleteByBookingId: (req, res) => {
// 요청된 booking_uuid를 가져옴
const { booking_uuid } = req.params;
// 예약된 정보에서 booking_uuid 값과 같은 정보를 찾아서 위치를 찾아 index 변수에 저장
const index = booking.findIndex(
(booking) => booking.booking_uuid === booking_uuid
);
// 위치가 -1이 아니라면 정보가 같은게 있으므로 splice 이용해서 예약 데이터를 삭제한다.
if (index !== -1) {
booking.splice(index, 1);
// send() -> 본문을 보내지 않음을 의미(body 본문)
return res.status(204).send();
} else {
// 동일한 예약 정보가 없으면 찾을 수 없다는 문구 json 형식으로 리턴
return res.status(404).json({ error: 'Booking not found' });
}
}
알게 된 지식 - 요청 객체와 응답 객체 (req, res)
req (요청객체)
- req.get(헤더 이름): 헤더의 값을 가져온다. req.get('Content-type')
- req.body: body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체이다. POST 방식으로 넘어오는 데이터를 담는다.
- req.params: 라우트 매개변수에 대한 정보가 담긴다.
- req.query: GET방식으로 넘어오는 데이터, 쿼리스트링의 정보가 담긴다.
- req.route : 현재 라우트에 관한 정보. 디버깅용.
res (응답 객체)
- res.status(코드) / res.sendStatus(코드): 응답 시의 HTTP 상태 코드를 지정한다.
- res.end(): 데이터 없이 응답을 보낸다.
- res.json(JSON): JSON 형식의 응답을 보낸다.
- res.send(body), res.send(status, body) : 클라이언트에 응답을 보냄. 상태 코드는 옵션
과제 진행에 큰 도움을 받은 자료
[EXPRESS] 📚 요청 객체와 응답 객체 (req, res) 정리
응답 객체 - response res.app: 똑같이 res 객체를 통해 app 객체에 접근한다. res.app.get('')같이 사용 가능. res.set(헤더, 값) / res.setHeader(헤더, 값): 응답의 헤더를 설정한다. req.get()이 헤더값을 가져오는거
inpa.tistory.com
'Boot Camp > code states' 카테고리의 다른 글
[ Code States ] Section 2 / KDT 회고 (0) | 2023.04.10 |
---|---|
[ Code States ] Section 2 / 기술 면접 준비 (0) | 2023.04.10 |
[ Code States ] Refactor Express 과제 (0) | 2023.04.06 |
[ Code States ] Mini Node Server 과제 (0) | 2023.04.06 |
코드스테이츠 OT - [ 메타인지적 학습법 / 앞으로의 나의 다짐 ] (0) | 2023.03.23 |