build(room): KSP room.schemaLocation 설정 및 exportSchema=true로 스키마 export 활성화
프로젝트가 이미 KSP를 사용하고 있어 KSP 인수 기반으로 Room 스키마 export를 활성화했습니다.
- app/build.gradle: ksp { room.schemaLocation 등 } 추가
- Room DB 클래스 3종: exportSchema=true
- app/schemas 디렉터리 버전 관리
			
			
This commit is contained in:
		@@ -214,3 +214,11 @@ dependencies {
 | 
				
			|||||||
    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.3.1'
 | 
					    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.3.1'
 | 
				
			||||||
    testImplementation 'io.mockk:mockk:1.13.10'
 | 
					    testImplementation 'io.mockk:mockk:1.13.10'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// KSP args for Room schema export
 | 
				
			||||||
 | 
					ksp {
 | 
				
			||||||
 | 
					    arg("room.schemaLocation", "$projectDir/schemas")
 | 
				
			||||||
 | 
					    arg("room.incremental", "true")
 | 
				
			||||||
 | 
					    arg("room.expandProjection", "true")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								app/schemas/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/schemas/.gitkeep
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					# Keep schemas directory under version control
 | 
				
			||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "formatVersion": 1,
 | 
				
			||||||
 | 
					  "database": {
 | 
				
			||||||
 | 
					    "version": 1,
 | 
				
			||||||
 | 
					    "identityHash": "b9a331035b36b70f8ca7a14962b13fdf",
 | 
				
			||||||
 | 
					    "entities": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "tableName": "playback_tracking",
 | 
				
			||||||
 | 
					        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `contentId` INTEGER NOT NULL, `totalDuration` INTEGER NOT NULL, `startPosition` INTEGER NOT NULL, `isFree` INTEGER NOT NULL, `isPreview` INTEGER NOT NULL, `endPosition` INTEGER, `playDateTime` TEXT NOT NULL)",
 | 
				
			||||||
 | 
					        "fields": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "id",
 | 
				
			||||||
 | 
					            "columnName": "id",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "contentId",
 | 
				
			||||||
 | 
					            "columnName": "contentId",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "totalDuration",
 | 
				
			||||||
 | 
					            "columnName": "totalDuration",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "startPosition",
 | 
				
			||||||
 | 
					            "columnName": "startPosition",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "isFree",
 | 
				
			||||||
 | 
					            "columnName": "isFree",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "isPreview",
 | 
				
			||||||
 | 
					            "columnName": "isPreview",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "endPosition",
 | 
				
			||||||
 | 
					            "columnName": "endPosition",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": false
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "playDateTime",
 | 
				
			||||||
 | 
					            "columnName": "playDateTime",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "primaryKey": {
 | 
				
			||||||
 | 
					          "autoGenerate": true,
 | 
				
			||||||
 | 
					          "columnNames": [
 | 
				
			||||||
 | 
					            "id"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "indices": [],
 | 
				
			||||||
 | 
					        "foreignKeys": []
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "views": [],
 | 
				
			||||||
 | 
					    "setupQueries": [
 | 
				
			||||||
 | 
					      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
 | 
				
			||||||
 | 
					      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b9a331035b36b70f8ca7a14962b13fdf')"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "formatVersion": 1,
 | 
				
			||||||
 | 
					  "database": {
 | 
				
			||||||
 | 
					    "version": 2,
 | 
				
			||||||
 | 
					    "identityHash": "7429c2998f64cb70e5e8b1d2525a4708",
 | 
				
			||||||
 | 
					    "entities": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "tableName": "alarms",
 | 
				
			||||||
 | 
					        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `time` INTEGER NOT NULL, `days` TEXT NOT NULL, `contentId` INTEGER NOT NULL, `contentTitle` TEXT NOT NULL, `contentCreatorNickname` TEXT NOT NULL, `volume` INTEGER NOT NULL, `isEnabled` INTEGER NOT NULL)",
 | 
				
			||||||
 | 
					        "fields": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "id",
 | 
				
			||||||
 | 
					            "columnName": "id",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "title",
 | 
				
			||||||
 | 
					            "columnName": "title",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "time",
 | 
				
			||||||
 | 
					            "columnName": "time",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "days",
 | 
				
			||||||
 | 
					            "columnName": "days",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "contentId",
 | 
				
			||||||
 | 
					            "columnName": "contentId",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "contentTitle",
 | 
				
			||||||
 | 
					            "columnName": "contentTitle",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "contentCreatorNickname",
 | 
				
			||||||
 | 
					            "columnName": "contentCreatorNickname",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "volume",
 | 
				
			||||||
 | 
					            "columnName": "volume",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "isEnabled",
 | 
				
			||||||
 | 
					            "columnName": "isEnabled",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "primaryKey": {
 | 
				
			||||||
 | 
					          "autoGenerate": true,
 | 
				
			||||||
 | 
					          "columnNames": [
 | 
				
			||||||
 | 
					            "id"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "indices": [],
 | 
				
			||||||
 | 
					        "foreignKeys": []
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "views": [],
 | 
				
			||||||
 | 
					    "setupQueries": [
 | 
				
			||||||
 | 
					      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
 | 
				
			||||||
 | 
					      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7429c2998f64cb70e5e8b1d2525a4708')"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "formatVersion": 1,
 | 
				
			||||||
 | 
					  "database": {
 | 
				
			||||||
 | 
					    "version": 1,
 | 
				
			||||||
 | 
					    "identityHash": "e46a8b457c3ea6ceefd0db76bb763056",
 | 
				
			||||||
 | 
					    "entities": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "tableName": "recent_contents",
 | 
				
			||||||
 | 
					        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`contentId` INTEGER NOT NULL, `coverImageUrl` TEXT NOT NULL, `title` TEXT NOT NULL, `creatorNickname` TEXT NOT NULL, `listenedAt` INTEGER NOT NULL, PRIMARY KEY(`contentId`))",
 | 
				
			||||||
 | 
					        "fields": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "contentId",
 | 
				
			||||||
 | 
					            "columnName": "contentId",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "coverImageUrl",
 | 
				
			||||||
 | 
					            "columnName": "coverImageUrl",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "title",
 | 
				
			||||||
 | 
					            "columnName": "title",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "creatorNickname",
 | 
				
			||||||
 | 
					            "columnName": "creatorNickname",
 | 
				
			||||||
 | 
					            "affinity": "TEXT",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "fieldPath": "listenedAt",
 | 
				
			||||||
 | 
					            "columnName": "listenedAt",
 | 
				
			||||||
 | 
					            "affinity": "INTEGER",
 | 
				
			||||||
 | 
					            "notNull": true
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "primaryKey": {
 | 
				
			||||||
 | 
					          "autoGenerate": false,
 | 
				
			||||||
 | 
					          "columnNames": [
 | 
				
			||||||
 | 
					            "contentId"
 | 
				
			||||||
 | 
					          ]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "indices": [],
 | 
				
			||||||
 | 
					        "foreignKeys": []
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    "views": [],
 | 
				
			||||||
 | 
					    "setupQueries": [
 | 
				
			||||||
 | 
					      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
 | 
				
			||||||
 | 
					      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e46a8b457c3ea6ceefd0db76bb763056')"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,7 +8,7 @@ import androidx.room.TypeConverters
 | 
				
			|||||||
import kr.co.vividnext.sodalive.audio_content.PlaybackTracking
 | 
					import kr.co.vividnext.sodalive.audio_content.PlaybackTracking
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.Converter
 | 
					import kr.co.vividnext.sodalive.common.Converter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Database(entities = [PlaybackTracking::class], version = 1)
 | 
					@Database(entities = [PlaybackTracking::class], version = 1, exportSchema = true)
 | 
				
			||||||
@TypeConverters(Converter::class)
 | 
					@TypeConverters(Converter::class)
 | 
				
			||||||
abstract class PlaybackTrackingDatabase : RoomDatabase() {
 | 
					abstract class PlaybackTrackingDatabase : RoomDatabase() {
 | 
				
			||||||
    abstract fun playbackTrackingDao(): PlaybackTrackingDao
 | 
					    abstract fun playbackTrackingDao(): PlaybackTrackingDao
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ import androidx.room.migration.Migration
 | 
				
			|||||||
import androidx.sqlite.db.SupportSQLiteDatabase
 | 
					import androidx.sqlite.db.SupportSQLiteDatabase
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.Converter
 | 
					import kr.co.vividnext.sodalive.common.Converter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Database(entities = [Alarm::class], version = 2)
 | 
					@Database(entities = [Alarm::class], version = 2, exportSchema = true)
 | 
				
			||||||
@TypeConverters(Converter::class)
 | 
					@TypeConverters(Converter::class)
 | 
				
			||||||
abstract class AlarmDatabase : RoomDatabase() {
 | 
					abstract class AlarmDatabase : RoomDatabase() {
 | 
				
			||||||
    abstract fun alarmDao(): AlarmDao
 | 
					    abstract fun alarmDao(): AlarmDao
 | 
				
			||||||
@@ -33,8 +33,8 @@ abstract class AlarmDatabase : RoomDatabase() {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private val MIGRATION_1_2 = object : Migration(1, 2) {
 | 
					        private val MIGRATION_1_2 = object : Migration(1, 2) {
 | 
				
			||||||
            override fun migrate(database: SupportSQLiteDatabase) {
 | 
					            override fun migrate(db: SupportSQLiteDatabase) {
 | 
				
			||||||
                database.execSQL(
 | 
					                db.execSQL(
 | 
				
			||||||
                    "ALTER TABLE 'alarms' ADD COLUMN 'volume' integer not null default 15"
 | 
					                    "ALTER TABLE 'alarms' ADD COLUMN 'volume' integer not null default 15"
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ import androidx.room.RoomDatabase
 | 
				
			|||||||
import androidx.room.TypeConverters
 | 
					import androidx.room.TypeConverters
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.Converter
 | 
					import kr.co.vividnext.sodalive.common.Converter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Database(entities = [RecentContent::class], version = 1)
 | 
					@Database(entities = [RecentContent::class], version = 1, exportSchema = true)
 | 
				
			||||||
@TypeConverters(Converter::class)
 | 
					@TypeConverters(Converter::class)
 | 
				
			||||||
abstract class RecentContentDatabase : RoomDatabase() {
 | 
					abstract class RecentContentDatabase : RoomDatabase() {
 | 
				
			||||||
    abstract fun recentContentDao(): RecentContentDao
 | 
					    abstract fun recentContentDao(): RecentContentDao
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,8 +16,8 @@ buildscript {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
plugins {
 | 
					plugins {
 | 
				
			||||||
    id 'com.android.application' version '8.4.2' apply false
 | 
					    id 'com.android.application' version '8.7.2' apply false
 | 
				
			||||||
    id 'com.android.library' version '8.4.2' apply false
 | 
					    id 'com.android.library' version '8.7.2' apply false
 | 
				
			||||||
    id 'org.jetbrains.kotlin.android' version '2.0.21' apply false
 | 
					    id 'org.jetbrains.kotlin.android' version '2.0.21' apply false
 | 
				
			||||||
    id 'com.google.devtools.ksp' version '2.0.21-1.0.25' apply false
 | 
					    id 'com.google.devtools.ksp' version '2.0.21-1.0.25' apply false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
#Sun Jul 23 18:26:44 KST 2023
 | 
					#Sun Jul 23 18:26:44 KST 2023
 | 
				
			||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
zipStorePath=wrapper/dists
 | 
					zipStorePath=wrapper/dists
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user