메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

펄(Perl)로 읽기 쉽고 관리하기도 편한 코드를 만들 수 있을까?

한빛미디어

|

2015-04-23

|

by 한빛

25,123

제공 : 한빛 네트워크
저자 : A. Sinan Unur
역자 : 전경원
원문 : Can One Write Readable and Maintainable Perl?



PERL펄의 융통성을 잘 활용하면, 코드를 간결하게 만들 수 있다.

간단하지만, 논란이 될 만한 질문의 답은 분명하게 '그렇다'이다. 90년대에 펄은 문제를 곧잘 해결하는 언어의 대명사로 통했고, 이러한 명성은 한편으론 수많은 나쁜 코드를 생산하는 결과를 낳았다. 펄을 새로 배우는 사람들은 이런 코드에 기겁하곤 한다.

암호문 같은 모습의 코드를 피하기만 해도 이런 기분은 피할 수 있다.

난 조금 늦게 펄을 배우게 되었다. 음.. 아니, 적당한 시기에 배우게 되었는지도 모르겠다. 펄을 배우기 시작할 때, 좋은 코딩 습관을 들일 수 있도록 도와줄 많은 도구를 익힌 상태였으니까.

그때 우연하게도 얼마있지 않아서 펄 5.8이 발표되었는데, 펄 4나 이전 버전에서 입문한 사람들이 흔히 쓰게 되는 나쁜 습관들을 피할 수 있었다.

새로 발표된 펄은 모듈, 렉시컬 파일핸들, 객체 시스템, 이 밖에도 지금은 당연시되는 수많은 멋진 특징을 갖추고 있었다.

펄의 역사를 뒤돌아보면, 그때가 펄을 시작하기에 가장 적절한 시점이었던 것 같다. 펄의 창시자인 래리 월이 말하듯이 펄 5엔 모든 것이 들어있었고, 심지어 그 모든 것을 소개하는 기능도 있었다.

바로 CPAN의 등장이다.

 

CGI의 매개변수를 구문 분석하는 코드를 웹에서 검색해보면, 아마도 펄 광신도가 작성한 것 같은 끔찍하고 불쾌한, 아래와 같은 코드를 만나게 될 거다.

 

# 나쁜 펄 예문: 이렇게 작성하면 안 된다.

for (split /&/, $ENV{QUERY_STRING}) {

   ($key,$val) = split /=/;

   $val =~ s/+/ /g;

   $val =~ s/%([0-9a-fA-F]{2})/chr(hex($1))/ge;

   $arg{$key} = $val;

}

 

 

comp.lang.per.misc에서 만난 사람들은 CGI.pm을 권했다. $cgi->param('username')을 쓰는 편이 좋은 건 분명해 보였다. 

 

HTML 코드를 생성하는 예제를 찾는다면, 아마도 아래와 같은 코드를 만나게 될 것이다.

 

print <<HTML;

<table>

<tr><td>$key</td><td>$val</td></tr>

<table>

 

운이 없는 사람은 아래와 같은 코드를 만날 수도 있다.

 

print table({-border=>undef},

           caption('When Should You Eat Your Vegetables?'),

           Tr({-align=>'CENTER',-valign=>'TOP'},

           [

              th(['Vegetable', 'Breakfast','Lunch','Dinner']),

              td(['Tomatoes' , 'no', 'yes', 'yes']),

              td(['Broccoli' , 'no', 'no',  'yes']),

              td(['Onions'   , 'yes','yes', 'yes'])

           ]

           )

        );

 

 

하지만, 아래와 같이 코드와 내용을 분리하여 깔끔한 코드를 작성하는 방법도 있다.

 

$template->param(vegetable_schedule => [

    {

        name => 'Tomatoes',

        breakfast => 'no',

        lunch => 'yes',

        dinner => 'yes',

    },

]);

 

 

펄은 -f>@+?*<.-&'_:$#/%!과 같은 요상한 문자로 이루어졌다고 주장하는 사람들이 있다. 펄로 많은 문제를 해결한 경험을 갖추고 이러한 경험을 나누려는 마음이 있는 사람들의 도움과 함께 펄을 배웠다면 이런 생각을 하지 않을 거다. 

 

펄은 내부 구현에 있어서 방대한 향상을 이루어왔고, 이와 더불어 매일같이 놀라운 기능을 갖춘 새로운 모듈이 발표되고 있으며, 펄 자체로도 5.18까지 나와 있다(역주: 2013년 12월 기준). 하지만 변하지 않는 가장 중요한 사실은 많은 사람이 펄 5가 제공하는 모든 방법을 동원하여 깔끔한 코드를 작성하려고 애쓰고 있으며, 이러한 모습은 자연스러운 현상이라는 점이다. 

 

깔끔한 펄 코드를 작성하는 데 있어서 가장 중요한 원칙은 기본적으로 다른 모든 프로그래밍 언어의 그것과 다르지 않다. 전역 변수 사용하지 않는 것부터 시작해서, 변수, 객체, 함수, 메서드의 이름을 의미 있는 것으로 정하기, 이러한 것들의 선언 영역과 그 사용 범위는 가능한 작은 영역으로 한정하기 등등...... 

 

펄 코드도 간단명료할 수 있다. 난 내가 원하는 바가 이루어지기만 한다면, 내 방식보다는 해당 언어가 추구하는 방향으로 생각하는 경향이 있다. 

 

일례로, 다음의 자바 코드를 살펴보자.

 

Multiset<Integer> lengths = HashMultiset.create();

for (String string : strings) {

  if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {

    lengths.add(string.length());

  }

}

이와 같은 기능을 하는 펄 코드는 아래와 같다.

my $lengths = Set::Bag->new;

for my $string (@strings) {

    if ($string =~ m{A p{Uppercase_Letter}+ z}x) {

        $lengths->insert(length($string) => 1);

    }

}

 

 

실제로 이 두 코드의 차이점은 정규식 검색인 m{A p{Uppercase_Letter}+ z}x 뿐이다. 다른 방법으론 CharMatcher->upper_case->matches_all_of을 쓸 수 있다고 가정하고 이러한 방식으로 코딩할 수도 있지만, 왠지 이렇게 하는 건 Integer.One.addTo(counter)라고 쓰는 것처럼 거추장스러워 보였다. 

 

Set::Bag을 사용하는 대신에 문자 길이를 해시에 저장하는 방법도 있다. 사실, 다른 문서에서 가져온 모든 대문자의 주기 조합에 가상의 연산을 수행하는 작업을 가정해본다면, 이렇게 펄의 FAQ 목록에서 가져온 것 같은 코드보단 더 나은 방법이 많이 있다. 

 

아래의 코드는 어떤가?

 

$lengths->insert($_ => 1) for

    map length,

    grep m{A p{Uppercase_Letter}+ z}x,

    @strings

 

많은 사람들에게 비난을 받는 $_ 변수를 사용하긴 했지만, 아래의 코드보다는 훨씬 깔끔하다.

 

Function<string, integer=""> lengthFunction = new Function<string, integer="">() {

  public Integer apply(String string) {

    return string.length();

  }

};

Predicate allCaps = new Predicate() {

  public boolean apply(String string) {

    return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);

  }

};

Multiset lengths = HashMultiset.create(

  Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction)

);

 

위 자바 코드의 출처에서도 언급한 내용이지만, for 루프를 사용하는 편이 더 좋다. 내가 언급하고 자 하는 바는 모든 사항을 일일이 명확하게 적는 게, 때론 읽기 편한 코드를 만드는 데 방해가 되기도 한다는 점이다. 펄 코드를 읽을 때는 다음 사항만 기억하면 된다.

map은 변환하고 

 

grep은 선택하고 

 

m{…}은 검색한다.

이건 깔끔한 코드를 만드는 데엔 여러 방법이 있는 걸 보여주는 한 예이다. 

 

펄이 모든 문제에서 항상 최상의 언어가 되진 못한다. 어떤 언어도 완벽하진 않다. 어떤 언어를 사용할 것인지 결정할 때, 언어의 특성은 많은 고려 사항 중 하나일 뿐이다. 내가 말하고자 하는 바는 잘 작성된 펄 코드는 읽기에도 편하다는 것이다. 

 

언어를 처음 배우는 사람에겐 구구절절이 써 놓은 코드가 유용할 수도 있다. 어떤 사람에겐 모든 사항이 상세하게 기술된 코드를 보는 게 편할지도 모르겠다. 다음의 C# 코드는 파일을 한 줄씩 읽어오는 기능을 한다.

 

string line;

 

// 파일의 내용을 한 줄씩 읽어와서 화면에 뿌려준다.

System.IO.StreamReader file =

   new System.IO.StreamReader("c:test.txt");

while((line = file.ReadLine()) != null)

{

   Console.WriteLine (line);

}

 

file.Close();

 

동일한 역할을 하는 펄 코드는 아래와 같다.

 

use autodie;

open my $file, '<', 'c:test.txt';

 

while (my $line = <$file>) {

    print $line;

}

close $file;

 

open의 두 번째 인자로 '<'를 쓰는 것보다 System.IO.StreamReader를 쓰는 편이 더 명확하게 보일지 모르겠다. IDE를 사용한다면, 키보드를 몇 번 치지 않고서도 이렇게 장황한 메서드를 쉽게 입력할 수도 있다. 하지만 이렇게 긴 클래스와 메서드 이름으로, 벽 한 면을 가득 채울 만큼 길어진 코드를 보면서 문제가 있는 부분을 찾아내는 건 곤혹스러운 일이다.

 

펄의 융통성을 잘 활용하면 코드가 불필요하게 장황하게 되는 걸 막을 수 있다. 

 

어떻게 하면 깔끔한 펄 코드를 작성할 수 있을까? 

 

읽기 편한 코드를 많이 접하라. 암호처럼 작성된 코드를 분석하는 일이 재미있을 수도 있지만, 읽기 쉽게 작성된 코드를 흉내 내는 편이 좋은 습관을 들이는 지름길이다. IOCCC에 올라온 C 코드에서 좋은 C 작성법을 배울 수 없는 것처럼, JAPH에서 실제로 사용할 만한 좋은 예를 찾기도 힘들다. 

 

Perl::Critic을 이용해 보는 것도 좋다. 펄 블로그에 올라오는 글도 많은 도움이 된다. 스택오버플로우의 펄 관련 쓰레드를 읽고 답변으로 올라온 다른 사람의 코드를 어떻게 개선할 수 있는지 고민해보는 것도 좋다. Perlbuzz, Perl Weekly, Effective Perl은 꼭 구독하길 권한다. 이 외에도 정기적으로 펄에 관련한 정보를 접하는 것이 좋다. 

 

뭔가 의심스럽다면 질문하라. 다른 사람의 경험에서 배우는 것도 좋은 방법이다.

TAG :
댓글 입력
자료실

최근 본 상품0