2023.03.17 - 안드로이드 앱개발자 과정

CHA·2023년 3월 19일
0

Android



Firebase

Firebase 는 구글에서 제공하는 NoSQL 의 데이터베이스 플랫폼입니다. 우리는 지금껏 해왔던 Retrofit 을 이용한 서버와의 작업들을 하면서 앱 개발자가 직접 php 파일까지 건드리는 작업을 해왔습니다. 이 Firebase 를 이용하면 Back-End 작업까지 생각할 필요없이 아주 간편하게 서버의 작업들을 처리할 수 있습니다. 이번 예제에서는 Firebase 프로젝트를 만들고, 내 안드로이드 앱과 연동 시키는 작업을 해봅시다.


Firebase 만들기

Firebase 만드는 방법은 크게 어렵지 않습니다. 사이트에서 하라는데로 그대로 따라해주기만 해도 되는데요, 다만 튜토리얼이 업데이트되지 않고 있기 때문에 약간의 오류는 있습니다. 일단 Firebase 프로젝트를 추가해봅시다.

firebase 프로젝트 추가


위 그림처럼 프로젝트를 추가해줍시다.


그러면 위와같이 프로젝트 이름을 설정하라고 합니다. 프로젝트 이름은 중복없이 지어야 하므로, 본인의 닉네임 등을 이용하여 프로젝트 이름을 작성해줍시다.


프로젝트 이름을 작성했다면, 애널리틱스를 설정하라는 화면이 나옵니다. 원래라면 설정해주어야 합니다만, 설정하면 라이브러리도 추가해야하고 추가적인 작업들이 필요하여 일단은 설정하지 않고 프로젝트를 완성시키겠습니다.


요렇게 되면 잘 설정된겁니다. 조금만 기달리면 프로젝트가 완성됩니다.

안드로이드 앱과 Firebase 프로젝트 연결


프로젝트를 완성하면 위와 같은 화면이 뜨는데요, 일단 프로젝트가 만들어졌으니 우리 앱이랑 연동을 시켜주어야 겠죠? 가운데쯤 보이는 안드로이드 아이콘을 눌러줍시다.


그러면 위와같은 화면이 뜹니다. 앱을 등록시켜주어야 하는데요, 패키지 이름으로는 실제 앱의 패키지 이름을 넣어주면 됩니다. 선택사항들은 안넣어도 상관없습니다.


그리고 다음을 눌러주면 json 파일을 다운로드해서 app 파일 안쪽에 넣어달랍니다. 그대로 해줍시다.


그리고 이 부분에서 튜토리얼이 갱신되지 않았습니다. 1번 항목을 보면, 프로젝트 수준 Gradle 파일에 코드를 추가하라고 합니다. 근데, 프로젝트 수준의 Gradle 파일을 실제로 보면 plugin 항목밖에 보이지 않습니다. 안드로이드 스튜디오가 업데이트 되면서, 해당 내용이 setting.Gradle 파일로 넘어갔기 때문입니다. 그래서 1번 항목의 코드 중, bulidscript 내용을 프로젝트 수준의 Gradle 파일 내부에 복사해주면 됩니다. 단, plugin 위쪽에 붙여넣기 해주어야 합니다. 그리고 buildscript 내용중, dependencies 부분만 필요하므로, 나머지 내용은 삭제해주어야 합니다.

그런 다음, 2번째 항목은 그대로 진행해주면 됩니다. 단, 튜토리얼에서는 bom 을 사용할 수 있는 dependency 항목이 추가되어있으므로, 사용하고자 하는 항목을 추가해주어야 하는데요, 이때 빨간색 글씨로 나와있는 https://firebase.google.com/docs/android/setup#available-libraries 사이트에 들어가 사용할 라이브러리를 가져와야 합니다.

위 사이트에서 우리가 사용할 라이브러리는 Cloud Storage 이므로, 종속 항목을 복사해서 앱 수준의 Gradle 파일의 Dependency 항목에 추가해주면 됩니다.

여기까지 완료했다면 모든 설정이 거의 끝났습니다. 이제 Firebase 메인 화면에서 왼쪽 빌드 항목에 Storage를 선택해주면 다음과 같은 화면이 나타납니다.

그리고 시작하기를 누릅니다. 그러면 프로덕션 모드에서 시작할지, 테스트 모드에서 시작할지 선택가능합니다. 프로덕션 모드로 시작합시다.

그리고 나면 다음과 같이 Cloud Storage 위치를 설정하는 창이 나오는데, asia-northeast3 으로 지정해주어야 합니다. 이게 서울 저장소 입니다. 저장소는 나중에 변경할 수 없으니 신중히 선택합시다.

여기까지 했으면, 내 안드로이드 앱과 연결된 저장소 하나가 만들어지며, 마지막으로 Rules 탭으로 가서 false 로 지정되어 있는 부분을 true 로만 바꿔주면 모든 설정이 끝이 납니다.


Firebase Storage

Firebase 와 앱을 연동을 시켜보았으니, 이제 Firebase 의 기능중 하나인 Storage 를 사용해봅시다. 우리는 이번에 사진을 가져와서 Firebase Storage 에 저장시킨 후, 로드해오는 작업을 해봅시다.

전체 코드

전체 코드 입니다.

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnLoad.setOnClickListener(view -> clickLoad());
        binding.btnSelect.setOnClickListener(view -> clickSelect());
        binding.btnUpload.setOnClickListener(view -> clickUpload());
    }

    void clickLoad(){
        FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();

        StorageReference rootRef = firebaseStorage.getReference();

        StorageReference imgRef = rootRef.child("bg_one10.jpg");
        if(imgRef != null){
            imgRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
                @Override
                public void onSuccess(Uri uri) {
                    Glide.with(MainActivity.this).load(uri).into(binding.iv);
                }
            });
        }
    }

    Uri imgUri = null;
    ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
        imgUri = result.getData().getData();
        Glide.with(this).load(imgUri).into(binding.iv);
    });

    void clickSelect(){
        Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
        launcher.launch(intent);
    }

    void clickUpload(){
        if(imgUri == null) return;

        FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String fileName = "IMG_"+simpleDateFormat.format(new Date())+".png";
        StorageReference imgRef = firebaseStorage.getReference("photo/"+fileName); 

        imgRef.putFile(imgUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {

            }
        });
    }
}

사진 선택

-------------- MainActivity.java


Uri imgUri = null;

ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
    imgUri = result.getData().getData();
    Glide.with(this).load(imgUri).into(binding.iv);
});

void clickSelect(){
    Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
    launcher.launch(intent);
}

사진 업로드

void clickUpload(){
    if(imgUri == null) return;

    FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    String fileName = "IMG_"+simpleDateFormat.format(new Date())+".png";
    StorageReference imgRef = firebaseStorage.getReference("photo/"+fileName); 
    
    imgRef.putFile(imgUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception e) {

        }
    });
}
  • if(imgUri == null) return;
    선택한 사진이 없다면, 업로드 하지 않게끔 합니다.

  • FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();
    파이어베이스의 저장소 관리 객체를 가져옵시다.

  • StorageReference imgRef = firebaseStorage.getReference("photo/"+fileName);
    저장소 파일 위치에 대한 참조 객체를 하나 만들었습니다. 여기에 실제 파일을 업로드 시켜줄 수 있습니다. 또한, photo 파일이 없다면 새로 생성시켜주니 참고합시다.

  • imgRef.putFile(imgUri).addOnSuccessListener()
    저장소 파일위치 참조객체에게 실제 파일을 업로드 시킵니다. addOnSuccessListener 를 사용하면 성공 여부에 따라 콜백메소드를 호출시킬 수 있습니다.

사진 불러오기

void clickLoad(){
    FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();

    StorageReference rootRef = firebaseStorage.getReference();

    StorageReference imgRef = rootRef.child("bg_one10.jpg");
    if(imgRef != null){
        imgRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
            @Override
            public void onSuccess(Uri uri) {
                Glide.with(MainActivity.this).load(uri).into(binding.iv);
            }
        });
    }
}
  • FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();
    파이어베이스 저장소 관리 객체를 가져옵시다.
  • StorageReference rootRef = firebaseStorage.getReference();
    getReference() 파라미터로 아무것도 전달하지 않으면, 저장소의 최상위 위치가 반환됩니다.

  • StorageReference imgRef = rootRef.child("bg_one10.jpg");
    저장소 참조객체를 새로 하나 만들어주고, child 를 이용하여 불러오고자 하는 파일의 이름을 파라미터로 전달합시다.

  • imgRef.getDownloadUrl().addOnSuccessListener()
    저장소 참조객체에게 이미지의 다운로드 Url 을 얻어와줍시다. 만약 성공했다면 Glide 를 이용해 이미지를 뷰에 띄워줍니다.


Firebase FireStore

Firebase FireStore 는 데이터베이스 입니다. 저장소에 사진을 저장했던것과 달리 문자열을 DB 의 형태로 관리할 수 있게끔 해줍니다. Storage 를 만들었던것과 마찬가지로 Firebase 메인페이지에서 빌드 해줍니다. 또한 Rules 도 수정해줍시다.

이번 테스트에서는 EditText 로 입력받은 이름과 나이, 주소를 Firebase DB 에 저장해보고, 불러와보는 작업을 해봅시다.

전체 코드

package com.example.ex89_firebase_firestoredb;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.Toast;

import com.example.ex89_firebase_firestoredb.databinding.ActivityMainBinding;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.QuerySnapshot;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());


        binding.btnSave.setOnClickListener(view->clickSave());
        binding.btnLoad.setOnClickListener(view->clickLoad());


    }

    void clickSave(){
        String name = binding.etName.getText().toString();
        int age = Integer.parseInt(binding.etAge.getText().toString());
        String address = binding.etAddress.getText().toString();

        FirebaseFirestore firestore =  FirebaseFirestore.getInstance();

        CollectionReference personRef = firestore.collection("person");

        Map<String,Object> person = new HashMap<>();
        person.put("name",name);
        person.put("age",age);
        person.put("address",address);
        personRef.document().set(person).addOnSuccessListener(unused -> {
            Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show();
        });

        //personRef.document().set(person).
    }
    void clickLoad(){
        FirebaseFirestore firestore = FirebaseFirestore.getInstance();
        CollectionReference personRef = firestore.collection("person");

        personRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                StringBuffer buffer = new StringBuffer();
                for(QueryDocumentSnapshot snapshot : queryDocumentSnapshots){
                    Map<String,Object> person = snapshot.getData();
                    String name = (String)person.get("name");
                    int age = Integer.parseInt(person.get("age").toString());
                    String address = (String) person.get("address");

                    buffer.append(name + " : " + age + " : " + address + "\n");
                }

                binding.tv.setText(buffer.toString());

            }
        });
    }
}

데이터 저장하기

void clickSave(){
    String name = binding.etName.getText().toString();
    int age = Integer.parseInt(binding.etAge.getText().toString());
    String address = binding.etAddress.getText().toString();

    FirebaseFirestore firestore =  FirebaseFirestore.getInstance();

    CollectionReference personRef = firestore.collection("person"); 

    Map<String,Object> person = new HashMap<>();
    person.put("name",name);
    person.put("age",age);
    person.put("address",address);

    personRef.document().set(person).addOnSuccessListener(unused -> {
        Toast.makeText(this, "Saved", Toast.LENGTH_SHORT).show();
    });

    //personRef.document().set(person).
}
  • FirebaseFirestore firestore = FirebaseFirestore.getInstance();
    DB 저장소 객체를 불러와줍니다.

  • CollectionReference personRef = firestore.collection("person");
    Storage 를 참조하는 객체를 이용하여 데이터를 처리했던과 마찬가지로 Collection 을 참조하는 객체 CollectionReference 객체를 만들어둡시다. collection 의 파라미터로 컬렉션의 이름을 지정할 수 있으며, 없으면 만들고 있으면 바로 참조하는 형식입니다.

  • Map<String,Object> person = new HashMap<>();
    person 컬렉션에 넣을 데이터들을 Map 데이터 형식으로 준비해줍니다. Map 객체의 이름은 어떤걸로 지정하던 상관없지만, 컬렉션의 이름과 맞춰주면 편할것같습니다. 그리고 String 데이터 형식만 저장되는게 아닐 수도 있으므로, 값 부분의 제네릭 타입을 Object 로 설정해주었습니다.

  • personRef.document().set(person).addOnSuccessListener()
    컬렉션 참조변수에게 document() 로 문서 하나를 생성합시다. 파라미터로는 도큐먼트의 이름을 설정할 수 있으며 아무것도 전달하지 않으면 랜덤값으로 이름이 지정됩니다. 보통은 현재 날짜와 시간을 이용하여 저장하여 순서를 정렬합니다. 그리고 set() 을 이용해 Map 형식의 데이터를 집어넣습니다. 잘 집어넣어졌다면 토스트를 하나 띄워보았습니다.

데이터 불러오기

void clickLoad(){
    FirebaseFirestore firestore = FirebaseFirestore.getInstance();
    CollectionReference personRef = firestore.collection("person");

    personRef.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
            StringBuffer buffer = new StringBuffer();
            for(QueryDocumentSnapshot snapshot : queryDocumentSnapshots){
                Map<String,Object> person = snapshot.getData();
                String name = (String)person.get("name");
                int age = Integer.parseInt(person.get("age").toString());
                String address = (String) person.get("address");

                buffer.append(name + " : " + age + " : " + address + "\n");
            }

            binding.tv.setText(buffer.toString());
        }
    });
}
  • FirebaseFirestore firestore = FirebaseFirestore.getInstance();
    DB 저장소 객체를 불러옵니다.

  • CollectionReference personRef = firestore.collection("person");
    컬렉션 참조 객체를 만들어줍시다.

  • personRef.get().addOnSuccessListener()
    컬렉션 참조객체에게 get() 을 요청하면 도큐먼트의 모든 데이터를 가져올 수 있습니다. 그리고 불러오기가 성공했을 때에는 onSuccess() 콜백메서드가 호출됩니다.

  • for(QueryDocumentSnapshot snapshot : queryDocumentSnapshots){}
    컬렉션에 도큐먼트가 하나가 아니기 때문에 foreach 문을 통해 모든 도큐먼트의 데이터를 가져올 수도 있습니다. snapshot 객체는 각각의 도큐먼트의 객체이며, 이 객체를 이용하여 도큐먼트가 가지고 있는 데이터를 빼올 수 있습니다.

특정 필드값 데이터 불러오기

personRef.whereEqualTo("name","sam").addSnapshotListener(new EventListener<QuerySnapshot>() {
    @Override
    public void onEvent(@Nullable QuerySnapshot value, @Nullable FirebaseFirestoreException error) {

    }
});

컬렉션 객체에게 get()을 호출하면 모든 데이터를 가져올 수 있지만, 특정 필드값만 가져오고 싶다면, 위와 같이 활용할 수 있습니다.


profile
Developer

0개의 댓글