RESTful API PUT method를 어떻게 구현할지 많이 고민 했다. RESTful API에 대한 정보를 찾아봐도, 특성만 나타나 있지 실제로 어떤식으로 구현해야할지는 없었다.

MkWeb의 초기 버전은 RESTful API의 성능을 떠나서 "동작하는 기능"으로써의 구현을 진행하고 있기 때문에, 개발 및 테스트 하면서 원리를 이해하고, 앞으로의 수정방향을 잡고 있기 때문에 일단 구현해 봤다.

MkWeb에서 PUT Method는 다음과 같이 정의 하였다.

삽입 / 수정, 즉 UPDATE를 담당하는 METHOD

PUT Method에 관한 응답 및 처리를 찾아 봤을 때, 어떤 글은 GET과 같은 조회와 POST의 삽입의 기능을 담당한다.

또 어떤 글은 POST의 삽입 기능과 SQL의 UPDATE문을 담당한다는 등 크게 두 파로 나뉘어 있었다.

그래서 사실 조회의 경우 URL을 통해 쉽게 할 수 있으니, 삽입과 수정의 기능을 바탕으로 만들었다.

그리고 수정의 경우, PATCH로써 동작하게끔 했다. ( PATCH 를 넣으니, 톰캣의 버전 때문인지 정상적인 method가 아니라며 실행되지 않았기 때문. 후에 가능하다면 MKWeb을 업데이트 할 때 PATCH를 새로 만들 예정이다.)

조회와 삽입을 구분하는 기준은 다음과 같이 정했다.

URI에 수정하고자 하는 대상의 조건을 보내고, Body Parameter에 수정 내용을 입력한다.
이 때, 대상이 존재하지 않으면 삽입, 존재하면 데이터를 수정한다.

이렇게 정의하고 나니 한결 쉬워졌는데, 데이터 입력 및 수정 할 때, 삽입/수정하고자 하는 column이 Not Null이고 Deafult 값이 없거나 하는 경우에는 오류가 발생해서 난감했다.

사실 이 문제는 RESTful API의 본질적인 기능을 잊고, 기능 구현에 집중하다보니 발생한 문제였는데, message와 error code를 보내주는걸로 해결했다.

사용 예는 다음과 같다.

URI : /users/name/dev.whoan
body parameter: email=dev.whoan@gmail.com

users Data Set에서 name column이 dev.whoan인 사람의 email을 dev.whoan@gmail.com으로 수정한다. 여기에 입력되지 않은 column은 기존 값을 유지한다.

삽입
URI: /users/name/whoan
body parameter: email=dev.whoan@gmail.com, name=whoan

users Data Set에 name=whoan이라는 사람이 존재하지 않으면, email=dev.whoan@gmail.com, name=whoan으로 데이터를 삽입한다. 이 때, 모든 column이 입력되어야 한다. 모든 column이 없으면, 400 Bad Request와 모두 입력하라는 message를 보내주었다.

 


구현할 때 문제점이 있었는데, MkWeb의 SQL을 정의하는 구간에서 각 data를 짜집어 SQL을 미리 정의해두는게 있었는데, 이 때 이미 SQL이 설정되어 있어서 따로 수정할 수 없는게 문제였다. 이는 따로 RESTful API전용 SQL Creator를 만들어 고쳤다. (여기서 중요한건 이게 아니니 패스)

Data의 유/무를 확인하기 위해 GET Method를 통해 PUT Method 요청시 특정된 데이터가 존재하는지 확인했고, 특정된 데이터가 있다면 update, 없으면 insert문을 수행하게 했다. 사실 insert문의 경우 POST로 대체할 수 있지 않을까 싶었는데, PATCH의 기능을 수행하게 하였기 때문에 column의 값들이 문제가 되어서 put에서 수행하게 했다.

그리고 PUT method 안에서 INSERT와 UPDATE 중 하나가 발생하기 때문에(두 메소드 모두 사용되기 때문에), 해당 작업 수행 후 MkRestApiResponse에 응답 코드를 담아서 반환해주었다.

프론트로부터 request.getParameter를 통해 데이터를 받을 때, 데이터가 없는 경우가 있다. 이 때는 Form Data나 Body Message에 데이터가 담겨온 것인데, 이럴 경우 Stream을 이용해 parameter를 받아와야 한다.
보통 프론트 사이드에서 HttpRequest에 Parameter를 전송할 경우 Servlet에서 다음 함수로 받을 수 있다.

파라미터명을 아는 경우
request.grtParamrter(String param);
파라미터명을 모르는 경우
request.getParameterNames()

위 방식은 쿼리 스트링, 혹은 Form Data의 경우 정상적으로 받아지지만 Content-Type이 변하여(application/json 등) Body Content에 parameter가 담겨올 경우, 위 방식으로 Parameter를 찾아보면 null 값이 나온다.

이럴 경우 body content를 읽어들여야 하는데, request.getInputStream()을 이용해 buffer를 읽어들이면 된다.

StringBuilder stringBuilder = new StringBuilder(); // String Builder BufferedReader bufferedReader = null; try(InputStream inputStream = request.getInputStream()){ if(inputStream != null){ bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); char[] charBuffer = new char[256]; int bytesRead = -1; while ((bytesRead = bufferedReader.read(charBuffer)) > 0){ stringBuilder.append(charBuffer, 0, bytesRead); } } } catch (IOException e){ throw e; } finally { if(bufferedReader != null){ try{ bufferedReader.close(); }catch(IOException e){ throw e; } } }

이는 URL encoding 된 데이터를 가져오기 때문에 URL decoding 후 데이터를 사용하거나, 있는 그대로 사용해도 된다.


그러나 위 방식으로 parameter를 읽어올 경우, inputStream이 초기화되는 문제가 발생한다. (Tomcat은 inputStream을 읽는 순간 초기화된다.)

따라서 getInputStream으로 body는 모든 작업이 끝난 후 (적어도 내가 필요한 정보를 모두 확인했으나, body를 직접 읽어야 하는 경우)읽는게 좋다.

만약 request.getParameter()로 정상적인 parameter 접근이 가능하지만, request.getInputStream()으로 모두 읽어들인 후 request.getParameter()를 하면 null이 발생한다.


중간에 charBuffer 크기를 얼마나 할지 의문이 들 수 있는데, 넉넉하게 2^7 이상을 주거나, character형이 아닌 String형으로 한 line씩 읽어와도 된다.

... bufferedReader = new Bufferedreader(new InputStreamReader(inputStream)); String line; while((line = bufferedReader.readLine()) != null){ stringBuilder.append(line); } ...

+ Recent posts