서비스(Service)

소개

서비스(Service)는 무엇인가요?

CodeIgniter 4의 Services는 새로운 클래스 인스턴스를 생성하고 공유하는 기능을 제공합니다. Config\Services 클래스로 구현됩니다.

CodeIgniter 내의 모든 핵심(core) 클래스는 “서비스(Service)”로 제공됩니다. 이는 로드할 클래스 이름을 하드 코딩하는 대신 호출할 클래스가 매우 단순한 구성 파일 내에 정의된다는 것을 의미합니다. 이 파일은 팩토리(factory) 유형으로 작동하여 필요한 클래스의 새 인스턴스를 생성합니다.

왜 서비스를 사용하나요?

간단한 예를 보면 상황이 더 명확해질 것이므로 Timer 클래스의 인스턴스를 가져와야 한다고 상상해 보십시오. 가장 간단한 방법은 단순히 해당 클래스의 새 인스턴스를 만드는 것입니다.

<?php

$timer = new \CodeIgniter\Debug\Timer();

다른 타이머 클래스를 사용하기로 결정할 때까지 이는 매우 잘 작동합니다. 이 타이머 클래스는 기본 타이머가 제공하지 않는 고급 보고(Report) 기능이 있을 수 있으며, 이 경우 어플리케이션의 로그를 보관하기 위한 시간이 많이 걸려 오류가 발생할 수 있습니다. 위와 같은 방식으로 클래스의 인스턴스를 생성하면 이를 수정하기 위해 어플리케이션에서 타이머 클래스를 사용한 모든 위치를 찾아야 합니다. 이런 경우 서비스방식이 유용해집니다.

우리가 인스턴스를 직접 만드는 대신 중앙 클래스가 클래스의 인스턴스를 만들도록 합니다. 이 클래스는 매우 간단하며, 서비스로 사용하려는 각 클래스에 대한 메소드만 포함합니다. 이 메소드는 일반적으로 해당 클래스의 공유 인스턴스를 반환하여 해당 클래스의 종속성을 전달합니다.

그런 다음 타이머 생성 코드를 이 새로운 클래스를 호출하는 코드로 바꿉니다.

<?php

$timer = \Config\Services::timer();

사용된 구현을 변경해야 할 경우 서비스 구성 파일을 수정할 수 있으며, 변경 작업 없이 어플리케이션 전체에서 자동으로 반영됩니다. 이제 새로운 기능을 활용하기만 하면됩니다.

이는 매우 간단하고 오류에 강합니다.

Note

서비스는 컨트롤러에서 생성하는 것이 좋습니다. 모델 및 라이브러리와 같은 다른 파일에는 종속성이 생성자 또는 setter 메소드를 통해 전달되어야 합니다.

서비스를 받는 방법

많은 CodeIgniter 클래스가 서비스로 제공되므로 다음과 같이 얻을 수 있습니다.

<?php

$typography = \Config\Services::typography();

$typography는 Typography 클래스의 인스턴스이며 \Config\Services::typography()를 다시 호출하면 동일한 인스턴스를 얻게 됩니다.

서비스는 일반적으로 클래스의 공유 인스턴스를 반환합니다. 다음 코드는 첫 번째 호출에서 CURLRequest 인스턴스를 생성합니다. 그리고 두 번째 호출은 정확히 동일한 인스턴스를 반환합니다.

<?php

$options1 = [
    'baseURI' => 'http://example.com/api/v1/',
    'timeout' => 3,
];
$client1 = \Config\Services::curlrequest($options1);

$options2 = [
    'baseURI' => 'http://another.example.com/api/v2/',
    'timeout' => 10,
];
$client2 = \Config\Services::curlrequest($options2);
// $options2 does not work.
// $client2 is the exactly same instance as $client1.

따라서 $client2에 대한 매개변수 $options2는 작동하지 않고 그냥 무시됩니다.

새 인스턴스 가져오기

Typography 클래스의 새 인스턴스를 가져오려면 $getShared 인수에 false를 전달해야 합니다.

<?php

$typography = \Config\Services::typography(false);

편의 함수

서비스를 위해 두 가지 함수가 제공되며, 이 함수는 항상 사용 가능합니다.

service()

첫 번째는 service()이며 요청된 서비스의 새 인스턴스를 반환합니다. 필요한 매개 변수(parameter)는 한 개이며, 서비스 이름입니다.

서비스를 통하여 반환된 인스턴스는 클래스의 SHARED 인스턴스이므로 함수를 여러 번 호출해도 항상 동일한 인스턴스가 반환됩니다.

<?php

$logger = service('logger');

// The code above is the same as the code below.
$logger = \Config\Services::logger();

서비스 생성시 추가 매개 변수를 전달이 필요하면 서비스 이름 다음 두 번째 매개변수로 전달합니다.

<?php

$renderer = service('renderer', APPPATH . 'views/');

// The code above is the same as the code below.
$renderer = \Config\Services::renderer(APPPATH . 'views/');

single_service()

single_service() 함수는 service() 함수와 똑같이 작동하지만, 호출할 때마다 새로운 클래스 인스턴스를 반환합니다.

<?php

$logger = single_service('logger');

// The code above is the same as the code below.
$logger = \Config\Services::logger(false);

서비스 정의

서비스가 제대로 작동하려면 각 클래스에 일정한 API 또는 인터페이스를 사용하여 서비스를 이용할 수 있어야 합니다. CodeIgniter의 거의 모든 클래스는 해당 클래스가 준수하는 인터페이스를 제공합니다. 코어 클래스를 확장하거나 교체하려는 경우 인터페이스의 요구 사항을 충족하고 클래스가 호환되는지 확인합니다.

RouteCollectionInterface를 구현한 RouteCollection 클래스를 예로 들어보겠습니다. 경로(route)를 생성하는 다른 방법을 제공하는 클래스를 만든다면, RouteCollectionInterface를 구현하는 새 클래스를 만들어야 합니다.

<?php

namespace App\Router;

use CodeIgniter\Router\RouteCollectionInterface;

class MyRouteCollection implements RouteCollectionInterface
{
    // Implement required methods here.
}

마지막으로 app/Config/Services.phproutes() 메소드를 추가하여 CodeIgniter\Router\RouteCollection 대신 MyRouteCollection의 새 인스턴스를 생성합니다.

<?php

namespace Config;

use CodeIgniter\Config\BaseService;

class Services extends BaseService
{
    // ...

    public static function routes()
    {
        return new \App\Router\MyRouteCollection(static::locator(), config('Modules'));
    }
}

매개 변수 허용

경우에 따라 인스턴스화 중에 클래스에 설정을 전달하는 옵션이 필요할 수 있습니다. 서비스는 매우 간단한 클래스이므로, 이 작업을 쉽게 수행할 수 있습니다.

좋은 예는 renderer 서비스입니다. 기본적으로 이 클래스는 APPPATH . 'views/'에서 뷰(view)를 찾습니다. 그러나 개발자는 필요에 따라 경로를 변경할 수 있는 옵션을 원하며, 이 클래스는 $viewPath를 생성자 매개 변수(parameter)로 허용합니다. 서비스 방법은 다음과 같습니다.

<?php

namespace Config;

use CodeIgniter\Config\BaseService;

class Services extends BaseService
{
    // ...

    public static function renderer($viewPath = APPPATH . 'views/')
    {
        return new \CodeIgniter\View\View($viewPath);
    }
}

생성자 메소드에서 기본 경로를 설정하지만, 사용하고자 하는 경로로 쉽게 변경할 수 있습니다.

<?php

$renderer = \Config\Services::renderer('/shared/views/');

공유 클래스

서비스의 단일 인스턴스만 생성하도록 요구해야 하는 경우가 있습니다. 이것은 팩토리 메소드(factory method) 내에서 호출하는 getSharedInstance() 메소드로 쉽게 처리됩니다. 클래스 내에서 인스턴스가 생성 및 저장되었는지 확인하고 그렇지 않은 경우 새 인스턴스를 만듭니다. 모든 팩토리 메소드는 단일 매개 변수 $getShared = true를 제공하며 이 규칙을 준수해야 합니다.

<?php

namespace Config;

use CodeIgniter\Config\BaseService;

class Services extends BaseService
{
    // ...

    public static function routes($getShared = true)
    {
        if ($getShared) {
            return static::getSharedInstance('routes');
        }

        return new \App\Router\MyRouteCollection(static::locator(), config('Modules'));
    }
}

서비스 검색

CodeIgniter는 정의된 네임스페이스 내에 생성한 Config\Services.php 파일을 자동으로 검색할 수 있습니다. 이를 통해 모듈 서비스 파일을 간단하게 사용할 수 있습니다. 사용자 정의 서비스 파일을 검색하려면 다음 요구 사항을 충족해야 합니다.

  • 네임스페이스 정의는 app\Config\Autoload.php에 해야 합니다.

  • 네임스페이스에 속한 파일은 Config\Services.php에서 반드시 찾을 수 있어야 합니다.

  • 반드시 CodeIgniter\Config\BaseService를 확장(extend)해야 합니다.

다음의 작은 예시를 살펴보십시오.

프로젝트 루트 디렉토리에 Blog라는 새로운 디렉토리를 만들었다고 상상하십시오. 여기에는 컨트롤러, 모델 등이 포함된 Blog module이 있으며 일부 클래스를 서비스로 제공하려고 합니다. 첫 번째 단계는 Blog\Config\Services.php라는 새 파일을 만드는 것입니다. 파일의 골격은 다음과 같습니다.

<?php

namespace Blog\Config;

use CodeIgniter\Config\BaseService;

class Services extends BaseService
{
    public static function postManager()
    {
        // ...
    }
}

이제 위에서 설명한대로 이 파일을 사용할 수 있습니다. 컨트롤러에서 게시물 서비스를 가져오려면 프레임워크의 Config\Services 클래스를 사용하여 서비스를 가져 오면 됩니다.

<?php

$postManager = \Config\Services::postManager();

Note

여러 서비스 파일의 메소드 이름이 동일한 경우 첫 번째 발견된 파일의 인스턴스가 반환(return)됩니다.