한참 헤매다.. app script 의 설정에 문제가 있는것을 알았다.
웹앱 배포 시 엑세스 권한이 나에게만 되어 있는지 확인 후 "모든사용자" 엑세스 권한으로 변경 후 배포 한다.
반나절을 날렸다..
'기타 > 기타' 카테고리의 다른 글
mac m1(Apple Silicon) 호환 JDK 설치 (1) | 2021.07.05 |
---|---|
'outlook 닫는중' 강제 종료 방법 (0) | 2021.01.07 |
한참 헤매다.. app script 의 설정에 문제가 있는것을 알았다.
웹앱 배포 시 엑세스 권한이 나에게만 되어 있는지 확인 후 "모든사용자" 엑세스 권한으로 변경 후 배포 한다.
반나절을 날렸다..
mac m1(Apple Silicon) 호환 JDK 설치 (1) | 2021.07.05 |
---|---|
'outlook 닫는중' 강제 종료 방법 (0) | 2021.01.07 |
http 통신 에러 D NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: false
NetworkSecurityConfig: No Network Security Config specified, using platform default
Flutter apk 빌드 후 http 통신 불가 로그 확인
logcat 확인 시 아래와 같은 로그 확인 함
"D NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: false"
검색 결과 Android 설정 문제로 확인 되어 아래 처럼 내용 수정 후 빌드 시 정상 확인
1. main -> AndroidManifest.xml 수정
<manifest
(...)
<uses-permission android:name="android.permission.INTERNET" /> <= 추가
<application
android:usesCleartextTraffic="true" <= 추가
android:networkSecurityConfig="@xml/network_security_config" <= 추가
(...)
>
<activity
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
2. main/res/xml 디렉토리 파일 생성 -> network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
플러터에서 웹 빌드 (Missing index.html. 에러처리) (0) | 2020.10.14 |
---|
so 별 별도 로직이 존재하므로 반복과 중복 if 문을 써서 코드가 복잡해 지는 구조가 있다.
java reflect 를 이용하여 동적으로 class 및 method 호출 과정에 대해 기록한다.
전체 코드는 다음 링크에서 확인 (https://github.com/pari0130/reflection)
service 별 버전이 상이하여 별도 클래스로 분리 할 경우 호출하는 로직에서 if 문 중첩이 발생 될 수 있으므로 동적 호출 방안에 대해 기록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* 설명
*
* Retention RunTime : compile time 과 binary 에도 포함되고, reflection 을 통해 접근 가능
*
* Target CLASS : class, interface, object, annotation class 에 사용 가능하도록 제한
*
* values : 서비스 버전 정보 입력을 위해 사용
*
* */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS)
annotation class ServiceVersion(
val values: Array<String> = []
)
|
interface TestService {
fun getTestItems(item: String): Map<String, Any>
fun insertCharge(param: Map<String, Any>)
}
// v1 로 사용 될 class
@Service("TestServiceV1Impl")
@ServiceVersion(values = ["v1"])
class TestServiceV1Impl(val testDiService: TestDiService) : TestService { }
// v1.1 로 사용 될 class
@Service("TestServiceV11Impl")
@ServiceVersion(values = ["v1.1"])
class TestServiceV11Impl(val testDiService: TestDiService) : TestService { }
// v1.2 로 사용 될 class
@Service("TestServiceV12Impl")
@ServiceVersion(values = ["v1.2"])
class TestServiceV12Impl(val testDiService: TestDiService) : TestService { }
|
override fun getTestService(version: String): TestService {
return applicationContext.getBeanProvider(TestService::class.java).stream() // TestService interface 를 상속한 Bean 조회
.filter { b -> !ObjectUtils.isEmpty(b.javaClass.getDeclaredAnnotation(ServiceVersion::class.java)) }
.filter { b ->
Arrays.stream(b.javaClass.getDeclaredAnnotation(ServiceVersion::class.java).values) // version annotation 을 사용하는 method 중 일치하는 값 조회
.anyMatch { v -> v.equals(version) }
}
.findAny()
.orElse(applicationContext.getBean("TestServiceV1Impl", TestService::class.java))
}
|
@Test
fun getTestService() {
var version = "v1"
val itemV1 = testDiService.getTestService(version).getTestItems("v1 item test")
version = "v1.1"
val itemV11 = testDiService.getTestService(version).getTestItems("v1.1 item test")
version = "v1.2"
val itemV12 = testDiService.getTestService(version).getTestItems("v1.2 item test")
logger.info("[TEST] item -> { $itemV1, $itemV11, $itemV12 }") // [TEST] item -> { {item=v1 item test}, {item=v1.1 item test}, {item=v1.2 item test} }
assertAll(
{ Assertions.assertEquals(itemV1["item"], "v1 item test") },
{ Assertions.assertEquals(itemV11["item"], "v1.1 item test") },
{ Assertions.assertEquals(itemV12["item"], "v1.2 item test") }
)
}
|
so 별 특정 함수를 호출하는 로직에 대해 중첩된 if 문이 발생 될 수 있으므로 보완 방안으로 Reflection 을 이용한 동적 method 호출에 대해 기록한다.
/**
* 설명
*
* Retention RunTime : compile time 과 binary 에도 포함되고, reflection 을 통해 접근 가능합니다.
*
* Target FUNCTION : 생성자를 제외한 함수들에 사용 가능하도록 제한
*
* soIds : so 별 특정 함수 실행 시 so 입력을 위해 사용
* actionItem : action item 이 다를 수 있으므로 item 입력을 위해 사용
*
* */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION) // method 에 선언 될 경우 FUNCTION 으로 지정
annotation class SpecificActionItem(
val soIds: Array<String> = [],
val actionItem: Array<String> = []
)
|
interface TestSpecificFunService {
fun insertChargeByCarplat(param: Map<String, Any>)
fun insertChargeByPeopleCar(param: Map<String, Any>)
fun insertChargeByWay(param: Map<String, Any>)
}
@SpecificActionItem(soIds = ["carplat"], actionItem = ["insert_charge"])
override fun insertChargeByCarplat(param: Map<String, Any>) {
logger.info("insertCharge -> { so : carplat, item : insert_charge, param : $param }")
}
@SpecificActionItem(soIds = ["peopleCar"], actionItem = ["insert_charge"])
override fun insertChargeByPeopleCar(param: Map<String, Any>) {
logger.info("insertCharge -> { so : peopleCar, item : insert_charge, param : $param }")
}
@SpecificActionItem(soIds = ["way"], actionItem = ["insert_charge"])
override fun insertChargeByWay(param: Map<String, Any>) {
logger.info("insertCharge -> { so : way, item : insert_charge, param : $param }")
}
|
override fun invokeServiceFun(so: String, actionItem: String, param: Map<*, *>): Any? {
if (so.isNullOrEmpty() || actionItem.isNullOrEmpty()) {
logger.info("[DI LOG] invoke func so or actionItem is Empty -> { so : $so, actionItem : $actionItem }")
return null
}
var invokeResult: Any? = null
applicationContext.getBeanProvider(TestSpecificFunService::class.java).stream()
.forEach extraClass@{ clazz ->
clazz.javaClass.declaredMethods.forEach extraMethod@{ method ->
val extraMethod = method.getAnnotation(SpecificActionItem::class.java) // Annotation method 조회
if (!ObjectUtils.isEmpty(extraMethod)) {
if (extraMethod.soIds.contains(so) && extraMethod.actionItem.contains(actionItem)) { // soid, actionItem
invokeResult = clazz.javaClass.getMethod(method.name, Map::class.java).invoke(clazz, param)
return@extraClass // 일치하는 값이 있을 경우 조회 중인 interface loop 를 종료
}
} else {
return@extraClass
}
}
}
return invokeResult
}
|
@Test
fun invokeServiceFun() {
var so = "carplat"
val actionItem = "insert_charge"
val param = mapOf("param1" to 1, "param2" to 2)
testDiService.invokeServiceFun(so, actionItem, param) // insertCharge -> { so : carplat, item : insert_charge, param : {param1=1, param2=2} }
so = "peopleCar"
testDiService.invokeServiceFun(so, actionItem, param) // insertCharge -> { so : peopleCar, item : insert_charge, param : {param1=1, param2=2} }
so = "way"
testDiService.invokeServiceFun(so, actionItem, param) // insertCharge -> { so : way, item : insert_charge, param : {param1=1, param2=2} }
}
|
초기 호출을 제외하고는 Reflection API를 사용하는 것이 성능에 대한 별 차이가 없는 것에 대한 테스트 블로그 내용 (https://lob-dev.tistory.com/entry/Java%EC%9D%98-Reflection-API)
Kotlin 고차함수에 대한 Callback param 전달 (0) | 2022.05.03 |
---|