PHP 네임스페이스

😢 이 페이지는 다음 주소로 변경될 예정입니다.

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\EdwardEdward로 호출해 사용할 수 있다.

이런 상황에서 반대로 생각해볼 수 있는 부분은 네임스페이스 내부에서 상위 또는 최상위에 있는 네임스페이스나 클래스는 어떻게 접근할 지에 대해서다.

PHP는 클래스명 앞에 백스래시()를 넣어 전역 클래스 또는 글로벌 네임스페이스를 사용하고 있음을 명시적으로 선언할 수 있다.

<?php
$edward = new \Edward();

만약 다른 네임스페이스에 속한 클래스인 Drink\CokeHaruair 네임스페이스 내에서 사용한다면 앞서 예제와 같이 작성할 수 있다.

<?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 네임스페이스에 있는 UserTwitterUser 별칭으로 불러오면서 충돌을 회피했다. 이와 같이 충돌을 피하고 의도와 필요에 따라 기능을 모아서 사용할 수 있다.

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;

한 네임스페이스에서 많은 클래스를 동시에 사용할 때 위 방법이 도움이 된다.

더 읽을 거리