사전 통일된 인터페이스
개념

통일된 인터페이스

gabury1고친 사람 github-actions[bot]

통일된 인터페이스는 어느 서버의 어떤 데이터든 같은 방식으로 다루게 해 줍니다. 사용자를 읽든 주문을 읽든 쓰는 동사가 같습니다. 그래서 처음 보는 서비스도 따로 배우지 않고 부를 수 있습니다. 웹 서비스를 지을 때 지키는 설계 제약 가운데 가장 중심이 되는 것입니다.

쉽고 빠른 이해

통일된 인터페이스는 모든 서비스가 같은 몇 개의 동사로 말하게 하는 약속입니다. 웹에서는 무엇을 가져오든 같은 「읽기」 요청을 쓰고, 무엇을 지우든 같은 「지우기」 요청을 씁니다.

이게 없으면 서비스마다 자기만의 함수 이름을 공개합니다. 부르는 쪽은 서비스마다 사용법을 새로 익혀야 합니다. 중간에서 응답을 저장해 주는 캐시도 어느 요청이 읽기인지 몰라 손을 못 댑니다.

통일하려면 아래 네 가지를 함께 지킵니다. 차례가 있는 절차가 아니라 넷 다 갖춰야 하는 조건입니다.

  • 다룰 대상마다 주소를 붙입니다
  • 대상을 정해진 형식의 문서로 주고받습니다
  • 메시지가 자기 형식과 결과를 스스로 밝힙니다
  • 응답에 다음에 할 수 있는 일을 링크로 실어 보냅니다

대가는 효율입니다. 누구나 아는 형식으로만 말하므로 한 애플리케이션에 꼭 맞춘 방식보다 요청이 늘어나기 쉽습니다. 그래도 누가 부를지 모르는 공개 서비스에는 잘 맞습니다. 한 팀이 양쪽을 다 만드는 내부 통신에서는 내려놓기도 합니다.

상세

어느 집에 가든 벽의 콘센트 모양은 같습니다. 그래서 처음 가 본 집에서도 헤어드라이어를 그냥 꽂으면 됩니다. 집마다 콘센트 모양이 다르면 집마다 맞는 어댑터를 따로 챙겨야 합니다.

통일된 인터페이스는 소프트웨어끼리 이 콘센트를 맞추는 일입니다. 시스템 안의 모든 컴포넌트가 같은 규칙으로 말하게 하는 설계 제약입니다. 컴포넌트는 클라이언트와 서버, 그 사이에 끼는 캐시처럼 메시지를 주고받는 프로그램 하나하나를 가리킵니다.

이 이름의 「인터페이스」는 자바의 interface 키워드가 아닙니다. 컴포넌트끼리 요청과 응답을 주고받는 규칙 전체를 가리킵니다. 어떤 동사로 부르는지, 대상을 어떻게 가리키는지, 결과를 어떻게 알리는지가 모두 들어갑니다.

REST 의 여섯 제약 가운데 하나

이 제약은 REST(Representational State Transfer, 표현 상태 전이)에서 나온 말입니다. REST 는 웹처럼 큰 분산 시스템을 지을 때 받아들이는 제약의 묶음입니다. API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)를 웹으로 공개할 때 흔히 이 묶음을 기준으로 삼습니다.

REST 의 제약은 여섯입니다. 하나하나가 시스템이 지킬 규칙 하나를 정합니다.

제약 정하는 것
클라이언트-서버 화면을 맡는 쪽과 데이터를 맡는 쪽을 나눕니다
무상태 서버가 앞 요청을 기억하지 않습니다. 요청마다 필요한 정보를 다 싣습니다
캐시 가능성 응답마다 저장해 두고 다시 써도 되는지를 밝힙니다
통일된 인터페이스 모든 컴포넌트가 같은 규칙으로 말합니다
계층 시스템 클라이언트는 바로 앞의 상대만 압니다. 그 뒤에 무엇이 몇 겹 있는지 모릅니다
코드 온 디맨드 서버가 클라이언트에서 실행할 코드를 내려보낼 수 있습니다. 여섯 가운데 이것만 선택입니다

이런 제약의 묶음에 이름을 붙인 것을 아키텍처 양식이라고 부릅니다. 표의 나머지 다섯은 다른 아키텍처 양식에도 흔히 나옵니다. 모든 컴포넌트가 같은 규칙으로 말하게 하는 것은 REST 의 고유한 특징입니다. 그래서 통일된 인터페이스가 REST 를 다른 양식과 가르는 기준이 됩니다.

서비스마다 동사를 새로 짓는 방식

통일된 인터페이스가 무엇을 바꾸는지는 반대편과 견주면 잘 보입니다. 반대편은 서비스마다 자기 함수를 공개하는 방식입니다. 대표적인 것이 RPC(Remote Procedure Call, 원격 프로시저 호출)입니다. 원격 서버의 함수를 내 코드의 함수처럼 이름으로 부르는 방식입니다.

RPC 식으로 지으면 서비스마다 동사가 늘어납니다. 사용자 서비스는 getUser 를 공개하고, 주문 서비스는 createOrder 와 cancelOrder 를 공개합니다. 서비스가 열 개면 부르는 쪽이 익힐 함수 목록도 열 벌입니다.

통일된 인터페이스는 동사를 몇 개로 고정합니다. 늘어나는 것은 동사가 아니라 다룰 대상인 자원입니다. 자원은 사용자 한 명이나 주문 하나처럼 주소를 붙여 가리키는 대상입니다.

웹에서는 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)의 요청 메서드가 그 고정된 동사입니다. GET 은 읽기, POST 는 새로 만들기, PUT 은 통째로 바꾸기, DELETE 는 지우기를 뜻합니다. 같은 일을 두 방식으로 적어 견주면 이렇습니다.

할 일 서비스마다 함수를 짓는 방식 통일된 인터페이스
사용자 42 읽기 getUser(42) GET /users/42
주문 만들기 createOrder(...) POST /orders
주문 7 고치기 updateOrder(7, ...) PUT /orders/7
주문 7 지우기 deleteOrder(7) DELETE /orders/7

왼쪽 열의 함수 이름은 그 서비스를 만든 팀이 지었습니다. 다른 팀이라면 fetchUser 라고 지었을 수도 있습니다. 오른쪽 열의 메서드 네 개는 HTTP 를 다루는 프로그램이면 누구나 뜻을 압니다.

통일된 인터페이스를 이루는 네 하위 제약

「같은 규칙으로 말한다」만으로는 무엇을 지켜야 하는지가 흐립니다. 그래서 REST 는 이 제약을 네 갈래로 나눠 정합니다. 넷을 다 지켜야 통일된 인터페이스라고 부릅니다.

  • 자원 식별 — 다룰 대상마다 고유한 주소를 붙입니다. 웹에서는 URI(Uniform Resource Identifier, 통일 자원 식별자)가 그 주소입니다
  • 표현을 통한 자원 조작 — 자원 자체가 아니라 자원의 지금 모습을 적은 문서를 주고받습니다. 이 문서를 표현이라고 부릅니다. 클라이언트는 표현을 고쳐 보내서 자원을 바꿉니다
  • 자기 서술적 메시지 — 받는 쪽이 메시지 하나만 읽고 그것을 어떻게 다룰지 알게 합니다. 메서드와 헤더와 상태 코드가 그 설명을 맡습니다. 헤더는 본문 앞에 붙는 「이름: 값」 꼴의 줄입니다. 상태 코드는 요청이 어떻게 끝났는지 알리는 세 자리 번호입니다
  • HATEOAS(Hypermedia As The Engine Of Application State) — 응답에 다음에 할 수 있는 일을 링크로 실어 보냅니다. 클라이언트는 주소를 외우지 않고 그 링크를 따라갑니다. 이름의 하이퍼미디어는 이렇게 링크를 품은 문서를 가리킵니다

넷이 메시지 어디에 있는지는 주문 하나를 읽는 요청과 응답에서 보입니다. 먼저 요청입니다.

http
GET /orders/7 HTTP/1.1
Host: api.example.com
Accept: application/json

첫 줄의 /orders/7 이 주문 7이라는 자원을 가리키는 주소입니다. 자원 식별이 바로 이 주소입니다. 앞의 GET 은 이 자원을 읽어 달라는 뜻입니다. Accept 헤더는 받고 싶은 본문 형식을 밝힙니다.

이번에는 서버의 응답입니다.

http
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 7,
  "status": "paid",
  "links": {
    "payment": "/orders/7/payment"
  }
}

중괄호로 감싼 본문이 주문 7의 표현입니다. JSON(JavaScript Object Notation, 자바스크립트 객체 표기법) 형식으로 주문의 지금 상태를 적었습니다.

첫 줄의 200 은 요청이 성공했다는 상태 코드입니다. Content-Type 헤더는 본문이 JSON 이라고 알립니다. 이 둘 덕분에 메시지가 자기를 어떻게 읽을지 스스로 설명합니다. 자기 서술적 메시지가 이런 모습입니다.

마지막의 links 는 이 주문의 결제 정보로 가는 주소를 알려 줍니다. 클라이언트는 결제 정보의 주소를 미리 몰라도 이 링크를 따라가면 됩니다. 이것이 HATEOAS 가 말하는 링크입니다.

같은 규칙이 주는 단순함

통일해서 얻는 첫째는 단순함입니다. 클라이언트가 익힐 규칙이 서비스 수만큼 늘지 않습니다. HTTP 를 다룰 줄 아는 클라이언트는 처음 보는 API 도 주소만 알면 GET 으로 읽어 볼 수 있습니다.

서버 쪽도 덕을 봅니다. 클라이언트는 인터페이스에만 기대고 서버 안의 구현은 모릅니다. 서버는 언어를 바꾸거나 데이터베이스를 갈아 끼울 수 있습니다. 같은 주소에서 같은 형식으로 답하기만 하면 클라이언트는 고칠 것이 없습니다. 인터페이스와 구현이 떨어져 있어 양쪽을 따로 고칠 수 있습니다.

중간 프로그램이 메시지를 알아보는 가시성

둘째 이득은 가시성입니다. 가시성은 오가는 메시지를 제3의 프로그램이 알아볼 수 있는 정도를 말합니다. 메서드와 상태 코드의 뜻이 정해져 있으므로 애플리케이션을 모르는 프로그램도 메시지만 보고 무슨 일인지 압니다.

HTTP 캐시가 그런 프로그램입니다. 응답을 저장해 두었다가 같은 요청이 오면 서버 대신 답합니다. 캐시가 요청을 받아 무엇을 보고 가르는지 그리면 이렇습니다.

flowchart TD
    C["클라이언트"] --> K["캐시 · 서비스 내용은 모른다"]
    K --> M{"메서드가 GET 인가"}
    M -->|예| S["저장해 둔 응답으로 답할 수 있다"]
    M -->|아니오| T["그대로 넘긴다"]
    T --> U["사용자 서비스"]
    T --> O["주문 서비스"]
    T --> P["결제 서비스"]

캐시는 사용자가 무엇이고 주문이 무엇인지 모릅니다. 그래도 메서드가 GET 이면 데이터를 바꾸지 않는 읽기라는 것을 압니다. 저장해 두고 다시 써도 되는지는 응답 헤더가 알려 줍니다. 그래서 세 서비스 앞에 캐시 하나만 두면 됩니다.

프록시와 모니터링 도구도 같은 원리로 일합니다. 프록시는 요청을 대신 받아 서버로 넘기는 프로그램입니다. 서버의 답이 끊겼을 때 요청이 GET 이면 데이터를 바꾸지 않으므로 다시 보내도 된다는 것을 압니다. 모니터링 도구는 서버 오류를 뜻하는 500번대 상태 코드만 세면 서비스 종류와 상관없이 오류율을 얻습니다.

통일이 치르는 대가

대가는 효율입니다. 모든 애플리케이션이 알아듣는 형식은 어느 한 애플리케이션에도 꼭 맞지 않습니다. 그 차이만큼 요청이 늘거나 메시지가 길어집니다.

흔히 드러나는 것이 요청 수입니다. 주문 화면 하나에 주문과 사용자와 결제 정보가 다 필요하다고 해 봅시다. 자원마다 주소가 따로라서 클라이언트는 요청을 세 번 보냅니다. 화면에 필요한 것을 함수 하나가 한 번에 돌려주는 방식보다 왕복이 많습니다.

동작이 고정된 동사에 안 맞을 때도 있습니다. 「주문 취소」는 읽기도 아니고 지우기도 아닙니다. 길은 둘입니다. 주문의 상태 값을 「취소됨」으로 바꾸는 PUT 으로 옮깁니다. 아니면 취소 요청을 새 자원으로 만들어 POST 합니다. 동사를 늘리지 않으려고 명사를 늘리는 셈입니다.

REST 는 이 대가를 알고 고른 설계입니다. 웹처럼 규모가 큰 환경에 맞춰져 있습니다. 그런 환경에는 서로 모르는 클라이언트와 중간 프로그램이 많습니다.

통일을 내려놓는 선택

서비스끼리 내부에서 주고받을 때는 저울이 달리 기웁니다. 보내는 쪽과 받는 쪽을 한 조직이 함께 만들면 함수 이름을 맞추는 비용이 작습니다. 중간에 끼는 캐시가 없을 때도 많습니다.

gRPC 는 이쪽을 고른 예입니다. 서비스마다 메서드를 스키마 파일에 정의합니다. 부르는 쪽은 그 파일에서 만든 코드로 원격 메서드를 함수처럼 부릅니다. 대신 HTTP 캐시는 그 메시지가 읽기인지 쓰기인지 알아보지 못합니다.

GraphQL 은 다른 방향으로 비켜 갑니다. 대개 한 주소에 질의문을 POST 로 보내 필요한 필드만 골라 받습니다. 화면 하나에 요청 하나로 끝나므로 요청 수 문제는 줄어듭니다. 대신 HTTP 메서드만 보고는 읽기와 쓰기를 가를 수 없습니다. 그 때문에 HTTP 캐시가 응답을 다루기 어렵습니다.

누가 부를지 모르는 공개 API 는 내부 통신과 사정이 다릅니다. 부르는 쪽과 미리 함수 이름을 맞출 길이 없고, 중간에 캐시가 낄 때도 많습니다. 낯선 클라이언트와 캐시가 따로 묻지 않고 메시지를 다룰 수 있어야 합니다. 그래서 공개 API 에서는 통일된 인터페이스를 지키는 편이 낫습니다.

관련 항목

통일된 인터페이스가 속하는 상위 개념

REST · 아키텍처 양식 · API 설계 · 분산 시스템 · 웹 아키텍처

REST 가 함께 거는 다른 제약

클라이언트-서버 · 무상태 · 캐시 가능성 · 계층 시스템 · 코드 온 디맨드

통일된 인터페이스를 이루는 하위 제약

자원 식별 · 표현을 통한 자원 조작 · 자기 서술적 메시지 · HATEOAS · 하이퍼미디어

HTTP 에서 이 제약을 맡는 요소

HTTP · URI · 자원 · 표현 · 요청 메서드 · GET · POST · PUT · DELETE · PATCH · 상태 코드 · 헤더 · 미디어 타입 · Content-Type · Cache-Control · 콘텐츠 협상

같은 규칙 덕에 메시지를 알아보는 중개자

HTTP 캐시 · 프록시 · 리버스 프록시 · CDN · API 게이트웨이 · 로드 밸런서

통일된 인터페이스가 받쳐 주는 성질

가시성 · 안전한 메서드 · 멱등성 · 느슨한 결합 · 독립적 진화 · 하위 호환

서비스마다 동작을 정의하는 통신 방식

RPC · gRPC · GraphQL · SOAP · 프로토콜 버퍼 · JSON-RPC

이 제약을 기준으로 API 를 짓는 방법

자원 지향 설계 · 리처드슨 성숙도 모델 · OpenAPI · RESTful API

다른 이름: uniform interface · 균일 인터페이스 · 일관된 인터페이스 · 유니폼 인터페이스 · 단일 인터페이스