스토리 홈

인터뷰

피드

뉴스

조회수 2861

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 #개발기 #업무환경 #인사이트 #경험공유 
조회수 3676

jekyll을 이용한 Github 블로그 만들기

Overview“githubPage로 기술 블로그를 만들자!” “jekyll로 만들면 한두 시간이면 가능할 거야!” 지난 1월, 브랜디 기술 블로그 제작 작업을 시작했습니다. 다양한 삽질과 험난한 여정의 결과물인 기술 블로그의 제작 및 커스터마이징 과정을 소개하겠습니다.GithubPage는 Github에서 공식적으로 운영하는 블로그 서비스입니다. 이곳엔 개발자들의 경험이나, 코드가 업로드되어 있습니다. 저장 공간도 무료로 제공되고, 도메인 연결도 편리하게 할 수 있지만, 무엇보다 GithubPage 혹은 GithubIO라고 하면 개발자 스멜이 풀풀 나기 때문에 선택의 이유가 되는거 같습니다.GitgubPage 제작 프로그램은 jekyll, HEXO가 가장 많이 쓰입니다. 브랜디의 기술 블로그는 템플릿의 종류도 많고, 더 어울리는 jekyll을 선택했습니다. jekyll 공식 사이트는 여기를 클릭하세요. 한국어도 지원하니 아주 멋집니다. 변역된 문서가 2015년 11월 23일 문서인 게 함정이지만 기능의 거의 유사하기 때문에 문제될 것은 없습니다. 1. 준비물을 챙기자!$ gem install jekyll $ jekyll new my-awesome-site $ cd my-awesome-site /my-awesome-site $ jekyll serve ▲ jekyll 설치 스크립트이제 브라우저로 http://localhost:4000 에 접속합니다.메인에 내거는 것처럼 설치와 실행이 쉽지만 몇 초 만에 되진 않습니다. 설치가 몇 분 정도 걸리고 ruby나 bundler같은 선행 조건이 갖춰져야 하기 때문입니다.ruby는 있는데 bundler가 없다면 아래와 같이 설치하면 됩니다.gem install bundler 설치가 잘 되고, 사이트 생성 후 실행을 했다면 브라우저와 함께 기본 페이지가 뜹니다. 설치가 성공했다!2. 1차 멘붕, 이제 무엇을?설치는 제법 쉽게 했지만 ‘이제 무엇을 해야 하나’ 막막하기만 합니다. 블로그 작성에 대한 아무런 가이드도 없고, 페이지나 이미지 추가 확인 등의 작업을 커멘드로만 하려니 힘들고 아찔하기만 합니다.커멘드 지옥..jekyll admin을 검색했더니 이런저런 아이들이 나옵니다. 그중에 jekyll 공식 플로그인을 선택했습니다. jekyll-admin 공식 사이트 이미지그런데 사이트 메뉴얼만 보고 설치하기 쉽지 않습니다. 이제 막 jekyll 띄웠을 뿐인데 플로그인은 어떻게 추가하는지 알 길이 없습니다. 이런저런 삽질을 거듭하고 안 되는 영어를 해석하다 보니 얼떨결에 성공했습니다.추가한 프로젝트 root에 보시면 Gemfile이 존재합니다.아마도 사이트 제너레이트 시 실행되는 스크립트인 것 같습니다. 파일을 열고 아래와 같이 추가합니다.# 아래와 같이 한줄 추가해주세요 gem 'jekyll-admin', group: :jekyll_plugins 프로젝트 root로 이동해 설치를 요청합시다.bundle install 달라진 게 없어 보여도 http://localhost:4000/admin 으로 접속하니 아뉫! 관리자가 설치되었습니다. 이제 한시름 놓입니다.3. 마크다운, 넌 누구?마크다운을 잘 안다면 넘어가도 됩니다.관리자를 설치하고 나면 그나마 좀 할만하지만 막상 글을 쓰려고 보니 무언가 다릅니다. HTML이나 위지윅 에디터도 아니고 Textarea만 덩그러니 있기 때문입니다....마크다운은 위키나 Github페이지 설명 작성 등에 쓰이는 언어입니다.1) HTML을 어느정도 한다면 문법만 읽어도 금방 이해할 수 있습니다. 생각보다 어렵지 않아서 간단한 문서 작성은 수월하게 가능합니다. 무엇보다 코드를 붙여넣을 때 아주 좋습니다. ``` PHP 코드 내용 ```위의 그림처럼 작성하면 자동적으로 신텍스 하이트라이트가 적용되니 개발 코드를 전달하기 편리합니다.4. Posts? Pages? Static Files? Data Files?확실히 jekyll은 그동안 봤던 블로그나 워드프레스 등 유명한 블로그와는 많이 다릅니다. 일단 개념부터 짚어보겠습니다. PostsPost는 한 개의 글을 지칭합니다. 블로그의 글 하나입니다. 어느 정도 구축이 되면 Post에서 글만 작성해도 쉽게 운영할 수 있습니다.PagesPost처럼 계속 추가되는 형태가 아닌 고정 페이지를 작성할 때 씁니다. About이나, 채용, 회사소개 등 Post와 분리가 필요한 글을 작성할 때 유용합니다.Static Files정적 리소스를 올리는 기능입니다. 생각보다 버그가 많아서 사용하기 쉽지 않습니다. 저는 이 기능을 커스터마이징해서 약간 쓰기 쉽게 바꾸었지만 쉽지는 않았습니다. 자세한 관리자 커스터마이징은 나중에 다루겠습니다.Data Files정적 데이터를 다루는 기능입니다. 저자 관리나 공통 변수를 담아두면 편리하게 쓸 수 있는 기능입니다. 역시나 버그가 넘쳐납니다. 당분간은 그냥 파일을 직접 수정하는 게 나을 겁니다.5. 블로그 제목 등 설정을 바꾸고 싶다면관리자의 configuration 메뉴를 이용하거나, 프로젝트 루트에서 _config.yml 을 열고 수정해도 됩니다. 사이트에서 사용할 전역 변수나, 플로그인, 기본값 등을 관리해주기 때문에 자주 수정하는 파일입니다. 제목을 변경하려면 title을 찾아서 변경하면 됩니다. 그외의 하단 문구는 buttomtitle을 변경하면 됩니다. 아래 보이는 각종 정보들은 맞게 수정하면 되고, social: 밑에 있는 정보들은 나중에 페이스북 공유나, 트위터 공유 등으로 사용할 수 있습니다. 해당 정보가 없거나 공유를 원치 않는다면 share를 false로 변경합니다. _config.yml은 수정 후 재시작을 해야만 반영되므로 jekyll를 다시 실행하면 됩니다.6. 테마를 적용하자테마 기능은 jekyll를 선택한 가장 중요한 이유였습니다. 멋진 디자인과 추가로 구현된 특수한 기능들은 jekyll이 가지고 있는 큰 메리트입니다. 테마를 사용하려면 소스를 다운로드 받고 압축을 해제해 사용하거나 git checkout 하면 됩니다. 해당 폴더로 이동해 실행하면 테마를 쉽게 사용할 수 있습니다.jekyll serve jekyll은 테마가 완성된 프로젝트 개념이기 때문에 바로 사용이 가능하지만 마이그레이션 이슈가 있습니다. 마이그레이션은 _post의 있는 파일과 _page에 있는 파일을 그저 테마 프로젝트 폴더에 덮어쓰기하면 됩니다.아쉽게도 _config.yml파일은 다시 세팅하는 게 빠릅니다. 어드민 설정 부분도 다시 하면 됩니다. 테마마다 약간씩 기능이 달라 마이그레이션이 안 되는 경우도 있으니 테마는 초기에 선택하는 게 좋습니다. 브랜디 랩스는 Centrarium 테마를 적용했습니다.테마가 적용된 화면7. 글에 이미지를 어떻게 넣을까?글을 쓰면 참고자료로 쓸 이미지도 필요합니다. static file에 업로드 기능이 있지만 업로드를 하면 프로젝트 루트 폴더에 업로드되어 관리상 좋지 않습니다. 앞서 공유한 것처럼 해당 기능 개선에 대해서는 다루지 않을 것이기 때문에 수기로 이미지를 관리하는 방법을 소개하겠습니다.로컬 프로젝트 기준에서 _site는 제너레이트된 최종결과라고 할 수 있습니다. 그래서 _site 폴더에 assets와 같은 폴더가 있으나 그 폴더에 올리면 덮어쓰기와 동시에 초기화 되므로 반드시 프로젝트 루트의 assets에 파일을 올려주시면 됩니다. 폴더를 생성하는 것도 문제 없으므로 포스팅마다 이미지를 나누길 권장합니다. 이미지가 폴더에 복사가 되었다면 이제 글에 넣어봅시다.마크다운 위지윅을 이용해도 좋고 이미지 부분을 HTML코드롤 사용해도 좋습니다. 마크다운으로 이미지를 추가할려면 아래처럼 사용하면 됩니다. ![이내용은 alt속성으로 치환됨](/assest/20180118/test.jpg "이 내용은 타이틀로 치환 됨") assets/test.jpg적용된 이미지이미지의 사이즈나 정렬을 변경하는 건 다음에 다루겠습니다.8. Gnav 변경은 어떻게 할까?커스터마이징한 Gnav영역테마도 적용했고, 글도 쓸 수 있지만 안 쓰는 기능 삭제를 비롯해 손볼 곳은 아직 많습니다. (분명 한두 시간이면 된다고 했던 일이 2주째 수정 중입니다…) 화면 구성을 고치려면 프로젝트에 포함되어 있는 템플릿 파일을 고쳐야 합니다.템플릿은 Liquid 라는 언어로 구성되어 있으며, 문법이 좀 난해하지만 충분히 헤쳐 나갈 수 있습니다. 다만 어디서부터 어떻게 고쳐야 하는지를 파악하는 게 어렵죠. 문법은 공식 사이트를 참고하고, 사용 가능한 변수는 여기를 참고하면 됩니다.사용 가능한 변수는 site와 page로 나눌 수 있습니다. site는 _config.yml 설정한 내용과 jekyll이 지원하는 전역 변수들입니다. page는 해당 페이지에 지정된 세부 변수들입니다. 글의 제목이나 경로 내용들은 기본적으로 세팅되어 있습니다. 추가적인 값을 다루려면 post를 작성하면서 meta정보를 추가하면 됩니다.템플릿의 시작파일은 index.html이고, 페이지에 layout이 지정되었다면 _layouts 안에 있는 [layout].html이 됩니다. 기본적 틀은 _layout/default.html에서 파생됩니다. 그외 파츠로 사용되는 HTML파일은 _includes에 넣고 `{% include header.html %} 같은 방식으로 추가하면 됩니다. 우리 변경하려는 파츠는 header.html에 있습니다. site.pages에는 모든 페이지가 들어있기 때문에 그중에 gnav가 지정된 글만 상단에 노출되게 했습니다. 그리고 상단 글에 대한 정렬이 없기 때문에 좋은 방식은 아니지만 1~10까지 숫자를 기입하면 순서대로 나오게 코드를 구성했습니다. (site.pages에는 posts와 pages가 같이 나옵니다.){% for i in (1..10) %}   {% for page in site.pages %}     {% if page.title and page.gnav == i %}     {{ page.title }}     {% endif %}   {% endfor %}  {% endfor %} 글에 옵션을 지정한 화면이제 pages에서 상단에 노출하고 싶은 글만 gnav를 숫자로 부여해 노출할 수 있게 변경했습니다.9. 스타일 변경은 어떻게 할까?sass로 구성된 스타일의 변경은 심도있게 다루지 않으려고 합니다. sass를 처음 사용한 것도 있지만 내용이 너무 깊어지기 때문입니다. 스타일변경은 _sass 밑에있는 scss 파일을 변경하면 되고, 템플릿마다 구조가 다르기 때문에 열심히 찾는 수밖에 없습니다.10. 저자 기능을 추가해보자 (1)최고의 난이도를 자랑하는 신규 기능 추가입니다. 브랜디의 기술 블로그에서는 작성자를 클릭하면 작성자의 글만 따로 모아서 볼 수있습니다. 하지만 이 기능은 공식적으로 지원되는 것이 아니기 때문에 처음부터 만들어야 했습니다. 완성된 작성자 기능위의 이미지와 같은 기능을 구축하려고 collection을 사용했습니다. collection은 posts나 pages와 같이 그룹핑된 글 목록을 이야기 합니다. posts나 pages는 기본 세팅되어 있고, 약간(?)의 설정 변경으로 collection을 추가할 수 있습니다. 작성자의 메인 페이지가 필요하니 authors라는 collection을 추가해보겠습니다.# _config.yml collections:   authors:     title: Authors     output: true jekyll을 재시작하면 아래와 같이 Authors가 관리자에 추가된 것을 볼 수 있습니다.authors는 작성자 메인 페이지만 생성하면 되므로, 내용에는 작성자에 대한 소개글만 간략히 쓰면 됩니다. jekyll admin에 한글 버그가 있기 때문에 우선 영어로 작성하고, 제목을 다시 한글로 수정하면 됩니다.포스팅마다 저자의 정보가 공통적으로 나와야 하기 때문애 위의 전역변수에 authors를 추가해 따로 관리하게 했습니다.# data/authors.yml # authors 공용 변수   - name: chunbs     koname: 천보성 팀장     email: [email protected]     position: R&D 개발2팀     img: /assets/profile/chunbs.jpg   - name: kangww     koname: 강원우 과장     email: [email protected]     position: R&D 개발2팀     img: /assets/profile/kangww.jpg 그리고 작성자의 포스팅을 엮어주려고 작성자의 아이디가 같을 때, 포스팅으로 나오게 구성합니다.{% if post.author %} {% for author in site.data.authors %}   {% if post.author == author.name %}   {{author.koname}}   {% endif %}  {% endfor %}  {% endif %} 11. 저자 기능을 추가해보자 (2)데이터가 준비되었다면 저자 레이아웃을 추가해야 합니다.(이거 도대체 언제 끝날까요) 저자가 작성한 글만 노출되어야 하는 게 어려울 수도 있지만 jekyll의 구동 원리를 이해하면 손쉽게 할 수 있습니다.jekyll은 내용 수정이 발생되면 전체를 다시 컴파일하는 구조입니다. 다시 말해 일부 파일이 변경되면 노출되는 모든 html파일을 다시 랜더링해서 write하는 것입니다. author의 각 작성자 페이지는 컬렉션에 포함되어 있기 때문에 랜더링이 발생하고 site.posts엔 작성된 모든 페이지 정보가 있습니다. site.posts를 foreach를 돌리고, 저자가 일치하는 페이지만 리스트로 보여줍시다.{% for post in site.posts %} <!-- author 정보가 저자와 같은 경우만 리스트로 출력한다. --> {% if post.author == page.author %}       {{ post.title }}         {{ post.content | strip_html | truncatewords: 25 }}         {{ post.date | date: "%Y-%m-%d" }}           {% if post.author %}         {% for author in site.data.authors %}           {% if post.author == author.name %}           {{author.koname}}           {% endif %}         {% endfor %}       {% endif %}       {% if forloop.last == false %} {% endif %}   {% endif %} {% endfor %} Conclusionjekyll admin은 은근히 버그가 많습니다. 그래도 ‘md파일을 메모장으로 작성하세요’라고 하는 것보단 편하죠. 다양한 기술을 사용하기 때문에 어려울 수도 있겠습니다. 글에서 소개할 수 없거나, 너무 깊어지는 내용은 소개에서 제외했습니다. 양해를 부탁드립니다. 대신에 브랜디 랩스는 저의 피땀 눈물로 만들어졌다는 걸 기억해주세요… 기타jekyll의 기본값 설정을 이용하면 layout과 같은 공통적인 부분을 쉽게 설정할 수 있다.# _config.yml defaults:  - scope:     path: ''     type: posts   values:     #permalink: "/blog/:title/"     layout: post     cover: /assets/default.jpg     author:  - scope:     path: ''     type: authors   values:     layout: author     cover: /assets/author.jpg     subtitle: ~담당하고 있습니다.     author: 영문이름 jekyll admin이 버그가 많아서 업로드 기능은 커스터마이징 했다. 루비와 UI코드를 고쳐서 다시 빌드하는 어지러운 작업을 했다.만약 버그를 고치기 어렵다면 IDE로 파일을 직접 수정하는 게 안전하다. 참고 1)마크다운 작성법은 여기를 참고하세요.글천보성 팀장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 3657

포스트맨 200% 활용하기

편집자 주 MAC OS 기준으로 작성했으며, 본문 내용 중 Proxy(또는 프록시)는 영문으로 통일하여 표기함. OverviewPOSTMAN은 API 테스트에 큰 도움을 주는 도구입니다. 강력한데다가 무료입니다. 안 쓸 이유가 없군요. POSTMAN은 사용하는 방법도 쉽습니다. 그래서 이번 글에서는 최근에 나온 POSTMAN native 버전 패킷캡쳐 방법을 공유하겠습니다.native App은 기존 크롬 플러그인 버전보다 깔끔하고 버그도 많이 줄었습니다. 하지만 원래부터 강력했던 postman interceptor가 아직 지원하지 않습니다.1)공식 블로그 답변입니다.이미 interceptor를 사용하고 있어서 native App에 대한 니즈는 없었는데요. 한글 패킷 캡쳐를 시도하고 생각이 완전히 바뀌었습니다. intetceptor로 캡쳐된 패킷테스트 중이던 공지사항 제목이 이상하게 변경됐습니다.Postman Proxy를 써보자!어쩔 수 없이 native App 을 써야겠다고 생각했습니다. 가장 먼저 postman interceptor에 연결할 방법이 필요했는데 위의 공식 블로그 답변처럼 지금은 안 된다고 합니다. 구글링을 했더니 아래와 같은 글이 보였습니다.스마트폰이나, 기타 기기들의 패킷을 캡쳐할 수 있기 때문에 매력적인 방법입니다. 웹을 사용할 땐 브라우저를 Proxy 태우면 결과는 비슷하게 나올 겁니다.native Appnative App은 여기에서 다운로드 받을 수 있습니다. nativeApp을 켜면 오른쪽 위의 메뉴에 interceptor 아이콘은 없고 위성안테나 모양의 아이콘이 있습니다. 이것은 Proxy Server 기능입니다. Proxy Server를 postman native가 구동해주고 사용하는 방식이죠.Proxy 설정 화면이 뜨는 기본 포트는 5555번입니다. 따로 할 건 없고, 캡쳐 위치는 기본 값인 History로 지정합니다. 만약 다른 컬렉션에 내용을 모으고 싶다면 그곳으로 지정하세요. Connect 버튼을 클릭하면Proxy가 구동됩니다.요청 내용을 긁어 모을 때다!Proxy 세팅을 마쳤으니 브라우저를 연결해야겠죠? 일반적인 방법으로는 연결되지 않습니다. 여기선 크롬 확장 프로그램인 Proxy SwitchyOmega의 도움을 받았습니다. 다운로드는 여기를 클릭하세요.이것은 Proxy 스위칭 프로그램입니다. 도메인 단위로 설정이 가능하기 때문에 on 또는 off 따로 하지 않고도 사용이 가능할 겁니다. 플러그인 설치를 마쳤다면 설정을 유도합시다.Server에는 localhost, Port에는 5555를 적어주세요.캡쳐하고 싶은 사이트에 들어가 Direct 옵션을 켭니다.Proxy를 활성시킵니다.브랜디 주요 도메인인 brandi.co.kr을 클릭해 Proxy를 활성시키면 ***.brandi.co.kr 도메인은 Local Proxy를 타고 넘어가는데요. 이제 받기만 하면 됩니다. (빵끗)진짜 긁어 모아보자!캡쳐하려고 했던 사이트에 접속해 요청을 발생시킵니다.내부 테스트 서버postman native App 캡쳐 내용와우! 발생한 요청 내용이 캡쳐되어서 들어오기 시작합니다.속이 뻥!!!속을 썩이던 한글도 깔끔하게 캡쳐되었군요. 이제 행복한 테스트만 남았습니다. 즐거운 시간 되시길 바랍니다.소소하지만 알찬 팁1: 필터 기능proxy 설정도구에서 필터 기능을 사용하면 원하는 것만 캡쳐할 수 있습니다.소소하지만 알찬 팁2: 테스트 기능스마트폰의 native App은 위와 같이 설정하면 테스트할 수 있습니다. 이제 휴대폰 테스트 결과를 PC로 수집할 수 있을 겁니다. 앱 테스트에 대한 상세 설명은 여기를 클릭하세요.소소하지만 알찬 팁3: 안 쓸 때는..proxy를 안 쓸 때는 System Proxy를 클릭해 끄도록 합시다.1) interceptor는 브라우저 요청을 postman에서 패킷을 캡쳐해주는 도구다.참고Capturing HTTP requests글천보성 팀장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #Postman
조회수 1204

머신러닝 엔지니어 정갑님을 소개합니다

같이 일하고 있는 직장 동료들에 대해 얼마나 알고 계시나요? 엑스브레인처럼 작은 팀의 경우에는 함께하는 한 분 한 분이 팀 전체 분위기에 끼치는 영향이 상당하답니다. 또한, 머신러닝 툴 ‘다리아’로 저희가 꿈꾸는 데이터 사이언스계의 변혁을 일으키려면, 이를 위해 일하는 팀 또한 서로 잘 알고, 협력할 줄 알아야겠죠.각각 개성이 넘치지만, 서로 모여 엑스브레인의 매일매일을 풍족하고 즐겁게 만들어가는 팀을 소개합니다! 각 멤버들의 일상과 엑스브레인에서의 직무에 대해서도 알아보고, 또 뉴욕타임즈에 실린 “상대방과 사랑에 빠질 수 있는 36가지 질문” 중 직장 동료에게 할 수 있을 만한, 가장 흥미로운 질문들을 추려서 진행한 인터뷰를 통해 엑스브레인 팀 멤버 개개인의 색다른 매력을 만나보세요.(그렇다고 진짜로 사랑에 빠지시면 곤란합니다…)가장 최근 엑스브레인 팀에 합류하신 정갑님은 따뜻하고 밝은 산타 클라라에서 서서히 동결 준비 중인 서울로 오셨답니다 (감기 조심하세요…). 그래도 석박사 시절을 이보다 훨씬 춥고 눈에 갇히기 일쑤인 미시건에서 보내셨다고, 추위에는 강하다고 하시네요. 머신러닝 엔지니어로서 다리아의 엔진을 위한 개발 작업을 하시는 정갑님은 여가시간엔 반려묘 졸리와 브래드와 함께하거나, 요리나 등산을 즐기시기도 한답니다. 정갑님을 만나보세요!Fun Fact: 정갑님은 팀 멤버 중 가장 아침 일찍 출근하신답니다안녕하세요 정갑님! 엑스브레인에서의 역할에 대해서 얘기해주세요정갑: 머신러닝 엔지니어로 입사를 했고, 머신러닝 엔진을 개발하는 것이 주요 업무입니다. 많은 사람들이 머신러닝을 쉽게 쓰기 위해서는 현 상황에서 어떤 기술들과 어떤 문제점이 있는지 알아내야 하고, 저는 그 문제들을 해결하기 위한 중요한 기술을 찾아서 연구를 하고 해결 방안을 찾는 역할을 하고 있습니다어떤 계기로 머신러닝 엔지니어가 되셨나요?정갑:대학원, 회사에서 연구를 하면서 머신러닝의 사용자 입장이었는데, 사용하고 이해하는 과정이 상당히 어려웠어요. 기존에 나와있는 툴들도 사용성이 좋지 않았고…이런 과정을 제가 직접 개선하면 좋을 것 같아서 머신러닝 엔지니어로서 엑스브레인 팀에 들어오게 되었습니다.왜 엑스브레인인가요?정갑: 일단 조직의 인력구성이 마음에 들었고, 팀원들의 역량과 조직문화가 제가 원하는 분위기여서 좋았습니다. 두번째는 엑스브레인이 추구하고자 하는 가치 — 머신러닝이란 기술에 대해서 갖고 있는 생각 — 이 제가 평소에 갖고 있던 생각과 일치해서요…머신러닝을 단순히 이윤 추구의 수단으로 생각하는게 아니라, 이걸 더 많은 사람들이 이용해서 가치를 찾게 하자는 뜻이 좋았어요. 또, 초창기 회사에서 한 번 어떻게 조직이 커가고, 함께 성장하는 경험을 해보고 싶기도 했고요. 그리고 주변 신뢰할 만한 분들에게서 엑스브레인에 대한 좋은 이야기도 많이 들었어요.보통 하루 일과가 어떻게 되나요?정갑:아침 9시 15분 쯤에 도착합니다. 밤새 와 있던 슬랙 메시지와 이메일을 체크하고, 커피 한 잔을 마십니다. 아침엔 집중이 잘 되니까 읽어봐야 될 논문이나 자료 등을 보고, 또 제가 머신러닝을 전공하지는 않았으니까 아직 따로 공부해야 될게 많기 때문에 그 부분에도 신경쓰고 있어요. 머리가 워밍업이 되면 기존에 짜여있던 코드를 보고, 개발할 부분이 있으면 개발을 합니다. 점심시간이 되면 점심을 같이 먹기도 하고요 (미국에 있을 때는 따로 점심 시간을 내서 팀원들끼리 대화할 기회가 없었기 때문에, 엑스브레인의 이런 문화가 좋습니다). 연구개발과 미팅의 연속이죠. 오늘은 현재 머신러닝 엔진에 문제가 있어서 그 이슈를 뜯어보았는데, 그 과정을 바탕으로 어떻게 해결할 것인지에 대한 아이디어를 구현하는 과정을 거쳤습니다. 구현과 테스팅과 trial and error을 앞으로 몇 주간 반복할 것 같아요.정갑님의 직무 중 가장 즐기는 일은?정갑:무언가를 향상시키는 것? 이렇게 고치면 좋아질 것 같은데…라는 생각을 가지고 일하는 게 좋습니다. 저희 기존 시스템을 향상시키는데도 관심이 있지만, 롱텀으로 봤을 땐 엑스브레인만의 유니크한 기술을 가져야 하기 때문에 그 기술이 뭔지 알아내고, 개발하고, 사용자들의 니즈를 파악하는 것에 관심이 있습니다. 그래서 시스템의 문제를 찾으려고 많은 시간을 생각하는데 투자하고 있죠.반대로, 가장 하기 싫거나 어려운 일은?정갑:어려워서 하기 싫다기보다는… 풀어야 할 문제를 찾는 거 자체가 어려운 것 같아요. 이럴 땐 네 가지 상황이 있는데, 이미 찾은 문제, 풀수 없는 문제, 너무 쉬워서 관심이 없는 문제, 그리고 풀수 있고 임팩트 있는 문제가 있죠. 저희는 그 마지막 예를 찾으려고 하는 거고요. 그 과정이 힘들긴 하지만 즐기고 있습니다.정갑님 책상에 있는 물건 중 정갑님을 가장 잘 대변한다고 생각하는 아이템은?정갑:딱히 책상에 물건을 두지는 않는데… 미국에서 일하던 시절 실리콘밸리에서 여러 유명한 회사들 (트위터, 링크드인 등등) 구경을 했는데 엔지니어들은 대부분 책상에 컴퓨터 하나만 있고 다른 장식이 없더라고요. 저는 그런 단순함이 좋았어요.엄청난 집중력을 발휘하시기도 하죠최근에 합류한 멤버로서, 정갑님이 생각하시는 엑스브레인의 비전을 말해주세요.정갑:비전이라기보다는 나아가야 할 방향 같은 건데, 지금은 머신러닝에 대해서 사람들이 굉장히 많은 이야기를 하지만, 차분하게 앉아서 연구와 기술개발을 해야 할 시점이라고 생각합니다. 롱텀으로 긴 안목을 갖고서 차근차근하게 기초단계를 밟아나가는, 유행에 휩쓸리지 않고, 기본에 충실한 엑스브레인이 되었으면 좋겠어요.씨네마 소사이어티 때 추천하고 싶은 영화가 있다면?정갑:맷 데이먼 주연의 Downsizing…개봉하면 팀 멤버들과 같이 보고싶네요. 끝나고 토론할 주제가 많을 것 같아서요.10년 뒤 지금, 정갑님은 어떤 모습일까요?정갑: 앞으로의 10년 동안 공부를 해서 제대로 된 머신러닝 엔지니어가 되고 싶어요. 지금은 초기 엔지니어지만, 그때는 좋은 개발자들을 발굴해 내서 성장하는데 도움도 줄 수 있는 시니어 급 엔지니어가 되고 싶습니다.내가 생각하는 엑스브레인의 “엑기스”를 세 단어로 말한다면?정갑:진지와 엉뚱함의 공존?엑스브레인의 어떤 멤버와도 저녁 식사를 할 수 있다면, 누구와 같이 먹고 싶나요?정갑:진영님. 같이 점심을 먹어본게 입사했을 때, 수요미식회 때 빼고는 없어서... 진영님과 대화할 기회가 별로 없었는데 재밌는 분일 것 같습니다.이 세상 어느 누구와도 저녁 식사를 할 수 있다면, 누구와 같이 먹고 싶나요?정갑:칼 세이건? 그분의 책을 읽고 어렸을 때 가졌던 우주에 대한 여러가지 동경을 되살려 보고 싶네요… 과학에 대한 열정을 다시 느끼고 싶기도 하고.유명해지고 싶나요? 어떤 방법으로요?정갑:아니요.정갑님에게 “완벽한” 날이란 어떤 날인가요?정갑:아직 오지 않은 내일이…아닐까요? 너무 엉뚱한 대답인가요?90살까지 살 수 있고 마지막 60년을 서른 살의 마음, 혹은 서른 살의 몸으로 살 수 있다고 해봅시다. 몸과 마음 중 어느 쪽을 택할 건가요?정갑: 몸. 마음은 성숙하지만, 몸은 퇴화하니까…정갑님의 인생에서 가장 감사하게 생각하는 것은 무엇인가요?정갑:건강함인 것 같아요.내일 아침 눈을 떴을 때 어떤 능력이나 특성을 가지게 된다면 어떤 것이었으면 좋겠어요?정갑:무언가를 읽고 이해하는데 오래 걸리는 편인데, 이해력이 빨라지면 좋겠습니다. 두뇌회전도 빨라지고…지금까지 정갑님 인생에서 가장 잘해낸 일은 무엇인가요?정갑:좋은 사람과 인연을 맺은 일인 것 같아요.엑스브레인에서 가장 기억에 남는 일이 뭔가요?정갑:오늘 인터뷰…? (하하하)혹시 농담의 대상으로 삼아서는 안 된다고 생각하는 것이 있다면 어떤 것들이 있을까요?정갑:듣는 대상에 따라 다르겠지만, 사람들의 약점에 대해서는 농담을 하지 말아야 한다고 생각합니다.정갑님의 모든 것이 있는 집이 불에 타고 있습니다. 가족들을 다 구한 후 마지막 한 가지를 가지고 올 수 있습니다. 어떤 것을 가지고 나올 건가요?정갑:하드 드라이브! 제 모든 사진과 파일이 담겨 있거든요.#엑스브레인 #팀원소개 #팀원인터뷰 #기업문화 #조직문화 #팀원자랑 #머신러닝 #머신러닝엔지니어
조회수 4939

소스코드 리뷰에 대한 짧은 이야기...

개발자와 개발 조직에게 소스코드 리뷰는 필수적이다. 팀간의 협업과 대화를 보다 원활하게 만들어 주는 매우 필요한 절차이다. 슬랙과 같은 협업도구가 명쾌하게 의미 있게 활용되려면 개발팀 간의 소스코드 리뷰는 필수적으로 수행되는 것이 좋다.매우 당연한 이야기이지만, 소스코드 리뷰는 거북하고 불편하고 어렵고 힘들다. 그럼에도 불구하고 필수적인 이벤트가 되어야 하는 이유가 너무도 많다. 개발자들에게 코드리뷰에 대한 이슈를 설득하고 실제 행위를 발생시키는 것은 정말 어려운일이다. 더군다나 뜬금없이 코드리뷰 이야기를 회사나 팀리더에게서 갑자기 듣는다면 개발자는 매우 불편해 한다. 그것은 매우 당연한 반응이다. 그러므로, 가능하다면 팀 세팅 초기 시부터 이 소스코드 리뷰 문화는 만들어질 수 있게 노력하는 것이 최선일 것이다.초기에 세팅된다면 그 후에 들어오는 팀원들은 자연스럽게 그 문화에 익숙해진다. 이런 일련의 작업들은 결국 조직과 팀의 단결과 협력, 향후 유지보수에 매우 긍정적인 효과를 준다.매우 당연하지만 개발자들은 팀에 소속되고 빠져나가기를 반복한다. 이를 두려워하지 않는 방법 중에 가장 먼저 선택할 수 있는 것이 바로 코드 리뷰라는 행위다. 인수인계와 유지보수를 위해서 소스코드 리뷰를 각 단계별에 배치해두고, 그 시간을 투자하는 것을  아까워하지 않도록 하자.그렇다면, 소프트웨어의 본체인 소스코드를 타인이 리뷰한다는 것이 왜 어려울까? 그것은 소스코드는 언제나 완성상태가 아니라는 점 때문이다. 개발자의 생각은 무언가 다양한 변화를 예측하고 있고, 그 상세한 준비를 담고 있다. 언제나 소스코드는 완성 상태가 아니라, 변화되어야 하는 시간의 축을 담고 있기 때문이다.하지만, 소프트웨어 품질이 중요한 현재의 시점에서 본다면, 코드 리뷰라는 행위는 정말 필수 불가결한 행위에  해당한다고 생각한다.이런 필수적인 코드리뷰는 그 형태와 범위에 대해서 팀 내부에 잘 정의되어야 한다.그래서, 보통 이 코드리뷰를 어떻게 할 것인가에 대해서 조직이나 담당하는 사람의 경우에는 명쾌한 판단 기준이 있어야 한다. 그러한 ‘판단기준’을 가져야만 명확한  리뷰될 수 있다.이를 두고, 디자이너에게는 크리틱(critique-비평)이 있고, 개발자에게는 코드리뷰가 있다고 정의한다.좋은 비평을 받고 좋은 리뷰를 하려면 다음의 3가지 원칙이 필수이다.1. 리뷰는 언제나 상호 합의가 되어진 상황에서 진행되어야 한다.2. 리뷰어의 해당 결과물에 대해서 객관성을 가지고 서로 인지해야 한다3. 개발자 자신의 작업물에 대해서 정말 객관적으로 바라볼 수 있는 작성가가 선정되어야 한다.특히, 소프트웨어 코드는 정량적인 검토와 정성적인 검토를 구분해야 한다. 이 영역의 구분이 모호해지면, 리뷰는 그 방향성을 상실하게 된다. 그중에 특히, 정량적인 검토와 기본적인 규칙들은 가능한 자동화하고, 소스 형상관리 도구에서 기본적인 것들의 규칙들을 지키도록 권장하여야 한다. 최소한 이 정량적인 것만 자동화하고  규칙화해도 소프트웨어의 품질은 급상승한다.하지만, 코드는 논쟁을 발생시키고, 어떤 것이 우선적인지에 대해서 서술하기 매우 어렵다. 이러한 점은 정성적인 부분에 대해서 검토할 때에 고민하자.코드리뷰의 정도는 어느 정도 해주어야 하는가?그 전부터 주목하는 개발 방법론의 추세는 ‘테스팅’을 주로 하고, SRS와 같은 요구사항에 집중하기 보다는, TDD와 같은 방법으로 완성 산출물을 높이는 방법을 현재에는 주로 사용하고 있다.그것은 과거에는 요구사항을 통해서 결과물이 완성되는 SI성 개발이 주로였다면, 현재에는 요구사항은 계속 변화하고 버그 없는 결과물이 중요시되는 테스트를 얼마나 더 집중적으로 하느냐에 따른 웹서비스의 시대이기 때문에 그 방향성은 시대에 따라서 변화를 많이 하였다. 그래서, 슬프지만, 당장의 성과물을 위해서라면 코드리뷰보다는 테스팅에 집중하는 것이 더 효율적이다. 빠르게 고속 개발하고 테스트를 통해서 버그를 찾은 다음 수정하는 것이 ‘특정 기능들을 나열하고 기능을 만족하는 소프트웨어’의 경우에는 테스트 주도 개발 방법이 가장 적합하다고 할 수 있다.물론, 이러한 방향성이나 전체적인 틀에 대해서는 아키텍트가 잘 결정하여야 한다. 내가 속한 개발 결과물이 어떤 결과물이냐에 따라서 이 방법은 혼용되어져서 사용되어야 하기 때문이다.하지만, 이번 글의 주목적은 코드리뷰. SRS중심이건, TDD중심이건. 코드리뷰는 중요하다는 것을 강조하고 싶다. 특히, 코드리뷰는 ‘기능 나열’이 아닌, 어느 정도 이상의 복잡도나 코드 품질이 필요한 경우에는 필수적으로 수행하는 것이 매우 현명한 행동이다.물론, 코드리뷰 행위가 불필요한 업무들도 많다. 정해져 있는 단순한 업무를 수행하는 경우에는 굳이 할 필요 없다. 국내에서 SI를 하는 경우에는 대부분 코드리뷰가 필요 없는 업무를 하는 소프트웨어 개발자들이 절대 다수인 경우도 많이 보았다.일반적인 SI의 형태라면 워크 스루의 형태만 적합하다. 특정 도메인에 매몰되어 있고, 처리방법이 명쾌하기 때문에, 해당 경험들을 교환하는 것으로도 충분하기 때문이다. 그리고, 자동화된 테스트 수행방법을 최대한 갖추어두는 것이 가장 현명하다.그러므로, 코드리뷰는 어느 정도 솔루션이나 서비스 등을 고려하고 있는 곳에서 더욱 적합하다고 정의한다.코드리뷰는 특정 제품이나 서비스를 발전적으로 지향하고 있는 경우라면 필수적으로 선택해야 한다. 하지만, 일부 제품의 경우에는 발전적인 지향이 굳이 필요 없는 제품 라인업을 가진 경우에도 굳이 수행할 필요 없다.그 경우에는 선택적인 코드리뷰를 지향하면 된다. 비용상의 문제 때문에 굳이 코드리뷰를 억지로 진행할 필요는 없는 경우도 많다. 대부분의 소프트웨어 개발은 테스트 케이스를 잘 만들고, 통과시키는 것으로써 충분한 신뢰를 가지면 충분한 경우가 대부분이다.특히, 시장이 고착상태이거나, 특별한 변화의 폭이 없다면, 그 정도로 충분한 경우가 된다. 다만, 글로벌 서비스나 웹서비스 등의 지속적인 확장이 필요한 경우라면, 코드리뷰는 필수라고 할 수 있다.코드리뷰가 필요 없는 경우 체크리스트는 다음의 5가지 정도를 체크해보자.1. 특정 도메인만 다루는 팀이나 회사의 개발팀인가?2. 지난 2~3년 정도 솔루션이 크게 변한 것이 없으며, 향후로도 기업이나 팀에서 투자가 없을 예정이다.3. 현재 개발자들이 해당 솔루션에 대한 개발일을 5년 이상하고 있다.4. 기능 위주의 SI성 업무를 주로 처리하고 있으며, 복잡한 알고리즘은 존재하지 않는다.5. 비용과 일정상 개발팀에게 리소스 투여가 불가능하다위의 사례에서 1개 이상이라도 체크된다면, 코드리뷰는 성립하기 힘들다. 대부분 단념하고, TDD나 테스트 케이스를 가능한 많이 축적하여 소프트웨어 품질을 올리기를 권장한다.코드리뷰가 필요한 경우의 체크리스트도 다음의 5가지 정도를 체크해보자.1. 다국어와 시장이 다변화된 환경에서 소프트웨어가 구동되어야 한다.2. 코드의 복잡도가 높으며, 단순 기능 나열의 요구사항이 아니라, 소프트웨어 아키텍처가 별도로 구성되기 시작하였다.3. 사용자의 경험성을 증가하기 위하여 매우 많은 변화가 예측된다.4. 현재 개발 중인 서비스는 중단 없이, 지속적으로 발전되어야 하는 서비스이다.5. 목표 요구사항이 계속 변화하고 있고, 프레임워크를 지향하여 소프트웨어 품질의 요구사항이 매우 중요하다.위의 케이스에서 하나라도 해당이 된다면, 코드리뷰는 매우 효과적으로 소프트웨어에 의미 있는 결과물들을 얻어 내기 위한 좋은 방법이 된다.하지만, 다음과 같은 경우도 같이 고려하여야 한다.코드리뷰의 정도와 질에 대한 검토 리스트의 최소 체크리스트는 다음의 3가지이다. 물론, 이 정의는 조직 내의 아키텍트나 아키텍트 롤을 하는 사람이 결정하는 것이 좋다.1. 실험적인 코드인가?2. 1~2명 이상이 공동으로 작업하는 코드인가?3. 향후 버려질 가능성이 높은 코드인가?코드리뷰를 하지 않는 경우에는 해당 코드의 repository나 디렉터리를 완전하게 분리하고, 리뷰가 안된 코드를 명쾌하게 구분할 수 있어야 한다. 그리고, 그 정보는 팀 전체에게 공개되어야 한다.가장 첫 번째는 코딩규칙 가이드라인의 준수 여부를 체크하는 것이다.개발자들 간의 상호 중요한 것은 스타일 가이드이다. 하지만, 정말 지키기 어려운 것 또한 스타일 가이드라고 할 수 있다. 하지만, 스타일 가이드는 가능한 준수해야 한다. 하지만, 100% 준수하려는 것은 매우 비효율적인 상황을 만들 수 있다. 하지만, 이 경우에 최소한 리뷰어가 제시하는 기준이나 변경 방향에는 대부분 수긍하는 것이 가장 현명하며, 이 부분은 해당 팀의 가장 경험이 풍부한 사람이 리드하는 것이 좋다.그래서, 소프트웨어 개발에는 경험이 풍부한 아키텍트의 역할과 선임의 역할이 가장 중요하다. 소셜에서 이야기하는 가장 중요한 포인트는 이런 경험이 풍부한 선임 개발자가 있다면, 돈이 얼마가 들더라도 ‘개발팀’에 모셔야 한다! 가 정답일 것이다.아직까지 이 부분은 ‘공학’으로 해결할 수 없고, ‘엔지니어링’과 ‘경험’에 의존할  수밖에 없다.주석의 경우에도 ‘가독성’이 충 부한 코드에는 서술할 필요 없다. 이 부분에 대해서는 꾸준한 팀원들 간에 코딩 문화에 대해서  커뮤니케이션하면서 주석의 범위에 대해서 공론화하는 것이 현명하다. 그래서, 소프트웨어 개발은 대부분이 ‘커뮤니케이션’이고 ‘소통’이다. 그래서, ‘팀워크’이 가장 중요한 것이고. 변수의 명칭에 대해서도 ‘명확’하다는 선에서 합의해야 한다.테스트가 쉽지 않은 구조는 다른 문제를 야기한다. Junit과 같은 단위 테스트 도구로 손쉽게 정의가 가능한 구조가 아니라면, 변경해야 한다.코드리뷰 후에 분명하고 타당한 지적에도 고집이 세서 변화가 없는 경우에는 한두 번 이야기하고 더 이상 변화가 없다면, 포기하고. 해당 코드를 격리하여 관리하는 것이 현명하다.  팀원들 간에 감정이 상하는 것이 더 위험하다. 사람은 변하지 않는다 감정에 대한 다툼이나 기대를 할 필요가 없다.UI가 중요한 코드는 해당 코드들이 급변할 가능성이 농후하다. 처음부터 공을 들여서 추상화를 실현하지 않으면, 해당 코드 때문에 프로젝트가 심각해질 수 있다. 사용자에게 더 좋은 경험을 전달하려고 하면, UI코드는 계속 변화를 일으킨다.테스트 코드 여부? 로직에 대한 검토, 변수 네이밍 검토와 레이아웃에 대한 것들? 에 대해서는 다음과 같이 판단하고 체크해보자.코드리뷰는 대부분 ‘직관’에 의존한다. 그래서, 정말 어렵고. 경험이 풍부한 사람이 할  수밖에 없다. 다만, 이러한 코드 리뷰 시의 체크리스트 항목을 몇 가지 간단하게 정리할 수 있다. 최소한의 2가지는 꼭 지키자.코드 리뷰 시의 필수 내용 두 가지는 다음과 같다.1. 코드 검토는 1시간 이내에 끝낼 분량으로 검토한다.2. 코드는 200라인 이상을 한 번에 검토하지 마라이 기준이 어겨지면, 리뷰어는 제대로 된 리뷰를 하기 어려울 것이다.  그리고, 이러한 리뷰를 하는 동안 기능에 대한 검토 체크사항에 대해서 나열해 보면 다음과 같이 나열이 될 수 있을 것이다.1. 시스템의 요구사항이 제대로 반영되었는가?2. 시스템의 설계의 규격대로 구현되었는가?3. 과도한 코딩을 하고 있지 않는가?4. 같은 기능 구현을 더 단순하게 할 수 있는가?5. 함수의 입출력 값은 명확한가?6. 빌딩 블록들( 알고리즘, 자료구조, 데이터 타입, 템플릿, 라이브러리, API )등이 적절하게 사용되었는가?7. 좋은 패턴과 추상화( 상태도, 모듈화 )등을 사용해서 구현하고 있는가?8. 의존도가 높은 함수나 라이브러리 등의 의존관계에 대해서 별도 기술하고 있는가?9. 함수의 반환(exit)은 한 곳에서 이루어지고 있는가?10. 모든 변수는 사용 전에 초기화하고 있는가?11. 사용하지 않는 변수가 있는가?12. 하나의 함수는 하나의 기능만 수행하고 있는가?또한, 스타일과 코딩 가이드에 대해서고 검토하고 리딩을 해야 한다.1. 코딩 스타일 가이드를 준수하고 있는가?2. 각 파일의 헤더 정보가 존재하는가?3. 각 함수의 정보를 코드에 대해서 설명하기에 충분한가?4. 주석은 적절하게 기술되어있는가?5. 코드는 잘  구조화되어있는가? ( 가독성, 기능적 측면 )6. 헤더, 함수 정보를 도구로 추출해서 자동으로 문서화할 수 있는 구조인가?7. 변수와 함수의 이름이 일관되게 기술되어 있는가?8. 프로젝트의 가이드를 통한 네이밍 규칙을 준수하고 있는가?9. 숫자의 경우 단위에 대해서 기술하고 있는가?10. 숫자를 직접 서술하지 않고, 상수를 사용하고 있는가?11. 어셈블리 코드를 사용하였다면 이를 대체할 방법은 없는가?12. 수행되지 않는 코드는 없는가?13. 주석 처리된 코드는 삭제가 되었는가? ( 버전 체크가 되었는가? )14. 간결하지만 너무 특이한 코드가 존재하는가?15. 설명을 보거나 작성자에게 물어봐야만 이해가 가능한 코드가 있는가?16. 구현 예정인 기능이 있다면, ToDo주석으로 표시되어 있는가?가장 중요한 아키텍처에 대한 검토를 잊으면  안 된다.1. 함수의 길이는 적당한가? ( 화면을 넘기면  안 된다. )2. 이 코드는 재사용이 가능한가?3. 전역 변수는 최소로 사용하였는가?4. 변수의 범위는 적절하게 선언되었는가?5. 클래스와 함수가 관련된 기능끼리 그룹화가 되었는가? ( 응집도는 어떤가? )6. 관련된 함수들이 흩어져 있지 않는가?7. 중복된 함수나 클래스가 있지 않는가?8. 코드가 이식성을 고려하여 작성되었는가? ( 프로세스의 특성을 받는 변수 타입이 고려되어있는가? )9. 데이터에 맞게 타입이 구체적으로 선언되었는가?10. If/else구분이 2단계 이상 중접되었다면 이를 함수로 더 구분하라11. Switch/case문이 중첩되었다면 이를 더 구분하라12. 리소스에 lock이 있다면, unlock은 반드시 이루어지는가?13. 힙 메모리 할당과 해제는 항상 짝을 이루는가?14. 스택 변수를 반환하고 있는가?15. 외부/공개 라이브러리 사용하였을 경우에 MIT 라이선스를 확인했는가? GPL의 경우에는 관련된 영역에서만 사용해야 한다.16. 블로킹 api호출시에 비동기적인 방식으로 처리하고 있는가?당연하겠지만, 예외처리 관련 체크리스트도 제대로 검토해야 한다.1. 입력 파라미터의 유효 범위는 체크하고 있는가?2. 에러코드와 예외(exception)의 호출 함수는 분명하게 반환되고 있는가?3. 호출 함수가 어려와 예외처리 코드를 가지고 있는가?4. Null포인트와 음수가 처리되는 구조인가?5. 에러코드에 대해서 명쾌하게 선언하고 처리하고 있는가?6. switch문에 default가 존재하고, 예외처리를 하고 있는가?7. 배열 사용시에 index범위를 체크하는가?8. 포인트 사용시에 유요한 범위를 체크하는가?9. Garbage collection을 제대로 하고 있는가?10. 수학계 산시에 overflow, underflow가 발생할 가능성이 있는가?11. 에러 조건이 체크되고 에러 발생 시 로깅 정보를 남기는가?12. 에러 메시지와 에러코드가 에러의 의미를 잘  전달하는가?13. Try/catch 에러 핸들링 사용방법은 적절하게 구현되었는가?요즘 프로그램은 대부분 이벤트성으로 구동되지만, 시간의 흐름에 대한 체크는 프로그램의 뼈대를 이루게 된다. 이 부분에 대해서도 제대로 검토해야 한다.1. 최악의 조건에 대해서 고려하였는가?2. 무한루프와 재귀 함수는 특이사항이 아니라면 없어야 한다.3. 재귀 함수 사용시에 call stack값의 최댓값이 고정되어 있는가?4. 경쟁조건이 존재하는가?5. 스레드는 정상 생성, 정상 동작하는 코드를 가지고 있는가?6. 불필요한 최적화를 통해서 코드 가독성을 희생하였는가?7. 임베디드의 경우에도 최적화가 매우 중요하지 않다면, 가독성을 더 중요하게 해야 한다가장 중요한 검증과 시험에 대해서도 제대로 인지하여야 한다. 그리고, 테스트를 위해서 가능한 최대한 자동화를 하기 위한 방법들을 이용해야 한다.1. 코드는 시험하기 쉽게 작성되었는가?2. 단위 테스트가 쉽게 될 수 있는가?3. 에러 핸들링 코드도 잘  테스트되었는가?4. 컴파일, 링크 체크 시에 경고 메시지도 100% 처리하였는가?5. 경계값, 음수값, 0/1등의 가독성이 떨어지는 코드에 대해서 충분하게 경계하고 있는가?6. 테스트를 위한 fault 조건 재현을 쉽게 할 수 있는가?7. 모든 인터페이스와 모든 예외 조건에 대해서 테스트 코드가 있는가?8. 최악의 조건에서도 리소스 사용은 문제가 없는가?9. 런타임 시의 오류와 로그에 대비한 시스템이 있는가?10. 테스트를 위한 주석 코드가 존재하는가?간혹 등장하는 하드웨어에 대한 테스트도  마찬가지이다. 다음과 같은 기준들을 통해서 검토해야 한다.1. I/O 오퍼레이션 코드에 대한 테스트로 하드웨어가 정상적인 동작을 보장하는가?2. 최소/최대 타이밍 요구사항에 대해서도 하드웨어 인터페이스가 충족하는가?3. 멀티 바이트 하드웨어 레지스터가 read/write오퍼레이션 중에도 값이 바뀌지 않음을 보장하는가?4. 시스템이 잘 정의된 하드웨어 상태로 리셋하는 것을 S/W가 보장하는가?5. 하드웨어의 전압이 떨어지거나 전원이 차단되는 경우에 잘 처리하는가?6. 대기모드 진입 시와 빠져나 올 때에 시스템이 옳게 동작하는가?7. 사용하지 않는 인터럽트 벡터가 에러 핸들러에 연결되어 있는가?8. EEPROM손상(데이터 깨짐)을 막기 위한 메커니즘이 있는가? ( 쓰기 동작 중 powe loss)등구체적으로 코드리뷰를 하고자 한다면, 다음의 코드리뷰에 대한 기법과 적당한 방법을 다음과 같이 설명할 수 있다.이러한 코드 리뷰를 위한 몇 가지 방법들이 알려져 있다. 그것들을 몇 가지 정리하여 보면 다음과 같다. 코드 인스펙션은 가장 정형화된 기법으로 전문화된 코드리뷰팀을 통해서 구분하는 방법이다. 이 방법은 리소스가 풍부하고, 일정에 여유가 있는 경우에만 사용이 가능하다. 대부분 대기업이나 대형 포털에서 구현 가능한 방법이라고 할 수 있다. ( 이런 곳에 있다면 행복해 하자. ~.~ ) 하여간, 비용과 일정 등이 있다면 이 방법이 현명하다. 그리고, 코드리뷰에 대한 품질에 대해서 정량적인 보고와 구성을 만들어 낼 수 있다는 것은 코드 인스팩션의 가장 좋은 장점이다. 이 코드 인스팩션을 하기 위한 롤을 구분하면 다음과 같이 4가지 롤로 구분할 수 있다.1. ModeratorA. 실질적인 매니저로 팀 간의 인터페이스와 리소스, 인프라를 확보하고, 프로세스에 대한 정의와 산출물의 정리를 담당한다.2. ReaderA. 각 산출물을 읽고, 리뷰하고, 방향성을 제시한다. 보통, 지식이 많은 사람이 담당한다.3. Designer/CoderA. Reader의 지시에 따라서 코드를 검증하고 잠재적인 발견 등의 수정 방안을 만든다.4. TesterA. 진행 중인 코드와 권장 수정 코드에 대해서 검증한다.그리고, 코드 인스펙션은 다음과 같은 6단계로 진행된다.1. PlanningA. 계획 수립2. OverviewA. 교육과 역할 정의3. PreparationA. 인터뷰와 필요한 문서 습득, 툴 환경 구축4. Meeting(Inspection)A. 각자의 역할대로 수행5. ReworkA. 보고된 Defect 수정6. Follow-upA. 보고된 Defect가 수정되었는지 확인이러한 절차를 통해서, 코드 인스팩션이 수행되면, 상당히 명쾌한 리뷰가 진행되게 된다. 하지만, 일정과 비용 문제 때문에 이 작업은 대부분의 스타트업에서는 선택하기 어렵다. 그래서 사용하는 방법 중의 하나가 팀 리뷰이다.팀 리뷰는 일정한 계획과 프로세스만 따르는 방법으로, 코드 인스펙션보다는 좀 덜 정형화된 방법으로 진행한다. 보통은 일주일에 한번 정도 팀 리뷰를 수행하거나, 특정 모듈이나 기능이 완료되는 시점을 기준으로 테스트 결과를 가지고 리뷰를 하는 방법을 사용한다.또한, 위험하거나 의견이 필요한 경우에도 팀 리뷰는 유용하다. 일반적인 팀에서 사용하는 방법이다.하지만, 이 역시. ‘리뷰’에 대한 제대로 된 인식이 없다면, 적용하기 어렵다. 그래서, 가끔 사용되는 방법이고, 과거 국내 SI업체들이 주로 사용하던 방법 중의 하나가 ‘웍쓰로’이다.웍 쓰루(Walkthrough)는 단체로 하는 코드 리뷰 기법 중에 비정형적인 방법으로, 발표자가 리뷰의 주제나 시간을 정해서 발표하고 동료들로부터 의견이나 아이디어를 듣는 시간을 가지는 방법으로써 주로 사례에 대한 정보 공유나 아이디어 수집을 위해서 사용하는 방법이다.이 방법은 ‘특정 도메인’에 종속된 코드를 만들거나, 비슷한 SI성 형태의 업무를 수행하는 경우에 적합하다. 그래서, 국내의 SI업체에서는 적극적으로 사용되면 좋겠지만. 이 ‘시간’마저도 부정확하고, 갑을병정의 SI체게에서 ‘정보공유’나 ‘아이디어 수집’과 같은 커뮤니케이션이 자유롭게 일어나는 것은 매우 힘들다.이 웍 쓰루는 동일한 조직 내에서 동일한 목적의식이 분명한 팀에서나 활용이 가능한 방법이다. 웍 쓰루를 SI에서 시도한 경우에는 대부분 실패했거나, 목적의식이 다르기 때문에 불분명한 결론들이 대부분 도출되었다.대부분의 국내 스타트업이나 IT 전문기업들은 ‘리뷰’에 대해서 상급 관리자들이 제대로 허락을 해주지 않는다.대부분은 팀내에서 어떻게든 자체적으로 해보려고 한다. 그래서, 팀장의 권한 선에서 적절하게 리뷰를 하는 방법 중의 하나가 Peer review or over the shoulder review방법이다. 이 방법은 보통 2~3명이 진행하는 코드리 뷰로 코드의 작성자가 모니터를 보면서 코드를 설명하고, 다른 한 사람이 설명을 들으면서 아이디어를 제안하거나 Defect를 발견하는 방법이다.또한, 이 방법은 신입사원이나 인턴사원의 경우에 업무 이해도를 높이면서 해당 코드를 사용할 수 있는 수준으로 활용할 경우에 의미 있는 방법이다. 문제는 이 방법은 개발자의 인력 투입이 거의 두배 이상으로 증가하는 것으로써, 고품질의 영역을 개발하거나, 빠른 시간 안에 신입 개발자의 업무 이해도를 높이는 경우가 아니라면 시행하지 않는다.이렇게도 리뷰가 진행이 되지 않으면, Passaroud는 돌려 보기 방법을 사용한다. 이 방법은 원래 상세한 리뷰 방법은 아니다. 온라인이나 실시간성이 아니라, 리파지토리나 이메일 등을 사용하여 천천히 리뷰하는 방식에 해당하는데, 속도는 느리지만, 중요한 코드이거나, 제품의 기능 개선이 필요한 경우에는 아주 의미가 있다. 보통은 제품의 기능 개선을 위하여 사용하는 방법이다.이처럼 리뷰의 방법에는 다양한 방법이 있지만, 결론적으로는 어느 정도 개발 조직이 서로  커뮤니케이션하고, 목적의식을 통일하고, 적절한 시간 분배를 통해서 리뷰를 할 수 있는 시간을 만들어 내느냐가 리뷰의 핵심이라고 할 수 있다.리뷰를 통해서 소프트웨어의 품질을  끌어올리고, 개발자들과 소통하고, 방향성을 만들어 내며, 새로운 기능 개선 작업을 위해서 리뷰는 다양하게 활용된다. 어떤 관점으로 리뷰를 할 것이고, 어떤 관점으로 리뷰라는 프로세스를 개발 프로세스에 탑재할 것인가에 대해서 진지하게 고민하는 것. 그것이 아키텍트의 첫 번째 역할 아닌가 한다.
조회수 1743

TDD(파이썬) : 테스트 잘하고 계신가요?

Overview반복적인 테스트에 지쳐가고 있던 무렵, TDD방법론을 접하게 되었습니다. TDD(Test Driven Development)는 테스트 주도적인 개발로 소스코드 작업 전에 테스트 코드를 먼저 작성해 소스수정에 대한 부담을 덜고 디버깅 시간을 줄일 수 있습니다. TDD 장점소스코드의 품질이 높다.재설계 및 디버깅 시간이 절감된다.TDD 단점단기적 코드일 경우 생산성이 떨어진다.실제 코드보다 테스트 케이스가 더 커질 수 있다.파이썬에서 TDD가 필요한 이유1) 파이썬에는 정적 타입 검사 기능이 없다. (Python 3.6 에서는 정적 타입 선언 가능)2) 동적언어이기 때문에 TDD를 하기에 적합하다.3) 파이썬은 간결성과 단순함으로 생산성이 높은 반면 런타임 오류가 발생할 수도 있다.4) 파이썬을 신뢰할 수 있는 유일한 방법은 테스트를 하는 것이다.파이썬 테스트 모듈 unittest이번 글에서는 unittest를 사용해 단위 테스트를 해보겠습니다. unittest는 이미 내장되어 있어 따로 설치하지 않아도 되는 표준 라이브러리입니다. 사용방법1) import unittest 2) unittest.TestCase 상속받는 하위 클래스 생성3) TestCase.assert 메소드를 사용하여 테스트 코드를 간략화4) unittest.main() 실행그럼 간단한 예제로 단위 테스트를 해보겠습니다.1.사칙연산 함수를 추가합니다.def add(a, b):     return a + b   def substract(a, b):     return a - b   def division(a, b):     return a / b   def multiply(a, b):     return a * b 2. unittest.TestCase 상속받아 테스트 클래스를 생성합니다. 아래는 각각의 함수 결과값을 비교해 텍스트를 출력하는 코드입니다.import unittest class TddTest(unittest.TestCase): def testAdd(self):         result = lib_calc.add(10, 20)         if result == 30:             print('testAdd OK')      def testSubstract(self):         result = lib_calc.substract(20, 30)          if result > 0:             boolval = True         else:             boolval = False if boolval == False:             print('testSubstract Error')      def testDivision(self):         try:             lib_calc.division(4, 0)         except Exception as e:             print(e)      def testMultiply(self):         result = lib_calc.multiply(10, 9)          if result < 100>             print('testMultiply Error') if __name__ == '__main__':     unittest.main() 3.결과: 해당 조건에 만족해 작성한 텍스트가 출력됩니다.이번에는 unittest에서 지원하는 TestCase.assert 메소드를 사용해 간략하게 소스를 수정해보겠습니다.TestCase.assert 메소드1) assertEqual(A, B, Msg) - A, B가 같은지 테스트2) assertNotEqual(A, B, Msg) - A, B가 다른지 테스트3) assertTrue(A, Msg) - A가 True인지 테스트4) assertFalse(A, Msg) - A가 False인지 테스트5) assertIs(A, B, Msg) - A, B가 동일한 객체인지 테스트6) assertIsNot(A, B, Msg) - A, B가 동일하지 않는 객체인지 테스트7) assertIsNone(A, Msg) - A가 None인지 테스트8) assertIsNotNone(A, Msg) - A가 Not None인지 테스트9) assertRaises(ZeroDivisionError, myCalc.add, 4, 0) - 특정 에러 확인1. TestCase.assert 메소드 사용TestCase.assert 메소드를 사용하여 에러를 발생시켜 보겠습니다.import unittest class TddTest(unittest.TestCase): def testAdd(self):         result = lib_calc.add(10, 20)          # 결과 값이 일치 여부 확인         self.assertEqual(result, 31)      def testSubstract(self):         result = lib_calc.substract(20, 10)          if result > 10:             boolval = True         else:             boolval = False # 결과 값이 True 여부 확인         self.assertTrue(boolval)      def testDivision(self):         # 결과 값이 ZeroDivisionError 예외 발생 여부 확인         self.assertRaises(ZeroDivisionError, lib_calc.division, 4, 1)      def testMultiply(self):         nonechk = True result = lib_calc.multiply(10, 9)          if result > 100:             nonechk = None # 결과 값이 None 여부 확인         self.assertIsNone(nonechk) if __name__ == '__main__':     unittest.main() 2. 결과1) 테스트가 실패해도 다른 테스트에 영향을 미치지 않음2) 실패한 위치와 이유를 알 수 있음다음으로 setUp(), tearDown() 메소드를 사용하여 반복적인 테스트 메소드 실행 전, 실행 후의 동작을 처리해보겠습니다.TestCase 메소드1) setUp() - TestCase클래스의 매 테스트 메소드가 실행 전 동작2) tearDown() - 매 테스트 메소드가 실행 후 동작 1. setUp(), tearDown() 메소드 사용- setUp() 메소드로 전역 변수에 값을 지정- tearDown() 메소드로 “ 결과 값 : ” 텍스트 출력import unittest class TddTest(unittest.TestCase): aa = 0     bb = 0     result = 0 # 매 테스트 메소드 실행 전 동작     def setUp(self):        self.aa = 10        self.bb = 20 def testAdd(self):         self.result = lib_calc.add(self.aa, self.bb)          # 결과 값이 일치 여부 확인         self.assertEqual(self.result, 31)      def testSubstract(self):         self.result = lib_calc.substract(self.aa, self.bb)          if self.result > 10:             boolval = True         else:             boolval = False # 결과 값이 True 여부 확인         self.assertTrue(boolval)      def testDivision(self):         # 결과 값이 ZeroDivisionError 예외 발생 여부 확인         self.assertRaises(ZeroDivisionError, lib_calc.division, 4, 1)      def testMultiply(self):         nonechk = True self.result = lib_calc.multiply(10, 9)          if self.result > 100:             nonechk = None # 결과 값이 None 여부 확인         self.assertIsNone(nonechk)      # 매 테스트 메소드 실행 후 동작     def tearDown(self):         print(' 결과 값 : ' + str(self.result))   if __name__ == '__main__':     unittest.main() 2. 결과- setUp() 메소드로 지정한 값으로 테스트를 수행 - tearDown() 메소드로 각각의 테스트 메소드 마다 “ 결과 값 : ” 텍스트 출력실행 명령어 여러 옵션을 사용하여 실행 결과를 출력해보겠습니다.실행 명령어python -m unittest discover [option]1. -v : 상세 결과 2. -f : 첫 번째 실패 또는 오류시 중단3. -s : 시작할 디렉토리4. -p : 테스트 파일과 일치하는 패턴5. -t : 프로젝트의 최상위 디렉토리1. 상세 결과테스트 메소드명 및 해당 클래스명 출력 2. 첫 번째 실패 또는 오류시 중단첫 번째 테스트에서 오류 발생하여 중단3. 여러 옵션 실행현재경로 디렉토리 안에 tdd_test*.py 패턴에 속하는 모든 파일의 상세 결과Conclusion지금까지 파이썬에서 unittest 모듈을 이용한 테스트 코드를 작성했습니다. 처음에는 귀찮고 번거롭지만 테스트 코드를 먼저 작성하는 습관을 길러보세요. 분명 높은 품질의 소스코드를 만들 수 있을 겁니다!참고Python 테스트 시작하기파이썬 TDD 101글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #파이썬 #Python
조회수 4867

웹서버 로그 수집과 모니터링 설정

우리는 고객이 무엇에 관심 있어 하고 무엇에 관심 없어하는지, 어떤 것을 보았을 때 클릭해 들어가고 어떤 것을 보았을 때 사이트에서 이탈하는지 궁금해 합니다. 이러한 정보를 얻기 위해 봐야 할 것은 역시 웹서버의 접속 로그입니다.처음에는 매일 생성되는 로그 파일을 일일이 파싱해서 원하는 정보를 DB에 쌓는 방법을 이용했지만, 이러한 방식은 한계가 있었습니다. 저장할 수 있는 데이터의 양에 심각한 제한이 있었고, 따라서 처음에 얻고자 했던 데이터 이상의 것을 새로 추출할 수도 없었습니다.그래서 지금은 웹서버 로그를 하둡(Hadoop) 클러스터에 쌓고 있습니다. Google Analytics 같은 외부 분석툴을 사용하기도 하지만, 아무래도 데이터를 우리 손에 직접 들고 있는 것이 더 유연한 분석을 제공할 수 있지요. 클러스터에서 로그를 분석하려면 가장 먼저 로그 수집 시스템을 만들어야 합니다.이번 포스팅에서는 이 로그 수집 시스템이 어떻게 만들어져 있는지, 그리고 그보다 더 중요한 시스템의 모니터링을 어떻게 하고 있는지 설명하려고 합니다.Flume 에이전트 설정하기Apache FlumeApache Flume은 로그와 같은 데이터의 흐름(streaming)을 제어할 수 있게 해주는 도구입니다. 단순하면서도 확장성 높은 구조로 되어 있기 때문에 많은 시스템에서 채택하는 도구가 되었고, 리디북스에서도 Flume 을 사용하게 되었습니다.Flume 의 기본 구조는 단순합니다.기본적인 에이전트 구성 (이미지 출처: Apache Flume 홈페이지)에이전트(agent)는 Source, Channel, Sink 로 이루어진 자바 프로세스이다.소스(source)는 외부에서 이벤트를 입력받아 채널(channel)로 전달하고, 채널은 이벤트를 저장하고 있다가 싱크(sink)로 전달한다. 싱크는 이벤트를 외부로 출력한다.한 에이전트의 Sink와 다른 에이전트의 Source가 같은 타입이면, 에이전트 간에 이벤트를 전달할 수 있다.굉장히 간단하지만 강력한 모델입니다. Flume 은 Avro, Thrift, Exec, HDFS, Kafka 등 다양한 라이브러리를 적용한 소스와 싱크를 미리 제공하고 있기 때문에, 사용자는 자기 입맛에 맞게 이를 조합해서 시스템을 구성할 수 있습니다.예를 들면 아래와 같습니다.좀 더 복잡한 에이전트 구성 (이미지 출처: Apache Flume 홈페이지)초기 에이전트 구성: Avro를 통해 클러스터에 로그 전송저희가 맨 처음 설정한 Flume 에이전트의 구성은 다음과 같습니다.초기 에이전트 구성각 웹서버ExecSource: exec 명령으로 실행된 프로세스의 표준 출력을 이벤트로 입력받음. (tail -F <로그파일>)MemoryChannel: 메모리상의 큐(queue)로 구현된 채널AvroSink: 클러스터에 상의 에이전트가 실행하는 Avro RPC 서버로 이벤트를 전송하둡 클러스터AvroSource: 웹서버의 에이전트가 Avro RPC 로 보내는 이벤트를 수신MemoryChannelHDFSSink: HDFS 상의 지정된 경로의 파일에 이벤트 내용을 출력각 웹서버에는 에이전트가 하나씩 실행되어서, 로그 파일에 새로 추가되는 로그를 클러스터에 전송합니다. 클러스터 상의 에이전트는 단 한 개 존재하는데, 웹서버로부터 전송받은 로그를 HDFS(Hadoop File System) 에 파일로 출력하는 역할을 합니다. 웹서버 에이전트와 클러스터 에이전트 간의 통신은 Avro RPC 로 하게 하였습니다. Flume 에서 기본적으로 AvroSource 와 AvroSink 를 구현하여 제공해 주는 것을 이용했습니다.사실은 클러스터 상의 에이전트가 Avro 서비스를 통해 데이터를 모아 주지 않고, 웹서버 상의 에이전트가 HDFSSink 를 이용해서 직접 클러스터에 파일을 쓰게 하더라도 대부분의 경우는 상관없습니다. 하지만 리디북스의 경우는 그렇게 할 수 없었는데, 왜냐하면 웹서버와 하둡 클러스터가 서로 다른 네트워크 상에 있기 때문입니다.리디북스의 웹서버는 국내 IDC에 존재하지만 하둡 클러스터는 Miscrosoft Azure 클라우드 내의 가상머신으로 실행되고 있습니다. 따라서 하둡의 네임노드(namenode)가 인식하는 각 노드의 사설 IP 주소를 웹서버들이 쉽게 접근할 수 없습니다. 이를 우회하는 다양한 방법을 시도해 보았지만 최종적으로는 Avro 서비스를 중간에 두어 해결하였습니다.모니터링 알람 설정하기JSON 리포팅 사용다음은 에이전트 프로세스를 모니터링하는 문제가 있었습니다. 예기치 않은 에러로 에이전트가 종료되어서 로그가 수집되지 않고 있는데 며칠 동안 모르고 있어서는 안되겠지요.Flume 에서는 모니터링 인터페이스도 여러가지를 제공하고 있는데, 그 중 가장 이용하기 간편한 것은 HTTP 를 통한 JSON reporting 이었습니다. 에이전트 자체가 HTTP 서비스로 작동해서, 특정 포트로 요청을 보내면 에이전트의 상태를 JSON 으로 정리하여 응답을 주게 되어 있습니다. 에이전트 실행시에 옵션 몇 개만 추가하면 바로 설정할 수 있기 때문에 매우 간단합니다.Health 페이지를 이용한 모니터링그런데 이 리포팅이 제대로 나오지 않으면 어떻게 알림을 받을 수 있을까요? 각 서버마다 JSON 리포팅을 요청해서 응답이 제대로 오지 않으면 이메일을 보내는 스크립트를 만들어서 cron 으로 5분마다 실행하는 방법도 있습니다. 하지만 이 스크립트가 제대로 동작하지 않거나, 이게 실행되는 서버가 다운되면?결국 스스로를 믿지 못하고 택한 방법은 외부 서비스 Pingdom을 이용하는 것이었습니다. 단, 외부 서비스가 각각의 웹서버에 직접 접근하여 리포팅을 요청하는 방식은 보안상 문제가 될 수 있어서 아래와 같이 보완하였습니다.웹 서비스 상에 health 페이지 구현. 이 페이지는 각 웹서버의 에이전트의 JSON reporting 포트로 요청을 보내서, 결과를 종합해서 다시 JSON 으로 보여줌.모든 에이전트가 정상적으로 리포트를 보내면 {“status”: “OKAY”} 를, 아니면 {“status”: “ERROR”} 를 보여줌.이 health 페이지의 내용을 모니터링하도록 Pingdom 설정. {“status”: “OKAY”} 가 응답에 없으면 알람 메일이 오도록 함.{ "status": "OKAY", "metrics": { "192.168.0.101": { "SOURCE.log_src": { ... }, "SINK.avro_sink": { "BatchCompleteCount": 562110, "ConnectionFailedCount": 294, "EventDrainAttemptCount": 56246850, "ConnectionCreatedCount": 31, "Type": "SINK", "BatchEmptyCount": 16, "ConnectionClosedCount": 30, "EventDrainSuccessCount": 56243927, "StopTime": 0, "StartTime": 1459135471379, "BatchUnderflowCount": 610 }, "CHANNEL.mem_channel": { ... } }, "192.168.0.102": { ... } } }Health 페이지의 Json내용JSON 리포팅의 문제이렇게 설정해 놓고, 며칠간 로그가 HDFS 상에 잘 수집되는 것을 확인하고 만족해 했습니다. 그런데 며칠간 신경을 쓰지 않은 사이, 다시 에이전트를 확인해 보니 모든 웹서버 에이전트가 죽어 있었습니다. HDFS에 로그도 쌓이지 않았구요.확인해 보니, MemoryChannel 의 설정 문제였습니다. byteCapacity 값을 실수로 너무 작게 설정해서, 채널 큐가 메모리 부족으로 터져나간 것이죠. 해당 문제는 byteCapacity 값을 늘려서 간단하게 해결했습니다.문제는 알람이 오지 않았다는 것이었습니다. 문제를 재현해 본 결과, 채널이 터져서 에이전트 실행이 중단되어도, 에이전트 프로세스는 죽지 않고 ExecSource 에서 실행한 자식 프로세스(tail -F)만 죽어 있었습니다. 이렇게 되면 JSON 리포팅도 정상적으로 나오기 때문에, 결국 JSON 리포팅으로는 이런 유형의 에러를 잡지 못한다는 결론이 나왔습니다.클러스터에 모니터링 설정하기결국 웹서버상에서 모니터링하는것 보다는 데이터를 최종 전달받는 하둡 클러스터 상에서 모니터링하는 것이 안정적이라 판단하였습니다. 다행히도, 하둡 클러스터에서 사용할 수 있는 꽤나 좋은 모니터링 도구가 이미 있었습니다.CDH 의 알람 트리거리디북스에서는 기본 하둡 패키지가 아닌, Cloudera에서 제공하는 하둡 배포판인 Cloudera CDH를 사용하고 있습니다. CDH는 클러스터 상에서 사용되는 서비스마다 각종 테스트를 자동으로 실행하여, 테스트가 통과되지 않을 때마다 메일로 알람을 보내줍니다. 그리고 웬만한 필수 테스트는 기본적으로 설정되어 있지만, 사용자가 커스텀 서비스를 직접 제작할 수도 있습니다. CDH가 각 에이전트의 소스, 채널, 싱크마다 초당 전송한 이벤트 개수 등의 측정치(metric)을 모두 기록하고 있기 때문에, 이 값들이 일정 수준 이상/이하가 될 때마다 알람이 트리거되도록 설정할 수 있습니다.CDH의 알람 트리거 편집 화면웹서버마다 알람 설정하기그런데 이것으로 끝이 아닙니다. 클러스터 에이전트는 각 서버에서의 트래픽이 모두 모이는 곳이기 때문에, 여기에서 모니터링을 하는 것은 웹서버 상에서 모니터링하는 것보다 기준이 애매해집니다.10대의 웹서버 중에 한 대만 문제가 생겼을 경우, 클러스터 에이전트가 받는 트래픽은 0으로 줄어드는 것이 아니라 90%로 줄어듭니다. 알람을 트리거하는 역치(threshold)를 평소 트래픽의 90%로 잡아야 한다는 것이지요. 그런데 트래픽이라는 것이 원래 날짜와 시간에 따라 달라지기 때문에, 이 역치값을 고정된 값으로 정할 수가 없습니다. 트래픽이 높은 때를 기준으로 하면, 트래픽이 낮아지는 새벽 시간마다 가짜 알람(false alarm)이 오게 되겠지요. 그렇다고 트래픽이 낮은 때를 기준으로 하면, 트래픽이 높은 때 웹서버 에이전트가 죽더라도 새벽이 될 때까지 알 수 없습니다.결국 클러스터 단에서도 각 웹서버마다 트래픽을 구분해 주어야 한다는 결론이 나옵니다. 다행히 한 에이전트가 여러 개의 채널과 싱크를 가질 수 있고, 이벤트 헤더의 내용에 따라 소스가 어느 채널로 이벤트를 보낼지 결정해 주는 채널 셀렉터 (Channel Selector)라는 것이 있습니다.웹서버 에이전트의 소스에서는 각 이벤트 헤더에 자기 호스트명을 달아 준다. (Interceptor 는 각 이벤트에 원하는 헤더를 달아주는 역할을 한다. HostInterceptor 이용)클러스터 에이전트는 1개의 소스와, 웹서버 대수만큼의 채널 및 싱크가 있다.클러스터의 소스는 이벤트의 host 헤더를 보고 그에 해당하는 채널로 이벤트를 전달한다. (MultiplexingSelector 사용)각 채널은 자신에게 대응되는 싱크에 이벤트를 전달하고, 싱크는 각자의 HDFS 경로에 이벤트를 파일로 출력한다.최종 에이전트 구성: 채널 셀렉터로 트래픽 나누기최종적으로 나온 에이전트의 구성은 다음과 같습니다.최종 에이전트 구성그리고 에이전트 설정 파일은 아래와 같이 작성했습니다.... log_to_avro.sources.log_src.type = exec log_to_avro.sources.log_src.command = tail -F /path/to/log/file log_to_avro.sources.log_src.restart = true log_to_avro.sources.log_src.channels = mem_channel log_to_avro.sources.log_src.interceptors = ts_ic host_ic # 호스트 인터셉터 설정 log_to_avro.sources.log_src.interceptors.ts_ic.type = timestamp # 이벤트 헤더에 timestamp 삽입 (날짜별 구분을 위해) log_to_avro.sources.log_src.interceptors.host_ic.type = host # 이벤트 헤더에 호스트명 삽입 (호스트별 구분을 위해) log_to_avro.sources.log_src.interceptors.host_ic.useIP = true # 호스트명 대신에 IP 사용 log_to_avro.channels.mem_channel.type = memory log_to_avro.channels.mem_channel.capacity = 10000 log_to_avro.channels.mem_channel.transactionCapacity = 10000 log_to_avro.channels.mem_channel.byteCapacityBufferPercentage = 20 log_to_avro.channels.mem_channel.byteCapacity = 10485760 log_to_avro.sinks.avro_sink.type = avro log_to_avro.sinks.avro_sink.channel = mem_channel log_to_avro.sinks.avro_sink.hostname = hostname.of.cluster.agent log_to_avro.sinks.avro_sink.port = 4141 ...웹서버 에이전트 설정파일... avro_to_hdfs.sources.avro_src.type = avro avro_to_hdfs.sources.avro_src.bind = 0.0.0.0 avro_to_hdfs.sources.avro_src.port = 4141 avro_to_hdfs.sources.avro_src.channels = c_101 c_102 avro_to_hdfs.sources.avro_src.selector.type = multiplexing # Multiplexing Selector 설정 avro_to_hdfs.sources.avro_src.selector.header = host # 호스트 이름으로 채널 나누기 avro_to_hdfs.sources.avro_src.selector.mapping.192.168.0.101 = c_101 # 192.168.0.101 에서 온 이벤트는 c_101 채널로 avro_to_hdfs.sources.avro_src.selector.mapping.192.168.0.102 = c_102 # 192.168.0.102 에서 온 이벤트는 c_102 채널로 # 채널 c_101 설정 avro_to_hdfs.channels.c_101.type = memory avro_to_hdfs.channels.c_101.capacity = 10000 avro_to_hdfs.channels.c_101.transactionCapacity = 10000 avro_to_hdfs.channels.c_101.byteCapacityBufferPercentage = 20 avro_to_hdfs.channels.c_101.byteCapacity = 10485760 # 싱크 k_101 설정 avro_to_hdfs.sinks.k_101.type = hdfs avro_to_hdfs.sinks.k_101.channel = c_101 avro_to_hdfs.sinks.k_101.hdfs.fileSuffix = .log.gz avro_to_hdfs.sinks.k_101.hdfs.path = hdfs://namenode/path/to/logs/dir/%Y%m%d/%{host} # 날짜별, 호스트별로 다른 디렉토리에 avro_to_hdfs.sinks.k_101.hdfs.rollSize = 104857600 avro_to_hdfs.sinks.k_101.hdfs.rollInterval = 7200 avro_to_hdfs.sinks.k_101.hdfs.rollCount = 0 avro_to_hdfs.sinks.k_101.hdfs.fileType = CompressedStream avro_to_hdfs.sinks.k_101.hdfs.codeC = gzip # 채널 c_102 설정 avro_to_hdfs.channels.c_102.type = memory avro_to_hdfs.channels.c_102.capacity = 10000 avro_to_hdfs.channels.c_102.transactionCapacity = 10000 avro_to_hdfs.channels.c_102.byteCapacityBufferPercentage = 20 avro_to_hdfs.channels.c_102.byteCapacity = 10485760클러스터 에이전트 설정파일p.s. Flume 설정 파일은 변수 또는 외부 파일 include 등을 지원하지는 않아서, 위와 같이 반복되는 설정을 여러 번 써 주어야 합니다.호스트마다 CDH 알람 트리거 설정그리고 CDH 상에서도 웹서버 호스트의 개수만큼 알람 트리거를 만들어 줍니다. 초당 이벤트 개수가 0에 가깝게 떨어지면 알람이 오도록 해 주면 됩니다. 채널/싱크 중 어느 것을 기준으로 해도 크게 상관은 없는데, 저희는 싱크가 초당 이동완료한 이벤트 개수를 기준으로 했습니다.CDH에서의 알람 트리거 상태 화면이렇게 해 놓으면 또 한가지 좋은 점은, CDH가 알아서 차트를 그려 주기 때문에, 웹서버마다 트래픽 추이를 한눈에 볼 수 있다는 것입니다.HDFSSink의 초당 이벤트 개수 그래프맺음말지금까지 Apache Flume 과 CDH 를 사용해 로그 수집 시스템을 구성하고 모니터링을 설정한 후기를 살펴 보았습니다. 이 과정에서 느낀 점들을 한번 정리해 보겠습니다.첫째, 일견 간단해 보이는 기능이었지만 의외로 많은 시행착오를 거쳐야 했습니다. 아무리 간단해 보이더라도 각자의 상황에 맞추어 시스템을 설계하는 데에는 그에 맞는 고민을 거쳐야 합니다.둘째, 처음에는 로그가 일단 수집되게 하는 것이 가장 중요하다고 생각했는데, 실제로 겪어보니 모니터링이 훨씬 어렵고 중요한 문제라는 것을 알게 되었습니다. 어떤 기능이 일단 실행되도록 설정을 해 놓더라도, 그것이 매일 문제없이 실행됨을 보장받는 것은 또 다른 문제입니다.셋째, Health 페이지와 Pingdom을 이용한 웹서버 측의 모니터링은 JSON 리포팅의 문제 때문에 큰 쓸모가 없게 되었습니다. 하지만 꽤 유용한 테크닉이라는 생각이 들고, 어딘가에서는 비슷하게 이용할 수 있을 것 같습니다.마지막으로 CDH 쓰면 좋습니다. 많은 것들이 편해집니다.P.S. 리디북스 데이터팀에서는 이러한 로그 시스템을 함께 고민하고 만들어나갈 분들을 찾고 있습니다. 많은 관심 부탁드립니다.#리디북스 #개발 #서버 #서버개발 #모니터링 #로그 #Flume #CDH #로그수정 #인사이트
조회수 1463

A씨의 일주일

스포카 개발팀 문성원입니다. 오늘은 스포카 개발팀의 가공의 개발자 A씨의 일주일을 통해, 스포카 개발팀에서는 일주일간의 개발 일정을 어떻게 진행하는지 살펴보겠습니다. 평소 스타트업(Startup) 개발팀의 문화에 관심이 있으셨던 분들에게 도움이 되길 바랍니다.월요일오전 10시, A씨는 평소보다 조금 일찍 사무실에 도착했습니다. 매주 월요일은 스포카 전체 미팅이 있는 날이기 때문이죠. 한 주간 각자 진행한 것을 토대로 이번 주에 진행할 일을 대외적으로 공표하는 이 회의에 앞서, 스포카 개발팀은 따로 미팅을 잠깐 가집니다. 그동안 지난 주 개발 사항, 이번 주 구현 목록 등을 트렐로(Trello)를 통해 정리한 뒤, 이를 전체 미팅에서 공유합니다. A씨는 지난 주에 미쳐 다 구현하지 못했던 서버의 몇 가지 기능과 클라이언트 신버전 배포 준비를 하게 되었습니다.정신없이 회의 하고 났더니 벌써 점심시간입니다. 늘 가던 근처 식당에서 즐겁게 점심을 먹고 사무실로 올라온 A씨는 막간을 이용해 간밤에 올라온 스포카 개발 블로그의 원고를 검토합니다. 몇 가지 오탈자와 맞춤법을 지적한 뒤 모두가 지루해할 월요일 오후 1~2시경에 공개하는 것이 목표입니다.올라간 블로그 글을 확인한 뒤에, A씨는 구현해야 할 서버 기능을 살펴보기로 했습니다. 생각보단 많긴 하지만 일주일 안에는 어떻게든 끝낼 수도 있을 것 같은 분량이네요. 우선 트렐로에 올라온 카드의 명세를 토대로 작업해야 할 내용을 체크리스트(Check List)로 정리합니다. 그다음은 모두가 짐작하시듯이 열심히 일합니다. A씨는 프로니까요.어느덧 저녁 시간이 다 되었습니다. 특별히 일이 없는 이상 야근은 하지 않는 주의인 A씨지만, 오늘만큼은 저녁을 먹고 조금 더 남아있기로 합니다. 팀 내에서 진행하고 있는 스터디 때문이죠. 혼자서 읽기는 까다로웠던 책을 다 같이 읽어보니 조금은 이해가 더 되는 느낌이 드네요.화요일A씨는 오전에 작업하던 중 이상한 점을 발견합니다. 구현하기로 한 기능이 기존 기능과 모순이 되기 때문이죠. 이걸 어떻게 해결할까 고민하던 A씨는 다행히 사무실에 남아있던 엔에이블러(Enabler)팀원들과 간단하게 미팅을 합니다. 문제를 설명하고 명세를 다시 확인한 A씨는 작성한 회의록과 함께 배포합니다. 트렐로의 해당 카드에도 첨부하여 나중에 다시 볼 수 있게 하는 것은 기본입니다.뜻하지 않은 문제 때문에 오전을 날려서 기분이 나빠진 A씨지만, 다행히 좋아하는 스파게티를 먹고 기운을 내기로 했습니다. 사무실에 올라와 인터넷 뉴스와 페이스북을 잠시 보던 A씨는 암묵적으로만 정해진 점심시간이 끝나자 바로 작업을 시작합니다. A씨는 프로니까요.그런데 문제가 있습니다. 오전에 배포한 회의록을 읽어 본 다른 팀원들이 이건 다른 문제의 원인이 될 수 있다고 합니다. A씨는 새 기능 추가가 단순히 로직이 아니라 클라이언트 UI를 포함한 대규모 변경이 필요하다는 것을 깨닫습니다. A씨는 새 기능에 대한 대략적인 스케치를 발사믹 목업(Balsamiq Mockup)으로 마친 뒤 이를 다시 배포합니다. 또한, 관련된 카드에 설명도 잊지 않습니다.수요일매주 수요일 오전에 스포카 개발팀은 짧은 미팅을 합니다. 금주의 진행사항 중 변경사항이나 도움이 필요한 내용을 공유하는 자리인데요. 여기서 A씨는 어제 일을 다시 정리해서 이야기하고, 일정이 지연될 수 있음을 전달합니다. A씨에게 할당된 카드 일부를 다음 주로 미루거나, 좀 한가한 사람에게 나눠주는 형식으로 짐을 던 A씨였지만, 여전히 큰일이 되어버린 기능 변경은 무거운 짐입니다.이런 대량의 작업 때문에 고민하던 A씨에게 같은 팀 B씨가 어떤 라이브러리를 소개해줍니다. A씨는 처음 보는 라이브러리인지라 B씨가 전담해서 가르쳐주는 모양이 되었지만, 생각보다 문제 해결에 큰 도움이 될 것 같습니다. 마침 다음 주에 개발 블로그에 글을 써야 할 당번이 된 A씨는 그 라이브러리에 대해 좀 더 공부해서 쓰기로 정합니다.B씨의 도움 덕에 진행 속도가 붙은 A씨는, 금주 업무 중 하나였던 클라이언트 새 버전의 테스트를 내일부터 진행하기 위해 페이스북이나 인터넷 뉴스도 보지 않은 채 열심히 일합니다. 이런 A씨의 프로다운 모습에 하늘도 감탄했는지, 퇴근 시간인 7시 전에 작업을 끝낸 A씨는 구현된 기능들을 테스트 해 보고 팀의 다른 개발자와 공유하기 위해 github에 만들어진 스포카 서버 코드 저장소에 푸시(Push)합니다.목요일구글 플레이(Google Play)는 하루 정도면 배포가 되니 다행이지만 애플 앱스토어(Apple App Store)는 일주일 정도의 심사기간이 있기 때문에 거절(Reject)당하지 않게 철저히 준비해야 합니다. 그래서 어느 때보다 A씨는 날카로운 눈매로 클라이언트를 점검합니다. 아니나 다를까 메뉴를 이동하다 보니 화면 구성이 흐트러지는 버그가 발견되었습니다. 하지만 프로답게 A씨는 당황하지 않고 재현 조건을 확인한 뒤, 클라이언트 담당자인 C씨에게 알려줍니다. “클라이언트 관련한 버그를 찾았는데, 트렐로를 확인해보세요.”라구요. QA(Quality Assurance) 업무 역시 스포카 개발팀은 직접 처리합니다.밖에 비가 오는지라 피자를 시켜먹은 뒤, 자리에 앉아 잠깐 쉬고 있던 A씨에게 D씨가 다가와서는, “어제 푸시한 소스를 내려받다(Pull)가 충돌(Conflict)이 났는데, 어떻게 병합(Merge)해야 할지 모르겠네요.” 라며 묻습니다. A씨는 D씨와 충돌이 난 소스를 함께 검토하고 문제가 발생하지 않게끔 조정한 뒤 이를 다시 푸시해서 상황을 종료합니다.이러는 사이 C씨가 A씨가 말한 버그를 고쳤다며 다시 확인해보라고 트렐로의 관련 카드를 “테스트” 리스트로 옮깁니다. A씨는 재현된 상황에서 문제 없이 동작하는 것을 확인하고 카드를 “완료” 리스트로 다시 옮깁니다. 이제 클라이언트 앱을 심의 신청하고 어제 구현한 서버 쪽 코드의 개선사항이 있는지 살펴봅니다. 서버는 클라이언트가 앱스토어나 플레이에 준비되는 것을 확인한 뒤, DotCloud에서 제공하는 배포 스크립트를 통해 손쉽게 버전업할 수 있기 때문에 시간이 좀 남아 있습니다. 현재로선 특별히 더 손댈 부분이 없다는 걸 확인한 A씨는 오늘도 즐겁게 퇴근합니다.금요일월요일과 마찬가지로 오늘도 A씨는 평소보다 조금 서둘러서 사무실에 도착했습니다. 오늘은 사내 전체적으로 한 주간 있었던 업무 내용을 간략하게 보고하는 자리가 있습니다. A씨는 이번 주에 맡은 서버 개발이 이러저러해서 이렇고 저렇게 바뀌었다고 설명한 뒤, 앱스토어에 신청되었다는 사실을 공지합니다. 전체 보고가 끝난 뒤엔 개발팀은 따로 남아서 약간 자세하게 금주 작업을 공유하면서 트렐로의 “완료” 상태에 있는 카드들을 정리하는 시간을 갖습니다.점심을 먹고 나서, 이번 주에 더는 급한 일정이 없다는 것을 확인한 A씨는 개발 블로그에 쓸 글을 정리하기 시작합니다. 수요일에 B씨가 알려 준 라이브러리의 사용 방법은 대강 배웠지만, 그것을 남에게 설명할 수 있을 만큼 자세히 알지는 못했기 때문에 A씨는 한동안 공식 문서와 예제 코드들과 씨름합니다. 그래도 어느새 옆에서 거들기 시작한 B씨 덕에 글은 생각보다 순조롭게 마무리되었습니다. 이제 다음 주 월요일까지 퇴고해서 블로그에 공개하기만 하면 되죠.생각보다 오늘 업무를 끝낸 A씨는 친구들과 약속이 있는 홍대로 가기 위해, 7시 정시에 사무실을 떠납니다.#스포카 #개발 #개발자 #개발팀 #개발자의일주일 #개발자의일상 #인사이트 #경험공유
조회수 1663

SQS + Lambda

Overview안녕하세요. 저는 브랜디 R&D 본부 개발1팀의 기둥을 담당하는 이상근입니다. 오늘은 SQS(Simple Queue Service)와 Lambda를 간단한 예제와 함께 정리해보려고 합니다. 각 서비스에 대한 설명은 이미 매뉴얼로 쉽게 정리되어 있으므로, 이번 글에서는 서비스 간 구성을 집중적으로 살펴보겠습니다.1)SQS와 Lambda에 대하여SQS(Simple Queue Service)는 마이크로 서비스와 분산 시스템, 그리고 서버리스 애플리케이션을 쉽게 분리하고 확장할 수 있는 ‘완전관리형 메시지 대기열 서비스’입니다. 그리고 Lambda는 ‘이벤트 처리 방식의 서버리스 컴퓨팅 서비스’입니다. 아래 그림은 SQS와 Lambda Function을 이용해 메시지를 등록-조회-처리하는데 필요한 구성요소를 정리한 것입니다. SQS, Lambda ArchitectureProducer - 처리할 작업 메시지를 SQS에 등록Trigger - 큐(Queue) 대기열에 있는 메시지들을 조회하기 위해 CloueWatch의 스케줄 이벤트를 이용하여 매 분마다 Lambda Consumer 실행Consumer - Lambda Consumer는 큐 대기열에 있는 메시지 목록을 조회하여 각 메시지를 Lambda Worker에서 처리할 수 있도록 실행Worker - Lambda Worker는 메시지를 받아 작업을 처리하고 해당 메시지를 삭제큐 생성하기이번에는 큐 생성에 대해 살펴보겠습니다. ‘Create New Queue’를 클릭했을 때 지역(Region)에 따라 각각 다른 화면이 노출됩니다. Create New Queue Button타입 선택 화면항목 입력 화면두 번째 이미지와 같이 SQS에서는 Standard, FIFO 두 가지 타입을 제공하고 있습니다. 표준 대기열은 순서에 맞지 않게 메시지가 전송될 수 있습니다. 만약 순서를 반드시 유지해야 한다면 FIFO 대기열을 사용하거나, 순서 정보를 추가하고 사용해야 합니다. 하지만 FIFO 대기열의 경우 현재 미국 동부(버지니아 북부), 미국 동부(오하이오), 미국 서부(오레곤) 및 EU(아일랜드) 지역(Region)이서만 제공되고 있기 때문에 다른 곳에서는 사용할 수 없습니다. 2) 3) 1.Create New Queue ‘Create New Queue’에는 여러 항목이 있습니다. 우선 아래를 참조하여 각 항목에 적절한 내용을 기재합니다. Default Visibility Timeout : 대기열에서 조회한 메시지가 중복 조회되지 않기 위한 시간Message Retention Period : 메시지 보관 기간Maximum Message Size : 메시지 최대 사이즈Delivery Delay : 신규 메시지 전달 지연 시간Receive Message Wait Time : 조회된 메시지가 없을 경우, 사용 가능한 메시지를 기다리는 long polling 시간 설정Dead Letter Queue Settings : 정상적으로 처리되지 못한 메시지를 보관하기 위하여 메시지 수신 최대 수를 지정, 지정한 수신을 초과할 경우 지정한 큐에 메시지 저장2.큐 등록 확인 기본 값으로 설정한 큐 등록을 확인합니다. Queue List3.SQS 메시지 등록 import boto3, json sqs_client = boto3.client(     service_name='sqs',     region_name='xxxxxx' ) SQS 메시지 등록  response = sqs_client.send_message(     QueueUrl='https://sqs.xxxxxx.amazonaws.com/xxxxxx/sqs-test-1',     MessageBody='메시지 내용' )   print(json.dumps(response))   {"MD5OfMessageBody": "xxxxxxx", "MessageId": "xxxxx-xxxx-xxxxxx", "ResponseMetadata": {"RequestId": "xxxxxxx", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Fri, 09 Feb 2018 08:01:13 GMT", "content-type": "text/xml", "content-length": "378", "connection": "keep-alive", "x-amzn-requestid": "xxxxxxx"}, "RetryAttempts": 0}} 4.AWS Console 메시지 등록 확인 View MessageDetail Message5.조회와 실행 1)SQS 메시지를 조회합니다.2)LambdaWorker 함수를 실행하고 > InvocationType으로 동기, 비동기 등의 실행 유형을 설정합니다. import boto3, json   def handle(event, context):     queue_url = 'https://sqs.xxxxxx.amazonaws.com/xxxxxx/sqs-test-1' sqs_client = boto3.client(         service_name='sqs',         region_name='xxxxxx'     )      lambda_client = boto3.client(         service_name='lambda',         region_name='ap-northeast-1'     )      # SQS 메시지 조회     response = sqs_client.receive_message(         QueueUrl=queue_url,         MaxNumberOfMessages=10,         AttributeNames=[             'All'         ]     )      print(json.dumps(response))      # {"Messages": [{"MessageId": "xxxxx-xxxx-xxxxxx", "ReceiptHandle": "xxxxx-xxxx-xxxxxx", "MD5OfBody": "xxxxxxx", "Body": "\uba54\uc2dc\uc9c0 \ub0b4\uc6a9", "Attributes": {"SenderId": "xxxxxxx", "ApproximateFirstReceiveTimestamp": "1518163931724", "ApproximateReceiveCount": "1", "SentTimestamp": "1518163466941"}}], "ResponseMetadata": {"RequestId": "", "HTTPStatusCode": 200, "HTTPHeaders": {"server": "Server", "date": "Fri, 09 Feb 2018 08:12:11 GMT", "content-type": "text/xml", "content-length": "1195", "connection": "keep-alive", "x-amzn-requestid": "xxxxxxx"}, "RetryAttempts": 0}}      for message in response['Messages']:         payload = {'message': message, 'queueUrl': queue_url}          # Lambda Worker 함수 실행         lambda_client.invoke(             FunctionName='lambda_worker',             InvocationType='Event',             Payload=json.dumps(payload)         ) 6.Lambda Consumer 함수 등록 Execution role : SQS ReceiveMessage, Lambda InvokeFunction, CloudWatchLogs7.확인-실행-삭제 1) 이벤트로 넘어온 메시지 내용을 확인하고2) 메시지 프로세스를 실행한 후3) SQS 메시지를 삭제합니다. import boto3, json   def handle(event, context):     sqs_client = boto3.client(         service_name='sqs',         region_name='xxxxxx'     )      message_body = json.loads(event['message']['Body'])      queue_url = event['queueUrl']     receipt_handle = event['message']['ReceiptHandle']      ###############     # 큐 메시지 처리     ############### # SQS 메시지 삭제     sqs_client.delete_message(         QueueUrl=queue_url,         ReceiptHandle=receipt_handle     ) 8.Lambda Worker 함수 등록 Execution role : SQS DeleteMessage, CloudWatchLogs9.CloudWatch의 Event Rule 등록 Event RulesCreate Rule10.1분에 한 번씩 지정한 Lambda 함수를 실행하여 SQS 메시지 확인 참고)이것만은 꼭 알아두세요! 여러 대의 서버에 메시지 사본을 저장하기 때문에 가끔씩 메시지 사본을 받거나 삭제하는 중엔 저장 서버 중 하나를 사용할 수 없을 수도 있다고 합니다. 이 경우, 해당 문제가 발생하면 사용할 수 없는 서버의 메시지가 삭제되지 않아, 메시지를 다시 가져와야 하는 문제가 생길 수 있습니다. 그러므로 애플리케이션에서 동일 메시지를 두 번 이상 처리하는 것도 대비해야 합니다.Conclusion지금까지 AWS 환경에서 SQS, Lambda, CloudWatch EventRule을 이용한 메시지 대기열 등록과 처리에 대한 기본 예제들을 실행해봤습니다. AWS의 다른 서비스들과 같이 아주 간단한 방법으로 메시지 대기열을 이용할 수 있었습니다. 오늘 살펴본 방법들을 활용하면 동영상 트랜스 코딩 등의 작업을 비롯해 분산 애플리케이션 간의 데이터 처리에도 유용하게 사용할 수 있을 겁니다. ps.아마존 형님들의 IT 인프라를 이용하여 편하게 개발에만 집중합시다. 참고 1) 각 서비스 매뉴얼은 아래 페이지 링크 참조하면 된다.SQSLambdaboto3 2)2018년 2월 기준이다. 3)표준 대기열과 FIFO 대기열의 특징은 아래와 같으며 자세한 내용은 매뉴얼에 정리되어 있다. 표준 대기열 : 무제한 처리량, 최선 정렬FIFO 대기열 : 높은 처리량, 선입선출 전송 글이상근 팀장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 761

HBase상 트랜잭션 라이브러리 Haeinsa를 소개합니다 - VCNC Engineering Blog

비트윈에서는 서비스 초기부터 HBase를 주요 데이터베이스로 사용하였습니다. HBase에서도 일반적인 다른 NoSQL처럼 트랜잭션을 제공하지 않습니다. HBase, Cassandra와 MongoDB는 하나의 행 혹은 하나의 Document에 대한 원자적 연산만 제공합니다. 하지만 여러 행에 대한 연산들을 원자적으로 실행할 수 있게 해주는 추상화된 트랜잭션 기능이 없다면 보통의 서비스 개발에 어려움을 겪게 됩니다. 비트윈 개발팀은 이런 문제를 해결하기 위해 노력했으며, 결국 HBase에서 트랜잭션을 제공해주는 라이브러리인 Haeinsa를 구현하여 실제 서비스에 적용하여 성공적으로 운영하고 있습니다. VCNC에서는 Haeinsa를 오픈소스로 공개하고 이번 글에서 이를 소개하고자 합니다.Haeinsa란 무엇인가?Haeinsa는 Percolator에서 영감을 받아 만들어진 트랜잭션 라이브러리입니다. HAcid, HBaseSI 등 HBase상에서 구현된 트랜잭션 프로젝트는 몇 개 있었지만, 성능상 큰 문제가 있었습니다. 실제로 서비스에 적용할 수 없었기 때문에 Haeinsa를 구현하게 되었습니다. Haeinsa를 이용하면 다음과 같은 코드를 통해 여러 행에 대한 트랜잭션을 쉽게 사용할 수 있습니다. 아래 예시에는 Put연산만 나와 있지만, 해인사는 Put외에도 Get, Delete, Scan 등 HBase에서 제공하는 일반적인 연산들을 모두 제공합니다.HaeinsaTransaction tx = tm.begin(); HaeinsaPut put1 = new HaeinsaPut(rowKey1); put1.add(family, qualifier, value1); table.put(tx, put1); HaeinsaPut put2 = new HaeinsaPut(rowKey2); put2.add(family, qualifier, value2); table.put(tx, put2); tx.commit(); Haeinsa의 특징Haeinsa의 특징을 간략하게 정리하면 다음과 같습니다. 좀 더 자세한 사항들은 Haeinsa 위키를 참고해 주시기 바랍니다.ACID: Multi-Row, Multi-Table에 대해 ACID 속성을 모두 만족하는 트랜잭션을 제공합니다.Linear Scalability: 트래픽이 늘어나더라도 HBase 노드들만 늘려주면 처리량을 늘릴 수 있습니다.Serializability: Snapshot Isolation보다 강력한 Isolation Level인 Serializability를 제공합니다.Low Overhead: NoSQL상에서의 트랜잭션을 위한 다른 프로젝트에 비해 오버헤드가 적습니다.Fault Tolerant: 서버나 클라이언트가 갑자기 죽더라도 트렌젝션의 무결성에는 아무 영향을 미치지 않습니다.Easy Migration: Haeinsa는 HBase를 전혀 건드리지 않고 클라이언트 라이브러리만 이용하여 트랜잭션을 구현합니다. 각 테이블에 Haeinsa 내부적으로 사용하는 Lock Column Family만 추가해주면 기존에 사용하던 HBase 클러스터에도 Haeinsa를 쉽게 적용할 수 있습니다.Used in practice: 비트윈에서는 Haeinsa를 이용하여 하루에 3억 건 이상의 트랜잭션을 처리하고 있습니다.Haeinsa는 오픈소스입니다. 고칠 점이 있다면 언제든지 GitHub에 리포지터리에서 개선에 참여하실 수 있습니다.Haeinsa의 성능Haeinsa는 같은 수의 연산을 처리하는 트랜잭션이라도 소수의 Row에 연산이 여러 번 일어나는 경우가 성능상 유리합니다. 다음 몇 가지 성능 테스트 그래프를 통해 Haeinsa의 성능에 대해 알아보겠습니다.아래 그래프는 3개의 Row에 총 6개의 Write, 3개의 Read연산을 수행한 트랜잭션의 테스트 결과입니다. 두 개의 Row에 3Write, 1Read 연산을 하고, 한 개의 Row에 1Read 연산을 한 것으로, 비트윈에서 가장 많이 일어나는 요청인 메시지 전송에 대해 시뮬레이션한 것입니다. 실제 서비스에서 가장 많이 일어나는 종류의 트랜잭션이라고 생각할 수 있습니다. 그런데 그냥 HBase를 사용하는 것보다 Haeinsa를 이용하는 것이 더 오히려 좋은 성능을 내는 것을 알 수 있습니다. 이는 Haeinsa에서는 커밋 시에만 모든 변경사항을 묶어서 한 번에 반영하기 때문에, 매번 RPC가 일어나는 일반 HBase보다 더 좋은 성능을 내는 것입니다.HBase 클러스터가 커질수록 트랜잭션 처리량이 늘어납니다. HBase와 마찬가지입니다.HBase 클러스터의 크기에 따른 응답시간 입니다. HBase와 다르지 않습니다..아래 그래프는 2개의 Row에 각각 한 개의 Write, 나머지 한 개의 Row에는 한 개의 Read 연산을 하는 트랜잭션에 대해 테스트한 것입니다. 각 Row에 하나의 연산만이 일어나기 때문에 최악의 경우라고 할 수 있습니다. 처리량과 응답시간 모두 그냥 HBase를 사용하는 것보다 2배에서 3배 정도 좋지 않은 것을 알 수 있습니다. 하지만 이 수치는 DynamoDB 상의 트랜잭션과 같은 다른 트랜잭션 라이브러리와 비교한다면 상당히 좋은 수준입니다.HBase보다 처리량이 떨어지긴 하지만, 클러스터가 커질수록 처리량이 늘어납니다.HBase보다 응답시간이 크긴 하지만 클러스터 크기에 따른 변화가 HBase와 크게 다르지 않습니다.프리젠테이션Haeinsa에 대한 전반적인 동작 원리와 성능을 소개하는 프리젠테이션입니다. 좀 더 자세히 알고 싶으시다면 아래 프리젠테이션이나 Haeinsa 위키를 참고해주세요.<iframe class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/2d4b2bd00fc201314ae312fe4cd13937?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 750px; height: 563px;">

기업문화 엿볼 때, 더팀스

로그인

/