스토리 홈

인터뷰

피드

뉴스

조회수 1264

“매일매일 새로운 도전으로 채워지는 자리”

“매일매일 새로운 도전으로 채워지는 자리” – 패스트캠퍼스에서 일하는 콘텐츠 마케터 이야기“마케팅 중 유효한 것은 콘텐츠 마케팅 뿐이다.” – 세스 고딘<보랏빛 소가 온다>를 쓴 세계적인 마케팅 구루 세스 고딘의 말처럼 콘텐츠 마케팅은 마케팅의 주류로 자리잡으며 전통적인 광고의 입지를 위협하고 있습니다. 그런데 콘텐츠 마케팅은 범주가 넓어 기업 특성에 따라 실무에서 담당하는 업무가 다양한데요. 이번 글에서는 패스트캠퍼스의 콘텐츠 마케터들은 무슨 일을 어떻게 하는지 자세히 알려드리고자 프로그래밍팀 시니어 콘텐츠 마케터 김하림님과 파이낸스팀 콘텐츠 마케터 이유나님을 모시고 인터뷰를 진행했습니다.안녕하세요 하림님 유나님, 오늘 인터뷰에 응해 주셔서 감사합니다. 간단하게 자기소개 부탁드려도 될까요? 안녕하세요, 프로그래밍팀 시니어 콘텐츠 마케터 김하림입니다. 지난주에 막 입사한 지 1년이 되었어요.안녕하세요, 저는 파이낸스팀 콘텐츠 마케터 이유나라고 합니다. 패스트캠퍼스에서 일한 지 이제 9개월 째고요. 두 분께서는 패스트캠퍼스에 합류하기 전 무슨 일을 하셨는지, 어떤 계기로 패스트캠퍼스 콘텐츠 마케터로 입사하게 되셨는지 궁금합니다. 패스트캠퍼스에 오기 전에는 웹디자인 일을 하고 있었는데, 회사 규모가 작아 세금계산서 발행부터 제안서 작성까지 회사 운영의 전과정에 참여해야 하다 보니 웹디자인에만 몰두할 수 있는 환경은 아니었어요. 전문성을 가지고 한 가지 일에 좀 더 집중하고 싶어 회사를 그만두었습니다. 그러다 채용공고를 살펴보던 중 패스트캠퍼스의 콘텐츠 매니저(지금은 콘텐츠 마케터로 직함이 바뀌었죠) 자리를 발견하게 되었고요. 제가 할 수 있는 다양한 일들을 업무 역량으로 발휘할 수 있을 것 같아 지원했어요. 저는 지금 마지막 학기를 보내고 있는 대학생이에요. 경영을 전공했고, 교육 분야에도 관심이 있어 국어교육학과를 복수전공하고 있었어요. 제가 흥미를 느낀 이 두 분야를 접목해 할 수 있는 일을 찾다 교육업에 있는 마케터 일이 저에게 딱 맞을 것 같아 지원서를 넣었던 기억이 납니다. 인턴으로 입사했다 정직원으로도 계속해서 함께하는 중이예요. 유나님께서는 인턴 기간이 종료된 후에도 이곳에서 일하고 계신데, 패스트캠퍼스를 선택하신 이유가 무엇일까요? 패스트캠퍼스에서는 인턴이라도 정직원과 같은 일을 하면서 눈치 보지 않고 자기 의견을 낼 수 있던 것이 좋았어요. 저에게는 자기발전을 계속할 수 있는지가 직업을 선택할 때 중요한 기준인데 여기에 맞고, 사회 초년생으로서 일을 배우기에도 좋은 환경인 것 같아 정직원으로 계속 일하고 있습니다. 제가 막 입사했을 때, 당시 팀장님께서 제 직무에 대해 설명해주셨던 것이 기억에 남아요. “프로덕트 매니저가 오프라인에서 기획을 하는 사람이면 콘텐츠 마케터는 온라인에서 기획을 하는 사람이다”라는 말이었는데 저희는 고객분들이 온라인에서 접하는 모든 콘텐츠를 기획·제작하고 글을 쓰는 만큼 일리가 있는 것 같아요. 기획자, 제작자, 에디터의 역량을 모두 발휘해야 하는 사람이 콘텐츠 마케터라고 생각합니다. 하림님의 말씀에 더해, 우리 회사 콘텐츠 마케터가 맡는 특별한 일 중 하나는 상세페이지를 기획 및 디자인해 고객을 설득하는 글쓰기를 한다는 것이에요. 마케터라 하면 광고 크리에이티브를 제작하는 데 업무의 초점이 맞춰져 있다는 느낌이지만, 여기서는 기획 역량까지 발휘해야 하는 점이 특징이죠. 콘텐츠 마케터로서 다양한 일을 하고 계시는데, 어느 정도 정해진 일과가 있을까요? 하루 일과를 딱 잘라서 말하긴 어렵습니다. 그때그때 담당하는 일의 중요도가 달라져서요. 우선 프로덕트 매니저 분이 새로운 강의 기획을 완성하시면 신규 상세페이지를 제작하고, 기존 강의를 업그레이드해 오시면 그에 맞게 기존 상세페이지의 내용을 수정합니다. 홍보 진행이 원활하지 않으면 팀원들과 트러블 슈팅을 통해 상세페이지나 광고 크리에이티브를 손보기도 하고 강사 인터뷰, 수강생 인터뷰 혹은 블로그 게시물이나 카드뉴스 형태의 오가닉 콘텐츠를 발행하기도 합니다. 업무 진행에 있어 큰 틀은 있겠지만 그때그때 업무의 우선순위가 달라져요. 일이 많아 야근할 때도 종종 있고요. 패스트캠퍼스 콘텐츠 마케터 직무, 입사 전 생각했던 것과 실무를 진행하는 것에 차이가 있나요? 저는 비슷한 것 같아요. 간단한 퍼블리싱, 마크업(HTML/CSS로 코딩을 하는 것)을 할 수 있는 사람으로서 이런 스킬들이 상세페이지 제작 업무에 도움이 될 거라고 생각했거든요. 입사 전 필수적으로 갖춰야 할 스킬은 아니었지만 업무를 진행하다 보니 마크업을 알아서 더 도움이 되는 게 많았어요. 그런데… 트러블 슈팅이 이렇게 많을 줄은 몰랐네요. 하하. 저는 하림님과 반대예요. 콘텐츠 마케팅이 이렇게까지 다양한 능력을 요구하는 일인 줄 전혀 몰랐어요. 업무 스킬은 물론 담당하는 강의에 대한 지식적인 부분까지도요. 깊게 파고들 필요는 없지만 얕고 넓은 지식이 필요한 일이더라고요.물론 하림님처럼 업무와 관련된 스킬을 가지고 입사하시면 실무에 확실히 도움 되는 부분이 있어요. 포토샵이나 HTML/CSS 같은 것들요. 하지만 저의 경우에는 포토샵도 못 다룰 만큼 아무것도 모르는 상태로 일을 시작했는데도 필요한 것들을 배워 가며 일할 수 있는 환경이라 괜찮았어요. 그 과정에서 성장하고 있는걸 스스로도 느낄 정도에요.지금 패스트캠퍼스에서는 프로그래밍, 데이터 사이언스, 마케팅, 외국어 등 다양한 팀에서 콘텐츠 마케터를 채용 중인데요. 팀별로 콘텐츠 마케터가 갖춰야 할 배경지식, 선호하는 스킬셋이 다를까요? 크게 차이는 없는 것 같아요. 합류하는 팀에 따라 만들게 되는 콘텐츠의 성격은 달라질 수 있지만 배경지식이 필수는 아니거든요. 프로덕트 매니저 분들이 작성하신 기획 문서를 읽고 핵심이 되는 부분을 짚어 콘텐츠로 만들어낼 수 있으면 됩니다. 이해하기 어려운 부분은 프로덕트 매니저 분들께 물어보면 어느 팀에서건 친절하게 알려주실 거예요. 맞아요. 저도 파이낸스 분야를 공부하며 콘텐츠를 만들고 있는데, 아는 게 점점 많아지고 있는 것 같아 뿌듯합니다. 콘텐츠 마케터는 끊임없이 새로운 것들을 배워야 하는 직무 같은데요. 패스트캠퍼스에서 콘텐츠 마케터로 일하며 가장 힘든 점은 무엇인지 솔직하게 말씀해 주신다면? 하나의 콘텐츠에 오랜 시간을 투입할 수 없는 점? 일주일에 새로운 상세페이지를 세 개씩 만들 때도 있다 보니 한 가지 업무만 집중해서 파고들 시간적 여유가 없어요. 특히 트러블 슈팅이 많이 발생하다 보면 업무 시간이 절대적으로 부족하죠. 유나님 말씀에 더해, 강의마다 특징을 가장 잘 보여줄 수 있는 상세페이지를 만들기 위해 고민하는 게 재밌으면서도 어려운 일 같아요. 이런 부분에 대해 어떤 도움을 드릴 수 있을까 다른 시니어 분들과 함께 고민 중이고요. 그리고 솔직히 말하자면, 일이 정말 많아요. 그게 제일 힘들죠. 업무 과다로 고생이 많으신데, 힘든 점들이 있음에도 이 일을 계속하게 만드는 원동력은 무엇인가요? 일은 많지만 업무 방식에 제한은 없어서 이것저것 새로운 시도를 해 볼 여지가 있다는 게 좋아요. 상세페이지를 수정했거나 새로운 광고 크리에이티브를 만들었는데 효율이 좋다거나, 오가닉 콘텐츠를 발행했는데 커뮤니티 등에 업로드되는 등 좋은 반응을 얻었다거나 하면 보람도 있고요. 틀에 박힌 일을 하지 않는다는 점이 재밌어요. 맞아요. 새로운 시도에 대한 제재가 없으니 할 수 있는 게 많아서 좋죠. 성과에 따른 연봉협상도 유연하게 이뤄지고요. 어떤 콘텐츠 마케터를 동료로 맞이하고 싶으신지 궁금합니다. 새로운 시도에 대한 거리낌이 없으신 분. 새로운 일이 주어졌을 때 ‘저는 이거 못하겠어요’가 아니라 ‘이것도 저것도 해 볼게요’라고 말할 수 있는 분! 팀원들과 협업을 잘할 수 있는 분. 프로덕트 매니저, 퍼포먼스 마케터의 의견을 반영해 콘텐츠를 제작하고 배포하기 때문에 커뮤니케이션 능력이 좋다면 일을 잘할 수 있을 것 같아요. 거기에 하나 더, 자신의 의견만 고집하기보다 다른 사람의 의견을 잘 받아들일 수 있는 분. 서로의 잘잘못을 따지기보다 더 나은 방향을 위해 협업하고 있다는 걸 잊지 않는 분이면 좋겠어요. 쓰는 걸 두려워하지 않는 분이면 정말 좋고요. 맞아요. 포토샵이나 워드프레스 스킬들은 모르셔도 괜찮아요. 저희가 알려드릴게요! 마지막 질문입니다. 두 분께 패스트캠퍼스란 어떤 곳일까요? 매일매일 변화무쌍한 곳. 틀에 박힌 일을 하지 않아요. 오늘, 지금입니다. 오늘이 쌓여서 내일이 되고 매일이 되는데, 그 오늘이 매일매일 새로워요.* 패스트캠퍼스 콘텐츠 마케터는? *  패스트캠퍼스 고객들이 접하게 되는 모든 접점을 컨트롤하는 역할을 담당합니다. 기획 과정에 참여하는 것은 물론 교육 콘텐츠 상세페이지를 제작하고, 매력적인 광고 크리에이티브를 만들고, 강사와 수강생들의 목소리를 전달하기도 합니다. 즉, 패스트캠퍼스에서 만들어지는 모든 콘텐츠의 외모를 결정하고 그 톤앤매너를 관리합니다.
조회수 994

Good Bye, Parse

한 번도 안 써 본 개발자는 있어도 한 번만 써 본 개발자는 없을 듯한 Parse(parse.com)가 오늘 아침 충격적인 공지 메일을 보내왔는데, 1년후 서비스 종료한다는 것이었다. Parse는 모바일에 특화된 DB 플랫폼을 시작으로, 어드민툴, 푸시, 애널리틱스, 로그인, 크래쉬리포트 등으로 서비스를 확장하며 모바일 개발자들의 백엔드 구축을 편하게 만들어주었는데 2013년 페이스북에 인수되면서 안정성에 대한 신뢰가 커지고, 가격 정책이 변경되어 상당히 저렴해지면서 개발자들의 더욱 큰 호응을 얻었다. 1초에 30개의 요청까진 무료였으니 1개월로 환산하면 7700만 요청이 무료이고, 이는 10만 사용자 이상도 무난하게 대응할 수 있는 숫자이다. 물론 사용자와 요청 수가 늘어나면 월 100~200달러씩 비용을 추가하면 되고 무엇보다 서버 확장에 신경쓰지 않아도 됐다. 푸시 발송은 100만대의 기기까지 발송이 무제한 무료였고 크래쉬리포트와 유저 리텐션 분석도 심플하고 쓸만했는데 무료였다. 역시 무료인 2TB 트래픽은 상용 CDN을 사용하면 수십만원의 비용이 든다.복잡한 쿼리를 작성하기 어렵고 서버사이드 코드를 디버깅하기 쉽지 않아 서버 로직이 복잡한 서비스에는 적합하지 않았지만 계속 보완된다는 느낌이 있고 무료이니 불편해도 감수하고 쓸만 했어서, 이외에는 단점을 찾기가 어려울 정도였다. 무료티어가 워낙 후해서 돈을 내고 쓰는 개발자가 얼마나 될까 싶다는 점이 오히려 불안요소였다. Parse 그리고 페이스북 입장에서 수익성이 어떻게 확보될까 의구심이 들었던 것이다.  수익성 측면에서 무료로 사용하던 앱들의 트래픽이 늘어나 과금티어로 넘어가야 하는데 그러려면 100만 다운로드 이상의 중대형 서비스로 성장해야 하고, 그 정도 되면 그 스타트업도 자체 백엔드 엔지니어가 확보되고 백엔드 로직도 복잡해져 Parse와 궁합이 잘 안 맞게 되는 것이다. 수익성이 아니라면 잠재적인 가치로써 Parse를 사용하는 스타트업과 페이스북의 연결고리를 찾아 유저 베이스를 활용하던지 광고 매체로써 활용하던지 방안이 있어야 하는데 페이스북 자체 패밀리들(페이스북, 메신저, 인스타그램, 와츠앱)의 매체력과 광고수익이 워낙 크고 견고하게 성장하는 중인데다 페이스북 애널리틱스와 외부 광고 서비스가 자리잡으면 Parse는 더더욱 투자가치가 떨어진다. 당장 수익성이 없고, 잠재적인 가치도 잘 모르겠고, 똑똑한 내부 엔지니어들이 묶여 있고, 무료로 쓰는 개발자들은 매일 뭐 만들어달라 뭐 고쳐달라 요청을 하니 '우리가 이걸 왜 붙들고 있어야 하지' 내부 논의가 계속 있어왔을 것 같은데, 그래도 작년 가을까지는 페이스북 코리아에서 Parse 관련 세미나와 컨퍼런스도 열고 미국에서 F8이 열린 시점에는 IoT와 React로 확장하는 등 좀더 적극적으로 활용하려는 듯 했다. 작년 겨울부터 분위기가 이상해졌다. 우선 작년 말 크래쉬리포트 서비스 종료. Parse의 서비스의 일부이긴 했지만 다른 대안과 비교해서 확실한 우위가 있던 서비스여서 '수익성'의 이유 외에는 정리할 이유가 없어보였다. 그 동안 문제 삼지 않았던 '수익성'이 이제는 문제가 된건가? 그러면 사실 Parse 전체 서비스도 접을 수 있다는 생각이 들었다. 그리고 이어서 나온 '오픈소스화'와 'Heroku 연동'. 오픈소스는 좋은 의미 또는 서비스의 발전을 위해서라고 볼 수도 있는데 Heroku 연동은 굳이 왜? Parse 입장에서나 Parse를 쓰는 개발자 입장에서나 백엔드를 Parse 클라우드 코드 대신 Heroku로 이동할 이유가 없어 보였다. Parse를 접게 되어 마이그레이션 해야 한다면 모를까...??? 가 현실이 되었다.나도 작년까지는 서버비 한 푼 내지 않고 대부분의 서비스를(수십개) Parse에 올렸었고 주변에나 Parse 세미나에서도 Parse의 장점에 대해 이야기하곤 했는데... 수익성 없는 앱들을 정리하면서 Parse에 대한 의존도가 낮아지긴 했지만 지금 제작하고 있는 것들부터 마이그레이션 해야 하고, 추천했던 지인들에게는 마이그레이션 전략을 컨설팅해야 할 판이다. 서비스 종료라는 극단적인 선택을 하기 전에 무료 티어를 좁혀서 수익화를 한 상태로 좀더 오래 버텨줬으면 어땠을까 하는 아쉬움이 든다. 구글앱스도 수익화를 하면서 유지하듯이. 광고 외의 B2B 비즈니스에서 페이스북에 대한 신뢰가 많이 하락하지 않을까 싶다. 마이그레이션 비용은 상당하다. 우선 Parse의 슬로건이 백엔드 작업 없이 백엔드를 이용할 수 있는 것이었기 때문에 이제 백엔드를 직접 구성하던지 Parse가 제공하던 서비스들의 대체제들을 각각 찾아 옮겨야 하고, 초기부터 그런 대안으로 구축한 것보다 이전하는 비용이 훨씬 큰 것은 당연하다. 클라이언트 엔지니어가 Parse로 구축했다가 아직까지 백엔드 엔지니어가 없는 서비스들은 그냥 정리될 가능성도 크다. 사실 백엔드 서비스들의 이용료 자체는 많이 낮아진 상태이기 때문에 여기서 말하는 마이그레이션 비용은 대부분 외주/컨설팅이나 백엔드 개발자 채용 등의 엔지니어링 비용이다. 나도 대안들을 이제 막 찾기 시작했지만 이전할 수 있는 대안들을 정리하면서 마무리해본다. Parse DB: http://firebase.com/ (구글이 인수했다. 데자뷰의 느낌이 들 수도 있지만 그래도 믿어보자)Parse Push: http://valuepotion.com/ (푸시 & 분석& 마케팅툴 - 카카오 국내 자회사의 글로벌 서비스)https://www.pushwoosh.com/ (urbanairship보다 저렴하다고)Parse Crash Reporting: http://crashlytics.com (트위터 인수, 과거에 유료였으나 무료로 전환됨)Parse Cloud Code:http://heroku.com/ (Parse의 Heroku 연동과 함께 살펴보자)https://github.com/ParsePlatform/parse-server (Parse에서 제공한 서버 오픈소스. 이를 활용한 마이그레이션 서비스가 나올 듯 싶다)
조회수 1887

CTE for postgresql and sqlalchemy

저희 서비스는 가게마다 웹에서 접속할 수 있는 어드민을 제공하는데, 프렌차이즈가 아닌 하나의 독립적인 가게들일 경우 정보를 가져와 나타내는 데는 굳이 CTE 를 쓸 필요가 없지만 프렌차이즈일 경우 본사와 지점들로 나누어져 있어서 본사와 지점들 정보를 다 가져오기 위해서 CTE 를 사용하게 되었습니다.그럼 postgresql 의 CTEReadme 에 나와 있는 예제와 sqlalchemy core 로 변환하는 것까지 살펴보겠습니다.CTE란?Common table expression 의 약자로 ‘공통 테이블 식’입니다.CTE 특징WITH절 같은 SELECT 문에서 효과적으로 테이블 식을 정의 할 수 있습니다.CTE는 VIEW의 사용방법과 비슷하지만, VIEW보다 편리합니다.VIEW와 달리 사전에 CTE를 정의할 필요가 없습니다.개체로 저장되지 않고, 쿼리 지속시간에만 존재합니다.CTE는 재귀 쿼리를 사용할 수 있습니다.재귀 CTE는 여러행을 반환 가능합니다.동일 문에서 결과 테이블을 여러번 참조 가능합니다.재귀 CTE 예제아래 예제는 ‘A’부서 하위에 있는 부서만 추출하는 예제입니다.일단 재귀 CTE를 이용한 쿼리를 사용하려면 ‘WITH RECURSIVE’ 키워드를 추가해야 합니다.Table ‘department’ 인접 리스트로 조직 구조를 나타냅니다.CREATE TABLE department ( id INTEGER PRIMARY KEY, -- department ID parent_department INTEGER REFERENCES department, -- upper department ID name TEXT -- department name ); INSERT INTO department (id, parent_department, "name") VALUES (0, NULL, 'ROOT'), (1, 0, 'A'), (2, 1, 'B'), (3, 2, 'C'), (4, 2, 'D'), (5, 0, 'E'), (6, 4, 'F'), (7, 5, 'G');부서 구조:ROOT-+->A-+->B-+->C | | | +->D-+->F +->E-+->G A의 하위 부서를 추출, 다음과 같은 재귀 쿼리를 사용할 수 있습니다.WITH RECURSIVE subdepartment AS ( -- non-recursive term SELECT * FROM department WHERE name = 'A' UNION ALL -- recursive term SELECT d.* FROM department AS d JOIN subdepartment AS sd ON (d.parent_department = sd.id) ) SELECT * FROM subdepartment ORDER BY name;위의 쿼리는 다음과 같이 설명할 수 있습니다.중간 테이블(Intermediate table), 작업 테이블(work table), 결과 테이블(result table)이 있습니다.초기화비재귀 구간을 실행 (SELECT * FROM department WHERE name = ‘A’)ResultTable = WorkTable = (‘A’) 결과 테이블과 작업 테이블에 결과를 배치합니다.IntermediateTable = () 중간 테이블을 비웁니다.재귀 쿼리 실행(SELECT d.* FROM WT AS d JOIN subdepartment AS sd ON d.parent_department = sd.id) 하위 부서와 작업 테이블을 바꾸고, 재귀 구간을 실행합니다.중간 테이블에 쿼리 결과를 할당합니다.결과 테이블 및 작업 테이블에 중간테이블 추가합니다.중간 테이블을 비웁니다.재귀가 끝났는지 확인2번 과정의 중간테이블이 비어 있으면 재귀의 실행이 종료되고, 결과 테이블은 반환됩니다.중간테이블이 비어 있지 않으면 다시 2번의 과정으로 돌아갑니다.“subdepartment”는 재귀 표현을 포함하고 있는 CTE입니다. 먼저 비재귀항이 평가되고, 다음 재귀항이 평가됩니다. 재귀항은 평가하고 처리하는 데이터가 없을 때까지 결과가 반복적으로 이전 결과에 추가됩니다. 끝으로 마지막 SELECT가 실행되고 데이터는 결과 집합에서 추출됩니다.CTE의 한계점SEARCH 및 CYCLE 절은 구현되지 않습니다.상호 재귀는 허용되지 않습니다.UNION ALL의 마지막 SELECT만 재귀 이름을 포함할 수 있습니다.재귀와 재귀스캔(RecursiveScan) 계획의 비용은 항상 0입니다sqlalchemy 로 변환sqlalchemy 에서 필요한 모듈들을 불러옵니다.from sqlalchemy import Table, Column, Text, Integer, MetaData, select metadata = MetaData() department 테이블을 정의합니다.department = Table('department', metadata, Column('id',Integer), Column('parent_department',Integer), Column('name',Text)) WITH 절부터 시작되는 CTE 부분의 비재귀항을 subdepartment로 만듭니다. 재귀 사용을 위해 .cte( recursive=True) 부분을 붙여줍니다.subdepartment = select([ department.c.id, department.c.parent_department, department.c.name]).where(department.c.name == 'A') \ .cte(recursive=True) department 와 subdepartment 에 각각 alias를 붙여줍니다.subd_alias = subdepartment.alias() department_alias = department.alias() CTE 부분의 재귀항과 비재귀 항을 union all 해주는 subdepartment를 만듭니다. (이 부분이 postgresql 예제 쿼리에서 봤던 WITH RECURSIVE subdepartment 전체를 나타내는 부분이라 할 수 있습니다.)subdepartment = subdepartment.union_all( select([ department_alias.c.id, department_alias.c.parent_department, department_alias.c.name]) \ .where(department_alias.c.parent_department == subd_alias.c.id)) 마지막으로 결과 쿼리를 출력하기 위한 statement를 만듭니다.statement = select([ subdepartment.c.id, subdepartment.c.parent_department, subdepartment.c.name]).order_by(subdepartment.c.name) 원문: CTEReadme참조: 공통 테이블 식 사용 ,공통 테이블 식을 사용하는 재귀 쿼리#스포카 #개발 #개발자 #서버개발 #개발팀 #꿀팁 #인사이트 #조언
조회수 2077

Kubernetes에 EBS 볼륨 붙이기

Kubernetes에서 컨테이너에 Persistent Volume을 붙이는 방법은 몇가지 있다. 여기서는 Kafka 서비스를 예로 삼아 주요 접근방법을 간단히 알아본다.Kubernetes v1.4.0를 기준으로 문서를 작성한다.Static말이 Static이지 수동 마운트를 뜻한다. 기본적으로 관리자가 EBS 볼륨을 만들고특정 Pod에 그 볼륨을 붙이는 작업을 한다. Volumes 문서에 나오는대로 하면 간단하다.apiVersion: v1 kind: Service metadata: name: kafka1 labels: app: kafka1 tier: backend spec: ports: # the port that this service should serve on — port: 9092 name: port targetPort: 9092 protocol: TCP selector: app: kafka1 tier: backend — - apiVersion: extensions/v1beta1 kind: Deployment metadata: name: kafka1 spec: replicas: 1 template: metadata: labels: app: kafka1 tier: backend spec: containers: — name: kafka1 image: wurstmeister/kafka imagePullPolicy: Always volumeMounts: — mountPath: “/kafka” name: kafka1volume ports: — containerPort: 9092 volumes: — name: kafka1volume awsElasticBlockStore: volumeID: vol-688d7099 fsType: ext4여기서 핵심은 다음의 두 줄 뿐이다.awsElasticBlockStore: volumeID: vol-688d7099Dynamic수동으로 볼륨을 붙이는 방법은 간단해서 좋다. 하지만 Autoscaling하는 서비스에 넣기에는 아무래도 무리다. 서비스가 뜰 때 요구사항에 맞는 볼륨을 스스로 만들어 붙이는 방법도 있다. Kubernetes Persistent Volumes를 참고해 작업해본다.우선 Kubernetes 생성할 EBS 볼륨의 사양을 정한다.# storages.yaml apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: default1a provisioner: kubernetes.io/aws-ebs parameters: type: gp2 zone: ap-northeast-1a iopsPerGB: “10” — - apiVersion: storage.k8s.io/v1beta1 kind: StorageClass metadata: name: default1c provisioner: kubernetes.io/aws-ebs parameters: type: gp2 zone: ap-northeast-1c iopsPerGB: “10”default1a를 선택하면 ap-northeast-1a Availablity Zone에 기가바이트당 IOPS는 10인 General SSD EBS 볼륨을 생성한다. 이제 다시 Kafka의 돌아가면apiVersion: v1 kind: Service metadata: name: kafka1 labels: app: kafka1 tier: backend spec: ports: # the port that this service should serve on — port: 9092 name: port targetPort: 9092 protocol: TCP selector: app: kafka1 tier: backend — - apiVersion: extensions/v1beta1 kind: Deployment metadata: name: kafka1 spec: replicas: 1 template: metadata: labels: app: kafka1 tier: backend spec: containers: — name: kafka1 image: wurstmeister/kafka imagePullPolicy: Always volumeMounts: — mountPath: “/kafka” name: kafka1volume ports: — containerPort: 9092 volumes: — name: kafka1volume persistentVolumeClaim: claimName: kafka1volumeclaim — - kind: PersistentVolumeClaim apiVersion: v1 metadata: name: kafka1volumeclaim annotations: volume.beta.kubernetes.io/storage-class: “default1a” spec: accessModes: — ReadWriteOnce resources: requests: storage: 300Gi이제 awsElasticBlockStore가 아닌 PersistentVolumeClaim을 통해 볼륨을 할당받는다. kafka1volumeclaim은 default1을 기준으로 스토리지 정책을 정하므로Availablity Zone: ap-northeast-1aIOPS: 기가바이트당 10General SSD300Gi 이상인 스토리지를 원한다는 요구사항을 기술한다. 위의 설정은 이러한 스토리지에 부합하는 EBS 볼륨을 생성하여 kafka1 Pod에 할당한다.분석Dynamic은 Autoscaling에는 적합하나 kubectl delete [service] 또는 kubectl delete [deployment] 등의 명령을 수행하여 서비스를 내렸다가 다시 올린 경우에 기존에 쓰던 볼륨을 마운트하지 않고 새 볼륨을 만드는 문제가 있다. 물론 delete를 하지 않고 서비스를 업데이트만 하는 경우에는 볼륨이 유지되지만 이래선 아무래도 문제의 소지가 많다.그래서 또다른 시나리오를 고민해볼 수는 있다. 짧게 설명하자면관리자가 Volumn Pool을 만들어놓고 Autoscaling 서비스가 이 풀 안에서 볼륨을 할당받게 한다. 이러면 앞서 본 두 가지 방식의 장점을 골고루 흡수할 수 있다.flocker 또는 glushterfs 같은 스토리지 관리 서비스를 활용해도 좋다. 하지만 배보다 배꼽이 큰 것 같은 느낌이 들지도 모르겠다.#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트 #꿀팁
조회수 2986

GitHub 계정으로 Kubernetes 인증하기

초기에는 kube-aws가 만들어준 관리자 인증서를 통해 Kubernetes를 관리했는데 역시나 대내외적으로 여건이 바뀌니 변화가 필요했다. 내부적으로는 개발 인력이 늘고 여러 프로젝트가 동시 진행되니 Staging 환경이 급격히 바뀌는데 계정이 하나이니 누가 무슨 작업을 했는지 확인하기 어렵고 외부적으로는 경쟁사의 보안사고 등에 영향을 받아 보안을 강화할 필요가 생겼다. 하여 보안 관련 작업을 여럿했고 그 중 하나가 바로 GitHub와 Kubernetes를 OAuth로 엮는 일이다.기본적으로는 개발자 각자가 자신의 GitHub 계정으로 인증 토큰을 받고 이를 이용해 Kubernetes API에 접근하는 것이다. 전체적인 흐름은 How I built a Kubernetes cluster so my coworkers could deploy apps faster 등을 참고하면 이해하기 그리 어렵지 않다.1. Admin time should be saved (since they are also our developers)2. New users can generate their own credentials without needing the admin3. User credential is always private for security reasons4. Developers have their own space to experiment5. Project spaces can be accessed and changed by multiple users6. In the future, we may want to enable auditing to track changes다만 저들과 달리 Webhook 토큰 인증 플러그인을 직접 짜지 않고 coreos/dex를 이용했다. Dex를 이용하면 GitHub를 비롯해 다양한 OpenID, OAuth 2.0 인증 서비스와 Kubernetes 클러스터를 엮기 쉽다. 더욱이 kube-aws에 Dex가 통합되어서 설치하기도 쉽다.설치하기구구절절 어떻게 설정하는지 설명할 생각은 없는데 회사와 프로젝트에 따라 세부적인 차이가 꽤나 클 수 있기 때문이다. 그러니 대략적인 작업 순서를 간략히 기술하고 끝내려 한다.우선 kube-aws의 cluster.yaml를 보자.# # Enable dex integration - https://github.com/coreos/dex # # Configure OpenID Connect token authenticator plugin in Kubernetes API server. # # Notice: always use "https" for the "url", otherwise the Kubernetes API server will not start correctly. # # Please set selfSignedCa to false if you plan to expose dex service using a LoadBalancer or Ingress with certificates signed by a trusted CA. # dex: # enabled: true # url: "https://dex.example.com" # clientId: "example-app" # username: "email" # groups: "groups" # selfSignedCa: true # # # Dex connectors configuration. You can add configuration for the desired connectors suported by dex or # # skip this part if you don't plan to use any of them. Here is an example of GitHub connector. # connectors: # - type: github # id: github # name: GitHub # config: # clientId: "your_client_id" # clientSecret: "your_client_secret" # redirectURI: https://dex.example.com/callback # org: your_organization # # Configure static clients and users # staticClients: # - id: 'example-app' # redirectURIs: 'https://127.0.0.1:5555/callback' # name: 'Example App' # secret: 'ZXhhbXBsZS1hcHAtc2VjcmV0' # # staticPasswords: # - email: "[email protected]" # # bcrypt hash of the string "password". You can use bcrypt-tool from CoreOS to generate the passwords. # hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" # username: "admin" # userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"우선 GitHub의 Organization Settings 메뉴로 가서 OAuth Apps에 Dex를 추가한다. 이때 Authorization calllback URL은 https://dex.example.com/callback가 된다.GitHub가 준 Client ID와 Client Secret를 cluster.yaml에 적어넣는다.dex: enabled: true url: "https://dex.example.com" clientId: "example-app" username: "email" groups: "groups" selfSignedCa: false # # # Dex connectors configuration. You can add configuration for the desired connectors suported by dex or # # skip this part if you don't plan to use any of them. Here is an example of GitHub connector. connectors: - type: github id: github name: GitHub config: clientId: "GITHUB_OAUTH_APP_CLIENT_ID" clientSecret: "GITHUB_OAUTH_APP_CLIENT_SECRET" redirectURI: https://dex.example.com/callback org: DailyHotel # # Configure static clients and users staticClients: - id: 'example-app' redirectURIs: 'https://kid.example.com/callback' name: 'Example App' secret: 'ZXhhbXBsZS1hcHAtc2VjcmV0'staticPasswords: - email: "[email protected]" # # bcrypt hash of the string "password". You can use bcrypt-tool from CoreOS to generate the passwords. hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" username: "admin" userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"여기서 dex.example.com은 kube-aws가 띄울 dex Deployment와 연결되는 서비스(ELB)의 도메인주소가 되어야 한다. 그런데 kube-aws는 Dex의 External service를 생성해주지 않으므로 아래와 같이 직접 서비스를 생성해야 한다. GitHub가 이쪽으로 콜백을 보내야 하므로 방화벽을 열어야 하고 회사 도메인 인증서를 붙일 것이므로 `selfSignedCa`값은 `false`로 한다.apiVersion: v1 kind: Service metadata: name: dex namespace: kube-system labels: app: dex component: identity dns: route53 annotations: domainName: dex.example.com service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:blahblah service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https spec: ports: # the ports that this service should serve on - name: https port: 443 targetPort: 5556 protocol: TCP selector: app: dex component: identity type: LoadBalancer loadBalancerSourceRanges: - 0.0.0.0/0staticClients / example-app는 Dex에 포함된 예제 프로그램이다. 이를 이용하면 웹 브라우저를 통해 GitHub에 인증하고 토큰을 내려받을 수 있다. DailyHotel/kid 등의 도커 이미지를 사용하면 쉽게 띄울 수 있다. kube-aws는 이 예제 프로그램을 띄우지 않기 때문에 직접 올려야 한다.apiVersion: v1 kind: Service metadata: name: kid namespace: kube-system labels: app: kid dns: route53 annotations: domainName: "kid.example.com" service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:blahblah service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https spec: ports: - name: https port: 443 targetPort: 5555 protocol: TCP selector: app: kid type: LoadBalancer loadBalancerSourceRanges: - 사무실IP/32 --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: kid namespace: kube-system spec: replicas: 1 template: metadata: labels: app: kid spec: containers: - name: kid image: dailyhotel/kid:latest livenessProbe: tcpSocket: port: 5555 timeoutSeconds: 120 ports: - containerPort: 5555 env: - name: CLIENT_ID value: example-app - name: CLIENT_SECRET value: ZXhhbXBsZS1hcHAtc2VjcmV0 - name: ISSUER value: https://dex.example.com - name: LISTEN value: http://0.0.0.0:5555 - name: REDIRECT_URI value: https://kid.example.com/callback이때 example-app의 REDIRECT_URI는 Dex의 REDIRECT_URI와는 다르다는 점에 주목하자. 옵션의 이름이 비슷하기 때문에 헷갈릴 수 있다. 또한 CLIENT_ID와 CLIENT_SECRET은 cluster.yaml 중 GitHub connector 설정이 아닌 staticClients 설정에서 쓴 값이라는 점도 눈여겨볼 필요가 있다.이 정도만 주의하면 dex를 설치하고 설정하는 것은 어렵지 않다. 이제 인증하는 방법을 알아보자.인증하기웹브라우저로 kid에 방문해서 토큰을 받는다. 첫 화면에서 Login 버튼을 누른 후 GitHub 로그인을 하면 토큰이 나온다.GitHub Public profile 메뉴로 가서 Public email 설정을 확인한다. 공개 이메일이 없다면 하나 추가한다. 로그인시 사용자 아이디로 쓰기 위함이다.kubeconfig 파일을 열고 kubeconfig 파일을 열고 MY_PUBLIC_GITHUB_EMAIL에는 GitHub 공개 이메일 주소를 적고 VISIT_KID_EXAMPLE_COM_AND_GET_TOKEN에는 앞서 받은 토큰을 적는다.apiVersion: v1 kind: Config clusters: - cluster: certificate-authority: credentials/ca.pem server: https://MY_KUBE_CLUSTER name: kube-aws-cluster contexts: - context: cluster: kube-aws-cluster namespace: default user: MY_PUBLIC_GITHUB_EMAIL name: kube-aws-context users: - name: MY_PUBLIC_GITHUB_EMAIL user: token: VISIT_KID_EXAMPLE_COM_AND_GET_TOKEN current-context: kube-aws-context인증 파일의 설정이 정확한지 확인하려면 kubectl --kubeconfig=./kubeconfig version을 실행해보자. 아래와 같이 Client/Server의 버전이 둘다 나오면 정상이다.$ kubectl --kubeconfig=./kubeconfig version Client Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.1", GitCommit:"b0b7a323cc5a4a2019b2e9520c21c7830b7f708e", GitTreeState:"clean", BuildDate:"2017-04-03T20:44:38Z", GoVersion:"go1.7.5", Compiler:"gc", Platform:"darwin/amd64"} Server Version: version.Info{Major:"1", Minor:"6", GitVersion:"v1.6.2+coreos.0", GitCommit:"79fee581ce4a35b7791fdd92e0fc97e02ef1d5c0", GitTreeState:"clean", BuildDate:"2017-04-19T23:13:34Z", GoVersion:"go1.7.5", Compiler:"gc", Platform:"linux/amd64"}참고 자료johnw188/dex-exampleKubernetes / Authenticating#데일리 #데일리호텔 #개발 #개발자 #개발팀 #기술스택 #도입후기 #일지 #경험공유 #Kubernetes #Github
조회수 2124

[인공지능 in IT] 인공지능 기업에서 하드웨어 엔지니어로 사는 법

인공지능 열풍이 거세게 불면서 이를 개발하는 엔지니어에 대한 수요도 폭발적으로 늘었다. 글로벌 IT의 중심이라 불리던 실리콘밸리를 포함해 중국에서도 막대한 자본을 바탕으로 엔지니어에게 기존 연봉의 2~3배를 제공하는, '인재 쟁탈전'에 돌입했다. 암흑기라 불릴 정도로 이공계 기피현상이 심했던 과거 일은 어느새 기억 속에서 잊혀질만큼 소프트웨어 엔지니어의 위상은 날이 갈수록 높아지고 있다. 심지어 교과 과정에 코딩 교육 의무화를 논의하는 단계다.AI 전문인력이란, 대게 원천기술을 개발하는 소프트웨어 엔지니어나, 학문적인 연구를 하는 리서치 인력을 많이 생각한다. 하지만, 실제로 핵심적인 역할을 담당하는 사람은 하드웨어 엔지니어다. 일반적으로 하드웨어 엔지니어라고 하면 많은 사람들이 납땜을 하고, 모터를 돌리는 등 '기계'를 만지는 엔지니어를 떠올리지만(물론 이 역시 하드웨어 엔지니어가 하는 일 중 하나다), 요즘처럼 인공지능이 적용되지 않은 곳이 없는 세상에서 하드웨어 엔지니어 역할은 그 이상이다.모두의 이해를 돕고, 인공지능 기술을 만드는 회사에서 하드웨어 엔지니어 역할이 얼마나 중요한지 알 수 있도록, 스켈터랩스 사내에서 시니어 하드웨어 엔지니어로 일하고 있는 오혁님과 질의응답 시간을 가졌다.< 스켈터랩스 오혁 하드웨어 엔지니어 >하드웨어 엔지니어로서 주로 하고 있는 일은?사용자가 실제로 만질 수 있는 기기, NVIDIA에서 생산하는 하드웨어 플랫폼과 운영체제(OS) 등을 막론하고, 소프트웨어 알고리즘을 실제 프로덕트로 구현하는 일을 한다. 이런 면에서 보면 소프트웨어 엔지니어링 서포터라고 생각할 수 있다. 하지만, 많은 사람이 더욱 더 심층적으로 인공지능을 연구할 수 있는 이유는 바로 하드웨어의 발전 때문이다.< '구글I/O 2017'에서 차세대 인공지능 전용 칩 'TPU'를 발표하는 모습, 출처: 구글 >예를 들어보자. 프로세서(CPU) 연산속도는 계속 빨라지고 있고, 그래픽 프로세서(GPU)가 프로세서 역할을 일부 담당하고 있으며, 대용량 메모리, 인공지능 가속기 등 모든 면에서 하드웨어 성능이 뒷받침되어야 한다. 하드웨어가 없다면, 소프트웨어 엔지니어가 열심히 만들어도 실제 구현하기가 어렵다. 간단하게 정리하면 하드웨어 엔지니어로서 리서처 역할을 하고, 눈으로 보이는 하드웨어도 만든다.일반인들이 알고 있는 것과 가장 큰 차이가 있다면?일반적으로 하드웨어 엔지니어라고 하면 많은 사람이 빛을 내거나, 모터를 돌리고, 부품을 조립하는 등 물리적인 제품을 만든다고 생각한다. 맞는 말이다. 하지만, 이 모든것을 컨트롤하기 위한 펌웨어, 미들웨어, OS, 디바이스 드라이버 등 소프트웨어가 돌아가기 바로 직전 단계까지, 하드웨어 엔지니어가 담당한다. 또한, 실제 제품 구현도 우리가 맡는다. 때문에, 놀랍게도 하드웨어 엔지니어도 소프트웨어 엔지니어의 전유물이라고 여겨지는 코딩을 많이 한다.< 'GTC 2017'에서 엔비디아 젠슨 황 CEO가 고성능 GPU 아키텍쳐 '볼타'를 발표하고 있다, 출처: 동아일보 >인공지능 붐이 일어나면서 어떤 점이 달라졌는가?인공지능 열풍이 불기 전 하드웨어 엔지니어는 제품을 목적에 맞게 실행하는 역할을 주로 했다. 제품을 구동되기 위해서는 대부분 순차적으로 프로세스를 진행한다. 땜질하고, 코딩하고, 각 부품에 연동하면 완성되는 형태다. 그러나, 지금은 이런 기본적인 프로세스 외에도 기계학습에 대한 알고리즘이나 인공지능 기술 전반에 걸쳐 소프트웨어적인 기술을 이해하지 못하면 절대로 원활하게 제품을 구현할 수 없다. 소프트웨어 엔지니어링을 서포트하는 역할에서 벗어나 인공지능 기술에 들어가는 소프트웨어가 어떻게 구현되는지 이해해야 적합한 제품을 만들 수 있기 때문이다.심지어 이제는 펌웨어를 코딩하는 것도 예전과 많이 달라졌다. 결과물만 놓고 보면, 모터를 돌리는 것은 같을 수 있다. 하지만, 예전에는 로봇 팔을 만들어 무언가를 잡기 위해, A 모터와 B 모터를 순차적으로 돌려서 잡는 것에 그쳤다면, 이제는 기계학습을 적용해 어떤 종류의 컵이라도 스스로 알아서 잡을 수 있도록 제작한다. 하나하나 펌웨어로 낮은 레벨에서 구현하는 것이 아니라, 로봇팔이 잡았을 때 이에 맞는 조건을 제공, 코드 하나로 학습하는 커스터마이징을 적용하는 것이다.< 국산 복강경 로봇수술기기 '레보아이'의 모습, 출처: 동아일보 >인공지능 기업의 하드웨어 엔지니어로서 가장 재미있는 점은?인공지능을 적용하면서 굉장히 재미있는 것을 많이 시도할 수 있다. 아이디어를 내고 무언가를 만들고 싶다면, 최종 제품으로 가는 길에 있어 여러 옵션을 선택할 수 있기 때문이다. 다시 말해 인공지능 소프트웨어를 통해 기존과 다른 방식으로 문제에 접근하고, 이를 해결할 수 있다는 점이다. 비유하자면, 지금까지 손으로 직접 돌리는 드라이버를 사용했다면, 이제는 앞의 부품을 언제든 바꿔낄 수 있는 전동드릴을 사용하고 있다고 보면 된다.반대로 힘든 점은 무엇인가?아무래도 인공지능이 너무 핫하다 보니 계속해서 새로운 기술이 등장한다. 인공지능 업계에서 종사하는 모든 사람들도 마찬가지겠지만, 신기술을 공부하고 연구해야 좋은 제품을 만들 수 있다. 또한, 하드웨어 엔지니어들이 열심히 만든 결과물이 너무나도 빠른 기술 발전속도로 잠시 거쳐가는 것에 불과할까 걱정되기도 한다.그럼에도 앞으로 기대되는 점은 무엇인가?하드웨어 엔지니어로서 세상을 이롭게 하는 제품을 더 많이 제작할 수 있다는 점이다. 인공지능 기술이 발전함에 따라 인간이 못 하는 영역도 조금씩 다가서고 있다.개인적으로는 로봇 쪽에 관심이 많다. 기술 발전속도가 빨라지는 만큼 다양한 제품이 등장해 사람들의 삶에 많은 혜택을 줄 것으로 기대한다. 예를 들면, 청각 장애인을 위해 음성인식을 시각적으로 바꿔주는 제품이나, 거동이 불편한 독거노인을 돕기 위한 기술 등이 있다. 삶을 윤택하게 해주는 기술을 개발하는 것이 점점 더 쉬워지고 있다는 점에서 많이 기대하는 중이다. 어떤 형태가 될 지는 예측할 수 없지만, 지금 단계에서는 소프트웨어든 하드웨어든 커다란 인공지능 플랫폼을 만들고 있다고 생각한다.이호진, 스켈터랩스 마케팅 매니저조원규 전 구글코리아 R&D총괄 사장을 주축으로 구글, 삼성, 카이스트 AI 랩 출신들로 구성된 인공지능 기술 기업 스켈터랩스에서 마케팅을 담당하고 있다#스켈터랩스 #기업문화 #인사이트 #경험공유 #조직문화 #인공지능기업 #기술기업 #하드웨어엔지니어
조회수 1040

Rxjava를 이용한 안드로이드 개발

Overview브랜디는 현재 2.0 기반 Android 버전입니다. Main Thread와 Sub Thread 사이의 ANR를 방지하려고 Volley, Otto Bus Library를 사용해서 백엔드 서비스(back-end Service)를 연동하고 있습니다. 이제 3.0 개발로 더 좋은 백엔드 서비스 기능을 만들려고 합니다. (기반 작업은 이미 완료했습니다.) 다만 3년 동안 브랜디 앱을 개발하면서 느꼈던 고통과 피로를 ‘제발’ 줄여보고 싶어서 브랜디 3.0에서는 Retrofit2 와 RxJava, Lambda 표현식을 사용하기로 했습니다. RxJava(Reactive programming)는 가장 추천하고 싶은 것 중 하나입니다. 우리는 함수형 리액티브(반응형) 프로그램이라는 표현으로 자주 마주치곤 하는데요. 주로 옵저버 패턴(Observer pattern)을 대체하기 위해 사용합니다. 단순히 데이터를 넘기고 마무리하는 건 옵저버 패턴으로도 충분하지만 대부분의 문제는 이벤트들을 묶어서 사용할 때 생깁니다.1) RxJava는 이벤트에 대한 조건 처리나 실패 처리, 리소스 정리에 대비해 사용합니다. 기존 방식의 명령형 리액티브 접근 방식을 사용하면 복잡함이 지속적으로 증가하는 반면, 함수형 리액티브 프로그래밍은 효율을 크게 높일 수 있습니다. 몇 가지 예제와 함께 살펴보겠습니다. Android에 직접 사용해보기새로운 프로젝트를 생성한 후, 아래와 같이 build.gradle 파일을 수정해봅시다. (JDK 1.8 설치 필수) apply plugin: 'com.android.application' android {    compileSdkVersion 26   defaultConfig {        applicationId "kr.co.brandi.myapplication"        minSdkVersion 21        targetSdkVersion 26        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }   //추가된 부분 1      compileOptions {        sourceCompatibility JavaVersion.VERSION_1_8        targetCompatibility JavaVersion.VERSION_1_8   }  } dependencies {    implementation fileTree(dir: 'libs', include: ['*.jar'])       //추가된 부분2    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'    implementation 'io.reactivex.rxjava2:rxjava:2.1.3'      implementation 'com.android.support:appcompat-v7:26.1.0'    implementation 'com.android.support.constraint:constraint-layout:1.0.2'    implementation 'com.android.support:design:26.1.0'    testImplementation 'junit:junit:4.12'    androidTestImplementation 'com.android.support.test:runner:1.0.1'    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' } 이제 람다 표현식과 RxJava를 사용할 준비가 되었습니다.Flowable.just("Hello World").subscribe(new Consumer() {    @Override   public void accept(String s) throws Exception {        Log.v(tag, s);   }  });   Flowable.just("Hello World !").subscribe(s -> Log.v(tag, s)); 간단한 생성자와 결과를 출력하는 부분입니다. 두 번째 subscribe는 람다 표현식으로 인터페이스를 생성하지 않더라도 첫 부분과 동일하게 결과물을 얻을 수 있습니다.2) 이제 RxJava에서 간단한 필터링으로 간편하게 데이터를 가공하는 능력을 확인해보겠습니다. 아래 코드는 기본적인 List 의 값을 출력하는 부분입니다.List valueList = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);   for (int data : valueList) {    String result = "value " + data;    Log.v(tag, result);  } Flowable.fromIterable(valueList)        .map(new Function() {            @Override            public String apply(Integer data) throws Exception {                return "value : " + data;            }        })        //.map(data -> "value : " + data)        .subscribe(data -> Log.v(tag, data)); 위의 코드에 조건을 추가해 ’짝수’만 출력하겠습니다.for (int data : valueList) {    if ((data % 2) == 0) {        String result = "value " + data;        Log.v(tag, result);    }  } Flowable.fromIterable(valueList)        //.filter(data -> {        //      return (data % 2) == 0;        //})        .filter(data -> (data % 2) == 0)        .map(data -> "value : " + data)        .subscribe(data -> Log.v(tag, data)); 위와 같이 데이터 가공은 순차적으로 진행되고, 여러 함수로 간단하게 처리할 수 있습니다. 원하는 데이터 가공을 위해 filter, map 등의 함수들을 순차적으로 이어 붙일 수도 있습니다.위에서 보여드린 RxJava는 간단한 예시이기 때문에 RxJava 의 기능을 좀 더 보여드리겠습니다.String[] data1 = {Shape.HEXAGON, Shape.OCTAGON, Shape.RECTANGLE};  String[] data2 = {Shape.TRIANGLE, Shape.DIAMOND, Shape.PENTAGON};   Flowable source =        Flowable.combineLatest(                Flowable.fromArray(data1)                        .zipWith(Flowable.interval(100L, TimeUnit.MILLISECONDS), (shape, notUsed) -> Shape.getId(shape)),                Flowable.fromArray(data2)                        .zipWith(Flowable.interval(150L, 200L, TimeUnit.MILLISECONDS), (shape, notUsed) -> Shape.getSuffix(shape)),                (id, suffix) -> id + suffix);   source.subscribe(s -> Log.d(getThreadName(), s)); CombineLatest() 함수를 이용해 두 개의 스트림을 하나로 처리하는 방법을 보여 드렸습니다. 각각의 스트림은 interval 함수를 시간 간격으로 data1과 data2 배열의 개수만큼 반복하여 처리하는 로직입니다. 서로 다른 두 스트림은 마지막 데이터를 가지고 있으며 새로운 데이터가 나올 때마다 하나의 스트림으로 출력됩니다.마블 다이어그램 3)결과Conclusion만약 RxJava를 이용하지 않고 두 개의 TimerTask를 이용해서 코딩했다면 결과는 같았을지도 모릅니다. 이제 RxJava를 알기 때문에 다시는 TimerTask를 이용해서 코딩할 일은 없을 겁니다. 알면 알수록 다양한 기능을 갖추고 있는 RxJava! 이제 브랜디 상용화 버전에 사용할 수 있게 다시 개발의 숲으로 들어가겠습니다. 그럼 이만. 1)함수나 네트워크 호출의 비동기 응답 2)Java 8 람다 표현식 자세히 살펴보기, 2018.03.09. 3)RxJava on Android 글고재성 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 923

Top Five Games Made in Seoul

 Based in a country seeing huge growth in its video game sector, Korean game studios have been releasing big hits in the past few years. As seen in our previous post, the Top Ten Things to do in Seoul for Gamers, Koreans of all ages have been diving headfirst into gaming culture. Mostly focused on digital gaming like mobile and PC, the gaming industry has been growing at an annual rate of 4.3% (Statista). Although the most popular games by far are MMORPGs, we tried to diversify the list for all types of players to enjoy.  MapleStory  MapleStory - Source: maplestory.nexon.net  MapleStory has been around for years and only continues to be a huge favorite in Korea and around the world. An extremely social game, it sees players work to improve their own character’s skills while chatting, looting and even getting married to others. The 2D element gives the game an almost retro vibe, even though it has been updated many times, including the release of a sequel. Create your avatar, choose your class and find out how to fulfill your quest by defeating the Black Mage.   Ragnarok Online  Ragnark Online - Source: mmoculture.com  You guessed it, another tried and true MMORPG. Based on a comic of the same name by Lee Myung-jin, this 3D game features a constantly changing timeline that players must interact and adapt with within the specific world. The key part of the game is choosing the “job” of your character. With that choice come make-or-break strengths and weaknesses that can determine the gameplay for you. Starting at 13 but growing to 50 classes, the choice is daunting but crucial as your job can change as well. Whether you want to try out the newest sequel, the mobile version, or even watch the animated TV series, Ragnarok Online is definitely one to check out.   Blade & Soul  Blade and Soul - Source: www.bladeandsoul.com  Developed by one of the most notable studios in Korea, NCSOFT, this fantasy martial arts game was only released in Western countries 2 years ago, but had been out in Korea and Japan since 2012. A super renowned character customization system gives the game an update from the more traditional fighting style games. There are four playable races that reference the four Chinese Symbols of Azure Dragon, Vermillion Bird, Black Tortoise and White Tiger. Definitely a must for fans of combat-driven games.   ANIPANG  ANIPENG - Source: anipang-for-kakao.en.softonic.com  Finally a change of pace! ANIPANG is a mobile puzzle game, and also the first Korean game to reach 20 million downloads. Filled to the brim with squishy animal faces, this match-3 style game can be enjoyed alone or by competing with friends. Whether it’s killing time waiting for the bus or just wanting to beat that one tricky level, ANIPANG can be played anywhere at any time.   Lineage  Linage - Source: mmogames.com  Rounding out this list is Lineage, a video game series released in the 90s and still receiving sequels and spin-offs to this day. Taking you back to medieval times, this game is one of the most successful MMORPGs to date. The realistic siege warfare and constant lore updates makes it a fun and addictive way to pass the time. The mobile release of the game broke records and had fans eager to play, so don’t miss out. 
조회수 2819

Retrofit2 로 전환

Android 와 NetworkAndroid 에서 Network 라이브러리들은 다양하지만 근 1년 사이에 주로 사용되는 라이브러리들이 점차적으로 적어지고 있습니다.오늘은 그 중에서 Retrofit 에 대해 이야기 하고자 합니다.토스랩의 Android Network Library1. Spring-Android, Retrofit 그리고 Retrofit2토스랩은 총 3개의 네트워크 라이브러리를 사용하였습니다. 초창기에는 AndroidAnnotations 에 연동되어 있는 Spring-Android 를 사용하였습니다. 하지만 다중 쓰레드 환경에서 동일한 Request 객체를 사용하면서 저사양 단말에서 문제로 두각되기 시작하였습니다.그래서 Retrofit 으로 2015년 중순쯤 전환을 하였습니다. 그러다 2016년 초 Retrofit2 가 정식 배포가 되면서 자연스럽게 Retrofit2 로의 전환이 대두되기 시작하였습니다. 전환의 이유는 내부의 네트워크 모듈에 대한 Refactoring 이었는데 그와 동시에 Retrofit2 로의 전환도 함께 진행되었습니다.2. 이슈들What the CALL기존의 Retrofit 은 200~399 에러에 대해서는 정상적인 Body 를 반환하고 400 이상의 경우에는 Typed Exception 형태로 로직을 진행하였습니다. 하지만 이정도로는 Response Status 나 Header 정보를 알기에는 추가적인 로직이 필요로 하였습니다. 물론 Success 케이스에도 마찬가지이긴 하였습니다.이는 Retrofit 의 기본적인 목적에 부합되지 않는다는 문제가 있었습니다. Retrofit 의 가장 기본적인 목적은 Okhttp 의 상위 구현체로써 쉽게 Request 와 Response 를 구현한다는 것입니다. 손쉬운 구현이 필요한 정보를 제외시킨다는 것은 별개의 문제이기 때문입니다.그래서 Retrofit2 에서는 Call 객체를 통해서 Request 와 Response 에 적용된 Header, StatusCode, Body 등을 직접 접근 할 수 있도록 인터페이스를 추가하였습니다.이 객체는 불변성을 가지고 있기 때문에 Getter 만이 존재하며 Request 에 필요한 정보는 다른 부분에서 적용되어야 함을 명시하셔야 합니다.Call 객체의 적용Call 객체를 적용하는 과정에서 2가지의 이슈가 있었습니다.Interface 의 모든 Return Value 를 Call 로 전환할 것Request Error 를 직접 핸들링 하도록 수정해야 함이 2가지 때문에 여러가지가 연쇄적으로 수정되어야 했습니다.먼저 수정과정을 설명하기 앞서 Jandi 앱의 Network 통신 전제조건에 대해서 설명해드리도록 하겠습니다.Jandi 앱은 모든 Network 통신은 Current Thread 에서 한다는 것을 전제로 합니다. 이는 MainThread 에서의 통신이 아니라 호출자의 Thread 를 따라간다는 것을 전제로 하고 있습니다. 또한 이를 위해 Reponse 반환, Error Handling, 세션 자동 갱신을 위해 Generic 으로 선언된 Facade 용도의 Wrapper Class 를 별도로 두고 있습니다.따라서 수정해야할 1,2 번을 위해 아래와 같은 수정을 하였습니다.Facade Class 내에서 성공여부를 직접 파악한다.성공시 Return Value 를 직접 반환할 수 있도록 한다.실패시 Status, Response 정보를 이용하여 throw Exception 을 한다. (세션 정보를 갱신 로직은 당연히 포함되어 있습니다.)그래서 아래와 같은 코드 형태가 되었습니다.Response response = apiExecutor.execute(); if (response.isSuccessful()) { RESULT object = response.body(); retryCnt = 0; return object; } else { // 400 이상 오류에 대해 처리 return handleException(apiExecutor, response, null); } Network 통신 과정에서의 Exception 이 나는 경우는 2가지 입니다.기기의 Network 자체가 끊겨 있거나 비정상인 경우Response 의 Parsing 과정에서 오류가 발생한 경우Annotation 의 변화Annotation 의 가장 큰 변화는 DELETE 였습니다. 기존의 Retrofit 에서는 DELETE 요청은 GET 방식으로 가능하였습니다. 즉 POST 처럼 Body 를 설정할 수 없게 되어 있었습니다. 따라서 DELETE 를 쓰기 위해서는 별도의 Custom HTTP Annotation 을 설정 할 적용하여야 했습니다.Retrofit2 에서는 이런 경우에 대비하기 위해 @HTTP 를 개방하였습니다. @HTTP(path = "{url}", method = "DELETE", hasBody = true) 와 같이 사용해야만 Custom HTTP Method 를 적용하실 수 있습니다.Jackson2-Converter 대응Jackson2-Converter 의 이슈는 최근에서야 알게 되었습니다. Jandi 앱은 그동안 Jackson 1.x 를 사용하였고 최근에서야 Jackson2 로 전환을 하였습니다.그 과정에서 Retrofit2 의 converter-jackson 라이브러리를 사용하려 하였으나 중대한 문제가 있었습니다.Retrofit2 에서 Reqeust Body 의 Serialize 는 메소드의 참조변수로 선언된 클래스만 지원하며 상속한 자녀클래스를 넣어도 부모 클래스의 결과만을 리턴 하는것이었습니다. (gson 과 여타 converter 에 대해는 해당 이슈에 대해 파악해보지 않았습니다).이를테면 아래와 같은 경우입니다.interface Api { @PUT("/profile") Call modifyProfile(@Body Profile profile); } public class Profile {} public class NameProfile extends Profile{ String name; } public class PhoneProfile extends Profile{ String phone; } // using case api.modifyProfile(new NameProfile("Steve")); 허나 아래와 같은 상황이 펼쳐집니다.// expect {"name":"Steve"} // actual {} 해당 문제는 Converter-Jackson 의 이슈이기 때문에 위와 같은 상황이 예상된다면 별도의 Converter.Factory 를 선언하여 사용하시기 바랍니다.OkHttpClient 생성 이슈Okhttp 에 여러가지 기능이 추가되었습니다. 그중 잔디가 사용 중인 목록입니다.okhttp-logging-interceptorauthenticatorCutome SSL이런 이유 때문에 OkHttpClient 를 직접 생성하여 사용 하고 있습니다.처음에는 OkHttpClient 를 모든 API 호출시 새로 생성하도록 하였습니다. 헌데 TestCode 가 200회가 넘어가면 File IO 를 너무 많이 사용했다는 오류가 계속적으로 발생하였습니다.이 오류가 단순히 File IO 가 많아서 라는 메세지 때문에 처음에는 Database 에 대한 오류인 줄 알고 Memory Cache 작업과 테스트코드 개선작업을 하였으나 정상 동작이 되지 않았습니다. (테스트 코드 1회에 평균 2번의 API 통신과 2회의 DB 처리를 합니다.)그 와중에 기존의 테스트 코드는 정상 동작하는 것을 보고 Retrofit2 작업을 진행한 branch 만의 문제임을 깨달았습니다.현재는 OkhttpClient.Builder 를 통해 생성한 1개의 OkhttpClient 만을 재사용하도록 변경하였습니다.Network Retry 시 동작 변경Retrofit2 는 Call 객체를 이용하여 동일한 정보로 재요청을 할 수 있도록 지원하고 있습니다. 하지만 이에 대한 제약이 하나 있습니다. 이미 Network IO 가 끝난 경우 Retrofit2 는 Call 객체를 복사하여 재사용할 것을 가이드 하고 있습니다. 그래서 재요청시 다음과 같이 코드를 작성하셔야 합니다.Call call = action0.call(); if (!call.isExecuted()) { return call.execute(); } else { return call.clone().execute(); } OkHttp3 의존성Okhttp 를 사용하는 타 라이브러리가 있다면 Okhttp3 의존성을 가지고 있기 때문에 이에 유념하셔야 합니다.3. 정리Retrofit1 -> Retrofit2 로 변경하는 과정에서 다양한 이슈를 발견하였습니다.Return Value 수정Exception 처리 강화Annotation 수정Request-Response Converter 수정OkhttpClient 재사용 정의재요청 처리에 대한 validation 추가OkHttp3 의존성Retrofit2 로 변경에 있어서 가장 큰 핵심은 Call 이라는 객체라고 할 수 있다는 것입니다.이 객체는 Request 에 대한 동작 제어(cancel, retry 등), Request-Response 의 독립성 보장, 그에 따라 각각의 정보에 대한 접근 등을 보장하게 됩니다.Retrofit2 는 그외에도 Okhttp3 와 다양한 플러그인 지원하고 있습니다. 요청-응답에 필요한 Body 의 변환툴 (Converter-xxx), EndPoint 에서 접근하는 Call 객체에 대한 다양한 툴 (CallAdapter-xxx)현재 Retrofit1 에서 잘 동작하고 있고 의도대로 흐름제어를 하고 있다면 Retrofit2 로 옮겨갈 이유는 없습니다. 하지만 변경을 하고자 한다면 이러한 영향도가 있을 것임을 공유해드렸습니다.참고하면 좋은 Slidehttps://speakerdeck.com/jakewharton/simple-http-with-retrofit-2-droidcon-nyc-2015Jake Wharton’ Retrofit2Presentation 영상#토스랩 #잔디 #JANDI #개발 #개발자 #인사이트 #경험공유
조회수 2152

스켈티인터뷰 / 스켈터랩스의 N잡러 엄단희 님을 만나보세요:)

Editor. 스켈터랩스에서는 배경이 모두 다른 다양한 멤버들이 함께 모여 최고의 머신 인텔리전스 개발을 향해 힘껏 나아가고 있습니다. 스켈터랩스의 식구들, Skeltie를 소개하는 시간을 통해 우리의 일상과 혁신을 만들어가는 과정을 들어보세요! 스켈터랩스의 N잡러 엄단희 님을 만나보세요:)사진1. 스켈터랩스의 N잡러 엄단희 님Q. 자기소개를 부탁한다.A. 스켈터랩스에 입사한 지 이제 8개월 정도 된 신입 소프트웨어 엔지니어, 엄단희다.Q. 스켈터랩스에서 어떤 업무를 맡고 있는가.A. 현재는 아이리스(Iris) 팀에 소속되어있다. 아이리스 팀은 맥락 인식(Context Recognition) 기술을 기반으로 SDK를 비롯한 여러가지 서비스 출시를 준비하고 있는데, 사실 지금은 레고(L.ego)팀이 준비하는 신제품인 스마트 미러 샘(Samm) 개발 업무가 주요 업무이다. 샘은 스켈터랩스가 가지고 있는 맥락 인식 기술 뿐만 아니라 음성, 얼굴, 제스처 인식을 비롯한 대화형 엔진이 모두 집약된 인텔리전트 디바이스(Intelligent Device)다. 여러 기능이 하나의 디바이스에 구현된 만큼, 샘은 다양한 모듈로 나누어져있다. 예를 들어 센서 정보를 모으는 모듈과 그 정보를 처리하는 모듈, 처리한 내용을 보여주는 UI 모듈 등이 있는데, 나는 이러한 모듈들을 gRPC 또는 bluetooth 등을 통해 서로 통신할 수 있도록 해주는 작업을 주로 진행했다. 최근에는 샘의 구매자에게 필요한 샘 어플리케이션 개발을 진행하고 있다. 아이리스 팀 관련해서는 파이어베이스(Firebase) 관련 작업을 서포트한 적이 있고, 얼마 전에는 스켈터랩스 웹사이트 개발에 참여하기도 했다.Q. 맡고 있는 업무의 가짓수가 많아 보인다. 한번에 여러 개의 프로젝트를 진행하는 것이 어렵진 않나.A. 쉽다고 말하기는 힘든 것 같다. 여러 업무에서 동일한 지식이 요구될 때도 있지만, 기본적으로 하나의 일을 처리하기 위해 집중하고 있다가 다른 업무로 전환할 때, 그 업무를 위한 나의 베이스를  바꾸는 등의 일들이 녹록치 않다. 처음에는 무엇보다 일의 우선순위를 정하는 것이 가장 버거웠다. 사실 업무마다의 기한이 정해져 있으면 당연히 급한 업무를 먼저 처리할텐데, 우리 회사는 그보다는 본인이 직접 업무량을 조정해서 기한을 정하고 처리하는 편이다. 그래서 하나의 일을 쪼개고 쪼개어, 그 중에서도 가장 빨리할 수 있는 일부터 먼저 처리하는 나만의 업무 프로세스를 만들고 있다. ‘빨리 할 수 있는 일'이라고 해서 마냥 쉬운 일을 말하지는 않는다. 그 때마다 내게 가장 맞는 일, 내가 가장 준비되어 있는 일을 자연스럽게 추려내어 업무 효율을 높이려고 한다.Q. 스켈터랩스에 어떻게 입사하게 되었는지 궁금하다.A. 재작년, 앤드비욘드라는 회사에서 인턴으로 근무했다. 당시 스켈터랩스가 앤드비욘드와 함께 개발중이던 스마트 포스(POS)기, GABE 프로젝트를 진행하며 한남동에서 같은 사무실을 쓰고 있었다. 그 프로젝트 팀에서 파견직처럼 일을 하게 되었는데, 가장 놀란 점은 ‘사람'이었다. 이렇게 누구 하나 빠짐 없이 개발을 잘하는 사람들이 모여있는 곳에서 개발하는 것은 처음이었다. 학교에서는 나름 ‘나도 잘하는 편이지 않을까’ 생각했는데 여기 와서 한없이 부족하다는 걸 깨달았다. 그런데 그렇게 부족한 신입 인턴임에도 불구하고 모두가 나를 평등하게 대해주셨고 개발 관련해서도 많이 배울 수 있었다. 덕분에 스켈터랩스는 내게 아주 좋은 이미지로 남아있었는데, 작년 스켈터랩스의 CTO인 조성진님께 오퍼를 받아서 스켈터랩스 인턴으로 일과 학업을 병행하다가 올해 정직원으로 입사하였다.Q. 인턴으로 일을 하며 학업과 병행했는지 몰랐다. A. 학교 스케줄을 우선시할 수 있도록 회사가 많이 배려해주었다. 다행히 학교가 회사와 멀지 않은 거리에 위치하기도 한다. 그래서 학교 수업은 주 2-3일 정도, 오전 타임으로 몰아서 구성했다. 시험기간이라고 하면 팀원들이 모두 나서서 ‘어서 집에 가서 공부부터 해라'라며 조언해주시고 업무적으로도 많이 도와주신 덕에 학업에 대한 지장 없이 일을 할 수 있었다.Q. 인턴을 마치고 정직원으로 입사했다면, 인턴 시절과 현재를 비교할 때 업무적으로 무엇이 가장 다른가.A. 우리 회사는 매 분기마다 분기의 목표 설정과 유사한 OKR(Objectives and Key Results)을 정하고, 이를 완료하는 방식으로 일을 진행한다. OKR에서 중요도가 높은 업무는 P0로, 가장 중요도가 낮은 업무는 P2로 표기한다. 인턴으로 처음 입사했을 때는 P1~P2 레벨의 자잘한 이슈들을 처리하는 업무가 많았다. 정직원이 되고 나니, 그만큼의 지식과 스킬이 쌓인 만큼 P0의 업무들을 조금 더 맡게되었다. 그러나 전반적인 업무의 결은 유사하다. 다만 확실히 책임감은 늘어났다고 생각한다. 인턴일 때는 ‘난 인턴이니까 몰라도 괜찮겠지?’와 같은 마인드가 있었는데, 정직원이 된 지금은 ‘정직원이 이 정도는 알고 있어야겠지?'라고 생각한다. 덕분에 공부하는 양도 이전보다는 늘어났다.사진2. 파워 코딩 중인 단희 님Q. 최근 스켈터랩스가 여러 학교의 커리어페어에 다녀오면서 많이 들었던 질문 중 하나가 ‘인공지능을 전문적으로 공부하지 않았는데, 일을 할 수 있을까요?’였다. 혹시 이 질문에 대한 답변을 해줄 수 있을까.A. 나도 입사 때 면접을 보며 같은 질문을 던졌다. 입사해서 느끼는 점은 정말 인공지능에 관련된 개발 외에 다른 영역에서도 개발해야 하는 일이 정말 많다는 점이다. 때문에 인공지능 분야를 잘 모른다고 해서 (물론 알면 좋지만) 막연한 두려움은 갖지 않아도 좋다. 물론 좀 더 코어한 부분을 개발할수록 인공지능 공부의 필요성을 점점 느끼게 된다. 이러한 기술적 갈증은 사내에서 열리는 테크톡(Tech Talk)과 같은 세미나를 통해 어느 정도 해결할 수 있으며, 업무를 위해 관련 공부가 필수적이라면 팀별로 스터디가 진행되기도 한다. 실제로 다른 팀에서는 주기적으로 관련 논문을 스터디하고 그 지식을 공유하는 세션이 진행되고 있다.Q. 스켈터랩스 입사 후 가장 뿌듯했던 순간과 힘든 순간을 꼽는다면?A. 나는 내가 무언가를 직접 만들고, 그 결과물을 선보이는 과정을 좋아한다. 그래서 가장 뿌듯한 순간으로는 회사 웹사이트를 런칭했을 때를 꼽고 싶다. ‘웹' 특성 상 내가 짠 코드들의 결과를 바로 눈으로 확인할 수 있기 때문에 개발하는 재미도 있었고, 아무래도 회사를 대표하는 사이트라 많은 사람들에게 보여질 것이라 생각하니 더욱 자부심을 갖고 일할 수 있었던 것 같다. 그런 측면에서 나중에 샘을 런칭하게 될 날도 기대된다. 반면 가장 힘들었던 순간은 작년 블루투스 개발 관련 디자인 문서 작업을 진행할 때 였다. 일단 블루투스 기술도 잘 모르는 데다가 디자인 문서 자체도 제대로 써본 적이 없어 생소했다. 사실, 개발이 안 풀리고 막혀있을 때는 그 순간만 힘들 뿐 어떻게든 해결책을 찾고 결과물을 낼 수 있었다. 그런데 디자인 문서 작업은 내가 어떤 방향성을 취해야 하는지, 지금 하고 있는 과정이 맞는 것인지가 계속 의구심이 들었다. 하루종일 컴퓨터 앞에 앉아 있어도 결과물이 없으니 마음만 조급해지는 일도 많았다. 다행히 당시 리뷰를 해주신 조성진님 등 기타 다른 개발자분들의 도움으로 문서는 마무리지을 수 있었는데, 내 한계에 대해 반성하기도, 많이 배우기도 했다.Q. 스켈터랩스 게임동호회 회장을 맡은 것으로 알고있다. 게임동호회를 소개하자면?A. 먼저 오류부터 수정해야할 것 같다. 나는 현재 게임동호회 회장은 아니다. 사내 게임동호회인 ‘Game of Troll’은 한달에 한 두번 모여서 게임을 함께 하는데, 그 게임에서 꼴찌를 한 사람이 회장이 된다. 나의 경우 저번 달 클래시로얄 게임에서 꼴찌를 하여 회장을 맡았었다. 하지만 회장이 정한 게임으로 다음 회장을 뽑기 때문에 내가 자신있는 게임인 오버워치를 9월 게임으로 선정했고, 현재는 정태형 님에게 회장 자리를 넘겨주었다. 게임은 종류에 따라 사내 블루룸 또는 PC방에서 진행한다. 블루룸에는 플스와 닌텐도 등의 각종 게임기가 완비되어 있어, 토너먼트 식으로 철권을 하거나 마리오카트를 했었다. 또 휴대폰으로는 클래시 로얄을 함께 플레이하기도 한다. PC게임인 경우에는 저녁에 함께 피씨방에 가는데, 재미있는 점은 원래 저녁을 먹고 피씨방에 가다가, 피씨방에 가서 저녁을 먹는 걸로 바뀌었다는 점이다. 저녁먹는 시간이 아까워서다. 이렇게 피씨방에서 플레이한 게임들은 스타1, 스타2, 카운터 스트라이크 온라인2, 오버워치, 히어로즈 오브 스톰 등이 있다.처음 게임 동호회에 들어올 때만 해도 ‘같은 회사 사람끼리 게임을 하는 것이 과연 재미있을까'란 생각을 했다. 그런데 막상 게임을 같이 해보니, 회사에서 일할 때는 보이지 않았던 그 사람의 의외의 면을 발견하는 재미도 있는 것 같다. 개인적으로는 초등학교 때부터 게임을 워낙 많이 했던 탓에 스스로 ‘내 인생을 게임에 너무 낭비한 것이 아닐까'란 자괴감을 느낀적도 있는데, 다른 훌륭한 개발자의 게임 덕후스러운 면모를 보면서 ‘나만 이렇게 게임에 빠진 것은 아니었구나'하는 위안도 받을 수 있었다.사진3. 스켈터랩스의 게임동호회 Game of Troll의 뒷풀이 모습Q. 와우, 플레이하는 게임이 정말 많다. 단희님이 가장 좋아하는 게임을 그 중 꼽는다면?A. 나는 단연 오버워치다. FPS게임을 선호하는 편인데 그 중에서도 오버워치를 주로 플레이한다. 개인적으로 스토리가 재미있기도 하고, 팀플레이를 진행하며 합을 맞춰가는 맛이 있다. 무엇보다 사람끼리 대결하는 PVP로 진행을 하면 정말 짜릿함이나 즐거움이 배가 되는 것 같다. 물론 협동 게임인 만큼 팀플레이가 제대로 되지 않는다거나 비매너 유저들과 붙을 때는 기분이 아주 다운되는 경우도 있지만 말이다. 그럴 때는 ‘GTA5’ 또는 최근에 시작한 ‘데스티니 가디언즈'에서 PVE를 하며 마음을 진정시킨다. 물론 이것만 하면 지루하겠지만 오버워치와 적절히 번갈아가면서 하다보면 고유의 재미가 느껴진다.Q. SNS에 웹툰도 연재하고 있는 것으로 알고있다. 어떻게 웹툰 연재를 시작하게 되었는지.A. 어렸을 때부터 만화 그리는 것을 좋아했다. 내가 상상하는 이야기들을 만화로 풀어내는 것도, 그 날 있었던 일을 재미있게 연출해서 일기 대신 그림으로 하루의 기록을 남기는 것도 좋아했다. 그렇게 학교에서 있었던 재미있는 에피소드를 글과 그림으로 남기다 보니, 이걸 모두에게 공개하면 재미있지 않을까란 생각을 했다. 때마침 한창 페이스북 페이지가 유행이었는데, 그때부터 노트에 끄적거린 짧은 만화들을 올리기 시작하면서 현재의 인스타그램 웹툰까지 오게되었다.   사진3. 단희 님이 연재 중인 <초코롤의 코딩일기>, 인스타그램과 페이스북에서 만나볼 수 있다Q. 웹툰 소개를 부탁한다.A. 인스타그램에선 @sw_chocoroll, 페이스북에서는 <초코롤의 코딩일기>라는 제목으로 게재하고 있다. 취미생활 겸 하다 보니 정기연재는 아니다. 제목에서 드러나 듯 주로 코딩(개발) 이야기를 다루고 있는 생활툰이다. 생활툰의 특성상 어쩔 수 없이 주변인들에 대한 묘사가 많고, 에피소드가 없을 때면 웹툰을 그리기도 쉽지 않다. 약간 과장하더라도 실제 있었던 일들을 중심으로 작업하기 때문에, 업로드 전 꼭 등장 인물들에게 검수를 거치기도 한다. 웹툰 그리는 것이 생각보다 집중도를 요하는 작업인지라 보통 주말에 진행하는데, 그래서 평일에는 에피소드를 꼼꼼히 기록해두는 습관이 생겼다. 무엇보다 웹툰을 그리며 가장 많이 느꼈던 점은 내 인생에 대해서 조금 더 알게됐달까, ‘나’를 다시 보게 된 느낌이 있다. 내가 기록한 에피소드가 대부분 게임과 개발에 편중되어 있는 점을 보면서, 인생에서 많은 지분을 ‘게임', ‘개발' 이 두 가지에 할애하고 있다는 것을 새삼 알게되었다.  Q. 재능 부자, N잡러로 보인다. 게임에도 웹툰에도 이렇게 관심이 많았는데 어떻게 개발자의 진로를 선택하게 되었나.A. 이유는 생각보다 단순하다. 위에 웹툰에서도 그렸듯이 영화를 보면 꼭 대형 모니터를 여러 개 띄워놓고 멋지게 주인공을 돕는 해커들이 등장하지 않나. 게임을 많이 하게 되면서 자연스럽게 컴퓨터에 친숙해지기도 했고, 영화를 보면서 ‘나도 컴퓨터 관련 전공을 택하면 저렇게 멋있는 사람이 될 수 있지 않을까’라고 생각했던 것 같다. 다만 어떤 분야의 개발자가 될 것인가에 대한 고민은 많았다. 영화처럼 정보보안 쪽도 잠깐 발을 담갔지만 지금 당장 할 수 있는 분야는 아니라는 생각을 했고, 그 다음은 게임 개발자에 관심을 가졌다. 그런데 당장 게임 회사에 들어간다고 해도 꼭 내가 만들고 싶은 게임만 만들 수는 없다는 것을 알게 되서 보류했다. 나는 일단 스토리가 탄탄하고 재미있는 게임을 정말 사랑한다. 예를 들어 <화이트데이>라는 공포 게임을 정말 재밌게 플레이했었는데, 공포 요소도 한국 정서에 맞게 잘 구현되었으면서 미연시(미소녀 연애 시뮬레이션) 요소도 가미되어 신선한 느낌을 주었었다. 제일 중요한 스토리도 배경 시나리오부터 인게임 진행까지 반전에 반전을 거듭하며 게임이 끝나고도 생각해볼 여지가 많았다. 이런 게임을 만들고 싶지만 우선 희망 사항으로 남겨둔 상태이다. 그러다 우연히 입사한 스켈터랩스에서 훌륭한 선배 개발자들을 보며 ‘개발' 자체의 즐거움을 느꼈고 당장은 어떤 특정 분야에 국한하지 않고 순수한 개발 능력을 향상시키기 위해 노력하고 있다. 스켈터랩스에서 중요하게 다루는 인공지능은 특히 기술적으로 미래의 변화를 주도하고 있기에, 여러 방면에서 매우 배울 점도 많고 발전할 수 있는 것 같다.Q. 최근 몰두하고 있는 것이 있다면?A. 여전히 웹툰과 게임이다. 웹툰을 그릴 수록 기초적인 그림 실력이 부족하다는 것을 많이 느꼈다. 전문적으로 그림을 배워볼까 싶다. 그리고 유튜브에 게임 채널을 열어보려고 한다. 내가 관심있었던 모든 일은 기본적으로 ‘창작'과 ‘기록'의 맥락을 가지고 있다. 개발 또한 어떻게 보면 내가 짜는 코드를 통해 하나의 프로그램을 만들어 내는 역할이지 않나. 웹툰도 내 일상에 대한 기록이다. 나의 일상에서 가장 큰 관심사 중 하나인 게임을 기록하는 방법에 대해 고민했는데, 역시 동영상이 최고라는 결론에 도달했다. 유튜브에는 게임 영상을 편집하여 조금씩 선보이고 있다.Q. 진부할 수 있지만, 이 인터뷰의 마지막 질문이다. 개인적인 꿈을 얘기해줄 수 있나.A. 언젠가는 접어두었던 게임 개발자의 꿈을 꾸려고 한다. 1인 개발자로서 스토리와 작화, 개발을 모두 맡은 개발자 말이다. 그러기 위해서는 그림 뿐만 아니라, 유저의 마음을 사로잡을 수 있는 스토리와 촘촘한 개발력 또한 갖추어야 한다. 개발력은 일단 스켈터랩스에서 빵빵하게 키워놓고, 스토리와 작화에 관련된 역량을 조금씩 갖추어간다면 1인 개발자로서 내 이름을 건 게임을 출시할 날이 조만간 올 수 있지 않을까.#스켈터랩스 #사무실풍경 #업무환경 #사내복지 #기업문화 #팀원인터뷰 #팀원소개 #팀원자랑
조회수 1434

크몽 gulp 개선기

안녕하세요. 프론트개발자 bk입니다 :)이 글은 gulp사용법에 대한 글이 아닙니다. build 자동화 도구를 개선할까 말까 고민하는 개발자들을 위해 제 경험을 공유드리려 합니다.음... build 자동화 꼭 해야 해..?가 아닙니다. 크몽이라는 서비스가 어떤 개발 환경을 통해 만들어졌는지, 좀 더 편하고 효율적으로 개발 생활을 즐기기 위해 어떻게 개선을 했는지에 대한 개발 경험을 나눠 보려고 합니다.왜 이 작업을 하게 되었고 뭐가 그리 중요했는지, 크몽 개발 환경에서 gulp가 개선되어야 했던 이유에 초점을 맞추었습니다.시작에 앞서본격적인 gulp에 대한 이야기를 하기 전에 왜 내가 크몽에 입사하자마자 gulp를 개선해야겠다 마음먹었습니다.크몽에서의 개발크몽에 입사한 지 이제 3개월이 되었습니다. 회사의 개선사항, 변화가 필요한 것들에 대해선 반드시 말하는 스타일이라 크몽의 자유로운 분위기와 수평적 문화는 정말 만족했고 적응에 큰 어려움은 없었습니다.첫 주는 개발환경 laravel + vuejs 공부의 시간을 보냈고 둘째 주부터 이벤트 페이지를 맡아서 작업했습니다. 기존 사용하였던 개발환경과 크게 다르지 않아 크게 어려움 없이 이벤트 페이지 작업을 완료하고 배포가 되었습니다.호환성을 위한 es6 환경 필요성하지만 늘 그렇듯 다음 날 출근을 하니 버그가 날 기다리고 있었습니다. 조금 생소한 버그였습니다. script관련 오류였는데, 이전의 개발환경에선 babel, browserify가 거의 대부분의 script에 걸려있었기 때문에 습관적으로 es6로 작업했습니다. 결국은 es5스타일로 복구하여 수정했습니다. 곰곰이 생각해보니 호환성을 위해 es6를 사용하지 않는 것이 아닌 es6를 사용하도록 환경이 구성되어야 하는 게 맞는 것 아닌가 라는 생각했습니다.gulp가 개선되어야만 하는 이유크몽에 입사한 지 1주일 만에 다른 작업을 뒤로하고 회사에서 gulp만 외치고 다녔습니다. 다른 무언가 개선을 하려면 gulp가 필연적으로 개선이 되어야 했습니다. 기존의 개발환경은 파일 수정할 때마다 terminal에서 gulp를 명령어를 쳐야 했습니다. 주르륵 코드를 적고 한번 새로고침해서 짠하고 바뀌는 스타일이 아니라, 내가 짠 코드도 의심이 되어 자주 스텝별로 확인 스타일이라 이러한 현재 환경은 저와 정말 맞지 않았습니다.앞잡이(크몽의 프론트앤드 멤버) 챕터 회의 때 gulp개선을 요청하였고 모두에게 현재의 문제를 공유하고 gulp개선을 해야 하는 이유에 대해 설득 한 뒤 크몽개발팀 내 프로젝트 일정으로 잡히게 되었다.나 편하자고 시작한 작업, 내가 편한 건 팀원도 편하다우선 gulp 개선의 가장 큰 목적은 4가지였다.es6 및 최신 기술과 라이브러리를 사용하자gulp watch를 효율적으로 사용하자script태그와 style태그로 쓰는 것을 지양하자 (.js, .scss파일 많아지는 것에 대한 부담 가지지 말자)script, style, directory 구조를 기능별로 구조화시키자bk's PLANs그렇게 gulp는 이렇게 만들었습니다.기존 크몽 개발 환경에서 너무 확 바뀌진 않도록 개발자 분들과 협업하여 flow를 최대한 유지하며 작업했습니다. (공통 모듈, 유틸, 서비스 관련, 인증, 라이브러리, 이벤트, 구매 판매 트랙, sass)로 모듈을 나누어 bundle을 하였고 각각에 watch를 걸어 파일을 변경하면 자동으로 관련 모듈만 bundle이 실행되도록 작업했습니다.gulp를 위한 작업이었지만directory구조도 깔끔해지고 project도 좀 더 가벼워졌습니다. 계획은 거창했고 의욕은 앞섰지만 build 자동화 툴을 제대로 만져본 적이 경험이 없어서 (gulp, elixir, babel, browserify, stream) 작업과 공부를 병행하느라 예상보단 조금 더 시간이 걸렸지만 결과적으론 개선된 지금이 훨씬 개발하기 편해졌다.불필요한 작업이 습관이 되기 전에 개선을 실행했습니다.사실 크몽의 이전 개발환경에선 gulp가 크게 중요하지 않았지만, 크몽의 팀원이 많아지고 개발자도 많아지면 제가 아니더라도 크몽팀 누군가는 했을 작업이었습니다. 더 나은 환경이 분명히 존재하는데도 불구하고 기존의 불필요한 작업이 습관이 되어 개선을 망설이거나 하는 회사, 개발자가 많다고 생각합니다.개발자가 더 나은 환경에서 개발하는 걸 막자고 할 사람은 없을 것입니다. 그것이 입사한 지 1개월이 되든 10년이 되든 누가 말하든지 간에 말입니다. 갓 들어온 주니어 개발자의 말을 잘 들어주고 gulp 개선에 대한 필요성을 인정해주어 작업이 가능했던 것 같습니다.올바른 방향으로 문제 해결방법을 목표로 삼았습니다.앞으로의 bk의 계획이제 gulp가 완성이 됨과 동시에 directory 구조와 es6가 해결되었으니, 원래 가장 하려던 크몽의 코드 스타일, ESLint를 적용할 예정입니다. 그 후 vue2 마이그레이션 작업이 진행될 예정입니다.마무리작지만 하나하나 개선해 나가면 더 나아진 개발환경 구축이 되고 이런 작은 개선 사항들이 모여서 더 나은 크몽이 될 것이라 생각합니다.real _마무리이렇게 개발했던 경험을 블로그로 포스팅한 건 이번이 처음입니다.역시 글을 쓰는 건 어렵고 두서없었지만 build 자동화 툴에 대해 더 깊게 공부할 시간을 가지게 되어 좋은 경험이었습니다.글을 마무리 지으려니 어떻게 지어야 할지 모르겠네요.그래서 급 마무리 인사드립니다.이렇게 부족한 글 귀한 분들께서 읽어주셔서 감사합니다.다음 포스팅에는 크몽의 개발 조직문화 소개로 돌아오겠습니다.#크몽 #개발팀 #개발자 #개발문화 #경험공유 #인사이트

기업문화 엿볼 때, 더팀스

로그인

/