API를 구조화하는 방법은 무엇이 있을까요?

API를 구조화하는 이유는 다음과 같습니다.

  • 논리적으로 그룹화하고 정렬된 경우 원하는 요소를 쉽게 찾아내고 그 목적도 이해하기 쉽게 해줍니다.
  • API의 목표를 가상 또는 물리적으로 구조화하면 컨슈머가 API를 이해하는데 큰 도움이 됩니다.
  • API를 정의하는 문서에서 목표를 정렬해서 배치하면 API 명세 포맷을 준수하는 이점을 활용해 쉽게 성취할 수 있습니다.

데이터와 피드백과 목표의 구조화 정도에 따라 사용하기 힘들 수도 있고, 반대로 매우 직관적일 수도 있습니다.

잘 구조화된 API를 디자인하는 첫 단계는 데이터에서 시작됩니다.

데이터 구조화하기

// 구조화전 데이터
{
	"date":"2022-10-10",
	"content":"코테 공부할 예정",
	"date2":"2022-10-11",
	"status":"RESERVATION_RETROSPECTIVE_COMPLETE",
	"content2":"보람찬 하루였다.",
}

위의 예시의 구조화 전의 데이터는 date, date2 같은 속성들로 인해 데이터들의 연관성을 전혀 파악할 수 없습니다. 위치도 뒤죽박죽이라 어떤 것끼리 연관된 것인지 이해하기 힘듭니다.

사실 date 같이 아무 숫자가 붙지 않은 속성들은 ‘예약’ 관련 데이터이고, date2처럼 숫자가 붙은 속성은 ‘회고’ 관련 데이터였습니다. 이제 몇 번의 단계를 거치면서 데이터를 구조화하여 한눈에 연관성을 파악할 수 있도록 변화시켜 보겠습니다.

먼저 위치를 통해 연관성 있는 속성끼리 가깝게 위치하도록 이동시켜 위치를 통한 연관성을 부여했습니다. 숫자가 붙지 않은 속성과 붙은 속성끼리 위치를 인접하게 변경해 그나마 연관성을 유추할 수 있게 되었습니다.

{
	"date":"2022-10-10",
	"content":"코테 공부할 예정",
	"status": "RESERVATION_RETROSPECTIVE_COMPLETE",
	"date2":"2022-10-11",
	"content2":"보람찬 하루였다.",
}

혹은 date, date2 같은 알 수 없는 이름을 없애고 연관된 속성에 동일한 접두사를 붙여 연관성을 부여할 수도 있습니다. 이제 각 속성이 어떤 것인지에 대해서는 알 수 있게 되었습니다.

{
	"reservationDate":"2022-10-10",
	"reservationContent":"코테 공부할 예정",
	"retospectivceDate":"2022-10-11",
	"reservationStatus":"RESERVATION_RETROSPECTIVE_COMPLETE",
	"retrospectiveContent":"보람찬 하루였다.",
}

// 위치 정렬과 접두사를 모두 활용하면 아래와 같이   연관성이 있어 보이겠죠
{
	"reservationDate":"2022-10-10",
	"reservationContent":"코테 공부할 예정",
	"reservationStatus":"RESERVATION_RETROSPECTIVE_COMPLETE",
	"retospectivceDate":"2022-10-11",
	"retrospectiveContent":"보람찬 하루였다.",
}

마지막으로 ‘예약(reservation)’과 ‘회고(retrospective)’라는 가상의 경계를 하위 구조를 통해 실제 경계로 만들어 냈습니다.

{
	"reservation": {
		"date":"2022-10-10",
		"content":"코테 공부할 예정"
		"status":"RESERVATION_RETROSPECTIVE_COMPLETE"
	},
	"retrospective": {
		"date":"2022-10-11",
		"content":"보람찬 하루였다."
	}
}

이렇게 그룹화를 통해 속성 간의 관계를 좀 더 분명하게 만들 수 있습니다. 그뿐만 아니라 데이터의 중요도에 따라 정렬을 함으로써 가독성을 좀 더 향상시킬 수도 있습니다. 데이터 구조화 전과 후를 비교하면 어떤가요? 데이터 구조화를 통해 훨씬 직관적인 API 데이터를 디자인할 수 있게 되었습니다.

피드백 구조화 하기

구조화가 잘 된 API의 경우 피드백도 구조화가 잘 되어 있습니다.

{
	"message": "Invalid request",
	"errors": [
		{
			"source": "reservation",
			"type": "DUPLICATE_RESERVATION",
			"message":"해당 날짜에 이미 예약이 존재합니다."
		},
		{
			"source": "retrospective",
			"type": "CONTENT_NOT_FOLLOWED_RULE",
			"message":"최소 100자 이상 입력해야 합니다."
		}
	]
}

기본 프로토콜의 피드백 구성을 활용하여 쉽게 해석할 수 있도록 피드백을 구성하고 구조화된 피드백 구조를 만들어 낸 뒤, 이 에러들을 심각한 에러부터 사소한 에러 순으로 정렬합니다.

목표 구조화 하기

목표도 구조화할 가치가 있습니다. API의 목표는 가상이든 실제 물리적으로든 조직화하여 나눌 수 있습니다. 목표를 그룹화할 때는 기능적인 관점에서 그룹화해야 합니다. 반드시 각 목표의 기능적인 측면에 집중해야 하며, 표현에 집중해서는 안 됩니다. 또한 URL 경로가 바로 목표를 대변한다고 착각해서도 안됩니다.

// 구조화 전
post /reservations
get /reservations
get /reservations/{id} 
put /reservations/{id}
post /signup
patch /reservations/{id}
post /login
get /reservations/{id}/retrospectives
post /reservations/{id}/retrospectives
put /reservations/{id}/retrospectives/{id}
// 1.목표에 따른 그룹화
retrospectives
	get /reservations/{id}/retrospectives
	post /reservations/{id}/retrospectives
	put /reservations/{id}/retrospectives/{id}
reservations
	post /reservations
	get /reservations
	get /reservations/{id} 
	put /reservations/{id}
	patch /reservations/{id}
auth
	post /login
	post /signup

// 2.기능적 관점으로 태그를 통한 구조화
retrospectives
	post /reservations/{id}/retrospectives
	put /reservations/{id}/retrospectives/{id}
	get /reservations/{id}/retrospectives
reservations
	put /reservations/{id}
	post /reservations
	get /reservations
	patch /reservations/{id}
	get /reservations/{id}
auth
	post /login
	post /signup

// 3.정렬된 태그 정의를 추가
auth
	post /login
	post /signup
reservations
	put /reservations/{id}
	post /reservations
	get /reservations
	patch /reservations/{id}
	get /reservations/{id}
retrospectives
	post /reservations/{id}/retrospectives
	put /reservations/{id}/retrospectives/{id}
	get /reservations/{id}/retrospectives

// 4.문서내의 리소스와 메서드를 정렬
auth
	post /signup
	post /login
reservations
	post /reservations
	get /reservations
	get /reservations/{id}
	put /reservations/{id}
	patch /reservations/{id}
retrospectives
	get /reservations/{id}/retrospectives
	post /reservations/{id}/retrospectives
	put /reservations/{id}/retrospectives/{id}

API의 목표를 가상 또는 물리적으로 구조화하면 컨슈머가 API를 이해하는 데 도움이 됩니다. 이는 API를 정의하는 문서에서 목표를 정렬해서 배치하면 API 명세 포맷을 준수하는 이점을 활용해 쉽게 성취할 수 있습니다.

API를 세분화하는 방법은 무엇이 있을까요?

API가 지나치게 많은 정보를 요구하거나, 많은 기능을 제공하면 일반적으로 사용하기 어려운 API가 됩니다. 따라서 데이터, 목표에 따라 세분화하는 것이 좋습니다.

데이터 세분화하기

데이터 세분화는 어떻게 할 수 있을까요? 아래 예시를 통해 데이터 세분화에 대해 알아보겠습니다. 코드숨 사이트의 예약 정보를 조회하는 API입니다.

{
	"id": 1,
	"name": "홍길동",
	"age": 38,
	"region": "대한민국",
	"city": "서울",
	"date": "2022-10-25",
	"content": "계획"
}

위의 데이터에서 서로 연관이 없는 데이터가 함께 있습니다. 그래서 속성을 봐도 이 데이터가 무엇을 나타내는지 이해하기 어렵습니다. 만약 기능적 관점에서 데이터를 구조화한다면 어떻게 될까요?

{
	"reservation" : {
		"id": 1,
		"content": "계획",
		"date": "2022-10-25"
	},
	"user": {
		"name": "홍길동",
		"age": 38,
    "address": {
		  "region" : "대한민국",
		  "city": "서울"
    }
  }
}

위와 같이 예약 정보, 사용자 정보, 그리고 주소 정보를 구조화했습니다. 서로 연관이 있는 데이터끼리 묶여있으므로 더 이해하기 쉬워졌습니다. 따라서 데이터를 기능에 따라 구조화하는 것이 좋습니다.

목표 세분화하기

위의 예시를 보면 예약 정보를 불러올 때도 위의 요청을 해야 하고, 사용자 정보를 요청할 때도 위의 요청을 해야 합니다. 그리고 사용자 정보만 필요하여 요청하더라도, 필요 없는 예약 정보가 같이 응답됩니다. 그러면 이 API에 두 가지 목표가 있기 때문에, 이 목표를 세분화해야 됩니다.

GET /reservations/{id}
{
	"id": 1,
	"content": "계획",
	"date": "2022-10-25"
}
GET /users

{
  "name": "홍길동",
	"age": 38,
  "address": {
	  "region" : "대한민국",
	  "city": "서울"
  }
}

사용자 정보를 가져오는 API와 예약 목록 API를 가져오는 API를 분리했습니다. 이제 예약 목록을 조회할 때 예약 정보만 가져옵니다. 따라서 데이터의 목표에 따라서 API를 세분화하는 것이 좋습니다.