여러가지 컨텐츠 형식(Contents Type)을 한 곳에서 처리한다
July 16th, 2007
레일스로 개발한 애플리케이션의 컨트롤러 코드를 보면 아래와 같은 형식의 소스를 쉽게 찾을 수 있다.
- respond_to do |format|
- format.html { render :template => 'some' }
- format.xml { render :xml => @obj }
- format.json { render :json => @obj.to_json }
- end
위 코드는 하나의 액션 코드로 여러가지 컨텐츠 형식을 처리하고 있다. 사용자가 XML 형식을 원하면(HTTP 요청에 Accept-Type으로 담아보내거나, format 파라메터에 xml을 넣어서 보내거나) 이를 감지해 응답을 XML 형식으로 보내는 식이다. 좋고 나쁨의 논쟁은 접어두고, 어쨌든 현재의 레일스 스타일로 자리잡은 코드이다.
스프링노트에서는 컨트롤러 메서드 대부분에서 HTML, XML, JSON 출력을 하고 있다. 그리고 일부 메서드에서 Ajax 호출에서 사용할 용도로 JS를 지원한다. 코드 중복을 피하기 위해 RenderResult라는 일종의 Presenter 모듈을 도입하고 있기는 한데, 이는 다음에 다루기로 하자.
여러가지 컨텐츠 형식을 처리해야하는 곳은 꼭 응답 문서를 만들 때만은 아니다. 요청 형식도 여러가지일 수 있다. 예를 들어 페이지 리소스를 POST(레일스 용어로는 create)할 때는 일반적인 HTTP 파라메터 형식(page[name]=name&page[content]=conent)을 사용할 수도 있지만, XML이나 JSON을 직접 보낼 수도 있다. 이를 사용자 선택에 맡기도 서버에서 잘 처리할 수 있다면 보다 유연한 REST 서비스가 될 수 있다.
그래서 스프링노트에서는 다음과 같이 API 요청 파라메터를 처리하는 규칙을 정했다.
- XML 문서를 xml 파라메터에 답아서 보낼 수 있다.
- JSON 문서를 json 파라메터에 담아서 보낼 수 있다.
- HTTP 요청의 Content Type이 'application/xml'이라면 RAW_POST_DATA에 XML 문서가 담긴 것이다.
- HTTP 요청의 Content Type이 'application/json'이라면 RAW_POST_DATA에 JSON 문서가 담긴 것이다.
- 그 외에는 일반적인 파라메터 형식을 따른다.
이와 같은 규칙으로 응담을 처리하기 위한 코드이다.
-
def api_param
if params[:xml] then Hash.from_xml(params[:xml])
elsif params[:json] then JsonDecoder.parse(params[:json])
elsif request.content_type == "application/xml" then Hash.from_xml(request.raw_post)
elsif request.content_type == "application/json" then JsonDecoder.parse(request.raw_post)
else params
end
rescue
raise BadRequest
end
once :api_param
Hash.from_xml은 XML을 파싱해서 해시 구조를 만들어준다. JsonDecoder는 직접 만든 것으로 내부적으로 ActiveSupport::JSON 모듈을 활용하고 있다. 그리고 혹시 이 과정에서 예외가 발생한다면, 사용자 요청에 문제가 있는 것으로 보고 BadRequest 예외를 발생시킨다(그러면 예외 처리 모듈이 이 예외를 받아서 400 에러 페이지를 렌더링 할 것이다).
이제 사용자가 보낸 객체에 접근하기 위해서 params를 직접 호출하는 대신 api_param을 사용하면 된다.
- def create
- @page = Page.create api_param['page']
- render :xml => @page
- end
이렇게 함으로써 애플리케이션 전반에 일관된 파라메터 규칙을 갖게 할 수 있고, 코드 중복도 피할 수 있다. 만세!




Leave a Reply