앱에 Microsoft 서비스를 추가하는 방법(HTML)
[ 이 문서는 Windows 런타임 앱을 작성하는 Windows에서 8.x 및 Windows Phone 8.x 개발자를 대상으로 합니다. Windows 10용으로 개발하는 경우에는 최신 설명서를 참조하세요.]
여기에서는 Windows 런타임 앱이 Microsoft OneDrive 및 Outlook.com 정보에 있는 사용자의 프로필 정보, 파일 및 사진에 액세스할 수 있도록 앱에 Microsoft 서비스의 기능을 추가하는 방법을 알아봅니다. 이 자습서의 단계는 새 앱으로 시작하며, 사용자의 Microsoft 계정에 로그인하고 사용자의 프로필 정보를 가져와서 앱에 표시하는 기능을 추가합니다.
중요 이 항목의 자습서에서는 Windows 스토어 앱에 대해 설명합니다. Windows Phone 스토어 앱에 Microsoft 서비스를 추가할 수도 있습니다. 하지만 Windows Phone 사용자 인터페이스는 플라이아웃을 지원하지 않으므로 Windows Phone 스토어 앱의 페이지를 사용하여 이 항목에서 사용되는 플라이아웃에 대한 기능을 구현해야 합니다.
알아야 할 사항
기술
사전 요구 사항
- Windows 8
- Microsoft Visual Studio
- Live SDK
- Windows 스토어 개발자 계정
- 앱에서 사용할 두 개의 이미지 파일(PNG 형식)
지침
단계 1: 새 앱 프로젝트 생성 및 Live SDK 포함
다음을 수행하여 Visual Studio 템플릿에서 새 앱을 만듭니다.
- Visual Studio의 파일 메뉴에서 **새 프로젝트...**를 클릭합니다.
- 새 프로젝트... 대화 상자에서 설치됨 > 템플릿 > JavaScript > Windows 스토어로 이동합니다.
- 새 앱을 선택합니다.
- 새 앱의 이름과 위치를 입력하고 확인을 클릭합니다.
- 앱을 빌드하고 테스트합니다. 앱이 시작되고 "Content goes here"만 포함된 빈 페이지가 표시되어야 합니다. 이 페이지가 표시되면 앱을 닫고 계속합니다.
단계 2: Windows 스토어 개발자 계정에 앱 추가
앱에서 Live SDK가 액세스하는 클라우드 서비스를 사용하려면 Windows 스토어 개발자 계정에 앱이 등록되어 있어야 합니다. 인증을 위해 Windows 스토어에 앱을 제출할 필요는 없습니다. Windows 스토어 개발자 계정에서 앱의 이름을 입력해야 합니다.
단계 3: 앱 데이터 및 설정 플라이아웃 추가
Live SDK를 사용하는 Windows 스토어 앱은 적어도 다음 두 가지 작업을 수행해야 합니다.
- 가능한 경우 사용자가 Microsoft 계정에 로그인하고 로그아웃할 수 있도록 허용합니다.
- 앱에서 액세스할 개인 데이터를 보호하는 방법을 설명하는 개인 정보 취급 방침을 표시합니다.
로그인 및 로그아웃 환경과 개인 정보 취급 방침에 대한 자세한 내용은 Microsoft 계정 로그인에 대한 요구 사항을 참조하세요.
Windows 스토어 앱에서는 설정 플라이아웃을 통해 액세스합니다.
앱에 플라이아웃을 추가하려면 다음을 수행합니다.
계정 플라이아웃 페이지를 만듭니다.
계정 플라이아웃 파일에 사용할 새 폴더를 만듭니다. 솔루션 탐색기에서 프로젝트 이름을 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 새 폴더를 선택합니다.
새 폴더의 이름을 account로 바꿉니다.
account 폴더를 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 **새 항목...**을 선택합니다.
새 항목 추가 대화 상자에서 설치됨 > JavaScript > Windows 스토어로 이동한 다음 페이지 컨트롤을 선택합니다.
이름 필드에 "account.html"을 입력하고 추가를 클릭합니다.
사용자 앱에 맞게 새 파일을 수정합니다.
account.html 파일에서 <div> 요소를 다음 코드로 바꿉니다.
<div id="account" data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{width: 'narrow'}"> <div class="SettingsPane"> <div class="win-label"> <button onclick="WinJS.UI.SettingsFlyout.show()" class="win-backbutton"> </button> <span class="SettingsTitle">Account</span> </div> <article class="SettingsContent"> <p id="accountPrompt"></p> </article> <div> <!-- define one button to sign in and another to sign out, but show only --> <!-- one at a time, depending on whether the user is currently signed in or not. --> <button id="signInBtn" onclick="signInCmd()" style="display: none">Sign in</button> <button id="signOutBtn" onclick="signOutCmd()" style="display: none">Sign out</button> </div> </div> </div>
account.css 파일의 끝에 다음 코드를 추가합니다.
.account p { margin-left: 120px; } .SettingsPane { margin-top:36px; margin-left:48px; } .SettingsTitle { margin-left: 36px; } .SettingsContent { margin-top: 24px; } #account { background-color: gray ; }
개인 정보 플라이아웃 페이지를 만듭니다.
개인 정보 플라이아웃 파일에 사용할 새 폴더를 만듭니다. 솔루션 탐색기에서 프로젝트 이름을 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 새 폴더를 선택합니다.
새 폴더의 이름을 privacy로 바꿉니다.
privacy 폴더를 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 **새 항목...**을 선택합니다.
새 항목 추가 대화 상자에서 설치됨 > JavaScript > Windows 스토어로 이동한 다음 페이지 컨트롤을 선택합니다.
이름 필드에 "privacy.html"을 입력하고 추가를 클릭합니다.
사용자 앱에 맞게 새 파일을 수정합니다.
privacy.html 파일에서 <div> 요소를 다음 코드로 바꿉니다.
<div id="privacy" data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{width: 'narrow'}"> <div class="SettingsPane"> <div class="win-label"> <button onclick="WinJS.UI.SettingsFlyout.show()" class="win-backbutton"> </button> <span class="SettingsTitle">Privacy</span> </div> <article class="SettingsContent"> <!-- Customize this text to fit your application. --> <h2>How we protect your personal information</h2> <h4>Your privacy statement or a link to your privacy statement goes here.</h4> </article> </div> </div>
사용자 개인 정보 취급 방침을 참조하도록 privacy.html의 콘텐츠를 변경해야 합니다.
account.css 파일의 끝에 다음 코드를 추가합니다.
.privacy p { margin-left: 120px; } .SettingsPane { margin-top:36px; margin-left:48px; } .SettingsTitle { margin-left: 36px; } .SettingsContent { margin-top: 24px; } #privacy { background-color: gray ; }
설정 명령을 추가합니다.
default.js 파일의 app.onactivated 이벤트 처리기에 다음 코드를 추가합니다.
// Define the Settings flyout commands. // The commands appear in the Settings charm from top-to-bottom // in the order they are added. app.onsettings = function (e) { e.detail.applicationcommands = { // Add the Account command "account": { // Location of page content href: "/account/account.html", // Command to show in settings menu title: "Account" }, // Add the privacy command. "privacy": { // Location of page content href: "/privacy/privacy.html", // Command to show in settings menu title: "Privacy" } } // Command to update app's settings menu // using the preceding definition. WinJS.UI.SettingsFlyout.populateSettings(e); }
앱을 빌드하여 실행합니다.
설정 참 메뉴를 엽니다. 계정 및 개인 정보 명령이 설정 창에 표시되는지 확인합니다.
각 명령을 클릭하여 플라이아웃이 열리는지 확인합니다.
두 플라이아웃을 모두 확인한 후 앱을 닫고 계속합니다.
단계 4: UI 콘텐츠 및 데이터 바인딩 추가
앱의 UI에 사용자 Microsoft 계정에 대한 연결의 현재 상태를 나타내려고 합니다. 앱의 UI에 정적 텍스트를 배치하는 대신 데이터 바인딩을 사용하여 해당 데이터 값이 변경될 때 UI 콘텐츠도 변경되도록 합니다.
이 단계에서는 앱의 데이터를 UI에 연결하는 코드를 추가합니다.
UI를 앱 데이터에 연결하는 바인딩 특성이 있는 UI 요소를 포함하도록 default.html을 업데이트합니다.
이렇게 하려면 default.html에서 <body> 태그의 콘텐츠를 다음 코드로 바꿉니다.
<div id="bindingDiv"> <!-- The elements in this div get their data from the app's data object by using a binding object. --> <div class="heading"> <h1> <!-- The app's title. This is configured by the program. --> <span id="titleText" data-win-bind="innerText: person.titleText">person.titleText</span> </h1> </div> <div class="content"> <!-- The app's content. This is a photo for this example. When the user is signed out, one photo is shown; when they are signed in, another is shown. --> <img id="appImage" data-win-bind="src: image.url; title: image.caption" /> <!-- Show the caption as text in the display as well as hover text in the image. --> <p id="appImageCaption" data-win-bind="innerText: image.caption">image.caption</p> </div> </div>
data-win-bind 특성이 있는 태그의 경우 특성에 바인딩된 데이터 필드도 태그 값에 표시됩니다. 이 기능은 디버깅에만 사용됩니다. 바인딩에 성공하면 프로그램의 데이터 값이 이 텍스트를 바꿉니다. 바인딩에 실패하면 UI에 표시되지 않는 데이터 값의 이름이 표시되므로 오류를 디버그하는 데 도움이 될 수 있습니다.
바인딩 개체로 사용할 데이터 개체를 만듭니다.
프로젝트의 js 폴더에 data.js라는 새 파일을 만들고 파일에 코드를 추가합니다. 이렇게 하려면 다음을 수행합니다.
솔루션 탐색기에서 js 폴더를 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 **새 항목...**을 선택합니다.
설치됨 > JavaScript > 코드로 이동한 다음 JavaScript 파일을 선택합니다.
이름 필드에 "data.js"를 입력하고 추가를 클릭합니다.
data.js의 콘텐츠를 다음 예제의 코드로 바꿉니다.
(function () { "use strict"; // The global data object used to reference the binding object WinJS.Namespace.define("binding", { Person: null } ); // Static text WinJS.Namespace.define("display", { state: ["Some nice photo", "'s favorite photo"] } ); // The app's data object that is used to map the // sign-in state to the UI. WinJS.Namespace.define("appInfo", { index: 0, // The sign-in state. image: { // The image to show url: "/images/SignedOutImage.png", caption: "Something not so special." }, person: { // The info about the user userName: null, titleText: display.state[0] } } ); })();
default.js를 참조하는 <script> 태그 앞에 다음 코드를 입력하여 default.html에 새 파일에 대한 참조를 추가합니다.
<!-- The app's data definition --> <script src="/js/data.js"></script>
데이터 개체를 UI에 바인딩하는 코드를 추가합니다.
default.js의 app.onactivated 이벤트 처리기에 다음 코드를 추가하여 바인딩 개체를 만들고 초기화합니다.
// Create the binding object that connects the appInfo data // to the app's UI. binding.Person = WinJS.Binding.as(appInfo); // Update the binding object so that it reflects the state // of the app and updates the UI to reflect that state. getInfoFromAccount(binding.Person);
이벤트 처리기를 추가하여 앱의 문서가 로드된 후 바인딩 개체의 데이터로 UI를 업데이트합니다.
default.js의 app.oncheckpoint 할당 바로 앞에 다음 이벤트 처리기를 추가합니다.
app.onloaded = function (args) { // Initialize the UI to match the corresponding data object. var div = document.getElementById("bindingDiv"); WinJS.Binding.processAll(div, appInfo); }
앱 데이터를 사용자 데이터와 동기화하는 함수를 추가합니다.
이 단계에서는 이 함수가 테스트를 위한 정적 데이터만 제공합니다. Microsoft 계정에서 사용자 데이터를 가져오는 함수는 이후 단계에서 추가됩니다.
default.js의 익명 함수 뒤에 다음 함수를 추가하여 앱의 다른 모듈에 함수가 표시되도록 합니다.
function getInfoFromAccount(p) { // Test for a parameter and assign the unbound data object // if a parameter wasn't passed. This doesn't refresh the binding // object, but it does keep the data object coherent with the // sign-in state. if (undefined === p) { p = appInfo; } if (0 == p.index) { // The program executes this branch when the user is // not signed in. // Set the data to the signed-out state values // and update the app title. p.person.userName = null; p.person.titleText = display.state[p.index]; // These elements are the default values to show // when the user is not signed in. p.image.url = "/images/SignedOutImage.png"; p.image.caption = "Something not so special."; } if (1 == p.index) { // The program executes this branch when the user is // signed in. // Set the data to the signed-in state, // get the user's first name, and update the app title. p.person.userName = "Bob"; p.person.titleText = p.person.userName + display.state[p.index]; // These elements would normally be read from the user's data, // but in this example, app resources are used. p.image.url = "/images/SignedInImage.png"; p.image.caption = "Something special to me."; } }
이미지 파일을 추가합니다.
프로젝트의 images 폴더에 이미지 파일 두 개를 복사합니다. 한 이미지의 이름은 "SignedOutImage.png"로 바꾸고 다른 이미지의 이름은 "SignedInImage.png"로 바꿉니다.
솔루션 탐색기에서 images 폴더를 마우스 오른쪽 단추로 클릭하고 추가 > **기존 항목...**을 선택합니다.
방금 추가한 이미지 파일 두 개를 선택하고 추가를 클릭합니다.
앱을 빌드하고 테스트합니다. 페이지에 올바른 텍스트와 SignedOutImage.png 이미지가 표시되는 경우 다음 단계를 계속합니다.
앱에 텍스트 대신 데이터 필드의 이름이 표시되는 경우 데이터 바인딩에 문제가 있는 것이며, 계속하기 전에 문제를 해결해야 합니다.
단계 5: 바인딩 개체를 사용하도록 계정 플라이아웃 업데이트
account.html에서 로그인 상태를 사용하여 플라이아웃에 올바른 단추를 표시하도록 <button> 태그를 다음과 같이 변경합니다.
<button id="signInBtn" onclick="signInCmd(binding.Person)" style="display:none">Sign in</button> <button id="signOutBtn" onclick="signOutCmd(binding.Person)" style="display:none">Sign out</button>
account.js의 익명 함수 뒤에 이 함수를 추가합니다.
function updateDisplay(p) { // Update the display to show the caption text and button // that apply to the current sign-in state. // Test for a parameter and assign the unbound global data object // if a parameter wasn't passed. This doesn't refresh the screen // but it does keep the data object coherent. if (undefined === p) { p = appInfo; } // Get the elements in the display for this function to update. var prompt = document.getElementById("accountPrompt"); var inBtn = document.getElementById("signInBtn"); var outBtn = document.getElementById("signOutBtn"); // Update the elements to show the correct text and button for the // the sign-in state. if (0 == p.index) { // The user is signed out, so prompt them to sign in. prompt.innerText = "Sign in to see your favorite photo." outBtn.style.display = "none"; inBtn.style.display = "block"; } else { // The user is signed in so welcome them and show the sign-out button. prompt.innerText = "Welcome, " + p.person.userName + "!" inBtn.style.display = "none"; outBtn.style.display = "block"; } } function signInCmd(p) { // Sign the new user in. // This call closes the Flyout and Settings charm. SignInNewUser(p); // Update the display to the signed-in state but keep the Flyout open // in case they want to sign in again. updateDisplay(p); // Return to the Settings flyout. WinJS.UI.SettingsFlyout.show(); } function signOutCmd(p) { // Sign the current user out. SignOutUser(p); // Update the display to the signed-out state but keep the Flyout open // in case they want to sign in again. updateDisplay(p); // Return to the Settings flyout. WinJS.UI.SettingsFlyout.show(); }
다음 예제와 마찬가지로 updateDisplay 호출이 포함되도록 WinJS.UI.Pages.define 호출의 ready case 함수를 수정합니다.
ready: function (element, options) { // TODO: Initialize the page here. // Update the Account Flyout to reflect // the user's current sign-in state. updateDisplay(binding.Person); },
사용자를 해당 Microsoft 계정에 로그인하고 로그아웃하는 함수를 default.js에 추가합니다.
이 함수는 아직 Windows Live 서비스 기능을 조작하지 않습니다. 이 기능은 이후 단계에서 추가됩니다. 이러한 기능을 통해 바인딩 개체를 테스트하여 개체가 두 로그인 상태에서 모두 작동하고 로그인 상태가 변경될 때 UI를 업데이트하는지 확인할 수 있습니다.
function SignInNewUser(p) { // Sign the user in. // Test for a parameter and assign the unbound global data object // if a parameter wasn't passed. This doesn't refresh the screen // but it does keep the data object coherent. if (undefined === p) { p = appInfo; } p.index = 1; getInfoFromAccount(p); } function SignOutUser(p) { // Sign the user out. // Test for a parameter and assign the unbound global data object // if a parameter wasn't passed. This doesn't refresh the screen // but it does keep the data object coherent. if (undefined === p) { p = appInfo; } p.index = 0; getInfoFromAccount(p); }
앱을 빌드하고 테스트합니다.
앱이 시작되고 SignedOutImage.png를 표시합니다.
설정 참 메뉴를 열고 계정 명령을 선택합니다. 로그인 단추 및 프롬프트가 표시되는지 확인합니다.
로그인 단추를 클릭하고 앱 상태와 계정 플라이아웃 콘텐츠가 로그인 상태에 따라 변경되는지 확인합니다.
계정 플라이아웃에서 로그아웃 단추 및 프롬프트가 표시되는지 확인합니다.
로그아웃 단추를 클릭하고 앱 상태와 계정 플라이아웃 콘텐츠가 로그아웃 상태에 따라 변경되는지 확인합니다.
앱이 이런 방식으로 작동하면 Windows Live 서비스 기능을 추가할 준비가 완료되었습니다.
단계 6: Live SDK 함수 추가
Live SDK에 대한 참조를 앱에 추가합니다.
솔루션 탐색기에서 참조를 마우스 오른쪽 단추로 클릭하고 **참조 추가...**를 선택합니다.
Windows > 확장으로 이동하고 Live SDK를 선택한 다음 확인을 클릭합니다.
default.html의 <head> 태그에서 default.css 링크 앞에 다음 줄을 추가합니다.
<!-- The Live SDK --> <script src="///LiveSDKHTML/js/wl.js"></script>
Live SDK를 초기화합니다.
default.js에서 app.onactivated 처리기의 binding.Person 할당 뒤에 다음 코드를 입력합니다.
// Initialize the Live SDK. WL.init();
앱이 사용자의 Microsoft 계정에서 사용자 로그인 상태를 가져오도록 getInfoFromAccount 함수를 업데이트합니다.
default.js의 getInfoFromAccount에서 p.index를 테스트하는 두 개의 if 문을 다음 코드로 바꿉니다.
// Call the user's Microsoft account to get the identity of the current // user. If the user is signed in, the success branch runs. // If the user is not signed in, the failure branch runs. WL.api({ path: "me", method: "GET" }).then( function (response) { // The program executes this branch when the user is // signed in. // Save the app's sign-in state. p.index = 1; // Set the data to the signed-in state, // get the user's first name, and update the app title. p.person.userName = response.first_name; p.person.titleText = p.person.userName + display.state[p.index]; // These elements would normally be read from the user's data, // but in this example, app resources are used. p.image.url = "/images/SignedInImage.png"; p.image.caption = "Something special to me."; }, function (responseFailed) { // The program executes this branch when the user is // not signed in. // Reset the app state. p.index = 0; // Set the data to the signed-out state values // and update the app title. p.person.userName = null; p.person.titleText = display.state[p.index]; // These elements are the default values to show // when the user is not signed in. p.image.url = "/images/SignedOutImage.png"; p.image.caption = "Something not so special."; } );
사용자를 해당 Microsoft 계정에 로그인하도록 SignInNewUser 함수를 업데이트합니다.
default.js의 SignInNewUser에서 매개 변수 테스트 뒤에 있는 코드를 다음 코드로 바꿉니다.
// Sign the user in with the minimum scope necessary for the app. WL.login({ scope: ["wl.signin"] }).then(function (response) { getInfoFromAccount(p); });
SignOutUser 함수를 업데이트합니다.
default.js의 SignOutUser에서 매개 변수 테스트 뒤에 있는 코드를 다음 코드로 바꿉니다.
// Sign out and then refresh the app's data object. WL.logout().then(function (response) { getInfoFromAccount(p); });
ShowSignOutButton 함수를 추가합니다.
default.js의 끝에 여기 표시된 ShowSignOutButton 함수를 추가합니다.
function ShowSignOutButton() { // Return true or false to indicate whether the user // can sign out of the app. return (WL.canLogout()); }
사용자가 로그아웃할 수 있는지 여부에 대한 테스트를 추가합니다. 사용자가 Microsoft 계정과 연결된 컴퓨터 계정에서 해당 앱에 로그인하는 경우에는 앱에서 로그아웃할 수 없습니다. 이 함수는 사용자에게 올바른 프롬프트가 표시될 수 있도록 이 조건을 테스트합니다.
account.js의 updateDisplay 함수에서 if 문을 다음 코드로 바꿉니다. 로그아웃 단추를 표시할지 여부를 나타내기 위해 추가된 테스트를 확인합니다.
if (0 == p.index) { // The user is signed out, so prompt them to sign in. prompt.innerText = "Sign in to see your favorite photo." outBtn.style.display = "none"; inBtn.style.display = "block"; } else { // The user is signed in, so welcome them. // If the user can sign out, show them the sign-out button. var promptText = "Welcome, " + p.person.userName + "!"; var signOutBtnStyle = "block"; if (ShowSignOutButton()) { // The user is signed in and can sign out later, // so welcome them and show the sign-out button. signOutBtnStyle = "block"; } else { // The user is signed in and can't sign out later, // so welcome them and hide the sign-out button. promptText = promptText + " The app is signed in through your Windows 8 account." signOutBtnStyle = "none"; } prompt.innerText = promptText; outBtn.style.display = signOutBtnStyle; inBtn.style.display = "none" }
이전 테스트에 사용된 더미 코드를 제거합니다.
account.js의 signInCmd에서 updateDisplay 및 WinJS.UI.SettingsFlyout.show 호출을 제거하여 다음 줄이 해당 함수의 유일한 코드 줄이 되도록 합니다.
// Sign the new user in. // This call closes the Flyout and Settings charm. SignInNewUser(p);
account.js의 signOutCmd에서 updateDisplay 호출을 제거하여 다음 줄이 해당 함수의 유일한 코드 줄이 되도록 합니다.
// Sign the current user out. SignOutUser(p); // Return to the Settings flyout. WinJS.UI.SettingsFlyout.show();
이제 Microsoft 계정으로 앱을 테스트할 준비가 되었습니다.
단계 7: 앱 테스트
앱을 빌드하여 실행하고 이러한 작업을 테스트합니다.
Microsoft 계정에 로그인을 테스트합니다.
앱이 사용자 Microsoft 계정에서 로그아웃된 상태로 시작하여 다음 단계를 시도합니다.
- 설정 플라이아웃을 열고 계정 명령을 선택한 다음 로그인을 클릭합니다.
- Microsoft 계정으로 로그인합니다. 앱에서 계속할 것인지 확인하는 메시지가 표시되면 예를 클릭합니다.
- 앱의 텍스트와 사진이 로그인한 텍스트와 사진으로 변경되는지 확인합니다.
앱에서 로그아웃을 테스트합니다.
참고 Microsoft 계정과 연결된 컴퓨터 계정에서 앱을 테스트하는 경우 로그아웃 단추를 사용할 수 없습니다. 이것은 예상된 동작입니다. 이 테스트를 실행하려면 Microsoft 계정과 연결되지 않은 컴퓨터 계정에서 앱을 실행해야 합니다.
앱이 사용자 Microsoft 계정에 로그인된 상태로 시작하여 다음 단계를 시도합니다.
- 설정 플라이아웃을 열고 계정 명령을 선택한 다음 로그아웃을 클릭합니다.
- 앱의 텍스트와 사진이 로그아웃한 텍스트와 사진으로 변경되는지 확인합니다.
Single Sign-On을 테스트합니다.
Single Sign-On은 컴퓨터 계정을 Microsoft 계정에 연결할 수 있게 해주는 Windows의 기능입니다. 이러한 컴퓨터 계정에서 앱을 실행하는 경우 앱이 위에서 설명한 것과 다르게 동작합니다.
예를 들면 다음과 같습니다.
- 앱이 자동으로 로그인한 상태로 시작될 수 있습니다.
- 앱 내에서 계정을 로그아웃할 수 없으므로 계정 플라이아웃에 로그아웃 단추가 표시되지 않습니다.
- 계정 플라이아웃은 앱에서 로그아웃할 수 없다는 추가 메시지를 표시합니다.