스토리 홈

인터뷰

피드

뉴스

조회수 690

숙박앱에 필요한와이즈트래커 리포트 Top 3

숙박앱은 O2O 서비스를 선도하는 사업 중 하나로 빠르게 시장을 확보하기 위한 다수의 앱이 생산되면서, 유사한 경쟁 앱 속에서 차별화 된 서비스로 고객을 잡기 위한 노력이 치열한 사업영역 이기도 합니다. 그래서 숙박 앱 운영에 도움이 될 와이즈트래커의 리포트 3가지를 소개하고자 합니다.1. 상품 카테고리 리포트숙박의 등급은 생각보다 다양합니다. 그러나 숙박 앱에서 한 화면에 보여지는 컨텐츠는 1~2개이며, 이후에는 지속적인 스크롤링으로 원하는 숙박장소를 찾기 때문에 사용자가 선호하는 컨텐츠를 상단에 효과적으로 배치하는 게 중요합니다.상품 카테고리 리포트는 각 숙박 카테고리별로 방문수(=유니크 조회수), 평균 체류시간 등 인게이지먼트 지표와 더불어 객실선택, 예약하기, 주문, 매출액 등 다양한 컨버전 지표를 함께 제공합니다. 나아가 상품 리포트를 통해서 ‘특급’이란 카테고리 중 실제 어떤 호텔이 효과가 좋았는지를 파악할 수 있습니다.이러한 데이터는 컨텐츠 배치 뿐만 아니라, 제휴 영역을 확장하는 데도 어떤 카테고리에 집중해야 할지 참고할 수 있는 유용한 데이터입니다.2. 화면 이동경로 리포트숙박 앱 UI는 매우 심플하고 직관적인 편입니다. 사용자는 예약이 앱 실행의 주 목적이기 때문에, 퍼블리셔는 보통 첫 화면에 컨텐츠를 스크롤링해서 볼 수 있도록 구성하지만 의외로 예약에 접근하는 행동패턴은 다양할 수 있습니다.샘플 데이터처럼 목적을 갖고 검색을 통해 빠르게 상품 정보를 획득하고자 하는 사용자의 비중이 많다면, 모바일 기기에 적합한 내부 검색엔진 편의성 및 결과 화면의 퀄리티가 매우 중요할 것이며 이는 예약율과 직결될 수 있습니다. 반대로 검색했는데 만족하지 못한 결과 값을 제공했다면, 높은 외부 유출 비율을 나타낼 것입니다.화면 이동경로 리포트는 이러한 다양한 사용자들의 행동패턴을 타겟별로 4가지 타입(A화면 이후, A화면 전/후, A화면 도달경로, A화면에서 B화면을 도달하는 경로)으로 분석하여 네비게이션 개선에 통찰력을 얻을 수 있는 데이터를 제공합니다.  3. 내부 검색어 리포트 숙박 앱에서 검색엔진을 사용하는 빈도는 꽤 높습니다. 앱을 실행하자마자 검색하는 사용자는 서핑을 즐겨하기보단 자신이 원하는 컨텐츠(정보)만을 빠르게 받고 싶은 성향이 있습니다. 해당 사용자에겐 효과적인 검색결과 화면을 제공하는 것이 핵심입니다.검색결과 화면은 2가지가 필히 고려돼야 합니다.1) 검색결과와 관련성 높은 결과 값 제시( 방대한 결과값은 오히려 재검색하게 하여 불편함을 제공)2) 검색결과 값이 없을 경우 대안을 제시( 빈 페이지 제시는 매우 부정적인 경험으로 기억)내부 검색어 리포트는 사용자의 검색빈도가 높은 인기 키워드를 파악할 수 있으며, 검색 실패수 지표를 통해 온전한 결과 값을 제시하지 못한 경우를 찾아 검색엔진 개선의 방향을 잡을 수 있습니다.  * WISETRACKER는 모바일 광고 성과 측정부터 In-app 이용자/컨텐츠 분석, 푸시메시지 최적화까지 지원하는 모바일 통합 분석/타겟팅 솔루션입니다. 와이즈트래커 솔루션의 무료체험을 원하실 경우 여기를 클릭해주세요.* WISETRACKER가 제공하는 무료 데이터 분석 컨설팅를 원하신다면 여기를 클릭해주세요.  #와이즈트래커 #서비스소개 #데이터분석 #데이터트래킹 #앱리포트
조회수 2973

66사이즈 여성에게 내놓은 솔루션

66사이즈 전용 여성 쇼핑몰아나운서→공기업→마케터 거쳐 창업코디 솔루션 업체로 성장예쁘고 늘씬한 연예인. 획일화된 미의 기준에 드는 건 무척 어렵다. 그런데 옷은 획일화된 미인에 맞춰 나온다. 예쁘고 늘씬해야 어울리는 옷이 대부분이다. 화면으론 예쁜데, 내가 입으면 기대했던 핏이 나오지 않는다. 내 몸이 문제인가? 얼굴이? 새 옷 샀다가 자괴감이 든다. 66사이즈 전문 쇼핑몰 ‘페르소나웨이’ 를 운영하는 장나영 스트롱소다 대표는 얼굴이, 몸이 문제가 아니라고 한다. ‘내가 입어서 예쁜 옷’을 만든다는 장 대표를 만났다.내가 입어서 예쁜 옷페르소나웨이(personaway.com)는 66사이즈 옷만 판다. “66사이즈는 여자 체형의 40%를 차지해요. 그런데 패션시장은 66이 중심이 아니에요. 늘씬한 모델 앞세워 ‘예쁜 옷’ 파는 곳이 더 많죠. 우리는 못 입는 옷이요. 그런데 팔려요. 왜? ‘살 빼서 입어야지’ 심정으로 걸려 드는 거죠. ‘내 모습 그대로’ 입어야 합니다.이미 외국에선 ‘body positive(있는 그대로 내 몸을 사랑하자)’ 열풍이 분지 꽤 됐어요. 모델도 보통 체형의 일반인을 쓰고요. 우리는 아직 생소하지만, 언젠가 대세가 될거라 확신합니다. 뷰티 쪽은 우리도 일반인 모델이 활발하게 활동하고 계시잖아요? 옷도 그렇게 될 겁니다.”-55사이즈 이하는 정말 팔지 않나요?“네. 66사이즈로 브랜딩해 놓고 다른 사이즈 팔면 배신이죠. 55 이하인 분이 굳이 사겠다면 막을 수 없겠지만 권장하진 않습니다. 그래서 ‘55이하인 분은 저희 옷이 안맞을 수 있으니 구매를 자제해 주세요’란 경고를 달아 놓습니다.”판매 옷의 30%는 직접 디자인해 외주 제작하고, 70%는 회사 정체성에 맞는 옷을 물색해 기존 업체에서 공급받는다. “자체 디자인 비중을 늘려나갈 예정입니다.”-어떤 디자인을 지향하나요?“누가 봐도 예쁘고 누구에게나 어울리는 옷은 아니에요. 사실 그래요. 그런 옷이 진짜 존재나 하는 걸까요? ‘내가 입으니 예쁜 옷’이어야 합니다. 옷만 보면 예쁘지 않아도, 내가 입어서 예뻐야 합니다. 차이는 디테일에서 나옵니다. 언뜻 무난해 보일 수 있어도, 입어서 예쁜 옷은 세세한 부분에 신경쓴 옷이거든요. 그런 옷을 만드려고 노력합니다.” 장 대표 스스로의 고민에서 출발한 사업이다. “제가 66이에요. 출근할 때마다 내 체형에 딱 맞는 옷은 없을까? 고민을 많이 했어요. 거기서 기획한 거죠.” 직장인 취향의 옷을 주로 다루는 이유이기도 하다. “누구보다 고객 고충을 잘 아는게 경쟁력입니다.” 모델도 당연히 66사이즈다. 장 대표 본인과, 인스타그램에서 3만8000명의 팔로워를 갖고 있는 강민주씨가 모델로 활약하고 있다. 강민주 씨는 출산으로 살이 급격히 찐 후 다이어트 과정을 공개해 인기를 끈 인플루언서다. 캠페인을 통해 강 씨 같은 일반인 모델을 늘려갈 계획이다.-고객들이 얼마나 만족하나요.“매장에서 옷을 살 때 외모나 몸매 때문에 자존감에 상처받는 여성이 많아요. 옷이 예뻐서 골랐는데 ‘고객님은 다리가 굵어서 못입으세요’ ‘허리가 길어서 안맞으세요’ 같은 소리 듣는거죠. 우리 쇼핑몰을 만난 후, 딱 맞는 옷으로 몰랐던 내 매력을 찾았다는 고객이 많으세요. ‘나는 생각보다 아름답다’는 자신감이 생겼다고도 하시구요. ‘자존감 찾았다’며 고맙다고 메시지 주는 고객도 있어요. 구입해줘서 고마운 건 전데, 거꾸로 고맙다고 해주시는 거죠.”-오프라인 매장 계획은요?“좀더 성장하면 열 계획이에요. 일단 강남역, 여의도, 광화문 등 여성 직장인이 많은 곳을 위주로 팝업스토어를 열면서 알리고 있어요. 나와 맞는 핏이 뭔지, 컬러 조합은 어떻게 해야 하는지 등을 알려주고 있습니다.”맞춤형 추천 솔루션단순 쇼핑몰에 그치지 않는다. 추천 솔루션 업체로 거듭나고 있다. “고객으로부터 체형, 라이프패턴 데이터를 받아서 옷을 만들고, 큐레이션해줄 예정이에요. 옷과 관련한 확실한 솔루션을 내주는 거죠. 메일링 등을 통해 맞는 옷을 추천하려고 합니다. 고객 입장에서 내 체형에 맞는 옷을 수시로 코디해주는 맞춤형 코디네이터를 두는 셈이죠.”-어떤 기준으로 추천해주죠?“체형이 가장 중요합니다. 기본적인 신체 사이즈 외에, 상체와 하체 중 어디가 발달했는지, 팔다리 길이는 어떻게 되는지 등등요. 각 체형 별로, 가장 잘 맞는 옷을 만들어 추천할 예정입니다. 실내 활동이 많은지, 바깥 활동이 많은지 등 라이프 패턴도 고려합니다.이를테면 활동량이 많은 사람한테 지나치게 두꺼운 옷을 추천하면 안돼요. 이밖에 내가 남에게 보이고 싶은 이미지 같은 것도 고려합니다. 이런 요소들을 합쳐 최적의 옷을 추천합니다. 이를 위해 회원 가입때 미리 체형, 라이프패턴 등 본인 특성을 받고 있습니다.”-얼마나 진척되고 있죠?“고객들이 기입해준 데이터를 기반으로 체형 분류 작업부터 하고 있습니다. 상체, 하체, 복부, 전체 등 4가지로 나눠 어디가 가장 발달했는지 대분류한 뒤, 팔다리, 허리, 골반, 종아리 등의 특성으로 세분류 하는 거죠. 이렇게 일단 12가지 유형을 만들어 놨어요. 데이터가 보다 많이 누적되면 더욱 세세한 모델링이 가능할 걸로 예상합니다. 그러면 같은 66사이즈 중에서도 특성별로 체형을 세분화해 옷을 추천할 수 있습니다. 당분간 데이터를 충분히 쌓는 데 집중한 후, 어느 정도 완성되면 알고리즘을 통해 추천해줄 예정이에요.”아나운서·공기업 거쳐 창업프리랜서 아나운서로 오래 일했다. “원래 꿈이었어요. 고교때 방송부 활동을 했죠. 아나운서가 너무 하고 싶어 대학 들어가자 마자 전공(경영학과 영어영문학)과 별개로 일 알아보러 다녔어요. 20살 때부터 현장MC 같은 일을 할 수 있었어요.”정식 아나운서가 되지는 못했다. 하지만 꿈을 버릴 수 없었다. 대학 졸업 후에도 계속 프리랜서 아나운서로 일했다. 몇몇 케이블 방송에 출연했고, 행사도 여럿 진행했다. 계속 하려고 했다. 하지만 복병을 만났다. “외모에 대한 스트레스가 갈수록 커지더라구요. ‘정식 아나운서 되려면 앞트임 해라’ ‘돌려 깎을 생각 없느냐’ 같은 소리를 듣는 거죠. 몸무게도 마찬가지에요. 1kg에 대한 압박감이 그렇게 클 수가 없었어요.“ ‘행복하자고 일하는 것 아니었나?’ 접기로 했다. 아나운서 하면서 관심 갖게 된 공연 쪽 일을 해보기로 했다. “다양한 행사를 진행하다 여러 공연을 접했어요. 재밌더라구요. 직장인 연극동호회도 했고요. 뭘 할까 고민하다 ‘공연 마케팅을 해보자’ 결심했어요” 세종문화회관 문화사업기획 파트로 입사했다. 4년을 일했다. 어느날 답답증이 몰려왔다. 일을 벌리고 싶은데, 자주 하지 말란 말이 돌아왔다. 공공기관 특성 탓이었다. 정년 보장이 무슨 의미가 있나. ‘다른 일 해보자’ 결심했다. ‘이미지 컨설턴트’에 도전하기로 했다. 정치인, 대기업 임원 등이 좋은 이미지를 가질 수 있도록 스피치, 제스처, 코디, 화장법 등을 조언해주는 직업이다. 일반인 수요도 있다. 취업이나 입시 면접에서 좋은 점수를 얻을 수 있도록 컨설팅해준다. “정치인 등은 개인 컨설팅을 받고요. 일반인은 강연을 통해 신뢰감 있게 말하는 법 등을 배웁니다. 아나운서 하면서 외모 스트레스 받고, 말 잘하기 위해 노력했던 경험을 이미지 컨설팅으로 연결시킬 수 있겠더라고요.” 마침 관련 교육을 해주는 기관이 있었다. 공부를 하면서, 강연 같은 일감도 소개 받을 수 있었다.-익숙한 직업은 아니네요.“네. 그때도 재밌었지만 지금도 많은 도움이 되고 있어요. 이미지 컨설팅 하면서 정식으로 옷 코디에 대해 공부할 수 있었거든요. 둥근 얼굴은 긴 귀고리가 어울린다 등등의 공식이요. 이런 공식을 기반으로 제 나름 코디법을 연구해, 현재 고객들께 솔루션을 드리고 있습니다.”이미지 컨설턴트로 일하다 스타트업 마케터로 일할 기회가 생겼다. “사물인터넷 관련 업체와 맛집 소개 어플리케이션 업체에서 마케터로 일했어요. 스타트업 대표 이미지 컨설팅을 하다가 연이 닿은 거죠. 브랜딩을 맡았는데, 기업 이미지 컨설팅이라 할 수 있어요. 개인 이미지 컨설팅과 큰 틀에서 일맥상통하죠. 스타트업을 경험할 수 있는 좋은 기회가 됐어요.”‘나도 스타트업을 해보자’ 목표가 생겼다. 일단 스타트업을 배우자. 은행권이 스타트업 지원을 위해 8500억원을 출연해 만든 ‘은행권청년창업재단’의 사무국 조직인 ‘디캠프(D.CAMP)’에 입사했다. “스타트업 생태계 핵심에 들어가, 많은 사람과 교류하고 행사를 벌일 수 있었어요. 정말 소중한 기회였습니다.”-패션 창업은 어떻게 생각하게 됐나요.“디캠프에 있으면서 패션 테크 창업자 분들과 만날 기회가 많이 생겼어요. 다양한 아이템을 접하다, 체형 별로 옷을 추천하는 솔루션을 해보면 어떨까. 생각이 들더라구요. 기존에는 비슷한 옷끼리 모아놓는 곳 밖에 없으니까. 되겠다. 생각으로 도전하게 됐습니다.” 디캠프(D.CAMP)가 매달 주최하는 데모데이인 D-DAY 행사 사회를 맡는 등, 프리랜서 아나운서도 계속하고 있다. 주로 스타트업 관련 행사를 한다. 업계 사람이라면 얼굴 모르는 이가 거의 없는, 스타트업 업계 행사의 여왕으로 통한다. “이만큼 스타트업 업계 분들 꾸준히 만날 수 있는 일이 없어요. 여러모로 운이 좋습니다.”경험과 사람이 중요-창업 전 좀더 준비했으면 좋았겠다는 점은요?“작은 쇼핑몰 한번 해봤으면 어땠을까. 생각이 많이 들어요. 아니면 뜻 맞는 친구들 끼리 작은 프로젝트라도 돌려봤으면 좋았을 것 같아요. 프리랜서 할 때는 혼자 잘하면 돼요. 사업은 달라요. 뜻맞는 사람과 경험이 중요해요. 새로 사람 만나 하나 하나 맞추는 데 시간이 참 많이 걸려요. 일찍 좋은 파트너 만나 오래 경험 쌓을수록 그 시간을 줄일 수 있습니다.”-예비 창업자들이 참고할만한 대표님 만의 경쟁력은요?“다양한 경험을 해본겨요. 이미지 컨설턴트, 아나운서, 스타트업 마케터 등. 뭐하나 버릴 경험이 없어요. 디캠프에서 IT 창업자 분들 만나면서 IT 쪽 이해도도 굉장히 높아졌어요. 패션 쪽 감성이 있으면서 IT 쪽 지식도 있는 CEO는 찾기 어려워요. 스스로 가깝다고 자부해요. 되도록 많은 경험을 해보는 게 중요합니다.”은행권청년창업재단 D.CAMP
조회수 870

날이 좋아서, 급떠난 게임베리 소풍, 롯데월드!

안녕하세요,게벰이 입니다 :)앞으로 게임베리 소식을 하나씩 포스팅 하려고 해요업무적인 것, 소소하게 생활하는 것,이번처럼 날이 좋아서 하던 일 마무리하고 같이 놀이동산 간 얘기 등등다양한 얘기로 알차게 채울테니 많이 들려주세요 ^^열심히 일한 당신, 놀 때도 제대로 놀자!대표님 曰" 땡땡땡하던 일 4시까지 마무리 해주세요~날이 좋아서 롯데월드 갑니다~ "꺄>.< 이런 급 이벤트는 또 마음을 설리설리 두준두준하게 하죠4시 즈음 다 정리하고 떠나서, 5시에 입장했어요!!이 날 저희 놀러 갈 줄 1도 몰랐는데,대표님 회의실에서 나오시더니,날이 좋아서 오늘 일찍 정리하고 떠나자고...!!YAY5시 입장했기에,하나라도 더 타려고 열심히 다녔어요ㅋㅋㅋ줄이 짧은게 보이면 일단 타고 봤다는..ㅋㅋㅋ그래서인지, 놀이기구 타는 사진은 전멸이네요....건진거라곤 아래 사진 뿐..ㅋㅋㅋ놀이기구 하나도 못 타는 사람도 탄다는 범퍼카~~짠거도 아닌데 직원들 한 마음 한 뜻으로어느새 대표님을 구석으로 몰았더라구요 ㅋㅋㅋㅋ이 외에도 할로윈 분장하신 분들이랑찍은 사진도 있고 했었는데...찾으면 다시 업데이트 할게요 :)씬~나게 놀다보니 어느새 어둑어둑해 졌어요집으로 가야하는건 아쉬웠지만,마침 할로윈 시즌이라 분위기가 더 할로윈스러워진건 좋았네요그러고... 이젠 진~짜 돌아가야 할 시간!월요일을 마치 금요일인냥 불태운 Gameberry추억 되새김 하며,다음에도 게임베리만의 소식 가지고 돌아오겠습니다기대해 주세요 :)
조회수 2845

네오펙트는 어떤 회사인가?

이번 글에서는 내가 창업하고 지금 CEO로서 경영하고 있는 네오펙트라는 회사에 대해서 이야기를 해보려고 한다. 네오펙트는 이제 성장을 시작하고 있는 창업한지 만 6년이 조금 안된 스타트업이다.  성공한 많은 스타트업의 선배들처럼 아직 자랑할 정도로 성공하지는 못했지만 아직까지 생존해서 꾸준히 성장하고 있다는 것에 위안을 삼고 열심히 하고 있다. 내 글을 읽는 사람들에게 내가 일하고 있는 네오펙트라는 회사를 소개하고 싶은 이유는 글을 쓰고있는 필자에 대해서 이해하기 위해서는 내가 창업하고 경영하고 있는 회사를 소개하는 것이 필요하다고 생각했기 때문이다.네오펙트는 어떤 회사 그리고 어떤 스타트업일까?  하드웨어 스타트업인가? 맞다 우리는 하드웨어를 개발할 수 있는 제품 디자이너, 회로 개발자, 기구 개발자들이 새로운 하드웨어들을 개발하고 있고 심지어는 자체적으로 제조를 할 수 있는 작은 공장도 있다. 그럼 SW 스타트업인가? 그것도 맞다. 우리 회사에는 하드웨어 개발을 담당하는 인력보다 SW 개발을 담당하는 인력이 더 많다. 그런 의미로 보면 우리는 SW 회사이다. 게임 개발사인가? 그것도 맞다. 우리 회사에는 기능성 게임을 전문적으로 만드는 팀이 있고 매달 1~2개의 새로운 게임을 출시하고 있다. 의료기기 회사인가? 그것도 맞다. 우리 회사는 의료기기를 만들고 있고 관련한 다수의 인증을 보유하고 있다. 인공지능 회사인가? 그것도 맞다. 우리 회사에는 국내와 해외 유수의 대학에서 머신러닝을 연구한 박사 출신 데이터 사이언티스트들이 다수 일하고 있고, 그들이 제품에 들어가는 인공지능 알고리즘을 연구하고 있다. 로봇 회사인가? 그것도 맞다. 우리가 만드는 제품에는 로보틱스 기술이 들어가 있고, 출시 예정인 제품은 더욱 로봇과 비슷하다. 그리고 우리가 만드는 제품을 재활로봇이라는 범주에 넣어서 분류하곤한다. 서비스 회사인가? 그것도 맞다. 우리는 향후 우리의 제품과 서비스가 결합되는 미래를 그리고 있다.여기까지 읽으셨다면, 그런 의문이 들것이다. 도대체 뭐하는 회사야? 정체가 뭐지?난 우리 회사의 정의를 "What" 즉 우리가 무엇을 하는 회사 혹은 무엇을 만드는 회사에서 시작해야 한다고 생각하지 않는다. 우리 회사는 "What"으로 정의할 수 없는 회사이다. 하지만 우리 회사를 "Why"라는 관점에서 본다면 너무나 명확하다. 우리는 재활 환자들과 그 가족들이 행복한 삶에 대한 희망을 놓치지 않도록 도움을 주기 위해서 설립되었고 그 목표를 이루기 위해 다양한 방법을 쓰고 있는 것이다. 그 여러 가지 방법 중에 하나가 의료기기 형태로, 게임의 형태로, 인공지능의 형태로, 그리고 서비스의 형태로 발현되는 것이다.우리는 새로운 희망을 만들어가기 위해서 의료기기로서 하드웨어를 개발하고 있고, SW 플랫폼, 게임, 인공지능 알고리즘, 로봇기술, 그리고 서비스를 연구하고 개발하고 있다. 나의 아버지는 뇌졸중으로 돌아가셨다. 그리고 나의 큰 아버지 두분도 뇌졸중으로 고생하시다 돌아가셨다. 그렇기 때문에 난 환자와 환자의 가족들이 느끼는 '절망'과 '포기'의 의미를 알고 있다. 네오펙트는 "Neo"와 "Effect"의 합성어이다. 새로운 효과를 만들어 내겠다는 꿈을 담고 있다. 그 새로운 효과는 '절망'을 '희망'으로 바꾸어 줄 수 있는 새로운 효과 일 것이다. 재활 이 필요로 하는 환자들에게 행복한 삶의 희망을 찾게 해주는 솔루션을 만들어 주는 것이 네오펙트의 미션이다. 그것이 우리가 추구하는 미션인 "We Inspire Hope"이다.우리 회사에 대한 자세한 내용을 알고 싶으시면 www.neofect.com 이나 Youtube에서 Neofect를 검색하면 다양한 영상을 확인할 수 있다.#NEOFECT #회사소개 #서비스소개 #CEO가하는일 #WhyHowWhat
조회수 1375

이유가 필요한 #데이터

그렇다. 항상 이유가 필요했다.무엇을 하든지 자세하진 않더라도 대략적인 방향성이 잡힌 이유가 필요했다. 그래서일까. 그 때문에 내 인생의 방향성을 찾기 위해서 어른들이 흔히 말씀하시는 쓸데없는 짓(?)을 꽤 많이 했던 것 같다. 핀다에 합류하게 된 것도 그 때문이었다. (그렇다고 핀다의 조인한 게 쓸데없는 짓이라는 뜻은 아니다) 학교에서 가르치는 과목들을 왜 배우는 지도, 실무에서 어떤 식으로 쓰이는지도 모르고 수업을 듣자니 좀이 쑤셨다. 그래서 선택한 게 ‘인턴’이었다. 컴퓨터 공학을 전공하고 있는 나는, 미래창조과학부에서 주최한 이공계 인턴쉽 프로그램 ‘오픈 챌린지'에 참여한 덕분에 ‘핀다’를 만날 수 있었고, 얼떨결에 우수 인턴으로 선정되었다. 25개의 인턴쉽 참여 기업들과 참여 인턴들 앞에서 대표로 성과공유를 발표해보는 좋은 경험도 해볼 수 있었다. 인턴쉽이 끝나고 정식 인턴으로 채용되어 현재 나는 핀다의 Database를 맡고 있다.미래창조과학부에서 주최한 이공계 인턴쉽 프로그램 ‘오픈 챌린지' / 우수인턴으로 발표하는 영광을!핀다에서의 많은 업무 중에서도 제일 재미있었던 것은 데이터 분석(Data Analysis)이다. 특히, 데이터를 여러 관점에서 나누고 그 안에서 인사이트를 내는 것은 굉장히 두근거리는 일이었기 때문이다. 처음 입사했을 때 간결하게 정돈되어 있는 Data Sheet를 보고 감동했을 정도였으니까.# 소비자(유저) 리뷰 분석핀다에서는 사용자들의 실질적인 리뷰를 중요시한다. 다만, 우리나라 문화상 (미국에 비해) 리뷰에 대한 태도가 자발적이지 못한 편이다. 따라서, 퀄리티 높은 리뷰를 많이 얻는데 굉장히 힘이 드는 것도 사실이다. 그렇게 힘들게 얻은 상품 리뷰가 어느덧 1,000개 정도가 되었다. 이를 분석함으로써 현재 우리 서비스를 이용하는 고객들은 어떤 고객들이고 금융 상품을 선택할 때 어떤 특징들에 민감한지, 나아가 앞으로 핀다가 집중해야 하는 고객은 어떤 특성을 가진 고객인지를 알아보는 데에 기본 바탕이 될 수 있다.1) 먼저 상품 카테고리 별 만족도는 ‘주택담보대출’이 가장 높았으며 ‘신용대출’이 가장 낮았고 할 말이 제일 많았던 카테고리는 전세대출(리뷰당 단어 수 전체 평균: 33.4 / 전세대출 평균: 42.2)이었다.2) 내용면으로 봤을 때 ‘금리’에 대한 관심이 높다는 것을 다시 한번 확인할 수 있었고 (‘금리’ 언급 비율: 55.9%) 흥미로웠던 것은 2030을 타겟으로 하는 핀다에서의 리뷰임에도 직원 혹은 지인 추천, 오프라인 가입과 같은 ‘오프라인 관련 요소’(21.6%)가 끼치는 영향이 적지 않다는 것, 그리고 영업점이 가까워서 혹은 처음 계좌를 만들 때 엉겁결에 가입하게 되는 ‘주거래은행’(17.5%) 역시 상품을 선택하는 데에 있어서 크게 기여한다는 것이다.3) 여담이지만 오프라인, 온라인 상담 관련한 내용(30.9%)도 많았는데 그중 83%가 상담이 친절했다거나 설명을 알아듣기 쉽게 해주었다는 등 긍정적인 내용이었다. 보통 리뷰는 리워드가 있는 이벤트성이 아닌 이상 부정적인 내용이 많을 거라고 생각했던 나에겐 신선한 충격이었다.핀다가 전하는 경제 소식, 오늘의 뉴스는 매일 오전8시에 업데이트 된다.# 오늘의 뉴스 분석핀다에서는 매일 아침 금융 관련 주제를 가진 ‘오늘의 뉴스’를 페이스북 페이지에 게시한다. 당연한 소리겠지만 같은 시간대에 올려도 어떤 기사는 좋아요, 공유, 도달률 등의 수치가 좋고 어떤 기사는 좋지 못하다. 그리고 이건 핀다의 페이스북 페이지 전체 도달률에 영향을 주게 되는데 때문에 오늘의 뉴스에 엮여있는 수치들을 분석하여 페이스북에서의 뉴스는 어떤 주제를 어떤 식으로 게시해야 반응이 좋은 지 인사이트를 내야 했다.도달률을 결과로 보고, 원인으로 기사 주제, 게시할 때 사용하는 헤드라인과 사진 등이라 했을 때,데이터를 분석하는 일에는 어느새 매의 눈이 되는 나! 집중 :)A. 전기세, 공카족(카페에서 공부하는 사람들)과 같이 생활에 밀접한 관련이 있는 주제이거나 특정 영화나 미디어의 어투, 워딩을 패러디한 헤드라인을 사용했을 경우, 또 전체적인 글의 분위기가 비교적 가벼운 기사들이 도달률이 높았다.B. 반면에 사드 배치로 인한 중국 관련 주식 하락, 미국 기준 금리 상승과 같이 거시적이거나 노령화, 보이스피싱과 같이 SNS 이용자의 대부분인 2030에게는 와 닿지 않는 주제인 기사는 도달률이 낮았다.C. 그리고 다른 부수적인 조건에 상관없이 높은 도달률을 달성하는 기사 주제가 있었는데 그건 바로 대기업이나 정부의 비리 관련된 주제의 기사였다. 요즘 사회 분위기를 대변하는 데이터가 아니었나 싶다. ‘어떤 데이터든 그 데이터가 발생된 이유(원인)가 있고 그 데이터는 또 다른 결과의 이유가 되기도 한다. 하지만 그런 데이터들 역시 목적이 없으면 한낱 글자나 숫자에 불과하다. 즉, 데이터는 그에 맞는 목적을 갖게 될 때 빛난다.’라는 게 현재까지 내린 결론이다.앞으로도 핀다는 데이터를 통해 다양한 소비자들의 반응을 보고 컨텐츠와 서비스의 방향을 고도화해 나갈 예정이다. 그 과정에서 내가 분석한 인사이트들이 반영이 되고, 조금씩 더 나은 컨텐츠와 더 발전된 서비스로 세상과 만날 수 있다면 정말 기쁘지 않을까! :)ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎ핀다의 데이터 꿈나무,권영진 드림Database AnalystYoungjin from Finda#핀다 #데이터애널리스트 #DataAnalyst #팀원소개 #인사이트
조회수 3813

앱(App)의 첫인상 = 홈 배너

첫인상은 한번 각인되면 쉽게 바뀌지 않는다는 말을 한 번쯤은 들어보셨을 텐데요. 표정, 옷매무새 등으로 자신을 어떻게 표현하느냐에 따라 첫인상이 좌지우지됩니다. 그렇다면 앱(App)에서의 첫인상은 어디서 결정될까요?바로 '홈 배너'입니다.'데일리호텔'앱 2.0 버전에는 기존에 없던 홈화면이 추가되면서 마케팅 성격의 배너 영역이 확대되었습니다.(이미지 참고) 해서 데일리의 첫인상을 책임질 새로운 배너 가이드의 필요성을 느끼게 됩니다.(좌)기존 앱 구동시  (우)업데이트 된 2.0 버전 앱 구동시01 무엇을 고려해야 할까?홈화면에는 '데일리호텔/데일리고메' 버튼이 새로 생성되었습니다. 사실상 두 버튼이 예약을 위한 제일 첫 단의 경로였기 때문에 이 버튼의 주목성을 방해하지 않는 선에서 레이아웃 및 톤 앤 매너를 정의해야 했어요. 또한 영역이 커진 만큼 주목도가 높아지기 때문에 유저가 지루함을 느껴서는 안 되었죠.때문에 크게 이러한 목표를 두었습니다.첫 번째. 디자인 개선두 번째. 프로덕트(UI)와의 조화위 두가지를 기반으로 데일리의 브랜드 디자인 키워드에 맞춰 아래와 같은 구체적인 방향을 설정하였습니다.02 타사 써칭 및 분석가이드를 잡기에 앞서, 타사의 경우 홈배너를 어떻게 활용하고 있는지 조사가 필요했습니다. 많은 자료들을 모아 분석해본 결과 데일리의 경우 크게 세 가지 형태로 배너를 표현할 수 있음을 도출할 수 있었죠.첫 번째. 텍스트 + 오브젝트를 함께 살리는 안두 번째. Full Image를 사용하여 하단에 텍스트 박스를 기재하는 안세 번째. 이미지에 Dim처리를 한 후 텍스트를 기재하는 안단순히 디자인의 심미성 영역을 넘어서 많이 사용되는 호텔과 레스토랑 이미지를 실제로 적용시킬 수 있는지의 판단 또한 필요했습니다. 호텔 내부 이미지의 경우 누끼(*오브젝트만 남기고 배경을 지우는 작업)를 딸 수 없는 경우가 많았기 때문이죠.03 우리와 어울리는 컬러는?배너의 레이아웃이 얼추 뼈대를 드러내는 시점에 함께 적용시킬 수 있는 컬러를 찾아야 했습니다. 앞서 말했듯이 예약 경로인 버튼의 주목성을 해치지 않는 톤 앤 매너를 유지하고, 마케팅적인 성격보다는 추천의 성격을 띠기 위해 차분한 톤이 필요했습니다.수많은 테스트 그리고 적용...또 적용04 결과최종적으로 반영된 사항은 아래와 같았습니다.1. 호텔/레스토랑 프로모션 배너 -> 누끼 혹은 그라데이션으로 이미지 처리2. 브랜드 메시지 배너 -> Full Image에 Black Dim 처리3. 누끼를 딸 수 있는 이미지 사용 권장4. 따뜻한 파스텔톤의 컬러 사용5. 워딩 Black/White Color로 통일(좌)구 배너  (우)개선된 배너해서 위와 같은 결과물을 얻을 수 있었습니다. 전과 후 배너 비교를 해보니 새삼 구 배너가 너무 많은 메시지 전달을 하려는 성향이 있었음을 느끼게 되었어요.(반성..) 또한, 이번 배너 개선 프로젝트를 통해 디자인 심미성뿐만이 아니라 많은 부분을 얻을 수 있었습니다.배너 개선 프로젝트로 얻은 것들마치며생각보다 길어진 프로젝트였지만 프로덕트와 마케팅적인 관점에서 많은 부분을 감안하고 작업을 진행한 만큼 모두가 만족할 수 있는 결과물이었습니다. 또한 홈화면의 홈배너 개선 후 많은 유저분들이 단순한 마케팅 메시지가 아닌 '라이프스타일 추천'을 받을 수 있어서 좋다는 피드백을 주셨습니다. 즉, 본 개선 작업으로 인해 브랜드 톤 앤 매너 또한 개선된 샘이었죠.앞으로도 데일리가 추구하는 방향을 유저에게 전달하기 위한 많은 과제가 놓여있다는 것을 알고 있습니다. 어떻게 하면 더 전달할 수 있을지, 더 가까워질 수 있을지, 더 특별한 삶을 보낼 수 있도록 도울 수 있을지 고민하는 데일리가 되도록 하겠습니다! 감사합니다.기획/진행 : Creative팀작성자 : Creative팀 Blair Ahn#데일리 #데일리호텔 #디자인 #디자이너 #인사이트 #브랜드 #앱디자인 #UI디자인
조회수 1017

[어반테이스트] 중국집 코스요리부터 선정릉 산책까지 완벽하게!

                                                                                          어반베이스의 복지문화 중 하나인 어반테이스트. 그 동안 훠궈, 파스타, 애슐리, 곱창, 등심 등 갖가지 맛있는 메뉴가 나왔죠. 이번 팀은 어떤 메뉴를 드시고 올 지 새삼 궁금해졌는데요, 이번 6기가 선택한 곳은 중국집이라는 소리가 들립니다?! 메뉴가 점점 다양해지고 있습니다. (박수박수)이번 어반테이스트는 한마디로[중. 국. 요. 리. 가. 성. 비. 甲]이라고 합니다. 생생한 후기를 만나보세요!회사에서 10분 정도 되는 거리에 맛있는 중국집을 찾았습니다. 조금 멀어서... 여기를 또 올까 싶었는데, 웬걸? 기본 메뉴만 먹었을 뿐인데 다른 중국집이 생각이 안날 정도로 맛있었습니다. 그래서 작정하고 [어반테이스트 6기] 로 다시 찾아오기로 결정! 자, 이제 먹으러 레츠 기릿-!먼저 히라이 위치 확인을 합니다.지하도 건너 선릉역 1번 출구로 나가서 오르막길을 조금 올라가서 오른쪽 골목으로 가면 등장! 특이점이 온 간판.. 전선이 간판을 갈라 버리고 있네요. 아무리 좋은 각도를 찾아도 전선이 나와버립니다. 내부 인테리어는 여느 중국집이랑 다를게 없긴 한데, 좀 더 앤틱해보이네요.사실 이곳이 낯이 익으신 분도 있으실텐데요. 바로 '나 혼자산다'에서 성훈이 트레이너와 함께 찾아온 중국집이었습니다. (원탁 테이블 사진이 없기에 대신 방송 캡쳐로)  메뉴판에 맛있는 메뉴가 한가득입니다. 코스요리를 먹을지, 단품 메뉴를 시킬지 고민하던 와중에 식사 끝내고 디저트와 산책을 겸하는 큰 그림을 그리며 가격대가 알맞는 점심 B코스(1인 25,000원 / 세가지 냉채 + 게살스프 + 팔보채 + 중새우(간소중하) + 청초우육사&꽃빵 + 식사) 를 시킵니다!기다리는 동안 심심하니까 포토타임도 한 번 가져보고..자 이제 아기다리고기다리 던 식사가 나오기 시작합니다!이제 본격적으로 한 번 먹어보겠습니다! 자 제일 먼저, 냉채로 시작하군요. 입맛 돋구기에는 딱입니다.다음은 게살 스프! 깔끔하네요.캬- 팔보채입니다. 빛깔이 영롱합니다. 평소 중국집에서 잘 시켜먹지 못하는 메뉴라 더욱 맛있네요. 다음은 중새우(간소중하)입니다. 이게 무엇인고 했더니, 우리가 흔히 알고 있는 깐쇼새우입니다! 맛있겠쬬? 양이 좀 작은 것 같지만, 그래도 맛있네요!다음은 청초우육사+꽃빵입니다. 이것 또한 무엇인고 했더니 고추잡채와 꽃빵이라고 생각하시면 되겠습니다. 사진이 지나치게 꽃빵에 집중된 것 같지만, 아무튼 맛있습니다.자 이제 코스요리가 끝났습니다. (더 나와주세요..) 이제 식사만 남겨두고 있습니다. 눈에 보기에는 양이 작아보이지만, 위가 크지 않은 사람은 다 먹기 힘들정도? 대식가 분들은 성에 차지 않을 수도 있겠네요.자 이제 코스 요리는 끝나고 식사시간! 역시 중국집은 짜장면 아니겠습니까. 그렇죠, 중국집은 짜장면이죠.중국집의 시그니처, 짜장면을 먹고 후식으로 오렌지까지 클리어 했습니다.식사까지 다 먹고나니 이제 진짜 배가 부르네요. 이번 식사를 한 장면으로 표현하면 요정도?여기서 끝인줄 알았죠? 알찬 식사시간 이후에 바로 미팅이 있다는걸 깜빡한 석재님은 회사로 뛰어가시고..(눈물) 주희님과 성민님은 어반 테이스트 2시간의 피날레를 무엇으로 할지에 대해 고민하다가 날도 좋으니 산책을 하고 가기로 합니다. 이것이야 말로 완벽한 풀코스 어반테이스트입니다.(사진찍고 가기 위해 나타난 급한 표정)그래서, 소풍 가겠다고 말만 하고 아직 가보지 못했던 선릉과 정릉을 가보게 됩니다. 어반테이스트는 식사 시간이 두시간으로 주어지기 때문에 이런 여유도 즐길 수 있답니다. 가는 길에 별다방이 있길래 음료 하나씩 사들고 선정릉으로 출발! 1분이라도 더 소풍 기분을 간직하고 싶어서 서둘러 발걸음을 옮기기로 했습니다. (소풍에 신난 30대)한창 가을 분위기가 물씬한 선정릉은 차분하고 따스한 분위기였어요. 시간이 더 있었으면 낮잠도 자고 가고 싶었는데 ... ㅠㅠ 가을 느낌만 안고서 현실로 돌아왔습니다 ㅎㅎ 자, 속도 든든히 채우고 볕 좋은 날 광합성까지 했으니 다시 삶의 현장으로 뛰어가봅니다. 중국집 코스요리와 선정릉 산책까지! 정말 알찼던 이번 어반테이스트였습니다.이번팀은 중국집 코스요리 + 가을날의 산책까지 가장 알찬 어반테이스트를 보내지 않았나 싶네요. 여태껏 메뉴가 한번도 겹치지 않은 만큼, 다음 팀의 행선지 또한 궁금해집니다. 가시는 팀마다 생각지도 못한, 아주 맛있는 것을 드시고 오니 다음 어반테이스트의 메뉴도 기대해 보겠습니다! 출처: https://blog.naver.com/urbanbaseinc 
조회수 7120

안드로이드 앱의 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 #개발후기 #인사이트
조회수 1623

워크인사이트 프론트엔드 개발환경

조이코퍼레이션은 오프라인 고객 분석 서비스인 워크인사이트를 만들고 있습니다. 워크인사이트는 스마트폰 신호를 통해 매장 방문객의 출입 및 체류 패턴을 측정하고 분석합니다. 분석된 데이터는 웹 대시보드를 통해 한 눈에 파악하기 쉬운 형태로 매장에 제공됩니다. 매장들은 이 대시보드를 보고 중요한 판단과 의사 결정을 내리기 때문에 대시보드는 보기 쉬워야 하고 쓰기 편해야 하며 무엇보다 아름다워야 합니다. 조이의 빅데이터 기술을 통해 분석된 데이터를 매장에 효과적으로 전달하기 위해 프론트엔드 기술에 많은 노력을 기울이고 있습니다. 이 글에서는 조이의 대시보드를 만들기 위해 사용하고 있는 기술과 개발 환경 그리고 기술적인 관점에서 고민하고 있는 부분들을 간략하게 공유하고자 합니다.그림1. 대시보드 화면사용하는 기술AngularJS: AngularJS를 기본 프레임워크로 사용하고 있습니다. AngularJS는 SPA (Single Page Application) 형태의 웹 애플리케이션을 빠르게 개발할 수 있도록 도와주는 MVC 프레임워크입니다. 조이에서는 현재 프로덕션 버전인 1.3.x를 사용하고 있습니다. 대시보드는 사용자의 이벤트에 따라 동적으로 데이터를 변경해야하는 애플리케이션적 요소가 많기 때문에 AngularJS의 양방향 데이터 바인딩의 유용함을 느끼고 있습니다.D3.js: 다양한 그래프를 아름답게 보여주기 위해서 D3.js를 사용합니다. D3.js는 데이터 시각화를 위한 자바스크립트 라이브러리로, HTML/CSS/SVG 등의 웹 기술을 이용해 그래프를 그릴 수 있습니다. 자유도가 매우 높아서 생각할 수 있는 많은 형태의 그래프를 그릴 수 있으며 부드러운 전환이나 애니메이션도 추가할 수 있습니다. 다만 초기 학습 비용이 높고 신경쓰지 않으면 너저분한 코드가 양산될 수 있다는 단점도 있습니다.CoffeeScript: 자바스크립트를 더 깔끔하고 효율적으로 사용할 수 있도록 Compile to JS 언어를 사용하는데, 여러 선택 사항 중 CoffeeScript를 사용하고 있습니다. CoffeeScript는 문법적 간결함 덕분에 타이핑을 줄이고 빠르게 코드를 작성할 수 있습니다. 특히 클래스와 클래스 상속 등을 문법적으로 지원하기 때문에 OOP적인 설계를 할 때 도움을 받았습니다. 하지만 자바스크립트와는 다른 새로운 문법을 익혀야하고 그마저도 일관성이 떨어지는 문제가 있습니다. 또 특별한 신경을 쓰지 않으면 가독성이 안 좋은 코드를 작성하기 쉽습니다. 조이에서는 Lint 툴과 코드 리뷰를 통해 코딩스타일을 엄격히 제한하고 있습니다.개발 환경빌드 및 배포: Bower와 npm(Node Package Manager)을 이용해서 패키지를 관리합니다. 빌드 시에는 JS Minify & Uglify, HTML/CSS 최적화, CoffeeScript Lint를 통한 코드 품질 검증, Karma를 이용한 테스트 수행 등의 과정을 거치며 이 모든 빌드 과정은 Grunt를 사용하여 자동화하고 있습니다. 빌드가 끝난 파일들은 AWS (Amazon Web Service)의 S3 저장소로 배포하고 있습니다. 배포 과정 역시 Grunt의 task로 자동화되어 있습니다.코드 관리: 모든 코드는 Jenkins로 통합되어 자동화된 테스트를 통과해야 합니다. 모든 커밋은Gerrit을 통해 다른 엔지니어의 리뷰를 거쳐야만 머지를 할 수 있습니다. 따라서 모든 코드는 적어도 둘 이상의 엔지니어가 이해하고 있습니다. 같은 코드에 대해 더 좋은 설계가 있는지 논의하면서 함께 코드를 발전시켜 나갑니다. 더 좋은 설계가 발견될 때마다 수시로 리팩토링을 진행합니다.프로젝트 관리: 디자이너와 엔지니어 그리고 기획에 참여하는 데이터 분석가 등은 Trello를 이용해 태스크와 이슈를 관리합니다. 일주일을 한 번의 스프린트로 보고 매주 월요일에 일을 분배하고 금요일에 회고를 합니다. 이 과정에서 엔지니어도 기획에 능동적으로 참여할 수 있으며, 어떤 데이터를 어떤 형태의 그래프로 보여주어야 효과적인지를 함께 고민할 수도 있습니다.그림2. 트렐로를 이용한 프로젝트 관리지속적으로 고민하는 부분성능 이슈: 대시보드에는 많은 수치 데이터를 다룹니다. API 서버로부터 하나의 큰 JSON 데이터를 받아서 시간별/일별/요일별/날씨별/최고기온별/평일휴일별 방문객 정보, 방문전환율/체류전환율/구매전환율 등의 지표를 그리기 위한 데이터로 가공합니다. 여기에는 계산량이 적지 않기 때문에 성능에 대한 고민을 많이 하게 됩니다. 연산 로직을 더 간단히 하거나 더 적게 Draw/Redraw 하는 방법을 고민하고 응답성을 향상시키기 위해 Async하게 연산하는 등의 고민을 합니다. 설계: 대시보드는 빠르게 업그레이드됩니다. 기능이 추가되고 변경됨에 따라 그에 맞는 좋은 설계도 계속해서 변합니다. 수시로 진행하는 리팩토링이 좋은 설계를 만든다고 생각합니다. 따라서 리팩토링에 쓰는 시간을 아까워하지 않습니다.테스트: 테스트는 매우 중요합니다. 특히 버그로 인해 잘못된 데이터가 보여지는 것은 용납될 수 없습니다. 그래서 데이터를 가공하는 로직에 대한 테스트는 엄격하게 수행됩니다. 설계 단에서도 테스트하기 쉬운 코드를 작성하려고 노력합니다.최신 기술: 조이의 프론트엔드 팀은 최신 기술에 민감합니다. Gulp, Angular 2.0, EcmaScript6, TypeScript, React와 같은 자바스크립트 최신 기술들에 관심을 갖고 그들의 기본 철학이나 장단점들을 파악하려고 노력합니다. 때때로 우리에게 더 잘 맞는 기술이 등장하면 과감하게 적용하기도 합니다.맺음말조이는 임베디드 기술과 빅데이터 기술을 보유한 기술 회사입니다. 그러나 프론트엔드 기술 역시 그 못지 않게 중요하게 생각하고 있습니다. 말뿐이 아니라 며칠 전에는 OKKY 자바스크립트 컨퍼런스에 후원을 하고 좋은 자바스크립트 개발자들을 만나기 위해서 부스를 차리기도 했습니다. 앞으로도 프론트엔드 기술 관련 컨퍼런스에 후원도 하고 기여도 계속 할 계획입니다.저도 조이에서 엔지니어로 일하면서 훌륭한 동료 엔지니어들과 함께 많은 성장을 했습니다. 무엇보다 기술적인 욕심과 의욕이 넘치는 분위기 속에서 일하는 것 자체가 즐겁고요. 혹시 이 글을 읽고 위와 같은 고민을 공유하고 폭풍 성장을 함께 할 멋진 자바스크립트 개발자가 있다면 이 글을 읽어보시길 바래요 :)그림3. OKKY 자바스크립트 컨퍼런스 부스#조이코퍼레이션 #개발팀 #개발자 #개발환경 #업무환경 #기업문화 #조직문화
조회수 1238

채널 데스크 프론트엔드 기술 스택

오프라인 고객 분석 솔루션 워크인사이트를 개발해 온 조이는 최근 온라인 접객 서비스 채널을 런칭했습니다. 이 글은 채널과 관련된 기술 블로그의 첫번째 글로 채널 데스크 프론트엔드(웹, 윈도우, OSX)의 기술 스택 및 개발 환경을 소개하도록 하겠습니다.React채널 개발을 처음 시작할 당시 (지금으로부터 1년 전) 에 워크인사이트 대시보드 및 기타 사내 툴에서는 AngularJS 1을 사용하고 있었습니다. 비교적 적은 코드로 복잡한 애플리케이션을 빠르게 만들 수 있는 점에는 만족했지만 퍼포먼스면에서는 아쉬운 부분이 많았습니다. 따라서 새로운 프레임워크 및 라이브러리를 리서치 했고 매우 가볍고 렌더링 퍼포먼스 면에서 AngularJS 1 대비 우위에 있던 React 를 사용하기로 결정했습니다.컴포넌트의 설계 패턴은 Redux를 만든 Dan이 제안한 Container 와 Presentational 컴포넌트를 구분하는 방식으로 설계하고 있습니다. 따라서 Container 가 data fetch 및 update 등의 액션을 실행하고 Presentational 컴포넌트들을 조합하여 렌더링을 하게 됩니다.React를 실제 1년째 사용해 본 결과 저를 비롯한 팀원들은 매우 만족하고 있습니다. 구조, 스타일, 동작을 한 컴포넌트로 묶어 재사용성이 매우 높아졌으며 React의 휴리스틱한 Dom diff algorithm 덕분에 렌더링 퍼포먼스에서도 많은 이득을 얻을 수 있었습니다.Facebook Flux Utils아키텍쳐는 페이스북이 제안한 flux 철학에 따라 설계되었습니다. flux를 구현하기 위한 기본적인 유틸리티 기능을 제공하는 Flux Utils을 사용합니다. Flux의 많은 구현체 중에 요즘 가장 인기인 Redux도 고려했었습니다. 저희가 프로젝트를 시작할 당시에 Redux는 5~6개월밖에 되지 않은 프로젝트였고 거의 Dan의 1인 프로젝트였기 때문에 향후 메인터넌스를 장담할 수 없다고 판단했습니다. 그보다는 페이스북이 만든 Flux Utils가 그런 면에서는 더 안전할 거라고 생각했던 것이죠.약 1년 정도 Flux Utils로 개발해오며 몇 가지 문제를 겪게 되었습니다. 애플리케이션이 커지면서 관리해야할 State가 많아지고 그들 사이의 의존성 관리 때문에 Store의 복잡도가 빠르게 증가했습니다. 그에 따라 테스트가 어려워지고 올바른 유닛테스트를 위해서는 테스트 코드 역시 매우 복잡해지는 문제가 있었습니다.그래서 Redux를 다시 리서치하게 되었고, 결론적으로 “단일 Store, 다수Reducer” 라는 Redux의 철학을 통해 State 관리 로직(Reducer)을 단순하고 테스트도 쉽게 유지할 수 있겠다는 생각을 하게 되었습니다. 뿐만 아니라 그 동안 설계와 관련되어 고민하고 필요한 경우 저희 스스로 개발해서 사용하던 많은 부분이 Redux의 서브 프로젝트 형태로 (redux-actions, redux-thunk, reselect 등) 개발되어 사용되고 있는 것을 발견해서 Redux로의 마이그레이션을 결정했고 현재 진행 중에 있습니다.Electron이 글의 도입부에서 이야기한 것처럼 채널 데스크는 윈도우용, OSX용 애플리케이션으로도 제공됩니다. 채널 개발 초기 당시 윈도우, OSX 각각 네이티브로 만들 리소스가 부족했기 때문에 웹 기술 기반으로 네이티브 앱을 만들 수 있는 다양한 솔루션들을 리서치했고 그 중 Electron을 선택하게 되었습니다.Electron은 제가 정말 좋아하는 제품인 Slack, Simplenote에서 사용하고 알려져 있고 국내에서는 Remember 등에서 사용하고 있습니다. 초기 개발 당시에는 안정성에 의문을 제기하는 개발자들도 많았고 저희도 여러 문제와 삽질(인증, 패키징, 이슈 레포팅의 어려움, 메모리릭 등등)을 많이 겪긴 했습니다만 개인적으로는 충분히 프로덕션에 쓸 수 있을 정도 수준이라고 생각합니다. 무엇보다 프론트엔드 개발자가 매우 적은 노력으로도 네이티브 데스크탑 앱을 만들 수 있는 장점이 다른 모든 문제점을 상쇄하고도 남습니다.언어개발 언어로는 자바스크립트 ES6를 사용합니다. 언어를 선택할 당시에도 여러 옵션이 있었는데 가능하면 실험적이지 않고 표준을 사용하는 것이 미래 유지보수에 안전하다고 판단했습니다. 또한 다른 자바스크립트 대안 언어를 사용하지 않더라도 ES6 (일부 ES7 포함) 스펙도 충분히 효율적인 개발이 가능하다고 생각했습니다.코딩 스타일은 기본적으로 Airbnb의 코딩 스타일 가이드라인을 따르며 조이의 상황과 맞지 않는 부분은 엔지니어들과 상의 후 수정해서 사용하고 있습니다. 스타일 체크는 ESLint로 자동화한 뒤 Circle CI와 붙여서 모든 풀리퀘스트에 대해 점검하고 있습니다.테스트초기 개발할 때는 테스트 코드를 별도로 붙이지 않았습니다. 고객의 요구와 기타 상황에 따라 기획과 설계가 크게 변경되기도 했고 그 때마다 기민하게 반응하기 위해서, 어느 정도 확립된 제품이 되기 이전에는 테스트 코드는 작성하지 않는 것이 좋다고 판단했습니다. 이제는 많은 부분이 확정되었고 안정성이 중요해지기 시작했으며 애플리케이션이 커지면서 자동화된 테스트는 필수가 되기 시작했기에 최근에 도입을 하고 있습니다.테스트를 위한 도구는 Jest, Enzyme 등을 사용합니다. Presentational 컴포넌트에 대한 테스트는 props에 따라 원하는 형태로 렌더링이 이루어지는지, 이벤트에 따라 콜백이 잘 실행되는지 등의 Spec 을 작성합니다. Container 컴포넌트에 대한 테스트는 각종 이벤트 및 동작을 시뮬레이션하고 그에 따라 Action이 잘 발생하는지 또는 내부 state가 잘 변경되는지를 테스트합니다. 또한 Store (또는 Reducer), Action Creator, Model, Util 등 모든 구성 요소에 대한 테스트를 붙이려고 노력하고 있습니다. 유닛 테스트가 아닌 e2e 테스트 혹은 css 스타일 테스트 등은 하지 않고 있습니다.빌드 및 배포현재 채널 데스크는 Client-side rendering을 합니다. 초기 로딩 속도가 느리다는 단점이 있어서 Server-side rendering으로의 전환도 고려하고 있습니다. 이미 Node.js 를 사용하고 있어서 Isomorphic Javascript의 형태로 어렵지 않게 전환이 가능합니다.작성된 자바스크립트는 Babel로 컴파일되고 Webpack으로 번들화됩니다. css를 포함한 각종 리소스들 역시 Webpack을 통해 처리됩니다. 웬만한 작업은 npm과 Webpack으로만 자동화하려고 했으며, Electron과 관련된 작업(패키징, 인증 등)들만 gulp를 이용해 자동화됩니다. 모든 리소스들은 Node.js + express 서버로 Serving 되고, Node.js 앱은 Docker로 빌드되어 AWS EC2로 배포됩니다.마무리이상으로 채널 데스크 프론트엔드의 기술 스택을 소개해드렸습니다. 앞으로 각 부분 별로 저희 팀이 고민해 온 문제들과 해결 방법을 공유하고자 합니다. 뛰어난 개발자 분들의 많은 관심과 피드백 부탁드립니다!#조이코퍼레이션 #개발자 #개발팀 #인사이트 #경험공유 #일지
조회수 1259

바둑 아마 4단, 싱어송라이터…이색 신입사원을 만나다[上]

봄이다. 비록 미세먼지가 코끝을 괴롭히지만 완연히 따뜻해진 날씨와 남쪽부터 올라오는 개화 소식이 봄이 왔음을 알려준다. 계절의 시작이 봄이라면 회사원의 봄은 신입사원 때 아닐까? 푸릇푸릇한 새싹처럼 새로운 마음가짐도, 일하며 아름다운 꽃을 피우고자 하는 열정도 가득했던 시절. 누구나 한 번쯤은 겪었을 시절. 그래서 준비했다! 이제 입사한 지 반년에서 1년 남짓, 한창 일 배우며 눈망울 초롱초롱한 시기를 겪고 있는 신입 사원들의 이야기를. 첫 번째 편에선 입사 전 각자의 분야에서 열정을 불태워본 경험이 있는 두 사람을 소개한다. “돌 좀 놔드릴까요?” 대답이 떨어지기 무섭게 흑돌과 백돌을 척척 배치하는 손길이 예사롭지 않다. 바둑 문외한의 눈에도 뭔가 규칙이 있는 듯 보였다. 예전 수를 복기하는지 물었더니 그건 아니고 바둑에도 이른바 ‘교과서’가 있단다. 한국기원을 나선 지 10년이 흘렀지만 수없이 놓던 돌의 자리를 몸이 기억하는 모양이다. 열네 살에 아마 4단증까지 따낸 이수림(삼성전자 무선사업부 전략마케팅실)씨다.▲해맑은 미소에 안심해선 안 된다. 자칭 ‘바둑 마니아’ 선배 둘을 다면기(多面棋, 바둑에서 한 사람이 여러 사람을 상대로 동시에 대국하는 일)로 이기는 실력자이니!중학교 진학 대신 한국기원행(行)… 1년 만에 ‘컴백’“아휴, 부끄러워요. 같이 바둑 배운 동기 중엔 프로 선수가 된 언니∙오빠들도 있거든요.” 손사래를 치지만 아마 4단은 “프로 입단도 가능하다”고 평가 받는 실력이다. 그런 그가 바둑 대신 삼성전자를 택했다. 왜?▲어릴 적 수림씨에게 바둑은 재밌는 놀이였다. 위 오른쪽 사진은 그가 일곱 살 때 시(남양주) 바둑대회에서 받은 인증서“프로 입단을 준비할 생각은 없었어요. 어릴 때 바둑이 배우고 싶어 부모님을 졸라 바둑학원에 등록하고 이후 꾸준히 둬오긴 했지만 성격이 워낙 외향적이고 사람 만나는 걸 좋아해 프로 선수가 맞을 것 같진 않았거든요. 바둑은 조용한 스포츠니까요. 그래도 바둑을 좋아하긴 해서 그와 관련된 일을 하고 싶었고 한때 전문 해설가도 꿈꿨죠.” 바둑 방송에 나오는 캐스터나 해설가가 되려면 바둑을 잘 알아야, 아니 일단 잘 둬야 했다. 중학교 진학 대신 한국기원행(行)을 택한 것도 그 때문이었다.“학교 생활을 병행하며 바둑까지 잘 두긴 어렵다고 생각했어요. 그래서 중학교 대신 기원에 다니기로 했죠. (아마 4단) 단증도 그때 땄고요.” 이후 여덟 달 동안 종일 기원에서 바둑만 뒀다. 처음엔 재밌었다. “꿈을 향해 가는 길”이라고 생각해서다. 그런데 문득 주변을 둘러보니 죄다 절박한 맘으로 프로 기사를 향해 달려가는 사람들뿐이었다. “바둑 연구생은 대부분 프로 입단을 꿈꿔요. 근데 사실 그게 정말 어렵거든요. 입단 시험을 거쳐 프로가 되는 사람 자체가 너무 적고 심지어 나이 제한도 있어요. 그런 분들의 꿈과 제 꿈은 차이가 크다고 생각했어요. 그리고 제 성격상 조용히 바둑 두는 건 1년이 한계였던 것 같아요, 헤헤.”교환학생 시절 삼성 로고 보며 해외영업맨 꿈 키워그렇게 다시 학교로 돌아왔고, 또래보다 1년 먼저 고등학교에 입학했다. 하지만 성적은 중학교 교과 과정 3년을 이수한 동급생에 비해 당연히 뒤처졌다. 전교생이 500명인 학교에서 석차가 300등 밖으로 밀려났다. “바둑 세계를 떠나며 ‘내가 정말 바둑 해설가가 될 수 있을까?’란 생각이 들었어요. 어릴 때부터 좋아한 일이었는데 중도에 방향을 틀어버린 것도 맘에 걸렸고요.” 목표가 사라지자 의욕도 붕 떴다. 그러던 중 새로운 기회가 왔다. 미국 국립 교환학생 프로그램에 참여하게 된 것.▲수림씨는 1년간 미국에서 교환학생으로 지내며 잃었던 목표를 다시 찾았다. 위 사진은 당시 친구들과 떠난 여행 도중 호스슈밴드(Horseshoe Band)에서 찍은 것. 아랫줄 오른쪽에서 두 번째가 수림씨다“솔직히 공부가 너무 하기 싫었어요(웃음). 교과 과정을 따라잡기가 너무 힘들었거든요. 그 와중에 교환학생 기회가 와 덜컥 잡았죠. 미국에서 지내며 예전엔 별 생각 없이 지나쳤던 한국 제품이 너무 사랑스럽게 다가왔어요. 한국 글로벌 기업 제품을 보며 ‘우리나라 것’이라고 말할 때의 뿌듯함이란!” 당시 그의 눈에 제일 자주 들어온 게 ‘삼성’이었다. “정말 그땐 삼성 로고만 봐도 뭉클했어요. ‘제품 하나로도 저렇게 우리나라를 해외에 알릴 수 있구나’ 싶더라고요. 하루는 뉴욕을 방문했는데 타임스퀘어 전면에 삼성 광고가 떡하니 박혀 있는 거예요. 순간, 심장이 두근거렸어요. ‘저 회사에서 꼭 일하고 싶다. 한국을 대표하는 글로벌 기업의 일원이 되고야 말겠다!’ 그런 생각을 그때 처음 했던 것 같아요. 교환학생으로 지내는 내내 그 목표를 잊지 않고 있다 귀국했죠.”‘글로벌 기업 입사’란 목표를 세운 수림씨는 무섭게 공부에 집중했다. “고 1 때 성적으론 글로벌 기업에 절대 갈 수 없잖아요(웃음). 정말 그때만큼 열심히 공부한 적이 없었던 것 같아요. 밥 먹는 시간도 아까워 국에 밥 말아 책상으로 가져갔을 정도니까요. 지금 다시 그렇게 하라면 절대 못할 거예요.” 노력은 그를 배신하지 않았다. 300등 언저리였던 성적은 고 2∙3 때 전교 1등으로 뛰어올랐다. 덕분에 원하는 대학에 진학할 수 있었고 해외 영업 전문가를 꿈꾸며 불어불문학을 전공했다. 그리고 거짓말처럼 꿈이 이뤄졌다. 삼성전자의 일원이 된 것이다.“삼성전자 제품은 일상과 가장 밀접하게 관련돼 있잖아요. 스마트폰만 해도 눈뜰 때부터 잠들기 전까지 늘 곁에 두고 쓰니까요. 그래서 길 가다 갤럭시 스마트폰 쓰는 사람들만 봐도 뿌듯해요. 특히 해외 출장 가서 접하면 더 벅차죠. ‘오래 꿈꿔온 일을 진짜 하고 있다’는 생각이 들어서요.” 바둑에 대한 미련은 없을까? 그의 대답은 단호했다. “아쉽진 않아요. 물론 바둑은 지금도 좋아하죠. 그래도 열정으로 치면 바둑에 매진했던 1년보다 글로벌 기업에 들어가기 위해 공부했던 고교 시절이 훨씬 컸습니다.”“바둑으로 익힌 역량, 삼성에서 발휘해보고 싶어요”수림씨는 요즘 마음이 분주하다. “배워야 할 게 많아 바쁘긴 하지만 앞으로의 회사 생활이 너무 기대돼요. 지금은 여기저기서 도움을 받기만 하는데 얼른 업무 역량을 쌓아 선배들을 도울 수 있는 후배가 되고 싶어요.” 그의 ‘바둑 사랑’이 끝난 건 물론 아니다. “이래저래 바둑 덕을 많이 봤어요. 회사 선배들과도 바둑 덕에 빨리 친해질 수 있었죠. 바둑은 주어진 상황에서 상대의 수를 예측한 후 그에 맞춰 최선의 수를 두는 게임이에요. 생각해보면 그런 방식은 어떤 일에나 도움이 돼요. 영업 전략도 경쟁사의 수를 보고 ‘우린 어떤 수를 둬야 최선일까?’ 궁리하는 거잖아요. 바둑을 배우며 익힌 제 역량을 잘 살려 언젠가 삼성전자 해외 영업 전략을 세워보고 싶습니다.” “안녕하세요.” 차분하고 따뜻한 음성에 귀가 번쩍 뜨였다. 김대능(삼성전자 무선사업부 개발실)씨는 그 목소리를 십분 활용, 싱어송라이터로 활동한 이력이 있다. ‘능라이터’란 이름으로 실제 음반도 냈다. 기타 선율과 잘 어우러지는 그의 노래 ‘너가 지나간 나’는 귀에 쏙쏙 박히는 가사가 일품이다. 경험담이냐고 슬쩍 물어봤더니 웃으며 비밀이란다, 알고 들으면 재미없지 않겠냐며.곡의 영감은 여러 곳에서 얻지만 주로 가사부터 쓴 후 그에 맞춰 곡을 붙인다. “제 삶은 한정적일 수밖에 없기 때문에 간접적으로나마 여러 감정을 느껴보려 노력해요. 드라마나 영화, 책을 보다가 와 닿는 글이나 대사를 발견하면 그에 대한 제 생각이나 느낌을 다시 글로 정리해 ‘삼성 노트’ 애플리케이션에 저장해두곤 하죠.”8년간 독학으로 작곡 공부… ‘능라이터’로 음반 발매곡 소재에 꼭 기승전결이 있어야 하는 건 아니다. 최근 발표한 ‘starryNstellar’엔 “별이 빛나는 밤에 보고 싶은 너를 생각해”란 가사가 등장한다. 서양미술사를 공부하다 빈센트 반 고흐의 그림 ‘별이 빛나는 밤’(1889)에서 영감을 얻어 쓴 곡이다. “그림이 마음에 들어 한참을 보다 고흐가 어떤 기분으로 이걸 그렸을지 궁금해졌어요. 당시 즐겨 보던 드라마 ‘도깨비’(tvN) 여주인공 ‘지은탁’ 대사와 그 질문을 연결시켜 노랫말을 썼죠.” 인상파 화가의 그림과 판타지 로맨스 드라마, 전혀 무관해 보이는 둘을 절묘하게 연결시켜 멋진 곡으로 탄생시킨 것이다.▲‘starryNstellar’ 앨범 재킷 사진. 작곡에 영감을 준 고흐 작품 ‘별이 빛나는 밤’을 연상시키는 디자인이 인상적이다▲피아노를 좋아하던 소년은 어느덧 전문 작업실에서 노래를 만들고 부르는 싱어송라이터로 성장했다대능씨는 어릴 때부터 음악에 소질을 보였다. 특히 피아노를 좋아해 남들 앞에서도 곧잘 연주했다. “중학교 진학을 앞두고 예술중학교에 가볼까, 고민한 적도 있어요. 결국 일반 중학교를 거쳐 대학에선 전기전자공학을 전공했지만 음악은 계속하고 싶었죠. 본격적으로 작곡을 해봐야겠다고 결심한 건 스무 살 때부터였을 거예요.”그는 밴드 활동을 한 적도, 음악 동아리에 가입한 적도 없다. 그저 음악이 좋아 8년간 독학으로 공부하고 노래를 만들었다. “그렇게 몇 년 지내다 문득 생각했어요. ‘나도 음반 낼 수 있겠는데?’ 대형 기획사를 거쳐야 음반을 낼 수 있던 시절은 지났으니까요. 사람들에게 제 음악을 들려주고 싶단 생각이 들기도 했고요.” 만들어둔 곡으로 작업을 시작했지만 편곡부터 믹싱까지 다양한 준비가 필요했다. 꼬박 1년간 준비한 끝에 첫 앨범을 품에 안았다. “결과물 자체는 엄청 뿌듯했지만 막상 발매 시점엔 이미 수백 번씩 부르고 들으며 녹음한 곡이라 그런지 별 감흥이 없더라고요. ‘좀 더 잘 부를 수 있었는데’ 싶은 부분이 들리면 후회도 되고.”(웃음)“카페서 내 노래 들리면 뿌듯… 일도 음악만큼 소중”앨범 발매는 그에게 새로운 경험을 안겼다. “강남 쪽에서 친구들과 모임이 있었어요. 어디선가 귀에 익은 멜로디가 흘러나오더라고요. 제 노래였죠. 기분이 정말 좋았어요. 곡 만드는 일은 오래전부터 해와서 그런지 신기하거나 대단하다고 느껴본 적이 별로 없어요. 그런데 앨범을 발표한 후 카페나 가게에서 제 노래가 나오고 회사에 들어온 후엔 주변 분들도 좋아해주시니 괜히 뿌듯하고 그랬어요. 특히 제가 모르는 사람이 제 노래를 듣고 있다고 생각하면 기분이 묘하죠.”▲대능씨는 “음악 작업은 대체로 혼자 조용히 했었는데 회사원이 된 후 무대에 서는 일이 부쩍 잦아졌다”고 말했다. 사진은 삼성전자 신입사원 연수 당시 공연 무대에 올랐던 모습앨범까지 낸 그가 가수의 길을 걷는 대신 삼성전자에 입사한 이유는 뭘까? 대능씨의 답은 의외로 간단했다. “음악을 포기하고 회사를 선택한 게 아니니까요.” 그도 한때는 오디션 프로그램에 도전하거나 기획사를 기웃거렸다. 그러다 문득 생각했다. ‘전공 공부가 적성에 안 맞는 것도 아니고 지금껏 전공과 음악 둘 다 잘해왔잖아. 엔지니어로 일하면서 좋아하는 음악도 병행할 수 있지 않을까?’대능씨가 삼성전자에 입사한 건 지난해. 하지만 1년가량 태스크포스(TF)에서 근무한 탓에 지금 부서에 배치된 건 얼마 되지 않는다. “실무 능력으로만 따지면 말 그대로 ‘신입’이에요. 다행히 선배님들께서 업무를 잘 알려주셔서 적응해가고 있습니다. 뛰어난 실력을 갖춘 엔지니어가 정말 많거든요. 일을 배우는 입장에선 정말 감사한 일이죠. 이렇게 훌륭한 분들이 모인 곳에서 일하고 있단 게 자랑스러워요. 취업을 준비 중인 후배들에게도 ‘꼭 열심히 해서 우리 회사 오라’고 추천 많이 합니다.”“혼자서 차근차근 음반 냈듯 일에서도 답 찾아갈 것”대능씨에 따르면 음악과 일은 다른 듯 닮았다. “제가 작곡을 독학했잖아요. 아무리 악기를 다룰 줄 안다 해도 컴퓨터로 곡 작업을 하는 건 또 전혀 다른 차원이거든요. 기껏 만들어놔도 막상 들어보면 생각한 것과 너무 달라 좌절할 때가 한두 번이 아니었어요. 회사 일도 비슷하다고 생각해요. 모르는 게 많아 막막하죠. 주변에서 많이들 도와주시지만 결국 잘해내려면 저 스스로 어떻게든 부딪쳐봐야 하니까요. 막막함을 이겨내고 음반 내는 데 성공했던 것처럼 일에서도 하나씩 답을 찾아가보려 합니다.”#삼성 #삼성전자 #구성원인터뷰 #기업문화 #사내문화 #조직문화 #신입사원 #임직원 #이색신입사원

기업문화 엿볼 때, 더팀스

로그인

/