* '##' macro operator (improved)


<token> ## <token>

: The ## operator takes two separate tokens and paste them together to form a single token.


즉:


#define test(x) aa ## _x

#define test(x) aa##_x


위의 두가지 모두 결과는 'aa_x' 로 같다.

('##' 사용시 중간에 공백은 token delimiter 의 역할을 하게 된다.)

'Language > C&C++' 카테고리의 다른 글

[C/C++] Template with value  (0) 2015.04.28
[C/C++] sizeof array  (0) 2015.03.19
'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25
[Linux] Using named pipe in linux (주의할 점)  (0) 2014.01.09

이미 많이 알려진 topic이지만.. 정리차원에서...


* 표준적인 방법은 'startForeground()' API를 통하는 방법이다.

그렇지만, 'startForeground()' 를 이용할 경우에는 반드시 'Notification()'을 띄워야 한다.


* 만약 Notification없이 죽지 않는 Service를 만들때, 그 service를 platform key로 signing할 수 있다면(즉 platform app인 경우)

<android:persistent='true' />를 이용할 수 있다.

이는 '<application', '<service', '<activity' 에 각각 적용 가능하다.

다만, Persistent App의 경우 항상 memory에 상주한다. (Kill 로 죽이더라도 곧바로 재시작한다.)


/libcore/luni/src/main/java/java/util/jar/JarVerifier.java


Here is steps for verifying package with it's signature at META-INF in Android system.

- Find files ends with '.RSA', '.DSA' or '.EC'(Certification file) and then find '.SF' file(Signature File) that has same basename.

  (In case of Android, 'CERT.RSA' and 'CERT.SF' file)

- Verifying Signature File by using Certification file => Signature File is verified.

- Read '-Digest' values from CERT.SF file. => Valid hash value for files are read.

- Then, when parsing package(Apk), all file entries except for files in 'META-INF' directory, are scanned and compared with corresponding hash value in CERT.SF.


Interesting point is, in Android, verification is processed based on file entries in APK.

That is, removing some entries from APK doesn't make any problem in terms of signature verification. :)

* When 'variable expansion' at Command Section, is processed at GNUMake --------------------------------------------------------------------- In GNUMake source code, command line section of each Target seems to be expanded at 'new_job()' function. In detail, 'for-loop part' under '/* Expand the command lines and store the results in LINES. */' comment. And then, first command line is fetched at 'job_next_command()' function. Then, when 'job slot(in case of using mutlple job)' is available, this command line that expansion is done, is executed on newly created child process. new_job - expand command line variables (line-by-line) - waiting until job slot is available. - job is executed.. Note that all variables in command section, are expanded before processing first command line. Note that, variables in pre-requisites and dependencies are expanded before expanding command line section. So, following gnumake code is dangerous. out/target-file: <...> ./generate-target-file.sh $@ ./postprocess.sh $(shell readlink -e $@) [ Issue ] "$(shell ...) function" part is expanded before "./generate-target-file.sh" is executed. So, af the first build, 'out/target-file' doesn't exist, "readlink -e $@" return empty string. And this may lead to unexpected result.

--- should be modified like below out/target-file: <...> ./generate-target-file.sh $@ ./postprocess.sh $$(readlink -e $@)


익히 알려진 바와 같이, 인사평가가 100% 공정 - 공정이라는 단어의 정의를 어떻게 하느냐에 따라 달라질 수도 있겠지만 - 할 수는 없다.

일례로, 기왕이면 진급 대상자에게 상위고과를 주는 것이 어찌보면 '인지상정'이라고 할 수도 있을 것이다.

이런 것이 '좋다' 혹은 '바르다'라고 이야기 하는 것이 아니다. 평가는 100% 공정한 것이 좋다. 그렇지만, '공정'이라는 것이 그 속성상 100% 객과적일 수가 없기 때문에, 주관적인 판단이 반드시 개입된다는 것을 인정해야 한다.

그리고, 이런 '주관적인 판단'에는 주변의 환경적인 요소가 포함되기 마련이다.

예를 들면, "같은 성과라면, 기왕이면 진급이 절실한 사람에게 손을 들어 준다던가."라는 것들 말이다.

비슷한 이유로, 상위고과가 다른 이들보다 절실한 사람이 있기 마련이다.

그렇다고 해서, 인사평가 시즌에 무턱대로, 이런 사람에게 아무런 이유없이 상위고과를 줄 수는 없는 일이다.

이런 경우, 어떻게 해야 합리적인 방법으로 무리없이 이런 문제를 처리할 수 있을까?


내가 보기에, 인사평가는 "일을 얼마나 잘 하였느냐?"보다는, "어떤 일을 하였느냐?"가 더 많은 부분을 차지하는것 같다.

SW의 분야의 경우 예를 들어보면, 기존일을 '유지/보수'하는 일은 정말 압도적인 '실력/성과'를 보이지 않고서는 최상위 평가를 받기는 거의 볼가능하다.

그렇지만, 회사가 주요하게 추진하는 새로운 제품을 개발한다던가, 새로운 기능을 추가 구현하는 일은 일단 무사히 업무를 마치기만 해도 상위 평가를 받을 가능성이 높다.

이 두가지 업무 중 어느 쪽이 더 힘들고, 덜 힘든지는 경우에 따라 다를 것이지만, 일을 마쳤을 때 받을 수 있는 평가의 차이는 상당하다.


다시, 앞의 이야기로 돌아가 보자.

어떤 사람에게 '기왕이면' 상위고과를 주고 싶은 경우는 위에서 언급한 내용을 응용하는 것이 좋다.

그 사람에게, '상위고과를 받을 수 있는 일'을 할 수 있는 기회를 주는 것이다.

'좋은'일이 생겼을 때, 소위 '챙겨줘야'할 사람에게, 먼저 "이 일을 할 것인지?"에 대한 의사를 물어보는 것이다. 그리고, 이 일을 잘 해내었을때 좋은 평가를 받을 수 있다는 것도 같이 언급하는 것이 좋다.

이렇게 먼저 기회를 주고, 무사히 일을 잘 해내었을 경우 큰 물의를 일으키지 않고 필요한 사람에게 '상위고과'를 줄 수 있게 된다.

한편, 이런 기회에도 불구하고, 일처리가 미숙했다면, 그 사람은 거기까지가 한계인 것으로 볼 수 있다.


지금까지 어찌보면 굉장히 '부당'하다고 생각할 수도 있는 이야기를 했다.

읽는 사람에 따라서 상당히 불편하게 받아들일 수도 있을 것이다.

그렇지만, 사람이 사는 곳이라면 이런 문제가 반드시 발생하고, 실제 많은 조직에서 이런 문제가 심심치 않게 보인다는 것에 이견을 가진 사람은 없으리라 생각한다.

따라서, 이런 일을 피할 수 없다면, 최대한 합리적인 방법으로 처리할 필요가 있고, 내 개인적인 생각으로는 위과 같은 방법이 무난하다고 여겨진다.


여기서 다시 한번 강조하고 싶은 내용은, 상당히 많은 경우 "그 사람이 얼마나 일을 잘 했느냐?"가 아니라, "그 사람이 어떤 일을 했느냐?"가 평가에 더 많은 비중을 차지한다는 것이다.


Software분야에서 소위 '성과 평가'에 대한 문제는 아주 오래되고, 또 진부한 문제다.

여러가지 한계를 이야기 하고 있고, 그럼에도 불구하고, "평가를 하지 않는 것 보다는 하는게 낫다."라는 이론(?)을 근거로, 여전히 다양한 형태의 '평가'가 행하여 진다.

이 글은 '대안'을 제시 - 가장 어려운 부분 - 하는 것이 아니고, 기존 '평가' 방식이 가지는 여러가지 문제 중 하나를 언급하기 위해 작성된 것이다.


Software분야의 여러가지 연구 중 아래와 같은 것이 있었던 것으로 기억한다. (출처는 기억이 잘...)

"Software engineer에 개인의 기술적인 역량은, '어떤 group에 속해 있느냐'에 따라 상당히 많이 좌우된다."

즉, 좋은 group에서 좋은 경험을 쌓은 engineer가 기술적으로 뛰어난 engineer가 될 확률이 높다는 뜻이다.

그리고 또 재미있는 연구 중 하나는 (이것도 출처가 잘...)

"Software engineer 개인의 성과 차이는 100배까지도 발생하지만, 조직의 성과 차이는 10배 정도까지 밖에 차이가 나지 않는다."

관점에 따라서, 80:20 법칙을 생각나게 하기도 한다.

개인의 역량차는 심하게 나지만, 조직의 역량차는 그렇게까지 크지 않다는 뜻이다.

아마, [역량이 '1'인 엔지니어들의 group] vs. [역량이 '100'인 엔지니어들의 group] 과 같은 극단적인 조건에서가 아니라, 현실세계에 존재하는 일반적인 group을 대상으로 한 조사였을 것이라 생각된다. 

그리고 또, 일반적인 '회사'에서 똑같은 일을 두 곳 이상에서 하는 경우는 거의 없다.


내가 하고 싶은 말은, 

"조직의 역량 차는 각 개인의 역량차에 비해 크지도 않고, 또 같은 일을 하는 조직이 없기 때문에 SW조직의 역량을 비교 평가하는 것은 상당히 어려운 일이다."

라는 것이다.

특히, 같은 일을 하는 조직이 없다는 뜻은, 그 조직의 역량과 무관하게, 해당 분야의 history / 경험/ domain knowledge 등에 따라 진입장벽이 만들어 진다는 말이다. 그리고, 이 진입장벽으로 인해, 조직간 역량 평가는 결국 무의미해 진다."

즉, 개인의 성과 평가보다도 SW 조직(개발 조직)의 평가가 더 어렵다.


SW조직의 평가가 어렵다는 것을 받아 들인다면, 독립된 두 조직이 있을때 어느 조직이 더 나은 조직인지 알지 못한다는 뜻이다.

A, B 두 조직 중 실제 A 조직이 더 우수하지만, 조직 평가의 어려움 때문에 A, B 조직의 평가가 같게 나온 상황을 가정하자.

이 상황에서, 우수한 조직에서는 우수한 engineer가 배출될 확률이 높고(앞서 언급했다.) A 조직과 B조직의 역량차는 계속해서 커진다.

그렇지만, 여전히 조직간 평가의 문제점/어려움 때문에 이 부분이 보여질 가능성은 극히 낮다.


이런 상황에서, 일반적인 '회사'에서는 '상대평가'를 수행한다.

각 조직에서 상위 성과자와 하위 성과자를 구분하는 것이다.

개인의 역량평가를 정확히 실시하는 것도 사실 거의 불가능 하지만, 개인간 정확한 성과 평가가 가능하다는 것을 가정하더라도, 이미 조직의 평가에서 정확성을 잃어 버렸으므로, 개인간 평가의 정확성은 상당부분 희석될 수 밖에 없다.

뛰어난 조직의 하위 성과자가, 다른 조직의 상위 성과자 보다 더 나은 성과를 보였을 가능성도 무시하기 힘들다.


별다른 통일성이 없는 글이였는데... 정리하면...

SW분야에서 개인의 역량 평가에 대한 연구도 중요하지만, 조직의 역량 평가에 대한 연구도 동등 혹은 그 이상으로 중요하다.




 Abi관련 변수들


Build.CPU_ABI

Build.SUPPORTED_ABIS
ro.product.cpu.abilist64
ro.product.cpu.abilist32


=========================

64bit, 32bit library의 경우


BaseDexClassLoader 에서 참조하는 native library는 null인데, 이는 default library path를 사용하는 것이고...

코드를 쫓아가다보면...

'java.library.path"

에서 library directory를 가져오는데(/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java) 이 값은 default로 LD_LIBRARY_PATH 환경변수값을 사용한다.(java_lang_System.cpp)

android_get_LD_LIBRARY_PATH() 함수를 찾아서 가보면, kDefaultLdPaths 에서 값을 읽어오는데, 아래와 같이 정의되어 있다.


91static const char* const kDefaultLdPaths[] = {
92#if defined(__LP64__)
93  "/vendor/lib64",
94  "/system/lib64",
95#else
96  "/vendor/lib",
97  "/system/lib",
98#endif
99  NULL
100};
101

그리고


system library의 경우

system/lib/

system/lib64/

vendor/lib/

vendor/lib64


========================

system library의 경우는 위와 같고... APK에 포함된 library의 경우는... aapt를 확인해야 하는데, 

/frameworks/base/tools/aapt/Command.cpp

를 보면, 

1981            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1982            if (dir != NULL) {
1983                if (dir->getFileCount() > 0) {
1984                    SortedVector<String8> architectures;
1985                    for (size_t i=0; i<dir->getFileCount(); i++) {
1986                        architectures.add(ResTable::normalizeForOutput(
1987                                dir->getFileName(i).string()));
1988                    }


코드가 보이는데... 즉 "lib/<archtecture-name>"의 형태로 directory가 구성되었다는 것을 알 수 있다.


====================================

APP이 뜰때, 64bit인지 32bit인지 확인해서 zygote64를 base로 뜰건지 zygote32를 base로 뜰건지 알아야 하는데,

Process.java

에서

openZygoteSocketIfNeeded(String abi)

처럼 abi가 넘어가게 된다.

이 abi값은 proces start에 argument로 넘어오는데,

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

3192            Process.ProcessStartResult startResult = Process.start(entryPoint,
3193                    app.processName, uid, uid, gids, debugFlags, mountExternal,
3194                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
3195                    app.info.dataDir, entryPointArgs);

3177            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
3178            if (requiredAbi == null) {
3179                requiredAbi = Build.SUPPORTED_ABIS[0];
3180            }
3181
3182            String instructionSet = null;
3183            if (app.info.primaryCpuAbi != null) {
3184                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
3185            }


에서 requiredAbi argument가 있고... default abi를 사용하긴 하나, 결국 package가 지원하는 abi를 알아야 한다.



/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

5726                    final int copyRet;
5727                    if (isAsec) {
5728                        copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
5729                    } else {
5730                        copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5731                                nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
5732                    }
5733
5734                    if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
5735                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
5736                                "Error unpackaging native libs for app, errorCode=" + copyRet);
5737                    }
5738
5739                    if (copyRet >= 0) {
5740                        pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
5741                    } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
5742                        pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
5743                    } else if (needsRenderScriptOverride) {
5744                        pkg.applicationInfo.primaryCpuAbi = abiList[0];
5745                    }


findSupportedAbi() 

com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,

=> 413static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {


결국 "/lib/<abi-name>"이란 규칙을 이용해서 apk가 지원하는 ABI를 찾는다.

그런데, PackageManagerService를 보면, package가 '.apk' file인 경우 - Monolithic apk - 와 directory이 경우 - Cluster apk - 두 가지로 나뉘어 지는 것을 알 수 있다.

두 경우 모두 cpu Abi를 결정하기 위해서 위의 방법을 사용하는데, 두 경우 모두


<package file/dir>/lib/<cpu abi>


형태의 구성을 가정하고 있다.

따라서, apk build 자체가 위의 구성으로 나오는 package build와 실제 so는 system에 두고 link만들 거는 system apk 모두를 cover하고 있다.


ex.

Monolithic


Gallery.apk

|-AndroidManifest.xml

|-...

|-lib/armeabi-v7a/xxx.so

|...




Cluster (PDK build시 "/system/app/<package name>" directory)


Gallery/ ('/system/app/Gallery')

|-Gallery.apk

|-x86_64/Gallery.odex

|-lib/x86_64/xxx.so



두 경우 모두 "<Gallery apk/dir>/lib/<cpu abi>"형태로 구성되고 이를 보고 판단한다.


아래 코드는 추가적으로 참고하자..

5686                    if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
5687                        if (isAsec) {
5688                            abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
5689                        } else {
5690                            abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5691                                    nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
5692                                    useIsaSpecificSubdirs);
5693                        }
5694                    }
5695
5696                    maybeThrowExceptionForMultiArchCopy(
5697                            "Error unpackaging 64 bit native libs for multiarch app.", abi64);
5698
5699                    if (abi64 >= 0) {
5700                       


뭐 결국은, apk의 "/lib/<abi-name>"이 platform이 지원하는 64bit abi에 포함되면, 64bit apk로 판단하는 방식이다...

즉, APK가 어떤 abi를 지원하는지는 manifest file 등등 이런 곳에 따로 기록되어 있는것이 아니고, native library의 "/lib/<abi-name>"규칙에 의존해서 찾는다.


Platform build시 jni library들은 default로 모두 'system/lib'쪽에 들어가게 되어 있는데 - APK에 lib이 포함되지 않는다. - 그럼, 해당 APK의 ABI어떻게 알 수 있는가?

(최초 booting시 해당 APK를 AOT(Ahead Of Time) compile을 위해서는 어떤 target ABI로 compile할지가 정해져야 한다.)


일단 생각해보면, 어차피 VM(DEX)에만 의존하는 APK라면 특별히 ABI에 영향을 받지 않으므로 그냥 platform default ABI로 인식하고, AOT compile을 수행하면 된다. 그렇지만, 사용하는 native library들이 있고, 이 library를 load해야하는 APK라면, ABI에 따라서 어떤 zygote(32bit 혹은 64bit)에서 fork될 것인가를 결정해야 하므로, ABI를 system이 알아야할 필요가 있다.


이 경우는, 'LOCAL_MULTILIB := 32' 같은 방식으로 second abi를 사용하도록 정의하고 빌드하면, 'system/app/<package>'쪽에 보면


*** x86_64 용 TARGET ABI로 빌드하고, 'LOCAL_MULTILIB := 32'를 사용하는 Gallery2를 예로 들면...

Gallery2

- Gallery2.apk

- lib/x86/<xxx>.so -> /system/lib/<xxx>.so (symbolic link)

- x86/Gallery2.odex



즉, 제대로 32bit 용으로 빌드되어 있다. (그렇지만, Gallery2.apk내부에는 'lib/<abi>' 형태의 directory가 없다.

그렇지만, 만약, 32bit용으로 DEX가 pre-optimization되어 있지 않다면, 어떻게 Gallery2가 32bit용 package인지 system이 알고 32bit용으로 dex를 optimization - ART의 경우 AOT compile - 시킬수 있을까?



/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

6485        } else {
6486            // Cluster install
6487            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
6488            info.nativeLibraryRootRequiresIsa = true;
6489
6490            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
6491                    getPrimaryInstructionSet(info)).getAbsolutePath();
6492
6493            if (info.secondaryCpuAbi != null) {
6494                info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
6495                        VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
6496            }
6497        }
6498    }




scanPackageDirtyLI() -> 여기서 installer를 이용해서 app data directory / lib directory 등을 만든다. nativeFindSupportedAbi => copyNativeBinariesForSupportedAbi : 여기서 abi를 가지고 instrunction set directory로 copy한다. 이때 nativeLibraryRootDir 로 복사하는데, 이 nativeLibraryRootDir 로 될 수 있는게 <bundled app> 은 final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info); [[ Monolithic ]] Monolithic && bundled "/system/lib[64]/<apkname>" Monolithic 이고 Non-bundled "/data/app-lib/" 이다 [[ Cluster ]] info.nativeLibraryDir = new File(info.nativeLibraryRootDir, getPrimaryInstructionSet(info)).getAbsolutePath(); if (info.secondaryCpuAbi != null) { info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); } 즉 Cluster 의 경우 NativeLibraryDir은 abi가 아니라 instruction set 이름으로 찾아들어간다. VMRuntime.java 38 static { 39 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm"); 40 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm"); 41 ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips"); 42 ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64"); 43 ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86"); 44 ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64"); 45 ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64"); 46 }


VMRuntime.getInstructionSet(info.secondaryCpuAbi)) 에서 arch이름을 찾는 규칙이 위와 같다.

즉 cpuabi 는 instruction set 이름을 찾기 위해서 사용되지 그 자체는 package manager service에서 사용되지 않는다.

재미있는 것은 build system에서는

"cpu abi" -> "arch"

오 가타은 이름을 사용하고 있고, package manager service에서는

"cpu abi" -> "instruction set"

을 사용한다.

즉, 같은 이름을 build system과 package manager service에서, 'arch', 'instruction set'이라는 이름으로 서로 다르게 사용하고 있다.




실제 emulator를 실행시킨 후 위의 - 첫번째 booting에서 apk가 install된다. - package의 data directory를 가 보면.


$ ls -l /data/data/<package-name>

lib -> /data/app-lib/<package-name>


ex.

root@generic_x86_64:/data/data/com.android.gallery # ls -l lrwxrwxrwx install install 2014-11-10 14:13 lib -> /data/app-lib/com.android.gallery


그리고 아래의 code를 보자.


/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

6477            } else {
6478                final String apkName = deriveCodePathName(codePath);
6479                info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
6480                        .getAbsolutePath();
6481            }


위의 코드에서 'mAppLib32InstallDir'이 바로 '/data/app-lib' directory이다.

이렇게 자신의 library를 찾아가게 된다.







예를 들어, 'gitweb', 'bugzilla' 등을 service하는 server를 configuration한다고 가정하면,

apache에서, 이것들을 한 virtual host '80 port'에 설정하고, uri 로 구분하는 방법 - (*A) - 과, 아예 virtual host를 다르게 설정하는 방법 - (*B) - 두가지가 있을 것이다.

(*A)의 경우, "<domain name>/gitweb" "<domain name>/bugzilla" 이런 식으로 나누어서 한 virtual host에서 service하면 port의 낭비도 막고, 쓸데 없이 virtual host를 하나 더 사용하는 낭비도 줄일 수 있다.

그렇지만, 경우, client의 browser의 cache 정책이나 설정등에 따라, 의도치 않은 동작들이 발생하는 경우를 많이 겪었다.

따라서, 경험적으로, 이런 경우, 불편하고, 또 약간 낭비적인 요소가 있더라도 그냥, 여러개의 virtual host로 설정하는 편이 여러가지 지뢰(?)를 피할 수 있는 가장 편한 방법이다.


"다른 사람을 blame하는 말을 할때, 주어가 '나'가 되고, 나를 blame하는 문장으로 바꾸어 구성하자."

아래와 같은 예를 보면 명확하게 이해될 것 같다.


* "너는 왜 아빠 말은 안듣니?"

=> "OO야, 아빠가 OO가 이해하기 어렵게 말했니?"


* "OO부장님께 말씀드렸습니다만, 받아들여지지 않았습니다."

=> "제가, OO부장님께 드린 설명이 부족했나 봅니다."


MVC Pattern에 대한 다양한 종류의 해석/적용이 있긴 한데... 일단, 내가 생각하기에, 일반적인 SW에서 좋다고 생각하는 구조는 아래와 같다 (Web SW나 기타 여러 case가 존재하고, 각각에 맞는 다양한 해석이 존재할 수 있으니, 정답은 없다.) 물론, 아직 나 자신이 MVC Pattern에 대해 미숙하기 때문에 확신할 수는 없으나...


+-----------+ Query(write/read) +------------+

| | <-------------------------- | |

| Model | | Control |

| | ---------------------------> | |

+-----------+ Response/Data for query +------------+

| | ^

| | |

| Feedback to user(ex. select view) | | User interaction event

| v |

| +------------+

| | |

+--------------------------------------> | View |

Read data / (Observe changes - optional) | |

+------------+


Pros : MVC간 연결이 최소화 되어 있어 각각에 대한 독립성이 잘 보장되어 있다.

Model부분이 최소화 되므로, Data 부분에 대한 안정성을 높이기 유리하다.

Cons : Control부분의 역할이 Model, View에 비해 압도적으로 복잡할 가능성이 높다.

따라서, 복잡한 UX를 가질 경우, Control 부분의 통제가 상당히 어려워질 수 있다.

일단 가장 좋은 점은, Model에 대한 update가 Control로 제한되어 있기 때문에

(View -> Model로 write path가 없다.), Data에 대한 관리가 일원화 되어 있다.

또한, Control이 Model의 data를 update하므로, View는 Model의 Data가 바뀌는지 아닌지 굳이 Observing할 필요가 없다.

(필요에 따라 Observing하는 것도 괜찮다.)



. .


+ Recent posts