스토리 홈

인터뷰

피드

뉴스

조회수 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 ↩#스타일쉐어 #개발 #개발팀 #개발자 #인사이트
조회수 1205

채널 데스크 프론트엔드 기술 스택

오프라인 고객 분석 솔루션 워크인사이트를 개발해 온 조이는 최근 온라인 접객 서비스 채널을 런칭했습니다. 이 글은 채널과 관련된 기술 블로그의 첫번째 글로 채널 데스크 프론트엔드(웹, 윈도우, OSX)의 기술 스택 및 개발 환경을 소개하도록 하겠습니다.React채널 개발을 처음 시작할 당시 (지금으로부터 1년 전) 에 워크인사이트 대시보드 및 기타 사내 툴에서는 AngularJS 1을 사용하고 있었습니다. 비교적 적은 코드로 복잡한 애플리케이션을 빠르게 만들 수 있는 점에는 만족했지만 퍼포먼스면에서는 아쉬운 부분이 많았습니다. 따라서 새로운 프레임워크 및 라이브러리를 리서치 했고 매우 가볍고 렌더링 퍼포먼스 면에서 AngularJS 1 대비 우위에 있던 React 를 사용하기로 결정했습니다.컴포넌트의 설계 패턴은 Redux를 만든 Dan이 제안한 Container 와 Presentational 컴포넌트를 구분하는 방식으로 설계하고 있습니다. 따라서 Container 가 data fetch 및 update 등의 액션을 실행하고 Presentational 컴포넌트들을 조합하여 렌더링을 하게 됩니다.React를 실제 1년째 사용해 본 결과 저를 비롯한 팀원들은 매우 만족하고 있습니다. 구조, 스타일, 동작을 한 컴포넌트로 묶어 재사용성이 매우 높아졌으며 React의 휴리스틱한 Dom diff algorithm 덕분에 렌더링 퍼포먼스에서도 많은 이득을 얻을 수 있었습니다.Facebook Flux Utils아키텍쳐는 페이스북이 제안한 flux 철학에 따라 설계되었습니다. flux를 구현하기 위한 기본적인 유틸리티 기능을 제공하는 Flux Utils을 사용합니다. Flux의 많은 구현체 중에 요즘 가장 인기인 Redux도 고려했었습니다. 저희가 프로젝트를 시작할 당시에 Redux는 5~6개월밖에 되지 않은 프로젝트였고 거의 Dan의 1인 프로젝트였기 때문에 향후 메인터넌스를 장담할 수 없다고 판단했습니다. 그보다는 페이스북이 만든 Flux Utils가 그런 면에서는 더 안전할 거라고 생각했던 것이죠.약 1년 정도 Flux Utils로 개발해오며 몇 가지 문제를 겪게 되었습니다. 애플리케이션이 커지면서 관리해야할 State가 많아지고 그들 사이의 의존성 관리 때문에 Store의 복잡도가 빠르게 증가했습니다. 그에 따라 테스트가 어려워지고 올바른 유닛테스트를 위해서는 테스트 코드 역시 매우 복잡해지는 문제가 있었습니다.그래서 Redux를 다시 리서치하게 되었고, 결론적으로 “단일 Store, 다수Reducer” 라는 Redux의 철학을 통해 State 관리 로직(Reducer)을 단순하고 테스트도 쉽게 유지할 수 있겠다는 생각을 하게 되었습니다. 뿐만 아니라 그 동안 설계와 관련되어 고민하고 필요한 경우 저희 스스로 개발해서 사용하던 많은 부분이 Redux의 서브 프로젝트 형태로 (redux-actions, redux-thunk, reselect 등) 개발되어 사용되고 있는 것을 발견해서 Redux로의 마이그레이션을 결정했고 현재 진행 중에 있습니다.Electron이 글의 도입부에서 이야기한 것처럼 채널 데스크는 윈도우용, OSX용 애플리케이션으로도 제공됩니다. 채널 개발 초기 당시 윈도우, OSX 각각 네이티브로 만들 리소스가 부족했기 때문에 웹 기술 기반으로 네이티브 앱을 만들 수 있는 다양한 솔루션들을 리서치했고 그 중 Electron을 선택하게 되었습니다.Electron은 제가 정말 좋아하는 제품인 Slack, Simplenote에서 사용하고 알려져 있고 국내에서는 Remember 등에서 사용하고 있습니다. 초기 개발 당시에는 안정성에 의문을 제기하는 개발자들도 많았고 저희도 여러 문제와 삽질(인증, 패키징, 이슈 레포팅의 어려움, 메모리릭 등등)을 많이 겪긴 했습니다만 개인적으로는 충분히 프로덕션에 쓸 수 있을 정도 수준이라고 생각합니다. 무엇보다 프론트엔드 개발자가 매우 적은 노력으로도 네이티브 데스크탑 앱을 만들 수 있는 장점이 다른 모든 문제점을 상쇄하고도 남습니다.언어개발 언어로는 자바스크립트 ES6를 사용합니다. 언어를 선택할 당시에도 여러 옵션이 있었는데 가능하면 실험적이지 않고 표준을 사용하는 것이 미래 유지보수에 안전하다고 판단했습니다. 또한 다른 자바스크립트 대안 언어를 사용하지 않더라도 ES6 (일부 ES7 포함) 스펙도 충분히 효율적인 개발이 가능하다고 생각했습니다.코딩 스타일은 기본적으로 Airbnb의 코딩 스타일 가이드라인을 따르며 조이의 상황과 맞지 않는 부분은 엔지니어들과 상의 후 수정해서 사용하고 있습니다. 스타일 체크는 ESLint로 자동화한 뒤 Circle CI와 붙여서 모든 풀리퀘스트에 대해 점검하고 있습니다.테스트초기 개발할 때는 테스트 코드를 별도로 붙이지 않았습니다. 고객의 요구와 기타 상황에 따라 기획과 설계가 크게 변경되기도 했고 그 때마다 기민하게 반응하기 위해서, 어느 정도 확립된 제품이 되기 이전에는 테스트 코드는 작성하지 않는 것이 좋다고 판단했습니다. 이제는 많은 부분이 확정되었고 안정성이 중요해지기 시작했으며 애플리케이션이 커지면서 자동화된 테스트는 필수가 되기 시작했기에 최근에 도입을 하고 있습니다.테스트를 위한 도구는 Jest, Enzyme 등을 사용합니다. Presentational 컴포넌트에 대한 테스트는 props에 따라 원하는 형태로 렌더링이 이루어지는지, 이벤트에 따라 콜백이 잘 실행되는지 등의 Spec 을 작성합니다. Container 컴포넌트에 대한 테스트는 각종 이벤트 및 동작을 시뮬레이션하고 그에 따라 Action이 잘 발생하는지 또는 내부 state가 잘 변경되는지를 테스트합니다. 또한 Store (또는 Reducer), Action Creator, Model, Util 등 모든 구성 요소에 대한 테스트를 붙이려고 노력하고 있습니다. 유닛 테스트가 아닌 e2e 테스트 혹은 css 스타일 테스트 등은 하지 않고 있습니다.빌드 및 배포현재 채널 데스크는 Client-side rendering을 합니다. 초기 로딩 속도가 느리다는 단점이 있어서 Server-side rendering으로의 전환도 고려하고 있습니다. 이미 Node.js 를 사용하고 있어서 Isomorphic Javascript의 형태로 어렵지 않게 전환이 가능합니다.작성된 자바스크립트는 Babel로 컴파일되고 Webpack으로 번들화됩니다. css를 포함한 각종 리소스들 역시 Webpack을 통해 처리됩니다. 웬만한 작업은 npm과 Webpack으로만 자동화하려고 했으며, Electron과 관련된 작업(패키징, 인증 등)들만 gulp를 이용해 자동화됩니다. 모든 리소스들은 Node.js + express 서버로 Serving 되고, Node.js 앱은 Docker로 빌드되어 AWS EC2로 배포됩니다.마무리이상으로 채널 데스크 프론트엔드의 기술 스택을 소개해드렸습니다. 앞으로 각 부분 별로 저희 팀이 고민해 온 문제들과 해결 방법을 공유하고자 합니다. 뛰어난 개발자 분들의 많은 관심과 피드백 부탁드립니다!#조이코퍼레이션 #개발자 #개발팀 #인사이트 #경험공유 #일지
조회수 878

로봇 택시가 산업 지형을 바꾼다

로봇 택시가 산업 지형을 바꾼다"자율 주행 전기차의 등장으로 유가가 배럴당 25달러까지 떨어지고, 새로운 세계 질서가 도래할 것이다."'에너지 혁명 2030' 의 저자로 2015년 한국을 방문한 적 있는 토니 세바 스탠포드대 교수(RethinkX의 공동 설립자)가 최근 미국 CNBC와 인터뷰에서 "기름 수요가 2020년~2021년 사이 1백만 배럴로 꼭지를 찍은 뒤 10년 내(2030년경)로 70만 배럴까지 줄어들면서 유가가 하락세를 면치 못할 것”이라며 이같이 예견했다.점차 많은 사람들이 공유경제의 축으로 자율 주행 전기차를 사용하게 되면서 ▽오일 회사는 생존이 어렵게 되고 ▽심해, 세일가스업체와 정유 파이프라인 회사들도 문을 닫게 될 것이라는 게 그의 예측이다.차량도 소유의 대상에서 서비스의 대상, 이른바 TAAS (transport as a service) 로 바뀐다. 차량을 더 이상 구입하지 않고, 편리하게 앱으로 로봇 택시를 호출해 이용하면 그만이다. 자율 주행 전기차의 가격도 싸지면서, 전기차 이용이 소유에 비해 10배 정도 저렴해질 것이라는 것.  경제학자인 그의 섬뜩한 전망은 계속된다. 급속한 기술의 진보로 인해 값비싼 정유회사나, 적응력이 떨어지는 카 메이커(의 주식)가 매력이 없어지며, 차량과 관련된 직업 중에서 차 딜러도 2024년까지 사라질 것이라는 예상이다.뿐만 아니라, 주차공간의 80% 이상이 쓸모없게 되고, (자동차)보험의 비용도 급격하게 떨어지기 때문에 보험회사도 설 땅이 좁아진다는 것.이 같은 그의 전망이 혼자만의 견해는 아니지만,  다른 사람들은 이런 변화에 훨씬 많은 시간이 걸리고, 그렇게 극적이지도 않을 거라고 내다본다.반대로 그는 차량 관련 지출이 줄면서 가계 소비가 늘어나 경제성장을 촉진할 것이라고 예상했다. 아울러 차세대 차의 개발과 관련된 차 운영체제, 컴퓨터 플랫폼, 배터리, 지도 소프트웨어 회사들을 눈여겨 보라고 조언했다.세바 교수의  유가 하락 전망이  결코 급진적인 것은 아니다.  유가는 WTI(서부 텍사스중질유)기준으로 2016년 2월에  배럴당 20달러대 중반까지 급락한 바 있다.  6월 14일 현재는  40달러대 중반에 머물러 있다.다만, 차량이 소유의 대상에서 이용 서비스의 대상으로 바뀐다든지, 차 딜러가 사라진다든지 하는 등의 혁신적이거나 과격한 전망이 실현될 지는 두고 볼 일이다.참조 : 다음은 토니 세바 교수가 참여한 화제의 보고서 ‘Rethinking transportation 2020-2030’ 원문 링크https://static1.squarespace.com/static/585c3439be65942f022bbf9b/t/591a2e4be6f2e1c13df930c5/1494888038959/RethinkX+Report_051517.pdf
조회수 7333

HTTP 404 Status Code 에 대한 고찰

뭐가 문제였나필자는 현재 HMR(가정간편식) 커머스를 다루는 모 스타트업에서 백엔드 개발자로 재직 중이다. 말이 백엔드지 최근 변화되고 있는 트렌드에 맞춰 열심히 API 작성 셔틀을 하고 있다.API 개발에 주로 사용하는 HTTP 상태 코드는 주로 200 (정상), 400 (잘못된 요청), 401 (보안 토큰 에러), 403 (권한 없음), 404 (찾을 수 없음) 정도가 있었다.문제는 여기에서 발생했는데, API를 계속 개발해 나가다 보니 API 요청 시 데이터가 없을 때 200 상태 코드에 빈 배열을 돌려주어야 하는지, 404 상태 코드를 돌려주어야 되는지 상황에 따라 다를 수 있겠다는 생각이 들었다.만약 '데이터가 없을 수도 있는 상황'과 '데이터가 없으면 안 되는 상황'에서 404 Not Found 에러 코드로 같게 응답할 경우 다음과 같은 애매한 상황이 펼쳐질 수 있다.API를 사용하는 클라이언트가 404 에러에 대한 대응을 에러로 표시할지 데이터 없음으로 표시할지 상황에 따라 다르게 정의해줘야 한다. 결과적으로 클라이언트에서 API 요청에 대한 처리가 복잡해진다.// front-endimport fetch from 'node-fetch'; function fetchUserList() {  // 유저 목록을 가져오는 API를 사용한다고 가정  return fetch('https://api.exmaple.com/users')    .then((response) => {      if (response.statusCode === 404) {        // 이 404 Http 상태 코드를 에러로 처리할 것인가? 데이터 없음으로 처리할 것인가?        // 에러일 경우 : throw new Error('Not Found');        // 데이터 없음일 경우 : return [];      } else if (response.statusCode === 200) {        return response.json();      } else {        throw new Error('Unexpected Http Status Code');      }    })    .then(result => render(successPage, result))    .catch(error => render(failurePage, error));}결국, 어떤 식으로 표시해야 명확하게 표현할 수 있을까 하여 페이스북 존잘 개발자님들에게 의견을 물었다. # 굉장히 많은 분이 의견을 주셨고 나름대로 생각을 정리할 수 있었다.결론적으로는 '데이터 없음'과 '404 Not Found'를 같은 용도로 사용하면 안 된다.그렇다면 뭘 어째야 하나위에서 나온 결론을 조금 더 자세히 풀어보면 다음 내용이다.상황에 따라 데이터가 없는 것이 정상인 상황이 있고, 데이터가 없는 것이 에러인 상황이 있다. 이를 구분 해야 한다.데이터가 없는 것이 정상일 수 있는 상황// server-sideAPI.get('/orders/date/:date', async (request, response) => {  // 특정 날짜의 주문을 검색. 특정 날짜에 주문이 없을 수도 있다.  const { date } = request.params;  const orders = await Repository.Order.findByDate(date);  // 200: OK  // 204: No Contents  response.statusCode(orders.length > 0 ? 200 : 204).json(orders);});데이터가 없는 것이 에러인 상황API.get('/orders/:orderId', async (request, response) => {  // 특정 ID의 주문을 검색. 데이터가 없으면 에러다.  const { orderId } = request.params;  const order = await Repository.Order.find(orderId);  if (order.length > 0) {    response.statusCode(200).json(order);  } else {    // 404: Not Found    response.statusCode(404).json({      message: `${orderId} is Not Found`    });  };});그렇다면 요청한 API 리소스가 없는 경우에는 어떤 에러를 보여줘야 하는가? 일반적으로는 404 Not Found 가 통상적으로 사용되지만 우리는 이미 404를 다른 용도로 사용하고 있다. 다행히도 HTTP 상태 코드에는 501 Not Implemented 이라는 좋은 친구가 있다. 이 친구를 사용할 수 있다.import { Users, Orders } from './Routes'; app.route('/users', Users);app.route('/orders' Orders);app.all('*', (request, response) => {  // 501: Not Implemented (구현되지 않음)  response.statusCode(501).json({    message: 'This Method is Not Implemented',  });})대충 이 정도면 클라이언트는 Http 상태 코드를 보고 다음 로직을 처리할 수 있을 것이다.물론 일반적으로 사용되는 상태 코드들이지만 실제 개발 진행 시에는 클라이언트를 개발하는 개발자와 미리 어떤 상황에서 어떤 상태 코드를 보낼 것인지 정해야 할 것이다.마무리API 개발 시 사용할 법 직한 응답 코드를 정리해보았다.200: OK (정상, 데이터 있음)204: No Contents (정상, 데이터 없음)301: Moved Permanently (리다이렉션)400: Bad Request (실패, 클라이언트에서 넘어온 파라미터가 이상함)401: Unauthorized (실패, 클라이언트에서 넘어온 보안 토큰이 이상함)403: Forbidden (실패, 사용자의 권한으로 리소스를 사용할 수 없음)404: Not Found (실패, 데이터가 있어야 하나 없음)410: Gone (실패, 데이터가 있었으나 삭제됨. 이건 굳이...?)500: Internal Server Error (실패, 서버 로직 문제)501: Not Implemented (실패, 없는 리소스 요청)기타 304나 502, 503 등의 상태 코드의 경우 API Application을 작성하는 개발자의 역할보다는 Server 쪽의 역할에 가깝다고 생각하여 작성하지 않음.뭔가 어렵다고 느껴진다면 다음 짤을 참고해서 쉽게 이해할 수 있다. #플레이팅 #개발 #개발자 #인사이트 #경험공유 #조언 #꿀팁 #HTTP #버그 #버그수정 #문제해결
조회수 11046

Next.js 튜토리얼 6편: 서버 사이드

* 이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다.** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요!목차1편: 시작하기 2편: 페이지 이동 3편: 공유 컴포넌트4편: 동적 페이지 5편: 라우트 마스킹6편: 서버 사이드 - 현재 글7편: 데이터 가져오기8편: 컴포넌트 스타일링9편: 배포하기개요이전 편에서는 깔끔한 URL를 생성하는 방법에 대해 배웠습니다. 기본적으로 다음과 같이 생긴 URL를 가질 수 있습니다: http://localhost:3000/p/my-blog-post하지만 이 URL은 클라이언트 사이드 이동 시에만 동작합니다. 페이지를 새로고침하면 404 페이지가 표시됩니다.페이지 디렉토리에 p/my-blog-post를 부르는 실제 페이지가 없기 때문입니다.Next.js 커스텀 서버 API를 이용하여 쉽게 해결할 수 있습니다. 어떻게 구현할 수 있는지 살펴봅시다.설치이번 장에서는 간단한 Next.js 애플리케이션이 필요합니다. 다음의 샘플 애플리케이션을 다운받아주세요:아래의 명령어로 실행시킬 수 있습니다:이제 http://localhost:3000로 이동하여 애플리케이션에 접근할 수 있습니다.커스텀 서버 생성하기Express를 사용하여 애플리케이션의 커스텀 서버를 생성할 예정입니다. 간단합니다.먼저 애플리케이션에 Express를 추가해주세요:npm install —save express애플리케이션에 server.js 파일을 생성하고 다음과 같이 작성해주세요:npm dev 스크립트를 수정해주세요:이제 npm run dev 명령어로 애플리케이션을 다시 실행시켜주세요.어떤 일이 일어날까요?- 깔끔한 URL을 지원하는 서버 사이드를 추가할 것이다.- 애플리케이션이 동작하지만 서버 사이드의 깔끔한 URL은 동작하지 않는다.- "Express와 Next.js은 함께 동작할 수 없습니다"라는 에러가 발생할 것이다.- "Next.js 커스텀 서버는 프로덕션에서만 동작합니다"라는 에러가 발생할 것이다.커스텀 라우트 생성하기경험했다시피 구현한 커스텀 서버가 "next" 바이너리 명령어와 비슷하기 때문에 이전과 비슷하게 동작합니다.블로그 포스트 URL과 매치되는 커스텀 라우트를 추가해봅시다.새로운 라우트가 있는 server.js는 다음과 같습니다:다음의 코드를 살펴봅시다:단순히 기존 "/post" 페이지에 커스텀 라우트를 매핑했습니다. 또한 쿼리 매개 변수도 매핑했습니다.이게 끝입니다.애플리케이션을 다시 실행시키고 다음 페이지로 이동해주세요:http://localhost:3000/p/hello-nextjs더이상 404 페이지가 보이지 않습니다. 이제 실제 페이지를 볼 수 있습니다.하지만 작은 문제가 있습니다. 뭔지 아시나요?- 아무런 문제가 없다.- 클라이언트 사이드에서 랜더링된 제목과 서버 사이드에서 랜더링된 제목이 다르다.- 서버 사이드에서 랜더링된 페이지는 콘솔에 에러를 발생시킨다.- 클라이언트 사이드에서 랜더링된 페이지는 콘솔에 에러를 발생시킨다.URL에 있는 정보/post 페이지는 쿼리 문자열 파라미터 title을 통해 제목을 가져옵니다. 클라이언트 사이드 라우팅에서는 쉽게 URL 마스킹을 통해 적당한 값을 전달할 수 있습니다. (Link의 as prop을 통해)서버 라우트에서는 URL에 있는 블로그 포스트 ID만을 가지기 때문에 제목이 없습니다. 이 경우 ID를 서버 사이드 쿼리 문자열 파라미터로 설정합니다.다음과 같은 라우트 정의를 볼 수 있습니다:문제가 발생하지만 실제로는 ID를 사용하여 클라이언트와 서버 모두 서버에서 데이터를 가져오므로 이는 별로 문제가 되지 않습니다.그래서 ID만 필요합니다.마무리Next.js의 커스텀 서버 API를 사용한 라우트를 간단히 구현해보았습니다. 깔끔한 URL을 지원하는 서버 사이드를 추가했습니다. 원하는 대로 여러 라우트를 구현할 수 있습니다.Express를 사용하는 것에 국한되지 않습니다. 원하는 Node.js 서버 프레임워크를 사용할 수 있습니다. 커스텀 서버 API에 대한 Next.js 문서를 볼 수 있습니다.#트레바리 #개발자 #안드로이드 #앱개발 #Next.js #백엔드 #인사이트 #경험공유
조회수 1048

컴공생의 AI 스쿨 필기 노트 ⑥인공신경망

인공지능, 머신러닝, 딥러닝이번 6주차 AI 스쿨에서는 딥러닝의 가장 기초적인 부분을 배웠어요. 인공지능과 머신러닝, 그리고 딥러닝을 많이 들어보긴 했는데 이 셋의 차이는 무엇일까요?인공지능이라는 개념은 1956년 미국 다트머스 대학에 있던 존 매카시 교수가 개최한 다트머스 회의에서 처음 등장했고 최근 몇 년 사이 폭발적으로 성장하고 있는 중이에요. 1956년 당시 인공지능의 선구자들이 꿈꾼 것은 최종적으로 '인간의 지능과 유사한 특성을 가진 복잡한 컴퓨터'를 제작하는 것이었죠. 이렇듯 인간의 감각, 사고력을 지닌 채 인간처럼 생각하는 것을 인공지능이라고 해요.인공지능은 위 세 개념 중 가장 큰 개념이에요. 머신러닝은 일반적으로 사람들이 이야기하는 인공지능, 즉 머신러닝에 기반한 인공지능을 말하는데요. 인공지능을 구현하는 구체적인 접근 방식이라고 할 수 있어요.머신러닝에는 linear regression, logistic regression 등의 여러 알고리즘이 있는데요.  그중 학습에 사용되는 모델을 딥러닝이라고 해요. 즉 딥러닝은 완전한 머신러닝을 실현하는 기능이라고 볼 수 있어요. 이러한 딥러닝의 등장으로 인해 머신러닝의 실용성은 강화됐고 인공지능의 영역은 확장됐다고 해요.인공 신경망(Neural Network)오늘 수업의 핵심인 인공 신경망(Neural Network)은 어떻게 만들어졌을까요?뉴런의 구조이것은 우리 몸에 존재하는 신경세포인 뉴런이에요. 뉴런은 전기적인 신호를 전달하는 특이한 세포인데 뇌는 뉴런의 집합체라고 할 수 있어요. 뉴런은 수상 돌기(dendrites, input)에서 신호를 받아들이고 축색 돌기(axon terminals, output)에서 신호를 전송해요. 신호가 전달되기 위해서는 일정 기준(임곗값 : threshold) 이상의 전기 신호가 존재해야 해요. 이 신호들의 전달을 통해서 정보를 전송하고 저장해요.이런 신경세포로 이뤄진 신경망 시스템을 위의 그림처럼 표현할 수 있어요. 이처럼 인공신경망은 사람 몸속의 신경들을 모방해서 만든 시스템이에요.위의 식처럼 뉴런을 수학적으로 표현할 수 있는데요. 입력 값들(X)에 가중치를 두어(W) 값 (f(x))을 구하고 그 값과 임계치와의 관계를 활성함수(active function)*로 판단하여 결괏값을 출력하게 돼요.( * 활성함수는 인공신경망의 개별 뉴런에 들어오는 입력신호의 총합을 출력 신호로 변환하는 함수로 비선형 함수(non-linear function)를 씁니다.**)이때 활성함수는 뉴런에서 임곗값을 넘었을 때만 출력하는 부분을 표현한 것으로 sigmoid 함수, Relu 함수 등 여러 방식이 있어요.인공 신경망의 구조인공 신경망 구조는 위의 그림처럼 나타낼 수 있어요. 인공 신경망 구조는 입력층(input layer), 은닉층(hidden layer), 출력층(output layer)으로 이루어져 있어요. 위의 그림은 그 구조에 의해 3-layer Neural Network 또는 2-hidden-layer Neural Network라 부를 수 있는데요. 3-layer Neural Network는 3개의 층을 가지는 인공신경망이라는 뜻이고, 위 그림에서는 은닉층1, 은닉층2, 출력층이 해당되겠죠. 인공 신경망에 입력층과 출력층은 항상 존재하기 때문에 은닉층의 개수만을 고려하여 부르기도 해요. 위 그림에서는 은닉층이 2개 있기 때문에 2-hidden-layer Neural Network라고 부를 수 있어요. 전파(Propagation)이번에는 실제로 학습하는 과정인 인공신경망의 알고리즘에 대해 알아볼게요. 순전파(Forward Propagation)와 역전파(Backward Propagation)가 있어요.순전파는 입력값에서 출력값으로 가중치를 업데이트를 하고 활성화 함수를 통해서 결괏값을 가져오는 것을 말해요. 인공신경망이 설계된 정방향(input → hidden → output)으로 데이터가 흘러가기 때문에 순전파라고 해요. 말 그대로 입력값을 앞쪽으로 보낸다고 생각하면 돼요.역전파는 출력값을 통해서 역으로 입력값 방향으로 오차를 다시 보내며 가중치를 재 업데이트하는 것이에요. 출력값에서 계산된 오차에 가중치를 사용해 바로 이전 층의 뉴런들이 얼마나 오차에 영향을 미쳤는지 계산해요. 결과에 영향을 많이 미친 뉴런일수록 더 많은 오차를 돌려줘요.개념을 코드에 적용하기NumPy로 구현된 Neural Network(이하 NN)의 작동 방법을 살펴볼게요. NN은 총 2개의 레이어로 이루어져 있어요. 이번 과제에서는 입력 x가 들어왔을 때, 레이블에 따라 예측치가 1로 수렴하는지 알 수 있는 인공신경망을 구현하는 것이 목적이에요.Neural Network다음 코드는 simpleNueralNet() 클래스를 나타내는 코드예요. simpleNueralNet()은 두 개의 레이어로 구성된 NN이에요.N, D_in, H, D_out = 64, 1000, 100, 10- N은 batch size, 즉 한 번에 처리할 수 있는 데이터 사이즈를 말해요. - D_in은 입력값 차원에 쓰이는 값으로 1000을 할당해요.- H는 은닉층 차원에 쓰이는 값으로 100을 할당해요.- D_out은 출력값 차원에 쓰이는 값으로 10을 할당해요.아래 코드를 통해서 랜덤 입력과 출력 데이터를 만들어요.x = np.zeros((N, D_in))     #1  x.fill(0.025)                         #2y = np.ones((N, D_out))   #31. np.zeros() 함수를 사용하여 (64, 1000)의 차원을 갖는 0인 행렬을 만들어요.2. fill() 함수를 통해 x 안의 모든 0을 0.025로 바꿔요.3. np.zeros() 함수를 사용해 (64, 10)의 차원을 갖는 0인 행렬을 만들어요.아래는 랜덤 값을 갖는 가중치(weight)들을 초기화하는 코드예요. w1은 1000, 100 차원의 랜덤 값을 갖는 행렬로, w2는 100, 10차원의 랜덤 값을 갖는 행렬로 만들어요.w1 = np.random.randn(D_in, H)   w2 = np.random.randn(H, D_out)learning_rate는 학습 속도를 의미해요. 아래는 단계별로 움직이는 학습 속도를 1e-6으로 정의하는 코드예요.learning_rate = 1e-6이제 5000번의 순전파를 할 거예요.h = x.dot(w1)     h_relu = relu(h)  y_pred = h_relu.dot(w2)h는 은닉층에 전달할 값이에요. x와 w1을 행렬곱한 값을 가져요.활성 함수 relu에 h를 넣어서 계산해요.y_pred는 예상되는 출력값이에요. relu로 계산된 h_relu와 가중치 w2를 행렬곱한 값이에요.아래는 순전파로 얻은 y_pred에서 진짜 y를 뺀 값을 제곱한 것의 합을 구해 손실 값(loss)을 구하는 코드예요. print(loss) 코드로 손실을 확인할 수 있어요.loss = np.square(y_pred - y).sum()순전파 후 역전파를 이용해 손실에 대한 가중치 w1과 w2의 gradients를 계산하여 update 할 거예요.grad_y_pred = 2.0 * (y_pred - y)              #1grad_w2 = h_relu.T.dot(grad_y_pred)    #2grad_h_relu = grad_y_pred.dot(w2.T)    #3grad_h = grad_h_relu.copy()                    #4grad_h[h < 0>grad_w1 = x.T.dot(grad_h)                         #61. 순전파로 얻은 y_pred에서 진짜 y값을 뺀 값에 2.0을 곱하여 grad_y_pred를 구해요.2. grad_w2는 순전파에서 y_pred = h_relu.dot(w2) 식을 사용했으므로  h_relu.T.dot(grad_y_pred) 로 구해요. h_relu가 반대로 곱해지기 때문에 T를 이용하여 shape을 바꿔줘야 해요.3. grad_h_relu는 방금 위에서 사용한 y_pred = h_relu.dot(w2)을 이용하여 grad_y_pred.dot(w2.T) 로 구해요. 이번에는 w2 shape의 반대를 grad_y_pred에 곱해줘야 해요.4. 순전파에서 h_relu = relu(h)였는데요. 역전파에선 grad_h와 grad_h_relu가 같기 때문에 copy() 함수로 그대로 복사해요!5. 0보다 작은 h는 0으로 만들어요.6. 가중치 w1의 값인 grad_w1은 순전파의 h = x.dot(w1)와 반대로 x.T.doT(grad_h) 곱해요. 역전파는 순전파의 식에서 이항한다고 생각하면 조금 더 쉽게 이해할 수 있을 것 같아요. 이항한 값은 .T를 붙여서 표현한다고 생각하면 될 것 같아요.아래는 가중치를 재업데이트하는 코드예요.w1 -= learning_rate * grad_w1 w2 -= learning_rate * grad_w2 과제1을 통하여 NN을 알아보았는데요. 복잡하지만 순전파와 역전파를 알고 있다면 많이 어렵지는 않은 것 같아요. 과제 2는 정확도를 95% 이상으로 만들어보는 과제인데 여러 가지 방법을 동원해서 풀어보는데 생각보다 쉽지가 않아요. ^^;이번 수업시간에 배운 딥러닝의 기초인 신경망은 굉장히 중요한 개념이라고 해요. 신경망을 기반으로 한 딥러닝을 강화하여 안면인식을 가능하게 하거나 저장된 데이터를 정확하게 인식하고 분류할 수 있는 기기들도 만들어지고 있어요. 이처럼 AI는 점진적으로 활용 범위가 넓어지고 있기 때문에 이 수업을 통해 쌓은 AI 지식을 마음껏 뽐낼 수 있는 날이 왔으면 좋겠어요!** 왜 활성함수로 비선형 함수를 쓸까요?선형함수인 h(x)=cx를 활성함수로 사용한 3-layer 네트워크를 생각해봐요. 이를 식으로 나타내면 y(x) = h(h(h(x)))가 되는데요.  이는 y(x) = c3x와 같습니다.  이렇게 활성함수로 선형함수를 사용하면 은닉층을 사용하는 이점이 없어요.* 이 글은 AI스쿨 - 인공지능 R&D 실무자 양성과정 6주차 수업에 대해 수강생 최유진님이 작성하신 수업 후기입니다.
조회수 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]로 이메일을 주시기 바랍니다!
조회수 1124

해커 준비: 좋은 코드 만들기

출처 : 구글 이미지 검색Just Hacks지난 몇 주간 저는 I/O의 devops문화 기반을 다지는 작업을 해왔습니다. 여전히 부족한 점이 많지만 그동안 일어난 변화를 지켜보면 첫 걸음은 비교적 잘 뗀듯 합니다. 지금부터는 이 devops문화가 제대로 자리잡는 일이 중요한 단계입니다. 다시말해, devops문화가 튼튼하게 뿌리내릴 수 있게 Hacking하는 것이 저의 당분간의 과제입니다.최근 devops를 연구하고 도입하는데 적잖은 시간과 노력을 쏟았기 때문에 실패할 경우 매몰비용이 만만치 않습니다. 꼭 성공시켜야하는만큼 실증적으로 엔진을 검증하기로했습니다. 그래서 지난 주부터는 저도 devops문화에 소속된 벡엔드 엔지니어로서의 일을 시작했습니다. 당분간 직접 코드를 만들어내야겠지요.설계에 그치지 않고 스프린트를 직접 참여해야만 현재 devops문화가 지닌 문제점이 무엇인지 제대로 볼수 있고 훌륭한 기술조직으로 거듭날 수 있다고 저는 믿습니다. 다시 개발자의 자세로 돌아가기 위해 가장먼저 좋은 코드를 작성하는 공부를 시작하였습니다.좋은 코드 만들기컴퓨터가 인식 가능한 코드는 바보라도 작성할 수 있지만, 인간이 이해할 수 있는 코드는 실력 있는 프로그래머만 작성할 수 있다. -마틴 파울러-SW엔지니어가 되기로한 이상, 제겐 감동까지는 아니지만 코드리뷰를 하는 짝꿍이 쉽게 이해할 수 있는 좋은 코드를 짜야할 의무는 있습니다. 그래서 지금까지 감명 깊게 읽은 고전 책들을 복습하기 시작했습니다. 그 첫 번째 책이 켄트백의 구현패턴입니다. 이 책은 설계나 디자인 패턴과 같은 추상적인 내용보다 키보드로 코드를 짜내는 순간에 고민해야하는 부분에서 교훈을 줍니다. 저는 이 책을 통해 코드를 바라보는 제 관점이 다음과 같이 바뀐듯 합니다.필드(현업)에서 생산된 코드는 코드를 작성하는데 드는 시간보다 읽는 시간이 압도적으로 많기 때문에 이를 감안해 봤을 때 읽기 “좋은 코드”를 짜는 노력이 가장 중요하다.돌이켜보면 학생 시절에는 왜 좋은 코드를 짜야하는지 당연히 모를 수 밖에 없었던 것 같습니다. 프로젝트성격의 코드만 짰기 때문에 종강하고나면 제가짠 코드를 다시는 들여다 볼일이 거의 없었거든요. 만약 대학교가 학생들의 취업경쟁력을 높이기 위해 CS 지식 뿐만아니라 Hacker 소양도 가르치고 싶다면 1학년부터 졸업할 때까지 서서히 발전되는 프로그램 하나를 만드는 4년짜리 과제를 두면 효과적일 것 같습니다.말씀드린 것처럼 필드에서 생성된 코드는 작성 시간보다 유지보수를 위해 읽혀지는 시간이 더 많은 편입니다. 특히 린스타트업을 충실하게 따르는 스타트업이라면 런칭기간이 극단적으로 짧기 때문에 제품(SW) 의 생애주기 중 99%의 시간이 유지보수 단계에 있을 것입니다. 이런 관점에 비춰보면 독자를 고려한 좋은 코드를 짜야한다는 사실은 더욱 중요해집니다.새로운 원칙지금까지 제가 견지하고 있는 좋은 코드를 만드는 원칙은 단순화와 중복제거였습니다. 이번 기회에 이 책을 다시 읽고 제 프로그래밍관에 새로운 원칙을 한 가지 더 추가하였습니다. 일관된 추상화인데요.좋은 코드는 일관된 추상화를 보여줍니다. 아래 예시 코드로 바로 확인하실 수 있습니다.void compute() { input(); flag |= 0x0080; // 나쁜 추상화 output(); }이 간단한 compute라는 함수는 제목처럼 입력(input)을 처리하고 이를 16진수 연산을 거친뒤에 출력(output)과정을 거치면서 마무리 됩니다. 그런데, 함 수 2번째 줄에 드러난 flag변수의 16진수 연산은 조금 쌩뚱 맞습니다. 암호처럼 느껴지네요. comput의 절차를 보여주는 input, output 사이에서 세부 구현사항을 친설하게 알려주려는 작성자의 배려는 되려 독자에게 혼란을 주기만 합니다. 이 혼란스러운 코드를 캡슐화를 통해서 일관된 추상화 수준으로 아래 코드처럼 리팩토링 할 수 있습니다.void compute() { input(); updateFlag(color.Brown); // 좋은 추상화 output(); }16진수 연산대신 의도가 드러나는 함수명과 인자전달을 통해 우리는 input을 처리하고 ouput을 갈색 텍스트로 출력시킨다는 사실을 자연스럽게 받아들일 수 있게 됩니다. 보시는 예제처럼 일관된 추상화는 문제해결 능력, 알고리즘 실력보다 코드를 작성하는 센스에 가깝습니다. 항상 독자를 배려하는 마음을 갖고 상대방에 입장에서 서서 코드를 작성하는 습관을 가져야 겠습니다. 이제 코드를 짜고 리뷰도 받으면서 구린내나는 코드를 신나게 리팩토링 할 일만 남았네요 :-)#스위쳐 #Switcher #DevOPS #데브옵스 #개발 #개발자 #DevOPS도입 #인사이트 #성장
조회수 1060

주니어 개발자가 외칩니다, "Hello, System Architecture!"

Overview주니어 개발자는 시스템 아키텍처(System Architecture) 또는 시스템 디자인(System Design)이라는 단어에 덜컥 겁부터 먹습니다. 지금 진행하고 있는 개발에만 집중하다 보니 큰 그림을 놓치고 있는 게 아닐까 란 생각이 들었죠. 조금 더 큰 그림을 보고자 공부를 시작했습니다. 문득 같은 생각을 하는 주니어 개발자 분들도 많을 것 같다고 생각했어요. 그래서 이번 글은 시스템 아키텍처에 ㅇ_ㅇ? 뀨? 하는 표정을 짓는 주니어 개발자들을 위해 썼습니다.상상의 나래: 가상의 패션 e커머스상상의 나래를 펼쳐봅시다. 패션 e커머스 서비스를 이용하는 김유저 씨가 구매한 옷이 마음에 들어 상품 리뷰를 남기고 싶어한다고요.김유저 씨는 본인의 착용 사진과 텍스트 리뷰를 작성하고 ‘리뷰 등록하기’ 버튼에 엔터를 탁! 누를 겁니다. 그런데 말이죠. 김유저 씨는 요청하고 싶은 웹서버의 IP 주소를 모르기 때문에 요청을 보낼 수가 없습니다.내 정체를 알려줘: DNS (Domain Name System)그래서, DNS(Domain Name System)에게 물어봅니다. 서버의 도메인 이름으로부터 해당 서버의 IP 주소를 알려주는 것이 바로 DNS입니다. 도메인 이름에 대한 질의를 하고, 만일 해당 도메인 이름이 DNS에 ‘A Record’ 형태로 등록이 되어 있다면 도메인 이름에 해당하는 IP 주소를 응답으로 돌려줍니다.서비스에서 자체 DNS 시스템을 가지고 있을 수 있습니다. 예를 들어 Route 53, Cloud Flare같은 서비스가 있습니다. 그렇다면 또 한 가지 의문이 생깁니다. 왜 서비스는 시스템적 부담을 안고서 자체 DNS 서버를 구축하고 있는 걸까요? 그 이유로 두 가지를 꼽을 수 있습니다.첫 번째로는 신뢰도가 높습니다. 직접 DNS Record를 관리 및 운영하기 때문입니다. 두 번째로는 보안이 우수합니다. 만약 공개하고 싶지 않은 IP 주소, 예를 들어 Database IP 주소 같은 건 공개하지 않습니다. 1)작업장소: Web Server이제 웹서버의 IP 주소를 알았으니 통신을 시도합니다. 웹서버는 웹서비스에서 필요로 하는 다양한 요청과 그에 대한 응답을 제공합니다. 클라이언트가 리뷰에 대한 사진과 텍스트를 등록하고 싶다면 웹서버에게 등록하라는 요청을 보내야 합니다.웹서버에서 요청을 받으면 사용자가 요구한 대로 사진과 텍스트를 등록하고, 그에 대한 결과 정보를 응답으로 보내줄 것입니다. 웹서버 내부에서는 그 과정에 필요한 연산을 수행합니다. 서버 개발자는 이 연산에 대한 코드를 작성하고요.센스가 없는 서버:API (Application Programing Interface)서버는 사람이 아닙니다. 센스나 재치가 없죠. 미리 정의되지 않은 요청은 대응하지 못합니다. (어버버버버 퉤! Error 404!) 그래서 약속한 요청을 보내면 약속한 방식으로 응답해줄게라고 명세를 제공합니다.약속한 요청으로 데이터를 보내면 원하는 요청에서 데이터를 정제해 잘 처리했는지, 또는 처리된 데이터를 약속한 방식(예를 들어, JSON 방식)으로 내보내죠. 웹서버는 정의된 API에 맞춰 요청과 응답을 합니다.그런데 웹서버가 수많은 요청을 받고 응답하면 과부하가 일어날 수도 있습니다. 사용자 수가 어마어마한 규모로 늘어나서 서버가 펑! 하고 터진다면, 김유저 씨는 서비스를 더 이상 이용할 수 없을 겁니다. 이용하고 싶지도 않을 겁니다!따라서, 서버가 감당하는 요청을 나누기 위해 같은 역할을 하는 서버 장비 수를 늘릴 수도 있습니다. 그러면 요청이 각기 다른 웹서버 장비에 분산되어 한 번에 감당할 수 있는 요청 수가 더욱 많아집니다.이 구역의 매니저는 나야: Load Balancer그림처럼 서버가 4대 존재하는 상황이라면, 서버 4대에 일을 적절히 분배해주는 역할이 필요합니다. 그것이 로드 밸런서(Load Balancer)입니다. 로드 밸런서가 서버에게 일을 나누는 방법론은 여러 가지가 있습니다.Random: 랜덤으로 분배하기Least loaded: 가장 적은 양의 작업을 처리하고 있는 서버에게 요청을 할당하기Round Robin: 순서를 정하여 돌아가며 작업 분배하기많이 쓰는 로드 밸런서의 종류는 Layer 4, Layer 7을 꼽을 수 있습니다.Layer 4 Load Balancer: 데이터의 내용을 보지 않고 IP주소 및 TCP/UDP 정보에 따라 단순히 분배를 해줍니다.Layer 7 Load Balancer: 서버가 하는 역할이 분리되어 있는 환경에서 데이터의 내용을 보고 각기 맞는 역할을 하는 서버에게 분배를 해줍니다.로드 밸런서는 클라이언트가 요청을 보내야 할 서버를 골라야 하는 부담을 덜어주며, 로드 밸런서에게 할당된 vIP (가상 IP)로 요청을 보내기만 하면 로드 밸런서에서 알아서 작업을 나눠줍니다. 서버에서는 적절한 로드 밸런서를 사용하면 들어오는 요청이 여러 장비에 분산되어 처리량이 늘어나고 응답 시간이 줄어드는 효과를 기대할 수 있습니다. 컨텐츠 저장소: CDN(Content Delivery Network)이제 웹서버가 클라이언트의 요청에 의해 웹페이지에 대한 응답 결과를 돌려줬습니다. 이때 클라이언트의 화면에 렌더링해야 하는 수많은 이미지가 필요합니다. 이 이미지들을 웹서버가 전부 주려면 데이터의 용량이 너무 크고, 무거워서 서버가 헥헥거리죠. (서버가 죽으면 어떻게 될까요? 클라이언트님이 경쟁사로 환승하겠죠.. 안 돼요..) 따라서 웹서버는 직접 이미지를 주는 대신 CDN(Content Delivery Network)에게 요청하라고 이야기합니다. CDN은 일반적으로 용량이 큰 컨텐츠 데이터(이미지, 비디오, 자바스크립트 라이브러리 등)를 빠른 속도로 제공하기 위해 사용자와 가까운 곳에 분산되어 있는 데이터 저장 서버입니다. 클라이언트는 용량이 큰 컨텐츠 데이터를 가까운 CDN에 요청해 멀리 있는 웹서버에서 직접 받는 것보다 빠르게 받을 수 있습니다. CDN이 동작하는 방식에는 크게 Push CDN, Pull CDN이 있습니다. Push CDN: 서버에서 컨텐츠가 업로드되거나, 변경되었을 때 모두 반영하는 방식 Pull CDN: 클라이언트가 요청할 때마다 컨텐츠가 CDN에 새로 저장되는 방식 두 방식 모두 장단점이 있습니다. Push CDN은 모든 컨텐츠를 갖고 있기에 웹서버에 요청할 일이 없지만 유지하는데 필요한 용량과 비용이 많이 필요하겠죠? Pull CDN은 클라이언트가 요청한 컨텐츠가 있으면 바로 응답하지만 그렇지 않을 땐 데이터를 웹서버로부터 가져와야 하기 때문에 서버에 요청하는 부담이 존재합니다. 컨텐츠명은 그대로인데 내용만 변경되었다면 인지하지 못하고 옛버전의 컨텐츠를 제공하죠. 그래서 Pull CDN에 들어가는 컨텐츠는 TTL(Time To Live)이 적용됩니다. TTL이란 유통기한이라고 생각하면 쉽습니다. 일정시간이 지나면 해당 데이터가 삭제되는 것이죠. 이런 방식이 적용된다면 Pull CDN의 최대 단점을 보완할 수 있습니다. 이렇게 보완이 되면 수정된 데이터에 대해서도 대응이 가능하며 서버의 용량 즉, 비용적 부담이 해소될 겁니다.소중한 내 데이터: Database서비스를 제공하다 보면 클라이언트의 소중한 정보, 이력, 상품 가격, 상품 정보 등 다양한 데이터를 저장하고, 또 제공합니다. 하지만 수많은 데이터를 웹서버에 전부 저장하고 사용하기엔 데이터의 양이 너무 많아 저장 공간도 부족하고, 데이터를 원하는 모양에 맞게 정제하기가 어렵습니다. 그래서 데이터를 저장하는 데이터베이스 서버가 따로 존재합니다.민감한 정보를 다루는 데이터베이스는 ACID라는 성질을 만족해야 하는데요.Atomicity(원자성): 데이터베이스에 적용되는 명령이 중간만 실행되지 않고 완전히 성공하거나 완전히 실패해야 한다는 것을 의미합니다. 반만 적용된 명령이 있다면 헷갈리겠죠.Consistency(일관성): 데이터베이스가 수행한 명령이 일관적으로 반영되어 있어야 한다는 의미입니다. 예를 들어 계좌에 돈을 입금했는데 잔고에 반영되지 않는다면 당황스러울 겁니다.Isolation(고립성): 데이터베이스가 수행하는 명령 도중 다른 명령이 끼어들지 못한다는 것을 의미합니다.Durability(지속성): 성공적으로 수행한 명령은 영원히 그 이후 상태로 남아있어야 한다는 걸 의미합니다. 갑자기 하루 뒤에 명령이 취소되거나 이전 상태로 롤백되면 안 됩니다. Replication (복제 / 이중화)큰 시스템에서는 똑같은 데이터베이스가 여럿 존재한다고 하는데요. 그렇다면 왜 비용적인 부담을 안으면서까지 복제 데이터베이스를 구축해놓는 걸까요? 만약에 데이터베이스가 정상적으로 동작하지 않는다면 클라이언트의 데이터를 변경하지 못하며, 클라이언트가 원하는 정보를 제공하지 못하는 불상사가 일어나게 됩니다. 글로만 써도 벌써 땀이 납니다. 그러므로 복제해놓은 데이터베이스를 얼른 마스터로 등업해 데이터 흐름에 차질이 없도록 대비해야 합니다.만약 하나의 데이터베이스가 어떤 일을 수행할 때 다른 요청들은 계속 기다려야 합니다. 그렇다면 데이터를 변경하는 데이터베이스는 하나, 읽기만 하는 데이터베이스는 여러 대가 존재해도 되지 않을까요? 바로 여기서 Master-Slave의 개념이 탄생합니다.master-slave-replicaMaster-Slave Replica (a.k.a 주인-노예)요청을 분산하기 위해서 데이터베이스를 늘리다 보면 master-slave 토픽이 등장합니다.Mater: CRUD(Create, Read, Update, Delete)가 모두 가능Slave: R(Read)만 가능Master가 데이터를 변경할 동안 읽기에 대한 요청은 Slave에게 보내집니다. 그렇게 하면 읽기 요청은 분산되어 훨씬 더 수월하고 빠른 속도로 데이터 처리가 가능할 것입니다. 만약 Master가 변경된다면 아래 계급인 Slave, Replica 데이터베이스에게도 이 정보를 전해야 합니다. 다시 말해, 자신에게 들어온 요청(Query)을 동일하게 보내 빠른 시간 안에 동기화를 시켜주죠. 하지만 동기화도 시간이 걸리는 작업이므로 무한대로 Slave Replica를 늘려 확장하기는 어렵습니다.Master-Master Replica의문이 하나 생길 겁니다. “여러 대의 Master를 두어서 변경도 가능하고, 읽기도 가능하게 하면 되지 않을까?”앞서 언급했듯이 같은 데이터의 변경 가능한 데이터베이스는 하나여야 할 것입니다. 동시에 같은 데이터를 변경했을 때 갈등을 해소하기 위한 방법론은 존재하지만, 그 방식이 복잡하고 오래 걸립니다. 안정성도 낮아지고, 효율도 떨어집니다. 그래서 Master-Slave 아키텍처를 선호하는 것이죠.Sharding그러면 같은 데이터베이스 테이블을 동시에 변경하는 건 불가능한 걸까요? 그것을 해소하기 위해 샤딩(Sharding)이라는 방법론을 사용합니다. 샤딩된 테이블은 개념적으론 하나의 테이블처럼 보이지만 사실 그 내용물이 쪼개져 있습니다. 쪼개는 방법은 여러 가지 선택할 수 있습니다만, 분명한 건 겹치는 데이터 없이 쪼갠다는 것입니다. 그래서 같은 테이블이어도 쪼개져 있다면 그 테이블에 동시에 접근해 데이터를 변경할 수 있는 것이죠.이외에 서비스별, 기능별로 쪼개어 데이터베이스를 관리하는 Federation 등 많은 데이터베이스 디자인 방법론이 존재합니다.시스템 아키텍처가 가지고 있어야 할 최소본 아키텍처요점: 시스템 아키텍쳐에서 고려해야 할 성질이렇게 간단한 시스템 아키텍처의 면면을 살펴봤습니다. 시스템 개발자라면 시스템을 디자인하면서 반드시 고려해야 할 성질들을 만날 텐데요. 위에서 소개한 내용들 역시 아래의 성질들을 충족하기 위해 탄생했다고 볼 수 있습니다.Scalability (확장성): 10만 명의 요청을 처리할 수 있는 시스템과 1000만 명의 요청을 처리할 수 있는 시스템은 다릅니다. 확장성을 고려한 시스템은 앞으로 클라이언트 수가 늘어났을 때 무리 없이 모든 요청을 처리할 수 있을 겁니다.Performance (성능): 속도와 정확성을 말합니다. 요청한 내용을 정확하고 빠르게 돌려주어야 합니다.Latency (응답 시간): 모든 요청은 클라이언트가 불편해하지 않을 정도로 빠른 시간 안에 돌려주어야 합니다.Throughput (처리량): 같은 시간 안에 더욱 많은 요청을 처리한다면 좋은 시스템입니다.Availability (접근성): 사용자가 언제든지 시스템에 요청을 보내서 응답을 받을 수 있어야 합니다. 비록 서버 장비 한두 대가 문제가 생겨 제 기능을 하지 못하더라도 사용자는 그 사실을 몰라야 합니다.Consistency (일관성): 사용자가 서버에 보낸 요청이 올바르게 반영되어야 하고, 일정한 결과를 돌려주어야 합니다. 요청을 보낼 때마다 불규칙한 결과를 돌려준다면 믿을 수 없는 서비스가 될 것입니다.결론발로 그렸나 싶을 정도의 그림과 기나긴 글을 마무리 지으며주니어 개발자로서 시스템 아키텍처를 공부하면서 느낀 점이 있다면 시스템에 대한 완벽한 대응은 없으며, 모두 장단점이 존재한다는 것입니다. (이것을 보통 trade-off라고 표현합니다.)하지만 설계하는 서비스를 잘 알고 서비스에서 무게를 둬야 할 부분을 파악한다면, 그에 맞는 시스템을 설계하고 디자인할 수 있을 겁니다. 김유저 씨도 만족시킬 수 있을 거고요. 꼬박 이틀을 밤새워서 쓴 글이 아직 시스템 아키텍처를 두려워하는 다른 주니어 개발자분들에게 도움이 되었으면 합니다. 이번에는 시스템에서 아주 기초적인 부분을 공부했으니 다음 글에선 MSA(MicroService Architecture)를 씹어봅시다! 겁이 나고 무서워도 외쳐보세요. “Hello, System Architecture!”이 세상 모든 주니어 개발자분들, 퐈잇팅입니다.참고1) 추가적인 이점에 대하여: 웹서버에서 요청을 보낼 때 database 도메인 네임으로 보낼 경우, 멀리 있는 공인 DNS 서버 (예를 들면 google public DNS server: 8.8.8.8)에 물어오는 것보다 자체 DNS 서버에 물어오는 것이 훨씬 더 빠른 속도로 응답을 받아올 수 있습니다.출처GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards.글오연주 사원 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #주니어개발자
조회수 3049

챗봇과 인공지능 머신러닝 ㅡ Part 1/2

스타워즈를 보신 분이라면 거기에 나오는 난쟁이 로봇 R2D2와 키다리 로봇 C3P0를 아실 것이다. 친근한 R2D2는 전자음을 조정해 인간과 대화를 하며 주로 말 잘하고 박식한 로봇인 C3P0가 통역을 해준다.이런 충실하면서 똑똑한 친구들이 옆에서 항상 나를 도와준다면 어떨까? 정말 좋을 것이다. 만약 매일 보는 스마트폰 안에서도 나의 질문에 답해주는 이런 고마운 친구들이 있다면 얼마나 좋을까? 이런 저런 생각을 하다보면 우리는 대화형 로봇의 필요성을 느낀다.챗봇(Chatbot)이란?챗봇의 정의는 “대화형 인터페이스 상에서 규칙 또는 지능으로 유저와 소통하는 서비스”이다. 이 말을 하나하나 풀어보자.먼저, 대화형 인터페이스란 뭐지? 어렵다. 쉽게 설명해 보자. 인터페이스는 사람과 컴퓨터를 연결하는 장치라고 한다. 역시 어렵다. 아! 그냥 스마트폰 앱으로 보면 된다. 그럼 소통한다는 말은 대화한다는 것이므로 스마트폰 앱에서 일방향이 아닌 양방향이 가능하다는 얘기다. 어! 이상하다. 양방향이라면 나의 말에 응대하는 로봇은 뭐로 움직이는 거지? 궁금하다. 누가 일정한 규칙으로 만들어 논건지 아니면 우리처럼 지능이 있는 건지. 지능이 있다면 그런 지능은 뭐지? 점차 우리는 자연스럽게 인공지능에 다가간다.인공지능(Artificial Intelligence)이라는 용어는 1956년 미국 다트머스의 한 학회에서 존 매카시가 처음 사용했다고 한다. 원래 인공지능은 소프트웨어인 정신을 말하고 로봇은 하드웨어인 육체를 말하는 것이지만 정신없이 육체가 존재할 수 없는 것처럼 로봇을 얘기하면 당연히 인공지능은 따라간다.학자들은 인공지능을 강(强)인공지능과 약(弱)인공지능으로 구분한다. 간단히 얘기하면 강인공지능이란 자의식이 있는 인간에 가까운 지능이고 약인공지능은 자의식이 없다. 자아가 없으며, 명령받은 일만을 수행한다. IBM의 왓슨(Watson), 작년에 인공지능의 붐을 가져온 구글의 알파고(Alpha-GO) 등은 모두 약인공지능이다. 이런 인공지능을 구현하는 기술은 무엇인가? 바로 기계한테 학습을 시키는 머신러닝(Machine Learning)이다.1959년 아서 사무엘은 머신러닝을 "기계가 일일이 코드로 명시하지 않은 동작을 데이터로 부터 학습하여 실행할 수 있도록 하는 알고리즘을 개발하는 연구 분야"라고 정의했다. 여기서 학습이란, 입력 값을 받아 결과 값을 내는 모델을 만드는 표현과 표현을 통해 주어진 업무가 얼마나 잘 수행됐는지 알아보는 평가, 그리고 평가에서 설정한 기준을 찾는 최적화로 구성된 일련의 과정을 말한다. 중요한건 우리가 시키지 않은 일도 학습에 의해 자율적으로 처리한다는 것이다. 정말 신기하지 않은가?이제 챗봇이 뭔지 감이 잡힌다. 스마트폰 앱상에 존재하는 로봇인데, 물론 육체는 화면의 아이콘으로 밖엔 안보이지만 인공지능을 가지고 머신러닝에 의해 동작을 하면서 우리와 대화를 하는 그분. 그렇다면 이제 남은 건 이분의 지능이 어느 정도인지 또 얼마나 일을 잘하는 지로 판가름 난다.우리는 평생 공부를 한다. 이제는 학교를 졸업하고 나서도 항상 배워야 한다. 학습이 없다면 지능도 없다. 학습은 일일이 지도받는 지도학습과 알아서 공부하는 자율학습이 있다. 알아서 공부하려면 먼저 머리에 지식이 많아야 한다. 역시 기계도 사람과 비슷하게 배운다.  다음시간엔 챗봇에게 학습을 시켜 지능을 가지게 하는 방법에 대해 알아본다.> Part 2에서 계속
조회수 2331

Good Developer 4 | 학습하는 개발자 -고농축 학습 자료 꿀팁

더 이상의 설명은 필요 없다.지금까지 우리는 Good Developer 시리즈는 커뮤니케이션과 나쁜 개발자의 습관을 통해 좋은 개발자가 무엇인지 알아보았다. 이번에는 좋은 개발자가 되기 위한 가장 중요한 조건 바로 학습하는 개발자에 대해 알아볼 것이다.개발자가 새로운 것을 익히고 배우는 것은 너무도 당연하다. 이것에 대해 글을 쓰는 것은 의미가 없는 것 같아서 많은 고민을 했다. 그래서 실질적으로 학습에 도움을 줄 수 있는 아주 고농축 꿀팁들을 주면 좋은 개발자가 되는데 도움이 되지 않을까 생각했다. 이번 편은 학습하는 개발자 - 고농축 학습 꿀팁 편이다.학습은 천천히, 그러나 꾸준히너무나 당연한 말을 한 번 더 하고 시작할까 한다. 개발뿐만 아니라 모든 학습이 마찬가지겠지만, 꾸준히 학습해야 하는 개발자에게 중요한 것은 학습 습관이다. 이것저것 깨작깨작 찔러보고 공부하는 깊이로는 새로운 기술들을 자신의 것으로 만들 수 없으며 오히려 시간을 낭비하는 것일 수도 있다. 하나의 기술을 배우기 시작했으면 서두르지 말고 천천히 음미하면서 학습해야 한다. 그 대신 한두 달 공부하고 끝내는 것이 아니라 충분한 깊이를 가질 때까지 꾸준히 학습하라!직장을 다녀본 사람은 알 것이다. 직장을 다니면서 따로 자기개발을 하고 학습을 하는 것이 쉽지 않다는 것을 말이다. 하지만 좋은 개발자, 더 나은 개발자가 되기 위해서라면 학습을 멈춰 서는 안된다. 그것이 개발자의 숙명이다. 그래서 개발은 정말 개발을 좋아하는 사람만이 할 수 있는 직업인 것 같다. 혹시 자신이 학습을 하는데 있어 자꾸 포기하게 되고 중단하게 된다면 이전에 썼던 '글로 배우는 코딩 1 | 포기하지 않고 끝까지 공부하는 법'편을 참고해보길 바란다.아래의 학습 정보들은 많이 알 수도 있는 정보지만, 개발자가 되려는 사람들, 잘 모르는 사람들에게는 분명히 좋은 정보가 되리라 생각한다. 알고리즘 사이트 Top 31. 백준 온라인 저지백준 저지는 1만 개 이상의 알고리즘 문제를 보유한 사이트다. 타 사이트에 비해 홈페이지 구성도 잘 되어 있고 문제도 잘 나누어져 있다.그리고 사람들이 문제들을 골라서 자신만의 문제집을 만들어서 공유하기도 한다. 또한 알고리즘 지원 언어도 60개 이상이기 때문에 어떤 언어를 공부하든 웬만해서는 문제없이 풀 수 있다.(이런 언어도 있나 싶을 정도로 많은 언어들의 채점을 지원하고 있다.)기회가 되면 언어들을 직접 세어보는 것도......Baekjoon Online JudgeBaekjoon Online Judge 프로그래밍 문제를 풀고 온라인으로 채점받을 수 있는 곳입니다. 14264 전체 문제 11797 채점 가능한 문제 9316 풀린 문제 64 채점 가능한 언어www.acmicpc.net2. 코드워즈(codewars)코드워즈는 게임 형식의 알고리즘 학습 사이트다. 약 20여 개의 언어를 지원하며, C, C++ C#, Go PHP, JAVA, Python 등 주요 언어들은 모두 지원한다. UI/UX적으로도 굉장히 구성이 잘 되어 있고 인터페이스만 익숙해지면 정말 좋은 코딩 학습 사이트다.영어 사이트이긴 하지만 어느 정도의 독해 수준이면 충분히 학습할 수 있다. 게임 형식으로 알고리즘을 풀기 때문에 정말 재미있게 알고리즘을 학습할 수 있는 사이트! 알고리즘 사이트 중 가장 추천하는 사이트다. 태그도 잘 되어 있어서 function, array, data types 별로 자신이 약한 부분을 집중적으로 학습할 수도 있다.Codewars: Train your coding skillsCodewars is where developers achieve code mastery through challenge. Train on kata in the dojo and reach your highest potential.www.codewars.com3. 프로그래머스프로그래머스는 단계적으로 알고리즘 문제를 풀어볼 수 있는데 최적화된 사이트다. 레벨 1부터 레벨 8까지 정리된 프로그래밍 알고리즘을 풀 수 있다. 지원되는 언어가 C++ 자바 파이썬 자바스크립트로 가장 많이 쓰는 언어만 지원한다는 단점이 있다.모든 문제가 한글이라서 영어가 부담되시는 분들에게는 체계적으로 부담 없이 할 수 있다. 문제를 풀고 제출하는 환경도 잘 구성되어 있어 편리성이 좋은 알고리즘 학습 사이트다. 다만, 다른 사이트 들에 비해 문제의 수가 적다는 점! 영어가 부담되고 단계별로 알고리즘 문제를 풀고 싶다면 이 사이트를 추천한다.프로그래머스동영상과 실습으로 구성된 최고의 프로그래밍 강좌를 만나세요. 프로그래머스에서는 프로그래밍 강좌, 알고리즘 문제, 프로그래밍 대회, 블록체인 자료를 만날 수 있습니다.programmers.co.kr코딩 학습 사이트 Top 51. 유데미(Udemy)엄청나게 질 좋은 강의를 엄청나게 저렴한 가격으로 이용할 수 있는 곳! 1만 원대의 강좌에서 이 정도 퀄리티의 학습 콘텐츠를 얻기는 유데미 외에서는 불가능할 것이다.(광고 글이 아니다 정말이다.) 강의의 분야와 주제도 많고(개발 외에도 여러 가지가 있다) 짧게 짧게, 5~7시간 커리큘럼의 강의들이 많아서 부담 없이 학습할 수 있는 사이트. 강의 수준도 초급부터 고급까지 다양해서 수준 있는 개발자들도 들을 강의가 많다.대부분이 영어 강의이기는 하지만 요즘 한국 강사들의 유입도 늘어서 한국 강의도 늘고 있는 추세다. 영어 자막도 제공하니 영어를 읽을 수만 있다면 강력 추천하는 학습 사이트다.글을 클릭하면 유데미 사이트로 이동합니다.2. 코드카데미(codecademy)체계적으로 코딩을 배우고 싶은데 무료로 배우고 싶다면?! 바로 코드카데미다. 동영상은 보기 귀찮고 읽으면서 단계적으로 코딩을 배우고 싶다면 코드카데미가 적격이다. 자바스크립트를 주축으로 하는 개발 도구 위주만 배울 수 있다는 단점이 있지만, 코딩을 직접 하면서 배울 수 있다는 큰 장점이 있기 때문에 동영상 강의만 보고 그냥 넘길 수 있는 다른 사이트와는 다르게 바로바로 코딩을 쓰면서 배울 수 있다. 역시 영어 학습 사이트지만 개발자가 되기 위해서 필요한 영어 실력만 가지고 있어도 충분히 학습해 나갈 수 있다.Codecademy - learn to code, interactively, for freeCodecademy is the world's most popular way to learn over 12 coding languages including HTML, CSS, JavaScript, Python, SQL, and Ruby. Sign up today and start learning to code in minutes.www.codecademy.com3. 코드스테이츠한국 최초의 코딩 부트 캠프 코드스테이츠. 코드스테이츠 입장에서 코드스테이츠를 추천하는 것이 민망해해 보일 수 있어도, 그만큼 자부심이 있다. 온/오프라인 교육이기 때문에 다른 온라인 교육 사이트보다 저렴하지는 않지만, 개발자를 꿈꾼다면 일정 금액을 투자하고 개발자가 확실히 될 수 있다는 장점이 있다.  온/오프라인에서 직접 멘토링을 받아 가면서 학습을 하고 싶다면 코드스테이츠를 강력 추천한다.온라인 학습과 오프라인 코칭으로 온라인 콘텐츠와 오프라인 교육을 둘 다 가져갈 수 있다는 장점이 있다. 코스 중간에 미니 해커톤과 실제 기업과 협업 프로젝트를 진행해 볼 수 있다는 것도 큰 장점! 단점은 다른 온라인 학습 사이트에 비해서는 가격이 어느 정도 있다.코드스테이츠 | 혁신적인 코딩 교육 부트캠프코드스테이츠(Code States)는 프로그래밍을 배우고 싶은 사람들을 위한 최상의 코딩 교육 프로그램을 제공합니다. 자바스크립트 HTML CSS를 기초로 탄탄한 이론과 실무에 최적화된 기술 스택들을 학습합니다. 주입식이 아닌 자기주도적 학습 방식으로 기존과는 차별화된 혁신적인 교육 시스템을 경험해보세요.goo.gl4. 유다시티유다시티는 가격대는 있지만 탄탄하고 검증된 커리큘럼의 온라인 학습 사이트다. 프로젝트 베이스에 과제도 탄탄하고 동영상 학습 중간중간 텍스트 자료와 퀴즈까지 적절하게 섞여 있어서 충분히 제값을 한다. 다른 온라인 학습 사이트에 비해 가격대가 있지만 그만큼 퀄리티는 훌륭하다. 가장 핫한 트렌드의 기술들도 배울 수 있고 난이도도 초급부터 고급까지 다양한 과정들이 있다.유다시티에서는 학습하기가 굉장히 편하다. 학습 시간을 적절히 쪼개서 부담 없이 학습이 가능하고 자료 또한 탄탄하다는 것이 장점! 하지만 역시 온라인 학습치고 가격은 부담이 된다.Udacity - Free Online Classes & Nanodegrees | UdacityJoin Udacity to learn the latest in Deep Learning, Machine Learning, Web Development & more, with Nanodegree programs & free online courses.www.udacity.com5. 인프런영어가 유데미가 있다면 한국어는 인프런이 있다! 다양한 수준의 프로그래밍 강의를 한국어로 들을 수 있는 온라인 학습 사이트다. 탄탄한 커리큘럼에 강좌 구성까지. 필요한 강의를 골라 들을 수 있다는 장점이 있다. 한국어 강좌다 보니 강사들과의 소통도 원활하다. 영어가 아직은 부담스럽다면 인프런에서 먼저 시작해보자!퀄리티 높은 무료 강좌도 존재하니 처음에는 무료 강좌들을 보면서 나에게 맞는지 확인해 보고 학습을 시작하면 된다. 단점은 유데미 보다는 가격이 비싸다는 점! 하지만 한국어 강의가 많다는 것 자체가 엄청난 장점이라 할 수 있겠다.인프런 - IT, 개발의 좋은 지식을 공유합니다개발, CG, 디자인 등 IT 분야의 고급 지식들을 편하고 경제적으로 학습할수 있는 공간입니다. 배우는 사람에겐 기회를, 지식공유자에겐 보상을 주는 문화를 만들어요.www.inflearn.com코딩 관련 질문을 하고 싶다면스택오버플로우(stackoverflow)스택오버플로우는 개발과 관련된 질문과 답변을 하는 사이트다. 코딩을 하다가 중간에 막혔는가? 괜찮다. 당신의 문제는 이미 선배 개발자들도 했던 고민이니 말이다. 스택오버플로우에서 how to 라는 말과 함께 당신이 궁금한 점을 물어보라 마법과 같은 일이 펼쳐질 것이다. 당신이 알고 싶어 하는 거의 모든 개발 관련된 문제들에 대한 답이 이곳에 있다.영어라서 부담스러워하지 말고 익숙해져보라. 스택오버플로우만 잘 이용해도 현재 당신이 안고 있는 개발 문제의 대부분이 해결될 것이다. 단, 이곳에서의 코드를 너무 복붙 했다가는 오히려 실력 저하가 온다는 것을 명심하기를...Stack Overflow - Where Developers Learn, Share, & Build CareersStack Overflow | The World’s Largest Online Community for Developersstackoverflow.com블로그&커뮤니티JS서울js서울은 자바스크립트에 대해 넓고 얕은 지식을 서울 사용자들에게 보급하려는 지역기반 커뮤니티다. 슬랙방을 만들어서 운영되고 있으며 자바스크립트를 이용하는 이용자라면 활동을 해보면 좋을 것이다.seoul.jsMeetups 2017.08.18(1st) Meeting Notes 2017.07.10 2017.07.19(kickoff) 2017.10.11(conference Staff Offline Meeting 1st) 2017.10.31(conference Staff Online Meeting 1) Seoul.js About Code Of Conduct Sponsors Why We Started Seoul.js Logo Call For Speaker Plan 2018 이제 폭넓게 사용되는 자바스크립트의 매력과 인사이트를 대한민국, 서울에seoul.js.orgOkky개발자들의 커뮤니티, 페이스북이 아니라 다른 페이지를 만들어서 활동하고 있는 개발 커뮤니티 중 가장 큰 규모가 아닌가 생각한다. 개발 관련된 질문도 하고 개발자와 관련된 생활, 진로, 일상들을 이야기하는 개발자 커뮤니티다. 질문을 올리면 선배 개발자들의 따끔한 조언을 얻을 수 있다.OKKY - All That DeveloperEditor's Choice 실리콘밸리를 그리다 - 24. 애자일 방법론으로 프로젝트 진행하기 Karen 10k 6일 전 [OKKY 세미나] 대용량 서비스 성능 개선 노하우 Karen 10k 6일 전 'IT업계 포괄임금제 미적용 특례지정'을 요청합니다. Good Luck 484 8일 전 [OKKY 취준 세미나] 국비 지원 학원 선택의 노하우와 효과적 학습법에 대하여 형 439 13일 전 OKKY 스팸 단어로 인한 글 등록 불가 문제 관련 공지사항 OKKY 475 29일 전 Q&A 자동로그인 코드 구현 할okky.kr조대협님 블로그이미 알만한 사람은 다 안다는 조대협님의 블로그. 개발자 블로깅은 이렇게 하느거야라는 정수를 느낄 수 있고 실제 유익한 정보들이 많이 올라온다. 유명한 개발자 블로거들이 많지만 나열하자면 지면이 길어지기에 대표적인 조대협님의 블로그를 추천한다. 개발 관련된 글, 정보를 얻고 싶다면 이곳에 들어가 보라!조대협의 블로그평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴.bcho.tistory.com
조회수 1269

린더를 만들고 있는 이유 1.0

여러 인공지능 서비스가 우후죽순 생겨나고 있습니다. 그리고 각각의 '인공지능 비서'들이 내세우는 주요 기능 중 하나는 바로 일정 관리죠. 그럴만도 한것이 일정관리야 말로 인간이 가장 큰 보조를 받을 수 있는 영역 중 하나이기 때문이라고 할 수 있겠습니다.개인 비서가 없어봐서 모르겠지만 영화나 드라마를 보면 주로 훤칠하게 잘생긴, 또는 아름다운 비서가 회장님이 묻기도 전에 그의 다음 일정을 알려줍니다. 내가 언제, 어디서, 무엇을 해야 하는지 끊임 없이 기록하고 상기 시켜주는 사람이 옆에 있다면 나의 삶도 여러모로 편해질수 있지 않을까요.이러한 측면에서 볼 때 다양한 인공지능 서비스가 나오고 있다는 점은 환영 할 일이지만, 그 서비스들이 실질적으로 사람들의 삶에 도움이 되는 기능들을 갖추고 있느냐는 완전히 다른 차원의 질문이 될 수 있습니다. 이름만 인공지능일 뿐이지 할줄 아는 것이라고는 내가 입력한 일정을 당일 아침에 읊어주는 수준이라면, 그것을 '비서'라고 부르기에는 부족할지 모릅니다.대부분의 사람들이 일정을 놓치게 되는 이유는 주로 해당 일정을 기록해두지 않기 때문입니다. 바쁜 생활 속에서 모든 일을 일일히 기록하기는 매우 어렵고, 나중에 해야지라는 생각으로 묻혀두었던 일정들은 어느새 지나있기 마련이죠.진정으로 똑부러지는 일정 도우미라면 내가 일정을 직접 입력하기도 전에 내가 선호할 만한 일정들을 먼저 정리하여 제시할 수 있어야 합니다. 우리는 여러개의 일정 중 가장 끌리는 것을 선택하기만 하면 되는것이죠. 그렇다면 위와 같이 사용자가 일정을 입력하기 전 먼저 선택지를 제시하기 위해서는 무엇이 필요할까요?현재 히든트랙팀에서 제공하고 있는 일정구독서비스, 린더( https://linder.kr )는 화장품 세일일정, 학교 학사일정, 프로야구 경기 일정 등 다양한 일정들을 한데 모아 개인의 캘린더로 구독 받을 수 있도록 돕고 있습니다. 현재까지 약 2만명의 사용자가 7천개가 넘는 다양한 일정들을 받아보고 있죠.아직 린더의 데이터는 아이돌 스케줄, 학사일정, 프로야구 경기일정 등에 국한되어 있지만, 이후 공연 티켓팅, 쇼핑몰 세일 등 다양한 분야로 확장해나갈 계획입니다. 기존에 심한 건망증으로 매번 놓쳤던 티켓팅이나 세일 일정이 있다면 린더를 통해 해당 일정을 놓치지 않고 실행에 옮길수 있게 되는것이죠.내가 직접 기록하지 않더라도 내 캘린더의 표시 되어있는 일정을 통해 행사나 이벤트에 참여할 수 있으며 주요 일정들에 대해서는 푸시알림을 통해 일정 시작 전 행사 정보를 파악 할수 있습니다. 락페스티벌을 좋아하시는분이라면 주요 락페스티벌의 티켓팅 및 공연 일정을 받아볼수 있고, 마라톤을 좋아하시는 분이라면 연간 마라톤 일정을 미리 확인 할 수 있게 되는것이죠.현재 린더는 캘린더를 통해 일정을 제공하고 있지만 이는 어디까지나 린더가 정보를 제공하는 여러 채널 중 하나일뿐입니다. 포화 된 앱 시장에서 돌파구를 찾고자 일시적으로 캘린더 플랫폼을 사용하고 있지만, 저희가 확보하고 있는 일정 데이터는 캘린더 뿐만이 아닌 모바일앱, 챗봇, AI스피커 등 다양한 형태로 제공 될 수 있습니다.캘린더에 표시도 안 한 2학기 수강신청을 10분 전에 내게 먼저 알려줄수 있는 앱이 있다면 멋지지 않을까요. 아침에 일어나자마자 고대하던 신상 구두가 출시 되었음을 알려주는 스피커가있다면 사랑스럽지 않을까요.잊고 있었던 티켓팅, 화장품 세일, 축구 경기, 신상 출시를 알려주는 당신만의 비서를 만들기 위해 저희 팀에서는 지속적으로 서비스를 개선해나가고 있습니다.아직 써보지 못하셨다면 사용해보신후 가감없는 피드백 부탁드리며, 내가 만들어도 이것보다 잘만들겠다 싶으신분이 있으시면 제게 연락주세요 ( [email protected] ). 제가 잘 꼬드겨서 저희팀으로 모셔갈수 있도록 하겠습니다 :)2017년 8월 2일. 목을 다쳐 하루종일 침대에 누워있지만 더 이상 잠은 안오는 어느날 밤.#히든트랙 #챗봇 #기술기업 #개발자 #개발팀 #인사이트 #경험공유

기업문화 엿볼 때, 더팀스

로그인

/