스토리 홈

인터뷰

피드

뉴스

조회수 2851

페이스북 광고 5가지 A/B 테스트 방법_콘텐츠편

# 이 광고 콘텐츠가 더 좋을 줄 알았는데'이 광고 콘텐츠 잘 먹힐 거 같애~' 퍼포먼스 마케터로 성장하는 과정 속에서 혼자 생각에 꽤나 괜찮은 디자인이 나오거나, 꽤나 괜찮은 카피가 나오거나, 꽤나 새로운 형태의 광고 콘텐츠를 제작했을 때 항상 속으로 위와 같은 말들을 내뱉는다. '이번 광고는 사이트 유입 단가가 낮을 거 같애, 내가 원하는 목표 전환당 비용이 꽤나 저렴해질 거 같애, 목표 전환율이 높아질 거 같애~' 라는 생각으로 광고를 집행해보면 막상 내가 예상했던 그림대로 안 움직이는 경우가 많다. 나의 감이 성과를 가져다 주지는 않았던 것이다. 주어진 시간 내에 빠르게 성과는 내야 하고 예산은 정해져 있고... 목표 전환율이 높은 광고 콘텐츠 형태를 찾기 위해서 광고 콘텐츠에도 A/B 테스트를 도입할 수밖에 없었다. 감이 아닌, 감 to the  검증을 위해서 말이다. # 광고 콘텐츠 A/B 테스트(페이스북과 인스타그램을 전제로)실험의 형태는 정말로 다양하다. 다양한 실험의 형태에서 브랜드의 서비스에 유효할 것 같은 실험 형태를 정해 놓고 보통 실험을 한다.(실험을 하는 주체에 따라서 많이 달라질 수 있을 것 같다.) 개인적으로 진행해봤던 실험들을 생각해보고 정리를 해보았다.(1) 광고 콘텐츠의 형태 단일 배너, 정사각 슬라이드, 간단한 영상, 콜랙션 광고, 인스타그램 스토리 광고 등 최초에는 리소스가 많이 들어가지 않는 선에서 실험을 진행하는 경우가 많다. 광고의 형태에 따라 광고 노출 영역이 다소 달라지긴 해서 리소스를 최소화하는 단일 배너 및 정사각 슬라이드, 소유하고 있는 영상이 있다면 영상까지 함께 집행한다. (영상이 잠재고객의 참여도가 좋다는 건 많이들 이야기 하지만, 기대하는 최종 kpi가 매체 효율뿐만 아니라 웹사이트에서의 특정 행동 전환율과 전환 단가 이기 때문에 크게 상관하지 않고 실험을 진행하는 편이다.)(2) 카피 베리에이션 동일한 디자인에서 배너에 들어가는 카피만 여러 개로 나눠서 실험을 진행하기도 한다. 그렇게 하는 이유는 광고를 집행하는 나도 어떤 메시지가 광고 매체 효율이 좋을지, kpi는 어떤 게 좋을지 사실 알 수 없기 때문이다. 실패의 확률을 줄이면서 리소스를 최소화해서 실험하는 방법 중의 하나이다. 보통 동일한 배너 디자인에 카피를 3개로 나누어 A/B/C 테스트를 한다.(3) 디자인 같은 카피 다른 디자인, 예를 들어 설명해주면 아기 화장품 제품을 광고하는데 소재에 들어가는 카피는 동일하되 디자인이 아기가 들어간 게 좋을지, 제품만 들어간 게 좋을지, 아기와 제품이 함께 들어가는 게 좋을지, 혹은 아기가 들어가는데 아기 실사가 들어가는 게 좋을지, 일러스트 느낌의 아기 이미지가 들어가는 게 좋을지를 실험해볼 수 있다. 매번 이렇게 진행할 수는 없지만, 우리에게 성과를 가져다주는 광고 콘텐츠의 형태를 찾는 단계에서 필수적이다.(4) 전면사진슬라이드 형태나, 영상 광고 집행할 때 많이 해봤던 것 같다. 영상이라 한다면 영상의 썸네일 이미지를 어떤 걸로 선택해서 하는 게 좋을지 실험을 해보는 것이고, 슬라이드 형태는 전면 슬라이드 이미지(첫 번째 카드 이미지)를 여러 개로 구분해서 실험을 해보는 것이다. 예를 들어 여성 쇼핑몰에서 여름휴가에 필요한 옷을 광고하는데 a 제품을 전면에 내세우는 게 좋을지, b 제품을 전면에 내세우는 게 좋을지 실험을 해보는 것이다.  총 5개의 카드 이미지가 있다고 가정하고, 그 중에서 전면에 배치하기에 좋은 카드 이미지가 3개가 있다고 한다면 아래 방법처럼 진행해볼 수 있다.a-b-c-d-eb-a-c-d-ec-a-b-d-e=> 초반 최적화 작업이 끝난 후에 광고 효율이 좋은 광고에 예산을 증액하고 나머지 광고는 off 하면 된다.(5) key 메시지앞서 언급했던 카피 베리에이션과 유사한 형태일 수도 있는데 조금은 다른 느낌의 실험이다. 우리 제품이나 서비스가 잠재고객에게 어필할 수 있는 요소가 다양한데 어떤 걸 보여주는 게 성과가 가장 좋을지 알아보는 것이다. 제품이나 서비스의 장점을 언급할까? 아니면, 이미 만족해서 사용하는 사용자의 후기를 보여줄까?, 아니면 할인에 대한 언급을 해줄까? 아니면 할인과 다른 내용을 합쳐서 보여줄까? 할인을 하면 할인하는 %를 보여줄까? 아니면 할인된 가격을 보여줄까 등등 카피 베리에이션과 비슷하면서도 조금은 다른 실험을 진행할 수도 있다. 예를 들어 후기의 형태로 광고를 한다면 이 것 또한 구분을 할 수 있을 것이다. 여러 명의 짧은 코멘트 후기를 나열해서 보여줄까? 아니면, 가장 괜찮은 후기 1개를 보여줄까? #실험의 전제 조건(1) KPI는 명확해야 한다. 그렇지 않으면 의사결정은 산으로 갈 수가 있다. 매체의 효율을 볼 것인가, 아니면 사이트에 유입된 후 회원가입률을 볼 것인가, 구매 전환율을 볼 것인가?, 다른 고객 행동 전환을 볼 것인가? 명확한 KPI는 정해져 있어야 한다. 광고주와 에이전시에 입장이라면 상호 간의 공유가 필요하고, 인하우스 마케터라 한다면 적어도 광고에 관여하는 누군가와는 명확한 kpi 공유가 이루어져야 한다. 그래야 데이터를 본 후 명확한 의사결정을 할 수 있고, 서로 얼굴 붉힐 일도 없을 것이다.(2) 분석할 수 있는 데이터 분석 툴이 필요하다 페이스북 픽셀을 설치해서 전환당 효과를 보든, 구글 애널리틱스로 광고 콘텐츠 별 성과 데이터를 보든, 광고 콘텐츠 A/B  테스트를 진행할 때에는 (개인적으로) 반드시 로그 분석 툴로 데이터 분석이 뒷받침 되어야 한다.(3) 상처받지 않는 기술 필요하다. 실험을 돌렸을 때 성과가 좋은 실험도 있고, 성과가 좋지 않은 실험도 있다. 반복적으로 좋지 못한 성과들을 마주할 수도 있는데 A/B 테스트에서 좋은 결과를 보기 전까지는 상처받지 않는 기술이 필요하지 않나 싶다. 퍼포먼스 마케터라면 광고를 집행하고 몇 시간마다 한 번씩 모니터링하는 경우가 많을 텐데 상처받지 말고 성공을 위한 실패로 받아들이는 기술이 필요할 것 같다. 그리고 실패에 대해서 이해할 수 있는 문화는 내부적으로나 에이전시와 광고주간에 꼭 필요하다. 개인적으로 성과가 좋지 못할 때는 잠시 이어폰을 꽂고 명상을 듣는다. 마음이 차분해진다. NEXT를 생각하게 된다. 쉽지 않지만 말이다^^광고 콘텐츠 A/B 테스트는 하면 할수록 유용하고 필수적이라는 생각이 든다. 적어도 퍼포먼서 마케터에게는 말이다. 최근에 진행해봤던 광고 콘텐츠 A/B 테스트, 그리고 A/B 테스트 후 다음 단계에서 유효한 타겟을 넓히는 작업을 진행해본 사례가 있는데 글이 너무 길어질 것 같아 다음에 다시 한번 정리를 해볼 생각이다. 누군가 이 글을 보고  광고 집행에 도움이 되길 바라는 마음이다. 그리고 추후엔 광고 콘텐츠 A/B 테스트뿐만 아니라 다양한 실험 사례들도 소개할 예정이다. 퍼포먼스 마케팅 에이전시, 오피노 바로가기
조회수 1381

[Buzzvil People] Andy Kim, Software Engineer

 Buzzvil People에서는 다양한 배경과 성격 그리고 생각을 지닌 버즈빌리언들을 한 분 한 분 소개하는 시간을 갖습니다. 어떻게 버즈빌에 최고의 동료들이 모여 최고의 팀을 만들어가고 있는 지 궁금하시다면, 색색깔 다양한 버즈빌리언들 한분 한분의 이야기가 궁금하시다면, Buzzvil People을 주목해주세요.1. 간단한 자기 소개 부탁드립니다. 안녕하세요. 저는 Andy입니다. 저는 중학교부터 대학교까지 미국에서 7~8년 동안 지내며 학업을 마치고 한국으로 돌아와 버즈빌에 자리를 잡았습니다. 대학 시절에는 제약에서부터 통계, 데이터/컴퓨터 공학까지 전공을 여러 번 바꿨습니다. 개인적으로 새로운 레시피나 방법으로 요리하는 것을 좋아합니다. 개인 시간에는 주로 유튜브에서 요리 콘텐츠를 즐겨보고, 동료나 친구들과 함께 새로운 레시피에 도전해보곤 합니다. 2. 어떻게 버즈빌에 오시게 되셨나요? 대학을 졸업하고 한국으로 돌아온 후, 새로운 소프트웨어 엔지니어링 툴을 적극적으로 사용하는 IT 회사에서 일하고 싶었습니다. 구인 사이트에서 클라우드 기술과 관련된 채용공고를 찾아보다가, 버즈빌을 발견했습니다. 3. 버즈빌에서 어떤 업무를 담당하고 계신가요? 저는 데브옵스(DevOps) 팀에 소속되어 데이터 및 데브옵스 엔지니어로 일하고 있습니다. 제 주 업무는 다음과 같지만 국한되어 있지는 않습니다. – 새로운 데이터 생성 시 Data pipelines 제작 및 유지보수 – 자사에서 사용하는 Redshift 데이터 웨어하우스 유지보수 및 최적화 – 메타베이스(Metabase) 를 이용해 데이터를 다른 직원들이 사용할 수 있도록 가공 4. 스타트업에서 혹은 광고업계에서 일하는 느낌이 어떠세요? 소프트웨어 엔지니어로서 스타트업에서 근무하는 것은 샌드박스 게임을 하는 것과 비슷했습니다. 대부분의 경우, 새로운 기술이나 컨셉을 주저하지 않고 시도해볼 수 있습니다. 업무 사항에서 한발 더 나아가 더 큰 노력을 쏟아붓고자 하면, 사실상 무수히 많은 데이터 자산과 인프라에 접근할 기회도 받습니다. 5. 이것만큼은 버즈빌이 참 좋다! 어떤 게 있으실까요? 무제한으로 제공되는 신선한 커피 원두가 행복한 충격이었습니다. 커피를 내릴 때 3가지 이상의 커피 원두를 마음껏 고를 수 있는데요. 제가 속한 데브옵스 팀에서 커피타임은 매우 중요한 의식 중 하나입니다. 매일 스크럼을 커피타임과 함께 진행하고 있습니다. 돈 한 푼 사용하지 않고 즐길 수 있는 좋은 품질의 커피는 버즈빌에서 일하면서 받을 수 있는 기분 좋은 보너스 중 하나입니다. 6. 개인적인 목표나 꿈이 있으신가요? 있다면, 버즈빌에서의 경험이 어떻게 도움이 된다고 생각하시나요? 최종적으로, 제 꿈은 요식업계에 도전하는 것입니다. 아직 구체적인 계획은 없지만, 편향된 데이터나 해로운 과정의 효과를 최소화해 대중들에게 좋은 음식을 제공하는 서비스를 만들고 싶습니다. 버즈빌에서 데이터 엔지니어로서 일한 경험은 좋은 팀을 만나게 해주고, 유연하면서도 탄탄한 서비스를 만들어, 최종적으로는 사람들의 먹거리에 영향을 주는 것에 도움이 될 것으로 기대하고 있습니다.
조회수 17503

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 #백엔드 #인사이트 #꿀팁
조회수 2468

사운들리 백엔드 이야기

사운들리는 '귀에 들리지 않는 소리'를 이용해서 컨텐츠를 전달할 수 있는 SaaS 플랫폼을 서비스하고 있습니다.제품의 구성요소는,음파를 송신할 수 있는 송신단음파를 모바일에서 수신할 수 있는 Android, iOS SDK그리고 컨텐츠를 제공하고 데이터를 수집, 분석하는 백엔드로 구성되어 있습니다.오늘은 구성 요소중 백엔드에 대해서 이야기 해보도록 하겠습니다.<그림 1. 사운들리 솔루션 구성도>사운들리의 인프라는 모두가 잘 아시는 아마존 웹 서비스를 이용하고 있으며, 크게 컨텐츠를 제공하는 API서버 부분, 로그를 수집, 분석하는 부분, 그리고 컨텐츠를 관리하는 CMS 부분으로 이루어져 있습니다.소프트웨어 스택Java : 현재 사운들리의 일부 시스템을 제외하고는 전부 자바로 작성되어 있습니다. Node.js로 시작하여 PHP를 거쳐 지금의 자바 기반의 시스템으로 구성하게 되었습니다. 다양한 사람들이 개발을 해오면서 각자 가장 잘할 수 있고, 빠르게 구현할 수 있는 언어로 개발되어 가다 현재의 자바로 통일되어 구성되게 되었습니다.Spring : API서버는 HTTP 기반의 REST API를 이용해 컨텐츠를 전달하고 있으며 스프링 프레임워크를 이용해 개발되었습니다. 이외에도 일부 분석에 스프링 배치를 사용하고 스프링을 편리하게 사용할 수 있게해주는 스프링 부트도 이용하고 있습니다.gRPC : 분산되어있는 서버들끼리 이기종 언어간 통신을 하기 위해서 Protocol Buffers 기반의 gRPC를 이용하고 있으며 서버들의 모니터링하는 서버와 에이전트들 사이의 통신 목적으로 사용합니다.Flume : 분산된 서버들에서 로그를 수집하는 역할을 합니다. 수집된 로그는 파일로 저장하며 실시간으로 볼수 있도록 엘라스틱서치에 같이 저장하고 있습니다. SDK에서 전송되는 로그 또한 웹서버의 엑세스 로그를 플럼 에이전트가 수집하는 방식으로 비동기로 처리하고 있습니다.ElasticSearch : 수집된 로그들을 실시간으로 확인하기 위해서 사용되며 Kibana를 이용해 시각화하고 있습니다.Angular.js : CMS의 프론트엔드는 Angular.js + Bootstrap을 이용해 개발되었으며, Bower를 이용한 라이브러리 관리, Grunt를 이용한 빌드 관리를 하고 있습니다.소프트웨어 개발/운영GIT : 소스코드는 git로 관리하며 Git-Flow를 이용한 브랜치 정책을 수립하여 가져가고 있고 저장소로는 깃허브를 이용합니다.Quality Practice : QA단계에서 제품을 테스트하기 전 개발자들은 QA 프로세스에 맞게 다음 3가지 기준으로 소스 코드의 품질을 관리합니다.코딩 컨벤션 : 사운들리 내부 코딩 컨벤션에 맞게 개발되었는지 확인합니다. Checkstyle의 규칙을 정의 및 자동화합니다.테스트 코드 : 단위 테스트 코드를 작성하며 테스트 결과는 모두 통과되어야 합니다.테스트 커버리지 : 단위 테스트 코드가 작성된 커버리지를 계산하며 현재 60%를 목표로 진행하고 있습니다.젠킨스 : 소스코드 저장소에 변동이 일어나면 젠킨스가 소스코드를 빌드하고 위에서 언급한 세가지에 대한 리포트를 작성합니다.소나큐브 : 무료 오픈소스로 코드 정적 분석을 해주며 및 QA 리포트를 같이 볼 수 있습니다.슬랙 : 인력이 적은 저희 팀도 슬랙을 적극적으로 개발/운영에서 사용하고 있습니다.팀 커뮤니케이션 : 팀원들 간의 의사사통을 위한 주요 수단으로 모든 팀원이 함께 사용하고 있습니다.분석 리포트 : 젠킨스나 배치를 통해 분석된 데이터들은 분석이 끝난 지표들은 슬랙으로 결과를 전송하여 모든 팀원이 볼 수 있도록 공유하고 있습니다.서버 모니터링 : 서버들의 이상 징후 감지나 배치 오류등을 슬랙을 통해 담당자에게 전송하여 조치할 수 있도록 합니다.애플리케이션 및 서버 모니터링 : 애플리케이션의 모니터링은 Naver에서 오픈소스로 공개한 핀포인트를 사용하고 있고, 서버 상태 모니터링을 위해 자체 개발한 모니터링 시스템을 사용하고 있습니다. 모니터링 데이터 수집을 하는 에이전트와 전체 시스템의 데이터를 관장 하는 서버간에는 gRPC를 이용하여 상태 체크를 합니다. 서버의 상태에 문제가 있을 때에는 slack을 통해 담당자들에게 알람을 주도록 시스템 설계를 하였습니다.개발 문화개발자들은 각각 개발을 할때 정해진 정책에 맞춰 브랜치를 만들어 개발합니다.각각 개발된 소스들은 저장소인 깃허브에 푸시된 후 깃허브의 댓글 기능을 이용하거나 오프라인을 통해 코드 리뷰를 진행합니다.리뷰가 끝난 후 합쳐진 소스는 QP 활동을 통해 분석이 됩니다.빌드가 실패할 경우 커피를 사야합니다 ^^ (커피를 얻어 먹으려는 것이 아닌 소스코드를 푸시하기 전 잘 확인하자는 취지입니다) AWSEC2 : 사운들리의 대부분의 구성 요소인 API서버와 로그 수집, 분석 서버, 엘라스틱서치, 플럼, CMS등이 모두 EC2에 구축되어 있습니다.RDS : 컨텐츠의 주 저장소로 데이터베이스 관리의 용이성을 고려하여 RDS의 Multi-AZ에 배포하여 Active-Standby로 구성되어 있으며 이 데이터들은 레디스와 로컬 캐시를 이용하여 API서버에서 활용하고 있습니다.S3 : 컨텐츠에 포함된 각종 정적 데이터들이 저장되며 수집된 로그들도 저장하여 보관됩니다. EMR : 로그 수집서버를 통해 S3에 저장된 로그들은 EMR을 이용해서 분석됩니다.Beanstalk : 개발 서버의 배포에 사용됩니다. 최근 IntelliJ의 플러그인이 업데이트 되면서 IntelliJ 15버전을 지원하게 되므로써 로컬에서 개발하고 개발 서버에 배포까지 편리하게 하고 있습니다. VPC : 인터넷이 필요 없는 서버들은 VPC 내부 private-zone에 배포 및 ELB를 통해 외부에서 접근하도록 구성되어 있습니다.<그림 2. AWS 배포 구성도>이상으로 사운들리에서 사용하고 있는 백엔드 소프트웨어들을 소개해 보았습니다. 적은 인력으로 빠르게 사업을 진행하는 스타트업에서는 비즈니스에 집중할 수 있도록 도와주는 다양한 툴이나 오픈소스를 이용하여 많은 도움을 받을 수 있는 것 같습니다. 또한 코드를 잘 작성하여 에러를 줄이는 것도 필요하지만 여유가 많지 않으면 최소한 제품의 에러에 빠르게 대응할 수 있도록 하는 방법도 필요한 것 같습니다.#사운들리 #개발 #개발자 #문제해결 #프레임워크 #스킬스택 #스택 #인사이트
조회수 1051

우리 제품의 USP를 정의하는 방법

안녕하세요. 오피노입니다. 저희는 데이터 분석을 기반으로 한 성과 최적화를 대행하는 퍼포먼스 마케팅 에이전시입니다. 저희가 진행하는 모든 마케팅 의사결정의 근거는 [데이터]입니다. 데이터는 거짓말을 하지 않고 확실하고 냉정하게 숫자로 성과를 표현해주기 때문이지요. 그래서 그 어떤 근거들 보다 정확합니다. 하지만 최근 제가 데이터를 기반으로 대행을 하다가 잃어버린 것들이 있습니다. 문제는 바로, "숫자에만 치중" 한다는 것이지요. 숫자에만 치중하게 되면, 어느 날 "우리 브랜드가 어떤 방향으로 가고 있나?", "KPI가 매출이라고 해서, 매출액을 올리는 데에만 급급하지 않은가" 고민하게 됩니다. 데이터를 기반으로 매체를 다루다가 , 어떤 한계에 봉착하는 느낌을 받았습니다. 더 이상 일정 부분에서 ROAS가 오르지 않는 한계점 같은 것이지요.네, 숫자에만 치중하게 되면 무언가 잘못되어가고 있다는 느낌을 지우지 못합니다. 어느새 이 제품의 브랜드는 사라지고, 이 브랜드가 고객들에게 다가가는 메시지는 할인 프로모션, 특가와 같은 '매출을 끌어올리기 위한 수단으로써의 메시지'만 있게 되는 것 같았습니다. 숫자는 반드시 마케팅 의사결정에 필요하지만, 과학의 시대인 21세기에도 [종교]라는 개념이 있는 걸 보면, 데이터와 숫자가 비즈니스의 전부가 아니란 생각도 자연스레 떠올릴 수 있습니다. 제품이 나아가야 할 방향, 즉 , USP(Unique Selling Point)의 부재에서 저는 비어있는 공간을 느꼈던 것이지요.그렇다면 마케팅원론에서 그렇게 질리도록 배운 USP는 도대체 무엇이고, 이 USP는 어떻게 정의해야 할까요? 잠깐 이 질문에 대답을 해봅시다 : "비슷한 제품들이 즐비하는 시장에서 당신의 제품을 어떻게 차별화되는가?어려운 질문이지요. 여러분과 저는 아마도, 여러 가지 생각들이 들 수 있을 것입니다. 예를 들면, [사용하기 쉽다거나, 내구성이 좋다거나, 다른 경쟁사 제품보다 훨씬 더 품질이 좋다거나]와 같은 것들을 떠올릴 수가 있습니다.하지만 지금 직관적으로 떠오르는 이 여러 가지 특장점들은 그저, 제품의 기술적인 특징에 지나지 않습니다. 대신에, 우리들만이 판매할 수 있는 고유한 특장점(Unique Selling Point : USP) 이 있을까요? USP는 간결하게 말해, 1) 우리의 제품이나 서비스가 고객들에게 감정적으로 제공할 수 있는 것(가치), 2) 사용자들의 불편함을 처리해주는 방법론(솔루션) 3) 시장에서의 다른 제품들과 더 나은 이유(차이점) 이 모든 3가지 조건을 충족시키는 한 가지의 특징입니다. 예를 들면, 경쟁사보다 조금 더 우위에 있는 우리 제품이 잠재고객들이 겪는 문제점을  해결하는데 중요한 영향을 미치는지 까지 파악할 필요가 있다는 것이지요. 모든 기술적으로 뛰어나거나 품질적으로 뛰어나거나 하는 등의 조건들을 배제하고, 위에서 말씀드린 이 3가지를 모두 만족시키는 하나의 USP를 만들어낸다면, 우리는 좀 더 체계적인 마케팅 전략과 세일링 프로세스를 구축할 수 있습니다.그래서, 어떻게 USP를 정의할까?제품의 특성을 정의하는 것을 정확히 찾아내는 것은 때로는, 아들 딸 중에서도 어떤 자녀를 더 좋아하는지를 선택하는 것과 비슷합니다. 아마도 우리는 우리 제품의 모든 면을 사랑하고, 그 제품이 가지고 있는 잠재성과 개개의 기능들도 좋아할 것입니다. 하지만, 고객들은 다릅니다. 제품을 보는 고객은 제품의 전체 모습을 좋아하는 것이 아니라 그 제품에서 자신의 문제를 해결해줄 수 있는 단 한 가지 면만 보고 구매 의사결정을 내립니다. 경쟁사가 많은 복잡한 시장에서 고객들은 한 제품의 모든 면을 볼 수 있도록 주의를 기울이지 않기 때문이지요. 고객들이 순간적으로 제품을 스쳐 지나가 볼 때, 그들의 문제를 해결할 수 있을만한 방안 또는 그들이 얻을 수 있는 가치를 얻지 못하면, 그들은 그대로 떠나버리기 마련입니다.우리 모두는 우리 브랜드에 대해서 색안경을 벗어야 할 필요가 있습니다. 여러분 제품의 강점과 약점을 철저하게 분석하고, 우리 제품의 강점이 우리 제품의 약점을 커버할 수 있을 만한지도 되물어보셔야 합니다. 마케팅 원론에서 그렇게나 배우던 SWOT 분석이 중요한 이유도 다 여기에 있지요. 우리 제품을 분석했다면, 경쟁사 제품 역시 분석이 필요합니다. 고객의 입장에서 느낄 수 있는 경쟁사 제품의 USP는 무엇이고 이들은 어떻게 마케팅을 하고 있는지 반드시 알아야만 하는 것입니다. 그런 다음, 추가적으로 우리 제품을 구매하는 사람들을 이해하기 위해, 우리 자신에게 질문을 던집니다.- 우리 고객들은 언제 우리 제품을 사용할까?- 우리 고객들은 우리 제품을 통해 어떤 경험을 할까? - 우리 고객들은 왜 다른 경쟁상품 대신 우리 것을 선택하였을까?- 그들의 구매 결정에 시발점이 되었던 메시지는 무엇이었을까? 저희 오피노가 Google Analytics나 Google Optimize와 같은 화려한 도구들로 도출해내는 데이터는 위에 열거한 4가지 질문에 대답을 얻기 위해서였습니다. 그렇습니다. 데이터는 대답을 얻기 위한 수단일 뿐, 그 데이터는 USP라는 거대한 요리 속에서 하나의 조미료에 불과합니다. USP는 이 데이터들에 [인간]이라는 거대한 재료를 첨가하고 나서야 비로소 완성될 수 있습니다.Revlon의 창업자는 이런 유명한 말을 남겼었죠. "공장에서, 우리는 립스틱을 만들고 있습니다. 하지만 광고에서, 우리는 희망을 팔고 있습니다."  제품 그 자체 기능에 집중을 하는 것이 아니라, 제품이 최종 구매자에게 제공해주는 가치에 집중을 하는 것이지요. 네 그렇습니다. 사실 우리는 우리 고객들이 제품을 구매하면서 느끼는 휴머니즘적인 가치, 그들의 감성에 대한 공감을 기반으로 마케팅 전략을 구축해야 합니다. 저는 고객들이 느끼는 이 추상적인 감성을 데이터화 시키는 것을 잘할 수 있던 것이지, 데이터 그 자체를 맹목적으로 쫓는 게 정답이 아니었단 걸 깨달았습니다.우리 제품의 기능적 우위 + 휴머니즘적 공감을 함께 결합하여 최종 USP를 만드는 것이지요.  기억해주세요. 데이터는 본질을 가져와주는 도구일 뿐, 그 자체가 목적이 되면 안 됩니다. 이상 센치해진 저만의 반성문이었습니다 :) 영감이 될 수 있는 글이 되었으면 좋겠습니다.퍼포먼스 마케팅 에이전시, 오피노 바로가기
조회수 1626

INSIDE KURLY: 상품위원회 편 1/2

매주 금요일, 컬리에서는 품목별 전문 MD가 컬리만의 70여 가지 기준에 맞춰 엄선한 상품을 직접 경험해보며 깐깐하게 검토하는 '상품위원회'가 열린다. 산지 곳곳에서 직접 공수해온 신선 채소부터 하루 전날 잡혀 식탁에 오른 갖가지 수산물, 새로운 푸드 트렌드를 제안하기 위한 생경한 이국의 식재료까지. 한 주간 채집한 1백여 개의 상품을 세밀하게 분석하며, 컬리가 소개하기에 가장 이상적인 가치를 지닌 상품을 골라내는 마지막 단계다. 생산자와 소비자 모두에게 옳은 서비스를 통해 나와 내 가족을 위한 상품을 선보이겠다는 컬리의 신념이 녹아있는 시간이기도 하다. 2015년 3월부터 꾸준히 이어오며 어느덧 200회를 넘긴 상품위원회의 현장에서 오간 흥미로운 대화를 기록했다. 김슬아대표: 이베리코 품종에 대해 얘기할 때 '왜 하필 세보일까'라고 많이들 하잖아요. 글쎄요. 왜 세보(Cebo)일까요?MD S: 이베리코 역시 한우처럼 등급이 나뉘어요. 베요타의 개체 수가 가장 적으니 그만큼 희귀성이 있죠. 대신 기름이 많고 가격이 비싸요. 이베리코의 높은 등급은 사실 건조육에서 그 가치가 발휘되는 거라 정육으로 먹는 고기에는 큰 영향이 없다고 봐요. 비육이나 운동량의 차이에 따라 육질이 조금 달라져요. 이번에 세보 등급 이베리코를 소개하는 가장 큰 목적은 이베리코 돼지 자체를 대중화하고 싶어서. 이베리코인데도 무항생제 한돈 가격 정도밖에 되지 않거든요. 김슬아대표: 드디어! 포항초 시즌이 되었군요. 포항초는 포항에서 나서 포항초인거죠? MD H: 네, 맞아요. 포항 쪽에서  재배한 시금치를 포항초라고 합니다. 시금치는 겨울 작물이죠. 노지에서 재배하려면 남부 지방에서만 가능해요. 일반 시금치보다 더 맛이 좋아서 겨울에는 포항초를 많이 찾으시더라고요.MD L: 포항초는 태풍이나 추위를 맞고 자라기 때문에 맛에서도 그쪽 지방의 특색이 느껴져요. 좀 더 달큼하고 깊은 맛이 나지요. 모양도 그렇고요. 밑단에서 나는 보랏빛 보이시죠? 포항초는 지금부터 3월까지 다룰 수 있습니다.  밑단을 잘 묶어두면 선도가 잘 유지됩니다. 김슬아대표: 정말 가지런하고 예쁘게 내어오셨네요.MD L: 마늘이랑 올리브만 해서 데쳤어요. 원래는 간을 더 해야 하는데 워낙 자체로 맛이 있어서 특별한 간을 할 필요가 없는 것 같아요.  포항초를 검색을 하면 포항초 샐러드 레시피가 많이 나와요. 그만큼 생식으로 많이 먹기도 하죠. MD J: 고트 치즈는 겨울철에 샤퀴테리와 치즈 많이 찾으실 것 같아서 가지고 왔습니다. 오리지널과 허니 고트를 준비했어요.김슬아대표: 컬리에서 쉐브르는 판매하고 있잖아요. 이 제품은 원산지가 어디죠? MD J: 쉐브르는 굉장히 부드러운 제형이고 이 고트 치즈 상품은 단단한 편입니다. 스페인에서 생산한 상품이에요.  산양유 89% 정도가 들어가 있고, 유통기한이 6개월로 좀 긴 편이라 산양유 거부감 없으신 분들은 조금씩 드셔보셔도 좋을 것 같아요.김슬아대표: 저는 완전 거부감 없고 정말 좋아해요. 괜찮아요. 맛있어요. 맛있네요. MD J: 산양유 향이 거부감이 엄청 드는 정도는 아니고, 무난하게 베이글에 곁들여 먹어도 괜찮은 정도인 것 같아요.MD S: 고트 찾으시는 분들이 치즈 입문자는 아니기 때문에 그리 낯설어 하시지 않을듯해요.김슬아대표: 먹어보니까 고트 치즈 치고는 굉장히 마일드한 편이네요. MD S: 이거 오믈렛에 넣으면 정말 맛있어요. MD J: 뒷맛에 나오는 양털 냄새 느껴지시죠? 김슬아대표: 워낙 부드러워서 양털 냄새 말씀 안 하셨으면 몰랐을 뻔했어요.MD J: 처음에는 잘 느껴지지 않는데, 먹다 보면 나중에 탁 치고 올라오는 맛이 있어요. 산양유도 마찬가지예요. 처음에는 우유랑 비슷한가 싶다가 뒤쪽에서 치고 올라오는 맛이 있죠. 오리지널 고트 치즈에 거부감이 있으신 분들은 허니 드셔보시면 좋을 거예요. 단맛이 느껴지기 때문에 특유의 향이 좀 덜 나거든요. 고트 치즈와 꿀은 굉장히 잘 어울리는 조합이에요. 빵에 올려 먹을 때도 견과류와 꿀을 같이 곁들이면 아주 맛있어요.MD S: 확실히 쉐브르보다 더 단단한 느낌이네요.김슬아대표: 산양유가 우유보다 지방이 적은 편이기는 한데 보다 더 지방을 많이 빼서 단단하게 만든 것 같고, 소프트 치즈 치고 제형이 독특하기는 하네요.CR P: 부서지는 질감에서 약간 하드 치즈의 특성이 느껴지기도 해요.김슬아대표: 원래 고트가 좀 그렇긴 해요. 안단테 치즈도 보시면, 딱 잘랐을 때 균일하게 부서지는 건 아니잖아요. 아마도 제형은 지방을 얼마나 뺐느냐일 거고요. 에디터 S: 보통 판매하는 고트 치즈들 보면 플라스틱 용기 안에 개폐를 할 수 있게 담겨있는데, 이 제품은 포장이 특이하네요.김슬아대표: 안단테 치즈 같은 경우는 원뿔 형태로 통을 만들거든요. 그렇게 하기 위해서 드레인 공정이 꽤 많이 들어가는 걸로 알고 있어요. 드레이닝을 많이 해서 시큼한 향도 많이 나는 게 아닐까요? 약간 신맛이 느껴지지 않으세요?"제197회 컬리 상품위원회에서 생긴 일,마켓컬리 YouTube에서 확인하실 수 있어요!"↓↓↓https://www.youtube.com/watch?v=82FynNhVPaA
조회수 7072

안드로이드 앱의 Persistent data를 제대로 암호화해 보자! (1/2)

들어가기오늘 소개해드릴 글은 안드로이드에서 좀 더 안전하게 파일 시스템에 데이터를 저장하는 방식에 관한 내용입니다. 이 글은 중급 이상, 상급 이하 안드로이드 개발자를 대상으로 작성했으며 완독하는데 약 20분 정도가 필요합니다. 최대한 쉽게 쓰려고 노력했습니다만 이 글이 잘 이해되지 않는 독자분들은 이 문서 말미의 더 보기 섹션에 링크된 외부 문서들을 읽어보시는 편이 좋습니다.1부에서는 Shared preferences 에 저장하는 데이터를 암호화 하는 방식에 대해 다루고 있으며, 2부에서는 데이터베이스를 암호화 하는 방식에 대해 다루겠습니다.내 앱의 데이터, 과연 유출로부터 안전할까?안드로이드 공식 사이트의 저장소 개발 가이드 문서는 데이터를 저장하는 여러 가지 방법을 소개하고 있습니다. 그 중 ‘내부 저장소’ 의 다음 특징은 눈여겨볼 만 합니다.기기의 내부 저장소에 파일을 직접 저장할 수 있습니다. 기본적으로, 내부 저장소에 저장된 파일은 해당 애플리케이션의 전용 파일이며 다른 애플리케이션(및 사용자)은 해당 파일에 액세스할 수 없습니다. 사용자가 애플리케이션을 제거하면 해당 캐시 파일은 제거됩니다.즉, 다른 애플리케이션에 노출하면 곤란한 중요한 정보들은 내부 저장소에 담아두면 안전하다고 할 수 있습니다. 하지만, 정말일까요? 다음 예제를 이용해 내부 저장소에 저장한 사용자의 중요한 정보를 어떻게 탈취하는지 알아보겠습니다. 예제 앱은 충성 사용자에게 보상하기 위해 사용자가 앱을 몇 번 실행시켰는지를 기록합니다.class AppRanTimesRecordingActivity : AppCompatActivity() {    privateval sharedPrefs by lazy {        // Shared preferences 는 Internal storage 에 저장된다.        getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)    }    private var appRanCount = 0    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        accessToSharedPrefs()        appRanCount++        Toast.makeText(applicationContext, "App has ran $appRanCount times!!", Toast.LENGTH_LONG).show()        finish()    }    override fun onDestroy() {        saveSharedPrefs()        super.onDestroy()    }    private fun accessToSharedPrefs() {         sharedPrefs.run { appRanCount = getInt(KEY_APP_RAN_COUNT, 0) }    }    private fun saveSharedPrefs() {        sharedPrefs.edit().run({            putInt(KEY_APP_RAN_COUNT, appRanCount)            apply()        })    }    companion object {        private const val SHARED_PREF_NAME = "MySecureSettings"        private const val KEY_APP_RAN_COUNT = "appRanCount"    } } [리스트 1] MODE_PRIVATE 로 보호하는 SharedPreferences 사용앱의 데이터는 /data/data/com.securecompany.secureapp 에 저장되어 있습니다만, 앱을 release 모드로 빌드하면 adb 명령으로도 볼 수 없으니 안전하다고 할 수 있을 겁니다. 실제로 adb 명령을 이용해 저장한 파일을 보려고 시도하면 아래와 같은 오류가 발생합니다.$ adb shell "run-as com.securecompany.secureapp ls -al /data/data/com.securecompany.secureapp" run-as: Package 'com.securecompany.secureapp' is not debuggable 그렇다면 디버거로도 볼 수 없으니 내부 저장소에 저장한 데이터가 안전하다고 말 할 수 있을까요?그렇지 않습니다! 안드로이드는 루팅이 매우 손쉬운 운영체제기 때문에 설령 release 모드로 빌드한 앱이라 하더라도 adb 명령을 이용해 모두 접근할 수 있습니다. 루팅한 기기에서 우리가 제작한 SecureApp의 내부 저장소 구조를 아래와 같이 확인할 수 있습니다.$ adb shell "sudo ls -al /data/data/com.securecompany.secureapp" drwxrwx--x u0_a431 u0_a431 2018-06-04 14:15 cache drwxrwx--x u0_a431  u0_a431           2018-06-04 14:15 code_cache drwxrwx--x u0_a431  u0_a431           2018-06-04 14:15 shared_prefs $ adb shell "sudo ls -al /data/data/com.securecompany.secureapp/shared_prefs" -rw-rw---- u0_a431 u0_a431 111 2018-06-04 14:15 MySecureSettings.xml $ adb shell "sudo cat /data/data/com.securecompany.secureapp/shared_prefs/MySecureSettings.xml" <?xml version='1.0' encoding='utf-8' standalone='yes' ?>     별다른 테크닉이 없더라도 인터넷에 널린 수많은 루팅 방법으로 기기를 루팅하면 제아무리 내부 저장소에 저장한 데이터라도 이렇게 손 쉽게 유출이 가능하다는 것을 확인할 수 있습니다. 이런 방식의 보안 기법은 불투명성에 의지한 보안이라고 하여, 방법을 전혀 모르는 공격자에게는 유효한 방식입니다만 이 글을 읽는 독자 수준의 개발자라면 취약점을 금세 파악할 수 있다는 단점이 있습니다.그렇다면 암호화를 적용하면 되지 않을까?맞습니다. 어차피 유출을 피할 수 없다면, 데이터를 암호화하면 됩니다. 그래서 암호화 로직으로 데이터를 암호화해 보도록 하겠습니다. 이 코드는 AES / CBC / PKCS5Padding 방식을 사용해 주어진 데이터를 암호화합니다. 각 용어를 간략하게 설명하자면 다음과 같습니다.AES: 미국에서 개발된 블럭 암호화 방식으로 좀 더 나은 보안성을 가진다. 데이터를 일정 크기(블럭)로 나눠 암호화하며 보통 128비트, 192비트, 256비트 단위로 암호화한다. 키의 길이는 암호화 방식에서 사용할 블럭 크기와 완전히 같아야 하는 특징이 있다.CBC: 블럭을 회전시키는 방식을 말한다. 최초로 소개된 블럭 회전 알고리즘인 ECB(Electronic Code Book) 의 보안 취약점을 해결하기 위한 방식으로 같은 데이터 입력에 대해 완전히 다른 결과를 내므로 보안성이 좀 더 높다. 하지만 CBC 방식을 위해서는 초기화 벡터(Initialisation Vector, IV)를 반드시 사용해야 한다.IV : CBC 블럭 회전방식에 사용하는 초기화 값. 암호화할 데이터와 키가 변하지 않더라도 이 값만 바뀌면 결과가 크게 달라진다. 암호화 key 와는 전혀 무관한 값이기 때문에 외부에 노출되더라도 보안 위협은 적은 편이며 암호화 요청마다 다른 IV 를 사용해 보안성을 높일 수 있다. 다만, 키 길이와 일치하는 길이의 IV 가 필요하다.PKCS5Padding: 블럭 암호화 방식은 입력 데이터의 길이가 블럭의 길이 혹은 그 배수와 일치해야 하는 문제점이 있다. 입력 데이터가 블럭 길이보다 짧을 경우 원칙적으로 암호화가 불가능하다. 이런 어이없는 단점을 보완하기 위한 방식으로, 입력 데이터를 강제로 블럭 크기만큼 맞춰주는 알고리즘의 일종이다.object AESHelper {    /** 키를 외부에 저장할 경우 유출 위험이 있으니까 소스 코드 내에 숨겨둔다. 길이는 16자여야 한다. */    private const val SECRET_KEY = "HelloWorld!!@#$%"    private const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5PADDING"    fun encrypt(plainText: String, initVector: String): String {        val cipherText = try {            with(Cipher.getInstance(CIPHER_TRANSFORMATION), {                init(Cipher.ENCRYPT_MODE,                         SecretKeySpec(SECRET_KEY.toByteArray(), "AES"),                         IvParameterSpec(initVector.toByteArray()))                return@with doFinal(plainText.toByteArray())            })        } catch (e: GeneralSecurityException) {            // 특정 국가 혹은 저사양 기기에서는 알고리즘 지원하지 않을 수 있음. 특히 중국/인도 대상 기기            e.printStackTrace()            ""        }        return Base64.encodeToString(cipherText, Base64.DEFAULT)    }    fun decrypt(base64CipherText: String, initVector: String): String {        val plainTextBytes = try {            with(Cipher.getInstance(CIPHER_TRANSFORMATION), {                init(Cipher.DECRYPT_MODE,                        SecretKeySpec(SECRET_KEY.toByteArray(), "AES"),                        IvParameterSpec(initVector.toByteArray()))                val cipherText = Base64.decode(base64CipherText, Base64.DEFAULT)                return@with doFinal(cipherText)            })        } catch (e: GeneralSecurityException) {            // 특정 국가 혹은 저사양 기기에서는 알고리즘 지원하지 않을 수 있음. 특히 중국/인도 대상 기기            e.printStackTrace()            ByteArray(0, { i -> 0 })        }        return String(plainTextBytes)    } } [리스트 2] 간단히 구현한 AES128 암호 및 해독 로직그리고 위의 AESHelper 를 이용해 SharedPreference 에 들어갈 자료를 암호화해 봅시다.class MainActivity : AppCompatActivity() {    privateval iv by lazy { lazyInitIv() }    privateval sharedPrefs by lazy {        getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)    }    private var appRanCount = 0    override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main) // Shared preferences 는 Internal storage 에 저장된다. accessToSharedPrefs()        appRanCount++ Toast.makeText(applicationContext, "App has ran $appRanCount times!!", Toast.LENGTH_LONG).show()    }    override fun onDestroy() {        saveSharedPrefs()        super.onDestroy()    }    private fun accessToSharedPrefs() {        sharedPrefs.run({            val appRanCntEncrypted = getString(KEY_APP_RAN_COUNT, "")            if (appRanCntEncrypted.isEmpty()) {                return@run            }            appRanCount = AESHelper.decrypt(appRanCntEncrypted, iv).toInt()        })    }    private fun saveSharedPrefs() {        sharedPrefs.edit().run({            putString(KEY_APP_RAN_COUNT, AESHelper.encrypt(appRanCount.toString(), iv))             apply()        })    }    private fun lazyInitIv(): String {        return sharedPrefs.run({            var iv = getString(KEY_SESSION_IV, "")            if (iv.isEmpty()) {                // 2001년 - 2286년 동안에는 항상 13자리로 나타난다. 그러므로 16자리 IV가 보장된다.                iv = "${System.currentTimeMillis()}000"                edit()                    .putString(KEY_SESSION_IV, iv)                    .apply()            }            return@run iv        })    }    companion object {        private const val SHARED_PREF_NAME = "MySecureSettings"        private const val KEY_APP_RAN_COUNT = "appRanCount"        private const val KEY_SESSION_IV    = "ivForSession"    } } [리스트 3] 리스트 2를 활용해 데이터를 암호화해 저장.저장한 SharedPreferences 를 확인해 보면 다음과 같은 결과를 얻을 수 있습니다.$ adb shell "sudo cat /data/data/com.securecompany.secureapp/shared_prefs/MySecureSettings.xml" <?xml version='1.0' encoding='utf-8' standalone='yes' ?>    1528095873216000    F9dq8ezypMPeUsHpPIUcnQ==     역시 기대대로 암호화되었네요. IV 는 노출돼도 상관없는 정보라고 했으니 괜찮겠죠. 이제 우리 앱의 사용자는 설령 기기를 잃어버리더라도 소중한 정보가 암호화되어 있으니 문제없을 겁니다.라고 생각한다면 오산입니다! 불행히도 안드로이드는 디컴파일이 매우 쉬운 플랫폼이기 때문에 이런 식의 암호화는 사실 그다지 효과가 있지 않습니다. 심지어 IV 가 그대로 노출되어 있기 때문에 공격자에게 큰 힌트가 되었습니다. IV 는 키와는 다른 값이므로 유출되어도 상관없다곤 하지만, 어쨌든 암호화 과정에서 중요하게 다뤄지는 정보임에는 매한가지이므로 사용자에게 굳이 노출할 필요는 없습니다.다소 극단적인 예를 들어 설명했습니다만 요지는 이렇습니다. 어떤 식으로든 우리의 로직 내에서 키를 관리하는 방식으로는 완벽하게 암호화했다고 말할 수 없습니다. AESHelper 소스의 첫 줄에 있는 내용을 다시 한번 살펴봅시다. /** 키를 외부에 저장할 경우 유출 위험이 있으니까 소스 코드내에 숨겨둔다. 길이는 16자여야 한다. */    private const val SECRET_KEY = "HelloWorld!!@#$%" 불행히도 이 소스에 적힌 코멘트는 틀렸습니다. jadx 나 bytecode-viewer 로 획득한 우리 앱의 APK 파일을 디컴파일 해 봅시다.@Metadata(   mv = {1, 1, 10},   bv = {1, 0, 2},   k = 1,   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0007\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0016\u0010\u0006\u001a\u00020\u00042\u0006\u0010\u0007\u001a\u00020\u00042\u0006\u0010\b\u001a\u00020\u0004J\u0016\u0010\t\u001a\u00020\u00042\u0006\u0010\n\u001a\u00020\u00042\u0006\u0010\b\u001a\u00020\u0004R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082T¢\u0006\u0002\n\u0000R\u000e\u0010\u0005\u001a\u00020\u0004X\u0082T¢\u0006\u0002\n\u0000¨\u0006\u000b"},   d2 = {"Lcom/securecompany/secureapp/AESHelper;", "", "()V", "CIPHER_TRANSFORMATION", "", "SECRET_KEY", "decrypt", "base64CipherText", "initVector", "encrypt", "plainText", "production sources for module app"} ) public final class zzw {   private static final String A = "HelloWorld!!@#$%";   private static final String B = "AES/CBC/PKCS5PADDING";   public static final zzw INSTANCE; // ... } [리스트 4] 리스트 2를 디컴파일한 결과. 키가 그대로 노출됨을 확인할 수 있다.이름은 난독화했지만 문자열이 그대로 노출된 상태이므로 공격자가 단서를 찾기란 매우 쉬울 겁니다. 더군다나 원본 소스의 내용이 짧으니 아무리 난독화 했더라도 내용을 파악하기란 그리 어렵지도 않을 것이고요.여기서 일부 독자분들은 ‘그럼 이 로직을 JNI 로 만들면 되지 않냐?’ 라고 반문하실 수도 있습니다. 하지만 JNI 로 컴파일한 .so 파일조차 objdump 같은 명령으로 내용을 다 들춰볼 수 있습니다. 특히 Kotlin 구현처럼 static const 형태로 소스코드에 적어두면 공격자 입장에서는 .data 세그먼트 만 확인하면 되죠. 그렇다면 .data 세그먼트를 회피하기 위해 로직으로 키를 생성하도록 작성했다고 해 봅시다. 좀 더 난이도가 올라가긴 하겠지만 숙련된 공격자라면 .text 세그먼트를 이 잡듯이 뒤져 실마리를 찾을 수 있을 겁니다. 물론 이 정도 수준의 역공학을 할 수 있는 사람의 수는 적지만, 아예 없지는 않으니 문제는 여전히 남아 있습니다. 한번 확인해 볼까요?static const char* SECRET_KEY = "HelloWorld!!@#$%" void encrypt(char* plainText, char* initVector, char[] result) {    char* now = malloc(sizeof(char) * 13);    itoa(&time(NULL), now, 10);    char* iv = malloc(sizeof(char) * 16);    strcpy(iv, *now);    strncpy(iv, "000", 3);   const struct AES_ctx aesCtx = { .RoundKey = 16, .Iv = *iv }    AES_init_ctx(aesCtx, SECRET_KEY);    // ... } [리스트 5] C 로 작성한 AESHelper 로직(일부).$ objdump "mySecureApp/build/obj/local/armeabi-v7a/libAESHelper.so" section .data    # 의미 불명의 문자열 발견! 혹시 key 는 아닐까???    00000200 db "HelloWorld!!@#$%", 16    00000210 equ $ - 00000200 section .text    global _start    _start:    # ...        mov rsi, 00000200  # 이 명령 앞뒤로 조사해보면 저 문자열의 용도를 파악할 수 있다.        mov rdx, 00000210        syscall    # ... [리스트 6] ARM EABI V7용으로 컴파일한 바이너리를 디스어셈블 한 결과.즉, 어떤 방식으로 구현하건 암호화에 쓸 키를 소스 코드에 박아두는 것은 그다지 현명한 선택이 아니란 것입니다. 더군다나 안드로이드에서 앱을 만든다는 것은 내 로직이 공격자에게 낱낱이 까발려져 있다는 것을 의미합니다. 중요한 데이터를 .text 에 들어가도록 숨기는 것도 가능하긴 하지만, 그런 방식은 나중에 유지보수하는 사람에게도 골치 아플 겁니다. 소스 코드가 그만큼 어려워질 테니까요. 그리고 그런 방식으로 정보를 숨긴다 하더라도 최정예 크래커 집단, 예를 들어 국정원 같은 수준이라면 그 정도는 큰 어려움 없이 파훼 가능합니다.꿈도 희망도 없는 상황처럼 보입니다만 다행히도 안드로이드는 이런 문제를 해결해 주는 KeyStore API 를 제공하고 있습니다.KeyStore 를 도입하자Android KeyStore 시스템 문서의 첫 머리에 적혀있는 글은 다음과 같습니다.The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device. Once keys are in the keystore, they can be used for cryptographic operations with the key material remaining non-exportable. Moreover, it offers facilities to restrict when and how keys can be used, such as requiring user authentication for key use or restricting keys to be used only in certain cryptographic modes.Android Keystore 시스템은 암호화 키를 ‘컨테이너’ 에 저장하도록 해 기기에서 키를 추출하기 더욱 어렵게 해 줍니다. 일단 키를 Keystore 에 저장하면 키를 추출 불가능한 상태로 암호화에 사용할 수 있습니다. 또한 Keystore 는 키 사용 시기와 방법(예: 사용자 인증 등의 상황)을 통제하고, 특정 암호화에서만 키를 사용하도록 허용하는 기능도 제공합니다.좀더 쉽게 다시 설명하자면, 암호화에 쓸 키를 소스코드 내부 어딘가가 아니라, 시스템만이 접근 가능한 어딘가(컨테이너)에 저장해 문제를 해결해 준다는 뜻입니다. 여기서 키가 저장되는 ‘컨테이너’ 는 기기별로 구현이 다를 수 있습니다만 핵심은 사용자 어플리케이션이 그 영역에 접근할 수 없다는 점입니다. 이 때문에 KeyStore 를 사용해서 키를 안전하게 저장할 수 있습니다.또한 앱에서 등록한 KeyStore 는 앱 삭제 시 함께 제거되므로, 똑같은 package name 으로 앱을 덮어씌우는 등의 공격으로 키를 유출할 수도 없습니다. 이는 여러 앱에서 공유하는 KeyChain 과는 다른 특성이며 기능 활성화를 위한 별도의 입력이 필요 없다는 장점이 있습니다.[그림 1] KeyChain API 사용시 나타나는 시스템 다이얼로그. 어려운 용어가 난무하는 등 사용자 경험이 그다지 좋다고 말할 수 없다.반면 Android M 이상에서는, KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean) API 로 시스템 다이얼로그의 표시 유무를 제어할 수 있습니다.Secure SharedPreferences 구현하기앞서 설명드렸던 KeyStore 를 사용해 SharedPreferences 의 내용을 암호화하는 로직입니다. 소스 코드의 길이가 꽤 길기에, github gist 링크로 대신합니다. 독자 여러분들을 위해 최대한 쉽고 간단한 형태로 구현했으므로 필요에 맞춰 커스터마이징 하는게 좋습니다.AndroidCipherHelper.kt - KeyStore 에서 생성한 랜덤 패스워드를 이용해 입력받은 문자열을 암호화 하는 로직. IV 설정 등 귀찮은 작업을 피하기 위해 비대칭 암호화 알고리즘을 사용했다. 또한 암호화 및 복호화 과정에서 비대칭키의 Public key 로 암호화하고, Private key 로 해독하도록 구현했다. TEE 를 올바르게 구현한 기기(안드로이드 23 이상 + 메이저 하드웨어 제조사)에서 동작하는 한, 이 데이터의 내용이 유출되더라도 복호화는 오직 이 로직 내부에서만 할 수 있다.SecureSharedPreferences.kt - AndroidCipherHelper 가 문자열 위주로 암호화하므로, 모든 입력값을 문자 형태로 변환 후 입출력한다.결과 확인Secure SharedPreferences 를 실제로 구현한 뒤, 앱의 shared preferences 를 열어보면 아래와 같은 결과가 나타납니다.$ adb shell "run-as com.securecompany.secureapp cat /data/data/com.securecompany.secureapp/shared_prefs/MySecureSettings.xml" <?xml version='1.0' encoding='utf-8' standalone='yes' ?>     oh+XL/vQqAdxNzFEkKVOfcZAkP7jh92tcKpxzM6bbv9iGUk2lR7ayJsR6FZXt3rAKC+4sLVTP1cy e+NpgZ67wjoeBM4maMjXjSkovc8cO8rVVsQLqedJtW3gGOItTTCkjIQGh+TsBDjz8C3IdmNSKqGE GmBwQBoV0QuO+uO6cdPI/Gx816P0kcLmr5xsAy9XUwJeTE9947sYydiztJsgkKxuiGFLJK435pAb UhatjSFse4MpBCugHcLUVg5UXGwQcfbJuuQ/CBcmQmYb3MldNzLfOWtsQiwQJpz0J12fsYlQOBnO UnLVcND+DU17cP+Q4Cjah8VwmiY1a0shMn09Rw==         ozh8dKH+yCRSWoiW0HQtF/bWD7Aw6rfjzklT302AlTOpYmVdEiIfVoTK97bsyK1mXbwN5Qpas82Q dYgnnZl9sfY8pzyXHM0dtm88euB5vgmzljb04LClF3oRZ7Qi5ZRyK90kQ/HN/6EgYvf6zEwR7Ydg 08kJ/bde4Z5lSz+kJ79dHEpE+QAV48U0F0/yp12+xKFRNbaBLBaaWclUNF10jONPKjC3HS/aQozT 1ngQWSKzPq87B0OFExraSPDoLT8zx8ElhTgEtpBRcUwtzmSnhGvgtIUhziFpZBbdvuqAGZ+L5El1 T7H9ipEosN3Aivh/5rz9dntJe3mJvfCFdFITlA==     (Android L 이상이라고 가정할 경우)앱의 개발자조차 키를 알 수 없기 때문에, 파일을 유출하더라도 이를 깨는것은 현재로선 매우 어렵습니다. 즉, 우리 앱은 사용자 데이터를 안전하게 보호하고 있다고 자신 있게 말할 수 있습니다.AndroidKeyStore 파헤쳐보기그렇다면 어떤 방식으로 AndroidKeyStore 가 동작하고, 왜 안전한지 좀더 상세히 살펴보겠습니다.“AndroidKeyStore” 문자열의 중요성Android Keystore system 문서에 따르면 Android Keystore Service 에 접근하기 위해서는 아래와 같이 코드를 작성해야 한다고 합니다. val keyStore = java.security.KeyStore.getInstance("AndroidKeyStore") [리스트 7] Android Keystore 인스턴스 획득 방법여기서 주의할 점은 AndroidKeyStore 라는 문자열입니다. 반드시 정확한 문자열로 입력해야 합니다. 왜냐면 이는 Google 이 안드로이드의 보안 시스템을 Java Cryptography Architecture(JCA) 표준에 맞춰 구현했기 때문에 그렇습니다. 그리고 JCA 표준을 구현하면 JVM 인스턴스(안드로이드도 변형 JVM 의 일종입니다) 내에서 동작하는 모든 로직이 Security 클래스에 등록된 암호화 구현체를 사용할 수 있게 됩니다. 즉, Google 이 컨트롤 할 수 없는 서드파티 로직(우리의 앱 혹은 각종 안드로이드 오픈 소스들)에서도 Android Keystore 를 표준 Java 방식으로 사용할 수 있도록 구현했기에 이런 방식으로 호출해야 하는 겁니다.물론 안드로이드에서는 AIDL 파일을 제공받는 방식 혹은 Context#getSystemService(String) 메소드로 서비스 인스턴스를 획득할 수도 있습니다. 하지만 첫 번째 방식은 바인드된 서비스가 언제든 Kill 될 수 있다는 문제가 있습니다. 그리고 두 방식의 공통적인 문제점은 보안을 사용하는 모든 로직에 if (currentEnvironment == "Android") then... 같은 예외 처리 로직을 넣어줘야 한다는 점입니다. 전 세계 모든 오픈소스 개발자들이 안드로이드로의 포팅을 위해 그런 귀찮은 작업을 해 줘야 하는 일인데.. 그게 가능할까요?“AndroidKeyStore” JCA Provider 등록 과정앞서 AndroidKeyStore 라는 문자열의 중요성을 알아봤습니다. 그렇다면 왜 중요한지도 알아두면 좋겠죠?안드로이드는 linux 기반의 운영체제입니다. 시스템 부팅 직후 실행되는 init.rc 스크립트에서는 /system/bin/app_process 명령을 실행하는데 이 명령은 Android Runtime 위에서 실행되는 Zygote process를 초기화 합니다.Zygote 는 간단하게 설명하자면 안드로이드 앱 실행속도를 향상시키기 위한 일종의 공용 런타임 같은 것입니다. 그리고 앱이 실행되면 Zygote 에 설정된 내용이 사전에 로드되는데, 아까 언급한 초기화 과정 중에 아래와 같은 내용이 있습니다.package com.android.internal.os; /**  * Startup class for the zygote process.  *  * Pre-initializes some classes, and then waits for commands on a UNIX domain  * socket. Based on these commands, forks off child processes that inherit  * the initial state of the VM.  *  * Please see {@link ZygoteConnection.Arguments} for documentation on the  * client protocol.  *  * @hide  */ public class ZygoteInit {    private static final String TAG = "Zygote"; /**     * Register AndroidKeyStoreProvider and warm up the providers that are already registered.      *     * By doing it here we avoid that each app does it when requesting a service from the      * provider for the first time.      */     private static void warmUpJcaProviders() {        // ...        // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert        // preferred providers. Note this is not done via security.properties as the JCA providers        // are not on the classpath in the case of, for example, raw dalvikvm runtimes.        AndroidKeyStoreProvider.install();        Log.i(TAG, "Installed AndroidKeyStoreProvider in "                 + (SystemClock.uptimeMillis() - startTime) + "ms.");        // ...    } // ... } [리스트 8] ZygoteInit.java 의 JCA provider 설치 및 속도향상 과정package android.security.keystore; /**  * A provider focused on providing JCA interfaces for the Android KeyStore. *  * @hide  */ public class AndroidKeyStoreProvider extends Provider {    public static final String PROVIDER_NAME = "AndroidKeyStore";    public AndroidKeyStoreProvider() {        super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");        // ...    } /**     * Installs a new instance of this provider.     */    public static void install() {        // ....        Security.addProvider(new AndroidKeyStoreProvider());        // ...    } } [리스트 9] AndroidKeyStoreProvider.java - “AndroidKeyStore” 라는 이름의 JCA provider 등록 과정이런 일련의 과정을 거쳐 시스템에서 등록한 AndroidKeyStore 라는 이름으로 Android KeyStore 서비스에 접근할 수 있게 됩니다. 그리고 안드로이드에서 사용 가능한 KeyStore provider 들의 종류를 뽑아보면, 아래와 같은 결과가 나타납니다.// List all security providers for (Provider p : java.security.Security.getProviders()) {    System.out.println(String.format("== %s ==", p.getName()));    for (Provider.Service s : p.getServices()) {        System.out.println(String.format("- %s", s.getAlgorithm()));    } } output: == AndroidKeyStoreBCWorkaround == == AndroidOpenSSL == ... == AndroidKeyStore ==    - AndroidKeyStore     - HmacSHA256    - AES    ... [리스트 10] 안드로이드 M(6.0.1)에서 지원하는 KeyStore provider 목록(중요) AndroidKeyStore 의 Hardware 레벨 지원 여부 확인다시 Android KeyStore 시스템의 설명으로 돌아가 봅시다.Key material of Android Keystore keys is protected from extraction using two security measures:…Key material may be bound to the secure hardware (e.g., Trusted Execution Environment (TEE), Secure Element (SE)) of the Android device. When this feature is enabled for a key, its key material is never exposed outside of secure hardware.Android KeyStore 는 키의 추출을 방지하기 위해 두 가지 보안 조치를 사용합니다:…키는 안드로이드 기기의 보안 하드웨어(e.g., Trusted Execution Environment (TEE), Secure Element (SE)) 에서만 동작할 수 있습니다. 이 기능이 활성화되면 키는 절대로 보안 하드웨어 밖으로 노출되지 않습니다.그런가보다 싶지만 유심히 읽어봐야 할 대목이 있습니다. 바로 Key material may be bound to … 부분입니다. is 가 아니라 may be 랍니다. 즉, 키가 하드웨어에 저장되지 않을 수도 있다는 사실입니다. 물론 문서에는 언급되어 있지 않지만 안드로이드 시스템 특징상 제조원가 절감을 위해 디바이스 제조사들이 KeyStore 를 소프트웨어로 구현할 수도 있다는 뜻입니다. AOSP 의 Keymaster 구현을 살펴보면 sw_enforced 라는 키워드가 있습니다. 이 keymaster API 를 하드웨어 제조사에서 Keymaster HAL 을 통해 호출하는데 만약 sw_enforced 인스턴스를 넘기는 형태로 구현할 경우 그 하드웨어는 KeyStore 를 지원하지만 (API Level 18), 그것이 반드시 별도의 보안 하드웨어 위에서 동작한다고 말할 수는 없습니다.그리고 “Inside Android Security” 의 저자 Nicolay Elenkov 에 의하면 Android M 이전의 Software-backed KeyStore 는 root 된 기기에서 유출 가능하다고 합니다. 링크의 내용이 다소 길기 때문에 요약하자면 software 기반의 KeyStore 구현은 키를 /data/misc/keystore/user_X(여기서 X 는 uid - 시스템이 앱마다 부여하는 id)에 저장하는데 이 파일의 내용은 keystore-decryptor 로 풀어볼 수 있다고 합니다. 그리고 하드웨어 보안을 지원하지 않는 기기를 확보하지 못해 실 기기에서는 확인할 수 없었습니다만, 에뮬레이터에서 실제로 확인해 본 결과 사실이었습니다.즉, (Android KeyStore)를 쓰더라도 Android M 이전의 기기에서는 우리 앱의 데이터가 100% 안전하다는 장담을 할 수는 없습니다. 아직까지 이 문제를 해결할 방법은 찾지 못했습니다만 아래와 같은 로직으로 ‘이 기기에서의 앱 실행은 안전하지 않을 수 있다’ 같은 안내를 띄우는 정도의 가이드는 개발 가능합니다.val privKey = (keyEntry as KeyStore.PrivateKeyEntry).privateKey val factory = KeyFactory.getInstance(privKey.getAlgorithm(), "AndroidKeyStore") val keyInfo: KeyInfo try {    keyInfo = factory.getKeySpec(privKey, KeyInfo::class.java)    println("HARDWARE-BACKED KEY???? " + keyInfo.isInsideSecureHardware) } catch (e: InvalidKeySpecException) {    // Not an Android KeyStore key. e.printStackTrace() } [리스트 11] KeyInfo API 로 키가 하드웨어로 안전하게 보호되고 있는지를 확인하는 방법다행히도 저희가 보유 중인 개발 시료에서 모두 확인해본 결과 모두 true 로 확인되는 것으로 보아 전 세계의 대중적인 API Level 18 이상인 Android 기기에서는 KeyStore 를 안심하고 사용할 수 있다는 결론을 얻었습니다.다만 API Level L 이전의 Android KeyStore 에는 사용자가 Lock screen 을 설정하지 않을 경우 초기화 된다거나, 직접 확인하진 못했지만 앱을 삭제하더라도 KeyStore 가 완전히 초기화되지 않는 등의 문제도 있다고 하니 유의하는 것이 좋겠습니다.맺으며이상으로 KeyStore 를 사용해 데이터를 암호화하는 방법에 대해 알아봤습니다. 저희 하이퍼커넥트에서도 현재 제작 중인 안드로이드 앱 일부에서 이 기능을 탑재해 고객 여러분들의 데이터를 안전하게 보호하려 노력하고 있습니다. 또한 iOS 도 Secure enclave라 하여 비슷한 기능을 제공하고 있으며 역시 저희 개발진은 이 기술의 적극 도입을 위한 노력을 진행 중입니다.물론 여기 적혀있는 내용들은 Android M(API Level 23) 이후에서만 100% 안전하기 때문에 저희는 그 이전의 안드로이드 버전에서도 데이터를 안전하게 저장할 방법에 대해 지금도 계속 고민 중입니다.또한 눈치 빠른 독자분들은 이 기법을 잘 응용하면 외부 저장소에 저장하는 파일도 암호화 할 수 있다는 사실을 깨달으셨을 겁니다. 이 기법은 요즘 데이터 불법 유출로 몸살을 앓고 있는 웹툰 앱들에도 유용합니다. 임시로 다운로드 한 이미지 파일을 KeyStore 가 생성해주는 키로 암호화해 버리고, WindowManager.LayoutParams#FLAG_SECURE 를 사용해 화면 캡쳐까지도 막아버린다면 대부분의 어설픈 유출 시도는 손쉽게 막으실 수 있으리라 생각합니다.꽤 길었던 1부가 끝났습니다. 2부에서는, 2017년 5월에 소개된 Room을 사용한 안드로이드 데이터베이스를 암호화하는 법에 대해 소개하겠습니다.더 보기Android KeyStore 시스템블록 암호 운용 방식초기화 벡터문자 인코딩AES 암호화RSA 암호화Padding(Cryptography)AOSP KeyStore implementation requirementsHow the Android keystore system can be secureJCA reference guideUnderstanding Android zygote and DalvikVMAndroid InternalsKeystore redesign in Android M - by Nicolay ElenkovAnalysis of Secure Key Storage Solutions on Android#하이퍼커넥트 #개발 #개발자 #안드로이드 #모바일 #앱개발 #PersistentData #개발후기 #인사이트
조회수 778

코호트 분석(Cohort Analysis)

올해 1월, Google Analytics(이하 GA)에서 Audience 카테고리에 Cohort Analysis(코호트 분석)라는 리포트가 추가되었습니다. 그런데 UI가 늘 보던 리포트와 달리 독특해서 이 리포트는 어떻게 데이터를 보고 해석해야 할지 막막하기까지 합니다. 일단 리포트보다 용어조차 생소한 코호트에 대한 이해가 필요합니다.( GA > Audience > Cohort Analysis Report )코호트 분석이란코호트 : 특정 기간에 특정의 경험을 공유한 사람들의 집합http://en.Wikipedia.org/wiki/Cohort_(statistics)코호트 분석 : 특정 기간에 특정의 경험을 공유한 집단간의 행동패턴을 비교/분석http://en.Wikipedia.org/wiki/Cohort_analysis리포트 조회 방법모바일 앱 분석에서 가장 많이 사용하는 코호트 분석은 같은 기간에 앱 설치를 경험한 사용자 그룹이 시간이 지남에 따라 앱의 꾸준한 사용여부(Retention)를 분석하는 것입니다. 앱은 설치보다 지속적인 재사용성이 앱 비즈니스를 좌우하기 때문입니다.( WISETRACKER > 방문행동 > Retention 리포트 )A열은 특정 기간에 앱을 설치한 사용자의 집단이며, +기간이 표기된 B열은 설치 시점으로부터 재사용율/삭제율을 제공하고 있는데, 여기서 데이터를 해석하기 위해선 데이터를 수직적으로 바라봐야 합니다.위 데이터로 예를 들면 다음과 같습니다.A : 2016년 12월 1일부터 5일까지 설치수가 꾸준히 증가하고 있다.B : 설치 후 하루가 지난 뒤 재사용률은 지속적으로 떨어지고 있고 오히려 삭제율이 증가하고 있다.코호트 분석 왜 필요한가첫째, 비즈니스 상황을 알 수 있다.위 그림의 데이터를 단순 앱 설치 추세 리포트로 보았다고 생각해봅시다. 설치수가 꾸준히 증가하고 있기 때문에 “우리 앱의 시장반응이 좋구나”라는 1차원적인 결과만 얻었을 것입니다. 그러나 코호트 분석을 통해 신규 고객 획득은 잘 이루어지고 있으나, 고객이 된 이후의 사용성이 떨어지고 앱을 삭제하는 비율이 점점 증가하고 있다는 사실을 인지함으로, 마케팅은 밑 빠진 독에 물 붓는 격이니 보다 고객관리/최적화에 먼저 집중해야 함을 알 수 있습니다. 이처럼 현재의 상황을 정확히 이해해야 더 나은 의사결정을 할 수 있습니다.둘째, 깊은 마케팅 인사이트 얻을 수 있다.보통 모바일 마케팅의 성과 지표로 얻을 수 있는 건 클릭수, 설치수 정도 입니다. 그러나 이것만으로 가치 채널을 도출하고 마케팅 전략을 수립하기엔 데이터가 부족한 것이 사실입니다. 같은 채널이라 하더라도 시점에 따라 게재하는 광고 내용도 다를 수 있고, 설치수가 많더라도 체리피커들 때문에 설치 후 바로 삭제하는(광고비만 날리는..) 비율도 꽤 높기 때문에 설치 이후의 데이터가 꼭 필요합니다. 코호트 분석은 특히 모바일 앱 기반의 스타트업에게 매우 중요한 분석기법으로, 사업 단계마다 우리가 잘하고 있는지 여부를 판단하고 더 나은 의사결정을 돕는 중요한 나침반 역할을 할 것입니다.
조회수 4538

KBS 신입사원 입사후기 - [최혁환] 포기하지마세요! 도전하면 문이 열립니다(촬영기자 편)

안녕하십니까. KBS 46기 촬영기자 최혁환입니다. 현재 연수를 끝내고 쉬고 있는 주말에 합격 후기를 작성하고 있는데, 어떠한 말을 먼저 해야 할지 감이 잡히지 않고 있습니다. 하지만 저의 이야기를 천천히 풀어 내보겠습니다. 저는 촬영분야 꿈을 키우면서 항상 KBS에 들어가고 싶었습니다. 영화와 사진을 전공했지만 방송분야로 진로를 선택하는 선·후배들이 많지 않았기에, 정보를 알 수 있는 길이 없었고, 모든 것을 혼자 준비했었습니다. 저는 KBS에 총 4번 지원했었습니다. 2014년과 2015년, 2017년, 2018년. 처음 지원한 2014년에는 모든 준비가 안 되어 있던 상태이기에 어떠한 전형으로, 어떻게 진행되는지 알고 싶어 지원하게 되었고, 운이 좋게 서류전형을 통과했지만 필기시험에서 탈락했었습니다. 조금은 준비된 2015년에는 최종 전형까지 가게 되었습니다. 2015년 필기시험을 치룬 후, 합격과 탈락이 전혀 감이 잡히지 않았습니다. 그만큼 필기시험을 치룬 후 개운치 않았던 것 이었습니다. 하지만 이번에도 운이 좋게 실무전형까지 가게 되었습니다. 실무전형을 본 후 분위기가 좋아 합격의 기운이 느껴졌습니다. 하지만 최종 전형은 실무전형과 전혀 달랐습니다. 열심히 준비를 하고 갔지만 긴장을 너무한 탓인지 질문에 올바른 대답도 하지 못했고, 탈락을 직감했었습니다. 직감대로 2015년 최종전형에서는 탈락을 했었습니다. 최종에서 탈락하고 힘들었었습니다. 누군가와 이별보다 힘이 들었고, 극복하는데 굉장한 시간이 소요되었습니다. 2016년에는 KBS 신입사원을 뽑지 않았습니다. 2017년에도 포기하고 현실에 집중하고 있었는데, 2017년 12월 크리스마스가 지나고 신입사원 공채가 있었습니다. ‘이 길을 포기할까?’라고 생각하던 순간 KBS 공채가 있었습니다. 2017년에도 필기를 치룬 후 합격과 탈락이 전혀 감이 잡히지 않았습니다. 다행히 이번에도 필기전형을 통과하고 실무전형과 최종면접에 가게 되었습니다. 2015년에 최종에서 떨어졌던 기억이 계속 났지만 그 기억을 잃어버리기 위해 혼자서 계속해서 ‘할 수 있다’라는 주문을 외우면서 면접장에 들어갔었습니다. 2015년 최종전형보다 많이 나아졌다고 생각했지만 이번에도 최종전형에서 탈락의 고배를 마시게 되었습니다. 2017년 최종전형 탈락은 여러 여행을 하면서 2015년 보다 쉽게 잊을 수 있었습니다. 그리고여행을 하면서 스스로에게 물어 보았습니다. ‘KBS가 정말 가고 싶은지’ 저의 답은 ‘내가 하고 싶고, 좋아하는 일이며, 여전히 가고 싶다’ 이었습니다. 2018년 전형은 2017년 보다 빨리 공채가 나왔고, 이전 공채와 필기전형 시험의 유형이 달랐습니다. 이전 공채는 주관식이었다면 이번 공채는 객관식과 주관식으로 문제가 출제되었습니다. 저는 긴장이 많은 편입니다. 그래서 처음 고사장에 입실하면 여러 사람들이 시험 자료들을 보지만 저는 자료를 봐도 읽히지 않는다는 것을 알기에 멍하게 있었습니다. 1교시 논술을 치르면서 점점 긴장이 풀리기 시작했고, 2교시 시사상식·KBS 문제는 긴장하지 않고 문제를 풀 수 있었습니다. 이전 공채 필기전형에서는 합격과 탈락이 전혀 감이 잡히지 않았지만, 이번 공채 필기전형을 본 후에는 마음이 편해졌고, 확신이 느껴졌습니다. 이번 공채는 3차 전형에서도 이전 공채와 다르게 직군별로 변화가 있었습니다. 저희 직군은 60분씩 5명이 면접에 들어갔었습니다. 60분 면접은 처음이었습니다. 면접장에 가면 시간이 빠르게 흘러가지만 60분은 너무 길다고 생각하며, 면접을 마치고 나왔습니다. 이전 공채에서는 2차 전형인 필기전형에서 감이 잡히지 않았다면, 이번 공채에서는 3차 전형에서 합격과 불합격의 감이 잡히지 않았습니다. 3차 전형 발표 날 마음을 졸이고 있었는데, 합격문자를 받았습니다. 3차 전형 합격이었지만, 지금까지 어떠한 합격보다 기쁜 합격이었습니다. 기쁨도 잠시, 4차 전형에서 두 번의 고배를 마신 기억이 있기에 이번에는 더욱 열심히 준비를 했었습니다. 일단 제 자신을 돌아보는 시간을 가졌습니다. 그리고 그것들을 토대로 질문들을 스스로 만들어 보았습니다. 4차 전형에서 면접관님들의 질문은 제가 만들었던 질문에서 많이 나왔고, 그것들을 토대로 대답을 했었습니다. 면접관님들은 제가 대답하면 세 번 정도 크게 웃으신 적도 있었습니다. 면접이 아니라 포근한 삼촌과 질의응답을 하는 느낌이었습니다. 그래서 이번 4차 전형은 다른 면접보다 시간이 빠르게 흘렀고, 갑자기 시간이 다 되었다는 말을 들었을 때 ‘벌써?’라는 생각을 했었습니다. 나가는 도중에 보도본부장님과 여러 면접관님들 재밌고 씩씩하다고 칭찬하는 말을 들을 수 있었고, 이번 4차 전형은 이전 4차와는 다르다는 생각을 했습니다. 발표당일은 항상 괴로운 날이었습니다. 이번에도 엄청난 괴로움에 몸부림치며, 결과를 기다렸습니다. 합격 문자를 받는 순간 이때까지의 전형과 이전 공채의 모습들이 파노라마처럼 지나가기 시작했습니다. 그리고 부모님과 가족, 친구, 선·후배들의 많은 축하를 받았습니다. 부모님을 비롯해 항상 저를 챙기던 친구와 선·후배들은 저에게 가장 소중한 존재이었습니다. 이들이 저에게 소중한 존재이었다는 것을 다시금 알게 되는 순간이었습니다. 평소에 저는 표현을 잘 하지 못하는 무뚝뚝한 사람입니다. 하지만 이 자리를 빌려 이들에게 고맙고, 사랑한다는 말을 해주고 싶습니다. 그리고 연수가 끝난 지 얼마 안 되었기에 먼저 KBS 인재개발원에서 일하시는 선배님들께 감사하다는 말을 드리고 싶습니다. 이번 연수는 지난 기수나 그전 기수에 비해 기간이 짧았다는 말을 들었습니다. 그리고 46기 동기들의 인원도 다른 기수보다 많았습니다. 많은 인원을 모두 신경써주시는 선배님들의 모습이 감동적이었고, 좋은 선배님들과 함께 열심히 일할 수 있다는 생각을 했었습니다. 인사팀 선배님들이나 연수원 선배님들이 저희에게 항상 해주셨던 말은 “KBS에 정말 잘 들어왔다”, “KBS는 정말 좋은 회사다” 이러한 말들이었습니다. 연수를 하면서 이러한 말을 실감하게 되었고, 정말 좋은 회사라는 것을 알 수 있었습니다. 현장에서 항상 국가와 국민들을 생각하는 촬영기자가 되도록 노력하고, KBS의 가치를 생각하며 열심히 일하는 촬영기자가 되도록 하겠습니다. 감사합니다.#한국방송공사 #KBS #KBS공채 #KBS신입채용 #KBS채용 #입사후기
조회수 1852

Genius? Jininus!

나는 인생을 살면서 많은 "천재"들을 만났다. 스타트업에 있다보면 더더욱 "영재""천재"로 불리는 수 많은 사람들을 보게 된다. 그들은 학문적으로 놀라운 성과와 스펙을 보유하고 있었다. 아마 당신이 한 회사를 운영하는 사람이거나 인사 담당자라면 분명 혹할 것이다. 하지만 정작 나는 같이 일하고 싶었던 사람이 단 한 명도 없었다. 주변에서는 천재들과 같이 일하면 성공할 것이라고 생각하지만, 사업적 결과물과 두뇌는 별개의 문제라고 나는 생각한다. 대단한 능력을 가지고도 빛 없이 사라진 사람들을 얼마나 많이 보았는가. 물론 나도 대단한 사람과 일하고 싶다. 그러나 그 기준을 "영특함"에 국한시키고 싶지는 않다. 사업적으로 혹은 사회적으로 더 나은 미래를 후손에 물려주기 위해서는 그 이상의 "무언가"가 필요하다. 지금부터 나에게 그 "무언가"를 가르쳐 준 "진짜 천재"에 대한 이야기를 하고자 한다. 그에 대한 이야기를 하기 전에 나에 대한 이야기를 가볍게 하고자 한다. 5년 전만 해도 나는 비전과 목표가 없었다. 어려서 부터 돈 욕심만 많았다. 대학교를 다니면서도 돈을 벌 수 있는 방법이면 수단과 방법을 가리지 않았다. 한 일화로 당시에 학원 강사 아르바이트를 하고 있었는데 도매시장에서 트렌디한 문구류를 사와 수업을 가르쳤던 중/고등학생에게 팔았다. 시간과 행동에 제약이 있는 학생들은 수업 시간에 벌어지는 소소한 쇼핑에 돈을 지불했다. 그러나 끝이 좋지 않았다. 학생의 부모님에게 알려져 결국 학원에서 해고 조치 되었다. 지금의 내가 돌이켜보면 엄청나게 창피한 일이다. 학생들에게 단순한 편리와 재미를 줄 순 있었지만, 돈 말고는 남는게 없었다.20대의 대부분은 가치 없는 돈벌이의 연속이었다. 혹자는 말한다. 우선 돈 벌고 가치 있는 곳에 쓰면 된다고. 그러나 이런 식의 무의미한 접근은 내가 가야할 길이 아니라고 느꼈다. 인생에서 가치 있는 일을 찾아야 했다. 그때 발견했다. 혁신, 도전, 열정이 정말 실천되고 있는 세계가 있다는 것을. 스타트업이라는 단어조차 생소했던 시기였다. 심지어 IT라는 분야를 그 전까지 제대로 공부해 본 적도 없었다. 스타트업의 "ㅅ"도 모르던 내가 이 세계에 적응할 수 있는 방법은 뛰어난 사람들과 함께 시작하는 것 뿐 이었다. 온갖 미사여구로 괜찮은 연봉과 복지를 내세우는 기업도 꽤 있었다. 그러나 나에게 가장 중요한 건 "내가 성장할 수 있는지"와 “구성원”이였다. 꽤나 당연한 조건으로 기업을 찾았음에도 불구하고 찾을 수가 없었다. 그러다가 첫 스타트업으로 선택한 게 라우드소싱 이라는 작은 팀이었다. (찾게 된 과정에 대해서는 다른 글을 통해 소개하겠다) 안정적인 연봉도 없고, 확실한 미래도 없었지만 내가 이 팀과 같이 해야겠다 결정한 건 "권진" 이라는 단 한 사람 때문이었다. 모든 기업이 그렇지만 누구나 회사에 합류하면 3개월간의 수습기간을 거친다. 스타트업이라고 예외는 아니다. 오히려 더 냉정하게 자신을 되돌아 보는 시간을 가져야 한다. 나는 내 스스로를 입증하고 싶었다. “제가 3달 안에 이 회사가 성장할 수 있는 계약들을 가져오겠습니다. 그 정도 능력도 발휘 못한다면 제 발로 나가겠습니다” 3달 동안 권진은 일에 대해서 전혀 간섭하지 않았다 . 팀워크에 있어서 가장 중요한 부분은 신뢰라고 생각한다. 하지만 신뢰라는 부분이 친하다고 해서 혹은 비전과 목표가 같다고 해서 생기는 것이 아니다. 각자의 위치에서 최고의 성과를 목표로 내고, 한계를 뛰어넘어 성장하는 모습을 보여줄 때 강력한 신뢰가 생긴다. 서로가 같이 일하고 싶은 마음을 만들어 주는 것.이게 팀워크의 핵심이다. 나는 나대로 권진은 권진대로 각자가 맡은 일들을 완벽하게 수행했고, 우리는 그 일들을 하나의 사업으로 만들어 갔다. 그는 나에게 따로 주저리 주저리 피드백을 하지 않았다. 하지만 행동으로 결과물의 중요성을 보여주었고, 나는 3달동안 7건의 B2B 계약을 성사시켰다.애초에 같이 할 사람을 정할 때는 모든 부분을 면밀히 살피고 고민해야 하지만, 내가 같이 하기로 결정 했다면 상대가 최고의 결과물을 낼 수 있도록 믿어주는 것. 내가 배운 첫번째 교훈이었다.실력을 보여주었다고 환상적인 Fit일까? 누구든 본인이 만들어 내는 결과물을 혼자만의 능력이라고 오판하기 쉽다. 내가 영업처를 설득하고, 계약서를 체결해 왔기 때문에 내가 없었으면 이 계약도 없었을 것이다. 감각적이고 환상적인 디자인을 뽑아냈는데 이건 순전히 나의 재능에 의한 것이다. 팀원들이 이런 생각들을 하기 시작한다면 그 팀은 단시간 내에 모래성처럼 무너질 것이다. 권진은 개인이 만들어 내는 결과물도 팀원들이 각자의 분야에서 해 온 노력들의 최종산출물이라고 생각한다.영업처를 설득할 수 있었던 건, 우리 팀이 환상적인 서비스를 만들어 주었기 때문이다.나의 디자인은 기획팀과 마케팅팀의 노력을 하나로 담은 것 뿐이다.톱니바퀴처럼 팀원들이 맞물려 돌아가며 서로의 존재에 대해 감사함을 느낄 때 놀라운 일이 벌어진다. 내가 배운 두번째 교훈이다.권진이 지켜온 2가지 요건이 계속 좋은 사람을 팀으로 영입할 수 있었던 강력한 요소였다고 생각한다. 나의 실력을 우리 팀에 입증하는 것. 나의 결과물은 우리 팀 노력의 산물 이라는 것.권진과 함께 일하며 느낀 그의 주요한 능력은 개발도 디자인도 아니었다. (물론 이 2가지도 잘한다)팀 내의 균형을 맞추고 팀원들이 끊임없이 성장하게 도와주는데 있다. 개성 넘치는 팀원들을 하나의 비전으로 묶어서 성장할 수 있게 하는 사람을 나는 살면서 권진 이외에는 아직 본 적이 없다. 장담컨데, 만약 현재 더팀스 대표가 권진이 아니라 다른 사람으로 바뀐다면 팀원들은 전부 팀을 나갈 것이다. (연봉이 대폭 인상된다 할지라도)그래서 나는 이걸 Jin in Us 라고 명칭했다. 권진이라는 확실한 구심점 안에 개성넘치는 팀원들이 한 몸처럼 목표로 향해가는. 나는 앞으로 대표라는 역할을 할 생각이 없다. 권진 이라는 사람보다 대표의 역할을 충실히 수행할 자신이 없어졌기 때문이다.리더십이라는 분야가 있다면 그는 천재가 아닐까?내가 우리 팀에 합류시키고 싶은 사람이 있을 때면 하는 단골멘트로 이 글의 마무리를 짓는다.“우리 팀의 권진을 만나보세요. 분명히 함께 하고 싶을 겁니다”#더팀스 #THETEAMS #천재디자이너 #풀스택개발자 #CEO #리더십 #경험공유 #팀원자랑 #팀원소개 #회사의자랑
조회수 1036

스타트업, 사대주의에 빠지지 말자.

하지만, 탈한국적이라는 것이 사대주의를 의미하는 것은 아니다.분명한 것은 한국적인 환경이 더 좋지 않다는 것을 의미하는 것이지 미국이나 일본, 중국의 환경만을 부러워하는 것은 아니다. 아주 단순하게 일반화시켜서 그들의 환상적인 성공사례만 수집해서 이야기하려고 하는 것도 아니다.정말 중요하게 전달하고 싶은 메시지는 한국적인 환경은 정말 암울하다고 경고하고 싶을 뿐이다.미국에서 창업하거나 M&A 이후에 가볍게 버림받는 것은 너무도 당연한 것인지도 모른다. 오히려, 국내 특정 대기업의 분위기는 한번 식구로 받아들여지면 쉽게 내치지 않는 문화를 가진 특정 상황들도 있다고 이야기할 수 있다.하지만, 그런 케이스들은 특정 케이스 들일뿐, 대부분의 분위기는 어느 정도 일반화되어있다고 이야기하겠다.사실, 계약관계에 의해서 더 이상 단물이 나오지 않는 조직과 사람에 대해서 가차 없이 인사관계를 진행하는 것은 미국적인 관행이 더 살벌하게 받아들여질 가능성이 더 높다고 이야기한다.하지만, 최소한 그들은 법적인 단계나 계약서를 뛰어넘는 행위에 대해서는 윤리적인 행위에 대해서 대부분은 지키려 한다고 이야기하겠다.국내 대기업들처럼 유망하거나 의미 있는 서비스를 만든 중소기업과 유사한 제품을 만들거나, 더 싸게 하기 위해서 비슷한 기업들에게 해당 기업의 노하우를 비공식적으로 공개한다던지 하는 행위는 분명하지 막 매우 적다.그들의 세계에서 그른 올바르지 않은 행위들이 자신의 신뢰를 깎아먹는 것인지에 대해서 너무도 잘 알고 있기 때문이다.그래서, 대부분 그들의 세계는 신뢰와 계약, 윤리적인 단계에 의해서 시스템이 움직인다. 그래서, 그들은 계약관계가 끝나거나 계약에 의해서 정해진 목적이 부족하다고 생각하면 가차 없이 잣대를 들이댄다.가끔 제가 글을 올리는 것에 대해서 너무 심한 일반화가 아니냐는 비판을 하시는 분도 계신다. 물론, 그 의견에도 공감한다. 왜냐하면 내가 글을 올리는 것은 내 경험을 기반으로 나열되는 것일 뿐, 완전 일반화가 되는 이야기가 아니기 때문이다.그냥, 내 경험상 그랬다는 것이고, 후배들에게 조금이라도 그 경험적인 가치에 대해서 전달하고 싶을 뿐이다. 가능한 많은 실패에 대해서 많이 전달하는 것 그것이 선배가 경험한 것을 최소한 전달해야 한다고 느끼기 때문이다.한국이건 중국이건 일본, 미국이건 스타트업을 만들고 생태계를 넘어서서 무언가 혁신을 이룬다는 것은 정말 매우 힘든 행위이다. 하지만, 불행한 것은 불행하다고 이야기해야 하는 것이 선배 된 입장이라고 생각한다.기존의 악습과 관습을 그대로 유지해야 시스템에서 받아들여주지, 그것을 조금만 벗어나려고 한다면, 수많은 잣대와 기준으로 사업 자체를 틀어막는 행위가 가장 극심한 나라가 한국이라고 악평을 해야겠다.차라리 틀이 잘 짜인 일본의 생태계를 성급하게 일반화시킨다면, 정해진 룰을 넘어선 방법으로 소비자들과 공급자를 연결하는 방법을 고안한다면 해당 시스템에 대해서 진지하게 기존 생태계가 바라보며, 그 혁신적인 시스템이 그대로 사장되지 않도록 보호하려고 애를 쓴다. 다만, 그 속도가 너무 느리기 때문에 일본의 시스템이 불안해 보이는지도 모르겠다.하지만, 일본의 시스템은 그런 움직임이 조금씩 진보적으로 움직인다.중국의 경우에는 악습이나 관습이 완전하게 경제 시스템 전체를 지배하고 있지 않기 때문에 매우 혁신적인 시스템이 시장에 도입되고 사용되는 것에 대해서 굉장히 빠른 속도로 움직이다. 일종의 불협화음 마저도 혁신의 속도의 과실까지 시스템이 허용하고 있다는 것을 느낀다.각 국가마다 나름대로 혁신을 포용하는 방법이 다르다.느리다는 일본도 다양한 방식으로 경제활동이 파급되도록 시스템이 개방되고 있다. 중국이나 미국은 두말을 할 이유가 없다.하지만, 한국은 아니다.슬프게도 꼰대들이 만들어 놓은 기존 시스템이 가진 관습을 그대로 유지해야 한다.헬스케어이건 금융이건 똑같다. 한국적 창조경제의 핵심은 그러하다. '기존의 악습과 관습이 유지되는 혁신'을 하라는 것이다.그 말이 가능한지 모르겠지만, 그렇게 사용되고 있다. 매우 창조적으로...하지만, 희망은 버리지 않고 있다. 우리가 분명하게 할 수 있는 일들이 많으며, 주변의 스타트업은 국내의 상황과 꼰대들의 바람과는 다르게 움직이고 있다.굳이 한국적인 서비스를 만들지 않으며, 꼰대들이 만들어 놓은 생태계에 몰입하지 않으며, 중국보다 더 디테일한 서비스를 만들며, 일본보다 빠르게 움직이고, 미국보다 더 창의적이고 지식기반으로 움직이는 사업들이 분명 존재한다.그런 멋진 후배들에게서 한국의 미래를 발견한다. 다들 파이팅이다!
조회수 685

기획자, 당신은 무엇을 하는 사람인가?

가장 애매한 전문가 : 기획자나는 기획자다.아마도 다양한 타이틀을 달고 있는, 나 같은 기획자들을 주변에 많이 볼 수 있을 것이다.특히, 대기업으로 갈 수록 업무가 세분화되어 있다보니, 상품기획, 서비스기획, 개발기획, 디자인기획, 광고기획, 사업기획 등등 왠만한 기능들의 뒤에 '기획'이라는 접미어를 붙여 마치 각 기능들을 앞에서 이끌 것 같거나, 아니면 각 기능들의 뒤치닥거리를 할 것 같은 그때 그때 다른(조직마다, 업종마다, 기능마다)느낌의 Job이다.사실, 가장 가까이에 있는 xx 기획자에게 한 번 물어보아라. "당신의 역할은 무엇인가요?" 한 마디로 쉽게 설명할 수 있다면, 그 사람은 꽤 유능한 기획자일 것이다.기획자의 역할 정의실제로 기획자의 역할은 대단히 폭넓고 다양하다. 당연히 어떤 산업에 종사하느냐, 어떤 부서에 누구와 일하느냐에 따라서 달라진다.어떤 곳에서는 핵심 '전략'을 담당하기도 하고, 어떤 회사에서는 '운영'을 담당하기도 하고, 어디에선 '리더'의 역할을, 다른 곳에선 '시다바리'의 역할을 맡기도 한다.어떻게 보면, 특정 기능(예를 들어 개발자, 디자이너, 영업, 재무 등 전문영역)을 전문적으로 수행하는 업무를 제외한 나머지 업무 모든것을 커버하는 Generalist 를 총칭한다고 볼 수도 있다.나는 디자인 기획자이다.대기업에서 상품을 구상할 때 필요한 신제품의 컨셉을 발굴하고, 디자인의 방향을 설정하고 사용자에게 유용한 기능들이 조화를 이루는지 꼼꼼히 확인하여, 디자인 목업과 프로토타입을 일정 내에 나올 수 있도록 매니징 하는 일을 하고 있다. (음... 뭔가 복잡하고 딱히 뭘 하는지 잘 이해가 안간다면...그게 바로 기획자의 실제 업무 들인 것이다 -_-)좀더 일반화해서 기획자의 업무를 크게 5가지로 구분해보겠다.정보 파악 기능 (searching): 팩트를 파악하고, 현황을 분석하여 올바른 판단을 할 수 있는 근거를 마련하는 업무문제 정의 기능 (defining) : 현황에 근거하여, 현재의 문제를 정확하게 파악하고 정의하는 업무자원 할당 계획 기능 (planning) : 과제를 언제, 얼마의 비용으로, 누구와 어떻게 하겠다는 계획을 수립하는 업무방향 설정 기능 (directing) : 목표를 명확히 정의하고, 집중 해야 할 방향을 선택하고 제안하는 업무운영/매니징 기능 (managing) : 설정된 계획에 차질없도록 관리 및 운영하고 커뮤니케이션 하는 업무기획자의 핵심은 '컨셉' 이다위에 나열된 업무들을 보면, 대게 경험이 쌓이면 조금씩 숙련도가 올라갈 법한 일들처럼 보인다. 자료를 조사하거나, 현황을 분석하거나, 자원을 할당하여 스케쥴과 예산을 산정하고, 무엇을 포기하고 어디에 집중할 것인지에 대해 제안하고, 차질없이 목표를 수행하는 기능들은 마치 직장인들이라면 마땅히 누구나 해야 하는 당연한 일쯤으로 보인다.하지만, 기획의 성공과 실패는 어디에서 나뉘어지는지 생각해보면, 위의 5가지 영역을 무리없이 처리한다고 하더라도 업무를 성공적으로 이끌긴 어렵다.창의력이 발휘되어야 하는 업무이기 때문이다.문제를 정의하고, 자원을 할당하고, 방향을 설정하는 단계가 창의적이거나 혁신적이지 않다면, 아마도 뻔~한 결과물로 일을 마칠 가능성이 높다. (그 일을 수행하는 전문가의 역량을 동일하다고 본다면...말이다)다시 말하면, 문제를 남다른 관점에서 정의하고, 전혀 다른 방식으로 방향을 이끌어 갈 수 있을 때, 새로운 해결책과 'wow' 요소가 나올 수 있다.하지만, 다시 현실로 돌아와보자.나와 한팀으로 같이 일하는 개발자, 마케터, 디자이너에게 우리가 상식적으로 알고 있는 상황을 전혀 새롭게 인식시키고, 전혀 다른 관점으로 문제를 풀어가자고 설득하는 일은 (게다가, 그들이 내 선배 또는 전문성과 경험으로 무장한 사람들이라고 한다면...-_-) 결코 만만치 않을 뿐만 아니라, 자칫 '그건 네 생각이고~', '난 아닌 것 같은데...' 몇 마디면, 보통 기획자들은 찌그러지게 되어 있다.기획자가 조직에서 '맨날 자기 세계에 빠져있는 자', 또는 '회의 소집하고, 회의록 정리하고 문서 작성하는 staff' 정도로 치부되는 경우가 적지 않은 이유이다.이 때 필요한 것이, 전체를 엮어나갈 '컨셉' 이다.현상을 다르게 바라보고, 문제를 새롭게 보고, 전혀 새로운 방식으로 해결책을 이끌어낼 수 있도록 하는 힘은 이것을 해야하는 '본질적인 이유'에 대한 질문과 그 답을 표현하는 '컨셉'에 달려있는 것이다.스티브잡스는 가장 위대한 기획자가만 보면, 주변에 꽤 뛰어난 개발자, 감각적인 디자이너, 열정적인 마케터, 지치지 않는 영업맨 들이 많다. 각 기능별로 뛰어난 훌륭한 전문가들은 마음 먹으면(비용은 좀 들겠지만...) 찾을 수 있다.만일 그런 전문가들로 구성된 드림팀을 만들면, 과연 세상을 깜짝 놀라게 하는 무언가가 자연스럽게 나올 수 있을까?그렇지 않다는 것을 직, 간접적으로 우리는 많이 보아왔을 것이다. 그런 논리라면 미국의 뉴욕 양키스 팀이나 스페인의 레알 마드리드 팀은 항상 우승을 해야하겠지만, 사실 어떤 감독과 어떤 작전을 펼치냐에 따라서 전혀 다른 결과물이 나온다.핵심은 전체를 한 방향으로 엮을 수 있는 리더쉽과 문제의 '본질'을 꿰뚫어 볼 수 있는 '컨셉'을 만들 수 있는 능력이다. 이 '컨셉'이라는 것은 총체적인 경험의 총합이어야 하며, 같은 팀원들에게 공유될 수 있도록 표현될 수 있는 무엇이어야 한다.그것이 기획자의 핵심 역량이어야 한다.Parameter Optimizer오케스트라의 다양한 악기들을 (각자 내로라하는 음악의 명장들이 포함된) 지휘자가 위대한 하모니를 만들 듯이, 각 기능의 전문가들이 때로는 양보하고 절제하고, 때로는 선두에서 힘을 발휘할 수 있도록 최적화 하는 일은 지휘지나 감독, 그리고 기획자들이 갖춰야 할 능력이다.단순히 보고서를 잘 정리하고, 꼼꼼하게 프로젝트의 일정을 챙기고, 문제가 발생하면 상부에 보고하고 프로세스를 잘 지키는 것이 중요한 것이 아니라, 이 프로젝트가 어떤 의미가 있고, 왜 이 문제를 해결해야 하는지에 대한 총체적인 질문에 답할 수 있고, 공감시킬 수 있어야 한다는 것이다.때로는 강력한 카리스마가 필요할 수도 있으며, 때로는 감성적인 부분으로 공감을 이끌어낼 수도 있어야 하며, 치밀한 숫자와 논리, 또는 은유와 비유로 총체적인 경험을 표현하는 '컨셉'을 공유할 수 있어야 한다.그런 측면에서 스티브잡스는 누구도 이루지 못했던 혁신적인 제품을 경영자이면서 동시에 '창의적 기획자'로서 세상에 선보일 수 있었다고 생각한다.쓸만한 기획자, 전략가가 없다요즘 linkedIn에 올라온 구직, 구인 정보들을 보면, 구체적인 직능을 수행하는 Expert들을 찾거나 또는 그런 Job을 찾는 내용들을 많이 볼 수 있다.특정 SW를 다룰 수 있는 소프트웨어 엔지니어를 찾거나, 5년 이상 IT 분야에서 종사한 UX 디자이너 또는 편집쪽 업무 경험이 있는 그래픽디자이너 등등 특정 기능을 수행하는 인력을 찾는 내용들은 많지만, 창의적인 사고와 남들과 다른 관점을 가진 '기획자'를 찾는 구인정보는 사실 흔치 않다.문제는 창의적인 사고를 평가할 수 있는 기준도 모호할 뿐만 아니라, 기획자의 입장에서도 자신의 '똘끼'나 창의력을 보여줄 수 있는 '포트폴리오'가 상당히 제한될 수밖에 없기 때문이다. 실제 직접 일을 하면서 과정을 같이 하지 않는 한, 훌륭한 기획자나 창의적인 사고를 구인/구직 시장에서 제대로 판별하기란 여간 쉬운 일이 아니다.설사 자신이 정말 창의적이고, 본질을 꿰뚫는 촌철살인의 혜안을 가지고 있다고 주장하더라도, 그것을 단시간 내에 입증하기도 사실 매우 어려운 것이 사실이다.물론, 이미 각 분야에서 성공적인 사례를 남긴 훌륭한 혁신가, 리더들이라면 이미 그 생각이 미디어를 통해 공유되고 성공사례를 통해 입증될 수 있다고 볼 수 있지만, 그런 인물은 소수일 수 밖에 없고, 이미 몸 값이 감당할 수준이 아닐 가능성이 높다.아마 회사의 터닝포인트를 가지고 싶거나, 혁신의 jump up을 모색하고자 한다면, 좋은 기획자를 다방면에 물색하여 찾으려는 노력이 반드시 따라야 할 것이며, 그 가치를 인정할 수 있는 내부의 안목 역시 뒷받침 되어야 할 것이다.기획자는 표현할 수 있어야 한다기획자는 오케스트라의 지휘자이어야 한다.바이올린과 첼로의 소리를 구분하고 조율할 수 있어야 하지만, 그렇게 만들어지는 소리가 전체적으로 어떤 '음악'이 되어야 하는지에 대한 Big Picture가 머리에 있어야 한다. 그래야, 그 큰그림을 나침반 삼아서 다양한 악기를 조율할 수 있는 것이다.다만, Big Picture가 자신의 머릿속에만 존재한다면, 같이 일하는 파트너들은 큰 그림을 볼 수 없는 상태에서 작은 지시와 조율된 내용만으로 전체 하모니를 만들어 낼 수 없다. 따라서, Big Picture, 즉 전체 스토리 '컨셉'을 파트너들에게 소개하고 공유하고 공감을 이끌 수 있도록 표현하고 설명할 수 있어야 한다.그것이 설득력 있는 보고서이건, 뛰어난 화술이건, 직관적인 비유이던 자신의 생각을 공유하고 소통할 수 없다면, 훌륭한 기획자를 기대하긴 어려울 뿐만 아니라, 하루라도 빨리 자신이 sales 할 수 있는 특정 기술(기능)을 배우는 것이 도움이 될 것이라고 생각한다.기획자는 그리 만만한 Job이 아니다.

기업문화 엿볼 때, 더팀스

로그인

/