room과 Rxjava2 같이 사용하기

2020. 8. 21. 15:49android

1. ROOM이란?


room 라이브러리는 SQLite 뿐만아니라 데이터베이스 액세스를 지원하는 추상화 계층을 SQLite에 지원하는 방식입니다.

이는 데이터베이스 객체를 자바나 코틀린 객체로 맵핑 시켜준다고 하여 ORM(Object Relational Mapping) 라이브러리라고도 합니다. 

2. SQLite와 다른점은?


1) SQLite인 경우에는 에러를 컴파일 도중에 확인할 수 없지만 room 같은 경우 컴파일 경우에 유효성검사를 하기 때문에 에러를 검출 할 수 있습니다.

 

2) room은 스키마를 작성하여 DB에 대한 전반적인 구조와 제약조건을 명세하고 사용하게 됩니다. 그런데 이러한 스키마가 일부 변경될 경우, 수동적으로 DB의 쿼리를 업데이트 시킬 필요없이, 간단한 작업만으로도 쿼리를 조작할 수 있습니다. 

 

3) room은 SQLite와 다르게 AAC 컴포넌트와 같이 사용될 수 있습니다. 예를들면 LiveData나 Rxjava의 클래스를 자료형으로 사용하여 쿼리 수행을 보다 효율적으로 사용할 수 있습니다.

3. ROOM의 구성요소


1) database : room은 스키마를 만들어서 객체화 시킬 수 있습니다. 이를 통해서 db에 대한 처리가 가능해집니다.

 

2) Entity: db의 테이블을 의미합니다. 

 

3) Dao(Data Access Object): 이름 그대로 데이터에 접근 할 수 있는 객체입니다. 쉽게 말해서 query, insert, delete 등 db 문법을 통해서 저장과 삭제 등 쿼리에 대한 조건문을 수행하기 위한 객체입니다. 

4. RxJava2


room의 Dao가 실행되려면 반드시 비동기 테스크로 처리가 되야합니다. 이유는 Dao에 대한 일처리가 길어져서 UI 갱신에 느릴 수 있기 때문인데요. 비동기 일을 처리하기 위해서 api 30 버전 이상부터 deprecated 되는 AsyncTask를 이용하기 보다 RxJava를 통해서 비동기테스크를 처리할 것이기 때문에 Rxjava에 대해서는 다음번에 더 자세하게 다루겠습니다.

 

물론 RxJava는 비동기 통신을 제외하고 다른 방향으로도 많이 쓰일 수 있습니다. 그런데 여기서는 비동기 테스크만 사용하도록 하겠습니다.

5.  의존성 추가


6. Entity 생성


먼저 db의 테이블을 생성하도록 하겠습니다. ROOM 라이브러리는 어노테이션이 굉장히 중요한 작용을 하기 때문에 반드시 같이 적용을 하셔야합니다. 여기서는 @Entity 어노테이션을 붙여야 해당 클래스를 db의 테이블로 인식합니다! 

 

또한 기본적으로 tableName을 선언하시면 Dao에서 사용될 query에서 tableName에서 선언한 이름을 사용하실 수 있습니다.  tableName을 선언 안해도 @Entity 어노테이션이 현재 클래스를 테이블이라 간주하기 때문에 Dao에서 class name을 통해서 table로 인식하게 끔 할 수 도 있습니다. 

 

1) @PrimaryKey(autoGenerate = true)

- 아이템이 추가될 때마다 자동으로 primaryKey를 생성해 주는 역할을 합니다.

 

2) @ColumnInfo(name = "id"), @ColumnInfo(name = "msg")

- 테이블을 구성할 내용들을 선언하기 위해서 필요한 어노테이션입니다. 여기서는 id와 msg로 이루어진 table 입니다.

7. Dao 생성


db에 직접적으로 접근 할 수 있는 객체인 Dao를 생성함으로써, 만든 Entity를 생성하고 삭제하고 수정하는 등의 작업을 할 수 있습니다. 기본적으로 Dao는 interface로 생성되기 때문에 class가 아닌 interface로 만듭니다. 

 

1) @Query("SELECT * FROM tableInputMsg")

- 쿼리로 사용할 메서드는 반드시 쿼리 어노테이션을 설정해 줘야합니다. 쿼리문을 볼 수 있듯이 Entity에서 설정한 tableName을 기준으로 하여 쿼리를 작성할 수 있습니다.

 

2)@Insert(onConflict = OnConflictStrategy.REPLACE)

- Entity를 삽입할 수 있습니다. 만약에 이미 존재하는 Entity가 있다면 충돌이 발생하기 때문에 onConflict를 설정하여 이러한 경우를 핸들링 할 수 있습니다. 여기서 Replace는 기존에 있는 Entity를 insert하는 Entity로 대체하여 충돌을 피하도록 핸들링하였습니다.

 

3) @Delete

- 메서드에서 받은 Entity를 db에서 제거하도록 합니다. 이렇게 insert, update, delete 등 쿼리문이 아닌 어노테이션에서는 반드시 메서드에 인자 값으로 Entity를 받아야합니다. 왜냐하면 db에서 접근하여 일을 처리해야하기 때문에 null이면 안됩니다. (궁금하시다면 한번 메서드에 인자 값을 아예 주지않고 실행 해 보시거나, 다른 타입으로 줘보세요! 에러가 날겁니다)

 

또한 1)에서 사용한 쿼리는 테이블과 테이블에 대한 item을 모두 갖고 오도록만 되어져 있지, 정작 조건문인 WHERE은 빠져있습니다. 그 이유는 쿼리로 사용하는 메서드의 인자 값이 없기 때문입니다. 만약 쿼리에 조건문으로 WHERE을 사용하고 싶다면 메서드에 인자 값도 추가가 되어야 합니다.

 

그리고 여기서 Rxjava2에 대한 자료형이 사용됩니다. 바로 Flowable과 Completable 입니다. 자세한 설명은 생략하고 왜 그렇게 설정했는지에 대해서만 짚고 넘어가겠습니다.

 

4) Flowable

- flowable은 Back pressure issue 대한 대응을 위해서 나온 것입니다. back pressure라는 것은 db나 서버로부터 데이터들을 가져오는 속도에 비해, 처리되는 속도의 차이가 생겨서 발생하는 문제입니다. 따라서 처리하는 곳의 일이 쌓여있다면 db나 서버로부터 데이터를 계속 가져오기 보다는 일을 어느정도까지 처리하도록 기다려주고 쌓인 일이 조금 해결 되면 다시 db나 서버로부터 데이터를 가져와 일을 처리하도록합니다. 이처럼 기존의 Observable처럼 일을 처리하든 말든 계속 값을 가져와 부하를 발생시키지 않기 위해 대안으로 나온 것이 Flowable입니다.  

 

여기서 쿼리를 사용하여 모든 값을 가져와야하니 부하가 발생할 수 있습니다. 고로 Observable보다 Flowable을 사용합니다.( 물론.. 데이터가 10000개 이상을 가져온다는 전제하에 그래야하는 것이지만요!)

 

5) Completable

- Completable을 사용한 이유는 데이터를 발행할 필요없이 수행만 하고 종료시키기 위해서 사용했습니다. 여기서 발행이라는 것은 Rxjava는 subscribe와 observe 개념을 사용하여 특정 이벤트 버스를 관찰하고 구독한다는 개념을 사용합니다. 이렇게 구독을 하게되면 이벤트가 발생할 때, 구독한 사용자에게 데이터가 발행된다고 말합니다. 

 

그런데 여기서 저희는 insert에 대한 어떠한 결과를 받을 이유가 없습니다. 단순히 실행만을 원하니까요! 그렇기 때문에 Completable을 사용합니다. 

8. 스키마(database) 생성


위에서 정의된 Entity와 Dao를 사용하기 위한 database class를 생성하도록합니다. 이제부터 db에 대한 처리는 스키마 클래스를 객체화하여 다뤄지게 됩니다. 

 

room은 orm이기 때문에 database 객체를 java나 코틀린의 객체로 맵핑하여 사용 할 수 있다고 했습니다. 따라서 어노테이션을 이용[하여 현재 클래스가 db임을 명시해주고 db에서 사용할 테이블을 모두 입력해 줄 수 있습니다. 현재 Entity는 한개 뿐이니 {}에 DbTable.class 하나만 넣도록 했습니다.

 

이때는 만드시 Class<T>를 받아야하기 때문에 Entity에서 선언한 TableName이 아닌 Entity의 클래스를 입력해야합니다. 

9. main.xml


예제를 통해서 room을 사용해 보도록 하겠습니다. 예제는 EditText에 내용을 입력하고 버튼을 클릭하면 db에 저장하도록 하도록 만들어 보겠습니다.

 

10. MainActivity.class


스키마 AppDataBase를 객체화하고 이를 통해서 msg를 insert 하도록합니다. 

 

onClick을 보면 inset와 getAll 메서드에서 사용하고 있는 방식이 특이합니다. 이것이 바로 Rxjava2를 이용하여 비동기를 통신하는 방법입니다. 

 

먼저 insert에서 처리해야하 하는 작업은 EditText에서 입력한 문자열입니다. 이를 비동기 테스크로 사용하기 위해서 subscribeOn을 통해 작업하는 Thread를 io로 변경하도록하여 비동기 테스크를 수행합니다.(참고로 subscribeOn은 upStream과 downStream 모두 thread를 변경하도록합니다. 쉽게 말해서 subscribeOn을 기준으로 위 아래 thread가 모두 변경되는 것입니다!)

 

이후 io 스레드에서 처리된 작업을 UI에 적용시키기 위해서 다시 MainThread로 변경해야 합니다. 이를 observeOn을 통해서 main thread로 변경하고 subscribe에서 핸들링된 작업들을 처리하도록합니다.(참고로 observeOn은 자신 이후부터 thread를 변경하도록 작업하기 때문에 위에서 작업된 thread를 변경하지않습니다. 그러니까 subscibeOn에 영향을 주지않습니다!)

11. 결과


결과에서 볼 수 있듯이 테이블 값이 하나한 들어가고 자동으로 primaryKey를 생성하도록 만었기 때문에 key값을 별도로 설정하지 않아도 증가되는 것을 볼 수 있습니다. 

'android' 카테고리의 다른 글

android 기기 해상도에 따른 이미지 크기 설정하기  (6) 2020.08.30
android ConstrainLayout  (2) 2020.08.24
ViewModel 사용하기  (4) 2020.08.19
Retrofit2 사용해보기  (12) 2020.08.19
DataBinding 사용해보기  (2) 2020.08.17