2019년 10월 30일 수요일

시퀀스맵으로 유저스토리를 만들어 TDD 까지 연계해보자


문서와 설명으로만 프로젝트를 진행하는 경우가 있다. 다들 머리속에는 같은 것을 그리고 있는데 아무도 그걸 구체화 시키지 않는다. 특히나 프로그래머는 뭐든지 로직으로만 생각하는 경우가 있는데, 그렇게 하다보면 로직->테스트코드작성 순으로 생각하게되는경향이 있다.
따라서 로직은 개발자한테 맡기되 고객/기획/디자이너/개발자가 공통된 하나의 스토리를 기반으로 같은 상상을 하게 해주는 좀더 구체화된것이 필요하다.
DDD 의 유비쿼터스언어 또한 언어라는 한계에서오는 제한으로 쉽게 진행되지 못하는경우가 있다.
이것을 애초부터 순서도로 만드는 거다. 인간의 언어에 제한되지 않는 순서도로 만들어서 상상했던 결과물이 최종결과물과 같은 순서도를 가진다면 누구나 수긍할수있는 프로젝트가 될것이다.

순서도를 그리는데는 시간도 걸리고 솔직히 귀찮다.
정말 좋은 PlantUml 을 사용해서 조금이라도 덜 귀찮게 만들수 있다.
단, 쓸데없는 중복코드를 피하기 위해서 별도의 함수파일을 만들자.

장점으로는 빨간색글씨로 TestCaseStory 를 미리 지정하여 , 테스터와 개발자가 동시에 테스트케이스를 작성할수 있다는거다.
10 단위로 되어있는 항목 중간에 1단위의 세부 항목을 넣을수 있다.



methods.txt
@startuml

title "Sequence UML"

!global $current_action = ""
!global $auto_num_step = 10
!global $current_auto_num = 0



!function a($to, $action)
     a($current_action, $to, $action)
!endfunction

!function a($from, $to, $action)
     !if ($action == 0 || $action == 1)
         setautonum($action)
         a($current_action, $from, $to)
     !else
          $from -> $to : $printautonum() $action
          !$current_action = $to
     !endif
   
!endfunction


!function alt_start($condition)
     alt $condition
!endfunction

!function alt_else($condition)
     else $condition
!endfunction

!function alt_end()
    end
!endfunction


!function s($from)
     activate $from
!endfunction

!function e($from)
     deactivate $from
!endfunction

!function setautonum($step)
     !$auto_num_step = $step
!endfunction


!function resume()
     !$auto_num_step = 10
     !$current_auto_num = ( $current_auto_num / 10 ) * 10
!endfunction


!function resetautonum($num)
     !$auto_num_step = 10
     !$current_auto_num = $num - $auto_num_step
!endfunction


!function $printautonum()
     !$outprint = $current_auto_num
     !$current_auto_num = $current_auto_num + $auto_num_step
     !if ($auto_num_step == 0)
          !$outprint = ""
          !$auto_num_step = 10
     !else
          !$outprint =   "<font color=red><b>[TCS1_" + $test1($current_auto_num)    +"]</b></font>"
     !endif
     !return $outprint
!endfunction

!function $test1($step)
     !if (%strlen($step) == 2)
          !return  "0" + $step
     !endif
     !return  $step
!endfunction

!function n($from,$message)
     note right of $from
            $message
     end note
!endfunction



@enduml


Source.txt

@startuml
!include methods.txt


'--- 사용자/앱구분 ---
!$유저 = "유저"
!$유저 = "유저"

'--- 참가화면 ---
!$메인화면 = "메인화면"
!$탭메뉴 = "탭메뉴"

!$쿠폰메인화면 = "쿠폰메인화면"
!$카테고리목록 = "카테고리목록"
!$상점목록 = "상점목록"
!$더보기단순링크 = "더보기_단순링크"
!$쿠폰목록 = "쿠폰목록"

!$앱 = "앱"


box "DPoint앱" #LightBlue
actor $유저
end box

box "메인화면" #LightBlue
     participant $메인화면
     participant $탭메뉴
end box

box "쿠폰화면" #LightBlue
     participant $쿠폰메인화면
     participant $카테고리목록
     participant $상점목록
     participant $더보기단순링크
     participant $쿠폰목록
end box


== TCS1 : 앱기동후 쿠폰탭을 클릭하여 쿠폰메인화면으로 이동 하는 유저스토리 ==
a($유저, $메인화면, "앱기동")
a($탭메뉴, "쿠폰버튼클릭")
     a($쿠폰메인화면, "화면오픈")
     s($쿠폰메인화면)
          a($카테고리목록, "카테고리목록표시",0)
          n($카테고리목록,"기본1개목록표시")
                 
          s($카테고리목록)
               a($카테고리목록,"기본1개목록표시",1)
               resume()
               a($카테고리목록,"기본2개목록표시")
               a($카테고리목록,"기본2개목록표시")
               resetautonum(100)
               box "test1" #LightBlue
                    a($카테고리목록,"기본2개목록표시")
                    a($카테고리목록,"기본3개목록표시")
               end box
               resume()
               alt_start("해당카테고리의 쿠폰이 1개이상있을시")
                    a($카테고리목록 , "일반카테고리목록표시")
               alt_end()
               a($카테고리목록 , "카테고리\n자동선택")
                    activate "카테고리목록"

               a($메인화면, $탭메뉴, "메뉴변경")

               alt_start("해당카테고리가없거나\n처음 시동시")
                         a($카테고리목록, $카테고리목록, "전체항목선택")
               alt_else("이전에 선택한 탭이 있었고\n 해당항목이 존재할경우 ")
                         a($카테고리목록, "지정된항목선택")
               alt_end()
               deactivate "카테고리목록"
          e($카테고리목록)
     e($쿠폰메인화면)

@enduml



2019년 10월 21일 월요일

IOS 의 GCD ( Grand Central Dispatch) 로 쓰레드처리를 편하고 안전하게.

쓰레드의 락, 동기화 이런거 신경쓰지 않고 GCD를 사용하여 메인 쓰레드에 단순히 dispatch 하면 걍 실행된다능.

헤더파일에서

@interface SingleToneSample : NSObject
+ (instancetype) sharedInstance;
@end


처럼 간단하게 공개용 함수만들고 구현파일에서는

+(instancetype)sharedInstance {
    static SingleToneSample *shared = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [[SingleToneSample alloc] init];
        
    });
                  
    return shared;
}

처럼 실체를 만들면된다.


참고

https://padgom.tistory.com/entry/iOS-%EA%B8%B0%EB%B3%B8-GCDGrand-Central-Dispatch-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

Cocoa Touch Framework 을 이용해서 공통모듈 만들기

안드로이드에서 aar 화일을 만들어 쓰듯이, Xcode 6 부터는 개발자가 Framework 프로젝트를  만들어서 여러프로젝트에 공통으로 사용할수 있도록 되었다.

1.일단 워크스페이스부터 따로 만든다.
2.새 프로젝트를 만들어야 하는데 [cocoa touch framework] 를 선택해서 새로 만든다.
 맨 마지막 프로젝트 폴더 선택하는 부분에서  add to 와 group 선택박스를 아까 생성한 워크스페이스로 선택한다.
3.프로젝트 구성을 보면 프레임헤더화일, 테스트 , 프로덕트가 있고 안에 .framework 파일이 있는데 아직 빌드를 안해서 빨강색이다.
4.프레임헤더를 열어보면 맨마지막줄에 public , 공개로 사용할 헤더를 적어주면 외부에서 해당 기능을 사용할수 있게된다라고 되어있다.
#import <myframework/Car.h> 라고 하자
5.이제 새화일->코코아터치 클래스 를 선택해서 Car.h , Car.m 을 만들자.
6.car 클래스는 평소대로 하고싶은 코딩을 한다.
7.이제 중요.
8. Car.h 를 선택하면 오른쪽 속성 판넬에 [Target MemberShip] 항목에 프레임이름과 private|public|project 가 있다.  외부에서  car 클래스에 접근하려면  하려면 public 으로 해야 된다.
9.그위의 Location 도 Relative to Group 으로 지정한다.
10. 왼쪽의 프로젝트 파란 놈을 클릭하고 항목에서 [Build Active Architecture Only] 를 No 로 해주어야 다른 플랫폼에서도 작동할수 있게된다.
11.프레임워크 프로젝트에서 빌드를 한다. 이떄 호환성을 위해서 에디터 상단의 빌드 디바이스를 [제너릭 디바이스] 로 지정하고 빌드한다.
12.왼쪽의 products/myframework.framework 파일이 생성되면 왼쪽으로 가는 화살표를 클릭해서 해당 폴더에서 파일을 복사해논다.
13.이제 아까만든 프레임워크를 사용할 프로젝트를 만든다. 워크스페이스, 프로젝트 폴더는 따로 만들어서 완전히 새로운 프로젝트를 구성한다.
14.새로운 프로젝트를 만들었으면 프레임워크를 보관할 임의의 폴더(그룹)를 만든다(임시로 framework 라고 만들었다. )
15. 만든 폴더에 아까의 프레임워크를 붙여넣는다.
16. 프레임워크를 사용하려면 m 파일에서 프레임워크에서 공개(public)으로 지정한 헤더를 임포트해서 해당 클래스를 사용하면된다.

#import <myframework/Car.h>
...
    Car *car = [[Car alloc] initWithName:@"audi"  year:1990];
    [car info];
17.이상태에서 빌드시에 에러가 나오기도 하는데  x86_64 관련에러는 프레임워크로 돌아가서 프로젝트 설정에 [Build Archtecture Active Only] 를 No 로 설정하고 다시 빌드후에 라이브러리를 다시 현재 작업 프로젝트에 던져 놓으면 된다.
18.이제 실행을 시켜보면  image not found 이 나온다.
18. 속성의 general 항목의ㄹ embedded binaries 항목에 myframework.framework을 추가하면 된다.

 x86_64  관련 에러가 계속 뜨고, 삽질을 계속함..

참고사이트

http://theeye.pe.kr/archives/2254
https://qiita.com/kakipo/items/3cd87e00bc96bc3888b2
https://qiita.com/sachiko-kame/items/be548a7942e6c22a1828

정적라이브러리형태로 배포하기
http://theeye.pe.kr/archives/2750


-추가로 framework 파일에 이미지등을 넣고 싶을때는 이렇게 해본다
1.프로젝트폴더밑에 jpg/testimg.png 를 만들고 넣는다.
2.프로젝트속성 Build Phases에 Copy Files 속성을 추가하고 jpg 폴더를 던져넣는다. 그리고 Destination 은 Frameworks 로 지정한다.
3.프레임워크의 파일내에서 해당 이미지를 사용해본다.

-(UIImage*)image2
{
    NSBundle *b = [NSBundle bundleForClass:[self class]];
    return [UIImage imageNamed:@"Frameworks/jpg/img.png" inBundle:b compatibleWithTraitCollection:nil];

}
4. 빌드한다.
5.사용하고자 하는 프로젝트에서 프레임워크내의 메소드를 호줄해서 UIImageView에 적용해본다.


    [_img setImage:[car image2]];



2019년 10월 12일 토요일

Android NDK 작성법 CMakeLists.txt 사용 -2

1 . Native c++ 프로젝트로 시작하면 CMakeLists, cpp 폴더 등 자동 설정된 채로 프로젝트를 시작할수있다.
2 . CMakelist.txt 의 설명은 여기를 참고 *https://crmn.tistory.com/80
    굳이 CMake 를 배우려면 https://www.tuwlab.com/ece/27234
3 . CMakelists 에 사용할 라이브러리 추가

add_library(
        hello-libs 
        SHARED
        hello-libs.cpp)
4 . Activity 에서는

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
        System.loadLibrary("hello-libs");
    }

    public native String getHello();
5.끝.


Android NDK 작성법 -1

C 소스와 연동하기 위해서는 NDK 연동해야 되는데 걍 귀찮네..

일단
고전적인 방법 

1. 액티비티 클래스에서 먼저 static 으로 만들고자 하는 ndk 모듈을 로딩한다.
JniTwo 라는 이름은 C++ 모듈이름으로 Android.mk 에 [ LOCAL_MODULE := jniTwo ] 라고 나중에 똑같이 적으면 된다.

    static {
        System.loadLibrary("jniTwo");
    }

2. 모듈을 로드했으면 액티비티 클래스에서 쓸 함수를 이름만 정의한다.

 private native String getStr(String str);

3. src->main->jni 폴더를 만든다.
4. 그 안에 cpp 파일을 만든다. * 헤더는 나중에 수동을 만들거다.

//
// Created by kimtaeho on 2019-10-12.
//

#include <string.h>

JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_getStr(JNIEnv *env, jobject thiz, jstring str) {
    char buf[255];
    char appendMsg[] = " JNI Hello";

    const char *ch1 = env->GetStringUTFChars(str, 0);
    strcpy(buf, ch1);
    strcat(buf, appendMsg);

    return env->NewStringUTF(buf);

 }

5. 액티비티 클래스에서 함수를 사용해본다.

        TextView ttt = (TextView)findViewById(R.id.ttt);
        ttt.setText(getStr("Kim"));

6. Android Studio Setting 에서 Tools->External Tools 에 새로 생성해서 다음과 같이 적는다.

Program : C:\Program Files\Java\jdk1.8.0_191\bin\javah.exe

Arguments : -classpath "$Classpath$" -v -jni $FileClass$

Working Directory : C:\workspace\MyApplication2\app\src\main\jni
7. 이 상태에서 Build->make Project 를 해본다.
8. 모듈을 로드하는 Activity Class 에서 마우스오른쪽 [External Tools] 에서 6에서 생성한 것을 선택하면 복잡한 h 파일이 생성된다. 해당 파일의 맨 밑에 cpp 에서 만든 함수가 정의되어 있는것을 확인하고 파일이름을 적당히 바꾸고 cpp 에서 include 한다.
9. jni 폴더에 Android.mk 를 만들어 모듈이름고 소스 파일이름을 지정한다.


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := jniTwo
LOCAL_SRC_FILES := Two.cpp
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)

10. build.gradle(app) 에 다음과 같이 추가.

import org.apache.tools.ant.taskdefs.condition.Os



// Project Structure에서 설정한 NDK 경로를 읽어 들여 Return합니다.
def getNdkBuildPath() {
    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())

    def command = properties.getProperty('ndk.dir')
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
        command += "\\ndk-build.cmd"
    } else {
        command += "/ndk-build"
    }
    return command
}


Android {

 ...



    sourceSets.main {
        // Compile된 Native Library가 위치하는 경로를 설정합니다.
        jniLibs.srcDir 'src/main/libs'
        // 여기에 JNI Source 경로를 설정하면 Android Studio에서 기본적으로 지원하는 Native
        // Library Build가 이루어집니다. 이 경우에 Android.mk와 Application.mk를
        // 자동으로 생성하기 때문에 편리하지만, 세부 설정이 어렵기 때문에 JNI Source의
        // 경로를 지정하지 않습니다.
        jni.srcDirs = []
    }

    ext {
        // 아직은 Task 내에서 Build Type을 구분할 방법이 없기 때문에 이 Property를
        // 이용해 Native Library를 Debugging 가능하도록 Build할 지 결정합니다.
        nativeDebuggable = true
    }

    // NDK의 ndk-build 명령을 이용하여 Native Library를 Build하기 위한 Task를 정의합니다.
    //noinspection GroovyAssignabilityCheck
    task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
        if (nativeDebuggable) {
            commandLine getNdkBuildPath(), 'NDK_DEBUG=1', '-C', file('src/main').absolutePath
        } else {
            commandLine getNdkBuildPath(), '-C', file('src/main').absolutePath
        }
    }



    // App의 Java Code를 Compile할 때 buildNative Task를 실행하여 Native Library도 같이
    // Build되도록 설정합니다.
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn buildNative
    }

    // NDK로 생성된 Native Library와 Object를 삭제하기 위한 Task를 정의합니다.
    //noinspection GroovyAssignabilityCheck
    task cleanNative(type: Exec, description: 'Clean native objs and lib') {
        commandLine getNdkBuildPath(), '-C', file('src/main').absolutePath, 'clean'
    }

    // Gradle의 clean Task를 실행할 떄, cleanNative Task를 실행하도록 설정합니다.
    clean.dependsOn 'cleanNative'



}

11. gradle.properties 에 추가


android.useDeprecatedNdk=true


2019년 10월 11일 금요일

Android Decompile 해서 다시 apk 로 만들어보기

-1 apktool 명령어로 해보기

1.java -jar apktool_2.4.0.jar d 앱이름.apk
2.압출풀린 폴더가 생긴다.
3.Manifest.xml 에 application 태그에  android:debuggable="true"  을 추가함.
4.java -jar apktool_2.4.0.jar b 앱이름폴더
5.임시키를 만듬
"C:\Program Files\Java\jre1.8.0_191\bin\keytool.exe" -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
6.그 키로 jar 싸인
"C:\Program Files\Java\jdk1.8.0_191\bin\jarsigner.exe" -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore com.nttdocomo.android.dpoint_2019-09-24.apk alias_name
7.에뮬레이터에 던져넣기.


-2 java 원본보기
dex2jar
- apk파일이나 apk파일에 포함된 classes.dex파일을 .jar파일로 변환



사용법

1.dex2jar 폴더 안에 apk 파일 넣기
2.해당 폴더에서 명령창 띄어 jar 파일 생성
dex2jar.bat [디컴파일할 apk]:bat 파일에 던져넣어도됨.
3.JD-GUI 에서 보기

https://programist.tistory.com/entry/Apktool%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-apk-%ED%8C%8C%EC%9D%BC-%EB%94%94%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EB%B0%8F-%EB%A6%AC%ED%8C%A8%ED%82%A4%EC%A7%95


-3 bytecode-viewer
https://brunch.co.kr/@c4u/23