5년차의 슬럼프

이미지
2-3년 전까지만해도 꿈을 이뤘다고 생각했다. 매 해 더 어렵고 까다로운 일들을 맡게되고, 또 그것을 잘 해내면서 인정을 받을 때 마다 내가 성장하고 있다고 믿었다. 내가 성장할 때마다 인정을 받았고, 인정 받을 때마다 난 성장했다. 쌓이는 신뢰도와 높은 인사평가의 점수가 곧 나의 자질이며 실력이라고도 생각했다. 내 목표의식은 항상 successful result에 있었기 때문에 그에 따르는 성취감과 업무 만족도는 비례했으며, 따라서 회사의 발전이 곧 나의 발전이었다. 덕분에 프로젝트들을 주도할 기회가 점차 많이 주어지게 되었고, 그 과정 속에서 솔직히 만족스러웠다기보단 회사의 비전을 고민하게 되는 계기가 되었다. 두세개의 프로젝트를 병행하면서도 나은 결과를 위해 신체적으로나 심리적으로 많이 무리했었고, 물리적으로 불가능한 일정을 해보겠다고 사무실에 남아 새벽까지 일하다보면 어떤 날들에는 막역한 부담감이 너무 무거워 하염없이 눈물이 나기도 했다. 같이 일하는 팀원들은 다들 이미 힘들고 지친 나머지 프로젝트의 완성도를 높이는 작업에는 여력이 없는 모습들을 보며, 나도 점차 지쳐갔다. 내 열성을 다른 사람에게까지 강요할 수는 없는 노릇이었으니까. 개발 과정에서 아무리 time complexity와 design에 대해서 이야기해도, 반론을 제기하며 논쟁 했던 사람이 없다. 심지어 이미 개발을 다 끝낸 코드에 이슈를 제기했을 때도 상대방은 바로 수긍하며 다시 짜겠다고했다. 이상하게도 그 모습을 보며 화가 났다. 고민하지 않고 작성한 덕분에 쉽게 지워버릴 수 있는, 자존감이 없는 코드에 화가 났고, 그렇게 짜올 동안 누가 지적 한번 해준 적 없다는 상황이 화가 났다. 무엇보다 더 나은 방법이 있는지 찾아보지 않고 내 말 곧이 곧대로 수정하는 모습이 결국 등을 돌리는 계기가 되었다. 내게 필요했던건 워라밸도, 높은 연봉도 아니라 최소한 나와 비슷한 열정을 가진 사람들이었다. 내가 더 이상 이 회사에 남아 있어야 할 이유가 없었다. 남아서 더 배워야할 우리

멀티 스레딩 환경에서의 Singleton 사용 및 견고한 Singleton 구현 방법 (Enum Singleton)

이미지
Singleton? Singleton pattern은 특정 인스턴스에 대하여 단일 인스턴스만 만들어질 수 있도록 제한하는 패턴이다. 스레드 풀이나 캐시, 네트워크 모듈 등등에서 같은 객체를 여러개 만드는 것은 불필요한 자원을 할당하며 예기치 않은 오류를 불러올 수 있는데, 이런 상황에서 singleton을 사용하여 하나의 인스턴스만을 생성하여 사용하도록 관리를 해주면 불필요한 메모리 낭비를 줄일 수 있고 사용성 측면에서도 효율적이라 자주 쓰이는 패턴중에 하나이다. 자주 사용하던 방식은 이런 방식이다. 이렇게 되면 생성자가 private이라 무조건  getInstance() 를 통해서만 인스턴스에 접근할 수 있기 때문에  mInstance == null 인 최초 상황에서만 초기화가 되며 그 이후에는 생성된 기존의 인스턴스를 사용하게 된다. 그러나 과연 'Singleton'을 보장할 수 있을까? 일단 기본적으로 Android는 멀티 스레딩 환경이다. 만약 A thread와 B thread에서 동시에 이 인스턴스에 접근하려고할 때 마침 mInstance 가 null이였다면, A thread에서도  mInstance == null 를 타기 때문에  mInstance 를 새로 할당할거고 B thread에서도  mInstance == null 를 타며 새로 할당하게 될 것이다. 그렇게 되면 예상했던 것과는 달리 mInstance 가 2개 이상의 중복 생성이 되며 예상하지 못한 오류를 만들어낼 수도 있다. 일단 해결책으로, Main thread에서만 접근하도록 제어할 수도 있을 것이다. 다른 스레드에서 접근하면 Exception을 날린다던가 하는 식으로 예외처리를 할 수 있지만, 만약 이 singleton 객체가 꼭 멀티 스레딩 환경에서도 사용가능하게끔 해야 한다면?? 그래서 나온 해결책이  getInstance() 를  synchronized  시키는 것이다.  mInstance 를 체크하고 할당하

ReactNative: 기존 네이티브 프로젝트에 이식하기 (+native 메소드 호출)

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.41) 1. What I want to do 내가 원한건 이런 화면이다. 전체적으로 Android 네이티브 코드로 제작되어있는데, (Toolbar나 DraweLayout 등...)    ① 기본적인 메뉴들은 네이티브 화면이 보여지지만    ② 특정 메뉴는 ReactNative로 코딩된 화면 노출    ③ 어떤 화면에서는 아예 혼합되어있어야 하고     ④ 아예 ReactNative로만 된 activity도 2 dept로 호출할 수 있는 앱    + 그리고 각 ReactNative 화면에서도 자유롭게 네이티브의 메소드들을 호출할 수 있게끔. ▼ gif로 보자면 대충 이런식으로 흐른다 . (연두색은 네이티브 / 하늘색은 ReactNative)   2. What I should do 일단 일반적인 방법으로 AndroidStudio에서 새로운 프로젝트를 생성해준다. 네이티브 코드로 대충 뼈대를 잡아준다. DrawerLayout , SlidingTab 등등.. ReactNative 사용 가능 환경 설정을 해준다. ( Integration With Existing Apps )  package.json ,  index.android.js  만들고 gradle 설정 작업 등을 한다.  ReactNative를 보여줄 부분에  ReactRootView 를 사용하여 dynamically adding 해준다. 대충 이런식으로.. 이 부분은 위에 링크한 공식 문서를 참고해 충분히 응용 가능하다. ReactNative에서 네이티브 내의 메소드들을 호출하기 위해 모듈을 생성한다.  ReactContextBaseJavaModule 를 오버라이딩 받아야하며, getName() 에서의 리턴값은 ReactNative에서 import 시 명시할 모듈이름이기 때문에 유의해서 짓는다. 그리고  @ReactMethod  어노테이션과 함께 원하는 메소

ReactNative: onHost 생명주기 메소드가 없을 때

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 기존 Android 네이티브 앱에 ReactNative를 이식하기 위해서 ReactNative 공식 doc에서 가이드해주는  Integration With Existing Apps 를 따라하고 있었다. 1. 이슈 내용 하라는대로 제대로 잘 따라한 것 같은데, 일단 첫번째 문제는 doc에서 사용하라는  onHost... 로 시작하는 생명주기 메소드들을 찾을 수가 없었다. (아래 스크린샷의 메소드) 일단 뭐 대충 보니 메소드 이름과 파라미터들은 달랐지만  onPause() ,  onResume() ,  onDestroy()  라는 메소드들이 있어서 '뭐 아직 문서가 업데이트 되지 않았나보다' 하고 대수롭지 않게 넘기고  onPause() ,  onResume() ,  onDestroy()  메소드들을 사용했다. 그러고나서 빌드하니 발생하는 RuntimeException: IllegalAccessError?? 2. 이슈 원인 일단 원인은 프로젝트 루트의  build.gradle  에서 maven  코드 블럭 추가시 잘못된 경로를 추가한 문제였다. doc에 나오는 정확히  $rootDir/../node_modules/react-native/android  이 부분이다. 일반적으로 이 doc 문서를 보면서 차근차근 따라했다면, 프로젝트 루트안에  node_modules  폴더가 만들어졌으므로 node_modules  폴더가 저 경로 안에 위치할리가 없다. 3. 해결방법 해당 url을  $rootDir/node_modules/react-native/android  로 수정 하면 앞에서 언급한  onHostPause() ,  onHostResume() ,  onHostDestroy()  메소드들을 사용할 수도 있고 RuntimeException도 당연히 해결할 수 있다. (또한 같은

ReactNative: 컴파일시 발생하는 에러 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 런타임시 발생하는 에러 해결 은 이쪽으로 ReactNative 프로젝트를 빌드하는 방법은 크게 2가지가 있는데, cmd 창에서  react-native run-android  명령어를 통해서 빌드하거나 AndroidStudio에서 일반적인 방법으로 빌드하는 것이다. (js 파일 업데이트가 아닌 네이티브 코드를 포함한 빌드를 말한다.) 개발을 하다보면 빌드 자체가 안되는 경우가 있는데, 사실 이 경우는 좀 무궁무진해서(...) 다 정리할 수는 없지만 일반적으로 나 혹은 동료 개발자들이 (특히 ReactNative 학습 초기에) 겪은 케이스를 정리중이다. 적어도 ReactNative가 언급한 개발 환경 은 모두 셋팅이 되어 있어야 한다. NodeJs 설치부터 환경변수 설정, SDK, 심지어 에뮬레이터까지 모두 설명이 되어있다. SDK 같은 경우에는 Marshmallow SDK 뿐만 아니라 그래픽 시스템 이미지 SDK 등까지 설치가 필요하므로 주의가 필요하다. Gradle plugin의 최소 요구 버전을 맞춰야 한다. 사실 이 최소 요구버전 코드가 대체 어디 명시가 되어있는지 찾질 못하겠다. ^^ 그러나 확실한건 버전이 특정 버전보다 낮은 경우에 '패키지명.패키지명.MainActivity.java'를 찾지 못하겠다며 (패키지명이 두번씩 경로에 언급된다) 컴파일이 안된다. AndroidStudio를 통해서 업데이트하면 쉽다. AndroidStudio 같은 경우 project를 clean하고 해결되는 경우도 있다.  주로 라이브러리 관련한 이슈가 발생할 때 해결 가능 방법인데, 어쨌든 '갑자기' 안된다 싶을 때는 거의 'clean'으로 해결이 된다. 특히 AndroidStudio 버전이 2.2 이상인가로 올라가면서 만들어진 'InstantRun'을 사용하는 경우 인스턴트런 특유의 버그 때문에 발생할 가능성

ReactNative: 6.0 Marshmallow 이상 버전에서 퍼미션 에러로 죽는 이슈 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 일단 ReactNative는 아직까지 Marshmallow의 Permission 관련 대응이 되지 않고 있다. 그래서 이 부분에 대한 개발자들의 예외처리가 필요한데, 일단 다른 기능을 사용하지 않아도 기본적으로 DevSettingMode에서부터 '다른 앱 위에 그리기 권한'이 필요하다. (DevSettingMode가 뭔지 모르거나 혹은 설정해준적 없는 경우, default가 '사용'이기 때문에 대부분 사용하고 있는 경우이다.) 일단 6.0 이상의 단말의 Permission 획득에 관한 내용은 Android Developer 문서 참고 Permission 예외처리 로직이 없는 경우 권한 획득에 실패하여 당연히 앱이 죽을 것이다. 특히나 이 '다른 앱 위에 그리기 권한' 같은 경우에는 Dangerous Permission이 아니기 때문에, 앱 설정에서 해줄 수 있는 부분이 아니여서 설정 화면을 직접 띄워줘야한다. (같은 맥락으로 onActivityResult() 에서의 Granted 체크가 어렵다.) 로직은 간단하다 : showReactView() 메소드에서 ReactNative 화면을 호출하든 ReactRootView를 통해 view를 이식하든 자유롭게 코딩하면 된다. 이렇게 하면 6.0 이상의 버전에서는 해당 요청시 '다른 앱 위에 그리기 권한'을 허용하는 화면이 뜬다.

ReactNative: 런타임시 발생하는 에러 해결

이미지
(글 작성한 시점의 최신 ReactNative ver: 0.40) 컴파일시 발생하는 에러 해결 은 이쪽으로 처음 ReactNative를 공부하다보면 단순히 앱이 예기치 않은 오류로 죽는 것 뿐만 아니라, 런타임 시점에 빨간 에러 화면을 무진장 볼 수 있는데: 바로 이런 경우에 대한 해결방법이다. (물론 스크립트 코딩을 잘못해서 발생하는 디버그 에러는 제외다.) 'Could not connect to development server' 이슈를 포함해 처음부터 안되거나 알 수 없는 이유인경우 아래 해결방법들로 해결 될 수도 있다. 아래 해결 방법들은 ReactNative 학습 초기 단계의 해결방법들이 대다수이다. 앱이 예기치 않은 오류로 실행 중 죽어버리는 경우 UnsatisfiedLinkError(AsyncTask #1) 이슈인 경우 : 블로그 본문 링크 참고 DevSettingMode를 사용중이며 단말의 버전이 6.0 이상인 경우 단말이 필수로 overlay 퍼미션을 허용해줘야한다.  : 블로그 본문 링크 참고 프로젝트 루트의 build.gradle에 maven 경로 추가시 올바르게 추가해줘야 한다. onHost... 로 시작하는 생명주기 메소드를 찾을 수 없는 경우 :  블로그 본문 링크 참고 혹은 build.gradle 파일에  repositories  가 2개여서 잘못 추가할 수도 있는데,  allprojects  의  repositories  에 추가해줘야 한다. 상단에 첨부한 빨간 에러 화면을 보이는 경우 Android sdk의 환경변수가 필히 'ANDROID_HOME'으로 잡혀야 한다.  사실 이건 굉장히 이상하며 불친절한 부분이다.  ReactNative Getting Started 가이드 에서 설명하는 AndroidStudio나 Android SDK 등이 이미 설치 되어있는 기존의 안드로이드 개발자의 경우에는 이 부분을 그냥 지나칠