한참 헤매다.. 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

 

1. 에러 내용

Flutter apk 빌드 후 http 통신 불가 로그 확인

logcat 확인 시 아래와 같은 로그 확인 함

"D NetworkSecurityConfig: Using Network Security Config from resource network_security_config debugBuild: false"

 

2. 해결

검색 결과 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>

'개발 > Flutter' 카테고리의 다른 글

플러터에서 웹 빌드 (Missing index.html. 에러처리)  (0) 2020.10.14

Overview

so 별 별도 로직이 존재하므로 반복과 중복 if 문을 써서 코드가 복잡해 지는 구조가 있다.

java reflect 를 이용하여 동적으로 class 및 method 호출 과정에 대해 기록한다.

전체 코드는 다음 링크에서 확인 (https://github.com/pari0130/reflection)


Service 버전

service 별 버전이 상이하여 별도 클래스로 분리 할 경우 호출하는 로직에서 if 문 중첩이 발생 될 수 있으므로 동적 호출 방안에 대해 기록 한다.

  1. annotation 구성
    1. class 상위에 선언 될 annotation 을 구성 한다
      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> = []
      )
  2. interface 및 class 구성
    1. getBeanProvider 를 이용해 하위 class 를 호출 하려면 interface 를 동일 지정, Annotation value 로 버전을 기록하여 구분
      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 { }
  3. interface 동적 호출을 위한 DI class 구성 
    1. 해당 class 의 목적은 전달받은 version 정보를 이용하여 interface 검색 후 override method 를 return
      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))
          }
  4. Test
    1. DI Service 에 version 정보를 전달하여 하위 interface 의 결과값을 조회 함
      @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") }
              )
          }

Service 에 대한 특정 Function 실행

so 별 특정 함수를 호출하는 로직에 대해 중첩된 if 문이 발생 될 수 있으므로 보완 방안으로 Reflection 을 이용한 동적 method 호출에 대해 기록한다.

  1. annotation 구성
    1. method 상위에 선언 될 annotation 을 구성 한다
      /**
       * 설명
       *
       * 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> = []
      )
  2. interface 및 class 구성
    1. 동일 interface 및 param type 을 지정하여 호출에 대한 공통화, Annotation 에 so 및 action item 으로 구분 분리
      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 }")
      }
  3. interface 동적 호출을 위한 DI class 구성
    1. TestSpecificFunService interfaces 및 하위 method 를 조회하여 soid, actionItem 에 해당하는 함수를 실행
      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
          }
  4. Test
    1. 함수 실행 결과 로그 확인
      @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' 카테고리의 다른 글

Kotlin 고차함수에 대한 Callback param 전달  (0) 2022.05.03

+ Recent posts