티스토리 뷰

구성해 나갈 PHP 예제는 PHP5를 기준으로 작성하고자 합니다.
곧 PHP6가 나온다는 말이 있는데, 일단 PHP5로 작성해 보고 나중에 PHP6가 나오면 어떤 차이점이 있는지 찾아보도록 하겠습니다.

 


예제를 구성해 나가는데 있어서, 주의점이 있습니다.
많은 부분 순수한 PHP가 아닌 Java에서 개념을 차용해 왔습니다. 왜냐면 이미 Java에서는 보편적인 개념들이 이번 PHP5 클래스와 오브젝트에 적용될 수 있다고 생각되었습니다. 그래서, 최대한 Java에 적용되는 개념을 동원해서 PHP 예제를 구성해 보고자 합니다.

 


기본 문법 설명


1. 주석
주석은 파일의 내용을 설명하거나, 함수의 용도를 설명하거나 필요에 따라서 유지보수 차원에서 기록하는 문장입니다.
이번에 사용할 주석은 크게 2가지로서 다음과 같습니다.


    가. 한줄 주석
        한줄 주석은 "//" 슬래쉬 2개를 사용해서 작성하겠습니다.
    나. 여러줄 주석
        여러줄 주석은 "/** 내용 */" 슬래쉬 한개와 별표 2개, 그리고 주석내용이 오고 마지막으로 별표 1개와
        슬래쉬 1개로 작성하겠습니다.


사용 예)
<?
/**
* 사용자 정보 객체를 정의합니다.
*
* author  Hyoseok Kim(toriworks@gmail.com)
* 2008.10.11
*/


// 사용자 정보객체 추가 변수 선언
private $regdate = "";
?>

 


2. 클래스
이번 "클래스로 만드는 PHP 예제"에 상당히 많이 사용될 키워드입니다. 상당 부분을 클래스로 작성하고자 합니다. 클래스에 대해서는 그때 그때 내용이 진행되면서 설명을 드리도록 하겠습니다.


사용 예)
<?
// 클래스를 키워드를 사용하여 PHP용 클래스를 만듭니다.
class User {


    // 클래스 안의 내용


} // class end
?>


위의 사용 예를 보면 굵은 글씨로 된 부분이 실제 클래스 stub 코드입니다. 이 stub 코드 안에 내용을 PHP 문법에 맞게 입력해 주면 됩니다.

 


3. 생성자, 소멸자(constructor, destructor)
생성자와 소멸자는 클래스에 있어서 특별한 존재들입니다. 클래스가 객체로 될 때 자동으로 호출되는게 생성자이고, 객체에서 해제될 때 호출되는게 소멸자입니다.
(destructor : 소멸자 혹은 소거자로 해석이 됩니다만, 논란꺼리가 되지 않으므로 "클래스로 만드는 PHP 예제"에서는 소멸자로 통일해서 부르도록 하겠습니다.)


사용 예)
<?
class User {


    // Constructor
    public __construct() {
        // 객체 생성시 수행할 내용
    }


    // Destructor
    public __destruct() {
        // 객체 소멸시 수행할 내용
    }


} // class end
?>


User라는 클래스를 만들고 그 안에 생성자(__construct())와 소멸자(__destruct())를 만들어 보았습니다.
생성자와 소멸자는 이미 이런 형식으로 정해진 키워드이기 때문에 예제에 보여드린 대로 사용하셔야 합니다.
(언더바(_) 2개와 키워드 조합을 사용)


다만, 생성자나 소멸자 앞에 function 키워드를 사용하는 건 가능합니다.
꼭 그렇게 해야할 필요는 없겠지만, function 키워드를 사용해도 특별히 에러가 발생하거나 하지는 않습니다.
되도록이면 PHP에서 제시한 문법을 따라줘야 하는게 맞다고 생각됩니다.


예)
// function 키워드를 붙였습니다. 문법 상 오류는 없지만, 일반적이지 않습니다.
public function __construct() {


}

 

 


인터페이스가 뭘까요? 어떻게 인터페이스라는 개념을 설명할까요? 어떤게 가장 쉬운 예제가 될까요?


고민을 해 본 결과, 실제 생활에서 쉽게 찾아 볼 수 있는 물건에서 해답을 찾았습니다.
우리가 잘 아는 콘센트가 그 대표적인 예입니다. 콘센트와 코드는 마치 하나와 같이 서로 잘 들어맞습니다. 어떻게 그렇게 잘 들어맞는 걸까요?


그건 바로 인터페이스를 서로 공유하고 있기 때문입니다.
동그란 코드(220V)는 동그란 콘센트에 들어맞고, 일자형 코드(110V)는 일자형 콘센트에 들어맞는 원리입니다. 서로 인터페이스를 정하고, 개발했기 때문에 어디에서나 동그란 코드는 동그란 콘센트에 잘 맞도록 되어 있습니다.


PHP의 인터페이스도 마찬가지라고 생각됩니다. PHP 도큐먼트에서 제시된 인터페이스에 대한 설명을 참고해 보도록 하겠습니다.
Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are handled.
인터페이스를 구현한 클래스는 반드시 특정한 메소드(PHP에서 function입니다.)를 구현하도록 강제한다고 써 있습니다. 강제한다는 건 규칙을 지키라는 의미라고 볼 수 있고, 생각의 공유(인터페이스를 작성한 사람의 생각)라고 볼 수 있습니다.

 


사용 예)
<?
/**
* 인터페이스를 만들어 봅니다.
*
* author    Hyoseok Kim(toriworks@gmail.com)
* 2008.10. 11
*/
interface UserDao {


    // 이 곳에 인터페이스로 공개될 function 을 만들어 줍니다.


} // interface end
?>


자세한 내용은 예제를 만들어 가면서 좀 더 설명하도록 하겠습니다.
사용 예에서 굵은 글씨로 된 부분이 인터페이스를 구성하는 stub 코드입니다. 기본적으로 인터페이스를 작성할 때 필요한 코드입니다.


주의점)
PHP에서 interface는 2개 이상을 구현상속하는 건 피해야 합니다.
혹시나 있을지 모르는 function 중복이 그 이유인데요, 어떤 클래스가 여러개의 interface를 상속할 경우 function 이름이 겹치거나 할 수도 있기 때문에 모호한 사용 피하기 위해 2개 이상의 interface를 사용하면 안됩니다.


interface는 클래스처럼 extends를 통해서 확장할 수 있습니다.(인터페이스를 인터페이스가 확장하는 경우)
Java에서 처럼 PHP에서도 interface를 extends로 확장 할 수 있습니다.

 


실제적인 사용 예는 아래와 같습니다. 총 4가지 정도를 생각해 볼 수 있습니다.
사용 예)
// 인터페이스를 인터페이스가 확장하는 경우
interface IAinterface extends IBinterface {


}

// 클래스를 클래스가 확장하는 경우
class Aclass extends Bclass {

}

// 클래스가 인터페이스를 구현하는 경우
class Cclass implements IDinterface {

}

// 클래스가 특정 클래스를 확장하면서 특정 인터페이스를 구현하는 경우
// Java에서는 일반적이지만, PHP에서는 테스트가 필요한 코드입니다.
class Dclass extends Eclass implements IFinterface {

}


상속 구조를 세우거나, 인터페이스 설계는 좀 더 고급 기술이기 때문에 나중에 알아보도록 하겠습니다.


Visibility : 클래스에 접근을 허용하기


Visibility를 구성하는 건 총 3가지입니다. 각각 어느만큼 접근이 허용될지에 대한 키워드 지정이라고 생각하면 됩니다.


1. public        : 어디에서나 접근이 허용됩니다.
2. protected    : 부모 클래스를 상속받은 자식 클래스만 접근이 허용됩니다.(부모는 당연히 포함)
3. private        : 자식 클래스나 외부 접근이 허용되지 않고, 자기 자신에서만 접근이 허용됩니다.


Visibility를 구성하는 키워드 의미는 상당히 쉽습니다만, visibility 사용은 보다 엄격하게 심사숙고해야 할 필요성이 있습니다.


왜냐면, 클래스 설계의 초점은 "불변성" 에 맞춰져야 합니다. 즉 "불변성" 을 보장하는 수단으로 접근의 허용, 불허용이 사용되기 때문입니다.
클래스라고 하는 것의 제작 목적을 따져서, 심사숙고하여 설계해야 하는 신중함이 필요하다고 생각됩니다.



이제 기본적인 개념에 대해 설명을 드렸으니, 실제로 클래스를 작성해보도록 하겠습니다.
역시나 실전에 사용하다보면 어떤 점을 공부해야 하는게 생각나지 않을까 생각됩니다.

 


기본적인 도메인 클래스 작성하기


도메인(또는 커맨드 클래스) 클래스를 작성해 보도록 하겠습니다. 도메인 클래스라는 용어에 집착할 필요는 없을 것 같습니다. 작성 목적은 데이터베이스 테이블과 1:1로 매핑되는 객체를 만들어 두고, DB에서 얻는 필드를 이 도메인 클래스로 매핑하고자 합니다.


<<DB 테이블>>                   <<도메인 클래스>>
      필드1             ----->             변수1
      필드2             ----->             변수2
      필드3             ----->             변수3


필드를 객체로 매핑해서 사용하면, 객체 단위로 움직이게 됨으로써 파라미터 수가 적어지고 객체 하나로 핸들링 할 수 있는 장점이 있습니다.


사용자 테이블에 대한 도메인 클래스를 작성해 보도록 하겠습니다.
<?
/**
* 사용자 정보 객체를 정의합니다.
* author    Hyoseok Kim(toriworks@gmail.com)
* 2008.10.11
*/
class User {


    // Constructor
    public __construct() {
        // 생성자에서 처리할 내용
    }


    // DB와 매핑될 필드명을 선언합니다.
    private $user_id = "";
    private $user_name = "";
    private $passwd = "";
    private $email = "";


    // 외부에서 이 객체에 접근할 함수를 선언합니다.
    public function getUserId() { return $this->user_id; }
    public function setUserId($value) { $this->user_id = $value; }
   
    public function getUserName() { return $this->user_name; }
    public function setUserName($this) { $this->user_name = $value; }


    public function getPasswd() { return $this->passwd; }
    public function setPasswd($value) { $this->passwd = $value; }


    public function getEmail() { return $this->email; }
    public function setEmail($value) { $this->email = $value; }


    // 객체에 담긴 값을 문자열로 반환합니다.
    public function toString() {
        $objStr = "user_id : ".$this->getUserId().", user_name : ".$this->getUserName();
        $objStr = $objStr.", passwd : ".$this->getPasswd().", email : ".$this->getEmail();


        return $objStr;
    }
} // class end
?>


이 파일을 "User.php" 라고 저장합니다. 가능하다면, 폴더를 클래스만 모아두는 폴더를 만들어 두면 좋습니다.


폴더 예)
source\class\domain
소스 폴더 밑에 클래스를 두고, 지금 작성한 파일을 domain 폴더 아래에 저장하면 됩니다.

 


이 클래스를 사용하려면 아래와 같이 "new" 키워드를 사용해서 객체로 만들어 주면 됩니다. 그리고, 현재 설정된 "public" 키워드가 붙은 함수에 접근 할 수 있습니다.


사용 예)
<?
// User 객체를 생성합니다.
$userObj = new User();


// User 객체에 값을 세팅합니다.
$userObj->setUserId("toriworks");
$userObj->setUserName("김효석");
$userObj->setPasswd("1234567890");
$userObj->setEmail("toriworks@gmail.com");


// User 객체에 세팅된 값을 찍어봅니다.
$userObjValue = $userObj->toString();
print "User 객체에 저장된 값 : ".$userObjValue;
?>


사용법도 쉽습니다. 객체 단위로 움직일 수 있기 때문에, 코드가 쉬워지는 좋은 점이 있습니다. 당장은 아니지만, 나중에 DB에 값을 입력하거나 할때 보다 소스가 쉬워지는 걸 보여드릴 수 있을 것 같습니다.


이렇게만 사용해도 코드를 상당히 재사용할 수 있는 장점이 있습니다.


계속 예제를 살펴보도록 하겠습니다.



지난 시간까지는 기본적인 내용을 살펴보았는데요, 이번 시간에는 지난 시간의 domain 객체 만들기에 이어서 dao 객체를 만들어 보도록 하겠습니다.

dao 객체에는 인터페이스 1개와 이 인터페이스를 구현하는 클래스를 작성해 보도록 하겠습니다.

폴더 예)

source\class\dao


위와 같이 폴더를 구성해 줍니다.

도메인 객체인 User.php 를 이용하는 dao 객체를 다음과 같이 만들어 보도록 하겠습니다.

인터페이스 작성 예)

<?
/**
* 사용자 정보 객체 관리를 위한 인터페이스를 공개한다.
* 등록, 수정, 삭제 목록, 목록 수
*
* author Hyoseok Kim(toriworks@gmail.com)
* since 2008.11.17
*/

interface UserDao {
    public function add( $conn, $obj );
    public function update( $conn, $obj );
    public function delete( $conn, $obj );
    public function lists( $conn, $wParam, $orderBy, $curPage, $pageMax );
    public function listsCount( $conn, $wParam );

    // 기타 외부에 공개할 function을 적어주시면 됩니다.
}
?>


총 5개의 인터페이스를 외부에 공개하도록 하겠습니다. 일명 규약을 세우는 작업입니다. 이 인터페이스를 구현 상속하는 클래스는 위의 5개의 function을 반드시 구현해야 하는 거죠.

그러면, 이 인터페이스를 구현하는 클래스를 만들어 보도록 하겠습니다.

클래스 작성 예)

<?
/**
* 사용자 정보 객체 관리 인터페이스를 구현한다.
* 등록, 수정, 삭제, 목록, 목록 수 인터페이스 구현
*
* author Hyoseok Kim(toriworks@gmail.com)
* since 2008.11.17
*/

require_once("/class/dao/UserDao.php");

class UserDaoImpl implements UserDao {
    // 상수 선언 (필요에 따라 선언하세요.)
    const SUCCESS = 100;
    const FAIL = 101;
    const ZERO = 0;

    // Constructor
    public function __construct() {}

    public function add( $conn, $obj ) {
        // 입력 결과 값
        $resultOfAdd = 0;

        // 질의문 작성
        $sql = "INSERT INTO user(필드명1, 필드명2, 필드명3) VALUES ";
        $sql .= ('".$obj->get필드명1()."','".$obj->get필드명2()."','".$obj->get필드명3()."') ";

        $resultOfAdd = mysql_query( $sql, $conn ) or die("location : UserDaoImpl add, error : ".mysql_error() );
        return $resultOfAdd;
    }

}

// 다른 function도 위와 같은 형식으로 구현합니다.
?>

그리 어렵지 않습니다.
위에서 파라미터로 넘어간 부분을 보면 $obj 가 있는데, 이 부분이 지난 시간에 작성했던 domain 객체 입니다. 이런 형식으로 넘겨주면, getXXX() 방식으로 값을 꺼내서 질의문을 만든다음 DB 처리를 하면 됩니다.




지난 시간에는 domain 객체를 파라미터로 받아서 DB 처리를 하는 dao 객체를 만들어 봤습니다. 이번에는 domain 레이어와 맞물리는 service 객체를 만들어 보도록 하겠습니다.

service 객체는 아래처럼 service 별로 폴더를 따로 구성해 보도록 하겠습니다.

폴더 예)
souce\class\service\user

dao 객체 만들기와 마찬가지로 인터페이스를 하나 구성하고, 이 인터페이스를 구현하는 클래스를 만들도록 하겠습니다.

인터페이스 예)
<?
/**
* 사용자 쪽 데이터베이스를 다루기 위한 서비스 인터페이시를 외부에 공개한다.
*
* author Hyoseok Kim(toriworks@gmail.com)
* since 2008.11.17
*/

interface UserService {
    public function add( $conn, $obj );
    public function update( $conn, $obj );
    public function delete( $conn, $obj );
    public function lists( $conn, $wParam, $orderBy, $curPage, $pageMax );
    public function listsCount( $conn, $wParam );
}
?>

눈치가 빠르시군요. dao 객체에서 만들어본 인터페이스와 같은 구조입니다. function 이름까지 동일하고 파라미터까지도 동일합니다. 어떻게 보면 service 레이어는 dao를 호출하기 위한 껍데기 처럼 느껴지는 군요.

그렇다고 service 레이어가 불필요하지는 않습니다. service 레이어쪽에서 어떤 기능상의 flow를 조절하면 좋거든요.

이 인터페이스를 구현하도록 하겠습니다.

인터페이스 구현 예)

<?
/**
* 사용자 쪽 데이터베이스를 다루기 위한 서비스 인터페이스를 구현한다.
*
* author Hyoseok Kim(toriworks@gmail.com)
* since 2008.11.17
*/

require_once("/class/service/user/UserServiceImpl.php");

class UserServiceImpl implements UserService {
    // 상수를 선언합니다. 필요에 따라서...

    // 생성자 선언
    public function __construct() {}

    // 외부에서 dao 객체를 주입해 줍니다. (이 부분은 나중에 설명하겠습니다.)
    private $userDao = "";

    public function setUserDao($userDao) {
        $this->userDao = $userDao;
    }

    // 다른 서비스 객체를 주입할 수 있습니다.(예를 들자면, 로그처리 서비스 객체)
    private $logService = "";

    public function setLogService($logService ) {
        $this->logService = $logService;

    }

    public function add($conn, $obj) {
        // 입력 결과 값
        $resultOfAdd = 0;
        $resultOfAdd = $this->userDao->add($conn, $obj);

        return $resultOfAdd;
    }

    // 인터페이스에 선언된 function들을 구현해야 합니다. 또한 기타 필요한 function을 만들어서
    // 사용하세요.
   
}
?>

재미있는 개념을 도입해 봤습니다.
외부에서 객체를 만들어서 service 객체에 주입하는 개념인데요. 물론 Java 쪽에 특히 Spring framework 에서는 일반적인 개념인 IoC(Inversion of Control) 에서 차용해 봤습니다.

장점은 객체를 외부에서 생성해서 주입하기 때문에, service 가 호출될때 적당한 객체가 주입되지 않으면 에러가 나게 되어 있습니다.
그리고, 주입되는 객체는 실제 외부에서 만들어서 주입하기 때문에 service 객체는 어떤 객체가 들어올지 몰라도 됩니다.

말로는 설명이 되는데, 글로는 설명이... 어렵네요. 10년차 PHP 프로그래머한테도 이해시키는데 쬐금 어려웠거든요. 외부에서 주입하는 개념이 없어서요.

암튼, 에러를 쉽게 찾아내고 레이어끼리 분리하기 위한 목적이라고 보심 되구요. 특히나, service 레이어를 분리하는데 장점을 보여주는데 다른 service 객체를 외부에서 만들어서 현재 객체에 주입함으로써 내 객체를 크게 변경하지 않고도 확장 하는 개념을 사용 할 수 있습니다. (구성을 통한 위임의 활용 - 너무 거창한가요???)

이게 다에요.



여기까지 잘 따라오셨습니다.

이제 마지막으로 사용자 쪽에서 이전 시간에 만들어 놓은 service 객체를 호출하는 페이지만 만들면 됩니다.

간단하게 끝내도록 하겠습니다.

사용 예)

<?
/**
* 사용자 신규 입력
*
* author Hyoseok Kim(toriworks@gmail.com)
* 2008.11.17
*/

include "/class/dao/UserDaoImpl.php";    // dao 객체 얻기
include "/class/service/user/UserServiceImpl.php";    // service 객체 얻기

// Dao 객체 생성
$userDao = new DaoImpl();

// 서비스 객체 생성
$userService = new UserServiceImpl();

// 객체 주입
$userService->setUserDao($userDao);

// 도메인 객체 생성
$user = new User();
$user->setUserId("tori");
....

// 서비스 사용
$userService->add($conn, $user);    // 이것만으로도 db에 저장됨
 
?>

이렇게 할 경우 장점 한 가지 더 생각 났습니다.
클래스 쪽만 담당하는 사람은 설계 이후에 클래스에 집중해서 개발을 담당할 수 있구요, view 쪽을 담당하는 사람은 클래스가 나오면 그냥 끌어다가 사용만 하면 됩니다.

즉, 역할 분담이 가능건데요. 역할 분담만 정확하다면, 그리고 교육만 제대로 되어있다면 충분히 개발 속도를 증가시킬 수 있다고 생각됩니다.

위 소스들은 실제로 제가 프로젝트에 적용한 사례이기도 합니다. 꼭 PHP 프레임워크가 아니더라도, 이렇게 개발해 두니 오류도 없고, 오류 찾기도 쉽고, 갑작스런 클라이언트의 서비스 추가에도 땀흘리지 않고 대처가 가능합니다.

이상입니다. ^^


 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크