스토리 홈

인터뷰

피드

뉴스

조회수 3171

Apache Spark에서 컬럼 기반 저장 포맷 Parquet(파케이) 제대로 활용하기 - VCNC Engineering Blog

VCNC에서는 데이터 분석을 위해 다양한 로그를 수집, 처리하는데 대부분은 JSON 형식의 로그 파일을 그대로 압축하여 저장해두고 Apache Spark으로 처리하고 있었습니다. 이렇게 Raw data를 바로 처리하는 방식은 ETL을 통해 데이터를 전처리하여 두는 방식과 비교하면 데이터 관리비용에서 큰 장점이 있지만, 매번 불필요하게 많은 양의 데이터를 읽어들여 처리해야 하는 아쉬움도 있었습니다.이러한 아쉬움을 해결하기 위해 여러 논의 중 데이터 저장 포맷을 Parquet로 바꿔보면 여러가지 장점이 있겠다는 의견이 나왔고, 마침 Spark에서 Parquet를 잘 지원하기 때문에 저장 포맷 변경 작업을 하게 되었습니다. 결론부터 말하자면 74%의 저장 용량 이득, 10~30배의 처리 성능 이득을 얻었고 성공적인 작업이라고 평가하지만 그 과정은 간단하지만은 않았습니다. 그 과정과 이를 통해 깨달은 점을 이 글을 통해 공유해 봅니다.Parquet(파케이)에 대해Parquet(파케이)는 나무조각을 붙여넣은 마룻바닥이라는 뜻을 가지고 있습니다. 데이터를 나무조각처럼 차곡차곡 정리해서 저장한다는 의도로 지은 이름이 아닐까 생각합니다.Parquet을 구글에서 검색하면 이와 같은 마룻바닥 사진들이 많이 나옵니다.빅데이터 처리는 보통 많은 시간과 비용이 들어가므로 압축률을 높이거나, 데이터를 효율적으로 정리해서 처리하는 데이터의 크기를 1/2 혹은 1/3로 줄일 수 있다면 이는 매우 큰 이득입니다. 데이터를 이렇게 극적으로 줄일 수 있는 아이디어 중 하나가 컬럼 기반 포맷입니다. 컬럼 기반 포맷은 같은 종류의 데이터가 모여있으므로 압축률이 더 높고, 일부 컬럼만 읽어 들일 수 있어 처리량을 줄일 수 있습니다.https://www.slideshare.net/larsgeorge/parquet-data-io-philadelphia-2013Parquet(파케이)는 하둡 생태계의 어느 프로젝트에서나 사용할 수 있는 효율적인 컬럼 기반 스토리지를 표방하고 있습니다. Twitter의 “Julien Le Dem” 와 Impala 프로젝트 Lead였던 Cloudera의 “Nong Li”가 힘을 합쳐 개발한 프로젝트로 현재는 많은 프로젝트에서 Parquet를 지원하고 컬럼 기반 포맷의 업계 표준에 가깝습니다.Parquet를 적용해보니 Apache Spark에서는, 그리고 수많은 하둡 생태계의 프로젝트들에서는 Parquet를 잘 지원합니다.val data = spark.read.parquet("PATH") data.write.parquet("PATH") Spark에서는 이런 식으로 손쉽게 parquet 파일을 읽고, 쓸 수가 있습니다. 데이터를 분석하기 전에 원본이라고 할 수 있는 gzipped text json을 읽어서 Parquet 로 저장해두고 (gzipped json은 S3에서 glacier로 이동시켜버리고), 이후에는 Parquet에서 데이터를 읽어서 처리하는 것 만으로도 저장용량과 I/O 면에서 어느 정도의 이득을 얻을 수 있었습니다. 하지만 테스트 결과 저장용량에서의 이득이 gz 23 GB 에서 Parquet 18GB 로 1/3 정도의 저장용량을 기대했던 만큼의 개선이 이루어지지는 않았습니다.Parquet Deep Dive상황을 파악하기 위해 조금 더 조사를 해보기로 하였습니다. Parquet의 포맷 스팩은 Parquet 프로젝트에서 관리되고 있고, 이의 구체적인 구현체로 parquet-mr 이나 parquet-cpp 프로젝트 등에서 스펙을 구현하고 있습니다. 그리고 특별한 경우에는 Spark에서는 Spark 내부에 구현된 VectorizedParquetRecordReader 에서 Parquet 파일을 처리하기도 합니다.파일 포맷이 바뀌거나 기능이 추가되는 경우에는 쿼리엔진에서도 이를 잘 적용해주어야 합니다. 하지만 안타깝게도 Spark은 parquet-mr 1.10 버전이 나온 시점에도 1.8 버전의 오래된 버전의 parquet-mr 코드를 사용하고 있습니다. (아마 다음 릴리즈(2.4.0)에는 1.10 버전이 적용될 것으로 보이지만)Parquet 의 메인 개발자 중에는 Impala 프로젝트의 lead도 있기 때문에, Impala에는 비교적 빠르게 변경사항이 반영되는 것에 비하면 대조적입니다. 모든 프로젝트들이 실시간적으로 유기적으로 업데이트되는 것은 힘든 일이기 때문에 어느 정도는 받아들여야겠지만, 우리가 원하는 Parquet의 장점을 취하기 위해서는 여러 가지 옵션을 조정하거나 직접 수정을 해야 합니다.VCNC 데이터팀에서는 저장 용량과 I/O 성능을 최적화하기 위하여 Parquet의Dictionary encoding (String들을 압축할 때 dictionary를 만들어서 압축하는 방식, 길고 반복되는 String이 많다면 좋은 압축률을 기대할 수 있습니다)Column pruning (필요한 컬럼만을 읽어 들이는 기법)Predicate pushdown, row group skipping (predicate, 즉 필터를 데이터를 읽어 들인 후 적용하는 것이 아니라 저장소 레벨에서 적용하는 기법)과 같은 기능들을 사용하기를 원했고, 이를 위해 여러 조사를 진행하였습니다.저장용량 줄이기102GB의 JSON 포맷 로그를 text그대로 gzip으로 압축하면 23GB가 됩니다. dictionary encoding이 잘 적용되도록 적절한 옵션 설정을 통해 Parquet로 저장하면 6GB로, 기존 압축방식보다 저장 용량을 74%나 줄일 수 있었습니다.val ndjsonDF = spark.read.schema(_schema).json("s3a://ndjson-bucket/2018/04/05") ndjsonDF. sort("userId", "objectType", "action"). coalesce(20). write. options(Map( ("compression", "gzip"), ("parquet.enable.dictionary", "true"), ("parquet.block.size", s"${32 * 1024 * 1024}"), ("parquet.page.size", s"${2 * 1024 * 1024}"), ("parquet.dictionary.page.size", s"${8 * 1024 * 1024}"), )). parquet("s3a://parquet-bucket/2018/04/05") 비트윈의 로그 데이터는 ID가 노출되지 않도록 익명화하면서 8ptza2HqTs6ZSpvmcR7Jww 와 같이 길어지기에 이러한 항목들이 dictionary encoding을 통해 효과적으로 압축되리라 기대할 수 있었고, Parquet에서는 dictionary encoding이 기본이기에 압축률 개선에 상당히 기대하고 있었습니다.하지만 parquet-mr 의 구현에서는 dictionary의 크기가 어느 정도 커지면 그 순간부터 dictionary encoding을 쓰지 않고 plain encoding으로 fallback하게 되어 있었습니다. 비트윈에서 나온 로그들은 수많은 동시접속 사용자들의 ID 갯수가 많기 때문에 dictionary의 크기가 상당히 커지는 상태였고, 결국 dictionary encoding을 사용하지 못해 압축 효율이 좋지 못한 상태였습니다.이를 해결하기 위해, parquet.block.size를 default 값인 128MB에서 32MB로 줄이고 parquet.dictionary.page.size를 default 값 1MB에서 8MB 로 늘려서 ID가 dictionary encoding으로만 잘 저장될 수 있도록 만들었습니다.처리속도 올리기저장용량이 줄어든 것으로도 네트워크 I/O가 줄어들기 때문에 처리 속도가 상당히 올라갑니다. 하지만 컬럼 기반 저장소의 장점을 온전하게 활용하기 위해서 column pruning, predicate pushdown들이 제대로 작동하는지 점검하고 싶었습니다.소스코드를 확인하고 몇 가지 테스트를 해 본 결과, Spark에서는 Parquet의 top level field에서의 column pruning은 지원하지만 nested field들에 대해서는 column pruning을 지원하지 않았습니다. 비트윈 로그에서는 nested field들을 많이 사용하고 있었기에 약간 아쉬웠으나 top level field에서의 column pruning 만으로도 어느 정도 만족스러웠고 로그의 구조도 그대로 사용할 예정입니다.Predicate pushdown도 실행시간에 크게 영향을 줄 거라 예상했습니다. 그런데 Spark 2.2.1기준으로 column pruning의 경우와 비슷하게, top level field에 대해서만 predicate pushdown이 작동하는 것을 확인할 수 있었습니다. 이는 성능에 큰 영향을 미치기에, predicate 로 자주 사용하는 column들을 top level 로 끌어올려 저장하는 작업을 하게 되었습니다. 여기에 추가로 parquet.string.min-max-statistics 옵션을 손보고 나서야 드디어 10~30배 정도의 성능 향상을 볼 수 있었습니다.매일 15분 정도 걸리던 "의심스러운 로그인 사용자" 탐지 쿼리가 30여초만에 끝나고, cs처리를 위해 한 사람의 로그만 볼 때 5분 정도 걸리던 쿼리가 30여초만에 처리되게 되었습니다.못다 한 이야기parquet.string.min-max-statistics 옵션과 row group skipping에 관해.Parquet 같은 포맷 입장에서 string 혹은 binary 필드의 순서를 판단하기는 어렵습니다. 예를 들어 글자 á 와 e 가 있을 때 어느 쪽이 더 작다고 할까요? 사전 편찬자라면 á가 더 작다고 볼 것이고, byte 표현을 보면 á는 162이고 e는 101이라 e가 더 작습니다. Parquet 같은 저장 포맷 입장에서는 binary 필드가 있다는 사실만 알고 있고, 그 필드에 무엇이 저장될지, 예를 들어 á와 e가 저장되는지, 이미지의 blob가 저장되는지는 알 수 없습니다. 그러니 순서를 어떻게 정해야 할지는 더더구나 알 수 없습니다.그래서 Parquet 내부적으로 컬럼의 min-max 값을 저장해 둘 때, 1.x 버전에서는 임의로 byte sequence를 UNSINGED 숫자로 해석해 그 컬럼의 min-max 값을 정해 저장했습니다. 이후에 이를 개선하기 위해 Ryan Blue가 PARQUET-686에서 parquet-format에 SORT_ORDER를 저장할 수 있도록 했습니다.여기에서 문제는 이전 버전과의 호환성입니다. SORT_ORDER가 없던 시절의 Parquet 파일을 읽으려 할 때, min-max 값을 사용해 row group skipping이 일어나면 query 엔진에서 올바르지 않은 결과가 나올 수 있으니, binary 필드의 min-max 값을 parquet-mr 에서 아예 반환하지 않게 되어있습니다.하지만 이는 우리가 원하는 동작이 아닙니다. 여기에 parquet.string.min-max-statistics option을 true로 설정하면, 이전처럼 binary필드의 min-max값을 리턴하게 되고 rowgroup skipping이 작동하여 쿼리 성능을 크게 올릴 수 있습니다.마치며Spark과 Parquet 모두 많은 사람이 사랑하는 훌륭한 오픈소스 프로젝트입니다. 또한 별다른 설정이나 튜닝 없이 기본 설정만으로도 잘 돌아가는 편이기 때문에 더더욱 많은 사람이 애용하는 프로젝트이기도 합니다.하지만 오픈소스는 완전하지 않습니다. 좋은 엔지니어링 팀이라면 단지 남들이 많이 쓰는 오픈소스 프로젝트들을 조합해서 사용하는 것에서 그치지 않고 핵심 원리와 내부 구조를 연구해가며 올바르게 활용해야 한다고 생각합니다. 기술의 올바른 활용을 위해 비트윈 데이터팀은 오늘도 노력하고 있습니다.
조회수 3045

14. 어니스트펀드 로고 제작 스토리

얼마 전 ‘어.바.시’에서 어니스트 팀에게 어니스트펀드 로고 제작 스토리와 디자이너의 프로젝트 진행과정에 대해서 들려드렸다. 디자인에 관심이 많은 몇몇 팀원에게는 어니스트펀드 로고의 의미에 대해서 알려드린 적이 있지만, 팀 전체에게 설명할 기회가 없었다. 그래서 이번 어.바.시를 통해 로고의 정확한 의미를 설명하고 우리의 본질이 무엇인지 한 번 더 되짚어보는 시간을 가졌다.‘어.바.시’는 한국형 TED라고 불리는 ‘세.바.시(세상을 바꾸는 시간)’에서 따온 이름이다. 하지만 나는 어.바.시를 ‘어니스트하게 바뀌는 시간’으로 새롭게 풀어내고 나의 생각을 정직하게 이야기할 수 있는 자리로 만들었다. 어.바.시에 대한 설명을 간단하게 덧붙이자면 ‘개인과 팀의 성장을 위해 개인이 알고 있는 지식을 공유하는 자리’로, 짧게는 매주에 한 번 씩 팀원들이 돌아가며 어.바.시를 빛내주고 있다.<어니스트펀드의 가장 핫한 공간 agora에서 열린 어.바.시>어니스트펀드가 세상에 나타나기 전, 우리는 무수히 많은 고민을 하고 또 했다.“우리가 만들고 싶은 브랜드는 어떤 모습일까?”“우리가 가장 잘할 수 있는 본질에 집중하자!”그렇게 브랜드의 본질이 되는 ‘정직’이라는 키워드가 수면 위로 올라왔고, 우리는 ‘어니스트펀드’라는 본질에 집중하지 않을래야 않을 수 없는 브랜드 네이밍으로 출발선 너머에 발을 내딛었다.내딛은 첫 발이 브랜드 네이밍이라면 다음으로 내딛을 발은 브랜드의 얼굴인 로고 디자인일 터.<루피가 염원하는 프라다(좌)와 우버(우)의 이미지>“프라다같이 고급스럽고 세련된 느낌이면서 동시에 우버처럼 모던하고 간결한 이미지가 좋겠다!” 루피(서상훈 대표 영어 이름)는 정직(Honest)이라는 키워드 하나만으로 로고 디자인이 어려울까 봐 어마어마한 도움(?)말을 건네 왔다.바를 정(正)자를 고급스럽고 세련되게 획을 그어 모던하고 간결한 이미지의 로고를 디자인하려다가 참았다.난감했다. 정직이라는 추상의 끝판 키워드와 어니스트펀드(HonestFund)의 짧지 않은 브랜드명을 어떻게 하면 잘 풀어낼 수 있을까…1. 활용도를 고려하다멋진 로고를 기대하며 나를 바라보던 팀원들의 눈빛이 아직도 선하다. 그 눈빛들을 저버릴 수 없기에 나는 고민하고 또 고민했다.우선 어니스트펀드라는 브랜드가 어떻게 운영될지 생각해보았다. P2P금융의 핵심은 ‘온라인 플랫폼’이다. 거품 없는 효율적인 금융시스템을 만들기 위해서는 온라인 플랫폼이 정답이다. 그렇다면 고객들과의 소통이 대부분 온라인상에서 이뤄질 텐데, 각기 다른 온라인 환경(ex. 브런치, 페이스북, 자사 웹사이트 등)에 적용할 수 있는 로고가 필요하겠다는 생각이 들었다.그리고 어니스트펀드는  두세 글자의 간편한 브랜드명이 아니기에 한눈에 어니스트펀드를 전달할 수 있는 상징적인 무언가가 필요했다. 그래서 시의적절하게 사용 가능함과 동시에 상징적인 의미를 담을 수 있는 심볼을 가진 시그니처 타입(Signature Type)을 선택했다.<심볼과 로고타입, 시그니처 타입에 대한 설명>2. 심볼(Symbol)에 의미를 담다HonestFund의 약자인 HF를 활용해 심볼을 디자인했다. 앞서 말했던 것처럼 우리의 본질이 너무 추상적이어서 이미지화시키기 쉽지 않을뿐더러, 비교적 긴 브랜드명(대부분의 게임에서도 여섯 글자까지 ID길이를 제한하지 않던가…!)이기에 줄여서 표현할 수 있는 약자가 필요했다. HF도 H와 F의 대소문자 조합을 어떻게 하느냐에 따라 각기 다른 스토리로 심볼을 풀어낼 수 있다.<핀터레스트에 HF logo를 검색했을 뿐인데… 이렇게 다양한 로고가 존재하다니…!>HF, hf, Hf 정도로 추려내고 어떤 스토리를 심볼에 담을까 고민했다. 우리가 하고 싶은 이야기는 무궁무진했지만 간결하고 직관적인 스토리로 압축해야 했다. 가령 우리가 무슨 일을 하려는 지 1분 내의 짧고 굵은 설명이 필요한 것처럼 말이다. 새로운 중・저금리 시장, 대출자와 투자자의 효과적인 연결, 혁신적인 기술 바탕의 플랫폼… 이것들을 정직이라는 본질로 이뤄내는 것이 우리 스토리 핵심이다.2-1. 크리에이티브한 여정HF, hf, Hf를 그리고 또 그렸다. 스케치를 보자니 이것들이 알파벳인지 외계어인지 알아볼 수 없을 정도로 춤을 추고 있었다.필자는 극심한 창조활동을 하거나 철야를 하면 방언이 터지는 버릇이 있다. 이 상황을 즐기지 않으면 답이 없다는 것을 본능적으로 느끼는 것인지, 체력적으로는 고되지만 이 순간을 즐기려고 노력하는 아우성인 듯하다. 마치 어린아이들에게 그림을 그리며 설명하듯 hf를 그리며 우리의 브랜드 스토리를 중얼거렸다.< 아 지난 날 의식의 흐름이여… Adios…!>그러다가 거짓말처럼 뇌리에 스쳐 지나간 스케치가 있었는데 그게 바로 지금 로고의 초안이 되리라고 상상이나 했겠는가…2-2. Story Visualizationhf에 새로운 중저금리 시장에 혁신적인 기술을 바탕으로 대출자와 투자자를 효과적으로 연결해준다는 스토리가 담긴다면 어떨 것 같은가? 저 가늘고 작은 소문자 2개에 묵직한 스토리를 담아낸 다는 자체가 과해 보일 수 있지만, 너무나 완벽하고 깔끔하게 맞아떨어졌다. 중얼거리며 한 낙서(?)가 로고의 결정적인 키 비주얼(Key visual)을 안겨준 셈이다.2-3. hf심볼에 담긴 의미h의 세로획은 대출시장 범위를 상징한다. 위로 올라갈수록 고금리 시장을 의미하며 밑으로 내려올수록 저금리 시장을 의미한다. 어니스트펀드는 대출시장의 거품을 제거하여 중・저금리시장을 선도하기 위한 서비스이기에 비교적 밑에 포지셔닝된다.정직한 중・저금리시장을 만들기 위해서는 우량 대출자 모집부터가 시작이다. 대출자를 의미하는 ‘하단의 점’은 곧 어니스트펀드 서비스의 출발점을 의미한다. ‘상단의 점’은 투자자를 의미하는데, 하단의 점이 상단의 점까지 이어지면서 ‘대출자에게는 더 낮은 금리(Low point)를, 투자자에게는 더 높은 수익률(High point)을 제공’한다는 스토리를 그려낸다.f의 가로획은 대출자와 투자자가 연결되는 스토리 위에 위치하는데 이것이 바로 ‘어니스트펀드 플랫폼’을 의미한다.대출자와 투자자를 연결하고 금리의 거품을 제거하기 위해서는 비즈니스 모델이 필요하다. 어니스트펀드는 혁신적인 기술력을 바탕으로 금융시스템을 변화시켜 나갈 금융 플랫폼이다.3. 보도니(Bodoni)로 쓴 HonestFund< 보도니 서체는 ‘보그(VOGUE)’ 잡지 타이틀 서체로도 유명하다. >보도니 서체는 학부시절 디자인사 수업시간에 처음 알게 됐다. 보도니는 대표적인 모던 스타일 서체로 ‘가로 획과 세로획의 대비가 뚜렷한 매력적인 서체’라는 정도만 기억하고 있었다. (‘보도니’는 영어 서체의 이름이다. 흔히 알고 있는 맑은고딕, 나눔고딕처럼 ‘맑은’, ‘나눔’과 같은 서체의 이름이다.)< 수직선과 수평선의 조화가 아름다운 보도니로 쓴 어니스트펀드 >HonestFund 로고타입(위 그림에서 설명했듯이 로고 구성에 있어서 hf가 심볼이라 한다면, HonestFund는 로고타입이라고 한다.)에 쓰일 서체는 어떤 것이 좋을까 고민하다가, 수평・수직의 조화가 어니스트펀드의 ‘정직함’을 함의할 수 있지 않을까 하여 보도니 서체에 대해 조금 더 자세히 알아보았다.3-1. 보도니와 디돈양식(Didone Style)보도니 서체에 대해 설명할 때 크게 강조되는 부분이 두 가지가 있다. 하나는 앞에서 간단하게 언급한 ‘모던스타일’, 그리고 다른 하나는 ‘디돈양식’이다. 모던스타일(Modern Style)은 가로・세로 획의 선 굵기가 강한 대비를 이루고, 기존의 펜글씨와 명확한 차이점을 나타내는 현대적 서체 양식을 말한다.< 보도니와 흡사한 디도(Didot) 서체(좌), 그리고 활자를 찍어내는 모습(우) >디돈양식(Didone Style)은 대표 서체인 디도(Didot)와 보도니를 결합한 이름이다. 18세기 수학적 형태와 비례미를 반영하여 완성한 ‘킹스로만체(King’s Roman)’를 기본으로 삼은 양식으로, 기존의 서체들보다 기하학적이고 수학적 원리를 훨씬 더 끌어낸 서체가 바로 보도니다. 이것이 가능했던 이유는 질 좋은 종이 개발과 활자 조각기의 정교화, 인쇄용 잉크의 고급화 등 인쇄술 발달로 얻어낸 결과이기 때문이다.3-2. 보도니가 가진 역사와 정신을 취하다< 글자 획 끝의 유무를 가지고 산 세리프체(위)와 세리프체(아래)를 구분한다. >보도니 서체를 알아가면서 우리가 추구하는 브랜드 방향성과 맞닿아있는 부분이 많다는 것을 느낄 수 있었다.세리프체(명조체)의 클래식한 아름다움과 산세리프체(고딕체)의 군더더기 없는 깔끔함이 동시에 느껴지는 서체로 유명한 보도니는 기존 금융권들이 가지고 있는 묵직함과 핀테크 기업의 심플한 매력을 동시에 담기에 충분한 서체다. 수학적 원리로 그려지는 보도니가 보여주는 정교한 이미지 또한 우리가 취해야 하는 정직과 맞닿아 있었다.< 수학적 원리를 바탕으로 그려지는 보도니 서체와 나란히 쓰일 수 있도록 심볼 디자인 역시 논리적으로 그려내기 위해 좋은 비율을 철저하게 구상했다. >보도니는 발전시킨 인쇄술로 타이포그래피 역사의 큰 획을 그었고, 어니스트펀드는 혁신적인 기술력을 바탕으로 금융시스템을 변화시킬 것이다. 어니스트펀드도 보도니처럼 혁신적인 기술로 금융계에 큰 획을 그을 것이다.4. 맺으며어니스트펀드는 혁신적인 플랫폼을 지향하기에 급변하는 ‘대 온라인시대’의 물살을 잘 타야 한다. 구글이 아이덴티티를 리뉴얼하여 발전시키 듯, 우리도 시의적절하게 브랜드를 조금씩 발전시켜 나갈 것이다. 훗날 로고의 형태에서는 초심으로 머릿속에 그려낸 어니스트펀드의 큰 그림이 보이지 않을지도 모르겠다.브랜드를 반으로 나눈다면  ‘변해도 되는 것’과 ‘변하지 말아야 할 것’으로 나눌 수 있다. 로고 디자인은 그 시대 사정에 맞게 변할 순 있지만, 우리가 만들고자 했던 새로운 중・저금리 시장, 그것을 혁신적인 기술로 선도하는 모습, 그리고 누구나 정직하고 싶지만 아무나 정직할 수 없다는 것을 알기에  우리가 직접 만들고자 한 ‘정직한 금융’은 변하지 말아야 할 모습이다.금융과 IT를 결합하여 기존의 대출·투자 경험을 혁신하는 P2P금융 스타트업, 어니스트펀드의 이야기가 연재될 팀 브런치에 많은 관심 부탁드립니다.어니스트펀드에 대해 더 알고 싶으시다면, 어니스트펀드 홈페이지를 방문해보세요.#어니스트펀드 #디자인 #로고 #로고디자인 #인사이트 #디자이너 #CI #CI제작 #성장 #후기
조회수 965

나는 부족한 사람입니다 -2

창업자 인터뷰 – 옐로모바일의 시작첫 번째 이야기의 열기를 이어 계속해서 이상혁 대표의 ‘부족한’ 창업기를 전해볼까 합니다. 이 번엔 기필코 2차 연장 없이 옐로모바일 창업까지 도달할 수 있도록 해보겠습니다 :)바쁜 여러분을 위한 Y의 다섯 문장 요약!!1. 성장의 기회를 모색하기 위해 다음에 신사업 제휴를 제안했다가 역으로 받은 인수 제안!2. 고심 끝에 내린 매각 결정, 그러나 인수 과정보다 힘들었던 것은 인수 이후의 사건들, 그로 인해 홀로 보낸 눈물의 밤3. 상심의 끝에서 마주한 것은 대표로서의 책임의 막중함과 스스로의 부족함에 대한 인지4. 무너진 자신감을 회복하고, 인수 당시의 약속을 이행하고자 다음에서 새로운 사업을 위해 노력했으나 큰 조직의 시스템이라는 벽에 부딪혀5. 결국 스스로가 창업가임을 인지하고, 그 간의 실패와 교훈을 바탕으로 새로운 사업에 도전하기로 결심디엠에스랩의 창업기, 그리고 극적인 피벗과 흑자전환까지의 이야기를 나눠보았습니다. 이제 회사를 더욱 성장시키는 일만 남은 것 같은데요?그게 큰 고민이었어요. 이 사업으로 충분히 의미 있는 성장을 이뤄낼 수 있을까? 아니면 신규 사업을 통해 새로운 성장 동력을 찾아야 하는 것일까? 창업을 한 많은 사람들의 꿈 중 하나가 상장일 텐데요, 저 또한 그 당시 상장을 꿈꾸며 성장에 대해 고민했었어요. 하지만 현재의 사업만으로는 충분한 성장이 어렵다고 판단했고, 신규 사업이 필요하단 결론을 내렸죠.첫 번째 피벗을 통해 신규 사업의 어려움을 충분히 느끼셨을 텐데 또 새로운 사업을요?말씀 드렸듯이, 창업가는 성공을 해야만 해요. 그래야 함께 한 직원들과 그 열매를 나눌 수 있으니까요. 그렇기에 더 큰 도전에 대한 두려움은 별로 없었어요. 제겐 그 도전이 의무라고 생각했죠. 당시엔 인터넷 포탈에 수 많은 사용자 트래픽 (Traffic)이 있었기 때문에, 새로운 비즈니스를 포탈과 함께하여 시너지를 낼 수 있다면 윈윈 (win-win) 할 수 있을 것 같다고 판단했고, 다음 (Daum)에 제안을 하기에 이르렀죠. 그 때 돌아온 답이 예상 밖에도 인수 제안이었어요.처음부터 회사 매각을 생각했던 것은 아니었군요?그 당시엔 M&A라는 것 자체가 제겐 생소한 개념이었어요. 한국 IT업계에 M&A가 그리 많지도 않았고요. 하지만 막상 고민을 해보니 좋은 기회라는 생각이 들었어요. 다음이라는 거대 플랫폼에서 미국의 옐프 (Yelp)와 같은 로컬 비즈니스를 해 볼 수 있겠다는 생각이 들었죠. 직원들의 의견은 둘로 갈렸어요. 함께해서 더 큰 회사가 될 수 있다라는 생각과, 우리끼리 더 큰 회사를 만들어보자는 생각.매각으로 최종 방향을 정한 이유는 무엇이었나요?사업의 성장, 그리고 임직원에 대한 보상을 위해서 옳은 선택이라고 생각했어요. 다음이라는 플랫폼과 함께할 수 있다는 것이 큰 매력이었고, 임직원들에게 나누어 주었던 주식으로 조금이나마 돈을 벌게 해 주면 좋겠다는 생각도 있었죠. 더 나아가서는 마이원카드의 명함보단 누구나 아는 다음의 명함이 우리 처녀 총각 직원들 결혼에도 더 도움이 되지 않을까 라는 생각까지 했으니까요. 그 동안 우리를 믿고 투자해 준 투자자들에게도 보상을 해주고 싶었고요.직원들 결혼까지 생각하셨다니, 뭔가 짠하네요… 인수 과정은 어땠나요?인수계약 체결 다음 날 다음 PMI (Post Merger Integration) 팀이 찾아와 이후의 일을 설명해 주었어요. 모든 인수합병 이후 거쳐야 하는 당연한 과정들이었지만, 아직도 잊지 못할 날들이었죠. 법인 인감과 통장, OTP 카드를 가져가고, 임직원 한 명 한 명을 인터뷰 해서 다음에 합류할 직원과 그렇지 않을 직원들이 나누어졌어요. 큰 충격이었죠. 이제 내가 의사결정자가 아니라는 현실을 직면했어요. 그렇게 일부 직원들과 다음으로 첫 출근을 한 날도 기억나요. 쭈뼛대며 자리를 잡고 어색하게 인사를 건네는데 마치 남의 회사에 온 느낌이었어요.적응하는 과정이 쉽지 않았군요.다음이라는 큰 조직에 적응하는 것이 생각보다 오래 걸렸던 것 같아요. 계속해서 이어지는 회의들에 치여 업무 시간을 확보하는데도 애를 먹었고요. 새로운 사업을 진행하기 위해 의사결정을 받는 것도 쉽지 않았고, 의사결정을 받아도 일의 추진 속도가 제 예상보다 느리다 보니 어느덧 의욕과 열정이 식어가고 제 스스로가 평범한 직장인이 되어가는 느낌이 들었어요. 그런 와중에 엄청나게 충격적인 일련의 사건들이 있었어요. 제가 다음에 회사를 팔아 큰 돈을 벌어 건물을 샀다는 소문이 났고, 같이 일했던 팀장이 저를 찾아와 사실 여부를 물으며 본인은 얻은 것이 별로 없는데 서운하다는 말을 전했어요. 또 한 번은 다음에 적응을 잘 하지 못한 팀장이 술자리에서 왜 다음에 인수된 것인지 모르겠다며, 다시 옛날로 돌아가고 싶다고 하소연을 하기도 했고요. 엄청나게 부끄러운 얘기지만, 그날 밤 집에 와서 한참을 울었어요. 13년 동안의 첫 번째 사업이 송두리째 부정되는 것만 같았죠. 임직원들이 액면가에 주식을 가질 수 있도록 최선을 다했던 노력들, 인수 과정에서 최대한 투자자, 임직원, 경영진과 공평하게 보상을 나누고자 했던 고민들이 스쳐 지나가면서요. 이런 이야기를 대중에 공개하는 것은 처음인데요, 저는 남들이 생각 하는 것처럼 회사의 다음 인수를 통해 어마어마한 돈을 벌지는 못 했어요. 창업을 해서 사업을 하는 동안 아버님께서 돌아가신 후 제가 집안의 실질적 가장 이었기에, 그 돈으로 어머님의 노후자금을 마련하고 두 동생의 가정 살림에 보태준 뒤, 저는 여전히 월세를 살고 있었는데 건물을 샀다니…상심이 적지 않으셨겠네요… 얘기해 놓고 나니 괜한 소리를 한 것 같네요. 그래도 모든 것이 제 책임이라고 생각했고, 최대한 상황을 개선시켜보려고 노력했어요. 다음에 인수 당시 주식을 매각하지 않았던 투자자들이 ‘인수 당시에 왜 매각을 권유하지 않았냐’며 제게 물어내라고 찾아왔을 때는 가족을 위해 쓰고 남은 돈으로 일부 투자자들의 주식을 되사 주기도 했어요.너무 혼자 책임을 떠안으실 필요는 없을 것 같다는 것이 개인적인 생각이지만, 역시나 대표의 어깨에 올려진 부담의 무게는 만만치 않다는 것을 새삼 깨닫게 되네요. 인수 후 다음에서 로컬비즈니스 본부장으로 1년 반여를 재직 하셨는데, 다른 에피소드는 없었나요?다음이 1등하는 서비스를 만드는 것이 목표 였는데, 결국 그 약속을 지키지 못한 것이 큰 줄거리인 것 같고요.. 에피소드라… 새로운 사업을 진행하는 과정에서 징계를 받은 것?징계요? 생각해보니 일전에 한 신문 기사에서 어렴풋이 보았던 기억이 있습니다만…당시 새로운 로컬 광고 상품을 판매할 대행사 조직을 꾸리던 중이었는데, 대행사를 찾기가 쉽지 않았어요. 당시 포탈의 광고상품은 주로 콜센터를 통해 전화로 판매하는 방식이었고, 영업사원들이 직접 매장을 방문해서 상품을 판매하는 경우가 없었거든요. 그래서인지 전국 주요 대도시에 로컬 광고상품을 판매할 영업 대행사 조직을 꾸리는 일이 여간 어려운 일이 아니었어요. 심지어 초기엔 상당기간 대행사가 적자를 감수해야 했거든요. 여러 업체를 만나서 설득을 반복했고, 인맥을 총동원해 지인들에게 영업 대행사가 되어 달라고 설득했었어요. 그런 과정에서 친동생에게도 대행사를 해보라고 권유했었고, 상당히 큰 적자를 보면서 운영하던 중에 임원이 회사 허가 없이 친인척과 사업계약을 했다는 이유로 징계를 받았죠.억울하기도 하셨겠지만 회사 입장에선 당연한 조치였던 것 같은데요?맞아요. 다음이라는 큰 조직에서 질서를 유지하기 위해서는 이런 원칙을 만들고 지키는 것이 필수겠구나 라는 생각에, 임원으로서 회사의 규칙을 잘 알지 못하고 오해의 소지를 만들었다는 것에 대해 반성했죠. 하지만 개인적으로는 정말 억울했고, 이 일을 계기로 다시 창업을 해야겠다는 생각을 굳히게 되었어요.1년 반 만에요? 징계가 많이 억울하셨나 봐요?하하하 징계 때문에 그런 것은 아니고요. 실은 그 전부터 조금씩 다시 창업을 해야겠다는 생각을 해 왔어요. 오랜 기간 사업을 해 온 제게, 직장 생활이라는 것이 맞지 않는구나 라는 생각도 들었고, 새로운 것을 시도해보고 싶어도 큰 조직에서는 제게 책임도 적지만 자유와 권한도 적기에 한계가 명확하다는 것도 체감하고 있었거든요. 대한민국 수많은 국민의 트래픽, 훌륭한 개발자들, 풍부한 자금… 이런 더할 나위 없이 좋은 환경이 도전과 혁신의 필요조건은 아니라는 결론이었어요. 제가 대학을 졸업한 후 배우고 깨달았단 수많은 것들, 첫 번째 사업을 통해 겪었던 온갖 시행착오들, 다음에서 경험했던 다양한 일들, 거기에 나이를 먹어가며 조금은 더 알게 된 사업, 그리고 인간의 본질… 이러한 것들을 잘 버무리면 조금은 더 나은 사업을 시작할 수 있겠단 자신이 있었죠.그렇게 해서 새롭게 창업한 회사가 옐로모바일이군요. 네, 맞아요. 그런데 시간이 다 되었으니 이 얘기는 다음 번에 더 나누는 것이 어떨까요?정말 ‘미친’ 타이밍이네요…ㅋ 알겠습니다. 아쉽지만 옐로모바일의 창업기는 다음 인터뷰 때 더 자세히 여쭤보도록 할게요. 긴 시간 고생 많으셨습니다!네, 기자님도요. 인터뷰 글 잘 부탁 드려요 2시간여가 순식간에 흘러갔고, 여기까지가 이상혁 대표가 옐로모바일을 창업하게 된 계기, 그리고 그 때까지 겪어온 말 그대로 ‘우여곡절’에 대한 이야기입니다. 누군가에게는 성공적으로 창업을 해서 매각이라는 ‘엑싯 (Exit)’을 이뤄낸 성공담으로 들릴 수도 있겠지만, 제게는 인생의 큰 흐름을 따라 표류하며 좌충우돌했던 한 부족한 창업가의 반성문으로 보였습니다. 그렇기에 그가 이 시기의 배움을 옐로모바일의 창업에 어떻게 접목했을지, 그래서 옐로모바일은 정말 더 나은 회사로, 지속 가능한 사업으로 성장할 수 있을 지가 더욱 궁금해졌습니다. 그 궁금증을 함께 풀어볼 수 있기를 바라면서, 저는 다음 이야기로 찾아 뵙겠습니다. Y였습니다.
조회수 4381

Flask로 만들어 보는 WSGI 어플리케이션

안녕하세요. 스포카 크리에이터팀 문성원입니다. 오늘은 WSGI(Web Server Gateway Interface)어플리케이션을 직접 작성해보고, 또 이런 작성을 보다 쉽게 도와주는 프레임워크 중 하나인 Flask에 대해서 알아보겠습니다.WSGIWSGI에 대해 기억이 가물하신 분들을 위해 지난 글의 일부를 잠깐 다시 살펴보죠.이 경우 uwsgi는 일종의 어플리케이션 컨테이너(Application Container)로 동작하게 됩니다. 적재한 어플리케이션을 실행만 시켜주는 역할이죠. 이러한 uwsgi에 적재할 어플리케이션(스포카 서버)에는 일종의 규격이 존재하는데, 이걸 WSGI라고 합니다.(정확히는 WSGI에 의해 정의된 어플리케이션을 돌릴 수 있게 설계된 컨테이너가 uwsgi라고 봐야겠지만요.) WSGI는 Python 표준(PEP-333)으로 HTTP를 통해 요청을 받아 응답하는 어플리케이션에 대한 명세로 이러한 명세를 만족시키는 클래스나 함수, (__call__을 통해 부를 수 있는)객체를 WSGI 어플리케이션이라고 합니다.글로는 감이 잘 안오신다구요? 그럼 코드를 보면서 같이 살펴봅시다. (모든 코드는 Python 2.7에서 테스트 되었습니다.)Hello World!def app(environ, start_response):    response_body = 'Hello World!'    status = '200 OK'    response_headers = [('Content-Type', 'text/plain'),                         ('Content-Length', str(len(response_body)))]    start_response(status, response_headers)        return [response_body]view rawgistfile1.py hosted with ❤ by GitHubapp은 일반적인 Python 함수지만, 동시에 WSGI 어플리케이션이기도 합니다. environ과 start_response를 받는 함수기 때문이죠.(PEP-333) 사실은 꼭 함수일 필요도 없습니다. 다음은 위의 app과 동일한 동작을 하는 WSGI 어플리케이션입니다.class App(object):    def __init__(self, environ, start_response):        self.environ = environ        self.start_response = start_response    def __iter__(self):        status = '200 OK'        response_body = "Hello World!"        response_headers = [('Content-Type', 'text/plain'),                            ('Content-Length', str(len(response_body)))]        self.start_response(status, response_headers)        yield response_bodyview rawgistfile1.py hosted with ❤ by GitHubApp는 Python 클래스(Class)로 environ과 start_response를 멤버 변수로 가지는데, 여기에는 약간의 트릭이 있습니다. 생성자(Constructor)인 App를 함수처럼 사용하게 하여 리턴되는 결과(실제로는 생성자를 통해 생성된 객체겠죠.)가 \_\_iter\_\_를 구현한 순회 가능한(Iterable) 값이 되게 하는 것이죠. (덤으로 이 객체는 발생자(Generator)를 돌려주게 됩니다.)그럼 이제 이 코드들을 실행하려면 어떻게 해야할까요? 그러려면 먼저 WSGI 규격에 맞게 어플리케이션을 실행시켜 줄 서버를 작성해야합니다. 하지만 다행히도 Python 2.5부터 제공되는 wsgiref.simple_server를 이용하면 간단히 테스트 해 볼 수 있습니다.(서버를 직접 작성하는 부분에 대해선 나중에 다루도록 하겠습니다.)from wsgiref.simple_server import make_serverhttpd = make_server('', 8000, app)httpd.serve_forever()view rawgistfile1.py hosted with ❤ by GitHub위 코드를 실행시킨 후에(당연히 app이나 App도 만들어져 있어야겠죠?) 웹 브라우져를 통해 localhost:8000으로 접속하면 작성한 어플리케이션의 동작을 확인할 수 있습니다.environ과 start_response테스트도 해봤으니 코드를 조금만 더 자세히 살펴봅시다. 함수 버젼의 app이나 클래스 버젼의 App모두 공통적으로 environ과 start\_response를 인자로 받아 요청을 처리하는 것을 확인할 수 있습니다. (당연한 이야기겠지만, 반드시 이름이 environ이나 start\_response일 필요는 없습니다만 편의상 이후 계속 environ과 start_response로 표기하겠습니다.)하나씩 살펴보자면, environ은 Python 딕셔너리(dictionary)로 HTTP 요청을 처리하는데 필요한 정보가 저장되어있습니다. HTTP 요청에 대한 정보는 물론, 운영체제(OS)나 WSGI 서버의 설정 등도 정의되어있지요. 다음 코드는 이러한 environ의 내용을 응답으로 주게끔 수정한 WSGI어플리케이션입니다.def dump_environ_app(environ, start_response):    response_body = "\n".join(["{0}: {1}".format(k, environ[k]) for k in environ.keys()])    status = '200 OK'    response_headers = [('Content-Type', 'text/plain'),                         ('Content-Length', str(len(response_body)))]    start_response(status, response_headers)        return [response_body]view rawgistfile1.py hosted with ❤ by GitHublocalhost:8000에 각종 쿼리 스트링(Query String)을 붙이거나, 브라우져를 바꿔가면서 확인해보면 출력되는 값이 바뀌는 것을 확인하실 수 있습니다.그럼 이제 start\_response를 한번 볼까요. start\_response는 일종의 콜백(Callback)으로 인터페이스는 다음과 같습니다. start_response(status, response_headers, exc_info=None) 실제 서버에서 어플리케이션으로부터 응답(Response)의 상태(Status)와 헤더(Header), 그리고 예외(Exception)의 유무를 확인받아 실행하게 되는데, status와 response_headers는 HTTP 응답 명세에 근거하여 작성하게 됩니다.Middleware지금까지 우리는 어떻게 WSGI 어플리케이션을 작성하는지에 대해 살펴봤습니다. 요청을 받아 처리하는 HTTP의 기본 기능에 충실한 어플리케이션이었죠. 그런데 일반적으로 우리가 작성하는 웹 어플리케이션에서 주로 다루게 되는 쿠키(Cookie), 세션(Session)에 대해서는 어떻게 처리해야 좋을까요? WSGI 명세에는 이러한 내용을 직접적으로 다루고 있지 않습니다. WSGI 자체는 서버나 프레임워크 자체가 아니라 서버가 어플리케이션과 통신하는 명세를 다루고 있기 때문이죠. 따라서 이러한 기능은 작성자가 직접 이를 구현해야 합니다. 그런데 이런 구현을 어플리케이션을 작성할때마다 하는건 너무 번거로운 일입니다. 그것보다는 이미 작성한 어플리케이션을 확장하는 것이 간단하겠지요. 이러한 확장을 위해 필요한 것이 WSGI 미들웨어(Middleware)입니다.미들웨어는 어플리케이션을 처리하기 전후의 처리나 environ의 추가등을 통해 작성된 어플리케이션을 확장할 수 있습니다. 다음은 쿼리 스트링의 \_\_method\_\_에 따라 HTTP 메소드(Method)를 임의로 변경하는 처리를 도와주는 간단한 미들웨어입니다.from werkzeug import url_decodeclass MethodRewriteMiddleware(object):    """        app = MethodRewriteMiddleware(app)    """    def __init__(self, app, input_name = '__method__'):        self.app = app        self.input_name = input_name    def __call__(self, environ, start_response):        if self.input_name in environ.get('QUERY_STRING', ''):            args = url_decode(environ['QUERY_STRING'])            method = args.get(self.input_name)            if method:                method = method.encode('ascii', 'replace')                environ['REQUEST_METHOD'] = method        return self.app(environ, start_response)view rawgistfile1.py hosted with ❤ by GitHubMethodRewriteMiddleware는 \_\_call\_\_를 통해 app을 대체하게 됩니다. (데코레이터(Decorator)가 생각나셨다면 정확한 이해십니다.)Flask미들웨어를 통해 어플리케이션을 확장하는 방법까지 알아봤습니다. 그러나 이것만 가지고 웹 어플리케이션을 만들기에는 아직 귀찮은 부분이 많이 남아있습니다. 각종 파라미터를 처리하기 위해서는 environ를 일일히 뒤져야하며, 요청에 대한 응답으로 전달할 HTML도 일일히 문자열로 적어야하죠. 이런 여러가지 불편함을 해결하기 위해 알아볼 것이 WSGI 마이크로프레임워크를 자처하는 Flask입니다. Flask는 WSGI 라이브러리인 Werkzeug를 만들기도 한 Armin Ronacher가 만든 프레임워크로 “마이크로”라는 수식어에 어울리게 아주 핵심적인 부분만을 구현하고 있지만, 유연하게 확장이 가능하게 설계된 것이 특징입니다.다시 한번 Hello World!우선 Flask를 시스템에 설치해야하는데, pip가 설치되어있다면 pip install flask로 설치 가능합니다.(환경에 따라 루트(root)권한이 필요할 수도 있습니다.) easy_install의 경우도 마찬가지로 easy\_install flask로 설치 가능합니다.설치가 완료되었으면 다음과 같이 아주 간단한 어플리케이션을 작성해봅시다.from flask import Flaskapp = Flask(__name__)@app.route("/")def hello():    return "Hello World!"if __name__ == "__main__":    app.run()view rawgistfile1.py hosted with ❤ by GitHubFlask(정확히는 Werkzeug)는 테스트를 위해 간단한 WSGI 서버를 자체 내장하고 있기 때문에 app.run을 통해 어플리케이션을 직접 실행할 수 있습니다.Route이번에 작성한 Flask 어플리케이션에는 이전까지 보지 못하던 개념이 들어 있습니다. app.route가 바로 그것인데요. 이 메서드는 URL 규칙을 받아 해당하는 규칙의 URL로 요청이 들어온 경우 등록한 함수를 실행하게끔 설정합니다. 위의 Hello World! 예제 같은 경우엔 “/”가 해당되겠지요. 또한 이런 규칙을 URL로 부터 변수도 넘겨 받을 수 있습니다.# http://flask.pocoo.org/docs/api/#[email protected]('/')def index():    [email protected]('/')def show_user(username):    [email protected]('/post/')def show_post(post_id):    passview rawgistfile1.py hosted with ❤ by GitHub이렇게 URL을 통해 처리할 핸들러를 찾는 것을 일반적으로 URL 라우팅(Routing)이라고 합니다. 이런 URL 라우팅에서 중요한 기능 중 하나가 핸들러에서 해당하는 URL을 생성하는 기능인데, Flask는 이를 url_for 메서드를 통해 지원합니다[email protected]('/')def index():    return ""@app.route('/')def show_user(username):    return [email protected]('/post/')def show_post(post_id):    return str(post_id)from flask import [email protected]("/routes")def routes():    return "".join([            url_for("index"),            url_for("show_user", username="longfin"),            url_for("show_post", post_id=3)            ])view rawgistfile1.py hosted with ❤ by GitHub보다 자세한 사항은 API 문서를 참고하실 수 있습니다.Template여태까지 우리는 요청에 대한 응답으로 단순한 문자열을 사용했습니다. 하지만 일반적인 웹 어플리케이션의 응답은 대부분이 그것보다 훨씬 복잡하지요. 이를 보다 쉽게 작성할 수 있게끔 도와주는 것이 바로 Flask의 템플릿(Template)입니다. Flask는 기본 템플릿 엔진으로 (역시 Armin Ronacher가 작성한)Jinja2를 사용합니다.기본적으로 템플릿엔진은 별도의 규칙(여기서는 Jinja2)에 맞게 작성된 템플릿 파일을 읽어 환경(Context)에 맞게 적용한 결과물을 돌려주는데 이 과정을 Flask에서는 render_template()가 담당하고 있습니다. 다음 코드는 hello.html이라는 템플릿 파일을 읽어서 이름을 적용한 뒤에 돌려주는 코드입니다.# from http://flask.pocoo.org/docs/quickstart/#rendering-templatesfrom flask import [email protected]('/hello/')@app.route('/hello/')def hello(name=None):    return render_template('hello.html', name=name)view rawgistfile1.py hosted with ❤ by GitHub쉽게 작성할 수 있게 도와준다고 해도, 템플릿 역시 나름의 학습을 필요로 합니다. 자세한 사항은 Jinja2의 API 문서를 참고하시기 바랍니다.RequestHTTP 요청을 다루기 위해서 때로는 environ의 내용은 너무 원시적일때가 있습니다. HTML 폼(Form)으로부터 입력받는 값이 좋은 예인데요. Flask에서는 request라는 객체(역시 Werkzeug에서 가져다가쓰는 거지만요)를 통해 이를 보다 다루기 쉽게 해줍니다. 다음은 HTML 폼으로부터 입력받은 message라는 값을 뒤집어서 출력하는 코드입니다.from flask import [email protected]("/reverse")def reverse():    message = request.values["message"]    return "".join(reversed(message))view rawgistfile1.py hosted with ❤ by GitHubSession로그인등으로 대표되는 요청간의 상태를 유지해야하는 처리에 흔히 세션(Session)을 사용하실 겁니다. Flask에서는 session객체를 지원합니다.# from http://flask.pocoo.org/docs/quickstart/#sessionsfrom flask import Flask, session, redirect, url_for, escape, requestapp = Flask(__name__)@app.route('/')def index():    if 'username' in session:        return 'Logged in as %s' % escape(session['username'])    return 'You are not logged in'@app.route('/login', methods=['GET', 'POST'])def login():    if request.method == 'POST':        session['username'] = request.form['username']        return redirect(url_for('index'))    return '''        <form action="" method="post">           <input type=text name=username>           <input type=submit value=Login>        </form>    '''@app.route('/logout')def logout():    # remove the username from the session if its there    session.pop('username', None)    return redirect(url_for('index'))# set the secret key.  keep this really secret:app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'view rawgistfile1.py hosted with ❤ by GitHubFlask는 기본적으로 시큐어 쿠키(Secure Cookie)를 통해 세션을 구현하므로 길이에 제한이 있습니다. 때문에 파일이나 DB기반의 세션을 구현하려면 Beaker와 같은 프레임워크를 통한 확장이 필요합니다.(하지만 이 또한 매우 쉽습니다.)#스포카 #개발 #개발자 #개발팀 #인사이트 #기술스택 #꿀팁 #Flask
조회수 1836

[CJ CGV] 유연한 조직을 만들기 위한 역발상? 

지난 8월, 영화를 넘어 문화를 선도하기 위해 노력하는 CGV에서 믿을 수 없는 소식(?)이 들려왔다. ‘사원이 멘토, 경영진이 멘티’가 되는 ‘리버스 멘토링(Reverse Mentoring)’을 진행한다는 것. 잘못 본거라고 생각했다. 멘토와 멘티가 서로 바뀐 것이 아니냐고 묻고 싶을 정도였으니까.하지만 사원이라고 새로운 것을 가르쳐주는 멘토가 되지 말라는 법이 없고, 경영진이라고 배움의 자세를 갖춘 멘티가 되지 말라는 법도 없는 게 아닌가! 이 프로그램을 통해 서로의 역할을 바꿔 상호 교류하며 세대 간의 격차를 해소하고 유연한 조직을 만들 수 있다면 이것은 금.상.첨.화! ‘리버스 멘토링’의 기획자 CGV 인사담당 이현주 님과 멘토·멘티로 참여한 CGV 천안펜타포트 김빛나 님, CGV 전략지원담당 조성진 님의 이야기를 들어보니 성공적인 프로그램이었다는 생각이 들었다. 리버스 멘토링, CGV라서 가능했다?▲(왼쪽부터)리버스 멘토링 기획자 이현주 님, 멘토로 지원한 김빛나 님, 멘티로 참여한 조성진 님. ‘리버스 멘토링’은 선배가 후배를 지도하는 전통적인 멘토링과는 반대로, 선배가 후배에게서 새로운 지식과 트렌드를 배우는 비형식적인 학습 프로그램을 말한다. 전 세계 기업에서 실시하고 있는 리버스 멘토링. 이 좋은 걸 CGV가 하지 않으면 섭섭하지~ 올해 CGV가 리버스 멘토링을 시작한 발단은 무엇이었을까? 2030세대를 알아야 CJ CGV의 문화도, 고객도 잡을 수 있어요. - 이현주 님 이현주 님은 주요 고객층이자 미래를 이끌어갈 성장 동력인 2030세대를 이해함으로써 CJ CGV의 바람직한 소통 문화를 굳건히 하는 한편 사원급 구성원들과의 세대간 격차를 줄이기 위해 프로그램을 고심하다 '리버스 멘토링'을 기획하게 됐다고 말했다. 지금은 CJ CGV 대표이사이신 최병환 님께서 올해 초 "2030세대의 시각과 트렌드를 이해하고 그들이 가진 인사이트를 공유하는 기회의 장이 필요하다"고 했던 이야기도 '리버스 멘토링'을 발족하는 원동력이 되었단다. ▲리버스 멘토링의 원활한 진행을 위해 여기저기 뛰어다닌 이현주 님. 많은 기업이 시행하는 프로그램이지만 중요한 건 성공 여부다. 진행했다는 것에만 의의를 두는 곳이 많았다. 그만큼 프로그램의 안착이 쉽지 않다는 것. 그러나 CGV는 달랐다. 여타 기업과 달리 구성원들의 낮은 평균 연령과 새로운 것을 수용하는 것을 꺼리지 않는 경영진, 그리고 수평적 커뮤니케이션이 가능한 환경 등 정착될 수 있는 요건이 갖춰져 있었다. 일일이 경영진을 찾아가 프로그램 목적과 취지를 설명하고 공감을 얻어낸 인사 담당의 노력도 한몫했다.그 결과, 대표이사를 비롯한 15명의 경영진들이 적극적으로 참여했다. 40여 명의 사원들은 프로그램 참가 이유와 자신 있는 분야를 적은 신청서 제출해 참여 의지를 내보였다. 멘티로 참여한 전략지원담당 조성진 님은 현장에서 근무하는 멘토들의 젊은 생각을 배우고 싶어서, 멘토로 참여한 김빛나 님은 현장에서 쉽게 만날 수 없는 경영진과의 만남과 회사의 가치관, 사업 인사이트를 얻기 위해서 신청했다고 전했다. 나의 멘토, 나의 멘티는 어디에?▲리버스 멘토링 서약서에 서명한 최병환 대표와 멘토로 참여한 사원들. 리버스 멘토링의 효과를 높이기 위해 멘토들과 적극적으로 소통하며 사전 준비를 철저히 했다. 멘토들이 직접 작성한 리버스 멘토링 신청 이유와 자신 있는 분야에 대해 추가적인 인터뷰를 진행해 유사한 주제별로 ‘멘토 그룹핑’을 실시했다. 이후 나온 주요 주제를 취합해, 이를 경영진들에게 보여주며 선택하도록 했다. 단, 멘토의 이름을 철저히 가렸다.‘나의 멘토, 나의 멘티는 어디에?’라는 궁금증은 지난 8월 2일 CJ 인재원에서 열린 리버스 멘토링 ‘Start-Up’에서 풀렸다. 첫 만남이라 어색한 기류가 흐른 것도 잠시, 뒷자리에 앉아 있는 경영진들을 대상으로 ‘신조어 테스트 및 아이돌 맞추기 게임’이 이뤄졌다. ‘핵인싸’ ‘JMT’ 등의 문제가 출제되고 답변을 못하는 경영진들이 속출하자 사원들은 놀란 표정을 지었다고. 누군가에게는 당혹스러운 시간이었겠지만, 젊은 세대의 트렌드를 알아야 한다는 필요성을 오롯이 느끼는 과정이었다. 답변을 못한 경영진들에겐 신조어 스터디에 대한 숙제가 주어졌다.그리고 대망의 15개 조 멘토-멘티 매칭 결과가 공개됐다. 어색한 기류가 흘렀지만 이어진 ‘미니 드론 날리기’ 프로그램을 함께하며 친밀감을 높였다. 원활한 스킨십을 위해 시합도 펼쳐졌는데, 팀을 위해 이겨 보겠다며 이쪽저쪽에서 연습하는 모습도 연출됐다. 이후 준비한 프로그램을 취소할 정도로 불꽃 튀는 대결이 이뤄졌다는 후문. 각자 멘토, 멘티 역할에 충실히 임하겠다는 서약서를 쓰며 4개월 간 최선을 다해 프로그램에 임할 것을 다짐했다. 멘티는 새로운 트렌드를, 멘토는 회사의 경영 방향을!▲한 번 팀은 영원한 팀! 우리는 ‘Back to the 20th’입니다. 인터뷰를 위해 나란히 자리에 앉은 조성진 님과 김빛나 님. 한 번 팀은 영원한 팀이라는 것을 내보이듯 최근 여행 다녀온 이야기로 꽃을 피웠다. 수많은 사람 중 한 팀을 이룬 조성진 님, 김빛나 님 그리고 두 명의 멘토들. 이들의 팀명은 ‘Back to the 20th’였다. 고심 끝에 지은 팀명에는 남다른 의미가 있다. 두 가지 의미인데요. ‘20대처럼 밝고 건강하고 신선하게 돌아가자’라는 것과, CGV 20주년을 맞이한 해로서 ‘초심으로 돌아가자’라는 뜻을 갖고 있어요. - 김빛나 님  ▲첫 번째 멘토링 핫플레이스 '보안여관'에 간 ‘Back to the 20th’. 팀명을 지은 후 이들이 한 일은 카톡 단체방 개설과 4개월간 해야 할 활동 계획서 작성이었다. 각 달의 주제와 함께 문화 공간과 체험 프로그램을 선정해야 했다. 트렌디함을 잘 알고 받아들일 자세가 된 멘티였다고 해도 멘토 입장에서는 트렌디한 장소와 체험 등에 대한 고민을 할 수밖에 없을 터. 많은 대화와 고심 끝에 첫 달 주제인 '트렌드 및 핫플레이스'에 맞춰 문화재생공간인 ‘보안여관’을 찾아갔다. 과거와 현재가 공존하는 이곳을 즐기며, 왜 많은 이들이 찾는지에 대한 이야기를 나눴다. ▲‘뮤직라이브러리’와 ‘아트 공방’ 체험을 함께하며 트렌드 소개 및 배우기에 올인! 두 번째 달엔 공통 관심사가 ‘음악’이라는 것을 감안해 ‘현대카드 뮤직라이브러리’에 가서 LP로 음악을 원 없이 들었다. 생전 처음 LP로 음악을 들은 김빛나 님은 새로움을 느꼈고, 과거 이를 많이 들었던 조성진 님은 향수에 젖었다. 이날 그는 과거 좋아했던 서태지의 음반을 계속 들었다고. 세 번째 달엔 ‘소비와 문화 및 여가생활’이란 주제에 걸맞게 '우연수집' 아트 공방 체험을 했다. 아크릴 판에 별을 새겨서 무드등을 만드는 작업이었다. 조성진 님은 직접 만든 무드등을 아내 생일 선물로 줘서 점수를 좀 땄다며, 멘토들 덕분이라고 공을 돌렸다.  2030세대에겐 극장 외에도 즐길거리가 많다는 걸 눈으로 확인한 기회였어요. 이번 프로그램을 통해 새로운 트렌드를 열린 마음으로 받아들이는 계기를 얻어 직장 생활에 새로운 전환점이 될 것 같습니다. - 조성진 님  모두의 노력으로 일궈낸 역발상의 기적, 내년에도 쭈~욱15개팀 모두 원래의 계획대로 리버스 멘토링 프로그램을 잘 해나가기를 바랐지만, 처음 진행한 프로그램인 만큼 경영진과 사원 간의 보이지 않는 벽이 존재했다. 이를 위해 인사 담당이 준비한 건 ‘멘.친.소(멘토의 친구를 소개합니다)’였다. 월별 활동 중 멘토의 선후배를 초대하는 프로그램으로, 멘토와 멘티의 가교 역할을 하면서 화기애애한 분위기를 조성하는 데 일등공신이었다. 프로그램 진행 중에는 멘토와 멘티에게 따로 설문을 진행해 서로가 느끼는 힘듦과 바람을 간접적으로 전달하고 틈틈이 인터뷰를 통해 서로 원활히 교류하고 이해할 수 있도록 도왔다. ▲기회만 된다면 내년에도 리버스 멘토링을 꼭 하고 싶어요! 멘토-멘티들의 적극적인 참여 속에 이뤄진 이번 프로그램을 마무리하는 지점에서 이현주 님은 나름 성공적이었다고 자평했다. 아쉬운 게 하나 있다면 더 많은 멘토들이 참여하지 못했던 것. 특히 멘토들에게는 경영진과의 소통 기회를 더 많이 누리게끔 하고 싶은 것이 그의 생각이었다. 2회 때는 올해 참여자들의 활동을 발판 삼아 다양한 방면으로 홍보에 박차를 가하고 형식에 얽매이지 않는 새로운 도전을 지속할 예정이다.리버스 멘토링을 직접 경험한 멘토인 김빛나 님도 많은 것을 느꼈을 터. 멘티에게 알려준 것보다 회사의 비전을 구체적으로 알게 되었던 것, 직장 선배이자 인생 선배로서 좋은 이야기를 많이 들은 것, 이를 통해 삶을 어떻게 보내야 할지에 대한 생산적인 고민을 많이 하게 되었다고 소회를 밝혔다. 이처럼 좋은 프로그램이 계속돼야 한다는 이들에게 각자의 바람을 들어 봤다. ▲리버스 멘토링. 내년에도 쭈~욱 이어지길 바랍니다. CGV에서는 리버스 멘토링 이외에도 다양한 멘토링 프로그램을 시행하고 있는데요. 이런 소통 문화가 잘 정착되어, 어느 회사에서도 따라잡을 수 없는 CJ CGV만의 소통 문화를 만들어 가면 좋겠습니다. 인사 담당에선 제도적인 환경만 조성하고, 멘토와 멘티들이 자발적으로 멘토링을 진행하는 문화로 발전되길 바라요. - 이현주 님 앞으로 많은 분들이 프로그램에 참여할 텐데요. 왜 참여하고, 어떤 걸 얻을 수 있는지 사전에 인식하고 적극적으로 활동하는 게 중요하다고 봐요. 멘토, 멘티 할 것 없이 즐기는 마음으로 좋은 활동하길 바랍니다. - 조성진 님 경영진에게 어떤 걸 알려줄 수 있을지 잘 몰라서 지원을 망설였던 분들이 많을 거예요. 그러나 걱정하지 말고 꼭 지원하셔서 유익한 시간을 보내세요. 리버스 멘토링, 앞으로도 계속 참여하고 싶습니다. - 김빛나 님 멘토링 프로그램의 궁극적인 목적은 멘토링 프로그램을 없애는 것이 아닐까 하는 생각이 들었다. 소통을 위한 장으로서 멘토링을 하는 것이니 소통 문화가 잘 이뤄지는 기업에서는 굳이 시행할 필요가 없는 것. 시작은 좋다. 중요한 건 이제부터! 멘토링 프로그램 없이도 원활한 소통과 유대감을 쌓아 발전해 나가는 CGV의 미래 모습, 그날을 기대해본다.[채널 CJ] #CJ #CJCGV #CGV #리버스멘토링 #멘토 #유연한조직 #기업문화 #조직문화 #구성원인터뷰 #역발상 #CGV공채 #CGV채용
조회수 7060

KBS 신입사원 입사후기 - [이수민] KBS에서 기다리겠습니다(기자 편)

2018년 12월 12일은 제가 절대로 잊을 수 없는 날입니다. 오후 5시, ‘합격’이란 글자를 보는 순간 머리가 멍해졌습니다. 그리고는 그동안의 불안한 마음을 씻어내려는 듯, 눈물이 계속 쏟아졌습니다. 드디어 제가 기자라는 꿈을 이룬 순간이었습니다. 9월부터 시작된 모든 전형을 거치면서 신입사원 입사 후기를 정말 많이 들여다봤습니다. 정해진 정답이 없는 시험이라지만, 왠지 입사 후기에 그 답이 있을 것만 같았기 때문입니다. 분명 저와 같은 마음이었던 분들이 있을 거로 생각합니다. 그 마음을 누구보다 이해하기에, 부족하겠지만 최선을 다해 후기를 써보려 합니다. 2018년 신입직원 공개채용은 총 4단계로 진행됐습니다. 1. 서류 전형자기소개서는 최종 면접에까지 영향을 주는 중요한 서류입니다. 그만큼 정성 들여 써야 합니다. 저는 ‘KBS에 지원한 동기는 무엇입니까?’라는 질문을 가장 많이 고민했습니다. 왜 타사가 아닌 KBS 기자여야만 하는지 본인만의 답을 꼭 찾으시길 바랍니다. 자기소개서 문항이 매년 달라질 수도 있지만, 직무 지원 동기, KBS 지원 동기와 같은 문항들은 미리 써보고 제출 직전까지 고민하면 좋을 것 같습니다. 저 역시 작성 후 주위 사람들로부터 피드백을 받아 여러 번 수정했습니다. 그리고 모든 문장은 읽기 쉽도록 짧게 쓰는 게 좋습니다. 2. 필기 전형필기 전형은 상식(객관식+약술), 논술, 작문으로 진행됐습니다. KBS 뉴스, 신문, 시사 상식 책 등으로 일반 상식을 공부했습니다. 추가적으로는 KBS 사보를 정리해 KBS 관련 질문에 대비했습니다. 방송학 문제는 출제되지 않았습니다. 논술과 작문은 문제에 대한 답만 쓰기보다, ‘왜 이 질문을 했는가?’에 초점을 맞춰 답안을 작성했습니다. 저는 예상하지 못했던 논술 주제가 나와서 조금 당황하기도 했습니다. 정치, 사회 등 현안부터 KBS 보도, 공영방송의 역할, 기자의 역할 등 폭넓게 주제를 잡고 공부하셨으면 합니다. 그리고 주어진 시간이 길지 않기 때문에 시간 분배 역시 중요합니다. 시간 안에 글을 작성하는 연습을 하면 좋을 것 같습니다. 논술과 작문도 자기소개서와 마찬가지로 최대한 단문으로 쓰려고 노력했습니다. 필기 전형은 짧은 시간 내에 완벽하게 준비하는 게 어렵습니다. 평소에도 꾸준히 상식 공부와 글쓰기를 병행하면, 시험장에서도 좋은 실력을 충분히 발휘하실 수 있을 거로 생각합니다. 3. 실무 전형실무 전형은 토론 면접, 실무진 면접(사전 과제, 카메라 테스트 포함)으로 이틀에 나눠 진행됐습니다. 저는 실무 전형에 대비하기 위해 스터디에 참여했습니다. 토론 면접을 위해서는 최근에 이슈였던 사안을 정리해 찬/반 모든 입장을 공부했습니다. 저는 토론 프로그램과 관련 기사들을 보며 입장을 정리했습니다. 그리고 실제 면접 때는 제게 주어진 발언 기회를 최대한 활용했습니다. 저는 주장과 논거를 논리적으로 말하는 데에 중점을 뒀습니다. 또한, 토론 면접 때는 ‘듣는 기자’의 모습을 보여주는 게 중요하다고 생각합니다. 다른 사람이 얘기할 때 끼어들거나, 혼자만 계속 답변하는 모습은 피하시길 바랍니다.실무진 면접을 위해서는 크게 자기소개서, KBS, 시사 이슈로 나눠 예상 질문과 답변을 준비했습니다. 자기소개서 질문은 스터디를 통해 추가 질문이 나올 것 같은 부분, 추상적인 문장에 관한 구체적 사례 등을 위주로 준비했습니다. KBS 질문은 사보와 관련 프로그램을 살펴보면서 공부했습니다. 제가 준비한 질문은 KBS 뉴스의 장단점, 기억 남는 보도, 타사와의 비교, 신뢰도 문제 등이었습니다. 그리고 시사 이슈는 토론 면접을 준비하면서 공부한 내용을 바탕으로 저만의 답변을 만들었습니다. 실제 실무진 면접에서는 카메라 테스트 후 면접이 진행됐습니다. 카메라 테스트를 위한 1분짜리 스트레이트 기사를 쓰는 게 사전 과제였습니다. 많은 수치를 넣기보다, 누구나 들었을 때 한 번에 이해되는 내용으로 작성했습니다. 카메라 테스트 때는 오독 없이, 최대한 잘 들리도록 큰 목소리로 발음에 주의하며 읽었습니다. 그리고 실무진 면접 때는 당당하지만 겸손한 태도로 임했습니다. 잘 모르는 질문에는 솔직하게 잘 모르겠다고 답하기도 했습니다. 모든 답변은 두괄식으로 하되, 최대한 짧게(1분 이내) 말했습니다. 면접 때 예상하지 못했던 질문들이 많이 나왔습니다. 당황하면 말이 빨라지거나 본인이 가진 버릇이 나오기 쉬우니, 이를 대비하기 위한 연습도 하면 좋을 것 같습니다. 4. 최종 면접최종 면접은 사전 과제와 임원 면접으로 하루에 진행됐습니다. 사전 과제는 90분 동안 주어진 자료와 상황을 보고 취재계획서와 3분짜리 방송용 기사를 작성하는 것이었습니다. 시간이 길지 않고 자료의 양이 많으므로, 빠르게 주제를 정하는 게 좋다고 생각합니다. 물론, 해당 주제가 왜 기사로 쓰일 수 있는지에 대한 충분한 근거 역시 필요합니다. 이를 위해선 뉴스를 평소에도 꾸준히 보면 훨씬 도움될 것 같습니다.임원 면접을 위해서는 실무진 면접 때 준비했던 부분 외에 추가로 ‘어떤 기자가 되고 싶은가’를 많이 고민했습니다. 들어가서 어떤 보도를 하고 싶은지, 어떤 언론인이 되고 싶은지에 대한 고민을 평소에 많이 하시길 바랍니다. 임원 면접 때도 3차와 마찬가지로 당당하지만 겸손한 자세로 임했습니다. 스터디를 통해 모의 면접을 진행하거나 혼자 있을 때도 거울을 보면서 연습했습니다. 모든 답변은 문장 단위로 외우기보다 키워드 중심으로 준비했습니다.  서류 접수부터 최종 발표가 나기까지, 매일 롤러코스터를 타는 기분이었습니다. 하루는 잘할 수 있을 것 같다가도 다음날은 무기력함에 자신감이 바닥을 치기도 했습니다. 하지만 이 모든 과정을 거치면서 느낀 건 ‘KBS는 도전하는 사람에게 열려있다’라는 점입니다. 제가 잘나서 이렇게 후기를 쓰고 있는 게 아닙니다. 포기하지 말고 자신 있게 KBS의 문을 두드려 주십시오. 그러면 어느 순간 문이 열리고 KBS 안으로 들어가는 여러분의 모습을 보실 수 있을 겁니다. KBS에서 기다리겠습니다.#한국방송공사 #KBS #KBS공채 #KBS신입채용 #KBS채용 #입사후기
조회수 1071

생각은 어떻게 정리해야 하는 걸까.

고백할 게 있습니다. 전 정리변태에요. 정리를 샤샤샥 하는 걸 굉장히 좋아하죠. 그래서 애프터모멘트(제 회사예요. 이제 다들 기억해줘어어어)의 빠워는 '정리력'에 있어요. 뭔갈 챡챡 정리해서 깔끔하게 짠. 하고 보여주는 걸 좋아하죠. 막 이런거..곰곰히 생각해보니 저는 디자인 일을 하고 있지만 크리에이티브하진 않은 것 같아요.예전엔 저의 예민함과 풍부한 감수성을 '예술성' 이라고 생각했었어요. 아...내가 희대의 예술가적 소울을 지니고 있나보다... 그래서 조만간 항정살에 소주라도 한 잔 걸치는 날엔 위대한 작품이 하나 나올 수도 있겠구나 싶었죠. 하지만 이젠 알 것 같아요. 그건 예술성이 아니라 그냥 성격이 이상한 것 뿐이었어요. 하지만 뭔가 기발하고 창의적인 무언가를 만들어내는 능력 대신 다른 게 있단 사실을 깨달았죠. 5살때부터 30년내내 끊이지 않고 해온 게 있더라구요.'정리'였어요. 어떤 유전자를 받았는지 모르지만 방정리를 할 때가 제일 행복했던 것 같아요. 쓰그으으스ㅡ쓱싸ㅏㅇ아아삭쓸데없이 고된 즐거움을 너무 어린 나이에 깨우쳐버렸달까요. 영원히 끝나지 않는 정리의 저주에 걸려서 보이는 방마다 다 청소를 하고 싶어지는 슬픈 운명을 짊어지게 되었어요.(물론 지전분한 방을 보면 도전정신과 자존감이 솟구쳐 올라요...)이런 변태취향은 일할 때도 고스란히 녹아들어요. 사실 앞서 말했듯 저에겐 디자인을 하면서 필요한 크리에이티브함 조차도 사실 기발한 재치와 아이디어보단 수백개의 레퍼런스를 모아서 정리하는 습관에서 만들어진 부수적인 능력일 거예요. 이제 다들 한 번쯤은 자기방이든, 생각이든, 뱃살이든, 일이든 정리해보고 싶은 시즌이 돌아왔어요. 연말연시의 마법이죠. 이런 시점에서 얘기해보고 싶은 게 있었어요. 생각을 정리하는 5가지 방법 말예요.생각을 정리하기일을 하다보면 혼돈의 사도들을 만날 때가 있어요. 스스로 생각이 정리가 안되서 내적붕괴를 일으키거나 차크라를 주변에 폭발시켜 직원 또는 동료에게 광역피해를 주는 거예요. 모두에게 큰 데미지대표님이 생각정리가 안되면 '어제 한 얘기 뒤엎기''한 말 또하기''말로 세상을 구하기' '정리가 안된다고 짜증내기''회의성애자'등등의 문제가 생겨요.실무자가 생각정리가 안되면'일 꼬이고 결과물 망치기''메일과 전화로 말실수하기''일속도가 -5 저하되고, 표정 어두워지기''자신의 적성과 미래에 대해 고민하기''스트레스성 위장장애와 원형탈모'등등의 문제가 생기구요. 혹여라도 생각정리가 안된 대표님과 실무자가 만나면 이 세상 회사가 아닌 새로운 사내문화가 탄생하기도 해요. 그래서 미팅할 때는 일단 실무자와 대표님의 말을 다 들어보려고 하는 편이예요. 둘은 바라보는 시점이 다르기 때문에 각 시점에서 정리가 되어야 해요. 대표님은 미래를 보고, 실무자는 현재를 보기 마련이거든요. 두 생각을 각각 한 문장으로 정리해요. 두 점을 만들어 선을 잇는 느낌이랄까요.이런 맥락에서 제가 현장에서 '생각정리' 에 대해 느낀 몇 가지를 얘기해드릴께요.1. 저 말은 훼이크다.말은 생각을 100% 담지 못해요. 흔히들 말은 '구체적이다' 라고 생각하는 경향이 있는데 그렇지 않아요. 말은 생각보다 훨씬 추상적이예요. 구체적인 단어를 써도 그 단어는 그 뜻이 아니에요. 예를 들어볼께요. 실무자와 미팅하던 도중 실무자가 갸웃하면서 다음과 같이 말했어요.'그런데 보고하려면 레퍼런스가 좀 더 필요할 것 같아요.'얼핏보면 실무자가 레퍼런스를 요구하는 것처럼 보여요. 아주 단순한 문장같죠. 하지만 중요한 건 '레퍼런스' 가 아니예요. '보고하려면' 이죠. 그래서 저 말에 대한 응답은 - 어떤 레퍼런스가 필요할까요? 가 아니라- 결정권자 성향이 어떠세요? 가 되어야 맞아요.레퍼러스든 계획서든 기획안이든 아니면 예제시안이든 상관없어요. 보고를 통과시키기 위한 썸띵이 필요한 것 뿐이예요.  이 실무자는 이미 컨펌자의 피곤한 성격에 많이 털려봤어요. 그러니 레퍼런스를 추가로 가져다주는 건 바보 짓이예요. 차라리 컨펌자의 성향부터 물어보는 게 원활한 대화를 이끌어 낼 수 있어요.2. 생각정리엔 의외로 다독임이 필요해요.생각은 태어난 곳과 자라난 곳이 달라요. 보통 태어나는 곳은 무의식과 욕망이죠. 특히 출산율 높은 곳은 '불안' 이라는 도시에요. 불안에서 태어난 생각은 이성적(인 것처럼 보이는) 논리라는 옷을 입고 그럴 싸한 생각으로 둔갑해요.그리고 지상으로 서서히 올라오죠. 생각은 피라미드와 같아서 제일 밑바닥에 있는 불안을 이해하지 못하면 꼭대기의 표현을 제대로 볼 수 없어요. 그래서 보통 생각정리에는 독설보다 위로가 효과적이에요. 불안을 자극하면 생각은 더더욱 논리로 무장해버려요. 위로를 받고 인정을 받았을 때 비로소 아랫층이 열리게 되죠. 그래서 '그간 많이 복잡하고 힘드셨죠?.. 이전에 일하시면서 가장 답답했던 부분이 있으셨어요?' 라는 위로와 공감을 먼저 해드려요.그럼 이런 표정이...절로3. 딕테이션이 필요해요.보통 자신의 생각을 말할 때는 자기가 뭔 말하는 지 몰라요. 사람은 자신이 굉장히 논리적으로 말하고 있다고 생각하지만 실은 수없이 떠오르는 생각의 일부만을 잡고 계속 연결시킬 뿐이에요. 마치 대강 '코길고 귀큰 동물! '하면 '코끼리'를 떠올리듯이 말이예요. 아주 일부의 정보들로 생각을 이어나가죠. 전체적인 면을 고려하고 내 말을 곱씹는 건 굉장히 피곤하고 어려운 일이거든요. 그래서 두뇌는 경제성을 우선적으로 선택해요.이런 혼돈의 생각을 멈추게 해주는 건 누군가가 내 말을 다시 반복해주는 거예요. '아 그럼, 말씀하시는 건 예술가들의 명화, 명작들을 자유분방한 거리문화의 결합을 말씀시는 거죠?'라는 식으로 말이예요. 토론에는 사회자가 필요하죠. 그리고 사회자의 역할은 패널의 의견을 한 번 정리해서 상대패널에게 넘기는 역할을 해요. 요점과 핵심을 정확히 추리고 방향성을 잡는 거죠. 이런 역할을 하는 사람은 타고나는 거예요. 그러니 이런 역할을 잘하는 친구와 대화를 하도록 하세요. 만약 친구가 없다면.... 괜찮아요. 없을 수 있어요. 엉엉..... 없다면 녹음기를 이용해봐요. 내가 한 말을 다시 들으면 이 세상 대화가 아닌 느낌을 받을 수 있을 거예요. 굉장히 부끄럽고 능욕당한 느낌이겠지만 조금만 참으면 돼요.4. 서술어가 진짜 중요해요.본인이 스스로 생각하든, 누군가의 컨설팅을 받든 제일 중요한 건 '서술어' 예요.'아 그럼, 말씀하시는 건 예술가들의 명화, 명작들을 자유분방한 거리문화의 결합을 말씀시는 거죠?'위에서 이런 식으로 딕테이션했다고 쳐봐요. '결합'은 서술어가 아니에요. 그냥 개념일 뿐이죠. 그러니까 결합을 어떤 방식으로 하느냐가 더 중요해요. 그래서 질문은 이렇게 들어가야 하죠.'그럼, 명화의 레벨을 낮추는 거예요? 아니면 거리문화의 레벨을 높이는 거예요?'맞아요. 낮추다. 높이다. 나란히 가다. 등등의 눈에 보이는 동작형 서술어를 활용해줘야 해요. 보통 무슨 말인지 모르겠는 대화의 특징은 형용사와 명사가 겁나 많다는 거예요. 명사가 많아지면 문장엔 개념만 판치게 되요. 개념은 각각 이해하는 의미가 다르기 때문에 오해와 혼란을 부추기죠. 하지만 동작은 아주 명확해요. 모두가 공통적으로 떠올릴 수 있어요.만들다. 인쇄하다. 제작하다. 포스팅하다. 광고태우다. 채용하다. 등등.... 정확한 서술어로 생각을 표현하는 게 좋아요.5. 멋진 말 뒤에는 혼돈이 숨겨져 있어요.자꾸 가치, 평화, 공유, 사회, 모두의 만족, 추구, 도모, 높인다.. 등등의 추상적인 말이 많아지는 이유는 3가지가 있어요.진짜 어휘력이 없거나양가감정 때문에 갈등하고 있거나진짜 욕망을 숨기고 싶을 때예요.이런 말을 쓰는 분과 얘길 하기 위해선 두 가지 방법이 있어요. 대놓고 정곡을 찌르던가 아니면 술을 마시는 거예요. -_-  (물론 위의 2번처럼 처음엔 위로와 공감을 시도해봐야겠죵)정곡을 찌르는 건 이런거예요. '그럼 돈은 어떻게 벌어요?' 내지는 '일은 누가...?' 또는 한 템포 쉬고 '그게....뭔 말이예요??....' 라거나.적당히 돌직구가 가능한 사이라면 이런 식의 정곡은 꽤 좋은 효과를 낼 수도 있어요. 물론 어색한 사이끼리 이런 말을 하면 햇님달님이 되겠죠. 두번째 방식인 음주미팅은 가끔 생각보다 효율적이예요. 원래 앞에서는 서로 공적인 대화니까 좋은 말과 칭찬 일색, 두리뭉실한 예쁨으로 가득한 언어가 가득해요. 하지만 술 한잔들어가고 파란만장한 얘기를 주루룩 늘어놓다보면 진짜 욕망이 드러나기도 하거든요. 생각이 민낯을 드러낼 땐 언어가 아닌 감정으로 드러나는 경우가 많아요. 생각이란 결국 언어로 포장된 욕망과 같거든요. 그래서 잘 정제된 언어로 드러내는 생각은 실상 팩트가 아닌 경우가 많죠. SUMMARY위에 5가지 이야기의 공통점이 있어요. 1. 자기 생각은 자기 스스로 정리하기 힘들다. 누군가의 도움이 필요하다는 것2. 생각은 왜곡과 합리화, 일반화의 함정에 빠져있을 수 있다는 것3. 생각은 작정하고 정리하는 것이 아니라는 것.정확히 말하자면 '생각을 정리하는' 게 아니라 '욕망을 정리하는' 것이 맞다고 생각해요. 내가 원하는 것이 무엇이고 그걸 이룰 수 있는 방법이 무언인지를 고민하는 과정이기 때문에, 결국엔 궁극적인 욕망과 욕망의 우선순위를 결정해야하는 것이죠. 하지만 우리는 욕망이란 종종 부끄럽거나 비도덕적인 것으로 여기기도 해요. 하지만 사실 남한테 말하고 보여주기 부끄럽고 뭐가 없어보여서 그렇지 그 자체가 나쁘진 않아요. '이유는 모르겠지만 돈이 있었으면 좋겠어!!!''그냥 쟤 짜르고싶다..개스트레스받아..''괜히 다른 거 한다고 하면...인내심이 없어보일까..''이것만 하면 불안한데...다른 것도 해야 먹고 살 수 있을 것 같은데..''주님의 뜻이니 굽힐 수 없어!!! 그냥 믿음으로 가는거야!!''21세기가 나를 원하고 있어. 이 미친세상에 빛이 되버리겠어!!! 고나비롸잍' '난 권력이 좋아!!! 하앍..''그냥 아무것도 하기 싫어. 그러니 안할래.'등등 실제의 욕망은 매우 단순하고 직관적이잖아요. 그래서 더욱 에너지가 강하고 충돌이 잦기도 해요. 그래서 우린 욕망의 소용돌이에서 도망쳐 생각의 숲으로 들어가요. 그곳은 아주 논리적이고 내가 이해할 수 있는 것들로 가득찬 안전한 세계니까요.생각이 복잡하고 혼란스럽다면 그 아래를 움직이는 욕망을 먼저 관찰해줘야 해요.그리고 욕망이 분명해졌다면 아주 심플하고 단순한 문장으로 날것의 욕망을 언어화 시켜요.'난 내년에 두배매출을 내서 통장에 1억을 만들고싶다. 왜냐면 없으면 불안하니까.'이런 욕망이 있다고 해봐요. 이게 잘못된 건 아니예요. 뭐 사람에 따라선 어리석어 보일 수도 있죠. 실제로 1억이 있어도 불안한 건 마찬가지일 테니까요. 하지만 지금 내 불안이 그걸 외치고 있다면 솔직하게 정리하는 게 좋아요. '있다고 해서 안불안할까? 그건 아니겠지만, 일단 없이 불안한 것보다 있고 불안한게 더 명확하니까.'라고 단순하게 명제화시켜요. 이건 부끄러운 게 아니예요. 남들에게 보여주기에 멋진 것을 만들려고 하면 점점 이상한 언어들로 꼬일 뿐이더라구요. 거창하고 예쁘고 멋지지 않아도 되니 깔끔하게 정리했다면... 그냥 그 방법들만 구체적으로 잘 만드는 게 좋다고 생각해요. 생각보다 실제 욕망은 음....? 스러운 게 많다라는..혹시 너무 많은 생각과 언어들에 둘러싸여 있나요? 그렇다면 지금 여러분은 종이, 스티로폼, 뽁뽁이, 박스, 비닐, 택배상자로 겹겹히 둘러쌓인 택배박스를 보며 머리를 쥐어뜯고 있는 것일 지도 몰라요. 내용물을 뜯어보도록 해요. 포장지만 잡고 달리다간, 언젠가 벗겨지기 마련이니까요.    :)끗
조회수 1211

안드로이드 개발자의 고민: Fragment

Activity는 화면의 기본 구성단위 입니다. 예전엔 하나의 Activity를 SubActivity 단위로 사용하려고 ActivityGroup으로 여러 Activity를 하나의 Activity로 묶어 사용했습니다. 이 방법은 장점보다 유지 관리 및 Lifecycle 관리 등의 이슈가 더 많았죠. 이제는 사용하지 않습니다.관리 이슈를 보완하기 위해 나온 것이 바로 Fragment입니다. View에는 없는 Lifecycle이 존재합니다. 이것을 이용해 Activity에서 할 수 있는 작업을 Fragment에서도 처리할 수 있습니다.더 이상 ActivityGroup을 이용해서 화면을 재활용하거나 Activity를 관리하지 않아도 됩니다. 대신 FragmentActivity를 이용해 여러 Fragment를 한 화면에서 보여주고 관리할 수 있게 되었습니다.브랜디에서 운영하는 하이버 앱은 위와 비슷하게 설계되어 있습니다. 화면의 기본이 되는 Activity에 실질적인 View를 담당하는 Fragment를 사용합니다. 여기에는 fragment layout이 있죠. 이런 설계 방식은 Activity 영역에선 보통 Toolbar 기능과 Bottom Menu Button을 만들 때 사용합니다. 실질적인 뷰는 Fragment 영역에서 보여주죠.하이버 앱은 Endless 기능을 포함한 RecyclerView가 80% 이상의 화면 비율을 차지합니다. 상품을 나열해서 보여주거나 스토어 목록을 보여주는 리스트 화면이 대부분이어서 RecyclerView에서는 다양한 api를 요청하고, 응답받은 데이터를 Adapter에서 View로 나누는 것이 주된 작업이었습니다.생각한 것과는 다르게 설계되고 말았습니다. 다양한 화면을 재활용하려고 사용한 Fragment들은 API 요청 URL만 바뀌었을 뿐, 화면의 재활용은 Lifecycle 기능이 없는 Adapter에서 관리했기 때문입니다.대부분의 Activity layout의 fragment는 fragment_default_f_adapter.xml 을 이용했습니다.더불어 Fragment를 사용하면서 제일 많이 접한 Fragmentmanager Transaction 버그 때문에 다양한 트릭을 써야 했습니다. 특히 비동기로 생기는 결함이 가장 큰 문제였습니다.문제점이 있어도 View에서는 가질 수 없는 Lifecycle 때문에 결국 Fragment를 사용해야 했습니다.이것은 모든 안드로이드 개발자가 가지고 있는 고민입니다. 하이버 앱은 리펙토링은 끝난 상태이기 때문에 더 이상 리펙토링에 시간을 쓸 수 없었습니다. 그래서 이번에 진행할 브랜디 리펙토링에서는 이 문제점을 고치려고 합니다. 저는 여기에서 도움을 많이 받았습니다.이전에도 이러한 라이브러리가 있다고 알고 있었지만 하이버를 리펙토링하면서 문제를 직접 마주하니 라이브러리가 왜 나왔는지 새삼 느꼈습니다. (역시 사람은 위기를 맞이할 때 큰 깨달음을 얻나 봅니다.)다음 화에서는 이러한 Fragment 문제를 극복하는 방법을 알아보겠습니다.글고재성 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #안드로이드
조회수 720

궁금해서 여행다녀오겠습니다 - 1

"한 사람의 삶에서 무엇이 영향을 미쳤고, 어떻게 시간을 보내며 살아가고 있을까? "집에 방문해보면 그 사람이 어떤 사람인지 더 깊이 알 수 있다."궁금해서 여행 다녀오겠습니다" 누군가의 책장을 살펴보면 관심분야와 생각을 알 수 있고, 가지고 있는 물건들을 보면 취미나 취향을 알 수 있다. 그렇게 한 사람을 좀 더 잘 파악할 수 있게 된다. 그렇다면 사람들, 인류 전체 역사에서 무엇이 중요한 영향을 미쳤고, 사람들(인류)은 어떻게 시간을 보내며 살아가고 있을까? 앞으로 우리는 어떻게 살게 될까?  이 질문에 대한 답을 찾으려면 사람들이 살고 있는 곳을 방문해야했다. 사람들이 살고 있는 도시에 가서, 역사와 흔적을 찾아보면 사람들(인류)을 더 잘 파악할 수 있을거라 믿었다. 그렇게 나의 500일에 걸친 도시 관찰 일기가 시작됐다.Prologue : 관찰 여행의 시작 사실 대학 입학 후, 나의 목표는 졸업 전까지 5대양 6대주를 다 보는 것이었다. 국제적인 이벤트에 지원받아서 참가할 수 있는 기회를 계속 찾고, 방학이 되면 해외로 나가기 위해 학기 중에 돈을 모았고, 그렇게 남미, 북미, 유럽, 중동, 동남아, 동북아를 하나하나 여행해 나갔다. 본격적으로 관찰 여행을 시작하게 된 것은 졸업 후 회사를 다니면서다. 언젠가는 창업을 하리라 라는 마음이 있었기에 스타트업에 계속 관심을 가지고 있었고 아이폰이 세상에 등장한 이후, 실리콘밸리에 대해 궁금증은 커져갔다. 그래서 회사를 다니는 동안, 휴가를 활용하여 세계적인 스타트업 허브들을 방문하기로 마음먹은 것이 첫 번째 관찰 여행의 시작이었다. 가서 어떻게 더 많은 것을 얻어올 수 있을까 라는 고민을 하면서 본격적으로 나만의 글로벌 탐방 여행을 기획하기 시작했다. 미국의 실리콘밸리, 이스라엘의 텔아비브 (이스라엘은 미국 나스닥(한국 코스닥에 해당)에 가장 많은 기업을 상장시켰다), 중국의 선전, 싱가폴 등 주요 도시를 목표로 기획서를 작성하고 차례로 방문하기 시작했다. 처음 실리콘밸리에 갈 때는 매일매일의 점심, 저녁 시간에 인터뷰 약속을 잡아놓고 출발했다. 인터뷰를 하면서 meetup을 통해 현지 event 에 참가하는 방법을 배웠고, 기회가 닿아 회사에도 방문하게 되었다. 그렇게 점점 기획하는 능력이 개선되어서, 텔아비브 방문 시에는 개인 인터뷰뿐만 아니라, meetup 등 현지 이벤트 참가, 기업 방문(startup, 대기업, VC, 엑셀러레이터) 등을 조합하여 기획해서 다니게 되었다. 결과적으로 이렇게 다닌 여행은 기존의 여행과는 다른 경험이 되었다. 다녀와서 생각이 달라지기도 했고, 세계의 흐름을 조금 더 이해할 수 있었다.To be Continued 

기업문화 엿볼 때, 더팀스

로그인

/