"이 글에서는 주로 프레임워크의 실행 과정을 소개합니다
"
프레임워크가 어떻게 실행되는지 모른다면 대부분의 코드는 그냥 코드 인식 소스 코드를 읽는 것은 해당 프레임워크의 디자인 아이디어와 코드 패턴을 배우는 것입니다.
실행 과정은 우리가 배운 것들을 더 잘 이해할 수 있도록 연결하는 것입니다. 카카는 실행 과정도 마인드맵 형태로 그려드립니다.
이 기사를 통해 모두가 조금씩 지식을 배울 수 있다면 Kaka는 만족할 것입니다.
이 흐름도는 초기화 실행 과정만을 위한 것입니다. 나머지 실행 과정은 추후 보완되어 마인드맵 형태로 여러분께 선보일 예정입니다.
여기의 내용은 실행 프로세스가 시작되기 때문에 컨테이너의 내용과 약간 반복됩니다. 항목 파일에서 최종적으로 컨테이너를 통해 실행됩니다.
그런 다음 파일을 입력합니다 이 메소드에서 thinkphp/library/think/App.php
의 run 메소드는 주로 아래 프레임에서 실행되는 초기화 메소드입니다. thinkphp/library/think/App.php
的run方法,在这个方法中主要就是下图框出来的地方,执行的initialize方法。
来到initialize这个方法,先看上半部分。
microtime(true);
返回的是unix的微秒数memory_get_usage
返回的是分配给PHP的内存量,单位为字节static::setInstance($this);
这里是将app这个实例设置为容器实例$this->instance('app', $this);
这个在之前容器章节就提到了,就是为了把app这个类绑定到容器里边去,也就是注册树模式。这里有一个小的问题点给大家提出来,在初始化应用的这个方法里边存在这样一行代码。
有没有小伙伴对这个$this->env
和下边的$this->config
microtime(true);
Unix의 마이크로초 수를 반환합니다memory_get_usage
는 PHP에 할당된 메모리 양을 바이트 단위로 반환합니다.static::setInstance($this);
여기서 앱 인스턴스를 다음과 같이 설정합니다. 컨테이너 인스턴스$this->instance(' app', $this );
이전 컨테이너 장에서 앱 클래스를 컨테이너에 바인딩하기 위해 언급한 내용이 바로 등록 트리 모드입니다.
🎜
🎜🎜여기에 모든 사람에게 작은 문제가 있습니다. 초기화 중에 다음과 같은 문제가 있습니다. 이 응용 프로그램 방법의 코드 줄입니다. 🎜🎜이 $this-> ; env
및 다음 $this-> ; config
이 두 호출에 대해 의심이 있습니다. 🎜
궁금하신 점이 있으시면 카카를 따라가서 읽어보시면 됩니다.
App 클래스는 상속된 컨테이너 클래스이므로 env 및 config에는 앱이나 컨테이너 클래스에 이 두 가지 속성이 없습니다.
그럼 어떻게 직접 호출할 수 있나요! 그리고 코드 추적은 env 클래스와 컨테이너 클래스로 추적됩니다.
소스를 알려면 컨테이너 클래스의 코드를 대략적으로 살펴봐야 합니다.
좀 열심히 읽으면 아래 그림에서 몇 줄의 코드를 볼 수 있습니다. 이 코드 줄은 모두 매직 메서드를 사용합니다.
접근한 env 클래스가 존재하지 않는 경우 make 메소드가 실행됩니다.
make 방법은 컨테이너 장에 너무 자세하게 설명되어 있어서 자세히 설명할 수 없습니다.
이 make 메소드는 결국 클래스의 인스턴스를 반환하고 이를 컨테이너에 저장합니다.
여기에는 make 메소드 중 하나의 코드만 넣으세요. 방법을 모르신다면 이전 글을 읽어보세요.
마지막 단계는 일련의 데이터를 로드하는 것입니다. 로드 세부 사항은 서문의 마인드 맵을 참조하세요.
소스코드를 읽는 과정에서 제어하기 어려운 문제가 있는데, 바로 , 메소드가 다른 곳에서 실행됩니다. 여러 곳에서 호출이 이루어졌는데, 한동안 실제로 어디서 호출되었는지 알 수 없었습니다.
다음은 init 메소드를 사용한 데모입니다.
init 메소드는 애플리케이션이나 모듈을 초기화하는 메소드인데, 여기서 모듈 매개변수에는 null 값이 있습니다.
먼저 중단점을 만들어 관련 데이터 정보를 확인하세요.
인쇄된 결과가 비어 있습니다. 이 방법은 한 번만 호출할 수 없기 때문에 일부 새로운 학습 파트너가 저지르는 실수입니다.
초기화 모듈이 모두 비어 있으면 이 메서드가 존재할 필요가 없습니다.
그러면 올바른 중단점 방법은 다음과 같아야 합니다.
이 때 문제가 발생합니다. 이 init 메소드는 분명히 두 번 호출되는데 다른 호출은 어디에 있습니까?
새로운 기술을 모른다면 init의 상위 수준에서 인쇄하는 등 실행이 수행되는 위치를 확인하기 위해 일련의 중단점을 인쇄하게 됩니다.
즉, 초기화 방법에서 중단점을 인쇄하는 것인데 이는 매우 번거롭고 많은 시간을 낭비할 가능성이 높으며 여전히 올바른 위치를 찾지 못합니다.
팁: debug_backtrace()
이 메서드는 메서드의 모든 호출 위치를 표시하는 역추적을 생성합니다.
사용 방법은 아래와 같습니다. debug_backtrace 메소드만 출력하면 됩니다.
획득된 데이터 정보를 바탕으로 매우 빠르게 측위를 수행할 수 있습니다.
첫번째는 앱클래스 215번째 줄입니다.
두 번째 호출은 60줄 thinkphp/library/think/route/dispatch/Module.php
classthinkphp/library/think/route/dispatch/Module.php
类的60行
可以在这里做一个打印,看一下这个module是否为index
所以说有了这个方法就可以非常快速地定位调用位置。
上文给大家提供了一个小技巧debug_backtrace
두 번째로 해당 장소에 전화할 때
🎜🎜여기에서 인쇄하여 이 모듈이 index🎜🎜인지 확인할 수 있습니다. 그래서 이 방법을 사용하면 매우 빠를 수 있습니다. 호출 위치. 🎜debug_backtrace
는 실제로 메소드가 실행되는 위치를 확인하는 방법을 보여줍니다. 🎜🎜그리고 이 사례에서는 init 메서드를 사용하여 시연합니다. 다음 단계는 init 메서드를 심층적으로 이해하는 것이기 때문입니다. 🎜🎜init 메소드에서 수행되는 주요 작업은 위의 마인드 맵에 명확하게 설명되어 있습니다. 🎜bindTo
등록 바인딩을 위한 메서드입니다. bindTo
方法进行绑定注册的。env
env
환경변수 구성의 마지막 단계는 컨테이너의 객체 인스턴스를 구성하고 업데이트하는 것입니다. 구체적인 업데이트에 대해서는 나중에 자세히 설명하겠습니다.
<span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br/> * 初始化应用或模块<br/> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@access</span> public<br/> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@param</span> string $module 模块名<br/> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@return</span> void<br/> */</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">function</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">init</span><span class="hljs-params" style="line-height: 26px;">($module = <span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span>)</span><br/> </span>{<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 定位模块目录</span><br/> $module = $module ? $module . DIRECTORY_SEPARATOR : <span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span>;<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br/> * 第一次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\<br/> * 第二次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\<br/> */</span><br/> $path = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->appPath . $module;<br/><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载初始化文件</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>;<br/> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">elseif</span> (is_file(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->runtimePath . $module . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->runtimePath . $module . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>;<br/> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载行为扩展文件</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'tags.php'</span>)) {<br/> $tags = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'tags.php'</span>;<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($tags)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->hook->import($tags);<br/> }<br/> }<br/><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载公共文件</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'common.php'</span>)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include_once</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'common.php'</span>;<br/> }<br/><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (<span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span> == $module) {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载系统助手函数</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->thinkPath . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'helper.php'</span>;<br/> }<br/><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载中间件</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'middleware.php'</span>)) {<br/> $middleware = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'middleware.php'</span>;<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($middleware)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->middleware->import($middleware);<br/> }<br/> }<br/><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 注册服务的容器对象实例</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'provider.php'</span>)) {<br/> $provider = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'provider.php'</span>;<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($provider)) {<br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->bindTo($provider);<br/> }<br/> }<br/><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br/> * $path : "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\"<br/> * "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\"<br/> */</span><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 自动读取配置文件</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_dir($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'config'</span>)) {<br/> $dir = $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'config'</span> . DIRECTORY_SEPARATOR;<br/> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">elseif</span> (is_dir(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configPath . $module)) {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\config\</span><br/> $dir = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configPath . $module;<br/> }<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// scandir:以升序的方式读取目录中的文件</span><br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 返回就是config目录中的所有文件</span><br/> $files = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">isset</span>($dir) ? scandir($dir) : [];<br/><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">foreach</span> ($files <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">as</span> $file) {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br/> * $this->configExt:配置文件的后缀<br/> * pathinfo返回的是文件后缀,关于pathinfo共有三个可选的参数PATHINFO_DIRNAME、PATHINFO_BASENAME、PATHINFO_EXTENSION,分别为只返回文件名,文件目录名,文件扩展<br/> */</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (<span class="hljs-string" style="color: #98c379; line-height: 26px;">'.'</span> . pathinfo($file, PATHINFO_EXTENSION) === <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configExt) {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br/> * 俩个参数分别为<br/> * 1.目录+config目录下的文件<br/> * 2.config目录下文件名<br/> */</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));<br/> }<br/> }<br/> }<br/><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->setModulePath($path);<br/><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> ($module) {<br/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 对容器中的对象实例进行配置更新</span><br/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->containerConfigUpdate($module);<br/> }<br/> }<br/></code>여기에는 이전 코드를 첨부해두었으니 위의 실행 과정을 코드를 보시면서 각 단계를 간략하게 설명드리겠습니다.
소스 코드 최적화에 대한 Kaka의 개인적인 의견
Kaka는 init 메소드가 두 곳에서 실행되기 때문에 모듈 설정 단계에서 그다지 엄격하다고 느끼지 않습니다. 처음 모듈이 비어 있을 때 이 코드를 실행하는 것은 의미가 없습니다. 다음은 모듈의 이 매개변수가 비어 있는지 확인하기 위해 컨테이너의 객체 인스턴스 구성을 업데이트할 때의 판단입니다. 비어 있지 않으면 실행됩니다. 그러면 같은 이유로 카카는 모듈 경로 설정도 이번 판단에 포함되어야 한다고 생각합니다. 🎜🎜두 번째 실행이 첫 번째 결과를 덮어쓰게 되지만 카카는 아래 그림처럼 사용하는 것이 더 좋을 것 같다고 생각합니다. 🎜이전 섹션에서 최종 내용은 다음과 같습니다. 구성을 업데이트할 때 정확히 무엇이 업데이트되고 어떻게 업데이트되는지에 대한 설명이 없습니다.
이 섹션에서는 설명이 이루어지며 서문의 마인드 맵을 통해서도 읽을 수 있습니다.
이 섹션에서 가장 중요한 것은 아래 그림의 콘텐츠라고 생각합니다.
한두 가지 메소드를 마음대로 추적하여 그곳에서 어떤 메소드가 실행되는지 확인할 수 있습니다.
추적 메소드 Db::init()
메소드를 추적해 보면 Db 클래스의 config 속성에 값을 할당하고, Db의 config 속성에 데이터베이스의 값을 할당하는 것을 볼 수 있습니다. 수업.
Tracking 메소드 $this->middleware->setConfig()
미들웨어 클래스에 들어오면 이 클래스의 구성과 전달된 매개변수 클래스가 병합되어 동일한 작업이 수행되는 것을 볼 수 있습니다. 구성 속성의
위 사례에서 Db 클래스의 init 메소드로 얻은 효과는 일관됩니다.
여기서 말씀드리고 싶은 점은 对容器中的对象实例进行更新配置
이 사진에서 보라색 부분은 이 카테고리에서 언급되지 않은 부분이라는 점입니다.
그럼 어떻게 실행할 수 있나요? App 클래스가 컨테이너 클래스를 상속받기 때문입니다. 컨테이너 클래스에는 4개의 매직 메서드가 있는데, 그 중 하나가 존재하지 않는 속성을 얻을 때 실행되는 메서드인 __get 메서드입니다.
make 메소드는 매직 메소드인 __get 메소드에서 실행됩니다. 이 메소드는 결국 애플리케이션의 인스턴스를 반환한 다음 이 인스턴스를 사용하여 해당 인스턴스 클래스의 메소드를 호출합니다.
잘 이해해야 합니다. 소스 코드를 읽는 것도 이와 같습니다. 이렇게 해야 우리의 프로그래밍 능력과 사고력이 향상될 수 있습니다.
이 섹션에서는 디버깅 모드 및 프레임워크 코드에 대해 간략하게 설명합니다. 중복 상황은 다음과 같습니다. 짧게 올렸습니다.
아무도 작성한 코드에는 허점이 없습니다. 허점이 있으면 특정 수준에 도달하지 않은 것입니다.
디버그 모드
첫 번째 섹션에서는 초기화 방법의 전반부만 언급했는데, 이 섹션 이전에는 애플리케이션 초기화 초기화에 관한 모든 이야기가 있었기 때문입니다.
이 섹션의 내용은 다음에 간략하게 설명하겠습니다.
다음 내용은 아마도 이해하기 쉽지 않을 것이며, 일상 업무에서 전혀 사용되지 않는 내용입니다.
지금은 위의 세 가지만 알아도 충분합니다. 기회가 된다면 나중에 글을 써서 설명하겠습니다.
프레임워크 코드 중복에 대하여
이 내용은 Kaka의 개인적인 의견일 뿐입니다.
먼저 코드의 이 부분을 살펴보세요. 이 두 코드는 매우 익숙합니까? 예, 위 init 메소드의 컨테이너 객체 인스턴스 구성 업데이트에서 본 적이 있습니다.
사진과 같이
카카의 5.1 소스코드 해석으로 인해 새 버전에서는 변경이 되었는지는 모르겠습니다.
이 섹션에서는 주로 프레임워크 실행 과정에서 초기화 적용에 대해 간략하게 설명합니다.
앱 클래스의 run 메소드 하에서 많은 실행 프로세스에 대해서는 이 섹션에서는 많은 설명이 없습니다.
소스코드를 읽는 과정에서 메소드가 어디서 실행되는지 확인하는 방법에 대한 좋은 팁을 알려드렸습니다.
이 방법은 debug_backtrace
. 이 방법은 인쇄된 결과에도 쓸모없는 정보가 많기 때문에 사용법을 알기 전에 몇 번을 사용해야 합니다.
이 방법은 소스 코드를 디버깅하는 과정에서 매우 효과적입니다. 이 방법을 꼭 활용하세요.
애플리케이션을 초기화하는 init 방법을 자세히 소개합니다.
그 중에서 Kaka는 이 디자인의 가장 좋은 부분은 컨테이너의 객체 인스턴스를 업데이트하고 구성하는 것이라고 생각합니다. 먼저 모든 구성을 읽은 다음 각 클래스의 메서드를 통해 구성을 설정합니다.
이런 종류의 코드 계획과 디자인 아이디어는 배울 가치가 있습니다.
마지막으로 디버깅 모드 및 프레임워크의 코드 중복 문제에 대해 이야기했습니다. 디버깅 모드와 관련하여 온라인 프로젝트의 디버깅 모드를 꺼야 한다는 점을 모두에게 상기시키고 싶습니다.
그렇지 않으면 프로젝트가 전혀 안전하지 않은 줄무늬와 같을 것입니다.
약간 이해하기 어려운 점은 완충지대입니다. 카카는 이 지역의 내용에 대해 일단 세부 사항을 밝힐 필요는 없다고 생각하며, 그 후에 심도 있는 조사를 진행해야 한다고 생각합니다. .
이 부분의 버퍼는 3~4년 정도 일한 사람들이 거의 사용하지 않는 것으로 추정되므로 먼저 알고 내용을 파악한 후 카카가 모두를 위해 보충해 줍니다. .
이번 섹션에서는 프레임워크 실행 프로세스의 초기 적용이 끝났습니다. 주로 코드 디자인 패턴과 구현 아이디어를 배울 수 있습니다.
마지막 사진은 소스코드를 따라가서 봐주셔야해요!
“배움에 대한 끈기, 블로그에 대한 끈기, 공유에 대한 끈기는 Kaka가 경력을 시작한 이래로 항상 고수해온 신념입니다. 거대한 인터넷이 가져올 수 있습니다. 도와주셔서 감사합니다. 다음에 뵙겠습니다.
위 내용은 ThinkPHP 프레임워크 실행 프로세스(브레인 맵 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!