포스트

HTTP

HTTP 웹 기본 지식 6: 폼 전송에서 리소스 중심 API 설계까지

데이터 전송 방식과 HTML 폼의 제약을 이해하고 컬렉션·스토어·업무 동작의 URI를 설계한다.

시리즈 HTTP 웹 기본 지식 10편 중 6편 HTTP 웹 기본 지식
  1. 1 HTTP 웹 기본 지식 1: 웹 프레임워크 앞에서 확인할 것들
  2. 2 HTTP 웹 기본 지식 2: IP, TCP, 포트, DNS의 역할 나누기
  3. 3 HTTP 웹 기본 지식 3: URL 한 줄에서 요청과 응답까지
  4. 4 HTTP 웹 기본 지식 4: 무상태, 연결 재사용, 메시지 구조
  5. 5 HTTP 웹 기본 지식 5: 메서드의 의미와 재시도 판단
  6. 6 HTTP 웹 기본 지식 6: 폼 전송에서 리소스 중심 API 설계까지
  7. 7 HTTP 웹 기본 지식 7: 상태 코드가 클라이언트의 다음 행동을 정한다
  8. 8 HTTP 웹 기본 지식 8: 표현과 협상, 인증과 쿠키를 헤더로 연결하기
  9. 9 HTTP 웹 기본 지식 9: 캐시의 저장, 신선도, 재검증을 구분하기
  10. 10 HTTP 웹 기본 지식 10: 배운 개념을 작은 API의 설계 기준으로 바꾸기

이 글은 김영한님의 모든 개발자를 위한 HTTP 웹 기본 지식 강의를 학습한 내용을 정리한 글입니다.

학습성과

메서드의 정의를 알아도 화면과 API를 설계하면 선택지가 남는다. 검색 조건은 어디에 넣는지, 파일은 어떻게 전송하는지, 생성할 자원의 주소는 누가 정하는지 같은 문제다. 이 섹션은 데이터 전송 방식과 API 설계를 함께 다룬다.

전송할 데이터와 요청 의미를 함께 본다

상황일반적인 구성설계 시 확인할 점
정적 이미지 조회GET + 경로리소스 주소와 캐시 정책
검색 결과 조회GET + 질의검색 조건과 페이지 기준
HTML 폼 제출GET 또는 POST브라우저의 기본 폼 동작
파일과 텍스트 제출POST + multipart각 파트의 타입·용량·검증
프로그램 간 API메서드 + 헤더 + 본문명시적인 스키마와 오류 계약

GET과 POST의 차이를 암호화 여부로 설명하면 안 된다. 질의 문자열을 쓰는지 본문을 쓰는지는 표현 방식이고, 전송 구간의 보호는 HTTPS 등의 문제다. 본문이라고 해서 자동으로 민감정보가 보호되지는 않는다.

기본 HTML 폼이 만드는 요청

다음은 실행 결과가 아니라 브라우저의 전송 형태를 설명하는 예시다.

1
2
3
4
<form action="/books" method="get">
  <input name="keyword" value="network">
  <button type="submit">검색</button>
</form>

이 폼은 검색 조건을 URL의 질의 부분으로 전달한다. 실제 문자가 포함되면 인코딩이 적용된다.

1
2
3
GET /books?keyword=network HTTP/1.1
Host: catalog.example

일반적인 POST 폼은 application/x-www-form-urlencoded 형식으로 본문에 값을 담는다. 파일을 함께 보낼 때는 다음처럼 multipart/form-data를 사용한다.

1
2
3
4
5
<form action="/covers" method="post" enctype="multipart/form-data">
  <input name="bookId" value="42">
  <input name="image" type="file">
  <button type="submit">표지 업로드</button>
</form>

multipart는 각 파트를 경계 문자열로 나누고 텍스트와 파일을 함께 표현한다. 파트별로 이름과 콘텐츠 정보가 있으므로 서버는 각각을 해석할 수 있다. 클라이언트가 보낸 파일 이름과 콘텐츠 타입만 믿고 저장하는 것은 별도 보안 문제를 만든다. 이 글의 전송 예시가 파일 업로드 검증 전체를 대신하지는 않는다.

기본 HTML 폼의 HTTP 전송은 GET과 POST를 중심으로 동작한다. 자바스크립트 API 호출에서는 PUT·PATCH·DELETE를 사용할 수 있지만, 이를 기본 폼 동작과 혼동해서는 안 된다.

생성할 URI를 누가 정하는가

도서 등록은 서버가 ID를 발급하는 방식으로 설계할 수 있다.

sequenceDiagram
    participant C as 클라이언트
    participant S as 서버
    C->>S: POST /books + 도서 정보
    S->>S: 새 ID 42 할당
    S-->>C: 201 Created, Location: /books/42

클라이언트는 등록 전에 /books/42라는 주소를 몰라도 된다. 이처럼 서버가 관리하는 컬렉션에 생성 요청을 보낼 수 있다.

반면 파일 저장 API에서 클라이언트가 /covers/42.png라는 대상을 정한다면 PUT으로 해당 위치의 표현을 생성하거나 대체하는 모델이 자연스러울 수 있다. 이를 스토어 관점으로 볼 수 있다. 차이는 파일인지 JSON인지보다 대상 URI를 누가 결정하는가에 있다.

모델생성 대상 결정예시
컬렉션서버가 새 항목의 URI 결정POST /books
스토어클라이언트가 대상 URI 지정PUT /covers/42.png
개별 문서이미 식별된 하나의 대상GET /books/42

이 분류는 API를 설명하는 설계 어휘다. HTTP가 모든 서비스에 같은 도메인 모델을 강제하는 것은 아니다.

화면 주소와 처리 주소는 다를 수 있다

서버 렌더링 화면에서는 입력 폼을 여는 요청과 실제 데이터를 변경하는 요청을 구별한다.

1
2
3
4
GET  /books/new          등록 화면
POST /books              등록 처리
GET  /books/42/edit      수정 화면
POST /books/42/edit      폼 기반 수정 처리

이 구성은 기본 폼의 제약을 받아들인 예시다. JSON API의 PATCH 엔드포인트와 화면용 POST 엔드포인트를 반드시 같게 만들 필요는 없다. 대신 동일한 업무 규칙을 서로 다르게 구현하지 않도록 내부 책임을 나눠야 한다.

CRUD로 설명되지 않는 작업

“출판 승인”에는 권한 확인, 현재 상태 검사, 이력 기록 같은 규칙이 들어갈 수 있다. 단순히 status 필드를 바꾸는 수정과 같은 계약인지 먼저 판단해야 한다.

1
2
3
4
POST /books/42/publish HTTP/1.1
Host: catalog.example
Content-Length: 0

이런 업무 동작을 명시하는 URI도 선택지다. 먼저 리소스 모델로 설명할 수 있는지 검토하되, 모든 동작을 억지로 CRUD 네 가지에 숨길 필요는 없다. 위 요청의 재실행이 허용되는지, 이미 출판된 책이면 어떤 응답을 주는지도 함께 정해야 한다.

API 검토에서는 “동사가 URI에 있느냐”보다 “클라이언트가 결과와 재시도를 예측할 수 있느냐”를 함께 보는 편이 생산적이다. 리소스 이름만 정하고 실패 계약을 비워 두면 사용하기 좋은 API가 되기 어렵다.

참고

이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다