스토리 홈

인터뷰

피드

뉴스

조회수 740

창업자의 일기장(6)-학생의 길

-----이전 이야기-----백수의 삶에서나름 도서관 전전하며 준비하다가...듣고 싶었던 교육에 선정되었다.퇴사한 후, 나의 계획 중에전문지식을 더 심화하여야 할교육이 있었다.창업하고자 하는 분야가제조업이다보니공장/공정에 대한 공부다.물론 신청해서 선정되어야 하고,3개월이라는 긴 시간을 각오해야 했다.350시간 공부해야하기에매일 아침 9시부터 저녁 6시까지 서초에서 공부하고, 시험치고발표하고...그리고 남는 시간에는 영어회화공부를 했다.전국각지에서 몰려온 청춘들과함께 공부하다보니 학생시절이 떠올랐다.물론 나보다 나이가 많은 형님들도 있었고,나처럼 직장을 관두고 공부하는 사람들도 있었다.갓 졸업하고, 취업 준비하면서공부하는 친구들이 많았는데특히나 분야가 플랜트쪽이다보니전공이 거의 화공이었다.그 중에 몇 안되는 타 과 전공자!그게 나였다.그래서인지 처음에는 못 알아 먹는 전문용어와기본적인 이론지식이 후달리더라.뒤처지는 것이 싫었던 나는정말 필사적으로 공부했다.감사하게도함께 공부한 학생들이 이런 나를 많이 도와주었다.특히나, 조별로 묶이게 된학생들은 나이 차이와지식의 차이가 현저하고,가족이 있는 나에게더욱 친절하게, 상세하게공부를 도와주었다.전체인원이 60명 정도 였는데 거기에서 나이로 치면 NO. 4 되었다.하긴 나보다 더 나이 많은 분들도그렇게 필사적으로 매달려서 공부하는데앓는 소리 할 수 없었지.중간중간 시험과 평가에서좋은 성적을 거두면서,처음에는 교육 수료가 목표였지만,조금씩 욕심이 생기기 시작했다.몸도 마음도 지친 상태로집에 들어가면,아내는 늘 웃으면서나의 일상을 물었다.그럴수록 정말 더 미친듯 집중해야겠다고다짐하고 또 다짐했다.새벽마다 경의선 첫 차타고 가고,밤이면 도서관 들려서 버스타고 들어왔다.게다가 그 때의 시기는 겨울이라눈이 많이 내렸다.빙판길과 눈길을 헤치고 다녔고,점심값을 아끼기 위해서편의점 삼각김밥이나 컵라면을 즐겼다.그래도 한없이 미안하더라.못난 남편이 자기 욕심에 던져버린,평범할 수 있던 삶에 대한 책임을같이 감당하는 아내에게,그리고 태 중의 아이에게너무 미안했다.늘 감사했고,나는 사치부리는거라 믿으며하루하루를 꽉꽉 채워갔다.결국 목적은 창업을 위한 준비다 보니우선순위를 두고, 부분별로 수행해 나갔다.1. 지금 당장 하고 있는 플랜트 공정 교육에 집중2. 꾸준한 영어회화 공부3. 사업계획서랑 팀원 꼬시기4. 실업급여 지급이 끝나고 먹고 살 것 찾기그리고 이 항목들은 시간별로 워크시트를 만들어서 체크해 나갔다.전체 스케쥴은 나중에 알게 된"프리마베라"라는 프로그램을 본 따서조정해 나갔다.시간이 쏜살같이 흘러가더라.그리고 예상보다 더디거나 로드되어 밀리는 현상도 생기고잘 안풀리는 것들도 많고....이런 것이 다 사업의 밑거름이다.지식의 향상도 있지만,이러한 경험들이 모든 사업 활동에서적용 되는, 마주하는 일상적인 패턴들이다.그래서 감사한 마음으로 배웠다.지금이 아니면언제 이렇게 배울 수 있으랴~!그리고 왠지 학생이라는 소속감이백수라는 내 현실을 잠시 잊게 해 주었다.
조회수 1515

1) 우리는 왜 애자일 하지  못할까

글목록1) 우리는 왜 애자일 하지 못할까 (현재 글)2) 우리는 애자일 하게 일하고 있을까?3) 나는 애자일 하게 일하고 있을까?소프트웨어를 만드는 회사, 그리고 스타트업들은 산업의 특성상,“빠르게 프로덕트를 만들고, 시장에서 프로덕트를 시험하고 지속적으로 발전, 또는 피봇 한다.” 라는 관점에서 린 스타트업, 익스트림 프로그래밍, 애자일 등의 방법론과 업무 프로세스에 대해 많은 사람들이 관심을 가지고, 이하 프로덕트 개발 방법론들을 도입하고 적용하기 위해 노력하고 있습니다. 그래서 많은 방법론 책, 애자일에 대해 신성시하는 글, 또는 애자일에 대한 부정적인 글들도 많이 나오고 있는데요,이런 많은 방법론들이 나오고 좋은 프로세스가 있음에도 불구하고 우리가 우리가 진짜 애자일(Agile)하게, 또는 린(lean)하게 일하고 있을까?라는 관점에서 봤을 때, 우리는 어떻게 일을 했었고, 우리가 일을 하는 방식이 정말 그런 방법론들이 이야기하는 방법으로 일을 하고 있는 건가에 대한 부분은 확인이 필요합니다. 제가 지금까지 다니며 많이 배운 회사들 그리고, 개발자, 디자이너 그리고 피엠분들과 이야기했을 때, “제가 다니고 있는 회사는 이런 부분에서 문제가 있었어요.”라는 이야기를 들을 때마다 공통적으로 느낀 점들은프로덕트를 사용할 “유저” 보다 생각하는 “기능”에 집중하고“무엇이 가장 중요한지”에 보다 “뭐든지 빨리” 만드려 하고우리가 들이는 시간과 노력을 “왜” 이 기능에 들여야 하는지에 대한 공감이 없고"기능"을 정확한 기간 안에 맞추기 위해 사람들이 시달리고, 팀 서로 간의 배려가 없어지고일을 하면서 소통을 위한 회고는 줄고, 업무의 피로도는 쌓이는등의 비슷한 상황들을 경험하시는 것을 확인했고, 이러한 불안정한 상황이 굉장히 애자일 하게(!) 돌아가는 상황들을 확인할 수 있었습니다. 그리고 이 부분을 개선할 수 있는 가장 기본적인 시작은 "프로덕트를 만드는 과정에서 우리는 어디에서부터 고민을 시작해야 할까"라는 생각을 했고, 이를 통해 빠르게 가치를 확인하는 프로덕트를 위해 무엇을 가장 먼저 고민해야 하는가라는 주제로 이야기를 좀 해보려 합니다.1. 처음 프로덕트를 만드는 건 우리지만, 결국 프로덕트를 사용하는 건 유저다. 애자일 프로세스에서 가장 기본적인 목표는 “Agility” 즉 빠르게 가치를 만들어 나가는 과정입니다. 그리고 빠르게 가치를 만들어 나간다는 것은 ”빠르게 프로덕트를 만든다.” 보단 “유저가 가치를 느낄 수 있는지 빠르게 확인하고 가치를 늘려나간다.”라는 부분이 더 중요하다고 생각해요.  그리고 빠르게 유저에게 가치를 줄 수 있는지는 실제 유저들과의 다양한 interation을 바탕으로, 빠르게 배포하고 빠르게 수정하는 과정이 애자일 프로세스의 가치입니다. 그래서 “어떤 기능을 어떻게 만들겠다.”라는 생각에 대해 어떤 유저가 사용할 것인지유저는 어떤 가치를 얻기 위해 사용할지가치를 얻기 위해 가장 먼저 해야 할 것은 무엇인지에 대한 고민이 우선되고, 가장 우선돼야 하는 일들부터 시작하는 것이 우선인데, 우리가 프로덕트를 만드는 과정에서는 “애자일, 스프린트”라는 이름에 갇혀 개발의 과정이 너무나도 가려져 온 것 같습니다. "어떤 기능을 만들어야 한다."라는 가치에 대한 제안(Value Proposition)이 나왔을 때, 가장 우선시 돼야 하는 것은 "이게 유저에게 얼마나 큰 가치가 있고, 이게 비즈니스 적으로 가장 중요한 일인가."라는 검증이 우선돼야 합니다. 정말 유저에게 좋은 프로덕트더라도 아무도 쓰지 않으면 의미가 없고, 가치를 통해 회사가 이윤을 얻을 수 없다면 좋은 기능이라고 판단할 수 없죠. 그렇다고 100% 검증된 기능을 만들 수 있다는 이야기는 더더욱 아닙니다. 그래서 우리는 더 작게, 유저가 가치를 얻을 수 있는 프로덕트를 만들고, 개선시켜 나감으로써 지속적인 배포를 가지고, 지속적으로 유저가 필요한 것들을 확인할 수 있습니다(파란색 줄). 그리고 이런 과정을 지속적으로 진행하면서 우리는 짧은 기간의 론칭 또는 배포 주기에 따라 유저의 성향에 맞춘 프로덕트를 만들고, 상대적으로 위험성을 낮춘 프로덕트를 만들 수 있습니다. 한 가지 기능을 위해서 내가 생각하는 모든 것들을 한 번에 100% 만들 필요는 없어요. 유저가  가치를 느낄 수 있는 가장 작은 범위부터 서비스를 만들고, "우리가 타케팅 한 유저는 어떤 걸 정말로 좋아하는지"에 대해 확인해 가면서 성장할 수 있으니까요.2. 프로덕트를 개발하는 과정은 “스프린트”가 아니라 "마라톤"이다. 애자일한 프로세스 진행 시 "정형화된 스프린트, " "목적이 명확한 이터레이션"에 막혀 릴리즈에 대한 압박들 때문에 스트레스를 느끼게 되는 경우가 많은데, 과연 이런 "정책"들이 "일하는 사람들"보다 중요할까요? 스프린트처럼 빠르게 진행되는 개발과 검증의 과정에서 일정한 움직임(Cadence)으로 빠른 속도(Velocity)는 굉장히 중요한 요소입니다. 비즈니스와 프로덕트의 목적에서 역시 계산 가능한 범위 산정 및 릴리즈 계획은 꼭 필요한 요소지만, 일정과 기능에 대한 기한 때문에 우리가 일하는 과정에서 정책, 개발론이 사람이 일하는 환경과 심리적 요소를 해친다면 결코 좋은 애자일 방법론이 아니라고 생각합니다.위 이미지와 같이 애자일 프로세스를 진행함에 따라, 일정한 개발 주기와 개발 속도를 따라감으로써 기술 부채와 러닝 커브를 줄이기 위해 노력하지만, 우리가 지금까지 일하는 방식에서는 - 정확한 이터레이션의 종점을 찍기 위해 - 정확한 개발 범위와 마커를 세우기 위해즉, "기계 같은 개발 속도와 빠른 론칭"을 얻기 위해 - 왜 무엇을 빌드해야 하는지에 대한 이해와 공감 없이 작업이 진행되고 - 팀원들의 심리적, 물리적 한계를 느끼게 되는상황들을 우리는 자주 볼 수 있었어요. 그리고 이런 공장 같은 프로세스에서는 지속 가능한 프로덕트보다는 만드는 과정에서 지치게 돼 사람들이 떠나는 아주 비 생산적인 프로덕트를 만들 수 있는 가능성이 커지게 되죠. 즉 프로덕트를 만드는 사람들이 숨도 돌릴 틈 없이 스프린트를(Sprint)하다가, 프로덕트가 결국 가야 하는 높은 레벨(High-level)을 가기 위한 기나긴 42.195km라는 길을 도착하기도 전에 지칠 수 있게 되는 거죠. 물론 비즈니스의 방향과 마일스톤, 그리고 프로덕트를 만드는 요소중 중요한 요소중 하나인 "일정"이란 부분은 절대적인 부분이기 때문에 거스를 수 없습니다. 그래서 프로세스와 정책이 있는 것이지만, 모든 프로세스와 정책은 결국 일하는 사람들이 어떤 가치를 위해 일할 때 이를 잘 이룰 수 있게 길을 제시하기 위해서 존재하는 것이라고 생각합니다. 오늘 말씀드린 이야기 두 가지를 한 번에 정리하자면, 프로세스는 유저와 팀원을 가두는 게 아니라, 그들을 기반으로 프로세스를 정립해야 한다는, "사람을 위한" 프로세스를 가져야 한다고 아주 간단하게 정리될 수도 있을 것 같아요. 이야기들이 조금 모호하고, 직접적인 내용들이 좀 부족해 다음엔 스토리엔 오늘 말씀드린 내용들을 중심으로기존에 제가 느꼈던 제가 했던 또는 들었던 프로덕트를 만들며 힘들었던 과정에 대한 자세한 설명유저 가치를 기반으로 작업함에 따라 힘들었던 과정을 지속적으로 수정하고 있는 업무 프로세스에 대해 설명하고 비교를 통해, 어떤 가치를 얻고 있는지 설명드리도록 하겠습니다!#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 1765

[스토리] 더부스 브루잉의 새로운 로고 소개

어느 순간부터더부스 브루잉의 로고가 바뀐 것알고 계셨나요?많은 분들이궁금하셨을 거예요. 어느 순간,갑자기 등장한파란색의 정체불명(?)의형태가 무엇인지.바로더부스 브루잉의 새로운 로고입니다!많은 질문을 받았어요.외계인인가요?아니면 하이힐?심지어강아지 똥?같다는 분도 계셨죠.더부스 새로운 로고의 의미,아래 영상을 보시죠너무 짧다고요?그럼 이제부터친절히 설명해 드릴게요. 마음을 채우다더부스는 경리단 15평 공간의 작은 펍에서 시작했어요. 모든 사람들이 ‘괜찮은’ 가격에 크래프트 비어의 즐거움을 누리게 하고 싶다는 마음 하나로 덜컥 시작한 펍이죠. 근데 가진 건 정말 “의지” 하나 밖에 없었어요. 원래 한의사, 기자, 투자 자문사였던 사람들이니까요. 당시에는 맥주 전문가가 아니었죠. 그래서 더부스 시작 당시 사실 ‘오리지널’ 더부스라고 말할 만한 것은 하나도 없었어요. 맥주 양조 방법은 빌에게서 빌려왔고, 다른 양조장의 시설과 인력을 빌려 맥주를 빚었죠. 많은 사랑을 받은 피자는 홍대의 몬스터피자에서 빌려왔고요. 하지만 우리의 바람대로 더부스에서 잔과 잔을 부딪히며 크래프트 비어를 즐기는 사람들이 많아질수록, "진짜 우리가 직접" 맥주를 만들어 갈 수 있었어요. 경리단 작은 펍에서 잔과 잔이 부딪히며, 서로의 마음을 채워주기 시작했죠!변화를 만들다더부스팀은 오늘이 있기까지 남들이 가지 않은 길에 도전하였고, 많은 변화를 이뤄냈어요. 그중 하나는, 처음에는 모두가 미쳤다고 했지만, 더부스팀이 유통하는 모든 맥주들의 맛을 "신선하게" 관리하기 위해 맥주가 생산되는 벨기에, 덴마크, 미국에서부터 서울까지 "냉장상태"로 맥주를 가져오기 시작한 거예요. 또 세계적인 브랜드 미켈러와의 콜라보레이션으로 "대동강"이라는 맛있는 맥주도 만들었구요. 지난 ’15년 9월에는 크라우드 펀딩인 8퍼센트를 통해 투자자를 모집하여 단 몇시간만에  "10억원의 투자금액"을 유치하는 저력을 보여주었습니다. 더부스 팀은 이 자금을 활용해 캘리포니아 유레카 지역의 양조 시설을 인수했고, 이제 더 놀라운 맥주를 본격적으로 생산하기 위해 준비중이에요.크래프트 비어를 만들고 판매하는데 멈추지 않고 한국 맥주의 변화를 이끌어 가기 위해 항상 노력하며, 크래프트 비어와 공유할 수 있는 문화를 즐겁고 의미 있는 방법으로 실현시키고 있는 회사. 바로 더부스 브루잉 입니다.사랑을 이루다이건 보너슨데, 아시죠? 더부스를 시작할 때 연인 관계였던 두 대표 희윤님과 성후님은 결국 사랑의 결실을 맺었다는 거!더부스 브루잉의 새로운 변화는 로고의 변신에서 멈추지 않고, 더욱 질 좋고 신선한 맥주와 상상을 넘는 이벤트로 찾아갈 거예요.우리의 두들 가이는 영원히 사라지지 않아요. 지금도 새로운 로고 속에서살아 숨 쉬고 있답니다. Make This Happen!#더부스브루잉컴퍼니 #창업자 #스타트업창업 #창업가 #인사이트 #히스토리 #브랜드 #브랜딩 #로고 #로고디자인
조회수 3055

챗봇과 인공지능 머신러닝 ㅡ Part 1/2

스타워즈를 보신 분이라면 거기에 나오는 난쟁이 로봇 R2D2와 키다리 로봇 C3P0를 아실 것이다. 친근한 R2D2는 전자음을 조정해 인간과 대화를 하며 주로 말 잘하고 박식한 로봇인 C3P0가 통역을 해준다.이런 충실하면서 똑똑한 친구들이 옆에서 항상 나를 도와준다면 어떨까? 정말 좋을 것이다. 만약 매일 보는 스마트폰 안에서도 나의 질문에 답해주는 이런 고마운 친구들이 있다면 얼마나 좋을까? 이런 저런 생각을 하다보면 우리는 대화형 로봇의 필요성을 느낀다.챗봇(Chatbot)이란?챗봇의 정의는 “대화형 인터페이스 상에서 규칙 또는 지능으로 유저와 소통하는 서비스”이다. 이 말을 하나하나 풀어보자.먼저, 대화형 인터페이스란 뭐지? 어렵다. 쉽게 설명해 보자. 인터페이스는 사람과 컴퓨터를 연결하는 장치라고 한다. 역시 어렵다. 아! 그냥 스마트폰 앱으로 보면 된다. 그럼 소통한다는 말은 대화한다는 것이므로 스마트폰 앱에서 일방향이 아닌 양방향이 가능하다는 얘기다. 어! 이상하다. 양방향이라면 나의 말에 응대하는 로봇은 뭐로 움직이는 거지? 궁금하다. 누가 일정한 규칙으로 만들어 논건지 아니면 우리처럼 지능이 있는 건지. 지능이 있다면 그런 지능은 뭐지? 점차 우리는 자연스럽게 인공지능에 다가간다.인공지능(Artificial Intelligence)이라는 용어는 1956년 미국 다트머스의 한 학회에서 존 매카시가 처음 사용했다고 한다. 원래 인공지능은 소프트웨어인 정신을 말하고 로봇은 하드웨어인 육체를 말하는 것이지만 정신없이 육체가 존재할 수 없는 것처럼 로봇을 얘기하면 당연히 인공지능은 따라간다.학자들은 인공지능을 강(强)인공지능과 약(弱)인공지능으로 구분한다. 간단히 얘기하면 강인공지능이란 자의식이 있는 인간에 가까운 지능이고 약인공지능은 자의식이 없다. 자아가 없으며, 명령받은 일만을 수행한다. IBM의 왓슨(Watson), 작년에 인공지능의 붐을 가져온 구글의 알파고(Alpha-GO) 등은 모두 약인공지능이다. 이런 인공지능을 구현하는 기술은 무엇인가? 바로 기계한테 학습을 시키는 머신러닝(Machine Learning)이다.1959년 아서 사무엘은 머신러닝을 "기계가 일일이 코드로 명시하지 않은 동작을 데이터로 부터 학습하여 실행할 수 있도록 하는 알고리즘을 개발하는 연구 분야"라고 정의했다. 여기서 학습이란, 입력 값을 받아 결과 값을 내는 모델을 만드는 표현과 표현을 통해 주어진 업무가 얼마나 잘 수행됐는지 알아보는 평가, 그리고 평가에서 설정한 기준을 찾는 최적화로 구성된 일련의 과정을 말한다. 중요한건 우리가 시키지 않은 일도 학습에 의해 자율적으로 처리한다는 것이다. 정말 신기하지 않은가?이제 챗봇이 뭔지 감이 잡힌다. 스마트폰 앱상에 존재하는 로봇인데, 물론 육체는 화면의 아이콘으로 밖엔 안보이지만 인공지능을 가지고 머신러닝에 의해 동작을 하면서 우리와 대화를 하는 그분. 그렇다면 이제 남은 건 이분의 지능이 어느 정도인지 또 얼마나 일을 잘하는 지로 판가름 난다.우리는 평생 공부를 한다. 이제는 학교를 졸업하고 나서도 항상 배워야 한다. 학습이 없다면 지능도 없다. 학습은 일일이 지도받는 지도학습과 알아서 공부하는 자율학습이 있다. 알아서 공부하려면 먼저 머리에 지식이 많아야 한다. 역시 기계도 사람과 비슷하게 배운다.  다음시간엔 챗봇에게 학습을 시켜 지능을 가지게 하는 방법에 대해 알아본다.> Part 2에서 계속
조회수 557

직원 근무일정 스케줄링 과정과 시프티

직원 스케줄링 프로세스의 기본은 팀 또는 지점의 각 직원의 역할을 알고 일별 근무일정 패턴을 파악하는 것으로 시작됩니다. 팀이 제 능력을 발휘하려면 주어진 시간에 각 역할별로 특정 인력이 필요합니다. 바텐더를 할 수있는 사람이 휴가를 가거나 회사를 떠나면, 그 자리를 채울 수 있는 사람을 찾아야 합니다. 팀과 사업에 대해 알지 못한다면 각 직원을 올바른 직무에 배치하는 것이 불가능할 수 밖에 없습니다.근무일정을 잘 계획하기 위해서는 다음과 같은 4 가지 필수 단계가 필요합니다.1. 근무 가능 시간 (Availability) 수집하기스케줄을 잡기 전에 각 직원의 근무가능일과 시간대를 알아야 직원들이 일할 수 없는 근무일정을 짜지 않을 수 있습니다. 보편적인 절차 중 하나는 각 직원의 재임기간동안 근무 가능 시간대를 수집 한 다음 해당 월의 휴무 (유급 및 무급) 요청을 수집하는 것입니다. 우리는 지금까지 다음과 같은 두 가지 방법으로 각 직원의 근무 가능 시간을 수집하는 많은 관리자들을 발견하였습니다.첫 번째는 풀타임 직원이 변동되는 휴무일을 가지는 경우입니다. 예를 들어, 각 직원은 한달에 8일의 무급 휴무일을 가지지만 쉬는 날을 정하는 것은 각자의 몫인 경우입니다. 직원들은 자신들의 쉬는 날, 그리고 유급 휴가(연차 등)를 언제 쓸지를 서로 합의한 후에 승인을 위해 관리자에게 제출합니다. 직원들은 일할 날, 쉴 날을 자유롭게 선택할 수 있었고 이는 매장에 일할 직원이 없는 날이 없도록만 하면 되었습니다.둘 째로 파트 타임 직원이 많을 때, 보통 관리자가 근무 일정을 계획/조정할 수 있는 권한이 더 많았습니다. 이 경우 관리자는 모든 직원의 근무 가능 시간을 알아야 합니다.시프티는 근무 가능 시간 (availabilities) 기능과 직원의 휴무 신청 및 관리할 수 있는 기능을 2018 년에 개발할 계획입니다. 이를 통해 관리자가 근무일정 캘린더와 직원들의 근무 가능 시간 엑셀 사이에서 왔다 갔다 참고하며 스케줄하는 시간을 절약하고자 합니다. 시프티에서 발송하는 업데이트 소식 이메일을 자주 확인해주세요.2. 스케줄하기모든 직원의 한 달 치 근무 가능 시간을 수집한 후에는 시프티 스케줄러를 이용하여 다음 기간에 대한 근무표를 계획할 수 있습니다. 현재, 시프티는 다수 근무일정을 일괄적으로 스케줄하는 방법이 두 가지 있습니다. 이 두 멀티스케줄 기능을 사용하기에 앞서 근무일정 템플릿(들)을 생성하기 바랍니다.예) 근무일정 템플릿템플릿명지점직무근무 시간오픈강남역점바리스타6:30 am - 1:00 pm미들강남역점바리스타12:45 am - 9:00 pm마감강남역점바리스타7:30 pm - 11:00 pm............위 예시처럼, 직무별 근무일정 템플릿들을 모두 생성하였다면, 다음 두 가지 방법의 멀티스케줄을 이용하여 일정을 계획해보세요.A. 다수 템플릿 멀티스케줄직원이 여러 가지 근무일정을 변칙적인 날에 근무할 일정들을 일괄적으로 생성합니다. 한 직원을 선택한 다음 각 템플릿을 선택하여 달력의 근무할 날들을 선택합니다.  B. 다수 직원 멀티스케줄일정한 근무일정을 가진 다수 직원들의 일정을 일괄적으로 짜는 기능입니다. 다수 직원을 선택하고 각 직원에 대한 템플릿을 선택한 다음 근무할 날들을 선택합니다.“시프티 스케줄러가 없다면 많은 관리자들은 근무표 엑셀을 붙잡고 컴퓨터 앞에 종일 앉아있어야 할 지도 모릅니다. 시프티는 당신의 고통을 충분히 느낍니다. 이제 시프티를 시작해보세요.” 3. 직원에게 공유하기다음 달 일정이 확정되면 근무표를 공유하기 위해 어떤 채널을 이용하고 있나요? 스크린샷을 찍어 이메일로 보내고 있나요? 문자 또는 채팅방에서 보내나요? 직원들이 실제로 그 근무표를 숙지했는지는 어떻게 알 수 있을까요? 시프티를 통해 자동으로 직원에게 근무일정을 공유할 수 있으며 변경사항은 실시간으로 반영됩니다. 당신의 인생에서 더이상의 근무표 스크린샷은 없습니다.4. 플랜 B직원을 관리 할 때 항상 예외와 우발적 변동이 있습니다. 때로는 인력 부족으로 레스토랑 운영이 재앙에 빠질 수도 있습니다. 직원의 근무 가능 시간을 수집하여 우발적인 상황에 대비하세요.직원 스케줄링을 위한 절차 수립은 비즈니스 운영에 있어 필수입니다. 오늘날도 이러한 프로세스가 없어 관리자와 심지어 사장까지도 어려움을 겪는 경우가 많습니다. 직원들의 근무표를 계획하고 알맞은 인원을 할당하는 것은 그렇게 힘든 일이 아니어야 합니다. 위의 절차를 따라해 보고 차이점을 느껴 보시기 바랍니다!시프티 출퇴근기록기로 직원들의 출퇴근을 기록하는 것도 잊지 마세요.#시프티 #고객가치 #핵심가치 #기업소개 #서비스소개
조회수 1948

나는 이쁜 데일리룩을 보고 싶은걸? \w pose estimation

안녕하세요. 스타일쉐어 백엔드 개발자 김동현입니다.2018년의 스타일쉐어에서는 뷰티, 중고 그리고 데일리룩이라는 피드가 추가로 등장했는데요, 그중 제가 작업했던 데일리룩 피드를 만들게 된 배경과 개발 방향에 대해 공유드리고자 합니다.스타일쉐어 데일리룩#데일리룩 #ootd / 타자 치는 것은 귀찮아데일리룩에 관련된 스타일들만 뽑아내는 방법 중에 가장 간단한 방법은 텍스트로 분리해내는 방법이었을 것입니다.하지만 #데일리룩 #ootd는 사진이나 내용이 관계가 없더라도 들어가 있는 경우가 많았습니다.또한 위의 피드처럼 정성스러운 글을 써주는 유저도 많긴 했지만 자신의 데일리 로그를 남기면서 글을 작성하지 않는 경우도 더러 있었습니다.즉, 단순히 텍스트로만 구별해내기에는 이미지에 대한 질을 확신할 수 없었고, 텍스트가 주된 서비스가 아니다 보니 설명 없는 좋은 이미지들이 많았는데요.우리는 이 이미지들을 놓치고 싶지 않았습니다.그래서 결과적으로 텍스트 대신 이미지를 사용하는 방향을 선택하게 되었습니다.이미지로 어떻게 구별해낼까?다행히도 R-CNN의 높은 인식률과 Pre-Trained 된 모델의 label 중 person이 이미 학습되어있던 터라 별도의 Transfer Learning 없이 이미지 내에서 body parts가 있는지 없는지 찾아내는 것은 아주 어렵지 않았습니다.다만 문제가 있다면 body parts에 들어가는 모든 부분을 person이라고 예측하던 부분이었죠.예를 들자면 아래와 같습니다.다음과 같이 제가 사용한 모델에서는 body parts를 person이라는 라벨로 처리하고 있었습니다.단순히 R-CNN의 person 라벨만을 믿기에는 의도했던 데일리룩 외에도 너무나도 많은 것들이 데일리룩이라는 이름으로 필터링될 것 같았습니다.그래서 또 다른 필터가 하나 더 필요하다는 생각이 들었습니다.Pose EstimationBody Parts 중 우리가 원하는 부분이 사진에 있으면 좋겠다!라는 생각을 곰곰이 하다 보니 우연히 머릿속에 스쳐 지나가는 하나의 장면이 있었습니다.Source: http://graphics.berkeley.edu/papers/Kirk-SPE-2005-06/바로 3D 모델링 중에서 Motion Tracker 에 관련된 장면이었는데요. 이것을 Tracker가 아니라 이미지에서 stick figure를 뽑아낼 수 있으면 되지 않을까?라는 생각이 들었습니다.놀라운 딥러닝의 세계에는 이미 여러 명의 Stick Figure를 뽑아낼 수 있는 경지에 도달해 있었습니다.Source: https://github.com/ZheC/Realtime_Multi-Person_Pose_EstimationPose Estimation 딥러닝 모델을 사용하여 아래와 같은 결과물을 얻어낼 수 있었는데요.이미지 내의 Body Parts의 존재 여부를 알게 되었으니 우리가 원하는 Body Parts가 이미지 내에 있는지 검사할 수 있게 되었습니다.하지만 해당 모델이 마냥 가볍지는 않았기에 사용자의 업로드가 많은 순간에는 예측 Task가 밀리기 시작했습니다.그래서 아주 단순하지만, 효과적인 아이디어들을 적용하였는데요.pose estimation을 하기 전에 R-CNN을 돌린 후 person으로 예측된 bounding box가 있다면 pose estimation 모델을 돌리도록 했습니다.하지만 위의 필터를 통했음에도 원하는 결과물이 안 나오는 경우가 종종 있었는데요.바로 다음과 같은 경우입니다.생각보다 작은 사람의 stick figure도 잘 추출 내어서 해수욕장으로 떠나 찍은 사진 속의 저 멀리 있는 휴양객을 데일리룩으로 잡는 일이 종종 발생했거든요.그래서 위의 조건에 더불어서 person이라고 예측된 bounding box size가 전체 이미지 크기 대비 n % 이상의 크기 일 경우 Pose Estimation을 진행하자는 것이었죠.적당한 크기 이상의 데일리룩들을 뽑아내고 싶었고 사람이 너무 작아서 안 보이는 경우도 피할 수 있었습니다.빠른 분류 속도는 덤이었고요.덕분에 유저들이 올린 콘텐츠 중 데일리룩이라는 범주에 속하는 콘텐츠를 잘 뽑아낼 수 있었습니다.아래는 위의 과정을 거쳐서 Pose Estimation까지 처리되어 데일리룩 사진이라고 판별된 이미지입니다.이다음으론 무엇을 더 해볼 수 있을까요?사진 속의 자세를 알 수 있게 되었으니 좀 더 재밌는 것을 할 수 있을 것 같은데요.예를 들면 K-Means를 적용하면 비슷한 모습의 데일리룩들만 모아볼 수도 있고 스타일쉐어 유저들이 자주 찍는 자세 라던가 유저 별 자세 선호도 등등 재밌는 것들을 할 수 있을 것 같습니다.날 따라 해 봐요 같은 것도 해볼 수 있겠네요 :)같이 해보지 않을래요?아직도 재밌는 것들이 많이 남은 스타일쉐어 에서는 더 많은 것을 하기 위해 개발자분들을 모시고 있습니다 :)백엔드 개발자라고 해서 백엔드 개발에만 국한되지 않고 하고 싶은 것들을 해도 된다, 할 수 있다고 이야기해 주는 회사라고 생각합니다.스타일쉐어를 좀 더 알고 싶으시다면 여기를 눌러 주세요 :)#스타일쉐어 #개발팀 #개발자 #백엔드개발 #개발인사이트 #경험공유 #후기
조회수 842

기획, 마케팅, 브랜딩은 막 막 대단히 멋진걸까?

오늘의 얘기는 다소 불편한 얘깁니다.비판적이고 불편한데다 불만가득한 말투가 다수 있습니다. 그리고 지엽적이고 개인적인 의견이니, 혹시라도 기획/마케팅/브랜딩이 너무도 멋있고 환상적이어서 우리오빠 욕하는 건 절대 못들어줄 마브기(마케팅,브랜딩,기획) 팬덤이시라면 뒤로가기를 눌러주시면 감사하겠습니다.어쩌다보니 기획자와 마케터와 브랜딩하는 사람(이건 뭐라고 해야할 지 모르겠다. BM이라고 해야하나..?)들이 주변에 많더라구요. 또는 그것을 꿈꾸는 취준생, 대학생, 신입사원, 이직희망자가 우글우글 합니다. 그들의 말을 들어보면 '마케팅해요.' 라는 말만 들어도 막 그 사람의 조언을 들어야 할 것 같고 너무 멋지다는 겁니다. 또는 나는 디지털마케팅학과를 나왔으니 벌써 마케터라고 하더라구요. 그 사례와 이유를 접어두고서라도 확실히 마케팅, 브랜딩, 기획은 그 단어자체가 주는 강렬함이 있긴 한가봅니다. 잘 모르겠고 어렵고 광범위하고 추상적인 단어들이죠. '기획,마케팅,브랜딩' 이란 단어는 잘못이 없습니다. 애시당초 그렇게 태어난 단어고 마케팅이 마케팅이지 더 뭘 설명해야 할까요. 그런데 가만보니 정확하게 정의되지 않는 단어들에는 항상 거품과 허풍이 끼기 마련입니다. 사짜들이 판치거나 갈등을 조장하는 말장난이 되기도 하죠. 그 중 오늘 제가 말하고자 하는 부분은 3가지 부류의 사람들입니다. 1. 입만 졸라 살아있는 부류2. 내 경험이 짱인 부류3. 뭐만 하면 강의만 나가려는 부류이런 분들은 주로 사내의 상급자이거나, 또는 컨설팅하러 오신 외부인력이거나 그냥 강사거나, 자문위원이거나 꼰대투자자거나 무슨 대표님 내지는 레퍼런스 좋은 지나가던 사람일수도 있겠네요. 우선 기획과 마케팅과 브랜딩이 어떤 건지 크게 정리해보고 위 부류의 사람에 대한 얘기를 해보겠습니다.기획자그 전에 기획/마케팅/브랜딩이 뭔지 일단 간략하게 제 의견을 얘기해보자면 이렇습니다. 기획은 논리를 구축하는 겁니다. 문제발견과 해결, 과정 등등 뭐 여러가지 정의가 있지만 어쨌든 궁극적으론 눈에 보이지도 않고 손에 잡히지도 않는 미래의 불확실한 어떤 것에 논리를 부여하는 역할을 합니다. 텍스트든 그림이든 피피티든 바디랭귀지든 외계어든..어떠한 수단을 써서 모두가 이해하고 인식할 수 있는 논리구조와 그림을 그려냅니다. 마케터마케터는 설계의 역할입니다. 고객이 우리에게 다가오고 나가고 다시 돌아오고 친구를 데려오고 구매하고 환불하고 불평하고 해결하고 가입하고 탈퇴하는...모오오오든 행동이 원활하게 이루어질 수 있도록 그 길을 설계하고 확장하는 역할을 합니다. 일회성 폭탄설치 전문가가 아닙니다. 한 번 빵 퍼뜨리고 뒤에 숨어서 나 이거 잘했지?!?! 라고 평생 울궈먹는 그런 게 마케팅이 아니라, 앞으로도 꾸준히 당신이 없어도 굴러갈 수 있게 만드는 겁니다. 브랜딩브랜딩은 사실 모르겠습니다. 전 브랜딩 전문가도 아니고 그런게 있는 지도 모르겠고 있어도 하고싶지도 않습니다. 브랜딩은 필연적이고 자연스럽게 발생하는 패시브성향의 리소스입니다. 우리가 하는 행동과 말과 보여지는 것, 회사내부의 문화 등..내외의 수많은 요소들이 만들어내는 정체성 그 자체입니다. 굳이 따지자면 '가이드' 의 역할같긴 합니다. 요소가 많다는 것은 각각의 것들이 다양한 방향성을 지닌다는 얘기인데 당연히 난장판이 될 위험이 높습니다. 때문에 일정한 톤과 규칙을 설정하여 일괄적인 스토리와 가이드를 제시하고 지키는 일종의 '내규'와 같습니다. 정답이 아니겠죠 당연히. 단순히 제 생각일 뿐입니다. 하지만 그 정의가 어쨌든 간에 기획자, 마케터, 브랜딩하는 사람에게 필요한 것이 무엇인지는 정확히 알고 있습니다. 그들에게 필요한 건 비용, 시간, 일머리입니다. 통찰력, 논리력, 소통능력 뭐 지겹게 얘기하긴 하는데... 그건 역으로 말하면 해당 기획과 마케팅전략과 브랜드전략의 실패가 니들이 멍청해서 그런거야. 라고 책임전가하는 느낌 아닌가요? 회의실에서 전략이 멋드러지게 나오는 건 사실 첫 단추에 불과합니다. 그런 전략으로 일이 다 될 것 같았으면 똑똑한 양반들만 모여있다는 국회에서 그런 법안들이 나오지 않겠죠. 실제 일을 하는 사람들은 보지도 않고 통찰력 얘기만 주구장창 하고 있으면... 너무 웃기잖아요 이거. 너무 많은 강의장에서 인사이트 인사이트만 외치고 있더라구요.물론 내부의 문제도 있어요. 기획하고 싶다, 행사하고 싶다, 브랜딩하자라고 해놓고 전체예산은 200만원 툭.. 내일까지 만들어. 그것도 이제 갓 들어온 신입사원에게.이게 지금 진짜 기획과 마케팅, 브랜딩에 관심있고 의욕있는 사람들의 애티튜드인가요? 물론 회사가 돈쓰고 사람쓰는 것에 민감하고 어려운 것은 잘 알고 있습니다. 적어도 그런 상황이라면 실무를 도와주던가, 적어도 방해를 하질 말던가 아니면 생색을 내서는 안됩니다. 오늘 얘기할 3가지 부류의 사람들은 제3자든, 내부인원이든 비용/시간/일머리(개인차)라는 요소를 제쳐두고 우주를 항해하는 추상적인 단어들로 무장해선 실무자의 고민을 식은 게살죽 정도로 만들어버리곤 하더라구요. 매년 같은 소개서를 계속 만들고 맥락도 뭣도 없는 페이스북 콘텐츠가 끊임없이 나오는 이유이기도 한 것 같아요. 스터디와 회의, 도서구매는 끝도 없지만 일하는 사람의 환경과 업무체계는 1도 바뀌지 않는 이유이기도 하구요. 마브기는 물론 통찰력과 구성능력, 스토리텔링능력 뭐 그런 것들이 있어야겠지만.... 가장 중요한 건 일단 사람이 있어야 하는 거 아닐까요.입만 살아있는 사람들은 일을 헛돌게 만듭니다.그들의 지식을 무시하는 것은 아닙니다. 그들은 브랜드가 몇 년전에 어떤 기원을 통해 만들어졌고 어떤 전략적 이론이 있고, 소비자심리가 어떤 식으로 움직이는 지 심리법칙을 읊어가며 브랜드 스토리텔링 전략을 구축합니다. 구글, 아마존, b8ta, 로하코, 애플 등등의 유수기업의 레퍼런스를 들며 끊임없이 '예를 들면, 예를 들면..' 하는데... 그래서 그 브랜딩전략을 200만원으로 어떻게 하는데요? 단어가 추상적이고 정의가 많아질수록 종교의 종파처럼 각자 교리를 주장하고 외치는 사람들이 많아집니다. 해석과 논란의 여지가 있으니 각 종파를 수호하고 따르는 추종자들도 있고 서로 대립하고 싸우고 내가 맞다 니가 틀리다 어쩐다.. 그런식의 에너지소모가 소위 브랜드 전문가라는 분들 사이에서 꽤나 있더라구요. 고상하게들 댓글로 싸우시거나 뒷담들을 까시는데 그래서 구글말고 우리 회사 브랜딩 어떻게 해줄거냐구요. 지금 실무자 2명있고, 다음 달에 한 명 퇴사해요. 1명 남는데 전 3분기 업무가 폭풍이에요. 대표님은 이번 시리즈B 투자받느라 뛰어다니고 디자이너는 IR만드느라 바빠요. 이거 어떡하냐구요.내 경험이 짱인 사람들은 일을 복잡하게 만듭니다.대기업에서 브랜딩/마케팅/프로젝트 기획해서 결과를 냈다는 건 확실히 굉장히 내공과 레퍼런스입니다. 스타트업에서 제로베이스를 그럴싸한 네임드 브랜드로 만든 것도 굉장한 일이 아닐 수 없습니다. 모두 인정받고 존중받아야 할 놀라운 능력입니다. 하지만, 그건 당신이 멋진 사람이라는 증거일 뿐이지 이번에도 똑같이 잘할 수 있다라는 것의 근거라거나, 또는 상대를 무시해도 된다는 얘기는 아닙니다. 강의다니느라 정신없는 사람들은 일을 안합니다.일 안하세요?주변에 이제 갓 취업준비생이거나 또는 이직자중에서.... 부쩍 참 요즘 마케터가 엄청나게 많아졌다는 생각을 하게됩니다. 기업이 많아지고 그만큼 중요도가 높아져서 그런 것이라고 생각하고 싶습니다. 기획자를 꿈꾸는 사람들도 겁나 많습니다. 브랜드 전문가님들도 셀 수 없이 많아져서 우리나라는 막강한 브랜드 기획력을 지닌 국가가 될 것 같습니다. 전문가가 많아지든 말든 솔직히 별로 신경쓰고 싶지 않습니다. 제가 지난 위클리에서도 그렇고 이전 매거진에서도 브랜드 관련해서 끊임없이 했던 말은...결국 지금 하고 있는 걸 잘하세요. 라는 것이니까요. 마치 기획/마케팅/브랜딩만 잘하면 회사가 완전 대박날 것 같이들 얘기하는데, 얘네들은 로또가 아닙니다. 자기계발만으로 해결될 문제두 아니구요. 말장난으로 멋짐을 포장한다고 해서 해결될 문제도 아닙니다. 시스템과 문화 자체가 바뀌어야하죠. 우리가 소위 찌질하다고 여기는 돈문제, 일문제, 계약서 등등부터 말입니다. 이런 것들을 제대로 직시하기 위해선..말거품부터 걷어낼 필요가 있다고 생각해요.경영하는 입장이라면 예산 늘려주고 시간 넉넉히 주고 사람 뽑아주세요. 지금은 그렇게 많은 비용을 쓸 수 없다라면 일도 거기에 맞춰서 만들어야 하는거죠. 100만원을 주고 1,000만원 어치의 아웃풋을 강요하면 안되는 거예요. 쓸데없는 일 좀 쳐내고 필요한 장비도 잘 챙겨주고, 뭔가 계약을 했으면 지키고, 맡겼으면 믿으세요. 그리고 브랜드 가이드에 이렇게 쓰라고 했으면 잘 지켜서 쓰셔야 할 것 같아요.실무자입장이라면 기획을 할 때는 소설이나 에세이처럼 쓰지말고, 잘 나가는 카드뉴스 베껴서 대강 만드는 거 아니고, 굿즈샘플은 귀찮더라도 내 눈으로 보고 손으로 만지면서 고르는 바쁨과 고민을 필요해요. 자기 과거 레퍼런스만 믿지말고 신중하고 디테일하게 일하는 거예요. 책상앞에서 모든 걸 다 할 수 없습니다. 전문가입장이라면 상대가 지불한 비용만큼 시간과 노고를 줄여주세요. 말만 하지말고 실제로 어드밴티지를 가져다 주셔야죠. 팔짱끼고 손가락으로 이거저거 하라고 지시만 하는 게 전문가는 아닐거예요.무엇보다 발이 바쁘고 몸이 뛰어다녀야 하는 노가다 3대 직종이 마브기가 아닐까합니다. 이건 일이예요. 이상하게 자꾸 브랜딩 이런거에 큰 의미를 부여하는데...그것들은 분명 가치있고 중요한 일이지만(모든 일이 다 그렇듯), 그렇게 '멋지기만 한' 일이라고만 부를 수 있을지는 모르겠네요. 우리가 멋지다고 외치는 만큼 마케팅/브랜딩/기획에 비용과 시간, 노력과 관심을 들이는지도 잘 모르겠구요. 혹시 그저 말로 잘 포장된 채 사전적 정의로만 빛나고 있는 환상을 동경하고만 있지는 않는 건지 한 번쯤 생각해 볼 필요가 있을 것 같아요.
조회수 4526

대화의 끈을 이어가는 비법

"요새 잘 지냈어?""어? 어어...별 일 없어.""......""......""......""......" 소설의 한 장면이 아니다. 살면서 누구나 한 번쯤은 겪어봤을 '대화'의 시간이다. 뭔가 말은 해야 하는데, 할 말은 안 떠오르고, 손톱 옆에 난 거스러미를 잡아 뜯을까 했다가, 괜히 상대가 입은 옷의 무늬를 쳐다봤다가, "이야 오늘 날씨 좋다 그지?"라고 말해볼까 하고 하늘을 봤는데 기온은 37도를 찍고 있고, 괜히 아무 말이나 하면 더 어색해질까 더 말을 꺼내지 못하는, 그런 상황. 이럴 때는 아는 사람 한 명이라도 좀 지나가면 좋으련만. 나는 이런 '어색한 침묵'이 세상에서 가장 힘들다. 말을 할 거면 하고, 말 거면 말고, 그것도 아니면 어디 좀 딴 데로 가던가, 이도저도 안 되겠다 싶으면 차라리 어디 편의점 가서 군것질이라도 좀 하다 오던가.  커뮤니케이션을 힘들어하는 사람은 생각보다 많다. 그리고 그게 뭐 그리 큰 잘못인 것도 아니다. 세상 사람이 모두 달변가였다면 세상은 상당히 시끄러웠을 테니까. 하지만, 이제 마음맞는 팀을 찾아 새로운 마음으로 가열차게 일을 하려고 하는 당신에게 사람을 어색하게 만드는 신비한 재주가 있다면 매우 난처한 일일지도 모른다. 사실 나도 그 과자 좋아하는데. 사실 나도 그 아이돌 팬인데. 맞아 맞아 나도 그런 적 있었는데...나중에 다 지나서 한 마디 해볼 걸 하며 후회해 봐야 이미 버스는 떠난 뒤다. 이 글은, '노력하라!'라던가, '이대로만 따라하면 당신도!'라던가, '이 글은 17세기 영국에서 시작되어..'같은 사기꾼이나 할 법한 말을 하지는 않는다. 다만 대화가 잘 이어지지 않고 막힐 때, 정말 그러기 싫은데 어색한 순간이 찾아올 때, 정말 난감한 그 상황에서 뭐라도 돌파구를 찾고싶은 그 때, 윤활유를 조금 쳐서 삐걱대는 소리가 조금이라도 덜 나게 할 수 있는, 말하자면 며느리가 시어머니 몰래 1/4스푼 집어넣은 MSG가루같이 아주 작은 의미의 비법이다. 새 직장, 새 사람들에 어떻게 적응해야 하나 잠 못 이루는 사람이라면 조금은 도움이 될 지도 모르는.1. 받았으면 던지자. "밥 먹었어?"라는 질문에 보통 "어."라고 대답하는 당신, 좋아하는 사람에게 "식사 하셨어요?"라고 물었는데 "네."라고 대답을 들으면 무슨 기분일까 생각해 보자. 십중팔구 '저 사람 철벽친다', '애인 있나봐' 같은 온갖 상상의 나래를 펼치게 될 것이다. 그리고 더 이상의 대화를 포기하게 될 것이다. 무뚝뚝한 사람들은 말수가 적은 게 아닌 경우가 의외로 많다. 경험상, 질문을 하면 꼬박꼬박 대답은 다 해준다. 그 대답이라는 게 죄다 단답형이라서 문제지. 이런 사람들과 얘기하면 보통 '벽하고 얘기하는 것 같아'라는 생각이 들게 된다. 밥 먹었니, 잘 지냈니 같은 말은 정말 당신이 밥을 먹었는지, 잘 지내는지가 궁금해서 던지는 말이 아니다. 대화의 시작을 당신과 하고 싶다는 강력한 시그널이다. 그 상황에서 당신이 질문에 답을 주어야 겠다고 생각하는 순간, 또 어색한 침묵이 찾아올 뿐이다. 해법은 간단하다. 상대방이 던졌으니, 내가 받아서, 다시 던지면 된다. 커뮤니케이션을 캐치볼에 비유하는 건 그래서이다. 항상 말의 끝에 물음표를 붙여서 돌려준다고 생각하자. 밥을 먹었는가 물어보면 상대방도 먹었는가 궁금해하고, 잘 지냈는가 물어보면 너도 잘 지내느냐 하고 돌려주면 된다. 전혀 어려운 일이 아니다.*그렇다고 이렇게 던지면 큰일난다.2. 관심을 주자. 보통 받은 말을 다시 던져주면 새로운 화제가 하나쯤 튀어나온다. "요새 정말 힘들다."라던가, "나도 밥 먹었어."라던가, 정말 고민이 많아 누구한테라도 하소연하고 싶은 사람은 바로 "알잖아, 나 저번에 아는 형한테 사기당한 거..."같은 말을 꺼내기도 한다.  여기서 조금만 더 관심을 주면 된다. 힘들면 왜 힘든지, 먹었으면 뭘 먹었는지 물어봐주면 대화의 소재는 무궁무진하다. 11층 김밥헤븐에서 라면을 먹었다는데 거기는 무슨 메뉴가 맛있는지 물어보면 또 무엇인가가 나올 거고, 그럼 그걸 잡아서 또 대화를 이어나가면 된다. 생각보다 많은 것을 이끌어 낼 수 있다. 이쪽에서 받아 던졌는데 저쪽에서 던져주지 않는다면 내가 하나 더 던져주면 된다. 작은 관심을 기울여 질문 하나를 생각해 내는 건 어렵지 않다. 물론 두 번이나 세 번쯤 물어봤는데 반응이 미적지근하다면 당신과 이야기하기 싫어진 것이거나 생각해 낸 질문이 부적절했기 때문이니까 조용히 어색함을 즐기면 된다. 중요한 건 연습이다. 자꾸 하다보면 어떻게 이끌어 낼 수 있을 지 감이 올 것이다.*꼬치꼬치 캐묻진 말자. 많으면 3개까지!3. 리액션 좀 해줘요! 영화 터미네이터 2를 보면 T-800과 T-1000이 나온다. 설마 안 본 사람은 없을 거라는 가정 하에 예시를 들자면, T-800과 T-1000 모두 기계이기 때문에 인간의 감정을 느낄 수 없는 무미건조함을 드러내고 있다. 하지만 대부분의 사람들은 T-1000보다 T-800을 더 '인간적'이라고 느끼고, 그의 마지막 따봉에 형언할 수 없는 감동을 느끼게 된다. 이 느낌의 차이는 사실 단순하다. T-800은 대사가 많고, T-1000은 대사가 없다. 좐 코너가 순진무구한 표정으로 말을 할 때, 전혀 상황과 맞지 않는 기계적 설명일지라도 여하간 말을 한다. 말을 하면, 말이 돌아온다. 인간미를 느낄 수 밖에 없는 것이다. 반면 T-1000은 죽어가면서조차 말을 하지 않는다. 요새 문구점 아이들에게 그렇게 사랑받는다는 액체 괴물 그 자체인데도 불구하고, 한낱 쇳덩어리에 불과한 T-800보다 더욱 기계적이고, 차가운 인상을 준다. 굳이 오버해서 손뼉을 치고 배를 잡고 뒹굴며 웃으라는 건 아니다. 돈 받고 웃어주는 방청객 알바도 그렇게는 못한다. 다만 상대방이 하는 말을 '듣고', '상대방의 주제에 맞는' 대답을 해주기만 하면 된다. 날씨가 덥다고 하면 요새 날씨가 너무 더워서 올해 처음으로 에어컨을 틀었다던가, 나는 더위를 잘 안 타는 체질이라 오히려 겨울이 더 힘들다던가, 뭐든 좋다. 중요한 건 '나는 네 말을 제대로 듣고 있다'라는 시그널을 주는 것이다. "밥 먹었어?" 라는 말에 "어. 요새 힘들지?"라고 답하는 사람이 있다면 터미네이터보다 인간미가 없는 자신에 대한 반성을 하기 바란다. 인간은 복잡한 사고를 하는 동물이라 무조건 원패턴으로 대답하면 바로 알아챈다. 어디까지나 내가 들은 말에 대한 반응을 해주는 것이 중요하다.*실로 인류 역사에 길이 남을 따봉이 아닐 수 없다.4. 대화는 말로만 하는 게 아니다. 다시 터미네이터 2로 돌아와서, T-1000이 가장 섬뜩하게 느껴지는 순간이 있다. 손가락 하나를 세워들더니 좌우로 까딱까딱 흔드는 장면이다. 여태껏 머리에 산탄총을 맞고 불에 지져지고 쇠창살을 스르륵 통과하고 그렇게 비인간적인 모습들을 보여줄 때는 그저 사람의 모습을 하고 있는 괴물로 느껴졌다면, 이 제스처 하나로 그 괴물이 인간의 영역에 들어오기 때문이다. 비언어적인 대화수단의 힘을 한 장면으로 느낄 수 있는 씬이다. 손짓, 발짓, 몸짓, 표정 등, 우리는 생각보다 많은 비언어적 커뮤니케이션을 활용한다. 대화하기 힘들다고 평가받는 사람들은, 이런 수단을 거의 활용하지 않는 경우가 많다. 기껏해야 어색한 미소를 짓거나, 거절할 때 두 손을 내밀어 흔드는 정도를 사용하는 수준에 그친다면 또 한 번 반성하자. 원숭이도 그것보단 많은 제스처를 사용한다. 상대가 썰렁한 농담을 할 때 일순 정색하는 표정을 지어보인다던가, 말할 때 손짓을 크게 한다던가 하는 수준까진 아니어도 좋다. 표정만이라도 조금 풍부하게 지어보자. 왠지 웃긴 이야기를 하는 것 같으면 억지로라도 조금 미소를 지어주고, 힘든 일을 토로하는 것 같으면 눈썹을 내리며 아픔에 공감하는 표정을 지어주자. 얼굴 근육은 안 쓰면 굳는다. 처음에는 정말 눈 뜨고 봐주기 힘든 미소를 짓고 있겠지만, 하다보면 자연스레 상대의 말에 진심으로 반응하는 것처럼 보이는 미소를 지을 수 있게 된다. 진심으로 그런 표정을 지어줄 수 있으면 더욱 좋겠지만, 그게 가능했다면 당신은 여기까지 읽어 내려올 정도로 커뮤니케이션에 서투른 사람은 아니다. 의식적으로 연기하는 것이더라도, 안 하는 것 보다는 낫다.*어....이 정도로 연습할 필요는 없다.5. 최고의 대화는 많이 듣는 대화이다. 좋아하는 주제나 꼭 말하고 싶었던 것들로 화제가 옮겨가면, 평소에 조용하고 커뮤니케이션을 잘 하지 않던 사람도 마치 딴 사람처럼 열변을 토하거나 말을 속사포처럼 많이 하는 경우가 있다. 하지만 항상 잊지 말아야 할 것은, 상대방이 말하는 양의 7할 정도만 말하기로 정해놓는 것이다. 나도 이 부분에서는 늘 자제해야 한다고 생각하면서도 아차 하는 순간 말이 많아질 때가 많다. 물론 상대방도 말하기보다 듣기를 좋아한다거나, 어떤 안건에 대해서 당신의 의견을 상세하게 듣고 싶은 경우는 있다. 이 때는 말을 많이 한다고 해서 문제가 될 것은 없다. 오히려 말을 길게 못해서 문제가 될 수는 있지만. 문제는, 일상적인 대화에서 자기도 모르게 흥분하여 말을 엄청나게 많이 하게 되는 경우이다. 우리는 누구나 내 말을 상대방이 들어주었으면 하고, 남의 말을 듣는 것을 그렇게 달가워하지 않는다. 남의 말을 듣는 것을 정말 좋아하는 사람이 있다면, 세상의 평화를 위해 꼭 학교의 교장선생님이 되어주었으면 좋겠다. 중학교 때 운동장에서 쓰러질 뻔 했다. 여튼, 내가 말하고 싶은 테마로 화제가 전환되었을 때는, 나의 체감상 상대방이 말하는 양의 7할 수준으로 말을 해야겠다고 마음을 먹어야 겨우 5:5 비율이 맞았다. 누구나 좋아하는 것이나 싫은 일에 대해서는 말이 많아질 수 밖에 없기 때문이다. 성공한 커뮤니케이션은 여러 가지가 있을 수 있다. 내 가슴 속에 쌓인 이야기들을 토해내고 싶다면, 있는 말 없는 말 모두 쏟아내었을 때 성공했다고 할 수 있다. 때로 상대방이 분통터지는 경험을 이야기 할 때에도, 같이 수다를 떨며 맞장구를 쳐주는 것이 성공일 때가 있고, 반대로 묵묵히 들어주는 것이 성공일 때가 있다. 감각적으로 느낄 수 있는 부분이다. 그러나 대부분의 커뮤니케이션은, 내가 많이 말하기보다 내가 많이 들어줄 때 좋은 결과를 가져올 때가 많다. 대화의 기본은 경청이다.*아마 올해 최고로 성공한 커뮤니케이션 아닐까? 이 글을 읽고나서, '뭐 이런 당연한 것들을 대단한 듯이 써놨어?'라고 느끼는 사람이 다수를 차지할 것 같다. 만약 그렇다면, 참 다행인 일이라고 생각한다. 필요 최소한도의 대화법을 아는 사람이 많다는 뜻이니까. 잠시 증권사 시절 이야기를 꺼내면, 정말 사람과의 소통에 절망적일 정도로 재능이 없는 동기가 있었다. 대인기피증이 있나 하는 생각이 들 정도로, 오랜만에 연수에서 만나면 최소 1주일 이상 지속해서 얼굴을 봐야 겨우 아침인사 정도나 들을 수 있을까, 나만 보면 몸이 딱딱하게 굳어서 아무 말도 나오지 않는다고 했다. 나중에서야 낯가림이 아주 심하다는 말을 하며 작게나마 웃는 모습이 귀엽던 친구였지만, 결국 직장생활에 적응하지 못하고 퇴사했다. 그리고 영업 일을 하면서 느꼈던 건, 말이 많은 사람도, 말이 적은 사람도 의외로 사람과 진실되게 대화하는 법을 잘 모른다는 것이었다. 그리고, 적절한 타이밍에 적절한 반응을 보이는 것만으로도, 함께 대화하는 사람을 상당히 즐겁게 해줄 수 있다는 것이었다. 팀원들과 친해지는 법에 정답은 없다. 백 마디 말보다 한 번의 행동으로 사람들의 마음을 사로잡을 수도 있다. 그렇지만 '팀'이라면, 최소한의 커뮤니케이션은 성립해야 한다. 하고싶은 말은 많았지만 어떻게 말해야 할 지 몰라 타이밍을 놓친다면, 당신의 진심은 아무도 알아주지 않는다.#더팀스 #THETEAMS #영업 #대기업경험 #커뮤니케이션 #인사이트 #경험공유
조회수 1442

고맙습니다! 창업멤버야!

스타트업을 구성하는 요소 중 가장 중요한 항목이 바로 팀(멤버)이다.창업자가 초기에 가장 고민하는 문제이기도 하다.1인 창조기업이라고진짜 혼자 사업을 하는 경우는 드물다.물론 글을 쓰는 작가,디자인 프리랜서,컨텐츠 제작, 어플 개발 등의특정 분야에서는 1인의 사장님이자 직원이 고군분투하면서사업을 이끌어나가는 케이스가 분명 존재한다.하지만...대부분의 1인창조기업은혼자가 아니라최소 2~3명의 멤버 또는 동료가업무를 분담하고 시작한다.다재다능한 창업자라해도능력의 한계가 있는 법!(출처: 어벤져스 2, 영화 포스터 이미지 중)어벤져스나 저스티스리그와 같은히어로들이 뭉치듯이어떤 영웅도 반드시 결점은 가지고 있고다른 분야의 정통한 프로들이 보완해 줄 때,대의를 이룰 수 있다...랄까?그런 면에서 창업 준비부터함께 동행해주고 있는창업멤버들에게왜 고마운지,왜 더 고마워 질 것인지에 대하여 고백하고자 한다.이야기를 풀기 전에우리 멤버들의 성향을 넌지시 언급하자면,1) 창업자: 연금술사- 필자다. 현자의 돌따위보다는 황금을 만들어내는 것에 더 관심이 있다.2) 창업멤버 1: 황소- 우직한 전형적인 연구원 스타일, 항상 창업자 편을 들어주는 강한 조력자3) 창업멤버 2: 외계인- 매우 이성적이며, 창업자랑 자주 대립하는 무서운 콜드마인드 캐릭터4) 창업멤버 3: 애처가- 둥글게 둥글게 사는 컨셉이라 우리 멤버들 갈등을 조정해주는 중간자 역할1. 창업멤버는 강력한 우군이다.: 실제 사례창업준비 전에 다니던 회사를 그만 둘 결심을 하고무식하게, 정말 단순 무식하게창업을 할거라 말하고 다녔다.필자에게는 다행이었지만지금의 창업멤버들에게는 잘못된 만남이랄까?그 때의 사전 포섭과 귀찮도록 앵기다보니이 분들도 순수하게 믿어주고, 동행하게 되었다.어느 정도 아이템에 대한 구성도 끝나고,타임스케쥴이 구체화되어 꼬드기기 시작했다.여기저기 전화를 돌리고,만나러가서 몇 일을 설득하고결국 2명은 다니고 있던 회사를 관두고 합류,다른 1명은 일단 긍정적인 구두계약(?)을 이끌어냈다.그 때까진 필자가 참 근거 빈약한 자신감으로섣불리 불러들였다는 사실을곧 깨닫게 되었다.사람들 불러놓고 아직 사업등록도 안 했는데창업 자금이 똑 떨어져버렸다.시작도 하기전에...통장잔고가 제로(0).아...월급은 고사하고, 활동하는데 들어갈 비용조차대기 힘들다니....사실 그 때 당시,투자해 주겠다는 정체불명의 중년의 명함만 믿었고,팀 전체를 사고 싶다는 어떤 회사와도 미팅 중이었기에자금은 금방 생길 줄 알았는데...말그대로...정체불명의 중년은 행방불명이 되었고,우리에게 돈을 준다던 회사는 사실 유령회사였다.필자가 참으로 호구 중에 왕호구였다는 걸,참 순진(?)하고, 풋내나는 동네 바보 청년이라는 걸빨리 깨닫게 되었다.이런 어리버리한 리더를 떠나 갈 수 있는 좋은 기회를우리 멤버들은 제대로 못 살렸다.솔직하게 말해서는 다투기도 하고,불안함에 술 마시고, 서로 침묵의 시간을 가지기도 했다.미안하더라.한없이 미안하더라.그런데 우리 중 한 명은필자가 가지지 못 한 능력이 있었다.바로 복기의 능력!우리가 어디서 실수를 했고,무엇이 잘못되었으며,앞으로 어떻게 할지에 대한 타임테이블과 미션을 준비해 왔다.좌절과 후회로 끝날 뻔 한 위기의 순간에동료의 능력은 빛이 난다.창업자는 완벽하지 않기에,결정이 잘못되기도하고,선택이 올바르지 못하기도 하며,상황 파악이 좁을 수도 있다.이럴때,직언을 하고, 수정해주며, 대안을 모색해주는동료의 존재는 선생과도 같다.(출처: TvN 더지니어스 중에서)2. 창업 멤버의 성향은 각자의 개성이 뚜렷해야 한다.팀 빌딩에 앞서, 어떤 성향의 멤버로 구성 할 것인가에 대해많은 고민을 해야한다.많은 창업자들이 기술 또는 능력위주의 팀 구성을 하는데,물론 다양한 기술과 능력이 고루 포진된 팀은 강하다.하지만,창업 멤버의 진짜 힘은어려움에서도 견디고,함께 동행해 주는 끈기와원활한 의사소통에서 나온다.이러한 힘의 근원은바로 개개인의 성향에서 나온다.창업자와 동일한 성향이라면,첫 만남에서야 편할 수도 있지만사업을 수행하면서 오히려 부딪히는 경우가 많다.따라서, 다양한 성향의 구성원을 모으길 추천한다.우리 회사의 경우를 예를 들자면,창업을 준비하면서 팀빌딩에 3가지 성향을기준 삼았다.하나. 창업자의 편이 되어주는 우호적인 동료둘. 창업자와 각을 세워 줄 대립적인 동료셋. 전체를 조율 해 줄 중립적인 동료그리하여 길고 끈질긴 포섭을 통해모인 구성원은 다음과 같다.1) 연금술사(필자)는연구원 출신이지만 사실 안에서 일하기보다는바깥에 뽈뽈거리며 돌아다니길 좋아한다.수다를 좋아하고,꽤 자기주장이 강한 편이다.2) 황소는안에서 일하길 즐기며,고집이 세며, 창업자의 의견에 매우 우호적이다.자신의 전문분야인 연구 쪽으로 특화되며,조용하고, 방해 받지 않는 업무를 선호한다.3) 외계인은독특하고 편집증적인 섬세함을 가지며,자기 주장을 매우 논리적으로 펼친다.우리 회사의 전체를 감독하는데 탁월하며,창업자와 정반대되는 성향이다.4) 애처가는성격이 유하여, 동료 및 인간관계가 원만하다.마찰이 생기면 양보하고 교통정리를 잘 하는 편이다.외부에서도 적이 없고,다양한 인적 네트워크를 가지고 있다.(출처: VIA9GAG.com에서 발췌): 진짜 이런 조원들이 모이면 답이 없다. 정말 답이 없다.다행히도팀빌딩을 위한 나름대로의 미션에 부합되는인물들로 모여서 활동 중이다.창업자와 외계인은 같은 현상, 같은 이슈에전혀 다른 시각과 결과를 도출하여업무에 필요한 잦은 마찰이 생긴다.이 때, 황소는 주로 창업자의 입장을 옹호하며,애처가는 우리 셋을 조율하고, 감정적인 마찰이 안 되도록정리하여 준다.여기서 예상되는 문제는외계인의 고립 또는 창업자의 의견이다수결로 결정되는 상황이다.하지만 생각과는 달리,결과는 항상 다양하게 도출 된다.왜냐하면,내부에서 논의 되는 의견이란게단순하게 창업자와 외계인의양자택일의 의견이 아니라4명이 다수의 선택지를 늘려나가는,보완/수정이 연달아 이루어지는의사결정이기 때문이다.황소와 애처가 역시 다른 인사이트를가지고 있기에 최종 결과는 항상네 명의 시각이 적용되어 도출된다.창업 초기에는 이러한 팀빌딩을 통해빠르고, 보다 나은 결정이 이루어지고많은 성과를 얻을 수 있었다.하지만 지금에와서는더 다양한 분야, 경험의 인사이트를절실하게 찾고 있다.이전에 멘토링이나 컨설팅을활용하는 방법도 어느 정도 한계가 있어현재 내부 전문인력을 더 채용하고 있다.(출처: 피씨컴의 기울어진 공관 티스토리 중에서)3. 창업멤버 영입의 순서순서가 중요할까?필자는 중요하다고 생각한다.우리 회사의 경우는다음과 같은 순서로 영입하였다.연금술사 - 황소 -  애처가 -  외계인(창업자)  (응원)   (중립)   (대립)왜 이랬을까?어쩌면 필자의 쓸데없는 계획성,또는  불필요한 걱정 때문일 수도 있다.창업자를 자동차의 핸들이라고 상상하자.차에 시동을 걸고 달리기 위해서는엑셀레이터를 밟아야한다.창업자와 읏샤읏샤 할 수 있는멤버부터 영입해야우선은 빠르게 일을 시작 할 수 있다.그 다음에 중립적인 멤버는앞으로 영입할 대립적인 멤버와의견충돌을 완충할 사전포섭이며,그 전까지는 약간 느슨한엔진 브레이크 역할을 한다.(완만한 속도 감속의 역할이랄까?)다음 멤버는 대립적인 멤버로,기존의 사업운영 대한 상반된 입장을거리낌없이 주장할 수 있어야 한다.강력한 핸드브레이크 또는 주제동 브레이크와 같다.현재 채용을 진행하는 마케팅과 경영관리 쪽은마치 차의 퍼포먼스와 다이나믹한 핸들링을가능하게 해 줄 것이다.(출처: 제갈토끼님의 트위터 "삼고초려"중에서)4. 팀 멤버 영입을 위한 전략인제를 영입하기 위한나의 명제는 다음과 같다.1) 초기 스타트업에 참여가 꺼려지는 이유(문제파악: 인재 매칭이 안 되는 원인)ㄱ)  99프로가 망하고 1프로가 살아남는 현실ㄴ) 초기 멤버들은 고생을 많이 할 수 밖에 없다.ㄷ) 보수와 보상을 적절하게 집행할 능력이 없다.ㄹ) 회사의 비전과 미션 수행의 미스매칭2) 인재영입을 위한 조건ㄱ) 아이템, 능력, 계획에 대한 확신을 줄 것!ㄴ) 팀구성에 왜 당신이 필요한지 제시할 것!ㄷ) 비전 대비 미션 수행 현황과 계획을선행하여 보여줄 것!ㄹ) 회사와 개인의 비전이 동조되도록협상을 통한 영입 조건을 제시 할 것!따라서, 인재 영입을 위해내가 수행한 전략은 다음과 같다.3) 팀 빌딩 전략ㄱ) 구성할 팀원에 대한 계획 설립: 구체적인 사업계획서와 PT자료를 만든다.: 가용 가능한 자금과 프로젝트 기간을 정한다.: 채용계획을 작성한다.ㄴ) 예비 팀원들을 사전에 접촉: 영입하고자하는 멤버에게  사업계획, 영입계획, 영입조건에 대한프리젠테이션을 한다.ㄷ) 작은 미션들을 먼저 수행: 팀원들에게 리더의 실행력을 보여줌: 계획서 내에서 달성 가능한 초기 목표를창업자가 선수행하여 근거로 제시한다.ㄹ) 개별적인 협상 진행: 팀원 개별적인 목표와 회사 목표를 매칭: 회사의 조건/멤버의 조건 차이를 좁힌다(협상): 멤버의 조건 수준이 높을 수록 의무/책임을 높인다.위의 수행이 절대적으로 정답은 아니다.다만,필자는 계획에 근거하여멤버를 설득하고, 영입할 때,미스매칭을 줄일 수 있다고 믿기에나름대로 실행한 프로세스이다.그리고지금까지는 꽤 좋은 멤버를 얻었고,회사가 꾸준히 성장할 수 있었다.싸우기도 참 많이 싸우고,함께 웃기도 참 많이 웃고 있다.어디에가든,필자는 팀 자랑을 하는 팔불출 대표다.기술력이라던가,수상실적이라던가,제품의 차별성을 내세울 수도 있지만이 모든 것들은강한 멤버가 있어가능했던 Product일 뿐.본질은 사람이다.그래서 감사하고그래서 더 고맙다.창업멤버들에게늘 설명하듯이앞으로 더 고생길이 많이 남았다.새로운 직원을 뽑으면서내부적으로 새로운 마찰이 예상된다.자금을 소진하며 제품 양산에 들어가며이전보다 더 허리띠를 졸라매야한다.야근이나 주말근무는 없지만,더 많은 고민과 프로젝트 완수의 압박은더 심해질 것이다.잦은 이사도 예상된다.잦은 다툼도 예상된다.잦은 실수도 예상된다.그래도 믿는다.우리는 이겨낼 것이라는 것을.우리는 견뎌낼 것이라는 것을.우리는 그때도 함께일거라고...앞으로도더 함께,더 크게성장하자.고마운 창업멤버님들~!최후의 순간까지 낙오하지 말자.영광의 그날까지함께 하자.덧붙이는 말:현실적인 이야기를 하나 하자면,많은 선배 창업자들은가난할 때보다,돈이 생기고, 자금이 풍요로워질때,창업멤버가 흔들리고 떨어져 나간다고 한다.첫 창업멤버가끝까지 함께하는 경우는 없다고.그러니 너무 창업멤버를 신뢰하지 말라고.아직은 믿고 싶지 않지만...그런 날이 올 수도 있다고 생각한다.사실 이전에 몇 번의 이별을 경험한 적이 있다.모두 공감할 상황에 의한 이별,개인적인 사정에 의한 이별,충분한 보상을 못 해주기에 생긴 이별.그렇지만 현실을 부정하려고 노력하고 있다.지금의 창업멤버를 끝까지 믿어주지 않으면,앞으로 난 어떻게 이 길을 갈 수 있을까?설령 지금의 멤버와 이별의 날이 온다해도그 때는 그때의 나와 그때의 멤버와협상할 일이다.지금부터 미리 단정짓고거리를 둔다면,우리의 앞날은의심과 쓸데없는 에너지 소모의 연속이 아닐까?그래서앞으로도나는 창업멤버들에게고마워 할 것이다.아니,고마워 해야 할 것이다.1. 우리 회사 웹페이지를 개설했습니다.http://goo.gl/3BKgZN2.또한, 회사 홈페이지도 올립니다.http://www.urains.com3. 더 많은 지난 글을네이버 블로그에 따로 개시하고 있습니다.http://yoworm.blog.me/원문 : 브런치#클린그린 #창업자 #초기창업 #고민 #초기멤버 #채용 #팀빌딩 #팀원 #팀구성
조회수 1402

레진 기술 블로그 - 자바 기반의 백엔드와의 세션 공유를 위한 레일즈 세션 처리 분석

레일즈 기반의 프론트엔드(브라우저에서 서버 사이드 렌더링 계층까지)와 자바 기반의 백엔드(내부 API와 그 이후 계층)이 세션을 공유하기 위해 먼저 레일즈의 세션 처리 과정을 분석하고, 레일즈 세션 쿠키를 다루기 위한 자바 소스 코드를 공유합니다.여기저기 자랑하고 다녔으니 아시는 분은 아시다시피 레진은 구글앱엔진을 사용하고 있습니다. 지금이야 Java, Python, Node.js, Go 언어와 Flexible Environment 같은 다양한 선택지가 있지만, 레진이 입주할 당시만 해도 Java 7(subset), Python(subset)을 지원하는 Standard Environment라는 선택지 밖에 없었죠.최근 Saemaeul Undong 기술 부채 탕감의 일환으로 자바7, 스프링3.x, JSP(!) 기반의 백엔드에 포함되어 있던 프론트엔드를 레일즈 기반의 프론트엔드 서버(서버 사이드 렌더링을 담당하는 서버는 프론트일까요? 백엔드일까요?)로 분리하고 있습니다.서로 다른 세계의 존재들 - 자바와 레일즈의 세션을 공유해야하는 상황이 문제의 발단입니다.자바와 레일즈의 세션을 공유하는 여러가지 방법이 있겠지만, 가장 단순하고 효과적인 방법은 쿠키(cookie)라고 판단하고, 세션 encrypt/decrypt와 marshal/unmarshal을 동일한 방식으로 맞추기로 했습니다. (백엔드 API를 완전히 stateless하게 새로 만들면 좋겠지만, 코인은 벌어야 소는 키워야죠)이를 위해 레일즈의 세션 처리 과정을 분석하고 정리했습니다.레일즈의 actionpack의 action_dispatch/middleware/cookie.rb를 보면 EncryptedCookieJar 클래스의 초기화 과정은 다음과 같습니다(digest의 경우 따로 지정안하면 SHA1이 사용되는 듯):class EncryptedCookieJar < AbstractCookieJar # :nodoc: include SerializedCookieJars def initialize(parent_jar) super if ActiveSupport::LegacyKeyGenerator === key_generator raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " + "Read the upgrade documentation to learn more about this new config option." end secret = key_generator.generate_key(request.encrypted_cookie_salt || '') sign_secret = key_generator.generate_key(request.encrypted_signed_cookie_salt || '') @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end private def parse(name, encrypted_message) debugger deserialize name, @encryptor.decrypt_and_verify(encrypted_message) rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage nil end def commit(options) debugger options[:value] = @encryptor.encrypt_and_sign(serialize(options[:value])) raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE end end key_generator는 EncryptedCookieJar에 포함된 SerializedCookieJars 모듈에 정의되어 있습니다:module SerializedCookieJars # ... def key_generator request.key_generator end end 흠… 좀 더 파보죠. request.key_genrator는 다음과 같습니다:class Request # ... def key_generator get_header Cookies::GENERATOR_KEY end #... end 흠… 좀 더 파야할 듯 ㅠㅠ.Cookies::GENERATOR_KEY는 다음과 같습니다:class Cookies #... GENERATOR_KEY = "action_dispatch.key_generator".freeze end action_dispatch.key_generator는 레일즈의 엔진 모듈에 해당하는 railties의 application.rb에 정의되어 있습니다:def key_generator # number of iterations selected based on consultation with the google security # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220 @caching_key_generator ||= if secrets.secret_key_base unless secrets.secret_key_base.kind_of?(String) raise ArgumentError, "`secret_key_base` for #{Rails.env} environment must be a type of String, change this value in `config/secrets.yml`" end key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000) ActiveSupport::CachingKeyGenerator.new(key_generator) else ActiveSupport::LegacyKeyGenerator.new(secrets.secret_token) end end # ... def env_config @app_env_config ||= begin validate_secret_key_config! super.merge( # ... "action_dispatch.key_generator" => key_generator, "action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt, "action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt, "action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt, "action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer, "action_dispatch.cookies_digest" => config.action_dispatch.cookies_digest ) end end 너무 깊이 판 느낌적느낌(?)이 있지만, 여기까지 왔으니 좀 더 파보겠습니다.핵심 알고리즘은 activesupport의 key_generator.rb, message_encryptor.rb, message_verifier.rb에 정의되어 있습니다.먼저, key_generator.rb의 핵심은 다음과 같습니다:class KeyGenerator def initialize(secret, options = {}) @secret = secret # The default iterations are higher than required for our key derivation uses # on the off chance someone uses this for password storage @iterations = options[:iterations] || 2**16 end # Returns a derived key suitable for use. The default key_size is chosen # to be compatible with the default settings of ActiveSupport::MessageVerifier. # i.e. OpenSSL::Digest::SHA1#block_length def generate_key(salt, key_size=64) OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size) end end 계속해서, message_encryptor.rb의 핵심은 다음과 같습니다:def initialize(secret, *signature_key_or_options) options = signature_key_or_options.extract_options! sign_secret = signature_key_or_options.first @secret = secret @sign_secret = sign_secret @cipher = options[:cipher] || 'aes-256-cbc' @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer) @serializer = options[:serializer] || Marshal end def _encrypt(value) cipher = new_cipher cipher.encrypt cipher.key = @secret # Rely on OpenSSL for the initialization vector iv = cipher.random_iv encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}" end def _decrypt(encrypted_message) cipher = new_cipher encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)} cipher.decrypt cipher.key = @secret cipher.iv = iv decrypted_data = cipher.update(encrypted_data) decrypted_data << cipher.final @serializer.load(decrypted_data) rescue OpenSSLCipherError, TypeError, ArgumentError raise InvalidMessage end def encrypt_and_sign(value) verifier.generate(_encrypt(value)) end def decrypt_and_verify(value) _decrypt(verifier.verify(value)) end (Hopefully)마지막으로, message_verifier.rb의 핵심은 다음과 같습니다:def initialize(secret, options = {}) raise ArgumentError, 'Secret should not be nil.' unless secret @secret = secret @digest = options[:digest] || 'SHA1' @serializer = options[:serializer] || Marshal end def valid_message?(signed_message) return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank? data, digest = signed_message.split("--".freeze) data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data)) end def verified(signed_message) if valid_message?(signed_message) begin data = signed_message.split("--".freeze)[0] @serializer.load(decode(data)) rescue ArgumentError => argument_error return if argument_error.message =~ %r{invalid base64} raise end end end def generate(value) data = encode(@serializer.dump(value)) "#{data}--#{generate_digest(data)}" end private def encode(data) ::Base64.strict_encode64(data) end def decode(data) ::Base64.strict_decode64(data) end def generate_digest(data) require 'openssl' unless defined?(OpenSSL) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data) end # ... # encode, decode는 base64사용 이제 레일즈가 쿠키 기반의 세션을 어떻게 처리하는지 조금 눈에 들어옵니다. 그러나 우리의 최종 목표는 레일즈의 내부를 공부하는 것이 아니라, 자바에서 동일한 처리를 하는 것입니다. 모듈 의존성 따위는 가볍게 무시하고 무한복붙(?)을 시전해서, 레일즈의 세션 처리 과정을 눈으로 확인할 수 있도록 재구성했습니다:require 'openssl' require 'base64' require 'concurrent/map' class Object def blank? respond_to?(:empty?) ? !!empty? : !self end def present? !blank? end end class Hash # By default, only instances of Hash itself are extractable. # Subclasses of Hash may implement this method and return # true to declare themselves as extractable. If a Hash # is extractable, Array#extract_options! pops it from # the Array when it is the last element of the Array. def extractable_options? instance_of?(Hash) end end class Array def extract_options! if last.is_a?(Hash) && last.extractable_options? pop else {} end end end module SecurityUtils def secure_compare(a, b) return false unless a.bytesize == b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end module_function :secure_compare end class KeyGenerator def initialize(secret, options = {}) @secret = secret # The default iterations are higher than required for our key derivation uses # on the off chance someone uses this for password storage @iterations = options[:iterations] || 2**16 end def generate_key(salt, key_size=64) OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size) end end class CachingKeyGenerator def initialize(key_generator) @key_generator = key_generator @cache_keys = Concurrent::Map.new end # Returns a derived key suitable for use. def generate_key(*args) @cache_keys[args.join] ||= @key_generator.generate_key(*args) end end class MessageVerifier class InvalidSignature < StandardError; end def initialize(secret, options = {}) raise ArgumentError, 'Secret should not be nil.' unless secret @secret = secret @digest = options[:digest] || 'SHA1' @serializer = options[:serializer] || Marshal end def valid_message?(signed_message) return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank? data, digest = signed_message.split("--".freeze) data.present? && digest.present? && SecurityUtils.secure_compare(digest, generate_digest(data)) end def verified(signed_message) if valid_message?(signed_message) begin data = signed_message.split("--".freeze)[0] @serializer.load(decode(data)) rescue ArgumentError => argument_error return if argument_error.message =~ %r{invalid base64} raise end end end def verify(signed_message) verified(signed_message) || raise(InvalidSignature) end def generate(value) data = encode(@serializer.dump(value)) "#{data}--#{generate_digest(data)}" end private def encode(data) ::Base64.strict_encode64(data) end def decode(data) ::Base64.strict_decode64(data) end def generate_digest(data) require 'openssl' unless defined?(OpenSSL) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data) end end class MessageEncryptor module NullSerializer #:nodoc: def self.load(value) value end def self.dump(value) value end end class InvalidMessage < StandardError; end OpenSSLCipherError = OpenSSL::Cipher::CipherError def initialize(secret, *signature_key_or_options) options = signature_key_or_options.extract_options! sign_secret = signature_key_or_options.first @secret = secret @sign_secret = sign_secret @cipher = options[:cipher] || 'aes-256-cbc' @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer) @serializer = options[:serializer] || Marshal end def encrypt_and_sign(value) verifier.generate(_encrypt(value)) end def decrypt_and_verify(value) _decrypt(verifier.verify(value)) end def _encrypt(value) cipher = new_cipher cipher.encrypt cipher.key = @secret # Rely on OpenSSL for the initialization vector iv = cipher.random_iv encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}" end def _decrypt(encrypted_message) cipher = new_cipher encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)} cipher.decrypt cipher.key = @secret cipher.iv = iv decrypted_data = cipher.update(encrypted_data) decrypted_data << cipher.final @serializer.load(decrypted_data) rescue OpenSSLCipherError, TypeError, ArgumentError raise InvalidMessage end def new_cipher OpenSSL::Cipher.new(@cipher) end def verifier @verifier end end #key generate encrypted_cookie_salt = 'encrypted cookie' encrypted_signed_cookie_salt = 'signed encrypted cookie' def key_generator secret_key_base = 'db1c366b854c235f98fc3dd356ad6be8dd388f82ad1ddf14dcad9397ddfdb759b4a9fb33385f695f2cc335041eed0fae74eb669c9fb0c40cafdb118d881215a9' key_generator = KeyGenerator.new(secret_key_base, iterations: 1000) CachingKeyGenerator.new(key_generator) end # encrypt secret = key_generator.generate_key(encrypted_cookie_salt || '') sign_secret = key_generator.generate_key(encrypted_signed_cookie_salt || '') encryptor = MessageEncryptor.new(secret, sign_secret, digest: 'SHA1', serializer: MessageEncryptor::NullSerializer) value = "{\"session_id\":\"6022d05887d2ab9c1bad8a87cf8fb949\",\"_csrf_token\":\"OPv/LxbiA5dUjVsbG4EllSS9cca630WOHQcMtPxSQUE=\"}" encrypted_message = encryptor.encrypt_and_sign(value) #encrypted_message = encryptor._encrypt(value) p '-----------encrypted value-------------' p encrypted_message # decrypt encrypted_message = 'bDhIQncxc2k0Rm9QS0VBT0hWc3M4b2xoSnJDdkZNc1B0bGQ2YUhhRXl6SU1oa2c5cTNENWhmR0ZUWC9zN05mamhEYkFJREJLaDQ3SnM3NVNEbFF3ZVdiaFd5YXdlblM5SmZja0R4TE9JbDNmOVlENHhOVFlnamNVS2g1a05LY0FYV3BmUmRPRWtVNUdxYTJVbG5VVUlRPT0tLXd1akRqOU1lTTVneU9LTWszY0I5bFE9PQ==--b0a57266c00e76e0c7d9d855b25d24b242154070' p '-----------decypted value-------------' puts encryptor.decrypt_and_verify encrypted_message p '---------------------------------------' 이 과정을 자바로 구현한 소스는 생략 깃헙에 올려두었습니다. 이 코드를 이용해서 서블릿 세션과 연동하는 방법은 추후 사측(?)과 협의되는 대로 공유할 예정입니다. 물론, 그 전에 쿠키를 공유할 필요가 없어지면(or 공유할 쿠키가 없어지면) 더 좋겠죠 :D
조회수 1071

주니어 개발자가 외칩니다, "Hello, System Architecture!"

Overview주니어 개발자는 시스템 아키텍처(System Architecture) 또는 시스템 디자인(System Design)이라는 단어에 덜컥 겁부터 먹습니다. 지금 진행하고 있는 개발에만 집중하다 보니 큰 그림을 놓치고 있는 게 아닐까 란 생각이 들었죠. 조금 더 큰 그림을 보고자 공부를 시작했습니다. 문득 같은 생각을 하는 주니어 개발자 분들도 많을 것 같다고 생각했어요. 그래서 이번 글은 시스템 아키텍처에 ㅇ_ㅇ? 뀨? 하는 표정을 짓는 주니어 개발자들을 위해 썼습니다.상상의 나래: 가상의 패션 e커머스상상의 나래를 펼쳐봅시다. 패션 e커머스 서비스를 이용하는 김유저 씨가 구매한 옷이 마음에 들어 상품 리뷰를 남기고 싶어한다고요.김유저 씨는 본인의 착용 사진과 텍스트 리뷰를 작성하고 ‘리뷰 등록하기’ 버튼에 엔터를 탁! 누를 겁니다. 그런데 말이죠. 김유저 씨는 요청하고 싶은 웹서버의 IP 주소를 모르기 때문에 요청을 보낼 수가 없습니다.내 정체를 알려줘: DNS (Domain Name System)그래서, DNS(Domain Name System)에게 물어봅니다. 서버의 도메인 이름으로부터 해당 서버의 IP 주소를 알려주는 것이 바로 DNS입니다. 도메인 이름에 대한 질의를 하고, 만일 해당 도메인 이름이 DNS에 ‘A Record’ 형태로 등록이 되어 있다면 도메인 이름에 해당하는 IP 주소를 응답으로 돌려줍니다.서비스에서 자체 DNS 시스템을 가지고 있을 수 있습니다. 예를 들어 Route 53, Cloud Flare같은 서비스가 있습니다. 그렇다면 또 한 가지 의문이 생깁니다. 왜 서비스는 시스템적 부담을 안고서 자체 DNS 서버를 구축하고 있는 걸까요? 그 이유로 두 가지를 꼽을 수 있습니다.첫 번째로는 신뢰도가 높습니다. 직접 DNS Record를 관리 및 운영하기 때문입니다. 두 번째로는 보안이 우수합니다. 만약 공개하고 싶지 않은 IP 주소, 예를 들어 Database IP 주소 같은 건 공개하지 않습니다. 1)작업장소: Web Server이제 웹서버의 IP 주소를 알았으니 통신을 시도합니다. 웹서버는 웹서비스에서 필요로 하는 다양한 요청과 그에 대한 응답을 제공합니다. 클라이언트가 리뷰에 대한 사진과 텍스트를 등록하고 싶다면 웹서버에게 등록하라는 요청을 보내야 합니다.웹서버에서 요청을 받으면 사용자가 요구한 대로 사진과 텍스트를 등록하고, 그에 대한 결과 정보를 응답으로 보내줄 것입니다. 웹서버 내부에서는 그 과정에 필요한 연산을 수행합니다. 서버 개발자는 이 연산에 대한 코드를 작성하고요.센스가 없는 서버:API (Application Programing Interface)서버는 사람이 아닙니다. 센스나 재치가 없죠. 미리 정의되지 않은 요청은 대응하지 못합니다. (어버버버버 퉤! Error 404!) 그래서 약속한 요청을 보내면 약속한 방식으로 응답해줄게라고 명세를 제공합니다.약속한 요청으로 데이터를 보내면 원하는 요청에서 데이터를 정제해 잘 처리했는지, 또는 처리된 데이터를 약속한 방식(예를 들어, JSON 방식)으로 내보내죠. 웹서버는 정의된 API에 맞춰 요청과 응답을 합니다.그런데 웹서버가 수많은 요청을 받고 응답하면 과부하가 일어날 수도 있습니다. 사용자 수가 어마어마한 규모로 늘어나서 서버가 펑! 하고 터진다면, 김유저 씨는 서비스를 더 이상 이용할 수 없을 겁니다. 이용하고 싶지도 않을 겁니다!따라서, 서버가 감당하는 요청을 나누기 위해 같은 역할을 하는 서버 장비 수를 늘릴 수도 있습니다. 그러면 요청이 각기 다른 웹서버 장비에 분산되어 한 번에 감당할 수 있는 요청 수가 더욱 많아집니다.이 구역의 매니저는 나야: Load Balancer그림처럼 서버가 4대 존재하는 상황이라면, 서버 4대에 일을 적절히 분배해주는 역할이 필요합니다. 그것이 로드 밸런서(Load Balancer)입니다. 로드 밸런서가 서버에게 일을 나누는 방법론은 여러 가지가 있습니다.Random: 랜덤으로 분배하기Least loaded: 가장 적은 양의 작업을 처리하고 있는 서버에게 요청을 할당하기Round Robin: 순서를 정하여 돌아가며 작업 분배하기많이 쓰는 로드 밸런서의 종류는 Layer 4, Layer 7을 꼽을 수 있습니다.Layer 4 Load Balancer: 데이터의 내용을 보지 않고 IP주소 및 TCP/UDP 정보에 따라 단순히 분배를 해줍니다.Layer 7 Load Balancer: 서버가 하는 역할이 분리되어 있는 환경에서 데이터의 내용을 보고 각기 맞는 역할을 하는 서버에게 분배를 해줍니다.로드 밸런서는 클라이언트가 요청을 보내야 할 서버를 골라야 하는 부담을 덜어주며, 로드 밸런서에게 할당된 vIP (가상 IP)로 요청을 보내기만 하면 로드 밸런서에서 알아서 작업을 나눠줍니다. 서버에서는 적절한 로드 밸런서를 사용하면 들어오는 요청이 여러 장비에 분산되어 처리량이 늘어나고 응답 시간이 줄어드는 효과를 기대할 수 있습니다. 컨텐츠 저장소: CDN(Content Delivery Network)이제 웹서버가 클라이언트의 요청에 의해 웹페이지에 대한 응답 결과를 돌려줬습니다. 이때 클라이언트의 화면에 렌더링해야 하는 수많은 이미지가 필요합니다. 이 이미지들을 웹서버가 전부 주려면 데이터의 용량이 너무 크고, 무거워서 서버가 헥헥거리죠. (서버가 죽으면 어떻게 될까요? 클라이언트님이 경쟁사로 환승하겠죠.. 안 돼요..) 따라서 웹서버는 직접 이미지를 주는 대신 CDN(Content Delivery Network)에게 요청하라고 이야기합니다. CDN은 일반적으로 용량이 큰 컨텐츠 데이터(이미지, 비디오, 자바스크립트 라이브러리 등)를 빠른 속도로 제공하기 위해 사용자와 가까운 곳에 분산되어 있는 데이터 저장 서버입니다. 클라이언트는 용량이 큰 컨텐츠 데이터를 가까운 CDN에 요청해 멀리 있는 웹서버에서 직접 받는 것보다 빠르게 받을 수 있습니다. CDN이 동작하는 방식에는 크게 Push CDN, Pull CDN이 있습니다. Push CDN: 서버에서 컨텐츠가 업로드되거나, 변경되었을 때 모두 반영하는 방식 Pull CDN: 클라이언트가 요청할 때마다 컨텐츠가 CDN에 새로 저장되는 방식 두 방식 모두 장단점이 있습니다. Push CDN은 모든 컨텐츠를 갖고 있기에 웹서버에 요청할 일이 없지만 유지하는데 필요한 용량과 비용이 많이 필요하겠죠? Pull CDN은 클라이언트가 요청한 컨텐츠가 있으면 바로 응답하지만 그렇지 않을 땐 데이터를 웹서버로부터 가져와야 하기 때문에 서버에 요청하는 부담이 존재합니다. 컨텐츠명은 그대로인데 내용만 변경되었다면 인지하지 못하고 옛버전의 컨텐츠를 제공하죠. 그래서 Pull CDN에 들어가는 컨텐츠는 TTL(Time To Live)이 적용됩니다. TTL이란 유통기한이라고 생각하면 쉽습니다. 일정시간이 지나면 해당 데이터가 삭제되는 것이죠. 이런 방식이 적용된다면 Pull CDN의 최대 단점을 보완할 수 있습니다. 이렇게 보완이 되면 수정된 데이터에 대해서도 대응이 가능하며 서버의 용량 즉, 비용적 부담이 해소될 겁니다.소중한 내 데이터: Database서비스를 제공하다 보면 클라이언트의 소중한 정보, 이력, 상품 가격, 상품 정보 등 다양한 데이터를 저장하고, 또 제공합니다. 하지만 수많은 데이터를 웹서버에 전부 저장하고 사용하기엔 데이터의 양이 너무 많아 저장 공간도 부족하고, 데이터를 원하는 모양에 맞게 정제하기가 어렵습니다. 그래서 데이터를 저장하는 데이터베이스 서버가 따로 존재합니다.민감한 정보를 다루는 데이터베이스는 ACID라는 성질을 만족해야 하는데요.Atomicity(원자성): 데이터베이스에 적용되는 명령이 중간만 실행되지 않고 완전히 성공하거나 완전히 실패해야 한다는 것을 의미합니다. 반만 적용된 명령이 있다면 헷갈리겠죠.Consistency(일관성): 데이터베이스가 수행한 명령이 일관적으로 반영되어 있어야 한다는 의미입니다. 예를 들어 계좌에 돈을 입금했는데 잔고에 반영되지 않는다면 당황스러울 겁니다.Isolation(고립성): 데이터베이스가 수행하는 명령 도중 다른 명령이 끼어들지 못한다는 것을 의미합니다.Durability(지속성): 성공적으로 수행한 명령은 영원히 그 이후 상태로 남아있어야 한다는 걸 의미합니다. 갑자기 하루 뒤에 명령이 취소되거나 이전 상태로 롤백되면 안 됩니다. Replication (복제 / 이중화)큰 시스템에서는 똑같은 데이터베이스가 여럿 존재한다고 하는데요. 그렇다면 왜 비용적인 부담을 안으면서까지 복제 데이터베이스를 구축해놓는 걸까요? 만약에 데이터베이스가 정상적으로 동작하지 않는다면 클라이언트의 데이터를 변경하지 못하며, 클라이언트가 원하는 정보를 제공하지 못하는 불상사가 일어나게 됩니다. 글로만 써도 벌써 땀이 납니다. 그러므로 복제해놓은 데이터베이스를 얼른 마스터로 등업해 데이터 흐름에 차질이 없도록 대비해야 합니다.만약 하나의 데이터베이스가 어떤 일을 수행할 때 다른 요청들은 계속 기다려야 합니다. 그렇다면 데이터를 변경하는 데이터베이스는 하나, 읽기만 하는 데이터베이스는 여러 대가 존재해도 되지 않을까요? 바로 여기서 Master-Slave의 개념이 탄생합니다.master-slave-replicaMaster-Slave Replica (a.k.a 주인-노예)요청을 분산하기 위해서 데이터베이스를 늘리다 보면 master-slave 토픽이 등장합니다.Mater: CRUD(Create, Read, Update, Delete)가 모두 가능Slave: R(Read)만 가능Master가 데이터를 변경할 동안 읽기에 대한 요청은 Slave에게 보내집니다. 그렇게 하면 읽기 요청은 분산되어 훨씬 더 수월하고 빠른 속도로 데이터 처리가 가능할 것입니다. 만약 Master가 변경된다면 아래 계급인 Slave, Replica 데이터베이스에게도 이 정보를 전해야 합니다. 다시 말해, 자신에게 들어온 요청(Query)을 동일하게 보내 빠른 시간 안에 동기화를 시켜주죠. 하지만 동기화도 시간이 걸리는 작업이므로 무한대로 Slave Replica를 늘려 확장하기는 어렵습니다.Master-Master Replica의문이 하나 생길 겁니다. “여러 대의 Master를 두어서 변경도 가능하고, 읽기도 가능하게 하면 되지 않을까?”앞서 언급했듯이 같은 데이터의 변경 가능한 데이터베이스는 하나여야 할 것입니다. 동시에 같은 데이터를 변경했을 때 갈등을 해소하기 위한 방법론은 존재하지만, 그 방식이 복잡하고 오래 걸립니다. 안정성도 낮아지고, 효율도 떨어집니다. 그래서 Master-Slave 아키텍처를 선호하는 것이죠.Sharding그러면 같은 데이터베이스 테이블을 동시에 변경하는 건 불가능한 걸까요? 그것을 해소하기 위해 샤딩(Sharding)이라는 방법론을 사용합니다. 샤딩된 테이블은 개념적으론 하나의 테이블처럼 보이지만 사실 그 내용물이 쪼개져 있습니다. 쪼개는 방법은 여러 가지 선택할 수 있습니다만, 분명한 건 겹치는 데이터 없이 쪼갠다는 것입니다. 그래서 같은 테이블이어도 쪼개져 있다면 그 테이블에 동시에 접근해 데이터를 변경할 수 있는 것이죠.이외에 서비스별, 기능별로 쪼개어 데이터베이스를 관리하는 Federation 등 많은 데이터베이스 디자인 방법론이 존재합니다.시스템 아키텍처가 가지고 있어야 할 최소본 아키텍처요점: 시스템 아키텍쳐에서 고려해야 할 성질이렇게 간단한 시스템 아키텍처의 면면을 살펴봤습니다. 시스템 개발자라면 시스템을 디자인하면서 반드시 고려해야 할 성질들을 만날 텐데요. 위에서 소개한 내용들 역시 아래의 성질들을 충족하기 위해 탄생했다고 볼 수 있습니다.Scalability (확장성): 10만 명의 요청을 처리할 수 있는 시스템과 1000만 명의 요청을 처리할 수 있는 시스템은 다릅니다. 확장성을 고려한 시스템은 앞으로 클라이언트 수가 늘어났을 때 무리 없이 모든 요청을 처리할 수 있을 겁니다.Performance (성능): 속도와 정확성을 말합니다. 요청한 내용을 정확하고 빠르게 돌려주어야 합니다.Latency (응답 시간): 모든 요청은 클라이언트가 불편해하지 않을 정도로 빠른 시간 안에 돌려주어야 합니다.Throughput (처리량): 같은 시간 안에 더욱 많은 요청을 처리한다면 좋은 시스템입니다.Availability (접근성): 사용자가 언제든지 시스템에 요청을 보내서 응답을 받을 수 있어야 합니다. 비록 서버 장비 한두 대가 문제가 생겨 제 기능을 하지 못하더라도 사용자는 그 사실을 몰라야 합니다.Consistency (일관성): 사용자가 서버에 보낸 요청이 올바르게 반영되어야 하고, 일정한 결과를 돌려주어야 합니다. 요청을 보낼 때마다 불규칙한 결과를 돌려준다면 믿을 수 없는 서비스가 될 것입니다.결론발로 그렸나 싶을 정도의 그림과 기나긴 글을 마무리 지으며주니어 개발자로서 시스템 아키텍처를 공부하면서 느낀 점이 있다면 시스템에 대한 완벽한 대응은 없으며, 모두 장단점이 존재한다는 것입니다. (이것을 보통 trade-off라고 표현합니다.)하지만 설계하는 서비스를 잘 알고 서비스에서 무게를 둬야 할 부분을 파악한다면, 그에 맞는 시스템을 설계하고 디자인할 수 있을 겁니다. 김유저 씨도 만족시킬 수 있을 거고요. 꼬박 이틀을 밤새워서 쓴 글이 아직 시스템 아키텍처를 두려워하는 다른 주니어 개발자분들에게 도움이 되었으면 합니다. 이번에는 시스템에서 아주 기초적인 부분을 공부했으니 다음 글에선 MSA(MicroService Architecture)를 씹어봅시다! 겁이 나고 무서워도 외쳐보세요. “Hello, System Architecture!”이 세상 모든 주니어 개발자분들, 퐈잇팅입니다.참고1) 추가적인 이점에 대하여: 웹서버에서 요청을 보낼 때 database 도메인 네임으로 보낼 경우, 멀리 있는 공인 DNS 서버 (예를 들면 google public DNS server: 8.8.8.8)에 물어오는 것보다 자체 DNS 서버에 물어오는 것이 훨씬 더 빠른 속도로 응답을 받아올 수 있습니다.출처GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards.글오연주 사원 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #주니어개발자
조회수 1192

UI, UX 그리고 디자인 프로세스

 이 글은 아무래도 실무를 진행하면서 쉽게 잊게 될 수 있는 이야기 입니다. 실무를 하고 계신분들은 다시한번 상기할 수 있는 기회를, 준비를 하시는 분들에게는 꼭 필요한 인사이트를 드리고 싶네요:)"내가 만든 서비스를 보다 쉽고 재밌게 이용자들에게 제공하고 싶다."라는건, 세상 모든 기획자, 개발자, 디자이너 분들이 다 가지고 있는 궁극적인 목표입니다. 열심히 만든 서비스가 단순히 "보기 힘들어서, 서비스 외적인 부분에 결함이 있어서" 사용하지 못하게 된다면 그것만큼 화나는 경험이 없겠죠. 그런 의미에서 UI/UX는 서비스의 얼굴을 만드는 과정이기 때문에 엄청나게 중요한 역할을 차지합니다. 굳이 서비스를 제작하시는 분들이 아니더라도, UI, UX에 대한 이야기들은 많이들 들으셨을 텐데요, 두단어가 비슷하게 보일 수는 있지만, 기본적으로 UI와 UX는 큰 차이가 있는 단어라고 생각합니다. 1. UX (User Experience) 직역하자면 사용자 경험이고, 이용자가 재화나 서비스 등을 사용할 때 생기는 신체적 반응이나 심리적 기호도(또는 선호도), 그리고 상징적 상호적인 사회적 기준 등을 수집하고, 이에 대한 조사와 연구 등을 통해 "이용자가 더 맘에  들어할 수 있는 재화 또는 서비스."를 설계하는 큰  과정입니다. UX는 개발자 중심이 아닌 사용자 중심의 컴퓨터 시스템을 구축하기 위한 제록스사의 연구 중 HCI(Human Computer Interection) 연구에서 처음 사용된 개념이며, 처음엔 하드웨어에 집중된 연구였다고 합니다.  현재는 UX라는 개념이 컴퓨터와 관련된  것뿐만 아니라 모든 산업을 통해 제공되는 재화와 서비스 전반적인 사회의 흐름이나 문화에 이르기까지 널리 응용되고 있고, 이를 연구하기 위하여는 다방면의 관점과 지식, 시야를 가지고 접근해나가야 합니다. 2. UI (User Interface) UI는 아무래도 더 친숙한 개념이실 텐데요, 왜 아이폰이나 애플, 또는 샤오미 같은 전자기기 회사에 대한 이야기를 할 때 "UI가 직관적이고 혁신적이다." 이런 말 많이 들어보셨죠? 정말 간단히 설명하자면, 전자기기 또는 어플이나 웹을 사용했을 때 이용자들이 보게 되는 화면 구성을  이야기하는데요, 이용자들이 선호하는 구성, 배치, 콘텐츠 등을 UX 조사를 통해 분석하고, 조율하여 보다 직관적이고 빠르게 서비스를 이용할 수 있도록 하는 개념입니다.  보시다시피 UI는 사용자의 경험을 기반으로 하여 어느 위치에 어느 기능이 담긴 어떤 기호 또는 인포그래픽을 그리고 어떤 콘텐츠를 기입함에 따라 보다 쉽게 이용자들의 생산성을 향상 시키는 분야라고 할 수 있습니다.(자세한 이야기는 여기, 와 여기를 참조하세요!) 설명에서  보시다시피 UX가 UI의 개념을 포괄하는 것을 보실 수 있죠, 그렇다면 UX, 또는 UI 정보는 어떻게 얻을 수 있을까요? 기본적으로 여러분들이 초보 디자이너 또는 기획자 시라면, 성공적으로 돌아가고 있는 서비스들이나, 다양한 UI 제작 회사나, UX 에이전시들의 제작 리뷰, 리서치 결과 등을 벤치마칭 하는 것으로 디자인 프로세스를 시작들 하시곤 하죠. 물론 검증이 된 서비스의 틀을 사용하는 것은 기존 이용자들의 편의성을 이끌어 낼 수 있기 때문에 굉장히 효율적인 방법이라고 생각합니다. "이용자의 경험에  기반한다."라는 약속을 100% 지킨 제작 방법이니깐요. 그러나 유명한, 또는 큰 서비스들의 심미성이나, 배치만을 보고 그대로 따라 하는 디자인은 절대로 옳지 않은 판단이라고 생각합니다. 아이디어의 유용에도 이유가 있어야 합니다. 출처: http://austinkleon.com/steal/ 저는 디자이너는 아니지만 재화 또는 서비스를 기획하고 디자인하시는 분이라면, 자신이 제작한 디자인, 또는 목업작업에 내재된 인사이트를 정확하게 설명을 할 수 있어야 하고, 이를 위해 스스로 리서치에 대한 결과를 분석하고 판단할 수 있어야 한다고 생각합니다. 예를 들면, 서울시 지하철 바닥에 붙인 동그라미 스티커나 버스 정류장에 붙여 있는 빨간색 스티커를 보신 적이 있으신가요? 출처: http://news.sbs.co.kr/news/endPage.do?news_id=N1003310218출처: http://www.lgchallengers.com/career/young1829/20130118_inter/  경제(행동경제학) 용어로는 넛지 효과(Nudge Effect)라고 하는데요, 의연 중에 이용자의 결정에 개입하여 원하는 결과를 이끌어 내는 효과를 뜻합니다. 제가 생각하기에는 이러한 피지컬 한(또는 비져블 한) UX도 그냥 여기서 끝나는 것이 아니라, 응용을 통하여 다방면의 분야에 퍼질 수 있다고 생각합니다. "이용자의 심리적인 판단의 흐름을 읽고, 인지적 선호에 따라 이용자의 행동을 파악, 이용자에게 시각적인 제한을 줌으로써 공공이익을 유도할 수  있다."라고 개인적인 인사이트를  발전시킬 수 있는 것 이죠. 웹을  벤치마킹하는 것도 다르지 않다고 생각합니다, 분명히 같은 모양, 같은 색, 같은 배열로 서비스 디자인을 할 수 있지만, 개인의 연구와 조사를 통해 인사이트를 발전시켜 나감에 따라 정당한 디자인 프로세스를 진행하고, 인사이트를 기반으로 익숙한 UX에 새로운 UX를 적용하는 것이 가장 바람직하다고 생각합니다.#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트

기업문화 엿볼 때, 더팀스

로그인

/