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
사용자 삽입 이미지

 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

 자, 그럼 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
사용자 삽입 이미지

실용주의 프로그래머(프로그램 프로그래밍 프로그래머 2) 상세보기
앤드류 헌트 지음 | 인사이트 펴냄
소프트웨어 공학 전문서. 이 책은 프로그램 개발의 고수들이 들려 주는 프로그램 비법서로실용적인 측면에서의 프로그램 마스터방법을 알려준다. 코드 중심이라기 보다는 프로그래머의 전반 활동에 대해 나이든 프로그래머가 들려주는 격언 같은 느낌이 들게 재미있게 구성했으며 '어떻게' 보다는 '왜'를 생각하게 한다. 어떤 언어를 쓰고, 어떤 분야를 다루든지 상관없이 단기간에 가장 실제적인 효과를 얻을 수 있을 것이다.

  요 근래에 2판인가... 새로운 양장본으로 나온 듯하지만... 사실 이 책은 일본 오기 전에 구입하고서 구석에 짱박아 둔 책들 중에 하나였습니다. 학생때 읽었었는데, 그때 깨진 유리창 법칙을 처음으로 알게되었던 것이 이 책이었죠.  아쉽게도 그때는 이 책이 와닫지 않았다고 해야될까요? 그렇게 프로그래밍에 빠져있지 않아서 그랬는지 몰라도...
 일본에서는 "달인 프로그래머"라는 제목으로 나왔고, 저자중 한 명인 데이빗 토머스는 예전에 Dan Kogai 씨의 인터뷰에서 본 적이 있습니다.
 
 책에서 저자들은 Perl 을 주로 언급하면서 절대적인 신뢰를 보여주고 있지만, 요근래 그들은 Ruby에 빠져있다고 하는 역자 주와,  인터뷰 내용들 등등등... 무엇이 그들을 Perl 에서 Ruby 로 빠져들게 만들었을까 생각하곤 합니다. 그만큼 Ruby가 매력적일까? 하는 그런 생각을요..

 예전에 읽은 "사랑하지 않으면 떠나라"라는 책에서도 이 책이 언급되고, 다른 여러 책들에서 인용되거나 할 정도로 훌륭한 팁들과 철학들이 곳곳에 배겨있습니다. 개선할 수 있는 업무적인 공백을 채우고, 팀원들간의 커뮤니케이션은 제대로 하고... 등등의 교과서적인 삶이죠. 하지만 사람은, 업무는, 프로그래밍은 교과서적으로 하기에 얼마나 힘든 가 하는 현실... 그래도 당장이라도 현실에, 나의 버릇에 반영해볼 수 있는 팁들은 해보자 하는 생각이 들게 끔 합니다.

 요즘 실용이라는 이름이 어떤 사람(혹은 어떤 사람들)때문에 참 많이 더러워져버렸습니다. 제대로 된 실용적인 개발과 삶을 살아보길 기원해봅니다.


이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
사용자 삽입 이미지

  집에 있는 읽은 책 중에 소개를 하지 않은 책이 여럿 있네요. 그래서 시간도 있고, 공부는 하기 귀찮고 해서 이런 책들에 대해서 소개해드리고자 합니다.

  보시다시피 Perl 에 관한 컬럼들을 보아놓은 책(혹은 잡지) 입니다. vol.1 이라고 해놓은 것을 보면 차후 계속될 여지를 남겨두고 있는 책이죠.

  한국에서는 이미 구시대의 유물로 취급받는 Perl 은 일본에서는 상당한 메이저취급을 받고 있습니다. 사장님들 마인드로는 PHP 처럼 결과가 빨리 보이는 그런 것이 좋을 지도 모르죠. 하지만 요즘에 와서는 Perl 이라고 개발속도가 느리다거나 하는 것은 역시 언어도단이라고 개인적으로 생각하고 있습니다.(물론 소규모에서는 PHP가 이길 수 있겠죠)

  이 책에서는 표지에도 나타나 있듯이 Perl 의 대표적인 프레임워크에 대해서 이야기하고 있습니다.
  Catalyst 와 Sledge 와 Jifty 인데요. Perl 을 사용해 보신 분들이라면 Catalyst 나 Jifty 는 들어보셨겠지만, Sledge 는 그렇지 않으신 분들이 계실겁니다. 이것은 일본 고유의 Perl 프레임워크이기 때문이죠. 이미 더이상 유지보수가 되지 않는 것처럼 보이기는 하지만 꽤 많은 사람들이 암암리에 쓰고 있는 것으로 알려져 있습니다.

  Perl 의 프레임워크 이외에도 Perl6 에 대한 이야기와 Parrot 에 대한 간단한 이야기도 다루고 있습니다.
  아마 근래에 Perl 에 대한 기대를 가지고 계신 분이라면 Perl6 와 Parrot 이라고 생각하고 있습니다.

  그리고 기본적인 Perl 사용법에 대한 설명이 함께합니다. 2바이트 언어권의 숙명인 Encode 와 CPAN 모듈에 관한 이야기로 Perl 에 대한 간단한 입문을 마칠 수 있구요.

  그리고 간단히 WEB API 를 사용하는 방법과 XML/JSON 등을 다루는 방법들이 소개됩니다.
  대표적인 Perl 의 RSS Aggregator 프로젝트인 Plagger 에 대한 입문기사도 있습니다.

  거기에 현재 일본의 대표적인 WEB 2.0 서비스라는 Hatena BookMark 와 Livedoor Reader 의 구성에 대한 소개도 함께합니다. (이 구성에서 Perl 은 필수라는 것이죠)

 이 책을 지은 필진들은 실제 저런 서비스를 만들거나 모듈을 만드는 대표적인 일본의 Perl Hacker 들입니다. 그리고 이들은 대표적인 블로거라고도 불리울 수 있죠.

 서로간의 정보공유 차원에서 새로운 기술들에 대한 실험과 그에대한 이야기를 늘어놓고... 그런 것이 이슈가 되면 많은 사람들이 모여서 그에 대해 토론하고 발전시키고... 그리고 그렇게 쌓인 지식들은 이렇게 출판의 재료가 됩니다.

 이만한 정보들을 원하는 소비자가 있으니 찍어낼려는 생각이 있는 것이겠죠.
 정보를 사용하는 사람이면서 생산자라는 WEB 2.0 이라는 개념은 결국은 저런 정보들을 엮은 책으로 출판까지 이어집니다. 한국에서는 WEB 2.0 이라는 이름만을 빌리고 대충 새로운 패러다임이라느니 새로운 추세라느니 하면서 허울좋은 소리만 하는 기획자들의 뻘글들이 책으로 나오는 것과는 다르게 말이죠.

 최근에 이 まるごと시리즈는 Java Script 와 Ruby 도 나왔더군요. 관심 있으신 분들은 한 번 훑어보시면 어떨까요?(마침 전 Java Script & Ajax! 도 구입해서 읽고 있으니 차후 소개시켜 드리겠습니다)

 まるごと(마루고또) : 있는 그대로

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
사용자 삽입 이미지

 PHP 에는 Smarty 라는 것을 써봤는데, 요즘은 Template_ 라는 게 인기라더군요(s.*_cheeru님 曰)
 Perl 에는 Template Toolkit 이라는 녀석이 있습니다. Perl 의 대표적인 템플릿 엔진인데요.
 
 대부분 이런 템플릿 엔진을 사용하는 이유는, 역시 코드와 HTML 을 분리하기 위함이겠죠?
 아무래도 Perl 코드에 지저분하게 HTML 을 옹기종기 박아놓는 것은 저 역시도 맘에 들지 않습니다.

 요 근래의 업무중에 Feed를 만드는 일이  있었는데.. 여기에 Feed 의 Content 로 HTML 이 들어가는 경우가 있어서, Template Toolkit 을 가볍게 사용해보았습니다.

 (물론 Catalyst 같은 Perl 의 프레임워크의 기본적인 템플릿 엔진은 Template Toolkit 입니다. 여기서는 단지 프레임워크와는 동떨어져서 Template Toolkit 을 어떻게 하면 간편하게 이용하느냐에 초점을 맞추겠습니다.)

 일단 RSS Feed 를 만들기 위해 XML::Feed 라는 모듈을 사용합니다. 아, 물론 Template Toolkit 도 깔려 있어야되겠죠.

  - XML::Feed
  - Template

 위의 두 모듈을 CPAN 인스톨 합니다.
 물론 관련된 의존 모듈이 여러가지 깔리게 되므로... 다른 것은 언급하지 않겠습니다. :-) (XML::Atom 이라든가...)

 feed_gen.pl
use strict;
use warnings;
use DateTime;
use XML::Feed;
use Template;

my $feed = XML::Feed->new('Atom');

$feed->title("이빨까기 인형");
$feed->link("http://jeen.tistory.com/");
$feed->description("사이비 개발자의 이빨까기");
$feed->author("JEEN");
$feed->modified(DateTime::W3CDTF->parse_datetime(DateTime->now( time_zone => 'Asia/Seoul' ). '+09:00'));

# blahblah 어쩌고 저쩌고 해서 Feed 에 넣을 데이터 모집 :: 여기서는 그냥 파일로 왔다고 하겠습니다.

while(<>) {
  chomp;
  my @data = split /¥t/, $_;
  my $entry = XML::Feed::Entry->new('Atom');
  $entry->title($data[0]);
  $entry->link($data[1]);
  $entry->summary($data[2]);
  $entry->modified(DateTime->W3CDTF->parse_datetime(DateTime->now( time_zone => 'Asia/Seoul'). '+09:00'));
 $entry->issued(DateTime->W3CDTF->parse_datetime(DateTime->now( time_zone => 'Asia/Seoul'). '+09:00'));
  $entry->id($data[3]);
  my $content = XML::Feed::Content->new({ body => _make_content() });
  $entry->content($content);
  $feed->add_entry($entry);
}

open my $fh, ">", "test.xml";
print $fh $feed->as_xml;
close $fh;

sub _make_content {
  my $tt = Template->new;
  my $html;
  $tt->process('file.html', { action => 'attack', target => [qw/ saillinux whitecat /], }, ¥$html);
  $html;
}

 file.html (Template)
[% FOREACH d = target %]
[% d %] 를 [% action %] 하겠습니다.<br/>
[% END %]
 -- Perl방 평화유지위원회 JEEN --

 물론 코드의 테스트는 안해봤습니다. 즉석에서 그냥 이래저래 읊어봐서요. 문제가 있고, 수정을 해야된다면 그건 숙제로 남기겠습니다. :-)

 그러면 결과로는 test.xml 이 생성되고, 그 xml 은 위에서 언급한 포맷으로 구성이 되며, 적절한 RSS 리더로 읽어들이면 보일겁니다.

 일단 메인은 Template 에 대한 이야기니까... 이것에 대한 상세한 코드를 볼까요?
  my $tt = Template->new;
    # Template 의 인스턴스를 생성합니다.

  my $html;  # 템플릿 결과물을 담을 그릇이 됩니다.

  $tt->process('file.html', { action => 'attack', target => [qw/ saillinux whitecat /], }, ¥$html);
     # process 의 첫번째 인수는 템플릿 파일, 두번째 인수는 템플릿 변수(?) 입니다. 여기에서 템플릿에서 처리할 데이터들을 보내주면 되겠죠?, 세번째는 그 담길 그릇에 대한 레퍼런스가 필요합니다.

  print $html;  # 이 시점에서는 템플릿 결과가 나오게 됩니다.
  # saillinux 를 attack 하겠습니다.
  # whitecat 를 attack 하겠습니다.
  # -- Perl 방 평화유지위원회 JEEN --

 TT2 의 너무 간단한 사용법에 대한 이야기는 여기까지입니다.
 사실 TT2 하나 만으로도 이미 책 한권이 나와버렸거든요. 그만큼 문서화가 잘되어 있기 때문에 문제없이 사용하실 수 있으실 겁니다.
 
 이상입니다. 너무나도 허접한 코드와 대충대충 설명 죄송합니다. ;; 뭘해도 역시 문서를 읽는 수 밖에 없겠죠 ;-)

 충고나 질문등은 IRC 와 댓글을 이용해주시면 감사하겠습니다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Posted by JEEN
 흠... #1 에서 하나 만들고 #2 쓰는 시점에서 또 하나 CPAN 모듈을 추가 했습니다.
 물론 하기 쉬운 것만 샥샥 골라서 했으니까요. :-)

 Perlmani 스터디도 있고해서(하지만 참석은 못하니...)
 심심한 틈을 타서 슬라이드로 만들어 봤습니다.

 사실 이것도 완전한 것도 아니고.. 대~충 CPAN 모듈 만드는 생각에서부터  CPAN Author 가 되기까지의
 간단한 설명을 포함하고 있습니다.

 그리고 StoryQ 라는 국내 Slideshare 같은 서비스를 발견해서요. 그것을 써보겠습니다. :-)

 


 이번 포스팅은 이걸로 때우겠습니다. 저도 CPAN 모듈 만들면서 삑사리 낸거랑, 피드백 받은 것들이
다수 있고... 이것을 정리해서 다음에 또 올리도록 하겠습니다.
 잘하면 이런 서비스를 이용하는 데 맛들일지도 모르겠네요 :-)

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