직관적인 표현으로 디자인해야 합니다.

명확한 이름을 짓는 것은 직관적인 표현으로 디자인하는 첫걸음입니다.
POST /postBookSt 이 API가 무슨 역할을 하는지 유추가 가능하신가요?
이 API는 사용자가 좌석을 예약할 때 사용하는 **API입니다.
사용자가 API 이름만 보고도 어떤 역할을 하는지 알 수 있어야 합니다.
그렇다면, POST /postBookSt 이름을 어떻게 바꿀 수 있을까요?
API를 직관적인 표현으로 디자인하기 위한 몇 가지 규칙이 있습니다.

명확한 이름 정하기
→ 컨슈머들이 쉽게 이해할 수 있는 이름으로 지어야 한다.
→ 대상을 포함하는 컨텍스트를 이용하여 짧고 분명한 이름을 짓는다.
→ 약어를 피하고 내부의 코딩 규칙이 노출되는 것을 피해야 한다.

사용하기 쉬운 데이터 타입과 포맷 정하기
→ 정확한 표현을 제공한다.
→ 복잡한 포맷을 사용하는 경우 정보를 확실하고 충분하게 제공한다.
→ 컨텍스트에 대한 이해 없이도 이해할 수 있어야 한다.

바로 사용할 수 있는 데이터 선택하기
→ 연관성이 있는 데이터를 제공해야 한다.
→ 명확하기 않은 데이터를 의미 있는 데이터로 변경해야 한다.
→ 컨슈머가 추가적인 작업을 할 필요가 없도록 부가적인 데이터를 제공한다.

위의 규칙을 반영하여 POST /postBookSt를 명확한 이름으로 수정해 보겠습니다.

  1. 약어를 피하기 위해서 St를 Seat로 변경하였습니다. PostBookSt ⇒ PostBookSeat
  2. 정확한 표현을 제공하기 위해서 Book을 Reservation으로 변경하였습니다. PostBookSeat ⇒ PostReservationSeat
  3. Post 는 HTTP 메소드를 통해 표현하기 때문에 불필요해 보이므로 Post를 제거했습니다. PostReservationSeat ⇒ ReservationSeat

기존에 POST /PostBookSt으로 명명했던 API 이름을 직관적인 표현으로 디자인하는 규칙을 반영하여ReservationSeat으로 변경하였습니다.

직관적인 상호작용으로 디자인해야 합니다.

상호작용의 첫 단계는 사용자로부터 시작합니다. 직관적인 입력을 제공하여 사용하기 쉬운 편안한 환경을 사용자에게 제공해 줄 수 있습니다. 우리가 만드는 코드숨 공부방 좌석 예약 api에서 좌석을 예약하는 기능을 예로 들어보겠습니다. 원하는 방문 일자와 계획을 입력해서 예약을 할 수 있습니다. 계획 제출과 동시에 예약이 발생할 것입니다. 간단한 입력을 디자인해보겠습니다.

POST /resv

{
	"dt":"1528502400", //방문일자
	"ct":"공부하기" // 계획내용
}

위 예시는 모호한 약어와 이해하기 어려운 데이터 포맷으로 작성되어 있습니다 이를 사용자가 입력하기 쉬운 데이터로 변경해 보겠습니다.

예약이라는 목표는 REST 명령으로 정의를 하며 POST /reservations 라는 것이 POST /resv(약어)로 구성하는 것보다 좋습니다.

속성의 이름도 명확하게 짓는 것이 좋은데, 날짜를 뜻하는 “date”와 내용을 뜻하는 “content”로 이름 짓는 것이 dt와 ct와 같은 약어로 짓는 것보다 좋습니다. 그리고 이해하기 쉬운 데이터 포맷을 사용해야 합니다.

위의 예시는 UNIX 타임스탬프를 사용하고 있는데, 이를 ISO 8601 데이트 포맷으로 변경하여 “2022-10-21”과 같이 사용자가 이해하기 쉽도록 만드는 것이 좋습니다.

POST /reservations

{
	"date":"2022-10-21",
	"content":"공부하기"
}

이렇게 직관적으로 만들 수 있습니다. 하지만 여기서 끝이 아닙니다. API 상호작용은 언제나 성공하지는 않기 때문에 반드시 각 목표 별로 발생할 수 있는 모든 에러를 식별해 내야 합니다.

예약 목표도 에러 피드백이 발생할 수 있습니다.

규격에 맞지 않는 에러

사용자가 date를 잘못된 데이터 타입으로 입력한 경우를 가정해 보겠습니다.

POST /reservations

{
	"date": 2022 ,
	"content":"공부하기"
}

date는 “2022-10-22”와 같이 문자열로 입력해야 하는데 컨슈머가 숫자로 잘못 입력했습니다. 이는 규격에 맞지 않는 에러이며, 즉 컨슈머가 유발한 에러이므로 컨슈머가 처리해야 함을 알려줘야 합니다.

이런 경우 400 Bad Request 에러를 반환하여 컨슈머에게 피드백을 줄 수 있습니다.

기능적 에러

만약 사용자가 이미 예약했던 날짜에 다시 예약을 시도한다면 어떤 에러 피드백을 주어야 할까요?

POST /reservations

{
	"date":"2022-10-21", // 중복신청
	"content":"공부하기"
}

서버가 이해할 수 있는 형태의 리퀘스트를 보냈지만 이미 예약한 날짜에는 또 예약을 할 수 없는 비즈니스 규칙의 구현 때문에 발생한 에러이므로 기능적 에러입니다. 400 Bad Request 에러를 반환해 컨슈머에게 피드백을 줍니다.

서버 에러

만약 좌석 예약에 필요한 데이터 베이스가 구축되어 있지 않다면, 사용자가 예약 요청을 정상적으로 보냈더라도 서버에서 처리를 할 수 없을 것입니다. 이 경우에는 프로바이더가 유발한 에러임을 알려주기 위해 500 Internal Server Error 를 반환해야 합니다.

이처럼 API의 에러 피드백은 최대한 유용한 정보를 제공해야합니다. 반드시 컨슈머에게 무엇이 문제인지 알려줘야하며, 곧바로 해결할 수 있도록 정보를 제공해야합니다.

유즈 케이스 사용 예 HTTP 상태 코드
기능적 에러 중복된 방문 예약 400
잘못된 파라미터 GET /reservations/100 존재하지 않는 100번 예약에 대한 조회 404 Not Found
필수 속성의 누락 date가 누락됨 400 Bad Request
예견치 못한 서버 에러 구현에 버그가 숨겨져 있음 500 Internal Server Error

그리고 에러 코드만으로 표현할 수 없는 상세한 정보를 주기 위해 메시지를 함께 응답할 수도 있습니다.

{
	"message" : "이미 예약된 날짜입니다. 다른 날짜로 예약해주세요."
}

유용한 성공 피드백 반환하기

에러 피드백을 만들어내는 것과 마찬가지로 유용한 성공 피드백을 제공하는 것은 직관적인 상호작용 중, 출력에 있어서 중요합니다. 정확한 HTTP 상태 코드와 직관적인 리스폰스 바디를 제공해야 합니다.

코드숨 예약이 성공한 후 받은 성공 피드백입니다.

200 OK

하지만 예약 취소를 가능하게 하려면 어떻게 해야 할까요?

200 OK

{
	"id" : "8c7aaf67489",
	"date" : "2022-10-21",
	"content" : "계획입니다"
}

리스폰스에는 생성된 리소스에 대한 모든 정보가 포함되어 있어야 합니다. id 속성은 리퀘스트에 의해 생성된 특정 예약에 대한 요청을 취소할 때 쓰일 수 있습니다. 이 속성이 없다면 예약 취소는 할 수 없을 것입니다.

직관적인 흐름으로 디자인해야 합니다.

사용자는 여러 상호작용을 연쇄적으로 사용해야 하는 경우도 있습니다. 이러한 연쇄적인 흐름은 사용성에 큰 영향을 미칩니다.

코드숨 좌석 예약 정보를 불러오는 기능을 예로 들어보겠습니다. 코드숨 예약 정보를 보여주기 위해서 예약 목록을 불러오고, 그 예약에 포함되어 있는 공부 계획서 정보와 회고 정보를 따로 불러와야 한다면, API를 호출하는 연쇄적인 흐름이 생깁니다.

예약 정보를 가져와야만 공부 계획서 정보와 회고 정보를 가져올 수 있다면, 이 흐름은 상태가 존재한다고 말할 수 있습니다. 왜냐하면 공부 계획서 정보, 회고 정보 조회 API는 예약 정보 조회에 의존하고 있기 때문입니다. 그래서 각 API가 서로 의존하고 상태가 없도록(stateless) 만들어야 합니다.

심지어 예약 정보를 불러오고 나서 공부 계획서 정보를 불러오다가 알 수 없는 에러가 발생했다고 가정해 보겠습니다. 그러면 예약 정보 불러오는 과정은 어떻게 되어야 할까요?

직관적인 표현 원칙 중 하나인 사용 가능한 데이터를 제공하는 방법을 사용할 수 있습니다. 예약 정보를 제공할 때 취소되거나 삭제된 정보들은 제외하고 보내주는 것이죠. 사용 가능한 데이터들만 제공하는 새로운 목표를 만들었고, 에러를 방지할 수 있게 되었습니다.

예약 정보와 공부 계획서 그리고 회고 정보는 각각 정보를 가져오는 것보다 호출 한 번으로 통합할 수 있습니다. 한 번의 호출로 정보를 전부 불러올 수 있다면, 컨슈머 입장에서 연쇄적인 흐름을 기억할 필요 없이 사용할 수 있습니다. 연쇄적 흐름을 없앰으로써 사용성을 개선할 수 있게 되는 것이죠.

그리고 더이상 공부 계획서 정보, 회고 정보 조회 API는 예약 정보 조회에 의존하지 않아 상태가 없어야 한다는 REST 제약도 따르게 되었습니다.

따라서 직관적인 흐름을 디자인하려면 다음 규칙들을 따라야 합니다.

  • 각 목표가 직관적인 상호작용을 제공하는지 확인합니다.
  • 목표의 호출에서 입력과 출력이 한결같은지 확인합니다.
  • 가능한 경우 기존 목표에 데이터를 추가하여 오류를 방지하여 새로운 목표를 만듭니다.
  • 가능한 경우 컨슈머의 관점에서 기능적으로 합리적이라면 목표들을 통합합니다.
  • 연쇄되어 있는 모든 목표는 상태가 없어야(stateless) 합니다.