App 개발 일지/Nutri Capture

Nutri Capture 백엔드 - DAO 논리적 오류 수정

interfacer_han 2024. 11. 26. 19:58

#1 개요

프로젝트의 데이터베이스(Room) 부분에서 작성했던 DAO 함수의 논리적 오류를 해결한다.
 

#2 논리적 오류 - ORDER BY

#2-1 문제 코드

...

@Dao
interface MainDAO {
    ...

    @Query("SELECT * FROM DayMealView")
    suspend fun getAllDayMeals(): List<DayMealView>

    @Query("SELECT * FROM DayMealView LIMIT :limit")
    suspend fun getAllDayMeals(limit: Int): List<DayMealView>

    @Query("""
    SELECT * FROM DayMealView 
    WHERE day_date <= :lastDate
      AND meal_time <= :lastTime 
      AND meal_id != :lastId
    LIMIT :limit
    """)
    suspend fun getNextDayMealsAfter(
        lastDate: LocalDate,
        lastTime: LocalTime,
        lastId: Long,
        limit: Int
    ): List<DayMealView>

    @Query("SELECT * FROM DayMealView WHERE meal_id = :mealId LIMIT 1")
    suspend fun getDayMeal(mealId: Long): DayMealView
}

데이터베이스는 복수의 레코드를 SELECT할 때, 정직하게 가져오지 않는다. 무슨 말이냐면, 레코드에 정렬된 순서 그대로를 지켜서 반환하지 않는다는 얘기다. DayMealView에 삽입되어있던 순서 그대로를 지켜서 반환된다고 생각하면 안 된다. 웬만하면 순서가 지켜겠지만 반드시 그렇지는 않다는 얘기다.
 

#2-2 해결

...

@Dao
interface MainDAO {
    ...

    @Query("""
    SELECT * FROM DayMealView
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    """)
    suspend fun getAllDayMeals(): List<DayMealView>

    @Query("""
    SELECT * FROM DayMealView
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    LIMIT :limit
    """)
    suspend fun getAllDayMeals(limit: Int): List<DayMealView>

    @Query("""
    SELECT * FROM DayMealView 
    WHERE day_date <= :lastDate
      AND meal_time <= :lastTime 
      AND meal_id != :lastId
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    LIMIT :limit
    """)
    suspend fun getNextDayMealsAfter(
        lastDate: LocalDate,
        lastTime: LocalTime,
        lastId: Long,
        limit: Int
    ): List<DayMealView>

    @Query("""
    SELECT * FROM DayMealView
    WHERE meal_id = :mealId
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    LIMIT 1
    """)
    suspend fun getDayMeal(mealId: Long): DayMealView
}

ORDER BY문을 추가한다. getDayMeal()은 복수 개의 레코드를 가져오지 않기 때문에 ORDER BY문이 필요없다. 하지만, 머지않은 미래에 getDayMeal()를 복사ㆍ붙여넣기해 새로운 기능을 수행하는 DAO 함수를 추가할 것 같은 예감이 들기에 (실수 방지를 위해) 그냥 냅두겠다. 이 부분은 나중에 리팩토링을 목적으로 프로젝트를 처음부터 끝까지 다듬을 때 수정될 것이다.

#3 논리적 오류 - AND

#3-1 문제 상황

... meal_id day_date meal_time ...
... ... ... ... ...
... 81 2024-11-25 09:50:57.13994 ...
... 80 2024-11-13 18:41:07.954925 ...
... ... ... ... ...

'meal_id가 80인 레코드' 다음 레코드인 'meal_id가 81인 레코드'가 LazyColumn 상에서 Load되지 않는 문제가 발생했다. 이것만 봐서는 문제의 원인을 알 수 없었지만, day_date가 차이난다는 점에서 뭔가 잘못되지 않았나 추측이 된다.
 

#3-2 문제 코드

...

@Dao
interface MainDAO {
    ...

    @Query("""
    SELECT * FROM DayMealView 
    WHERE day_date <= :lastDate
      AND meal_time <= :lastTime 
      AND meal_id != :lastId
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    LIMIT :limit
    """)
    suspend fun getNextDayMealsAfter(
        lastDate: LocalDate,
        lastTime: LocalTime,
        lastId: Long,
        limit: Int
    ): List<DayMealView>

    ...
}

문제는 AND의 남발이었다. date가 작거나 같으면서 동시에 time이 작거나 같으면서 동시에 meal_id는 달라야한다는 조건. 이 조건은 AND의 나열로 구성되었는데, 이러면 내가 정확히 원하는 조건과 논리적으로 다른 조건이 된다.
 
DayMealView는 1차로 date에 대해 내림차순으로 정렬하고, date가 같으면 time에 대해 time마저 같으면 id에 대해 내림차순으로 정렬하는 테이블이며, 이 테이블의 정렬 조건을 준수하여 WHERE문을 작성했어야 했다. 제대로 다시 쿼리의 WHERE문을 수정해 내가 정확히 원하는 조건과 같게 만들어본다.
 

#3-3 해결

...

@Dao
interface MainDAO {
    ...

    @Query("""
    SELECT * FROM DayMealView 
    WHERE day_date < :lastDate
       OR (day_date = :lastDate AND meal_time < :lastTime)
       OR (day_date = :lastDate AND meal_time = :lastTime AND meal_id < :lastId)
    ORDER BY day_date DESC,
             meal_time DESC,
             meal_id DESC
    LIMIT :limit
    """)
    suspend fun getNextDayMealsAfter(
        ...
    ): ...

    ...
}

#3-2에서 말한 내가 정확히 원하는 조건 그대로를 쿼리문에 넣었다. WHERE 문이 DayMealView의 정렬 조건을 준수하게 만든 것이다.
 

#4 완성된 앱

#4-1 이 게시글 시점의 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-2 본 프로젝트의 가장 최신 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com