44μž₯ REST API

REST APIλž€?

  • RESTλŠ” 무엇인가?

    • REpresentational State Transfer의 μ€„μž„λ§.

    • HTTP/1.0 κ³Ό 1.1 μŠ€νŽ™ μž‘μ„± 및 μ•„νŒŒμΉ˜ HTTP μ„œλ²„ ν”„λ‘œμ νŠΈμ˜ 곡동 μ„€λ¦½μž 둜이 ν•„λ”©μ˜ λ…Όλ¬Έμ—μ„œ 처음 μ†Œκ°œλ˜μ—ˆλ‹€.

    • HTTP ν”„λ‘œν† μ½œμ˜ μž₯점을 μ΅œλŒ€ν•œ ν™œμš©ν•  수 μžˆλŠ” μ•„ν‚€ν…μ³λ‘œ μ†Œκ°œλ˜μ–΄ HTTP ν”„λ‘œν† μ½œμ„ μ˜λ„μ— 맞게 λ””μžμΈν•˜λ„λ‘ μœ λ„ν•˜κ³  μžˆλ‹€.

  • μ—„κ²©ν•œ 의미 vs κ°„λ‹¨ν•œ 의미

    • μ—„κ²©ν•œ 의미 λ„€νŠΈμ›Œν¬ 아킀텍쳐 μ›λ¦¬μ˜ λͺ¨μŒ, 즉 μžμ›μ„ μ •μ˜ν•˜κ³  μžμ›μ— λŒ€ν•œ μ£Όμ†Œλ₯Ό μ§€μ •ν•˜λŠ” 방법 μ „λ°˜μ„ μ˜λ―Έν•œλ‹€.

    • κ°„λ‹¨ν•œ 의미 μ›Ή μƒμ˜ 자료λ₯Ό HTTP μœ„μ—μ„œ λ³„λ„μ˜ 전솑계측 없이 μ „μ†‘ν•˜κΈ° μœ„ν•œ μ•„μ£Ό κ°„λ‹¨ν•œ μΈν„°νŽ˜μ΄μŠ€

      • SOAP(Simple Object Access Protocol : HTTP, HTTPS 등을 톡해 XML 기반 메세지λ₯Ό κ΅ν™˜ν•˜λŠ” ν”„λ‘œν† μ½œ) μ΄λ‚˜ μΏ ν‚€λ₯Ό ν†΅ν•œ μ„Έμ…˜ νŠΈλž™ν‚Ή 같은 전솑계측 것듀 없이 톡신

  • RESTFulκ³Ό REST APIλŠ” 무엇인가?

    • REST의 κΈ°λ³Έ 원칙을 μ„±μ‹€νžˆ 지킨 μ„œλΉ„μŠ€ 닀지인을 RESTfulν•˜λ‹€κ³  ν‘œν˜„ν•œλ‹€.

    • RESTλ₯Ό 기반으둜 μ„œλΉ„μŠ€ APIλ₯Ό κ΅¬ν˜„ν•œ 것을 μ˜λ―Έν•œλ‹€.

RESTλŠ” HTTPλ₯Ό 기반으둜 ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ˜ λ¦¬μ†ŒμŠ€μ— μ ‘κ·Όν•˜λŠ” 방식을 κ·œμ •ν•œ 아킀텍쳐고, REST APIλŠ” RESTλ₯Ό 기반으둜 μ„œλΉ„μŠ€ APIλ₯Ό κ΅¬ν˜„ν•œ 것을 μ˜λ―Έν•œλ‹€.

44.1 REST API의 ꡬ성

REST APIλŠ” μžμ›(resource), ν–‰μœ„(verb), ν‘œν˜„(representation)의 세가지 μš”μ†Œλ‘œ κ΅¬μ„±λœλ‹€. RESTλŠ” 자체 ν‘œν˜„ ꡬ쑰둜 κ΅¬μ„±λ˜μ–΄ REST API 만으둜 HTTP μš”μ²­μ˜ λ‚΄μš©μ„ 이해할 수 μžˆλ‹€.

  • νŽ˜μ΄λ‘œλ“œ : 큰 데이터 쀑에 'ν₯λ―ΈμžˆλŠ”' 데이터λ₯Ό κ΅¬λ³„ν•˜λŠ” 데 μ‚¬μš©.

    • ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 주둜 메세지 ν”„λ‘œν† μ½œ 쀑에 ν”„λ‘œν† μ½œ μ˜€λ²„ν—€λ“œμ™€ μ›ν•˜λŠ” 데이터(즉 νŽ˜μ΄λ‘œλ“œ)λ₯Ό ꡬ뢄할 λ•Œ μ‚¬μš©ν•œλ‹€

    • μ˜ˆμ‹œ - JSON ν˜•μ‹μ˜ μ›Ή μ„œλΉ„μŠ€ 응닡

    {
      "status": "OK",
      "data": {
        "message": "Hello, world!"
      }
    }

    μ—¬κΈ°μ—μ„œ "Hello, world!"κ°€ ν΄λΌμ΄μ–ΈνŠΈκ°€ 관심을 κ°€μ§€λŠ” νŽ˜μ΄λ‘œλ“œμ΄κ³ , λ‚˜λ¨Έμ§€ 뢀뢄은 μ€‘μš”ν•˜κΈ΄ ν•˜μ§€λ§Œ ν”„λ‘œν† μ½œ μ˜€λ²„ν—€λ“œμ΄λ‹€. 좜처 : [wiki]https://ko.wikipedia.org/wiki/%ED%8E%98%EC%9D%B4%EB%A1%9C%EB%93%9C_(%EC%BB%B4%ED%93%A8%ED%8C%85)

44.2 REST API 섀계 원칙

REST μ—μ„œ κ°€μž₯ μ€‘μš”ν•œ 기본직인 원칙은 두가지이닀!!

1. URIλŠ” λ¦¬μ†ŒμŠ€λ₯Ό ν‘œν˜„ν•˜λŠ”λ° μ§‘μ€‘ν•œλ‹€.
2. HTTP μš”μ²­ λ©”μ„œλ“œλ₯Ό 톡해 ν–‰μœ„λ₯Ό μ •μ˜ν•œλ‹€.
  1. URIλŠ” λ¦¬μ†ŒμŠ€λ₯Ό ν‘œν˜„ν•΄μ•Ό ν•œλ‹€.

#bad
GET /getTodos/1    - (1)번 μ˜ˆμ‹œ
GET /todos/show/1  - (2)번 μ˜ˆμ‹œ

# good
GET /todos/1

URIλŠ” λ¦¬μ†ŒμŠ€λ₯Ό ν‘œν˜„ν•˜λŠ”λ°μ— 쀑점을 두어야 ν•œλ‹€.

  • (1)번 μ˜ˆμ‹œ -> μ˜ˆμ‹œμ²˜λŸΌ get 같은 ν–‰μœ„μ— λŒ€ν•œ ν‘œν˜„μ΄ λ“€μ–΄κ°€μ„œλŠ” μ•ˆλœλ‹€

  • (2)번 μ˜ˆμ‹œ -> λ¦¬μ†ŒμŠ€λ₯Ό 식별할 수 μžˆλŠ” 이름은 λ™μ‚¬λ³΄λ‹€λŠ” λͺ…사λ₯Ό μ‚¬μš©ν•œλ‹€.

  1. λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ ν–‰μœ„λŠ” HTTP μš”μ²­ λ©”μ„œλ“œλ‘œ ν‘œν˜„ν•œλ‹€.

HTTP μš”μ²­ λ©”μ„œλ“œλŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλ²„μ—κ²Œ μš”μ²­μ˜ μ’…λ₯˜μ™€ λͺ©μ (λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ ν–‰μœ„)을 μ•Œλ¦¬λŠ” 방법이닀.

  # bad
  GET /todos/delete/1 - (1)번 μ˜ˆμ‹œ

  # good
  DELETE /todos/1

λ¦¬μ†ŒμŠ€μ— λŒ€ν•œ ν–‰μœ„λŠ” HTTP μš”μ²­ λ©”μ„œλ“œλ₯Ό 톡해 ν‘œν˜„ν•˜λ©°, URI에 ν‘œν˜„ν•˜μ§€ μ•ŠλŠ”λ‹€.

  • (1)번 μ˜ˆμ‹œ - GET은 λ¦¬μ†ŒμŠ€λ₯Ό 취득할 λ•Œ μ‚¬μš©ν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€. μ‚­μ œ μš”μ²­μ„ ν•˜λ €λ©΄ DELETE λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€.

44.3 REST API μ‹€μŠ΅

44.3.1 ~ 44.3.3 JSON Serverν™˜κ²½ μ„ΈνŒ… (μƒλž΅)

  • λ‹€μŒ JSON 파일 데이터λ₯Ό κΈ°μ€€μœΌλ‘œ todo에 λŒ€ν•œ 데이터λ₯Ό CRUD ν•  수 μžˆλŠ” μ„œλ²„λΌκ³  μƒκ°ν•΄λ³΄μž.

{
  "todos": [
    {
      "id": 1,
      "content": "JS",
      "completed": true
    },
    {
      "id": 2,
      "content": "React",
      "completed": false
    },
    {
      "id": 1,
      "content": "FrontEnd",
      "completed": false
    }
  ]
}

44.3.4 GET μš”μ²­

  • λͺ¨λ“  todos λ¦¬μ†ŒμŠ€λ₯Ό 취득 ν•  λ•Œ

    const xhr = new XMLHttpRequest();
    
    /**G
     * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ GET μš”μ²­μœΌλ‘œ μ·¨λ“ν•˜κ² λ‹€
     * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, todos λ¦¬μ†ŒμŠ€λ₯Ό μ·¨λ“ν•˜κ² λ‹€(index)
     */
    xhr.open("GET", "/todos");
    
    xhr.send();
    
    xhr.onload = () => {
      if (xhr.status === 200) {
        console.log(xhr.response);
      } else {
        console.error("Error", xhr.status, xhr.statusText);
      }
    };
    • response

      {
        "todos": [
          {
            "id": 1,
            "content": "JS",
            "completed": true
          },
          {
            "id": 2,
            "content": "React",
            "completed": false
          },
          {
            "id": 3,
            "content": "FrontEnd",
            "completed": false
          }
        ]
      }
  • νŠΉμ • todos λ¦¬μ†ŒμŠ€λ₯Ό 취득

    const xhr = new XMLHttpRequest();
    
    /**
     * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ GET μš”μ²­μœΌλ‘œ μ·¨λ“ν•˜κ² λ‹€
     * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, todos/1 둜 id:1인 todo λ¦¬μ†ŒμŠ€λ₯Ό μ·¨λ“ν•˜κ² λ‹€
     *
     * todos λ¦¬μ†ŒμŠ€μ—μ„œ νŠΉμ • todoλ₯Ό 취득(retrieve)
     */
    xhr.open("GET", "/todos/1");
    
    xhr.send();
    
    xhr.onload = () => {
      if (xhr.status === 200) {
        console.log(xhr.response);
      } else {
        console.error("Error", xhr.status, xhr.statusText);
      }
    };
    • response

      {
        "todos": [
          {
            "id": 1,
            "content": "JS",
            "completed": true
          }
        ]
      }

44.3.5 POST μš”μ²­

const xhr = new XMLHttpRequest();

/**
 * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ POST μš”μ²­μœΌλ‘œ μƒμ„±ν•˜κ² λ‹€
 * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, μƒˆλ‘œμš΄ todoλ₯Ό μƒμ„±ν•˜κ² λ‹€
 */
xhr.open("POST", "/todos");

// μš”μ²­ λͺΈμ²΄μ— λ‹΄μ•„ μ„œλ²„λ‘œ 보낼 νŽ˜μ΄λ‘œλ“œμ˜ MIME νƒ€μž… 지정
xhr.setRequestHeader("Content-Type", "application/json");

// νŽ˜μ΄λ‘œλ“œλ₯Ό λ‹΄μ•„μ„œ μ„œλ²„λ‘œ 전솑
xhr.send(JSON.stringify({ id: "4", content: "Angular", completed: false }));

xhr.onload = () => {
  if (xhr.status === 200) {
    console.log(xhr.response);
  } else {
    console.error("Error", xhr.status, xhr.statusText);
  }
};
  • response

    {
      "todos": [
        {
          "id": 1,
          "content": "JS",
          "completed": true
        },
        {
          "id": 2,
          "content": "React",
          "completed": false
        },
        {
          "id": 3,
          "content": "FrontEnd",
          "completed": false
        },
        {
          "id": 4,
          "content": "Angular",
          "completed": false
        }
      ]
    }

44.3.6 PUT μš”μ²­

const xhr = new XMLHttpRequest();

/**
 * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ PUT μš”μ²­μœΌλ‘œ λ¦¬μ†ŒμŠ€μ „μ²΄λ₯Ό κ΅μ²΄ν•˜κ² λ‹€
 * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, id둜 todoλ₯Ό νŠΉμ •ν•˜μ—¬ idλ₯Ό μ œμ™Έν•œ λ¦¬μ†ŒμŠ€ 전체λ₯Ό ꡐ체
 */
xhr.open("PUT", "/todos/4");

// μš”μ²­ λͺΈμ²΄μ— λ‹΄μ•„ μ„œλ²„λ‘œ 보낼 νŽ˜μ΄λ‘œλ“œμ˜ MIME νƒ€μž… 지정
xhr.setRequestHeader("Content-Type", "application/json");

// νŽ˜μ΄λ‘œλ“œλ₯Ό λ‹΄μ•„μ„œ μ„œλ²„λ‘œ 전솑
xhr.send(JSON.stringify({ id: "4", content: "Svelte", completed: false }));

xhr.onload = () => {
  if (xhr.status === 200) {
    console.log(xhr.response);
  } else {
    console.error("Error", xhr.status, xhr.statusText);
  }
};
  • response

    {
      "todos": [
        {
          "id": 4,
          "content": "Svelte",
          "completed": false
        }
      ]
    }

44.3.7 PATCH μš”μ²­

const xhr = new XMLHttpRequest();

/**
 * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ, PATCH μš”μ²­μœΌλ‘œ λ¦¬μ†ŒμŠ€μ „μ²΄λ₯Ό κ΅μ²΄ν•˜κ² λ‹€
 * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, id둜 todoλ₯Ό νŠΉμ •ν•˜μ—¬ μ›ν•˜λŠ” λΆ€λΆ„λ§Œ ꡐ체
 */
xhr.open("PATCH", "/todos/2");

// μš”μ²­ λͺΈμ²΄μ— λ‹΄μ•„ μ„œλ²„λ‘œ 보낼 νŽ˜μ΄λ‘œλ“œμ˜ MIME νƒ€μž… 지정
xhr.setRequestHeader("Content-Type", "application/json");

// νŽ˜μ΄λ‘œλ“œλ₯Ό λ‹΄μ•„μ„œ μ„œλ²„λ‘œ 전솑
xhr.send(JSON.stringify({ completed: true }));

xhr.onload = () => {
  if (xhr.status === 200) {
    console.log(xhr.response);
  } else {
    console.error("Error", xhr.status, xhr.statusText);
  }
};
  • response

    {
      "todos": [
        {
          "id": 2,
          "content": "React",
          "completed": true
        }
      ]
    }

44.3.8 DELETE μš”μ²­

/**
 * 1. ν–‰μœ„λŠ” λ©”μ„œλ“œλ‘œ DELETE μš”μ²­μœΌλ‘œ λ¦¬μ†ŒμŠ€λ₯Ό μ‚­μ œν•˜κ² λ‹€
 * 2. λ¦¬μ†ŒμŠ€λŠ” URI둜 ν‘œν˜„, id둜 todoλ₯Ό νŠΉμ •ν•˜μ—¬ ν•΄λ‹Ή todo μ‚­μ œ
 */
xhr.open("PATCH", "/todos/4");

// νŽ˜μ΄λ‘œλ“œλ₯Ό λ‹΄μ•„μ„œ μ„œλ²„λ‘œ 전솑
xhr.send(JSON.stringify({ completed: true }));

xhr.onload = () => {
  if (xhr.status === 200) {
    console.log(xhr.response);
  } else {
    console.error("Error", xhr.status, xhr.statusText);
  }
};
  • response

    {}

Last updated