Feathers: REST ํด๋ผ์ด์–ธํŠธ: GET ์š”์ฒญ์ด ๋งŽ์€ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ๋˜๋Š” ๋ณธ๋ฌธ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆ˜๋ฝ

์— ๋งŒ๋“  2019๋…„ 05์›” 27์ผ  ยท  3์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: feathersjs/feathers

์•ˆ๋…•,

GET ์š”์ฒญ์˜ ๊ธด URL์— ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ "์ฐพ๊ธฐ" ์ฟผ๋ฆฌ์—์„œ ๊ธด ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก์„ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์†”๋ฃจ์…˜์€ ์ƒˆ๋กœ์šด ๋ฏธ๋“ค์›จ์–ด์™€ ๋ฉ”์„œ๋“œ ์žฌ์ •์˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

// allow x-http-method-override header for GET methods
exports.default = function (req, res, next) {
    if (req.headers['x-http-method-override'] === 'GET' && req.method === 'POST') {
        req.method = 'GET';
        req.query = req.body;
        delete req.body;
    }
    next();
};

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚ด ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

await awesomeService.create(
          {
             someParam: aVeryLongArray // will replace req.query server side thanks to the middleware, and thus bypass URL limitations of browsers
          },
          {
            headers: {
              "x-http-method-override": "GET" // tell the server that this is actually a GET request, so it triggers our middleware
            }
          }
        );

Slack์— ๋Œ€ํ•œ ๋„์›€์„ ์ฃผ์‹  Sebastian์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค(Github ํ•ธ๋“ค์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค).

์ด SO ๋‹ต๋ณ€์— ๋”ฐ๋ฅด๋ฉด POST HTTP ๋‹จ์–ด๋Š” ์ƒ์„ฑ ์ž์ฒด์— ๊ตญํ•œ๋˜์ง€ ์•Š๊ณ  ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์—†๋Š” ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์ž‘์—…๊ณผ๋„ ๊ด€๋ จ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ํšจํ•œ ํŒจํ„ด ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ "๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์€ ์ฐพ๊ธฐ"๋Š” POST์˜ ๊ด€๋ จ ์‚ฌ์šฉ ์‚ฌ๋ก€๋กœ ๊ฐ„์ฃผ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ... ๋ฌธ์ œ๋Š” Feathers์—์„œ POST ์š”์ฒญ์ด "์ƒ์„ฑ" ์ž‘์—…๊ณผ ๊ฐ•ํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. REST ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ด๋Š” create ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฉฐ, ์ด๋Š” ๋ฐฉํ•ด๊ฐ€ ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ๊ณ  ์„œ๋ฒ„ ์ธก์—์„œ๋Š” create ํ›„ํฌ์— ๊ด€๋ จ ๊ถŒํ•œ์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉํ•ด.

GET ์š”์ฒญ์—์„œ body ๋˜๋Š” data ํ•„๋“œ๋ฅผ ์ง€์›ํ•˜๋„๋ก ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ get ๋ฐ find ๋ฉ”์„œ๋“œ). ๋‚˜๋Š” ๊ตฌํ˜„์ด ์‚ฌ์†Œํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ (PR์— ๊ธฐ๋ปํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค), ๊ทธ๋Ÿฌ๋‚˜ ์ €๋Š” ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

  • ์ข‹์€ ์ƒ๊ฐ์ด๋ผ๋ฉด(๋Œ€๋ถ€๋ถ„ ๋ธŒ๋ผ์šฐ์ €์™€ ๊ด€๋ จํ•˜์—ฌ ์š”์ฒญ ์š”์ฒญ์—์„œ data ๋˜๋Š” body ํ•„๋“œ๋ฅผ ์Šคํฌ๋žฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?)
  • ๊ฐ€์žฅ ์ข‹์€ ํŒจํ„ด์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ( body ๋งค๊ฐœ๋ณ€์ˆ˜, data ๋งค๊ฐœ๋ณ€์ˆ˜?)

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

REST ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ข€ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์‚ฌ์šฉ์ž ์ •์˜๋œ ํด๋ผ์ด์–ธํŠธ ์„œ๋น„์Šค๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ โ€‹โ€‹์ธ์Šคํ„ด์Šคํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. JSON์„ ์‚ฌ์šฉํ•˜์—ฌ POST ์ฟผ๋ฆฌ๋ฅผ ํ—ˆ์šฉํ•˜๋ฉด ๋ชจ๋“  URL ๋ฌธ์ž์—ด ๋ณ€ํ™˜ ๋ฐ ๋ฐฐ์—ด ๊ตฌ๋ฌธ ๋ถ„์„ ์ œํ•œ๋„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ํด๋ผ์ด์–ธํŠธ์—์„œ REST ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค(์ด๊ฒƒ์ด ๋‹ค๋ฅธ ๋์ ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค).

const FetchService = require('@feathersjs/rest-client/lib/fetch');

class MyFetchService extends FetchService {
  get (id, params = {}) {
    if (typeof id === 'undefined') {
      return Promise.reject(new Error(`id for 'get' can not be undefined`));
    }

    return this.request({
      url: this.makeUrl({}, id),
      method: 'POST',
      data: params.query,
      headers: Object.assign({
        'x-http-method-override': 'GET'
      }, params.headers)
    }, params).catch(toError);
  }
}

app.use('/someservice', new MyFetchService({
  connection: window.fetch,
  name: 'someservice',
  base: 'mybaseUrl',
  options: {}
}));

ํ•ต์‹ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const feathers = require('@feathersjs/feathers');
const rest = require('@feathersjs/rest-client');

const app = feathers();

// Connect to the same as the browser URL (only in the browser)
app.configure(rest({
  base: '/',
  Service: MyFetchService
}));

๋ชจ๋“  3 ๋Œ“๊ธ€

REST ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ข€ ๋” ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ์‚ฌ์šฉ์ž ์ •์˜๋œ ํด๋ผ์ด์–ธํŠธ ์„œ๋น„์Šค๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ โ€‹โ€‹์ธ์Šคํ„ด์Šคํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. JSON์„ ์‚ฌ์šฉํ•˜์—ฌ POST ์ฟผ๋ฆฌ๋ฅผ ํ—ˆ์šฉํ•˜๋ฉด ๋ชจ๋“  URL ๋ฌธ์ž์—ด ๋ณ€ํ™˜ ๋ฐ ๋ฐฐ์—ด ๊ตฌ๋ฌธ ๋ถ„์„ ์ œํ•œ๋„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ํด๋ผ์ด์–ธํŠธ์—์„œ REST ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค(์ด๊ฒƒ์ด ๋‹ค๋ฅธ ๋์ ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค).

const FetchService = require('@feathersjs/rest-client/lib/fetch');

class MyFetchService extends FetchService {
  get (id, params = {}) {
    if (typeof id === 'undefined') {
      return Promise.reject(new Error(`id for 'get' can not be undefined`));
    }

    return this.request({
      url: this.makeUrl({}, id),
      method: 'POST',
      data: params.query,
      headers: Object.assign({
        'x-http-method-override': 'GET'
      }, params.headers)
    }, params).catch(toError);
  }
}

app.use('/someservice', new MyFetchService({
  connection: window.fetch,
  name: 'someservice',
  base: 'mybaseUrl',
  options: {}
}));

ํ•ต์‹ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const feathers = require('@feathersjs/feathers');
const rest = require('@feathersjs/rest-client');

const app = feathers();

// Connect to the same as the browser URL (only in the browser)
app.configure(rest({
  base: '/',
  Service: MyFetchService
}));

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋„ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐพ๊ธฐ ์š”์ฒญ์ด ์—ฌ๋Ÿฌ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ณ  URL ์ œํ•œ์— ๋„๋‹ฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ POST๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  "๋งŒ๋“ค๊ธฐ" ๋Œ€์‹  "์ฐพ๊ธฐ"๋กœ ๋ผ์šฐํŒ…ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ณ ์‹ฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

HTTP ์š”์ฒญ ๋ฉ”์„œ๋“œ ์œ ํ˜•์„ POST์—์„œ GET์œผ๋กœ ์žฌ์ •์˜ํ•˜์—ฌ @eric-burel์ด ์ œ์•ˆํ•œ ๊ฒƒ์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ "์ฐพ๊ธฐ"๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ๋Œ€์‹  ์„œ๋น„์Šค์—์„œ "๋งŒ๋“ค๊ธฐ" ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ ๊ฐœ์ฒด ๋ฉ”์„œ๋“œ๊ฐ€ ์‹ค์ œ๋กœ "GET"์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์ง€๋งŒ ์ด ๋ณ€๊ฒฝ ์ „์— ํŽ˜๋” ๋ผ์šฐํŒ…์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์„๊นŒ์š”?

app.use('/์•ฝ์†', function(req, res, next) {
if (req.method === 'POST') { // ์ด ์„œ๋น„์Šค์— ๋Œ€ํ•ด์„œ๋งŒ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
req.method = '๊ฐ€์ ธ์˜ค๊ธฐ';
req.query = req.body;
req.body ์‚ญ์ œ;
}
๋‹ค์Œ();
}, {
์ฐพ๊ธฐ: ํ•จ์ˆ˜(๋งค๊ฐœ๋ณ€์ˆ˜, ์ฝœ๋ฐฑ) {
// ๋ญ”๊ฐ€๋ฅผ ํ•˜๋‹ค
}
});

์—ฌ๊ธฐ์—์„œ ๋‚ด๊ฐ€ ๋†“์น˜๊ณ  ์žˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๋Š”์ง€ ์„ค๋ช…ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋˜๋Š” ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์ด ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

์ด๊ฒƒ์€ v5์— ๋‚˜์˜ค๋Š” ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž ์ •์˜ ๋ฐฉ๋ฒ•(https://github.com/feathersjs/feathers/issues/1976)์—์„œ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰