반응형

보다 복잡한 프로그램을 작성하기 위해서는 프로그램을 구조화하여 객체 지향 기술을 사용하는 것이 중요해지고 있다. 더욱 진보한 프로그래밍을 위해 이에 대해 이해해 두어야 한다.


함수로 구조화하기

프로그래밍 수준이라는 것은 무엇으로 결정할까? 이는 사실은 '분량'이라고 말할 수 있다. 전부해서 100 라인 정도의 프로그램이라면 아마추어도 작성할 수 있지만, 1000 라인의 프로그램은 초보자는 작성할 수가 없다. 1만 라인의 프로그램은 프로가 아니면 어렵다. 이는 사용하는 함수가 어렵거나 알고리즘이 복잡하거나 하는 것은 이전의 문제이다. 즉, "얼마나 큰 프로그램을 깔끔하게 정리해서 파악할 수 있도록 설계할 수 있는가"라는 문제이다.

100 라인 프로그램은 단지 줄줄이 쓰는 것만으로도 만들 수 있다. PHP으로 여러 폼을 송신하거나 하는 것만으로도 순식간에 작성할 수 있을 것이지만, 1000 라인 프로그램은 그런 식으로는 만들 수 없다. 프로그램을 그 역할에 띠라 정리하고 필요에 따라 필요한 처리를 호출하는 구조를 제대로 생각해서 설계해야 한다. 이것이 "구조화"라는 개념이다.

PHP에서 구조화의 첫 걸음은 "함수"이다. 그냥 줄줄이 쓰고 있었던 스크립트를 정리하고 부분 부분을 분리하여 "함수"로 정의해 나가는 것으로, 전체적으로 깨끗하게 알기 쉽게 설계하는 것이 더 큰 프로그램을 쓸 수 있게 되는 첫 걸음이라고 할 수 있다.

함수라고 하면, 지금까지 echo 라든지 implode/explode이나 fopen/fclose 이라든지, 여러 가지가 등장 했었다. PHP에서는 기본적인 기능은 모두 함수로 정의되어 있다. 이 함수라는 것은 이렇듯 'PHP 본체에 준비되어 있는 것'만 아니다. 개발자가 직접 스크립트를 작성하여 정의할 수도 있는 것이다. 이 함수는 다음과 같이 정의한다.

function 함수명(가인수1, 가인수2, ...) {
    ...... 여기에 수행할 처리을 쓴다 ......
    ...... 결과를 반환한다면, 마지막에 return한다 ......
}

함수를 만들 때, 생각해야 할 것은 "인수"와 "반환 값"이다. 인수라는 것은 함수를 호출할 때 전달되는 값이다. 예를 들면, fopen( "data.txt");와 같은 식으로 함수를 호출 할 때는 ()에 필요한 값을 넣어 지금까지도 호출했던 것이다. 이것이 인수이다.

자신의 함수를 정의할 때도 그 함수로 어떤 값이 필요할 것인가를 생각하고 그것을 인수로 지정한다. 이것은 함수의 () 내에 전달되는 값을 담을 변수를 기술해야 한다. 이것이 "가인수"라는 것이다. 함수를 호출하면 인수에 지정된 값이 이 가인수의 변수에 할당된다. 그리고 이후는 이 변수를 사용하여 전달된 인수의 값을 사용할 수 있다는 것이다.

반환 값은 그 함수를 호출 측에 결과 값을 반환하는 것이다. 이것은 반환하기 원하는 값을 "return 값;"와 같은 형식으로 작성한다. 이 return은 함수를 빠져나가서, 지정된 값을 호출한 측에 돌려준다. 이는 처리를 벗어나는 것이기 때문에, return 후에 처리가 있어도 그것은 실행되지 않는다.

그럼 함수를 실제로 사용해 보자. 우선, 함수를 이용하기 전에 소스 코드를 적어 보겠다.

<?php
    if ($_POST != null) {
        $str = $_POST['text1'];
        $str = htmlspecialchars(strtoupper($str));
         
        $result = str_replace('PHP', '<b>PHP</b>', $str);
    }
?>
<!DOCTYPE html>
<html lang="ko">
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1>Hello PHP!</h1>
        <p><?php echo $result; ?></p>
        <hr>
        <p>여기에 PHP라는 문자를 포함한 문장을 써주세요.</p>
        <form method="post" action="./index.php">
            <textarea name="text1"  cols="40" rows="5"><?php echo $str; ?></textarea>
            <br><input type="submit">
        </form>
        <hr>
    </body>
</html>

여기에는 텍스트를 써서 보내면, "PHP"라는 텍스트를 모두 볼드(bold)로 표시한다. 전송된 텍스트를 str_replace 함수로 치환하는 간단한 예제이다. 또한 여기에서 보내 온 텍스트를 "strtoupper"라는 함수로 처리하고 있다. 이는 알파벳을 대문자로 텍스트를 변환하여 반환하는 함수이다.

함수의 장점

그럼 먼저 예제를 함수로 구조화(라고 할만큼 과장하지 않지만 ...)하여 작성해 보겠다. 아래에 소스 코드가 작성된 코드이다.

<?php
    function getBoldStr($str) {
        $str = htmlspecialchars(strtoupper($str));
        $res = str_replace('PHP',  '<span style="font-weight:bold;color:red">PHP</span>', $str);
        return $res;
    }
 
    if ($_POST != null) {
        $str = $_POST['text1'];
    }
?>
<!DOCTYPE html>
<html lang="ko">
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1><?php echo getBoldStr("Hello PHP!"); ?></h1>
        <p><?php echo getBoldStr($str); ?></p>
        <hr>
        <p><?php echo getBoldStr("여기에 PHP라는 문자를 포함한 문장을 써주세요."); ?></p>
        <form method="post" action="./index.php">
            <textarea name="text1"  cols="40" rows="5"><?php echo $str; ?></textarea>
            <br><input type="submit">
        </form>
        <hr>
    </body>
</html>

이번에는 더욱 변환되는 곳을 알 수 있도록 볼드(bold) 및 빨간색으로 PHP를 표시해 보았다. 차이점을 살펴 보자.

여기에서는 최초에 function getBoldStr($str)라는 함수를 정의하고 있다. 인수는 $str는 가인수를 하나 준비했다. 이 $str의 텍스트를 치환하고 완성된 것을 return으로 반환한다. 호출하는 곳을 보면,

<?php echo getBoldStr($str); ?>

이런 식으로 작성되어 있다. getBoldStr 후에 필요한 값을 ()로 전달한다. 평범한 PHP에서 제공되는 함수와 사용법이 전혀 다르지 않다는 것을 알 수 있다.

이와 같이 함수화하는 것으로 이 처리는 프로그램의 어디서나 호출 할 수 있게 되었다. 여기에서는 전송된 텍스트 표시뿐만 아니라, 제목과 설명 텍스트 등도 모두 이 함수에서 처리하여 출력시키고 있다. 함수화를 하였기에 동일한 처리를 여러 번 쓸 필요가 없게 되었다. 언제든지 필요할 때 호출하면 필요한 작업을 수행하는 것이다. 이것이 함수화를 했을 때의 장점이다.

변수의 사용 범위 (scope)

이 스크립트를 잘 보면, 변수에 대해 좀 재미있는 것을 알게 될 것이다. 함수의 가인수에도, 그것 이외의 곳에서도, $str이라는 변수가 사용되고 있다. 같은 변수를 이렇게 사용하더라도 도중에 값이 쓰여져 변경되거나 하는 걱정은 없는 것인가? 사실은 함수에서 $str와 외부 $str는 다른 변수이다.

변수에는 그것을 사용할 수 있는 범위라는 것이 정해져 있다. 이는 "스코프(scope)"라고 한다. 함수의 안에서 변수를 사용하는 경우, 그 변수는 함수 내에서만 사용할 있도록 되어 있다. 그 함수에서 빠져나온 시점에서 그 변수는 소멸하는 것이다. 또한 다시 그 함수를 호출해도, 전에 호출 했을 때의 변수는 깨끗하게 없어져 있으며, 모든 새로 만들어 진다.

따라서 함수의 내부에서 만들어진 내부에서만 사용되는 변수를 "로컬(local) 변수"라고 한다. 이에 대해 함수 외부에서 만들어진 스크립트 전체에서 사용할 수 있게하는 변수를 "전역(global) 변수"라고 한다. 함수를 이용하는 경우에는 이 "변수의 범위(scope)"라는 것에 대해 잘 이해하고 있어야 한다.




객체 지향이란?

함수에 의한 구조는 긴 작업을 여러 개의 작은 덩어리로 구성할 수 있다. 하지만 더 큰 프로그램이 되면 이것만으로는 부족하다. 예를 들어, 소스 코드에서 1000개의 함수가 줄지어 있는 상태를 상상해 보자. 이것은 과연 '정리되어 있다"라고 할 수 있는가?

1000 개의 메소드를 더 정리하려면 어떻게 해야 좋은가? 먼저 생각할 있는 것은 "관련 것을 한곳에 모아 처리하도록 한다"는 것이다. 이렇게 하면, 예를 들어 "관련 있는 함수를 20개씩 정리한 것이 50개 늘어놓는다"라고 한다면 전체를 파악할 수 있을 것이다. 적어도 1000개가 늘어선 것보다 많이 좋아질 것이다.

함수는 그것으로 좋다고 해도, 이번에는 또 다른 문제가 발생하는 것이다. 그것은 '1000개의 전역 변수를 어떻게 할 것인가"이다. 긴 프로그램이 되면 사용하는 변수도 엄청날 것이다. 이것은 또한 관련된 변수를 한곳에 모아 정리할 수 있으면 대단히 다르다.

즉, 구조화의 다음 단계에서 누구나 생각할 수 있는 것은 "관련된 기능이나 값을 한곳에 모아 취급 방법"이라고 해도 좋을 것이다.

예를 들어 "데이터 액세스"라는 카테고리를 준비하여, 그 안에 데이터를 읽거나 검색하거나 일부를 대체 할 여러가지 기능을 밀어 넣는다. 동시에 액세스하는 데이터 파일의 이름이나 데이터의 구조 라든지 등 필요한 모든 정보를 그 안에 준비해 둔다. 그리고 필요에 따라 거기에 값을 설정하고, 거기에 있는 기능을 호출하기도 한다. "데이터 액세스에 대한 모든 데이터와 기능은 모두 이 안에 있다"라는 것과 같은 카테고리를 만들 수 있다면, 대단히 편하다.

이런 것을 다양한 용도마다 만들어 함께 간다. 이렇게 하면 아무리 프로그램이 커져도 대응할 수 있다.

이것이 '객체(object)'의 개념이다. 객체는 "자체적으로 필요한 데이터와 기능이 모두 자신 안에 준비되어 있으며, 독립적인 프로그램으로 어디서나 사용할 수 있는 카테고리"이다.

객체의 설계도 "클래스"

이 객체라는 것은 PHP에서는 '설계도'과 '만든 부품'으로 구성되어 있다. 우선 최초에 객체의 내용을 정의하는 설계도를 만들고, 그 설계도를 기초로 하여 실제로 작동하는 부품을 만드는 셈이다.

이 설계도를 "클래스(class)", 클래스로 부터 만들어진 부품을 "인스턴스(instance)"라고 한다. 클래스는 다음과 같은 형태로 설계한다.

class 클래스 {
    필드1;
    필드2;
    ...... 필요한 만큼 기술 ......
    
    function 메소드1 (가인수) {
        ...... 수행 할 처리
    }

    function 메소드2 (가인수) {
        ...... 수행할 처리
    }

    ...... 필요한 만큼 기술 ......

}

클래스는 '필드'와 '방법'으로 구성된다. "필드"라는 것은 클래스에 값을 저장하는 변수이다. 그리고 "메소드"는 클래스 안에 준비된 처리를 작성한 함수이다. 이 2가지 요소를 필요에 따라 작성하고 클래스는 만들어진다.

그리고 만든 클래스에서 인스턴스(실제로 조작하는 부품)를 만들어, 그 안에 있는 필드나 메소드에 액세스하려면 다음과 같이 한다.

$변수 = new 클래스(인수);
$변수->필드;
$변수->메소드(인수);

인스턴스는 "new 클래스()"와 같은 형태로 만든다. 클래스에 따라 이 때에 인수가 필요한 경우도 있다. 이것으로 인스턴스가 생성된 변수로 설정된다. 인스턴스에 메소드나 필드를 조작하는 경우, "$변수->필드", "$변수->메소드()"와 같은 식으로 인스턴스가 할당된 변수 뒤에"->"라는 기호를 붙이고 그 필드명이나 메소드명을 작성한다.

클래스에 대해서는 설명하지 않으면 안되는 것은 아직도 산더미처럼 있지만, 우선은 "클래스 정의 방법", "클래스에서 인스턴스를 만드는 방법", "인스턴스의 필드나 메소드를 이용하는 방법"을 알면, 당장이라도 클래스를 이용할 수 있게 될 것이다.




클래스를 만들어 보자!

그러면 실제로 클래스를 만들고, 그것을 이용하여 보자. 이전에 "텍스트 속에서 PHP라는 텍스트만 표시를 변경"하는 예제를 더 수정하고, 좀 더 범용적인 클래스를 만들어 그것을 이용하기로 해보자.

아래와 같이 예제 코드를 작성한다.

<?php
class TextModify {
    private $header = "<b>";
    private $footer = "</b>";
    private $body = "";
    private $find = "PHP";
     
    public function __construct($h,$f){
        $this->setHeader($h);
        $this->setFooter($f);
    }
     
    function setHeader($s){
        $this->header = $s;
    }
     
    function setFooter($s){
        $this->footer = $s;
    }
     
    function setBody($s){
        $this->body = htmlspecialchars(strtoupper($s));
    }
    function setFind($s){
        $this->find = $s;
    }
     
    function getRenderText(){
        $res = str_replace($this->find, $this->header . $this->find . $this->footer, $this->body);
        return $res;
    }
     
    function writeRenderText(){
        echo $this->getRenderText();
    }
}
 
// 인스턴스 준비
$title_obj = new TextModify('<span style="color:red;">', '</span>');
$title_obj->setFind("PHP");
$title_obj->setBody('Hello PHP!');
$msg_obj = new TextModify('<span style="color:blue;">', '</span>');
$msg_obj->setBody('여기에 PHP라는 문자를 포함한 문장을 써주세요.');
$rep_obj =  new TextModify("<b>","</b>");
if ($_POST != null){
    $str = $_POST['text1'];
    $rep_obj->setBody($str);
}
?>
<!DOCTYPE html>
<html lang="ko">
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1><?php $title_obj->writeRenderText(); ?></h1>
        <p><?php $msg_obj->writeRenderText(); ?></p>
        <hr>
        <p><?php $rep_obj->writeRenderText(); ?></p>
        <form method="post" action="./index.php">
            <textarea name="text1" cols="40" rows="5"><?php echo $str; ?></textarea>
            <br><input type="submit">
        </form>
        <hr>
    </body>
</html>

여기에서는 "TextModify"라는 클래스를 만들고 이를 이용하여 헤더용, 메시지용, 그리고 텍스트의 대체용의 인스턴스를 준비하고 각각의 기능을 호출하여 표시를 작성한다.

만든 TextModify 클래스를 보면 새롭게 등장한 것이 몇개가 등장하고 있다. 그들을 보충 설명하여 보겠다.

1. 접근자 키워드(access keywords)

여기에서는 $header, $footer, $body, $find라는 4개의 필드를 제공하고 있다. 각각 대체 텍스트 헤더(전에 붙이는 텍스트), 풋터(뒤에 붙이는 텍스트), 바디(대상이 되는 텍스트 본체), 그리고 검색 문자를 저장하는 용도이다.

이 필드들을 보면, 앞에 "private"이라는 것이 붙어 있다. 이것은 이 필드를 어디까지 사용할 수 있는가 하는 이용 범위를 지정하는 것으로, "접근자 키워드"라는 것이다. 여기에는 다음과 같은 것이 준비되어 있다.

  • private : 해당 클래스 중에서만 사용할 수 있다. 외부에서는 절대 사용할 수 없다.
  • protected : 해당 클래스 및 그 서브 클래스에서 사용할 수 있다.
  • public : 클래스 밖에서도 사용할 수 있다.

이 중에 "서브 클래스"라는 것은 객체 지향의 "상속"이라는 기능을 이용한 것이다. 이것에 대해서는 나중에 다시 설명하겠다. 우선 여기에서는 "private으로 하면 클래스 안에서만 사용할 수 있고, public으로 하면 외부에서도 자유롭게 사용할 수 있다"라고만 기억해 두도록 하자.

이러한 필드는 객체의 성질 등을 나타내는 값이 저장되어 있기 때문에, 외부에서 마음대로 변경해 버리면 안된다. 그래서 필드 자체는 private으로 두고, 변경용 메소드를 별도로 준비하여 외부에서 그 메소드를 호출하여 값을 조작할 수 있도록 하는 것이 일반적이다.

2. 생성자 (construct)

여기에서는 __construct라는 특이한 이름의 public(즉, 외부에서 자유롭게 사용할 수 있는) 메소드가 작성되어 있다. 이 메소드는 "생성자"라고 하는 것이다. 이는 new로 인스턴스를 생성할 때, 자동으로 호출되는 메소드에서 인스턴스의 초기화 처리 등을 실행하며, 필요 없으면 생략해도 된다.

이 생성자에는 2개의 인수를 준비하고 있기에 new 할 때도 2개의 인수를 넣게 된다. 실제로 new하고 있는 부분을 보면,

$rep_obj =  new TextModify("<b>", "</b>");

이런 상태에 헤더와 풋터를 인수로 지정해 new한다.

3. 접근 메소드 (access method)

클래스에서 제공되는 메소드 중에는 필드를 조작하는 것이 몇 가지가 준비되어 있다. setHeader, setFooter, setBody, setFind가 있다.

앞서 언급한 바와 같이, 필드는 private으로 해두고, 그 값을 얻거나 변경하는 메소드를 별도로 준비해 두는 것이 일반적이다. 이러한 방법을 '접근자'라고 한다.

접근자는 일반적으로 "get필드명", "set필드명"라는 이름으로 선언한다. 이는 그렇지 않으면 안되는 것은 아니지만, 이러한 규칙에 따라 이름을 붙이는 편이 알기 쉽게 된다.

4. $this 대해

메소드는 "자기 자신"에 대해 작업을 수행하는 경우가 많다. 일반적으로 클래스는 인스턴스를 만들어 사용하지만, 이 "지금 사용하고 있는 이 인스턴스 자신안에 있는 필드나 메소드"를 이용하는 경우 어떻게 해야할까?

이러한 경우에 사용되는 것이 "$this"라는 변수이다. 이것은 "이 인스턴스 자신"을 나타내는 변수이다. 이를 이용하여 자기 자신의 기능을 호출 할 수 있다.

 

우선, 이러한 내용을 생각하면 코드를 보도록 해보자. 어떤 식으로 클래스와 인스턴스가 동작하고 있는지 알게 될 것이다.



상속에 의한 기능 확장

객체 지향에는 작성한 클래스는 여러 곳에서 재사용할 수 있다. 하지만 실제로 사용하려고 하면 "여기를 이렇게 재작업하여 사용하고 싶다"라든지 "이것은 기능으로 좀 부족 하니까 더 확장하고 싶다"는 것도 나올 것이다.

이런 경우에 클래스의 코드를 재작성하여 변경하는 것도 하나의 방법이지만, 만약 큰 프로젝트에서 많은 곳에 이미 사용되고 있거나 했을 때, 마음대로 클래스의 내용을 변경해서는 안될 것이다. 그렇다고 해서 클래스의 코드를 복사하고 또한 새로운 클래스를 만드는 것은 바보 같은 짓이다. 거의 같고 일부만 조금 다른 클래스를 여러 만드는 것은 코드의 낭비이다.

아래의 클래스는 건드리지 않고, 최소한의 수정만으로 클래스를 재사용 할 수 있게 한다는 생각을 바탕으로 만들어진 것이 '상속'이라는 개념이다.

상속은 이미있는 클래스의 기능을 그대로 모두 계승하고 새로운 클래스를 만드는 것이다. 이것은 다음과 같이 클래스를 정의하여 사용할 수 있다.

class 클래스명 extends 상속하는 클래스 {...}

이 상속하는 근원이 되는 클래스를 "슈퍼 클래스", 상속하고 새로 만든 클래스를 "서브 클래스"라고 한다. 서브 클래스는 슈퍼 클래스의 모든 필드, 메소드를 이어 받아 그대로 사용할 수 있다 (그러나, 액세스 키워드가 private으로 된 것은 감춰져 있기 때문에 사용할 수 없다).

아래는 이전에 예제를 더 수정 한 것이다.

<?php
class TextModify {
    private $header = "<b>";
    private $footer = "</b>";
    private $body = "";
    private $find = "PHP";
     
    public function __construct($h,$f){
        $this->setHeader($h);
        $this->setFooter($f);
    }
     
    function setHeader($s){
        $this->header = $s;
    }
     
    function setFooter($s){
        $this->footer = $s;
    }
     
    function setBody($s){
        $this->body = htmlspecialchars(strtoupper($s));
    }
    function setFind($s){
        $this->find = $s;
    }
     
    function getRenderText(){
            $res = str_replace($this->find, $this->header . $this->find . $this->footer, $this->body);
        return $res;
    }
     
    function writeRenderText(){
        echo $this->getRenderText();
    }
}
 
class TitleModify extends TextModify {
     
    public function __construct($s){
        $this->setHeader('<span style="color:red;">');
        $this->setFooter('</span>');
        $this->setFind('PHP');
        $this->setBody($s);
    }
}
 
class MsgModify extends TextModify {
     
    public function __construct($s){
        $this->setHeader('<span style="color:blue;">');
        $this->setFooter('</span>');
        $this->setFind('PHP');
        $this->setBody($s);
    }
}
 
class RepModify extends TextModify {
     
    public function __construct($s){
        $this->setHeader('<b>');
        $this->setFooter('</b>');
        $this->setFind('PHP');
        $this->setBody($s);
    }
}
 
// 인스턴스 준비
$title_obj = new TitleModify('Hello PHP!');
$msg_obj = new MsgModify('여기에 PHP라는 문자를 포함한 문장을 써주세요.');
if ($_POST != null){
    $str = $_POST['text1'];
    $rep_obj = new RepModify($str);
}
?>
<!DOCTYPE html>
<html lang="ko">
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1><?php $title_obj->writeRenderText(); ?></h1>
        <p><?php $msg_obj->writeRenderText(); ?></p>
        <hr>
        <p><?php if (isset($rep_obj)) $rep_obj->writeRenderText(); ?></p>
        <form method="post" action="./index.php">
            <textarea name="text1" cols="40" rows="5"><?php echo $str; ?></textarea>
            <br><input type="submit">
        </form>
        <hr>
    </body>
</html>

여기에서는 TextModify 클래스를 상속하여 "TitleModify", "MsgModify", "RepModify"라는 서브 클래스를 만들었다. 각 클래스에서는 각각 __construct을 준비하여 필요한 설정을 할 수 있도록 하고 있다.

이 서브 클래스들에는 생성자 밖에 없지만, 그 안에 각 필드의 값을 접근 메소드로 설정하고 있으며, 실제 이용은 writeRenderText로 출력을 하고 있다. 슈퍼 클래스 TextModify의 기능을 그대로 사용할 수 있다는 것을 알 수 있다.

이렇게하여 클래스를 상속해서 기능을 더 확장하여 재사용하는 것으로, 보다 유연하게 클래스 작성을 할 수 있게 되는 것이다.



클래스 메소드와 클래스 필드

지금까지는 "클래스는 인스턴스를 만들고 조작한다"것이 기본이라고 설명했었다. 그러나 실은 꼭 그래야만 하는 것인 아니다. 클래스를 클래스 그대로 사용할 수도 있다. 다만, 그러기 위해서는 메소드나 필드를 다른 형태로 작성을 해야 한다.

인스턴스가 아닌 클래스에서 직접 사용할 수 있는 메소드나 필드는 "클래스 메소드", "클래스 필드 '라고 한다. 클래스를 단순한 설계도라고 생각하면, 어떻게 클래스 안에 값을 저장하거나 메소드를 실행하거나 할 수 있는지 조금 이상한 느낌이 있지만, "클래스도 객체에서 클래스라는 객체를 바탕으로 인스턴스라는 객체를 만들고 있다"고 상상하면 좋을 것이다.

클래스에서 제공하는 메소드나 필드는 앞에 "static"라는 키워드를 붙인다. 이는 '정적'이라는 의미로, 클래스 메소드나 클래스 필드는 "static 메소드", "static 필드"라고도 한다.

그럼 이러한 기본적인 사용법을 간단하게 정리해 보자. 우선 이러한 정의는

static $변수;
static function 메소드(가인수) {...}

이렇게 앞에 static을 넣기 만하면 된다. 액세스 키워드를 넣는 경우는 static 후에 "static private $hoge;"와 같은 식으로 쓴다. 이러한 필드나 메소드의 호출은

클래스::$변수;
클래스::메소드(인수);

이와 같이 작성한다. 클래스명 후에는 ::라는 기호를 붙이고 작성한다. 주의할 점은 필드이다. 인스턴스 필드는 "클래스->변수"과 같이 작성하지만, 클래스 필드의 경우 "클래스::$변수"과 같이, $를 붙여 작성한다.

그럼 이도 실제 예제를 보도록 하자.

<?php
class TextModify {
    static private $header = "<b>";
    static private $footer = "</b>";
    static private $find = "PHP";
     
    static function setTagData($h,$f) {
        self::$header = $h;
        self::$footer = $f;
    }
     
    static function setFind($f) {
        self::$find = $f;
    }
     
    static function writeRenderText($s) {
        $res = str_replace(self::$find, self::$header . self::$find . self::$footer, $s);
        echo $res;
    }
}
     
// 클래스 준비
TextModify::setTagData('<span style="font-size:200%;">', '</span>');
TextModify::setFInd('PHP');
 
if ($_POST != null){
    $str = $_POST['text1'];
}
?>
<!DOCTYPE html>
<html lang="ko">
    <head> 
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
        <title>sample page</title>
    </head>
    <body>
        <h1>
        <?php
        TextModify::writeRenderText('Hello PHP!');
        ?>
        </h1>
        <p><?php TextModify::writeRenderText('여기에 PHP라는 문자를 포함한 문장을 써주세요.'); ?></p>
        <hr>
        <p><?php TextModify::writeRenderText($str); ?></p>
        <form method="post" action="./index.php">
            <textarea name="text1" cols="40" rows="5"><?php echo $str; ?></textarea>
            <br><input type="submit">
        </form>
        <hr>
    </body>
</html>

위 예제는 먼저번 예제인 TextModify 클래스를 클래스 메소드와 클래스 필드를 이용하는 형태로 다시 작성한 것이다. 여기에서는 PHP라는 텍스트를 2배의 글꼴 크기로 표시하고 있다.

이 예에서는 setTagData에 끼워 넣는 전후의 태그를 설정하고, setFind으로 검색 문자를 설정한다.

TextModify::setTagData('<span style="font-size:200%;">', '</span>');
TextModify::setFInd('PHP');

new로 인스턴스를 만들지도 않고, 바로 클래스에 값을 설정하고 있다. 그리고 텍스트를 출력할 때,

TextModify::writeRenderText('Hello PHP!');

이런 식으로 그냥 메소드를 호출할 뿐이다. 매우 간단하게 사용하고 있다.

클래스 메소드와 클래스 필드를 사용하면, 인스턴스를 만들지 않고도 이처럼 매우 간단하게 기능을 호출 할 수 있다. 반면 다양한 데이터를 보유하고 그것에 따라 동작하는 경우는 사용할 때마다 많은 데이터를 다시 설정해야 않기 때문에 매우 복잡하다.

많은 데이터를 보유하고 그에 따라 처리를 실시하는 것은 인스턴스를 만들어 사용하는 것이 훨씬 편리 하다. 클래스 메소드는 변경하는 일이 거의 없는 데이터 밖에 없는 경우, 단순히 정해진 기능을 호출만 하는 경우에 사용하면 좋다.

예를 들어, 수치 계산(인수로 숫자를 전달해서 결과를 반환 받는 일) 등을 클래스로 정리해서 사용하는 경우에는 일일이 인스턴스를 만드는 것보다, 클래스 메소드로 손쉽게 호출하는 것이 편리하다. 용도에 따라 구분하여 사용하는 것이 좋다.



객체 지향은 무엇이 필요한가?

그런데, 객체 지향의 기본, 특히 "클래스"를 만드는 방법 및 사용법에 대해 대충 설명을 했지만, 읽어보고 어떻게 느꼈는가?

"편리하겠지만, 귀찮다. 실제로 사용할 수 없잖아?"

이렇게 생각한 사람도 많을 것이다. 분명한 것은 방대한 코드를 작성하게 되면 필요한 것이다. 하지만 그렇게 거대한 프로그램 따위 만들지 않는다. 자신이 만드는 사이트의 규모는 일일이 클래스는 필요로 하지 않는 것이다. 그렇게 생각하는 사람도 분명 많을 것으로 생각된다.

하지만 그런 사람도 객체 지향을 이해할 수 없는 곤란한 것이 여러가지 있는 것이다. 그것은 무엇을 의미하나?

1. 표준 기능이 클래스로 대체되고 있다.

먼저, 무엇보다 큰 것은 "최근의 PHP에는 표준으로 제공되는 기능이 함수가 아닌 클래스로 대체되고 있다"는 점이다.

예를 들어 XML을 이용하기 위한 기능이라고 한다. 이는 옛날에는 모든 함수로 준비되어 있었지만, 지금은 SimpleXMLElement라는 클래스의 인스턴스를 생성하고 이용하는 것이 일반적으로 되어 가고 있다.

또한, 데이터베이스 액세스도 이전에 각 데이터베이스마다 함수가 많이 준비되어 있었지만, 지금은 PDO(PHP Data Objects)라는 클래스의 인스턴스를 사용할 수 있게 하고 있다.

따라서 표준으로 제공되는 기능이 점차 함수에서 클래스로 대체되고 있다. 향후 이용을 감안할 때, 객체 지향에 익숙해야 한다.

2. 프레임워크는 대부분 클래스

최근에는 어느 정도 규모의 사이트가 되면 "프레임워크(framework)"를 이용하는 경우가 많아지고 있다. 특히 "CakePHP"는 간단하게 이해하기 쉬운 프레임 워크로 널리 사용되고 있다.

이러한 프레임워크는 거의 모두가 "클래스"로 구성되어 있다. 프레임워크는 방대한 소스 코드로 구성되어 있으며, 모든 함수로 구축하려고 하면 무척 어려운 코드가 될 것이다. 또, 클래스로 정리하지 않으면 엄청 복잡하게 되어 버릴 것이다.

이러한 프레임워크를 사용하려면 객체 지향의 지식은 필수라고 할 수 있다.

3. 유지 보수를 생각하면 클래스로 해야 한다.

지금 사이트를 만든 사람은 "일단 동작하다면 괜찮아"라고 생각하고 있을지도 모른다. 하지만, 만약 "오랫동안 사이트를 운영하고 싶다"고 생각한다면, 앞으로의 유지 보수를 생각하지 않으면 안된다.

유지 보수를 생각했을 때 무엇보다 중요한 것은 "비록 자신이 작성한 코드도 반년 지나면 잊어버린다"라는 것이다. 점차 사이트가 정교해지고 있고, 이에 따라 함수도 점점 늘어나게 되면, 언젠가 본인도 버거울 질지도 모른다.

처음부터 클래스의 형태로 정비해두면 어느 정도 깔끔하게 정리해 둘 수 있다. 또한 기능을 강화하는 데에도 이미 있는 클래스를 상속하고 확장하는 등 해나가면 쉽게 대응할 수 있다. 무엇보다 여차하면 원래의 클래스에 되돌릴 수도 있기 때문에 문제되도 안심이다.

 

이런 것을 생각한다면, PHP에도 이제 객체 지향의 지식은 필수가 되고 있다고 해도 좋을 것이다. "자신은 클래스 따윈 필요 없어"라고 생각하고, 우선 지식만으로도 몸에 익혀두면 만일의 경우 불편없이 끝날지도 모른다.

"우선, 객체 지향에 의한 사이트 구축이 무엇인지 공부하고 싶다"고 생각한다면, "CakePHP" 프레임워크를 사용해 보는 것을 추천한다. 이것을 사용해 보면 객체 지향에 의한 사이트 구축의 세계가 어떤 것인지 잘 알 수 있다.

반응형

'php' 카테고리의 다른 글

[php] XML 데이터의 이용  (0) 2017.12.09
[php] PDO를 사용한 데이터베이스 엑세스  (1) 2017.12.09
[php] 구조와 객체 지향  (0) 2017.12.09
[php] 페이지 전환, 쿠키, 세션  (0) 2017.12.09
[php] 텍스트 및 날짜 조작  (0) 2017.12.09
[php] 텍스트 파일 이용  (0) 2017.12.09

+ Recent posts