vanila java로 구현하던 was 프로젝트에서, http request를 읽다가 마주친 문제점들과 해결책들이 있어 정리해보고자 한다!
클라이언트 가 서버에 작업을 요청하는 메세지메소드 (Method) 요청 유형 정의GET, POST, DELETE, PUT, PATCH, ...URL : 요청하는 리소스 경로HTTP 버전 : 사용되는 HTTP 프로토콜 버전Host, User-Agent, Accept, Content-Type , ... key - value로 이루어져 있다.여기서 Http Request를 읽으면서 문제가 발생했던 부분이었다 ㅜㅅㅜ
POST, PUT 요청에 사용됨데이터를 포함form 데이터, JSON, XML , ...private BufferedReader br;
public HttpRequestReader(InputStream in) {
try {
this.br = new BufferedReader(new InputStreamReader(in, Config.getEncoding()));
} catch (UnsupportedEncodingException e) {
logger.error("[ERROR] 지원되지 않는 인코딩 에러 발생");
this.br = new BufferedReader(new InputStreamReader(in));
}
}
...
private String readBody(HttpRequestStartLine startLine, HttpRequestHeader header) throws IOException {
if (startLine.getHttpMethod().equals(HttpMethod.GET)) {
return NONE;
}
int length = Integer.parseInt(header.getValue("Content-Length"));
char[] body = new char[length];
br.read(body, 0, length);
return new String(body);
}
BufferedReader 을 통해 InputStream 값을 읽어오고 있었다.Request Body를 읽지 않는 GET 요청까지는 잘 구현했다(?) 고 생각했다...POST 를 구현하면서, Header에 저장되어있는 length 값을 통해 Request Body 또한 읽을 수 있겠네~ 하고 구현하면서 에러가 나지 않아 행복한 코딩 시절..이었다 이미지를 서버에 업로드 하게 되면서, 본격적인 헬코(헬코딩)가 시작되었다.
그래서 이것을 해결!! 하면서 했던 나의 삽질을 정리해보겠다~!

HTTP POST 메서드는 서버로 데이터를 전송합니다. 요청 본문의 유형은 Content-Type 헤더로 나타냅니다.
PUT과 POST의 차이는 멱등성으로, PUT은 멱등성을 가집니다. PUT은 한 번을 보내도, 여러 번을 연속으로 보내도 같은 효과를 보입니다. 즉, 부수 효과(side effect)가 없습니다.
POST 요청은 보통 HTML 양식을 통해 서버에 전송하며, 서버에 변경사항을 만듭니다. 이 경우의 콘텐츠 유형(Content-Type)은 <form> 요소의 enctype 특성이나 <input>, <button> 요소의 formenctype 특성 안에 적당한 문자열을 넣어 결정합니다.
application/x-www-form-urlencoded: &으로 분리되고, "=" 기호로 값과 키를 연결하는 key-value tuple로 인코딩되는 값입니다. 영어 알파벳이 아닌 문자들은 percent encoded 으로 인코딩됩니다. 따라서, 이 content type은 바이너리 데이터에 사용하기에는 적절치 않습니다. (바이너리 데이터에는 use multipart/form-data 를 사용해 주세요.)_
multipart/form-data
text/plain
여기서 유심히 봐야 할 키워드는?!
바이너리 데이터에는 use multipart/form-data 를 사용해 주세요...!POST로 전달하기 위해 사용되는 `콘텐츠 타입대량의 바이너리 데이터 를 전송할 때 유용하다.POST /upload HTTP/1.1
Host: www.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 5000
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="field1"
value1
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="field2"
value2
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain
... (file content) ...
------WebKitFormBoundary7MA4YWxkTrZu0gW--
오케이! 그렇다면
Request Body는 대량의바이너리 데이터로 온다는 것을 알았다!그렇다면 이제 발생하는 문제에 대한 원인을 좁힐 수 있었다.
BufferedReader.. 너바이트읽을 수 있어???

java.io 패키지에 포함된 클래스 중 하나.문자 입력 스트림에서 텍스트를 효율적으로 읽을 수 있도록 버퍼링 기능 제공텍스트 데이터 를 읽는 데 사용Multipart/Form-Data 는 Binary 타입으로 서버에게 전달된다.BufferedReader로 해당 Request Body를 읽고 있어, Encoding 오류 문제가 계속 발생하였다.BufferedReader는 텍스트 를 읽기 위해 사용되는 io 클래스이며, 우리는 Byte 단위로 읽어야 하기 때문에, BufferedReader는 적합하지 않다!private BufferedInputStream bis;
public HttpRequestReader(InputStream in) {
bis = new BufferedInputStream(in);
}
...
private byte[] readBody(HttpRequestStartLine startLine, HttpRequestHeader header) throws IOException {
if (startLine.getHttpMethod().equals(HttpMethod.GET)) {
return NONE;
}
int contentLength = Integer.parseInt(header.getValue("Content-Length"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
int totalBytesRead = 0;
while (totalBytesRead < contentLength &&
(bytesRead = bis.read(buffer, 0, Math.min(BUFFER_SIZE, contentLength - totalBytesRead))) != END_OF_LINE) {
baos.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
return baos.toByteArray();
}
BufferedInputStream 을 이용해서 byte 단위로 Request Body를 읽어서 성공적으로 이미지 파일을 업로드 할 수 있었다!사실 공식 문서를 조금만 찾아봐도 해결할 수 있는 문제였지만, 해당 문제로 거의 1주일은 넘게 골머리를 앓았던 것 같다...
왜냐하면BufferedReader이나Scanner외에 io 클래스는 사용해 본 적이 없어서 무지했던 것 같다.
하지만, 이번 Trouble Shooting을 처리하면서, 오류가 생기거나 모르는 것이 있으면, 다른 사람의 기술 블로그나 코드를 보는 것 보다,공식 문서를 보고 개선해 나가는 것이 나의 학습과 성장에 있어서 더욱 좋은 방향이라고 느끼게 되는 계기가 되어서 너무 뿌듯하다!