스토리 홈

인터뷰

피드

뉴스

조회수 925

2017 NDC 리뷰) 몬스터 슈퍼리그 리텐션 프로젝트

 2017년 4월 25일부터 27일까지 진행된 Nexon Developer Conference 에 나녀온 후기입니다.제가 들었던 재밌는 세션들 하나하나 올릴 테니 기대해 주세요! :)2017 NDC 재밌었다능!!!몬스터 슈퍼리그 리텐션 15% 개선 리포트 - 숫자보다 매력적인 감성 테라피"몬스터 슈퍼리그"의 게임 리텐션 개선 리포트였는데요, 기본적인 서비스의 소비자를 향한 어프로치인"당신은 똑똑한 유저!"라는 인식 심기(쉬운 접근성/ 심도 깊은 진행 유도)"빠른 어필"(이벤트에 대한 빠른 피드백)"축복받은 계정" (다양하고 많은 초반 보상)이라는 인식과,"주어지면 알아서 하겠지""보상이 있으면 알아서 하겠지"라는 생각을 지양하고, "의도하지 않았지만 수행을 할 수 있도록" 하는 넛지(Nudge) 효과를 일으킬 수 있는 무의식을 자극하는 재밌는 전략에 대해서 흥미를 느꼈습니다. 그리고 이후 리텐션 강화를 위한 프로젝트로 가장 중요한 건,단지 "무슨 기능을 만들 것이냐?"가 아닌, "어떤 부분에서 유저가 이탈"하게 되고,이탈한 유저들 중 "우리가 진짜 챙겨야 할 유저"가 어떤 유저들인가에 집중한다.라는 부분에서 항상 우선돼야 하지만, 되지 못한 부분들을 생각하게 되었던 것 같아요. 그래서 이를 통해스스로 모험 입장을 몇 번 한 유저: 원하는 것에 접근하지 못한 유저에 대한 파악 후 개선다음 지역에 접근 한 유저: 지속 플레이 의향 있으나, 니치를 못 찾은 유저들의 의도 파악 후 개선이라는 개선에 필요한 정확한 목표를 가지고, 어떤 방식으로 접근해야 하는지에 대한 고민을 보는 것에 정말 재미를 느꼈고요, 이에 대한 진행방향을 듣는 것도 정말 값진 경험이었습니다.개선 시퀀스1차 개선 (-)보상 10배 상향에도 불구, 큰 성장 없음.>"가치비교가 익숙하지 않은 유저들에게 보상의 절대적 수치 증가는 큰 감흥이 없다."라는 점을 파악하고, 유저에 "감정"을 터치하는 방법을 고안.2차 개선 (+ & -)조사 결과, 첫 패배 지점에서 유저들의 높은 이탈률을 파악> 패배 지점을 인위적으로 미루지 않되, 패배에 신경 쓰지 않도록 다른 부분들에 대한 장치를 추가.> "도전 가능한 포인트를 생성하는 것은 유효하다."는 부분을 Metric으로 확인했으나, 타깃 유저 범위를 정확하게 파악하지 않고  Metric만 집중해서 정확한 범위 파악을 놓침.3차 개선 (+ & +)유저가 얻을 수 있는 보상의 기회를 꼭 찾아가도록 유도옵션 1. 텍스트 강조? 텍스트는 망각의 영역 (X)옵션 2. 강제 터치? 이미 자유 플레이가 된 유저에게 부자연스러운 접근 (X)옵션 3. 얻고 싶은 보상이 있다면, 어떨까? 그리고 보상 등에 대해서 스토리 텔링이 될 수 있다면?  (O)  - 부정 경험 개선을 통한 리텐션 향상 효과  - 초반에 한 일을 다시 하게 하는 것은 큰 부정 경험을 초래  - 강제적 이동보다는 원하는 보상을 통해 부여4차 개선 (+)스토리 텔링 요소 추가  1. 일러스트  2. 캐릭터에 대한 스토리 추가글로벌 원빌드로서 북미권 영역에서  특히 추가결과개선 프로세스 이후,"무언가가 무조건 있다."라는 이야기 보단, "기대치 않은 행동에 대해서 얻는 보상의 획득"으로 유저의 감성을 자극하는 스토리 텔링의 중요성 확인.교훈보상도 주지만, "보상을 준다"라는 이벤트를 행하는 것 만으로 서비스 제공자는 끝내선 안된다. 보다 감성적인 접근을 통해 유저의 감정을 이해하는 것이 더 중요하다. 첫날 첫 번째 세션이었는데요, 아침부터 정말 보람찬 세션 들을 수 있어서 정말로 감사했습니다. 사실, 게임이건, 모바일 서비스건, 웹 서비스건 "소비자를 이해한다."라는 부분은 언제 어디서나 필요한 부분이지만, 결과적으로 서비스 제공자들은 "보상을 제공했다."로 서비스 제공을 스스로 끝을 내버리는 순간들을 더 많이 마주하기 때문에, 다양한 분야들에서 생각해 볼만한 이야기라고 생각합니다. 한줄평: 중요한 건, "내가 이런 걸 줬다!"보다는 "이런 걸 줘서 고마워"라는 것을 느낄 수 있도록 소비자의 마음에 초점을 맞추는 서비스 제공이 맞다!#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 1470

Semantic Versioning 소개

Semantic Versioning 소개Versioning?소프트웨어 개발 생태계는 수많은 사람들이 서로의 기술과 성과를 이어받아 오며 믿을 수 없는 수준의 협력 체제를 구축해오고 있습니다. 의존성은 이러한 협력체제에서 나오게 된 요소로, 다른 사람들이 만들어온 기능을 다시 만들 필요 없이 손쉽게 가져와서 재활용하는 방식으로 빠르게 소프트웨어를 만들 수 있게 되었습니다.하지만 이렇게 여러 사람에게 이용되는 패키지가 새롭게 업데이트될 때, 생각보다 다양한 문제에 직면하게 되었습니다. 기능의 사용법을 바꾸어버리거나 동작 방식의 변경 같은 변화들은 그에 의존하는 다른 소프트웨어를 의도대로 동작하지 못하게 하므로, 새로운 변화와 기존의 것을 구분할 필요가 생겼습니다. 버전이라는 개념은 이러한 패키지의 변화를 구분하기 위해 사용하기 시작하였습니다.Semantic Versioning?버전이라는 코드 형태의 구분방식은 많은 핵심 문제를 해결해주었지만, 아직 여러 과제가 남아있었습니다. 버전 명의 작성 방식에 관한 기준이 패키지마다 제각각 다른 것이 문제였습니다. 0.x와 1.x의 차이, 1.0.0 혹은 1.000. 선행 배포와 정식 버전의 구분 방법 등 모든 소프트웨어, 패키지는 저마다의 기준을 가지고 있었으며, 이는 어느 정도의 적당한 공통점이 있었지만, 그 점이 미묘하게 모두 차이가 있어 버전에 따른 의미 해석을 어렵게 하였습니다.Semantic Versioning은 Github의 공동창업자인 Tom Preston-Werner가 위의 문제를 해결하기 위해 기존의 현안을 모아 만든 제안입니다. 스펙 문서는 RFC 2119에 의해 규칙을 표기하여 의미적 엄격함을 높이고, 패키지 개발 생명주기에 발생할 수 있는 여러 상황을 포괄적으로 담아 일관성과 유연성을 균형 있게 갖추고 있습니다.규칙다음은 Semantic Versioning(v2.0.0-rc1)의 스펙을 한국어로 번역한 내용입니다.1. Semantic Versioning을 쓰는 소프트웨어는 반드시 공개 API를 정의해야 한다. 이 API는 코드 자체에 정의되어 있거나 명시적으로 문서화 되어있어야 한다. 이 과정은 포괄적이며 정확해야 한다.2. 일반 버전 명은 반드시 X.Y.Z 형태를 보여야 하며 X, Y, Z는 음이 아닌 정수이다. X는 주요한 버전이며, Y는 작은 버전, Z는 패치버전이다. 각 요소는 1씩 차례로 증가해야 한다. 예: 1.9.0 -> 1.10.0 -> 1.11.0.3. 주요 버전 숫자가 올라갈 때, 작은 버전 숫자와 패치 버전 숫자는 0으로 재설정되어야 한다. 작은 버전 숫자가 올라갈 때, 패치 버전 숫자는 0으로 재설정되어야 한다. 예: 1.1.3 -> 2.0.0, 2.1.7 -> 2.2.04. 버전 명이 주어진 패키지가 한번 공개되면, 해당 버전의 내용은 절대 수정되어선 안된다. 어떤 수정도 반드시 새로운 버전으로 공개되어야 한다.5. 주요 버전 0 (0.y.z)은 초기 개발을 위한 것이다. 언제든 변경될 수 있다. 공개 API는 안전하지 않다고 여긴다.6. 버전 1.0.0은 공개 API를 정의한다. 이 공개 이후의 버전 숫자가 바뀌는 방법은 공개 API와 변경 방법에 따라 결정된다.7. 패치 버전 Z (x.y.Zx > 0)는 하위호환을 하지만 버그 수정이 있을 때 올라간다. 버그 수정은 내부적으로 잘못 처리되고 있는 것을 고치는 것을 의미한다.8. 작은 버전 Y (x.Y.zx > 0)는 새로운 기능이 추가되었지만 기존의 공개 API가 하위호환되고 있을 때 올라간다. 공개 API가 하나 이상 deprecated될 시에도 올라가야 한다. 부가적인 새 기능이나 개선이 내부 코드 (private code)에 있을 시에도 올릴 수 있다. 이는 패치 수준의 변화를 포함할 수 있으나, 작은 버전이 올라가면 패치 버전은 꼭 0이 되어야 한다.9. 주요 버전 X (X.y.zX > 0)는 하위호환되지 않는 변화가 추가될 때 반드시 올라가야 한다. 이는 패치 수준과 작은 수준의 변화를 포함할 수 있으나, 주요 버전이 올라가면 작은 버전과 패치 버전은 꼭 0이 되어야 한다.10. 선행 배포 버전은 대시(-)와 점으로 나누어진 식별자들의 묶음을 패치 버전 뒤에 표시한다. 식별자들은 ASCII 영숫자와 대시로만 구성되어야 한다. [0-9A-Za-z-]. 선행 배포 버전은 연관된 일반 버전보다 낮은 우선순위를 가진다.11. 개발 버전은 더하기(+)와 점으로 나누어진 식별자들의 묶음을 패치 버전 뒤에 표시한다. 식별자들은 ASCII 영숫자와 대시로만 구성되어야 한다. [0-9A-Za-z-]. 빌드 버전은 연관된 일반 버전보다 높은 우선순위를 가진다.12. 우선순위는 주요, 작은, 패치, 선행 배포, 빌드 식별자 내 숫자 순으로 계산되어야 한다. 주요, 작은, 패치 버전은 항상 숫자로 비교되어야 한다. 선행 배포와 빌드 버전의 우선순위는 반드시 각 점으로 나누어진 식별자들이 아래 규칙에 따라 비교되어야 한다: 1. 숫자로만 이루어진 식별자는 숫자로 비교 (2) 문자와 대시가 포함된 식별자는 ASCII 정렬 순서대로 비교. 숫자 식별자는 숫자가 아닌 식별자보다 낮은 우선순위를 가진다. 예: 1.0.0-alpha < 1>응용여러 오픈소스 프로젝트들이 이미 Semantic Versioning에 따라 버전 명을 표기하기 시작하였으며, 해당 규칙에 기반을 둔 버전 비교 라이브러리도 만들어지고 있습니다.•node.js: https://github.com/isaacs/node-semver•PHP: https://github.com/GordonSchmidt/SemVer•Python: https://github.com/k-bx/python-semver•Ruby: https://github.com/iantruslove/SemverStringerseaport는 node.js 에서 서비스 클러스터들이 Semantic Versioning에 따라 버전 의존성을 가지게 설계할 수 있어 보다 안정적인 버전 협상이 가능하도록 하고 있습니다.server.js:var seaport = require('seaport');var ports = seaport.connect('localhost', 9090);var http = require('http');var server = http.createServer(function (req, res) {res.end('beep boop\r\n');});server.listen(ports.register('[email protected]'));client.js:var seaport = require('seaport');var ports = seaport.connect(9090);var request = require('request');ports.get('[email protected]', function (ps) {var u = 'http://' + ps[0].host + ':' + ps[0].port;request(u).pipe(process.stdout);});output:$ node server.js &[1] 6012$ node client.jsbeep boop마치며비록 작은 통일일지는 모르나, 버전 명을 작성하는 훌륭한 기준이 있다는 것은 장기적으로 개발 생태계를 더욱 빠르고 긴밀하게 협력하도록 도와줄 것이라 생각됩니다. 의미적 해석이 가능한 코드는 의존성 문제를 더 똑똑한 수준으로 자동화할 수 있기 때문이죠. 버전 명을 지으실 때 좋은 안내서가 되었으면 좋겠습니다.#스포카 #개발 #개발자 #개발팀 #꿀팁 #인사이트
조회수 1260

Humans of TODAIT : CTO 유병한을 만나다

어느 화창했던 3월, ‘Humans of TODAIT’ 의 첫 주인공인 투데잇 CTO 유병한을 만나봤습니다. 투데잇 핵심엔진인 그의 이야길 함께 들어볼까요?Q. 자기소개 부탁드려요.안녕하세요! 투데잇에서 CTO를 맡고 있는 유병한입니다. ‘SW 마에스트로’라는 과정에서 대표님과 좋은 인연이 되어 투데잇의 전신인 투데잇브레이커부터 지금까지 열심히 개발중입니다. 안드로이드분야에 관심이 많아서 시작하게 되었는데, 차츰 기술 스펙을 확장해 나가면서 서버개발부터 최근엔 iOS 개발까지 맡고 있습니다.Q. ‘꿈을 향한 오늘, 투데잇’ 이라는 슬로건처럼 CTO님의 꿈에 대해 들을 수 있을까요?제겐 두가지 꿈이 있는데요, 먼저 투데잇이라는 서비스 자체에 대해선 전국민 앱으로 거듭나고 싶어요. 기존 교육관련 산업에서 우뚝 솟을 수 있는 서비스가 되고 싶은데요, 공부하는 사람의 입장에서 처음부터 끝까지 도움을 줄 수 있는 서비스를 제공하고 싶어요. 그래서 ‘저 사람이 일하는 투데잇은 일하기 좋은 회사다!’ 라던가 ‘성장하기 좋은 회사!’라는 인식을 주고 싶어요! 여러사람들을 심쿵!하게 만들고 싶습니다(웃음)음.. 그리고 제 개인적인 꿈으론 진짜 언젠간 해보고 싶은건데, 다큐멘터리 내셔널 지오그래픽급의 사진작가가 되고 싶어요.이래저래 심쿵하는 사람이 되고, 심쿵하게 만드는 서비스를 제공하고 싶습니다.Q. ‘개발자’로서의 시작은 어땠나요?음 제가 처음부터 개발자가 되고 싶단 생각은 안했어요. ‘개발’과 인연의 끈이라고 되짚어본다면, 아마 어릴 적에 접했던 나모웹에디터로 홈페이지 만들기?였던 것 같아요.그리고 수시를 과감하게 버리고 제가 즐겁게 할 수 있었던 컴공이나 관련 학과로 찾다보니 지금의 과에 입학하게 되었어요. 대학교와서도 다양한 학교 수업 중 개발 관련 수업을 맛보면서 ‘아 이게 나한테 맞겠구나!’ 싶어서 본격적으로 공부했어요.(Q. 앗, 그럼 ‘개발자’라는건 갑작스러운 전환이었나요? )그렇다고 해서 아주 갑작스럽진 않았어요. 학생때 사진찍는걸 즐겨서 색감에 대한 거라던가 화면에서의 구도에 대한 이해같은게 높았거든요. 학과에서 배웠던 다양한 편집툴들이 지금의 UI 센스에 발판이 되지않았나 생각해요.어느 순간 하나가 쓸데없던 적은 없었던 것 같아요. 자그마한 순간순간들이 지금의 저로 만든 것 같아요.Q. 본인이 맡은 업무에 대해 어떻게 접근하나요?일단 맡은 분야에 대해서도 그렇고 제가 욕심이 좀 많아요(웃음) 내가 잘하고 싶은 욕심, 가지고 싶은 욕심이 여러 힘든 과정을 이겨낸 원동력이 아닐까 생각해요.Q. 예를 들면 어떤 경험이 있나요? 조금 더 자세하게 듣고 싶어요!음.. 아 이건 좀 비밀인데 중학교때 사진을 찍게 되었는데 보통 그냥 똑딱이카메라쓰던 시절이었거든요. 그런데 DSLR이 너무 가지고 싶어서 조르기도하고 한푼두푼 모으기도해서 결국엔 DSLR을 손에 넣었어요. 되게 사소한것 같지만 나름 원하는걸 얻어낸 뜻깊은 추억이죠. 뭐든 전문가처럼 해야겠단 욕심이 강한 것 같아요.(Q. 오.. 그런데 책상에 책이 되게 많이 쌓여있네요? )책을 쌓아두는게 사실 좀 최근 관심이 가진 프로그래밍 언어라던지 관심이 가는거 위주로 가져다 놓긴 했어요. 아이폰 관련 서적이 몇개 있는데, (이제 세달 정도) 레퍼런스로 많이 찾아보기 위해서 책들이 상시대기하고 있어요. 2–3권. 스위프트라는 언어를 배우면서 기존의 코틀린 자바 스크립트 등 다양한 관심이 생겨서 , 언어들에 대한 욕구가 좀 큰 요즘입니다~(웃음)Q. 일을 하다보면 힘든 순간도 많았을 것 같아요.힘든 순간은 매순간인것 같아요(하하) 그래도 진짜 엄청 힘든 순간이 있었는데 제겐 ‘아버지’가 되게 큰 힘이 되주셨어요. 아버지께서 목사님이시거든요. 평생 부산에서만 사셨던 분이 산골 깊숙히 들어와서 농촌교회를 준비하시면서 힘든 부분이 분명 클텐데도, 지금은 사회복지기관까지 운영하시는 걸 보면 정말 대단하신 것같아요. 홀로 타지에서 모든걸 감내하셨던 부분이, 그리고 하고자 하는 일에 대한 끊임없는 열정을 많이 배워요.그런 아버지를 보고 감명받아서인지 저도 모르는 사이에 창업가 마인드가 생겼던 것 같아요. “ ‘내’가 능동적으로, ‘내’회사를, ‘내’회사에서, ‘내’회사를 위해 일하는, ‘내’일을 한다. ”라는 생각 자체가 아버지의 영향을 많이 받지 않았나싶네요.음 그리고 역시나 빼놓을 수 없는! 우리 투데잇을 사랑해주시는 유저분들이 정말 큰 힘이되요. 실은 투데잇브레이커 당시에 제가 이일을 시작하게 된 이유자체가 ‘이 서비스를 사용하는 유저들이 있다’란 거였거든요. 제겐 그분들이 제 모든 이유인것 같아요. 제 스스로가 성장할 수 있는 이유, 투데잇이 인정받을 수 있는 이유, 그 모든 이유의 근간이라고 생각해요.Q. 항상 좋은 리뷰만 있진 않았을 것 같아요. 혹시 가장 기억에 남는 리뷰 있나요?되게 오래전 리뷰인데, 많이 부족했던 투데잇을 보고 ‘대체 언제쯤 기능 업데이트 되냐, 3D게임 만드냐’라고 하셨던 리뷰가 가장 기억에 남아요. 순간은 되게 기분이 상하면서도 점점 더 잘해내고 싶단 의지가 생기더라구요. ‘내가 3D게임 개발하는 것도 아닌데, 이정도라니. 더 개발 좀 열심히 해야겠다.’하구요. 지금은 그분께 감사하죠.그때 당시만해도 투데잇이란 서비스가 되게 부족했을텐데도 끊임없이 애정해주시면서 기다려주신 유저분 중 한 분이니까요.좋았던 리뷰들은 정말로 셀 수 없이 많아요. 저희가 매주 리뷰를 함께 공유하는 자리가 있는데, ‘성적이 올랐다는 리뷰’부터, ‘투데잇 덕분에 공부 스타일이 또는 생활 습관이 바뀌었어요’, ‘지금 수험생활을 하고 있는데 힘을 얻고 있어요.’ 그리고 ‘합격소식’까지. 진짜 큰 힘이 되죠. 제가 어떤 무형의 무언가를 하고 있단게 현실에서 드러난다는게. 그게 정말 큰 힘이 되요.Q. 나에게 ‘기술’ 이란?저는 아까 말씀드렸던 것처럼 욕심이 좀 많은 편인데, 그 중 제일 욕심 많은게 바로 ‘기술’이예요. 이 분야에서 전문가가 되기 위해서 하나하나 하면 하나를 깊게 파는 스타일이예요.그래서 다양한 분야의 기술을 심도있게 잘하고 싶은 욕심이 있었는데, 투데잇을 개발하면서 그런 욕심을 부릴 수 있는 기회가 주어졌어요. 당시만 해도 당장 없는 개발팀원 자릴 메우기 위해 열심히 욕심부렸던게 지금의 이 자리에 앉게되지 않았나 생각됩니다.제게 기술이란건, 기술 자체가 가지고 싶은 그 무언가예요. 끊임없이 욕심을 내서 계속해서 닿고싶은 그런 존재?(웃음)Q. 오픈소스활동에서 핫하다는 이야기가 있는데요~오픈소스 활동이 아주 거창하진 않아요. 아직은 걸음마단계 수준이죠. 음 처음엔 제가 필요한 오픈 소스를 사용하면서 발견한 에러나 버그 부분에 대해 피드백을 드렸어요. 되게 간단한 부분이었는데 그쪽에서 긍정적으로 받아들여주셨어요. 그런 상호 피드백이 오가면서 관심을 갖게되었습니다.제트브레인스라는 회사에서 근무하시는 분의 피드백을 받는 과정이 오픈 소스활동을 통해 서로 교류할 수 있단 점이 매력적이었어요.활동을 하게된 결정적인 계기는, 다른 회사에 다니는 친구와 함께 깃허브에 올리고 IOS 개발 커뮤니티에 올렸더니 반응이 핫하더라구요. 큰 이슈는 아니지만, 사람들이 긍정적인 피드백을 해줘서 즐거운 순간이었죠.앞으로도 작은것부터 하나씩 해나갈 예정이에요. 큰 규모의 기술은 아니더라도, 투데잇의 ANDROID/IOS에 필요한, 하지만 불편함을 해소해줄 수 있는 라이브러리나 툴들을 만들어나가려고 생각을 하고 있구요. 기존에 투데잇 안에서만 쓰던 걸, 조금씩 정리해서 공유해나갈 생각입니다.Q. 안드로이드 앱 개발을 하면서 소프트웨어 아키텍쳐에 관한 고민이 있다던데, 어떤 고민을 하고계신지 들어볼 수 있을까요?지금 저희 나름대로, 기존에 있던 MVVM, VIPER라든지 그런 아키텍쳐들을 많이 보고 차용을 해서, 투데잇에도 적용을 해나가고 있는 중이에요. 직접 해보니 학교에서 책으로 배운 “프로그래밍 구조가~” 나 “아키텍쳐 구조가~” 에 대해 필요성을 뼈저리게! 몸소 부딪혀가면서 느끼는 중입니다.개발을 하다보면, 사소한 버그나 문제점을 많이 발견하게 되는데, 이를 어떻게 미리 테스트할지 또 어떻게 검증할지에 대해 고민을 하고 있어요. 그런데 기존 소스코드는 각각 다른 기능을 하는 코드가 한데 뭉쳐있어서, 이걸 분리해서 테스트하기에 용이한 아키텍쳐에 대해 개선 및 적용해나가고 있습니다.또 안드로이드와 아이폰 버전을 개발 중인데, 각각 플랫폼에 종속적인 부분을 빼놓고 두 버전 모두 동일한 구조를 가지고 갈 수없을까에 대한 고민 중인데요, 이러한 고민을 함께 하실 분들이 오셨으면 좋겠어요. 이상적인 구조를 향해서 말이죠. (웃음)Q. 현재 일하고 있는 팀원이 7명이나 된다고!네! 대표님과 단둘이 끌어왔던게 엊그제같은데, 벌써 7명의 투데잇팀으로 구성되었네요. (웃음) 사실 제게 투데잇팀은 그냥 공기같은 존재예요. 같이 있을 땐 중요성을 모르다가도, 누구하나 자릴 비우게 되면 그 느낌이 진짜 오묘해요. 서로가 서로를 너무 당연하게 자리하고 있다고 느끼고 있어서요.언젠가 한번 기호형님(COO)이 자릴 비운적이 있었어요. 아직 일한지 1년도 채 안되는데도, 옛날 옛적부터 알고 있던 사람처럼 그때 그 공허감이 되게 크더라구요.사실 좋을 땐 다 좋죠. 중요한건 일하면서 분명 좋지 않은 순간이 올텐데, 이때 서로 어떻게 커뮤니케이션을 하느냐 인것 같아요. 부정적 피드백에 대해 받아들이는 자세가 우리 팀의 가장 메리트라고 생각해요.지금 팀원들은 일에 있어서 피드백이 오갈 땐, 감정적인 건 잠깐 내려놓고, 객관적으로 앞으로 더 발전 방향에 있어서 뭘 어떻게 해야할지를 생각하는 태도를 보이거든요. 이 부분에 대해 ‘서로 핏이 잘 맞는다’라는 문장이 딱 맞는 표현 같아요.그래서 전 “같이 있으면서 어색하지 않은 그런 사이”가 좋아요. 물론 시간이 지나면서 서로 맞춰가는거겠지만, 왜 그런거 있잖아요. 함께 일을 해도 계속해서 어색한 사람이 있고 조금 풀리는 사람이 있는거. 그런 점이 저희 팀의 메리트라고 생각합니다.Q. CTO의 입장에서, 같이 일하고 싶은 개발자는 어떤 사람인가요?당연한 거겠지만, 일단 서로 존중해줄 수 있는 사람이면 좋겠어요. 아무리 비즈니스라지만 서로가 서로에게 예의와 매너가 갖춰진 사람을 원합니다.업무적으론, 뭔가 새롭게 배우는거에 대해 두려움이 없는게 좋은 개발자의 기본 자세라고 생각해요. 그래서 바로바로 과감한 도전 정신이 있는 사람! 그리고 제게 없는 재능을 가져서 서로가 상호 보완해나갈 수 있는 파트너면 좋겠어요.Q. 지원하고 싶은, 지원을 생각하는, 이 글을 보고있는 사람에게 한마디 부탁드려요!“만약에 지원을 하신다면, 마음을 단단히 먹어야할겁니다.”초반만 하더라도, 스타트업에 대한 환상이 있었어요. 이상적인 모습, 장밋빛 회사생활만을 꿈꿨거든요. 언론에서 소위 말하는 ‘젊은 창업가!’의 그 이면엔, 장밋빛을 현실화 하기위해 매일매일이 고난의 길이란걸 잊지 않으셨음 해요. 스타트업이란게 자신의 한계를 확인하는 과정이기 때문에, 같은 출발선에서 함께 발전하기 위해 달릴 준비가 되신 분을 환영합니다.또 자기 나름의 미션이 있고, 그걸 회사의 가치 성장에 일치시켜 나가면서 함께 실현해나가실 분을 모십니다!우리의 이상을 위해 함께 이 현실을 헤쳐나가실 분을 찾습니다!#투데잇 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #조직문화 #개발자 #개발팀 #CTO
조회수 2042

비트윈의 스티커 시스템 구현 이야기 - VCNC Engineering Blog

 비트윈에는 커플들이 서로에게 감정을 더욱 잘 표현할 수 있도록 스티커를 전송할 수 있는 기능이 있습니다. 이를 위해 스티커 스토어에서 다양한 종류의 스티커를 제공하고 있으며 사용자들은 구매한 스티커를 메시지의 첨부파일 형태로 전송을 할 수 있습니다. 저희가 스티커 시스템을 구현하면서 맞딱드린 문제와 이를 해결한 방법, 그리고 프로젝트를 진행하면서 배운 것들에 대해 소개해 보고자 합니다.스티커 시스템 아키텍처비트윈에서 스티커 기능을 제공하기 위해 다양한 구성 요소들이 있습니다. 전체적인 구성은 다음과 같습니다.비트윈 서버: 이전에 소개드렸었던 비트윈의 서버입니다. 비트윈의 채팅, 사진, 기념일 공유 등 제품내의 핵심이 되는 기능을 위해 운영됩니다. 스티커 스토어에서 구매한 스티커는 비트윈 서버를 통해 상대방에게 전송할 수 있습니다.스티커 스토어 서버: 스티커를 구매할 수 있는 스토어를 서비스합니다. 스티커 스토어는 웹페이지로 작성되어 있고 아이폰, 안드로이드 클라이언트와 유기적으로 연동되어 구매 요청 등을 처리합니다. 처음에는 Python과 Flask를 이용하여 구현하려 하였으나 결국엔 서버 개발자들이 좀 더 익숙한 자바로 구현하기로 결정하였습니다. Jetty와 Jersey를 사용하였고, HTML을 랜더링하기 위한 템플릿 엔진으로는 Closure Template을 이용하였습니다. ORM으로는 Hibernate/JPA, 클라이언트와 웹페이지간 연동을 위해서 Cordova를 이용하였습니다. EC2에서 운영하고 있으며 데이터베이스로는 RDS에서 제공하는 MySQL을 사용합니다. 이미 존재하는 솔루션들을 잘 활용하여 최대한 빨리 개발 할 수 있도록 노력을 기울였습니다.스티커 다운로드 서버: 스티커는 비트윈에서 정의한 특수한 포맷의 파일 형태로 제공됩니다. 기본적으로 수 많은 사용자가 같은 스티커 파일을 다운로드 받습니다. 따라서 AWS에서 제공하는 CDN인 CloudFront을 이용하며, 실제 스티커 파일들은 S3에서 호스팅합니다. 그런데 스티커 파일들은 디바이스의 해상도(DPI)에 따라 최적화된 파일들을 내려줘야하는 이슈가 있었습니다. 이를 위해 CloudFront와 S3사이의 파일 전송에 GAE에서 운영중인 간단한 어플리케이션이 관여합니다. 이에 대해서는 뒷편에서 좀 더 자세히 설명하도록 하겠습니다.구현상 문제들과 해결 방법들적정 기술에 대해 고민하다스티커 스토어 서버를 처음 설계할때 Flask와 SQLAlchemy를 이용하여 구현하고자 하였습니다. 개발팀 내부적으로 웹서버를 만들때 앞으로 Python과 Flask를 이용해야겠다는 생각이 있었기 때문이며, 일반적으로 Java보다는 Python으로 짜는 것이 개발 효율이 더 좋다는 것은 잘 알려진 사실이기도 합니다. 하지만 Java에 익숙한 서버 개발자들이 Python의 일반적인 스타일에 익숙하지 않아 Python다운 코드를 짜기 어려웠고, 오히려 개발하는데 비용이 더 많이 들어갔습니다. 그래서 개발 중에 다시 웹 서버는 자바로 짜게 되었고, 여러가지 스크립트들만 Python으로 짜고 있습니다. 실제 개발에 있어서 적절한 기술의 선택은 실제 프로젝트에 참여하는 개발자들의 능력에 따라 달라져야한다는 것을 알게되었습니다.스티커 파일 용량과 변환 시간을 고려하다사용자는 스티커 스토어에서 여러개의 스티커가 하나로 묶인 스티커 묶음을 구매하게 됩니다. 구매 완료시 여러개의 스티커가 하나의 파일로 압축되어 있는 zip파일을 다운로드 받게 됩니다. zip파일내의 각 스티커 파일에는 스티커를 재생하기 위한 스티커의 이미지 프레임들과 메타데이터에 대한 정보들이 담겨 있습니다. 메타데이터는 Thrift를 이용하여 정의하였습니다.스티커 zip파일 안에는 여러개의 스티커 파일이 들어가 있으며, 스티커 파일은 다양한 정보를 포함합니다카카오톡의 스티커의 경우 애니메이션이 있는 것은 배경이 불투명하고 배경이 투명한 경우에는 애니메이션이 없습니다. 하지만 비트윈 스티커는 배경이 투명하고 고해상도의 애니메이션을 보여줄 수 있어야 했습니다. 배경이 투명한 여러 장의 고해상도 이미지를 움직이게 만드는 것은 비교적 어려운 점이 많습니다. 여러 프레임의 이미지들의 배경을 투명하게 하기 위해 PNG를 사용하면 JPEG에 비해 스티커 파일의 크기가 너무 커집니다. 파일 크기가 너무 커지면 당시 3G 환경에서 다운로드가 너무 오래 걸려 사용성이 크게 떨어지기 때문에 무작정 PNG를 사용할 수는 없었습니다. 이에 대한 해결책으로 투명 기능을 제공하면서도 파일 크기도 비교적 작은 WebP를 이용하였습니다. WebP는 구글이 공개한 이미지 포맷으로 화질 저하를 최소화 하면서도 이미지 파일 크기가 작다는 장점이 있습니다. 각 클라이언트에서 스티커를 다운 받을때는 WebP로 다운 받지만, 다운 받은 이후에는 이미지 로딩 속도를 위해 로컬에 PNG로 변환한 스티커 프레임들을 캐싱합니다.그런데 출시 된지 오래된 안드로이드나 iPhone 3Gs와 같이 CPU성능이 좋지 않은 단말에서 WebP 디코딩이 지나치게 오래 걸리는 문제가 있었습니다. 이런 단말들은 공통적으로 해상도가 낮은 디바이스였고, 이 경우에는 특별히 PNG로 스티커 파일을 만들어 내려줬습니다. 이미지의 해상도가 낮기 때문에 파일 크기가 크지 않았고, 다운로드 속도 문제가 없었기 때문입니다.좀 더 나은 주소 포맷을 위해 GAE를 활용하다기본적으로 스티커는 여러 사용자가 같은 스티커 파일을 다운받아 사용하기 때문에 CDN을 이용하여 배포하는 것이 좋습니다. CDN을 이용하면 스티커 파일이 전 세계 곳곳에 있는 엣지 서버에 캐싱되어 사용자들이 가장 최적의 경로로 파일을 다운로드 받을 수 있습니다. 그래서 AWS의 S3와 CloudFront를 사용하여 스티커 파일을 배포하려고 했습니다. 또한, 여러 해상도의 디바이스에서 최적의 스티커를 보여줘야 했습니다. 이 때문에 다양한 해상도로 만들어진 스티커 파일들을 S3에 올려야 했는데 클라이어트에서 스티커 파일을 다운로드시 주소 포맷을 어떻게 가져가야 할지가 어려웠습니다. S3에 올리는 경우 파일와 디렉터리 구조 형태로 저장되기 때문에 아래와 같은 방법으로 저장이 가능합니다.http://dl.sticker.vcnc.co.kr/[dpi_of_sticker]/[sticker_id].sticker하지만, 이렇게 주소를 가져가는 경우 클라이언트가 자신의 해상도에 맞는 적절한 스티커의 해상도를 계산하여 요청해야 합니다. 이것은 클라이언트에서 서버에서 제공하는 스티커 해상도 리스트를 알고 있어야 한다는 의미이며, 이러한 정보들은 최대한 클라이언트에 가려 놓는 것이 유지보수에 좋습니다. 클라이언트는 그냥 자신의 디스플레이 해상도를 전달하기만 하고, 서버에서 적절히 계산하여 알맞은 해상도의 스티커 파일을 내려주는 것이 가장 좋습니다. 이를 위해 스티커 다운로드 URL을 아래와 같은 형태로 디자인하고자 하였습니다.http://dl.sticker.vcnc.co.kr/[sticker_id].sticker?density=[dpi_of_device]하지만 S3와 CloudFront 조합으로만 위와 같은 URL 제공은 불가능하며 따로 다운로드 서버를 운영해야 합니다. 그렇다고 EC2에 따로 서버를 운영하는 것은 안정적인 서비스 운영을 위해 신경써야할 포인트들이 늘어나는 것이어서 부담이 너무 컸습니다. 그래서, 아래와 같이 GAE를 사용하기로 하였습니다.GAE는 구글에서 일종의 클라우드 서비스(PaaS)로 구글 인프라에서 웹 어플리케이션을 실행시켜 줍니다. GAE에 클라이언트에서 요청한 URL을 적절한 S3 URL로 변환해주는 어플리케이션을 만들어 올렸습니다. 일종의 Rewrite Engine 역할을 하는 것입니다. 서비스의 안정성은 GAE가 보장해주고, S3와 CloudFront의 안정성은 AWS에서 보장해주기 때문에 크게 신경쓰지 않아도 장애 없는 서비스 운영이 가능합니다. 또한 CloudFront에서 스티커 파일을 최대한 캐싱 하며 따라서 GAE를 통해 새로 요청을 하는 경우는 거의 없기 때문에 GAE 사용 비용은 거의 발생하지 않습니다. GAE에는 클라이언트에서 보내주는 해상도를 보고 적당한 해상도의 스티커 파일을 내려주는 아주 간단한 어플리케이션만 작성하면 되기 때문에 개발 비용도 거의 들지 않았습니다.토큰을 이용해 보안 문제를 해결하다실제 스티커를 구매한 사용자만 스티커를 사용할 수 있어야 합니다. 스티커 토큰을 이용해 실제 구매한 사용자만 스티커를 전송할 수 있도록 구현하였습니다. 사용자가 스티커 스토어에서 스티커를 구매하게 되면 각 스티커에 대한 토큰을 얻을 수 있습니다. 스티커 토큰은 다음과 같이 구성됩니다.토큰 버전, 스티커 아이디, 사용자 아이디, 유효기간, 서버의 서명서버의 서명은 앞의 네 가지 정보를 바탕으로 만들어지며 서버의 서명과 서명을 만드는 비밀키는 충분히 길어서 실제 비밀키를 알지 못하면 서명을 위조할 수 없습니다. 사용자가 자신이 가지고 있는 스티커 토큰과 그에 해당하는 스티커를 비트윈 서버로 보내게 되면, 비트윈 서버에서는 서명이 유효한지 아닌지를 검사합니다. 서명이 유효하다면 스티커를 전송이 성공하며, 만약 토큰이 유효하지 않다면 스티커의 전송을 허가하지 않습니다.못다 한 이야기비트윈 개발팀에게 스티커 기능은 개발하면서 우여곡절이 참 많았던 프로젝트 중에 하나 입니다. 여러 가지 시도를 하면서 실패도 많이 했었고 덕분에 배운 것도 참 많았습니다. 기술적으로 크게 틀리지 않다면, 빠른 개발을 위해서 가장 익숙한 것으로 개발하는 것이 가장 좋은 선택이라는 알게 되어 스티커 스토어를 Python 대신 Java로 구현하게 되었습니다. 현재 비트윈 개발팀에서 일부 웹사이트와 스크립트 작성 용도로 Python을 사용하고 있지만 Python을 잘하는 개발자가 있다면 다양한 프로젝트들를 Python으로 진행할 수 있다고 생각합니다. 팀내에 경험을 공유할 수 있는 사람이 있다면 피드백을 통해 좋은 코드를 빠른 시간안에 짤 수 있고 뛰어난 개발자는 언어와 상관없이 컴퓨터에 대한 깊이 있는 지식을 가지고 있을 것이기 때문입니다.네 그렇습니다. 결론은 Python 개발자를 모신다는 것입니다.
조회수 961

검은 머리 외국인으로서 스푼 라디오에 입사하기까지

스푼을 만드는 사람들 여섯 번째 이야기서비스 플랫폼 팀 막내이자 분위기를 담당을 맡고 있는, 6개월 차 개발자 Kyu를 소개하고자 한다.영어가 편해요? 아니면 한국어가 편해요?"일반적인 의사소통에 있어선 한국어가 편하고, 업무를 볼 땐 영어가 편해요."Q. 원래 되게 개구쟁이(?)의 이미지를 가지고 계신 줄 알았는데.."저 원래 진지한 거 진짜 싫어해요. 제가 겉보기엔 늘 장난꾸러기 같아 보이실 수도 있지만, 사실 이렇게 단 둘이 이야기를 하면 또 다른 진지하고 진정성 있는 저의 모습이 보이실 거예요. 저 지금 많이 진지해요?"(인터뷰 전에는 큐가 그저 재미있는 사람이라고 생각했는데, 인터뷰를 하고 나서 그를 다시 보았습니다..)'Kyu'라는 사람을 알고 싶습니다.Q. 본인은 어떤 사람이라고 생각하세요?Me, Myself, and I - "저는 제가 느끼는 것 그리고 원하는 것에 굉장히 집중을 하는 편이에요.제 본인 스스로에게 집중하는 양도 기준치도 꽤나 높은 편이에요. 무엇보다 스스로 혼자만의 시간을 굉장히 중요시합니다."Q. 국적이 Canadian이라 들었습니다. "네, 저는 8살 때 부모님과 함께 교육을 위해서 캐나다로 이민을 갔었어요. 그리고 캐나다에서 고등학교까지 있었고 그 후엔 미국에서 대학을 졸업했어요. 졸업 후에 한국에 취업을 하게 되어서 어느덧 한국 생활이 1년 3개월 차가 되어가고 있네요."Q. 한국에서 취업을 하게 된 계기가 있다면?"사실 처음에 제가 스타트업에 취업을 한다고 했었을 때, 주변에서 안정적인 곳이 아닌 스타트업을 선택하느냐라고 많이들 물어보셨어요. 그것도 한국에서요. 근데 저는 제가 정말 무슨 일을 하고 싶은지 잘 몰랐었어요. 목표의식과 노력 없이 공부를 하다 보니, 어느덧 졸업이 다가왔고 좌절하게 됐었어요. 정말 오랜 시간 아무것도 못했었어요. 길을 잃었다고 할까요? 그러다가, 용기를 내서 현실적으로 내가 할 수 있는 일이 무엇일까 고민 끝에 한국을 선택했어요. 한국엔 유능한 사람들이 정말 많고, 실력 있는 사람들이 열심히도 하는 곳이에요. 정말 무언가를 최선을 다해서 해본 다는 게 무엇인지 겪기 위해선 한국에서 배워보는 게 좋다고 생각했고, 실제로도 그렇다는 걸 느끼고 있어요."당신의 회사생활이 궁금합니다Q. 서비스 플랫폼 팀(서버팀)에서 하고 계신 업무는?"저는 현재 하고 있는 업무는, 정확히 말하자면 로그 데이터 수집 및 스푼 앱 내에서 발생하는 유저들의 행동 그리고 현상에 대한 데이터를 실시간으로 수집하고 조회합니다. 그리고 시간에 흐름에 따른 서비스 상태를 나타내 주는 작업을 하고 있습니다."Q. 현재 업무의 만족도는 어느 정도인가요?"업무에 대한 만족도는 높습니다. 저는 신입이고, 기본 역량이 팀원들에 비해서는 낮지만 제가 입사한 후 처음 시도한 것이 '로그 데이터 수집'인데요. 처음부터 끝까지 독립 시스템을 맡고 있다는 점이 굉장히 뿌듯합니다. 저를 그만큼 믿어주시기에 가능한 일이라고 생각합니다. 그렇다 보니 주인의식을 가지게 되고요. 앞으로 조금 더 만족도를 높이고자 한다면, 팀원들과 프로젝트를 도 함께 진행해보고 싶습니다."Q. 스푼 라디오가 큐의 첫 직장인 가요?"네, 정사원으로는 첫 직장이지만 그 전에는 인턴을 잠시 했었어요. 이건 제가 한국에서 겪은 좋지 않은 기억이지만, 인턴 생활 때, 타 스타트업에서 3개월 정도를 일을 했었는데, 임금 체불 문제가 있었어요. 당연한 부분이자 저의 권리가 지켜지지 않는 것을 보고, 다시 캐나다에 가고 싶단 생각을 했었어요. 그때 자존감도 많이 낮아지고 참 암울했던 시기였어요."Q. 한국 회사에서 느끼는 문화 차이가 있나요?"사실 제가 생각했던 것보다 워라벨이 잘 지켜지고 있어서 그 부분은 의외라고 생각이 들었어요.다만, 사람들과 함께 편하게 이야기를 하는 과정에서 문화적 차이를 느끼곤 해요. 예를 들면 Gender 부분 이라던지 등등. 의식이 조금 다르다고 느낄 때가 있어요. 하지만 한국 문화라던지, 의식의 차이를 저도 받아들이고 많이 노력하고 있어요. 누구나 의견과 관점은 다를 수 있으니까요. 잘 못되었다기 보단, 다른 사람들이구나 하고 받아들이려고 합니다."Q. 회사에서 가깝게 지내는 동료는 누구인가요?"업무를 가장 많이 함께 해서 가까운 분은 찰스, 개인적으로 제일 친하다고 느끼는 분은 샘입니다. 왜 친하다고 느끼는지는 모르겠지만 저도 모르게 자꾸 관심이 가요. 빨리 더 친해지고 싶은 생각도 들고, 그저 좋은 분이라고 느껴서입니다." (하지만 그분의 마음은 저도 몰라요.. 저만 친하다고 느낄 수도?)커피를 좋아하는 Kyu 당신의 사생활이 궁금합니다Q. 언제 가장 캐나다가 그립다거나 가고 싶어요?"일단, 미세먼지 많은 날이요.  그리고, 가끔씩 이런 마음이 들 때가 있어요. 한국에서는 쳇바퀴도는 매일 똑같은 삶을 사는 것 같다는 느낌(?) 한국에서는 아무것도 하지 않아도, 뭔가 늘 바쁜 그런 느낌이 들어요. 안정감이 없다고 해야 할까요? 한국은 소비를 통해서 스트레스를 해소하는 나라인 거 같아요. 주로 뭘 사 먹거나, 소유하거나. 근데 캐나다에서 랑 미국에선 다른 방식으로 스트레스를 풀 수 있었거든요. 공감하시려는지 모르겠어요. 저는 그렇답니다. 한국에 살다 보니 이제는 사실 오히려 이제는 외국에 나가 산다는 게 더 큰 도전이 된 느낌이기도 하고요."Q. 가장 좋아하는 캐나다 음식은?"캐나다 초밥요! 캘리포니아 롤이 캐나다 밴쿠버에서 만들어졌단 사실 알고 계시나요? 저 그거 정말 좋아합니다.."Q. 스스로를 어느 나라 사람이라고 생각하나요?"저는 국적은 캐나다이지만, 저의 정체성은 한국에서 시작되었고, 한 번도 그걸 잊은 적이 없어요. 캐나다에서도 한국 문화에 대한 관심을 늘 가지고 있었거든요. 예능이라던지, 시트콤 다 따라서 봤었으니까요. (원래 외국에 살면 더 한국 프로그램 많이 보게 된다는..) 아무쪼록, 저는 제가 한국인임을 잊어 본 적이 없어요. 비록 국적은 캐나다인이지만요. 그리고 저는 최대한 한국의 가십거리를 말하지 않아요. 왜냐면, 저는 이곳에 오래 살지 않았고 제가 기여할 수 있는 부분이 굉장히 제한적이거든요. 제가 국방의 의무를 했다거나, 투표권이 있으면 모를까 제가 감히 함부로 한국에 대해서 말하고 싶지 않아요. 무엇보다 저는 제 스스로가 어느 국가의 사람인 지보단 '나'라는 스스로에 집중하는 편이에요."(앞으로 외국인이라고 부르지 않을게요 큐..)Q. 다른 이루고 싶은 꿈이 있다면?"다음 생에 저는 래퍼가 되고 싶어요. 정말로 진지하게, 힙합과 랩이라는 문화를 존중하고 좋아합니다. 그저 취미로 시작하고 싶은 게 아니라,  정말 다시 태어나면 온전히 랩에 집중해서 좋은 래퍼가 되고 싶어요."Q. 어떤 사람과 함께 일하고 싶나요?개발자로서 이루고 싶은 비전이 확실한 사람이요. 무엇보다 소통하는 데 있어서 나이를 떠나, 마음이 열려있는 사람과 함께 일하고 싶습니다. 서로를 존중할 수 있는 그런 사람이요.탁구를 좋아하는 KyuQ. 마지막으로 하고 싶은 말이 있다면?"주변 친구들이 스푼에서 일을 시작하기 전과 후가 많이 바뀌었다고 말하는데, 저는 제 스스로에게 정말 많은 변화가 생겼다고 생각해요. 조금 더 진지하고 진중한 사람이 된 것 같고 이 긍정의 변화가 앞으로도 계속되길 바랍니다. 아! 그리고 회사에 제공되는 샐러드가 매일 아침마다 오면 좋겠어요. 저 그럼 정말 회사 지금보다 더 즐겁게 다닐 수 있습니다"P.S: 매번 다른 사람들의 인터뷰를 하고 계신 Sunny를 제가 직접 인터뷰해보고 싶어요.서비스 플랫폼팀 팀원들이 Kyu를 한마디로 표현한다면?Charles 曰:  '대장' - 대시보드 장인Sam 曰:  '거머리' - 자꾸 달라붙어서..Mark 曰: '감초 같은 사람' - 약방의 감초처럼 저희 팀 업무 전반에 없어선 안될 사람(큐가 이렇게 하라고 시켰어요) 
조회수 1226

Single Layer Perceptron

Single Layer Perceptron이번 포스팅에서는 모든 인공신경망의 기초가 되는 perceptron의 개념에 대해서 배워보고, 이를 이용한 단층 퍼셉트론 구조를 구현해보도록 하겠습니다.퍼셉트론은 여러분이 고등학교 과학시간에 한 번쯤은 들어보았을 인간의 신경망, 뉴런으로부터 고안되었습니다. 퍼셉트론은 여러 개의 신호를 입력받으면, 하나의 신호를 출력합니다. 이 때 퍼셉트론이 출력하는 신호는 전달 혹은 차단이라는 1 또는 0의 값을 갖게됩니다. 직관적인 예시를 들어보도록 하죠. 여러분이 매달 초 용돈, 아르바이트비를 받거나(1) 받지 않는다(0)고 가정해보겠습니다. 여러분의 통장에 입금된 이 두 가지 수익을 input(입력) 신호라고 합니다. 이 때 여러분은 두 개의 수익이 합쳐진 통장 잔고를 확인하고 전부터 갖고 싶던 옷을 살지(1) 혹은 사지 않을지(0)를 결정합니다. 이렇게 여러분이 내리는 결정이 output(출력) 신호가 되는 것입니다.하지만 여러분의 의사결정은 이것보다는 복잡할 것입니다. 용돈은 거의 생활비로만 사용하고, 아르바이트비를 주로 취미생활에 사용한다고 가정해보죠. 그럼 여러분이 옷을 살지 여부를 결정할 때에는 아르바이트비가 들어왔는지가 좀더 중요할 것입니다. 따라서 우리는 각 input(입력) 신호를 그대로 사용하지 않고, 각각에 가중치(weight)를 주어 output(출력) 신호를 결정하게 됩니다. 이것을 도식으로 나타내면 다음과 같습니다. 이처럼 input에 weight가 곱해진 형태가 정해진(혹은 학습된) 임계치를 넘을 경우 1을 출력하고 그렇지 않을 경우 0을 출력하게 하는 것이 퍼셉트론의 동작 원리입니다. 정말 간단하죠! 이는 아래 수식과 같습니다.하지만 임계치를 그때그때 바꿔주는 것은 조금 직관적이지 않습니다(저만 그런가요). 그래서 우리는 아래 형태로 식을 바꾸게 되며, 이 때 추가된 b를 bias 혹은 절편이라고 말합니다. 위 식은 여러분이 중고등학교 수학 수업을 잘 들었다면 굉장히 익숙한 형태일 것입니다. 바로 2차원 좌표축을 그리고 직선을 그었을 때, 그 직선을 기준으로 나뉘는 두 개의 공간을 표현한 식입니다. 역시 말보다는 그림이 이해하기 쉬울테니, 아래에 그림을 그려보도록 하겠습니다.위처럼 공간을 올곧은 직선으로 나누는 것을 선형으로 나눴다고 말합니다. 하지만 직선만으로 공간을 나누는 것은 유연하지 않습니다. 위와 같은 방식으로는 OR, AND, NAND 문제는 해결할 수 있지만, XOR 문제는 해결할 수 없습니다. 이와 같은 문제를 해결하기 위해서는 층을 하나 더 쌓고, 공간을 단순한 선형이 아닌 곡선으로 분리해내어 좀더 유연한 적용이 가능해져야합니다.(사실 XOR은 선형만으로도 층을 하나 더 쌓으면 해결이 가능합니다). 이에 따라 multi layer perceptron(MLP) 의 개념이 등장하고, activation function(활성함수) 의 개념이 등장하게 됩니다. 후에 활성함수의 개념을 배우게 되면, 지금 배운 단순 퍼셉트론은 활성함수로 계단함수를 가진 것과 동일하다는 것을 알게 되실겁니다.정리단층 퍼셉트론은 모든 딥러닝 공부의 시작이다.단층 퍼셉트론은 입력 신호를 받으면 임계치에 따라 0 또는 1의 값을 출력한다.이러한 단층 퍼셉트론은 결국 공간을 선형으로 잘라서 구분하는 것과 동일하다.
조회수 1815

Amazon SageMaker는 처음이지?

Overview브랜디 랩스를 사랑해주시는 여러분, 안녕하세요. 개발자 오-연주입니다. 지난 4월, Brandi Back-end 개발자 분들과 코엑스에서 열렸던 AWS Summit(04.18 - 04.19)에 다녀왔습니다!여러 세션을 듣는 와중에 우연히 AI machine learning 를 쉽게 도와주는 Cloud Machine learning Flatform인 Amazon SageMaker에 대해 들었습니다. 듣던 중 머닝러닝에서 학습을 시켜 그 데이터로 ‘Brandi 서비스와 연관지으면 어떨까’ 라는 생각을 했는데요. 그래서 오늘은 많은 분들의 관심사인 머신러닝 학습관련 Amazon Amazon SageMaker에 대한 글을 쓰려고 합니다.sage는 마법사, 현자라는 의미입니다.sageMaker를 create하자!“자, 퐈이팅 넘치게 신나게 sagemaker를 create해볼까요!” 했는데…Seoul Region이 없다!현재 지원되는 리전은 아직 네 군데입니다. 저는 제일 있어 보이는 미국 동부의 버지니아를 선택하겠습니다.1] EU (Iceland) 2] US West (Oregon) 3] USEast (N. Virginia) 4] US East (Ohio)SageMaker를 create하기 전에는 학습할 데이터와 학습 모델을 저장할 S3 Bucket이 필요합니다.1. Default 값으로 S3를 만드세요.중요한 점은, bucket 이름이 “sagemaker-” 로 시작되어야 한다는 것입니다. 그래야 나중에 notebook instance가 어느 곳에 데이터를 저장할지 알 수 있습니다.Next, Create bucket 버튼을 누르다 보니, S3 Bucket이 생성되었습니다.2. Create notebook instance 버튼을 눌러 SageMaker를 만들어 봅시다!원하는 이름을 지어줍니다. 저는 machineLearningTest 라고 지었어요. IAM role 선택하는 부분에서 None을 눌러 Default 값으로 sageMaker를 만듭니다.인고의 Pending 시간3. Pending이 끝나고 “open” action을 선택하면 Jupyter가 열립니다.Jupyter(Jupyter Notebook)는 오픈 소스로 라이브 코드, 등식, 코드에 대한 시각화를 위해 사용됩니다. 또한 description을 위한 텍스트 문서(마크다운 등)를 지원하는 웹 어플리케이션입니다. 이렇게 하면 코드에 대한 문서화가 가능합니다. 이 글에서는 Jupyter Notebook을 통해 데이터를 학습하고, 그 데이터를 테스트하겠습니다. 제가 진행한 전체 코드 스크립트(entire script)는 이 글의 마지막 부분에 기술있으니 참고해 주세요.자, 이제 드디어 머신러닝 학습을 시킬 차례입니다. 머신러닝 학습에 꼭 필요한 키워드 두 가지를 뽑아봤는데요. - Dataset: 정제된 데이터와 그 데이터에 대한 label을 정리해 놓은 데이터 모음      - Machine learning Algorithm: 기계학습 알고리즘 우리는 MNIST 데이터셋을 k-means 알고리즘으로 학습시킬 겁니다.1)MNIST Dataset기계학습 알고리즘을 사용할 때 가장 기본적으로 테스트하는 데이터셋으로 MNIST 데이터셋이 있습니다. 이것은 사람이 0부터 9까지 숫자 중 하나를 손글씨로 쓴 이미지 데이터와, 해당 이미지에 대한 레이블(0 - 9)이 6만 개 들어있는 학습 데이터셋입니다. 각 이미지는 가로와 세로가 각각 28 픽셀로서, 각 픽셀은 0부터 255 사이의 숫자가 있습니다. 다시 말해, 하나의 이미지는 28 x 28 = 784개의 숫자로 이루어진 데이터입니다. 하나의 이미지를 나타내는 데이터의 array > length가 784라고 표현할 수 있겠네요.MNIST dataset2)k-means지금 만든 SageMaker 학습 알고리즘은 AWS 튜토리얼에서 제시한 K-means를 사용할 예정입니다. k-means는 label 없이, 즉 정답을 모르는 상태로 학습을 하는 비지도 학습 (unsupervised learning) 알고리즘 중 가장 쉽고 많이 쓰입니다. 정답을 모르니, ‘비슷한 애들끼리 뭉쳐봐’ 라고 하고, 알고리즘은 비슷한 친구들끼리 뭉쳐 놓습니다. k-means에서 k는 ‘k개 덩어리로 뭉쳐주세요’라고 제시하는 숫자입니다. 우리는 0부터 9까지 비슷한 친구들끼리 모이게 하고 싶으니 k=10을 쓸 겁니다.지금부터 해야 할 TO DO!1. MNIST 데이터셋을 다운로드받고, 우리가 학습시키기 좋도록 정제하기(preprocessing)2. Amazon SageMaker를 통하여 데이터 학습시키기(training job)3. Amazon SageMaker를 통하여 학습된 데이터를 배포하기(Deploy the model)4. 배포된 모델에 요청을 보내 테스트 데이터에 대한 예측값을 받아오기(inference)4. Jupyter 노트북 인스턴스 생성하기Jupyter에 New Notebook(conda_python3)을 선택해 새로운 노트북을 생성합니다.5. 학습시키기 위한 기본 셋팅드디어 코딩 시작입니다! (의욕활활) 초기 설정해두었던 IAM role, S3 Bucket, MNIST 다운로드, 다운받은 데이터 등을 확인하세요. 글보다 코드로 주석을 보는 게 가독성이 더 좋습니다. 아래 노트북을 통해 마크다운, 주석처리를 통해 description을 해두었으니 참고 바랍니다.외부에서 MNIST 다운로드가 쉽도록 한 url로 MNIST를 다운받는데 성공했습니다. MNIST 데이터셋 내용물 중 하나를 jupyter notebook에 그려서 제대로 다운 받았는지 show_digit() 함수를 작성해 확인하겠습니다.서른 번째 데이터는 누군가 3을 손글씨로 쓴 이미지입니다.6. 머신러닝 학습하기이 세션에서는 기계학습 알고리즘 설정, 학습할 데이터 경로를 지정하겠습니다. 그 후 MNIST 학습 데이터를 S3 버킷에 옮겨 저장합니다.kmeans.fit() 함수를 호출해 직접 학습을 시켜볼까요? 학습 과정은 상당히 오래 걸린다고 했는데 다행히 4분 만에 학습이 끝났습니다.여기서 잠깐! 여기서 k = 10에 대해서 조금 더 알아보도록 할게요. cluster란 한 지점에 점을 찍고 데이터 분석을 한 뒤, 비슷한 데이터들의 군집을 만들어 주는 것입니다. k-means가 진행되면서 각 cluster의 중심이 서로가 잘 뭉치는 방향으로 이동합니다. 직접 그려봤어요(부끄).7. 학습된 모델을 배포하기학습을 시키면 테스트를 하거나 사용할 수 있어야겠죠? 학습된 모델을 배포해 주세요.8. 배포된 모델 테스트 진행하기배포된 모델에 valid_set 데이터로 검증 데이터를 진행합니다..predict() 함수를 호출하면 새로운 이미지가 어떤 cluster에 속했는지 예측 결과를 알려줍니다. 가장 가까운 cluster가 0번이라고 예측 결과를 반환했네요. 또한 cluster 중심과의 거리는 5.85라고 알려줍니다. 여기서 중요한 점은 cluster 번호와 실제 숫자는 일치하지 않는다는 겁니다. 알고리즘은 임의로 cluster 중심에 번호를 매기는데, 꼭 0번 클러스터가 숫자 ’0’을 뭉쳐놓은 건 아니에요!9. 데이터 예측해보기더 많은 데이터를 예측해볼까요? valid set에 있는 100개 데이터를 예측해봅시다! 각 cluster에 가까운 데이터들이 쭉 선정되었습니다. 정확하지는 않지만 비슷한 숫자 모양들이 서로 군집되어 나타납니다. 0과 2같은 숫자들은 잘 표현되지만, 알고리즘이 9랑 4를 헷갈리거나 5와 3을 헷갈리는 듯 하네요.FASHION MNIST로 SageMaker 머신러닝 학습 및 예측해보기자, 이제 몸도 풀었으니 제가 하고 싶었던 패션 관련 머신러닝 학습 및 예측을 진행해볼게요. 마침 옷 그림으로 MNIST와 매우 비슷한 데이터를 만들어 놓은 fashion-MNIST라는 데이터셋을 발견했어요!1. 패션 관련 MNIST 다운로드 받기패션 MNIST 데이터셋을 우선 다운받아 볼게요! 다운로드는 여기에서 받을 수 있습니다. 총 네 개의 파일을 다운로드 받으세요.- train-images-idx3-ubyte.gz : train set 이미지  - train-labels-idx1-ubyte.gz : train set 레이블  - t10k-images-idx3-ubyte.gz : test set 이미지  - t10k-labels-idx1-ubyte.gz : test set 레이블  다운로드 받은 패션 Mnist의 label은 아래와 같이 되어 있습니다. 숫자 0부터 9 대신에 각 이미지가 어떤 이미지인지 텍스트로 표현되어 있어요.LabelDescription0T-shirt/top1Trouser2Pullover3Dress4Coat5Sandal6Shirt7Sneaker8Bag9Ankle boot2. Fashion-MNIST 데이터셋을 이전에 사용했던 mnist.pkl.gz 와 같은 형태로 변환해주는 스크립트 작성해주기위에서 연습할 때는 mnist.pkl.gz 한 개 파일만 사용했는데요!?! 그래서 다운로드 받은 네 개의 파일을 똑같은 형식의 파일 하나로 만들어주는 파이썬 스크립트를 작성해 fashion-mnist.pkl.gz 파일로 만들었어요.import gzip import pickle import numpy as np # MNIST 데이터셋은 train, test 셋이 각각 image, label로 나누어 저장되어있는 4개의 파일로 구성 test_image_path = 't10k-images-idx3-ubyte.gz' test_label_path = 't10k-labels-idx1-ubyte.gz' train_label_path = 'train-labels-idx1-ubyte.gz' train_image_path = 'train-images-idx3-ubyte.gz' out_file_name = 'fashion-mnist.pkl.gz' # train label / images 추출 with gzip.open(train_label_path, 'rb') as train_label_f:     train_label = np.frombuffer(             train_label_f.read(), dtype=np.uint8, offset=8).astype(np.int64)   with gzip.open(train_image_path, 'rb') as train_image_f:     train_imgs = np.frombuffer(             train_image_f.read(), dtype=np.uint8, offset=16).reshape(-1, 784).astype(np.float32)   # test label / images 추출 with gzip.open(test_label_path, 'rb') as test_label_f:     test_label = np.frombuffer(test_label_f.read(), dtype=np.uint8, offset=8).astype(np.int64)   with gzip.open(test_image_path, 'rb') as test_image_f:     test_imgs = np.frombuffer(             test_image_f.read(), dtype=np.uint8, offset=16).reshape(-1, 784).astype(np.float32)   # 기존 60000개 training set에서 50000개는 train set으로 사용하고, 10000개는 valid set으로 활용 train_label, valid_label = train_label[:50000], train_label[50000:]  train_imgs, valid_imgs = train_imgs[:50000], train_imgs[50000:]   # train set, validati on set, test set을 튜플 자료형으로 저장 out_data = ((train_imgs, train_label),             (valid_imgs, valid_label),             (test_imgs, test_label))   # pickle file로 dataset 데이터 포맷 맞춰주기 with gzip.open(out_file_name, 'wb') as out_f:     pickle.dump(out_data, out_f) 이 과정을 통해 나온 결과물, fashion-mnist.pkl.gz 를 Jupyter Notebook이 있는 경로에 업로드합니다.fashion-mnist.pkl.gz가 업로드 되었습니다!3. 머신러닝 학습하기아까 사용했던 활용했던 숫자 MNIST 스크립트를 그대로 사용하겠습니다. show_digit()을 이름만 바꾼 show_fashion()으로 데이터를 살펴보니 드레스가 보입니다.조금 전에 했던 숫자 MNIST와 똑같은 과정을 SageMaker를 이용해, 학습 → 테스트 → 예측해보니 아래와 같은 예측 결과를 얻을 수 있었습니다. 신발은 신발끼리, 바지는 바지끼리, 가방은 가방끼리 분류된 게 너무나 신기합니다. (아까 진행한 숫자보다 더 학습이 잘 된 것 같은건 기분 탓일까요…?)머신러닝이라고 겁내지 않아도 됩니다! 유저들에게 더 좋은 서비스 제공할 수 있으니까요. 지금까지 브랜디 개발2팀의 단아한 개발자 오연ㅈ….참사를 막아주세요.앗, 잠시만요!! 중요한 것을 놓칠 뻔 했네요.저처럼 테스트를 하면 그냥 지나치지 마세요. 자동 결제로 출금되는 뼈 아픈 경험을 할 수도 있습니다. 반드시 이용했던 서비스들을 stop 하거나 terminate 해주세요. (Clean-up단계) 자세한 내용은 여기를 클릭하세요.지금까지 Brandi 개발 2팀, 단아한 개발자 오연주였습니다!# entire script (숫자 Mnist) # 오호 드디어 coding start! # 이제부터 Brandi의 단아한 개발자, 저를 따라오시면 됩니다 :) # 노트북 Block을 실행하는 방법은 Shift + Enter 입니다 from sagemaker import get_execution_role role = get_execution_role()  # 초기에 설정해 뒀던 IAM role 가져오기 bucket = 'sagemaker-julie-test' # 초기 단계에 만들었던 S3 Bucket 이름 적기 %%time import pickle, gzip, numpy, urllib.request, json   # 여기서 잠깐, 생소한 라이브러리 설명을 드릴게요! # pickle: python식 데이터 압축 포맷 # numpy: 수치 계산을 하기 위한 python package # Load the dataset urllib.request.urlretrieve("http://deeplearning.net/data/mnist/mnist.pkl.gz", "mnist.pkl.gz") with gzip.open('mnist.pkl.gz', 'rb') as f:     train_set, valid_set, test_set = pickle.load(f, encoding="latin1")     # matplotlib로 그리는 그림이 jupyter 노트북에 바로 보여줄 수 있도록 설정 %matplotlib inline import matplotlib.pyplot as plt # 도표나 그림을 그릴 수 있게 해주는 라이브러리 plt.rcParams["figure.figsize"] = (2, 10) # 그림의 크기 지정 def show_digit(img, caption='', subplot=None):     if subplot is None:         _,(subplot) = plt.subplots(1,1)         imgr = img.reshape((28, 28))     subplot.axis('off')     subplot.imshow(imgr, cmap='gray')     plt.title(caption)   # train_set의 그림과[0] 데이터 이름[1]을 예시로 보여준다 show_digit(train_set[0][30], 'This is a {}'.format(train_set[1][30]))   # 학습을 하기 위해 학습 알고리즘 및 데이터 경로 설정! from sagemaker import KMeans data_location = 's3://{}/kmeans_highlevel_example/data'.format(bucket) output_location = 's3://{}/kmeans_example/output'.format(bucket)   print('training data will be uploaded to: {}'.format(data_location)) print('training artifacts will be uploaded to: {}'.format(output_location))   kmeans = KMeans(role=role,                 train_instance_count=2,  # 장비 2대를 사용하여 학습하겠어요!                 train_instance_type='ml.c4.8xlarge',                 output_path=output_location,                 k=10,  # 아래 그림을 참고해 주세요!                 data_location=data_location) %%time   # 학습 시작! kmeans.fit(kmeans.record_set(train_set[0]))   %%time # 모델을 만든 후 사용하기 위하여 배포하기 kmeans_predictor = kmeans.deploy(initial_instance_count=1,                                 instance_type='ml.m4.xlarge')                                  # valid_set에 30번째 sample을 테스트 해보기 result = kmeans_predictor.predict(valid_set[0][30:31])  print(result)   %%time   # vaild_set에 있는 0번부터 99번까지의 데이터로 cluster를 예측 해보자 result = kmeans_predictor.predict(valid_set[0][0:100])   # 예측 결과에 대한 cluster 정보를 수집 clusters = [r.label['closest_cluster'].float32_tensor.values[0] for r in result]   # 각 cluster별 예측된 이미지 출력 for cluster in range(10):     print('\n\n\nCluster {}:'.format(int(cluster)))     digits = [ img for l, img in zip(clusters, valid_set[0]) if int(l) == cluster ]     height = ((len(digits)-1)//5)+1     width = 5     plt.rcParams["figure.figsize"] = (width,height)     _, subplots = plt.subplots(height, width)     subplots = numpy.ndarray.flatten(subplots)     for subplot, image in zip(subplots, digits):         show_digit(image, subplot=subplot)     for subplot in subplots[len(digits):]:         subplot.axis('off')     plt.show() 출처Getting Started - Amazon SageMaker CodeOnWeb - 머신러닝 초보를 위한 MNIST fashion-mnist 글오연주 사원 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1931

입사 후 4개월, 나는 그동안 무엇을 했을까

8월 18일에 입사하여 글을 쓰는 오늘까지 4개월이란 시간이 흘렀다. 트레바리는 4개월을 한 시즌으로 묶어 운영하는 멤버십 서비스이기 때문에 트레바리에서 4개월을 일했다는 건 한 시즌에 필요한 모든 시기를 거쳤다는 의미이다. 4개월을 함께 해야지만 비로소 트레바리를 한 번 했다고 말할 수 있게 된다. 나는 이제서야 트레바리에서 한 번 일했다.트레바리에서의 한 번을 보내며 나는 그동안 무엇을 했는지 정리해보려고 한다. 할 말이 많다 보니 이번 글에서는 기능적으로 무엇을 했는지만 이야기할 예정이다. 어떻게 일했는지, 잘했던 점은 무엇이었는지, 아쉬웠던 점은 얼마나 많았는지에 대한 건 아쉽지만 다음 글에 담기로 했다. 4개월 동안 내가 진행한 일 중 큰 단위의 작업 위주로 살펴보고자 한다.4개월 동안 내가 한 일은 크게 두 가지다. 첫 번째는 기존의 웹 서비스를 개선하는 일. 두 번째는 노가다로 했던 일들에 IT를 끼얹는 일. 두 가지 일에 대한 요구 사항들은 모두 추상적인 문장으로 주어졌고, 나의 역할은 그 추상적인 요구들을 정리하여 실질적인 기능으로 정의하고 구현하는 것이었다. 그동안 어떤 요구 사항들이 있었고 그에 대해 어떤 결과물을 내었는지 정리해보았다.1. 독후감을 활성화되게 만들어주세요.입사 후 최우선으로 개선이 요구됐던 부분은 독후감이었다. 독후감은 트레바리 서비스가 독특하다는 평가를 듣는 이유 중 하나이다. 아무리 돈을 내고 온 멤버일지라도 우리가 내어준 400자의 독후감이라는 숙제를 해오지 않으면 독서 모임에 참가하지 못한다. 우리 크루들은 독후감을 통해 자신의 생각을 한번 정리하고 참가한 독서 모임이 아무런 준비 없이 맞닥뜨리는 독서 모임보다 더 풍성해짐을 안다. 그렇기에 멤버들이 트레바리 홈페이지에서 더 열심히 독후감을 쓰고, 더 많이 다른 사람들의 독후감을 읽고, 더 다양하게 대화할 수 있도록 만들어야 했다.디자인 개선[이전 디자인]일을 시작하자마자 가장 먼저 갈아치우기 시작한 건 디자인이었다. 페이지에 보이는 정보들의 가독성이 나빴다. 독후감 정보와 관련 없는 이미지 배경을 가지고 있었고, 모바일에서는 본문을 포함하여 모든 요소들의 배열이 일정하지 않았다. 가장 문제였던 점은 좋아요 기능이 있음에도 불구하고 유저들이 좋아요 버튼이 있는지를 몰라 활성화가 되지 않는 것이었다.(대표님 얼굴과 우왕이라는 글자가 떡하니 자리 잡은 곳이 좋아요 버튼이다.) 답댓글 없이 한 줄로만 나열된 댓글도 불편했다.[변경한 디자인]전반적으로 컨텐츠가 더 잘 보일 수 있도록 변경했다. 불필요한 배경 이미지를 빼고 책 정보를 추가했다. 좋아요 버튼도 보다 쉽게 인지할 수 있도록 보편적인 모양의 하트로 바꾸었다. 한 줄로만 나왔던 댓글에는 대화하는 듯한 느낌의 UI로 변경하고 답댓글 기능을 추가했다. 특히 모바일에서 더 편하게 쓸 수 있도록 각 요소들을 일정하게 배열했고, 이미지로는 보이지 않겠지만 독후감을 읽고 목록으로 다시 갈 때마다 다른 모임 정보가 뜨는 이상한 시나리오도 개선했다.넛지 만들기더 나은 디자인만으로는 부족했다. 멤버들이 실제로 더 많이 좋아요를 누르고 댓글을 달고 싶게 만들어주는 넛지가 필요했다. 좋아요 수에 따라 재밌는 워딩이 나오고, 댓글 입력 창의 워딩이 항상 다르고 등의 디테일한 요소들을 살렸다. 페이스북 공유하기 기능도 추가했으며 우리 모임에 놀러 오는 멤버들을 보여주는 UI도 추가하는 등의 작업을 진행했다. 하지만 결정적인 한 방이 필요했고 그 한 방은 이달의 독후감 기능이었다.이달의 독후감 선정 기능홈페이지 밖의 운영에서 돌아가던 이달의 독후감이라는 시스템이 있었다. 매 모임마다 가장 좋았던 독후감을 선정하는 것이었는데 잘 알지 못하는 멤버들이 많아 좋아요 수 자체가 적었고, 선정된 독후감을 찾아보기 어려워 활성화가 되지 못했었다. 그래서 이달의 독후감 시스템을 홈페이지에 어워드 형태로 옮겨오면 동기부여와 동시에 별도의 안내 없이도 이달의 독후감 시스템을 학습시킬 수 있겠다고 생각했다.그래서 결과는?결과는 데이터로 나타났다. 디자인과 기능 개선 후 독후감 한 개 당 평균 좋아요/댓글 수가 대략 150% 증가했다. 크루들이 매번 이달의 독후감을 선정하고 하이라이트 구문을 뽑는 오퍼레이션도 줄일 수 있었다. 변경 후 독후감 쓰는 것이 더 즐거워졌다는 멤버 피드백도 종종 들을 수 있었다.2. 멤버십 신청 페이지를 개선해주세요.멤버십 신청 페이지는 트레바리 멤버가 아닌 유저들이 가장 많이 보게 되는 페이지다. 트레바리가 어떤 곳인지 어필하고 결제까지 진행하게 하는 가장 중요한 역할을 하고 있다. 흔히들 말하는 판매 페이지로 트레바리에서 가장 중요한 서비스인 독서 모임을 파는 곳이다. 그 중요성에 비해 디자인과 기능이 모두 엄청나게 부실했고 개선해야 했다.디자인 개선[이전 디자인]대체 트레바리가 어떤 곳인지 알 수 있는 부분이 하나도 없었다. 독서 모임에 대한 설명마저 줄글이 전부였다. 내가 트레바리 독서 모임에 가면 어떤 분위기를 즐길 수 있고 만나는 사람들은 어떨지 상상하기 어려웠다. 모바일에서는 특히 불편했고 필수적인 정보들만 보이는 곳에 불과했다.[변경한 디자인]각 독서 모임에 대한 소개가 풍성하지만 편하도록 변경했다. 이전과 다르게 사진을 많이 활용하여 트레바리 독서 모임이 어떤 분위기인지 보여주고 싶었다. 설명 글도 더 잘 읽힐 수 있도록 배치를 중점적으로 신경 썼고 포인트 컬러를 틈틈이 사용했다. 같은 모임이지만 다양한 시간과 장소가 있는 독서 모임인 경우에는 한 페이지에서 한 번에 볼 수 있도록 구성했다. 모바일 접속자가 압도적으로 많은 만큼 모바일 UI에 많은 시간을 들였다.결제 기능 추가멤버십 신청 페이지에서 가장 큰 문제는 결제였다. 실시간 결제가 아닌 계좌 이체로만 가능하게 되어있다 보니 엄청나게 불편했다. 유저들도 수동으로 이체를 해야 했고, 담당하는 대표님도 24시간 잠도 못자며 휴대폰을 붙잡고 있다가 계좌 이체 알림이 올때마다 등록 처리를 해주어야 했다.(매 시즌 대표님 혼자 몇천 명의 계좌 이체를 확인하고 등록해주셨다. 그래서 멤버십 신청 기간 때에는 제대로 자보신 적이 없다고...)그런데 트레바리는 작은 회사에다 무형의 서비스를 팔고 있다 보니 PG사를 통한 결제를 붙이는 게 어려웠다. PG를 제외한 편하게 결제할 방법을 찾다가 토스 결제를 찾아보게 되었다. 찾자마자 바로 미팅을 진행했고 토스 측에서도 트레바리의 가치를 잘 봐주셔서 미팅부터 결제 연동까지 빠르게 진행하여 구현했다.사랑해요 토스그래서 결과는?막상 개선하여 배포하니 예상보다 저조한 유저 반응이 나타났다. 물론 지난 시즌보다는 훨씬 더 많은 유저분들이 등록하시기는 하였으나 기대했던 목표치에는 못 미쳤다. 디자인이 좋아지고 이용하기 편해지면 당연히 등록 효율이 몇 배로 높아질 거라고 생각했으나 생각처럼 되지 않았다. 신청 기간 내내 저조한 이유에 대한 가설을 세우고, 변경하고, 데이터 보기를 반복했다. 그 과정에서 몇몇 유저분들과 인터뷰를 진행했고 막판에 등록에 영향을 미치는 것은 의외로 홈페이지 사용성이 아닌 다른 곳에 있음을 발견했다. 아쉽게도 늦게 원인을 찾아 더 많은 것을 해보기 전에 신청 기간이 끝나버렸지만, 다음 시즌에는 이번 시즌보다 뾰족하고 탁월하게 개선할 수 있겠다고 생각했다. 영훈님과 함께 공부도 시작해보기로 했다.결제 부분에서는 자세한 데이터를 공개할 수는 없지만 많은 유저들이 토스를 통해 결제를 진행했다. 원래도 트레바리는 N빵 할 일이 많아 토스 송금을 이용하는 유저들이 많았지만 이번 결제 연동을 덕분에 새로 쓰게 된 분도 많아진 것 같았다. 핀테크에 대한 막연한 불안감 때문에 쓰지 않았다는 유저들도 있었지만 막상 써보니 엄청나게 편해서 놀랐다는 피드백도 많이 받았다. 아마도 트레바리에서는 앞으로 계속 토스 송금/결제를 활발하게 사용할 것 같다.3. 트레바리가 어떤 곳인지 보여줍시다.위에서 말한 등록에 영향을 미치는 것은 서비스에 대한 설득이었다. 그동안 트레바리는 지인의 소개로 오는 유저들이 많았고, 기사를 보고 오는 유저들이 많았고, 소문을 듣고 오는 유저들이 많았다. 그래서 따로 트레바리가 어떤 곳인지 잘 설명할 필요성이 적었던 것 같다. 이제는 유저들이 점점 많아지면서 트레바리가 어떤 곳인지 적극적으로 보여줄 필요가 있었고 방치되어 있던 랜딩페이지를 끄집어냈다.[이전 랜딩페이지]이곳만 봐서는 트레바리가 어떤 곳인지 알 수 없었다. 트레바리가 얼마나 매력적인지 어필이 되지 않았고 어떤 활동을 할 수 있는지도 알기 어려웠다.[변경한 랜딩페이지]트레바리가 지향하는 가치들을 더 많이 설명했다. 중간중간 트레바리 사용설명서 영상을 볼 수 있는 곳을 추가했고 실제 멤버들의 후기도 담았다. 트레바리는 독서 모임을 제공하는 서비스이므로 대표적인 독서 모임이 무엇이 있는지도 보여주고 싶었다. 각종 미디어에서 이야기하는 트레바리와 멤버들만을 위한 혜택도 정리해두었다.그래서 결과는?급하게 만드느라 트레바리의 매력을 아직 반의반도 못담았다고 생각한다. 랜딩페이지만 봐도 트레바리가 어떤 곳이고 트레바리를 통해 당신이 얼마나 더 멋있어질 수 있는지를 보여주고 싶다. 랜딩페이지는 꾸준히 만지고 다듬어야 할 과제로 남겨두었다.4. 손에 잡히는 무언가를 주기(일명 손잡무)한 시즌을 끝낸 멤버들에게 각자가 한 시즌 동안 무엇을 해왔는지 쥐여주고 싶었다. 그래서 시즌 말 약 1700명의 멤버들 모두에게 개개인이 이뤄온 활동 데이터를 이미지로 재밌게 엮어 나눠주었다. 기발한 워딩과 이미지는 이 방면에 재능이 있는 세희님과 지현님이 함께해주셨다.[1705 시즌 손잡무][1709 시즌 손잡무]1700명 모두에게 개인화된 이미지를 노가다로 만들어주는 것은 불가능했다. 자동화를 하기 위해서 SQL 쿼리를 통해 필요한 로우 데이터를 추출하고, 스케치라는 디자인 툴을 활용해 이미지 생성을 자동화했다. 덕분에 모든 멤버들의 이미지를 한땀한땀 만드는 노가다를 피하면서도 개인화된 컨텐츠를 제작할 수 있었다. 이 방법은 이다윗님의 코드로 100명 이상의 네임택 한 번에 디자인하기 글을 보고 영감을 받아 가능했다.그래서 결과는?성공적이었다. 인스타그램에 #트레바리를 검색하면 손잡무를 나눠준 시점에 많은 멤버분들이 공유해주신걸 볼 수 있다.(개근상 받으신 분들이 제일 많이 공유해주셨다.) 이미지를 공유해주시면서 4개월 동안 얼마나 즐거웠고 많이 배웠는지에 대한 후기도 소상히 적혀있는 경우도 많아 더욱 뿌듯한 결과물이었다.5. 그 외 각종 버그/개선 요구 사항 해결도 해주세요.각종 UI 및 사용성 개선여러 페이지들의 UI를 개선하고 기능을 개선하여 배포하였다. 자잘한 기능 추가부터 페이지 통째로 갈아엎기까지 손을 댈 수 있는 리소스만큼 건들여보고 개선했다. 이 과정에서는 우선순위를 정하는 일이 중요했는데 우선순위에 대한 이야기는 후에 다시 해볼 예정이다.각종 버그/요구 사항 해결 + 그에 따른 CS내가 만든 것도 많았지만(…) 그거말고도 도대체 개발자가 없을땐 홈페이지가 어떻게 굴러갔지 싶을 정도로 버그가 많았다. 버그도 많고 요구되는 개선 사항도 많았다. 줄어들고는 있지만 아직까지도 버그 및 요구 사항에 응대하는 시간이 하루에 한 시간씩은 꼬박꼬박 들고 있다. 더 많이 줄여나가는 것을 목표로 하고 있다.자동화독서 모임을 이끄는 크루들이 노가다를 하느라 고생하는 시간이 많다. 위에서 이야기 했던 계좌 이체 확인이 가장 큰 사례이다. 그 외에도 개설되는 클럽 데이터 입력을 어드민에서 며칠동안 노가다로 진행해야하는 등의 낭비가 많았다. 이런 부분에서 IT를 끼얹어 공수를 덜 들이고 빠르게 끝낼 수 있도록 엑셀 import 등의 기능을 구현했다.트레바리의 한 번을 끝마치며 나는 그동안 무엇을 했는지 정리해보았다. 쓰다보니 만족스러운 것보다 아쉬운 것들이 눈에 더 많이 들어온다. 무엇이 아쉬웠나 하면 할말이 너무나도 많아 다른 글에 써보기로 하고 이번 글은 기능적인 이야기로만 마무리했다.돌이켜 생각해보면 트레바리에서 쓰이는 기술 스택인 루비도 레일스도, 서버 인프라도 하나 모르는 나를 믿고 이 모든걸 배우고 익힐때까지 기다려준 크루들이 새삼 대단하다고 느낀다. 그 과정에서 실수로 인한 버그도 엄청 많았고 그 버그 때문에 불필요하게 운영 코스트가 늘어났을 때도 있었지만 나무란 적 한 번 없이 격려와 함께 기다려주고 믿어주었다. 그래서 더 열심히 달릴 수 있었던 것 같다.아쉬움과 감사함 때문에라도 다음 4개월에는 일을 더 '잘'하는 사람이 되어야겠다고 다짐했다. 앞으로도 계속 성공하고 실패하고, 배우고 성장한 일들을 꾸준히 기록해나가며 일을 더 잘하는 사람이 되고 싶다. 다음 4개월은 지난 4개월보다 보다 더 실질적이고 큰 변화들을 만들 수 있는 사람이 되어야 겠다. 걱정반 기대반이지만 설레는 마음으로 새로운 시즌을 맞이하며 글을 끝맺으려 한다.어떻게 하면 더 잘할지 고뇌하는 모습의 크루들#트레바리 #기업문화 #조직문화 #CTO #스타트업CTO #CTO의일상 #인사이트
조회수 9108

왜 SQLite 에서 Realm 으로 옮겼는가?

SQLite 와 Realm잔디 앱은 2015년 중반부터 앱 내에 Offline Caching 기능이 포함되면서 본격적으로 Local-Databae 를 사용하기 시작했습니다.당시에 Realm 과 SQLite 를 검토하는 과정에서 다음과 같은 사유로 Realm 을 포기하였습니다.1.0 이 아직 되지 않은 미성숙된 상태의 라이브러리사용 사례에서 리포팅되는 버그들 (CPU 지원 등)Data 의 상속을 지원하지 않는 문제Robolectric 미지원 (안드로이드 팀 당시 테스트 프레임웍은 Robolectric 이었으며 현재 Android Test Support Library 입니다.)위의 문제로 인해 SQLite 를 선택하였고 여러 SQLite-ORM Library 를 검토한 후 ORMLite 를 선택하였습니다.누구보다 가볍고 빠르게2016년 6월경 앱의 핵심 데이터에 대해 개선작업이 되면서 그에 따라 기존의 Cache Data 로직도 많은 부분이 변경되었습니다. 그에 따라 실시간성으로 DB 를 대상으로 Read-Write 동작이 발생하게 되었습니다. Locking 등에 대한 처리가 되면서 성능에 대한 이슈가 계속적으로 발생할 수 밖에 없었습니다.간헐적인 성능 이슈는 사용자에게 나쁜 UX 로 다가갈 수 있기 때문에 다음과 같은 병목지점들에 대해 성능 향상을 꾀하였습니다.서버와의 통신 향상비지니스 로직 개선내부 DB 로직 향상서버와의 통신 향상병목 지점이 되는 것으로 판단되는 API 를 찾아 원인을 분석하여 개선요청을 서버팀에서 개선할 수 있도록 하였습니다.비지니스 로직 개선불필요한 객체 생성, 비동기로 처리해도 되는 동작들에 대해서는 로직 수정, 최소한의 검증 후에만 앱 실행, 네트워크 동작 최소화, 캐싱 활용 등 다양한 전략을 시도하였습니다.내부 DB 로직 향상SQLite 를 대상으로 빈번한 쿼리 작업을 최소한으로 하기 위해 2~3개의 쿼리로 이루어진 부분에 대해서 최소한의 쿼리만으로 동작하도록 여러 시도를 하였습니다.ORMLite 의 한계점ORMLite 를 대상으로 여러가지 시도를 하였습니다. 쿼리를 최소한으로 하고 1:N, N:M 동작에 대해서 로직 중간에 Query 가 발생하지 않도록 애초에 Join Query 를 하도록 하는 등 여러가지 전략을 시도하였으나 궁극적으로 ORMLite 자체에 대한 성능을 개선하는 것은 불가능하다는 결론이 도출하였습니다.여러 시도를 하였으나 고작 10~20% 정도의 성능향상밖에 없었으며 이는 사용자 관점에서 여전히 느릴 수 있다고 느끼기 충분한 수준이었습니다. 기존에 목표했던 100ms 이하의 쿼리를 기대하기엔 어려운 상황이었습니다.그래서 GreenDAO, Requery 라이브러리를 검토하였습니다.GreenDAO 의 문제점GreenDAO 를 검토하는 과정에서 겪은 가장 큰 문제점은 실제 Object 코드에 GreendDAO 코드가 생성이 붙으면서 유지보수에 큰 걸림돌이 될 수 있다는 것이 예상되었습니다.Requery 의 문제점성능면에서 ORMLite 에 비해서 큰 개선을 가져오지 못했습니다. Requery 는 JPA 를 가장 잘 채용한 것으로 알려져 있지만 그렇다고 SQLite 자체의 성능을 극적으로 개선했다고 보기엔 어려운 부분들이 있었습니다.SQLite vs RealmSQLite 가 가진 자체적인 성능 이슈를 SQLite 기반 라이브러리 범위안에서는 개선할 수 없다는 결론에 도달하였습니다.검토 방법 : 기존의 Object 를 대상으로 ORMLite 와 Realm 을 대상으로 성능을 검토합니다.데이터는 1:N / 1:1 관계가 되어 있는 여러 Object 의 집합으로 구성되어 있다.Database 에서 데이터를 가져올 때는 Eager Loading 방식으로 택한다.Write : 20회, Read : 20회 를 수행했고 그에 대한 평균 성능을 비교한다. SQLiteRealm성능 향상Write4039ms1142ms3.5xRead6010ms2450ms2.5x(Realm 의 벤치마크 정보와 너무 상이하여 재테스트한 결과 수정하였습니다.)위의 비교차트에서 봤듯이 Realm 은 무시무시한 성능이 입증되었습니다.도입 검토시에 Realm 버전은 2.0 이었기 때문에 충분히 신뢰할 수 있을 만큼 성숙되었다고 판단하고 최종적으로 도입을 결정하였습니다.Realm 도입 과정에서 문제점Realm 을 도입한다고 해서 여전히 잠재적인 문제가 해결된 것은 아니었습니다.파악된 다음 문제를 해결 해야 했습니다.Primitive 타입에 대해 Collection 저장을 지원하지 않는다.RealmObject 에 대한 호출 Thread 를 유지해야 한다.상속을 지원하지 않는다.Primitive 타입에 대한 Collection 관리를 해결하기이 문제는 ORMLite 에서 이미 겪었기 때문에 의외로 쉽게 구할 수 있었습니다. long, int 등에 대한 Wrapper 를 만들고 Json Convert 등의 과정에서 Post Processing 과정에서 Wrapper 로 데이터를 이관하도록 처리하였습니다.// example class Data extends RealmObject { private transient List refs; private List refIds; } class RealmLong extends RealmObject { private long value; } RealmObject 에 대한 호출 Thread 분리Realm 은 Object 에 대해 query 후 객체를 받는다 하더라도 실제로 객체 내 데이터르 접근할 때는 다시 Query 로 접근하기 때문에 실제로 Object 전체에 대해서 Eager Loading 방식으로 접근해야 합니다.Jandi 는 싱글톤 객체를 통해 데이터베이스에 접근하며, Background Thread 에서 진행하고 UI Thread 에서 객체 내 변수에 접근해서 UI 에 그리는 작업이 빈번하기 때문에 Thread 독립을 반드시 해야했습니다.Realm 에서는 Eager-Loading 을 지원하고 있습니다. Realm.copyFromObject() 를 사용하면 Return 값이 Eager-Loading 된 Object 가 반환됩니다.단, Realm 의 가장 큰 특징이로 보는 ZeroCopy 를 포기하는 것이기 때문에 신중하게 생각해야 합니다.// example public Chat getChat(long chatId) { return execute((realm) -> { Chat it = realm.where(Chat.class) .equalTo("id", chatId) .findFirst(); if (it != null) { return realm.copyFromRealm(it); } else { return null; } }); } 상속을 지원하지 않는다.가장 큰 문제였는데 해결방법을 찾을 수 없어 결국 상속을 포기하고 모든 Data 를 1개의 Object 에 표현하기로 하였습니다.위의 3가지 문제를 이렇게 해결해서 안드로이드팀에서는 1차적으로 도입을 완료하였습니다.결론현재까지 Realm 전환에 있어서 성공적인 도입으로 판단되어 차후에 다른 데이터에 대해서도 하나씩 DB 이전을 할 예정입니다.Realm 은 이제 충분히 신뢰할 수 있을만큼 성숙되었다고 생각이며 Realm 에서 처음부터 강조하던 성능또한 믿기 어려울 정도로 빨라졌습니다. 더 빠른 Mobile Database 를 원하신다면 Realm 을 적극 추천합니다.#토스랩 #잔디 #JANDI #개발 #개발환경 #업무환경
조회수 7043

BPM, RPA 그리고 Process Mining(프로세스마이닝)

새로운 IT 기술의 등장과 기업 환경의 변화로 새로운 과학 경영 기법들이 비즈니스 유행어처럼 등장하고 사라지지만 그 가운데서도 변하지 않는 것이 있다면 프로세스 개선과 관련된 대한 끊임없는 노력과 관심일 것입니다.프로세스 마이닝은 이벤트 로그를 기반으로 비즈니스 프로세스를 분석할 수 있는 프로세스 관리 기술입니다. 정보 시스템에서 기록한 이벤트 로그에 포함된 패턴 및 세부 정보를 식별하기 위해 별도의 분석 알고리즘이 이벤트 로그 데이터에 적용됩니다. 프로세스 마이닝은 프로세스의 효율성과 이해를 향상시키는 것을 목표로 하며, “자동화된 비즈니스 프로세스 발견” ABPD (Automated Business Process Discovery)이라는 좀 더 일반화된 명칭으로 불리기도 합니다.이러한 프로세스 마이닝은 어디서 갑자기 나온 개념은 아니고 기존 비즈니스 프로세스 관리 기법에 대한 연구와 데이터 분석 기술이 합쳐져서 나온 산물이기에 “비즈니스 프로세스”와 관련된 기술들을 살펴보고 프로세스 마이닝과의 연관성을 찾아보고자 합니다.BPM (Business Process Management)프로세스 마이닝은 일반적으로 BPM과 데이터 마이닝이 겹치는 중간 영역에 위치합니다.BPM은 비즈니스 프로세스를 발견, 모델링, 분석, 측정, 개선, 최적화 및 자동화하기 위해 다양한 방법을 사용하는 운영 관리 기법을 의미하며, 프로세스를 관리하여 기업 성과를 향상시키는 데 중점을 둡니다. 좁은 의미에서 BPM은 업무 프로세스를 사전에 모델링하고, 설계된 프로세스 대로 업무 결제, 승인, 구매 등의 업무 등이 자동화되어 흘러갈 수 있도록 도와주는 IT 시스템을 지칭하기도 합니다..BPM은 Top-Down 방식으로 프로세스 모델을 그려서, 해당 프로세스 모델 대로 업무를 수행하도록 강제하는 방식이라면 프로세스 마이닝은 이미 수행된 업무로부터 프로세스 모델을 도출하는 Bottom-up 방식을 따릅니다.  하지만 점점 복잡해져 가는 기업 업무 활동을 BPM처럼 중앙 집권적 방식으로 모든 것을 통제하기에는 한계가 있습니다.  BPM의 통제를 벗어난 다양한 여러 시스템을 업무 관점에서 통합적으로 관리하고 모니터링하기 위해서는 개별 시스템은 그대로 두고 이로부터 쏟아져 나오는 로그를 통해 프로세스를 관리하는 분권적 방식이 BPM의 한계를 보완하는 역할을 합니다. RPA (Robot Process Automation)로봇 프로세스 자동화 (RPA)는 소프트웨어 로봇 또는 AI (인공 지능) 작업자의 개념을 기반으로 하는 사무 자동화 기술의 새로운 형태입니다.소프트웨어 '로봇'은 컴퓨터 시스템의 사용자 인터페이스와 상호 작용하는 인간의 행동을 복제하는 소프트웨어 응용 프로그램입니다. 예를 들어, ERP 시스템에 데이터 입력을 실행하거나 실제로 비즈니스 프로세스를 수행하는 것이 소프트웨어 로봇의 일반적인 활동이 될 것입니다. 소프트웨어 로봇은 사람과 동일한 방식으로 사용자 인터페이스(UI)에서 작동합니다. 이것은 기존에 애플리케이션 프로그래밍 인터페이스(API)에 기반한 전통적 형태의 IT 통합과 크게 다릅니다. 즉, 사용자 인터페이스의 데이터 아키텍처 계층을 기반으로 한 기계 간(machine-to-machine) 통신 형태를 취합니다.앞서 언급한 BPM이 프로세스 개선을 위해 프로세스 자체를 재설계하고 변경하려는 방식이라면 RPA는 사람이 하던 현재 방식을 그대로 모방하여 소프트웨어로 대체하여 자동화하는 방식입니다. 이러한 RPA가 업무에 더 많이 적용될 수록 더 많은 시스템 로그가 나올 것이고 이에 대한 성과 분석과 모니터링이 필요해질 것입니다. 프로세스 마이닝은 RPA 도입 전 초기 단계에 전체 프로세스를 분석하여 RPA가 적용될 만한 구간을 식별하여 타당성을 검증하고, RPA 도입 이후의 전후 비교를 통해 지속적으로 업무 효율성을 측정할 수 있는 방법을 제공합니다.앞서 살펴본 것처럼 BPM, RPA, Process Mining 이 세 가지는 서로의 영역을 다투는 것이 아니라 상호 보완적인 존재로 볼 수 있습니다.프로세스 마이닝은 “프로세스”와 관련된 다양한 시스템과 활동들에 대해서 데이터에 근거한 현재 프로세스의 모델링 및 성과 측정 방법을 제공합니다. 이를 통해 과거 혹은 신규 프로세스 혁신 기법들과 맞물려 해당 시스템 및 방법론이 성공적으로 수행될 수 있도록 자동화된 “업무 조언자” 역할을 수행하게 됩니다. [참고 자료]https://en.wikipedia.org/wiki/Business_process_managementhttps://en.wikipedia.org/wiki/Robotic_process_automationhttps://www.minit.io/blog/robotic-process-automation-and-process-mininghttps://medium.com/towards-data-science/unleash-the-value-of-process-mining-4e3b5af4e9d8http://www.cdevworkflow.com/bpm-life-cycle/https://www.uipath.com/blog/the-robotic-process-automation-infographic#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트

기업문화 엿볼 때, 더팀스

로그인

/