스토리 홈

인터뷰

피드

뉴스

조회수 4743

Elasticsearch X-Pack Alerting 체험기

Logstash로 로그를 수집한 후 Elasticsearch와 Kibana로 분석하는 방법을 다룬 글은 많다. 그런데 이상하더라 이 말이지. 로그를 분석하고 경향을 파악하는 정도라면야 괜찮은데 심각한 오류 로그를 발견했을 때 Slack이나 이메일 등으로 알람 받을 수단이 마땅치 않더라. 사람이 키바나 대시보드를 5분마다 확인할 수도 없는 노릇이다. (이건 새로운 차원의 고문?)이런 생각을 먼저 한 사람이 있기 마련이라 Yelp의 elastalert라던가 Elasticsearch의 X-Pack을 활용하면 이런 문제를 해소할 수 있다. 오늘은 그 중에서 후자를 살펴볼 예정이다.경고! X-Pack은 Elasticsearch가 유료 서비스 시장을 열려고 야심차게 미는 모양인데 “자기네가 직접 만들었으니 쿨하겠지?”라고 쉽게 생각하면 하루 안에 절벽 아래로 떨어지는 끔찍한 기분을 맞이할 수도 있다.X-Pack은 가격이 상당한데 Alert 등을 설정하려면 전적으로 RESTful API에 의존해야 한다. 적어도 아직까지는! 이 사실을 깨닫자마자 당황할 수 있는데 침착하자. 이것은 시작일 뿐이다. 여러분이 검색엔진의 초보라면 그 다음 난관은 검색 쿼리를 작성하는 것이다. “나는 그냥 OutOfMemoryError 로그를 발견하면 알람을 보내줬으면 좋겠어"라고 쉽게 생각했겠지만 그 간단한 결과를 얻으려면 험난한 여정을 거쳐야 한다."search" : { "request" : { "indices" : [ "", ], "body" : { "query" : { "bool" : { "must" : { "multi_match": { "query": "OutOfMemoryError", "fields": ["message", "log"] } }, "filter" : { "range": { "@timestamp": { "from": "{{ctx.trigger.scheduled_time}}||-5m", "to": "{{ctx.trigger.triggered_time}}" } } } } } } } }음… 좋다. 일단 이렇게 작성한 쿼리가 제대로 된 것인지 테스트하려면 어떻게 해야 하는가? 검색 API로 대충 테스트해볼 수는 있다.GET logstash-2017.02.2*/_search { "query" : { "bool" : { "must" : { "multi_match": { "query": "OutOfMemoryError", "fields": ["message", "log"] } } } } }어찌어찌 잘 나온다. 그래서 잘 돌 줄 알았지? 그럴 줄 알고 있다가 이런 메시지를 만난다.Trying to query 1157 shards, which is over the limit of 1000. This limit exists because querying many shards at the same time can make the job of the coordinating node very CPU and/or memory intensive. It is usually a better idea to have a smaller number of larger shards. Update [action.search.shard_count.limit] to a greater value if you really want to query that many shards at the same time.음… logstash 인덱스를 매시간마다 분할했더니 샤드가 꽤 많아진 모양이다. 그래서 최근 두 개의 인덱스로 검색 대상을 제한하려고 한다. Date math support in index names라는 문서에 인덱스 이름을 동적으로 바꾸는 법이 나와 있긴 하다. 그런데 막상 내가 짠 게 어떤 값이 나오는지 확인하는 방법은 제대로 안 나온다. 예를 들어 가 logstash-2017.02.22t01로 해석되는지 어떻게 아는가? 많은 삽질 끝에 방법을 찾았다.를 URL 인코딩한다.그렇게 얻은 값 을 가지고 인덱스 조회 API를 호출한다. GET /3Clogstash-{now-1h/d}t{now-1h{HH}}>그러면 다음과 같이 결과가 나와서 인덱스 이름이 어떻게 해석됐는지 확인할 수 있다.{ "logstash-2017.02.23t01": { "aliases": {}, "mappings": { /* 중략 */ } }여기까지는 전적으로 검색 쿼리 작성 경험이 부족해서 발생한 삽질이다. 하지만 애플리케이션 로그 분석을 패턴화하지 않고 이렇게 검색 쿼리를 복잡하게 짜야 한다니 아직 갈 길이 멀다는 생각이 든다. DataDog 또는 NewRelic 같은 상용 서비스를 참고해서 개선하면 좋겠다.이제 결과를 알람으로 보내면 된다. 이래저래 고생하다 대충 아래와 같은 형태로 완성했다.PUT _xpack/watcher/watch/outofmemoryerror { "trigger" : { "schedule" : { "cron" : "0 0/4 * * * ?" } }, "input" : { "search" : { "request" : { "indices" : [ "", "" ], "body" : { "query" : { "bool" : { "must" : { "multi_match": { "query": "OutOfMemoryError", "fields": ["message", "log"] } }, "filter" : { "range": { "@timestamp": { "from": "{{ctx.trigger.scheduled_time}}||-5m", "to": "{{ctx.trigger.triggered_time}}" } } } } }, "sort" : [ { "@timestamp" : {"order" : "desc"}}, "_score" ] } } } }, "condition" : { "compare" : { "ctx.payload.hits.total" : { "gt" : 0 }} }, "actions" : { "notify-slack" : { "throttle_period" : "5m", "slack" : { "message" : { "to" : [ "#ops", "@dev" ], "text" : "로그 모니터링 알람", "attachments" : [ { "title" : "OutOfMemoryError", "text" : "지난 5분 동안 해당 오류가 {{ctx.payload.hits.total}}회 발생했습니다. 가장 최근의 오류는 다음과 같습니다.", "color" : "warning" }, { "fields": [ { "title": "환경", "value": "Prod", "short": true }, { "title": "발생시각", "value": "{{ctx.payload.hits.hits.0._source.@timestamp}}", "short": true }, { "title": "메시지", "value": "{{ctx.payload.hits.hits.0._source.message}}", "short": false }, { "title": "확인명령어", "value": "`GET /{{ctx.payload.hits.hits.0._index}}/{{ctx.payload.hits.hits.0._type}}/{{ctx.payload.hits.hits.0._id}}`", "short": false } ], "color" : "warning" } ] } } } } }4분마다 검색 쿼리를 실행해서 최근 5분 간의 레코드를 감시하기 때문에 동일한 오류에 대해 2회 연속으로 알람을 받을 가능성이 있다. X-Pack은 이를 우회할 방법을 제공하지 않는 것 같다. 그래서 쿼리가 발견한 레코드의 인덱스 ID를 Slack 메시지 중 확인명령어 필드에 넣었다. 알람이 두 번 왔지만 인덱스 아이디가 동일하다면 오류가 한번 발생한 것으로 간주하면 된다.참고 문서위의 Alert를 작성하며 도움을 받은 문서는 다음과 같다.Multi Search Template은 검색 쿼리를 짤 때 도움이 됐다.Search Input 문서는 검색 쿼리 또는 검색 결과를 작성할 때 어떤 변수를 사용할 수 있는지 설명한다. 예) {{ctx.payload.hits.hits.0._source.message}}Watcher APIsSlack ActionDate math support in index names 문서는 인덱스 이름을 동적으로 바꾸는 법을 설명한다.기타Elasticsearch Cloud는 기본적으로 이메일 발송을 지원하기 때문에elasticsearch.yml 설정에 xpack.notification.email를 추가하지 않아도 된다. 아니, 추가하면 잘못된 설정이라며 거부한다. Illegal이라고만 하지 이유를 자세히 알려주지 않기 때문에 삽질하기 쉽니다. Invalid addresses라고 오류 로그가 찍히면 이것은 설정 문제가 아니다. 이메일 설정 메뉴로 가서 Watcher Whitelist에 수신 이메일 주소를 등록하면 문제가 해결된다.테스트용 로그 메시지를 Fluentd로 보내고 싶다면 fluent-cat 명령을 이용한다.echo '{"message":"Dummy OutOfMemoryError"}' | fluent-cat kubernetes.logOriginally published at Andromeda Rabbit.#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트
조회수 2164

Android Gradle Tips

안드로이드와 GradleAndroid 가 Gradle 을 이용하기 시작한 것도 3년이 다 되어 갑니다. 이제는 많은 유저가 당연히 Gradle 을 Android 기본 개발 환경으로 사용하고 있습니다.하지만 기본 설정으로만 Gradle 을 사용하는 사용자들이 많습니다. 게다가 구글에서 Android Gradle Build DSL 을 끊임없이 변경했기 때문에 많은 사용자들이 이를 이해하기도 전에 변경이 되는 경우가 매우 빈번했습니다.Gradle Dependency 분리하기안드로이드 자동화 툴위 두번의 포스팅을 통해서 TossLab 에서 사용하고 있는 Gradle 에 대해서 소개를 해드렸습니다.오늘은 Android 팀이 사용하는 Custom 설정들에 대해서 정리하도록 하겠습니다.1. 초기화 값 검증 및 설정하기개발자들이나 CI 에서 관리해야하는 속성 값에 대해서는 각각 다르게 설정할 필요가 있습니다.안드로이드 팀은 3개의 추가적인 속성값을 추가하여 사용하고 있습니다.# gradle.properties inhouse_version=2 # 배포/qa 버전의 hofix version 을 관리학 ㅣ위함 report_coverage=false # coverage 측정에 대한 on/off 기능 dev_min_sdk=21 # minSDK 의 개별적인 관리를 위함 위의 3개의 값은 존재 하지 않으면 빌드가 되지 않도록 하는 강제사항으로 만들었으나 새로운 개발자가 입사하게 되었을 때 또는 CI 서버에 실수로 기입하지 못하게 되었을 때 Project Import 나 빌드가 아예 되지 않는 현상이 발생하였기에 초기 값을 설정할 수 있도록 하였습니다.report_coverage 는 5. Android Gradle DSL 에서 buildTypes.debug.testCoverageEnabled 에서 사용되며 이 값은 설정에 따라서 디버그 과정에서 변수값들이 제대로 노출되지 않게 됩니다. report 가 필요한 CI 서버 용으로 만들어진 값입니다.// valid.gradle def checkValidProperties() { println "Properties Valid Checking.........." if (!project.hasProperty("inhouse_version")) { println "set up to gradle.propeties --> inhouse_version = 1 (default)" project.ext.inhouse_version = 1 } if (!project.hasProperty("report_coverage")) { println "set up to gradle.propeties --> report_coverage = false (default)" project.ext.report_coverage = false } if (!project.hasProperty("dev_min_sdk")) { println "set up to gradle.propeties --> dev_min_sdk = 19 (default)" project.ext.dev_min_sdk = 19 } println "Properties Valid Check OK" } checkValidProperties() // ------------------------------- // build.gradle apply from: 'valid.gradle' 위와 같이 설정한 뒤 gradle.properties 에 아무런 값을 설정하지 않고 빌드를 하게 되면 빌드 최초에 다음과 같은 log 를 보실 수 있습니다.================================================================================ Properties Valid Checking.......... set up to gradle.propeties --> inhouse_version = 1 (default) set up to gradle.propeties --> report_coverage = false (default) set up to gradle.propeties --> dev_min_sdk = 19 (default) Properties Valid Check OK ================================================================================ 2. APK Copy 하기QA 팀 전달 또는 스토어 배포시에 Android Studio 의 기본 기능을 이용하지 않고 Gradle Task 를 사용하여 빌드를 하게 되면 /app/build/outputs/apk 에 있는 패키지를 복사하는 것이 여간 귀찮은 작업이 아닐 수 없습니다.그래서 Gradle 에서 기본적으로 제공되는 Copy Task 를 이용하여 APK Copy Task 를 만들었습니다.// apk-copy.gradle android.applicationVariants.all { variant -> // 1. Copy Task 생성 def task = project.tasks.create("copy${variant.name}Apk", Copy) task.from(variant.outputs[0].outputFile) // 2. 바탕화면 Task 로 복사 task.into("${System.properties['user.home']}/Desktop/") // 3. 복사하는 과정에서 APK 이름 변경 def targetName = "jandi-${variant.baseName}-${variant.versionName}.apk" task.rename ".*", targetName task.doFirst { println "copy from ${source.singleFile.name} to $destinationDir" } task.doLast { value -> println "completed to copy : $targetName" } } // --------------- // build.gradle apply from: 'apk-copy.gradle' 위의 Task 는 총 3개의 단계로 구분할 수 있습니다.Copy Task 생성~/Desktop 으로 복사복사 할 때 APK 이름 변경Task 를 정의하는 과정에서 application 의 flavor, build-type, version 을 기반으로 복사하도록 한 것입니다.위와 같이 설정하면 다음과 같이 사용할 수 있습니다.# flavor : qa , build-type : Debug $> ./gradlew assembleQaDebug copyqaDebugApk # 또는 줄여서 아래와 같이 쓸 수 있습니다. $> ./gradlew aQD copyQDA Application Variant 에 대한 변수는 링크에서 확인하실 수 있습니다.3. CI TasksCI 용으로 CheckStyle 과 PMD 를 사용하기 때문에 관련 설정 또한 별도로 처리하였습니다.task pmd(type: Pmd) { source 'src/main' include '**/*.java' ruleSetFiles = files('../pmd.xml') ignoreFailures = true } task checkstyles(type: Checkstyle) { configFile file('../checkstyle.xml') source('src/main') include '**/*.java' classpath = files() showViolations = true ignoreFailures = true } // --------------- // build.gradle apply from: 'ci-tasks.gradle' CheckStyle 과 PMD 설정에 필요한 정보 또한 별도의 script 로 설정하였습니다.4. Gradle Properties빠른 빌드를 위해 추가적인 설정을 하고 있습니다.# gradle.properties # 백그라운드 빌드 org.gradle.daemon=true # 동시 빌드 org.gradle.parallel=true # jvm heap size org.gradle.jvmargs=-Xmx4346m # build jdk org.gradle.java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home 위의 설정 중에서 제일 보셔야 할 것이 org.gradle.jvmargs 입니다. Android Gradle 설정 중에서 위의 값이 적으면 빌드속도가 현저히 느려집니다.빌드 할 때 console log 를 확인하시고 값을 적절하게 맞춰주실 것을 권장합니다.5. Android Gradle DSL 추가 정의하기 // build.gradle // ...중략 android { // 특정 Flavor에서 Release Build 막기 android.variantFilter { variant -> if (variant.buildType.name.equals('release') && (variant.getFlavors().get(0).name.equals('qa') || variant.getFlavors().get(0).name.equals('dev'))) { variant.setIgnore(true); } } buildTypes { debug { debuggable true testCoverageEnabled = project.hasProperty("report_coverage") && report_coverage.toBoolean() } // ..중략... } productFlavors { dev { // demo version applicationId 'com.tosslab.jandi.app.dev' versionName(defaultConfig.versionName + ".dev." + inhouse_version) minSdkVersion project.hasProperty("dev_min_sdk") ? dev_min_sdk : 19 } // ..중략.. } // 빌드 과정에서 CPU 와 Ram 최적화 하기 dexOptions { javaMaxHeapSize "2g" maxProcessCount Math.max(1, ((int) (Runtime.getRuntime().availableProcessors() / 2))) } } variant-filter 를 이용해서 qa 나 dev 용 빌드는 release 버전이 빌드되지 않도록 하였습니다.buildTypes 와 productFlavors 에서는 앞서 설정한 gradle-properties 에 대해서 설정에 따라 기본값이 지정되도록 하였습니다.dexOptions 설정은 개발하는 기기의 PC 환경에 따라 다를 수 있습니다.Android DSL 에 의하면 Dex 빌드 과정에서 최종적으로 사용하는 메모리는 heapsize * process-count 라고 합니다.heapsize 기본값 : 2048MBprocess-count 기본값 : 4참고문서6. Android Resource Image 의 EXIF 정보 삭제하기보통 디자이너가 Photoshop 과 같은 툴을 이용하여 이미지를 만들게 되면 자동으로 adobe 와 관련된 exif 정보가 붙게 됩니다. 그래서 빌드 할 때 libpng warning : iCCP ... 와 같은 warning 메세지를 보실 수 있습니다. 이는 Android Build 과정에서 aapt 가 이미지 최적화 하는 과정에서 불필요한 exif 정보로 인해서 오류를 내게 됩니다.따라서 exif 정보를 초기화 해주는 작업이 필요합니다.맥 사용자에 한해서 지원됩니다.HomeBrew 를 이용해서 exiftool 을 설치하셔야 합니다. exiftool 설명find . -path '*src/main/res/*' -name '*.png' -exec exiftool -overwrite_original -all= {} \; 저는 별도로 쉘 스크립트를 만들어서 실행합니다.아래를 복사해서 붙여넣기로 실행하시면 됩니다.echo "find . -path '*src/main/res/*' -name '*.png' -exec exiftool -overwrite_original -all= {} \;" > exif_clean.sh chmod 744 exif_clean.sh 관련 정보 : adt-dev google group 에서 제시된 해결책Wrap up안드로이드 팀은 Gradle 을 이용하여 반복적일 수 있는 작업을 자동화 하고 다양한 초기화 설정과 편의를 가지고자 하였습니다.초기화 값 검증 및 설정Apk 복사 자동화CI Task 정의Gradle Properties 지정Android Gradle DSL 정의Android Resource Image EXIF 삭제Gradle 을 얼마나 잘 활용하냐에 따라서 조직에 필요한 Task 를 금방 만드실 수 있습니다. 이번 포스팅이 도움이 되었기를 바라며 활용해보실 것을 권장합니다.#토스랩 #잔디 #JANDI #개발자 #개발팀 #앱개발 #안드로이드 #인사이트
조회수 1276

[인공지능 in IT] 올림픽의 주인공은 여전히 인간이다

2011년 7월 7일 새벽 오전0시 18분, 남아프리카 공화국 더반 컨벤션센터에서 평창이라는 단어가 적힌 흰색 쪽지가 뽑힐 당시, 대한민국 국민 모두의 염원이 현실로 이뤄졌다. 88 서울 올림픽, 2002 한일 월드컵 이후 우리나라에서 열린 전세계 규모의 가장 큰 축제가 평창에서 개최됨을 알리는 순간이었다. 동계올림픽은 인종과 국가, 정치 및 이념을 초월하고 전인류의 평화와 화합을 증진시키기 위한 글로벌 겨울 축제이자 세계 젊은이들의 힘과 기록의 제전이라 할 수 있다. 1924년 프랑스 샤모니 대회를 시작으로 2018년까지 총 22회 개최, 고대 그리스의 올림피아 경기부터 시작된 '올림픽'보다 그 역사가 현저히 짧지만, 올림픽이라는 의미만은 분명하다.< 2018>올림픽에 참가하는 선수들은 그들의 육체적, 정신적인 능력의 최대치를 겨룬다. 올림픽은 인간이 가진 힘을 시험하는 장이고, 이에 관중들은 우월한 선수들의 능력에 환호성을 자아낸다. 물론, 기술이 발달하며 선수들이 입는 유니폼이나 여러 도구들이 진화를 거듭했지만, 올림픽이라는 것은 인간 본연의 모습에 큰 초점을 맞춘 시험대다. 이렇듯 'Raw'하다고 볼 수 있는 선수들의 경쟁을 비 선수 입장에서 지켜볼 수 있는 것만으로도 엄청난 기회라고 생각한다.이번 평창 동계올림픽에서 발견한 재미있는 요소 중 하나는 관중을 위한 기술의 접목이라 할 수 있다. 과학기술정보통신부는 동계올림픽과 패럴림픽 진행 중 다양한 기술을 누릴 수 있도록 '평창 ICT 올림픽 가이드북'을 발간했다. 해당 가이드북을 살펴보면, ICT 올림픽은 이번 평창 올림픽의 5대 목표 중 하나다. 인공지능, 5G, UHD, IoT, 가상현실(VR) 등을 어떻게 이용할 수 있는지 국문과 영문으로 된 가이드북을 제작하고, 평창 ICT 체험관과 인천공항 등 오프라인을 포함한 온라인 상에도 게시한다.< 2018>5대 ICT 서비스 중 가장 관심이 가는 영역은 인공지능이다. 한국을 찾는 외국인들이 느낄 수 있는 언어장벽을 완화시키기 위해 인공지능 기반 통번역서비스를 제공하고, 올림픽이 열리는 동안 경기 정보 및 교통상황을 알아보기 위해 인공지능 콜센터를 통해 24시간 문의할 수 있다. 혹자는 단순히 편의성을 위해 간단한 기술을 도입했다 할 수 있을 것이다. 하지만, 인공지능 기술은 그 자체로도 어려운 과제임과 동시에, 선수가 아닌 관람객을 위해 도입했다는 것에서 의미를 부여하고 싶다.다시 한번 언급하지만, 올림픽은 오랜 훈련을 통해 능력을 입증하고 싶은 선수들은 물론, 경기를 지켜보고 이들을 응원하는 관람객들까지 모두 '참여'하는 축제다. 한국어를 전혀 모르는 핀란드의 방송사 관계자가 통번역서비스를 활용해 아무 문제없이 스피드 스케이팅 경기를 중계할 수 있을 것이고, 대구에서 평창까지 봅슬레이 경기를 보러 가는 가족들이 편안한 운전을 위해 새벽 1시에라도 교통 정보를 얻을 수 있는 것이다.최근 인공지능을 활용한 통번역 서비스는 날이 갈수록 진화를 거듭하고 있다. 대부분의 기업에서 'NMT(Neural Machine Translation)'라고 부르는 인공신경망 기계번역을 활용하는데, NMT는 인공지능 기술을 적용해 어마어마한 양의 번역 결과 데이터를 스스로 학습하고, 다른 번역 케이스에도 적용하는 기술이다. 우리에게 많이 알려진 NMT 탑재 번역 서비스는 구글 번역과 네이버 파파고가 대표적이다. 이번 평창올림픽에서 사용되는 통번역서비스는 한글과컴퓨터가 한국전자통신연구원(ETRI)과 공동 개발한 '말랑말랑 지니톡'이다. 실제로 지니톡에는 올림픽 종목, 강원도 지역 관광지, 음식 등 동계올림픽에 관련된 데이터베이스 10만 건을 적용, 상당한 정확도를 보인다고 한다.한글과컴퓨터가 한국전자통신연구원(ETRI)과 공동 개발한 말랑말랑 지니톡, 출처: 한컴24시간 상담할 수 있는 콜센터도 인공지능은 핵심적인 역할을 담당한다. 여기서의 인공지능은 대량의 텍스트 기반 데이터를 학습하는 것이 중요하다. (1) 인식률이 높은 'DNN(Deep Neural Network)'을 토대로, (2) 한국어를 음성으로 인식할 수 있는 'STT(Speech To Text)'를 구축한 뒤(굉장히 어려운 과제다), (3) 콜센터로 들어오는 상담 내용을 분석하고, (4) 이를 제대로 학습할 수 있는 인공지능 기술을 접목해야 한다. 이 모든 것을 만족해야 콜센터에 상담을 요청하는 고객들이 인간 상담원과의 기계간의 괴리감을 적게 느낄 수 있기 때문이다.인공지능 상담 영역은 CS가 가장 활발히 이루어지는 업계 중 하나인 금융, 특히 보험 업계에서 많이 적용 중이다. AIA생명 한국지점은 인공지능을 도입해 고객과 상담하는 챗봇과 전화로 응답하는 로보텔러 서비스를 실시한다. 고객이 자주하는 문의에 대해서는 채팅 형태로 24시간 365일 인공지능 챗봇이 1차 상담을 진행하고, 대기시간 없이 바로 연결할 수 있어 생산성과 효율성, 정확도 등을 높일 수 있다. 또한, 고객에게 판매된 보험계약에 대해 로보텔러가 직접 전화를 걸어 완전 판매를 모니터링하는 업무도 진행한다. 여기는 인공지능 상담사가 학습한 대화를 기반으로 고객과 대화를 진행해 계약정보를 확인하고 계약을 확정하는 음성서비스를 적용했다.이번 평창 동계올림픽을 통해 인공지능은 그 범위를 가리지 않고 곳곳에 적용될 수 있다는 새로운 잠재력을 평가 받고 있다. 사실 최근 몇 년 사이에 IT 뿐만이 아니라 유통, 제조 등을 막론하고 기업들은 사활을 걸고 인공지능에 투자 중이다. 고도의 기술을 축적하고, 이를 적용해 새로운 사업 모델로 확장하거나, 단순반복적인 일을 대체해 비용을 줄일 수 있다는 점은, 인공지능을 하나의 패러다임으로 이끌고 있다. 물론, 이번 평창 동계올림픽에서 선보인 인공지능 기술들은 여러 기업들이 사업적인 측면을 고려한 사안일 것이다. 하지만, 인간이 주체가 되어 열리는 올림픽이라는 축제 속에서 인공지능이 한자리를 차지한다는 것은, 이제 더이상 놀랄 일은 아닐지 모른다. 결국, 인공지능도 인간의 삶을 이롭게 하기 위해 탄생된 기술이라는 것을 잊지 말자.이호진, 스켈터랩스 마케팅 매니저조원규 전 구글코리아 R&D총괄 사장을 주축으로 구글, 삼성, 카이스트 AI 랩 출신들로 구성된 인공지능 기술 기업 스켈터랩스에서 마케팅을 담당하고 있다#스켈터랩스 #기업문화 #인사이트 #경험공유 #조직문화 #인공지능기업 #기술기업
조회수 4838

신입 개발자를 위한 코드의 정석

Overview대학을 수석으로 졸업했지만, 정작 회사에서는 A부터 Z까지 제대로 할 줄 아는 게 없었습니다. 실수를 남발할 때마다 느꼈던 좌절감은 아직도 생생하지만 되돌아보면 그때의 삽질이 소중한 피와 살이 되었지요. 오늘은 헤매고 있는 신입 개발자를 위한 글을 쓰려고 합니다. 신입 개발자, 당신! 내 이야기를 편하게 듣고 가지 않으실래요? 남을 위한 코드, 클-린 코드“너랑 같이 일하는 사람은 어떻게 보라는 거야?” “한 명이 짠 코드인데, 어째 한 사람이 짠 것 같지가 않다..” “이게 네 스타일이냐?” 대학생이었을 땐, 대부분 혼자서 프로젝트를 진행했습니다. 다른 사람이 제 코드를 보는 일도 거의 없어서 띄어쓰기나 들여쓰기 등에 통일이 없었고, 함수의 네이밍도 전혀 고려하지 않았습니다. 이게 나쁜 습관이었다는 걸 입사하고 알게 되었죠. 이 습관을 고치려고 코딩 컨벤션(coding convention)을 지키는 것에 꽤 오랜 시간을 들여야만 했습니다. 우리는 협업을 하는 사람들입니다. 사람들이 더러운 방보다 깨끗한 방을 좋아하는 것처럼, 당신과 협업하는 개발자도 보기 어려운 코드보다 깨끗한 코드를 더 좋아합니다. 클린 코드를 작성하기 위한 세 가지 기본 원칙을 잠시 소개합니다. 클린 코드를 위한 세 가지 기본 원칙 코드를 최초로 작성한 사람이 끝까지 유지보수를 한다는 보장은 없다.이미 잘 정리된 코드는 효율성이 증가한다. 정리할 시간에 코드 한 줄을 더 분석할 수 있으니까!리팩토링은 미루었다가 한꺼번에 하는 것이 아니다. 코드를 작성하는 매순간 함께 하는 활동이다.작은 것 하나부터 습관을 들여보세요. 분명 깔끔하고 보기 좋은 코드를 만드실 수 있을 겁니다. 머지 않아 “남을 위한 코드는 곧 나를 위한 코드”라는 것도 알게 될 거고요. 책의 한 구절이 떠오르네요. “우리는 저자이다. 저자에게는 독자가 있다. 그리고 저자에게는 독자와 잘 소통해야할 책임이 있다.”⌈Clean Code⌋의 저자, Robert C. Martin 설마가 사람 잡는다, 철저한 검증만약 누군가 검증 단계에서 잊지 말아야할 것이 뭔지 묻는다면 이렇게 대답하고 싶습니다. “절대 사용자가 입력한 값을 신뢰하지 말라. 프론트엔드에서도, 백엔드에서도.” 모든 사용자가 각 항목에 맞게 올바른 정보만 입력해준다면 얼마나 좋을까요? 세상에는 다양한 사용자가 있습니다. 너무 바빠서 얼른 회원가입을 해야하는 사용자는 항목을 채우지도 않고 신청 버튼을 누를 수도 있습니다. 영어로 입력해야 하는 항목엔 한글을 입력한 사용자도 있을 겁니다. 이런 휴먼 에러(human error)뿐만 아니라 의도적으로 비정상적인 요청을 시도하는 사용자도 분명 있습니다. 이 때문에 개발자는 기능에 대해 항상 검증해야 합니다. 바로 이렇게요!그런데 프론트엔드에서 유효성 검사를 하면, 백엔드에는 유효한 값만 넘어온다고 보장할 수 없습니다. 자바스크립트는 브라우저 엔진에 따라 다르게 동작할 수도 있습니다. 또 자바스크립트에서 다루는 값들은 크롬의 개발자도구(option + command + i)를 이용하면 얼마든지 값을 변조하거나 검증을 회피할 수 있습니다. 또 불온한 시도가 아니더라도 다양한 예외 케이스들이 존재하기 때문에 백엔드에서도 무조건 검증해야 합니다.페이스북 페이지의 개발자 도구를 열었을 때 노출되는 화면입니다. 얼마나 나쁜 사람들이 많으면 경고화면까지 만들었을까요?누군가 질문할 수도 있겠군요. “프론트엔드의 검증이 의미가 없다면, 백엔드에서만 검증을 해도 되지 않을까요?” 네, 아닙니다.(단호) 많은 양의 일을 한꺼번에 하는게 힘든 것처럼 무분별한 요청이 서버에 쏟아지면 서버도 사람처럼 지치고 말 겁니다. 응답이 느려지는 등의 문제가 생길 수도 있고, 결국 사용자가 불편해질 것입니다. 그러므로 가장 좋은 검증 방식을 3단계로 정리하면 아래와 같습니다. 고수가 되는 검증 방식 3단계프론트엔드에서 먼저 값 검증을 하여 빠른 사용자 경험을 제공한다.백엔드에서 다시 한 번 더 검증을 거쳐 상황에 적절한 응답 코드를 내려준다.프론트엔드는 상황에 맞게 적절한 UX와 메시지를 보낸다. 동작 그만! 정리는 하고 코딩하자!예전에는 요구사항이 있으면 바로 키보드 위에 손부터 올렸습니다만, 그건 좋은 태도가 아니었습니다. 팀장님이 “이 경우엔 어떻게 하지?”라고 질문하면 아무 대답도 하지 못했기 때문이죠. 팀장님은 늘 “항상 먼저 생각하고 코딩하자!“라고 조언하십니다. 맞습니다. 최대한 모든 경우의 수를 머릿속에 정리하고 코딩을 시작해야 합니다. 시간이 없다는 핑계로 무작정 시작하면 분명 문제가 발생합니다. 또 구현할 기능만 몰두하지 말고, ‘이 기능이 다른 기능에 영향을 미칠 수 있을까?’를 고민하면 훨씬 좋은 코드를 만들 수 있을 겁니다. “이런 거 다 생각하고 짜면 도대체 코딩은 언제 하라고?” “얼른 선임 분들에게 코딩하는 내 모습을 보여줘야 하는데!” “당장 코드 안 짜고 있으면 노는 것처럼 보이지 않을까?” 혹시 이런 생각을 하고 계신가요? 나도 그런 생각을 했던 적이 있습니다. 하지만 요구사항을 확실하게 정리한 후, 코드를 짜는 게 더 효율적입니다. (그렇지 않으면.. ‘수정’이란 이름 아래 모든 것을 뒤엎고 처음부터 다시 시작해야할 수도 있습니다.) ‘더 나은 개발자가 되는 8가지 방법(8 Ways to Become a Better Coder)’이란 글에는 이런 내용이 있습니다. “동작하는 코드는 끝이 아니라 시작이다.” 신입 개발자는 종종 요구사항에 따라 동작하는 코드만 짜면 된다고 여길 때가 있습니다. 물론 사회생활에 적응하느라 정신 없는 와중에 그나마 자신의 코드가 요구사항대로 동작하면 무척 뿌듯할 겁니다. 하지만 동작만 한다고 절대 지나치지 말아주세요. 위에서 언급한 것처럼 깨끗한 코드가 되도록 리팩토링을 하고, 검증하고, 동작이 제대로 되는 것인지 의심하면서 꾸준히 노력해야 합니다. 마지막으로 항상 중요하게 생각하는 문장 하나를 남기고 글을 마치겠습니다.“진정으로 훌륭한 프로그래머는 적극적으로 어디가 잘못되었지를 찾는다. 자기가 놓친 결함은 결국 ‘사용자’가 발견하게 된다는 것을 알고 있기 때문이다.” Esther SchindlerConclusion지금까지 다룬 내용은 결국 같은 맥락입니다. 모든 개발조직은 좋은 품질의 소프트웨어를 개발할 줄 아는 개발자, 협업할 줄 아는 개발자를 원합니다. 누군가 “당신은 잘 지키고 있는가”라고 질문한다면, “저 또한 노력하고 있습니다.”라고 답변하고 싶습니다. 같은 자리에 머무르는 개발자가 될 것인지, 부족함을 알고 항상 배우고 나아가는 개발자가 될 것인지는 스스로의 몫이라고 생각합니다. 이 글을 끝까지 읽은 신입 개발자 당신! 같이 노력하지 않으실래요? :-) 참고 Robert C. Martin, 「Clean Code」, 케이엔피북스(2010)Esther Schindler, 8 Ways to Become a Better Corder, New Relic, 2018.03.02.유석문, 「프로그래머 철학을 만나다 - 소프트웨어를 사랑하는 기술」, 로드북(2014)임백준, 「읽기 좋은 코드가 좋은 코드다」, 한빛미디어(2012)팀장들이 꼽은 신입 PHP 개발자가 가급적 빨리 알았으면 하는 것들프론트에서”만” 유효성 검사가 문제인 경우남을 위한 코드 만들기 - 클린코드글김우경 대리 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1014

국내 IT 산업을 주무르는 첫눈 마피아

업계에서 페이팔 마피아(PayPal mafia)에 대한 이야기를 듣는 건 어렵지 않다. 전세계 IT 산업의 핵심인 실리콘밸리를 장악하고 있는 페이팔 마피아를 탄생시킨 회사는 컨피니티(Confinity)라는 회사다. 이 회사의 이름은 낯설지 몰라도 이 회사가 개발한 페이팔(PayPal)이라는 전자 송금 서비스의 이름은 낯설지 않을 것이다. 1998년 시작된 컨피니티는 페이팔을 만들어 2002년 상장한 뒤 얼마 지나지 않아 이베이(eBay)에 매각된 닷컴 버블 폭풍의 몇 안되는 생존자 중 하나였다. 페이팔 마피아란 페이팔의 초기 임직원들을 일컫는 말로, 이들은 이후에 또 다른 혁신적인 회사들(Tesla Motors, LinkedIn, Palantir Technologies, SpaceX, YouTube, Yelp, Yammer 외 다수)을 계속 만들어 내고 있다. 이들의 모습이 마치 사회 모든 곳에 얽혀 있는 마피아 조직을 연상시킨다는 것에 비유해, 이들을 페이팔 마피아라고 부르기 시작했다. 심지어 포춘지(Fortune)에서는 2007년 이들을 한데 모아 마피아 같은 옷을 입히고 재미있는 사진을 남기기도 했다. 출처 : Fortune Magazine, 2007년 11월페이팔 마피아들의 성공은 절대 우연이 아니라고 생각한다. 이들이 닷컴 버블의 위기를 이겨낼 수 있었던 밑바닥에는 페이팔이라는 회사가 가진 확고한 고유의 문화(culture)가 존재했다. 자신들이 만들어 낸 본연의 문화가 몸에 배인 초기 구성원들이 새로운 회사를 시작하고, 또 다시 새로운 그들만의 확고한 문화를 만들어 가면서 또 다른 페이팔이 만들어 질 가능성이 높아졌다고 할 수 있다. 또한 각자가 가진 강력한 인적 네트워크를 통해 서로 도움을 주고 받으며 새로운 회사를 만들고 함께 성장할 수 있도록 하는 노력을 아끼지 않았다. 실리콘밸리에 또 다른 닷컴 버블이 터진다 하더라도 이들 페이팔 마피아의 생존 가능성은 의심할 여지가 없을 것이다. 정부 시스템이 무너져도 마피아 조직의 견고함은 무너지지 않는 영화 같은 이야기가 실리콘밸리에서 실제로 일어나고 있는 것이다.출처 : jamesaltucher.com한국에서는 이런 사례가 없는지 늘 궁금했다. 얼마 전 알토스벤처스의 한킴 대표님 페이스북에서 ‘네오위즈 마피아'에 대한 이야기를 본적이 있다. 그런데 나는 이 이야기를 읽으면서 네오위즈보다 주목해야 할 회사는 첫눈이 아닌가 하는 생각이 들었다. 네오위즈, 넥슨, NHN 같은 회사보다 훨씬 더 마피아의 밀집도가 높은, 미국의 페이팔을 연상시킬만한 작은 조직이었기 때문이다.첫눈은 2005년 6월 네오위즈에서 분사하며 설립되었던 검색 회사다. 가장 큰 특징은 자사의 내부 DB를 통해 검색 결과를 제공하는 경쟁사들과 차별화하여 인터넷 전체를 검색 대상으로 삼는다는 점이었다. 이후 설립 1년 만에 NHN에 350억원 규모로 매각되었고, NHN의 당시 보도자료에 따르면 임직원은 총 55명이었다. ‘첫눈 마피아’에 대해 정리하려고 마음 먹고 그간 나왔던 첫눈에 대한 기사들을 많이 읽어 봤다. 간간이 첫눈을 페이팔 마피아에 비유했던 기사들도 있었다. 또 마침 우리 회사 렌딧의 홍보 담당인 꼬날이 첫눈의 홍보 담당이었던 덕분에 생생한 이야기를 직접 들을 수 있었다. 정리해 본 첫눈 마피아들은 아래와 같다 (존칭 생략):장병규 : 지난 9월 25일 4차산업혁명위원장에 임명되어 국가적 화제의 인물로 떠오른 장병규 블루홀스튜디오 의장이 첫눈의 창업자. 2005년 6월 네오위즈에서 검색엔진 개발팀 인력들을 이끌고 분사해 첫눈을 창업했다. 2006년 6월 첫눈 설립 1년 만에 NHN 과 350억원 규모로 매각. 이렇게 NHN에 들어간 첫눈의 인재들이 주축이 되어 개발한 서비스가 라인(LINE)이다. 장병규 대표는 첫눈의 NHN 인수 후 초기 기업에 전문적으로 투자하는 벤처캐피털 본엔젤스벤처파트너스를 설립해 우수한 창업자와 스타트업을 발굴하고 지원하는 일에 힘써왔다. 2007년에는 게임개발사인 블루홀스튜디오를 창업해 현재까지 의장직을 맡고 있다.신중호 : 신중호 라인 CGO(Chief Global Officer)는 첫눈의 CTO였다. 2005년에 첫눈 창업 시 장병규 대표와 함께 네오위즈에서 나왔다. 2006년 NHN 인수합병 시 NHN에 합류, NHN 재팬의 검색서비스를 책임지고 일본에 건너가 있던 중 라인을 개발했다. 일본과 동남아시아 여러나라에서 현지화에 성공, 2016년 7월 라인의 나스닥 상장을 견인했다. 최근에는 WAVE, Clova 등 네이버의 AI 사업을 총괄하며 미래를 이끌어 가고 있다.이상호 : SK텔레콤 AI사업단장 역시 첫눈 출신이다. 자연언어처리와 음성합성, 음성검색 분야의 국내 최고 권위자로 알려졌다. 첫눈에 합류하기 전에는 LG전자 연구원을 거쳐 서울산업기술대학 교수를 지냈다. 당시 이상호 교수를 첫눈에 영입하기 위해 장병규 대표가 오랜 시간 공을 들인 것으로 알려졌다. 이상호 단장 역시 첫눈의 NHN 인수합병으로 NHN에 합류한 후 음성 검색 서비스 등 검색 개발에 집중하다, 2011년 다이알로이드라는 음성 인식 개발사를 창업했다. 국내 최고의 음성 검색 전문가 4인으로 구성되었던 다이알로이드는 2012년 9월 국내 최초로 음성으로 문자를 전송하는 앱 ‘다이알로이드'를 선보였다 (관련 내용 : http://limwonki.com/536). 이상호 대표는 2012년 말 다음이 다이알로이드를 인수하며 다음을 거쳐 카카오에서 음성 검색 연구를 지속했으며, 이후 SK플래닛에 입사, CTO 로 기술을 책임지다 2017년 4월부터 SKT AI 사업단장을 맡아 누구 (NUGU) 등 AI 부문 사업을 총괄하고 있다.노정석 : 잘 알려지지 않은 사실이지만 노정석 리얼리티 리플렉션 대표 역시 첫눈 출신이다. 첫눈 창업 초기 약 4개월 간 글로벌 사업 담당으로 일하다, 2005년 9월에 블로그 개발사인 태터앤컴퍼니를 창업했다. 노정석 대표는 1996년 카이스트-포항공대 해킹 전쟁의 주인공으로 유명하다. 이후 1997년 선배들과 창업한 보안회사 인젠이 2002년 코스닥 상장에 성공. 2005년에 설립한 태터앤컴퍼니는 2007년 9월 구글이 인수하며 국내 스타트업으로는 드물은 글로벌 M&A 성공 사례로 기록 되었다. 이후 구글에서 2년여 간 프로덕트 매니저로 일하다 2010년에 설립한 파이브락스(설립 시 사명은 아블라컴퍼니)가 2014년 8월 다시 미국의 모바일 광고 플랫폼 회사인 탭조이에 매각되며, 국내 스타트업에서 드물은 글로벌 M&A 성공 사례를 다시 남긴 주인공이 되었다. 2015년 5번째 회사인 리얼리티 리플렉션을 창업해 국내 대표적인 ‘연쇄 창업가'로 불리운다. 창업과 더불어 엔젤투자자로서 좋은 창업가들을 발굴하고 후배 창업가들과 함께 호흡하는 것을 좋아한다. 티몬, 비트파인더, 미미박스, 다이알로이드, 다노, 다이알로이드(다음이 인수), 파프리카랩(일본 그리 인수), 울트라캡숑(카카오 인수) 등에 투자했다. 나 역시 2011년 미국에서 창업했던 두번째 회사인 스타일세즈 창업 때 노정석 대표의 엔젤투자를 받았다.그 외 2011년 모바일 메신저 ‘틱톡'을 개발해 카카오톡의 강력한 경쟁자로 부상했던 매드스마트의 김창하 대표 역시 첫눈 개발자 출신이다. 김창하 대표는 2012년 매드스마트를 SK플래닛에 매각하며 SK플래닛에서 일하다, 현재는 라인에 합류해 신중호 CGO와 함께 일하고 있는 것으로 알려져 있다. 라인의 박의빈 CTO 역시 첫눈 출신으로 오랜 기간 신중호 CGO 와 함께 하고 있는 핵심 인물이다. 천재 개발자로 유명한 보이저엑스 남세동 대표 역시 장병규 대표와 오랫동안 함께 한 개발자다. 19살에 네오위즈 인턴으로 시작해서 세계 최초의 웹기반 채팅 서비스인 ‘세이클럽 채팅' 개발을 주도하였고, 이후 첫눈을 거쳐 라인에서 일했다. 카카오의 AI 사업을 총괄하고 있는 김병학 부사장 역시 첫눈 출신이다.    대기업과 스타트업을 오가며 활약 중인 첫눈의 인재들도 있다. 이은정 라인플러스 이사는 베인앤컴퍼니 등에서 컨설턴트로 일하던 중 장병규 대표에게 스카우트되어 첫눈에 입사. 첫눈이 NHN에 인수된 후 현대카드, GS홈쇼핑, 삼성카드 등 대기업에서 승승장구 하던 중 2014년 라인플러스에 입사해 글로벌 사업의 중추 역할을 하며 다시 신중호 CGO와 일하고 있다. 엘지생활건강에서 모바일 플랫폼 혁신을 주도하고 있는 권도혁 상무 역시 첫눈 출신이다. 첫눈 합류 전 베인앤컴퍼니와 NHN에서 경력을 쌓았던 권도혁 상무는, 첫눈이 NHN에 인수된 후 다시 전직장 NHN에 합류하지 않고 스타트업인 큐박스에 합류했다. 이후 2011년 ‘울트라캡숑' 이라는 재미있는 이름의 스타트업을 창업, 대학생들의 익명 커뮤니티인 ‘클래스메이트', 소셜미디어 서비스 ‘너 말고 니 친구', ‘마티니', 다이어트 앱 ‘다이어터' 등을 개발해 서비스 하던 중 2014년 카카오에 매각하고 엘지생활건강에 합류했다.   스타트업 홍보를 열심히 하고 있는 우리 회사 꼬날도 첫눈 출신이다. 첫눈 (NHN 매각) - 태터앤컴퍼니 (구글 매각) - 엔써즈 (KT 매각, 이후 닐슨에 재매각됨) - 파이브락스 (탭조이 매각) 등 성공적인 엑시트로 평가되는 스타트업에 연이어 렌딧에 합류. 스타트업 홍보의 미다스 손으로 불리는 국내 유일무이한 이력의 홍보전문가다.첫눈이 NHN에 인수되면서 첫눈의 새로운 검색 정책과 혁신적인 서비스에 기대를 많이 했던 초기 사용자들이 '첫눈이 녹아 버렸다'며 아쉬워했었다고 한다. 하지만 첫눈은 매각 당시 보도했던대로 NHN과 함께 글로벌 서비스 개발에 힘썼고, 메신저 서비스 라인을 만들어 글로벌 시장 진출에 성공했다. 또한 다양한 첫눈 마피아들이 여전히 창업 전선에서 맹활약 중이다. 내가 창업에 뛰어든지 이제 만 12년이 되었고 세번째 회사인 P2P금융 렌딧을 시작한지 2년 반이 지났다. 렌딧은 기술 혁신을 통한 금융 서비스의 효율화라는 미션을 갖고 시작되었다. 혁신적인 서비스를 통해 우리 삶에 긍정적인 영향을 주는 것만큼이나 내게 강한 동기가 되는 것은 함께 일하는 사람들과의 동반 성장이다. 렌딧의 성장 뿐만 아니라 우리 고유의 문화가 몸에 배인 렌딧맨들이 미래에 또 다른 곳에서 새로운 혁신을 만들어 낼 수 있는 강력한 렌딧 마피아가 형성되기를 기대해본다.지난 5월27일, 렌딧의 SeriesB 투자가 확정되던 날 모든 렌딧맨과 함께
조회수 17492

Nodejs 기반의 개발 환경 클린하게 재 구성하기

다양한 언어 기반으로 개발 환경을 구축하여 만들다보면, 소프트웨어 버전관리 해야할 일이 흔히 생기곤 한다. 특히, 종종 대격변이 있는 버전의 판올림으로인해 충돌이 나거나 심볼릭 링크가 유실되는 경우들이 간혹 있는데 이번에도 그런 케이스였다.최근 node.js 기반으로 다양한 프로젝트 (vue.js, react.js등)를 진행하다가 이것저것 환경을 만지고 고치다보니 결국 node.js 를 완전히 클린하게 삭제해야 할 일이 생겼다.아마 이 환경에 결정타를 먹인 것이 OSX 환경에서 El Capitan에서 작업하던 Node.js를 그대로 high sierra로 OSX를 판올림 하면서 퍼미션 권한의 문제가 생긴건지, 노드 패키지 관리나 npm이 정상적으로 동작하지 않으면서 개발환경을 재 설정 할 수 밖에 없게 되었는데, 그 과정에 기름을 부어버리듯 당시에 brew로 설치한 노드가 brew로 삭제가 되지 않는 문제가 발생해버렸다.결국 환경을 처음부터 재 설치 해야하는 과정을 겪어야했는데 기존에 설치된 다양한 패키지 모듈의 찌꺼기들이 남아서 한방에 클린 설치를 할 수 있는 방법이 없을까 싶어 구글링을 해본 결과 앞서서 수많은 시행착오를 겪은 선배님들의 아주 좋은 작업 방식이 있어서 아래에 방법을 공유해본다.요세미티에서 nodejs 정리하는 법 [1]Uninstall nodejs from OSX Yosemite# 첫번째:lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom | while read f; do  sudo rm /usr/local/${f}; donesudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.*# 완전히 nodejs + npm 을 날려버리는 방법 :# /usr/local/lib 경로로 가서 node 와 관련된 노드 모듈을 전부 삭제cd /usr/local/libsudo rm -rf node*# /usr/local/include 경로로 가서 node 와 관련된 노드 모듈 전부 삭제cd /usr/local/includesudo rm -rf node*# 만약 brew 로 인스톨을 했다면 아래와 같은 방법으로 삭제도 가능함. (저는 아래는 brew자체가 망가졌었는지 판올림으로 인한 권한 문제인지 brew로는 삭제 불가능했음.)brew uninstall node# home 디렉토리나 local, lib, include등의 폴더와 관련된 모든 파일은 아래의 경로에 있으니 찾아 들어가서 삭제cd /usr/local/binsudo rm -rf /usr/local/bin/npmsudo rm -rf /usr/local/bin/nodels -las# 아마 혹시 모르니까 클린하게 아래의 명령어도 한번 돌려주자sudo rm -rf /usr/local/share/man/man1/node.1sudo rm -rf /usr/local/lib/dtrace/node.dsudo rm -rf ~/.npmhomebrew를 사용하는 유저들 중에 npm이 제대로 동작하지 않으면 아래와 같은 방법으로도 처방이 가능하다. [2]rm -rf /usr/local/lib/node_modulesbrew uninstall nodebrew install node --without-npmecho prefix=~/.npm-packages >> ~/.npmrccurl -L https://www.npmjs.com/install.sh | sh클린하게 설치를 끝나고 react-native를 컴파일하는 과정에서 깃에 관련된 오류가 발생한다면 아래의 방법을 사용해보자. [3]오류메세지 :xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun솔루션 :xcode-select --install엘케피탄에서 하이시에라로 osx를 업데이트 하면서 homebrew의 링크가 깨졌다면 아래의 방법으로 다시 붙여준다. [4]sudo chown -R "$USER":admin /usr/localsudo chown -R "$USER":admin /Library/Caches/Homebrewbrew link libpng참고 출처 :[1] : https://gist.github.com/TonyMtz/d75101d9bdf764c890ef[2] : https://stackoverflow.com/questions/32893412/command-line-tools-not-working-os-x-el-capitan-macos-sierra-macos-high-sierra[3] : https://stackoverflow.com/questions/39778607/error-running-react-native-app-from-terminal-ios[4] : https://github.com/mikepurvis/ros-install-osx/issues/28 #더팀스 #THETEAMS #풀스택개발자 #Node.js #백엔드 #인사이트 #꿀팁
조회수 1601

냉정과 열정 사이

냉정과 열정 사이라는 소설이 있다. 대학교 때 읽었던 소설인데 두 사람의 여정을 각자의 시선에서 다룬 소설이다. 에잇퍼센트에 인턴으로 입사해 9개월간 일하고 훨훨 날아간 병훈님과 나도 이 소설처럼 각자의 시선에서 지난 9개월을 되돌아보려 한다. (경고한다. 로맨틱하지 않다.)병훈님이 떠나는 날. 아마 여러분이 보는것과 내가 이 사진을 보는 느낌이 많이 다를거다.1. 만나기까지- 소병훈 이야기2015년 대학교 3학년이 시작될 때부터 졸업 이후에 대한 고민이 생겨났다. 대학원 진학과 취직은 수많은 대학생들의 공통된 고민이기에 수많은 조언이 넘쳐나지만 결론은 '나에게 맞는 길'을 선택하는 것이다. 내 인생 내가 선택해야지 언제까지 남들 좋다는 길로만 가겠는가. 둘 다 겪어보고 내가 선택하겠다고 다짐했다.졸업을 위해서는 대학원에서 과제연구를 1년 해야 했기에 대학원은 겪어 볼 수 있었다. 그러면 취직도 경험해보고 싶은데 어떻게 하지? 대기업에서 1~2개월 인턴을 했던 친구들에게 물어보니 한결같이 '놀고먹다 보니 월급이 나온다'는 경험담을 늘어놓았다. 하지만 정말로 취직해서 놀고먹으면 잘리겠지. 대기업 인턴은 패스. 스타트업 관련 세미나에서 한 VC의 '스타트업은 망해도 스타트업 인턴은 망하지 않는다'는 말을 들었다. 창업에 생각이 있으면 스타트업에서 인턴을 해보라는 말이었다. 그래서 '스타트업에서 일해보자'라고 결정했다.수많은 스타트업 중에서 왜 에잇퍼센트를 선택했다고 물으신다면, 빠른 속도로 성장하면서 변하고 있는 스타트업 속에서 일해보기를 원했기 때문이다. 그리고 당시의 나는 CTO의 멋진 말 한마디에 눈을 반짝이며 '이 회사에서 이 사람과 일하고 싶다'라고 생각하면 앞뒤 안 가리고 지원하는 이상주의자였다. 그래서 페이스북에서 호성님의 글을 읽고 '이 회사가 내가 생각하던 회사구나'라는 생각이 들어서 먼저 지원했던 회사를 포기하고, 에잇퍼센트 입사를 간절히(?) 원하게 되었다.- 이호성 이야기2016년 1월의 첫 번째 근무 날. 대표님이 모두를 불러 모았다. 그리고는 회사의 투자 유치 소식을 알려 주었다. (무슨 투자 유치 소식을 "오늘 저녁에는 치킨을 시켜 먹기로 했어요." 수준으로 재미없게 이야기하는지 모르겠다.) 투자를 받는 것이 확정되었으니 대표님이 내게 전달해 주신 미션은 개발자를 채용해서 제품 개발의 속도를 높이라는 것이었다. 사실 에잇퍼센트에 오기 전에 한 회사에만 오래 있기도 했고 개발자들과의 네트워킹도 게을리했던 터라 당장 좋은 개발자를 뽑을 수 있는 방법이 없었다.그래서 블로그에 회사를 알리는 글을 쓰기 시작하면서 주위 분들께 추천을 부탁드렸다. 그중 JDLab의 양주동 대표님이 괜찮은 학교 후배를 추천해 주신다고 해서 속으로 쾌재를 불렀다. 하지만 추천해 주신 친구가 애매하게 9개월만 일할 수 있는 상황이라고 하니 고민이 되었다. 주니어가 실제로 일을 잘 하게 되려면 꽤 긴 시간이 필요한데, 실제로 일을 잘할 수 있게 되었을 때쯤 회사를 떠나는 게 아닐까 하는 생각이 들었기 때문이다. 하지만 인력(당시 4명)에 비해 해야할 일이 너무나도 많았기에 누군가의 손이라도 빌려야 할 판이었다. 그래서 일단 병훈님을 만나 보기로 했다.2. 면접- 소병훈 이야기에잇퍼센트에 들어가는 과정은 상당히 길다.처음에 간단한 티타임을 시작으로 실제 코딩 문제를 풀어보게 하고, 그 뒤에서 다시 1대 n으로 토론하는 과정, 그리고 대표님과의 이야기로 면접이 이어진다. 요즘은 논술 문제도 있다고 들었다. (역시 취직은 어려워)내 경우는 '면접 보려는 것은 아니니 그냥 커피 한잔 하자'는 부님의 간단한 속임수에 넘어가 티타임을 가졌다. 카페에서 커피 한잔 하면서 부님과 에잇퍼센트는 어떠냐고 물어보려고 왔는데, 어느새 내 앞에는 호성님이 앉아 있었고, 메일로 코딩 문제를 받는 것으로 커피 한잔이 끝났다. 이 티타임은 면접보다는 나에게 회사를 소개하고 회사가 나에게 적합한지 보는 과정이었다.코딩 문제는 성호님의 글로 유명해진 pingpong을 포함한 take-home 과제였다. 문제를 받은 다음날 다른 회사 면접을 보고 온 뒤 밤샘으로 문제를 풀었던 것과 제출할 때 pingpong 문제만큼은 자신 있어했던 기억이 난다. 그때의 기억을 떠올리며 당시에 제출했던 코드를 보니 'Assignment를 쓰지 말 것'이라는 조건이 깨져있었다. 자신감 넘치던 과거의 내가 부끄러워지는 순간이다.마지막 면접 과정도 조금은 숨 막히는 경험이었다. 가볍게 대화하는 분위기 속에서 대학에서 들었던 전공과목 별로 하나하나 물어가며 내 지식의 바닥을 확인했다. 대학에서 3년간 들었던 전공과목은 많지만, 질문 들어오는 족족 '모르겠습니다' 밖에 할 수 없었다. 내가 답할 수 있는 수준을 찾으시려는지 점점 질문의 난이도가 낮아졌고, 마지막으로 스택과 큐를 물어보는 질문에 답하면서 '이 회사는 못 들어가겠구나'라는 생각이 들었다.동시에 진행했던 다른 회사에서 합격 메일이 왔기에 에잇퍼센트에 '0월 0일까지 합격/불합격 결과를 알려주세요'라는 당당한 요구를 한 뒤 떨어졌다는 메일을 기다리고 있었다. 하지만 예상치 못한 합격 메일을 받았고, 그 메일에는실력에 대해서는 회사에 오셔서 보여주세요라는 잊지 못할 문구가 있었다.그리고 첫 출근, 4월 4일 9시 20분에 출근해서 잠긴 문을 보며 에잇퍼센트의 첫 날을 맞이했다.- 이호성 이야기병훈님이 왔다고 하셔서 학교 선배인 부님과 함께 회사 옆 '피어나' 카페로 갔다. (당시만 해도 사무실에 회의실이 없어서 모든 미팅을 회사 옆 카페에서 해야만 했다.) 병훈님의 첫인상은 “꺼벙이"였다.공대에서 흔히 볼 수 있는 스타일. 하지만 말하고 이야기하는 것은 번뜩이는 느낌은 크게 들지 않았다. 아마 나정도로 평범한 느낌이었던 것 같다. 이야기를 하다 보니 다른 곳에 면접을 이미 본 상태였다. 일단 우리 회사와 나에 대해서 좋은 감정을 갖게 하는 것이 좋겠다 싶어서 이래저래 약을 팔았다. 그리고 면접 문제를 메일로 전달하겠다고 하고 첫 번째 만남을 마쳤다.며칠 뒤 제출한 과제를 가지고 다시 한번 병훈님을 만났다. 전공에 관련된 기본적인 질문들을 던졌다.(정확히는 졸업한 지 10년이 지나서 그냥 내가 기억나는 것들을 물어보았다.) 그런데 10개의 질문을 던지면 8개의 질문 은 원하는 답을 듣지 못했다. 실망했다. 겸손하고 배움의 자세가 갖춰져 있는 친구라는 생각은 들었다. 하지만 이렇게 모르는 친구를 뽑아도 될까 하는 생각이 들었다.하지만 뽑았다. 솔직히 그냥 학벌을 보고 뽑았다. 좋은 학교에 다니고 있는 친구이니 지금까지 최소한 한 번쯤은 최선을 다해 본 적이 있겠지 라는 생각을 했다. 무엇보다 당시 나는 꽤 급했다.합격 메일에는 ‘실력에 대해서는 회사에 오셔서 보여주세요'라는 내용을 적어서 보냈다. 부족한 만큼 회사에 와서 최선을 다해주었으면 하는 마음이었다. 그리고 입사할 주에 있을 워크숍 준비에 대한 요청도 함께 드렸다.지금 와서 이야기하는 것이지만 최근에 병훈님을 면접 봤다면 떨어뜨렸을 거다. 생각해 보면 이게 면접, 특히 주니어 면접의 어려움이다. 그 사람이 입사해서의 2주 정도는 예상해 볼 수 있지만 그 뒤는 예상하는 것은 너무나 어려운 일이다.3. 들어와서 처음 했던 일- 소병훈 이야기들어와서 처음으로 했던 일은 나를 대학생에서 직장인으로 바꾸는 일이었다.회사에 들어온 지 1~2개월이 지났을 때 외부 업체와 전문 통신을 개발하는 작업을 맡았다. 대학교에서 두 PC 사이의 전문 통신 프로젝트를 만들었던 기억이 있어 충분히 혼자서(그리고 짧은 기간에) 만들 수 있으리라 생각하고 작업을 시작했다. 기존의 코드를 조금씩 수정하고 추가하던 이전의 작업들과는 다르게 처음부터 만들어내는 일이었다.이 일을 하면서 지금까지 '하나의 동작을 하는 무언가'를 100% 혼자서 만든 적이 없다는 것을 깨달았다. 항상 기본 틀을 받아서 코딩하고, 어려울 때는 모범 답안을 보면서 힌트를 얻었으며, 그러고도 힘이 부치면 7~80%만 완성하고 (시간이 없었다는 핑계를 대면서) 넘어갔었다. 회사에서는 이 일이 '소켓 통신의 이해를 확인하기 위한 프로그래밍'이라고 설명되어 있지 않았고, 어디서 버그가 발생했는지 힌트를 얻을 수 없었다.대학 강의로 들었던 내용들과 전혀 다른 지식들이 필요했지만, 필요한 기초적인 요소들은 구글에서 찾을 수 있었다. 하지만 어떤 키워드를 검색해야 하는지부터가 문제였다. 검색해야 하는 단어를 알아내려고 시니어 개발자님들께 돌아가면서 물어봤다. (너무 자주 물어야 해서 한 분에게만 묻기 죄송했다.) 처음에는 학교에서 배운 내용은 쓸모없다고 생각했었는데, 지금 생각해보니 그마저도 없었으면 구글과 위키의 내용도 이해 못했을 것 같다.웹 개발에 대한 기초도 없고, 어디가 끝인지 확신도 없어서 개발 시간이 길어졌다. 야근을 반복했다. 노력한다고 해서 없던 능력이 생기지는 않았고, 결과로 커다란 똥덩어리 같은 코드가 만들어졌다. 다행히도 (달리는 중간에 몇 개의 부품을 갈아 끼운 이후에) 최소한의 기능은 정상적으로 돌아갔다.그렇지만 이 코드가 12월까지 구린 냄새를 피우고 있었다. 에러를 만들지는 않지만 가독성이 떨어지고 창의적인 구조 때문에, 유지/보수를 할 때마다 과거의 내 실력을 확인하는 좋은 지표가 되었다.- 이호성 이야기시간이 많다면 병훈님을 옆에 앉혀 두고 차근차근 알려주고도 싶고, 같이 스터디도 하고 싶었지만 내게는 당장 해야 하는 일이 쌓여 있었다. 다행히 팀에 계신 시니어 개발자 분들이 병훈님일 이래 저래 잘 돌봐 주었다. (20살이 넘는 청년에게 "돌봐 주었다"라는 표현이 적당한 가에 대해서 곰곰 생각해 보았는데, 흠. 역시 적절하다.) 병훈님께 처음 한 달 동안은 조각을 고치는 일, 작지만 급한 일 들을 맡겼다. 덕분에 시니어 개발자들이 다른 일들에 집중을 할 수 있었다. 그리고는 한 달이 지나자 하나의 일을 떼어서 맡겨볼 수 있겠다는 생각이 들었다. 단순히 개발하는 일뿐만 아니라 다른 회사 개발자들과 커뮤니케이션을 직접 하면서 일을 할 수 있도록 했다. 병훈 님은 잊어버렸을지는 모르겠지만 외부 업체로 처음 전화를 걸었을 때 우리 팀의 시니어 개발자들은 모두들 키보드에서 손을 놓고 병훈님의 대화를 노심초사하면서 듣고 있었다.이 프로젝트는 곧 병훈님이 예상한 일정을 넘어섰고, 얼마 이후에는 내가 예상한 일정도 넘어섰다. 병훈님이 끙끙 앓고 있는 게 보였다. 그 일을 다른 사람에게 넘겨야 하는가의 고민도 여러 번 했다. 병훈님이 만들어 낸 창의적(이라고 말하고 싶겠지만 상식을 벗어난)인 코드들을 뜯어고치고 싶다는 생각도 했다. 하지만 시간은 지났고 테스트를 통과한 코드는 에잇퍼센트 프로덕트의 한 자리를 차지했다.4. 무엇을 배웠을까?- 소병훈 이야기첫 번째로 회사가 어떻게 돌아가는 지를 배웠다. 작은(?) 스타트업이었기에 개발팀 외 다른 팀원들과도 친하게 지낼 수 있었고, 회사 내에서 생기는 사건들을 전부 들을 수 있었다. 그렇게 아이디어가 나오는 순간부터 제품으로 완성되는 과정을 볼 수 있었다. 또한 회사의 크고 작은 의사 결정 과정을 지켜볼 수 있었는데, 모든 의사 결정들에 원인과 논리적인 과정이 따른다는 점이 재밌었다.내가 알지 못하는 원인들과 다른 사람들이 결정하게 된 이유가 궁금해서 여기저기 물어보았고, 모두들 숨기지 않고 말해 주었다. 대표님과의 티타임에 찾아가서 묻기도 하고(모두에게 열려있었는데 단 2명이 왔었다), 퍼포먼스 마케팅이 궁금하다며 점심시간에 옆에 앉아 이야기하고, 전화 응대를 어깨너머로 들어보기 했다. 글로 적어보니 처음 초등학교에 들어간 8살 아이 같기도 하지만, 에잇퍼센트에 있으면서 물어보는 만큼 알 수 있었고, 그만큼 이전과는 다른 세상을 알 수 있었다. (그리고 그만큼 야근을 했다.)두 번째는 개발자가 되는 과정을 배웠다. 당연히 개발 실력도 늘었지만, 조금 더 보태서 개발자가 되는 과정을 배웠다고 말하고 싶다. 누구라고 말할 것 없이 남는 시간을 조금씩 쪼개 공부하고 있는 사람들, 새벽 4시가 넘었음에도 꼼꼼히 기록을 남기며 마무리하는 야간작업, 그리고 혼자서는 만들 수 없는 거대한 코드를 점진적으로 만들어가는 개발팀을 보면서 개발자라는 직업을 만날 수 있었다. 내가 본 개발자는 (에잇퍼센트의 개발자만의 특징일 수도 있지만,) 모든 결과를 '우연'으로 넘기지 않고 원인을 찾았고, 원하는 분야를 찾아서 스스로 공부하고, 삶의 즐거움을 하나씩 가지고 있는 사람들이었다.마지막으로 나 되돌아보기. 나는 내 실력을 과대평가하고 있었다. 회사에 들어오면서 '열심히 하겠다'라고 말했지만 몇 개월 '열심히' 뛰어서 갈 수 있는 거리에는 한계가 있었다. 다른 사람들이 불가능하다고 말해도 혼자서 '노력하면 할 수 있다'라고 생각하면서 오기로 붙잡고 있다가 결국 기한을 넘긴 적이 많았다. 시간이 지나면서 '할 수 있을 것 같다'는 자신감이 아니라 완성된 결과물을 보면서 실력을 확인했다. 항상 자신감이 넘치다 보니 매번 내 생각보다 실력이 뒤에 있었고, 그런 생각이 들 때마다 기숙사에서 공부를 했다.그렇지만 나를 과대평가했던 것처럼 나의 목표도 과대평가 했었다. 내가 도달하려고 했던 목표도 꾸준히 달리면 도달할 수 있는 거리에 있었고, 생각보다 멀리 있지 않았다. 다만 '꾸준히'의 기준이 몇 주, 몇 개월이 아니라는 것을 배웠을 뿐이다.- 이호성 이야기내가 입사하기 전에 에잇퍼센트에 여러 명의 개발 인턴이 있었다고 했다. (commit log에서만 만날 수 있는 그대들이여. 왜 버그를 내게 주고 갔는가.) 그리고 한 명을 제외하고는 회사를 모두 떠났다. 처음에 대표님이 인턴 채용 제안을 몇 번 하셨을 때 개발팀에는 인턴을 채용하지 않겠노라고 말했었다. 사람이 전부인 개발팀에서 떠나는 것이 예정된 사람을 뽑고 싶지 않은 마음이었다. 하지만 병훈님은 이런 내 생각을 바꿔 놓았다.연말 평가에서 성장에 대한 상을 받을 만큼 병훈님의 성장은 눈부셨다. 이제 좋은 주니어는 무엇인가에 대해서 병훈님을 기준으로 생각을 할 수 있게 되었다. 주니어 채용에 대한 성공체험을 했다고도 할 수 있겠다.상은 병훈님이 받는데 주는 사람이 더 좋아하네?좋은 주니어는 당연하게도 일정 시간이 지났을 때 높은 곳에 올라갈 수 있는 사람이다. 높은 곳에 올라가기 위한 조건은 다음과 같은 것들이 있다.1) 상대적으로 이미 높은 곳에 있을 것 전설 속에만 존재하는 시니어 같은 주니어 되시겠다. 고등학교, 대학교 때 많은 지식과 경험을 쌓아서 이미 현업에서 잘할 수 있는 친구들이다.2) 인지능력, 학습능력문제를 이해하고 정의하는 능력이 뛰어나고 논리적인 사고를 한다. 속칭 똑똑한 친구들이다. 문제를 자세하게 설명해 주지 않아도 문제의 본질을 이해할 수 있고, 답으로 가는 길을 빠르게 찾아낼 수 있다. 새로운 것을 빠르게 익히고 배움에 대한 두려움이 없다.3) 지적겸손배움에 대해 열린 자세를 가지고 있어야 한다. 나는 개인적으로 주니어의 경우 이 능력을 "내갈굼력"이라고도 부른다. 다른 사람들에게 지적과 갈굼을 받으면서도 그것이 배움으로 이어진다면 감사한 마음으로 받아들인다. 감사한 마음은 다시 지식을 전해주는 사람에게 긍정적인 피드백이 되어 더 많은 것을 알려주게 되는 선순환 구조를 만들어 간다.4) 태도긍정적이고 도전적인 태도를 갖추어야 한다. 자신의 인생을 발전적으로 개척해 나갈 태도. 그리고 자신을 둘러싼 환경에 감사하는 태도. 이 태도는 팀에도 많은 영향을 미친다.병훈님을 면접 볼 때의 나는 1) 만을 중요하게 생각했기에 병훈님을 떨어뜨려야 하나 하고 생각했다. 하지만 이제 와서 뒤돌아 보니 병훈님은 2), 3), 4)을 모두 갖추고 있는 인재였다. 아마 몇 년 뒤에는 1)도 충분히 갖추게 되리라.5. 어떻게 일했나?- 소병훈 이야기 9개월 동안 에잇퍼센트를 다니면서 항상 내 능력으로 조금 힘들지만 불가능하지 않을 만큼 업무가 들어왔다. 스프린트(2주) 단위로 업무를 나눠가지는데, 일방적으로 업무를 할당받지 않고 팀 회의로 업무를 나눠갖는다. 호성님이 업무를 강요하지도 않고 업무 일정도 각자가 정하지만, 모두가 보고 있다는 느낌과 '스스로를 과대평가하는 나' 때문에 매번 촉박한 일정에 시달리게 되었다. 그렇게 나는 손을 들고 해당 업무의 책임자가 된다. 초반에는(전문 개발할 때까지)는 아예 질문하지 않아서 혼자 끙끙 댔는데, 너무 안쓰러워 보였는지 옆에 앉은 연태님이 먼저 도와주셨다. 시간이 지나면서 길이 명확하게 보이지 않을 때는 시니어 개발자(대부분 연태님)에게 물어보면서 일을 진행했다. 어느 날 호성님이 에잇퍼센트처럼 '실패하면서 성장할 수 있는 환경'이 다른 회사에서는 없다고 말했었다. 생각해보니 내가 자유롭게 개발해도 테스트와 코드 리뷰를 거치면서 문제를 잡아낸다. 그러고도 버그가 생기면 실서버에서 디버깅해서 문제를 해결한다. 심적으로 매우 죄송한 마음이 들지만 추가적으로 다른 벌은 받지 않았다. 일을 시작하기 전부터 지레 겁먹을 필요는 없었다. 그 뒤로 길이 희미하더라도 우선 걸어가 봤다. 그러다가 도저히 길이 보이지 않을 때, 조언을 받는 방식으로 일을 진행했다. 시간이 더 오래 걸리면서 최종 결과물의 수준이 떨어지는 상황이 발생했지만, 코드 리뷰를 받으며 최소한의 수준은 맞춰졌다. (그러면서 시간은 더 오래 걸린다.) 최대한으로 생각해서 만들어도 항상 놓치는 부분이나 더 간단한 해결 방법이 있었고, 그때 느끼는 아쉬움과 안타까움이 다음 개발할 때 잊지 않고 기억나서 내가 성장한다는 느낌이 들었다. 막바지에는 개발을 시작하기 전에 항상 의자를 들고 해당 업무를 요청한 사람 옆으로 갔다. 말로 이야기면 Slack이나 Trello로 이야기하는 것보다 빠르고, 해당 문제를 직접 보면서 자세한 설명도 들을 수 있었다. 요청사항을 받아 개발하는 느낌이 아니고 함께 문제를 해결한다는 느낌으로 이야기하면서 실시간으로 여러 해결방안을 제시하면서 생각을 주고받았다. 문제를 해결하면서 회사에 장기적으로 도움이 되는 방향을 고민하다 보니 '주인의식을 가지고 일한다'는 느낌이 들었다. (정확히는 이왕 만드는 거 아름답게 만든다는 생각이었다.)- 이호성 이야기회사에서 병훈님의 별명은 '아기새'였다. 업무를 하면서도 사람들의 보살핌을 필요로 했지만 그것 외에도 이런저런 허술한 면을 많이 보여줘서 누가 붙였는지 기억이 나진 않지만 모두의 입에 착 붙어 있는 별명이었다. (개발팀 내에서는 간혹 '아. 이런. 손이 많이 가는 친구'로 불리기도 했다.)에잇퍼센트에는 퇴사하면 털린다. 다들 떠나지 마라.병훈님을 연태님 옆자리에 앉게 했다. 회사 내에서 스위퍼(스프린트 내의 개발 잡일들을 처리하는 담당) 팀도 연태님과 같이할 수 있도록 했다. 경험과 인내심이 많고 상냥한 언니 같은 연태님(남자)은 병훈님의 좋은 파트너가 되어 주었다. 그리고 세바님은 어려운 문제를 함께 해결해 주고 코드의 퀄리티에 대한 감시자(갈굼자)가 되어 주었다. 언젠가 병훈님이 개발자의 길을 가게 되어 첫 월급을 받게 되면 이 두 분에게는 빨간 내복을 사드려야 할 거다.처음에는 아기새의 Pull Request(반영하고자 하는 코드 뭉치)에는 코멘트가 수십 개가 달렸다. 그것들을 꾸역꾸역 고치고 나면 다시 그 절반 정도의 코멘트가 달리곤 했다. 하지만 병훈님이 떠날 때쯤에는 내 코드에 "이렇게 저렇게 고치는 게 더 좋은 것 같은데요?"라고 코멘트를 달곤 했으니 발전하지 못한 나는 부끄러울 따름이다.그리고 병훈님은 다른 팀일에도 참 관심이 많았다. 그러고 보면 나도 처음에 스타트업에서 일하기 시작했을 때 다른팀 일들도 왜 그렇게 재미있었는지 모르겠다. 하지만 분명한 것은 작은 조직에서는 다른 팀에 대한 관심이 개발을 잘하는 데 많은 도움이 된다.6. 떠나기 이주일 전- 소병훈 이야기정해졌던 퇴사일이 가까워지면서 새로운 업무는 들어오지 않았다. To-do list는 사라지고, 대신 '인수인계'라는 일이 생겨났다. 지금까지 했던 일들을 문서로 남기면서 새로운 책임자에게 넘겨주는 일이었다. 큰 그림을 그렸던 것들이 있는데 완성을 하지 못한다는 아쉬움이 컸다.호성님께 1,2월 프리랜서 제안서를 받게 된 건 우연이였다. 다 같이 점심을 먹을 때 우연히 호성님과 같은 테이블에 있었고, 1,2월에 남은 일정을 이야기하다 농담처럼 나온 제안이었다.제안서를 받은 날, 기숙사에서 많은 계산을 했다. 개발하는데 어느 정도 시간이 걸릴지, 제안서의 업무 기한을 변경한다면 일정이 어떻게 될지, 그렇게 받은 돈으로 어느 정도 도움이 될지. 충분히 가능한 일정이었다. 못해서 아쉬워하던 일이기도 했다. 그렇기에 더 고민했다.긍정적으로 고민하던 제안을 거절한 이유는 '여행 도중에도 계속 개발을 생각할까 걱정되어서'였다. 이번 여행에서 아쉬움이 남으면 다음은 언제가 될지 모른다는 생각이 들자, 내 시간이 더 귀하다는 생각이 들었다.그러면서 내가 조금이라도 더 필요하다고 말해주는 점이 고마웠다. 내가 생각해보지 못한 선택지를 받아서, 나의 가치관을 되짚어 본 느낌이었다.- 이호성 이야기병훈님과 같이 식사를 했다. 병훈님은 복학하기 전 유럽으로 여행을 다녀온다고 했다. 밤에 돌아다니면 위험하니까 숙소에서 코딩이나 하라고 살살 꼬셨다. 밤에 코딩하고 그 아르바이트비로 낮에 럭셔리하게 맛있는 것 먹고 다니면 얼마나 즐거운 여행이 되겠냐고. 제안서를 하나 작성해서 해야 할 일과 보수를 적어서 병훈님께 주었다. 왠지 넘어올 것만 같은 느낌이었다.병훈님이 하루 정도 생각해 보더니 "어정쩡한 상태가 될 것 같아요. 생각해보니 이런 제안을 주신 것만으로도 정말 감사합니다."라고 했다. 실패했다.회사 입장에서 업무를 잘 알고 있는 병훈님이 조금이라도 일을 더 해주면 하는 마음이었다. 하지만 다시 생각해보니 인생의 후배에게는 좋지 않은 권유였던 것 같다. 돈이 중요할 때가 아니라 세상에 대한 경험과 자신을 뒤돌아 볼 시간이 필요했던 거니깐.7. 떠나는 날케익이나 먹고 떠나랏!- 소병훈 이야기떠나는 날도 크게 다르지 않았다. 코드도 살펴보고 pull request도 적으면서 이전과 같은 하루를 보냈다. 그리고 마지막 날, 혹시 작별 인사를 하면서 내가 울지 않을까 걱정했지만 괜한 걱정이었다. 2달 전부터 작별 인사(라 쓰고 갈굼이라 읽는다)를 받아서 그런지 마지막 인사가 특별한 느낌이 들지 않았다.그렇지만 그 뒤로 며칠간 회사를 나왔다는 묘한 홀가분함과 그동안 했던 일들이 내 손을 떠난 공허함이 있었다. 내가 없으면 회사가 바뀌지 않을까 하는 조그만 기대도 있었지만, 다들 나 없이 잘 지내나 보다. 나는 조금 아쉬웠는데.- 이호성 이야기9개월이라는 시간이 참 금방 지났다. 남은 기간 동안 여행을 떠나는 병훈님에게 사람들이 "에이 그거 여행 가면 뭐해. 그냥 회사에서 일해"와 같은 장난을 수도 없이 했던 것 같다. 하지만 떠날 시간은 정해져 있었고 병훈님은 사람들의 박수를 받으며 떠났다. 마치 80분을 열심히 뛴 축구선수가 교체를 위해 떠날 때 받는 박수처럼.8. 떠나고 난 후- 이호성 이야기며칠 간은 아침 데일리 미팅이 왠지 허전하고, 슬랙으로 말을 걸면 대답을 할 것만 같았다. 하지만 또 새로운 사람이 회사에 들어오고 바쁘게 회사가 돌아가면서 금방 잊혀지긴 하더라. 아 그러고 보니 병훈님이 만든 코드에서 버그가 나올 때마다 우리는 회사에 남은 아기새 인형을 괴롭히긴 했다.병훈님이 떠나고 나서 같은 학교의 후배인 선희님이 회사에 마케팅 인턴으로 들어왔다. 선희님이 자기소개 시간에병훈 선배와 같은 동아리에..라고 말하자마자 전 직원이 다 뒤집어졌다. 그렇다. 우리에게 "병훈"과 "선배"는 함께할 수 없는 단어였다.여행을 갔다가 돌아온 아기새 병훈님이 와인을 하나 물어왔다. 그리고는 파닥파닥.군대 문제가 있기에 당분간 병훈님과 함께 오래 일할 수 있는 기회는 찾아오지 않을 것 같다. 아쉬운 마음이 든다. 에잇퍼센트에서의 병훈님을 "막 알에서 깨어나 호기심을 가지고 세상을 바라보는 아기새"로 기억해야겠다. 그리고 그 모습을 기억하며 나 또한 초심을 되새겨야지.우리가 다시 만날 그날까지 병훈님이 더 큰 날갯짓으로 더 넓은 세상을 여행하길 바란다. 9개월간 함께 해준 병훈님께 감사한다. 안녕!덧, 그나저나 난 또 어디에서 찾아야 하나. 이 다음 아기새를.#8퍼센트 #에잇퍼센트 #인턴 #조직문화 #후기 #팀워크 #팀플레이
조회수 1268

AWS Lambda + API Gateway로 API 만들어보자

Overview좋은 아침입니다. 오늘은 AWS Lambda와 API Gateway 이용하여 API를 만들어보겠습니다. 서버 구축부터 해야 하지만 이번 글에서 서버는 따로 필요 없습니다. 당황하셨나요? 괜찮습니다. 이 글을 보면 곧 이해가 될 겁니다. 우선 Lambda와 API Gateway부터 알아봅시다. Lambda는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있게 해주는 컴퓨팅 서비스입니다. 브랜디 랩스에는 이미 이것을 활용한 예제가 많은데요. 아마 아래의 포스팅들을 보시면 도움이 될 겁니다.SQS + Lambda: 이상근 팀장님CodeStar + Lambda + SAM으로 테스트 환경 구축하기: 천보성 팀장님API 호출부터 결과 확인까지API Gateway는 규모에 상관없이 API 생성, 유지 관리, 모니터링과 보호를 할 수 있게 해주는 서비스입니다. 이 글에서는 API를 호출해 결과를 확인하는 걸 목표로 진행하겠습니다.최종 API 호출 URL* GET /v1/reviews/{review-no}/comments* POST /v1/reviews/{review-no}/comments AWS(Amazon Web Service) 가입 절차는 생략하겠습니다. 1.AWS 로그인 후 API Gateway 시작!AWS에서도 설명되어 있듯이 API gateway엔 이와 같은 장점이 있습니다.1. API 개발 간소화: 새로운 버전을 신속하게 반복하고, 테스트하고, 출시할 수 있습니다.2. 규모에 따른 성능: 백엔드 시스템에 대한 트래픽 관리하여 유동적으로 API 호출하여 성능을 높이는데 도움이 됩니다.3. SDK 생성: 사용자 지정 SDK를 만들어 애플리케이션에서 신속하게 API를 테스트하고 배포할 수 있습니다. 2.API 생성새 API로 엔드 포인트 유형을 지역으로 선택하여 생성하세요. 엔드 포인트 유형1. 지역 : 현재 리전에 배포2. 최적화된 에지 : CloudFront 네트워크에 배포3. 프라이빗 : VPC에서만 엑세스 가능3.최종 호출 url로 순차적으로 리소스 생성리소스 이름과 리소스 경로를 입력하고 리소스를 생성합니다.리소스는 호출할 수 있는 특정 URL입니다. 생성된 리소스로 /reviews 주소가 만들어졌습니다.다음은 /reviews 주소 뒤에 {review-no}를 생성합니다. 리소스 경로에 {} 가 포함되어 있으면 경로 파라미터를 나타냅니다.마지막 리소스를 생성하게 되면 위의 이미지와 같이 /reviews/{review-no}/comments 리소스가 생성되었습니다. 이제 메서드에 연결할 Lambda 함수를 먼저 생성하겠습니다.4.Lambda 함수 생성GET, POST 메서드에 연결할 각각의 Lambda 함수를 생성합니다.‘Hello from Lambda’ 문자열로 리턴되는 Lambda 함수가 생성되었습니다. 생성된 Lambda 함수를 API Gateway 메서드에 연결해보겠습니다.5.메서드 생성GET, POST 메서드를 생성합니다.메서드의 의미* POST : 새로 생성(Create)* GET : 조회(Read)* PUT : 수정(Update)* DELETE : 삭제(delete)* PATCH : 일부만 수정(Update) 새 메서드의 통합 유형을 Lambda 함수로 선택하고 기존에 생성한 함수명으로 입력한 다음 저장을 누릅니다.메서드 실행 화면입니다. 해당 메서드에 통합 요청할 Lambda 함수가 연결됩니다. 연결된 Lambda 함수를 눌러보겠습니다.왼쪽 목록 트리거 추가하는 부분에 API Gateway가 연결되었습니다. 그럼 이제 정상적으로 호출되는지 테스트해보겠습니다.테스트를 클릭하면 오른쪽에 요청에 대한 결과가 나옵니다. 조금 전에 연결했던 Lambda 함수에 ‘Hello from Lambda’ 값으로 출력됩니다. 이제 리소스로 추가한 경로 파라미터를 매핑하여 출력해보겠습니다.메서드 요청에서는 사용자에게 노출되는 API를 정의할 수 있습니다. 리소스로 경로 파라미터를 추가하게 되면 메서드 요청 -> 경로 요청 부분에 자동으로 추가되어 있습니다.통합 요청에서는 백엔드와의 통신 방식을 지정할 수 있습니다. 메서드 요청에서 보낸 URL 경로 부분을 매핑시켜야 합니다. 명명 규칙은 아래와 같습니다. method.request.{"path" | "querystring" | "header"}.{param_name}매핑 템플릿을 추가하여 수신된 요청을 변환하여 통합 백엔드로 보내야 합니다. 정의된 템플릿이 없기 때문에 매핑 템플릿을 추가한 후 메서드 요청 패스스루로 지정합니다. 그러면 클라이언트가 제공한 요청이 변환없이 통합 백엔드로 전달됩니다.클라이언트가 요청한 경로 파라미터 출력하도록 Lambda 함수를 수정합니다.이제 다시 테스트를 해보겠습니다. 경로에 값을 요청하여 응답 본문에 출력되는 걸 확인할 수 있습니다.6.API 배포스테이지 정보를 입력하고 배포를 클릭합니다.스테이지 상세 정보에 API 호출 주소가 생성됩니다. Postman으로 생성된 API주소를 입력하여 정상적으로 return 값을 확인합니다.Conclusion정말 긴 과정이었습니다. 지금까지 API Gateway를 이용하여 API 생성부터 배포까지 알아봤습니다. API Gateway를 사용하면 서버 없이 높은 확장성을 가진 백엔드 애플리케이션을 구축하고 운영할 수 있게 될 겁니다. 백엔드에 관심이 있는 개발자에게 이 글이 도움이 되길 바랍니다.글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유
조회수 4748

Elasticsearch로 느린 쿼리 분석하기

응당 인덱스가 있으리라 생각한 칼럼에 인덱스가 없고 인덱스를 걸자마자 응답속도가 평균 10배 가까이 좋아지는 모습을 지켜보니 여러 생각이 들더라. 통계와 지표가 제공되는 곳은 주기적으로 검토하고 문제가 커지기 전에 손을 쓰는데 그렇지 않은 곳이 문제이다. 주기적으로 Slow query 로그를 훑어볼 수는 있다. 하지만 특정 시점에 일부 로그만 훑어봐서는 엉뚱한 문제를 해결하기 일쑤다. 예를 들어 1초짜리 쿼리보다 10초짜리 쿼리가 문제라고 생각하기 쉽지만 이 1초짜리 쿼리를 10초짜리 쿼리보다 1000배 많이 실행한다면 이야기가 달라진다. 요는 느린 쿼리를 지속적으로 수집하고 통계를 낼 필요가 있다는 것이다.이러한 모니터링 도구를 어떻게 구현할까? 우리 손에 있는 도구를 검토하는 일부터 시작했다.통계분석은 MySQL 또는 Elasticsearch 를 쓰면 된다.Elasticsearch를 쓴다면 Kibana를 이용해 시각화하기 편하다.느린 쿼리 로그를 Elasticsearch에 보내는 일은 Fluentd를 쓰면 된다.그러니까 Fluentd, Elasticsearch, Kibana 조합이라면 데이터를 눈으로 보고 문제를 해결하기 좋을 것이다. 그렇다면 어떻게 구현할 것인가?우선 RDS에서 느린 쿼리를 뽑아서 Fluentd에 보내는 방법을 찾아야 한다.Fluentd를 이용해 Elasticsearch에 데이터를 보내는 건 쉬우니 대시보드만 잘 구성하면 끝!문제는 RDS에서 느린 쿼리를 뽑아서 Fluentd에 보내는 것인데 크게 두 가지 방법이 있다. RDS 설정에 따라 느린 쿼리 로그를 테이블 또는 파일에 저장할 수 있는데 이에 따라 구체적인 구현방법이 달라진다. 하지만 기본적으로는 동일한 과정을 거치는데 대충 이런 식이다.느린 쿼리 로그를 읽는다.같은 쿼리라도 매개변수 값이 다를 수 있으므로 mysql_slow_log_parser 또는 pt-query-digest 같은 도구를 사용해 쿼리를 일반화한다.Fluentd를 통해 해당 로그를 ES로 보낸다.새로 추가된 로그만 읽어서 다시 ES로 보낸다.이와 관련해서는 AWS RDS Mysql SlowQuery monitoring on Kibana using Logstash 등의 글이 잘 설명한다.다행히 테이블에 저장한 로그를 읽어들이는 Fluentd 플러그인을 구하기는 쉽다. 변형체가 많은데 대부분은 kenjiskywalker/fluent-plugin-rds-slowlog에서 파생됐다. 파일에 저장한 로그의 경우는 in_rds_mysqlslowlog_stream.rb를 써서 처리하면 된다. 우리는 테이블에 저장하기 때문에 전자를 선택했다.이쯤 조사를 마치고 나니 진행방향은 매우 명확하다. 적당히 잘 만든 Fluentd 플러그인을 골라서 적용한 후에 ES에 대시보드를 만들면 된다. 물론 우리는 Kubernetes 위에 모니터링 도구를 띄워야 하니 Dockerize할 필요도 있다. 이쯤에서 또다시 구글링을 하니 무시무시한 게 나온다. inokappa/rds-slowquery-log-demo는 방금 설명한 모든 과정을 하나로 정리해서 제공한다. Docker로 만든 Fluentd와 ES 대시보드 설정을 한데 묶어놓았다. 거기에 파일 로그, 테이블 로그 둘 다 예제로 제공한다. 덕분에 일이 쉽게 끝날 줄 알았다. 하지만!개발한지 꽤 시간이 지난 지라 최신 버전의 Fluentd와 ES에서 계속 문제를 일으켰다. 문제점에 대해 구구절절 설명할 생각은 없고 DailyHotel/rds-slowquery-log-demo를 참고해서 적용하면 된다는 점만 이야기하고자 한다. 일어로 된 README 파일은 구글 번역기를 돌리면 적당히 읽을만해진다.삽질을 약간만 하면 아래와 같이 간지!나는 대시보드를 얻을 수 있으니 해볼만 할 것이다.참! DailyHotel/rds-slowquery-log-demo는 테이블 로그인 경우만 테스트했으니 파일 로그를 사용하는 경우라면 이 점을 주의해야 한다.더 읽을거리Collecting and Analying Slow Query Logs for MySQLRDS(MySQL) のスロークエリを EFK スタック + Docker で出来るだけ手軽に可視化する考察(2)〜 log_output: FILE の場合 〜#데일리 #데일리호텔 #개발 #개발자 #개발팀 #Elasticsearch #엘라스틱서치 #꿀팁 #도입후기 #일지
조회수 2820

리디북스 웹뷰어의 이어보기를 개발하며

최근 리디북스에서는 판타지 연재물을 웹에서 바로 볼 수 있는 기능을 새롭게 선보였습니다.기존에는 별도의 앱을 설치하고 다운로드하는 과정을 거쳐야 했기에 연재물을 보는 사용성이 좋지 않았습니다만, 브라우저에서 바로 볼 수 있는 “웹뷰어” 기능을 제공함으로써 사용성을 높일 수 있었습니다.그리고 여기에 사용성을 더하기 위해 추가된 것이 이어보기 기능입니다. 짧아도 100화 이상, 길게는 1000화가 넘는 연재물에서 다음 화로의 매끄러운 연결은 매우 중요합니다. 혹은 잠시 읽기를 중단했다가 다시 돌아왔을 때, 어디까지 보고 있었는지를 빠르게 알려준다면 호흡을 이어서 작품에 더욱 몰입할 수 있을 것입니다.이어보기가 구현된 모습리디북스에 로그인되어 있다면, 이곳에서 확인하실 수 있습니다.이번 글은 이어보기 기능에 대한 개발 후기입니다. 요구 사항에 따라 여러 저장소 솔루션을 비교해 보았으며 최종적으로 Couchbase를 선택한 이유와 간단한 벤치마크 결과, 그리고 겪었던 문제를 공유합니다.요구 사항기획된 내용을 요약하니 아래와 같습니다.연재물의 가장 마지막에 읽은 화를 알 수 있다.보았던 모든 연재물에서 가장 마지막에 읽은 연재물을 알 수 있다.사용자가 본 모든 연재물 목록을 확인할 수 있다.이를 개발자 용어로 다시 풀어보면 아래와 같습니다.연재물을 읽을 때마다 연재물 ID와 화(episode) 정보를 기록한다.보았던 연재물을 최신순으로 정렬하여 가져온다.선택된 연재물의 마지막으로 읽은 화를 가져온다.목록에서 특정 연재물을 삭제한다.이어보기는 가장 마지막에 읽은 연재물을 기억하기 위해 작품을 열 때마다 해당 정보를 기록해야 합니다. 그런데 수십 화를 연달아서 보는 연재물의 특성상 내가 어디까지 읽었는지를 조회하는 것(read)보다 내가 읽은 연재물을 기록하는 것(write)이 더 많을 것으로 판단했습니다. 즉, 읽기보다 쓰기가 더 많을 것으로 예상했습니다.NoSQL을 쓰자대부분의 연산이 쓰기(write)와 관련된 이상, 어떤 저장공간을 사용할 것인지가 주된 관심사였습니다.특히 RDBMS와 NoSQL 사이에서 어떤 것을 사용할지 많은 고민과 테스트를 했고, 결국 아래와 같은 이유로 NoSQL을 사용하는 것이 적합하다고 판단했습니다.현재 사용 중인 MariaDB를 그대로 사용한다면 마스터에 부담을 줄 수 있다.별도로 MariaDB를 구성하더라도 운영 및 쓰기 분산하기가 여전히 어렵다.반면 NoSQL은 RDBMS 대비 확장(Scale out)이 간편하므로 운영에 대한 부담이 적다.단순 Key-Value 보관 용도면 충분하다.이어보기 데이터는 독립적인 성격을 가지고 있어서 다른 사용자 데이터와 JOIN을 할 필요가 없다.이어보기 데이터는 크리티컬한 트랜잭션이 필요하지 않다.MongoDB vs. Couchbase데이터를 영속적으로 유지해야 한다는 요구 사항을 충족하기 위해, Redis 등의 메모리만 사용하는 NoSQL은 제외했습니다. 물론 디스크에 기록할 수 있지만, 성능이 급감하기 때문에 실용적이지 못 합니다. 또한, 메모리 사이즈에 기반을 두기 때문에 Scale up 비용이 크고, 서비스 확장시 Scale out 빈도가 높습니다.그래서 MongoDB와 Couchbase를 비교 대상으로 했습니다. 둘 다 도큐먼트 기반의 NoSQL이고 확장이 용이합니다. 과거에는 MongoDB가 Write lock 사용에 있어서 문제점이 있었지만, 최근 버전에서는 문제가 되지 않습니다.[1] 둘 다 기업용 서비스 및 충분한 부가 기능들을 제공하므로 선택하기 어려웠지만, 최종적으로 아래와 같은 이유로 Couchbase(CE)를 선택했습니다.1. 이미 사내에서 다른 서비스에 사용되고 있습니다.가장 중요한 요인이었습니다. 더 좋은 솔루션이 있더라도 어디까지나 서버 스택을 늘리는 것 이상의 효용이 있는지를 따져보아야 합니다. 이미 사용하고 있는 솔루션이 있다면, 검증이 되었을 뿐만 아니라 개발 및 운영 경험도 활용할 수 있습니다.2. 이어보기는 복잡한 쿼리(Query)가 필요 없습니다.이어보기에서 사용할 쿼리는 간단하기 때문에 Couchbase의 뷰(View)만으로 충분했습니다.Couchbase, 실제 성능은 어떨까?테스트를 하기 전 우리가 어떤 식으로 사용할 것인지 정리해야 합니다. 애플리케이션 액세스 패턴이나 동시성 문제, 데이터 구조화 등을 파악하고 그에 맞는 테스트를 진행해야 합니다. 이번 이어보기는 쓰기 연산이 보다 많기 때문에 이로 인한 뷰의 인덱싱(Indexing)에 초점을 맞추고 테스트를 진행했습니다.성능을 위협하는 요소들View IndexingCouchbase는 MapReduce를 이용하여 뷰를 제공합니다. MapReduce는 일반적으로 리소스를 많이 소모하는 동작입니다. 그래서 Couchbase는 버킷의 새로 갱신된 데이터만 인덱싱하는 Incremental MapReduce라는 기법을 적용해서 리소스 소모를 줄였다고 합니다.[2] 하지만 해당 작업으로 인한 부하는 여전히 발생합니다.Auto CompactionCouchbase는 데이터와 인덱스를 디스크에 데이터를 저장할 때 파일에 추가하기(Append) 모드로만 쓰기를 수행합니다.[3] 그리고 오래되고 불필요한 데이터들은 추후 한꺼번에 정리하는데, 이는 디스크 쓰기 성능을 최대화하기 위함입니다.그런데 이렇게 추가만 하게 되면 오래된 정보들은 파일의 앞에 쌓이게 됩니다. 그리고 사용하지 않게 된 데이터도 남아있습니다. 이를 주기적으로 정리해서 최적화하는 작업을 Auto Compaction이라고 합니다. 뷰의 인덱스는 디스크에 존재하기 때문에 디스크 작업이 있으면 인덱싱에 영향을 미치게 됩니다.성능 테스트Couchbase는 기본적으로 5,000ms마다 Index를 업데이트합니다.[2] 그리고 데이터를 비동기적으로 응답합니다. 비동기는 응답속도를 빠르게 하지만, 데이터 불일치가 발생할 수 있습니다. 데이터 불일치가 신경 쓰이고 이 시간이 길다고 생각되면, stale 옵션을 지정해서 뷰의 인덱스를 업데이트할 수 있습니다.이어보기는 뷰가 간단하기 때문에 응답시간에 큰 문제가 없을 것으로 예상하고 stale 옵션을 꺼두었습니다. 이 옵션은 뷰를 조회했을 때 버킷의 변경사항에 따라 뷰를 인덱싱하고 데이터를 응답합니다. 하지만 예상한 것과 같이 실제로도 응답시간이 짧은지 확인할 필요가 있습니다. 그래서 다음과 같이 테스트를 진행했습니다.테스트 환경은 아래와 같이 2-tier로 준비하고 요청을 늘려가면서 RPS를 측정했습니다.서버 구성OS: Ubuntu 14.04Application: Couchbase Server (CE) 3.1.3클라이언트 구성클라이언트 1개에서 50개의 세션으로 요청10만 사용자 가정책은 1만개의 책중 랜덤으로 선택됨요청의 70%는 책 읽기(Bucket Write)요청의 30%는 연재물의 마지막에 읽은 책 가져오기(View Read)그래프 분석성능 테스트 주요 지표RPS : Response Per SecondSP : Saturation PointBuckle zone : 시스템 과부하로 인해 내부 자원이 서로 경쟁상태나 적체 상태가 심해지기 때문에 최대 처리량보다 더 떨어지는 경우가 발생함성능테스트 결과그래프를 보면 요청이 늘어남에 따라 RPS가 선형으로 증가하지만, SP인 8,000 RPS에 도달하고 나서 Buckle zone에서 7,000 RPS로 수렴하고 있습니다. 물론 1개의 클라이언트에서 세션을 생성해서 테스트를 진행했기 때문에 서버의 성능 부족이 아닌 클라이언트의 병목 현상이 원인일 수 있습니다. 또한 JMeter나 다른 부하 테스트 툴을 사용하지 않고 간략하게 만든 테스트 툴을 사용하였기 때문에 수치가 부정확할 수 있습니다. 그러나 어디에서 병목이 있었든 현재 이 이상의 성능이 필요하지 않기 때문에 테스트 결과에 만족할 수 있었습니다.이어보기 배포 후모바일 브라우저 캐시 문제이어보기 기능을 배포하자마자 당일 저녁 이슈 하나를 접수했습니다. 아이패드와 PC를 번갈아 이용할 경우 이어보기 데이터가 맞지 않다는 것이었습니다.데이터를 쌓을 때 모든 이력을 기록하지는 않았지만, 다행히도 Couchbase에 이용기기와 시간은 기록하였기 때문에 이를 바탕으로 디버깅을 할 수 있었습니다. (서비스 초기라 할지라도 최대한 많은 이력을 남기는 것이 중요함을 다시 느꼈습니다)원인은 아이패드의 멀티태스킹으로 인한 캐시 소멸이었습니다. 아이패드 브라우저의 캐시가 소멸되면서 마지막으로 열어두었던 페이지가 강제적으로 리로딩되었고, 이때 의도치 않게 마지막 위치 정보가 덮어씌워진 것입니다.이 문제는 기술적으로 해결이 쉽지 않아 결국 기획을 수정하게 되었습니다. 사용자가 해당 책을 읽었다고 판단하는 기준이 “페이지를 열어본 즉시”였다면, 이를 “페이지를 열고 수 초 이상을 유지”하는 것으로 기준을 변경하였습니다. 물론 근본적인 해결책은 아니었지만, 실제 사용에는 지장이 없는 합리적인 해결책이라고 생각합니다.Key 구조의 변경 및 동시성 문제Couchbase는 높은 성능을 위해 메타데이터(Key + @)를 모두 메모리에 적재하는 특징이 있어서, Document 하나가 평균 350Byte를 차지하고 있었습니다. 따라서 현재 상태로 1000만개의 데이터를 저장할 경우 최소 3.5G의 메모리를, 2개의 사본(Replica)를 유지할 경우 약 10.5G의 메모리를 사용하게 될 것으로 예상되었고 이는 큰 부담으로 다가왔습니다.처음에는 단순히 “사용자ID_연재물ID” 형태의 Key를 사용하였지만, 보다 빠르게 증가할 것으로 예상되는 것은 사용자보다 연재물 이었으므로 아래와 같이 Key값을 변경하여 메모리 사용량을 크게 줄였습니다.// U_id : S_id 조합을 사용하면 Key가 엄청 많아진다. // 그래서 사용자당 Key를 100개로 제한하도록 한다. Count = 100 Key = '사용자ID' + ('연재물ID' % Count) 그런데 이렇게 Key 구조를 변경하였더니, 간단한 업데이트 동작임에도 불구하고 정상적으로 수행되지 않는 경우가 빈번하게 발생하였습니다. 이유는 낙관적 동시성(Optimistic concurrency) 모델의 특징 때문이었는데, Couchbase는 명시적인 잠금 이외에도 “Check and Set(CAS)”이라는 기능을 제공하고 있었습니다.공식 문서의 예제를 참고하여 아래와 같이 로직을 수정한 뒤로는 다행히도 동시성 문제가 아직까지 발생하지 않고 있습니다.boolean updateUsingCas(key, value) {  for (tryCount = 0; tryCount < MAX>    orgValue, cas = getValueAndCas(key)           // Update the original value.     // newValue = ... if setValueWithCas(key, newValue, cas)      return SUCCESS sleep(0.1) // 부하를 줄이기 위해  }  return FAIL } 맺으며동작하는 서비스에 새로운 기능을 추가한다는 것은 어려운 일입니다. 특히 새로운 데이터 스토리지를 필요로 하는 일이라면 더더욱 어렵다고 생각합니다. 그리고 그럴 때일수록 설계에 많은 시간을 들여야 한다는 것을 느꼈습니다. 설계 초기에는 RDBMS의 샤딩까지 고려하였지만, 요구 사항을 구체화할수록 단순 Key-Value로도 같은 문제를 해결할 수 있음을 깨달았기 때문입니다.또한, 서비스 개발에 있어서 어려운 문제를 마주했을 때 기술적으로만 접근할 것이 아니라 고객이 정말 원하는 것이 무엇인지를 고민하여 기획적으로 해결하는 능력도 중요하다는 것을 실감하였습니다.마지막으로 Couchbase는 현재로서도 꽤 좋고 앞으로도 많은 발전이 기대되는 NoSQL입니다. 도입을 고민하시던 분들께 조금이라도 도움이 되었기를 바랍니다.참고자료[1] MongoDB - Concurrency[2] Couchbase - Views Operations[3] Couchbase - File write#리디북스 #개발 #개발자 #서버개발 #서비스개발 #고객중심 #기능개발 #Couchbase #인사이트 #개발후기
조회수 1644

Android 의 Sqlite Tip

Android 와 SqliteAndroid 에서 Sqlite 는 오랫동안 사용되어 왔습니다. 지금은 Realm 과 그외의 데이터베이스들이 그 위치를 넘보고 있지만 여전히 사용되고 있음은 틀림없습니다.현재 Jandi 는 서버의 대다수 정보를 앱의 Sqlite-Database 에 Cache 를 하고 있습니다. 따라서 Sqlite 를 얼마나 잘 분리하고 제어하느냐가 앱 자체의 라이프사이클에 큰 영향을 미칩니다.오늘은 Android 팀이 Sqlite 를 어떻게 사용하는지 공유해드리고자 합니다.1. ORM안드로이드만 하신 분들에게는 다소 생소한 개념일 수 있지만 다른 분야에서는 널리 사용되고 있습니다.Android 에서 Sqlite 는 Database 용 Access 객체를 통해서 column/row 단위로 정보를 가져와서 객체를 완성합니다. 하지만 Access 객체에 일일이 Query 를 작성하는 것은 실수가 많을 뿐더러 column/row 단위 정보 매핑 작업은 매우 불편하고 지루하며 잠재적 버그를 내포한 작업니다.그러기 때문에 Object-Query-Databse 를 각각에 맞게 매핑해주는 라이브러리들 통해 단순하고 반복적인 작업을 간단하게 회피할 수 있습니다.아래의 블로그들이 Sqlite-Orm 라이브러리를 사용하는 좋은 정보들이 될 것입니다. 현재 Jandi-Android 의 주요 Orm 라이브러리는 OrmLite 입니다.Sqlite-Orm : 네이버 기술블로그GreenDao BenchmarkRealm Database2. Database-Access유사 관심사 Domain 끼리 묶음수많은 데이터를 테이블로 관리하다보면 많은 Database-Access-Object(DAO) 가 필요합니다. 하지만 자세히 들여다보면 관계된 것끼리의 묶음이 생기게 되며 이를 묶어서 하나의 Access 객체를 만들 수 있습니다.Jandi 의 메시지는 크게 Text, File, Sticker 로 구분되어 있으며 이에 대한 상위로 Message 라는 개념이 있습니다. Text, File, Sticker 는 하위에 각각 2~3개의 Table 로 구성되어 있습니다. 이 전체를 각각 분리해서 관리하면 그에 따른 부수적인 제어 코드들이 불가피 하기 때문에 Jandi 에서는 최상위 Message 도메인에 맞춰서 하나의 묶음으로 관리하였습니다.코드는 다음과 같은 형태를 띄고 있습니다.public class MessageRepository { public List getMessages(/*args...*/) { /*코드 생략*/}; public Message save(/*args...*/) { /*코드 생략*/}; public Message update(/*args...*/) { /*코드 생략*/}; public Text getText(/*args...*/) { /*코드 생략*/}; /*이하 생략*/ } 이러한 형태로 독립성을 가질 수 있는 최상위 Domain 을 기준으로 Repository 클래스를 가지고 있습니다.3. Repository 요청 관리하기위의 모습처럼 관심사별로 Domain 을 분리한 이유 중 가장 큰 이유는 Domain 단위로 요청을 관리하기 위함입니다.Android-Sqlite 는 내부적으로 Read-Write lock 을 가지고 있지만 신뢰도가 높다 할 수 없으며 다양한 테이블에 동시 접근하는 경우 오류가 나지 않을 것이라 보장할 수 없습니다.따라서 보장이 안될바에 1번에 1개의 요청만 처리 할 수 있도록 Domain 단위로 요청을 제한해버리자는 결론을 냈습니다.그러기 위해 2가지 코드를 사용하였습니다.Lock 객체 사용요청을 래핑할 template interface 사용하기멀티 쓰레드로 요청을 처리할 때 Lock 객체를 통해 1번의 1개씩의 동작만 할 수 있도록 하였으며 이를 좀 더 쉽게 쓸 수 있도록 하기 위해 Template Interface 를 만들었습니다.public class LockTemplate { private Lock executorLock; LockTemplate() { executorLock = new ReentranceLock(); } protected T execute(Executable e) { executorLock.lock(); try { return e.execute(); } finally { executorLock.unlock(); } } interface Executable { T execute(); } } 위와 같은 클래스를 만들고 앞서 만든 Repository 클래스에 상속받도록 하였습니다.코드는 다음과 같습니다.public class MessageRepository extends LockTemplate { /*싱글톤으로 동작하도록 합니다. 코드 생략*/ public List getMessages(long roomId) { return execute(() -> { return dao.query(roomId); }); } public int save(List messages) { return execute(() -> { return dao.save(messages); }); } } 위와 같이 함으로써 최종적으로 같은 Repository 에 멀티쓰레드에서 요청을 하여도 1개의 처리만 할 수 있도록 원천적으로 작업하였습니다.정리Android 에서 Sqlite 는 Mysql 이나 PostSQL 과 유사한 RDBMS 를 제공하는 DB 툴입니다. 하지만 기본적인 사용이 매우 번거러울 뿐만 아니라 메모리릭과 오류에 매우 쉽게 노출됩니다. 그래서 다음과 같은 방법을 통해 최소한의 안전망을 구현했습니다.ORM 을 사용하라.반복적이고 DB 접근 과정에서 오류를 최소화 시켜줍니다.Lock 을 의도적으로 사용하라.멀티쓰레드 접근에 의한 오류를 최소화 합니다.synchroized 보다는 concurrent 패키지에서 제공해주는 Lock 을 사용해주세요.#토스랩 #잔디 #JANDI #개발 #앱개발 #인사이트
조회수 2671

MOIN 안드로이드 개발자를 소개합니다

영화 같은 일들이 매일같이 벌어지는 요즘 모두들 안녕하신가요?해외송금 스타트업 모인에게 최근 새로운 변화가 생겼습니다.안드로이드 개발자가 합류했습니다.어떤 분인지 지금부터 소개 해드리겠습니다 ^^안드로이드 개발을 해주실 효찬님!- Professional Experience -2015.01~2016.10 kt R&D 연구개발센터 전임연구원2013.07~2015.12 kt R&D 연구개발센터 연구원2013.03~2013.06 kt R&D 연구개발센터 인턴- Education -2006.03~2013.08 고려대학교 컴퓨터통신공학부 학사2001.09~2005.05 Jakarta International School▶ 모인에서 어떤 일을 담당하고 계신가요?총체적인 안드로이드 개발과 웹 서버를 보조하는 일을 맡고 있습니다.▶ 개발자가 되겠다고 한 계기가 궁금합니다. 개발자로서 이력에 대해 간략히 설명해주시겠어요? 특정한 계기가 있어서 개발자가 되겠다고 한 건 아니었어요. 고등학교 시절, 컴퓨터 게임 하면서 막연히 나도 게임을 만들어 보고 싶다고 생각했고 그래서 관심은 가지고 있었죠. 친구들 이름 넣어서 RPG를 만들어보기도 했는데, 생각해보면 스토리 만들기가 재밌었던 거 같아요. 그러면서 자연스럽게 어떻게 하면 컴퓨터 개발할 수 있나 생각도 해보게 됐고, 책도 뒤적여보게 됐습니다. 이런 생활이 고2때까지 이어졌어요. 그런데 마땅히 공부할 수 있는 방법이 없어서 대학가서 해야겠다 생각했습니다. 대학 와서 본격적으로 컴퓨터공학을 공부하면서 재미를 느끼게 됐어요.▶ 그 중에서도 안드로이드 개발을 선택하게 된 이유는 뭐였을까요?예전에 KT에 있을 때, 안드로이드 개발 프로젝트를 맡으면서부터입니다. 원래 이 분야에 대해 전혀 몰랐는데 회사에서 3일간 안드로이드 개발 교육을 받고 해보라는 지시를 받게 된거죠. 막상 해보니 재밌었어요. 특히 이 시기가 2014년 초였는데, 당시에는 안드로이드가 워낙 인기 있는 분야여서 더욱 할만하겠다는 생각을 하게 됐습니다. 효찬님이 선보인 안드로이드 앱 (왼부터)가계부투게더, 메모캐스트, 돈테크 ▶ 본격적으로 모인에 들어오게 된 이야기를 들어보고 싶습니다. 어떻게 모인을 알게 되셨나요?이전 회사에서 늘 입버릇처럼 ‘스타트업을 하고 싶다’는 말을 하고 다녔습니다. 생각해보면 제가 굉장히 밉상이었을텐데 주변 회사분들이 응원을 많이 해주셨어요. 정말 좋으신 분들입니다. (하하) 지인 추천으로 원티드를 알게 됐어요. 저는 초기 단계에 있는 스타트업에 가고 싶었는데 쉽게 찾기는 힘들더라고요. 이후 설립한지 1년도 안 된 ‘모인’을 찾게 됐습니다. 회사에 대해 이것저것 찾아보고 한 번 만나서 이야기해보면 좋겠다는 생각이 들어 대표님을 만났어요. 대표님과 만나 이야기를 나누어 보니 같이 일하고 싶었습니다.  ▶ ‘스타트업을 하고 싶다’는 말을 입버릇처럼 하셨다고 했는데, 특별한 계기가 있었나요?대학 때, 그래픽 프로그래밍 관련 Term Project를 수행했던 적이 있었어요. 이 때 친구들과 밤을 새면서도 웃고떠들며 프로젝트를 해낸 게 제겐 정말 좋은 경험이었습니다. 친한 친구들과 같이 일을 하면 힘든 업무도 웃으면서 즐겁게 할 수 있다는 생각이 들었어요. 앞으로도 좋은 사람들과 같이 즐겁게 일할 수 있으면 좋겠다는 생각을 가지게 되었죠. 스타트업에서 근무하면 일과 동시에 좋은 조직문화를 만들어나갈 수 있을 거라고 생각했어요.  ▶ 개발자로서 자신 있는 영역이 무엇인가요?두루 다룰 줄 안다는 게 제 장점일 수 있겠네요. 그래서 스스로 찾아가면서 어떤 서비스든 개발할 수 있다는 자신이 있습니다. 하지만 역시 한 분야에 대한 전문성은 좀 부족하지 않나 생각해요. 안드로이드에 더더욱 집중해보려고 노력한 이유이기도 합니다. 앞으로도 저만의 차별점을 발굴하는데 계속 노력을 기울일 생각입니다. 효찬님이 가장 애착간다는 원피스 '상디'▶ 개발 외 관심 있는 영역이 무엇인가요?개발 외적으로는 조직 문화에 관심이 많습니다. 제가 개인적으로 일본 만화 ‘원피스’를 좋아해요. 루피 해적단을 보면 개개인이 발전하면서 동시에 팀이 강해지는 모습을 볼 수 있거든요. 어떠한 모험도 할 수 있을 정도로 강해지죠. 루피 해적단 같은 조직을 꿈꿉니다. 어떻게 보면 제가 꿈꾸는 조직 문화가 담겨있다고도 할 수 있죠.특히, 배트맨을 보면 악당이 배트맨 지인을 인질로 잡으면 배트맨은 지인을 구하러 가죠. 하지만 원피스에서는 악당이 루피 친구들을 인질로 잡으면 루피는 친구를 구하러 가지 않아요. 다만, 친구가 함정에서 알아서 잘 나올거라 믿습니다. 그리고 악당을 쓰려트려야 하는 자신의 역할을 수행하는 데 충실해요. 동료를 믿기 때문에 가능한 자세라고 생각해요. 제 나름대로 ‘믿음의 리더십’이라고 혼자 정의해봤어요. (웃음) 대신 내 능력은 스스로가 키워나가야 하죠. 이렇게 각자 자신의 일에 최선을 다하는 사람들과 함께 큰 꿈을 이루는 게 지금 제게 마지막으로 남아 있는 순수한 이상입니다.▶ 더 키워나가고 싶은 역량이 있나요?역량이라기 보다는 제가 만든 작품을 Developing 해나가고 싶어요. 발전가능성이 없는 서비스는 더 이상하고 싶지 않습니다. 내가 만든 앱을 상용화 시키고, 고객들이 반응하는 걸 직접 보고 싶어요. 더불어 서비스 개선에 필요한 역량은 지속적으로 키워나가려고 합니다.장효찬 개발자에게 '함께 일하고 싶은 사람'이란?#온정 #진솔 #파이팅 ▶ 출근한지 일주일도 안됐지만 (웃음) 모인에 대한 첫인상은 어땠어요?정말 아직 1주일도 안됐는데…. (웃음) 대기업에 있다 와서 그런지 소위 ‘젊음의 열정’이라고 하죠? 다들 파이팅 넘치는 모습이 좋았습니다. 그러면서 동시에 나 잘났다고 으스대지도 않고, 특히 대표님 같은 경우는 능력도 있으신데, 겸손하기까지 해서 반했어요. 무언가를 물어보면 설명도 친절하면서 꼼꼼하게 해주시구요. 팀워크가 좋을 거 같다는 긍정적인 예감이 들었어요. 사람 뽑는 데 신중하시다는 대표님을 믿으며, 앞으로도 잘 부탁드립니다.  ▶ 앞으로 어떤 개발자가 되고 싶으신가요? 모인에게도 한마디 해주세요.개발 PM(Project Manager)에 관심이 많습니다. 사실 앱 구현보다 뼈대를 구축하는 일이 더 중요하다고 생각해요. 저는 이 부분이 개발에서 30% 혹은 그 이상을 오롯이 혼자 차지하고 있다고 생각해요. 그러기 위해서는 리소스나 구현한 코드를 어떻게 관리할까, 어떤 부분을 어떻게 더 추가를 해서 연동시킬 수 있을까 등을 서비스 앱 전체를 보고 관리할 줄 알아야 하죠. 이 역할이 국내에서는 중요하게 다뤄지는 거 같지 않아 안타깝습니다. 대부분 보이는 것에만 관심을 가지고 제품이 어떤 제질로 만들어졌는지는 큰 관심을 가지지 않는 추세거든요. 저는 이러한 부분을 중요시 여기는 개발자가 되고 싶습니다. “저를 버리시면(?) 아니됩니다 (웃음)”- 장효찬이 꼽은 인생 명언 -“Do what you love. Everything else is secondary”by. Steve Jobs#모인 #MOIN #개발자 #개발팀 #안드로이드개발자 #안드로이드 #팀원 #팀원소개 #팀원인터뷰 #인터뷰 #기업문화 #사내문화 #조직문화

기업문화 엿볼 때, 더팀스

로그인

/