스토리 홈

인터뷰

피드

뉴스

조회수 804

스푼 일본팀의 마스코트 CS담당 호마레를 만나보세요!

스푼을 만드는 사람들 10번째 이야기보노보노의 포로리를 닮은, 사랑스럽고, 일본팀에 밝은 꽃 같은 CS 담당자 '호마레'를 소개합니다.먼저, 글에 들어가기 앞서 'SPOON JAPAN TEAM' 은 서울 및 도쿄에서 근무하고 있음을 알려드립니다.*호마레는 서울 지사에서 근무를 하고 있는 멤버입니다탁구 치는 열정 마레..호마레를 '마레찌' 라고 불러주세요!Q. 일본 팀원들이 다들 호마레를 '마레찌'라고 부르던데..A: "하하하.. 어떻게 아셨어요? 혹시 가수 '세븐틴' 아세요? 제가 세븐틴 팬인데, 일본에서 세븐틴이 '세븐찌'로 불리거든요. 그래서 저희 팀원들이 저에게 별명을 지어줬어요."가장 좋아하는 음식: 오므라이스'Homare' 당신이 궁금합니다.Q.  사내 탁구왕, 다트 왕이 되신 계기가 있나요?(스푼에는 사내에 탁구대와 다트 기계가 있다)"저는 어렸을 때부터 여러 가지 운동을 했었어요. 수영도 배웠고, 배구랑 테니스도 했었고.. 운동을 좋아하는 편이에요! 그리고 보통 일본에선 체육을 좀 적극적으로(?) 가르치는 것 같고요. 무엇보다 제가 다녔던 학교가 '배구'로 유명했던 곳이라서, 배구도 좀 오래 했었고요. 전 스포츠류는 다 좋아하는 것 같아요. 무엇보다 공으로 하는 스포츠가 제일 좋고요! 제가 탁구왕과 다트 왕이 된 건.. 글쎄요? 전 재미로 하는데.. 그래도 이기면 재미있잖아요? 하하.. 그리고 특히 Jay (스푼 CSO)를 이겨야 재밌어요. 그분이 지는 건 싫어하셔서 그런지, 그분을 이기면 너무 재밌더라고요!" a.k.a 제이 잡는 호마레..Q. 3개 국어를 하신다고 들었습니다 (능력자 of 능력자)"아니에요!(겸손) 음, 일단 저는 사실 어머니가 한국분이세요. 아버지가 일본분이시고요. 비록 저는 일본에서 태어나서 쭉 일본에서 자랐지만, 어릴 때부터 쭉 한국어를 배워왔어요. 그러다 보니까 자연스레 한국어도 유창하게 되었고 영어는 한국어만큼은 잘하진 못해요!"Q. 가장 좋아하는 음식과 싫어하는 음식이 뭔가요?"저는 오므라이스를 정말 좋아해요! 그 한국식 오므라이스 말고, 계란 반으로 자르면 양 옆으로 흘러내리는 그 일본식 오므라이스요! 그리고 싫어하는 음식은, '날 것' 이요! 하하.. 제가 일본인이라 이 말 하면 다들 놀라시는데 저는 스시를 못 먹어요. 날 생선과 육회를 못 먹거든요. 하지만 캘리포니아롤은 좋아한답니다."왠지 모르겠지만 이분과 사내 탁구 라이벌 관계 - Jay  당신의 회사생활이 궁금합니다Q. 현재 하고 맡고 계신 업무가 어떻게 되세요?"저는 스푼 일본팀(한국지사)에서 환전 업무를 맡고 있고요. 유저들의 메일에 응답 및 모니터링도 맡고 있습니다. 종종 모니터링하면서 좋은 콘텐츠를 찾기도 하고, 트위터를 통해서 그런 좋은 콘텐츠를 소개하기도 해요. 제가 재미있는 것 찾는 걸 좋아해서 좋은 콘텐츠 찾는 게 정말 재미있더라고요. 트위터에서 유저들 반응 보는 게 진짜 재미있어요!" (호마레 마케팅팀으로 부서 이동하셔도 될 듯)Q. 독특한 이력이 있다고 들었습니다."스푼에 입사하기 전, 저는 NGO(비영리 단체)에서 오랫동안 일을 했었어요. 원래 저의 어릴 적 꿈이 UN에서 일하기였거든요. 그래서 대학 전공도 그쪽으로 했고, 영어도 그 계기로 공부했던 것 같아요. 해외서 일하고 싶다는 꿈? 이 있었어요. 처음에 대학을 다니는 동안은 책으로만 배우다가, 우연히 지인의 아버지께서 계신 NGO를 알게 되었고, 그 계기로 필리핀에 처음 해외봉사를 갔었어요. 새로운 사람들 만나는 게 정말 재미있더라고요. 그게 시초였던 것 같아요. 그 후에 필리핀, 한국, 네팔, 캄보디아 그리고 인도네시아를 오고 가며 일을 했었어요. 저의 주 업무는 봉사활동 오시는 분들 코디네이터 겸, 사전 답사를 했었고요. 그렇게 몇 년이 지난 후, 조금 더 다양한 커리어를 만들고 싶다는 생각에 한국에 와서 저의 언어를 바탕으로 할 수 있는 일을 찾다가 마이쿤(스푼 라디오)에 입사하게 되었어요."Q. 일본팀이 항상 행복할 수 있는 비법은?"하하, 저희가 자주 웃어서 가끔 시끄러우셨죠? 예전에 아침마다 팀마다 스크럼 끝에 사진을 찍었었는데, 그때 저희는 재미있고 다양한 필터가 있는 어플을 썼었거든요. 그래서 매일 아침마다 서로 그런 거 보면서 웃었던 거 같아요. 그리고 아무래도 문화적인 특성도 있는 것 같아요. 다들 서로 배려해주는 문화랄까요? 그래서 늘 다들 서로 웃어주려고 노력하는 것 같아요. 무엇보다 팀원들이 다들 너무 좋아서 행복한 면도 있고요"Q. 하루 중 가장 좋아하는 시간이 언젠가요? (퇴근 시간 제외 (^o^)/)"저는 점심시간이요. 왜냐면 점심시간에 다른 팀원들하고 함께 식사도 할 수 있고, 함께 커피 마시러 갈 수도 있어서 서로 알아갈 수 있는 좋은 시간이라고 생각해요. 아무래도 다른 시간엔 다들 업무에 집중하시니, 다른 팀원들과 함께 말할 기회가 없는 것 같아서요."Q. 회사생활 중 가장 기억에 남는 에피소드가 있다면?"저희가 작년 말에 일본 유저 설문조사를 진행했었는데요. 질문 하나를 만드는 것이 얼마나 어렵고, 시간이 오래 걸리는지 처음 알았어요. 유저가 불편해하는 부분을 찾는 목적의 설문조사였는데.. 어떻게 해야 원하는 답변을 얻을 수 있는지, 어떻게 접근을 해야 하는지 고민을 많이 했었어요. 그래서 그런지 기억에 많이 남아요. 쉽다고 생각했던 부분들이, 알고 보면 어렵고 오래 걸릴 수도 있겠구나라는 깨달음도 얻었고요"Q. 어떤 사람과 근무하고 싶으세요?시야가 넓은 사람이요. 주변을 둘러볼 줄 알고, 주변 사람들과 잘 어울릴 수 있는 그런 사람이요.(視野が広い人ですね。周りの見渡し方とゆうものを知っていて、色んな人と良く付き合うことの出来るそんな方です。)당신의 사생활이 궁금합니다Q. 한국과 일본의 큰 문화 차이가 있다면?"있어요! '약속문화'에 관한 건데요. 일본 - 친한 친구 와도 최소 일주일 전에 약속을 잡고 만남.한국- "오늘 뭐해? 나와!" 이 부분이 정말 다른 것 같아요. 일본에서는 친한 친구 와도 미리 약속을 잡고 만나는 문화(?)가 있어요. 근데 한국에서는 보통 갑자기 밥 먹자고 한다거나, 만나는 경향이 있더라고요. 그 부분이 신기하고 달랐어요. 한 번 에피소드가 있는데, 갑자기 Jay가 오늘 저녁 먹자고 하시는 거예요. 그래서 제가 "저는 당일 약속은 안 잡아요"라고 말했었었어요. 일본에서는 미리 알려주고, 서로 동의하에 약속 잡는 게 배려(?)이자 예의라고 생각하거든요. 아무래도 거절을 잘 못하는 문화다 보니, 만나고 싶지 않아도 Yes라고 말할까 봐 애초 미리 서로 시간을 정해놓고 만나는 것 같아요. 이 부분 말고 크게 한국과 일본이 다르다고 생각한 적은 없어요."Q. 해외생활 힘들진 않아요?"저는 사실 스푼에 입사하면서, 한국이 저에게 외국 같다고 생각한 적이 없어요. 이곳에서 아무도 저를 외국인이라고 느끼게 하지 않거든요. 그냥 동등하게 저를 대해 주시고, 호마레라는 한 사람. 그 인격체로 저를 바라봐주셔서 그런 것 같아요. 저는 그 부분이 너무 좋아요. 단 한분도 저에게, 제가 일본인이라서 무례한 질문을 하신다거나 대답하기 난감한 걸 물어보신 적이 없어요. 그게 정말 감사한 것 같아요. 일본인 호마레가 아닌, 그냥 호마레 자체로 대해주시니까요."Q. 일본이 언제 가장 그립나요?"음, 일본은 친구들이 그리울 때 가장 그리워요. 그래서 일 년에 한두 번 정도 왔다 갔다 한답니다. 그거 말고는 저는 사실 한국에 사는 게 좋아요. 어느 나라에서 일하는 게 중요하진 않지만, 회사에 따라 다를 것 같아요. 뭔가 이유 없이 억압적이고 수직적인 분위기를 좋아하지 않거든요. 일본엔 그런 기업이 대다수라서, 저는 그런 게 맞지 않아서 한국에서 사는 게 좋은 것 같아요."포로리 닮은 마레찌스푼 일본팀이 호마레를 한마디로 표현한다면?Emika 曰:  SPOON의 마스코트 캐릭터(SPOON の マスコットキャラクター)"많은 직원들한테 사랑을 받고 있는 호마레는 마치 스푼의 마스코트 같아요!"Emily 曰:  보노보노의 포로리 - "뭔가 닮은 느낌도 들고 귀여운 목소리가 포로리 같아요"Kaho 曰:  안심장치 - "바로 제 옆자리에 계시는데 마음이 따뜻해지고 안심이 됩니다"Haru 曰: 세븐틴 덕후 오브 덕후 - "마레찌가 세븐틴을 엄청나게 좋아해서요!"Yurim 曰:  탱탱볼 - "발랄하고 통통 튀고 귀여워서"Ayumi 曰: 마이쿤의 아이유 - "너랑 나가 호마레의 18번이라서"
조회수 1641

[도떼기 비하인드 스토리] 3화 : 도떼기마켓이 '중개'하지 않는 이유

여러분은 중고 거래에 대해 어떻게 생각하시나요?혹시 '평화로운 그 곳'에서 물건을 사고 팔아본 경험이 있으신가요?꼭 익명의 인터넷 사이트 상이 아니라도 크고 작게, 누구나 한번쯤 해봤을 중고 거래.기억을 더듬어 보세요.오래 전 '아나바다'라는 슬로건이 성행하던 시절이 있었는데요. 단순히 아끼고 나누는 것 외에 같은 반 친구들, 한 동네 이웃들과 입지 않는 옷이나 사용하지 않는 물건 등을 바꿔 쓰고 다시 쓰는 알뜰살뜰하고도 가슴 따땃해지는 운동이었죠. 어디 그 뿐인가요? 매해 연말 '사랑나눔 바자회'라는 벼룩시장은 꿀같은 득템은 물론 수익금 일부가 사회 소외된 곳에 기부되어, 세상을 온화히 데우는 데에 동참할 수 있었던 좋은 장이었답니다.나에게서 의미를 잃은 것은 다른 이를 만나 가치를 되찾으며같은 방법으로 나 또한 누군가로부터 무의미해져 버린 것에서 새로운 가치를 찾는 것.도떼기마켓은 그 가치를 일깨우는 연장선 상에 있는 서비스입니다. 도떼기마켓은 보다 쉽고 편하며 안전한 중고 거래를 지향합니다. 당신 또한 우리를 통해 긍정과 호의의 중고 거래를 조우하길 소망합니다. 사람들로 하여금 새로운 라이프 스타일을 경험할 수 있도록 펼쳐진 너른 장이 되길 도떼기마켓은 기꺼이 자처합니다.지금부터 도떼기마켓의 탄생 비하인드 스토리들을 꺼내 들려 드리려고 합니다.이로써 당신의 생각 한켠에 자리한 중고 거래에 대한 인식이 이전보다 조금은 나아지길 기대해봅니다.' 중 고 '이거 지-인짜 좋은데... 뭐라 표현할 방법이 없거든요!3화 도떼기마켓은 '중개'하지 않습니다.: 도떼기마켓은 당신의 스타일을 구입합니다.3년 여 간 플리마켓을 개최해오며 제법 알찬 노하우가 쌓였다고 자부했습니다. 이정도면 생각하고 있는 것을 실행하기에 모자르지 않다고 판단했죠. 야심차게 시작한 도떼기마켓, 노하우만으로 개인간 중고 거래의 본질적인 문제점을 해결하기엔 턱없이 부족한 우리였습니다.다행인건, 이 사실을 깨닫는 데에 그리 오랜 시간이 걸리지 않았다는 거죠.  # 이러려고 중고거래 하나, 괴롭고 자괴감 들어..개인간 거래는 판매자가 해야 할 자질구레한 일들이 너무 많습니다. 성가시기 딱 좋은 일들로만 가득합니다.일단 사진을 찍어야 합니다. 그리고 업로드를 해야 합니다. '상세 사진'을 요구하는 프로 디테일러들의 귀찮은 재촬영 요청에 미리 대비하려면 발로 찍어도 요리조리 찍어내야 합니다. 여차저차 찍어낸 사진을 카페나 중고거래 앱 등에 올려둔 후 연락을 기다립니다. 하염없이 기다립니다. 기다리는 내가 심심할까봐 전국 방방곡곡의 또다른 판매자들이 내 게시물 위를 무서운 속도로 밟고 올라섭니다. 업데이트된 게시물이 많아 내가 올린 상품이 뒤로 밀리면 한 번씩 재업을 해줘야 합니다.드디어, 드디어, 드디어...!구매자가 나타납니다. 허나 그냥 순순히 사가면 그건 올바른(?)구매자가 아닙니다. 깎아 달라 합니다. 네, 뭐 이해 못하는 건 아니에요. 디스카운트 욕구는 본능에 가까운, 무의식이 지배하는 행동이라 할 수 있죠. 인정. 내가 구매자라도 그랬을 테니까요. 아니 그래도 그렇지.. 아직 쓸만하고 말짱해서 버리기엔 영 아까워 파는건데 사사건건 트집을 잡는 걸 듣고 있자니 괜히 속이 쓰리고 슬슬 분노가 치밀죠. 쌓인 정 때문일까요? 하... 사람이건 물건이건 쿨하게 이별하는 건 정말 어려운 일인가봅니다.그 많던 택배 상자는 누가 가져 갔을까. 평소엔 성가시게 굴던 빈 상자가 택배만 보낼라 치면 감감무소식입니다. 택배 박스를 찾으면요? 판매할 물건을 포장하고 택비 접수를 한 뒤, 보내야죠. 전전긍긍 기다리는 구매자에게 운송장 번호도 친절히 알려주고 걱정말라고 안심시키는 건 물론, 택배가 잘 도착했는지 확인까지! 이러다 구매자랑 정분나겠습니다.한 개 팔기도 이렇게 힘든데, 옷장 정리 후 한번에 대여섯개, 열 개 이상 팔려고 하면… 간편해졌다고 하더라도 직접 옷을 팔고, 실랑이하고, 배송에 확인까지 하는 건 여간 피곤한 일이 아닙니다.그래요, 팔아 치워보자며 마음을 다부지게 먹었던 우리가 결국 헌옷 수거함 앞에 서있는 이유기도 하죠. # 레몬마켓이 아닌, 피치마켓이 되어보자.판매자를 닦달해대는 구매자는 뭐 등 따숩고 편해서 그러나요?음, 조금 딱딱한 얘기를 하나 해볼까 합니다.중고 거래 시장은 속성상 상품 정보에 대한 판매자와 구매자(소비자)간의 정보 비대칭이 존재합니다. 필연적으로 말이죠. 이른바 레몬마켓(Lemon Market)이라 부르는 중고차 시장과 같은 맥락입니다. 구매자는 상대적으로 판매자만큼의 상품의 정보를 알지 못합니다. 이는 구매자로 하여금, 상품에 대한 불만족과 함께 사기를 당했다는 느낌까지도 주게 되죠. 결국 중고 상품에 대해 좋지 않은 인식을 심어주고, 중고 거래 자체에 대한 신뢰도 하락으로 이어지게 되는 거구요.아, 우린 이런 걸 원한 게 아니었는데...'쉽고 편한 중고 거래, 중고 상품에 대한 긍정적인 인식.'우리가 바란 건 이런 것들이죠. 보다 근본적인 부분에서 문제를 해결하고, 기존의 중고 거래와는 다른 차원의 혁신적인 편리함이 필요한 시점이었습니다. 무엇보다도 상품에 대해 객관적이고 정확한 정보를 제공함으로써 중고 상품 자체에 대한 신뢰를 회복하는 것이 중요했습니다.이 시장을 바꿔보리라. 직접 피치마켓(Peach Market)으로 만들어 보리라!(비장)# 당신의 스타일을 구입합니다.우리는 ‘중개자’에서 ‘중간(유통)자’가 되기로 결심하였습니다.판매자가 팔고자 하는 중고 의류를 도떼기마켓이 '직접' 구매하고 '직접' 케어해서 '직접' 판매하기로 한 것!중고 거래 과정에서 경험해야 하는 크고 작은 문제들을 우리가 대신 해결하기로 하였습니다. 당연히 사기를 당할 위험도 없어지는 거죠. 매의 눈을 가진 전문 패션 MD가 직접 옷을 검수하고 합리적인 판매금액을 제안합니다. 판매금액을 수락하면, 48시간 내에 통장으로 현금이 ‘안전’하게 입금됩니다. 더 이상 손품을 팔고, 발품을 팔고, 맘 고생할 필요가 없죠.무게(kg)당 몇 백원으로 쳐주는 터무니없는 헌 옷 매입 업체와는 결이 다릅니다. 도떼기마켓은 상품의 컨디션, 디자인, 트렌드 등 다양한 부분을 고려하여 합리적인 가격으로 구매하고 합리적인 가격으로 판매합니다. 그것도 중고 거래의 번거로운 일들을 모두 대신하면서 말이죠.도떼기마켓을 대표하는 역대 제비들도떼기마켓이 가져온 혁신적인 변화.빈티지 소셜 마켓에서, 중고 패션 마켓플레이스로 완벽하게 진화한 도떼기마켓이당신의 스타일을 구입합니다.어떻게? 바로 이렇게!다음 주, 도떼기마켓 비하인드 스토리 4회가 계속됩니다.#유니온풀 #도떼기마켓 #서비스 #서비스소개 #팀소개 #회사소개 
조회수 835

소비자 행동 데이터 측정의 의미

탈 인구통계적 소비주의2017년은 trendwatching.com이 연례보고서 Post-demographic Consumerism(탈-인구통계적 소비주의)을 통해 인구통계적 정보로 고객의 소비활동을 예측하는 모델을 버리라고 주장한 지 3년이 되는 해입니다. 3년이 지난 지금 우리의 생활은 어떤 모습일까요?(Post-demographic Consumerism 리포트의 첫페이지.  ‘소비자 행동에 혼란스러워하는 사람은 당신만이 아니다. 이제 소비자들은 그들이 행동해야 하는 방식대로 행동하지 않는다’ 라는 문장이 등장합니다) 대중교통 안이나 팀원들과의 점심식사 자리처럼, 물리적으로 동일한 시공간에 타인과 함께 존재하는 순간에도 우리는 스마트폰으로 다른 친구와 카톡을 하고, 관심 있는 기사를 읽고, 셀카를 찍거나, 페이스북 고양이 동영상에 좋아요를 누릅니다. 같은 시공간에 존재한다고 해서 반드시 동일한 집단적 경험을 공유하지는 않게 되었습니다.특히 나이와 소득수준에 관계 없이 스마트폰 보급률이 높은 우리나라는 각자의 취향에 걸맞은 컨텐츠를 소비하고 생산하는 것이 자유롭습니다. 디지털 영역에서 개인별 파편화가 일어나기 쉬운 환경으로, 준거집단이나 인구통계와 같은 집단적 동질성에 기반한 마케팅 전략이 통하기 어려운 시장이란 해석도 가능합니다. (스티브 사마티노는 그의 저서 위대한 해체(The Great Fragmentation)에서 기존 산업사회의 논리가 파편화/해체된 후 디지털 융합으로 최적화 된다고 주장합니다. 개인의 파편화를 커버 아트로 채택한 것이 흥미롭습니다.)소비자 행동 데이터의 필요성마케터, 기획자, MD 등 인간을 소비자로서 이해해야 하는 직업인들에게 이런 현상은 반갑지만은 않을 것입니다. 변화하는 시장에 걸맞은 새로운 전략이 필요해졌기 때문입니다. 여전히 인구주택 총 조사같은 통계자료, 리서치펌의 시장 조사 자료 등을 참고하지만 가장 면밀히 살펴보는 것은 자사 소비자의 행동 데이터입니다.실행 가능한 전략을 만들기 위해서는 통계와 시장조사 자료가 제시하는 거시적인 트렌드와 자사의 소비자행동 간 상관관계를 분석할 수 있어야 합니다. ‘1인가구 시대를 맞아 혼밥혼술이 유행한다’라는 외부 자료가 있어도, 자사 소비자의 선호도와 행동에 관한 데이터가 없다면 무엇을 만들고 어떻게 팔아야 할지 판단이 어려울 것이기 때문입니다.따라서 적어도 자사의 홈페이지, 소셜 채널, 쇼핑몰, 모바일 앱과 같은 온드 미디어(Owned Media)에 적절한 측정 툴을 적용해 소비자의 행동특성을 데이터로 남길 수 있어야 합니다. 어떤 제품을 얼마나 구매하는지, 신규 고객이라면 왜 우리 브랜드를 선택했고 어떤 경로로 유입 되었을지, 기존 고객이라면 방문 횟수, 구매량, 구매빈도에 주목할 만한 변화가 있는지 등을 측정할 수 있어야 합니다.소셜 미디어로 ‘공유’하는 활동도 소비자의 행동특성으로써 측정할 필요가 있습니다. 특히 커머스 분야에서는 상품의 소셜 미디어 공유가 남다른 의미를 가집니다. 상품의 URL을 자신의 메신저로 복사해 놓고 여유 있는 시간에 해당 상품을 비교구매 하는 패턴이 관찰되는 추세입니다. 따라서 소셜 미디어 공유는 강력한 구매징후로 판단하고 관리할 필요가 있습니다.앞으로 3년 후의 환경 역시 변화할 것입니다. 하지만 거시적인 인구통계정보는 변화하는 환경에서의 소비자의 특성을 반영하지 못할 것입니다. 따라서 우리 비즈니스의 소비자를 판단하기 위한 기준은 그들 각각의 행동이고 이것을 데이터화 할 수 있어야 합니다. 자사 채널의 데이터는 미지의 상황에 전략적으로 대응하는데 필요한 강력한 팩트라는 사실을 잊어서는 안되겠습니다.
조회수 857

이건 니가 하시고, 이건 내가 할게요.

<들어가기 전에 잠시 개인적인 생각>일이란 게 참 톱니바퀴 같아요. 이 글을 쓰고 있는 순간에도 사실 다양한 일을 처리해야 하기에 하루하루 생각도 바뀌고 경험도 바뀌어 가고 있어요. 제 앞에 누군가가 있고, 제 뒤에 또 누군가가 있죠. 결국 일이란 게 사람이 하는 일이기에 담당자의 성격과 역량에 따라 수많은 케이스가 나올 수 밖에 없더라구요.이걸 하나로 일반화시키려면 평균치를 내야해요. 다수의 케이스를 고민해야하고, 성공한 또는 실패한 케이스를 찾아봐야 하죠. 하지만 본질적인 고민이 들긴 합니다. 평균치란 건 꽤나 무서운 거예요. 마치 직장인들의 평균연봉이 4,000만원이다! 라는 기사와 같죠. 사실 누구도 딱 평균만큼의 금액을 받는 사람은 없어요. 거리만 다를 뿐 결국엔 각각 평균의 위아래 어딘가에 점으로 위치하고 있습니다. 그러니 어찌보면 평균선이란 것은 허구와도 같아요. 가상의 선이죠. 실무얘기를 하면서 제일 어려운 부분은 바로 이 부분이예요. 딜레마죠. 케이스란 걸 평균화시킬 수 있는가...에 대한 고민도 있고.평균화시킬만큼 케이스가 충분한가? 에 대한 고민도 있죠.마지막으론 그 평균이 과연 정확한 명제인가? 에 대한 의구심도 들어요.그럼에도 불구하고 이런 글을 쓰는 이유앞으로 저와 일할 분들과 '일을 잘하고 싶기 때문' 이예요. 이 글 하나에 무슨 문화가 바뀐다거나 더 나은 사회를 위한 철학을 담진 못할 거예요. 다만 저는 이렇게 일하는 걸 좋아하고 서로서로 편하게 일해야 한다고 생각해요. 안그래도 힘든 일 굳이 짜증내면서 하면 더 힘들잖아요. 대부분의 클라이언트를 제 글을 통해 만나게 되는 경우가 많기 때문에 밑밥까는 느낌으로 적어놓는 느낌이죠.시작에 앞서 이렇게 긴 서론을 쓴 이유는 저번, 오늘, 다음에 할 얘기가 사실 굉장히 이상적이고 추상적인 주제란 걸 알기때문이예요. 그래서 사실 책에 나온 얘기대로 이래저래 각색해서 쓰다가 다 지워버렸어요.평균선으로 어정쩡하게 말하기 보단, 구체적으로 단순하게 말해보도록 할께요. 오늘은 업무분장에 대한 얘기예요.1. 업무분장을 할 땐 리스트업을 해줘요.기획은 니가하고 디자인은 너가 해. 라고 하지 마세요. 기획, 디자인, 마케팅, 총괄..이런 단어들은 엄청나게 커요. 정확히는 하나의 '직무'에 가깝다구요. 어떤 업무들을 할 지 한 단계 더 들어가 줘야 해요.1) 사전미팅 주관/스케쥴링/회의록 작성2) 회의 토대로 기획안 작성(10p 미만) / 제출 및 피드백 반영 후 수정3) 9/15일까지 기획안 최종안 완성4) 투자제안서 플로우 기획 및 텍스트 정리(매출자료는 경지팀 지원)5) 9/20까지 텍스트 완료 후 보고 / 컨펌 시 디자인팀에 인계이렇게 투두리스트를 두두두두 써줘요. 정확하게 언제 얼만큼 무슨 일을 해야하는 지... 표로 만들지 타임라인 형태로 잡을 지 트렐로를 쓸지 플로우를 쓸 지 슬랙을 쓸지 등등은 자유지만 분명한 건 기한과 업무와 책임자이것이 분명하지 않으면 3일 정도 지난 후 혼돈의 사도가 되어있는 담당자를 만날 수 있을 거예요.담당자2. 담당자를 좀 정리해봐요.누가 무슨 일을 하는 지 헷갈리는 경우가 있어요. 외주를 맡길 땐 담당자가 있기 마련이예요. 외부업체 입장에선 컨택포인트...라는 것이 깔끔하길 바라죠. 이 사람 저 사람이 전화를 해대면 혼란스러워요. 만약 제작물 종류가 다양해서 회사소개서는 박사원이포스터와 초대장제작은 이대리가굿즈제작은 오과장이한다고 쳐봐요. 이럴 수 있죠. 한 사람이 모든 걸 다 맡을 순 없으니 쪼갤 수 있어요. 하지만 이건 내부사정이니까 클라이언트님밖에 몰라요. 외부업체입장에선 박사원, 이대리, 오과장의 전화를 번갈아 받아야 하는데, 이대리한테 해야할 말을 오과장한테 하기도 하고 전달이 되기도, 안되기도 하고.... 그야말로 톨킨 세계관의 중간계 전쟁과 같은 카오스가 펼쳐질 수 있어요.이런느낌.외부업체와 컨택 시 담당자가 여러명이라면 회사소개서 제작(9/15) :20p내외/가로좌철 중철제본/500부/표지4p(랑데뷰250g) + 내지16p(스노우180g)담당자 박창선(010-1234-5678 / [email protected]) 이런 식으로 3명 모두 정리해서 전달해주세요. 그리고 외부업체에도 컨택포인트가 있을 거 아녜요. 그러면 그 쪽 업체의 담당자와 매칭해서 누가 누구와 연락을 하는 지 확실히 하는 게 좋아요.우리회사 오과장 - 애프터모멘트 박창선대표 (이렇게 짝궁)이렇게 말예요. 그래야 박창선씨에게 전화오면 오과장님에게 넘겨줄 수 있죠. 참고로 박창선은 제 이름이에요.으하하..3. 사실 이 두개면 돼요. 다른 거 안해도 될 것 같아요.그냥 업무 확실히 잡고 담당자 연락처만 제대로 통일시켜줘도 성은이 망극하여 발등에 키스를 할 거예요.근데 생각보다 업무분장이 잘 되는 곳을 보기 드물어요. 그 이유를 생각해보니 아래와 같아요.1. 일 못하는 사람 짱많아요. 2. 일 잘하는 사람이 적어요.3. 일을 못하는 데 잘한다고 생각하는 사람이 더 많아요.4. 일을 잘하는 데 안하려고 하는 사람도 있어요.5. 전체 일이 어떻게 돌아가는 지 몰라요.6. 전체 일이 돌아가는 건 아는데 하기가 싫어요.7. 쫄보에요. 안해본 일은 안해요.8. 눈치도 있고 다 좋은데 손이 느려요.9. 사내정치가 오져요. 쟤랑 일하기 싫어요.10. 업무분장 자체가 없어요. 그냥 일잘러만 죽어나요.11. 분장은 잘했는데 결과물이 개판이에요.12. 담당자가 퇴사했어요. 인수인계를 안했어요.13. 자꾸 자기가 칭찬받고 싶어해요. 이런 등등의 이슈들이 있어요. 사실 업무분장 자체는 단순한 일이예요. 그냥 일을 구체적으로 쪼개서 나눠주는 거예요. 회사 엠티가서 된장찌개 만들 때 누가 감자썰래? 하는 것과 비슷한 거예요. 그런데 문제는....감자가 들어가는 지 모르거나감자를 썰 줄 모르거나감자를 썰다가 손이 다치거나감자를 정성스레 한오백년 썰고 있거나감자를 너무 크게 썰거나감자를 채로 썰거나감자를 안썰거나감자써는걸 떠넘기거나감자를 먹어버리기 때문이에요.모르면, 배워야 해요.이러다보니 된장찌개를 만들어본 사람에게 업무가 과중되고 그 사람은 지치고 다다음달에 그만둬요. 그럼 감자썰 줄 아는 사람이 아무도 없어요. 물론 껍질을 안벗기고 그냥 끓여도 된장찌개가 안되는 건 아니에요. 맛이 이상할 뿐이죠. 그렇게 이상한 된장찌개를 계속 끓이다보니 그게 맞는 줄 알게되요.아니예요 틀렸어요.업무분장은 제대로 확실하게 해야해요.회의시간이 10시간이면 7시간은 업무분장에 써도 돼요. 제발 그랬으면 좋겠어요.서로서로 납득이 가고 온당하게. 합리적이고 효율적인 배치가 나올 때까지 고민하고 또 대화했으면 좋겠어요. 그런 날이 언제쯤 올진 모르겠지만 그랬으면 좋겠어요. 
조회수 791

컴공생의 AI 스쿨 필기 노트 ④ 교차 검증과 정규화

지금까지 Linear Regression, Logistic Regression 모델을 만들어보았는데요. 우리가 만든 모델이 과연 잘 만들어진 모델이라고 볼 수 있을까요? 이를 알기 위해서 이번 4주차 수업에서는 우리가 만든 모델의 적합성을 보다 객관적으로 평가하기 위한 방법으로 교차 검증(Cross Validation)과 정규화(Regularization)를 배웠어요. 차례대로 하나씩 알아볼까요?1. Cross Validation교차 검증은 새로운 데이터셋에 대해 반응하는 모델의 성능을 추정하는 방법이에요. 학습된 모델이 새로운 데이터를 받아들였을 때 얼마나 예측이나 분류를 잘 수행하는지 그 성능을 알기 위해서는 이에 대한 추정 방식이 필요해요. 먼저 Whole population(모집단)에서 Y와 f를 구하기 위해 Training Set(모집단에서 나온 데이터셋)에서 f와 똑같지 않지만 비슷한 모델 f^를 만들어요. 그리고 이 모델을 모집단에서 나온 또 다른 데이터 셋인 Test Set을 이용하여 확인해요. 하지만 일반적으로 Test Set이 별도로 존재하는 경우가 많지 않기 때문에 Training Set을 2개의 데이터셋으로 나눠요. 이 Training Set에서 Training Set과 Test Set을 어떻게 나누느냐에 따라 모델의 성능이 달라질 수 있어요. 이런 테스트 방법을 교차 검증(Cross validation)이라고 해요.이번 시간에는 교차 검증 방법으로 LOOCV(Leave-One-Out Cross Validation)와 K-Fold Cross Validation을 알아봤어요. LOOCV(Leave-One-Out Cross Validation)LOOCV는 n 개의 데이터 샘플에서 한 개의 데이터 샘플을 test set으로 하고, 1개를 뺀 나머지 n-1 개를 training set으로 두고 모델을 검증하는 방식이에요.K-Fold Cross ValidationK-Fold CV는 n 개의 데이터를 랜덤하게 섞어 균등하게  k개의 그룹으로 나눠요. 한 개의 그룹이 test set이고 나머지 k-1개의 그룹들이 training set이 되어 k번을 반복하게 돼요. LOOCV도 n-fold CV로 볼 수 있어요!코드로 나타내기Step1. 데이터 생성 & train set과 test set  단순 분리# model selection modulefrom sklearn.model_selection import train_test_splitfrom sklearn.discriminant_analysis import LinearDiscriminantAnalysis# read datadf = pd.read_csv('data/data01_iris.csv')data = df.iloc[:,:-1].as_matrix()target = df['Species'].factorize()[0]LOOCV와 K-Fold CV에 사용할 데이터를 구하는 코드에요. data 파일 안의 data01.csv 파일을 읽어서 데이터 프레임 형태로 가져와요.df(데이터 프레임) 안에는 이와 같은 105개의 데이터 셋이 저장되어 있어요.df(데이터 프레임)의 Sepal.Length부터 Petal.Width의 값들을 매트릭스 형태로 data에 할당해요.Species에는 ‘setosa’, ‘versicolor’, ‘virginica’ 값들이 있는데요. factorize() 을 이용하여 setosa는 0, versicolor는 1, virginica는 2로 바꿔줘요.# random splitX_train, X_test, y_train, y_test = train_test_split(            data, target, test_size=0.4, random_state=0)X_train.shape, y_train.shapeX_test.shape, y_test.shape그다음에는 data와 target 데이터를 가지고 training set과 test set으로 6:4로 나눠요.X_train.shape = (90,4),  X_test.shape = (60, 4)가 돼요.# LDA f = LinearDiscriminantAnalysis() f.fit(X_train,y_train) y_train_hat = f.predict(X_train) table_count(y_train,y_train_hat) f.score(X_train,y_train)LDA(Linear discriminant analysis)는 대표적인 확률론적 생성 모형이에요. 즉 y의 클래스 값에 따른 x의 분포에 대한 정보를 먼저 알아낸 후, 베이즈 정리를 사용하여 주어진 x에 대한 y의 확률 분포를 찾아낸다고 해요.Step2. test set 준비(1) LOOCV으로 test set 준비# leave-one-out  from sklearn.model_selection import LeaveOneOutloo = LeaveOneOut()loo.get_n_splits(X_train)scv = []for train_idx, test_idx in loo.split(X_train):    print('Train: ',train_idx,'Test: ',test_idx)    f.fit(X_train[train_idx,:],y_train[train_idx])    s = f.score(X_train[test_idx,:],y_train[test_idx])    scv.append(s) get_n_splits() 함수를 사용하여 (90,4)의 shape을 가지는 X_train을 90개로 나눠요.test set에 0부터 89까지 하나씩 할당되고 할당된 숫자 외의 나머지 숫자들은 training set으로 모델을 검증해요. 위의 결과에서도 볼 수 있듯이 test set에 0이 할당되면 train set에는 1 ~ 89가 할당되어 모델을 검증하게 돼요!(2) K-fold CV로 test set 준비# K-fold CVfrom sklearn.model_selection import KFoldkf = KFold(5)kf.get_n_splits()scv = []for train_idx, test_idx in kf.split(X_train):    print('Train: ',train_idx,'Test: ',test_idx)    f.fit(X_train[train_idx,:],y_train[train_idx])    s = f.score(X_train[test_idx,:],y_train[test_idx])    scv.append(s) KFold(5) : 위에서 배운 k-fold 교차 검증에서 k를 5로 설정하여 우리가 가지고 있는 데이터 셋을 5개의 그룹으로 나눠서 교차 검증을 할 거예요.kf.get_n_splits()를 사용하여 5번 교차 검증할 것을 정해요.위에서 90개의 데이터셋을 5개의 그룹으로 나눴어요. 그리고 각 그룹 한 개씩 test set으로 정하고 나머지 그룹들은 training set으로 할당하고 모델을 검증해요. 예를 들어 그룹 1이 0~17, 그룹 2가 18 ~ 35, 그룹 3이 36~53, 그룹 4가 54~71, 그룹 5가 72~89라고 할 때, test set에 그룹 1을 할당하면 train set에는 그룹 2, 3, 4, 5가 할당되어 모델을 검증하게 돼요.Step3. 교차 검증 시행CV는 단순히 데이터 셋을 나누는 역할을 수행할 뿐이에요. 실제로 모형의 성능(편향 오차 및 분산)을 구하려면 이렇게 나누어진 데이터셋을 사용하여 평가를 반복해야 해요. 이 과정을 자동화하는 명령이 cross_val_score()이에요.# K-fold CVfrom sklearn.model_selection import cross_val_scoref = LinearDiscriminantAnalysis()s = cross_val_score(f,X_train,y_train,cv=3)cross_val_score(f, X_train, y_train, cv=3) : cross validation iterator cv를 이용하여 X_train, y_train을 분할하고 f에 넣어서 scoring metric을 구하는 과정을 반복해요.2. Regularization앞서 말한 우리의 목적은 우리의 데이터셋에 맞는 Y와 f를 구하는 것이었어요. f를 결정하기 위해서는 먼저 결정해야 하는 요소가 있어요. 아래 다섯 가지가 f를 결정하는 요소들이에요.- Model family : linear, neural 등 방법론 결정- Tuning parameter : 모델에 맞는 파라미터 조절 - Feature selection(특징 선택) : 많은 데이터 중 어떤 데이터를 쓸지 고르는 것 - Regularization(정규화)  - Dimension reduction(차원 축소)f를 결정하는 요소 중 Regularization(정규화)에 대해 알아볼게요!정규화 선형회귀 방법은 선형회귀 계수(weight)에 대한 제약 조건을 추가함으로써 모형이 과도하게 최적화되는 현상(과최적화, overfitting)을 막는 방법이에요. 모형이 과도하게 최적화되면 모형 계수의 크기도 과도하게 증가하는 경향이 나타나요. 따라서 정규화 방법에서 추가하는 제약 조건은 일반적으로 계수의 크기를 제한하는 방법이에요. 일반적으로 Ridge Regression, Lasso, Elastic Net 이 세 가지 방법이 사용돼요.Ridge Regression머신 러닝에서는 모델의 오차를 찾기 위해 보통 최소제곱법(Least squares fitting)을 이용하여 β를 최소화시켜요. 위의 RSS는 잔차제곱식으로 예측값과 실제 값 사이의 차이를 구하는 식이에요. 회귀분석의 계수 값을 RSS을 최소화하는 β값을 찾음으로써 구할 수 있어요.Ridge Regression은 최소제곱법에 가중치들의 제곱합을 최소화하는 것을 추가적인 제약 조건으로 갖는 방법이에요. λ는 기존의 제곱합과 추가적 제약 조건의 비중을 조절하기 위한 하이퍼 파라미터에요. λ가 크면 정규화 정도가 커지고 가중치의 값들이 작아져요. λ가 작아지면 정규화 정도가 작아지며 λ가 0이 되면 일반적인 선형 회귀 모형이 돼요.코드로는 아래와 같이 나타낼 수 있어요.from sklearn.linear_model import Ridgef = Ridge(alpha=0.5)f.fit(xtrain,ytrain)f.intercept_,f.coef_f.score(xtrain,ytrain)f.score(xtest,ytest)LassoLasso는 가중치의 절댓값의 합을 최소화하는 것을 추가적인 제약 조건으로 가져요. 아래와 같이 코드로 나타낼 수 있어요.from sklearn.linear_model import Lassof = Lasso(alpha=1.0)f.fit(xtrain,ytrain)f.intercept_,f.coef_f.score(xtrain,ytrain)f.score(xtest,ytest)Elastic NetElastic Net은 가중치의 절댓값의 합과 제곱합을 동시에 제약 조건으로 가지는 모형이에요. 코드로는 아래와 같아요.from sklearn.linear_model import ElasticNetf = ElasticNet(alpha=0.1,l1_ratio=0.5)f.fit(xtrain,ytrain) f.intercept_,f.coef_f.score(xtrain,ytrain)f.score(xtest,ytest)Lasso와 Ridge Regression의 차이점왼쪽 : Lasso, 오른쪽 Ridge Regression위의 두 그림은 Lasso와 Ridge Regression의  차이점을 잘 나타내는 그림이에요. 초록색 부분은 회귀계수(회귀분석에서 독립변수가 한 단위 변화함에 따라 종속변수에 미치는 영향력 크기)가 가질 수 있는 영역이고 빨간색 원은 RSS가 같은 지점을 연결한 것을 보여주는 것으로 가운데로 갈수록 오차가 작아져요.Lasso와 Ridge Regression 모두 RSS를 희생하여 계수를 축소하는 방법이라는 공통점이 있어요.하지만 Ridge Regression과 Lasso의 가장 큰 차이점은 Ridge 회귀는 계수를 축소하되 0에 가까운 수로 축소하는 반면, Lasso는 계수를 완전히 0으로 축소화한다는 점이에요.Cross validation(교차 검증)과 Regularization(정규화)에 대해 알아보았는데요. 간단히 요약해 볼게요.Cross validation(교차 검증)은 머신러닝 모델의 타당성을 검증하는 방법 중의 하나로, 특정 데이터를 training set과 test set으로 분할한 뒤 training set을 활용해 학습하고 test set으로 테스트하여 학습의 타당성을 검증하는 방법이에요. 교차 검증에는 여러 가지 방법이 있는데 그중에서도 우리는 LOOCV와 K-Fold CV를 배웠어요.Regularization(정규화)는 모델의 일반화 오류를 줄여 과적합을 방지하는 방법을 말해요. 일반적으로 Ridge Regression, Lasso, Elastic Net 이 세 가지 방법을 사용해요.이상적인 머신러닝 모델을 만들기 위해 고려해야 할 점들은 정말 많은 것 같아요. 우리가 만든 모델이 적합한 모델인지 이번 수업시간에 배운 교차 검증과 정규화를 통해 잘 살펴봐요!* 이 글은 AI스쿨 - 인공지능 R&D 실무자 양성과정 4주차 수업에 대하여 수강생 최유진님이 작성하신 수업 후기입니다.
조회수 1080

알리바바에서 소싱할 때

안녕하세요 대한민국 셀러들의 성공적인 아마존 진출을 도와주는 컨설팅 회사이자 대행사인 주식회사 컨택틱의 이이삭 대표입니다.이전에는, 제조사와 거래할 때와 무역회사와 거래할 때의 장단점을 비교해봤습니다. 그렇다면 알리바바에서 소싱 할 때 제조사와 무역회사를 구분 짓는 방법이 무엇이 있을까요? 4가지만 기억하세요.1. 대놓고 물어본다너무 당연한 상식이라 이걸 보고 좀 당황스러울 수도 있다고 생각합니다. 하지만 사람은 대개 당연한 걸 무심코 넘기는 경향이 있다고 모두가 동의할 것입니다. 상대방이 제조사인지 무역회사인지, 대놓고 물어보는 게 어찌 보면 가장 깔끔하고 시원한 방법이 될 수 있습니다. 그리고 대부분의 경우, 이런 질문을 받았을 때, 망설임이나 질문을 우회하는 것 없이 직설적으로 ‘예 저희는 공장입니다’라고 답변하는 분들 중에 거짓말하는 사람은 굉장히 드물기 마련입니다. 만약 썩 시원치 않은 대답을 받을 경우, 예를 들어 ‘우리는 파트너십을 맺은 공장이 있다. 공장이나 다름없다. 맞다 우린 제조’유통’업자다.’ 등등, 일단 의심하고 보는 게 좋습니다. 수출 자격증이 있는지 물어보는 것도 아니고, 인허가 자료를 구비했는지 물어보는 것도 아니라, 단순히 제품을 직접 제조하는 제조사인지 물어봤을 뿐인데, 만약 본인이 정말 제조사 즉 공장이면 대답을 회피할 필요가 전혀 없기 때문입니다.2. 전시된 상품 라인을 검토한다이것도 사실 당연한 얘기지만, 정말 상대방이 제조사가 맞다면 상품 라인 (상품군)이 어느 정도 일관성이 있을 수밖에 없습니다. 식칼류에 특화되어있거나, 수건류에 특화되어있다거나, 이런 식으로 어느 특정 시장에 특화되어있다는 얘기이죠. 그럴 수밖에 없는 게, 한 ‘종류’의 상품을 제조하려면 그에 맞는 장비가 필요합니다. 하지만 제조에 필요한 장비 시설의 가격은 절대 만만하지 않습니다. 해당 공장이 대기업 수준으로 규모가 크지 않는 이상 한 공장에서 휴대폰 케이스도 제조하고, 베개도 제조하고, 식탁도 제조할 수는 없는 게 당연합니다.반대로, 정말 상식을 벗어난 다양한 상품군의 제품들이 알리바바 미니 몰 스토어에 전시되어있다면 해당 판매자는 공장이 아니라 무역 회사일 가능성이 매우 높습니다. 이런저런 공장과 연을 형성하고 그들의 제품들을 해당 무역회사의 알리바바 storefront에 전시해놓는 셈입니다.Photo by Hayes Potter on Unsplash3. 상품에 대한 구체적인 질문을 한다구체적인 질문을 물어보면 해당 분야에 대한 상대방의 지식수준이 여실히 드러나게 됩니다. 전문가 수준의 질문까진 필요하지 않더라도, 해당 분야에 주로 사용되는 소재나 규격, 부품, 건전지가 들어있다면 수명 시간 등에 대한 질문 및 용어를 미리 사전에 시장조사를 하면서 익히고, 상담하고 있는 상대방에게 전화나 채팅으로 실시간으로 물어봐야 합니다. 이메일로 물어보면 지식이 없던 사람도 구글링을 통해 금방 알아보고 어쨌거나 답변을 할 수는 있게 되기 때문에, 반드시 전화나 채팅으로 물어보셔야 됩니다. 그리고 질문을 받은 당사자가 즉시 대답을 하지 못하거나 당황해한다면 제조사라기보단 중간에 알선해주는 무역 회사일 가능성이 높다고 판단할 수가 있습니다.4. 직접 공장을 방문한다사실 여기까지 하는 것은 여러분께 큰 부담이 될 수 있습니다. 중국은 방문하려면 비자가 필요하고, 무엇보다 사업장을 비우고 외국 출장을 갔다 오는 것이기 때문에 시간적, 인력적, 금전적 투자가 발생하는 셈입니다. 하지만 그런 모든 것을 감수할 정도로 중요한 사안이면 당연히 방문을 하는 게 맞습니다. 그리고 방문하겠다고 했을 때 흔쾌하게 ‘OK’하는 분들은 공장을 직접 운영하는 제조사일 가능성이 높은 것은 당연합니다. 제조사 입장에서 여러분의 방문은 전혀 손해 볼 것이 없는 것이기 때문에 방문을 절대로 마다하지 않습니다. 만약 온갖 핑계를 대면서 (지방에 위치해있다, 요즘 바빠서 방문은 어렵다, 등등) 피한다면 분명히 무역회사가 본인의 돈벌이 수단이나 마찬가지인 ‘공장 인맥’을 숨기기 위함이니, 그런 분들은 무역회사라고 단정 지을 수 있습니다.Photo by Ant Rozetsky on Unsplash알리바바는 80% 정도가 무역회사라고 합니다. 20%의 실질적으로 공장을 소유하고 상품을 직접 제조하는 제조사를 찾기 위해서는 위 4가지 방법을 잘 기억해주셔서 알리바바에서 상담하실 때 요긴하게 사용하시면 금방 누가 제조사이고 누가 무역회사인지 구분할 수 있을 것입니다.컨택틱의 모든 교육은 파트너인 글로벌셀러창업연구소와 접수하고 진행합니다. 교육 신청은 아래 링크나 글로벌셀러창업연구소의 홈페이지를 통해 가능합니다.오프라인 아마존 입문 과정오프라인 아마존 기초/심화 과정온라인 아마존 입문 과정그럼 오늘도 즐거운 글로벌 셀링 되세요!감사합니다.컨택틱서울특별시 서초구 서초대로 356, 606호(서초동, 서초지웰타워)대표 전화: 02-538-3939이메일: [email protected]홈페이지: https://www.kontactic.com네이버 블로그: https://blog.naver.com/kontactic카카오 브런치: https://brunch.co.kr/@allaboutamazon유튜브 채널: https://www.youtube.com/c/kontactic
조회수 4250

크몽 검색 기능 개선기

안녕하세요? 크몽의 백엔드 개발자로 활동하고 있는 에이든입니다. :)오늘은 크몽에 입사하고 한 달 동안 UX팀에서 진행한 검색 기능 개선에 대한 이야기를 해보려고 합니다.배경크몽에는 재능을 판매하는 프리랜서의 서비스 정보가 많이 저장되어있습니다. 판매하는 서비스 정보가 많을수록 검색 기능이 잘 되어있다면 사용자는 원하는 서비스를 빨리 찾을 수 있고, 프리랜서는 다양한 서비스를 의뢰인에게 판매할 수 있습니다.크몽에서는 사용자에게 정확한 검색으로 다양한 서비스를 제공하기 위해 노력하고 있습니다. 이번 글에서는 크몽 UX팀에서 보다 나은 검색 기능을 위해 어떠한 노력을 했는지 공유하고자 합니다.기존의 검색 기능기존의 검색 기능은 기본적인 키워드 검색 외에 별다른 기능을 제공하지 않았습니다. 그리고 스핑크스 검색엔진으로 구성되었습니다. 스핑크스는 전문 텍스트 검색 기능을 제공하며 데이터베이스와 잘 통합될 뿐만 아니라 스크립트 언어에 쉽게 접근할 수 있도록 설계되었습니다. 스핑크스의 동작 구조는 다음과 같습니다.스핑크스의 동작 구조Searchd는 클라이언트로부터 요청을 받고 스핑크스 인덱스에 대해 검색을 실행하는 역할을 합니다. 그리고 스핑크스 인덱서는 스핑크스 인덱스로 데이터를 가져오는 역할을 합니다.크몽은 이를 통해 사용자에게 검색 기능을 제공했습니다. 하지만 기존의 검색 기능은 불편한 점이 있었습니다.기존의 검색 기능의 불편한 점기존의 검색 기능은 의뢰인이 어떤 서비스를 필요로 하는지 본인이 정확하게 정의할 수 있어야 했습니다. 그게 아니라면 여러 키워드를 검색해보거나 원하는 서비스를 찾기 위해 해당 카테고리에서 서비스 전체를 둘러봐야 했습니다. 또한 많은 유료광고로 인해 사용자는 일반 서비스를 찾기가 힘든 문제가 있었습니다.기능상의 불편한 점뿐만 아니라 구현상에도 불편한 점이 있었습니다. 스핑크스에서 한글 검색을 구현하기 위해서는 복잡한 설정을 거쳐야 했으며 ngram analyzer를 통해서만 한글 형태소 분석이 가능했습니다. ngram analyzer는 음절 단위의 한국어 형태소 분석을 하므로 인덱스의 양이 많아질 뿐만 아니라 불필요한 정보까지 검색에 노출이 됩니다. 불필요한 정보가 노출되면서 종료율은 높아지고 서비스 상세페이지의 전환율이 낮아졌습니다. 또한 스핑크스는 데이터의 저장이 되지 않기 때문에 분석을 위해서는 별도의 과정이 필요했습니다.이에 크몽 개발팀은 사용자를 위한 검색 기능 보강뿐만 아니라 검색 엔진 변경이라는 결론을 내립니다.새로운 검색 기능새로운 검색 기능을 개발하기에 앞서 요구사항을 파악하고 새로운 검색 엔진에 대한 기술 탐색을 선행했습니다.프로젝트 진행 목적 및 요구사항정확한 검색 결과 제공광고 상품 제거를 통한 서비스 상세페이지로의 전환율 증대서비스 검색에 최적화된 검색 플로우무엇을 검색해야 할지 모르는 사용자를 위한 검색 가이드검색 엔진 및 한글 형태소 분석기 변경을 통해 사용자에게 정확한 검색 결과를 제공하는 게 우선순위였습니다. 그리고 광고 상품을 제거하고 사용자가 다양한 서비스를 찾을 수 있게 도와주는 기능을(자동완성검색, 연관검색어, 인기검색어) 추가했습니다. 그뿐만 아니라 서비스 검색에 최적화된 검색 플로우를 위해 UI 개선도 진행했습니다.새로운 검색 엔진새로운 검색엔진을 찾던 중 은전한닢 한글 형태소 분석기를 공식으로 지원하는 엘라스틱서치를 찾았습니다.17개 검색 엔진 순위 (출처: DB-ENGINES)17개 검색 엔진의 순위를 살펴보면 아파치 루씬 기반의 엘라스틱서치가 다른 검색 엔진보다 100점 넘게 차이 나는 압도적인 점수를 기록하고 있습니다. 위의 점수는 구글이나 빙에서 언급 횟수, 구글 트렌드, 기술적 논의 횟수, 채용 공고, 소셜 네트워크에서 언급 횟수 등으로 측정한 점수입니다. 점수 산정 방법이 객관적이지 못하지만 엘라스틱서치가 핫하다는 것에는 이견이 없었습니다. 이에 본격적으로 엘라스틱서치에 대해서 기술 탐색을 시작했으며 스핑크스와 비교도 해봤습니다.엘라스틱서치엘라스틱서치는 확장성이 뛰어난 RESTful 검색 및 분석 엔진입니다. 대용량 데이터를 빠르고 실시간으로 저장, 검색 및 분석할 수 있습니다. 기술 탐색 결과 엘라스틱서치에 저장한 데이터를 키바나를 통해서 분석하고 시각화할 수 있다는 점이 매력적이었고, 공식으로 한글 형태소 분석기를 지원하기 때문에 검색 정확도를 높일 수 있다고 생각했습니다. 한글 형태소 분석기를 이용한 엘라스틱서치의 분석 과정은 다음과 같습니다.한글 형태소 분석기를 이용한 엘라스틱서치의 분석 과정필드의 title에 블로그 검색에 엘라스틱서치를 적용해보려고 합니다. 라는 문장이 있다면 지정한 analyzer를 통해서 분석을 진행합니다. 먼저 문자 필터를 거치고 은전한닢으로 한글 형태소 분석을 수행합니다. 형태소 분석이 완료되면 [블로그, 검색, 엘라스틱, 서치, 적용, 보, 하]로 나누어집니다. 그리고 토큰 필터를 통해 [블로그, 검색, 엘라스틱, 일래스틱, elasticsearch, es, 서치, 적용, 보, 하]로 term이 만들어집니다. 이 term은 elasticsearch index에 문서 id와 함께 저장됩니다.다음은 엘라스틱서치와 스핑크스를 비교해봤습니다.엘라스틱서치 vs 스핑크스엘라스틱서치 vs 스핑크스엘라스틱서치와 스핑크스를 비교해보면 스핑크스도 충분히 좋은 검색엔진이지만 한글형태소 분석기와 키바나의 시각화, 데이터 분석 같은 장점을 활용하기 위해 엘라스틱서치를 도입하기로 했습니다.도입을 결정하고 엘라스틱서치를 구축하는 방법을 알아봤습니다.  1. 엘라스틱 클라우드를 사용하는 방법  2. AWS Elasticsearch Service를 이용해서 구축하는 방법3. EC2 인스턴스에 오픈소스 엘라스틱서치를 직접 설치해서 구축하는 방법   엘라스틱서치를 구축하는 방법에는 보통 3가지 방법이 있고 아래의 특징을 가지고 있습니다.1번은 엘라스틱에서 관리 및 교육, 컨설팅을 지원해줍니다. 그리고 한글 형태소 분석기 은전한닢을 지원합니다. 최신 버전의 엘라스틱 스택을 바로 사용할 수 있으며 모니터링 기능도 지원합니다. 라이선스 별 지원은 링크를 통해서 확인할 수 있습니다.2번은 AWS에서 제공하는 Elasticsearch Service이며, 관리형 서비스입니다. 같은 VPC에 묶여있는 인스턴스를 통해서만 접근할 수 있게 되어있으며 외부에서는 접근할 수 없습니다.(퍼블릭 액세스도 있으나 AWS에서 권장하지 않습니다.) 키바나를 사용하기 위해서는 같은 VPC의 인스턴스 웹 서버 프록시나 AWS 코그니토로 접근해야 합니다. 한글 형태소 분석기 은전한닢을 지원하지만 다른 플러그인은 지원하지 않는 경우가 많이 있습니다. AWS Elasticsearch Service에서 지원하는 플러그인 리스트는 여기에서 확인할 수 있습니다.3번은 EC2 인스턴스에 오픈소스 엘라스틱서치를 설치해서 사용하는 방법입니다. 직접 서버를 구축하는 방법이기 때문에 사용자가 어떻게 사용하느냐에 따라 달라집니다.크몽 개발팀은 가격, 관리적 측면을 고려한 결과 2번 AWS Elasticsearch Service로 구축을 진행했습니다.구현구현은 엘라스틱에서 라라벨 프레임워크에서 사용할 수 있는 엘라스틱서치 관련 라이브러리를 정리해둔 링크를 참고했습니다. 3개의 라이브러리 중 스타가 제일 많은 Plastic 라이브러리를 사용해서 구현을 시도한 적이 있었는데 몇 가지 장점이 있었지만 엘라스틱서치 5까지만 지원을 하므로 field type에 text, keyword가 존재하지 않아 매핑하는데 문제가 있었습니다. 그리고 아직 지원하지 않는 쿼리도 존재하기 때문에 결국에는 PHP 공식 엘라스틱서치 클라이언트 라이브러리인 Elasticsearch-PHP를 사용해야 되는 상황도 발생했습니다. 위에서 말한 점 때문에 Plastic 라이브러리를 걷어내고 Elasticsearch-PHP만 이용해서 개발을 진행했습니다. 엘라스틱에서 제공하는 Elasticsearch-PHP 가이드도 잘 정리되어있습니다. 더욱 자세한 구축, 구현 방법을 알고 싶으신 분들은 아래의 글에서 확인하실 수 있습니다.라라벨 프레임워크 - 엘라스틱서치 사용 경험기 : 초기 작업 수행라라벨 프레임워크 - 엘라스틱서치 사용 경험기 : 문서 관리 작업 수행결과검색 기능 개선 결과는 아래와 같습니다,1.자동완성검색자동완성검색 기능2. 연관검색어 + 검색 결과 광고 제거연관검색어 및 검색결과 광고 제거3. 키워드와 관련된 카테고리 추천키워드와 관련된 카테고리 추천4. 검색 결과가 없는 키워드에는 인기검색어 추천검색 결과가 없는 키워드에는 인기검색어 추천무엇을 검색해야 할지 모르는 사용자를 위한 검색 가이드를 만들기 위해 노력했으며, 기능 추가로 사용자의 검색 만족도와 정확도를 높이려고 노력했습니다.또한 엘라스틱서치와 한글 형태소 분석기 은전한닢을 이용해 검색 기능 개선을 통한 결과 평균 체류 시간은 20초 정도 증가했으며 종료율은 최대 22.4%, 평균 1% 정도 떨어졌습니다. 또한 서비스 상세페이지 전환율은 최대 78.3%, 평균 3% 이상 증가했습니다. 서비스 상세페이지 전환율의 상승은 사용자의 검색 만족과 검색 정확도가 상승했다고 볼 수 있습니다.정리이번 글에서는 엘라스틱서치와 한글 형태소 분석기 은전한닢을 이용해 검색 기능을 개선한 이야기를 정리해봤습니다. 검색 기능 개선 이후 서비스 상세페이지 전환율이 조금씩 상승 중입니다. 릴리즈한지 두 달 정도밖에 되지 않아 조금 더 지켜봐야 하겠지만 전환율이 조금씩 상승하고 있다는 건 좋은 신호인 거 같습니다. 다만 짧은 글을 통해서 경험을 전달하려고 하니 많은 내용을 담지 못한 것 같아 아쉽습니다. 다음에는 더욱더 깊이 있는 글을 전달할 수 있는 에이든이 되겠습니다. 감사합니다.#크몽 #개발팀 #개발자 #개발문화 #경험공유 #인사이트
조회수 1578

RxJava2 함수 파헤치기!

Overview지난 글 Rxjava를 이용한 안드로이드 개발에서는 RxJava의 Android 연결 방법과 기본적인 사용법을 다뤘습니다. 이번 글에서는 RxJava의 강력하고 다양한 함수들을 살펴보고자 합니다. Android에서 복잡하게 구현되는 내용들을 단 몇 개의 함수로 처리할 수 있는 RxJava를 꼭 사용해보길 권합니다.1. just2. fromArray/fromlterable3. range/rangLong4. interval5. timer6. map7. flatMap8. concatMap9. toList10. toMap11. toMultiMap12. filter13. distinct14. take15. skip16. throttleFirst17. throttleLast18. throttleWithTimeout참고: 공통적으로 사용하는 구독(수신) 클래스는 아래와 같습니다.static class CustomSubscriber<T> extends DisposableSubscriber<T> { @Override public void onNext(T t) { System.out.println(Thread.currentThread().getName() + " onNext( " + t + " )"); } @Override public void onError(Throwable t) { System.out.println(Thread.currentThread().getName() + " onError( " + t + ")"); } @Override public void onComplete() { System.out.println(Thread.currentThread().getName() + " onComplete()"); } } 1. just파라미터를 통해 받은 데이터로 Flowable을 생성하는 연산자입니다. 최대 10까지 전달할 수 있고, 모든 데이터가 수신되면 onComplete() 수신됩니다. 기본적인 Flowable 생성자 함수로 볼 수 있으며 단순 작업에서 많이 사용합니다.public static void just() { //파라미터 값을 순차적으로 송신하는 Flowable 생성 Flowable<String> flowable = Flowable.just("A", "B", "C", "D", "E", "F"); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( A ) main onNext( B ) main onNext( C ) main onNext( D ) main onNext( E ) main onNext( F ) main onComplete() 2. fromArray/fromIterablefromArray, fromIterable 함수는 파리미터로 배열 또는 Iterable(리스트 등)에 담긴 데이터를 순서대로 Flowable을 생성하는 연산자입니다. 모든 데이터를 순차적으로 송신 후 완료됩니다. 반복적인 데이터 변환 작업 같은 경우 for 문 대신 대체할 수 있습니다. 결과를 보면 main Thread 에서 작업 결과가 나오지만, flatMap 을 사용한다면 별도의 Thread로 main Thread의 부하를 막을 수 있습니다.1. fromArray public static void fromArray() { //fromArray 배열로 파라미터를 전달 받는다. Flowable<String> flowable = Flowable.fromArray("A", "B", "C", "D", "E"); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( A ) main onNext( B ) main onNext( C ) main onNext( D ) main onNext( E ) main onComplete() 2. fromIterable public static void fromIterable() { List<String> list = Arrays.asList("A", "B", "C", "D", "E"); //fromIterable 리스트로 파라미터를 전달받는다. Flowable<String> flowable = Flowable.fromIterable(list); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( A ) main onNext( B ) main onNext( C ) main onNext( D ) main onNext( E ) main onComplete() 파라미터와 함수는 다르지만 동일하게 처리된다. 3. range/rangLongrange 함수는 지정한 숫자부터 지정한 개수만큼 증가하는 Integer 값 데이터를 송신하는 Flowable를 생성합니다. rangLong 함수는 range와 동일하며 데이터 타입은 Long을 사용합니다. 두 함수 데이터 송신을 마치면 onComplete를 송신합니다.1. range public static void range() { //range(int start, int count) //start : 시작 값 //end : 발생하는 횟수 Flowable<Integer> flowable = Flowable.range(10, 5); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( 10 ) main onNext( 11 ) main onNext( 12 ) main onNext( 13 ) main onNext( 14 ) main onComplete() 2. rangLong public static void rangeLong() { //range(int start, int count) //start : 시작 값 //end : 발생하는 횟수 Flowable<Long> flowable = Flowable.rangeLong(10, 5); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( 10 ) main onNext( 11 ) main onNext( 12 ) main onNext( 13 ) main onNext( 14 ) main onComplete() 4. interval지정한 간격마다 0부터 시작해 Long 타입 숫자의 데이터를 송신하는 Flowable을 생성합니다. 데이터는 0, 1, 2, 4 순차적으로 증가된 데이터를 송신합니다. Android 에서는 반복적인 작업인 TimerTask를 대신해서 interval로 간단하게 처리할 수 있습니다. UI 변경이 필요한 부분에서는 interval scheduler를 AndroidSchedulers.mainThread() 를 변경해 적용할 수 있습니다.public static void interval() { //(long time, TimeUnit unit, Scheduler scheduler) //time : 발생 간격 시간 //unit : 간격 시간 단위 //scheduler : 발생 scheduler를 변경하여 사용할 수 있습니다. // ex)AndroidSchedulers.mainThread() // - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 // 1초 간격으로 데이터 요청을 송신하다. Flowable<Long> flowable = Flowable .interval(1000L, TimeUnit.MILLISECONDS).take(10); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 1 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onNext( 5 ) RxComputationThreadPool-1 onNext( 6 ) RxComputationThreadPool-1 onNext( 7 ) RxComputationThreadPool-1 onNext( 8 ) RxComputationThreadPool-1 onNext( 9 ) 5. timertimer 함수는 호출된 시간부터 일정한 시간 동안 대기하고 Long 타입 0을 송신 및 종료하는 flowable을 생성합니다. interval이 조건까지 반복적으로 송신한다면, timer는 한번만 송신하고 종료됩니다.public static void timer() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd hh:mm ss"); System.out.println("현재시간 : " + simpleDateFormat.format(System.currentTimeMillis())); //(long time, TimeUnit unit, Scheduler scheduler) //time : 발생 간격 시간 //unit : 간격 시간 단위 //scheduler : 발생 scheduler를 변경하여 사용할 수 있습니다. // ex)AndroidSchedulers.mainThread() Flowable<Long> flowable = Flowable.timer(1000L, TimeUnit.MILLISECONDS); //구독을 시작한다. flowable.subscribe(value -> { System.out.println(" timer : " + simpleDateFormat.format(System.currentTimeMillis())); }, throwable -> { System.out.println(throwable); }, () -> { System.out.println(" complete"); }); } 결과 현재시간 : 2019.04.29 09:09 56 timer : 2019.04.29 09:09 57 complete 6. mapFlowable 에서 송신하는 데이터를 변환하고, 변환된 데이터를 송신하는 연산자입니다. 하나의 데이터만 송신할 수 있으며, 반드시 데이터를 송신해야 합니다. 혹여 송신되는 데이터가 null 을 포함하면 map 대신 아래의 flatMap 을사용하는 것이 좋습니다.public static void map() { Flowable<String> flowable = Flowable.just("A", "B", "C", "D", "E") //map(Function mapper) //mapper : 받은 데이터를 가공하는 함수형 인터페이스 //알파벳 값을 소문자로 변경하여 return 한다 .map(value -> value.toLowerCase()); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( a ) main onNext( b ) main onNext( c ) main onNext( d ) main onNext( e ) main onComplete() 7. flatMapflatMap은 map과 동일한 함수이지만, map과는 달리 여러 데이터가 담긴 Flowable을 반환할 수 있습니다. 또한 빈 Flowable를 리턴해 특정 데이터를 건너뛰거나 에러 Flowable를 송신할 수 있습니다.파라미터 mapper에서 새로운 Flowable의 데이터 전달이 아닌 다른 타임라인 Flowable로 작업하면 들어온 데이터 순서대로 출력을 지원하지 않습니다. 타임라인 Flowable(timer, delay, interval 등)에서는 가급적 사용을 피하거나, 순서에 지장이 없을 때 사용하는 것이 좋습니다.public static void flatMap() { Flowable<String> flowable = Flowable.range(10, 2) //flatMap(Function mapper, BiFunction combiner) //mapper : 받은 데이터로 새로운 Flowable를 생성하는 함수형 인터페이스 //combiner : mapper가 새로 생성한 Flowable 과 원본 데이터를 조합해 새로운 송신 데이트를 생성하는 함수형 인터페이스 //첫 번째 데이터를 받으면 새로운 Flowable를 생성한다. //take(3) : 3개까지만 발생한다. .flatMap(value -> Flowable.interval(100L, TimeUnit.MILLISECONDS).take(3), (value, newData) -> "value " + value + " newData " + newData); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( value 10 newData 0 ) RxComputationThreadPool-2 onNext( value 11 newData 0 ) RxComputationThreadPool-1 onNext( value 10 newData 1 ) RxComputationThreadPool-2 onNext( value 11 newData 1 ) RxComputationThreadPool-1 onNext( value 10 newData 2 ) RxComputationThreadPool-2 onNext( value 11 newData 2 ) RxComputationThreadPool-2 onComplete() 결과를 보면 각기 생성된 Flowable이 비동기식으로 송신 되기때문에 서로 다른 스레드에서 실행돼 데이터를 받는 순서대로 송신하지 않는다는 점을 주목하자 8. concatMap받은 데이터를 Flowable로 변환하고 변환된 Flowable을 하나씩 순서대로 실행해서 수신자에서 송신합니다. 다시 말해 여러 데이터를 계속 받더라도 첫 번째 데이터로 생성한 Flowable 의 처리가 끝나야 다음 데이터로 생성한 Flowable을 실행하는 것입니다.생성된 Flowable의 스레드에서 실행되더라도 데이터를 받은 순서대로 처리하는 것을 보장하지만, 처리 성능에 영향을 줄 수 있습니다.public static void concatMap() { Flowable<String> flowable = Flowable.range(10, 5) //map(Function mapper) //mapper : 받은 데이터를 가공하는 함수형 인터페이스 .concatMap(value -> Flowable.interval(100L, TimeUnit.MILLISECONDS).take(2) .map(data -> ("value : " + value + " data : " + data))); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( value : 10 data : 0 ) RxComputationThreadPool-1 onNext( value : 10 data : 1 ) RxComputationThreadPool-2 onNext( value : 11 data : 0 ) RxComputationThreadPool-2 onNext( value : 11 data : 1 ) RxComputationThreadPool-3 onNext( value : 12 data : 0 ) RxComputationThreadPool-3 onNext( value : 12 data : 1 ) RxComputationThreadPool-4 onNext( value : 13 data : 0 ) RxComputationThreadPool-4 onNext( value : 13 data : 1 ) RxComputationThreadPool-5 onNext( value : 14 data : 0 ) RxComputationThreadPool-5 onNext( value : 14 data : 1 ) RxComputationThreadPool-5 onComplete() 결과를 보면 생성된 Flowable 스레드와 데이터 순서대로 출력이 보장된다 것을 알 수 있다. 9. toListtoList는 송신할 데이터를 모두 리스트에 담아 전달합니다. 한꺼번에 데이터를 List로 가공해서 받기에 좋습니다. 하지만 많은 양의 데이터를 처리할 경우 버퍼가 생길 수 있고, 쌓은 데이터 때문에 메모리가 부족해질 수도 있습니다. 또한 수신되는 데이터는 하나이므로 Flowable이 아닌 Single 반환값을 사용합니다.public static void toList() { Single<List<String>> single = Flowable.just("A", "B", "C", "D", "E", "F") .toList(); // 구독을 시작한다. single.subscribe(new SingleObserver<List<String>>() { @Override public void onSubscribe(Disposable d) { System.out.println(Thread.currentThread().getName() + " onNext()"); } @Override public void onSuccess(List<String> strings) { //최종 완료된 리스트를 순서대로 출력한다. for (String text : strings) { System.out.println(Thread.currentThread().getName() + " onSuccess( " + text + " )"); } } @Override public void onError(Throwable e) { System.out.println(Thread.currentThread().getName() + " onError() " + e); } }); } 결과 main onNext() main onSuccess( A ) main onSuccess( B ) main onSuccess( C ) main onSuccess( D ) main onSuccess( E ) main onSuccess( F ) 10. toMaptoMap은 송신할 데이터를 모두 키와 값의 쌍으로 Map에 담아 전달합니다. 나머지는 toList의 특징과 같습니다. 송신되는 데이터 타입은 Map에 담아서 송신하는데 동일한 key에서 value는 마지막 데이터가 덮어 씁니다. 요청되는 값보다 결과 값이 적을 수도 있습니다. List 값을 손쉽게 key, value로 분리할 수 있는 함수이기도 합니다.public static void toMap() { Single<Map<Long, String>> single = Flowable.just("1A", "2B", "3C", "1D", "2E") //toMap(Fuction keySelector, Function valueSelector, Callable mapSupplier) //keySelector : 받은 데이터로 Map에서 사용할 키를 생성하는 함수형 인터페이스 //valueSelector : 받은 데이터로 Map 넣을 값을 생성하는 함수형 인터페이스 .toMap(value -> Long.valueOf(value.substring(0, 1)), data -> data.substring(1)); //구독을 시작한다. single.subscribe(new SingleObserver<Map<Long, String>>() { @Override public void onSubscribe(Disposable d) { System.out.println(Thread.currentThread().getName() + " onNext()"); } @Override public void onSuccess(Map<Long, String> longStringMap) { //최종 완료된 map을 순서대로 출력한다. for (long id : longStringMap.keySet()) { System.out.println(Thread.currentThread().getName() + " onSuccess( id : " + id + ", value " + longStringMap.get(id) + " )"); } } @Override public void onError(Throwable e) { System.out.println(Thread.currentThread().getName() + " onError() " + e); } }); } 결과 main onNext() main onSuccess( id : 1, value D ) main onSuccess( id : 2, value E ) main onSuccess( id : 3, value C ) 11. toMultiMap키와 컬렉션 값으로 이루어진 Map을 데이터로 변환하여 송신하는 함수입니다. 나머지 특징은 toList, toMap과 같습니다. toMap에서 중복되는 value를 관리하는 건 없었지만, value를 collection으로 관리하여 전달되는 데이터를 모두 수신할 수 있습니다.public static void toMultiMap() { Single<Map<String, Collection<Long>>> single = Flowable.interval(100L, TimeUnit.MILLISECONDS) .take(5) //toMultimap(Function keySelector, Function valueSelector) .toMultimap(value -> { //value가 홀수인지 짝수 인지 판단해서 key값을 리턴한다. if (value % 2 == 0) { return "짝수"; } else { return "홀수"; } }); //구독을 시작한다. single.subscribe(new SingleObserver<Map<String, Collection<Long>>>() { @Override public void onSubscribe(Disposable d) { System.out.println(Thread.currentThread().getName() + " onNext( " + d + " )"); } @Override public void onSuccess(Map<String, Collection<Long>> stringCollectionMap) { for (String key : stringCollectionMap.keySet()) { StringBuffer stringBuffer = new StringBuffer(); for (long value : stringCollectionMap.get(key)) { stringBuffer.append(" " + value); } System.out.println(Thread.currentThread().getName() + " onSuccess( id : " + key + ", value " + stringBuffer.toString() + ")"); } } @Override public void onError(Throwable e) { System.out.println(Thread.currentThread().getName() + " onError() " + e); } }); } 결과 main onNext() RxComputationThreadPool-1 onSuccess( id : 짝수, value 0 2 4 ) RxComputationThreadPool-1 onSuccess( id : 홀수, value 1 3 ) 12. filterfilter는 받은 데이터가 조건에 맞는지 판단해 결과가 true인 값만 송신합니다. 위의 just, fromArray, interval이 반복적인 케이스였다면, filter는 if문처럼 조건문의 역할을 할 수 있습니다. 반복문 함수와 조건문 함수를 같이 사용해 몇 줄 안에 for, if와 똑같이 구현할 수 있죠.public static void filter() { Flowable<Long> flowable = Flowable.interval(300L, TimeUnit.MILLISECONDS) //짝수만 통과한다. 3개만큼 .filter(value -> value % 2 == 0).take(3); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 13. distinct이미 처리된 데이터를 다시 볼 필요가 없을 때 사용하는 함수입니다. 송신하려는 데이터가 이미 송신된 데이터와 같다면 해당 데이터는 무시합니다. 이 함수는 내부에서 HashSet으로 데이터가 같은지 확인합니다.public static void distinct() { Flowable<String> flowable = Flowable.just("A", "a", "B", "b", "A", "a", "B", "b") //distinct(Function keySelector) //keySelector : 받은 데이터와 비교할 데이터를 확인하는 함수 //모두 소문자로 변환하여 알파벳 기준으로 데이터를 판단한다. .distinct(value -> value.toLowerCase()); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 main onNext( A ) main onNext( B ) main onComplete() 14. take1.taketake 함수로 지정된 횟수만큼 받은 데이터를 송신합니다. 지정된 횟수에 도달하면 완료를 송신해 처리 종료합니다.2.takeUntil지정된 조건까지 데이터를 송신하는 연산자입니다. 조건이 되면 완료를 송신해 종료합니다.3.takeWhile지정된 조건이 해당할 때만 데이터를 송신하는 연산자입니다.4.takeLast데이터의 끝에서부터 지정한 조건까지 데이터를 송신하는 연산자입니다.take 함수는 한 화면에 출력되거나 칠요한 데이터만큼 리스트에서 값을 하나씩 수신할 때 사용합니다. 예를 들어 화면에 데이터가 6개가 필요하면 take를 이용해 원하는 만큼의 데이터를 가져올 수 있습니다.Flowable.take(6) 또한 이후에 나올 skip 함수를 같이 사용하면 두 번째 화면에서 필요한 데이터를 6개 가져올 수 있습니다.Flowable.skip(6).take(12) 1. take public static void take() { // 100 밀리세컨드만큼 반복하며 총 5개를 출력후 종료한다. Flowable<Long> flowable = Flowable.interval(100L, TimeUnit.MILLISECONDS) .take(5); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 1 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 2. takeUntil public static void takeUntil() { // 100 밀리세컨드만큼 반복하며 값이 5가 될때까지 송신한다. Flowable<Long> flowable = Flowable.interval(100L, TimeUnit.MILLISECONDS) .takeUntil(value -> value == 5); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 1 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onNext( 5 ) RxComputationThreadPool-1 onComplete() 3. takeWhile public static void takeWhile() { // 100 밀리세컨드만큼 반복하며 값이 5가 아닐경우까지 송신한다. Flowable<Long> flowable = Flowable.interval(100L, TimeUnit.MILLISECONDS) .takeWhile(value -> value != 5); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 1 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 4. takeLast public static void takeLast() { //100밀리 세컨트만큼 반복하며 5개의 출력중 뒤에 2개만 송신한다. Flowable<Long> flowable = Flowable.interval(100L, TimeUnit.MILLISECONDS) .take(5) .takeLast(2); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 15. skip1.skip함수로 지정된 횟수만큼 받은 데이터 송신을 제외합니다. 지정된 횟수가 초과되면 나머지 데이터를 송신합니다.2.skipUntil지정된 조건까지 데이터 송신을 제외하는 연산자입니다. 조건이 되면 나머지 데이터를 송신합니다.3.skipWhile지정된 조건이 해당될 때만 데이터 송신을 제외하는 함수입니다.4.skipLast데이터의 끝에서부터 지정한 조건까지 데이터 송신을 제외하는 함수입니다.take와 반대의 기능을 갖고 있습니다. 보통 페이저나 리스트에서 paging을 처리할 때는 take와 skip을 혼용합니다.1. skip public static void skip() { //100 밀리세컨드만큼 반복하며 5번 발행하고, 처음 2개를 제외합니다. Flowable<Long> flowable = Flowable.interval(100L, TimeUnit.MILLISECONDS) .take(5) .skip(2); //구독을 시잔한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 2. skipUntil public static void skipUntil() { //300밀리 세컨드만큼 반복하며 5개를 발행하고, 1000 밀리세컨드 제외 후 송신합니다. Flowable<Long> flowable = Flowable.interval(300L, TimeUnit.MILLISECONDS) .skipUntil(Flowable.timer(1000L, TimeUnit.MILLISECONDS)) .take(5); //구독을 시잔한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-2 onNext( 3 ) RxComputationThreadPool-2 onNext( 4 ) RxComputationThreadPool-2 onNext( 5 ) RxComputationThreadPool-2 onNext( 6 ) RxComputationThreadPool-2 onNext( 7 ) RxComputationThreadPool-2 onComplete() 3. skipWhile public static void skipWhile() { //300밀리세컨드만큼 반복하며 5개를 발행하고, 데이터 3이 올때까지 데이터를 제외힙니다. Flowable<Long> flowable = Flowable.interval(300L, TimeUnit.MILLISECONDS) .skipWhile(value -> value != 3) .take(5); //구독을 시잔한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 3 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onNext( 5 ) RxComputationThreadPool-1 onNext( 6 ) RxComputationThreadPool-1 onNext( 7 ) RxComputationThreadPool-1 onComplete() 4. skipLast public static void skipLast() { //1000 밀리세컨드만큼 반복하며 5개를 발행하고 마지막 2개는 제외합니다 Flowable<Long> flowable = Flowable.interval(1000L, TimeUnit.MILLISECONDS) .take(5) .skipLast(2); //구독을 시작한다. flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 1 ) RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onComplete() 16. throttleFirst데이터를 송신하고 지정된 시간 동안 들어오는 요청을 무시합니다. 이 함수는 View의 Event 처리에서 많이 사용됩니다. 중복되는 처리를 막기 위해 최초 실행 후 일정 시간 동안 View의 클릭 이벤트나 API 이벤트를 막을 수 있기 때문에 비동기 처리와 화면에 직접적인 피드백이 발생했을 때 throttleFirst를 자주 사용하고 있습니다. //데이터 요청이 30 밀리초마다 5번 발생합니다. //데이터 요청 발생시 100 밀리세컨트 동안 들어오는 데이터 요청을 무시합니다. // — 0 — 1 — 2 — 3 — 4 interval 30 밀리초 마다 // — — -*- — throttleFirst 100 밀리초 무시 Flowable<Long> flowable = Flowable.interval(30L, TimeUnit.MILLISECONDS) .take(5).throttleFirst(100L, TimeUnit.MILLISECONDS); flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 0 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 17. throttleLastthrottleLast 함수는 데이터를 송신하고 지정된 시간 동안 들어오는 마지막 요청을 송신합니다. 이 함수도 throttleFirst처럼 반복적인 선택 이벤트 처리에 유용하게 사용할 수 있습니다. 간단하게 장바구니 카운트 변경을 요청할 때 마지막 변경 이벤트 데이터만 처리하면 되므로 값이 선택되고 일정 시간이 지났을 때 API를 요청해 리소스 낭비를 줄일 수 있습니다.public static void throttleLast() { //데이터 요청이 1 초 마다 6번 발생합니다. //데이터 요청 발생시 2 초 동안 들어오는 마지막 요청을 송신하다. // - 0 - 1 - 2 - 3 - 4 interval 1 초 마다 // - - -* - throttleLast 2 초의 마지막 값 송신 Flowable<Long> flowable = Flowable.interval(1, TimeUnit.SECONDS) .take(5) .throttleLast(2, TimeUnit.SECONDS); flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( 2 ) RxComputationThreadPool-1 onNext( 4 ) RxComputationThreadPool-1 onComplete() 18. throttleWithTimeoutthrottleWithTimeout 함수는 데이터를 송신하고 지정된 시간 동안 다음 데이터를 받지 못하면 현재 데이터를 송신합니다. 완료 시엔 마지막 데이터를 송신하고 종료됩니다.public static void throttleWithTimeout() { Flowable<String> flowable = Flowable.<String>create(emitter -> { emitter.onNext("A"); Thread.sleep(1000L); // 1000 밀리세컨드 슬립 // 500 밀리세컨드 동안 데이터 다음 데이터 요청이 없으므로 A 송신 emitter.onNext("B"); Thread.sleep(300L); // 300 밀리세컨드 슬립 emitter.onNext("C"); Thread.sleep(300L); // 300 밀리세컨드 슬립 emitter.onNext("D"); Thread.sleep(1000L); // 1000 밀리세컨드 슬립 // 500 밀리세컨드 동안 데이터 다음 데이터 요청이 없으므로 D 송신 emitter.onNext("E"); Thread.sleep(100L); // 100 밀리세컨드 슬립 emitter.onComplete(); //완료 요청 시 마지막 데이터 송신 후 종료 }, BackpressureStrategy.BUFFER) .throttleWithTimeout(500L, TimeUnit.MILLISECONDS); flowable.subscribe(new CustomSubscriber<>()); } 결과 RxComputationThreadPool-1 onNext( A ) RxComputationThreadPool-1 onNext( D ) main onNext( E ) main onComplete() ConclusionRxJava에서 많이 사용되고, 또 알고 있으면 좋은 함수들을 살펴봤습니다. 브랜디에서도 이 함수들을 응용해 그동안 다양한 기능을 구현했고, 복잡한 함수도 사용하고 있습니다. 지금까지는 Flowable로 송신과 수신이 1 : 1 로 진행되었지만, 다양한 수신자를 사용해 하나의 Flowable로도 다른 화면에서 여러 수신자를 등록하여 반복적인 작업을 할 수 있습니다. 덕분에 같은 작업을 코드 중복 없이 간단하게 구현할 수 있죠.다음 글에서는 2개 이상의 Flowable을 결합해 사용하는 방법과 Android View에서 RxJava를 응용하는 방법, 구독을 관리하는 방법 등 Android에서 유용하게 쓰는 방법들을 알아보겠습니다.글고재성 팀장 | R&D 개발MA팀[email protected]브랜디, 오직 예쁜 옷만
조회수 1039

[인터뷰] 내 입맛에 맞게, 내가 차린 회사! 미미박스 CEO 디노의 인터뷰 by Sellev

안녕하세요!미미박스의 소식을 전해주는 Ava입니다!오늘은 미미박스의 CEO인 디노(하형석)의 인터뷰를 여러분께 소개해드리려고 합니다. 바로 며칠 전 영감을 주는 인터뷰가 가득한 SELLEV에 디노의 인터뷰가 올라왔는데요.매일매일(출장이 있으실 때 빼고) 만나는 디노이지만이렇게 또 CEO의 생각을 접하게 되니어떻게 오늘 하루를 보내야 할지 다짐을 한 번 더 하게 되네요!그럼 디노가 영상에서 어떤 이야기를 나눴는지 간단히소개 드리겠습니다! #동기부여흔히 실리콘밸리에 있는 기업들을 떠올리면 '복지', '자유' 등을 떠올리는데요.디노가 느낀 실리콘밸리는 자기 동기부여를 통해 일하는 사람들로 꽉 찬 곳이었다고 합니다.이를 보고 위기의식을 느낀 디노는 '우리도 자기 동기부여를 통해 일할 수 있는 회사'를 만들자고 하게 된 것이죠.#창업 계기 '오직 일에만 집중할 수 있는 회사'국내 회사의 문화들을 보면서 디노는 '일에만 집중할 수 있는 회사'를 가고 싶었다고 합니다.하지만 국내에는 그런 회사가 없지 않을까? 하는 생각을 했고, 결국 자신이 꿈꾸는 회사를 직접 창업하게 된 것이죠!#리더십리더십은 경력, 직급, 직책에서 나오는 것일까요?디노는 '오늘 입사한 인턴도 미미박스에 새로운 비전을 제시할 수 있다'고 얘기합니다.그렇게 미미박스는 '반란을 꿈꾸는 사람들'이 모이게 된 것이죠!리더십에 가장 중요한 것은 확고한 철학 아닐까요? #당장 실행하세요'당장 실행하세요'미국에 진출하게 된 것도, 이렇게 미미박스가 성장하게 된 것도 이런 디노의 철학을 바탕으로 나온 결과물이라는 생각이 들어요. 실제로 디노는 만나고 싶은 사람이 있으면 SNS를 통해 물어보고 연락한다고 합니다.고민하고 정의하는 것보다 먼저 실행하는 것! 그것이 영감이 되고 경쟁력이 되지 않을까요?이렇게 디노의 이야기를 만나보았습니다. 여러분은 어떤 생각이 드시나요!?여러분도 오늘 만나고 싶었던 사람이 있다면,혹은 해보고 싶었던 일이 있었다면 작은 것이라도 한번 실행해 보는 건 어떨까요. 
조회수 1184

스타트업에게 비전이란?

먹고사는 것도 바쁜 스타트업에게 비전(vision)이란 어떤 의미가 있을까? 사실 파펨(paffem)에게도 지금 당장 비전이 있다고 해서, 지금 진행하고 있는 향기 사업이 더 잘될 것이라는 보장도 없다. 그런데 왜 이 작은 회사가 비전이라는 것을 생각하게 될까? 물론 정확한 답을 드릴 수는 없겠지만.. 내가 생각하는 이유로는 1) 스스로의 존재에 대한 이유예전 경영학과 수업에서 주식회사의 존재 이유는 "주주가치의 극대화"라는 내용을 들었던 기억이 나는데, 물론 맞는 말이지만.. 누군가에게 돈을 벌어주는 것 외에도, 이 기업이 세상에 존재하는 이유가 없다면 그것 또한 공허한 일이다. 엄청난 부를 만들어냈지만, 내가 이 세상에 그 부를 가지고 어떤 의미 있는 일을 할 수 있을까?라는 생각이 없다면... 그것 또한 비참할 듯2) 하루하루 0.1mm 라도 성장하기 내가 지금 어디로 가고 있는지를 알고 한걸음을 떼는 것과, 그냥 일단 한걸음을 떼는 것에는 큰 차이가 있다는 생각을 가지고 있다. 머릿속에 내가 가야 할 방향이 있다면.. 아주 조금이라도 그 고민이 하루의 노력에 묻어날 것이고, 또한 그렇게 생각하는 사람 주변에는 그것을 도와줄 운과 사람, 기회가 생겨나기 마련.(아래의 수식은 그것을 너무나도 잘 보여주는 것이라 나도 한번 인용해 봄)1.01^365=37.80.99^365=0.033) 꿈이 없다면.. 갈 길이 너무 힘들지 않을까?같은 스타트업 내에서 공유되고 공감하는 꿈이 있다면, 그 길이 힘들더라도 서로 응원해 가면서 갈 수 있으리라는 생각. 희망이 있는 사람들에게서는 에너지가 느껴질 수밖에 없고, 그것을 만들기 위해 여러 가지로 고민을 하기 때문. 그런 차원에서 파펨은.... 파펨은 "후각의 객관화"라는 비전을 가지고 있습니다. 말이 조금 어려울 수도 있는데.. 후각이라는 영역은 인간의 인지의 10~15% 정도를 차지하기에 중요도가 높지 않아, underdeveloped 될 수밖에 없었고, 그것 외에도 후각의 영역은 객관적으로 표현이 어렵다는 점도 크다. 즉, 난이도가 높다는 것인데..  Color는 채도와 명도, 그리고 3 원색의 조합을 통해 객관적으로 설명할 수 있고, 청각의 경우는 인간들이 다양한 기준(길이, 높낮이, 진동폭 등)으로 이미 표준화가 진행되어 있는 것을 봐도 잘 알 수 있을 듯. 몇 년 전 Google이 Google Nose beta 가 출시되었음을 알렸는데, 검색한 키워드의 냄새가 랩탑, 스마트 폰 등을 통해서 확인이 가능하다는 것이었음. 난 이게 만우절 농담인지 모르고 (ㅡㅡ;;), 진짜 세상이 많이 발전했구나 역시 구글!!!...이라고 생각하였으나.. 너무나도 진지한 만우절 장난이었다는.... You tube 동영상  (꼭 한번 보시길.. 구글이 어찌나 진지하게 이 동영상을 만들었는지 ㅜㅜ)  장난이었지만, 당시에 그 영상을 보면서.. 저런 기술이 있으면 좋기는 하겠네..라는 생각을 하게 되었고, 이제는 내가 그것을 만들어 보겠다는 vision을 가진 startup을 만들게 되었다는 사실이 재미있다. 물론 파펨이 당장 이러한 기술을 만들겠다는 것은 아니고.. (아니.. CAN NOT 이 더 정확한 표현이겠네요 ^^;;)하지만 우리가 가야 할 길은 저 방향이기 때문에 차근차근 만들어가다 보면, 같은 고민을 하고 있는 회사나 친구를 만날 것이고, 또 함께 고민해 나갈 수 있을 것이라는 생각이 든다. (이 글을 보고 또 누군가에게 어떤 도움을 받을 수 있을지 누가 알겠나..) 물론 이 세상 어느 누군가는 이러한 것들을 이미 실행하고 있을지도 모른다. TED에서 이런 동영상을 본 적이 있다. 제목은 The Science of Scent by Luca Turin (2005)2005년에 제작된 것이니, 꽤 오래전 것이고 아마도 그 이후로 엄청난 발전이 있었을 것으로 생각되는데..  동영상 15분을 투자하기 어려운 분을 위해 간략히 설명하면.. 각각의 물질에서 냄새가 다른 이유는 1) 분자의 모양이 달라서.. 혹은 2) 분자의 vibration(진동) 이 다르기 때문이라고 설명하는데.. 발표를 하는 Luca Turin이라는 분은 분자의 진동으로 냄새의 차이를 설명한다. 놀라운 것은, 이 사람의 회사에서 이러한 분자의 진동을 계산하여 세상에 있는 물질의 냄새와 거의 비슷한 것을 직접 만들어 냈다는 것이다. 이 기술이 더 발전하게 된다면, 냄새를 만들어 내는 것이 향을 가지고 있는 물질에서 뽑아내는 것이 아니라, 그냥 분자를 합성하여 만들 수 있다는 것이다. 파펨의 조금 더 long-term  비전도...냄새를 생성해 내는 tool을 만들자.이를 통해.. 향후 VR/AR 영역에서 체험자가 시각/청각 정보뿐만이 아니라 후각을 통해 reality를 높일 수 있는 수단으로 활용될 수 있을 것이다.이를 이해서는 먼저 후각의 객관화 작업이 필요하다.그렇다면 하늘에 달려 있는 비전까지 가지 위해서 파펨은 지금 무엇을 하고 있나? 조금은 다르다고도 할 수 있고.. 아니면 많이 다르다고도 할 수 있다. 파펨은 현재 fragrance commerce business를 하고 있다. 하지만, 동시에 파펨은 이렇게 출시한 향기들을 성분과 image 등으로 표현하는 작업을 하고 있으며, 고객의 취향을 파악하고 향수를 추천해 줄 수 있는 알고리즘, PerfumeTeller 의 version 1을 출시하였다. 비전이 너무 tangible 한 것도 문제일 수 있겠고(금방 달성 가능하니..) 너무 뜬구름을 잡아서도 안 될 것이며, 비전을 향해서.. 꾸준히 가다 보면 닿을 수 있는 것, 그것이 비전이라는 생각이다. 추가로 아직은 파펨이 생각하고 있는 그 정도의 수준은 아니지만... 최근 기사 검색에서 Feelreal (feelreal.com)이라는 회사의 기사를 보았는데, 이 회사도 VR에서 후각을 체험할 수 있는 device를 만들고 있다는 것을 알게 되었다. 이런!! 우리가 먼저 해야 하는데...라는 생각도 1초 정도 들었지만, 이런 실천을 하고 있다는 기업을 만난다는 것 자체가 반가운 일!! ^^;이 회사의 제품도 아직은 pre-order 단계이고, 게다가 아직은 수준이 높지는 않은데, 몇몇 가지 향기 ample이 들어있는 것을 helmet에 장착하고 이와 관련된 화면이 나올 때를 프로그램으로 setting 해두면, 그때 그 향이 나오는 방식이다. 즉, 아직 높은 수준을 구현했다기보다는.. 4DX 극장의 장비 수준을 개인용으로 만든 정도라고나 할까? 이전 구글 동영상 정도의 제품은 언제쯤이나 출시될 수 있을까? 이 회사도 아직은 초-초보적인 단계라고 할 수 있겠다. 하지만, 시각과 청각을 카메라와 마이크에 담아서 다른 곳으로 보내는 기술이 이미 충분히 발전한 만큼, 후각의 영역에서도 이러한 발전이 속속 이루어질 것이라는 기대를 해본다. 물론 우리는 아직 시작한 지 1년도 되지 않았고, 당장은 향기 관련 제품을 만드는 commerce에 집중하고 있다. 그렇다.. 지금 당장 뭣이 중한데?라는 질문을 받는다면.. 먹고사는 것이 중하지요!라는 대답을 할 수밖에 없다. 하지만.. No Rush! 천천히 만들어가고 그 과정을 즐기는 것이 또 중허지 않겠는가? #파펨 #스타트업 #창업가 #창업자 #마인드셋 #인사이트 #비전 #목표
조회수 572

블랭크에 없는 10가지

블랭크는 10가지를 덜어내고 앞으로 나아가는 기업 입니다.아래 내용에 공감하고 뛰어오르고 싶은 사람!언제든 두팔 벌려 환영합니다  VIDEO_BLANK CORP. BI 1. 블랭크는 내가 속한 '팀'이 없습니다.팀이라는 단어에는 배타적인 기운이 있어요. 우리 팀 외에는 배척하려는 강한 힘을 가졌죠. 팀에 소속되면 자기 팀만 챙기게 되는 속성이 있다 보니 블랭크는 큰 하나의 팀만을 가지게 되었습니다. 그것은 'Team blank' 입니다.블랭크는 큰 하나의 팀으로 공동의 목표를 향해 함께 가며, 기존 회사의 팀 개념인 '유닛'은 소속이 아니라 일을 정말 빠르게 하기 위해 만든 단위로 언제든 변화할 수 있는 것이지요. 우리에게 팀은 ‘블랭크 전체’입니다. 2. 블랭크는 ‘룰(변하지 않는)’이 없습니다.‘금속활자’ 기술은 수세기 문명을 이끌었습니다. 그러나, PC의 시대는 30년, 모바일 시대는 10년, 현재 기술은 그 변화주기를 상상할 수 없을만큼 빨라지고 있어요. 우리가 지금 만든 룰이 과연 1년 후에도 적용 가능한 룰일까요? 우리는 구성원 모두가 최대한 동의하는 '상식'을 찾아 나가야 해요. 그 상식은 문맥과 상황에 따라 계속 정의되고 바뀌어야 해요. 우리는 끊임없이 검증할거고 이 방식 그 자체가 우리의 문화가 될 것이라 생각합니다. 3. 블랭크는 ‘직급’과 ‘위계’가 없습니다.직급이 생기면 모든 사람이 가설을 얘기하고 비판하며 토론할 수 없어요. 부장님이 말하는데 "제 생각은 달라요" 라고 말하는 것.. 정말 어려운 일이죠. 대신 우리는 모두 '프로'라는 직급을 가지고 서로를 '님'이라고 불러요. 그런데!! 조심해야할 것이 있어요. 직급이 없지만 그렇다고 형, 동생, 언니같은 허물없는 친화도 조심해야해요. 어떤 동료끼리 친하다고 언니/오빠/형/누나라고 서로를 부른다면 그 외 나머지 사람은 그 관계를 의식하게 됩니다. 이후 논리적인 토론을 하거나 비판하기가 어려울 수 있을 것이고, 또 평가에 대한 공정성이 무너질 수도 있기 때문입니다. 4. 블랭크는 ‘좋게~좋게~’가 없습니다."아 좋게좋게 가자는데 왜케 반대하세요"라는 말! "그냥 대충 갑시다"와 같은 말이죠. 우리는 그런거 없습니다.우리는 그 누구의 논리적 과정을 비판할 수 있어요. 그것은 그 사람에 대한 공격이 아니라 일의 개선을 위한 데이터로 받아들여야 합니다. 비판을 통해 자신의 가설을 깨나가면서 검증하는 것이 블랭크의 방식입니다. 만약 자신의 논리를 비판한 그가 미워졌다면 본인의 자존감에 대해 다시 한번 생각해봐야 합니다.물론 논리를 비판함과 인격을 무시함은 확연히 다릅니다. 5. 블랭크는 ‘시키는 대로 해야 하는 것’이 없습니다.제가 만약 "0000가 잘 될 것 같아요. 한번 가봅시다"라고 해서 무비판적으로 일을 진행했다고 쳐보자고요. 그런데 그것이 실패했다면 우리는 그 실패를 통해 무엇을 배울 수 있을까요? 직관으로, 혹은 리더가 시켜서, 혹은 관행이었으므로 그 일을 했다면 그 것의 성공과 실패로는 배움이 없을거에요. 모든 도전은 다음번의 성공확률을 더 높이는데 일조해야 하므로 모든 도전에는 가설과 논리가 있어야 합니다. 6. 블랭크는 ‘상대평가’가 없습니다.대학교때 상대평가를 하는 과목과 절대평가를 하는 과목의 강의실 분위기가 달랐던 것 기억하시나요?? 예를 들어, 매출과 성공을 기준으로 순위 매기기, 혹은 상대평가를 한다면 조직별, 개인별 이기주의가 생겨 그 누구도 공유를 하려하지 않을 것입니다. 계속 바꾸고 변화해나가야 하지만 현재 우리는 '블랭크 팀'의 평가이고, 진일보한 방식이 될 것임을 자부합니다.  7. 블랭크는 ‘사수제도’가 없습니다.모든 구성원은 독립된 'PRO'에요. 스스로 일을 찾아서 해야 합니다. 처음 들어온 '신입'이 한 명의 '사수'에게 수동적으로 일을 배우고 그 안에서만 사고하게 된다면, 스스로 일을 찾는 능력이 떨어질 뿐만 아니라 '사수'가 누구냐에 따라 좋지 않은 습관을 답습할 수도 있습니다. 8. 블랭크는 ‘연간 KPI’가 없습니다.2번에서 이야기했듯이 세상이 너무나 빠르게 변하는데 1년 간의 나의 목표를 정하고, 1년동안은 변하지 않는 목표를 지향한다는 것이 정말 정답일까요..? 연간 KPI보다는 일주일, 하루, 끊임없이 나만의 목표를 설정하고, 관리해야 하는 숫자를 계속 점검하고 그 시기의 중요도를 생각하며 수정해 나갈 수 있는 KPI를 갖는 것이, 빠르게 변화하는 이 세상과는 더 부합하다고 생각합니다. 9. 블랭크는 ‘정해진 예산’이 없습니다.가장 적정한 마케팅 예산, 상품원가, 제작비 등을 알 수 있는 사람은 누구일까요? 그런 사람 아무도 없어요. 각 영역의 전문가, 실무를 뛰고 있는 당신이 가장 논리적으로 예산을 설정할 수 있어요. 당신은 끊임없이 의심하고 정의해나가며 가장 적정한 비용을 찾아나갈 것이니까요. 당신을 신뢰하니까요. 10. 블랭크는 ‘비밀’이 없습니다.블랭크의 모든 소통은 투명하게 이루어져야 합니다. 당신은 저에게나 그 누구에게나 블랭크에 관한 모든 것을 물어볼 수 있고 들을 수 있습니다. 만약 그 자리에서 공개될 수 없는 '비밀'이 있다면 '이유'가 분명해야 하고 공유할 수 있는 시기를 고지해야 합니다. 이러한 사고의 바탕에는 당신에 대한 신뢰가 존재합니다.지금 확인해보세요. Jason KH커뮤니케이션    기획자
조회수 1021

주니어 개발자가 외칩니다, "Hello, System Architecture!"

Overview주니어 개발자는 시스템 아키텍처(System Architecture) 또는 시스템 디자인(System Design)이라는 단어에 덜컥 겁부터 먹습니다. 지금 진행하고 있는 개발에만 집중하다 보니 큰 그림을 놓치고 있는 게 아닐까 란 생각이 들었죠. 조금 더 큰 그림을 보고자 공부를 시작했습니다. 문득 같은 생각을 하는 주니어 개발자 분들도 많을 것 같다고 생각했어요. 그래서 이번 글은 시스템 아키텍처에 ㅇ_ㅇ? 뀨? 하는 표정을 짓는 주니어 개발자들을 위해 썼습니다.상상의 나래: 가상의 패션 e커머스상상의 나래를 펼쳐봅시다. 패션 e커머스 서비스를 이용하는 김유저 씨가 구매한 옷이 마음에 들어 상품 리뷰를 남기고 싶어한다고요.김유저 씨는 본인의 착용 사진과 텍스트 리뷰를 작성하고 ‘리뷰 등록하기’ 버튼에 엔터를 탁! 누를 겁니다. 그런데 말이죠. 김유저 씨는 요청하고 싶은 웹서버의 IP 주소를 모르기 때문에 요청을 보낼 수가 없습니다.내 정체를 알려줘: DNS (Domain Name System)그래서, DNS(Domain Name System)에게 물어봅니다. 서버의 도메인 이름으로부터 해당 서버의 IP 주소를 알려주는 것이 바로 DNS입니다. 도메인 이름에 대한 질의를 하고, 만일 해당 도메인 이름이 DNS에 ‘A Record’ 형태로 등록이 되어 있다면 도메인 이름에 해당하는 IP 주소를 응답으로 돌려줍니다.서비스에서 자체 DNS 시스템을 가지고 있을 수 있습니다. 예를 들어 Route 53, Cloud Flare같은 서비스가 있습니다. 그렇다면 또 한 가지 의문이 생깁니다. 왜 서비스는 시스템적 부담을 안고서 자체 DNS 서버를 구축하고 있는 걸까요? 그 이유로 두 가지를 꼽을 수 있습니다.첫 번째로는 신뢰도가 높습니다. 직접 DNS Record를 관리 및 운영하기 때문입니다. 두 번째로는 보안이 우수합니다. 만약 공개하고 싶지 않은 IP 주소, 예를 들어 Database IP 주소 같은 건 공개하지 않습니다. 1)작업장소: Web Server이제 웹서버의 IP 주소를 알았으니 통신을 시도합니다. 웹서버는 웹서비스에서 필요로 하는 다양한 요청과 그에 대한 응답을 제공합니다. 클라이언트가 리뷰에 대한 사진과 텍스트를 등록하고 싶다면 웹서버에게 등록하라는 요청을 보내야 합니다.웹서버에서 요청을 받으면 사용자가 요구한 대로 사진과 텍스트를 등록하고, 그에 대한 결과 정보를 응답으로 보내줄 것입니다. 웹서버 내부에서는 그 과정에 필요한 연산을 수행합니다. 서버 개발자는 이 연산에 대한 코드를 작성하고요.센스가 없는 서버:API (Application Programing Interface)서버는 사람이 아닙니다. 센스나 재치가 없죠. 미리 정의되지 않은 요청은 대응하지 못합니다. (어버버버버 퉤! Error 404!) 그래서 약속한 요청을 보내면 약속한 방식으로 응답해줄게라고 명세를 제공합니다.약속한 요청으로 데이터를 보내면 원하는 요청에서 데이터를 정제해 잘 처리했는지, 또는 처리된 데이터를 약속한 방식(예를 들어, JSON 방식)으로 내보내죠. 웹서버는 정의된 API에 맞춰 요청과 응답을 합니다.그런데 웹서버가 수많은 요청을 받고 응답하면 과부하가 일어날 수도 있습니다. 사용자 수가 어마어마한 규모로 늘어나서 서버가 펑! 하고 터진다면, 김유저 씨는 서비스를 더 이상 이용할 수 없을 겁니다. 이용하고 싶지도 않을 겁니다!따라서, 서버가 감당하는 요청을 나누기 위해 같은 역할을 하는 서버 장비 수를 늘릴 수도 있습니다. 그러면 요청이 각기 다른 웹서버 장비에 분산되어 한 번에 감당할 수 있는 요청 수가 더욱 많아집니다.이 구역의 매니저는 나야: Load Balancer그림처럼 서버가 4대 존재하는 상황이라면, 서버 4대에 일을 적절히 분배해주는 역할이 필요합니다. 그것이 로드 밸런서(Load Balancer)입니다. 로드 밸런서가 서버에게 일을 나누는 방법론은 여러 가지가 있습니다.Random: 랜덤으로 분배하기Least loaded: 가장 적은 양의 작업을 처리하고 있는 서버에게 요청을 할당하기Round Robin: 순서를 정하여 돌아가며 작업 분배하기많이 쓰는 로드 밸런서의 종류는 Layer 4, Layer 7을 꼽을 수 있습니다.Layer 4 Load Balancer: 데이터의 내용을 보지 않고 IP주소 및 TCP/UDP 정보에 따라 단순히 분배를 해줍니다.Layer 7 Load Balancer: 서버가 하는 역할이 분리되어 있는 환경에서 데이터의 내용을 보고 각기 맞는 역할을 하는 서버에게 분배를 해줍니다.로드 밸런서는 클라이언트가 요청을 보내야 할 서버를 골라야 하는 부담을 덜어주며, 로드 밸런서에게 할당된 vIP (가상 IP)로 요청을 보내기만 하면 로드 밸런서에서 알아서 작업을 나눠줍니다. 서버에서는 적절한 로드 밸런서를 사용하면 들어오는 요청이 여러 장비에 분산되어 처리량이 늘어나고 응답 시간이 줄어드는 효과를 기대할 수 있습니다. 컨텐츠 저장소: CDN(Content Delivery Network)이제 웹서버가 클라이언트의 요청에 의해 웹페이지에 대한 응답 결과를 돌려줬습니다. 이때 클라이언트의 화면에 렌더링해야 하는 수많은 이미지가 필요합니다. 이 이미지들을 웹서버가 전부 주려면 데이터의 용량이 너무 크고, 무거워서 서버가 헥헥거리죠. (서버가 죽으면 어떻게 될까요? 클라이언트님이 경쟁사로 환승하겠죠.. 안 돼요..) 따라서 웹서버는 직접 이미지를 주는 대신 CDN(Content Delivery Network)에게 요청하라고 이야기합니다. CDN은 일반적으로 용량이 큰 컨텐츠 데이터(이미지, 비디오, 자바스크립트 라이브러리 등)를 빠른 속도로 제공하기 위해 사용자와 가까운 곳에 분산되어 있는 데이터 저장 서버입니다. 클라이언트는 용량이 큰 컨텐츠 데이터를 가까운 CDN에 요청해 멀리 있는 웹서버에서 직접 받는 것보다 빠르게 받을 수 있습니다. CDN이 동작하는 방식에는 크게 Push CDN, Pull CDN이 있습니다. Push CDN: 서버에서 컨텐츠가 업로드되거나, 변경되었을 때 모두 반영하는 방식 Pull CDN: 클라이언트가 요청할 때마다 컨텐츠가 CDN에 새로 저장되는 방식 두 방식 모두 장단점이 있습니다. Push CDN은 모든 컨텐츠를 갖고 있기에 웹서버에 요청할 일이 없지만 유지하는데 필요한 용량과 비용이 많이 필요하겠죠? Pull CDN은 클라이언트가 요청한 컨텐츠가 있으면 바로 응답하지만 그렇지 않을 땐 데이터를 웹서버로부터 가져와야 하기 때문에 서버에 요청하는 부담이 존재합니다. 컨텐츠명은 그대로인데 내용만 변경되었다면 인지하지 못하고 옛버전의 컨텐츠를 제공하죠. 그래서 Pull CDN에 들어가는 컨텐츠는 TTL(Time To Live)이 적용됩니다. TTL이란 유통기한이라고 생각하면 쉽습니다. 일정시간이 지나면 해당 데이터가 삭제되는 것이죠. 이런 방식이 적용된다면 Pull CDN의 최대 단점을 보완할 수 있습니다. 이렇게 보완이 되면 수정된 데이터에 대해서도 대응이 가능하며 서버의 용량 즉, 비용적 부담이 해소될 겁니다.소중한 내 데이터: Database서비스를 제공하다 보면 클라이언트의 소중한 정보, 이력, 상품 가격, 상품 정보 등 다양한 데이터를 저장하고, 또 제공합니다. 하지만 수많은 데이터를 웹서버에 전부 저장하고 사용하기엔 데이터의 양이 너무 많아 저장 공간도 부족하고, 데이터를 원하는 모양에 맞게 정제하기가 어렵습니다. 그래서 데이터를 저장하는 데이터베이스 서버가 따로 존재합니다.민감한 정보를 다루는 데이터베이스는 ACID라는 성질을 만족해야 하는데요.Atomicity(원자성): 데이터베이스에 적용되는 명령이 중간만 실행되지 않고 완전히 성공하거나 완전히 실패해야 한다는 것을 의미합니다. 반만 적용된 명령이 있다면 헷갈리겠죠.Consistency(일관성): 데이터베이스가 수행한 명령이 일관적으로 반영되어 있어야 한다는 의미입니다. 예를 들어 계좌에 돈을 입금했는데 잔고에 반영되지 않는다면 당황스러울 겁니다.Isolation(고립성): 데이터베이스가 수행하는 명령 도중 다른 명령이 끼어들지 못한다는 것을 의미합니다.Durability(지속성): 성공적으로 수행한 명령은 영원히 그 이후 상태로 남아있어야 한다는 걸 의미합니다. 갑자기 하루 뒤에 명령이 취소되거나 이전 상태로 롤백되면 안 됩니다. Replication (복제 / 이중화)큰 시스템에서는 똑같은 데이터베이스가 여럿 존재한다고 하는데요. 그렇다면 왜 비용적인 부담을 안으면서까지 복제 데이터베이스를 구축해놓는 걸까요? 만약에 데이터베이스가 정상적으로 동작하지 않는다면 클라이언트의 데이터를 변경하지 못하며, 클라이언트가 원하는 정보를 제공하지 못하는 불상사가 일어나게 됩니다. 글로만 써도 벌써 땀이 납니다. 그러므로 복제해놓은 데이터베이스를 얼른 마스터로 등업해 데이터 흐름에 차질이 없도록 대비해야 합니다.만약 하나의 데이터베이스가 어떤 일을 수행할 때 다른 요청들은 계속 기다려야 합니다. 그렇다면 데이터를 변경하는 데이터베이스는 하나, 읽기만 하는 데이터베이스는 여러 대가 존재해도 되지 않을까요? 바로 여기서 Master-Slave의 개념이 탄생합니다.master-slave-replicaMaster-Slave Replica (a.k.a 주인-노예)요청을 분산하기 위해서 데이터베이스를 늘리다 보면 master-slave 토픽이 등장합니다.Mater: CRUD(Create, Read, Update, Delete)가 모두 가능Slave: R(Read)만 가능Master가 데이터를 변경할 동안 읽기에 대한 요청은 Slave에게 보내집니다. 그렇게 하면 읽기 요청은 분산되어 훨씬 더 수월하고 빠른 속도로 데이터 처리가 가능할 것입니다. 만약 Master가 변경된다면 아래 계급인 Slave, Replica 데이터베이스에게도 이 정보를 전해야 합니다. 다시 말해, 자신에게 들어온 요청(Query)을 동일하게 보내 빠른 시간 안에 동기화를 시켜주죠. 하지만 동기화도 시간이 걸리는 작업이므로 무한대로 Slave Replica를 늘려 확장하기는 어렵습니다.Master-Master Replica의문이 하나 생길 겁니다. “여러 대의 Master를 두어서 변경도 가능하고, 읽기도 가능하게 하면 되지 않을까?”앞서 언급했듯이 같은 데이터의 변경 가능한 데이터베이스는 하나여야 할 것입니다. 동시에 같은 데이터를 변경했을 때 갈등을 해소하기 위한 방법론은 존재하지만, 그 방식이 복잡하고 오래 걸립니다. 안정성도 낮아지고, 효율도 떨어집니다. 그래서 Master-Slave 아키텍처를 선호하는 것이죠.Sharding그러면 같은 데이터베이스 테이블을 동시에 변경하는 건 불가능한 걸까요? 그것을 해소하기 위해 샤딩(Sharding)이라는 방법론을 사용합니다. 샤딩된 테이블은 개념적으론 하나의 테이블처럼 보이지만 사실 그 내용물이 쪼개져 있습니다. 쪼개는 방법은 여러 가지 선택할 수 있습니다만, 분명한 건 겹치는 데이터 없이 쪼갠다는 것입니다. 그래서 같은 테이블이어도 쪼개져 있다면 그 테이블에 동시에 접근해 데이터를 변경할 수 있는 것이죠.이외에 서비스별, 기능별로 쪼개어 데이터베이스를 관리하는 Federation 등 많은 데이터베이스 디자인 방법론이 존재합니다.시스템 아키텍처가 가지고 있어야 할 최소본 아키텍처요점: 시스템 아키텍쳐에서 고려해야 할 성질이렇게 간단한 시스템 아키텍처의 면면을 살펴봤습니다. 시스템 개발자라면 시스템을 디자인하면서 반드시 고려해야 할 성질들을 만날 텐데요. 위에서 소개한 내용들 역시 아래의 성질들을 충족하기 위해 탄생했다고 볼 수 있습니다.Scalability (확장성): 10만 명의 요청을 처리할 수 있는 시스템과 1000만 명의 요청을 처리할 수 있는 시스템은 다릅니다. 확장성을 고려한 시스템은 앞으로 클라이언트 수가 늘어났을 때 무리 없이 모든 요청을 처리할 수 있을 겁니다.Performance (성능): 속도와 정확성을 말합니다. 요청한 내용을 정확하고 빠르게 돌려주어야 합니다.Latency (응답 시간): 모든 요청은 클라이언트가 불편해하지 않을 정도로 빠른 시간 안에 돌려주어야 합니다.Throughput (처리량): 같은 시간 안에 더욱 많은 요청을 처리한다면 좋은 시스템입니다.Availability (접근성): 사용자가 언제든지 시스템에 요청을 보내서 응답을 받을 수 있어야 합니다. 비록 서버 장비 한두 대가 문제가 생겨 제 기능을 하지 못하더라도 사용자는 그 사실을 몰라야 합니다.Consistency (일관성): 사용자가 서버에 보낸 요청이 올바르게 반영되어야 하고, 일정한 결과를 돌려주어야 합니다. 요청을 보낼 때마다 불규칙한 결과를 돌려준다면 믿을 수 없는 서비스가 될 것입니다.결론발로 그렸나 싶을 정도의 그림과 기나긴 글을 마무리 지으며주니어 개발자로서 시스템 아키텍처를 공부하면서 느낀 점이 있다면 시스템에 대한 완벽한 대응은 없으며, 모두 장단점이 존재한다는 것입니다. (이것을 보통 trade-off라고 표현합니다.)하지만 설계하는 서비스를 잘 알고 서비스에서 무게를 둬야 할 부분을 파악한다면, 그에 맞는 시스템을 설계하고 디자인할 수 있을 겁니다. 김유저 씨도 만족시킬 수 있을 거고요. 꼬박 이틀을 밤새워서 쓴 글이 아직 시스템 아키텍처를 두려워하는 다른 주니어 개발자분들에게 도움이 되었으면 합니다. 이번에는 시스템에서 아주 기초적인 부분을 공부했으니 다음 글에선 MSA(MicroService Architecture)를 씹어봅시다! 겁이 나고 무서워도 외쳐보세요. “Hello, System Architecture!”이 세상 모든 주니어 개발자분들, 퐈잇팅입니다.참고1) 추가적인 이점에 대하여: 웹서버에서 요청을 보낼 때 database 도메인 네임으로 보낼 경우, 멀리 있는 공인 DNS 서버 (예를 들면 google public DNS server: 8.8.8.8)에 물어오는 것보다 자체 DNS 서버에 물어오는 것이 훨씬 더 빠른 속도로 응답을 받아올 수 있습니다.출처GitHub - donnemartin/system-design-primer: Learn how to design large-scale systems. Prep for the system design interview. Includes Anki flashcards.글오연주 사원 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #주니어개발자

기업문화 엿볼 때, 더팀스

로그인

/