SMAIVNN
article thumbnail

 

해당 게시글은 영진닷컴의 <일상 속 사물이 알려주는 웹 API 디자인 - 아노드 로렛>을 바탕으로 작성 되었으며

영진닷컴의 저작권 허락을 바탕으로 게재되는 글입니다.

 

1편 - [좋은 웹 API 디자인] API 디자인 기초 https://smaivnn.tistory.com/8

 

이 전 글에서 우리는 컨슈머의 입장에서 api를 구현해야 한다고 했다. 

그렇다면 컨슈머의 입장은 어떻게 알 수 있을까?, 바로 사용자 입장에서의 직관이다.

 

직관적인 이름

나는 판타지 소설을 좋아한다. 판타지 소설을 읽다보면 미들네임을 갖고 있는 이름이 많이 보인다. 가령 예를 들어 "게임 속 바바리안으로 살아남기"의 주인공 이름은 "얀델의 아들 비요른"이다. 이러한 미들네임은 서양에서 많이 볼 수있다. 고대 로마의 가이우스 율리우스 카이사르부터 현대의 한나 다코타 패닝, 로빈 리한나 펜티 등.. 

이러한 미들네임은 고대에는 씨족, 계파의 이름까지 넣는 것이 기원이었다.

 

여기서 우리는 무엇을 알 수 있는가? 부모의 이름을 넣어가며 부모를 기리고 싶어하는 자식의 마음? 아니다.

바로 구체적인 그들의 네이밍을 봐야한다.

 

명확한 네이밍은 아주 중요하다, 이렇게 명확한 네이밍을 위해서는 api의 목적이 무엇이건 가능한 두 세 단어의 조합까지만이 권장된다. 또한, 약어(※자주 사용하는 것은 제외)를 최대한 지양하며, 내부의 코딩 규칙이 노출되는 것은 피해야한다.

 

데이터 포맷

아래와 같은 데이터 포맷이 있다고 생각해보자.

{// 1
	"nubmer" : 1234567,
	"sendDate" : 1534960860,
	"recieveDate" : 1423267200,
	"type" : 1
}

{// 2
	"nubmer" : "00012345678",
	"sendDate" : "2018-08-22T18:01:00z",
	"recieveDate" : "2015-02-07",
	"type" : "checking"
}

1번과 2번 중 어느 것이 더 익숙한가?

물론 UNIX를 다루는 개발자라면 1번이 더 익숙할 수 있다. 하지만 대부분, 그리고 개발을 모르는 일반인이 더 알아보기 쉬운 것은 2번이라고 확신한다. 우리는 "기계한테 무엇이 더욱 편할 것인가?"가 아닌 인간 중심적인 사고 즉, "우리는 어떤 것을 더 잘 알아볼 것인가?"를 고민해야 한다.

 

그래서 어떻게..?

자 그럼 어떻게 직관적인 이름을 지을까?

1. 명확한 이름 직기

src > source, idx > index 등

 

2. 이해하기 쉬운 데이터 포맷사용하기

UNIX의 타임스탬프 대신 ISO8601 데이트 포맷

 

3. 제공하기 쉬운 데이터를 요청하기

 

이렇게 첫 번째 상호작용을 디자인 해 봤다. 그럼 두번째로 따라오는 디자인은 무엇일까? 

바로 피드백이다.

 

피드백

404 not found

인터넷을 사용해본 사람들이라면 많이들 본 문구일 것이다.

API는 에러에 대해 어떤 오류인지 그리고 어떤 케이스에서 발생한 것인지 친절할 필요가 있다.

 

에러

에러는 세 가지로 분류할 수 있다.

 

1. 규격에 맞지 않는 에러

2. 기능적 에러

3. 서버 에러

 

이러한 에러들에 대해서 문제 상황이 무엇인지 정보를 알려주기 위한 에러 코드(상태 코드)의 반환 또한 정밀해야 한다.

 

404 not found의 404가 상태 코드를 뜻한다.

현재 IANA가 관리하는 공식 HTTP 상태 코드는 다음과 같다.

  • 1xx (정보): 요청을 받았으며 프로세스를 계속한다
  • 2xx (성공): 요청을 성공적으로 받았으며 인식했고 수용하였다
  • 3xx (리다이렉션): 요청 완료를 위해 추가 작업 조치가 필요하다
  • 4xx (클라이언트 오류): 요청의 문법이 잘못되었거나 요청을 처리할 수 없다
  • 5xx (서버 오류): 서버가 명백히 유효한 요청에 대해 충족을 실패했다

출처 : 위키백과, HTTP 상태 코드

 

이 중 에러 사항에 대해서 상태 코드와 더불어 message를 함께 추가하기도 하는데, 이러한 message는 사용자를 위해 상세한 에러 리스폰스가 권장된다. 상세한 에러 리스폰스는 과정을 해소하기 위해 프로그래밍적인 방법으로 문제를 유발하는 속성을 알려주는 편이 더 좋다.

{ // 기본 에러 리스폰스
	"message" : "금액은 필수입니다."
}

{ // 상세한 에러 리스폰스
	"source" : "amount",
	"type" : "AMOUNT_OVER_SAFE",
	"message" : "금액이 사용가능 금액을 초과합니다."
}

이처럼 반환된 에러는 source와 type을 통해 어떤 로직의 문제인지 컨슈머가 쉽게 파악하고 해소할 수 있도록 한다.

 

물론 아직 고려할 부분은 많다.

만약 "동시성을 갖는 에러라면?", "같은 요청으로 다른 에러가 여러번 나는 상황이라면?"

정말 많은 케이스가 있을 것이다. 하지만 이에 대한 정답은 당연하게도 없다. 책에서 소개되는 추천되는 방법은 400 bad request로 한 번에 잘못된 유형의 요청 에러와 기능적 에러에 대한 피드백을 한 번에 반환하는 것이다.

 

물론 가장 좋은 것은 에러가 발생하지 않는 상황이겠지만 개발을 하며 이러한 상황은 거의 모든 상황에서 없을 것이다. 따라서 이를 위한 가장 좋은 대비책은 가능한 오류를 분석하여 예방하는 것이다.

 

명심하자. 한 번의 호출이면 완료가 된다거나 당연하게도 입출력이 한 결같은, 컨슈머의 입장에서도 편리해야 한다.

 

예측 가능성

우리 사회에는 과거에서 부터 내려오는 여러가지 일관적인 표시들이 있다. 예를 들어, 재생/일시정지, 화살표 기호, 전원 버튼과 같이 말이다. 우리는 이러한 일관적인 표시들을 통해 처음 보는 물건이라도 익숙한 감각을 통해 사용에 대한 예측을 진행한다.

 

이처럼 일관성을 갖는 무엇인가는 직관적인 인터페이스를 형성시켜준다.

우리의 API 구성 또한 이러한 일관성을 지켜야한다. 대부분 위에서 언급한 리소스, 파라미터, 리스폰스, 프로퍼티데이터 포맷상태 코드가 그렇다.

 

일관성 있는 API

그럼 어떻게 일관성 있는 좋은 API를 만들 수 있을까?

책에서는 아래 4단계를 지키면 된다고 소개한다.

 

1. API 내부에 일관성이 존재

2. 팀 API / 회사 / 조직 간의 일관성이 존재

3. 도메인 간의 api의 일관성이 존재

4. 외부 세계와 일관성이 존재

 

이처럼 우리는 현재 내 상태에서의 여러가지 환경을 고려하여 일관성 있는 API를 구상하는 것이 중요하다.

가장 좋은 것은, 이미 만들어진 통용되는 것을 따르는 것이다. 타인이 만들어 놓은 것, 조직에서 사용되는 것, 사회에서 통용되는 것을 활용한다. REST API는 문서화된 HTTP 프로토콜의 규칙을 준수만 한다면 일관성을 가질 수 있다.

 

하지만 여전히 명심해야 할 부분은, 너무 일관성을 생각한다고 직관성을 해치는 디자인 혹은 불필요한 상식 등을 가져올 필요는 없다. 그 타협점을 잘 확인하여야 한다. 여전히 강조되는 것은, 사용자가 혼란을 겪지 않도록 사용자의 관점을 갖는 것이다.

 

데이터 구조화

우리는 직관적이고 예측 가능한 API를 설계했다. 마지막으로 가장 중요한 것은 구현에 대한 크기를 정하는 것이다.

 

구조화

데이터

설계 단계에서 우리는, 사용 가능한 데이터를 디자인하며 반드시 데이터 그룹을 만들어 데이터를 구조화 해야한다.

1. 관련된 속성을 서로 가깝게 배치하고

2. 일반적인 접두사를 사용하며

3. 필요하다면 하위 구조를 만들고

4. 데이터의 그룹은 중요한 순서부터 덜 중요한 순서로 배치해야 한다.

 

피드백

이렇게 구조화가 잘 된 API는 피드백 또한 자연스럽게 구조화가 잘 되었을 가능성이 높다.

 

앞서 말한 상태 코드에 따른 통일, 그리고 "type"을 통한 에러 타입 표현 등이 이와 같다. 백 엔드 개발자로서 로그를 기록하다보면 치명적인 에러에서 덜 치명적인 에러 순서대로 정렬해서 반환하듯, 중요한 것 > 중요하지 않은 것의 피드백이 기록되어야 한다.

 

목표

우리가 구현하는 기능들  API명세서에 올라가는 그 목표들 또한 구조화한다.

1. 동작은 카테고리화 한다.

같은 목적을 갖는 것을 기능적인 관점에서 생각하여 구조화 한다. 예를 들어 은행API라면, 수신 받는 계좌에 관한 모든 것과 송금 내역은 서로가 서로의 기능에서 사용될 수 있는 여지를 갖고 있다. 따라서 이 둘을 서로 떨어뜨리기 보다는 하나의 카테고리 안으로 묶는 것이 마땅하다.

 

2. 사용 빈도에 따른 도메인을 정리한다.

은행 API 사용자들은 보통 돈을 보내는 것 보다 계좌를 가져오고 계좌를 조회하는 동작에 관심이 있을 것이다. 이처럼 우리는 카테고리 분류의 순서를 고려하여 정렬해야 한다.

 

3. 이해하기 쉽게한다.

다음 우리는 description등을 이용하여 적절한 설명을 기재한다. 주석, 설명 등은 이해를 돕기 위한 좋은 도구이다. 설명 뿐만이 아니다. 순서를 맞추는 것도 이해를 돕기 위햇 중요하다. GET > POST > DELETE의 순서 혹은 커스터마이징한 순서대로 HTTP 메서드의 순서 또한 모든 리소스간 동일해야 한다.

 

우리가 생각해야 할 것은, 목표 구조화에서 URL 혹은 표현과 같은 부분보다 기능적인 측면에 집중하는 것이다.

 

API 사이징

객체가 지나치게 많은 기능/정보를 제공하면 일반적으로 사용성을 굉장히 저하시킨다. 따라서  API를 구현할 때는 API의 사이즈를 깊게 고민해야한다.

 

앞서 설명한 은행 API를 예로 보자. 이체, 조회 등 통장에 대한 다양한 기능이 있다. 만약 계좌 정보에 대한 조회를 진행하는데 이체 목록에 대해서 혹은 이체와 관련된 다양한 내용이 줄줄이 나온다며? 또는, 내가 계좌 정보를 변경했는대도 이 전에 사용한 계좌 정보가 나온다면? 다양한 문제 혹은 귀찮음이 생길 것이다.

 

이를 해결하기 위한 적절한 방법은 API를 분리하는 것이다.

{ 은행API }를 { 은행 계좌  API, 송금 API }로 분리한다. 이렇게 둘은 상호 독립적으로 되었으며 다양한 상황에서 더 쉽게 사용할 수 있게 되었다. 

 

이처럼 데이터 구조와 목표, API까지도 만약 모두 기능적으로 유의미한 수준에서 쪼갤 수 있다면 더 작게 쪼개도록 하자.

profile

SMAIVNN

@SMAIVNN

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!