스토리 홈

인터뷰

피드

뉴스

조회수 1149

프리미엄병에 걸리지 말자

** 본 글은 문돌이 PM의 마케터 따라하기 시리즈 입니다.** 1화 보기 - 초기에 할만한 ASO (앱스토어 최적화) 팁** 2화 보기 - 초보 PM이 알아야 하는 초기 모바일앱 분석 101** 3화 보기 - 스타트업 브랜딩: 내가 보는 나와 너가 보는 나의 일치** 4화 보기 - 홍보영상 직접 제작해서 수백만원 절약해보자** 5화 보기 - 바이럴루프, 중요한건 알겠는데 어떻게 적용할래?** 6화 보기 - 인스타그램 노가다 마케팅 101** 7화 보기 - 문돌이도 간지나는 HTML 이메일좀 보내보자** 8화 보기 - 인스타 마케팅 헛수고를 줄이는 10가지 마케팅 방법론** 9화 보기 - 초기 스타트업의 무료 마케팅 채널I'm Coexed!2014년, 한동안 유행했던 말이었다. "폭망했어요"라는 뜻이다. 저 말이 유행탄 이유는 알다시피 우리나라 최대 상권중 하나였던 코엑스몰이 2014년 대대적인 리모델링 후에 유동인구가 반토막이 나버리고, 롯데월드몰까지 생겨버리는 바람에 그 드넓은 공간이 직장인들 구내식당으로 전락해 버렸기 때문이다. 시사매거진 2580에 의하면 리모델링 전에 하루 최소 10만명 이상 찾던 상권이 촬영당시 5-6만을 밑도는 수준으로 떨어지고, 특히 가장 피크타임이어야 할 금요일 저녁 6시부터 촬영한 영상을 보면 보는 내가 불쌍할 정도로 지나다니는 사람들이 별로 없다.화려해 보이는 코엑스몰이지만 정작 중요한 손님이 없다.나는 2011년 부터 약 5년간 저 근처에 있는 회사들을 다녔기 때문에 코엑스몰의 전/후 광경을 모두 목격한 사람 중 한명이다. 코엑스몰이 리모델링 후 폭망하게 된 이유는 사실 여러가지가 있다. 동선의 복잡함, 롯데월드몰과 경쟁, 중고가 브랜드만 입점, 맛집의 실종 등등 수 많은 이유들을 지적할 수 있지만 나는 가장 큰 이유로 '프리미엄병'을 꼽는다.프리미엄병이란게 뭐냐하면, 대략 다음과 같이 정의내릴 수 있다.타겟과 타겟행동의 맥락에 대한 명확한 이해 없이 무조건 가격향상, 있어보이는 디자인 등으로 고급화를 꾀하는 병에 걸린 상황즉, 프리미엄병은 기획자가 타겟, 경쟁제품, 본인제품의 철학과의 관계를 총체적으로 보지 못한 채 무조건 "우리 브랜드는 프리미엄이예요, 이정도 가격은 되야죠, 무조건 고급스러운 톤으로 해요" 등의 말들을 외치는 상황을 말한다.프리미엄병은 Premium Pricing Strategy를 잘못 이해하는데서 시작한다.옛날에 학교다니던 시절 내가 가장 존경했던 모 교수님의 브랜딩 강의에서 가장 강조했던 개념 중에 'Perception'이란게 있었다. 한국말로는 한번에 해석이 어렵고 대략 '사람들의 두뇌 정보처리 상에 입혀지는 이미지'정도로 이해할 수 있겠다. 흔히들 '지각'이라고 번역하는 이 개념은 사실 브랜딩에서는 단순히 인간의 오각에 의한 인지활동을 의미한다기 보다는 그 인지활동을 통해 두뇌의 기억 프로세스 상에서 그려지는 상을 더 강조하는 단어이기에 난 개인적으로 '지각'이라고 번역하는걸 좋아하지는 않는다.얘기가 잠시 샐 뻔했는데, 이 얘기를 왜 하는거냐면, 사실 브랜딩의 거의 모든 개념이 바로 이 Perception과 관련된 인간의 정보처리 과정을 연구하는데서 나온 개념이고, 지금 얘기할 프리미엄 가격 전략 역시 이 Perception이라는 개념을 빼고는 이해할 수 없는 개념이기 때문이다.프리미엄 가격 전략의 정의는 다음과 같다. (Lisa Magloff, Chron)A premium pricing strategy involves setting the price of a product higher than similar products. This strategy is sometimes also called skim pricing because it is an attempt to “skim the cream” off the top of the market. It is used to maximize profit in areas where customers are happy to pay more, where there are no substitutes for the product, where there are barriers to entering the market or when the seller cannot save on costs by producing at a high volume.우리말로 심플하게 바꾸면 다음과 같다.프리미엄 가격 전략이란 사람들한테 비슷한 제품들보다 좀더 높은 가격으로 뻥카를 잘 쳐서 이게 다른 제품들보다 더 좋아보이게 만드는 전략이다.여기서 '다른 제품들 보다 더 좋아보이게' 라는 부분을 주목해야 한다. 프리미엄 가격 전략의 코어가 바로 '비슷해 보이는' 비교제품들 사이에서 차별화 포인트가 마땅치 않은 상황에 순전히 가격으로 뻥카를 잘 쳐서 소비자로 하여금 '아, 싼게 비지떡이라고 이게 비싼 이유가 있겠지..' 하고 믿게 만드는 전략이 바로 프리미엄 가격전략의 정수라고 할 수 있다.프리미엄 가격전략은 일종의 뻥카를 전략적으로 잘 치는거에 의미가 있다.프리미엄과 프리미엄 가격 전략이 다르다.위 문단에서 설명한바와 같이, 프리미엄 가격전략은 어디까지나 전술적이고 단기적 개념의 마케팅 툴이지, 프리미엄 그 자체하고는 완전히 다른 영역이다. 프리미엄이란 사실 외국에서는 뭔가 'additional bonus' 같은 느낌으로 더 많이 사용되는 단어이긴 하지만, 우리나라의 경우로 한정지어서 뜻을 해석해 보면 다음과 같다.타겟에게 타 제품에 비해 exceptional한 가치를 느끼게 함으로써 내가 이 가격을 지불하는게 정당하다고 느끼게 만드는 perception의 하나즉, 내 제품의 타겟이 타 제품과 비교했을때 뭔가 유의미하게 레벨 차이를 느껴서 이 비싼 가격을 내는거에 보람을 느껴야 프리미엄이 형성된다. 자꾸 '유의미하게'를 강조하는 이유는, 그 차이가 유의미하게 크지 않으면 프리미엄 perception 형성에 실패하게 되고, 그냥 가격만 비싼 제품으로 전락해 버리기 때문이다. 프리미엄 가격전략이 단기적인 전술이라면, 프리미엄 그 자체는 매우 장기적이고 그 브랜드의 철학과 직결되는 영역이기 때문에 이 두개를 절대로 혼동해서는 안되는데, 생각보다 많은 기획자가 이걸 구분 없이 생각한다.프리미엄과 프리미엄 가격전략을 혼동하면 십중팔구 Overcharging 전략이 되버린다.고급진게 중요한게 아니라 내가 누구인가가 중요하다.프리미엄병의 가장 핵심이다. 내가 누구인지, 즉 내 브랜드의 정체성, 페르소나, 타겟과의 관계에 대한 고민이 부족한 상태에서 무조건 고급진 인테리어, 고급진 프로모션, 있어보이는 컨텐츠, 비싼 가격등으로 무장하고 싶어한다면, 이건 십중팔구 프리미엄병에 걸렸을 가능성이 크다.내 브랜드의 정체성, 즉 나는 누구이고 어떤 가치를 가지고 있는 사람인가에 대한 고민이 부족한 채 갑자기 내 정체성과 어울려보이지 않는 비싼 옷, 비싼 가방, 어색한 화장을 칠해버리면 프리미엄 형성은 커녕 비호감이 생겨버리는것과 같은 맥락이다. 또한, 내 소비자와 브랜드와의 관계를 무시하는 수준의 프리미엄이라는 옷을 섣불리 입혀버리면 프리미엄이 형성되기는 커녕 그 소비자는 상처받고 나를 떠날 가능성이 커진다.초창기 코엑스 몰의 엄청난 유동인구를 만들어 준 핵심 타겟은 럭셔리 직장인이 아닌 지갑 사정이 어렵지만 제법 저렴한 가격에 고퀄의 데이트가 가능한 젊은 대학생, 중고딩들이였고, 코엑스몰의 제법 깔끔하고 럭셔리한 분위기 속에서 수 많은 중저가 레스토랑과 액세서리 샵들, 보세 의류매장들이 넘쳐났던 코엑스 몰은 주머니 사정이 넉넉치는 않지만 로맨틱하고 특별한 데이트나 쇼핑을 원하던 젊은 학생들이 메인 타겟이였던 것이다. 이런 타겟과의 관계는 무시한 채 갑자기 고급 브랜드, 비싼 레스토랑, 나와는 어색한 부티끄스러운 인테리어로 무장해서 갑자기 그들 앞에 나타났으니 기존 타겟들이 다 떠나버려 지금같은 썰렁한 고급 쇼핑몰이 되어버린 것이다.꼭 기억하도록 하자. 프리미엄이란건 소비자의 perception에 형성되는 것이지 내가 만드는게 아니라는 것을.글쓴이는 스팀헌트 (Steemhunt) 라는 스팀 블록체인 기반 제품 큐레이션 플랫폼의 Co-founder 및 디자이너 입니다. 비즈니스를 전공하고 대기업에서 기획자로 일하다가 스타트업을 창업하고 본업을 디자이너로 전향하게 되는 과정에서 경험한 다양한 고군분투기를 연재하고 있습니다.현재 운영중인 스팀헌트 (Steemhunt)는 전 세계 2,500개가 넘는 블록체인 기반 앱들 중에서 Top 10에 들어갈 정도로 전 세계 150개국 이상의 많은 유저들을 보유한 글로벌 디앱 (DApp - Decentralised Application) 입니다 (출처 - https://www.stateofthedapps.com/rankings).스팀헌트 웹사이트 바로가기
조회수 865

[Tech Blog] Keep Principles in Mind

원칙(Principle)은 중요합니다. “난 원칙대로 살지 않겠어!” 라고 외치고 싶더라도, 원칙이 있고 원칙을 충분히 이해하고 있지 않다면 그저 사춘기 소년/소녀의 이유 없는 반항 정도로 밖에 들리지 않을테니까요. 사실 대부분의 이런 경우 원칙 보다는 “규칙(Rule)대로 살지 않겠다”에 가깝지만, 여기에서는 그냥 넘어가도록 하죠. 소프트웨어 개발에도 다양한 원칙들이 존재합니다. 학부 수업에서 잠깐 들었거나 이런 저런 글들을 읽다가 접해 봤을 이런 원칙들은 실제 서비스를 만들면서 바쁘게 기능을 추가하고 버그를 수정 하느라 어느새 기억 속에서 잊혀지곤 하죠. 정신없이 기능을 구현하다가 문득 코드를 돌아봤을 때 ‘이게 왜 여기에 있지’ 라는 의문이 든다면 한 번쯤 원칙을 되새겨 보라는 신호가 아닐까요? 이 글에서는 Clean Architecture 와 Clean Code 등의 저자로 유명한 Uncle Bob(Robert C. Martin)이 얘기하는 S.O.L.I.D Principles 에 대해 얘기해 보려고 합니다. SOLID 원칙은 밥 아저씨가 2000년도 자신의 논문 Design Principle and Design Patterns 에서 OOD(Object-Oriented Design)를 위해서 제안한 5가지 원칙의 앞 글자만 떼서 붙여졌습니다. Object-Oriented Design 을 대상으로 제안된 원칙이지만 Agile 개발 등의 개발 방법론 핵심 철학에도 적용될 수 있는 개념들 입니다. S.O.L.I.D Principles Single Responsibility Principle Class 는 오직 한 가지의 책임이 주어져야 하고, 오직 한 가지 이유에서만 변경되어야 합니다. 보고서를 편집하고 출력하는 모듈에 대해서 생각해 볼까요. 해당 모듈은 두 가지의 이유로 변경될 가능성이 있습니다. 보고서의 내용이 바뀌었을 때도 변경되어야 하고, 보고서의 형식이 바뀌었을 때도 변경되어야 합니다. 편집 과정 때문에 모듈을 변경하다 보면 해당 변경 사항이 출력 부분에도 영향을 미칠 가능성이 상당히 높습니다. 이 경우 내용을 편집하는 모듈(i.e 내용을 담당하는 모듈)과 출력하는 모듈(i.e 형식을 담당하는 모듈) 두 가지로 나뉘어야 합니다. “할 수 있다고 해서 해야 한다는 뜻은 아닙니다.” Open / Closed Principle Class, Module, Function 등의 소프트웨어 구성 요소는 확장(extension)에 대해 열려 있어야 하며, 변경(modification)에 대해 닫혀 있어야 합니다. 어떤 모듈이 Data Structure 에 필드를 추가하거나 함수를 추가하는 등 확장이 가능하다면 그 모듈은 확장에 대해 열려 있다고 표현합니다. 반면에 어떤 모듈이 수정 없이 다른 모듈에 의해 사용될 수 있다면 그 모듈은 닫혀 있다고 표현합니다.  public class CreditCard {     private int cardType;       public int getCardType() { return cardType; }       public void setCardType(int cardType) { this.cardType = cardType; }          public double getDiscount(double monthlyCost){          if (cardType == 1) {              return monthlyCost * 0.02;          } else {              return monthlyCost * 0.01;          }     } }  위 CreditCard class 에 새로운 카드 타입을 추가하려고 하면 getDiscount 함수를 변경할 수 밖에 없습니다. 이 경우 Open/Closed Principle 을 위반된다고 볼 수 있습니다. “코트를 입기 위해서 개복 수술을 할 필요는 없으니까요.” Liskov Substitution Principle 프로그램 상의 Object 들은 프로그램의 정확성을 해치지 않으면서 하위 타입의 Instance 로 변경 가능해야 합니다. 하위 타입 함수 인자의 반공변성(Contravariance), 하위 타입 함수 반환 타입의 공변성(Covariance), 상위 타입의 예외를 상속하지 않는 추가적인 예외 발생 금지 등의 요구 사항이 있습니다. OOP 에서 상속 개념을 배울 때 이해를 돕기 위해 주어진 몇 가지 예시들이 있었을텐데, 우습게도 우리가 생각하기에 타당한 상속에 관한 예시들 중 의외로 원칙을 위배하는 경우가 많습니다. Liskov Substitution Principle 을 위반하는 대표적인 예시는 정사각형과 직사각형입니다. 정사각형은 직사각형의 일종이니 Square가 Rectangle을 상속받는 것이 충분이 타당한 것으로 보입니다. 정말 그럴까요? Rectangle 의 넓이를 구하는 함수의 테스트를 구성해 봅시다.  Rectangle rect = new Rectangle(); rect.setWidth(10); rect.setHeight(20); assertEquals(200, rect.getArea());  여기에 new Rectangle() 대신에 new Square()가 rect 에 할당되면 어떻게 될까요? 넓이는 400 을 반환하기 때문에 테스트는 실패하겠죠. 정사각형이 직사각형을 상속 받으면 Liskov Subsitution Principle 을 위반한다고 볼 수 있습니다. 상속은 문제를 해결하는데 있어서 상당히 유혹적인 방법입니다. 하지만 상당히 많은 경우에 상속을 오용할 가능성이 높습니다. “오리처럼 생기고 오리처럼 꽥꽥 거리더라도, 배터리가 필요하다면 오리가 아닙니다.” Interface Segregation Principle 많은 것을 아우르고 일반적으로 사용 가능한 하나의 interface 보다 특정 클라이언트를 위한 여러 개의 interface 가 낫습니다. Xerox는 Stapling(프린터기가!?), Fax 등의 다양한 기능이 포함된 신규 프린터 소프트웨어를 개발 도중, 더이상 개발이 불가능할 정도로 프로그램이 번잡 해졌다는 것을 인정하고 밥 아저씨에게 도움을 요청합니다. 문제는 Job Class 하나가 모든 기능을 다 구현하고 있다는데 있었습니다. 이 비대한 Class 는 Client 입장에서 사용되지도 않을 모든 함수를 알 수 있게 구성 되어 있었죠. 이 문제에 대해 밥 아저씨는 Interface Segregation Principle 을 적용하여 각 Client 입장에서 사용해야 하는 함수 만을 가지고 있는 각 interface 들을 따로 만들었습니다. 그리고는 다음에 나올 Principle 인 Dependency Inversion Principle 을 통해서 해당 기능을 구현하게 함으로써 문제를 해결했습니다. Dependency Inversion Principle “추상화에 의존해야지, 구체화에 의존하면 안됩니다.” 상위 계층의 모듈은 하위 계층의 구현이 아니라 추상화에 의존해야 합니다. 상위 계층이 하위 계층의 구현에 의존하던 전통적인 의존 관계를 역전 시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있습니다. 예를 들어 Dependency Injection 은 이 원칙을 따르는 방법 중 하나 입니다. Conclusion 세상에 나쁜 프로그램은 있습니다. 당장 눈에 보이는 기능이 똑같다고 같은 프로그램인 것은 아닙니다. 생각보다 많은 코드들이 ‘그 곳에 넣을 수 있기 때문에’, ‘그 곳에 넣어도 돌아가기 때문에’ 깊은 고민 없이 그 곳에 정착합니다. 당장 좀 더 빠르게 기능을 추가해서 주변 사람들의 박수를 받을 수도 있습니다. 허나 이것들이 쌓이면 더이상 손댈 수 없는 코드가 되고, 문제를 느끼고는 Refactoring을 하자고 다짐하고, 모두 엎은 다음 또 다시 같은 코드를 만들게 되겠죠. 쉬운 코드가 가장 만들기 어려운 코드이고, 그런 좋은 코드는 좋은 원칙으로 부터 나옵니다. 변화에 적응할 수 있는 프로그램, 의도가 쉽게 읽히는 프로그램, 문제 발생 가능성이 적은 프로그램, 쉽게 확장할 수 있는 프로그램 등 좋은 프로그램을 만드는 것은 우리가 실제로 목표하는 것을 달성하기 위해서 정말 중요합니다. 이는 그저 경험이나, Tweak 만으로 이루어지지 않습니다. 다양한 신규 기술들과 Framework 들을 두루 섭렵하면서 활동 반경을 넓히고 경험을 쌓았다면, 가끔은 잠시 서서 원칙에 대해 되돌아 보는 것은 어떨까요?   *버즈빌에서 활기찬 개발자를 채용 중입니다. (전문연구요원 포함)작가소개 Whale, Chief Architect “Keep calm and dream on.”
조회수 1136

개발자의 경력관리란?

경력이 아닌 업력이 되는 단계에 이르러야 가능한 것 아닌가 합니다.대부분의 경력은 '어느 회사의 누구'라는 표현에서 만들어진 것이 아닙니다.진정한 경력의 결과는 '자신의 이름'이 곧 브랜드화 되는 것입니다.매우 당연하게,하루 이틀, 한 두해 한다고 해서 얻어지는 것이 아닙니다."10년 경력!"10년 이상 한 분야나 하나의 도메인, 하나의 테크, 하나의 경력, 하나의 경험을 꾸준하게 파고들었을 때에 얻어지고, 그러는 경험속에서 인사이트, 통찰력이 생기게 됩니다.물론. 그래서, 20대에도 명성을 얻을 수 있는 '경력관리'가 가능하다고 이야기합니다.(실제 얻은 사람을 많이 봤습니다. 그들은 10대에 시작했죠. )회사의 테두리 내에서 얻을 수 있는 '경력'은 '경험'일뿐입니다.자신의 이름을 중심으로 기술할 수 있을 때에 '경력'이라고 이야기할 수 있습니다.개발자라면...글을 써서도 얻을 수 있고,강연을 해서도 얻을 수 있고,GitHub에 오픈소스를 공개하면서도 얻을 수 있습니다.현재 30대와 그 이전의 개발자라면...10대와 20대도 똑같습니다.40대, 50대 이후를 준비하세요.반복적인 일, 똑같은 일, 회사의 프로세스의 하나인 일만 하는 '사람'이라면...그냥, 그 회사의 톱니바퀴가 되는 것입니다.대부분 '경력관리'가 잘 안됩니다.앞으로 50대 이후에도 '브랜드'를 얻을 사람이 되려면...자신의 '경력'관리를 잘 해야 얻을 수 있습니다.나중에 닭 튀기거나 치킨 배달할 것이 아니라면...관리를 잘해야 합니다.경력관리가 가능하려면 어떤 회사를 찾아야 할까요.다음을 기억하세요.1. 구루급 개발자가 있는 회사를 찾으세요.2. 자신이 주도적으로 무언가를 만들 수 있는 권한과 책임을 줄 수 있는 회사를 찾으세요.3. 커뮤니티나 외부 강연, 외부 오픈소스 개발 행사에 적극 참여할 수 있는 기회를 주는 회사를 찾으세요.4. 반복적인 업무와 정체된 마켓에서만 반복적으로 서비스를 하는 회사는 회피하세요.5. 우리 도메인은 원래 이래, 이 일은 원래 이래... 이런 식으로 이야기하는 '상급자'가 있는 회사를 피하세요.6. 쉽게 설명할 수 있도록 준비하고, 리뷰를 할 수 있는 기회와 시간이 주어지는 회사를 찾으세요.그리고, 마지막으로...비전은 누가 주거나 만들어 주지 않습니다.결국, 자기 자신이 찾아야 하는데...이것도, 주변에 이야기가 통하는 '구루급 개발자'가 있어야 그나마 방향성을 찾기 좋습니다.혼자 고민하거나,주변에 비슷한 사람들끼리 고민해봐야 답이 안 나옵니다.꼭, 기억하세요!'구루급 개발자'와 상의하세요.그분들은 실패와 성공, 포기와 단념, 선택과 집중에 대해서 알고 있답니다.퇴근시간이라면..구루급 개발자에게 치맥 한잔 하자고 하세요!
조회수 3481

성장하는 스타트업은 어떻게 일하고 있을까?

어니스트펀드 제품개발팀은 P2P금융을 고객이 직접 경험해볼 수 있도록 서비스를 만들고 꾸준히 발전시켜 나가는 일을 합니다. 제품개발팀은 고객의 니즈를 파악해 전체적인 서비스 구조와 화면을 설계하는 기획자, 고객과의 접점이 되는 화면과 인터랙션을 디자인하는 디자이너, 기획/디자인된 결과물이 실제로 동작할 수 있도록  제품에 생명력을 불어넣는 프런트/서버 엔지니어, 그리고 다양한 데이터를 수집하고 분석함으로써 더 나은 서비스를 가능케 하는 데이터 엔지니어까지 다양한 직군의 전문가들로 이루어져 있습니다.이들이 어떤 프로세스 체계를 갖추고 어떻게 커뮤니케이션하며 업무를 진행하느냐에 따라 서비스 품질뿐만 아니라 업무 효율성에도 많은 차이가 나게 되는데, 어니스트펀드 제품개발팀이 일하는 방법을 소개함으로써 도움이 될만한 부분을 공유하고자 합니다.매일 오전 스크럼 미팅으로 하루를 시작합니다.agora에서 매일 오전 스크럼 미팅제품개발팀은 매일 오전 agora에서 스크럼 미팅을 진행합니다. 스크럼 미팅은 팀 멤버별로 어제 있었던 이슈와 오늘의 할 일을 간단히 공유하고, 새로 추가되거나 변경된 업무와 관련된 맥락을 공유하는 시간입니다. 이 시간을 통하여 멤버 모두 팀 내의 최신 상황을 업데이트하고 서로의 업무에 자유롭게 의견을 교환함으로써, 업무적으로 혹시나 놓칠 수 있는 부분을 최소화하고 각자의 일정을 다시 한번 체크합니다. 짧게는 15분에서 논의가 길어질 때는 1시간 넘게까지 진행되는 등 특별한 제약 없이 얘기 나누는 이 시간은 하루 업무를 시작하는 일상처럼 되었습니다.커뮤니케이션을 효율적으로 합니다.사내 협업도구로 무엇을 사용하고 있나요? 조직이 성장하고 인원이 많아지게 되면 작은 조직일 때와는 다르게 커뮤니케이션이 원활히 되지 않을 수 있습니다. 따라서 적절한 협업도구의 도입과 멤버 모두의 적극적인 사용은 매우 중요합니다.'슬랙'에서 점심 메뉴를 재빠르게 취합하고 있는 팀원들어니스트펀드에서는 여러 도구를 같이 사용하면서 커뮤니케이션을 효율화하고 있습니다. 어떤 주제든 쉽게 얘기할 수 있는 슬랙(Slack)을 기본으로, 업무를 정리해 공유하는 컨플루언스(Confluence), 이슈의 진행 단계를 추적하고 이슈별 논의를 하는 지라(Jira), 소스 관리를 위한 용도로 GIT을 기본적으로 사용 중입니다. 한편으로는 더 나은 도구의 도입을 위한 시도를 계속하고 있고 최근에는 아지트(Agit), 트렐로(Trello) 등도 부분적으로 도입해보고 있습니다.척추를 곧게 유지합니다."척추 펴기는 잘 되어가고 있나요?", "네 열심히 펴고 있습니다." 혹은 "다른 일정으로 조금 지체되고 있어요." 제품개발팀 미팅에서 이런 대화가 오가면, 갓 합류한 직원들은 "척추 펴기가 뭐예요?"라는 질문을 합니다.소프트웨어를 개발하는 회사라면 정도의 차이는 있지만 대부분 기술부채를 가지고 있습니다. 기술 부채란 기술적 이슈 해결을 나중으로 미루고 당장 필요한 시간을 버는 대신, 추후에 시간과 노력을 이자로 지불하는 것을 의미합니다. 당장 필요한 시간을 버는 대신 이 부채가 점점 쌓이게 되면 장기적으로 작업 효율이 떨어지며, 서비스 품질에도 영향을 미칠 수 있어 꾸준한 관리와 청산이 중요한데, 어니스트펀드에서는 보통 기술부채라 불리는 이것들을 척추에 비유합니다. 사람이 바로 서는데 척추가 제일 중요한 것처럼, 중요도를 높여 기술부채를 관리함으로써 개발의 효율을 높이고 시스템을 최적화시켜 나갑니다.물론 척추 펴는 기간을 별도로 할당하고 집중해서 펴는것도 가능하지만, 보통 현실에서는 이런 상황을 기대하기는 힘들고 다른 업무와 병행해서 틈틈히 진행해야 하는 경우가 대부분입니다. 이때 전략을 잘 세우는 것이 중요한데, 척추를 완전히 편 후 한꺼번에 서비스에 적용하는 것보다는 일단 척추를 펼 자리를 잘 마련해놓은 후에(즉, 전체적인 틀을 먼저 잡아 놓은 후에), 한 부분을 작업한 후 서비스에 적용하고 또 다른 부분을 작업한 후 서비스에 적용하고 하는 사이클을 반복해 나가는 것이 좋습니다.척추펴는 작업은 언제든 다른 일정으로 인해 중단될 수 있는데, 서비스 적용까지의 사이클을 최대한 짧게 가져감으로써 일부분씩 펴나가는 것이 중요합니다. 어니스트펀드 제품개발팀에서는 이렇게 꾸준히 척추펴는 작업을 진행함으로써 점진적으로 시스템을 개선해 나가고 있습니다. 앞으로도 P2P금융업계는 빠르게 성장하는 만큼 많은 변화가 예상되는데, 시간이 지나면서 펴진 척추가 언제든 다시 굽어질 수 있습니다. 따라서 상황에 따라 굽어지는 것을 받아들이되 이것을 자각하고, 꾸준히 펴 나가려는 계획과 행동이 매우 중요합니다.정기적으로 깊이 있는 업무/기술 공유의 시간을 갖습니다.제품개발팀에서는 정기적으로 각자 주제를 정해 깊이 있게 공유하는 세션을 가집니다. 현재 시스템의 구조에 대한 것부터, 담당하고 있는 모듈의 앞으로의 개선방향, 또는 그동안 개선한 부분, 공유하고 싶은 기술 등 업무와 관련된 부분이 주가 됩니다. 한 주제에 대해 깊이 있게 공유하면서, 팀원 모두 시스템 전반에 대한 이해를 높일 수 있고, 기술적으로도 발전할 수 있는 좋은 기회가 됩니다.첫 세션을 진행한 후 한번 듣고 버리기엔 아까운 내용이라는 생각에, 두 번째 세션부터는 간단히 스마트폰으로 녹화를 진행하고 파일을 위키에 정리해 왔는데, 최근에 새로 합류한 팀원이 이 녹화된 파일을 인터넷 강의 시청하듯 참고하여 많은 도움이 되었다는 얘기를 들었습니다. 글로 정리하는 것과는 분명 다른 현장의 분위기나 디테일을 그대로 느낄 수 있었을 것입니다. 발표자가 부담을 느끼지 않는 범위에서, 사내 세션도 이렇게 녹화해 언제든 꺼내 볼 수 있게 하는 것도 좋은 방법인 것 같아 고려해보면 좋은 방법 같습니다.페어프로그래밍으로 대화를 많이 하고, 품질을 향상시킵니다.제품개발팀의 서버파트는 두 명씩 페어를 구성하고 있습니다. 페어는 각자 업무를 진행하면서 의논할 일이 있으면 첫 번째로 대화할 상대이며, 위에서 언급한 주번 활동을 같이 하기도 합니다. 또한 자율적으로 페어프로그래밍을 진행하기도 합니다. 페어프로그래밍이란 말 그대로 두 명이 짝을 지어 코딩과 리뷰를 동시에 진행하는 것으로, 하나의 모니터를 같이 보며 한 명은 코드를 작성하고 한 명은 작성 중인 코드를 리뷰하는 방식입니다. 이 방식의 장점은 둘이 머리를 맞댐으로써 더 좋은 품질을 기대할 수 있고, 리뷰를 동시에 진행함으로 버그가 감소하며, 지식 공유를 더 활발하게 할 수 있습니다. 최근에 한 페어가 적극적으로 페어프로그래밍을 진행하고 있고, 매일 결과를 위키에 업데이트하고 있는데, 재미있는 부분이 많아서 일부 화면을 캡처해봅니다. 두 분 응원합니다.페어프로그래밍을 진행하고 있는 제품개발팀 druwa와 sinclair테스트 케이스가 없다면 아직 완료가 아닙니다.테스트는 아무리 강조해도 지나치지 않습니다. 여기서 테스트는 QA조직이 진행하는 테스트가 아닌, 개발팀 내에서의 테스트 케이스 작성이며, 유닛 테스트부터 통합 테스트까지 모두 포함합니다.견고한 테스트 케이스 작성은 신규 서비스를 개발할 때뿐만 아니라, 척추 펴기나 구조개선을 할 때 변경된 로직을 검증하는 데 있어 반드시 필요합니다. 또한, 다양한 시나리오를 상상하며 테스트 케이스를 작성하게 되는데 기능을 구현할 때와는 또 다른 방향에서 접근하게 되면서, 업무 로직에 대한 이해를 더욱 높일 수도 있습니다. 그리고, 어니스트펀드는 서비스의 특성상 외부 서비스(신용평가업체, 벤더업체 등)와 연동하는 부분이 많습니다. 따라서, 완전한 테스트를 위해서는 이를 위한 mock 서버 구현과, 테스트를 용이하게 하기 위한 패턴 적용 등도 개발 시 꼭 고려해야 합니다. 번갈아가며 주번 활동을 합니다.운영팀(allen)의 요청에 따른 제품개발팀(money, turbo)의 대응서비스를 운영하다 보면 일상적이지만, 사람 손을 꼭 필요로 하는 부분이 있습니다. 예를 들면, 운영팀으로 인입된 고객 문의 중 제품개발팀의 확인이 필요한 것에 대한 지원, 다른 팀으로부터의 단발성 요청에 대한 처리, 또는 일상적인 서버 배포 등의 업무가 있습니다. 담당자가 명확하지 않은 상태에서는 자칫하면 이런 업무가 특정 팀원에게 집중되거나, 책임이 불명확해 처리가 지연될 수도 있습니다. 제품개발팀에서는 주번 제도를 도입해서 이런 이슈를 처리하고 있습니다. 주번 제도는 개발팀 멤버 두 명씩 페어가 되어 2주 간격으로 로테이션하면서 진행됩니다. 다른 팀에서 위와 같은 이슈로 개발팀의 손길이 필요한 경우에는 언제든지 슬랙에서 주번을 소환하면 주번은 책임지고 업무를 처리합니다.주번 활동이 중요도가 떨어지는 귀찮은 작업처럼 보일 수 있지만, 운영 전반에 대한 이슈를 개발팀 모두 경험하면서 개선할 부분에 대한 공감대를 형성하기도 하고, 자기가 담당한 모듈이 아닌 다른 부분을 처리함으로써 전체적인 이해도를 높이는데 많은 도움이 됩니다. 또한, 주번이 아닌 동안에는 부가적인 업무 요청 없이 자기 업무에 집중할 수 있어 방해받지 않고 업무에 집중할 수 있습니다.누구보다 먼저 예외/장애 상황을 인지합니다. 혹시나 발생할지 모르는 서비스의 예외/장애 상황을 즉각적으로 인지할 수 있는 수단이 있나요? 고객에게 서비스를 제공하고 운영하는 팀이라면 누구보다 먼저 장애나 예외 상황을 인지할 수 있어야 합니다.어니스트펀드에서는 문제 상황이 발생할 경우 슬랙이나 SMS를 통해 실시간으로 정보를 받을 수 있게끔 구성되어 있고, 문제의 경중에 따라 천천히 처리하기도 하고 즉각적으로 해결하기도 합니다. 완성도 있는 서비스 제공을 위해서 이런 알림 채널을 갖추는 것과 적절한 알림을 보내기 위한 고도화는 꼭 필요한 부분입니다. 마치며.개개인이 아무리 뛰어나더라도 팀으로써 유기적으로 일하지 못한다면 좋은 결과물을 기대하기는 힘듭니다. 유기적으로 일을 하기 위해서는 체계적인 업무 프로세스와 효율적인 커뮤니케이션의 중요성은 두말할 나위가 없습니다. 특히 P2P금융업계처럼 급격히 성장하고, 서비스 모델이 빠르게 변화하는 시장에서는 더더욱 중요할 것이며, 이를 위한 어니스트펀드 제품개발팀의 노력은 계속될 것입니다. #어니스트펀드 #스타트업 #개발자 #CTO #스타트업일상 #인사이트
조회수 1424

카바조 밋업! 정비사님들과의 만남~ 앞으로도 잘 부탁드립니다 ^^

안녕하세요~토요일을 앞두고 너무 신나는 카바조입니다!뭐 지난주만큼은 아니지만요~지난주에는 정비사님과의 만남카바조 밋업 행사가 있었거든요~!장소는 마루 180.카바조와 함께하는 정비사님들을 초청하여함께 이야기하는 자리를 마련해보았습니다.이번 행사로 그동안 정비사님들과함께 해온 카바조 활동들에 대해서로 소통하는 시간을 가질 수 있었습니다.특히 이번 행사에서는 새로 출시한카바조 정비사용 어플을 소개해드리게 되었는데요.정비사님들도 좀 더 편한 방법으로나은 서비스를 제공할 수 있도록 보완하고자 했습니다. 기존 서비스에서는 정비사님의 전화번호가노출되어 불편을 드린 점 또한 보완하여카바조의 새로운 서비스에서는050 안심전화번호 서비스가 제공됩니다.카바조는 어제보다 더 나은서비스를 만들기 위해 끈임없이 노력하며,정비사님들과 함께 가는 서비스가될 것을 약속드립니다.현재 카바조 정비사로 활동하고 계시는정비사님들의 이야기도 들을 수 있었습니다.카바조 정비사로만활동하시는 이승룡 정비사님과대구에서 정비소를 운영하며카바조 정비사를 겸업하시는 김상범 정비사님.두 정비사님이 대표로 말씀해주셨습니다.이승룡 정비사님은 카바조 플랫폼을 통해검수 가능한 시간에 정직한 검수 서비스를 제공하며수입을 가져갈 수 있다는 점이 좋다고 이야기해주셨습니다.정비소를 함께 운영하시는 김상범 정비사님은카바조로 검수 서비스를 제공하면서추가적인 고객 유치가 가능해졌다고 하셨습니다.또한 정비소 운영 시간 외,자신의 능력을 살린 아르바이트가가능하다는 점이 좋다고 말씀해주셨습니다.카바조 또한 정비사님들의정직하고 꼼꼼한 활동에 감사드리며선물과 감사장을 준비했습니다.선물은 정비사님들의피곤을 풀어줄 EMS 마사지기입니다ㅎㅎ앞으로도 카바조, 잘 부탁드립니다!<이번 행사는 카바조 아산문화재단우수 졸업기업 선정으로 MARU 180과 함께하였습니다>
조회수 8280

Node.js로 Amazon DynamoDB 사용하기

DynamoDB 로컬 설정 (다운로드 버전)실제 DynamoDB 웹 서비스에 액세스하지 않고 로컬에서 애플리케이션 작성 및 테스트를 할 수 있음1. 다운로드 링크에서 DynamoDB 무료 다운로드2. 압축 해제 후 해당 디렉터리에서 아래의 명령어로 실행java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb* Ctrl+C로 중지할 수 있고 중지하기 전까지 수신 요청을 처리함* 기본적으로 8000번 포트를 사용Node.js 용 AWS SDK 설치1. 설치npm install aws-sdk2. 실행// app.jsvar AWS = require("aws-sdk");var s3 = new AWS.S3();// 버킷 이름은 모든 S3 사용자에게 고유한 것이어야 합니다.var myBucket = "dynamodb.sample.wonny";var myKey = "myBucketKey";s3.createBucket({ Bucket: myBucket }, function(err, data) {  if (err) {    console.log(err);  } else {    params = { Bucket: myBucket, Key: myKey, Body: "Hello!" };    s3.putObject(params, function(err, data) {      if (err) {        console.log(err);      } else {        console.log("Successfully uploaded data to myBucket/myKey");      }    });  }});node app.js테이블 생성// CreateTable.jsvar AWS = require("aws-sdk");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var dynamodb = new AWS.DynamoDB();var params = {  TableName: "Movies",  KeySchema: [    { AttributeName: "year", KeyType: "HASH" }, // Partition key    { AttributeName: "title", KeyType: "RANGE" } // Sort key  ],  AttributeDefinitions: [    { AttributeName: "year", AttributeType: "N" },    { AttributeName: "title", AttributeType: "S" }  ],  // 다운로드 버전인 경우 아래 코드 무시  ProvisionedThroughput: {    ReadCapacityUnits: 10,    WriteCapacityUnits: 10  }};dynamodb.createTable(params, function(err, data) {  if (err) {    console.log(      "Unable to create table. Error JSON: ",      JSON.stringify(err, null, 2)    );  } else {    console.log(      "Created table. Table description JSON: ",      JSON.stringify(data, null, 2)    );  }});node CreateTable.js샘플 데이터 로드1. 이곳에서 샘플 데이터 파일 다운로드데이터 형태는 아래와 같음[    {        "year": 2013,        "title": "Rush",        "info": {            "directors": ["Ron Howard"],            "release_date": "2013-09-02T00:00:00Z",            "rating": 8.3,            "genres": [                "Action",                "Biography",                "Drama",                "Sport"            ],            "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg",            "plot": "A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.",            "rank": 2,            "running_time_secs": 7380,            "actors": [                "Daniel Bruhl",                "Chris Hemsworth",                "Olivia Wilde"            ]        }    },    ...]- year 및 title을 Movies 테이블을 위한 기본 키 속성 값으로 사용- info의 나머지 값들은 info라는 단일 속성에 저장- JSON을 DynamoDB 속성에 저장2. 샘플 데이터 Movies 테이블에 로드// LoadData.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();console.log("Importing movies info DynamoDB. Please wait.");var allMovies = JSON.parse(fs.readFileSync("moviedata.json", "utf8"));allMovies.forEach(function(movie) {  var params = {    TableName: "Moves",    Item: {      year: movie.year,      title: movie.title,      info: movie.info    }  };  docClient.put(params, function(err, data) {    if (err) {      console.error(        "Unable to add movie",        movie.title,        ". Error JSON:",        JSON.stringify(err, null, 2)      );    } else {      console.log("PutItem succeeded:", movie.title);    }  });});node LoadData.js테이블에 항목 추가// PutItem.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";var params = {  TableName: table,  Item: {    year: year,    title: title,    info: {      plot: "Nothing happens at all.",      rating: 0    }  }};console.log("Adding a new item...");docClient.put(params, function(err, data) {  if (err) {    console.error(      "Unable to add item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("Added item:", JSON.stringify(data, null, 2));  }});node PutItem.js- 기본 키가 필요하므로 기본 키 (year, title) 및 info 속성 추가항목 읽기// GetItem.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";var params = {  TableName: table,  Key: {    year: year,    title: title  }};docClient.get(params, function(err, data) {  if (err) {    console.error(      "Unable to read item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("GetItem succeeded:", JSON.stringify(data, null, 2));  }});node GetItem.js항목 업데이트// UpdateItem.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";var params = {  TableName: table,  Key: {    year: year,    title: title  },  UpdateExpression: "set info.rating = :r, info.plot=:p, info.actors=:a",  ExpressionAttributeValues: {    ":r": 5.5,    ":p": "Everything happens all at once.",    ":a": ["Larry", "Moe", "Curly"]  },  ReturnValues: "UPDATED_NEW"};console.log("Updating the item...");docClient.update(params, function(err, data) {  if (err) {    console.error(      "Unable to update item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));  }});node UpdateItem.js- 지정된 항목에 대해 수행하고자 하는 모든 업데이트를 설명하기 위해 UpdateExpression을 사용- ReturnValues 파라미터는 DynamoDB에게 업데이트된 속성("UPDATED_NEW")만 반환하도록 지시원자성 카운터 증가시키기update 메서드를 사용하여 다른 쓰기 요청을 방해하지 않으면서 기존 속성의 값을 증가시키거나 감소시킬 수 있음 (모든 쓰기 요청은 수신된 순서대로 적용)실행 시 rating 속성이 1씩 증가하는 프로그램// Increment.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";// Increment an atomic countervar params = {  TableName: table,  Key: {    year: year,    title: title  },  UpdateExpression: "set info.rating = info.rating + :val",  ExpressionAttributeValues: {    ":val": 1  },  ReturnValues: "UPDATED_NEW"};console.log("Updating the item...");docClient.update(params, function(err, data) {  if (err) {    console.error(      "Unable to update item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));  }});node Increment.js항목 업데이트(조건부)UpdateItem을 조건과 함께 사용하는 방법조건이 true로 평가되면 업데이트가 성공하지만 그렇지 않으면 수행되지 않음// ConditionalUpdateItem.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";// Increment an atomic countervar params = {  TableName: table,  Key: {    year: year,    title: title  },   UpdateExpression: "remove info.actors[0]",  ConditionExpression: "size(info.actors) > :num",  ExpressionAttributeValues: {    ":num": 3  },  ReturnValues: "UPDATED_NEW"};console.log("Attempting a conditional update...");docClient.update(params, function(err, data) {  if (err) {    console.error(      "Unable to update item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("UpdateItem succeeded:", JSON.stringify(data, null, 2));  }});node ConditionalUpdateItem.js다음과 같이 작성하면 아래와 같은 에러 메시지가 표시 됨The conditional request failed"영화에는 3명의 배우가 있는데 배우가 3명보다 많은지를 확인하고 있어 에러가 발생다음과 같이 수정하면 정상적으로 항목이 업데이트 됨ConditionExpression: "size(info.actors) >= :num",항목 삭제// DeleteItem.jsvar AWS = require("aws-sdk");var fs = require("fs");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var table = "Movies";var year = 2017;var title = "The Big Wonny";var params = {  TableName: table,  Key: {    year: year,    title: title  },  ConditionExpression: "info.rating <= :val",  ExpressionAttributeValues: {    ":val": 5.0  }};console.log("Attempting a conditional delete...");docClient.delete(params, function(err, data) {  if (err) {    console.error(      "Unable to update item. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log("DeleteItem succeeded:", JSON.stringify(data, null, 2));  }});node DeleteItem.js다음과 같이 작성하면 아래와 같은 에러 메시지가 표시 됨The conditional request failed특정 영화에 대한 평점이 5보다 크기 때문에 에러가 발생다음과 같이 수정하면 정상적으로 항목이 삭제 됨var params = {  TableName: table,  Key: {    year: year,    title: title  }};데이터 쿼리- 파티션 키 값을 지정해야 하며, 정렬 키는 선택 사항- 1년 동안 개봉한 모든 영화를 찾으려면 year만 지정, title을 입력하면 2014년 개봉된 "A"로 시작하는 영화를 검색하는 것과 같이 정렬 키에 대한 어떤 조건을 바탕으로 일부 영화를 검색할 수도 있음한 해 동안 개봉한 모든 영화// QueryYear.jsvar AWS = require("aws-sdk");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var params = {  TableName: "Movies",  KeyConditionExpression: "#yr = :yyyy",  ExpressionAttributeNames: {    "#yr": "year"  },  ExpressionAttributeValues: {    ":yyyy": 1985  }};docClient.query(params, function(err, data) {  if (err) {    console.error("Unable to query. Error JSON:", JSON.stringify(err, null, 2));  } else {    console.log("Query succeeded.");    data.Items.forEach(function(item) {      console.log(" -", item.year + ": " + item.title);    });  }});node QueryYear.jsExpressionAttributeNames는 이름을 교체함. 이를 사용하는 이유는 year가 DynamoDB에서 예약어이기 때문. KeyConditionExpression을 포함해 어떤 표현식에서도 사용할 수 없으므로 표현식 속성 이름인 #yr을 사용하여 이를 지칭ExpressionAttributeValues는 값을 교체함. 이를 사용하는 이유는 KeyConditionExpresssion을 포함해 어떤 표현식에서도 리터럴을 사용할 수 없기 때문. 표현식 속성 값인 :yyyy를 사용해 지칭* 위의 프로그램은 기본 키 속성으로 테이블을 쿼리하는 방법. DynamoDB에서 1개 이상의 보조 인덱스를 테이블에 생성하여 그 인덱스로 테이블을 쿼리하는 것과 동일한 방식으로 쿼리 작업 가능. 보조 인덱스는 키가 아닌 속성에 대한 쿼리를 허용하여 애플리케이션에 더 많은 유연성을 부여함한 해 동안 개봉한 모든ㄴ 영화 중에 특정 제목을 지닌 영화year 1992에 개봉한 영화 중에 title이 "A"부터 "L"까지의 알파벳으로 시작하는 영화를 모두 조회합니다.// QueryTitle.jsvar AWS = require("aws-sdk");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();console.log(  "Querying for movies from 1992 - titles A-L, with genres and lead actor");var params = {  TableName: "Movies",  ProjectionExpression: "#yr, title, info.genres, info.actors[0]",  KeyConditionExpression: "#yr = :yyyy and title between :letter1 and :letter2",  ExpressionAttributeNames: {    "#yr": "year"  },  ExpressionAttributeValues: {    ":yyyy": 1992,    ":letter1": "A",    ":letter2": "L"  }};docClient.query(params, function(err, data) {  if (err) {    console.error("Unable to query. Error JSON:", JSON.stringify(err, null, 2));  } else {    console.log("Query succeeded.");    data.Items.forEach(function(item) {      console.log(        " -",        item.year + ": " + item.title + " ... " + item.info.genres + " ... ",        item.info.actors[0]      );    });  }});node QueryTtiel.js스캔테이블의 모든 항목을 읽고 테이블의 모든 데이터를 반환선택 사항인 filter_expression을 제공할 수 있으며 그 결과 기준이 일치하는 항목만 반환하지만 필터는 테이블 전체를 스캔한 후에만 적용됨// Scan.jsvar AWS = require("aws-sdk");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var docClient = new AWS.DynamoDB.DocumentClient();var params = {  TableName: "Movies",  ProjectionExpression: "#yr, title, info.rating",  FilterExpression: "#yr between :start_yr and :end_yr",  ExpressionAttributeNames: {    "#yr": "year"  },  ExpressionAttributeValues: {    ":start_yr": 1950,    ":end_yr": 1959  }};console.log("Scanning Movies table.");docClient.scan(params, onScan);function onScan(err, data) {  if (err) {    console.error(      "Unable to scan the table. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    // print all the movies    console.log("Scan succeeded.");    data.Items.forEach(function(movie) {      console.log(        movie.year + ": ",        movie.title,        "- rating:",        movie.info.rating      );    });    // continue scanning if we have more movies, because    // scan can retrieve a maximum of 1MB of data    if (typeof data.LastEvaluatedKey != "undefined") {      console.log("Scanning for more...");      params.ExclusiveStartKey = data.LastEvaluatedKey;      docClient.scan(params, onScan);    }  }}node Scan.jsProjectionExpression은 스캔 결과에서 원하는 속성만 지정FilterExpression은 조건을 만족하는 항목만 반환하도록 조건을 지정. 다른 항목들은 모두 무시됨테이블 삭제// DeleteTable.jsvar AWS = require("aws-sdk");AWS.config.update({  region: "us-west-2",  endpoint: "http://localhost:8000"});var dynamodb = new AWS.DynamoDB();var params = {  TableName: "Movies"};dynamodb.deleteTable(params, function(err, data) {  if (err) {    console.error(      "Unable to delete table. Error JSON:",      JSON.stringify(err, null, 2)    );  } else {    console.log(      "Deleted table. Table description JSON:",      JSON.stringify(data, null, 2)    );  }});node DeleteTable.js#트레바리 #개발자 #안드로이드 #앱개발 #Node.js #백엔드 #인사이트 #경험공유 #데이터베이스 #DB #개발 #AWS #아마존 #NoSQL 
조회수 2446

MySQL의 Transaction Isolation Level (Lock에 관하여)

편집자 주문맥에 따라 ‘Transaction’과 ‘트랜잭션’으로 영어와 한글을 혼용함.문맥에 따라 ‘LOCK’과 ‘lock’으로 대문자와 소문자를 혼용함.OverviewMySQL DB는 일반적인 운영환경에서 뛰어난 성능을 제공합니다. 특히 적은 양의 자료가 빈번하게 교류되는 환경에서는 더욱 빛을 발하죠. 국내에서는 주로 작은 규모의 웹사이트를 구축할 때 MySQL을 사용합니다. 그런데 문제는 사이트의 규모가 커지면서부터 생긴다는 것이죠. 조금씩 느려지는 Query가 생기면 원인도 파악하고, Query를 튜닝하고, 설계도 변경하지만 MySQL의 특징적인 문제를 곧 만나게 됩니다.테이블을 복제(CREATE SELECT)하거나 다른 테이블로 옮기면(INSERT SELECT) 작업을 하는 동안 SELECT 절에 있는 테이블들이 Lock이 걸립니다. 게다가 다른 Session에서 해당 테이블을 수정(UPDATE / DELETE)하면 복제와 이동을 마칠 때까지 대기 상태로 있어야 한다는 것입니다. 이러한 문제는 시스템을 구축하고 자료가 일정량 쌓이기 전까지는 알 수 없습니다. 또한 Oracle과 같은 DB를 사용하던 사용자가, MySQL을 사용하면 이와 같은 문제가 있을 것이라고 생각하기도 어렵습니다.이러한 특징을 가진 MySQL의 Transaction Isolation Level을 알아보고자 합니다. Transaction Isolation Level 은 Transaction의 경리 수준을 말합니다. 트랜잭션 처리 시 다른 트랜잭션에서 접근해 자료를 수정하거나 볼 수 있도록 하는 수준입니다.Transaction Isolation Level의 종류와 특성Transaction Isolation Level에는 READ UNCOMMITTED, READ COMMIITED, REPEATABLE READ, SERIALIZE 네 가지 종류가 있습니다. 1)READ UNCOMMITTED1) COMMIT 되지 않은 데이터에 다른 트랜잭션에서 접근할수 있다.2) INSERT, UPDATE, DELETE 후 COMMIT 이나 ROLLBACK에 상관없이 현재의 데이터를 읽어온다.3) ROLLBACK이 될 데이터도 읽어올 수 있으므로 주의가 필요하다.4) LOCK이 발생하지 않는다.READ COMMIITED1) COMMIT 된 데이터에 다른 트랜잭션에서 접근할 수 있다.2) 구현 방식이 차이 때문에 Query를 수행한 시점의 데이터와 정확하게 일치하지 않을 수 있다.3) LOCK이 발생하지 않는다.4) MySQL에서 많은 양의 데이터를 복제하거나 이동할 때 이 LEVEL을 추천한다.REPEATABLE READ1) Default LEVEL이다.2) SELECT시 현재 시점의 스냅샷을 만들고 스냅샷을 조회한다.3) 동일 트랜잭션 내에서 일관성을 보장한다.4) record lock과 gap lock이 발생한다.5) CREATE SELECT, INSERT SELECT시 lock이 발생한다.SERIALIZE1) 가장 강력한 LEVEL이다.2) SELECT 문에 사용하는 모든 테이블에 shared lock이 발생한다.LOCK과 테이블, 어떻게 해결할 수 있을까?지금부터는 관련된 내용을 확인해보겠습니다. 우선 현재의 경리 수준부터 알아보겠습니다.mysql> SHOW VARIABLES WHERE VARIABLE_NAME='tx_isolation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | REPEATABLE-READ | +---------------+-----------------+ 1 row in set (0.00 sec) 다음으로 TEST 테이블을 만듭니다. 이때 SELECT절의 테이블을 UPDATE할 경우, 대기 상태로 빠지는 것을 확인해보겠습니다. 테이블을 만들고 상태를 확인합니다.CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 ; -- 생성시 INFORMATION_SCHEMA.PROCESSLIST 로 상태를 확인합니다. mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 5 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 1 row in set (0.00 sec) 다음으로 테이블 생성 시 UPDATE를 해 대기 상태로 빠지는지 확인해보겠습니다.UPDATE test.TB_PROD_BAS SET PROD_MEMO = 'TEST' WHERE PROD_ID = 1 ; mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 24 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 *************************** 2. row *************************** ID: 11006 USER: hansj HOST: 192.168.1.150:50719 DB: test COMMAND: Query TIME: 22 *****이부분 중요합니다.****** STATE: updating *****이부분 중요합니다.****** INFO: UPDATE test.TB_PROD_BAS SET PROD_MEMO = 'TEST' WHERE PROD_ID = 1 2 rows in set (0.00 sec) 위의 TIME을 보면 테이블이 생성될 때까지 대기하고, UPDATE 문의 상태가 updating 으로 표시됩니다. 하지만 이렇게 나올 경우 건수가 많으면 실제 UPDATE 중인지 대기상태인지 확인하기가 어렵습니다. LOCK이 걸린 테이블을 확인하려면 INNODB LOCK 테이블로 정확하게 알 수 있습니다. 아래 세 가지 테이블로 확인해보겠습니다. 보다 자세한 설명은 MySQL 홈페이지를 확인합니다.information_schema.INNODB_TRXLOCK을 걸고 있는 프로세스 정보information_schema.INNODB_LOCK_WAITS현재 LOCK이 걸려 대기중인 정보information_schema.INNODB_LOCKSLOCK을 건 정보위의 각 항목마다 테이블 생성 및 UPDATE 시 정보가 어떻게 나타나는지 확인해보겠습니다.1.information_schema.INNODB_TRXmysql> SELECT -> T101.TRX_ID -> ,T101.TRX_STATE -> ,T101.TRX_STARTED -> ,T101.TRX_REQUESTED_LOCK_ID -> ,T101.TRX_WAIT_STARTED -> ,T101.TRX_WEIGHT -> ,T101.TRX_MYSQL_THREAD_ID -> ,T101.TRX_ISOLATION_LEVEL -> ,SUBSTR(T101.TRX_QUERY,1,10)AS TRX_QUERY -> FROM information_schema.INNODB_TRX T101 -> ; +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ | TRX_ID | TRX_STATE | TRX_STARTED | TRX_REQUESTED_LOCK_ID | TRX_WAIT_STARTED | TRX_WEIGHT | TRX_MYSQL_THREAD_ID | TRX_ISOLATION_LEVEL | TRX_QUERY | +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ | 8771591 | LOCK WAIT | 2019-05-27 16:15:53 | 8771591:70031:4:306 | 2019-05-27 16:15:53 | 2 | 11006 | REPEATABLE READ | UPDATE tes | | 8771586 | RUNNING | 2019-05-27 16:15:51 | NULL | NULL | 1538969 | 11004 | REPEATABLE READ | CREATE TAB | +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ 2 rows in set (0.00 sec) TRX_ID_STATE트랜잭션의 상태를 나타냅니다. 실행 중인지 LOCK WAIT 상태인지 알 수 있습니다.TRX_MYSQL_THREAD_IDPROCESSLIST 의 ID를 나타냅니다.TRX_ISOLATION_LEVELISOLATION LEVEL을 나타냅니다.따라서 위의 내용을 보면 CREATE TABLE이 실행 중인 것과, UPDATE가 LOCK WAIT인 것, 그리고 관련된 PROCESSLIST의 ID까지도 알 수 있습니다2.information_schema.INNODB_LOCK_WAITSmysql> SELECT -> * -> FROM information_schema.INNODB_LOCK_WAITS T101 -> ; +-------------------+---------------------+-----------------+---------------------+ | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id | +-------------------+---------------------+-----------------+---------------------+ | 8771591 | 8771591:70031:4:306 | 8771586 | 8771586:70031:4:306 | +-------------------+---------------------+-----------------+---------------------+ 1 row in set (0.01 sec) requesting_trx_idLOCK WAIT 인 TRX_IDblocking_trx_idLOCK 을 건 TRX_ID현재 LOCK이 걸린 TRX_ID와 LOCK을 걸어둔 TRX_ID를 알 수 있습니다.3.information_schema.INNODB_LOCKSmysql> SELECT -> * -> FROM information_schema.INNODB_LOCKS -> ; +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data | +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ | 8771591:70031:4:306 | 8771591 | X | RECORD | `test`.`TB_PROD_BAS` | PRIMARY | 70031 | 4 | 306 | 1 | | 8771586:70031:4:306 | 8771586 | S | RECORD | `test`.`TB_PROD_BAS` | PRIMARY | 70031 | 4 | 306 | 1 | +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ 2 rows in set (0.01 sec) lock_trx_idLOCK 과 관련된 TRX_IDlock_modeX 쓰기, S 읽기 2)어떤 테이블이 LOCK을 걸고 있는지 알 수 있습니다.위의 내용들을 통해 REPEATABLE READ에서 CREATE SELECT시 SELECT 테이블에 LOCK이 걸려 UPDATE가 대기하게 되는 것을 알 수 있습니다. 이번에는 Transaction Isolation Level 을 READ COMMIITED로 변경하고 CREATE SELECT 및 UPDATE를 진행해보겠습니다.SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; SHOW VARIABLES WHERE VARIABLE_NAME='tx_isolation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | READ-COMMITTED | +---------------+-----------------+ 1 row in set (0.00 sec) UPDATE 문은 다음과 같이 수행됩니다. mysql> UPDATE test.TB_PROD_BAS -> SET PROD_MEMO = 'TEST' -> WHERE PROD_ID = 1 -> ; Query OK, 0 rows affected (0.04 sec) Rows matched: 1 Changed: 0 Warnings: 0 기존에 대기했던 것과 다르게 0.04초가 걸렸습니다.mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 9 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 1 row in set (0.00 sec) -- 프로세스 정보도 CREATE TABLE 만 진행중임을 알수 있습니다. mysql> SELECT -> T101.TRX_ID -> ,T101.TRX_STATE -> ,T101.TRX_STARTED -> ,T101.TRX_REQUESTED_LOCK_ID -> ,T101.TRX_WAIT_STARTED -> ,T101.TRX_WEIGHT -> ,T101.TRX_MYSQL_THREAD_ID -> ,T101.TRX_ISOLATION_LEVEL -> ,T101.TRX_QUERY -> FROM information_schema.INNODB_TRX T101 -> ; +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | TRX_ID | TRX_STATE | TRX_STARTED | TRX_REQUESTED_LOCK_ID | TRX_WAIT_STARTED | TRX_WEIGHT | TRX_MYSQL_THREAD_ID | TRX_ISOLATION_LEVEL | TRX_QUERY | +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | 8771856 | RUNNING | 2019-05-27 17:17:45 | NULL | NULL | 4594347 | 11004 | READ COMMITTED | CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 | +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) READ COMMITTED LEVEL로 CREATE만 수행 중인 것을 알 수 있습니다.mysql> SELECT -> * -> FROM information_schema.INNODB_LOCK_WAITS T101 -> ; Empty set (0.00 sec) mysql> SELECT -> * -> FROM information_schema.INNODB_LOCKS -> ; Empty set (0.00 sec) LOCK을 걸고 걸린 것이 없어 내용도 없습니다.Conclusion지금까지 Transaction Isolation Level 을 기준으로 CREATE SELECT 시 SELECT 에 사용되는 테이블도 LOCK이 걸릴 수 있는 것을 확인했고, 그에 따른 해결 방법까지 알아봤습니다.INSERT INTO SELECT에서도 같은 현상이 나타납니다. 그렇기 때문에 운영 중인 테이블을 복제(CREATE SELECT)하거나 다른 테이블로 옮길 경우(INSERT SELECT) Transaction Isolation Level을 READ COMMITTED 변경하고 작업하기를 권장합니다.그렇지 않으면 관련된 TABLE은 LOCK이 걸리고, 관련 Query들이 대기 상태로 빠지면서 시스템 장애가 발생할지도 모릅니다.참고1)MySQL :: MySQL 5.6 Reference Manual :: 14.7.2.1 Transaction Isolation Levels2)MySQL :: MySQL 5.6 Reference Manual :: 14.7.1 InnoDB Locking글한석종 부장 | R&D 데이터팀[email protected]브랜디, 오직 예쁜 옷만
조회수 1110

The Booth의 시작, 세 친구의 의기투합!

The Booth의 시작, 경리단길 15평 작은 매장! The Booth(더부스)는 2013년 5월, ‘한국 맥주가 대동강 맥주보다 맛없다’라는 기사를 쓴 이코노미스트지의 다니엘 튜더가 기자를 그만두고, 한의사 김희윤, 투자자문사에 다니던 양성후와 함께 의기투합하여 "정말 맛있는 한국맥주"를 선보이기 위해 오픈한 펍입니다.맛있는 맥주를 팔기 위해 뜻을 같이한 세 친구가 모여 경리단길의 15평 작은 매장 벽을 직접 칠하며 매장을 꾸미고, Bill's Pale Ale이라는 단 하나의 맥주를 판매했던 것이 더부스의 작은 시작이었죠! 더부스는 오픈 이후 언제나 "정말 맛있는 한국맥주"를 위해 치열하게 도전했고, 그 과정에서 많은 변화를 겪었으며, 몹시 빠르게 성장했습니다.세계 유명 브루어리와의 콜라보, 그리고 판교 마이크로 브루어리! 2015년 더부스는 Rate Beer 기준 전 세계 Top 3 Brewery인 Mikkeller 와의 공동작업을 통해 북한의 대동강 맥주보다 맛있다고 자신있게 말할 수 있을만한 "대동강 페일에일"을 만들었고, 그 이후에는 세계 Top 9 Brewery인 To Øl과 함께 Wit My Ex를 출시했습니다. 또한 더부스팀은 세계 유수의 브루어리들과의 콜라보에서 만족하지 않고, 더부스 혼자만의 힘으로 국내에서 생산된 적 없는 '실험적인 맥주'를 만들기 위해 한국에서 가장 작은 마이크로 브루어리를 판교에 오픈하였습니다.(희윤과 성후의 결혼식. 왼쪽부터 다니엘, 희윤, 성후.)국내 최초의 수입 맥주 냉장 유통 수입사, Beer for Geeks! 그리고 더부스를 통해 도전하고 성장하는 과정에서, 평생을 함께 하기로 한 다니엘 튜더의 두 친구 성후와 희윤은 2014년 4월 결혼 한 후 '맥주를 마시기 위해' 미국으로 신혼여행을 갔습니다. 신혼여행에서 브루어리 투어를 하며 크래프트 비어 200여가지를 마신 성후와 희윤은, '맥주의 신선함'이 얼마나 중요한지를 깨닫고, 한국에 돌아와 국내 최초로 모든 맥주를 냉장 유통하여 수입하는 수입사 Beer for Geeks를 설립했습니다. 준비하는 과정에서 주위의 모든이가 "냉장 유통은 수지타산이 안맞는 미친짓" 이라고 의아했지만, 결국 "우리가 옳다고 생각하는, 맥주의 신선함을 국내 소비자에게 그대로 전달 할 수 있는" 시스템을 실현시킨것입니다. 더 많은 사람들에게 크래프트 비어의 행복을, The Booth Station! 맥주를 정말 사랑하는 더부스팀은, 더 많은 사람들이 크래프트 비어를 마시고 행복하기를 바랍니다. 그래서 우리는 더 많은 사람들이 Mikkeller, To Øl, Evil Twin, 8wired등의 프리미엄 수입 수제맥주를 누구나 부담없이 친구들과 나눠마시는 날이 오기를 꿈꾸며, The Booth Station을 시작했습니다. 더부스의 다양하고 신나는 도전들을 같이 지켜봐주세요!Make this happen!#더부스브루잉컴퍼니 #창업자 #스타트업창업 #창업가 #인사이트 #히스토리 #경험공유
조회수 930

160310_페이스북 포스팅 복기

목적페이스북을 "오래된 여자친구처럼 대한다"라는 충격적인 피드백을 받았다(페이스북 사랑해). 다시 예전처럼 스위처 페이스북 페이지를 관계를 좋게 하기 위해(페이지 활성화하기 위해) "내가 무엇을 잘못하는지", "앞으로는 어떻게 해야 하는지" 등을 파악하려고 한다. 나아가 이를 원칙을 만들어 앞으로 함께 할 마케터가 배움에 있어 조금이나마 도움이 되면 좋겠다.상황 설명3월 28일 스위처 발매 한다는 내용을 포스팅한 상황나는 3월 28일 정식 판매를 앞두고 매주 2회씩 포스팅을 약속했고, 약속을 지키기 위해 1주 첫 번째 포스팅할 콘텐츠를 기획/제작하려고 했다.  결과포스팅 된 컨텐츠(포스팅 URL : https://www.facebook.com/switcher.io/posts/923636374421388)그 결과물로 위 사진의 콘텐츠를 포스팅하였다. 다음 포스팅 때는 더 효율적이고 효과적인 콘텐츠 생산을 위해 위 콘텐츠 작성 과정을 돌이켜보려 한다.단계 1. 독자 파악독자 파악 단계에서 다음과 같은 질문을 받았다.1. 현재 스위처 페이지 문맥상 어떤 포스팅부터 올려야 하는가?2. Audience는 누구인가?3. Audience의 TPO는 어떠한가?4. 콘텐츠의 목표(결괏값)는 무엇인가?5. 콘텐츠의 목표를 이루기 위해 설계는 타당한가?이에 나는 다음과 같은 답을 냈다.1. 스위처 외관2. 지난번 연락을 보냈던 구매 희망자 + 페이스북 포스팅을 보고 댓글을 남겨준 사람들.3. T : 포스팅 후 개별 연락받고 핸드폰을 확인한 순간.(20:00 이후)P : 메시지(문자/카톡) 받아 보고 링크를 눌러 열린 사이트O : 자신의 할 일을 마치고 숨을 고르는 상황.4. 포스팅 좋아요/댓글/도달 범위5. 네 과거 비슷한 내용의 콘텐츠를 작성했던 방식과 유사하게 설계하였습니다.여기서 문제는 구체적으로 Audience와 그들의 TPO를 말하지 못했다는 점이다. 현재 페이스북 페이지를 '좋아요' 누른 사람은 4000명 정도가 된다. 무의식 중에 이들"도" 위 콘텐츠의 독자(Audience)라고 생각했던 것 같다. 그래서 TPO가 저렇게 엉망진창 (그냥 일반 직장인 정도?)로 나온 것 같다.작살같이 날카로운 타깃팅이 안되니깐 정확히 뭘 말해줘야 할지도 모르니깐 기획단계에서 더 우왕좌왕했던 것 같다. 이 문제를 해결하기 위해 난 2가지 행동을 하였다.a. 페이스북 지난 포스팅 '좋아요' 눌러준 사람 파악지난 포스팅은 누가누가 좋아하셨나독자는 이미 정해져 있습니다. 제가 할 일은 이분들이 누구인지 확인하고 어떤 내용을 궁금해하실지 생각하고 글을 쓰는 것이겠지?한분 한분 살펴보니 대부분 스위처를 기존부터 알고 계셨던 분이셨다. 그렇다면 기존 스위처와 비교했을 때 개선된 부분을 보여주면 좋지 않을까? 생각했다. (우린 고객의 목소리를 중요시하니깐.)b. 고객 문의 파악하기고객 문의그래서 그동안 고객이 겪었던 문제를 다시 읽어보았다. 많은 문제 중 빈도수가 가장 높은 5가지를 선정하여 고객 의견이 반영된 모습을 보여주면 좋을 것 같았다.1. 1구 스위처 -> 2구 스위처2. 전원 on/off 버튼 추가  3. 새로운 부착방식4. 스위처 두께가 얇아짐5. 충전 단자 개선위 5가지 주제를 어떤 식으로 표현하면 좋을까? 생각을 하여 미디엄이 아닌 '카드 뉴스' 형식을 사용하는 것이 좋다고 생각하여 다음과 같은 초안을 준비해보았다.단계 2. 콘텐츠 기획하기손으로 그린 컨텐츠 초안5가지 주제를 포함한 콘텐츠를 만들기 위해 어떤 내용을 만들어야 할 요소를 그려보았다."어떤 내용을 담을 것인가?"에 대한 질문에는 타당한 기획이었지만 "어떻게 표현할 것인가?"에 대한 질문에는 타당하지 못했다.카드 뉴스는 이미지 내에 텍스트를 넣기 때문에 사진의 구도가 중요할 수 있다. 가령 1번 이미지에선 스위처가 가운데에 위치했고 2번 이미지에는 오른쪽, 3번 이미지에선 왼쪽에 위치하여 모든 이미지마다 텍스트 위치가 다 달라져 불필요한 편집이 필요하다. 이로 불필요한 작업시간이 추가되었기 때문이다.다음 포스팅 기획에는 어떤 방식으로 포스팅을 해야 하는가? 질문했을 때, 방식만 생각하는 것이 아니라 선정된 방식을 또 어떻게 표현할 것인가(말이 모호하군) 역시 생각해 보아야 한다고 생각했다.단계 3. 촬영촬영에서의 문제는 혼자 세팅&모델&촬영을 한다는 것이 어려웠다. 그리고 더 나은 사진을 원하다 보니 시간적인 배분에 문제가 있었던 것 같다.앞으로는 촬영 전 도움이 필요한 장면은 팀원에게 미리 양해를 구하고 도움을 요청하고한 컸다(이미지의 경우) 5분의 촬영 시간을 넘기지 않도록 해야겠다. (영상은 촬영하지 않아 모르겠군.)단계 4. 콘텐츠 제작http://tyle.io 라는 좋은 카드 뉴스 제작 사이트가 있다. 근데 글씨 크기 변경이나 이미지가 보이는 방식에는 아직 불편함이 있어, PPT로 제작하는 것이 더 나은 것 같다. '타일'에서 좋은 Form을 확인한 후 PPT에 해당 Form을 미리 제작해두고 이미지에 덮어 씌우는 방식으로 만들면 더 좋을 것 같다.단계 5. 포스팅 하기저걸 왜 못봤지.."publish 버튼"을 누르기 전에 3분간 바람을 쐬고 와서 다시 봐야 할 것 같다.2.8cm.. 왜 못 봤을까.. 왜..틀린 그림 찾기 하듯 Fresh 한 머리로 콘텐츠를 봐야 할 것 같다..단계 6. 결과 분석지난 주 2건의 포스팅 결과값 비교지난주 포스팅한 2개의 포스팅 결괏값을 비교해보았다.LPR은 그냥 만든 용어에요.. 헤헿..제품 디자인 콘텐츠를 올릴 때 '좋아요'를 100개 넘게 받는 것이 목표였는데, 91개밖에 받지 못했다."어떤 콘텐츠가 공유될까?", "콘텐츠의 어떤 요소가 공유를 자극할까?"라는 남규의 질문에 아직 답을 하진 못하겠다. 하지만 '공유'전에 '댓글'을 많이 달 수 있는 콘텐츠가 더 높은 도달률과 많은 좋아요를 달성한다는 것은 알고 있다. (이건 나중에 얘기하는 걸로.)그래서 다음 포스팅에는 '댓글'이 많이 달릴 수 있는 요소를 고민하고 추가해야겠다.-끝-#스위쳐 #Switcher #SNS마케팅 #SNS마케터 #마케터 #마케팅 #페이스북 #페이스북마케팅 #경험공유 #조언 #꿀팁 #고생담
조회수 3640

인스타그램 마케팅 5대 궁금증

내 블로그를 구독하시던 분들은 인스타슈가라는 인스타그램 마케팅 자동화 툴을 약 5개월째 운영중인걸 알고계실텐데 벌써 3400개 이상의 고객사에서 사용중이고 2500만개 이상의 활동데이터가 싸일 정도로 규모가 커져 버렸다. 이렇게 많은 고객사+개인을 상대하며 비즈니스를 하다 보니 인스타그램 마케팅 관련해서 공통된 질문들을 많이 받게 된다. 이 기회에 이를 정리해 보는것도 의미가 있을 것 같아, 지금까지 인스타슈가 라이브챗을 통해 문의받은 약 600여건의 질문내용 중 가장 빈도높은 것 5개만 뽑아봤다. (인스타슈가 비즈니스가 궁금하신 분들은 인스타그램 마케팅 DO & DON'T을 참고해 주세요~)1. 인스타그램 채널 운영하면 매출이 늘어날까요?인스타그램 마케팅을 처음 시작하시는 분들이 가장 궁금해하는 부분이다. 특히 소규모 업체, 쇼핑몰 등에서 가장 궁금해 한다. 아쉽지만 결론부터 말하면 "직접적으로 연결되지는 않습니다" 이다. 우리가 측정한 데이터로는 평균 컨버젼, 즉 팔로워 수 대비 하루 평균 프로필 링크 클릭율이 약 3~7%대에 형성되는 편이다. 이건 링크 클릭만을 산정한거고 여기서 실 구매로 연결되는 컨버젼까지 감안해 보면 사실 인스타그램으로 다이렉트하게 매출을 일으키는 부분은 통상 1% 미만으로 생각하는게 합리적이다. 즉, 인스타는 매출을 직접적으로 발생시킬 수 있는 마케팅 채널이 절대로 아니다. 그럼에도 불구하고 인스타그램 채널로 마케팅을 하는 이유는? 그건 인스타그램이 FAN-BASED MARKETING 채널로서 매출에 미치는 간접적 효과가 어마하기 때문이다.잘 운영되는 인스타그램 채널은 보통 다음과 같은 특징을 지니고 있다.- 팔로워의 모수보다는 실제 교감이 발생하는 활성 팔로워에 집중한다.- 팔로워들과 댓글로 활발하게 대화한다. ("소통해요" "피드 느낌 좋아요" 이런 댓글들을 말하는게 아니다.)- 제품의 직접적인 어필 보다는 이를 일상적 컨텐츠로 잘 녹여낸 사진들이 올라온다. (이건 있다가 더 자세하게 설명하겠음)- (해당 브랜드가 제법 규모가 있어 다양한 마케팅 채널을 운영중이라면) 인스타그램 채널만의 독자적인 톤&매너, 할인행사 등으로 뭔가 팔로우를 유지할만한 가치가 있게 해준다.위 내용 말고도 컨텐츠 하나하나에 쓰는 내용이나 특정 팔로워를 띄워주기 한다던지, 아무튼 인스타 채널을 잘 운영하는 브랜드의 특징은 "인스타그램 계정을 팔로워 한다는 것의 의미"를 가장 충실히 잘 수행하면서 차곡차곡 팬 규모를 쌓아나가서 이를 통해 간접적인 매출효과 뿐 아니라 브랜드의 신뢰도, 로얄고객층 형성 등의 보다 중장기적 목표를 가지고 운영한다는 점에 있다.2. 우리 브랜드 공식 인스타그램 계정이니까 제품이나 브랜드 관련 컨텐츠만 올려야 할까요?생각보다 많은 기업계정들이 이 부분을 상당히 오해하고 있다. 인스타그램에서 저게 본인 브랜드의 "공식 계정"이니의 여부는 전혀 상관이 없다. 즉, 공식계정이라고 제품샷이 도배된 컨텐츠 운영을 해야한다는 강박관념에 빠져서는 안된다는 뜻이다. 본인이 스타벅스급으로 사람들이 알아서 좋아해주는 브랜드가 아니라면 다음 사항을 유념해야 한다.기업계정을 맞팔해주는 경우는 1) 나도 팔로워를 늘리고 싶은데 기업계정들이 가장 맞팔을 잘해주니까 맞팔해주는 경우, 2) 그 기업계정의 컨텐츠가 진짜 마음에 들거나 내가 평소 관심있었던 분야라서 해주는 이 딱 2개 케이스밖에 없는데, 100명이 맞팔을 한다면 1번이 거의 80% 이상의 비중을 차지한다.자, 그러면 1번의 이유로 들어온 사람들을 내 팔로워로 붙들어서 향후에는 내 브랜드에 관심갖게 만드려면? 당연히 그들 피드에서 컨텐츠가 튀어야 하고 그들 피드를 광고성 컨텐츠로 도배해서는 안된다. 이런상황에서 당신이 계속 특색도 없는 제품관련 샷만 도배한다면 당연히 맞팔을 했다가도 언팔율이 높아질수 밖에 없고, 언팔율이 높아지면 당연 팬을 형성하는것도 불가능해진다.그렇다면 어떤 컨텐츠가 좋은 컨텐츠일까? Bad case를 소개하면 해당 기업에서 컴플레인이 올수 있기 때문에 Good case만 몇가지 소개해 보겠다. (아, 참고로 인터넷이나 책같은 곳에서 말해주는 베스트 케이스니 이런거 보면 뭐 대부분 스벅, 나이키 등등 이런급 계정을 얘기하는데, 그들 인스타가 잘 운영되는건 그들의 인스타그램 전략이 좋아서가 아니라 그냥 그 브랜드 자체가 파워풀해서임을 명심해야한다. 내 브랜드력이 저 급이 아닌데 저 계정들을 베스트케이스라고 보고 배우자.. 정말 1도 도움이 안되는 예시다.)1) 제품샷이지만 "인스타틱하고 엣지가 있는" 컨텐츠들이어서 언팔하고 싶은 느낌이 안드는 계정들여기에 해당되는 계정들은 보통 다음과 같은 공통된 특징이 있다.- 엣지가 있다. 즉, 피드에서 확실히 단일 컨텐츠가 튀어보인다.- 잡지에 나오는 정형화된 모델샷들이 아니다.- 제품 말고도 본인 브랜드들에 대한 다양한 스토리를 소개한다.Wear Your Label (@wearyourlabel)SpheroNineteenth Amendment2) 대기업 브랜드 아니고서야 오피셜 브랜드라고 계정도 오피셜일 필요가 있을까? 창업자 본인의 페르소나로 본인 제품을 잘 띄우고 있는 계정들 (특히 패션, 아트 분야일수록 이 전략이 더 유효한 경우가 많다)여기에 소개된 계정들은 보통 다음과 같은 공통된 특징이 있다.- 창업자 본인을 메인 페르소나로 빼서 자기 제품을 띄우고 있다.- 역시 컨텐츠에 엣지가 있다.- 본인의 라이프를 통해 내 브랜드가 어필하고싶은 메시지를 강하게 전달한다.Jordan Washburn (Well-dressed Wayfarer 창업자)Young Bae (Diamond Tatto Studio 창업자) JI Eon Lee (하플리 창업자)3) 컨텐츠도 나쁘지 않으면서, 팔로워 한명한명을 마치 내 상점에 방문하는 고객을 응대하는것 처럼 정성을 다해 소통하다보니 팔로워 수에 비해 계정 활성도가 매우 뛰어난 계정들여기에 소개된 계정들은 보통 다음과 같은 공통된 특징이 있다.- 사진에 달리는 댓글 하나하나에 영혼없는 응대가 아닌 실제 보이스로 반응해준다.- 적은 규모라도 팔로워들만 대상으로 다양한 이벤트를 상시 운영해서 이 계정을 팔로워할 가치가 있게 한다.- 팔로워 규모가 크지 않아도 댓글이나 (소통해요~ 이런댓글 말고) 라이크수가 왠만한 K찍힌 계정들을 능가한다. (사실 이런 계정들이 진짜 알짜배기 계정들이라 할 수 있음)김해 금란다원 (@twiny2k)랩노쉬 (@labnosh_official)페티앙북스 (@petianbooks)사실 1번, 2번 케이스는 크리에이티브도 뛰어나야 하고, 모델의 역할이 매우 중요해서 스타트업이나 자영업 계정에서 시도하기에는 조금 어려운 면이 있다. 하지만 3번 케이스의 경우 누구나 조금만 노력을 기울이면 쉽게 활성팔로워들을 키워나갈 수 있고, 이들 중 반드시 당신 브랜드의 소비자로 전환되는 루프가 형성될 수 있으니 인스타그램 마케팅을 막 시작하신 분들은 꼭 3번 케이스를 중심으로 연구하길 바란다.3. 팔로워 수가 많을수록 우리 컨텐츠 노출도 비례해서 많아지나요?결론부터 말하면 "그렇지 않습니다"이다. 유저수가 많지 않고 노출 알고리즘이 단순했던 인스타그램 초창기에나 내가 컨텐츠를 포스팅하면 시간순으로 팔로워들 피드에 모두 노출이 됐지만 페이스북에 인수된 이후 지금은 어마어마하게 복잡한 노출알고리즘이 운영중이다. 특히 2016년 3월을 기점으로 어마어마한 변화, 즉 팔로워들의 포스트를 시간순으로 배열하던 방식을 완전히 버렸는데, 이에 대한 내용은 인스타그램의 공식 공지사항을 읽어보자.2016년 3월, 인스타그램은 기존의 시간순 노출을 버리고 새롭게 태어났다.사실 위의 글만 가지곤 인스타그램 알고리즘이 도대체 어떻게 진화하고 있는지 알길이 없으니, 수 많은 사람들의 분석글을 참고해 볼 필요가 있는데, 이 글들을 다 읽어보라고 하면 욕먹을 수 있으니 당신이 기억해야 하는 가장 중요한 내용을 정리해 보면 다음과 같다. 단계별 반응도에 따른 포스트 퀄리티 인덱스 (QI)가 일정 수준을 넘지 않으면 노출량이 줄어든다.무슨 말이냐면, 예를들어 팔로워가 100명이면 100명에게 모두 컨텐츠를 노출하지 않고 평소에 내 사진에 반응이 자주 있었던 사람들, 또는 내 프로필을 자주 방문했던 사람들 위주로 일부 노출한다. 여기서 일정비율 이상의 초기 반응을 얻는 포스트는 QI값이 높아져서 그 다음 그룹에 노출되고, 또 반응이 좋으면 그 다음 그룹... 이렇게 사다리 타기 방식으로 노출이 된다. 따라서, 당신의 QI값이 별로라면 팔로워가 아무리 많아도 노출이 잘 안될 수 있고, 당신의 QI값이 월등하면 적은 팔로워로도 노출 짱짱맨이 될 수 있다. 시중에 있는 유령/허위 네트워크로 5분만에 팔로워 찍어주는 서비스를 절대로 사용해서는 안되는 이유가 바로 여기에 있다. 팔로워가 만명인데 반이상이 허위 팔로워 찍혀있는거면 당연히 QI가 안나오니 컨텐츠 노출이 잘될리가 없다. 보통 팔로워가 막 K 찍혀있는데 컨텐츠에 라이크가 막 50개도 안달려 있거나, 컨텐츠에 라이크가 막 1000개 넘게 찍혀있는데 댓글은 가뭄에 콩나듯 달린 포스트들은 백퍼 이런 케이스에 해당한다.포스트 노출에 가장 중요한 요소는 양적인 팔로워 수 보다는 "활성팔로워 수"인데, 이게 높아야 QI지수가 높아지기 때문이다.** 인스타그램 노출 알고리즘 관련 분석글들 중 가장 잘된것만 몇개 추려봤으니 궁금하신 분들은 아래 링크 글들을 참고해 주길 바란다. (제법 out-dated 된 글들도 있으니 알아서 취사선택 바람)Instagram's got a new way to determine which photos show up in your feed — here's how it works (인스타그램 관계자가 한 말이라 제법 신뢰도가 있는 글)Understanding the Instagram Algorithm: 7 Key Factors and Why the Algorithm is Great for Marketers (위 글을 기반으로 나름의 상상력을 펼쳤는데 제법 그럴싸 해 보이는 글)How the News Feed Algorithms Work on Facebook, Twitter & InstagramHow do news feed algorithms work? (Quora, 첫번째에 있는 Abhinav Sharma의 답변을 참고하자)4. 컨텐츠 올릴때 해시태그를 많이 달 수록 노출이 많아지나요?결론은 "그렇지 않습니다" 이다. 필자도 사실 옛날에는 그런줄 알았다. 해시태그를 달면 해시태그 검색에 걸리고, 해시태그 서핑을 타고 들어온 오가닉 유입, 혹은 얻어걸린 탑 포스트에서 들어오는 유입이 많아질 거라 생각했는데, 어느날 우리 개발자가 "저렇게 해시태그 스팸질을 하는 꼴을 인스타가 그냥 놔둘것 같진 않은데 우리 한번 제대로 파보자" 해서 한달동안 인스타슈가를 통해 쌓인 수천만건의 DB를 분석했더니, 컨텐츠에 달리는 해시태그 수랑 노출량은 전혀 관계가 없다는 결과가 나왔다. 그 이유를 나름 추정해 봤는데, 인스타그램에서 해시태그 스패밍 이슈 해결을 위해 한 포스트에 태그를 많이 달수록 각 태그별 웨잇을 나눠서 분산시키기 때문이다. 무슨 말이냐면, 태그를 0-1개를 달때 1이라면, 10개를 달면 1/10이 10개가 되서 결국 1이 되는 개념이다. 이에대한 자세한 분석 결과는 우리 개발자가 쓴 인스타그램 #해시태그는 많이쓸수록 좋을까? 글을 참고해 주길 바란다.이렇게 해시태그 스팸 + 복붙할 시간에 컨텐츠나 댓글에 신경쓰자5. 라이크를 많이 받아야 탑 포스트에 노출되나요?이것도 결론은 "반드시 그렇지만은 않습니다" 이다. 탑 포스트 노출 로직은 더 베일에 싸여 있는데, 예전에 레딧의 한 유저가 실험을 해 본 글이 유명하다 - How to get to Instagram "top-posts" almost instantly (물론 2년전 글이기도 하고 개인이 자기 계정으로 실험해본거니 신빙성이 떨어지나, 그 로직 자체는 그럴싸 하다) 이 글에 의하면, 컨텐츠를 포스팅 한 후 24시간 이내에 라이크를 일정 수준 이상 받게되면 탑 포스트에 올라가고 (이걸 Growth Index라고 부른다), 한 해시태그에서 일정 기간동안 GI가 높은 포스트들을 번갈아 가면서 보여준다. 즉, 단순히 포스트에 라이크가 가장 많다고 탑 포스트에 계속 올라가 있는게 아니라는 뜻이고, 실제로 필자도 몇번 실험을 해봤는데 이 글의 로직이 어느정도 신빙성 있다.이제 탑포스트에 올라가는것 조차 복잡한 알고리즘이 돌아가고 있고, 계속 GI를 측정하면서 순환되기 때문에 굳이 비싼돈 들여 허위 라이크 구매하는 서비스를 사용할 필요가 없어졌다.지금까지 인스타슈가를 통해 접수되는 질문들 중 사람들이 가장 궁금해하는 토픽 5개를 뽑아 정리해 봤다. 아무쪼록 이 글이 위와 같은 질문을 상습적으로 받는 인스타그램 마케팅 담당자님들 (또는 광고주를 상대하는 대행사 분들)에게 조금이나마 도움이 됐으면 좋겠다.여기 아래부터는 우리가 서비스하는 인스타슈가라는 인스타그램 마케팅 자동화 솔루션을 (대놓고) 광고하려고 하니, 광고에 알러지가 있으신 분들은 여기서 창을 닫아주시기 바란다. (제발 "다 읽어보니 기승전광고네"라고 욕하지 말아주세요 ㅠㅠ)본인 브랜드가 스벅, 나이키 급도 아니고, 그렇다고 엄청난 컨텐츠력이 있는 상황도 아닌데 완전 제로베이스에서 인스타그램 팔로워를 늘려나가는건 결국 엄청난 노가다 작업이 수반된다. 노가다 작업이란건 뭐냐면, 맞팔류 태그들이나 빅태그들을 돌아다니면서 내 비즈니스 타겟에 맞는 사람들 계정을 선팔하며 돌아다니다 보면, 그들 중 일부가 맞팔을 해주게 되고, 이렇게 생긴 팔로워들을 잘 관리하면서 꾸준히 노가다를 하면 계정 활성도가 높은 몇천명대 인스타 계정을 누구나 만들수 있는 방법인데, 참 사람이 하기에는 너무 불쌍하고 고된 작업이다.시중에 이런 작업을 대신 해주는 프로그램들을 돈받고 파는 업체들이 매우 많이 있으나, 대부분은 다음과 같은 문제점을 앉고 있다.1) 설정해놓은 태그에서 최신 포스트의 계정을 무작위로 선팔하기 때문에 성인, 스팸계정, 내 비즈니스와 전혀 상관없는 계정들이 대거 걸리게 된다.2) 인스타그램에서는 사실 위와 같은 프로그램의 사용을 정책상 금지하고 있어서 계정별로 활동 리밋을 12-24시간 기준으로 정해놓는데, 물론 공개된 데이터가 아니다. 위의 프로그램들은 대부분 이런 리밋을 고려치 않고 설계되있고, 유저가 속도를 임의로 조절하면서 돌리도록 되있는데, 이러다가 계정이 기능블락에 걸리다가 심한 경우 disabled 먹는 경우도 다반사다.3) 비즈니스 계정들이 대거 걸린다. 내가 B2B 하는거 아니라면 대부분 인스타에서 "소비자"를 팔로워로 타겟하고 싶을텐데, 맞팔로 걸리는 계정들이 거진 "비즈니스"계정들이라, 심지어 같은 경쟁사들, 동종업체들끼리 모두 맞팔이 되어있는 경우도 다반사다.아무튼, 위에 언급한 문제 말고도 프로그램을 써서 팔로워를 늘리는거에는 정말 수만가지의 문제점들이 있어서, 작년에 우리가 직접 사용할 스크립트를 만들어 우리 계정 + 주변 스타트업 계정들 도와주다 보니 엄청난 퀄리티를 자랑하는 스크립트로 유명해져서 아예 정식 비즈니스가 된게 바로 인스타슈가라는 솔루션이다.인스타슈가 - https://instasugar.co/<iframe width="940.000000" height="529.000000" src="//play-tv.kakao.com/embed/player/cliplink/vdf62MgDwepuMGxRDaeyxpN@my?service=daum_brunch§ion=article&showcover=1&showinfo=0&extensions=0&rel=0" frameborder="0" allowfullscreen="">이 솔루션이 월등한 이유는 다음과 같다.1. 40여가지 이상의 기준으로 타겟할 유저를 결정2. 머신러닝 기반의 봇계정이 돌아다니며 수집하고 있는 160만건 이상의 성인, 스팸계정 DB를 통해 99.8%의 정확도로 스팸계정 필터링3. 해당 계정이 개인 계정인지, 비즈니스 용도인지를 검증하여 비즈니스 필터링 모드가 on 되어 있으면 비즈니스 계정들을 94%의 정확도로 필터링4. 인스타그램의 활동 리밋양을 추정하고 이 범위 내에서 최대효율을 내는 확률모델을 통해 가장 팔로워 전환 확률이 높을것으로 추정되는 계정들만 타겟함5.대시보드 -  현재 프로그램이 움직이는 로그, 타겟팅 해시태그 설정, 프로그램의 상태, 시작 및 정지, 다양한 특수 기능들을 모두 실시간으로 확인 & 통제 가능6. 안정성 - 해당 계정에 기능블락이나 특정 이슈가 생기는걸 실시간 감지하여 자동 정지, 속도 조절, 자동 재생 등이 통합적으로 이루어짐인스타슈가 사이트 회원가입 후 계정 연결하면 무료로 체험해 볼 수 있는 10명 토큰을 지급하고 있으니 관심있으신 분들은 체험해 보길 바란다. (무료라고 기능이 딸리고 그런거 아님. 유료나 무료나 동일한 토큰을 지급해 드립니다.)인스타슈가 웹사이트 바로가기
조회수 3920

마케터가 알아야할 스토리텔링 카드뉴스 가이드라인

마케터는 한번쯤은 만들어본 카드뉴스강연을 다니면서 끄적끄적 적어본 이야기를풀어보도록 하겠습니다.그동안 들어본 강연은IBM마케팅 AWAKE / 열정에 기름붓기 대표님들이 알려주신 이야기이며,강연을 들으면서 정리한 이야기 입니다.No.1 알아야할 팁 스토리텔링 컨텐츠를 만드는 "AWAKE"대표님께서 엄청난 시간과 투자를 하면서결론을 낸 것은 3개의 포맷이다.1. 영화관의 느낌을 주며 테스트탑/모바일 등 웹 등에 적용이 용이하다.2. 대표적인 포맷 / 모든 면에서 모바일에 최적화된 포맷이며 3개 중 안정감과 몰입감을 전달해준다.3. #1 보단 사이드배경의 검은색이 적지만 몰입감에 최적이다.No.2 알아야할 팁 카드뉴스 제작시 도달 위치에 놓여있는 유저들이 보이기에가장 최적화된 방법이라고 생각하시면 됩니다.이런 케이스로 운영되는 페이스북 페이지는'열정에 기름붓기''AWAKE''책 끝을 접다'스토리텔링 컨텐츠 페이지에서많이 이용하는 케이스입니다이런 포맷형식도 수많은 시간/노력을 투자해서만든 방법이니 한번 시도해보는 것도 좋을 것 같습니다.No.3 알아야할 팁 많은 마케터분들이 고민하시는 단계라 생각됩니다.이미지를 보면 뒷배경이 안정적으로 되어있으면구독자의 시각에서 편안함을 느끼며,텍스트 내용이 집중될 수 있다고 합니다.No.4 알아야할 팁사진을 보면 44"%저건 모두 같은 이미지를 활용한 카드뉴스이지만자극적인 빨강색 A여성의 얼굴B도달률 테스트를 한 결과입니다.여자이미지가 개인적으로 높을 줄 알았지만'타임라인의 승부는 0.3초'이목을 끌려면 자극적인 색이효과적이라고 생각되네요.No.5 알아야할 팁그러면 카드뉴스 기획 및 제작 시에4개의 동그라미를 참고하시면서 스토리를 어떻게 끌어갈 것인지 고민해보시면 좋을겁니다.참고로 '열정에 기름붓기'정말 진심과 콘텐츠적으로성공적인 페이지라고 생각됩니다.만든이의 감성이 전달되기 때문이죠.그래서 페이지 리서치를 통해서 연구해보는 방법도 좋을 것 같구요(히히)이만 건포어였습니다^^#오누이 #마케터 #마케팅 #카드뉴스 #인사이트 #마케팅기획 #성장
조회수 1210

말 잘하는 공대생 되는 법

 남고를 나와서 공대, 군대를 거쳐 결국 엔지니어라는 진로를 택한 저는 정말 말을 못 합니다. 그리고 비슷한 과정을 밟아온 제 친구들도 그렇고 저와 함께 일하는 동료 엔지니어 분들도 마찬가지입니다. 회의를 할 때마다 느끼는 점은 엔지니어들은 말이 별로 없어요. 그리고 한 번 말을 하게 되면 말도 안 되는 말을 지껄이다가 무자비하게 까여요. 그리고 집에 와서 이불 킥을 하며 "아, 원래 이런 말을 해서 이렇게 전개되었어야 하는데."라고 중얼대다가 잠들죠. 그리고 이런 상황이 반복되다 보면 결국 말없는 공대생이 되고 맙니다. 저도 공대생으로서, 엔지니어로서, 여러분의 고충을 천 번 만 번 공감하고 저의 경험에 비춘 실질적인 방법들을 아래 제시해 보았습니다.말하는 태도1. 한 박자 쉬기 말 못 하는 공대생의 특성상 미리 생각했던 말, 준비했던 말을 모두 온전하게 전달하려고 애를 쓰게 되는데요. 그러다 보면 쉬는 틈 없이 말을 다다다 하게 되는 것이 사실입니다. 상대방의 이해보다는 내가 하고 싶은 말을 다 해야 한다라는 것에 포커스를 맞추게 되지요. 이럴 때는 한 박자 쉬는 것이 좋습니다. 상대방에게 휴식 시간을 줄뿐더러 나에게도 앞으로 어떻게 말해야 좋을지 생각할 수 있는 시간이 될 수 있기 때문입니다. 또한 한숨 돌리면서 천천히 목소리를 한 톤 낮춰서 설명하면 더욱 설득력이 높아집니다. 흥분으로 높아진 목소리와 빠른 말투는 상대방에게 안 좋은 인식을 줄 수 있고 분위기에 긴장감을 조성할 수 있습니다. 만약 조금 오래 생각해야 할 필요가 있다면 정중하게 "잠깐만요, 생각 좀 해볼게요"라고 양해를 구하고 어떻게 문제를 해결하고 설명해야 할지 시간을 갖는 것도 좋은 방법입니다. 이런 이야기를 한다고 해서 상대방은 절대 여러분을 무시하지 않을 것이며 더욱 신중하게 일을 처리하는 사람으로 생각할 가능성이 높아집니다.2. 웃으면서 이야기한다 여러분과 이야기하는 상대방은 여러분을 평가하는 사람이 아닙니다. 그들은 우리의 적이 아니라 친구이며 여러분의 팀원, 가족, 친구라고 생각해보세요. 상대방이 이해를 못하더라도 기분 나빠하거나 무시하지 말고 웃으면서 설명해주세요. 더욱 분위기가 좋아지고 여러분은 이야기하는 것이 즐거워질 거예요. 3. 저격하지 않는다 절대! 상대방을 저격하는 말을 하지 않습니다. 상대방도 바보가 아닌 이상 여러분이 자신을 향해 그런 말을 한다는 것을 알아차릴 가능성이 높습니다. 저격당한 상대방은 여러분에게 적개심을 가지게 되고 이야기는 잘 풀리지 않을 가능성이 높아져요. 특히 여러 명이 있는 자리에서는 더욱더 기분 나빠할 거예요. 또한 그 상대방과의 이야기를 나눌 다음 자리에서도 상대방은 이미 저격을 한번 당했기 때문에 여러분에게 호의적이지 않을 거예요.4. 상대방이 먼저 말하게 한다 어떤 질문이 들어왔을 때 구체적인 생각이 없거나 전략적으로 판단해야 한다면 곧장 내 의견을 말하기보다는 상대방의 의견을 먼저 물어보고 그 사이에 생각할 시간을 갖는 것도 방법입니다. 상대방이 "이건 어떤가요?"라고 물어보았을 때 (이미 답을 알고 있더라도), "흠, 글쎄요. 당신은 어떻게 생각하시나요?"라고 역으로 물어보며 상대방의 의중을 파악하고 생각할 시간을 가지세요.5. 콘텐츠가 중요하다 간혹, 원래 말을 못 하는 성격이라 또는 말할 때 더듬는 버릇이 있어서 말하기를 꺼려하시는 분들이 종종 있습니다. 그런 분들은 자신의 말하는 모습 때문에 마이너스가 될 거라고 생각하는 경향이 있습니다. 그러나 말할 때 중요한 것은 겉모습보다 내용입니다. 사람들은 내용에 집중하지 여러분의 말하는 모습에 집중하는 것이 아니거든요. 좋은 아이디어가 있다면 용기 있게 먼저 말을 꺼내보세요. 상대방은 들을 준비가 되어있고 여러분의 겉모습보다는 여러분의 훌륭한 아이디어에 공감할 테니까요화술1. 절대 A가 아니다 > A보다 B가 더 좋은 것 같다 공대생들의 특징 중 하나로 절대, 반드시라는 말을 자주 쓰는 경향이 있습니다. 자신의 의견을 확고히 하려는 어떤 무의식의 하나로 생각되는데요. "절대"라는 말은 이야기를 하는 데 있어서는 너무 강하고 안 좋은 말입니다. 상대방에게 부정적으로 느끼게 할 수 있으며 아니라고 말을 들은 이후에 "그래서 뭐 어쩌자고"라는 식으로 생각할 수도 있으니까요. 강한 부정보다는 A보다 B가 더 좋다는 식의 해결책을 제시하면서 이야기를 이끌어나가는 면이 더 좋습니다. 여기에 근거를 덧붙이면 더 좋고요. 이것의 응용 버전으로는 "A보다 B가 상대적으로 더 ~하다"라는 방법이 있습니다. 절대적보다는 상대적이라는 단어를 써서 서로 생각하는 정도의 차이를 좁혀나가는 편이 좋습니다.2. ~이 맞다 > 나는 ~라고 생각한다. 왜냐하면 ~이기 때문이다 1번과 같은 맥락에서 강한 표현보다는, 이 의견은 자신의 생각이라는 점을 부각하는 방법입니다. "이게 맞으니까 그냥 따라와" 보다는 "이렇게 하면 이런 것들이 좋으니 이렇게 하는 게 좋겠다고 생각해"라고 얘기하는 편이 듣는 사람도 좋겠죠. 또한 이 방법은 혹시나 내가 틀릴 경우에 대비해서 가장 좋은 도피처(?)가 될 수도 있답니다.3. 이건 안됩니다 > 이 방법은 힘들 것 같네요 특히 영업하시는 분들과 개발하시는 분들 사이에 많이 오고 가는 대화일 것 같아요. "고객이 이렇게 해달래요"라고 영업맨이 이야기합니다. 그러면 개발자는 "이거 안되는데요"라고 대답하지요. 그러면 뒤에 가서 서로에 대해 이렇게들 얘기합니다. "아니, 뭐 맨날 안된대", "저게 그냥 되는 줄 아나 봐" 안된다는 강한 부정은 듣는 사람도 언짢습니다. 그보다는 "그거요? 지금 어떤 이슈가 있는데 그것 때문에 조금 힘들 것 같아요"라고 얘기해보는 건 어떨까요?4. 당신의 말도 맞지만 이 방법도 있습니다. 한 번 들어보시겠어요? 상대방이 틀린 말을 했다고 했을 때, 또는 여러분이 틀린 말을 다른 사람에게 했을 때, 틀린 말을 한 사람 입장에서 가장 당혹스러운 순간은 말한 자신이 틀린 것을 증명받는 순간입니다. 그래서 자신의 의견이나 주장이 틀렸을 때 굉장히 당황하며, 어떤 경우에서는 오히려 화를 내게 되는 경우도 있죠. 상대방은 바보가 아닙니다. 그 사람도 여러분으로부터 더 좋은, 더 맞는 의견을 들었을 때 여러분의 의견이 더 좋다는 것을 깨달을 것입니다. 그렇지만 그 상황에서 "거봐, 내 말이 맞잖아"라는 식으로 나오면 상대방은 더욱 인정하기 싫어지겠지요. 틀린 말을 한 상대방을 친구라고 생각해봅시다. 그리고 이렇게 말해봐요. "당신의 말도 맞지만 이런 방법도 있는데 한번 들어보실래요?" 라구요. 상대방은 기꺼이 당신의 말을 들으려고 할 것입니다. 그리고 상대방에게 틀렸다고 납득시키지 말고 선택권을 주세요.5. 당신이 몰라서 하는 말입니다 > 겉으로 보기엔 그렇게 생각할 수도 있지만 사실 잘 살펴보면 이렇습니다 4번과 같은 맥락에서 상대방이 틀린 의견을 말했을 때 대처하는 방법입니다. 상대방의 무지를 탓하기보다는 알려주는 방향으로, "이걸 어떻게 모르지?" 보다는 "아직 모르니까 이렇게 말하는 거겠지"라고 알려주는 방향으로 이야기해보면 더 즐겁게 이야기할 수 있을 거예요. 다음에 여러분이 모르는 것이 생겼을 때 그 사람이 친절하게 알려준다면 더욱 감사하겠지요. 사실 위에서 말한 방법들은 대부분 말을 잘하는 화술에 대한 것보다는 사람을 내 친구처럼 생각하고 살갑게 대하는 방법과도 같습니다. 왜 친구들이랑은 말이 잘 통하는데 회사 선임과는 말이 안 통할까요? 회사 선임은 친구가 한 명도 없어서 일까요? 이미 깨달으신 분들도 계시겠지만, 먼저 상대방에게 마음을 열고 실수를 용서할 수 있는 사람은 상대방의 마음을 열 수 있고 서로 오해가 생기지 않도록 얘기할 수 있어요. 그 사람이 실제로 답답해서가 아니라 내가 이미 그 사람을 나쁜 사람으로, 답답한 사람으로 생각하고 있기 때문에 트러블이 발생하는 경우가 많죠. 대화하는 상대방을 친구로 생각하고 먼저 마음을 여는 사람은 그 사람의 호감을 자동적으로 얻을 수 있으며 대화를 잘 하게 된다는 점을 기억하세요. 그리고 상대방의 말에 귀 기울이는 습관을 가지세요. 자, 당신이 생각하는 말 잘하는 방법은 무엇인가요?#비주얼캠프 #인사이트 #경험공유 #조언 #개발자 #개발팀 #꿀팁

기업문화 엿볼 때, 더팀스

로그인

/