개발자공부일기
HTML Method 본문
HTTP 메서드
HTTP 메서드는 클라이언트가 서버에게 “무엇을 어떻게 하길 원하는지”를 표현하는 표준화된 동사다. 같은 URL이라도 메서드에 따라 의미와 서버 동작이 달라진다.
메서드 종류
- GET: 자원 조회. 본문 없이 조회 중심. 안전하고 멱등. 캐싱 우선.
- POST: 자원 생성·처리. 안전·멱등 아님. 폼 제출, 명령형 작업.
- PUT: 전체 업데이트(치환). 멱등. 전체 상태를 보내 재설정.
- PATCH: 부분 업데이트. 멱등 아님(보통 그렇지만 멱등하도록 설계 가능).
- DELETE: 자원 삭제. 멱등.
- HEAD: 헤더만 조회(본문 없음). 빠른 존재 확인·사이즈 확인.
- OPTIONS: 지원 메서드·CORS 프리플라이트 확인.
- TRACE/CONNECT: 특수 목적. 일반 API에서는 거의 사용 안 함.
GET
리소스 조회 메서드(Read).
전달할 데이터는 쿼리스트링으로 보낸다. 메시지 바디를 써서 데이터를 보낼 수도 있지만, 많은 서버/캐시/라이브러리가 지원하지 않거나 무시하므로 권장하지 않는다. 조회를 POST로 할 수도 있으나, 일반적으로 GET을 쓰는 것이 유리하다.
예시 URL
GET /members/100?username=inpa&height=200
정적 데이터 조회 과정
- 클라이언트가 100번 회원을 조회
- 서버가 내부 DB 등에서 100번 회원을 찾음
- 성공하면 200 OK와 함께 본문(JSON/HTML/이미지 등)을 돌려줌
요청 예시 (HTTP 원문)
GET /members/100 HTTP/1.1
Host: api.example.com
Accept: application/json
응답 예시
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 100, "name": "Kim", "height": 180 }
curl
curl -i "https://api.example.com/members/100"
동적 데이터 조회 과정 (검색/목록)
- 검색어나 페이징을 쿼리 파라미터로 전달
- 서버가 조건에 맞는 데이터를 조회
- 결과 목록과 함께 200 OK
요청 예시
GET /search?q=hello&page=2&size=10 HTTP/1.1
Host: api.example.com
Accept: application/json
응답 예시
HTTP/1.1 200 OK
Content-Type: application/json
{ "items": [/* ... */], "page": 2, "size": 10, "total": 134 }
curl
curl -i "https://api.example.com/search?q=hello&page=2&size=10"
HTML Form 데이터 조회 과정 (GET 전송)
- 사용자가 폼에 입력 후 전송
- 브라우저가 입력값을 쿼리스트링으로 붙여 전송
- 서버가 파라미터로 조회하고 결과 페이지 반환
요청 예시
GET /members?keyword=kim&sort=recent HTTP/1.1
Host: www.example.com
응답 예시(HTML)
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
<!doctype html>
<html>...</html>
POST
리소스 생성 또는 서버측 액션 실행(예: 결제 트리거).
전달 데이터는 요청 바디에 담는다.
생성 과정
- 클라이언트가 생성할 데이터(JSON 등)를 바디로 보냄
- 서버가 검증 후 DB에 저장
- 생성 성공 시 201 Created와 함께 새 리소스 위치(Location) 또는 생성된 데이터 반환
요청 예시
POST /posts HTTP/1.1
Host: api.example.com
Content-Type: application/json
{ "title": "HTTP Method 정리", "body": "내용..." }
응답 예시
HTTP/1.1 201 Created
Location: /posts/123
Content-Type: application/json
{ "id": 123, "title": "HTTP Method 정리", "body": "내용..." }
curl
curl -i -X POST "https://api.example.com/posts" \
-H "Content-Type: application/json" \
-d '{ "title": "HTTP Method 정리", "body": "내용..." }'
액션 실행(예: 결제 생성)도 POST로 바디에 파라미터를 실어 보낸다.
PUT
대상 리소스의 전체 상태를 “치환(교체)”한다.
부분만 보내면 나머지는 초기화될 수 있으므로, 서버/클라이언트가 “전체 상태를 보낸다”는 계약을 공유해야 한다.
전체 교체 과정
- 클라이언트가 리소스 전체 상태를 바디로 보냄
- 서버가 해당 id 리소스를 통째로 새 값으로 교체
- 보통 200 OK(교체 내용 반환) 또는 204 No Content
요청 예시
PUT /users/42/profile HTTP/1.1
Host: api.example.com
Content-Type: application/json
{ "nickname": "JavaCPP", "bio": "백엔드 dev", "avatarUrl": null }
응답 예시
HTTP/1.1 204 No Content
curl
curl -i -X PUT "https://api.example.com/users/42/profile" \
-H "Content-Type: application/json" \
-d '{ "nickname": "JavaCPP", "bio": "백엔드 dev", "avatarUrl": null }'
PATCH
대상 리소스의 “일부 필드만” 변경한다.
서버는 전달된 필드만 병합·수정하고, 나머지는 그대로 둔다.
부분 업데이트 과정
- 클라이언트가 바꾸고 싶은 필드만 보냄
- 서버가 해당 필드만 업데이트
- 보통 200 OK(변경된 내용 반환) 또는 204 No Content
요청 예시
PATCH /users/42/profile HTTP/1.1
Host: api.example.com
Content-Type: application/merge-patch+json
{ "nickname": "Youmin" }
응답 예시
HTTP/1.1 204 No Content
curl
curl -i -X PATCH "https://api.example.com/users/42/profile" \
-H "Content-Type: application/merge-patch+json" \
-d '{ "nickname": "Youmin" }'
DELETE
리소스를 삭제한다.
삭제 과정
- 클라이언트가 삭제 대상의 경로로 DELETE 요청
- 서버가 리소스를 삭제(또는 삭제 플래그 설정)
- 성공하면 보통 204 No Content
요청 예시
DELETE /posts/123 HTTP/1.1
Host: api.example.com
응답 예시
HTTP/1.1 204 No Content
curl
curl -i -X DELETE "https://api.example.com/posts/123"
HEAD
리소스의 헤더만 요청(본문 없음).
용도: 존재 확인, 용량(Content-Length), ETag 등 빠른 체크
요청 예시
HEAD /files/abc.zip HTTP/1.1
Host: cdn.example.com
응답 예시
HTTP/1.1 200 OK
Content-Type: application/zip
Content-Length: 1048576
ETag: "abc123"
curl
curl -I "https://cdn.example.com/files/abc.zip"
OPTIONS
특정 경로가 허용하는 메서드, CORS 프리플라이트 확인.
요청 예시(메서드 확인)
OPTIONS /posts HTTP/1.1
Host: api.example.com
응답 예시
HTTP/1.1 204 No Content
Allow: GET,POST,PUT,PATCH,DELETE,OPTIONS
CORS 프리플라이트 요청 예시(브라우저가 자동 전송)
OPTIONS /posts HTTP/1.1
Host: api.example.com
Origin: https://myblog.tistory.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
응답 예시
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://myblog.tistory.com
Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600
curl
curl -i -X OPTIONS "https://api.example.com/posts" \
-H "Origin: https://myblog.tistory.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization"
상태 코드와 메서드 궁합
- 200 OK: 성공(대부분의 메서드)
- 201 Created: POST로 생성 성공(응답 Location 헤더에 새 리소스 URI)
- 204 No Content: 본문 없는 성공(DELETE, PUT, PATCH 결과 등에 활용)
- 304 Not Modified: 조건부 GET/HEAD 캐시 적중
- 400/401/403/404: 클라 오류·인증·권한·리소스 없음
- 409 Conflict: 중복 생성·버전 충돌 등
- 405 Method Not Allowed: 경로는 맞지만 이 메서드는 금지, Allow 헤더로 허용 목록 전달
- 415 Unsupported Media Type: Content-Type 미지원
- 422 Unprocessable Entity: 유효성 검증 실패
CRUD 한 사이클 모음(curl)
# 생성
curl -i -X POST https://api.example.com/posts \
-H "Content-Type: application/json" \
-d '{ "title": "Hello", "body": "first" }'
# 조회
curl -i -X GET https://api.example.com/posts/1
# 부분 업데이트
curl -i -X PATCH https://api.example.com/posts/1 \
-H "Content-Type: application/merge-patch+json" \
-d '{ "title": "Hello, world!" }'
# 삭제
curl -i -X DELETE https://api.example.com/posts/1
Express(Node.js) 라우팅 스니펫(동작 흐름 주석 포함)
import express from "express";
const app = express();
app.use(express.json());
// [GET] 목록/검색: 쿼리스트링으로 조건 전달
app.get("/posts", async (req, res) => {
const { q, page = 1, size = 10 } = req.query;
// 1) 파라미터 해석 -> 2) DB 조회 -> 3) 결과 반환
res.status(200).json({ items: [], page: Number(page), size: Number(size) });
});
// [POST] 생성: 본문(JSON)으로 데이터 전달
app.post("/posts", async (req, res) => {
const { title, body } = req.body;
// 1) 유효성 검사 -> 2) DB insert -> 3) 201 + Location
const newId = 123;
res.status(201).location(`/posts/${newId}`).json({ id: newId, title, body });
});
// [GET] 단건 조회
app.get("/posts/:id", async (req, res) => {
const id = req.params.id;
// 1) DB 조회 -> 2) 없으면 404, 있으면 200
res.status(200).json({ id, title: "Hello", body: "first" });
});
// [PUT] 전체 교체
app.put("/posts/:id", async (req, res) => {
const id = req.params.id;
// 1) 전체 필드 치환 -> 2) 저장 -> 3) 204
res.sendStatus(204);
});
// [PATCH] 부분 변경
app.patch("/posts/:id", async (req, res) => {
const id = req.params.id;
// 1) 변경 필드만 반영 -> 2) 저장 -> 3) 204
res.sendStatus(204);
});
// [DELETE] 삭제
app.delete("/posts/:id", async (req, res) => {
// 1) 삭제 처리 -> 2) 204
res.sendStatus(204);
});
// [HEAD] 메타 정보만 확인
app.head("/files/:name", async (req, res) => {
// 1) 파일 존재/사이즈 확인 -> 2) 헤더만 설정하고 끝
res.set({ "Content-Length": "1048576", "Content-Type": "application/zip" });
res.end(); // 본문 없음
});
// [OPTIONS] 허용 메서드 및 CORS 프리플라이트 응답
app.options("/posts", (req, res) => {
res.set({
"Access-Control-Allow-Origin": "https://myblog.tistory.com",
"Access-Control-Allow-Methods": "GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD",
"Access-Control-Allow-Headers": "Content-Type,Authorization",
"Access-Control-Max-Age": "600",
"Allow": "GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD",
});
res.sendStatus(204);
});
app.listen(3000);
