๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Frontend Dev/๐Ÿฅ ์ฝ”๋“œ์Šคํ…Œ์ด์ธ  FE ๋ถ€ํŠธ์บ ํ”„

Section2 Unit10 [Web Server] ๊ธฐ์ดˆ - StatesAirline Server

๋ฐ˜์‘ํ˜•


Section2 Unit10 [Web Server] ๊ธฐ์ดˆ - StatesAirline Server

 

โญ๏ธ ๊ณผ์ œ.  StatesAirline Server

โœ”๏ธ Bare Minimum Requirement

 โœ… statesairline/controller/flightController.js์™€ statesairline/controller/bookController.js ์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.

 โœ… Express ๊ณต์‹๋ฌธ์„œ์—์„œ req.query , req.params๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•˜์„ธ์š”. Query์™€ Params๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 โœ… ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋Š” controller/bookController.js ์•ˆ์— ์ž‘์„ฑ๋œ let booking = []; ๋ฐฐ์—ด์— ์ €์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 โœ… Flight API -> ํ•ญ๊ณตํŽธ ์ˆ˜์ •์€ Advanced Challenges์ž…๋‹ˆ๋‹ค.

 

๊ตฌํ˜„๊ณผ์ • & ์ฝ”๋“œ

 API ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉฐ Bare Minimum๊ณผ Advanced Challenges ๋ชจ๋‘ ๊ตฌํ˜„ ์™„๋ฃŒํ–ˆ๋‹ค.

 Node.js์™€ Express๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ •์‹ ์ด ํ•˜๋‚˜๋„ ์—†์–ด์„œ ๋ธ”๋กœ๊ทธ์— ๋‹ค๋ฅธ ๋‚ด์šฉ์€ ์ •๋ฆฌ๋ฅผ ๋ชปํ–ˆ์—ˆ์ง€๋งŒ, ์ด๋ฒˆ์— ๊ณผ์ œ๋ฅผ ํ†ตํ•ด req.body, req.params, req.query ๊ณต๋ถ€ํ•˜๋ฉฐ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค. 

 ์„œ๋ฒ„ ์ชฝ์€ ๊ณต๋ถ€ํ•ด๋ณผ ์ƒ๊ฐ์กฐ์ฐจ ๋ชปํ–ˆ์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ ์œ ๋‹›์„ ํ†ตํ•ด Node.js์™€ Express๋ฅผ ๋‹ค๋ค„๋ณผ ์ˆ˜ ์žˆ์–ด์„œ ๋‹คํ–‰์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. (ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋„ ์„œ๋ฒ„๋ฅผ ๋งŒ๋“ค์ค„์€ ์•Œ์•„์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค...๐Ÿ˜ญ ํ•  ์ค„ ์•Œ๋ฉด ์ข‹์€๊ฑด ๋‹น์—ฐํžˆ ์•Œ๊ฒ ์ง€๋งŒ ์ƒ๊ฐ๋ณด๋‹ค ์žฌ๋ฐŒ์–ด์„œ ๋” ๊ณต๋ถ€ํ•ด๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. )

 

Express ์š”์ฒญ ๊ฐ์ฒด req.body, req.params, req.query ์‚ฌ์šฉ ์˜ˆ์ œ

req.body req.params req.query๋Š” Express์˜ ์š”์ฒญ ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๋กœ ๊ฐ๊ฐ ์š”์ฒญ ๋ณธ๋ฌธ, ๊ฒฝ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜, ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. ๐Ÿ‘พ req.body POST ์š”์ฒญ์˜ ๋ณธ๋ฌธ(body)์— ํฌํ•จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด

fay-story.com

 

๊ณผ์ œ์—์„œ ๋ฉ”์ธ์œผ๋กœ ๋‹ค๋ค„์•ผ ํ•  ์ฝ”๋“œ๋Š” flightController.js ์™€ bookController.js ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์ด๋ฒˆ ๊ณผ์ œ์—์„œ๋Š” ๊ด€์‹ฌ์‚ฌ๋ถ„๋ฆฌ๊ฐ€ ๋œ ํด๋”๊ตฌ์กฐ์™€ ๋‚˜๋จธ์ง€ ํŒŒ์ผ๋“ค๋„ ๋ˆˆ์—ฌ๊ฒจ ๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋„ ํ•จ๊ป˜ ์‚ดํŽด๋ณด์•˜์—ˆ๋‹ค.

 

โญ• ๊ณผ์ œ ์ œ์ถœ ํ›„ ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ๊ณต๋ถ€๋ฅผ ํ•˜๋ฉฐ ๋น„๊ตํ•ด๋ณด๊ณ , ์ผ๋ถ€ ์ž‘์—…์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

 1. ๊ฐ ํ•จ์ˆ˜๋งˆ๋‹ค ํ•„์š”ํ•  ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•˜์—ฌ ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์ด ์œ ํšจํ•œ ๊ฐ’์ธ์ง€ ํ™•์ธํ•˜๋Š” ์ž‘์—…์„ ์ถ”๊ฐ€ํ•˜์˜€๊ณ 

 2. ์š”์ฒญ ํ›„ ์‘๋‹ต์‹œ ์ƒํƒœ์ฝ”๋“œ ๋˜ํ•œ ์ ์ ˆํ•œ ๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•ด ์ฃผ์—ˆ๋‹ค.

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋กœ ๋” ๋ช…ํ™•ํ•œ ๊ฒ€์‚ฌ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์ผ€์ด์Šค๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ์„ฑ๊ณตํ•œ ์š”์ฒญ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์œ ํšจํ•˜์ง€ ์•Š์€ ์š”์ฒญ์— ๋Œ€ํ•œ ์ƒํƒœ์ฝ”๋“œ ๋˜ํ•œ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์•„๋ž˜๋Š” flightController.js ์™€ bookController.js์˜ ๋‚ด๊ฐ€ ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ์™€ ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๋‹ค์‹œ ์ •๋ฆฌํ•œ ์ฝ”๋“œ ๐Ÿ˜Š

 

 

 ๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป flightController.js 

const flights = require('../repository/flightList');

module.exports = {
  // [GET] /flight
  findAll: (req, res) => {
    const { departure_times, arrival_times, destination, departure } = req.query;

    /** ์œ ํšจํ•œ Airport Code์ธ์ง€ ํ™•์ธ
     * /^[A-Z]{3}$/ ์„ธ ๊ฐœ์˜ ์—ฐ์†๋œ ๋Œ€๋ฌธ์ž ์•ŒํŒŒ๋ฒณ
     * .test() ์ •๊ทœ ํ‘œํ˜„์‹ ํŒจํ„ด์— ๋Œ€ํ•œ ๋ฌธ์ž์—ด ๋งค์นญ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” JS ๋ฉ”์„œ๋“œ (true ๋˜๋Š” false ๋ฐ˜ํ™˜)
     */
    const isValidAirportCode = (code) => {
      return typeof code === 'string' && code.length === 3 && /^[A-Z]{3}$/.test(code)
    }

    /** ์œ ํšจํ•œ ๋‚ ์งœ์ธ์ง€ ํ™•์ธ
     *  date.toString() === 'Invalid Date' : Date ๊ฐ์ฒด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ (์œ ํšจํ•˜์ง€ ์•Š์€ ๋‚ ์งœ์ผ ๊ฒฝ์šฐ Invalid Date ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.)
     * date.toISOString() !== timestamp : ์œ ํšจํ•œ ๋‚ ์งœ/์‹œ๊ฐ„ ๊ฐ’์ธ์ง€ ๊ฒ€์‚ฌ
     */
    const isValidDate = (timestamp) => {
      if (!timestamp) {
        return false;
      }
      
      const date = new Date(timestamp);

      return date.toString() === 'Invalid Date' || date.toISOString() !== timestamp;
    }

    /** TODO
     * ์š”์ฒญ ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ departure_times, arrival_times, departure, destination ๊ฐ’๊ณผ ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ€์ง„ ํ•ญ๊ณตํŽธ ๋ฐ์ดํ„ฐ ์กฐํšŒ
     */

    // ์š”์ฒญ๋œ ์ฟผ๋ฆฌ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ „์ฒด ํ•ญ๊ณตํŽธ ์ถœ๋ ฅ
    if (Object.keys(req.query).length === 0) return res.json(flights); 

    // departure_times, arrival_times
    // Ex. http://localhost:3001/flight?departure_times=2021-12-02T12:00:00&arrival_times=2021-12-03T12:00:00
    else if (isValidDate(departure_times) && isValidDate(arrival_times)) {
      console.log("test")
      const filteredData = flights.filter (
        (flight) => flight.departure_times === departure_times && flight.arrival_times === arrival_times
      )
      return res.json(filteredData);
    } 

    // departure, destination
    // EX. http://localhost:3001/flight?departure=ICN&destination=CJU
    else if (isValidAirportCode(departure) && isValidAirportCode(destination)) {
      const filteredData = flights.filter (
        (flight) => flight.departure === departure && flight.destination === destination
      )
      return res.json(filteredData);
    }

    // departure๋งŒ ์ „๋‹ฌํ•œ ๊ฒฝ์šฐ
    // EX. http://localhost:3001/flight?departure=ICN
    else if (isValidAirportCode(departure)) {
      const filteredData = flights.filter (
        (flight) => flight.departure === departure
      )
      return res.json(filteredData);
    }   

    // ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ
    else {
      return res.status(400).json('Incorrect request');
    }
  },

  // [GET] /flight/:uuid
  // TODO: ์š”์ฒญ ๋œ uuid ๊ฐ’๊ณผ ๋™์ผํ•œ uuid ๊ฐ’์„ ๊ฐ€์ง„ ํ•ญ๊ณตํŽธ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  // Ex. http://localhost:3001/flight/af6fa55c-da65-47dd-af23-578fdba40bed
  findById: (req, res) => {
    console.log(req.params) // { uuid: 'af6fa55c-da65-47dd-af23-578fdba40bed' }
    const { uuid } = req.params;
    const filteredData = flights.filter((flight) => flight.uuid === uuid);
 
    // ์œ ํšจํ•˜์ง€ ์•Š์€ uuid์˜ ๊ฒฝ์šฐ 404
    if (filteredData.length) {
      return res.json(filteredData);
    } else {
      return res.status(404).json('Not Found');
    }
  },

  // Advanced
  // [PUT] /flight/:uuid ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  // TODO: ์š”์ฒญ ๋œ uuid ๊ฐ’๊ณผ ๋™์ผํ•œ uuid ๊ฐ’์„ ๊ฐ€์ง„ ํ•ญ๊ณตํŽธ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ณฅ ๋œ Body ๋ฐ์ดํ„ฐ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

  /* ๊ธฐ์กด data
    [{
        "uuid": "af6fa55c-da65-47dd-af23-578fdba40bed",
        "departure": "ICN",
        "destination": "CJU",
        "departure_times": "2021-12-02T12:00:00",
        "arrival_times": "2021-12-03T12:00:00"
    }]
  */

  update: (req, res) => {
    const { uuid } = req.params;
    const bodyData = req.body;

    /*
    // -----------------------------------------
    // 1. ๋‚ด๊ฐ€ ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ
    const filteredData = flights.filter((flight) => flight.uuid === uuid);

    // Object.assign() bodyData์˜ ์†์„ฑ์„ ๋ณต์‚ฌํ•ด filteredData[0]์— ๋ฐ˜์˜ํ•œ ํ›„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    const updatedData = Object.assign(filteredData[0], bodyData);
    // -----------------------------------------
    */

    
    // -----------------------------------------
    // 2. ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ
    // findIndex() ์ „๋‹ฌ๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ ๋งŒ์กฑํ•˜๋Š” ๋ฐฐ์—ด์˜ ์ฒซ๋ฒˆ์งธ ์š”์†Œ์˜ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ˜ํ™˜. ๋งŒ์กฑํ•˜๋Š” ๊ฐ’์ด ์—†์œผ๋ฉด -1์„ ๋ฐ˜ํ™˜.
    const updatedIdx = flights.findIndex((flight) => flight.uuid === uuid);
    if (updatedIdx === -1) {
      return res.status(404).json('Not Found')
    }
    const updatedData = { ...flights[updatedIdx], ...bodyData }
    console.log(updatedData)

    // splice() ์ธ๋ฑ์Šค updatedIdx ๋ถ€ํ„ฐ, 1๊ฐœ์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , updatedData ์‚ฝ์ž…
    // ์ฆ‰, ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ์ธ updatedData ์‚ฝ์ž…
    flights.splice(updatedIdx, 1, updatedData)
    // -----------------------------------------
    
    res.status(200).json(updatedData)
  }
};

 

 ๐Ÿ‘ฉ๐Ÿป‍๐Ÿ’ป bookController.js 

// POST /book์—์„œ ์‚ฌ์šฉํ•  uuid์ž…๋‹ˆ๋‹ค.
const { v4: uuid } = require('uuid');
// ํ•ญ๊ณตํŽธ ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
// booking์— ๋”๋ฏธ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ (ํ…Œ์ŠคํŠธ์šฉ)
let booking = [
  {
    "booking_uuid": "1c69bd78-9404-4138-9e01-9b66db9d65ff",
    "flight_uuid": "af6fa55c-da65-47dd-af23-578fdba40bed",
    "name": "ํ•˜๋‚˜",
    "phone": "010-1234-5678",
  }
];

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;
    if (phone) {
      const filteredData = booking.filter (
        (book) => book.phone === phone
      )
      return res.status(200).json(filteredData)
    }
  },

  // [GET] /book/:phone/:flight_uuid ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  // TODO: ์š”์ฒญ ๋œ id, phone๊ณผ ๋™์ผํ•œ uuid, phone ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  findByPhoneAndFlightId: (req,res) => {
    const { phone, flight_uuid } = req.params;

    const filteredData = booking.filter (
      (book) => book.phone === phone && book.flight_uuid === flight_uuid
    )
    return res.status(200).json(filteredData)
    
  },
  
  // [POST] /book ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  // TODO: ์š”์ฒญ ๋œ ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  create: (req, res) => {
    // POST /book์—์„œ ์‚ฌ์šฉํ•  booking_uuid์ž…๋‹ˆ๋‹ค.
    const booking_uuid = uuid();
    
    /*
    // -----------------------------------------
    // 1. ๋‚ด๊ฐ€ ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ
    req.body.booking_uuid = booking_uuid;
    booking.push(req.body);
    return res.status(201).json(booking)
    // -----------------------------------------
    */

    // -----------------------------------------
    // 2. ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ
    const { flight_uuid, name, phone } = req.body;
    
    if (booking.find ((book) => book.phone === phone && book.flight_uuid === flight_uuid)) {
      return res.status(409).json("It's already booked"); // 409 Conflict (์ถฉ๋Œ)
    } else {
      const newBooking = { booking_uuid, flight_uuid, name, phone }
      booking.unshift(newBooking); // ๊ฐ€์žฅ ์•ž์ชฝ์œผ๋กœ ์ •๋ ฌ
      // res.location์€ HTTP ์‘๋‹ต์˜ Location ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋กœ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ๋ฆฌ๋‹ค์ด๋ ‰์…˜์„ ์ง€์‹œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
      res.location(`/book/${booking_uuid}`); 
      return res.status(201).json(booking[0])
    }
    // -----------------------------------------
  },

  // Optional
  // [DELETE] /book/:booking_uuid ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  // TODO: ์š”์ฒญ ๋œ id ๊ฐ’๊ณผ ๋™์ผํ•œ ์˜ˆ์•ฝ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
  deleteByBookingId: (req, res) => {
    const { booking_uuid } = req.params;

    booking = booking.filter((book) => book.booking_uuid !== booking_uuid);
    return res.status(204).json("No Content") 
    // 204 No Content
    // 204 ์ƒํƒœ์ฝ”๋“œ๋Š” ์š”์ฒญ์ด ์„ฑ๊ณตํ–ˆ์œผ๋‚˜, ์ปจํ…์ธ ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. (HTTP Response body๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ)
    // → ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— "No Content"๋Š” ์ถœ๋ ฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
  }
};

 

 ๐Ÿ’ฌ

 ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉฐ ๊ธฐ์กด์— ์•Œ๊ณ ์žˆ๋˜ ๋‚ด์šฉ์„ ํ™•์‹คํžˆํ•˜๊ณ , ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๋” ๋‚˜์€ ์ฝ”๋“œ ์ž‘์„ฑ๋ฒ•(์ ์ ˆํ•œ status๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” ๊ฒƒ)์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ๊ณ ,

 ๋ชฐ๋ž๋˜ ๋ถ€๋ถ„(res.location์ด๋‚˜, ํŠนํžˆ 204๋กœ status๋ฅผ ๋ณด๋‚ด๋ฉด ๋ณธ๋ฌธ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋†€๋ผ์šด ์ ๋„...)๋“ค๋„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ๋‹ค. 


๐ŸŒ™  ์˜ค๋Š˜์˜ ํšŒ๊ณ 

 API ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉฐ StatesAirline๋„ ์„œ๋ฒ„ ๊ตฌํ˜„์„ ํ–ˆ๋‹ค. node.js์™€ express๋ฅผ ์ž ๊น ํ•™์Šตํ•œ ํ›„์— ํ•˜๋Š” ์ž‘์—…์ธ๋ฐ๋‹ค๊ฐ€ ๋ฐ‘๊ทธ๋ฆผ์€ ์ฝ”๋“œ์Šคํ…Œ์ด์ธ  ๊ธฐ์กด ์ฝ”๋“œ์— ์ด๋ฏธ ๋‹ค ์ž‘์„ฑ์ด ๋˜์–ด ์žˆ์–ด์„œ ์ด๋ฒˆ ๊ณผ์ œ๋Š” ๊ทธ๋‚˜๋งˆ ์ข€ ์ˆ˜์›”ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ์ด๋ฒˆ ์œ ๋‹›๋™์•ˆ ํŽ˜์–ด ํ™œ๋™์„ ํ•˜๋ฉฐ ๋‚ด๊ฐ€ ๋ช…ํ™•ํžˆ ์•Œ์ง€ ๋ชปํ•˜๊ณ  ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ๋“ค๊ณผ ์ด๋ก , ๊ฐœ๋…๋“ค์ด ์ •๋ง ๋งŽ๋‹ค๋Š” ๊ฒƒ๋„ ๊นจ๋‹ซ๊ณ , ํŽ˜์–ด์—๊ฒŒ์„œ๋„ ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ๋‹ค. ์ข€ ๋” ํ™•์‹คํ•˜๊ฒŒ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•  ๋ถ€๋ถ„๋“ค์„ ๋‹ค์‹œ ์ƒ๊ฐํ•ด๋ด์•ผ ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

 ์ •๊ทœ ์ˆ˜์—…์ด ๋๋‚˜๊ณ  ์Šคํ„ฐ๋”” ์ฃผ์ œ(? ์ •ํ™•์ด ๋ฌด์—‡์„ ์œ„ํ•œ ๊ฑด์ง€๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ)์˜€๋˜ movie app server ๋งŒ๋“ค๊ธฐ ๊ณผ์ œ๋„ ์™„๋ฃŒํ•˜๊ณ , ๊ฐ•์˜๋ฅผ ๋“ค์—ˆ๋‹ค. ๊ณผ์ œ ์ž์ฒด์—์„œ ๊ตฌํ˜„์„ ์š”๊ตฌํ•œ ์ฝ”๋“œ๋Š” ์ด๋ฏธ ์˜ค๋Š˜ ํ–ˆ๋˜ StatesAirline์— ๋‚˜์™”๋˜ ์ฝ”๋“œ์™€ ๋น„์Šทํ•œ ์œ ํ˜•์ด๋ผ ์–ด๋ ต์ง€๋Š” ์•Š์•˜๋Š”๋ฐ, ๊ทธ ํ›„์— ๊ด€์‹ฌ์‚ฌ๋ถ„๋ฆฌ๋ฅผ ์œ„ํ•ด ํ•˜๋‚˜์˜ ํŒŒ์ผ์— ์ž‘์„ฑ๋˜์–ด ์žˆ๋˜ ์ฝ”๋“œ๋ฅผ routes, controller ํด๋”์— ๊ฐ๊ฐ ๋‚˜๋ˆ„์–ด ๋ถ„๋ฆฌ์‹œ์ผœ์ค€ ๋ถ€๋ถ„์ด ํฅ๋ฏธ๋กœ์› ๋‹ค.   StatesAirline์—์„œ๋Š” ์ œ๊ณต๋ฐ›์€ ์ฝ”๋“œ๊ฐ€ ์ด๋ฏธ ๊ด€์‹ฌ์‚ฌ๋ถ„๋ฆฌ๊ฐ€ ๋˜์–ด์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๊ฐํ•  ๊ธฐํšŒ๊ฐ€ ์—†์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ ๊ณผ์ œ๋กœ ๊ด€์‹ฌ์‚ฌ๋ถ„๋ฆฌ๋ฅผ ์œ„ํ•ด ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์ผ๋ถ€ ์ˆ˜์ •ํ•˜๊ณ , ์˜ฎ๊ธฐ๋Š” ์ž‘์—…์„ ํ•ด ๋ณธ ๊ฒƒ์ด ์žฌ๋ฏธ์žˆ์—ˆ๋‹ค๋Š” ์ƒ๊ฐ์„ ํ–ˆ๋‹ค. ๐Ÿ˜Š 4์ฃผ์ฐจ ์‹ค์Šต๊นŒ์ง€ ๋๋‚˜๋ฉด ๋ธ”๋กœ๊ทธ์—๋„ ์ •๋ฆฌ ํ•œ ๋ฒˆ ํ•ด๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

๋ฐ˜์‘ํ˜•