이 글은 제가 가장 좋아하는 PHP 프레임워크인 Yii 에 관한 여섯번째 글입니다. 첫번째 글에서는 어떻게 Yii 를 다운로드하고 테스트하는 지 보여드렸고, 두 번째 글에서는 기본적인 웹 어플리케이션을 생성하는 방법을 설명했습니다. 세 번째 글에서는 설정을 변경하는 방법을 보여드렸고 네 번째 글에서는 Yii 에서 사용할 데이터베이스를 디자인해 보았습니다. 다섯번째 글에서 어떻게 Gii 툴을 사용해서 모델, 뷰, 컨트롤러를 생성하는지 보여드렸죠. 이번 글에서는 Gii 툴로 생성된 모델을 입맛에 맞게 수정하는 방법을 보여 드릴것입니다. 이 과정을 통해 Yii 모델 전반에 관한 정보를 얻을 수 있을 것입니다. 코드를 보여드리기 위해서 지난 게시물들에서 생성한 직원 관리 프로그램을 사용할 것입니다. 이에 관한 정보를 다시 얻고 싶으시면 지난 게시물들을 참조하세요.
모델은 어플리케이션에서 사용되는 데이터를 대표합니다. 일반적으로 데이터들은 데이터베이스에서 가져오지만 그 외의 조작을 통해서도 데이터를 받을 수 있습니다(Yii 기본 어플리케이션에 있는 ContactForm 모델처럼 저장되지 않고 메일로만 발송되는 데이터 같은 예가 있겠네요). Yii 에서 데이터베이스 테이블에 기반한 모델은 기본적으로 액티브 레코드(Active Record)를 확장(extends, 다른 언어에서의 상속과 비슷한 개념입니다)한 클래스로 정의됩니다. 액티브 레코드는 루비 온 레일즈에서도 사용되는 보편적이고 인기있는 방식입니다. 액티브 레코드를 사용하면 데이터에 대한 실제 처리 코드를 모델 내에서 정의하지 않고 부모 클래스인 액티브 레코드에서 처리하게 됩니다. 그러니까 모델의 코드를 열었을 때 레코드를 생성하거나 업데이트하거나 뭐 요래요래 데이터를 실제로 조작하는 메소드가 없다고 해서 놀랄 필요는 없습니다. 전부 상속된 메소드들이니까요. 그런 데이터 조작과 관련된 기능들이 이미 정의되어 있기 때문에, 우리가 해야 할 일은 그저 모델을 필요에 따라 확장하고 최적화 하는 것 뿐입니다.
모델 안에는 공용으로 사용할 수 있는 Yii 전용 메소드들이 있습니다. 이 메소드들중 일부는 Gii 툴을 이용해 모델을 생성할 때 만들어집니다. 언제든 다른 메소드를 추가할 수도 있구요. 저는 여기서 대부분의 모델들에 공통적으로 존재하는 이 Yii 전용 메소드들을 중점적으로 다룰 생각입니다. (이후 글에서 모델에 추가할 수 있는 사용자 지정 메소드를 작성하는 예제를 보여드릴겁니다)
rules() 메소드는 엄청 중요한 메소드들 중 하나로, 모델의 데이터들이 준수해야 하는 규칙들의 목록을 가지고 있습니다. 어플리케이션의 보안과 신뢰도에 관한 많은 부분이 이 메소드에 기대고 있습니다. 실제로 이 메소드가 프레임 워크 내에서 대표적으로 사용되는 곳은 데이터 검증 부분입니다. 새로운 레코드를 작성하거나 기존에 있는 레코드를 갱신하려고 할 때 데이터 검증 루틴을 작성하고 복제하고 테스트할 필요가 없습니다. Yii 가 해주기 때문이죠. 모델 내의 주석이 보여주듯이, 그저 사용자가 제공하는 데이터에 사용되는 필드에 대한 규칙만 확립하면 됩니다. 예컨데 MySQL 이 생성하는 기본 키인 Employee id 필드에 대한 규칙을 설정할 필요는 없습니다.
다른 Yii 메소드들과 마찬가지로 rules() 메소드는 배열 형태의 데이터를 반환합니다.
public function rules()
{
return array();
}
|
Yii 문서에 실제 규칙에 관한 내용들이 모두 나와 있습니다.(여기와 여기를 보세요). 하지만 저는 주로 사용되는 것만 조명하겠습니다. 여기서 확인할 수 있듯이 모든 규칙은 배열 형태로 반환할 수 있도록 작성됩니다.
먼저, 가장 중요한 제한은 필드가 필수적인 것인지를 표시하는 것입니다. 단순히 필드 이름들을 쉼표로 구분한 문자열을 첫번째 반환 값에 넣고 두 번째 반환 값으로 'required' 라는 단어를 넣으면 됩니다.
array('name, email, subject, body', 'required'),
|
데이터를 숫자만으로 제한한다거나, 좀 더 상세하게는 정수로만 제한하는 것도 가능합니다. 이런 제한을 하기 위한 문법은 위와 약간 다릅니다. 여기 ext 필드를 정수로 제한하는 예가 있습니다.
array('ext', 'numerical', 'integerOnly'=>true),
|
문자열의 경우에는 최대 길이를 제한 할 수 있습니다.
array('name','length','max'=>40),
|
혹은 최소 길이를 제한하거나
array('name','length','min'=>6),
|
둘 다 제한할 수도 있습니다.
array('name','length','min'=>6, 'max'=>40),
|
또 다른 유용한 검증 루틴으로 문자열이 이메일 주소인지 확인하는 것이 있습니다. userEmail 필드에 관해 이러한 검증을 설정해보겠습니다.
array('userEmail', 'email'),
|
문자열이 URL 주소여야 하는 경우에는 이렇게 할 수도 있습니다:
유용한 다른 규칙중에 비교 규칙이 있습니다. 예를 들어 회원 가입을 하기 위해 패스워드를 입력하고 제대로 입력했는지 확인하기 위해서 패스워드 확인 란을 입력하는 경우 등에 사용하면 좋겠죠.
array('password1', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
|
"safe" 라는 규칙도 있습니다. 이 규칙은 다른 검증 루틴을 통하지 않은 데이터에 접근해야 할 때 사용됩니다. 예를 들어 이메일 필드는 이미 이메일 주소를 검증하는 루틴을 통해 검증했기 때문에 "safe" 규칙을 사용할 필요가 없습니다. 하지만 Employee 모델에는 leaveDate 라는 필드가 있는데, 이 필드에 관해서는 설정할 만한 규칙이 없습니다(이 것은 부분적으로는 날짜에 적용할만한 규칙이 없기 때문이기도 하고, 또 한 편으로는 이 필드가 NULL 값일 수도 있기 때문이기도 합니다). 이 값에 접근을 가능하게 하려면 이 값을 "safe" 하다고 표시해줘야 합니다.
array('leaveDate', 'safe'),
|
"safe"로 표시해야 할 필드가 여러 개 있다면, 그냥 쉼표로 분리해서 다 적어주면 됩니다.
검색에 사용할 수 있는 필드를 설정하는 규칙도 있습니다. 기본값으로 모든 규칙들이 검색에 사용할 수 있도록 설정되어 있지만 해당 필드 목록에서 원하는 필드를 제거할 수도 있습니다.
array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
|
이러한 규칙들을 모두 적용한 Employee 모델의 rules() 메소드는 아래와 같은 모양이 될 것입니다.
public function rules()
{
return array(
array('departmentId, firstName, lastName, email, hireDate', 'required'),
array('departmentId, ext', 'numerical', 'integerOnly'=>true),
array('firstName', 'length', 'max'=>20),
array('lastName', 'length', 'max'=>40),
array('email', 'length', 'max'=>60),
array('email', 'email'),
array('leaveDate', 'safe'),
array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
);
}
|
계속해서, 또 다른 중요한 모델 메소드는 바로 relations() 입니다. 이 메소드는 모델들 간의 관계를 나타냅니다. 데이터 베이스를 제대로 설계했다면 이 메소드는 이미 적절하게 작성되어 있을 것입니다. Gii 툴 덕분이죠. 아래에 Employee 모델의 relations() 메소드는 아래와 같을 겁니다.
public function relations()
{
return array('department' => array(self::BELONGS_TO, 'Department', 'departmentId') );
}
|
관계 설정은 이름을 반홥합니다. 여기서는 department 네요. 이 관계 설정은 Employee 모델의 departmenntId 컬럼이 Department 모델에 속해있다는 것을 나타냅니다. 이러한 관계 설정이 어떻게 작동할까요? 특정한 고객에 대한 정보를 불러들일 때에는 해당 정보의 관계 설정도 함께 불러들이게 됩니다. 그렇게 함으로써 department 에 관한 참조는 Department 모델의 레코드에 대한 참조와 같이 취급됩니다. 따라서 Employee 모델을 나타내는 $model 객체가 있을 때에, $model->department->name 을 참조하게 되면 해당 직원이 속해있는 분류의 이름을 가져오게 됩니다.
Department 모델 내에서는 이 관계가 아래처럼 설정되어 있습니다.
public function relations()
{
return array('employees' => array(self::HAS_MANY, 'Employee', 'departmentId') );
}
|
따라서 Department 모델을 나타내는 $model 객체가 있을 경우 $model->employees 는 해당 분류에 속해있는 모든 Employee 객체의 배열을 나타내게 됩니다.
각 모델들 간의 관계 설정은 복합적인 MVC 사이트의 핵심적인 요소입니다. 제대로 정의된 관계 설정을 통해서 컨텐츠들을 정확히 참조할 수 있게 됩니다. 이후에 이어질 두 개의 글에서 이에 관해 조금 더 배워보도록 하겠습니다.
조금 사소하지만 멋진 것들을 살펴볼까요? attributeLabels() 라는 메소드가 있습니다. 이 메소드는 폼, 에러메시지, 그 외 필요한 곳에서 사용될 각 필드의 이름을 담고 있는 배열을 반환합니다. Yii 프레임워크는 이 이름들을 멋지게 자동으로 생성해줍니다. firstName 은 First Name 으로, departmentId 는 Department 로 변환하는 식으로 말이죠. 그래도 이 이름들을 직접 설정하고 싶어질 수 있습니다. Employee 모델의 경우 저는 아래와 같이 설정했습니다.
public function attributeLabels()
{
return array(
'id' => 'Employee ID',
'departmentId' => 'Department',
'firstName' => 'First Name',
'lastName' => 'Last Name',
'email' => 'Email',
'ext' => 'Ext',
'hireDate' => 'Hire Date',
'leaveDate' => 'Leave Date',
);
}
|
이제 모델에 관해 어느 정도 기초적인 수준의 커스터마이징을 해 보았습니다. 아까도 말했듯이 나중에 자신만의 메소드를 모델에 추가할 수 있습니다. 그리고 이 글에서 살펴본 것 외에도 자주 사용하는 Yii 메소드들이 몇 개 있습니다. 모델이 저장되기 전에 자동으로 호출하는 beforeSave() 메소드라던지, 필드 값 검증을 하기 전에 호출되는 beforeValidate() 메소드 같은 것들이죠. 이 메소드들은 모델에서 값 검증을 하기 전에 보이지 않는 처리를 하거나 값들을 변경할 필요가 있을 경우 유용하게 사용됩니다. 또 search() 메소드가 있는데 이 메소드는 나중에 다른 글에서 별도로 다루겠습니다. 어쨌든 모델 내에서 가장 중요하게 사용되는 메소드는 rules() 와 relations() 메소드라는 걸 기억합시다!
This is a translation of a work originally written in English by Larry Ullman (www.LarryUllman.com). It is translated and republished with his permission.
이 글은 영어로 작성된 Larry Ullman 의 원본 글을 번역한 것입니다 ( http://www.LarryUllman.com ). 이 글은 저자의 허락을 받아 번역되었습니다.