[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

2017년 1월 10일 화요일

[JAVA] multipart는 HTTP POST로만 전송해야 한다..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

작업을 하다가 multipart/form-data 로 PUT 전송을 할 일이 생겼다. 예를 들어 일반적인 회원가입 폼을 생각해 보면 회원가입시에는 /signup 에 POST로 폼 전송을 하지만 회원 가입 후 회원정보를 갱신한다고 생각하면 /user/{id} 에 PUT 전송을 하는게 요즘은 일반적이다. 완전히 REST를 다 따르진 않다고 하더라도 /user/edit?userId=1 과 같이 사용할 수도 있지만 요즘은 RESTful하게 작성하는 것이 어느 정도 보편화되었고 리소스중심으로 URI를 가져가면 여러 모로 더 깔끔해 지는 장점도 있다.

어쨋든 이러한 회원 가입 시나리오에서 회원정보에 이미지같은 파일이 있기 때문에 폼 전송을 multipart/form-data 로 하면서 PUT으로 하게 된 것이다. 뭘 개발하냐에 따라 다르겠지만 요즘은 input[type='file'] 보다는 파일을 업로드하는 컴포넌트를 따로 써서 폼전송과 별도로 업로드 하는 게 더 일반적이기 때문에 multipart/form-data 자체를 사용한지가 좀 오래되었다. 스프링 웹MVC를 사용하는 환경이었는데 브라우저에서 폼 전송을 하다보니 바로 PUT 전송을 하는 것이 아니라 메서드 오버라이드를 이용해서 POST전송을 하면서 <input type="hidden" name="_method" value="PUT"> 을 이용해서 PUT으로 처리되도록 사용하고 있었는데 스프링 웹MVC가 이를 PUT으로 인식하지 않고 계속 POST로만 인식하고 있었다. 스프링은 이를 HiddenHttpMethodFilter가 처리하는데 이 처리가 제대로 되지 않았다.(물론 multipart/form-data 가 아니면 정상적으로 동작한다.)

추적해 본 결과 CommonsMultipartResolver Apache Commons FileUpload사용하는데 ServletFileUpload의 isMultipartContent 소스를 보면 다음과 같이 POST로 하드코딩이 되어 있다.


1
2
3
4
5
6
7
8
Java

public static final boolean isMultipartContent(HttpServletRequest request) {
  if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
    return false;
  }
  return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}

일단 multipart/form-data 로 넘어왔으므로 HiddenHttpMethodFilter 가 쿼라파라미터를 제대로 판단하지 못했고 멀티파트는 아예 POST만 받도록 처리하게 되어 있었다. 도움을 받아서 이를 우회할 수 있도록 일단 처리는 했지만 POST로 하드 코딩되어 잇는 이유가 궁금했다. 듣보잡 라이브러리도 아니고 아파치 커먼즈 라이브러리에서 POST만 받도록 처리한데는 이유가 있을것 같아서 원인을 찾기 시작했다.

검색으로는 잘 나오지 않았기 때문에(대부분은 "_method 인풋을 히든으로 만드세요"라는 답변이거나 좀 더 찾으면 PUT을 인식할 수 있게 리졸버를 작성하라는 내용이 전부였다.) FileUpload 라이브러리의 이슈를 찾아가니 isMultipartContent가 PUT을 지원하지 않는다라는 이슈가 있었다.
Roy T. Fielding added a comment - 07/Mar/13 06:25
PUT means the sent representation is the replacement value for the target resource. A server could certainly support that functionality using any container format, it wouldn't be "normal" to use a MIME multipart, nor is it expected to be supported by the file upload functionality defined for browsers in RFC1867.
If you want to PUT a package, I suggest defining a resource that can be represented by an efficient packaging format (like ZIP) and then using PUT on that resource to have the side-effect of updating the values of its subsidiary resources.
대충 번역하자면
PUT은 대상 리소스의 값을 교체한다는 의미이다. 서버는 어떤 컨테이너 포맷이라도 사용해서 이 기능을 반드시 구현해야 하는데 여기서 포맷은 보통 multipart MIME을 사용하지 않고 RFC 1867에 따라 브라우저도 파일 업로드 기능으로 지원하지 않는다.
PUT으로 패키지를 올리기 원한다면 패키지 포맷(zip같은)을 나타내는 리소스를 정의하고 해당 리소스의 값을 갱신하는 리소스에 PUT을 사용해야 한다.
이 글이 달리고 내용이 정리되어 해당 이슈는 닫혔는데 여기서 댓글을 단 Roy T. Fielding은 HTTP 스펙을 작성한 사람중의 하나이면서 아파치 HTTP 서버를 만든 사람이기도 하고 REST를 최초로 만든 그 로이 필딩이다! 이견없이 한번에 이해가 됐다. "아~ PUT으로 multipart를 보내면 안되는 거구나." 그러니까 PUT은 특정 리소스를 갱신하는 역할을 하는데 multipart로 보내면 한번에 여러 리소스를 처리하므로 이미지 같은 경우를 PUT으로 처리하려면 이미지등에 대한 리소스 URI에 별도의 PUT 요청을 보내서 갱신하고 일반적인 폼은 따로 처리하라는 의미이다. 스펙에 빠삭하지 못해서 정확치는 않지만 이 경우에는 한 URI로 PUT을 보내서 여러 리소스(회원 정보 + 이미지)를 한꺼번에 처리하려고 했으므로 PUT이 적합치 않다는 의미로 보인다.

UI를 당장 파일업로드별로 따로 처리할 수 없다보니(요즘은 다 이미지 같은건 따로 처리하는데 이게 꼭 Rich한 UI를 가지기 위한 것만은 아님을 알 수 있었다.) POST로 처리해버렸다.

My Comment..
쓸일이 아주 많지는 않지만 무엇보다 대부분 그냥 있던 것을 생각없이 쓸 때가 많았다.. 그냥 되던 것을 붙여서 그것이 동작하도록 하고는 나중에 살펴봐야지..?? 하곤 누구나..?? 그렇듯이 그냥 지나치고 잊어버리기 때문이다..

물론 과거보다는 그래도 나름 정리하려는 습관을 갖으려고는 하지만 마음처럼 안되는게 현실이기도 하다.. 햄 덕분에 조금 더 깊이..?? 정확하게..?? 이해를 하게 된듯하다..

올해도 느낀다.. 사람은 항상 고민을 하고, 발전을 하려고 하고, 본인이 한 것들에 대해서 정리하고 기록하는 습관을 갖어야 된다는 것을 말이지..

But.. 뜻처럼 안되.. ㅠㅜ 흙..;;;;;

[Book] 코딩 호러의 이펙티브 프로그래밍..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

제프 앳우드(Jeff Atwood)Coding Horror라는 유명 블로그를 운영하는 블로거이자 프로그래머로 우리같은 개발자들에게는 이제 너무 소중해진 스택오버플로우를 만든 사람 중에 하나이기도 하다. 보통 스택오버플로우하면 조엘 스폴스키가 사람들에게 더 잘 알려져있는데 제프 앳우드가 같이 만들었다.(만든 과정을 자세히 알지는 못하지만 이 책을 읽고 나면 실제 코딩은 제프 앳우드가 다 한듯하다.)

책 제목이 이펙티브 프로그래밍이라서 이펙티브 자바같은 성능좋은 프로그래밍 방법을 알려주는 책으로 생각할 수도 있는데 이 책은 그런 내용의 책은 아니다. 이 책의 원제는 Effective Programming: More Than Writing Code인데 책을 읽고 난 느낌은 부재인 More Than Writing Code 이 더 크다고 생각하는데 프로그래밍이라는 직업 자체에 대한 이야기들이 담겨있고 제프 앳우드가 자신의 블로그에 쓴 글을 모아서 책으로 엮은 것이다.

목차를 보면 대충은 무슨 내용일지 알 수 있지만 프로그래밍 혹은 개발자와 관련된 다양한 얘기가 모여있는 책이다. 프로그래밍에서 신경써야 하는 부분외에도 채용방법, 면접에 대한 생각들, 협업에 대한 얘기, 심지어 의자는 어떻게 고르고 일하는 곳의 조명은 어떻게 해야 하는가에 대한 얘기까지 나온다. 제프 앳우드가 블로그에 글을 오래 썼기 때문에 다양한 주제에 대한 많은 생각을 알 수 있고 번역도 괜찮아서 에세이를 읽듯이 재미나게 읽을 수 있다. 한 챕터에 몇페이지 되지 않고 내용이 계속 이어지는 것이 아니라서 읽기 쉬운 책이다. 뒷부분으로 가면서 서비스를 만드는 부분에 대한 얘기나 스택오버플로우를 만드는 것과 관련된 내용들이 나오는데 나는 이부분이 가장 재미있었다. 아무래도 스택오버플로우라는 사이트가 현재 가지는 위상은 물론이고 이러한 서비스를 만든 사람이 자신의 생각, 의도, 경험을 얘기해준다는 것은 아주 가치있기 때문에 도움될 내용도 많았고 몰랐던 스택오버플로우 얘기도 들을 수 있어서 좋다.

다음은 책을 읽으면서 인상적이었는 부분들...
진짜 프로그래머는 모든 시간을 코딩으로부터 나오는 가치를 창출하는데 보내는 사람이라고 생각한다. ...중략... 프로그래머가 되는 것은 돈과 관련 있는 것이 아니다. 프로그래밍은 어디까지나 열정에 대한 것이다.
다른 무엇보다 중요한 것은 바로 의사소통 능력이다. ...중략... 위대한 프로그래머는 다른 사람을 설득함으로써 영향력을 확대한다.
우리가 스택오버플로우에 참여하는 이유는 다음과 같다.
  • 우리는 프로그래밍을 사랑한다.
  • 우리는 다른 프로그래머들에게 길잡이 역할을 해주는 빵조각을 떨어뜨려서 그들이 우리가 저질렀던 것과 같은 우둔한 실수를 반복하지 않도록 만들고자 한다.
  • 동료를 가르치는 것은 어떤 일을 완전히 습득하기 위한 지름길이다.
  • 우리의 관심이 어느 쪽으로 향하든 그것을 따라갈 수 있다.
  • 작은 노력을 기울여서 커뮤니티를 집단적인 방식으로 더 나은 곳으로 만들고자 한다.
세상에는 이미 스택오버플로우 엔진을 복제한 사이트가 여럿 있다. 나는 그런 사이트들이 더욱 번성하길 바란다. 복제할 만한 가치가 있는 대상을 만들었다는 사실이 자랑스럽기 때문이다.
겸손한 프로그래머가 되는 방법의 핵심은 어떤 상황에 처하더라도 결국 모든 잘못의 뿌리는 자기가 작성한 코드라는 사실을 인정하는 것이다. 그것은 언제나 당신의 잘못이다. ...중략... 소프트웨어 개발자로서의 당신은 자기 자신의 가장 큰 적이다. 이 사실을 일찍 깨달을수록 더 휼륭한 프로그래머가 될 수 있다.
내가 보기에 아이디어는 실행되지 않는 한 아무 가치도 없다. 아이디어는 단지 증폭기 같은 것에 불과하다. 수백 만 달러의 가치가 있는 것은 실행이다.
커뮤니티 피드백의 90%는 쓰레기다 나머지 10%는 대단히 훌륭한 것들이다! 나는 당신이 그 10%에 도달하기 위해 100개 정도의 글을 읽을 수 있는 참을성을 가지고 있기만 하다면 모든 사람들을 위해 사이트를 더 나은 곳을 만들수 있는 황금과도 같은 글을 열 개 정도 발견할 것이라고 장담 할 수 있다.