 |
|
난이도 : 초급 John Mertic, Software Engineer, SugarCRM
원문 게재일 : 2009 년 1 월 20 일 번역 게재일 : 2009 년 4 월 21 일 PHP V5.3이 곧 릴리스될 예정입니다. 이 "PHP
V5.3의 새로운 기능" 시리즈에서는 PHP V5.3의 흥미롭고 새로운 기능에 대해 설명합니다. Part 1에서는
PHP 5.3의 오브젝트 지향 프로그래밍 및 오브젝트 처리와 관련된 변경 사항을 살펴 보았으며 Part 2에서는
람다 함수와 클로저에 대해 살펴 보았습니다. 이제 Part 3에서는 PHP V5.3에서 가장 큰 기대를
받으면서도 가장 큰 논란의 대상이기도 한 네임스페이스에 대해 설명합니다. 네임스페이스 개념은
같은 이름으로 여러 번 정의되는 여러 함수, 클래스 및 상수와 관련된 문제를 해결하는 데 도움이
됩니다.
네임스페이스는 C++ 및 Java™ 프로그래밍 언어를 비롯한 여러 언어에서 사용되고
있다. 네임스페이스는 대량의 코드베이스를 구성하는 데 도움이 되지만 함수 또는 클래스
이름이 애플리케이션과 겹치면서 발생하는 문제점도 가지고 있다. 네임스페이스를 사용하면
코드에서 제공하는 함수 또는 유틸리티를 쉽게 식별할 수 있을 뿐 아니라 그 기원까지도
지정할 수 있다. 예를 들어, C#의 System 네임스페이스에는 .NET 프레임워크에서 제공하는
모든 함수 및 클래스가 있다.
공식적인 네임스페이스가 없는 다른 언어(예: PHP V5.2 이전 버전)에서는 클래스 또는 함수 이름
내에서 특정 이름 지정 규칙을 사용하여 네임스페이스를 에뮬레이션한다. Zend Framework에서는
네임스페이스를 에뮬레이션하기 위해 각 클래스 이름을 Zend로 시작하고
각 하위 네임페이스를 밑줄로 구분한다. 예를 들어, 클래스 정의 Zend_Db_Table은
Zend Framework의 일부이고 데이터베이스 기능을 가지고 있는 클래스이다. 이 방법을 사용할 경우에는
결과 코드가 복잡해지며 특히, 클래스 또는 함수가 여러 단계의 계층으로 구성된 경우 코드가 매우
복잡해진다. (Zend_Cache_Backend_Apc는 Zend Framework에서 사용되는
클래스 정의를 보여 주는 예이다.) 또 하나의 문제는 모든 코드가 이 양식을 따라야 한다는 것이다.
이 이름 지정 규칙을 따르지 않는 애플리케이션에서 써드파티 코드를 통합할 경우 작업이 어려워질
수 있다.
네임스페이스가 PHP에 채택되기까지 많은 우여곡절이 있었다. 처음에는 PHP V5에 채택될 예정이었지만
구현 작업이 순탄치 않아서 개발 단계에서 취소되었다. 최종적으로 PHP V6부터 채택하기로 했었지만 유니코드와
관련되지 않은 모든 개선 사항을 새로운 PHP V5.x 버전에 적용한다는 결정이 2007년에 내려진 후 PHP V5.3에
채택되었다. 네임스페이스의 동작 중 대부분은 원래 설계를 그대로 따랐지만 사용할 연산자 문제의 경우 커뮤니티의
모든 구성원들이 극명한 견해차를 보여주었던 중요한 문제였다. 2008년 10월에 백슬래시를 연산자로 사용하기로 하면서
언어 설계 및 사용성 측면에서 제기된 다른 여러 가지 연산자를 사용하는 방안에 대한
모든 문제가 해결되었다.
PHP의 네임스페이스
PHP는 다른 언어, 특히 C++에서 네임스페이스 구문 및 설계의 많은 부분을 차용했다. 하지만
PHP에서는 네임스페이스가 고유한 방법으로 처리되는 부분도 있기 때문에 다른 언어와 완전히
동일하게 작동할 것으로 예상한 사용자의 경우 혼란을 겪을 수도 있다. 이 섹션에서는 PHP에서
네임스페이스가 작동하는 방법을 살펴본다.
네임스페이스 정의하기
새 네임스페이스를 정의하는 방법은 상당히 간단하다. Listing 1의 다음 행을 첫 번째 명령
또는 출력으로 파일에 추가하면 된다.
Listing 1. 네임스페이스 정의하기
<?php
namespace Foo;
class Example {}
?>
|
여기서, 중요한 점은 namespace 선언이 파일의 첫 번째 명령
또는 출력이어야 한다는 것이다. 선언 부분 앞에 다른 내용이 있으면 치명적 오류가 발생한다. Listing
2에서는 네임스페이스를 잘못 선언한 예제를 보여 준다.
Listing 2. 잘못된 네임스페이스 정의 방법
/* File1.php */
<?php
echo "hello world!";
namespace Bad;
class NotGood {}
?>
/* File2.php */
<?php
namespace Bad;
class NotGood {}
?>
|
Listing 2의 첫 번째 부분에는 네임스페이스 정의 전에 콘솔로 출력하는 구문이 있기
때문에 치명적 오류가 발생한다. Listing 2의 두 번째 부분을 보면, <?php
시작 태그 앞에 공백이 있으며, 이 또한 치명적 오류의 원인이다. 이러한 실수는 PHP에서
네임스페이스 작업을 수행할 때 자주 발생하기 때문에 코드를 작성할 때 이러한 실수가
없는지 잘 살펴야 한다.
하지만 위 두 예제 모두 네임스페이스 정의와 네임스페이스 선언의 일부가 될 코드를 별도의
파일로 구분하여 다시 작성한 후 이 파일을 원래 파일에 포함시킬 수 있다. Listing 3에서는
이러한 방법을 구현한 예제를 보여 준다.
Listing 3. 올바르게 수정된 네임스페이스 정의 방법
/* Good.php */
<?php
namespace Good;
class IsGood() {}
?>
/* File1.php */
<?php
echo "hello world!";
include './good.php';
?>
/* File2.php */
<?php
include './good.php';
?>
|
지금까지는 파일 내에서 코드에 대한 네임스페이스를 정의하는 방법을 살펴보았다. 이제
이 네임스페이스가 적용된 코드를 애플리케이션에서 활용하는 방법을 살펴보자.
네임스페이스가 적용된 코드 사용하기
네임스페이스를 정의하고 코드를 네임스페이스에 입력한 후에는 애플리케이션 내에서
코드를 매우 쉽게 사용할 수 있다. 네임스페이스가 적용된 함수, 클래스 또는 상수는 여러
가지 방법으로 호출할 수 있다. 한 가지 방법은 네임스페이스를 호출에 대한 접두어로 명시적으로
참조하는 것이다. 또는 네임스페이스의 별칭을 정의한 후 이 별칭을 호출의 접두어로 사용할
수도 있다. 이렇게 하면 네임스페이스 접두어의 길이가 짧아진다. 마지막으로 코드 내에서 네임스페이스를
직접 사용할 수 있다. 이 경우 해당 네임스페이스는 기본 네임스페이스가 되며 모든 호출은 기본적으로 이
네임스페이스를 참조하게 된다. Listing 4에서는 여러 가지 호출 방법을 보여 준다.
Listing 4. 네임스페이스 내에서 함수 호출하기
/* Foo.php */
<?php
namespace Foo;
function bar()
{
echo "calling bar....";
}
?>
/* File1.php */
<?php
include './Foo.php';
Foo/bar(); // outputs "calling bar....";
?>
/* File2.php */
<?php
include './Foo.php';
use Foo as ns;
ns/bar(); // outputs "calling bar....";
?>
/* File3.php */
<?php
include './Foo.php';
use Foo;
bar(); // outputs "calling bar....";
?>
|
Listing 4에서는 네임스페이스 Foo에 있는 bar() 함수를
호출하는 여러 가지 방법을 보여 준다. File1.php에서는 호출 앞에 네임스페이스 이름을
사용하여 명시적으로 호출하는 방법을 볼 수 있다. File2.php에서는 네임스페이스 이름에
대한 별칭을 사용하기 때문에 네임스페이스 이름을 별칭으로 대체한다. 마지막으로 File3.php에서는
네임스페이스를 그대로 사용하기 때문에 접두어 없이 bar()를
호출할 수 있다.
namespace 호출을 파일에 추가하여 두 개 이상의 네임스페이스를
정의할 수도 있다. Listing 5에서는 이러한 방법을 구현한 예제를 보여 준다.
Listing 5. 여러 개의 네임스페이스가 있는 파일
<?php
namespace Foo;
class Test {}
namespace Bar;
class Test {}
$a = new Foo\Test;
$b = new Bar\Test;
var_dump($a, $b);
Output:
object(Foo\Test)#1 (0) {
}
object(Bar\Test)#2 (0) {
}
|
지금까지 네임스페이스 내에서 호출하는 방법에 기본적인 내용을 살펴보았으며 이제
좀 더 복잡한 네임스페이스 상황과 이러한 호출이 작동하는 방법을 살펴보자.
네임스페이스 확인
네임스페이스를 이해하려면 범위 확인의 작동 방법을 잘 알아야 한다. Listing 4와 같이 간단한
경우에는 이해하기가 쉽지만 네임스페이스를 서로 중첩하거나 한 네임스페이스에서 전역 범위에 대한
호출을 하려고 하면 이해하기가 쉽지 않다. PHP V5.3에는 이러한 문제를 합리적인 방법을 통해 자동으로
해결하기 위한 규칙이 있다.
먼저 hello() 함수가 정의되어 있는 include 파일 세 개를 만들어보자.
Listing 6. 서로 다른 네임스페이스에 정의된 hello() 함수
/* global.php */
<?php
function hello()
{
echo 'hello from the global scope!';
}
?>
/* Foo.php */
<?php
namespace Foo;
function hello()
{
echo 'hello from the Foo namespace!';
}
?>
/* Foo_Bar.php */
<?php
namespace Foo/Bar;
function hello()
{
echo 'hello from the Foo/Bar namespace!';
}
?>
|
Listing 6에서는 전역 범위, Foo 네임스페이스 및 Foo/Bar 네임스페이스
등의 각기 다른 범위로 hello() 함수를 세 번 정의한다. hello() 함수 호출의
범위에 따라 호출되는 hello() 함수가 달라진다. 아래 예제에서는 이러한 호출이 작동하는 방법을 보여
준다. 이 예제에서는 Foo 네임스페이스를 사용하여 다른 네임스페이스에 있는 hello()
함수를 호출하는 방법을 보여 준다.
Listing 7. Foo 네임스페이스에서 모든 hello() 함수 호출하기
<?php
include './global.php';
include './Foo.php';
include './Foo_Bar.php';
use Foo;
hello(); // outputs 'hello from the Foo namespace!'
Bar\hello(); // outputs 'hello from the Foo/Bar namespace!'
\hello(); // outputs 'hello from the global scope!'
?>
|
현재 네임스페이스 내에서 하위 네임스페이스를 참조할 경우 네임스페이스 접두어를 줄일 수 있다는
것을 알 수 있다. (Foo/Bar/hello() 호출을 Bar/hello()로
단축할 수 있다.) 호출 앞에 네임스페이스 연산자를 붙이는 것만으로도 전역 범위의 메소드를 호출할
수 있다는 것을 알 수 있다.
지금까지 네임스페이스가 작동하는 메커니즘을 살펴보았으며, 이제 코드 내에서 네임스페이스를 사용하는 방법을 살펴보자.
PHP의 네임스페이스 사용 사례
네임스페이스의 주 목적은 전역 범위에 있는 정의 수를 줄여서 코드를 효과적으로 구성하는
것이다. 이 섹션에서는 네임스페이스를 사용하여 최소한의 노력으로 이러한 목표를 달성하는
일부 예제를 살펴본다.
써드파티 코드에 네임스페이스 적용하기
많은 PHP 애플리케이션에서는 다양한 소스의 코드를 사용한다. 이러한 코드는 PEAR 저장소에 있는
코드처럼 일관되게 설계된 코드이거나, CakePHP 또는 Zend Framework와 같은 다양한 프레임워크의
코드이거나, 인터넷에 있는 수많은 코드 중 하나일 수도 있다. 이 코드를 통합할 때 가장 큰 문제 중
하나는 기존 코드와 잘 합쳐지지 않을 수 있다는 것이다. 즉, 함수 또는 클래스 이름이 애플리케이션에서
사용하고 있는 기존 이름과 충돌할 수 있다.
이러한 문제점을 보여 주는 예로 PEAR Date 패키지가 있다. 이 패키지에서는 Date라는
클래스 이름을 사용한다. 그러나 이 이름은 상당히 일반적인 클래스 이름이기 때문에 일반적인 코드에서도 자주 볼 수
있다. 따라서 이 문제를 효과적으로 해결하는 방법은 패키지에 있는 Date.php 파일 상단에 간단한 네임스페이스 명령을
추가하는 것이다. 이렇게 하면 PEAR Date 클래스와 고유 Date 클래스를 구별해서 사용할 수 있다.
Listing 8. 네임스페이스에 정의된 PEAR Date 클래스 사용하기
<?php
require_once('PEAR/Date.php');
use PEAR\Date; // the name of the namespace we've specified in PEAR/Date.php
// since the current namespace is PEAR\Date, we don't need to prefix the namespace name
$now = new Date();
echo $now->getDate(); // outputs the ISO formatted date
// this example shows the full namespace specified as part of the class name
$now = new PEAR\Date\Date();
echo $now->getDate(); // outputs the ISO formatted date
?>
|
PEAR/Date.php 파일에서 PEAR/Date 네임스페이스에 있는
PEAR Date 클래스를 정의했으므로 이제 해당 코드를 파일을 포함시킨 후 새 네임스페이스를
사용하거나 네임스페이스 이름을 클래스 또는 함수 이름의 접두어로 사용하기만 하면 된다. 이
방법을 사용하면 애플리케이션에 써드파티 코드를 안전하게 포함시킬 수 있다.
이름 충돌은 써드파티 코드에서만 발생하는 문제가 아니다. 함께 사용할 계획이
없었던 부분이 포함된 대용량 코드베이스에서도 발생할 수 있다. 다음 섹션에서는
네임스페이스를 통해 이 상황을 단순화하는 방법을 설명한다.
유틸리티 함수 이름 충돌 피하기
모든 PHP 애플리케이션에는 수많은 유틸리티 메소드가 있다. 이들은 애플리케이션 전체에 기능을
제공하기는 하지만, 실제로는 애플리케이션에 있는 오브젝트에 속해 있지 않으며 애플리케이션의
어느 부분에도 배치되어 있지 않다. 하지만 애플리케이션이 확장되면서 유지 보수 부담이 늘어날 수 있다.
애플리케이션을 실행하는 코드를 테스트하는 코드를 작성하게 되는 유닛 테스트에서 이러한 문제가
발생할 수 있다. 대부분의 유닛 테스트 세트는 전체 테스트 세트에 있는 모든 테스트를 실행하도록
설계된다. 예를 들어, 함께 포함되지 않을 두 개의 유틸리티 파일이 있다고 가정하자. 그러나 전체
애플리케이션을 한 번에 테스트하기 때문에 이 두 파일은 테스트 세트에 함께 포함된다. 애플리케이션을
이러한 방식으로 설계하게 되면 레거시 코드베이스가 많아지기 때문에 장기적인 유지 보수 관점에서
좋은 방법이 아니다.
Listing 9에서는 이 문제점을 피하는 방법을 보여 준다. 오른손잡이 및 왼손잡이 사용자를
위한 유틸리티 기능의 콜렉션인 utils_left.php와 utils_right.php라는 두 개의 파일이 있다. 파일별로
고유 네임스페이스가 정의되어 있다.
Listing 9. utils_left.php 및 utils_right.php
/* utils_left.php */
<?php
namespace Utils\Left;
function whichHand()
{
echo "I'm using my left hand!";
}
?>
/* utils_right.php */
<?php
namespace Utils\Right;
function whichHand()
{
echo "I'm using my right hand!";
}
?>
|
이제 사용자가 오른손잡이인지, 왼손잡이인지를 결과값으로 출력하는 whichHand() 함수를
정의한다. Listing 10에서는 손쉽게 두 파일을 안전하게 포함시킨 후 호출하려는 네임스페이스
간에 전환하는 방법을 보여 준다.
Listing 10. utils_left.php와 utils_right.php를 함께 사용하는 예제
<?php
include('./utils_left.php');
include('./utils_right.php');
Utils\Left\whichHand(); // outputs "I'm using my left hand!"
Utils\Right\whichHand(); // outputs "I'm using my right hand!"
use Utils\Left;
whichHand(); // outputs "I'm using my left hand!"
use Utils\Right;
whichHand(); // outputs "I'm using my right hand!"
|
이제 두 파일을 안전하게 함께 포함시킬 수 있으며 사용할 네임스페이스를 지정하여
함수 호출을 처리할 수도 있다. 이 기능을 지원하기 위해 리팩토링할 때 파일 상단에 use
명령문을 추가하여 사용할 네임스페이스를 지정하기만 하면 되기 때문에 기존 코드에 미치는
영향도 최소화할 수 있다.
이는 사용자가 정의한 PHP 코드 이외의 영역에도 적용할 수 있다. 다음 섹션에서는
네임스페이스의 내부 함수를 재정의하는 방법에 대해 설명한다.
내부 함수 이름 재정의하기
PHP의 내부 함수는 대부분 뛰어난 기능을 제공하지만 가끔씩은 사용자의 예상과 조금 다른 결과를
제공하기도 한다. 이 경우 함수의 작동 방식에 대한 논의가 필요할 수도 있지만 범위로 인해 발생하는
혼동을 피하기 위해 다른 이름을 사용하여 함수를 재정의할 수도 있다.
파일 시스템 함수의 경우 이러한 재정의 작업이 필요할 수 있다. file_put_contents()를
통해 생성된 모든 파일에 특정 권한을 설정하려는 경우 예를 들어, 이 함수를 통해 생성된 파일에 읽기 전용 권한을
설정하려는 경우 다음과 같이 새 네임스페이스에서 함수를 재정의할 수 있다.
Listing 11. 네임스페이스에서 file_put_contents() 정의하기
<?php
namespace Foo;
function file_put_contents( $filename, $data, $flags = 0, $context = null )
{
$return = \file_put_contents( $filename, $data, $flags, $context );
chmod($filename, 0444);
return $return;
}
?>
|
이 예제에서는 함수 내에서 내부 file_put_contents() 함수를 호출한 후
함수 이름에 백슬래시를 접두어로 붙여서 함수를 전역 범위에서 처리하도록 지정한다. 이는 곧 내부 함수가
호출된다는 것을 뜻한다. 내부 함수를 호출한 후 chmod()를 사용하여 파일에
적절한 권한을 설정한다.
지금까지 네임스페이스를 사용하여 코드를 개선하는 방법을 보여 주는 일부 예제를
살펴보았다. 이러한 예제에서는 함수 또는 클래스의 이름에 접두어를 붙여서 고유한
이름으로 만드는 등의 복잡한 방법을 사용하지 않았다. 네임스페이스를 사용하면 이름
충돌에 대한 우려 없이 대용량 애플리케이션에 써드파티 코드를 안전하게 포함시킬 수
있다는 것도 알게 되었다.
요약
PHP V5.3에 추가된 네임스페이스 기능은 애플리케이션 내에서 코드를 효율적으로 관리하는
데 많은 도움을 주는 유용한 기능이다. 이름 지정 표준을 사용하여 네임스페이스를 처리하지
않아도 되기 때문에 좀 더 효율적인 코드를 작성할 수 있다. 오랜 시간이 걸려서 도입된 네임스페이스는
이름 충돌로 인한 문제점을 안고 있는 대용량 PHP 애플리케이션에 큰 도움이 될 수 있는 기능이다.
참고자료 교육
제품 및 기술 얻기
-
DVD로 제공되거나 다운로드할 수 있는 IBM 시험판
소프트웨어를 사용하여 후속 오픈 소스 개발 프로젝트를 구현해 보자.
-
IBM 평가판 제품을
다운로드하여 DB2®, Lotus®, Rational®, Tivoli® 및 WebSphere®의 애플리케이션 개발 도구 및
미들웨어 제품을 사용하자.
토론
필자소개  | |  | John Mertic은 Kent State University의 컴퓨터 과학과를 졸업했으며,
SugarCRM에서 소프트웨어 엔지니어로 근무하고 있다. 여러 오픈 소스 프로젝트에 참여했으며, 특히 PHP 프로젝트에 주력하고 있다. 또한 PHP
Windows Installer를 개발 및 유지보수를 수행한다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|