"리플렉션 없음"의 범위

이 원칙은 *프레임워크 core*에 적용된다: 라우팅, 파라미터 추출, JSON 처리는 어디에서도 리플렉션을 쓰지 않는다.

기본 템플릿 엔진은 바로 그 경계에 놓여 있으므로, 그 범위를 정확히 밝혀 둘 가치가 있다. jte는 모든 템플릿을 Java 클래스로 컴파일하므로, ${deck.title}`은 컴파일된 메서드 호출로 실행된다. 모델은 결코 리플렉션 대상이 되지 않으며, 그 표현식의 오타는 조용히 빈 값이 되는 대신 컴파일 에러가 된다. 남는 것은 로딩이다: 엔진은 생성된 클래스를 이름으로 찾아 그 `render 메서드를 리플렉션으로 호출한다. 그 리플렉션은 빌드가 생성한 클래스에 한정되며, 직접 작성한 클래스에는 결코 닿지 않는다. 그 한정이 네이티브 이미지 이야기를 기계적으로 유지하는 것이다: jte 자체의 빌드 확장이 정확히 그 생성된 클래스들에 대한 리플렉션 설정을 쓰며, 모델 클래스도 프레임워크 클래스도 항목이 필요 없다.

별도 모듈에 있는 템플릿 엔진은 각자 스스로 답한다: Handlebars는 레코드나 빈에 대해 리플렉션으로 물러나고, FreeMarker는 객체 래퍼를 통해 읽으며, Thymeleaf는 모든 표현식을 OGNL로 평가한다. 그것이 각각을 core 밖에 두는 또 하나의 이유다. 모듈을 가져오면 그 리플렉션도 함께 가져온 것이며, 기본 jte에 머무는 애플리케이션은 모델 접근을 리플렉션 없이 유지한다.

spider-silk-jetty-websocket도 같은 방식으로 스스로 답하며, 잘 답한다. Jetty는 엔드포인트의 콜백 각각에 대한 `MethodHandle`을 잡아 엔드포인트 클래스별로 캐시하는 방식으로 바인딩하고, 이 모듈이 여는 모든 연결은 모듈 자신이 선언한 하나의 클래스다. Jetty가 찾아보는 것은 그 하나의 클래스다. 애플리케이션이 작성한 `WebSocketHandler`는 인터페이스 호출로 도달되며 결코 리플렉션 대상이 되지 않는다.

애플리케이션이 추가하는 JSON 바인딩 라이브러리는 템플릿 모듈과 같은 조건으로 스스로 답한다: 가져오면 그 리플렉션도 함께 가져온 것이다. Jackson과 Gson은 필드를 리플렉션으로 읽고 쓰므로, 바인딩되는 모든 타입은 네이티브 이미지 설정에 항목이 필요하다. avaje-jsonb는 컴파일 시점에 타입별 어댑터를 생성하므로 항목이 필요 없으며, 이는 jte가 템플릿에 대해 하는 것과 같은 거래다. 어느 쪽이든 core는 관여하지 않는다: 이음새(seam)는 `WebResponse.json(String)`과 `req.bodyStream()`이며, 손으로 쓴 라이터에 머무는 애플리케이션은 와이어 포맷을 리플렉션 없이 유지한다.

예제 프로젝트의 리포지토리는 spring-jdbc의 `DataClassRowMapper`를 택하며, 이는 도메인 레코드를 리플렉션으로 생성한다. 그것은 프레임워크의 선택이 아니라 예제의 선택이며, 경계가 어디를 지나는지 보여주기 위해 의도적으로 유지한 것이다. 네이티브 이미지에서는 그 선택의 대가로 빌드가 생성하는, 하나의 도메인 패키지에 한정된 리플렉션 설정을 치른다. core는 거기에 단 하나의 항목도 보태지 않는다.