들어가며
PhantomJS를 사용할 때 PhantomJS만으로 프로젝트가 구성되는 경우는 많지 않다. 대부분은 NodeJS와 연동하게 되는데, 이번 프로젝트에서도 물론 NodeJS가 PhantomJS를 감싼 구조로 모듈을 개발했다. 프로젝트를 진행하며 만난 문제들 중 특히 골치를 아프게 했던 절대 경로 문제에 대해 다뤄보려고 한다.
상대 경로 문제
PhantomJS 로직들이 규모가 커지면서, 초반에는 예상하지 못한 경로 문제가 생겨났다. 처음 고민이 되었던 건, PhantomJS에서도 지원하는 CommonJS방식 module의 경로를 어떻게 잡아주는가였다. 이 문제는 간단한 테스트를 통해 생각보다 쉽게 해결되었다.
+--+ node_modules | + | +--+ bluebird | +--+ phantom | + | +--+ phantom_modules | | + | | +--+ module_first.js | | | | | +--+ module_second.js | | | +--+ phantom_script.js | +--+ index.js
위와 같은 구조로 NodeJS가 PhantomJS를 감싸고 있고, NPM으로 bluebird를 install해둔 상황이라고 가정하면 PhatomJS 파일은 phantom_script.js 파일에서는 아래와 같이 NodeJS 모듈과 PhantomJS 사용자 모듈을 로드해줄 수 있다.
var Promise = require('bluebird'); var first = require('./phantom_modules/module_first'); var second = require('./phantom_modules/module_second');
bluebird처럼 NPM을 통해 설치한 모듈은 (PhantomJS에서 정상동작하기만 하면) NodeJS에서와 동일하게 모듈명을 적는 것만으로도 require가 가능하다. PhantomJS의 사용자 모듈은 어떤 경우에도 현재 PhantomJS 스크립트 파일을 기준으로 정확하게 상대경로를 인식해서 require 해준다. 따라서 PhantomJS를 감싼 NodeJS 어플리케이션이 또다른 어플리케이션의 모듈이 된다고 해도 적어도 require를 사용해서 상대경로로 모듈을 가져오는 것은 큰 문제를 일으키지 않는다.
절대 경로 문제
진짜 문제는 PhantomJS를 NodeJS가 감싸고 있고 이 NodeJS 어플리케이션이 또다른 어플리케이션의 모듈로 포함되는 상황에서 PhantomJS 로직에서 절대경로가 필요해지는 상황이다. 처음에는 단순히 PhantomJS의 fs.absolute 메서드나 fs.workingDirectory 메서드를 사용하면 쉽게 해결될거라 예상했지만 실제로는 달랐다.
아래와 같이 /home/user/projects/aaa/bbb/ccc/test.js 에 위치한 스크립트를 /home/user/projects 에서 실행하면 어떤 결과가 나올까?
(아마도 일반적으로 PhantomJS가 NodeJS로 감싸져 있을 때 이와 비슷한 실행 형태가 될 것이다)
/home/user/projects$ phantomjs ./aaa/bbb/ccc/test.js
//test.js 파일 var fs = require('fs'); console.log('fs.workingDirectory -> ', fs.workingDirectory); console.log('fs.absolute(./) -> ', fs.absolute('./')); phantom.exit();
앞서 분명히 require할 때는 ./ 와 같은 상대 경로를 정확히 인식했지만 여기에서는 아래와 같이 약간 다른 결과가 나온다.
fs.workingDirectory -> /home/user/projects fs.absolute(./) -> /home/user/projects/
만약 /home/user/projects/aaa/bbb/ccc/ 와 같이 PhantomJS가 실행되는 위치의 절대 경로를 얻고 싶다면 약간 다르게 접근해야 한다.
PhantomJS의 system 모듈은 args의 첫번째 인자에 현재 실행된 PhantomJS 스크립트의 경로를 담는다. 앞의 예를 들면, ./aaa/bbb/ccc/test.js 를 담고 있는 것이다. 이 실행 경로와 fs.absolute 메서드를 함께 사용하면 현재 실행된 PhantomJS 스크립트의 절대 경로를 아래와 같이 추출해줄 수 있다.
var fs = require('fs'); var system = require('system'); var modulePath = system.args[0].replace(/[^\/]*$/, ''); console.log('fs.workingDirectory -> ', fs.workingDirectory); console.log('fs.absolute(./) -> ', fs.absolute('./')); console.log('modulePath -> ', modulePath); console.log('fs.absolute(modulePath) -> ', fs.absolute(modulePath)); phantom.exit(); /* [실행결과] /home/user/projects $ phantomjs ./aaa/bbb/ccc/test.js fs.workingDirectory -> /home/user/projects fs.absolute(./) -> /home/user/projects/ modulePath -> ./aaa/bbb/ccc/ fs.absolute(modulePath) -> /home/user/projects/aaa/bbb/ccc/ */
위의 소스를 살펴보면, system.args[0] 에 담겨있는 PhantomJS 스크립트 경로를 가져온 후 스크립트 파일명은 정규식으로 제거한 후 fs.absolute 로 절대경로를 추출한 것을 볼 수 있다.
마치며
PhantomJS를 NodeJS가 감싸고 있고 이 NodeJS 어플리케이션이 또다른 어플리케이션의 모듈로 포함되는 약간은(?) 복잡한 상황에서 PhnatomJS Script의 절대 경로가 필요하다면 위에서 다룬 내용이 도움이 되었기를 기대한다.