Android

안드로이드 13 타겟팅시 변경되는 주요 사항들(미리미리 대비하세요)

내년 이맘 때 쯤이면 안드로이드 13이 정식 버전으로 출시될 것입니다.

앱이 Android 13 이상을 타겟팅한다면 다음  동작을 올바르게 지원하도록 앱을 수정해야 합니다.

 

가장 큰 변화라고 생각되는 부분은 개인정보보호 센션에서

미디어파일 액세스 권한 관련해서 READ_EXTERNAL_STORAGE 퍼미션을 사용했습니다.

 

그럼 폴더에 모든 파일들에 접근이 가능해졌는데 이부분에 대해서

사용자에게 너의 사진과 동영상 등에 절대 접근하지 않는다고

공지를 해도 의구심을 품었고 권한을 부여하지 않는 사용자들이 있어요

그런데 이 권한이 드디어 세분화 됩니다. 좀 빨리 해주지!!

 

세분화된 미디어 권한

앱이 Android 13 이상을 타겟팅하고 다른 앱에서 만든 미디어 파일에 액세스해야 하는 경우 READ_EXTERNAL_STORAGE 권한 대신 다음과 같은 세분화된 미디어 권한을 하나 이상 요청해야 합니다.

미디어 유형요청 권한

이미지 및 사진 READ_MEDIA_IMAGES
동영상 READ_MEDIA_VIDEO
오디오 파일 READ_MEDIA_AUDIO

사용자가 이전에 READ_EXTERNAL_STORAGE 권한을 앱에 부여한 경우 시스템은 세분화된 미디어 권한을 앱에 자동으로 부여합니다. 이전에 권한을 부여하지 않은 경우 시스템은 앱이 위의 표에 나온 권한을 요청할 때 사용자 대상 대화상자를 표시합니다. 

READ_MEDIA_IMAGES 권한과 READ_MEDIA_VIDEO 권한을 동시에 모두 요청하면 시스템 권한 대화상자가 하나만 표시됩니다.

참고: 앱에서 이미지와 사진, 동영상에만 액세스해야 하면 READ_MEDIA_IMAGES  READ_MEDIA_VIDEO 권한을 선언하는 대신 사진 선택 도구를 사용하는 것이 좋습니다.

 

<!-- Required only if your app needs to access images or photos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<!-- Required only if your app needs to access videos
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- Required only if your app needs to access audio files
     that other apps created. -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<!-- If your app doesn't need to access media files that other apps created,
     set the "maxSdkVersion" attribute to "28" instead. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                 android:maxSdkVersion="32" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="29" />

https://developer.android.com/about/versions/13/behavior-changes-13?hl=ko 

 

동작 변경사항: Android 13 이상을 타겟팅하는 앱  |  Android 개발자  |  Android Developers

Android 13 이상을 타겟팅하는 앱에 영향을 미치는 Android 13의 변경사항을 알아봅니다.

developer.android.com

https://developer.android.com/training/data-storage/shared/media#media_store

 

공유 저장소의 미디어 파일에 액세스  |  Android 개발자  |  Android Developers

공유 저장소의 미디어 파일에 액세스 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 많은 앱에서 더욱 풍부한 사용자 환경을 제공하기 위해 사용자가 외부

developer.android.com

개인 정보 보호

알림 권한이 포그라운드 서비스 모양에 영향을 줌

사용자가 알림 권한을 거부하면 알림 창에서 포그라운드 서비스와 관련된 알림을 볼 수 없습니다. 하지만 포그라운드 서비스와 관련된 알림은 알림 권한 부여 여부에 관계없이 포그라운드 서비스(FGS) 작업 관리자에 계속 표시됩니다.

근처 Wi-Fi 기기의 새 런타임 권한

이전 버전의 Android에서는 사용자가 앱에 여러 일반적인 Wi-Fi 사용 사례를 완료할 수 있는 ACCESS_FINE_LOCATION 권한을 부여해야 합니다.

사용자가 위치 정보 액세스 권한을 Wi-Fi 기능과 연결하기는 어려우므로 Android 13(API 수준 33)에서는 Wi-Fi를 통해 근처 액세스 포인트에 대한 기기의 연결을 관리하는 앱을 위해 NEARBY_DEVICES 권한 그룹의 런타임 권한을 도입했습니다. 이 권한 NEARBY_WIFI_DEVICES는 다음과 같은 Wi-Fi 사용 사례를 처리합니다.

  • 프린터나 미디어 전송 기기와 같은 근처 기기를 찾거나 기기에 연결합니다. 이 워크플로를 통해 앱은 다음과 같은 작업을 할 수 있습니다.
    • BLE 등을 통해 대역 외 AP 정보를 수신합니다.
    • 로컬 전용 핫스팟을 사용하는 Wi-Fi Aware 및 Connect를 통해 기기를 검색하여 연결합니다.
    • Wi-Fi Direct를 통해 기기를 검색하여 연결합니다.
  • 자동차나 스마트 홈 기기와 같이 알려진 SSID에 연결을 시작합니다.
  • 로컬 전용 핫스팟을 시작합니다.
  • 근처 Wi-Fi Aware 기기까지 다양한 기기를 지원합니다.

Android 13 이상을 타겟팅하고 Wi-Fi API를 사용하는 경우에는 ACCESS_FINE_LOCATION 대신 NEARBY_WIFI_DEVICES를 요청하세요(앱이 Wi-Fi API에서 물리적 위치 정보를 얻는 것이 아니어야 함). NEARBY_WIFI_DEVICES 권한을 선언할 때 앱이 Wi-Fi API에서 물리적 위치 정보를 얻지 않는다고 강력하게 어설션합니다. 이렇게 하려면 android:usesPermissionFlags 속성을 neverForLocation으로 설정합니다. 이 프로세스는 Android 12(API 수준 31)에서 블루투스 기기 정보가 위치에 사용되지 않는다고 어설션할 때 실행하는 프로세스와 유사합니다.

참고: 이 변경사항은 Wi-Fi API를 호출하는 경우에만 앱에 영향을 줍니다. 영향을 받는 API 목록을 확인하세요.

근처 Wi-Fi 기기에 대한 액세스 권한을 요청하는 방법을 자세히 알아보세요.

 

백그라운드에서 생체 신호 센서를 사용하려면 새로운 권한이 필요함

Android 13에서는 심박수, 체온, 혈중 산소 농도와 같은 생체 신호 센서에 ‘사용 중인 동안’ 액세스라는 개념이 도입되었습니다. 이 액세스 모델은 Android 10 (API 수준 29)의 위치에 도입되었던 액세스 모델과 매우 유사합니다.

앱이 Android 13을 타겟팅하며 백그라운드에서 실행되는 동안 생체 신호 센서 정보에 액세스해야 하는 경우, 기존 BODY_SENSORS 권한에 더해 새로운 BODY_SENSORS_BACKGROUND 권한을 선언해야 합니다.

참고: 이는 기기의 설치 프로그램이 앱의 허용 목록에 권한을 포함하기 전까지는 부여되지 않는 ‘엄격하게 제한된’ 권한입니다. 자세한 내용은 이 가이드의 제한된 권한을 참고하세요.

 

성능 및 배터리

배터리 리소스 사용률

앱이 Android 13을 타겟팅하는 동안 앱을 백그라운드 배터리 사용량에 관해 ‘제한됨’ 상태로 설정하면 시스템은 다른 이유로 앱이 시작되기 전까지는 BOOT_COMPLETED 브로드캐스트 또는 LOCKED_BOOT_COMPLETED 브로드캐스트를 전달하지 않습니다.

 

사용자 환경

PlaybackState에서 파생된 미디어 컨트롤

Android 13(API 수준 33) 이상을 타겟팅하는 앱의 경우 시스템은 PlaybackState 작업에서 미디어 컨트롤을 가져옵니다. 이를 통해 시스템은 스마트폰과 태블릿 기기 사이에 기술적으로 일관된 더 풍부한 컨트롤 세트를 표시할 수 있고, Android Auto 및 Android TV와 같은 다른 Android 플랫폼에서 미디어 컨트롤이 렌더링되는 방식과도 일치시킬 수 있게 됩니다.

그림 2는 스마트폰과 태블릿 기기에서 각각 어떻게 표시되는지 보여줍니다.


출처 : https://developer.android.com

그림 2: 스마트폰과 태블릿 기기의 미디어 컨트롤

Android 13 전에는 시스템이 MediaStyle 알림에서 작업이 추가된 순서대로 최대 5개의 작업을 표시했습니다. 압축 모드(예: 축소된 빠른 설정)에서는 setShowActionsInCompactView()로 지정된 작업이 최대 3개까지 표시되었습니다.

Android 13부터 시스템은 다음 표에 설명된 대로 PlaybackState를 기반으로 작업 버튼을 최대 5개 표시합니다. 압축 모드에서는 처음 3개의 작업 슬롯만 표시됩니다. Android 13을 타겟팅하지 않거나 PlaybackState를 포함하지 않는 앱의 경우 시스템은 MediaStyle 알림에 추가된 Action 목록에 기반하여 컨트롤을 표시합니다(이전 단락에서 설명).

  작업 기준
1 재생 PlaybackState의 현재 상태는 다음 중 하나입니다.

  • STATE_NONE
  • STATE_STOPPED
  • STATE_PAUSED
  • STATE_ERROR
로딩 스피너 PlaybackState의 현재 상태는 다음 중 하나입니다.

  • STATE_CONNECTING
  • STATE_BUFFERING
일시중지 PlaybackState의 현재 상태는 위에 없습니다.
2 이전 PlaybackState 작업에는 ACTION_SKIP_TO_PREVIOUS가 포함됩니다.
맞춤식 PlaybackState 작업에는 ACTION_SKIP_TO_PREVIOUS가 포함되지 않고 PlaybackState 맞춤 작업에는 아직 배치되지 않은 맞춤 작업이 포함됩니다.
비어 있음 PlaybackState extras에는 SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV 키의 true 불리언 값이 포함됩니다.
3 다음 PlaybackState 작업에는 ACTION_SKIP_TO_NEXT가 포함됩니다.
맞춤식 PlaybackState 작업에는 ACTION_SKIP_TO_NEXT가 포함되지 않고 PlaybackState 맞춤 작업에는 아직 배치되지 않은 맞춤 작업이 포함됩니다.
비어 있음 PlaybackState extras에는 SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT 키의 true 불리언 값이 포함됩니다.
4 맞춤식 PlaybackState 맞춤 작업에는 아직 배치되지 않은 맞춤 작업이 포함됩니다.
5 맞춤식 PlaybackState 맞춤 작업에는 아직 배치되지 않은 맞춤 작업이 포함됩니다.

맞춤 작업은 PlaybackState에 추가된 순서대로 배치됩니다.

 

앱 색상 테마가 WebView 콘텐츠에 자동으로 적용됨

Android 13(API 수준 33) 이상을 타겟팅하는 앱의 경우 setForceDark() 메서드가 지원 중단되므로 이 메서드가 호출되면 작동하지 않습니다.

대신 이제 WebView가 앱의 테마 속성 isLightTheme에 따라 미디어 쿼리 prefers-color-scheme을 항상 설정합니다. 즉, isLightTheme이 true이거나 지정되지 않은 경우 prefers-color-scheme은 light입니다. 그 외의 경우에는 dark입니다. 이 동작은 콘텐츠에서 지원하면 웹 콘텐츠의 밝은 스타일이나 어두운 스타일이 앱 테마에 맞게 자동으로 적용된다는 것을 의미합니다.

대부분의 앱에서 새 동작은 적절한 앱 스타일을 자동으로 적용해야 합니다. 하지만 앱을 테스트하여 어두운 모드 설정을 수동으로 제어했을 수 있는 사례를 확인해야 합니다.

그래도 앱의 색상 테마 동작을 맞춤설정해야 한다면 setAlgorithmicDarkeningAllowed() 메서드를 대신 사용하세요. 이전 Android 버전과의 호환성을 위해 AndroidX에서 동등한 setAlgorithmicDarkeningAllowed() 메서드를 사용하는 것이 좋습니다.

앱의 targetSdkVersion 및 테마 설정에 따라 앱에서 발생할 수 있는 동작을 자세히 알아보려면 이 메서드에 관한 문서를 참고하세요.

 

Google Play 서비스

광고 ID에 필요한 권한

Google Play 서비스 광고 ID를 사용하고 Android 13(API 수준 33) 이상을 타겟팅하는 앱은 다음과 같이 앱의 매니페스트 파일에서 AD_ID 일반 권한을 선언해야 합니다.

<manifest ...>
    <!-- Required only if your app targets Android 13 or higher. -->
    <uses-permission android:name="com.google.android.gms.permission.AD_ID"/>

    <application ...>
        ...
    </application>
</manifest>

앱이 Android 13 이상을 타겟팅할 때 이 권한을 선언하지 않으면 광고 ID가 자동으로 삭제되고 0으로 이루어진 문자열로 대체됩니다.

앱이 라이브러리의 매니페스트에서 AD_ID 권한을 선언하는 SDK를 사용하는 경우 이 권한은 앱의 매니페스트 파일과 기본적으로 병합됩니다. 이 경우 앱의 매니페스트 파일에서 권한을 선언할 필요가 없습니다.

자세한 내용은 Play Console 도움말의 광고 ID를 참고하세요.

 

업데이트된 비 SDK 제한사항

Android 13에는 Android 개발자와의 공동작업 및 최신 내부 테스트를 기반으로 제한된 비 SDK 인터페이스의 업데이트된 목록이 포함되어 있습니다. 비 SDK 인터페이스를 제한하는 경우, 가능하면 해당 인터페이스에 대한 공개된 대안이 사용 가능한지 여부를 확인합니다.

Android 13을 타겟팅하지 않는 앱의 경우 이러한 변경사항 중 일부는 개발자에게 곧바로 영향을 주지 않을 수도 있습니다. 앱의 대상 API 수준에 따라 현재 일부 비 SDK 인터페이스를 사용하고 있을 수 있습니다. 하지만 비 SDK 메서드 또는 필드를 사용하면 언제든지 앱이 중단될 위험이 있다는 점에 유의하시기 바랍니다.

앱에서 비 SDK 인터페이스를 사용하는지 확실히 알 수 없는 경우 앱을 테스트하여 확인할 수 있습니다. 앱에서 비 SDK 인터페이스를 사용하는 경우 대체 SDK로의 이전을 계획해야 합니다. 일부 앱의 경우 비 SDK 인터페이스 사용에 관한 유효한 사용 사례가 있음을 알고 있습니다. 앱 기능을 구현하기 위해 비 SDK 인터페이스 대신 무엇을 사용해야 할지 알 수 없다면 새 공개 API를 요청해야 합니다.

이 Android 버전의 변경사항을 자세히 알아보려면 Android 13의 비 SDK 인터페이스 제한사항 업데이트를 참고하세요. 비 SDK 인터페이스에 관해 전반적으로 알아보려면 비 SDK 인터페이스 제한사항을 참고하세요.

 

 

동작 변경사항: 모든 앱

Android 13 플랫폼에는 앱에 영향을 줄 수 있는 동작 변경사항이 있습니다. targetSdkVersion과 관계없이 Android 13에서 실행되는 모든 앱에 적용되는 동작 변경사항은 다음과 같습니다. 이러한 변경사항을 적절히 지원해야 하는 경우 앱을 테스트한 후 필요에 따라 수정해야 합니다.

또한 Android 13을 타겟팅하는 앱에만 영향을 주는 동작 변경사항 목록을 검토해야 합니다.

 

성능 및 배터리

FGS(포그라운드 서비스) 작업 관리자

그림 1. 사용자가 진행 중인 포그라운드 서비스가 있는 앱을 중지할 수 있는 FGS 작업 관리자 워크플로. 이 워크플로는 Android 13 이상을 실행하는 기기에만 표시됨

Android 13(API 수준 33)부터 사용자는 알림 창에서 워크플로를 완료하여 그림 1과 같이 진행 중인 포그라운드 서비스가 있는 앱을 중지할 수 있습니다. 이러한 어포던스를 포그라운드 서비스(FGS) 작업 관리자라고 합니다. 앱은 사용자가 시작한 이러한 중지 작업을 처리할 수 있어야 합니다.

 

JobScheduler를 사용하여 작업 미리 가져오기 처리 개선

JobScheduler는 앱이 JobInfo.Builder.setPrefetch()를 사용하여 특정 작업을 ‘미리 가져오기’ 작업으로 표시할 수 있습니다. 미리 가져오기 작업은 사용자 환경을 개선하기 위해 다음번 앱이 실행되기 직전에 실행해야 하는 작업을 의미합니다. 지금까지 JobScheduler는 이 신호를 사용하여, 기회가 있으면 미리 가져오기 작업이 사용 가능한 데이터나 여분의 데이터를 사용할 수 있도록 했습니다.

Android 13(API 수준 33) 이상에서 시스템은 다음에 앱이 실행될 시점을 판단하려고 하며 이 추정을 사용하여 미리 가져오기 작업을 실행합니다. 앱은 다음번 앱 실행에 앞서 실행할 모든 작업을 미리 가져오기 작업으로 처리하도록 시도해야 합니다.

 

 

배터리 리소스 사용률

Android 13(API 수준 33)에서는 시스템이 기기 배터리 수명을 더 효과적으로 관리할 수 있는 다음과 같은 방법을 제공합니다.

  • 시스템이 ‘제한됨’ 앱 대기 버킷에 앱을 배치하는 경우에 적용되는 규칙 업데이트
  • 사용자가 앱의 백그라운드 배터리 사용을 ‘제한됨’ 상태로 설정한 경우 앱이 실행할 수 있는 작업에 관한 새로운 제한사항

우선순위가 높은 Firebase 클라우드 메시징(FCM) 할당량

Android 13(API 수준 33)에서는 Firebase 클라우드 메시징(FCM) 할당량을 업데이트하여 우선순위가 높은 FCM에 응답하여 알림을 표시하는 앱을 위해 우선순위가 높은 FCM 전송의 안정성을 개선합니다. Android 13(API 수준 33)에서 변경된 사항은 다음과 같습니다.

  • 앱 대기 버킷이 앱에서 사용할 수 있는 우선순위가 높은 FCM 수를 더 이상 결정하지 않습니다.
  • 우선순위가 높은 FCM 할당량은 우선순위가 높은 FCM에 대한 응답으로 사용자에게 표시되는 알림 수에 비례하여 확장됩니다.

이전 버전의 Android에서와 같이 할당량을 초과하는 우선순위가 높은 FCM은 일반 우선순위로 다운그레이드됩니다. FCM에 대한 응답으로 포그라운드 서비스(FGS)가 시작될 때 RemoteMessage.getPriority()의 결과를 확인하여 PRIORITY_HIGH인지 확인하고 잠재적인 ForegroundServiceStartNotAllowedException 예외를 처리하는 것이 좋습니다.

애플리케이션에서 우선순위가 높은 FCM에 대한 응답으로 알림을 항상 게시하지는 않는 경우 알림을 유발하는 메시지가 다운그레이드되지 않도록 이러한 FCM의 우선순위를 일반으로 변경하는 것이 좋습니다.

 

개인 정보 보호

알림 런타임 권한

Android 13(API 수준 33)에서는 런타임 알림 권한 POST_NOTIFICATIONS를 도입했습니다. 이 변경사항으로 인해 사용자는 가장 중요한 알림에 주목할 수 있습니다.

참고: 미디어 세션 및 전화 통화를 자체 관리하는 앱과 관련된 알림은 이 동작 변경사항에서 제외됩니다.

이 기능의 추가적인 제어 기능과 유연성을 이용할 수 있도록 가급적 빠른 시일 내에 Android 13 이상을 타겟팅할 것을 적극 권장합니다.

앱 권한 권장사항에 관해 자세히 알아보세요.

 

클립보드에서 민감한 콘텐츠 숨기기

앱에서 사용자가 민감한 콘텐츠(예: 비밀번호나 신용카드 정보)를 클립보드에 복사할 수 있도록 허용하는 경우 ClipboardManager#setPrimaryClip()을 호출하기 전에 ClipData의 ClipDescription에 플래그를 추가해야 합니다. 이 플래그를 추가하면 민감한 콘텐츠가 콘텐츠 미리보기에 표시되지 않습니다.

 

민감한 콘텐츠를 신고하려면 ClipDescription에 불리언 추가 항목을 추가하세요. 모든 앱은 타겟팅된 API 수준과 관계없이 이 작업을 실행해야 합니다.

 
// When your app targets API level 33 or higher
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app targets a lower API level
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

새로운 클립보드 UI에 관해 자세히 알아보려면 복사하여 붙여넣기 기능 페이지를 방문하세요.

보안

인텐트 필터가 일치하지 않는 인텐트를 차단함

앱이 Android 13 이상을 타겟팅하는 다른 앱의 내보낸 구성요소로 인텐트를 전송하면 이 인텐트는 수신 앱의 <intent-filter> 요소와 일치하는 경우에만 전달됩니다. 일치하지 않는 인텐트는 차단됩니다.

마찬가지로 앱이 Android 13 이상으로 업그레이드되면 다른 앱에서 전송된 모든 인텐트가 앱에서 선언된 <intent-filter> 요소와 일치하는 경우에만 앱의 내보낸 구성요소로 전달됩니다.

인텐트를 다른 앱의 인텐트 필터와 일치시키는 방법을 자세히 알아보세요.

공유 사용자 ID에서 이전

앱이 지원 중단된 android:sharedUserId 속성을 사용하고 이 속성의 기능에 더 이상 종속되지 않는 경우 다음 코드 스니펫과 같이 android:sharedUserMaxSdkVersion 속성을 32로 설정할 수 있습니다.

 
<manifest ...>
    <!-- To maintain backward compatibility, continue to use
         "android:sharedUserId" if you already added it to your manifest. -->
    android:sharedUserId="SHARED_PACKAGE_NAME"
    android:sharedUserMaxSdkVersion="32"
    ...
</manifest>

이 속성은 앱이 공유 사용자 ID를 더 이상 사용하지 않는다고 시스템에 알립니다. 앱이 android:sharedUserMaxSdkVersion을 선언하고 Android 13 이상을 실행하는 기기에 새로 설치된 경우 앱은 android:sharedUserId가 정의된 적이 없는 것처럼 작동합니다. 업데이트된 앱은 여전히 기존 공유 사용자 ID를 사용합니다.

주의: 매니페스트에 android:sharedUserId 속성을 이미 정의한 경우 삭제하지 마세요. 삭제하면 앱 업데이트가 실패합니다.

공유 사용자 ID는 패키지 관리자 내에서 비결정적 동작을 일으킵니다. 대신 앱에서는 공유 구성요소 간 상호 운용성이 용이하도록 서비스 및 콘텐츠 제공자와 같은 적절한 커뮤니케이션 메커니즘을 사용해야 합니다.

사용자 환경

닫을 수 있는 포그라운드 서비스 알림

Android 13 이상을 실행하는 기기에서는 사용자가 기본적으로 포그라운드 서비스와 연결된 알림을 닫을 수 있습니다.

핵심 기능

음성 서비스 구현의 기존 사본이 삭제됨

Android 13에서는 Voice IME, RecognitionService, 인텐트 기반 API를 비롯한 SpeechService 구현을 Google 앱에서 삭제합니다.

Android 12의 변경사항은 다음과 같습니다.

  • SpeechService 기능을 기본 SpeechService 제공자가 된 Google 음성 서비스 앱으로 이전했습니다.
  • RecognitionService 기능을 Android 시스템 인텔리전스 앱으로 이동하여 기기 내 음성 인식을 지원했습니다.

Android 12에서 앱 호환성을 유지할 수 있도록 Google 앱은 트램펄린을 사용하여 Google 음성 서비스 앱으로 트래픽을 전환합니다. Android 13에서는 이 트램펄린이 삭제됩니다.

앱은 특정 앱을 하드 코딩하는 대신 기기의 기본 SpeechService 제공자를 사용해야 합니다.

 

 

 

[reference]

 

Leave a Reply

error: Content is protected !!