java.lang.IllegalStateException: Cannot forward after response has been committed
위와 같은 에러 메시지를 봤을 때, 결론부터 말하면, 인크루드를 사용하자.
개인적으로 간단한 경량 웹 프레임워크를 만들고 있는데, 서블릿 단에서 위와 같은 에러가 발생했다. forward 하기 이전에 response의 Writer를 얻어 내용을 썼기 때문이다.
처음부터 하나씩 설명하면, 먼저 자바에서 HttpServlet을 상속한 어떤 서블릿이 doPost, doGet 메소드를 오버라이드한 상태라고 해보자. 이 때 보통은 doPost, doGet 내부에서 forward로 원하는 경로의 jsp로 이동시킨다.
예를 들면 아래와 같은 식이다.
RequestDispatcher dispatcher = req.getRequestDispatcher( “원하는/경로의/파일.jsp” );
dispatcher.forward( req, res );
그런데 이 jsp에, 내가 원하는 코드를 동적으로 합쳐서 jsp를 만들어보내고 싶다고 해보자. 나의 경우엔 에러메시지를 담아 보내거나, js변수들을 서버 단에서 만들어 보낼 작정이었다.
그렇다면 response에서 getWriter를 사용하여 Writer 객체를 얻고, write 함수로 내용을 넣으면 된다. 이것이 스프링에서의 @ResponseBody기능이다.
그런데 forward 전에, 아래와 같이 response에 무언가를 쓰는 행위를 했다고 하면 에러가 난다.
res.setCharacterEncoding(“UTF-8”);
res.getWriter().write(“<script> alert(‘Hello World’); </script>”);
res.getWriter().flush();
기본적으로 res에 write를 하고 flush를 하면 그것으로 끝난거지(그것이 페이지의 전부이다!), 이후에 forward를 쓸 수 없다. 그랬다간 java.lang.IllegalStateException: Cannot forward after response has been committed 에러가 난다.
스프링에서도 위와 같은 기능(기존 jsp에 원하는 코드를 동적으로 합쳐 내보내는 기능)은 지원하지 않기 때문에, 현재로서는 특정 컨트롤러 메소드에 @ResponseBody 어노테이션을 붙여놓고, ajax로 해당 부분을 불러오는 방법을 쓴다. 다시 말해서 jsp단 ajax 코드가 필수적이다.
나는 jsp에 일일히 코드가 들어가길 원하지 않았다. 그저 기존에 존재하는 jsp에, 원하는 에러메시지 스크립트를 합쳐서 보내고 싶었다. 안타깝게도 redirect를 써도 소용이 없다.
res.sendRedirect( “원하는/경로의/파일.jsp” );
라고 보내면 java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed 에러가 뜬다. 마찬가지로 내용을 쓰고 나서 redirect를 해선 안된다. 더구나 sendRedirect는 forward와 달리 Attribute가 전달되지 않는다. 스프링으로 치면 모델 단에서 작업한 내용이 유실되는 셈이다. (그래서 스프링은 jsp 단에서 ajax로 한 번 더 부르는 방식을 사용한다.)
방법은 있다. include를 쓰는 것이다.
jsp 단에서 include를 부르느니 ajax와 차이가 없기 때문에, 서버 단에서 부른다. 아래와 같이 코딩하면 된다.
req.setAttribute(“myName”, “흑곰”); //테스트용 코드
res.setCharacterEncoding(“UTF-8”);
RequestDispatcher dispatcher = req.getRequestDispatcher( “원하는/파일의/경로.jsp” );
dispatcher.include( req, res );
res.getWriter().write(“<script> alert(‘Hello World’); </script>”);
res.getWriter().flush();
이렇게 include를 사용하면 된다. 이제 jsp에 동적으로 원하는 코드가 삽입된다. 해당 jsp에 ${myName}이라고 써넣고 어트리뷰트도 잘 넘어오는지 확인하자.
여기서 포인트는, Writer의 write와 flush를 뒤에 써준다는 것이다. 스스로 알아냄.