질문을 삭제하지 말아주세요.!
 
2
1
0

 

마크다운뷰어 실습중인데요 (챕터19/178페이지)

 

-------------------[코드19-3]------------------

class DocsController extends Controller {

    protected $docs;

    public function __construct( \App\Documentation $docs)

   {

        $this->docs = $docs;

    }

    ....

-----------------------------------------

보면 __construct( \App\Documentation $docs) 라고 생성자에 타입만 선언했을 뿐인데..

$docs 인스턴스가 어디선가 생성이 되어 의존성주입이 되는것 같은데요..

마치 마술을 보는것 같아서요

 

도데체 저 $docs 인스턴스는 어디서 생성되는 것인지요?

라우터(routes/web.php) 에서는 단순히 컨트롤러만 지정했을 뿐인데요..

 

  • Route::get( 'docs/{file?}', 'DocsController@show');

 

라라벨 프레임워크에서 컨트롤러 연결시에 컨트롤러 생성자함수를 분석(?) 해서

$docs 인스턴스를 new \App\Documentation() 형식으로 인스턴스를 자동 생성해서 넘겨주는 방식인가요?

 

이런 방식이 컨트롤러만 그런것인지, 아니면 모델이나 다른 클래스도

이렇게 자동으로 의존성주입이 되는 것인가요?

 

 

 

    CommentAdd your comment...

    3 answers

    1.  
      3
      2
      1

      일단 어떻게 생성되는지 이야기 드리자면 PHP에서 제공하는 Reflection 이라는 기능을 통해서 만들었는데 아주 신기하고 기똥찬 녀석입니다. (PHP 해킹에 입문하신것을 축하드립니다.)

      일단 어디서 생성되냐고 물으셨는데요, Container 객체의 build라는 메서드가 생성하고 있습니다.

      https://github.com/laravel/framework/blob/5.3/src/Illuminate/Container/Container.php#L740

      정수님이 주신 링크를 읽으면 개념은 이해가 될것 같고요, 더 정확한 생성원리 설명을 위해 라라벨 소스를 가지고 왔습니다. 주석에 최대한 설명해서 적어놓았습니다. (smile)

       

      public function build($concrete, array $parameters = [])
          // $concrete에는 "DocsController" 클래스 스트링이 들어옵니다. 네임스페이스랑 같이요.
          // 예를들어, "App\Http\Controller\DocsController" 뭐 요런식으로요.
          {
              // $concrete는 string이니까 여기는 그냥 지나갑니다~
              if ($concrete instanceof Closure) {
                  return $concrete($this, $parameters);
              }
      
              // PHP에는 ReflectionClass라는 녀석이 있는데요, 클래스 자체를 객체화하는
              // 강력한 녀석입니다.
              $reflector = new ReflectionClass($concrete);
      
              // 객체 생성가능성을 묻는데 여기도 해당되지 않으니 그냥 지나갑니다~
              if (! $reflector->isInstantiable()) {
                  /* 생략 */
              }
      
              // Circular 때문에 넣는 것 같은데 그냥 모르셔도 되요.
              $this->buildStack[] = $concrete;
      
      
              // Reflection을 통해서 생성자의 객체를 가지고 옵니다.
              // Reflection을 거치면 클래스 뿐 아니라 각각의 메서드 객체에 접근할 수 있습니다.
              // ReflectionMethod 타입의 객체가 반환됩니다.
              $constructor = $reflector->getConstructor();
      
              // 생성자가 없다는 의미입니다. 생성자가 없으면 그냥 new DocsController(); 하면 되니까
              // 다음과 같이 생성합니다.
              if (is_null($constructor)) {
                  array_pop($this->buildStack);
                  return new $concrete;
              }
      
              // 생성자가 있으면 생성자의 Parameter를 가지고 옵니다.
              // DocsController 객체 생성자에 __construct(\App\Documentation $docs)
              // 내용이 들어있으니 그녀석을 반환해줍니다.
              // ReflectionParameter 객체의 배열을 반환합니다. Parameter가 하나니까
              // 한개짜리가 오겠네요.
              $dependencies = $constructor->getParameters();
      
              // 지정된 $parameters 변수가 $dependencies에 있으면 그녀석을 사용하겠다는 건데..
              // $parameters 변수는 [] 이기 때문에 여기서는 사실상 아무런 로직이 돌지 않습니다.
              // 반환값 역시 $parameters입니다.
              $parameters = $this->keyParametersByArgument(
                  $dependencies, $parameters
              );
      
              // ReflectionParameter의 배열을 통해서 $docs 값을 만듭니다.
              // 요기서 내부적으로 $this->build("App\Documentation")을 다시
              // Recursive 하게 호출하게 됩니다. 그리고 App\Documentation의 객체를 반환합니다.
              $instances = $this->getDependencies(
                  $dependencies, $parameters
              );
      
              // Cicular 때문에 넣는 로직
              array_pop($this->buildStack);
      
      
              // 다음 로직은 new DocsController($instances[0], $instances[1] ... ) 과
              // 같이 동작합니다. 짠, 우리가 원하던 객체가 완성되었습니다.
              return $reflector->newInstanceArgs($instances);
          }
      
      
        CommentAdd your comment...
      1.  
        3
        2
        1

        컨트롤러, Validator.. 등 라라벨의 여러곳에서 Service Container 가 의존성을 주입해줍니다. 

        이미 지정된 경우에 그렇다고 보시면 되구요, 직접 구성하신 클래스라면

        Container 를 통해서 직접 의존성을 해결하도록 할 수도 있습니다. 

         

        물론 생성자의 의존성을 해결하려면 미리 컨테이너에 바인딩을 해놓아야 하구요, 

        바인딩은 Service Provider 에서 구성하고 있습니다.

         

        컨트롤러의 메소드에 의존성을 주입하는 것은 https://laravel.kr/docs/5.3/controllers#dependency-injection-and-controllers를 참고하시면 될것 같습니다.

         

        1. 김주원

          와우 ~ 정수님! 담배피러 잠깐 나갔다가 메일 보고 답하려 들어왔더니, 벌써 답변이 떠억!! 최곱니다.

        2. 수수나무

          감사합니다!~

          링크 걸어주신 자료가 도움이 될 것 같습니다. 

        CommentAdd your comment...
      2.  
        2
        1
        0

        예 Automatic Resolution이 작동하구요. PHP의 Reflection API를 이용하는 겁니다. 실제 코드는 여기를 참고하시면 됩니다. Reflection API로 의존성을 재귀적으로 계속 찾아서 주입하는 방식입니다.

        https://github.com/laravel/framework/blob/5.3/src/Illuminate/Container/Container.php#L740

         

        제가 알고 있기로는 다음의 경우에 작동합니다.

        1. 생성자의 인자에 타입 힌트를 정확히 주었을 때
        2. 메서드의 인자에 타입 힌트를 정확히 주었을 때
        3. App::make('ClassName') 또는 app('ClassName') 을 이용할 때

        의존성의 의존성의 의존성의...까지 자동으로 주입해 줍니다. 단 타입 힌트가 있을 때만요.
        혹시 제가 틀렸다면 다른 분들이 지적해 주실 겁니다. 

         

        정수님이 번역하신 매뉴얼 좌표 남깁니다. https://laravel.kr/docs/5.3/container#resolving 

          CommentAdd your comment...