HTTP 메서드는 CRUD에 대응하는 단어가 아니다

vx_developer·4일 전

개발하다가

목록 보기
26/30
post-thumbnail

프로그래밍을 처음 배울 때 HTTP 메서드는 보통 CRUD 작업과 연결해서 배운다.

GET    // 조회
POST   // 생성
PUT    // 수정
DELETE // 삭제

웹 클라이언트가 서버에 어떤 작업을 요청하는지 구분하는 기본 개념을 이해하기에는 충분한 설명이다.

하지만 실제 서비스를 개발하면 단순히 데이터베이스 작업을 고르는 것보다 더 많은 판단이 필요하다.

  • 이 요청은 서버의 상태를 변경하는가?
  • 같은 요청이 두 번 전달되어도 결과가 같은가?
  • 네트워크 오류가 발생했을 때 안전하게 다시 요청할 수 있는가?
  • 전체 데이터를 교체하는가, 일부만 변경하는가?
  • 사용자의 의도가 생성인가, 취소인가, 결제인가?
  • 요청이 성공했지만 응답을 받지 못했다면 어떻게 해야 하는가?

HTTP 메서드는 서버 내부에서 실행할 CRUD 작업을 알려주는 명령어가 아니다. 클라이언트가 요청의 의도와 성질을 서버와 중간 시스템에 전달하는 공통 언어다.

실제 서비스에서 HTTP 메서드는 CRUD에 대응하는 단어가 아니라 요청의 의도와 안전성, 재실행 가능성을 전달하는 방법이다.


같은 조회처럼 보여도 서버 상태를 바꾸면 GET이 아니다

크리스가 예약 가능한 시간을 확인한다고 생각해 보자.

const response = await fetch(
  "/api/reservation-slots?date=2026-09-20"
);

const slots = await response.json();

이 요청은 예약 가능 시간을 읽는다. 요청을 여러 번 보내더라도 예약 상태는 달라지지 않는다.

이러한 작업에는 GET이 자연스럽다.

그러나 다음처럼 GET 요청으로 예약을 확정할 수도 있다.

await fetch(
  "/api/reservations/confirm?id=reservation_42"
);

URL에 confirm이 들어 있으므로 의도는 읽을 수 있다. 하지만 HTTP 관점에서 GET은 서버 상태를 변경하지 않는 안전한 메서드로 취급된다.

브라우저, 검색 엔진, 프록시와 사전 로딩 기능은 사용자의 명시적인 클릭 없이 GET 요청을 실행할 수 있다. 캐시가 이전 응답을 재사용할 수도 있다.

따라서 상태를 변경하는 작업을 GET으로 표현하면 요청의 실제 성질과 HTTP 메서드가 전달하는 의미가 달라진다.

await fetch(
  "/api/reservations/reservation_42/confirm",
  {
    method: "POST",
  }
);

이제 요청은 서버 상태를 변경하는 행동이라는 사실을 명시한다.

중요한 기준은 URL의 동사가 아니다.

요청이 서버가 기억하는 사실을 변경하는지가 먼저다.


HTTP 메서드는 데이터베이스 쿼리를 원격으로 선택하지 않는다

예약 생성 기능을 다음처럼 이해하기 쉽다.

POST   /api/reservations // INSERT
GET    /api/reservations // SELECT
PUT    /api/reservations // UPDATE
DELETE /api/reservations // DELETE

이 대응 관계는 입문 단계에서 HTTP와 데이터베이스의 연결을 파악하는 데 도움이 된다.

그러나 실제 서비스의 행동은 하나의 CRUD 작업으로 끝나지 않을 수 있다.

예약 확정에는 다음 작업이 함께 포함될 수 있다.

  • 선택한 시간이 여전히 비어 있는지 확인한다.
  • 현재 사용자에게 예약 권한이 있는지 확인한다.
  • 예약을 저장한다.
  • 해당 시간의 남은 인원을 감소시킨다.
  • 결제 대기 시간을 생성한다.
  • 확인 알림을 발송할 작업을 등록한다.
async function confirmReservation(
  reservationId: string,
  currentUserId: string
) {
  const reservation =
    await reservationRepository.findById(
      reservationId
    );

  if (!reservation) {
    return {
      ok: false,
      code: "RESERVATION_NOT_FOUND",
    };
  }

  if (!reservation.canBeConfirmedBy(currentUserId)) {
    return {
      ok: false,
      code: "RESERVATION_CANNOT_BE_CONFIRMED",
    };
  }

  const confirmedReservation =
    reservation.confirm({
      confirmedAt: new Date(),
    });

  await reservationRepository.save(
    confirmedReservation
  );

  return {
    ok: true,
    reservation: confirmedReservation,
  };
}

외부에서는 하나의 POST 요청이지만 내부에서는 조회, 검증, 변경과 저장이 함께 일어난다.

HTTP 메서드는 어떤 SQL 쿼리를 실행할지 정하지 않는다. 외부 소비자가 시스템에 어떤 종류의 행동을 요청하는지 표현한다.


안전한 요청은 반복해도 서버의 사실을 바꾸지 않는다

HTTP에서 안전한 메서드란 보안상 안전하다는 뜻이 아니다.

요청의 본래 목적이 서버 상태를 변경하지 않는다는 뜻이다. 대표적으로 GETHEAD가 여기에 해당한다.

const reservation = await fetch(
  "/api/reservations/reservation_42"
).then((response) => response.json());

이 요청을 반복하더라도 예약이 생성되거나 취소되어서는 안 된다.

물론 서버는 다음과 같은 부수적인 기록을 남길 수 있다.

  • 접근 로그
  • 성능 지표
  • 조회 횟수 통계
  • 보안 감사 기록

이러한 기록이 생긴다고 해서 GET의 본래 의도가 상태 변경으로 바뀌는 것은 아니다.

반면 다음 요청은 예약 취소라는 명시적인 변경을 요구한다.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "DELETE",
  }
);

안전성은 클라이언트와 서버뿐 아니라 브라우저, 캐시, 프록시와 모니터링 도구에도 중요한 정보다.

중간 시스템은 메서드의 의미를 근거로 요청을 미리 실행하거나 캐시할 수 있는지 판단하기 때문이다.


멱등성은 같은 요청을 다시 보내도 최종 상태가 같다는 뜻이다

네트워크 요청에는 결과를 확실히 알 수 없는 순간이 생긴다.

크리스가 예약을 취소한 직후 연결이 끊겼다고 생각해 보자.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "DELETE",
  }
);

서버가 취소를 완료한 뒤 응답만 전달하지 못했을 수도 있다. 반대로 서버가 요청을 받기 전에 연결이 끊겼을 수도 있다.

클라이언트는 요청이 실행되었는지 알 수 없다.

이때 같은 DELETE 요청을 다시 보내더라도 예약의 최종 상태는 계속 취소 상태다.

async function cancelReservation(
  reservationId: string
) {
  const reservation =
    await reservationRepository.findById(
      reservationId
    );

  if (!reservation) {
    return {
      ok: false,
      code: "RESERVATION_NOT_FOUND",
    };
  }

  if (reservation.status === "cancelled") {
    return {
      ok: true,
      reservation,
    };
  }

  const cancelledReservation =
    reservation.cancel({
      cancelledAt: new Date(),
    });

  await reservationRepository.save(
    cancelledReservation
  );

  return {
    ok: true,
    reservation: cancelledReservation,
  };
}

첫 번째 요청이 예약을 취소하고, 두 번째 요청은 이미 취소된 상태를 확인한다.

두 요청의 응답이 완전히 같아야 한다는 뜻은 아니다. 핵심은 동일한 요청을 한 번 실행했을 때와 여러 번 실행했을 때 서버의 최종 상태가 같다는 점이다.

이러한 성질을 멱등성이라고 한다.

일반적으로 PUTDELETE는 멱등성을 갖도록 설계하며, GET과 같은 안전한 메서드도 멱등적이다.

그러나 메서드 이름만으로 멱등성이 자동으로 만들어지지는 않는다. 서버 구현이 그 의미를 지켜야 한다.


POST는 생성 전용이 아니라 멱등성이 보장되지 않는 처리를 표현한다

POST는 흔히 데이터 생성 메서드라고 배운다.

await fetch("/api/reservations", {
  method: "POST",
  body: JSON.stringify({
    slotId: "slot_10am",
  }),
});

실제로 새로운 예약을 생성할 때 자주 사용한다. 하지만 POST의 의미를 INSERT로 제한하면 충분하지 않다.

다음 요청들도 POST로 표현할 수 있다.

POST /api/reservations/{id}/confirm
POST /api/reservations/{id}/send-reminder
POST /api/reservations/{id}/request-refund

각 요청은 단순한 행 생성이 아니라 서비스가 제공하는 행동을 실행한다.

POST 요청은 일반적으로 같은 요청을 반복했을 때 동일한 결과가 보장되지 않는다.

예약 생성 요청을 두 번 보내면 예약도 두 개 만들어질 수 있다.

async function createReservation(
  slotId: string,
  currentUserId: string
) {
  return reservationRepository.create({
    slotId,
    userId: currentUserId,
  });
}

네트워크 오류 때문에 클라이언트가 같은 요청을 다시 보내면 중복 예약이 발생할 수 있다.

따라서 중요한 POST 작업에는 메서드 선택만으로 부족한 별도의 중복 방지 전략이 필요하다.


중요한 POST 요청에는 멱등성 키가 필요할 수 있다

크리스가 예약 확정 버튼을 한 번 눌렀지만 응답이 늦어 다시 눌렀다고 생각해 보자.

await fetch("/api/reservations", {
  method: "POST",
  body: JSON.stringify({
    slotId: "slot_10am",
  }),
});

각 요청을 새로운 생성 명령으로 처리하면 같은 예약이 중복 생성될 수 있다.

클라이언트가 작업마다 고유한 멱등성 키를 만들고 서버가 처리 결과를 보관하는 방법을 사용할 수 있다.

const idempotencyKey = crypto.randomUUID();

await fetch("/api/reservations", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({
    slotId: "slot_10am",
  }),
});

서버는 같은 사용자와 같은 키로 들어온 요청이 이미 처리되었는지 확인한다.

async function createReservation(
  input: CreateReservationInput,
  currentUserId: string,
  idempotencyKey: string
) {
  const previousResult =
    await idempotencyRepository.find({
      userId: currentUserId,
      key: idempotencyKey,
    });

  if (previousResult) {
    return previousResult.response;
  }

  const reservation =
    await reservationService.create({
      ...input,
      userId: currentUserId,
    });

  const response = {
    ok: true,
    reservation,
  };

  await idempotencyRepository.save({
    userId: currentUserId,
    key: idempotencyKey,
    response,
  });

  return response;
}

같은 키의 재요청은 새로운 예약을 만들지 않고 이전 결과를 반환한다.

멱등성 키가 특히 중요한 작업은 다음과 같다.

  • 결제 승인
  • 주문 생성
  • 예약 확정
  • 쿠폰 사용
  • 포인트 지급
  • 외부 시스템에 작업 전달

키만 받는다고 문제가 모두 해결되는 것은 아니다.

서버는 키의 유효 범위, 보관 기간, 사용자와의 관계, 같은 키로 다른 본문이 들어왔을 때의 처리 방법도 정해야 한다.


PUT과 PATCH의 차이는 수정량보다 요청의 의미에 있다

PUT은 전체 수정, PATCH는 일부 수정이라고 간단히 구분한다.

이 설명은 출발점으로는 유용하지만 단순히 필드 개수로 두 메서드를 나누면 경계가 모호해진다.

크리스가 예약의 변경 가능한 표현 전체를 교체한다고 생각해 보자.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      slotId: "slot_11am",
      guestCount: 2,
      note: "Window seat",
    }),
  }
);

PUT은 지정한 리소스를 요청 본문의 표현으로 교체한다는 의미에 가깝다.

같은 요청을 반복해도 slotId, guestCountnote의 최종 상태는 같다.

반면 메모만 변경한다면 PATCH가 의도를 더 정확히 표현할 수 있다.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "PATCH",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      note: "Near the entrance",
    }),
  }
);

PATCH는 리소스 일부에 적용할 변경을 전달한다.

그러나 PATCH라고 해서 항상 멱등적인 것은 아니다.

다음 요청은 실행할 때마다 인원수를 증가시킨다.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "PATCH",
    body: JSON.stringify({
      incrementGuestCount: 1,
    }),
  }
);

같은 요청을 두 번 보내면 결과가 달라진다.

반대로 최종값을 전달하면 반복 요청에도 같은 상태를 만들 수 있다.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "PATCH",
    body: JSON.stringify({
      guestCount: 3,
    }),
  }
);

따라서 PUTPATCH를 선택할 때는 필드 개수만 세지 않는다.

요청이 전체 표현을 교체하는지, 변경 사항을 적용하는지, 재전송되었을 때 어떤 결과가 생기는지를 함께 판단해야 한다.


DELETE는 데이터베이스 행을 물리적으로 제거하라는 명령이 아니다

예약 취소를 다음과 같이 요청할 수 있다.

await fetch(
  "/api/reservations/reservation_42",
  {
    method: "DELETE",
  }
);

이 요청이 반드시 데이터베이스에서 예약 행을 삭제해야 한다는 뜻은 아니다.

서비스는 기록 보존이나 환불 처리를 위해 상태만 변경할 수 있다.

async function cancelReservation(
  reservationId: string
) {
  return reservationRepository.update(
    reservationId,
    {
      status: "cancelled",
      cancelledAt: new Date(),
    }
  );
}

데이터베이스에는 예약이 남아 있지만 활성 예약으로는 더 이상 사용할 수 없다.

HTTP에서 DELETE는 소비자가 해당 리소스를 현재의 기능적 관계에서 제거하려는 의도를 표현한다. 내부에서 소프트 삭제, 상태 전이 또는 물리 삭제 중 무엇을 사용할지는 서비스 규칙이 결정한다.

다만 취소와 삭제가 사용자에게 다른 의미라면 별도의 행동으로 표현하는 편이 명확할 수 있다.

POST   /api/reservations/{id}/cancel
DELETE /api/reservations/{id}

첫 번째 요청은 업무 상태를 취소로 전환한다. 두 번째 요청은 사용자가 관리하는 임시 예약 자체를 제거할 수 있다.

메서드 하나만으로 모든 도메인 의미를 표현하려 하지 않아야 한다.


메서드의 의미와 서버의 검증은 서로 다른 책임이다

POST를 사용했다고 해서 잘못된 예약 생성이 차단되는 것은 아니다.

외부 입력은 검증 전까지 신뢰할 수 없다.

type CreateReservationRequest = {
  slotId: string;
  guestCount: number;
};

TypeScript 타입은 개발 중 구조를 설명하지만 실제 네트워크 요청을 검증하지는 않는다.

서버 경계에서 런타임 검증이 필요하다.

const result =
  createReservationSchema.safeParse(
    await request.json()
  );

if (!result.success) {
  return Response.json(
    {
      ok: false,
      code: "INVALID_RESERVATION_INPUT",
      fields:
        result.error.flatten().fieldErrors,
    },
    {
      status: 400,
    }
  );
}

입력 형식이 유효하더라도 최신 예약 가능 여부는 서버에서 다시 확인해야 한다.

const slot =
  await reservationSlotRepository.findById(
    result.data.slotId
  );

if (!slot?.isAvailable()) {
  return Response.json(
    {
      ok: false,
      code: "SLOT_NOT_AVAILABLE",
    },
    {
      status: 409,
    }
  );
}

클라이언트 화면에 표시된 예약 가능 상태는 시간이 지난 복사본일 수 있다.

현재 슬롯 상태와 수용 인원은 서버 데이터가 Source of Truth다.

HTTP 메서드는 요청의 성질을 표현하고, 입력 검증과 도메인 로직은 그 요청을 실제로 허용할 수 있는지 판단한다.


메서드 선택은 재시도 정책에 영향을 준다

네트워크 오류가 발생하면 모든 요청을 같은 방식으로 다시 보내기 쉽다.

async function requestWithRetry(
  url: string,
  options: RequestInit
) {
  try {
    return await fetch(url, options);
  } catch {
    return fetch(url, options);
  }
}

이 코드는 조회 요청에는 사용할 수 있어 보이지만 모든 변경 요청에 안전하지는 않다.

예약 생성 POST가 서버에서 이미 처리된 뒤 응답만 사라졌다면 재시도로 중복 예약이 만들어질 수 있다.

재시도 정책은 다음 정보를 고려해야 한다.

요청 성질예시자동 재시도 판단
안전하고 멱등적예약 목록 GET일시적 네트워크 오류에 재시도 가능
상태를 변경하지만 멱등적예약 취소 DELETE구현이 멱등성을 지키면 재시도 가능
멱등성 키가 있는 작업예약 생성 POST같은 키를 유지할 때 재시도 가능
중복 실행 위험이 있는 작업알림 발송 POST결과 확인 없이 자동 재시도하면 위험
현재 상태에 의존하는 변경인원수 증가 PATCH최신 상태와 중복 여부 확인 필요

메서드만 보고 자동 재시도를 결정해서도 안 된다.

서버 구현, 멱등성 키, 오류 종류와 업무 영향까지 함께 고려해야 한다.


메서드는 요청 흐름 전체에 같은 의미를 전달한다

예약 생성 요청은 여러 계층을 통과한다.

flowchart LR
    A[예약 화면] --> B[HTTP 요청]
    B --> C[브라우저·네트워크]
    C --> D[API 경계]
    D --> E[예약 도메인 로직]
    E --> F[데이터베이스]
    D --> G[HTTP 응답]
    G --> A

각 계층은 HTTP 메서드에서 요청의 성질에 관한 단서를 얻는다.

  • 프론트엔드는 조회와 변경 요청을 구분한다.
  • 브라우저와 캐시는 안전한 요청의 응답을 재사용할 수 있다.
  • 서버 라우터는 요청을 적절한 처리기로 연결한다.
  • 재시도 로직은 멱등성 여부를 고려한다.
  • 로그와 모니터링은 어떤 종류의 행동이 실패했는지 구분한다.
  • 보안 계층은 상태 변경 요청에 추가 보호가 필요한지 판단한다.

따라서 메서드 선택은 라우터를 동작시키기 위한 형식적인 설정이 아니다.

서로 다른 시스템이 요청을 일관되게 해석하도록 돕는 설계 결정이다.


HTTP 메서드를 선택하기 전에 물어봐야 할 질문

요청의 의도가 명확한가

  1. 이 요청은 정보를 읽는가, 서버가 기억하는 사실을 변경하는가?
  2. URL과 메서드가 함께 사용자의 행동을 설명하는가?
  3. 내부 데이터베이스 작업이 아니라 서비스의 기능을 표현하는가?
  4. 생성, 확정, 취소와 환불처럼 서로 다른 업무 행동이 구분되어 있는가?
  5. 하나의 요청이 하나의 일관된 의도를 담고 있는가?

안전성과 멱등성을 판단했는가

  1. 요청을 여러 번 실행해도 서버 상태가 바뀌지 않아야 하는가?
  2. 같은 요청을 반복했을 때 최종 상태가 같은가?
  3. 응답을 받지 못했을 때 클라이언트가 안전하게 재시도할 수 있는가?
  4. 중복 생성이나 중복 결제를 막기 위한 멱등성 키가 필요한가?
  5. 서버 구현이 선택한 메서드의 의미를 실제로 지키는가?

변경 범위와 결과가 분명한가

  1. 전체 리소스 표현을 교체하는가, 일부 변경을 적용하는가?
  2. 변경값이 최종 상태를 나타내는가, 현재 값에 연산을 수행하는가?
  3. DELETE가 물리 삭제, 소프트 삭제와 업무 취소 중 무엇을 의미하는가?
  4. 최신 서버 상태를 기준으로 변경 가능 여부를 다시 확인하는가?
  5. 변경 후 서버가 Source of Truth가 되는 결과를 반환하는가?

재시도와 실패를 설계했는가

  1. 네트워크 오류가 실행 실패를 의미한다고 잘못 가정하고 있지는 않은가?
  2. 타임아웃 후 작업 결과를 다시 조회할 방법이 있는가?
  3. 자동 재시도가 중복 작업을 만들 가능성이 있는가?
  4. 같은 멱등성 키가 재시도 중 유지되는가?
  5. 충돌, 잘못된 입력과 일시적 장애를 구분할 수 있는가?

외부 입력과 권한을 확인하는가

  1. 요청 본문을 런타임 스키마로 검증하는가?
  2. 사용자 신원을 요청 본문이 아니라 세션이나 토큰에서 가져오는가?
  3. 현재 사용자가 해당 예약에 행동할 권한이 있는가?
  4. 화면에 표시된 상태가 아니라 최신 서버 상태를 확인하는가?
  5. 메서드 선택만으로 보안과 검증이 해결된다고 생각하지 않는가?

흔한 실수는 HTTP 메서드를 장식으로 만든다

GET으로 서버 상태를 변경한다

GET /api/reservations/confirm?id=reservation_42

사전 로딩, 캐시와 자동 요청으로 사용자의 의도 없이 상태가 변경될 수 있다.

상태를 변경하는 행동에는 그 성질을 나타내는 메서드를 사용한다.


모든 생성과 행동을 POST 하나로 처리한다

POST /api/reservations/action

본문의 action 값만 보고 생성, 취소, 환불과 확정을 모두 처리하면 기능의 경계와 권한을 이해하기 어려워진다.

POST /api/reservations
POST /api/reservations/{id}/confirm
POST /api/reservations/{id}/request-refund

서로 다른 업무 행동을 명확한 기능 경계로 분리한다.


POST 요청을 무조건 자동 재시도한다

return fetch(url, options).catch(() =>
  fetch(url, options)
);

첫 요청이 이미 처리되었다면 예약이나 결제가 중복될 수 있다.

업무 영향이 큰 요청에는 결과 조회 방법이나 멱등성 키를 설계한다.


PUT과 PATCH를 필드 개수로만 구분한다

// 필드가 하나이므로 항상 PATCH라고 판단한다.
PATCH /api/reservations/{id}

중요한 기준은 필드 개수가 아니라 전체 표현 교체인지, 변경 명령인지와 반복 실행의 결과다.


DELETE를 데이터베이스 행 삭제와 동일시한다

await database.reservation.delete({
  where: {
    id: reservationId,
  },
});

예약 기록이 환불, 감사와 고객 지원에 필요할 수 있다.

HTTP 의도와 내부 보관 정책을 구분한다.


메서드를 올바르게 선택하면 검증도 끝났다고 생각한다

POST /api/reservations

올바른 메서드도 유효하지 않은 입력, 권한 없는 사용자와 이미 마감된 시간을 차단하지 않는다.

입력 검증, 인증, 권한과 도메인 규칙을 서버 경계에서 별도로 적용한다.


HTTP 메서드의 핵심은 요청의 성질을 공유하는 데 있다

HTTP 메서드를 처음 배울 때는 다음 대응만으로도 요청 구조를 이해할 수 있다.

GET = 조회
POST = 생성
PUT = 수정
DELETE = 삭제

그러나 실제 서비스를 설계할 때는 더 많은 질문에 답해야 한다.

  • 요청은 서버 상태를 변경하는가?
  • 여러 번 실행해도 안전한가?
  • 같은 요청의 최종 결과가 항상 같은가?
  • 네트워크 실패 후 자동으로 재시도할 수 있는가?
  • 전체 표현을 교체하는가, 변경 사항을 적용하는가?
  • 중복 생성과 중복 결제를 어떻게 막을 것인가?
  • HTTP 의도와 내부 데이터 저장 방식을 어떻게 분리할 것인가?
  • 서버가 최신 데이터와 권한을 다시 검증하는가?

GET은 읽기 SQL을 선택하는 명령이 아니다. 상태를 변경하지 않는 조회라는 약속이다.

POST는 생성 전용 명령이 아니다. 새로운 처리나 행동을 요청하며, 반복 실행의 결과가 같지 않을 수 있음을 나타낸다.

PUT, PATCHDELETE도 특정 데이터베이스 쿼리를 직접 의미하지 않는다. 리소스를 어떻게 변경하려는지와 요청을 다시 실행했을 때 어떤 결과를 기대할 수 있는지를 전달한다.

결국 HTTP 메서드를 선택한다는 것은 다음 질문에 답하는 일이다.

이 요청은 시스템에 어떤 행동을 요구하며, 같은 요청이 다시 전달되었을 때 시스템은 어떻게 반응해야 하는가?

HTTP 메서드는 CRUD에 대응하는 단어가 아니다.

클라이언트, 서버와 중간 시스템이 요청의 의도와 안전성, 재실행 가능성을 함께 이해하도록 만드는 공통 언어다.

profile
Vision eXperience Developer

0개의 댓글