Computer Science 모아보기 👉🏻 https://github.com/seoul-developer/CS
GitHub - seoul-developer/CS: 주니어 개발자를 위한 전공 지식 모음.zip
주니어 개발자를 위한 전공 지식 모음.zip. Contribute to seoul-developer/CS development by creating an account on GitHub.
github.com
💋 인트로
어제 스터디 통해서 현직 개발자님의 RESTful API에 대한 강의를 듣고 정리해본 글이다.
최대한 쉬운 언어로 작성하려고 노력했고, 나의 재해석이 많이 들어가 있으니, 참고해서 읽어주시면 감사하겠습니다.
💋 용어 정리
✔️ API
약속, 규약으로 통신에 참여하는 애플리케이션 간의 계약
이다.
백엔드, 프론트를 각각 애플리케이션이라고 부를 때 각각의 애플리케이션끼리 소통하는 방식을 나타낸 것이다.
모스부호로 말하든 암호로 대화하든 여러 가지 방법이 있는데 그 방법 중 REST API, WebSocket API, GraphQL API 등이 있고 이 개념들은 모두 개념적으로는 같은 계층이라고 보면 된다.
어떤 API를 사용할 지 결정하기 위해서는 반드시 적절한 이유가 필요하다.
- 프론트, 백엔드 간의 긴밀한 실시간 소통이 필요하면 ⇒ WebSocket API를 고려해볼 수 있다.
- 그냥 일반적인 소통이고 뭐 그닥 특별한 것 없다 하면 ⇒ REST API를 고려해볼 수 있다.
- 빅데이터 업체에서 로그, 어제 시청자 통계처럼 다계층적인 데이터를 백엔드 개발자에게 제공해야 한다면 ⇒ GraphQL API를 고려해볼 수 있다.
다만 여기서 중요한 것은, 백엔드에서 GraphQL을 쓰기로 했다면 프론트와 안드로이드가 모두 같이 GraphQL을 써야 한다는 것이다. API를 사용하는 것은 개발자이니, 백엔드에서 어떤 형태로 API를 제공한다고 하면 그 API를 사용할 웹, ios, 안드로이드 클라이언트 개발자 모두가 해당 형태의 API를 사용할 줄 알아야 한다. (최신기술이라고 무작정 좋은 것은 아닌 이유)
✔️ 하이퍼텍스트/하이퍼미디어
- 하이퍼텍스트: 일반적인 글자가 아니라, 어딘가로 이동할 수 있도록 연결이 되어 있는 텍스트
- 오늘날 대부분의 미디어는 하이퍼텍스트다. 이 블로그 글도 내가 자꾸 이렇게 링크를 달아놓으니 다른 곳으로 이동할 수 있어서 하이퍼텍스트다.
- 하이퍼미디어: 하이퍼텍스트인데 이미지도 있고 영상도 있으면 하이퍼미디어다.
- 하이퍼 텍스트 용어의 확장
- 우리가 사용하는 대부분의 것들은 하이퍼텍스트이자 하이퍼미디어다.
✔️ REST
- REST: Representational State Transfer
- REST 아키텍처 스타일의 규칙을 지켜 디자인한 API (규칙은 아래에 자세히 설명한다)
- RESTful API
- REST 아키텍처의 규칙들을 정확하게 지켜서 만든 API (규칙 한개는 선택이긴 함)
- 너무 빡빡해서 다 지키기 어렵고, 오히려 개발 생산성이나 보안에 취약해질 수 있다.
- 다 지키면 무슨 인증도 해준다.
- REST API
- 위처럼 모든걸 다 지켜서 만드는 것은 오히려 비효율적일 수 있으니, restful에서 주장하는 몇 가지만 알아서 쓰고, 규칙을 좀 줄여서 적당히 융통성 있게 만든 API
- 이정도만 하면 훌륭하다고 한다.
💋 REST 원칙
구체적으로 API를 설계하는 원칙이라기보다는, 의외로 그냥 아키텍처를 어떻게 설계하냐에 대한 인사이트를 더 담고 있다.
✔️ 1. client - server로 나누어 구성해라
- 프론트, 백엔드를 나누라는 원칙
관심사 분리, 단순화
를 통해, ui와 로직이 각각 독립적으로 발전하고, 확장성(재탕 가능성)을 고려해 설계할 수 있다.
과거(~2010년정도..?)에는 프-백 나누기보다는 웹개발자, 앱개발자로 나눴었다고 한다.
그래서 그냥 ui 나타내는 코드(현재로 따지면 프론트)에서 그냥 db 쿼리를 날리기도 했다고 한다. (나 초딩때라 난 경험한 적 없는 세계) 아마도, 옛날에는 앱이 없어서 굳이 클라이언트를 따로 나눌 필요가 없었는데 이제 클라이언트가 웹, Ios, 안드로이드 다 해야해서 프-백을 확실히 분리를 해야 백엔드 재탕을 할 수 있기 때문이 아닐까,, 추측한다.
✔️ 2. Stateless하게 설계해라
- 백엔드 세션을 쓰지 말라는 원칙
- 백엔드 서버는 별도로 요청하는 쪽을 기억하지 않고, request만 분석한 내용으로도 모두 처리할 수 있다.
stateless로 해야 백엔드 서버를 여러 개 늘릴 수 있기 때문이다.
stateful하게 만들면 이전에 요청했던 서버랑 동일한 서버에 정확히 요청해야해서 확장성이 매우 떨어지기 때문이다.
✔️ 3. Cache를 사용해라
- 캐시는 크게 client side, server side 2가지 있다.
- client side
- 같은 페이지는 계속 서버에 요청하지 않고 그냥 데려온다.
- 네이버 페이지에서 계속 새로고침할 때마다 다 똑같은 내용을 계속 서버하면 서버 터지기 때문에 적당히 저장해 놨다가 데려온다.
- server side
- 매번 DB에서 정확하게 데려오지 않아도 되는 내용은 캐시해 둔 데이터를 활용한다.
- 어제까지의 조회수, 시청자 수를 계속 db에서 불러오지 않아도 된다. db에서 불러온 정보를 캐시로 저장해두고 쓰는 것이다.
여기까지만 보면, 대체 API 설계에 대한 내용이 없다.
아래로 가도 대부분이 그렇다.
✔️ 4. Layered System
- 서버를 여러개로 만들라는 원칙
예를 들어서, 요청이 들어왔을 때 아래아 같은 로직이 있다고 하자.
어뷰징하는 요청인지 판단
→ 인증 과정이 유효한 유저인지 판단
→ 캐시가 되어있는 내용이면 캐시 서버에서 반환
→ 모두 통과 시 로직을 돌려서 결과 반환
이때 이 모든 과정을 하나의 서버에서 전부 처리하지 말고, 각 서버를 verify, cache, logic, log 서버로 다 분리해서 여러 겹으로 만들라는 것이다. (난 이제까지 돈이 없어서 전부 한 서버에서 했었지,,,)
이 경우에도 역시 프로그래밍에서 굉장히 중요한 원칙인 ‘관심사의 분리’를 위한 것이기 때문에, 한 서버 애플리케이션 내부에서도 로직들이 중첩되게 거미줄처럼 엮지 말아야 한다. 긍까, 인증 서버를 통과했다면, 그 뒤 로직으로 넘어갔을 때 다시 인증을 해야 하도록 로직을 작성하면 안된다. 하나의 요청에 대해서 인증 서버에서 모든 걸 처리하고 뒤 서버로 인증이 완료된 요청을 넘겼어야만 한다.
예를 들어서, 내가 게시판을 만들었다. 근데 사람들이 여길 마약의 소굴로 사용하면 안될테니 ‘대마쵸’ 이런 검색어는 금칙어로 검색도 할 수 없도록 막고 싶다. 게시판 서비스는 크게 금칙어인지 확인, 인증, 컨텐츠 반환으로 나눌 수 있다. 위 세 가지를 각각 서버로 나누라는 것이 이 원칙에서 말하는 것이다.
이렇게 각 서버를 뜯어 버리면, 검색 요청이 들어왔을 때, 금칙어인지 확인하는 api 쏴보고 나서 해보고, 통과하면 컨텐츠 서버에서 검색 후 결과를 반환한다.
사실 이렇게 서버를 다 나눠버리면 그냥 한 대 서버에서 할 때는 트래픽 관리(서버 부하 관리)만 하면 되는데 api 호출로 인한 네트워크 비용
까지 모두 감당해야 한다는 문제가 생긴다. (특히나 이 생각이 시작된 옛날엔 네트워크 비용이 더 비쌌겠지..?)
그럼 왜 이렇게 하지?
이것 역시 마찬가지로 관심사의 분리의 관점에서 확장성이 늘어난다는 결론이다.
- 검색이 아닌 다른 기능에서도 금칙어 확인 api를 쓸 일이 있을수도 있을 수 있다.
- 코드 변경, 로직 변경 시에도 api로 뜯어 놓으면 여기만 신경쓰면 된다.
- 이렇게 하지 않는다면, 라이브러리로 만들어 놓거나, 금칙어 확인을 사용하는 모든 팀에서 코드를 복사하면 될테지만, 코드 변경 시에 유연하게 대처하긱 어렵고 결과적으로는 신경쓸게 너무 많아진다. (사람은 멀티가 원래 잘 안된다. 이런 사소한 것에서 실수가 나는 것보다 어쩌면 네트워크 비용이 더 저렴할 수 있다는 계산 때문일까..?)
- 이건 내 뇌피셜인데, 금칙어인지 확인하는 서버가 좀 더 가볍고, 검색 서버의 쪽이 더 무거워 더 비쌀 것 같다.
- 트래픽이 늘어났을 때, 금칙어인지 확인하는 것도 함께 넣어놓으면 굳이 필요없이 서버를 더 늘려야해서 오히려 더 비용이 많이 들어갈 수 있다.
- 갈갈이 찢어놓으면 정확히 더 비싼 것만 늘리면 더 비용적으로 효율적일 수도 있을 듯..?
그러니깐, 이 원칙은 네트워크 비용보다 팀에 어떻게 규칙을 줘야 개발자들이 잘 관리할 지가 더 중요시하는 원칙이다. rest api는 개발자를 위한 것으로, 비용이 조금 들더라도 개발자들이 에러를 덜 내면 된다는 생각인 것 같다!
✔️ 5. uniform interface
- 일관적인 규약을 쓰라는 원칙
우리가 흔히 아는 rest api에 대한 내용이다. 뭐 계층적으로 uri 엔드 포인트를 만들라느니, 하는 행동은 uri가 아니라 메서드에 표시하라느니,,, 그런 흔한 내용이 여기 규칙에 해당한다.
이 내용은 아래에서 더 자세하게 설명하고 먼저 마지막 규칙부터 간단히 살펴보고 가자!
⇒ 마지막 규칙은 옵셔널이라 여기까지 다 지키면 restful api 인증 받을 수 있다ㅋㅋ
사실 여기까지만 보면, rest api는 rest라는 규칙에 충실하게, 아무렇게나 그때그때 일관성 없이 만들지 말고 실수하지 않게 다른 개발자가 보더라도 예측할 수 있게 깔끔하게 잘 만들라는 것이다.
여기까지는 훌륭하다. 어떤 박사가 만든 규칙이라니,,, 근데 아래 마지막 규칙에서 약간의 뇌절로 인해 큰 비판을 받게 된다.
✔️ 6. code-on-demand
비판의 시작점.. 두둥
- 유일하게
옵셔널
한 규칙(인데 뇌절) - 서버는 클라이언트에
코드를 전송
해서기능을 확장, 수정 가능
하다.
..? 뭥미
클라이언트를 단순화하고 시스템 확장성 증가하는 규칙이다.
클라이언트에 코드를 전송..? 이 개념은 이미 우리가 매일 사용하는 서비스에서 도입하고 있다.
바로바로 브라우저
!
웹 브라우저는 늘 클라이언트는 그대로 가만히 있는데, 서버에서 보내는 자바 스크립트 코드를 데려다가 출력한다. 따라서 웹 브라우저=code on demand
그러니깐 브라우저 말고 그냥 모든 서비스를 다 브라우저처럼 만들라는겨..?
이 규칙을 잘 지키면, 클라이언트는 배포 없이도 계속 서비스를 할 수 있다.
그냥 새로운 기능 생기면 백엔드 코드가 바뀌니깐 클라이언트는 유연하게 백엔드 코드를 받아다가 새롭게 배포할 필요 없이 띄워주면 되는 것이다. 이론적으로는 클라이언트, 백엔드 코드를 동시에 배포할 수 없는 경우에도 무중단으로 쓸 수 있게 된다.
(클라이언트 개발자 실직하는거 아닌교..?)
나름 ㄱㅊ은 것 같은데 왜이렇게 까이지?
이 규칙을 지키게 되면, 백엔드 코드가 결과적으로 클라이언트 구현에 의존하게 되고, 보안 이슈가 생긴다.
백엔드에서 코드를 전송할 수 있다면 해커도 클라이언트에 코드를 전송할 수 있기 때문이다.
💋 UNIFORM INTERFACE
지금부터는 이제까지 우리에게 좀 더 익숙한 ‘일관적인 api를 설계해라’에 대한 규칙이다.
✔️ 1. identification of resource
- 모든 자원은 고유한 식별자(id)를 가지고 있어야 한다
이 규칙에 따르면, 요청에서 아이디를 보냈다 하더라도 응답에서 또 주는 것에 좋다고 한다.
클라이언트가 id: 1인 사용자 정보 줘~ 하면 서버는 id:1, 이름: 깃짱, 직업: 프로그래머 이렇게 주는 것이다.
클라이언트 측에서 기존에 뭐라 요청했는지 신경 안써도 되도록 식별자인 아이디는 요청에서 한 번 말했어도 또 주라는 것이다.
✔️ 2. Resource manipulation through representation
이 부분은 내가 정말 많이 플젝 하면서 고민했던 부분인데 속이 뻥 뚫렸다.
정보의 원천(저장된 형태)과 표현(응답으로 보내는 모양)은 달라도 된다는 규칙이다.
그러니깐, db를 긁어서 그대로 뿌리지 말고, 클라이언트랑 약속한대로~
api를 너무 db에 종속적으로 구현하지 말라는 규칙이다. DB는 snake case로 개발했더라도 응답을 보낼 때는 카멜 케이스로 응답을 보내도 된다. db에서 생/년/월/일을 다 떼어 각각을 저장해놨더라도 응답에서는 생년월일을 합쳐서 보내줘도 된다.
만약에 주민등록번호를 000***-4******라고 표시하는 화면에서 직접 찐퉁 주민번호를 백엔드에서 주지 말고 응답에서부터 ***으로 가려서 보내라 이런 느낌이다.
그치만, 표현은 자유롭게 하되 input, output은 일관적으로 해야 한다.
만약에 생년월일을 통으로 응답을 보내줄거면, 그 데이터에 대한 요청을 받을 때도 생년월일 통으로 받고 year/month/day로 나눠 받지 말아야 한다.
일관된 규칙을 정해서 프론트, 백엔드 싸우지 말라는 규칙인 듯 하다.
✔️ 3. self-descirptive messages
- 응답에 필요한 내용을 header에 다 넣으라는 규칙이다.
status code를 잘 정해서, 통신이 성공한 것과 로직이 성공한 것을 분리해서 처리하라는 규칙이다.
근데 실무적으로는 너무 상태코드를 구체적으로 쓰면 보안에 취약해질 수 있다.
막 실패했는데도 200대 응답으로 주는 것처럼 너무 http 프로토콜을 무시하면서 쓰지는 말되, 개발자들이 잘 이해할 수 있으면 해커도 잘 이해할 수 있다는 것을 염두하고 사용하면 좋을 것 같다.
그냥 실패했으면 400대로 주더라도 약속한 error code를 따로 사용한다던가 하는 방식으로 http 규칙도 무시하지 않으면서 보안도 지킬 수 있는 적정 지점이 어딘가 있을 테니 그 융통성있게 적당히 적당히 팀에서 정한대로,,,
⇒ 여기까지 모두 다 그때그때 다르게 구현하지 말고, 잘 이해되고, 직관적이고 실수하지 않게 잘 만들라는 규칙들
✔️ 4. hateoas
박사님 잘 나가다 마지막에 또 뇌절,,,
- 응답에 그냥 내용만 주는게 아니라,
추가적으로 접근할 수 있는 엔드 포인트도 함께 보내주라
는 규칙
이렇게 하면 엔드포인트를 클라이언트랑 상의 없이 변경할 수 있다는 장점이 있다. 그냥 내맘대로 변경해도 다른 요청들에서 다 변경된 엔드포인트를 응답에 보내주면 되니껜!
약간 위에서 말했던 code on demand와 연결되는 개념이다.
다른 곳에 갈 수 있는 모든 정보를 줘야 하는 규칙이라서, 보다시피 굉장히 복잡성이 늘어난다. 내가 클라이언트였으면 무슨 정보를 요청했었는지도 까먹을 지경(은 좀 많이 갔나..?)
그냥 db 째로 다 퍼다 주지 그래?라는 비판도 많이 받고 있고 필요 없는 정보 너무 많이 준 거 아니야?라는 비판도 많이 받고 있다.
code on demand 때부터 클라이언트를 너무 무시해서 비난을 많이 받는 중인 것 같다. 다 실직시킬 생각인가..?
💋 아웃트로
무튼 오늘은 이렇게 RESTful 하다는 것의 의미에 대해서 진짜 자세히 살펴보았다.
나름 취할 것은 취하고, 뇌절인 것은 무시하면서 우리 서비스가 여러 개발자들 사이에서 ‘실수 없이 일관성있게 잘 만들도록’ 하면 되는 것이라고 생각한다.
모두 화이팅
도움이 되었다면, 공감/댓글을 달아주면 깃짱에게 큰 힘이 됩니다!🌟
비밀댓글과 메일을 통해 오는 개인적인 질문은 받지 않고 있습니다. 꼭 공개댓글로 남겨주세요!
'Computer Science > ETC' 카테고리의 다른 글
[CS] 프레임워크 vs 라이브러리 (0) | 2023.12.26 |
---|