스토리 홈

인터뷰

피드

뉴스

조회수 3054

국내 스타트업 개발자들도 저녁이 있는 삶을 산다.

[대화 1]친구 A: 남편은 무슨일 해?아내: 어, IT회사 다녀.친구 A: 거기서 무슨일 하는데?아내: 개발자에요.친구 A: 아 그래? 그럼 퇴근 제때 못할텐데, 애들 키우기 힘들겠네.…[대화2]아내: 아니 그렇게(반바지) 입고 회사 가려고?필자: 음... 요즘 판교 쪽에서는 패피들은 반바지에 샌들 정도 신어줘야 인정받아..아내: 우리(금융회사)는 반바지 입는 사람은 생수 배달하는 사람 뿐인데. 갈아입고 가.금융기관에서 일하는 필자 아내와의 일상 대화 중 일부입니다. 대화는 짧지만 많은 의미가 함축되어있습니다. 우리 사회에서 금융권 직원이라 하면 말끔한 수트를 차려입고 아침부터 아메리카노 한잔 하면서 뭔가 중요한 딜을 성사시킬 것 같은 느낌이라면, IT개발자라 하면 그 금융권에서 사용하는 시스템 개발을 하위 위해 파견온 협력회사 직원과 그 회사에서 고용한, 소위 을, 병, 정 프리랜서들로 반바지에 좀 헝크러진 머리를 하고 밤늦게까지 그리고 주말에도 코딩하느라 제대로 씻지도 못하고 다니는 사람을 먼저 떠올립니다. 최근에 국내 유수의 게임 회사 한 곳에서만 세 명이 과로사하거나 업무 부담으로 회사에서 자살했다고 하니 그런 인식이 전혀 틀리지만은 않은 듯 합니다.미국에서는 개발자들이 대접은 잘 받지만 업무 난이도와 강도는 정말 높다고 합니다. 미국에서는 소프트웨어 개발자라고 하면 엄지손가락을 치켜 세우며 ‘6 digits’이냐고 물어보고들 합니다. 연봉이 $100,000 즉  1억 1,200만원 이상이냐고 묻는 것입니다. 연봉 10만 달러는 미국에서도 높은 편이지만, 소프트웨어 개발자들은 일반적으로 이를 상회합니다. 실리콘밸리에서는 개발자 대졸 초임이 10만 달러 정도 된다고 합니다.시가총액 상위 기업 대부분이 ICT 기업들이고 미국에서도 소프트웨어 개발 인력은 공급이 상당히 부족하니 그럴 수 밖에 없습니다. 공대중에서 최고라 하는 스탠포드와 MIT에서 최고 인기 전공은 단연 컴퓨터 사이언스라고 하는데, 대한민국에서는 인재들이 소프트웨어 분야를 기피하고, 이 분야가 더 열악해지는 악순환이 계속되고 있습니다. 자율주행 시스템, 암진단을 인간 의사보다 잘한다는 IBM 왓슨, 자산관리 로봇까지 가지 않더라도 뱅킹, 콜센터, 주차 정산, 음식 주문, 모바일 게임 등 우리 일상 생활을 소프트웨어 개발자들이 책임지고 있는데, 만성적인 개발 인력 부족으로 우리 ICT 산업의 경쟁력이 갈수록 떨어지지 않을까 걱정입니다.어제 오늘의 이야기도 아니고, 해결책이 과연 있는가?고무적인 것은 과거보다는 소프트웨어 개발자의 근무 환경에 더 관심을 가지고 야근 문화를 없애나가려고 노력하는 기업들이 많아지고 있다는 점입니다.핀테크 기업 핀다도 접근 방법은 다소 다르지만 이런 긍정적인 문화를 확산시키는 데 노력하고 있습니다. 그로 인해 우수한 인력이 한명이라도 더 핀다를 선택하고, 대한민국 젊은이 몇명이라도 더 공시생이 되기보다는 소프트웨어 개발자로 진로를 선택하기를 기대합니다.업무 환경이 중요하다.핀다의 개발자는 공유오피스 위워크(Wework) 을지로점 내의 사무실 및 라운지 등에서 자유롭게 근무합니다. 근무중에 사무실 내의 탁구장에서 함께 탁구를 치기도 하고 다트 게임을 하기도 합니다. 위워크 다른 층 라운지 쇼파에서 탁트인 전망을 보며 일하기도 합니다.물론 업무가 몰리고 데드라인에 쫓기면 야근을 하기도 하고 주말에 집에서 일하기도 하지만 이를 권장하기 보다는 지양하고 더 줄여나가려고 합니다. 저녁이 있는 삶을 보장하기 위해 지속적으로 노력할 것입니다.Wework 16층 회의실 겸 탁구장에서 열심히 탁구치는 우리 개발자. Le Viet Hoang‘월화수목금금금’ 일해도 일정 맞추기 어려운데 무슨 배부른 소리인가?소프트웨어 개발은 집중력을 요하는데, 사람이 하루 8시간도 집중해서 일하기는 쉽지 않습니다. 집중하지 못한 상황에서 작성한 낮은 품질의 코드로 더 많은 오류를 일으키고 이를 해결하기 위해 더 많은 시간을 일해야 하는 악순환이 발생합니다. 해당 직원의 행복지수도, 건강도, 로열티도 떨어지고 퇴사할 가능성이 높아집니다. 결국 회사는 잃는 것이 더 많아지게 됩니다. 하지만, 단지 초과 근무로 인해 생산성이 떨어지므로 이를 지양해야 한다고 하기에는 현실은 일반적으로 너무 열악하고 다급합니다. 초과 근무를 대신할 다른 혁신적인 방안이 있어야 기업의 관리자를 설득할 수 있을 것입니다.핀다 개발팀은 다릅니다. 개발 환경을 소개합니다.1. 이슈관리 시스템 Jira를 이용하여 태스크, 오류 등 모든 이슈를 관리합니다.      위키 시스템 Confluence를 통해 회사 및 프로젝트의 날리지를 관리합니다.  위키에 프로젝트별로 이와 같이 스페이스를 만들고 트리 구조로 페이지를 생성합니다.그림 상의 페이지에는 Jira에서 생성한 이슈들을 나열한 것을 볼 수 있습니다. 이런 방식으로 회사의 모든 지식은 체계적으로 정리되고 공유됩니다.2.  Git을 이용하여 소스코드 뿐 아니라 디자인 프로젝트까지 관리합니다.동시에 여러 버전의 소스를 유지하고, 여러 사람이 협업하기 위해 위와 같은 Git flow를 준수합니다.소스 변경(커밋) 시에는 그림과 같이 관련 이슈 번호를 넣어서 커밋과 이슈를 연동합니다.상용 배포 버전에는 그림과 같이 버전을 태그로 달아두고 버전별로 릴리즈 노트를 작성합니다.3. Jenkins를 이용하여 시스템 빌드 및 배포를 자동화하고 있습니다. 각 빌드에도 버전을 태그로 붙이고 있습니다.4. 객체지향 프로그래밍 방식을 철저히 준수합니다.시스템을 모듈로 나누고 각 모듈 간의 의존도는 최소화합니다. 논리적으로 관련된 코드는 한 패키지, 클래스 등에 모아서 응집도를 최대화합니다. 데이터와 데이터 처리 코드는 한 클래스에 모읍니다. 중복된 코드는 피할 수 있다면 한 줄이라도 허용하지 않고, 상속, 함수화, 오버로딩 등을 최대한 활용하여 코드 사이즈를 줄입니다.5. 이해하기 쉬운, 설명이 필요 없는 코드와 문서를 작성합니다.소프트웨어는 본질적으로 복잡합니다. 복잡한 문제를 최대한 쉽게 풀어내는 것이 소프트웨어 개발자의 능력의 핵심 중 하나입니다. 문제를 더 복잡하게 만들어서 다른 사람이 이해하기 어려워 하는 것을 본인의 능력이 뛰어나서라고 자만하거나, 주석을 달거나 문서화를 하지 않고서 다른 사람이 코드를 보고 이해하면 된다는 식의 생각은 아마추어리즘일 뿐입니다.핀다의 소프트웨어 프로젝트는 경험이 부족한 신입 개발자라도 30분 내에 구조와 흐름을 파악할 수 있도록 하고 있습니다.6.  웹, 안드로이드, 아이폰 앱은 철저히 통일된 MVC 구조로 구현합니다.모델(M) 부분은 서버로부터 데이터를 받아오는 모듈, 데이터의 세부사항을  처리하는 모듈, 데이터의 보존과 공급을 담당하는 모듈로 철저히 분리하여 구현합니다.화면의 부분을 담당하는 뷰(V)는 주어진 데이터로 화면을 그리는 것만 담당합니다.화면을 구성하기 위해서는 뷰를 배치하고 모델로부터 데이터를 받아서, 뷰에 전달해야 합니다. 이는 컨트롤러(C)가 담당하는데 컨트롤러는 철저히 컨트롤만 하고 세부적인 사항을 처리하지 않습니다.핀다의 웹, 안드로이드, 아이폰 앱은 모두 동일한 폴더, 클래스 구조를 가지도록 설계하고 있습니다. 이로 인해 다른 분야를 접해보지 못한 개발자라도 하루 내에 파악하여 코드 수정까지 할 수 있어서 누구나 쉽게 풀스택 개발자가 될 수 있습니다.종합해보면, 핀다 개발팀은 나만의 스타일로 코드를 작성할 자유가 없고, 프로그래밍 컨벤션을 따라 최적의 간결한 코드를 작성해야 합니다. 타이트한 프로세스를 따라야 합니다. 구글이나 마이크로소프트 보다 더 높은 수준의 클린 코드를 작성해야 합니다. 다소 타이트해보일 수 있지만, 유능한 핀다의 개발자들은 적극적으로 이를 준수하고 오히려 더 나은 개선 방안을 내놓고 있습니다. 결국 핀다의 개발자는 저녁이 있는 삶 뿐 아니라 신나고 발전적인 직장생활까지 누리게 될 것입니다.핀다의 미래가 밝아 보이나요? 아니면 너무 타이트해 보이나요?핀다는 핀다의 미래가 밝아 보인다고 느끼는 개발자에게 문을 활짝 열어놓고 있습니다.많은 기업이 핀다 방식 혹은 더 나은 방식을 도입하여 행복하게 일하는 개발자들이 더 많아지기를 기대해봅니다.#핀다 #개발 #개발팀 #개발자 #저녁이있는삶 #기업문화 #조직문화 #사내복지
조회수 1104

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

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

VCNC가 Hadoop대신 Spark를 선택한 이유 - VCNC Engineering Blog

요즘은 데이터 분석이 스타트업, 대기업 가릴 것 없이 유행입니다. VCNC도 비트윈 출시 때부터 지금까지 데이터 분석을 해오고 있고, 데이터 기반의 의사결정을 내리고 있습니다.데이터 분석을 하는데 처음부터 복잡한 기술이 필요한 것은 아닙니다. Flurry, Google Analytics 등의 훌륭한 무료 툴들이 있습니다. 하지만 이러한 범용 툴에서 제공하는 것 이상의 특수하고 자세한 분석을 하고 싶을 때 직접 많은 데이터를 다루는 빅데이터 분석을 하게 됩니다. VCNC에서도 비트윈의 복잡한 회원 가입 프로세스나, 채팅, 모멘츠 등 다양한 기능에 대해 심층적인 분석을 위해 직접 데이터를 분석하고 있습니다.빅데이터 분석 기술큰 데이터를 다룰 때 가장 많이 쓰는 기술은 Hadoop MapReduce와 연관 기술인 Hive입니다. 구글의 논문으로부터 영감을 받아 이를 구현한 오픈소스 프로젝트인 Hadoop은 클러스터 컴퓨팅 프레임웍으로 비싼 슈퍼컴퓨터를 사지 않아도, 컴퓨터를 여러 대 연결하면 대수에 따라서 데이터 처리 성능이 스케일되는 기술입니다. 세상에 나온지 10년이 넘었지만 아직도 잘 쓰이고 있으며 데이터가 많아지고 컴퓨터가 저렴해지면서 점점 더 많이 쓰이고 있습니다. VCNC도 작년까지는 데이터 분석을 하는데 MapReduce를 많이 사용했습니다.주스를 만드는 과정에 빗대어 MapReduce를 설명한 그림. 함수형 프로그래밍의 기본 개념인 Map, Reduce라는 프레임을 활용하여 여러 가지 문제를 병렬적으로 처리할 수 있다. MapReduce slideshare 참조MapReduce는 슈퍼컴퓨터 없이도 저렴한 서버를 여러 대 연결하여 빅데이터 분석을 가능하게 해 준 혁신적인 기술이지만 10년이 지나니 여러 가지 단점들이 보이게 되었습니다. 우선 과도하게 복잡한 코드를 짜야합니다. 아래는 간단한 Word Count 예제를 MapReduce로 구현한 것인데 매우 어렵고 복잡합니다.MapReduce로 단어 갯수를 카운트하는 간단한 예제 (Java). 많은 코드를 작성해야 한다.이의 대안으로 SQL을 MapReduce로 변환해주는 Hive 프로젝트가 있어 많은 사람이 잘 사용하고 있지만, 쿼리를 최적화하기가 어렵고 속도가 더 느려지는 경우가 많다는 어려움이 있습니다.MapReduce의 대안으로 최근 아주 뜨거운 기술이 있는데 바로 Apache Spark입니다. Spark는 Hadoop MapReduce와 비슷한 목적을 해결하기 위한 클러스터 컴퓨팅 프레임웍으로, 메모리를 활용한 아주 빠른 데이터 처리가 특징입니다. 또한, 함수형 프로그래밍이 가능한 언어인 Scala를 사용하여 코드가 매우 간단하며, interactive shell을 사용할 수 있습니다.Spark으로 단어 개수를 카운트하는 간단한 예제 (Scala). MapReduce에 비해 훨씬 간단하다.Spark과 MapReduce의 성능 비교. I/O intensive 한 작업은 성능이 극적으로 향상되며, CPU intensive 한 작업의 경우에도 효율이 더 높다. (자료: RDD 논문)Apache Spark는 미국이나 중국에서는 현재 Hadoop을 대체할만한 기술로 급부상하고 있으며, 국내에도 최신 기술에 발 빠른 사람들은 이미 사용하고 있거나, 관심을 갖고 있습니다. 성능이 좋고 사용하기 쉬울 뿐 아니라, 범용으로 사용할 수 있는 프레임웍이기에 앞으로 더 여러 분야에서 많이 사용하게 될 것입니다. 아직 Spark를 접해보지 못하신 분들은 한번 시간을 내어 살펴보시길 추천합니다.기존의 데이터 분석 시스템 아키텍처기존의 데이터 분석 시스템 아키텍처기존의 시스템은 비용을 줄이기 위해 머신들을 사무실 구석에 놓고 직접 관리했으며, AWS S3 Tokyo Region에 있는 로그를 다운받아 따로 저장한 뒤, MapReduce로 계산을 하고 dashboard를 위한 사이트를 따로 제작하여 운영하고 있었습니다.이러한 시스템은 빅데이터 분석을 할 수 있다는 것 외에는 불편한 점이 많았습니다. 자주 고장 나는 하드웨어를 수리하느라 바빴고, 충분히 많은 머신을 확보할 여유가 없었기 때문에 분석 시간도 아주 오래 걸렸습니다. 그리고 분석부터 시각화까지 과정이 복잡하였기 때문에 간단한 것이라도 구현하려면 시간과 노력이 많이 들었습니다.Spark과 Zeppelin을 만나다이때 저희의 관심을 끈 것이 바로 Apache Spark입니다. MapReduce에 비해 성능과 인터페이스가 월등히 좋은 데다가 0.x 버전과는 달리 1.0 버전에서 많은 문제가 해결되면서 안정적으로 운영할 수 있어 비트윈 데이터 분석팀에서는 Spark 도입을 결정했습니다.Apache Zeppelin은 국내에서 주도하고 있는 오픈소스 프로젝트로써, Spark를 훨씬 더 편하고 강력하게 사용할 수 있게 해주는 도구입니다. 주요한 역할은 노트북 툴, 즉 shell에서 사용할 코드를 기록하고 재실행할 수 있도록 관리해주는 역할과 코드나 쿼리의 실행 결과를 차트나 표 등으로 시각화해서 보여주는 역할입니다. VCNC에서는 Zeppelin의 초기 버전부터 관심을 가지고 살펴보다가, Apache Spark를 엔진으로 사용하도록 바뀐 이후에 활용성이 대폭 좋아졌다고 판단하여 데이터 분석에 Zeppelin을 도입하여 사용하고 있고, 개발에도 참여하고 있습니다.또한, 위에서 언급한 하드웨어 관리에 드는 노력을 줄이기 위해서 전적으로 클라우드를 사용하기로 함에 따라서1 아래와 같은 새로운 구조를 가지게 되었습니다.새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템 아키텍처새로운 데이터 분석 시스템은 아키텍처라고 하기에 다소 부끄러울 정도로 간단합니다. 애초에 전체 시스템 구성을 간단하게 만드는 것에 중점을 두었기 때문입니다. 대략적인 구성과 활용법은 아래와 같습니다.모든 서버는 AWS 클라우드를 이용수 대의 Zeppelin 서버, 수 대의 Spark 서버운영Spark 서버는 메모리가 중요하므로 EC2 R3 instance 사용로그는 별도로 저장하지 않고 서비스 서버에서 S3로 업로드하는 로그를 곧바로 가져와서 분석함중간 결과 저장도 별도의 데이터베이스를 두지 않고 S3에 파일로 저장Zeppelin의 scheduler 기능을 이용하여 daily batch 작업 수행별도의 dashboard용 Zeppelin을 통해 중간 결과를 시각화하며 팀에 결과 공유이렇게 간단한 구조이긴 하지만 Apache Spark와 Apache Zeppelin을 활용한 이 시스템의 능력은 기존 시스템보다 더 강력하고, 더 다양한 일을 더 빠르게 해낼 수 있습니다.기존현재일일 배치 분석코드 작성 및 관리가 어려움Zeppelin의 Schedule 기능을 통해 수행Interactive shell로 쉽게 데이터를 탐험오류가 생긴 경우에 shell을 통해 손쉽게 원인 발견 및 수정 가능Ad-hoc(즉석) 분석복잡하고 많은 코드를 짜야 함분석 작업에 수 일 소요Interactive shell 환경에서 즉시 분석 수행 가능Dashboard별도의 사이트를 제작하여 운영관리가 어렵고 오류 대응 힘듦Zeppelin report mode 사용해서 제작코드가 바로 시각화되므로 제작 및 관리 수월성능일일 배치 분석에 약 8시간 소요메모리를 활용하여 동일 작업에 약 1시간 소요이렇게 시스템을 재구성하는 작업이 간단치는 않았습니다. 이전 시스템을 계속 부분적으로 운영하면서 점진적으로 재구성 작업을 하였는데 대부분 시스템을 옮기는데 약 1개월 정도가 걸렸습니다. 그리고 기존 시스템을 완전히 대체하는 작업은 약 6개월 후에 종료되었는데, 이는 분석 성능이 크게 중요하지 않은 부분들에 대해서는 시간을 두고 여유 있게 작업했기 때문이었습니다.Spark와 Spark SQL을 활용하여 원하는 데이터를 즉석에서 뽑아내고 공유하는 예제Zeppelin을 활용하여 인기 스티커를 조회하는 dashboard 만드는 예제결론비트윈 데이터 분석팀은 수개월에 걸쳐 데이터 분석 시스템을 전부 재구성하였습니다. 중점을 둔 부분은빠르고 효율적이며 범용성이 있는 Apache Spark, Apache Zeppelin을 활용하는 것최대한 시스템을 간단하게 구성하여 관리 포인트를 줄이는 것두 가지였고, 그 결과는 매우 성공적이었습니다.우선 데이터 분석가 입장에서도 관리해야 할 포인트가 적어져 부담이 덜하고, 이에 따라 Ad-hoc분석을 수행할 수 있는 시간도 늘어나 여러 가지 데이터 분석 결과를 필요로 하는 다른 팀들의 만족도가 높아졌습니다. 새로운 기술을 사용해 본 경험을 글로 써서 공유하고, 오픈소스 커뮤니티에 기여할 수 있는 시간과 기회도 생겼기 때문에 개발자로서 보람을 느끼고 있습니다.물론 새롭게 구성한 시스템이 장점만 있는 것은 아닙니다. 새로운 기술들로 시스템을 구성하다 보니 세세한 기능들이 아쉬울 때도 있고, 안정성도 더 좋아져야 한다고 느낍니다. 대부분 오픈소스 프로젝트이므로, 이러한 부분은 적극적으로 기여하여 개선하여 나갈 계획입니다.비트윈 팀에서는 더 좋은 개발환경, 분석환경을 위해 노력하고 있으며 이는 더 좋은 서비스를 만들기 위한 중요한 기반이 된다고 생각합니다. 저희는 항상 좋은 개발자를 모시고 있다는 광고와 함께 글을 마칩니다.연관 자료: AWS 한국 유저 그룹 - Spark + S3 + R3 을 이용한 데이터 분석 시스템 만들기↩
조회수 4916

Gradle Dependency 분리하기

본 포스팅은 아래 코드를 보시면 좀 더 이해하기 쉽습니다.build.gradledependencies-variable.gradledependencies-classpath.gradledependencies-app.gradleGradle 의 역할Gradle 은 이제 안드로이드 개발에 있어서 그 중심이 되는 빌드 환경입니다. 안드로이드 빌드에 대한 기본 설정 뿐만 아니라 빌드에 필요한 Task 를 지정하거나 의존성을 추가할 수 있습니다.특히 의존성에서 일반적인 서비스들은 다양한 오픈소스를 활용하게 됩니다. 네트워크 라이브러리, 이미지 라이브러리, DI 라이브러리, Support 라이브러리,Play-Service 라이브러리 등등 이젠 프로젝트를 시작함에 있어서 기본적으로 10개 이상의 라이브러리를 추가하게 됩니다. 이러한 라이브러리들이 많아질수록 필연적으로 빌드 스크립트가 길어지게 됩니다. 이는 나중에 빌드에 관련된 코드를 추가/수정할 때 유지보수에 영향을 끼치게 됩니다.Gradle 의존성 분리하기토스랩에서는 꽤 많은 숫자의 라이브러릴 사용하고 있습니다. 테스트용 라이브러리들까지 포함해서 60여개의 라이브러리를 쓰고 있습니다. 이러한 라이브러리 코드들이 1개의 빌드 스크립트 안에 포함되어 진다면 라이브러리의 버전을 변경하거나 수정하는 작업을 할 때에는 불가피하게 시간이 소요될 수 밖에 없습니다.그에 따라 Gradle 에서 라이브러리들을 변수화 해서 분리하는 작업을 하였습니다.1. 라이브러리 변수화 하기ext { retrofit = 'com.squareup.retrofit2:retrofit:2.1.0' retrofit2_gson = 'com.squareup.retrofit2:converter-gson:2.1.0' retrofit2_rxjava2 = 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:2.1.0' } 가장 간단한 변수화였습니다. 하지만 Retrofit 은 관련 라이브러리들이 함께 수반되기 때문에 버전명을 다시 분리하였습니다.2. 라이브러리 버전 변수화 하기ext { retrofit_version = '2.1.0' retrofit = "com.squareup.retrofit2:retrofit:$retrofit_version" retrofit2_gson = "com.squareup.retrofit2:converter-gson:$retrofit_version" retrofit2_rxjava2 = "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:$retrofit_version" } 하지만 버전명과 라이브러리이름이 함께 있는 것이 깔끔해보이진 않습니다. 그래서 아래와 같이 바꿨습니다.3. 라이브러리 이름과 버전의 분리ext { retrofit = '2.1.0' } ext.dependencies = [ retrofit2 : "com.squareup.retrofit2:retrofit:$ext.retrofit", retrofit2_gson : "com.squareup.retrofit2:converter-gson:$ext.retrofit", retrofit2_rxjava2 : "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:$ext.retrofit_rxjava2", ] 실제에는 다음과 같이 사용하면 됩니다.dependencies { compile rootProject.ext.dependencies.retrofit2 compile rootProject.ext.dependencies.retrofit2_gson compile rootProject.ext.dependencies.retrofit2_rxjava2 } 이제 라이브러리를 변수화 해서 분리를 하였습니다.이제 변수로 지정한 라이브러리들은 build.gradle 파일안에 존재하게 됩니다.// build.gradle ext { retrofit = '2.1.0' } ext.dependencies = [ retrofit2 : "com.squareup.retrofit2:retrofit:$ext.retrofit", retrofit2_gson : "com.squareup.retrofit2:converter-gson:$ext.retrofit", retrofit2_rxjava2 : "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:$ext.retrofit_rxjava2", ] buildscript { // blah blah } 라이브러리가 3개뿐이니 깔끔해보이는군요. 하지만 토스랩의 라이브러리는 60여개 입니다. 변수명도 60여개라는 말이죠. 그래서 라이브러리 변수들만 파일을 분리하기로 했습니다.4. 라이브러리 변수를 파일로 분리하기// dependencies-variable.gradle ext { retrofit = '2.1.0' } ext.dependencies = [ retrofit2 : "com.squareup.retrofit2:retrofit:$ext.retrofit", retrofit2_gson : "com.squareup.retrofit2:converter-gson:$ext.retrofit", retrofit2_rxjava2 : "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:$ext.retrofit_rxjava2", ] // build.gradle apply from :'dependencies-variable.gradle' buildscript { // blah blah } 이제 좀 교통정리가 되어가는 기분이네요.하지만 app 의 build.gradle 을 보았습니다.// app 의 build.gradle apply plugin: 'com.android.application' dependencies { // 라이브러리 60개 compile rootProject.ext.dependencies.library.retrofit2 compile rootProject.ext.dependencies.library.retrofit2_gson compile rootProject.ext.dependencies.library.retrofit2_rxjava2 } android { // 중략 } 뭔가 잘못되어 가고 있습니다. 여전히 dependencies 가 큰 부분을 차지하고 있습니다.5. app.dependencies 분리하기이제 dependencies 를 분리할 차례입니다.// dependencies-app.gradle repositories { jcenter() } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile rootProject.ext.dependencies.library.retrofit2 compile rootProject.ext.dependencies.library.retrofit2_gson compile rootProject.ext.dependencies.library.retrofit2_rxjava2 compile rootProject.ext.dependencies.library.okhttp3 compile rootProject.ext.dependencies.library.okhttp3_logging compile rootProject.ext.dependencies.library.stetho_okhttp3 } // app 의 build.gradle apply from: 'dependencies-app.gradle' 이제 dependencies 와 관련된 스크립트가 분리되었습니다.하지만 저 apply from 이 항상 app 의 build.gradle 에 따라 붙어야 하는 것이 아쉽습니다. 그래서 buildscript 에 아예 추가하기로 하엿습니다.6. 빌드 스크립트에 dependencies 추가 동작하기먼저 빌드 스크립트용 스크립트를 만들겠습니다.// dependencies-classpath.gradle rootProject.buildscript.repositories { jcenter() } rootProject.buildscript.dependencies { classpath rootProject.ext.dependencies.classpath.android } 그리고 buildscript 가 시작될 때 모든 dependencies 스크립트가 인식할 수 있게 하겠습니다. 인식할 스크립트는 다음과 같습니다.dependencies-variable.gradle - 라이브러리 변수 저장dependencies-classpath.gradle - 빌드용 스크립트 저장dependencies-app.gradle - 라이브러리 추가 스크립트 저장rootProject 의 build.gradle 를 아래와 같이 변경합니다.// rootProject 의 build.gradle buildscript { apply from: "dependencies-variable.gradle" apply from: "dependencies-classpath.gradle" } apply from: 'dependencies-app.gradle' 위와 같이 변경을 하면 빌드스크립트가 동작하는 시점에 변수를 인식하고 빌드용 스크립트를 인식합니다.하지만 앱용 라이브러리 추가 스크립트는 아직 준비가 덜 되었습니다. “app” 프로젝트가 인식이 된 시점에 라이브러리가 추가되어야 하기때문에 처음 만들었던 스크립트로는 한계가 있습니다.그래서 아래와 같이 변경하겠습니다.// dependencies-app.gradle rootProject.allprojects { project -> if (project.name == 'app') { project.afterEvaluate { repositories { jcenter() } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile rootProject.ext.dependencies.library.retrofit2 compile rootProject.ext.dependencies.library.retrofit2_gson compile rootProject.ext.dependencies.library.retrofit2_rxjava2 } } } } afterEvaluate 는 프로젝트의 인식이 완료되면 동작이 되는 함수이기 때문에 모든 것이 끝나고 dependencies 가 추가되는 것으로 이해하시면 됩니다.정리위의 과정을 거침으로써 gradle 파일은 좀 더 나뉘었지만 app 의 build.gradle 은 안드로이드 프로젝트 그 자체에 집중 할 수 있도록 하였습니다.이렇게 나누었던 본래의 목적은 의존성 라이브러리와 코드 품질 관리용 스크립트가 1개의 스크립트 파일에 담겨지면서 관리하는 데 있어서 큰 문제가 발생하게 되었습니다. 그에 따라 각각을 나누고 그 목적에 맞도록 각가의 파일 만들었습니다.라이브러리의 변수용 파일buildscript 용 classpath 를 관리하는 파일본 프로젝트의 라이브러리 의존성 관리 파일참고 소스Github : https://github.com/ZeroBrain/DataBind-MVVM-Sample#토스랩 #잔디 #JANDI #개발 #개발후기 #인사이트
조회수 10380

Next.js 튜토리얼 7편: 데이터 가져오기

* 이 글은 Next.js의 공식 튜토리얼을 번역한 글입니다.** 오역 및 오탈자가 있을 수 있습니다. 발견하시면 제보해주세요!목차1편: 시작하기 2편: 페이지 이동 3편: 공유 컴포넌트4편: 동적 페이지 5편: 라우트 마스킹6편: 서버 사이드 7편: 데이터 가져오기 - 현재 글8편: 컴포넌트 스타일링9편: 배포하기개요꽤 그럴듯한 Next.js 애플리케이션을 만드는 방법과 Next.js 라우팅 API의 모든 장점을 배웠습니다.대부분의 경우 데이터 소스에서  원격으로 데이터를 가져와야 합니다. Next.js는 페이지에 데이터를 가져오기 위한 표준 API를 제공합니다. getInitialProps라 불리는 비동기 함수를 사용하여 구현할 것입니다.주어진 페이지에 원격 데이터 소스를 통해 데이터를 가져오고 원하는 페이지에 props을 통해 전달할 수 있습니다. 서버와 클라이언트 둘 다 동작하도록 getInitialProps를 작성할 수 있습니다. 그래서 Next.js는 클라이언트와 서버에서 모두 사용할 수 있습니다. 이번 편에서는 getInitialProps를 사용하여 공개된 TVmaze API에서 가져온 데이터로 배트맨 TV 쇼에 대한 정보를 보여주는 애플리케이션을 구현할 예정입니다.설치이번 장에서는 간단한 Next.js 애플리케이션이 필요합니다. 다음의 샘플 애플리케이션을 다운받아주세요:아래의 명령어로 실행시킬 수 있습니다:이제 http://localhost:3000로 이동하여 애플리케이션에 접근할 수 있습니다.배트맨 쇼 데이터 가져오기데모 애플리케이션 내의 home 페이지에 블로그 포스트 목록이 있습니다. 배트맨 TV 쇼 목록을 표시할 것입니다.쇼의 데이터들을 하드코딩하는 대신에 원격 서버에서 그 정보를 가져옵시다.여기서는 TV 쇼를 가져오기 위해 TVMaze API를 사용합니다.TV 쇼 정보를 검색하는 API 입니다.먼저 isomorphic-unfetch를 설치해야 합니다. 데이터를 가져올 때 사용할 라이브러리입니다. 브라우저 fetch API 구현을 간단히 할 수 있도록 만들어진 것이지만 클라이언트와 서버 환경에서 모두 동작합니다.npm install --save isomorphic-unfetchpages/index.js를 다음과 같이 변경해주세요:위의 페이지에 있는 모든 내용은 아래에 표시된 Index.getInitialProps를 제외하고는 익숙할 것입니다:애플리케이션의 어떤 페이지에든 추가할 수 있는 정적 비동기 함수입니다. 이것을 사용하여 데이터를 가져오고 가져온 데이터를 props를 통해 페이지로 보낼 수 있습니다.보다시피 배트맨 TV 쇼 데이터를 가져오고 'shows' props를 통해 페이지로 전달합니다.위에서 보았던 getInitialProps 함수에서 가져온 데이터 숫자를 콘솔에 출력합니다.이제 브라우저 콘솔과 서버 콘솔을 살펴봅시다. 그리고 페이지를 새로고침 해주세요.페이지를 새로고침 한 후 출력되는 메시지는 어디에서 보였나요?- 서버 콘솔- 브라우저 콘솔- 둘 다- 어떤 콘솔에도 출력되지 않았다서버에서만 출력됩니다이 경우 메시지는 서버에서만 출력됩니다.이는 서버에서 페이지가 랜더링되기 때문입니다.이미 데이터를 가지고 있어 클라이언트에서 다시 정보를 가져올 필요가 없습니다.post 페이지 구현하기TV 쇼에 대한 자세한 정보를 보여주는 "/post" 페이지를 구현해봅시다.먼저 server.js를 열고 /p/:id 라우트를 다음과 같이 바꿔주세요.위처럼 바꾼 코드를 적용하기 위해 애플리케이션을 재실행시켜주세요.이전에는 title 쿼리 파라미터를 페이지에 매핑했습니다. 이제 id로 이름을 바꿔야합니다.다음과 같은 내용으로 pages/post.js를 변경해주세요.페이지의 getInitialProps을 살펴봅시다:여기에서 함수의 첫 번째 파라미터는 context 객체입니다. 정보를 가져올 때 사용할 수 있는 쿼리 필드를 가지고 있습니다.예제에서 쿼리 파라미터로부터 보여지는 ID를 선택하고 TVMaze API로부터 데이터를 가져옵니다.이 getInitialProps 함수에서 표시할 제목을 출력하는 console.log를 추가했습니다. 이제 어디에서 출력되는지 볼 수 있습니다.서버와 클라이언트의 콘솔를 둘 다 열어주세요.그 다음 홈페이지 http://localhost:3000로 이동하여 배트맨 쇼 제목을 클릭하세요.위에서 애기했던 console.log 메시지가 보여지는 장소는 어디인가요?- 서버 콘솔- 브라우저 콘솔- 콘솔 둘 다- 아무 콘솔에서도 출력되지 않는다클라이언트 사이드에서 데이터 가져오기브라우저 콘솔에서 메시지를 볼 수 있습니다.클라이언트 사이드를 통해 포스트 페이지에 이동했기 때문입니다. 그런 다음 클라이언트 사이드로부터 데이터를 가져오는 것은 가장 좋은 방법입니다.예를 들어 http://localhost:3000/p/975에 직접 이동한다면 클라이언트가 아닌 서버에서 메시지가 출력되는 것을 볼 수 있습니다.마무리데이터를 가져오고 서버 사이드에서 렌더링하도록 만드는 Next.js의 가장 중요한 기능 중 하나를 배웠습니다.대부분의 유스 케이스에서 충분히 사용할 수 있는 getInitialProps의 기본을 배웠습니다. 더 많은 것을 배우고 싶다면 Next.js의 문서 중 data fetching 문서를 참고할 수 있습니다.#트레바리 #개발자 #안드로이드 #앱개발 #Next.js #백엔드 #인사이트 #경험공유
조회수 1373

웹 서비스 개발자가 APM을 사용해야 하는 이유

백엔드 서비스를 만들고 운영하는 개발자라면, 지금 바로 APM 서비스를 사용해 보세요. 와탭의 APM은 국내 수많은 Enterprise 기업에서 자사의 서비스를 분석하기 위해 사용되고 있으며 많은 효과를 보고 있습니다. 북미에서는 이미 수많은 스타트업이 DevOps의 기본 도구로 APM을 선택하고 있습니다. APM은 원래 대규모 서비스를 운영하는 분들이 전문적으로 사용하고 있었지만 최근 트렌드는 운영자에서 개발자로 이동하고 있는 서비스 이기도 합니다. 특히 와탭의 APM은 개발자 분들을 위한 스택 분석 기능이 있습니다. 개발자라면 와탭 APM 서비스가 제공하는 아래의 3가지 스택 분석 기능을 꼭 사용해 보세요. 유니크 스택탑 스택액티브 스택많은 개발자들이 자신이 만든 서비스가 어떻게 동작하는지 또는 웹 서비스에 어떤 영향을 주고 있는지 알지 못합니다. 하지만 와탭 애플리케이션 성능 모니터링(APM) 서비스를 사용하면 메소드가 애플리케이션에서 어떻게 사용되는지 얼마나 사용되는지 알수 있습니다. 와탭은 다른 APM 서비스와 다르게 10초에 한번씩 활동중인 트랜잭션을 검사하여 트랜잭션에 콜스택정보를 저장하고 있습니다. 그리고 이렇게 저장된 스택정보를 가지고 3가지 형태로 가공하여 보여주는데, 이 것이 유니크 스택 / 탑 스택 / 액티브 스택입니다. 먼저 유니크 스택은 가장 많이 사용된 스택 정보를 보여주는 방식입니다. 트랜잭션에서 실행되고 있는 메소드가 A 이고 이를 호출한 메소드가 모두 일치하는 스택을 유니크 스택이라고 합니다.1. A() ← C()2. A() ← C()3. B() ← D()4. B() ← E()5. B() ← F()위와 같은 경유 유니크 스택은 아래와 같이 통계를 내어 보여 줍니다. 40% A()    A()    C()20% B()    B()    D()20% B()    B()    E()20% B()    B()    F()이렇게 콜스택 정보 전체를 기준으로 분석을 하는 경우에는 성능에 영향을 주는 기능 단위의 분석이 가능합니다. 하지만 성능에 영향을 많이 주는 메소드를 알고 싶을 때가 있습니다. 이런 경우에 사용하는 것이 탑 스택 분석입니다. 아까와 같은 상황을 예를 들겠습니다.1. A() ← C()2. A() ← C()3. B() ← D()4. B() ← E()5. B() ← F()이런 상황에서 탑 스택 분석은 아래와 같이 가장 많이 사용되느 메소드를 알려줍니다. 60% B()    33% D()    33% E()    33% F()40% A()    100% C()유니크 스택에서는 A() ← C() 가 가장 많이 사용된 스택이라는 것을 알려주지만 탑 스택에서는 B() 메소드가 가장 많이 사용된 메소드라는 것을 알려줍니다. 이 두가지 내용을 통해 가장 많이 사용되는 메소드의 집합가 가장 많이 호출되는 메소드를 알아 낼 수 있습니다. 만일 서비스를 메소드 단위에서 개선하고 싶다면 이 정보를 기반으로 개선 작업을 진행하면 많은 도움을 받을 수 있습니다. 위에 화면에서 메소드를 선택하면 메소드를 호출한 스택들의 정보를 확인 할 수 있습니다. 마지막으로 액티브 스택입니다. 액티브 스택은 WAS 서버와 URL 그리고 발생 시간을 기준으로 저장된 콜스택의 정보를 보여줍니다. 서비스 성능이 떨어진 시간대의 콜스택 정보를 확인 함으로써 메소드 구간에서의 튜닝 정보를 제공합니다. 액티브 스택은 핵심 기능이 하나더 있습니다. 바로 서비스가 동작하는 스탭정보에 통합됨으로써 문제를 바로 확인할 수 있는 기능입니다. 와탭의 APM에서만 분석가능한 기능이며 특허로 등록되어 있습니다. 액티브 스택은 통계 관점이 아니라 실행 관점에서 문제를 바라보고 있습니다. 우리가 만든 웹 어플리케이션을 고객에 입장에서 보면 아래와 같이 동작합니다. 고객 → 웹 서비스 요청 → 서버 접속 → 서비스 접속 → 애플리케이션1 → 메소드 1 → DB 1접근 → Query 1 → Query 2 → 메소드 2 → 파일 접근 → 메소드 3 → 결과 취합 → WAS 통과 → 웹 서비스 결과 반환 일반적으로 애플리케이션 모니터링은 이런 상항을 아래와 같이 보여줍니다. 서비스 접속 → Query 1 → Query 2 → 파일 접근 → 트랜잭션 종료와탭의 애플리케이션 모니터링은 수집된 콜 스택 정보를 기반으로 아래와 같이 보여줍니다.  서비스 접속 → Query 1 → 메소드 2 → Query 2 → 파일 접근 →메소드 3 → 트랜잭션 종료위에 상황은 트랜잭션에서 메소드 2와 메소드 3이 수집된 경우에 트랜잭션의 스탭의 실행시간에 맞쳐서 정보를 재구성하는 것을 보여주고 있습니다. 이렇게 확인하게 된다면 메소드에서 발생하는 성능 문제를 확인 할 수 있습니다. APM 서비스는 와탭 / 뉴렐렉 / 데이터 독과 같은 서비스들을 통해서 2주에서 한달간 언제든 무료로 사용가능합니다. 다만 메소드에 대한 분석 기능은 와탭의 APM에서만 제공하는 기능들이 많습니다. 개발자라면 한번쯤 와탭의 APM 서비스를 통해 자신이 만들고 운영하고 있는 서비스에서 가장 많이 사용되는 메소드가 무엇인지 확인 해 보시기 바랍니다. Tip!! APM은 개발시에 사용하는 디버깅 도구라기 보다는 막대한 량의 트랜잭션이 발생하는 운영과정에서 사용되는 도구입니다. 트랜잭션 자체가 적다면 원하는 데이타가 안 나올 수 도 있습니다. 와탭으로 모니터링 하기 - 목차 바로가기#와탭랩스 #개발자 #개발팀 #인사이트 #경험공유 #일지 #서비스소개
조회수 1242

AWS Lambda + API Gateway로 API 만들어보자

Overview좋은 아침입니다. 오늘은 AWS Lambda와 API Gateway 이용하여 API를 만들어보겠습니다. 서버 구축부터 해야 하지만 이번 글에서 서버는 따로 필요 없습니다. 당황하셨나요? 괜찮습니다. 이 글을 보면 곧 이해가 될 겁니다. 우선 Lambda와 API Gateway부터 알아봅시다. Lambda는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있게 해주는 컴퓨팅 서비스입니다. 브랜디 랩스에는 이미 이것을 활용한 예제가 많은데요. 아마 아래의 포스팅들을 보시면 도움이 될 겁니다.SQS + Lambda: 이상근 팀장님CodeStar + Lambda + SAM으로 테스트 환경 구축하기: 천보성 팀장님API 호출부터 결과 확인까지API Gateway는 규모에 상관없이 API 생성, 유지 관리, 모니터링과 보호를 할 수 있게 해주는 서비스입니다. 이 글에서는 API를 호출해 결과를 확인하는 걸 목표로 진행하겠습니다.최종 API 호출 URL* GET /v1/reviews/{review-no}/comments* POST /v1/reviews/{review-no}/comments AWS(Amazon Web Service) 가입 절차는 생략하겠습니다. 1.AWS 로그인 후 API Gateway 시작!AWS에서도 설명되어 있듯이 API gateway엔 이와 같은 장점이 있습니다.1. API 개발 간소화: 새로운 버전을 신속하게 반복하고, 테스트하고, 출시할 수 있습니다.2. 규모에 따른 성능: 백엔드 시스템에 대한 트래픽 관리하여 유동적으로 API 호출하여 성능을 높이는데 도움이 됩니다.3. SDK 생성: 사용자 지정 SDK를 만들어 애플리케이션에서 신속하게 API를 테스트하고 배포할 수 있습니다. 2.API 생성새 API로 엔드 포인트 유형을 지역으로 선택하여 생성하세요. 엔드 포인트 유형1. 지역 : 현재 리전에 배포2. 최적화된 에지 : CloudFront 네트워크에 배포3. 프라이빗 : VPC에서만 엑세스 가능3.최종 호출 url로 순차적으로 리소스 생성리소스 이름과 리소스 경로를 입력하고 리소스를 생성합니다.리소스는 호출할 수 있는 특정 URL입니다. 생성된 리소스로 /reviews 주소가 만들어졌습니다.다음은 /reviews 주소 뒤에 {review-no}를 생성합니다. 리소스 경로에 {} 가 포함되어 있으면 경로 파라미터를 나타냅니다.마지막 리소스를 생성하게 되면 위의 이미지와 같이 /reviews/{review-no}/comments 리소스가 생성되었습니다. 이제 메서드에 연결할 Lambda 함수를 먼저 생성하겠습니다.4.Lambda 함수 생성GET, POST 메서드에 연결할 각각의 Lambda 함수를 생성합니다.‘Hello from Lambda’ 문자열로 리턴되는 Lambda 함수가 생성되었습니다. 생성된 Lambda 함수를 API Gateway 메서드에 연결해보겠습니다.5.메서드 생성GET, POST 메서드를 생성합니다.메서드의 의미* POST : 새로 생성(Create)* GET : 조회(Read)* PUT : 수정(Update)* DELETE : 삭제(delete)* PATCH : 일부만 수정(Update) 새 메서드의 통합 유형을 Lambda 함수로 선택하고 기존에 생성한 함수명으로 입력한 다음 저장을 누릅니다.메서드 실행 화면입니다. 해당 메서드에 통합 요청할 Lambda 함수가 연결됩니다. 연결된 Lambda 함수를 눌러보겠습니다.왼쪽 목록 트리거 추가하는 부분에 API Gateway가 연결되었습니다. 그럼 이제 정상적으로 호출되는지 테스트해보겠습니다.테스트를 클릭하면 오른쪽에 요청에 대한 결과가 나옵니다. 조금 전에 연결했던 Lambda 함수에 ‘Hello from Lambda’ 값으로 출력됩니다. 이제 리소스로 추가한 경로 파라미터를 매핑하여 출력해보겠습니다.메서드 요청에서는 사용자에게 노출되는 API를 정의할 수 있습니다. 리소스로 경로 파라미터를 추가하게 되면 메서드 요청 -> 경로 요청 부분에 자동으로 추가되어 있습니다.통합 요청에서는 백엔드와의 통신 방식을 지정할 수 있습니다. 메서드 요청에서 보낸 URL 경로 부분을 매핑시켜야 합니다. 명명 규칙은 아래와 같습니다. method.request.{"path" | "querystring" | "header"}.{param_name}매핑 템플릿을 추가하여 수신된 요청을 변환하여 통합 백엔드로 보내야 합니다. 정의된 템플릿이 없기 때문에 매핑 템플릿을 추가한 후 메서드 요청 패스스루로 지정합니다. 그러면 클라이언트가 제공한 요청이 변환없이 통합 백엔드로 전달됩니다.클라이언트가 요청한 경로 파라미터 출력하도록 Lambda 함수를 수정합니다.이제 다시 테스트를 해보겠습니다. 경로에 값을 요청하여 응답 본문에 출력되는 걸 확인할 수 있습니다.6.API 배포스테이지 정보를 입력하고 배포를 클릭합니다.스테이지 상세 정보에 API 호출 주소가 생성됩니다. Postman으로 생성된 API주소를 입력하여 정상적으로 return 값을 확인합니다.Conclusion정말 긴 과정이었습니다. 지금까지 API Gateway를 이용하여 API 생성부터 배포까지 알아봤습니다. API Gateway를 사용하면 서버 없이 높은 확장성을 가진 백엔드 애플리케이션을 구축하고 운영할 수 있게 될 겁니다. 백엔드에 관심이 있는 개발자에게 이 글이 도움이 되길 바랍니다.글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유
조회수 1048

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

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

[Buzzvil Culture] Strategy Talk for Engineer Hiring : How we hire engineers

 버즈빌에서는 전사 차원에서 고민하고 있는 회사의 현안과 전략적 방향성에 대해 모두와 함께 공유하고 의견을 나눈다는 취지 하에 한 달에 한 번 Strategy Talk을 진행하고 있습니다. Strategy Talk의 주제는 매 달의 화두와 고민에 맞게 진행되고 있는데요. 지난 번에 버즈빌 블로그를 통해 소개드렸던 Machine Learning(AI) 부터 프로덕트 로드맵, 시장 동향, 그리고 회사의 비전과 미션 등 다양한 주제로 진행되고 있습니다. 이번 달, Strategy Talk은 ‘버즈빌의 Engineer Hiring Strategy’ 라는 주제로 진행되었습니다. 이번 세션은 버즈빌의 Product side를 총괄하고 있는 Young의 주도하에 진행되었는데요. 세션을 통하여 왜 버즈빌이 더 많은 엔지니어가 필요한지에 대한 배경부터 어떤 방법들을 통해 채용을 해 나갈 것인지, 나아가 버즈빌이 어떤 모습으로 변해갈 것인지에 대한 내용을 공유하고 함께 논의하는 시간을 가졌습니다.새로운 개발자들을 대규모 채용하는 것이 어떤 의미가 있을까요? 기본적으로 새로운 사업을 만들어가며 공격적으로 성장하고 현재 리소스의 한계 때문에 진행하지 못하고 있는 기존 Product의 개선 작업들을 진행 하기 위해서는 당연히 충분한 개발자들이 합류하는 것이 필요합니다. 뿐만아니라 다양한 경험을 가진 더 많은 개발자를 채용하는 것은 ‘버즈빌의 개발문화’와도 큰 연관이 있습니다. 버즈빌은 좋은 개발문화를 가지고 있기로 유명합니다. 수평 / 자율 / 성장 삼박자가 고루 갖추어진 환경이라고 할 수 있는데요. 개발팀의 개발자들 모두가 동등한 Software Engineer로 일하고 있고 그만큼 개발 과정에서 의견 교환이 자유롭게 일어납니다. 누군가의 일방적인 지시가 아닌 모두가 최적이라고 합의할 수 있는 방향으로 개발을 진행해 나가고 있습니다. 뿐만아니라 개발 과정에서 각각의 엔지니어가 본인의 업무를 맡아 주도적으로 처리해 나가며 자신이 맡은 이슈에 대해 주인의식을 가지고 일하고 있습니다. 따라서 특정 개발 방향이 주어져서 틀에 박힌 개발을 해야한다거나 다른 사람의 눈치를 보면서 맞춰가는 개발을 해야하는 건 버즈빌의 개발문화와는 거리가 멀다고 할 수 있습니다. 그리고 개인의 성장을 위해 여러가지 지원을 하고 있는데요. 업무를 진행하는 과정중에 필요하다면 AWS의 다양한 서비스를 포함한 여러가지 툴들을 자유롭게 사용해 볼 수 있습니다. 외부에서 열리는 세미나 / 강연등에 참여하는 것을 독려하며 회사에서 관련비용을 지원하기도 합니다. 이러한 개발문화를 가지고 있기에 버즈빌은 개발자들이 자신의 역량을 100% 발휘할 수 있고 새로운 것들을 배워가며 성장해 나가기에는 최적의 조건을 가지고 있다고 할 수 있습니다. (버즈빌에 개발문화에 대한 보다 자세한 내용은 여기를 참고해 주세요!)이러한 버즈빌의 개발 문화를 유지하고 더 나아가 발전시켜 나가기 위해서도 다양한 경험을 가진 많은 엔지니어들이 합류하는 것이 긍정적인 영향을 미치리라 생각합니다. 지금도 내부적으로 개발자들이 돌아가면서 기술 관련 세미나를 진행하면서 서로의 노하우와 새로운 기술에 대한 논의들을 하고는 있지만 더 많은 개발자들이 합류 하면서 이런 기회 들을 더욱 확장해 나갈 수 있음은 물론 관심사가 맞는 개발자들 끼리 모여 관련 주제로 스터디 모임을 진행한다거나 새로운 사업모델 발굴을 위해서 버즈빌의 자원들을 활용하여 새로운 프로젝트들도 진행해 볼 수 있을 것입니다.
조회수 1372

QA 끝! ADB 설치부터 사용까지

Overview안드로이드 개발자라면 모두 ADB(Android Debug Bridge)를 사용합니다. 안드로이드 SDK에 포함되어 있는 기능인데요. 쉽게 말하면 에뮬이나 안드로이드 단말과의 연결고리, 도구를 의미합니다. 특히나 QA(Quality Assurance)를 진행할 때 ADB를 사용하면 아주 유용하고, 있어 보입니다. 이번 글에서는 ADB를 잘 모르는 QA직군들을 위해 설치 방법과 간단한 사용법을 공유하려고 합니다. SDK, ADB 설치하기앞서 ADB는 SDK에 포함된 기능이라고 했죠? 우선 여기를 클릭해 SDK를 설치해주세요. 참, 안드로이드는 JAVA가 기본 언어! JAVA도 설치하고 환경 변수도 설정해주세요!SDK를 설치했다면 plalform-tools 폴더 안의 adb.exe파일을 찾아야 합니다. 저의 설치 경로는(C:\Users\brandi_171205_02\android-sdks\platform-tools) 네요.경로를 찾았다면 JAVA 환경 변수 설정하듯 ADB도 환경변수를 설정해야 합니다. ‘내 컴퓨터 마우스 오른쪽 > 속성 클릭’해주세요.고급 시스템 설정 클릭 (개인정보라 지웠습니다.)환경변수 클릭시스템변수 영역 path클릭 > 편집 클릭윈도우10은 앞뒤로 ;를 추가하지 않아도 됩니다. ADB 경로를 추가해주세요. (C:\Users\brandi_171205_02\android-sdks\platform-tools)cmd창을 열고 ADB를 입력하고, 엔터를 눌러주세요.아래와 같이 나오면 성공!잘 따라왔나요? 그 다음은 단말기입니다. 개발자 옵션 > usb디버깅 허용 후 단말을 pc와 연결해주세요. CRM창에서 adb devices 를 입력해주세요. 이 명령어는 에뮬이나 단말 연결을 확인하는 명령어 입니다.ADB 설치를 마쳤습니다. 참 쉽죠? 지금부턴 자주 쓰는 ADB 명령어를 알려드립니다. 한 번 사용해보세요. 한 번 써봤다는 사람은 봤어도, 한 번만 썼다는 사람은 못 봤습니다.자주 쓰는 ADB 명령어단말 재시작QA진행하시면 재시작 많이 하죠? 단말초기화..!adb rebootapk설치 내컴퓨터 > 단말 > 다운로드할 필요가 없어요. 바로 설치!!adb install -r [파일명].apkapk 삭제adb uninstall [패지지명]Android버전 확인adb shell getprop ro.build.version.releaseScreenshotadb shell /system/bin/screencap -p 장치내경로동영상 녹화 QA일하면서 필수입니다. 정말 유용해요.adb shell screenrecord /sdcard/[저장할파일명].mp4텍스트 입력 로그인, 텍스트 입력 테스트 진짜 좋습니다.adb shell input text “[입력할 텍스트]”마치며ADB엔 엄~청나게 많은 명령어가 있습니다. 더 많은 정보를 알고 싶다면 adb help를 입력해보세요. 명령어 도움말이 툭 나올 겁니다. ADB가 있다면 이슈 등록과 이슈 관리 정말 편해집니다. 우선 알려드린 7번까지만 사용해보세요. 당신의 QA가 편안해질 겁니다. 지금까지 브랜디 QA 문지기, 김치영이었습니다.글김치영 대리 | R&D PM팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유
조회수 2789

Radix? Redis!

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

GUI가이드라인 정의와 목적

S/W 개발자가 디자인대로 화면을 구현할 때, 어떻게 디자인 요소 위치를 잡아야 하는지 정확한 정보가 필요합니다. 이런 정보는 GUI 디자이너가 포토샵과 같은 디자인 툴을 사용하여 개발자가 사용 가능한 형태로 사이즈 정보와 리소스를 만들어 전달하는 작업을 GUI 가이드라인 제작 작업이라 합니다.GUI 가이드 문서 상에는 화면상에 표현되는 모든 GUI 요소들의 정보가 표시가 됩니다. 화면상의 위치 X/Y 좌표값, 디자인 요소의 폭/높이 사이즈 정보, 이미지 파일 리소스명, 폰트 타입, 폰트 크기 등 다양한 그래픽 요소의 정보를 정확하게 수치화 하여 기재한 것입니다.가이드 문서의 양식은 딱 정해진 틀은 없지만, 소위 대기업의 경우 표준 템플릿을 이용합니다. 단말 하나에 탑재되는 앱 별로 수십 벌의 문서를 제작하여 관리해 왔습니다. 현재 과도기적인 단계라 스케치(.sketch) 파일과 가이드라인 문서를 함께 운영하는 곳도 있을 정도입니다.기존에 GUI 가이드 문서 제작을 위해서는 아래와 같은 일련의 순서로 작업을 하였습니다.디자인 시안 작업 > 디자인 시안 확정 > 개발 가능성 리뷰 > 최종 수정 >GUI 가이드라인 문서 제작 & 이미지 파일 리소스 작업이 중에서 가이드 문서 제작 과정을 초점에 두고 살펴보면, GUI 디자이너가 직접 이미지를 자르고 위치와 크기 정보를 확인하여, 파워포인트 문서로 정보를 입력하는 일련에 단순 노가다를 반복적으로 진행하게 됩니다.대부분의 에이전시 신입 디자이너들이 중국집 요리사 탱크트리와 유사하게 최소 2년 정도 GUI 가이드라인 작업을 하고 난 뒤에 시안 디자인 작업을 참여할 수 있는 구조였습니다. 크리에이티브를 위해 디자인 작업에 시간을 일주일 중 3일을 쓰고, 4일은 가이드를 쳐야 할 정도의 노력과 시간이 드는 노동 집약적 작업이었습니다.이렇듯 GUI 가이드라인 문서 제작은 모든 디자인 요소 정보들을 일일이 확인한 후, 파워포인트로 옮겨 적어야 하는 야근의 헬게이트를 열어주는 대표적인 업무였습니다.디자인 완료 후 개발자에게 “디자인을 이렇게 구현해 주세요.” 라고 말하면 얼마나 쉽나요? 근래에는 야근의 대부분을 차지하는 이러한 업무들로부터 스케치 툴이 많은 디자이너를 구해준 셈입니다.업무의 프로세스상 디자이너가 가이드라인 문서와 이미지 리소스 파일들을 넘겨줘야 개발자들이 개발진행을 할 수 있기에 디자이너들은 타이트한 데드라인에 쫓기듯 업무할 수 밖에 없었습니다.이러다 보니, GUI 가이드라인 문서 제작 중 휴먼에러(크기 정보 오타, 이미지 파일 누락 등)로 개발자가 작업하던 도중 디자이너에게 가이드라인 문서 업데이트 요청을 해오는 경우가 매우 빈번했습니다. 또한, 대규모 프로젝트 일수록 가이드라인 문서, 이미지 리소스 파일, PSD 디자인 파일 등 관리해야 할 대상이 많아서 개발자와 디자이너 사이의 커뮤니케이션 빈도수도 잦아지고 많은 비용이 필요했습니다.비단 3년 전만해도 GUI 디자인을 개발자가 구현하기 위해 필요한 정보를 수천 페이지나 되는 파워포인트 문서로 전달했지만, 요즘은 스케치를 활용한 제플린이나 심플리 등과 같은 가이드 정보를 제공해주는 여러 서비스를 이용하여 가이드 문서 제작은 거의 하지 않고 있습니다. 조만간 가이드 문서가 완전히 사라지는 날이 오지 않을까 싶습니다.그 끝에 크래커나인이 일조하는 날이 오기를 바라며 글을 마칩니다.#에이치나인 #디자이너 #개발자 #협업툴 #크래커나인 #솔루션기업

기업문화 엿볼 때, 더팀스

로그인

/