스토리 홈

인터뷰

피드

뉴스

조회수 1287

MySQL에서 RDS(Aurora) 로 이관하기

안녕하세요. 스티비팀 서버 개발자 이학진 입니다. 저희는 최근 서비스에서 사용 중이던 MySQL DB를 RDS로 이관하는 작업을 진행하였습니다. 무엇 때문에 이관을 결정하게 되었는지와 어떻게 이관을 진행하였는지에 대해 글을 써보도록 하겠습니다.배경stibee.com은 작년 11월에 정식 오픈한 새내기 이메일 마케팅 서비스 입니다. 사실 오픈 초기부터 얼마전까지만 해도 AWS EC2의 m4.large 인스턴스 하나로 운영되던 서비스였습니다.(사실 웹+API 서버 1대, 메일발송서버 1대)그리고 이 싱글 인스턴스에 무려 6개의 서버, mysql 1개, kafka 1개, redis 1개가 돌고 있었습니다. 그럼에도 불구하고 cpu사용률은 20%를 넘지 않았습니다.하지만 최근 사용자도 점점 늘어났고, 네이버에서 메일 수신정책을 변경하면서 메일발송서버에 대한 요청이 급증했습니다.스티비에서 네이버로 대량메일을 발송했을 때 해당 메일의 본문 링크를 자동검사하는 것을 발견했는데요, 따라서 네이버로부터 비정상적으로 많은 요청이 들어오고 있었습니다. (어떤 기준으로 이런 검사를 하는 것인지 정확한 정책은 아직 모릅니다. 담당자분 이 글을 보신다면 연락주세요. 친하게 지냈으면 합니다#슬로워크 #스티비 #개발 #서버개발 #개발환경 #MySQL #인사이트
조회수 1142

어제의 실수는 오늘의 노하우!

Overview서비스되는 프로젝트에 첫 커밋(Commit)했던 순간이 아직도 생생합니다. 직원이 10명 남짓이던 시절, 특정 데이터를 삭제할 때나 쓰던 관리자 페이지였는데요. 당시엔 MVC Pattern, Transaction 등 아무것도 몰랐기 때문에 실수를 반복했습니다. (팀장님으로부터 피드백도 많이 받았죠.) 어떤 실수였는지 궁금하시죠? 오늘은 두 번 다시 겪고 싶지 않은 실수들과 깨달은 몇 가지 이야기와 개발자가 꼭 지켜야할 것을 소개하겠습니다. 사용자를 생각하는 마음예전에는 로직을 짤 때 실패하는 케이스를 깊게 생각하지 않았습니다. 왜냐하면 “나는 기능을 만들고, 사용자는 내가 만든 기능을 쓴다.”고 생각했기 때문입니다. 요구 사항대로 동작하게 만들고, 예외 케이스는 사용자의 책임으로 돌렸습니다. 하지만 이런 태도로 개발하면 UI/UX는 발전할 수 없고, 서비스도 개선될 수 없으며, 사용자의 불만만 생긴다는 걸 곧 알게 되었죠. 작년 이맘때쯤 브랜디 앱에 진열될 상품 관리 페이지를 개발했습니다. 요건에 기재된 내용을 요약하면 아래와 같았습니다.제시된 요건등록 가능한 상품의 개수는 ‘무제한’이다.하나의 페이지에 여러 구좌를 관리하는 영역이 들어갔으면 좋겠다.상품 조회 화면에는 ‘누적 판매량’과 ‘7일 판매량’ 항목이 추가되어야 한다.우선 ‘무제한’이라는 단어에 각 관리 영역마다 max-height를 지정했는데요. 여러 관리 영역이 하나의 페이지에 들어가더라도 스크롤을 많이 하지 않아도 되게 작업했습니다. 이뿐만이 아닙니다. 중복된 상품을 등록할 수도 있기 때문에 그것에 대한 유효성도 추가했죠. 하지만 막상 프로덕션(production)에 배포되니 직원들의 피드백이 쏟아졌습니다.“상품을 등록하고 다시 관리 페이지에 진입하려니 시간이 오래 걸려요.”“상품이 중복됐다고 alert이 뜨는데 어떤 상품이 겹치는지 알 수는 없나요? 혹시… 일일이 찾아야 해요?” 2)“상품 setting 후에 등록을 했는데 다시 보니 안 되어있어요!”“아뿔싸, ’무제한’이라는 단어를 보고 max-height 값만 떠올리다니!” 드러난 이슈들을 수정하면서 반성하고 또 반성했습니다. 등록된 상품들을 가져와서 페이지에 렌더링(rendering)할 때, 상품 수가 많을수록 뷰 페이지의 로딩 속도는 느려진다는 걸 예측하지 않았습니다. 심지어 하나의 페이지에 여러 구좌를 관리할 수 있도록 개발했으니, 불러와야 할 상품은 수백, 수천 개였을 겁니다. 직원들은 하염없이 페이지만 바라보며 불만을 터트릴 수밖에 없었고요. 이후엔 페이지에 진입하자마자 상품 목록을 가져오지 않고, 특정 버튼을 눌렀을 때 ajax로 상품을 로딩하는 방식으로 개선했습니다.당시 개발했던 진열 관리 화면상품 등록이 잘 안 된다는 이슈는 로컬(local) 및 스테이징(staging) 서버에서 재현되지 않아 고개를 갸웃거렸는데요. 프로덕션(production) 정보를 보고 나서야 원인을 잡을 수 있었습니다. ajax를 이용해 POST로 전송할 수 있는 array의 최대 사이즈가 정해져 있다는 걸 알게 된 것이죠.1) 결국 JSON 형태로 바꾸어 데이터를 전송하고, 서버사이드에서 배열을 다시 변환해 로직을 수행하도록 개선했습니다. 팀장님의 질문도 기억에 남습니다. 팀장님은 단호하게 물었죠.“쿼리 돌아가는 건 확인했어?”일정이 급급하다는 이유로 쿼리를 확인하는 과정을 간과했습니다. 데이터는 당연히 0건으로 나왔지만 조건에 부합하는 데이터가 없어서인지, 잘못된 질의 때문인지는 의심하지 않았던 것이죠. 팀장님은 말했습니다.“네가 자꾸 실수하면 사용자는 우리 시스템을 신뢰할 수 없을 거야.”PRODUCT_REGIST_DATETIME BETWEEN NOW() AND NOW() - 7 나 : 7일동안 등록된 상품 데이터를 가져와주세요.데이터베이스 : …???주위를 관심 있게 둘러보는 눈지난 번에 쓴 신입개발자를 위한 코드의 정석을 보면 ‘모든 개발조직은 좋은 품질의 소프트웨어를 개발할 수 있는 개발자를 원한다’는 문장이 있습니다. 좋은 품질과 가치 있는 서비스를 만드는 건 개발자가 당연히 가져야 할 책임과 소신입니다. 서비스에 대한 이해도 어느 정도 필요하고요. 그렇지 않으면 엉뚱한 서비스가 나옵니다.재작년, 브랜디 커머스 웹 1.0 버전을 개발했을 땐 e-commerce에 대한 이해도가 거의 없었습니다. 유사한 서비스들의 레퍼런스를 진행하고 개발을 시작해야 했는데 그저 상상력에 의존한 채 UI/UX 개발을 진행했었습니다. 그때 느꼈던 걸 몇 가지 정리해보겠습니다. 유사한 서비스를 적극적으로 사용하자!사람들은 많이 쓰는 서비스의 UI/UX에 익숙합니다. 그러므로 유명하면서도 비슷한 목적을 수행하는 다른 서비스들을 사용해보세요. 그 분야에 대한 센스가 무럭무럭 커질 겁니다. 더 나아가서는 사람들이 익숙하다고 느끼는 것보다 훨씬 더 편한 UI/UX를 떠올릴 수도 있겠지요!다른 개발자의 생각도 물어보자!같은 문장을 보고도 다르게 해석하듯, 같은 서비스를 개발하는 개발자들도 저마다 솔루션은 다릅니다. 자신은 괜찮다고 생각하더라도 다른 개발자에게 꼭 물어보세요. 미처 생각하지 못했던 의견들이 나올 수 있습니다. 즉, 많은 커뮤니케이션이 더 좋은 개발을 돕는 것이죠.개발하기 쉬운 서비스 말고, 사용자가 쓰기 편한 서비스로 만들자!일정에 쫓기면 당장 개발하기 편한 방법을 선호할 수도 있습니다. 개발자의 주관적인 판단이 UI/UX를 망칠 수 있는데도 말이죠. 실수는 자신이 만회해야 합니다. 눈앞의 것을 생각하지 말고, 사용자를 생각하며 개발합시다. 사용자가 기분 좋게 서비스를 이용하는 게 훨씬 뿌듯하잖아요. Conclusion무수한 실패담 중에 기억나는 몇 가지만 추렸습니다. 과거의 코드나 실수의 이력들을 글로 써 보니 ‘전부 내 경험이 되었구나’라는 생각이 듭니다. 지금 이 글을 읽고 있는 당신은 어떤 실수를 해보셨나요? 손해 보는 경험은 없습니다. 분명 언젠가는 도움이 될 거예요. 주석1)이 때문에 상품을 등록할 때, 스크립트에서 array로 담아 전송하면 데이터가 누락되어 제대로 등록되지 않거나 에러가 발생할 수 있는 결함이 있었다.2)중복된 상품을 화면에 표시해주는 기능은 여러 상황으로 인해 개선하지 못했다. 이후에는 발생하는 문제의 사유를 사용자에게 친절히 알려주어서 원하는 결과를 얻도록 힘쓰고 있다. 참고개발자는 개발만 잘하면 된다?사용자는 결코 실수하지 않는다글김우경 대리 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1166

테이블이냐, 컬렉션이냐, 그것이 문제로다!(KOR)

편집자 주 외래어 표기법에 따르면 ‘원어에서 띄어 쓴 말은 띄어 쓴 대로 한글 표기를 하되, 붙여 쓸 수도 있다.’고 규정하고 있다.(제3장 제1절 영어의 표기, 제10항과, 컴퓨터 전문어, 전기 전문어 등) 즉 ‘원칙’과 ‘허용’이 모두 가능하다는 의미다. 이를 바탕으로 여러 표기 용례를 참고한 결과, TableView는 ‘테이블뷰(원칙)’로 표기해야 하나, 본문에서는 독자의 가독성을 높이기 위해 ‘테이블 뷰(허용)’로 표기한다. 응용하여, CollectionView는 ‘컬렉션 뷰’로, TableViewCell은 ‘테이블 뷰 셀’ 등으로 띄어 쓴다. Overview앱에서 데이터를 사용자에게 보여줄 땐 여러 가지의 모습으로 나타납니다. 설정 앱처럼 목록으로 보여줄 때도 있고, 사진 앱처럼 그리드(grid) 형식으로 보여줄 때도 있습니다. 이처럼 데이터를 보여줄 때 많이 사용되는 뷰는 테이블 뷰(UITableView) 또는 컬렉션 뷰(UICollectionView)입니다. 각자 특징이 있기 때문에 앱의 성격에 따라 적절한 뷰를 사용해야 합니다. 왜냐하면 목록을 보여주는 디자인을 바꿀 때, 다시 개발해야 하는 수고를 덜 수 있기 때문입니다. 이번 글에선 각각의 뷰를 간략하게 알아보겠습니다. 목록 형식의 설정 앱과 그리드 형식의 사진 앱 스크린샷테이블 뷰(UITableView)단일 열에 배열된 행을 사용해 데이터를 표시하는 뷰입니다. 수직 스크롤만 가능하며, 테이블의 개별 항목을 구성하는 셀은 테이블 뷰 셀(UITableViewCell) 객체입니다. 테이블 뷰는 이 객체들을 이용해 테이블에 표시되는 행을 그립니다. 여러 행은 하나의 섹션 안에 구성될 수 있으며, 각 섹션은 헤더(header)와 푸터(footer)를 가질 수 있습니다. 섹션과 행은 인덱스 번호로 구별하는데, 번호는 0부터 시작합니다. 테이블 뷰는 plain과 grouped 스타일 중 한 가지의 스타일을 가질 수 있습니다. Plain 스타일은 보통 목록 스타일입니다. 섹션의 헤더와 푸터는 섹션 분리기(inline separators)로 표시되고 스크롤을 할 때 해당 섹션 안에 있는 콘텐츠 위에 나타납니다. Grouped 스타일은 시각적으로 뚜렷한 행 그룹을 표시하는 섹션이 있습니다. 섹션의 헤더와 푸터는 콘텐츠 위에 나타나지 않습니다. 아래와 같은 사진을 보시면 확연히 차이를 볼 수 있습니다. plain 스타일의 연락처 앱과 grouped 스타일의 설정 앱테이블 뷰의 많은 메소드들은 인덱스패스(NSIndexPath) 객체를 매개변수 또는 리턴 값으로 사용합니다. 테이블 뷰는 해당하는 행의 색인 인덱스와 섹션 인덱스 값을 가져올 수 있게 인덱스패스의 범주를 선언합니다. 또한 색인 인덱스와 섹션 인덱스 값을 가지고 인덱스패스를 만들 수 있습니다. 특히 여러 섹션이 있는 테이블 뷰는 섹션 인덱스 값이 반드시 있어야 행의 인덱스 번호로 구별할 수 있습니다.override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> AttractionTableViewCell {         // Table view cells are reused and should be dequeued using a cell identifier.         let cellIdentifier = "AttractionTableViewCell"              guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? AttractionTableViewCell else {             fatalError("The dequeued cell is not an instance of AttractionTableViewCell.")         }                 let attraction = attractions[indexPath.row]                 cell.attractionLabel.text = "\(indexPath.row). \(attraction.nameWithDescription)"         cell.attractionImage.image = attraction.photo                 cell.attractionImage.tag = indexPath.row                 attraction.indexPath = indexPath                 ...                 return cell     } 위의 코드는 데이터 소스(data source) 메소드로, 테이블 뷰의 특정한 위치에 셀을 추가합니다. 다시 말해, 이 메소드는 테이블 뷰가 ‘표시할 새로운 셀이 필요할 때마다’ 특정 행에 노출할 정보가 있는 셀을 만들고 리턴하는 걸 말합니다. 매개변수로 필요한 셀 객체의 행을 가리키는 indexPath 값을 전달합니다. 그리고 indexPath의 row 값을 이용해서 attraction이라는 배열 인덱스로 활용하고, 셀에 표시할 정보들을 설정합니다. 여기서 attraction 배열은 관광 명소들의 정보들이 담고 있는 배열인데, 1행은 첫 번째로 저장한 관광 명소, 2행은 두 번째로 저장한 관광 명소 등 순서대로 설정하도록 indexPath.row 값을 이용하는 것입니다. indexPath의 row 값과 배열의 인덱스 값은 0부터 시작하기 때문입니다. 해당 예제는 섹션이 1인 경우이기 때문에 섹션 인덱스 값이 없지만, 섹션이 여러 개 있다면 반드시 섹션 인덱스 값을 이용해서 설정해야 합니다.테이블 뷰 객체는 데이터 소스(data source)와 델리게이트(delegate)가 필요합니다. 데이터 소스는 UITableViewDataSource 프로토콜을 구현해야 하고, 델리게이트는 UITableViewDelegate 프로토콜을 구현해야합니다. 데이터 소스는 테이블 뷰가 테이블을 만들 때 필요한 정보를 제공하고 테이블의 행이 추가, 삭제 또는 재정렬할 때 데이터 모델을 관리합니다. 델리게이트는 화면에 보이는 모습과 행동을 담당합니다. 예를 들어 표시할 행의 수, 사용자가 특정 행을 터치했을 때, 행의 재정렬 등과 같은 것입니다.override func numberOfSections(in tableView: UITableView) -> Int {         // #warning Incomplete implementation, return the number of sections         return 1     }      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {         // #warning Incomplete implementation, return the number of rows         return attractions.count     } 위의 두 소스는 데이터 소스가 필수적으로 구현해야 하는 메소드입니다. 하나는 섹션의 개수를 리턴하고, 또 하나는 한 섹션 안에 있는 행의 개수를 리턴합니다.테이블 뷰는 수정 모드에서 행을 추가, 삭제, 재정렬할 수 있습니다. 각 행은 테이블 뷰 셀에 연관된 editingStyle에 따라서 추가, 삭제, 재정렬을 할 수 있는데, 예를 들어 editingStyle이 insert라면 추가하는 메소드를 실행하고, delete면 삭제하는 메소드를 실행합니다. 행의 showsReorderControl 속성이 true라면, 재정렬하는 메소드를 실행할 수 있습니다.// Override to support editing the table view.     override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {         if editingStyle == .delete {             // Delete the row from the data source             ...                 // delete rows and attractions and reload datas             attractions.remove(at: indexPath.row)             tableView.deleteRows(at: [indexPath], with: .middle)             tableView.reloadData()         } else if editingStyle == .insert {             // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view         }     } 위 소스는 editingStyle이 delete일 때 셀을 삭제하고 테이블 뷰를 다시 로드하는 기능을 구현한 것입니다.테이블 뷰를 만드는 가장 쉽고 권장하는 방법은 바로 스토리보드에서 테이블뷰컨트롤러(UITableViewController)를 이용해서 만드는 겁니다. 런타임에 테이블뷰컨트롤러는 테이블 뷰를 만들고 델리게이트와 데이터 소스를 자기 자신으로 할당합니다.컬렉션 뷰(UICollectionView)컬렉션 뷰는 테이블 뷰에서 할 수 있는 모든 것을 할 수 있습니다. 섹션을 가질 수 있고, 인덱스패스 값을 이용해서 셀을 구별합니다. 이 셀들은 컬렉션 뷰 셀(UICollectionViewCell)의 서브 클래스이며 데이터 소스(UICollectionViewDataSource)와 델리게이트(UICollectionViewDelegate)가 필요합니다. 셀을 추가, 삭제, 재정렬하는 기능도 구현할 수 있습니다. 그렇다면 컬렉션 뷰와 테이블 뷰를 구분하는 특징은 무엇일까요? 바로 레이아웃입니다. 컬렉션 뷰는 여러 개의 열과 행으로 셀을 표현할 수 있습니다. 예를 들어, 그리드(grid) 형태로 아이템의 목록을 보여줄 수 있습니다. 그래서 수직 스크롤뿐만 아니라 수평 스크롤도 할 수 있습니다.스토리보드에서 디자인한 테이블 뷰 셀과 컬렉션 뷰 셀위 스크린샷에서 테이블 뷰와 컬렉션 뷰의 가장 큰 차이는 바로 셀입니다. 테이블 뷰에서는 하나의 열에 여러 행을 표시하는 형식이기 때문에, 셀의 모습을 행에 맞춰서 디자인합니다. 하지만 컬렉션 뷰는 열과 행을 만들 수 있기 때문에, 꼭 행의 모습이 아니더라도 다양한 모습으로 셀을 디자인할 수 있습니다. 컬렉션 뷰 셀의 가장 큰 특징이기도 하죠. 위처럼 셀을 디자인하고 앱을 실행하면 아래의 화면이 나타납니다.테이블 뷰와 컬렉션 뷰의 앱 화면 차이또한 컬렉션 뷰는 레이아웃 객체가 있습니다. 기존에 제공하는 flow layout을 사용해도 괜찮지만, 본인이 원하는 레이아웃 모양을 custom layout을 만들어서 사용합니다. 이를 담당하는 프로토콜은 UICollectionViewDelegateFlowLayout 입니다.func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {         let fullWidth = collectionView.frame.size.width - (self.CGFLOAT_INSET_WIDTH * 3) - (self.CGFLOAT_ITEMSPACING * 3)         let width = fullWidth/3         return CGSize(width: width, height: width + self.CGFLOAT_HEIGHT_ATTRACTIONCELL_DEFAULT)     }         func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {         return UIEdgeInsetsMake(self.CGFLOAT_LINESPACING_VERTICAL, self.CGFLOAT_INSET_WIDTH, self.CGFLOAT_LINESPACING_VERTICAL, self.CGFLOAT_INSET_WIDTH)     } 위 소스에서 collectionView(:layout:sizeForItemAt:) 메소드는 해당하는 셀의 사이즈를 설정하고, collectionView(:layout:insetForSectionAt:) 메소드는 섹션 안에 margin을 설정합니다.여러 모양의 셀을 이루어 하나의 뷰 화면을 구현할 수도 있습니다. 섹션마다 셀을 만들어 각각 다른 모습의 셀을 설정하고, 한 화면에 다양한 모습의 셀을 가진 뷰를 만드는 것입니다. 예를 들어, 헤더, 메뉴, 본문, 푸터 각각 셀을 만들어서 원하는 모양으로 만들고, 하나의 뷰 컨트롤러에 셀을 조합해서 한 화면에 나타나게 할 수 있습니다. 이 방법을 사용하면 자주 사용하는 셀을 재활용할 수 있습니다. 똑같은 헤더와 푸터 셀을 여러 번 만들지 않고 기존의 셀을 재활용하면 시간도 절약하고, 훨씬 깔끔한 소스를 만들 수 있을 겁니다.브랜디 앱 스크린샷 일부위의 스크린샷처럼 여러 화면에서 보여줘야 할 똑같은 뷰가 있을 때, 셀 xib 파일을 만들고 컬렉션 뷰에서 셀을 섹션별로 설정 및 사용하면 재활용하기 좋습니다.Conclusion지금까지 테이블 뷰와 컬렉션 뷰의 특징들을 살펴봤습니다. 한마디로 정리하면 테이블 뷰는 가장 간단한 목록을 만들 수 있습니다. 컬렉션 뷰는 다양한 모습의 목록으로 커스터마이징(Customizing)할 수 있습니다.그렇다면 우리는 어떤 것을 선택해야 할까요? 구현할 목록이 얼마나 복잡한지에 따라 선택은 달라집니다. 테이블 뷰는 간단하고 보편적인 목록을 만듭니다. 반면에 컬렉션 뷰는 특정한 모습의 목록을 만들 수 있습니다. 그래서 테이블 뷰는 목록이 간단하고 디자인 변경이 없을 때만 사용하길 권장합니다. 하지만 나중에 디자인이 바뀔 수도 있다면 컬렉션 뷰를 사용하는게 더 좋겠죠.Simple is the best! 간단하게 구현할 수 있는 건 테이블 뷰를 사용합시다. 테이블 뷰에서 구현하기 힘들다면 컬렉션 뷰를 이용해 개성 있는 목록을 마음껏 만들어봅시다!참고UITableView - UIKit | Apple Developer DocumentationUICollectionView - UIKit | Apple Developer Documentation 글김주희 사원 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1327

정확한 프로세스 모델을 측정하는 기준은?

이벤트 로그로부터 정확한 프로세스 모델을 도출하기 위해 고려해야 할 점은 무엇일까요?프로세스의 특성과 이벤트 로그에 맞는 적절한 알고리즘을 적용하는 것이 중요하지만 오늘은 좀 더 일반적인 사항에 대해 생각해 보겠습니다.프로세스 모델의 품질을 측정하는 4가지 기준은 Fitness, Generalization, Simplicity, Precision입니다.좋은 프로세스 모델을 발견하기 위해서는 이 4가지 기준 사이에서 균형을 잘 유지해야 합니다.[그림 1] 프로세스 모델 품질 측정의 4가지 기준Fitness(적합도)는 관찰된 이벤트 로그를 얼마나 잘 설명할 수 있는지를 나타냅니다. Fitness가 높을수록 모든 이벤트 로그의 경로를 표현하기 때문에 데이터 집합을 잘 설명할 수 있으나 수많은 경로가 프로세스 모델에 나타나게 되어 프로세스 모델이 복잡해지게 됩니다.Generalization(일반화)은 Overfitting을 피하는 것입니다. Overfitting된 모델은 모델 추출 대상이 되는 데이터(이벤트 로그)에 대해서는 정확도가 높으나 동일 프로세스에서 추출한 다른 데이터 집합에 대해서는 정확도가 낮고, 높은 오류율을 보여주게 됩니다. 따라서 Generalization 수준이 높을수록 다른 데이터에 적용했을 때의 적중률(설명 정도)이 높아져서 프로세스 모델을 다른 데이터에 적용하기가 좋습니다. 하지만, 지나치게 Generalization이 높을 경우 대상 데이터 집합에 대한 프로세스 모델 적중률만 높아지지 프로세스에 대한 의미 있는 정보를 전달하지 못하는 문제가 발생합니다.Simplicity(단순화)는 프로세스 모델을 단순하게 만드는 것입니다. 프로세스 모델이 단순할수록 쉽게 이해하고 한눈에 프로세스를 파악할 수 있으나 적합도가 떨어지게 됩니다. 적합도가 떨어지면 추출한 프로세스 모델로 설명할 수 없는 이벤트 로그가 많아지게 됩니다.Precision(정확도)은 Underfitting을 피하는 것입니다. Underfitting된 모델은 Overfitting과 달리 모델을 단순화하여 공통 경로만 표현하게 되어 프로세스를 정확하게 설명할 수 없게 됩니다. Precision이 높을수록 기존 데이터에 대해 정확하게 설명할 수 있으나 지나치게 높을 경우 다른 데이터 셋에 대한 오류율이 증가하는 문제가 생깁니다.4가지 품질 특성을 보면 Fitness와 Simplicity, Generalization과 Precision은 서로 반대되는 특성을 가지고 있습니다. 즉, Fitness가 너무 높으면 Simplicity가 낮은 문제가 생기고 Generalization이 높으면 Precision이 낮은 문제가 생기게 됩니다.Overfitting과 Underfitting 예제를 통해 좀 더 살펴보도록 하겠습니다.[그림 2] Underfitting과 Overfitting 그림[그림 2]에서 볼 수 있듯이 Underfitting은 데이터 분류 기준을 단순하게 구할 수 있으나 새로운 데이터 집합을 Underfitting된 모델에 대입하면 의미 있는 결과를 얻기가 힘듭니다. 이에 반해 Overfitting은 모든 데이터를 정확히 분류하고 있으나 데이터의 특성을 일반화시킬 수 없습니다. [그림 3] Underfitting 모델[그림 3]의 경우 모든 경로를 표현 가능하여 Fitness 만족, 다른 모델에도 적용 가능하여 Generalization 만족, 모델도 간단하여 Simplicity도 만족하지만 실제 프로세스가 어떻게 수행되는지 설명해 주지 못해 도출된 프로세스 모델에서 유의미한 정보를 얻을 수 없습니다. 즉, 모델이 Underfitting되어 Precision 조건을 만족시키지 못합니다.[그림 4] Overfitting 모델[그림 4]는 관찰된 이벤트 로그를 모두 나열한 프로세스 모델입니다. 이렇게 할 경우 모델을 도출할 때 사용한 이벤트 로그의 프로세스 패턴을 모두 나타내어 Fitness와 Precision은 만족하나 Simplicity와 Generalization은 만족하지 않습니다. Overfitting된 모델도 프로세스 모델에서 유의미한 정보를 얻을 수 없습니다.이상과 같이 Fitness, Generalization, Simplicity, Precision 4가지 기준을 잘 조화시켜야 정확한 프로세스 모델 도출이 가능합니다.#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트
조회수 1430

[인공지능 in IT] 서로 다른 우리, 대화할 수 있을까?

설연휴 동안 그간 못 봤던 밀린 TV 프로그램들을 맘껏 즐기며 여유로운 시간을 보냈다. 그 중에서도 여러 분야의 전문가를 초빙해 특강을 해주는 tvN의 '어쩌다 어른'을 보기 시작했다. 몇 년 전 언어인문학을 주제로 한 조승연 작가님편을 보니 새삼 현재 대한민국이 처한 현실을 피부로 느끼게 되더라.< tvN>강연에서 가장 심도있게 다룬 부분은 대한민국 영어교육의 현실이다. 초등학교부터 영어 수업을 듣고, 심지어 말도 제대로 떼기 전인 유아기부터 영어를 주입시키는 것이 어느새 자연스러운 일이 되어버렸다. 하지만, 10년, 20년 이상 영어 교육을 받았는데도 막상 영어로 문서 작업을 하거나, 외국인이 길을 물어보면 식은땀을 흘리는 이유는 무엇일까? 어째서 한국에서는 영어를 제대로 하려는 노력보다, 영어를 아는 노력을 하고 있는 것일까?재미있는 사실은 우리만 영어를 배우려고 애먹는 것이 아니다. 미국인이 한국어를 배울 때에도 비슷한 현상을 겪는다. 강연 중 'FSI(The Foreign Service Institute)'에서 미국인들이 다른 나라 언어를 얼마나 공부해야 소통할 수 있는지에 대한 연구 자료를 공개했다. 언어별 Level 1부터 Level 5까지 다섯 가지 난이도로 구분 되어있고, 이에 따른 총 필요시간으로 구성되어 있는 연구에서, 한국어는 일본어, 중국어와 함께 소통하기 까지 총 2,200시간을 공부해야 하는 Level 5군에 속해 있었다.즉, 전세계 7,000여 개가 넘는 언어 중 한국어는 영어와 문장구조가 완전히 다르기 때문에, 24시간 내내 공부해도 90일 넘게 공부해야 한다는 것. 이렇듯 모국어가 아닌 다른 언어를 배우기 위해서는 어마어마한 시간과 노력이 필요하다. 만약, 단순히 언어를 알기 위해 배우는 것보다, 소통하기 위해 배운다면 흔히들 말하는 'ROI(Return on Investment)'를 더 높일 수 있자 않을까.출처: 동아일보소통을 위한 언어 학습은 비단 사람에게만 해당되는 것이 아니다. 기계와 사람의 소통 역시 요즘과 같은 인공지능 시대에서는 빼놓을 수 없는 부분이다. 몇 년 전부터 업계에서는 '챗봇(Chatbot)' 열풍이 불고있다. 챗봇은 대화(Chat)와 로봇(Robot) 두 단어를 합친 신조어로서, 각종 앱이나 웹을 기반으로 문자를 통해 사용자의 의도를 파악해 대화할 수 있는 인공지능 기계다. 여기에는 '자연어 처리(Natural Language Processing, NLP)', '자연어 이해(Natural Language Understanding, NLU)', '머신러닝(Machine Learning)' 등 수많은 기술이 접목되어 발전 중이다. 현재 챗봇은 나날이 진화하며, 텍스트를 텍스트로만 처리하는 것을 넘어, '음성으로 변환(Text-To-Speech, TTS)'시키거나, '음성을 텍스트로 변환(Speech-To-Text)'시키는 등 다양성에 있어 점점 넓은 범위에 적용되고 있는 추세다.< 출처: Understanding Natural Language Understanding, Bill MacCartney >글로벌 챗봇 시장은 매년 큰 폭으로 성장하고 있는 추세이며, 여러 사업 분야에 걸쳐 사용되고 있다. 북미의 시장조사기관 'Credence Research' 조사에 따르면, 글로벌 챗봇 시장은 2015년부터 2023년까지 연평균 35% 성장할 것으로 예상된다. IT솔루션 기업 'MindBowser'가 조사한 결과, 95%의 기업이 챗봇 활용성에 대해 긍정적인 반응을 보였으며, 고객응대(93%)부터, 마케팅(61%), 상품 주문(47%), 소셜 미디어(32%) 등 사업 분야에서 활용되는 용도 역시 다양한 것으로 밝혀졌다.챗봇은 어떠한 프로세스를 통해 실제로 작동하는지 살펴보기 위해서 사내 엔지니어의 도움을 받았다. 스켈터랩스에서 대화형 인공지능 프로젝트팀에 있는 정태형 엔지니어가 메신저를 통한 간단한 시범 사례를 스크린샷으로 찍어 보여주었다.< 인공지능 메신저 사례, 출처: 스켈터랩스 >여행지를 자동으로 추천해주는 엡에 적용할 수 있는 챗봇과의 대화다. 사용자가 "여행을 가고 싶다"고 말하자 자동으로 '카이트봇'이 반응하고, 여행 기간과 테마를 물어본다. 여기서 사용자가 "여행 기간"을 말하자 챗봇은 자동으로 '3월'과 '7일'을 인식, 이전 질문에서 대답하지 않은 테마에 대해 질문한다. 이렇게 사용자와 챗봇 사이에서 대화를 자연스럽게 주고 받을 수 있는 것은 대화의 구성 요소 중 '의도(Intent)', '개체(Entity)', '맥락(Context)'이 중요한 역할을 한다. 이를 간단히 살펴보도록 하자.의도(Intent)는 사용자가 어떠한 의도로 대화를 하는지를 의미한다. 위 스크린샷의 경우, 여행을 가는 것'이 의도라 할 수 있다. 예를 들어, "여행 가고 싶어"가 아닌 "여행 가볼까?"로 입력하더라도 - 미리 여행을 가는 것에 대한 자연어 기반 패턴이 'Intent Classifier'에 입력되어 있는 상태라면 - 이를 '사용자가 여행을 가고 싶구나'라는 의도로 이해할 수 있는 것이다.개체(Entity)는 사용자의 의도 중에서 실체가 될 수 있는 변수를 말한다. 개체는 사용자가 입력한 문장에서 특정한 변수가 달라질 때 사용된다. 위 스크린샷의 경우, '3월 3일', '해변', '일주일' 등과 같이 주로 명사 형태로 구성된 문장에 들어가는 구성 요소를 말한다.문맥(Context)은 이전 대화를 자연스럽게 이어갈 수 있도록 처리할 수 있는 기능이다. 예를 들어, 사용자가 챗봇에게 "가수 빅뱅의 프로필을 검색해달라"고 요청했다. 그리고 빅뱅의 노래를 듣기 위해 "거짓말 틀어 줘"라고 명령하면, 기존에 빅뱅이라는 가수에 대해 대화하고 있던 문맥을 인식해 God의 거짓말이 아닌 빅뱅의 거짓말을 재생하는 것이다.이 외에도 챗봇에는 '말뭉치(Utterance)', '시나리오(Scenario)', '슬롯채우기(Slot Filling)' 등 다양한 구성요소를 통해 대화를 이어나갈 수 있다. 물론, 아직 100% 인간과 대화하는 기술까지 이르지는 못했다. 하지만, 우문현답하지 않고 사용자 의도를 정확하게 파악하는 수준에 이르러 생활에 편의성을 제공하고 있다.한국어의 경우 언어의 난이도 때문에 국내 기업은 물론 많은 글로벌 IT 기업도 아직 완벽한 수준에 도달하지 못했다. '잘 한다'라는 말만 하더라도 '훌륭하게 하다', '만족할 만하다', '자주 하다' 등의 긍정적인 표현이 있는가 하면, '잘 하는 짓이다' 등의 부정적인 표현인 경우도 흔하기 때문이다. 결국 챗봇도 기계이기 때문에, 여러가지 문장과 상황을 학습시켜 한국어 성능을 향상시켜야만 한다.다시 '어쩌다 어른'으로 돌아가보자. 강연을 마무리할 즈음 조승연 작가는 이렇게 말한다."영어도 결국 언어의 한 종류, 영어를 쓰는 사람들도 우리와 같은 사람, 우리처럼 희로애락을 느끼는 인간입니다. 기계와 얘기하기 위해 법칙에 맞춰 말해야 하는 것이 아니라 그 사람과 감정을 통하게 해주는 어떤 도구입니다."여전히 우리는 챗봇이라는 기계와 소통한다기 보다, 일방적으로 질문을 던지고, 챗봇은 미리 입력되어 있는 규칙 안에서만 답한다. 학습을 통해 수많은 데이터가 축적된다 하더라도, 아직까지 언어를 통해 전달되는 인간의 감정을 완벽히 이해하기에는 부족한 것이 사실이다. 과연 기계가 '법칙'에 맞춰서 말해야 하는 것 이상을 넘어서는 순간이 올까? 우리는 그 순간을 찾아 지금도 노력하고 있는지 모른다.이호진, 스켈터랩스 마케팅 매니저조원규 전 구글코리아 R&D총괄 사장을 주축으로 구글, 삼성, 카이스트 AI 랩 출신들로 구성된 인공지능 기술 기업 스켈터랩스에서 마케팅을 담당하고 있다#스켈터랩스 #기업문화 #인사이트 #경험공유 #조직문화 #인공지능기업 #기술기업
조회수 1145

[맛있는 인터뷰 1] 잔디의 든든한 리베로, 백엔드(Back-end) 개발자 John을 만나다

[맛있는 인터뷰 1] 잔디의 든든한 리베로, 백엔드(Back-end) 개발자 John을 만나다                                    잔디의 든든한 수문장, John         스타트업(Startup)의 경우, 구성원들과 회사가 그 운명을 같이하는 것 같다.         개개인의 발전이 곧 회사의 발전으로 이루어지기 때문이다.           – John Kang, 잔디 개발팀편집자 주: 잔디에는 현재 40명 가까운 구성원들이 일본, 대만, 한국 오피스에서 일하고 있습니다. 국적, 학력, 경험이 모두 다른 멤버들. 이들이 어떤 스토리를 갖고 잔디에 합류했는지, 잔디에서 무슨 일을하고 있는지 궁금해 하시는 분들이 많았습니다. 이에 잔디 블로그에서는 매주 1회 ‘맛있는 인터뷰’라는 인터뷰 시리즈로 기업용 사내 메신저 ‘잔디’를 만드는 사람들의 이야기를 다루고자 합니다. 인터뷰는 매주 선정된 인터뷰어와 인터뷰이가 1시간 동안 점심을 함께 하며 다양한 이야기를 나누며 진행됩니다. 인터뷰이에 대해 궁금한 점은 댓글 혹은 이메일([email protected])을 통해 문의 부탁드립니다.안녕하세요, John! 맛있는 인터뷰의 첫 대상자가 되셨어요. 오늘 저희가 먹을 ‘맛있는 메뉴’는 무엇인지 설명해주세요.– 생선구이 어떠세요? 고등어와 연어 요리가 맛있는 집이 국기원 쪽에 있는데요. 비즈니스 팀의 YJ가 버디런치*때 데리고 갔던 곳인데 테이스티로드에도 나오고 꽤 맛있어요.*버디런치(Buddy Lunch): 잔디에서는 매주 금요일 점심 제비뽑기를 통해 짝을 지어 점심을 먹는 버디런치를 실행 중이다                                맛있는 인터뷰 시작 전, 인증샷 한장~!자기소개 부탁드려요.– 잔디의 백엔드(Back-end)를 맡고 있는 John입니다. 잔디에 합류한 건 반년쯤 된 것 같네요. 2014년 9월에 합류했어요. 남중-남고-공대-군대-IT회사까지 소위 ‘솔로계의 엘리트 코스’를 밟고 있는 개발자입니다. 고향은 대구이구요, 서울말을 제 2외국어로 사용하고 있습니다. 회사에서는 서울말을 하고 있지만 고향 친구들을 만나면 자동으로 사투리가 나옵니다. (하하)잔디에는 어떻게 합류하시게 됐는지?– Justin(CTO)과 YB(COO)와 함께 패스트트랙에서 창업 관련 수업을 들었어요. 그때 Justin이 농담처럼 나중에 함께 일하자 했는데 정말 이렇게 부를 줄 몰랐네요.잔디의 어떤 점에 이끌리셨나요?– 잔디라는 서비스도 매력적이었고, 함께 일할 사람들도 매력적이었어요. 개발하면서 직접 만들어보면 재미있겠다고 생각을 한 것이 있었는데 잔디가 바로 그런 서비스였어요. 게다가 함께 일할 사람들이 너무 좋았어요. 프로덕트 아이디어도 중요하지만 함께 일할 동료도 정말 중요하다고 생각해요.  몇 년 전 사업을 구상했던 적이 있는데 아이템에 대한 이견차이로 결국 무산되었던 경험이 있어요. 그 당시 연애하다 헤어진 것과 맞먹는 상실을 겪었는데요. 이런 경험이 있다 보니 뜻이 맞는 동료들이 중요하구나를 뼈저리게 느꼈어요.잔디에서의 역할이 백엔드라 하셨는데 조금 더 자세히 설명해 주실래요?– 용어가 어렵죠? 제가 하는 백엔드 업무는 사용자가 직접 눈으로 보거나 경험하는 부분이 아닌 그 뒤의 처리 과정을 담당하는 일이에요.눈에 보이지 않는 부분이요?– 쉽게 말하면 잔디를 통해 메세지를 보내면 그게 끝이 아니거든요. 메세지를 서버에 저장하고 처리해서 받는 사람에게 잘 전달되도록 해야 해요 그걸 가능하게 만드는 거죠. 잔디에선 MK와 함께 일을 하고 있어요. 업무 특성상, 안드로이드 개발자, 아이폰 개발자와도 함께 일하고 있죠.성과가 눈에 잘 보이지 않는 업무인 것 같아요.– 사실 프론트엔드(Front-end)에 비해 그런 편이죠. 백엔드와 프론트엔드 업무를 모두 해봤는데 각기 장단점이 있어요. 백엔드는 성과가 잘 안 보이는 반면 프론트엔드는 누구나 오류를 지적 할 수 있거든요.둘 다 경험이 있다고 하셨는데 어떤 쪽이 더 재미있으세요?– 어렵네요. 백엔드를 하다 지칠 땐 프론트엔드가 생각나고 프론트엔드 일을 하다 지칠 땐 백엔드가 생각나요. 지금은 백엔드에 만족하고 있어요.지금 하고 계신 업무를 좋아하시는 것 같단 생각이 드네요.– 그래 보여요? 사실 적성에 맞는 것 같아요. 모든 일이 그렇겠지만 프로그래밍은 꾸준히 발전하지 않으면 도태되기 십상이에요. 그러다 보니 계속해서 공부하게 되는 것 같아요. 저뿐만 아니라 잔디의 다른 개발자 분들도 꾸준히 공부를 하고 있고 스터디도 열심히 참여하고 있어요.바쁜 가운데 꾸준히 공부를 하신다니 인상적이네요.– Startup의 경우 구성원들과 회사가 그 운명을 같이하는 것 같아요. 개개인의 발전이 곧 회사의 발전으로 이루어지니까요. 그러니 열심히 할 수밖에 없죠.                                 오피스 근처 커피숍에서 커피 한잔!취미가 있으시다면?– 몸으로 하는 활동을 즐겨서 하고 있어요. 헬스, 조깅, 윈드서핑을 좋아해요. 한동안은 등산도 즐겨했지만 친구들이 하나둘 결혼하고 나니.. 점점 모임이 뜸해지더라고요. 일을 하면서 체력관리는 필수인 것 같아요. 어릴 땐 몰랐지만 체력관리를 하지 않으면 자기도 모르는 사이 배가 조금씩 조금씩 나오는 것 같아서..주로 혼자 하는 운동들이네요.– 정말 그렇네요? 앞으로 여유가 생긴다면 다이빙이나 서핑, 암벽 등반을 해보고 싶어요. 그리고 가능할진 모르겠지만 올해 안에 휴가를 내서 발리에 가서 서핑도 즐겨보고 싶고, 돈을 많이 벌면 레이싱도 해보고 싶어요.시간이 벌써 이렇게 됐네요. 끝으로 레이싱 얘기가 나와서 여쭤보는데 혹시 드림카가 있으신가요?– 페라리요. 잔디가 성공해야 드림카를 소유할 수 있겠죠?1시간 동안 진행된 ‘맛있는 인터뷰’를 통해 좀 더 자세히 알게된 John. 이번 인터뷰를 음식에 비유하자면 진하고 담백한 사골국 같았습니다. 개발자로서의 자부심과 일에 대한 애정이 남다른 John을 보며 조금이나마 개발팀을 머리에 그려볼 수 있었습니다. 앞으로 매 주 진행될 잔디 멤버들과의 다른 인터뷰들도 기대해주세요!#토스랩 #잔디 #JANDI #개발자 #백엔드 #개발팀 #팀원소개 #팀원인터뷰 #팀원자랑 #조직문화 #기업문화 #사내문화
조회수 1395

AWS Rekognition + PHP를 이용한 이미지 분석 예제 (1/2)

OverviewAWS Rekognition은 딥 러닝 기반의 이미지, 동영상 분석 서비스입니다. Rekognition API를 사용하면 서비스에서 객체, 사람, 텍스트, 장면 및 동작을 식별하고 부적절한 콘텐츠를 탐지할 수 있습니다. Rekognition은 딥 러닝 기술을 기반으로 하고 있기 때문에 지금 이 순간에도 새로운 데이터를 통해 끊임없이 학습하고 있고, AWS에서도 새로운 레이블과 얼굴 인식 기능을 추가하고 관리합니다. 이번에는 AWS S3 Bucket에 업로드한 이미지로 이미지 분석 결과를 볼 수 있는 예제 사이트를 통해, Rekognition과 친해지는 시간을 갖도록 하겠습니다. 저는 예제 사이트를 개발하기 위해 PHP 프레임워크인 CodeIgniter 3, MAMP, Bootstrap을 사용했습니다.1. AWS Rekognition SDK 설치하기1-1) AWS Rekognition 사이트에 접속해 Download SDKs 를 클릭합니다.1-2) AWS 에서 제공하는 다양한 언어의 SDK를 확인할 수 있습니다. 저는 PHP를 사용할 것이므로 PHP 의 Install을 클릭하겠습니다.1-3) AWS SDK 를 설치할 수 있는 방법은 여러가지가 있습니다. 이 중에서 저는 Composer를 이용해 설치했습니다.curl -sS https://getcomposer.org/installer | php php -d memory_limit=-1 composer.phar require aws/aws-sdk-php 1-4) 짠! 짧은 명령어 2줄로 SDK 설치가 완료되었습니다 :)2. AWS S3 Bucket 에 업로드된 이미지를 분석하기2-1) 여기에 임의로 만든 예제 사이트가 있습니다. [이미지 선택] 과 [S3에 이미지 업로드하기] 를 통해 이미지 파일을 등록하면, 백단(Back-end) 에서는 해당 파일을 특정 S3 Bucket 에 업로드 한 후 Rekognition 에게 이미지 분석을 요청하도록 짜여있습니다. 관련 코드는 아래와 같습니다.{     "Image": {         "S3Object": {             "Bucket": "bucket",             "Name": "input.jpg"         }     },     "MaxLabels": 10,     "MinConfidence": 80 } 위의 코드 블록은 AWS Rekognition 개발자 안내서에 나와있는 예제 포맷이고, 아래의 코드는 예제 포맷을 PHP 에서 요청할 수 있는 방식으로 코딩한 것입니다.detectLabels 메소드 를 이용해 분석할 이미지가 저장되어 있는 S3 Bucket 과 이미지의 Name 을 전달해줍니다. 1) MaxLabels : 응답 받을 최대 Label 수 2) MinConfidence : Label 에 대한 최소 신뢰성 여기서 Label 이란 ‘이미지에서 발견되는 객체, 장면 또는 개념’ 이라고 생각하면 됩니다. 예를 들어 해변에 있는 사람들을 촬영한 사진에는 ‘사람’, ‘물’, ‘모래’ (객체) 및 ‘해변’ (장면) 그리고 ‘야외’ (개념) 등이 Label 이 될 수 있습니다. 자, 우주 사진을 한 번 분석해볼까요? array(3) {     ["Labels"]=>     array(8) {       [0]=>       array(2) {         ["Name"]=>         string(9) "Astronomy"         ["Confidence"]=>         float(96.8987350464)       }       [1]=>       array(2) {         ["Name"]=>         string(5) "Earth"         ["Confidence"]=>         float(96.8987350464)       }       [2]=>       array(2) {         ["Name"]=>         string(5) "Globe"         ["Confidence"]=>         float(96.8987350464)       }       [3]=>       array(2) {         ["Name"]=>         string(11) "Outer Space"         ["Confidence"]=>         float(96.8987350464)       } ...     } Rekognition이 업로드한 우주 사진을 분석하여 정확히 연관된 Label들만 반환한 것을 확인할 수 있습니다. 이 Label을 가지고 이미지 태그를 간단하게 구현했습니다.참 쉽죠 ?Conclusion이번 시간에는 AWS Rekognition 을 이용하여 기본적인 이미지 분석을 해보는 시간을 가져봤습니다. 다음 시간에는 ‘얼굴 감지 및 분석’ 기능을 응용하여 Collection 을 생성해보고, 얼굴 검색을 해보는 시간을 갖겠습니다. 참고놀라운 무료 이미지 · Pixabay핀터레스트 스타일 레이아웃 만들기 (masonry) - 생활코딩이미지에서 레이블 감지 - Amazon Rekognition글김우경 대리 | R&D 개발1팀[email protected]#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1476

AWS, Kubernetes 그리고 WAF

모니터링을 지속적으로 강화하다 보니 사용자 약관에 어긋나게 행동하는 주체가 눈에 띄기 시작한다. 특정 시간대에 판매 목록을 긁어가려고 시도하는 크롤러가 대표적이다. 비정상적인 서비스 이용을 탐지한 건 좋은데 이를 어떻게 차단할지가 또 고민이다. 차단 방법이야 많지만 가급적유지보수가 쉽고현재 서비스 구조에 살짝 얹기만 하면 되는그런 멋진 구조가 없을까 잠시 조사를 해보았다. 결론적으로는 현재 우리의 구조에선 1시간만 작업하고 펑펑 놀아도 되는 그런 방법은 없었다. 하지만 조금만 더 참고 기다리면 꽤 괜찮은 접근방법이 있을 것도 같더라. 우선 현 상황을 보자면 우리의 인프라는 주로Kubernetes가 서비스의 90% 이상을 통제하며웹 서비스는 주로 AWS ELB를 통해 인터넷 망에 노출한다.그러니 이론상으로는 AWS의 WAF, 그러니까 Web Application Firewall을 이용하면 손 안대고 코 풀기가 딱이다. 하지만 문제가 하나 있으니!!!AWS WAF는 ELBv2 그러니까 Application Load Balancer만 지원하는데 Kubernetes 1.5.x는 ELBv1만 지원한다. AWS WAF가 L4 로드밸런서인 ELBv1을 지원하던가 Kubernetes가 AWS ELBv2도 External Load Balancer로 선택가능하게 지원하던가 해야 Kubernetes + AWS ELB + WAF를 조합할 수 있다. 이 문제만 해결되면 금방 적용가능한 구성이라 매우 땡긴다. 설사 Kubernetes이 ELBv2는 지원하되 WAF 연동을 지원하지 않더라도 이를 수행하는 Kubernetes 플러그인을 개발하는 건 이틀이면 충분하지 싶다.왜 WAF인가?그러고 보니 여태 왜 이런 구성이 제일 낫다고 생각하는지 설명하지 않았다. 웹애플리케이션 방화벽을 구현하는 방법이야 AWS WAF 말고도 많지만 이러한 구성에는 분명한 장점이 있는데IP 평판 목록을 수집해서 한데 정리하는 서비스를 AWS가 제공하기 때문에 내가 이걸 구현한다고 시간낭비할 필요 없고매우 간단한 구조라서 처음 설치하고 설정하는데 30분에서 1시간이면 족하고Classless Inter-Domain Routing (CIDR) 표기법을 지원하므로 특정 아이피 대역을 막는 건 일도 아니며무엇보다 내가 관리하는 평판 목록도 쉽게 추가할 수 있다.이러니 “굳이 다른 솔루션을 찾아서 생고생해야 하나?”라는 생각이 들 수밖에 없다.다른 읽을꺼리How to Import IP Address Reputation Lists to Automatically Update AWS WAF IP Blacklists: AWS WAF의 구조와 WAF를 CloudFront에 적용하는 방법을 설명한다.AWS WAFがALB(Application Load Balancer)で利用出来るようになりました: AWS WAF를 ELBv2에 적용하는 방법을 설명한다.Akamai — Protect your organization with a web application firewall.Originally published at Andromeda Rabbit.#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트
조회수 2017

Interview - Android App Developer 박형일님

크래커나인 팀에서는 사용자의 의견을 적극 반영하기 위해서 안드로이드 개발자 박형일님의 크래커나인 사용기 인터뷰를 해보았습니다.개발자 박형일님본인 소개를 부탁 드려요~저는 에이치나인에서 안드로이드 개발 파트를 담당하고 있는 박형일 입니다. 개발 경력은 12년 정도 됐구요.에이치나인에 입사한지는 4년 정도 됐습니다. 주로 외주 안드로이드 앱 개발 업무를 하고 있습니다.개발일을 꽤 오래 하셨네요. 시니어 개발자들은 코드를 직접 작성하는 것을 선호 하시는 것 같던데, 형일님은 어떠신가요?저도 코드를 직접 작성하는 것을 선호 하는 편입니다. 툴에서 생성되는 코드를 별로 신뢰하지 않아요. 제가 원하는 대로 안 나온다는 느낌을 많이 받거든요.그럼 크래커나인의 첫인상은 어떠셨나요? GUI 로 부터 코드를 생성해 주는 툴인데..처음엔 아이디어는 괜찮은데, 이게 과연 제대로 된 코드를 생성 해 줄까? 라는 의심이 들었죠. 꼭 사용 해 보고 싶은 프로그램은 아니었어요.크래커나인을 사용하신지는 얼마나 되셨죠?올해 6월쯤에 처음 접하여 2달 정도 된 같아요.어떤 프로젝트에 적용 해 보셨나요?회사에서 회의실 예약 시스템이 필요하다고 해서 회의실 예약 앱을 만들었어요.그 때 처음 사용 했죠. 공식 프로젝트가 아니라 디자이너 없이 웹에서 무료로 사용할 수 있는 Sketch 파일로 된 디자인 샘플을 받아서 혼자서 만들었어요.앱을 구성하는 화면은 대략 5~6개 정도로 간단한 프로젝트여서 가능 했죠. 지금은 외주 과제를 하고 있는데, 크래커나인을 사용 해서 하고 있어요.외주 같으면 주로 파워포인트로 작성된 GUI 가이드라인 문서를 보고 개발 하잖아요. 크래커나인을 사용하기 전에는 디자이너와 무엇을 통해서 개발에 필요한 디자인 정보를 얻으셨어요? 에이치나인에 있으면서 거의 대부분 GUI 가이드라인 문서를 보고 개발 했죠. 최근에 다른 프로젝트 팀에서 GUI 가이드라인 문서 없이 제플린을 쓰더라구요. 그래서 그것도 조금 써봤어요.제플린과 같은 기존의 유사 서비스와 비교해서 크래커나인은 어땠나요?GUI 가이드라인 문서를 보면서 개발을 하다 제플린을 써보니까 너무 편리하더라구요. 문서에서 원하는 정보 찾는게 불편했거든요.그래서 사실 처음 크래커나인을 사용 했을 때는 제플린과 큰 차이를 못 느꼈어요.근데 프로젝트를 진행 하다 보니 크래커나인의 코드 생성 기능이 있어서 좀 더 편리하다고 느껴지는 순간이 오더라구요. 제플린을 보고 개발 할 때는 코드나 수치를 직접 입력하다 보니 실수를 할 때도 있었고, 여전히 XML 작성하는 수고로움이 남아 있었거든요.근데 크래커나인의 레이아웃 코드 생성 기능을 사용해서 나온 XML 코드가 100% 는 아니지만 70~80%는 작업을 안해도 될 수준이더라구요. 그래서 XML 작성하는데 들이는 시간이 많이 줄어 들었어요.크래커나인을 익히는데 어렵지는 않으셨어요?타 서비스를 사용한 경험이 있어서 많은 부분 기능을 따로 매뉴얼로 보지 않아도 파악이 됐는데요.사용하는데 크게 문제는 없었던 거 같아요. 직관적으로 파악이 안 되는 기능은 사용하지 못한 것 같지만 따로 매뉴얼은 찾아보지 않았습니다.Cracker9의 어떤 기능이 가장 편리하셨나요?당연히 레이아웃 코드 자동 생성 기능이었습니다.손으로 직접 코딩 하지 않고 View들의 관계를 맺으면 자동으로 View의 관계와 속성을 xml 코드로 생성해주는 기능이 개발하는 데에 상당히 많은 도움이 되었습니다.Cracker9을 사용하면서 불편했던 점이나 개선했으면 하는 부분이 있었나요?Asset 이름이나 리소스(문자열, drawable) 이름이 해쉬값이나 text1, text2 이런 식으로 되어 있어 나중에 다 변경을 해 주어야 되었습니다. 이 부분은 개선이 되었으면 좋겠습니다.※ 위의 내용은 Cracker9 0.9.5 에서 개선되었습니다.Cracker9의 정식 버전으로 출시가 되면 사용할 의향이 있으신가요?네, 사용할 것 같아요. 가격만 너무 비싸지 않으면 전 무조건 사용하겠습니다.App을 만들거나 디자이너와 소통해야 하는 다른 개발자들에게 알려주고 싶은 Cracker9 사용 Tip이 있다면 알려주세요~디자이너가 텍스트나 이미지 단위로 View를 만드는데요. 개발자가 원하는 모든 View를 만들진 않기 때문에 Custom Layout 활용은 필수입니다.Custom Layout을 잘 활용하시면 원하시는 레이아웃 작업이 모두 가능합니다. 그리고, 오른쪽에 Tree Structure 패널이 있는데요.View의 트리 구조 순서로 레이아웃 xml 코드가 생성되기 때문에 어떻게 코드가 만들어질지 예상할 수 있어요. 물론 View의 순서나 부모/자식 관계는 마우스 드래그로 편집할 수 있습니다.마지막으로 Constraint Layout의 관계를 맺을 때, View가 작으면 정확하게 클릭하여 작업하기 어려울 수 있는데요. 당연하지만 View를 확대해서 연결 지으면 쉽게 작업할 수 있습니다.마지막으로 Cracker9에게 한 말씀해주세요~저는 안드로이드 App을 개발하면서 레이아웃 작업은 초반에 하는 시간 잡아먹는 노가다 작업이라고 생각을 했는데요.레이아웃을 자동으로 생성해주는 Cracker9을 사용하면서 더 이상 노가다라는 생각을 하지 않게 되었습니다. 아직은 Beta 버전이라 부족한 부분이 보이지만, 개선될 것이라고 믿고 계속 사용할 거 같아요.앞으로 발전하는 모습 기대하겠습니다 파이팅!#에이치나인 #디자이너 #개발자 #협업툴 #크래커나인 #솔루션기업 #팀원인터뷰 #기업문화 #조직문화 #팀원자랑
조회수 1877

아키텍트, 개발 리더십의 변화...

보통, 하나의 서비스를 개발하는데 얼마나 걸리며, 그 시간 동안 어떤 일을 '구체적'으로 진행시켜야 하느냐에 따라서 아키텍팅의 관점이 변화된다.자주 쓰는 장표 중의 하나이다. 간단하게 설명하면 과거의 비즈니스와 현재의 비즈니스의 차이를 디지털 서비스로 만들어 내는 기간으로 표시한 것이다.과거에는 하나의 디지털 비즈니스가 동작하기 위해서 데이터를 수집하고 분석, 기획, 구현, 실행하기까지 대부분 8.5개월에서 10개월 정도의 시간이 소요되었고, 이렇게 만들어진 서비스들은 실제 고객과 단절되어 있는, 내부 시스템에 가까웠다는 것을 표현한다.그리고, 디지털 비즈니스의 세계에서는 모바일로 실 고객과 커넥티드 되어 있으며, 각 비즈니스가 실제 수집부터 실행까지 1주에 동작되는 세계를 표현한다.이 차이는 정말 개발 조직과 개발 리더십에 많은 차이를 주게 된다.Classic Business에서는 8개월 이상의 방향성이 흔들리지 않도록, 전체적인 방향성이 흐트러지지 않도록 개발 리더십을 발휘하는 것이 중요했다. 특히, 초기의 개발 조직을 세팅하고 예산과 비즈니스의 완성과 실 서비스 후의 이익과 같은 경영적인 판단이 더 중요하던 시기였기 때문에, 실제 소프트웨어를 만들어내는 관점은 디테일하고, 기능적인 것에 집중화된 상태로 개발 조직이 구성되고, 리더십도 그것을 최대한 끌어내는 것에 집중했다.또한, 내부적 조직의 문제로 일이 더디게 진행되거나, 품질이나 세부적인 문제를 쥐어짜거나, 어떻게든 일정을 맞추기 위해서 조정하는 조정자의 역할도 매우 큰 상태였다. 개발 리더십도 그런 관점에서 구성되었고, 기술적인 변화도 거의 없이 초기에 결정된 상태로 대부분 진행되었다.그런데, Digital Business의 세계로 넘어오면 이것은 완전 다른 구도를 가지게 된다.1주 단위의 개발 및 배포까지 매우 유연한 상태로 가동되고, 이 단위는 기술적 선택과 실패가 매우 빠르게 반복되는 것을 의미하게 되며, 개발 조직은 말 그대로 작게 세분화되고, 전체적인 방향성은 계속 유동적으로 변화하게 된다.24시간 내에 하나의 개념이 수립되고, 이를 배포까지 진행시키기 위한 매우 다양한 시도들을 선택할 수 있게 하며, 기획 조직과 개발 조직이 하나의 '지표'나 '시각화'된 장표를 보고 빠르게 판단하게 할 수 있다.매우 빠른 순간 판단이 중요하며, '몇 분'간격으로 회사의 운명을 결정할 수 있는 서비스의 론칭도 가능하게 한다.관리적인 방법은 DevOps의 자동화된 환경과, 세분화된 배포 권한, 기획자들과의 유기적인 환경들을 보다 효율적으로 운용할 수 있는 방법들에 대해서 개발 리더십은 고민하게 된다.어떻게 빠르게 일을 효과적으로 움직일 것이며, 빠른 판단을 할 수밖에 없다. 빠르게 변화하는 기술 스택을 더 잘 알고 있는 것은 개발 조직이기 때문에, 아키텍트나 개발 리더의 권한은 계속 실무자에 가깝게 내려가게 되는 것이 순리에 가깝다.현재 DevOps를 지향하고 있는 개발 조직에서 아키텍트가 지향하는 것은 크게 개념적으로 변화한 것은 없다. '고객과 비즈니스를 이해하는 개발'임에는 틀림없으나, 기존의 아키텍팅과 많이 달라진 것은 실시간 서비스에 대한 분석과 기획의 변화, 데이터 중심의 개발 구조의 시각화를 통해서 개발 조직을 통제한다기보다는, 개발 조직을 숨 쉬게 만드는 '심장'과 같은 역할을 하게 된다.마치, 비즈니스가 빨라지면, 심장도 빨리 뛰고, 비즈니스가 좀 수월해지면 호흡을 고를 수 있는 형태...현재의 아키텍트는 개발 조직의 '심장'과도 같아.속도와 박자, 전체적인 흐름을 중시하는 것이 현재의 아키텍트의 역할이다.건축가인 아키텍트들에게는 엄청난 규칙과 법칙, 책임의 범위가 상당하다. 하지만, 소프트웨어 아키텍트들에게는 그런 책임이 법적으로 제시되고 있지 못하고 있다. 보통 소프트웨어 아키텍트라고 한다면, 부정적인 환경에서 제대로된 소프트웨어를 만들 수 없기 때문에 부당한 개발환경을 담당할 가능성이 없다는...그래서, SI현장에서 아키텍트는 거의 나오지 않는다고 봐야 한다. 슬프지만. 그리고, 마지막으로... 아키텍트는 '직위'나 '권위'가 아니다. '롤'일뿐이다. 그뿐...
조회수 1652

[Tech Blog] PhantomJS를 Headless Chrome(Puppeteer)로 전환하며

버즈빌에서는 모바일 잠금화면에 내보내기 위한 광고 및 컨텐츠 이미지를 생성하기 위한 PhantomJS 렌더링 서버를 다수 운영하고 있습니다. 일반적으로 PhantomJS는 웹페이지 캡쳐에 많이 쓰이지만, 기본적으로 headless하게 웹페이지를 렌더링하고 캡쳐할 수 있다는 특성 때문에 동적인 이미지 생성에도 많이 활용됩니다. 버즈빌의 렌더링 서버는 200개 이상의 컨텐츠 프로바이더로부터 실시간으로 잠금화면 컨텐츠 이미지를 생성하고 있어 분당 수백 건의 이미지를 안정적으로 생성하는 것이 가능해야 합니다.  렌더링 서버의 스케일링 이슈를 해결하기 위해 버즈빌에서는 여러 대의 렌더링 서버를 둬서 횡적으로 확장을 함과 동시에, 개별 서버 내에서도 리소스 사용률을 높이기 위해 Ghost Town이라는 라이브러리를 작성해 PhantomJS 프로세스 풀을 구성하여 사용하고 있었습니다(Scaling PhantomJS With Ghost Town ) 한편, 시간이 지나면서 잠금화면에서 렌더링하는 이미지 템플릿의 종류가 다양해지고, emoji 및 여러 특수문자를 표현하기 위해 렌더링 서버에 여러 폰트(대표적으로 Noto Sans CJK)를 설치해야 하는 요구사항이 추가됐는데, PhantomJS에서 폰트 렌더링이 일관적이지 않은 문제가 발생했습니다. 동일한 템플릿이지만 폰트가 비일관적으로 렌더링되고 있는 모습 이 문제의 정확한 원인은 결국 찾지 못했지만 PhantomJS의 이슈였거나 시스템 상에 폰트가 시간이 지나면서 추가 설치됨에 따라 font cache가 서버마다 일관되지 않은 상태가 되었기 때문인 것으로 짐작하고 있습니다. 다른 워크로드와 마찬가지로 렌더링 서버도 최초에는 packer를 이용해 일관되게 이미지를 빌드하고 업데이트하려고 했지만, 자주 기능이 추가되거나 배포되는 서비스가 아니기에 서버를 오래 띄워놓고 수동으로 유지보수를 한 케이스들이 누적되어 더 이상 packer를 이용해 시스템이나 폰트를 최신 상태로 유지하는 것이 어려운 상태였습니다. 모든 눈꽃송이가 자세히 보면 조금씩 다르게 생겼다는 것에서 비롯된 snowflake, 즉 배포된 서버들이 시간이 지남에 따라 조금씩 다른 상태가 된 것입니다. 평소에는 문제가 없어 보이지만, 추가적인 확장성이 필요해 scale out을 하거나 새로운 템플릿을 개발해 배포를 하면 문제가 발생하는 상황이었습니다. 사실 더 큰 문제는 PhantomJS 프로젝트가 더 이상 관리되지 않는다는 점이었습니다. 2017년 Google Chrome 59버전부터 Headless Chrome이 내장되기 시작하였고, 곧바로 Node API인 puppeteer가 릴리즈 되어, 현시점에서 가장 많이 쓰이는 렌더링 엔진을 손쉽게 headless로 사용할 수 있는 환경이 되었습니다. 때문에 PhantomJS 관리자가 사실상의 중단을 선언하였고, 2018년에는 최초 개발자에 의해 프로젝트가 아카이브 되었습니다. 프로젝트가 업데이트되지 않는 것은 템플릿에 최신 CSS 스펙을 사용하지 못한다는 것을 의미하고, 버그 수정도 되지 않기에 어플리케이션의 유지보수가 굉장히 어려워짐을 의미합니다. 현재까지의 문제점을 정리하면 아래와 같습니다.  자주 배포되지 않는 서비스 특성으로 인한 서버들이 snowflake화 되는 현상(특히 폰트) PhantomJS의 개발 중단으로 인해 버그 픽스 및 최신 CSS 속성 사용이 어렵게 되고, 향후 유지보수나 새로운 템플릿 개발이 어려워짐  해결방안은 명확했습니다. 첫번째 문제를 해결하기 위해서는 어플리케이션과 폰트가 설치된 시스템을 통째로 컨테이너로 만들고, CI/CD 파이프라인을 통해 지속적으로 빌드하여 snowflake화 되지 않도록 하면 됩니다. 사실 최초에 packer를 이용해 AMI 이미지를 생성하도록 구성이 되어있었기에, 매 배포마다 AMI를 새로 생성하고 지속적으로 렌더링 서버를 배포하는 환경이기만 했으면 snowflake를 방지할 수 있었을 것입니다. 하지만 자주 기능이 추가되거나 배포되는 서비스가 아닌데다, AMI를 빌드하는 과정이 CI/CD에 통합돼 있지 않고 어플리케이션만 지속적으로 배포하는 환경이었기에 편의상 서버를 종료하지 않고 장기간 관리를 해 오게 되었고, packer로 새로운 AMI 이미지를 빌드하는 것이 어려워 졌습니다. 때문에 AMI 빌드를 통한 배포 대신, 이미 운영 중인 kubernetes 클러스터에 도커 컨테이너를 빌드해 immutable한 형상으로 배포하기로 결정하였습니다. 두번째 문제의 간단한 해결책은 PhantomJS를 puppeteer로 변경하는 것입니다. 이 부분은 생각보다 간단했습니다. 의도했는지는 알 수 없으나 puppeteer의 api는 PhantomJS와 꽤나 비슷합니다. drop-in replacement까진 아니지만, PhantomJS api 호출하는 부분만 살짝 바꿔주는 정도로 교체가 가능하였습니다. 물론 교체만 하였다고 해서 기존에 개발된 템플릿이 의도된 대로 출력되는 것을 보장하지는 않기에, 렌더링 서버가 렌더링하는 수많은 템플릿들을 PhantomJS와 puppeteer로 각각 출력하여 일일히 비교하는 작업이 필요했습니다. 어떤 템플릿이 어떤 인자를 필요로하며 의도된 출력 결과가 무엇인지에 대한 정의가 남아있지 않았기에 템플릿마다 샘플 케이스들을 생성하는 작업이 필요했습니다. 아직까지는 수동으로 결과를 비교해야하는 문제점이 있지만 적어도 직접 확인할 수 있는 것은 큰 도움이 되었습니다. 향후에는 자동화된 테스트 케이스를 구성하여 기능 개발이 좀 더 용이하도록 보완할 계획입니다. 결과는 만족스러웠습니다. 많은 경우 기존과 출력 결과가 달랐지만, 최신의 크롬 웹킷이 사용되면서 오히려 템플릿을 개발할 때 의도했던대로 CSS를 더 정확하게 렌더링하게 된 것이었습니다.  FROM node:10-slim RUN apt-get update && \ apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \ libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \ libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \ ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget unzip && \ wget https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64.deb && \ dpkg -i dumb-init_*.deb && rm -f dumb-init_*.deb && \ apt-get clean && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* RUN yarn global add [email protected] && yarn cache clean ENV NODE_PATH="/usr/local/share/.config/yarn/global/node_modules:${NODE_PATH}" RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser # Set language to UTF8 ENV LANG="C.UTF-8" RUN wget -P ~/fonttmp \ https://noto-website-2.storage.googleapis.com/pkgs/NotoSans-unhinted.zip \ https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip \ https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKkr-hinted.zip \ https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKtc-hinted.zip \ https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKsc-hinted.zip \ https://noto-website-2.storage.googleapis.com/pkgs/NotoColorEmoji-unhinted.zip \ && cd ~/fonttmp \ && unzip -o '*.zip' \ && mv *.*tf /usr/share/fonts \ && cd ~/ \ && rm -rf ~/fonttmp WORKDIR /app # Add user so we don't need --no-sandbox. RUN mkdir /screenshots && \ mkdir -p /home/pptruser/Downloads && \ mkdir -p /app/node_modules && \ chown -R pptruser:pptruser /home/pptruser && \ chown -R pptruser:pptruser /usr/local/share/.config/yarn/global/node_modules && \ chown -R pptruser:pptruser /screenshots && \ chown -R pptruser:pptruser /usr/share/fonts && \ chown -R pptruser:pptruser /app # Run everything after as non-privileged user. USER pptruser RUN fc-cache -f -v COPY --chown=pptruser:pptruser package*.json /app/ RUN npm install && \ npm cache clean --force COPY --chown=pptruser:pptruser . /app/ ENTRYPOINT ["dumb-init", "--"] CMD ["npm", "start"]  puppeteer를 사용하면서 약간의 권한 문제가 있어서 결과적으로 위와 같은 Dockerfile을 작성하게 되었는데, puppeteer 도커 이미지 작성에 관한 최신 정보는 여기서 확인할 수 있습니다. 컨테이너 오케스트레이션(K8s)을 사용하면 process 기반의 스케일링은 컨테이너를 여러대 띄워 로드밸런싱을 손쉽게 할 수 있지만, 개별 컨테이너의 throughput을 향상시키기 위해 기존에 Ghost town을 작성해 PhantomJS 프로세스 풀을 만든 것처럼 크롬 프로세스 풀을 구성하기로 하였습니다. 프로세스 풀 구성에는 generic-pool 라이브러리를 사용하였으며 아래처럼 구성하였습니다.  const puppeteer = require("puppeteer"); const genericPool = require("generic-pool"); const puppeteerArgs = ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"]; const createPuppeteerPool = ({ max = 5, min = 2, maxUses = 50, initialUseCountRand = 5, testOnBorrow = true, validator = () => Promise.resolve(true), idleTimeoutMillis = 30000, ...otherConfig } = {}) => { const factory = { create: async () => { const browser = await puppeteer.launch({ headless: true, args: puppeteerArgs }); browser.useCount = parseInt(Math.random() * initialUseCountRand); return browser; }, destroy: (browser) => { browser.close(); }, validate: (browser) => { return validator(browser) .then(valid => Promise.resolve(valid && (maxUses <= 0 || browser.useCount < maxUses xss=removed xss=removed xss=removed> genericAcquire().then(browser => { browser.useCount += 1; return browser; }); pool.use = (fn) => { let resource; return pool.acquire() .then(r => { resource = r; return resource; }) .then(fn) .then((result) => { pool.release(resource); return result; }, (err) => { pool.release(resource); throw err; }); }; return pool; }; module.exports = createPuppeteerPool;  Caveats PhantomJS에서 puppeteer로 전환함에 있어서 몇가지 주의해야 할 점이 있었는데요. 첫째는 기존에 사용하던 템플릿의 html에 이미지 소스를 file:// url 프로토콜을 이용해 로드하는 경우가 있었는데, PhantomJS에서는 정상적으로 로드가 되지만 Headless Chrome에서는 보안 정책으로 인해 로컬 파일을 로드할 수 없었습니다(관련 이슈). 때문에 로컬 이미지가 필요한 템플릿은 Express 서버에서 static file serving을 하도록 하고 http:// 프로토콜로 변경하였습니다. 다음으로 발생한 문제는 PhantomJS을 이용한 기존 구현에서는 jade template을 compile한 후 page 객체의 setContent 메소드를 이용해 html을 로드하였는데, puppeteer에서는 page#setContent API 호출 시 외부 이미지가 로드될 때까지 기다리지 않는다는 점입니다. puppeteer 에 올라온 관련 이슈에서는 `=setContent`= 대신 아래와 같이 html content를 data URI로 표현하고 page#goto의 인자로 넘기면서 waitUntil 옵션을 주는 방식을 해결방법으로 권하고 있습니다.  await page.goto(`data:text/html,${html}`, { waitUntil: 'networkidle0' });  이 때 주의해야 할 점은 waitUntil의 옵션으로 networkidle0이나 networkidle2 등을 사용하면 외부 이미지가 충분히 로드될 때 까지 기다리는 것은 맞지만, 500ms 이내에 추가적인 네트워크 커넥션이 발생하지 않을 때까지 기다리는 옵션이기 때문에 외부 이미지가 로드되더라도 추가적으로 500ms를 기다리게 됩니다. 때문에 SPA 웹페이지를 캡쳐하는 경우가 아니라 정적인 html을 로드하는 경우라면 `load` 이벤트로 지정하면 됩니다. 이외에도 향후에 프로젝트의 유지관리나 운영 중인 서비스의 모니터링을 위해 Metrics API 엔드포인트를 만들어 prometheus에서 메트릭을 수집할 수 있도록 하고 grafana 대시보드를 구성하였습니다. 이 대시보드는 어떤 템플릿이 실제로 사용되고 있는지, 템플릿 렌더링에 시간이 얼마나 소요되는지 등을 모니터링할 수 있도록 구성하여 사용되지 않고 있는 템플릿을 판단하거나 서비스 지표를 모니터링 하는 데 이용하고 있습니다. grafana와 prometheus를 이용해 구현한 렌더링 서버 모니터링 대시보드. 마치며 최근에 들어서는 PhantomJS를 사용하던 많은 곳에서 puppeteer로의 전환을 해오고 있어 본 포스팅에서 다루고 있는 내용이 크게 새로운 내용은 아닐 수 있습니다. 하지만 버즈빌에서는 렌더링 서버가 과거에 이미 PhantomJS를 사용하는 것을 전제로 상당한 최적화가 진행되어 왔고, 꽤나 높은 동시 처리량이 요구되는 상황에서 puppeteer로 교체를 해버리기에는 여러 불확실한 요소들이 존재하는 상황이었습니다. 버즈빌의 핵심 비즈니스 중 하나인 잠금화면에 사용되는 이미지를 렌더링하는 서비스가 레거시(개발이 중단된 PhantomJS)에 의존하는 코드베이스 때문에 변경이 어려워지는 것은 향후 꽤나 큰 기술부채로 작용할 것이라 판단하였습니다. 이번 마이그레이션을 진행하면서는 이 부분을 염두에 두고 컨테이너를 사용해 CI/CD 파이프라인을 구축해 지속적으로 컨테이너 기반의 이미지를 생성하도록 변경하였고, 그 결과는 꽤나 만족스러웠습니다. 마이그레이션 이후 그간 밀려 있던 신규 템플릿 개발이나 신규 컨텐츠 프로바이더를 추가하는 과정이 수월해졌기 때문입니다. 빠르게 변화하는 비즈니스 요구사항에 대응하다보면 기술부채는 필연적으로 쌓일 수밖에 없습니다. 개발자에게는 당연히 눈에 보이는 모든 기술부채들을 청산하고 싶은 욕구가 있지만 늘 빚 갚는데 시간을 쓰고 있을 수만은 없는 노릇입니다. 리소스에는 한계가 있으니까요. 어떤 기술부채를 지금 당장 해결해야하는지 의사결정을 하는데 있어 고민이 된다면 일단 “측정”을 해보는 것을 권장합니다. 수치화된 지표가 있다면 당장 의사결정권자나 팀을 설득하는 데 사용할 수도 있지만, 서비스의 핵심 지표들을 하나 둘씩 모니터링 해나가다 보면 서비스에 대한 가시성이 높아지고 미래에 정말로 병목이 되는 지점을 찾아내기 쉬워질 것입니다. 참고 자료  https://docs.browserless.io/blog/2018/06/04/puppeteer-best-practices.html https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md Icons made by Freepik from Flaticon is licensed by Creative Commons BY 3.0    *버즈빌에서 개발자를 채용 중입니다. (전문연구요원 포함)작가소개 Liam Hwang, Software Engineer 버즈빌에서 DevOps를 담당하고 있습니다. Cloud Native 인프라를 구현하기 위해 여러 노력을 기울이고 있으며 새로운 기술들을 공부하는 것을 좋아합니다.
조회수 2143

왜 차세대 SaaS는 페이스북처럼 될 것인가.  

사람들이 매일 사용하는 서비스 중 가장 유용한 것은 무엇일까?대부분의 경우에 있어, 그것은 Slack, Gmail 혹은 Excel 같은 SaaS 툴이 아닐 것이다. 그것은 바로 페이스북이다.페이스북으로 할 수 있는 모든 것들에 대해 생각해보자.친구들에게 메시지 보내기 영상 통화 하기 뉴스 보기 이벤트 기획하기 사진과 동영상 공유하기사람들은 페이스북에 얼마나 많이 의지하고 있는 지 종종 잊어버리지만, 페이스북은 이미 우리의 일상 생활에 아주 깊숙이 자리잡고 있다. 오늘날에는 수 백만 개의 서비스가 존재하지만, 그들은 그럼에도 만족할 줄을 모른다. 그리고 페이스북은 SaaS 회사들이 할 필요가 있는 것들을 정확히 집어서 하고 있다.On-premise(인하우스 서비스)에서 SaaS(클라우드 컴퓨팅)로SaaS는 “Software as a Service.” 의 약자이다. 페이스북은 사실 기술적으로 SaaS라기 보다는, 일종의 소비자 네트워크 서비스라고 할 수 있다. 하지만 페이스북만큼 많은 서비스를 제공하는 곳은 존재하지 않는다. 페이스북이 이렇게까지 성공한 것은 그 서비스 내에서 유저들의 이용률을 크게 늘렸기 때문이다. 다른 SaaS 기업들은 이 부분을 더 신경 써야 될 필요가 있다. 이용률이야말로 지금 SaaS 비즈니스의 생존에 있어 그 어느 때보다 중요하기 때문이다.그 이유는 다음과 같다. 예전에, 소프트웨어는 회사의 컴퓨터 네트워크에 실제 물리적으로 깔려야만 했다. 소프트웨어 판매업자들은 대기업에 라이선스를 팔기도 했고, 그런 기업들은 해당 소프트웨어 이용을 위해 Accenture나 CSC 같은 회사에 돈을 지불하기도 했다. 당시 판매업자들은 라이선스를 많이 팔기만을 원했지, 얼마나 많은 사람들이 그 소프트웨어를 쓸 지에 대해선 관심이 없었던 것이다.그리고 1999년, Salesforce의 공동 창업자인 Marc Benioff는 새로운 모델을 소개하며 다음과 같이 말했다.“설치하는 데만 수 개월이 걸리고 하드웨어와 네트워킹에 엄청난 투자를 요구하는 비싼 CD-ROM 소프트웨어를 기업들에게 파느니, 우리는 클라우드 컴퓨팅이라고 알려진 모델을 통해 Software-as-a-Service(SaaS)를 팔기로 했다. 기업들은 이제 유저의 수에 맞춰 서비스를 이용한 만큼 비용을 지불해야 할 것이고, 그런 서비스들은 인터넷, 클라우드를 통해 즉시 제공될 것이다.”구독 기반(subscription-based) 소프트웨어는 회사 내부의 데이터 센터가 아닌 웹 브라우저를 통해 제공된다. 이는 소프트웨어 개발자로 하여금 언제든지, 즉각적으로 그들의 고객에 접근할 수 있게 해주었다. 어느 순간, 유저를 만족시키는 일은 CIO(Chief Information Officer)나 시스템 통합업체의 책임이 아니게 된 것이다. 그 일은 이제 소프트웨어 판매업자가 하게 되었다.이러한 클라우드 컴퓨팅 방식은 SaaS 소프트웨어로 하여금 생존을 위해 끊임없이 자신들의 가치를 어필하게끔 만든다. 그리고 SaaS 회사들은 계속해서 자신들의 소프트웨어를 이용하는 소비자들을 확보하기 위해 많은 양의 돈을 쓰고 있다. 이는 과거 기업 고객들에게 소프트웨어 라이선스를 팔러 다니던 때와는 180도 달라진 상황인 것이다. 오늘날의 SaaS 회사들은 예전처럼 높으신 몇몇 분들을 만나 무언가를 사라고 설득할 필요가 없다. 그저 이용자들이 자신들의 제품을 계속 사용하게끔 유도하면 되는 것이다.페이스북은 SaaS의 새로운 모델이다이제 페이스북을 한 번 살펴보자. 페이스북은 클라우딩를 통해 지속적으로 서비스를 제공한다. 그들은 광고를 통해 돈을 벌기 때문에, 그들의 가장 중요한 목표는 사람들로 하여금 계속 서비스를 이용하게 하는 데 있다. CIO들을 만나서 큰 계약을 체결하는 데 시간을 쓸 바에야 그 100분의 1초도 안 되는 시간에 12억 명의 사람들에 서비스를 파는 것이 더 낫다는 것이다.페이스북이 딱 한 가지 신경 써야 될 것이 있다면 그것은 사람들이 지금보다 더 적극적으로 페이스북을 이용하게끔 만드는 것에 있다.“우리의 최우선 목표는 모바일 장치나 개인용 컴퓨터를 통해 사람들을 연결시켜주고 공유하게끔 하는 유용하고 매력적인 서비스를 창조하는 것에 있습니다.” – 미국증권협회 기업정보 페이지의 페이스북 파일에서페이스북이 사람들의 관심을 많이 받을수록, 그들은 더 많은 광고를 사람들에게 보여줄 수 있다. 페이스북에게 있어서, 그러한 관심은 아주 중요한 것이다. 더 많은 관심을 받는 다는 것은 더 많은 성장과 확장의 기회를 갖는 다는 것을 의미하기 때문이다. 이것은 드롭박스나 Slack과 같이 바텀업 방식으로 성장한 SaaS 기업들이 새겨들어야 할 점이다. 유저들이 서비스를 쓰는 시간이 많아진다면, 앞으로 그들에게 더 많은 다른 서비스를 쓰게 만들 수 있기 때문이다.앞으로 페이스북이 더 성장하고 발전하려면 유저의 관심이 필요하다. 그래야 여러 방면에서 이용률을 늘릴 수 있는 방법을 찾을 수 있기 때문이다. 이제 여기서 페이스북이 그들 서비스의 이용률과 성장을 이뤄낸 3가지 방법에 대해서 소개해 보도록 하겠다. 모든 SaaS 기업들은 비슷한 방법으로 자신들의 이용률과 성장을 이뤄낼 수 있을 것이다.페이스북은 이용률을 측정하여 현재 운영하는 서비스를 최적화 시켰다페이스북은 이용률을 늘리기 위해 새로운 서비스를 내놓는다페이스북은 다른 앱들과 통합하는 과정을 거쳤기 때문에 페이스북을 쓰지 않는 사람들조차 페이스북을 쓰게 되었다페이스북이 이용률을 어떻게 늘렸는지에 대해 좀 더 깊이 이야기해 보도록 하겠다. 그러고 나면 페이스북의 노하우를 다른 SaaS에 어떻게 적용할 수 있을 지 분명하게 보여줄 수 있을 것이다.이용률 측정을 통해 서비스의 최적화를 이뤄낸다지금 사람들이 어떻게 서비스를 이용하고 있는 지 모르고 있다면 그들에게 당신의 서비스를 사용하게 만들 수도 없을 것이다. 페이스북은 이용률을 늘리는 방법에 대해 집요하게 연구해왔기 때문에 좋은 사례로 들기에 적합하다.핵심은 사람들이 지금 하고 있는 것, 그리고 그들이 원하는 것을 정확하게 아는 것에 있다. 페이스북은 단순히 월 이용자 수나 일 이용자 수를 알아보려 애쓰지 않는다. 왜냐하면 그런 수치들은 사용자들이 그 서비스를 통해 무엇을 하는지를 전혀 설명하지 못하기 때문이다. 대신 페이스북은 서비스 이용의 질적인 부분에 집중한다. 사람들이 페이스북을 통해 무엇을 이루려고 하는 지와 그들이 실제로 그렇게 할 수 있는 지에 대해서 말이다.이 부분에 있어 페이스북의 대표적인 전략 중 하나가 바로 10일안에 친구 7명 만들기이다. 일찍이, 페이스북은 10일안에 7명의 친구를 만드는 사람은 페이스북을 계속 사용할 확률이 훨씬 더 높다는 사실을 알게 되었다. 일단 이것을 알게 되자, 그들은 신규 유저들이 7명의 친구를 만날 수 있게 하기 위해 가진 모든 수단을 쓸 수 있게 된 것이다.바로 지금도, 페이스북은 새로운 친구를 추가할 것을 사람들에게 계속해서 권장한다. 왜냐하면 이것이야 말로 네트워크를 이루는 데 있어서 가장 가치 있는 부분이기 때문이다.페이스북 계정을 만들자마자, 유저들은 뉴스 피드 상단에 새로운 친구를 추가하시겠냐는 메시지가 뜨는 것을 볼 수 있다.아래 사진은 유저들이 다른 페이지를 둘러 보는 동안 뜨는 사이드바인데, 보다시피 그들이 알 수 있을 법한 사람들을 친구로 추가하게끔 권장하고 있다.또한 페이스북은 뉴스 피드와 같이 그 기능을 최대한 활용하기 위해 더 많은 친구들을 추가할 것을 권장하고 있다.페이스북은 이런 전략을 앞으로도 고수할 것이다. 2017년, 페이스북은 “Discover people” 이라는 새로운 기능을 출시했다. 이는 당신으로 하여금 프로필을 업데이트 하게끔 유도하고 기존에 친구가 아니더라도 같은 이벤트에 참여하는 경우 서로를 연결시켜 준다.페이스북은 사람들이 자신들의 서비스를 계속 이용하게 만들기 위해 기나긴 세월 동안 노력해왔고 앞으로도 그럴 것이다. 그들은 친구 최적화를 빠르게 해줄 뿐만 아니라 흥미를 잃은 사람들도 쉽게 다시 돌아올 수 있도록 여러 요인들을 제공해준다. 페이스북의 성장 전담 부서를 이끌고 있는 Chamath Palihapitiya은 “당장의 단기적인 이익에만 집중하지 않기 위해서는 절제력이 필요하다.” 라고 말한다. 페이스북은 초창기부터 무엇보다 사람들의 이용률이야말로 그들의 성패를 좌우한다는 것을 알고 있었다. 사람들의 주된 목표를 파악해서 이용률을 장기적으로 늘리는 것이 그들의 제1과제 였던 것이다.Trello는 어떻게 유저들이 쉽게 직장 동료를 추가하도록 만들었는가페이스북과 똑같이, Trello는 유저들이 무엇을 하는지를 이해하고 그들이 원하는 걸 더 많이 하게 도와주는 방식으로 이용률을 올렸다. Trello의 핵심적인 가치는 사람들이 프로젝트를 협력하게끔 만드는 것이었기 때문에, 그들이 그렇게 하도록 도움을 줘서 자신들 서비스의 가치를 보여줘야 했다.그래서 Trello가 직장 동료를 추가하는 방식은 놀라울 정도로 쉽게 되어 있다. 이는 페이스북이 친구를 추가하는 방식과 정확히 똑같다. 페이스북이 사람들로 하여금 쉽게 친구를 추가하게 하여 소셜 네트워크의 가치를 입증했다면, Trello는 쉽게 동료들을 추가하게 하여 프로젝트 협업 툴로써의 가치를 입증했다.Trello는 유저들로 하여금 이름이나 이메일 주소로 아는 사람들을 등록할 수 있게 만들었다. 유저들은 코드나, ID, 링크 같은 것 없이도 사람들을 쉽게 추가할 수 있다. 심지어 다른 사람들이 Trello를 사용하는지도 알 필요가 없다. 어찌 됐든 Trello를 통해 사람들을 찾아보고 확인해 볼 수 있는 것이다.또 만약 Trello를 한 번이라도 썼던 사람이라면 더욱 쉽게 목록에 추가할 수 있다.이런 방식을 통해 이용자들은 아무런 마찰 없이 많은 동료, 협력자들을 통해 프로젝트를 공유할 수 있다. 즉, Trello의 핵심 가치를 이루게 되는 것이다. 이는 사람들에게 Trello가 얼마나 유용한 서비스인지를 빠르고 쉽게 이해시켰다. 또한 이는 더 많은 사람들이 더 많은 프로젝트를 하게끔 유도했고, 결국 모두가 Trello를 더 많이 이용하게 되었다.Slack은 어떻게 이용률을 늘려왔는가이렇게 사용자의 이용률에 집중해서 성장을 이루고 있는 유명한 SaaS 기업이 또 하나 더 있다. Slack이 바로 그 기업인데, Slack은 메시지를 매끄럽게 전송하는 역할 하나에만 전념하고 있다.Slack은 자신들의 서비스를 이용해 2000개 이상의 메시지를 보낸 적 있는 팀들은 Slack의 가치를 알고 있기 때문에 앞으로도 계속 서비스를 사용할 것이라고 예측한다. 왜냐하면 Slack의 통계에 따르면, 다른 요소들이 어떻든 간에, 2000개 이상의 메시지를 보낸 팀들 중 93%가 지금까지도 Slack을 사용하고 있기 때문이다. 그래서 이용률을 늘리기 위해선, 메시지를 보내는 것을 더 쉽게 만들어야 하는 것이다. Slack의 공동 창업자인 Stewart Butterfield 역시도 이 목표를 위해 사람들이 실제로 어떻게 Slack을 쓰고 있는가에 대해서 생각해보았다.“처음으로 Slack을 쓰려고 온 사람이 되었다고 생각해 보는 겁니다. 특히 진짜 사회생활을 하는 사람들 말이죠. 상사에게 Slack을 쓰라고 해서 쓰게 된 사람, 아침 먹을 시간도 없어서 짜증이 난 사람, 주말이 오기 전에 프로젝트를 끝낼 수 있을지 걱정하는 사람… Slack을 면밀히 살펴봐서, 이런 사람들에게 먹히지 않을 것 같은 요소들을 생각해 내는 겁니다. 냉정하게 보는 거에요. 최고의 서비스를 주기 위해서 말이죠.”Slack은 메시지 전송에 따르는 불편함을 개선하면서 이용률을 늘려왔다. 그러한 개선의 예를 들어 보자면, 누군가가 Slack에서 링크를 걸었다고 했을 때, Slack은 그 링크에 대한 간단한 정보를 미리 보여준다. 즉, 사람들은 링크를 보려고 앱에서 빠져나와야 될 필요가 없는 것이다. 나중에 다시 그것을 확인해보기도 편하고 말이다.이런 시스템상의 개선점들이 Slack을 성장하게 만들었다. 메시지를 보내는 것에 있어서 사람들이 원하는 부분을 아주 쉽게 할 수 있게 만들었기 때문이다.이렇듯 페이스북, Trello, Slack은 모두 실제 이용자들이 원하는 것을 이해하고 그들이 그것을 쉽고 빠르게 할 수 있는 서비스를 제공하고 있다. 아래에 이런 SaaS 기업들이 어떻게 자신들의 서비스를 통해 이용자들에게 도움을 줬는지 요약해보았다.페이스북의 10일안에 친구 7명 만들기, Slack의 2000개 이상의 메시지 보내기, Dropbox의 파일 한 개 업로드 하기 등과 같이 그들은 수치로 표시되는 목표를 세웠다. 이러한 목표는 당신의 팀으로 하여금 무엇이 가장 이용률을 끌어오는데 중요한 지를 확인시켜줄 뿐만 아니라 그들에게 목표 달성을 위한 구체적인 숫자를 알려준다.핵심적인 기능들을 사람들이 이용하게 하려면 그것을 직관적으로 만들어야 한다. Raymond Loewy(미국의 전설적인 산업 디자이너)에 따르면, 성공적인 서비스는 사람들이 당장 사용하기에 편해야 한다고 한다. 예를 들어, 페이스북이 처음 “On this day” 서비스를 도입한 것은 유저들로 하여금 무언가 새로운 것을 하는 걸 권하기 위해서였다. 하지만, 이 서비스는 여전히 유저들에게 친숙한 태그, 공유하기 기능들을 사용하고 있다.유저들의 참여를 막을 만한 요소들을 찾아서 없애야 한다. 사용자들이나 얼리 엑세스 베타 테스터 등과 이야기를 해봐서 무엇이 서비스에 있어 가장 짜증나는 요소인지 알아내야 한다. “이거 어떻게 하는 건지 모르겠어요” 라던가 “이게 좀 쉽게 됐으면 하는데…” 와 같은 불만들에 귀기울여야 한다. 이런 장애물들을 제거하면 유저들이 서비스를 이해하기 더 쉽고 그 서비스의 가치를 파악하는 것 역시 쉬워진다.즉, 현재 가지고 있는 서비스 내에서 이용률을 끌어올리려면 유저들에게 무엇이 가장 도움이 되고 의미가 있는지 파악하는 것이 가장 중요하다고 할 수 있다.이용률을 늘리기 위해 서비스를 추가한다이용률을 끌어올린다는 것은 단순히 사람들로 하여금 기존의 서비스를 계속 쓰게 만드는 것 만을 의미하지는 않는다. 당신은 끊임없이 실험을 해보고 새로운 서비스를 제공해서 유저들이 서비스를 통해 더 많은 것들을 얻을 수 있도록 해야 한다.페이스북은 기존에 그들이 가진 서비스가 수명이 다할 것을 걱정해서 계속 실험을 하고 이용자들이 앞으로 무엇을 원할지를 예상해왔다.페이스북의 직원 가이드북을 보면, 새로운 직원들은 그들의 팀이 계속 새로운 생각을 하게끔 자극 할 것을 권장하고 있다.그 결과, 페이스북은 끊임없이 혁신하고, 또 그만큼 실패를 경험하고 있다.페이스북은 스냅챗으로부터 이용자들을 뺏어오기 위해 2012년 별도의 앱인 Poke를 출시한다. 그런데 이 앱은 대실패작이 되었고 페이스북은 얼마 지나지 않아 앱스토어에서 이 앱을 삭제하게 되었다.2014년에 페이스북은 이용률을 늘리기 위한 일환으로 슬링샷이라는 앱을 출시했다. 이 앱은 사진과 함께 메시지를 보내면 스냅챗과 같이 몇 초안에 사라지는 것이 특징인데 불과 1년만인 2015년에 앱스토어에서 내려가게 되었다.또 페이스북은 2016년 Quick Update라는 것을 시도했다. 이는 스냅챗과 비슷한 기능을 페이스북 앱에 추가시키는 것이었는데, 이런 기능을 유저들을 대상으로 그룹테스트 해 본 결과 반응이 좋지 않아 결국 공식적으로는 출시되지 못하게 되었다.이런 좋지 않은 결과들은 페이스북이 혁신에서 실패하고 있다는 소문을 자아냈다. Jason Calacains 같은 논평가는 이에 대해 “페이스북의 앱 플랫폼은 망하기 위해서 혁신을 하는 것인가?” 라고 하기도 했다.하지만 페이스북의 이런 계속되는 시도는 결국 그들을 새로운 기회로 인도했다. 그들은 스냅챗의 스토리 기능을 페이스북과 인스타그램에 도입하려고 시도해 왔는데 이 과정에서 마침내 페이스북 라이브라는 새로운 서비스를 만들어냈다. 이 서비스는 대히트를 쳤고, 이제 회사, 미디어, 그리고 유명인사들까지 모두 페이스북의 라이브 스토리를 사용하고 있다.이렇듯 페이스북이 큰 성공을 거둘 수 있었던 이유는 그만큼 실패도 많이 해봤기 때문이다. 그들은 그저 사람들이 관심 가질 만한 새로운 무언가를 계속 만드는데 집중할 뿐이다. 왜냐하면 이런 시도야말로 궁극적으로 이용률을 더 많이 올릴 수 있는 방법이기 때문이다.드롭박스 역시 이용률을 높게 유지하기 위해 새로운 서비스를 만들고 있다SaaS 기업들은 현재의 서비스보다 한 걸음 더 앞선 서비스 제공을 통해 이용률을 끌어올릴 수 있다. 그들은 지금 하는 것 이외에 이용자들이 무엇을 더 원하고 더 신경 쓸까를 생각해 볼 필요가 있다.그 예로 드롭박스의 드롭박스 페이퍼를 들 수 있다. 드롭박스는 원래 파일 공유 서비스였다. 하지만 오늘날, 드롭박스는 파일을 공유하는데 있어 다양한 방법을 제공해준다. 만약 드롭박스가 처음 서비스 이외에 유저들이 뭘 더 원할 것인 지를 생각해보지 않았다면 결국 이용률을 올릴 방법이 바닥나서 망하게 됐을 것이다.즉 드롭박스는 단순한 파일 공유 서비스에서 사람들이 함께 일하는 걸 더 쉽게 만들어 주는 일종의 팀 협업 툴로 자신들의 브랜드를 쇄신한 것이다. 이러한 재브랜딩 과정과 함께, 드롭박스는 2015년에 “창조적인 업무를 위한 새로운 형태의 파일 편집 툴” 이라는 신규 서비스인 드롭박스 페이퍼를 런칭했다.드롭박스 페이퍼는 단순히 문서와 파일을 저장하는 데 드롭박스를 쓰는 것이 아니라, 이제 문서와 파일을 만드는 데에도 드롭박스를 쓸 수 있게 만들어 주었다. 드롭박스 페이퍼는 사람들이 더 많이 서비스를 이용하게 만들었는데, 이는 파일 공유를 넘어 사람들간의 협업을 더 쉽게 해준다는 추가적인 옵션을 제공해줬기 때문이다.드롭박스가 이렇게 새로운 서비스를 만들려는 이유는 생존하기 위해서이다. 이 산업에 있어 망하는 일은 너무나 쉽게 일어나기 때문이다. Intercom의 Des Traynor는 다음과 같이 이를 설명한다.“원래 이쪽 산업이란 게 이런 겁니다, 기술이란 것의 특성 자체가 이런 것이죠. 모든 서비스가 결국 다 죽어 없어지게 되어있습니다. 만약 내 말이 사실이 아니라고 생각한다면 저에게 그렇지 않은 경우를 알려주세요. 한때는 SaaS 비즈니스가 절대 안 망할 것 같은 시절도 있었습니다. 하지만 더 이상은 아니에요.”만약 당신이 유저들이 당장 원하는 것에 대해서만 생각하고 있다면, 이미 망하고 있는 것이다. 성공적인 SaaS 기업들은 항상 유저들이 미래에 뭘 원하게 될 지에 대해서 생각한다. 아래에 SaaS 기업들이 어떻게 소비자들의 미래 욕구와 새로운 서비스에 대해 예측하려 하는 지 정리해보았다.당신의 경쟁자들, 그리고 왜 유저들이 그들의 서비스를 이용하는지 이해하라. 온라인 포럼 등을 보고 사람들이 경쟁사의 서비스를 어떻게 평가하는지를 알아내라. 이를 통해 당신은 사람들이 무엇을 원하는지, 그 방향이 어디로 향하게 되는지에 대한 통찰력을 얻게 된다. 이런 과정은 서비스의 확장과 새로운 서비스를 실험해 볼 수 있는 기회도 제공해준다.당신의 서비스를 사용했을 때 유저들이 무엇을 할 수 있을지를 생각해 봐야 한다. 유저들이 당장 요구하는 것만 만드는 것이 아닌 그들이 앞으로 원할 것이 무엇인지를 한 발 앞서 생각해 보는 것이다. 예를 들어, 아마존이 최근 개시한 새로운 서비스인 “Your idea” 리스트를 보자. 이 서비스는 유저들이 쇼핑을 하면서 비록 구입 하진 않더라도 커뮤니티에 자신이 생각한 리스트를 보여주고 싶은 욕구를 미리 연구해서 나온 결과물이다.가장 효과적이면서도 남들이 쉽게 예상하기 힘든 기능들을 우선순위로 짜는 것이 좋다. Gusto의 Tomer London은 서비스를 만들고 그것을 개선시킬 때, 가장 좋은 기능은 타인이 예측하기 어려움에도 불구하고 사용자 경험을 개선시키는데 가장 효과적인 것들이라고 한다. 사람들이 서비스를 통해 무엇을 가장 하고 싶어하는 지를 이해하고 그들을 도와줄 더 쉽고 나은 방법들을 생각해본다면 가장 효과적인 기능에 대한 단서를 잡을 수 있다. 남들이 예측하기 어려운 방법들은 당신이 처한 경쟁 지형에 대해 이해함으로써 알아갈 수 있다. 서비스 이용률을 늘리기 위해 다른 서비스와 통합한다우물 안의 개구리처럼 서비스를 홀로 제공하려 한다면 최대한의 이용률을 얻기란 요원하다. 당신은 새로운 서비스를 내놓음으로써 이용률을 늘릴 수 있지만, 그것으론 충분하지 않다. 유저들은 항상 다른 서비스 역시도 사용하고 있다. 당신이 이길 수 있는 방법은 당신의 서비스를 다른 서비스에 포함시킴으로써 사람들이 그 서비스를 쓸 때, 당신의 서비스도 쓰게 만드는 것이다.당신이 페이스북 웹사이트나 앱을 통해 페이스북을 쓰고 있지 않더라도, 당신은 페이스북을 사용하고 있는 것이나 마찬가지이다.페이스북을 이용해서 다른 서비스에 로그인 할 수 있다당신은 다른 웹사이트의 컨텐츠를 페이스북에 공유할 수 있다당신이 작업하는데 쓰는 서비스를 페이스북에 연결시킬 수 있다.티켓마스터를 통해 공연 티켓을 구매하는 것 역시도 페이스북으로 할 수 있다.페이스북은 다른 서비스들과도 완전히 통합이 되었기 때문에 사람들은 페이스북 인터페이스를 다른 서비스에서 보더라도 전혀 이상하게 생각하지 않는다. 심지어 어떤 경우에는, 페이스북 계정이 없다면 다른 사이트에 가입하기 어려울 때도 있다.페이스북이 다른 서비스와 더 통합이 될수록 당신은 더 페이스북을 쓰게 되고 그것을 필요로 하게 된다. Social Capital LP의 공동 경영자인 Arjun Sethi는 이점에 대해 다음과 같이 말한다.“페이스북이 권장하는 행동들이 일종의 문화가 되고 있어요. 페이스북은 그냥 가만히 앉아서 다른 서비스가 자신의 특징들을 베끼는 걸 보고만 있지 않았습니다. 자신들의 서비스를 다른 곳에 아주 쉽게 통합될 수 있게 만들었고 그 과정에서 핵심적인 이득은 다 챙겨갔습니다.”이것은 페이스북의 신중한 성장 전략의 일환이다. 다른 서비스의 개발자들이 페이스북을 쉽게 그들의 서비스에 통합할 수 있게 만듦으로써, 그냥 자신들의 서비스 내에만 머물러 있는 것에 비해 훨씬 더 많이 사람들이 페이스북을 사용하게끔 만들었다.Slack 역시도 다른 툴과 쉽게 통합이 가능하다페이스북이 다른 소셜, 라이프스타일 서비스들과 통합해서 유저들을 끌어모은 것처럼, Slack 역시도 자신들의 서비스를 다른 관련된 툴들과 통합할 수 있게 만들었다.Front와 같은 이메일 클라이언트와의 통합은 사람들로 하여금 Slack에서 바로 이메일을 관리할 수 있게 하였다.Slack은 또 Stripe와 통합을 하였는데, 이로 인해 사람들은 Slack 내에서 고객 결제 데이터를 보고 관리할 수 있게 되었다.Google Docs와의 통합으로 Slack 앱을 나가지 않고도 구글 문서 활동들을 볼 수 있게 되었다.Slack은 서드 파티의 통합을 장려하기 위해 거대한 앱 생태계를 구축하고 있다. 2015년에, 그들은 앱과 관련해서만 8천만 달러의 벤처 펀드를 만들었다. 2016년에, Slack은 자신의 플랫폼 내에 600개 이상의 앱을 보유하게 되었다. 그래서 이메일을 관리하거나, 고객과 커뮤니케이션을 하거나, 제품 분석 결과를 보는 것 등을 하러 다른 곳으로 일일이 가는 대신에 Slack 유저들은 기존 자신들의 서비스를 통해서 그 모든 것들을 할 수 있게 되었다.페이스북과 Slack은 그들 서비스의 유저들이 사용할 만한 다른 서비스들과 통합을 통해 이용률을 올렸다. 당신 서비스의 이용자들도 알고 있는 이런 기술의 생태계 속에 당신의 서비스를 끼워 넣는 방법에 대해 아래에 정리해 보았다.당신의 서비스를 사용하는 유저들의 워크플로우 대해 생각해보고 그것을 개선시킬 수 있는 점들에 대해서 추측해보라. 예를 들어, HubSpot을 이용하는 기업들의 궁극적인 목적은 사람들을 광고로 유인해서 실제 고객으로 만드는 데 있다. 그래서 HubSpot은 그 목적을 더 잘 수행하기 위해 자신들의 CRM 툴을 페이스북의 광고 관리 프로그램인 Adespresso와 통합할 수 있 게 만들었다. 즉, 사람들이 페이스북 광고를 클릭하게 되면 그 유저의 정보는 자동으로 그들의 CRM에 업로드가 된다.다른 유명 서비스들과의 통합을 통해 그들의 규모가 가진 이점을 가져오는 것이 좋다. 눈에 잘 띄는 서비스와의 통합은 당신의 서비스 역시도 눈에 잘 띄게 만들어준다. 잠재적 유저들에게 당신의 서비스를 소개할 수 있는 기회를 더 얻을 수 있을 뿐만 아니라, 다른 유명 서비스가 가진 브랜드 신뢰성 역시도 가져올 수 있다. 만약 당신의 회사가 아직 작다면, 유명하고 접근하기도 쉬운 Slack이나 페이스북과 같은 서비스와 함께 시작하라.Zapier를 활용해서 다른 서비스들과의 통합을 도모해라. Zapier에 호환이 되도록 앱을 만든다면, 유저들로 하여금 당신이 아직 직접적으로 통합을 제안하기 어려운 다른 앱들과 통합할 수 있는 옵션을 제공해 주는 것과 다름이 없다. 이 방법은 당신의 서비스가 아무리 독특하다 할지라도 그것을 유저들의 워크플로우에 집어넣는 데 도움이 된다.서비스를 개선시키는 데 있어 한 가지 방법만 써서는 이용률을 끌어올리는 데 한계가 있다. SaaS 기업들이 정말로 유저들로 하여금 그들의 서비스를 계속 좋아하고 이용하게끔 만들려면, 할 수 있는 모든 방면에서 이용률 최적화를 해야 한다. 기존의 서비스 내에서 할 뿐만 아니라, 새로운 서비스, 다른 유저들에게 이미 필요한 다른 서비스와의 통합을 해서라도 말이다.차세대 SaaS를 만드는 것에 대해SaaS 서비스들은 점점 더 무용지물이 되어 가는 경우가 많고 사라지는 서비스들도 많다. 만약 SaaS 기업들이 왜 사람들이 그들의 서비스를 쓰는 지 이해하지 못한다면, 그들은 계속 성장할 수 없을 것이고 유저들도 이탈할 것이다.지금까지의 내용을 정리하자면 페이스북은 이용률과 성장을 도모할 수 있는 매우 포괄적이면서도 단순한 방법 3가지를 생각해냈다. 사람들이 현재의 서비스를 더 많이 사용하게 만드는 것, 새로운 서비스를 통해 더 많이 사용하게 만드는 것, 그리고 다른 서비스와의 통합을 통해 자신의 서비스를 더 이용하게 만드는 것. 이 3가지이다. 그리고 이렇게 이용률을 올린다는 것은 성공을 의미한다.미래에 가장 성공적인 SaaS 기업 역시 이용률에 중점을 두게 될 것이다. 지금까지 페이스북을 모델로 삼아 설명한 것처럼, 이것들이 SaaS 기업이 앞으로 더 나은 서비스를 만드는 방법이 될 것이다.원문 : 프로덕트해빗#더팀스 #THETEAMS #SaaS #인사이트 #페이스북

기업문화 엿볼 때, 더팀스

로그인

/