API를 설계하려고 하다 보니 가장 먼저 든 의문은 그런데 어떻게 짜야 좋은 API를 만들 수 있을까? 였다. 내 첫 프로젝트에서 API 주소를 만들 때는 솔직히 말해서 깊은 고민을 별로 하지 않았다. 남에게 공개할 목적으로 만들어진 Open API가 아니어서 그런 것도 있었던 것 같고, REST API라는 개념만 어렴풋하게 알고 있어서 DB에 데이터 저장할 목적이 아니면 GET, 새롭게 저장하는건 POST, 삭제는 DELETE, 수정은 PATCH를 쓴다는 정도로 이해하고 넘겨줘야 할 거 같은 데이터는 일단 다 JSON 안에 눌러 담았다.
그 때 짰던 코드를 보면 절대 좋은 코드라고 말할 수 없었다. 요청이 성공하는 경우에만 처리하고, 실패한 경우의 에러 처리를 단순히 Typescript의 throw error를 이용해서 모든 에러를 뭉뚱그려서 했다. 나중에 Frontend와 연결할 때 AxiosError가 발생했는데 그게 어떤 에러인지 정확히 몰라서 일일이 추적하느라 고생을 많이 했었다. 그리고 쓰일 것 같은 데이터를 모조리 전달했었는데 실제로는 쓰이지 않는 데이터까지 전달하는 등의 문제가 있었다. 이러한 문제들을 겪고 이번 프로젝트에서는 좋은 API란 어떤 것일까에 대한 고민을 API를 실제로 설계하기 전 반드시 해야겠다고 생각했다.
내가 생각했을 때 좋은 API는 곧 사용자가 사용하기 쉬운 API이다. 어떻게 짜야 사용자 입장에서 사용하기 편할까를 구글링하면서 생각해 본 결과 아래의 몇 가지 조건들을 생각해 볼 수 있었다.
불필요한 정보를 추가하는 것은 최대한 삼가야 한다. 그리고 어떤 field를 제공할 지는 신중하게 선택해야 한다. field가 부족하여 새 데이터를 추가하는 것은 어렵지 않지만, 이미 존재하는 field를 삭제하거나 수정하면 API를 이용하는 프로그램에서 에러가 생길 가능성이 있다. 따라서 무조건 필요하다고 생각되는 데이터만 처음에 field에 넣고, 확실하지 않은 field는 추가하지 않아야 한다.
에러를 확실하게 표시해 주는 것이 좋다. 가장 적절한 방법은 적절한 error code를 보내 주는 것이다. 예를 들면 다음과 같은 코드들을 쓸 수 있을 것이다.
요점은 에러가 났을 때 무슨 일이 일어났는지 확실히 알려 주는 것이 중요하다는 것이다.
값을 사용자가 어느 정도 예측 가능해야 한다. 예를 들어 응답이 정해져 있는 경우, ENUM type을 사용하여 값을 전달한다. 그리고 Boolean 값에 null을 넣지 않는 등 사용자가 예상 가능한 범주에서 값을 보내 주는 것이 좋은 것 같다. field 이름의 형식을 최대한 맞추는 것도 이에 해당될 수 있을 것 같다.
시스템이 제대로 작동하는지 확인할 수 있는 방법도 만들어야 한다. 서버 상태가 정상이 아닌데 계속 요청을 보내는 일을 막기 위해, 현재 API 응답이 제대로 이루어지고 있는지 확인할 수 있어야 혼란을 방지할 수 있다.
API 설계에 대해 검색하며 가장 많이 나온 말이 REST API이다. 사실 진짜 대강 알고 있는 개념이고, 이를 정확하게 설명하라고 하면 설명하는 것이 쉽지 않았다.
REST란 REpresentational State Transfer 의 약자이다. 즉 표현에 의해 State를 전달한다는 뜻이고, 표현은 자원의 표현을 의미한다. 즉 URI에는 자원, Resource를 쓰고 Method를 통해 CRUD를 적용하는 방식이라고 이해하면 될 것 같다. REST에는 6가지 제약 조건이 존재한다고 한다.
간단히 말해 클라이언트와 서버가 독립적으로 분리되어 있어야 한다는 뜻이다. 클라이언트와 서버를 독립적으로 구축할 수 있다는 뜻이므로, Backend와 Frontend를 나누어 개발하는 것을 생각하면 될 듯 하다.
클라이언트와 서버가 서로의 상태를 기억할 필요가 없다는 뜻이다. 그러니까 서버는 각 사용자에 대한 어떠한 정보도 기억하지 않는다. 각 요청은 과거의 요청과 상관없이 독립적으로 처리되어야 한다.
이 성질로 인해 서버는 각 클라이언트를 일일이 추적할 필요 없다. 모든 필요한 정보는 전부 request 안에 담겨 있기 때문이다.
서버에서 데이터를 클라이언트로 보내 줄 때 반드시 데이터가 캐시 가능한지를 표시해 주어야 한다. http에서는 cache-control header를 이용한다고 한다.
앞서 말한 대로, 자원에 대한 요청을 한정적인 method를 통해 수행하는 것과 관련이 있는 성질이다. 네 가지로 나눌 수 있는데, 네 가지 전부를 만족시키는 것이 상당히 힘들기 때문에 완벽한 REST API는 구현하기 힘들다고 한다. 네 가지 세부 사항은 다음과 같다.
이 외에도 layerd system(계층화), code-on-demand(서버에 코드를 요청하고, 서버는 실행가능한 코드를 제공) 원칙이 있어 총 여섯 가지가 있다. 그 중 code-on-demand는 optional이라고 한다.
REST API에 대해 자세히 알아보면서 내가 지금 만드려고 하는 API는 엄밀히 따지자면 uniform interface의 self-descriptive, HATEOAS 조건을 만족하지 않으므로 REST API라고 할 수 없다는 것을 알게 되었다. 위 조건을 굳이 만족시킬 이유를 아무리 생각해도 찾지 못해서 프로젝트에는 나머지 조건만 만족하는 HTTP API를 쓰기로 하였다. 그리고 representation은 Frontend를 React.js를 이용하여 구현할 것이기 때문에 JSON이 가장 적합하다고 결론내렸다.
다음 글에서는 이제 HTTP API의 URI와 Method를 정의하고, 각 request별 response, request body를 어떻게 해야 할지 JSON으로 정의해 볼 생각이다. 양이 많아질 것 같아서 두 편에 걸쳐 글을 쓸 수도 있을 것 같다.