스토리 홈

인터뷰

피드

뉴스

개발에 관심있다면 꼭 읽어야하는 글
조회수 1865

파이콘 2018 도도 파이터 후기

아이들과 오전에 놀아주고 집안일을 마치고 나서 지하철을 탔다. 파이콘에 가는 길이었다. 5년째 참석하다 보니 이제 모든 세션을 빡빡하게 들어야 한다는 부담이 없다. 그래서 늦었지만 여유로웠다. 가는 길에 습관적으로 본  페이스북 타임라인은 이미 파이콘 이야기로 가득했다. 인증과 세션 자료 그리고 개발자를 뽑고 싶어 하는 회사들의 홍보로. 피드에서 스포카에서 진행하는 도도 파이터 이벤트를 보고 "이건 뭐야?" 싶어서  링크를 눌렀다. 어이쿠 개발자 컨퍼런스에 이게 도대체 뭐야오. 깔끔하게 잘 만들었다. 예제 코드를 살펴보니 설명도 잘 되어 있고 간단하다. 도전해 보고 싶은 생각이 들었다. 지하철 자리에 앉아 테더링을 연결하고 코딩을 시작했다. (사실 이것이 내가 세션은 듣지 않고 이틀 동안 부스/이벤트 체험만 하게 된 계기가 될 줄은 몰랐다.)대단히 잘 할 생각은 없었다. 세상에 굇수는 많으니까. 참여에 의의를 둬야지 싶었다. 비록 설명에는 “인공지능 코드”를 작성하여 다른 참가자와 겨루는 “인공지능 격투 대전”이라고 되어 있지만 당연해 보이는 규칙만 구현하고 나머지는 랜덤으로 동작하게 해서 제출해 보자 싶었다. 코엑스에 도착한 후  조금만 더 작업해서 제출하려고 하는데 아무리 제출해도 제출이 되지 않고 다음과 같은 메시지만 받았다.  코드가 테스트를 통과하지 못했습니다.아니 랜덤 봇이랑 하면 잘만 이기는데 왜 통과를 못하는 거야! 하던 차에 다시 설명을 읽어 보니  가만히 있는 더미 에이전트를 상대로 이겨야 제출이 이루어집니다.란다. 먼저 가면 손해인지라 가까워지면 더 안 가고 제 자리에서 주먹질만 시켰더니 더미 에이전트를 못 이기나 보다. 그래서 5초 아래로 시간이 남고 지금까지 한 번도 안 싸웠으면 앞으로 가도록 했더니 테스트를 통과하고 제출이 되었다.  제출에 성공하고 기분 좋게 돌아다니면서 다른 부스도 구경하고 있는데 회사 슬랙으로 함께 파이콘에 참여하고 계신 동료 분이 메시지를 보내셨다.봇이 화끈하면 뭐햐나. 이기면 장땡!스포카 부스에서 사람들이 제출한 봇들을 랜덤으로 붙여 주는 모양이었다. 후후. 어찌 되었든 이겼다고 하니 기분이 좋군.첫날 마지막 행사인 라이트닝 토크에서 스포카 도도 파이터 개발자분의 발표가 있었다. 회사에서 파이콘을 준비하면서 한 달 가까이 준비했다고 한다. 그리고 최근 2주도 동안은 도도 파이터만 달렸다는 이야기를 해주셨다. 컨퍼런스 이벤트로 만든 게임의 퀄리티가 좋아서 감탄한 것도 있었지만 팀에서 개발자들에게 그런 여유를 줄 수 있는 것도 부러운 마음이 들었다. 좋은 회사다. 도도 파이터 토너먼트는 다음날 파이콘 정식 행사가 끝나고 열렸다. 기억으로는 80명 정도가 참여했었던 것 같다. 조별 토너먼트를 진행하고 우승자들을 모아서 다시 토너먼트를 하는 구조였다.   싸워라! 싸워라!조금 늦게 왔더니 자리가 없어서 가장 앞자리에 나왔는데, 내 봇의 차례가 될 때마다 github 계정의 내 얼굴이 스크린에 크게 나와서 부끄러웠다. 외국 친구들은 자기 얼굴 github 프로필에 잘 넣어 놓던데, 왜 우리나라 개발자들은 자기 사진을 안 넣는 걸까... 게다가 내 봇이 나오는 경기는 모두 지루하고 얍삽한 느낌이 있어서 왠지 더 부끄러웠다. 니가 올래? 내가 갈까?다행히 조별리그도 통과해서 결승 리그에 올라갔다. 사실 한 두경기만 이기면 좋겠다 했었는데, 결승 리그에 올라가니 왠지 욕심이 생겼다. 제일 그럴싸하게 싸운 경기운 좋게도 아슬아슬하게 16강부터 4경기를 모두 이겨서 우승을 하고 문성원 CTO님께 해피해킹 키보드도 상품으로 받았다. 기분이 좋으면서도 멋쩍기도 한 기분이다. 사실 이번 파이콘에 와서 여러 곳의 부스를 참여하고, 이벤트도 적극적으로 참여해 본 이유는 내년에 8퍼센트도 파이콘에 스폰서로 참여하고 싶어서 였다. 우리의 (잉여) 개발력도 보여주고, 다른 개발자 분들과도 적극적으로 교류하고 싶은 마음이었다. 그 바람이 꼭 이루어질 수 있게 다음 파이콘 때 까지 좋은 분들을 모시고, 회사의 성장을 만들어 나가야겠다는 생각이 들었다. 마지막으로 내 코드를 공개한다.  https://gist.github.com/leehosung/f784d9efc71dce12855739647dd98877다시 코드를 살펴보니 개선할 점도 여러 개 보인다. 하지만 기존에 제출한 코드를 보기 좋게 정리만 하고 주석만 붙여 보았다. 사실 별 특별한 것이 없는 코드다. 실제 작성하고 테스트하는 것에도 한 시간이 걸리지 않았다.다음에 이런 기회가 온다면 글을 읽으시는 분들도 가벼운 마음으로 도전해 보셨으면 한다.  성적이 좋으면 더 좋지만 나쁘면 또 어떠한가? 개발자인 우리만 즐길 수 있는 놀이인데.  #8퍼센트 #에잇퍼센트 #파이콘 #파이썬 #Python #Pycon #이벤트참여 #참여후기 #개발자 #개발
조회수 1770

네이버 신디케이션 — Rails

블로그에 새 글이 올라올 때, naver에 사이트 등록을 한다. 네이버 신디케이션 API를 이용하면 자동으로 등록된다.Wordpress에는 네이버 신디케이션 plugin이 존재한다. Rails gem을 찾아보니 애석하게도 없었다. 직접 만들면서 알게 되었다. 딱히 gem을 만들 만한 일도 아니더라.네이버 신디케이션을 이용하려면 우선 네이버 웹마스터 도구를 이용해야 한다. 해당 url이 자기 것이라는 인증과정만 거치면 바로 사용할 수 있다.작동방법은 대강 이렇다.네이버 신디케이션 API를 이용해서, 새로운 글이 생성되었음을 알린다. (혹은 글이 지워졌음을)네이버 크롤링 봇, Yeti가 와서 크롤링 해간다.API를 이용할 때 미리 약속된 format으로 만들어야 되는데, ATOM feed와 구조가 거의 같다. 다만 네이버가 정한 룰 때문에 (꼭 이름/저자/업데이트날짜 이런 순서를 지켜야 한다.)Rails에서 제공하는 atom_feed helper를 그대로 이용할 수 없다. 그러나 format만 살짝 바꾸면 되기 때문에 atom_feed helper를 이용해서, feed를 만드는 방법을 알려주는 Railscast가 늘 그렇듯 엄청 도움이 된다.(요즘 새로운 episode가 안올라오고 있는데… 힘내시라는 의미에서 예전에 유료결제 해드렸다)atom_feed helper의 코드를 그대로 가져와서 formating만 바꾼 naver_atom_feed helper를 만들었다. 별다른 건 없고, feed option 초기화 부분과 제일 마지막에 나와야 되는 link 부분을 주석처리한게 전부다.module NaverSyndicationHelper def naver_atom_feed(options = {}, █) ... feed_opts = {} //feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'} ... xml.feed(feed_opts) do xml.id... // xml.link... // xml.link... yield ActionView::Helpers::AtomFeedHelper::AtomFeedBuilder.new(xml, self, options) end end end새로만든 naver_atom_feed helper를 이용해서, feed부분만 완성한 code이다.naver_atom_feed({xmlns: "http://webmastertool.naver.com", id: 'http://ikeaapart.com'}) do |feed| feed.title "이케아아파트" feed.author do |autor| autor.name("이케아아파트") end feed.updated Link.maximum(:updated_at) feed.link(:rel => 'site', :href => (request.protocol + request.host_with_port), :title => '이케아아파트')이제 entry쪽을 만들어야 되는데, 네이버가 지정한 순서에 맞아야지만 신디케이션 서버에 전달할 수 있다. 정말 이상한 형식이다. 아무튼 그래서 Rails에서 제공하는 entry method를 사용하지 못한다. 이번엔 AtomFeedBuilder class에 naver_entry method를 만들었다.#config/initializers/feed_entry_extentions.rbmodule ActionView module Helpers module AtomFeedHelper class AtomFeedBuilder def naver_entry(record, options = {}) @xml.entry do @xml.id... # if options[:published]... # @xml.published(...) # end # if options[:updated]... # @xml.updated(...) # end # @xml.link(..) ...이번에도 순서 때문에 주석처리 한 것 밖에 없다. naver_entry method를 이용해서 완성된 코드가 아래 코드이다.# views/links/show.atom.buildernaver_atom_feed({xmlns: "http://webmastertool.naver.com", id: 'http://ikeaapart.com'}) do |feed| feed.title "이케아아파트" feed.author do |autor| autor.name("이케아아파트") end feed.updated Link.maximum(:updated_at) feed.link(:rel => 'site', ...) feed.naver_entry(@link, {id: link_url(@link)}) do |entry| entry.title(@link.title) entry.author do |author| author.name("이케아아파트") end entry.updated(@link.updated_at.xmlschema) entry.published(@link.created_at.xmlschema) entry.link(:rel => 'via', :href => (request.protocol + request.host_with_port)) entry.content(@link.contents) end end이제 새 글이 만들어 질 때, 이 atom 파일 주소를 네이버 신디케이션 API로 보내주면 된다. 참고로 Rails에서는 어떤 view파일을 사용할지 알아서 해주니, controller에 따로 ‘response_to’ 를 이용해서 format을 나눠줄 필요는 없고, 이름만 잘 맞춰주면 된다. (위 파일명은 show.atom.builder 이다)네이버 신디케이션 API에 핑을 보내는 code이다. 네이버가 지정해 놓은 header를 설정해 줘야 되고, 신디케이션 인증 토큰을 받아서 header에 넣어줘야 된다. 신디케이션 토큰은 네이버 웹마스터 페이지에서 볼 수 있다.require 'net/http' ... header = {"User-Agent"=>"request", "Host"=>"apis.naver.com", "Progma"=>"no-cache", "Content-type"=>"application/x-www-form-urlencoded", "Accept"=>"*/*", "Authorization"=>"Bearer " + ENV["NAVER_SYNDICATION_TOKEN"]} uri = URI.parse('https://apis.naver.com/crawl/nsyndi/v2') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true args = {ping_url: link_url(link_id, format: "atom")} uri.query = URI.encode_www_form(args)request = Net::HTTP::Post.new(uri.request_uri, header) http.request(request)네이버 신디케이션 페이지에서 핑이 제대로 도달하는지 바로 확인해 볼 수 있다.#티엘엑스 #TLX #BA #BusinessAnalyst #비즈니스애널리스트 #꿀팁 #인사이트 #조언
조회수 2936

타다 클라이언트 개발기

앞서 종합 모빌리티 플랫폼인 타다의 시스템 설계를 위한 많은 고민과 기술적 결정들에 대해서 서버팀에서 소개한 바 있습니다. 이번 글에서는 타다 서비스를 출시하기까지 타다 모바일 클라이언트를 개발하는 과정에서 내린 클라이언트 팀의 전략적 결정들과, 타다 클라이언트를 개발하는데 사용한 기술들을 공유합니다.시작 전 상황3달 반의 개발 기간: 타다는 VCNC가 SOCAR에 인수되면서 개발하게 된 서비스입니다. 빠르게 시장에 뛰어들어서 선점하는 것이 무엇보다 중요했기에 시간과의 싸움은 필수적이었습니다. 프로젝트는 6월에 시작되었고 1.0 출시는 추석 연휴 직전인 9월 중순으로 결정되었습니다. VCNC에서 오프라인 운영은 처음이었기 때문에 차량을 실제로 운행해보면서 사용성 경험을 테스트할 필요가 있었습니다. 그래서 8월 초에 사내 테스트용 알파 버전을 출시하기로 했습니다.클라이언트 팀 통합: 비트윈 때는 Android/iOS 팀이 나뉘어 있었습니다. 회사 인수 과정에서 발생한 조직 개편으로 인해 타다 클라이언트 개발자는 5명으로 이루어졌습니다. 전부터 다른 OS 개발도 경험하고 싶던 적극적이고 열정적인 5명의 멤버들은 과감하게 팀을 통합해서 Android/iOS을 함께 개발하기로 했습니다.3개의 앱 개발: 타다의 서비스를 위해서는 Android/iOS, 라이더/드라이버 총 4개의 앱을 제작해야 합니다. 하지만 시간과 일정을 고려했을 때 4개의 앱을 다 제작하기는 무리라고 판단을 했습니다. iOS에서는 내비게이션 앱을 사용 중에 드라이버 앱으로 손쉽게 전환하는 기능을 제공할 수 없고 내비게이션 앱으로 경로 안내를 요청하는 것도 제한적이기 때문에 iOS 드라이버 앱은 제작하지 않기로 했습니다.무에서 시작한 프로젝트: 타다는 코드 베이스가 없는 empty repository에서 시작했습니다. 언어도 바뀌었고 레거시 코드와도 엮이고 싶지 않았기 때문에 비트윈에서 어떠한 라이브러리도 가져오지 않고 전부 새로 만들기로 했습니다.클라이언트 팀의 5명의 정예 용사들. by Sam코드 아키텍처 - RIBs프로젝트가 시작되고 기획이 진행되는 동안 3주의 시간을 기반 작업에 쓰기로 했습니다. 가장 먼저 진행한 것은 코드 아키텍처 정하기입니다. 당시에 제가 SAA(Single-Activity Application)에 관심을 가지고 있었는데, 때마침 Google I/O 2018의 세션 중 Modern Android development: Android Jetpack, Kotlin, and more 에서도 비슷한 언급이 나와서 팀에 제안했고, 본격적으로 조사를 해보았습니다. 팀원들이 조사를 진행해보니 Uber, Lyft, Grab 등 굴지의 모빌리티 서비스 회사들이 전부 SAA 기반으로 앱을 개발했다는 것을 알게 되었습니다. 무거운 리소스인 지도를 중심으로 화면이 구성되기에 반복적인 지도 리소스 할당/해제를 피하기 위한 필연적인 선택으로 보입니다. 큰 기업들이 수년간 서비스를 하며 문제를 느끼고 내린 선택인 만큼 저희도 따라가기로 결정했습니다. 비트윈 때 Activity Stack으로 인해 굉장히 고통을 겪은 적이 있는지라 SAA를 원하는 공감대도 있었고요.SAA로 개발을 하기로 정한 이후에는 어떤 프레임워크를 사용해서 개발할지를 고민했습니다. 여러 개의 오픈소스를 비교할 때 Android/iOS 간의 통일된 아키텍처로 개발할 수 있는지를 가장 중점적으로 보았습니다. 대부분의 팀원이 한쪽 OS에만 익숙하기 때문에 초보임에도 빠르게 적응하고 개발하려면 비즈니스 로직을 구현하는 부분이 통일되어 있어야 한다고 생각했습니다. Uber의 RIBs는 저희의 이런 요구를 가장 잘 충족했습니다. 거기에 데이터의 scope와 전달 방식 명확해서 side-effect 없이 개발할 수 있다는 점, 그로 인해 효율적으로 협업이 가능하고 여러명이 개발한 RIB 을 레고 조립하듯 합쳐서 기능을 완성할 수 있다는 점에서 RIBs를 선택하게 되었습니다.RIBs는 아키텍처를 이해하는 것 자체가 굉장히 난해합니다. 오픈소스 상으로 공개가 되지 않은 부분들도 있어서 저희의 입맛에 맞게 변형하는 데 매우 많은 시간을 할애했습니다. RIBs와 관련한 내용은 Nate(김남현)가 Let'Swift 2018에서 발표한 RxRIBs, Multiplatform architecture with Rx 의 영상 및 발표자료를 참조하세요.추후 RIBs를 상세하게 다루는 포스팅을 해보도록 하겠습니다.서버와의 통신 프로토콜새로운 서버 API가 생길 때마다 해당 API의 명세를 문서화하고 전달하는 것은 굉장히 불편한 일입니다. 또한 문서를 작성할 때나 클라이언트에서 모델 클래스를 생성할 때 오타가 발생할 수도 있습니다. 타다에서는 서버 클라이언트 간 API 규약을 Protocol Buffer를 사용해서 단일화된 방법으로 정의하고 자동화하기로 했습니다. 모든 API의 url은 .proto 파일 이름으로 정형화되어 있고 POST body로 Params 객체를 JSON으로 serialization 해서 보내면 Result JSON이 응답으로 옵니다. 서버가 새로운 API를 개발할 때 .proto 파일만 push 하면 클라이언트에서 스크립트를 돌려서 Model 객체를 생성하고 해당 객체를 사용해서 호출만 하면 되는 아주 간단하고 편한 방식입니다.참고로 타다의 서버군에 대한 설명은 타다 시스템 아키텍처에 기술되어 있습니다.기반 작업타다는 빈 repository에서 시작한 깔끔한 프로젝트였기 때문에 Base 코드와 내부 라이브러리들을 전부 새로 개발했습니다.API Controller, gRPC Controller서버와의 통신에 필요한 모듈들을 개발했습니다. 모든 API는 Rx의 Single과 Completable로 wrapping 되어 있습니다.RIBs가장 자주 사용하는 Router 패턴들을 wrapping.Android에서 구현이 공개되어 있지 않은 ScreenStack 구현.SAA이므로 Android에서 Activity가 아닌 화면 단위의 로깅을 구현.Router의 기초적인 화면 Transition을 구현RIB 뼈대 코드용 template 파일 제작Prefs(Android)/Store(iOS)타다에서는 DB를 사용하지 않고 key-value store로만 데이터를 저장합니다. Android SharedPreference와 iOS UserDefaults의 wrapper를 만들었습니다. Object를 serialization 해서 저장하는 기능, Rx 형태의 getter, cache layer, crypto layer 등이 구현되어 있습니다.Design SupportAndroid에서 drawable을 생성하지 않고 layout.xml 상에서 border, corner-radius, masking을 쉽게 설정하기 위해서 제작했습니다.ButterKtAndroid에서 View Binding 처리를 위해 개발했습니다. 비슷한 기능을 하는 Kotter Knife, Kotlin Android Extension이 가지고 있는 lazy binding 문제를 해결하고 싶었고 가능하면 Butter Knife와 달리 apt 없이 동작하는 라이브러리를 만들고 싶었습니다. 이와 관련된 저희의 생각은 여기에 David(김진형)이 상세하게 기록해 두었습니다. 코드도 공개되어 있으니 잘 활용해 보시길 바랍니다.ToolsModel CompilerPBAndK, swift-protobuf를 수정해 .proto 파일을 저희가 원하는 형태의 kotlin data class와 swift codable struct로 변환하는 스크립트를 구현했습니다.Import ResourceUI/UX 팀에서 작업해서 Google Drive File Stream으로 공유하는 리소스를 프로젝트에 sync 하는 스크립트입니다. 타다에서는 기본적으로 벡터 포맷(Android xml, iOS pdf)을 사용하고 Android에서 벡터로 표현이 안되는 이미지들은 png를 사용합니다. 또한 애니메이션을 위한 Lottie json 파일도 사용합니다. 현재는 Android 용으로만 스크립트가 구현되어 있고 리소스를 프로젝트 내의 각각의 res 폴더에 sync 하는 기능과 svg로 전달받은 벡터 파일을 Android xml 형식으로 변환하는 기능을 포함합니다.sync Lokalise타다에서는 Lokalise로 문자열 리소스를 관리합니다. strings.xml, Localizable.strings 파일로 다운받아서 프로젝트에 sync 하는 스크립트 입니다.Code Template & Settings개발 편의를 위한 간단한 Android Studio Code Template과 코드 통일성을 위한 idea settings를 공유합니다.사용된 기술들OS 공통Firebase: Analytics, Crashlytics, Messaging, Storage 등 다양한 용도로 Firebase를 활용하고 있습니다.gRPC, ProtoBuf: 서버에서 실시간 Event를 받기 위해서 사용합니다.RIBs: 타다의 기반 아키텍처 입니다.Lottie: 애니메이션 요소를 표현하기 위해 사용합니다.Semver: 앱의 버전은 Semantic Versioning 규약을 따라 정의합니다. 버전을 파싱하고 관리하기 위해서 Nate(김남현)가 Kotlin 버전과 Swift 버전의 라이브러리를 제작하고 공개했습니다.Braze: CRM(Customer Relationship Management) 툴인 Braze는 유저를 타게팅해서 전면팝업을 띄우거나 푸시 알림을 발송하기 위해 사용합니다.TeamCity, Fastlane, Beta: CI/CD를 위해서 개발 초기에는 Jenkins를 사용했습니다. 출시 대응을 빠르게 하기 위해서 parallel build 및 우선순위 컨트롤을 하고 싶었는데 Jenkins의 Parallel build가 원하는 대로 동작하지 않아서 현재는 TeamCity로 이전했습니다. Beta를 사용해서 모든 브랜치의 빌드를 배포해서 QA 팀에서 테스트할 수 있게 했습니다. 출시용 빌드는 Android의 경우 아직은 수동 업로드를 하고 있고 iOS의 경우 Fastlane으로 배포합니다.git-flow: Git branching model로는 git-flow를 사용합니다. Branch의 종류에 따라서 TeamCity에서의 빌드 우선순위가 결정됩니다.AndroidKotlin: 당연한 선택이겠죠? 타다의 모든 소스 코드는 Fork 해서 수정한 RIBs의 클래스들을 제외하면 전부 Kotlin으로 구현되어 있습니다.AndroidX: 타다 개발을 시작하는 순간에 AndroidX가 공개되었습니다. 기존 Support Library를 사용하게 되면 언젠가는 migration 해야 할 것이기 때문에 알파 버전임에도 불구하고 처음부터 사용하기로 했습니다. ConstraintLayout, PagingLibrary, Material Component, KTX 등 다양한 Component를 사용합니다.Retrofit, OkHttp: 서버와의 HTTP 통신을 위해서 사용합니다.RxJava: 클라이언트 팀은 Rx 없이는 개발할 수 없을 정도로 적극적으로 Rx를 활용합니다.AutoDispose: Rx subscription을 dispose 하기 위해서 사용합니다. 관련해서 도움이 될만한 글을 읽어보시는 것을 추천합니다. Why Not RxLifecycle?RxBinding: View 이벤트를 Observable 형태로 바꿔주는 RxBinding은 굉장히 유용합니다.Moshi: JSON 라이브러리입니다. Kotlin data class와의 호환을 위해서 Gson 대신 선택했습니다.Glide: 이미지 로딩을 위해서 사용합니다.Detekt: Kotlin을 위한 static code analyzer 입니다. Detekt의 extension을 통해 ktlint도 활용하고 있습니다.Dagger: RIBs는 Dependency injection을 기반으로 합니다. RIBs에선 어떠한 DI system이든 사용할 수 있게 Builder가 분리되어 있습니다. RIBs에서는 Dagger로 설명이 되어 있고 저희도 마찬가지로 Dagger를 사용합니다.ThreeTen Backport: Java8의 날짜 및 시간 라이브러리인 JSR-310의 Java SE6 & 7을 위한 backport 라이브러리입니다. 문자열 파싱 및 시간 연산을 위해 사용합니다.iOSSwift: Kotlin과 마찬가지로 당연한 선택입니다. Swift4.2의 CaseIterable Swift5의 Result 등 항상 최신 버전의 Swift를 사용합니다.RxSwift: 역시나 reactive programming은 필수입니다.RxCocoa, RxGesture: iOS에서도 역시 모든 뷰 이벤트는 Rx 형태로 감지합니다.SnapKit: AutoLayout DSL을 제공하므로 코드상에서 편하게 Constraint를 조절할 수 있습니다.Moya/RxSwift, Alamofire: Http 서버와의 통신을 위해 추상화된 네트워크 라이브러리인 Moya를 사용합니다. 역시나 Rx로 wrapping 된 버전을 사용하고 있습니다.Codable: Swift4부터 제공된 프로토콜로 JSON Encoding, Decoding으로 사용중입니다.Hero: RIBs의 Router가 attach/detach 될 때의 Transition을 처리하는데 이용합니다.Kingfisher: 이미지 로딩을 위해서 사용합니다.KeychainAccess: Access Token 같은 중요 정보를 안전하게 저장하기 위해 사용합니다.Swiftlint: SwiftLint는 fastlane action으로 실행해서 code validation을 합니다.출시 후의 회고짧은 시간에 여러 개의 앱을 만들기 위해서는 시간 및 인원을 아주 효율적으로 배분해야 했습니다. 각 OS의 기존 개발자들이 먼저 프로젝트 기반을 닦는 동안 나머지는 스터디를 진행했습니다. 차량 운영 경험을 쌓는 것이 알파 테스트의 목적이었으므로 일정에 맞추기 위해 드라이버 앱도 개발해야 하는 Android로만 알파 버전을 개발했습니다. 대신에 iOS 알파 버전은 서버팀 YB(김영범)가 아주 빠르게 웹앱으로 개발해주었습니다(1.0은 Native입니다.). 알파 버전의 스펙도 호출-하차까지의 시나리오 외의 다른 부가 기능은 전부 제외했습니다.회사 구성원들이 전부 처음 도전하는 분야였기에 기획을 포함해서 모두가 지속적인 변화에 대응해야 했습니다. 특히 사내 테스트를 시작한 직후 실제 운영을 해보며 깨닫고 변경한 기획 및 UX가 상당히 많았습니다. 개발적으로는 익숙하지 않은 아키텍처인 RIBs를 이해하며 개발하는 것이 생각 이상으로 난도가 높았고 개발하는 중간에도 큰 리팩터링을 여러 번 해야 해서 힘들었습니다. 이러한 이유들로 1.0 출시도 시작 전 상황에서 언급한 것보다 2주 정도 미뤄졌습니다.실제 타다 프로젝트 타임라인하지만 저희는 성공적으로 타다를 출시했습니다! 아래는 팀 내에서 출시를 회고하며 나왔던 몇몇 의견입니다.OS 간 아키텍처가 통일되어서 한 명이 같은 기능을 두 OS 전부 개발할 때 굉장히 효율적이다. 비즈니스 로직의 경우 정말로 Swift <-> Kotlin간 언어 번역을 하면 되는 정도.결과적으로 앱 개발 순서를 굉장히 잘 정했다. 한쪽을 먼저 빠르게 개발하고 문제점을 느껴보며 정비해 나가니까 프로젝트 후반부에 빠른 속도로 기능을 개발하는 데 도움이 되었다. 큰 수정을 양쪽 OS에 하지 않아도 됐던 게 좋았다.짧은 기간 개발했음에도 앱 퀄리티가 굉장히 만족스럽다. 매 상황에서 기술적 선택, 인원 배분 등 경험에서 우러나온 아주 적절한 판단들을 했다고 생각한다.각자 독립적으로 개발하던 기능들이 쉽게 합쳐지고 큰 문제없이 잘 동작하는 하나의 앱이 되는 과정이 정말 신기했다. 아키텍처 설계에 쓴 많은 시간이 결코 아깝지 않았다.마치며아직 저희가 하고 싶고 도전해야 하는 과제들은 무궁무진합니다. 그 중 간략히 몇 가지를 소개합니다.테스트 코드 작성: 시간과의 싸움 속에서 테스트 코드 작성을 지금까지 미뤄왔습니다. RIBs의 Interactor 에 구현된 비즈니스 로직은 반드시 테스트 되어야 합니다.OS 간 구조 통일: 같은 화면임에도 OS 간 작업자가 다른 경우 많은 파편화가 일어났습니다. 1순위로 RIB tree 및 Interactor의 비즈니스 로직 통일하는 작업을 진행하고 있습니다. AlertController 같은 공통적인 컴포넌트들도 최대한 포맷을 통일하려는 작업을 지속해서 진행할 예정입니다.iOS DI: RIBs에서 Android에선 Dagger를 활용해서 쉽게 Builder 구현이 가능하지만, iOS에서는 좋은 방법이 없어서 수동으로 DI를 해결하고 있었습니다. 그래서 Uber가 개발 중인 Needle을 적용하려고 관심 있게 보고 있습니다.네트워크 에러 handling 개선: 중첩돼서 뜨는 Alert를 해결하는 것, global 하게 에러를 처리하는 좋은 구조 찾기 등의 이슈가 있습니다.String Resource 관리: 개발하면서 생성하고 아직 Lokalise에 동기화하지 않은 리소스 키를 체크해서 빌드 오류를 발생시키려고 합니다. 또한 iOS에서 "some_key".localize 형태의 extension으로 번역을 코드상에서 불러오는데 key 값 오타가 나면 런타임에서만 오류를 알 수 있습니다. 따라서 String resource를 enum 형태로 관리하려고 합니다.그 외 50여 가지나 되는 팀원들이 하고 싶은 백로그 목록이 여러분을 기다리고 있습니다. 타다가 성공적으로 런칭할 수 있었던 것은 훌륭한 팀원들이 있었기 때문입니다. 앞으로 저희와 함께 좋은 서비스를 만들어 나갈 멋진 분들의 많은 관심 바랍니다.
조회수 3708

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]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유

기업문화 엿볼 때, 더팀스

로그인

/