서버에 로그인을 하게 되면 accessToken과 refreshToken을 발급해 주는데
우리는 accessToken을 통해 서버와 통신을 하므로 항상 가지고 있어야 한다 !
간단한 방법으로 앱 내부에 저장을 해서 사용하는 법을 알아보자 !
// EncryptedSharedPreference
implementation ("androidx.security:security-crypto:1.1.0-alpha06")
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideEncryptedSharedPreferences(
@ApplicationContext context: Context,
): SharedPreferences {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val prefFileName = "encrypted_prefs"
return EncryptedSharedPreferences.create(
context,
prefFileName,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
}
}
hilt를 사용하여 암호화된 SharedPreferences를 제공하는 방법입니다
@Provides
Hilt에게 이 함수가 의존성 주입을 위한 객체를 제공하는 함수임을 알려줍니다
@Singleton
Hilt에게 이 함수가 제공하는 객체가 애플리케이션 전역에서 단 하나만 생성되고 재사용되어야 함을 명시합니다
provideEncryptedSharedPreferences
함수
암호화된 SharedPreferences 객체를 생성하여 제공
@ApplicationContext context: Context
Hilt가 애플리케이션 Context를 주입하도록 합니다
MasterKey.Builder(context)
암호화에 사용할 마스터 키를 생성하는 객체
이 객체는 암호화된 SharedPreferences 에서 데이터를 암호화하고 해독하는 데 사용합니다
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
마스터 키를 생성할 때 사용할 키 스키마를 설정
AES256_GCM 스키마를 사용하여 AES-256-GCM 알고리즘으로 키를 생성합니다
val prefFileName = "encrypted_prefs"
본인이 사용할 SharedPreferences 파일의 이름
EncryptedSharedPreferences.create
암호화된 SharedPreferences 객체를 생성하는 정적 메서드
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV
SharedPreferences 키를 암호화하는 데 사용할 암호화 스키마를 지정합니다
여기서는 AES256_SIV 스키마를 사용합니다
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
SharedPreferences 값을 암호화하는 데 사용할 암호화 스키마를 지정합니다
여기서는 AES256_GCM 스키마를 사용합니다
class LocalUserDataSourceImpl @Inject constructor(
private val sharedPreferences: SharedPreferences,
) : LocalUserDataSource {
override suspend fun saveToken(provider: String, token: LoginResponse) {
withContext(Dispatchers.IO) {
sharedPreferences.edit().apply {
putString("provider", provider)
putString("ApiAccessToken", token.accessToken)
putString("ApiRefreshToken", token.refreshToken)
}.commit()
}
}
}
저장할 곳에서 Hilt로 SharedPreferences를 주입받고 사용할 곳에서 아래와 같이 사용해 주시면 됩니다
// 동기식 , 호출 시 블로킹
sharedPreferences.edit().apply {
putString("키", 값)
}.commit()
}
- - - - - - - - - - - - - - - - - - - - - - - - - -
// 비동기식, 호출 시 비블로킹
sharedPreferences.edit().apply {
putString("키", 값)
}.apply()
}
}
두 방식 모두 안전하게 사용될 수 있지만, 일반적으로 SharedPreferences 변경 사항을 저장할 때는 apply()를 사용하는 것이 권장됩니다
apply()는 더 효율적이고, 불필요한 블로킹을 피할 수 있습니다
class ProfileGetDataSourceImpl @Inject constructor(
private val profileInfoGetApi: ProfileInfoGetApi,
private val accessToken: SharedPreferences,
): ProfileGetDataSource{
override suspend fun getInformation(): ApiResult<ProfileResponse> {
...
return try {
val profileInfoGetResponse = profileInfoGetApi.getInformation(
accessToken.getString("ApiAccessToken", null),
)
...
}
} catch (e: Exception) {
...
}
}
}
사용할 곳에서 Hilt로 SharedPreferences를 주입받고 사용할 곳에서 아래와 같이 사용해 주시면 됩니다
accessToken.getString("키", null)
뒤에 null을 넣은 이유는 "키"에 해당하는 값이 존재하지 않을 때
null
을 반환하도록 하기 위해서 입니다
중요하지만 간단한 데이터를 저장하기 위해서 여러 가지 데이터 저장 방식을 고민해봤을 때
SharedPreferences는 간단하고, 경량이며, 사용하기 쉬운 데이터 저장 방법으로 제가 하고있는 프로젝트에서 강점이 있다고 생각했습니다
특히, EncryptedSharedPreferences를 사용하면 보안성을 높일 수 있어 민감한 데이터를 안전하게 저장할 수 있습니다