스토리 홈

인터뷰

피드

뉴스

조회수 450

iOS 개발자를 구합니다!

“세상 모든 광고영상을 누구나 쉽게 만들 수 있게 한다.”영상광고는 사업의 규모와 업종을 막론하고 모든 분야에서 필수적인 요소로 자리잡고 있습니다. 잘 만든 영상광고가 매출로 이어진다는 사실은 검증되었고, 그 중요성은 나날이 증가하고 있습니다.하지만, 영상제작 전문기술 없이 광고영상을 제작한다는 것은 시간과 비용적인 측면에서 매우 어려운 일입니다. 광고영상을 SNS에 업로드 하고 싶은 마케터나 창업가들은 영상 전문가나 디자이너가 되는 것을 꿈꾸지 않습니다. 단지 자신의 서비스와 제품이 멋지게 홍보될 영상을 원하고 있습니다.더브이플래닛은 전문기술 없이도 누구나 쉽고 빠르게 광고영상을 제작할 수 있는 브이플레이트를 통해 많은 마케터들과 창업가들이 겪는 시간과 비용에 대한 어려움을 해소할 것입니다.“더브이플래닛”에서 영상광고 생태계의 흐름을 바꿀 iOS개발자를 모집합니다.광고영상을 누구나 쉽게 만들 수 있도록 함께 고민하고 시장을 주도해나갈 분을 애타게 찾고 있어요.우리는 한사람 한사람의 소중한 능력들이 맘껏 발휘될 수 있도록 존중과 배려로 서로를 응원하고 있어요. 우리와 함께 소중한 능력을 맘껏 발휘하실 분들의 많은 지원 부탁드려요.
조회수 1467

코인원 마이페이지가 더욱 더 새로워졌습니다 :) - 유저플로우셀 팀터뷰

웹서비스에서 나만의 서비스 이용내역과 개인정보를 확인할 수 있는 공간을 ‘마이페이지'라고 하죠. 유저들은 마이페이지를 통해 나의 상태를 체크하며 해당 서비스에 좀 더 애착을 갖기도 합니다. 이번에 코인원 마이페이지도 더욱 더 새로워지면서 애정이 가득해졌다는 유저들의 제보가 속속 들어오고 있어요!오늘은 코인원 마이페이지를 새롭게 탄생시킨 유저플로우셀 예은님, 정유님, 현진님, 종헌님과 함께 마이페이지의 모든 것을 파헤쳐보겠습니다.코인원 유저플로우셀은 트레이딩 영역을 제외한 전반적인 서비스 영역을 담당하고 있습니다. 각 서비스에 대한 유저 경로 동선을 만들고 서비스를 제공하며, 누구나 거래를 하고 싶은 코인원을 만들고 있답니다. :-)Q. 안녕하세요, 유저플로우셀 여러분. 자기소개와 함께 현재 업무를 소개해주세요!예은 : 유저플로우셀에서 서비스 기획자로 일하고 있는 지예은입니다. 저는 코인원 유저들이 겪는 문제상황과 UX트렌드 분석을 통해 기존의 서비스를 개선하고 고도화하고 있어요.정유 : 프로덕트 디자이너로 일하고 있는 이정유입니다. 유저플로우셀은 유저와 거래소를 이어주는 모든 페이지를 담당하고 있어요. 저는 기획자들과 함께 유저의 니즈를 페이지에 UI(User Interface)적으로 어떻게 반영할지 고민하고, 이를 디자인 시스템에 녹여 시각적 일관성을 전달합니다.  현진 : 프론트엔드 개발자로 불철주야 개발 중인 박현진입니다. 프론트엔드는 한마디로 코인원 프로덕트에서 실제로 유저들에게 보여지는 웹화면이에요. 저는 유저들에게 보이는 영역을 책임지며 프로그래밍하고 있습니다.종헌 : 웹 API를 담당하고 있는 백엔드 개발자 김종헌입니다. 프론트엔드가 유저에게 보이는 영역을 담당한다면, 저는 보이지 않는 곳인 백엔드에서 입출금 서비스, 거래기록, 개인정보 등 코인원의 다양한 서비스와 유저를 연결하고 있어요.Q. 이번에 마이페이지 개선이 대대적으로 진행되었습니다. 어떤 계기와 방향성으로 개선하게 되었나요?예은 :  마이페이지 개선은 유저의 고충을 파악하기 위한 코인원 고객센터 인터뷰에서 시작되었습니다. 거래소 이용에 필요한 인증, 계정 보안에 대한 관리가 익숙하지 않은 유저들의 ‘페인 포인트(Pain Point)’를 발견했거든요. 서비스 기획자, 디자이너, 개발자가 함께 모여 UI나 정보로 사용자에게 도움을 주고 CS비용을 최소화 할 수 있는 방법을 고민하기 시작했습니다. 우선, ‘마이페이지'는 코인원 서비스를 이용하는 유저 개개인을 챙겨주는 공간이라고 생각합니다. 이번 개선 과정에서 가장 중점을 둔 부분도 ‘고객을 챙겨주는 마이페이지' 경험을 전달하는 것이었어요. 이렇게 설정된 방향성에 따라 유저들의 상태별로 필요한 상황을 안내하도록 구성했습니다. 한마디로 ‘유저 맞춤형 마이페이지’로 진화한겁니다!▲ 더욱 더 새로워진 코인원 마이페이지정유 : 이전의 마이페이지는 엉켜있는 플로우로 인해, 유저가 어떤 상태인지, 어떤 인증과정을 거치고 있는지 인지하기가 힘들었습니다. 목적을 달성하기 위해 마이페이지에 접속했지만 목적 달성을 끝마칠 수 없었죠. 먼저 흩어져 있는 기능, 정보, 구조들을 그룹핑하며 플로우를 개선하는 작업을 시작했어요. 아이데이션 과정을 거치면서 마이페이지를 ‘내 서랍, 내 방' 등 나만의 정체성을 확인할 수 있는 키워드로 정의했습니다. 그리고 키워드를 확장시켜 ‘나의 데이터'를 한 눈에 관리할 수 있는 대시보드 형태의 디자인을 지향하게 되었어요. 결과적으로 현재 마이페이지에는 나의활동, 개인정보관리, 인증단계 총 3 개의 탭으로 위계를 설정했습니다. :D▲ 코인원 거래소 인증단계가 훨씬 간편해졌습니다!Q. 기술적으로는 어떤 변화가 있었을까요?현진 : 마이페이지를 포함해서 코인원 웹 프로덕트에 기술부채(Technical Debt)가 조금씩 쌓여 있었어요. 이 부분을 덜어내기 위해 마이페이지를 개선하면서 ‘기획/디자인/개발’ 삼박자로 변화를 주는 리빌딩(Re-building)을 진행했습니다. 덕분에 기술적으로 관리 포인트가 많이 줄었어요. 이제는 웹 유지/보수가 좀 더 용이하게 되었답니다.종헌 : 그 동안 코인원 웹은 하나의 비대한 서비스로 운영되었습니다. 하나의 서비스가 덩치가 점점 커지다 보니 개발자가 서비스 로직을 온전히 이해하기 어려웠어요. 웹을 유지하고 보수하거나, 새로운 기능을 추가하는 것도 쉽지 않았습니다. 그래서 하나의 비대한 서비스를 여러 개의 작은 서비스로 나누는 작업인 리빌딩을 진행했어요. 여러 작은 서비스로 분리하고 책임 영역을 나누면서 서비스 로직에 대해 제대로 이해하고 체계적으로 코드를 작성할 수 있게 되었습니다.    Q. 마이페이지 개선 전과 후, 달라진 점을 말씀해주세요.예은 : 코인원 마이페이지는 이전보다 유저들에게 더욱 친근하게 다가가고 있습니다. 마이페이지의 콘텐츠가 유저의 상태에 맞춰 변화하며, 유저마다 다음 인증 과정이나 활동 내역을 다르게 안내합니다. 유저가 기능을 먼저 찾지 않아도, 마이페이지가 길을 찾아주는 가이드의 형태를 띄고 있어요.또한 인증단계 별로 수수료나 회원등급이 달라지는데, 유저들이 하나하나 가이드를 보며 찾아볼 수는 없다고 생각해요. 한눈에 자신의 상태를 파악할 수 있도록 UI를 활용하는 것이 중요한 부분이죠. 마이페이지의 개선된 UI로 유저가 코인원의 서비스 정책을 한층 더 깊게 이해하는데 도움이 되었으면 해요.정유 : 유저가 코인원 프로덕트와의 관계성을 인지할 수 있는 디테일들이 추가되었습니다. 가장 대표적인 예시로는 ‘코인원과 함께한 지 000일째 입니다.’라는 문구가 있겠네요. 코인원 유저들에게 ‘챙겨준다'라는 느낌을 전달하기 위해 정말 많은 회의와 아이데이션을 거쳤습니다. 그 과정 중 나왔던 아이디어인데 이번에 반영하게 되었어요. ‘제품’보다는 ‘서비스'로서 느껴질 수 있도록, 대화하는 느낌을 잘 살려주는 포인트이기에 매우 뿌듯했죠.▲ 심...심쿵....!!!!!현진 : 개발자 입장에서 바라봤을 때, 페이지 애니메이션이 가장 좋았어요. 페이지 애니메이션은 웹페이지가 다른 웹페이지로 이동할 때 발생하는 애니메이션을 말합니다. ‘툭' 하고 넘어가는 것이 아니라 ‘sha~(?)’ 하게 넘어가는 느낌이라고 할까요. 페이지와 페이지 사이가 하나의 관계성을 가지고 넘어가게 됩니다. 유저들은 ‘암호화폐 거래소에서 마이페이지에 이런 디테일한 부분까지 신경쓰고 있구나’를 느낄 수 있을거에요. 또한 에러메시지, 경고메시지와 같은 피드백 인터랙션도 정교해졌어요. 사용자와 교감할 수 있는 쪽에 코인원만의 감성이 잘 버무려졌습니다.종헌 : 이전의 코인원 프로필 서비스는 사용빈도가 높지는 않았어요. 그라바타(Gravatar)라는 외부서비스를 사용했는데, 이것을 사용하지 않는 유저들에게 친숙하지 않았거든요. 이제는 코인원에서 프로필 이미지를 정해두고 원하는 이미지로 클릭해서 쉽게 변경할 수 있게 설정했어요. 참고로 프로필 이미지를 설정하는 것이 보안측면에서도 좋습니다. 예를 들어, 은행에서는 프로필 이미지를 설정하면 바로 내가 사용하는 계정인지 아닌지를 알 수 있어요. 코인원에서도 프로필 이미지를 설정하면 내가 가입한 계정인지 아닌지 식별이 가능합니다.▲ 프로필 사진 설정 기능도 많이 이용해주세요 :)Q. 마이페이지의 개선 작업 과정에서 많은 고민이 있으셨을 것 같아요. 가장 중점적으로 생각했던 부분이 있었나요?정유 : 가장 중점이 되었던 부분은 서비스를 이용하는 유저 개개인의 상태를 반영하는거였어요. 유저별로 동일한 정보가 아닌 맞춤형 정보를 제공하기 때문에 한 페이지 안에 들어가는 정보의 위계가 상태값에 따라 계속 변하는 것이 관건이었습니다. 예를 들어, 마이페이지에는 나의 정보를 업데이트하기 위한 많은 버튼들이 들어갑니다. 그럼 유저 케이스별로 중요한 정보를 바꿔보면서 어떤 버튼이 가장 위계가 높은지 고민하고 계산해요. 이러한 과정을 거치면서 유저의 상태값을 쉽게 알려주고 변경할 수 있는 디자인이 완성되었습니다. 예은 : 기존부터 유저 인터뷰를 진행하며 ①신규 유저 ②타사 이용 유저 ③거래소 이용에 문제를 겪고 있는 유저 ④코인원을 오래 이용해준 고마운 유저 케이스까지 다양한 상황에 놓여있는 유저들에게 만족스러운 UX 경험을 드리기 위해 고민해왔습니다. 특히 운영지원셀과 코인원 고객센터 CS로 인입되는 주요 인터뷰들을 중점적으로 수집하여 인증과정에 문제가 되는 것들을 모아서 개선회의를 해왔어요. 이외에 마케팅, 프로덕트쪽도 함께 서비스를 제공하는 공급자 입장에서의 니즈도 취합해 마이페이지에 반영할 수 있었습니다.▲ (절대 설정샷 아니에요) 훈훈하게 회의중인 유저플로우셀!Q. 혹시 개선된 마이페이지를 이용한 코인원 고객들의 후기도 있었나요?예은 : 개선된 마이페이지로 바뀐 지 얼마되지 않아, 유저의 피드백을 직접적으로 접하지는 못했어요. 대신 정량적인 부분에서 여러 수치들이 올라간 것을 확인할 수 있었습니다. 대략적으로 재방문자의 UV(Unique Visitor)수가 개선 전과 대비해서 약 70%정도 크게 증가했습니다. 이전에는 회원가입을 끝마치고 인증과정 중에 페이지를 이탈한 유저도 보였지만, 개선된 후에는 마이페이지 탭 이용빈도가 크게 올라갔습니다. 마이페이지가 좀 더 원활한 거래소 서비스 이용을 위한 가이드 역할을 해주길 기대하면서, 지속적으로 니즈를 관찰하고 개선해 나갈 예정입니다.Q. 마이페이지 이외에도 기억에 남는 유저플로우셀의 프로젝트가 있나요?예은 : 코인원의 수익현황을 한 눈에 볼 수 있는 자산탭이 기억에 남아요. 그 동안 코인원 유저들이 수익률을 확인할 수 있는 기능을 많이 요청했었는데, 팀원들이 함께 고민하여 새로 개편한 기능이라서 그 의미가 컸어요.정유 : 저는 실질적으로 프로젝트에 돌입하기 전에 진행했던 코인원 유저 인터뷰가 가장 기억에 남아요. 인터뷰 내용이 개선점으로 가득찰 줄 알았는데, 응원의 목소리를 전달해주셨거든요. 더 열심히 UI 디자인을 해야겠다는 의욕을 불타오르게 해주었어요!현진 : 코인원 웹프로덕트를 사용하시는 분들이 눈치 채셨는지 모르겠지만, 마이페이지 이전부터 진행해왔던 리빌딩 프로젝트(랜딩, 거래소, 프로차트, 코인원 톡 등)들이 기억에 남아요. 사실 마이페이지 이전 리빌딩 프로젝트들은 기술적으로만 접근하다보니 우여곡절이 많았어요. 그래도 마이페이지 리빌딩은 업무적으로도 많이 배우고, 기술 뿐만 아니라 전체적으로 변화한 것이 보여 저 또한 성장하는 시간이었습니다.종헌 : 이외에도 유저플로우셀은 UX개선을 여러 프로젝트와 함께 진행하고 있습니다. 정신없긴 하지만 개발요소도 새롭고 다이나믹한 것이 많아서 즐겁게 하고 있습니다!▲ 화기애애하게 UI 시안을 보고 있는(?) 유저플로우셀Q. 코인원에서 디자이너 그리고 개발자로 일하는 큰 장점은 무엇인가요?예은 : 코인원에선 셀마다 다른 직무의 인원들이 빠르게 소통하여 의사결정하는 목적조직 형태로 일합니다. 중간중간 기획리뷰, 디자인리뷰 과정을 거치면서 더 꼼꼼하게 일하고, 다른 직무에 계신 분들의 작업도 공유하고 있어요. 거래소에서 일어날 수 있는 다양한 문제 상황을 긴밀하게 대응하고 있죠.정유 : 현재 코인원은 ‘셀(Cell)’이라는 목적조직 형태입니다. PM, 개발자, 디자이너가 한 조직에 속하다보니 Output 나오는 속도가 매우 좋아졌습니다. 또한 여러 직군이 함께 팀웍을 맞추다보니 서비스를 다양한 각도에서 바라볼 수 있고, 이는 디자이너로서 서비스 이해도를 높이는데 굉장히 좋은 환경이라고 생각해요.  종헌 : 코인원은 개발자도 기획 단계부터 적극적으로 참여하여 프로젝트를 설계하고 있습니다. 이로 인해 개발을 하다 예기치 않은 변수가 생기는 일이 거의 발생하지 않아요. 또한 정기적으로 회고를 하며 프로세스의 문제점을 도출해내고, 개선을 위해 다양한 시도를 해볼 수 있다는 것도 장점입니다. 현진 : 현재 코인원 기술본부는 트렌디한 기술을 곳곳에 사용하고 있어요. 기술에 민감하게 반응하는 프론트엔드 개발자분이 코인원에 온다면 기술적으로 매우 만족하실거에요. Q. 앞으로 이루고 싶은 목표는 무엇인가요?예은 : 암호화폐 거래소는 UX를 기획하기에 매우 도전적인 분야입니다. 블록체인 기술이 곳곳에서 화제가 되고 있지만, 아직 업계의 워딩이나 사용에서의 유저 친화적 성숙도가 높지 않은것 같아요. 앞으로의 목표는 누구나 쉽게 거래할 수 있는 암호화폐 거래소를 만드는 것입니다. 점점 더 발전하는 코인원의 모습을 많이 기대해주세요!정유 : 코인원 UI에는 아직 블록체인 공급자적 시선이 많이 담겨있어요. 예를들어, 개발자가 아니면 이해하기 어려운 용어나 UI가 남아있는 부분이 있거든요. 이를 디자인적으로 해소하고 싶습니다. 유저가 갖고 있는 암호화폐 거래 장벽을 낮추고, 코인원의 가치가 잘 반영된 프로덕트를 만드는 것이 목표에요. 종헌 : 코인원에서는 트레이딩 이외에도 여러가지 서비스들을 유저에게 제공하는 다양한 시도를 하고 있어요. 저는 다양한 서비스들을 연결하면서 서비스의 안전장치를 견고하게 쌓아올리고 싶네요. 장애 발생에도 끄떡없는 안정적인 코인원을 유저에게 선보이고 싶습니다.현진 : 대한민국에서 적어도 사용성 1위 암호화폐 거래소를 만들거에요. 유저플로우셀에서 마이페이지 이후에도 많은 프로젝트를 준비하고 있거든요. 매주(?) UX가 점차적으로 개선되는 코인원 거래소의 모습을 확인할 수 있을 거에요. 마지막으로 꼭 하고싶은 말이 있는데, 코인원에 많은 개발자분들이 지원해주셨으면 좋겠어요. 아직 업계에 부정적인 인식이 강하지만, 블록체인이 발전하는 과정을 보며 점차 해소될거라고 믿어요. 기술적으로 발전할 가능성이 무궁무진한 곳이니 기술적인 욕심을 채우고 싶은 분들, 함께 성장하고 싶으신 분들 코인원으로 오세요!▲ 코인원 유저플로우셀 많이 기대해주세요!무엇보다도 긍정적인 에너지로 가득찼던 유저플로우셀의 인터뷰를 들어봤어요.코인원 마이페이지에 큰 변화를 가져온 활기찬 에너지, 다들 느끼셨나요?마이페이지 이후에도 다양한 프로젝트를 준비하고 있는 유저플로우셀. 곧 코인원 웹 거래소를 사용하면서 UX적으로 편리한 사용성을 경험할 수 있을겁니다.끝으로, 특별한 문화를 경험할 기회! 코인원 채용에 함께하는 것도 잊지 마세요 :-)
조회수 1755

제니퍼에서 새로운 가능성을 실험하라

제니퍼는 기업 내부망에 설치되는 On-Premise 방식의 소프트웨어 제품이다. 12년 넘게 국내 점유율 1위를 지키고 있는 제품이다보니 그만큼 고객의 요구사항도 다양하다. 대부분의 솔루션 회사는 제품 개발 초기에 단일 소스코드를 유지하며 개발하는 것을 추구했을 것이다. 하지만 비즈니스를 하다보면 특정 고객을 위한 기능을 추가할 수 밖에 없는 상황이 오게 된다. 보통 이런 경우에는 숨겨진 기능으로 개발하거나 고객사 별로 소스코드를 다르게 가져가기도 한다.기존의 제니퍼를 사용하는 고객들은 애플리케이션 모니터링만이 아닌 브라우저나 스마트폰 같은 클라이언트 영역과 데이터베이스 관리 시스템까지 연계된 통합 모니터링을 하고자하는 요구사항을 오랫동안 요청했었다. 모니터링 제품 간의 연계를 생각하면 약간 생소하게 생각할 수 있는데, 특정 데이터를 수집하고, 이를 가공하여 사용자에게 보여주는 단순한 매커니즘의 하나라고 생각하면 이해가 쉬울 것 같다.즉, 다른 종류의 데이터를 하나의 화면에서 볼 수 있는 통합 환경을 제공해야 한다. 그래서 최근에는 오픈소스로 배포되고 있는 엘라스틱서치나 상용 제품인 스플렁크 같은 로그분석 솔루션이 주목받고 있다. 하지만 위와 같은 제품들을 사용하여 제니퍼 성능 데이터와 연계하여 통합 환경을 구축한다는 것은 말처럼 간단하지 않다. 제품을 구매하고 학습하는 비용이 생각보다 크고, 통합을 위한 별도의 시스템이 갖춰져야 한다는 것은 고객의 입장에서 큰 부담이 된다. 이러한 부담을 덜어주기 위해서 제니퍼는 실험실이라 불리우는 확장 기능을 제공한다. 실험실은 워드프레스의 플러그인과 비슷한 성격을 가지며 코드 레벨 영역에서 확장될 수 있다. 실험실은 처음부터 다른 모니터링 제품과의 연계를 위해 개발된 것은 아니었다. 기획 초기에는 방대한 제니퍼 데이터를 좀 더 다양한 형태의 화면으로 제공하기 위함이었는데, 아무래도 실험적인 요소가 강하다보니 기존의 대시보드나 분석 같은 범주로 들어가기에는 완성도 측면이나 제니퍼의 방향성에 영향을 미칠 수 있다는 판단에 별도의 범주로 만들게 되었다.  실험실이란 이름은 구글 메일의 실험실에서 따온 것인데, 아직 개발 중인 실험적 기능을 위한 테스트 공간이고, 언제든지 변경 또는 중단되거나 사라질 수 있다. 그리고 모든 실험실 소스코드는 깃허브를 통해 공개하는 것이 기본 정책이다. 제니퍼소프트 깃허브에 가보면 실제로 다수의 실험실 프로젝트가 존재한다는 것을 알 수 있다. 그 중 한가지만 간략하게 소개하자면 사용자 관점의 웹 서비스 모니터링 제품인 아르고스와 연계하여 브라우저나 스마트폰 같은 사용자 관점의 성능 데이터를 제니퍼 트랜잭션 데이터와 연계하여 분석할 수 있는 기능을 제공한다. 실은 그동안 고객들에게 사용자 관점의 성능 모니터링에 대한 요구사항이 많았지만 제니퍼 본연의 영역과 확연하게 다른 측면이 있어서 요구사항을 수용하는데 많은 고민이 필요했다. 그래서 우리는 관련된 솔루션 업체를 찾았고, 상호 간의 비즈니스 협력을 통해 서로의 부족한 부분을 보완하기로 결정했다. 실험실은 제니퍼가 시도하고 있는 새로운 기능을 미리 체험해 볼 수 있을 뿐만이 아니라 오픈소스나 관련된 솔루션과의 연계를 하기 위한 화면을 제공할 수 있다. 뿐만 아니라 코드 레벨 영역에서 확장을 하는 것이다보니 제품의 커스터마이징 범위가 넓어진다. 즉, 화면에 대한 고객의 요구사항이 제니퍼의 방향성과 크게 다르더라도 많은 고민을 하지 않고 충분히 원하는 것을 구현해줄 수 있다. 과거와 달리 동일한 데이터라도 좀 더 시각적인 화면을 요구하는 요즘같은 시기에 실험실은 이러한 시도를 하기에 좋은 방법이 된다.제니퍼는 화면 단위의 확장 기능인 실험실 뿐만이 아니라 트랜잭션 데이터가 수집되는 시점이나 특정 이슈가 발생할 때, 생성되는 이벤트 데이터를 어댑터를 통해 전달받을 수 있다. 어댑터도 실험실과 마찬가지로 코드 레벨 영역에서 확장할 수 있다. 실시간으로 전달받은 트랜잭션 데이터는 별도의 스토리지에 저장하여 목적에 맞게 조회해서 사용할 수 있다. 특히 이벤트 관련 어댑터는 가장 많이 사용되는 제니퍼 확장 기능이며, 고객사의 관제시스템 연동에 주로 사용된다.  실험실은 어댑터와 달리 제니퍼 서버에서 전달받은 데이터를 처리만 하는 단순한 구조가 아니었다. 제니퍼와 독립적인 화면 구성에 필요한 모든 요소들을 갖춰야했기 때문에 고려해야할 것들이 너무 많았다.  그럼에도 불구하고 만들게 된 이유는 단순히 필자의 편리함을 위해서였다. 평소에 데이터 시각화에 관심이 많았기 때문에 이미 존재하는 방대한 제니퍼 데이터를 다양한 방식으로 표현하기 위한 시도를 했었다.하지만 상용 솔루션인 제니퍼에 테스트 코드를 필자 임의로 추가해서 배포하거나 숨긴 기능으로 만들기에는 꽤 부담스러운 일이었다. 그렇다고 별도의 소스코드로 다르게 가지고 가기에는 관리 측면에서 어려움이 있다. 그렇기 때문에 기존의 제니퍼 소스코드를 참조만 하되 서로 독립적으로 개발하는 형태를 생각하게 되었다. 이렇게 필자의 편리함을 위해 시작한 실험실이지만 오픈소스나 다른 솔루션과의 연동을 위한 화면을 제공하고, 새로운 제니퍼 기능에 대한 비전을 시사하거나 고객의 피드백을 수용하는 용도로 확장되었다.소프트웨어 개발을 하다보면 제품이 추구하는 방향과 달라서, 또는 구현은 가능하지만 소모되는 리소스 비용이 부담이 될 경우, 그리고 특정 사용자를 위한 특화된 기능을 구현할 때, 모두가 만족할만한 기능이라는 확신이 없다면 제대로 진행하기가 어려운게 현실이다. 사실 새로 시도하는 기능은 시기와 때에 따라 앞에서 고려했던 것들과 다르게 평가되는 경우도 있다.그래서 아무리 작은 아이디어라도 시도를 해보는 것 자체만으로도 큰 의미가 있으며, 새로운 가능성을 발견하는 계기가 될 수 있다. 다만 현재는 제니퍼 기능 확장에 대한 기반 정도만 갖춰진 시작 단계라서 관련된 API 문서나 개발 도구에 대한 지원이 미흡한 것이 아쉬움으로 남는다. 다음 편에서는 자바 개발자 대상으로 실험실을 직접 구현하는 방법에 대해 알아볼 것이다.
조회수 2204

스포카 서버의 구조

안녕하세요. 스포카 개발팀에서 서버 관련 개발 업무를 담당하고 있는 문성원입니다. 오늘은 스포카 서버의 구조와 사용된 기술들에 대해서 함께 살펴보겠습니다.스택이란?먼저 스택(Stack)이란 용어에 대해서 함께 생각해보죠. 컴퓨터 과학을 공부하신 분들이라면 선입후출(FILO)이나 스택 오버플로우(Stack Overflow)등의 개념으로 익숙하실만한 용어기도 합니다. 그런데 서버 구조를 설명한다면서 왠 스택이냐구요? 다행히(?)도 지금부터 살펴 볼 스택은 솔루션 스택(Solution Stack)입니다. 스포카 서버라는 큰 솔루션이 원활히 동작하기 위해서 쓰이고 있는 각종 서브 시스템과 컴포넌트들의 묶음을 이야기하는 것으로 바꿔말하자면 이 글에서 다룰 기술 이야기는 모두 이 스택에 관한 이야기입니다.2011년 12월 현재 스포카 서버를 구성하고 있는 스택은 다음과 같습니다.DotcloudLinux 2.6.38.2nginx 0.8.53uwsgi 0.9.8.5Python 2.6.5Redis 2.2.2Celery 2.2.7Amazon Relational Database ServiceMySQL 5.5.12Amazon Simple Storage ServiceDotcloudDotcloud는 지금부터 설명드릴 스택을 묶어서 제공해주는 PaaS(Platform as a Service)의 일종입니다. Amazon Elastic Cloud Computing(Amazon EC2) 기반으로 동작하며 거기에 더해 손쉬운 확장과 배포가 장점입니다. 스포카 서버는 데이터베이스(Amazon RDS)와 업로드되는 데이터(Amazon S3) 이외의 모든 서비스를 Dotcloud를 통하여 제공하고 있습니다.nginx, uwsgi. 그리고 WSGI기본적으로 스포카 서버는 HTTP 형식의 요청을 받아 응답을 돌려주는 웹 어플리케이션입니다. 이러한 처리는 1차적으로 nginx를 통해 이뤄지는데, 이 중 서버사이드에서 처리가 필요한 경우에는 uwsgi라는 데몬이 이 처리를 담당합니다. (구버젼의 Apache Tomcat을 사용하시던 Java개발자분들은 Apache Tomcat과 Apache httpd와의 관계를 떠올리시면 편합니다.)이 경우 uwsgi는 일종의 어플리케이션 컨테이너(Application Container)로 동작하게 됩니다. 적재한 어플리케이션을 실행만 시켜주는 역할이죠. 이러한 uwsgi에 적재할 어플리케이션(스포카 서버)에는 일종의 규격이 존재하는데, 이걸 WSGI라고 합니다.(정확히는 WSGI에 의해 정의된 어플리케이션을 돌릴 수 있게 설계된 컨테이너가 uwsgi라고 봐야겠지만요.) WSGI는 Python표준(PEP-033)으로 HTTP를 통해 요청을 받아 응답하는 어플리케이션에 대한 명세로 이러한 명세를 만족시키는 클래스나 함수, (__call__을 통해 부를 수 있는)객체를 WSGI 어플리케이션이라고 합니다.정리하자면 스포카 서버는 WSGI에 맞게 작성된 프로그램을 nginx와 uwsgi를 통해 운용하여 요청을 처리하는 웹 어플리케이션이라고 할 수 있습니다.RedisRedis란 키-값(Key-Value) 저장 서버로 확장이 용이하며 속도가 우수합니다. 스포카 서버에선 이를 내부적인 임시 데이터 관리와 Celery의 작업(Task) 분배에 사용하고 있습니다.CeleryCelery는 Python으로 작성된 비동기 작업 큐(Asynchronous task queue/job queue)입니다. 앞서 소개한 작업(Task)를 브로커(Broker, 스포카 서버는 Redis를 사용)를 통해 전달하면 하나 이상의 워커(Worker)가 이를 처리하는 구조입니다. 포인트 적립-공유에 따른 분배처리, 포스팅 기능, 페이스북/트위터 공유등의 비동기 처리가 필요한 작업을 Celery에 위임하여 처리하고 있습니다.Amazon Relational Database Service대부분의 웹 어플리케이션과 마찬가지로 스포카 서버는 영속적으로 저장되어야하는 정보(회원 목록, 구매 내역)들을 디스크 기반의 데이터베이스(Database)에 저장합니다. Amazon Relational Database Service(Amazon RDS)는 Amazon EC2를 기반으로 그러한 데이터베이스를 간편하게 관리(모니터링, 백업, 접근제어)할 수 있게 도와주는 웹서비스입니다. Oracle과 MySQL을 지원하는데 스포카 서버는 그 중 MySQL을 사용하고 있습니다.Amazon Simple Storage ServiceAmazon Simple Storage Service(Amazon S3)는 Amazon RDS와 마찬가지로 Amazon EC2를 기반으로 한 데이터 저장 관리 서비스입니다. 스포카 서버에 업로드 되는 사진이나 문서등의 파일들을 통합하여 관리하여 서버의 인스턴스를 늘려 확장하는 경우에도 문제없이 대처할 수 있도록 하는 것이 주 목적입니다.#스포카 #스택 #개발 #개발자 #개발팀 #인사이트 #조언 #스킬스택 #스택설명
조회수 1400

A씨의 일주일

스포카 개발팀 문성원입니다. 오늘은 스포카 개발팀의 가공의 개발자 A씨의 일주일을 통해, 스포카 개발팀에서는 일주일간의 개발 일정을 어떻게 진행하는지 살펴보겠습니다. 평소 스타트업(Startup) 개발팀의 문화에 관심이 있으셨던 분들에게 도움이 되길 바랍니다.월요일오전 10시, A씨는 평소보다 조금 일찍 사무실에 도착했습니다. 매주 월요일은 스포카 전체 미팅이 있는 날이기 때문이죠. 한 주간 각자 진행한 것을 토대로 이번 주에 진행할 일을 대외적으로 공표하는 이 회의에 앞서, 스포카 개발팀은 따로 미팅을 잠깐 가집니다. 그동안 지난 주 개발 사항, 이번 주 구현 목록 등을 트렐로(Trello)를 통해 정리한 뒤, 이를 전체 미팅에서 공유합니다. A씨는 지난 주에 미쳐 다 구현하지 못했던 서버의 몇 가지 기능과 클라이언트 신버전 배포 준비를 하게 되었습니다.정신없이 회의 하고 났더니 벌써 점심시간입니다. 늘 가던 근처 식당에서 즐겁게 점심을 먹고 사무실로 올라온 A씨는 막간을 이용해 간밤에 올라온 스포카 개발 블로그의 원고를 검토합니다. 몇 가지 오탈자와 맞춤법을 지적한 뒤 모두가 지루해할 월요일 오후 1~2시경에 공개하는 것이 목표입니다.올라간 블로그 글을 확인한 뒤에, A씨는 구현해야 할 서버 기능을 살펴보기로 했습니다. 생각보단 많긴 하지만 일주일 안에는 어떻게든 끝낼 수도 있을 것 같은 분량이네요. 우선 트렐로에 올라온 카드의 명세를 토대로 작업해야 할 내용을 체크리스트(Check List)로 정리합니다. 그다음은 모두가 짐작하시듯이 열심히 일합니다. A씨는 프로니까요.어느덧 저녁 시간이 다 되었습니다. 특별히 일이 없는 이상 야근은 하지 않는 주의인 A씨지만, 오늘만큼은 저녁을 먹고 조금 더 남아있기로 합니다. 팀 내에서 진행하고 있는 스터디 때문이죠. 혼자서 읽기는 까다로웠던 책을 다 같이 읽어보니 조금은 이해가 더 되는 느낌이 드네요.화요일A씨는 오전에 작업하던 중 이상한 점을 발견합니다. 구현하기로 한 기능이 기존 기능과 모순이 되기 때문이죠. 이걸 어떻게 해결할까 고민하던 A씨는 다행히 사무실에 남아있던 엔에이블러(Enabler)팀원들과 간단하게 미팅을 합니다. 문제를 설명하고 명세를 다시 확인한 A씨는 작성한 회의록과 함께 배포합니다. 트렐로의 해당 카드에도 첨부하여 나중에 다시 볼 수 있게 하는 것은 기본입니다.뜻하지 않은 문제 때문에 오전을 날려서 기분이 나빠진 A씨지만, 다행히 좋아하는 스파게티를 먹고 기운을 내기로 했습니다. 사무실에 올라와 인터넷 뉴스와 페이스북을 잠시 보던 A씨는 암묵적으로만 정해진 점심시간이 끝나자 바로 작업을 시작합니다. A씨는 프로니까요.그런데 문제가 있습니다. 오전에 배포한 회의록을 읽어 본 다른 팀원들이 이건 다른 문제의 원인이 될 수 있다고 합니다. A씨는 새 기능 추가가 단순히 로직이 아니라 클라이언트 UI를 포함한 대규모 변경이 필요하다는 것을 깨닫습니다. A씨는 새 기능에 대한 대략적인 스케치를 발사믹 목업(Balsamiq Mockup)으로 마친 뒤 이를 다시 배포합니다. 또한, 관련된 카드에 설명도 잊지 않습니다.수요일매주 수요일 오전에 스포카 개발팀은 짧은 미팅을 합니다. 금주의 진행사항 중 변경사항이나 도움이 필요한 내용을 공유하는 자리인데요. 여기서 A씨는 어제 일을 다시 정리해서 이야기하고, 일정이 지연될 수 있음을 전달합니다. A씨에게 할당된 카드 일부를 다음 주로 미루거나, 좀 한가한 사람에게 나눠주는 형식으로 짐을 던 A씨였지만, 여전히 큰일이 되어버린 기능 변경은 무거운 짐입니다.이런 대량의 작업 때문에 고민하던 A씨에게 같은 팀 B씨가 어떤 라이브러리를 소개해줍니다. A씨는 처음 보는 라이브러리인지라 B씨가 전담해서 가르쳐주는 모양이 되었지만, 생각보다 문제 해결에 큰 도움이 될 것 같습니다. 마침 다음 주에 개발 블로그에 글을 써야 할 당번이 된 A씨는 그 라이브러리에 대해 좀 더 공부해서 쓰기로 정합니다.B씨의 도움 덕에 진행 속도가 붙은 A씨는, 금주 업무 중 하나였던 클라이언트 새 버전의 테스트를 내일부터 진행하기 위해 페이스북이나 인터넷 뉴스도 보지 않은 채 열심히 일합니다. 이런 A씨의 프로다운 모습에 하늘도 감탄했는지, 퇴근 시간인 7시 전에 작업을 끝낸 A씨는 구현된 기능들을 테스트 해 보고 팀의 다른 개발자와 공유하기 위해 github에 만들어진 스포카 서버 코드 저장소에 푸시(Push)합니다.목요일구글 플레이(Google Play)는 하루 정도면 배포가 되니 다행이지만 애플 앱스토어(Apple App Store)는 일주일 정도의 심사기간이 있기 때문에 거절(Reject)당하지 않게 철저히 준비해야 합니다. 그래서 어느 때보다 A씨는 날카로운 눈매로 클라이언트를 점검합니다. 아니나 다를까 메뉴를 이동하다 보니 화면 구성이 흐트러지는 버그가 발견되었습니다. 하지만 프로답게 A씨는 당황하지 않고 재현 조건을 확인한 뒤, 클라이언트 담당자인 C씨에게 알려줍니다. “클라이언트 관련한 버그를 찾았는데, 트렐로를 확인해보세요.”라구요. QA(Quality Assurance) 업무 역시 스포카 개발팀은 직접 처리합니다.밖에 비가 오는지라 피자를 시켜먹은 뒤, 자리에 앉아 잠깐 쉬고 있던 A씨에게 D씨가 다가와서는, “어제 푸시한 소스를 내려받다(Pull)가 충돌(Conflict)이 났는데, 어떻게 병합(Merge)해야 할지 모르겠네요.” 라며 묻습니다. A씨는 D씨와 충돌이 난 소스를 함께 검토하고 문제가 발생하지 않게끔 조정한 뒤 이를 다시 푸시해서 상황을 종료합니다.이러는 사이 C씨가 A씨가 말한 버그를 고쳤다며 다시 확인해보라고 트렐로의 관련 카드를 “테스트” 리스트로 옮깁니다. A씨는 재현된 상황에서 문제 없이 동작하는 것을 확인하고 카드를 “완료” 리스트로 다시 옮깁니다. 이제 클라이언트 앱을 심의 신청하고 어제 구현한 서버 쪽 코드의 개선사항이 있는지 살펴봅니다. 서버는 클라이언트가 앱스토어나 플레이에 준비되는 것을 확인한 뒤, DotCloud에서 제공하는 배포 스크립트를 통해 손쉽게 버전업할 수 있기 때문에 시간이 좀 남아 있습니다. 현재로선 특별히 더 손댈 부분이 없다는 걸 확인한 A씨는 오늘도 즐겁게 퇴근합니다.금요일월요일과 마찬가지로 오늘도 A씨는 평소보다 조금 서둘러서 사무실에 도착했습니다. 오늘은 사내 전체적으로 한 주간 있었던 업무 내용을 간략하게 보고하는 자리가 있습니다. A씨는 이번 주에 맡은 서버 개발이 이러저러해서 이렇고 저렇게 바뀌었다고 설명한 뒤, 앱스토어에 신청되었다는 사실을 공지합니다. 전체 보고가 끝난 뒤엔 개발팀은 따로 남아서 약간 자세하게 금주 작업을 공유하면서 트렐로의 “완료” 상태에 있는 카드들을 정리하는 시간을 갖습니다.점심을 먹고 나서, 이번 주에 더는 급한 일정이 없다는 것을 확인한 A씨는 개발 블로그에 쓸 글을 정리하기 시작합니다. 수요일에 B씨가 알려 준 라이브러리의 사용 방법은 대강 배웠지만, 그것을 남에게 설명할 수 있을 만큼 자세히 알지는 못했기 때문에 A씨는 한동안 공식 문서와 예제 코드들과 씨름합니다. 그래도 어느새 옆에서 거들기 시작한 B씨 덕에 글은 생각보다 순조롭게 마무리되었습니다. 이제 다음 주 월요일까지 퇴고해서 블로그에 공개하기만 하면 되죠.생각보다 오늘 업무를 끝낸 A씨는 친구들과 약속이 있는 홍대로 가기 위해, 7시 정시에 사무실을 떠납니다.#스포카 #개발 #개발자 #개발팀 #개발자의일주일 #개발자의일상 #인사이트 #경험공유
조회수 1584

Humans of TODAIT : 안드로이드 천재 개발자 김범준을 만나다

‘Humans of TODAIT’의 네번째 주인공, 투데잇 안드로이드 개발자 김범준씨를 만나보았습니다. 투데잇의 천재 개발자로 불리는 그의 이야기를 함께 들어볼까요?(2017.08)Q. 자기소개 부탁드려요.안녕하세요! 투데잇에서 까칠남을 맡고 있는 안드로이드 개발자 김범준입니다. 퇴사자 인터뷰를 하게 되니, 정들었던 팀원분들과 헤어질 생각에 아쉽고 싱숭생숭하네요. (웃음) 작년 초 쯤 ‘SW 마에스트로’ 프로그램에서 만난 멘토님께서 제게 투데잇 안드로이드 개발자 자리를 추천해주신 덕분에 이렇게 투데잇과 인연이 닿게 되었어요. 사실 처음에는 큰 생각이 없었는데, 대표님과 팀장님을 만나보니 저와 코드도 잘 맞고 개발 쪽으로도 많이 배워볼 수 있을 것 같아서 그 날 바로 입사 결정을 내렸고, 지금은 퇴사를 앞두고 있네요.Q. 그렇게 좋은 투데잇을 떠나는 이유는 무엇인가요?원래 병특을 가야 했어요. 제가 군대를 아직 안 갔기 때문에, 군대 문제를 해결 해야 더 많은 기회도 생기고 지금 가지고 있는 마음의 짐 같은 것도 덜 수 있거든요. 아쉽게도 투데잇이 병특 산업기능요원지정업체가 아니어서 군대 문제를 해결하기 위해서는 퇴사할 수 밖에 없는 상황이에요. 사실 원래부터 군대 문제 때문에 잠시 동안만 일하기로 했던건데, 회사생활이 너무 만족스럽고 일이 즐거워서 계속 미루다가 이제서야 결정을 내렸네요. 지금도 많이 아쉬워요. 투데잇만한 회사 없거든요.Q. 팀 내에서 평소 자기계발을 많이 하는 것으로 유명한데, 혹시 자기계발 노하우가 있나요?사실 공부는 진짜 하는 것보다 시작하는 것이 어렵잖아요. 그래서 저는 일부러 저한테 강제성을 주는 편이에요. 매주 하는 동아리 활동이라든지 발표 기회를 만든다든지 관련 세미나를 참여한다든지 그런 일정이 생기면 자연스럽게 하게 되더라고요. 하면 또 잘하고 싶은 게 사람 마음이니까 자꾸 강제적으로 그런 기회를 만들죠.그리고 저는 일상에서 배울 수 있는 기회를 얻으려고 해요. 일하다가 힘들거나 머리가 잘 안 돌아갈 때 저장해둔 아티클을 보곤 하죠. 또 술마실 때도 같은 직업군의 친구들을 만나면 그런 얘기를 많이 하잖아요. 너 이거 시도해봤냐 어땠냐 이건 어떻게 하는거냐 같은 이야기요. 제가 주위 사람들에게 자극을 많이 받거든요. 책상 앞에 앉아서 하는 공부보다는 일상적 시간을 활용하고 뭔가를 준비하기 위한 공부의 자기계발을 하는 것 같아요.Q. 지난 1년을 돌아보는 의미에서, 개발자로서의 좌우명이나 철학이 있을까요?저는 어떤 일을 하든 명확한 근거가 있어야 한다고 생각해요. 커뮤니케이션에서도 그렇고 개발에 있어도 마찬가지예요. 내가 하는 일에 대한 충분한 이유가 있어야 하고 그게 코드에 녹아 있어야 해요.예를 들면, 같은 풍경을 보고 글을 쓸 때도 여러 방법이 있잖아요. 사람마다 글 쓰는 방법이 다르고. 그 방법을 선택한 데엔 저마다 이유가 있어요. 코드도 마찬가지예요. 어떤 기능을 개발할 때 그 기능을 구현할 수 있는 여러 방법이 있는데, 개발자라면 내가 만든 코드에 대해 내가 왜 이렇게 짰는지 다른 사람에게 자신 있게 말할 수 있는 개발자가 되어야 한다고 생각해요.저는 힙한 개발자가 되고 싶어요. 그러니까 최신 트렌드에 민감하고, 새로운 것에 도전하고 두려워 하지 않는 그런 개발자요. (웃음)Q. 힙한 개발자 멋지네요. 그렇다면 10년 후에는 무엇을 하고 싶은지 궁금한데요?제 꿈은 그냥 행복하게 사는거예요. (하하) 추상적인 이야기 같겠지만, 행복하게 살기 위해선 많은 것들이 필요하잖아요? 우리가 말하는 이상적인 행복이란 것은 돈, 인간관계, 사회적 직위, 건강과 같은 모든 박자가 잘 맞아 떨어졌을 때 이루어지는 행복이거든요. 그래서 저는 행복하기 위해서는 끊임없이 노력해야 한다고 생각해요. 장차 10년 후에 제가 뭘 하고 있을지는 모르지만, 지금 현재의 상황에서 제가 할 수 있는 최선의 선택을 하면서 열심히 단계적으로 이루어나가면, 10년 후에도 충분히 행복할 것 같아요. 저는 지금 행복하거든요. (웃음)Q. 일하다 보면 해결하기 힘든 난제를 만날 때가 있을 것 같은데, 그럴 땐 어떻게 극복하나요?내가 스트레스를 많이 받고 있다는 걸 깨달으면, 그냥 최대한 스트레스 받지 않으려고 해요. 그냥 뭐 하면 되지 라는 생각이죠. 하면 되지 하면서 하다보면 결국 되는 것 같아요. 어차피 해야 될 일인데, 스트레스 받으면서 하기 보다는 그냥 아무 생각 없이 열심히 하는 게 나으니까요. 만약에 제가 몰라서 못하고 있는 일이면 여러 사람들에게 물어보려고 하면서 어떻게든 해결하려고 하고요.Q. 그렇다면 투데잇에서 가장 만족스러운 결과물은 무엇인가요? 개인적으로 뿌듯하다거나 실제 반응이 좋았다거나 그런 것들이요!‘스탑워치’ 기능이 두 개 다 포함돼요. 이전 개발자가 스파게티 코드(엉망진창의 코드)로 만들어 놓았던 것이 있는데 그 코드를 제가 깔끔하게 다 수정했고, 계속 유저분들이 요청해주셨던 시간 잠금, 극강의 잠금 모드 같은 기능들을 추가해서 코드를 예쁘게 잘 만들어놓았거든요. 일단 제가 기발한 기능과 함께 코드를 예쁘게 잘 만들어냈다는 점에서 스스로도 만족을 했었고, 유저분들도 팀원분들도 좋은 피드백을 해주셔서 굉장히 좋았습니다.Q. 지금 이 글을 보고 계시는 스탑워치 기능 애용 유저분들께 한마디 해주세요!우선 잘 사용해주셔서 감사해요! 제가 만든 기능을 이용해 공부하시는 걸 보면, 저도 정말 큰 자부심을 느끼거든요. :) 다만, 아직 스탑워치 기능에 문제가 조금 있는 거로 알고 있어요. 약간 불편하더라도 이왕이면 둥글게 좋게 별 5점으로 리뷰 주시면! 저희와 의사소통하면서 함께 좋은 서비스 만들어 나갈 수 있을 것 같아요. 안 보는 것 같지만 투데잇 개발자 전체가 매일 열심히 읽고 있거든요. 정말 리뷰 하나에 울고 리뷰 하나에 웃습니다. 저희 투데잇 지금까지 사랑해주셨지만, 앞으로도 계속 사랑해주시면 감사하겠습니다. :)Q. 반대로 투데잇 안드로이드 개발에 있어 아쉬운 부분도 있을 것 같아요. 나 이거 진짜 욕심났다! 혹시 있을까요?음.. 저는 옛날에 있던 아키텍처를 일단 전부 바꾸고 싶어요. 최근에 꽂힌 아키텍쳐가 있는데, 그 아키텍쳐에 맞게 코드를 다 변경해보고 싶다는 욕심이 있거든요. 근데 그 아키텍쳐 특성상 현재 코드에서는 완전히 대대적인 수정이 들어가야되는데, 제가 남은 시간이 얼마 없어서 많이 수정을 못했죠. 우리가 좀 더 많은 시간이 있고 여유가 있었더라면 더 바꿔볼 수 있었을텐데 그런 부분들을 못한 게 조금 아쉬워요.“투데잇의 힘은 서로에 대한 믿음인 것 같아요”Q. 범준님에게 투데잇이란? 투데잇 팀의 힘이 무엇이라고 생각하시나요?무엇보다 투데잇의 힘은 서로에 대한 믿음인 것 같아요. 커뮤니케이션이 잘 되려면 그 사람에 대한 믿음이 있어야 되잖아요. 근데 저흰 그게 되게 잘 되고 있다고 생각되거든요. 업무적으로 제 이야기를 자신있게 할 수 있었던 이유도 이 사람들은 전부 다 각자 일을 열심히 하고 책임을 지려는 사람, 멋있는 사람이라는 걸 알고 있었기 때문에 가능했거든요. 다들 맡은 바에 있어서 최선을 다하고 정말 열심히해요. 그 분위기가 서로에 대한 믿음을 만들고 우리의 원동력을 만들죠. 확실히 저희 팀은 일단은 진짜 서로에 대한 믿음이 강하다? 업무적 믿음이 강하다? 그런 게 있는 것 같아요.Q. 투데잇에서 가장 고마웠던 사람은 누구였나요?솔직히 다 고마운데, 저는 대표님께 가장 감사했어요. 이번에도 혼자 고민하다가 힘들게 퇴사 의사를 밝혔는데, 대표님께서 그건 당연한 거라고 이야기해주시더라고요. 저는 투데잇 팀이 참 좋은 게 어떤 이야기를 했을 때 명확한 근거가 있다면 그 후에 뒤끝이 하나도 없어요. 이번 일도 그렇고 일적으로 이야기 할 때도 그렇고, 이유가 확실하면 OK하고 쿨하게 가곤 하셨거든요. 다 업무적 믿음이 있기 때문이라고 생각해요 저는. 여러모로 저를 많이 믿어주신 대표님한테 제일 감사하죠. 대표님 에너지도 너무 좋고 카리스마도 본받고 싶고 제가 되게 좋아하는 분이에요.Q. 범준님의 다음 타자가 될! 투데잇에 입사하고 싶은, 입사할 분들에게 한 마디 부탁드려요!“팀원 하나하나가 굉장히 중요한 역할을 하고 있는 사람들이어서 그만큼 책임감이 있지만, 그만큼의 자율성도 있는 회사에요”굉장히 좋은 팀이에요. 일적에서는 절대 스트레스 주는 일이 없고요. 뭔가 일이 밀리거나 못하는 거에 있어서는 스트레스가 있을 수도 있어요. 팀원 하나하나가 굉장히 중요한 역할을 하고 있는 사람들이어서 그만큼 책임감이 있지만, 그만큼의 자율성도 있는 회사에요. 노력하는 그대로의 모습을 사람들에게 보여줄 수 있고 인정 받을 수 있기 때문에 흔히 말하는 꼰대 문화가 싫으신 분들은 투데잇에서 행복하게 일할 수 있을 거예요. 업무적으로나 환경적으로나 대우도 근무 환경도 굉장히 좋으니까 관심 있으신 분이면, 특히 안드로이드 개발자 분이면 지금 바로 들어오실 수 있을 것 같아요. 유저한테 피드백도 받을 수 있고 개인적으로 리스펙하는 멋진 CTO분도 계시고, 개발자로서 특히 굉장히 좋은 곳입니다. 주저 마세요!#투데잇 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #조직문화
조회수 4438

Kubernetes을 활용한 분산 부하 테스팅

Kubernetes을 활용한 분산 부하 테스팅동명의 글이 Google Cloud Platform에도 있으니 여기서는 여태까지 한 삽질과 교훈에 집중한다.첫 시도 ngrinder처음에는 ngrinder로 부하 테스트 환경을 구축하려 했다. 몇 달 전에 부하 테스트를 진행할 때 잠시 쓴 적이 있었기 때문에 굳이 다른 솔루션을 찾을 이유가 없었다. 하지만 결국 후회하고 다른 솔루션으로 넘어갔는데 그 이유를 중요한 순으로 꼽자면로컬 개발환경과 실제 환경이 차이가 많다. 로컬에서는 JUnit 기반으로 개발과 디버깅이 가능하다. 하지만 이렇게 작성한 코드를 ngrinder에 넣으려 하면 외부 라이브러리가 문제가 된다. .jar 등 패키지 파일을 업로드하는 방식이 아니라 Groovy 스크립트 따로 스크립트에서 사용하는 라이브러리 따로 업로드를 해야 하는데 상당히 번거롭다.웹 UI를 통해 설정한 내용이 내장 데이터베이스에 바이너리로 들어가기 때문에 ngrinder 데이터를 관리하기가 힘들다.개발이 활발하지 않다. 주력 개발자가 Naver를 떠났다는 이야기도 있긴 한데 아무튼 커밋 히스토리를 보면 개발이 정체되어 있는 건 분명하다.설계가 진보적이지 않다. 예를 들어 현재 쓰레드의 ID를 시스템이 직접 계산해서 주입하지 않고 개발자가 주어진 코드 스니펫을 Copy & Paste 해야 하는 이유를 모르겠다.등이 있다. 이런 까닭에 좀더 간단한 솔루션을 찾아보았다.대안몇 가지 대안을 살펴보았는데Artillery는 테스트 스크립트를 yaml로 기술하기 때문에 얼핏 쉬워보이지만 이런 식의 접근 방법은 매번 실망만 안겨주었다. 조금만 테스트 시나리오가 복잡해지면 일반적인 코딩보다 설정 파일이 훨씬 짜기가 어렵고 이해하기도 어렵다.config: target: 'https://my.app.dev' phases: - duration: 60 arrivalRate: 20 defaults: headers: x-my-service-auth: '987401838271002188298567' scenarios: - flow: - get: url: "/api/resource"Gatling은 아직 분산 서비스를 지원하지 않아서 제외했다. 팀 내에 Scala 개발경험이 있는 사람이 극소수인 점도 문제였다.Locust로 정착이런 까닭에 Locust로 넘어왔다. 장점은파이썬 스크립트로 시나리오를 작성하니 내부에 개발인력이 충분하다.로컬환경과 실제 부하테스팅 환경이 동일하다. 즉, 디버깅하기 쉽다.Locust 데이터를 Dockerize하기 쉽다.한마디로 ngrinder에서 아쉬웠던 점이 모두 해결됐다. 반면 ngrinder에 비해 못한 면도 많긴 하다.통계가 세밀하지 않다.테스트 시나리오를 세밀하게 조정하기 힘들다.현재로썬 그때그때 가볍게 시나리오를 작성해서 가볍게 돌려보는 게 중요하지 세밀함은 그리 중요하지 않아서 Locust가 더 나아 보인다. 시나리오는 몰라도 통계의 경우, DataDog 같은 모니터링 시스템에서 추가로 정보를 제공받기 때문에 큰 문제도 아니긴 하다.결과물Locust on KubernetesGoogleCloudPlatform/distributed-load-testing-using-kubernetes에 있는 소소코드를 참고로 작업하면 된다. 단지 Dockerfile의 경우, 테스트 스크립트만 바뀌고 파이썬 패키지는 변경사항이 없는 경우에도 파이썬 스크립트 전체를 새로 빌드하는 문제가 있다.# Add the external tasks directory into /tasks ADD locust-tasks /locust-tasks# Install the required dependencies via pip RUN pip install -r /locust-tasks/requirements.txt 그러므로 이 부분을 살짝 고쳐주면 좋다.ADD locust-tasks/requirements.txt /locust-tasks/requirements.txtRUN pip install -r /locust-tasks/requirements.txtADD locust-tasks /locust-tasksngrinder on Kubernetesngrinder를 Kubernetes v1.4.0 위에서 돌리는데 사용한 설정은 다음과 같다. 참고로 dailyhotel/ngrinder-data는 ngrinder의 데이터만 따로 뽑아서 관리하는 도커 이미지이다.ControllerapiVersion: v1 kind: Service metadata:  name: ngrinder  labels:  app: ngrinder  tier: middle  dns: route53  annotations:  domainName: “ngrinder.test.com” spec:  ports:  # the port that this service should serve on  — name: port80  port: 80  targetPort: 80  protocol: TCP  — name: port16001  port: 16001  targetPort: 16001  protocol: TCP  — name: port12000  port: 12000  targetPort: 12000  protocol: TCP  — name: port12001  port: 12001  targetPort: 12001  protocol: TCP  — name: port12002  port: 12002  targetPort: 12002  protocol: TCP  — name: port12003  port: 12003  targetPort: 12003  protocol: TCP  — name: port12004  port: 12004  targetPort: 12004  protocol: TCP  — name: port12005  port: 12005  targetPort: 12005  protocol: TCP  — name: port12006  port: 12006  targetPort: 12006  protocol: TCP  — name: port12007  port: 12007  targetPort: 12007  protocol: TCP  — name: port12008  port: 12008  targetPort: 12008  protocol: TCP  — name: port12009  port: 12009  targetPort: 12009  protocol: TCP  selector:  app: ngrinder  tier: middle  type: LoadBalancer  — - apiVersion: extensions/v1beta1 kind: Deployment metadata:  name: ngrinder spec:  replicas: 1  template:  metadata:  labels:  app: ngrinder  tier: middle  spec:  containers:  — name: ngrinder-data  image: dailyhotel/ngrinder-data:latest  imagePullPolicy: Always  volumeMounts:  — mountPath: /opt/ngrinder-controller  name: ngrinder-data-volume  — name: ngrinder  image: ngrinder/controller:latest  resources:  requests:  cpu: 800m  ports:  — containerPort: 80  — containerPort: 16001  — containerPort: 12000  — containerPort: 12001  — containerPort: 12002  — containerPort: 12003  — containerPort: 12004  — containerPort: 12005  — containerPort: 12006  — containerPort: 12007  — containerPort: 12008  — containerPort: 12009  volumeMounts:  — mountPath: /opt/ngrinder-controller  name: ngrinder-data-volume  volumes:  — name: ngrinder-data-volume  emptyDir: {}AgentsapiVersion: extensions/v1beta1 kind: Deployment metadata:  name: ngrinder-agent spec:  replicas: 5  template:  metadata:  labels:  app: ngrinder-agent  tier: middle  spec:  containers:  — name: ngrinder-agent  image: ngrinder/agent:latest  imagePullPolicy: Always  resources:  requests:  cpu: 300m  args: [“ngrinder.test.com:80”]구 블로그 시절의 댓글#데일리 #데일리호텔 #개발 #개발자 #개발팀 #기술스택 #도입후기 #일지 #Kubernetes #인사이트
조회수 2219

비트윈의 멀티티어 아키텍처를 위한 프레젠터 이야기 - VCNC Engineering Blog

블로그 첫 글에서 비트윈의 시스템 아키텍처에 대해 다룬 적이 있습니다. 시스템 구성의 미래에 대한 계획으로 멀티티어 아키텍처에 대해 언급했었는데, 이는 프로토콜을 단순화시키고 배포 자동화를 가능하게 하기 위해서 클라이언트와 비즈니스 로직을 담당하는 서버 사이에 일종의 게이트웨이를 두는 것이었습니다. 그 외에도 여러 가지 필요성이 생겨 해당 역할을 담당하는 프레젠터라는 것을 만들게 되었고 비트윈의 채팅 시스템에 적용하게 되었습니다. 만드는 과정 중에 여러 기술적인 문제들이 있었고 이를 해결하기 위한 노력을 하였습니다. 이 글에서는 비트윈 시스템에서의 프레젠터에 대해 이야기 하고자 합니다.프레젠터프레젠터는 일종의 게이트웨이 입니다. 기존의 시스템에서는 클라이언트들이 ELB를 통해 채팅 서버에 직접 TCP 연결을 하였습니다. 하지만 비트윈 PC버전과 자체 푸시 서버를 만들면서 ELB로는 해결할 수 없는 부족한 점들이 생겼고, ELB의 부족한 점을 채워줄 수 있는 시스템이 필요하게 되었습니다. ELB를 대체하는 역할 외에도 다른 여러 필요했던 기능들을 제공하는 프레젠터를 만들기로 하였습니다.프레젠터는 ELB의 역할을 할 뿐만 아니라 여러 다른 기능들도 제공합니다.프레젠터의 기능패킷을 적절한 샤드로 중계비트윈에서는 커플 단위로 샤딩하여 같은 커플의 채팅 요청에 대해서는 같은 채팅 서버에서 처리하고 있습니다. Consistent Hash를 통해 커플을 여러 채팅 서버로 샤딩하고 ZooKeeper를 이용하여 이 정보를 여러 채팅 서버 간 공유합니다. 프레젠터 또한 ZooKeeper와 연결을 하여 어떤 채팅 서버가 어떤 커플을 담당하는지에 대한 정보를 알고 있도록 설계되어 있습니다. 따라서 프레젠터는 첫 연결 시 보내는 인증 패킷을 보고 해당 채팅 연결에서 오는 요청들을 어떤 채팅 서버로 보내야 할지 판단할 수 있습니다. 어떤 채팅 서버로 보낼지 판단하는 과정은 처음 한 번만 일어나며, 이후 패킷부터는 자동으로 해당 채팅 서버로 중계합니다.프레젠터의 이런 기능 덕분에 클라이언트는 더 이상 어떤 채팅 서버로 붙어야 하는지 알아내는 과정 없이 아무 프레젠터와 연결만 맺으면 채팅을 할 수 있게 되었습니다. 기존에는 클라이언트들이 여러 채팅 서버 중 어떤 서버에 붙어야 하는지 확인하는 작업을 한 후에 할당된 채팅 서버로 연결 맺어야 했습니다. 그래서 클라이언트가 채팅 서버와 연결을 맺기 위해 다소 복잡한 과정을 거쳐야 했지만, 이제는 클라이언트가 프레젠터의 주소로 연결 요청만 하면 DNS Round Robin 통해 아무 프레젠터와 연결하는 방식으로 프로토콜을 단순화할 수 있었습니다. 덕분에 새로운 채팅 서버를 띄울 때마다 ELB를 Warm-Up 시켜야 했던 기존 시스템의 문제가 없어졌습니다. 그래서 비트윈 개발팀의 오랜 염원이었던 채팅 서버 오토스케일의 가능성도 열렸습니다.많은 수의 연결을 안정적으로 유지PC버전과 푸시 서버를 만들면서 기존의 채팅 연결과 다르게 많은 수의 연결이 장시간 동안 유지 되는 경우를 처리할 수 있어야 했습니다. 기존에는 TCP 릴레이를 하도록 설정된 ELB가 연결들을 받아주었습니다. 한 머신당 6만 개 정도의 Outbound TCP 연결을 맺을 수 있는데, ELB도 트래픽에 따라 여러 대의 머신에서 돌아가는 일종의 프로그램이므로 이 제한에 걸린다고 생각할 수 있습니다. 따라서 많은 수의 연결을 맺어놓고 있어야 하는 경우 ELB에 문제가 생길 수 있다고 판단했습니다. (과거 ELB가 연결 개수가 많아지는 경우 스케일아웃이 안되는 버그 때문에 문제가 된 적이 있기도 했습니다) 또한 클라이어트 연결당 내부 연결도 하나씩 생겨야 하면 클라이언트가 연결을 끊거나 맺을 때마다 서버 내부 연결도 매번 끊거나 연결해야 하는 오버헤드가 발생합니다.이를 해결하기 위해 프레젠터에서는 TCP 연결을 Multiplexing하는 프로토콜을 구현하여 적은 수의 내부 연결로 많은 수의 클라이언트 연결을 처리할 수 있도록 하였습니다. 서버 내부에서는 고정된 개수의 몇 개의 연결만 맺어 놓고 이 연결들만으로 수많은 클라이언트 연결을 처리할 수 있습니다. 이처럼 TCP Multiplexing을 하는 것은 Finagle과 같은 다른 RPC 프로젝트에서도 지원하는 기능입니다.TCP Multiplexing 프로토콜을 통해 많은 수의 클라이언트 연결을 소수의 서버 내부 연결로 처리합니다.또한, 프레젠터는 많은 수의 SSL 연결을 처리해야 하므로 암복호화 로직을 실행하는데 퍼포먼스가 매우 중요하게 됩니다. 채팅 서버 한 대를 제거하거나 하는 경우 많은 연결이 한꺼번에 끊어지고 연이어 한꺼번에 연결을 시도하게 되는 경우가 있을 수 있는데, 이 때 대량의 SSL Handshaking을 하게 됩니다. 기존 서버들로 대량의 SSL Handshaking을 빠른 시간안에 처리하기 위해서는 높은 퍼포먼스가 필요합니다. Java로 작성된 프로그램만으로 이런 퍼포먼스 요구사항을 달성하기 어려우므로, 클라이언트와의 연결을 담당하는 부분은 OpenSSL, libevent를 이용한 C++로 코드로 작성하였습니다. 인증 패킷을 파싱하거나 패킷들을 릴레이 하는 등의 로직을 담당하는 부분은 Alfred라는 Netty를 이용하여 만든 인하우스 RPC 라이브러리를 이용해 작성되었습니다. 연결을 담당하는 부분은 TCP 연결을 유지하는 역할과 들어온 패킷들을 Netty로 작성된 모듈로 릴레이 하는 역할만 담당하므로 매우 간단한 형태의 프로그램입니다. 짧은 시간 안에 어럽지 않게 구현할 수 있었습니다.클라이언트의 연결을 받아주는 역할을 하는 부분은 C++, 실제 로직이 필요한 부분은 Java로 작성하였습니다.여러 네트워크 최적화 기술의 지원ELB에는 여러 네트워크 최적화 기술들을 아직 제공하지 않는 경우가 있습니다. 대표적으로 HTTP/2 혹은 SPDY, QUIC, TCP Fast Open 등이 있습니다. 특히 모바일 환경에서는 SSL Handshaking 등 부가적인 RTT로 인한 지연을 무시할 수 없으므로 이런 기술들을 이용한 초기 연결 시간 최적화는 서비스 퀄리티에 중요한 부분 중 하나입니다. ELB는 AWS에서 관리하는 서비스이므로 AWS에서 이런 기능들을 ELB에 적용하기 전에는 이용할 수 없지만, 프레젠터는 직접 운영하는 서버이므로 필요한 기능을 바로바로 적용하여 서비스 품질을 높일 수 있습니다. ELB에서 이미 제공하는 최적화 기술인 SSL Session Ticket이나 다른 몇몇 기술은 이미 적용되어 있고 아직 적용하지 않은 기술들도 필요에 따라 차차 적용할 예정입니다.프레젠터의 구현C++ 연결 유지 모듈프레젠터는 퍼포먼스를 위해 C++로 작성되었습니다. 이는 Pure Java를 이용한 암복호화는 프레젠터에서 원하는 정도의 퍼포먼스를 낼 수 없기 때문입니다. 처음에는 OpenSSL과 libevent를 이용해 작성된 코드를 JNI를 통해 Netty 인터페이스에 붙인 event4j라는 인하우스 라이브러리를 이용하려고 했으나, 코드가 복잡하고 유지보수가 어렵다는 점 때문에 포기하였습니다. 그 후에는 netty-tcnative를 이용해보고자 했으나 테스트 결과 연결당 메모리 사용량이 큰 문제가 있었고, 이를 수정하기에는 시간이 오래 걸릴 것 같아 포기하였습니다. 결국, 페이스북에서 오픈소스로 공개한 C++ 라이브러리인 folly를 활용하여 프레젠터를 작성하게 되었습니다. folly의 네트워크 API들이 OpenSSL과 libevent를 이용해 구현되어 있습니다.릴레이 로직프레젠터는 첫 인증 패킷을 파싱하여 릴레이할 채팅 서버를 판단하며, 이후의 패킷부터는 실제 패킷을 까보지 않고 단순 릴레이 하도록 설계하였습니다. 처음의 Netty 파이프라인에는 Alfred 프로토콜을 처리할 수 있는 핸들러들이 설정되어 있어 인증 패킷을 파싱 할 수 있으며 인증 패킷에 있는 정보를 바탕으로 어떤 채팅 서버로 패킷을 릴레이 할지 결정합니다. 그 이후 파이프라인에 있던 핸들러를 모두 제거 한 후, 읽은 byte 스트림을 Multiplexing Protocol 프레임으로 감싸서 그대로 릴레이 하는 매우 간단한 로직을 담당하는 핸들러 하나를 추가합니다. 덕분에 로직 부분의 구현도 매우 간단해질 수 있었으며, 채팅 서버에 API가 추가되거나 변경되어도 프레젠터는 업데이트할 필요가 없다는 운영상 이점도 있었습니다.Multiplexing Protocol프레젠터의 Multiplexing Protocol은 Thrift를 이용하여 직접 정의 하였으며, 비트윈 개발팀 내부적으로 사용 중인 RPC 라이브러리인 Alfred에 이 프로토콜을 구현하였습니다. Thrift를 통해 C++과 Java로 컴파일된 소스코드를 각각 프레젠터의 연결 처리 부분과 로직 처리 부분에서 이용하여 통신합니다. 프레젠터에서는 Multiplexing된 TCP 연결들을 Stream이라고 명명하였으며 이는 SPDY나 HTTP/2에서의 호칭 방법과 유사합니다. SPDY나 HTTP/2도 일종의 Multiplexing 기능을 제공하고 있으며, 프레젠터의 Multiplexing Protocol도 SPDY 프레임을 많이 참고하여 작성되었습니다.수 많은 클라이언트와의 TCP연결을 Stream으로 만들어 하나의 내부 TCP연결을 통해 처리합니다.Alfred에서는 Multiplexing 된 TCP 연결을 Netty의 Channel 인터페이스로 추상화하였습니다. Netty에서 TCP 연결 하나는 Channel 하나로 만들어지는데, 실제 Stream도 Channel 인터페이스로 데이터를 읽거나 쓸 수 있도록 하였습니다. 이 추상화 덕분에 비트윈 비즈니스 로직을 담당하는 코드에서는 Stream으로 Multiplexing 된 TCP 연결을 마치 기존의 TCP 연결과 똑같이 Channel을 이용해 사용할 수 있었습니다. 그래서 실제 비즈니스 로직 코드는 전혀 건드리지 않고 프레젠터를 쉽게 붙일 수 있었습니다.로드 밸런싱클라이언트는 Route53에서 제공하는 DNS Round Robin 기능을 이용하여 아무 프레젠터에 연결하여 채팅 요청을 날리게 됩니다. 하지만 무조건 동등하게 Round Robin 하게 되면 새로 켜지거나 하여 연결을 거의 맺지 않고 놀고 있는 프레젠터가 있는데도 연결을 많이 맺고 있는 기존 프레젠터에에 연결이 할당되는 문제가 생길 수 있습니다. 충분한 시간이 흐르면 결국에는 연결 개수는 동등하게 되겠지만, 처음부터 놀고 있는 프레젠터에 새로운 연결을 가중치를 주어 할당하면 로드를 분산되는 데 큰 도움이 될 것입니다. 그래서 Route53의 Weighted Routing Policy 기능을 이용하기로 하였습니다. 현재 연결 개수와 CPU 사용량 등을 종합적으로 고려하여 Weight를 결정하고 이를 주기적으로 Route53의 레코드에 업데이트합니다. 이런 방법으로 현재 로드가 많이 걸리는 서버로는 적은 수의 새로운 연결을 맺게 하고 자원이 많이 남는 프레젠터로 더 많은 새로운 연결이 맺어지도록 하고 있습니다.스케일 인/아웃AWS에서는 트래픽에 따라 서버 개수를 늘리기도 하고 줄이기도 하는 AutoScaling 이라는 기능이 있습니다. 프레젠터가 스케일 아웃될때에는 프레젠터가 스스로 Route53에 레코드를 추가하는 식으로 새로운 연결을 맺도록 할 수 있습니다. 하지만 스케일 인으로 프레젠터가 제거될 때에는 Route53에서 레코드를 삭제하더라도 함부로 프레젠터 서버를 종료시킬 수 없습니다. 종종 클라이언트의 DNS 캐싱 로직에 문제가 있어, Route53에서 레코드를 삭제되었는데도 불구하고 이를 업데이트하지 못해 기존 프레젠터로 연결을 시도하는 경우가 있을 수 있기 때문입니다. 따라서 프레젠터 클러스터가 스케일 인 될 때에는 기존의 모든 연결이 끊어지고 충분한 시간 동안 새로운 연결이 생기지 않은 경우에만 서버를 종료시켜야 합니다. AutoScaling Group의 LifeCycleHook을 이용하여 위와 같은 조건을 만족 시켰을 때에만 프레젠터 서버를 완전히 종료시키도록 하였습니다.못다 한 이야기프레젠터라는 이름이 이상하다고 생각하시는 분들이 있을 것으로 생각합니다. 멀티티어 아키텍처를 이야기할 때 프레젠테이션 티어, 어플리케이션 티어, 데이터베이스 티어로 구분하곤 하는데 이 프레젠테이션 티어에서 나온 이름입니다. 지금은 실제 프레젠터가 하는 역할과 프레젠테이션 티어가 보통 맡게 되는 역할에는 많은 차이가 있지만, 어쩌다 보니 이름은 그대로 가져가게 되었습니다.프레젠터에서 AutoScaling을 하기 위해 LifeCycleHook을 이용합니다. 이때 프레젠터를 위해 LifeCycleHook 이벤트를 처리하는 프로그램을 직접 짠 것이 아니라 비트윈 개발팀이 내부적으로 만든 Kharon이라는 프로그램을 이용하였습니다. Kharon은 인스턴스가 시작되거나 종료될 때 실행할 스크립트를 작성하고 인스턴스의 특정 위치에 놓는 것만으로 LifeCycleHook을 쉽게 이용할 수 있게 하는 프로그램입니다. Kharon 덕분에 비트윈 내 다양한 시스템에서 별다른 추가 개발 없이 LifeCycleHook을 쉽게 활용하고 있습니다. 후에 Kharon에 대해 자세히 다뤄보도록 하겠습니다.정리비트윈 개발팀에서는 오랫동안 유지되는 수많은 채팅 서버 연결들을 처리하고 클라이언트와 서버 간 프로토콜을 단순화시키는 등 여러 이점을 얻고자 ELB의 역할을 대신하는 프레젠터를 만들었습니다. 프레젠터를 만드는 과정에서 여러 기술적 문제가 있었습니다. 이를 해결하기 위해 C++로 연결 유지 모듈을 따로 작성하였고 Multiplexing Protocol을 따로 정의하였으며 그 외 여러 가지 기술적인 결정들을 하였습니다. 이런 과정에서 시행착오들이 있었지만 이를 발판 삼아 더 좋은 기술적 결정을 내리기 위해 고민하여 결국 기존 시스템에 쉽게 적용할 수 있고 쉽게 동작하는 프레젠터를 만들어 이용하고 있습니다.
조회수 4967

Bluetooth Low Energy(BLE) 파헤치기

1. What is BLE?스마트폰이 출시되어 대중화가 될 무렵, ‘스마트’한 개념의 밴드, 워치, 글래스 등이 출시되면서 웨어러블 디바이스 시장이 태동하기 시작했다. 그리고, 2015년 상반기, 애플워치의 등장으로 작은 생태계를 이루고 있던 웨어러블 디바이스들이 다시 한번 각광을 받게 되었다. 각기 생긴 모습은 다르지만 이들의 공통점은 스마트폰과 연동되어 작동한다는 것이었다. 과거부터 기기들간의 단거리 무선통신은 Bluetooth라는 기술이 이용되었다. Bluetooth가 공식적으로 등장한지 약 16년이라는 세월이 흘렀지만, 여전히 기기간의 무선통신에는 Bluetooth가 사용된다. 하지만, 지금 사용되는 Bluetooth는 기존과는 다른 방식이다. 바로 BLE라는 특징을 가진 Bluetooth인데, 바로 이것이 오늘날 다양한 종류의 웨어러블 디바이스들이 태어날 수 있었던 원동력이 되었다. 그렇다면 BLE라는 것이 도대체 무엇일까?그림1. BLE가 뭐지? 먹는건가?과거부터 기기들간의 무선 연결은 주로 Bluetooth라는 기술을 이용했는데, 이들은 기기간에 마스터, 슬레이브 관계를 형성하여 통신하는 Bluetooth Classic이라는 방식을 이용했다. 사람들이 이러한 기기들을 이용하면서 많이 염려했던 것은 ‘Bluetooth를 연결하면 베터리가 빨리 소모된다’, ‘사용하지 않을 때는 Bluetooth 꺼놓아야지’ 등과 같은 베터리 관련된 문제들이었다. 사실이었다. Bluetooth Classic은 다른 디바이스를 무선으로 연결을 하여 사용할 수 있는 편리함을 주었지만, 연결이 되는 동안에는 베터리를 빠르게 소모시켰기 때문에 사용하는 데에 많은 불편함이 있었다.2010년, 새로운 Bluetooth 표준으로 Bluetooth 4.0 이 채택이 된다. 기존의 Bluetooth Classic과의 가장 큰 차이는 훨씩 적은 전력을 사용하여 Classic과 비슷한 수준의 무선 통신을 할 수 있다는 점이었다. 이는 당시 Bluetooth의 최대 단점이었던 과도한 베터리를 소모 문제를 해결하는 기술이었기 때문에, Bluetooth 관련 업계에 큰 반향을 일으켰다. 이렇게 저전력을 이용하여 무선통신을 하는 특징을 Bluetooth Low Energy (이하 BLE) 라고 부르는데, Bluetooth 4.0 이후의 버전들은 이 용어로 대체되서 불리기도 한다. 최근 출시되고 있는 스마트 밴드, 워치, 글래스 등의 웨어러블 무선통신 기기들의 대부분은 이 BLE 방식을 이용하여 무선 통신을 한다.Bluetooth Smart Ready, Smart, ClassicBLE 기술이 등장하면서 Bluetooth 디바이스들은 아래와 같이 3가지로 분류 되었다.그림2. BLE 3가지 분류Bluetooth 4.0과 함께 새롭게 등장한 Bluetooth Smart Ready, Bluetooth Smart에 대해서 살펴보면,Bluetooth Smart Ready 디바이스는 Bluetooth Classic 및 저에너지 Bluetooth 무선통신 (BLE)을 지원하기 때문에 “듀얼 모드” 라디오라고 불린다. 따라서, 이들은 현재 시장에 나와 있는 수억 종의 Bluetooth 디바이스들에 대한 역방향 호환성을 가진다. 종류에는 스마트폰, 태블릿, PC, TV 그리고 셋탑박스 및 게임 콘솔 등이 있다. 이런 디바이스들은 클래식 Bluetooth 디바이스 및 Bluetooth Smart 디바이스들로부터 데이터를 받아, 이들을 유용한 정보로 변환시키는 Bluetooth 시스템의 허브라고 할 수 있다.Bluetooth Smart 디바이스 내에 있는 라디오는 “싱글모드” 라디오라 불리는데, BLE 연결만을 지원한다는 의미이다. 이들은 기존의 Bluetooth Classic 디바이스들과 호환이 되지 않고 듀얼모드 라디오를 가진 Bluetooth Smart Ready 디바이스 혹은 제조업체에 의해 호환성이 명시된 특정 Bluetooth 디바이스에만 연결이 가능하다. Bluetooth Smart 디바이스들은 ‘우리 집의 창문은 모두 잠겨 있는지’, ‘내 인슐린 농도는 얼마인지’, ‘오늘 내 몸무게는 몇 킬로그램인지’ 등과 같이 특정한 형태의 정보를 수집해, Bluetooth Smart Ready 디바이스로 보내기 위해 만들어진 디바이스이다. 종류에는 심박 모니터, 스마트 손목시계, 창문 및 현관 보안 센서, 자동차 키 체인, 그리고 혈압 팔찌 등이 있다.이 글에서는 BLE를 사용하는 디바이스들이 어떤 과정으로 서로 연결되어 통신을 하는지 그리고 이 과정들을 tracking 할 수 있는 장비인 Ubertooth 에 대해 내용을 정리해서 공유해보고자 한다.2. How they communicate?BLE를 지원하는 디바이스들은 기본적으로 Advertise(Broadcast) 과 Connection 이라는 방법으로 외부와 통신한다.Advertise Mode ( = Broadcast Mode)특정 디바이스를 지정하지 않고 주변의 모든 디바이스에게 Signal을 보낸다. 다시 말해, 주변에 디바이스가 있건 없건, 다른 디바이스가 Signal을 듣는 상태이건 아니건, 자신의 Signal을 일방적으로 보내는 것이라고 생각하면 된다. 이 때, Advertising type의 Signal을 일정 주기로 보내게 된다.Advertise 관점에서, 디바이스의 역할은 다음과 같이 구분된다.Advertiser ( = Broadcaster) : Non-Connectable Advertising Packet을 주기적으로 보내는 디바이스.Observer : Advertiser가 Advertise를 Non-Connectable Advertising Packet을 듣기 위해 주기적으로 Scanning하는 디바이스.그림3. Advertiser and ObserverAdvertise 방식은 한 번에 한 개 이상의 디바이스와 통신할 수 있는 유일한 방법이다. 주로 디바이스가 자신의 존재를 알리거나 적은 양(31Bytes 이하)의 User 데이터를 보낼 때도 사용된다. 한 번에 보내야 하는 데이터 크기가 작다면, 굳이 오버헤드가 큰 Connection 과정을 거쳐서 데이터롤 보내기 보다는, Advertise를 이용하는 것이 더 효율적이기 때문이다. 게다가 전송할 수 있는 데이터 크기 제한을 보완하기 위해 Scan Request, Scan Response을 이용해서 추가적인 데이터를 주고 받을 수 있다 (이에 대해서는 뒤에 자세히 설명한다). Advertise 방식은 말 그대로 Signal을 일방적으로 뿌리는 것이기 때문에, 보안에 취약하다.Connection Mode양방향으로 데이터를 주고받거나, Advertising Packet으로만 전달하기에는 많은 양의 데이터를 주고 받아야 하는 경우에는, Connection Mode로 통신을 한다. Advertise처럼 ‘일대다’ 방식이 아닌, ‘일대일’ 방식으로 디바이스 간에 데이터 교환이 일어난다. 디바이스간에 Channel hopping 규칙을 정해놓고 통신하기 때문에 Advertise보다 안전하다.Connection 관점에서 디바이스들의 역할은 다음과 같이 구분된다.Central (Master) : Central 디바이스는 다른 디바이스와 Connection을 맺기 위해, Connectable Advertising Signal을 주기적으로 스캔하다가, 적절한 디바이스에 연결을 요청한다. 연결이 되고 나면, Central 디바이스는 timing을 설정하고 주기적인 데이터 교환을 주도한다. 여기서 timing이란, 두 디바이스가 매번 같은 Channel에서 데이터를 주고 받기 위해 정하는 hopping 규칙이라고 생각하면 된다.Peripheral (Slave) : Peripheral 디바이스는 다른 디바이스와 Connection을 맺기 위해, Connectable Advertising Signal을 주기적으로 보낸다. 이를 수신한 Central 디바이스가 Connection Request를 보내면, 이를 수락하여 Connecion을 맺는다. Connection을 맺고 나면 Central 디바이스가 지정한 timing에 맞추어 Channel을 같이 hopping을 하면서 주기적으로 데이터를 교환한다.그림4. Central and Peripheral3. Protocol Stack디바이스들은 Bluetooth로 통신을 하기 위한 Protocol Stack을 가지고 있다. 일반적으로 네트워크 통신을 하기 위해서는, 통신을 위한 규약인 Protocol을 정의해야 되는데, 이렇게 정의된 Protocol들을 층층이 쌓아놓은 그룹이 Protocol Stack이다. Bluetooth Signal Packet을 수신하거나 송신할 때, 이 Protocol Stack을 거치면서 Packet들이 분석되거나 생성된다.그림5. Protocol Stack위 그림에서 볼 수 있듯이 Protocol Stack은 가장 아랫단부터 크게 Controller, Host, Application 로 나뉜다. 여기서는 Connection 과정에서 필요한 부분인 Physical Layer, Link Layer, Generic Access Profile(GAP), Generic Attribute Profile(GATT)에 대해서 알아볼 것이다.3.1 Physical LayerPhysical Layer에는 실제 Bluetooth Analog Signal과 통신할 수 있는 회로가 구성되어 있어서, Analog 신호를 Digital 신호로 바꾸어 주거나 Digital 신호를 Analog 신호로 바꾼다. 또한 Bluetooth에서는 2.4 GHz 밴드를 총 40개의 Channel로 나누어 통신을 한다. 40개 Channel 중 3개 Channel은 Advertising Channel 로써 각종 Advertising Packet을 비롯하여 Connection을 맺기 위해 주고 받는 Packet들의 교환에 이용된다. 나머지 37개의 Channel은 Data Channel 로써 Connection 이후의 Data Packet 교환에 이용된다.그림6. Channels3.2 Link LayerPhysical Layer의 바로 윗단에는 Link Layer이 있다. Link Layer은 하드웨어와 소프트웨어의 조합으로 구성되어 있다. 하드웨어 단에서는 높은 컴퓨팅 능력이 요구되는 작업들 (Preamble, Access Address, and Air Protocol framing, CRC generation and verification, Data whitening, Random number generation, AES encryption 등)이 처리되고, 소프트웨어 단에서는 디바이스의 연결 상태를 관리한다. 또한 통신하는데 있어서 디바이스의 Role을 정의하고 이에 따라 변경되는 State를 가지고 있다.RoleMaster : 연결을 시도하고, 연결 후에 전체 connection을 관리하는 역할.Slave : Master의 연결 요청을 받고, Master의 timing 규약을 따르는 역할.Advertiser : Advertising Packet을 보내는 역할.Scanner : Advertising Packet을 Scanning하는 역할. Scanner는 아래와 같은 2가지 Scanning 모드가 있다.Passive Scanning : Scanner는 Advertising Packet을 받고 이에 대해 따로 응답을 보내지 않는다. 따라서 해당 Packet을 보낸 Advertiser는 Scanner가 Packet을 수신했는지에 대해서 알지 못한다.Active Scanning : Advertising Packet을 받은 Scanner는 Advertiser에게 추가적인 데이터를 요구하기 위해 *Scan Request라는 것을 보낸다. 이를 받은 Advertiser는 *Scan Response로 응답한다.Scan Request, Scan Response : Advertising Packet type의 한 종류이다. 앞서, 31bytes 이하의 User data에 대해서는 Advertising Signal Packet에 넣어서 보낼 수 있다고 하였다. 하지만 31bytes보다는 크지만, Commection까지 맺어서 보내기는 오버헤드가 큰 데이터가 있을 때, Scan Request, Scan Response를 이용하면 두 번에 걸쳐서 데이터를 나눠 보낼 수 있게 된다. Advertising Packet을 받은 Scanner는 추가적인 User Data(예를 들어, Peripheral 디바이스의 이름)를 얻기 위해 Scan Request를 보내게 된다. Scan Request를 받은 Advertiser는 나머지 데이터를 Scan Response Signal에 담아서 보낸다.이들은 크게 Connection 전의 역할(Advertiser, Scanner), 후의 역할(Master, Slave)로 분류된다.StateLink Layer는 5가지 State를 가지고 있는데, 각 디바이스는 서로 연결이 되는 과정에서 이 State를 변화시킨다. 다음과 같은 5개의 State가 존재한다.Standby State : Signal Packet을 보내지도, 받지도 않는 상태.Advertising State : Advertising Packet을 보내고, 해당 Advertising Packet에 대한 상대 디바이스의 Response를 받을 수 있고 이에 응답할 수 있는 상태.Scanning State : Advertising Channel에서 Scaning하고 있는 상태.Initiating State : Advertiser의 Connectable Advertising Packet을 받고난 후 Connetion Request를 보내는 상태.Connection State : Connection 이후의 상태.아래 그림은 각각의 State를 Diagram으로 나타낸 것이다.그림7. Link Layer State3.3 Generic Access Profile (GAP)Generic Access Profile (GAP)는 서로 다른 제조사가 만든 BLE 디바이스들끼리 서로 호환되어 통신할 수 있도록 해주는 주춧돌 역할을 한다. 즉, 어떻게 디바이스간에 서로를 인지하고, Data를 Advertising하고, Connection을 맺을지에 대한 프레임워크를 제공한다. 그래서 GAP는 최상위 Control Layer라고도 불린다. Advertising Mode일 때, GAP에서 Advertising Data Payload와 Scan Response Payload를 포함할 수 있다.또한 GAP에서는 BLE 통신을 위해 Role, Mode, Procedure, Security, Additional GAP Data Format 등을 정의한다. 이들은 실제 API와 직접적으로 많은 연관이 있기 때문에 그 내용이 상당히 많지만, 여기서는 BLE Connection과 관련이 있는 Role에 대해서만 알아보겠다.RoleBroadcaster : Link Layer에서 Advertiser 역할에 상응한다. 주기적으로 Advertising Packet을 보낸다. 예를 들면, 온도센서는 온도데이터를 자신과 연결된 디바이스에게 일정주기로 보낸다.Observer : Link Layer에서 Scanner 역할에 상응한다. Broadcaster가 뿌리는 Advertising Packet에서 data를 얻는다. 온도센서로부터 온도데이터를 받아서 디스플레이에 나타내는 테블릿 컴퓨터의 역할이다.Central : Link Layer에서 Master 역할과 상응한다. Central 역할은 다른 디바이스의 Advertising Packet을 듣고 Connection을 시작할 때 시작된다. 좋은 성능의 CPU를 가지고 있는 스마트폰이나, 테블릿 컴퓨터들의 역할이다.Peripheral : Link Layer에서 Slave 역할과 상응한다. Advertising Packet을 보내서 Central 역할의 디바이스가 Connection을 시작할 수 있도록 하게끔 유도한다. 센서기능이 달린 디바이스들의 역할이다.3.4 Generic Attribute Profile (GATT)BLE Data 교환을 관리하는 GATT는 디바이스들이 Data를 발견하고, 읽고, 쓰는 것을 가능하게 하는 기초적인 Data Model과 Procedure를 정의한다. 그래서 GATT는 최상위 Data Layer라고도 불린다. 디바이스간에 low-level에서의 모든 인터렉션을 정의하는 GAP와는 달리, GATT는 오직 Data의 Format 및 전달에 대해서만 처리한다. Connection Mode일 때, GATT Service와 Characteristic을 이용하여 양방향 통신을 하게 된다. Service와 Characteristic에 대한 내용은 여기를 참고하길 바란다.GATT도 Data 처리와 관련해서 다음과 같은 역할을 정의한다.RoleClient : Server에 Data를 요청한다. 하지만 처음에는 Server에 대해서 아는 것이 없기 때문에, Service Discovery라는 것을 수행한다. 이 후, Server에서 전송된 Response, Indication, Notification을 수신할 수 있다.Server : Client에게 Request를 받으면 Response를 보낸다. 또한 Client가 사용할 수 있는 User Data를 생성하고 저장해놓는 역할을 한다.4. Packet TypeBLE 통신에서는 두 가지 종류의 패킷인 Advertising Packet, Data Packet만이 존재한다. Connection을 맺기 전에는 Advertising Packet type, 맺은 후에는 Data Packet type으로 Signal을 생성한다. Data Packet은 하나로 통일되지만, Advertising Packet은 특정 기준에 따라서 다음과 같은 성질들을 갖는다.ConnectabilityConnectable : Scanner가 Connectable Advertising Packet을 받으면, Scanner는 이를 Advertiser가 Connection을 맺고 싶어한다는 신호로 받아들인다. 그러면 Scanner는 Connection Request (이하 CONNECTREQ)를 보낼 수 있다. 해당 Connectable Signal을 보낸 Advertiser는 Scanner가 CONNECTREQ가 아닌 다른 타입의 Signal을 보내면 해당 Packet을 무시하고 다음 Channel로 이동하여 계속 Advertising을 진행한다.Non-Connectable : Non-Connectable Packet을 받은 Scanner는 CONNECT_REQ를 보낼 수 없다. 주로 Connection 목적이 아닌, Data 전달이 목적일 때 쓰인다.ScannabilityScannable : Scanner가 Scannable Advertising Packet을 받으면, Scan Request (이하 SCANREQ)를 보낼수 있다. Scannable Signal을 보낸 디바이스는 Scanner가 SCANREQ가 아닌 다른 타입의 Signal을 보내면 해당 Packet을 무시하고 버린다.Non-Scannable : Non-Scannable Signal을 받은 Scanner는 SCAN_REQ를 보낼 수 없다.DirectabilityDirected : Packet안에 해당 Signal을 보내는 디바이스의 MAC Address와 받는 디바이스의 MAC Address가 들어있다. MAC Address 이외의 데이터는 넣을 수 없다. 모든 Directed Advertising Packet은 Connectable 성질을 갖는다.Undirected : 해당 Signal을 받는 대상이 지정되어 있지 않다. Directed Advertising Packet과는 다르게, 사용자가 원하는 데이터를 넣을 수 있다.위의 내용을 종합하면, Advertising pakcet을 아래와 같이 4가지 type으로 나눌 수 있다.그림6. Advertising Packet Type5. How they really communicate?BLE 통신의 핵심은 ‘timing’이다.Before ConnectionConnection 전, 디바이스는 3개의 Advertising Channel을 이용해서 데이터를 주고 받는다고 했다. 이들은 이 3개의 Channel을 자신만의 time interval로 hopping한다. 서로의 hopping 규칙이 일치하지 않기 때문에 Channel이 서로 엇갈리는 경우가 많을 것이다. 예를 들어, Advertiser는 1번 Channel에 Advertising Packet을 보냈는데, 같은 시간에 Scanner는 3번 Channel에 대해서 Scanning을 하게 되면 데이터 전달이 되지 않는 것이다. 하지만 이러한 hopping이 빠르게 자주 일어나기 때문에, 두 디바이스가 같은 Channel에 대해 Advertising와 Scanning이 발생하는 경우도 많이 생긴다. 이 경우에 서로 데이터를 주고 받을 수 있다.After ConnectionConnection이 되면, Advertising은 종료되고 기기들은 Central, Peripheral 중 하나의 역할을 하게된다. Connection을 개시한 기기가 Central이며, Advertiser가 Peripheral이 된다. 그리고 두 디바이스는 엇갈렸던 hopping 규칙을 통일시킨다. 그렇게 함으로써, 매번 같은 채널로 동시에 hopping하면서 Signal을 주고 받을 수 있게 된다. 이는 둘 간의 Connection이 끊어질 때까지 지속된다.6. How they connect each other?디바이스간의 BLE 연결을 iPhone과 Zikto Walk와의 연결과정으로 설명하면 다음과 같다.1) Zikto Walk가 Advertising Channel을 hopping하면서 Advertising Packet을 보낸다.(Zikto Walk의 Advertising Packet 유형은 ADV_IND이다)2) iPhone Bluetooth를 켠 후, Zikto 앱에 Zikto Walk를 등록한다. iPhone은 Advertising Channel을 hopping하면서 Scan을 하다가 연결하려는 Zikto의 디바이스 이름 등의 추가적인 정보를 얻기위해 SCAN_REQ를 보낸다.3) SCANREQ를 받은 Zikto Walk는 SCANRSP를 보낸다.4) Pairing이 완료되고, Zikto Walk는 다시 Advertising Packet을 다시 일정 주기마다 보낸다.5) iPhone에서 Zikto Walk로부터 걸음 수 등의 Data를 받기 위해 Sync 버튼을 누른다. 이 버튼을 누르면 iPhone은 CONNECT_REQ를 보낸다.6) Zikto와 iPhone은 서로 Acknowledging을 시작하고, timing 정보 등을 동기화 한다.7) Connection이 완료된다.8) Connection이 완료된 후, Service Data, Characteristic Data 등에 대한 Data 교환이 일어난다.9) iPhone과 Zikto Walk간에 Data Sync가 완료되면, Connection이 해제되고, 다시 Advertising Packet을 보낸다. 이를 그림으로 표현하면 아래와 같다.그림6. Advertising Packet Type7. Ubertooth디바이스간 BLE를 이용한 통신 과정에 대해 알고나니, Bluetooth Signal Packet도 Capturing 할 수 있을 거라는 생각이 들었다. 검색을 해 본 결과, 오픈소스 Bluetooth Test tool인 Ubertooth라는 장치로 디바이스간의 BLE 통신을 tracking 할 수 있다는 사실을 알게 되었다. 가격은 100달러로 생각보다 저렴했지만 국내에서는 구매할 수가 없었다. 그렇다고 궁금한 것을 해보지도 않고 포기하는 것은 엔지니어의 마인드가 아니지 않겠는가. 직접 아마존 (www.amazon.com)에서 해외구매를 하였다. 이렇게 바다 건너 멀리서 날아온 Ubertooth를 사용했던 경험을 바탕으로, Ubertooth의 원리와 BLE 통신에 대해서 조금 더 자세히 설명을 해보고자 한다.Ubertooth는 10cm정도의 몸체와 그와 비슷한 길이의 안테나를 가지고 있는 매우 작고 귀여운 모양이다. 이것이 이름하여 Ubertooth!그림8. Ubertooth오픈소스이기 때문에 모든 소스가 공개되어 있고, 소스를 빌드하고 사용하는 방법도 Ubertooth Github 및 Ubertooth Blog에 잘 나와 있어서 사용하기가 수월했다.How it works?Ubertooth는 크게 Bluetooth Classic을 tracking하는 기능과 BLE를 tracking하는 기능으로 나뉘는데, 여기서는 BLE 통신을 tracking 하는 원리에 대해서 다루겠다.BLE는 앞에서 언급했다시피, Connection 전, 후로 통신하는 방법이 다르다. 그리고 위의 내용들을 꼼꼼히 읽은 독자라면 BLE 통신에서 가장 중요하다고 언급했던 timing 이라는 것을 기억할 것이다. timing 은 BLE 통신에서 굉장히 중요한 요소이기 때문에, 보다 더 자세하고 쉽게 설명을 해보겠다.종이컵 전화기를 사용하여 대화를 해야하는 두 사람이 있다. 종이컵 전화기는 총 40개가 놓여져 있다. 이 두 사람은 40개 전화기 중 하나를 사용해서 대화를 주고 받고, 일정시간 뒤에 다음번 전화기를 이용해야 한다. 이러한 커뮤니케이션 방식에서 소통을 하기 위해서는 한 전화기로 얼마만큼의 시간동안 통화를 할 것인지, 다음 전화기는 어떤 전화기를 사용할 것인지, 그리고 어떤 방식으로 자신들의 대화를 다른 사람들의 대화들로부터 구분할 것인지 등에 대해 알아야 할 것이다. 이것들이 위에서 말했던 timing 관련 정보이다.실제 BLE 통신에서 timing 과 관련된 정보들은 다음과 같다 : Access Adress, CRC Info, Hop Interval, Hop Increment (해당 내용들에 대한 자세한 설명은 여기를 참고하기 바란다). BLE 통신을 하는 디바이스들은 이 timing 관련 정보를 동기화하여, Connection이 맺어진 이후에 해당 규칙에 따라 Channel을 hopping하면서 데이터를 주고 받는다. Ubertooth는 바로 이 정보를 알아내어, Master, Slave와 같은 패턴으로 Channel을 hopping하면서 대화를 엿듣는다. 아까 말한 종이컵 전화기에 빗대어 말하면, 제 3자(Ubertooth)가 두 사람이 정한 대화 규칙을 알아내서, 매번 이들이 전화기를 바꿔가며 대화를 할 때 마다 해당 전화기의 대화 내용을 엿듣는 것이다. 굉장히 흥미로운 방법이 아닐 수 없다. 그렇다면 Ubertooth는 어떻게 이 정보를 알아낼까?Before Connection두 디바이스가 연결되기 전, Ubertooth가 timing 관련 정보를 알아내는 방법은 매우 간단하다. Scanner가 Advertiser에게 Connection을 맺기위해 보냈던 CONNECT_REQ을 기억하는가? 공교롭게도 해당 패킷에는 이 네 가지 정보가 전부 들어있다. Ubertooth는 그 정보를 추출해내어 저장해 두고, 그 규칙에 맞게 Channel을 hopping하면서 Signal Data를 전부 엿듣는다.그림9. Ubertooth로 Capture한 CONNECT_REQ packetAfter Connection이미 연결된 디바이스들은 CONNECT_REQ를 보낼 일이 없다. 그러면 Ubertooth는 Connection 이후의 상황에 대해서는 Signal Data를 엿듣지 못하는 것일까? 아니다. Connection 이후의 상황에 대해서 Ubertooth는 다음과 같은 방법을 이용한다.BLE Signal Packet은 Advertise Mode이든 Connection Mode이든간에 무조건 하나의 Signal Packet format만 존재하기 때문에, Packet마다 특정 정보가 존재하는 부분은 어느 Packet에서나 똑같다. 4가지 정보 중 Access Address라는 것은 모든 Signal Packet마다 존재한다. Access Address라는 것은 두 디바이스간의 Unique한 Connection을 나타내는 4bytes 크기의 Identifier로써, CONNECT_REQ를 보내는 디바이스에 의해 랜덤하게 생성된다. Ubertooth는 37개의 Data Channel을 hopping하면서 모든 Data Packet의 Access Address를 추출해내어 Look Up Table 형태로 저장해 놓는다. 그리고는 각각의 Access Address가 등장한 횟수를 세게 되는데, 가장 먼저 특정 횟수만큼 등장한 Access Address를 target으로 잡는다. 나머지 3가지 정보는 각각 추출해내는 방법 및 알고리즘이 따로 존재하는데 해당 내용도 위에 언급한 사이트에 잘 나와있다. 이렇게 해서 네 가지 정보를 알아낸 Ubertooth는 두 디바이스와 같은 패턴으로 Channel을 hopping하면서 Signal Data를 엿듣는다.그림10. Ubertooth로 Capture한 Aceess Address과 나머지 3가지 정보들이렇게 보면, Ubertooth로 모든 것을 할 수 있을 것처럼 보이지만, 몇 가지 한계점이 있기도 하다. Ubertooth가 timing 관련 정보를 얻어내는 과정에 대해 다시 한 번 생각해보길 바란다. 잘 모르겠다면, 직접 Ubertooth 구매하여 테스트를 해보는 것도 엔지니어로써 굉장히 좋은 경험이 될 것이다.8. ConclusionBLE 통신과 이를 tracking하는 Ubertooth에 대해서 알아보았다. 매우 장황한 내용처럼 보이지만 이것도 매우 압축해서 설명한 것이다. 하나하나 디테일하게 쓰기 보다는 BLE를 처음 접하는 사람이 최대한 이해하기 쉽도록 작성하는 것에 초점을 맞추었다. 위의 내용들을 바탕으로, 독자들이 BLE에 더 넒고 깊은 지식을 얻게 되었으면 하는 바램이다. 글을 읽으면서 Bluetooth Classic은 어떻게 통신하는지에 대해 궁금하신 분들도 있을거라 생각한다. 이에 대해서 간단히 언급하자면, Bluetooth Classic 통신 방식은 BLE보다 훨씬 더 복잡하다. BLE에 대해서 어느 정도 알게 되었다면, Bluetooth Classic에 도전해보는 것도 괜찮을 것이다. BLE내용과 관련해서 보충이 필요한 내용이나, 관련 경험 혹은 궁금한 점 등에 대해서 아낌없이 조이와 공유해주길 바란다.9. ReferenceAkiba, “Getting Started with Bluetooth Low Energy: Tools and Techniques for Low-Power Networking”, O’Reilly Media(2015)http://www.slideshare.net/steveyoon77/bluetooth-le-controllerhttp://www.hardcopyworld.com/ngine/aduino/index.php/archives/1132https://www.bluetooth.org/ko-kr/bluetooth-brand/smart-marks-faqshttp://trvoid.blogspot.kr/2013/05/ble.htmlhttp://blog.lacklustre.net/posts/BLEFunWithUbertooth:SniffingBluetoothSmartandCrackingItsCrypto/#조이코퍼레이션 #개발팀 #개발자 #개발환경 #업무환경 #인사이트 #경험공유
조회수 2146

프로세스 마이닝과 AI를 통한 프로세스 혁신

지난해 이세돌과 알파고의 대결 이후에 인공 지능 (AI)과 기계 학습은 국내에서 많은 대중들의 관심을 얻어 중요한 추진력을 얻었으며, 모든 산업 분야의 기업들이 해당 기술을 빠른 속도로 계속 적용하여 사용하는 비중이 더욱 높아졌습니다. 실제로 Gartner는 2022년까지 스마트 머신과 로봇이 고학년 전문직 분야를 대체할 수 있을 것으로 내다봤으며, 심지어는 인공지능이 경영자 CEO도 대체 가능할 것인지에 대한 논의도 일어나고 있습니다. 이것은 사람이 과거 경험에 의해서 의사 결정을 내리 듯이 인공 지능도 확보한 데이터를 기반으로 의사 결정 모델을 만들 수 있다는 유사성에 기반합니다.  인공 지능에 의한 의사 결정은 사람한테 종종 있을 수 있는 감정이나 개인적 이해관계 및 관례에 의해 불합리한 판단에서 벗어나 데이터의 의한 객관적 판단을 할 수 있다는 장점이 있습니다.여기서 중요한 것은 인공지능이 학습하기 위한 “데이터”입니다.  지금까지 머신러닝이 막대한 이미지, 음성, 영상 데이터를 축적한 후 해당 데이터의 특징을 추출하여 패턴을 학습하여 자연어 처리 등을 통해 사람처럼 인식하여 분류하거나 상황을 판단하였듯이 기업 내 여러 가지 업무 활동에 머신 러닝을 적용하기 위해서는 이와 마찬가지로 관련 데이터가 필요합니다.제조 분야의 공정 관리, 공공 서비스, 물류 공급망 관리 등 전통적인 기업 내 업무 프로세스는 인공 지능에 의한 자동화과 효율화를 통해 혁신이 필요한 분야입니다. 기존에 외부 협력 업체로부터의 납기 예측, 소요되는 자재 인력 등 리소스 산정, 생산 스케줄, 장비 파라미터 입력값 등은 사람에 의해 수작업으로 진행 시 몇 주에서 수개월 소요되었지만, 인공 지능과 기계 학습 기반의 솔루션 도움으로 정확하게 지속적인 추세를 인식하고 인간의 개입 없이 데이터 중심의 결정이 가능해집니다.지금까지 기업 내 축적된 엄청난 양의 데이터를 활용하여 여러 산업 분야에서 숨겨진 패턴과 상관관계, 이상 징후 및 불량 탐지, 고객 수요 예측 등이 시도되었습니다. 하지만 이러한 시도들은 기업 내 문제 요인을 파악하여 우선적으로 어떤 부분에 초점을 맞추어 개선을 해야 하는지 알아야 하므로, 기업 경영 활동 전반에 걸쳐 돌아가는 판세를 읽는 노력이 필요합니다. 하지만, 기업 내에서 이뤄지고 있는 프로세스는 충분히 복잡하여, 개별 단위 작업의 전문가들은 존재하겠지만, 각 개별 부서, 구성원, 시스템 간에서 발생하는 다양한 상호작용과 이에 따른 예외 상황이 존재하여 이를 파악하기가 쉽지 않습니다.프로세스 마이닝은 데이터 기반의 프로세스 분석을 통해 문제 부분을 파악하여, 실제 인공 지능이나 머신 러닝을 적용하여 개선할 부분을 찾을 수 있도록 도와줍니다. 그리고, 프로세스 개선을 위해 머신러닝을 적용하기 위해서는 앞서 말한 것처럼 “데이터”가 학습될 수 있는 형태의 기반을 제공합니다.아래 그림과 같이 이벤트 로그를 기반으로 프로세스 모델을 생성하고, 수집된 패턴들과 각 분기 단계에서의 주요 성과 지표들을 디지털화하여 인공지능이 이해할 수 있는 형태로 축적합니다 이렇게 축적된 프로세스 패턴 데이터를 가지고 알파고가 최적화된 다음의 한 수를 예측하듯이 프로세스 마이닝은 인공 지능 기술과 결합하여 과거 프로세스에 대한 이해뿐만 아니라, 현재 시점에서 앞으로의 프로세스를 예측하여 합리적인 의사 결정을 도와줄 것입니다.#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트
조회수 2313

Angular Lazy Loading 모듈 사용하기

Angular는 비동기식 라우팅이 가능합니다. 나중에 사용할 기능들을 NgModule로 분리하여 사용자의 요청이 들어왔을 때 모듈을 불러와 기능을 사용할 수 있고, 이러한 기술을 지연 로딩이라 합니다.프로젝트가 진행되고 기능이 추가될수록 어플리케이션 번들 크기가 커지고, 결국엔 초기 로딩 시간도 길어지게 됩니다. 지연 로딩을 사용하면 초기 로딩 시간을 줄일 수 있습니다. 컴파일 단계에서 나중에 사용할 모듈들을 메인 모듈에서 분리하여 번들을 생성합니다. 그리고 사용자가 기능을 요청할 때 비동기로 스크립트를 불러와 실행합니다. 지연 로딩에 대한 소개와 사용법은 Angular 공식 문서의 Routing & Navigation — Milestom 6: Asynchronous routing 을 참고하시길 바랍니다하지만 지연 로딩을 사용할 때 유의해야할 점이 몇 가지 있습니다.지연 로딩 모듈과 인젝터(Injector)지연 로딩이 완료되었을 때 Angular는 지연 로딩된 모듈을 루트 인젝터(Root Injector)의 자식이 되는 자식 인젝터를 이용하여 초기화하고, 서비스들을 자식 인젝터에 추가합니다. 즉, 인젝터가 분리되기에 지연 로딩된 모듈의 클래스들은 자식 인젝터로의 서비스 주입이 가능하지만 루트 인젝터로 만들어진 클래스들은 불가능합니다.이는 Angular의 독특한 의존성 주입 시스템 때문입니다. Angular의 인젝터는 처음 애플리케이션이 시작되었을 때, 컴포넌트나 다른 서비스에 주입되기 전에 포함된 모든 모듈들의 서비스 제공자들을 블러와 루트 인젝터를 생성합니다. 애플리케이션이 시작되고 나면 인젝터는 서비스들을 생성하고 주입을 시작하고, 새로운 서비스들을 제공자로 추가가 불가능합니다.그러므로 지연 로딩된 서비스들은 이미 생성이 완료된 루트 인젝터로 추가가 불가능합니다. 따라서 Angular는 지연 로딩된 모듈에 대해서 새로운 자식 인젝터를 만들는 전략을 취하게 된 것입니다.자식 인젝터가 새로 만들어지기 때문에 공통된 모듈을 사용할 때 주의하여야 합니다. 예를 들어 다음과 같이 SharedModule 에 CounterService 를 서비스로 추가하고 루트 모듈인 AppModule 과 지연 로딩 모듈인 LazyModule 에 각각 SharedModule 을 import 하였습니다.import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SharedModule } from './shared/shared.module'; import { AppShellComponent } from './app-shell.component'; const APP_ROUTES = [ { path: 'lazy', loadChildren: 'app/lazy/lazy.module#LazyModule' } ]; @NgModule({ imports: [ BrowserModule, SharedModule, RouterModule.forRoot(APP_ROUTES) ], declarations: [ AppShellComponent ], bootstrap: [AppShellComponent] }) export class AppModule { }import { Injectable } from '@angular/core'; @Injectable() export class CounterService { count = 0; increase(): void { this.count++; } decrease(): void { this.count--; } }import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SharedModule } from '../shared/shared.module'; import { SomeLazyComponent } from './some-lazy.component'; const LAZY_ROUTES = [ { path: '', component: SomeLazyComponent } ]; @NgModule({ imports: [ SharedModule, RouterModule.forChild(LAZY_ROUTES) ] }) export class LazyModule { }import { NgModule } from '@angular/core'; @NgModule({ providers: [ CounterService ] }) export class SharedModule { }그리고 루트 모듈의 컴포넌트와 지연 로딩 모듈의 컴포넌트에서 각각 CounterService 를 사용하여 숫자 값을 바꿔봅니다.서로 다른 인젝터에 CounterService 인스턴스가 만들어졌기 때문에 두 컴포넌트에 표시되는 숫자값은 다릅니다. 앞에서 말했듯이 지연 로딩 모듈은 루트 인젝터가 아닌 자식 인젝터를 이용하여 초기화하기 때문입니다.만약, 지연 로딩 모듈에서 제공되는 서비스를 다른 모듈에서 사용하려면 루트 모듈에 포함시켜 줘야 합니다. 다음과 같이 루트 모듈에게만 노출시킬 서비스 제공자들을 따로 빼내어 줄 수 있습니다.import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AccountLoginPageComponent } from './login-page.component'; const ACCOUNT_ROUTES: Routes = [ { path: 'login', component: AccountLoginPageComponent } ]; @NgModule({ imports: [ ... RouterModule.forChild(ACCOUNT_ROUTES) ], decalartions: [ AccountLoginPageComponent ] }) export class AccountLazyModule { }import { ModuleWithProviders, NgModule } from '@angular/core'; import { AccountAuthService } from './auth.service'; @NgModule({ imports: [...] }) export class AccountModule { static forRoot(): ModuleWithProviders { return { ngModule: AccountModule, providers: [ AccountAuthService ] }; } }AccountModule.forRoot() 를 루트 모듈에 import하면 다른 모듈에서도 AccountAuthService 를 사용할 수 있게 됩니다. 물론 이 경우 AccountModule를 지연로딩 모듈로 만들면 루트 모듈에 포함되기 때문에 번들을 나누는 의미가 없어질 수 있으니 AccountLazyModule 을 따로 두어 코드를 분리하였습니다.#타운컴퍼니 #개발 #개발자 #인사이트 #꿀팁
조회수 2632

Next.js 튜토리얼 3편: 공유 컴포넌트

* 이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다.** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요!목차1편: 시작하기2편: 페이지 이동 3편: 공유 컴포넌트 - 현재 글4편: 동적 페이지5편: 라우트 마스킹6편: 서버 사이드7편: 데이터 가져오기8편: 컴포넌트 스타일링9편: 배포하기개요Next.js는 전부 페이지에 관한 것입니다. React 컴포넌트를 export하고 그 컴포넌트를 pages 디렉터리 안에 넣어 페이지를 생성할 수 있습니다. 그러면 파일 이름을 기반으로 고정된 URL를 가지게 됩니다.export 된 페이지들은 JavaScript 모듈이므로 다른 JavaScript 컴포넌트를 이 페이지들 안에 import 할 수 있습니다.이는 어떤 JavaScript 프레임워크에서든 가능합니다.이번 편에서는 Header 컴포넌트를 만들고 여러 페이지들에서 사용해 볼 예정입니다. 마지막에는 하나의 Layout 컴포넌트를 구현하고 어떻게 이것이 여러 페이지들의 모양을 정의하는데 도움이 되는지 살펴볼 것입니다.설치이번 장에서는 간단한 Next.js 애플리케이션이 필요합니다. 다음의 샘플 애플리케이션을 다운받아주세요:아래의 명령어로 실행시킬 수 있습니다:이제 http://localhost:3000로 이동하여 애플리케이션에 접근할 수 있습니다.Header 컴포넌트 구현하기Header 컴포넌트를 구현해봅시다.다음과 같은 components/Header.js를 추가해주세요.이 컴포넌트는 애플리케이션에서 이용가능한 페이지에 대한 두 개의 링크가 있습니다. 또한 보기 쉽도록 링크를 스타일링 하였습니다.Header 컴포넌트 사용하기다음으로 페이지들 안에 Header 컴포넌트를 import하고 사용해봅시다. index.js 페이지를 다음과 같이 변경해주세요:about.js 페이지도 똑같이 변경할 수 있습니다.지금 http://localhost:3000로 이동하면 새로운 Header가 보이고 페이지 이동이 가능합니다.이 애플리케이션에서 간단한 몇 가지를 수정해봅시다!- 애플리케이션을 종료하세요.- conponents 디렉토리의 이름을 comps로 바꾸세요.- ../components/Header 대신에 ../comps/Header로부터 Header를 import 하세요.- 애플리케이션을 다시 실행시키세요.동작하나요?- 네- 아뇨. "컴포넌트를 찾을 수 없습니다"라는 에러가 발생합니다.- 아뇨. "컴포넌트는 components 디렉토리 안에 있어야합니다"라는 에러가 발생합니다.- 아뇨. "comps는 잘못된 디렉터리입니다"라는 에러가 발생합니다.컴포넌트 디렉토리예상대로 잘 동작합니다.꼭 특정한 디렉토리에 컴포넌트를 둘 필요는 없습니다. 원하는 대로 이름을 설정할 수 있습니다. 특정한 디렉토리는 pages 디렉토리뿐입니다.pages 디렉토리 안에 컴포넌트를 생성할 수 있습니다.Header 컴포넌트는 이를 가르키는 URL이 필요하지 않기 때문에 pages 디렉토리 안에 두지 않았습니다.레이아웃 컴포넌트애플리케이션 안의 다양한 페이지에서 공통의 스타일을 사용할 예정입니다. 이를 위해 공통 레이아웃 컴포넌트를 만들고 각 페이지에서 사용할 수 있습니다. 여기 예시가 있습니다:components/MyLayout.js에 다음의 내용을 추가해주세요:위와 같은 코드를 작성하면 다음같이 원하는 페이지에서 레이아웃을 사용할 수 있습니다:http://localhost:3000 페이지로 이동하여 확인할 수 있습니다.이제 레이아웃에서 {props.children}을 지워보고 무슨일이 일어나는지 살펴봅시다.무슨 일이 일어날까요?- 아무 일도 일어나지 않을 것이다- 표시되는 페이지의 내용이 사라질 것이다- "레이아웃은 내용이 필요합니다"라는 에러가 발생할 것이다- 브라우저의 컴포넌트에 대한 경고 메시지가 표시될 것이다하위 컴포넌트 렌더링하기{props.children}을 삭제하면 Layout은 아래와 같이 Layout 엘리먼트 하위에 둔 내용들을 랜더링하지 못합니다:이것은 레이아웃 컴포넌트를 생성하는 방법 중 하나입니다. 다음은 레이아웃 컴포넌트를 생성하는 다른 방법들입니다: 컴포넌트들 사용하기공유 컴포넌트를 사용하는 두 가지 경우를 다뤘습니다.1. 공통 Header 컴포넌트2. 레이아웃스타일을 지정하고 페이지 레이아웃 및 기타 원하는 모든 작업에 컴포넌트들을 사용할 수 있습니다. 더불어 NPM 모듈에서 컴포넌트를 import 하고 사용할 수도 있습니다.#트레바리 #개발자 #안드로이드 #앱개발 #Next.js #백엔드 #인사이트 #경험공유

기업문화 엿볼 때, 더팀스

로그인

/