[GWT] 엔터프라이즈 환경 GWT로 개발환경 구성하기
모 제약사 그룹웨어를 GWT로 개발하다보니 메인 모듈이 5~6개 정도로 구성이 되었는데
하나의 entry-point로 구성하기에는 소스크기가 너무 커지며 HTML로 컴파일하는데도
시간이 10여분이 걸리는 최악의 상황이 발생하기도 한다.
모듈별로 GWT 프로젝트 구성 (entry-point 분할하기)
Mail 모듈과 전자결재 모듈 간의 GWT를 통하여 작업을 처리할 일은 없을 것이다.
공통으로 사용하는 클래스들을 별도의 GWT 프로젝트로 구성하고 각각의 모듈에 대해서
GWT 프로젝트를 구성한 후 공통 프로젝트를 inherit 하는 형식으로 프로젝트를 구성한다.
이런식으로 나누어 컴파일 할 경우에는 결과물의 용량도 줄어들며 컴파일 시간도
많이 줄어들게 된다.
모듈별로 GWT 프로젝트를 분리할 경우 발생하는 문제는 엔터프라이즈 환경의 프로젝트는
혼자서 하는 것이 아니므로 CVS 나 SVN 과 같은 소스 서버를 사용할 경우 컴파일 결과가
동일한 이름으로 나오지 않기 때문에 관리에 어려움을 겪기도 한다.
컴파일 결과물 파일명 통일하기 (GWTCompiler 수정하기)
컴파일된 결과물 이름이 01C4D9CAEE45ACC08AEAA5EC3D15509D.cached.html 로 나오는데
버전관리가 안되는 것은 당연한 것이다. Mail 모듈은 MailModule.cached.html로 결과가 나오게 한다면
그 문제는 해결이 될 것이다.
GWTCompiler.java 파일을 다음과 같이 수정한다.
수정 전
private String realizePermutation(TreeLogger logger, Property[] currentProps, String[] currentValues, int permNumber) throws UnableToCompleteException { String msg = "Analyzing permutation #" + permNumber; logger = logger.branch(TreeLogger.TRACE, msg, null); logProperties(logger, currentProps, currentValues); // Create a rebind oracle that will record decisions so that we can // cache // them and avoid future computations. // CompilationRebindOracle rebindOracle = new CompilationRebindOracle(); // Tell the property provider above about the current property values. // Note that the rebindOracle is actually sensitive to these values // because // in its ctor is uses propOracle as its property oracle. // propOracle.setPropertyValues(currentProps, currentValues); // Check to see if we already have this compilation. // This will have the effect of filling the rebind oracle's cache. // String[] entryPts = module.getEntryPointTypeNames(); Compilation cached = compilations.find(logger, rebindOracle, entryPts); if (cached != null) { msg = "Matches existing compilation " + cached.getStrongName(); logger.log(TreeLogger.TRACE, msg, null); return cached.getStrongName(); } // Now attach a compilation into which we can record the particular // inputs // and outputs used by this compile process. // Compilation compilation = new Compilation(); rebindOracle.recordInto(compilation); // Create JavaScript. // String js = jjs.compile(logger, rebindOracle); // Create a wrapper and an unambiguous name for the file. // String strongName = writeHtmlAndJsWithStrongName(logger, js); // Write out a cache control file that correlates to this script. // compilation.setStrongName(strongName); writeCacheFile(logger, compilation); // Add this compilation to the list of known compilations. // compilations.add(compilation); return compilation.getStrongName(); }
수정 후
private String realizePermutation(TreeLogger logger, Property[] currentProps, String[] currentValues, int permNumber) throws UnableToCompleteException { String msg = "Analyzing permutation #" + permNumber; logger = logger.branch(TreeLogger.TRACE, msg, null); logProperties(logger, currentProps, currentValues); // Create a rebind oracle that will record decisions so that we can // cache // them and avoid future computations. // CompilationRebindOracle rebindOracle = new CompilationRebindOracle(); // Tell the property provider above about the current property values. // Note that the rebindOracle is actually sensitive to these values // because // in its ctor is uses propOracle as its property oracle. // propOracle.setPropertyValues(currentProps, currentValues); // Check to see if we already have this compilation. // This will have the effect of filling the rebind oracle's cache. // String[] entryPts = module.getEntryPointTypeNames(); Compilation cached = compilations.find(logger, rebindOracle, entryPts); if (cached != null) { msg = "Matches existing compilation " + cached.getStrongName(); logger.log(TreeLogger.TRACE, msg, null); return cached.getStrongName(); } // Now attach a compilation into which we can record the particular // inputs // and outputs used by this compile process. // Compilation compilation = new Compilation(); rebindOracle.recordInto(compilation); // Create JavaScript. // String js = jjs.compile(logger, rebindOracle); String entryPointName = entryPts[0].substring(entryPts[0] .lastIndexOf(".") + 1); String name = entryPointName + "_" + currentValues[2] + "_" + currentValues[3]; String strongName = writeHtmlAndJsWithStrongName(logger, js, name); compilation.setStrongName(strongName); writeCacheFile(logger, compilation); // Add this compilation to the list of known compilations. // compilations.add(compilation); return compilation.getStrongName(); } private String writeHtmlAndJsWithStrongName(TreeLogger logger, String js, String strongName) throws UnableToCompleteException { try { byte[] scriptBytes = js.getBytes("UTF-8"); { byte[] prefix = getHtmlPrefix().getBytes("UTF-8"); byte[] suffix = getHtmlSuffix().getBytes("UTF-8"); File outFile = new File(outDir, strongName + ".cache.html"); Util.writeBytesToFile(logger, outFile, new byte[][] { prefix, scriptBytes, suffix }); String msg = "Compilation written to " + outFile.getAbsolutePath(); logger.log(TreeLogger.TRACE, msg, null); } { byte[] prefix = getJsPrefix().getBytes("UTF-8"); byte[] suffix = getJsSuffix().getBytes("UTF-8"); File outFile = new File(outDir, strongName + ".cache.js"); Util.writeBytesToFile(logger, outFile, new byte[][] { prefix, scriptBytes, suffix }); String msg = "Compilation written to " + outFile.getAbsolutePath(); logger.log(TreeLogger.TRACE, msg, null); } return strongName; } catch (UnsupportedEncodingException e) { logger.log(TreeLogger.ERROR, "Unable to encode compiled script as UTF-8", e); throw new UnableToCompleteException(); } }
알 수 없던 바이트코드의 조합이었던 파일 명을 entry-point의 이름과 언어 코드, 브라우저 종류의 조합으로
항상 같은 형식으로 결과가 나오게 변경하였다. ex) MailModule_default_ie6.cache.html
realizePermutation 메소드의 44~49번째 라인이 수정되었으며, writeHtmlAndJsWithStrongName 메소드는
기존 메소드를 바탕으로 파일을 만드는 부분을 수정하였다.
지난 1년 6개월 동안 GWT를 통하여 그룹웨어를 만들면서 느낀점이 많다.
다음번 프로젝트 때는 1년 6개월 간의 노하우로 더 좋은 프로젝트를 해보고 싶다.