강좌
클라우드/리눅스에 관한 강좌입니다.
리눅스 분류

Perl programming ( 입문, CGI, 관리자 perl) I, II, III

작성자 정보

  • 웹관리자 작성
  • 작성일

컨텐츠 정보

본문

icon01.giftitle27.gif

Perl Programming
- Perl 입문 -

임 대 호(lawwal@way.co.kr)
- Way-CGI 시리즈 개발자 -

 

리눅스와 함께 꾸준히 발전해온 Perl.
“CGI 최적언어”라는 평가를 받은 지는 이미 오래지만 국내에 Perl 전문개발자가 의외로 적고 Perl의 쓰임세 또한 가볍게 여기는 경향이 있는 게 현실이다.
단순히 Perl은 C와 문법이 비슷하고 CGI를 제작하기에 적당하다는 정도로 인식하고 있는 독자라면 연재될 글들을 주의 깊게 살피기를 권하고 싶다.  필자가 연재하게 될 내용은 이미 많은 책자에서 거론되었을 법한 것도 있고 전혀 색다른 시도라고 느껴지는 것도 있을 수 있을 것이다.
지면 관계상 원하는 모든 내용을 실을 수는 없겠지만 적어도 C보다는 간결하고 쉽게 구현되는 결과물을 접할 수 있도록 구성할 계획이다.

 

Ⅰ. Perl 입문

필자는 Linux를 접하기 전부터 Perl을 이용해 배포용 CGI(http://way.co.kr)를 위시로 하여 개발해왔으며, Linux를 접하게 된 후부터는 Linux가 Perl 프로그램 개발에 더없이 편리한 환경을 제공한다는 것을 알 수 있었다.
Linux 설치를 끝냈고 C나 기타 언어를 학습중이거나 학습할 계획인 리눅서라면 Perl을 공부해보라고 권하고 싶다.

Perl의 기능은 C에서 많이 빌려왔으며 sed나 awk 등의 문자열 처리용 프로그램에서도 많은 기능을 계수하였다.  그래서 C를 어느 정도 알고 있는 사람이 Perl 소스를 접하게 되면 유사한 문법 때문에 대략적인 윤곽을 파악할 수 있게 된다. 그렇다고 Perl을 배우기 위해 일부러 C를 먼저 배우는 어리석음은 범하지 말기를 바란다.

요즘 필자는 Linux에서 vi에디터를 이용해 Perl 프로그램을 개발하고 있다.  대부분의 Linux 배포판은 Perl이 기본적으로 설치되고 레드햇 등의 배포판은 rpm과 같은 바이너리 패키지 형태로 제공되어 설치 및 관리가 간편하다.  만약 Perl을 직접 컴파일하여 설치하려거든 http://www.perl.com/등에서 파일을 다운로드하면 되지만 구체적인 설치방법은 지면관계상 생략하기로 한다.  만약 윈도우즈 계열의 서버에서 테스트해보고 싶다면 http://way.co.kr의 “CGI 강좌” 게시판을 참고하기 바란다.

먼저 Perl이 설치되어있는지 확인해보자.  대부분의 경우 /usr/bin 경로에 perl 프로그램이 위치할 것이다.  레드햇의 경우 $ rpm -qa | grep perl 명령으로도 설치여부를 확인할 수 있다.  perl -v를 입력하면 설치된 Perl의 버전정보를 얻을 수 있으며 perl -V는 좀더 상세한 정보를 얻을 수 있다.

$ perl -v

This is perl, version 5.004_05 built for i386-linux
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-1998, Larry Wall
< 하략 >

프로그래밍 언어 중에는 C 등과 같이 컴파일을 통해 독자적인 파일 하나로도 프로그램 실행이 가능한 언어가 있다. 하지만 Perl은 프로그램 소스를 해석하여 실행해주는 프로그램(인터프리터, interpreter)이 설치되어 있어야만 하고 그 때문에 Perl이나 shell 스크립트는 소스의 첫 번째 줄에 #!/usr/bin/perl 등과 같이 해석용 프로그램의 위치를 적어주어야 하는 것이다.  그러니까 Perl이 설치되어있지 않은 서버에서는 Perl 소스 프로그램을 실행할 수 없다고 보면 되는 것이다.(최근에는 소스를 컴파일 하는 방법도 제공되고 있다.)

1. 실행방법

Perl 소스 프로그램에 실행권한(chmod +x)을 부여하고 첫 번째 줄에 Perl의 위치를 적어줄 경우 다음과 같이 프로그램명만으로 직접 실행시킬 수 있다.  
특히 웹상에서 실행하고자 할 경우 이와 같이 해주어야 한다.

$ program(프로그램명)

현재 경로가 PATH에 걸려있지 않다면 ./program과 같이 실행하여야 한다.
만약 shell 상태에서 다음과 같이 실행한다면 소스 첫 번째 줄에 Perl의 위치를 적어주거나 실행권한을 부여할 필요가 사라진다.

$ perl 프로그램명

작성된 소스코드의 오류를 점검하기 위해서는 perl -c 옵션을 이용한다.  
소스코드에 이상이 없을 경우 “syntax OK”라는 메시지가 나타난다.

$ perl -c 프로그램명

 

2. 프로그램 맛보기

Perl이 시스템에 설치되어있다면 이제부터 간단한 Perl 문법부터 익혀보도록 하자.

print “Hello, Way!\n”;

간단하면서도 완벽한 하나의 프로그램이다. vi 등의 편집기에 소스내용을 입력하여 저장한 후 실행해 보자.

$ perl hello(파일명)
Hello, Way!

소스를 분석해보면, “Hello, Way!”라는 문자열과 함께 줄 바꿈 문자(\n)를 출력하는 것이며 각 줄의 끝은 ;(세미콜론) 기호로 마무리한다.

 

3. 변수와 기본연산

다른 언어들과는 달리 변수형을 지정할 필요 없이 대입과 동시에 변수형이 결정된다.  변수는 $기호로 시작되며 배열의 경우 @, 조합배열(hash)은 %기호로 시작된다.  $bom = 123; 과 같이 $bom 변수에 123이라는 정수를 대입하게 되면 별도의 조치 없이 정수형을 갖게 된다.  변수간의 연산도 매우 자유롭다.  예를 들어 다음과 같은 경우 결과값은 정상적인 수치 연산결과인 100이 출력된다.

$bom = 49;   # 정수
$danvi = “51”;   # 문자열
print $bom + $danvi;  # 100 출력

perl 문법상 # 기호 이후부터는 주석으로 인식한다.  예상하듯이 Perl에서는 사칙연산에 +, -, *, / 기호가 이용되며 나머지(계수)에는 % 기호가 사용된다.
문자열 연산자는 .(마침표, dot)를 사용하며, 연산방법 또한 앞에서와 같이 간단하다.

$bom = 100;  # 정수
$danvi = “200”;  # 문자열

print $bom . $danvi; 와 같이 문자열 연산자를 이용하면 “100200”이 출력되고 print $bom + $danvi; 와 같이 수치연산자를 이용하면 300이 출력된다.

 

4. 조건문

if (조건) { 참일 때; }
else { 거짓일 때; }

두 개 이상의 조건문은 elsif(“elseif”가 아님에 주의)를 이용하여 다음과 같이 구성할 수 있다.

if (조건) { 참일 때; }
elsif (조건) { 새로운 조건이 참일 때; }
elsif (조건) { 새로운 조건이 참일 때; }
. . . .
else { 모두 거짓일 때; }

조건판별을 위한 연산자로서 ==(eq)는 일치, !=(ne)는 불일치로 사용되며 <, >, <=, >= 등의 기호를 사용할 수 있다.  또한 논리연산자로서 &&(and), ||(or), !(not)도 사용할 수 있다.(기호와 영문 모두 사용할 수 있다.)

$bom = 5;
if ($bom < 10 and $bom > 3) {
  print “10보다는 작고 3보다는 크다. \n”; }

소스를 실행해보면 당연히 “10보다는 작고 3보다는 크다.”가 출력될 것이다.

if와는 반대로 사용되는 조건문으로는 unless가 있다.

unless (조건) { 거짓일 때; }
else { 참일 때; }

 

5. 반복문

while (조건) { 조건이 참인 동안 실행; }
until (조건) { 조건이 거짓인 동안 실행; }
무한루프를 원치 않는다면 당연히 중괄호({})를 실행하는 동안 조건과 관련한 값이 변화되어야 할 것이다.

$bom = 1;
while($bom <= 10) {
   print $bom, “\n”;
   $bom++; }

초기의 $bom의 값은 1이었고 해당 값이 10보다 작기 때문에 while문의 조건에 일치하므로 중괄호({}) 가 실행된다.  중괄호로 처리방향이 넘겨진 후 print 문에 따라 $bom의 값인 1이 출력된다. 만약 $bom++; 와 같이 $bom 값을 변화시켜주지 않는다면 $bom 값은 영원히 1일 것이므로 무한루프가 되어버릴 것이다. $bom++ 는 자동증가를 뜻하는 것으로서 원래의 $bom 값에 1을 가산한 후 다시 $bom에 값을 저장하게 되는 것이다. $bom-- 는 그 반대이다. 루프를 한번 돌게되면 $bom 값은 2가 되고 한번씩 더 돌 때마다 3, 4, 5, 6, 7, 8, 9, 10이 된다.  $bom 값이 10일 경우 10을 출력하고 $bom++ 에 따라 $bom 값이 11이 되면서 while문의 조건에 맞지 않아 루프는 결국 종료된다.

또다른 반복문으로는 for문이 있다.

for (초기값; 조건; 변화값) { 조건이 참인 동안 실행; }

다음 소스는 앞서 설명한 while문의 예제와 동일한 결과를 보이게 된다.

for ($bom = 1; $bom <= 10; $bom++) {
   print $bom, “\n”; }

반복문중에는 foreach문도 있으나 배열과 관련하여 주로 사용되므로 설명을 뒤로 미루기로 하겠다.

 

6. 파일 핸들링

파일을 읽거나 저장하기 위해서는 파일을 열어야하며 일반적으로 다음과 같은 규칙에 의해 사용되어진다.

open(파일핸들, “[조작방법] 파일명”);

‘파일핸들’은 사용자의 식별을 용이하게 하기 위해 임의로 지정하여 사용한다. ‘조작방법’에는 읽기전용(< 또는 생략), 쓰기(>), 덧붙이기(>>) 등이 있다.

open(FILE, “>bom”);
  print FILE “Way\n”;
close(FILE);

분석해보면 다음과 같다.

open(FILE, “>bom”);

bom이라는 파일명으로 파일을 쓰기모드로 열되 파일핸들은 FILE로 지정한다.

print FILE “Way\n”;

FILE이라는 파일핸들(실제로는 bom이라는 파일)에 “Way\n”라는 문자열을 기록한다.

close(FILE);

FILE 이라는 핸들의 파일을 닫는다.  조작이 끝나는 즉시 닫는 게 바람직하다.

앞에서 알 수 있듯이 파일핸들을 사용함으로 해서 특정 파일에 print(기록)하고, 손쉽게 파일을 닫을 수 있게 된다.
소스가 실행되게 되면 bom이라는 파일이 생성되어지고 파일에는 Way라는 내용이 들어가게 될 것이다.
쓰기모드일 경우 이미 동일한 파일이 존재하면 내용을 덮어쓰게 되므로 종전내용을 잃게 된다. 하지만 덧붙이기(>>)모드로 파일을 열게 되면 종전내용의 하단에 새로운 내용이 추가된다.(대부분의 log파일 저장방식과 동일하다.)

이제 다음과 같은 소스를 작성하여 실행해보자.

open(FILE, “>>bom”);
  print FILE “Way2\n”;
close(FILE);

특별한 문제가 없는 한 bom이라는 파일내용은 다음과 같을 것이다.

$ cat bom
Way
Way2
$

저장하는 방법을 알았으니 이제 읽어 오는 방법을 알아보자.
open(FILE, “bom”);
  $line1 = <FILE>;
  $line2 = <FILE>;
close(FILE);

<FILE>이 어떤 기능을 하는지 궁금할 것이다.  <FILE>에서 FILE은 앞에서와 마찬가지로 파일핸들이며 <>(‘줄입력 연산자’라고도 한다.)로 묶이므로 해서 한 줄을 읽어 온다는 뜻이다.

$line1= <FILE>;
bom파일의 첫 번째 줄 내용을 $line1 이라는 변수에 저장(대입)한다.

$line2 = <FILE>;
한번 더 <FILE>을 사용하였으므로 두 번째 줄을 읽어 $line2에 저장하는 것이다.  그런데 모든 파일의 내용을 하나의 변수에 넣어 조작하고자 한다면 몇 줄인지 알 수 없기 때문에 앞의 방법보다는 다음과 같이 while문을 이용하는 것이 편리하다.

open(FILE, “bom”);
  while($line = <FILE>) {
        $all_line = $all_line . $line; }
close(FILE);

약간 복잡하게 느껴질 수도 있을 것이다.  첫줄과 마지막 줄은 설명이 필요 없을 듯 하여 가운데부분만 설명하도록 하겠다.  while문은 참일 경우 바로 뒤에 위치한 중괄호({})가 실행된다.  Perl에서는 아무런 값이 없을 경우 ‘거짓’을 돌려주므로 bom 이라는 파일을 모두 읽어오고 나면 while 루프가 종료되는 것이다.  약간 더 설명을 덧붙이자면, $line = <FILE> 이 참일 경우(읽어올 줄이 있을 경우는 참이 된다.) $line 변수에 읽어온 줄의 내용을 저장하고 문자열연산자(.)에 의해 $all_line 변수에 추가되며, 파일 끝까지 읽고 나면 읽어올 줄이 없어 거짓을 리턴 하므로 while 루프가 종료되는 것이다.

다음과 같이 간단히 나타낼 수도 있다.

open(FILE, “bom”);
  while(<FILE>) { $all_line .= $_; }
close(FILE);

$all_line .= $_ 부분은 $all_line = $all_line . $_와 동일한 결과를 보인다.

이제 특정파일의 내용을 읽어 변수에 저장하였으며 이로써 조작이 가능하게 되었다.

파일을 열거나 조작하고자 할 때 발생할 수 있는 오류를 미리 예방하고 파일(또는 디렉토리)에 관한 정보를 얻어 활용하기 위해 Perl은 다양한 파일테스트 연산자를 제공한다.

주요 연산자는 다음과 같다.

 

- e

  존재 여부

- r

  읽기 가능 여부

- w

  쓰기 가능 여부

- x

  실행 가능 여부

- d

  디렉토리 여부

- T

  텍스트파일 여부

- B

  이진파일(binary)  여부

- s

  파일크기

 

if (-e “bom”) { print “파일 있음\n”; }
else { print “파일 없음\n”; }

print “bom 파일의 크기는 “, -s “bom”, “ byte입니다.\n”;

 

7. 함수

구조적인 프로그램을 위해, 특히 빈번히 사용되는 특정 기능이나 처리절차를 하나의 함수로 생성하여 활용할 수 있다.

함수는 다음과 같이 구성되며 &함수명과 같이 호출한다. 함수는 중괄호({})로 묶이며 함수영역 내외를 불문하고 사용되어지는 문법은 동일하다.

sub 함수명 { 처리내용; }

앞서 설명했던 for문을 이용해 함수를 적용해보겠다.

for ($bom = 1; $bom <= 10; $bom++) {
   print $bom, “\n”;
   &job; }

sub job { print “===============\n”; }

소스를 실행해보면 1부터 10까지 출력되는 과정중 숫자 사이사이에 구분선이 그어질 것이다.  이런 용도의 함수는 작업을 좀더 간결히 하는 정도의 효과밖에 기대할 수 없지만, 다음과 같은 경우 호출시 &함수명(“넘길값”)과 같이 인자를 넘기고 호출된 함수에서는 넘겨받은 인자를 계산(처리)하여 결과를 리턴하게되는 완전한 함수의 형태를 띄게 된다.

for ($bom = 1; $bom <= 10; $bom++) {
   $danvi = &job($bom);
   # job 함수로 $bom이라는 변수값을 넘기고,
   # 돌려 받은 처리결과를 $danvi 변수에
   # 저장한다.
   print $danvi, “\n”; }

sub job {
  ($input) = @_;   # 값을 넘겨받는다.
  $output = $input * 10;  # 계산한다.
  return $output; }  # 결과를 돌려준다.

소스를 실행해보면 10, 20, 30, ... 과 같이 $bom 값에 10을 곱한 값이 순서대로 출력된다.

 

8. 배열

여러 개의 값들을 하나의 변수에 저장하여 활용하기 위해 배열(array)과 조합배열(hash)이 지원된다.  둘의 차이점은 인덱스(index)가 숫자인가 그렇지 않은가 이다.

배열에 값들을 직접 대입하는 방법은 괄호를 이용하여 다음과 같이한다.
@way = (1, 2, 3, 4, 5);

배열대입은 다음과 같이 개별적으로도 가능하다.
$way[0] = 1;
$way[1] = 2;
$way[2] = 3;
$way[3] = 4;
$way[4] = 5;

배열을 일반변수형태로 추출하기 위해서는 다음과 같이 사용한다.
$way[배열번호]

첫 번째 값의 배열번호는 0이다.  따라서 $way[0]에는 1이, $way[1]에는 2가 저장되어 있는 것이다.

배열에서 값을 추출하여 또 다른 변수에 저장해보자.
$bom1 = $way[0];
$bom2 = $way[1];
$bom3 = $way[2];
$bom4 = $way[3];
$bom5 = $way[4];

또 다른 방법으로는 다음과 같은 방법도 존재한다.
배열에 직접 대입하던 것과는 반대이다.
($bom1, $bom2, $bom3, $bom4, $bom5) = @way;

조합배열은 %기호를 사용하며 숫자인덱스 대신 임의의 문자열(key)을 인덱스로 갖는다.  따라서 저장된 순서는 그다지 큰 의미가 없다.

%way = (“빨강” => “red”,
             “파랑” => “blue”,
             “검정” => “black”);

앞서 설명한 배열과 동일한 방법으로 대입하였지만 주의 깊게 관찰할 부분은 => 기호를 이용해서 key와 value로 구성된다는 것이다.

마찬가지로 $way{‘빨강’} = “red”; 와 같은 방식으로 개별적인 저장도 가능하다.

자료를 추출하기 위해서는 다음과 같이 사용하면 된다.

$way{key}

$way{‘빨강’} 에는 “red”가, $way{‘파랑’}에는 “blue”가 저장되어 있다.

보통 => 기호 대신 ,(comma)를 사용하기도 하지만 가독성을 높이기 위해 => 기호를 사용하는 것이 바람직하다.

print “파랑색은 영문으로 $way{‘파랑’}입니다.\n”;

소스가 실행되면 “파랑색은 영문으로 blue입니다.”가 출력될 것이다.
조합배열은 keys와 values를 이용해 인덱스나 값만을 추출해낼 수 있다.
print keys %way
소스가 실행되면 “빨강파랑검정” 등과 같이 출력된다.

이제 앞에서 설명을 미뤘던 foreach문에 대해 알아보자.

foreach 추출값 (배열 또는 목록) { 실행영역; }

배열값을 하나씩 추출하고 그때마다 중괄호로 묶인 실행영역을 거치게 된다. 배열의 모든 원소가 추출되고 나면 루프는 종료된다.

@bom = (“봄”, “여름”, “가을”, “겨울”);
foreach $danvi (@bom) {
  $count ++;
  print “$count, $danvi\n”; }

소스를 실행하면 다음과 같이 출력될 것이다.

1, 봄
2, 여름
3, 가을
4, 겨울

foreach문은 다음과 같이 사용할 수도 있다.  결과는 앞의 결과와 동일하다.

foreach $danvi (“봄”,”여름”,”가을”,”겨울”) {
  $count ++;
  print “$count, $danvi\n”; }

배열은 @way = (); 또는 %way = (); 와 같이 초기화할 수 있다.

배열에 원소를 추가하고자 할 경우 push를 이용한다.
push(배열명, 추가할 값);

@way = (1, 2, 3, 4, 5);
push(@way, 6);

@way 에는 6이라는 원소가 추가된다.

 

9. 문자열 처리

Perl은 문자열 처리에 상당한 능력을 과시하고 있다.

“ ”기호로 묶인 상태에서 변수를 직접 사용할 수 있다.
$way = “CDE”;
$way1 = “AB$way”;  # ABCDE

특정 문자를 지정한 수량만큼 복제하기 위해 x 라는 특수한 연산자를 이용한다.  구분선 출력이나 열을 맞추기 위한 공백 등에 자주 사용된다.
$way2 = “=” x 10;   # ==========

문자열중 일부분을 추출하는 방법은 다음과 같다.  시작위치는 0부터이다.

substr(변수, 시작위치, 글자수);

$way 라는 변수의 값중 첫 번째부터 2글자를 추출하여 $way3에 저장하는 방법은 다음과 같다.
$way3 = substr($way, 0, 2);   # CD

문자열 중에서 특정문자의 존재여부를 파악하는 방법은 다음과 같다.  실행결과 “있음”이 출력된다.

$way = “봄 여름 가을 겨울”;
if($way =~ /봄/) { print “있음\n”; }
else { print “없음\n”; }

문자열을 치환(substitution)하려면 다음과 같이 하면 된다.  실행결과 “이것이 사랑일까?”가 출력된다.

$way = “이것이 우정일까?”;
$way =~ s/우정/사랑/;
print “$way\n”;

 

10. 시스템 명령

`(역따옴표)기호를 이용할 경우 원하는 시스템 명령을 실행할 수 있으며 실행결과를 얻어와 활용할 수 있다.

$way = `ls -al`;
print $way;

이렇게 할 경우 shell에서와 마찬가지로 ls 명령결과가 출력된다.

$bom = `w`;
@user = (“way”, “lawwal”, “danvi”);
foreach $user_id (@user) { &login_check($user_id); }

sub login_check {
 ($id) = @_;
 if ($bom =~ /$id/) { print “$id : On-Line\n”; }
  else { print “$id : Off-Line\n”; }
}

필자가 사용중인 서버에서 소스를 실행한 결과 다음과 같이 출력되었다.

way : On-Line
lawwal : On-Line
danvi : Off-Line

만약 결과를 얻어올 필요가 없고 명령에 따라 부수적인 작업을 해야 한다면 system이나 exec를 사용한다.  exec의 경우 실행중인 Perl 프로그램은 종료되고 지정된 명령어가 실행된다는 차이점이 있다.

print “top 명령을 실행할까요? [Y/n] “;
$input = <STDIN>;
if ($input =~ /y/i) { system(“top”); }
else { print “취소되었습니다.\n”; }

줄입력연산자인 <> 사이에 STDIN(표준입력)이 들어가게 되면, 키보드로부터 값을 입력받는다. 입력된 값에 Y 또는 y가 존재하는 경우(i 옵션은 대소문자를 구별하지 않는다.) top 명령을 실행하도록 한다.

이번에는 대략적인 문법을 위주로 설명하였다. 기본적인 문법을 토대로 하나씩 응용범위를 늘려가다 보면 여러분도 어렵지 않게 쓰임세 있는 Perl 프로그램을 개발할 수 있을 것이다.

다음에는 웹프로그램을 위한 CGI로서의 Perl을 살펴보도록 하겠다.

icon01.giftitle29.gif

임 대 호(lawwal@way.co.kr /  Way-CGI 시리즈 개발자)

 

UNIX 계열에서 사용되는 웹프로그램 언어로는 C, Perl, PHP 등이 있는데 장단점을 개인적 견해로 비교해보면 다음과 같다.

C는 코딩속도와 라이브러리 등에서 열세를 보이는 반면 실행속도가 빠르다는 장점이 있다.
Perl의 경우 거의 대부분의 UNIX 시스템에 기본적으로 설치되어 있어 시스템간 호환성이 뛰어나고 코딩 속도도 빠르지만 인터프리터 언어 특성상 실행속도가 약간 느리고 다수 프로그램이 동시에 실행될 경우 서버에 부담이 될 수 있다.  PHP는 코딩 속도와 실행속도 면에서 우위를 보이지만 웹서버와 조화를 이뤄야 하므로 범용 프로그램으로 배포하기엔 부적절한 데다 비대한 웹서버가 동작하게 되어 서버에 부담이 될 수 있다.

필자가 다른 언어보다 Perl을 주로 사용하는 이유는 코딩속도와 시스템 도구나 문자열 조작도구로 사용할 수 있기 때문이다.

 

Ⅱ. CGI Using Perl

생소할지 모르지만 다음의 소스는 본쉘(bash) 스크립트를 이용한 간단한 CGI이다.

#!/bin/bash
echo Content-type: text/html
echo
echo “bash를 이용한 CGI입니다.<BR>”
echo “접속주소 : $REMOTE_ADDR”

다음은 C를 이용한 CGI이다.  소스를 way.c라는 파일에 저장한 후 컴파일($ gcc -oway.cgi way.c)해서 웹상에서 실행해보면 간단한 결과가 출력된다.

main() {
 printf (“Content-type: text/html\n\n”);
 printf (“C를 이용한 CGI입니다.<BR>”);
 printf (“접속주소 : %s”, getenv(“REMOTE_ADDR”));
}

이렇듯 많은 언어나 쉘스크립트를 이용해 CGI를 만들어낼 수 있다.

 

1. Perl CGI 맛보기

Perl로 웹상에서 특정 문자를 출력하기 위해서는 내용을 출력하기 전에 다음과 같은 헤더를 출력하여 형식을 선언해줘야 한다.

print “Content-type: text/html\n\n”;

간혹 뉴스그룹(han.comp.lang.perl)에 올라오는 질문 중 적지 않는 질문이 이런 사소한 문제가 원인인 경우가 많다.
특히 “Internal Server Error(500)”는 대부분 웹서버 Error log나 구문점검 옵션($ perl -c 파일명)을 통해 원인을 파악할 수 있다.
다음 소스를 way.cgi 라는 파일로 저장한 후 실행권한을 부여(chmod +x)하면 웹브라우져로 “Hello, Way!”라는 내용을 접할 수 있다.

#!/usr/bin/perl
print “Content-type: text/html\n\n”;
print “<H1>Hello, Way!</H1>\n”;

특정 URL로 접속을 전환(Redirect)하기 위해서는 다음과 같이 사용한다. 보통 http:// 등의 프로토콜 지정 없이 상대 URL로 사용할 수도 있지만 원칙적으로 프로토콜까지 포함해서 적어줘야 한다. 생략할 경우 오라클 웹서버, PWS 구버전 등의 일부 웹서버에서는 에러가 발생하기도 한다. 다음 소스가 브라우져로 실행되면 지정된 URL로 접속이 전환된다.

#!/usr/bin/perl
print “Location: http://way.co.kr\n\n”;

 

2. 아파치 웹서버 설정

만약 사용하는 웹서버에서 CGI 실행을 허용하지 않거나 CGI 파일에 실행권한이 부여되지 않았을 경우 “Forbidden(403)”이나 “Method Not Allowed(405)” 에러가 발생할 것이다. 경우에 따라 Perl 소스가 브라우져에 그대로 출력되기도 한다.

※ 아파치 웹서버 CGI 실행오류 해결방법

가. AddHandler cgi-script .cgi 부분의 주석 제거
나. <Diretory> 설정 중 Options 에 ExecCGI 추가

 

<Directory “/usr/local/apache/htdocs”>
Options Indexes Includes ExecCGI
AllowOverride FileInfo AuthConfig
</Directory>
다음은 계정사용자의 웹문서 경로에 CGI실행을
허용하는 설정이다.
<Directory /home/*/public_html>
Options Indexes Includes ExecCGI
AllowOverride FileInfo AuthConfig
</Directory>

 

다. ScriptAlias 설정

 

ScriptAlias /cgi-bin/ “/usr/local/apache/cgi-bin/”
(VirtualHost의 경우도 적용이 가능하다.)

·ScriptAlias란?
1. 실제 디렉토리를 가상 디렉토리로 인식하도록
    한다.(Alias, 별칭 기능)
2. 디렉토리 내의 파일을 브라우저로 접근하면
    무조건 실행 프로그램(CGI)으로 취급하여 실행한다.
3. 보안상 HTML 문서 등은 퍼미션 에러나 서버에러가
    발생하고 이미지는 나타나지 않는다.

 

3. FORM 태그

HTML 문서를 이용해 CGI에 정보를 전달하기 위해 FORM 태그가 사용된다.  FORM 태그는 여느 HTML 태그와 마찬가지로 <FORM>으로 시작하여 </FORM>으로 끝이 난다.

<FORM ACTION=”연결할 CGI URL” METHOD=GET>

TARGET을 지정하거나 NAME을 지정할 수 있으며, 다음과 같이 파일 업로드를 위한 선언도 가능하다.

<FORM ACTION=”연결할 CGI URL” METHOD=POST
  ENCTYPE=”multipart/form-data”>

다음과 같이 FORM 태그 내부에 <INPUT>, <SELECT>, <TEXTAREA> 등의 태그를 사용하여 실질적인 정보를 전달하도록 한다.

<FORM ACTION=”way.cgi” METHOD=GET>
  성명 <INPUT TYPE=TEXT NAME=user><BR>
  주소 <INPUT TYPE=TEXT NAME=address><BR>
  성별 <SELECT NAME=sex>
          <OPTION>남
          <OPTION>여
       </SELECT><BR>
       <INPUT TYPE=SUBMIT VALUE=”전송”><BR>
</FORM>

성명에 way를 입력하고 주소에 Kwangju를, 성별은 ‘남’을 선택하면 브라우져 주소입력줄에 다음과 같이 나타나며 지정된 CGI에 정보를 전달하게 된다.(GET 방식의 예이다.)

way.cgi?user=way&address=Kwangju&sex=%B3%B2
----- ----- -------- ------- -------------
  CGI        성명                   주소                    성별

? 기호를 구분자로 하여 실행될 CGI(좌측)와 인자(우측)로 구성되며 각 인자들은 & 기호로 연결된다.  각 인자들은 = 기호로 키(항목명)와 값으로 구성된다.

 

4. 인자의 전달과 처리

FORM 태그나 GET방식의 직접연결을 통해 전달된 정보는 약간의 절차를 거쳐야 Perl에서 활용할 수 있다.  특정 기호로 나뉜 인자들을 추출해 내야하고 성별과 같이 16진수 형태로 전달된 한글이나 특수문자를 정상적인 자료로 변환해야 한다.  이를 위해 많은 이들이 cgi-lib.pl 라이브러리나 CGI.pm 모듈을 사용하는 것이 보통이다.  하지만 필요한 범위 이상의 비대한 소스를 사용하고 싶지 않기에 필자는 한번도 사용해본 적이 없다.  적어도 다음의 소스 정도면 넘겨받은 인자를 Perl에서 정상적인 자료로 활용할 수 있기 때문이다.

 

sub input_form {
# 사용할 변수를 지역변수로 선언
local ($form, $tmp, $key, $value);
# GET, POST 방식을 판별하여 인자 습득
if($ENV{‘REQUEST_METHOD’} eq “GET”) {
$form = $ENV{‘QUERY_STRING’}; }
if($ENV{‘REQUEST_METHOD’} eq “POST”) {
read(STDIN, $form, $ENV{‘CONTENT_LENGTH’}); }
# 키와 값으로 구분하고 16진수를 일반문자로 변환
foreach $tmp (split(/&/, $form)) {
  ($key, $value) = split(/=/, $tmp);
  $key   =~ tr/+/ /;
  $value =~ tr/+/ /;
  $key   =~ s/%(..)/pack(“C”, hex($1))/ge;
  $value =~ s/%(..)/pack(“C”, hex($1))/ge;
  $input{$key} = $value; }
}

 

소스를 간략히 설명하면 다음과 같다.
1. 인자 전달방식(GET 또는 POST) 판별
2. 전체 인자 습득
3. 전체인자에서 & 기호를 기준으로 각 인자 추출
4. 각 인자에서 = 기호를 기준으로 키와 값으로 구분
5. + 기호로 넘어온 공백문자를 정상적인 공백으로 변환
6. 한글이나 특수문자인 16진수 자료를 일반문자로 변환
7. %input 조합배열에 키와 값 저장

이제 &input_form; 을 먼저 호출하고 $input{‘항목명’} 과 같이 자료를 추출하여 활용할 수 있다.

앞서 설명한 FORM 태그가 포함된 HTML 문서에서 CGI를 호출하면 전달된 정보를 출력하는 CGI를 만들어 보자.

다음은 HTML문서(FORM 태그 사용)에 웹브라우져로 정보를 입력하는 화면(lynx)이다.

 

성명   way_________________
주소   Kwangju_____________
성별   [남]
[ 전송 ]

 

다음은 way.cgi 파일이다.  반드시 Perl경로를 확인하고 실행권한을 부여해야 정상적인 결과를 얻을 수 있을 것이다.

 

 #!/usr/bin/perl

&input_form;
&print_html;

sub input_form { ... }  # 앞서 설명한 소스
sub print_html {
print “Content-type: text/html\n\n”;
print “<HR>\n”;
print “<H2>원래 전달되었던 인자</H2>\n”;
print “$ENV{‘QUERY_STRING’}<BR>\n”;
print “<HR>\n”;
print “<H2>변환하여 활용이 가능해진 자료</H2>\n”;
print “성명 : $input{‘user’}<BR>\n”;
print “주소 : $input{‘address’}<BR>\n”;
print “성별 : $input{‘sex’}<BR>\n”;
print “<HR>\n”;
exit;
}

 

정상적인 경우 결과는 다음과 같다.

 

 ___________________________________________
원래 전달되었던 인자
user=way&address=Kwangju&sex=%B3%B2
___________________________________________
변환하여 활용이 가능해진 자료
성명 : way
주소 : Kwangju
성별 : 남
___________________________________________

 

5. 모듈과 라이브러리
Socket, DBI, DBD 등의 Perl 모듈은 Perl의 활용범위를 확장시켜주는 매우 유용한 도구이다.  Perl 모듈을 사용하기 위해서는 다음과 같이 선언해주어야 한다.

use 모듈명;

Socket 모듈의 경우 원격통신을 가능하게 해주는 도구로서, sendmail 이 지원되지 않는 UNIX계열 서버나 WindowsNT/98/95 운영체제의 서버에서 SMTP 서버에 접속하여 메일을 전송하는 경우가 좋은 예이다.  관심 있는 독자는 Way-BOARD(Ver1.2)를 다운로드 하여 압축을 해제한 후 lib/mail.cgi 파일 하단의 Socket Mail 부분을 참고하기 바란다. 많은 모듈은 Perl이 설치되면서 함께 설치되지만 그 밖의 모듈은 http://www.perl.com/CPAN/에서 구할 수 있다.

자주 사용되는 함수를 특정 파일에 넣어두고 필요에 따라 꺼내 쓸 수 있다.

require(“라이브러리 파일명”);

C나 PHP의 include문과 거의 동일한 기능을 한다.  앞의 인자변환 소스도 라이브러리 파일에 넣어두고 각종의 CGI에서 함께 공유할 경우 작업효율은 극대화 된다.  라이브러리 파일에 넣어둘 경우 실행권한을 부여하거나 Perl 위치를 적어줄 필요가 없지만 유의할 점은 파일의 제일 하단에 다음과 같이 return 1; 이라는 줄을 추가해줘야 에러가 발생하지 않는다는 것이다.

 

# Perl Library File

sub input_form { ... }

sub html_header { ... }

return 1;

 

6. 환경변수
앞서 설명한 인자처리 소스와 bash, C를 이용한 CGI에서 각각 환경변수를 읽어들여 활용하는 법을 볼 수 있었다.  Perl에서는 환경변수가 조합배열인 %ENV에 저장되어있다.
다음은 Perl, PHP, C, bash에서 접속자의 IP정보를 얻어내는 방법이다.

 

구  분

환경변수 습득방법

perl

$ENV{‘REMOTE_ADDR’}

PHP, C

getenv(“REMOTE_ADDR”)

bash

$REMOTE_ADDR

 

다음의 소스를 실행해보면 Perl에서 사용되는 각종의 환경변수명과 값을 구할 수 있다.

#!/usr/bin/perl
print “Content-type: text/html\n\n”;
foreach $env_val (keys %ENV) {
  print “* $env_val : $ENV{$env_val}<BR>\n”; }

다음은 필자의 서버에서 소스를 실행한 결과다.  대충 훑어보면 어떤 기능을 하는 것들인지 알 수 있을 것이다.  경우에 따라 환경변수 종류가 늘어나거나 줄어들 수 있다.

 

* SERVER_SOFTWARE : Apache/1.3.9 (Unix)
* GATEWAY_INTERFACE : CGI/1.1
* DOCUMENT_ROOT : /home/way/public_html
* REMOTE_ADDR : 210.123.123.123
* SERVER_PROTOCOL : HTTP/1.1
* SERVER_SIGNATURE : Apache/1.3.9 Server
   at way.co.kr Port 80
* REQUEST_METHOD : GET
* QUERY_STRING :
* HTTP_USER_AGENT : Mozilla/4.0 (compatible;
   MSIE 5.0); Windows 98
* PATH : /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
* HTTP_ACCEPT : */*
* HTTP_CONNECTION : Keep-Alive
* REMOTE_PORT : 1129
* SERVER_ADDR : 210.123.254.100
* HTTP_ACCEPT_LANGUAGE : ko
* SCRIPT_NAME : /test/env.cgi
* HTTP_ACCEPT_ENCODING : gzip, deflate
* SCRIPT_FILENAME : /home/way/
   public_html/test/env.cgi
* SERVER_NAME : way.co.kr
* REQUEST_URI : /test/env.cgi
* SERVER_PORT : 80
* HTTP_HOST : way.co.kr
* SERVER_ADMIN : webmaster@way.co.kr

 

만약 브라우져에 입력된(특히 VirtualHost에서) 도메인을 얻기 위해서는 $ENV{‘HTTP_HOST’}라는 변수를 읽어들이면 된다.

예시에 나와있지 않지만 HTTP_COOKIE(쿠키정보), HTTP_REFERER(CGI 실행 URL), REMOTE_USER
(인증 사용자 ID) 등의 환경변수도 매우 유용하다.

다음 소스는 환경변수를 이용해 스스로의 URL을 표시하는 간단한 소스이다.

#!/usr/bin/perl
print “Content-type: text/html\n\n”;
print “http://$ENV{‘HTTP_HOST’}$ENV{‘SCRIPT_NAME’}\n”;

 

7. 시간정보

시간정보를 얻기 위해서는 time() 함수를 이용한다. 얻을 수 있는 값은 1970년 1월 1일을 기준으로 한 초 단위 값이다. 보통의 경우 localtime() 함수를 사용하여 단위별 값들을 리스트로 추출해낸다. 다음은 시간정보를 보기 좋게 추출해내는 소스이다. 주의할 점은 요일($weekday)이나 월($month)은 0부터 시작된다는 것이다. 또한 항간에 시끄러운 Y2K문제와 관련해 고려할 부분은 $year가 실제 연도에서 1900을 뺀 수치로 구성된다는 것이다. 그러니까 $year 값에 1999년은 99, 2000년은 100이 저장된다.
외국서버를 사용하는 경우와 같이 필요에 따라 지역시간대에 따른 조정이 필요하면 time()에 초단위로 조정을 가할 수 있다. localtime(time()+(12*60*60))와 같이 사용할 경우 서버시간에서 12시간 빠른 시간정보가 추출된다.

 

#!/usr/bin/perl
print “Content-type: text/html\n\n”;
($second, $minute, $hour, $date, $month,
$year, $weekday, $yearday, $Isdst)
=  localtime(time());
$month ++;
$year += 1900;

@dayname = (“일”,”월”,”화”,”수”,”목”,”금”,”토”);
$weekday = $dayname[$weekday];

print “일자 : $year년 $month월 $date일 $weekday요일<BR>\n”;
print “시간 : $hour시 $minute분 $second초<BR>\n”;

 

PHP에서 사용하는 date 함수처럼 표시형식의 자유로운 지정이 가능한 Perl 함수를 만들어보는 것도 좋은 공부가 될 것이다.

 

8. 연속출력

정보를 연달아 출력하기 위해서는 수많은 print문을 사용하기도 하고 print문 하나로 다수의 라인을 출력하기도 하지만 “ 기호 등을 표시할 때 \”와 같이 표시해 줘야하므로 매우 불편하다. 하지만 다음과 같이 print문을 print <<”구분문자”;와 같이 사용하게 되면 “구분문자”가 나타날 때까지 계속 출력할 수 있다. 해당범위 내에서는 HTML 문서처럼 코딩해도 크게 문제가 없으며 Perl에서 사용중인 변수를 직접 사용할 수도 있고 “ 등의 큰따옴표를 가공 없이 사용할 수도 있다.  
유의할 점은 끝을 알리는 “구분문자”는 첫 번째 자릿수부터 입력되어야 한다는 것과 메일주소에 쓰이는 @기호는 \@와 같이 사용해야 한다는 것이다. 만약 print <<’EOF’;와 같이 작은따옴표를 이용해 구분문자를 감싸주면 @ 기호를 그대로 사용할 수 있지만 변수참조가 불가능하다.

 

#!/usr/bin/perl
$user = “way”;
$address = “Kwangju”;
print “Content-type: text/html\n\n”;
print <<EOF;
<HTML>
<HEAD>
<TITLE>연속출력 시험</TITLE>
</HEAD>
<BODY>
성명 : “$user”<BR>
주소 : “$address”<BR>
</BODY>
</HTML>
EOF
print “출력완료”;

 

9. 메일전송

게시판에 새로운 글이 등록되거나 방문자가 방명록에 글을 남길 경우, 특정양식의 신청서를 입력받는 경우 메일로 정보가 전달되도록 메일을 전송하는 방법을 알아보자.
앞서 설명했듯이 파일을 조작하기 위해 다음과 같이 사용했었다.

open(파일핸들, “[조작방법] 파일명”);

여기에 조작방법을 “|” 기호로 하고 파일명 대신 명령어(sendmail)나 장치(프린터 등)를 적어주면 Perl에서 정보를 전달할 수 있다.

다음은 메일을 전송하는 소스이다.  @기호는 \@와 같이 표시하거나 큰따옴표 대신 작은따옴표로 감싸주면 에러가 발생하지 않는다.

 

#!/usr/bin/perl
open(MAIL,”|/usr/sbin/sendmail -t”);
  print MAIL “To: way\@abc.co.kr\n”;
  print MAIL “Subject: 제목입니다.\n\n”;;
  print MAIL “메세지 본문입니다.”;
close (MAIL);

print “Content-type: text/html\n\n”;
print “전송완료”;

 

발송자(From)를 지정하려면 To 아랫줄에 다음과 같이 삽입한다.

print MAIL “From: 임대호 <lawwal\@way.co.kr>\n”;

HTML형식으로 메일을 보내려면 Subject 부분을 다음과 같이 바꿔주면 된다.

print MAIL “Subject: 제목입니다.\n”;
print MAIL “Content-type: text/html\n\n”;

 

10. 쿠키

게시판에 글을 쓰면 다음에 글을 쓸 때 자동으로 이름과 메일주소를 적어주거나 접속자 인증에 주로 쿠키가 사용된다.  Perl에서도 쿠키를 직접 핸들링할 수 있다.
다음 소스는 Perl에서 사용자의 브라우져에 쿠키값을 저장하는 함수이다.

 

sub cookie_set {
   local($name, $value, $exp) = @_;
   $exp = scalar localtime(time()+(24*60*60)*$exp);
   print “Content-type: text/html\n”;
   print “Set-Cookie: $name=;\n”;
   print “Set-Cookie: $name=$value;expires=$exp;\n”;
 }

 

사용자의 브라우져에 저장된 쿠키값은 환경변수인 $ENV{‘HTTP_COOKIE’}에 저장된다.
다음 소스는 쿠키를 읽어들여 %cookie라는 조합배열에 넣어 활용이 가능토록 하는 함수이다.  자세히 보면 앞서 설명한 input_form() 함수와 유사하다는 것을 알 수 있을 것이다.

 

sub cookie_read {
@strings = split(/; /,$ENV{‘HTTP_COOKIE’});
foreach $strs (@strings) {
   local ($name, $value) = split(/=/,$strs);
       $value =~ tr/+/ /;
       $value =~ s/%(..)/pack(“C”, hex($1))/ge;
       $cookie{$name} = $value; }
  }

 

두 개의 함수를 이용해서 쿠키를 저장하고 읽어보도록 하겠다.

#!/usr/bin/perl
&cookie_set(“name”, “way”, 7);   # 키, 값, 만료기일
  print “Content-type: text/html\n\n”;
  print “쿠키를 저

관련자료

댓글 0
등록된 댓글이 없습니다.

공지사항


뉴스광장


  • 현재 회원수 :  60,150 명
  • 현재 강좌수 :  36,432 개
  • 현재 접속자 :  278 명