스물한번째 날: Plack on SL4A

저자

@aanoaa - Perl, 야구, 자전거, 낙타, 돌고래, 포청천 마니아. 최신 휴대폰에 관심은 없지만 요일별로 스마트 폰을 바꿔가며 들고다닌다. YAPC::Asia를 다녀온 후 현재 넥서스 원과 갤럭시 탭, 노트북의 바탕화면은 Larry Wall. 현재 안드로이드 앱 개발에 매진 중이다.

시작하며

Perl은 대부분의 운영체제에 이식되어 기본으로 내장되어 있는 범용적인 언어입니다. 비록 기본 내장은 아니지만 안드로이드 플랫폼에도 이식되었습니다. 바로 SL4A가 그 주인공인데요. 자세한 내용은 SL4A 공식 홈페이지를 확인하세요. 아! 물론 이번 크리스마스 달력 16일자 기사도 놓치지마세요! ;-) SL4A가 설치되었다면 여러분의 안드로이드 기기는 웹서비스 머신으로 탈바꿈할 모든 준비가 된 것입니다.

초간단 웹서비스

우선 아직은 안드로이드 기기에서 바로 cpan 명령을 이용하거나 바로 XS 모듈을 설치할 수 있는 간단한 방법이 없다는 것에 유의하세요. 이 두 가지 제약 사항은 꽤나 아쉬운 부분이긴 하지만 순수 펄 모듈로만 구성된 CPAN 모듈이라면 약간의 기교를 부려 어렵지 않게 안드로이드 기기에서 사용할 수 있습니다. 그럼 안드로이드폰에서 보다 편리하게 CPAN을 활용하는 방법을 알아보죠. :-)

다음은 웹서버를 이용해서 파일 디렉토리 나열 옵션을 켜고 브라우저로 접속한 화면입니다.

아파치를 사용해 구동시킨 HTTP 파일 서버

화면에서 볼 수 있는 웹 서비스를 일반 PC에서 아파치 웹서버를 이용해 구축한다면 다음처럼 설정해야 할 것입니다.

1
2
3
<Directory /path/to>
    Options Indexes FollowSymLinks
</Directory>

앞의 예제 설정 파일은 /path/to 하위 디렉토리를 인덱싱하고 읽기 권한이 있는 파일에 웹서버를 통해 접근할 수 있도록 설정해줍니다. 물론 처음부터 구현해서 동일한 기능을 만들 수도 있겠지만, 대신 CPAN의 Plack::App::Directory 모듈을 이용하겠습니다. Plack::App::Directory를 이용하면 HTTP 파일 서버를 띄우는 일은 너무 간단합니다.

1
Plack::App::Directory->new(root => "/path/to")->to_app;

sdcard를 파일 서버의 루트 디렉터리로 마운트하고 8080 포트로 웹서버를 실행시키는 전체 소스는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use strict;
use lib '/sdcard/sl4a/scripts/perl5/lib/perl5';
use Android;
use Plack::Builder;
use Plack::App::Directory;
use HTTP::Server::Simple::PSGI;
 
my $droid = Android->new;
my $app = builder {
    mount "/" => Plack::App::Directory->new(root => '/sdcard');
};
 
my $server = HTTP::Server::Simple::PSGI->new(8080);
$server->app($app);
$server->run;

의존 모듈 설치

앞에서 보다시피 CPAN의 모듈을 사용하면 간단히 원하는 웹서비스를 구축할 수 있습니다. 하지만 이렇게 CPAN 모듈을 사용하려면 해당 모듈은 물론 그에 의존하는 모듈을 모두 설치해야 합니다. 앞서 작성한 Perl 스크립트를 실행시키려면 안드로이드 기기의 /sdcard/sl4a/scripts/perl5/lib/perl5 디렉터리에 기본적으로 사용하고 있는 다음 모듈을 설치해야 합니다.

  • Plack::Builder
  • Plack::App::Directory
  • HTTP::Server::Simple::PSGI

이 외에도 동일한 경로에 다음 의존 모듈도 설치해야 합니다.

  • CGI
  • parent
  • integer
  • CGI::Util
  • DirHandle
  • FileHandle
  • HTTP::Date
  • Time::Local
  • URI::Escape
  • File::Spec::Unix
  • HTTP::Server::Simple::CGI

사실 parentinteger 두 모듈은 Perl의 기본(core) 모듈이지만 어찌된 이유인지 SL4A용 Perl에는 기본으로 들어가 있지 않기 때문에 역시 따로 설치해야 합니다.

Use App::cpanminus;

원리는 간단합니다. 순수 Perl로 구성된 모듈을 시스템에 설치한 후 직접 설치한 파일을 안드로이드 기기의 해당 디렉터리에 복사하면 됩니다. 참 쉽죠? :-)

그런데 만만하게 보고 실제로 복사하기 시작하면 녹녹한 일이 아니란란 것을 알게 될 것입니다. 기본적으로 모듈의 의존성 부분을 점검해서 의존하는 모듈을 모두 재귀적으로 복사해야 하기 때문입니다. :-(

하지만 걱정하지 마세요! 우리에겐 CPAN의 App::cpanminus 모듈이 있답니다! App::cpanminus를 설치하면 같이 설치되는 명령줄 도구인 cpanm을 사용하면 우리가 해야할 일은 놀라울 정도로 간단해집니다. 작업 순서는 다음과 같습니다.

  • SL4A 설치
  • App::cpanminus를 설치
  • 컴퓨터에 안드로이드 기기를 연결해서 USB 저장소로 마운트
  • PC의 명령줄에서 cpanm-l 옵션으로 사용할 모듈을 안드로이드 기기에 바로 설치
  • SL4A를 실행해서 확인

도전!

SL4A 설치

SL4A 설치는 생략하겠습니다. SL4A 설치를 완료하면 /data/data/com.googlecode.perlforandroid/files/perl/perl에 Perl 바이너리 파일이 있습니다. 안드로이드 SDK를 설치했다면 adb로 확인할 수 있습니다.

1
2
3
4
$ adb shell "/data/data/com.googlecode.perlforandroid/files/perl/perl -v"
This is perl, v5.11.0 (*) built for arm-eabi-linux
Copyright 1987-2009, Larry Wall
...

참고로 SL4A Perl의 @INC 정보는 다음과 같습니다.

1
2
3
4
5
6
$ adb shell "/data/data/com.googlecode.perlforandroid/files/perl/perl -e 'print join(\"\n\", @INC)'"
/data/data/com.googlecode.perlforandroid/files/perl/site_perl/5.10.0/arm-eabi-linux
/sdcard/com.googlecode.perlforandroid/extras/perl/site_perl/5.10.0
/data/data/com.googlecode.perlforandroid/files/perl/5.10.0/arm-eabi-linux
/data/data/com.googlecode.perlforandroid/files/perl/5.10.0
/sdcard/com.googlecode.perlforandroid/extras/perl/site_perl

App::cpanminus 설치

이번 크리스마스 달력 18일자 기사에 소개된 cpanm을 사용하려면 App::cpanminus 모듈을 설치해야 합니다.

1
$ cpan App::cpanminus

안드로이드 기기를 USB 저장소로 마운트

안드로이드 기기를 USB로 연결해서 컴퓨터에서 직접 SD 카드에 접근할 것이므로 USB 저장소로 마운트하도록 합니다. 넥서스원의 경우 USB 저장소로 마운트하면 안드로보이 아이콘이 녹색에서 주황색으로 바뀝니다.

USB 저장소로 안드로이드 기기 마운트 전

USB 저장소로 안드로이드 기기 마운트 후

cpanm으로 모듈 설치

안드로이드 기기를 USB 저장소로 마운트했기 때문에 컴퓨터에서는 일반적인 하드디스크와 마찬가지로 접근할 수 있습니다. 여기에 cpanm의 설치 경로를 지정하는 -l 옵션(--local-lib 옵션과 동일)을 이용해서 안드로이드 기기에 직접 설치하는 것이 바로 핵심입니다! 이것 말고도 cpanm의 유용한 옵션은 cpanm -h으로 확인해보세요.

cpanm으로 필요한 CPAN 모듈을 설치하려면 다음 명령을 실행합니다.

1
2
3
4
5
6
7
8
9
10
$ cpanm -l /media/3532-3831/sl4a/scripts/perl5 --reinstall -qfn \
    CGI \
    CGI::Util \
    HTTP::Date \
    Time::Local \
    URI::Escape \
    Plack::Builder \
    Plack::App::Directory \
    HTTP::Server::Simple::CGI \
    HTTP::Server::Simple::PSGI

하지만 설치해야할 모듈이 많고, 몇 가지 빠진 기본 모듈도 있는 관계로 쉘스크립트로 만들었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/sh
DST=/media/3532-3831/sl4a/scripts/perl5
CPAN_MIRROR=file:///home/hshong/mirrors/minicpan
 
cpanm --mirror $CPAN_MIRROR --mirror-only FileHandle DirHandle
cpanm --mirror $CPAN_MIRROR --mirror-only -l $DST --reinstall -qfn \
    CGI \
    CGI::Util \
    HTTP::Date \
    Time::Local \
    URI::Escape \
    Plack::Builder \
    Plack::App::Directory \
    HTTP::Server::Simple::CGI \
    HTTP::Server::Simple::PSGI
 
# cpanm이 설치해주지 않는 기본 모듈중 의존성 있는 항목을 직접 복사
mkdir -p $DST/lib/perl5/File/Spec
cp `perldoc -l File::Spec::Unix` $DST/lib/perl5/File/Spec
cp `perldoc -l integer`          $DST/lib/perl5 # SL4A에 없는 Perl 기본 모듈
cp `perldoc -l parent`           $DST/lib/perl5 # SL4A에 없는 Perl 기본 모듈
cp `perldoc -l DirHandle`        $DST/lib/perl5
cp `perldoc -l FileHandle`       $DST/lib/perl5
 
# 필요없는 XS모듈 디렉토리 제거
rm -rf $DST/perl5/lib/perl5/auto
rm -rf $DST/perl5/lib/perl5/autoi686-xxx

우선 --mirror 옵션과 관련된 $CPAN_MIRROR등의 항목은 CPAN을 로컬 미러링해서 사용하는 사용자가 아니라면 필요없습니다. $DST에서 사용하는 경로인 /media/3532-3831는 현재 제가 사용하고 있는 넥서스원의 SD 카드를 마운트했을 때의 경로이므로 실제로 사용하는 안드로이드 기기에 맞는 정보로 변경해주세요. 컴퓨터에 연결했다면 리눅스의 경우 df 명령어로 확인할 수 있습니다.

SL4A에서 확인

자 Plack을 사용해서 작성한 파일서버 스크립트를 실행시켜 볼까요?

running simple server

내부 IP를 확인하고 http://192.168.x.x:8080/으로 접속해보죠.

Plack을 사용한 HTTP 파일 서버

우앙!굳! ;-)

이제 웹서비스를 안드로이드 기기에 구축하고 실행할 수 있게 되었습니다. 참 현재 Android::wifiGetConnectionInfo에 버그가 있어서 내부 IP를 바로 확인할 수 없다는 것에 유의하세요.

정리하며

안드로이드 기기에서 Perl이 돌아간다는 것은 단순히 안드로이드 기기에서 Hello World를 출력할 수 있는 것 이상을 의미합니다. 비록 cpan 쉘을 바로 사용할 수 없다는 것과 XS 모듈의 경우 크로스 컴파일 환경을 갖추지 않는 이상 설치하기가 번거롭다는 점이 아쉽지만 순수하게 Perl로 구현된 모듈이라면 정말 간단하게 설치해서 사용할 수 있습니다. 안드로이드 기기에서 Perl을 사용해 웹서비스를 만들고 서버로 실행시킬 수 있다는 것, 매력적이지 않나요? 자, 이제 필요한 것은 여러분의 아이디어입니다. ;-)