스토리 홈

인터뷰

피드

뉴스

조회수 1605

스마트 컨트랙트 개발과정에서의 실수 — TransferFrom

Hexlant는 Blockchain 전문 개발 팀으로, 다양한 기관들의 스마트 컨트랙트 코드를 검수하는 업무도 진행하고 있습니다.지금까지 다양한 컨트랙트 코드들을 리뷰하면서 나왔던 문제점들을 공유하고, 더 나은 방법으로 개발 할 수 있는 방법들에 대해 이야기 해보고자 합니다.transferFrom에 대한 이해ERC-20 표준에 보면, transferFrom 이라는 함수가 있습니다. 일반적으로 많이 쓰이는 기능이 아니다 보니 잘 모르고 넘어가는 경우가 많습니다.function transferFrom(address _from, address _to, uint256 _value) public returns(bool)transferFrom은 남이 가지고 있는 토큰을 누군가에게 보내는 기능입니다.그 누군가는 내가 될 수도 있습니다.이 설명만 보면, 아래와 같은 의문이 생기실 겁니다.어? 남의 토큰을 내 마음대로 옮길 수 있다고??당연히 마음대로 옮기면 안되겠죠.그래서 approve 함수를 통해, 내 토큰을 사용할 수 있는 사람을 지정할 수 있습니다function approve(address spender, uint256 _value) public returns(bool)토큰의 holder는 approve함수를 호출하여 spender에게 일정량 만큼을 사용할 수 있게 허용을 해 줍니다. 그럼 spender는 허용된 범위 안에서 토큰을 마음대로 옮길 수 있습니다.허가되지 않은 토큰의 이동많이 쓰지 않는 기능이다 보니, 이 부분에 대해 고려하지 않고 개발 하는 경우가 있을 수 있습니다.아래는 저희가 리뷰했던 코드 중 일부입니다function approve(address _spender, uint256 _value) public returns (bool success) { require(_spender > address(0)); allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; }function transferFrom(address _from, address _to, uint256 _value) public { require(_from > address(0)); require(_to > address(0)); require(balances[_from] >= _value); require(balances[_to] + _value > balances[_to]); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); Transfer(_from, _to, _value); }approve 함수를 우선적으로 보면, allowed 테이블에, msg.sender가 _spender에게 얼마만큼 토큰사용을 허용해 주었는지 저장하는것 말고는 특별한 기능은 없습니다.allowed[msg.sender][_spender] = _value;이제 transferFrom 함수를 확인해 보겠습니다.transferFrom은 실제 토큰이 전송되는 부분이니 예가 필요할 것같습니다.Alice에게 10000개의 토큰이 있을 때, Bob이 transferFrom을 다음과 같이 호출했다고 합시다.transferFrom(Alice, Bob, 10000)자 이제 transferFrom코드를 따라가며 토큰이 어떻게 전송이 되는지 확인해 봅시다.require는 안에 들어간 조건이 만족해야만 다음 라인을 실행 할 수 있다는 명령어 입니다. require를 만족하지 못하면, 해당 트랙잭션은 수행되지 않고 실패로 처리됩니다.require(_from > address(0)); require(_to > address(0));위의 두 줄의 조건은 입력된 주소_from, _to는 각각 Alice와 Bob의 지갑 주소이기 때문에 0x*****형태로 0x0000…0000이 아니기에 해당 조건들을 모두 만족합니다.require(balances[_from] >= _value); require(balances[_to] + _value > balances[_to]);Alice의 지갑에는 10000개의 토큰이 있고 _value는 10000개이니까 저 require를 실제 숫자로 대입하면require(10000 >= 100000); require(0+10000 > 0);조건을 충분히 만족합니다.그 다음부분들을 실제로 Alice의 주소에서 Bob의주소로 10000개의 토큰을 옮기는 작업입니다.balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); Transfer(_from, _to, _value);Alice의 잔액에서 10000개만큼이 빠지고,Bob의 잔액에 10000개가 추가됩니다.balances[Alice] = balances[Alice].sub(10000); balances[Bob] = balances[Bob].add(10000); Transfer(Alice, Bob, 10000);이로서 Bob은 Alice의 토큰 10000개를 자신의 지갑으로 이동시켰습니다.일련의 과정을 요약하면1. 주소 오류 검증 2. 보내려는 토큰이 Alice가 가진 잔액보다 작은지 검증 3. 받았을때 Overflow가 발생하는지 체크 4. Alice의 잔액에서 보내는 만큼의 토큰 수량을 뺀다 5. Bob의 잔액에 보내는 만큼의 토큰 수량을 더한다과정을 보면 Bob이 Alice로 부터 토큰 사용을 허락받았는지 체크하는 부분이 없습니다.따라서 누군가가 보유한 토큰을 다른 사람이 제멋대로 쓸수 있게됩니다.오류수정transferFrom이 정상적으로 동작하려면 어떻게 수정되어야 할까요?function transferFrom(address _from, address _to, uint256 _value) public { require(_from > address(0)); require(_to > address(0)); require(balances[_from] >= _value); require(balances[_to] + _value > balances[_to]); require(allowed[_from][msg.sender] >= _value); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value) Transfer(_from, _to, _value); }첫 번째로는 당연히 transferFrom을 호출한 사람이 권한이 있는지 확인해야 합니다.require(allowed[_from][msg.sender] >= _value);이 조건을 통해 허용된 수량안에서만 토큰을 옮길 수 있게 만들 수 있습니다.두번째는, 토큰을 옮긴 후 허용량을 줄여주어야 합니다.allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value)만일 Alice가 Bob에게 10000개의 토큰을 허용해 주고, Bob이 그중 100개를 사용했다면, 그 다음번에 Bob은 9900개 안에서만 사용할 수 있어야 합니다.#헥슬란트 #HEXLANT #블록체인 #개발자 #개발팀 #기술기업 #기술중심 #실수담
조회수 9065

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

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

스켈티인터뷰 / 스켈터랩스의 금손 이주현 님을 만나보세요:)

Editor. 스켈터랩스에서는 배경이 모두 다른 다양한 멤버들이 함께 모여 최고의 머신 인텔리전스 개발을 향해 힘껏 나아가고 있습니다. 스켈터랩스의 식구들, Skeltie를 소개하는 시간을 통해 우리의 일상과 혁신을 만들어가는 과정을 들어보세요! 스켈터랩스의 하드웨어팀 금손 이주현 님을 만나보세요:)사진1. 스켈터랩스의 하드웨어 엔지니어 이주현 님Q. 자기소개를 부탁한다.A. 스켈터랩스의 하드웨어 엔지니어로 일하고있는 이주현이다.Q. 스켈터랩스에서 구체적으로 어떤 일을 맡고 있는가.A. 현재는 스켈터랩스의 레고(L.ego)팀에서 곧 출시 예정인 스마트 미러, 샘(Samm)을 만들고 있다. 레고 팀은 스켈터랩스가 가진 원천 기술을 소비자가 쉽고 편하게 접할 수 있도록 디바이스(Device) 형태로 구현하는 팀이다. 우리의 원천 기술이 다양하다 보니, 이 기술을 어떻게 활용하여 어떤 제품을 만들어야 할지부터 고민한다.Q. 매번 새로운 기획을 하고 아이디어를 내는 것이 쉬운 일은 아닐 것 같다.A. 그래서 다양한 소스를 참고하고 많은 사람에게 의견을 구하려고 한다. 킥스타터(Kickstarter)나 와디즈(Wadiz)와 같은 크라우드펀딩 플랫폼을 들여다보거나 DIY 상품을 여러가지 찾아보며 영감을 얻는다. 최근에는 레고팀 PM(Product Manger)이신 아영님의 소개로 산업디자인과 수업을 청강했다. 산업디자인이 내가 일하는 분야와 아주 밀접한 것은 아니지만 학생들이 아이디어를 개진하여 그것을 발전시켜나가는 것을 보며 나 또한 아이디어를 얻을 수 있었다. 이런 과정을 통해 제품이 구체화되면 성공 가능성에 연연하지 않고 일단 개발을 시도하려 한다.Q. 실제로 제작하는 과정에서도 예기치 못한 문제에 많이 부딪히지 않나.A. 맞다. 참신해보였던 아이디어도 기능을 구체화하는 단계에 접어들면 자잘한 이슈가 생기기 마련이다. 사람마다 생각이 다르기 때문에, 고객에게 제품의 어떤 기능이 유용할 지 예상하기도 쉽지 않다. 때문에 소프트웨어 엔지니어와 디자이너, 마케터와 같은 다른 포지션의 동료들과 자주 미팅을 갖는다.제품의 구체화가 성공적으로 완료되더라도, 실제 구현이 녹록치 않다. 가령 곧 출시를 앞두고 있는 스마트 미러 제품, 샘(Samm)의 경우 사용자의 제스처(Gesture)를 인식하여 작동하는데 생각보다 카메라의 한계가 있더라. 그래서 요즘은 카메라 뿐만 아니라 다양한 센서를 활용하는 방법을 찾고있다.Q. 내가 상상했던 ‘일반적인 하드웨어 엔지니어'의 업무와는 조금 달라보인다. 기획자 역할까지 겸비하는 것으로 보이는데, 맞나.A. ‘일반적인 하드웨어 엔지니어'의 역할을 무엇이라고 정의하는지에 따라 다른 것 같다. 나는 오히려 스켈터랩스에서 하는 업무가 내가 생상했던 ‘하드웨어 엔지니어'의 업무다. 보통 엔지니어들은 직접 만들어보는 것을 좋아한다. 그렇지만 만들고 싶은 디바이스가 늘 회사의 방향성과 일치하는 것은 아니기 때문에, 집에서 홀로 개발하기에는 시간과 돈이 늘 부족하다는 하소연을 많이 듣곤 한다. 또한 회사의 규모가 커질수록 하드웨어 엔지니어는 하나의 제품을 깊게 들여다보기 때문에 전문가로 성장하는 반면, 내가 하고싶은 개발을 할 수 있는 기회는 줄어들기 마련이다. 하지만 스켈터랩스에서는 내가 상상한 디바이스를 구현하기 위해 각종 부품을 조립하여 테스트하고, 응용하여 새로운 디바이스를 만들고 있다. 그래서인지 이곳이 내게는 딱딱한 회사의 느낌이 아니다. 정확히 내가 꿈꾸고 하고싶었던 일을 할 수 있게 도와주는 곳이라고 느낀다.Q. 최근에는 어떤 디바이스를 만들고 있는가.A. 흔히 인공지능이라고 하면 일종의 어시스턴트를 많이 떠올리는 것 같다. 개인적으로는 이 ‘어시스턴트'라는 것이 너무 범위가 넓고 거대한 느낌이다. 나는 조금 더 작고 가벼운 기술, 그리고 특정한 범위 내에서 나의 일상에 정말 도움을 주는 제품을 개발하고 싶었다. 처음에는 방에 무드 조명이 있는데 ‘이 조명이 좀더 스마트하다면’이라는 생각을 가지고 확장시켜나갔다. 피터팬에 등장하는 “팅커벨”이라는 캐릭터가 생각이 났고 원하는 분위기에 따라서 혹은 알람을 제공하기 위해 예쁘게 불빛을 밝혀주는 것이 초기 모델이었다. 가정에서 인공지능 스피커를 사용하는 사용자들은 스피커를 실상 똑똑하게 쓰지 못하는 경우가 많다. 심지어 꺼놓는 경우도 많이 보았다. 나 또한 구매 초기에는 열심히 사용하다가 요즘은 알람 기능 만을 사용하고 있다. 개인적으로 인공지능 스피커를 잘 사용하지 않는 이유가 현재의 사용성과 음성으로 정보를 전달한다는 한계 때문이라고 생각했다. 스피커는 음성 명령을 잘 알아듣지도 못할 뿐더러, 내게는 스피커의 부자연스러운 음성이 시끄럽게 느껴지기조차 했다. 이런 불편함을 개선하기 위해 무드 조명의 색 조합을 통한 정보 전달을 구상했다. 조명의 색깔로 전달한다면, 스피커처럼 음성이 다 끝날 때 까지 기다리지 않아도 되고, 더욱 빠르고 덜 성가신 방법으로도 정보를 전달할 수 있다고 생각한다. 프로젝트를 구체화하며 조명과 사물인터넷(IoT)에 대해 공부하고, 컨셉을 발전시키다 보니 사물인터넷을 통한 조명 컨트롤이라는 새로운 방향성이 생겼다.사진2. 이주현 님은 다양한 실험을 통해 최적의 디바이스를 개발하고 있다.Q. 스켈터랩스에 어떻게 입사하게 되었는지.A. 어릴 때 부터 아이디어를 내고, 그것을 실제로 구현해보는 다양한 활동을 좋아했다. 학부 시절에는 아이디어를 발제하고 이를 직접 만들어보는 소모임에도 참여하였다. 학부 전공이 전자공학이지만 인공지능 기술에 대한 관심도 컸다. 사실 인공지능은 소프트웨어 분야 아닌가. 그래서 졸업작품을 인공 지능 관련 디바이스로 정했을 때도 소프트웨어 관련 강의를 찾아 들어야했다. 그러다 현재 우리회사 하드웨어 엔지니어 파트의 리더를 맡고 있는 재경님을 만나게 되었다. 처음에는 아이디어를 실현하기 위한 기술 자문을 구하기 위해 뵈었는데, 재경님이 근무하고 계신 회사 얘기를 들으면서 입사에 대한 꿈을 키우게 되었다. 그렇게 우연히 스켈터랩스에 대해 알게된 것 같다.Q. 자발적으로 인공지능 관련 공부를 했다지만, 스켈터랩스에서 일하며 인공지능 기술 회사에 하드웨어 엔지니어로 근무하기가 녹록치않을 것 같다.A. 인공지능 기술을 비롯한 소프트웨어 전반의 공부를 계속 해야하는 것은 맞다. 그렇지만 스켈터랩스는 자발적으로 공부하기 좋은 문화를 갖추고 있고, 자연스럽게 최신 기술을 접할 수 있는 기회도 많다. 너와 나의 일을 규정짓고 나누기보다는, 무엇이든 스스럼 없이 질문하고, 함께 답변을 찾아 가는 분위기가 조성되어있다. 그래서 기술 하나를 물어보면 열을 가르쳐주려고 한다. A를 물어볼 때, 시간이 된다면 A부터 Z까지는 알아서 답변해주는 분위기 같다. Tech-Talk와 같은 사내 세미나를 통해서 강의 형태로 인공지능 기술에 대해 접하기도 한다. 또한 하드웨어 팀 내부적으로도 공부에 대한 필요를 느끼고  자체 세미나를 진행한다. 거창한 것은 아니지만, 우리가 스켈터랩스 기술에 대해 알아야 할 부분을 각자 공부하고 공유하는 자리였다. 이러한 과정이 버겁기 보다는 좋아하는 분야를 더욱 심층적으로 접할 수 있어 좋다.Q. 스켈터랩스에서 일하며 느끼는 좋은 점을 자랑한다면.A. 스켈터랩스는 ‘일단 해보자'라는 분위기가 있다. 아이디어를 내면, 시간과 재화를 제공해주고 시도해볼 것을 권장한다. 작은 실패에 연연해 할 필요도 없다. 해보고 아니다 싶을 때, 그 때 가서 접어도 늦지 않다, 라는 쿨한 문화가 있다. 나와 같이 새로운 것을 생각하고 만드는 것을 좋아하는 이들이라면, 이곳이 정말 이상적이다. 집에서 혼자 하던 것을 ‘일'로서 지원받으며 할 수 있으니까 말이다. 그리고 정말 눈치보지 않는 문화라는 점을 강조하고 싶다. 일하다 지칠 때면 블루룸(스켈터랩스에서 가장 큰 룸인데, 게임방으로 활용되고 있다)에서 게임을 할 수도 있고, 쇼파로 편하게 자리를 옮겨 일하기도 한다. 입사 초창기에 휴가에 대해서 미리 양해를 구하곤 했는데, 그럴 때마다 들은 말은 ‘알아서 할테니 걱정하지 말아라. 휴가썼다고 말도 하지 말고 떠나라' 였다. 이처럼 자율적인 문화에서도 각자 알아서 제 몫을 톡톡히 해내고 있다는 것이 스켈터랩스의 가장 멋진 점이라고 생각한다.Q. 반대로 가장 힘든 점은.A. 아무리 하드웨어 엔지니어 파트에 대한 지원이 있더라도, 우리는 어디까지나 ‘인공지능 기술’ 회사다. 그렇기 때문에 소프트웨어 엔지니어가 훨씬 많고, 프로그램 개발이 회사의 메인 테스크(Main Task)로 인식될 때가 많다. 전자공학을 전공했는데 인공지능 회사에 다닌다고 하면 의아해 하는 엔지니어들도 많다. 하지만 최근 하드웨어 단에서 인공지능을 작은 저전력 디바이스에 옮기려는 연구는 계속해서 진행되고 있다. 소프트웨어팀이 멋지게 구현한 어플리케이션 등의 서비스를 100퍼센트 전달할 수 있는 디바이스를 만드는 것을 목표로 하고 있다.사진 3. 스켈터랩스의 블루룸에는 각종 게임이 구비되어있고 밴드부 연습실로 활용된다.Q. 스켈터랩스에서 업무 외에 어떤 활동을 하고 있나.A. 밴드, 축구, 헬스동아리까지 하고 있다. 취미가 음악이라 대학교 때부터 밴드부로 활동했는데, 그때마다 공간의 필요성을 절감했었다. 악기 대여비도 만만치않게 들지 않나. 스켈터랩스 밴드인 Terkels는 공간과 악기를 모두 갖추고 있다. 심지어 PA(Public Address) 앰프와 공연용 스피커까지 구비되어 있다. 축구 동아리에서 매주 1회 풋살 대결을 펼치고, 점심 시간마다 헬스 동아리원들과 함께 헬스장에 간다. 이렇다보니 부모님한테 ‘놀려고 회사가냐'라는 핀잔을 들을 정도다.Q. 많은 동아리와 업무를 병행하는 것이 힘들지는 않은가.A. 전혀. 오히려 동아리 활동으로 더욱 친해진 팀원과 함께 머리를 맞대고 하는 업무이다보니 ‘일'이 아니라 일종의 ‘놀이'처럼 인식될 때가 있다. 그리고 스켈터랩스 특유의 문화가 겉으로는 느릿느릿 여유롭더라도 내부적으로는 치열한 부분이 있다. 축구동아리에 처음 참여했을 때 동아리원들이 ‘살살 뜁시다' 하더니 막상 경기 시작되자마자 엄청나게 공격적이더라. 살살 뛰는 사람은 한 명도 없었다. 무섭게 뛰고 공격하면서 골이 계속 터졌다. 헬스동아리는 최근에 생긴 동아리다. 여름맞이 몸을 만들기 위해서 여럿이 뭉쳐서 헬스장을 함께 간다. 헬스 자체가 함께 할 수 있는 운동은 아니지만, 그래도 시간을 정해서 함께 이동하다 보니 ‘오늘은 좀 운동하지말고 먹을까' 싶다가도 다른 분들이 가면 자극을 받게 되고, 더 열심히 운동하게 되더라. 일도 마찬가지다. 처음에는 ‘회사가 이렇게 놀게 해줘도 되나'했지만, 내부적으로 탄탄하게 서로 함께 놀고 일하며 자극과 영감을 받는 문화다.회사는 딱히 데드라인을 촉박하게 주지도 않고, 압박을 하는 경우도 없다. 그런데 다들 게임방에서 신나게 게임을 하다가도 다음 날이면 개발을 마친 결과물을 들고 온다. 자율적이지만 확실하게 자신의 업무에 대해 책임을 지는 문화가 형성되어 있다. 그렇다보니 나 또한 자연스럽게 동아리 활동을 하다가도 오늘 하루 내가 끝내야할 일로 정해놓은 것들은 마치고 퇴근하려 한다.Q. 회사에 게임방이라니, 게임방 얘기를 듣고싶다.A. 게임을 좋아하는 사람들이 많다 보니 닌텐도를 비롯해서 엑스박스(Xbox), 플레이스테이션(Playstation)을 비롯한 각종 게임기가 마련되어 있다. 다트와 탁구대, 당구대까지 준비되어 있다. 사무실을 성수로 이사하면서 테드님(Ted Cho, 스켈터랩스의 대표인 조원규 님은 사내에서 테드님으로 불린다)이 ‘모두가 놀 수 있는 공간을 만들겠다'라고 했었는데, 정말 놀이터를 만들어주시더라. 덕분에 점심시간마다 삼삼오오 모여서 각종 게임과 탁구, 당구를 즐기고 있다.Q. 하드웨어 엔지니어로서 최종 목표가 있다면.A. 테드님이 우리에게 자주 하는 말 중 하나가 ‘Don’t be evil’이다. 이 말은 사실 구글의 모토인데, 스켈터랩스의 모두가 공감하는 얘기다. 기업이 이윤을 추구할수록 소수에 대한 외면이 발생하기도 하고, 기술 기업으로서 수익 창출 만을 목표로 하면 정작 일상을 어떻게 더욱 편리하고 윤택하게 만들어줄 수 있는지를 쉽게 망각하는 것 같다. 사악해지지 않으면서, 정말 우리의 삶을 나아지게 하는 방법을 계속해서 고민하고 싶다.#스켈터랩스 #사무실풍경 #업무환경 #사내복지 #기업문화 #팀원인터뷰 #팀원소개 #팀원자랑
조회수 295

프로그래밍 수업의 모든 것.

안녕하세요 엘리스입니다. :)엘리스의 프로그래밍 수업은 누구에 의해서, 어떻게, 어떤 생각을 바탕으로 만들어질까요?미래를 이끌어나갈 컴퓨터 사이언스 기술과 그 근간이 되는 교육 사이에서 좋은 프로그래밍 수업을 만들기 위해 치열하게 고민하는 엘리스의 코스 매니저가 직접 이야기합니다! 마침 엘리스는 코스 매니저 채용 중에 있으니 관심이 있다면 눈여겨 봐주세요!코스 매니저가 관여한 프로덕트로 인하여 사용자가 성장을 하고 있다면 그것은 충분히 의미 있는 일.안녕하세요 저는,트라우마를 극복한 프로그래밍 수업 크리에이터.Q. 자기소개 부탁드려요.A. 엘리스의 프로그래밍 과목을 만드는 코스 매니저 이용희입니다.Q. 엘리스에서 일하게 된 이유는 무엇인가요?A. 원래는 프로그래밍에 대한 트라우마가 있었어요. 하지만 기술 창업에 대한 꿈이 있었기 때문에 프로그래밍은 극복해야 할 산이었죠. 엘리스는 가장 뛰어난 기술자들이 모여 창업한 스타트업이에요. 당연히 기술 창업을 가장 가까이에서 경험할 수 있는 매력적인 곳으로 느껴졌죠. 그리고 프로그래밍 교육을 제공한다는 것 역시 기회로 느껴졌어요. 저와 같이 프로그래밍을 미워하고 두려워하는 사람들에게 보다 쉽게 배울 수 있는 환경을 마련해주고 싶다는 기대로 일을 시작하게 되었습니다.Q. 두려운 대상을 향해 몸을 던지셨군요! 그런데 코스 매니저가 프로그래밍을 몰라도 되나요?A. 많이 알면 알수록 당연히 좋아요. 많이 알고 있을수록 시도할 수 있는 것도 많고 학생에게 전달해줄 수 있는 것은 더욱더 많기 때문에요. 하지만 최소한으로는 Class가 뭔지 알고 있으면 OK. 예를 들어서 코드를 보고 이 코드가 어떤 목적을 갖는지 알 수 있으면 직접 코딩을 하지는 못한다고 해도 괜찮아요.Q. 코스 매니징 외에도 라이브 수업 참여, 조교, 챌린지 사회자 등 많은 역할을 하셨는데 이유가 있나요?A. 좋은 수업을 만들기 위한 첫 번째 방법은 코스를 만드는 모든 과정에 참여하는 사람들의 역할을 직접 체험해 보는 것이라고 생각했어요. 학생으로서, 조교로서, 사회자나 라이브 어시스턴트로서. 이렇게 하니까 학생으로서 수업을 접할 때의 감상은 무엇인지, 조교로서 가르쳤을 때는 어떤 어려움이 있는지를 알 수 있었어요. 라이브 수업 어시스턴트로 참여했을 때는 방송하시는 선생님들의 애로사항을 알 수 있겠더라고요.코스 매니징의 정수.프로그래밍적 성장을 도움으로써 가치를 만들어 냅니다.Q. 코스 매니징의 A to Z는? 구체적인 업무 프로세스가 궁금해요.A. 크게 기획 - 모집 - 제작 - 분석의 네 단계로 이루어져 있어요. 1. 수업 기획 -  어떤 과목을 만들 것인가? 주차별로 무엇을 다룰 것인가? 흥미로운 콘텐츠는? 2. 선생님, 조교 모집 - 엘리스가 구상한 수업을 가장 잘 전달할 수 있는 선생님과 조교를 모집. 3. 수업 제작 및 운영 - 실습 문제, 강의 자료 등을 엘리스의 색깔로 제작하여 수업을 운영. 4. 데이터 분석 - 학생들의 피드백과 데이터를 다음 수업의 발전 및 교육자와의 관계 개선에 반영.Q. 업무 방식은? 어떤 메리트가 있나요?A. 처음부터 끝까지 모든 과정을 주도해나가는 방식이에요. 어떤 회사를 가도 프로덕트의 end to end 프로세스를 전부 경험하기는 어려운데 엘리스에서는 그 전 과정을 경험할 수 있어요. 저는 이러한 경험이 교육 업계나 특정 프로덕트에만 적용할 수 있는게 아니라 다른 업계에 간다고 하더라도 충분히 전환될 수 있는 좋은 경험이라고 생각해요.Q. 미래 산업의 근간이 될 교육을 직접 만든다는 중책을 맡고 계신다고 생각하는데요, 좋은 프로그래밍 수업을 만들기 위해 어떤 노력들을 하시나요?A. 그런 영향을 미칠 수 있다는 게 무서운 일인 것도 같아요. 어떤 사람들은 엘리스를 통해서 프로그래밍을 처음 접하는 것일 수도 있는데 그 경험이 불쾌했다면 앞으로 프로그래밍을 배울 생각이 전혀 들지 않을 수도 있는 거잖아요. 그래서 최대한 다양한 피드백을 받아서 수렴하려고 해요. 외적으로는 대학강의, 수많은 수업들을 참고해요. 여러 강의를 보다보면 좋은 예도 많지만 모든 수업이 재미있지는 않아요. 중간에 듣다 마는 경우도 있고요. 그럴 때마다 내가 왜 중단했고 어떤 요소를 바꾸면 엘리스에서는 학생들이 끝까지 들을 수 있을까 고민해서 반영하려고 하죠.Q. 언제 보람을 느끼나요?A. 내가 관여한 프로덕트가 누군가에게 임팩트를 만들어내고 나뿐만 아니라 프로덕트를 사용하는 사람들이 성장을 하고 있다면 그것은 충분히 가치 있는 일인 것 같아요. 저희 플랫폼에서는 대시보드를 통해서, 그리고 학생이 코드를 어떻게 짜고 있는지 보면서 그 결과를 가시적으로 확인할 수 있어요. 누군가 제가 만든 코스를 수강함으로써 실질적으로 성장하는 게 눈에 보일 때 가장 큰 보람을 느끼는 것 같아요.한 번은 한 선생님께서 학생으로부터 ‘선생님 덕분에 취업할 수 있었어요’라는 메시지를 받은 것을 엘리스와 공유해주셨는데 그때 정말 행복하더라고요. 이게 엘리스가 추구하는 거다,라는 생각을 했어요. 엘리스도 하나의 커뮤니티이고 싶거든요. 이 경우에는 학생-선생님-엘리스가 서로의 영향으로 좋은 결과를 만들어 낸 거죠. 이런 접점을 앞으로 더 많이 만들려고 생각하고 있어요.대시보드에 나타나는 학생들의 학습 현황 및 성취도.엘리스는 이런 팀.가치, 성장, 사람. 포기할 수 없는 세 가지가 있는 곳.Q. 함께 일하는 동료들은 어떤 사람들인가요? 총평을 하자면?A. 항상 내가 최고의 사람들과 함께하고 있다라는 확신이 있어요. 각자 자기 분야에서 최고의 실력을 가진 사람들과 함께 일한다는 것만으로도 큰 자극이 되죠. 프로그래밍이든 스타트업 생존 노하우든 항상 뭔가를 새롭게 배우고 성장하게끔 동기부여를 해주는 사람들이에요. 저는 트라우마가 있었을 정도로 프로그래밍을 두려워했지만 이들과 함께 일하며 작은 피드백을 하나 듣는 것만으로도 제 실력이 빠르게 성장한다는 것을 몸소 느낄 수 있었어요. Q. 엘리스의 분위기, 팀 문화는 어떤가요?A. 새로운 것에 도전하는 것을 환영하는 수평적이고 자유로운 팀. 인턴도 아이디어를 제시할 수 있어요. 이 다음이 더 중요한데, 아이디어에서 그치는 게 아니라 활발한 피드백이 오가요. 아이디어를 실행하기 어렵다고 판단하더라도 왜 그렇고 어떻게 발전시킬 수 있는지 이야기하죠. 실행하게 되었을 때는 아이디어를 제시한 사람에게 일에 대한 권한이 전적으로 주어지고요. 저도 처음엔 파트타임 인턴이었지만, 이런 팀문화 덕분에 계속해서 업무 범위를 확장하고 제 역량을 키울 수 있었어요.코스 매니저 채용.Generalist & Infinite LearnerQ. 현재 코스 매니저를 구인하고 있는데요. 코스 매니저에 적합한 성향이 있나요?A. 두 단어가 떠오르네요. Generalist, 그리고 Infinite Learner. 깊게 한 분야를 아는 사람보다는 얕고 넓게 아는 사람이 더 적합하다고 생각해요. 다르게 말하면 새로운 것을 시도하는 것을 좋아하고 새로운 것을 접할 때 포용력이 높은 사람이요. 두 번째로는 배움에 재미를 느끼는 사람. 엘리스는 교육 스타트업이고 코스 매니저는 직접 교육의 경험을 만드는 사람이니 스스로가 배움에서 행복을 느끼는 사람이라면 훨씬 더 재미있게 일할 수 있겠죠. 한 가지 덧붙이면, 데이터 분석을 배우고 싶은 분께 엘리스는 최고의 장소입니다.Q. 코스 매니저로서 갖추고 있으면 좋은 역량이나 자질이 있다면?A. 소통 능력과 균형 감각. 코스 매니저는 수업을 만드는 모든 단계에서 다양한 이해당사자들과 일하게 돼요. 이들과 원활하게 소통하고 의견을 공유하는 게 중요하죠. 그리고 다양한 사람들 사이에서 최고의 균형을 찾아내는 것도 중요해요. 예를 들어서 선생님의 경우 개발만 해왔고 교육이라는 것을 접해본 적이 없는 분들이 대부분이고, 학생은 프로그래밍을 처음 접하면 그 수업이 좋은 건지 아닌지 평가하기 어려워요. 때문에 코스 매니저가 이 둘 사이에 다리를 놓는 중재자의 역할을 하기 위해서는 다양한 시각에서 볼 수 있는 균형 감각이 필요하다고 생각해요.
조회수 1862

비트윈의 스티커 시스템 구현 이야기

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

소스코드 리뷰에 대한 짧은 이야기...

개발자와 개발 조직에게 소스코드 리뷰는 필수적이다. 팀간의 협업과 대화를 보다 원활하게 만들어 주는 매우 필요한 절차이다. 슬랙과 같은 협업도구가 명쾌하게 의미 있게 활용되려면 개발팀 간의 소스코드 리뷰는 필수적으로 수행되는 것이 좋다.매우 당연한 이야기이지만, 소스코드 리뷰는 거북하고 불편하고 어렵고 힘들다. 그럼에도 불구하고 필수적인 이벤트가 되어야 하는 이유가 너무도 많다. 개발자들에게 코드리뷰에 대한 이슈를 설득하고 실제 행위를 발생시키는 것은 정말 어려운일이다. 더군다나 뜬금없이 코드리뷰 이야기를 회사나 팀리더에게서 갑자기 듣는다면 개발자는 매우 불편해 한다. 그것은 매우 당연한 반응이다. 그러므로, 가능하다면 팀 세팅 초기 시부터 이 소스코드 리뷰 문화는 만들어질 수 있게 노력하는 것이 최선일 것이다.초기에 세팅된다면 그 후에 들어오는 팀원들은 자연스럽게 그 문화에 익숙해진다. 이런 일련의 작업들은 결국 조직과 팀의 단결과 협력, 향후 유지보수에 매우 긍정적인 효과를 준다.매우 당연하지만 개발자들은 팀에 소속되고 빠져나가기를 반복한다. 이를 두려워하지 않는 방법 중에 가장 먼저 선택할 수 있는 것이 바로 코드 리뷰라는 행위다. 인수인계와 유지보수를 위해서 소스코드 리뷰를 각 단계별에 배치해두고, 그 시간을 투자하는 것을  아까워하지 않도록 하자.그렇다면, 소프트웨어의 본체인 소스코드를 타인이 리뷰한다는 것이 왜 어려울까? 그것은 소스코드는 언제나 완성상태가 아니라는 점 때문이다. 개발자의 생각은 무언가 다양한 변화를 예측하고 있고, 그 상세한 준비를 담고 있다. 언제나 소스코드는 완성 상태가 아니라, 변화되어야 하는 시간의 축을 담고 있기 때문이다.하지만, 소프트웨어 품질이 중요한 현재의 시점에서 본다면, 코드 리뷰라는 행위는 정말 필수 불가결한 행위에  해당한다고 생각한다.이런 필수적인 코드리뷰는 그 형태와 범위에 대해서 팀 내부에 잘 정의되어야 한다.그래서, 보통 이 코드리뷰를 어떻게 할 것인가에 대해서 조직이나 담당하는 사람의 경우에는 명쾌한 판단 기준이 있어야 한다. 그러한 ‘판단기준’을 가져야만 명확한  리뷰될 수 있다.이를 두고, 디자이너에게는 크리틱(critique-비평)이 있고, 개발자에게는 코드리뷰가 있다고 정의한다.좋은 비평을 받고 좋은 리뷰를 하려면 다음의 3가지 원칙이 필수이다.1. 리뷰는 언제나 상호 합의가 되어진 상황에서 진행되어야 한다.2. 리뷰어의 해당 결과물에 대해서 객관성을 가지고 서로 인지해야 한다3. 개발자 자신의 작업물에 대해서 정말 객관적으로 바라볼 수 있는 작성가가 선정되어야 한다.특히, 소프트웨어 코드는 정량적인 검토와 정성적인 검토를 구분해야 한다. 이 영역의 구분이 모호해지면, 리뷰는 그 방향성을 상실하게 된다. 그중에 특히, 정량적인 검토와 기본적인 규칙들은 가능한 자동화하고, 소스 형상관리 도구에서 기본적인 것들의 규칙들을 지키도록 권장하여야 한다. 최소한 이 정량적인 것만 자동화하고  규칙화해도 소프트웨어의 품질은 급상승한다.하지만, 코드는 논쟁을 발생시키고, 어떤 것이 우선적인지에 대해서 서술하기 매우 어렵다. 이러한 점은 정성적인 부분에 대해서 검토할 때에 고민하자.코드리뷰의 정도는 어느 정도 해주어야 하는가?그 전부터 주목하는 개발 방법론의 추세는 ‘테스팅’을 주로 하고, SRS와 같은 요구사항에 집중하기 보다는, TDD와 같은 방법으로 완성 산출물을 높이는 방법을 현재에는 주로 사용하고 있다.그것은 과거에는 요구사항을 통해서 결과물이 완성되는 SI성 개발이 주로였다면, 현재에는 요구사항은 계속 변화하고 버그 없는 결과물이 중요시되는 테스트를 얼마나 더 집중적으로 하느냐에 따른 웹서비스의 시대이기 때문에 그 방향성은 시대에 따라서 변화를 많이 하였다. 그래서, 슬프지만, 당장의 성과물을 위해서라면 코드리뷰보다는 테스팅에 집중하는 것이 더 효율적이다. 빠르게 고속 개발하고 테스트를 통해서 버그를 찾은 다음 수정하는 것이 ‘특정 기능들을 나열하고 기능을 만족하는 소프트웨어’의 경우에는 테스트 주도 개발 방법이 가장 적합하다고 할 수 있다.물론, 이러한 방향성이나 전체적인 틀에 대해서는 아키텍트가 잘 결정하여야 한다. 내가 속한 개발 결과물이 어떤 결과물이냐에 따라서 이 방법은 혼용되어져서 사용되어야 하기 때문이다.하지만, 이번 글의 주목적은 코드리뷰. SRS중심이건, TDD중심이건. 코드리뷰는 중요하다는 것을 강조하고 싶다. 특히, 코드리뷰는 ‘기능 나열’이 아닌, 어느 정도 이상의 복잡도나 코드 품질이 필요한 경우에는 필수적으로 수행하는 것이 매우 현명한 행동이다.물론, 코드리뷰 행위가 불필요한 업무들도 많다. 정해져 있는 단순한 업무를 수행하는 경우에는 굳이 할 필요 없다. 국내에서 SI를 하는 경우에는 대부분 코드리뷰가 필요 없는 업무를 하는 소프트웨어 개발자들이 절대 다수인 경우도 많이 보았다.일반적인 SI의 형태라면 워크 스루의 형태만 적합하다. 특정 도메인에 매몰되어 있고, 처리방법이 명쾌하기 때문에, 해당 경험들을 교환하는 것으로도 충분하기 때문이다. 그리고, 자동화된 테스트 수행방법을 최대한 갖추어두는 것이 가장 현명하다.그러므로, 코드리뷰는 어느 정도 솔루션이나 서비스 등을 고려하고 있는 곳에서 더욱 적합하다고 정의한다.코드리뷰는 특정 제품이나 서비스를 발전적으로 지향하고 있는 경우라면 필수적으로 선택해야 한다. 하지만, 일부 제품의 경우에는 발전적인 지향이 굳이 필요 없는 제품 라인업을 가진 경우에도 굳이 수행할 필요 없다.그 경우에는 선택적인 코드리뷰를 지향하면 된다. 비용상의 문제 때문에 굳이 코드리뷰를 억지로 진행할 필요는 없는 경우도 많다. 대부분의 소프트웨어 개발은 테스트 케이스를 잘 만들고, 통과시키는 것으로써 충분한 신뢰를 가지면 충분한 경우가 대부분이다.특히, 시장이 고착상태이거나, 특별한 변화의 폭이 없다면, 그 정도로 충분한 경우가 된다. 다만, 글로벌 서비스나 웹서비스 등의 지속적인 확장이 필요한 경우라면, 코드리뷰는 필수라고 할 수 있다.코드리뷰가 필요 없는 경우 체크리스트는 다음의 5가지 정도를 체크해보자.1. 특정 도메인만 다루는 팀이나 회사의 개발팀인가?2. 지난 2~3년 정도 솔루션이 크게 변한 것이 없으며, 향후로도 기업이나 팀에서 투자가 없을 예정이다.3. 현재 개발자들이 해당 솔루션에 대한 개발일을 5년 이상하고 있다.4. 기능 위주의 SI성 업무를 주로 처리하고 있으며, 복잡한 알고리즘은 존재하지 않는다.5. 비용과 일정상 개발팀에게 리소스 투여가 불가능하다위의 사례에서 1개 이상이라도 체크된다면, 코드리뷰는 성립하기 힘들다. 대부분 단념하고, TDD나 테스트 케이스를 가능한 많이 축적하여 소프트웨어 품질을 올리기를 권장한다.코드리뷰가 필요한 경우의 체크리스트도 다음의 5가지 정도를 체크해보자.1. 다국어와 시장이 다변화된 환경에서 소프트웨어가 구동되어야 한다.2. 코드의 복잡도가 높으며, 단순 기능 나열의 요구사항이 아니라, 소프트웨어 아키텍처가 별도로 구성되기 시작하였다.3. 사용자의 경험성을 증가하기 위하여 매우 많은 변화가 예측된다.4. 현재 개발 중인 서비스는 중단 없이, 지속적으로 발전되어야 하는 서비스이다.5. 목표 요구사항이 계속 변화하고 있고, 프레임워크를 지향하여 소프트웨어 품질의 요구사항이 매우 중요하다.위의 케이스에서 하나라도 해당이 된다면, 코드리뷰는 매우 효과적으로 소프트웨어에 의미 있는 결과물들을 얻어 내기 위한 좋은 방법이 된다.하지만, 다음과 같은 경우도 같이 고려하여야 한다.코드리뷰의 정도와 질에 대한 검토 리스트의 최소 체크리스트는 다음의 3가지이다. 물론, 이 정의는 조직 내의 아키텍트나 아키텍트 롤을 하는 사람이 결정하는 것이 좋다.1. 실험적인 코드인가?2. 1~2명 이상이 공동으로 작업하는 코드인가?3. 향후 버려질 가능성이 높은 코드인가?코드리뷰를 하지 않는 경우에는 해당 코드의 repository나 디렉터리를 완전하게 분리하고, 리뷰가 안된 코드를 명쾌하게 구분할 수 있어야 한다. 그리고, 그 정보는 팀 전체에게 공개되어야 한다.가장 첫 번째는 코딩규칙 가이드라인의 준수 여부를 체크하는 것이다.개발자들 간의 상호 중요한 것은 스타일 가이드이다. 하지만, 정말 지키기 어려운 것 또한 스타일 가이드라고 할 수 있다. 하지만, 스타일 가이드는 가능한 준수해야 한다. 하지만, 100% 준수하려는 것은 매우 비효율적인 상황을 만들 수 있다. 하지만, 이 경우에 최소한 리뷰어가 제시하는 기준이나 변경 방향에는 대부분 수긍하는 것이 가장 현명하며, 이 부분은 해당 팀의 가장 경험이 풍부한 사람이 리드하는 것이 좋다.그래서, 소프트웨어 개발에는 경험이 풍부한 아키텍트의 역할과 선임의 역할이 가장 중요하다. 소셜에서 이야기하는 가장 중요한 포인트는 이런 경험이 풍부한 선임 개발자가 있다면, 돈이 얼마가 들더라도 ‘개발팀’에 모셔야 한다! 가 정답일 것이다.아직까지 이 부분은 ‘공학’으로 해결할 수 없고, ‘엔지니어링’과 ‘경험’에 의존할  수밖에 없다.주석의 경우에도 ‘가독성’이 충 부한 코드에는 서술할 필요 없다. 이 부분에 대해서는 꾸준한 팀원들 간에 코딩 문화에 대해서  커뮤니케이션하면서 주석의 범위에 대해서 공론화하는 것이 현명하다. 그래서, 소프트웨어 개발은 대부분이 ‘커뮤니케이션’이고 ‘소통’이다. 그래서, ‘팀워크’이 가장 중요한 것이고. 변수의 명칭에 대해서도 ‘명확’하다는 선에서 합의해야 한다.테스트가 쉽지 않은 구조는 다른 문제를 야기한다. Junit과 같은 단위 테스트 도구로 손쉽게 정의가 가능한 구조가 아니라면, 변경해야 한다.코드리뷰 후에 분명하고 타당한 지적에도 고집이 세서 변화가 없는 경우에는 한두 번 이야기하고 더 이상 변화가 없다면, 포기하고. 해당 코드를 격리하여 관리하는 것이 현명하다.  팀원들 간에 감정이 상하는 것이 더 위험하다. 사람은 변하지 않는다 감정에 대한 다툼이나 기대를 할 필요가 없다.UI가 중요한 코드는 해당 코드들이 급변할 가능성이 농후하다. 처음부터 공을 들여서 추상화를 실현하지 않으면, 해당 코드 때문에 프로젝트가 심각해질 수 있다. 사용자에게 더 좋은 경험을 전달하려고 하면, UI코드는 계속 변화를 일으킨다.테스트 코드 여부? 로직에 대한 검토, 변수 네이밍 검토와 레이아웃에 대한 것들? 에 대해서는 다음과 같이 판단하고 체크해보자.코드리뷰는 대부분 ‘직관’에 의존한다. 그래서, 정말 어렵고. 경험이 풍부한 사람이 할  수밖에 없다. 다만, 이러한 코드 리뷰 시의 체크리스트 항목을 몇 가지 간단하게 정리할 수 있다. 최소한의 2가지는 꼭 지키자.코드 리뷰 시의 필수 내용 두 가지는 다음과 같다.1. 코드 검토는 1시간 이내에 끝낼 분량으로 검토한다.2. 코드는 200라인 이상을 한 번에 검토하지 마라이 기준이 어겨지면, 리뷰어는 제대로 된 리뷰를 하기 어려울 것이다.  그리고, 이러한 리뷰를 하는 동안 기능에 대한 검토 체크사항에 대해서 나열해 보면 다음과 같이 나열이 될 수 있을 것이다.1. 시스템의 요구사항이 제대로 반영되었는가?2. 시스템의 설계의 규격대로 구현되었는가?3. 과도한 코딩을 하고 있지 않는가?4. 같은 기능 구현을 더 단순하게 할 수 있는가?5. 함수의 입출력 값은 명확한가?6. 빌딩 블록들( 알고리즘, 자료구조, 데이터 타입, 템플릿, 라이브러리, API )등이 적절하게 사용되었는가?7. 좋은 패턴과 추상화( 상태도, 모듈화 )등을 사용해서 구현하고 있는가?8. 의존도가 높은 함수나 라이브러리 등의 의존관계에 대해서 별도 기술하고 있는가?9. 함수의 반환(exit)은 한 곳에서 이루어지고 있는가?10. 모든 변수는 사용 전에 초기화하고 있는가?11. 사용하지 않는 변수가 있는가?12. 하나의 함수는 하나의 기능만 수행하고 있는가?또한, 스타일과 코딩 가이드에 대해서고 검토하고 리딩을 해야 한다.1. 코딩 스타일 가이드를 준수하고 있는가?2. 각 파일의 헤더 정보가 존재하는가?3. 각 함수의 정보를 코드에 대해서 설명하기에 충분한가?4. 주석은 적절하게 기술되어있는가?5. 코드는 잘  구조화되어있는가? ( 가독성, 기능적 측면 )6. 헤더, 함수 정보를 도구로 추출해서 자동으로 문서화할 수 있는 구조인가?7. 변수와 함수의 이름이 일관되게 기술되어 있는가?8. 프로젝트의 가이드를 통한 네이밍 규칙을 준수하고 있는가?9. 숫자의 경우 단위에 대해서 기술하고 있는가?10. 숫자를 직접 서술하지 않고, 상수를 사용하고 있는가?11. 어셈블리 코드를 사용하였다면 이를 대체할 방법은 없는가?12. 수행되지 않는 코드는 없는가?13. 주석 처리된 코드는 삭제가 되었는가? ( 버전 체크가 되었는가? )14. 간결하지만 너무 특이한 코드가 존재하는가?15. 설명을 보거나 작성자에게 물어봐야만 이해가 가능한 코드가 있는가?16. 구현 예정인 기능이 있다면, ToDo주석으로 표시되어 있는가?가장 중요한 아키텍처에 대한 검토를 잊으면  안 된다.1. 함수의 길이는 적당한가? ( 화면을 넘기면  안 된다. )2. 이 코드는 재사용이 가능한가?3. 전역 변수는 최소로 사용하였는가?4. 변수의 범위는 적절하게 선언되었는가?5. 클래스와 함수가 관련된 기능끼리 그룹화가 되었는가? ( 응집도는 어떤가? )6. 관련된 함수들이 흩어져 있지 않는가?7. 중복된 함수나 클래스가 있지 않는가?8. 코드가 이식성을 고려하여 작성되었는가? ( 프로세스의 특성을 받는 변수 타입이 고려되어있는가? )9. 데이터에 맞게 타입이 구체적으로 선언되었는가?10. If/else구분이 2단계 이상 중접되었다면 이를 함수로 더 구분하라11. Switch/case문이 중첩되었다면 이를 더 구분하라12. 리소스에 lock이 있다면, unlock은 반드시 이루어지는가?13. 힙 메모리 할당과 해제는 항상 짝을 이루는가?14. 스택 변수를 반환하고 있는가?15. 외부/공개 라이브러리 사용하였을 경우에 MIT 라이선스를 확인했는가? GPL의 경우에는 관련된 영역에서만 사용해야 한다.16. 블로킹 api호출시에 비동기적인 방식으로 처리하고 있는가?당연하겠지만, 예외처리 관련 체크리스트도 제대로 검토해야 한다.1. 입력 파라미터의 유효 범위는 체크하고 있는가?2. 에러코드와 예외(exception)의 호출 함수는 분명하게 반환되고 있는가?3. 호출 함수가 어려와 예외처리 코드를 가지고 있는가?4. Null포인트와 음수가 처리되는 구조인가?5. 에러코드에 대해서 명쾌하게 선언하고 처리하고 있는가?6. switch문에 default가 존재하고, 예외처리를 하고 있는가?7. 배열 사용시에 index범위를 체크하는가?8. 포인트 사용시에 유요한 범위를 체크하는가?9. Garbage collection을 제대로 하고 있는가?10. 수학계 산시에 overflow, underflow가 발생할 가능성이 있는가?11. 에러 조건이 체크되고 에러 발생 시 로깅 정보를 남기는가?12. 에러 메시지와 에러코드가 에러의 의미를 잘  전달하는가?13. Try/catch 에러 핸들링 사용방법은 적절하게 구현되었는가?요즘 프로그램은 대부분 이벤트성으로 구동되지만, 시간의 흐름에 대한 체크는 프로그램의 뼈대를 이루게 된다. 이 부분에 대해서도 제대로 검토해야 한다.1. 최악의 조건에 대해서 고려하였는가?2. 무한루프와 재귀 함수는 특이사항이 아니라면 없어야 한다.3. 재귀 함수 사용시에 call stack값의 최댓값이 고정되어 있는가?4. 경쟁조건이 존재하는가?5. 스레드는 정상 생성, 정상 동작하는 코드를 가지고 있는가?6. 불필요한 최적화를 통해서 코드 가독성을 희생하였는가?7. 임베디드의 경우에도 최적화가 매우 중요하지 않다면, 가독성을 더 중요하게 해야 한다가장 중요한 검증과 시험에 대해서도 제대로 인지하여야 한다. 그리고, 테스트를 위해서 가능한 최대한 자동화를 하기 위한 방법들을 이용해야 한다.1. 코드는 시험하기 쉽게 작성되었는가?2. 단위 테스트가 쉽게 될 수 있는가?3. 에러 핸들링 코드도 잘  테스트되었는가?4. 컴파일, 링크 체크 시에 경고 메시지도 100% 처리하였는가?5. 경계값, 음수값, 0/1등의 가독성이 떨어지는 코드에 대해서 충분하게 경계하고 있는가?6. 테스트를 위한 fault 조건 재현을 쉽게 할 수 있는가?7. 모든 인터페이스와 모든 예외 조건에 대해서 테스트 코드가 있는가?8. 최악의 조건에서도 리소스 사용은 문제가 없는가?9. 런타임 시의 오류와 로그에 대비한 시스템이 있는가?10. 테스트를 위한 주석 코드가 존재하는가?간혹 등장하는 하드웨어에 대한 테스트도  마찬가지이다. 다음과 같은 기준들을 통해서 검토해야 한다.1. I/O 오퍼레이션 코드에 대한 테스트로 하드웨어가 정상적인 동작을 보장하는가?2. 최소/최대 타이밍 요구사항에 대해서도 하드웨어 인터페이스가 충족하는가?3. 멀티 바이트 하드웨어 레지스터가 read/write오퍼레이션 중에도 값이 바뀌지 않음을 보장하는가?4. 시스템이 잘 정의된 하드웨어 상태로 리셋하는 것을 S/W가 보장하는가?5. 하드웨어의 전압이 떨어지거나 전원이 차단되는 경우에 잘 처리하는가?6. 대기모드 진입 시와 빠져나 올 때에 시스템이 옳게 동작하는가?7. 사용하지 않는 인터럽트 벡터가 에러 핸들러에 연결되어 있는가?8. EEPROM손상(데이터 깨짐)을 막기 위한 메커니즘이 있는가? ( 쓰기 동작 중 powe loss)등구체적으로 코드리뷰를 하고자 한다면, 다음의 코드리뷰에 대한 기법과 적당한 방법을 다음과 같이 설명할 수 있다.이러한 코드 리뷰를 위한 몇 가지 방법들이 알려져 있다. 그것들을 몇 가지 정리하여 보면 다음과 같다. 코드 인스펙션은 가장 정형화된 기법으로 전문화된 코드리뷰팀을 통해서 구분하는 방법이다. 이 방법은 리소스가 풍부하고, 일정에 여유가 있는 경우에만 사용이 가능하다. 대부분 대기업이나 대형 포털에서 구현 가능한 방법이라고 할 수 있다. ( 이런 곳에 있다면 행복해 하자. ~.~ ) 하여간, 비용과 일정 등이 있다면 이 방법이 현명하다. 그리고, 코드리뷰에 대한 품질에 대해서 정량적인 보고와 구성을 만들어 낼 수 있다는 것은 코드 인스팩션의 가장 좋은 장점이다. 이 코드 인스팩션을 하기 위한 롤을 구분하면 다음과 같이 4가지 롤로 구분할 수 있다.1. ModeratorA. 실질적인 매니저로 팀 간의 인터페이스와 리소스, 인프라를 확보하고, 프로세스에 대한 정의와 산출물의 정리를 담당한다.2. ReaderA. 각 산출물을 읽고, 리뷰하고, 방향성을 제시한다. 보통, 지식이 많은 사람이 담당한다.3. Designer/CoderA. Reader의 지시에 따라서 코드를 검증하고 잠재적인 발견 등의 수정 방안을 만든다.4. TesterA. 진행 중인 코드와 권장 수정 코드에 대해서 검증한다.그리고, 코드 인스펙션은 다음과 같은 6단계로 진행된다.1. PlanningA. 계획 수립2. OverviewA. 교육과 역할 정의3. PreparationA. 인터뷰와 필요한 문서 습득, 툴 환경 구축4. Meeting(Inspection)A. 각자의 역할대로 수행5. ReworkA. 보고된 Defect 수정6. Follow-upA. 보고된 Defect가 수정되었는지 확인이러한 절차를 통해서, 코드 인스팩션이 수행되면, 상당히 명쾌한 리뷰가 진행되게 된다. 하지만, 일정과 비용 문제 때문에 이 작업은 대부분의 스타트업에서는 선택하기 어렵다. 그래서 사용하는 방법 중의 하나가 팀 리뷰이다.팀 리뷰는 일정한 계획과 프로세스만 따르는 방법으로, 코드 인스펙션보다는 좀 덜 정형화된 방법으로 진행한다. 보통은 일주일에 한번 정도 팀 리뷰를 수행하거나, 특정 모듈이나 기능이 완료되는 시점을 기준으로 테스트 결과를 가지고 리뷰를 하는 방법을 사용한다.또한, 위험하거나 의견이 필요한 경우에도 팀 리뷰는 유용하다. 일반적인 팀에서 사용하는 방법이다.하지만, 이 역시. ‘리뷰’에 대한 제대로 된 인식이 없다면, 적용하기 어렵다. 그래서, 가끔 사용되는 방법이고, 과거 국내 SI업체들이 주로 사용하던 방법 중의 하나가 ‘웍쓰로’이다.웍 쓰루(Walkthrough)는 단체로 하는 코드 리뷰 기법 중에 비정형적인 방법으로, 발표자가 리뷰의 주제나 시간을 정해서 발표하고 동료들로부터 의견이나 아이디어를 듣는 시간을 가지는 방법으로써 주로 사례에 대한 정보 공유나 아이디어 수집을 위해서 사용하는 방법이다.이 방법은 ‘특정 도메인’에 종속된 코드를 만들거나, 비슷한 SI성 형태의 업무를 수행하는 경우에 적합하다. 그래서, 국내의 SI업체에서는 적극적으로 사용되면 좋겠지만. 이 ‘시간’마저도 부정확하고, 갑을병정의 SI체게에서 ‘정보공유’나 ‘아이디어 수집’과 같은 커뮤니케이션이 자유롭게 일어나는 것은 매우 힘들다.이 웍 쓰루는 동일한 조직 내에서 동일한 목적의식이 분명한 팀에서나 활용이 가능한 방법이다. 웍 쓰루를 SI에서 시도한 경우에는 대부분 실패했거나, 목적의식이 다르기 때문에 불분명한 결론들이 대부분 도출되었다.대부분의 국내 스타트업이나 IT 전문기업들은 ‘리뷰’에 대해서 상급 관리자들이 제대로 허락을 해주지 않는다.대부분은 팀내에서 어떻게든 자체적으로 해보려고 한다. 그래서, 팀장의 권한 선에서 적절하게 리뷰를 하는 방법 중의 하나가 Peer review or over the shoulder review방법이다. 이 방법은 보통 2~3명이 진행하는 코드리 뷰로 코드의 작성자가 모니터를 보면서 코드를 설명하고, 다른 한 사람이 설명을 들으면서 아이디어를 제안하거나 Defect를 발견하는 방법이다.또한, 이 방법은 신입사원이나 인턴사원의 경우에 업무 이해도를 높이면서 해당 코드를 사용할 수 있는 수준으로 활용할 경우에 의미 있는 방법이다. 문제는 이 방법은 개발자의 인력 투입이 거의 두배 이상으로 증가하는 것으로써, 고품질의 영역을 개발하거나, 빠른 시간 안에 신입 개발자의 업무 이해도를 높이는 경우가 아니라면 시행하지 않는다.이렇게도 리뷰가 진행이 되지 않으면, Passaroud는 돌려 보기 방법을 사용한다. 이 방법은 원래 상세한 리뷰 방법은 아니다. 온라인이나 실시간성이 아니라, 리파지토리나 이메일 등을 사용하여 천천히 리뷰하는 방식에 해당하는데, 속도는 느리지만, 중요한 코드이거나, 제품의 기능 개선이 필요한 경우에는 아주 의미가 있다. 보통은 제품의 기능 개선을 위하여 사용하는 방법이다.이처럼 리뷰의 방법에는 다양한 방법이 있지만, 결론적으로는 어느 정도 개발 조직이 서로  커뮤니케이션하고, 목적의식을 통일하고, 적절한 시간 분배를 통해서 리뷰를 할 수 있는 시간을 만들어 내느냐가 리뷰의 핵심이라고 할 수 있다.리뷰를 통해서 소프트웨어의 품질을  끌어올리고, 개발자들과 소통하고, 방향성을 만들어 내며, 새로운 기능 개선 작업을 위해서 리뷰는 다양하게 활용된다. 어떤 관점으로 리뷰를 할 것이고, 어떤 관점으로 리뷰라는 프로세스를 개발 프로세스에 탑재할 것인가에 대해서 진지하게 고민하는 것. 그것이 아키텍트의 첫 번째 역할 아닌가 한다.
조회수 1099

앱 공모전 기획자에서 비전공 개발자가 되기까지

스푼을 만드는 사람들 다섯 번째 이야기클라이언트팀의 유일한 여성 개발자 Julia를 소개하고자 한다.바나나 최대 몇 개까지 드세요?"마케팅팀 썸머에겐 아귀찜이 있다면, 저에겐 '바나나'입니다. 저는 바나나 우유도 좋아하고, 바나나 한 송이를 그 자리에서 혼자 다 먹을 만큼 좋아해요. 카카오톡 이모티콘도 바나나 이모티콘을 가장 많이 사용할 정도로요. 바나나는 맛도 있지만, 먹으면 기분이 좋아지는 과일이에요"(인터뷰 후, 줄리아에게 바나나 한 다발 선물해드렸습니다. 맛있게 드셨길 바라요)Q. 할머니 감성을 가지셨다고 들었는데, 사실인가요? "네, 모르시는 분들이 많으시겠지만 저는 친구들이 '할머니'라고 불러줘요. 이유인즉슨, 건강에 관심이 워낙 많아서 영양제도 잘 챙겨 먹고 꽃무늬 옷이 많거든요. 정확히 말하면 꽃무늬 치마! 그리고 사석에서는 고향(전라도) 사투리를 많이 써서 그런 것 같아요"줄리아 닮은꼴: 닥터 슬럼프 아리 '줄리아'를 더 알아가고 싶어요본인은 어떤 사람이라고 생각하세요?독한 사람 - 저는 웬만한 것에 있어서 타의적으로 절대 포기를 하지 않아요. 제 스스로가 싫증이 날 때까지는 꼭 끝까지 해내고 말거든요.그래서 전 제 스스로를 독한 사람이라고 말하고 싶어요. 이전부터 개발자로서 커리어를 쌓아오셨나요?"저는 원래 문과생이에요. 비전공자죠. 대학 때 독어를 전공했고, 개발과는 사실 거리가 먼 사람이었어요. 저는 이 전에 많은 경험들을 해왔어요. 세계일주를 하고 싶어서 해상 승무원 준비도 했었고, 중국에서 무역회사에서 근무도 했었고요. 통역도 잠시 했었고, 이 전에는 앱 공모전 기획자로서의 삶도 있었어요. 앱 공모전 기획자라는 건, 회사 및 대회를 홍보하기 위해 직원 대상 또는 시민을 대상으로 행사 및 공모전을 기획해서 행사업체를 고용하거나 직접 운영하는 업무랍니다. 그리고 현재는 안드로이드 개발자로 커리어를 쌓고 있습니다."많은 커리어를 거쳐 개발자가 되신 계기가 있다면?"저는 인생 계획을 짧으면 5년, 길게는 10년씩 잡고 살아가요. 20대 때는 해보고 싶은 게 너무 많았고, 지금도 여전히 많아요. 그래서 20대는 정말 하고 싶은 모든 걸 해보자라는 마음으로 살아왔어요. 30대가 되면서 조금 더 안정적으로 살고픈 마음이 생기기 시작했고 무엇보다 하나의 전문적인 직업을 가지고 싶단 욕구가 커졌어요. 그래서 개발을 선택하게 되었습니다."책상에 약이 굉장히 많네요?"제가 아까 할머니 감성이 있다고 했는데.. 저는 건강을 엄청 챙기거든요.. 그래서 탕비실에도 돼지감자 차 및 영양제 등 굉장히 뭘 많이 챙겨 먹습니다. 그래서 제 책상엔 비타민 등 영양제가 가득하답니다!"집에서 가져온다는 돼지감자 차 당신의 회사생활이 궁금합니다Q. 여성 개발자로 일하는 삶은 어떤가요?"사실 저는 '개발'을 하는 일을 성별로 나누고 싶지는 않아요. 남자 개발자가 많은 이유는 아무래도 공대에 남성 비율이 더 많기 때문이라고 생각이 들기도 하고, '여자' 이기에 특별히 다르다거나 불편한 점은 없어요. 아직은 신입 개발자이다 보니, 배우고 있는 시점이기도 하고요. 그저 열심히 배우는 단계라고 봐주시면 좋을 것 같습니다 :) 무엇보다 제 위로 8년 차, 14년 차 선배분들과 함께 일하면서 정말 많이 배우고 있습니다."Q. 일하면서 언제가 가장 뿌듯하세요?"개발을 하시는 분들은 공감하실 텐데.. 안되던 문제가 갑자기 될 때(?)에요. 분명히 어제는 안됐는데, 오늘은 되는 날이 있거든요. 반대인 경우도 있고요. 그때 정말 뿌듯(?)하고 행복해요. 또 다른 하나는, 보통 다른 곳은 신입 개발자는 보조만 하는 경우가 많거든요. 하지만 팀원들이 저를 믿어주셔서 제가 새로운 기능을 맡아서 짠 추가 코드가 프로덕트에 적용이 될 때가 정말 뿌듯해요."Q. 회사 다니면서 가장 기억에 남는 일이 있다면?"제가 입사 후 함께 처음으로 새로운 국가에 출시했을 때요. 저는 새로운 국가에 서비스를 출시할 때마다 너무 기대되고 업무가 더 즐거워져요. 조금 더 다양한 업무가 주어지고, 생각도 더 많이 하게 되거든요. 그리고 저는 건강에 정말 신경 많이 쓰는데, 저번에 Jun 이 막내 특집(?)으로 홍삼 음료를 주셨는데.. 너무 취향 저격인 거예요. 딱 제가 정말 좋아하는 건강한 맛! 그래서 그날도 너무 행복했어요."Q. 어떤 사람과 일하고 싶으세요?배울 점이 있는 사람이요. 저 또한 누군가에게 배울 점이 있는 사람이고 싶어요.줄리아 업무 공간 당신의 사생활이 궁금합니다Q. 안드로이드 개발자는 안드로이드만 사용하나요?"모두가 그런 건 아니겠지만, 저는 사실 여태 살면서 안드로이드 폰만 사용했었어요. 무엇보다 저는 안드로이드 캐릭터가 너무 귀엽다고 생각하기에.."Q. 주말에는 무엇을 하며 시간을 보내세요?"저는 지난 1년간은 매주 주말마다 코딩 스터디를 해왔어요. 아무래도 비전공자에 늦게 시작한 개발자다 보니 엄청난 노력이 필요하거든요. 지금도 스터디를 하고 있어요. 그리고 2019년부터 목표는 한 달에 한 번쯤은 리프레쉬하기 위해 가까운 곳이라도 여행을 가려고 노력하고 있어요."Q. 개발자가 된 후 삶에 있어 변한 점이 있다면?"예전에는 어떤 것을 설명하거나 표현할 때, 굉장히 문과적(?) 이게 표현을 했었던 것 같아요. 지금도 완전히 바뀌진 않았어요. 하지만, 무언가 문제가 있을 때 원인과 결과를 먼저 파악하는 성향이 생겼달까요? 그리고 편견일 수도 있지만 조금 더 프로페셔녈 해 보이고 싶어서 백팩이나 후디를 자주 입습니다!" 비전공자로서 개발자를 꿈꾸는 사람들에게 "먼저, 비전공자라 하여 못할 거라는 생각을 하지 않으셨으면 좋겠어요. 저도 여전히 배우고 있는 입장이지만 생각보다 비전공자 중에 개발자로서 훌륭하신 분이 굉장히 많거든요. 늦더라도 정말 하고 싶은 마음이 있다면 꼭 도전하라고 말하고 싶어요. 그리고 꼭 영어 공부하세요. 아무래도 문서들이 영어로 되어있으니, 영어를 배워두면 번역기의 도움이 없이도 되기에 큰 도움이 되고 시간이 절약되거든요! 아, 그리고 개발을 배우고자 만약 학원에 가서 수업을 들을 예정이시라면, 수업을 듣기 전에 혼자라도 미리 예습을 하고 가셨으면 좋겠어요. 학원을 다닌다고 해서 정말 모든 걸 알려주진 않거든요. 얼마나 열심히 하고 노력하느냐에 따라 성패가 달린다고 생각합니다."안드로이드 팀원들이 줄리아를 한마디로 표현한다면?Derek 曰:  “줄리아는 강한 사람이라고 생각합니다. 외부의 환경에 흔들리지 않고 자신의 꿈을 향해 계속 전진하는 강한 사람이라고 생각합니다.”Yong 曰:  "낯선 길에서 의지를 잃지 않고 가고자 하는 길을 걷는 사람, 그리고 미소가 예뻐서 꽃 같은 사람입니다" 
조회수 1475

스타일쉐어에서 이미지 분류하기 (시작 편) feat.ML

안녕하세요.스타일쉐어에서 백엔드 개발을 하고 있는 김동현입니다.작년 11월 스타일쉐어에서 뷰티에 관련된 사진들을 따로 모아서 보여줄 피드.바로 뷰티피드 라는 것을 만들었습니다. 하지만 피드를 만드는 과정이 순탄치 만은 않았는데요.그간의 과정과 얻었던 경험들을 공유하고자 합니다.들어가기에 앞서혹시 설명을 하다 보면 스타일쉐어에서만 사용되는 단어가 있을 수 있다는 생각이 들어 단어에 대한 공유를 먼저 드리고자 합니다.스타일쉐어에서는 이를 “피드”라 칭합니다.스타일쉐어에서는 이를 “스타일”이라 칭합니다.여러 가지 카테고리 중에서 왜 뷰티인가요?기존의 서비스에서는 유저들이 올리는 스타일에 대한 카테고리가 없어서 유저들이 보고 싶어 하는 스타일들을 쏙쏙 뽑아서 보여줄 수 없는 상황이었지만 “내가 보고 싶은 것들만 볼 수 있었으면 좋겠다”라는 유저들의 니즈는 계속 올라가고 있었습니다.서비스 특성상 1020 유저들이 많이 있었고 하루 동안 올라오는 스타일에 대해서 사람이 직접 카테고리를 하나하나 나눠봤을 때 가장 활발하게 대화가 이루어지고 반응이 좋고 충성도도 높은 카테고리가 바로 뷰티였습니다.뷰티만이라도 따로 보여줄 수 있도록 해보자그럼 어떻게 뷰티에 관련된 게시물들을 뽑아낼 건가요?올라오는 스타일들 중에서 뷰티라는 속성을 찾아내어 분류하는 방법으로 두 가지의 제안이 나왔습니다.1. 사람이 직접 뽑아낸다.2. 요즘 뜨고 있는 딥러닝을 이용해서 뽑아낸다.처음엔 사람이 직접 모니터링 해볼까? 라는 이야기가 나왔었습니다.당장이라도 시작 할 수 있다는 점과 높은 정확도를 가졌다는 장점이 있기 때문이였죠.하지만 주말 관계없이 4000~6000개씩 올라오는 스타일들을 상시 모니터링하고 모두 검토해야 하는 상황이 너무 막막하게 느껴졌습니다. 관련 업무를 하시는 분의 업무 만족도는 낮을 것이 당연하기도 했지만 그럴만한 인적자원이 충분하지 않았습니다.그래서 요즘 뜨고 있는 딥러닝을 이용해보자는 방향으로 일이 진행되었습니다. 게다가 요즘 딥러닝으로 Image Classification 하는데에 있어서 정확도가 사람을 넘어섰다는 이야기도 결정에 한몫을 했답니다.딥러닝으로 분류하기로 결정했다! 근데 트레이닝 셋은?딥러닝을 하시는 분들이 애용하는 사이트인 캐글만 가보아도 문제와 트레이닝 셋이 잘 정리되어있기에 개발자는 어떻게 하면 잘 예측할 수 있을까에 대한 고민만 했으면 되었었습니다. 하지만 당연하게도 실제 필드에서 처리해야 하는 문제와 그에 대한 트레이닝 셋은 존재하지 않았습니다.우선 딥러닝으로 분류하기로 결정을 하였으니 서비스에서 뷰티라는 카테고리 안에 넣을 소카테고리를 나누었고 다음과 같았습니다.* 눈 화장 관련* 입술 화장 관련* 얼굴 화장 관련* 헤어* 화장품* 발색* 네일그래도 태양 아래 새로운 것은 없다 라는 말처럼 비슷한 것들이 존재할까 하고 찾아보았으나…https://www.kaggle.com/openfoodfacts/openbeautyfactshttp://www.antitza.com/makeup-datasets.htmlㅇ…없잖아?!그렇습니다. 공개된 것은 없던 새로운 것이었습니다. 위의 소카테고리들을 모으는 방법을 모색해야 했습니다.위에서 언급했듯이 잉여 인적자원이 없었기 때문에 몇만 개의 데이터를 모을만한 데이터를 모으는 일은 저를 포함해서 개발자 2명이서 진행을 했었습니다.그래서 결국 뷰티 피드는…성.공.적.다행히도 잘 마무리되었습니다. 화자 되고 있는 딥러닝 기술을 실제로 사용해볼 수 있어서 좋았고 팀원들도 이게 되는구나, 다른 것도 해볼 수 있겠다 라는 피드백을 많이 받았고 저 또한 개발을 하면서도 이게 된다고? 하는 반응이 제일 많았던 것 같습니다. 물론 앞으로 모델을 계속 개선해나가야겠지만요.사실 딥러닝을 거의 처음 공부하는 수준에 가까웠고 초반에 우왕좌왕 하기도 많이 했었는데 믿고 기다려줬던 스타일쉐어 팀원 분들 덕분에 잘 마무리될 수 있었던 것 같습니다.분류와 트레이닝 셋에 대한 좀 더 자세한 글은 다음 포스팅 (분류 편)에서 찾아뵙겠습니다.#스타일쉐어 #개발팀 #개발자 #개발후기 #경험공유 #인사이트
조회수 1247

vulcan과 buildpack을 이용한 Heroku 바이너리 배포

vulcan과 buildpack을 이용한 Heroku 바이너리 배포안녕하세요. 스포카 개발팀에서 서버 관련 개발 업무를 담당하고 있는 문성원입니다. 오늘은 저희가 사용하는 PasS(Platform as a service)인 Heroku에 직접 바이너리를 빌드하여 올리는 방법을 함께 알아보겠습니다.Why?________________________________________지난주 저희 개발팀은 새로운 상점 사진을 출력하기 위해 한 사진을 비율이 다른 이미지로 바꿔서 저장하는 작업을 해야 했습니다. 다행히 이 문제는 Seam carving, 혹은 Liquid rescaling으로 불리는 방법, 그리고 이를 구현한 ImageMagick과 그 Python 바인딩인 wand로 쉽게 해결할 수 있을 것 같았습니다. (Seam carving과 wand에 대해서는 이 글을 읽어보시는 것을 권합니다.)그런데 막상 서비스에 배포하려니 한가지 문제가 있었습니다. 저희는 최근 서비스를 Heroku에서 운영 중인데, 이 Heroku에 ImageMagick 라이브러리는 깔렸었지만, liblqr이 없어 Liquid rescalig이 불가능한 상태였던 겁니다. 개발자의 로컬에서 테스트할 때야 소스를 받아서 직접 빌드라도하면 되지만 이 고지식한 PasS에서 그건 무리였죠.결국, 저희는 Heroku의 배포 도구인 buildpack과 바이너리를 빌드하기 위한 서버인 Vulcan에 대해서 조사했습니다.Workflow________________________________________Heroku 앱에 사용할 바이너리를 만드는 데는 크게 2가지 과정이 필요합니다. 먼저 빌드 서버인 Vulcan을 통해 필요한 바이너리를 Heroku(정확히는 아마존 EC2)용으로 빌드해야하며, 이를 buildpack을 통해 새로 만들거나 운영 중인 앱에 적용해야 합니다.재미있는 점은 Vulcan 서버 역시 Node.js로 작성된 Heroku 앱이기때문에 buildpack을 적용할 수 있습니다. 즉 위와 같은 상황이라면 먼저 liblqr을 빌드한 뒤 이를 Node.js 용 buildpack에 적용해서 Vulcan에 올린 뒤 ImageMagick을 빌드해야 합니다.I am a Vulcan, bred to peace________________________________________우선 Vulcan부터 깔아보겠습니다. (Ruby와 Heroku 계정이 필요합니다. 경우에 따라선 sudo가 필요할 수 있습니다.)$ gem install vulcan그다음 빌드에 사용할 서버 애플리케이션을 vulcan 커맨드를 통해 만듭니다. (눈치채신 분도 계시겠지만 앱 이름은 적당히 바꿔서 지으셔야 에러가 안 납니다.)$ vulcan create vulcan-dodo-dev혹시 모르니 만들어진 서버의 업데이트를 한번 해줍시다.$ vulcan update --app vulcan-dodo-devIf I could change to liquid…________________________________________이제 본격적으로 빌드를 해봅시다. 먼저 필요한 건 liblqr입니다. 소스를 적당한 디렉터리에 내려받아 풀어둡니다.$ wget http://liblqr.wikidot.com/local--files/en:download-page/liblqr-1-0.4.1.tar.bz2$ tar xzf liblqr-1-0.4.1.tar.bz2최신 소스를 원하신다면 git 저장소를 복제하셔도 됩니다.$ git clone git://repo.or.cz/liblqr.git편하신 대로 소스를 다 내려받으셨다면 이제 앞서 생성한 Vulcan을 통해 이를 빌드해봅시다.$ cd liblqr$ vulcan buildVulcan은 현재 디렉토리의 소스를 모두 묶어서 EC2상의 서버로 올린 뒤 그 서버에서 빌드한 바이너리를 다시 사용자의 컴퓨터로 내려줍니다. 이제 이를 buildpack을 통해 Vulcan 서버(vulcan-dodo-dev)에 적용해야 합니다.Buildpack is ready________________________________________buildpack을 직접 만들어 적용하는 건 아주 쉽습니다. 우선 다음 명령어로 Node.js용 buildpack을 복제합니다.$ git clone git://github.com/heroku/heroku-buildpack-nodejs.git그다음에는 Heroku용으로 빌드된 liblqr을 Heroku 앱 빌드시 포함시키기 위해 bin/compile파일의 마지막에 다음 코드를 추가합니다. (앞서 빌드한 liblqr을 외부에서 접근할 수 있게끔 적당한 장소(ex. Amazon S3, 혹은 Dropbox의 Public 디렉터리등)에 올려둬야 합니다.)# liblqr                                                                                  LIBLQR_BINARY="https://dl.dropbox.com/u/55786385/liblqr-1-0.4.tgz"                        SPOQA_VM_VENDOR="vendor/spoqa/liblqr"                                                    mkdir -p $1/SPOQA_VM_VENDOR                                                            curl $LIBLQR_BINARY -o - | tar -xz -C $1/$SPOQA_VM_VENDOR -f -이제 buildpack을 커밋(commit)한뒤 적당한 공개 저장소(ex. github) 등에 올려(push)둡니다. 그리고 나선 아까 만든 Vulcan 앱(vulcan-dodo-dev)의 buildpack을 다음 명령어로 지정합니다.$ heroku config:set BUILDPACK_URL=https://github.com/spoqa/heroku-buildpack-nodejs.git --app vulcan-dodo-dev마지막으로 Vulcan 앱을 업데이트하여 새 buildpack을 반영시킵니다.$ vulcan update --app vulcan-dodo-dev확인을 위해서 Vulcan 앱에 들어가 보는 것도 좋습니다.$ heroku run bash --app vulcan-dodo-devheroku run bash --app vulcan-dodo-devRunning `bash` attached to terminal...~ $ ls vendor/ls vendor/spoqa  gemsIt’s a kind of magic________________________________________이제 liblqr을 이용해서 ImageMagick을 빌드해보죠. 기본적으로는 liblqr을 빌드할때와 다르지 않지만 ./configure를 통해 옵션을 줘야 하기에 build 커맨드가 좀 복잡해집니다.vulcan build -p /tmp/ImageMagick -c "export PKG_CONFIG_PATH=/app/vendor/spoqa/liblqr/lib/pkgconfig && export CFLAGS=-I/app/vendor/spoqa/liblqr/include/lqr-1 && LD_LIBRARY_PATH=/app/vendor/spoqa/liblqr/lib && ./configure --prefix=/tmp/ImageMagick --with-lqr && make install" -v조금만 자세히 살펴보면, -p 옵션으로 내려받을 경로를 지정하고 -c 옵션으로 실제 빌드에 사용할 커맨드를 지정합니다.(-v는 짐작하시다시피 확인을 위한 verbose 옵션입니다.) 앞서 수정한 buildpack에서 liblqr은 /app/vendor/spoqa/liblqr 밑에 설치되게끔 되어있기에 PKG_CONFIG와 CFLAGS 설정을 추가해주고 --with-lqr을 줘서 LQR 딜리게이트(Delegate)를 활성화 시킵니다.On your mark________________________________________이렇게 만들어진 ImageMagick 바이너리와 liblqr 바이너리를 실 서버에 적용할 buildpack에 추가해주면 이 험난한 여정도 끝입니다. 앞서 했던것처럼 대상 서버에 맞는 buildpack을 똑같이 복제합니다. (여기서는 Python을 사용합니다.)$ git clone git://github.com/heroku/heroku-buildpack-python.gitbin/compile을 고치는 것도 추가해야 할 라이브러리가 2개라는 점만 빼면 거의 같습니다.# ImageMagick with lqr                                                                                                                  LQR_BINARY="https://dl.dropbox.com/u/55786385/liblqr-1-0.4.tgz"IMAGE_MAGICK_BINARY="https://dl.dropbox.com/u/55786385/ImageMagick-6.8.tgz"IMAGE_MAGICK_WITH_LQR_DIR="vendor/ImageMagick+lqr"mkdir -p $1/$IMAGE_MAGICK_WITH_LQR_DIRcurl $IMAGE_MAGICK_BINARY -o - | tar -xz -C $1/$IMAGE_MAGICK_WITH_LQR_DIR -f -curl $LQR_BINARY -o - | tar -xz -C $1/$IMAGE_MAGICK_WITH_LQR_DIR -f -똑같이 고친 buildpack을 커밋, (적당한 저장소에) 푸시하고 대상 서버의 BUILDPACK_URL을 바꿔줍니다.$ heroku config:set BUILDPACK_URL=https://github.com/spoqa/heroku-buildpack-python.git --app dodo-dev바뀐 buildpack을 적용하기 위해서 빈 커밋을 만들어 새로 배포해보겠습니다.$ git commit --allow-empty -m "empty commit"$ git push heroku master마지막으로 대상 서버의 설정을 바꿔줍니다.$ heroku config:set MAGICK_HOME=/app/vendor/ImageMagick+lqr LD_PRELOAD=/app/vendor/ImageMagick+lqr/lib/libMagickCore.so --app dodo-dev#스포카 #개발 #개발자 #개발팀 #개발팁 #꿀팁 #인사이트
조회수 10000

Estimator: BLE를 사용한 Planning Poker 애플리케이션

1. Planning PokerStyleShare 개발팀에서는 스크럼을 활용하여 일을 진행하고 있습니다.1 스크럼에는 일감의 크기를 추정estimate하는 과정이 있는데요. 구성원들 모두가 일감에 대해 이해하고 일감의 크기가 어느정도인지 함께 논의하여 합의에 이르는 과정입니다. 스프린트 회의에서 일감을 등록한 사람(리포터)이 일감에 대해 설명하고 나서 전체 구성원들이 일감의 크기를 추정하는데, 이 때 사용하는 것이 바로 Planning Poker입니다.Planning Poker는 0.5부터 시작해서 1, 3, 5, 8, 13, 20, … 100과 같이 피보나치 수열로 증가하는 숫자를 가진 카드 덱입니다. 리포터의 설명이 끝난 뒤 스크럼 마스터가 하나, 둘, 셋을 외치면 각자 생각한 일감의 크기에 맞는 카드를 꺼내고, 스크럼 마스터는 구성원들의 추정치가 최대한 가까워지도록 부가설명이나 질문을 유도합니다.2▲ Planning Poker는 이렇게 생겼다. (출처: Control Group 블로그)하지만 개발팀이 커지면서 불편함이 생기기 시작했습니다. 회의에 참여하는 인원이 7-8명씩 되다 보니, 각자가 어떤 카드를 들고있는지 한눈에 보기가 어려워진 것입니다. StyleShare에서 자칭 아이디어 뱅크 역할을 담당하고 있는 저는 획기적인 방법이 필요하다고 생각했고, 굳이 카드를 꺼내들지 않아도 각자가 무슨 카드를 선택했는지를 쉽게 볼 수 있는 애플리케이션을 만들기로 결심했습니다.2. BLE (Bluetooth Low Energy)불편함을 덜기 위한 애플리케이션이므로, 사용자 경험이 굉장히 직관적이고 단순해야 했습니다. N:N 통신이 가능해야하고, 사용자를 귀찮게 하는 페어링Pairing이나 네트워크 접속 과정이 없어야 했습니다. 한마디로, 카드를 꺼내들고 눈으로 확인하는 것보다 더 편한 무언가를 만들어야 했습니다!처음에는 근거리 무선 통신을 위한 기술로 스타벅스에서 사이렌 오더 개발에 사용한 고주파 인식 기술을 생각했습니다.3 각자의 기기에서 선택한 카드에 맞는 소리를 내보내고, 다른 기기에서는 고주파를 읽겠다는 것이었는데요. Soundlly(구 aircast.me)와 같은 상업용 SDK를 쓰지 않는 이상, 사운드 프로그래밍을 한 번도 해본 적 없는 저에게는 데이터가 실린 고주파를 만드는 것부터 소리를 인식해서 데이터를 읽어내는 과정이 마치 화성에서 감자 키우는 이야기처럼 들렸습니다.그러다 문득 생각난 것이 바로 비콘Beacon입니다. 언젠가 소비자가 오프라인 매장에 방문하면 BLE를 이용해서 매장 위치를 파악하는 기술이 있다는 이야기를 들은 적이 있었습니다. 찾아보니 시중에 나와있는 대부분의 모바일 기기에서는 BLE를 위한 최소 조건인 블루투스 4.0을 지원했고, 페어링이나 네트워크 접속 과정도 불필요했습니다. 무엇보다, 화성에서 감자 키우는 것보다는 쉬워보였습니다.3. Swift로 BLE 개발하기그래서 BLE를 사용해서 개발하기로 했습니다. 컨셉은 간단했습니다. 내가 선택한 카드를 브로드캐스팅하고, 다른 사람들이 선택한 카드를 내 모바일 기기에 보여주면 되는 것이었습니다. BLE를 사용하면 정보를 브로드캐스팅할 수 있고, 다른 기기에서 브로드캐스팅하는 정보를 읽을 수 있습니다.BLE에서 데이터를 브로드캐스팅하는 것을 Advertising이라고 합니다. 정보를 advertising하는 주체는 Peripheral이고, advertising되는 정보를 스캔하여 데이터를 읽어들이는 주체는 Central이라고 합니다. Peripheral에서 정보를 advertising할 때에는 특정한 정보를 실어나를 수 있는데요. 이를 Advertising Data Payload라고 합니다. 이 정보에 카드 숫자와 이름을 실어서 전송하면 될 것 같습니다.BLE를 구현하기 위해서, iOS에서는 SDK에 기본적으로 포함돼있는 CoreBluetooth 프레임워크를 사용하면 손쉽게 개발이 가능합니다. CBPeripheralManager 클래스와 CBCentralManager 클래스를 쓰면 되는데요. BLE를 이용하여 제 이름 석자를 advertising하는 코드는 다음과 같습니다.Peripheralimport CoreBluetooth let serviceUUID = CBUUID(string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") let service = CBMutableService(type: serviceUUID, primary: true) /// 1. `CBPeripheralManager`를 초기화하고, self.peripheral = CBPeripheralManager(delegate: self, queue: nil) /// 2. 사용가능한 상태가 되면 특정 UUID를 가진 서비스를 추가한 뒤에 func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager) { if peripheral.state == .PoweredOn { self.peripheral.addService(service) } } /// 3. 원하는 정보를 advertising합니다. func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?) { self.peripheral.startAdvertising([ CBAdvertisementDataLocalNameKey: "전수열", CBAdvertisementDataServiceUUIDsKey: [serviceUUID], ]) } 참고로, UUID는 커맨드라인 명령어를 통해 쉽게 만들 수 있습니다.$ uuidgen XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 마찬가지로, Peripheral에서 advertising하는 정보를 스캔하는 Central 코드는 다음과 같이 작성할 수 있습니다. UUID는 Peripheral에서 advertising에 사용한 UUID와 동일해야합니다.Centralimport CoreBluetooth let serviceUUID = CBUUID(string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") let service = CBMutableService(type: serviceUUID, primary: true) /// 1. `CBCentralManager`를 초기화하고, self.central = CBCentralManager(delegate: self, queue: nil) /// 2. 사용가능한 상태가 되면 특정 UUID를 가진 서비스를 스캔합니다. func centralManagerDidUpdateState(central: CBCentralManager) { if central.state == .PoweredOn { // 이미 한 번 스캔된 정보라도 계속 스캔합니다. let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true] self.central.scanForPeripheralsWithServices([serviceUUID], options: options) } } /// 3. Peripheral이 스캔되면 이 메서드가 호출됩니다. func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) { print(advertisementData[CBAdvertisementDataLocalNameKey]) // "전수열" } 스캔을 시작할 때 CBCentralManagerScanOptionAllowDuplicatesKey 옵션을 true로 설정해서 한 번 스캔된 정보라도 중복으로 계속 스캔하도록 합니다.4. 원하는 정보를 실어나르기CBPeripheralManager을 사용하여 advertising을 할 때에는 Advertising Data Payload를 포함시킬 수 있는데, 이 정보 중 개발자가 원하는 값을 넣을 수 있는 곳은 CBAdvertisementDataLocalNameKey밖에 없습니다. 그마저도 길이가 제한돼있기 때문에, 패킷을 효율적으로 사용하기 위해서는 정보를 저장하는 프로토콜을 직접 정의해야 합니다.우선, 카드에 대한 정의는 enum을 사용해서 작성했습니다. 0부터 0xFF까지의 숫자를 가지도록 정의했습니다.public enum Card: Int { case Zero = 0 case Half = 127 case One = 1 case Two = 2 case Three = 3 case Five = 5 case Eight = 8 case Thirteen = 13 case Twenty = 20 case Fourty = 40 case Hundred = 100 case QuestionMark = 0xFD case Coffee = 0xFE case None = 0xFF } 그리고 제가 정의한 패킷의 프로토콜은 다음과 같습니다.영역길이예시설명Version200프로토콜 버전 (00~FF)Channel201BLE 커버리지 내에서 회의하는 팀이 여럿일 수 있으니, 채널로 구분합니다. (00~FF)Card2FE카드의 16진수 값 (00~FF)Name12전수열사용자 이름 (UTF-8 기준 한글 4글자)이렇게 하면 총 18바이트 내에서 필요한 정보를 모두 전송할 수 있습니다. 이제 이 "00", "01", "FE", "전수열" 값을 직렬화해서 CBAdvertisementDataLocalNameKey로 advertising하면 됩니다.Peripheralself.peripheral.startAdvertising([ CBAdvertisementDataLocalNameKey: "0001FE전수열", CBAdvertisementDataServiceUUIDsKey: [serviceUUID], ]) 그리고, Central에서 정보를 스캔할 때에는 이 값을 각 영역의 길이에 맞게 끊어서 읽을 수 있습니다.5. 마치며비록 적은 양의 정보지만, BLE를 사용해서 실시간으로 근거리 통신을 할 수 있게 되었습니다. 이제 남은 것은 카드를 선택할 수 있는 화면과, 다른 사용자가 선택한 카드를 화면에 보여주는 인터페이스입니다. UI 개발은 본 포스트에서 중점적으로 다루고자 하는 주제와는 조금 벗어난 이야기가 될 것 같아, 오픈소스로 공개된 코드로 대신하려고 합니다. 소스코드는 GitHub에서 볼 수 있으며, Estimator는 앱스토어에서 받아보실 수 있습니다.6. 참고 자료BLE(BLUETOOTH LOW ENERGY) 이해하기 - Hard Copy World스타일쉐어의 스크럼이 지나온 길 포스트에 보다 자세히 설명되어 있습니다. ↩구성원들의 추정치에 차이가 난다는 것은 해당 일감에 대해 서로가 이해하고 있는 정도가 다르기 때문입니다. 스크럼 마스터는 구성원들이 일감에 대해 모두 비슷한 생각을 가지도록 커뮤니케이션을 유도해야합니다. ↩http://www.bloter.net/archives/226643 ↩#스타일쉐어 #개발 #개발팀 #개발자 #인사이트
조회수 1267

[인터뷰] Humans of MEME, 그 마지막 주인공을 만나다. - 긍정의 힘을 지닌 듀크의 이야기

여러분 안녕하세요.미미박서의 평범하지만 특별한 이야기를 담아왔던 모뜨입니당!오홍 벌써 프로젝트의 마지막 이야기가 다가왔네요.Humans of MEME 의 마지막 주인공은바로 Global SCM 팀의 듀크입니다 !듀크의 솔직하고 담백한 이야기를들어보실까요 ?Q. 듀크가 담당하시는 업무인 SAP는 사내에서도 어렵다고 소문이 났는데요(쥬륵). SAP를 간략하게 소개해주신다면, 무엇인가요?A. 미미박스라는 회사가 원활하게 운영될 수 있도록 도와주는 시스템이 ERP(Enterprise Resource Planning : 전사적 자원 관리)이고 그 ERP 안에 여러가지 툴 중의 하나가 SAP이에요. 또 SAP에는 많은 프로그램들이 있는데, 그 프로그램을 개발하는 것이 abap 개발을 담당하고 있어요. 저는 컴퓨터를 전공하여 대학교 때부터 계속 컴퓨터만 해왔어요. SAP는 거의 대학교 과정에 없는 내용이라, 우연찮게 첫 직장에 들어가면서 처음 접했어요. 실무를 접하게 되면서 여러가지 상황에 대응하는 능력을 배우면서 적성에도 맞고 차차 젖어든 것 같아요. 전공에 따라 직업이 선택되기도 하지만 둘 사이의 직접적인 관련보다는 직업을 선택하는 것에 있어서 여러가지 경험 중의 한 단계인 것 같아요. 저도 컴퓨터가 전공이었지만 기획하고 여러가지 활동적인 일들도 하고 싶어서 찾아보기도 했었어요. 2가지 사이의 직접적인 연관은 없지만, 전공은 직업을 선택하는 데에 있어서 토대를 마련해주는 경험의 일종이라고 생각해요.  Q. 미미박스를 어떻게 만나게 되셨나요?A. 이전 직장 동료의 추천으로 미미박스에 합류하게 되었어요. 이전 직장의 동료들이 현재 미미박스의 동료들이기도 합니다(웃음). 저는 물론 하고 있는 업무도 중요하지만 동료와의 관계가 회사 생활의 50%를 차지한다고 생각해요. 동료와의 관계가 좋아야지 같이 시너지 효과를 내면서 분명히 업무 또한 잘 할 수 있는 것 같아요. 일도 마음도 잘 맞는 동료들과 함께 일을 하다보면 즐거운 일도 같이 공유하고 속상한 일이 있어도 서로 그때그때 풀 수 있어요. Q. 삶에서 도전적인 경험을 하신 적이 있으세요?A. 저는 늘 여린 외모때문에 주변 분들에게 약해보인다, 여려보인다 등 이런 얘기를 들은 적이 많아요. 그래서 그런지 몰라도 자꾸 무모한 도전을 해보려고 했던 과거 시절이 있었어요. 그 중의 하나로 대학교를 휴학한 후 자전거로 전국 일주를 다녀왔어요. 남들이 해보지 않은 경험을 해보고 싶었고 스스로 강해지고 싶다는 욕구도 있었어요. 저를 포함해서 친구들 3명과 같이 일주를 했어요. 저는 3이라는 숫자를 좋아해요. 2명이라면 싸울 수도 있는데 3명이라면 싸워도 2:1 이 되기 때문에 늘 그 자리에서 결론이 나거든요(웃음).서울에서 출발해서 미시령을 넘고, 강원도에서 부산으로 내려와, 부산에서 배를 타고 제주도를 갔어요. 제주도 한바퀴를 돌고 다시 배를 타고 목포에 도착했어요. 그렇게 목포에서 서울로 다시 올라왔습니다. 그렇게 총 한달 정도 걸렸어요.자전거로 한달 동안 전국을 돌면서 많은 사람들도 만났고 위험한 일도 많이 겪었어요. 무모하게 시작했던 것이지만 지금 돌이켜보면 가장 기억에 남고 제 자신의 한계를 시험해볼 수 있었던 것 같아요.자전거 전국일주를 하던 2002년의 듀크(좌)! WOWOWQ. 요즘 느끼시는 소소한 행복이 있으신가요?A. 최근에 아내가 아이를 출산했어요. 태어난지 현재 4개월 째가 되었는데 아이를 보는 낙에 살아가고 있어요. 제가 눈썹만 움직여도 아이는 꺄르르 웃으며 자지러지는데, 아이가 웃으며 결국 저도 웃거든요!저는 예전에는 운동하는 것이 특기이자 취미였어요. 이전에는 다른 즐거움이 분명히 있었는데 세월이 흐르다 보면서 또다른 즐거움을 맞이하고 있어요. 아내와 아이를 보면서 살아가는 데서 행복을 느끼고 에너지를 받는 것 같아요. Q. 듀크는 스스로 어떤 사람이고 싶으세요?A. 저는 늘 마음에 품고 있는 말이 있어요. 바로 ‘긍정의 힘’ 이라는 말이에요. 상황을 부정하고 의심하기보다 어려운 상황 속에서도 긍정적인 요소를 찾아낼 수 있어야 해요.먼저 긍정적인 마인드는 스스로를 변화시킬 수 있어요. 또한 저의 긍정적인 마인드를 통해 주변 사람들 또한 변화시킬 수 있는 것 같아요. 제가 긍정적인 에너지를 줌으로써 옆에 계신분들에게도 웃음을 전달할 수 있고 기쁜 순간들을 같이 할 수 있을 때 뿌듯해요. 앞으로도 저는 스스로에게도 긍정적으로, 주변 사람들에게도 긍정의 힘을 전파할 수 있는 사람이고 싶어요.듀크가 말한 긍정적인 마인드가 자신을 변화시키고나아가 주변 사람들도 변화시킬 수 있다는 힘과짧은 시간이나마 인터뷰를 진행하며 듀크의 긍정적인 기운을 느낄 수 있었어요 :)매일 행복할 수는 없지만행복한 일은 매일 있다는 말이 있듯이 여러분도 긍정의 힘을 믿어보시는 것은 어떠세요 !?이렇게 7번째 주인공 듀크를 마지막으로Humans of MEME 프로젝트가 끝나게 되었습니다.실화인가요?실화입니다.흫 여러분들은 이야기를 보며 어떠셨나요?저 모뜨는 인터뷰를 통해개인적으로나 회사의 속한 구성원으로서나새로운 자극을 받기도 하고 많이 성장할 수 있었던 시간이였습니다!판교 미미박스 본사 10층 플레이미미Humans of MEME 프로젝트는블로그에 올라오는 이야기 뿐만 아니라 미미박스 사내의 카페테리아에 매주마다 주인공들의 포스터가 붙여졌었답니다! (매주 포스터 구경하는 재미가 쏠쏠했다구여)Humans of MEME 는미미박서분들이 가장 많이 찾는 공간인 10층 플레이미미에서서로서로를 알아갈 수 있었던좋은 커뮤니케이션의 채널로서도 자리잡았었는데요!아쉽게도 프로젝트가 끝이 나게 되지만,미미박서 FOREVER 얍얍얍 미미박스 FOREVER 얍얍얍앞으로도 더 멋진 미미박서와 미미박스의 이야기로꾸준히 찾아오도록 하겠습니다 !안녕히계세요 !
조회수 1973

Circle CI에서 rbenv를 이용해서 Ruby 2.2와 CocoaPods 0.39 버전 사용하기

최근 Circle CI에서 Ruby 버전을 2.3으로, CocoaPods 버전을 1.0으로 업그레이드함에 따라 발생하는 빌드 문제를 rbenv를 이용해서 해결한 경험을 공유합니다. 최종적으로 완성된 Gemfile과 circle.yml 파일은 마지막 섹션에서 확인하실 수 있습니다.1. CocoaPods 1.0지난 2015년 12월에 CocoaPods 1.0.0 베타 버전이 처음 공개되었습니다. CocoaPods이 1.0 버전으로 업그레이드되면서 굉장히 많은 변화가 있었는데요. 가장 큰 변화는 DSL입니다. 추상 타겟Abstract Target과 타겟 상속Target Inheritance이 새롭게 소개되면서, 0.39 버전까지 자주 사용되던 link_with 및 :exclusive => true와 같은 구문이 제거되었습니다.이에 따라 기존에 사용하던 Podfile이 CocoaPods 1.0 버전과는 호환되지 않는 문제가 발생했습니다. 이를 해결하기 위한 가장 좋은 방법은 새로운 DSL을 사용하여 Podfile을 다시 작성하는 것이지만, 꽤 많은 서드파티 라이브러리를 사용하는 StyleShare의 경우 새로운 DSL을 적용하여 빌드하면 각종 문제로 인해 빌드가 정상적으로 이루어지지 않았습니다. 4년동안 유지되고 있는 프로젝트이다보니, 레거시 Objective-C 코드와 라이브러리, 그리고 새로운 Swift 코드와 라이브러리가 혼용되어 사용되는 것도 원인 중 하나일 것입니다.따라서 StyleShare에서는 CocoaPods 0.39 버전을 사용하기로 결정을 했습니다. 하지만 최근 Circle CI에서 CocoaPods 버전을 공식적으로 1.0 버전으로 업그레이드하면서 빌드가 깨지기 시작했습니다. Circle CI 환경에서 CocoaPods 0.39 버전을 사용하려면 어떻게 해야 할까요?▲ ㅠㅠ2. Bundler를 이용해서 Gem 관리하기Bundler는 Ruby로 작성된 라이브러리들의 버전을 관리해주는 강력한 도구입니다. CocoaPods에서 Podfile에 의존성을 기재하듯, Bundler에서는 Gemfile에 의존성을 기재합니다.source 'https://rubygems.org' gem 'cocoapods', '~> 0.39' $ gem install bundler 명령어를 사용하면 Gemfile에 기재된 의존성 라이브러리들을 설치해줍니다. 이렇게 설치된 CocoaPods을 사용할 때에는 $ pod COMMAND 대신 $ bundle exec pod COMMAND 명령어를 사용해야 합니다.$ gem install bundler $ bundle install --path vendor/bundle $ bundle exec pod --version 0.39.0 3. Ruby 2.3과 CocoaPods 0.39Bundler를 사용해서 CocoaPods 0.39 버전을 사용하기만 하면 모든 문제가 해결될 줄 알았습니다. 하지만 더 큰 삽질이 남아있었는데요. 바로 Ruby 2.3 버전이 CocoaPods 0.39 버전과 호환되지 않는 것이었습니다.$ bundle exec pod install Updating local specs repositories Analyzing dependencies 신나게 $ bundle exec pod install 명령어를 실행하니, 의존성을 분석하는 듯 싶다가 갑자기 에러를 주르륵 뱉습니다. 에러 로그의 #### Error 항목을 보면 에러 메시지가 나와있습니다.NoMethodError - undefined method `to_ary’ for #이 에러 메시지로 CocoaPods GitHub 저장소의 이슈를 검색해보면 꽤나 많은 이슈가 올라와 있습니다. 이 이슈들을 보면, 모두 Ruby 버전이 2.3이라는 공통점이 있습니다. Ruby 버전을 2.2로 내렸더니 문제가 해결됐다는 댓글들도 굉장히 많고요. Circle CI의 Ruby 버전을 2.2로 낮추면 문제가 해결될 것 같습니다.Circle CI 문서 내용에 따라 circle.yml에 Ruby 버전을 기재해봅시다.machine: ruby: version: 2.2.5 그러나 Circle CI의 OS X 컨테이너에서는 Ruby 버전 변경을 지원하지 않는다고 합니다.▲ ㅠㅠ (2)4. rbenv를 이용해서 Ruby 2.2 사용하기그러다가 알게된 것이 바로 rbenv입니다. rbenv를 사용하면 여러개의 Ruby 버전을 깔끔하게 관리할 수 있게 됩니다. rbenv는 Homebrew를 사용해서 쉽게 설치할 수 있습니다.$ brew install rbenv rbenv는 ~/.rbenv 디렉토리에 안에 여러 Ruby 버전을 설치하고 관리합니다. rbenv를 설치한 뒤 가장 먼저 할 일은 환경변수 $PATH를 설정해주는 것입니다. $PATH에는 $HOME/.rbenv/shims와 $HOME/.rbenv/bin 경로가 포함되어있어야 합니다.4.1 환경변수 설정하기Circle CI에서는 환경변수를 설정하는 편리한 인터페이스를 제공합니다. 하지만, Circle CI에서 실행되는 각 명령어는 별도의 쉘에서 실행됩니다. 그말인 즉슨, 각 명령어가 실행되기 직전에 새로운 쉘이 실행되고, $PATH 환경변수를 덮어쓰는 .bash_profile이 실행된 후 명령어가 실행된다는 뜻인데요. 이렇게 될 경우 $PATH 환경변수의 가장 우선순위는 항상 /usr/local/bin이 가지게 됩니다. 그리고 같은 이유로 $ export FOO=bar와 같은 명령어도 사용할 수 없게 됩니다.1고민을 하다가 생각해낸 방법은 바로 .bash_profile의 내용을 변경(!)하는 것입니다. 그렇게 되면 우리가 원하는 $PATH를 항상 우선순위로 둘 수 있게 됩니다. 아래와 같이 환경변수를 설정하는 명령어를 .bash_profile의 가장 아랫줄에 삽입하도록 설정했습니다.machine: pre: - echo "export PATH=\$HOME/.rbenv/shims:\$HOME/.rbenv/bin:\$PATH" >> .bash_profile - echo "export RBENV_SHELL=bash" >> .bash_profile 4.2 rbenv에 Ruby 2.2 설치하기그 다음으로 할 일은 원하는 Ruby 2.2 버전을 설치하는 것입니다. $ rbenv install -l을 사용해서 설치 가능한 모든 Ruby 버전을 조회할 수 있고, $ rbenv install 2.2.5 명령어를 사용해서 2.2.5 버전을 설치할 수 있습니다.$ rbenv install -l Available versions: 1.8.5-p113 1.8.5-p114 1.8.5-p115 1.8.5-p231 ... $ rbenv install 2.2.5 이렇게 설치된 버전은 두 가지 방법으로 사용될 수 있습니다. 한 가지 방법은 시스템 전체에서 사용하는 것이고, 다른 한 가지 방법은 프로젝트 단위로 사용하는 방법입니다. 시스템 전체에서 사용하려면 $ rbenv global 2.2.5 명령어를, 프로젝트 단위로 사용하려면 $ rbenv local 2.2.5명령어를 사용합니다.global 명령어를 사용해서 Ruby 버전을 선택하면 ~/.rbenv/version 파일에 선택된 버전이 기록됩니다.$ rbenv global 2.2.5 $ cat ~/.rbenv/version 2.2.5 local 명령어를 사용하면 현재 디렉토리의 .ruby-version 파일에 선택된 버전이 기록됩니다.$ rbenv local 2.2.5 $ cat .ruby-version 2.2.5 local 명령어로 선택된 Ruby 버전은 global 명령어로 선택된 Ruby 버전보다 우선순위가 높습니다. $ rbenv version 명령어를 사용하면 현재 선택된 버전을 확인할 수 있습니다.$ rbenv version 2.2.5 (set by /project/path/.ruby-version) Circle CI에서는 편의를 위해 global 명령어를 사용해서 Ruby 버전을 선택하도록 했습니다.dependencies: pre: - brew update - brew install rbenv - rbenv install 2.2.5 - rbenv global 2.2.5 4.3 Bundler 다시 설치하기rbenv를 사용해서 새로운 Ruby 버전을 설치했기 때문에, Circle CI 시스템에서 제공하는 Gem도 다시 설치해야 합니다. 우리는 Bundler로 Gem 의존성을 관리하기로 했으므로, Bundler만 재설치합니다.$ gem install bundler --no-ri --no-rdoc $ rbenv rehash $ gem install 명령어를 실행한 후에는 $ rbenv rehash 명령어를 실행해서 executable 경로들을 재설정해주어야 합니다.4.4 ~/.rbenv 경로 캐싱하기rbenv를 사용해서 Ruby를 설치하는 과정이 굉장히 오래 걸립니다. 이 경우, Circle CI에서 제공하는 캐싱 기능을 사용해서 이 과정을 한 번만 하고 건너뛸수 있게 됩니다.dependencies: cache_directories: - ~/.rbenv 위와 같이 circle.yml를 설정해주면 컨테이너 실행시 ~/.rbenv 디렉토리가 캐시로부터 설정됩니다. 캐싱된 디렉토리를 사용하는 경우 Ruby 버전이 미리 설치되어있기 때문에 $ rbenv install시에 --skip-existing 옵션을 추가해주어서 캐싱된 버전을 재설치하지 않도록 합니다.5. 마치며최종적으로 완성된 Gemfile과 circle.yml 파일은 다음과 같습니다.Gemfilesource 'https://rubygems.org' gem 'cocoapods', '~> 0.39' circle.ymlmachine: pre: - echo "export PATH=\$HOME/.rbenv/shims:\$HOME/.rbenv/bin:\$PATH" >> .bash_profile - echo "export RBENV_SHELL=bash" >> .bash_profile xcode: version: 7.3 dependencies: cache_directories: - ~/.rbenv pre: - brew update - brew install rbenv - rbenv install 2.2.5 --skip-existing - rbenv global 2.2.5 - gem install bundler --no-ri --no-rdoc - rbenv rehash - bundle install --path vendor/bundle override: - bundle exec pod --version - bundle exec pod install https://circleci.com/docs/environment-variables/#custom ↩#스타일쉐어 #개발 #개발자 #개발팀 #후기 #일지 #인사이트

기업문화 엿볼 때, 더팀스

로그인

/