TouchedView ! is Button 조건을 사용하지 않고 dispatchTouch event만 사용했을 경우 키보드가 뜬 상태로 버튼 선택이 되지 않는 SideEffet가 발생하여 선택한 영역이 버튼일 경우에는 제외하는 조건을 추가 하였다.
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
val focusView = currentFocus
if (focusView != null) {
val rect = Rect()
focusView.getGlobalVisibleRect(rect)
val x = ev.x.toInt()
val y = ev.y.toInt()
// 터치된 뷰가 버튼인지 확인
val touchedView = findViewAtPosition(x, y)
if (!rect.contains(x, y) && touchedView !is Button) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(focusView.windowToken, 0)
focusView.clearFocus()
}
}
return super.dispatchTouchEvent(ev)
}
private fun findViewAtPosition(x: Int, y: Int): View? {
return window.decorView.findViewById<ViewGroup>(android.R.id.content)?.let { rootView ->
findViewAtPosition(rootView, x, y)
}
}
private fun findViewAtPosition(parent: ViewGroup, x: Int, y: Int): View? {
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
if (child is ViewGroup) {
val foundView = findViewAtPosition(child, x, y)
if (foundView != null) {
return foundView
}
} else {
val rect = Rect()
child.getGlobalVisibleRect(rect)
if (rect.contains(x, y)) {
return child
}
}
}
return null
}
비교적 간단한 데이터를 저장하기위해 안드로이드 개발 시 SharedPreferences를 사용한다. 설정에 따라서 SharedPreferences 파일은 프레임워크에서 관리하며 비공개이거나 공개로 설정할 수 있다.
비공개로 설정할 경우 일반적인 방법으로는 저장된 데이터를 확인할수는 없지만, 루팅을 통해 관리자 권한을 획득한 경우 저장된 데이터를 확인할 수있다. 개인정보(카드번호, 인증 비밀번호) 등 중요한 데이터가 저장하여야 하는경우에는 SharedPreferences는 적합하지 않다.
Android SDK 23부터 androidx.security를 통해 EncryptedSharedPreferences를 제공한다.
안드로이드 프로젝트 빌드 중 Execution failed for task ':app:validateSigningDebug'에러가 발생하였다.
이유는 프로젝트 내에 debug.keystore가 존재하지 않아서 발생하였다.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:validateSigningDebug'.
> Keystore file '/Users/Documents/gitlab/app-frontend/android/app/debug.keystore' not found for signing config 'debug'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 13s
error Failed to install the app. Make sure you have the Android development environment set up: https://reactnative.dev/docs/environment-setup. Run CLI with --verbose flag for more details.
Error: Command failed: ./gradlew app:installDebug -PreactNativeDevServerPort=8081
Warning: Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01
debug.keystore는 /Users/.android/debug.keystore 에 존재하며, 로컬에 존재하는 debug.keystore를 해당경로로 옮겨주니 정상적으로 빌드가 되었다.
Failed to construct transformer: Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:71:19)
at Object.createHash (node:crypto:133:10)
at stableHash (/Users/Documents/gitlab/app-frontend/node_modules/metro-cache/src/stableHash.js:19:8)
at JsTransformer.getCacheKey (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/JSTransformer/worker.js:478:7)
at getTransformCacheKey (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/DeltaBundler/Transformer/getTransformCacheKey.js:39:29)
at new Transformer (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/DeltaBundler/Transformer.js:147:28)
at /Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:54:29
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
[Tue Nov 29 2022 13:52:10.710] BUNDLE ./index.js
error: TypeError: Cannot read properties of undefined (reading 'transformFile')
at /Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:91:34
at Generator.next (<anonymous>)
at asyncGeneratorStep (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:14:24)
at _next (/Users/Documents/gitlab/app-frontend/node_modules/metro/src/Bundler.js:34:9)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
오류 내용으로 검색해보니 node v17이상에서는 digital envelope routines이 지원되지 않아서 발생하는 문제라고 되어있었다.
nvm으로 노드 버전을 v12.22.12 으로 변경 후(프로젝트의 ReadMe의 환경 설정에 노드 버전이 12.x.x버전) node_modules을 제거후 다시 npm install을 해보았다.
동일한 Error: error:0308010C:digital envelope routines::unsupported 오류가 계속 발생하여 반나절 정도 삽질을 하였다.
삽질 중 nvm 의 default버전이 v18.12.1로 되어 있어 있는데, 에러가 이부분에 연관된 것이 아닌가라는 의심이 들어 혹시나 하는 마음에 nvm default 버전을 변경해 보았다.
nvm alias default 12.22.12
변경 후 nvm list명령어로 nvm defalut가 정상적으로 변경되었음을 확인하였다.
node_module을 제거 후 새로운 터미널에서 다시 npm install 후 실행을 하니 정상적으로 앱이 실행되었다.
switch (manager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
case BiometricManager.BIOMETRIC_SUCCESS: { // 생체 인증 가능
Log.d("MainActivity", "Application can authenticate with biometrics.");
break;
}
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: { // 기기에서 생체 인증을 지원하지 않는 경우
Log.d("MainActivity", "Biometric facility is not available in this device.");
break;
}
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: {
Log.d("MainActivity", "Biometric facility is currently not available");
break;
}
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: { // 생체 인식 정보가 등록되지 않은 경우
Log.d("MainActivity", "Any biometric credential is not added in this device.");
break;
}
default: { // 기타 실패
Log.d("MainActivity", "Fail Biometric facility");
break;
}
}
생체 인증 등록
얼굴 인식의 경우는 Android 11 (API level 30)부터 지원
BiometricPrompt.PromptInfo.Builder promptBuilder = new BiometricPrompt.PromptInfo.Builder();
promptBuilder.setTitle("Biometric login for my app");
promptBuilder.setSubtitle("Log in using your biometric credential");
promptBuilder.setNegativeButtonText("Use account password");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){ // 안면인식 ap사용 android 11부터 지원
promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL);
}
promptInfo = promptBuilder.build();
Android 앱을 개발하면서 서버와 통신을 하게 되는데 그때 Rest API를 많이 사용한다. 앱 개발을 하면서 Rest를 사용하였고 주변에서도 Rest라는 용어를 많이 사용하지만 누군가 Rest가 무엇이냐고 물었을 때 "클라이언트와 서버 사이에 값을 주고 받는 것" 라는 두리뭉실한 이야기 밖에 해 줄수 없다는 것에 부끄러움을 느끼며, 블로그에 정리 하게되었다.
* Rest란?
Rest(Representational state transfer)를 있는 그대로 해석하면 표현을 통한 상태의 전이 라고 할수 있다. 여기서 표현이란 Http 요청 메시지와 거기에 해당하는 리소스의 내용을 전달하는 Http 응답 메시지로 간단히 설명하면 클라이언트와 서버간에 메시지로 서로의 상태변화를 공유하자는 것이다. 이런 Rest이론을 따르는 시스템구조를 Restful 시스템이라고 부른다.
* Rest의 설계 원칙
1) 모든 자원은 URI(URL)로 관리 2) 클라이언트의 상태와 기능은 서버로 요청한 자원으로 파악 3) URI(URL)는 클라이언트와 서버 간의 자원을 가리키는 유일한 인터페이스 4) 클라이언트의 요청 정보는 서버에 저장되지 않음
서버내의 자원은 URL(Uniform Resource Locator) 혹은 URI(Uniform Resource Identifier)을 이용하여 관리한다.
URL 형식 : 서버 주소 + 서비스 이름+ 자원
* 장점
- URI(URL)형태로 자원을 관리 하기 떄문에 느슨한 결합(loose coupling)이 가능하며, 속도 또한 빠르다.(URI(URL)형태로 자원을 관리 하기 떄문에 자주 사용하는 자원에 대해 캐싱 처리가 가능하다.)
- Http 프로토콜을 사용하여 패킷의 변화 없이 그래도 하용할수 있다. - 결합도가 낮아 확장성이나 배포가 편리하다. - 거의 모든 운영체제의 지원이 가능하며 별도의 라이브러리 배포없이 사용이 가능하다. - 서버와 클라이언트의 역할이 독립되어 있어 변경이나 확장이 용이하다.
* 단점
- 공급자가 같더라도 서버별로 Rest API사이에 일관성이 없다. - Rest 모델에 적합하지 않은 형태의 URI(URL) 체계가 존재한다
- 표준화된 구성이나 정의가 없다.
이론적으로 Rest에 대해 간단히 정리하였고, 나중에 시간이 된다면 더 정리해 놓아야 할것 같다.