스토리 홈

인터뷰

피드

뉴스

조회수 1606

정말.. 싼 게 비지떡인가?

글을 시작하기에 앞서, "싼 게 비지떡"이라는 말의 어원을 좀 집고 넘어가야겠다. 싼 게 비지떡이라는 말은...옛날 충청도 산골에 서울로 과거를 보러 가는 선비들이 들르는 주막집이 있었는데, 그 주막의 주모는 가난한 선비들을 푸짐하게 대접하기로 유명했다고 한다. 하루 묵고 다시 길을 떠나는 선비들에게 주모는 아침마다 보자기에 싼 것을 손에 들려 보냈는데.. 그럴 때마다 선비들이 물었다고 한다. "이 보자기에 싼 것이 무엇이요?"'싼 게 비지떡'입니다. 가다가 출출할 때 드세요..사진은 마포 을밀대의 녹두전 ㅎㅎ 비지떡은 아님!!보자기에 싼 그 무엇이 바로 콩 비지떡이라고 한다. 즉 싼 게 비지떡이라는 말은 넉넉한 인심을 표현하는 말이었는데 의미가 와전되었다고 한다. 90년대를 강타한 '덩달이 시리즈' 에나 나올법한 이야기(#연식인증)지만, 정설이라고 하니... ㅎㅎ우선 이 글을 쓰고 있는 나도 제품의 일반적 가격대비 싼 것은 잘 소비하지 않는다. 지금까지 40년 가까이 살아오면서 "싼 게 비지떡"이라는 것을 체험해왔다. 이윤을 극대화시키려는 인간의 본성을 생각한다면, 싸게 판다는 것은 본성을 거스르는 것이고, 그런데도 불구하고 싸게 판다는 것은 그 나름의 이유가 있다는 의심을 가진 터라, 그것이 좋게 보이지 않는 경우가 많았다.간단하게 생각하면,이윤(margin) = 가격(price) - 비용(cost)이 되는 것이고, 그렇다면 이윤을 극대화하기 위해서는 가격을 높이던가, 비용을 낮추던가.. 혹은 그 둘다를 하던가 또는 판매되는 unit의 개수를 늘려(박리다매) 이윤의 총 합을 극대화할 수 도 있겠다. 그런 차원에서 가장 쉬운 방법은 가격을 높이는 것이다. 낮은 가격을 유지하면서도 비용을 낮추고 이윤을 극대화하는 것은 실로 엄청난 노력이 따르기 때문! 잠시 이야기의 초점을 가격(price)에 맞춰본다면, 가격은 어떻게 형성되는가?1) 제조업자 (혹은 브랜드) 입장에서는 제조원가에 원하는 수준의 마진을 더하여 가격을 도출할 수 있고..2) 고객의 입장에서는 내가 지불하고자 하는 의지 (wiling to pay) 일 수 있고..3) 시장 입장에서는 유사한 다른 제품들이 판매되고 있는 가격이 기준이 되어 가격을 도출할 수 있다...이러한 조건들이 만족되는 지점의 근처에서 가격이 형성될 것이라는 생각이다. 그런데 요즘 향수, 디퓨져 등등의 시장 조사를 하다 보면, 좀 어처구니없는 가격표들을 붙여놓은 것들을 너무나도 많이 발견하게 된다. 뭔가 시장과 player 간의 암묵적 담합인 듯한데.. 1), 3) 번의 기준에서 살펴보면.. 거품이 너무 많다는 생각을 지울 수 없다. 그렇다면 2)의 기준에서 고객이 이 상품/서비스를 구매 안 하면 그만인데.. 그걸 또 사는 고객들이 있다는 것이다. OTL.... 물론 가격은 서비스와 제품을 판매하는 사람의 결정이고, 의사결정이며 전략이 될 수 있다.  고객의 입장에서는 제품/서비스에 저 정도 가치를 느낀다면 구매를 할 것이고.. 아무튼! 1차적인 결론은 대부분의 상품/서비스 중에 싼게 비지떡인 것은 사실이다. 그럼 파펨의 가격은 왜 만원인가?파펨의 가격은 특별한 계산 없이 "매월 만원이라면 subscription 하는데 부담이 없지 않을까?"라는 생각에서 출발하였고, 고객이 지불하는데 부담이 없을 것이라고 생각하였다. 10,000원이라는 가격 조건을 정해 놓은 후, 1)의 조건, 즉 cost를 조정하기 시작했다. 그러고 나서 남는 margin은 흠.. 꽤나 tight 하지만 도전해볼 만한 수준이라는 생각이었다. 그렇게 해서 파펨은 정기구독 시, 월 만원이라는 가격이 되었다. (곧 월 12,000원이 된다 ㅎㅎ)  문제는 파펨이 "만원"이라는 가격 표시를 붙여 놓으니.. quality가 비지떡일 것 같다는 #선입견 이 생긴다는 것이다. 이 부분에서 좌절이다. (우선 이러한 이야기가 나오면 조금 흥분하는데.. ) 적정한 가치를 제공하지 못하면서 높은 가격 요구하는 브랜드가 있는데 그것은 팔리고 있다는 점이 화가 나고, 오히려 가격이 높은 것이 좋을 것이라는 단순 생각으로 구매하는 고객들에게도 화가 난다. ^^;; 화가 나봐야 어쩔 수 없는 것은 나 밖에 없다는 것을 알면서도.. ㅎㅎ또한 한가지 특이한 사실은... 고객은 저렴한 가격을 붙여놓으면 저렴한 가격의 이유를 찾기 시작하는 경향을 보이는데, 이케아가 저렴하다는 가격표를 보고는 "이케아는 2년 정도 쓰면 제대로 못쓴데.. 그러니까 싼 거야"라는 판단을 해야 본인의 정신적인 스트레스가 감소할 수 있다. 즉 요즘 일부 고객들은 비싼 가격에 명품 행세하고픈 제품에 돈을 지불한다는 것이다.그럼 명품이란 무엇일까? 내 기준에서 명품은...고가의 제품이 아니라, 제품에 대한 명확한 "철학"(philosophy)을 가지고, "최선의 노력"을 다해 "꾸준하게" 제품과 서비스를 "upgrade"하는 노력을 기울이는 회사의 제품이라는 생각이다. 가격의 여부가 명품이 아니라, 그 value를 키워가는 노력과 정성이 살아있다고 느껴지는 제품들이 명품이다. 그런데 요즘 제품들은 위와 같은 생각과 노력보다는.. 이미 명품이 된 것들을 흉내 내려고 하는 것에서 그치고 있다는 느낌이 강하다. 그런 것들을 보면 또 화가 난다. ㅡㅡ;;;파펨은 명품이 되고 싶다. 추운 겨울날에도 방산시장에 가서 혹은 을지로, 청계천 일대를 돌아다니면서 우리가 제품의 quality 증대를 위해서 할 수 있는 것들에 대해서 search 하고, 어떻게 하면 좀 더 가치 있는 제품이 될 수 있을까를 고민한다. 그러한 제품을 고객에게 합리적인 가격에 제공하고 싶다는 생각이다.그럼 싼 것과 합리적인 가격은 어떤 차이가 있는가? 싼 것은 낮은 가격으로"만" 고객을 잡겠다는 목적이다. 합리적인 가격은 value for money이다. 고객이 이 정도의 가격이라면 본인이 가진 것과 교환하는데 충분히 만족을 느낀다는 것이다.자, 그럼 싼 게 비지떡인가? 싸다고 모든 것들이 비지떡이라는 생각은 하지 않았으면 좋겠다. 물론 그럴 가능성이 높겠지만, 합리적인 가격인데 저렴하기까지 하다면 더없이 좋지 않을까? 그런 제품/서비스를 만들겠다는 스타트업들이 많아질수록 더 합리적인 세상이 되는 것이 아닐까? 그리고 이러한 것들을 고객들이 알아줄 수 있으면 좋겠다. ㅎㅎ 물론 마케팅 측면에서 이런 message를 잘 전달해야 알 수 있겠지만..마지막으로.. 억울하면 가격 올리면 되는 거 아니겠는가? 그건 안 하련다...파펨이기 때문에!!!#파펨 #스타트업 #창업가 #창업자 #마인드셋 #인사이트
조회수 1135

개나 고양이만 불쌍하고 소는 안불쌍하니?

※ 조금 격양된 표현이 있을 수 있습니다.오늘 말복이다. 또 얼마나 많은 불쌍한 강아지들이 냄비속으로 끌려들어갈지 생각만 하면 화가 치밀어오른다. 얼마전 해프닝으로 이슈를 끈 기보배 어머니 사건은 결국 기보배 아버지가 정말로 보신탕집을 운영했던 이력이 드러났다. 동물보호협회에서 이번에는 야심차게 버스광고도 하고 있고, 전반적으로 개고기 반대 운동이 옹호되는 분위기가 형성되는 듯 해서 기분이 좋지만, 여전히 개고기 반대 운동마다 늘상 지겹게 달리는 다음과 같은 댓글들이 있어서 좀 조목조목 반박해 보고자 한다.1. 개나 고양이만 불쌍하고 소나 닭은 안불쌍해?이런 댓글은 진짜 나는 정말 무식한 사람입니다라고 광고하는것과 마찬가지이다. 개나 고양이가 굳이 왜 '반려동물'이라고 불리겠는가? 과반수 이상의 인간들에게 가축, 단순한 동물의 존재를 넘어서서 가족의 일원으로 함께 살아가는 존재로 변모해온 동물이 개와 고양이이기 때문에 붙은거다. 혹시 '근거 있니?' 이런 소리 할 안티들을 위해 간단한 통계를 보도록 하자 (출처: ASPCA - Pet Statistics, APPA, GfK SE survey 2013)미국에서 개체수로 치면 반려동물은 애들보다 많다.미국에서 반려동물을 키우는 사람들의 90% 이상이 반려동물을 가족과 동등한 일원으로 여긴다.미국에서는 반 이상의 개들이 가족과 함께 침대에서 잔다.전 세계적으로 57%의 가정에 반려동물이 있으며 이 중 개는 33%, 고양이가 23%를 차지한다.놀랍게도 반려동물 보유가정이 가장 높은 나라는 미국이 아닌 아르젠티나 (82%), 멕시코 (81%), 브라질 (76%) 순이다. (개체수가 아닌 전체 모수 중 반려동물 보유 가정의 비율이다.)Top 20 Dogs and Cats Populations WorldwidePet Ownership Internationally물론 누군가에게는 소가 반려동물일 수도 있고, 닭이 반려동물일 수도 있다. 그런데 왜 소고기 반대운동, 닭고기 반대운동은 별로 보이지 않는걸까? 안타깝지만, 그들을 반려동물로 인식하는 인구의 숫자가 유의미하게 많지 않아서 그런것 뿐이다. 만일 먼 미래에 진짜 갑자기 소라는 동물이 두집 건너 한집 꼴로 애완견처럼 같이 지내고 있다면, 당연히 소고기 반대 운동도 일어나겠지. 다시 말해서, 소나 닭은 놔두고 굳이 개고기 반대 운동을 벌이는 이유는 개라는 존재를 가족의 일원처럼 여기는 사람들의 인구가 월등하게 많은 것 뿐이다. 누가 당신 가족의 일원을 잡아서 거꾸로 나무에 묶은 다음에 죽을때까지 두둘겨 팬 후에 토막토막내서 냄비에 끓여서 팔고있다면 당신은 심정이 어떨것 같나?2. 저렇게 개고기 반대운동 하는 사람들 치맥은 열심히 먹더라.위와 같은 맥락인데, 개고기 반대운동과 채식주의 운동은 엄연히 그 성격이 다른 운동이다. 물론 채식주의의 범주가 더 크긴 하지만, 동물보호를 이유로 한 채식주의자들은 인간과 동물의 공존이라는 주제로 운동을 벌이는거고 개고기 반대운동은 1번에서 설명한 내 가족같은 존재를 누군가가 토막토막내서 사고팔고 있다는게 참을 수 없어서 벌어지는거라는 점에서 차이가 있다. 따라서, 개고기 반대운동한다고 치맥 못먹을거 없고, 반려견 보호소에서 일한다고 치맥 못먹을 이유 없다는 거다. 개고기와 채식은 전혀 별개의 주제이다.참고로, 전 세계의 채식주의자 인구 비율은 5%가 채 안된다 (물론 종교적으로 강제하는 나라 제외하고 동물보호를 목적으로 채식을 하는 인구를 말함). 미국이 2015년 기준 1.9%-3.4%, 호주가 2010년 기준 2%, 독일이 다소 높은 6%-8.7% 수준이다. (출처: Vegetarianism by Country, Wikipedia) 이렇듯이 개고기 반대와 베지테리아니즘은 엄연히 다른 범주, 다른 영역의 주제를 가지고 있는데 너는 왜 개고기 반대하면서 치맥을 열심히 먹고 있느냐는 무식한 얘기는 제발 하지 말자. 사실 한가지 더 덧붙이면 많은 수의 채식주의자들이 반려동물을 보호하는것에서 부터 시작해서 채식주의까지 발전하는 경우가 많으니 서로 완전 별개의 사람들은 아니다. 내 주장의 요지는 '개고기 반대운동 = 채식주의자' 요런 등식이 잘못됐다는 것일 뿐이다.Worldwide Vegetarian Population3. 개고기는 우리 민족 고유 문화다.아 이거 내가 역사학자도 아니고 그쪽으로 지식이 미천한지라 역사적으로 옳다 그르다라고 말하기는 불가능하다만, 이거 하나는 확실히 말할 수 있다. '문화'라는건 항상 역동적이고 융합되어 변화하고 움직이는 거다. '고유'의 문화라는건 한 문화의 뿌리가 어디에 닿아있느냐는 거고, 그 문화를 보존하냐 마느냐의 문제이지, 그게 우리 고유의 문화라고해서 모두가 고수해야 한다는 건 진짜 유치원 5살짜리 논리만도 못한 생각이다. 그런 논리면 한복도 우리 고유 문화니까 다 닥치고 한복입고 다녀야 하고 상투틀고 머리 안자르던것도 우리 고유 문화니까 남자들 이발하면 안되는건가? 핵심은 이거다.그 문화가 보존할 가치가 있다고 사회적 합의가 있을 경우 그 문화는 보존되고 지켜나가지는 것이라는거.한복은 저걸 보존하자는 사회적 합의가 명확하지? 그러니까 저걸 계속 보존하려고 온 나라가 노력하는거고, 당신 말처럼 개고기가 만일 우리 민족 고유 문화라고 치자. 그래서 이걸 보존해 나갈 가치가 있나? 그런 사회적 합의가 있을까? 이건 지나가는사람 붙잡고 설문조사 몇번만 해도 금방 결과 나오는거고, 심지어 본인이 개고기 먹는 사람조차도 지가 먹는거 숨기고 있는 사회다. 따라서 개고기가 우리 고유 문화니까 공격하지 말라는 논리 역시 말도 안되는 얘기다.4. 개고기 반대는 서양 사대주의다이건 사실 상대할 가치가 없는 논리이긴 한데, 생각보다 이런 댓글이 많이 달리더라... 개고기 먹는걸 야만인처럼 여기는 문화는 서양에서 들어온거고, 이를 맹목적으로 따라가는건 서양 사대주의일 뿐이다... 라는 주장이다. 이들이 얘기하는 사대주의는 정확하게 말하면 '문화 사대주의 (Cultural Toadyism)'을 의미하는데, 그 정의는 단순이 다른 나라/민족의 문화적 양식을 따라하거나 좋아하는걸 의미하는게 아니다. 그런식으로 따지면 동남아에서 한류바람으로 한국 드라마 닥치는대로 정주행하고 남친한테 한국식 데이트를 요구하는 동남아 여자들은 다 문화 사대주의에 빠진 사람들인가?문화 사대주의는 자국보다 강한 국가, 또는 자민족 보다 우세한 민족의 문화에 복종하고나 맹목적으로 받아들이려는 주의를 말한다. 조선시대에 우리 사대부들이 중국은 우리 형님나라요, 우리나라의 본원이 중국의 한 갈래에 있으니 중국의 문화는 우리보다 무조건 우월하니 닥치고 받아들이시오... 하던게 바로 문화 사대주의의 전형이다. 개고기 반대가 이거랑 어떻게 같나? '개고기 반대하는 사람들이 서양문화가 무조건 우리보다 우월하니까 개고기먹는 사람은 야만족 취급하는 서양애들 말이 100% 맞는거야...' 이런 이유로 개고기 반대하는 사람이 세상에 얼마나 존재한다고 생각하나?말복을 맞아 조금 격양된 논조로 개고기 반대운동 안티들을 향해 조금 쓴소리를 해봤다. 예전에 유투부에서 한창 돌아다니던 동물사랑실천협회 대표와 진중권 교수가 개고기 관련 주제로 토론하던 영상도 봤는데, 사람들 반응이 가관이더라. 진중권은 무슨 논리로 무장된 개념넘치는 영웅이고 박소연씨는 무슨 무개념에 논리가 1도 없는 사람처럼 취급하더라. (심지어 나무위키에도 그런식으로 소개하고 있다.) 이 동영상은 그냥 박소연씨가 진중권 특유의 토론 방식에 (상대로 하여금 자승자박에 걸리게 하는 질문을 던지는 아주 고난이도 스킬) 말려들어서 그냥 망해버린 토론일 뿐이지 그렇다고 진중권교수가 '개고기 반대론자들의 논리가 빈약하다..' 뭐 이렇게 말할 꺼리는 절대로 아니다. 반려동물은 인간이 동물에게 '나는 너를 반려동물로 선택했소..' 뭐 이런 인간우월적인 개념에서 나온게 아니다 (사실 여기서 출발 했을수도 있지만...) 뭔가 역사적으로, 인류학적으로 무슨 엄청난 배경이 있어서 생긴 문화도 아니다. 그저 개와 고양이라는 동물이 인간과 자연스럽게 생활하다가 생긴 유대감에서 발전되어나가 그들이 아예 가족의 한 구성원으로 여기게 된 사람들의 인구가 유의미하게 많아져서 하나의 문화가 되버린 현상일 뿐이다. 개고기 반대의 이유? 내가 자식새끼처럼 여기는 개, 고양이를 누군가가 두들겨패서 먹어버리는 행태에 대해 그냥 화가 치밀어 오르는거, 그 이상 그 이하의 의미부여도 필요없다.https://www.youtube.com/watch?v=lC5g0o_bLPU<iframe width="700.000000" height="394.000000" src="//www.youtube.com/embed/lC5g0o_bLPU" frameborder="0" allowfullscreen="">이 동영상에서 진중권 진짜 비호감이다. 말빨 하나로 본인이 우월한 사람인양, 상대방을 그냥 우기는 사람 취급해 버린다..아, 참고로 나도 채식주의자다. (채식주의자가 되다 글 참고)한가지만 더 추가하려고 한다. 생각보다 이런 댓글도 드문드문 보이더라.5. 나도 개키우는 사람인데 개고기 반대 운동은 다양성을 인정하지 않는 사고방식 아닌가요? 내가 개키운다고 다른사람도 나 처럼 생각해 달라고 하면 안되는거죠.이건 사실 어느정도 일리는 있는 주장이긴 하다. 내가 일리가 있다고 말하는건 '나도 개키우는 사람인데'가 아니라 '다른사람도 나처럼 생각해 달라고 하면 안된다'에 일리가 있다는거다. 인간은 항상 본인의 사고방식에 위배되는 행동을 강요받으면 본능적으로 위기감을 느끼고 스트레스를 받기 때문에 자기방어적이 되기 쉽다. 이런 차원에서 남에게 내 사고를 강요하는건 어쨌든 기본적으로는 상대방에게 스트레스를 유발시킨다.하지만, 그렇다고해서 내 생각을 남에게 강요하거나 주장하는 모든 행위가 불가능해 진다면, 이 세상에 환경보호 운동이니, 페미니즘 운동이니, 성 소수자 운동이니 등등의 모든 '운동'자 붙인 행위가 모두 불가능해 진다. '운동' 영어로는 'movement'라는건 어쨌든 어떤 특정 가치를 믿는 일부 그룹의 사람들이 그 가치가 인류공생에 가치가 있다는 신념하에 다른사람에게 전파시키고자 하는 행위이기 때문에 언제나 그들과 생각이 다른 사람들에게 강요행위를 하게 된다. 이때 그 운동이 올바르냐 옳지 못하느냐의 평가는 사실 매우 복잡한거고, 대부분은 그 시대에서 평가받지 못하고 다음, 또는 먼 세대에서 평가받곤 한다. 하지만 사회적으로 다수의 지지자를 확보하면 그 운동은 대부분 정당성을 확보하기 마련이다. 환경보호 운동은 절대 다수의 지지를, 페미니즘운동도 유의미한 다수자의 지지를 얻기 때문에 사회적으로 정당성을 인정받고 있는거고, 성 소수자 운동은 아직 다수의 지지가 확보되지 않은 상황인지라 이렇게 힘들게 운동하고 있는것 뿐이다.이런 차원에서 '이미 사회에서 많은 사람들의 가족 구성원으로 존재하고 있는 개나 고양이를 식용으로 먹는 행위는 사회 다수의 사람들에게 극도의 불쾌감과 스트레스를 유발시키기 때문에 근절되어야 하는 행동이야'라고 개고기 반대 운동을 펼치는게 나는 합당한 사회적 운동이라고 보는거다.아, 한가지 더 덧붙이자면, '나도 개키우는데..'라고 붙이는 사람들은 당신이 애완동물을 악세사리처럼 달고 살고 있는건지, 아니면 진정 반려동물로서 내 가족의 일원으로 여기고 함께 살아가고 있는건지 다시한번 진지하게 고민해보고 그런 말 하길 바란다. 당신이 후자의 사람이라면 '나도 개 키우는데 개고기 반대하지 않아요'라는 말은 목에 칼을 들이대도 하지 못할껄? 왜냐면 우리들에게 그런 말은 '나도 집에 애들이 있는데요 어디 원주민들이 애를 식용으로 잡아먹는 행위는 반대하지 않아요'라고 말하는 것과 똑같기 때문이다.
조회수 1046

[Buzzvil Career] 좋은 디자이너는 어떤 사람일까요?

 버즈빌은 어떠한 인재를 찾는지 지원자에게 잘 알리려고 노력합니다. 그럼 지원자도 버즈빌이 자신에게 맞는 기업인지 알 수 있을 테니까요.  Buzzvil Career에서는 각 직무에 대해 더욱 심도 있는 정보를 제공합니다. 현재 채용 중인 디자이너 포지션을 여기에서 확인 해주세요. 이번 글은 지난 13년동안 디자인에 매진한 Max 에 대한 직무 인터뷰입니다. (그에 대한 자세한 내용은 LinkedIn 에서도 확인할 수 있습니다!) 그는 여전히 이 분야에서의 일을 즐기고 있고, 지난 4년 동안 수석 디자이너로 버즈빌과 함께 했습니다. 그가 생각하는 좋은 디자이너란 누구인지 함께 이야기해 보았는데요. 버즈빌과 함께 성장하고 싶은 디자이너라면 이 글에 주목해 주세요. 버즈빌에서 하루 일과를 설명해주세요. 저는 매일 일과를 차분하게 계획하기 위해 평화로운 오전 9시 정도에 출근해 하루를 시작합니다. 그러고 나서 Young, James, Lucas를 포함한 몇 명의 버즈빌리언과 9:30분에 명상을 즐깁니다. 오전 10시에는 팀원들과 daily scrum을 한 뒤 커피를 마시는 시간을 가져요. 이렇게 순조로운 업무를 위해 소통하며 하루를 시작합니다. 저는 오전에 생산성이 가장 높기 때문에, 어려운 작업은 이 시간에 모두 하려고 노력합니다. 에너지 반만 쓰면서 오후 내내 하는 것보다 오전 한 시간에 온전히 집중하면 더 많은 일을 할 수 있는 것 같아요. 그 후 최근 인기가 좋은 송리단길에서 새로운 맛집을 찾아 점심을 먹거나 회사에서 제공하는 아침 식사를 브런치처럼 간단하게 먹습니다. 오후는 보통 팀원들이나 다른 버즈빌리언들과 소통하고 팔로우업 하는 시간입니다. 하루의 마지막 시간들은 문서화 작업, 리서치,  벤치 마킹하며 시간을 보냅니다. 왜 버즈빌을 선택했나요? 버즈빌은 디자이너들에게 특별한 환경을 제공합니다. 회사는 창의성을 이해하고 각 팀의 분야를 존중하기 때문이죠. 각 팀은 팀원들이 직접 일을 어떻게 관리할지를 자유롭게 결정할 수 있습니다. 이것은 디자이너들이 일하는 방법을 찾고 끊임없이 개선하는데 적합한 환경이죠. 이러한 기업 문화는 보기 드물거든요. 버즈빌은 어떤 곳인가요? 버즈빌은 모두에게 자기 주도적일 것을 권합니다. 스스로 도전할 준비가 됐고 이를 위한 최상의 환경을 원하는 디자이너들에게 아주 적절합니다. 버즈빌의 디자이너는 한 가지 일이 아닌 많은 것을 하게 될거에요. 그리고 한가지 특정 분야에 매여 있지 않은 대신에 여러 가지 프로젝트들을 맡으며 역량의 범위를 무한대로 넓힐 수 있습니다. 아이디어가 있을 때, 그것을 실행에 옮기는 데 시간을 낭비하지 않도록 디자인 팀은 소통과 새로운 아이디어에 늘 열려 있어야 합니다. 버즈빌는 조화를 가장 중요시하는데요. 많은 IT 회사들은 종종 좋은 환경에 낮은 임금 또는 반대로 나쁜 업무 환경에 높은 임금 둘 중 하나만을 제공하잖아요. 그러나 여기 이곳은 훌륭한 기업 문화를 제공하면서 많은 직원 혜택과 결코 낮지 않은 임금을 제시합니다. 팀 분위기는 어떤가요? 버즈빌의 디자인 팀은 고효율을 매우 중요시합니다. 우리는 내부적으로 회사의 브랜드, 마케팅 그리고 자체 잠금화면 앱 서비스 허니스크린, 슬라이드조이 및  50개 이상의 파트너사의 잠금화면 앱들을 포함한 제품들에 관한 폭넓은 이슈를 다룹니다. 우리는 문제를 제기하고 이것을 빠르게 처리합니다. 그리고 한 사람이 똑같은 일을 두 번 할 필요가 없도록 현명하게 일할 줄 아는 디자이너들을 찾고 있습니다. 나를 포함한 여기 디자이너들은 기술적인 백그라운드를 가지고 있어서 문제를 더욱 쉽게 다루고 있습니다. 우리는 결국 IT 스타트업이기 때문에 필수는 아니지만, 프로그래밍 경험이 있는 지원자를 선호합니다. 우리는 제품이 어떻게 작동하고 어떻게 사람들의 삶을 변화시킬 수 있는지를 이해할 수 있어야 하기 때문이죠. 좋은 디자이너는 어떤 사람일까요? 호기심이 많은 사람: 좋은 디자이너는 평생 공부하는 사람입니다. 좋은 디자이너는 모든 디테일에 주의를 기울여야 하고 비지니스 측면을 포함하여 무엇이 중요한지를 이해해야 합니다. 디자인은 끊임없이 변화하는 분야이기 때문에 디자이너들은 항상 최신 트렌드나 이슈를 잘 알고 있어야 합니다. 행동하는 사람: 디자인은 아이디어를 실질적으로 구체화할 수 있는 능력을 가지는 분야입니다. 디자이너는 초기 단계일지라도 새로운 것을 시도하는데 절대로 주저해서는 안 됩니다. 리서치는 중요하고 과정의 일부이지만 아이디어를 실행으로 옮겨야 실력 있는 디자이너로 성장할 수 있지 않을까요. 잘 소통하는 사람: 디자이너들은 아이디어들을 전달하기 위해 시각 언어를 만들어내고 사용합니다. 공유하고, 가르쳐주고, 알려주고, 토론하고 더 나은 디자인을 위해 다른 사람들의 피드백에 근거하여 다시 디자인 작업을 반복하는 것이 디자이너들의 역할이죠. 그리고 한 사람이 훌륭한 디자이너가 될 수 있다고 한들 그가 아이디어를 나누는 방법을 모르면 그게 무슨 의미가 있을까요? 좋은 디자이너는 확실한 방법론을 가진 사람인데요. 방법은 모든 프로젝트의 기초가 됩니다. 자신만의 방법을 가진 디자이너는 다양한 프로젝트에 적응할 수 있습니다. 최신 디자인과 관련된 트렌드를 잘 알기 위해 Slack같은 툴을 이용하여 소규모의 온라인 커뮤니티를 시작하거나 가입하는 건 어떨까요. 기사를 읽고, 팟캐스트를 듣고, 관련 영상과 그것들 밑에 추천되는 콘텐츠를 접해도 되겠네요. 그런 콘텐츠를 통해 다른 디자이너들은 어떻게 문제를 해결하는지에 대한 좋은 아이디어를 얻는 게 중요한 것 같아요. 버즈빌에서 우리 디자인 팀은 더 커질 필요가 있고 우리는 함께할 최고의 팀플레이어를 찾고 있습니다. 여기에서 멋진 경험과 보람찬 모험을 함께 했으면 좋겠습니다. 현재 어떤 포지션을 채용 중인가요? (2018년 10월 기준) 현재 자율적으로 웹과 모바일 관련 프로젝트를 노련하게 담당해줄 Product Designer를 찾고 있습니다. 기술적이고 사업적인 분석까지 가능하여 상항에 맞게 잘 적응할 수 있는 분이였으면 좋겠습니다. 무엇보다 팀워크가 중요하다는 것을 잘 이해하고 팀원들과 함께 공유하고 배우고 참여하는 디자이너라면 지원해주세요. 개인적으로 그리고 팀원으로 함께 성장했으면 좋겠습니다.   *버즈빌의 채용공고(전문연구요원 포함)를 확인하고 싶으면 아래 버튼을 눌러주세요!
조회수 1744

1 Personality Trait Steve Jobs Always Looked for When Hiring for Apple

애플의 초창기 시절부터, 스티브 잡스는 회사를 성공적으로 만들고 싶어했다. 한 때 애플은 회사 외부로부터 “전문적인” 경영자들을 데려왔었지만, 잡스는 곧바로 그들을 해고해 버렸다.“그들의 방식은 전혀 먹히지 않았습니다.” 유튜브에서 볼 수 있는 젊은 시절의 스티브 잡스가 한 말이다. “그들 중 대부분은 멍청이에요. 관리만 할 줄 알지 다른 건 아무것도 할 줄 모르죠.”잡스의 경영 방식을 다룬 이 비디오는 최근 Quartz at Work라는 웹사이트에 의해 재조명되었다. 비록 이 영상의 잡스는 아직 그의 상징인 검은 터틀넥을 입기 전이지만, 애플 창립자로써 그의 인사이트는 세월이 지났음에도 가치가 있는 것이다.전문 경영인들을 쓰지 않기로 선언한 이후, 잡스는 그간 경영자들에게 요구되지 않던 색다른 자질을 가진 사람을 찾기 시작했다. 그 자질은 바로 열정이었다.“우리가 바라는 사람들은 자신의 분야에서 미친듯이 뛰어난 사람입니다. 꼭 그들이 경험 많은 전문가들일 필요는 없죠. 다만 자기 분야에 대해 열정이 있고 최신기술에 대한 이해력, 그리고 그 기술들로 뭘 할 수 있을지를 알고만 있으면 되는 겁니다.”잡스는 화려한 이력서나 경력 같은 것들을 신경 쓰지 않았다. 열정 있고 문제를 해결할 능력만 있으면 됐던 것이었다. 전문 경영인을 해고한 그 자리에, 잡스는 경영과는 관계 없는 부서에서 일하던 Debi Coleman을 그 자리에 앉혔다. 그녀는 영문학을 전공한 32살의 경험 없는 직원이었다(우연히도, 영문학은 마크 큐번이 예상한 앞으로 가장 가치 있을 전공 중에 하나이다). 그리고 이러한 채용 방식은 통했다. 애플의 제조 담당자로 일하고 난 다음, Coleman은 불과 35살의 나이에 애플의 CFO가 되었다.잡스가 계속해서 설명하길, 뛰어난 직원일수록 ‘관리’ 해 줄 필요가 없다고 한다. 만약 그들이 열정 넘치고, 똑똑하고 동기가 충분하다면, 그들은 스스로를 ‘관리’ 할 수 있다는 것이다. 물론 그러기 위해선 직원들이 회사의 비전에 대해 완벽하게 이해하는 것이 필요하다.그때야말로, “관리” 라는 것이 역할을 할 때인 것이다. 직원들에게 일일이 할 일을 지시하는 대신에, 모두에게 비전을 명확하게 보여줘서 그들이 같은 목표를 향해 일할 수 있도록 만드는 것이야말로 진정한 리더십이라고 잡스는 믿고 있다.비디오의 후반부를 보면, 초기 애플의 직원들이 신입사원 면접을 맡아볼 때 지원자들의 열정을 확인하는 방법이 나온다. Andy Hertzfeld는, 애플 초창기부터 함께해 온 소프트웨어 엔지니어인데, 그가 말하길 그들 면접팀은 일부러 지원자들에게 매킨토시 프로토타입을 보여 준다고 한다. 그리고 그들이 어떻게 반응하는 지를 지켜본다는 것이다. 만약 지원자가 별다른 반응이 없다면, 그는 아마 채용될 가능성이 적을 것이다.“우리는 그들의 눈이 반짝반짝 빛나며 흥분하는 모습을 보고 싶은 거에요. 그제서야 ‘이 사람도 우리랑 같구나’ 라는 것을 알게 되니까요.”원문 : https://www.inc.com/betsy-mikel/to-hire-all-star-employees-steve-jobs-looked-for-1-non-negotiable-quality.html#더팀스 #THETEAMS #애플 #스티브잡스 #인사이트
조회수 1245

2년 차 스타트업의 평가

매년 12월은 평가의 달이다.내게 평가라는 것은 항상 불안하면서도 불만족스러운 무언가였다. 전 회사에서도 평가지를 받으면 늘 머리를 싸맸던 기억이 난다. 나 자신에 대한 평가도 마찬가지이고 특히 팀원에 대한 평가는 더욱 어려웠다. 이 사람이 잘하고, 이 사람은 열심히 하는 것 같아 라는 내 마음속의 구분은 있었지만 그것을 평가의 형태로 풀어내는 것은 쉽지 않았다. 숫자로 평가하는 것도 쉽지 않았고 그 사람에 대한 피드백을 전달하는 것에도 용기가 필요했다. 결국 그 용기가 부족해서 마음속에 있던 이야기를 정확히 하지는 못했고, 나도 동료로부터 필요한 이야기를 듣지 못했다.30여 명의 직원을 가진 에잇퍼센트에도 12월이 다가왔다. 일 년의 끝에 다다르자 구성원들에게 피드백을 주어야겠다는 생각을 했다. 그리고 나 또한 그들에게 피드백을 받고 싶었다. (구성원에게 피드백 주기는 나의 1년 회고에 적혀 있는 목표 중 하나이기도 하다.) 하지만 그 형식을 고민하다 보니 결국 평가라는 이름이 붙게 되었고, 평가라는 것에 대해 고민해보게 되었다.평가는 왜 하는가?평가의 목적은 개인의 방향과 회사의 방향을 일치시켜서 회사가 나가고자 하는 힘을 강하게 하는 것이다.평가의 목적(1) 회사의 방향과 다른 방향으로 나가고 있는 A에게 회사의 방향을 알려 준다.(2) 이미 회사의 방향과 거의 같은 곳을 바라보고 있는 B에게 격려를 통해 그 힘을 더 강하게 한다.(3) 회사의 방향에 도움이 되지 않는 C의 영향력을 줄인다.좋은 평가는 무엇인가?첫 번째로 회사의 방향이 명확해야 한다. 평가라는 과정을 통해서 모든 개인(벡터)에게 변화를 주게 되는데 이 변화가 회사의 방향을 향해야 한다. 그러기 위해서는 무엇보다 이 기준이 명확해야 한다.두 번째로 빨간색의 이동이 효과적으로 일어나야 한다. 이 이동의 동기는 다음과 같은 것들이 있다. 이를 효과적으로 사용해야 한다.피드백 (긍정적인 그리고 부정적인)금전적인 인센티브성장의 기회의 제공세 번째로 빨간색의 이동에 대한 반작용이 적어야 한다. 즉, 합리적이고 투명한 평가가 되어야 한다.에잇퍼센트는 올해 평가를 할 수 있을까?2년 차 스타트업인 에잇퍼센트는 평가를 해본 적이 없다. 아마도 구성원들은 12월이 되었지만 당연히 평가가 있을 거라고 기대하지 않았을 것 같다. 먼저 공표된 평가 기준도 없고 아직 성과가 없는 상태에서 인센티브를 제공할 수도 없다. 하지만 우리는 여전히 회사와 구성원들의 동기를 맞추는 일을 해야 했고, 무엇인가가 필요했다. 그래서 시작한 에잇퍼센트의 2106년 회고를 공유한다.2016년 평가 안회사의 평가 컨플루언스 페이지 (조석님 짤방 감사)2016년 평가는 다음 4가지로 진행된다.에잇퍼센트 어워드피어 리뷰회사 회고회고 평가에잇퍼센트 어워드우리가 일하는 방식인 에잇퍼센트 Way의 각 항목에 떠오르는 사람을 투표한다. 이를 통해 회사가 원하는 인재에 대해 개인들이 한번 더 인지할 수 있도록 한다.회사의 컴플라이언스를 담당하고 계신 성일님이 당연하게도 "마땅히 옳은 일을 한다" 상을 받으셨다. 허리 디스크에도 상을 받기 위해서 회식에 참여하셨다는 소문이 있다.  올해 에잇퍼센트를 부동산 대출도 할 수 있는 종합 P2P 금융사로 이끌어 주신 심사팀장 달수님이 "할까 말까 싶을 땐 한다" 상을 받으셨다. 올해 과감하게 도전하신 달수님 또한 이 상에 적절하다.헌신을 의미하는 "나인가 싶을 땐 나다" 상은 CS팀의 지아님이 받으셨다. 업무뿐 아니라 회사의 많은 곳에서 솔선수범 하셔서 회사가 부드럽게 돌아갈 수 있는데 많은 도움을 주셨다. (왜 이렇게 부끄러워하시는지는 잘 모르겠다)성장을 의미하는 "어제보다 잘한다" 상은 역시 어린 친구들의 몫인가 보다. 회사 #study 채널을 리드하면서 늘 열심히 공부하시는 성호님과 백지(미안 병훈님)에서 시작해 어느덧 모두가 함께 일하고 싶어 하는 개발팀의 병훈님이 받으셨다.에잇퍼센트 어워드를 진행하면서 놀란 두 가지가 있다. 하나는 놀랍도록 적절한 사람에게 상이 주어졌다는 것과 내가 상을 받지 못했다는 사실이다. 여하튼 이 작은 상이 수상자에게 작은 동기 부여가 되었으면 하는 바람이다.피어리뷰피어리뷰는 다음 3명에게 기명으로 리뷰를 받는다.자신이 지정한 1인그룹 내 랜덤 1인그룹 외 랜덤 1인제비뽑기를 통해 랜덤 평가자들이 정해지고 구글폼을 통해서 평가가 진행되었다. 이 결과들은 취합되어 개인들에게 다시 전달되었다. 내가 받은 리뷰 일부를 옮겨본다.잘한 것무늬만 스타트업이었던 회사를 제대로 된 스타트업으로 환골탈태시켰습니다.탑다운이 아닌 바텀업이 가능한 조직으로 변모시켰습니다.프로덕팀을 넘어 전사적으로 효율적으로 일할수 있도록 시스템을 구축하였습니다.study를 장려하고 자기계발에 동기부여를 할 수 있는 분위기를 만들었습니다.좋은 동료를 모으는데 구심점이 되었습니다.못한 것개발팀 외의 문제에 대해서는 상대적으로 관심이 부족했다고 느꼈습니다.일과시간 중 업무에 대한 비중이 지나치게 큰 것 같습니다. 수다도 떨고 장난도 치고 커피나 간식도 사러가는 그런 사교의 시간을 조금 더 내어보면 좋을 것 같습니다.프로덕트팀의 악역이 되어 주세요.잘한 것은 기대했던 것들을 받은 것 같은데, 못한 것은 의외의 것들이 많아서 여러 가지를 생각해 볼 수 있는 좋은 기회가 되었다. 그리고 마지막에 전달받은 대표님의 한마디도 기분이 좋았다. (역시 사람은 칭찬에 약하다)여기에서도 내 예상과 틀린 부분이 있었다. 많은 사람들이 자신의 보스(에잇퍼센트에는 보스라는 개념이 명확하진 않지만 업무적으로 지시를 하고 보고를 해야 하는 사람이 있다)로부터 리뷰를 받기를 원할 거라고 기대했었는데 대부분의 사람이 자신과 밀접하게 업무를 같이 하는 사람으로부터 리뷰를 받기를 원했다.회사 회고 회사에 대한 회고도 에잇퍼센트 Way에 맞춰서 진행되었다. 솔직한 의견을 듣기 위해 무기명으로 진행되었다. 모든 회고가 진행된 이후에는 답변들을 요약하고 정리해서 컨플루언스에 공개했다.에잇퍼센트 Way 를 기준으로 우리 회사가 지난 1년 동안 잘한 것은 무엇일까?에잇퍼센트 Way 를 기준으로 우리 회사가 지난 1년 동안 부족했던 것은 무엇일까?우리 회사가 내년에 어떤 것을 잘하면 "성공" 할 수 있을까?이효진 대표에게 바란다.이 회고에서는 그룹별의 차이를 느낄 수 있었다. 어떤 파트에서는 다양한 시도와 성장을 잘한 일로 뽑았고, 다른 파트에서는 다양한 시도와 성장에 따르는 부족한 완성도를 단점으로 뽑았다.회사 회고를 통해서 내가 보고 있는 회사와 구성원들이 바라보는 회사가 크게 다르지 않다는 것에 안도감을 느꼈다. 그리고 "우리 회사가 내년에 어떤 것을 잘하면 "성공" 할 수 있을까?"에 대한 질문을 통해서 내년에 우리가 나아가야 될 방향에 대해서 고민해 볼 수 있었다.마지막으로 "이효진 대표에게 바란다"를 통해 사람들이 이효진 대표를 어떻게 생각하는지를 엿볼 수 있었다. "똑바로 해라"라는 직설적인 의견부터 "번아웃 되지 않게 조심하세요."라는 따뜻한 의견 까지. 대체로 구성원들이 대표와의 거리를 멀게 느끼지 않았다.회고 평가마지막은 2016년 회고에 대한 평가를 무기명으로 받았다. 이번 평가(회고라는 이름이 붙어 있는)가 에잇퍼센트의 첫 번째 평가이고 앞으로도 평가가 계속되어야 하기 때문에 구성원들의 평가에 대한 피드백을 받을 필요가 있었다. 받은 의견들은 다시 정리해서 컨플루언스에 공개했다.평균 4.1점이면 괜찮은 것 아닌가?다음과 같은 질문을 받았다. 회고의 어떤 점이 좋았나요?평가라는 것을 진행한 것 자체가 좋았다는 의견이 많았다. 그리고 리뷰어를 제비뽑기로 정한 방식도 새롭다고 느낀 분들이 있었다.회고의 어떤 점이 부족했나요?평가에 따른 상벌이 없음에도 불구하고 쓴소리보다는 좋은 말만 쓰는 경향이 있었다. 그리고 제비뽑기로 리뷰어를 지정하다 보니 적절한 피드백을 줄 수 있는 사람으로부터 피드백을 받지 못하는 문제가 있었다. 그리고 처음이다 보니 과정이 매끄럽게 진행되지 못했다.다음 회고를 잘하기 위한 아이디어가 있으세요?좀 더 자주 평가를 했으면 한다고 의견과 그 평가들을 효과적으로 만들기 위한 아이디어들이 제시되었다.이런 의견들을 회사 데모 시간을 통해 구성원들에게 전달하고 이런 의견을 반영한 평가 개선안을 공유하는 것으로 2016년 평가를 마쳤다.개인적인 회고개인적으로 우리의 방식으로 평가의 첫걸음을 내디뎠다는 것에 대해서 만족한다. 어떻게 생각해보면 참 허접한 평가기도 했지만 또 이것이 린하게 접근하는 스타트업의 방식이라는 생각도 든다. 앞으로 에잇퍼센트라는 회사는 계속 성장하게 될 것이고 그에 따른 평가 방식들을 갖춰 나가야 할 것이다. 이를 위한 예방주사를 맞았다고 생각할 수 있을까.  이와 같은 허접한 시도도 달갑게 받아들이고 회사와 대표에 대해 의견을 아끼지 않는 것을 보고 구성원들이 참 건강하구나라는 것을 느꼈다. 이런 조직에서 일할 수 있는 것에 다시 한번 감사한다.마지막으로 나 자신이 에잇퍼센트에 앞으로도 좋은 영향을 미칠 수 있겠구나라는 자신감이 들었다. 2017년도 파이팅하자.#8퍼센트 #에잇퍼센트 #2016년 #평가 #조직문화 #기업문화 #팀문화
조회수 1500

3) 나는 애자일 하게 일하고 있을까?

글목록1) 우리는 왜 애자일 하지 못할까2) 우리는 애자일 하게 일하고 있을까?3) 나는 애자일 하게 일하고 있을까? (현재 글)지속적인 속도로 업로드를 못했네요 죄송합니다! 앞으론 지속 가능한 글쓰기를 실천할 수 있도록 할게요!이전 글에서는1. 어떤 가치를 기반으로 저는 일하고 있고,2. 이런 방식으로 일하면서도 제가 겪고 있는 수많은 난제들에 대해 설명드리려 했으나,  이번 글에서는 오늘도 분량 조절의 실패로 제가 일하고 있는 팀은 어떤 가치를 기반으로 일하는지 설명드리도록 하겠습니다.우리는 어떤 가치를 기반으로 일하고 있는가1. "나" 보단 "우리"가 맞고, "우리"보단 "유저"가 맞다 프로덕트의 가치를 확인할 때 나(또는 개인)의 기준보다는 프로덕트를 만들고 있는 모두의 의견이 더 중요하고, 그리고 이보다 더 중요한 건 이 프로덕트를 사용하는 유저가 프로덕트에 가지는 의미가 더 중요합니다. 이를 실천하기 위해 User Persona(유저 페르소나)를 구축하고, 해당 User Persona를 더 정확하게 구성하기 위해 많은 유저들과의 인터뷰를 진행하고,1. 유저가 가지고 있는 기본적인 사실들을 확인 (Facts)2. 유저가 어떻게 행동하고 있는지에 대한 행태를 조사하고 (Behaviors)3. 그리고 그 유저가 어떤 니즈와 목표를 가지고 있는지(Needs & Goals)를 확인하고, "유저들이 니즈와 목표를 수행할 수 있도록 프로덕트는 어떤 것들이 가장 필요할 것인가"에 집중하고 있습니다.유저들과의 인터뷰를 통해, 공통된 니즈와 목표, 행태, 그리고 팩트를 기반으로 Persona를 만듭니다 이렇게 작업을 하는 이유는 프로덕트를 만들며 나올 수 있는 다양한 "기능" 또는 "전략" 등 다양한 이해관계가 같이 일하는 많은 동료들(우리 식구들!) 간 "어떤 것을, 왜, 그리고 언제"에 대한 것을 결정하며 많은 갈등 상황을 초래할 수 있고, 이해되지 않는 부분들에 대해서도 명확하게 설명할 수 없는 상황들이 생길 수 있으나, "누군가의 의견"또는 "아직은 검증되지 않은 이야기"들을 기반으로 하는 게 아닌, "진짜 유저들이 원하는 무언가"를 전달할 수 있는 가치에 대해 모두가 하나의 시선으로 바라볼 수 있게 함으로써 모두가 프로덕트에 집중할 수 있기 때문입니다.2. 무엇이 유저 or 비즈니스 상 필요한가 / 빠르게 빌드하고 테스트할 수 있는가를 기반으로 순서를 만들고 개발한다. 아무리 Persona를 기반으로 작업한다고 하더라도, 많은 Task들이 눈앞에 닥칠 경우, 개발팀은 당연히 많은 작업량 때문에 시작도 하기 전 겁을 먹을 수 있고, 무엇을 어떻게 시작해야 할 지에 당황하고 걱정하기 마련입니다. 그리고 이런 상황에서 리소스(Resource)는 지극히 한정되어 있기 때문에 항상 우선순위를 두고 작업을 진행하는 것은 상당히 중요합니다. 그리고 제가 일하는 팀은 이를 해결하기 위해 몇 가지 기준을 가지고,1. 무엇이 유저에게 또는 비즈니스 상에서 가장 큰 가치를 주는가2. 무엇이 가장 빠르게 개발할 수 있는가 또는 테스트할 수 있는가를 기준으로 우선순위를 지속적으로 산정하고 일하고 있습니다.우선순위는 지속적으로 변경되고 발전합니다. 그리고 우선순위에 대한 기준은 언제나 유저 입니다! 이런 우선순위의 설정과 지속적인 확인은 우리가 개발을 해야 하는 이유에 대해 명확한 이유를 만들고, 수행과정에서 일을 하고 있는 모두가 같은 같은 곳을 바라볼 수 있어 굉장히 중요합니다. 그리고 지속적인 우선순위에 대한 산정을 통해 더 빠르게 개발하고, 검증하고, 성장하고, 실패를 통해 배울 수 있습니다. 우선순위에 대한 산정에 있어 많은 의견도 나오고 조율에 시간이 필요하지만, 유저를 기반으로, 그리고 우리가 가진 산업에 대한 지식을 기반으로 일하기 때문에 생각보다 빠르게 의사결정을 할 수 있기 때문에 효율적으로 일하고 있습니다.3. 프로덕트 팀에서도 "유저에게 가치가 있다"라는 부분이 이해될 때 작업한다. 아무리 좋은 "기능", "방향"이래도1. 개발이 비즈니스 사이드 또는 디자인 사이드에서 의도한 대로 개발되지 않고2. 유저 또는 비즈니가 원하는 목표와 니즈를 만족시키지 못하는 기능이 만들어진다면결국, "사용할 수 없는 프로덕트"를 양산할 수밖에 없습니다. 그리고 이런 리스크를 최소화하기 위해,- 개발자 / PM과 함께 유저 시나리오를 기반으로 한 디자인 스튜디오 진행- 개발 이터레이션 이전 Pre-IPM/ IPM 진행을 통해 개발/ PM/ 디자인 간의 투명성을 높이고, 모두가 "유저에게 필요한 건 무엇이고, 어떤 이유 때문에 지금 해당 개발을 진행할 예정이다."라는 것을 알고 일할 수 있도록 최선을 다하고 있습니다. 이해가 되지 않는 프로덕트에 대해 PM이 방향성을 설명할 수 없고, 근거에 대해 정확하게 포인트 아웃(Pointing out)할 수 없으면 개발을 요청하기보단, 검증이 부족한 부분을 더 채우고 진행하려고 노력합니다.(* 디자인 스튜디오: 목표와 유저 시나리오를 기반으로 개발/ PM/ 디자인 사이드에서 생각하는 과정 예외에 대한 부분들을 자유롭게 그려보고 아이디어를 공유하는 워크샵)(* Pre-IPM, IPM: Iteration Planning Meeting의 줄임말로, 개발 전 1. 무엇을 개발할지, 2. 유저에게 가치가 있는지, 3. 작업량의 분기는 적당한지, 3. 기술적 결함이나 선행되어야 할 부분들이 있는지 확인하는 미팅) "어떤 기능이 필요하다"라는 단순한 판단으로 기능 개발을 요구, 혹은 강요하는 건 기능 개발에 대한 이해를 해치고, 실제 작업하는 작업자들의 판단 기준을 흐리게 하고, 능률을 저하시키는 등 많은 최악의 경우들을 지속적으로 만듭니다. 함께 하는 모든 분들이- 해당 기능이 왜 필요한지- 유저는 어떤 가치를 얻을 수 있는지- 해당 기능을 위해서 내가 할 일은 무엇인지에 대해 정확하게 알고 있다면, 서로가 놓치는 부분들에 대해서 공유할 수 있고 이해할 수 있는 부분들이 생기고 이렇게 지속적으로 신뢰가 생기기면 우리는 이제 진짜로 "100미터 달리기"로 프로덕트를 만드는 것이 아닌 "진짜 마라톤"을 모두가 같이 달릴 수 있다고 생각합니다.오늘도 분량 조절에 실패했습니다... 글쓰기는 굉장히 어려운 것이군요(또르르...)다음장에서는- 이렇게 좋은 말만 가득함에도 불구하고, 겪고 있는 문제점과 이를 해결하기 위해 노력하는 부분을 이야기하도록 하겠습니다. 이후에도 애자일한 개발과 검증 그리고 피봇을 하기 위해 하고 있는 다양한 방법들과 애자일 하게 나갈 수 있는 방법에 대해(XP 그리고 Lean Roadmap, Interview 등등등) 소개하는 글도 차례차례 올릴 예정이니 기대해 주세요! 감사합니다!#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 949

임팩트 커리어 Y는 꿈을 응원받을 수 있는 기회입니다

임팩트 커리어 Y를 통해소셜섹터 마케팅 커리어를 경험한루트임팩트 전예진 님임팩트 커리어 Y 2기로 루트임팩트에서 인턴기간을 마친 전예진 님. 커뮤니티를 통해 사회에 선한 영향력을 미치고 싶다는 전예진 님의 이야기를 들어보자.대학교 2학년 재학중에 임팩트 커리어 Y 2기의 최연소 참가자가 된 예진 님(오른쪽)예진님을 소개해 주세요. 어떻게 사회혁신 커리어에 관심을 갖게 되었나요?고등학생 때 ‘소셜밸류’라는 교내동아리를 통해 사회적기업에 처음 관심을 가졌어요. 소셜벤처 경연대회에 나가보고, 협동조합 안내책자도 만들면서 소셜섹터의 존재를 알게 되었고요. 이후에는 대학교 창업동아리에서 잠시 활동하며 단순히 금전적 이익만 추구하는 것이 저에게 동기부여를 하지 못한다는 것을 깨달았습니다. 마침 휴학하고 새로운 경험을 하고자 했던 시기에 임팩트 커리어Y를 알게 되어 루트임팩트에서 인턴으로 일해보는 소중한 경험을 할 수 있었습니다.많은 회사들 중에 왜 루트임팩트에 지원하게 되었나요? 저는 아직 2학년이기 때문에 소셜섹터와 생태계 전반에 대한 이해를 하고 싶었어요. 그래서 생태계를 조성하고 있는 지원조직인 루트임팩트가 가장 먼저 눈에 들어왔어요. 대학 생활을 하면서 비슷한 가치관을 가진 친구의 영향을 받아서 관심이 더 깊어졌는데요. 서로 나누던 대화중에 커뮤니티를 통해 선한 영향력을 미치는 방법에 대한 이야기도 있었는데요. 루트임팩트가 그런 일을 하는 조직이라고 생각했습니다.부트캠프 교육기간 동안 가장 좋았던 것은 무엇인가요?본격적으로 일을 시작해서 가장 힘이 된 건 서로 응원하고지지해주는 동기들이었어요.'디지털마케팅 강의', '비즈니스 모델 이해하기'와 같은 강의들도 첫 인턴경험이라 살짝 겁을 먹은 저에게 차분한 마음가짐을 하도록 도와주었어요. 하지만 본격적으로 일을 시작해서 가장 힘이 된 건 서로 응원하고 지지해주는 동기(a.k.a 멋진 언니, 오빠들)들이었어요. 각자 자신의 자리에서 하고 있는 고민들을 서로 나누고 조언해주는 임팩트커리어 동기가 있어서 든든했습니다.인턴기간 동안 무슨 일을 했고, 어떤 성장을 했다고 생각하시나요? 저는 루트임팩트 마케팅팀에서 디지털마케팅 인턴으로 근무했습니다. 제가 했던 업무는 크게 SNS 채널 관리라고 할 수 있는데요, 기획부터 실행까지 업무의 자율성이 많이 보장되었어요. 구체적으로는 홈페이지의 체인지메이커 매트릭스 수정 및 번역, 루트임팩트 브런치 중 "People in 루트임팩트"관리, 디자인툴 배우기 및 적용하기, SNS월간홍보캘린더 관리, 그리고 루트임팩트와 헤이그라운드 인스타그램 기획 및 관리를 했습니다. 저의 배움과 성장을 응원하는 팀 분위기 덕분에 해보지 않았던 인스타그램 마케팅을 공부하고 기획하고 실행해볼 수 있었고, 미팅을 따라다니면서 다양한 소셜섹터 플레이어들을 만나 소셜섹터의 생생한 현장을 경험해볼 수 있었습니다. 커리어적으로는 소셜섹터를 넓게 이해할 수 있게 된 것, 이전보다 더 적극적인 행동을 해볼 용기가 생긴 것이 성장한 부분이 아닐까 생각합니다.헤이그라운드를 누비며 입주사 분들과의 인터뷰를 기획했던 예진 님.임팩트커리어 3기 지원자들에게 해주고 싶은 말과 함께 예진 님의 앞으로의 계획을 나눠주세요!아직 학업을 마치지 않은 재학생 분들도 앞으로 소셜섹터에서 일해보고 싶다면, 그리고 배우고 성장하고자 하는 열정이 있다면 열린 마음으로 도전하셨으면 좋겠어요. 저는 인턴 끝나고도 헤이그라운드를 자주 방문하고 있는데, 지금 사회혁신 컨설팅 대회에 참여해서 소셜벤처 비즈니스 모델을 구상하고 있어서 그래요! 우선 이 프로젝트를 열심히 하다가 복학해서 흥미로워 보이는 수업들을 마음껏 듣겠죠? 언젠가 소셜섹터에서 다시 만날 날을 기대합니다.#루트임팩트 #헤이그라운드 #임팩트커리어 #스타트업인턴 #마케팅인턴 #인턴마케터 #부트캠프 #소셜벤처 #커리어경험
조회수 3943

회사에서 당신의 이름은 무엇인가요?

루피, 제우스, 드루와, 아이린, 돌핀, 윈터, 앤, 시나몬……위에 언급된 단어들을 보면 어떤 것들이 떠오르는가. 애니메이션 ‘원피스’의 주인공, 그리스 로마 신화 속 신들, 영화 ‘신세계’ 속 황정민의 대사 등 다양한 매체들을 통해 형성된 이미지들이 떠오를 것이다. 그런데 왜 연관성이라고는 하나도 없는 단어들을 함께 나열한 것일까?도통 공통점이라고는 없어 보이는 이 단어들은 하나의 공통점을 가지고 있다. 바로 어니스트펀드에서 꿈을 키워나가고 있는 팀원들의 닉네임이라는 것이다. 어니스트펀드의 모든 팀원들은 닉네임을 사용하고 있다. 회사에 오면 만화 캐릭터, 신화 속 인물, 동물, 계절 등 다양한 닉네임들이 사람들의 입을 오간다.어니스트펀드에 합류하기로 결정한 이들에게 제일 처음으로 주어지는 일은 자신이 사용할 닉네임을 짓는 것이다. 나 또한 마찬가지였다. 만약 내게 입사 전에 나의 직급에 대해 알려주고, 회사의 조직도를 보여줬다면 입사하는 나의 마음가짐은 지금과는 많이 달랐을 것이다. 조직도를 보며 직급과 이름을 외우고, 만나보기도 전에 직급에 따른 편견을 만들었을 확률이 높다.입사 후에 회사 사람들을 소개받을 때 오로지 닉네임에 대해서만 소개받았다. ‘이 분은 XXX이사님이시고 IT기업인 XXX출신이십니다’라는 식의 소개 대신 ‘이 분은 린다이고 마케팅을 담당하고 있어요’라는 식의 소개를 받는 것이다. 이런 식의 소개를 받다 보니 직급과 경력에 따른 편견 대신 ‘저 닉네임의 뜻은 무엇일까’와 같은 지극히 개인적인 호기심이 먼저 든다.이전에 다니던 직장에서는 본명을 소개함과 동시에 자연스럽게 이어지는 질문들이 존재했다. 고향과 나이, 출신학교와 경력 등의 질문이 통과의례처럼 이어졌다. 그리고 지금의 어니스트펀드에서는 ‘윈터’라는 닉네임을 소개하고 나면 ‘윈터라는 닉네임은 어떻게 지었나요’, ‘윈터는 겨울을 좋아하나요’와 같은 질문을 주로 받는다. 내 배경에 대한 질문 대신 온전히 나 자신에게 집중된 질문이 이어지는 것이다.언어는 생활을 지배하고, 문화를 만든다. 서로 편하게 닉네임을 부른다는 것은, 업무를 진행할 때도 그대로 반영된다. 직급이나 연차와 상관없이 닉네임을 부르며 동등하게 의견을 낼 수 있고, 모두들 그것을 당연하게 생각한다.업무 효율이 높다는 것도 좋지만, 사실 가장 좋은 것은 사람을 조건이나 배경 대신 있는 그대로 볼 수 있다는 것이다. 어니스트펀드에 오기 전에는 직장에서 친구를 만든다는 것에 대해 굉장히 회의적인 생각을 가지고 있었다. 서로 다른 배경을 가진 이들이 모여서 편견을 가지고, 서로 간의 격차가 있다고 믿는 분위기 속에서 가까워지지 못하는 경우가 많았다. 직장에서 친구를 만들기 위해서 필요한 것이 진심이 아니라 다양한 조건과 배경이 되어버린 것이다.하지만 닉네임을 사용하는 지금은 다르다. 나이와 출신학교, 경력과 상관없이 동료이자 친구가 된다. 서로에 대해 알아갈 때 그 사람의 배경이 아닌, 함께 생활하며 느낀 그 사람에 대한 솔직한 감정에서 관계가 시작된다. ‘이전 직장에서 일할 때는 어땠어요’라는 질문 대신 ‘출근길에 보니까 책 읽고 계시던데 저도 책 추천 좀 해주세요’라는 질문으로 관계가 시작될 수 있는 것이다.이름은 일종의 배역이다. 본명인 ‘김승용’과 어니스트펀드에서 사용하는 닉네임인 ‘윈터’는 동일인물이 사용하고 있는 이름이지만 그 성격을 달리 한다. 김승용이라는 이름 뒤에는 수많은 배경이 존재한다. 김승용이라는 개인은 태어나서 학교에 입학하고, 군대에 가고, 직장에 들어가는, 사회가 만들어놓은 대한민국 남성 평균의 절차를 거쳤을 것이라고 사람들은 예상한다.반면 윈터라는 닉네임은 어니스트펀드에서 시작되었기에 함부로 예측할 수 없다. 윈터라는 개인의 삶을 그 누구도 정해두고 제한하지 않는다. 어니스트펀드라는 집단 안에서 윈터의 영역은 만들어가기 나름인 것이다. 마케팅 업무를 하는 윈터가 데이터 업무에 관심을 가진다고 해도 그 누구도 뭐라고 하지 않는다. 이름 대신 새로운 닉네임으로 일을 한다는 것, 그것은 결국 모든 편견에서 벗어나서 주체적으로 일할 수 있다는 것을 뜻한다. 하나의 이름은 하나의 고유한 세계를 담고 있다. 앞으로 내게 ‘윈터’라는 단어는 어니스트펀드에서의 삶을 온전히 함축한 단어로 기억될 것이다. 훗날 윈터라는 단어를 보며 어떤 모습을 떠올리게 될까. 먼 훗날에도 여전히 사람들이 내게 배경이 아닌 윈터라는 닉네임을 짓게 된 이유에 대해 묻고, 서로의 삶에 안부를 물으며 편한 동료가 될 수 있다면 윈터로서의 나의 삶은 충분히 성공적으로 기억될 것 같다.금융과 IT를 결합하여 기존의 대출·투자 경험을 혁신하는 P2P금융 스타트업, 어니스트펀드의 이야기가 연재될 팀 브런치에 많은 관심 부탁드립니다. 어니스트펀드에 대해 더 알고 싶으시다면, 어니스트펀드 홈페이지를 방문해보세요#어니스트펀드 #사내문화 #수평적조직 #닉네임 #호칭 #사내호칭
조회수 658

잘나가는 브랜드를 내 맘대로 분석해보았다.

맨날 당신이 브랜딩이 안되는 이유만 까고 있으니 너무 까칠해 보이기도 하고, 뭔가 꼴사납기도 해도 시선을 조금 돌려보았습니다. 주변에 잘 나가는 브랜드들은 당최 왜 저렇게 잘되어가고 있는가..라는 쪽으로 말이죠. 물론 회사 내부 사정이야 제가 재무제표를 까본 것은 아니니 카드돌려막기를 하는지 어떤지는 알 수 없으나, 여튼 소비자 입장에서 딱 들으면 아 그거. 하고 알만한 것들에는 이유가 있다고 생각합니다. 그러니 회사자체의 문화화나 비지니스 내적인 부분은 모른다셈 치고 일단 '브랜딩'에는 성공한 3가지의 케이스들을 살펴보도록 하겠습니다. 위에서 말했다시피, 사내시스템/문화/재무상태가 어떻게 돌아가느냐에 상관없이 브랜딩은 별개의 문제입니다. 브랜딩이란 것은 회사가 이렇게 하쟛! 이라고 해서 이렇게 챡 되는 부분이 아니기에, 소비자들이 알아서 판단할 몫이지요. 때문에 어그로를 끌수도 있고, 언플을 할 수도 있고, 셀럽을 동원할 수도 있고, 기타 등등 다양한 발버둥을 치지만 그 결과가 엉뚱하게 만들어지기도 합니다. 결과적으론 브랜딩에 성공하기도 하고, 백날 노력해도 폭망과 닭발과 소주로 귀결되기도 하지요. 마케팅을 하지말자LG는 원래 백색가전이 굉장히 유명했습니다. 하지만, LG가 삼성보다 실제로 가전쪽의 품질이 우수할까요? 뭐 항간의 소문에는 모터달린 것은 LG것을 사라는 얘기도 있었지만, 그것은 낭설일 뿐입니다. 물론 품질면에서 누가 더 낫다라는 얘기는 아닙니다. LG의 백색가전은 튼튼하고 오래쓸 수 있죠. 그러나 백색가전 신디롬의 팩트는 어떤 데이터나 근거가 아닌 그저 인터넷에 떠돌아다니는 얘기일 뿐입니다. 이것이 퍼져나간 것은 바이럴의 힘입니다. 보통 백색가전은 신혼부부의 수요가 많고, 순환주기가 느린 편입니다. 이 얘기는 한 번 살 때 이것저것 꽤나 따져보게 된다는 얘기지요. 그러나 태어나서 한번도 냉장고, TV, 세탁기따윌 내 돈으로 사본 적이 없는데 혼수를 장만하려니 뭐 알겠습니까? 그러니 커뮤니티와 인터넷노가다를 통해서 정보를 얻게 되는데, LG는 대기업입니다. 삼성과 항상 비교당하죠. 1등의 제품은 물론 겁나 좋다!! 라는 인식이 있지만, 비싸다..라는 선입견도 함께 작용합니다. 2등은 저것보단 조금 안좋겠지만 특정부분은 좋다더라. 근데 2등이니까 조금 더 쌀거야. 라는 말도 안되는 사고회로가 작용합니다.이것은 순위에 대한 선입견이 개입하면서 발생하는 논리의 오류입니다. 실제로 LG가 삼성보다 싸진 않습니다. 오히려 비싼 제품군들도 있죠. 그리고 뭐가 좋다, 안좋다에 대한 명확한 판단의 근거가 없습니다. 그냥 소비자는 아는 언니가 써보니 좋았다더라는게 '옆집이모통신'의 정보가  더 신빙성있게 다가오기 마련이니까요. 게다가 오래 쓰기때문에 구매에 있어서 인지부조화가 잘 이루어지는 편입니다. 단점이 생활로 카바되는 거죠. 저놈의 세탁기가 덜덜덜덜덜더더덜덜덜덜 거려도 쓸만하고 잘 빨리더라. 라는 등...어차피 오래 쓸 내 가구니까 기왕이면 장점을 보자..라는 인지부조화의 오류가 재구매로 이어지는 이상한 기현상을 만들기도 합니다. 내가 쓰고 있으니, 그걸 추천하고 싶은 것도 당연합니다. 단레몬 합리화죠. 내가 가지고 있는 레몬은 달고 맛있다고 여기는 거예요. 내 제품은 쓰레기니 이거 사지마!! 라고 할 사람은 사실 거의 없습니다. 만약 있다면 진실과 진리를 탐구하는 머나먼 역사속 성현의 후손으로 생각하고 발을 씻겨드릴 수 있습니다. (실제로 있긴 합니다.) 분명 LG의 마케팅은 거지같습니다. 뭐 요즘엔 LG그램광고를 이 실험 저 실험하면서 열심히 하고 있긴한데...정작 굉장한 능력들은 쉽게 드러나지 않습니다. 이건 LG마케팅팀의 기본전략이 다소 보수적이고 교과서적인 STP전략을 따르고 있는 탓이 큽니다. 타켓에 필요한 특정기능만을 부각하고 나머지는 감추는 셈이죠. 감춘다기 보단 아예 얘기를 안한달까요. 물론 마케팅팀이 일을 안하는 것도 있지만, 개판치는 경우도 많습니다. LG V20의 티저 광고 문구 "듣다 보다 그 이상"의 경우는 세로드립으로 듣보잡? 이라는 놀림을 받기도 했고, LG G5의 제품 사진 한 가운데 애플 로고가 떠 있기도 했습니다. 이건 뭐..아예 검수를 안한건지...게다가 좋은 제품의 특성들은 그냥 쌩까버리죠. 토네이도에 날려 던져진 냉장고가 고장없이 작동하고 있었다던가, LG그램의 무게를 최저가 아닌, 평균치로 계산해버려서 오히려 10g무겁게 광고한다던가... 뭔가;;; 오전에 알밤막걸리 한잔씩하고 일하는 건지 흥미진진한 마케팅을 보여주고 있습니다.아다만티움 냉장고그런데 놀랍게도 고객들이 알아서 마케팅을 해주겠다고 장점들을 찾아서 마구 뿌려줍니다. 물론 가십과 루머들이 많지만, 자연스러운 바이럴이 이루어지면서 고객들 자체적으로 LG는 마케팅을 안할 뿐, 잘하는 기업 이라는 인식을 만들어가기 시작했습니다. 2위에 대한 측은함, 제품에 대한 만족도, 마케팅에 대한 불만이 섞이면서 몇몇 덕력넘치는 사람들의 소수 가십으로 시작된 바이럴브랜딩의 표본이라고 할 수 있겠습니다. 이처럼 인간의 행동과 움직임은 역회전시켜서 던진 탱탱볼과 같습니다. 심리적 반동과 인지부조화, 불만의 역주행 등..다양한 구매심리가 뒤섞이고 서로 방향을 달리하면서 예상치못한 흐름을 만들어내기도 하죠.배달의 민족 포스터 中배민의 브랜딩은 이미 유명합니다. 사실 분석하고 말 것도 없죠. 물론 내부적인 이런저런 사정이야 있겠지만, 브랜딩을 하는 사람들에게 아주 유용한 레퍼런스임은 변치 않는 사실입니다. 소비자입장에서 배민의 브랜드는 '한결같음' 입니다. 하지만 실제로 비지니스가 한결같았냐..하면 그건 아닙니다. 배달앱으로 시작했고, 푸드플랫폼으로 확장하다가, 지금은 반찬가게느낌으로 바뀌었습니다. 종종 도시락을 배달시켜먹곤 했는데, 어느 순간 반찬만 가득하더라구요. 분명 비지니스는 역동적으로 바뀌고 있지만 중요한 건 그걸 눈치채지 못하게 정말 한결같은 브랜드 철학을 고집했다는 겁니다. 배민의 브랜드컨셉은 전반적으로 '키치함' 입니다. 위트있고 유머러스한 언어유희와 아주 대중적인 것과 의외의 것들을 섞은 치믈리에 프로젝트 등 어렵고 복잡한 것이 아닌, 공감대를 형성할 수 있는 '쉬운 언어'를 사용합니다. 이것은 놀라운 일입니다. 대부분의 회사가 제품과 서비스의 장점을 극대화시키려고 전문용어나 추상적인 용어를 남발하는 것에 비해 배민의 언어는 8살짜리도 이해할 수 있죠. 또한 브랜드와 관련한 다양한 굿즈들이 제작되면서 인스타와 페북에 널리널리 퍼져가기 시작했습니다. 이 굿즈들은 은행로고박혀있는 2017년 달력같은게 아니라, 그 자체로 소장하고 싶은 욕구가 넘치는 재밌고 예쁜 것들이었죠. 사람들은 배민 브랜드제품을 가지고 있다는 것을 자랑하고 싶어했습니다. 그냥 사은품이나 한낱 판촉물이 아니라, 그것 자체가 곧 완성된 제품과도 같았죠. 도대체 이런 건 누가..당신은 대체..하지만 대부분의 회사는 어떤가요. 그 3색펜에다가 로고 띡 박아서는 졸라 어디가서 쓰지도 못하게 촌스러운 투명초록색 목걸이펜 증정품....이라던지, 다이어리 뒷면에는 왜 꼭 무슨생명..하고 턱 하니 박아서 들고다니면 친구에게 ...'어....오..오랜만이긴한데..너 요즘 보험하니...?' 라는 소리나 듣게 만드는 그런 것들 투성입니다. 일단 판촉물자체가 개구린데다가, 그것에 정성도 없고, 브랜드굿즈라는 인식도 없습니다. 로고만 박혀있으면 끝났다!!~~~라는 것이 일반적인 생각이죠. 배민의 브랜드성공비결은 개인적으론한결같은 쉬운언어와 키치함모든 것에 브랜드언어를 녹여낸 치밀함공감과 트렌드를 적용한 멋진 워딩들이라고 생각합니다. 소비자가 임의대로 조작한 이미지가 아니라, 배민이 원하는 이미지 그대로 시장에 퍼져나가도록 아주 일관성있고 지속적인 브랜딩을 진행했다고 봅니다. 물론 직접 그런 의도로 하지 않았어도...이미 그렇게 보인달까요.세번째는 카카오입니다. 카카오의 브랜딩은 말할 것이 없죠. 뭔가 유쾌하고 트렌디하지만 배민의 그것과는 확연히 다릅니다. 카카오에 대해선 마케팅부분에 대한 여러가지 분석해놓은 글들이 많아서, 여기선 그것까진 논하지 않겠습니다. 비쥬얼적인 부분만 볼께요. 일단 카카오의 브랜딩을 선도적으로 이끌었던 것은 다름아닌 '캐릭터' 입니다. 그리고 '색깔'이죠.카카오의 캐릭터는 정말 엄청나게 귀엽습니다. 놀라운 건 이 귀여움이 스토리를 눌러버렸다는 것이죠. 흔한 상식으론 캐릭터=스토리라는 공식이 있습니다. 스토리가 탄탄한 캐릭터가 성공한다는 것이 일반론이죠. 하지만, 아직도 라이언이 사자인지 곰탱이인지 모르는 사람들이 널려있는데다가 무지가 토끼라는 둥의 소리를 하고 있는 것 보면 사실 스토리가 정말 중요한 것인가??...라는 의문이 듭니다. 귀여움은 스토리보다 강한 것 같습니다. 게다가 저 캐릭터는 사실 이모티콘으로 시작했던 것이라서 그 캐릭터자체라기 보단 '언어의 일환' 으로 작용했습니다. 이모티콘=언어. 인셈이죠. 언어. 사람들끼리의 유대와 문화를 만들어가는 가장 강력한 요소입니다.언어. 사람들끼리의 유대와 문화를 만들어가는 가장 강력한 요소. 캐릭터를 보내고 받는다는 것은 곧 '카카오의 브랜드' 가 언어가 되고 문화가 되어간다는 얘깁니다. 한 번 습득한 언어는 쉽사리 바뀌지 않죠. 세종대왕이 한글을 창조한 것에 비하면 상놈이겠지만, 여튼 캐릭터이모티콘과 움짤이모티콘으로 21세기의 새로운  나랏말쌈을 지으셨다고 할 수 있겠습니다.두 번째 요소는 컬러입니다. 뭐 기업의 색깔이 있니..그딴 색이 아니라, 그냥 진짜 '컬러' 말입니다. 노랑. 카카오하면 딱 떠오르는건 노란색입니다. 것도 꽤나 쨍한 노란색이죠. 채도가 엄청 강합니다. 사실 브랜드의 색으로 적합할까??? 싶기도 할 정도의 강렬한 노랑에, 심지어 아주 짙은 갈색톤의 백그라운드가 있습니다. 엄청난 대비죠.컬러..누가봐도 그냥 카카옵니다. 이런 강력한 색대비를 통한 비쥬얼 브랜딩은 사실 유일무이하다고 보여집니다. 딱 떠올려보면, 온라인기반 서비스중 색으로만 설명되는 브랜드가 떠오르시나요? 사실 그리 많지 않습니다. 메인 앱이미지의 색정도는 있겠지만, 이렇게 온통 노란색으로 치덕치덕해놓은 곳은 드물죠.비쥬얼브랜딩이 어떻게 전체 브랜드를 지배할 수 있는지 잘 보여주는 사례입니다. 색은 매우 원초적인 개념입니다. 딸기는 빨강, 바다는 파랑, 하늘은 하늘색 등..우리는 딱 사물과 색을 연결시켜서 인식하려고 합니다. 포도가 흰색이면 아주 개떡같겠죠? 회색 수박을 생각해보셨나요? 그렇습니다... 이 개념은 쉽사리 바뀌지 않습니다. 시각정보중에서 가장 강력한 브랜딩수단은 바로 '색' 입니다. 카카오는 이 부분을 성공적으로 녹여냈고, 모든 굿즈와 제품, 서비스에 동일한 컬러패턴을 유지시켰습니다. 대충 누가봐도 카카오란 것을 알 수 있는 비쥬얼컨셉은 사실상 사람들이 '카카오'와 '다른 어떤 것' 을 구별시키는 데에 크게 기여했죠.마지막으론 서비스포인트입니다. 사실 요즘에 카카오가 아닌 곳이 없습니다. 카카오톡은 물론이고, 택시, 팝업스토어, 악세서리, 네비게이션, 뱅크까지. 생활전반의 크고작은 부분에 카카오 서비스가 침투해 들어가기 시작하는데, "뗄레야 뗄 수 없는" 것들에 잠식하기 시작하면서 브랜드라기 보단 하나의 인프라로 작용하기 시작했습니다. 물론 문제도 많았죠. 카카오 계열사 중에선 우리가 알고있는 것보다 폭망한 것들이 많습니다. 그러나 소비자 입장에선 알바가 아니죠. 지금 내가 쓰고 있는 카카오택시가 택시를 잘 부르는 장땡이니까요.이러한 인프라가 만들어지기 위해선 이미 성공적인 브랜드구축을 기반으로 합니다. 그리고 새로운 서비스를 키워나가면서 다시 브랜딩이 되는 식의 순환구조를 만듭니다. 브랜드의 힘은 이미 정보와 신뢰, 기성인프라의 축을 흔들고 있습니다. 가장 보수적이고 신뢰를 기반으로 한다고 여겨지던 은행업계에 파급력을 미치면서 그것을 증명했죠. 이미 시대는 정보의 독점이나 어려운 용어와 절차를 통해 상하관계를 만들던 시대를 넘어섰습니다. 누구든 정보에 접근할 수 있고, 고객들은 선택의 폭이 넓어졌으며 똑똑해졌습니다. 기고만장하게 콧대세우고 있는 은행들의 잣가지 높아 고고한 서리바람 아래 화랑의 모습보단 친구같고 편안한 은행인 카카오에 방향을 돌린 것은 그 이유입니다. 카카오의 브랜딩포인트는 새로운 언어를 창조하다강려크한 색으로 아이덴티티를 규정하다브랜드, 인프라가 되다.입니다. 끝.
조회수 987

런칭, 그리고 문제에 직면하다

2012년 8월 1일.위제너레이션 사이트가 런칭하기로 한 날이다.사이트야 외주를 통해 개발한 것이니 원하는대로 100% 구현된 것도 아니고 오류가 있긴 했지만그래도 큰 문제는 어느 정도 잡았고 언제까지나 미룰 수도 없으니 일단 8월 1일로 런칭 시점을 잡았다. +이전에도 서비스 런칭 관련해서는 특별한 경험을 한 일이 있었다.위제너레이션 이전에 함께 했던 서비스 런칭 시점의 일이었다.12월 23일. 때는 바야흐로 크리스마스 이브 전날.당시 회사는 서비스 런칭을 맞아 네이버 메인 배너 광고를 준비했다.모든 스타트업들이 꿈에 그릴 몇천만원짜리 광고!마케팅이 생명인 서비스였기에 큰 맘 먹고 대규모 노출 광고를 준비한 것이다.하나 둘 셋땡!런칭 기사 배포와 동시에 네이버 배너 광고가 시작되었다.정말 사람들이 우리 사이트에서 구매를 할까? 두근거리는 마음으로 기다리던 찰나,이게 왠 일인가.대규모 트래픽으로 사이트가 터져버린 것이다.기술 이사님은 1시간여를 컴퓨터와 싸우고...옆에 있는 우리는 애태우며 발만 동동 구르고...그렇게 당황스럽게도 터져버린 사이트와 함께, 광고 시간은 모두 지나가버렸고밖에는 애꿎은 눈만 펑펑 내렸다고 한다-+여하튼 이러한 사건을 겪었으니 '서비스 런칭'이라고 하면 그 때처럼 뭔가 드라마틱한 문제들을 기대(?) 했던 것 같다.하지만 더 큰 문제는 따로 있었으니,아무 일도 일어나지 않았다는 것이다.서비스를 런칭한다고 이 사회에 새로운 물결과 반향을 불러오고 사람들과 미디어의 온갖 관심을 불러일으키는 일은 알겠지만, 없었다. 유명인과 함께 하더라도 말이다.성과는 그렇다치고, 우리가 계획했던 모델이 부딪혀보니 현실적으로 실행 자체가 불가능한 계획이었다.애초의 계획은 이렇다.1) 자선단체 1개 - 유명인 1명 - 기업 1개씩을 엮어 2주마다 3개의 모금 캠페인을 시작하고, 2주 동안 모금을 진행한다. 2) 모금이 성공했을 시, 모금에 참여한 사람 중 3명을 추첨하여 유명인은 그들과 함께 저녁식사를 한다(재능기부) 3) 기업에서는 개인 모금액과 동일한 금액을 1:1 매칭한다. 4) 기업이 낸 기부금의 20%에 해당하는 금액을 위제너레이션이 마케팅 비용으로 받는다.당시 유명했던 해외의 자선 크라우드펀딩 사이트가 모금 기간을 2주로 두고 있었고, 개인의 기부금에서 수수료를 받는 것은 국내에서 거부감이 클 것이라 생각했기 때문에 위와 같이 계획한 것이다.그러나 결국 부딪혀보니 현실성이 없었다. 1) 유명인이 안 해줌: 니네가 뭔데? (기존 레퍼런스가 없는 문제가 가장 큼)2) 기업이 안 해줌: 니네가 뭔데? + 작은 규모의 기업은 사회공헌할 여력이 없음 + 큰 기업은 알아서 하거나, 하더라도 가볍고 빠른 시도가 아니라 1~2년 이상의 장기적인 파트너십을 원함3) 개인 기부자들이 기부를 안 해줌: 니네가 뭔데? 즉, 규모도 작고 전문성도 없는 팀에서 레퍼런스도 없이 자선단체와 유명인과 기업을 섭외하고 캠페인을 기획해 관련 내용을 만들고 마케팅을 준비해서 2주라는 시간 안에 캠페인 3개씩을 진행할수가 없었다이 와중에 디자이너분들은 회사를 떠났고,공동 대표 한 분은 개인 사정 상 잠깐 미국으로 출국하게 되었고, 다른 공동 대표 한 분은 적성에 맞지 않는 외부 활동으로 고통받다가 팀을 나갈 결심을 먹고 있었고, 나와 영인이는 다가오는 모금일 마감 이후를 어떻게 메꿀지 발등에 떨어진 불을 다른 발로 밟아가며, 유래없이 뜨거운 2주를 보내고 있었다.#라이비오 #경험공유 #인사이트 #스타트업 #스타트업창업
조회수 1410

React 공식 튜토리얼 한글 번역

<button type="button">메뉴</button>* 오역 및 오탈자가 있을 수 있습니다. 발견하시면 댓글로 제보해주세요!** 브런치 에디터의 한계로 마크다운 적용이 되지 않아 가독성이 떨어지고 복사 기능이 지원되지 않습니다. 이왕이면 이곳에서 보시기를 권장합니다. >> 가독성 좋은 문서로 보기React 공식 튜토리얼 바로가기시작하기 전에무엇을 구현할 것인가대화형 틱택토 게임을 구현하려고 합니다.원한다면 최종 결과물을 여기에서 확인할 수 있습니다. 아직 코드가 이해되지 않거나 문법이 낯설어도 걱정하지 마세요. 튜토리얼에서 차근차근 틱택토 게임을 구현하는 방법을 배울테니까요.게임을 플레이해보세요. 이동 리스트에 있는 버튼을 클릭하여 클릭한 때로 돌아가고, 그 때로 돌아간 후 보드가 어떻게 보이는지 확인할 수 있습니다.게임에 익숙해지셨다면 탭을 닫으세요. 다음 섹션에서 간단한 템플릿을 가지고 시작할 것입니다.사전 준비HTML과 JavaScript에 익숙할 것으로 생각합니다. 하지만 HTML과 JavaScript를 사용해본 적이 없더라도 튜토리얼을 따를 수 있어야 합니다.JavaScript를 다시 봐야한다면 이 가이드를 추천합니다. 튜토리얼에서 JavaScript의 최신 버전인 ES6의 몇 가지 특징들인 화살표 함수, 클래스, let, const를 사용할 것입니다. Babel REPL을 사용하여 ES6 코드가 어떻게 컴파일되는지 확인해볼 수 있습니다.튜토리얼을 공부하는 방법튜토리얼을 공부하기 위한 두 가지 방법이 있습니다. 브라우저에서 코드를 작성하거나 컴퓨터의 로컬 개발 환경을 설치할 수 있습니다. 편한 방법을 선택하여 공부하시면 됩니다.브라우저에서 코드를 작성하기 원한다면가장 빨리 시작할 수 있습니다!새로운 탭에서 시작 코드를 여세요. 빈 틱택토 필드를 볼 수 있습니다. 튜토리얼에서는 이 코드를 수정하여 진행합니다.다음 섹션인 로컬 개발 환경 설정을 스킵할 수 있습니다. 바로 개요 섹션으로 넘어가세요.사용하던 에디터에서 코드를 작성하기 원한다면다른 방법으로 사용하는 컴퓨터에 프로젝트를 설치할 수 있습니다.이 방법은 필수가 아닌 선택 사항입니다!더 많은 준비 작업이 필요하지만 에디터의 편리함을 누리며 공부할 수 있습니다.만약 이 방법으로 공부하기를 원한다면 필요한 단계들이 있습니다.  1. 설치된 Node.js가 최신 버전인지 확인해보세요.2. 새로운 프로젝트를 생성하기 위해 설치 방법을 따르세요.$ npm install -g create-react-app$ create-react-app my-app3. 새 프로젝트의 src/ 폴더에 있는 모든 파일들을 삭제해주세요. (폴더 안의 내용만 삭제하되 폴더는 삭제하지 마세요)$ cd my-app$ rm -f src/*4. 이 CSS 코드를 src/ 폴더에 index.css 파일로 추가해주세요.5. 이 JS 코드를 src/ 폴더에 index.js 파일로 추가해주세요.6. src/ 폴더에 있는 index.js의 최상단에 아래 세 줄을 추가해주세요.import React from 'react';import ReactDOM from 'react-dom';import './index.css';이제 프로젝트 폴더에서 npm start 명령어를 실행하고 브라우저에서 http://localhost:3000 를 여세요. 빈 틱택토 필드를 볼 수 있습니다.에디터에서 문법 하이라이팅 설정을 하고 싶다면 이 문서를 따르세요.도와주세요! 막히는 부분이 있어요!막히는 부분이 생겼다면 지원하는 커뮤니티를 확인해보세요. 특히 Reactiflux chat은 빠르게 도움을 받을 수 있는 좋은 방법입니다. 어떤 커뮤니티에서도 필요한 대답을 듣지 못했다면 이슈를 제출하세요. 우리가 도와드립니다.다 끝났으면 시작해봅시다!개요React란 무엇인가요?React는 유저 인터페이스 구현을 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리입니다.React는 여러 종류의 컴포넌트들을 가지고 있지만 우리는 React.Component의 서브클래스를 사용하여 시작할 것입니다.  class ShoppingList extends React.Component {  render() {    return (            Shopping List for {this.props.name}                Instagram         WhatsApp         Oculus                );  }}// Example usage:XML과 비슷한 재밌는 태그들을 사용할 것입니다. 작성한 컴포넌트는 React에게 무엇을 랜더링하고 싶은지 알려줍니다. 그러면 React는 데이터가 변경될 때 올바른 컴포넌트들을 업데이트하고 랜더링합니다.여기에서 ShoppingList는 React 컴포넌트 클래스 혹은 React 컴포넌트 타입입니다. 하나의 컴포넌트는 props라 불리는 파라미터를 사용하고, render 메서드를 통해 표시할 뷰 계층 구조를 반환합니다.render 메서드는 랜더링하길 원하는 내용을 반환하면 React는 그 내용을 가져와 스크린에 랜더링합니다. 특히 render는 랜더링할 간단한 내용인 React 엘리먼트를 반환합니다. 대부분의 React 개발자들은 이 구조를 더 쉽게 작성할 수 있게 해주는 JSX라는 특별한 문법을 사용합니다.라 쓰면 빌드 시 React.createElement('div')로 변환됩니다. 위의 코드는 아래의 코드와 동일합니다.  return React.createElement('div', {className: 'shopping-list'},  React.createElement('h1', /* ... h1 children ... */),  React.createElement('ul', /* ... ul children ... */));전체 코드는 여기에서 볼 수 있습니다.createdElement()에 대해 더 많은 내용이 궁금하다면 API reference 에 자세한 설명이 있습니다. 튜토리얼에서는 createdElement()를 직접적으로 사용하지 않습니다. 대신 JSX를 사용할 것입니다.JSX에서는 중괄호 안에 JavaScript 문법을 사용할 수 있습니다. 각 React 엘리먼트는 변수에 저장하거나 프로그램에 여기저기에 전달할 수 있는 실제 JavaScript 객체입니다.ShoppingList 컴포넌트는 내장된 DOM 컴포넌트만 랜더링하지만  코드를 작성하여 커스텀 React 컴포넌트를 쉽게 구성할 수 있습니다. 각 컴포넌트는 캡슐화되어 독립적으로 동작할 수 있습니다. 이때문에 간단한 컴포넌트들로 복잡한 UI를 구현할 수 있습니다.시작하기시작 코드를 가지고 시작해봅시다.이 코드는 우리가 구현할 틱택토 게임의 틀을 가지고 있습니다. 필요한 스타일들을 준비해두었기 때문에 JavaScript만 신경쓰면 됩니다.세 가지 컴포넌트로 구성되어 있습니다.- Square- Board- GameSquare 컴포넌트는 하나의 <button>을 랜더링합니다. Board 컴포넌트는 9개의 사각형을 랜더링합니다. Game 컴포넌트는 나중에 우리가 채워 넣어야 할 공백이 있는 하나의 보드를 랜더링합니다. 지금 이 컴포넌트들은 아무런 동작도 하지 않습니다.props를 통해 데이터 전달하기본격적으로 시작하기 위해 Board 컴포넌트에서 Square 컴포넌트로 데이터를 전달해봅시다.Board의 renderSquare 메서드에서 Square 컴포넌트 prop에 value 값을 전달하도록 코드를 변경해주세요.  class Board extends React.Component {  renderSquare(i) {    return ;  }value 값을 보여주기 위해 Square 컴포넌트의 render 메서드 안의 코드 {/* TODO */}를 {this.props.value}로 변경해주세요.  class Square extends React.Component {  render() {    return (      <button className="square">        {this.props.value}      </button>    );  }}변경 전:변경 후: 랜더링된 결과에서는 각 사각형 안에 숫자가 위치합니다.지금까지의 코드는 이곳에서 볼 수 있습니다.대화형 컴포넌트클릭 시 "X"로 채워지는 Square 컴포넌트를 만들어봅시다. Square의 render() 함수에서 반환된 버튼 태그를 다음과 같이 변경해주세요.  class Square extends React.Component {  render() {    return (      <button className="square" onClick={() => alert('click')}>        {this.props.value}      </button>    );  }}이제 사각형을 클릭하면 브라우저에서 알럿창이 뜨는걸 확인할 수 있습니다.새로운 JavaScript 문법인 화살표 함수를 사용하였습니다. onClick prop에 함수를 전달하였습니다. onClick={alert('click')} 코드를 작성하고 버튼을 클릭하면 알럿창 대신 경고가 뜨게됩니다.React 컴포넌트는 생성자에서 this.state를 설정하여 상태를 가질 수 있습니다. 상태는 각 컴포넌트마다 가지고 있습니다. 사각형의 현재 value 값을 상태에 저장하고 클릭할 때 바뀌도록 만들어봅시다.먼저 상태를 초기화하기 위해 클래스에 생성자를 추가해주세요.  class Square extends React.Component {  constructor(props) {    super(props);    this.state = {      value: null,    };  }  render() {    return (      <button className="square" onClick={() => alert('click')}>        {this.props.value}      </button>    );  }}JavaScript 클래스에서 서브클래스의 생성자를 정의할 때 super(); 메서드를 명시적으로 호출해줘야 합니다.Square의 render 메서드에서 현재 상태의 value 값을 표시하고 클릭할 때 바뀌도록 수정해주세요.- <button> 태그 안의 this.props.value 를 this.state.value로 변경해주세요.- () => alert() 이벤트 핸들러를 () => this.setState({value: 'X'})로 변경해주세요.<button> 태그는 다음과 같습니다.  class Square extends React.Component {  constructor(props) {    super(props);    this.state = {      value: null,    };  }  render() {    return (      <button className="square" onClick={() => this.setState({value: 'X'})}>        {this.state.value}      </button>    );  }}this.setState가 호출될 때마다 컴포넌트가 업데이트되므로 업데이트된 상태가 전달되어 React가 이를 병합하고 하위 컴포넌트와 함께 다시 랜더링합니다. 컴포넌트가 랜더링될 때 this.state.value는 'X'가 되어 그리드 안에 X가 보이게 됩니다.이제 사각형을 클릭하면 그 안에 X가 표시됩니다.지금까지의 코드는 이곳에서 볼 수 있습니다.개발자 도구크롬과 파이어폭스의 React 개발자 도구 확장 프로그램은 React 컴포넌트 트리를 브라우저의 개발자 도구 안에서 검사할 수 있게 해줍니다.트리 안의 컴포넌트들의 props와 상태를 검사할 수 있습니다.설치 후 페이지에서 검사하길 원하는 컴포넌트를 오른쪽 클릭하고 "Inspect"를 클릭하여 개발자 도구를 열면 오른쪽 마지막 탭에 React 탭이 보입니다.CodePen을 사용하여 이 확장 프로그램을 동작시키고 싶다면 추가적으로 필요한 작업들이 있습니다.1. 로그인 혹은 회원가입을 하고 이메일을 인증받으세요.2. "Fork" 버튼을 클릭하세요.3. "Change View"를 클릭하고 "Debug mode"를 선택하세요.4. 새롭게 열린 탭에서 React 탭이 있는 개발자 도구를 볼 수 있습니다.상태 들어올리기이제 틱택토 게임을 위한 기본 블록들이 있습니다. 하지만 아직 각 Square 컴포넌트 안에 상태들이 캡슐화되어 있습니다. 더 원활하게 동작하는 게임을 만들기 위해 한 플레이어가 게임에서 이겼는지를 확인하고 사각형 안에 X와 O를 번갈아 표시해야 합니다. 누가 게임에서 이겼는지 확인하기 위해 Square 컴포넌트들을 쪼개지 않고 한 장소에서 9개의 사각형의 value 값을 모두 가지고 있어야 합니다.Board가 각 Square의 현재 상태가 무엇인지만 확인해야 한다고 생각할 수도 있습니다. 이 방법은 기술적으로 React에서 가능하기는 하나 코드를 이해하기 어렵고 불안정하고 리팩토링하기 힘들게 만듭니다.각 Square에 상태를 저장하는 대신에 Board 컴포넌트에 이 상태를 저장하는 것이 가장 좋은 방법입니다. 이 Board 컴포넌트는 이전에 각 사각형에 인덱스를 표시한 방법과 동일한 방법으로 무엇을 표시할지 각 Square에게 알릴 수 있습니다.여러 하위 컴포넌트로부터 데이터를 모으거나 두 개의 하위 컴포넌트들이 서로 통신하기를 원한다면 상위 컴포넌트 안으로 상태를 이동시키세요. 상위 컴포넌트는 props를 통해 하위 컴포넌트로 상태를 전달해줄 수 있습니다. 그러면 하위 컴포넌트들은 항상 하위 컴포넌트나 상위 컴포넌트와 동기할 수 있습니다.이와 같이 상태를 상위 컴포넌트로 들어올리는 것은 React 컴포넌트들을 리팩토링할 때 가장 많이 사용하는 방법입니다. 이 기회를 통해 연습해봅시다. Board에 생성자를 추가하고 9개의 사각형과 일치하는 9개의 null을 가진 배열을 포함한 상태로 초기화하세요.  class Board extends React.Component {  constructor(props) {    super(props);    this.state = {      squares: Array(9).fill(null),    };  }  renderSquare(i) {    return ;  }  render() {    const status = 'Next player: X';    return (             {status}                 {this.renderSquare(0)}         {this.renderSquare(1)}          {this.renderSquare(2)}                        {this.renderSquare(3)}          {this.renderSquare(4)}          {this.renderSquare(5)}                        {this.renderSquare(6)}          {this.renderSquare(7)}          {this.renderSquare(8)}                );  }}나중에 이것을 다음과 같이 생긴 보드로 채울 예정입니다.  [  'O', null, 'X',  'X', 'X', 'O',  'O', null, null,]현재 Board의 renderSquare 메서드는 다음과 같습니다.    renderSquare(i) {    return ;  }Square에 value prop를 전달하도록 수정하세요.    renderSquare(i) {    return ;  }지금까지의 코드는 이곳에서 볼 수 있습니다.이제 우리는 사각형이 클릭되면 발생할 변경 사항을 구현해야 합니다. Board 컴포넌트는 어떤 사각형이 채워졌는지 저장하고 있습니다. 그렇기 때문에 Square가 Board가 가지고 있는 상태로 업데이트할 방법이 필요합니다. 사각형의 컴포넌트 상태가 각자 정의되고 있기 때문에 Board가 Square의 상태를 가지고올 수 없습니다.보통의 패턴은 사각형이 클릭될 때 호출되는 함수를 Board로부터 Square에 전달하는 것입니다. Board 안의 renderSquare를 다시 변경해봅시다.    renderSquare(i) {    return (              value={this.state.squares[i]}        onClick={() => this.handleClick(i)}      />    );  }가독성을 위해 리턴 안의 요소들을 여러 줄로 나누고, 괄호를 추가하여 JavaScript가 세미콜론 없이 코드를 마무리하도록 했습니다.Board에서 Square로 value와 onClick 두 개의 props를 전달합니다. onClick Square의 rennder에 있는 this.state.value 를 this.props.value로 변경하세요.- Square의 rennder에 있는 this.state.value 를 this.props.value로 변경하세요.- Square의 rennder에 있는 this.setState() 를 this.props.onClick()로 변경하세요.- 더이상 각 Square가 상태를 가지지 않도록 Square에 정의한 constructor를 삭제하세요.모든 변경 사항을 구현한 Square 컴포넌트는 다음과 같습니다.  class Square extends React.Component {  render() {    return (      <button className="square" onClick={() => this.props.onClick()}>        {this.props.value}      </button>    );  }}이제 사각형이 클릭될 때 Board로부터 전달되는 onClick 함수를 호출합니다. 어떤 일이 일어나는지 되짚어봅시다.1. 내장된 DOM <button> 컴포넌트의 onClick prop는 React에게 클릭 이벤트 리스너를 설정하라고 알립니다.2. 버튼이 클릭될 때 React는 Square의 render() 메서드 안에 정의된 onClick 이벤트 핸들러를 호출합니다.3. 이 이벤트 핸들러는 this.props.onClick()을 호출합니다. Square의 props는 Board에서 명시한 것입니다.4. Board는 onClick={() => this.handleClick(i)}을 Square에 전달하고, 호출될 때 Board의 this.handleClick(i)가 동작합니다.5. Board에 있는 handleClick() 메서드는 아직 정의되지 않았으므로 코드는 오류가 발생합니다DOM <button> 엘리멘트의 onClick 속성이 React와는 다른 의미를 가집니다. Square의 onClick prop나 Board의 handleClick 메서드와는 다릅니다. React 애플리케이션에서는 속성에 on* 이름을 사용하고 핸들러 메서드에 handle* 을 사용하여 처리하는 것이 일반적입니다.사각형을 클릭해봅시다. handleClick을 아직 정의하지 않았으로 에러가 발생합니다. Board 클래스에handleClick 메서드를 추가해봅시다.  class Board extends React.Component {  constructor(props) {    super(props);    this.state = {      squares: Array(9).fill(null),    };  }  handleClick(i) {    const squares = this.state.squares.slice();    squares[i] = 'X';    this.setState({squares: squares});  }  renderSquare(i) {    return (              value={this.state.squares[i]}        onClick={() => this.handleClick(i)}      />    );  }  render() {    const status = 'Next player: X';    return (             {status}                 {this.renderSquare(0)}          {this.renderSquare(1)}          {this.renderSquare(2)}                        {this.renderSquare(3)}          {this.renderSquare(4)}          {this.renderSquare(5)}                        {this.renderSquare(6)}          {this.renderSquare(7)}          {this.renderSquare(8)}                );  }}지금까지의 코드는 이곳에서 볼 수 있습니다.이미 있는 배열을 수정하는 대신 squares 배열을 복사하기 위해 .slick()를 호출합니다. 왜 immutability이 중요한지 알고 싶다면 이 섹션으로 이동해주세요.이제 사각형을 클릭하여 다시 사각형을 채울 수 있어야 하지만 상태가 각 Square가 아닌 Board 컴포넌트에 저장되어 있어 게임을 계속 구현해나가야 합니다. Board의 상태가 변경될 때마다 Square 컴포넌트들은 자동으로 다시 랜더링됩니다.Square은 더 이상 각 상태를 유지하지 않습니다. 이들은 상위 Board 컴포넌트로부터 데이터를 전달받고, 클릭될 때 알립니다. 우리는 이 제어된 컴포넌트 같은 컴포넌트들을 호출합니다.왜 immutability가 중요할까전의 예제 코드에서 이미 존재하는 배열을 수정하지 않고 변경 사항을 반영하기 위해 squares 배열을 .slice()연산자를 사용하여 복사하였습니다. 이는 무엇을 의미하며 왜 이 컨셉이 중요할까요.mutation을 사용한 데이터 변경  var player = {score: 1, name: 'Jeff'};player.score = 2;// Now player is {score: 2, name: 'Jeff'}mutation을 사용하지 않은 데이터 변경  var player = {score: 1, name: 'Jeff'};var newPlayer = Object.assign({}, player, {score: 2});// Now player is unchanged, but newPlayer is {score: 2, name: 'Jeff'}// Or if you are using object spread syntax proposal, you can write:// var newPlayer = {...player, score: 2};mutation을 사용하지 않더라도(기본 데이터를 변경하여도) 결과적으로는 다를게 없습니다. 하지만 컴포넌트와 전체 애플리케이션의 성능을 향상시키는 장점이 있습니다.쉽게 Undo/Redo와 시간 여행하기immutability는 이 복잡한 기능들을 훨씬 더 쉽게 구현할 수 있게 해줍니다. 예를 들어 이 튜토리얼에서 우리는 게임의 다른 단계들 사이에 시간 여행을 구현할 것입니다. 데이터 변경을 피하면 우리가 이전 버전의 데이터를 계속 참조할 수 있게 해주고 원할 때 변경할 수 있게 해줍니다.변경 사항 트래킹하기변경되는 객체가 변경 사항이 있는지 아는 방법은 변경 사항이 객체로 만들어지기 때문에 복잡합니다. 그러면 이전 버전을 복사하기 위해 전체의 객체 트리를 현재 버전과 비교하고 각 변수와 값들을 비교해야 합니다. 이 과정은 갈수록 복잡해집니다.immutable 객체가 변경 사항이 있는지 아는 방법은 쉬워집니다. 만약 참조되고 있는 객체가 이전과 다르다면 이 객체는 변경된 것입니다. 이게 끝입니다.React에서 언제 다시 랜더링할지 결정하기React에서 immutability의 가장 큰 장점은 간단한 순수 컴포넌트들이 다시 랜더링될 때를 결정하기 쉽다는 점입니다.shouldComponentUpdate()에 대해 더 배우고 싶고 어떻게 순수 컴포넌트들을 성능 최적화 할 수 있는지 알고 싶다면 이 글을 보세요.함수 컴포넌트우리는 생성자를 지웠습니다. 사실 React는 render 메서드만으로 구성된 Square와 같은 컴포넌트 타입을 위해 함수 컴포넌트라 불리는 간단한 문법을 지원합니다. React.Component를 확장한 클래스를 정의하는 것보다 간단하게 props를 가져오고 랜더링 해야할 것을 반환하는 함수를 작성하는 것이 좋습니다.다음과 같은 함수를 사용해 Square 클래스를 변경하세요.  function Square(props) {  return (    <button className="square" onClick={props.onClick}>      {props.value}    </button>  );}여기서는 this.props를 둘 다 props로 바꿔야 합니다. 애플리케이션에 있는 여러 컴포넌트들은 함수 컴포넌트로 구현할 수 있습니다. 함수 컴포넌트는 더 쉽게 작성할 수 있고 React가 더 효율적으로 최적화할 수 있습니다.코드를 깔끔하게 만들면서 onClick={() => props.onClick()}을 onClick={props.onClick}으로 바꿨습니다. 함수를 전달하는 것은 이 코드만으로 분합니다. onClick={props.onClick()}는props.onClick을 호출하기 때문에 동작하지 않습니다.지금까지의 코드는 이곳에서 보실 수 있습니다.변화 가져오기지금 우리의 게임의 단점은 오로지 X만 플레이할 수 있다는 점입니다. 고쳐봅시다.기본적으로 첫 이동을 'X'가 되도록 설정해봅시다. Board 생성자에서 초기 상태를 수정해주세요.  class Board extends React.Component {  constructor(props) {    super(props);    this.state = {      squares: Array(9).fill(null),      xIsNext: true,    };  }이동할 때마다 xIsNext의 불린 값은 바뀌면서 상태에 저장되어야 합니다. Board의 handleClick 함수를xIsNext 값이 바뀔 수 있도록 수정해봅시다.    handleClick(i) {    const squares = this.state.squares.slice();    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      squares: squares,      xIsNext: !this.state.xIsNext,    });  }이제 X와 O가 순서대로 번갈아 나타납니다. 다음에 무엇이 표시될 때 보여주기 위해 Board의 render에서 "status" 텍스트를 바꿔봅시다.    render() {    const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    return (      // the rest has not changed변경 사항을 적용한 Board 컴포넌트는 다음과 같습니다.  class Board extends React.Component {  constructor(props) {    super(props);    this.state = {      squares: Array(9).fill(null),      xIsNext: true,    };  }  handleClick(i) {    const squares = this.state.squares.slice();    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      squares: squares,      xIsNext: !this.state.xIsNext,    });  }  renderSquare(i) {    return (              value={this.state.squares[i]}        onClick={() => this.handleClick(i)}      />    );  }  render() {    const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    return (             {status}                 {this.renderSquare(0)}          {this.renderSquare(1)}          {this.renderSquare(2)}                        {this.renderSquare(3)}          {this.renderSquare(4)}          {this.renderSquare(5)}                        {this.renderSquare(6)}          {this.renderSquare(7)}          {this.renderSquare(8)}                );  }}지금까지의 코드는 이곳에서 볼 수 있습니다.승자 알려주기언제 게임에서 이기는지 표시해봅시다. 파일 맨 하단에 헬퍼 함수를 추가해주세요.  function calculateWinner(squares) {  const lines = [    [0, 1, 2],    [3, 4, 5],    [6, 7, 8],    [0, 3, 6],    [1, 4, 7],    [2, 5, 8],    [0, 4, 8],    [2, 4, 6],  ];  for (let i = 0; i < lines>    const [a, b, c] = lines[i];    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {      return squares[a];    }  }  return null;}Board의 render 함수에서 누가 게임에서 이겼는지 확인할 수 있도록 호출할 수 있습니다. 또 누군가 이겼을 떄 "Winner: [X/O]" 상태 텍스트를 표시할 수 있습니다.Board의 render에서 status를 선언을 수정해주세요.    render() {    const winner = calculateWinner(this.state.squares);    let status;    if (winner) {      status = 'Winner: ' + winner;    } else {      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    }    return (      // the rest has not changedBoard에서 handleClick을 일찍 반환하여 이미 누군가 이긴 게임에서 클릭하거나 이미 칠해진 사각형을 클릭하는 경우 무시하도록 변경할 수 있습니다.축하합니다! 틱택토 게임을 완성하셨습니다! 이제 React의 기초를 알았습니다. 여기서 진짜 승자는 여러분입니다.지금까지의 코드는 이곳에서 볼 수 있습니다.히스토리 저장하기보드의 이전 상태로 되돌려 이전 상태가 표시되도록 만들어봅시다. 이동이 있을때마다 새 squares 배열을 만들었습니다. 덕분에 이전 상태의 보드를 쉽게 저장할 수 있습니다.상태에 이와 같은 객체를 저장해봅시다.  history = [  {    squares: [      null, null, null,      null, null, null,      null, null, null,    ]  },  {    squares: [      null, null, null,      null, 'X', null,      null, null, null,    ]  },  // ...]우리는 이동 리스트를 표시하여 응답할 수 있는 더 수준 높은 Game 컴포넌트를 만들고 싶습니다. 그래서 Square 상태를 Board로 들어올린 것처럼 Board의 상태를 Game으로 들어올려 최 상위 레벨에서 필요한 모든 정보를 저장해봅시다.먼저 생성자를 추가해 Game의 초기 상태를 설정해주세요.  class Game extends React.Component {  constructor(props) {    super(props);    this.state = {      history: [{        squares: Array(9).fill(null),      }],      xIsNext: true,    };  }  render() {    return (                                              {/* status */}         {/* TODO */}                );  }}그 다음 Board를 수정하여 props를 거쳐 squares를 가져오고 이전에 Square에서 했던 것처럼 Game에서 지정한 onClick prop를 만들어줍시다. 각 사각형의 위치를 클릭 핸들러로 전달하여 어떤 사각형이 클릭되었는지 알 수 있습니다. 필요한 변경 사항은 다음과 같습니다.- Board의 constructor를 삭제하세요.- Board의 renderSquare에 있는 this.state.squares[i]를 this.props.sqaures[i]로 대체하세요.- Board의 renderSquare에 있는 this.handleClick(i)를 this.props.onClick(i)로 대체하세요.변경 사항을 반영한 Board 컴포넌트는 다음과 같습니다.  class Board extends React.Component {  handleClick(i) {    const squares = this.state.squares.slice();    if (calculateWinner(squares) || squares[i]) {      return;    }    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      squares: squares,      xIsNext: !this.state.xIsNext,    });  }  renderSquare(i) {    return (              value={this.props.squares[i]}        onClick={() => this.props.onClick(i)}      />    );  }  render() {    const winner = calculateWinner(this.state.squares);    let status;    if (winner) {      status = 'Winner: ' + winner;    } else {      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    }    return (             {status}                 {this.renderSquare(0)}          {this.renderSquare(1)}          {this.renderSquare(2)}                        {this.renderSquare(3)}          {this.renderSquare(4)}          {this.renderSquare(5)}                        {this.renderSquare(6)}          {this.renderSquare(7)}          {this.renderSquare(8)}                );  }}Game의 render는 히스토리 전체를 보고 게임 상태를 계산하여 가져올 수 있어야 합니다.  render() {    const history = this.state.history;    const current = history[history.length - 1];    const winner = calculateWinner(current.squares);    let status;    if (winner) {      status = 'Winner: ' + winner;    } else {      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    }    return (                                  squares={current.squares}            onClick={(i) => this.handleClick(i)}          />                        {status}         {/* TODO */}                );  }Game에 상태를 랜더링하고 있기 때문에{status}를 지우고 Board의 render 함수로부터 상태를 계산하는 코드를 지울 수 있습니다.  render() {    return (                      {this.renderSquare(0)}          {this.renderSquare(1)}          {this.renderSquare(2)}                        {this.renderSquare(3)}          {this.renderSquare(4)}          {this.renderSquare(5)}                        {this.renderSquare(6)}          {this.renderSquare(7)}          {this.renderSquare(8)}                );  }그 다음 Board에서 Game으로 handleClick 메서드를 옮겨야 합니다. Board 클래스에서 잘라내기를 하고 Game 클래스로 붙여넣을 수 있습니다.Game 상태는 다르기 때문에 수정해야 할 것이 조금 있습니다. Game의 handleClick은 히스토리 항목을 연결하여 새로운 배열을 만들어 스택에 푸시해야 합니다.  handleClick(i) {    const history = this.state.history;    const current = history[history.length - 1];    const squares = current.squares.slice();    if (calculateWinner(squares) || squares[i]) {      return;    }    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      history: history.concat([{        squares: squares,      }]),      xIsNext: !this.state.xIsNext,    });  }여기에서 Board는 renderSquare와 render만 필요합니다. 상태 초기화와 클릭 핸들러는 둘 다 Game에서 동작합니다.지금까지의 코드는 이곳에서 보실 수 있습니다.이동 표시하기지금까지 게임에서 진행된 이동을 표시해봅시다. 이전에 React 컴포넌트가 클래스로 JS 객체이고 그 덕에 데이터를 저장하고 전달할 수 있다고 배웠습니다. React에서 여러 아이템들을 랜더링하기 위해 React 요소의 배열을 전달했습니다. 배열을 빌드하는 가장 흔한 방법은 데이터 배열에서 map을 이용하는 것입니다. Game의 render 메서드에서 해봅시다.    render() {    const history = this.state.history;    const current = history[history.length - 1];    const winner = calculateWinner(current.squares);    const moves = history.map((step, move) => {      const desc = move ?        'Go to move #' + move :        'Go to game start';      return (                 <button onClick={() => this.jumpTo(move)}>{desc}</button>             );    });    let status;    if (winner) {      status = 'Winner: ' + winner;    } else {      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');    }    return (                                 squares={current.squares}            onClick={(i) => this.handleClick(i)}          />                        {status}         {moves}                );  }지금까지의 코드는 이곳에서 볼 수 있습니다.히스토리의 각 단계에서 <button>이 있는 리스트 아이템을 만들었습니다. 이 리스트 아이템은 우리가 곧 구현할 클릭 핸들러를 가지고 있습니다. 코드에서 다음과 같은 경고 메시지와 함께 게임에서 만들어지는 이동 목록을 볼 수 있습니다.Warning: Each child in an array or iterator should have a unique “key” prop. Check the render method of “Game”.경고: 배열이나 이터레이터에 있는 각 자식은 유니크 "key" prop을 가져야한다. "Game"의 render 메서드를 확인해보세요.이 경고의 의미가 무엇인지 얘기해봅시다.Keys아이템 리스트를 랜더링할때 React는 항상 리스트에 있는 각 아이템에 대한 정보를 저장합니다. 만약 상태를 가진 컴포넌트를 랜더링한다면 컴포넌트가 어떻게 실행되는지와 관계없이 상태는 저장 되어야 하고 React는 네이티브 뷰의 뒤에 참고할 것을 저장한다.리스트를 업데이트할 때 React는 무엇을 바꿀지 결정해야 합니다. 리스트에 아이템들을 추가하고, 지우고, 재배열하고, 수정할 수 있습니다.이 코드가 아래의 코드로 변경된다고 상상해봅시다.  Alexa: 7 tasks leftBen: 5 tasks leftBen: 9 tasks leftClaudia: 8 tasks leftAlexa: 5 tasks left사람의 눈에는 Alexa와 Ben의 자리가 바뀌고 Claudia가 추가된 것처럼 보인다. 하지만 React는 단순한 컴퓨터 프로그램이므로 여러분의 의도를 알지 못합니다. React는 리스트의 각 요소에서 key 속성을 지정해달라고 요청합니다. 문자열은 형제로부터 각 컴포넌트들을 구분합니다. 이 경우에 alexa, ben, claudia는 구분할 수 있는 키가 됩니다. 만약 아이템들이 데이터베이스의 객체와 일치시켜야 한다면 데이터베이스 ID을 사용하세요.{user.name}: {user.taskCount} tasks leftkey는 React에서 제공되는 특별한 속성입니다(ref에서 더 확장된 기능). 엘리먼트가 만들어질때 React는 key 속성을 가져오고 반환된 엘리먼트에 직접적으로 key를 저장합니다. key가 props의 한 부분으로 보일지라도 이것은 this.props.key로 참조할 수 없습니다. React는 어떤 하위 엘리먼트가 수정될지 결정하는 동안 알아서 key를 사용합니다. 컴포넌트가 자신의 키를 알 수 있는 방법은 없습니다.리스트가 랜더링될 때 React는 새로운 버전의 각 엘리먼트를 가져오고 이전 리스트에서 매칭되는 키를 가진 것을 찾습니다. key가 세트에 추가될 때 컴포넌트는 만들어집니다. 키가 삭제될 때 컴포넌트는 소멸됩니다. 키들은 React가 각 요소를 구별할 수 있도록하여 다시 랜더링하는 것을 무시하고 상태를 유지할 수 있게 합니다. 만약 컴포넌트의 키를 바꾼다면 완전히 지운 후 새롭게 생성됩니다.동적으로 리스트를 빌드할 때마다 적당한 키를 할당할 것을 강력 추천합니다. 만약 적당한 키를 가지지 못한다면 이를 위해 데이터를 재구성하여야 할지도 모릅니다.특정한 키를 구분하지 못한다면 React는 경고를 주고 배열 인덱스를 키로 사용합니다. 이는 올바른 선택이 아닙니다. 만약 리스트에 있는 엘리먼트들을 정렬하거나 리스트에 있는 버튼을 통해 지우거나 추가하면 명시적으로 key={i}를 전달하는 방법을 사용한다면 경고를 표시하지는 않지만 동일한 문제를 발생시키므로 대부분의 경우에 추천하지 않습니다.컴포넌트의 키가 전부 다를 필요는 없지만 관련있는 형제들 사이에서는 유니크해야 합니다.시간 여행 실행하기이동 리스트를 위해 우리는 각 단계에서 유니크 ID를 가졌습니다. Game의 render 메서드에서 키는로 추가하면 경고는 표시되지 않습니다.    const moves = history.map((step, move) => {      const desc = move ?        'Go to move #' + move :        'Go to game start';      return (                 <button onClick={() => this.jumpTo(move)}>{desc}</button>             );    });지금까지의 코드는 이곳에서 보실 수 있습니다.아직 junmTo가 정의되지 않았기 때문에 이동 버튼을 클릭하면 에러가 발생합니다. 지금 표시된 단계가 무엇인지 알기 위해 Game 상태에 새로운 키를 추가해봅시다.먼저Game의 constructor에  stepNumber: 0를 추가해주세요.class Game extends React.Component {  constructor(props) {    super(props);    this.state = {      history: [{        squares: Array(9).fill(null),      }],      stepNumber: 0,      xIsNext: true,    };  }그 다음 각 상태를 업데이트하기 위해 Game의 jumpTo 메서드를 정의해봅시다. 이 메서드에서는 xIsNext를 업데이트하고, 이동의 인덱스가 짝수라면 xIsNext를 true로 설정합니다.Game 클래스에jumpTo 메서드를 추가해주세요.handleClick(i) {    // this method has not changed  }  jumpTo(step) {    this.setState({     stepNumber: step,      xIsNext: (step % 2) === 0,    });  }  render() {    // this method has not changed  }Game handleClick에 상태를 업데이트 하기위해 stempNumber:history.length를 추가하여 새로운 이동이 있을 때마다  stepNumber를 업데이트 합니다. 현재 보드의 상태를 읽을 때 handleClick이 stepNumber라고 보고 클릭하는 시간대로 상태를 되돌릴 수 있습니다.  handleClick(i) {    const history = this.state.history.slice(0, this.state.stepNumber + 1);    const current = history[history.length - 1];    const squares = current.squares.slice();    if (calculateWinner(squares) || squares[i]) {      return;    }    squares[i] = this.state.xIsNext ? 'X' : 'O';    this.setState({      history: history.concat([{        squares: squares      }]),      stepNumber: history.length,      xIsNext: !this.state.xIsNext,    });  }이제 히스토리의 각 단계를 알기 위해 Game의 render를 수정할 수 있습니다.  render() {    const history = this.state.history;    const current = history[this.state.stepNumber];    const winner = calculateWinner(current.squares);    // the rest has not changed지금까지의 코드는 이곳에서 보실 수 있습니다.이제 이동 버튼을 클릭하면 보드는 즉시 그때 표시된 게임으로 변경됩니다.마무리틱택토 게임을 플레이 해보세요.- 틱택토 게임을 플레이 해보세요.- 한 명의 플레이어가 게임에서 이길 때를 이를 알려줍니다.- 게임이 진행되는 동안 이동 기록이 저장됩니다.- 게임 보드의 에전 버전을 표시하기 위해 시간을 되돌릴 수 있습니다.잘 동작하네요! React가 어떻게 동작하는지 잘 아셨기를 바랍니다.최종 결과물은 여기에서 확인하세요.시간이 더 있거나 새로운 스킬들을 연습해보고 싶다면 해볼 수 있는 몇 가지 아이디어가 있습니다. 점점 더 어려운 순으로 배치해두었습니다.1. 움직임 리스트에서 (col, row) 형태에 각 움직임 위치를 표시하세요.2. 움직임 리스트의 선택된 아이템을 볼드처리하세요.3. 하드 코딩한 것들 대신 사각형을 두 개의 루프를 사용하여 Board를 다시 작성하세요.4. 오름차순 혹은 내림차순 뭐든지 움직임을 정렬하는 버튼을 추가해보세요.5. 누군가 이겼을 때 무엇 때문에 이겼는지 세 개의 사각형을 하이라이트하세요.튜토리얼이 진행되는 동안 우리는 엘리먼트, 컴포넌트, props, 상태를 포함한 React의 수많은 컨셉들을 다뤘습니다. 각 주제에 대한 깊은 설명을 원한다면 남은 문서를 확인하세요. 컴포넌트 정의에 대해 더 많이 배우고 싶다면 이 문서를 확인하세요.#트레바리 #개발자 #안드로이드 #앱개발 #Node.js #백엔드 #인사이트 #경험공유 #React #리액트 #리액트가이드 #한글 #번역 #문서번역
조회수 1450

비트윈 시스템 아키텍처 - VCNC Engineering Blog

VCNC는 커플을 위한 모바일 앱 비트윈을 서비스하고 있습니다. 비트윈은 사진, 메모, 채팅, 기념일 등 다양한 기능을 제공하며, 오픈 베타 테스트를 시작한 2011년 11월부터 현재까지 연인 간의 소통을 돕고 있습니다. 그동안 비트윈 시스템 아키텍처에는 많은 변화가 있었으며 다양한 결정을 하였습니다. 비트윈 아키텍처를 발전시키면서 배우게 된 여러 가지 노하우를 정리하여 공유해보고자 합니다. 그리고 저희가 앞으로 나아갈 방향을 소개하려 합니다.소프트웨어 스택Java: 비트윈 API서버는 Java로 작성되어 있습니다. 이는 처음 비트윈 서버를 만들기 시작할 때, 서버 개발자가 가장 빨리 개발해낼 수 있는 언어로 프로그래밍을 시작했기 때문입니다. 지금도 자바를 가장 잘 다루는 서버 개발자가 많으므로 여전히 유효한 선택입니다.Netty: 대부분의 API는 HTTP로 호출되며, 채팅은 모바일 네트워크상에서의 전송 속도를 위해 TCP상에서 프로토콜을 구현했습니다. 두 가지 모두 Netty를 통해 사용자 요청을 처리합니다. Netty를 선택한 것은 뛰어난 성능과 서비스 구현 시 Thrift 서비스를 통해 HTTP와 TCP 프로토콜을 한 번에 구현하기 쉽다는 점 때문이었습니다.Thrift: API서버의 모든 서비스는 Thrift 서비스로 구현됩니다. 따라서 TCP뿐만 아니라 HTTP 또한 Thrift 인터페이스를 사용합니다. HTTP를 굳이 Thrift서비스로 구현한 이유는, TCP로 메세징 전송 시 똑같은 서비스를 그대로 사용하기 위함이었습니다. 덕분에 빠른 채팅 구현 시, 이미 구현된 서비스들을 그대로 사용할 수 있었습니다. 또한, 채팅 패킷들은 패킷 경량화를 위해 snappy로 압축하여 송수신합니다. 모바일 네트워크상에서는 패킷이 작아질수록 속도 향상에 크게 도움이 됩니다.HBase: 비트윈의 대부분 트랜젝션은 채팅에서 일어납니다. 수많은 메시지 트랜젝션을 처리하기 위해 HBase를 선택했으며, 당시 서버 개발자가 가장 익숙한 데이터베이스가 HBase였습니다. 서비스 초기부터 확장성을 고려했어야 했는데, RDBMS에서 확장성에 대해 생각하는 것보다는 당장 익숙한 HBase를 선택하고 운영하면서 나오는 문제들은 차차 해결하였습니다.ZooKeeper: 커플들을 여러 서버에 밸런싱하고 이 정보를 여러 서버에서 공유하기 위해 ZooKeeper를 이용합니다. Netflix에서 공개한 오픈 소스인 Curator를 이용하여 접근합니다.AWS비트윈은 AWS의 Tokyo리전에서 운영되고 있습니다. 처음에는 네트워크 및 성능상의 이유로 국내 IDC를 고려하기도 했으나 개발자들이 IDC 운영 경험이 거의 없는 것과, IDC의 실질적인 TCO가 높다는 문제로 클라우드 서비스를 이용하기로 하였습니다. 당시 클라우드 서비스 중에 가장 안정적이라고 생각했던 AWS 를 사용하기로 결정했었고, 지금도 계속 사용하고 있습니다.EC2: 비트윈의 여러 부가적인 서비스를 위해 다양한 종류의 인스턴스를 사용 중이지만, 메인 서비스를 운용하기 위해서는 c1.xlarge와 m2.4xlarge 인스턴스를 여러 대 사용하고 있습니다.API 서버: HTTP 파싱이나 이미지 리시아징등의 연산이 이 서버에서 일어납니다. 이 연산들은 CPU 가 가장 중요한 리소스이기 때문에, c1.xlarge를 사용하기로 했습니다.Database 서버: HDFS 데이터 노드와 HBase 리전 서버들이 떠있습니다. 여러 번의 테스트를 통해 IO가 병목임을 확인하였고, 따라서 모든 데이터를 최대한 메모리에 올리는 것이 가장 저렴한 설정이라는 것을 확인하였습니다. 이런 이유 때문에 68.4GB의 메모리를 가진 m2.4xlarge를 Database 서버로 사용하고 있습니다.EBS: 처음에는 HBase상 데이터를 모두 EBS에 저장하였습니다. 하지만 일정 시간 동안 EBS의 Latency가 갑자기 증가하는 등의 불안정한 경우가 자주 발생하여 개선 방법이 필요했는데, 데이터를 ephemeral storage에만 저장하기에는 안정성이 확인되지 않은 상태였습니다. 위의 두 가지 문제를 동시에 해결하기 위해서 HDFS multiple-rack 설정을 통해서 두 개의 복제본은 ephemeral storage에 저장하고 다른 하나의 복제본은 PIOPS EBS에 저장되도록 구성하여 EBS의 문제점들로부터의 영향을 최소화하였습니다.S3: 사용자들이 올리는 사진들은 s3에 저장됩니다. 사진의 s3키는 추측이 불가능하도록 랜덤하게 만들어집니다. 어차피 하나의 사진은 두 명밖에 받아가지 않고 클라이언트 로컬에 캐싱되기 때문에 CloudFront를 사용하지는 않습니다.ELB: HTTP는 사용자 요청의 분산과 SSL적용을 위해 ELB를 사용합니다. TCP는 TLS를 위해 ELB를 사용합니다. SSL/TLS 부분은 모두 AWS의 ELB를 이용하는데, 이는 API서버의 SSL/TLS처리에 대한 부담을 덜어주기 위함입니다.CloudWatch: 각 통신사와 리전에서 비트윈 서버로의 네트워크 상태와 서버 내의 요청 처리 시간 등의 메트릭을 CloudWatch로 모니터링 하고 있습니다. 따라서 네트워크 상태나 서버에 문제가 생긴 경우, 이메일 등을 통해 즉각 알게 되어, 문제 상황에 바로 대응하고 있습니다. Netflix의 Servo를 이용하여 모니터링 됩니다.현재의 아키텍처처음 클로즈드 베타 테스트때에는 사용자 수가 정해져 있었기 때문에 하나의 인스턴스로 운영되었습니다. 하지만 처음부터 인스턴스 숫자를 늘리는 것만으로도 서비스 규모를 쉽게 확장할 수 있는 아키텍쳐를 만들기 위한 고민을 하였습니다. 오픈 베타 이후에는 발생하는 트래픽에 필요한 만큼 여러 대의 유연하게 서버를 운영하였고, 현재 채팅은 TCP 위에서 구현한 프로토콜을 이용하여 서비스하고 있습니다.HTTP 요청은 하나의 ELB를 통해 여러 서버로 분산됩니다. 일반적인 ELB+HTTP 아키텍처와 동일합니다.채팅은 TCP 연결을 맺게 되는데, 각 커플은 특정 API 서버로 샤딩되어 특정 커플에 대한 요청을 하나의 서버가 담당합니다. 비트윈에서는 커플이 샤딩의 단위가 됩니다.이를 통해, 채팅 대화 내용 입력 중인지 여부와 같이 굉장히 빈번하게 값이 바뀌는 정보를 인메모리 캐싱할 수 있게 됩니다. 이런 정보는 휘발성이고 매우 자주 바뀌는 정보이므로, HBase에 저장하는 것은 매우 비효율적입니다.Consistent Hashing을 이용하여 커플을 각 서버에 샤딩합니다. 이는 서버가 추가되거나 줄어들 때, 리밸런싱되면서 서버간 이동되는 커플들의 수를 최소화 하기 위함입니다.클라이언트는 샤딩 정보를 바탕으로 특정 서버로 TCP연결을 맺게 되는데, 이를 위해 각 서버에 ELB가 하나씩 붙습니다. 어떤 서버로 연결을 맺어야 할지는 HTTP 혹은 TCP 프로토콜을 통해 알게 됩니다.Consistent Hashing을 위한 정보는 ZooKeeper를 통해 여러 서버간 공유됩니다. 이를 통해 서버의 수가 늘어나거나 줄어들게 되는 경우, 각 서버는 자신이 담당해야 하는 샤딩에 대한 변경 정보에 대해 즉각 알게 됩니다.이런 아키텍처의 단점은 다음과 같습니다.클라이언트가 자신이 어떤 서버로 붙어야 하는지 알아야 하기 때문에 프로토콜 및 아키텍처 복잡성이 높습니다.서버가 늘어나는 경우, 순식간에 많은 사용자 연결이 맺어지게 됩니다. 따라서 새로 추가되는 ELB는 Warm-up이 필요로 하며 이 때문에 Auto-Scale이 쉽지 않습니다.HBase에 Write연산시, 여러 서버로 복제가 일어나기 때문에, HA을 위한 Multi-AZ 구성을 하기가 어렵습니다.한정된 자원으로 동작 가능한 서버를 빨리 만들어내기 위해 이처럼 디자인하였습니다.미래의 아키텍처현재 아키텍처에 단점을 보완하기 위한 해결 방법을 생각해보았습니다.Haeinsa는 HBase상에서 트렌젝션을 제공하기 위해 개발 중인 프로젝트입니다. 구현 완료 후, 기능 테스트를 통과하였고, 퍼포먼스 테스트를 진행하고 있습니다. HBase상에서 트렌젝션이 가능하게 되면, 좀 더 복잡한 기능들을 빠르게 개발할 수 있습니다. 서비스에 곧 적용될 예정입니다.Multitier Architecture를 통해 클라이언트와 서버 간에 프로토콜을 단순화시킬 수 있습니다. 이 부분은 개발 초기부터 생각하던 부분인데, 그동안 개발을 하지 못하고 있다가, 지금은 구현을 시작하고 있습니다. 커플은 특정 Application 서버에서 담당하게 되므로, 인메모리 캐싱이 가능하게 됩니다. 클라이언트는 무조건 하나의 ELB만 바라보고 요청을 보내게 되고, Presentation 서버가 사용자 요청을 올바른 Application 서버로 릴레이 하게 됩니다.Multitier Architecture를 도입하면, 더 이상 ELB Warm-up이 필요하지 않게 되므로, Auto-Scale이 가능하게 되며, 좀 더 쉬운 배포가 가능하게 됩니다.Rocky는 API 서버의 Auto-Failover와 커플에 대한 샤딩을 직접 처리하는 기능을 가진 프로젝트입니다. 현재 설계가 어느 정도 진행되어 개발 중에 있습니다. 알람이 왔을 때 서버 팀이 마음을 놓고 편히 잠을 잘 수 있는 역할을 합니다.기본적인 것은 위에서 언급한 구조와 동일하지만 몇 가지 기능이 설정을 추가하면 Multi-AZ 구성이 가능합니다.특정 커플에 대한 모든 정보는 하나의 HBase Row에 담기게 됩니다.HBase의 특정 리전에 문제가 생긴 경우, 일정 시간이 지나면 자동으로 복구되긴 하지만 잠시 동안 시스템 전체에 문제가 생기가 됩니다. 이에 대해 Pinterest에서 Clustering보다는 Sharding이 더 낫다는 글을 쓰기도 했습니다. 이에 대한 해결책은 다음과 같습니다.원래는 Consistent Hashing을 사용하여 커플들을 Application 서버에 샤딩하였습니다. 하지만 이제는 HBase에서 Row를 각 리전에 수동으로 할당하고, 같은 리전에 할당된 Row에 저장된 커플들은 같은 Application 서버에 할당하도록 합니다.이 경우에, 같은 커플들을 담당하는 Application 서버와 HBase 리전 서버는 물리적으로 같은 머신에 둡니다.이렇게 구성 하는 경우, 특정 HBase 리전이나 Application 서버에 대한 장애는 특정 샤드에 국한되게 됩니다. 이와 같이 하나의 머신에 APP과 DB를 같이 두는 구성은 구글에서도 사용하는 방법입니다.이와 같이 구성하는 경우, Multi-AZ 구성이 가능하게 됩니다.AWS에서 같은 리전에서 서로 다른 Zone간 통신은 대략 2~3ms 정도 걸린다고 합니다.Presentation의 경우, 비동기식으로 동작하기 때문에 다른 리전으로 요청을 보내도 부담이 되지 않습니다.HBase에서 Write가 일어나면 여러 복제본을 만들게 됩니다. 하나의 사용자 요청에 대해 Write가 여러번 일어나기 때문에 HBase연산의 경우에는 서로 다른 Zone간 Latency가 부담으로 작용됩니다. Haeinsa가 적용되면, 한 트렌젝션에 대해서 연산을 Batch로 전송하기 때문에 AZ간 Latency 부담이 적습니다.프리젠테이션다음은 2월에 있었던 AWS 유저 그룹 세미나에서 발표했던 자료 입니다. 비트윈 서버 아키텍처에 대해서 배포 방법을 중심으로 설명이 되어 있습니다. 비슷한 내용이 많이 있으니 살펴보시기 바랍니다.<iframe class="speakerdeck-iframe" frameborder="0" src="//speakerdeck.com/player/e4af60d05bb6013025f71231381b23b3?" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" style="border: 0px; background: padding-box rgba(0, 0, 0, 0.1); margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 750px; height: 563px;">

기업문화 엿볼 때, 더팀스

로그인

/