아파치 톰캣(Apache Tomcat) 8 버전이 곧 베타에서 정식 버전으로 발표될 예정인데 스프링 프레임웍 팀에서 웨비나를 하길래 오늘 방금 전 자정부터 1시까지 그걸 봤다. 동영상 리플레이 및 발표 자료는 1-2주 후에 https://www.youtube.com/springsourcedev에 올린다고 한다. 아래 웨비나 내용 및 공부한 내용을 정리해본다. 예제 출처는 github.com/swilliams-pivotal/s2gx-tomcat다.

사실 톰캣 8 자체보다는 톰캣이 구현하는 자바 EE의 기능 소개 성격이 더 강한 웨비나였는데 아무튼 일단 톰캣 8은 다음 내용으로 요약할 수 있다.

서블릿 3.1

서블릿 3.0부터 HTTP 입출력에 큰 기능이 하나 추가됐는데 바로 "차단되지 않는 입출력(non-blocking IO)"이다. 그전까지는 말하자면 웹브라우저 하나에서 웹페이지를 다운로드(또는 업로드)할 때 페이지 로딩이 끝날 때까지 다른 아무 것도 할 수가 없었다. 하지만 이제는 입출력을 비동기적으로 처리할 수 있어서 서버가 다른 작업을 하면서 입출력을 처리할 수 있다. (다른 말로 또는 반대로 말하자면 입출력을 처리하면서 동시에 다른 작업을 할 수 있다. 이것이 가능한 것은 입출력을 별도의 자바 스레드를 생성해 처리하기 때문이다.)

다음 예제 코드를 보자. 아주 단순한 예제로서 실용성은 전혀 없는데 개념은 잘 드러나 있다.

    @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. Start Async
final AsyncContext asyncContext = req.startAsync();

// 2. Add Write Listener - to say Hello
ServletOutputStream outputStream = resp.getOutputStream();
WriteListener listener = new WriteListener() {
@Override
public void onWritePossible() throws IOException {
// 3. Write to output stream
ServletOutputStream output = asyncContext.getResponse().getOutputStream();
if (output.isReady()) {
output.print("Hello World!");
}

// 4. Call complete, to signal we are done
asyncContext.complete();
}

@Override
public void onError(Throwable throwable) {
throwable.printStackTrace(System.err);
asyncContext.complete();
}
};
outputStream.setWriteListener(listener);
}

서블릿의 doGet 메서드를 보인 것인데 4행에서 HttpServletRequest#startAsync 메서드를 호출하여 비동기 처리를 준비하고 WriteListener 인터페이스를 구현하여 비동기적으로 "Hello World!"를 출력하고 있다. 비동기적이므로 27행에 이후에 뭔가 다른 코드가 있다면 해당 코드가 실행되면서 동시에 "Hello World!"를 클라이언트로 출력하는 작업은 그 작업대로 실행될 것이다.

이 글은 톰캣 8 소개 글이므로 차단되지 않는 입출력은 여기까지만 알아보기로 한다.

JSP 2.3

JSP 2.3은 "maintenance release"로서 톰캣 8에서 그 전 버전과 별 차이가 없다고 한다.

Expression Language 3.0

EL 3.0은 이전 버전과 많이 달라졌지만 하위 호환성이 있다고 한다. 이전 버전과 달리 ELProcessor라는 클래스를 단순 빈(bean) 방식으로 선언하여 사용할 수 있으므로 JSP가 아닌 임의의 자바에서도 EL을 사용할 수 있게 됐다.

    // EL을 사용할 수 있는 API를 제공하는 ELProcessor 생성
ELProcessor processor = new ELProcessor();

// 간단한 데이터 등록 및 EL 실행
processor.defineBean("person", this.person);
System.out.println(processor.eval("person.name"));

// EL 3.0에서는 셋, 리스트, 맵을 쉽게 선언할 수 있다.
Set set = (Set) processor.eval("{1, 2, 3, 3, 2, 1}");
System.out.println("Set -> " + set);

List list = (List) processor.eval("[1, 2, 3, 3, 2, 1]");
System.out.println("List -> " + list);

Map<String,Integer> map = (Map<String, Integer>) processor.eval("{'one': 1, 'two': 2, 'three': 3}");
System.out.println("Map -> " + map);

EL 3.0은 위의 예시 외에도 덧셈 연산자(+)로 문자열 연결하기, map/reduce 등 다양한 기능이 추가됐다.

웹소켓 1.0

톰캣 7에서도 웹소켓 API를 지원했으나 톰캣 8 및 7.0.43부터 새로운 API로 웹소켓이 구현됐다고 한다. 또한 톰캣 7에서는 차단되는(blocking) 방식이었으나 웹소켓 1.0에서는 거의 비차단식으로 제공된다.

@ServerEndpoint("/websockets/events/annotation")
public class EventsAnnotationEndpoint {

// called when connection is opened, use blocking api to send a response message
@OnOpen
public void onOpen(Session session) {
try {
session.getBasicRemote().sendText("Welcome!");
} catch (IOException e) {
e.printStackTrace();
}
}

// called on close, we do nothing.
@OnClose
public void onClose() {
// Clean up resources here
}

// called when a full message is received, we echo back to client using blocking api
@OnMessage
public void onMessage(Session session, String message) {
try {
session.getBasicRemote().sendText("Echo [" + message + "]");
} catch (IOException e) {
e.printStackTrace();
}
}

}

톰캣 자체의 변화

일정

톰캣 8의 일정은 다음과 같다고 한다.

이상으로 웨비나 및 내가 간단히 찾아본 내용 정리를 마치기로 한다. 베타 버전 상태므로 실전에서 사용하기에는 부족하다고 하지만 톰캣이 원래 개발자들이 손쉽게 사용할 수 있는 웹 서블릿 컨테이너였으므로 개발단에서 톰캣 8을 사용하는 데 별 문제는 없을 것 같다. 모바일 환경이나 HTML 5가 중요한 흐름인 시대가 됐으므로 서블릿/JSP나 웹소켓 등의 최신 기술을 남보다 먼저 사용해보는 것이 여러 모로 유익하지 않을까 생각된다.