이번 유닛에서는 Room 라이브러리의 활용과 데이터베이스 관리에 대해서 집중적으로 학습했습니다.
이 글에서는 실습 내용보다는 학습할 때 접했던 개념들을 간단하게 정리하고자 합니다.
구체적인 내용은 영어로 된 공식 문서에서 공부할 수 있습니다ㅎㅎ
Room
room 라이브러리는 Android Jetpack의 데이터베이스 라이브러리 중 하나입니다.
build.grade에서 의존성을 설정해서 사용할 수 있습니다.
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,
@ColumnInfo(name = "start_time_milli")
val startTimeMilli: Long = System.currentTimeMillis(),
@ColumnInfo(name = "end_time_milli")
var endTimeMilli: Long = startTimeMilli,
@ColumnInfo(name = "quality_rating")
var sleepQuality: Int = -1
)
실습에서는 자바 기반의 언어답게 @어노테이션을 많이 활용해주는 모습을 볼 수 있었습니다.
@어노테이션은 가독성도 많이 좋아지고 코드가 간결해지는 장점이 있죠
Room 라이브러리의 어노테이션을 사용해 Entity, Primary key 등 데이터베이스를 깔끔하게 관리했습니다.
Database DAO
DAO는 Data Access Object의 약자로, DB에 접근하는 객체를 의미합니다.
SELECT, DELETE, UPDATE 등의 SQL 문법을 통해 DB에 접근할 수 있습니다.
간편하게 @DAO 어노테이션으로 DAO Interface를 만드는 실습을 진행했습니다.
SQL 문법에서는 대문자로 가독성을 높이는 경향이 있었습니다.
Static vs Companion Object
자바에서 Static 키워드를 접한 적이 있습니다. 하지만 Companion object라는 키워드 코틀린을 공부하게 되면서 처음 접해봤는데요
두 키워드는 차이가 있습니다.
먼저 자바에서는 클래스 멤버임을 지정하기 위해 static 키워드를 붙입니다. 클래스가 메모리에 적재될 때 자동으로 함께 생성이 되기 때문에 인스턴스 생성 없이 바로 참조할 수 있다는 특징이 있습니다.
간단히 말해서 클래스가 만들어질 때 자동으로 타고나는 특징이 있습니다. main 함수를 선언할 때 static void main() 과 같이 선언해야 하는 언어도 있죠
static과 다르게 companion object는 단어 뒤에 object라는 말이 붙어있습니다. 말그대로 객체라는 뜻입니다. 객체이기 때문에 변수에 할당할 수 있고, static 키워드 만으로는 클래스 멤버를 독립된 객체로 여길 수 없습니다.
volatile
발음하기도 힘든 키워드를 처음 접했네요.
이 키워드는 항상 메모리에 접근하라는 뜻입니다.
프로그래머가 코딩을 하고, 컴퓨터는 컴파일이라는 작업을 하게됩니다. 제가 쓴 코드를 번역해서 컴퓨터가 알아들으려고 하는 작업입니다.
설명을 위해 C언어로 된 코드로 예를 들어 보겠습니다.
int i = 0;
while(i < 10)
i++
이 코드는 컴파일 하는 동안 i = 10; 이라는 코드로 변하게 됩니다. 굳이 실행할 때 번거롭게 계산하지 말고 번역할 때 미리 계산을 합니다.
volatile int i = 0;
while(i < 10)
i++;
하지만 이처럼 volatile 키워드를 붙여준다면 i에게 1씩 10번 더해주는 반복문은 미리 처리하지 않고 실행할 때가 되어서야 그때그때 열 번 덧셈을 진행합니다. 실습에서는 @volatile 어노테이션을 사용해서 싱글톤 패턴을 구현해보았습니다,
Test
누구든 코드를 실행하면 오류를 경험합니다. 저도 C언어를 공부할 때에는 printf("check"); 코드를 여기저기 추가해보면서 오류가 어디서 발생했는지 찾아다니곤 했는데요. 이런 무식한 방법을 사용하지 않고 잘 실행되는지 테스트하는 방식을 실습했습니다.
@RunWith(AndroidJUnit4::class)
class SleepDatabaseTest {
private lateinit var sleepDao: SleepDatabaseDao
private lateinit var db: SleepDatabase
@Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
// Using an in-memory database because the information stored here disappears when the
// process is killed.
db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
sleepDao = db.sleepDatabaseDao
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun insertAndGetNight() {
val night = SleepNight()
sleepDao.insert(night)
val tonight = sleepDao.getTonight()
assertEquals(tonight?.sleepQuality, -1)
}
}
코드가 길다고 당황하지 않고, 천천히 아는 영단어만 보더라도 이해할 수 있는 코드입니다.
- @Befor createDB()
- 객체를 생성할 때 DB를 생성합니다. @Before createDB()
- @Test insertAndGetNight()
- DAO에 night를 insert하고, DAO에서 tonight을 얻었을 때 tonight이랑 sleepQuality가 일치하는지 테스트합니다
- @After closeDB()
- 끝났으니 db를 닫겠습니다.
JUnit4라고 하는 Java 유닛단위 테스트를 사용했습니다.
이러한 테스트 코드를 사용하는 방식은 협업에서도 장점이 있습니다.
TDD라는 방식을 살짝 예로 들어 보겠습니다.
TDD
TDD는 Test Driven Development의 약자로,
테스트가 개발을 이끌어나간다는 뜻입니다.
테스트를 먼저 작성해서 수시로 피드백을 받는 기법입니다.
위에서 장점만 언급해서 자칫 만능 방법이라고 느낄 수도 있지만, 만능은 아닙니다.
일단 장점으로는 유지보수 비용과 버그가 줄어들고 가독성이 좋아집니다.
이러한 방식은 불확실성이 높을 때 사용하면 좋은 방법입니다..
구체적으로는
- 주제를 처음 접했을 때
- 고객의 요구조건이 수시로 바뀔 때
- 수정할 일이 많을 때
- 유지보수의 역할을 넘겨야할 때
사용하면 좋습니다.
협업에서의 장점은 크게 두가지가 있습니다.
첫째로 내 코드를 잘 모르는 사람이 보더라도 값을 대입해보면서 테스트 해볼 수 있다는 장점이 있습니다.
둘째로는 남의 코드를 수정할 때 잘못 건드린다면 테스트 코드가 나에게 피드백을 자세히 해줍니다.
이런 특징은 남이 짜놓은 긴 코드를 접했을 때 엄두가 안나더라도 자신있게 코드를 수정할 수 있는 장점이 있습니다.
하지만 단점도 있습니다.
코딩을 할 때 미래의 유지보수를 대비하기에 좋지만, 당장의 성과가 중요할 때가 많습니다.
개발시간이 테스트 코드를 짜는 시간까지 포함해서 많이 필요합니다.
도구와 규칙에 얽매이는 것은 애자일에 모순됩니다. 복사 붙여넣기가 반복되며 일정한 패턴에 갇혀 코딩하게 될 수 있습니다.
이외에도 BDD와 같이 개발자가 아닌 사용자의 관점으로 코딩하는 방법도 있습니다.
비동기
구글에 검색을 하러 갔습니다. 하지만 구글 로고에 문제가 생겨서 느릿~느릿하게 이미지가 나타납니다.
만약 구글 검색 메인화면을 코딩했을 때, 로고보다 검색창이 뒤에 코딩됐다면,
이미지가 다 나타날 때 까지 우린 검색 기능을 쓸 수 없을까요?
이 때 필요한 방식이 비동기 방식입니다.
코틀린에서의 비동기 처리 방식은 Rx(ReactiveX)를 사용하는 방식에서 Coroutine을 사용하는 방식으로 넘어가고 있습니다.
Rx
Rx 방식은 RxJava,RxKotlin 등이 있고, 옵저버 패턴 + 이터레이터 패턴 + 함수형 프로그래밍이 결합한 형태를 갖습니다.
옵저버 패턴으로 읽은 data를 stream 형태로 내려보내는 방식입니다.
Coroutine
coroutine 방식은 함수를 호출하고, return을 기다리는 방식입니다.
이외에도 Thread 방식이 있는데, 힘들고 잦은 구현이 필요헌 방식입니다.
코루틴에는 크게 세가지 키워드를 접할 수 있습니다.
- Job
- 모든 코루틴은 그것을 취소시킬 수 있는 job을 갖고 있습니다.
- Dispatcher
- CoroutineContext를 상속받아 어떤 스레드를 이용해 어떻게 동작할지 정의합니다.
- Scope
- 코루틴을 묶음으로 관리합니다.
- CoroutineScope는 코루틴이 실행될 수 있게 관리합니다.
suspend 한정자도 접할 수 있는데요
private suspend fun getTonightFromDatabase(): SleepNight? {
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
return night
}
suspend는 한글로 매달린다는 뜻을 가지고 있습니다. 사용하던 함수를 잠깐 매달아두는 느낌을 갖고있습니다.
suspend 한정자는 현재의 코루틴을 멈추고, resume 한정자로 멈춰있던 코루틴을 다시 시작할 수 있습니다.
자칫 백그라운드에서 실행되는 것처럽 보일 수 있지만 코루틴은 메인 쓰레드 위에서 실행됩니다.
디자인 패턴
안드로이드 공부를 하다보면 여러가지 디자인 패턴을 접하게 됩니다.
이번 유닛에서는
Adapter pattern : 같이 쓸 수 없는 클래스를 연결해서 쓰는 방식
ViewHolder pattern : 뷰를 보관하는 Holder 객체를 둬서 findViewById()와 같은 반복적 메서드를 줄이는 방식
을 실습했습니다. 빠른 시일 내로 디자인 패턴에 대해서도 자세히 다뤄보고 싶네요
이후에는 RecyclerView를 활용하는 실습을 진행했고, 실습 위주라서 따로 게시하지는 않겠습니다.
이 글을 읽으시는 분들 께서는 비동기, DB, 테스트, 디자인 패턴과 같은 키워드를 접해보시고
간단히 무슨말인지 이해하셨으면 공식문서 등을 찾아보며 더 자세히 공부해보는 기회가 되었으면 좋겠네요
긴 글 읽어주셔서 감사합니다ㅎㅎ
'안드로이드' 카테고리의 다른 글
[android 1 team]8. Connect to the internet (0) | 2021.03.06 |
---|---|
Android 1Team LiveData (0) | 2021.03.01 |
Android - 1Team View Model (0) | 2021.03.01 |
[android-1team] unit 04. Navigation (0) | 2021.01.23 |
[android - 1team] Unit3 : Layout (0) | 2021.01.16 |