이 글은 AWS Amplify 개발 도구를 사용하여 AWS 클라우드 기반 Android 모바일 앱을 제작하는 방법에 대한 실습 시리즈입니다.
여러분이 제일 좋아하는 애완 동물 목록을 표시하기 위한 Android 모바일 앱을 개발한다고 가정해 봅시다. 사용자 경험을 높여줄 프론트 엔드 사용자 경험에 집중 하기 위해 중요한 기능 중 하나인 애완 동물 데이터를 나열, 생성 및 저장하는 API를 설정하는 백엔드 서버 인프라를 관리 및 운영하고 싶지는 않습니다. 또한, 각 사용자가 자신의 애완 동물을 추가할 수 있도록 사용자 인증 기능을 별도로 구현하지 않고 손쉽게 설정하고 싶습니다.
즉, 오로지 Android 앱 개발 작업에 집중하고 싶다면, 어떻게 해야 할까요? 이 글에서는 이를 위해 AWS Amplify를 사용하여 손쉽게 서버리스 모바일 앱을 개발하기 위한 제작하기 단계별 실습 방법을 살펴 보겠습니다.
마지막으로 네트워크 호출 및 오프라인 상태에 대한 <uses-permissions>에 대한 업데이트를 통해 AndroidManifest.xml을 업데이트합니다. 또한 구독을 사용할 수 있도록 MqttService에 <application> 아래에 <service> 항목을 추가하십시오.
이제 AWS Amplify CLI를 설치하고 Android 프로젝트와 통합하여 Amplify CLI 도구 체인을 최대한 활용할 수 있습니다.
AWS Amplify CLI 설치
터미널을 열고 명령줄에서 다음을 실행합니다. 이미 AWS Amplify CLI를 설치한 경우 명령을 다시 실행하여 최신 업데이트를 받으십시오.
npm install -g @aws-amplify/cli -update
AWS Amplify 프로젝트 초기화
이제 Android 앱을 위한 새로운 AWS Amplify 프로젝트를 초기화해 봅시다.
cd 터미널 창에서 Android Studio 프로젝트 루트로 이동하고 다음을 실행합니다.
amplify init
각 항목에 대해 다음을 입력하십시오.
기본 편집기를 선택: Visual Studio 코드(또는 좋아하는 편집기)
android처럼 제작 중인 앱 유형을 선택하십시오.
Res 디렉토리의 위치: (app/src/main/res): 기본값을 사용하려면 Enter를 누릅니다.
AWS 프로파일을 사용하시겠습니까? 네.
사용할 프로파일을 선택: 기본값
AWS CloudFormation은 앱을 지원하는 초기 인프라입니다. 작업이 완료되면 AWS Amplify CLI 도구 체인이 새 프로젝트를 초기화하고 앱의 프로젝트 디렉토리에 amplify 및 .amplifyrc와 같은 몇 가지 새로운 파일과 폴더가 표시됩니다. 이 파일은 프로젝트의 구성을 유지합니다.
GraphQL API 추가, 인증 추가 및 클라이언트 코드 생성
AWS Amplify 도구 체인은 API를 만들고, 인증을 추가하고, 클라이언트 코드를 생성하는 능률적인 프로세스를 제공합니다. 먼저 앱의 루트 디렉터리에서 다음 명령을 실행해 봅시다.
amplify add api
각 항목에 대해 다음을 입력하십시오.
위에서 언급한 서비스 중 하나를 선택: GraphQL
API 이름을 입력: AmplifyAndroid
API의 인증 유형을 선택: Amazon Cognito User Pool
기본 인증 및 보안 구성을 사용하시겠습니까? 예, 기본 구성을 사용합니다.
주석이 달린 GraphQL 스키마가 있습니까? 아니요.
가이드 스키마 생성을 원하십니까? 네.
프로젝트를 가장 잘 설명하는 형태는 다음과 같습니다. (예 : ID, 이름, 설명이 포함된 “Todo”)
지금 스키마를 편집하시겠습니까? (네/아니요) 네.
메시지가 나타나면 스키마를 다음으로 업데이트합니다.
type Pet @model {
id: ID!
name: String!
description: String
}
쿼리, 변형 및 구독으로 이뤄진 파일 이름 패턴을 입력하십시오(app/src/main/graphql/**/*.graphql): 기본값을 그대로 사용하려면 Enter를 누릅니다.
가능한 모든 GraphQL 연산(쿼리, 변형 및 구독)의 생성 및 업데이트 여부(네/아니요) 네.
AWS CloudFormation이 재실행되어 새로 생성된 API 및 인증 메커니즘을 AWS 계정으로 업데이트합니다. 이 프로세스에는 몇 분이 소요될 수 있습니다.
생성된 새 AWS AppSync API를 보려면 언제든지 https://console.aws.amazon.com/appsync의 대시보드로 이동하십시오. 또한 AWS 리전이 올바르게 설정되었는지 확인하십시오.
생성된 새로운 Amazon Cognito 사용자 인증을 보려면 언제든지 https://console.aws.amazon.com/cognito/의 대시보드로 이동하십시오. 또한 AWS 리전이 올바르게 설정되었는지 확인하십시오.
AWS CloudFormation이 클라우드의 리소스 업데이트를 완료하면 GraphQL API 엔드포인트가 생성되고 생성된 GraphQL 문을 프로젝트에서 사용할 수 있습니다.
API가 투명하고 API를 곧바로 사용할 수는 있지만 app/src/main/graphql/com/amazonaws/amplify/generated/graphql하의 Android Studio에서 새로 생성된 GraphQL 쿼리, 변형 구독을 검토할 수 있습니다.
Android 앱 제작
백엔드는 준비되었습니다. 이제 Android 앱에서 사용해 봅시다.
시작하기 전에 자동 가져오기 기능을 켜야 합니다. 많은 라이브러리를 사용 중입니다! 이를 위해서는 Preferences -> Editor -> General -> Auto import를 여십시오. 그런 다음 즉시 명확한 가져오기 추가를 선택하십시오.
프로젝트를 제작하고 실행하여 클라이언트 코드 생성 프로세스를 시작하십시오. 이 gradle 빌드 프로세스는 즉시 사용할 수 있는 모든 네이티브 객체 유형을 만듭니다. 다음 스크린샷과 같이 빈 앱을 볼 수 있어야 합니다.
궁금한 점이 있으면 프로젝트 보기로 전환하고 app/build/generated/source/appsync/com/amazonaws/amplify/generated/graphql/을 검색하여 생성된 모든 객체 유형, 쿼리, 변형 및 구독 Java 클래스를 검사합니다.
인증 추가
앞서 인증을 위해 Amazon Cognito 사용자 풀을 사용하도록 앱을 구성 했으므로 인증을 앱에 통합해야 합니다. 작업 편의를 고려해 Amazon Cognito 인증을 위해 AWS Mobile 라이브러리에 내장된 로그인 UI를 활용할 것입니다.
앱의 build.gradle을 열고 다음과 같은 종속성을 추가합니다.
// Mobile Client for initializing the SDK
implementation('com.amazonaws:aws-android-sdk-mobile-client:2.8.+@aar') { transitive = true }
// Cognito UserPools for SignIn
implementation('com.amazonaws:aws-android-sdk-auth-userpools:2.8.+@aar') { transitive = true }
// Sign in UI Library
implementation('com.amazonaws:aws-android-sdk-auth-ui:2.8.+@aar') { transitive = true }
앱 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 New -> Activity -> Empty Activity를 선택합니다. AuthenticationActivity 활동의 이름을 지정하고 Launcher Activity 확인란을 선택한 다음 Finish을 클릭합니다.
AuthenticationActivity.java 클래스에서 클래스를 다음과 같이 수정하십시오.
public class AuthenticationActivity extends AppCompatActivity {
private final String TAG = AuthenticationActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authentication);
AWSMobileClient.getInstance().initialize(getApplicationContext(), new Callback<UserStateDetails>() {
@Override
public void onResult(UserStateDetails userStateDetails) {
Log.i(TAG, userStateDetails.getUserState().toString());
switch (userStateDetails.getUserState()){
case SIGNED_IN:
Intent i = new Intent(AuthenticationActivity.this, MainActivity.class);
startActivity(i);
break;
case SIGNED_OUT:
showSignIn();
break;
default:
AWSMobileClient.getInstance().signOut();
showSignIn();
break;
}
}
@Override
public void onError(Exception e) {
Log.e(TAG, e.toString());
}
});
}
private void showSignIn() {
try {
AWSMobileClient.getInstance().showSignIn(this,
SignInUIOptions.builder().nextActivity(MainActivity.class).build());
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
이제 AuthenticationActivity가 우리의 시작 관리자 활동인지 확인해 봅시다. AndroidManifest.xml을 열고 AuthenticationActivity에 대해 <intent-filter> 블록이 다음과 같이 지정되었는지 확인하십시오. MainActivity를 위해 <intent-filter> 및 android:theme을 제거해야 합니다.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
에뮬레이터에서 앱을 제작하고 시작하십시오. 로그인 UI는 다음과 같이 표시될 것입니다.
이제 사용자를 추가하겠습니다. 에뮬레이터에서 Create New Account를 선택합니다. 사용자 이름을 입력하고 복잡한 비밀번호를 선택하십시오. 암호는 8자 이상이어야 하며 대문자, 소문자, 특수 문자 및 숫자를 포함 할 수 있습니다. 유효한 이메일을 입력하면 인증 코드를 받을 수 있습니다.
Sign Up을 선택합니다.
"cognito-idp.us-east-1.amazonaws.com" 호스트를 확인할 수 없음과 같은 오류가 표시되면 에뮬레이터가 인터넷에 연결되어 있는지 다시 확인하십시오. 필요한 경우 에뮬레이터를 다시 시작하십시오.
확인 코드가 지정된 이메일의 받은 편지함에 곧 도착할 것입니다. 해당 코드를 다음 화면에 입력하고 Confirm을 선택하여 가입 절차를 완료하십시오.
성공적으로 로그인하면 성공적인 메시지가 표시되고 MainActivity인 동일한 빈 화면으로 다시 이동해야 합니다.
Amazon Cognito 사용자 풀에서 생성된 새 사용자를 보려면 https://console.aws.amazon.com/cognito/의 대시보드로 돌아가십시오. 또한 AWS 리전이 올바르게 설정되었는지 확인하십시오.
AWS AppSync 클라이언트 생성
이제 API 호출을 수행하기 위해 AWSAppSyncClient를 생성해야 합니다. 패키지에 새로운 ClientFactory.java 클래스를 추가하십시오.
public class ClientFactory {
private static volatile AWSAppSyncClient client;
public static synchronized void init(final Context context) {
if (client == null) {
final AWSConfiguration awsConfiguration = new AWSConfiguration(context);
client = AWSAppSyncClient.builder()
.context(context)
.awsConfiguration(awsConfiguration)
.cognitoUserPoolsAuthProvider(new CognitoUserPoolsAuthProvider() {
@Override
public String getLatestAuthToken() {
try {
return AWSMobileClient.getInstance().getTokens().getIdToken().getTokenString();
} catch (Exception e){
Log.e("APPSYNC_ERROR", e.getLocalizedMessage());
return e.getLocalizedMessage();
}
}
}).build();
}
}
public static synchronized AWSAppSyncClient appSyncClient() {
return client;
}
}
이 ClientFactory 클래스는 데이터 액세스 활동을 수행하는 데 사용할 수 있는 AppSync 클라이언트를 제공합니다.
데이터 쿼리
아직 목록에 아무런 데이터도 없지만 데이터가 있을 때 데이터를 표시 할 수 있는 기능을 구축합시다.
RecyclerView를 추가하여 항목 목록 표시
이제 항목을 표시할 수 있도록 앱을 제작해 봅시다.
우리는 RecyclerView를 사용하여 데이터를 표시합니다. src/res/layout/content_main.xml을 열고 텍스트 보기로 전환한 다음 <TextView>를 다음 내용으로 바꿉니다.
이제 목록에 있는 각 항목의 모양을 정의해 보겠습니다. res/layout 폴더를 마우스 오른쪽 버튼으로 클릭하고 새로운 Layout 리소스 파일을 추가합니다. 파일 이름을 recyclerview_row.xml로 합니다. Root element를 LinearLayout으로 변경하고 나머지는 기본값으로 유지한 다음 OK를 선택하십시오.
recyclerview_row.xml의 텍스트 보기로 전환하고 레이아웃을 다음과 같이 수정합니다.
RecyclerView를 사용하기 때문에 이를 위해 어댑터를 제공해야 합니다. 새로운 Java 클래스 MyAdapter.java를 추가하십시오. 이는 RecyclerView.Adapter를 확장합니다.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<ListPetsQuery.Item> mData = new ArrayList<>();;
private LayoutInflater mInflater;
// data is passed into the constructor
MyAdapter(Context context) {
this.mInflater = LayoutInflater.from(context);
}
// inflates the row layout from xml when needed
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
return new ViewHolder(view);
}
// binds the data to the TextView in each row
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bindData(mData.get(position));
}
// total number of rows
@Override
public int getItemCount() {
return mData.size();
}
// resets the list with a new set of data
public void setItems(List<ListPetsQuery.Item> items) {
mData = items;
}
// stores and recycles views as they are scrolled off screen
class ViewHolder extends RecyclerView.ViewHolder {
TextView txt_name;
TextView txt_description;
ViewHolder(View itemView) {
super(itemView);
txt_name = itemView.findViewById(R.id.txt_name);
txt_description = itemView.findViewById(R.id.txt_description);
}
void bindData(ListPetsQuery.Item item) {
txt_name.setText(item.name());
txt_description.setText(item.description());
}
}
}
클래스 수준 변수mData에 주의하십시오. 이는 ListPetsQuery.Item 유형의 목록입니다.이 목록은 스키마를 기반으로 생성된 GraphQL 유형입니다.
또한 우리는 데이터 세트의 외부 재설정을 허용하기 위해 setItems 메서드를 표시했습니다.
RecyclerView를 채우는 화면 만들기
MainActivity.java를 열고 클래스를 수정하여 쿼리 메서드를 구현하고 RecyclerView를 채웁니다.
public class MainActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
MyAdapter mAdapter;
private ArrayList<ListPetsQuery.Item> mPets;
private final String TAG = MainActivity.class.getSimpleName();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.recycler_view);
// use a linear layout manager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// specify an adapter (see also next example)
mAdapter = new MyAdapter(this);
mRecyclerView.setAdapter(mAdapter);
ClientFactory.init(this);
}
@Override
public void onResume() {
super.onResume();
// Query list data when we return to the screen
query();
}
public void query(){
ClientFactory.appSyncClient().query(ListPetsQuery.builder().build())
.responseFetcher(AppSyncResponseFetchers.CACHE_AND_NETWORK)
.enqueue(queryCallback);
}
private GraphQLCall.Callback<ListPetsQuery.Data> queryCallback = new GraphQLCall.Callback<ListPetsQuery.Data>() {
@Override
public void onResponse(@Nonnull Response<ListPetsQuery.Data> response) {
mPets = new ArrayList<>(response.data().listPets().items());
Log.i(TAG, "Retrieved list items: " + mPets.toString());
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.setItems(mPets);
mAdapter.notifyDataSetChanged();
}
});
}
@Override
public void onFailure(@Nonnull ApolloException e) {
Log.e(TAG, e.toString());
}
};
}
appSyncClient는 AWS AppSync GraphQL 엔드포인트를 쿼리하는 작업을 수행합니다. 최신 데이터를 얻기 위해 네트워크에 접속하는 동안 로컬 캐시의 데이터를 먼저 검색하기 때문에 CACHE_AND_NETWORK 모드를 사용하기로 했습니다. 가져오기가 완료되면 queryCallback이 다시 호출되고 데이터 세트가 최신 데이터로 업데이트됩니다. 앱 데이터 가져오기 요구에 따라 사용할 수 있는 다른 캐시 또는 네트워크 전용/첫 번째 모드가 있습니다.
앱을 다시 제작하여 오류가 없는지 확인합니다. 여전히 빈 화면이 표시되지만 Logcat 창에서 로그를 볼 수 있을 것입니다. 이는 다음과 비슷한 쿼리가 성공적으로 완료되었음을 의미합니다.
09-28 10:32:16.789 11605-11699/com.example.demo.mypetapp I/MainActivity: Retrieved list items: []
애완 동물 추가
이제 애완 동물을 추가하는 기능을 추가해 보겠습니다.
New -> Activity -> Empty Activity를 선택하여 새로운 Empty Activity을 추가하십시오. 활동의 이름을 AddPetActivity로 지정한 다음 Finish을 선택하십시오.
레이아웃 파일 activity_add_pet.xml을 열고 기존 <android.support.constraint.ConstraintLayout> 내에 다음 레이아웃을 추가하십시오.