2. Spring Mobile Device Module

2.1 Introduction

디바이스 감지는 데스크탑 브라우저와 모바일 디바이스에 의한 요청이 다르게 처리 될 때 유용합니다. 스프링 모바일 디바이스모듈은 서버사이드 디바이스 감지 지원을 제공합니다. 이 지원은 디바이스 해석 프레임워크, 사이트 우선권 매니저, 사이트 스위처, 뷰 관리로 구성되어있습니다.

Device detection is useful when requests by mobile devices need to be handled differently from requests made by desktop browsers. The Spring Mobile Device module provides support for server-side device detection. This support consists of a device resolution framework, site preference management, site switcher, and view management.

2.2 How to get

모듈을 얻기 위해 스프링 모바일 디바이스 아티팩트를 클래스 패스에 추가하세요 :

To get the module, add the spring-mobile-device artifact to your classpath:

<dependency>
    <groupId>org.springframework.mobile</groupId>
    <artifactId>spring-mobile-device</artifactId>
    <version>${org.springframework.mobile-version}</version>
</dependency>
            

이 리파지토리에서 릴리즈 버젼이 사용가능합니다.

Release versions are available from the following repository:

<repository>
    <id>spring-repo</id>
    <name>Spring Repository</name>
    <url>http://repo.spring.io/release</url>
</repository>
            

만약 당신이 릴리즈 후보나 마일스톤에 맞추지 않고 개발한다면 다음 artifact를 해석하기 위해 다음 리파지토리를 추가할 필요가 있을 것입니다.

If you are developing against a milestone or release candidate, you will need to add the following repository in order to resolve the artifact:

<repository>
    <id>spring-milestone</id>
    <name>Spring Milestone Repository</name>
    <url>http://repo.spring.io/milestone</url>
</repository>
            

만약 당신이 최근의 스냅샷 빌드버젼을 테스팅한다면, 당신은 다음의 리파지토리를 추가해야할 것입니다.

If you are testing the latest snapshot build version, you will need to add the following repository:

<repository>
    <id>spring-snapshot</id>
    <name>Spring Snapshot Repository</name>
    <url>http://repo.spring.io/snapshot</url>
</repository>
            

2.3 디바이스 해석

2.3 Device resolution

디바이스 해석은 어떤 기기에서 요청이 들어왔는지 결정하기 위해 HTTP요청을 검사합니다. 이것은 일반적으로 User-Agent 헤더와 다른 요청헤더를 분석함으로써 이뤄집니다.

기초적인 레벨에서 디바이스 해석은 이 질문에 대답을 합니다 "클라이언트가 모바일 혹은 타블렛을 사용하는가?". 이 대답은 당신의 어플리케이션이 작은 화면을 가지는 모바일 디바이스나 터치 인터페이스를 가지는 태블릿 디바이스에 대해 다르게 응답할 수 있게 해줍니다. 좀 더 복잡한 디바이스 해석기가 또한 좀 더 구체적인 스크린 사이즈, 제조업체, 모델, 선호된 마크업같은 디바이스 특징을 식별할 수 있습니다.

스프링 모바일에서는 DeviceResolver 인터페이스가 디바이스 해석을 위한 인터페이스를 정의합니다. :

Device resolution is the process of introspecting an HTTP request to determine the device that originated the request. It is typically achieved by analyzing the User-Agent header and other request headers.

At the most basic level, device resolution answers the question: "Is the client using a mobile or tablet device?". This answer enables your application to respond differently to mobile devices that have small screens, or tablet devices that have a touch interface. More sophisticated device resolvers are also capable of identifying specific device capabilities, such as screen size, manufacturer, model, or preferred markup.

In Spring Mobile, the DeviceResolver interface defines the API for device resolution:

public interface DeviceResolver {

    Device resolveDevice(HttpServletRequest request);
    
}
            

반환된 Device 모듈이 디바이스 해석의 결과를 모델링합니다. :

The returned Device models the result of device resolution:

public interface Device {
    
    /**
     * True if this device is not a mobile or tablet device.
     */
    boolean isNormal();

    /**
     * True if this device is a mobile device such as an Apple iPhone or an 
     * Android Nexus One. Could be used by a pre-handle interceptor to redirect 
     * the user to a dedicated mobile web site. Could be used to apply a 
     * different page layout or stylesheet when the device is a mobile device.
     */
    boolean isMobile();
    
    /**
     * True if this device is a tablet device such as an Apple iPad or a 
     * Motorola Xoom. Could be used by a pre-handle interceptor to redirect 
     * the user to a dedicated tablet web site. Could be used to apply a 
     * different page layout or stylesheet when the device is a tablet device.
     */
    boolean isTablet();

    /**
     *
     * @return resolved DevicePlatform
     */
    DevicePlatform getDevicePlatform();

}
            

위에서 보신 대로, Device.isMobile() 은 클라이언트가 스마트폰같은 모바일기기인지 검사하는 데 사용될 수 있습니다. 비슷하게 Device.isTablet() 은 클라이언트가 태블릿인지 검사하는 데 사용될 수 있습니다. 추가적으로 디바이스 플랫폼은 Device.getDevicePlatform() 프로퍼티를 검사함으로써 결정될 수 있습니다. 사용하고 있는 DeviceResolver에 따라서, Device 는 추가적은 프로퍼티 정보를 갖지 않을 수 있습니다.

As shown above, Device.isMobile() can be used to determine if the client is using a mobile device, such as a smart phone. Similarly, Device.isTablet() can be used to determine if the client is running on a tablet device. Additionally, the device platform may be determined by inspecting the Device.getDevicePlatform() property. Depending on the DeviceResolver in use, a Device may support being downcast to access additional properties.

2.3.1 When to perform

웹어플리케이션은 요청처리를 시작할 때, 어떤 요청핸들러가 발동되기 전에, 디바이스 해석을 수행해야 합니다. 이것은 어떤 처리가 발생하기 전에 리퀘스트scope에서 Device 모델이 만들어진다는 것을 보장합니다. 리퀘스트 핸들러는 그 후에 Device 인스턴스를 얻을 수 있고, 요청 상태에 따라 다르게 응답할 수 있게 하는 데 그 인스턴스를 사용합니다..

Web applications should perform device resolution at the beginning of request processing, before any request handler is invoked. This ensures the Device model can be made available in request scope before any processing occurs. Request handlers can then obtain the Device instance and use it to respond differently based on its state.

기본적으로는, LiteDeviceResolver 가 디바이스 해석을 위해 사용됩니다. 당신은 또한 생성자 아규먼트에 주입을 하여서 다른 DeviceResolver구현체를 플러그인할 수 있습니다.

By default, a LiteDeviceResolver is used for device resolution. You may plug-in another DeviceResolver implementation by injecting a constructor argument.

DeviceResolverHandlerInterceptor

스프링 모바일은 HandlerInterceptor를 가지고 preHandle에서 DeviceResolver에게 위임을 합니다. 해석된 Device는 'currentDevice'라는 요청속성(reqeust attribute) 아래에 색인되어서 요청처리를 통해 디바이스가 핸들러에서 사용가능하게 해줍니다. .

Spring Mobile ships with a HandlerInterceptor that, on preHandle, delegates to a DeviceResolver. The resolved Device is indexed under a request attribute named 'currentDevice', making it available to handlers throughout request processing.

활성화 시키기 위해 DeviceResolverHandlerInterceptorDispatcherServlet 설정에 정의된 인터셉터 목록에 추가하겠습니다.

To enable, add the DeviceResolverHandlerInterceptor to the list of interceptors defined in your DispatcherServlet configuration:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
</interceptors>
                    

아니면, 당신은 자가기반의 콘테이너 설정을 사용하는 DeviceResolverHandlerInterceptor 를 추가할 수 있습니다. :

Alternatively, you can add the DeviceResolverHandlerInterceptor using Spring's Java-based container configuration:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
}
                    

DeviceResolverRequestFilter

DeviceResolverHandlerInterceptor에 대한 대안으로 , 스프링 모바일은 또한 서블릿필터를 가지고서 DeviceResolver에게 위임할 수 있습니다. 해석된 Device는 'currentDevice'라는 요청속성(reqeust attribute) 아래에 색인되어서 요청처리를 통해 디바이스가 핸들러에서 사용가능하게 해줍니다.

이것을 활성화하기 위해 DeviceResolverRequestFilter를 당신의 web.xml 에 추가하겠습니다. :

As an alternative to the DeviceResolverHandlerInterceptor, Spring Mobile also ships with a Servlet Filter that delegates to a DeviceResolver. As with the HandlerInterceptor, the resolved Device is indexed under a request attribute named 'currentDevice', making it available to handlers throughout request processing.

To enable, add the DeviceResolverRequestFilter to your web.xml:

<filter>
  <filter-name>deviceResolverRequestFilter</filter-name>
  <filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>
                    

2.3.2 Obtaining a reference to the current device

당신의 코드에서 현재 Device 를 찾을 필요가 있을 때, 당신은 몇 가지 방법으로 이것을 할 수가 있습니다. 만약 당신이 이미 ServletRequest 나 스프링의 WebRequest에 대한 참조를 가지고 있다면 간단하게 DeviceUtils를 사용하세요 :

When you need to lookup the current Device in your code, you can do so in several ways. If you already have a reference to a ServletRequest or Spring WebRequest, simply use DeviceUtils:

Device currentDevice = DeviceUtils.getCurrentDevice(servletRequest);
                

만약 당신이 현재 Device를 컨트롤러 메소드의 아규먼트로 전달하고 싶다면, DeviceWebArgumentResolver를 설정하세요 :

If you'd like to pass the current Device as an argument to one of your @Controller methods, configure a DeviceWebArgumentResolver:

<annotation-driven>
  <argument-resolvers>
    <bean class="org.springframework.mobile.device.DeviceWebArgumentResolver" />
  </argument-resolvers>
</annotation-driven>
                

당신은 또한 자바 설정을 사용해서 DeviceHandlerMethodArgumentResolver 를 설정할 수 있습니다.

You can alternatively configure a DeviceHandlerMethodArgumentResolver using Java-based configuration:

@Bean
public DeviceHandlerMethodArgumentResolver deviceHandlerMethodArgumentResolver() {
    return new DeviceHandlerMethodArgumentResolver();
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(deviceHandlerMethodArgumentResolver());
}
                

당신은 또한 Device 를 당신의 @Controller에 다음과 같이 주입할 수 있습니다.

You can then inject the Device into your @Controllers as shown below:

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping("/")
    public void home(Device device) {
        if (device.isMobile()) {
            logger.info("Hello mobile user!");
        } else if (device.isTablet()) {
            logger.info("Hello tablet user!");
        } else {
            logger.info("Hello desktop user!");         
        }
    }

}
                

2.3.3 지원되는 DeviceResolver 구현체

2.3.3 Supported DeviceResolver implementations

스프링 모바일은 다른 디바이스 리졸버 구현체들의 개발을 가능하게 해줘서 다양한 레벨의 해석가능성을 제공합니다. 첫번째로 기본적으로 LiteDeciceResolver는 모바일디바이스의 존재를 감지하지만, 구체적인 특징은 감지하지 못합니다..

Spring Mobile allows for the development of different DeviceResolver implementations that offer varying levels of resolution capability. The first, and the default, is a LiteDeviceResolver that detects the presence of a mobile device but does not detect specific capabilities.

LiteDeviceResolver

기본적인 DeviceResolver 구현체는 "lite" 감지 알고리즘에 기반을 두고 있으며 이것은 Wordpress Mobile Pack 의 일부로 구현되었습니다.. 이 해석기는 오직 모바일이나 태블릿 디바이스의 존재를 감지하고 구체적인 특징은 감지하지 못합니다. 이 해석기를 활성화하기 위해 어떠한 특별한 설정도 필요하지 않습니다. 단순히 기본적인 DeviceResolverHandlerInteceptor를 설정하면 이것은 당신을 위해 활성화될 것입니다..

The default DeviceResolver implementation is based on the "lite" detection algorithm implemented as part of the Wordpress Mobile Pack . This resolver only detects the presence of a mobile or tablet device, and does not detect specific capabilities. No special configuration is required to enable this resolver, simply configure a default DeviceResolverHandlerInterceptor and it will be enabled for you.

LiteDeviceResolver 가 부적절하게 User-Agent를 모바일 디바이스로 읽는 경우가 생길 수 있습니다. LiteDeviceResolver는 "보통"기기를 해석하기 위한 User-agent 키워드 목록을 세팅 할 수 있게 해주며, 효과적으로 기본 행동을 오버라이딩 할 수 있습니다. 이러한 키워드는 모바일이나 태블릿 디바이스 감지 키워드 이전에 우선권을 가지게 됩니다. 다음의 예제는 어떻게 보통 키워드를 DeviceResolverHandlerInterceptor의 설정에 생성자 아규먼트를 통해 설정하는지 보여줍니다. 이러한 경우 "iphone"이나 "android" 를 포함하는 User-Agenets는 더 이상 모바일 디바이스를 해석하지 않습니다. (맞나..?)

It is possible that the LiteDeviceResolver incorrectly identifies a User-Agent as a mobile device. The LiteDeviceResolver provides a configuration option for setting a list of User-Agent keywords that should resolve to a "normal" device, effectively overriding the default behavior. These keywords take precedence over the mobile and tablet device detection keywords. The following example illustrates how to set the normal keywords in the configuration of the DeviceResolverHandlerInterceptor by injecting a constructor argument. In this case, User-Agents that contain "iphone" and "android" would no longer resolve to a mobile device.

<interceptors>
  <!-- Detects the client's Device -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor">
    <constructor-arg>
      <bean class="org.springframework.mobile.device.LiteDeviceResolver">
        <constructor-arg>
          <list>
            <value>iphone</value>
            <value>android</value>
          </list>
        </constructor-arg>
      </bean>
    </constructor-arg>
  </bean>
</interceptors>
                    

자바설정을 통해서도 할 수 있습니다.

The same thing can be accomplished using Java-based configuration.

@Bean
public LiteDeviceResolver liteDeviceResolver() {
    List<String> keywords = new ArrayList<String>();
    keywords.add("iphone");
    keywords.add("android");
    return new LiteDeviceResolver(keywords);
}

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor(liteDeviceResolver());
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
}
                    

대안적으로, 당신은 LiteDeviceResolver를 상속하고 init()메소드를 오버라이딩하여 당신의 값을 넣을 수 있습니다.

Alternatively, you may subclass LiteDeviceResolver, and set the values by overriding the init() method.

public class CustomLiteDeviceResolver extends LiteDeviceResolver {

    @Override
    protected void init() {
        super.init();
        getNormalUserAgentKeywords().addAll(Arrays.asList(NORMAL_KEYWORDS));
    };

    private static final String[] NORMAL_KEYWORDS = new String[] { "iphone", "android" };

}
                    

2.4 Site preference management

디바이스 해석은 종종 어떤 사이트가 사용자에게 제공될 것인지 결정되는 데 사용됩니다. 예를 들자면, 모바일 유저가 작은 화면에 최적화된 내용을 포함하는 모바일 사이트를 제공받을 것이고, 아니면 데스크탑 유저가 평범한 사이트를 제공받을 것입니다. 여러 개의 사이트를 지원하는 것은 Device.isMobile()과, 이 값에 의해 많은 컨트롤러와 뷰 렌더링 로직에 기반하여 가능합니다. 이와같이 태블릿에 대한 지원또한 Device.isTablet()를 사용함으로써 가능합니다.

Device resolution is often used to determine which "site" will be served to the user. For example, a mobile user may be served a "mobile site" that contains content optimized for display on a small screen, while a desktop user would be served the "normal site". Support for multiple sites can be achieved by introspecting Device.isMobile() and varying controller and view rendering logic based on its value. Likewise, support for tablets is achieved by using Device.isTablet().

하지만, 어플리케이션이 여러 개의 사이트를 지원하면서 유저로 하여로 하여금 필요할 때 그 사이트들 간을 스위칭하게 하는 것은 좋은 사용경험으로 필요합니다. 예를 들면 모바일 유저가 현재 모바일 사이트를 보고 있지만, 보통 사이트를 대신 보고 할지도 모릅니다. 왜냐하면 몇몇 콘텐츠들은 모바일UI로 접근이 힘들기 때문입니다.

However, when an application supports multiple sites, allowing the user to switch between them, if desired, is considered a good usability practice. For example, a mobile user currently viewing the mobile site may wish to access the normal site instead, perhaps because some content he or she would like to access is not available through the mobile UI.

디바이스 해석 시스템에서 Building 하는 것은 이러한 종류의 유저 사이트 선호를 제공하는 것입니다. 이러한 기능은 사용자에게 평범한 사이트, 모바일, 태블릿 사이트를 선호하는 지 가리키게 해줍니다. 가리켜진 SitePreference는 그 후에 많은 컨트롤과 뷰렌더링 로직에 사용될 것입니다.

Building on the device resolution system is a facility for this kind of "user site preference management". This facility allows the user to indicate if he or she prefers the normal, mobile or tablet sites. The indicated SitePreference may then be used to vary control and view rendering logic.

SitePreferenceHandler 인터페이스는 선호 사이트 관리를 위한 핵심 서비스 API를 정의합니다.

The SitePreferenceHandler interface defines the core service API for site preference management:

public interface SitePreferenceHandler {

    /**
     * The name of the request attribute that holds the current user's site 
     * preference value.
     */
    final String CURRENT_SITE_PREFERENCE_ATTRIBUTE = "currentSitePreference";

    /**
     * Handle the site preference aspect of the web request.
     * Implementations should first check if the user has indicated a site 
     * preference. If so, the indicated site preference should be saved and 
     * remembered for future requests. If no site preference has been 
     * indicated, an implementation may derive a default site preference from 
     * the {@link Device} that originated the request. After handling, the 
     * user's site preference is returned and also available as a request 
     * attribute named 'currentSitePreference'.
     */
     SitePreference handleSitePreference(HttpServletRequest request, HttpServletResponse response);

}
            

해석된 선호사이트는 이늄밸류값입니다.

The resolved SitePreference is an enum value:

public enum SitePreference {
    
    /**
     * The user prefers the 'normal' site.
     */
    NORMAL {
        public boolean isNormal() {
            return true;
        }
    },
    
    /**
     * The user prefers the 'mobile' site.
     */
    MOBILE {        
        public boolean isMobile() {
            return true;
        }
    },
    
    /**
     * The user prefers the 'tablet' site.
     */
    TABLET {        
        public boolean isTablet() {
            return true;
        }
    };
    
    /**
     * Tests if this is the 'normal' SitePreference.
     * Designed to support concise SitePreference boolean expressions 
     * e.g. <c:if test="${currentSitePreference.normal}"></c:i>.
     */
    public boolean isNormal() {
        return (!isMobile() && !isTablet());
    }

    /**
     * Tests if this is the 'mobile' SitePreference.
     * Designed to support concise SitePreference boolean expressions 
     * e.g. <c:if test="${currentSitePreference.mobile}"></c:i>.
     */
    public boolean isMobile() {
        return false;
    }
    
    /**
     * Tests if this is the 'tablet' SitePreference.
     * Designed to support concise SitePreference boolean expressions 
     * e.g. <c:if test="${currentSitePreference.tablet}"></c:i>;.
     */
    public boolean isTablet() {
        return false;
    }
    
}
            

스프링 모바일은 StandardSitePreferenceHandler라 이름붙여진 단일 SitePreferenceHandler 구현체를 제공하며 이것은 대부분의 필요에 적합합니다. 이것은 쿼리 파라미터기반의 선호사이트표시, pluugable 한 SitePreference저장소 를 지원하며 스프링 MVC에서 HandlerInterceptor를 이용하여 활성화됩니다. 추가적으로 SitePreference가 명시되지 않았다면 기본값은 사용자의 기기에 의해 결정됩니다..

Spring Mobile provides a single SitePreferenceHandler implementation named StandardSitePreferenceHandler, which should be suitable for most needs. It supports query-parameter-based site preference indication, pluggable SitePreference storage, and may be enabled in a Spring MVC application using a HandlerIntercepor. In addition, if no SitePreference has been explcitly indicated by the user, a default will be derived based on the user's Device (MOBILE for mobile devices, TABLET for tablet devices, and NORMAL otherwise).

2.4.1 Indicating a site preference

사용자는 아마 다음의 선호 사이트쿼리파라미터를 전송하는 링크를 활성화하여서 선호하는 사이트를 지시할 것입니다.

The user may indicate a site preference by activating a link that submits the site_preference query parameter:

Site: <a href="${currentUrl}?site_preference=normal">Normal</a> | 
<a href="${currentUrl}?site_preference=mobile">Mobile</a>
                

사용자를 위해 지시된 선호 사이트는 SitePreferenceRepository 에 저장되고, currentSitePreference라 이름붙여진 요청 속성으로 사용가능하게 해집니다.

The indicated site preference is saved for the user in a SitePreferenceRepository, and made available as a request attribute named 'currentSitePreference'.

2.4.2 Site preference storage

가리켜진 선호사이트는 SitePreferenceRepository에 저장될 수 있으며, 유저에 의해 만들어진 다음의 요청에서 기억될 것입니다. CookieSitePreferenceRepository 는 기본 구현체이고 사용자의 선호를 클라이언트 사이트 쿠키에 저장합니다.

Indicated site preferences are stored in a SitePreferenceRepository so they are remembered in future requests made by the user. CookieSitePreferenceRepository is the default implementation and stores the user's' preference in a client-side cookie.

public interface SitePreferenceRepository {

    /**
     * Load the user's site preference.
     * Returns null if the user has not specified a preference.
     */
    SitePreference loadSitePreference(HttpServletRequest request);
    
    /**
     * Save the user's site preference.
     */
    void saveSitePreference(SitePreference preference, HttpServletRequest request, HttpServletResponse response);

}
                

2.4.3 Enabling site preference management

요청이 처리 되기 전에 SitePreference 관리를 활성화하기 위해, SitePreferenceHandlerInterceptor를 당신의 DispatcherServlet 에 추가해줍시다.

To enable SitePreference management before requests are processed, add the SitePreferenceHandlerInterceptor to your DispatcherServlet configuration:

<interceptors>
  <!-- On pre-handle, manage the user's site preference (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor" />
</interceptors>
            

자바 설정 또한 가능합니다.

Java-based configuration is also available:

@Bean
public SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor() {
    return new SitePreferenceHandlerInterceptor();
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(sitePreferenceHandlerInterceptor());
}
            

기본적으로, 인터셉터는 CookieSitePreferenceRepository와 함께 설정된 StandardSitePreferenceHandler에게 위임할 것입니다. 당신은 생성자 아규먼트를 주입하여 다른 SitePreferenceHandler를 플러그인 할 수가 있을 것입니다. 인터셉터가 동작한 이후에 SitePreferece는 currentSitePreference라는 이름의 리퀘스트 속성으로 사용가능해질 것입니다.

By default, the interceptor will delegate to a StandardSitePreferenceHandler configured with a CookieSitePreferenceRepository. You may plug-in another SitePreferenceHandler by injecting a constructor argument. After the interceptor is invoked, the SitePreference will be available as a request attribute named 'currentSitePreference'.

2.4.4 Obtaining a reference to the current site preference

당신의 코드에서 현재 SitePreference 를 찾을 필요가 있을 때, 당신은 몇 가지 방법으로 이것을 할 수가 있습니다. 만약 당신이 이미 ServletRequest 나 스프링의 WebRequest에 대한 참조를 가지고 있다면 간단하게 SitePreferenceUtils를 사용하세요 :

When you need to lookup the current SitePreference in your code, you can do so in several ways. If you already have a reference to a ServletRequest or Spring WebRequest, simply use SitePreferenceUtils:

SitePreference sitePreference = SitePreferenceUtils.getCurrentSitePreference(servletRequest);
            

만약 당신이 현재 SitePreference@Controller 메소드들 중의 하나에 대한 아규먼트로 전달하고 싶다면, SitePreferenceWebArgumentResolver를 설정하세요.

If you'd like to pass the current SitePreference as an argument to one of your @Controller methods, configure a SitePreferenceWebArgumentResolver:

<annotation-driven>
  <argument-resolvers>
    <bean class="org.springframework.mobile.device.site.SitePreferenceWebArgumentResolver" />
  </argument-resolvers>
</annotation-driven>
                

자바 설정 또한 가능합니다

Java-based configuration is also available:

@Bean
public SitePreferenceHandlerMethodArgumentResolver sitePreferenceHandlerMethodArgumentResolver() {
    return new SitePreferenceHandlerMethodArgumentResolver();
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 
    argumentResolvers.add(sitePreferenceHandlerMethodArgumentResolver());
}
                

당신은 그러면 지시된 SitePreference를 당신의 @Controller 에 다음과 같이 주입할 수 있습니다.

You can then inject the indicated SitePreference into your @Controller as shown below:

@Controller
public class HomeController {

    @RequestMapping("/")
    public String home(SitePreference sitePreference, Model model) {
        if (sitePreference == SitePreference.NORMAL) {
            logger.info("Site preference is normal");
            return "home";
        } else if (sitePreference == SitePreference.MOBILE) {
            logger.info("Site preference is mobile");
            return "home-mobile";
        } else if (sitePreference == SitePreference.TABLET) {
            logger.info("Site preference is tablet");
            return "home-tablet";
        } else {
            logger.info("no site preference");
            return "home";
        }
    }

}
                

2.5 Site switching

몇몇 어플리케이션은 그들의 모바일 사이트를 보통의 사이트와는 다른 다른 도메인에서 운영하고 싶을 것입니다. 예를 들자면 구글에 모바일 기기로 접근하면 m.google.com 로 접속되는 것같이 말입니다.

Some applications may wish to host their "mobile site" at a different domain from their "normal site". For example, Google will switch you to m.google.com if you access google.com from your mobile phone.

스프링 모바일에서는 당신은 아마 SiteSwitcherHandlerInterceptor를 써서 모바일 유저를 모바일 사이트로 리다이렉트 시킬 것입니다. 사용자는 또한 아마 사이트 선호를 지시할 수 있을 것입니다. 예를 들자면, 모바일 유저가 여전히 평범한 사이트를 원하는 경우 같이 말입니다. 표준 사이트 스위칭 관례를 구현하는 편리한 정적 팩토리 메서드가 제공됩니다.

In Spring Mobile, you may use the SiteSwitcherHandlerInterceptor to redirect mobile users to a dedicated mobile site. Users may also indicate a site preference; for example, a mobile user may still wish to use the 'normal' site. Convenient static factory methods are provided that implement standard site switching conventions.

mDot, dotMobiurlPath 팩토리 메서드들은 쿠키 기반의 SitePreference 저장소를 설정합니다. 쿠키값은 모바일과 일반 웹사이트간에 공유될 것입니다. 내부적으로 인터셉터가 SitePreferenceHandler에게 위임을 하고 스위처를 쓸 때 SitePreferenceHandlerInterceptor를 등록할 필요가 없게 됩니다.

The mDot, dotMobi and urlPath factory methods configure cookie-based SitePreference storage. The cookie value will be shared across the mobile and normal site domains. Internally, the interceptor delegates to a SitePreferenceHandler, so there is no need to register a SitePreferenceHandlerInterceptor when using the switcher.

2.5.1 mDot SiteSwitcher

mDot 팩토리 메서드를 사이트 스위처를 생성하기 위해 사용합니다. 사이트 스위처는 모바일 유저를 m.${serverName}에 리다이렉트 시킵니다. 예를 들면 m.myapp.com같이 말입니다.

Use the mDot factory method to construct a SiteSwitcher that redirects mobile users to m.${serverName}; for example, m.myapp.com:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "m.myapp.com" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="mDot">
    <constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
  </bean>
</interceptors>
                

기본적으로는 태블릿 디바이스는 기본 사이트를 보게 됩니다. 두번째 생성자 아규먼트는 테이블 디바이스를 모바일 사이트로 리다이렉트하게 해주는 데 사용될 수 있습니다.

By default, tablet devices see the 'normal' site. A second constructor argument is available for specifying that tablet devices are redirected to the 'mobile' site:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "m.myapp.com" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="mDot">
    <constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
    <constructor-arg index="1" type="java.lang.Boolean" value="true"/>
  </bean>
</interceptors>
                

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.mDot("myapp.com", true);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                

2.5.2 dotMobi SiteSwitcher

dotMobi 팩터리 메서드를 사용해서 사이트스위처를 생성하여, 모바일 유저를 ${serverName - lastDomain}.mobi; 로 리다이렉트 시키세요. 예를 들자면 myapp.mobi같은 주소가 되게 됩니다.

Use the dotMobi factory method to construct a SiteSwitcher that redirects mobile users to ${serverName - lastDomain}.mobi; for example, myapp.mobi:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "myapp.mobi" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="dotMobi">
    <constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
  </bean>
</interceptors>
                

이전에 mDot 팩터리 메서드에서 소개된 것과 마찬가지로, 태블릿 디바이스는 기본 사이트를 봅니다. 두번째 아규먼트가 테블릿 디바이스가 모바일 사이트로 가게 할 수 가 있습니다.

As described earlier with the mDot factory method, tablet devices see the 'normal' site. A second constructor argument is available for specifying that tablet devices are redirected to the 'mobile' site:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "myapp.mobi" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="dotMobi">
    <constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
    <constructor-arg index="1" type="java.lang.Boolean" value="true"/>
  </bean>
</interceptors>
                

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.dotMobi("myapp.com", true);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                

2.5.3 Standard SiteSwitcher

mDot 이나 dotMobi전략을 위한 더 많은 커스터마이징 설정을 위해서 standard 팩터리 메서드가 사용가능합니다. 모바일, 태블릿 사용자들을 특별한 스키마로 이동시키는 사이트스위처를 생성해보세요.

For a more customized configuration of the mDot or dotMobi strategies, the standard factory method is available. Construct a SiteSwitcher that redirects mobile and tablet users to a specified schema:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "m.myapp.com" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="standard">
    <constructor-arg value="app.com"/>
    <constructor-arg value="mobile.app.com"/>
    <constructor-arg value="tablet.app.com"/>
    <constructor-arg value=".app.com"/>
  </bean>
</interceptors>
                

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.standard("app.com", 
        "mobile.app.com", "tablet.app.com", ".app.com");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                

2.5.4 urlPath SiteSwitcher

urlPath 팩터리 메서드를 사용해서 사이트 스위처를 생성하여, 모바일유저를 어플리케이션 내의 다른 경로로 리다이렉트 시키세요. mDotdotMobi와는 달리 이 사이트스위처는 모바일 사이트를 위한 다른 DNS엔트리를 설정할 필요가 없습니다.

Use the urlPath factory method to construct a SiteSwitcher that redirects mobile users to a different path within the application. Unlike mDot and dotMobi, this SiteSwitcher does not require setting up a different DNS entry for a mobile site.

Mobile Path

urlPath 팩토리 메소드를 사용하여 사이트스위처를 생성하여, 모바일 유저가 ${serverName}/${mobilePath}로 이동하게 하세요. 예를 들면 myapp.com/m/와 같은 경로가 됩니다.

Use the urlPath factory method to construct a SiteSwitcher that redirects mobile users to ${serverName}/${mobilePath}; for example, myapp.com/m/:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "myapp.com/m" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="urlPath">
    <constructor-arg index="0" type="java.lang.String" value="/m" />
  </bean>
</interceptors>
                    

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.urlPath("/m");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                    

Mobile Path and Root Path

당신은 또한 urlPath에서 어플리케이션의 root path를 명시할 수도 있습니다. 다음의 샘플은 사이트스위처를 생성하여, 모바일 유저를 ${serverName}/${rootPath}/${mobilePath};로 이동하게 해줍니다. 예를 들자면 myapp.com/showcase/m/가 되는 것입니다.

You can also specify the root path of the application in the urlPath factory method. The following sample constructs a SiteSwitcher that redirects mobile users to ${serverName}/${rootPath}/${mobilePath}; for example, myapp.com/showcase/m/:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "myapp.com/showcase/m" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="urlPath">
    <constructor-arg index="0" type="java.lang.String" value="/m" />
    <constructor-arg index="1" type="java.lang.String" value="/showcase" />
  </bean>
</interceptors>
                    

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.urlPath("/m", "/showcase");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                    

Mobile Path, Tablet Path, and Root Path

마지막으로, urlPath 팩터리 메소드는 태블릿 사이트를 위한 경로를 설정하는 것을 지원합니다. 다음의 예즈는 사이트 스위처를 생성하여 모바일 유저를 ${serverName}/${rootPath}/${mobilePath}로 이동하게 해주고, 태블릿 사용자는 ${serverName}/${rootPath}/${tabletPath} 로 이동하게 해줍니다..

Lastly, the urlPath factory method supports configuring a path for a tablet site. The following sample constructs a SiteSwitcher that redirects mobile users to ${serverName}/${rootPath}/${mobilePath} for mobile sites, and ${serverName}/${rootPath}/${tabletPath} for tablet sites.

다음의 설정 예제에서, 모바일 사이트는 myapp.com/showcase/m/에 위치하게 되며, 태블릿 사이트는 이와 비슷하게 myapp.com/showcase/t/가 됩니다.

In the following configuration example, the mobile site would be located at myapp.com/showcase/m/, while the tablet site would be similarly located at myapp.com/showcase/t/:

<interceptors>
  <!-- On pre-handle, resolve the device that originated the web request -->
  <bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
  <!-- On pre-handle, redirects mobile users to "myapp/showcase/m" (declare after DeviceResolverHandlerInterceptor) -->
  <bean class="org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" 
      factory-method="urlPath">
    <constructor-arg index="0" type="java.lang.String" value="/m" />
    <constructor-arg index="1" type="java.lang.String" value="/t" />
    <constructor-arg index="2" type="java.lang.String" value="/showcase" />
  </bean>
</interceptors>
                    

Java-based configuration is also available:

@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor() {
    return new DeviceResolverHandlerInterceptor();
}

@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
    return SiteSwitcherHandlerInterceptor.urlPath("/m", "/t", "/showcase");
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(deviceResolverHandlerInterceptor());
    registry.addInterceptor(siteSwitcherHandlerInterceptor());
}
                    

Additional Configuration

urlPath 사이트 스위처가 잘 동작하게 하기 위해서 당신은 모바일과 태블릿 사이트 경로를 위한, 그에 상응하는 url 패턴을 web.xml 에 추가하여야 합니다.

Please note that in order for the urlPath SiteSwitcher to work properly, you will need to add a corresponding url pattern to your web.xml for the mobile and tablet site paths.

<servlet-mapping>
  <servlet-name>appServlet</servlet-name>
  <url-pattern>/</url-pattern>
  <url-pattern>/m/*</url-pattern>
  <url-pattern>/t/*</url-pattern>
</servlet-mapping>
                

더 많은 조정을 위해서는 SiteSwitcherHandlerInterceptor 자바문서를 보세요. 실행가능한 사이트 스위처 예제를 위해서는 samples예제를 보세요.

See the JavaDoc of SiteSwitcherHandlerInterceptor for additional options when you need more control. See the spring-mobile samples repository for runnable SiteSwitcher examples.

2.6 Device aware view management

디바이스 감지를 사용하면서, 당신의 컨트롤러들에 조건적인 로직을 넣어서, 디바이스 타입에 맞는 구체적인 뷰들을 리턴하는 것이 가능합니다. 그러나 많은 뷰들을 다룰 때, 이러한 작업은 힘든 일이 될 수 있습니다. 다행스럽게도 스프링 모바일은 다른 디바이스 타입에 따른 뷰들을 관리하는 대안메소드를 제공합니다.

Using device detection, it is possible to add conditional logic within your controllers to return specific views based on device type. But this process can be laborious if you are dealing with a large number of views. Fortunately, Spring Mobile offers an alternative method for managing views for different device types.

2.6.1 Device aware view resolving

스프링 모바일은 AbstractDeviceDelegatingViewResolver 와 다른 뷰 리졸버 구현체를 위임하는 추상화 ViewResolver 랩퍼 를 포함하여서, 각각의 뷰를 위해 정의된 매핑을 필요로 하는 대신에 디바이스 구체적인 뷰네임의 해석을 허용합니다. 경량화 구현체가 제공되어서 뷰네임이 기본인지, 모바일인지 태블릿 기반인지 조정하는 것을 지원합니다.

Spring Mobile includes AbstractDeviceDelegatingViewResolver, an abstract ViewResolver wrapper that delegates to another view resolver implementation, allowing for resolution of device specific view names without the need for a dedicated mapping to be defined for each view. A lightweight implementation is provided, which supports adjusting view names based on whether the calling device is normal, mobile, or tablet based.

당신의 어플리케이션 내에서, 당신은 기본, 모바일, 태블릿 디바이스를 위한 대안적인 뷰들과 적절하게 주어진 설정을 생성할 수 있습니다. 스프링 모바일은 뷰네임을 조절하여 적절한 뷰네임을 찾을 것입니다. 이것은 내부적으로 일어나며 컨트롤러에서 조건적인 로직이 필요가 없습니다. 다음의 테이블은 "home" 뷰에 대한 요청을 받았을 때 LiteDeviceDelegatingViewResolver 의 행위를 설명하고, 그 요청이 prefix를 사용하도록 조절합니다. 이것은 예를 들자면 당신이 서브디렉터리에 모바일뷰를 저장할 수 있게 해줍니다.

Within your application, you can then create alternate views for normal, mobile or tablet devices, and given the proper configuration, Spring Mobile will adjust the view name to resolve to the correct one. This happens internally, without the need to add conditional logic through your controllers. The following table illustrates the behavior of the LiteDeviceDelegatingViewResolver when receiving a request for the "home" view and adjusting it to use a prefix. This allows you to store "mobile" views in a subdirectory, for example.

Table 2.1. Prefixes

Resolved DeviceMethodPrefixAdjusted View
NormalsetNormalPrefix()"normal/""normal/home"
MobilesetMobilePrefix()"mobile/""mobile/home"
TabletsetTabletPrefix()"tablet/""tablet/home"

대안적으로, LiteDeviceDelegatingViewResolver 또한 접미사와 함께 뷰들을 조정하는 것을 지원합니다. 다음 테이블은 home 뷰에 들어온 요청을 받았을 때에 대한 결과들을 보여줍니다. 예를 들어서 이것은 당신이 모든 뷰들을 같은 폴더에 넣지만, 그것들은 접미사를 사용하여 구별하는 것을 보여줍니다.

Alternatively, the LiteDeviceDelegatingViewResolver also supports adjusting views with suffixes. The following table shows the results of receiving a request for the "home" view. For example, this allows you to store all your views in the same folder, and distinguish between them by using different suffixes.

Table 2.2. Suffixes

Resolved DeviceMethodSuffixAdjusted View
NormalsetNormalSuffix()".nor""home.nor"
MobilesetMobileSuffix()".mob""home.mob"
TabletsetTabletSuffix()".tab""home.tab"

2.6.2 Enabling device aware views

다음의 예제는 InternalResourceViewResolver를 위임하기 위해 어떻게 사이트를 설정하는 지 보여줍니다. 이것은 요청된 디바이스가 모바일이나 태블릿으로 결정될 때, 추가적인 mobile/ or tablet/ 접두사를 뷰 네임에 추가시킴으로써 뷰 네임을 조정합니다.

The following example illustrates how to configure a site that delegates to an InternalResourceViewResolver. It is configurated to adjust the view name by adding a mobile/ or tablet/ prefix if the requesting device is determined to be mobile or tablet respectively.

XML configuration:

<bean class="org.springframework.mobile.device.view.LiteDeviceDelegatingViewResolver">
  <constructor-arg>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/WEB-INF/views/" />
      <property name="suffix" value=".jsp" />
    </bean>
  </constructor-arg>
  <property name="mobilePrefix" value="mobile/" />
  <property name="tabletPrefix" value="tablet/" />
</bean>
                

Java-based configuration:

@Bean
public LiteDeviceDelegatingViewResolver liteDeviceAwareViewResolver() {
    InternalResourceViewResolver delegate = new InternalResourceViewResolver();
    delegate.setPrefix("/WEB-INF/views/");
    delegate.setSuffix(".jsp");
    LiteDeviceDelegatingViewResolver resolver = new LiteDeviceDelegatingViewResolver(delegate);
    resolver.setMobilePrefix("mobile/");
    resolver.setTabletPrefix("tablet/");
    return resolver;
}
                

2.6.3 Fallback resolution

ViewResolver를 사용하는 것은 뷰네임 전략을 당신의 사이트 전체에 적용하는데, 몇몇 뷰는 다른 디바이스 타입이나 다른 디바이스 버젼이 필요없이 분리된 구현체들이 필요없는 경우가 있는 경우가 있을 것입니다. 이러한 경우 당신은 fallback 지원을 활성화 할 수 있습니다. fallback 지원을 활성화한다는 것은 조정된 뷰네임이 해석되지 않으면, ViewResolver가 원래 뷰 요청 해석을 시도한다는 것을 의미합니다. 이 특징은 위임한 ViewResolverresolveViewName에 대한 요청에서 존재하지 않는 뷰에 대한 해석을 시도하여서, null을 반환할 때만 지원됩니다.

Because using a ViewResolver will apply the view name strategy to your entire site, there may be times when some of your views do not have separate implementations for different device types or you do not need different versions. In this case, you can enable fallback support. Enabling fallback support means if an adjusted view name cannot be resolved, the ViewResolver will attempt to resolve the original view request. This feature is only supported if the delegate ViewResolver returns null from a call to resolveViewName when attempting to resolve a view that does not exist.

fallback 지원을 활성화하는 것은 enableFallback 속성을 설정하여서 가능합니다.

Enable fallback support by setting the enableFallback property.

XML configuration:

<bean class="org.springframework.mobile.device.view.LiteDeviceDelegatingViewResolver">
    ...
    <property name="enableFallback" value="true" />
    ...
</bean>
                

Java-based configuration:

@Bean
public LiteDeviceDelegatingViewResolver liteDeviceAwareViewResolver() {
    ...
    resolver.setEnableFallback(true);
    ...
    return resolver;
}
                

댓글