1. REST API 기본 원칙과 설계
1. REST의 특징
- Stateless (무상태성)
- 설명
- 서버는 클라이언트의 상태를 유지하지 않음
- 각 요청은 독립적으로 처리되며, 서버는 이전 요청에 대한 정보를 기억하지 않음
- 장점
- 확장성: 서버가 상태를 유지하지 않으므로, 클라이언트의 수가 증가해도 추가적인 서버 자원이 필요하지 않음
- 단순성: 요청 간 의존성이 없으므로 구현이 간단함
- 캐싱 가능: 상태 정보를 유지하지 않으므로 동일한 요청에 대해 응답을 쉽게 캐싱할 수 있음
- 단점
- 클라이언트가 필요한 모든 정보를 요청에 포함해야 함 (예: 인증 토큰, 페이징 정보 등)
- 추가적인 데이터 전송으로 인해 네트워크 부하가 늘어날 수 있음 (예: Authorization 헤더를 통해 매번 인증 정보를 전송)
- Uniform Interface
- 설명
- 클라이언트와 서버 간의 인터페이스가 일관되도록 설계되어야 함
- REST API의 주요 원칙 중 하나로, API가 간단하고 표준화된 방식으로 동작해야 함
- 장점
- API 사용이 직관적이며, 학습 곡선이 낮음
- 클라이언트-서버 간 의사소통의 일관성 보장
- 주요 구성 요소
- Resource Identification (자원 식별):
- URI로 자원을 고유하게 식별
- 예:
/api/users/12345 (ID가 12345인 사용자 리소스)
- Resource Manipulation (자원 조작):
- HTTP 메서드 (GET, POST, PUT, DELETE)를 통해 자원을 조작
- Self-descriptive Messages (자기 설명적 메시지):
- 요청과 응답에는 필요한 모든 정보가 포함되어야 함
- Hypermedia as the Engine of Application State (HATEOAS):
- 클라이언트가 서버에서 제공하는 하이퍼링크를 통해 리소스를 탐색하기도 함\
- Hateoas를 사용하면 Client에서 "rel"의 이름으로 요청 URI를 사용하기 때문에 URI 수정이 발생하더라도 Client 사이드는 수정이 이루어질 필요가 없다
{
"account_id": 12345,
"balance": "350,000"
} // 기존의 전형적인 REST API 응답
{
"account_id": 12345,
"balance": "350,000"
"links": [
{
"rel": "self",
"href": "http://localhost:8080/accounts/12345"
},
{
"rel": "withdraw",
"href": "http://localhost:8080/accounts/12345/withdraw"
},
{
"rel": "transfer",
"href": "http://localhost:8080/accounts/12345/transfer"
}
]
} // Hateoas가 적용된 응답 (URI 정보를 응답에 추가)
- Client-Server
- 설명
- 클라이언트와 서버의 역할이 명확히 분리됨
- 클라이언트는 사용자 인터페이스와 사용자 경험을 담당하고, 서버는 데이터 관리와 비즈니스 로직을 처리
- 장점
- 독립적 개발 가능: 클라이언트와 서버가 독립적으로 개발되고 배포될 수 있음
- 확장성: 클라이언트나 서버를 각각 독립적으로 확장 가능
- 예시
- 클라이언트는 React와 같은 프론트엔드 프레임워크로 동작하고, 서버는 Springboot와 같은 백엔드 프레임워크로 구성
- Cacheable
- 설명
- 서버의 응답은 명시적으로 캐싱 가능 여부가 지정되어야 함
- 클라이언트는 캐시된 데이터를 사용할 수 있어야 하며, 이를 통해 네트워크 부하를 줄이고 성능을 개선
- HTTP 캐싱 관련 헤더
Cache-Control: 캐싱 정책을 명시 (예: no-cache, max-age)
ETag: 자원의 버전을 식별하는 태그로, 클라이언트가 변경 여부를 확인할 수 있음
Expires: 캐시의 만료 시간 설정
- 장점
- 성능 향상: 네트워크 지연 감소 및 서버 부하 감소
- 비용 절감: 불필요한 데이터 전송 감소
- 예시
GET /api/products
Cache-Control: max-age=3600
- Layered System
- 설명
- REST 아키텍처는 여러 계층으로 구성될 수 있음
- 클라이언트는 서버와 직접 통신하는지, 혹은 중간 계층(예: 로드 밸런서, 프록시, 게이트웨이)을 통해 통신하는지 알 필요가 없음
- 장점
- 보안 강화: 중간 계층에서 인증 및 권한 관리를 처리할 수 있음
- 로드 밸런싱: 중간 계층에서 부하 분산 가능
- 확장성: 클라이언트와 서버 사이에 추가 계층을 삽입해 기능 확장 가능
- 예시
- 클라이언트 → Load Balancer → API Gateway → Backend Server
- Code on Demand(Optional)
- 설명
- 서버가 클라이언트로 코드를 전송해 실행할 수 있도록 지원
- 일반적으로 클라이언트는 고정된 기능만 제공하지만, 필요에 따라 서버가 실행 가능한 코드를 제공해 클라이언트의 기능을 확장할 수 있음
- 예: Javascript 코드, JSON 스키마 제공 등
- 장점
- 클라이언트의 기능을 동적으로 확장 가능
- 유연한 클라이언트 개발 가능
- 단점
- 구현 복잡성 증가
- 보안 문제 (전달된 코드의 신뢰성 확인 필요)
- 예시
- 서버가 클라이언트에 Javascript 코드를 제공해 UI 동작을 동적으로 변경
2. REST API 설계 시 고려사항
- 엔드포인트 설계 (URI Naming 규칙: 동사 대신 명사 사용 권장)
- HTTP 메서드의 적절한 사용 (GET, POST, PUT, DELETE 등)
- GET
- 목적: 리소스 조회
- 특징
- 서버에서 데이터를 가져옴
- 안전한(Safe) 메서드: 서버의 상태를 변경하지 않음
- 멱등성(Idempotent): 여러 번 호출해도 동일한 결과를 반환
- 데이터는 URI와 쿼리 파라미터로 전달 (Body를 사용하지 않음)
- 사용 예:
GET /users : 전체 사용자 목록 조회
GET /users/12345 : 특정 사용자(12345) 조회
GET /products?page=1&size=10 : 페이지네이션으로 상품 조회
- POST
- 목적: 리소스 생성
- 특징
- 서버에 데이터를 전송하여 새로운 리소스를 생성
- 안전하지 않음: 서버의 상태를 변경
- 멱등하지 않음: 동일한 요청을 여러 번 보내면 리소스가 중복 생성될 수 있음
- 데이터는 요청 Body에 포함
- 사용 예:
POST /users
Content-Type: application/json
{ "name": "John Doe", "email": "john.doe@example.com" }
새로운 사용자 생성
- PUT
- 목적: 리소스 전체 업데이트 또는 생성(없으면 생성)
- 특징
- 리소스가 있으면 업데이트, 없으면 생성
- 멱등성: 동일한 요청을 여러 번 보내도 결과는 동일
- 데이터는 요청 Body에 포함
- 사용 예
PUT /users/12345
Content-Type: application/json
{ "name": "John Doe", "email": "john.doe@example.com" }
12345번 사용자 데이터를 완전히 덮어씀
- PATCH
- 목적: 리소스의 일부 업데이트
- 특징
- 기존 리소스의 일부만 수정
- 멱등성 보장: 동일한 요청을 여러 번 보내도 결과는 동일
- 데이터는 요청 Body에 포함
- 사용 예
PATCH /users/12345
Content-Type: application/json
{ "email": "new.email@example.com" }
12345번 사용자의 이메일만 수정
- DELETE
- 목적: 리소스 삭제
- 특징
- 서버에서 리소스를 제거
- 멱등성 보장: 동일한 요청을 여러 번 보내도 결과는 동일
- 요청 Body를 사용하지 않음
- 사용 예
DELETE /users/12345
12345번 사용자를 삭제
- HEAD, OPTIONS, TRACE 등
3. JWT와 OAuth 같은 인증 및 권한 관리
웹 애플리케이션에서 인증(Authentication)과 권한 부여(Authorization)는 보안의 핵심이다. JWT와 OAuth는 이를 구현하는 데 널리 사용되는 기술이다. 각각의 역할과 차이를 이해하면 API 설계와 보안 강화에 유용하다.
1. JWT (JSON Web Token)
개념
- JWT는 클라이언트와 서버 간의 데이터 전송에 대한 인증 벙보를 안전하게 전달하기 위해 사용되는 토큰 기반 인증 방식이다.
- Base64URL 형식으로 인코딩된 문자열로, 서명(Signature)을 포함하여 무결성을 보장
구조
- JWT는 3개의 파트로 구성
1. Header: 토큰 유형(JWT)과 서명 알고리즘(DP: HS256) ``` json
{ "alg": "HS256", "typ": "JWT" }
```
2. **Payload**: 인증 정보(예: 사용자 ID, 권한 등). JSON 형식
``` json
{ "user_id": 12345, "role": "admin", "exp": 1700000000 }
```
3. **Signature**: Header와 Payload를 결합한 후 비밀 키로 서명
``` SCSS
HWACSHA256(
base64URLEncode(header) + "." + base64UrlEncode(payload),
secret
```
최종 JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NSwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzAwMDAwMDAwfQ.-signature-
동작 원리
1. 클라이언트가 서버에 로그인을 요청하고 서버는 성공 시 JWT를 생성해 반환
2. 클라이언트는 이후 요청 시 JWT를 Authorization 헤더에 포함
3. 서버는 JWT의 서명을 검증하고, Payload를 기반으로 사용자를 식별
특징
- Stateless: 서버가 상태를 유지할 필요 없음. 클라이언트가 JWT를 유지
- 무결성 보장: 서명을 통해 토큰이 변조되지 않았음을 확인
- 만료 시간 설정: Payload에
exp 필드로 토큰 만료 시간 지정
장점
- 서버 부하 감소 (상태 유지 없음)
- 서명 검증으로 무결성 보장
단점
- 토큰 탈취 시 만료 시간 내에 재사용 가능
- Payload는 인코딩된 상태(Base64URL)로 전달되므로, 암호화되지 않음. 민감 정보는 저장하지 말아야 함
2. OAuth
개념
- OAuth는 제3자 애플리케이션에 제한된 리소스 접근 권한을 위임하기 위한 권한 부여 프레임워크이다.
- 예: Google 계정을 사용해 다른 서비스에 로그인하거나, 앱이 Google Drive 파일에 접근하도록 허용
OAuth의 주요 버전
- OAuth 1.0: 복잡한 서명 알고리즘, 현재 거의 사용되지 않음
- OAuth 2.0: 더 간단하고 효율적인 권한 부여 프로세스 제공. 현재 가장 널리 사용됨
주요 개념
1. Resource Owner: 리소스를 소유한 사용자
2. Client: 리소스에 접근하려는 애플리케이션
3. Authorization Server: 권한 부여를 처리하는 서버
4. Resource Server: 실제 리소스를 관리하는 서버
OAuth 2.0의 흐름
- 클라이언트 요청
- 사용자는 애플리케이션(Client)에 권한을 위임
- 애플리케이션은 Authorization Server에 인증 요청
- 사용자 인증 및 권한 부여
- 사용자는 Authorization Server에서 자신의 자격 증명을 입력
- 권한이 승인되면 Authorization Code를 클라이언트에 반환
- Access Token 발급
- 클라이언트는 Authorization Code를 Authorization Server에 제출하고 Access Token을 받음
- Resource Server에 요청
- 클라이언트는 Access Token을 사용해 리소스 요청
- Resource Server는 Access Token을 확인하고 리소스 제공
2. 기술 스택
1. Reverse Proxy란?
Reverse Proxy는 클라이언트의 요청을 받아 백엔드 서버로 전달하고, 백엔드 서버의 응답을 클라이언트로 반환하는 중간 계층 역할을 합니다.
클라이언트는 실제 백엔드 서버에 직접 접근하지 않고, Reverse Proxy를 통해 간접적으로 통신합니다.
Reverse Proxy 주요 기능
- 로드 밸런싱:
- 여러 백엔드 서버에 트래픽을 분산시켜 부하를 균등화.
- 백엔드 서버의 과부하 방지.
- 보안 강화:
- 클라이언트가 백엔드 서버의 IP를 알지 못하게 숨김.
- SSL/TLS 종료: HTTPS 요청을 처리하고 내부는 HTTP로 통신.
- 캐싱:
- 빈번한 요청 데이터를 캐싱하여 서버 부하 감소.
- 압축:
- 클라이언트로 전송하는 데이터를 압축해 대역폭 절약.
- HTTP 요청/응답 관리:
- URL 리다이렉션, 헤더 수정, 쿠키 관리 등.
2. API Gateway란?
API Gateway는 Reverse Proxy의 기능을 포함하면서 더 많은 역할을 수행합니다.
마이크로서비스 아키텍처에서 주로 사용되며, 클라이언트와 백엔드 서비스 간의 중간 계층 역할을 합니다.
API Gateway 주요 기능
- Reverse Proxy 기능:
- 인증 및 권한 부여:
- JWT, OAuth 등을 이용한 인증과 권한 검증.
- 데이터 집계:
- 여러 백엔드 서비스에서 데이터를 수집해 하나의 응답으로 반환.
- Rate Limiting:
- 클라이언트 요청 횟수를 제한해 DoS(서비스 거부) 공격 방지.
- API 버저닝:
- 로그 및 모니터링:
- API 호출 로그 기록 및 성능 지표 모니터링.
3. Nginx를 Reverse Proxy로 사용하는 방법
Reverse Proxy 설정
Nginx를 클라이언트와 백엔드 서버 사이에 배치해 요청을 전달합니다.
Nginx 기본 설정 예제
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}