CS/Http, Network

[매트 스터디] 3주차 HTTP 메서드 활용 & HTTP 상태코드

kth990303 2022. 5. 15. 21:12
반응형

우아한테크코스 레벨2 에서 매트가 주관한 스터디로, 인프런 김영한님의 강의 모든 개발자를 위한 HTTP 웹 기본 지식 스터디를 진행중이다. 이 포스팅에서는 스터디에 PR을 날릴 내용과 함께 스터디 시간에 얻어간 내용을 적을 예정이다.


1. HTTP 메서드 활용

클라이언트에서 서버로 데이터 전송

  •  쿼리 파라미터를 통해 전송할 때

주로 데이터를 조회할 때, 즉 GET Http method에서 쿼리 파라미터를 통해 전송하는 경우가 많다. 만약 쿼리 파라미터가 아닌 메시지 바디를 통해 데이터를 전송할 경우에도 크게 문제는 없긴 하다.

메시지 바디를 통해 데이터 전송 시 / 출처: 김영한님 강의자료

하지만 간단한 정적 데이터의 경우에도 메시지 바디를 통해 데이터를 전송하여 위처럼 난독화를 시킬 필요는 없다. 단순한 데이터 조회는 리소스 경로 혹은 쿼리파라미터에 전달하여 key-value값으로 간단하게 꺼낼 수 있도록 해주는 것이 좋을 듯하다. 또한, get 메서드일 경우 서버에서 body message 처리를 조회하지 않도록 설계됐을 수도 있으므로 리소스 조회 기능의 GET http method에서의 데이터 전송은 쿼리 파라미터를 이용해주도록 하자. 주로 조회 조건을 줄여주는 필터 (ex. query=hello), 정렬 조건 (ex. sort=ascend) 설정 등을 쿼리 파라미터로 전송한다.

 

참고로 정적 데이터의 경우는 특히 쿼리 파라미터도 필요 없이 리소스 경로만으로 조회 가능한 경우도 꽤 있다고 한다.

 

  •  메시지 바디를 통해 전송할 때

주로 GET이 아닌 POST, PUT, PATCH 등의 http method와 같이 수정, 삭제 등의 리소스 변경 작업을 할 때 이용되는 방법이다. 만약 js 코드에서 html form 데이터 전송 작업을 GET으로 설정하여 보내준다면 웹 브라우저에서 http 메시지를 생성할 때 쿼리 파라미터 형식으로 생성해준다. GET으로 http method를 지정해주고 위 작업을 해도 수행되긴 하지만, http 약속을 전혀 지키지 않는 방법이므로 그렇게 하지 말자. 참고로 Html Form 데이터 전송은 GET, POST만 지원한다고 한다. PUT, PATCH를 지원하지 않는다.

 

우리는 개발자이기 때문에 주로 Http API 데이터 (여기서는 api 응답 json 데이터를 말한다.)를 전송할 일이 많을 것이다. 이 경우 서버 -> 클라이언트(웹, 안드로이드, ios 등)이 주를 이루며, 서버 - 서버가 될 수도 있겠다. Application/json Content-Type으로 주로 보내주며, http method를 잘 설정해주어 보내주면 된다.


HTTP API 설계 예시

  • 2주차 포스팅에 언급했던 것처럼 POST와 PUT의 차이는 클라이언트가 URI를 알고 있는지에 따라 다르다. 회원등록 시에 클라이언트에서 몇 번째 URI인지 (/members/100) 알 필요 없는 것과, 회원수정 시에 클라이언트에서 몇 번째 회원을 수정할 것인지 알아야 하는 것처럼 말이다. 이처럼 서버 입장에서 데이터를 관리하는 공간을 Collection, 클라이언트 입장에서 데이터를 관리하는 공간을 Store라고 한다.
  • HTML form 데이터 전송은 GET, POST만 사용 가능하다. 회원 등록을 예시로 들 경우, 등록 폼을 조회하는 것은 GET으로, 등록 폼을 통해 회원을 등록시키는 것은 POST로 하면 된다. 이 때 URI는 GET: '/members/new', POST: '/members/new' || '/members' 둘 다 가능하다. 하지만 주로 GET과 POST를 관련 기능일 경우 같은 URI로 처리하는 것이 편하다고 한다. 같은 URI로 처리해야 POST 작업 시 validate 작업에서 올바르지 않게 입력했을 경우에, 별도의 경로 처리를 할 필요 없이 다시 등록폼으로 되돌아가게 할 수 있기 때문이다.  
  • HTML form 데이터 전송은 GET, POST만 사용 가능하기 때문에 URI로 명확히 지정해주기 위해 동사형을 사용하기도 한다. ex. /members/new, /members/edit 등의 컨트롤 URI. 실무에선 실제로 굉장히 많이 사용한다고 한다. 최대한 명사형으로 작성해보고, 힘들 때에만 컨트롤 URI를 사용할 수 있도록 하자. 

2. HTTP 상태코드

우리가 http 요청을 하면 제대로 처리가 되었는지 http 응답을 받을 수 있다.

실제로 504 gateway time-out, 404 not found와 같은 에러 코드는 개발자가 아닌 일반인들도 굉장히 많이 발견했을 것이다.

이번 시간엔 이 status code들을 정리해보려 한다.

 

http status code에 익숙해지면 아래 사이트에 들어가서 자신이 잘 알고 있는지, 그림에 공감되는지 확인해봐도 좋을 듯하다.

https://http.cat/

 

HTTP Cats

API for HTTP Cats

http.cat

귀여운 고양이들로 HTTP status code들을 표시해준 사이트이다.


성공 - 2xx

실제로 200번대 status code들은 컨트롤러 코드를 작성할 때 많이 작성해봤을 것이다. ResponseEntity.ok().body(body)와 같이 200(OK)를 의도한 응답을 많이 보내기 때문이다. 가장 많이 쓰이는 상태코드이며, 성공했다는 것을 보내주는 가장 평범하고 좋은 status code이다. 리소스 등록 시에 201(Created)도 꽤 쓰인다. 새롭게 리소스가 생성되었다는 걸 알려주기 위해 201을 보내주면 좋다. ResponseEntity.created().body(body)와 같이 쓰인다. 204(No content) 또한 많이 사용된다. 리소스를 등록하거나 삭제할 때, 성공했다는 내용만 필요하고 딱히 별도의 메시지가 필요 없으면 ResponseEntity.noContent().build()도 많이 사용되기 때문이다.

 

202(Accepted)는 해당 요청이 접수됐으나 처리가 완료되지 않았을 때 보내주는 status code이다. 데이터를 모아두었다가, 특정 시점에 한꺼번에 처리하는 배치 처리 작업에서 많이 쓰이는 상태코드이다. 많이 쓰이지는 않는다고 한다. 

 

사실 200(OK)만 사용하거나, 200(OK), 201(Created)만 사용하는 곳들도 꽤 많다고 한다. 너무 많이 사용한다고 해서 반드시 좋은 것은 아니다. 협업하는 개발자들끼리 적절하게 규칙을 정해보도록 하자. 


리다이렉션 - 3xx

300번대 status code들도 꽤 자주 쓰인다. 이 상태코드에서 응답 결과에 Location 헤더가 있으면, 웹 브라우저는 해당 Location 위치로 자동 이동한다. 300(Multiple Choices)는 두 개 이상의 응답이 주어져 클라이언트에서 선택해야 하는 경우를 의미한다. 선택하는 표준화된 방법이 없어 이 상태코드는 거의, 진짜 거의 쓰이지 않는다고 한다. 사실상 301 ~ 308 status code들이 자주 쓰인다고 한다.

 

만약 예전에 생성해둔 URI인 '/event'가 더 이상 쓰이지 않고, '/event-new'의 uri가 새롭게 생성되어 여기로 이동시키고 싶다면 301(move permanently)를 사용하면 된다. 완전히 영구적으로 해당 Location의 URI로 리다이렉션 시켜버리는 것이다. 만약 일시적으로 잠깐 이동시키는 경우에서의 리다이렉션을 사용하고 싶다면 이 status code는 해당되지 않는다. 참고로 308(Permanent Redirect)도 동일한 기능을 한다. 그러면 301과 308은 어떤 차이점이 있을까?

  • 301(Move Permanently): 리다이렉트 시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있음.
  • 308(Permanent Redirect): 리다이렉트 시 요청 메서드와 본문 유지

하지만 실무에서 308은 거의 사용되지 않는다고 한다. 새로운 URI로 개편이 됐다는 것 자체가, 요구하는 요청 메시지가 바꼈을 가능성이 농후하기 때문에 실제론 등록폼을 띄우기 전에 301을 사용하여 현재 사용되는 uri로 리다이렉션 시킨다고 한다.

 

일시적인 리다이렉션에는 302(Found), 307(Temporary Redirect), 303(See Other)이 존재한다. 예시로, 결제화면에서 결제가 완료된 경우에 다시 주문내역 및 결제완료 화면으로 리다이렉션할 때 이 status code들이 쓰인다. 이 셋의 차이점은 아래와 같다.

  • 302(Found): 리다이렉트 시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있음.
  • 307(Temporary Redirect): 리다이렉트 시 요청 메서드와 본문 유지. 메서드가 변하면 안됨!
  • 303(See Other): 리다이렉트 시 요청 메서드가 GET으로 변경돼야 할 때!

사실상 302가 모호해서 307, 303이 나왔지만, 많은 곳에서 아직 302를 사용하고 있고, 실제로 크게 문제가 없는 상황이라 아직까지도 유지중이라 한다.

 

 일시적인 리다이렉션을 사용함으로써, 요청이 중복돼서 들어가는 것(POST - 200으로만 할 경우 새로고침하면 다시 POST - 200)을 방지할 수 있다. 리다이렉션이 필요한 이유기도 하다. 이러한 패턴을 PRG(Post/Redirect/Get)라고 한다. POST 처리가 완료되면 새로고침과 같은 작업을 수행할 경우 GET으로 조회하도록 하는 것이다. (ex. 주문완료(POST) - 리다이렉션(302 Found, Location: 주문완료폼 URI) - 주문완료창 폼 (GET)) 이렇게 해 주면 완료창 조회만 GET을 하게 하므로, 중복 POST 요청이 들어가지 않는다.

 그렇지만, 이 302만을 의지하고 서버 측에서 중복 요청의 경우 예외처리를 해놓지 않으면 큰일난다. 302 응답을 받기 전에 클라이언트에서 새로고침 등의 중복 요청을 보낼 수도 있기 때문이다. 따라서 http 예외처리 뿐만 아니라 서버 측에서도 철저한 예외처리를 해두어야 한다.

 

기타 리다이렉션 중 하나로 304(Not modified)가 존재한다. 주로 조건부 GET이나 HEAD에서 많이 쓰인다. 실무에서도 꽤나 쓰이는 status code라 하며, 캐시를 목적으로 사용하는 상태코드이다. 클라이언트에게 리소스가 수정되지 않았음을 알려주며, 캐시로 리다이렉트하기 때문에 응답 메시지 바디를 포함하지 않고 로컬 캐시를 사용하게 한다.


클라이언트 오류 - 4xx

400번대 오류는 클라이언트 오류이기 때문에, 잘못된 요청을 보내고 있을 때 발생한다. 따라서 500번대 서버 오류와 달리, 똑같은 요청을 보내더라도 재실패하게 된다.

 

400(Bad Request)를 만난다면 API 스펙에 맞지 않는 요청을 보냈을 확률이 높다.

401(Unauthorized)는 해당 리소스를 접속하기에 인증이 되지 않았다는 상태코드이다. 여기서 인증과 인가의 차이점을 알고 가면 좋은데, 인증(Authentication)은 본인이 누구인지 몰라 확인하는 작업을 의미하고, 인가(Authorization)은 인증은 됐지만 권한이 충분한지 확인하는 작업을 의미한다. 401은 인증이 되지 않았을 때임을 명심하자. 인증은 됐지만 접근 권한이 없을 땐 주로 403(Forbidden)이 많이 쓰인다

 

404(Not Found)는 요청 리소스를 찾을 수 없을 때 의미한다. 요청 측에서 존재하지 않는 리소스를 조회하려 한 것이므로 (또는 숨기고 싶은 리소스) 클라이언트 측 오류를 의미한다.

 

그 외에 수많은 상태코드들이 존재한다. 실제로 개발하다보면 405, 406, 415 등등 다양하게 만나긴 하지만, 가장 많이 보게되는 status code들을 언급한 것이다. 다른 status code 예시들은 아래 링크를 확인해보자.

https://kth990303.tistory.com/304

https://kth990303.tistory.com/321

https://kth990303.tistory.com/320

 

[ERROR] 406 에러 _ Not Acceptable (스프링 직렬화/역직렬화)

문제 상황 스프링 환경에서 특정 dto 를 반환해주는 api에서 406 에러가 발생했다. @GetMapping("/status") public ResponseEntity status() { return ResponseEntity.ok(chessService.getStatus()); } 뿐만 아니..

kth990303.tistory.com


서버 오류 - 5xx

500번대 오류는 서버 오류이므로, 서버에서 복구가 될 경우 똑같은 요청을 보내더라도 성공할 수 있다.

 

주로 500(internal server error)가 많이 쓰인다. 서버가 과부하걸리거나, 예정된 작업으로 사용되지 못하는 경우엔 503(Service Unavailable)가 쓰이기도 한다.

 

참고로 웬만해선 500 에러를 내면 안된다. 정말 서버 측에서 문제가 잘못됐을 때를 제외하곤, 정상 프로세스 상에서의 요청에 대한 예외처리를 해주는 400번대 에러를 내주는 것이 옳다고 한다.


400번대, 500번대 에러에 대해서 어느정도 알고 있다고 생각했지만, 맨 마지막 부분의 `요청에 대한 예외처리는 400번대 에러를 내준다`는 부분을 듣고 다시 한 번 깨달음을 얻게 됐다.

 

우테코에서 체스 미션을 수행할 때, 해당 체스말이 이동하지 못하는 위치로 요청이 들어올 경우를 처리해주는 경우가 생각이 났다.

해당 예시의 경우, 요청이 잘못된 것이므로 400번대 에러가 적절한 것이었는데, 그 때는 서버에서의 IllegalArgumentException이라 생각해서 500번대 에러를 보내주곤 했다.

 

이런 걸 보면 확실히 개발은 알고리즘, 프레임워크의 능숙함 뿐만 아니라 cs 공부도 철저하게 하는 것이 중요한 듯하다.

반응형