스토리 홈

인터뷰

피드

뉴스

조회수 2470

사운들리 백엔드 이야기

사운들리는 '귀에 들리지 않는 소리'를 이용해서 컨텐츠를 전달할 수 있는 SaaS 플랫폼을 서비스하고 있습니다.제품의 구성요소는,음파를 송신할 수 있는 송신단음파를 모바일에서 수신할 수 있는 Android, iOS SDK그리고 컨텐츠를 제공하고 데이터를 수집, 분석하는 백엔드로 구성되어 있습니다.오늘은 구성 요소중 백엔드에 대해서 이야기 해보도록 하겠습니다.<그림 1. 사운들리 솔루션 구성도>사운들리의 인프라는 모두가 잘 아시는 아마존 웹 서비스를 이용하고 있으며, 크게 컨텐츠를 제공하는 API서버 부분, 로그를 수집, 분석하는 부분, 그리고 컨텐츠를 관리하는 CMS 부분으로 이루어져 있습니다.소프트웨어 스택Java : 현재 사운들리의 일부 시스템을 제외하고는 전부 자바로 작성되어 있습니다. Node.js로 시작하여 PHP를 거쳐 지금의 자바 기반의 시스템으로 구성하게 되었습니다. 다양한 사람들이 개발을 해오면서 각자 가장 잘할 수 있고, 빠르게 구현할 수 있는 언어로 개발되어 가다 현재의 자바로 통일되어 구성되게 되었습니다.Spring : API서버는 HTTP 기반의 REST API를 이용해 컨텐츠를 전달하고 있으며 스프링 프레임워크를 이용해 개발되었습니다. 이외에도 일부 분석에 스프링 배치를 사용하고 스프링을 편리하게 사용할 수 있게해주는 스프링 부트도 이용하고 있습니다.gRPC : 분산되어있는 서버들끼리 이기종 언어간 통신을 하기 위해서 Protocol Buffers 기반의 gRPC를 이용하고 있으며 서버들의 모니터링하는 서버와 에이전트들 사이의 통신 목적으로 사용합니다.Flume : 분산된 서버들에서 로그를 수집하는 역할을 합니다. 수집된 로그는 파일로 저장하며 실시간으로 볼수 있도록 엘라스틱서치에 같이 저장하고 있습니다. SDK에서 전송되는 로그 또한 웹서버의 엑세스 로그를 플럼 에이전트가 수집하는 방식으로 비동기로 처리하고 있습니다.ElasticSearch : 수집된 로그들을 실시간으로 확인하기 위해서 사용되며 Kibana를 이용해 시각화하고 있습니다.Angular.js : CMS의 프론트엔드는 Angular.js + Bootstrap을 이용해 개발되었으며, Bower를 이용한 라이브러리 관리, Grunt를 이용한 빌드 관리를 하고 있습니다.소프트웨어 개발/운영GIT : 소스코드는 git로 관리하며 Git-Flow를 이용한 브랜치 정책을 수립하여 가져가고 있고 저장소로는 깃허브를 이용합니다.Quality Practice : QA단계에서 제품을 테스트하기 전 개발자들은 QA 프로세스에 맞게 다음 3가지 기준으로 소스 코드의 품질을 관리합니다.코딩 컨벤션 : 사운들리 내부 코딩 컨벤션에 맞게 개발되었는지 확인합니다. Checkstyle의 규칙을 정의 및 자동화합니다.테스트 코드 : 단위 테스트 코드를 작성하며 테스트 결과는 모두 통과되어야 합니다.테스트 커버리지 : 단위 테스트 코드가 작성된 커버리지를 계산하며 현재 60%를 목표로 진행하고 있습니다.젠킨스 : 소스코드 저장소에 변동이 일어나면 젠킨스가 소스코드를 빌드하고 위에서 언급한 세가지에 대한 리포트를 작성합니다.소나큐브 : 무료 오픈소스로 코드 정적 분석을 해주며 및 QA 리포트를 같이 볼 수 있습니다.슬랙 : 인력이 적은 저희 팀도 슬랙을 적극적으로 개발/운영에서 사용하고 있습니다.팀 커뮤니케이션 : 팀원들 간의 의사사통을 위한 주요 수단으로 모든 팀원이 함께 사용하고 있습니다.분석 리포트 : 젠킨스나 배치를 통해 분석된 데이터들은 분석이 끝난 지표들은 슬랙으로 결과를 전송하여 모든 팀원이 볼 수 있도록 공유하고 있습니다.서버 모니터링 : 서버들의 이상 징후 감지나 배치 오류등을 슬랙을 통해 담당자에게 전송하여 조치할 수 있도록 합니다.애플리케이션 및 서버 모니터링 : 애플리케이션의 모니터링은 Naver에서 오픈소스로 공개한 핀포인트를 사용하고 있고, 서버 상태 모니터링을 위해 자체 개발한 모니터링 시스템을 사용하고 있습니다. 모니터링 데이터 수집을 하는 에이전트와 전체 시스템의 데이터를 관장 하는 서버간에는 gRPC를 이용하여 상태 체크를 합니다. 서버의 상태에 문제가 있을 때에는 slack을 통해 담당자들에게 알람을 주도록 시스템 설계를 하였습니다.개발 문화개발자들은 각각 개발을 할때 정해진 정책에 맞춰 브랜치를 만들어 개발합니다.각각 개발된 소스들은 저장소인 깃허브에 푸시된 후 깃허브의 댓글 기능을 이용하거나 오프라인을 통해 코드 리뷰를 진행합니다.리뷰가 끝난 후 합쳐진 소스는 QP 활동을 통해 분석이 됩니다.빌드가 실패할 경우 커피를 사야합니다 ^^ (커피를 얻어 먹으려는 것이 아닌 소스코드를 푸시하기 전 잘 확인하자는 취지입니다) AWSEC2 : 사운들리의 대부분의 구성 요소인 API서버와 로그 수집, 분석 서버, 엘라스틱서치, 플럼, CMS등이 모두 EC2에 구축되어 있습니다.RDS : 컨텐츠의 주 저장소로 데이터베이스 관리의 용이성을 고려하여 RDS의 Multi-AZ에 배포하여 Active-Standby로 구성되어 있으며 이 데이터들은 레디스와 로컬 캐시를 이용하여 API서버에서 활용하고 있습니다.S3 : 컨텐츠에 포함된 각종 정적 데이터들이 저장되며 수집된 로그들도 저장하여 보관됩니다. EMR : 로그 수집서버를 통해 S3에 저장된 로그들은 EMR을 이용해서 분석됩니다.Beanstalk : 개발 서버의 배포에 사용됩니다. 최근 IntelliJ의 플러그인이 업데이트 되면서 IntelliJ 15버전을 지원하게 되므로써 로컬에서 개발하고 개발 서버에 배포까지 편리하게 하고 있습니다. VPC : 인터넷이 필요 없는 서버들은 VPC 내부 private-zone에 배포 및 ELB를 통해 외부에서 접근하도록 구성되어 있습니다.<그림 2. AWS 배포 구성도>이상으로 사운들리에서 사용하고 있는 백엔드 소프트웨어들을 소개해 보았습니다. 적은 인력으로 빠르게 사업을 진행하는 스타트업에서는 비즈니스에 집중할 수 있도록 도와주는 다양한 툴이나 오픈소스를 이용하여 많은 도움을 받을 수 있는 것 같습니다. 또한 코드를 잘 작성하여 에러를 줄이는 것도 필요하지만 여유가 많지 않으면 최소한 제품의 에러에 빠르게 대응할 수 있도록 하는 방법도 필요한 것 같습니다.#사운들리 #개발 #개발자 #문제해결 #프레임워크 #스킬스택 #스택 #인사이트
조회수 1185

OLTP에 대하여

Overview우리는 대부분의 활동을 스마트폰 하나로 해결할 수 있습니다. 은행에 가지 않아도 앱만 있으면 은행 업무를 할 수 있고, 몇 번의 터치만으로 다양한 물건을 구매할 수 있습니다. 모든 것을 온라인에서 해결합니다. 이 말을 바꿔 말하면, ‘온라인에 연결되지 않았다는 건 대부분의 경제활동에서 벗어났다’는 의미이기도 합니다. 우리가 온라인에서 무언가를 클릭(또는 터치)한다는 건 서버에 호출하는 행위이기도 합니다. 서버가 실시간으로 원하는 결과를 우리에게 다시 보내주는 것이죠. 이렇듯 많은 사람들에게 실시간으로 서버가 자료를 처리하는 과정을 OLPT(Online transaction processing)라고 합니다. Table의 구조OLTP 처리를 하려면 DB를 어떻게 설계해야 할까요. 예를 들어봅시다. 모든 웹사이트는 서비스를 이용하려면 우선 회원가입을 해야 합니다. 가입을 할 때는 ID 와 비밀번호를 꼭 만들어야 하고요. 이것을 DB Table로 가정하면 회원 Table은 ID와 암호 컬럼으로 구성될 겁니다. 회원ID암호위의 Table은 가입자 수가 많아지면 운영을 하고 건수가 많아지면 두 가지 문제가 발생합니다. 첫 번째는 ID가 중복된다는 것, 그리고 두 번째는 자료가 많아질수록 가입된 회원의 ID를 가져오는 게 느려진다는 것이죠. 전자의 문제는 Application 단에서 어느 정도 확인할 수는 있지만 다중 사용자 구조에서 중복되지 않는다는 보장을 할 수 없습니다. 후자의 문제는 Table만으로는 해결되지 않습니다. 이를 해결하려면 Index(Primary Key)를 생성해 해결할 수 있습니다. Index 생성으로 문제를 해결할 수 있다는 게 생소할지도 모릅니다. 우선 Index의 기본적인 구조를 알아야 합니다. 보통 Table에 자료를 Insert하면 입력한 순서대로 자료가 쌓입니다. 회원입력순서ID암호1홍길동12342강감찬56783이순신abcd4김좌진efgh하지만 Oracle Cluster Table과 MySQL InnoDB Table은 Table에는 입력한 순서대로 쌓이지 않고, 특정 KEY에 따라 쌓입니다. 그러므로 모든 테이블이 꼭 위의 예시처럼 순서대로 쌓이진 않습니다. Oracle Cluster Table과 MySQL InnoDB Table은 아래 예시처럼 보여집니다.회원입력순서ID암호2강감찬56784김좌진efgh3이순신abcd1홍길동1234이번에는 Index에서 가장 많이 사용하는 BTree Index를 살펴보겠습니다. Index는 보통 테이블의 자료를 빠르게 검색하기 위해 생성합니다. 1개의 Table 위에 N개의 Index를 생성할 수 있습니다.1) 회원 테이블의 ID를 KEY로 하는 Index를 생성한다고 가정하면 아래와 같은 Index 구조를 가집니다.회원_ID_IndexID(KEY)Table 위치 값강감찬XXX김좌진XXX이순신XXX홍길동XXXIndex는 KEY의 순서(오름차순 or 내림자순)로 정렬되어 있습니다. 그러므로 N개의 KEY를 지정해 Inedx를 생성하면 N개의 KEY 순서대로 정렬됩니다. 그렇다면 BTree Index는 왜 정렬되어 있을까요? 자료를 찾는 속도가 빠른 것과는 어떤 관계가 있을까요?자료 구조를 조금이라도 공부했다면 이미 BTree라는 이름에서 눈치채셨을 겁니다. Btree Index는 이진검색(Binary Search)에 기반을 두고 있습니다. Binary Search는 자료가 정렬되어 있는 상태에서 자료의 절반 위치를 찾아가는 구조입니다. (처음 전체의 절반, 절반의 절반 , 그 절반의 절반) 전체를 읽을 때보다 빠르게 원하는 값을 찾을 수 있고, 자료를 읽어내는 속도도 빨라집니다. 이렇게 해서 Index가 생성되어 있다면 Index에서 값을 빠르게 찾을 수 있고, 이 값이 위치한 Table의 레코드를 바로 접근해 원하는 값을 가져올 수 있게 됩니다. Index에서 원하는 값을 빠르게 찾을 수 있기 때문에 Index를 생성할 때 속성(UNIQUE or NON UNIQUE)을 설정해 중복 허용 여부를 지정할 수 있습니다. Index와 Table관계를 표시하면 아래와 같습니다.회원_ID_IndexID(KEY)Table 위치 값강감찬2김좌진4이순신3홍길동1▼회원입력순서ID암호1홍길동12342강감찬56783이순신abcd4김좌진efghPrimary Key만약 ID의 컬럼 속성을 NOT NULL로 설정하면 중복이 되지 않고 값을 항상 입력합니다. ID의 무결정을 보장하고, 자료도 빠르게 찾을 수 있게 되는데요. 방법은 크게 두 가지로 설정할 수 있습니다. 하나는 Unique Index 와 NOT NULL을 사용하는 것이고, 다른 하나는 Primary Key를 지정하는 것입니다.2) 그렇다면 우리는 어떤 것을 지정하는 것이 좋을까요? 사실 DB 특성과 Table특성, 용도에 따라 달라지기 때문에 정답은 없지만 일반적으로 Primary Key를 지정합니다. Primary Key를 지정하는 건 몇 가지 이유가 있습니다. 첫째, 논리적으로 Primary Key를 지정해 Table의 기준을 알 수 있습니다. 둘째, 거의 모든 DB가 같은 조건(Index가 여러 개 있을 경우)이라면 Primary Key를 우선적으로 사용합니다. 마지막으로, 특정 DB는 Table(MySQL InnoDB Table)이 Primary Key로 정렬되고, 이것이 위치 값으로 사용되면 다른 Index를 쓰는 것보다 속도가 빠릅니다. 그러므로 가능한 Primary Key를 사용하는 것이 좋고, 그 외의 경우엔 Index를 사용하면 됩니다. Conclusion지금까지 OLTP 처리를 할 때의 기본적인 회원 Table 구조와 문제점 및 해결 방안 , 간단한 Index 및 Primary Key를 알아봤습니다. 다음 글에서는 조금 더 확장된 개념인 단일 Table을 Select하는 법을 다뤄보겠습니다. 뭐든 기초가 중요하니까요. 하하.. 참고 1) Oracle Bitmap Index의 경우 2개 테이블을 연결하여 1개의 Index를 생성할 수도 있습니다. 2) Primary Key는 NOT NULL컬럼만 지정 가능합니다. 글한석종 부장 | R&D 데이터팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 2177

스켈티인터뷰 / 스켈터랩스의 N잡러 엄단희 님을 만나보세요:)

Editor. 스켈터랩스에서는 배경이 모두 다른 다양한 멤버들이 함께 모여 최고의 머신 인텔리전스 개발을 향해 힘껏 나아가고 있습니다. 스켈터랩스의 식구들, Skeltie를 소개하는 시간을 통해 우리의 일상과 혁신을 만들어가는 과정을 들어보세요! 스켈터랩스의 N잡러 엄단희 님을 만나보세요:)사진1. 스켈터랩스의 N잡러 엄단희 님Q. 자기소개를 부탁한다.A. 스켈터랩스에 입사한 지 이제 8개월 정도 된 신입 소프트웨어 엔지니어, 엄단희다.Q. 스켈터랩스에서 어떤 업무를 맡고 있는가.A. 현재는 아이리스(Iris) 팀에 소속되어있다. 아이리스 팀은 맥락 인식(Context Recognition) 기술을 기반으로 SDK를 비롯한 여러가지 서비스 출시를 준비하고 있는데, 사실 지금은 레고(L.ego)팀이 준비하는 신제품인 스마트 미러 샘(Samm) 개발 업무가 주요 업무이다. 샘은 스켈터랩스가 가지고 있는 맥락 인식 기술 뿐만 아니라 음성, 얼굴, 제스처 인식을 비롯한 대화형 엔진이 모두 집약된 인텔리전트 디바이스(Intelligent Device)다. 여러 기능이 하나의 디바이스에 구현된 만큼, 샘은 다양한 모듈로 나누어져있다. 예를 들어 센서 정보를 모으는 모듈과 그 정보를 처리하는 모듈, 처리한 내용을 보여주는 UI 모듈 등이 있는데, 나는 이러한 모듈들을 gRPC 또는 bluetooth 등을 통해 서로 통신할 수 있도록 해주는 작업을 주로 진행했다. 최근에는 샘의 구매자에게 필요한 샘 어플리케이션 개발을 진행하고 있다. 아이리스 팀 관련해서는 파이어베이스(Firebase) 관련 작업을 서포트한 적이 있고, 얼마 전에는 스켈터랩스 웹사이트 개발에 참여하기도 했다.Q. 맡고 있는 업무의 가짓수가 많아 보인다. 한번에 여러 개의 프로젝트를 진행하는 것이 어렵진 않나.A. 쉽다고 말하기는 힘든 것 같다. 여러 업무에서 동일한 지식이 요구될 때도 있지만, 기본적으로 하나의 일을 처리하기 위해 집중하고 있다가 다른 업무로 전환할 때, 그 업무를 위한 나의 베이스를  바꾸는 등의 일들이 녹록치 않다. 처음에는 무엇보다 일의 우선순위를 정하는 것이 가장 버거웠다. 사실 업무마다의 기한이 정해져 있으면 당연히 급한 업무를 먼저 처리할텐데, 우리 회사는 그보다는 본인이 직접 업무량을 조정해서 기한을 정하고 처리하는 편이다. 그래서 하나의 일을 쪼개고 쪼개어, 그 중에서도 가장 빨리할 수 있는 일부터 먼저 처리하는 나만의 업무 프로세스를 만들고 있다. ‘빨리 할 수 있는 일'이라고 해서 마냥 쉬운 일을 말하지는 않는다. 그 때마다 내게 가장 맞는 일, 내가 가장 준비되어 있는 일을 자연스럽게 추려내어 업무 효율을 높이려고 한다.Q. 스켈터랩스에 어떻게 입사하게 되었는지 궁금하다.A. 재작년, 앤드비욘드라는 회사에서 인턴으로 근무했다. 당시 스켈터랩스가 앤드비욘드와 함께 개발중이던 스마트 포스(POS)기, GABE 프로젝트를 진행하며 한남동에서 같은 사무실을 쓰고 있었다. 그 프로젝트 팀에서 파견직처럼 일을 하게 되었는데, 가장 놀란 점은 ‘사람'이었다. 이렇게 누구 하나 빠짐 없이 개발을 잘하는 사람들이 모여있는 곳에서 개발하는 것은 처음이었다. 학교에서는 나름 ‘나도 잘하는 편이지 않을까’ 생각했는데 여기 와서 한없이 부족하다는 걸 깨달았다. 그런데 그렇게 부족한 신입 인턴임에도 불구하고 모두가 나를 평등하게 대해주셨고 개발 관련해서도 많이 배울 수 있었다. 덕분에 스켈터랩스는 내게 아주 좋은 이미지로 남아있었는데, 작년 스켈터랩스의 CTO인 조성진님께 오퍼를 받아서 스켈터랩스 인턴으로 일과 학업을 병행하다가 올해 정직원으로 입사하였다.Q. 인턴으로 일을 하며 학업과 병행했는지 몰랐다. A. 학교 스케줄을 우선시할 수 있도록 회사가 많이 배려해주었다. 다행히 학교가 회사와 멀지 않은 거리에 위치하기도 한다. 그래서 학교 수업은 주 2-3일 정도, 오전 타임으로 몰아서 구성했다. 시험기간이라고 하면 팀원들이 모두 나서서 ‘어서 집에 가서 공부부터 해라'라며 조언해주시고 업무적으로도 많이 도와주신 덕에 학업에 대한 지장 없이 일을 할 수 있었다.Q. 인턴을 마치고 정직원으로 입사했다면, 인턴 시절과 현재를 비교할 때 업무적으로 무엇이 가장 다른가.A. 우리 회사는 매 분기마다 분기의 목표 설정과 유사한 OKR(Objectives and Key Results)을 정하고, 이를 완료하는 방식으로 일을 진행한다. OKR에서 중요도가 높은 업무는 P0로, 가장 중요도가 낮은 업무는 P2로 표기한다. 인턴으로 처음 입사했을 때는 P1~P2 레벨의 자잘한 이슈들을 처리하는 업무가 많았다. 정직원이 되고 나니, 그만큼의 지식과 스킬이 쌓인 만큼 P0의 업무들을 조금 더 맡게되었다. 그러나 전반적인 업무의 결은 유사하다. 다만 확실히 책임감은 늘어났다고 생각한다. 인턴일 때는 ‘난 인턴이니까 몰라도 괜찮겠지?’와 같은 마인드가 있었는데, 정직원이 된 지금은 ‘정직원이 이 정도는 알고 있어야겠지?'라고 생각한다. 덕분에 공부하는 양도 이전보다는 늘어났다.사진2. 파워 코딩 중인 단희 님Q. 최근 스켈터랩스가 여러 학교의 커리어페어에 다녀오면서 많이 들었던 질문 중 하나가 ‘인공지능을 전문적으로 공부하지 않았는데, 일을 할 수 있을까요?’였다. 혹시 이 질문에 대한 답변을 해줄 수 있을까.A. 나도 입사 때 면접을 보며 같은 질문을 던졌다. 입사해서 느끼는 점은 정말 인공지능에 관련된 개발 외에 다른 영역에서도 개발해야 하는 일이 정말 많다는 점이다. 때문에 인공지능 분야를 잘 모른다고 해서 (물론 알면 좋지만) 막연한 두려움은 갖지 않아도 좋다. 물론 좀 더 코어한 부분을 개발할수록 인공지능 공부의 필요성을 점점 느끼게 된다. 이러한 기술적 갈증은 사내에서 열리는 테크톡(Tech Talk)과 같은 세미나를 통해 어느 정도 해결할 수 있으며, 업무를 위해 관련 공부가 필수적이라면 팀별로 스터디가 진행되기도 한다. 실제로 다른 팀에서는 주기적으로 관련 논문을 스터디하고 그 지식을 공유하는 세션이 진행되고 있다.Q. 스켈터랩스 입사 후 가장 뿌듯했던 순간과 힘든 순간을 꼽는다면?A. 나는 내가 무언가를 직접 만들고, 그 결과물을 선보이는 과정을 좋아한다. 그래서 가장 뿌듯한 순간으로는 회사 웹사이트를 런칭했을 때를 꼽고 싶다. ‘웹' 특성 상 내가 짠 코드들의 결과를 바로 눈으로 확인할 수 있기 때문에 개발하는 재미도 있었고, 아무래도 회사를 대표하는 사이트라 많은 사람들에게 보여질 것이라 생각하니 더욱 자부심을 갖고 일할 수 있었던 것 같다. 그런 측면에서 나중에 샘을 런칭하게 될 날도 기대된다. 반면 가장 힘들었던 순간은 작년 블루투스 개발 관련 디자인 문서 작업을 진행할 때 였다. 일단 블루투스 기술도 잘 모르는 데다가 디자인 문서 자체도 제대로 써본 적이 없어 생소했다. 사실, 개발이 안 풀리고 막혀있을 때는 그 순간만 힘들 뿐 어떻게든 해결책을 찾고 결과물을 낼 수 있었다. 그런데 디자인 문서 작업은 내가 어떤 방향성을 취해야 하는지, 지금 하고 있는 과정이 맞는 것인지가 계속 의구심이 들었다. 하루종일 컴퓨터 앞에 앉아 있어도 결과물이 없으니 마음만 조급해지는 일도 많았다. 다행히 당시 리뷰를 해주신 조성진님 등 기타 다른 개발자분들의 도움으로 문서는 마무리지을 수 있었는데, 내 한계에 대해 반성하기도, 많이 배우기도 했다.Q. 스켈터랩스 게임동호회 회장을 맡은 것으로 알고있다. 게임동호회를 소개하자면?A. 먼저 오류부터 수정해야할 것 같다. 나는 현재 게임동호회 회장은 아니다. 사내 게임동호회인 ‘Game of Troll’은 한달에 한 두번 모여서 게임을 함께 하는데, 그 게임에서 꼴찌를 한 사람이 회장이 된다. 나의 경우 저번 달 클래시로얄 게임에서 꼴찌를 하여 회장을 맡았었다. 하지만 회장이 정한 게임으로 다음 회장을 뽑기 때문에 내가 자신있는 게임인 오버워치를 9월 게임으로 선정했고, 현재는 정태형 님에게 회장 자리를 넘겨주었다. 게임은 종류에 따라 사내 블루룸 또는 PC방에서 진행한다. 블루룸에는 플스와 닌텐도 등의 각종 게임기가 완비되어 있어, 토너먼트 식으로 철권을 하거나 마리오카트를 했었다. 또 휴대폰으로는 클래시 로얄을 함께 플레이하기도 한다. PC게임인 경우에는 저녁에 함께 피씨방에 가는데, 재미있는 점은 원래 저녁을 먹고 피씨방에 가다가, 피씨방에 가서 저녁을 먹는 걸로 바뀌었다는 점이다. 저녁먹는 시간이 아까워서다. 이렇게 피씨방에서 플레이한 게임들은 스타1, 스타2, 카운터 스트라이크 온라인2, 오버워치, 히어로즈 오브 스톰 등이 있다.처음 게임 동호회에 들어올 때만 해도 ‘같은 회사 사람끼리 게임을 하는 것이 과연 재미있을까'란 생각을 했다. 그런데 막상 게임을 같이 해보니, 회사에서 일할 때는 보이지 않았던 그 사람의 의외의 면을 발견하는 재미도 있는 것 같다. 개인적으로는 초등학교 때부터 게임을 워낙 많이 했던 탓에 스스로 ‘내 인생을 게임에 너무 낭비한 것이 아닐까'란 자괴감을 느낀적도 있는데, 다른 훌륭한 개발자의 게임 덕후스러운 면모를 보면서 ‘나만 이렇게 게임에 빠진 것은 아니었구나'하는 위안도 받을 수 있었다.사진3. 스켈터랩스의 게임동호회 Game of Troll의 뒷풀이 모습Q. 와우, 플레이하는 게임이 정말 많다. 단희님이 가장 좋아하는 게임을 그 중 꼽는다면?A. 나는 단연 오버워치다. FPS게임을 선호하는 편인데 그 중에서도 오버워치를 주로 플레이한다. 개인적으로 스토리가 재미있기도 하고, 팀플레이를 진행하며 합을 맞춰가는 맛이 있다. 무엇보다 사람끼리 대결하는 PVP로 진행을 하면 정말 짜릿함이나 즐거움이 배가 되는 것 같다. 물론 협동 게임인 만큼 팀플레이가 제대로 되지 않는다거나 비매너 유저들과 붙을 때는 기분이 아주 다운되는 경우도 있지만 말이다. 그럴 때는 ‘GTA5’ 또는 최근에 시작한 ‘데스티니 가디언즈'에서 PVE를 하며 마음을 진정시킨다. 물론 이것만 하면 지루하겠지만 오버워치와 적절히 번갈아가면서 하다보면 고유의 재미가 느껴진다.Q. SNS에 웹툰도 연재하고 있는 것으로 알고있다. 어떻게 웹툰 연재를 시작하게 되었는지.A. 어렸을 때부터 만화 그리는 것을 좋아했다. 내가 상상하는 이야기들을 만화로 풀어내는 것도, 그 날 있었던 일을 재미있게 연출해서 일기 대신 그림으로 하루의 기록을 남기는 것도 좋아했다. 그렇게 학교에서 있었던 재미있는 에피소드를 글과 그림으로 남기다 보니, 이걸 모두에게 공개하면 재미있지 않을까란 생각을 했다. 때마침 한창 페이스북 페이지가 유행이었는데, 그때부터 노트에 끄적거린 짧은 만화들을 올리기 시작하면서 현재의 인스타그램 웹툰까지 오게되었다.   사진3. 단희 님이 연재 중인 <초코롤의 코딩일기>, 인스타그램과 페이스북에서 만나볼 수 있다Q. 웹툰 소개를 부탁한다.A. 인스타그램에선 @sw_chocoroll, 페이스북에서는 <초코롤의 코딩일기>라는 제목으로 게재하고 있다. 취미생활 겸 하다 보니 정기연재는 아니다. 제목에서 드러나 듯 주로 코딩(개발) 이야기를 다루고 있는 생활툰이다. 생활툰의 특성상 어쩔 수 없이 주변인들에 대한 묘사가 많고, 에피소드가 없을 때면 웹툰을 그리기도 쉽지 않다. 약간 과장하더라도 실제 있었던 일들을 중심으로 작업하기 때문에, 업로드 전 꼭 등장 인물들에게 검수를 거치기도 한다. 웹툰 그리는 것이 생각보다 집중도를 요하는 작업인지라 보통 주말에 진행하는데, 그래서 평일에는 에피소드를 꼼꼼히 기록해두는 습관이 생겼다. 무엇보다 웹툰을 그리며 가장 많이 느꼈던 점은 내 인생에 대해서 조금 더 알게됐달까, ‘나’를 다시 보게 된 느낌이 있다. 내가 기록한 에피소드가 대부분 게임과 개발에 편중되어 있는 점을 보면서, 인생에서 많은 지분을 ‘게임', ‘개발' 이 두 가지에 할애하고 있다는 것을 새삼 알게되었다.  Q. 재능 부자, N잡러로 보인다. 게임에도 웹툰에도 이렇게 관심이 많았는데 어떻게 개발자의 진로를 선택하게 되었나.A. 이유는 생각보다 단순하다. 위에 웹툰에서도 그렸듯이 영화를 보면 꼭 대형 모니터를 여러 개 띄워놓고 멋지게 주인공을 돕는 해커들이 등장하지 않나. 게임을 많이 하게 되면서 자연스럽게 컴퓨터에 친숙해지기도 했고, 영화를 보면서 ‘나도 컴퓨터 관련 전공을 택하면 저렇게 멋있는 사람이 될 수 있지 않을까’라고 생각했던 것 같다. 다만 어떤 분야의 개발자가 될 것인가에 대한 고민은 많았다. 영화처럼 정보보안 쪽도 잠깐 발을 담갔지만 지금 당장 할 수 있는 분야는 아니라는 생각을 했고, 그 다음은 게임 개발자에 관심을 가졌다. 그런데 당장 게임 회사에 들어간다고 해도 꼭 내가 만들고 싶은 게임만 만들 수는 없다는 것을 알게 되서 보류했다. 나는 일단 스토리가 탄탄하고 재미있는 게임을 정말 사랑한다. 예를 들어 <화이트데이>라는 공포 게임을 정말 재밌게 플레이했었는데, 공포 요소도 한국 정서에 맞게 잘 구현되었으면서 미연시(미소녀 연애 시뮬레이션) 요소도 가미되어 신선한 느낌을 주었었다. 제일 중요한 스토리도 배경 시나리오부터 인게임 진행까지 반전에 반전을 거듭하며 게임이 끝나고도 생각해볼 여지가 많았다. 이런 게임을 만들고 싶지만 우선 희망 사항으로 남겨둔 상태이다. 그러다 우연히 입사한 스켈터랩스에서 훌륭한 선배 개발자들을 보며 ‘개발' 자체의 즐거움을 느꼈고 당장은 어떤 특정 분야에 국한하지 않고 순수한 개발 능력을 향상시키기 위해 노력하고 있다. 스켈터랩스에서 중요하게 다루는 인공지능은 특히 기술적으로 미래의 변화를 주도하고 있기에, 여러 방면에서 매우 배울 점도 많고 발전할 수 있는 것 같다.Q. 최근 몰두하고 있는 것이 있다면?A. 여전히 웹툰과 게임이다. 웹툰을 그릴 수록 기초적인 그림 실력이 부족하다는 것을 많이 느꼈다. 전문적으로 그림을 배워볼까 싶다. 그리고 유튜브에 게임 채널을 열어보려고 한다. 내가 관심있었던 모든 일은 기본적으로 ‘창작'과 ‘기록'의 맥락을 가지고 있다. 개발 또한 어떻게 보면 내가 짜는 코드를 통해 하나의 프로그램을 만들어 내는 역할이지 않나. 웹툰도 내 일상에 대한 기록이다. 나의 일상에서 가장 큰 관심사 중 하나인 게임을 기록하는 방법에 대해 고민했는데, 역시 동영상이 최고라는 결론에 도달했다. 유튜브에는 게임 영상을 편집하여 조금씩 선보이고 있다.Q. 진부할 수 있지만, 이 인터뷰의 마지막 질문이다. 개인적인 꿈을 얘기해줄 수 있나.A. 언젠가는 접어두었던 게임 개발자의 꿈을 꾸려고 한다. 1인 개발자로서 스토리와 작화, 개발을 모두 맡은 개발자 말이다. 그러기 위해서는 그림 뿐만 아니라, 유저의 마음을 사로잡을 수 있는 스토리와 촘촘한 개발력 또한 갖추어야 한다. 개발력은 일단 스켈터랩스에서 빵빵하게 키워놓고, 스토리와 작화에 관련된 역량을 조금씩 갖추어간다면 1인 개발자로서 내 이름을 건 게임을 출시할 날이 조만간 올 수 있지 않을까.#스켈터랩스 #사무실풍경 #업무환경 #사내복지 #기업문화 #팀원인터뷰 #팀원소개 #팀원자랑
조회수 1285

jekyll의 메커니즘을 이해하고 커스터마이징하기

편집자 주-PHP 기반의 서비스를 기준으로 설명했다.-서버의 프로그램은 ‘서버 스크립트’로 표기했다.-HTML/html: 약어로 사용할 경우엔 대문자, 파일명으로 사용할 경우엔 소문자로 표기했다.목차jekyll이 어렵게 느껴지는 이유 jekyll은 모든 화면을 미리 만들어둔다.서버 스크립트 없이 검색 기능을 어떻게 만들까?이미지 캡션 추가이미지 사이즈 대응부록: 글 반영 과정, 도메인 연결 방법, 추가 옵션에 대하여Overview기술 블로그인 브랜디 랩스를 관리하기에 jekyll은 안성맞춤인 도구입니다. 1년 넘게 탈 없이 잘 사용하고 있죠. 물론 커스터마이징을 하려면 고생이 이만저만이 아닙니다. 그 과정은 jekyll을 이용한 Github 블로그 만들기에도 잘 나와있습니다. 도대체, jekyll은 왜 이리도 어려운 걸까요? 브랜디 랩스를 사례로 설명하겠습니다.jekyll이 어렵게 느껴지는 이유일반적인 웹서비스는 정적 리소스와 동적 스크립트의 조합으로 이뤄집니다. 예를 들어 PHP 서비스에서는 정적인 부분을 아파치 웹서버로, 동적인 부분을 PHP 스크립트로 작동합니다.하나의 게시글이 생기면 PHP 스크립트가 데이터베이스에 row 생성을 요청합니다. 게시글 등록 요청을 마치고, 글 목록 화면 요청을 한다면 데이터베이스에서 등록된 글목록을 정리해 HTML 양식으로 응답값을 만들어줄 것입니다.PHP 기반의 블로그 프로그램하지만 jekyll은 컨셉부터 다릅니다. 아주 생소한 메커니즘을 갖고 있습니다. 파일 기반의 데이터를 정적인 리소스로 빌드해서 서비스하죠. 게시글마다 md 파일이나 html 파일을 생성합니다. 글을 작성하고 배포하기 위한 빌드를 진행하면 응답할 html 화면을 만들고, 파일로 저장해 준비합니다. 이 상태에서 유저가 특정 화면을 요청하면 미리 생성한 html 파일을 찾아 꺼내주기만 하면 되죠. 다시 말해, 데이터베이스를 조회하고 HTML 양식으로 응답값을 만드는 과정이 생략되는 것입니다.실제로 Github page가 아파치 서버를 쓰는지는 알 수 없지만 개념 설명을 위해 동일하게 그렸다.jekyll은 모든 화면을 미리 만들어둔다.jekyll은 유저가 요청할 수 있는 모든 화면을 미리 빌드하는 방식을 씁니다. 앞서 다뤘던 브랜디 랩스의 gnav 영역의 회사소개, 채용 화면도 미리 빌드해둬야 합니다. 저자를 소개하는 프로필 페이지도 마찬가지죠. 글이 많아지면서 점점 길어지는 글 목록 화면도 예외는 아닙니다. 글 목록을 보여주는 화면이 많아지만 페이지 수만큼 미리 만들어야 합니다.위의 이미지는 jekyll이 동작하는 메커니즘을 간단히 정리한 것입니다. jekyll을 커스터마이징하려면 완전히 새로운 관점으로 접근해야 합니다. 지금부터는 브랜디 랩스의 검색 기능 구현 과정을 살펴보면서 커스터마이징을 자세히 알아보겠습니다.서버 스크립트 없이 검색 기능을 어떻게 만들까?검색을 하려면 작성된 모든 글의 제목과 내용에 원하는 키워드가 있는지 찾아야 합니다. 하지만 검색어는 변동값이므로 미리 빌드하는 방식으로는 커버할 수 없습니다. 검색어마다 화면을 미리 만들 수 없기 때문입니다.이럴 때는 클라이언트 스크립트는 활용해야 합니다. 서버 스크립트를 쓸 수 없기 때문에 어쩔 수 없는 선택이기도 합니다. 검색에 필요한 정보를 json 파일로 빌드시키고 자바 스크립트를 이용해서 검색하도록 했습니다.먼저 최상위 경로에 search.json을 만듭니다. 파일 시작점에 아래와 같은 패턴이 있다면 빌드 대상으로 인식됩니다.--- --- 이전에 쓴 jekyll 문서를 PDF로 배포하기에서 pdf.html 파일을 만들 때도 비슷한 방법을 사용했습니다.--- --- [ {% for post in site.posts %} { "title" : "{{ post.title | escape }}", "category" : "{{ post.category }}", "tags" : "{{ post.tags | join: ‘, ’ }}", "url" : "{{ site.baseurl }}{{ post.url }}", {% if post.author %}{% for author in site.data.authors %}{% if post.author == author.name %} "author" : "{{author.koname}}", "email" : "{{author.email}}", {% endif %}{% endfor %}{% endif %} "date" : "{{ post.date }}", "content" : "{{ post.content | strip_html | replace: "\", ‘’ | replace: ‘"’, ‘\"’ | replace: ' ‘,’ ' | replace: ' ‘, ’ ' }}" } {% unless forloop.last %},{% endunless %} {% endfor %} ] ▲서머리 데이터를 만드는 json 파일search.json은 모든 페이지의 제목과 내용을 정리해 json으로 만들어야 하기 때문에 site.posts변수를 이용해 만들었습니다. post내용에는 글의 저자, 작성일, 제목, 내용 등 필요한 정보가 있으니 출력하면 됩니다. json을 만드는 것이므로 내용에 “가 들어가면 안되 "으로 치환시켰습니다. 마지막으로 HTML 태그는 검색에 필요하지 않기 때문에 luqid strip_html 함수를 이용해 제거했습니다.http://labs.brandi.co.kr/search.json위의 URL을 클릭하면 브랜디 랩스에서 검색에 사용하는 json을 볼 수 있습니다. 빌드하면 search.json이 만들어지는 것을 확인할 수 있습니다. 이제 json을 로딩하고 해당 키워드를 가진 글을 찾아내기만 하면 됩니다. json 내에 제목과 내용에 입력한 키워드가 있을 때 아래와 같은 UI로 표현했습니다. 기능 구현은 Simple-Jekyll-Search를 이용했습니다. 1)이미지 캡션 추가블로그는 이미지를 많이 사용하고, 상황에 맞게 노출도 해야 합니다. 아래 이미지는 최종적으로 적용한 이미지와 캡션의 결과 화면입니다. {% include figure.html file="/assets/20190415/05.png" alt="05" caption="커스터마이징한 gnav 영역" width="fitcontent" border="true" %} 위와 같이 구성하려고 html과 css를 다음과 같이 구성했습니다. 커스터마이징한 Gnav영역 ▲캡션 html 소스figure { margin: 1em auto; } figcaption { text-align: center; font-weight: bold; color:#999; } ▲캡션에 관련된 css 소스이미지는 가운데 정렬했고, 캡션 텍스트도 옅은 회색으로 가운데 정렬했습니다. 하지만 편집을 담당하는 장근우 대리는 개발자가 아니므로 태그를 입력해달라고 하기엔 무리가 있었습니다. 좀 더 편리한 방식이 없을지 고민하다가 liquid 템플릿의 include 기능을 쓰면 되겠다는 생각이 들었죠. 아래는 브랜디 랩스 원고에 이미지를 넣을 때 쓰는 liquid 문법입니다.{% include figure.html file="/assets/easydebug/5.png" alt="07" caption="커스터마이징한 Gnav영역" %} liquid 템플릿 엔진에서 include할 때 추가 파라미터를 전달할 수 있습니다. file, alt, caption은 파라미터로 전달하고, include되는 파일에서 전달할 내용을 바탕으로 프로그램을 구현할 수 있습니다. {{include.caption}} ▲ /_includes/figure.html이미지 사이즈 대응작은 이미지를 확대하면 이렇게 된다.대부분은 이미지는 화면에 꽉 차지만, 어떤 이미지는 사이즈가 너무 작아 원래의 사이즈로 보여줘야 했습니다.{% include figure.html width="fitcontent" border="true" file="/assets/easydebug/5.png" alt="07" caption="커스터마이징한 Gnav영역" %} ▲사이즈와 외곽 테두리 선에 스펙을 추가했다.추가 전달 인자를 넣고, figure.html 파일에서도 사이즈 대응을 했습니다. {{include.caption}} ▲완성된 /_includes/figure.html 파일figure { margin: 1em auto; } figure.percent100 { width: 100%; } figure.percent90 { width: 90%;} figure.percent80 { width: 80%;} figure.percent70 { width: 70%;} figure.percent60 { width: 60%;} figure.percent50 { width: 50%;} figure.percent40 { width: 40%;} figure.percent30 { width: 30%;} figure.percent20 { width: 20%;} figure.percent15 { width: 15%;} figure.percent10 { width: 10%;} figure.percent5 { width: 5%;} figure.fitcontent { width: fit-content;} figcaption { text-align: center; font-weight: bold; color:#999; } ▲완성된 css이제 원하는 사이즈를 지정해 이미지 상황별 적절한 대응을 할 수 있게 되었습니다.Conclusionjekyll은 브랜디 랩스를 운영하기에 아주 유용한 도구입니다. 기본 템플릿도 훌륭하지만 상황과 편의에 맞게 변경하면 개성 있는 기술 블로그를 만들 수 있을 겁니다. 물론 커스터마이징이 어려울 수 있지만 jekyll의 메커니즘을 이해한다면 금방 적응할 수 있을 겁니다. 이제 블로그를 만들 모든 준비가 끝났습니다. 자, 도전해봅시다!부록1.글 반영 과정jekyll을 이용해서 글을 작성했나요? 이제 Github 저장소에 push하면 글이 반영될 겁니다. push하는 과정을 보면 빌드된 파일을 push하는 게 아니라, 원본에 해당하는 md파일 또는 html 파일을 push하는 걸 알 수 있습니다. push하면 Github page에 바로 반영되지 않고, 몇 분 정도 걸립니다. 이것을 통해 작성한 글이 저장소에 push되면 스케줄러나 트리거에 의해 빌드된다는 걸 유추할 수 있습니다. 아마도 빌드 결과를 위한 저장소가 따로 있고, 빌드된 결과가 저장되는 것이라 예상합니다.2.도메인 연결 방법jekyll 서비스에서는 구매한 도메인을 간편하게 연결할 수 있습니다. 프로젝트의 가장 위쪽에 CNAME 파일을 만들고 push하면 금방 적용됩니다.CNAME 파일3.추가 옵션에 대하여자료를 조사하던 중에 공식 사이트의 빌드 추가 옵션을 찾았지만 0.2초 정도로 큰 차이가 없었습니다. 만약 별도의 옵션이 없다면 빌드 결과는 _site 폴더로 모일 겁니다.공식 사이트 빌드 옵션옵션을 넣어 빌드옵션을 넣지 않고 빌드참고1) GitHub - christian-fei/Simple-Jekyll-Search: A JavaScript library to add search functionality to any Jekyll blog.글천보성 팀장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만
조회수 1677

개발자 직군 파헤치기 3 | 블록체인 개발자

이번 포스팅은 블록체인 개발자!2017년 대한민국은 가상화폐의 광풍에 휩싸이게 된다. 남녀노소 가릴 것 없이 많은 사람들이 가상화폐에 투자를 했다. 전 세계에 유례가 없을 정도로 국내 가상화폐 투자 열기는 뜨거웠으며 해외의 가상화폐 투자자들은 국내 투자자들의 움직임에 예의주시하면서 투자할 정도였다.이와 동시에 가상화폐의 기술적 원천이 된 블록체인(Block Chain) 기술에 대한 관심도 증가했다. 제2의 인터넷이 될 거라는 찬사를 받으면서 가상화폐의 부상은 뜨거운 관심의 대상이었다. 정부의 가상화폐 규제가 있었지만 시장에서는 블록체인 기술의 잠재적 가능성에 주목을 하면서 이것을 가지고 어떻게 혁신적인 서비스를 제공할지 고심하고 있다. 이에 따라 일반 개발자는 물론이고 기업에서도 블록체인 개발자에 대한 수요와 관심이 늘고 있다.이러한 관심 때문에 개발자를 꿈꾸는 많은 분들이 블록체인 개발자는 무엇을 하고 어떻게 될 수 있는 것인지 궁금해하고 있다. 그래서 이번 포스팅에서는 블록체인 개발자가 되기 위해서는 어떻게 해야 하는지와 더불어, 블록체인 기술에 대한 전망 그리고 블록체인 개발자의 시장 수요에 대한 글을 써 볼 것이다.*이 글은 블록체인 기술에 대한 자세한 설명은 하지 않고 있습니다.*Photo by Andre Francois on Unsplash미래를 여는 신기술, 블록체인의 전망블록체인 기술이 뜨고 있다고 하지만 도대체 어떤 분야에서 적용될 수 있기에 이렇게 많은 관심을 받고 있는 것일까? '딱 이 분야가 유망합니다'라고 말하기가 어려울 정도로 블록체인은 폭넓은 분야에 걸쳐서 파괴적인 혁신성을 가지고 있다. 그중  몇 가지만 추려서 이야기하고자 한다.1. 금융, 은행블록체인의 기술의 특징은 탈 중앙화, 신뢰성, 보안성이다. 전문가들은 기존의 은행들이 하던 업무를 핀테크 기업들이 혁신적인 서비스와 가격으로 대체할 것으로 예상한다. 블록체인 기술이 가지고 있는 신뢰성과 보안성으로 인해 일반 기업들도 거대 은행이 보유하고 있는 보안성을 획득할 수 있는 것이다. 거래 과정에서 제3자를 거치지 않기 때문에 거래의 속도와 효율성 그리고 경제성이 크게 증가한다.핀테크 스타트업뿐만 아니라 거대 금융권의 은행들이나 기존의 대기업들도 블록체인 기술을 이용한 서비스를 발 빠르게 준비하고 있다. 마스터카드는 블록체인 기술을 통해 즉석 지불 시스템을 개발하고 있다. 또한 글로벌 기업 IBM은 서로 다른 국가에 위치한 금융 기관이 블록체인 기술을 이용해서 결제를 처리할 수 있도록 하는 뱅킹 솔루션을 개발했다.현재 이더리움이 선도하는 스마트 계약도 금융권에서 주목하는 분야다. 스마트 계약은 특정 조건이 충족되면 은행과 같은 제3자를 거치지 않고 계약을 이행하게끔 도와준다. 예를 들어, 납품 기업과 발주 기업이 스마트 계약을 맺었다. 납품 기업이 발주 기업의 창고에 물품을 보내고 물품이 도착을 하면(IOT 센서로 감지된다) 납품 기업은 자동으로 결제 대금을 자동으로 받게 된다.2. 물류위에서 블록체인 기술의 특징 중 신뢰성과 보안성을 언급했다. 정보의 신뢰성과 보안성은 물류 시스템에도 파급력을 미칠 것이다. 많은 전문가들이 블록체인으로 인해 물류 시스템에 혁신적인 변화가 가능하다고 말한다. 블록체인 기술이 어떻게 물류 시스템에 적용될 수 있는지 직접 예시로 살펴보자.영국의 소프트웨어 회사 프로비넌스는 블록체인 기술을 이용해서 소매업자와 식당들이 원래 계약했던 대로 재료가 들어오는지 확인할 수 있게 만들었다. 트래킹(tracking) 기술과 결합하여 만든 이 소프트웨어는 재료가 수확되는 과정부터 최종 소비자가 구매하는 단계까지 꼼꼼히 추적하고 관리한다. 공급망의 모든 단계에서 변경 불가능한 데이터가 블록에 추가된다. 이 소프트웨어를 통해 소비자들은 자신이 무엇을 먹는지, 기업이 불법 조업을 통해 재료를 조달한 것이 아닌지, 이 농작물에 대한 적절한 보상이 농부에게 돌아갔는지, 이 식재료가 유기농이 맞는지 명확하게 확인할 수 있다.한편, 중국의 월마트와 IBM이 협력해 중국에서 유통되는 돼지고기의 유통 전 과정을 블록체인 기술을 활용해 추적을 하기도 했다. 월마트는 더 나아가 농산물 공급망의 모든 단계를 추적하기 위한 프로젝트를 IBM과 진행하고 있다. 또한 영국의 신생기업 에버레저는 블록체인 기술을 이용해 다이아몬드와 같은 고부가 가치 상품들의 원산지 추적과 인증을 IBM 블록체인 기술을 활용하고 있다.3. 디지털 콘텐츠블록체인 기술로 디지털 콘텐츠에 대한 지적 재산권 보호를 더 확실하게 할 수 있다. 기존에는 불법적인 경로로 승인되지 않은 디지털 콘텐츠들이 유포가 됐다. 하지만 블록체인 기술로 온라인 콘텐츠에 대한 저작권, 권한, 결제를 관리할 수 있도록 작업할 수 있다. 블록체인 기술을 통해 콘텐츠 원저자의 증명을 더 쉽게 하고 해당 콘텐츠에 누가 접근을 했는지 추적할 수 있다.또한 항상 이슈가 되어 왔던 뮤지션들의 음반 판매 금액에 대해서도 블록체인 기술이 혁신을 가져다줄 것이다. 기존의 중간 단계의 서비스를 없애고 비용을 절감해서 음반 제작자들에게 더 많은 몫이 돌아갈 수 있다. 위에서 언급한 스마트 계약을 통해 라이선스 이슈의 문제들도 해결할 수 있다.블록체인 개발자, 얼마나 핫해?블록체인 기술에 대한 뜨거운 관심에 힘입어 블록체인 개발자에 대한 수요도 크게 증가하고 있다. 아직은 국내에서 가상화폐에 대한 비즈니스가 주를 이루고 있지만 정부의 정책과 시장의 기대와 맞물려 그 수요는 더욱더 커질 예정이다. 여기 블록체인 개발자의 수요에 대한 기사 있다.기사에 따르면 지난 1분기 취업사이트 잡코리아에 블록체인 키워드로 등록된 채용공고는 총 1500여 건이다. 이는 전년 동기 대비 9배 이상 늘어난 수치라고 한다. 또 잡코리아가 분석한 결과 지난해 하반기부터 암호화폐 개발자 수요가 급증했다고 한다. 지난해 1분기와 비교하면 올해 1분기 등록건수는 9배 이상이며 직전 분기와 비교해도 3배에 달한다.다른 아웃소싱 플랫폼 사이트도 비슷하다. 4만 7천 명의 유저를 보유한 위시캣에 따르면 2014년 8월부터 지난달까지 등록된 암호화폐 관련 프로젝트는 108건이다. 그리고 이 중에 절반인 55번이 올해 1분기에 등록이 되었다고 한다. 개발자 커뮤니티 OKKY에 올라온 구인 글에는 월 급여 900만 원을 제시하고 암호화폐 개발하는 개발자를 찾고 있었다. 또 구인 사이트에 올라온 암호화폐 거래소 프로젝트 중 개발비용이 45일 동안 2억 5천만 원에 이른 사례도 있었다.기사에 나온 것처럼 블록체인 개발자의 수요는 암호화폐에 집중되어 있기는 하지만 빠르게 증가하고 있다. 또 많은 기업들이 암호화폐가 아닌 다른 비즈니스의 모델을 찾으면서 블록체인 서비스를 준비하고 있다. 블록체인 개발자는 말 그대로 요즘 가장 핫하다고 할 수 있다.그래서 블록체인 개발자 되기 위해서는?이렇게 핫한 블록체인 개발자가 되기 위해서는 어떤 기술들을 공부해야 할까? 물론 어떤 분야의 개발자가 된다는 것이 특정 기술을 익힌다고 되는 것은 아니다. 웹 개발자가 된다고 해서 자바스크립트와 HTML, CSS를 익힌다고 되는 것이 아닌 것처럼 말이다. 회사마다 진행하는 프로젝트가 있고 그것에 맞춘 기술 스택들을 익혀야 한다. 하지만, 그럼에도 블록체인 개발에 있어서 주류가 되는 기술들은 있다. 이것들을 공부해 가면서 블록체인 개발자를 준비한다면 한층 더 수월해질 것이다.1.솔리디티(Solidity)솔리디티는 블록체인 플랫폼에서 스마트 계약을 만들기 위한 프로그래밍 언어다. 이더리움의 핵심인 스마트 계약을 만들기 위해서는 솔리디티를 배워야 한다. 솔리디티는 EVM(Ethereum Virtual Machine)에서 돌아가도록 설계되었고, 자바스크립트와 비슷한 문법 구조를 갖고 있다. 자바스크립트를 알고 있다면 배우기가 훨씬 수월할 것이다.블록체인은 다양한 분야가 존재하지만 그중 스마트 계약은 가장 대표적인 혁신 기술이다. 그 스마트 계약 기술을 선도하는 이더리움 프로젝트를 다루기 위해서는 솔리디티를 배우는 것이 필수적이다. 블록체인 개발에도 다양한 기술 스택들이 있겠지만 주류 기술을 배운다고 하면 솔리디티를 배워야 한다.솔리디티를 배우기 위해서는 다양한 방법이 있겠지만, 유데미의 강좌를 들으면서 공부하는 것을 추천한다. 저렴한 가격에 퀄리티 높은 강좌를 들을 수 있다. 자세한 사항은 이곳을 참고하면 된다.2.하이퍼레저(Hyperledger)블록체인이 개념이 퍼블릭 네트워크 기반의 시스템이기는 하지만 누구나 데이터를 볼 수 있기 때문에 기밀문서 관리에는 사용되기 힘들다. 특히 금융권이나 기업 문서 같은 경우는 더더욱 그러하다. 그래서 폐쇄적인 프라이빗 네트워크 내에서 블록체인을 활용할 수 있는 기술이 필요하다.하이퍼레저는 이러한 프라이빗 블록체인을 필요로 하는 기업들의 연합체라고 볼 수 있다. 이 기업들은 컨소시엄을 맺고 프라이빗 블록체인을 더 발전시키려고 다양한 분야에서 협업하고 있다. 하이퍼레저 프로젝트는 리눅스 재단에서 주도하며 금융, 은행, 제조, 기술 등 다양한 분야에 관여한다. 그리고 이 프로젝트는 스마트 계약, 분산 합의 네트워크에 목표를 두고 있다.지금까지 에어버스, 엑센츄어, 바이두, IBM, J.P 모건, 히타치, 삼성 SDS 등 다양한 기업이 하이퍼레저에 포함되어 있다. 그만큼 많은 기업들이 하이퍼레저 프로젝트에 관심을 가지고 있다. 하이퍼레저 역시 유데미의 강좌를 통해서 배울 수 있다. 자세한 사항은 이곳을 참고하면 된다.블록체인 개발자의 첫걸음블록체인 개발자가 되기 위해서는 위의 기술 스택뿐만 아니라 기본적으로 백엔드에 대한 지식과 최소한 암호 기법에 대한 기본 지식이 있어야 한다. 또한, 블록체인의 많은 API 및 SDK가 자바스크립트와 nod.js로 이루어져 있다. 무엇부터 시작해야 할지 모르는 분이라면 자바스크립트부터 시작하면서 첫걸음을 떼는 것을 추천한다.블록체인은 IT 기술의 최전선에 있는 기술이다. 그렇기 때문에 산업 동향을 항상 예의주시하면서 빠르게 움직여야 한다. 기업이 원하는 블록체인 기술 스택을 빠르게 습득하고 그에 맞는 실력을 갖추는 것이 중요하다.지금까지 블록체인 개발자에 대해 알아보았다. 많은 내용들을 다루다 보니 각각에 내용들에 깊이 있게 다루지는 못했다. 그래도 이 글을 통해 블록체인 개발자가 되기 위한 가닥은 잡을 수 있었으면 좋겠다.
조회수 10245

파이썬의 시간대에 대해 알아보기(datetime.timezone)

안녕하세요. 스포카 크리에이터 김두리입니다.  스포카는 많은 프로덕트에서 국제화 서비스를 제공하고 있습니다. 그래서 시간대와 시간을 제대로 정확하게 처리하는 것은 중요합니다. 하지만 파이썬의 datetime.datetime은 날짜(datetime.date)와 시각(datetime.time)의 정보를 담고 있고, 시간대(datetime.timezone)의 정보는 담거나 담지 않을 수도 있으므로 헷갈리는 부분이 존재합니다.     시간을 처리할 때 시간대는 왜 중요할까요? 시간대가 명시되지 않은 시각은 충분한 정보를 내포하고 있지 않습니다. 저는 얼마 전, Google Calendar API를 이용하여 작업할 때 골치 아픈 일을 겪었습니다. 오늘의 일정을 불러오고 싶어서 오늘 0시~24시로 데이터를 요청했지만, 계속해서 결괏값에 다음 날의 일정도 포함되어서 반환되었습니다.   왜 다음날 일정도 포함되었던 걸까요? 아래와 같은 코드를 작성하여 Google Calendar API에 요청했습니다.   today = datetime.date.today() from_ = datetime.datetime(today.year, today.month, today.day, 0, 0, 0) to = datetime.datetime(today.year, today.month, today.day, 23, 59, 59) events = get_events_from_google_calendar(from_, to)   몇 시간 동안 머리를 싸매고 코드를 한 줄 한 줄 따져가며 고민을 했습니다. 결국, 제가 요청한 시각에 시간대가 지정되어 있지 않아 get_events_from_google_calendar() 함수 내부에서 from_과 to가 의도하지 않은 시간대의 시각으로 인식되어서 발생했던 문제라는 것을 알게 되었습니다.  # 원래 의도했던 시간대: 대한민국 시간대(KST)에서 오늘 0시 0분 0초 KST = datetime.timezone(datetime.timedelta(hours=9)) from1 = datetime.datetime(today.year, today.month, today.day, 0, 0, 0, tzinfo=KST) # get_events_from_google_calendar()가 받아들인 시간대: UTC 시간대에서 오늘 0시 0분 0초 from2 = datetime.datetime(today.year, today.month, today.day, 0, 0, 0, tzinfo=datetime.timezone.utc)   위 예제에서 from2 - from1를 하게 되면 timedelta(hours=9)가 계산됩니다. 우리가 원했던 것은 KST 기준 오늘 0시부터의 일정이었지만, Google Calendar API에서는 시간대를 UTC로 취급하여 KST 기준 오늘 9시부터 다음날 9시까지의 일정을 불러왔던 것입니다.  이렇듯 시간 관련 작업을 할 때 시간대에 대해 제대로 알고 있지 않으면 의도치 않게 많은 시간을 소모하게 될 수도 있습니다.  오늘은 제가 파이썬으로 시간대 관련 처리를 하며 모았던 정보를 정리하여 공유하고자 글을 작성하게 되었습니다.  시간대  나라 또는 지역마다 살아가는 시각이 다르기 때문에 시간대에 따른 편차가 존재합니다. 이 차이가 피부로 잘 와닿지 않은 채 살아가더라도 캘린더 API나 국제화 서비스 준비 등등 시간과 관련된 작업을 진행하다 보면 시간대 문제에 직면하게 됩니다.  시간대는 영국의 그리니치 천문대(본초 자오선, 경도 0도)를 기준으로 지역에 따른 시간의 차이, 다시 말해 지구의 자전에 따른 지역 사이에 생기는 낮과 밤의 차이를 인위적으로 조정하기 위해 고안된 시간의 구분 선을 일컫는다. 시간대는 협정 세계시(UTC)를 기준으로 한 상대적인 차이로 나타낸다.     UTC에 대한 더 자세한 내용은 여기를 참고해주세요.   시간대에 대한 더 자세한 내용은 여기를 참고해주세요.   파이썬의 datetime.datetime.now()는 실행 환경의 시간대에 따라서 시각을 표시합니다.  2019-01-01 00:00:00 +09:00에 시간대가 Asia/Seoul로 설정된 제 랩탑에서 현재 시각을 가지고 오면, 아래와 같은 시각이 표시됩니다.  >>> print(datetime.datetime.now()) 2019-01-01 00:00:00.000000   그런데, 같은 시각에 Asia/Taipei로 설정된 랩탑에서는 현재 시각이 아래와 같이 표시됩니다.  >>> print(datetime.datetime.now()) 2018-12-31 23:00:00.000000  위의 예제처럼 시간대에 따라 시각이 다를 수 있다는 것을 알 수 있습니다.  나라별 시간대 비교해보기  UTC를 기준으로 시간이 빠르면 +시차, 시간이 느리면 -시차로 표시합니다.                                                                                                                                시간대나라코드UTC-5미국(동부)ESTUTC영국GMTUTC+8대만TWUTC+9대한민국KSTUTC+9일본JSTUTC+10오스트레일리아(동부)AEST     나라별 시간대 차이에 대한 더 자세한 내용은 여기를 참고해주세요.   시간대를 명확히 표시하지 않은 시각은 혼동을 일으킬 수 있습니다. 예를 들어서, 서울에 살고 있는 점주가 2019년 1월 1일 0시 0분에 방문한 고객을 알고 싶어 한다고 가정해봅시다. 이 데이터를 파이썬으로 표현하면 아래와 같이 적을 수 있습니다.  KST = datetime.timezone(datetime.timedelta(hours=9)) korea_1_1 = datetime.datetime(2019, 1, 1, 0, 0, 0, tzinfo=KST)   만약, 대만에 사는 점주가 이를 요청했다면 아래와 같이 적을 수 있습니다.  TW = datetime.timezone(datetime.timedelta(hours=8)) taipei_1_1 = datetime.datetime(2019, 1, 1, 0, 0, 0, tzinfo=TW)   위 예제에서 보이는 것 같이 대한민국과 대만에 있는 점주가 같은 시각을 요청했더라도, 시간대(KST/TW)에 따라서 별도로 처리해야 합니다.  assert korea_1_1 != taipei_1_1 assert taipei_1_1 - korea_1_1 == datetime.timedelta(hours=1) # 같은 시각이지만 시간대에 따라서 시간차가 있습니다.   그렇기 때문에 시간대가 표시되어 있지 않은 2019년 1월 1일이라는 정보만으로는 정확한 시각을 알 수 없습니다.  naive_1_1 = datetime.datetime(2019, 1, 1, 0, 0, 0) assert korea_1_1 != naive_1_1 assert taipei_1_1 != naive_1_1   이런 상황을 해결하기 위해 시각은 어떤 한 시각을 기준으로 하여 그 차이가 표시되어야 합니다. 그 기준으로 정한 것이 UTC입니다. 대한민국은 UTC를 기준으로 아홉시간 빠르기 때문에 korea_1_1의 시각을 UTC 시간대로 표현하면 2018-12-31 15:00:00+00:00입니다. 대만은 UTC를 기준으로 여덟시간 빠르기 때문에 taipei_1_1의 시각을 UTC 시간대로 표현하면 2018-12-31 16:00:00+00:00입니다. 위의 시각은 각각 대한민국(2019-01-01 00:00:00+09:00), 대만(2019-01-01 00:00:00+08:00)으로 표시할 수 있습니다. 이렇게 시간대와 같이 표시하면 혼란 없이 정상적으로 처리할 수 있습니다.  datetime  datetime은 파이썬에서 기본으로 제공하는 표준 라이브러리로, 간단하거나 복잡한 방식으로 날짜와 시각을 조작하기 위한 클래스를 제공합니다.  The datetime module supplies classes for manipulating dates and times in both simple and complex ways.  datetime은 시간대 포함 여부에 따라서 naive datetime, aware datetime 두 가지로 나눕니다.  naive datetime / aware datetime  datetime의 타입을 알아봅시다. 파이썬에서 시간 관련 연산을 하다 보면 종종 아래와 같은 에러 문구를 만날 수 있습니다.  >>> a = datetime.datetime.now() >>> b = datetime.datetime.now(datetime.timezone.utc) >>> a - b Traceback (most recent call last): File "", line 1, in TypeError: can't subtract offset-naive and offset-aware datetimes      naive datetime : naive datetime 객체는 그 자체만으로 시간대를 찾을 수 있는 충분한 정보를 포함하지 않습니다. (e.g. datetime.datetime(2019, 2, 15, 4, 58, 4, 114979))   aware datetime(timezone-aware) : 시간대를 포함합니다. (e.g.datetime.datetime(2019, 2, 15, 4, 58, 4, 114979, tzinfo=)) aware datetime 객체는 자신의 시각 정보를 다른 aware datetime 객체와 상대적인 값으로 조정할 수 있도록 시간대나 일광 절약 시간 정책 혹은 적용 가능한 알고리즘 정보를 담고 있습니다.   tzinfo는 UTC, 시간대 이름 및 DST 오프셋에서 로컬 시간의 오프셋을 나타내는 방법을 담고 있습니다. 더 자세한 내용은 공식 문서를 확인해주세요.  naive datetime은 어느 시간대를 기준으로 하는 시각인지 모호하므로 aware datetime을 이용하는 것을 권장합니다.  직접 확인해보기  준비한 몇 가지 코드를 보며 확인해봅시다. naive datetime과 aware datetime의 차이를 확인하고, 시간대 지정 방법에 대한 내용을 다룹니다.  개발환경     Python 3.7   pytz   여기서는 datetime을 쉽게 다루기 위해 pytz 라이브러리를 사용합니다. pytz는 아래와 같은 장점이 있습니다.    시간대를 시간차가 아닌 사람이 알아보기 쉬운 지역 이름으로 비교적 쉽게 설정할 수 있습니다.   원하는 시간대의 aware datetime으로 변경해주는 localize() 메소드를 제공합니다.   pytz 사용에 앞서, pytz가 제공하는 시간대 식별자를 확인하시려면 다음을 따라 해주세요. import pytz for tz in pytz.all_timezones: print(tz)  혹은 여기를 참고하셔도 좋습니다.  naive datetime  naive datetime은 날짜와 시각만을 갖습니다.  import datetime datetime.datetime.utcnow() # UTC 기준 naive datetime : datetime.datetime(2019, 2, 15, 4, 54, 29, 281594) datetime.datetime.now() # 실행 환경 시간대 기준 naive datetime : datetime.datetime(2019, 2, 15, 13, 54, 32, 939155)   aware datetime naive datetime과 달리 aware datetime은 시간대 정보(tzinfo) 도 갖습니다. import datetime from pytz import utc utc.localize(datetime.datetime.utcnow()) # UTC 기준 aware datetime : datetime.datetime(2019, 2, 15, 4, 55, 3, 310474, tzinfo=)   now는 UTC를 기준으로 현재 시각을 생성합니다. 하지만, naive한 시각입니다.  now = datetime.datetime.utcnow()   이 시각은 naive한 시각이므로 pytz.timezone.localize를 통해 timezone-aware한 시각으로 변환된 시각과 동일하지 않습니다.  assert now != utc.localize(now)   시간대 제대로 지정하기  시간대가 무엇이고, 명시하는 것이 왜 중요한지 알게 되셨다면 시간대를 원하는 의도에 맞게 지정하는 방법에 대해 알아봅시다.  import datetime from pytz import timezone, utc KST = timezone('Asia/Seoul') now = datetime.datetime.utcnow() # UTC 기준 naive datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879) utc.localize(now) # UTC 기준 aware datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=) KST.localize(now) # UTC 시각, 시간대만 KST : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=) utc.localize(now).astimezone(KST) # KST 기준 aware datetime : datetime.datetime(2019, 2, 15, 13, 18, 28, 805879, tzinfo=)   replace() 메소드로 날짜나 시간대를 변경할 수 있습니다.  KST = timezone('Asia/Seoul') TW = timezone('Asia/Taipei') date = datetime.datetime.now() # datetime.datetime(2019, 2, 15, 13, 59, 44, 872224) date.replace(hour=10) # hour만 변경 # datetime.datetime(2019, 2, 15, 10, 59, 44, 872224) date.replace(tzinfo=KST) # tzinfo만 변경 # datetime.datetime(2019, 2, 15, 13, 59, 44, 872224, tzinfo=) date.replace(tzinfo=TW) # tzinfo만 변경 # datetime.datetime(2019, 2, 15, 13, 59, 44, 872224, tzinfo=)   하지만 replace는 그 속성 자체만을 바꿔버리는 것이기 때문에 사용에 주의할 필요가 있습니다.  now = datetime.datetime.utcnow() assert utc.localize(now) == now.replace(tzinfo=utc) assert KST.localize(now) != now.replace(tzinfo=KST) assert TW.localize(now) != now.replace(tzinfo=TW)  그뿐만 아니라 replace()를 이용할 경우 의도하지 않은 시간대로 설정될 수도 있으므로 유의해야 합니다. 그 이유는 아래와 같습니다.     시간대는 생각보다 자주 바뀝니다(더 자세한 내용은 스포카의 규칙 2번을 참고해주세요). 이렇게 변경되는 사항들은 tz database에 기록되는데, pytz는 이에 기반합니다. pytz의 버전이 2018.9와 같은 날짜로 되어있는데 2018.9 버전은 2018년 9월 기준 시간대 테이블을 기준으로 시간대를 만들어주는 버전입니다. 이 버전에선 Asia/Seoul의 시간대는 UTC+9입니다.   pytz는 무슨 이유에서 인지 datetime.replace()나 datetime.astimezone()에서 호출될 때 이 tz database 타임 테이블의 맨 첫 번째(가장 오래된) 기록을 가지고 변환을 시도합니다. 서울의 경우 초기에 UTC+8:28이었기 때문에 이 정보를 기반으로 변환합니다.   그래서 pytz를 사용할 때는 pytz.timezone.localize()를 항상 써야 하고, .astimezone()같은 파이썬의 표준 메서드들을 사용하고 싶다면 datetime.timezone을 사용해야 합니다.  스포카의 규칙 스포카에서 datetime을 다룰 때 흔히 따르는 두 가지 큰 원칙이 있습니다.  1. naive datetime은 절대 사용하지 않습니다. 가장 큰 이유는 naive datetime과 aware datetime을 서로 섞어서 쓰지 못한다는 것입니다.  >>> from datetime import datetime, timezone >>> datetime.utcnow() + datetime.now(tz=timezone.utc) Traceback (most recent call last): File "", line 1, in TypeError: unsupported operand type(s) for +: 'datetime.datetime' and 'datetime.datetime'   동적 타입 언어에서 쓸 수 있는 가장 간단한 타입 검사 수단인 isinstance() 체크로도 이 둘을 구별할 수가 없으므로, 코드의 어느 지점에서 naive datetime이 섞이기 시작하면 예기치 않은 지점에서 버그 발생 가능성이 급격히 올라갑니다. Python 2에서 str과 unicode를 섞으면 안 되는 것과 비슷한 이유라고 생각하시면 됩니다.  2. 장기적으로 보존해야 하는 datetime은 항상 UTC를 기준으로 저장합니다. 지역 시간대는 지정학적 또는 정치적인 이유로 생각보다 자주 바뀝니다. 예컨대 1961년 이전까지 한국은 UTC+08:30을 지역 시간대로 사용했었고, 1988년 올림픽 즈음에는 일광 절약 시간대를 시행하고 있었습니다. 시간대 데이터베이스(tz database)는 이런 변경 내역을 담고 있고, pytz가 제공하는 시간대 객체의 동작에도 반영되어 있습니다. 그 때문에 시간대 데이터베이스가 제때 업데이트되지 않거나, 갑작스러운 시간대 변경으로 데이터베이스에 반영이 늦어지거나 하면, 시간 계산에서 오차가 발생할 여지가 있습니다. 또한 같은 aware datetime 이어도 서로 다른 시간대를 가진 datetime끼리 연산하거나 하는 상황도 문제를 복잡하게 만들고, DB나 다른 서비스의 API를 사용할 때, 그 서비스가 시간대를 제대로 다루는 데에 필요한 복잡도를 감수하는 대신 단순히 UTC 기준의 고정 오프셋 시간대만 사용하는 등의 이유로 서로 지원 범위가 맞지 않아 곤란을 겪을 수도 있습니다.  혼선을 줄일 수 있는 좋은 규칙 중 하나는, str과 unicode를 다루던 것과 비슷하게 모든 내부적인 계산에서 UTC 기준의 aware datetime만 사용하고, 사용자에게 보여줘야 할 때만 필요한 시간대로 변환해서 보여 주는 것입니다.  스포카에서는 메인 서버의 dodo.datetime 유틸리티 모듈도 이런 규칙을 따르고 있으며, 대부분의 SQLAlchemy DB 모델 객체의 DateTime 컬럼에서 timezone=True 옵션을 켜서 사용하고 있습니다.  정리  시간 관련 작업을 하신다면 아래 사항을 꼭 기억해주세요.시간대를 명시합시다.시각을 애플리케이션 로직이나 데이터베이스에서 저장할 때는 UTC로 사용하고, 유저에게 표시할 때만 유저의 시간대로 변환하여 보여주도록 합시다.    백엔드 서버끼리 통신할 때도 항상 UTC를 사용한다는 가정을 하면, 시간대가 없더라도 robust하게 처리할 수 있습니다.
조회수 370

금요일의 해커톤

안녕하세요. 엘리스입니다!지난 8월 말, 엘리스의 야심 찬 첫 해커톤이 있었습니다. 이번 해커톤은 매주 금요일 찾아가는 문제 ‘금요일에 코딩하는 토끼’에 대한 수강생 여러분의 성원에 힘입어 개최되었습니다.주제는 ‘코딩 문제의 A에서부터 Z까지 직접 설계하고 제작한다.’ 해커톤에서는 아이데이션 단계에서부터 문제 기획과 코딩, 채점을 위한 그레이더 제작까지 코딩 문제의 모든 것을 다루었습니다. 물론 실제 문제 동작을 위해 실행과 채점을 반복하며 디버깅하여 완벽한 실습 문제를 만드는 것 역시 이번 경연의 핵심이었는데요.이를 통해 모든 참가자 여러분들은 일일 엘리스 아카데미 실습 문제의 출제자가 되었습니다. 어떤 과정을 거친 어떤 결과물들이 있었을까요?해커톤 현장 스케치해커톤의 소개를 경청 중이신 참가자 여러분.지금까지 프로그래밍 문제를 많이 풀어보셨을 여러분이, 반대로 문제의 출제자가 되어 문제를 구성하는 관점에서 생각해보고 채점 방식까지 고민해본다면 프로그래밍에 대한 이해도를 더 높일 수 있을 것이라는 기대로 이와 같은 해커톤이 기획되었습니다. 교육자로서 엘리스 플랫폼의 다양한 기능을 직접 이용해볼 수 있는 것은 일석이조의 이점이었죠!경직된 분위기를 깨고 뇌를 말랑말랑하게 만들기 위한 아이스 브레이킹 시간은 팀 대항전으로 진행되었습니다.간단한 코딩 문제를 가장 먼저 맞히는 팀이 점수를 얻는 스피드 코딩 게임을 통해서 순발력을 높이고, 잠시 후 해커톤에서 본격적으로 사용하게 될 엘리스 플랫폼과 친해질 시간도 가질 수 있었습니다.'그림 그리기 게임'에서는 각 팀 디자이너들의 창의력이 폭발! 개발과 관련된 온갖 단어들을 1초 만에 그림으로 표현해야 하는 설명자의 재치와 크로키 실력(?)이 강조되었던 순간이었는데요. 승자는 '오즈'팀! 모두 오즈 팀 디자이너의 그림 실력에 입을 다물지 못했다고 합니다.게임을 하는 동안 어느새 어색했던 처음의 분위기가 파괴되었습니다. ^^ 1시간 동안 문제의 초안을 기획하는 시간이 주어지고, 이어 각 팀의 아이디어 발표 시간이 있었습니다.해커톤의 룰은 아래와 같았는데요.실행 가능한 프로그래밍 문제 1개 출제.동화를 모티브로 한 문제 스토리를 기획.채점 가능한 그레이더 제작.모든 팀들이 알고리즘 문제를 기획해주셨습니다. 동화의 서사구조를 논리적으로 단순화하거나 변형하여 알고리즘 문제에 녹여낸 과정이 인상적이었습니다.아이데이션 단계에서는 문제의 완성된 모습이 전부 그려지지는 않았지만 많은 고민의 흔적과 창의적인 생각들을 엿볼 수 있어 이로부터 탄생될 프로그래밍 실습을 기대할 수 있었습니다.밤샘 코딩 중...우승 문제 소개기획하고 코딩하고 디자인을 하다 보니(!) 어느새 날이 밝아왔습니다. 이제 남은 것은 팀별 결과물 발표와 우승팀 시상 뿐!'금코토'를 패러디하여 팀 명을 지어주신 어린 왕자 팀. /* prince */로고까지 깨알 섬세!모든 팀이 각기 다방면에서 강점을 부각하는 문제를 출제해주셨기 때문에 우열을 가리기 어려웠는데요. ‘금코토’배 해커톤이라는 이름에 걸맞게 금코토 과목의 취지와 가장 부합하는 문제를 출제한 팀에게 가산점을 주어 우승팀을 선발하였습니다. 그 결과 대망의 우승 문제는...거울나라의 앨리스팀의 ‘케이크와 병’ 단순한 명료한 문제 구성과 초등학생도 이해할 수 있는 쉽고 친절한 프레젠테이션으로 인상 깊었던 문제였습니다. 완성도, 문제 활용도 면에서 금코토 문제를 능가하며 단순하면서도 재미있게 풀 수 있는 문제라는 심사위원들의 평가가 있었습니다. 우승팀인 거울나라의 앨리스 팀 전원에게는 엘리스 굿즈를 선물로 보내드립니다. :)이밖에 겁쟁이 사자를 동물의 왕으로 만들기 위해 용기의 성을 짓는 알고리즘 문제를 낸 오즈의 마법사 팀의 문제는 스토리에 착안하여 자칫 복잡해질 수 있는 내용을 세세한 문제 설계로 극복하려 했던 점이 우수하게 평가받았습니다. 술주정뱅이 별에 사는 만취한 아저씨를 옮기는 알고리즘 문제를 낸 ‘목요일에 코딩하는 어린 왕자’ 팀은 참신성과 '넓이 우선 탐색', '깊이 우선 탐색', '다익스트라 알고리즘'을 모두 공부해볼 수 있도록 한 문제 구성 면에서 높은 평을 받았습니다.큰 상품도 내걸지 않았던 첫 해커톤이었는데도 참가자분들 모두가 열과 성을 다해 밤을 새워 문제를 만들어 주셨습니다. 모든 참가자 여러분들께 감사의 말씀 전합니다. :) 해커톤 이후 진행한 설문 조사에서 100%의 확률로 모든 분들이 다음 해커톤에 재참가 의사를 밝히셨는데요. 모두 첫 해커톤을 즐겨주셨던 것 같네요. 엘리스에서는 앞으로도 해커톤을 지속적으로 개최할 예정입니다. 코끝 시려질 때쯤 더욱 풍성하고 유익한 기획의 해커톤으로 찾아뵐 예정이니 많은 관심 가져주세요!*금코토 — ‘금요일에 코딩하는 토끼’라는 엘리스 아카데미 과목의 줄임말. 매주 금요일 저녁때쯤 업로드되는 문제로, 특정 루트로 토끼가 움직이도록 코딩해야 하는 콘셉트와 귀여운 휴보 래빗이 특징입니다. >>문제 풀어보기(무료)
조회수 2728

Next.js 튜토리얼 4편: 동적 페이지

* 이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다.** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요!목차1편: 시작하기 2편: 페이지 이동 3편: 공유 컴포넌트4편: 동적 페이지  - 현재 글5편: 라우트 마스킹6편: 서버 사이드7편: 데이터 가져오기8편: 컴포넌트 스타일링9편: 배포하기개요여러 페이지가 있는 Next.js 애플리케이션을 만드는 방법을 배웠습니다. 페이지를 만들기 위해 한 개의 실제 파일을 디스크에 만들어야 합니다.그러나 진짜 애플리케이션에서는 동적 컨텐츠를 표시하기 위해 동적으로 페이지를 생성해야 합니다. Next.js를 사용해 이를 수행하는 여러 방법들이 있습니다.쿼리 문자열을 사용하여 동적 페이지를 생성해봅시다.간단한 블로그 애플리케이션을 만들 예정입니다. 이 애플리케이션은 home (index) 페이지에 전체 포스트 목록을 가지고 있습니다.포스트 제목을 클릭하면 뷰에서 각 포스트를 볼 수 있어야 합니다.설치이번 장에서는 간단한 Next.js 애플리케이션이 필요합니다. 다음의 샘플 애플리케이션을 다운받아주세요:아래의 명령어로 실행시킬 수 있습니다:이제 http://localhost:3000로 이동하여 애플리케이션에 접근할 수 있습니다.포스트 목록 추가하기먼저 home 페이지 안에 포스트 제목 목록을 추가해봅시다.pages/index.js에 다음과 같은 내용을 추가해주세요.위의 내용을 추가하면 다음과 같은 페이지가 보입니다:첫 번째 링크를 클릭하면 404 페이지가 나지만 괜찮습니다.페이지의 URL은 무엇인가요?- /?id=Hello Next.js- /post?title=Hello Next.js- /post?title=Hello Next.js- /post쿼리 문자열을 통해 데이터 전달하기쿼리 문자열(쿼리 파라미터)를 통해 데이터를 전달했습니다. 우리의 경우에는 "title" 쿼리 파라미터입니다. 다음에서 보이는 것처럼 PostLink 컴포넌트를 이용해 구현해봅시다:(Link 컴포넌트의 href prop를 확인해주세요.)이처럼 쿼리 문자열을 이용하여 원하는 모든 종류의 데이터를 전달할 수 있습니다."post" 페이지 생성이제 블로그 포스트를 보여줄 post 페이지를 생성해야 합니다. 이를 구현하기 위해 쿼리 문자열로부터 제목을 가져와야 합니다. 어떻게 구현하는지 살펴봅시다:pages/post.js 파일을 추가하고 다음과 같이 내용을 작성해주세요:다음과 같이 보입니다:위의 코드에서 무슨 일이 일어났는지 살펴봅시다.- 모든 페이지에서 현재 URL과 관련된 내용들을 가진 "URL" prop를 가져옵니다.- 이 경우 쿼리 문자열을 가진 "query" 객체를 사용하고 있습니다.- props.url.query.title를 사용해 제목을 가져왔습니다.애플리케이션에서 몇 가지를 수정해봅시다. "pages/post.js"를 다음과 같이 변경해주세요: http://localhost:3000/post?title=Hello Next.js 페이지로 이동하면 무슨 일이 일어날까요?- 예상대로 동작할 것이다.- 아무 것도 랜더링하지 않을 것이다.- 해더만 랜더링할 것이다.- 에러를 발생시킬 것이다.특별한 prop "url"보다시피 위의 코드는 이와 같은 에러를 발생시킵니다:url prop는 페이지의 메인 컴포넌트에만 전달되기 때문입니다. 페이지에서 사용되는 다른 컴포넌트에는 전달되지 않습니다. 필요하다면 다음과 같이 전달할 수 있습니다:마치며쿼리 문자열을 사용하여 동적 페이지를 생성하는 방법을 배웠습니다. 이제 시작일 뿐입니다.동적 페이지를 렌더링하기 위해 더 많은 정보가 필요합니다. 그리고 쿼리 문자열을 통해 모든 것을 전달할 수는 없을 것입니다. 또는 http://localhost:3000/blog/hello-nextjs와 같은 깔끔한 URL을 원할 것입니다.다음 편에서 이것들에 대해 모두 배울 수 있습니다. 이번 편은 모든 것의 기초입니다.#트레바리 #개발자 #안드로이드 #앱개발 #Next.js #백엔드 #인사이트 #경험공유
조회수 34292

소규모팀에 적합한 QA 프로세스 구축기(스타일쉐어팀의 QA방식)

안녕하세요. 스타일쉐어에서 PM을 맡고있는 박성환 입니다. 스타일쉐어팀이 QA프로세스를 도입한 것은 약 4개월 정도 되었습니다. 기존에는 QA 프로세스 없이 진행했었지만 주요 기능에 대한 오류감소 및 릴리즈 안정성 확보를 위해 도입을 고민하게 되었습니다.QA프로세스를 처음 도입할때 많은 고민이 있었습니다. 대규모 서비스에 적용하는 QA프로세스를 그대로 도입하기에는 인력 + 시간이 모두 부족했기에 시간과 인력이 많이 투여되는(다만, 안정성이 높음) 명세기반 테스트는 최소화하고, 도입 가능한 서비스(구글플레이의 단계적 배포, Crashlytics)를 활용해 부족한 부분을 커버하는 형식으로 저희 식의 간략화된 QA프로세스를 만들었습니다.(인력 + 시간이 상대적으로 제한적인 스타트업에 좀 더 효율적인 방식.)스타일쉐어팀의 QA 기간 : 앱 업데이트 당 3일(테스트/수정/릴리즈까지의 모든 기간)테스트 인원 : 2명 (1차QA 1명, 최종확인 1명)마이너 버그 수정 버전에서는 QA진행하지 않음스타일쉐어팀의 QA프로세스는 “주요 사용 케이스의 동작 확인” + “수많은 사용 패턴에 대한 대응”으로 정리할 수 있습니다. 저희 팀이 진행하고 있는 방식을 조금 더 자세히 설명해 드리자면 아래와 같습니다.(API 테스트, 자동화 테스트를 제외한 앱 릴리즈 전 진행하는 사용성 테스트에 대한 내용만을 담았습니다.)1. QA일정스타일쉐어 앱의 업데이트 주기는 4주에 1회로 진행하고 있습니다. 그 중 1주 단위의 스프린트가 3주 동안 진행되고 4주차 스프린트는 QA 및 릴리즈 스프린트로 진행됩니다. 매 스프린트에서 담당 엔지니어가 수정 혹은 추가된 단위기능에 대해 간단한 테스트가 끝나면 4주차에 알파 빌드 및 전 구성원이 설치/사용해보고 동시에 1차 QA(통합 테스트)를 진행하게 됩니다. 1차 QA의 버그들을 수정하면 베타버전 빌드 및 최종 확인을 진행한뒤 문제없으면 바로 릴리즈가 되어 사용자에게 신규 버전을 제공합니다.2. 주요 사용 케이스의 동작 확인1) 1차 QA(명세기반 테스팅)4주차에 신규 알파버전이 생성되면 1차 QA를 진행하게 됩니다. 스타일쉐어는 전담 QA담당자가 없습니다. 1차 QA는 다른 파트 엔지니어 1명이 테스트를 진행하고 2차는 PM이 최종확인 후 릴리즈 됩니다. 이 단계에서는 Test case를 바탕으로한 명세기반 테스트로 진행됩니다.테스트 케이스(TC)를 통한 테스팅은 핵심적인 기능 및 주 사용케이스에 대한 검수작업이라고 보시면 됩니다. 게임 혹은 복잡도가 높은 서비스의 경우에는 매 업데이트마다 모든 케이스에 대한 테스트가 어렵고 비효율적이기 때문에 리스크 분석기법, 탐색적 테스팅, 경계값 테스팅 등과 같은 방식을 사용하지만 스타일쉐어 서비스의 경우 상대적으로 복잡도가 낮아 매 업데이트 마다 대부분의 기능에 대한 테스팅을 진행합니다(TC로 100% 커버리지를 목표로 하지 않습니다. 불가능하다는 것을 인정하고 진행하는 것이 효율적). 테스트케이스 작성시에 유의했던 부분은 쉽고 명확하게 케이스를 명시해서 오류에 대한 판단이 명확하도록 하고 스타일쉐어 앱을 처음 본 사람도 바로 테스트가 가능하도록 작성하고 있습니다. (스트레스 테스트는 특이 사항이 있을 경우에만 진행합니다.)2) 교차 테스팅스타일쉐어의 경우에는 1차QA 과정을 담당 엔지니어가 아닌 다른 파트의 엔지니어(iOS버전 테스트의 경우 web, backend, Android 개발자 중 1명이 진행)가 1차 테스트를 진행합니다. 이 방식의 장점은 매번 같은 사람이 테스트하는 것보다 다른 백그라운드를 가진 엔지니어가 테스트 함으로써 다양한 시각으로 테스트를 하게 되 오류발견이라던지 서비스 개선 아이디어를 찾는데 더 효과적이었습니다. 그리고 신규 입사자의 경우 가장 먼저 테스트 담당자로 참여할 수 있도록 합니다(가장 빠르게 서비스 플로우를 이해할 수 있는 방법).3) 최종확인1차 QA 및 전사 베타버전 사용의 피드백을 통해 나온 버그/주요 기능에 대해 마지막 점검하는 절차입니다. 이 부분은 제품책임자(PM)가 담당을 하며, 이 부분을 통과하면 릴리즈 단계로 진행되어 사용자에게 업데이트 된 앱이 전달됩니다.3. 수많은 사용 패턴에 대한 대응단계적 출시(안드로이드)1차 QA과정인 테스트케이스를 통한 테스팅은 명시되어 있는 패턴과 제한적인 환경(Device, 해상도, 인터넷 환경 등등)에서의 주요 케이스에 대한 테스팅만 가능합니다. 하지만 사용자는 수많은 환경 및 사용패턴으로 서비스를 사용하기 때문에 이 부분을 TC의 스크립트로 모두 추가하고 살펴보기란 불가능에 가깝습니다. 그래서 저희 팀은 단계적 출시를 도입해서 대응하고 있습니다.모든 테스트 과정을 완료한 뒤 구글플레이 개발자 콘솔에서 앱 업데이트시 ‘지금 출시’가 아닌 ‘단계적 출시’로 선택합니다. 그리고 비율을 선택할 수 있는데 이 비율은 업데이트가 적용되는 사용자 비율을 설정하는 기능입니다. 즉, 전체 사용자가 아닌 미리 지정한 비율의 사용자에게만 업데이트 버전을 제공함으로써 우선적으로 우리가 예상하지 못한 버그나 불편한 부분이 있는지 확인해볼 수 있습니다. 스타일쉐어팀의 경우 5%의 사용자 비율로 단계적 출시를 1~2일 동안 진행한뒤 버그 리포팅 및 CS내용 확인 후 100% 대상으로 업데이트를 진행합니다.(5% 단계적 출시 이후 패치된 버전을 배포하면 해당그룹(5%)에게만 업데이트 됩니다.)이 부분은 오류에 대한 대응 및 새로운 기능에 대한 부분적인 반응을 볼 수 있는 용도로도 사용할 수 있어 매우 활용도가 높습니다.(신규 앱에 대해서는 해당 기능 사용이 불가능합니다. 업데이트시에만 사용가능합니다.)4. 도입효과1) Crash Free Sessions(Crashlytics)4월 13일 기준으로 Crash Free Sessions는 전체 사용자 중 99.8%의 안정성을 가져가고 있으며(이전에는 95~96%), 기존에는 주말과 같이 사용자가 많은 경우 그만큼 크래시 발생빈도도 높았지만 최근 버전에서는 주말/평일 관계없는 그래프를 보이고 있습니다.2) Crash Report(Flurry)위 지표는 1월~3월 까지의 Flurry의 안드로이드 버전 Crash Report를 캡처한 화면입니다. 1월 초만 해도 일 40회 정도의 크래시가 발생했다면 최근은 일 3~5회 정도로 개선된 모습을 확인할 수 있습니다.5. 마무리다만, 이러한 노력에도 버그는 여전히 존재합니다. 그래서 저희 QA프로세스도 개선할 방향을 모색하고 있는데, 현재의 개선 목표는 ‘퀄리티는 유지하되 속도는 빠르게’ 라는 방향으로 진행 중입니다. 그물을 더 촘촘히 짜듯이 명세기반 테스트의 규모를 늘리는 것에는 시간적/효율적인 한계가 분명히 존재하므로 자동화 테스팅(UI)의 강화를 통해서 부족한 부분을 채워보기 위한 시도를 준비하고 있습니다.하루라도 빠른 서비스의 개선도 매우 중요하지만 그만큼 우리가 전달하고자 하는 것을 문제없이 사용자에게 제공하는 것도 속도만큼 중요하다 생각 합니다. 문제없이 전달하기 위해 계속해서 고민하고 시도해볼 수 있도록 하겠습니다.#스타일쉐어 #개발 #개발팀 #개발자 #노하우 #인사이트
조회수 1075

안드로이드 클라이언트 Reflection 극복기 - VCNC Engineering Blog

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

[인공지능 in IT] 구글이 말하는 인공지능의 혁신성

지난 2018년 5월 8일부터 5월 10일까지 3일간, 미국 샌프란시스코에서 '구글 I/O 2018(Google Input/Ouput 2018)'이 열렸다. 구글 I/O는 매년 구글이 혁신적인 제품을 선보이는 행사로, 구글의 신제품과 신기술을 가장 먼저 접할 수 있는 자리다. 필자는 지난 몇 년간 구글IO를 지켜봤지만, 개인적으로 이번만큼 신선한 충격을 받지는 못했던 것 같다.< 구글 I/O 2018, 출처: 구글, 제공: 스켈터랩스 >구글 선다 피차이(Sundar Pichai) CEO는 올해 구글 듀플렉스(Duplex)라는 음성 기술을 시연했다. 구글 듀플렉스는 시연을 통해 미용실과 레스토랑에 스케줄을 예약하며, "Mm-hmm"이나 "Aha"라고 자연스러운 대화 흐름을 선보여 많은 사람에게 경외 혹은 두려움을 불러 일으켰다. 구글 듀플렉스가 베이퍼웨어(Vaperware, 개발 중이지만 아직 완성되지 않은 또는 완성되지 않을 수 있는 소프트웨어)일 가능성도 있지만, 구글의 인공지능 기술 수준을 전세계에 알리기에 충분한 계기라고 생각한다.< 구글 듀플렉스, 출처: 구글, 제공: 스켈터랩스 >구글IO 2018을 보며 스스로에게 질문을 던졌다. 구글이라는 기술 공룡은 어떻게 혁신의 아이콘이 될 수 있었을까? 먼저 혁신의 사전적 의미를 살펴보면 다음과 같다. '묵은 풍속, 관습, 조직, 방법 따위를 완전히 바꾸어서 새롭게 함.' 여기서 가장 집중할 부분은 '완전히 바꾸어서 새롭게 한다는 것'으로, 대다수의 사람은 짠하고 나타나는 새로운 기술을 떠올릴 것이다. 틀린 말은 아니다. 다만, 조금 다른 관점으로 생각해본다면 기술이라는 결과물을 만들기 위해, 어떠한 방식으로 접근(Approach)했는지도 중요할 것이다.이번 구글IO 2018 중 듀플렉스를 시연하며 선다 피차이 CEO가 던진 질문을 끝으로 짧은 글을 마무리한다."60%의 소상공인들은 온라인 예약 시스템을 가지고 있지 않다. 이를 인공지능이 해결할 수 있지 않을까?"질문만 듣고 판단한다면, 구글 자체가 거대한 인공지능 기술기업이기에 당연히 온라인 예약시스템을 대체하거나 더 요긴하게 사용할 수 있는 인공지능 플랫폼을 만들 것이라고 생각할 것이다. 그러나 구글은 다른 관점에서 접근했다."온라인 예약 시스템이 없다면, 인공지능이 직접 전화를 걸면 된다"고.이호진, 스켈터랩스 마케팅 매니저조원규 전 구글코리아 R&D총괄 사장을 주축으로 구글, 삼성, 카이스트 AI 랩 출신들로 구성된 인공지능 기술 기업 스켈터랩스에서 마케팅을 담당하고 있다#스켈터랩스 #기업문화 #인사이트 #경험공유 #조직문화 #인공지능기업 #기술기업
조회수 3991

코드리뷰, 이렇게 하고 있습니다.

토스랩 안드로이드팀이 코드리뷰 하는 방법실리콘밸리 이야기 - 코드리뷰는 어떻게 하나요? 를 보고 토스랩이 코드리뷰 하는 프로세스와 방법에 대해서 공유해드리고자 합니다.왜 코드리뷰를 하게 되었나요?토스랩에 안드로이드가 팀 단위로 꾸려진 것은 5월 전후였습니다. 그 전에는 1인 개발 체제를 가지고 있었습니다. 갑작스럽게 인원이 많아지면서 코드스타일, 구조의 일관성 등이 계속적으로 깨지게 되고 이에 따라 제품의 안정성도 급격히 떨어지는 사태가 발생하였습니다.이에 내부적으로 제품의 품질을 강화하기 위한 대책들이 강구되었는데 그 중에 하나가 코드리뷰였습니다.코드리뷰를 위한 프로세스는 토스랩 웹 개발팀의 프로세스를 참고하여 안드로이드 개발 팀원의 내부 의견을 반영하여 진행되었습니다.1. 언제 코드리뷰를 요청하나요?안드로이드팀은 코드리뷰 요청에 대해 별도의 제약을 두지 않았습니다. 언제든지 코드리뷰 시스템이 코드리뷰를 요청할 수 있습니다. 다만 코드 리뷰가 시작되는 시점이 조금 다릅니다.모든 개발자가 코드리뷰를 각자의 업무(Task)가 완료되면 코드리뷰 시스템에 코드리뷰를 요청하고 이를 각 개발자가 언제든지 확인할 수 있도록 하고 있습니다.코드리뷰의 시작은 3. 그럼 코드리뷰는 언제 하나요? 에서 확인해보록 하겠습니다.2. 어떻게 요청하나요코드리뷰는 내가 아닌 다른 사람이 코드를 읽어야 하므로 어떤 목적에서 작업 된 코드인지를 미리 할 수 있어야 빠르게 코드리뷰를 할 수 있습니다. 최대한 자유롭게 하되 아래와 같은 형식을 지키도록 하고 있습니다.TitleFeature/Bug-fix 건인지 알 수 있도록 합니다.어떤 목적인지 간략하게 적도록 합니다.어떤 이슈와 연결된 건인지 알 수 있도록 합니다.Description어떤 로직을 추가/수정했는지를 작성합니다.어떻게 추가/수정했는지를 작성합니다ex)Title - [fix] 소켓 API 버전 처리 (JND-3986) Description@Version 커스텀 어노테이션 추가Version 없는 Event 에 Version 필드 추가, @Version어노테이션 부여SocketObject -> EventObject 로 파싱하는 로직 공통 메소드로 분리파싱 후 바로 반환하지 않고 Version Valid 로직 추가class JandiSocketServiceModel { T getObject(Object, T) // 파싱 공통 메소드 boolean validVersion(Object) // version 확인 } Java Reflection 사용.위와 같이 작성함으로써 이 이슈는 소켓 API 버전에 대한 버그 수정건으로 JND-3986 이라는 이슈와 연관된 것임을 알 수 있습니다. 상세 내용으로는 @Version 이 JandiSocketServiceModel의 getOject 와 validVersion 메소드와 연관되어 있음을 알 수 있도록 기술하였습니다.코드리뷰를 상세하게 쓰는 것은 리뷰어들이 코드리뷰를 효율적으로 하기 위함이기 때문에 리뷰할 부분을 빨리 확인 할 수 있게 적도록 하도 있습니다.3. 그럼 코드리뷰는 언제 하나요?실리콘 밸리의 큰 회사들 (구글, 페이스북 등)은 코드리뷰가 요청이 오면 업무의 최우선순위로 조정되어 즉시 응답하도록 하는 것이 원칙입니다. (지금 당장 하든지 아니면 언제부터 할 것인지를 피드백을 반드시 줘야 한다고 들었습니다.)하지만 스타트업은 일반적으로 개발해야 할 것들이 훨씬 더 많고 코드리뷰가 아니더라도 일이 산더미인 경우가 많습니다. 저희 토스랩이라고 이를 크게 벗어나지 않기 때문에 안드로이드팀은 별도로 코드리뷰하는 프로세스를 정의하였습니다.월~수 : feature/bug-fix 개발이 업무의 최우선 순위이다.목, 금 : 코드리뷰가 업무의 최우선 순위이며 코드리뷰 대상은 목요일 출근 전까지 리뷰 요청을 한 건을 대상으로 한다.이는 개발자들끼리 코드리뷰의 중요성을 이해하지만, 이것이 개발 건보다 더 큰 업무 비중을 차지하게 되면 개발 속도나 의욕을 저해할 수 있기 때문에 최대한 분리하여 해당 건에만 집중하기 위해 룰을 정하였습니다.업무에 따라서 편차는 있지만, 대개의 코드리뷰는 금요일에 모두 완료를 하고 있으며 긍정적 피드백이 나올때까지 코드를 변경해야만 완료가 됩니다.4. 무엇을 리뷰하나요?개발자 개인의 성향과 개발건의 성격에 따라 그때마다 다른 모습을 보여줍니다.성능 개선 개발 : 시간복잡도신규 feature 개발 : 잠재적인 오류에 대한 검출리팩토링 : 테스트코드나 구조에 대한 물음신규 기술 도입 : 해당 기술의 로직과 그에 대한 물음기타 : 변수명과 같은 코드 컨벤션을 하기도 합니다. 전체적인 흐름을 이해하기 위해 실제 빌드를 해서 동작을 시켜보고 이해하기도 합니다.기본적인 사항들은 CI 품질도구 리포팅 기능을 이용하기 때문에 주로 큰 그림에서의 코드리뷰를 하는 편입니다.5. 코드리뷰 코멘트는 어떻게 작성하나요?OO 보다는 XX 가 더 나은 것 같아요.XX 는 OO 부분을 참고해서 이용하면 되요.OO 는 XX 에 의해서 문제되지 않을까요?XX 를 하려다가 OO 로 했는데 어떻게 생각하세요?위와 같이 가급적이면 상대방을 공격하지 않는 느낌을 주도록 하며 단순히 문제를 이슈업하기 보다는 대안을 제시하는 방법을 주로 하고 있습니다. 코드리뷰는 서로의 코드에서 이해할 수 없는 부분을 찾고 문제가 될 수 있는 부분을 미리 찾아내는 자리인만큼 문제의 검출과 해결에 주안을 두고 진행합니다.6. 코드리뷰가 끝나면 어떻게 하나요?서로가 이해할 수 있을 만큼 리뷰가 진행되면 코드는 그때서야 개발용 브랜치에 통합을 합니다. 최소 1명의 피드백도 진행되지 않은 코드는 통합하지 않는 것이 원칙으로 하며 통합되어야 하는 건이 코드리뷰가 진행되지 않으면 늦어도 월요일 아침에 긴급히 진행해 줄 것을 환기시킵니다.7. 긴급히 코드리뷰해야 하는 건은 어떻게 하나요?긴급히 해야하는 건은 그만큼 사안이 중요하다고 생각하기 때문에 리뷰를 요청하는 즉시 진행을 하도록 합니다. 다만 해당 건이 즉시 반영해야 할만큼 중요한지를 서로간의 의논해서 진행하도록 합니다.총평안드로이드팀이 코드리뷰를 최초 시작한 것은 6월초입니다. 브랜치를 통합하기 전 개발 완료된 건에 대한 코드리뷰가 처음이었기 때문에 자리를 잡는데는 2달여 시간이 흐른 다음이었습니다. 초기에는 실수로 코드리뷰를 생략한다던가, 어떻게 코멘트를 남겨야할지에 대해서 조심스럽다던가 하는 시행착오를 겪어서 지금은 개발 건에 따라 20건이 넘는 의견이 남겨질 정도로 활발하게 의견을 교류하고 통합을 거칩니다.코드리뷰에 생소한 사람은 대개 나의 작업물을 누군가에게 검토 받는다는 느낌에 거부감을 가지기 마련입니다. 하지만 더 큰 그림에서 본다면 코드리뷰는 코드의 안정성을 서로 다른 관점에서 검토하는 것이기 때문에 코드의 신뢰성이 더욱 커지는 과정입니다. 그러기에 이에 대한 이해 없이 진행하는 코드리뷰는 금방 유명무실해지기 때문에 모두의 이해를 가진 다음에 진행 할 것을 추천합니다.제품의 안정성을 신경써야 하는 시점에 QA 강화와 같은 외부의 요인만을 찾는 것보다 내부에서 좀 더 개선 할 수 있는 요인을 찾는 것도 하나의 방법입니다. 토스랩에서는 다양한 품질 검증 과정에서 코드리뷰를 매우 중시하고 있습니다. 모든 팀이 각자만의 스타일대로 코드리뷰를 진행하고 있습니다.모든 개발자분들이 코드리뷰에 열린 자세로 올바른 코드리뷰를 진행하기를 바랍니다.#토스랩 #잔디 #JANDI #개발 #개발팀 #개발자 #개발환경 #업무환경 #코드리뷰 #인사이트 #조언

기업문화 엿볼 때, 더팀스

로그인

/