스토리 홈

인터뷰

피드

뉴스

조회수 1740

네이버 신디케이션 — 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 #비즈니스애널리스트 #꿀팁 #인사이트 #조언
조회수 938

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

스푼을 만드는 사람들 여섯 번째 이야기서비스 플랫폼 팀 막내이자 분위기를 담당을 맡고 있는, 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 曰: '감초 같은 사람' - 약방의 감초처럼 저희 팀 업무 전반에 없어선 안될 사람(큐가 이렇게 하라고 시켰어요) 
조회수 13602

슬랙봇, 어디까지 만들어봤니?

스포카에서 다년간 일하면서 나에게는 몇 가지 별명이 생겼다. 그 중 하나는 봇맘(Bot mom)이다. 다른 스타트업에서처럼 으레 스포카에서도 주어지는 일만 하는게 아니라 작고 큰 문제를 스스로 발견하고 고민할 기회가 왕왕 생긴다. 나 또한 그런 기회가 있었고 그러던 중 (귀차니즘을 극복하기 위해라고 쓰고) 일을 더 효율적으로 하기 위해(라고 읽는다) 봇(Bot)에 재미를 느끼게 되었다. 그리고 하나 둘 봇으로 문제를 해결하게 되었고 어느새 사람들이 그 별명을 붙여주었다.봇(Bot)2014년 즈음부터 스포카는 슬랙(Slack)을 사내 메신저로 사용하기 시작했다. 슬랙 도입 초창기에는 기본적인 업무 커뮤니케이션과 아틀라시안 제품군(JIRA, Confluence 등), Github 등 사내 업무 툴의 슬랙 라우팅 기능으로만 슬랙을 사용하였다. 하지만 기본 기능 만으로는 실제 업무 환경에서 불편한 부분들이 더러 있어 슬랙봇 기능을 점차 활발히 사용하게 되었다. 팀마다 사용빈도는 다르지만 현재 많은 직원이 슬랙봇을 활용하고 있는데 지속적으로 업무 환경을 개선하는데 봇 기능이 상당한 기여를 하고 있다.인터넷 상에서 자동화된 작업(스크립트)를 실행하는 응용 소프트웨어봇(Bot)은 위와 같이 설명되고 있다. 예를 들어, 슬랙에서 사용자가 설정한 단어가 입력되거나 시간대가 되었을 때, 설정했던 이미지나 텍스트가 자동으로 나오는 기능이라고 생각하면 된다.슬랙에서 기본적으로 제공하는 슬랙 봇과 Reminder 기능만 잘 활용해도 누구나 업무환경 개선을 시도해볼 수 있다. 개인적으로 스포카의 봇 활용(hacking)1은 어떠한 다른 팀과 비교해도 뒤지지 않는다 생각한다. 실제 업무에 적용한 사례를 보면 봇이 무엇인지, 무엇을 할 수 있는지 아는데 도움이 될 것이다. 큰 도움이 되고 있었던 사례를 모아 소개하겠다.2Case1. 자연스럽게 직원들에게 세뇌시키기상황 및 의도서비스 내 용어가 팀별로 다르게 쓰이거나 여러가지로 불리고 있는 것들이 있었다. 혹은 서비스가 런칭/업데이트되면서 개편된 제품/기능이름들이 있었다. 이는 아는 사람끼리는 문제가 없지만 신규입사자나 아직 전달이 덜된 타팀과 소통할 때에는 오해가 생길 수 있었다. 이런 상황에서 UXD팀에서는 추가적으로 새로운 이름을 알리고 즉각 교정 효과를 볼 수도 있는 효율적인 방법을 고안하고자 했다.1-1. 도도 매틱이 도도 메시지로 서비스명이 변경되었음을 알리는 봇이다1-2. 개편된 제품/기능이름을 알릴 때 쓰였던 슬랙봇들. 시간이 지나면서 제 임무를 다하고 사라졌다.효과잘못된 단어를 사용할 때마다 봇이 알려주니 즉각 교정 효과가 나타났다. 사람마다 교육되는 기간을 달랐지만 점차 잘못된 단어를 사용하는 사람들이 사라졌고, 몇 개월 후에는 옛날의 잘못된 단어가 무엇인지 까먹은 사람도 있었다. 그리고 시간이 지나고 제 목표를 달성한 슬랙봇들을 삭제하기까지 이르렀다.Case2. 개발자님 도와주세요ㅠㅠ상황 및 의도디자이너가 코드를 다루다가 가끔 알 수 없는 함정에 빠질 때가 있다. 서버가 왜인지 켜지지 않는다거나 원인을 명확히 알 수 없는 에러가 뜬다거나 하는 경우다. 그런 때면 개발자에게 도움을 요청하는데, 개발자의 입장에서는 진행하고 있던 업무를 잠시 중단하고 해결할 수 있는 커맨드를 알려주거나 알아보는데 시간이 걸릴 수 있다. 이럴 때 봇이 취해야하는 커맨드를 알려준다.봇으로 개발자가 도와줘야 하는 단계가 하나 줄었다!효과개발자가 도움요청 메시지를 보기 전, 디자이너가 먼저 바로 응급처치를 해볼 수 있어 덜 답답했고 개발자도 하나의 예상원인을 제거할 수 있어 빠르게 상황을 파악할 수 있었다.Case3. 항상 똑같은 질문과 답변은 그만!상황 및 의도기억력의 한계와 투명한 업무 진행상황 공유를 위해 이슈 기록 등 문서 작성에 기를 가하는 문화가 있다보니 사내위키문서가 자연스레 방대해졌다. 찾고자 하는 문서가 어딨는지 못 찾아 메일함과 위키사이트를 헤매고 못 찾으면 항상 팀원들에게 물어보게 되어 괜히 미안한 상황이 있었다. 그냥 누군가 물어볼 때 딱!하고 찾아주었으면 했다.다른 경우로는, 매번 특정 팀에게 물어보는 것이 있다. 사이트 내 친절히 설명을 작성하고 공지해도 정보 접근이 귀찮거나 어려운 곳에 있으면 바로 담당자에게 물어서 바로 올바른 답변을 얻고자 하게 된다. 이런 경우, 같은 질문을 하는 사람은 수십 명인데 답변하는 사람은 한 두명여서 답변하는 담당자는 피로해질 수 있다.3-1. 우리팀 주간미팅 회의록이 어딨더라...?3-2. 디자인팀에게 요청할 때 뭘 알려드려야 하지?3-3. 이 지역 담당자가 누구더라?효과원하는 문서의 바로가기 링크를 바로 얻거나 정보를 얻을 수 있어 위키 메뉴를 헤매지 않고 시간을 절약할 수 있었다. 반복적으로 물어보게 되는 사항을 물어보고 싶을 때 불편한 마음을 전혀 가지지 않아도 되었다.Case4. 이번엔 누구에게 의견을 물어볼까?상황 및 의도현재 스포카 Visual design팀(이하 VD팀)은 5명이며 디자인이라면 모두 관심을 가지고 의견을 주는데 주저함이 없다. 어떤 이슈를 진행할 때 중간 점검의 느낌으로 가볍게 1~2명에게 리뷰를 받고 싶을 때가 있다. 항상 같은 사람에게만 리뷰를 부탁하는건 아닌지, 다양한 의견을 받아보고는 싶은데 누구에게 돌리는게 좋을까, 리뷰어 선정에 고민을 하게 될 때가 있다. 혹은 이슈진행자가 정해지지 않았을 때 마음의 짐을 덜고 책임자를 정하는 잔인한 방법이 되기도 한다.(ㅋㅋ) 5명인데 1명 혹은 2명을 고르고 싶으므로 or/and를 병기하여 모든 경우의 수를 정리하여 봇을 만들었다.VD리뷰랜덤효과누구에게 리뷰를 맡길지 고민하는 시간이 줄었다. 타팀에서도 VD팀 누군가에게 리뷰를 부탁하고 싶을 때 활용되기도 한다. 하지만 휴가 중이라던지 가끔 리뷰를 볼 수 없는 사람이 계속 무작위로 나올 때가 있어 두세번 봇을 불러야 하는 일이 있다.Case5. 다나와 대화형 봇 (심화)앞서 소개한 유형들이 너무 단순하다고 느껴진다면 키워드 봇을 연속적으로 활용해보는 방법도 있다. 채팅형 봇을 만든 듯한 착각을 느끼게 할 수 있다.사이즈 다나와 (혹자는 이 사례를 보고 슬랙해킹의 정점을 달려가는 것 아니냐 감탄하였다.)Case6. 잊는 법이 없는 나만의 비서!봇이 일상화되니 왠만한 정기적인 업무일정은 무조건 봇으로 만드는게 습관이 되었다. 예전에는 다른 봇제작 서비스를 통해 만들던 기능이었는데, 슬랙에 리마인드(Remind) 기능이 업데이트 되면서 더 편해졌다. 리마인드 기능 설명은 이쪽을 참고 바란다.6-1. 스프린트 시작 알림 봇6-2. 데일리미팅 알림 봇6-3. 주말의 시작을 알리는 봇6-4. 파트타이머 급여 처리를 잊지 않도록 도와주는 비서봇Case7. 슬랙 API를 활용한 데이터드리븐 봇 (고급)상황 및 의도지금까지 소개한 것들은 회사 내부에서 업무를 진행할 때 도움을 받거나 내부 커뮤니케이션을 위한 것들이었다. 이번에 소개할 것은 회사 서비스와 관련된 개발자 친화적인 방법이다. 서비스 내 DB와 슬랙에서 제공하는 API를 접목하여 별도의 트래킹(tracking)툴 없이 실제 사용자의 행동 중 주요하게 알아야 하는 것을 슬랙봇으로 만든 것들이다.7-1. 부정적립으로 의심되는 이벤트를 알려주는 봇7-2. 매장 잔여코인 알림과 코인결제완료를 알려주는 봇효과별도의 트래킹툴이나 웹사이트에 접속할 필요 없이 실시간 데이터를 파악할 수 있었다. 또한 서비스에 주요한 영향을 끼치는 사용자의 실제 행동을 팀원들과 함께 빠르게 공유할 수 있었다.봇을 만들 수 있는 다른 방법슬랙의 리마인드 기능을 쓰지 않더라도 봇을 부릴(?) 수 있는 방법이 있다. 슬랙봇은 슬랙을 사용해야하고 관리자 권한이 있어야 설정 가능하다. 그러므로 개인적으로 쓴다면 아래 2가지 서비스들을 추천한다. 조합할 수 있는 서비스가 다양하니 자동화할 수 있는 아이디어가 있다면 시너지가 엄청날 것이다. 업무 뿐만 아니라 일상생활에도 활용할 수 있다.Zapier글쓴이가 슬랙에 리마인드 기능이 없을 때 애용하던 서비스이다. 무료 플랜으로 사용하면 설정할 수 있는 봇 개수와 작동하는 횟수가 제한적이지만 소소하게 가끔 필요한 것을 쓰기에는 괜찮다. 업데이트가 계속 되고 있으니 시도해보시라.IFTTTIf this, then that. 컨셉별 봇 레시피가 잘 정리되어 있어 바로 일상생활에 적용해볼 아이디어를 제공한다. 슬랙 외에도 다양한 앱과 연동하여 사용할 수 있는 장점이 있다.슬랙봇과 스포칸업무에 유용한 봇을 위주로 소개했으나 스포카의 슬랙봇은 업무의 즐거움을 향상시키는 스포칸의 드립 아카이브 역할을 하기도 한다. 업무에 활용하는 것만큼 다양한 방식으로 소구되고 있는데, 드립의 특성상 시간이 지나면 그 재미가 무뎌지는 것들이 있어 굳이 소개하지는 않겠다. 또한, 그외 개발자분께서 직접 창의적인 봇용 앱을 만든 사례도 여러 개 있었는데 나중에 기회가 되어 소개를 해볼 수 있으면 좋겠다.계속 슬랙이 업데이트되면서 나 외에도 비IT직군도 슬랙봇을 잘 활용해나가고 있고, 다른 팀원들도 번뜩이는 위트를 겸하며 슬랙봇을 활용하고 계시다. 여러가지로 활용되고 있는 슬랙봇은 하나의 값진 유산이라고 생각되기까지 한다. 간단한 기능임에도 더 집중해야 할 곳에 집중할 수 있도록 도와주기도 하고, 동료 간의 유대감을 깊게 만들기도 하기 때문이다. 스포카 외에 슬랙을 사용하는 다른 회사/팀들도 각자 사용하고 계시는 툴을 재밌고 유용한 방식으로 활용하며 팀 커뮤니케이션에 보탬이 되었으면 좋겠다.시스템 혹은 프로그램의 문제를 고치기 위한 행위 ↩이 포스팅의 예시 중에는 1~2년 전 스포카의 슬랙에서 활발히 쓰였다가 현재는 잘 사용되지 않는 경우도 있다. ↩#스포카 #개발 #개발자 #사내문화 #조직문화 #인사이트 #꿀팁
조회수 2953

야놀자 기술 블로그 만들기

Hello world!저는 CX서비스실에서 기획을 담당하고 있는 강미경입니다. R&D 그룹의 기술 블로그, 그 영광의 첫 포스트로 개발의 보람을 대신할 수 있어 기쁩니다. 오늘은 ‘기획자가 어쩌다가’ 기술 블로그를 만들게 되었는지 얘기해보려고 합니다.왜 기술 블로그인가제가 야놀자에 입사한 지 만 1년이 되었습니다. 입사하면서 가진 개인적인 목표 중의 하나는 블로그를 운영하는 것이었습니다. 저는 오래전부터 개인 블로그를 운영하고 있고, 외부 커뮤니티 활동에서도 팀 블로그를 운영합니다. 그래서 개발자에게는 기술 블로그에 쓸 글을 작성하는 것보다 코딩을 하는 게 더 쉬울 정도로, 글 쓰는 고통이 남다르다는 것도 알고 있지요.하지만 ‘알고 있다’고 생각하는 정보를 정리하고 그것이 잘 전달될 수 있도록 하는 것은 개발실력과는 약간은 다른 영역의 것이기도 합니다. 그래서 테크 스웩이 넘치는 블로그가 아니더라도, 꾸준히 스토리를 전달하면 그게 개인과 조직의 히스토리로써의 가치가 충분하다고 생각했습니다. 무엇보다 조직 자체의 성장에 큰 밑거름이 되고요.블로그를 시작해보자기술 블로그를 하자는 말에, 놀랍게도 한결같이 ‘관심만’ 주더군요(…) 평소 업무가 많고 바쁨을 떠나서, 보람보단 책임만 남아 유지보수 대상이 되어버릴 가능성이 무궁하지 않겠습니까. 하지만 목마른 사람이 우물을 파라고, 개발자의 도움 없이 블로그를 만들 각오를 하기에 이르렀습니다.(과거의 나를 규탄…#야놀자 #개발팀 #블로그 #인사이트 #경험공유
조회수 476

자바스크립트, 웹페이지의 들러리에서 주인공으로!

지루한 통근(학) 시간. 대중교통으로 이동하는 동안에는 자연스럽게 스마트폰을 찾게 되지 않나요? SNS로 다른 사람과 연락을 하거나, 재미있는 영상을 보기도 하죠. 이때 우리는 웹페이지에 있는 텍스트, 이미지, 영상 등 수많은 정보를 보게 됩니다. 웹페이지를 보기 위해 어떤 브라우저를 사용하시나요? 대부분 Chrome이나 Internet Explorer 등을 사용하실 거예요. 이 브라우저를 개발하다가 만들어진 언어에 대해 이야기해볼게요.움직이는 브라우저 ― 자바스크립트의 탄생지금은 대부분 Chrome이나 Internet Explorer와 같은 브라우저를 사용하지만 1990년대 초반만 해도 Mosaic(모자이크)라는 브라우저를 사용했어요.Mosaic 브라우저의 Yahoo! 페이지 (출처 : dweb3d.com on Pinterest)이 당시의 웹페이지는 대부분 흰색 바탕에 검은색 글씨, 그리고 파란색 글씨로 된 링크로만 구성되어 있었는데요. 지금의 웹페이지와 비교해보면 굉장히 지루하고 단조롭죠.아마도 같은 지루함을 느꼈던 것 같은 '브랜든 아이크'라는 사람이 새로운 브라우저를 개발했는데 단 10일 만에 웹페이지에 동작을 넣을 수 있는 언어를 뚝딱 만들어냈어요. 지금처럼 버튼을 눌렀을 때 안내 창이 뜨게 하는 등 좀 더 생동감 있는 웹페이지를 만들 수 있게 된 거예요.이때 만들어진 언어가 바로 JavaScript 랍니다!Java? Javascript! ― 이름의 유래Java와 [removed] 이름이 유사하네요!JavaScript라는 언어가 생소한 분들도 아마 Java라는 언어는 한 번쯤 들어보셨을 거예요. 이 두 언어는 이름이 비슷하지만 전혀 다른 언어예요. 마치 인도와 인도네시아처럼요!이와 관련해서 재밌는 일화가 있는데, 사실 지금의 JavaScript는 초창기에 Mocha(모카)라는 이름으로 개발되었어요. 그런데 당시에 Java 언어가 개발되어 큰 인기를 끌게 되자 Java를 만든 회사와 협약을 체결해 이름을 JavaScript로 변경했답니다. Java의 인기가 높아짐에 따라 덩달아 JavaScript의 인기도 높아지게 되었죠! Javascript 전성시대JavaScript의 인기가 높아지게 된 이유는 비단 Java의 유명세 때문만은 아니에요. 2000년대 중반에 들어서서 기술이 점점 더 발전함에 따라 웹페이지에서 시각적인 것이 중요해졌는데, 태생부터가 웹페이지를 생동감 있게 만들기 위해 개발된 JavaScript는 이런 상황에 활용되기 제격이었던 겁니다.많은 사람들이 웹페이지에 JavaScript를 사용하게 되고, 또 JavaScript를 잘 활용하기 위해 관련 정보들을 모은 라이브러리(자료집)가 발달하면서 활용 분야는 더욱더 넓어졌어요.Node.js : JavaScript의 변신!특히 node.js라고 하는 라이브러리는 JavaScript가 웹페이지를 표현하는 역할에 그치지 않고, 웹페이지와 웹페이지 사이를 연결해주는 연결고리(서버) 역할을 하게 해주었어요.이렇게 JavaScript를 사용하는 분야가 증가하면서 사용자 수도 폭발적으로 증가하게 되었고 현재 JavaScript는 웹 개발에 필수적인 언어로 자리매김하게 되었습니다.또 다른 장점 ― Javascript를 배우는 이유수많은 사람들이 JavaScript를 배우려고 하는 이유는 또 있어요. 우선 C언어나 Java보다 시작하기 쉽다는 점 때문인데요. 예를 들면 C나 Java는 변수를 선언할 때 숫자형, 문자형 등 자료의 유형을 명시해주어야 하지만 JavaScript는 그럴 필요가 없어요. 쉽게 이야기하면 앞의 두 언어는 자료를 상자에 담아서 관리할 때 반드시 자료의 크기에 맞는 상자를 준비해줘야 하지만 JavaScript는 그럴 필요 없이 마치 요술 상자처럼 하나의 상자에 모든 자료를 담을 수 있죠! 그래서 어떤 자료를 다룰 때 그 자료의 형태를 일일이 따져보지 않아도 된다는 편리함이 있어요.JavaScript는 앞서 이야기했던 것처럼 웹페이지를 꾸미거나 이들의 연결망을 만들고, 엄청 많은 자료들을 저장하는 저장소(데이터베이스)를 짓는 데에도 쓰이는 등 활용하는 분야가 무궁무진합니다.웹페이지를 보조하기 위해 탄생한 언어가 웹페이지를 만들기 위한 주류 언어가 되다니, 정말 놀랍지 않나요? 앞으로 JavaScript가 어떤 분야에서 활약하게 될지 더욱더 기대되는 이유입니다!>> 자바스크립트 과목 보기(참고 자료)Press release announcing JavaScript, "Netscape and Sun announce JavaScript", PR Newswire, December 4, 1995.Brendan Eich (3 April 2008). "Popularity". Retrieved 2018-07-06.              
조회수 2850

iOS 개발을 위한 11가지 노하우

Overview기고 제안을 받자마자 iOS 개발을 시작했을 때가 떠올랐습니다. 신대륙을 마주한 것 같았던 그때의 기분을 아직도 잊지 못하기 때문입니다. 당시까지만 해도 Android 개발만 했기 때문에 iOS는 그야말로 미지의 영역이었습니다. 게다가 개발을 시작하려고 조심스럽게 첫 발을 내딛은 순간, 입이 떡 벌어질 수밖에 없었죠.“이렇게 느린 IDE가 있다니…““개발자 프로그램이 뭐 이렇게 비싸?”XCodeXCode는 그동안 접했던 IDE 중에서도 가장 최악이었고, 개발자 프로그램 등록은 13만 원 상당의 비용을 지불해야 했습니다. 가장 중요한 건 맥 컴퓨터(Macintosh)를 보유해야만 했죠. 처음 개발을 시작하려니 넘어야 할 산이 매우 많았습니다. 맞습니다. 팜므파탈의 대명사 마타하리(Mata Hari)처럼 iOS 개발은 밀당과도 같습니다. 분명 매력적인 일이지만 XCode와 개발자 프로그램 등록은 빙산의 일각이기 때문입니다. iOS는 곳곳에 구덩이를 파고 초보 개발자들을 집어삼킬 준비를 하고있습니다. (예를 들면 리소스를 묶어놓은 R.java 파일 같은 레퍼런스가 없습니다. 흑.)그래서 준비했습니다. 수많은 초보 개발자들이 iOS의 구덩이를 피해갈 수 있는 팁을 말이죠.iOS의 구덩이를 피하는 11가지 방법1.비싼 맥(Macintosh)을 사세요.iOS 개발자에게 MacOS는 필수입니다. XCode가 MacOS만 지원하기 때문입니다. 오픈 소스로 공개된 Swift에는 제약이 없지만 XCode는 MacOS에서만 동작하는 제약이 있습니다. 따라서 맥은 iOS 개발자에게 가장 필요한 준비물입니다. 게다가 하드웨어 리소스를 많이 사용하는 XCode 탓에 더 크고, 더 비싸고, 더 아름다운 맥을 구매하셔야 합니다. Macbook이나 Macbook Air 모두 추천하지 않습니다. 15형 Macbook Pro 모델을 비롯해, Mac Pro나 iMac Pro 등의 고급 모델을 사용하면.. 개발이 잘 됩니다.2.돈을 내세요.iOS를 개발하려면 가장 먼저 Apple Developer Portal에서 연 129,000원의 개발자 프로그램에 등록해야 합니다. XCode를 사용해서 코드만 볼 것이라면 문제가 되지 않지만, 디바이스에 앱을 설치하고, 테스트하며, AppStore에 배포할 거라면 반드시 구매해야 합니다. 이 계정은 앞서 말한 것처럼 1년이 지나면 다시 구매해야 합니다. 만약 기업 개발자로 등록하려면 Enterprise Program이 따로 준비되어 있습니다. 기업을 위해 특화된 In-House 배포 등의 이점이 있습니다. 구매해야할 것이 꽤 많죠? 이제 익숙해져야 합니다.3.XCode를 설치하세요.XCode는 Mac App Store에서 설치할 수 있습니다. 용량이 크기 때문에 설치하기 전에 하드디스크 저장공간부터 확인하는 것이 좋습니다. 처음 실행하면 추가 컴포넌트를 다운로드하는 과정이 실행되고, 그 이후에 XCode를 사용할 수 있습니다. XCode와 관련된 자세한 내용은 아래에서 자세하게 다루겠습니다.4.어려운 것에 대비하세요.1)인증서‘들’XCode 설치 이후에도 몇 가지를 발급 받고, 셋업해야 합니다. 방 탈출 게임처럼 한 단계 한 단계 거치는 과정이 필요합니다. 첫 번째로 인증서‘들’을 발급받아야 합니다. 애플을 대신해 앱을 설치하고, 배포할 수 있는 권한을 위임 받는 과정입니다. 이 인증서들은 Apple Developer Portal의 ‘Certificates, IDS & Profiles’ 항목에서 발급 받을 수 있으며, MacOS의 키체인 앱을 이용해 개인 키를 생성하는 방식으로도 방식으로 발급 받을 수 있습니다. 2)디바이스 등록디바이스-iOS-에 개발한 앱을 설치하려면 애플 개발자 계정에 개발용 디바이스를 등록해야 합니다. 이 과정은 XCode에 신규 디바이스를 연결하고, 빌드 및 배포를 할 때 XCode가 알아서 합니다. 만약 디바이스를 보유하고 있지 않은 상황이라면 해당 디바이스의 UUID를 받아서 개발자 포털에 직접 등록할 수도 있습니다. 3)Bundle IDBundle ID는, 앱의 고유한 ID입니다. iOS가 앱을 식별할 때 사용하는 식별자이며, 보통 ‘com.companyname.appname’ 의 형식으로 회사나 개인의 도메인을 거꾸로 쓰는 것이 보편적입니다. 하지만 Bundle ID는 어디까지나 개발자가 결정하는 영역이므로 인스턴스 이름 지정하듯이 자신만의 고유한 방법을 사용해서 Bundle ID를 지정해도 문제가 없습니다. 4)Provisioning ProfileProvisioning Profile은 디바이스 정보와 앱 정보, 인증서 정보를 매핑해주는 Profile입니다. 최신 XCode에서는 이 Provisioning Profile을 자동으로 관리해주기 때문에 따로 신경쓰지 않아도 좋습니다.5.개발자 포럼에 질문하거나, StackOverflow에 질문하거나!질문하는 사람은 아름답습니다. 궁금하거나, 잘 안 풀리는 코드는 개발자 포럼에서 질문할 수 있습니다. 대신 영어 실력이 좋아야 합니다.크게 기대는 하지 않는 것이 좋습니다. 등록된 discussion에 대한 답글들이 ‘나도 같은 상황이다’, ‘나도 궁금한 점이다’ 등의 다른 개발자들의 답변 정도가 일반적이기 때문이죠.그들의 답변...저는 개발자 포럼보다 StackOverflow를 더 선호합니다. 참여하는 개발자 규모가 다르기 때문에 보다 양질의 정보를 빠르게 찾을 수 있습니다. (하지만 허위 정보도 존재합니다.) Vote 시스템으로 신뢰 높은 정보를 필터링할 수 있으나, 어떤 정보를 선택할지는 당신의 몫입니다.6. iTunesConnect와 친하게 지내세요.앱을 개발했다면, iTunesConnect를 통해 앱을 전 세계의 사용자들에게 배포할 수 있습니다. iTunesConnect는 iOS용으로 개발된 바이너리를 배포하는 등 앱 배포/테스트와 관련된 전반적인 사항들을 관리할 수 있는 포털입니다. AppStore에서 앱을 판매하려면 이 iTunesConnect를 통해 애플과 계약을 해야만 가능합니다. 출시할 앱을 등록하기도 하고, 앱의 사용자들이 어떤 경향을 보이는지 Trend Analysis를 확인할 수도 있습니다.iTunesConnectiTunesConnect에는 다양한 메뉴들이 있고, 앱을 배포하고 관리하는데 필요한 여러 툴이 있으므로 개발 중에 시선을 환기하고자 한다면 iTunesConnect를 한 바퀴 둘러보는 것도 좋습니다. 언젠가는 다 사용하게 될 테니까요.7.앱 개발을 마쳐도 XCode를 사용하세요.앱을 개발하고 iTunesConnect에 업로드하려면, XCode를 통해 간접적으로 바이너리를 업로드하게 됩니다. 서드파티 앱을 사용할 수도 있지만, 제가 주로 많이 사용하는 방식은 XCode입니다. 소스코드가 준비되었다면, XCode 메뉴의 Product > Archive 메뉴를 선택해 XCode가 배포용 앱을 빌드합니다. 빌드가 완료되면, 자동으로 Organizer 창이 열리면서 앱을 업로드할 수 있게 되죠. 이 때 미리 구매한 개발자 계정의 인증서가 준비되어 있어야 합니다. 모든 준비가 완료되고 아카이빙이 완료되면, Organizer의 Archives 탭에서 우측단의 ‘Upload to App Store…’ 버튼으로 바이너리 업로드를 진행할 수 있습니다.8.배포 전에 시험비행을 해봅시다.앱을 개발했다면, 테스트플라이트(TestFlight)를 통해 실제로 앱을 배포하기 전 ‘시험비행’을 할 수 있습니다. iTunesConnect에 관련 테스터들을 등록하고, 등록된 사용자들을 대상으로 미리 앱을 테스트할 수 있도록 요청하는 것이죠. 이 테스트플라이트에 배포된 바이너리를 그대로 AppStore에 배포하게 되므로, 테스트용으로 유용합니다.TestFlight테스트플라이트는 원래 iOS 배포 관리 솔루션을 제공하는 업체였지만 지금은 애플이 인수해 iTunesConnect에서 관리하도록 제공하고 있습니다.9.앱이 죽는다면 Organizer로 확인하세요.iOS는 충돌보고 Crash Report를 Organizer를 통해 오류를 확인합니다. 앱을 설치한 사용자가 동의하면 XCode는 iOS가 앱을 실행하면서 발생한 Crash Report를 애플에 자동으로 업로드합니다. 업로드된 Crash Report들은 XCode의 Organizer를 통해 다운로드하고, 확인할 수 있습니다. Organizer는 XCode > Window > Organizer 항목에서 실행하세요.Organizer와 Crash ReportCrash Report는 Organizer의 상단 Crashes 탭에서 확인이 가능합니다. 또 현재 보고 있는 Crash Report의 어느 부분에서 오류가 발생했는지 알고 싶다면 우측단의 ‘Open in Porject…’ 버튼을 눌러보면 됩니다.10.내 친구 XCode최근 XCode는 메이저 업데이트를 통해 사용성과 퍼포먼스를 향상시켰습니다. 하지만 이만큼 무겁고 느린 통합개발툴 IDE는 이클립스(Eclipse) 이후에 처음입니다. 안드로이드의 경우 IntelliJ 기반의 Android Studio로 쾌적한 개발환경을 제공하고 있는 반면, XCode의 업데이트는 퍼포먼스나 사용성 개선보다는 Swift의 메이저 버전 반영에 더 급급한 느낌입니다. (업데이트 때마다 속지만 ‘혹시 이번에는..’하고 또 속아 넘어갑니다.) XCode 사용을 위한 네 가지 팁을 소개합니다.1)XCode는 모노로그입니다.XCode는 로그를 따로 ‘예쁘게’ 볼 수 없습니다. 검은 화면에 흰 로그가 정리되지 않은 상태로 마구마구 출력됩니다. 개발자들에게는 쥐약같은 상황이죠. 이런 불편한 로그 출력 방식 때문에 저는 별도의 GlobalLogger 모듈을 작성해서 다음과 같은 스타일로 로그를 출력하도록 하고 있습니다.$$ BrandiLogger Error Log ##MESSAGE: Initial Parameter is not exist. ##LOCATION: BRLogPringer.swift @Line: 122 2)iOS개발자를 위한 휴식시간, 빌드 타임XCode의 빌드 타임은 개발자에겐 기나긴 휴식 시간입니다. 소스가 비대해질수록 퍼포먼스는 떨어지며, 담배 한 대를 태우고, 화장실에서 손을 씻고 들어와도 빌드가 절반도 안 된 상황을 마주할 겁니다. 빌드 타임을 줄이고자 구글링을 하면 몇 가지 팁을 발견할 수 있는데, 특히 빌드 타임을 가장 많이 단축할 수 있는 방법이 있습니다.짜잔! 공개합니다!먼저, 프로젝트 셋팅의 ‘Build Settings’ 항목에서 ‘Optimization Level’을 검색합니다. ‘Swift Compiler - Code Generation’ 항목을 찾을 수 있는데요. 여기서 Optimization Level의 Debug 항목을 ‘None’으로 설정하면, 빌드시간이 엄청나게 줄어든 것을 확인할 수 있습니다. Brandi iOS 버전의 소스코드는 원래 컴파일에 7분 이상이 소요되었지만, Optimization Level을 변경한 후 1분 내외로 단축되었습니다. Optimization Setting을 변경할 때는 꼭 Debug 항목만 변경하고, Release 버전은 기존 설정을 유지하는 것이 좋습니다. 그래야 빌드 과정에서의 버그를 막을 수 있기 때문이죠. 만약 이 설정으로 개발하던 도중 소스가 충돌되면 Command+Shift+K 단축키를 눌러 소스를 한 번 클린하고, 재빌드하세요. 충돌이 사라지는 경우가 많습니다. 빠른 빌드를 위해 종종 감수해야 하는 부분이기도 합니다. 3)Derived Data빌드가 자꾸 안되고 꼬일 때는 Derived Data 폴더를 삭제 해 보세요. Derived Data 폴더는 XCode > File > Project Settings(Workspace Settings) 항목에서 ‘Derived Data’ 항목 아래의 폴더 경로에서 접근할 수 있습니다. Derived Data 접근 경로Derived Data 폴더를 삭제하면 거짓말처럼 빌드 오류가 사라지는 기적을 만날 수 있습니다. 4)CocoaPods‘바퀴를 두 번 발명할 필요는 없다’는 격언이 있습니다. 이것을 개발에 적용하면 ‘잘 만들어진 라이브러리를 사용하라’ 정도가 되겠습니다. 개발자의 개발 시간을 현저하게 단축시키는 오픈소스 라이브러리. 이것들을 간편하게 사용하는 방식이 iOS에도 존재하는데, 바로 CocoaPods입니다. 프로젝트 Root 폴더에 Podfile을 생성하고, 원하는 오픈소스 라이브러리들을 명시한 후에 ‘pod install’ 명령어를 입력해주면….CocoaPods오픈소스 라이브러리가 설치되었습니다. 귀찮은 소스 다운로드와 임포트 과정을 거치지 않아도 됩니다. CocoaPods 설치와 사용에 관한 글은 구글링으로 쉽게 찾을 수 있습니다. 꼭 사용하길 권합니다.Mac App Store에서의 XCode 평점XCode는 느리고 불편합니다. 숨겨진 편의기능도 많지만 고질적인 빌드 문제와 사용성 문제를 마주하면 높은 평점을 줄 수가 없습니다. 그런데, 저만 그렇게 생각하진 않더라고요.(위 스크린샷 참조) XCode의 사용법은 기회가 되면 따로 정리하겠습니다.11.어떤 경우에도 대응할 수 있는 화면 구성을 원한다면, AutoLayoutiOS를 사용하면서, 금융권이나 쇼핑 앱들을 사용하다 보면 이런 상황이 발생합니다. 금융권 앱. 화면에 꽉 차지 않는 레이아웃 혹은 비정상적으로 커진 글씨본래 iOS는 단일 디바이스를 지향하는 플랫폼이었습니다. 아이폰 시리즈도 해상도가 변하지 않았기 때문에, 디바이스 종류가 많은 안드로이드처럼 다양한 스크린 사이즈를 지원할 필요가 없었습니다. 하지만 이제는 iPhone SE, iPhone 8, iPhone 8 Plus의 해상도에 iPhone X의 해상도까지 더해지면서 그야말로 ‘해상도 춘추전국시대’가 되었습니다.이런 다양한 해상도를 모두 지원하는 레이아웃을 구성하려면, iOS에서는 AutoLayout을 사용해야 합니다. AutoLayout은 Xib Editor에서 AutoLayout을 활성화하는 방식으로 사용할 수 있습니다. 거기에 한 가지 덧붙이면 Layout Constraints라는 개념도 있습니다. 레이아웃에 조건을 주는 방식입니다. 예를 들어 ‘어떤 해상도에서든 이 컴포넌트는 왼쪽 끝으로부터 10Point의 여백을 가지도록 한다’ 라는 식이죠. AutoLayout, Layout Constraint이 Layout Constraint를 이용하면 짧은 시간 안에 다양한 해상도를 지원하는 레이아웃을 쉽게 만들 수 있습니다. 가히 AutoLayout의 꽃입니다.ConclusionXCode/iOS 개발과 관련된 팁은 대부분 구글링으로 찾을 수 있습니다. 다룰 내용이 많지만 초보 iOS개발자들이 당황할 수 있는 내용을 중심으로 글을 썼습니다. 소소한 이야기지만, 분명 도움을 받을 수 있을 겁니다.글이정환 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #iOS #개발기 #업무환경 #인사이트 #경험공유 
조회수 1601

2017 NDC 리뷰) 크립돈 퓨처 미디어와 하츠네미쿠

 이번글은 덕력이 솟구친다는!!!은 아니고요(진짜 아니에요), 혹시 "하츠네 미쿠"라는 캐릭터를 보신 적 있으신가요?하츠네 미쿠! 설마 처음보는 분들이 계신가요?? 출처: https://ec.crypton.co.jp/pages/prod/vocaloid하츠네 미쿠는 VOCALOID(보컬로이드)로서, 간단히 설명하면, 야마하에서 만든 음성 엔진입니다(자세한 내용은 링크를 확인!). 해당 엔진을 기반(자세히는 VOCALOID2인데... 아, 저는 잘 몰라요 진짜예요...)을 기반으로 크립톤 퓨처 미디어사가 아티스트를 만들고, 이를 지적 재산권(이하 IP라고 하겠습니다)으로 창출해 낸 사례입니다! 해당 세션은 이 보컬로이드가 성공할 수 있게 된, 창작자들에게 프로그램 번들 시디를 팔던, 크립톤 퓨처 미디어사가 새로운 미디어와 아트의 중심에 설 수 있게 된 이유를 들을 수 있게 된 좋은 시간이었습니다. 앞으론 말이 매우 딱딱하니 이점 히해해 주세요~! 시작하겠습니다!씨디파는 회사가 인터넷 시대를 맞이하며 겪게 된 위기, 그리고 해결방안. 앞서 말씀드렸든, 크립톤 퓨처 미디어(이하 크립톤이라고 하겠습니다)는 창작자들을 위한 서비스(또는 프로그램)를 번들 또는 디스크 형식으로 판매하는 회사였습니다. 그리고 새로운 세대로 들어서면서, 해당 사업이 사양되고 있고(디스크 판매> 콘텐츠 다운로드의 변화), 특히 음악 제작 서비스의 경우, 작은 시장의 규모 때문에 비즈니스에 대한 한계를 느끼고, 새로운 사업 영역을 펼쳐나가기 위해 방향 모색하기 시작했다고 합니다. 그리고 크립톤이 생각할 수 있는 "자사가 가장 잘할 수 있는 것"을 생각해 보았을 때, "소리"라는 콘텐츠를 방점으로 서비스를 응용해 나가면서 스팩트럼을 넓히자!라는 생각을 했다고 합니다. 그래서 시작한 것이 바로, 보컬로이드!라는 것이었죠.보컬 합성 기술(보컬로이드) + IP의 도입은 처음부터 성공적이진 않았습니다. 처음 크립톤은 야마하의 보컬로이드 기술을 기반, Leon과 Lola라는 소프트웨어를 제작,  당사에서 유통을 시작했을 때에는, 타깃 유저를 잡는데 실패해 매출에 전혀 도움이 되지 않았다고 합니다(아래 사진을 보면 왠지 알 거 같...)첫 보컬로이드 레온과 로라입니다..... 음... 입술이 매력적 이네요.... 출처: http://vocaloid.wikia.com/wiki/Forever_(Zero-G_song) 그 이유는 해당 서비스를 사용할 것이라고 타게팅한 아티스트들의 경우, 목소리에 관해 리얼함을 추구하는 데, 해당 소프트웨어는 하드웨어로 조정하는 음과 음성들이 리얼함이 다소 떨어져 전혀 니즈가 없었던 것이죠.그래서 트립톤은"해당 서비스를 진짜 사용하는 유저들은 어떤 사람들 일까?"에 대한 고려를 기반으로,"메이코"라는 일본어로 노래하는 보컬로이드를 제작, 흥미를 끌 수 있도록 캐릭터를 모티브로 하는 커버 디자인 작업 시작(안드로이드 아니 보컬로이드 이니깐요!)이제는 버전 쓰리가 된 메이코! (출처:http://vocaloid.wikia.com/wiki/MEIKO) 첫 출시 당시, 거부감도 있었지만, 당시 KPI 목표인 500개를 훌쩍 넘어 3,000개의 판매 성공을 거뒀고, 성공의 요인은 패키징 디자인과 단순한 아티스트뿐만이 아닌, 다양한 콘텐츠에 관심을 가지는 다양한 유저들을 유저들을 이끌 수 있는 요소들이 있어서 라고 판단하였다고 합니다. (서비스를 사용할 것이다 라는 사용자의 경험에 대한 고려를 더 많이 한 포인트라고 생각되는 부분이지요!)메이코 이후 드디어 그분을 만들어 내는 것을 준비합니다.크립톤은 이때부터 정말로 사용자들이 무엇을 원하는가에 대한 생각을 많이 한 것 같다고 보이는 포인트입니다. 메이코의 등장 이후, "하츠네 미쿠"라는 캐릭터 산업으로 만들어 내는 것을 준비합니다. 그리고 해당 캐릭터를 하나의 "사업전략"으로 생각해 낸 이유는 메이코의 KPI달성도 있겠지만, "사람의 목소리와 극히 다른 목소리로 노래를 부르게 된다면, 이상하지 않을까?라는 부분을 오히려 역으로 기획, "인간이 아닌 다른 안드로이드가 하는 노래"라는 새로운 존재로서 IP를 만들어 버린 것이죠!또,  캐릭터를 기반으로 다양한 성격을 가질 수 있도록 "성우"라는 시스템을 집어넣어 "특별한 존재"라는 특징 성을 추가하였고, 기존의 보컬로이드는 "인간의 가수를 대체하는 것"이었으나, 하츠네 미쿠는 "안드로이드 가 부르는 진짜 보컬로이드"라는 접근을 통해 새로운 존재를 만들고, 메이코 디자인을 기반으로, "아이돌 라이즈 된 새로운 사이버 가수"를 만든 것이죠!아아.... 이제 고인이 되신 사이버 가수 아담... (http://beautinaru.tistory.com/196 또한, 해당 콘텐츠를 기반으로 음악을 만들었던 유저들에게 레트로 한 마크들을 집어넣어서 예전에는 이랬었지 라는 향수를 불러일으키고, 해당 콘텐츠를 기반으로 다시 작업을 할 동기를 줄 수 있도록 유저들의 의견을 듣고 반영하는 일들을 굉장히 많이 했다고 합니다!그리고 하츠네 미쿠의 진정한 아이덴티티를 생성합니다. 그것은 바로 Chain of Co-creation!!!하츠네 미쿠가 이렇게 성장할 수 있었던 이유는 저는 하나만 꼽으라 라고 한다면 이쁘잖아요! 가아니라... "확산 가능 여부"에 대한 많은 고려가 있었기에 가능했다고 생각합니다.인터넷 덕분에 음악 등을 만드는 사람들이 쉽게 업로드하고 공유할 수 있는 많은 플랫폼들이 생성되는 현실.덕이 많은 분들이 공유를 통해 자아실현을 하는 공감대를 형성할 수 있는 움직임이 확산.콘텐츠가 콘텐츠를 만들고 퍼져나가는 순기능적인 부분들이 늘어나는 현상들을 확인하고,2차 3차 저작물을 통한 확산> Chain of co-creation의 순선환 적인 기능들이 생겨나는 것이죠!! 그리고 그런 상황을 기반으로, 궁극적으론,모든 사람들이 제작자가 될 수 있는 현실 상황을 받아들이고,제작할 수는 있지만,  저작에 관련한 법률 등에서 막히는 상황을 막기 위해, 창작자들의 창작활동을 돕고, 실제 업로드된 콘텐츠를 기반으로, 실제 사업이 일어날 수 있는 방향으로 전개합니다!그리고 수익화를 통해서 창작자들이 창작활동 = 수익활동이 될 수 있도록 플랫폼 화를 추진한 것이죠!그래서 처음 하츠네 미쿠가 나온 2007년부터 10년이 지난 지금까지도 "보컬로이드"의 선두 주자로 전 세계적으로 콘서트를 다니며 성공적인 투어를 하고 있습니다.투어는 계속된다. (출처: http://mikuexpo.com/) 저는 하츠네 미쿠가 단지 덕후들의 승리라고 요만큼도 생각하지 않습니다.하츠네 미쿠를 성장시킬 수 있었던 건 "우리가 제공하는 서비스가 어떤 유저들에게 더 많은 강점이 있고, 해당 유저들은 어떤 행동을 통해서 자아를 성찰할 수 있을까? 그리고 해당 행동을 통해 유저가 얻는 궁극적인 이익들이 잇을까?"를 생각했던, 크립톤의 유저를 생각하는, 유저의 직접적인 경험을 서비스에 반영하려고 하는 강한 의지가 해당 서비스를 성공시킬 수 있게 한 요인이라고 생각해요. 그런 의미에서 저에겐 정말로 뜻깊고 즐거웠던 세션이었습니다!P.S.: 이제 슬슬 NDC2017 영상들이 올라오기 시작하네요! 관심 있는 분들은 https://ndc.nexon.com/main에서 확인해 보세요~오늘도 긴 글 읽어주셔서 감사합니다! 다소 글이 엉망진창이라도 이해해 주세요! #코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 1392

[어반베이스 피플] 홈디자이닝 AR앱 'Urbanbase AR' 개발자 인터뷰

어반베이스 AR을 사용하여 원하는 가구 및 가전제품을 미리 배치해볼 수 있다는 사실, 알고 계시죠? 최근 가구, 가전, 화장품, 의류 등 다양한 업계에서 AR을 활용해 고객들에게 새로운 경험을 제공하고 있으며 이러한 서비스들은 점점 증가하고 있습니다. 미래에는 AR을 활용한 쇼핑 플랫폼들이 점차 대중화 될 것이고, AR 쇼핑 플랫폼을 설계하는 전문가에 대한 수요도 늘어날 것으로 예상됩니다.서울산업진흥원은 미래 경쟁력 있는 신직업 40개를 선정했는데, 선정한 미래직업 중 'AR 쇼핑 플랫폼 설계자'가 포함되었고, '어반베이스 AR'의 담당 개발자 우석님이 인터뷰를 진행하게 되었습니다.홈디자이닝 AR앱 'Urbanbase AR'의 개발자Q. 일하면서 보람을 느끼는 순간은 언제인가요? 사람들은 작은 물건 하나를 구입할 때도 성능과 디자인 등을 꼼꼼히 살핍니다. 몇 번이나 구매를 망설이기도 하고요. 살아가는 집, 그 공간을 꾸미는 데는 얼 마나 많은 시간과 노력이 필요할까요? 가구와 인테리어 소품을 일일이 쇼핑하지 않고도 스마트폰 안에서 내가 원하는 상품들로 내 방을 미리 꾸며볼 수 있는 셀프인테리어 앱을 설계하는 것이 저의 일입니다. VR, AR 기술을 통해 가 구 배치, 벽지 교체, 인테리어 등을 미리 경험해보고 구매할 수 있기에, 시간과 비용은 줄어들고 만족도는 올라가게 됩니다. 제가 만든 가상의 공간이 누군가에게 편안하고 안락한 삶을 선사해주는 것을 볼 때 제 일에 보람을 느낍니다.Q. AR 쇼핑 플랫폼 설계자가 신직업으로서 가지는 경쟁력은 무엇일까요? 지금 이 순간에도 수많은 기업에서 무수히 많은 제품이 개발, 생산되고 있습 니다. 제품 정보나 장점을 소비자에게 보다 정확하게 전달해 반품율을 줄이 고 판매율을 높이는 것은 모든 기업이 바라는 점이죠. 그 대안이 될 수 있는 것이 AR 쇼핑인 만큼 AR 쇼핑 플랫폼 설계자에 대한 니즈는 빠르게 증가할 것입니다. AR은 커머스뿐 아니라 건설, 교통, 의료, 부동산, 인테리어 등 현대 산업 전체에 적용 가능한 기술이죠. 이는 AR 쇼핑 플랫폼 설계자로 쌓은 경험과 경력을 바탕으로 다양한 분야에 진출할 수 있다는 의미이기도 합니다. Q. AR 쇼핑 플랫폼 설계자에게 가장 필요한 자질은 무엇이라고 생각하시나요? AR 쇼핑 플랫폼 설계자는 크게 본다면 프로그래머 직군에 속합니다. 그렇기에 컴퓨터공학에 대한 소양이나 정보처리기사 자격증 등을 미리 준비해 두는 것이 좋습니다. 또한 AR 플랫폼은 주로 모바일 환경에서 제공되기 때문에 안드로이드 혹은 iOS 플랫폼에 대한 이해가 필수적입니다. 여기에 3D 그래픽에 대한 개념을 알고 있으면 업무를 수행하는 데 큰 도움이 됩니다. AR 쇼핑 플랫폼 설계자는 많은 가능성을 가진 유망 직종이지만, 이제 막 출 발한 분야이기에 상대적으로 참고할 수 있는 레퍼런스가 많지 않습니다. 그렇기 때문에 누군가가 만들어 놓은 길을 따라가기보다는 치열하게 연구하고 도전하는 자세가 필요합니다. Q. AR 쇼핑 플랫폼 설계자를 꿈꾸는 이들에게 조언 한마디 부탁드립니다.AR 기술을 습득하고 활용하기 위해서는 여러 가지 기본 지식들이 뒷받침돼야 합니다. AR 기술을 온라인에 접목하려면 쇼핑 플랫폼은 물론 관련 상품에 대한 지식도 필수적이고요. 이러한 지식들은 하루아침에 습득할 수 없는 것들입니다. 그렇기에 너무 조급해하지 말고 하나씩 내 것으로 만드는 자세 가 중요합니다. 시공간에 구애받지 않는 ‘가상의 세계’를 만들어내는 일은 분명 신나는 일입니다. 실패를 두려워하지 않는 개척자 마인드를 가진 사람이라면 충분히 즐기면서 일할 수 있으니, 꼭 도전해보세요.사진 출처 및 인터뷰 전문https://blog.naver.com/urbanbaseinc 
조회수 2810

리디북스 웹뷰어의 이어보기를 개발하며

최근 리디북스에서는 판타지 연재물을 웹에서 바로 볼 수 있는 기능을 새롭게 선보였습니다.기존에는 별도의 앱을 설치하고 다운로드하는 과정을 거쳐야 했기에 연재물을 보는 사용성이 좋지 않았습니다만, 브라우저에서 바로 볼 수 있는 “웹뷰어” 기능을 제공함으로써 사용성을 높일 수 있었습니다.그리고 여기에 사용성을 더하기 위해 추가된 것이 이어보기 기능입니다. 짧아도 100화 이상, 길게는 1000화가 넘는 연재물에서 다음 화로의 매끄러운 연결은 매우 중요합니다. 혹은 잠시 읽기를 중단했다가 다시 돌아왔을 때, 어디까지 보고 있었는지를 빠르게 알려준다면 호흡을 이어서 작품에 더욱 몰입할 수 있을 것입니다.이어보기가 구현된 모습리디북스에 로그인되어 있다면, 이곳에서 확인하실 수 있습니다.이번 글은 이어보기 기능에 대한 개발 후기입니다. 요구 사항에 따라 여러 저장소 솔루션을 비교해 보았으며 최종적으로 Couchbase를 선택한 이유와 간단한 벤치마크 결과, 그리고 겪었던 문제를 공유합니다.요구 사항기획된 내용을 요약하니 아래와 같습니다.연재물의 가장 마지막에 읽은 화를 알 수 있다.보았던 모든 연재물에서 가장 마지막에 읽은 연재물을 알 수 있다.사용자가 본 모든 연재물 목록을 확인할 수 있다.이를 개발자 용어로 다시 풀어보면 아래와 같습니다.연재물을 읽을 때마다 연재물 ID와 화(episode) 정보를 기록한다.보았던 연재물을 최신순으로 정렬하여 가져온다.선택된 연재물의 마지막으로 읽은 화를 가져온다.목록에서 특정 연재물을 삭제한다.이어보기는 가장 마지막에 읽은 연재물을 기억하기 위해 작품을 열 때마다 해당 정보를 기록해야 합니다. 그런데 수십 화를 연달아서 보는 연재물의 특성상 내가 어디까지 읽었는지를 조회하는 것(read)보다 내가 읽은 연재물을 기록하는 것(write)이 더 많을 것으로 판단했습니다. 즉, 읽기보다 쓰기가 더 많을 것으로 예상했습니다.NoSQL을 쓰자대부분의 연산이 쓰기(write)와 관련된 이상, 어떤 저장공간을 사용할 것인지가 주된 관심사였습니다.특히 RDBMS와 NoSQL 사이에서 어떤 것을 사용할지 많은 고민과 테스트를 했고, 결국 아래와 같은 이유로 NoSQL을 사용하는 것이 적합하다고 판단했습니다.현재 사용 중인 MariaDB를 그대로 사용한다면 마스터에 부담을 줄 수 있다.별도로 MariaDB를 구성하더라도 운영 및 쓰기 분산하기가 여전히 어렵다.반면 NoSQL은 RDBMS 대비 확장(Scale out)이 간편하므로 운영에 대한 부담이 적다.단순 Key-Value 보관 용도면 충분하다.이어보기 데이터는 독립적인 성격을 가지고 있어서 다른 사용자 데이터와 JOIN을 할 필요가 없다.이어보기 데이터는 크리티컬한 트랜잭션이 필요하지 않다.MongoDB vs. Couchbase데이터를 영속적으로 유지해야 한다는 요구 사항을 충족하기 위해, Redis 등의 메모리만 사용하는 NoSQL은 제외했습니다. 물론 디스크에 기록할 수 있지만, 성능이 급감하기 때문에 실용적이지 못 합니다. 또한, 메모리 사이즈에 기반을 두기 때문에 Scale up 비용이 크고, 서비스 확장시 Scale out 빈도가 높습니다.그래서 MongoDB와 Couchbase를 비교 대상으로 했습니다. 둘 다 도큐먼트 기반의 NoSQL이고 확장이 용이합니다. 과거에는 MongoDB가 Write lock 사용에 있어서 문제점이 있었지만, 최근 버전에서는 문제가 되지 않습니다.[1] 둘 다 기업용 서비스 및 충분한 부가 기능들을 제공하므로 선택하기 어려웠지만, 최종적으로 아래와 같은 이유로 Couchbase(CE)를 선택했습니다.1. 이미 사내에서 다른 서비스에 사용되고 있습니다.가장 중요한 요인이었습니다. 더 좋은 솔루션이 있더라도 어디까지나 서버 스택을 늘리는 것 이상의 효용이 있는지를 따져보아야 합니다. 이미 사용하고 있는 솔루션이 있다면, 검증이 되었을 뿐만 아니라 개발 및 운영 경험도 활용할 수 있습니다.2. 이어보기는 복잡한 쿼리(Query)가 필요 없습니다.이어보기에서 사용할 쿼리는 간단하기 때문에 Couchbase의 뷰(View)만으로 충분했습니다.Couchbase, 실제 성능은 어떨까?테스트를 하기 전 우리가 어떤 식으로 사용할 것인지 정리해야 합니다. 애플리케이션 액세스 패턴이나 동시성 문제, 데이터 구조화 등을 파악하고 그에 맞는 테스트를 진행해야 합니다. 이번 이어보기는 쓰기 연산이 보다 많기 때문에 이로 인한 뷰의 인덱싱(Indexing)에 초점을 맞추고 테스트를 진행했습니다.성능을 위협하는 요소들View IndexingCouchbase는 MapReduce를 이용하여 뷰를 제공합니다. MapReduce는 일반적으로 리소스를 많이 소모하는 동작입니다. 그래서 Couchbase는 버킷의 새로 갱신된 데이터만 인덱싱하는 Incremental MapReduce라는 기법을 적용해서 리소스 소모를 줄였다고 합니다.[2] 하지만 해당 작업으로 인한 부하는 여전히 발생합니다.Auto CompactionCouchbase는 데이터와 인덱스를 디스크에 데이터를 저장할 때 파일에 추가하기(Append) 모드로만 쓰기를 수행합니다.[3] 그리고 오래되고 불필요한 데이터들은 추후 한꺼번에 정리하는데, 이는 디스크 쓰기 성능을 최대화하기 위함입니다.그런데 이렇게 추가만 하게 되면 오래된 정보들은 파일의 앞에 쌓이게 됩니다. 그리고 사용하지 않게 된 데이터도 남아있습니다. 이를 주기적으로 정리해서 최적화하는 작업을 Auto Compaction이라고 합니다. 뷰의 인덱스는 디스크에 존재하기 때문에 디스크 작업이 있으면 인덱싱에 영향을 미치게 됩니다.성능 테스트Couchbase는 기본적으로 5,000ms마다 Index를 업데이트합니다.[2] 그리고 데이터를 비동기적으로 응답합니다. 비동기는 응답속도를 빠르게 하지만, 데이터 불일치가 발생할 수 있습니다. 데이터 불일치가 신경 쓰이고 이 시간이 길다고 생각되면, stale 옵션을 지정해서 뷰의 인덱스를 업데이트할 수 있습니다.이어보기는 뷰가 간단하기 때문에 응답시간에 큰 문제가 없을 것으로 예상하고 stale 옵션을 꺼두었습니다. 이 옵션은 뷰를 조회했을 때 버킷의 변경사항에 따라 뷰를 인덱싱하고 데이터를 응답합니다. 하지만 예상한 것과 같이 실제로도 응답시간이 짧은지 확인할 필요가 있습니다. 그래서 다음과 같이 테스트를 진행했습니다.테스트 환경은 아래와 같이 2-tier로 준비하고 요청을 늘려가면서 RPS를 측정했습니다.서버 구성OS: Ubuntu 14.04Application: Couchbase Server (CE) 3.1.3클라이언트 구성클라이언트 1개에서 50개의 세션으로 요청10만 사용자 가정책은 1만개의 책중 랜덤으로 선택됨요청의 70%는 책 읽기(Bucket Write)요청의 30%는 연재물의 마지막에 읽은 책 가져오기(View Read)그래프 분석성능 테스트 주요 지표RPS : Response Per SecondSP : Saturation PointBuckle zone : 시스템 과부하로 인해 내부 자원이 서로 경쟁상태나 적체 상태가 심해지기 때문에 최대 처리량보다 더 떨어지는 경우가 발생함성능테스트 결과그래프를 보면 요청이 늘어남에 따라 RPS가 선형으로 증가하지만, SP인 8,000 RPS에 도달하고 나서 Buckle zone에서 7,000 RPS로 수렴하고 있습니다. 물론 1개의 클라이언트에서 세션을 생성해서 테스트를 진행했기 때문에 서버의 성능 부족이 아닌 클라이언트의 병목 현상이 원인일 수 있습니다. 또한 JMeter나 다른 부하 테스트 툴을 사용하지 않고 간략하게 만든 테스트 툴을 사용하였기 때문에 수치가 부정확할 수 있습니다. 그러나 어디에서 병목이 있었든 현재 이 이상의 성능이 필요하지 않기 때문에 테스트 결과에 만족할 수 있었습니다.이어보기 배포 후모바일 브라우저 캐시 문제이어보기 기능을 배포하자마자 당일 저녁 이슈 하나를 접수했습니다. 아이패드와 PC를 번갈아 이용할 경우 이어보기 데이터가 맞지 않다는 것이었습니다.데이터를 쌓을 때 모든 이력을 기록하지는 않았지만, 다행히도 Couchbase에 이용기기와 시간은 기록하였기 때문에 이를 바탕으로 디버깅을 할 수 있었습니다. (서비스 초기라 할지라도 최대한 많은 이력을 남기는 것이 중요함을 다시 느꼈습니다)원인은 아이패드의 멀티태스킹으로 인한 캐시 소멸이었습니다. 아이패드 브라우저의 캐시가 소멸되면서 마지막으로 열어두었던 페이지가 강제적으로 리로딩되었고, 이때 의도치 않게 마지막 위치 정보가 덮어씌워진 것입니다.이 문제는 기술적으로 해결이 쉽지 않아 결국 기획을 수정하게 되었습니다. 사용자가 해당 책을 읽었다고 판단하는 기준이 “페이지를 열어본 즉시”였다면, 이를 “페이지를 열고 수 초 이상을 유지”하는 것으로 기준을 변경하였습니다. 물론 근본적인 해결책은 아니었지만, 실제 사용에는 지장이 없는 합리적인 해결책이라고 생각합니다.Key 구조의 변경 및 동시성 문제Couchbase는 높은 성능을 위해 메타데이터(Key + @)를 모두 메모리에 적재하는 특징이 있어서, Document 하나가 평균 350Byte를 차지하고 있었습니다. 따라서 현재 상태로 1000만개의 데이터를 저장할 경우 최소 3.5G의 메모리를, 2개의 사본(Replica)를 유지할 경우 약 10.5G의 메모리를 사용하게 될 것으로 예상되었고 이는 큰 부담으로 다가왔습니다.처음에는 단순히 “사용자ID_연재물ID” 형태의 Key를 사용하였지만, 보다 빠르게 증가할 것으로 예상되는 것은 사용자보다 연재물 이었으므로 아래와 같이 Key값을 변경하여 메모리 사용량을 크게 줄였습니다.// U_id : S_id 조합을 사용하면 Key가 엄청 많아진다. // 그래서 사용자당 Key를 100개로 제한하도록 한다. Count = 100 Key = '사용자ID' + ('연재물ID' % Count) 그런데 이렇게 Key 구조를 변경하였더니, 간단한 업데이트 동작임에도 불구하고 정상적으로 수행되지 않는 경우가 빈번하게 발생하였습니다. 이유는 낙관적 동시성(Optimistic concurrency) 모델의 특징 때문이었는데, Couchbase는 명시적인 잠금 이외에도 “Check and Set(CAS)”이라는 기능을 제공하고 있었습니다.공식 문서의 예제를 참고하여 아래와 같이 로직을 수정한 뒤로는 다행히도 동시성 문제가 아직까지 발생하지 않고 있습니다.boolean updateUsingCas(key, value) {  for (tryCount = 0; tryCount < MAX>    orgValue, cas = getValueAndCas(key)           // Update the original value.     // newValue = ... if setValueWithCas(key, newValue, cas)      return SUCCESS sleep(0.1) // 부하를 줄이기 위해  }  return FAIL } 맺으며동작하는 서비스에 새로운 기능을 추가한다는 것은 어려운 일입니다. 특히 새로운 데이터 스토리지를 필요로 하는 일이라면 더더욱 어렵다고 생각합니다. 그리고 그럴 때일수록 설계에 많은 시간을 들여야 한다는 것을 느꼈습니다. 설계 초기에는 RDBMS의 샤딩까지 고려하였지만, 요구 사항을 구체화할수록 단순 Key-Value로도 같은 문제를 해결할 수 있음을 깨달았기 때문입니다.또한, 서비스 개발에 있어서 어려운 문제를 마주했을 때 기술적으로만 접근할 것이 아니라 고객이 정말 원하는 것이 무엇인지를 고민하여 기획적으로 해결하는 능력도 중요하다는 것을 실감하였습니다.마지막으로 Couchbase는 현재로서도 꽤 좋고 앞으로도 많은 발전이 기대되는 NoSQL입니다. 도입을 고민하시던 분들께 조금이라도 도움이 되었기를 바랍니다.참고자료[1] MongoDB - Concurrency[2] Couchbase - Views Operations[3] Couchbase - File write#리디북스 #개발 #개발자 #서버개발 #서비스개발 #고객중심 #기능개발 #Couchbase #인사이트 #개발후기
조회수 3241

Attention is all you need paper 뽀개기

이번 포스팅에서는 포자랩스에서 핵심적으로 쓰고 있는 모델인 transformer의 논문을 요약하면서 추가적인 기법들도 설명드리겠습니다.Why?Long-term dependency problemsequence data를 처리하기 위해 이전까지 많이 쓰이던 model은 recurrent model이었습니다. recurrent model은 t번째에 대한 output을 만들기 위해, t번째 input과 t-1번째 hidden state를 이용했습니다. 이렇게 한다면 자연스럽게 문장의 순차적인 특성이 유지됩니다. 문장을 쓸 때 뒤의 단어부터 쓰지 않고 처음부터 차례차례 쓰는 것과 마찬가지인것입니다.하지만 recurrent model의 경우 많은 개선점이 있었음에도 long-term dependency에 취약하다는 단점이 있었습니다. 예를 들어, “저는 언어학을 좋아하고, 인공지능중에서도 딥러닝을 배우고 있고 자연어 처리에 관심이 많습니다.”라는 문장을 만드는 게 model의 task라고 해봅시다. 이때 ‘자연어’라는 단어를 만드는데 ‘언어학’이라는 단어는 중요한 단서입니다.그러나, 두 단어 사이의 거리가 가깝지 않으므로 model은 앞의 ‘언어학’이라는 단어를 이용해 자연어’라는 단어를 만들지 못하고, 언어학 보다 가까운 단어인 ‘딥러닝’을 보고 ‘이미지’를 만들 수도 있는 거죠. 이처럼, 어떤 정보와 다른 정보 사이의 거리가 멀 때 해당 정보를 이용하지 못하는 것이 long-term dependency problem입니다.recurrent model은 순차적인 특성이 유지되는 뛰어난 장점이 있었음에도, long-term dependency problem이라는 단점을 가지고 있었습니다.이와 달리 transformer는 recurrence를 사용하지 않고 대신 attention mechanism만을 사용해 input과 output의 dependency를 포착해냈습니다.Parallelizationrecurrent model은 학습 시, t번째 hidden state를 얻기 위해서 t-1번째 hidden state가 필요했습니다. 즉, 순서대로 계산될 필요가 있었습니다. 그래서 병렬 처리를 할 수 없었고 계산 속도가 느렸습니다.하지만 transformer에서는 학습 시 encoder에서는 각각의 position에 대해, 즉 각각의 단어에 대해 attention을 해주기만 하고, decoder에서는 masking 기법을 이용해 병렬 처리가 가능하게 됩니다. (masking이 어떤 것인지는 이후에 설명해 드리겠습니다)Model ArchitectureEncoder and Decoder structureencoder는 input sequence (x1,...,xn)<math>(x1,...,xn)</math>에 대해 다른 representation인 z=(z1,...,zn)<math>z=(z1,...,zn)</math>으로 바꿔줍니다.decoder는 z를 받아, output sequence (y1,...,yn)<math>(y1,...,yn)</math>를 하나씩 만들어냅니다.각각의 step에서 다음 symbol을 만들 때 이전에 만들어진 output(symbol)을 이용합니다. 예를 들어, “저는 사람입니다.”라는 문장에서 ‘사람입니다’를 만들 때, ‘저는’이라는 symbol을 이용하는 거죠. 이런 특성을 auto-regressive 하다고 합니다.Encoder and Decoder stacksEncoderN개의 동일한 layer로 구성돼 있습니다. input $x$가 첫 번째 layer에 들어가게 되고, layer(x)<math>layer(x)</math>가 다시 layer에 들어가는 식입니다.그리고 각각의 layer는 두 개의 sub-layer, multi-head self-attention mechanism과 position-wise fully connected feed-forward network를 가지고 있습니다.이때 두 개의 sub-layer에 residual connection을 이용합니다. residual connection은 input을 output으로 그대로 전달하는 것을 말합니다. 이때 sub-layer의 output dimension을 embedding dimension과 맞춰줍니다. x+Sublayer(x)<math>x+Sublayer(x)</math>를 하기 위해서, 즉 residual connection을 하기 위해서는 두 값의 차원을 맞춰줄 필요가 있습니다. 그 후에 layer normalization을 적용합니다.Decoder역시 N개의 동일한 layer로 이루어져 있습니다.encoder와 달리 encoder의 결과에 multi-head attention을 수행할 sub-layer를 추가합니다.마찬가지로 sub-layer에 residual connection을 사용한 뒤, layer normalization을 해줍니다.decoder에서는 encoder와 달리 순차적으로 결과를 만들어내야 하기 때문에, self-attention을 변형합니다. 바로 masking을 해주는 것이죠. masking을 통해, position i<math>i</math> 보다 이후에 있는 position에 attention을 주지 못하게 합니다. 즉, position i<math>i</math>에 대한 예측은 미리 알고 있는 output들에만 의존을 하는 것입니다.위의 예시를 보면, a를 예측할 때는 a이후에 있는 b,c에는 attention이 주어지지 않는 것입니다. 그리고 b를 예측할 때는 b이전에 있는 a만 attention이 주어질 수 있고 이후에 있는 c는 attention이 주어지지 않는 것이죠.Embeddings and Softmaxembedding 값을 고정시키지 않고, 학습을 하면서 embedding값이 변경되는 learned embedding을 사용했습니다. 이때 input과 output은 같은 embedding layer를 사용합니다.또한 decoder output을 다음 token의 확률로 바꾸기 위해 learned linear transformation과 softmax function을 사용했습니다. learned linear transformation을 사용했다는 것은 decoder output에 weight matrix W<math>W</math>를 곱해주는데, 이때 W<math>W</math>가 학습된다는 것입니다.Attentionattention은 단어의 의미처럼 특정 정보에 좀 더 주의를 기울이는 것입니다.예를 들어 model이 수행해야 하는 task가 번역이라고 해봅시다. source는 영어이고 target은 한국어입니다. “Hi, my name is poza.”라는 문장과 대응되는 “안녕, 내 이름은 포자야.”라는 문장이 있습니다. model이 이름은이라는 token을 decode할 때, source에서 가장 중요한 것은 name입니다.그렇다면, source의 모든 token이 비슷한 중요도를 갖기 보다는 name이 더 큰 중요도를 가지면 되겠죠. 이때, 더 큰 중요도를 갖게 만드는 방법이 바로 attention입니다.Scaled Dot-Product Attention해당 논문의 attention을 Scaled Dot-Product Attention이라고 부릅니다. 수식을 살펴보면 이렇게 부르는 이유를 알 수 있습니다.Attention(Q,K,V)=softmax(QKT√dk)V<math>Attention(Q,K,V)=softmax(QKTdk)V</math>먼저 input은 dk<math>dk</math> dimension의 query와 key들, dv<math>dv</math> dimension의 value들로 이루어져 있습니다.이때 모든 query와 key에 대한 dot-product를 계산하고 각각을 √dk<math>dk</math>로 나누어줍니다. dot-product를 하고 √dk<math>dk</math>로 scaling을 해주기 때문에 Scaled Dot-Product Attention인 것입니다. 그리고 여기에 softmax를 적용해 value들에 대한 weights를 얻어냅니다.key와 value는 attention이 이루어지는 위치에 상관없이 같은 값을 갖게 됩니다. 이때 query와 key에 대한 dot-product를 계산하면 각각의 query와 key 사이의 유사도를 구할 수 있게 됩니다. 흔히 들어본 cosine similarity는 dot-product에서 vector의 magnitude로 나눈 것입니다. √dk<math>dk</math>로 scaling을 해주는 이유는 dot-products의 값이 커질수록 softmax 함수에서 기울기의 변화가 거의 없는 부분으로 가기 때문입니다.softmax를 거친 값을 value에 곱해준다면, query와 유사한 value일수록, 즉 중요한 value일수록 더 높은 값을 가지게 됩니다. 중요한 정보에 더 관심을 둔다는 attention의 원리에 알맞은 것입니다.Multi-Head Attention위의 그림을 수식으로 나타내면 다음과 같습니다.MultiHead(Q,K,V)=Concat(head1,...,headh)WO<math>MultiHead(Q,K,V)=Concat(head1,...,headh)WO</math>where headi=Attention(QWQi,KWKi,VWVi)dmodel<math>dmodel</math> dimension의 key, value, query들로 하나의 attention을 수행하는 대신 key, value, query들에 각각 다른 학습된 linear projection을 h번 수행하는 게 더 좋다고 합니다. 즉, 동일한 Q,K,V<math>Q,K,V</math>에 각각 다른 weight matrix W<math>W</math>를 곱해주는 것이죠. 이때 parameter matrix는 WQi∈Rdmodelxdk,WKi∈Rdmodelxdk,WVi∈Rdmodelxdv,WOi∈Rhdvxdmodel<math>WiQ∈Rdmodelxdk,WiK∈Rdmodelxdk,WiV∈Rdmodelxdv,WiO∈Rhdvxdmodel</math>입니다.순서대로 query, key, value, output에 대한 parameter matrix입니다. projection이라고 하는 이유는 각각의 값들이 parameter matrix와 곱해졌을 때 dk,dv,dmodel<math>dk,dv,dmodel</math>차원으로 project되기 때문입니다. 논문에서는 dk=dv=dmodel/h<math>dk=dv=dmodel/h</math>를 사용했는데 꼭 dk<math>dk</math>와 dv<math>dv</math>가 같을 필요는 없습니다.이렇게 project된 key, value, query들은 병렬적으로 attention function을 거쳐 dv<math>dv</math>dimension output 값으로 나오게 됩니다.그 다음 여러 개의 head<math>head</math>를 concatenate하고 다시 projection을 수행합니다. 그래서 최종적인 dmodel<math>dmodel</math> dimension output 값이 나오게 되는거죠.각각의 과정에서 dimension을 표현하면 아래와 같습니다.*dQ,dK,dV<math>dQ,dK,dV</math>는 각각 query, key, value 개수Self-Attentionencoder self-attention layerkey, value, query들은 모두 encoder의 이전 layer의 output에서 옵니다. 따라서 이전 layer의 모든 position에 attention을 줄 수 있습니다. 만약 첫번째 layer라면 positional encoding이 더해진 input embedding이 됩니다.decoder self-attention layerencoder와 비슷하게 decoder에서도 self-attention을 줄 수 있습니다. 하지만 i<math>i</math>번째 output을 다시 i+1<math>i+1</math>번째 input으로 사용하는 auto-regressive한 특성을 유지하기 위해 , masking out된 scaled dot-product attention을 적용했습니다.masking out이 됐다는 것은 i<math>i</math>번째 position에 대한 attention을 얻을 때, i<math>i</math>번째 이후에 있는 모든 position은 Attention(Q,K,V)=softmax(QKT√dk)V<math>Attention(Q,K,V)=softmax(QKTdk)V</math>에서 softmax의 input 값을 −∞<math>−∞</math>로 설정한 것입니다. 이렇게 한다면, i<math>i</math>번째 이후에 있는 position에 attention을 주는 경우가 없겠죠.Encoder-Decoder Attention Layerquery들은 이전 decoder layer에서 오고 key와 value들은 encoder의 output에서 오게 됩니다. 그래서 decoder의 모든 position에서 input sequence 즉, encoder output의 모든 position에 attention을 줄 수 있게 됩니다.query가 decoder layer의 output인 이유는 query라는 것이 조건에 해당하기 때문입니다. 좀 더 풀어서 설명하면, ‘지금 decoder에서 이런 값이 나왔는데 무엇이 output이 돼야 할까?’가 query인 것이죠.이때 query는 이미 이전 layer에서 masking out됐으므로, i번째 position까지만 attention을 얻게 됩니다.이 같은 과정은 sequence-to-sequence의 전형적인 encoder-decoder mechanisms를 따라한 것입니다.*모든 position에서 attention을 줄 수 있다는 게 이해가 안되면 링크를 참고하시기 바랍니다.Position-wise Feed-Forward Networksencoder와 decoder의 각각의 layer는 아래와 같은 fully connected feed-forward network를 포함하고 있습니다.position 마다, 즉 개별 단어마다 적용되기 때문에 position-wise입니다. network는 두 번의 linear transformation과 activation function ReLU로 이루어져 있습니다.FFN(x)=max(0,xW1+b1)W2+b2x<math>x</math>에 linear transformation을 적용한 뒤, ReLU(max(0,z))<math>ReLU(max(0,z))</math>를 거쳐 다시 한번 linear transformation을 적용합니다.이때 각각의 position마다 같은 parameter W,b<math>W,b</math>를 사용하지만, layer가 달라지면 다른 parameter를 사용합니다.kernel size가 1이고 channel이 layer인 convolution을 두 번 수행한 것으로도 위 과정을 이해할 수 있습니다.Positional Encodingtransfomer는 recurrence도 아니고 convolution도 아니기 때문에, 단어의sequence를 이용하기 위해서는 단어의 position에 대한 정보를 추가해줄 필요가 있었습니다.그래서 encoder와 decoder의 input embedding에 positional encoding을 더해줬습니다.positional encoding은 dmodel<math>dmodel</math>(embedding 차원)과 같은 차원을 갖기 때문에 positional encoding vector와 embedding vector는 더해질 수 있습니다.논문에서는 다른 *frequency를 가지는 sine과 cosine 함수를 이용했습니다.*주어진 구간내에서 완료되는 cycle의 개수PE(pos,2i)=sin(pos/100002i/dmodel)<math>PE(pos,2i)=sin(pos/100002i/dmodel)</math>PE(pos,2i+1)=cos(pos/100002i/dmodel)<math>PE(pos,2i+1)=cos(pos/100002i/dmodel)</math>pos<math>pos</math>는 position ,i<math>i</math>는 dimension 이고 주기가 100002i/dmodel⋅2π<math>100002i/dmodel⋅2π</math>인 삼각 함수입니다. 즉, pos<math>pos</math>는 sequence에서 단어의 위치이고 해당 단어는 i<math>i</math>에 0부터 dmodel2<math>dmodel2</math>까지를 대입해 dmodel<math>dmodel</math>차원의 positional encoding vector를 얻게 됩니다. k=2i+1<math>k=2i+1</math>일 때는 cosine 함수를, k=2i<math>k=2i</math>일 때는 sine 함수를 이용합니다. 이렇게 positional encoding vector를 pos<math>pos</math>마다 구한다면 비록 같은 column이라고 할지라도 pos<math>pos</math>가 다르다면 다른 값을 가지게 됩니다. 즉, pos<math>pos</math>마다 다른 pos<math>pos</math>와 구분되는 positional encoding 값을 얻게 되는 것입니다.PEpos=[cos(pos/1),sin(pos/100002/dmodel),cos(pos/10000)2/dmodel,...,sin(pos/10000)]<math>PEpos=[cos(pos/1),sin(pos/100002/dmodel),cos(pos/10000)2/dmodel,...,sin(pos/10000)]</math>이때 PEpos+k<math>PEpos+k</math>는 PEpos<math>PEpos</math>의 linear function으로 나타낼 수 있습니다. 표기를 간단히 하기 위해 c=100002idmodel<math>c=100002idmodel</math>라고 해봅시다. sin(a+b)=sin(a)cos(b)+cos(a)sin(b)<math>sin(a+b)=sin(a)cos(b)+cos(a)sin(b)</math>이고 cos(a+b)=cos(a)cos(b)−sin(a)sin(b)<math>cos(a+b)=cos(a)cos(b)−sin(a)sin(b)</math> 이므로 다음이 성립합니다.PE(pos,2i)=sin(posc)<math>PE(pos,2i)=sin(posc)</math>PE(pos,2i+1)=cos(posc)<math>PE(pos,2i+1)=cos(posc)</math>PE(pos+k,2i)=sin(pos+kc)=sin(posc)cos(kc)+cos(posc)sin(kc)=PE(pos,2i)cos(kc)+cos(posc)sin(kc)<math>PE(pos+k,2i)=sin(pos+kc)=sin(posc)cos(kc)+cos(posc)sin(kc)=PE(pos,2i)cos(kc)+cos(posc)sin(kc)</math>PE(pos+k,2i+1)=cos(pos+kc)=cos(posc)cos(kc)−sin(posc)sin(kc)=PE(pos,2i+1)cos(kc)−sin(posc)sin(kc)<math>PE(pos+k,2i+1)=cos(pos+kc)=cos(posc)cos(kc)−sin(posc)sin(kc)=PE(pos,2i+1)cos(kc)−sin(posc)sin(kc)</math>이런 성질 때문에 model이 relative position에 의해 attention하는 것을 더 쉽게 배울 수 있습니다.논문에서는 학습된 positional embedding 대신 sinusoidal version을 선택했습니다. 만약 학습된 positional embedding을 사용할 경우 training보다 더 긴 sequence가 inference시에 입력으로 들어온다면 문제가 되지만 sinusoidal의 경우 constant하기 때문에 문제가 되지 않습니다. 그냥 좀 더 많은 값을 계산하기만 하면 되는거죠.Trainingtraining에 사용된 기법들을 알아보겠습니다.Optimizer많이 쓰이는 Adam optimizer를 사용했습니다.특이한 점은 learning rate를 training동안 고정시키지 않고 다음 식에 따라 변화시켰다는 것입니다.lrate=d−0.5model⋅min(step_num−0.5,step_num⋅warmup_steps−1.5)warmup_step<math>warmup_step</math>까지는 linear하게 learning rate를 증가시키다가, warmup_step<math>warmup_step</math> 이후에는 step_num<math>step_num</math>의 inverse square root에 비례하도록 감소시킵니다.이렇게 하는 이유는 처음에는 학습이 잘 되지 않은 상태이므로 learning rate를 빠르게 증가시켜 변화를 크게 주다가, 학습이 꽤 됐을 시점에 learning rate를 천천히 감소시켜 변화를 작게 주기 위해서입니다.RegularizationResidual ConnectionIdentity Mappings in Deep Residual Networks라는 논문에서 제시된 방법이고, 아래의 수식이 residual connection을 나타낸 것입니다.yl=h(xl)+F(xl,Wl)<math>yl=h(xl)+F(xl,Wl)</math>xl+1=f(yl)<math>xl+1=f(yl)</math>이때 h(xl)=xl<math>h(xl)=xl</math>입니다. 논문 제목에서 나온 것처럼 identity mapping을 해주는 것이죠.특정한 위치에서의 xL<math>xL</math>을 다음과 같이 xl<math>xl</math>과 residual 함수의 합으로 표시할 수 있습니다.x2=x1+F(x1,W1)<math>x2=x1+F(x1,W1)</math>x3=x2+F(x2,W2)=x1+F(x1,W1)+F(x2,W2)<math>x3=x2+F(x2,W2)=x1+F(x1,W1)+F(x2,W2)</math>xL=xl+L−1∑i=1F(xi,Wi)<math>xL=xl+∑i=1L−1F(xi,Wi)</math>그리고 미분을 한다면 다음과 같이 됩니다.σϵσxl=σϵσxLσxLσxl=σϵσxL(1+σσxlL−1∑i=1F(xi,Wi))<math>σϵσxl=σϵσxLσxLσxl=σϵσxL(1+σσxl∑i=1L−1F(xi,Wi))</math>이때, σϵσxL<math>σϵσxL</math>은 상위 layer의 gradient 값이 변하지 않고 그대로 하위 layer에 전달되는 것을 보여줍니다. 즉, layer를 거칠수록 gradient가 사라지는 vanishing gradient 문제를 완화해주는 것입니다.또한 forward path나 backward path를 간단하게 표현할 수 있게 됩니다.Layer NormalizationLayer Normalization이라는 논문에서 제시된 방법입니다.μl=1HH∑i=1ali<math>μl=1H∑i=1Hail</math>σl= ⎷1HH∑i=1(ali−μl)2<math>σl=1H∑i=1H(ail−μl)2</math>같은 layer에 있는 모든 hidden unit은 동일한 μ<math>μ</math>와 σ<math>σ</math>를 공유합니다.그리고 현재 input xt<math>xt</math>, 이전의 hidden state ht−1<math>ht−1</math>, at=Whhht−1+Wxhxt<math>at=Whhht−1+Wxhxt</math>, parameter g,b<math>g,b</math>가 있을 때 다음과 같이 normalization을 해줍니다.ht=f[gσt⊙(at−μt)+b]<math>ht=f[gσt⊙(at−μt)+b]</math>이렇게 한다면, gradient가 exploding하거나 vanishing하는 문제를 완화시키고 gradient 값이 안정적인 값을 가짐로 더 빨리 학습을 시킬 수 있습니다.(논문에서 recurrent를 기준으로 설명했으므로 이에 따랐습니다.)DropoutDropout: a simple way to prevent neural networks from overfitting라는 논문에서 제시된 방법입니다.dropout이라는 용어는 neural network에서 unit들을 dropout하는 것을 가리킵니다. 즉, 해당 unit을 network에서 일시적으로 제거하는 것입니다. 그래서 다른 unit과의 모든 connection이 사라지게 됩니다. 어떤 unit을 dropout할지는 random하게 정합니다.dropout은 training data에 overfitting되는 문제를 어느정도 막아줍니다. dropout된 unit들은 training되지 않는 것이니 training data에 값이 조정되지 않기 때문입니다.Label SmoothingRethinking the inception architecture for computer vision라는 논문에서 제시된 방법입니다.training동안 실제 정답인 label의 logit은 다른 logit보다 훨씬 큰 값을 갖게 됩니다. 이렇게 해서 model이 주어진 input x<math>x</math>에 대한 label y<math>y</math>를 맞추는 것이죠.하지만 이렇게 된다면 문제가 발생합니다. overfitting될 수도 있고 가장 큰 logit을 가지는 것과 나머지 사이의 차이를 점점 크게 만들어버립니다. 결국 model이 다른 data에 적응하는 능력을 감소시킵니다.model이 덜 confident하게 만들기 위해, label distribution q(k∣x)=δk,y<math>q(k∣x)=δk,y</math>를 (k가 y일 경우 1, 나머지는 0) 다음과 같이 대체할 수 있습니다.q′(k|x)=(1−ϵ)δk,y+ϵu(k)<math>q′(k|x)=(1−ϵ)δk,y+ϵu(k)</math>각각 label에 대한 분포 u(k)<math>u(k)</math>, smooting parameter ϵ<math>ϵ</math>입니다. 위와 같다면, k=y인 경우에도 model은 p(y∣x)=1<math>p(y∣x)=1</math>이 아니라 p(y∣x)=(1−ϵ)<math>p(y∣x)=(1−ϵ)</math>이 되겠죠. 100%의 확신이 아닌 그보다 덜한 확신을 하게 되는 것입니다.Conclusiontransformer는 recurrence를 이용하지 않고도 빠르고 정확하게 sequential data를 처리할 수 있는 model로 제시되었습니다.여러가지 기법이 사용됐지만, 가장 핵심적인 것은 encoder와 decoder에서 attention을 통해 query와 가장 밀접한 연관성을 가지는 value를 강조할 수 있고 병렬화가 가능해진 것입니다.Referencehttp://www.whydsp.org/280http://mlexplained.com/2017/12/29/attention-is-all-you-need-explained/http://openresearch.ai/t/identity-mappings-in-deep-residual-networks/47https://m.blog.naver.com/PostView.nhn?blogId=laonple&logNo=220793640991&proxyReferer=https://www.google.co.kr/https://www.researchgate.net/figure/Sample-of-a-feed-forward-neural-network_fig1_234055177https://arxiv.org/abs/1603.05027https://arxiv.org/abs/1607.06450http://jmlr.org/papers/volume15/srivastava14a.old/srivastava14a.pdfhttps://arxiv.org/pdf/1512.00567.pdf
조회수 3885

크몽 개발팀 문화와 구조 이야기

안녕하세요. 크몽 개발자들과 함께하고 있는 크레이그(a.k.a. 크알)입니다.크몽 개발자 그룹은 1년 내 그 규모가 3배로 커지고, Data Science, Growth Hacking 조직이 만들어지는 등 질적, 양적으로 급성장하고 있는 팀입니다.크몽 개발 부서에 계신 분들은 크몽에 대해 이렇게 이야기 합니다.(참고 : 크몽 개발팀원 더팀스 인터뷰 - '신뢰할 수 있는 동료와 함께 초고속 성장을 만들어가는 크몽 팀' )"제가 크몽에서 전반적으로 느낀 인상은 능동적인 분들이 많다는 거예요. 수동적인 업무를 책임감 있게 하는 것도 중요하지만 문제를 스스로 찾고, 동료들에게 제기하고, 문제를 해결했을 때 진심으로 기뻐하면서 행복감을 느끼시는 분들이 많아요. 그게 큰 조직에 있다가 온 저에게는 정말 많은 자극이 되었어요. "- 데이터분석 KM님"크몽이 저의 개발자 커리어에서 마지막 회사였으면 좋겠다고 생각해요. 실은 진심이고요. 그동안 회사의 성장을 지켜봤고 개발적으로도 많은 변화를 경험했어요"- BackEnd Sean님이렇게 개발자들이 행복하게 개발할 수 있는 환경을 우선시하고 있습니다. 그리고 크몽의 오픈 커뮤니케이션 문화를 지향함과 동시에 ‘Work Happy’와 'Freedom with Responsibility’ 란 가치 아래 최대한 자율성을 보장된 실무자 중심의 개발 문화를 추구합니다.크몽 개발 조직 구조위 핵심 가치 아래 크몽 개발 조직 구조는 크게 ‘Go’와 ‘Chapter’로 구성되어 있습니다.Go  ; 고우선 ‘Go’는 프로젝트 개발 팀 단위로 크몽 서비스를 개선하기 위한 목표 중심의 조직입니다. 다른 회사에서는 ‘Silo’, ‘Team'로 명칭 하기도 합니다. 물리적으로 한 공간에서 스크럼을 이루어 일할 수 있도록 자원을 갖추고 있습니다. Go 안에는 Go Leader(GL) 가 있어 팀 업무 관리 및 우선순위를 정합니다.현재 크몽 개발 파트의 Go는 아래와 같이 구성되어 있습니다.UX-Go크몽 서비스 UX를 개선하기 위한 목표로 데이터를 기반으로한 UX Iteration & Growth Mission 을 수행하는 팀Data-Go데이터 파이프라인을 구축, 활용하여 조직 내 필요한 데이터 자료를 공급하고, 크몽 서비스안에 머신러닝/딥러닝 등의 인공지능 기술 영역을 담당하는 팀Dasi-Go서비스 안정적인 운영 및 릴리즈,  CRM 기술 지원을 담당하는 팀Mobile-Go검색 서비스, 서비스 카테고리 개선 등 크몽 서비스 향상을 위한 모듈 개발팀크몽 라운지Chapter  ; 챕터'Chapter'는 직군별 조직 단위로 주 1회 정도의 커뮤니케이션 타임을 통해 업무 및 기술 동향을 교환합니다. 더불어 챕터 안에서 필요한 스터디, 외부 교육 등의 직군별 자기 능력 향상을 도모하고, 회사에선 이를 적극 지원합니다. 그리고 챕터 내 프로젝트를 통해 서비스 개선에 기여하기도 합니다.크몽 개발 파트는 아래와 같은 챕터가 있습니다.(참고 : 웹 프로트엔드 챕터의 'gulp 개선기' -  https://brunch.co.kr/@kmongdev/5 )**챕터 프로젝트는 챕터 내에서 개발자분들이 스스로 필요하다는 판단 하에 빌딩 된 프로젝트입니다. 챕터 내에는 CL(Chapter Leader)가 존재하며, Chapter 구성원 관리 및 의견을 모아 조직에 전파하는 역할을 담당합니다.Guild  ; 길드개발 파트 안에서의 'Guild'는 토이 프로젝트 같은 성격의 공통 관심 분야를 지닌 프로젝트 팀이라고 볼 수 있습니다. 길드 기획 단계에서 회사 전사적으로 적용되면서, 동호회 성격으로 피보팅(Pivoting) 되어 있지만, 기본적으로 공통의 관심 분야를 같이 학습하고 프로젝트에 적용하는 팀입니다. 매주 수요일 오후 2~3시 사이의 시간은 챕터(Chapter), 고(Go)를 떠나 본인이 원하는 길드에 들어가서 새로운 영역을 탐색하고 연구하는 시간입니다.크몽 개발 파트는 아래와 같은 길드가 있습니다.(참고 : 코틀린 길드의 코틀린 리서치 이야기  https://brunch.co.kr/@kmongdev/9 )정리모든 개발 조직은 '성과 중심' 또는 '성장 중심'의 문화를 가지고 있습니다. 균형을 꾀하는 게 이상적이긴 하지만 스타트업에선 쉽지 않은 일입니다.하지만 크몽 개발 부서에선 인적 성장 중심 문화를 고민하고, 끊임없이 시도하고 있습니다. 이를 위해 여러 전문 교육 기관과 협약을 맺고 교육 지원을 하고 있으며, 국내 정상급 권위자 분들로 구성된 외부 컨설턴트 그룹을 구성해 개발자 분들께 배움과 성장의 기회를 부여하려고 노력하고 있습니다. 1년의 기간 동안 이직률3%의 수치를 기록하고 있는 크몽 개발 파트에선 신규 인력 채용 시 제 1의 인사 기준은 '높은 학력'도, '화려한 커리어'도 아닌우리와 '오랫동안' 함께 '성장'할 수 있는가?입니다. 이를 위해선 개발자 성장을 돕기 위한 환경 구축 및 관리가 필수이고,  그것이 궁극적으로는 회사 및 팀원에게도 장기적인 발전을 가져올 꺼란 굳은 믿음이 있습니다.크몽 개발 그룹CTO#크몽 #개발팀 #개발자 #사내복지 #기업문화 #조직문화 #사내스터디 #CTO
조회수 10186

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

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

기업문화 엿볼 때, 더팀스

로그인

/