스프링에서 ContextLoaderListener의 역할 / 목적?


169

내 프로젝트에서 사용중인 Spring Framework 를 배우고 있습니다. web.xml 파일 에서 ContextLoaderListener 항목을 찾았습니다 . 그러나 그것이 개발자에게 정확히 어떻게 도움이되는지 알 수 없었습니까?

ContextLoaderListener 의 공식 문서에서 WebApplicationContext 를 시작한다고 말합니다 . WebApplicationContext 와 관련하여 JavaDocs는 다음 과 같이 말합니다.

웹 애플리케이션을위한 구성을 제공하기위한 인터페이스.


그러나 WebApplicationContext 를 내부적으로 초기화하는 ContextLoaderListener 로 달성 한 것을 이해할 수 없습니다 .

내 이해에 따라 ContextLoaderListener 는 Spring 구성 파일 ( web.xml의 contextConfigLocation에 제공된 값으로)을 읽고 구문 분석 한 다음 해당 구성 파일에 정의 된 싱글 톤 Bean을 로드 합니다. 마찬가지로 prototype bean 을로드 할 때 동일한 웹 애플리케이션 컨텍스트를 사용하여로드합니다. 따라서 ContextLoaderListener를 사용 하여 웹 응용 프로그램을 초기화하여 구성 파일을 미리 읽거나 구문 분석 / 확인하고 종속성을 주입하려고 할 때마다 지연없이 바로 수행 할 수 있습니다. 이 이해가 맞습니까?


1
누구든지 RequestContextListener와 ContextLoaderListener의 차이점을 알려줄 수
있습니까

답변:


111

이해가 정확합니다. 는 ApplicationContext귀하의 봄 콩이 사는 곳이다. 의 목적 ContextLoaderListener은 두 가지입니다.

  1. 의주기 넥타이 ApplicationContext의 라이프 사이클에 ServletContext

  2. 의 생성을 자동화 ApplicationContext하기 위해 명시 적 코드를 작성할 필요가 없습니다. 편의 기능입니다.

에 대한 또 다른 편리한 점은 비아 빈과 메소드에 대한 액세스를 제공 하고 제공 ContextLoaderListener한다는 것 입니다.WebApplicationContextWebApplicationContextServletContextServletContextAwaregetServletContext


2
두 번째 요점에 대해서는 의문이 있습니다. ServletContextListener가 ServletContext에 대한 액세스를 제공한다고 말했다. 그러나 web.xml에 ServletContextListener가 없어도 WebApplicationContext를 통해 ServletContext에 액세스 할 수 있습니다 (WebApplicationContext는 자동 연결됨). 그렇다면 ServletContext와 정확히 어떤 관련이 있습니까?
서밋 데 사이

를 만듭니다 WebApplicationContext. 그렇지 않으면 수동으로 작성해야합니다.
sourcedelica

않는 ContextLoaderListener경우 웹 컨테이너 닫힌다 아래 모든 콩을 파괴하는 파괴 메소드를 구현?
ass

예- contextDestroyed호출 될 때 그렇게합니다 . API 문서를 참조하십시오.
sourcedelica

@sourcedelica이 글을 읽은 후에 한 가지 의문이 생겼습니다 web.xml. 내 XML 파일에이 두 개의 리스너는 ContextLoaderListenerDispatcherServlet. 그래서 둘 다 필요하지 않다고 생각 ContextLoaderListener합니다. 응용 프로그램이 7-8 개월 이후에 살고 있기 때문에 묻는 이유 를 제거하는 것이 안전 합니까? web.xml이 여기 에 있습니다.
Amogh

43

ContextLoaderListener이다 선택 . 그냥 여기에 포인트로 만들려면 : 혹시 구성하지 않고 Spring 애플리케이션을 부팅 할 수 있습니다 ContextLoaderListener, 단지 기본 최소 web.xml로를 DispatcherServlet.

다음과 같은 모습입니다.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemaLocation="
        http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    id="WebApp_ID" 
    version="2.5">
  <display-name>Some Minimal Webapp</display-name>
  <welcome-file-list>   
    <welcome-file>index.jsp</welcome-file>    
  </welcome-file-list>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

라는 파일을 작성하고 dispatcher-servlet.xml아래에 저장하십시오 WEB-INF. index.jsp환영 목록에서 언급 했으므로이 파일을 아래에 추가하십시오 WEB-INF.

dispatcher-servlet.xml

에서 dispatcher-servlet.xml당신의 bean을 정의 :

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd     
        http://www.springframework.org/schema/context     
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="bean1">
      ...
    </bean>
    <bean id="bean2">
      ...
    </bean>         

    <context:component-scan base-package="com.example" />
    <!-- Import your other configuration files too -->
    <import resource="other-configs.xml"/>
    <import resource="some-other-config.xml"/>

    <!-- View Resolver -->
    <bean 
        id="viewResolver" 
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
      <property 
          name="viewClass" 
          value="org.springframework.web.servlet.view.JstlView" />
      <property name="prefix" value="/WEB-INF/jsp/" />
      <property name="suffix" value=".jsp" />
    </bean>
</beans>

2
이 옵션의 경우,시는 것입니다 원하는 그것을 사용할 수 있나요? Spring Security는 DelegatingFilterProxy를 사용해야합니다.
David

6
기본 이름 "[servlet-name] -servlet.xml"및 "Web-INF /"아래의 경로가 아닌 사용자 정의 위치 또는 사용자 정의 이름으로 서블릿 파일을 저장하려는 경우이 파일을 사용해야합니다.
Ramesh Karna

applicationContext.xml보다 dispatcher-servlet.xml에서 Bean을 정의하는 것이 좋은 생각입니까?
Chetan Gole

8
일반적으로 애플리케이션 아키텍처의 계층을 반영하여 Bean을 분배하는 것이 좋습니다. 프리젠 테이션 계층의 Bean (예 : mvc 컨트롤러)은 dispatcher-servlet.xml에있을 수 있습니다. 서비스 계층에 속하는 Bean은 applicationContext.xml로 정의해야합니다. 엄격한 규칙은 아니지만 우려를 분리하는 것이 좋습니다.
Claudio Venturini

2
@Ramesh Karna 나는 그것이 이름과 위치 변경에 필요하다고 생각하지 않습니다. 여러 Dispatcher 서블릿을 초기화 할 때 루트 컨텍스트를 모든 DispaterServlets 자체 컨텍스트에서 공유하고 싶을 때 ContextLoaderListener를 사용해야한다고 생각합니다.
초신성

23

간단한 Spring 애플리케이션의 경우 ; ContextLoaderListener에서 정의 할 필요가 없습니다 web.xml. 모든 Spring 구성 파일을 <servlet>다음 위치에 넣을 수 있습니다 .

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/mvc-core-config.xml, classpath:spring/business-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

여러 한 더 복잡한 Spring 애플리케이션의 경우, DispatcherServlet정의, 당신은 모두가 공유하는 공통의 봄 구성 파일 수 DispatcherServlet에 정의를 ContextLoaderListener:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/common-config.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>mvc1</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/mvc1-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>mvc2</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/mvc2-config.xmll</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

명심 ContextLoaderListener하고 루트에 대한 실제 초기화 작업을 수행하십시오. 응용 프로그램 컨텍스트에 .

:이 기사가 많은 도움이 발견 웹 응용 프로그램 컨텍스트 대 응용 프로그램 컨텍스트 - 스프링 MVC를


여기에 공유 된 기사는 개념에 대한 깊은 이해를 보장합니다
Priyank Thakkar

10

블로그 " ContextLoaderListener의 목적 – Spring MVC "는 아주 좋은 설명을 제공합니다.

이에 따르면 Application-Contexts는 계층 적이므로 DispatcherSerlvet의 컨텍스트는 ContextLoaderListener의 컨텍스트의 하위가됩니다. 그로 인해 컨트롤러 계층 (Struts 또는 Spring MVC)에서 사용되는 기술은 루트 컨텍스트 생성 ContextLoaderListener와 독립적 일 수 있습니다.


친구 공유해 주셔서 감사합니다 .. :)
Deepak Kumar

3

기본 이름 지정 규칙 [servletname]-servlet.xml및 경로가 아닌 사용자 정의 위치 또는 사용자 정의 이름으로 서블릿 파일을 저장하려는 경우을 Web-INF/사용할 수 있습니다 ContextLoaderListener.


3

ContextLoaderListner는 모든 다른 구성 파일 (서비스 계층 구성, 지속성 계층 구성 등)을 단일 스프링 애플리케이션 컨텍스트로로드하는 서블릿 리스너입니다.

이를 통해 스프링 구성을 여러 XML 파일로 분할 할 수 있습니다.

컨텍스트 파일이로드되면 Spring은 Bean 정의를 기반으로 WebApplicationContext 객체를 생성하고이를 웹 애플리케이션의 ServletContext에 저장합니다.


3

여기에 이미지 설명을 입력하십시오이 부트 스트랩 리스너는 Spring의 루트 WebApplicationContext 를 시작하고 종료하는 것 입니다. 웹 애플리케이션은 여러 디스패처 서블릿을 가질 수 있고 각각 컨트롤러, 뷰 리졸버, 핸들러 맵핑을 포함하는 자체 애플리케이션 컨텍스트를 가질 수 있지만 루트 애플리케이션 컨텍스트에 서비스 Bean, DAO Bean을 갖고 모든 하위 애플리케이션 컨텍스트에서 사용하려고 할 수 있습니다. 디스패처 서블릿으로 작성된 애플리케이션 컨텍스트).

이 리스너의 두 번째 사용은 스프링 보안을 사용하려는 경우입니다.


3

루트 및 하위 컨텍스트 추가 정보를 읽기 전에 다음 사항을 이해하십시오.

Spring은 한 번에 여러 컨텍스트를 가질 수 있습니다. 그중 하나는 루트 컨텍스트이고 다른 모든 컨텍스트는 하위 컨텍스트입니다.

모든 하위 컨텍스트는 루트 컨텍스트에 정의 된 Bean에 액세스 할 수 있습니다. 그러나 반대는 사실이 아닙니다. 루트 컨텍스트는 하위 컨텍스트 Bean에 액세스 할 수 없습니다.

ApplicationContext :

applicationContext.xml은 모든 웹 응용 프로그램의 루트 컨텍스트 구성입니다. Spring은 applicationContext.xml 파일을로드하고 전체 애플리케이션에 대한 ApplicationContext를 생성한다. 웹 애플리케이션 당 하나의 애플리케이션 컨텍스트 만 있습니다. contextConfigLocation 매개 변수를 사용하여 web.xml에서 컨텍스트 구성 파일 이름을 명시 적으로 선언하지 않으면 Spring은 WEB-INF 폴더에서 applicationContext.xml을 검색하고이 파일을 찾을 수 없으면 FileNotFoundException을 발생시킵니다.

ContextLoaderListener 루트 응용 프로그램 컨텍스트에 대한 실제 초기화 작업을 수행합니다. "contextConfigLocation"컨텍스트 매개 변수를 읽고 값을 컨텍스트 인스턴스에 전달하여 값을 여러 개의 파일 경로로 구문 분석하여 여러 개의 쉼표와 공백으로 구분할 수있는 여러 파일 경로로 구문 분석합니다 (예 : "WEB-INF / applicationContext1.xml, WEB-INF / applicationContext2.xml”을 참조하십시오. ContextLoaderListener는 선택 사항입니다. 여기서 요점을 밝히기 위해 : DispatcherServlet을 사용하는 기본 최소 web.xml 인 ContextLoaderListener를 구성하지 않고도 Spring 응용 프로그램을 부팅 할 수 있습니다.

DispatcherServlet DispatcherServlet은 기본적으로 구성된 URL 패턴과 일치하는 수신 웹 요청을 처리하는 데 사용되는 서블릿 (HttpServlet을 확장)입니다. 들어오는 URI를 가져와 컨트롤러와 뷰의 올바른 조합을 찾습니다. 전면 컨트롤러입니다.

스프링 구성에서 DispatcherServlet을 정의 할 때 contextConfigLocation 속성을 사용하여 컨트롤러 클래스, 뷰 맵핑 등의 항목이있는 XML 파일을 제공합니다.

WebApplicationContext ApplicationContext 외에도 단일 웹 응용 프로그램에 여러 WebApplicationContext가있을 수 있습니다. 간단히 말해, 각 DispatcherServlet은 단일 WebApplicationContext와 연관되어 있습니다. xxx-servlet.xml 파일은 DispatcherServlet에 고유하며 웹 애플리케이션은 요청을 처리하도록 구성된 둘 이상의 DispatcherServlet을 가질 수 있습니다. 이러한 시나리오에서 각 DispatcherServlet에는 별도의 xxx-servlet.xml이 구성되어 있습니다. 그러나 applicationContext.xml은 모든 서블릿 구성 파일에 공통입니다. Spring은 기본적으로 webapps WEB-INF 폴더에서“xxx-servlet.xml”이라는 파일을로드합니다. 여기서 xxx는 web.xml의 서블릿 이름입니다. 해당 파일 이름의 이름을 변경하거나 위치를 변경하려면 contextConfigLocation을 사용하여 initi-param을 param 이름으로 추가하십시오.

그들 사이의 비교와 관계 :

ContextLoaderListener 및 DispatcherServlet

ContextLoaderListener는 루트 애플리케이션 컨텍스트를 작성합니다. DispatcherServlet 항목은 서블릿 항목 당 하나의 하위 애플리케이션 컨텍스트를 작성합니다. 자식 컨텍스트는 루트 컨텍스트에 정의 된 Bean에 액세스 할 수 있습니다. 루트 컨텍스트의 Bean은 하위 컨텍스트의 Bean에 직접 액세스 할 수 없습니다. 모든 컨텍스트가 ServletContext에 추가됩니다. WebApplicationContextUtils 클래스를 사용하여 루트 컨텍스트에 액세스 할 수 있습니다.

Spring 문서를 읽은 후 다음을 이해합니다.

a) 응용 문맥은 계층 적이며 WebApplicationContext도 마찬가지입니다. 여기에서 설명서를 참조하십시오.

b) ContextLoaderListener는 웹 애플리케이션에 대한 루트 웹 애플리케이션 컨텍스트를 작성하고이를 ServletContext에 넣습니다. 이 컨텍스트는 컨트롤러 계층 (Struts 또는 Spring MVC)에서 어떤 기술이 사용되고 있는지에 관계없이 스프링 관리 Bean을로드 및 언로드하는 데 사용할 수 있습니다.

c) DispatcherServlet은 자체 WebApplicationContext를 작성하고 핸들러 / 컨트롤러 / 뷰 리졸버는이 컨텍스트에 의해 관리됩니다.

d) ContextLoaderListener가 DispatcherServlet과 함께 사용되는 경우, 루트 웹 애플리케이션 컨텍스트가 앞서 언급 된대로 먼저 작성되고 하위 컨텍스트도 DispatcherSerlvet에 의해 작성되며 루트 애플리케이션 컨텍스트에 첨부됩니다. 여기에서 설명서를 참조하십시오.

Spring MVC와 함께 작업하고 서비스 계층에서 Spring을 사용하는 경우 두 가지 애플리케이션 컨텍스트를 제공합니다. 첫 번째는 ContextLoaderListener를 사용하여 구성하고 다른 하나는 DispatcherServlet을 사용하여 구성합니다.

일반적으로 DispatcherServlet 컨텍스트에서 모든 MVC 관련 Bean (컨트롤러 및보기 등)과 ContextLoaderListener에 의해 루트 컨텍스트에서 보안, 트랜잭션, 서비스 등과 같은 모든 교차 절단 Bean을 정의합니다.

자세한 내용은 https://siddharthnawani.blogspot.com/2019/10/contextloaderlistener-vs.html 을 참조하십시오.


2

기본적으로 ContextLoaderListner를 사용하여 루트 애플리케이션 컨텍스트와 웹 애플리케이션 컨텍스트를 분리 할 수 ​​있습니다.

컨텍스트 매개 변수로 맵핑 된 구성 파일은 루트 애플리케이션 컨텍스트 구성으로 작동합니다. 디스패처 서블릿으로 매핑 된 구성 파일은 웹 응용 프로그램 컨텍스트처럼 작동합니다.

모든 웹 애플리케이션에는 여러 디스패처 서블릿이 있으므로 여러 웹 애플리케이션 컨텍스트가 있습니다.

그러나 모든 웹 응용 프로그램에는 모든 웹 응용 프로그램 컨텍스트와 공유되는 루트 응용 프로그램 컨텍스트가 하나만있을 수 있습니다.

우리는 공통 서비스, 엔티티, 측면 등을 루트 애플리케이션 컨텍스트에서 정의해야합니다. 컨트롤러, 인터셉터 등은 관련 웹 애플리케이션 컨텍스트에 있습니다.

샘플 web.xml은

<!-- language: xml -->
<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>example.config.AppConfig</param-value>
    </context-param>
    <servlet>
        <servlet-name>restEntryPoint</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>example.config.RestConfig</param-value>
        </init-param>       
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>restEntryPoint</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>webEntryPoint</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>example.config.WebConfig</param-value>
        </init-param>       
        <load-on-startup>1</load-on-startup>
    </servlet>  
    <servlet-mapping>
        <servlet-name>webEntryPoint</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app> 

여기서 config class example.config.AppConfig는 다른 모든 웹 응용 프로그램 컨텍스트와 공유 될 루트 응용 프로그램 컨텍스트에서 서비스, 엔터티, 측면 등을 구성하는 데 사용할 수 있습니다 (예 : 여기에 우리는 두 개의 웹 응용 프로그램 컨텍스트 구성 클래스 RestConfig 및 WebConfig가 있음)

추신 : ContextLoaderListener는 완전히 선택 사항입니다. 여기서 web.xml에 ContextLoaderListener를 언급하지 않으면 AppConfig가 작동하지 않습니다. 이 경우 WebConfig 및 Rest Config에서 모든 서비스와 엔티티를 구성해야합니다.


1

웹 응용 프로그램 배포 시간에 실행하려는 코드를 넣는 후크 포인트를 제공합니다.


Jigar, 실제로 이것이 내가 찾으려고하는 것입니다. 배포시 기본 컨텍스트 로더 클래스가 제공하는 기능은 무엇입니까?
M Sach

서버를 다시 시작하지 않고 속성 / xml 파일을 변경하고 런타임에 다시로드 할 수 있도록
vsingh

1

리스너 클래스-이벤트를 청취합니다 (예 : 서버 시작 / 종료)

ContextLoaderListener-

  1. 서버 시작 / 종료 중 청취
  2. Spring 구성 파일을 입력으로 가져와 구성에 따라 Bean을 작성하고 준비하십시오 (종료 중에 Bean을 파괴 함)
  3. web.xml에서 이와 같이 구성 파일을 제공 할 수 있습니다.

    <param-name>contextConfigLocation</param-name>  
    <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>  

1

스프링 프레임 워크의 맥락에서 ContextLoaderListener의 목적은 애플리케이션의 백엔드를 구동하는 미들 티어 및 데이터 티어 컴포넌트와 같은 애플리케이션에 다른 Bean을로드하는 것입니다.


0

이해가 정확합니다. ContextLoaderListener에서 왜 이점이 보이지 않는지 궁금합니다. 예를 들어, 데이터베이스를 관리하기 위해 세션 팩토리를 빌드해야합니다. 이 작업은 다소 시간이 걸릴 수 있으므로 시작시 작업을 수행하는 것이 좋습니다. 물론 당신은 init 서블릿이나 다른 것들로 그것을 할 수 있지만 Spring의 접근 방식의 장점은 코드를 작성하지 않고 구성하는 것입니다.


0

ContextLoaderListener없이 web.xml을 작성하면 스프링 보안에서 customAuthenticationProvider를 사용하여 의미를 줄 수 없습니다. DispatcherServelet은 ContextLoaderListener의 하위 컨텍스트이므로 customAuthenticationProvider는 ContextLoaderListener 인 parentContext의 일부입니다. 따라서 부모 컨텍스트는 자식 컨텍스트의 종속성을 가질 수 없습니다. 따라서 spring-context.xml을 initparam에 쓰는 대신 contextparam에 쓰는 것이 가장 좋습니다.


0

예를 들어 하나 이상의 구성 파일을 원하거나 applicationcontext.xml 대신 xyz.xml 파일 이있는 경우 실제 사용이 가능 합니다.

<context-param><param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/training-service.xml, /WEB-INF/training-data.xml</param-value> </context-param>

ContextLoaderListener에 대한 또 다른 접근법은 아래와 같이 ContextLoaderServlet을 사용하는 것입니다.

<servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.