시작 화면을 더 오래 표시

앱의 연장된 시작 화면을 만들어 시작 화면을 더 오랫동안 표시합니다. 이 확장형 화면은 앱이 열렸을 때 나타나는 시작 화면을 그대로 따라 하지만 사용자 지정할 수 있습니다. 실시간 로딩 정보를 표시해야 하거나 초기 UI를 준비하도록 앱에 추가 시간을 부여해야 하는 경우, 확장형 시작 화면을 통해 시작 환경을 정의할 수 있습니다.

참고 항목

이 항목의 “연장된 시작 화면” 구는 연장된 기간 동안 화면에 유지되는 초기 화면을 가리킵니다. SplashScreen 클래스에서 파생되는 하위 클래스를 의미하지는 않습니다.

중요 API

이 토픽에서는 다음 API가 사용됩니다.

기본 시작 화면 권장 사항

다음 권장 사항에 따라 확장형 시작 화면이 기본 시작 화면을 정확하게 따라 하는지 확인하세요.

  • 확장형 시작 화면 페이지에서는 앱 매니페스트(앱의 시작 화면 이미지)에서 시작 화면으로 지정된 이미지와 일치하는 620 x 300 픽셀 이미지를 사용해야 합니다. Microsoft Visual Studio에서 시작 화면 설정은 앱 매니페스트(Package.appxmanifest 파일) 내 시각 자산 탭의 시작 화면 섹션에 저장됩니다.
  • 확장형 시작 화면에서는 앱 매니페스트(앱의 시작 화면 배경)에서 시작 화면으로 지정된 배경색과 일치하는 배경색을 사용해야 합니다.
  • 코드에서는 SplashScreen 클래스를 사용하여 앱의 시작 화면 이미지를 기본 시작 화면과 동일한 화면 좌표에 배치해야 합니다.
  • 코드에서는 확장형 시작 화면에서 항목의 위치가 변경되도록 SplashScreen 클래스를 사용해서 창 크기 조정 이벤트(예: 화면이 회전하거나 앱이 다른 앱의 안내 화면 옆으로 이동하는 경우)에 응답해야 합니다.

다음 단계에 따라 기본 시작 화면을 효과적으로 따라 하는 확장형 시작 화면을 만드세요.

기존 앱에 빈 페이지 항목 추가하기

이 토픽에서는 C#, Visual Basic 또는 C++를 사용해서 확장형 시작 화면을 기존 UWP(유니버설 Windows 플랫폼) 앱 프로젝트에 추가해야 한다고 가정합니다.

  • Visual Studio에서 앱을 엽니다.
  • 메뉴 모음에서 프로젝트를 누르거나 열고 새 항목 추가를 클릭하세요. 새 항목 추가 대화 상자가 나타납니다.
  • 이 대화 상자에서 새로운 빈 페이지를 앱에 추가하세요. 이 토픽에서는 확장형 시작 화면 페이지의 이름을 "ExtendedSplash"로 지정합니다.

빈 페이지 항목을 추가하면 파일 두 개가 생성되는데, 하나는 태그용 파일(ExtendedSplash.xaml)이고 다른 하나는 코드용 파일(ExtendedSplash.xaml.cs)입니다.

확장형 시작 화면의 필수 XAML

확장형 시작 화면에 이미지 및 진행 컨트롤을 추가하려면 다음 단계대로 하세요.

ExtendedSplash.xaml 파일:

  • 앱 매니페스트(Package.appxmanifest 파일의 시각적 자산 섹션)에서 앱 시작 화면에 대해 설정한 배경색과 일치하도록 기본 Grid 요소의 Background 속성을 변경합니다. 기본 시작 화면 색은 연한 회색(16진수 값 #464646)입니다. 이 그리드 요소는 새 빈 페이지를 생성할 때 기본적으로 제공됩니다. 그리드를 사용할 필요는 없습니다. 이는 확장형 시작 화면을 빌드하는 데 편리한 기반일 뿐입니다.
  • 그리드캔버스 요소를 추가하세요. 이 캔버스 를 사용하여 확장형 시작 화면 이미지를 배치해야 합니다.
  • 캔버스이미지 요소를 추가하세요. 기본 시작 화면으로 선택한 확장형 시작 화면에 동일한 620 x 300 픽셀 이미지를 사용하세요.
  • (선택 사항) 앱이 로딩 중임을 사용자에게 표시하려면 진행 컨트롤을 추가하세요. 이 토픽에서는 확정 또는 미정 상태인 ProgressBar 대신 ProgressRing을 추가합니다.

다음 예제에서는 이러한 추가 및 변경 내용이 있는 Grid를 보여 줍니다.

    <Grid Background="#464646">
        <Canvas>
            <Image x:Name="extendedSplashImage" Source="Assets/SplashScreen.png"/>
            <ProgressRing Name="splashProgressRing" IsActive="True" Width="20" HorizontalAlignment="Center"></ProgressRing>
        </Canvas>
    </Grid>

참고 항목

이 예제에서는 ProgressRing의 너비를 20픽셀로 설정합니다. 사용자는 수동으로 너비를 앱에 적합한 값으로 설정할 수 있습니다. 단, 컨트롤은 20픽셀 미만의 너비로 렌더링되지 않습니다.

확장형 시작 화면 클래스의 필수 코드

확장형 시작 화면은 창 크기(Windows에만 해당) 또는 방향이 변경될 때마다 응답해야 합니다. 창이 어떻게 변경되든 확장형 시작 화면이 멋지게 보이도록 사용할 이미지의 위치를 업데이트해야 합니다.

다음 단계에 따라 확장형 시작 화면을 올바르게 표시할 메서드를 정의하세요.

  1. 필수 네임스페이스 추가

    SplashScreen 클래스, Rect 구조체 및 Window.SizeChanged 이벤트에 액세스하려면 다음 네임스페이스를 ExtendedSplash.xaml.cs에 추가해야 합니다.

    using Windows.ApplicationModel.Activation;
    using Windows.Foundation;
    using Windows.UI.Core;
    
  2. partial 클래스 만들기 및 클래스 변수 선언

    확장형 시작 화면을 나타내는 부분 클래스를 생성하려면 다음 코드를 ExtendedSplash.xaml.cs에 넣으세요.

    partial class ExtendedSplash : Page
    {
        internal Rect splashImageRect; // Rect to store splash screen image coordinates.
        private SplashScreen splash; // Variable to hold the splash screen object.
        internal bool dismissed = false; // Variable to track splash screen dismissal status.
        internal Frame rootFrame;
    
       // Define methods and constructor
    }
    

    이 클래스 변수는 여러 메서드에서 사용됩니다. splashImageRect 변수는 시스템에서 앱의 시작 화면 이미지를 표시한 좌표를 저장합니다. splash 변수는 SplashScreen 개체를 저장하고, dismissed 변수는 시스템에 나타나는 시작 화면이 사라졌는지 여부를 추적합니다.

  3. 이미지를 올바르게 배치하는 클래스 생성자 정의

    다음 코드는 창 크기 조정 이벤트의 수신을 대기하고, 확장형 시작 화면에 이미지 및 진행 컨트롤(선택 사항)을 배치하고, 탐색용 프레임을 생성하고, 저장된 세션 상태를 복원할 비동기 메서드를 호출하는 확장형 시작 화면 클래스의 생성자를 정의합니다.

    public ExtendedSplash(SplashScreen splashscreen, bool loadState)
    {
        InitializeComponent();
    
        // Listen for window resize events to reposition the extended splash screen image accordingly.
        // This ensures that the extended splash screen formats properly in response to window resizing.
        Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
    
        splash = splashscreen;
        if (splash != null)
        {
            // Register an event handler to be executed when the splash screen has been dismissed.
            splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);
    
            // Retrieve the window coordinates of the splash screen image.
            splashImageRect = splash.ImageLocation;
            PositionImage();
    
            // If applicable, include a method for positioning a progress control.
            PositionRing();
        }
    
        // Create a Frame to act as the navigation context
        rootFrame = new Frame();            
    }
    

    앱이 확장형 시작 화면에 이미지를 올바르게 배치하도록 클래스 생성자에 Window.SizeChanged 처리기(예제의 ExtendedSplash_OnResize)를 등록해야 합니다.

  4. 연장된 시작 화면에서 이미지를 배치하는 클래스 메서드 정의

    이 코드는 splashImageRect 클래스 변수로 확장형 시작 화면 페이지에 이미지를 배치하는 방법을 보여줍니다.

    void PositionImage()
    {
        extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
        extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
        extendedSplashImage.Height = splashImageRect.Height;
        extendedSplashImage.Width = splashImageRect.Width;
    }
    
  5. (옵션) 연장된 시작 화면에 진행률 표시줄을 배치하는 클래스 메서드 정의

    확장형 시작 화면에 ProgressRing을 추가하기로 선택한 경우, 시작 화면 이미지를 기준으로 배치하세요. 이미지 아래의 32픽셀 지점 가운데에 ProgressRing을 배치하려면 다음 코드를 ExtendedSplash.xaml.cs에 추가하세요.

    void PositionRing()
    {
        splashProgressRing.SetValue(Canvas.LeftProperty, splashImageRect.X + (splashImageRect.Width*0.5) - (splashProgressRing.Width*0.5));
        splashProgressRing.SetValue(Canvas.TopProperty, (splashImageRect.Y + splashImageRect.Height + splashImageRect.Height*0.1));
    }
    
  6. 클래스 내에서 Dismissed 이벤트 처리기 정의

    ExtendedSplash.xaml.cs에서는 dismissed 클래스 변수를 true로 설정해서 SplashScreen.Dismissed 이벤트 발생 시 응답하세요. 앱에 설정 작업이 있으면 이 이벤트 처리기에 추가하세요.

    // Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view).
    void DismissedEventHandler(SplashScreen sender, object e)
    {
        dismissed = true;
    
        // Complete app setup operations here...
    }
    

    앱 설정이 완료되면 확장형 시작 화면에서 다른 화면으로 이동하세요. 다음 코드는 앱의 MainPage.xaml 파일에 정의된 MainPage(으)로 이동하는 DismissExtendedSplash(이)라는 메서드를 정의합니다.

    async void DismissExtendedSplash()
      {
         await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,() =>            {
              rootFrame = new Frame();
              rootFrame.Content = new MainPage(); Window.Current.Content = rootFrame;
            });
      }
    
  7. 클래스 내에서 Window.SizeChanged 이벤트 처리기 정의

    사용자가 창의 크기를 조정할 경우 요소의 위치가 변경되도록 확장형 시작 화면을 준비하세요. 이 코드는 새 좌표를 캡처하고 이미지의 위치를 변경해서 Window.SizeChanged 이벤트 발생 시 응답합니다. 확장형 시작 화면에 진행 컨트롤을 추가했다면 이 이벤트 처리기 내부에서도 해당 컨트롤의 위치를 변경하세요.

    void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
    {
        // Safely update the extended splash screen image coordinates. This function will be executed when a user resizes the window.
        if (splash != null)
        {
            // Update the coordinates of the splash screen image.
            splashImageRect = splash.ImageLocation;
            PositionImage();
    
            // If applicable, include a method for positioning a progress control.
            // PositionRing();
        }
    }
    

    참고 항목

     이미지 위치를 가져오기 전에 예제에 표시된 것처럼 클래스 변수(splash)에 올바른 SplashScreen 개체가 있는지 확인합니다.

     

  8. (선택 사항) 저장된 세션 상태를 복원하는 클래스 메서드 추가

    4단계, 시작 활성화 처리기 수정에서 OnLaunched 메서드에 추가한 코드로 인해 앱을 시작할 때 연장된 시작 화면이 표시됩니다. 연장된 시작 화면 클래스에 앱 실행과 관련된 모든 메서드를 통합하려면 메서드를 ExtendedSplash.xaml.cs 파일에 추가하여 앱 상태를 복원하는 것이 좋습니다.

    void RestoreState(bool loadState)
    {
        if (loadState)
        {
             // code to load your app's state here
        }
    }
    

    App.xaml.cs에서 시작 활성화 처리기를 수정할 때 앱의 이전 ApplicationExecutionStateTerminated인 경우에도 loadstate를 true로 설정합니다. 설정되면 RestoreState 메서드가 앱을 이전 상태로 복원합니다. 앱 시작, 일시 중단 및 종료에 대한 개요는 앱 수명 주기를 참조하세요.

시작 활성화 처리기 수정하기

앱이 열리면 시스템이 시작 화면 정보를 앱의 시작 활성화 이벤트 처리기에 전달합니다. 이 정보를 사용하면 확장형 시작 화면 페이지에 이미지를 올바르게 배치할 수 있습니다. 이 시작 화면 정보는 앱의 OnLaunched 처리기에 전달되는 활성화 이벤트 인수에서 가져올 수 있습니다(다음 코드의 args 변수 참조).

앱의 OnLaunched 처리기를 아직 재정의하지 않은 경우 앱 수명 주기를 참조하여 활성화 이벤트를 처리하는 방법을 알아봅니다.

App.xaml.cs에서 확장형 시작 화면을 만들고 표시하려면 다음 코드를 추가하세요.

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    if (args.PreviousExecutionState != ApplicationExecutionState.Running)
    {
        bool loadState = (args.PreviousExecutionState == ApplicationExecutionState.Terminated);
        ExtendedSplash extendedSplash = new ExtendedSplash(args.SplashScreen, loadState);
        Window.Current.Content = extendedSplash;
    }

    Window.Current.Activate();
}

전체 코드

다음 코드는 이전 단계에서 표시된 조각과 약간 다릅니다.

  • ExtendedSplash.xaml에는 DismissSplash 버튼이 포함되어 있습니다. 이 버튼을 클릭하면 이벤트 처리기인 DismissSplashButton_Click이(가) DismissExtendedSplash 메서드를 호출합니다. 앱에서 리소스 로딩 또는 UI 초기화가 완료되면 DismissExtendedSplash을(를) 호출하세요.
  • 이 앱은 프레임 탐색 기능을 사용하는 UWP 앱 프로젝트 템플릿도 사용합니다. 그 결과 App.xaml.cs에서는 시작 활성화 처리기(OnLaunched)가 rootFrame을(를) 정의하고 이를 사용하여 앱 창의 내용을 설정합니다.

ExtendedSplash.xaml

이 예제에는 로드할 앱 리소스가 없으므로 DismissSplash 단추가 포함되어 있습니다. 앱에서 리소스 로딩 또는 초기 UI 준비가 완료되면 확장형 시작 화면이 자동으로 사라집니다.

<Page
    x:Class="SplashScreenExample.ExtendedSplash"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SplashScreenExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="#464646">
        <Canvas>
            <Image x:Name="extendedSplashImage" Source="Assets/SplashScreen.png"/>
            <ProgressRing Name="splashProgressRing" IsActive="True" Width="20" HorizontalAlignment="Center"/>
        </Canvas>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom">
            <Button x:Name="DismissSplash" Content="Dismiss extended splash screen" HorizontalAlignment="Center" Click="DismissSplashButton_Click" />
        </StackPanel>
    </Grid>
</Page>

ExtendedSplash.xaml.cs

DismissExtendedSplash 메서드는 DismissSplash 단추에 대한 click 이벤트 처리기에서 호출됩니다. 앱에서는 DismissSplash 버튼이 필요하지 않습니다. 대신 앱이 리소스 로딩을 완료하여 해당 리소스의 기본 페이지로 이동하려는 경우 DismissExtendedSplash을(를) 호출하세요.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

using Windows.ApplicationModel.Activation;
using SplashScreenExample.Common;
using Windows.UI.Core;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234238

namespace SplashScreenExample
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    partial class ExtendedSplash : Page
    {
        internal Rect splashImageRect; // Rect to store splash screen image coordinates.
        private SplashScreen splash; // Variable to hold the splash screen object.
        internal bool dismissed = false; // Variable to track splash screen dismissal status.
        internal Frame rootFrame;

        public ExtendedSplash(SplashScreen splashscreen, bool loadState)
        {
            InitializeComponent();

            // Listen for window resize events to reposition the extended splash screen image accordingly.
            // This is important to ensure that the extended splash screen is formatted properly in response to snapping, unsnapping, rotation, etc...
            Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);

            splash = splashscreen;

            if (splash != null)
            {
                // Register an event handler to be executed when the splash screen has been dismissed.
                splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);

                // Retrieve the window coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();

                // Optional: Add a progress ring to your splash screen to show users that content is loading
                PositionRing();
            }

            // Create a Frame to act as the navigation context
            rootFrame = new Frame();

            // Restore the saved session state if necessary
            RestoreState(loadState);
        }

        void RestoreState(bool loadState)
        {
            if (loadState)
            {
                // TODO: write code to load state
            }
        }

        // Position the extended splash screen image in the same location as the system splash screen image.
        void PositionImage()
        {
            extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
            extendedSplashImage.Height = splashImageRect.Height;
            extendedSplashImage.Width = splashImageRect.Width;

        }

        void PositionRing()
        {
            splashProgressRing.SetValue(Canvas.LeftProperty, splashImageRect.X + (splashImageRect.Width*0.5) - (splashProgressRing.Width*0.5));
            splashProgressRing.SetValue(Canvas.TopProperty, (splashImageRect.Y + splashImageRect.Height + splashImageRect.Height*0.1));
        }

        void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
        {
            // Safely update the extended splash screen image coordinates. This function will be fired in response to snapping, unsnapping, rotation, etc...
            if (splash != null)
            {
                // Update the coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();
                PositionRing();
            }
        }

        // Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view).
        void DismissedEventHandler(SplashScreen sender, object e)
        {
            dismissed = true;

            // Complete app setup operations here...
        }

        void DismissExtendedSplash()
        {
            // Navigate to mainpage
            rootFrame.Navigate(typeof(MainPage));
            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

        void DismissSplashButton_Click(object sender, RoutedEventArgs e)
        {
            DismissExtendedSplash();
        }
    }
}

App.xaml.cs

이 프로젝트는 Visual Studio의 UWP 앱 빈 앱(XAML) 프로젝트 템플릿을 사용하여 만든 것입니다. OnNavigationFailed 이벤트 처리기와 OnSuspending 이벤트 처리기 모두 자동으로 생성되며, 확장형 시작 화면을 구현하기 위해 변경될 필요가 없습니다. 이 토픽에서는 OnLaunched만 수정해야 합니다.

앱에 프로젝트 템플릿을 사용하지 않은 경우 Frame 탐색을 사용하지 않는 수정된 OnLaunched의 예제를 보려면 4단계: 시작 활성화 처리기 수정을 참조하세요.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Application template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234227

namespace SplashScreenExample
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
            Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
            Microsoft.ApplicationInsights.WindowsCollectors.Session);
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                // Set the default language
                rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

                rootFrame.NavigationFailed += OnNavigationFailed;

                //  Display an extended splash screen if app was not previously running.
                if (e.PreviousExecutionState != ApplicationExecutionState.Running)
                {
                    bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
                    ExtendedSplash extendedSplash = new ExtendedSplash(e.SplashScreen, loadState);
                    rootFrame.Content = extendedSplash;
                    Window.Current.Content = rootFrame;
                }
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainPage), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            // TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

참조