스토리 홈

인터뷰

피드

뉴스

조회수 1593

스마트 컨트랙트 개발과정에서의 실수 — 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 #블록체인 #개발자 #개발팀 #기술기업 #기술중심 #실수담
조회수 1767

Google I/O 2018: Firebase의 새로운 기능

멋진 서비스를 제공하기 위해 잘 만들어진 앱을 개발하는 것은 중요합니다. 하지만 출시 이후 앱 운영을 통해 사용자 Retention과 Engagement를 유지 및 증가시키는 것 또한 앱을 잘 개발하는 것 만큼이나 중요하고 많은 고민과 노력을 들여야합니다. 그런 관점에서 Firebase는 앱을 운영함에 있어서 고민할 법한 다양한 기능들을 적절히 잘 모아놓은 서비스인 것 같습니다.스타일쉐어에서도 Crashlytics, Remote Config, Analytics 등 Firebase에 포함된 서비스들을 잘 활용하고 있습니다. 그래서 Firebase에 개선 및 변경 사항이 있다면 항상 주의 깊게 살펴보고 있습니다.https://events.google.com/io/올해도 어김없이 Google I/O가 개최됐습니다. 역시나 흥미로운 주제들이 많았습니다만, 이번 글에서는 앞서 언급한 Firebase 에 추가된 새 기능을 다룬 ‘What’s new in Firebase’ 세션에 대해서 공유드리고자 합니다.세션에서 주요 골자는 다음 4 가지입니다.ML Kit 베타 시작Test Lab iOS 플랫폼 지원Performance Monitoring 개선Google Analytics 개선이 글에서는 이 4 가지 내용에 대해서 정리하도록 하겠습니다.ML Kit 베타아직까지 베타 버전이긴 합니다만 ML Kit 를 통해 Machine Learning 을 앱에서 쉽게 활용할 수 있다고 하니 Machine Learning 이 한층 가까워진 느낌입니다.ML Kit 는 Android, iOS 양 플랫폼 모두 지원합니다. 따라서 앱에서 Machine Learning 을 활용해 서비스를 제공해보고 싶다면, 양 플랫폼 모두 시도해볼 수 있겠네요.What’s new in Firebase (Google I/O ’18) SlideML Kit 은 기본 API 를 제공합니다. 이 API 들은 Machine Learning 에 대한 폭 넓은 배경지식이 없더라도 충분히 활용할 수 있기 때문에 저처럼 막막한 느낌이 드는 분들은 아래 기본 API 5가지를 사용해서 먼저 친해져보는 것도 좋겠네요.텍스트 추출얼굴 인식바코드 스캔이미지 라벨링렌드마크 인식ML Kit 은 on-device 와 cloud 에서 모두 동작하기 때문에 상황에 맞게 적절한 방식을 사용하면 된다고 합니다. 예를 들어 사진앱 처럼 네트워크 연결이 중요치 않은 서비스의 경우에는 on-device 를 통해 오프라인으로도 동작이 원활하게 만들 수 있겠네요.또한 Machine Learning 에 대한 배경 지식이 충분하다면 TensorFlow-Lite 모델을 통해 직접 원하는 학습을 시킬 수도 있습니다.ML Kit 은 Android, iOS 양 플랫폼 모두 사용 가능하며 기본으로 제공하는 5가지 이외에도 향후에 더 추가될 예정이라고 합니다. 추가될 기능들에 대해서 조금 더 일찍 테스터로서 경험해보고 싶다면 waiting list에 메일을 등록하면 됩니다.Test Lab iOS 플랫폼 지원Test Lab 은 다양한 디바이스를 모두 고려한 앱을 개발하기 어려운 Android 플랫폼의 특징을 보완하기 위한 서비스입니다. 주로 UI 테스팅과 관련된 기능들을 제공하며, 좀더 쉽고 편하게 UI 테스팅을 작성하고 다양한 디바이스에서 테스트할 수 있는 환경을 제공해줍니다.What’s new in Firebase (Google I/O ’18) Slide앱을 서비스 할 때 Android, iOS 어느 한 쪽 플랫폼만 개발하는 경우는 드문 것 같습니다. 그래서인지 Firebase 팀도 iOS 지원에 항상 신경을 쓰는 것 같은 인상을 받았는데, 이번 경우도 그런 느낌이 강하게 듭니다.이번에 추가된 iOS 용 Test Lab 을 활용한다면 출시 전 Android와 iOS 모두 동일한 기준으로 품질 상태를 확인하고 배포할 수 있는 환경을 갖출 수 도 있겠네요. iOS용 Test Lab 은 다음 달에 정식으로 선보일 예정입니다만, 이 기능 또한 일찍 테스터로 참여하고 싶다면 waiting list에 메일을 등록하면 됩니다.Performance Monitoring 개선Performance Monitoring 의 베타 기간이 끝나고 정식으로 서비스를 한다고 합니다. Crash-free 도 중요하지만 사용자 입장에서 고려해봤을 때 앱의 퍼포먼스도 놓치면 안되는 중요한 요소입니다. Performance Monitoring 은 이런 관점에서 인사이트를 얻을 수 있게 도와주는 서비스라고 합니다.What’s new in Firebase (Google I/O ’18) Slide세션에서 강요한 기능은 New Issues Feed 입니다. Performance Monitoring화면의 상단에서 확인할 수 있는 이 기능은 단순한 데이터를 나열하는 것이 아니라 자체적인 분석을 통해 가장 최근에 해결해야할 이슈를 제안합니다.What’s new in Firebase (Google I/O ’18) Slide이 외에도 디바이스에서 렌더링할 때나 네트워크 요청을 할 때의 이벤트들을 기록해서 퍼포먼스 저하 요소들을 보여줍니다. 덕분에 어떤 부분에서 퍼포먼스 저하가 가장 심한지 보다 쉽게 파악할 수 있다고 합니다.Performance Monitoring 은 별도 코드 없이 모든 페이지에서 자동으로 데이터를 수집하고 있으니 별도의 노력없이 인사이트를 얻을 수 있다는 점이 또 다른 장점입니다.Google Analytics 개선What’s new in Firebase (Google I/O ’18) SlideGoogle Analytics 에서 두드러지는 개선 점은 Project level reporting이 가능해졌다는 것 입니다. 플랫폼 별 사용자 특성이 있기는하지만 하나의 서비스 차원에서 병합해서 데이터를 보고싶은 경우가 종종 있는데, 그럴 때 마다 별도의 서버 처리를 통해 병합하는 과정이 번거로웠습니다. 하지만 이번 개선을 통해서 프로젝트 단위의 데이터 분석이 가능해진 덕분에 번거로움을 좀 덜어낼 수 있겠습니다.그리고 세션에서 언급하진 않았지만, Filter가 조금 더 유연해지고 세분화된다고 합니다.지금까지 ‘Google I/O 2018: What’s new in Firebase’ 세션 중 주요 내용만 간단하게 살펴봤습니다. Firebase 는 매년 발전을 거듭해가며 앱 운영의 통합 관리 서비스로서의 자리매김을 해나가는 중인 것 같습니다. 덕분에 직감에만 의존해서 앱의 방향을 정하던 예전에 비해 정량적 데이터에 기반을 두며 더 성공에 가깝게 한발짝 씩 다가갈 수 있는 것 같습니다.이번에 Firebase 에 새로 추가된 기능들을 조금씩 건드려보면서 우리 서비스에서 어떻게 활용하며 인사이트를 얻고, 서비스를 이용하는 사용자들에게 더 나은 서비스를 제공해 줄 수 있을까 고민해봐야겠습니다.#스타일쉐어 #개발자 #개발팀 #인사이트 #Firebase #경험공유 #일지 #후기
조회수 9027

왜 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 #개발 #개발환경 #업무환경
조회수 2832

개발자 직군 파헤치기 2 | 게임 개발자

게임 개발자국내 게임 산업에서 모바일 게임의 매출액은 2011년 4235억원에서 2013년 2조3276억원으로 2년 만에 6배 가까이로 늘어났습니다.(출처:한국콘텐츠진흥원) 한국 모바일 게임은 해외에서도 인기를 끌고 있는 추세입니다. 뿐만 아니라 최근 엄청난 인기를 끌고있는 배틀그라운드는 한국 게임 산업의 가능성을 증명합니다. 배틀그라운드는 작년 한 해 7621억원의 수익을 거두면서 2017년 가장 큰 수익을 거둔 PC 게임 패키지 1위를 차지했습니다.배틀그라운드의 일러스트게임을 좋아하는 사람이라면 한번쯤은 게임 개발에 관심을 가져보았을 것입니다. 특히 프로그래밍을 하는 사람이라면 자신의 게임을 만들어보고 싶다는 생각을 해보거나, 게임 회사에서 일 하는 것을 고려해보았을 것입니다. 그러나 한편으로는 압도적인 근무 시간에 대한 부담으로 게임 개발자가 되겠다는 생각을 접게 되신 분들도 많습니다.이번 포스팅은 게임 개발자에게 필요한 역량이 무엇인지 알아보고, 게임 개발자의 두 가지 커리어 종류에 대해 설명하려고 합니다. 또한 지금 당장, 코딩을 전혀 할 줄 모르는 상태에서 게임 개발에 도전해볼 수 있는 방법 또한 소개해드리겠습니다.게임 개발자에게 필요한 역량게임을 만들기 위해서는 그래픽을 다루는 능력, 스토리와 레벨을 기획하는 능력, 3D 모델링, 그래픽 엔진을 다루는 능력 등 많은 영역들에서 전문성을 필요로 합니다. 물론 이 모든 것을 전문적으로 다루는 사람이 되기란 불가능에 가깝습니다. 그렇기 때문에 스토리라인과 컨셉 구성은 기획자가 담당하고, 기획자의 아이디어는 개발자와 그래픽 디자이너의 손을 거쳐 게임의 모습을 갖춥니다. 그래픽 디자이너가 시각적 구현을 맡는다면, 개발자는 PC나 모바일에서 게임이 실행될 수 있도록 만드는 작업을 하게되는 것입니다. 게임 개발자도 결국 개발자 직군의 일환이기 때문에 일반적으로 개발자들이 많이 다루는 언어에 대한 숙련도나 프로그래밍 능력이 필요합니다. 그러나 게임 개발자의 경우 다른 직군의 개발자에게는 필수적이지 않은 지식을 필요로 할 때가 있습니다. 아래에는 특히 게임 개발자들에게 중요한 세 가지 요소입니다. 1. 프로그래밍 언어대부분의 대규모 게임 회사들은 C++을 가장 많이 사용합니다. 모바일 게임이 대세로 더오르면서 C#을사용하는 경우가 많아진 것은 사실입니다. 그러나 PC, 모바일, 비행기 제어 프로그램까지 폭넓게 지원하는 고성능의 3D 게임을 개발하기 위해서는 여전히 C++이 최적이라는 평가를 받습니다. 주의할 점은 C/C++은 계속해서 발전하고 있는 언어라는 점입니다. 언어를 배우기 위한 서적, 인터넷 강의 등은 무궁무진하지만 중요한 것은 최신의 것을 배워야 한다는 점입니다.2. 게임 엔진게임 엔진은 간단하게 말해 게임을 개발하는 과정을 쉽게 만드는 ‘도구’입니다. 중력 같은 기본적인 물리 효과나 오브젝트 사이의 충돌 여부를 판정하는 ‘컬라이더’ 등, 개발에 필요한 기본적인 기능이 탑재되어있기 때문에 게임 엔진은 개발 과정을 획기적으로 단축시켜줍니다. 가장 많이 쓰이는 게임 엔진은 유니티와 언리얼입니다.이 글을 읽고 있을 대부분의 분들이 개발을 배우는 과정에 있다는 가정하에 학습의 용이함을 기준으로 비교해보면, 유니티의 경우 공식적으로 지원하는 교육 프로젝트의 수는 9개입니다. 그러나 공식적인 자료 외에도 한글 서적이나 온라인 강좌들은 매우 풍부합니다. 반면에 언리얼이 제공하는 공식 교육 프로젝트는 수십개입니다. 대부분이 한글 자막을 지원해줄 뿐만 아니라 다양한 주제를 경험할 수 있습니다. 언리얼의 한계라면 공식 채널 외에서 학습할 수 있는 자료나 커뮤니티가 아직까지는 많지 않다는 점입니다. 3. 수학게임 개발자에게 수학은 매우 중요하고도 기본적인 것입니다. 특히 3D 게임을 다루고 싶다면 수학적 지식과 역량은 매우 중요한 부분을 차지할 것입니다. 물론 위에서 말한 게임 엔진이 수학적인 계산이나 물리와 관련된 문제들을 해결해 줄 수는 있습니다. 그러나 게임 엔진을 활용한다 하더라도 기본적으로 그것이 어떻게 작동하는지는 이해해야 합니다. 그렇기 때문에 이산 수학, 즉 벡터, 행렬, 집합, 논리 연산 등에는 능숙할 필요가 있습니다. 게임 개발자의 커리어게임 개발자가 되기 위한 길이 게임 회사에 취직하는 것만 있는 것은 아닙니다. 최근에는 크게 성공하는 인디 게임, 즉 대규모 회사가 아닌 저예산의 1인기업 혹은 작은 팀단위로 만들어 내는 게임들의 사례가 늘어나고 있습니다. 게임 회사에 취직하는 것만큼 확실한 방법이 없다는 생각을 갖고 계신 분들, 혹은 자신만의 게임을 만드는 것에 강한 매력을 느끼시는 분들을 위해 두 가지 커리어 옵션을 비교해 보았습니다.1. 대규모 게임 회사대부분의 게임 개발자가 특정 회사에 소속되어 일을 합니다. 회사에 소속되어 있기에 안정적인 수입이 보장된다는 것이 첫번째 장점이라면, 두번째 장점은 혼자서는 절대 만들 수 없는 규모의 게임을 개발하는 데에 기여할 수 있다는 점입니다. 한 마디로 말해 완성도 있고 유명한 게임에 일조 했다는 자부심을 가질 수 있게 되는 것입니다. 또한 주니어 개발자로서 풍부한 경험을 가진 시니어 개발자를 포함해 배울 점이 많은 사람들로 구성된 팀에 소속될 수 있다는 것 또한 큰 장점입니다.한편 회사의 크기가 큰 경우에는 각 사람이 맡는 개발의 영역이 매우 세분화 되어있기 마련입니다. 자신이 느끼기에는 조금 지루하고 단순한 일이라고 생각되는 일을 맡게 될 수도 있습니다. 그러나 반대로 말하면 디자인, 기획, 마케팅 등 개발 외의 업무 등에 신경을 쓰지 않고 오직 자신의 일에 집중할 수 있는 환경이 제공되는 것이기도 합니다.2. 인디게임 개발규모가 있는 회사에 취직하는 것이 아니더라도 게임을 만들 수 있는 방법은 많습니다. 또한 안정적인 수입이 보장된 것은 아니지만, 성공하는 경우 생각는 것보다 그 수익이 큽니다. 예를 들어 트리오브라이프를 개발한 오드윈게임즈는 1년 간 20억의 매출에 도달했습니다. 단지 한 사람이 2주 동안 만든 게임, 숨바꼭질은 한 달만에 5000만원의 수익을 냈습니다. 물론, 이를 성공 신화에 불과하다고 말할 수도 있기 때문에 분명히 감수해야 하는 위험이 있는 커리어인 것이 사실입니다. 인디 게임 간에도 경쟁이 매우 치열하기 때문입니다.그럼에도 불구하고 소규모로, 혹은 혼자서 게임을 개발하는 사람들은 게임에 대한 애착을 가지고 개발 과정 전체를 아우르며 작업할 수 있다는 점에서 만족감을 느낍니다. 특히 투자 규모나 시기에 구애를 받지 않고 개성적인 게임, 만들고 싶은 게임을 만들 수 있다는 것이 장점이라고 할 수 있습니다. 지금 시작하기게임 개발을 하고 싶은데 어디서 시작해야 하는지를 막막해하고 있다면, 무조건 일단 만들어보기 시작하는 것이 중요합니다. 자신의 아이디어, 혹은 이미 있는 게임들을 가지고 점점 난이도를 높여가며 여러 프로젝트를 실행해 보는 것이 좋습니다. 이는 실력을 쌓는 데에도 도움이 되지만, 이후에 훌륭한 포트폴리오가 되기도 합니다.일단 만들어보라는 조언도 막막하신 분들을 위해 준비한 것은 무료로 사용할 수 있는 게임 개발 프로그램들입니다. 코딩을 전혀 할 줄 모르는 사람부터 완성도 있는 게임을 만들고 싶어하는 사람들까지 다양한 수준에서 접근할 수 있는 도구들을 소개해드리겠습니다.1.Flow CreatorFlow Creator는 코딩을 해본 적이 없어도 간단한 드래그앤드롭으로 게임을 만들 수 있는 웹사이트입니다. 시각적으로 논리적 구조를 짤 수 있기 때문에 어떤 언어도 배워본 적이 없어도 됩니다. 무료 버전의 경우 5개의 레벨, 50개의 개체로 제한이 되어있지만 유료 버전의 경우 앱으로 만들어 스토어에 올릴 수도 있습니다.2. StencylStencyl도 Flow Creator와 마찬가지로 프로그래밍 언어가 아니라 Stencyl의 사용법만 잘 익히면 훌륭한 게임을 만들 수 있습니다. 사용법이 Flow Creator에 비해 좀더 까다로운 것은 사실이지만 결과물의 완성도가 더 높습니다. 또한 이미 만들어져있는 코드블록 외에도 직접 코드를 작성하고 라이브러리를 불러오는 등 확장할 수 있는 가능성도 있습니다.3. Game Maker StudioGame Maker는 위의 두 가지 프로그램처럼 드랙 앤 드롭으로 만들 수 있지만, Game Maker Language(GML)이라는 자체 언어를 활용하여 만들 수도 있습니다. GML을 사용해서 게임을 만드는 것은 프로그래밍을 학습하는 데에도 도움이 될 것입니다.게임 개발자의 종류는 정말 많다.오늘 포스팅에서 언급한 게임 개발자는 일부입니다. 게임 개발자의 종류에는 온라인 게임, 모바일 게임, 콘솔 게임 등 정말 다양하고 무궁무진합니다. 여러분들이 어떤 게임 개발자가 되고 싶든 중요한 것은 게임에 대한 열정인 것 같습니다. 자신이 정말 하고 싶고 좋아하는 게임을 만든다는 것은 세상에 의미있는 프로그램을 만드는 개발자만큼이나 행복한 개발자겠지요. 다음 편에는 더 재밌는 개발자 직군으로 찾아오겠습니다.
조회수 927

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

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

음성 기반 인터페이스의 등장

필자가 재직 중인 일정 데이터 스타트업 히든트랙(린더)은 현재 SKT NUGU, Google Assistant에서 '아이돌 캘린더'라는 이름의 일정 검색/구독 서비스를 운영 중이며, 삼성 빅스비와 협업을 통해 내년 상반기 전시/공연 일정 검색/구독 서비스 상용화를 앞두고 있다.https://blog.naver.com/nuguai/221387861674세계적으로도 아직 음성 관련 서비스 사례가 많지 않은 상황에서 VUI 기반 서비스 개발에 도움이 될만한 자료를 국내에서 찾기는 더더욱 쉽지 않았고, 향후 음성 기반 서비스를 준비하는 다른 이들이 우리가 겪었던 시행착오를 줄일 수 있기를 바라는 마음으로 간단하게 5부작 형태의 글로 우리가 고민해온 과정을 준비해보았다.음성 서비스 시장의 확대해외 리서치 업체 닐슨에 따르면 2018년 2분기 기준 미국 가구 중 4분의 1에 해당하는 24%가 최소 1대 이상의 AI 스피커를 소유하고 있으며 미국 성인의 20%가 하루 1회 이상 음성 검색 서비스를 활용하고 있다. 국내 리서치 전문 기관인 컨슈머 인사이트에 따르면 국내 AI 스피커 사용 경험률은 11%에 달하며 올해 안으로 세계 5위 수준의 스피커 시장 점유율(3%)을 확보할 것으로 예상된다.아마존 에코는 시각 장애인들이 콘텐츠에 접근하는 속도를 최대 10배까지 빠르게 만들어주었으며 SKT 내비게이션 서비스 T-Map은 NUGU의 음성 인터페이스를 통해 터치 인터랙션을 26%까지 감소시켜 사고 위험을 줄였다.음성 서비스 시장이 확대되고 있다는 것과, 그 변화가 사람들의 삶에 많은 영향을 끼치고 있다는 것은 누구도 부정할 수 없는 자명한 사실이다.하지만 여전히 아쉬운 일상 속 음성 서비스 만족도그렇다면 과연 우리의 일상 속 음성 서비스 경험의 만족도는 어떨까?지난 4월 진행된 컨슈머인사이트의 조사에 따르면 국내 주요 음성 서비스에 대한 사용자 만족률은 49%로, 절반에 채 못 미치고 있는 상황이다."국내 음성 서비스 만족도 - 49%"주요 불만족 이유로는 ‘음성 명령이 잘되지 않는다’(50%), ‘자연스러운 대화가 곤란하다’(41%), ‘소음을 음성 명령으로 오인한다’(36%) 등이 꼽혔으며, 아직도 대다수의 사용자들에게 AI 스피커는 기업들의 서툰 시도로 인식되고 있다.국내 음성 기반 서비스 만족도는 타 스피커 상용화 국가들과 대비해서도 현저히 낮은 편인데, 유독 국내의 사용자들이 만족스러운 음성 서비스 경험을 누리지 못하고 있는 이유가 대체 무엇인지, 이번 글을 통해 잠시 논해보고자 한다.1. 과열된 AI 마케팅국내 'AI 스피커' 시장은 타 국가 대비 매우 치열한 점유율 경쟁이 벌어지고 있는 곳이다. 미국의 경우만 하더라도 구글 어시스턴트, 아마존 알렉사, 애플 시리의 삼파전이 벌어지고 있는 상황에서 국내는 KT 기가지니, SKT NUGU, 네이버 클로바, 카카오 i, 삼성 빅스비 등 5개가 넘는 다양한 플레이어들이 이 작은 시장을 차지하기 위해 혈투를 벌이고 있다.AI, 즉 인공지능은 사전적으로 '인간의 지능으로 할 수 있는 사고, 학습, 자기 개발 등을 컴퓨터가 할 수 있도록 하는 방법'을 뜻하는데, 현존하는 대다수의 속칭 'AI' 서비스들이 해당 수준에 다다르기에는 아직 많은 시간이 필요하다는것은 누구도 부정할 수는 없을듯 하다. 경쟁이 과열되다 보면 제품을 판매하기 위해 다소 공격적인 선택을 하는 경우가 있고, 현재 국내에서 이루어지고 있는 AI라는 용어의 지나친 남발이 바로 그 대표적인 예시라고 할 수 있다.멀리 갈 것 없이 각 나라에서 스피커를 부르는 호칭을 보면 잘 알 수 있는데, 우리가 흔히 'AI 스피커'라 부르는 구글 홈, 아마존 에코 등 대다수의 스피커는 미국 내에서 '스마트 스피커'라는 단어로 통용된다.(구글에 AI Speaker를 검색해보면 Smart Speaker로 자동 대체되는 것을 확인할 수 있다)구글 내 AI 스피커 검색 결과(첫 두 검색은 광고)즉, 아직은 '스마트'하다고 부를 수밖에 없는 수준의 기능에 대한 과장 된 'AI 마케팅'으로 인해 국내 사용자들은 시장 생성 초기부터 고도화된 인공지능을 기대하게 되고, 이는 결국 자연스레 낮은 사용자 만족도로 이어질 수밖에 없는 것이다.향후 AI가 음성 기반 서비스의 핵심 기술이 될것은 분명하지만 당장의 지나친 기대감은 되려 국내 음성 기반 서비스의 *캐즘 기간을 장기화시킬 수 있을것으로 우려된다.*캐즘: 첨단기술 제품이 선보이는 초기 시장에서 주류시장으로 넘어가는 과도기에 일시적으로 수요가 정체되거나 후퇴하는 단절 현상2. 조금 더 시간이 필요한 기술력앞서 언급한 컨슈머 인사이트의 조사에 따르면 사용자의 불만족 이유 중 TOP 3 모두가 '낮은 인식률' 바탕으로 하고 있는 것을 재차 확인할 수 있다.1. 음성 명령이 잘되지 않는다(50%)2. 자연스러운 대화가 곤란하다(41%)3. 소음을 음성 명령으로 오인한다(36%)  컨슈머인사트 AI 스피커 만족도 통계음성 서비스 경험은 사용자의 명확한 의사가 전달되지 않는다면 애초에 시작될 수 없다. 자연스러운 대화를 진행하기 위해서는 결국 사람의 언어, 즉 자연어를 분석하여 의도를 파악할 수 있어야 하며 이를 실현하기 위해서는 아래에 소개 된 ASR(음성 인식)과 NLU(자연어 처리)가 높은 수준으로 구현되어야 한다.T map X NUGU 디자인 사례로 알아보는 음성인터페이스 디자인 1강 - https://youtu.be/Dz-rxGV-dOAASR과 NLU 성능이 뒷받침되지 않는 음성 서비스는 아무리 고도화 된 서비스 로직이 준비된들 '대화'가 진행될 수 없으며 부족한 성능은 결국 국내 대다수 스피커들이 "죄송합니다. 무슨 말인지 이해 못했어요"를 출력하며 사용자 불만족도를 상승시키는 주요 요인으로 볼 수 있다.인식 정확도를 상승시키기 위해서는 결과적으로 더 많은 양의 학습 데이터가 필요하며 대다수의 업체가 아직 관련 기술력이 많이 부족한 상황에서도 공격적으로 스피커를 출시하는 이유 또한 결국 초기 점유율 높여 이 학습 데이터를 지속적으로 쌓기 위해서다.국내에서는 아직 높은 수준으로 두 단계를 구축한 메이저 업체가 없는 상황에서, 국내 기업들은 경쟁력을 확보하기 위해 관련 기술력을 가진 국내외 다양한 기업에 지속적으로 투자를 늘려나가고 있는 상황이다.http://www.zdnet.co.kr/view/?no=201702231628363. 더 많은 고민이 필요한 음성 사용자 경험(VUX) 디자인이번 협업 프로젝트를 진행하며 VUX를 공부하는 과정에서 우리의 사례를 포함한 몇 가지 재미있는 질문들을 발견할 수 있었다.질문1. 음악 앱이 재생되는 상황에서 사용자가 "앞으로 10초"라고 말했다면, 빨리 감기를 하는 게 맞을까 되감기를 하는 게 맞을까? - 네이버 클로바 사례질문2. 자정이 살짝 넘은 새벽 1시, 사용자가 "내일 일정 알려줘"라고 말했다면, 향후 23시간 동안의 일정을 알려주는 게 맞을까 23시간이 지난 그 다음날 일정을 알려주는 게 맞을까? - 히든트랙 린더(빅스비, SKT 파트너 스타트업) 사례질문3. '오늘'이라는 이름의 기업이 존재하는 상황에서 "오늘 기업 정보 알려줘"라고 말했다면, 오늘의 주요 기업 정보를 제공하는게 맞을까 주식회사 '오늘'의 정보를 제공하는게 좋을까? - 딥서치(빅스비 파트너 스타트업) 사례앞서 언급했던 1,2번의 사용자 만족도 문제가 이미 어쩔 수 없는 국내 시장의 지나친 경쟁과 더 시간이 필요한 기술력에 대한 아쉬움을 토로하는 내용이었다면, 3번의 VUI상의 새로운 경험에 대한 고민들이 이번 글을 쓰게 된 계기이자 목적이라고 볼 수 있다. 아직도 각 질문에 대한 뚜렷한 정답이 없는 상황에서 위와 같은 고민들을 함께 논의하며 최대한으로 정답에 가까운 선택을 내릴 수 있었으면 한다.클로바의 "앞으로 10초", 린더의 "내일 일정 알려줘", 딥서치의 "오늘 기업 정보 알려줘"에 대한 해답과 같이 '최선'이라고 부를 수 있는 가이드가 아직 존재하지 않는 현 VUX 시장은 더욱더 깊은 고민과 통찰이 필요한 시점이다. 단순히 해외 사례를 그대로 인용하여 국내 서비스에 적용하는 것이 아닌 정서와 문화, 그리고 각 콘텐츠에 대한 높은 이해도를 바탕으로 적절히 녹여낼 수 있어야 한다.올해 초 처음으로 챗봇을 디자인해보며 겪었던 애로사항들을 적은 부족한 글이 새로운 디자인을 시도하는 이들에게 조금이나마 도움이 되었다는 피드백을 받을 수 있었고,http://magazine.ditoday.com/ui-ux/일정-구독-서비스-린더의-탄생/이에 용기를 얻어 이번에는 다소 길지만 조금 더 많은 내용을 담고 있는 글을 준비하게 되었다.SKT NUGU, 삼성 빅스비와의 협업 과정에서 '음성 기반 인터페이스(VUI)'는 챗봇과는 확연히 다른 또 다른 형태의 디자인이라는 것을 알 수 있었고, 단순히 대화형 인터페이스(CI: Chatting Interface)를 음성의 형태로 재가공하는 것이 아닌, 서비스 기반부터 리디자인이 필요하다는것을 깨달았다.이미 구글, 아마존, 애플 등 메이저 업체들이 수년간의 경험과 데이터를 기반으로 다양한 VUX 가이드라인을 제시하고 있으며, 최근에는 SKT NUGU, 네이버 클로바 등 국내 업체들도 조금씩 VUX 서비스 제작에 대한 구체적인 로드맵을 제공하고 있는 상황이다.https://developers.nugu.co.kr/docs/voice-service-design-guideline/앞으로 약 다섯 달간 연재 진행 예정인 향후 4편의 내용들은 위 가이드 문서들에서 언급하는 다양한 해외와 국내 사례들을 바탕으로 주제를 선정하였으며, 각 편의 내용들은 VUI 서비스 제작 경험이 있는 다양한 국내 회사들의 고민 과정을 조금씩 담고 있다.1편: 음성 기반 인터페이스의 등장2편: 음성 기반 인터페이스와 TPO3편: 음성 기반 인터페이스와 페르소나4편: 음성 기반 인터페이스 vs GUI5편: 국내 음성 기반 인터페이스 현황음성 인터페이스는 정말 유용할까?음성 인터페이스는 먼 미래의 것이 아니다. 우리는 이미 수 년 전부터 다양한 종류의 음성 인터페이스를 접해왔으며, 그중 대표적인 예시가 바로 누구나 한 번쯤은 경험해보았을 ARS, 자동응답 시스템이다.각종 정보를 음성으로 저장 한 후, 사용자가 전화를 이용하여 시스템에 접속하면 음성으로 필요한 정보를 검색할 수 있도록 사용법을 알려주고, 필요한 정보를 찾으면 이를 음성으로 들려 주는 바로 그 시스템이 현 음성 인터페이스 경험의 모태라 할 수 있다.예약을 진행하는 과정에서 어떤 제품군을 수리 맡기고 싶은지, 냉장고인지, 컴퓨터인지, 노트북인지, 핸드폰인지 '말로 검색하고 말로 예약 확인을 받는' 바로 그 과정이 바로 수년 전부터 존재해온 음성 인터페이스이다. 우리가 말로, 음성으로 수리하고 싶은 제품을 말하고 응답을 받아온 이유는 간단하다.더 편했기 때문이다.다만 그렇다고 해서 음성 인터페이스가 모든 분야를 혁신시킬 변화의 축이 되기는 힘들다.음성 입출력의 한계는 매우 명확하며, 시각적 입출력이 반드시 필요한 산업과 분야(음식, 지도 등)는 꾸준히 기존과 같은 시각 기반의 인터페이스를 필요로 할 것이다.모든 분야에 적용될 수는 없는 음성 인터페이스이지만 한가지 확실한 것은 이제 시작이라는 것이다.다소 장황하고 부족한 이 글이 조금이나마 앞으로의 험난한 여정을 도울 기초적인 가이드가 될 수 있었으면 하는 마음으로 연재를 시작해본다.저도 아직 많이 낯선 분야인만큼 의아하시거나 틀린부분이 있다면 댓글로 많은 지적 및 피드백 부탁드립니다. 감사합니다 :)#히든트랙 #음성기반기술 #스타트업인사이트 #UX디자인 #음성기반디자인
조회수 1302

레진 기술 블로그 - Kotlin의 빛과 그림자

핀터레스트의 안드로이드 개발팀이 코틀린을 도입하면서 겪은 어려움과 해결책을 소개한 The Case Against Kotlin을 foot번역하고 자의적으로 해석하고 요약했습니다. 저자 라이언 쿡(Ryan Cooke)은 현재 코틀린이 가트너의 하이프 사이클에서 “뻥튀기된 기대감의 산(Peak of Inflated Expectations)” 쯤에 있다고 말합니다. 레진시 개발동에서는 이미 코틀린을 부분적으로 도입했고, 현재는 범위를 넓혀가는 중인데요… 정말 괜찮은 걸까요?문제: 학습 곡선자바 개발자로서 문법에 익숙해지는 데 1주일 정도 걸립니다.코틀린을 이미 잘하는 사람이 없으면 베스트 프랙티스들을 찾아보면서 해야하는 데 시간이 듭니다.코틀린 사용을 가속화 시키는 데 팀 트레이닝을 계속 해야합니다. -> 기회비용 많이 듭니다.하기 싫어 하는 사람도 있고…혼자서 알아서 잘 배우는 사람도 있고…해결책: 학습 곡선코틀린은 아직 말년병장성숙한 언어가 아닙니다! 지금도 자라나고 있습니다! 그게 제일 무서워..책도 있고 인터넷 리소스도 있지만, 코틀린 신봉자가 하나 있어서 다 가르쳐주는 게 짱입니다.필자가 코틀린을 하고 싶었던 이유는 생산성인데요, 동료들 중에는 그렇게 느꼈던 사람들이 많지 않은 것 같습니다. 정착이 되면 보이겠죠.문제: 빌드 속도Gradle 빌드 속도는 보통 30초, 클린 빌드는 75초 까지 걸립니다.코틀린은 보통 빌드 속도의 25%, 클린 빌드의 40% 밖에 안나옵니다.해결책: 빌드 속도알아서 하셈 ㅋ코틀린 파일 하나 변환 -> 클린 빌드 시 조금 시간이 더 걸립니다. 파일을 많이 변환할수록 느려지긴 하지만 체감하긴 어렵습니다.보통 빌드할 때는 코틀린 파일 많아도 상관 없습니다.결론: 클린 빌드할 때 느려진다는 걸 체감할 겁니다.문제: 개발 안정성코틀린의 문법이나 특성이 문제가 아니라, 코드를 생산성 있게 작성하는 자신을 막는 새로운 문제들 때문이라고 생각합니다.사실 그냥 코틀린 배우기 싫은 거 같아요.예를 들면, 코틀린 애노테이션 프로세서 툴(kapt) 때문에 빌드가 안 되고, 무조건 클린 빌드로만 개발을 했던 적이 있습니다.이거… 코틀린 때문 아니야?!?!?! 하는 의심들 많았죠.고치느라 시간이 많이 흘렀습니다.또 어떤 문제가 튀어나올지에 대한 두려움이 커지네요.해결책: 개발 안정성그냥 IDE 나 언어의 stable 버전만 업데이트 하세요.안정된 버전들만 사용하면 그나마 힘든 일 없을거예요.정말?문제: 정적 분석FindBugs, PMD, Error Prone, Checkstyles and LintJava 는 이와 같은 툴들로 인해 Code Review에 쓸데없는 걸 줄이거나 룰을 적용할 수 있는데,코틀린에는… 이런 게 없… 분석을 위한 게 아직… 없습니다… 사람들이 알아서 다 찾아야 합니다.해결책: 정적 분석그냥 손가락빨고 기다려야 합니다. 아니면, 직접 만드세요!문제: 나 돌아갈래~돌아가기 쉽지 않습니다. 자바를 코틀린으로 옮기기에는 쉬운데, 반대는… 어렵습니다!코드가 깨지고, 변수명부터, 이런 저런 부분들을 다시 구현해야합니다.코틀린스럽거나, 코틀린의 고유한 기능들을 사용했다면, 여기서부터 헬이죠.해결책: 나 돌아갈래~되돌아오는 건 쉽지 않기 때문에 잘 생각해야 합니다.유닛 테스트가 정말 잘 된 파일들부터 바꾸세요.간단하고 재사용 가능한 잘 모듈화된 파일들을 먼저 바꾸세요.결론이 글은 고려해야 할 리스크에 대해서 나열했습니다.단점들은 구글과 젯브레인과 스택오버플로우가 차차 해결해 줄 겁니다.TL;DR 코틀린으로 작성하는 건 쉽지만, 되돌리기는 어렵습니다.그래서 말인데… 레진코믹스에서 코틀린 삽질을 함께 할 개발자를 모십니다!
조회수 2795

Radix? Redis!

얼마전부터 antirez twitter에서 radix tree 관련 트윗이 올라왔습니다. 얼마 지나지 않아 antirez가 radix tree를 구현한 rax 프로젝트를 공개하고 redis의 cluster hash_slot의 저장구조를 radix tree로 수정 되는것을 보았습니다.그동안 antirez의 코드 읽으면서 배우는 게 많았고, 자료구조에 관심이 많아서 살펴보기 시작했습니다. radix tree를 왜 구현 했는지, 어떻게 구현쟀는지 알아보고 radix tree를 redis에 어떻게 적용하였는지도 알아보겠습니다.antirez는 redis의 hash-slot -> key 구조에서 중복으로 인한 메모리 사용을 줄이기 위해 radix tree 를 만들었다고 합니다. 이 포스트에선 rax를 적용시킨 redis cluster로 이야기를 진행 하겠습니다.“현재는 hash-slot -> key에만 사용되지만 추후에는 다양한 곳에 사용 예정”이라는 트윗redis cluster?redis에는 cluster 기능이 있습니다.6대 이상의 redis 노드를 cluster 구성하면(최소 leader 3대, follower 3대 구성해야 cluster 가능) 16384개의 hash_slot이 노드 갯수에 맞게 분배가 됩니다. 즉 3대의 leader로 cluster 구성하면 각각의 leader는 0 ~ 5460, 5461 ~ 10922, 10923 ~ 16383 hash_slot을 나눠 가집니다.cluster 구성 후 client가 데이터 저장/삭제/조회 명령어를 redis server에 전송할 때 마다 key의 hash값을 구하고 어떤 leader hash_slot에 포함되는지 찾습니다.# example 127.0.0.1:7000> set hello world # hash_slot = crc16("hello") & 0x3FFF 계산된 값이 현재 접속한 leader의 hash_slot 범위에 있다면 그대로 실행 되지만 다른 leader의 hash_slot 이라면 에러를 발생하고 다른 leader로 이동하라고 힌트를 줍니다.cluster 구성 후에 노드를 추가 하거나 제거 할 경우 각 leader의 hash_slot을 재분배 하고, hash_slot에 맞게 key도 재분배 되어야 합니다. 단순하게 생각하면 leader의 hash_slot 재분배한 후 모든 key를 재계산하고 hash_slot에 맞는 leader에 할당 하는 겁니다.[현재까지 저장된 keys].forEach(v => { hash_slot = crc16(v) & 0x3FFF // leader에 할당된 hash_slot에 맞게 분배 }) 하지만 antirez는 redis Sorted set 데이터 타입의 구현체인 skiplist 을 이용하여 문제를 풀었습니다. skiplist는 member와 score를 저장하고, score를 기준으로 정렬합니다. skiplist의 member에는 key를 저장하고 score에는 key의 hash_slot을 저장합니다.(변수명 slots_to_keys)slots_to_keys 정보는 cluster 구성된 모든 노드가 저장합니다. 이후 재분배가 필요해지면 16384개 hash_slot을 leader 갯수에 맞게 재분배 하고 slots_to_keys에 저장된 “key:hash_slot” 정보를 가지고 해당 hash_slot의 key를 조회 및 재분배 합니다. 즉 slots_to_keys에 이용하여 재분배시 발생하는 계산을 없앤것입니다.잘 했구만 뭐가 문제냐?redis에 key가 추가/삭제 될때마다 slots_to_keys에 데이터가 저장되고 지워집니다. redis에 저장되는 key 갯수가 증가 할수록 slots_to_keys의 크기도 커짐을 의미 합니다.(※ 메모리 사용량)또한 leader 갯수에 맞게 16384개 hash_slot을 leader에 재분배하고, 각 hash_slot에 맞는 key를 찾고 할당 합니다. 예를들어 slots_to_keys에서 score 0인(hash_slot 0을 의미) member를 조회해서 0번 hash_slot에 할당, score 1인 member를 조회해서 1번 hash_slot에 할당 하는 방식으로 0 ~ 16383 hash_slot을 진행합니다.앞에서 말한 hash_slot에 속한 key를 조회 하는 GETKEYSINSLOT 명령어가 있는데 여기에 이슈가 있습니다.cluster GETKEYSINSLOT slot count # slot: hash_slot 번호 # count: 특정 hash_slot에서 조회할 key 갯수 # example 127.0.0.1:7000> cluster GETKEYSINSLOT 0 3 # 0번 hash_slot의 key를 3개 조회한다. "47344|273766|70329104160040|key_39015" "47344|273766|70329104160040|key_89793" "47344|273766|70329104160040|key_92937" 사용자가 특정 hash_slot에 몇개의 key가 저장 되었는지 모르기때문에 count에 Integer.MAX 를 대입하는데, redis는 hash_slot에 실제로 저장된 key 갯수와는 상관없이 client가 전달한 count만큼의 메모리를 할당합니다.} else if (!strcasecmp(c->argv[1]->ptr,"getkeysinslot") && c->argc == 4) { /* cluster GETKEYSINSLOT */ long long maxkeys, slot; unsigned int numkeys, j; robj **keys; // ... 명령어의 4번째 인자를 maxkeys에 할당, 즉 사용자가 입력한 count if (getLongLongFromObjectOrReply(c,c->argv[3],&maxkeys,NULL) != C_OK) return; // ... keys = zmalloc(sizeof(robj*)*maxkeys); numkeys = getKeysInSlot(slot, keys, maxkeys); addReplyMultiBulkLen(c,numkeys); for (j = 0; j < numkeys>zmalloc maxkeyscluster GETKEYSINSLOT unnecessarily allocates memory그래서 메모리도 적게 차지하면서(압축 가능) key와 key의 hashslot을 효율적으로 저장 및 조회가 가능한 자료구조가 필요했고 antirez는 radix tree를 선택합니다.※ 뜬금 없는데 2012년, redis 자료형에 Trie를 추가한 P/R이 생각났습니다.radix tree 구현한 rax 알아보기시작하기전 radix tree (Wikipedia) 위키 페이지의 그림을 보고 감을 잡은 후에 아래를 보시면 잘 읽힙니다.자! 이제부터 rax의 주석과 코드를 보면서 어떻게 구현됐는지 알아보겠습니다.Noderax의 노드 구성은 다음과 같습니다.typedef struct raxNode { uint32_t iskey:1; /* Does this node contain a key? */ uint32_t isnull:1; /* Associated value is NULL (don't store it). */ uint32_t iscompr:1; /* Node is compressed. */ uint32_t size:29; /* Number of children, or compressed string len. */ unsigned char data[]; } raxNode; 노드의 정보를 담고있는 32 bit(iskey, isnull, iscompr, size)와 key/value 그리고 자식 노드의 포인터를 저장하는 unsigned char data[]가 있습니다. 특이한 점은 key/value를 동일한 노드에 저장 하지 않고 key가 저장된 노드의 자식 노드에 value를 저장합니다.※ 사진 출처위 그림을 예로 32 bit 정보가 어떤걸 의미하는지 알아보겠습니다.iskey는 노드가 key의 종착역(iskey:1)인지 중간역(iskey:0)인지 나타내는 flag입니다. 1, 3 노드는 iskey:0 이고 2, 4, 5, 6, 7 노드는 iskey:1이 됩니다.isnull은 value의 null 여부를 표시합니다. unsigned char data[]에 key/value 그리고 자식 노드의 포인터를 저장하므로 value를 찾으려면 계산이 들어갑니다. 불필요한 연산을 줄이기 위해 만든 필드 같습니다.Trie는 각 노드에 한글자씩 표현 하지만 Radix는 압축을 통해 한 노드에 여러 글자 표현이 가능합니다. 이를 나태내는 플래그 iscompr 입니다. 노드가 압축된 노드(iscompr:1)인지 아닌지(iscompr:0)를 나타냅니다.size는 iscompr 값에 따라 의미가 다릅니다. iscompr이 1이면 저장된 key의 길이를 의미하고 iscompr이 0이면 자식노드의 갯수(저장된 key의 갯수)를 의미합니다.위 4개 정보를 이용해서 한 노드의 크기를 구하는 코드는 아래와 같습니다.#define raxNodeCurrentLength(n) ( \ sizeof(raxNode)+(n)->size+ \ ((n)->iscompr ? sizeof(raxNode*) : sizeof(raxNode*)*(n)->size)+ \ (((n)->iskey && !(n)->isnull)*sizeof(void*)) \ ) ※ 노드에 value 주소를 저장하거나, 마지막 자식 노드 포인터를 알고 싶을때 사용합니다.FindraxLowWalk 함수를 이용해 key가 존재 하는지 판단합니다.size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) rax에 “ANNIBALE” -> “SCO” -> [] 로 저장 되어있을때 어떤 값을 리턴하는지 알아보겠습니다.*s 가 “ANNIBALESCO”이고 len이 11 인 경우# splitpos: 0, return value: 11 "ANNIBALE" -> "SCO" -> [] ^ | *stopnode *s가 “ANNIBALETCO”이고 len이 11인 경우# splitpos: 0, return value: 9 "ANNIBALE" -> "SCO" -> [] ^ | *stopnode *s의 길이 len과 return value가 같다면 rax에 key가 존재하는 것입니다. *s의 길이 len과 return value가 다른 경우 어디까지 매칭됐는지 보여주는 return value와 어떤 노드에 어디까지 일치했는지 표현하는 *stopnode, splitpos를 통해 추가 정보를 얻을수 있습니다.InsertraxLowWalk 함수를 이용해서 저장할 위치를 찾습니다. (*stopnode, splitpos, return value)1번에서 구해진 데이터를 이용해서 새로운 노드 생성 및 링크를 연결합니다.rax에 “ANNIBALE” -> “SCO” -> [] 상태에서 “ANNIENTARE”를 저장하는 과정입니다.1. raxLowWalk 함수를 이용하여 저장할 위치 탐색 splitpos: 4, return value: 4 "ANNIBALE" -> "SCO" -> [] ^ | *stopnode 2. *stopnode, splitpos 데이터를 이용하여 노드 분리 "ANNI" -> "B" -> "ALE" -> [] 3. iscompr: 0인 노드 "B"를 기준으로 새로운 key 저장 ("B"와 "E"는 같은 노드) |B| -> "ALE" -> [] "ANNI" -> |-| |E| -> "NTARE" -> [] RemoveraxLowWalk 함수를 이용해서 저장할 위치를 찾습니다. (*stopnode, splitpos, return value)1번에서 구해진 데이터를 이용해서 노드 제거 및 compress가 가능다면2가지 경우가 있습니다.마지막 노드만 iskey: 1이고, 연속으로 iscompr:1인 노드가 된 경우마지막 노드만 iskey: 1이고, iscompr:1 -> iscomplr:0 -> iscomplr:1 노드 구조가 된 경우입니다.첫번째 경우를 알아 보겠습니다. rax에 “FOO” -> “BAR” -> [] 상태에서 “FOO”를 지우는 과정입니다.1. raxLowWalk 함수를 이용하여 저장할 위치 탐색 splitpos: 3, return value: 3 "FOO" -> "BAR" -> [] ^ | *stopnode 2. 해당 key 삭제, 여기서는 자식노드가 있으므로 노드 삭제는 하지 않고 노드의 iskey: 0으로 세팅 "FOO" -> "BAR" -> [] 3. compress가 가능한 경우 진행 "FOOBAR" -> [] 두번째 경우를 알아 보겠습니다.0. "FOOBAR"와 "FOOTER"가 저장된 상황입니다. FOOTER를 지우는 경우입니다. |B| -> "AR" -> [] "FOO" -> |-| |T| -> "ER" -> [] 1. raxLowWalk 함수를 이용하여 저장할 위치 탐색 splitpos: 0, return value: 6 |B| -> "AR" -> [] "FOO" -> |-| |T| -> "ER" -> [] ^ | *stopnode 2. 해당 key 삭제 "FOO" -> "B" -> "AR" -> [] 3. compress가 가능한 경우 진행 "FOOBAR" -> [] cluster 정보는 어떻게 저장되나?기존 skiplist 자료구조를 이용했던게 어떻게 변경 되었는지 알아보겠습니다.server.cluster->slots_keys_count[hashslot] += add ? 1 : -1; if (keylen+2 > 64) indexed = zmalloc(keylen+2); indexed[0] = (hashslot >> 8) & 0xff; indexed[1] = hashslot & 0xff; memcpy(indexed+2,key->ptr,keylen); if (add) { raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL); } else { raxRemove(server.cluster->slots_to_keys,indexed,keylen+2,NULL); } 먼저 slots_keys_count 변수를 이용하여 각 hash_slot의 key 갯수를 저장합니다.그리고 key는 hash_slot(2 byte) + key, value는 NULL로 rax에 저장하여 특정 hash_slot에 속한 key 조회를 쉽게 만들었습니다.마치며rax 구현과 rax가 어떻게 redis에 적용됐는지 보면서 오랜만에 재밌게 코드를 읽은것 같습니다. 개인적으로 데이터 관련 유용한 무언가를 만드는게 목표인데, 이런 좋은 코드들을 하나 둘씩 제것으로 만드는것도 과정이라 생각하며 진행했습니다.앞으로 rax가 redis에서 어떻게 쓰일지 흥미롭고, Redis를 Saas 형태로 제공하는 업체들이 언제 적용할지도 궁금합니다.긴 글 읽어주셔서 감사합니다.cluster, rax 관련 antirez twitterRedis cluster Insertion cluster Issuesame amount data hash table vs radix treehashset + ziplist -> radix tree + listpack 1/5replace Hashset with Radix treeraxNode에서 사용한 flexible memberflexible memberrax 를 이용한 Redis Streams(2017.12.17일 업데이트)Redis Stream#잔디 #토스랩 #JANDI #기술스택 #도입후기 #Redis #인사이트
조회수 3645

jekyll을 이용한 Github 블로그 만들기

Overview“githubPage로 기술 블로그를 만들자!” “jekyll로 만들면 한두 시간이면 가능할 거야!” 지난 1월, 브랜디 기술 블로그 제작 작업을 시작했습니다. 다양한 삽질과 험난한 여정의 결과물인 기술 블로그의 제작 및 커스터마이징 과정을 소개하겠습니다.GithubPage는 Github에서 공식적으로 운영하는 블로그 서비스입니다. 이곳엔 개발자들의 경험이나, 코드가 업로드되어 있습니다. 저장 공간도 무료로 제공되고, 도메인 연결도 편리하게 할 수 있지만, 무엇보다 GithubPage 혹은 GithubIO라고 하면 개발자 스멜이 풀풀 나기 때문에 선택의 이유가 되는거 같습니다.GitgubPage 제작 프로그램은 jekyll, HEXO가 가장 많이 쓰입니다. 브랜디의 기술 블로그는 템플릿의 종류도 많고, 더 어울리는 jekyll을 선택했습니다. jekyll 공식 사이트는 여기를 클릭하세요. 한국어도 지원하니 아주 멋집니다. 변역된 문서가 2015년 11월 23일 문서인 게 함정이지만 기능의 거의 유사하기 때문에 문제될 것은 없습니다. 1. 준비물을 챙기자!$ gem install jekyll $ jekyll new my-awesome-site $ cd my-awesome-site /my-awesome-site $ jekyll serve ▲ jekyll 설치 스크립트이제 브라우저로 http://localhost:4000 에 접속합니다.메인에 내거는 것처럼 설치와 실행이 쉽지만 몇 초 만에 되진 않습니다. 설치가 몇 분 정도 걸리고 ruby나 bundler같은 선행 조건이 갖춰져야 하기 때문입니다.ruby는 있는데 bundler가 없다면 아래와 같이 설치하면 됩니다.gem install bundler 설치가 잘 되고, 사이트 생성 후 실행을 했다면 브라우저와 함께 기본 페이지가 뜹니다. 설치가 성공했다!2. 1차 멘붕, 이제 무엇을?설치는 제법 쉽게 했지만 ‘이제 무엇을 해야 하나’ 막막하기만 합니다. 블로그 작성에 대한 아무런 가이드도 없고, 페이지나 이미지 추가 확인 등의 작업을 커멘드로만 하려니 힘들고 아찔하기만 합니다.커멘드 지옥..jekyll admin을 검색했더니 이런저런 아이들이 나옵니다. 그중에 jekyll 공식 플로그인을 선택했습니다. jekyll-admin 공식 사이트 이미지그런데 사이트 메뉴얼만 보고 설치하기 쉽지 않습니다. 이제 막 jekyll 띄웠을 뿐인데 플로그인은 어떻게 추가하는지 알 길이 없습니다. 이런저런 삽질을 거듭하고 안 되는 영어를 해석하다 보니 얼떨결에 성공했습니다.추가한 프로젝트 root에 보시면 Gemfile이 존재합니다.아마도 사이트 제너레이트 시 실행되는 스크립트인 것 같습니다. 파일을 열고 아래와 같이 추가합니다.# 아래와 같이 한줄 추가해주세요 gem 'jekyll-admin', group: :jekyll_plugins 프로젝트 root로 이동해 설치를 요청합시다.bundle install 달라진 게 없어 보여도 http://localhost:4000/admin 으로 접속하니 아뉫! 관리자가 설치되었습니다. 이제 한시름 놓입니다.3. 마크다운, 넌 누구?마크다운을 잘 안다면 넘어가도 됩니다.관리자를 설치하고 나면 그나마 좀 할만하지만 막상 글을 쓰려고 보니 무언가 다릅니다. HTML이나 위지윅 에디터도 아니고 Textarea만 덩그러니 있기 때문입니다....마크다운은 위키나 Github페이지 설명 작성 등에 쓰이는 언어입니다.1) HTML을 어느정도 한다면 문법만 읽어도 금방 이해할 수 있습니다. 생각보다 어렵지 않아서 간단한 문서 작성은 수월하게 가능합니다. 무엇보다 코드를 붙여넣을 때 아주 좋습니다. ``` PHP 코드 내용 ```위의 그림처럼 작성하면 자동적으로 신텍스 하이트라이트가 적용되니 개발 코드를 전달하기 편리합니다.4. Posts? Pages? Static Files? Data Files?확실히 jekyll은 그동안 봤던 블로그나 워드프레스 등 유명한 블로그와는 많이 다릅니다. 일단 개념부터 짚어보겠습니다. PostsPost는 한 개의 글을 지칭합니다. 블로그의 글 하나입니다. 어느 정도 구축이 되면 Post에서 글만 작성해도 쉽게 운영할 수 있습니다.PagesPost처럼 계속 추가되는 형태가 아닌 고정 페이지를 작성할 때 씁니다. About이나, 채용, 회사소개 등 Post와 분리가 필요한 글을 작성할 때 유용합니다.Static Files정적 리소스를 올리는 기능입니다. 생각보다 버그가 많아서 사용하기 쉽지 않습니다. 저는 이 기능을 커스터마이징해서 약간 쓰기 쉽게 바꾸었지만 쉽지는 않았습니다. 자세한 관리자 커스터마이징은 나중에 다루겠습니다.Data Files정적 데이터를 다루는 기능입니다. 저자 관리나 공통 변수를 담아두면 편리하게 쓸 수 있는 기능입니다. 역시나 버그가 넘쳐납니다. 당분간은 그냥 파일을 직접 수정하는 게 나을 겁니다.5. 블로그 제목 등 설정을 바꾸고 싶다면관리자의 configuration 메뉴를 이용하거나, 프로젝트 루트에서 _config.yml 을 열고 수정해도 됩니다. 사이트에서 사용할 전역 변수나, 플로그인, 기본값 등을 관리해주기 때문에 자주 수정하는 파일입니다. 제목을 변경하려면 title을 찾아서 변경하면 됩니다. 그외의 하단 문구는 buttomtitle을 변경하면 됩니다. 아래 보이는 각종 정보들은 맞게 수정하면 되고, social: 밑에 있는 정보들은 나중에 페이스북 공유나, 트위터 공유 등으로 사용할 수 있습니다. 해당 정보가 없거나 공유를 원치 않는다면 share를 false로 변경합니다. _config.yml은 수정 후 재시작을 해야만 반영되므로 jekyll를 다시 실행하면 됩니다.6. 테마를 적용하자테마 기능은 jekyll를 선택한 가장 중요한 이유였습니다. 멋진 디자인과 추가로 구현된 특수한 기능들은 jekyll이 가지고 있는 큰 메리트입니다. 테마를 사용하려면 소스를 다운로드 받고 압축을 해제해 사용하거나 git checkout 하면 됩니다. 해당 폴더로 이동해 실행하면 테마를 쉽게 사용할 수 있습니다.jekyll serve jekyll은 테마가 완성된 프로젝트 개념이기 때문에 바로 사용이 가능하지만 마이그레이션 이슈가 있습니다. 마이그레이션은 _post의 있는 파일과 _page에 있는 파일을 그저 테마 프로젝트 폴더에 덮어쓰기하면 됩니다.아쉽게도 _config.yml파일은 다시 세팅하는 게 빠릅니다. 어드민 설정 부분도 다시 하면 됩니다. 테마마다 약간씩 기능이 달라 마이그레이션이 안 되는 경우도 있으니 테마는 초기에 선택하는 게 좋습니다. 브랜디 랩스는 Centrarium 테마를 적용했습니다.테마가 적용된 화면7. 글에 이미지를 어떻게 넣을까?글을 쓰면 참고자료로 쓸 이미지도 필요합니다. static file에 업로드 기능이 있지만 업로드를 하면 프로젝트 루트 폴더에 업로드되어 관리상 좋지 않습니다. 앞서 공유한 것처럼 해당 기능 개선에 대해서는 다루지 않을 것이기 때문에 수기로 이미지를 관리하는 방법을 소개하겠습니다.로컬 프로젝트 기준에서 _site는 제너레이트된 최종결과라고 할 수 있습니다. 그래서 _site 폴더에 assets와 같은 폴더가 있으나 그 폴더에 올리면 덮어쓰기와 동시에 초기화 되므로 반드시 프로젝트 루트의 assets에 파일을 올려주시면 됩니다. 폴더를 생성하는 것도 문제 없으므로 포스팅마다 이미지를 나누길 권장합니다. 이미지가 폴더에 복사가 되었다면 이제 글에 넣어봅시다.마크다운 위지윅을 이용해도 좋고 이미지 부분을 HTML코드롤 사용해도 좋습니다. 마크다운으로 이미지를 추가할려면 아래처럼 사용하면 됩니다. ![이내용은 alt속성으로 치환됨](/assest/20180118/test.jpg "이 내용은 타이틀로 치환 됨") assets/test.jpg적용된 이미지이미지의 사이즈나 정렬을 변경하는 건 다음에 다루겠습니다.8. Gnav 변경은 어떻게 할까?커스터마이징한 Gnav영역테마도 적용했고, 글도 쓸 수 있지만 안 쓰는 기능 삭제를 비롯해 손볼 곳은 아직 많습니다. (분명 한두 시간이면 된다고 했던 일이 2주째 수정 중입니다…) 화면 구성을 고치려면 프로젝트에 포함되어 있는 템플릿 파일을 고쳐야 합니다.템플릿은 Liquid 라는 언어로 구성되어 있으며, 문법이 좀 난해하지만 충분히 헤쳐 나갈 수 있습니다. 다만 어디서부터 어떻게 고쳐야 하는지를 파악하는 게 어렵죠. 문법은 공식 사이트를 참고하고, 사용 가능한 변수는 여기를 참고하면 됩니다.사용 가능한 변수는 site와 page로 나눌 수 있습니다. site는 _config.yml 설정한 내용과 jekyll이 지원하는 전역 변수들입니다. page는 해당 페이지에 지정된 세부 변수들입니다. 글의 제목이나 경로 내용들은 기본적으로 세팅되어 있습니다. 추가적인 값을 다루려면 post를 작성하면서 meta정보를 추가하면 됩니다.템플릿의 시작파일은 index.html이고, 페이지에 layout이 지정되었다면 _layouts 안에 있는 [layout].html이 됩니다. 기본적 틀은 _layout/default.html에서 파생됩니다. 그외 파츠로 사용되는 HTML파일은 _includes에 넣고 `{% include header.html %} 같은 방식으로 추가하면 됩니다. 우리 변경하려는 파츠는 header.html에 있습니다. site.pages에는 모든 페이지가 들어있기 때문에 그중에 gnav가 지정된 글만 상단에 노출되게 했습니다. 그리고 상단 글에 대한 정렬이 없기 때문에 좋은 방식은 아니지만 1~10까지 숫자를 기입하면 순서대로 나오게 코드를 구성했습니다. (site.pages에는 posts와 pages가 같이 나옵니다.){% for i in (1..10) %}   {% for page in site.pages %}     {% if page.title and page.gnav == i %}     {{ page.title }}     {% endif %}   {% endfor %}  {% endfor %} 글에 옵션을 지정한 화면이제 pages에서 상단에 노출하고 싶은 글만 gnav를 숫자로 부여해 노출할 수 있게 변경했습니다.9. 스타일 변경은 어떻게 할까?sass로 구성된 스타일의 변경은 심도있게 다루지 않으려고 합니다. sass를 처음 사용한 것도 있지만 내용이 너무 깊어지기 때문입니다. 스타일변경은 _sass 밑에있는 scss 파일을 변경하면 되고, 템플릿마다 구조가 다르기 때문에 열심히 찾는 수밖에 없습니다.10. 저자 기능을 추가해보자 (1)최고의 난이도를 자랑하는 신규 기능 추가입니다. 브랜디의 기술 블로그에서는 작성자를 클릭하면 작성자의 글만 따로 모아서 볼 수있습니다. 하지만 이 기능은 공식적으로 지원되는 것이 아니기 때문에 처음부터 만들어야 했습니다. 완성된 작성자 기능위의 이미지와 같은 기능을 구축하려고 collection을 사용했습니다. collection은 posts나 pages와 같이 그룹핑된 글 목록을 이야기 합니다. posts나 pages는 기본 세팅되어 있고, 약간(?)의 설정 변경으로 collection을 추가할 수 있습니다. 작성자의 메인 페이지가 필요하니 authors라는 collection을 추가해보겠습니다.# _config.yml collections:   authors:     title: Authors     output: true jekyll을 재시작하면 아래와 같이 Authors가 관리자에 추가된 것을 볼 수 있습니다.authors는 작성자 메인 페이지만 생성하면 되므로, 내용에는 작성자에 대한 소개글만 간략히 쓰면 됩니다. jekyll admin에 한글 버그가 있기 때문에 우선 영어로 작성하고, 제목을 다시 한글로 수정하면 됩니다.포스팅마다 저자의 정보가 공통적으로 나와야 하기 때문애 위의 전역변수에 authors를 추가해 따로 관리하게 했습니다.# data/authors.yml # authors 공용 변수   - name: chunbs     koname: 천보성 팀장     email: [email protected]     position: R&D 개발2팀     img: /assets/profile/chunbs.jpg   - name: kangww     koname: 강원우 과장     email: [email protected]     position: R&D 개발2팀     img: /assets/profile/kangww.jpg 그리고 작성자의 포스팅을 엮어주려고 작성자의 아이디가 같을 때, 포스팅으로 나오게 구성합니다.{% if post.author %} {% for author in site.data.authors %}   {% if post.author == author.name %}   {{author.koname}}   {% endif %}  {% endfor %}  {% endif %} 11. 저자 기능을 추가해보자 (2)데이터가 준비되었다면 저자 레이아웃을 추가해야 합니다.(이거 도대체 언제 끝날까요) 저자가 작성한 글만 노출되어야 하는 게 어려울 수도 있지만 jekyll의 구동 원리를 이해하면 손쉽게 할 수 있습니다.jekyll은 내용 수정이 발생되면 전체를 다시 컴파일하는 구조입니다. 다시 말해 일부 파일이 변경되면 노출되는 모든 html파일을 다시 랜더링해서 write하는 것입니다. author의 각 작성자 페이지는 컬렉션에 포함되어 있기 때문에 랜더링이 발생하고 site.posts엔 작성된 모든 페이지 정보가 있습니다. site.posts를 foreach를 돌리고, 저자가 일치하는 페이지만 리스트로 보여줍시다.{% for post in site.posts %} <!-- author 정보가 저자와 같은 경우만 리스트로 출력한다. --> {% if post.author == page.author %}       {{ post.title }}         {{ post.content | strip_html | truncatewords: 25 }}         {{ post.date | date: "%Y-%m-%d" }}           {% if post.author %}         {% for author in site.data.authors %}           {% if post.author == author.name %}           {{author.koname}}           {% endif %}         {% endfor %}       {% endif %}       {% if forloop.last == false %} {% endif %}   {% endif %} {% endfor %} Conclusionjekyll admin은 은근히 버그가 많습니다. 그래도 ‘md파일을 메모장으로 작성하세요’라고 하는 것보단 편하죠. 다양한 기술을 사용하기 때문에 어려울 수도 있겠습니다. 글에서 소개할 수 없거나, 너무 깊어지는 내용은 소개에서 제외했습니다. 양해를 부탁드립니다. 대신에 브랜디 랩스는 저의 피땀 눈물로 만들어졌다는 걸 기억해주세요… 기타jekyll의 기본값 설정을 이용하면 layout과 같은 공통적인 부분을 쉽게 설정할 수 있다.# _config.yml defaults:  - scope:     path: ''     type: posts   values:     #permalink: "/blog/:title/"     layout: post     cover: /assets/default.jpg     author:  - scope:     path: ''     type: authors   values:     layout: author     cover: /assets/author.jpg     subtitle: ~담당하고 있습니다.     author: 영문이름 jekyll admin이 버그가 많아서 업로드 기능은 커스터마이징 했다. 루비와 UI코드를 고쳐서 다시 빌드하는 어지러운 작업을 했다.만약 버그를 고치기 어렵다면 IDE로 파일을 직접 수정하는 게 안전하다. 참고 1)마크다운 작성법은 여기를 참고하세요.글천보성 팀장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1682

StyleShare Engineering Blog?!

변정훈님 강의 모습생각해보기한 번도 생각해본 적이 없다! 왜 글을 작성해야 하는가?! 왜냐하면, 우리는 글로 먹고사는 사람도 아니고, 수려한 글솜씨도 없기 때문에?! 하지만, 이미 우리 사회는 PR의 시대를 뛰어넘어 미디어의 홍수에서 살아가고 있고, 매우 쉽게 무의식적으로 많은 글을 읽고 있다.하지만, 우리가 글을 작성하기 위해서 얼마나 많은 준비가 되어 있을까? 이 글을 쓰고 있는 필자 역시 글을 써본 경험이 거의 없다. 특히 회사의 이름을 걸고 글을 쓴다는 것은 매우 부담스러운 일이다.그래서 우리는 변정훈[Outsider’s Dev Story]님을 초대하여, 그분이 생각하는 블로그 일상과 엔지니어링 블로그에 대한 생각 공유의 시간을 가져보았다.엔지니어링 블로그회사 블로그 운영을 해보았는가?아쉽게도 변정훈 님도 회사 블로그를 운영해본 적은 없다고 하신다. 그 원인을 다음과 같은 이유로 해석을 하였다.주제가 많지 않다.개발보다 우선순위가 떨어진다.누구나 처음부터 글을 잘 적을 수 있는 건 아니다.그렇다. 이 글을 쓰는 중에도 위와 공감할 수 있는 부분이 적지 않다.우선 글을 많이 써보지 못한 필자로서도 어떤 글을 적어야 할지 난감하게 느껴지고, 업무 중에서도 중요도가 떨어지는 것은 분명하다. 마지막으로 주제 선정부터 매우 어렵게 느껴진다.그런 이유로 글쓰기를 즐겨 하시는 변정훈 님 조차 회사 블로그 운영을 잘 이끌어 본 적이 없다고 하신다.변정훈 님의 블로그는 2007년 부터 총 1,300여개 글이 게시되어 있다고 한다.왜 우리는 블로그를 운영하려 하는가?여러 가지가 있겠지만, 필자가 가장 공감한 부분은 이 부분이었다.팀 내 지식/경험 공유잠재적 입사자에게 기술 스택 및 문화 공유팀 전체의 실력 향상그동안 개발일을 해오면서, 몇 년 동안 풀리지 않는 큰 숙제 중에 하나가 좋은 개발자를 찾는 것이었다. 항상 사람을 찾는 것이 어렵다. 좋은 사람의 기준이 높아서인지 더 좋은 기업이 많아서인지 알 수는 없지만, 일하면서 느낀 가장 어려운 문제 중에 하나이다. 결국, 내부의 인력을 더 좋은 사람으로 만들고, 외부의 좋은 사람과도 교류의 장을 만들 수 있다는 희망을 품을 수 있게 되는 것이다.글 작성의 문턱을 어떻게 낮춰야 할까?좋은 점은 쉽게 공감이 되지만, 언제나 가장 어려운 것은 실천이 아닐까 싶다. 특히 회사에서 업무로 이런 일이 발생된다면, 많은 사람들이 엄청난 부담감을 가질 것이고, 결국 회사 엔지니어링 블로그는 대문만 남은 유명무실한 블로그가 될 가능성이 높다.그래서 변정훈 님은 이렇게 제안하셨다.월 1개 보다 적어도 된다.주제를 계속해서 제안하고 만들어 내야 한다.돌아가면서 작성한다.챙겨주는 사람이 필요하다.글도 리뷰하는게 좋다.부담감은 의도적으로 줄여야 …특히 변정훈님은 부담감을 줄이는 방법에 대해서, 팀 공유를 해주셨는데 잠시 소개하면 다음과 같다.나보다 모르는 사람 — 나 — 나보다 잘하는 사람언제나 어떤 기술에 대하여, 나보다 잘 모르는 사람과 나보다 잘 하는 사람이 있다는 것을 인지하고, 나보다 못하는 사람을 위해서 글을 작성한다는 것이다. 그러다 나보다 잘하는 누군가가 어쩌다 피드백을 준다면 오히려 매우 감사하게 새로운 지식을 터득하게 된다는 것이다. 역시 모든일에는 긍정적인 마인드가 중요하다. 세상 어딘가에는 나의 작은 지식이라도 필요로하는 사람들이 분명히 있을테니, 작은 용기를 가지고 세상에 누군가를 위해서 작성한다면, 세상은 분명 아름다워질 것이다.좋은 글, 좋은 주제란 무엇일까?사실 가장 어려운 이야기일지도 모른다. 변정훈님은 이런 내용을 좋다고 표현하셨는데, 잠시 정리하면 다음과 같다.개발팀의 문화어떻게 일하는가?프로젝트 수행 회고실패기개인적으로 실패기가 가장 적기 어려운 글이라고 생각한다. 하지만, 누군가에게는 가장 소중한 경험이 될지도 모르니, 간접경험의 공유는 특히 소중한 것일 수 있다.만약 회사 블로그의 글이 회사 내/외부의 사람들에게 지식 공유와 전달이 목적이라면, 그리고 좋은 문화를 계속 가지고 유지하기 위해서라면, 실패기가 어쩌면 가장 소중한 경험의 공유가 아닐까 생각해본다.질문과 답변을 통한 소통의 시간마지막으로 이 날 소개 받은 좋은 글의 흐름이란 다음과 같았다.하고자 했던 일 (Context)경험한 문제 사황 정리(격리된 상황)시도해 본 방법(내가 아는 지식)왜 동작이 안되는가? 왜 동작하는가?(가설)문제 상황 재현예제 코드관련 링크개념 설명지금까지 적은 이 글을 위의 원칙대로 다시 한번 살펴본다. 부족한 부분이 있는지, 수정할 부분이 있는지…이제 부터 회사 블로그를 더욱더 적극적으로 운영해보자!!!#스타일쉐어 #개발팀 #조직문화 #블로그 #기업문화 #사내복지
조회수 3130

eventlet을 활용한 비동기 I/O 프로그래밍

안녕하세요. 스포카 크리에이터팀 문성원입니다. 현대적인 프로그래밍 환경에서 네트워크는 더는 특정 직군의 개발자만 접하는 분야가 아닙니다. 그런 만큼 대량의 요청을 네트워크를 통해 송수신하는 프로그램이 생각보다 성능이 나오지 않는 경우를 경험하신 분들도 많으실 겁니다. 물론 스포카 개발팀도 예외는 아니었습니다. 그래서 오늘은 저희의 이러한 경험과 그 해결책-eventlet을 통한 비동기 I/O(Asynchronous I/O)-에 대해 소개합니다.Why우선 스포카 개발팀에서 겪었던 문제부터 시작하죠. 얼마 전 페이스북(facebook)의 FQL(Facebook Query Language)를 통해 정보를 수집해서 이를 활용하는 기능을 작성해야 했습니다. 기존의 함수들은 필요할 때마다 FQL을 요청하는 방식이었고 당연히 이건 너무 느렸죠. 그래서 생각한 것이 “하루의 일정 시간마다 대량의 FQL 요청을 보내서 필요한 정보를 미리 갱신시켜놓자.”였습니다. 여기까진 좋았죠. 이때 제가 작성한 코드의 얼개를 살펴보면 대강 이렇습니다.# 페이스북 계정들을 가져와서 반복하면서for account in FacebookAccount.query:    account.update() #FQL을 보내자.view rawgistfile1.py hosted with ❤ by GitHub그런데 문제가 있었습니다. 기존의 FQL을 보내는 FacebookAccount.update()는 FQL요청이 완료될때까지 멈추고 기다립니다. 대부분의 FQL요청이 2, 3초 정도 걸린다고 했을 때 이러한 지연은 매우 치명적입니다. 대안이 필요했고 자연스레 떠오른 것이 서두에 소개한 비동기 I/O(Asynchronous I/O)였습니다.Asynchronous과거 일부 고급 서버 개발자만 알고 있는(혹은 알아야 하는) 기술로 치부되던 ‘비동기(Asynchronous)’란 개념은 2000년대 들어 등장한 Ajax(Asynchronous JavaScript and XML)의 성공 이후 많은 개발자에게 강한 인상을 줬습니다. 사용자는 HTTP 요청이 끝날 때까지 멈추어 있는 하얀 화면으로부터 해방되었고, 다양하고 많은 요청과 응답들이 자연스럽게 서버로 흘러들어 가서 나왔습니다. 개발자들의 이러한 경험과 통찰은 이후 node.js와 같은 플랫폼의 등장에도 많은 영향을 끼쳤습니다.다시 문제로 돌아가죠. 그렇다면 이러한 비동기에 관한 개념은 위의 상황을 어떻게 해결할 수 있을까요? 문제의 원인부터 다시 살펴봅시다. 2, 3초 정도씩 걸리는 FQL 요청이 문제일까요? 물론 요청이 매우 빨리 처리된다면 별도의 처리 없이도 저 코드는 문제없이 동작합니다. 하지만 현실적으로 이런 I/O의 속도를 빠르게 하는데에는 물리적으로 한계가 있습니다. 오히려 여기에서 주목해야 할 점은 ‘2, 3초’ 보다 ‘기다린다’라는 점입니다. FacebookAccount.update() 같은 경우, I/O가 처리되는 동안 CPU는 하던 일을 멈추고 문자 그대로 기다리게 됩니다. 만약 CPU가 멈추지 않고 다른 요청을 보낸다면 어떨까요? 이렇게 말이죠.비동기만으로는 부족하다?이러한 아이디어는 그동안 많은 개발자가 대량의 I/O를 다루는 올바른 방식으로 여겨왔습니다. 하지만 보통 이러한 비동기 I/O를 통한 구현은 동기식 I/O와는 좀 다른 형태를 띠게 됩니다. 이렇게 말이죠.# http://docs.python.org/library/asyncore.html#asyncore-example-basic-http-clientimport asyncore, socketclass HTTPClient(asyncore.dispatcher):    def __init__(self, host, path):        asyncore.dispatcher.__init__(self)        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)        self.connect( (host, 80) )        self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path    def handle_connect(self):        pass    def handle_close(self):        self.close()    def handle_read(self):        print self.recv(8192)    def writable(self):        return (len(self.buffer) > 0)    def handle_write(self):        sent = self.send(self.buffer)        self.buffer = self.buffer[sent:]client = HTTPClient('www.python.org', '/')asyncore.loop()view rawgistfile1.py hosted with ❤ by GitHub불행하게도, 이 경우 기존에 사용하던 urllib2대신 HTTP 요청을 처리하는 핸들러를 이처럼 재작성 해야합니다. 거기에 FacebookAccount.update()의 호출 방식마저 바뀔 수 있죠. 더군다나 콜백(Callback) 투성이의 코드는 유지보수가 쉬어 보이지도 않습니다. 여러모로 손이 많이 가는 상황이죠.결국, 기존 코드를 최대한 수정하지 않으면서도, 어느 정도 성능은 보장되는 그런 해결책이 필요했습니다. 그런 해결책이 있을까요? 다행히도 그렇습니다.What저희가 해결책으로 택한 eventlet은 Python(정확히는 CPython)에서 코루틴(Coroutine)을 지원하기 위해 만들어진 greenlet을 이용해 작성된 네트워크 관련 라이브러리입니다. 생소한 용어가 갑자기 튀어나와서 놀라셨을지도 모르니 우선 eventlet에 대해 설명하기 전에 앞에 나온 용어들을 찬찬히 한번 살펴보죠.코루틴과 greenlet먼저 코루틴(Coroutine)부터 살펴보죠. 전산학도라면 누구나 그 이름을 한번은 들어봤을 도널드 카누쓰(Donald Knuth)는 자신의 저서 The Art of Computer Programming에서 코루틴을 다음과 같이 설명합니다.Subroutines are special cases of more general program components, called “coroutines.” In contrast to the unsymmetric relationship between a main routine and a subroutine, there is complete symmetry between coroutines, which call on each other.코루틴은 우리가 잘 알고 있는 서브루틴(Subroutine)과 달리 진입점(Entry Point)이 여러 개일 수 있습니다. 쉽게 이야기하면 실행을 멈췄다가(Suspend) 재개(Resume)할 수 있다는 점인데요. 이 특성을 살리면 우리가 익히 아는 스레드(Thread)처럼 쓸 수 있게 됩니다. 다만 스레드와 달리 코루틴은 비선점적(Non-Preemptive)이기때문에 코드의 흐름을 전적으로 사용자가 제어할 수 있습니다.하지만 불행히도 모든 언어에서 이런 코루틴이 지원되진 않습니다. greenlet은 이런 코루틴을 CPython에서 지원하기 위해 작성된 라이브러리입니다.eventlet코루틴을 통해 스레드를 대체할 수 있다는 점에 주목한 사람들은 greenlet을 통해 유용한 네트워크 라이브러리를 만들어냈습니다. eventlet도 그 중 하나죠. 잠시 eventlet의 소갯글을 봅시다.Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it.위에서 볼 수 있듯이 eventlet은 사용성에 중점을 두었습니다. 기존의 블로킹 I/O 스타일의 프로그래밍에 익숙한 개발자들도 쉽게 비동기 I/O의 장점을 얻을 수 있게끔 하는 게 목적이죠.특히 저희가 주목한 점은 eventlet의 멍키패치 기능입니다. 멍키패치는 본래 동적 언어에서 런타임에 코드를 고쳐서 별도의 파일 변경 없이 본래 소스의 기능을 변경하는 것을 말합니다. eventlet은 eventlet.monkey_patch 메서드를 통해 표준 라이브러리의 I/O 라이브러리를 논블러킹으로 동작하게끔 변경해서 코루틴에 적합하게 만듭니다.How앞서 소개한 eventlet.monkey_patch를 이용하면 실제로 고칠 부분은 정말로 적어집니다. 다음 코드가 eventlet을 이용해 변경한 전부입니다.import eventleteventlet.monkey_patch() #표준 라이브러리를 변환# 여러가지 import를 하고...pool = eventlet.GreenPool()# 페이스북 계정들을 가져와서 반복하면서for account in FacebookAccount.query:    # 코루틴들에게 떠넘기자.    pool.spawn_n(FacebookAccount.update, account)        pool.waitall()view rawgistfile1.py hosted with ❤ by GitHub정말 적죠? 조금만 구체적으로 살펴보죠. 우선 eventlet.monkey_patch는 socket이나 select등의 Python 표준 라이브러리를 eventlet.green 패키지안에 정의된 코루틴 친화적인 모듈들로 바꿔치기 합니다.# from eventlet/pathcer.pydef monkey_patch(**on):    """Globally patches certain system modules to be greenthread-friendly.    The keyword arguments afford some control over which modules are patched.    If no keyword arguments are supplied, all possible modules are patched.    If keywords are set to True, only the specified modules are patched.  E.g.,    ``monkey_patch(socket=True, select=True)`` patches only the select and     socket modules.  Most arguments patch the single module of the same name     (os, time, select).  The exceptions are socket, which also patches the ssl     module if present; and thread, which patches thread, threading, and Queue.    It's safe to call monkey_patch multiple times.    """        accepted_args = set(('os', 'select', 'socket',                          'thread', 'time', 'psycopg', 'MySQLdb'))    default_on = on.pop("all",None)    for k in on.iterkeys():        if k not in accepted_args:            raise TypeError("monkey_patch() got an unexpected "\                                "keyword argument %r" % k)    if default_on is None:        default_on = not (True in on.values())    for modname in accepted_args:        if modname == 'MySQLdb':            # MySQLdb is only on when explicitly patched for the moment            on.setdefault(modname, False)        on.setdefault(modname, default_on)            modules_to_patch = []    patched_thread = False    if on['os'] and not already_patched.get('os'):        modules_to_patch += _green_os_modules()        already_patched['os'] = True    if on['select'] and not already_patched.get('select'):        modules_to_patch += _green_select_modules()        already_patched['select'] = True    if on['socket'] and not already_patched.get('socket'):        modules_to_patch += _green_socket_modules()        already_patched['socket'] = True    if on['thread'] and not already_patched.get('thread'):        patched_thread = True        modules_to_patch += _green_thread_modules()        already_patched['thread'] = True    if on['time'] and not already_patched.get('time'):        modules_to_patch += _green_time_modules()        already_patched['time'] = True    if on.get('MySQLdb') and not already_patched.get('MySQLdb'):        modules_to_patch += _green_MySQLdb()        already_patched['MySQLdb'] = True    if on['psycopg'] and not already_patched.get('psycopg'):        try:            from eventlet.support import psycopg2_patcher            psycopg2_patcher.make_psycopg_green()            already_patched['psycopg'] = True        except ImportError:            # note that if we get an importerror from trying to            # monkeypatch psycopg, we will continually retry it            # whenever monkey_patch is called; this should not be a            # performance problem but it allows is_monkey_patched to            # tell us whether or not we succeeded            pass    imp.acquire_lock()    try:        for name, mod in modules_to_patch:            orig_mod = sys.modules.get(name)            if orig_mod is None:                orig_mod = __import__(name)            for attr_name in mod.__patched__:                patched_attr = getattr(mod, attr_name, None)                if patched_attr is not None:                    setattr(orig_mod, attr_name, patched_attr)        # hacks ahead; this is necessary to prevent a KeyError on program exit        if patched_thread:            _patch_main_thread(sys.modules['threading'])    finally:        imp.release_lock()view rawgistfile1.py hosted with ❤ by GitHub이렇게 바꿔치기된 eventlet.green안의 모듈들은 I/O에 의해 블럭되는 경우 다른 코루틴에 제어권을 넘기는 식으로 지연을 방지합니다.다른 대안들사실 이러한 목적으로 사용되는 라이브러리는 eventlet만 있는 것은 아닙니다. gevent는 eventlet에서 영향을 받았지만, libevent를 기반으로 하여 더욱 나은 성능과 성숙한 인터페이스를 갖추고 있습니다. 저희처럼 libevent의 설치에 제한이 있는 환경이 아니라면 이쪽을 살펴보셔도 좋습니다.만약 이벤트 주도적 프로그래밍(Event-Driven Programming)에 흥미가 있으신 분은 Twisted역시 좋은 대안이 될 수 있습니다.#스포카 #개발 #개발자 #인사이트 #꿀팁
조회수 633

스마트링크 시즌2 : 은하철도 프로젝트

스마트링크 시즌2 채용공고에 보내주신 뜨거운 반응 감사합니다!! 정말 많은 분들의 열정과 관심에 분주하지만 즐거운 만남들을 여럿 가질 수 있었습니다. 그리고 드디어!! 은하철도에 함께 탑승할 5명의 동료가 최종 선발되셨습니다. 뜨거운 관심과 지원에 다시 한번 감사드리며 아쉽지만 이번에 함께하지 못한 분들도 저희가 좌석을 보다 넉넉하게 꾸리게되면 함께할 수 있는 날이 오면 좋겠습니다.여기서 잠깐!그렇다고해서 스마트링크 시즌2 채용이 완전히 완료된 것은 아닙니다. 스마트링크는 언제나 좋은 분들과 함께할 준비가 되어있습니다. 상시채용 형태로 계속 이어나갈 예정이니 스마트링크 은하철도에 관심있는 분들은 언제나 문을 두드려 주시면 감사하겠습니다. 그럼 새로운 동료들과 슬슬 날아갈 준비를 하러 이만 :) - 2019. 6. 25 어느 기분좋은 화요일---------------------------------------------------------------------안녕하세요. 스마트링크의 Mike 라고 합니다. 기획과 마케팅을 담당하고있죠. 스마트링크는 작년부터 저희와 함께할 분들을 애타게 찾고 있습니다. 그 사이에 많은 분들을 뵙고 기회를 도모하기도 했습니다. 여러 다양한 경험을 축적하기도 했구요. 이렇게 여러 과정을 거치던 와중에 그동안 아기다리고기다리던, 그리고 열심히 준비했던 성과들이 하나둘 나오기 시작했습니다. 마치 미드에서 시즌이 바뀌는 것처럼 우리에게 근본적인 패러다임의 변화가 있었다랄까요? 이런 변화를 염두하며 지난 채용공고를 봤는데...안되겠어. 다시 써야겠어!그래서 이렇게 시즌2 만을 위한 채용공고를 작성하는 중입니다. 스마트링크의 시즌2는 어떻게 진행되고 그래서 어떤 분들과 함께하고 싶은지 지금부터 이런저런 이야기를 해보도록 하겠습니다.  뭐하는 회사임?스마트링크는 소프트웨어 개발사 입니다. 끝. 참 쉽죠? 그런데 세상은 넓고 소프트웨어 개발사는 넘치고 넘칩니다. 그런데 뭐가 그렇게 다른가? 라고 물으신면! MVP(Minimum Viable Product) 소프트웨어 개발 컨설팅 전문 업체라고 말씀드릴 수 있겠습니다. 이게 뭔말이냐 하면 덩치 큰 SI도 진행하지만 주로 스타트업 또는 초기 사업 아이디어가 빠르게 시장에 진입할 수 있도록 기획, 디자인, 개발, 테스팅, 데브옵스까지 (물론 견적에 따라 달라집니다! 단호! ㅋㅋ) 풀 패키지로 작업하는걸 좋아하는 업체라고 보시면 되겠네요. 그래서 프로젝트 기간이 짧고 굵은게 많죠. 늘어지는 프로젝트 별로 안좋아 합니다. AtoZ로 빠르게, 효율적으로, 효과적으로! 일하는걸 선호하고 실제로 그렇게 일을 진행합니다. 그런데 아마 이런 의문이 드실거에요. 왜 작은일 맡는걸 좋아하지? 사실, 규모가 중요한게 아니라 AtoZ 라는게 중요합니다. (심지어 예산 높은 큰 프로젝트 요청을 까기도 합니다. 꽤 자주;;) 그 이유는? 면접때 질문 주시면 신나게 답해드리도록 하죠 ㅎㅎ 다 이유가 있습니다!  누가 일하고 있는데?AtoZ, 풀패키지로 일하는걸 좋아한다는 대목에서 아시겠지만 있을 사람은 다 있습니다. 기획, 디자인, 개발 인력 모두 있구요. 그래야 일이 되겠죠? 다만 현재 사람수가 많지는 않아요. 소수정예! 하지만 모두 각 분야에서 베테랑들이라 자부합니다. 특히 개발사이니만큼 모든 분야는 개발을 중심으로 돌아가구요, 각 영역을 생판 모르는 분야로 치부하지않고 서로를 끊임없이 알아가고 파악하고 융화되는 방식으로 일합니다. 예를 들면 기획과 개발은 DB구조나 Convention을 공유하고, 디자인은 Front-end 최적화된 디자인과 UI/UX를 뽑아냅니다. 여기서 일일이 언급하기는 뭐하지만 일 잘하는 사람들이 모여있다고 자부하고 있고, 앞으로 동료들도 일 잘하는 사람을 가장 원하고 바라고 있습니다. 일을 잘한다는 기준이 절대적일 수는 없겠지만, 예를 들면 이런거죠. 최대한 정확하고, 낭비나 누수없이, 빠르게 문제를 해결하기 위해 계속 꼼수를 쓰는 사람들! 이랄까요? 세상에 (노는것 포함) 할일이 얼마나 많은데! 극단적 효율을 추구하는 집단이라고 보시면 되겠습니다.  제대로된 꼼수는 사실 탄탄한 정석 바탕에서 나올 수 있다죠.다만 아직 목마릅니다. 일을 더 잘하고 싶어요. 그래서 우리는 시즌1을 보내면서 내부를 다지는 일도 지속적으로 탄탄하게 단내 나도록 해왔습니다. 그리고 슬슬 그 결과들이 눈 앞에 펼쳐지고 있네요. 그래서 결심할 수 있었습니다. 이제 확장의 시기가 왔다! 시즌2로 나아갈 때가 되었다!   시즌2라...시즌1엔 어떻게 했고, 시즌2에서는 어떻게 할건데??시즌1에서 스마트링크 작업방식을 정의내리자면 이렇습니다.천상천하유아독존!!네, 그렇습니다. 각자 부여된 일을 독자적으로 수행해서 최종 결과물을 내는 방식이었죠. 내부적으로 진행하는 일이야 Agile 방법론을 적극 도입한다해도 외부 프로젝트를 진행하는 경우에는 어쩔 수 없는 Waterfall 방식이었습니다. 기획 작업을 마무리하면, 받아서 디자인 작업을 하고, 마지막으로 개발을 완료하는 방식이었죠. 특히 개발은 Ownership을 기반으로한 책임개발제(라 쓰고 독박이라 읽는다)로 운영되고 있었습니다. 이 방식으로 운영했던 이유는 모호한 업무분담과 그로 인한 누수를 최소화하기 위한 방책이었죠. 사공이 많으면 배가 산으로 간다는 속설을 극복할 방법이기도 했구요. 실력있는 개발자를 중심으로 이 방법은 한동안 잘 유지되는듯 했습니다. 그런데 계속 이렇게 운영하다보니 이런 상황이 발생했습니다.될놈될, 안될안 ㅠ 개발 결과물의 빈부격차 ㅠ책임개발제는 결과물이 사람에 의해 결정된다는 의미 입니다. 실무자의 경험이나 실력에 따라 천차만별일 수 밖에 없는거죠. 그러다보니 퀄리티 확보를 위해서는 결국 다시 여러 사람들의 손을 거쳐야하는 이슈들이 종종 발생했습니다. 사실 이는 필연적인 부분일지도 모르겠습니다. Full-Stack 개발을 추구한다해도 결국 저마다 가지고있는 개성과 강점은 다르니까요. 그럼에도 불구하고 지금까지는 딱히 문제 없었습니다. 다만 미래를 염두하면 걱정되는 부분들이 있더군요. 인력이 늘어나고 보다 다양한 사람들이 함께하게된다면 과연 이 시스템이 버틸 수 있을까? 라는 근본적 의문이 드는겁니다. (그래서 이번 채용은 Front-end와 Back-end를 구분해서 진행합니다.) 그리고...Ownership이고 뭐고 다 좋은데 왜 외롭냐...외롭기도 하더군요. 기획, 디자인, 개발 모두가 그랬고 특히 개발자들은 그냥 말 그대로 굉장히 외롭게 되었습니다. 복작이며 한 팀으로 일하는 방식이라기보다는 프리랜서들 조합과 같은 이 상황은 구성원들을 각자 개인의 울타리로 고립시키는 결과로 이어졌습니다. 기획, 디자인, 개발은 각자 나름의 방식으로 일하면 결국 서로 Sync를 맞추기 위한 작업이 추가될 수 밖에 없습니다. 효과적인 분업도 좋지만 결국 우리는 함께 일하는 회사라는 공동체 안에 있습니다. 능률, 효율과 더불어 협업도 굉장히 중요하죠. 적당한 균형점을 찾는게 중요해졌습니다. 앞으로 사공은 엄청 많아질거거든요. 그것도 다양한 특징과 강점을 가진 각양각색의 사공들이 말이죠. 이렇게 사공이 많아져도 배가 산으로 가면 안되죠.  우주로 가는건 괜찮을지도... 사공이 많은 배라면 차라리 이런걸 만들면 어떨까?사공이 많은 멋진 배를 만드는 방법이란 뭘까? 누수 없는 업무처리와 능률을 모두 잡는 방법은 무엇일까? 이런 고민을 하던 와중에 우리에게 필요한건 엔진이란걸 알게 되었습니다. 이 엔진은 이런 조합으로 구성되어야 했습니다.목표한 기능을 정확하고 안정적으로 구현할 수 있는 동력자칫 시야를 좁힐 수 있는 미시적 요소들을 과감하게 skip할 수 있는 돌파력누수없이 매끄럽게 진행되는 안정적 업무 전달계통그리고 이 과정을 우리 모두 함께하고 있다는 응집력 뭔가 뜬구름 잡는 이야기들로 보일지도 모르겠습니다. 하지만 이 조합은 연역적이라기보다는 귀납적입니다. 실제 우리가 고민해온 부분을 해결하고자한 일들의 결과물이 위와 같은 역할을 하고있다는 것이 보다 정확한 표현이겠네요. 그리고 이 엔진은 한 단어로 귀결됩니다.그렇습니다. 컴포넌트.그리고 우리는 Components 를 엔진 삼아 우주전함 대신 은하철도 시스템을 구축했습니다. 이른바 스마트링크 시즌2 은하철도 프로젝트!  은하철도 프로젝트라니... 뭥미?? - 스마트링크 시즌2 은하철도 프로젝트보통 스타트업이 성장하는 모습을 로켓에 비유하기도 합니다. 빠르고 가파르게 수직상승하는 모습을 본딴 것이겠죠. 하지만 우리는 조금 다르게 생각합니다. 한가지 아이템으로 절체절명의 상황을 이겨내고 급성장하는 방법도 좋겠지만 우리는 오히려 안정성과 지속가능성에 더 초점을 맞추고 있습니다. 이를 위해서 스마트링크는 꽤 오랜시간 공들여 Component 구축을 진행했고 그 결실이 드디어 빛을 봤습니다! 장기적으로 효율적이고도 생산적인 구조를 위해 이제까지의 내부 프로세스를 과감하게 변경하고 새롭게 아래와 같은 구조로 진행합니다. 반영구적 Components 엔진을 돌리면서 모두를 리딩하는 곳, 기관실우리의 엔진 Components를 계속 다듬고 발전시킵니다. 내부 프로젝트도 진행하죠.실무자들의 즐거운 놀이터, 1등석이미 잘 구축된 Components로 안락하고 쾌적하고 빠르게 할당된 프로젝트를 진행합니다. 특히 개발자에게는 상용 서비스에서 활용 가능한 React Skill을 마음껏 연마하는 과정이기도 합니다 :)초심자들의 탄탄한 학습의 장, 일반석숙련도와 경험이 적은 초보자들은 체계적인 교육과 안정적인 Components 활용법을 익히고 1등석에 옮겨탈 준비를 합니다.뭔가 괜찮은 열차죠? 은하철도 프로젝트는 크게 이런 구조로 작동하게 됩니다. 이번 채용공고를 통해 모시고자하는 자리는 1등석과 일반석 입니다.베테랑들은 탑승한 동료들을 위해 열심히 기관실을 돌리면서 최대한 안정적이고 쾌적한 작업환경을 위해 움직입니다. 물론 내부적인 방향과 비전을 위한 고민, 세팅도 주도하겠지만 최종적으로는 모든 구성원들과 함께 공유하고 의견을 모아 진행합니다. 기관실과 객석들 역시 유기적이고 탄탄하게 연결돼야 하니까요.가즈아~ 기관실은 구비되어있다!!기관실과 객석이 설국열차처럼 꽉 막혀있지 않습니다. 본인이 원한다면 일정정도 열정과 의지로 기관실에 옮겨탈 수도 있습니다. 이건 순전히 본인의 취향에 달려있다고 생각해요. 세상은 넓고 사람은 다양하고 가치관도 제각각입니다. 그저 선택의 문제일 뿐이죠. 우리는 그저 보다 많은 사람들이 우리의 은하철도에 올라탈 수 있기를 바랄 뿐입니다. 그래서 선택할 수 있는 자리를 마련한 것 뿐이구요. 실무자들이 실무에만 집중할 수 있는 구조는 회사라는 공동체에서 매우 중요하다고 생각합니다. 선택은 여러분의 몫입니다.  1등석과 일반석이라... 좀 더 설명해보지?고민의 공간, 기관실.1등석과 일반석을 설명하자면 먼저 기관실 설명을 하지 않을 수 없습니다. 기관실은 끊임없이 소프트웨어 Core를 생산하는 곳이라고 보시면 되겠습니다. 그 중심은 당연히 Components 겠죠. 세상의 모든 서비스를 커버하겠다는 야심과 함께 사용자에게는 쾌적한 경험을, 개발자들에게는 효율적이고 신속한 개발환경을 선사하는 영역입니다. 그래서 개발언어를 잘 이해하고 보다 핵심적인 영역을 손대고 싶은 사람에게 적합합니다. 실력도 당연히 동반되어야겠지만 이제까지 경험으로 보자면 자기주도적인 취향도 핵심이더군요. 기관실은 이런 사람들이 모여있습니다. 사용자경험 뿐 아니라 내부 개발진들의 의견을 끊임없이 추적하고 해결하는 고민의 공간 입니다.기관실이 잘 할테니까 팔로팔로미~ ㅎㅎ효율의 공간, 1등석위에서 '취향'에 대해 언급했는데요. 1등석은 취향에 따라 자신의 업무방향을 선택할 수 있는 공간 입니다. 잘 짜여진 Components와 Convention에 따라 실제 상용서비스를 만들거나 관리하는 역할을 합니다. 고민의 폭은 줄이고, 실질적인 결과물에 초점을 맞추는 효율의 공간이라고 보시면 되겠어요. 새로운 결과물을 세상에 선보이고, 이들을 잘 작동시키는 사람들이 모여있는 곳입니다. 그러다가 지금 쓰고있는 Components 개선이 좀 더 필요할거 같다 싶으면 자체적으로 해결해도되고 기관실로 넘길 수도 있습니다. 이 부분이 바로 취향의 영역이라고 볼 수 있는데요. 본인의 실력과 더불어 이 취향에 따라서 기관실로 갈지, 1등석에서 작업할지 결정할 수도 있습니다.학습의 공간, 일반석일반석은 다른 말로 초심자의 영역이라고 보시면 되겠습니다. 세상은 급변하고 소프트웨어 변화 역시 엄청나죠. 우리는 끊임없이 학습하고 발전해야만하는 영역에서 일하고 있습니다. 그래서 이 부분을 절대 간과해선 안된다고 생각하고 있어요. 다만 취미 정도의 학습이라면 각자 개인의 소양 정도로 진행하는 것이 적절하겠죠. 일반석은 실제 상용 서비스에 적용 가능한 수준의 학습이 이뤄지는 공간입니다. 그 핵심은 React, Meteor, MongoDB 라고 보시면 되겠구요. 고퀄 서비스들을 실제로 만들어낼 수 있는 핵심 역량을 키울 수 있는 곳입니다. 사람들은 각자 일하는 방법이나 인생설계 방향을 가지고 있습니다. 그리고 여기에 따라 너무 다양한 나름의 스타일을 가지고있죠. 우리는 이 부분을 간과해서는 안된다고 생각해요. 우리가 말하는 취향은 바로 이런 것입니다. 취향에 따라 내가 주도적인지 수동적인지, 스스로 설계하는 스타일인지 주어진 과제를 잘 해결하는 스타일인지 나뉘는게 당연하겠죠. 이 부분은 실력과는 또 다른 축인거 같습니다. 한가지 방식을 강요해봤자 상황이 제대로 돌아갈리는 만무하고 또 그래서도 안됩니다. 일을 잘 하고싶은 스마트링크는 그래서 우리가 운영 가능한 범위 내에서 최대한의 공간과 가능성을 만들고 싶었습니다. 그래서 이런 구조를 생각해낸거구요.좀 더 솔직히 말하자면, 네. 이거 준비하는데 힘들었습니다 ㅠ 그냥 실력있는 사람들이 머리를 맞대고 모이기만 한다면야 이런 고민과 구상이 필요 없을지도 몰라요. 오히려 그게 편하기도 하구요. 척 하면 척~ 착 하면 착~ 아시죠? 그리고 이 은하철도 프로젝트를 채용공고에서 공개하는 것이 과연 좋을까? 라는 고민이 있었던것도 사실입니다. 우리 자뻑모드로로 보자면 중요한 영업비밀일지도 모른다고 생각했거든요. 하지만 채용공고가 다소 길지라도 가능한 범위 내에서는 충분히 미리 공유하는게 좋겠다고 생각했습니다. 사실 이런 생각까지는 쉬운데 실제로 이렇게 구조를 잡는건 생각보다 매우매우 오래걸리고 어렵거든요. 그리고 그 어려운걸 우리는 해냈습니다. Components를 잘 구축해놨다 이겁니다 ㅎㅎㅎ다시 한번 말하자면 스마트링크는 로켓이 아니라 은하철도 입니다!! 날아오른다!!! 이거시 바로 은하철도!!!  알겠고, 그렇다면 구체적인 채용정보를 내놓아라!그래서 누굴 뽑는것인가? 라고 물으신다면 개발자 0명 찾습니다! 0명은 무엇이냐? 좋은 사람이 있으면 있는만큼 욕심을 낼것이다! 이런 욕구와 목마름이 있다는 것이죠! 많이 지원해 주세요! 공통적으로 체크해보실 수 있는 정보를 우선 드릴까요? 현재 사용중인 기술 스택 및 도구공통: Google Drive, Trello, Slack기획: FramerX, Adobe XD디자인: FramerX, Adobe XD 포함 Adobe 모든 제품군, ZeplinFront-end: Semantic UI, React, React NativeBack-end: MeteorTesting: Mocha, JestDevOps: Jenkins, Docker, Phusion Passenger, Nginx, AWSDatabase: MongoDB 근무환경최상의 사무 환경 및 공간 제공 (넓고 쾌적한 책상! 빵빵하고 쾌적한 냉난방시설! 막 엎어져서 작업하는 소파! 등) 식대 지원 (중식/석식) 4대 보험 주5일 근무 Refresh 휴가 출근시간 선택제 (8-5 / 9-6 / 10-7 / 11-8)경조사비 지원 근무지: 서울시 서초구 양재동 4-14 3층워크샵이라 하면 적어도 뷔페와 함께하는 야간 요트 유람 정도는 해줘야하는거 아닙니까? (사실 명목은 지스타…)  알겠고, 개발자 채용요건을 내놓아라! 네, 드...드리겠습니다. 아래를 봐주세요. 참고로 위에서 충분히 설명했듯 우선 1등석과 일반석에 모셔요~ ㅎㅎ Global Spec과 실무경험을 국내에서 탑재할 수 있는 기회를 놓치지 마세요! 이제 개알못 기획자는 아웃! React 코드를 보고 이렇게 반응하는 사람이라면 우리는 이렇게 됩니다 ㅎㅎ기술 스택스마트링크는 2001년 부터 C > C++ > Java > Object Pascal > PHP > JSP > Rails > Python 등의 개발 언어 기반으로 많은 프로젝트를 수행하여 왔습니다. 현재는 Javascript, Nodejs, React, React Native, Meteor, MongoDB의 매력에 흠뻑 빠져 있지만, 프로젝트 진행의 효율을 더(even more productive) 개선할 수 있는 새로운 기술이나 방법론에 대한 목마름으로 언제든 Early Adapter가 될 준비가 되어 있습니다.   모집분야 : 각 영역의 Front-end 혹은 Back-end 개발자를 모십니다.Javascript/Nodejs/Meteor 기반의 웹/모바일 애플리케이션 개발자 React + Meteor + MongoDB 기술 기반의 Web Application 개발 React Native + Meteor + MongoDB 기술 기반의 Mobile Native Application 개발  자격요건 : 개발에 미친 사람!!! 자유로운 소통과 공유의 가치를 잘 이해하고, 자기주도적인 환경에서 최대의 능력을 발휘하며, 긍정에너지 발산이 가능한 분 논리적이고 체계적인 문제해결 능력 및 오픈 마인드 커뮤니케이션 능력 전산 관련학과 학사 이상 또는 동일한 자격 (경력 무관)  우대조건 React, React Native 등의 JavaScript SPA(Single Page Application) 프레임워크 경험 Nodejs + MongoDB 기반 Micro Service Architecture 서비스 개발 경험 영어 커뮤니케이션 능력 (특히, 영문서 이해 능력: 해외 최신 기술을 주로 이용하다보니 한글 자료가 없는 경우가 많습니다.) AWS 등 클라우드 서비스 운영 경험 Git 포트폴리오: 직접 작성한 패키지, 오픈소스 기여 경험Docker 컨테이너 기반 서비스 구축 및 운영 경험 CI 시스템 구축 및 운영 경험 Mocha, Jest 등의 테스팅 프레임워크 또는 TDD(Test Driven Development) 경험  어떻게 지원하면 되는거임? 아래 루트로 지원해주시면 서류검토 후 면접일정을 직접 안내해 드립니다. 이메일과 핸드폰 연락처가 모두 기재되어있으면 참 좋겠죠? 면접이 진행되면 스마트링크에 궁금한 것, 알아보고 싶은 모든 것을 물어보실 수 있습니다! 함께 대화하는 자리라고 생각하시는게 가장 좋을거 같네요. 1. 이메일로 지원하세요! [email protected]해당 정보들도 함께 보내시면 금상첨화!이력서 (희망연봉포함)포트폴리오개발 경력 자료 (github 주소 환영합니다!) 2. 로켓펀치에서도 지원하실 수 있습니다!일반석 채용공고 https://www.rocketpunch.com/jobs/574961등석 채용공고 https://www.rocketpunch.com/jobs/57499 3. 잡코리아도 됩니다!스마트링크 은하철도에 탑승할 개발자 정규직 채용(신입&경력)http://www.jobkorea.co.kr/Recruit/GI_Read/28711079?Oem_Code=C1 4. 사람인도 됩니다!스마트링크 은하철도에 탑승할 개발자 정규직 채용(신입&경력)http://www.saramin.co.kr/zf_user/jobs/relay/view?rec_idx=36338553&view_type=etc   지금 망설이고 있다면???국내에서는 중소기업, 특히 신생기업이나 스타트업에 대한 인식이 그렇게 좋지않죠. 이런 현실적인 부분도 감안해서 저희는 직접적인 코딩테스트나 압박면접 같은건 진행하지 않습니다. 차분하고 진실된 마음의 대화가 가장 중요하다고 생각해요. 본인의 평소 생각을 그저 편안하게 나눈다 생각하고 부담없이 관심만 가지고 다가와주세요 :)이 짤처럼 무서운거 아니에요 ㅋㅋㅋ 편하게 드루와 드루와~지금까지 소개해드린 스마트링크 시즌2, 은하철도 프로젝트 느낌이 어떠신가요? 저희의 설렘과 기대가 잘 전달이 되었을지 모르겠어요. 같은 설렘과 기대가 느껴지신다면 망설이지 마세요! 우리의 은하철도에 탑승할 분들을 그야말로 간절한 마음으로 기다리고 있습니다.  지금 당신은 지원 메일을 보내고있다~!!!

기업문화 엿볼 때, 더팀스

로그인

/