html
적합한 API 디자인 선택하기: Richardson Maturity Model 이해하기
목차
- 소개
- Richardson Maturity Model 개요
- 레벨 0: RESTful API가 아님
- 레벨 1: 여러 URI와 단일 동사
- 레벨 2: 여러 URI와 여러 동사
- 레벨 3: URI, HTTP 메서드 및 HATEOAS
- 비교표: Richardson 성숙도 레벨
- 실용적인 예제: 레벨 2 RESTful API 구현
- 결론
소개
웹 개발의 진화하는 환경에서, 효과적이고 효율적인 API(Application Programming Interfaces) 디자인은 매우 중요합니다. 초보 개발자이든 기본적인 지식을 가진 사람이든, API 디자인의 원칙을 이해하는 것은 애플리케이션의 성능과 사용성을 크게 향상시킬 수 있습니다. API의 성숙도와 정교함을 설명하는 프레임워크 중 하나는 Richardson Maturity Model입니다. 이 eBook은 이 모델의 복잡성을 탐구하며, 기능적일 뿐만 아니라 확장 가능하고 유지 관리가 용이한 API를 설계하는 데 도움이 되는 포괄적인 가이드를 제공합니다.
Richardson Maturity Model 개요
Richardson Maturity Model이란?
Richardson Maturity Model은 Leonard Richardson에 의해 소개된, REST(Representational State Transfer) 원칙 준수에 따라 API를 분류하는 프레임워크입니다. 이 모델은 레벨 0(가장 낮은 성숙도)에서 레벨 3(가장 높은 성숙도)까지의 네 가지 API 성숙도 레벨을 설명합니다. 각 레벨은 이전 레벨을 기반으로 구축되며, API의 효율성, 확장성 및 사용성을 향상시키기 위해 보다 많은 RESTful 기능을 통합합니다.
API 성숙도의 중요성
더 높은 수준의 API 성숙도를 달성하면 API가 견고하고 확장 가능하며 유지 관리하기 쉬워집니다. 성숙한 API는 표준화된 프로토콜과 최선의 실천 방법을 준수하여 개발자가 통합하고 활용하기 쉽게 만듭니다. 또한, 성숙한 API는 현대 웹 및 모바일 애플리케이션에 필수적인 성능, 보안 및 유연성을 향상시키는 데 기여합니다.
레벨 0: RESTful API가 아님
레벨 0의 특성
- Single URI: 모든 작업을 위한 단일 엔드포인트.
- Single HTTP Method: 종종 POST와 같은 하나의 메서드에 제한됨.
- SOAP-Based Payloads: XML payloads와 함께 SOAP(Simple Object Access Protocol)을 사용.
- Plain Old XML (POX): HTTP 의미를 활용하지 않고 데이터 교환을 위해 XML에 의존.
Example:
1 |
http://Showroom/manage |
모든 작업이 요청 본문에 포함되어 리소스나 작업의 명확한 구분이 부족합니다.
장단점
- Pros:
- 매우 기본적인 API에 대해 구현이 간단함.
- 유연성이 중요하지 않은 내부 또는 엄격하게 통제된 환경에 적합.
- Cons:
- 확장성과 유연성이 제한됨.
- API가 복잡해짐에 따라 관리가 어려워짐.
- 명확한 리소스-작업 분리가 없어 조직이 불량함.
레벨 0을 사용할 때
레벨 0 API는 전체 RESTful 관행을 구현하는 오버헤드가 불필요한 단순한 내부 애플리케이션에 가장 적합합니다. 고유한 제한 사항 때문에 공개 또는 대규모 애플리케이션에는 권장되지 않습니다.
레벨 1: 여러 URI와 단일 동사
레벨 1의 특성
- Multiple URIs: 각 리소스가 고유한 엔드포인트를 가짐 (예: /cars, /brands, /employees).
- Single HTTP Verb: 보통 POST와 같은 하나의 메서드를 사용하여 작업을 수행.
Example:
1 2 3 |
POST /cars POST /brands POST /employees |
장단점
- Pros:
- 고유한 URI로 인해 레벨 0에 비해 더 나은 조직 구조.
- 리소스가 분리되어 있어 약간 더 관리가 용이함.
- Cons:
- 단일 HTTP 동사에 의존하기 때문에 기능이 제한됨.
- 다양한 작업을 위한 HTTP 메서드의 전체 잠재력을 활용하지 못함.
레벨 1을 사용할 때
레벨 1 API는 여러 리소스를 관리해야 하지만 여전히 복잡성이 낮은 시나리오에 적합합니다. 리소스를 분리하여 레벨 0보다 약간 개선되었지만 전체 RESTful 기능에는 미치지 못합니다.
레벨 2: 여러 URI와 여러 동사
레벨 2의 특성
- Multiple URIs: 각 리소스에 대해 고유한 엔드포인트가 있음.
- Multiple HTTP Verbs: GET, POST, PUT, DELETE와 같은 다양한 HTTP 메서드를 사용.
- CRUD Operations: 생성(Create), 조회(Read), 수정(Update), 삭제(Delete) 작업에 적절한 HTTP 메서드를 사용하여 구현됨.
Example:
1 2 3 4 |
GET /cars POST /cars PUT /cars/{id} DELETE /cars/{id} |
장단점
- Pros:
- HTTP 메서드의 완전한 활용으로 명확성과 기능성이 향상됨.
- RESTful 최선의 실천 방법과 일치하여 API가 직관적이고 표준화됨.
- 확장성과 유지 관리가 용이함.
- Cons:
- RESTful 원칙에 대한 더 포괄적인 이해가 필요함.
- 레벨 1에 비해 구현이 약간 더 복잡함.
레벨 2를 사용할 때
레벨 2는 견고한 API 기능이 필요한 대부분의 현대 애플리케이션에 이상적입니다. 복잡성과 사용성 사이의 균형을 이루어 공개 및 엔터프라이즈 수준의 애플리케이션 모두에 적합합니다.
레벨 3: URI, HTTP 메서드 및 HATEOAS
레벨 3의 특성
- URIs: 각 리소스를 위한 전용 엔드포인트.
- HTTP Methods: 적절하게 사용되는 HTTP 동사의 전체 스펙트럼.
- HATEOAS: 클라이언트가 가능한 다음 작업을 안내하는 하이퍼미디어 링크를 포함한 응답.
Example:
1 |
GET /cars/{id} |
Response:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "id": 1, "model": "Tesla Model S", "links": [ { "rel": "self", "href": "/cars/1" }, { "rel": "update", "href": "/cars/1" }, { "rel": "delete", "href": "/cars/1" } ] } |
장단점
- Pros:
- RESTful 원칙을 최대한 활용하여 API가 매우 직관적임.
- 하이퍼미디어 링크를 통해 검색 가능성과 탐색 용이성이 향상됨.
- 클라이언트-서버 분리가 개선되어 각각 독립적으로 진화할 수 있음.
- Cons:
- HATEOAS를 통합하기 때문에 구현이 더 복잡함.
- 응답 payload에 추가 오버헤드를 초래할 수 있음.
레벨 3을 사용할 때
레벨 3은 검색 가능성과 통합 용이성이 중요한 공개 API에 가장 적합합니다. 유연성과 확장성을 가장 높은 수준으로 제공하여 광범위한 성장과 발전을 예상하는 애플리케이션에 이상적입니다.
비교표: Richardson 성숙도 레벨
성숙도 레벨 | URIs | HTTP Methods | HATEOAS | Example |
---|---|---|---|---|
Level 0 | Single URI | Single Method (POST) | No | http://Showroom/manage |
Level 1 | Multiple URIs | Single Method (POST) | No | /cars, /brands, /employees |
Level 2 | Multiple URIs | Multiple Methods (GET, POST, PUT, DELETE) | No | /cars, /cars/{id} |
Level 3 | Multiple URIs | Multiple Methods (GET, POST, PUT, DELETE) | Yes | /cars/{id} with hypermedia links |
실용적인 예제: 레벨 2 RESTful API 구현
Richardson Maturity Model의 레벨 2를 설명하기 위해, 자동차 컬렉션을 관리하는 간단한 RESTful API를 구현해 보겠습니다.
샘플 코드
Sample Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
const express = require('express'); const app = express(); const port = 3000; app.use(express.json()); let cars = [ { id: 1, model: 'Tesla Model S' }, { id: 2, model: 'Ford Mustang' } ]; // GET all cars app.get('/cars', (req, res) => { res.json(cars); }); // GET a specific car by ID app.get('/cars/:id', (req, res) => { const car = cars.find(c => c.id === parseInt(req.params.id)); if (!car) return res.status(404).send('Car not found'); res.json(car); }); // POST a new car app.post('/cars', (req, res) => { const newCar = { id: cars.length + 1, model: req.body.model }; cars.push(newCar); res.status(201).json(newCar); }); // PUT update a car app.put('/cars/:id', (req, res) => { const car = cars.find(c => c.id === parseInt(req.params.id)); if (!car) return res.status(404).send('Car not found'); car.model = req.body.model; res.json(car); }); // DELETE a car app.delete('/cars/:id', (req, res) => { const carIndex = cars.findIndex(c => c.id === parseInt(req.params.id)); if (carIndex === -1) return res.status(404).send('Car not found'); const deletedCar = cars.splice(carIndex, 1); res.json(deletedCar); }); app.listen(port, () => { console.log(`API listening at http://localhost:${port}`); }); |
구문 설명
- Express.js: 최소적이고 유연한 Node.js 웹 애플리케이션 프레임워크.
- app.get('/cars', ...): 모든 자동차에 대한 GET 요청을 처리하는 라우트를 정의.
- app.post('/cars', ...): 새로운 자동차를 추가하기 위한 POST 요청을 처리하는 라우트를 정의.
- app.put('/cars/:id', ...): 기존 자동차를 업데이트하기 위한 PUT 요청을 처리하는 라우트를 정의.
- app.delete('/cars/:id', ...): 자동차를 삭제하기 위한 DELETE 요청을 처리하는 라우트를 정의.
단계별 코드 설명
- 설정:
- Express.js를 초기화하고 포트를 3000으로 설정.
- express.json() 미들웨어를 사용하여 JSON 본문을 파싱.
- 데이터 저장소:
- 자동차 객체를 id와 model로 저장할 인메모리 배열 cars를 생성.
- GET /cars:
- 모든 자동차의 전체 목록을 JSON 형식으로 반환.
- GET /cars/:id:
- 특정 id로 자동차를 검색.
- 자동차를 찾을 수 없는 경우 404 오류를 반환.
- POST /cars:
- 목록에 새로운 자동차를 추가.
- model이 포함된 JSON 본문을 기대.
- 새로 생성된 자동차를 201 상태 코드와 함께 반환.
- PUT /cars/:id:
- 기존 자동차의 모델을 업데이트.
- 자동차를 찾을 수 없는 경우 404 오류를 반환.
- 업데이트된 자동차 객체를 반환.
- DELETE /cars/:id:
- id에 따라 목록에서 자동차를 제거.
- 자동차를 찾을 수 없는 경우 404 오류를 반환.
- 삭제된 자동차 객체를 반환.
- 서버 시작:
- API가 지정된 포트에서 수신을 시작하고 확인 메시지를 로그에 기록.
Output Example:
GET /cars:
1 2 3 4 |
[ { "id": 1, "model": "Tesla Model S" }, { "id": 2, "model": "Ford Mustang" } ] |
POST /cars with body { "model": "Chevrolet Camaro" }:
1 2 3 4 |
{ "id": 3, "model": "Chevrolet Camaro" } |
결론
Richardson Maturity Model은 API 디자인을 평가하고 향상시키기 위한 명확한 프레임워크를 제공합니다. 네 가지 레벨을 이해하고 적용함으로써 API의 기능성, 확장성 및 사용자 친화성을 체계적으로 향상시킬 수 있습니다. 단순한 내부 서비스이든 포괄적인 공개 API이든, 더 높은 성숙도 레벨을 지향하면 최선의 실천 방법을 준수하여 더 나은 통합과 전반적인 성능을 보장할 수 있습니다.