Perl 로 메일을 보내려면 어떻게 해야할까요?

  저도 몰라서 근처 사는 최선생님께 물어봤습니다.

사용자 삽입 이미지

"메일, 그거 삶아먹으면 되는 거 아닙니까?"


.
.
.
라는 것은 훼이크고...

 Net::SMTP 라든가... CPAN 을 찾아보면 Mail 에 관련된 모듈은 널리고 널렸습니다.

 전 MIME::Lite 를 사용하기 때문에... 이쪽으로만 다루겠습니다. :-)

 사실 Korean Perl Workshop 참가신청을 만들 때, 메일 송신 스크립트 때문에 좀 고단했던 적이 있었습니다.
 메일서버도 없고, 사용할만한 건 Gmail 인데... CPAN 에서 Gmail 에 관련된 메일 송신 모듈이 제대로 동작하지 않는 것도 문제였죠. KPW 자체적인 메일 서버도 가지고 있지 않았구요. 시간은 촉박했고...
  설령 메일서버가 있다고 해도... 메이저 포털로 메일을 보낼 때 잘 안된다고 하는 그런 이슈도 있었구요.

 그때 한줄기 빛이 내려왔는데,, 그것이 SSMTP 라는 것입니다.
 
 SSMTP 가 무엇이냐는 자세한 설명은 여기에서 하지 않겠습니다. 구글해주세요.

 우분투에서 간단하게 SSMTP 패키지를 설치할때는...

sudo apt-get install ssmtp

 로 설치가 가능합니다.

  그리고는 /etc/ssmtp/ssmtp.conf 에 아래의 설정을 넣습니다.

root=your.email@gmail.com
mailhub=smtp.gmail.com:587
useSTARTTLS=YES
AuthUser=your.email@gmail.com
AuthPass=your.password
rewriteDomain=gmail.com
FromLineOverride=YES
hostname=blah

 그래서 메일계정은 gmail 을 사용하기로 하고 위와같이 설정했습니다. Gmail 을 사용하면 어디든지 메일을 보낼 수 있기 때문이었죠. Gmail 을 스팸처리하는 곳은 없을 테니...
  MIME::Lite 모듈이 없으면 설치해주세요.
 
use strict;
use warnings;
use MIME::Lite;

my $msg = MIME::Lite->new(
    'Return-Path' => 'sender.email@gmail.comr',
    'From'        => 'sender.email@gmail.com',
    'To'          => 'receive@email.com',
    'Subject'     => 'saillinux 산 버터, 어떻게 생각하시나요?',
    'Charset'     => 'utf-8',
    'Encoding'    => '8bit',
    'Data'        => '사실 그거 삶아먹으면 괜찮은거 아닙니까?'
    );
$msg->send;

 이렇게 스크립트 하나 만들어서 실행시키면...

사용자 삽입 이미지

 이렇게 메일이 오게됩니다. +_+

 뭐, 파라메터 넘겨서 From ,To 바꾸거나, 제목 바꾸거나... DB 에서 긁어와서 집어넣거나... 혹은 Template 를 사용해서 메일 내용을 꾸미거나... 하는 방법도 있겠죠.

 여러가지 다양한 부수적인 내용은 직접 해보시면 됩니다.
 현재 KPW 사이트에서는 KPW::Mail 이라는 Wrapper 모듈을 만들어 두고 사용하고 있습니다. Template 도 만들어서 Mail 내용등은 완전히 분리시켜두고 말이죠.

  Daum이나 Yahoo 에서는 Subject 가 깨지는 현상도 있다고 하는데요. keedi 님께서 이에 대한 처치방법을 알려주셨네요.
  use MIME::QuotedPrint qw(encode_qp);
  use Encode qw(encode);

 필요한 모듈을 use 해서... Subject 를

...
Subject => '=?UTF8?Q?' . encode_qp(encode('utf-8', "블라블라"), '') .'?=',
...
 
 이런식으로 감싸면 된다는 군요. keedi++

 (SSMTP 는 jachin 님으로부터 줏어들어서 saillinux 님께서 설정해주셨습니다. jachin++, saillinux++)


  :: 이상 IRC #perl-kr 에서 h0ney 님께서 "메일 어케날려요?" 라는 떡밥을 날리셔서 썼습니다.
  :: (떡밥주도 블로그 포스팅 전략!!)
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
사용자 삽입 이미지
 
 2008/07/20 - [IT/Gears] - [ iPhone ] 결국은 사버렸습니다!

 지난 7/11일에 발매된 iPhone.. 오늘이 8/11일이니.. 한달동안 써봤다고 생각하면서 간단한 사용후기를 써볼 까 합니다(사실 Referer 가 제일 많은 게 iPhone 이어서... 물타기 한번)

 최근에 Apple 에서 iPhone 2.0.1 firmware 를 내고서부터 약간 안정된 모습을 보여주는 iPhone.

 그 이전에는 어떠했냐면...

 - iPod 음악을 켜고 Safari 에서 한 두페이지 넘어가면 Safari 가 튕깁니다.
 - 물론 Safari 뿐 아니라 다른 App 에서도 마찬가지입니다.
 - 그냥 Safari 켜고 돌아다녀도 튕깁니다.
 - 일본어자판 사용이 극악으로 느립니다.
 - App Store 에서 다운받은 App 들이 실행조차 안될 때가 있습니다.

 MobileMe 같은 건 안써봐서 모르겠지만... 써보신 분들은 느리다고도 하고. 이에 대해서 스티브 잡스도 MobileMe 는 완성된 것이 아니라고 말한 적이 있었지요.

 그냥 냅두고 써도 불안정해서.. 불안함을 극대화시켜볼까 하는 생각으로 jailbreak 를 했습니다.

 ** Jailbreak

 첫번째 jailbreak 에서 통신사 정보를 읽어오지 못해서 휴대폰으로의 사용이 불가능했었죠.
 그래서 다시 밀고 두번째 jailbreak를 했는데.. 그때 iTunes 에서 통신사 정보를 업데이트 하겠느냐는 메시지가 뜨더군요.

 최근에는 MobileInstallation 이라는 파일을 덮어쓰면 유료 App 을 hacking 한 파일을 설치하면 그대로 설치가 된다는 얘기도 있어서 해봤습니다만... 역시 유료 App 에서도 그닥 만족할 만한 App 가 없습니다.

  iPhone 2.0.1 firmware 를 그대로 업데이트 하면 다시는 jailbreak 할 수 없다는 얘기도 나오던데요. 그래서 jailbreak 하지 말라고 하는 얘기도 나와서 그대로 냅뒀습니다. 그리고 주말즈음에 2.0.1 용 jailbreak 가 나와서 jailbreak 했죠.

  여기서 든 생각이... 한국에 iPhone 을 발매하게 되면 분명 firmware 버젼은 2.0.1 이상이 될 것이라는 건데.. 제가 듣기로는 순정 2.0.1 에서 jailbreak 방법이 아직 없는 것으로 알고 있는데요. 한국에서 나중에 발매하게 된다면 이부분이 어떻게 될 지 주목되네요.(아마 깨지지 않을 까 싶지만,...)

  어쨌든 firmware 2.0.1 에서 위에 적었던 내용들이 나아졌느냐? ... 네.. 분명 좋아진 것은 사실이지만..
  아직도 불안정하다는 것도 사실입니다. (App Store 에서 받은 App 가 불안정한지도 모르겠지만...)


 ** 휴대성

  처음에는 때탈까봐 애지중지 하던 iPhone 은 이제 집에서 굴러다니는 군요. 그래도 아직은 실기스 하나 안보일 정도로 제대로 쓰고 있지만... 불안해서 일단 케이스 같은 것을 구입해야되겠더군요.
  일단 저는 청바지 호주머니에 집어넣고 다닙니다. 그러니 자리에 앉을 때는 바지가 땡겨서 혹시 iPhone 이 부러지지 않을까 하는 걱정도 사고 1주일 정도는 해봤습니다만.. 다행히도 아직까지 문제가 없네요.
  이어폰에 달린 마이크로 가끔 쓰기도 하는데... 길거리에서 그거 쓰니까 왠지 혼자서 중얼거리는 게.. 주위의 시선이 여간 부담스러워서 그냥 들고 통화를 합니다.
  배터리는 물론 항상 USB 꼽아두고 살아서... 어떤지 모르겠습니다. 짧기는 짧은 가 봅니다.
  iPhone 을 구입하기 전에는 휴대폰과 iPod touch 를 주머니에 넣고 다녔는데.. 요즘은 iPhone 만 들고 다니면 되니까... 편리해졌다는 것을 느끼고 있기는 합니다. :-)

  ** 3G

  흠.. Youtube 는 3G 네트워크에서 저화질, 저음질을 자랑합니다. 물론 Wifi 네트워크 안에서는 제대로된 음질과 화질을 보장해주죠. 그래서 전 jailbreak 해서 mxTube 를 이용해서 일반적인 Youtube 영상을 다운받아서 보곤합니다. 최근 일본의 Softbank 는 세계에서 처음으로 3G iPhone에 대한 이용요금을 조정했거든요. 무조건 7200엔정도 내는 돈이 최저 2900엔까지 떨어지니... 주의를 기울일 만도 합니다.
  일단 지하철안에서는 통화권이탈이니 출퇴근때는 그냥 iPod 로 이용하고 있는 중입니다. 별시리 밖에서 3G 네트워크를 사용해서 Safari 를 돌린다거나 할 일은 아직까지는 없네요.

  ** App

  App Store 에서 받은 App 들은 GPS 와 중력감지센서를 이용한 App 들이 많습니다.

   * Twinkle 같은 경우는 자신의 반경 1KM - 50KM 안에서의 Twitter 메시지를 송수신 할 수 있죠. Twinkle 의 메시지는 Twitter 의 자기 계정으로 전송되지 않는 것 같더군요.
   * Twitterific 은 그냥 Twitter iPhone Client App 입니다.
   * midomi 는 허밍으로 노래검색이 가능한 App 입니다. 회사에서 틀어놓은 라디오에서 귀에 익은 노래가 나오면 허밍을 통해서 노래를 찾아보고는 합니다. 한국노래는 없는 것 같더군요.
    * PhotoShare 는 말 그대로 PhotoShare 입니다. 재미있는 게 어제 Most Popular 의 사진에 BALLPEN GIRL 이라고.. 예전에 짤방으로 많이 살포되던 볼펜녀의 사진이 가장 인기있더군요.
     * PhoneSaber 는 스타워즈의 광선검입니다.. 지잉지잉 하는 소리와 휘두를때 소리도 나죠. 처음에는 신기해서 휘두르고 하지만.. 나중에는 그냥 그저 그런 App 가 되었죠.

  App Store 를 통해서 하루에 2000달러를 번다는 그런 얘기도 많은데... 사실 제 입맛에 맞는 App 들이 별로 없는 게 아쉽네요. 그래도 한달만에 쏟아져 나온 App 갯수는 상당합니다. 집에 Objective-C 책은 사뒀는데.. 언제 iPhone App 만들어볼까 ...;; 항상 시간탓만 하고 있습니다.

  ** 전화

  처음에 발신자번호도 제대로 안뜨길래 놀랬는데.. 아마도 통신사의 문제였던 듯 합니다. 이제는 전화가 걸려와도 전화번호가 제대로 남더군요.
  Mac 의 연락처나 Windows 의 Outlook 의 연락처 정보를 iPhone 과 동기를 하기때문에... 연락처 관리가 쉽습니다. 하지만 전화로써의 기능은 아쉽습니다.
  일단 전화걸기 위해서 "전화" 버튼을 누르고, 연락처를 열때 딜레이가 좀 있습니다. 2-3초 정도의 딜레이는 감수하면서 쓰고 있습니다. 빠른 전화를 원한다면 SpeedDial 이라는 App 도 생각해볼만 하겠습니다.

  ** SMS, 메일

  iPhone 에서는 일본의 그림문자라든가 .. 그런 것들을 지원하지 않습니다. 물론 저도 쓰지 않으니 별 상관없지만요. SMS 나 Mail 에서 아쉬운 것은 예약 전송입니다. 일단 전파가 안통하는 지하철에서 SMS 를 보내곤 하는데.. 전파가 안들어올 때는.. 전파를 찾을 때까지 계속 SMS 전송 버튼을 누르지 않습니다. 그리고 한두칸 뜨면 그때 버튼을 누르죠. 그런 편리함이 없기 때문에 쓰기가 좀 불편합니다. 이건 Mail 에서도 마찬가지입니다. 이것을 위한 Background 처리가 없는 것 같아서 아쉬움이 많네요. 아마 이부분은 App 로 떼우든가 하는 방법도 있겠지만...

--------
  대충 이정도의 소감입니다. 휴대폰에 대한 사용자 경험 노하우가 별로 없는 Apple 이라서 그런지... 솔직히 휴대폰으로서의 iPhone 은 좀 실망입니다. App 로 땜빵기능 채우는 것도 한계가 있지 않을까 싶기도 하네요.
  솔직히 뭐 iPhone 을 휴대폰만으로 쓰겠다고 산 것도 아니지만... 아무튼 어느정도 만족하고 살고 있습니다. :-)
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
사용자 삽입 이미지

 2주후, 8월 23일에 드디어 Korean Perl Workshop 이 개최됩니다(물론 전 못가지만...).

 세션은 오후 1시부터 저녁 9시까지의 세션이니  8시간입니다. 일반적으로 대충 3-4시간하고 끝나는 중소규모의 컨퍼런스나 Tech Talk 와는 달리 8시간이면.. 꽤나 하드코어한 것이죠.  하드코어한만큼 좋은 내용들이 즐비합니다.

 생물학, 언어학같은 생소한 부분을 비롯해서... Perl 을 이용한 보안/해킹, GUI 개발, 웹 개발...
 상당히 넓은 장르를 한꺼번에 소화할 수 있는 기회가 될 것 같습니다.

 거기에 한국에서는 아마도 처음으로 열리는 Perl 만을 위한 워크샵이니 많은 기대를 하고 있습니다. KPW2008 의 주제에 맞게 다시금 Perl 이 떠올라서 CGI 로만 결부되는 인식을 무참하게 깨뜨렸으면 하네요.

 자세한 정보는 KPW2008 홈페이지 를 참조하세요.

(사실 예전에 있었던 2008/06/18 - [IT/Perl] - * 제 1회 Korean Perl Workshop : Rising Perl 개최 * , 이것이 연기된 것입니다.)
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN

 현재 개발중인 프로젝트에 쓰이는 내용을 살짝 다뤄볼까 합니다.

 음... 테이블의 칼럼 수가 많거나, 적거나.. 아직 정해지지 않아서 나중에 추가될 지도 모른다거나... 이런 불안함에서 제대로 테이블을 만들더라도, 나중에 ALTER TABLE 의 폭발을 겪게 되는 경우가 있었습니다. 컬럼을 추가하거나, 지우거나, 바꾸거나 하면서 불안함이 있다는 것이죠. 그래서 YAML 을 그대로 집어넣게 되었습니다.

 이럴때 가볍게 쓸 수 있는 것이 inflate_column 입니다.
 위의 얘기는 잠시 접어두고 그럼 예제를 볼까요? MyTest::User 에 다음과 같은 것을 추가했습니다.

 __PACKAGE__->inflate_column(<br />    user_id => {<br />        inflate => sub {<br />            my ($value, $obj) = @_;<br />            # $obj->$column_name 으로 User Schema Class 의 컬럼을 참조할 수 있습니다.<br />            sprintf "%04d", $value;<br />        },<br />        deflate => sub {<br />            my ($value, $obj) = @_;<br />            $value;<br />        },<br />    });

  이것으로 무엇을 하느냐...

  User 의 user_id 컬럼을 지정해서 user_id 를 처음부터 건드려놓은 상태로 내놓거나(inflate), Table 에 입력을 할 시에도 마찬가지로 입력 직전에 건드린 상태로 쓰기를 수행(deflate) 하는 겁니다.

  위의 예제에서는 User 의 user_id 를 zero-fill 합니다. 1 이라면 0001 이라든가.. 이런 형식으로 나오겠죠?

my $user = $schema->resultset('User')->search({});

while(my $row = $user->next) {
    print $row->user_id."\t".$row->name."\n";
}

  그럼 한번 뽑아볼까요?
사용자 삽입 이미지
  자, 이렇게 나오네요.

  이렇게해서, 예를들면 datetime 의 data type 을 가지고 있는 created_on, updated_on 은 DateTime 인스턴스로 만들어 줄 수도 있겠죠.

  제가 사용하는 경우는 ...

CREATE TABLE test (
   no    INT NOT NULL,
   attributes   TEXT NOT NULL,
   created_on DATETIME NOT NULL,
   updated_on DATETIME NOT NULL,
   primary key(no)
 );

  이런 Schema 가 있다면...

use YAML;
__PACKAGE__->inflate_column(
  attributes => {
     inflate => sub {
        my ($value, $obj) = @_;
        YAML::Load($value);
      },
      deflate => sub {
        my ($value, $obj) = @_;
        YAML::Dump($value);
       },
});

  이렇게 inflate_column 을 설정해두었습니다. 그리고 이런식으로 등록합니다.

 my $obj = {
    irc => {
       perl => {
         saillinux => {
           envy => 'yuni',
           attack => 'JEEN',
           love  => [ 'ero', '하얀_고양이', 'Fate' ],
          },
        },
    },
 };
 
 $schema->resultset('Test')->create( attributes => $obj );

 이렇게 등록된 데이터는 YAML 형식으로 보관되어 있을 겁니다.
 그리고 빼내서 쓸려면 이런식으로 합니다.
 
my $rs = $schema->resultset('Test')->search({})->first;
print $rs->attributes->{irc}->{perl}->{saillinux}->{envy};  # yuni
print for (@{ $rs->attributes->{irc}->{perl}->{saillinux}->{love} }); # ero 하얀_고양이 Fate

  이렇게 사용합니다. :-)

  inflate_column 를 사용하지 않는 경우에는 집어넣을 때는 매번 YAML::Dump 하고...
  뺄때는 매번 YAML::Load 해야 되겠죠.

  이렇게 가볍게 InflateColumn 에 대해서 알아보았습니다.
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN

 자, 그럼 Relation 에 대한 이야기입니다.

 Relation 은 말그대로 관계입니다. 뭐에 대한 관계냐면, Schema Class 간의 관계이죠.
 
 User, Company, Deathnote 에서 Deathnote 는 User 의 user_id 와 Company 의 company_id 를 가지고 있습니다.
 그럼 어떻게 Deathnote 에서 User 의 name 을 알 수 있을까요?
 ( SQL 스럽게 하면 애들장난같은 얘기라서 접어두겠습니다만 )

  아, 그전에 일단 테스트 용으로 데이터를 집어넣겠습니다.

my $deathnote = $schema->resultset('Deathnote');

while(<DATA>) {
    chomp;
    my @data = split/,/, $_;
    $deathnote->create({
        user_id => $data[0],
        company_id => $data[1],
    });
}

__DATA__
1,1
2,2
3,3
4,4
5,5
6,6
7,7

  자.. 어떻게 할 것인가... 가장 간편하게 이런 방법이 있습니다.
my $deathnote = $schema->resultset('Deathnote')->search({});

while(my $row = $deathnote->next) {

    my $user = $schema->resultset('User')->find($row->user_id);
    print $user->name if $user;
}
 흠? 근데 이건 좀 너무하다 싶지 않나요?

 이렇게 생각하면 Relation 이 나올 때입니다.
 MyTest/Deathnote.pm 을 보겠습니다. 아, 다른 건 다 집어치우고... 그냥 추가된 것만 보죠.

__PACKAGE__->has_one( user => 'MyTest::User', { 'foreign.user_id' => 'self.user_id' } );
__PACKAGE__->has_one( company => 'MyTest::Company', { 'foreign.company_id' => 'self.user_id'
} );

 이렇게 추가했습니다.
  Deathnote 에서 User 로 연결하기 위해서 user 라는 키를 사용하고,
User 와 Deathnote 는  user_id 로 연결된다는 내용입니다. (has_one 이라는 것은 말 그대로 1:1의 관계입니다)
  그리고 Company 역시나 마찬가지입니다.
  이렇게 Relation 을 지정해두면 어떻게 되려나요?

 my $deathnote = $schema->resultset('Deathnote')->search({});

while(my $row = $deathnote->next) {
  print $row->user->name."¥n";
}

  이런 형식으로 간단하게 user의 name 을 얻어올 수 있습니다.
.
.
.
  정말로 그럴까요? 두번째 장에서 하얀_고양이라는 User 를 삭제했기 때문에 세번째 Deathnote 에서는 에러가 발생합니다.

  can't call method "name" on an undefined value at ..

 그러면 이렇게 해주면 됩니다. user 가 있으면 찍어라 라고...

   print $row->user->name if $row->user;

  간단하죠;;;

 근데 User 가 어떤 Company 인지를 알고 싶습니다. 근데 이 정보는 Deathnote 에만 있어요.
 User->Deathnote->Company 식으로 넘어가야되는데요...

  우선은 MyTest/User.pm 을 열어서 한 줄 추가합니다.

__PACKAGE__->belongs_to( note => 'MyTest::Deathnote', { 'foreign.user_id' => 'self.user_id' });

  belongs_to 라는 것은 말 그대로입니다. :-) Deathnote 에 User 가 has_one 으로 Relation 관계를 성립하고 있습니다. belongs_to 로 Deathnote 에 속하게 됩니다. (근데 has_one 으로도 Relation 관계가 성립되더군요;;; 뭔가 일방적인 관계일지도..)

  그러면 User 에서 Company 의 name  을 읽어 볼까요?

my $user = $schema->resultset('User')->search({});

while(my $row = $user->next) {
   
    print $row->name .'=>'.$row->note->company->name."\n" if $row->note && $row->note->company;

}

  이렇게 하면 되겠죠?

  근데 여기서 잠깐... "언제까지 전체 레코드를 읽어올 것인가요. 제대로 SQL 조건도 좀 주고.. 이러고 싶은데.." 라고 saillinux 님께서 사장님 마인드로 또 말씀하십니다.

  그러면서 던져주신 조건이란게 << user_id 가 3 을 넘고, company_id 가 2 를 넘는 것 >> 이라고 하네요.
 근데 User Schema Class 를 사용하라고 합니다. 어라... 분명 User Schema Class 에는 company_id 가 없는 데 말이죠. 역시 사장님 마인드입니다.

  "거기다가 company_id 순으로 정렬하고, 그 중에 딱 2개만 보고 싶어요" 라고 하시는 군요...
  오늘 제대로 뽕을 뽑으시는 군요.

   요건 정의를 다시 해보겠습니다.

    "user_id" 가 3 을 넘고,
    "company_id"  가 2 를 넘고,
    "User" Schema Class 를 사용하고,
    "company_id" 순으로 정렬하고
    "2개" 만 출력한다 입니다.
.
.
.
.
  답은 이렇습니다.  

my $user = $schema->resultset('User')->search({
    'me.user_id' => { '>' => 3 },
    'company.company_id' => { '>' => 2 },
},{
    prefetch => [ { 'note' => 'company' } ], 
    order_by => 'company.company_id asc',
    rows => 2,
});

while(my $row = $user->next) {
    print $row->name. '=>' .$row->note->company->name."\n";
}

  User Schema Class 에서 Company 와는 어떤 관계도 아닙니다. 그럴려면 Deathnote 를 통해서 Company 에 접근할 수 있습니다.
 
   prefetch => [ { 'note' => 'company' } ]

  MyTest::User 에서 note 로 Deathnote 에 갈 수 있고, Deathnote 는 company 로 Company에 접근할 수 있습니다.  Relation 관계는 이미 Schema Class 에서 정의해줬기 때문에 이렇게 간단하게 접근할 수 있습니다.

    order_by => 'company.company_id asc',

  company_id 순이라면 위의 prefetch 에서 이미 길을 텄기때문에 "company.company_id"  로 접근할 수 있습니다.(SQL::Abstract 를 참조하세요)
 
      rows => 2

  단 두줄만 뺀다는 겁니다. MySQL 의 LIMIT 2 같은 사용법이죠.

  물론 Relation은 might_have, has_many 같은 것들도 있습니다. 자세한 내용은 문서를 참조하세요. :-)

  이런저런 빼고 넘어가는 부분들이 많은지라... 조금만 문서를 보시면 금방 아실만한 내용들이 대부분일 겁니다. 모르시면 같이 한번 궁리해보죠 :-)

  흡.. 빠진 부분들(제가 아는 한도에서)은 다음에 DBIC 를 정리하는 차원에서 가볍게 짚고 가도록 하겠습니다.


이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
  이번에는 Component 에 대해서 알아보겠습니다. 일단 예제부터 보고 시작하죠.

  Mytest/Company.pm 을 열어봅니다.

package MyTest::Company;

use strict;
use warnings;

use base 'DBIx::Class';
use AutoStoreDateTime;

__PACKAGE__->load_components("PK::Auto", "+AutoStoreDateTime", "Core");
__PACKAGE__->table("company");
__PACKAGE__->add_columns(
  "company_id",
  { data_type => "INT", default_value => undef, is_nullable => 0, size => 11 },
  "name",
  { data_type => "VARCHAR", default_value => "", is_nullable => 0, size => 255 },
  "created_on",
  { data_type => "DATETIME", default_value => "", is_nullable => 0, size => 19 },
  "updated_on",
  { data_type => "DATETIME", default_value => "", is_nullable => 0, size => 19 },
);
__PACKAGE__->set_primary_key("company_id");


# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-08-05 16:39:27                      
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:DHRIea3OvvtGG+yhaBVTvQ                        


# You can replace this text with custom content, and it will be preserved on regeneration    
1;

 이 중에서 제가 살짝 추가한 부분이 있습니다.

use AutoStoreDateTime;
__PACKAGE__->load_components("PK::Auto", "+AutoStoreDateTime", "Core");

 바로 이부분에 PK::Auto 라는 component 도 불러오라고 지정했습니다. PK::Auto 는 온라인 게임에서 자동으로 PK 를 수행하는   Primary Key Auto Increment 라고 보시면 됩니다. 즉 이 Schema Class 에서는 company_id 가 Primary Key 이니까 자동으로 Auto Increment 해준다는 거죠. (물론 SQL 에서 Auto Increment 를 지정해주면 DBMS 자체적으로 이 행위를 수행하지만, auto increment 가 지정되지 않은 경우에는 PK::Auto가 알아서 해줍니다. 이것을 확인하기 위해서 직접 Company 테이블을 변경해보시길 바랍니다)

  그리고 AutoStoreDateTime 도 있습니다. 근데 이 앞에 +라고 붙었는데.. 이것은 Custom component 입니다. PK::Auto 같은 경우는 DBIx::Class::PK::Auto 라는 모듈을 불러오는 것인데, AutoStoreDateTime 은 DBIC 정식 모듈이 아닌... 여기서 만들어서 쓰겠다라는 것이죠.
use AutoStoreDateTime;

use strict;
use warnings;
use base 'DBIx::Class';

use DateTime;

sub insert {
    my $self = shift;
   
    $self->created_on( DateTime->now( time_zone => 'Asia/Seoul' ) )
                             if $self->result_source->has_column('created_on');
                      
    $self->next::method(@_);
}

sub update {
    my $self = shift;

    $self->updated_on( DateTime->now( time_zone => 'Asia/Seoul' ) )
                           if $self->result_source->has_column('updated_on');
    $self->next::method(@_);
}

1;

  이와 같은 컴포넌트 모듈을 만들었습니다. 이것을 AutoStoreDateTime.pm 이라고 저장하고 MyTest.pm 과 같은 디렉토리에 놓습니다.
  그럼 어떤 결과를 보이는 지 확인해 볼까요?
my $company = $schema->resultset('Company');

while(<DATA>) {
    chomp;
    $company->create({
        'name'    => $_,
    });
}

__DATA__
I company
N company
D company
E company
A company
Y company

  이렇게 Company.name 을 만들도록합니다. company_id 도 지정하지 않았고, created_on 도 지정하지 않았습니다. 등록날짜도 모르면 어떻게 하려고 이럴까요?
사용자 삽입 이미지
  하지만 이렇게 created_on 은 create 한 시점의 날짜/시간을 가지고 있습니다. company_id 도 물론 지정하지 않았는데 이렇게 생겼네요(SQL에서 auto increment 가 있으면 뭐 그게 그거입니다만).

 근데 사장 마인드를 가지신 saillinux 님께서 짝수 회사번호를 가진 회사는 전부 소문자로 표기하고 싶다는 얼토당토 않은 요구를 하고 있습니다. 어쩔 수 있나요. 까라면 까야지..

my $company = $schema->resultset('Company')->search({});

while(my $row = $company->next) {
    if ($row->company_id % 2 == 0) {
        $row->name(lcfirst $row->name);
        $row->update;
    }
}

  이렇게 하면 되겠죠?
사용자 삽입 이미지
  그러면 update 된 레코드만 updated_on 이 지정되게 되죠.  어때요? 참 쉽죠?

 이렇게 DBIC 의 Component 활용에 대해서 알아보았습니다. :-) 직접 Component 를 만들어서 보다 편하게 DB 를 주물러 보심은 어떠실런지요?

  다음에는 Relation 에 대해서 살짝 알아보겠습니다. 

* 참고
 - **** Project



이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN

  그럼, DBIC::Schema::Loader 로 만든 Schema Class 는 어떻게 사용하는 것일까요?

use strict;
use warnings;
use MyTest;  # 앞서 정의한 Schema Class
use DateTime;

my $schema = MyTest->connect(
    'dbi:mysql:mytest',
    'root',
    'PASSWORD');  # DBI 의 connect_info 랑 같습니다.

my $user = $schema->resultset('User');

while(<DATA>) {
    chomp;
    my @data = split /,/, $_;
    $user->create({
        'user_id' => $data[0],
        'name'    => $data[1],
        'created_on' => $dt->ymd. ' '. $dt->hms,
    });  # INSERT INTO..
}

__DATA__
1,JEEN
2,saillinux
3,하얀_고양이
4,a3r0
5,yuni
6,keedi
7,pung96
8,song
9,ssie
10,amorette

  이 스크립트를 MyTest.pm 과 같은 장소에 놓으시고... 적당한 이름으로 저장해서 실행해봅니다.
  아무것도 안나올겁니다. 그렇다면... DB 를 확인해볼까요?
사용자 삽입 이미지
이렇게 제대로 들어가 있네요 :-)
그러면 이렇게 입력된 데이터를 가지고 놀아 볼까요?

use strict;
use warnings;
use MyTest;
use DateTime;

my $schema = MyTest->connect(
    'dbi:mysql:mytest',
    'root',
    'PASSWORD');

my $user = $schema->resultset('User')->search({
    'user_id' => { '<' => 5 },
});  # SELECT * FROM user WHERE user_id < 5;

while(my $row = $user->next) {
 print $row->name."\n";
}
 위의 주석처럼 user_id 가 5보다 작은 사람들을 불러모읍니다. :-)
 JEEN
 saillinux
 하얀_고양이
 a3r0
 결과는 이처럼 나오겠죠?
 
  ->search 로 데이터를 줏어올 때는 기본적으로 복수로 가정합니다. 그러니 $user->next 같은 것으로 루프를 돌려주는 것입니다. $user->all 같은 것을 사용하면 말 그대로 전체 데이터를 ARRAY 로 받습니다.

 여기에서 생각난 예제:
 근데 전 하얀_고양이 님을 별로 좋아하지 않습니다. 그래서 지워버리고 싶어요.

use utf8;

my $user = $schema->resultset('User')->find({
    'name' => '하얀_고양이',
});    # find 를 사용할 경우 하나의 레코드만을 얻어옵니다.

print $user->name;
$user->delete;
 
  혹은
use utf8;
my $user = $schema->resultset('User')->search({});

while(my $row = $user->next) {
  $row->delete if $row->name eq '하얀_고양이';  # 이렇게도 삭제할 수 있죠 :-)
}

  이렇게 해서 하얀_고양이님을 지웠습니다.
  그러니 IRC #perl 에 계신 분들이 모두 기뻐하셔서 빵글이를 붙이고 싶다는 데요. :-)
  그럼 어떻게 할까요?
my $user = $schema->resultset('User')->search({});

while(my $row = $user->next) {
    $row->name($row->name . '_^^');
    $row->update;
}
 
사용자 삽입 이미지

 과연.. 모두들 기뻐하시는 군요 :-)

 이런식으로 직감적으로 DB의 데이터를 조작할 수 있는 것이 바로 DBIC 의 강점입니다.
 이에관한 예제는 DBIx::Class 페이지에서 쉽게 찾아볼 수 있고, 보다 다양한 예제를 제공해줍니다.
  그리고 보다 자세한 SELECT 구문에 대한 예제는 앞으로도 계속 적어나가도록 하겠습니다.

  다음번에는 Component 에 대해서 간단하게 알아보겠습니다. 

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
: 요 근래 잠깐 깜빡하고 Catalyst 에 관한 글을 쓰지도 못하고 있었네요.

일단 그전에 미리 Template Toolkit 같은 Template Engine 이나
DBIx::Class 같은 ORM 같은 것을 알아놓고 가는 게 좋겠다는 생각에...
잠시 얘기가 빙빙 겉돌고 있습니다.

 일단 DBIx::Class, 통칭 DBIC 라고 불리는 것부터 알고 나중에 차근차근 Catalyst 내용을 이어가도록 하겠습니다.

 ORM 이라고 아시죠? Object-Relation Mappings 라고...
 자세한 내용은 검색해보시면 짠 하고 나올겁니다. ( 검색 예 )

 Perl 에서 ORM 을 얘기하면 DBIC 가 대표적인 예가 될 것입니다. 그외 여러가지 있지만 안써봐서 생략합니다.

 DBIC 를 제대로 사용하려면 Schema 클래스를 만들어야 합니다.

 만들어야한다에 대한 제대로 다시 정의를 하자면, "만들게 해야합니다" 입니다.

 DBIx::Class::Schema::Loader 가 이 일을 해주는데요. 그런고로 한번 만들게 해보겠습니다.
 CREATE TABLE user (
   user_id  int not null auto_increment,
   name    varchar(255) not null,
   created_on datetime not null,
   updated_on datetime not null,
   primary key(user_id)
 );

 CREATE TABLE company (
   company_id int not null auto_increment,
   name           varchar(255) not null,
   created_on datetime not null,
   updated_on datetime not null,
   primary key(company_id)
 );

 CREATE TABLE deathnote (
   death_id int not null auto_increment,
   user_id   int not null,
   company_id int not null,
   created_on datetime not null,
   updated_on datetime not null,
   primary key(death_id)
 );

 이와같은 SQL 을 깨작거렸습니다. 우선 mytest 라는 DB 를 만들고 위의 user 와 company와 deathnote 라는 테이블을 생성합니다.

use strict;
use warnings;

use DBIx::Class::Schema::Loader (qw/make_schema_at/);

make_schema_at(
    'MyTest', {
        components => [],
        dump_directory => './',
    },
    \@ARGV,
    );

 그리고 이와같은 스크립트를 만들었습니다.(schema-loader.pl)

 perl schema-loader.pl dbi:mysql:mytest "mysql user" "mysql pass"

 그런 다음에는 이렇게 명령을 내립니다. (DBI 에서 connect_info 설정해주는 그것입니다)

Dumping manual schema for MyTest to directory ./ ...
Schema dump completed.
  그러면 이렇게 Schema dump 가 끝났다고 말해주죠.
 
사용자 삽입 이미지
  그러면 이처럼 Schema 클래스가 생성되게 됩니다.

package MyTest;

use strict;
use warnings;

use base 'DBIx::Class::Schema';

__PACKAGE__->load_classes;


# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-08-05 16:25:30        
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:P0OGC/gBKsOEyhe8yV+xsA          


# You can replace this text with custom content, and it will be preserved on re\
generation                                                                     
1;

  MyTest.pm 은 이처럼 썰렁합니다만..

package MyTest::User;

use strict;
use warnings;

use base 'DBIx::Class';

__PACKAGE__->load_components("Core");
__PACKAGE__->table("user");
__PACKAGE__->add_columns(
  "user_id",
  { data_type => "INT", default_value => undef, is_nullable => 0, size => 11 },
  "name",
  { data_type => "VARCHAR", default_value => "", is_nullable => 0, size => 255 },
  "created_on",
  { data_type => "DATETIME", default_value => "", is_nullable => 0, size => 19 },
  "updated_on",
  { data_type => "DATETIME", default_value => "", is_nullable => 0, size => 19 },
);
__PACKAGE__->set_primary_key("user_id");


# Created by DBIx::Class::Schema::Loader v0.04005 @ 2008-08-05 16:25:30                      
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wV7hRdze6zdTrll+QAg4fw                        


# You can replace this text with custom content, and it will be preserved on regeneration    
1;

  MyTest/User.pm (MyTest::User) 는 이처럼 속이 꽉찼습니다. 처음에 SQL 로 정의해준 내용들이 그대로 정의되어 DBIC Schema 를 구성하게 됩니다.

  Catalyst 에서는 위의 스크립트(schema-loader.pl)를 쓸 필요없이...
./script/[App]_create.pl model MyTest DBIC::Schema MyTest create=static dbi:mysql:mytest *** ****
 이런식으로 하면 Schema Class 가 생성됩니다.

 다음에는 DBIC 를 이용해서 어떻게 데이터를 다루는 가에 대해서 알아보겠습니다.

 -- 참고
 * DBIx::Class
 * DBIx::Class::Schema::Loader
 
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
  Perl로 작업하다 보면(뭐, 굳이 이야기하면 Perl 뿐만 아니라), 단발에 끝나는 작업들이 많이 있습니다.

  주로 데이터 집계나 해석용으로 만드는 간단한 스크립트들인데요. 일때문에 서둘러 만들어서 쓰고는 별도로 관리하지 않고 그대로 놔둡니다. 그리고는 시간이 지나서... "이거 뭐하는 거지?" 라고 물음표를 세 번정도 날리고... "필요없나보다" 하고 지워버립니다.

  그렇게 지우고서, 집계 소스 데이터는 또 덩그러니 놔둡니다. 그것도 나중에는 어디에 쓰는 건지 몰라서 또 지워버리죠.

  (주로 단발에 끝나는 스크립트들은 대개 이름조차도 성의없는  d.pl, e.pl, a.pl, blah.pl 등등등 입니다)

  그리고는 시간이 지나고, 다시 비슷한 작업을 할 때가 오고... "어라 이거 예전에 했던 건데" 하고는 예전에 작업한 곳들을 뒤져봐도 답이 안나옵니다. 그리고 또 다시 만들고... 이렇게 시간을 버리는 것이죠.

  프로젝트 돌아가는 Repo 서버에 집어넣는 것도 좀 그렇다 싶어서...
  요즘에는 아예 개인적인 Repo 를 만들어버렸습니다.

사용자 삽입 이미지

  (근 1년새에 Google Code 에 만든 Project 들인데요... 대부분 Ownership 이 되어있어서;; Project Owner 아니면 참가도 안하는 놈으로 낙인이 찍혀버렸습니다)

  일단 적당한 분류법도 정해놓지 않았습니다. 일단 코드가 있다면 적당한 주석을 집어넣고 일단 닥치는 대로 커밋해버립니다. 분류는 자료가 모인다음에 해도 늦지 않을 거라고 생각해서 입니다.
 
  커밋을 위한 어떤 기준도 없습니다. 그냥 10줄 이상의 코드라면 일단 커밋하고 본다는 것이죠. 기준같은 것이야, 어느 정도의 규모가 되고나서 생각해도 된다고 생각해서 입니다.

  사실 이런식으로 일단 커밋하고 보자는 게 일본의 Repository 공유 Project 인 CodeRepos 인데요.
  CodeRepos 를 보고 있으면 사람이 많아질 수록 어떤 기준과 분류가 생겨서 자유로움이 없어지는 경향이 있어서요. 거기에 여러 사람이 있으면 지켜야할 룰이 있어야 하는 것은 당연하게 되죠.

  그래서 다른 사람 눈치볼 필요도 없고, 룰따위도 전혀없이 제 맘대로하는  순전 100% 개인용 Repository라는 겁니다.(사실 개인 서버를 Repo 서버로 할 수도 있지만 Trac 같은 서비스 올리는 것도 싫고 해서.. Google Code 를 골랐습니다)

  그리고는 하루에 두서너개씩 커밋을 합니다. 개인용 스크립 뿐 아니라, 업무에서 사용하는 다른 사람들 스크립트도 마구잡이로 끌어들이고 있고... 웹에서 줏어들은 스크립트도 모으고 있습니다.

  뭐, 그냥 이러다가보면 언젠가 도움되겠지 하는 막연한 생각이 드는 것이라고 할까요?

  (근데 Google Code 에서 자기 Project 의 소스검색이 안되네요? -- 안된다기 보다는 일반적인 검색방법과 다른걸까나.. 사용법을 잘 모르겠네요. 이러면 Gonzui 같은 소스검색기를 도입해야 되는건가?)

  (결국은 혼자서 중얼중얼 이었습니다)
이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
사용자 삽입 이미지

 지난 11일에 iphone 이 발매되었고... 그 뒤에 침묵의 기간이 지나고.. 드디어 오늘 iPhone 을 구입했습니다.

 사실 어제 17시 쯤에 신주쿠 사쿠라야에서 마지막 한대 남은 iPhone 을 계약하려고 했는데... 심사통과에 실패했었습니다.
 (소프트뱅크에서 제시하는 심사기준은 외국인일 경우 비자 26개월 이상일 것.. 혹은 크레딧 카드와 보험증을 가지고 있을 것..., 그외 여러가지 기준이 있지만 제가 해당되는 것은 이 두개니까요..)
 요즘 요금떼먹고 토끼는 사람들이 많아서 그런지 최근들어 이런 심사들이 까다롭게 바뀌었다고 하더군요. 아무튼 어제는 크레딧 카드와 보험증으로 심사통과에 실패해서, 오늘 아침에 다시 오겠다고... 팔지말라고 언질을 주고 집으로 왔었습니다. 그리고 오늘 아침 다시 갔었죠.

 계약후 iPhone 을 집어들고 난 다음의 허전함이라고 할까요? 아아.. 내가 이것때문에 이렇게 맘졸이고 있었던 가 하며 지난 시간들을 되새겨 봅니다.

사용자 삽입 이미지

 뭐 내용물은 다들 아실 겁니다. 전원탭, USB, 번들 이어폰, 그리고 액정 닦개...
 사쿠라야 포인트가 쌓이길래 그걸로 포인트 액정 필름 하나 샀습니다.

사용자 삽입 이미지

 헙.. ipod touch 랑 비교해 봤습니다. iphone 이 약간 더 기네요. 거기에 금테 두른 iPhone 의 뽀대가 더 살아납니다. 뭐.. 뒷면은 ipod touch 의 금박 뽀대가 더 쎄긴하죠. 참고로 전 하얀색 16GB 였습니다.
사용자 삽입 이미지

 두께를 보면 얼추 두배차이는 나는가 싶기도 합니다. 아아.. 급하게 찍어버린다고 화질이 좀 짱이네요.

사용자 삽입 이미지

 그리고 요즘 소프트뱅크 모바일에 가입하면 이런 개 스트랩을 줍니다. 누르면 걸걸한 아저씨 목소리가 나오는 데요. 요근래 인기라고 하는 소프트뱅크 CM 의 아빠 개 캐릭터입니다. 다섯가지 정도 목소리가 나옵니다(참고로 중국제입니다).

 역시 전화로 사용하기에는 불편합니다. 이건 확실하더군요. iPhone 이니 전화가 메인이라고 생각할 법도 한데... 아무튼 약간 불편합니다. 신호도 집에서는 달랑 두칸 잡히네요. 기존에 가지고 있는 게 풀로 다 뜨는데 말이죠.

 그리고 SMS 에서 주소록을 읽어올 때 약간 딜레이가 기네요. 다른 몇몇 부분에서도 느리기는 합니다만...

 만족여하는 더 써봐야 알겠네요. 기존에 들고다니던 벽돌 핸드폰은 이제 그만 써도 되겠습니다. 바지주머니 볼록해질일이 없겠고, ipod touch, phone 따로 들고다닐 일도 없겠네요 :-)


이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN