AWS 시작하기
Android 애플리케이션 구축
AWS Amplify를 사용하여 간단한 Android 애플리케이션 만들기
모듈 3: 인증 추가
이 모듈에서는 Amplify CLI와 라이브러리를 사용하여 인증을 구성하고 앱에 추가합니다.
소개
다음으로 추가할 기능은 사용자 인증입니다. 이 모듈에서는 관리형 사용자 자격 증명 공급자인 Amazon Cognito를 사용하여 Amplify CLI와 라이브러리에서 사용자를 인증하는 방법을 알아봅니다.
또한 Cognito를 통해 호스팅되는 사용자 인터페이스를 사용하여 단 몇 줄의 코드로 사용자 가입, 로그인, 암호 재설정을 허용하는 전체 사용자 인증 흐름을 제공하는 방법도 배웁니다.
“호스팅되는 사용자 인터페이스”를 사용한다는 것은 애플리케이션의 사용자 로그인 및 가입 인터페이스 흐름에 Cognito 웹 페이지를 활용한다는 것을 의미합니다. 앱의 사용자는 Cognito를 통해 호스팅되는 웹 페이지로 리디렉션되며 로그인 후 다시 앱으로 리디렉션됩니다. 물론, Cognito와 Amplify는 기본 UI도 지원합니다. 이 워크숍 지침에서 사용자 지정 인증 UI에 대해 자세히 알아볼 수 있습니다.
배우게 될 내용
- 인증 서비스 생성 및 배포
- Cognito 호스팅 UI 인증을 포함하도록 Android 앱 구성
주요 개념
Amplify 라이브러리 – Amplify 라이브러리는 웹 또는 모바일 애플리케이션에서 AWS 서비스와 상호 작용할 수 있도록 해줍니다.
인증 – 소프트웨어에서 인증이란 인증 서비스 또는 API를 사용하여 사용자의 자격 증명을 확인하고 관리하는 프로세스를 의미합니다.
완료 시간
10분
사용되는 서비스
구현
-
인증 서비스 생성
인증 서비스를 생성하려면 터미널을 열고 다음 명령을 실행합니다.
amplify add auth ? Do you want to use the default authentication and security configuration? Select Default configuration with Social Provider and press enter ? How do you want users to be able to sign in? Select the default Username and press enter ? Do you want to configure advanced settings? Select the default No, I am done and press enter ? What domain name prefix do you want to use? Select the default and press enter Enter your redirect signin URI: type gettingstarted:// and press enter ? Do you want to add another redirect signin URI? Select the default N and press enter Enter your redirect signout URI: type gettingstarted:// and press enter ? Do you want to add another redirect signout URI? Select the default N and press enter ? Select the social providers you want to configure for your user pool: do not select any provider and press enter
다음 메시지가 표시되면 성공적으로 구성된 것입니다(정확한 리소스 이름은 다름).
Successfully added resource androidgettingstartedfc5a4717 locally
-
인증 서비스 배포
인증 서비스가 로컬로 구성되었으므로, 이제 클라우드에 배포할 수 있습니다. 터미널에서 프로젝트 디렉터리로 이동하여 다음 명령을 실행합니다.
amplify push # press Y when asked to continue
잠시 후에 다음 메시지가 표시됩니다.
✔ All resources are updated in the cloud Hosted UI Endpoint: https://androidgettingstarted-dev.auth.eu-central-1.amazoncognito.com/ Test Your Hosted UI Endpoint: https://androidgettingstarted-dev.auth.eu-central-1.amazoncognito.com/login?response_type=code&client_id=1234567890&redirect_uri=gettingstarted://
-
프로젝트에 Amplify 인증 라이브러리 추가
코드 작업을 시작하기 전에 Android Studio로 돌아가 이전에 추가한 다른 `amplifyframework` 구현과 함께 모듈의 `build.gradle`에 다음 종속 구성 요소를 추가하고 메시지가 표시되면 [지금 동기화(Sync Now)]를 클릭합니다.
dependencies { ... // Amplify core dependency implementation 'com.amplifyframework:core:1.4.0' implementation 'com.amplifyframework:aws-auth-cognito:1.4.0' }
-
런타임 시 Amplify 인증 라이브러리 구성
Android Studio로 돌아가 Backend.kt 파일을 엽니다. Backend 클래스에서, 이전 섹션에서 initialize() 메서드에 추가한 amplify 초기화 코드에 한 줄을 추가합니다.
완성된 코드 블록은 다음과 같습니다.
// inside Backend class fun initialize(applicationContext: Context) : Backend { try { Amplify.addPlugin(AWSCognitoAuthPlugin()) Amplify.configure(applicationContext) Log.i(TAG, "Initialized Amplify") } catch (e: AmplifyException) { Log.e(TAG, "Could not initialize Amplify", e) } return this }
Android Studio가 import 문을 자동으로 추가합니다(Mac에서는 코드 편집기에서 탐지된 각 오류에서 Alt + Enter 클릭).
이전 단계와 마찬가지로, 누락된 각 클래스 정의에 필요한 import 문을 추가합니다(빨간색 단어에서 Alt + Enter 누름).
정상적으로 작동하는지 확인하기 위해 프로젝트를 빌드합니다. [빌드(Build)] 메뉴를 클릭하고 [프로젝트 만들기(Make Project)]를 선택하거나 Mac에서 ⌘F9을 입력합니다. 오류가 발생하지 않아야 합니다.
-
런타임 시 인증 트리거
나머지 코드 변경은 사용자의 상태(로그인 여부)를 추적하고, 사용자가 자물쇠 아이콘을 클릭하면 SignIn/SignUp 사용자 인터페이스를 트리거합니다.
a. signIn 및 signOut 메서드를 추가합니다.Backend 클래스의 아무 곳에나 다음 네 가지 메서드를 추가합니다.private fun updateUserData(withSignedInStatus : Boolean) { UserData.setSignedIn(withSignedInStatus) } fun signOut() { Log.i(TAG, "Initiate Signout Sequence") Amplify.Auth.signOut( { Log.i(TAG, "Signed out!") }, { error -> Log.e(TAG, error.toString()) } ) } fun signIn(callingActivity: Activity) { Log.i(TAG, "Initiate Signin Sequence") Amplify.Auth.signInWithWebUI( callingActivity, { result: AuthSignInResult -> Log.i(TAG, result.toString()) }, { error: AuthException -> Log.e(TAG, error.toString()) } ) }
그런 다음 누락된 각 클래스 정의에 필요한 import 문을 추가합니다(빨간색 단어에서 Alt + Enter 누름). 여러 클래스를 선택할 수 있는 경우 아래 스크린샷과 같이 Amplify 패키지에서 선택해야 합니다.
이러한 메서드에서 UserData.isSignedIn 플래그를 업데이트하지 않으며, 이 작업은 다음 섹션에서 수행합니다.
b. 인증 허브 리스너 추가
인증 상태의 변경 사항을 추적하기 위해 Amplify가 전송한 인증 이벤트를 구독하는 코드를 추가합니다. Backend.initialize() 메서드의 허브를 초기화합니다.
인증 이벤트가 수신되면 updateUserData() 메서드를 호출합니다. 이 메서드는 UserData 객체를 동기화합니다. UserData.isSignedIn 속성은 LiveData<Boolean>입니다. 즉, 값이 변경되면 이 속성을 구독하는 옵저버에게 알립니다. 이 메커니즘을 사용하여 사용자 인터페이스를 자동으로 새로 고칩니다. Android 문서에서 LiveData에 대해 자세히 알아볼 수 있습니다.
애플리케이션 시작 시간에 이전 인증 상태를 확인하는 코드도 추가합니다. 이 코드는 애플리케이션이 시작될 때 Cognito 세션이 있는지 확인하고 UserData를 적절히 업데이트합니다.
Backend.initialize()에서 try/catch 블록 뒤, 그리고 return 문 앞에 다음 코드를 추가합니다.
// in Backend.initialize() function, after the try/catch block but before the return statement Log.i(TAG, "registering hub event") // listen to auth event Amplify.Hub.subscribe(HubChannel.AUTH) { hubEvent: HubEvent<*> -> when (hubEvent.name) { InitializationStatus.SUCCEEDED.toString() -> { Log.i(TAG, "Amplify successfully initialized") } InitializationStatus.FAILED.toString() -> { Log.i(TAG, "Amplify initialization failed") } else -> { when (AuthChannelEventName.valueOf(hubEvent.name)) { AuthChannelEventName.SIGNED_IN -> { updateUserData(true) Log.i(TAG, "HUB : SIGNED_IN") } AuthChannelEventName.SIGNED_OUT -> { updateUserData(false) Log.i(TAG, "HUB : SIGNED_OUT") } else -> Log.i(TAG, """HUB EVENT:${hubEvent.name}""") } } } } Log.i(TAG, "retrieving session status") // is user already authenticated (from a previous execution) ? Amplify.Auth.fetchAuthSession( { result -> Log.i(TAG, result.toString()) val cognitoAuthSession = result as AWSCognitoAuthSession // update UI this.updateUserData(cognitoAuthSession.isSignedIn) when (cognitoAuthSession.identityId.type) { AuthSessionResult.Type.SUCCESS -> Log.i(TAG, "IdentityId: " + cognitoAuthSession.identityId.value) AuthSessionResult.Type.FAILURE -> Log.i(TAG, "IdentityId not present because: " + cognitoAuthSession.identityId.error.toString()) } }, { error -> Log.i(TAG, error.toString()) } )
정상적으로 작동하는지 확인하기 위해 프로젝트를 빌드합니다. [빌드(Build)] 메뉴를 클릭하고 [프로젝트 만들기(Make Project)]를 선택하거나 Mac에서 ⌘F9을 입력합니다. 오류가 발생하지 않아야 합니다.
c. 사용자 인터페이스 코드 업데이트
마지막 코드 변경은 사용자 인터페이스와 관련된 것으로, 기본 작업에 FloatingActionButton을 추가합니다.
res/layout에서 activity_main.xml을 열어 기존 FloatingActionButton을 다음으로 바꿉니다.
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fabAuth" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@drawable/ic_baseline_lock" app:fabCustomSize="60dp" app:fabSize="auto" />
res/drawable 아래에 자물쇠 아이콘을 추가합니다. drawable을 마우스 오른쪽 버튼으로 클릭하고 [새로 만들기(New)], [벡터 자산(Vector Asset)]을 선택합니다. 클립아트에서 자물쇠 아이콘을 선택하고 ic_baseline_lock(_24 제외)을 이름으로 입력하고 클립아트에서 닫힌 자물쇠 아이콘을 선택합니다. [다음(Next)]과 [마침(Finish)]을 차례로 클릭합니다.
열린 자물쇠 아이콘에 대해 동일한 작업을 반복합니다.
그러면 drawable 디렉토리에 다음 파일이 있는 것을 확인할 수 있습니다.
이제 코드에서 새로 만든 버튼을 연결합니다. java/com.example.androidgettingstarted/에서 MainActivity.kt를 열고 다음 코드를 추가합니다.
// anywhere in the MainActivity class private fun setupAuthButton(userData: UserData) { // register a click listener fabAuth.setOnClickListener { view -> val authButton = view as FloatingActionButton if (userData.isSignedIn.value!!) { authButton.setImageResource(R.drawable.ic_baseline_lock_open) Backend.signOut() } else { authButton.setImageResource(R.drawable.ic_baseline_lock_open) Backend.signIn(this) } } }
아직 MainActivity에 있는 상태로 onCreate() 메서드의 끝에 다음 코드를 추가합니다.
setupAuthButton(UserData) UserData.isSignedIn.observe(this, Observer<Boolean> { isSignedUp -> // update UI Log.i(TAG, "isSignedIn changed : $isSignedUp") if (isSignedUp) { fabAuth.setImageResource(R.drawable.ic_baseline_lock_open) } else { fabAuth.setImageResource(R.drawable.ic_baseline_lock) } })
위의 코드는 Userdata.isSignedIn 값에 옵저버를 등록합니다. isSignedIn 값이 변경되면 닫기가 호출됩니다. 지금은 자물쇠 아이콘만 변경합니다. 사용자가 인증되면 자물쇠가 열리고, 사용자의 세션이 없으면 자물쇠가 닫힙니다.
정상적으로 작동하는지 확인하기 위해 프로젝트를 빌드합니다. [빌드(Build)] 메뉴를 클릭하고 [프로젝트 만들기(Make Project)]를 선택하거나 Mac에서 ⌘F9을 입력합니다. 오류가 발생하지 않아야 합니다.
d. AndroidManifest.xml 및 MainActivity 업데이트
마지막으로, Cognito 호스팅 사용자 인터페이스로 제공되는 웹 인증 시퀀스의 끝에 앱이 시작되는지 확인해야 합니다. 매니페스트 파일에 새 작업을 추가합니다. gettingstarted URI 스키마가 수신되면 이 작업이 호출됩니다.
Android Studio의 매니페스트에서 AndroidManifest.xml을 열고 아래의 작업을 애플리케이션 요소 내부에 추가합니다.
<activity android:name="com.amazonaws.mobileconnectors.cognitoauth.activities.CustomTabsRedirectActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="gettingstarted" /> </intent-filter> </activity>
Java/com.example.androidgettingstarted/에서 MainActivity.kt를 열고 다음 코드를 클래스의 아무 곳에나 추가합니다.
// MainActivity.kt // receive the web redirect after authentication override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) Backend.handleWebUISignInResponse(requestCode, resultCode, data) }
Java/com.example.androidgettingstarted/에서 Backend.kt를 열고 다음 코드를 클래스의 아무 곳에나 추가합니다.
// Backend.kt // pass the data from web redirect to Amplify libs fun handleWebUISignInResponse(requestCode: Int, resultCode: Int, data: Intent?) { Log.d(TAG, "received requestCode : $requestCode and resultCode : $resultCode") if (requestCode == AWSCognitoAuthPlugin.WEB_UI_SIGN_IN_ACTIVITY_CODE) { Amplify.Auth.handleWebUISignInResponse(data) } }
e. 구축 및 테스트
정상적으로 작동하는지 확인하기 위해 프로젝트를 빌드하고 실행합니다. 도구 모음에서 [실행(Run)] 아이콘 ▶을 클릭하거나 ^ R을 입력합니다. 오류가 발생하지 않아야 합니다. 앱이 시작되고 화면 오른쪽 하단에 잠긴 자물쇠 플로팅 버튼이 표시됩니다.
전체 등록 흐름은 다음과 같습니다.