AWS 시작하기

Android 애플리케이션 구축

AWS Amplify를 사용하여 간단한 Android 애플리케이션 만들기

모듈 1: Android 앱 생성 및 배포

이 모듈에서는 AWS Amplify의 웹 호스팅 서비스를 사용하여 Android 애플리케이션을 생성하고 클라우드에 배포합니다.

소개

AWS Amplify는 웹 및 모바일 애플리케이션에 대한 서버리스 백엔드의 생성, 관리, 통합 및 배포를 위한 Git 기반 워크플로를 제공합니다. Amplify CLI는 사용자 인증 또는 REST 또는 GraphQL API와 같은 애플리케이션 백엔드 서비스를 프로비저닝하고 관리할 수 있는 단순한 텍스트 기반 사용자 인터페이스를 제공합니다. Amplify 라이브러리는 이러한 백엔드 서비스를 코드 몇 줄로 손쉽게 애플리케이션에 통합하는 데 사용됩니다.

이 모듈에서는 여행 메모를 기록하는 새 Android 애플리케이션을 생성합니다. 노트는 제목, 설명 및 사진으로 구성됩니다. 다음 모듈에서는 이 애플리케이션에 향상된 기능을 추가합니다.

배우게 될 내용

  • Android 애플리케이션 만들기
  • 기본 보기를 업데이트하여 항목 목록 표시
  • 애플리케이션 구축 및 테스트

주요 개념

Kotlin - Kotlin은 보일러플레이트 코드를 많이 줄일 수 있고 형식 안전성을 염두에 두고 설계되었기 때문에 이 자습서의 프로그래밍 언어로 선택되었습니다.

Jetpack - 이 자습서는 Android 앱에 모범 사례를 적용하고 이전 버전과의 호환성을 제공하는 Android 라이브러리 모음인 Android의 Jetpack을 사용합니다.

 완료 시간

10분

 사용되는 서비스

구현

  • Android 프로젝트 생성

    Android Studio를 시작하고 스플래시 화면에서 [새 Android Studio 프로젝트 시작(Start a new Android Studio project)]을 선택합니다.

    AndroidAppTutorial_Modiule1_Image1

    [Phone and Tablet(휴대폰 및 태블릿)]에서 [기본 작업(Basic Activity)]을 선택하고 [다음(Next)]을 클릭합니다.

    AndroidAppTutorial_Modiule1_Image2

    프로젝트의 이름(예: Android Getting Started)을 입력합니다. 언어가 Kotlin이고 최소 SDK가 API 26: Android 8.0(oreo)인지 확인합니다. 그리고 [마침:(Finish:)]을 클릭합니다.

    AndroidAppTutorial_Modiule1_Image3

    프로젝트의 골격이 준비되었으므로 기본 앱을 실행하려면 4단계가 필요합니다.

    1. 원하지 않는 클래스 및 파일을 삭제하고 플러그인을 추가합니다.
    2. 데이터 구조를 보유할 클래스를 만듭니다.
    3. 목록에서 개별 노트를 보관할 보기를 만듭니다.
    4. MainActivity를 수정하여 노트 목록을 표시합니다.
  • 필요 없는 파일을 삭제하고 플러그인을 추가합니다.

    res/layout 및 java/com.example.androidgetingstarted에서 4개의 조각 파일을 삭제합니다. 4개의 파일을 선택하고 마우스 오른쪽 버튼을 클릭한 후 컨텍스트 메뉴에서 [삭제(Delete)]를 선택합니다.

    AndroidAppTutorial_Modiule1_Image4

    Gradle Scripts에서 build.gradle (Module:app)을 열고 Kotlin Extension 플러그인을 추가합니다.

    plugins {
        id 'com.android.application'
        id 'kotlin-android'
        id 'kotlin-android-extensions' //  <== add this line
    }
  • UserData 데이터 클래스를 생성합니다.

    UserData 클래스는 사용자 상태, 즉 isSignedIn 플래그 및 Note 값 목록을 유지합니다.

    새 클래스를 생성하려면 java/com.example/androidgettingstarted를 마우스 오른쪽 버튼으로 클릭하고 [새로 만들기(New)] -> [Kotlin 파일/클래스(Kotlin file/class)]를 선택합니다. 이름으로 UserData를 입력합니다.

    AndroidAppTutorial_Modiule1_Image5

    방금 만든 새 파일(UserData.kt)에 아래 코드를 붙여 넣습니다.

    package com.example.androidgettingstarted
    
    import android.graphics.Bitmap
    import android.util.Log
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    
    // a singleton to hold user data (this is a ViewModel pattern, without inheriting from ViewModel)
    object UserData {
    
        private const val TAG = "UserData"
    
        //
        // observable properties
        //
    
        // signed in status
        private val _isSignedIn = MutableLiveData<Boolean>(false)
        var isSignedIn: LiveData<Boolean> = _isSignedIn
    
        fun setSignedIn(newValue : Boolean) {
            // use postvalue() to make the assignation on the main (UI) thread
            _isSignedIn.postValue(newValue)
        }
    
        // the notes
        private val _notes = MutableLiveData<MutableList<Note>>(mutableListOf())
    
        // please check https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
        private fun <T> MutableLiveData<T>.notifyObserver() {
            this.postValue(this.value)
        }
        fun notifyObserver() {
            this._notes.notifyObserver()
        }
    
        fun notes() : LiveData<MutableList<Note>>  = _notes
        fun addNote(n : Note) {
            val notes = _notes.value
            if (notes != null) {
                notes.add(n)
                _notes.notifyObserver()
            } else {
                Log.e(TAG, "addNote : note collection is null !!")
            }
        }
        fun deleteNote(at: Int) : Note?  {
            val note = _notes.value?.removeAt(at)
            _notes.notifyObserver()
            return note
        }
    
        fun resetNotes() {
            this._notes.value?.clear()  //used when signing out
            _notes.notifyObserver()
        }
    
    
        // a note data class
        data class Note(val id: String, val name: String, val description: String, var imageName: String? = null) {
            override fun toString(): String = name
    
            // bitmap image
            var image : Bitmap? = null
    
        }
    }

    방금 추가한 것은 무엇입니까?

    • UserData 클래스는 사용자 데이터, 즉 현재 인증 상태 및 Note 객체 목록을 추적하는 isSignedIn 플래그를 저장하는 역할을 합니다.
    • 이 두 속성은 LiveData 게시/구독 프레임워크에 따라 구현됩니다. GUI(그래픽 사용자 인터페이스)를 통해 변경 사항을 구독하고 그에 따라 대응할 수 있습니다. LiveData에 대해 자세히 알아보려면 이 문서를 읽거나 이 짧은 비디오 자습서를 따르면 됩니다. 모범 사례를 따르려면 MutableLiveData 속성을 비공개로 유지하고 읽기 전용 LiveData 속성만 노출합니다. 게시할 데이터가 목록인 경우 목록의 개별 구성 요소가 수정될 때 옵저버에 알리도록 하려면 몇 가지 추가 상용 코드가 필요합니다.
    • 개별 노트의 데이터를 저장하기 위해 Note 데이터 클래스도 추가했습니다. ImageName 및 Image에는 두 가지 고유한 속성이 사용됩니다. 이미지는 후속 모듈에서 처리됩니다.
    • UserData만으로 애플리케이션 내의 어느 위치에서나 참조할 수 있도록 UserData 객체에 대해 싱글톤 설계 패턴을 구현했습니다.
  • 목록에서 개별 셀에 대한 GUI 추가

    화면에 더 이상 보기를 표시할 수 없는 경우 사용자가 위아래로 스크롤할 때 보기를 재활용할 수 있으므로, 스크롤 목록의 개별 셀을 RecyclerView라고 합니다.

    일반 보기와 마찬가지로 레이아웃 XML 파일과 Kotlin 클래스를 생성합니다. 개별 셀은 다음과 같습니다.

    AndroidAppTutorial_Modiule1_Image6

    레이아웃 파일을 생성하려면 res/layout을 마우스 오른쪽 버튼으로 클릭하고 [새로 만들기(New)] -> [레이아웃 리소스 파일(Layout Resource File)]을 선택합니다. content_note를 이름으로 입력하고, XML 파일을 직접 편집할 것이므로 다른 모든 값은 그대로 둡니다.

    AndroidAppTutorial_Modiule1_Image7

    새로 생성한 파일에 대해 코드 보기를 엽니다.

    AndroidAppTutorial_Modiule1_Image8

    방금 생성한 새 파일의 코드 보기(content_note.xml)에서 아래 코드를 붙여 넣어 생성한 코드를 바꿉니다.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingVertical="16dp">
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher_background" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="5dp"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="20sp"
                android:textStyle="bold" />
    
            <TextView
                android:id="@+id/description"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="15sp" />
    
        </LinearLayout>
    </LinearLayout>

    마지막으로 Kotlin 클래스를 생성합니다. java/com.example/androidgettingstarted를 마우스 오른쪽 버튼으로 클릭하고 [새로 만들기(New)] -> [Kotlin 파일/클래스(Kotlin file/class)]를 선택합니다. 이름으로 NoteRecyclerViewAdapter를 입력합니다.

    AndroidAppTutorial_Modiule1_Image9

    방금 생성한 새 파일(NoteRecyclerViewAdapter.kt)에 아래 코드를 붙여넣 습니다.

    package com.example.androidgettingstarted
    
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ImageView
    import android.widget.TextView
    import androidx.recyclerview.widget.RecyclerView
    
    // this is a single cell (row) in the list of Notes
    class NoteRecyclerViewAdapter(
        private val values: MutableList<UserData.Note>?) :
        RecyclerView.Adapter<NoteRecyclerViewAdapter.ViewHolder>() {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.content_note, parent, false)
            return ViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    
            val item = values?.get(position)
            holder.nameView.text = item?.name
            holder.descriptionView.text = item?.description
    
            if (item?.image != null) {
                holder.imageView.setImageBitmap(item.image)
            }
        }
    
        override fun getItemCount() = values?.size ?: 0
    
        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val imageView: ImageView = view.findViewById(R.id.image)
            val nameView: TextView = view.findViewById(R.id.name)
            val descriptionView: TextView = view.findViewById(R.id.description)
        }
    }

    방금 추가한 것은 무엇입니까?
    위의 코드는 다음으로 구성됩니다.

    • 노트를 나타내는 셀의 구성 요소 레이아웃을 설명하는 레이아웃 xml 파일. 이미지, 노트 제목 및 노트 설명을 표시합니다.
    • 지원 Kotlin 클래스. 생성 시 Note 데이터 객체를 수신하고 해당 보기(이미지, 제목 및 설명)에 개별 값을 할당합니다.
  • 기본 작업 업데이트

    이제 데이터 클래스(UserData 및 Note)와 개별 노트의 보기(NoteRecyclerViewAdapter)를 생성했으므로 주요 작업에 대한 노트 목록을 만들어 보겠습니다.

    Android Studio 왼쪽에 있는 파일 목록에서 res/layout/content_main.xml을 열고 코드를 다음 내용으로 바꿉니다.

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/item_list"
            android:name="com.example.myapplication.ItemListFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="60dp"
    
            android:paddingHorizontal="8dp"
            android:paddingVertical="8dp"
            app:layoutManager="LinearLayoutManager"
            tools:context=".MainActivity"
            tools:listitem="@layout/content_note" />
    
    </FrameLayout>

    Android Studio 왼쪽에 있는 파일 목록에서 java/com.example/androidgettingstarted/MainActivity.kt를 열고 코드를 다음 콘텐츠로 바꿉니다.

    package com.example.androidgettingstarted
    
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.Observer
    import androidx.recyclerview.widget.ItemTouchHelper
    import androidx.recyclerview.widget.RecyclerView
    import kotlinx.android.synthetic.main.activity_main.*
    import kotlinx.android.synthetic.main.content_main.*
    
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setSupportActionBar(toolbar)
    
            // prepare our List view and RecyclerView (cells)
            setupRecyclerView(item_list)
        }
    
        // recycler view is the list of cells
        private fun setupRecyclerView(recyclerView: RecyclerView) {
    
            // update individual cell when the Note data are modified
            UserData.notes().observe(this, Observer<MutableList<UserData.Note>> { notes ->
                Log.d(TAG, "Note observer received ${notes.size} notes")
    
                // let's create a RecyclerViewAdapter that manages the individual cells
                recyclerView.adapter = NoteRecyclerViewAdapter(notes)
            })
        }
    
        companion object {
            private const val TAG = "MainActivity"
        }
    }

    방금 추가한 것은 무엇입니까?

    • 기본 레이아웃은 이전에 생성한 개별 셀 목록을 관리하는 RecyclerView입니다.
    • 기본 작업 클래스는 Notes 목록의 변경 사항을 관찰하고 개별 셀을 생성할 NoteRecyclerViewAdapter를 생성합니다.
  • 빌드 종속성 확인

    Gradle Scripts에서 build.gradle(Module:app)를 열고 생성된 종속 구성 요소가 올바른지 확인합니다. `libraries versions`를 확인해야 합니다.

    ```gradle
    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.core:core-ktx:1.3.2'
        implementation 'androidx.appcompat:appcompat:1.2.0'
        implementation 'com.google.android.material:material:1.2.1'
        implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
        implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
        implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.2'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    }
  • 구축 및 테스트

    이제 시뮬레이터에서 앱을 구축하고 시작합니다. 도구 모음에서 [실행(Run)] 아이콘 ▶을 클릭하거나 ^ R을 입력합니다.

    AndroidAppTutorial_Modiule1_Image10

    잠시 후 Android 시뮬레이터에서 앱이 시작되고 초기 빈 화면이 표시됩니다.

    AndroidAppTutorial_Modiule1_Image11

    이 단계에서는 런타임에 렌더링할 데이터가 없습니다. 메일 아이콘은 나중에 제거하겠습니다.

결론

Android 앱이 생성되었습니다. 이제 Amplify로 구축을 시작할 수 있습니다!

이 모듈이 유용했습니까?

감사합니다.
좋아하는 사항을 알려주세요.
닫기
실망을 드려 죄송합니다.
오래되었거나 혼란스럽거나 부정확한 사항이 있습니까? 피드백을 제공하여 이 자습서를 개선할 수 있도록 도와주십시오.
닫기

Amplify 초기화