PHP 5.3에서 새로운 기능으로 네임스페이스가 추가되었다. (= 이미 오래된 기능이다.) 많은 현대 언어는 이미 이 기능을 추가한지 오래지만 PHP는 조금 늦게 추가되었다. 최근에 개발되는 대다수의 PHP 라이브러리는 네임스페이스로 패키징해 composer
, League
등을 통해 제공되고 있어 현대 PHP를 사용하려고 한다면 필수적으로 알아야 하는 기능이다.
PHP에서는 같은 이름을 가진 두 클래스를 동시에 사용할 수 없다. 클래스는 항상 유일해야만 한다. 이 제한으로 인해 서드파티 라이브러리에서 User
라는 클래스명을 사용하고 있을 때는 User
라는 클래스명을 사용할 수 없었다. 이렇게 간단한 클래스명을 사용하지 못하는건 불편하다.
PHP 네임스페이스는 위와 같은 클래스명 중복 문제를 해결한다. 그 뿐 아니라 코드를 패키징하거나 벤더명을 지정해 소유권을 표시하는데도 사용할 수 있다.
전역 네임스페이스
여기 간단한 클래스가 있다.
<?php
class Edward
{
}
별로 특별한 부분이 없다. 다음과 같이 사용할 수 있다.
<?php
$edward = new Edward();
이 상황에서 이 클래스는 전역 네임스페이스를 가졌다고 볼 수 있다. 즉 이 클래스는 네임스페이스 없이 존재한다. 그냥 일반 클래스와 같다.
단순한 네임스페이스
이제 네임스페이스 밑으로 클래스를 만들어보자.
<?php
namespace Haruair;
class Edward
{
}
위에서 만든 클래스와 유사하지만 작은 차이가 있다. namespace
라는 지시문이 추가되었다. namespace Haruair;
는 여기서 작성한 모든 PHP 코드가 Haruair
네임스페이스와 관련이 되어 있음을 뜻하고 이 파일에서 생성한 클래스가 모두 Haruair
네임스페이스에 포함되어 있음을 뜻한다. 네임스페이스를 통해 클래스를 생성하면 다음과 같이 사용할 수 있다.
<?php
$edward = new Haurair\Edward();
위 코드와 같이 네임스페이스와 함께 클래스를 선언할 수 있다. 네임스페이스와 클래스 사이에는 백슬래시()로 구분이 된다. 위와 같은 방법으로 네임스페이스로 클래스를 다룰 수 있다.
이와 같은 방법으로 여러 단계의 위계를 활용하고 있는 경우를 많이 볼 수 있다.
This\Namespace\And\Class\Combination\Is\Silly\But\Works
의존성 원칙
PHP는 현재의 namespace에 따라 상대적으로 동작한다.
<?php
namespace Haruair;
$edward = new Edward();
Haruair
네임스페이스 내에서 개체를 생성했다. 동일한 네임스페이스에 속해 있는 상황이기 때문에 Haurair\Edward
를 Edward
로 호출해 사용할 수 있다.
이런 상황에서 반대로 생각해볼 수 있는 부분은 네임스페이스 내부에서 상위 또는 최상위에 있는 네임스페이스나 클래스는 어떻게 접근할 지에 대해서다.
PHP는 클래스명 앞에 백스래시()를 넣어 전역 클래스 또는 글로벌 네임스페이스를 사용하고 있음을 명시적으로 선언할 수 있다.
<?php
$edward = new \Edward();
만약 다른 네임스페이스에 속한 클래스인 Drink\Coke
를 Haruair
네임스페이스 내에서 사용한다면 앞서 예제와 같이 작성할 수 있다.
<?php
namespace Haruair;
$coke = new \Drink\Coke();
매번 전체 위계를 입력하는 것이 번거롭다면 use
를 활용할 수 있다.
<?php
namespace Haruair;
use Drink\Coke;
$coke = new Coke();
use
를 활용하면 다른 네임스페이스에 있는 하나의 클래스를 현재 네임스페이스 내에서 사용할 수 있게 해준다. 동일한 클래스명을 불러오게 되는 경우가 온다면 다음과 같이 활용할 수 있다.
<?php
namespace Haruair;
use Drink\Pepsi as BlueCoke;
$pepsi = new BlueCoke();
위와 같이 as
키워드를 쓰면 Drink\Pepsi
클래스에 별칭 BlueCoke
를 지정해 사용할 수 있다. 같은 이름의 클래스 여럿을 동시에 사용한다 해도 문제 없다.
<?php
namespace Facebook;
use Twitter\User as TwitterUser;
class User {}
$twitter_user = new TwitterUser();
$facebook_user = new User();
Twitter
네임스페이스에 있는 User
를 TwitterUser
별칭으로 불러오면서 충돌을 회피했다. 이와 같이 충돌을 피하고 의도와 필요에 따라 기능을 모아서 사용할 수 있다.
use
는 필요한 만큼 넣어서 사용할 수 있다.
<?php
namespace Haruair;
use Twitter\Follower;
use Facebook\WallPost;
use Cyworld\WallPost as CyPost;
구조
네임스페이스는 단순히 충돌을 피하기 위해서만 사용하는 것이 아니라 조직이나 소유권을 표기하기 위해 사용하기도 한다.
오픈소스 라이브러리를 만든다고 가정하자. 내가 만든 코드를 다른 사람이 사용한다면 분명 좋을 것이다. 다만 내 코드를 사용하는 사람들에게 불편함을 주지 않았으면 좋겠다. 클래스명이 충돌하게 되면 엄청나게 불편할 것이 확실하다. 그래서 다음과 같이 네임스페이스를 구분하기로 했다.
Haruair\Blog\Content\Post
Haruair\Blog\Content\Page
Haruair\Blog\Tag
여기서 내 아이디를 사용해서 이 코드가 누가 만들었는지 표시하는 것과 동시에 내 라이브러리 안에 만들어 코드를 사용하고자 하는 사람의 코드와 충돌하지 않도록 돕는다. 내 기초 네임스페이스에 여러개의 서브 네임스페이스를 만들어 내부 구조를 잡았다.
Composer를 사용하면 PSR-0, PSR-4를 통해 정해진 규칙에 따라 네임스페이스를 통해 클래스 정의를 자동으로 불러오는 등의 작업을 할 수 있다. 위 두 문서에서 이 유용한 방식을 확인해보는 것을 강력하게 추천한다.
제한
PHP가 제공하는 네임스페이스에는 한계가 있다. 다른 언어들에서의 구현과는 거의 유사하지만 약간 다른 점이 존재한다. Java의 경우, wildcard(*)를 이용해 해당 네임스페이스에 속해 있는 모든 클래스를 한번에 불러올 수 있다. 또한 Java에서의 import
는 앞서 살펴 본 use
와 같은 역할을 해서 패키지나 네임스페이스 내에 있는 클래스를 쉽게 이용할 수 있게 돕는다. 다음은 Java의 예시다.
import haruair.blog.*;
위와 같은 코드로 haruair.blog
의 모든 패키지를 로드할 수 있다.
PHP는 이와 같은 방법으로 불러올 수 없다. 대신 상위의 네임스페이스를 use
로 불러와 유사하게 사용할 수 있다.
<?php
namespace weirdmeetup;
use Haruair\Blog as Cms;
$post = new Cms\Content\Post;
$page = new Cms\Content\Page;
$tag = new Cms\Tag;
한 네임스페이스에서 많은 클래스를 동시에 사용할 때 위 방법이 도움이 된다.