2 minute read

1. SOP (Same-Origin Policy, 동일 출처 정책)

SOP는 웹 브라우저에서 실행되는 스크립트가 다른 출처(origin)의 리소스에 접근하는 것을 제한하는 정책입니다.

출처(Origin)의 정의

출처는 다음 세 가지 요소의 조합으로 정의됩니다:

  • 프로토콜(http, https)
  • 호스트(도메인)
  • 포트 번호

예를 들어, https://example.com:443에서는

- 프로토콜: https
- 호스트: example.com
- 포트: 443

SOP의 필요성

SOP는 악의적인 스크립트가 사용자의 민감한 정보에 접근하는 것을 방지합니다. 이 정책이 없다면, 악성 웹사이트는 사용자가 로그인된 다른 웹사이트의 데이터를 쉽게 탈취할 수 있습니다.

개발자는 API를 설계할 때 SOP의 제약을 고려해서 클라이언트와 서버가 다른 출처에 있는 경우(예: 프론트엔드는 example.com, API는 api.example.com), CORS를 구현하여 리소스 접근을 허용해야 합니다.


2. CORS (Cross-Origin Resource Sharing, 교차 출처 리소스 공유)

CORS는 SOP의 제한을 우회하여 다른 출처 간의 리소스 공유를 안전하게 허용하는 HTTP 헤더 기반의 메커니즘입니다. 서버 개발자는 CORS 설정을 통해 필요한 클라이언트에게만 API 접근을 허용합니다.

CORS 작동 원리

단순 요청(Simple Requests)

다음 조건을 모두 만족하는 요청:

  • GET, HEAD, POST 메서드 중 하나
  • Content-Type이 특정 값으로 제한(application/x-www-form-urlencoded, multipart/form-data, text/plain)
  • 특별한 헤더를 포함하지 않음

단순 요청의 경우, 브라우저는 직접 요청을 보내고 서버는 응답에 Access-Control-Allow-Origin 헤더를 포함시켜 접근 허용 여부를 알립니다.

프리플라이트 요청(Preflight Requests)

단순 요청이 아닌 경우, 브라우저는 본 요청 전에 OPTIONS 메서드를 사용한 프리플라이트 요청을 먼저 보냅니다. 이를 통해 서버에 실제 요청이 안전한지 확인합니다.

서버는 다음과 같은 헤더로 응답합니다.

  • Access-Control-Allow-Origin: 허용된 출처
  • Access-Control-Allow-Methods: 허용된 HTTP 메서드
  • Access-Control-Allow-Headers: 허용된 헤더
  • Access-Control-Max-Age: 프리플라이트 응답 캐시 시간

CORS 구현하기

// Spring Boot에서 CORS 설정 예시
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://frontend.example.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("Authorization", "Content-Type")
            .allowCredentials(true)
            .maxAge(3600);
    }
}

주요 CORS 헤더

  1. Access-Control-Allow-Origin: 접근을 허용할 출처를 지정
  2. Access-Control-Allow-Methods: 허용할 HTTP 메서드 지정
  3. Access-Control-Allow-Headers: 허용할 요청 헤더 지정
  4. Access-Control-Allow-Credentials: 인증 정보(쿠키, HTTP 인증) 포함 허용 여부
  5. Access-Control-Max-Age: 프리플라이트 요청 결과 캐시 시간(초)
  6. Access-Control-Expose-Headers: 브라우저에 노출할 응답 헤더 지정


3. RESTful API

REST(Representational State Transfer)는 웹 서비스를 설계하는 아키텍처 스타일로, RESTful API는 이 원칙을 따르는 API입니다.

REST 아키텍처의 주요 원칙

1. 자원(Resource) 중심 설계

  • URI는 자원을 나타내며, HTTP 메서드는 자원에 대한 작업을 나타냅니다.
  • 예: /users는 사용자 자원을 나타냅니다.

2. 상태 없음(Statelessness)

  • 각 요청은 독립적이며 서버는 클라이언트의 상태를 저장하지 않습니다.
  • 모든 요청은 해당 요청을 이해하는데 필요한 모든 정보를 포함해야 합니다.

3. 캐시 가능성(Cacheability)

  • 응답은 캐시 가능 여부를 명시해야 합니다.
  • Cache-Control, Expires 등의 헤더를 사용합니다.

4. 계층화 시스템(Layered System)

  • 클라이언트는 서버와 직접 통신하는지, 중간 서버와 통신하는지 알 수 없습니다.
  • 로드 밸런싱, 캐싱, 보안 계층을 추가할 수 있습니다.

5. 통일된 인터페이스(Uniform Interface)

  • 자원 식별: URI로 자원을 식별
  • 표현을 통한 자원 조작: JSON, XML 등
  • 자기 서술적 메시지: Content-Type 등의 헤더 사용
  • HATEOAS(Hypermedia as the Engine of Application State): 응답에 관련 자원 링크 포함

RESTful API 설계 모범 사례

1. 적절한 HTTP 메서드 사용

  • GET: 자원 조회
  • POST: 자원 생성
  • PUT: 자원 전체 수정
  • PATCH: 자원 부분 수정
  • DELETE: 자원 삭제

2. 명확한 URI 설계

  • 명사를 사용하여 자원 표현: /users, /products
  • 계층 관계 표현: /users/{id}/orders
  • 복수형 명사 사용: /users(단수형보다 일관성 있음)
  • 동사 대신 리소스와 HTTP 메서드로 행동 표현
// 좋은 예
GET /users         // 사용자 목록 조회
POST /users        // 사용자 생성
GET /users/{id}    // 특정 사용자 조회
PUT /users/{id}    // 사용자 정보 전체 수정
PATCH /users/{id}  // 사용자 정보 일부 수정
DELETE /users/{id} // 사용자 삭제

// 나쁜 예
GET /getUsers
POST /createUser
GET /deleteUser/{id}

3. 적절한 상태 코드 사용

  • 200: 성공
  • 201: 생성 성공
  • 204: 성공했지만 반환할 콘텐츠 없음
  • 400: 잘못된 요청
  • 401: 인증 필요
  • 403: 권한 없음
  • 404: 자원을 찾을 수 없음
  • 500: 서버 오류

4. 버전 관리

  • URI에 버전 포함: /api/v1/users
  • 헤더에 버전 지정: Accept: application/vnd.company.v1+json

5. HATEOAS 구현

응답에 관련 리소스에 대한 링크를 포함하여 클라이언트가 API를 탐색할 수 있도록 합니다.

{
  "id": 123,
  "name": "홍길동",
  "links": [
    {"rel": "self", "href": "/users/123"},
    {"rel": "orders", "href": "/users/123/orders"}
  ]
}

Leave a comment