웹 개발을 하다 보면 HTTP 메서드를 언제 무엇을 써야 할지 고민될 때가 많다. 단순히 데이터를 보내는 용도라면 POST 하나로 다 해결할 수 있을 것 같지만, 실제로는 목적에 따라 엄격하게 구분해서 사용한다. 왜 이렇게 나누어 놓았는지, 그리고 각 메서드의 차이점이 무엇인지 정리해 본다.
1. HTTP 메서드별 역할 (CRUD)
가장 기본적으로 HTTP 메서드는 데이터베이스의 CRUD 작업과 매칭된다.
| 메서드 | 역할 (CRUD) | 멱등성 | 설명 |
| GET | Read | Yes | 리소스 조회 |
| POST | Create | No | 리소스 생성 및 복잡한 연산 |
| PUT | Update | Yes | 리소스 전체 수정 (덮어쓰기) |
| PATCH | Update | No | 리소스 부분 수정 |
| DELETE | Delete | Yes | 리소스 삭제 |
멱등성(Idempotency)이란?
동일한 요청을 여러 번 수행해도 결과(서버의 상태)가 똑같은 성질을 의미한다. 네트워크 오류로 인해 클라이언트가 요청을 재시도해야 할 때 매우 중요한 기준이 된다.
2. 왜 굳이 나누어서 사용하는가?
단순히 "데이터 전송"만 생각하면 비효율적이라고 느낄 수 있지만, 명확하게 구분했을 때 얻는 이점이 더 크다.
첫째, 시맨틱(Semantics) 즉, 의미의 명확성 때문이다.
코드나 로그만 봐도 이 요청이 조회를 위한 것인지, 삭제를 위한 것인지 한눈에 알 수 있다. 협업 과정에서 API의 의도를 파악하는 시간을 획기적으로 줄여준다.
둘째, 캐싱을 통한 성능 최적화다.
GET 요청은 서버의 상태를 바꾸지 않기 때문에 브라우저나 프록시 서버에서 결과를 미리 저장(캐싱)해둘 수 있다. 반면 데이터가 변하는 POST나 PUT은 캐싱하지 않아 데이터의 무결성을 지킨다.
셋째, 네트워크 안정성이다.
멱등성이 보장되는 GET, PUT, DELETE는 네트워크 장애 시 자동으로 재시도(Retry) 로직을 타게 설계할 수 있지만, POST는 중복 생성 위험 때문에 재시도가 조심스럽다.
3. 부분 수정은 어떻게 할까? (PUT vs PATCH)
데이터를 수정할 때 흔히 하는 실수가 무조건 PUT을 사용하는 것이다. 하지만 둘은 확실히 다르다.
- PUT (전체 수정): 리소스의 모든 필드를 보내야 한다. 만약 일부 필드만 보낸다면, 서버에서는 보내지 않은 나머지 필드를 Null이나 기본값으로 덮어버릴 위험이 있다. 즉, 리소스를 통째로 갈아 끼울 때 쓴다.
- PATCH (부분 수정): 수정하고 싶은 데이터(예: 비밀번호, 닉네임)만 Body에 담아 보낸다. 자원을 효율적으로 사용하고 부수 효과를 줄일 수 있다.
4. GET으로 DELETE를 처리하면 안 되는 이유
편의상 GET /user/delete?id=1 처럼 설계하는 경우가 간혹 있는데, 이는 매우 위험한 안티 패턴이다.
- 검색 엔진 크롤러의 위협: 구글이나 네이버 같은 크롤러는 웹상의 모든 GET 링크를 자동으로 타고 들어간다. 만약 삭제 로직이 GET으로 되어 있다면, 크롤러가 지나가는 것만으로 DB의 데이터가 전부 삭제되는 대참사가 날 수 있다.
- 보안 및 기록: GET 요청은 모든 파라미터가 URL에 노출된다. 서버 로그나 브라우저 히스토리에 삭제 관련 정보가 남기 때문에 보안상 취약하다.
- 캐싱 문제: 삭제 요청인데 브라우저가 "이미 처리된 결과가 있네?" 하고 캐싱된 데이터를 보여주면, 실제 서버에서는 삭제가 안 되었음에도 클라이언트는 삭제된 것으로 착각할 수 있다.
5. 메서드별 캐싱(Caching) 메커니즘 정리
| 메서드 | 캐싱 가능 | 비고 |
| GET | Yes (기본) | 대부분의 HTTP 캐싱은 GET 요청을 대상으로 함. |
| HEAD | Yes | 응답 바디 없이 헤더 정보만 가져오므로 캐싱 가능. |
| POST | Yes (조건부) | 캐시 관련 헤더(Expires 등)가 명시적일 때만 가능하나, 실무에선 거의 쓰지 않음. |
| PUT/PATCH/DELETE | No | 리소스를 변경하므로 캐싱하지 않음. 기존 캐시를 무효화함. |
5-1. 왜 특정 메서드만 캐싱하는가?
GET, HEAD가 캐싱되는 이유
- 안전성(Safe): 이 메서드들은 서버의 데이터를 변경하지 않습니다. 즉, 언제 호출해도 같은 결과(리소스)를 받을 가능성이 높기 때문에 복사본을 저장해 두었다가 꺼내 쓰는 것이 효율적입니다.
- 성능 최적화: 웹 페이지의 이미지, CSS, JS 파일 등은 모두 GET으로 호출됩니다. 이를 매번 서버에서 새로 가져오지 않고 캐시에서 불러와야 로딩 속도가 비약적으로 빨라집니다.
POST가 캐싱되기 어려운 이유
- POST는 호출할 때마다 서버에 새로운 리소스를 생성하거나 처리를 요청합니다.
- "결제하기" 요청을 캐싱해서 재사용한다면, 사용자는 한 번 클릭했지만 서버는 여러 번 결제된 것으로 오해할 수 있는 위험이 있습니다. 따라서 기본적으로는 캐싱하지 않는 것이 원칙입니다.
5-2.수정/삭제 메서드와 캐시의 관계 (Cache Invalidation)
PUT, PATCH, DELETE 메서드는 캐싱이 되지 않을 뿐만 아니라, 기존에 저장된 캐시를 삭제(무효화) 하는 중요한 역할을 합니다.
- 동작 원리:
- 브라우저가 GET /user/1 요청을 보내서 사용자 정보를 캐싱함.
- 이후 PATCH /user/1 요청(이름 수정)을 보냄.
- 브라우저와 프록시 서버는 "아, 리소스가 변했구나"라고 판단하여 기존에 저장되어 있던 GET /user/1의 캐시 데이터를 즉시 삭제함.
- 이를 통해 클라이언트가 수정된 이후에도 과거의 잘못된(Stale) 데이터를 보는 것을 방지합니다.
5-3. 캐싱 제어 방법
실제 개발 시에는 단순히 메서드 구분뿐만 아니라 응답 헤더를 통해 캐싱을 더 세밀하게 제어합니다.
주요 캐시 제어 헤더
Cache-Control: max-age=3600 (1시간 동안 캐시 유지)
Cache-Control: no-store (절대 캐싱 금지 - 민감한 정보)
Cache-Control: no-cache (캐시는 하되, 사용 전에 서버에 확인 요청)
요약 및 마무리
결국 HTTP 메서드를 나누는 이유는 "약속"을 지키기 위함이다.
- 조회는 GET
- 생성은 POST
- 전체 교체는 PUT, 부분 수정은 PATCH
- 삭제는 DELETE
이 원칙(RESTful)만 잘 지켜도 훨씬 유지보수하기 쉽고 안정적인 서비스를 만들 수 있다.