스토리 홈

인터뷰

피드

뉴스

조회수 1670

덕질도 신박하게! R을 활용한 텍스트 마이닝 도전기

Overview대학원에서 소프트웨어 공학을 전공하고 있습니다. 이번 학기엔 ‘빅데이터 분석’ 과 ‘대용량데이터베이스관리론’ 과목을 수강하면서 생애 처음으로 R Studio 프로그램을 설치해봤는데요. 머신 러닝을 다뤄본 적도, 자연언어처리 분야를 개발한 적도 없지만 어느 날 텍스트 마이닝 관련 강의에서 불현듯 이런 생각이 떠올랐습니다. “내가 좋아하는 가수로 텍스트 마이닝을 하면 어떤 결과가 나올까?”머릿속으로 생각하는 것과 내가 직접 구현을 해보는 것은 절대 다른 법! 일단 도전해보기로 했습니다. 개발 3년과 덕질 10년의 실력을 쏟아 부을 겁니다.지금까지 예쁜 디자인이라고만 알고 있었던 WordCloudStep1. 트위터 Developer 에서 인증키 받기트위터 Developer (Twitter Developer Platform — Twitter Developers) 에 접속해서 개인 계정으로 로그인하고, 오른쪽 위의 Apply를 클릭합니다.Twitter standard APIs > Get started with standard access를 클릭합니다.등록된 개발자 앱이 없으면 Create an app의 apps.twitter.com을 클릭합니다.Create New App을 클릭합니다.각 항목을 입력합니다. 저는 Website 가 없기 때문에 로컬 호스트를 기재했습니다.약관에 동의한 후 Create your Twitter application을 클릭합니다.만약 어플리케이션 이름이 중복된다면 위와 같은 에러 메세지가 나올 겁니다. 정상적으로 어플리케이션이 등록되면 위의 화면과 함께 API Key를 발급받을 수 있습니다. Consumer Key (API Key) 옆의 내용 (캡쳐화면에는 비공개)을 클릭하면 API Key 뿐만 아니라 API Secret, Access Token 등 세부 내용을 관리할 수도 있습니다.Step2. R Studio 설치하기 (Mac OS 기준)구글에서 R for macOS를 검색을 하면 맨 위에 설치 페이지가 보입니다. 1)먼저 R 패키지를 설치해야, 나중에 R Studio를 설치했을 때 실행이 가능합니다.R Studio 홈페이지에서 R Studio를 다운받습니다. 다운로드 링크는 여기를 클릭하세요.RStudio가 정상적으로 실행이 된다면, 이제 준비는 끝났습니다! Step 3. 필요한 패키지를 먼저 설치하기따로 설치가 필요한 패키지는 RStudio에서 명령어로 설치할 수 있습니다.—한 개씩 설치하는 법install.packages(“packageName”)—여러 개의 패키지를 한 번에 설치하고 싶을 땐 위와 같이 설치할 수 있습니다.—여러 개를 한꺼번에 설치하는 법install.packages(c(“package1”, “package2”,”package3”))—설치를 했다고 해서 바로 사용할 수는 없습니다. 이 패키지를 사용하겠다는 명령어를 다시 입력해야 합니다.—설치한 패키지를 사용하기library(“packageName”)—이번 글에서는 아래와 같은 패키지들이 필요합니다.twitteRROAuthbase64enchttpuvtmSnowballCwordcloudRColorBrewerStep 4. 트위터 api와 연동하여 WordCloud 생성하기먼저 각자 API 관련 Key 들로 객체를 생성해주고, setup_twitter_oauth() 메소드를 사용하여 Twitter API에 접근합니다.searchTwitter 4) 라는 함수를 사용하면, 트위터 API 를 통해 관련 트윗 내용을 추출할 수 있는데요. 좋아하는 일본 아이돌 가수인 “아라시”를 키워드로 추출하려고 첫 번째 파라미터에 “Arashi”를 넣었습니다. 그 뒤의 내용은 영문으로 작성된 최근(Recent) 트윗을 최대 1500개까지 리턴 받겠다는 의미입니다. resultType에는 popular를 넣으면 가장 인기있는 트윗을 받을 수도 있습니다.데이터를 가져오면, 위와 같이 데이터가 추출된 것을 확인할 수 있습니다.이제 matchTweets에 있는 내용으로 분석가가 되어 마음대로 데이터를 가공할 수 있습니다. class 등으로 구조와 클래스를 확인할 수 있을 뿐만 아니라, nchar() 를 이용해 트윗당 문자 수를 계산할 수도 있습니다. 이번 글에서는 위와 같이 트윗을 20개 추출했습니다.각각의 트윗을 보면, 이상한 코드나 슬래시 등 필요 없는 데이터들이 포함되어 내려온 것을 확인할 수 있습니다. 이 부분들을 제거해 깔끔한 데이터로 가공해보겠습니다. 그리고 텍스트 집합이라고 볼 수 있는 Corpus를 생성한 후, WordCloud 까지 생성해볼게요.데이터를 Corpus 로 만들 때는 Corpus() 를 사용하면 됩니다. 저는 VectorSource 라는 명령어를 사용해 단어들을 Vector로 바꿔주었고, 데이터가 잘 들어갔는지 확인하기 위해 inspect() 를 사용했습니다.사람이 읽기 불편한 단어들을 제거하는 건 tm_map 함수 하나면 충분합니다.위의 이미지를 보면, 각 행마다 특정 특수문자들을 제거하기 위한 명령어가 있습니다. 중간 부분엔 stopwords 라는 단어가 있는데, 영어 문장에 들어가는 i.e 나 etc 같은 표현들을 제거할 수 있는 겁니다. 그 외에도 대문자를 소문자로 바꾸거나 번호를 제거하는 등의 옵션들이 이미 R에서는 제공되고 있기 때문에, 우리는 입맛에 맞게 가져다 쓰기만 하면 됩니다.이제 대망의 WordCloud를 만들 차례입니다.max.words는 최대 N개의 단어를 고르는 옵션이며, min.freq는 최소 N번 이상 나온 단어, random.order = FALSE는 제일 많이 나온 단어가 먼저 나오도록 지정하는 옵션입니다. colors는 지정하지 않으면 검정색으로만 나오지만, 알록달록 예쁘게 표현하고 싶다면 여러 옵션을 지정해서 Frequency 에 따라 다른 색이 나오도록 할 수도 있습니다. 5) 첫 번째 이미지가 이번 글의 예제로 얻은 결과인데요. 추출 언어를 영어로만 한정했더니 일본어 발음을 영문으로 표현한 데이터가 많았습니다. 기타 설정을 변경하여 다시 추출한 게 바로 두 번째 이미지입니다. 큼직큼직하게 나온 단어들을 보면 DVD 나 블루레이 출시와 관련된 트윗이 대다수인 것을 볼 수 있는데요, 검색 결과 최근 2017-2018 라이브 투어 ‘Untitled’가 출시된 것을 확인할 수 있었습니다. 기타 작게 표현된 단어들을 보면 아라시의 노래 제목들도 확인 가능한데, 이 노래들이 인기있다는 것도 예측할 수 있습니다.Conclusion지금까지 R을 이용해 트위터 API 와 연동한 텍스트 마이닝을 했습니다. 데이터를 WordCloud로 생성하는 것도 해봤고요. 이번 글에서는 기본적인 예제를 다뤘지만 텍스트 마이닝의 세계는 아주 깊고 넓습니다. 만약 이 글로 텍스트 마이닝에 조금이라도 흥미가 생겼다면 일단 도전해보세요! 좋아하는 것과 연관 지어서 따라 하다 보면 꽤 즐거운 시간이 될 겁니다.참고1) 18년 6월 6일 기준이다.2) Twitter Sentiment Analysis Tutorial3) Text mining: Twitter extraction and stepwise guide to generate a word cloud4) R 함수 관련 설명은 R Documentation 사이트에서 확인할 수 있다. 5) 색상 옵션이 궁금하다면 여기에서 참고할 수 있다. 6) 머신러닝 언어처리 - R로 WordCloud 만들어보기 - 데이터 사이언스 랩글김우경 대리 | R&D 개발1팀kimwk@brandi.co.kr브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #R #텍스트마이닝
조회수 1006

일 잘하는 메모 정리법

어느덧 회사 생활을 시작한 지 5년차다. 대리라는 직함도 달게됐다. 이제는 좀 회사에 적응했나 싶지만, 입사 초반은 매사가 좌충우돌의 연속이었다. 새로운 업무를 배우랴, 시키는 일들을 해가랴, 가끔있는 회식까지. 그래도 신입 첫해는 그럴 수 있다는 방패막이 있었지만, 연차가 쌓일수록 빠뜨린 업무가 하나 발생하는 건 이미지에 큰 타격을 입혔다. 처음에는 성격좋은 덜렁이로 생각할 수 있을지언정, 한 두번 실수가 쌓이면 구멍으로 인식하게 되는 것은 순식간이다. 더 큰 문제는 나에게 있었다. 외부에서 나를 바라보는 불안감 어린 시선보다도, 스스로가 업무를 제대로 처리하지 못한다는 불안감을 갖게 되면서 오히려 업무에 집중할 수 없는 게 문제였다.뾰족한 해결책이 필요했다. 처음 떠올린 건 메모였다.필요한 일은 메모지에 써서 붙여 두기. 하지만 사실 업무가 좀 많은가. 못한 일만 가득 적힌 메모지로 벽이 도배가 돼서 어느 메모가 중요한지 알아볼 수 없게 됐다. 보는 것만 해도 스트레스였다. 결국 너저분한 벽으로 팀장님께 한번 제대로 혼나고 이 방법은 접게됐다. 다음은 다이어리였다. 다이어리도 초반에는 열심히 적었다. 할 일이 빼곡히 적힌 메모지가 여기저기 날라다니는 것 보다는 한 공간에 할 일들을 적어가니 훨씬 정리정돈도 잘됐다. 하지만 안 써 버릇하던 걸 신경 써서 쓰자니 쉽지 않았다. 게다가 다이어리는 왜이렇게 무거운지. 불편함에 한 두 번 다이어리 없이 회의에 참석했다가 할 일을 다른 노트에 적고 나서부터는 결국 이노트 저노트를 쓰다가 다이어리도 치우게 됐다.그나마 변화가 생기기 시작한 것은 팀장님이 팀원 전체에 내일 할 일 목록을 적고 퇴근하도록 지시했을 때부터였다. 솔직히 처음에는 반발심도 있었다. 초등학생도 아니고 매일 내일 할 일을 적고 퇴근하라니. 당연히 그 속내를 드러내지는 않았다. 직장인이니까. 팀장님은 이 프로젝트에 ‘내일 3줄’이라는 이름까지 붙였다. 이런 이름이 없었다면 나는 아마도 끝도 없이 해야할 일들을 나열하고 있었을 것이다. 다행히 팀장의 꼼꼼한(사실은 빡빡한) 성격 덕분에 ‘내일 3줄’을 효과적으로 활용할 수 있게됐다. 처음에는 그저 기억에 잘 남게 할 광고 카피 정도의 역할로 생각했던 ‘내일' 과 '3줄’이라는 이름에 사실은 심오한 뜻이 담겨 있었다. 사실 (나도 그랬지만) 사람들은 어딘가에 해야할 일들을 열심히 적지만, 열심히 적어나가기만 할 뿐 리뷰를 한다거나 우선순위를 정하는 일에는 소홀하다. 나 역시 해야하는 일이 생기면 메모장에 할일 목록을 열심히 적기에만 바빴지 무엇이 중요한 지, 어떤 것부터 해야하는지와 같은 일을 챙기는 데에는 무심했다. 그러다보니 잊어먹어서 일을 못한다기 보다는 다른 일을 처리하다가 중요한 일을 놓치는 경우가 있었다. 그것도 중요하고 시급한 일들을. 나의 내일 3줄 할일들할 일을 적을 때의 포인트는 중요한 일을 적은 개수로 적는 것이다. 사람의 인지능력에는 한계가 있어서 너무 많은 항목을 적으면 오히려 집중력이 흐트러진다고 한다. 3개 정도가 적당하고, 많아도 5개를 넘기지 않는 것이 좋다고 한다. '내일 3줄' 프로젝트에서는 할 일을 3개로 적도록 개수를 제한하니, 오늘 하려고 적어놨던 많은 일들 중에 내일 꼭 해야할 일 3개를 퇴근 전에 추려보게 됐다. 자연스럽게 우선순위에 기반해서 중요하거나 시급한 일이 목록에 들어오게 됐고, 퇴근 후에도 적어놓은 일들을 위주로 어떻게 처리할까에 대해 생각해보게 됐다. 예전처럼 20개 이상의 할 일들이 너저분히 적혀 있었더라면 사실 노트를 들춰보고 싶은 마음조차 안생겼을 것이다.두 번째로 강조할 부분은 현실적인 목표를 적어야 한다는 것이다. to-do list 의 숨겨진 효과 중 하나는 목표를 달성했을 때 항목을 하나씩 지워가면서 성취감을 쌓는 것이다. 터무니 없는 목표를 적으면 내일도 모레도 그 목표를 달성하지 못한 채 목록에 써야 한다. 예를 들면, 이직을 한다고 하면, 이직을 1번 목표라고 적을 것이 아니라, 이력서 쓰기나 관련분야 사람만나기 같이 바로 실행으로 옮길 수 있는 일들을 적어야 한다. 하루만에 끝낼 요량이라면 월간 보고서라고 써도 좋지만 그렇지 않다면, 월간 보고서 주제잡기나 팀장님과 보고서 주제 논의하기 정도가 적당하다. 며칠 째 to-do list 상단에 묵혀놓은 할 일이 남아있다면, 어느 순간 우리 뇌는 그 목록은 안 지워지는 일로 자연스럽게 인식하게 된다. 그래서 가능하면 목록은 하루 이틀내에 지울 수 있을 정도의 태스크 수준으로 적어두는 것이 좋다.그런데 3줄 요약 쓰는 것 자체를 잊어버리면? 실제로 몇 번 이런 일도 있었다. 이럴 때는 자신이 퇴근 전 꼭 하는 행동과 3줄 요약 쓰기를 연계하면 좋다. 내 경우 퇴근 전 머그잔을 씻는 습관이 있어서, 머그잔 근처에 3줄 요약을 쓸 포스트 잇을 붙여두었다. 사실 노하우라고 하기에는 너무 소소한 방법이지만 아무튼 그 이후로는 3줄 요약 없이 퇴근했던 기억은 없다.사실 나는 아직도 일정 관리를 더 잘할 수 있는 방법을 찾고 있다. 언젠가는 다시 다이어리를 효과적으로 활용할 수 있게 될지도 모른다. '내일 3줄' 도 쓰다보니 가끔 지겨워지고 변화를 주고 싶다는 생각이 드는 게 사실이다. 아직은 그 생각이 강하지 않아서 '내일 3줄' 쓰는 일을 계속 하고 있지만, 언젠가는 새로운 방법으로 내일의 할 일을 적게될지도 모르겠다. 중요한 것은 업무의 홍수 속에서 중요한 일을 놓치지 않고 성과를 내는 것이지 3줄 요약이라는 방법에 집착하는 것이 아니다.by 직장 에이스를 꿈꾸는 5년차 직장인챌린저스, 확실한 목표달성 꾸준한 습관형성 앱www.chlngers.com
조회수 1196

흐린 날의 생일 파티

안녕하세요.공항을 오가는 가장 편한 법 벅시(BUXI) 입니다.이런 말씀은 안 드리려고 했는데...그 벅시가 아니라 벅시(BUXI) 입니다.BUS+TAXI해외여행 갈 때, 출장 갈 때, 그냥 공항가고 싶을 때집 앞까지 모시러가는 벅시(BUXI) 입니다.오늘은 오랜만에 포스팅을 하게 되었습니다.근데 너무너무너무너무너무 진부한 내용을 포스팅 해달라고 하네요.아휴 무슨 팀원 생일 파티까지 제가 올려야 하나요?날씨도 엄청 흐린데 남의 생일 제가 알게 뭔지???...?!음... 생각해보니 벅시(BUXI)의 마블리, 우리 운영팀의 마동석,석헌님의 탄생일이군요....진짜 저렇게 생기심때리지 마세요 ㄷㄷㄷ포스팅 할께요 ㄷㄷㄷ마동... 아니 석헌님은 우리 벅시(BUXI) 서비스의 운영팀의 업무를 맡아주고 계십니다.되게 친절하시고 일도 엄청 잘하십니다 ㄷㄷㄷ친절한 그의 메신저 프로필 사진... 도대체 왜? ㄷㄷㄷ그... 뭐냐 운동도 엄청 좋아하셔서 저한테 아래 사진 같은거를 가끔 보내주고 그러십니다 ㄷㄷㄷ존경하는 사람이라고 ㄷㄷㄷ네... 뭐 암튼 하하하사진 찍은 것 좀 보실까요 ㄷㄷㄷ왕의 모습 ㄷㄷ왕관이 잘 어울리십니다 왕이시여...충신들의 바쁜 손놀림 ㅠㅠ이거 쓰다보니 문득 든 생각인데,그 동안 우리 회사의 생일자 분들이 많으셨거든요.근데 파티하자고 한 건 이번이 처음 ㄷㄷ이유가 뭘까요? 맞기...싫어서?조공 5초전...영상으로 제 충성심을 표현해보겠습니다!흡족해하시는 모습을 보니 '조공'이 성공적인 것 같네요.기분이 좋으셨는지 '직접' 케익을 '하사'하시는 모습까지! ㄷㄷㄷ팔뚝에 근육 ㄷㄷㄷ비록 비가오고 흐린 날 이었지만마동... 아 아니 석헌님의 생일 파티가 있어서 매우 행복한 하루였습니다 ㄷㄷㄷ이 글을 읽으시는 모든 분들도 행복한 기운을 느끼셨으면 좋겠습니다.....마무리ㄷㄷㄷ공항을 오가는 가장 편한 방법 벅시(BUXI)였습니다.석헌님 생일 다시 한번 축하드려요~#벅시 #스타트업일상 #운영 #성장 #일지 #기업문화 #조직문화 #사내복지
조회수 1971

AWS S3를 이용하여 Vue 배포하기

Vue를 처음 만났을 때, 이것으로 무엇을 할 수 있을지 궁금했다. 하지만 Vue로 데모 앱과 개발 가이드를 따라하면서 의문은 점점 풀렸다. 알다시피 Vue는 front-end 로 활용이 된다. 빌드가 없어도 되고, 빌드를 해서 배포할 수도 있다. Vue는 일반 CDN을 이용하여 페이지를 만드는 방법과 여러 프레임워크를 활용하여 배포하는 방법 외에 다양한 방법이 존재하는데, 무슨 방법을 쓰든 결과물은 html과 js, css 같은 static 파일로만 이루어져 있다.처음에는 일반적인 방법으로 테스트하면서 다양한 디렉티브와 손쉽게 DOM 처리를 하는 방법을 익혔다. 나중엔 프로젝트에 참여하면서 webpack 으로 빌드해 배포하도록 프로젝트를 구성했다. webpack을 이용한 배포방법은 여기 를 참고하면 된다. 참고로 webpack은 nodeJS로 실행되기 때문에 기본적인 환경을 세팅해야 한다.webpack build.js 일부위처럼 직접 스크립트를 만들어서 사용해도 되지만 Vue에서 제공하는 템플릿으로 프로젝트를 생성할 수도 있다. 단 Vue-CLI가 미리 설치되어 있어야 한다.터미널에서 vue init webpack 프로젝트명만 치면 세팅된 템플릿으로 폴더 및 스크립트들이 구성된다. 아래와 같이 프로젝트의 기본 속성들을 입력하자.프로젝트를 만들면 기본적인 파일들로 이루어진 폴더가 생성된다. 현재는 관련 라이브러리들이 없는 상태이므로 npm install 을 통해 설치한다. 설치 후 nom run dev 로 개발모드를 실행하면 브라우저로 화면을 볼 수 있다. 만약 설치하고 빌드 설정을 수정하지 않았다면 기본 8080 포트로 가동된다. 브라우저를 실행해 http://localhost:8080 으로 접속하면 아래와 같은 화면이 나온다.여기까지 하면 webpack 으로 배포할 수 있는 상태가 되었다. 이제 AWS로 가서 회원가입을 하고 S3를 생성한다. 생성 방법은 여기를 참고하면 된다. 버킷까지 생성되었다면 이제 빌드 후 업로드하자.위와 같이 nom run build 를 하면 빌드가 시작된다.빌드가 완료되면 해당 프로젝트 폴더에 dist 폴더가 생성된다. dist 폴더에는 index.html 과 js, css 와 같은 리소스들이 들어간다. 이제 S3로 가서 올리려는 버킷을 클릭하자.업로드 버튼을 클릭하고, dist 폴더에 있는 index.html 과 static 폴더를 업로드한다. 폴더가 업로드되면 아래와 같이 파일과 폴더들이 보인다.업로드가 완료되었다고 지금 바로 웹사이트처럼 접근할 수는 없다. 정적 웹사이트 호스팅 설정을 활성화해야 비로소 가능하다. 속성 탭을 클릭해 정적 웹사이트 호스팅을 활성화 상태로 만든다.위와 같이 활성화하고 인덱스 문서에만 index.html 을 입력한 후 저장 버튼을 클릭한다. 현재 보이는 엔드포인트 주소가 외부에서 접근할 수 있는 사이트 도메인이다. 그 후 엔드포인트 주소로 접속하면 아래와 같이 오류 페이지를 볼 수 있다.이게 무슨 오류란 말인가… index.html 파일도 있는데 403 오류라니..자세한 http 응답코드는 여기를 참고하면 된다. 위의 오류는 권한이 없어서 파일에 액세스할 수 없다는 페이지다. S3는 기본적으로 모두에게 공개하진 않는다. 그래서 특정 파일이나 특정 버킷만 공개형으로 변경해줘야 한다.이 문제를 해결하려면 권한 탭으로 이동해 버킷 정책을 설정해야 한다. 아래와 같이 설정해주면 누구에게나 공개되어 접근할 수 있다.위 내용을 아래와 같이 버킷 정책으로 설정한다.설정을 저장한 후 다시 엔드포인트로 접속하면 아래와 같이 로컬에서 보였던 페이지가 보인다.이렇게 보이면 성공!다음엔 Vue가 어떤식으로 동작을 하는지 알아보도록 하겠다.마치며Vue는 간결하면서도 강력한 기능을 가지고 있는 front-end 프레임워크다. 개념과 디렉티브, 이벤트 핸들링, 보안 등 궁금한 게 많았지만 신통방통한 놈인 건 확실하다. 아직 큰 프로젝트에 사용하는 건 힘들 수도 있으나 아래와 같이 장점이 많아 서버단과 클라이언트단 분리 개발, 외부 라이브러리와 사용하면 훌륭한 프레임워크가 될 거라는 생각이 든다.재사용 가능한 기능별 컴포넌트 개발훌륭한 라우터 탑재서버와 통신 가능한 ajax 모듈이 다양함 ( jQuery Ajax, Axios )다양한 호환 라이브러리를 활용하면 분명 훌륭한 프레임워크가 될 것!편집자 주) 함께 보면 좋아요!Vue, 어디까지 설치해봤니?PHP Codeigniter 환경에서 VUE 사용해보기JQuery 프로젝트에 VUE를 점진적으로 도입하기Vue와 Vuex, 컴포넌트간 통신과 상태 관리글장현준 팀장 | R&D 개발3팀janghj@brandi.co.kr브랜디, 오직 예쁜 옷만
조회수 1400

냉정과 열정 사이

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

[SQL] 이탈고객의 재사용률 분석

저는 TLX에서 PM 과 Business Analyst로 일하고 있습니다. TLX의 서비스인 TLX Pass는 하나의 멤버십으로 여러가지 운동종목을 이용할 수 있는 서비스입니다.대부분 사람들이 운동이라는게 항상 심리적 부채로 가지고있고, 연초나 여름시즌을 앞두고 헬스장을 등록하며 그 부채를 갚으려 합니다.TLX Pass 사용자들도 그러한 패턴이 있는지 분석하려고, 각 월별로 이탈한 사용자가, 어느시점에 다시 돌아오는지, 돌아오는 비율이 어느정도 되는지 분석해보았습니다.이 글에서는 SQL을 이용해서 이탈고객(churned user)에 대한 재사용률(return rate)을 분석하는 방법에 대해서 설명하려고 합니다.Part 1. 월별 churned user 리스트당월 churned user(이탈회원)의 정의를 다음과 같이 정합니다.- 전월 멤버십을 가진 사용자 중에서 당월 멤버십이 없는 사용자Churned user 리스트는 여집합의 형태로 당월 retained user (유지회원)을 우선 구하고, 전월 멤버십 회원 리스트에서 빼는 형태로 만듭니다.[1-a] 월별 멤버십 회원 리스트멤버십의 사용시작일과 종료일을 기준으로 user_id를 가져온다.아래 sql은 2016년 9월 멤버십 회원 리스트를 가져오는 query이다.//[QUERY 1-a] 월별 멤버십 회원 리스트 select memberships.user_id from memberships where left(date_start, 7) <= "2016-09" and left(date_end, 7) >= "2016-09"[1-b] 월별 유지회원 리스트[1-a 월별 멤버십 리스트]에서 구한 회원들을 대상으로 1개월 뒤에도 멤버십을 가진 사용자 리스트를 구합니다.아래는 2016년 9월에 멤버십을 가진 회원중에서, 2016년 10월에도 멤버십을 가지고 있는 회원의 user_id 를 가져오는 SQL입니다.//[QUERY 1-b] 월별 retained user 리스트 select current.user_id from memberships current where current.user_id IN ( [QUERY 1-a] ) and left(current.date_start, 7) <= "2016-10" and left(current.date_end, 7) >= "2016-10"[1-c] 월별 churned user 리스트여집합으로 구하기 위해 [1-a 월별 멤버십 리스트]와 [1-b 월별 유지회원 리스트] 리스트를 left join으로 합치면, 당월에 멤버십이 없는 회원은 future.user_id 값이 NULL 이 됩니다.아래와 같이 future.user_id가 NULL 인 것만 모으면 바로 churned user 리스트가 됩니다.//[QUERY 1-c] 월별 churend user 리스트 select distinct current.user_id from ( [QUERY 1-a] ) as current //전월 멤버십 회원 리스트 left join ( [QUERY 1-b) as future //당월 retained user 리스트 on current.user_id = future.user_id where future.user_id is null //당월 멤버십이 없는 조건[1-d] 월별 churned user 리스트를 temporary table 에 저장하기이제 churned user를 분석하기 위해서 위에서 구한 churned user 리스트를 query에서 계속 사용해야 한다. 이럴땐 with 구문이나 with 구문이 지원되지 않는 db라면 temporary table 로 저장해 두면 편리하기도 하고 query가 가벼워져서 데이터를 뽑는 속도도 좋아집니다.Temporary table (임시 테이블)- 임시로 db에 생성하는 table로, db connection이 끊어지면 자동으로 삭제됩니다.- 임시라는 것 외엔 테이블과 동일한 SQL을 이용하면 됩니다.아래는 [1-c 월별 churned user 리스트] query 결과를 oct_16_churned_user_ids 라는 임시 테이블에 넣는 SQL입니다.create temporary table oct_16_churned_user_ids ( user_id varchar(20) )insert into oct_16_churned_user_ids ( [QUERY 1-c] )// select * from oct_16_churned_user_idsPART2. Churned period 별로 return rate 분석하기churned user 를 대상으로, churned 시점보다 이후에 만들어진 멤버십을 멤버십 시작월 기준으로 그룹핑을 시킵니다.select date_format(membership.date_start, '%Y-%m') , count(distinct membership.user_id) from membership where user_id IN ( select * from oct_16_churned_user_ids ) left(membership.date_start, 7) > '2016-09' // churned 시점 이후 group by date_format(membership.date_start, '%Y-%m') // 멤버십 시작월로 그루핑이 데이터를 월별로 모아서, Excel의 pivot table을 이용하면,아래와 같이 월별로 churned user가 언제 다시 돌아오는지, 돌아오는 비율은 어떻게 되는지 분석할 수 있습니다.데이터는 실제 데이터가 아님.뭔가 pivot table을 만드는 부분에서 휘리릭 끝내는 감이 있네요 ㅎㅎ코멘트를 언제나 환영입니다. :)#티엘엑스 #TLX #비즈니스애널리스트 #BA #BusinessAnalyst #업무 #꿀팁 #인사이트
조회수 2114

[H2W@NL] 로봇과 디자인

디자인이란 단어가 이제는 어디서나 익숙합니다. 그만큼 디자인의 정의와 역할은 다양한 영역에서 분화되어 있기도 합니다. 네이버랩스에서는 로봇이라는 대상에 대해 여러 분야의 디자인이 진행되고, 종국에는 통합됩니다. 하나의 로봇으로 이어지는, 로봇시스템/UX/ID 각각의 디자인에 대해 물었습니다.Q. 어떤 ‘디자인’을 하나요?로봇의 메커니즘에서 인터페이스까지, 최적의 시스템을 디자인(김인혁|Robot) 제가 하는 디자인은, 시스템 디자인이라고 말할 수 있습니다. 아, 물론 제가 속한 Robot팀엔 더 많은 디자인 과정들이 있어요. 로봇의 기구, 전장, SW 등 각각의 영역에서도 디자인 과정이 존재합니다. 저는 그 중에서 주로 시스템 제어 엔지니어로서의 디자인을 이야기할 수 있겠네요.사실 시스템이란 말이 좀 모호하죠. 과학분야에선 이렇게 정의할 수 있습니다. 구성 요소들이 내외부와 경계를 가진 상태에서 각 요소 간에 긴밀한 상호작용을 하는 집합체. 쉽게 설명하고 싶었는데, 여전히 어렵긴 하네요.로봇은 단순한 기능을 구현할 때에도 복잡한 요소들이 동시에 작동합니다. 메커니즘, 동력원, 에너지원, 제어기와 인터페이스 등. 이들이 서로 잘 연결되어 작동할 수 있어야 합니다. 이를 위한 최적의 시스템을 구성하는 디자인이라 하겠습니다.로봇, 그리고 사람, 그 사이에서의 상호작용(김석태|UX) UX의 입장에서는 HRI (human-robot interaction) 디자인이라고 정의할 수 있습니다. 앱이나 웹 등의 화면 기반 인터페이스와는 조건이 다른데요. 물리 공간에서 로봇이 동작한다는 점이 그렇습니다. 주변 사물이나 사람을 로봇이 인식하는 순간처럼 다양한 상황에서 로봇이 어떻게 동작하거나 반응해야 하는지, 그리고 로봇을 활용한 서비스는 다른 디바이스나 앱과 달리 어떤 방식을 통해 제공되어야 더욱 직관적으로 사람과 상호작용이 가능한지 등을 디자인하고 있습니다.기술만큼, 인상과 매력도 중요하다(김승우|ID) 로봇의 외관도 중요합니다. 로봇은 여전히 일반인들에겐 생소합니다. 이들에게 로봇은 흥미로움을 일으키는 대상일 수도 있지만, 마주치는 순간 기피하고 싶은 이질적 존재일 수도 있어요. 그래서 외관을 통해 느끼는 인상과 그 효과에 대해 세심한 접근을 하고 있습니다. 로봇 서비스가 보편화되지 않은 시점에서는, 사람들이 기대하는 로봇다운 매력을 잘 체감할 수 있게 하는 것도 로봇 대중화를 위해 중요한 역할인 것 같습니다.“기술이 지닌 본래의 가치를 더욱 잘 느낄 수 있도록 전달하는 것, 그것도 디자인의 역할입니다.” Q. 어떤 프로세스로 작업하나요?단순한 목표를 위해 필요한 복잡한 과정들(김인혁|Robot) 기본 목표라고 한다면, 일단 요구 스펙을 잘 만족하는 시스템을 설계하는 것입니다. 현실은 아주 복잡하죠. 요소들이 워낙 다양하기 때문인데요. PoC, 성능 테스트 등 평가 과정을 거치면 조정해야 할 것들이 많아집니다. 아예 새로 개발을 할지를 고민하게 될 때도 있는데, 참고할만한 레퍼런스가 없을 때는 참 어려워집니다. 이럴 때는 원론적으로 풀 수밖에 없죠. 공학적인 문제부터 정의하고 문제 해결을 위한 방법론을 탐색합니다. 이런 일들이 수없이 많지만, 시스템 디자인의 일반적인 프로세스이기도 합니다. 목표는 단순하지만, 과정은 현란하죠.산업을 이해하면 목표가 보이고, 사람을 이해하면 디테일이 보인다(김석태|UX) 앞서 말씀드린 것처럼, 서비스 로봇은 다른 앱/웹 서비스와 상황이 많이 다르죠. 앱이라면 프로토타이핑과 검증 과정을 상당히 빠른 주기로 반복할 수 있는데, 로봇은 그런 면에서는 제약이 있습니다.일단 로봇 서비스 산업에 대한 이해부터 시작하였습니다. 그간 어떤 로봇들이 어떤 서비스를 했고, 학계에서는 어떤 연구들이 선행 되었는지를 꼼꼼히 연구했습니다. 그리고 나니 목표 수준이 좀 더 명확해지고, 시나리오를 구체화할 수 있었습니다.중요한 건 역시 사람에 대한 이해입니다. 실제로 유용하다고 느낄까? 어떤 니즈가 여전히 숨어있을까? 로봇이 대신 해 주었을 때 더 가치 있는 것은? 이런 질문에 대한 답을 찾은 후 다음 숙제가 이어집니다. 사람들의 삶 속으로 이질감없이 자연스럽게 녹아 들기 위한 인터랙션입니다. 인터랙션 상황들을 정의하는 일부터가 시작이고, 어떤 이슈나 문제가 있는지를 찾아냅니다. 가장 단순하면서도 자연스러운 해결 방법은 무엇일지 실험을 통해 검증합니다. 이 과정에서 굉장히 많은 디테일들이 새롭게 발견됩니다.기술에 대한 이해도 중요합니다. 예를 들어 최근 AROUND C에는 디자이너가 가장 이상적인 로봇의 속도 및 이동 경로를 선택하면, 이를 바탕으로 딥러닝 기술을 적용해 최적화된 자율주행을 할 수 있는 기술이 적용되어 있습니다. 지켜보는 사람이 언제 안정감을 느끼는지, 로봇과 사람이 교차할 때엔 상대 속도나 동선을 어떻게 할지, 공간상의 제약이 복합적으로 작용하면 어떤 기준을 세워야 할지 등등. 수많은 요소들 사이에서 최적의 인터랙션 디자인을 설계해야 합니다. 이런 사소해보이는 사용자 경험이 로봇 서비스 과정에서 뜻밖의 감동까지도 전달할 수 있다고 생각합니다.“우리가 추구하는 기본 방향은, 실용적이면서도 사람을 배려하는 로봇입니다. 문제 상황을 분석해 나온 다양한 해결책 중에, 사람이 직관적으로 파악할 수 있는 방법을 택합니다.” 최근에는 AROUND C에서는 gaze, sound, lighting을 통한 비언어적 커뮤니케이션을 테스트하고 있습니다. 왜 굳이 로봇이 직접 말하게 하지 않고 비언어적 커뮤니케이션을 연구할까요? 그게 서비스 시나리오 상에서 더 직관적이며, 심지어 더 똑똑해 보이기 때문입니다. 스타워즈의 R2D2와 C3PO를 떠올리시면 됩니다. 점과 선을 활용해 가장 로봇다운 눈을 디자인 했고, 이를 통해 다양한 상태 정보를 사람에게 직관적으로 전달하고자 했습니다.전체의 통일감과 개별 디자인의 완성도라는 두개의 과녁(김승우|ID) 제가 공을 들이는 건 전체 제품의 통일감과, 개별 디자인의 완성도입니다. 네이버랩스에서 그간 공개했던 제품들은 작은 디바이스부터 중형 로봇, 대형 차량 센서박스에 이르기까지 다양한 카테고리에 걸쳐 있습니다. 디자인의 토대가 되는 조형 요소인 제품의 크기와 형태, 구조가 상이하다 보니 각각의 형태와 구조적 특성을 고려하면서도 전체 제품에 통일감이 느껴지도록 하는데 많은 노력을 기울여 왔습니다. 기업에서 일관된 메시지를 전달하는 것은 그 기업을 신뢰할 수 있는가에 대한 중요한 가치라고 생각해요. 디자인도 마찬가지입니다. 네이버랩스라는 기술 기업에서 전달해야 할 가치는 ‘정밀함’과 ‘단단함’이라고 생각했고, 로봇을 포함한 전체 제품에서 이 키워드들을 담은 일관된 디자인 언어가 느껴질 수 있도록 조형의 기본이 되는 면, 면의 기본이 되는 선을 세밀하게 다듬으며 디자인했습니다.또한 개별 디자인의 완성도를 위해 밸런스와 디테일을 중요하게 생각합니다. 로봇은 움직이기 때문에 다양한 각도에서 바라보게 되고, 어느 방향에서 보아도 완성도 높은 밸런스가 특히 중요합니다. 잘 안보이는 곳의 디테일도 쉽게 드러나기 때문에 세밀한 디테일을 놓치지 않기 위해 노력하고요.로봇의 경우엔 일반인들의 디자인 완성도에 대한 기대 수준이 더 높은 편입니다. 이런 기대를 충족시키는 동시에 기술적인 요구도 충족해야 합니다. 예를 들어, AMBIDEX의 전체 디자인 균형을 잡는 과정에서 팔의 부피를 늘리는 선택이 필요했는데, 동시에 무게는 가볍게 유지해야만 로봇의 기능을 100% 발휘할 수 있었습니다. 경량성이 AMBIDEX라는 로봇 팔 기술의 핵심 특성이기 때문이죠. 외관 부피를 늘려 디자인 밸런스를 최적으로 잡으면서도 1g을 더 줄이기 위해 질량을 체크하며 표면과 두께를 조정하고, 강성을 높이는 내부 구조를 추가하며 문제를 해결했습니다. 이런 디자인 과정을 거쳤기에 외관에서도 내부의 단단함과 견고함이 배어 나온다고 생각합니다.Q. 서로 어떻게 협업을 하나요?어차피 목표는 하나(김인혁|Robot) 각기 다른 분야의 전문가들이 협업할 때의 견해차이는 프로세스를 통해 해결되어야 한다고 생각해요. 그게 아니라 의견의 일방향성이 생기면 그건 곤란하죠. 저는 각 분야의 선/후행을 두지 않고 초기부터 과정 전반에 걸쳐 계속 공유하고 의견을 나누며 서로의 수용성을 늘리는 것이 아주 중요하다고 생각해요.“한 영역의 전문가가 모든 결정을 하고 다른 분야의 전문가는 일방적으로 종속되어야 한다면, 그건 문제가 있습니다. 선행과 후행을 나누면 안됩니다. 초기부터 같이 고민하고 대화하고 함께 풀어야 합니다.” (김석태|UX) 저도 커뮤니케이션이 협업 과제를 빠르게 가속하는 가장 중요한 요소라고 봅니다. 다양한 관점에서 의견을 나누는 건 정말 필요해요. 그 과정 없이 한번에 이상적인 솔루션을 바라는 건 무리입니다. 지금 진행 중인 1784 프로젝트 역시 이러한 소통을 원활히 이어가고 있기 때문에 좋은 협업이 진행되고 있고요.(김승우|ID) 차이란 것은 자연스럽죠. 좋은 결과를 위해 필수적입니다. 궁극적인 목표를 달성하고자 한다는 동질감을 느끼기 때문에 서로의 진정성을 확인허는 과정이기도 합니다. 어떤 디자인이라도 많은 협의와 조율이 전제됩니다. 하나의 입장에 매몰되어 있는지 되돌아보기도 하고, 전체를 바라보는 기회로 삼기도 합니다.Q. 앞으로의 도전은?(김인혁|Robot) 우리의 목표는 사람에게 도움이 되는 로봇을 개발하는 것입니다. 단순하죠. 이를 기술 관점에서 고민하고, 가장 적합한 답을 찾고, 그 답을 세상과 공유하고 싶습니다. 그것이 제가 맡은 역할이라 생각하고요. 그 역할을 잘 할 수 있도록 연구개발자로서도, 프로젝트를 리드하고 완성하는 실무자로서도 역량에 깊이를 더하고 싶습니다.새로운 스탠다드라는 설레는 도전(김석태|UX) 이제는 실험실이나 전시장이 아니라, 우리가 실제 살아가는 공간으로 로봇이 들어옵니다. 그런 시대에 도달했습니다. UX디자이너로서는 완전히 새로운 기회이자 설레는 도전입니다. 한때 모바일이란 세상으로 패러다임이 이동했던 시기가 있었죠. 이제는 가상 세계에서 제공하던 다양한 서비스와 기술들이 일상의 물리 공간으로 다시 돌아올 것입니다. 서비스 로봇을 통해 이 분야의 새로운 스탠다드를 만들고 싶습니다.(김승우|ID) 네이버랩스에서는 늘 흥미로운 프로젝트들이 진행되어 왔습니다. 그 중에서도 로봇 디자인은, 다른 어느 로봇보다도 디자인 완성도가 높으며, 동시에 기능적 가치를 충실히 구현하는 것을 목표로 진행해 왔습니다. 게다가 로봇은 외관 그 자체가 하나의 강렬한 인상이자 브랜드 체험 요소가 되기 때문에 더욱 큰 책임감을 느끼고 있습니다. 네이버랩스는 기술이 강점인 회사입니다. 동시에 디자인 또한 우리의 탁월한 강점입니다. 이를 위해 앞으로도 노력하려고 합니다. 네이버랩스의 인재상은 passionate self-motivated team player입니다. 어쩌면 '자기주도적 팀플레이어'라는 말은 형용모순(形容矛盾)일 지도 모릅니다. 하지만 우린 계속 시도했고, 문화는 계속 쌓여갑니다. 다양한 분야의 전문가들이 경계없이 협력하고 스스로 결정하며 함께 도전하는 곳의 이야기를 전합니다. How to work at NAVER LABSH2W@NL 시리즈 전체보기
조회수 998

안드로이드 클라이언트 Reflection 극복기

비트윈 팀은 비트윈 안드로이드 클라이언트(이하 안드로이드 클라이언트)를 가볍고 반응성 좋은 애플리케이션으로 만들기 위해 노력하고 있습니다. 이 글에서는 간결하고 유지보수하기 쉬운 코드를 작성하기 위해 Reflection을 사용했었고 그로 인해 성능 이슈가 발생했던 것을 소개합니다. 또한 그 과정에서 발생한 Reflection 성능저하를 해결하기 위해 시도했던 여러 방법을 공유하도록 하겠습니다.다양한 형태의 데이터¶Java를 이용해 서비스를 개발하는 경우 POJO로 서비스에 필요한 다양한 모델 클래스들을 만들어 사용하곤 합니다. 안드로이드 클라이언트 역시 모델을 클래스 정의해 사용하고 있습니다. 하지만 서비스 내에서 데이터는 정의된 클래스 이외에도 다양한 형태로 존재합니다. 안드로이드 클라이언트에서 하나의 데이터는 아래와 같은 형태로 존재합니다.JSON: 비트윈 서비스에서 HTTP API는 JSON 형태로 요청과 응답을 주고 받고 있습니다.Thrift: TCP를 이용한 채팅 API는 Thrift를 이용하여 프로토콜을 정의해 서버와 통신을 합니다.ContentValues: 안드로이드에서는 Database 에 데이터를 저장할 때, 해당 정보는 ContentValues 형태로 변환돼야 합니다.Cursor: Database에 저장된 정보는 Cursor 형태로 접근가능 합니다.POJO: 변수와 Getter/Setter로 구성된 클래스 입니다. 비지니스 로직에서 사용됩니다.코드 전반에서 다양한 형태의 데이터가 주는 혼란을 줄이기 위해 항상 POJO로 변환한 뒤 코드를 작성하기로 했습니다.다양한 데이터를 어떻게 상호 변환할 것 인가?¶JSON 같은 경우는 Parsing 후 Object로 변환해 주는 라이브러리(Gson, Jackson JSON)가 존재하지만 다른 형태(Thrift, Cursor..)들은 만족스러운 라이브러리가 존재하지 않았습니다. 그렇다고 모든 형태에 대해 변환하는 코드를 직접 작성하면 필요한 경우 아래와 같은 코드를 매번 작성해줘야 합니다. 이와 같이 작성하는 경우 Cursor에서 원하는 데이터를 일일이 가져와야 합니다.@Overridepublic void bindView(View view, Context context, Cursor cursor) { final ViewHolder holder = getViewHolder(view); final String author = cursor.getString("author"); final String content = cursor.getString("content"); final Long timeMills = cursor.getLong("time"); final ReadStatus readStatus = ReadStatus.fromValue(cursor.getString("readStatus")); final CAttachment attachment = JSONUtils.parseAttachment(cursor.getLong("createdTime")); holder.authorTextView.setText(author); holder.contentTextView.setText(content); holder.readStatusView.setReadStatus(readStatus); ...}하지만 각 형태의 필드명(Key)이 서로 같도록 맞춰주면 각각의 Getter와 Setter를 호출해 형태를 변환해주는 Utility Class를 제작할 수 있습니다.@Overridepublic void bindView(View view, Context context, Cursor cursor) { final ViewHolder holder = getViewHolder(view); Message message = ReflectionUtils.fromCursor(cursor, Message.class); holder.authorTextView.setText(message.getAuthor()); holder.contentTextView.setText(message.getContent()); holder.readStatusView.setReadStatus(message.getReadStatus()); ...}이런 식으로 코드를 작성하면 이해하기 쉽고, 모델이 변경되는 경우에도 유지보수가 비교적 편하다는 장점이 있습니다. 따라서 필요한 데이터를 POJO로 작성하고 다양한 형태의 데이터를 POJO로 변환하기로 했습니다. 서버로부터 받은 JSON 혹은 Thrift객체는 자동으로 POJO로 변환되고 POJO는 다시 ContentValues 형태로 DB에 저장됩니다. DB에 있는 데이터를 화면에 보여줄때는 Cursor로부터 데이터를 가져와서 POJO로 변환 후 적절한 가공을 하여 View에 보여주게 됩니다.POJO 형태로 여러 데이터 변환필요Reflection 사용과 성능저하¶처음에는 Reflection을 이용해 여러 데이터를 POJO로 만들거나 POJO를 다른 형태로 변환하도록 구현했습니다. 대상 Class의 newInstance/getMethod/invoke 함수를 이용해 객체 인스턴스를 생성하고 Getter/Setter를 호출하여 값을 세팅하거나 가져오도록 했습니다. 앞서 설명한 ReflectionUtils.fromCursor(cursor, Message.class)를 예를 들면 아래와 같습니다.public T fromCursor(Cursor cursor, Class clazz) { T instance = (T) clazz.newInstance(); for (int i=0; i final String columnName = cursor.getColumnName(i); final Class<?> type = clazz.getField(columnName).getType(); final Object value = getValueFromCursor(cursor, type); final Class<?>[] parameterType = { type }; final Object[] parameter = { value }; Method m = clazz.getMethod(toSetterName(columnName), parameterType); m.invoke(instance, value); } return instance;}Reflection을 이용하면 동적으로 Class의 정보(필드, 메서드)를 조회하고 호출할 수 있기 때문에 코드를 손쉽게 작성할 수 있습니다. 하지만 Reflection은 튜토리얼 문서에서 설명된 것처럼 성능저하 문제가 있습니다. 한두 번의 Relfection 호출로 인한 성능저하는 무시할 수 있다고 해도, 필드가 많거나 필드로 Collection을 가진 클래스의 경우에는 수십 번이 넘는 Reflection이 호출될 수 있습니다. 실제로 이 때문에 안드로이드 클라이언트에서 종종 반응성이 떨어지는 경우가 발생했습니다. 특히 CursorAdapter에서 Cursor를 POJO로 변환하는 코드 때문에 ListView에서의 스크롤이 버벅이기도 했습니다.Bytecode 생성¶Reflection 성능저하를 해결하려고 처음으로 선택한 방식은 Bytecode 생성입니다. Google Guice 등의 다양한 자바 프로젝트에서도 Bytecode를 생성하는 방식으로 성능 문제를 해결합니다. 다만 안드로이드의 Dalvik VM의 경우 일반적인 JVM의 Bytecode와는 스펙이 다릅니다. 이 때문에 기존의 자바 프로젝트에서 Bytecode 생성에 사용되는 CGLib 같은 라이브러리 대신 Dexmaker를 이용하여야 했습니다.CGLib¶CGLib는 Bytecode를 직접 생성하는 대신 FastClass, FastMethod 등 펀리한 클래스를 이용할 수 있습니다. FastClass나 FastMethod를 이용하면 내부적으로 알맞게 Bytecode를 만들거나 이미 생성된 Bytecode를 이용해 비교적 빠른 속도로 객체를 만들거나 함수를 호출 할 수 있습니다.public T create() { return (T) fastClazz.newInstance();} public Object get(Object target) { result = fastMethod.invoke(target, (Object[]) null);} public void set(Object target, Object value) { Object[] params = { value }; fastMethod.invoke(target, params);}Dexmaker¶하지만 Dexmaker는 Bytecode 생성 자체에 초점이 맞춰진 라이브러리라서 FastClass나 FastMethod 같은 편리한 클래스가 존재하지 않습니다. 결국, 다음과 같이 Bytecode 생성하는 코드를 직접 한땀 한땀 작성해야 합니다.public DexMethod generateClasses(Class<?> clazz, String clazzName){ dexMaker.declare(declaringType, ..., Modifier.PUBLIC, TypeId.OBJECT, ...); TypeId<?> targetClassTypeId = TypeId.get(clazz); MethodId invokeId = declaringType.getMethod(TypeId.OBJECT, "invoke", TypeId.OBJECT, TypeId.OBJECT); Code code = dexMaker.declare(invokeId, Modifier.PUBLIC); if (isGetter == true) { Local<Object> insertedInstance = code.getParameter(0, TypeId.OBJECT); Local instance = code.newLocal(targetClassTypeId); Local returnValue = code.newLocal(TypeId.get(method.getReturnType())); Local value = code.newLocal(TypeId.OBJECT); code.cast(instance, insertedInstance); MethodId executeId = ... code.invokeVirtual(executeId, returnValue, instance); code.cast(value, returnValue); code.returnValue(value); } else { ... } // constructor Code constructor = dexMaker.declare(declaringType.getConstructor(), Modifier.PUBLIC); Local<?> thisRef = constructor.getThis(declaringType); constructor.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef); constructor.returnVoid();}Dexmaker를 이용한 방식을 구현하여 동작까지 확인했으나, 다음과 같은 이유로 실제 적용은 하지 못했습니다.Bytecode를 메모리에 저장하는 경우, 프로세스가 종료된 이후 실행 시 Bytecode를 다시 생성해 애플리케이션의 처음 실행성능이 떨어진다.Bytecode를 스토리지에 저장하는 경우, 원본 클래스가 변경됐는지를 매번 검사하거나 업데이트마다 해당 스토리지를 지워야 한다.더 좋은 방법이 생각났다.Annotation Processor¶최종적으로 저희가 선택한 방식은 컴파일 시점에 형태변환 코드를 자동으로 생성하는 것입니다. Reflection으로 접근하지 않아 속도도 빠르고, Java코드가 미리 작성돼 관리하기도 편하기 때문입니다. POJO 클래스에 알맞은 Annotation을 달아두고, APT를 이용해 Annotation이 달린 모델 클래스에 대해 형태변환 코드를 자동으로 생성했습니다.형태 변환이 필요한 클래스에 Annotation(@GenerateAccessor)을 표시합니다.@GenerateAccessorpublic class Message { private Integer id; private String content; public Integer getId() { return id; } ...}javac에서 APT 사용 옵션과 Processor를 지정합니다. 그러면 Annotation이 표시된 클래스에 대해 Processor의 작업이 수행됩니다. Processor에서 코드를 생성할 때에는 StringBuilder 등으로 실제 코드를 일일이 작성하는 것이 아니라 Velocity라는 template 라이브러리를 이용합니다. Processor는 아래와 같은 소스코드를 생성합니다.public class Message$$Accessor implements Accessor { public kr.co.vcnc.binding.performance.Message create() { return new kr.co.vcnc.binding.performance.Message(); } public Object get(Object target, String fieldName) throws IllegalArgumentException { kr.co.vcnc.binding.performance.Message source = (kr.co.vcnc.binding.performance.Message) target; switch(fieldName.hashCode()) { case 3355: { return source.getId(); } case -1724546052: { return source.getContent(); } ... default: throw new IllegalArgumentException(...); } } public void set(Object target, String fieldName, Object value) throws IllegalArgumentException { kr.co.vcnc.binding.performance.Message source = (kr.co.vcnc.binding.performance.Message) target; switch(fieldName.hashCode()) { case 3355: { source.setId( (java.lang.Integer) value); return; } case -1724546052: { source.setContent( (java.lang.String) value); return; } ... default: throw new IllegalArgumentException(...); } }}여기서 저희가 정의한 Accessor는 객체를 만들거나 특정 필드의 값을 가져오거나 세팅하는 인터페이스로, 객체의 형태를 변환할 때 이용됩니다. get,set 메서드는 필드 이름의 hashCode 값을 이용해 해당하는 getter,setter를 호출합니다. hashCode를 이용해 switch-case문을 사용한 이유는 Map을 이용하는 것보다 성능상 이득이 있기 때문입니다. 단순 메모리 접근이 Java에서 제공하는 HashMap과 같은 자료구조 사용보다 훨씬 빠릅니다. APT를 이용해 변환코드를 자동으로 생성하면 여러 장점이 있습니다.Reflection을 사용하지 않고 Method를 직접 수행해서 빠르다.Bytecode 생성과 달리 애플리케이션 처음 실행될 때 코드 생성이 필요 없고 만들어진 코드가 APK에 포함된다.Compile 시점에 코드가 생성돼서 Model 변화가 바로 반영된다.APT를 이용한 Code생성으로 Reflection 속도저하를 해결할 수 있습니다. 이 방식은 애플리케이션 반응성이 중요하고 상대적으로 Reflection 속도저하가 큰 안드로이드 라이브러리에서 최근 많이 사용하고 있습니다. (AndroidAnnotations, ButterKnife, Dagger)성능 비교¶다음은 Reflection, Dexmaker, Code Generating(APT)를 이용해 JSONObject를 Object로 변환하는 작업을 50번 수행한 결과입니다.성능 비교 결과이처럼 최신 OS 버전일수록 Reflection의 성능저하가 다른 방법에 비해 상대적으로 더 큽니다. 반대로 Dexmaker의 생성 속도는 빨라져 APT 방식과의 성능격차는 점점 작아집니다. 하지만 역시 APT를 통한 Code 생성이 모든 환경에서 가장 좋은 성능을 보입니다.마치며¶서비스 모델을 반복적으로 정의하지 않으면서 변환하는 방법을 알아봤습니다. 그 과정에서 Reflection 의 속도저하, Dexmaker 의 단점도 설명해 드렸고 결국 APT가 좋은 해결책이라고 판단했습니다. 저희는 이 글에서 설명해 드린 방식을 추상화해 Binding이라는 라이브러리를 만들어 사용하고 있습니다. Binding은 POJO를 다양한 JSON, Cursor, ContentValues등 다양한 형태로 변환해주는 라이브러리입니다. 뛰어난 확장성으로 다양한 형태의 데이터로 변경하는 플러그인을 만들어서 사용할 수 있습니다.Message message = Bindings.for(Message.class).bind().from(AndroidSources.cursor(cursor));Message message = Bindings.for(Message.class).bind().from(JSONSources.jsonString(jsonString));String jsonString = Bindings.for(Message.class).bind(message).to(JSONTargets.jsonString());위와 같이 Java상에 존재할 수 있는 다양한 타입의 객체에 대해 일종의 데이터 Binding 기능을 수행합니다. Binding 라이브러리도 기회가 되면 소개해드리겠습니다. 윗글에서 궁금하신 점이 있으시거나 잘못된 부분이 있으면 답글을 달아주시기 바랍니다. 감사합니다.저희는 언제나 타다 및 비트윈 서비스를 함께 만들며 기술적인 문제를 함께 풀어나갈 능력있는 개발자를 모시고 있습니다. 언제든 부담없이 jobs@vcnc.co.kr로 이메일을 주시기 바랍니다!
조회수 1727

빨간약 줄까, 파란약 줄까?

버닝맨에 참가하는 사람들을 버너(Burner)라 부른다. 사람의 형상(Man)을 한 조형물을 축제의 끝자락에 불로 태우는 의식(Man Burn)이 버닝맨(Burning man)이라는 이름을 만들었고, 그래서 이 곳에 참가하여 함께 태우는 사람들이 버너라 불리우게 된다.버너들에게는 독특한 세계관이 있다. 세상이 둘로 나눠져 있다는 것인데 하나는 태어나면서 저절로 속해진 세상, 내가 선택하지 않았으나 저절로 일원이 되어 살아가야하는 세상을 디폴트월드(Default world)라 부르고, 다른 하나, 내가 스스로 선택한 진짜 세상을 리얼월드(Real world)라 부른다. 지금 우리가 살고 있는 세상은 내가 선택하지 않았지만 태어나면서 저절로 속해졌으니 이 또한 디폴트월드이다.뭔가 익숙한 느낌이 든다면 바로 영화 매트릭스(Matrix)를 기억해서 일 것이다. 모피우스(Morpheus)가 네오(Neo)에게 빨간약과 파란약 중에 하나를 선택하라고 요구하는 장면이 나온다. 파란약을 먹으면 지금 살고있다고 믿고있는 세상의 침대에서 깨어나 지금까지 살아 온 대로 살게되고, 빨간약을 먹게 되면 뭔가 믿기 힘들겠지만 진짜 세상을 보게된다는 말에 네오는 빨간약을 삼킨다. 영화 매트릭스에서의 디폴트월드는 지금 우리가 살고 있는 세상처럼 성공하기 위해 경쟁하고, 승진하기 위해 애를 쓰고, 행복하기 위해 열심히 살기도 하는 곳이었으나, 실상 진짜 세상은 에너지를 공급하며 꿈만 꾸면서 갇혀서 살고 있는 매트릭스(Matrix)안이었다.출처: http://www.balancedis.comYou take the blue pill – the story ends, you wake up in your bed and believe whatever you want to believe. You take the red pill – you stay in Wonderland and I show you how deep the rabbit-hole goes.– Morpheus버너들은 이렇게 스스로 선택하지 않은 디폴트월드에서 일년의 거의 대부분인 354일을 살고 있기에 버닝맨이 열리는 블랙락(Black rock)사막에 오는 단 며칠을 리얼월드로 돌아오는 것이라 생각한다. 디폴트월드에서는 다른 사람들의 눈치도 보고, 이기기 위해 경쟁도 하고, 먹고 살기위해 야근도 하며, 남들이 뭐라 할까봐 자신의 내면을 드러내지도 않고 살지만, 리얼월드 버닝맨에 오면 나자신으로서, 그 어떤 것이라도 표현하며 온전히 나자신으로 존재 할 수 있기에 그들은 이 사막을 홈(Home)이라 부른다.홈에서는 오롯이 나로 존재하고 나를 표현할 수 있기에 동화에 나오는 코스튬을 입던, 게임이나 영화에 나오는 복장을 하던, 아니면 아무것도 입지 않고 돌아다니던 그 모든 것은 100% 자유이다. 관광객모드가 아닌 대부분의 참가자들은 다른 사람이 무엇을 입던, 입지 않던 자연스럽게 생각하려고 하며 그 모든 것을 당연하게 인정한다. 다만 한가지 중요한 그라운드룰은 나만큼 다른 사람 하나하나도 각각으로 표현하고 존재할 수 있는 곳이기에 나의 어떠한 행동으로 인해 다른 사람의 그 존재와 자유에 침해가 되는 것만큼은 용납되지 않는다. 각각의 개인 하나하나가 자기 자신으로 존재하며 표현하지만 그 모든 사람들의 다양성이 그 자체로 공존할 수 있는 곳이 바로 버닝맨인 것이다.  하지만 영화 매트릭스에서 네오도 스스로를 깨닫고 초월적인 존재가 되기까지 자기부정의 시간과 깨달음의 시행착오가 있었듯 버닝맨에 참여한다고 모두가 저절로 자기 자신이 되며 온전한 존재로 살게 되지는 않는다. 오히려 혼동스럽고 잘못된 표현과 행동으로 상처입거나 디폴트월드와의 괴리로 인생의 방향성을 잃게 될 수도 있다. 버닝맨에 참여하는 모든 사람들이 깨달음을 얻는 것도 아니며, 자기주체적이 되는 것도 아니다. 영화 매트릭스에서 모피우스가 비슷한 말은 한다. 자신이 해 줄 수 있는 유일한 것은 문을 보여주는 것 뿐이며, 그것을 통해 온전한 존재까지의 여정은 스스로 겪어내야 한다고 말이다. 버닝맨도 마찬가지이다. 단지 모든 다양성들이 함께 존재할 수 있고 함께, 또는 홀로 경험하고 실험할 수 있는 모든것이 용인되는 곳일 뿐 진정한 리얼월드를 인지하고 살아나가는 것은 각각의 몫인 셈이다.    I’m trying to free your mind, Neo. But I can only show you the door. You’re the one that has to walk through it.– Morpheus그래서 2016년 처음으로 버너가 된 내가 이제 막 겪어내기 시작했고 깨닫기 시작한 작은 경험과 생각들을 계속 나눠볼까 한다. 리얼월드의 존재와 함께 그 곳이 존재할 수 있게 된, 그리고 31년간 지속되어 올 수 있었던 비밀을 함께 엿볼수만 있다면 그것만으로도 충분히 가치가 있을테니 말이다. 아직은 불확실하고 성숙되지 않았으며 깊이가 없는 생각이지만, 진정한 리얼월드가 어디인지, 난 누구이며 무엇을 위해 살아가야 하는지, 그리고 온전한 나는 어떤 존재인지 먼 여정을 걸어가는 동안 이 글들이 누군가에게 모피우스가 남긴 조각들처럼 발견되는 작은 기적을 바래본다.#라이프스퀘어 #스타트업 #창업자 #창업가 #마인드셋 #조언
조회수 1184

아마존 유럽 - VAT 시리즈 (2)

★02★Q: VAT 가입, 꼭 해야하나요? (영국 기준)A: 공통 답변 - VAT 가입은 판매 방식에 따라 해도 되고 안해도 되지만, 가급적이면 어떤 식으로 판매를 하더라도 VAT 가입할 것을 추천드립니다.아래에 구체적으로 설명되어 있지만, FBM으로 판매하든 FBA로 판매하든 VAT 납부는 피할 길이 없습니다. VAT 가입 여부와는 무관하게 VAT (부가세) 자체는 발생합니다. 그리고 심지어 그걸 내야하는 의무는 여러분입니다 - 아마존 고객들이 아닙니다. 많은 분들이 착각하는 게, FBM으로 보내면 수취인이 관부가세를 납부하도록 하면 된다고 생각을 하시는데, 전혀 아닙니다... FBM으로 보내도, 상세페이지에 관부가세는 수취인 부담을 원칙으로 한다고 써놓더라도, 세금 관련 발생한 고객 클레임에 대해 여러분이 아무리 아마존에 항변을 해도, 아마존 판매자 약관 내용 중에 the price you display on amazon must be inclusive of all duty/tax 라는 내용이 있고 여러분은 입점할 당시 이 약관 내용에 대해 동의를 했기 때문에 결국 아마존에서 발생하는 모든 주문, 모든 발송 되는 FBM 화물에 대하여 여러분이 관부가세를 내야하는 책임이 있습니다.A: 아니요 - 영국에 법인을 설립하면 85,000 파운드 누적 매출까지는 VAT 가입/신고/납부가 면제됩니다.영국에 법인을 설립하는 경우 85,000 파운드의 매출까지는 VAT 가입/신고/납부가 면제됩니다 (한국 기준으로 본다면 간이사업자와 같은 혜택이라고 볼 수 있습니다). 대한민국 사업자가 영국 법인을 설립하는 게 쉽거나 저렴한 것도 아니라서 이건 그냥 패스하겠습니다.A: 아니요 - 모든 주문을 FBM으로만 처리하는 경우에는 VAT 가입 없이 아마존 UK에서 판매할 수 있습니다 (비추천).아마존 UK에서 발생하는 모든 주문을 FBM (fulfilled by merchant 한국 직배송)으로 처리하고 모든 발송물들에 대해 DDP 무역조건으로 보내서 세금 문제도 발송인(여러분)이 해결한다면, VAT 가입 없이 아마존 UK에서 판매 활동을 할 수 있습니다. 하지만 FBM으로만 판매한다고 하더라도 VAT 가입/신고/납부 하는 것을 추천드립니다. 어차피 세금 공제 혜택을 제대로 누리기만 한다면 VAT 가입 하고 판매 활동을 하는 것이나 VAT 가입 안하고 판매 활동을 하는 것이나 수익성이 똑같을 뿐더러, 나중에라도 해외 셀러들에 대한 영국 법이 바뀌거나 아마존 정책이 바뀔 때 걱정할 일이 없습니다.A: 네 - 주문 중에 일부라도 FBA로 처리하는 경우에는 VAT 가입을 반드시 해야 아마존 UK에서 판매할 수 있습니다.아마존 UK에서 발생하는 주문 중에 일부라도 FBA(fulfilled by amazon 아마존 배송대행)으로 처리한다면 VAT 가입을 해야 아마존 UK에서 판매 활동을 할 수 있습니다.추가 설명 및 근거(위 답변에 대한 구체적인 이유가 궁금하신 분들만 읽으세요.설명이 매우 복잡하고 깁니다.)제가 아래에 드릴 모든 설명은 영국 국세청 (HMRC)에서 발표한 자료를 근거로 합니다. 아래 링크에 나온 자료를 전부 꼼꼼하게 읽어보신다면 영국 VAT 개념에 대해 매우 많은 부분을 이해하실 수 있을겁니다.https://www.gov.uk/government/publications/vat-notice-7001-should-i-be-registered-for-vat/vat-notice-7001-should-i-be-registered-for-vat#Taxable-supplies하지만 20~30페이지 되는 분량의 영어 자료를 자세히 읽고 이해하는 것이 쉽진 않을거라 생각합니다. 영어 울렁증이 있으신 분들, 그리고 시간적 여유가 없으신 분들을 위해 위 링크의 모든 내용을 제가 대신하여 매우 자세하게 읽어봤고, 이제 여러분들처럼 아마존 유럽에 판매하고자 하는 대한민국 사업자의 입장에서 모든 관점을 맞추고 최대한 쉽게 풀어서 한국어로 설명드리고자 합니다. 즉, 여러분들은 제가 아래에 정리해드린 특정 내용만 눈여겨 보시면 충분히 이해가 되실겁니다.요점 #1-1: 대한민국 사업자는 HMRC 입장에서 바라볼 때 NETP (non-established taxable person)입니다.'NETP가 뭔가요?' 가장 먼저 알아야할 것은 NETP의 정의입니다.위에서 보이듯이, NETP라는 것은 영국에 통상적으로 거주하지 않고 영국 내 부동산이 없으며, 기업의 경우 영국 내에서 설립한 회사가 아닌 기업을 보고 NETP라고 합니다. 이 정의대로라면, 너무 당연한 얘기지만, 대한민국 사업자는 NETP입니다.요점 #1-2: NETP의 VAT 가입 의무 기준이제 대한민국 사업자가 NETP라는 것을 알았다면 다음으로 짚고 넘어가야할 것은 NETP의 VAT 가입 의무 기준입니다.위에서 보이듯이, NETP로 분류되는 주체는 Taxable Supplies라는 것이 영국 내에서 발생하게 되면 VAT 가입 의무가 생깁니다. 'Taxable Supplies? 그게 도대체 뭐죠?' 다음으로 알야할 것은 Taxable Supplies의 정의입니다.Taxable Supply란, '영국 안에서 발생한 그 어떤 Supply(과세/영세 포함)'를 말하는데, 이 Supply라는 개념에 대해서도 좀 더 깊이 파고들면 'Supply is the sale of goods or services' 즉, 제품이든 서비스든 그 어떤 것을 '판매하는 것'을 말합니다. 그럼 다시 돌아가서 Taxable Supply라는 것은 과세든 영세든 그 어떤 제품이든 서비스든 '영국 안에서 발생한 매출'이라고 정확히 정의할 수 있습니다.정리하자면...Taxable Supply = 영국 안에서 발생한 그 어떤 SupplySupply = 제품이든 서비스든 그 어떤 것을 판매하는 것 (다른 말로 '매출')Taxable Supply = 영국 안에서 발생한 그 어떤 '매출'그리고 위 사진에서 가운데쯤에 적색 박스로 칠해진 부분을 보시면 'if you are an NETP, and you make any taxable supplies in the UK, then you must register for VAT'라고 되어있습니다. 이제 여러분은 대한민국 사업자로서 이미 NETP라는 것을 알고 있습니다. 그럼 NETP인 여러분과 제가 아마존 UK라는 시장에서 판매한다는 것은 과연 NETP로서 '영국 안에서 매출을 발생하고 있는것인가'에 대한 결론을 내릴 수 있다면 '대한민국 사업자가 아마존 UK에 판매하기 위해서 VAT 가입이 필수이냐'에 대한 확실한 답변을 내릴 수가 있습니다. 하지만 이게 쉽지 않더라고요... 왜냐하면...여기서 저도 개인적으로 굉장히 헷갈렸던 부분이 바로 'supply made in the UK' 즉 영국 안에서 발생한 매출이라는 개념인데, 여러분과 저 같은 대한민국 사업자들이 아마존 UK라는 온라인 마켓플레이스에서 매출이 발생한거라면, 과연 이게 '영국 안에서 발생한 매출인가?'라는 질문을 하게 되었습니다.그러다보니 굉장히 재미있는 일이 벌어지는데요, 대한민국 사업자의 입장에서 생각해본다면, 아마존의 주문 이행 방식은 FBA와 FBM 두 가지 방식이 있는데, FBA의 경우 고객이 주문을 하는 그 순간을 놓고 봤을 때 (매출 발생 시점), 제품 재고가 영국 현지 안에 있는 상태에서 판매가 이루어지는 것이기 때문에 대한민국 사업자가 FBA로 판매하는 경우 해당 판매 건은 '영국 안에서 발생한 매출'로 간주되는 것은 어찌보면 당연한 것입니다. 다른 말로, FBA로 판매하는 대한민국 사업자는 VAT 가입 의무가 있는 것입니다. 하지만 FBM의 경우는요? FBM의 경우 고객이 주문을 하는 그 순간을 놓고 봤을 때 (매출 발생 시점), 제품 재고가 한국에 존재하는 상태에서 판매가 이루어지는 것이기 때문에 엄밀히 말해서 'supply made in the UK' (영국 안에서 발생한 매출)가 아니라 'supply made in Korea' (한국 안에서 발생한 매출)가 되는 것입니다. 다른 말로, FBM으로 판매하는 대한민국 사업자는 VAT 가입 의무가 없는 것입니다 (이것은 저도 100% 확실하지는 않지만 굉장히 많은 리소스들을 검토해보고 확인해본 결과 제 최종적인 개인 의견이며, 조금 이따가 밝힐 HMRC의 Overseas Seller의 정의와도 부합합니다).기타 드릴 말씀: 물론, FBM으로 판매하는 대한민국 사업자가 VAT 가입 의무가 없다고 해서, 발생한 매출에 대한 VAT 납부 의무가 없다는 것은 절대 아닙니다. 이 두 가지는 엄연히 다른 것입니다. 매출이 발생했다는 것은 어쨌거나 거래가 일어났다는 것이기 때문에 부가세가 발생합니다 (FBM의 경우에서는 그 거래가 한국 >>> 영국이 되는거라 화물이 영국에 수입 되는 시점에 발생하는 수입 부가세를 납부하는 것에 대한 의무는 여전히 있다고 생각하시면 됩니다). 이 부가세는 판매자이자 발송인인 여러분이 부담해야하기 때문에 (아마존의 모든 최종 판매가는 all duty/tax inclusive라는 내용의 아마존 판매자 약관 참조), FBM으로 모든 주문을 처리할 경우, 모든 화물은 DDP 무역조건으로 발송하셔야한다는 것을 필히 숙지하시기 바랍니다 (이따가 자세히 설명합니다).요점 #2-1: 대한민국 사업자는 HMRC에서 바라볼 때 Overseas seller입니다.위에 언급한 컨셉 (제 개인 추측)을 뒷받침 해주기도 하며 연관성이 깊은 두 번째 개념입니다. 이번에도 먼저 Overseas Seller의 정의에 대해 정확하게 알아보겠습니다.위에서 보이듯이, Overseas Seller라는 것은 영국에 재고가 존재하는 상태에서, 온라인 매개체를 통해 영국 소비자들에게 판매하는, 영국에 설립되지 않은 기업을 말합니다. 그렇다면 아마존 UK에 판매하고자 하는 대한민국 사업자의 입장에서 바라보는 이 문구는 'FBA를 이용해서 판매하는 대한민국 사업자들을 일컫는 말이 Overseas Seller'라는 것을 알 수 있습니다. 앞서 요점 #1에서 이미 충분히 설명했지만, Overseas Seller, 즉 FBA를 통해 판매하는 대한민국 사업자들은 고객이 주문을 하는 순간을 놓고 봤을 때 (매출 발생 시점), 제품 재고가 영국 현지 안에 있는 상태에서 판매가 이루어지는 것이기 때문에 '영국 안에서 발생한 매출'로 간주됩니다.결론적으로...Overseas Seller = 'NETP의 입장으로서 영국 안에서 Taxable Supply가 발생하는 것'이 공식이 성립되므로, VAT 가입 의무가 생기는 것입니다.또한 결론적으로...FBM으로 판매하는 셀러 = 'NETP의 입장이긴 하지만, 영국 안에서 Taxable Supply가 발생하는 것은 아님'이 공식이 성립되므로, VAT 가입 의무가 없는 것입니다.
조회수 488

출퇴근기록 시스템 도입. 꼭 따져보자

귀하의 비즈니스에 도입할 적합한 도구 또는 시스템을 찾는 것은 간혹 어려운 프로젝트가 될 수 있습니다. 운영방침에 맞는 완벽한 소프트웨어 검색은 그 자체만으로도 매우 시간 소모적인 작업입니다. 몇 가지 옵션을 찾았다고 하더라도 이는 시작에 불과합니다. 많은 사장 또는 관리자는 직원들의 출퇴근기록을 위해 POS 내의 근태관리 서비스, 인트라넷, 펀치 카드 등과 같이 쉽게 찾을 수 있는 서비스를 선택하곤 합니다. 요즘처럼 이미 많은 업무들이 디지털화된 시대에 모바일 앱을 통해 근무표 스케줄링이나 출퇴근기록을 할 수 있다면 편리하고 매력적인 옵션이 될 수 있습니다.부적절한 출퇴근기록 시스템을 도입하면 무엇이 잘못될 수 있까요?신뢰성 저하: 직원은 동료를 위해 대신 출퇴근해 줄 수도 있습니다.관리 권한 불충분: 시스템에 액세스 레벨이 명확하게 구분되지 않는 경우, 누구나 출퇴근기록에 접근하여 수정할 수 있으므로 효과적이지 않습니다.다수 지점 지원: 둘 이상의 매장을 운영하십니까? 도입한 출퇴근기록 시스템이 다수 지점을 지원하지 않으면 각 매장마다 출퇴근기록 자료를 수집하는 것이 불편합니다.낮은 접근성: 어디서나 시스템에 접속할 수 있습니까? 말 그대로, 관리자가 스마트폰이나 자택의 PC에서도 출퇴근기록을 관리할 수 있나요? 출퇴근기록을 정정하거나 보고서를 만들기 위해 내일 아침까지 기다리는 당신을 상상해보세요.리포트/급여정산 업무: 기록된 출퇴근시간만 출력해 볼 수만 있는 출퇴근기록 시스템을 사용하고 있나요? 리포트 기능이 없으면 사용자가 직접 보고서 및 급여정산 작업을 수행하게됩니다.이 외에도... 목록은 계속됩니다.시프티는 위의 출퇴근기록 문제를 모두 해결할 뿐만 아니라 근무일정 스케줄링 및 급여정산까지도 서비스합니다. 근무일정과 실제 출퇴근기록을 대조하여 어느 직원이 근면하고 태만한지도 분석할 수 있습니다. 각종 출퇴근 및 지각 알림 기능은 보너스!시프티 - 근무일정 스케줄러 및 출퇴근기록기의 장점:다수 지점 관리가 가능합니다. (자세히 알아보기)총괄관리자와 지점관리자 중 관리자 액세스 레벨을 배정할 수 있습니다. (자세히 알아보기)직원들의 근무일정 스케줄링직원들이 자신의 스마트폰을 이용하여 출퇴근기록을 하도록 합니다.근태 보고서를 열람하거난 시급직원에 대한 급여정산을 처리할 수 있습니다.계획한 근무일정이 직원들에게 실시간 공유됩니다.출퇴근기록 활동에 대한 알림을 받습니다. (출근/퇴근, 지각, 초과근무 알림)복잡하고 어려운 직원관리 업무도 적합한 시스템을 도입한다면 많은 시간을 절약 할 수 있습니다.시프티의 출퇴근기록기에 대해 자세히 알아보세요.#시프티 #고객가치 #핵심가치 #기업소개 #서비스소개

기업문화 엿볼 때, 더팀스

로그인

/