한참 헤매다.. 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
@Test
    fun callbackTest(){
        listLoop(callBack = {
            logger.info("callBack index -> ${it["index"]}")
            Thread.sleep(500)
        })
    }

    fun listLoop(callBack:(Map<String, Any>) -> Unit){
        var index = 0
        while (index < 10) {
            logger.info("loop -> $index")
            callBack.invoke(mapOf("index" to index))
            index++
        }
    }

 

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

Kotlin(java) Reflect 를 활용한 중첩 if 문 제거  (0) 2022.06.13
gradle build 에러 "CreateProcess error=206, 파일 이름이나 확장명이 너무 깁니다"

1. 에러 내용

bootRun 을 통해 서비스 실행 후 gradle build 중 아래와 같은 에러가 발생함

 

2. 해결

검색 시 경로가 길어서 문제가 될수 있다고 하여 해당 서비스 경로도 변경 해봤으나 증상이 동일

다른 케이스를 확인 중 build version 에 대한 문제가 발생 할 수 있다 하여 gradle version 을 update

  • 현재 버전 : gradle 6.7.1
  • 업데이트 버전 : gradle 6.8
  • 업데이트 방법 : ./gradlew wrapper --gradle-version 6.8
  • 참고 : 빌드는 bootRun service 디렉토리에서 실행해야합니다.(당연..)
  1. 사전 설치
  2. zulu jdk down
  3. arm 64 다운로드 및 설치
    • Operating System: mac / Architecture: ARM 64-bit / Java Package: jdk / .dmg 버전으로 다운로드
  4. jenv 환경 추가
    • jenv add /Library/Java/JavaVirtualMachines/zulu-15.jdk/Contents/Home
    • jenv global zulu64-15.0.3
    • 선택 버전 확인
  5. intellij application service 실행 시 종류 확인
    • intel 이 아닌 apple 로 실행됨 확인
QueryDsl 사용 시 date 시작/종료 구간 검색

 

JPAQuery 부분 
query.where(qOrder.createdDate.between(dto.getSearchStartDate(), dto.getSearchEndDate()));


dto 부분
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime searchStartDate;

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime searchEndDate;

 

LocalDateTime 을 사용할때에는 DateTimeFormat 을 입력해줘야 param 에 2021-02-01 09:52:53 같은 date time을 변환하여 받을 수 있다

개발 중 insert, update 쿼리 중 Exception 이 발생 하였지만 쿼리 요청이 rollback 되지않는 현상 정리

 

1. 액션

 - insert 혹은 update 문이 false 로 떨어질 경우 throw new Exception 을 처리 함

if(!dao.updateBl("updateQuery", dto)){
	throw new BaseException(SystemException.DB_NOT_UPDATE_ACTION);
}

 

2. 기대동작

 - RuntimeException 을 상속 받은 Exception 처리를 했으니 @Transactional 에 의해 rollback 처리를 기대함

 

3. 증상

 - DB update 가 rollback 되지 않고 정상적으로 업데이트가 됨

 

4. 원인 정리

 - update 문을 감싸는 try-catch 문을 신경쓰지 않았으며, catch 문에 처리되는 Exception 이 throw new Exception(e.getMessage()) 만 타고 있었음

 - Exception() 의 경우 checked exception 이므로 rollback 처리가 안됨

 

5. 해결방안

- 첫번째 방법 : try-catch 문의 Exception을 다음과 같이 변경 (RuntimeException 상속받은 클래스)

} catch (Exception e) {
	throw new BaseException(e.getMessage());
}

 - 두번째 방법 : throw new Exception() 으로 처리하고 @Transactional 의 옵션값을 보완함

@Transactional(rollbackFor = {RuntimeException.class, Exception.class})

 

현재 첫번째 방법으로 처리 했습니다.

 

 

참고 : cheese10yun.github.io/checked-exception/

outlook 사용 중 비정상 종료 후 윈도우 우측하단에 'outlook 닫는중' 으로 회색만 표시되고 닫히지 않는 증상시 강제 종료 방법 

 

1. window + r 키로 윈도우 cmd 창을 실행

 

2. 윈도우 cmd 창에서 'tasklist' 를 입력하여 현재 실행중인 프로세스 호출

 

3. 프로세스 중 'OUTLOOK.EXE' 를 찾아서 PID 항목을 기록

 

4. 'OUTLOOK.EXE' 프로그램에 대해 강제로 종료

 

 

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

 

flutter에서 flutter build web 수행 시 Missing index.html. 에러가 발생하는 경우는 flutter create가 수행되지 않은 문제

 

아래 순서대로 수행시 문제없습니다.

 

  1. flutter channel beta
  2. flutter upgrade (필요시)
  3. flutter config --enable-web
  4. flutter create . (<- . 점 현재 디렉토리 까지 선택)
  5. flutter devices
  6. flutter run -d chrome

 

+ Recent posts