본문 바로가기
FireBase

Firebase -데이터베이스(2) NoSQL구조, Firestore선언, create하기

by 봄석 2019. 1. 9.

Firebase - 데이터베이스


Firestore 사용하기 

일단 데이터베이스에는 CRUD라는 개념이 있습니다. 

Create, Read, Update, Delete의 약자로 쓰기, 읽기, 수정하기, 삭제하기 를 말합니다.

Firestore로 어떻게 CRUD를 할수 있는지 알아보겠습니다.



NoSQL 데이터베이스의 기본 구조

Firestore의 구조는 크게 세 가지로 나눌 수 있습니다. 기본적인 데이터베이스의 가장 기초적인 구성 요소인 Data가 있고 Data가 모여서 하나의 문서 즉 Document를 구성하며 Document가 모여서 하나의 Collection을 구성한다.  ( Data -> Document -> Collection)

만약 유저정보를 가지고 있는 데이터베이스를 예로 들 경우 유저 정보의 한 조각인 이메일이나 가입일, 최종 접속일 등을 하나의 Data로 볼 수 있습니다. 한 명의 유저에 대한 모든 정보는 Document라고 생각하면 이해하기 쉬우며 그 유저 정보의 대한 모든 정보의 집합이 Collection이라고 생각하면 됩니다.




안드로이드 프로젝트에서 Firestore 선언하기

Firestore를 선언하기 위해서는 "Firesotre.getInstance()"를 입력하면 됩니다.


class MainActivity : AppCompatActivity() {
    lateinit var firestore :FirebaseFirestore
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        firestore = FirebaseFirestore.getInstance()
    }
}





데이터베이스 입력하기 (Create)

데이터를 입력하는 방식에는 두 가지가 있습니다.

 NoSQL 데이터베이스는 Document 구조의 데이터베이스로 Primarykey 중복 생성이 발생되지 않는 구조이기 때문에 처음부터 중복 입력에 대한 보호막이 존재하지 않는다.

그렇기 때문에 좋아요, 인구수, 투표수 같은 다중 클라이언트로부터 들어오는 정보를 한 곳에 모아서 카운트를 할 때 중복 입력이 발생될 수 밖에 없는구조입니다.


그래서 편의에 따라 간단한 데이터는 Set으로 데이터를 입력해주면 되며 만약 다중 클라이언트로 부터 들어오는 정보를 한 곳에 모아서 카운터 할 경우 runTransaction을 사용하면 됩니다.


입력 방식 

사용 목적 

  Set

 기본적인 데이터 입력 

  runTransactions 

 여러 클라이언트의 데이터 중복 접근 방지 


데이터 베이스를 입력하기 위해서는 일단 첫 번째로 Collection 이름을 넣어줘야 합니다. 

즉 관계형 데이터베이스로 비교하자면 테이블 이름을 넣어준다고 생각하면 됩니다.

두 번째로는 Document 이름을 입력해주면 되는데 이 이름은 관계형 데이터베이스에서 Primarykey라고 생각하면 됩니다. 


Document 이름을 생성하는 방식에는 두 가지가 있는데

직접 Document에 이름을 넣는 방법과 자동으로 생성하는 방법이 있습니다.


1)Document 이름을 직접 넣는 방식

collection(Collection 이름).document(Document 이름).set(입력할 데이터)
.addOnCompleteListener{ task ->
 
}

Collection의 소괄호 안에 Collection 이름을 넣고 Document에 이름을 넣은 뒤 입력할 데이터 클래스나 Json 값을 set으로 넣고 코드를 실행하게 되면 데이터베이스 입력하기가 작동되는 것을 확인할 수 있습니다.


그리고 정상적으로 결과값이 입력되었는지 확인하려면 .addOnCompleteListener를 넣어주면 됩니다. 이부분은 생략해도 상관 없습니다.



먼저 데이터를 입력하기 전에 데이터 모델을 선언해야 하므로 데이터모델을 선언해보도록 하겠습니다.


데이터모델 선언

먼저 UserDTO 라는 클래스를 만들어 주고 안에다가 name과  address를 받을 수 있는 변수를 추가해줍니다.


형식

data class UserDTO(var name:String? =null, var address :String?=null)


사용 예)

private fun createData(){
        var userDTO= UserDTO("범석","서울")
        firestore?.collection("User")?.document("document1")?.set(userDTO)
            .addOnCompleteListener {
                if(it.isSuccessful)
                    toast("create성공")
            }
    }


위 코드를 실행하면 성공시에 토스트메시지로 "create 성공" 이라는 단어를 보여줍니다.


실행결과



2) Document 이름을 임의적으로 생성하는 방식

임의로 Document를 생성하기 위해 Document에 아무값도 넣지 않을 경우 

Document 아이디가 자동으로 생성된다. 그렇다고 해서 무작위로 Document 아이디가 생성되는것은 아니며 일정한 규칙에 의해서 생성된다.


독자들 중에 관계형 데이터베이스를 사용해본 사람들은 왜 하나씩 증가되는 키를 사용하지 않고 무작위 키를 사용하는지 궁금할 것입니다. 필자도 마찬가지로 왜 이유 없이 내용만 긴 키를 

PrimaryKey 즉 Document 이름으로 만들었는지 이해할 수가 없었습니다. 오히려 저장할 데이터

만 늘어나는게 아닌가 생각했었습니다.


PrimaryKey 

이름 

주소 

나이 

성별 

... 

... 

 1

 가

서울 

20 

남 

 

 

 2

 나

부산 

20 

여 

 

 

 3

 다

대구 

21 

여 

 

 

 ....

 ...

 ...

 ...

 ...

 

 

 100,000,000

 이름

강릉 

남 

 

 

 100,000,001

      


간단한 예를 들어보면 데이터베이스에 1억명의 가입된 Collection이 이미 있다고 가정하고

만약 한 사람이 가입할 경우 100,000,001 이라는 PrimaryKey를 발급한 후 안에 데이터를 넣어주어야 합니다. 물론 여기까지는 문제가 없어보입니다.


하지만 100,000,001 번째 행이라고 데이터베이스가 인식하기 위해서는 기존에 데이터가 몇 개가 있는지 다읽어봐야 하니다. 즉 1억건의 데이터를 읽어봐야 새로 발급해야 할 PrimaryKey를 알 수가 있는것입니다. 체크를하는 작업을 진행하는 동안 데이터베이스에 잠깐 읽기 잠금을 걸게됩니다.  몇 명이 사용하는 데이터베이스에 잠깐 읽기 잠금을 거는 건 크게 중요하지 않지만

이용자가 많을 경우 이런 읽기 잠금은 성능에 지대한 영향을 미치게 됩니다.


그래서 FireStore에서 무작위로 PrimaryKey를  생성해도 읽기 잠금 이벤트를 발생시키지 않습니다. 그럼 무작위로 생성하다가 "키가 중복되면 어떻하지?" 라고 의문을 가질수도 있습니다.

다행이. 날짜+난수로 PrimaryKey가 생성하기 때문에 중복의 염려는 없습니다.


형식

collection(Collection 이름).document().set(입력할데이터).addOnCompleteListener{
task->

}



사용 예)

private fun createData(){
        var userDTO= UserDTO("범석","서울")
        firestore?.collection("User")?.document()?.set(userDTO)
            .addOnCompleteListener {
                if(it.isSuccessful)
                    toast("create성공")
            }
    }


실행결과

결과를 보면 날짜+난수 로 도큐먼트가 생성된 것을 알 수 있습니다.


3) runTransaction 방식

이 방식 같은 경우 조금 다르게 진행이 되는데 일단 저장하고 싶은 경로를 클래스로 만든 뒤에

runTransaction을 호출하는 방식으로 되어 있습니다.

여기서 중요한 점은 한 사람이 Document에 접근해서 데이터를 쓰는 동안 다른 사용자는 데이터를 쓰는 동안 다른 사용자는 데이터베이스로 접근할 수 없습니다.

다른 사용자가 데이터를 중복으로 쓰지 못하게 하기 위해 많이 사용됩니다.


형식

runTransaction{ transaction->
    var result =trasaction.get(데이터베이스경로).toObject(데이터모델)
    //result.필드 =수정내용
    transaction.set(데이터베이스 경로, result)
}



사용 예)

기존의 User에있는 Document1의 name값인 "범석"을 "조범석"으로 바꿔보겠습니다.

 private fun runTransaction(){
        var tsDoc=firestore?.collection("User").document("document1")
 
        firestore?.runTransaction {transaction->
            val userDTO=transaction.get(tsDoc).toObject(UserDTO::class.java)
            userDTO?.name="조범석"
            transaction.set(tsDoc,userDTO!!)
        }
    }


위값이 입력되는 잠깐 동안 다른 클라이언트 사용자들은 데이터베이스를 읽고 쓸 수 없습니다.

댓글