From 61a582d6eb44bbcc14dfdf119cea49a8a6b77616 Mon Sep 17 00:00:00 2001 From: Younglong Date: Sun, 18 Feb 2024 13:50:51 +0900 Subject: [PATCH 1/3] feat: Web API (20231121) --- _posts/2023-11-21-web-api.md | 212 +++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 _posts/2023-11-21-web-api.md diff --git a/_posts/2023-11-21-web-api.md b/_posts/2023-11-21-web-api.md new file mode 100644 index 0000000..0a58b83 --- /dev/null +++ b/_posts/2023-11-21-web-api.md @@ -0,0 +1,212 @@ +--- +layout: post +title: 실시간 웹 애플리케이션을 위한 API 전략 +author: 김영롱 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2023/1121/interface.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [웹 API, REST, GraphQL, WebSocket, 기술세미나] +--- +안녕하세요, *Lune* 입니다. + +저는 이번에 **실시간 웹 애플리케이션을 위한 API 전략**이라는 주제로 기술 세미나를 진행했습니다. + +이 주제를 통해 REST, GraphQL, WebSocket API에 대해 주로 설명하고 각각을 비교해보려고 합니다. + +## 1. API란 +먼저 API(Application Programming Interface)가 무엇인지 간단히 알아볼까요? + +우선 Application은 고유한 기능을 가진 모든 소프트웨어, Interface는 애플리케이션 간의 서비스 계약이라고 할 수 있습니다. 그리고 계약은 요청과 응답을 사용하여 애플리케이션이 서로 통신하는 방법이라고 할 수 있죠. + +좀 더 쉬운 설명을 위해 이미지를 가져와봤습니다. 식당을 예시로 들어볼게요.
+1) 손님은 점원이 가져다준 메뉴판으로 메뉴를 주문하고, 점원이 주문을 요리사에게 전달 +2) 요리사는 주문받은 요리를 만들어 점원에게 주고, 손님은 점원이 가져다준 요리로 맛있게 식사 + +여기서 점원이 바로 API와 같은 역할을 한다고 볼 수 있습니다. + +![api](https://github.com/Kernel360/blog-image/blob/main/2023/1121/api.png?raw=true) + +식당 예시를 그대로 아래 이미지처럼 바꾼다고 하면 API는 좌측 프로그램이 요청할 수 있는 명령 목록을 제공하고, 명령을 받으면 우측 프로그램과 상호작용하여 명령에 대한 값을 받아 전달하게 되는 거죠. 쉽게 말해, API는 프로그램들이 서로 상호작용하는 것을 도와주는 매개체라고도 볼 수 있습니다. + +![api-2](https://github.com/Kernel360/blog-image/blob/main/2023/1121/api-2.png?raw=true) +(일부 설명 및 이미지 출처 → [api란-쉽게-설명-그린클라이언트](https://blog.wishket.com/api란-쉽게-설명-그린클라이언트)) + +### 웹 API +웹 API는 웹 서버와 웹 브라우저 간의 애플리케이션 처리 인터페이스입니다. +웹 API의 종류는 REST API, GraghQL API, WebSocket API, SOAP API, RPC API 등 다양한데요. +그 중 앞의 3가지 API에 초점을 맞춰서 이야기해보겠습니다. + +## 2. REST API +**REST API**는 REST 아키텍처 스타일을 따르는 API로 REST는 Representational State Transfer의 약어입니다. REST는 로이 필딩(Roy Fielding)이 HTTP의 장점을 최대한 활용할 수 있도록 고안한 아키텍처로 2000년에 처음올 소개되었고, 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하므로 ***웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일*** 입니다. + +### 주요 특징 +REST API의 주요 특징은 다음과 같습니다. +1) **Stateless** (무상태) + - 서버는 요청이 오가는 동안 클라이언트의 상태를 저장하지 않음 +2) **Cacheable** (캐시 가능성) + - 클라이언트가 응답을 캐시하여 네트워크 부하를 줄이고 성능을 향상시킬 수 있음 +3) **Uniform Interface** (통일된 인터페이스) + - API 디자인이 통일되어 있어 사용하기 쉬움 + - 즉, 애플리케이션 요구사항별로 다른 형식이 아닌, 표준화된 형식으로 정보를 전송할 수 있도록 구성 요소 간 통합된 인터페이스를 가짐 +4) **Server-Client** (서버-클라이언트 구조) + - 클라이언트와 서버가 각각 독립적으로 발전할 수 있음 + - 예를 들어, 웹 브라우저는 사용자에게 웹페이지를 보여주고, 서버는 데이터를 제공 +5) **Layered System** (계층화된 시스템) + - 시스템이 계층으로 나뉘어 있음 + - 각 계층은 특정 역할을 수행하며, 상위 계층은 하위 계층의 구현을 알 필요가 없음 + - 예를 들어, 클라이언트는 데이터를 요청하면 중간에 여러 계층을 거쳐 서버에 도달할 수 있지만 클라이언트는 중간 계층의 존재를 몰라도 됨 + +## 3. GraphQL API +**GraphQL API**는 2012년 Facebook(현 Meta)에서 개발한 API용 쿼리 언어로 2015년 오픈소스화되었는데요. 클라이언트가 서버로부터 원하는 데이터를 효율적으로 요청할 수 있게 하는 기술로 ***REST의 대안으로 설계*** 되었다고 합니다. + +그런데 REST API의 대안이라니.. 무슨 문제가 있다는 걸까요? GraphQL API을 더 잘 이해하기 위해 잠시 짚고 넘어가겠습니다. + +### REST API에 무슨 문제가 있나요? +아래 JSON 응답을 참고해 설명해보겠습니다.
+(Fetch는 *웹 페이지를 구성하기 위해서 다양한 서버에 요청을 보내고 데이터를 받아오는 작업* 이라고 생각하시면 됩니다.) + +1) Over-Fetching + - 원하는 응답 데이터 → 도서 ID, 도서명, 저자명만 필요, 그러나 해당 API Response 값에 가격, 출판사, ISBN 등이 포함되어 있다면 다 받아야 함 + - 즉, Over-Fetching은 필요한 데이터 이상으로 서버에서 데이터를 받아오게 되는 것을 의미하며, 필요없는 데이터까지 받아와 서버와 자원이 낭비됨 +2) Under-Fetching + - 도서 상세 페이지에서 도서 정보와 리뷰 목록을 보여주려고 함, 그러나 API가 도서 정보와 도서 리뷰에 대해 각각 다른 End-Point를 사용한다면 필연적으로 2번의 API 호출 발생 + - 즉, 한 번의 요청으로 필요한 데이터를 모두 받아오지 못해 여러 번의 요청을 수행하는 것을 의미하며, 네트워크의 지연이 발생할 수 있고 사용자는 느린 로딩 속도로 인해 불편함을 겪을 수 있음 +3) 다양한 엔드포인트 + - REST API는 여러 엔드포인트가 존재하며, 각자의 역할을 하고 있으므로 클라이언트는 다양한 엔드포인트를 요청해야 함 +```json +{ + "books": [ + { + // 필요한 정보 + "id": 1, + "title": "Do it! 자바 프로그래밍 입문", + "author": "박은종", + // 필요하지 않은 정보 + "price": 25000, + "publisher": " 이지스퍼블리싱", + "isbn": "9791163030195" + }, + { + // 필요한 정보 + "id": 2, + "title": "모두의 한국어 텍스트 분석 with 파이썬", + "author": "박조은, 송영숙", + // 필요하지 않은 정보 + "price": 27000, + "publisher": "길벗", + "isbn": "9791140704521" + } + ] +} +``` +위 문제들이 와닿으셨을까요? GraphQL API를 사용하면 REST API의 단점 중 하나인 Over-Fetching이나 Under-Fetching 문제를 효과적으로 해결할 수 있다고 합니다. + +### 주요 특징 +GraphQL API의 주요 특징은 다음과 같습니다. +1) **유연하고 강력한 데이터 쿼리 언어** + - 클라이언트가 필요한 데이터의 구조와 양을 정확하게 명시할 수 있는 강력한 쿼리 언어를 제공하므로 과도한 데이터 전송이나 다수의 요청을 최소화할 수 있음 + - 즉, REST API의 오버 페칭과 언더 페칭과 같은 이슈가 발생하지 않음 +2) **단일 엔드포인트** + - REST API에서는 각 엔드포인트마다 데이터를 요청해야 했지만, GraphQL은 단일 엔드포인트를 사용하여 클라이언트가 단일 요청으로 여러 데이터를 가져올 수 있음 + - ex) @PostMapping("/graphql") +3) **실시간 데이터 업데이트** + - 실시간 데이터 업데이트를 지원 + - 일반적으로 Subscription이라 불리는 메커니즘을 통해 이루어짐 +```text +query { + user(id: 123) { + id + name + email + posts { + title + content + } + } +} +``` +```json +{ + "data": { + "user": { + "id": 123, + "name": "John Doe", + "email": "john.doe@example.com", + "posts": [ + { + "title": "GraphQL Basics", + "content": "Introduction to GraphQL" + }, + { + "title": "Advanced GraphQL", + "content": "Deep dive into GraphQL concepts" + } + ] + } + } +} +``` + +## 4. WebSocket API +마지막으로 **WebSocket API**입니다. 일반적인 HTTP 통신은 클라이언트-서버간 요청-응답을 주고 받는 단방향 통신인데요. 채팅, 주식, 온라인 게임과 같은 실시간 애플리케이션에서는 ***빠른 속도로 정보를 전달하고 업데이트*** 하기 위해 양방향 통신이 필요합니다. WebSocket API는 이런 필요성이 요구될 때 사용되는 API랍니다. + +### 주요 특징 +WebSocket API의 주요 특징은 다음과 같습니다. +1) **양방향 통신** + - 클라이언트-서버간 양방향 통신 가능 + - 서버가 클라이언트에게 데이터를 푸시하고, 클라이언트가 서버에게 데이터를 전송할 수 있음 +2) **실시간성** + - 연결을 유지하면서 데이터를 실시간으로 전송 + - 단방향 통신과는 달리, 데이터의 지연 시간을 최소화하여 실시간 응용 프로그램을 구축하는 데 적합 +3) **단일 연결 유지** + - WebSocket은 한 번의 연결을 설정하고 유지함으로써 여러 요청에 대한 새로운 연결을 맺지 않아도 됨 +4) **효율적인 데이터 전송** + - 연결을 유지하면서 계속 데이터를 주고받기 때문에, 새로운 연결을 설정할 필요가 없어 헤더의 오버헤드가 감소함 +5) **이벤트 기반 모델** + - 이벤트 기반의 모델을 사용하여 메시지를 수신하고 처리할 수 있음 + +## 5. API 비교 +앞서 설명했던 내용에 덧붙여 간략하게 API별 비교를 위해 표로 정리해보겠습니다. + + 특징 | REST API | GraphQL API | WebSocket API +----------|--------------------------------|-------------------------------|----------------------------------- + 용도 | 주로 데이터 조회 및 간단한 상호 작용 (CRUD 작업) | 복잡한 데이터 요청 및 실시간 업데이트 | 실시간 데이터 전송 및 양방향 통신 + 사용사례 | 소셜 미디어, 블로그 등 | 복잡한 데이터 요청이 필요한 애플리케이션 | 실시간 채팅, 주식 시세 업데이트, 실시간 협업 등 + 통신방식 | 단방향 요청-응답 구조 | 클라이언트가 필요한 데이터를 정의하고 서버가 응답 | 양방향 통신으로 실시간 데이터 전송 + 지연시간 | 새로운 연결마다 지연 시간이 발생 | 클라이언트가 필요한 데이터만을 요청하여 효율적인 전송 | 연결 유지로 낮은 지연 시간 + 실시간 업데이트 | Polling 또는 Webhooks을 통한 업데이트 | 실시간 데이터 업데이트 | 실시간 데이터 업데이트 + 유연성 | 여러 리소스에 대한 각각의 고유한 엔드포인트를 사용 | 클라이언트가 필요한 데이터만 요청 가능 | 단일 연결로 다양한 메시지 처리 | + 복잡한 쿼리 | 여러 엔드포인트에 각각의 요청을 보내 복잡한 쿼리 처리 | 복잡한 쿼리 및 중첩된 필드 지원 | 주로 간단한 메시지 전송에 사용되며, 덜 복잡한 데이터 구조 + 상태 | 상태 저장이 필요하지 않음 | 상태 저장이 필요하지 않음 | 상태 저장이 필요한 경우 (예: 게임 상태) + 오버헤드 | HTTP 헤더, 상태 코드 등의 오버헤드 발생 | 필요한 데이터만 요청하므로 상대적으로 적음 | 일반적으로 상대적으로 낮은 오버헤드 + +## 6. 정리 +지금까지 기술 세미나에서 나눴던 얘기를 마무리했습니다.
+아래에는 각각의 웹 API를 어떤 상황에서 사용하면 좋을지 간단히 작성해봤습니다. 하나의 프로젝트에서 여러 API를 함께 사용할 수도 있기 때문에 상황에 알맞은 API를 적용해 사용하면 되겠죠~? + +- 주로 상태를 저장하지 않고 단순한 데이터 전송에 사용되며, 서버의 부하가 크게 발생하지 않는 경우 + - REST API +- 클라이언트와 서버 간의 상태를 유지하고 실시간 양방향 통신이 필요한 경우 + - WebSocket API +- 복잡한 데이터 요청이 필요한 경우이거나 실시간 업데이트가 필요한 경우 + - GraphQL API + +정말 정말 마지막으로 영상 하나를 추천하려고 해요!
+이번 세미나를 준비하며 보게 된 영상으로 흥미롭게 들었던 내용이라 공유드립니다. REST API와 관련된 내용인데 자주 사용하는 API인 만큼 시간되실 때 참고해보면 좋을 것 같습니다.
+ +[그런 REST API로 괜찮은가](https://youtu.be/RP_f5dMoHFc?si=9clhxJ_3Ucn5L4U3) + +## 7. 참고자료 +- [API란? 비개발자가 알기 쉽게 설명해드립니다!](https://blog.wishket.com/api란-쉽게-설명-그린클라이언트) +- [RESTful](https://positiveko-til.vercel.app/til/cs/restful.html) +- [REST란? REST API 와 RESTful API의 차이점](https://dev-coco.tistory.com/97) +- [WebSocket API의 기본 구성요소 및 기능](https://appmaster.io/ko/blog/websocket-api-guseong-yoso-mic-gineung) +- [GraphQL](https://graphql.org/learn/) +- [REST API에서 GraphQL로의 패러다임 전환 - Facebook이 주목한 기술](https://enjoydev.life/blog/frontend/11-graphql) +- [실시간으로 최신 데이터를 불러오는 Websocket API, REST API와 어떤 차이가 있을까?](https://youtu.be/LddPLO4bXmQ) +- [[10분 테코톡] 정의 REST API](https://youtu.be/Nxi8Ur89Akw?si=koW8ZxGvhG1xAiRl) +- [[10분 테코톡] ✨ 아론의 웹소켓&스프링](https://youtu.be/rvss-_t6gzg?si=sf4NiNiHzhwAl52D) From 7f94ce510746930b995af9aff55a7dab0fb7c78e Mon Sep 17 00:00:00 2001 From: Younglong Date: Sun, 18 Feb 2024 17:49:26 +0900 Subject: [PATCH 2/3] feat: Spring REST Docs (20240124) --- _posts/2024-01-24-spring-rest-docs.md | 272 ++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 _posts/2024-01-24-spring-rest-docs.md diff --git a/_posts/2024-01-24-spring-rest-docs.md b/_posts/2024-01-24-spring-rest-docs.md new file mode 100644 index 0000000..4dc3c08 --- /dev/null +++ b/_posts/2024-01-24-spring-rest-docs.md @@ -0,0 +1,272 @@ +--- +layout: post +title: 이번엔 Spring REST Docs를 써볼까? +author: 김영롱 +categories: 기술세미나 +banner: + image: https://github.com/Kernel360/blog-image/blob/main/2024/0124/spring-rest-docs.png?raw=true + background: "#000" + height: "100vh" + min_height: "38vh" + heading_style: "font-size: 4.25em; font-weight: bold; text-decoration: underline" +tags: [API 문서, Spring REST Docs, 기술세미나] +--- +안녕하세요, *Lune* 입니다. + +이번엔 **Spring REST Docs**를 기술 세미나 주제로 가져와봤는데요. + +Spring REST Docs를 프로젝트에서 사용하게 된 이유부터 적용기까지 공유해보려고 합니다. + +## 1. API 문서의 필요성 +조금 뻔한 얘기로 시작해볼까요?
+개발을 하면서 API 문서들이 필요한 이유는 무엇일까요? 제가 생각해봤을 때, 그리고 검색을 해봤을 때 아래와 같은 이유들이 있었습니다. + +1. API 사용법 이해 +2. 개발자 간의 효율적인 협업 +3. 빠른 문제 해결 및 버그 대응 +4. 개발 생산성 향상 + +개인적으로는 일단 2번이 제일 와닿습니다. 협업할 때 같은 API 문서를 보며 얘기하면 소통이 더 잘된다고 느낀 적이 많았거든요. + +## 2. API 문서를 만드는 여러 방법들 +자, 그럼 이번에는 API 문서를 만들 수 있는 방법들에 대해 이야기해볼게요. + +1. 구글 공유 문서 (Docs / Sheets) +2. Notion +3. GitBook +4. Swagger +5. Spring REST Docs + +위에 리스트 말고도 더 많은 방법들이 있겠지만, 제가 사용해봤거나 사용사례를 본 적 있는 것 위주로 작성해봤습니다. + +### 구글 공유 문서 / Notion / GitBook +코드베이스가 아니라 수동으로 직접 문서를 작성하는 방식입니다. 장단점을 알아보자면 다음과 같습니다. + + 장점 | 단점 +-----------------|------------------ + 쉬운 협업 및 공유 | API 문서 작성 기능 제한적 + 커스텀 스타일 적용 쉬운 편 | 비용 문제 발생 가능 + 사용자 친화적인 UI/UX | API 문서 자동화 어려움 + +위 내용이 3가지 모두에 동일하게 적용된다고 할 수는 없지만, 보편적인 장단점 위주로 설명해보려고 했으니 조금 안 맞다고 생각이 들어도 이해해주세요 👐 + +### Swagger / Spring REST Docs +코드 내에 API 문서화를 위한 작업을 진행하는 방식입니다. 장단점을 알아보자면 다음과 같습니다. + + 장점 | 단점 +----------------|----------------- + 자동 문서 생성 | 러닝커브 존재 및 설정 복잡 + 일관된 형식과 스타일 유지 | 커스터마이제이션 한계 + 실시간 업데이트 | 의존성 및 업그레이드 어려움 + +여기서 *커스터마이제이션 한계* 란 문서의 외관이나 기능을 개발자가 원하는 대로 조정하는 데에 한계가 있다는 것을 의미합니다. + +## 3. Spring REST Docs를 선택한 이유 +현재 진행 중인 프로젝트에서는 왜 Spring REST Docs를 선택하게 되었을까요? + +우선 첫 시작은 GitBook 이었답니다. 기능 개발이 들어가지 않은 상태에서 프론트엔드 개발자와의 빠른 협업을 위해 GitBook으로 API 문서를 만들기 시작했었죠. 그런데 초반에 잘 알아보지 않은 탓에 얼마 지나지 않아 무료 버전의 한계를 맞닥뜨리게 되었습니다. 무료 버전에서는 여러 사람이 문서 편집을 할 수 없어 공동 작업이 더 이상 불가능하게 되어버렸거든요 🥲 + +그 다음으로 생각한 건 API 문서 자동화가 가능한 Swagger와 Spring REST Docs였습니다. 그래서 그 둘을 비교해봤습니다. + +### Swagger + + 장점 | 단점 +----------------|--------------------------- + 어노테이션 기반 문서 생성 | 프로덕션 코드에 작업 필요 + 화면에서 API 테스트 | 프로덕션 코드와 동기화 안 되어 있을 수 있음 + 비교적 쉬운 적용 | + +### Spring REST Docs + + 장점 | 단점 +-----------------|--------------- + 프로덕션 코드에 영향 없음 | 적용이 어려운 편 + 테스트 성공 시 문서 생성 | 테스트 코드 양이 늘어남 + API 문서 최신 상태 유지 | + +(제목에 드러나있듯이) 결과적으로 Spring REST Docs를 사용하기로 결정을 했습니다. 이번 프로젝트에서는 테스트 코드를 작성하기로 했었고, 프로덕션 코드에 영향이 없고 늘 현행화가 가능하다는 점에 이끌렸거든요. + +## 4. Spring REST Docs 적용기 +이제부터는 Spring REST Docs를 적용했던 과정을 하나씩 설명해보겠습니다.
+참고로 저는 Asciidoctor & MockMvc 방식을 사용했는데요. 공식 문서에 따르면 문서 작성을 위해 Asciidoctor와 Markdown을 지원하고 있고 MockMvc, WebTestClient, REST Assured 방식의 테스트 예시를 보여주고 있습니다. + +### 1) build.gradle 설정 +build.gradle에 추가되어야 하는 내용 및 설명은 아래 코드로 대체합니다. +```groovy +plugins { + // Asciidoctor 플러그인 적용 + id "org.asciidoctor.jvm.convert" version "3.3.2" +} + +ext { + // 생성된 snippets 출력 위치를 정의하는 snippetsDir 속성을 구성 + snippetsDir = file('build/generated-snippets') +} + +configurations { + // asciidoctorExt 구성을 선언 + asciidoctorExt +} + +dependencies { + // asciidoctorExt 구성에서 spring-restdocs-asciidoctor에 대한 종속성을 추가 + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' + // MockMvc 테스트 방식을 사용 + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' +} + +tasks.named('test') { + // 테스트 작업을 실행하면 출력이 snippetsDir에 기록된다는 것을 Gradle이 인식하도록 함 + outputs.dir snippetsDir +} + +asciidoctor { + // asciidoctor 작업을 구성 + dependsOn test + configurations 'asciidoctorExt' + baseDirFollowsSourceFile() + inputs.dir snippetsDir +} + +asciidoctor.doFirst { + // asciidoctor 작업이 수행될 때 가장 먼저 수행 + delete file('src/main/resources/static/docs') +} + +task copyDocument(type: Copy) { + // html 파일 복사 + dependsOn asciidoctor + from file("build/docs/asciidoc") + into file("src/main/resources/static/docs") +} + +bootJar { + dependsOn copyDocument +} +``` + +### 2) 테스트 코드 작성 +첫 번째 *테스트 코드* 외에는 구현 방식에 따라 다를 수 있으므로 가볍게 봐주시면 됩니다. +```java +/** 테스트 코드 **/ +result.andExpect(status().isOk()) + .andDo(document( + "commoncode/get-common-codes", + getDocumentRequest(), + getDocumentResponse(), + // pathParameters, queryParameters, requestFields, responseFields는 필요 시 각각 작성 + pathParameters( + parameterWithName("codeName").description("코드명") + ), + responseFields(beneathPath("value").withSubsectionId("value"), + fieldWithPath("codeNo").type(JsonFieldType.NUMBER).description("코드번호"), + fieldWithPath("codeName").type(JsonFieldType.STRING).description("코드명"), + fieldWithPath("description").type(JsonFieldType.STRING).description("설명").optional() + ) +)); +``` +```java +/** Utils 만들어 사용 **/ +public interface RestDocumentUtils { + + static OperationRequestPreprocessor getDocumentRequest() { + return preprocessRequest(modifyUris().scheme("http") + .host("spring.restdocs.test") // 실제 host 아님 + .removePort(), prettyPrint()); + } + + static OperationResponsePreprocessor getDocumentResponse() { + return preprocessResponse(prettyPrint()); + } +} +``` +```java +/** 추상 클래스 작성 **/ +@WebMvcTest({ + CommonCodeController.class, +}) +@AutoConfigureRestDocs // 통합 테스트에서 Spring REST Docs를 활성화하고 구성하는 데 사용 +public abstract class ControllerTest { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @MockBean + protected CommonCodeService commonCodeService; +} + +// 아래와 같이 상속받아 사용 +// class CommonCodeControllerRestDocsTest extends ControllerTest { +``` + +### 3) 테스트 성공 +위와 같이 작성한 테스트 코드가 통과하게 된다면 build/generated-snippets 경로 하위에 adoc 확장자 파일들이 여러 개 생성된 것을 확인할 수 있습니다. 파일을 하나씩 선택해서 보면 Asciidoc 문법에 맞게 작성된 내용과 미리보기를 확인할 수 있답니다. + +![gradle-build](https://github.com/Kernel360/blog-image/blob/main/2024/0124/gradle-build.png?raw=true) + +### 4) API 문서 틀 작성 +위에서 본 문서 조각들(snippets)을 그대로 활용할 수 있다면 좋겠지만, HTML 형태의 API 문서로 만들어 주기 위해서는 아직 추가적인 작업이 남아있습니다. 한 데 모아주는 작업을 해줘야 하는데요. 우선 저는 *index.adoc* 이라는 파일을 만들어 API별 adoc 파일을 include 하는 구조를 사용했습니다. 참고로 index.adoc 파일은 *src/docs/asciidoc 하위* 에 만들어주었는데 이 경로가 gradle을 사용할 경우의 기본 경로랍니다. + +```asciidoc +// index.adoc += API Document +// Asciidoc 문서의 구조와 스타일 정의 +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + +// adoc 파일 include +include::overview.adoc[] +include::sample-api.adoc[] +``` +```asciidoc +// sample-api.adoc +== 샘플 API + +[[공통코드-조회]] +=== 공통코드 조회 + +==== Request +include::{snippets}/commoncode/get-common-codes/path-parameters.adoc[] + +===== HTTP Request 예시 +include::{snippets}/commoncode/get-common-codes/http-request.adoc[] + +==== Response +include::{snippets}/commoncode/get-common-codes/response-fields-value.adoc[] + +===== HTTP Response 예시 +include::{snippets}/commoncode/get-common-codes/http-response.adoc[] +``` + +### 5) API 문서 생성 +이제 거의 마무리 단계인데요. gradle의 bootJar 작업을 실행시키면 build.gradle에 작성한 copyDocument 작업을 거쳐서 미리 지정한 resources/static/docs 경로 하위에 build 내에 생성되어 있던 html 파일이 복사되어 들어오고, 서버를 띄웠을 때 도메인 하위 /docs/index.html 경로로 접속해 API 문서를 확인할 수 있게 된답니다. + +![api-doc](https://github.com/Kernel360/blog-image/blob/main/2024/0124/api-doc.png?raw=true) + +### 6) API 문서 살펴보기 +최종적으로 만들어진 문서의 형태는 다음과 같습니다. 좌측에 ToC가 있어 원하는 API로의 이동이 간편하고 화면도 깔끔하지 않나요? + +![index-html](https://github.com/Kernel360/blog-image/blob/main/2024/0124/index-html.png?raw=true) + +## 5. 정리 +지금까지 저와 함께 API 문서부터 Spring REST Docs 적용기까지 살펴봤는데요. 읽기에 어떠셨을지 궁금합니다 😄 + +Spring REST Docs를 적용하면서 여러 차례 시행착오를 거치며 보낸 시간이 생각나네요. 처음 접하는 부분이 많아 공식 문서, 영상, 블로그 등을 참고했는데 그 과정에서 조금씩 이해하게 되고 원하는 결과를 만들어낼 수 있어서 개인적으로 뿌듯하고 좋았던 경험이었습니다. + +아 그리고 이번 세미나를 준비하면서 추가적인 정보를 찾다가 알게 된 건데 Swagger와 Spring REST Docs의 장점만 취해서 API 문서화를 할 수 있는 방법도 있다고 해요. 가능하다면 다음에는 이 방식을 사용하거나 시간이 될 때 변경해볼 수 있으면 좋을 것 같다는 생각이 드네요. + +## 6. 참고자료 +- [Spring REST Docs](https://docs.spring.io/spring-restdocs/docs/current/reference/htmlsingle/) +- [Spring REST Docs에 날개를… (feat: Popup)](https://techblog.woowahan.com/2678) +- [Spring REST Docs를 사용한 API 문서 자동화](https://hudi.blog/spring-rest-docs) +- [RestDocs로 API 문서화하기](https://velog.io/@junho5336/RestDocs%EB%A1%9C-API-%EB%AC%B8%EC%84%9C%ED%99%94%ED%95%98%EA%B8%B0) +- [[ 스프링 기반 REST API ] 스프링 REST Docs 소개](https://youtu.be/BFme2t9KSwA?si=ziyQ3jC1l-tQ57Md) +- [[10분 테코톡] 승팡, 케이의 REST Docs](https://youtu.be/BoVpTSsTuVQ?si=VG3mhS5b32l1EY_s) +- [[Spring] restdocs + swagger 같이 사용하기](https://velog.io/@hwsa1004/Spring-restdocs-swagger-%EA%B0%99%EC%9D%B4-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0) From 024917ed228f2871892b8e371c5a2c21a35b81b0 Mon Sep 17 00:00:00 2001 From: Younglong Date: Sun, 18 Feb 2024 18:12:30 +0900 Subject: [PATCH 3/3] fix: Web API (20231121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 실제 post에 ordered list 적용이 안 된 상태로 나와 수정함 --- _posts/2023-11-21-web-api.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/_posts/2023-11-21-web-api.md b/_posts/2023-11-21-web-api.md index 0a58b83..ab4cbca 100644 --- a/_posts/2023-11-21-web-api.md +++ b/_posts/2023-11-21-web-api.md @@ -23,8 +23,8 @@ tags: [웹 API, REST, GraphQL, WebSocket, 기술세미나] 우선 Application은 고유한 기능을 가진 모든 소프트웨어, Interface는 애플리케이션 간의 서비스 계약이라고 할 수 있습니다. 그리고 계약은 요청과 응답을 사용하여 애플리케이션이 서로 통신하는 방법이라고 할 수 있죠. 좀 더 쉬운 설명을 위해 이미지를 가져와봤습니다. 식당을 예시로 들어볼게요.
-1) 손님은 점원이 가져다준 메뉴판으로 메뉴를 주문하고, 점원이 주문을 요리사에게 전달 -2) 요리사는 주문받은 요리를 만들어 점원에게 주고, 손님은 점원이 가져다준 요리로 맛있게 식사 +1. 손님은 점원이 가져다준 메뉴판으로 메뉴를 주문하고, 점원이 주문을 요리사에게 전달 +2. 요리사는 주문받은 요리를 만들어 점원에게 주고, 손님은 점원이 가져다준 요리로 맛있게 식사 여기서 점원이 바로 API와 같은 역할을 한다고 볼 수 있습니다. @@ -45,17 +45,17 @@ tags: [웹 API, REST, GraphQL, WebSocket, 기술세미나] ### 주요 특징 REST API의 주요 특징은 다음과 같습니다. -1) **Stateless** (무상태) +1. **Stateless** (무상태) - 서버는 요청이 오가는 동안 클라이언트의 상태를 저장하지 않음 -2) **Cacheable** (캐시 가능성) +2. **Cacheable** (캐시 가능성) - 클라이언트가 응답을 캐시하여 네트워크 부하를 줄이고 성능을 향상시킬 수 있음 -3) **Uniform Interface** (통일된 인터페이스) +3. **Uniform Interface** (통일된 인터페이스) - API 디자인이 통일되어 있어 사용하기 쉬움 - 즉, 애플리케이션 요구사항별로 다른 형식이 아닌, 표준화된 형식으로 정보를 전송할 수 있도록 구성 요소 간 통합된 인터페이스를 가짐 -4) **Server-Client** (서버-클라이언트 구조) +4. **Server-Client** (서버-클라이언트 구조) - 클라이언트와 서버가 각각 독립적으로 발전할 수 있음 - 예를 들어, 웹 브라우저는 사용자에게 웹페이지를 보여주고, 서버는 데이터를 제공 -5) **Layered System** (계층화된 시스템) +5. **Layered System** (계층화된 시스템) - 시스템이 계층으로 나뉘어 있음 - 각 계층은 특정 역할을 수행하며, 상위 계층은 하위 계층의 구현을 알 필요가 없음 - 예를 들어, 클라이언트는 데이터를 요청하면 중간에 여러 계층을 거쳐 서버에 도달할 수 있지만 클라이언트는 중간 계층의 존재를 몰라도 됨 @@ -69,13 +69,13 @@ REST API의 주요 특징은 다음과 같습니다. 아래 JSON 응답을 참고해 설명해보겠습니다.
(Fetch는 *웹 페이지를 구성하기 위해서 다양한 서버에 요청을 보내고 데이터를 받아오는 작업* 이라고 생각하시면 됩니다.) -1) Over-Fetching +1. Over-Fetching - 원하는 응답 데이터 → 도서 ID, 도서명, 저자명만 필요, 그러나 해당 API Response 값에 가격, 출판사, ISBN 등이 포함되어 있다면 다 받아야 함 - 즉, Over-Fetching은 필요한 데이터 이상으로 서버에서 데이터를 받아오게 되는 것을 의미하며, 필요없는 데이터까지 받아와 서버와 자원이 낭비됨 -2) Under-Fetching +2. Under-Fetching - 도서 상세 페이지에서 도서 정보와 리뷰 목록을 보여주려고 함, 그러나 API가 도서 정보와 도서 리뷰에 대해 각각 다른 End-Point를 사용한다면 필연적으로 2번의 API 호출 발생 - 즉, 한 번의 요청으로 필요한 데이터를 모두 받아오지 못해 여러 번의 요청을 수행하는 것을 의미하며, 네트워크의 지연이 발생할 수 있고 사용자는 느린 로딩 속도로 인해 불편함을 겪을 수 있음 -3) 다양한 엔드포인트 +3. 다양한 엔드포인트 - REST API는 여러 엔드포인트가 존재하며, 각자의 역할을 하고 있으므로 클라이언트는 다양한 엔드포인트를 요청해야 함 ```json { @@ -107,13 +107,13 @@ REST API의 주요 특징은 다음과 같습니다. ### 주요 특징 GraphQL API의 주요 특징은 다음과 같습니다. -1) **유연하고 강력한 데이터 쿼리 언어** +1. **유연하고 강력한 데이터 쿼리 언어** - 클라이언트가 필요한 데이터의 구조와 양을 정확하게 명시할 수 있는 강력한 쿼리 언어를 제공하므로 과도한 데이터 전송이나 다수의 요청을 최소화할 수 있음 - 즉, REST API의 오버 페칭과 언더 페칭과 같은 이슈가 발생하지 않음 -2) **단일 엔드포인트** +2. **단일 엔드포인트** - REST API에서는 각 엔드포인트마다 데이터를 요청해야 했지만, GraphQL은 단일 엔드포인트를 사용하여 클라이언트가 단일 요청으로 여러 데이터를 가져올 수 있음 - ex) @PostMapping("/graphql") -3) **실시간 데이터 업데이트** +3. **실시간 데이터 업데이트** - 실시간 데이터 업데이트를 지원 - 일반적으로 Subscription이라 불리는 메커니즘을 통해 이루어짐 ```text @@ -156,17 +156,17 @@ query { ### 주요 특징 WebSocket API의 주요 특징은 다음과 같습니다. -1) **양방향 통신** +1. **양방향 통신** - 클라이언트-서버간 양방향 통신 가능 - 서버가 클라이언트에게 데이터를 푸시하고, 클라이언트가 서버에게 데이터를 전송할 수 있음 -2) **실시간성** +2. **실시간성** - 연결을 유지하면서 데이터를 실시간으로 전송 - 단방향 통신과는 달리, 데이터의 지연 시간을 최소화하여 실시간 응용 프로그램을 구축하는 데 적합 -3) **단일 연결 유지** +3. **단일 연결 유지** - WebSocket은 한 번의 연결을 설정하고 유지함으로써 여러 요청에 대한 새로운 연결을 맺지 않아도 됨 -4) **효율적인 데이터 전송** +4. **효율적인 데이터 전송** - 연결을 유지하면서 계속 데이터를 주고받기 때문에, 새로운 연결을 설정할 필요가 없어 헤더의 오버헤드가 감소함 -5) **이벤트 기반 모델** +5. **이벤트 기반 모델** - 이벤트 기반의 모델을 사용하여 메시지를 수신하고 처리할 수 있음 ## 5. API 비교