스토리 홈

인터뷰

피드

뉴스

조회수 1586

개발자 커리어 전환기1| 하드웨어 개발자, 소프트웨어 개발자가 되기로 마음먹다.

개발자는 도대체 어떻게 되는 거고 누가 되는 거야?코드스테이츠가 가장 많이 받아온 질문 중 하나입니다. 그래서 준비한 특급 연재! 개발자 커리어 전환기! 매주 Immersive를 수강하고 있는 수강생 한 분과 인터뷰해서 어떻게 개발을 시작하게 되었는지, 또 현재 무엇을 배우고 있는지에 대한 인터뷰를 포스팅하려고 합니다.아무것도 모르는 비전공자 출신 분들이(물론 전공자 분도 계십니다!) 개발자가 되어가는 과정을 생생하게 보실 수 있습니다. 그럼, 첫 번째 포스팅의 주인공 이야기를 시작하겠습니다.코드스테이츠 코딩 부트 캠프, Immersive 6기 과정이 시작되었습니다. 지난 5기 데모데이를 들뜬 마음으로 지켜보던 Pre-course 수강생들이 어느덧 새로운 Immersive 과정의 주인공이 되었는데요.오늘은 하드웨어 개발자 출신으로서 커리어 전환을 위해 코드스테이츠를 찾아온 6기의 전한길님을 만나봤습니다. Q) 한길님 반갑습니다. Precourse 수료를 축하드리며 간단한 자기소개 부탁드려요.- 안녕하세요! 전한길입니다.Q) 정말 간단하네요! 보통은 인터뷰어를 위해 좀 더 길게 합니다만...- 아... 전 대학에서 전자공학을 전공했고요. 어쩌다 보니 전자회로 설계일을 하게 되었어요. 원래는 소프트웨어 쪽에 관심이 있었는데 하드웨어 쪽으로 일을 했습니다. 사실, 당시에는 집 가까운 회사를 찾다보니..(웃음) 어쨌든! 커리어 전환을 위해 코드스테이츠에 오게 되었습니다.Q) 원래 이쪽에 관심이 있으셔서 소프트웨어 엔지니어로 커리어 전환을 하시는 건가요?- 자신만의 기술을 가질 수 있다는 점이 좋아서 소프트웨어 엔지니어를 하기로 결심했어요. 저는 회사생활을 6년 동안 했는데요. 하루하루 똑같은 업무와 일상이 지루하더라구요. 직급이 올라간다고 해서 더 나아질 거 같지도 않았고...사실 깊이 생각하는 성격이 아니에요. 무작정 회사를 나와서 고민했죠. 그러다가 "나만의 기술을 가질 수 있는 매력적인 직업을 갖자"는 저만의 원칙을 고수한 끝에 소프트웨어 엔지니어가 되기로 결심했어요.Q) 그러면 특별히 코드스테이츠를 선택하신 이유가 무엇인가요?- 처음에는 국비지원과정도 알아봤어요. 국비지원과정에서 공부를 할까 하다가 우연히 친구 소개로 코드스테이츠를 알게 되었죠.'자기 주도적 학습'이라는 단어에 끌렸어요. 전 코딩이 언어랑 비슷하다고 생각하거든요. 문법을 잘 안다고 영어를 잘하는 건 아니잖아요. 아는 것도 중요하지만 습관이 중요하다고 생각해요. 지름길을 가면서 스스로 코딩을 많이 해볼 수 있을 거 같아서 코드스테이츠를 선택하게 되었죠.코딩은, 아는 것도 중요하지만 습관이 중요하다고 생각해요Q) 이건 개인적으로 매우(?) 긴장되는 질문인데, 실제로 Pre-course는 어땠나요?- 코드스테이츠 학습 방식 자체가 강의식이 아니다 보니 생각한 대로 '자기 주도적 학습 위주'고, 특히 실제로 코딩을 많이 해봐서 좋았어요.그리고 위에서 얘기한 것처럼 저는 지름길이 필요했는데요. 방향을 잘 잡아주셔서 좋았어요. 단계별로 공부할 수 있는 내용이 잘 정리되어있더라구요. 시간을 효율적으로 쓸 수 있었습니다.Q) 담당자로서 매우 뿌듯한 답변이네요. :) 특히 어떤 프로젝트가 가장 기억에 남나요?- *twittler 를 만들었을 때가 가장 기뻤어요. 뭐라고 말로 표현하기는 어려운 감정인데, 실제로 눈에 보이는 걸 만들었을 때 성취감이 크더라구요. 그 성취감이 동기부여가 되어서 더 열심히 했던 거 같습니다.*twittler: 코드스테이츠 Pre-course과정에서 수행하는 프로젝트로, 트위터의 일부 기능을 구현한 프로그램한길님이 구현한 twittlerQ) 이제 막 Immersive 과정이 시작되었는데요. 과정에서는 어떤 걸 기대하나요?- 코드스테이츠의 체계적인 커리큘럼과... 웹 개발자로 취업하는 거??Q) 교과서 같은 답변이네요.^^ 3개월 뒤면 웹 개발자가 되어있을 거라고 믿습니다. 그렇다면 어떤 개발자가 되고 싶나요?- 기술을 잘 아는 개발자가 되고 싶어요. 개인적으로 호기심이 많아요. 블록체인부터 빅데이터까지.. 새로운 기술과 관련된 단어들을 들으면 호기심이 생기죠. 이렇게 호기심이 생겼을 때 그 기술에 대해 이해하고 실제로 기술을 잘 구현하는 개발자가 되고 싶어요. 소위 말하는 백엔드 쪽에 더 관심이 있는 거 같아요. 앞으로도 이 방향으로 나아가고 싶구요.음.. 그리고 하나만 덧붙이면, 제 생활도 잘 지킬 수 있는 개발자가 되고 싶어요. 일도 일이지만.!Q) 마지막으로 코드스테이츠를 다른 사람에게 추천한다면?소프트웨어 엔지니어가 되고 싶으신 분들에게 꼭 추천하고 싶어요. 독학을 해도 좋은 점이 있겠지만.. 체계적인 커리큘럼을 따라가면서 방향성 있는 공부를 하면 효율적일 거 같아요. 시간을 아낄 수 있죠. 커리어 전환을 고민하시는 분들에게도 적합하구요.그리고 무엇보다 같은 길을 가는 사람들과 커뮤니티를 형성하고 함께 공부할 수 있다는 점이 코드스테이츠의 가장 큰 장점이라고 생각합니다.
조회수 1625

프로세스 모델의 적합도 검사하기

프로세스 모델 도출은 프로세스 마이닝의 출발점이며, 매우 유용합니다. 원본 데이터로부터 프로세스 흐름 모델을 자동으로 구성하여 실제 프로세스를 알 수 있습니다. 이렇게 도출된 프로세스 모델과 이벤트 로그를 비교하는 것이 적합도 검사(Conformance checking)입니다. 적합도는 이전에 말씀드린 정확도(Precision)와는 다른 개념입니다. 정확도(Precision)는 Underfitting을 피하여 데이터를 정확하게 설명할 수 있으나 정확도가 높을수록 프로세스 모델이 대체로 복잡해지게 됩니다. 하지만 적합도가 높다고 하여 프로세스 모델이 복잡해지는 것은 아닙니다.적합도 검사의 기본 아이디어는 프로세스 모델 위에 이벤트 로그를 재생하는 것입니다.아래 예제 모델에 이벤트 로그 a → c → e → g를 재생하여 적합성 검사를 해보겠습니다.[그림 1] 프로세스 모델 예제먼저 a 이벤트를 수행하였습니다.[그림 2] a 이벤트 수행 후다음으로 c 이벤트를 수행했습니다.[그림 3] a, c 이벤트 수행 후이벤트 로그에서는 다음에 e를 수행해야 합니다. [그림 3]을 보면 e를 수행하기 위해서는 d가 먼저 수행되어야 합니다. 하지만 실제 로그에서는 d 수행 없이 e가 수행되었기 때문에 d를 무시하고 e를 수행합니다.마지막으로 g 이벤트 수행하여 프로세스를 마칩니다.이벤트 로그 재생이 완료되면 액티비티 d에 실행되지 못한 토큰이 남아있게 됩니다. [그림 5] 이벤트 로그 재생 후 남아 있는 토큰프로세스 모델 위에 이벤트 로그를 재생하는 동안 얼마나 많은 토큰을 사용하고(이벤트 수행 횟수) 어떤 이벤트를 생략하고 추가했는지 기록합니다. 이를 통해 기록된 이벤트 로그와 모델의 적합도를 비교할 수 있습니다. 적합도가 1이면 모든 로그가 프로세스 모델에 잘 맞는다는 뜻이고, 0에 가까우면 적합도가 매우 낮다는 의미입니다.적합도 검사는 어디에 활용할 수 있을까요? 사람들이 표준 프로세스와 달리 행동하는 이유를 찾을 때 활용 가능합니다. 왜 사람들이 기존 프로세스를 벗어나는지, 벗어나는 부분에 대해서는 잘 보고되었는지 확인할 수 있습니다. 일반적인 감사(Audit and compliance) 절차에도 활용 가능합니다.다른 사례는 도출된 프로세스 모델의 품질을 측정하기 위해 활용할 수 있습니다. 여러 알고리즘을 사용하여 프로세스 모델을 도출했을 경우 어떤 모델이 가장 적합하고 좋은 모델인지 비교해 볼 수 있습니다.마지막으로 프로세스 설명이 제대로 되어 있는지 실제 행동을 기반으로 확인할 수 있습니다. 예를 들어 어떤 서비스를 제공하는 경우 서비스 실행 방법 매뉴얼과 실제로 제공되는 서비스를 비교하여 일치하는지 확인할 수 있습니다.※ 본 블로그에 사용된 그림은 Van der Aalst 교수님 강의자료를 사용하였습니다.#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트
조회수 1075

스푼 EX팀의 Chuck을 만나보세요!

스푼을 만드는 사람들 열두 번째 이야기누구라도 '내 주변에도 이런 사람 한 명쯤은 있으면 좋겠다'라고 할법한 그런 사람.핑크색 아이폰이 너무나도 잘 어울리는 남자! 회사에서 보면 좋은 동료 같고, 때론 편한 동네 언니(?) 같이 카페에서 5시간 동안 함께 수다 떨 수 있을 법한 그런 다양한 매력이 있는, 멋진 척을 소개합니다!남자는 턱수염이죠!"제가 처음에 스푼에 입사 전에 물어본 게 있어요. 바로 '수염'을 안 깎아도 되는지에 대한 질문이었어요. 근데 웬걸.. 복장도 자유, 모자 쓰고 오시는 분들도 있고 저의 수염이 막 튀거나, 남다르게 느껴지지 않더라고요. 신선했습니다! 나와 코드가 잘 맞는 곳이구나!라고 생각했죠. 저 수염 기르고 싶거든요."EX 멤버들과 Chuck (오른쪽)듣고 싶은 당신의 스푼 라이프Q. 스푼에 입사하시게 된 계기가 궁금해요"저는 사실 취직을 조금 늦게 한 편인데요. 예전에 첫 직장을 다니다가 몸이 안 좋아져서 조금 오랫동안 쉬었어요. 충분히 쉬고 나서 회복되었을 때, 다시 구직활동을 하려던 차, 스푼에 근무하고 있는 지인이 추천을 해주시더라고요. 사실 그전부터 저는 라이브 스트리밍에 관심이 많은지라  스푼에 대해서 이미 알고 있었고 지인이 스푼을 너무 즐겁게 그리고 열심히 다니시는 모습을 보고 궁금하기도 하고 관심이 생겼었는데, 기회가 닿아서 입사를 하게 되었어요." Q. 척은 어떤 업무를 담당하고 있나요?"저는 사실 처음에 총무 포지션으로 들어왔다가, EX팀 업무도 함께 병행하면서 May의 제안으로 EX팀에서 노무 업무를 맡고 있어요! 예를 들면, 회사 규정을 만드는 업무 있잖아요? 규칙 등 그런 일들을 합니다. 무엇보다 다른 분들을 서포트하는 업무를 많이 하고 있어요."Q.  EX팀에서 나의 존재는?아기 - "EX팀에서 유일한 초보자이니까요!"그래서, 앞으로 배워야 할 것도 많고 열심히 배우려고 노력하고 있답니다. 경험 많은 팀원들께서 잘 이끌어주시고 도와주셔서 열심히 따라가고 있어요.Q. 내가 생각하는 스푼에서 일하는 장점은?"업무에 대해 개개인의 의견을 말할 수 있는 기회가 참 많은 것 같아요. 모든 구성원의 의견을 다 귀담아 들어주려고 노력하는 모습도 멋지고요. 이 부분이 저는 가장 큰 장점이라고 생각해요. 수평적인 조직의 문화의 기초가 되는 부분이라고 생각하거든요"Q. 함께 일하고 싶은 사람은 어떤 사람인가요?제겐 없는 부분을 가진 사람, 차분하고 밸런스가 잡힌 사람과 일하고 싶어요.그 예로, 저희 팀 새로 들어오신 Noah가 계신데요. 면접 때가 굉장히 인상 깊었어요. 면접 때 긴장하셨을 텐데도 불구하고 질문에 대한 답변을 굉장히 차분히, 틀린 부분은 정정하시면서 대답을 해주시더라고요. 그 부분이 굉장히 매력 있고 저와는 다른 부분으로 서로 부족한 부분을 채워줄 수 있을 것 같다고 생각했어요. 팀 내에 다양한 성향과 성격의 사람들이 있으면 그런 부분이 좋을 것 같아요.척이 수집하는 신발들의 '일부분' 사진알고 싶은 Chuck의 이야기Q. 나를 한마디로 표현한다면?오나이 - "사나이의 상반되는 개념이고, 한량이되 한심하지 않은 사람을 말합니다."Q. 법을 공부하셨다고 들었습니다."네, 어릴 땐 제 꿈이 법조인이 되는 거라고 생각했고, 그래서 법학과를 나왔어요. 생각해보면 제가 법을 공부하고 고시 준비를 했던 건 법조인이 되고 싶다는 마음보다는, 법조인이 된다면 제가 얻을 수 있는 것들과 제게 돌아오는 것들이 좋다고 생각했던 것 같아요. 공부는 중학교 때 까진 정말 열심히 했던 것 같은데, 고등학교 땐 잘 안 했던 것 같아요(겸손모드..) 그 당시엔 사실 저는 공부 말고 제가 무엇을 잘하는지 모르겠더라고요. 그래서 열심히 해야 한다고 생각했던 것 같아요"Q. 신발 수집은 언제부터 시작됐나요?"어릴 때부터 신발을 좋아했던 것 같아요. 우리 세대, 제 세대엔 마이클 조던이 전성기였거든요. 그때 뭔가 트렌드였어요. 저는 운동화뿐만 아니라, 부츠도 좋아하고 모든 신발을 좋아하지만 그중 운동화가 가장 많은 것 같아요. 이유는 음.. 모르겠어요.. 그냥 좋아하는 신발을 신고 있다는 그 느낌이 좋아요. 근데 저 생각보다 운동화 몇 켤레 없어요. 한 20켤레 정도 될걸요? 더 어릴 땐 지방까지 내려가서 사고, 줄 서서 사곤 했는데 요즘은 그러진 않아요! 아! 그리고 저 모자도 수집해요. 매년 4월이 되면 모자를 꼭 하나씩 사요. 생일 쿠폰이 나오거든요. 그래서 얼마 전에 또 신상 모자 하나 샀어요"Q. 척의 인싸력은 타고난 건가요?"저요? 저 낮 좀 가리는 편인데요? (실상 전혀 그렇게 보이지 않음. 누구보다도 친근하고, 편함)단지 저는 어색한 상황을 좋아하지 않는 편이에요. 아마 그래서 모두와 편하게 지내려고 하는 게 아닐까 싶어요"Q. 원래부터 Yolo 인생을 살았나요?*Yolo (You live only once) : 미래를 위해 현재를 희생하기보다 현재를 즐기려는 사람"저는 오늘이 행복하지 않으면, 내일도 행복하지 않다고 생각해요. 제 좌우명이 오늘이 행복하면 됐다이거든요. 내가 지금 행복한가?라고 묻는 다면 그건 내가 지금 행복하지 않기에 묻는 질문이라고 생각해요. 원래부터 그랬던 건 아닌 것 같은데, 크게 아프고 나서 변한 것 같아요. 지금은 물론 의학적으로 건강하지만요. 저는 제가 완전한 Yolo족은 아닌 것 같은데.. 제가 다른 분들에겐 그래 보일 수도 있을 것 같네요!"Q. 인터뷰해보시니 어떠셨어요?"기분이 좋았어요. 누군가 저에게 관심을 가져주고, 질문을 해준다는 게 기분 좋은 일이더라고요 :)"(인터뷰에 응해주셔서 감사합니다)Chuck은, 1. 음식을 가리지 않지만, '직화' 요리만 먹지 않습니다 2. 술, 담배를 하지 않습니다.함께 식사를 하게 된다면, 센스 있게 '직화' 요리는 피하고 술과 담배는 권하지 않으면 센스만점 동료가 될 수 있을 것 같아요 :) 팀원들이 척을 한마디로 표현한다면?Go 曰: 마이쿤의 명태 코다리 명태 코다리는 사계절 내내 명태의 참맛을 느낄 수 있다고 합니다,속초 출신인 척이 마이쿤을 위해서 사계절 내내 열심히 일해주세요~May 曰: 냉철한 두뇌와 뜨거운 마음의 소유자 사고는 논리적이고 체계적이지만 행동은 정의롭고 따뜻하거나 가끔 뜨겁기도 함 ㅎㅎKai 曰: 무서운 형 - 가끔 눈살을 찌푸릴 때 화난 거 같이 보여서요..Noah 曰: 고등학교 동창 - 낯설지 않은 친근함이 매력 포인트
조회수 1571

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 #리액트 #리액트가이드 #한글 #번역 #문서번역
조회수 1815

프론트엔드 개발자라면!

Angular의 A마크를 알아본 프론트엔드 개발자님!이 글은 새로운 플랫폼을 개발하고 있는 타운컴퍼니 개발팀으로 당신을 모셔보려는 글이에요.이들이 당신과 함께 일하고 싶은 동료입니다..타운컴퍼니팀은 알고 있습니다.잘 만들어진 편리한 앱과 고객의 이탈률이 얼마나 밀접한 관계가 있는가를요.1%의 스타트업에는 1% 개발자가 필요하며 그들이 1%의 플랫폼을 만든다는 것을요.자율적이며 열정이 넘치는 팀으로 즐겁게 높은 수준의 개발을 할 수 있는 환경이죠!당신에게 즐거운 회사, 좋은 동료가 되어 줄 수 있습니다.급여는 협의 후 결정이니 원하는걸 말해봐요!좋은 동료를 얻을 수 있다면 그정도가 어렵겠어요 ; )우리는 현재 플랫폼(townus.co.kr)이 많이 부족하다는걸 알고 있어요.그래서 완전 멋있게 새롭게 만들고 있는 중입니다 :)일단 우리는 협업툴 JIRA와 Confluence, Slack을 사용하고 있어요.우리팀은 Agile 칸반을 바탕으로 테스트 주도 개발, 코드 리뷰, 페어프로그래밍으로 프로젝트를 진행하고 있죠.도메인이 잘 분석된 명세서가 Confluence에 정리되어 있고 사용자를 위한 깊은 고민이 녹아있는 디자인이 Zeplin에 올라가고 있어요.- Back-End는 Django(DRF) 기반으로 개발되고 있고, AWS, Vagrant, Docker같은 기술을 사용해요.- Front-End는 Angular 5를 사용해서 개발하고 있고, Less, RxJS, Webpack 등의 기술을 사용하고 있어요.Angular 상용 프로젝트 개발 경험이 있다면 격하게 환영하며 모십니다. 리엑티브 프로그래밍, Ionic 경험이 있다면 더 좋구요!엥 이거 완전 나 아니냐!? 라는 생각이 들었다구요? 그렇다면 얼른 지원해야지 뭐해요!무엇보다 개발을 즐기고 오픈소스활동을 좋아하는 사람이라면,종종 맛있는것도 먹으면서 많은 대화를 나눌수 있지 않을까요? :>우리 개발자는 맥주제조도 할 줄 알거든요 (겁나 맛이 좋더라구요)물론 당신에 대해 알 수 있는 Github 주소와 이력서를 보내준다면 우리가 연락하기 더 쉬울거에요!아! 참고로 보충역 산업기능요원 편입도 가능하니 문의가 필요하다면 언제든 환영이에요!타운컴퍼니팀에게 연락하고 싶다면 02–561–0950 잊지말아요,[email protected]로 메일 보내준다면 언제든 답변줄게요 :D#타운컴퍼니 #개발자 #채용 #팀빌딩 #조직문화
조회수 2325

CloudWatch에 대하여

OverviewAmazon Web Services(AWS)는 많은 고객들이 이용하고 있습니다. AWS를 이용하여 프로젝트를 운영하고 있다면 각종 서비스의 리소스를 모니터링 하는 게 귀찮게 느껴질 수 있습니다. 이번 글에서는 AWS 리소스를 효과적으로 모니터링할 수 있는 Cloudwatch 서비스를 소개하겠습니다.Cloudwatch는 통합 뷰를 확보하는데 필요한 데이터를 제공합니다. 뿐만 아니라 이벤트 및 리소스를 이용해 경보를 생성할 수도 있습니다.1. Events2. Logs3. Custom Metrics(맞춤형 지표) 생성하기4. Alarm 생성5. Dashboards쉬어가기: Query 언어가 지원하는 여섯 가지 명령 유형1. EventsCloudWatch Events는 정기적인 일정에서 트리거(trigger)되는 규칙을 생성할 수 있습니다.1.규칙 생성을 클릭합니다.2.대상을 호출할 일정을 설정합니다.호출 방식에는 이벤트 패턴과 일정 두 가지가 있습니다. 이벤트 패턴은 json 구조로 표현됩니다. AWS 서비스에서 발생하는 패턴과 일치하면 트리거가 동작합니다. 일정은 지정한 시간과 일치하면 트리거가 동작합니다.cron 또는 rate 표현식을 사용해 예약된 모든 이벤트는 UTC+09:00 시간대를 사용합니다. 최초 단위는 1분입니다.아래는 각각의 필드에 대한 일정 cron식 설명입니다.이번 예제에서는 특정 시간에 트리거되는 일정으로 설정하겠습니다.매일 4시에 동작하도록 설정19 + 9(UTC) - 24(하루) = 새벽 4시3.대상 추가를 선택해 호출할 대상을 지정합니다.Lambda 함수 외에 여러 서비스를 선택할 수 있지만 이번 예제에서는 Lambda 함수를 지정하여 구성하겠습니다.4.규칙의 이름과 설명을 등록하고 규칙 생성을 클릭합니다.5.규칙이 생성된 것을 볼 수 있습니다.2. LogsCloudWatch Logs는 운영 중인 애플리케이션 리소스를 기록하고 액세스할 수 있으며, 관련된 로그 데이터를 검색할 수도 있습니다.1.생성된 규칙이 지정된 시간에 동작하면 CloudWatch Logs에 로그 그룹이 생성된 걸 확인할 수 있습니다.2.Lambda 함수에서 실행된 로그 메시지를 확인할 수 있으며 필터링도 가능합니다.3.로그 그룹에 이벤트 만료 시점을 설정해 오래된 데이터는 모두 자동으로 삭제되도록 설정할 수 있습니다.3. Custom Metrics(맞춤형 지표) 생성하기모니터링하고자 하는 통계치를 직접 선정하고, CloudWatch로 보내 관리하는 지표를 생성해보겠습니다.1.Log Groups에 대한 지표를 생성하겠습니다. 해당 Log Groups에 ‘Filters’를 클릭합니다.2.’Add Metric Filter’를 클릭합니다.3.로그 지표에 대한 필터 패턴을 정의합니다.Filter Pattern* “INFO Success 200” → 세 단어를 모두 포함하는 로그 이벤트 메시지와 일치* “INFO - Start - End” → ‘INFO’ 포함된 메시지 중에 ‘Start’, ‘End’ 제외된 필터 로그 이벤트 메시지와 일치4.필터 및 지표 정보를 입력한 후 ‘Create Filter’를 클릭합니다.Metric Details* Metric Namespace → CloudWatch 지표에 대한 대상 네임 스페이스* Metric Name → 모니터링된 로그 정보가 게시되는 CloudWatch 지표의 이름* Metric Value → 일치하는 로그가 발견될 때마다 지표에 게시하는 숫자 값* Default Value → 일치하는 로그가 발견되지 않은 기간 동안 지표 필터에 보고되는 값5.두 가지 케이스의 필터를 생성했습니다.4. Alarm 생성단일 CloudWatch 지표를 감시하거나 CloudWatch 측정치를 기반으로 하는 수학 표현식의 결과를 감시하는 CloudWatch 경보를 생성할 수 있습니다. 지표가 지정된 임계값에 도달하면 자동으로 이메일을 보내는 Alarm을 만들어보겠습니다.1.추가된 지표 필터에 ‘Create Alarm’ 버튼을 클릭해 경보를 추가합니다.2.경보 세부 정보 및 수행할 작업을 정의합니다.경보 평가경보를 생성할 때, CloudWatch가 경보 상태를 변경하는 조건 세 가지에 대한 설정을 지정할 수 있습니다.기간은 경보에 대해 개별 데이터 포인트를 생성하기 위해 지표 또는 표현식을 평가하는 기간입니다. 초로 표시됩니다. 1분을 기간으로 선택하면 1분마다 하나의 데이터 포인트가 생성됩니다.Evaluation Period(평가 기간)는 경보 상태를 결정할 때 평가할 가장 최근의 기간 또는 데이터 포인트의 수입니다.Datapoints to Alarm(경보에 대한 데이터포인트)는 평가 기간에 경보가 ALARM상태에 도달하게 만드는 위반 데이터 포인트의 수입니다. 위반 데이터 포인트가 연속적일 필요는 없습니다. Evaluation Period(평가 기간)와 동일한 마지막 데이터 포인트의 수 이내면 됩니다.3.경보가 발생할 Alarm 상태와 알림 받을 이메일을 등록합니다.경보 상태/OK/ 지표 또는 표현식이 정의된 임계값 내에 있습니다./ALARM/ 지표 또는 표현식이 정의된 임계값을 벗어났습니다./INSUFFICIENT_DATA/ 경보가 방금 시작되었거나, 측정치를 사용할 수 없거나, 또는 측정치를 통해 경보 상태를 결정하는데 사용할 충분한 데이터가 없습니다.4.이메일 수신함에서 ‘AWS 알림 - 구독 확인’이라는 제목의 메일을 클릭합니다. 내용에 포함된 링크를 클릭해 알림을 수신할 것을 확인합니다. (AWS는 확인된 주소로만 알림을 전송할 수 있습니다.)5.이메일 수신함을 확인해 ‘Confirm subscription’을 클릭합니다.6.등록한 이메일이 확인되었습니다.7.AWS에 이메일이 정상적으로 등록되었는지 SNS Subscriptions 메뉴에서 확인합니다.8.Lambda를 실행해 Alarm 상태를 변경해보겠습니다.9.등록한 이메일 주소로 Alarm 메일이 도착했습니다.5. DashboardsCloudWatch를 통해 리소스를 손쉽게 모니터링할 수 있는 맞춤형 통계 기능입니다.1.Metric Filter에서 추가된 Custom Namespaces를 클릭합니다.2.생성된 Metrics를 선택한 후, Graphed metrics Tab을 클릭합니다.3.Metrics에 표시될 그래프를 설정합니다.1)그래프 제목 : testLambda12)그래프 표시 : 숫자3)그래프 라벨 : testMetrics-400, testMetrics-2004)통계 : 합계5)기간 : 1 Day4.수식을 응용하여 여러 형식의 Metrics 표현식을 추가하겠습니다.지표 수식 함수* METRICS() : 요청에 모든 지표를 반환* SUM(METRICS()) : 모든 지표의 합계* AVG(METRICS()) : 모든 지표의 평균* MIN(METRICS()) : 모든 지표의 최소값* MAX(METRICS()) : 모든 지표의 최대값* ABS(METRICS()) : 각 요소의 절대값* RATE(METRICS()) : 각 요소의 초당 변경 비율5.완성된 지표 Source를 복사합니다.{ "metrics": [ [ { "expression": "SUM(METRICS())", "label": "합계", "id": "e1", "stat": "Sum", "period": 86400 } ], [ { "expression": "AVG(METRICS())", "label": "평균", "id": "e2", "stat": "Sum", "period": 86400 } ], [ { "expression": "MIN(METRICS())", "label": "최소값", "id": "e3", "stat": "Sum", "period": 86400 } ], [ { "expression": "MAX(METRICS())", "label": "최대값", "id": "e4", "stat": "Sum", "period": 86400 } ], [ { "expression": "SUM(METRICS())/SUM(m1)", "label": "SUM(METRICS())/SUM(m1)", "id": "e5", "stat": "Sum", "period": 86400 } ], [ { "expression": "SUM(100/[m1, m2])", "label": "SUM(100/[m1, m2])", "id": "e6", "stat": "Sum", "period": 86400 } ], [ "testMetrics", "testMetrics1", { "id": "m1", "stat": "Sum", "period": 86400, "label": "testMetrics-400" } ], [ ".", "testMetrics2", { "id": "m2", "stat": "Sum", "period": 86400, "label": "testMetrics-200" } ] ], "view": "singleValue", "stacked": false, "region": "ap-northeast-1", "title": "testLambda1", "period": 300 } 6.Dashboard name을 입력한 후 ‘Create dashboard’를 클릭합니다.7.’Add widget’을 클릭해 Number 유형을 선택합니다.8.Source Tab에서 복사해 둔 지표 Source를 붙여 넣고, ‘Create widget’을 클릭합니다.9.위젯이 추가되었습니다. 추가된 위젯은 ‘Save dashboard’ 버튼을 클릭해야 최종 저장됩니다.10.이번에는 로그 메시지 결과를 확인할 수 있는 Query result 유형을 추가해보겠습니다. 먼저 Query result 유형을 선택합니다.11.로그 메시지에 조건을 추가해 필터링합니다.잠시 쉬어가기!: Query 언어가 지원하는 여섯 가지 명령 유형fields : 지정한 필드를 검색합니다. 필드 명령 내에서 함수 및 연산을 사용할 수 있습니다. 만약 @ 기호, 마침표(.) 및 영숫자 문자 이외의 문자가 포함된 로그 필드가 쿼리에 명명되어 있으면 해당 필드 이름은 억음 기호로 둘러싸야 합니다.filter : 하나 이상의 조건으로 필터링합니다. filter statusCode like /2\d\d/ → 필드 statusCode의 값이 200~299인 로그 이벤트를 반환합니다.stats : 로그 필드에 대한 지정된 시간 간격의 집계 통계를 계산합니다.sort : 검색된 로그 이벤트를 정렬합니다.limit : 쿼리에서 반환되는 로그 이벤트 수를 제한합니다.parse : 로그 필드에서 데이터를 추출하고 쿼리로 추가 처리할 수 있는 임시 필드가 하나 이상 생성됩니다.12.추가된 위젯은 이름과 사이즈를 조절한 후, ‘Save dashboard’ 버튼을 클릭해 최종 저장합니다.13.생성한 Alarm을 Dashboard에 추가하겠습니다.14.Dashboard가 완성되었습니다!Conclusion지금까지 CloudWatch 서비스를 소개했습니다. 이 서비스를 이용하면 로그와 지표를 쉽게 시각화할 수 있고, 작업을 자동화할 수도 있는 것을 확인했습니다. CloudWatch를 이용해 애플리케이션을 최적화하고, 원활하게 실행해보는 건 어떨까요. 분명 리소스를 효과적으로 다룰 수 있을 겁니다.글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만
조회수 934

[맛있는 인터뷰 4] 잔디의 헬스보이, 안드로이드 개발자 Steve를 만나다

[맛있는 인터뷰] 잔디의 헬스보이, 안드로이드 개발자 Steve를 만나다                                         미소, 승리의 V, 로맨틱, 성공적                                       삶은 생각보다 심플한 것 같아요.                              인생은 결국 생각하는 대로 풀리게 되더라고요.                                     잔디에서 제 목표를 이뤄가고 있어요.                                      – Steve, 잔디 안드로이드 개발자편집자 주: 잔디에는 현재 40명 가까운 구성원들이 일본, 대만, 한국 오피스에서 일하고 있습니다. 국적, 학력, 경험이 모두 다른 멤버들. 이들이 어떤 스토리를 갖고 잔디에 합류했는지, 잔디에서 무슨 일을 하고 있는지 궁금해하시는 분들이 많았습니다.  이에 잔디 블로그에서는 매 주 1회 ‘맛있는 인터뷰’라는 인터뷰 시리즈로 기업용 사내 메신저 ‘잔디’를 만드는 사람들의 이야기를 다루고자 합니다. 인터뷰는 매 주 선정된 인터뷰어와 인터뷰이가 1시간 동안 점심을 함께 하며 다양한 이야기를 나누며 진행됩니다. 인터뷰이에 대해 궁금한 점은 댓글 혹은 이메일([email protected])을 통해 문의 부탁드립니다.오늘의 ‘맛있는 인터뷰’ 장소는 어디인가요?‘롱브레드’라는 빠니니집이에요. YB와 같이 버디런치할 때 갔었는데 맛있었어요. 강남이라는 위치 특성 상 보통 식당들이 혼잡한데 여긴 조용한 편이에요.                                       빠니니 앞에 우리는 겸손해진다.잔디 블로그가 유명해지면 그리되겠죠? 자기소개 좀 부탁드릴게요안녕하세요? 스타트업을 동경해 안정적인 삶을 뒤로 하고 잔디에 조인한 안드로이드 애플리케이션 개발 담당자 Steve입니다.안드로이드 개발 중에서 어떤 일을 맡고 계신가요?지금은 전체적인 부분을 다 하고 있어요. 안드로이드 쪽으로 가장 먼저 입사한 사람이라 요즘 들어오는 개발자분들 OJT도 하고, 주요 개발 포인트에도 열심히 참여하고 있습니다.그렇군요. 헬스 트레이너 자격증을 갖고 계신단 얘길 들었어요.사실 운동을 전문적으로 하려 그런 건 아니었고, 옷 맵시를 잘 살리고 싶어 운동을 시작했어요. 제가 과거에 개그 콘서트 ‘헬스보이’에 나오는 김수영 같았담 몸매였다면 믿기시겠어요? 인생의 암흑기였던 그 시절, 어떤 옷을 입어도 멋있지 않았어요. 딱 한 번이라도 좋으니 뭘 입어도 간지가 났으면 좋겠단 생각을 했어요. 그게 제 생활 습관을 바꾼 계기였어요.트레이너 자격증 따는 게 쉽지 않을 것 같아요트레이너 자격증 준비할 당시엔 장기적으로 꾸준히 운동했어요. 아침 6시에 일어나 운동하고 오전, 오후 일과를 보낸 뒤 오후 5시부터 다시 운동하고 11시에 자곤 했어요. 식단은 하루에 5끼를 한 가지 종류의 메뉴로 구성해 1년 동안 먹었는데요. 정말 힘들 때는 한 달에 한 번 피자를 먹기도 했습니다.참기 힘든 유혹의 순간이 있진 않았나요?음.. 실기 시험 일주일 전이었어요. 여긴 특이하게 짧은 바지만 입고 몸을 보여주는 테스트를 통과해야 필기 시험을 볼 수 있었는데요. 실기 시험 전 참석했던 친한 동기 생일에서 술을 마다하고 최대한 절제하고 있었어요. 근데 친구가 자기 생일인데 왜 안 마시냐 핀잔 아닌 핀잔을 주더라고요. 그 때 조금 마셨는데 순간 고삐가 풀리더라고요. 이후 3시간 동안 미친 듯이 술과 안주를 먹었어요. 정말 다행히 실기 시험에 통과했지만 그때 한번 제대로 이성을 잃었던 적이 있습니다.헬스를 하면서 얻은 수확이 있다면?1년 정도 운동을 하니 규칙적인 생활이 몸에 뱄어요. 언제, 무엇을 할지 계획을 세워 생활하다 보니 어떻게 하면 효율적으로 시간을 사용할 수 있을지 알게 되었는데요. 운동을 통해 스스로 인내하고 제어하는 방법을 그 때 다 배웠어요.그 습관이 업무에 도움이 되셨나요?업무 관련 이야기는 아니지만 잔디에 합류하기 전 이직 준비를 1년 넘게 했어요. 이직에 필요한 사항을 정리해 최선을 다해 준비해보자 마음먹었어요. 이때 운동을 통해 다진 규칙적인 생활 습관이 큰 도움이 되었는데요. 꼬박 1년 동안 밤낮을 가리지 않고 개발 공부에 매달렸어요. 덕분에 ‘함께 일해볼 생각이 없냐?’는 제의를 많이 받았어요.                                 일할 땐 진지 모드, 밥 먹을 땐 샤방 모드.그런 제의를 고사하고 잔디를 선택하신 이유가 있다면?몇 가지 이유가 있는데요. 스타트업에 계시는 다른 분들을 보고는 비전이 있는 곳으로의 이직을 결심했어요. 5년 차 엔지니어로서 1년이라는 시간을 가지고 승부수를 던진 거예요. ‘생각하면서 살면 생각한 대로 살지만, 살면서 생각하면 사는 대로 생각하게 된다.’는 말을 인상 깊게 봤어요. 이 말대로 실천하려고 노력해 온 게 시간이 지나니 확실히 남들과 차이가 커지더군요.  그래서 생각하면서 사는 게 얼마나 중요한지 알고 있어요. 그중에서도 직장은 하루에 큰 부분을 차지하니까 신중하게 직장을 선택하는 건 인생의 중요한 전환점이죠.잔디에서의 생활은 만족스러우세요?기대했던 모든 게 다 잔디에 있는 것 같아요. 업무에 대한 자율성과 책임감이 적절히 섞여 있어요. 일반 회사의 수직적 구조도, 팀장급 이상에게만 주어지는 의사 결정권도 잔디에선 찾아볼 수 없어요. 덕분에 다양한 시각과 방법으로 개발 업무를 할 수 있기 때문에 전 개인적으로 만족하며 일하고 있습니다.쉬는 날에는 보통 어떤 활동을 하세요?업무 관련 공부를 하거나 친구들을 만나곤 해요. 개인적으로 회사 근처에 사는 걸 선호해 현재 강남 쪽에 살고 있는데요. 덕분에 친구들과의 약속이 잦아졌어요. 약속이 없는 날에는 주로 혼자 공부하고 있어요.이전 인터뷰이인 Jay님이 오늘 인터뷰이에게 ‘좋은 프로덕트란 어떤 것인지’ 물어봐달라고 하셨는데요. 이 질문에 대한 Steve의 답변은?좋은 프로덕트란 ‘복잡한 설명이 없어도 모든 동작을 깔끔하게 작동할 수 있게 만드는 것이다’ 라고 정의하고 싶습니다. 이를 위해선 개발자들이 모든 프로세스를 다 자동화해야겠죠? 생각보다 매우 꼼꼼한 업무가 필요한 과정이라 개발자들에게는 스트레스가 될 수 있을 거에요. 하지만 이건 개발자의 몫이고 사용자에게는 ‘편리함’과 ‘익숙함’을 제공해야 한다고 생각합니다. 제품 사용을 위한 프로세스를 최대한 단순화시켜 사용자가 자신이 원하는 동작 이외의 행동에 대해 생각하지 않게 만드는 게 최고의 프로덕트인 것 같습니다.미리 준비하셨나요? 인상적인 답변이네요. 마지막으로 다음 인터뷰를 위한 릴레이 질문이 있으시다면?다음 인터뷰이 분에게 ‘일과 사랑 어느 쪽이 우선인지’ 꼭 물어봐 주셨으면 좋겠습니다. 보통 스타트업을 다니면 연애하기 힘들다고 하잖아요. 왠지 다음 분께서 어떤 대답을 하실지 궁금하네요.열정적인 Steve와의 인터뷰 이후 ‘잔디의 안드로이드 개발 부분은 걱정 없겠구나’ 란  생각을 하게 되었습니다. 앞으로도 잘 부탁해요 Steve! 다음 주 인터뷰도 많은 기대 부탁 드려요.#토스랩 #잔디 #JANDI #개발자 #앱개발자 #애플리케이션 #모바일 #팀원 #팀원소개 #팀원인터뷰 #인터뷰 #사내문화 #조직문화 #기업문화 #팀원자랑
조회수 704

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

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

린더를 만들고 있는 이유 2.0

본문은 2017년 8월 작성한 린더를 만들고 있는 이유 1.0 의 후속편입니다.히든트랙이 해결하고자 한 문제히든트랙팀은 '린더'라는 일정을 받아보는 경험을 만들어가고 있습니다. 2018년 4월 기준 약 16만명의 사용자가 린더를 통해 일정을 받아보고 있으며, 린더가 존재하기 전 사람들을 일일히 자신들이 필요로 하거나 궁금한 일정들을 검색하여 확인해야만 했습니다. 우리가 문제를 해결한 방식은 매우 간단합니다. 매번 필요할 때마다 검색해봐야 했던 일정을 우리가 대신 기록하여 그것을 받아볼수 있도록 제공 하는것, 다시 말해 다수가 공통적으로 안고 있던 귀찮음을 소수의 노력으로 해결하고자였으며 이와 같은 문제 해결 방식은 명함 수기 입력 앱 - 리멤버 또는 전단지 모음 앱 - 배달의 민족이 접근한 방식과 유사합니다.첫 번째 선택, 캘린더 기반 일정 구독 ( https://linder.kr/ )일정을 받아보는 경험은 모바일앱, 챗봇, AI 스피커 등 다양한 방식으로 구현될 수 있습니다. 그중에서도 히든트랙팀이 선택한 첫 번째 방식은 이미 다수가 활용중인 캘린더 앱의 구독 기능을 활용한 것입니다. 스마트폰 기본앱인 캘린더를 하나의 정보 전달 채널로 활용함으로써 거부감 없이, 낮은 진입장벽으로 출시 반년 만에 15만명이 넘는 사용자를 확보할수 있었습니다.캘린더 기반 일정 구독의 한계하지만 캘린더를 기반으로 한 일정 구독에는 명확한 한계가 몇 가지 있었습니다. 1) 구독 캘린더의 특성상 리마인더 기능이 매우 제한적이었으며  2) 각 플랫폼 별 다른 동기화 시간으로 인해 실시간 업데이트가 불가했습니다. 3) 또한 기존 캘린더에 입력되어있던 개인 일정과 받아보는 일정이 혼재되어 분류가 어려웠으며 4) 일정을 삭제하거나 메모를 입력할 수 없었습니다.캘린더의 한계를 극복할 수 있는 자체 앱 제작 ( http://bit.ly/2EB41TW )이에 히든트랙팀은 지난 2017년 말 진행한 다수의 유저 인터뷰를 바탕으로 2018년 1월부터 약 3개월 간 일정을 받아보는 경험에 최적화된 모바일 앱을 개발하였습니다. 모바일의 핵심은 필요한 일정을 정확한 시점에, 검색 없이도 쉽게 받아 볼 수 있는데 초점을 두고 있습니다.린더 : 받아보는 캘린더 - Google Play 앱play.google.com 일정을 받아보는 경험에 대한 사용자와 이해 관계자히든트랙팀이 캘린더 기반의 일정 구독자와 모바일앱 사용자 모두에게 공통적으로 제공하고자 하는 것은 사용자가 자신에게 필요한 일정을 보다 쉽고 확실하게 소비할 수 있도록 돕는 것입니다. 사람들에게 필요한 일정은 아이돌 스케줄부터 화장품 세일, 학사일정에서부터 마트 휴무일까지 다양한 분야에 존재합니다. 일정을 받아보는 경험을 만들어가는 과정에서 우리가 일반 사용자 외에도 고려해야 할 나머지 두 종류의 이해 관계자는 일정을 공급하는 공급 파트너와 유통을 돕는 유통 파트너가 있습니다.망하기 딱 좋은 일정 데이터 생산 비즈니스일정을 받아보는 경험을 만들어가는 과정은 여느 타 서비스에 비해 매우 소모적입니다. 일정 데이터는 리뷰(왓챠)나 댓글(크리마), 연락처(리멤버) 등 과는 다르게 데이터의 휘발성이 매우 강하며 변동성 또한 매우 크기 때문에 다수의 기업들이 기피하는 데이터 형태라고 볼 수 있습니다. 일례로 2016년부터 2017년 중순까지 운영되었던 SKT의 Someday(썸데이)는 내부 조직장 교체와 비효율적인 ROI로 서비스가 종료된 바 있습니다.같은 실수를 저지르지 않기 위한 일정 데이터 서비스 전략 로드맵히든트랙팀은 2017년 1월부터 다수의 일정 관련 서비스 개발을 진행해왔으며 이 과정에서 습득한 노하우를 바탕으로 일정 데이터 생산 및 공급망을 구축할 수 있는 3단계 계획을 세우게 되었습니다.STEP.1 린더 파트너스 - 데이터 공급 파트너 확보캘린더 기반 일정 마케팅 솔루션 '린더 파트너스'는 해외 eCal, CalendarX, Eventable 등 다수의 캘린더 마케팅 업체를 벤치마킹하여 국내 인터넷 환경에 맞추어 최적화시킨 아시아 유일의 캘린더 마케팅 솔루션 입니다. 2018년 3월 기준 롯데자이언츠, 두산베어스, 수원삼성FC, 아디다스 코리아 등 20여 개의 데이터 공급 파트너를 확보한 린더 파트너스를 기반으로 히든트랙팀은 공식적인 데이터 공급 파트너를 확보함과 동시에 데이터 생산을 위한 초기 자본을 조달할 수 있게 되었습니다. 파트너스 영업은 현재 영업팀을 주축으로 이루어지고 있으며 2018년 말까지 현 20여 개의 파트너를 50여 개 수준으로 늘리는 것을 목표로 하고 있습니다.STEP.2 린더 모바일앱 - 일반 사용자 확보린더 파트너스를 통해 확보한 자금과 일정 생산력을 바탕으로 모바일앱 데이터의 정확도와 품질을 향상하고 사용자 중심의 서비스를 구축합니다. 기업 친화적인 린더 파트너스와는 다르게 린더 모바일앱은 오로지 일반 사용자를 위한 서비스로서 사용자 친화적인 인터페이스와 일정 콘텐츠 소비 경험을 핵심으로 합니다. 다수의 일반 사용자를 확보함으로써 제보 기능(크라우드소싱)을 활용하여 데이터의 정확도와 유저별 선호 캘린더 데이터를 파악할 수 있게 됩니다.  2018년 4월 안드로이드/iOS 앱 출시가 예정되어 있으며 2018년 연말까지 5만 이상의 MAU 확보를 목표로 하고 있습니다.STEP.3 린더 데이터헙 - 데이터 유통 파트너 확보글의 서두에서 언급한 바와 같이 일정을 받아보는 경험은 단순히 캘린더나 모바일앱 외에도 다양한 방식으로 제공될 수 있습니다. 확보한 데이터 공급 파트너와 일반 사용자 제보를 바탕으로 일정 데이터량과 품질을 향상하고, 더 나아가서는 보유한 유저 Pool을 바탕으로 사용자들의 선호도를 사전에 파악할 수 있게 됩니다. 이러한 다양한 종류의 데이터를 기반으로 현재 스피커 및 기타 AI 서비스를 제공 중인 네이버, 카카오, 삼성, SKT, KT 등의 유통 파트너를 대상으로 영업을 진행, 협력을 통해 다양한 방식으로 사용자들에게 일정 정보를 전달할 수 있게 됩니다.히든트랙의 3가지 비즈니스 모델위 언급한 3단계의 전략 로드맵을 통해 히든트랙은 3가지 수익창출 기회를 확보할 수 있습니다. 1) 캘린더 마케팅 솔루션 - 린더 파트너스의 Enterprise SaaS 형태 공급 및 데이터 관리 용역을 통한 수익2) 린더 앱 내 확보한 사용자 선호도를 바탕으로 일정 기반의 마케팅 광고주들에게 제공하여 창출하는 수익 3) 그리고 유통 파트너들에게 일정 데이터를 제공하는 대가로 받는 데이터 판매 및 용역에 대한 수익 이 바로 그 3가지 입니다.'린더' 하다 = 일정을 받아보다다각적인 비즈니스 모델과 단계가 존재하지만 결과적으로 이를 통해 확보한 매출의 재투자와 회사의 방향성은 하나로 일원화 될 수 있습니다. 그것은 바로 사람들의 소중한 일정을 놓치지 않도록 도와주는것. 자동차 네비게이션과 같이 서비스가 삶에 완벽히 녹아들어 그것이 부재하던 시절의 삶을 상상할 수 없게 되는 것이야 말로 가장 높은 수준의 서비스 구현이라 할 수 있습니다. 과거에 지도에만 의존하여 길을 찾던 시절 소수의 사람들이 네비게이션의 가능성을 보고 그것을 만들어왔던 것처럼, 사람들이 린더를 통해 그들의 소중한 일정을 놓치지 않도록 도와주는 것이 우리의 최종 목표입니다.#히든트랙 #챗봇 #기술기업 #개발자 #개발팀 #인사이트 #경험공유
조회수 880

프로그래밍에는 왜 창의성이 필요하다고 할까요?

왜 프로그래밍에는 창의성이 필요하다고 할까요? 실제로 프로그래밍을 하다 보면 복잡한 문법을 이해하고, 암호 같은 에러를 차분히 해결해야 하니, 오히려 수학적이며 논리적인 사고가 더 필요해 보입니다. 그런데도 프로그래밍이 창의적이어야 하는 이유는 하나의 프로그램을 만드는 답이 여럿이기 때문입니다.답이 하나여도 가르치기 어려운데, 다양한 방법을 어떻게 가르쳐야 하는 걸까요? 또, 기상천외한 학생들의 코드를 보며 이해하고 교육하는 것이 얼마나 긴 시간이 필요하며 어려운 일인가요? 어디서부터 해결해나가야 할지 막막합니다.Elice 리서치 팀에서 하는 일 중 하나는 학생들의 수많은 코드 중 비슷한 타입들을 추려내는 것입니다. 코드를 몇 가지 타입으로 추려내고 나면, 선생님은 학생 하나하나의 코드를 보고 교육하는 것이 아니라, 비슷한 형태의 코드를 작성한 학생 그룹 전체에게 적절한 피드백을 줄 수 있습니다. 이렇게 그룹 전체에게 피드백을 준다면 선생님은 같은 시간에 더 많은 학생을 교육할 수 있을 것입니다.그럼 이제, 비슷한 코드를 어떻게 찾아내서 분류할지 이전 연구를 보며 알아봅시다. (현기증이 날 수 있으며, 다 이해할 수 없어도 괜찮으니 걱정하지 마세요!)지프의 법칙과 숙제 제출 패턴자연어 처리(Natural Language Processing;NLP)를 배울 때 자주 거론되는 사람이 있습니다. 바로 미국의 언어학자인 조지 킹슬리 지프George Kingsley Zipf인데, 이 사람이 만든 지프의 법칙은 자연적으로 일어나거나 생성되는 특정 정보들이 일정하게 나타내는 경향을 나타낸 것입니다. 지프는 영어로 된 텍스트를 분석하던 도중, 자주 쓰이는 단어를 순서대로 나열하면 각 단어의 빈도는 그 단어의 출현 순위에 반비례함을 찾아냈습니다. 영어에서 가장 많이 사용되는 단어 1~3위가 “the”, “of”, “and” 인데, “the”는 “of”의 약 두 배, “and”의 약 세 배의 빈도를 보입니다.이것을 수학적으로 표현하면, 일정 크기 이상의 영어 말뭉치(corpus)에 들어 있는 단어들의 개수를 전부 세서 그 단어들을 가장 많이 쓰이는 것부터 순위를 1위부터 나열했을 때, 특정 단어의 순위가 k 라면 (즉 전체에서 k번째로 많이 쓰인 단어라면) 그 단어가 말뭉치에서 쓰인 개수는 1/k에 비례한다는 것입니다. 이것을 그래프로 그려보면 다음과 같습니다. 재미있는 사실은, 여기에서 x축과 y축에 log를 씌워 보면 (이것을 log-log scale로 변환한다고 합니다.) 다음과 같은 직선 형태로 변환된다는 것입니다.이것이 도대체 숙제 제출과 무슨 관계가 있길래 이렇게 장황한 설명을 한 것일까요? 위에서 나온 지프의 법칙을 기억하시나요? 학생이 낸 숙제를 채점하다 보면 꽤 많은 학생이 비슷한 방식으로 숙제를 푸는데, 제출된 풀이 방식들을 비슷한 것끼리 묶어 분석해 보니, 이것 또한 지프의 법칙을 따른다는 것이 발견되었습니다. 예를 들어 가장 인기 있었던 풀이 방식으로 100명이 숙제를 제출했다면, 두 번째로 인기 있는 풀이 방식으로는 약 50명이 숙제를 제출했다는 뜻입니다.여기서 우리가 찾아낼 수 있는 인사이트는 무엇일까요? 첫째, 학생들의 숙제들을 비슷한 것끼리 묶을 수 있다면, 그리고 이 분류를 컴퓨터로 자동으로 할 수 있다면 조교가 채점하거나 코멘트를 할 때 써야 할 시간이 상당히 줄어들 것입니다. 둘째, 방법 서너 개에 대해서만 어떻게 채점할지 혹은 어떻게 코멘트할지에 대해 준비를 해놓으면, 그걸로 숙제 대부분을 채점/코멘트할 수 있을 것입니다. 대다수의 숙제는 몇 가지 인기 있는 풀이방식으로 만들어졌을 것이기 때문입니다.그러면 이제 다음 문제는, ‘비슷한 풀이 방식으로 푼 프로그램 코드’를 어떻게 찾아낼 것인가? 를 고민해봅니다. MIT의 Elena Glassman은 이에 대한 해법으로 Overcode를 제시했습니다.Overcode뉴스 기사나 책, 블로그 글 등 자연어로 이루어진 텍스트 데이터를 분석하고, 여기에 어떤 주제가 들어있는지 밝혀내는 연구는 많이 진행 됐습니다. 이를 위한 머신러닝 알고리즘 중 하나가 토픽 모델, Topic model 입니다(토픽 모델에 대해서는 다른 글에서 자세히 다룰 예정입니다). 그러나 토픽 모델링을 프로그래밍 문제에 실제로 적용하기는 쉽지 않습니다. 코드에 사용되는 문법이나 키워드가 자연어와 1:1로 매칭되지 않기 때문에, 기존에 자연어에서 사용되던 모델을 그대로 사용할 수 없기 때문입니다. 가령, 슬쩍 보면 무척 달라 보이는 아래 두 파이썬 코드는 사실 완전히 같게 동작합니다. 이 두 코드를 (사람들이 일상생활에서 사용하는) 자연어를 분석하는 모델로 분석한다면 제대로 된 결과를 낼 수 없는 건 당연합니다.def fibonacci(): parents, babies = (1, 1) while babies < 100 xss=removed>fibonacci()def fib(parents, babies): ‘’’ parents = 1 babies = 1 ‘’’ while True: print ‘This generation has {0} babies’.format(babies) parents = babies # set parents as babies babies = parents + babies # recursively add number of babies if babies >= 100: break fib(1, 1)Elena Glassman이 제시한 Overcode의 목적은 비슷한 로직으로 만들어진 프로그래밍 코드들을 모으는 것입니다. 이제 Overode가 어떻게 작동하는지 간단하게 소개하도록 하겠습니다. 가장 먼저 수행되어야 하는 것은 서로 다른 형식으로 쓰인 소스 코드들을 정리하는 것입니다. 소스코드 정리에는 주석 제거, 줄 및 공백/들여쓰기를 일정하게 맞추는 작업 등이 포함됩니다.Image from Overcode하지만 이 작업만으로는 충분하지 않습니다. 거의 같아 보이는 코드도 실제로 프로그램을 실행하기 전까지는 같은지 알 수 없고, 꽤 달라 보이는 코드도 실제로는 완전히 같게 동작할 수 있기 때문입니다 (여기서 같게 동작한다 함은, 결과를 같게 내는 것 이상으로 결과를 내는 중간과정이 완전히 같다는 것을 의미합니다). 다시 위로 돌아가, fibonacci() 함수와 fib(parents, babies) 함수를 살펴봅시다. 위 두 코드는 기존의 자연어 처리 기법에 따르면 완전히 다른 프로그램일 것입니다. 변수명이 달라서가 가장 큰 이유일 텐데, 사실 컴퓨터의 입장에서 변수는 어떤 값을 할당하는 공간에 불과하며, 그 공간에 어떤 이름을 붙이느냐는 중요하지 않습니다. 코드를 작성하는 것이 사람이기 때문에 공간에 편하게 이름을 붙이는 것뿐입니다. 서로 다른 프로그램에서 어떤 변수가 서로 같은 역할을 하는지, 컴퓨터가 알아내려면 어떻게 해야 할까요? (컴퓨터는 창의적이지 않습니다!)Image from OvercodeElena가 제시한 방법은 프로그램을 실행하면서 변수의 값이 어떻게 바뀌는지를 추적한 것입니다. 아래 두 코드를 보고, 한번 머릿속으로 프로그램을 실행해 봅시다. 학생 B의 코드는 for문으로 5의 3승을 계산했고, 학생 C의 코드는 while문으로 5의 3승을 계산한 것입니다. 학생 B의 코드가 실행됨에 따라 r이라는 변수가 어떻게 변하는지, 학생 C의 코드에서 result가 어떻게 변하는지 확인해보면 둘 다 1 → 5 → 25 → 125 의 값을 가지게 됩니다. 그렇다면 컴퓨터는 이렇게 판단할 수 있습니다. “B의 코드에서의 변수 r과 C의 코드에서의 result가 완전히 같은 방식으로 변하니, 같은 의미로 사용된 것이다.”Image from Overcode이제 같은 의미를 가지는 변수들을 알아냈다면, 컴퓨터는 쉽게 가장 “흔한” 이름으로 변수의 이름을 바꿔치기 합니다. 그러면 처음에 서로 다르게 보였던 코드들도 이제 같아질 것입니다.물론 이것이 다는 아닙니다. 예를 들어, 간단한 테스트 케이스들을 통해 결과를 비교함으로써 변수를 분석하기 전에 먼저 거르는 방법, 너무 흔한 변수들을 처리하는 방법 (예를 들어 완전히 다르게 동작하는 코드들에서도 반복문에서 사용되는 인덱싱 변수들은 같이 변화할 것입니다), 한 변수가 다른 의미론적으로 두 번 사용되는 경우 처리하는 방법… 등이 논문에는 더욱 자세히 적혀있습니다. 궁금한 독자들은 한번 논문을 읽어보도록 합시다.남은 문제들Elena가 제시한 방법은 위에서 보여준 예제와 같은 간단한 코드에서 꽤 잘 동작합니다. 예를 들어, 다음과 같은 문제들이 있습니다.a의 b승을 구해서 리턴하는 프로그램N번째 피보나치 숫자를 리턴하는 프로그램다항식의 미분 결과를 리턴하는 프로그램하지만 대학교에서 1학년만 넘어가더라도 이런 간단한 프로그램 과제는 내지 않습니다. 예를 들어 Elice에서 교육 중인 기초 프로그래밍/ 프로그래밍 유치원 수업을 듣는 학생들은 매우 많은 실습문제를 풉니다. 여기에서 푸는 과제들은 초반 몇 주가 지나면 이 정도의 간단한 프로그래밍 수준을 뛰어넘기 때문에, 코드가 꽤 길어지고 다양성이 생기게 되는데 이런 경우 이 방법은 잘 동작하지 않습니다.또 다른 문제는, 이 방법이 “동작하는” 코드에서만 작동한다는 것입니다. 예를 들어, 수강생들이 아직 기초 문법을 배우고 있다면? 제대로 실행도 되지 않는 코드를 만들었을 때, 비슷한 실수를 한 사람들끼리 묶어주고 싶다면? 아쉽게도 Elena가 제시한 방법은 이렇게 에러가 나는 코드에서는 동작하지 않습니다. 코드가 실행되지 않는다면 변수의 값의 변화를 추적할 수 없기 때문입니다.마치며이번 포스트에서는 학생의 제출 코드를 비슷한 것끼리 묶는(Clustering하는) 방법에 대해 간단하게 살펴 보았습니다. 학생이 낸 비슷한 답안을 모아주는 솔루션은 수학 문제 같은 단답식 문제, 혹은 영어 에세이같은 자연어에 대해서는 이미 상용화가 되어 있습니다. 영어 에세이의 경우 여러분들이 가장 친숙할 만한 상용화된 솔루션은 아마 copy detector일 것입니다.하지만 프로그래밍 코드의 클러스터링은 연구가 계속 진행되고 있습니다. 앞서 말했듯 코드에서 한 글자 한 글자가 가지는 의미가 자연어에서 가지는 알파벳과는 완전히 다르기 때문이기도 하고, 정말 실행을 해 보기 전까지는 어떻게 동작하는지 예측하는 것이 매우 어렵기 때문이기도 합니다. Elice 리서치 팀에서도 프로그래밍 코드에 대한 분석을 자동으로 수행하는 머신러닝 연구를 수행하고 있습니다. 이러한 기술을 통해 선생님이나 조교가 학생을 더욱 효율적으로 지도하고, 컴퓨터의 도움으로 지도에 아낀 시간을 한 단계 더 개인화된 도움을 주도록 하는 것이 Elice의 목표 중 하나 입니다.글쓴이김수인: KAIST 전산학부 박사과정 / Research Lead, Elice김재원: KAIST 전산학부 박사과정 / The Lead, Elice배휘동: KAIST 전산학부 박사과정 / Frontend Lead, Elice#엘리스 #코딩교육 #교육기업 #기업문화 #조직문화 #서비스소개
조회수 1225

[직무] iOS 개발 : 애플이 새로운 걸 내놓는 순간, 누구보다 빠르게 개발한다

안녕하세요미미박스 PEOPLE 팀의 Ava입니다오늘은 미미박스의 iOS개발 직무를 소개해드리겠습니다 ! 다들 미미박스 어플 사용해보셨나요?커머스 기능뿐 아니라 새로운 기능을 통한 즐거움을 맛볼 수 있죠.오늘은 미미박스 iOS 개발자 직무가어떤 생각을 가지고 어떻게 일하고 있는지소개해드리겠습니다."애플이 버전업을 하자마자 즉시 해당 버전의 업데이트를 내놓죠" 앱스토어에 가서 '미미박스'를 검색해보면 아래와 같이 안내가 되어있는데요.Offers iMessage AppOffers Apple Watch App미미박스는 아이폰용 앱뿐만 아니라애플 와치, 아이메시지, 투데이 익스텐션 앱스토어가 생기자마자우리나라에서 가장 초기에 해당 앱들을 개발했습니다.뿐만 아니라 포니이펙트 셀에 직접 아이디어를 제안하고협업하여 포니이펙트 아이메시지 스티커도 오픈하게 되었죠.국내 커머스나 뷰티 앱 중에도 이렇게 모든 버전의 앱을 다 개발한 곳은 흔하지 않은 것 같아요 ! 미미박스 iOS 개발팀은새로운 것, 트렌디 한 것이 있으면호기심을 가지고 가장 먼저 만들어보고 싶어 하는 개발자들이 모여있습니다."이제 막 열린 새로운 버전에서 서비스를 개발하게 되면불안한 점이 많아요.그럼에도 저희가 빠르게 만들어 낼 수 있는 건,미미박스 앱이 결제나 여러 측면에 있어서 기반이 탄탄하다는 뜻이죠."탄탄한 기본기와트렌디한 빠른 개발,벌써부터 손이 간질간질하지 않나요?"앱 사용감 너무 좋네요""미미박스 화장품 관련 앱 중 최고""넘나 편리하고 유익한 것""획기적인 앱"앱스토어에서 미미박스 보러 가기iOS 팀의 목표는고객들이 쫀득한 사용감에 감탄하고 계속 쓰고 싶은 앱을 만드는 것입니다. 미미박스 앱은편안하고 안정적인 쇼핑은 기본이고,앱 안에서 뷰티를 즐길 수 있는 신선한 경험과온라인과 오프라인을 넘나들며 사용할 수 있는 기능도 제공하고 있습니다.대표적인 것이 디스커버리 영역과스토어 모드입니다. 디스커버리 영역은 앱에 접속하면바로 만날 수 있는 뷰티 컨텐츠 영역입니다.이곳의 영상들은 광고 영상이라기보단고객들에게 친근하게 뷰티에 대한 팁과 정보를 쉽게 전달해주는 곳이죠.혹시 제품을 구매하러 가셔서'이게 나랑 맞을까'하는 마음에후기를 찾아보신 적이 있나요?여러분 지금 손에 스마트폰을 들고 있다면미미박스 앱을 켜고 쉐킷쉐킷 흔들어보세요스토어 모드가 실행됩니다.스토어 모드는 온라인과 오프라인을 연결하는데요.오프라인에서 만난 제품의 솔직 고객 후기와 어울리는 제품을빠르게 찾아볼 수 있죠.흔들고 - 바코드 스캔하면 - 고객들의 솔직 리뷰가 쫙~ 앞으로 브라우저를 키고, 검색해서 누르고, 뒤로 갔다가 다시 찾고...이런 번거로움 없이 오프라인에서도 쉽게 제품 정보를만날 수 있죠!오프라인 매장, 브랜드 제품, 플랫폼 등등미미박스에는 여러분이 연결할 수 있는 다양한 점이 있습니다.미미박스에서 이 점들을 연결해무엇을 할 수 있을지 많은 아이디어를 내보고,새로운 것을 창조해보세요."iOS팀은.. 정..정말 눈치 안 보고 아이디어 내나요?""정말이라니까요ㅎㅎ 서비스 기획도 함께 할 수 있고요.생각한 바를 적용하고, 하고 싶은 것을 제안하는데 눈치란 없습니다!"iOS 개발팀은 뷰티에 머무르지 않는 다양한 시도를 하고 있습니다. 서비스 기획, 아이디어 회의도 같이 들어가서 참여하고직접 아이디어를 내기도 하죠!"하고 싶은 게 분명하고, 많은 사람"iOS 개발팀이 함께 일하고 싶은 미미박서입니다. 누구보다 빠르고, 재미있게, 그리고 능동적으로앱을 창조하고 싶으시다면 꼭 지원하세요.태그마스터, 더블개발자(iOS-안드로이드), 디테일장인, 인터렉션 오타쿠 등 당신의 멋진 동료들이 기다리고 있습니다 ! 
조회수 4030

풀스택 개발자, 그것은 환상..

풀스택 개발자라는 용어가 가끔 등장한다. 죄송하지만, 한국에서는 이 용어가 정말 잘못 이해된 상태로 사용되고 있다. 처음에 만들어진 의미와 뜻이 한국에 들어오면서 변한 것을 보는 것이 이번만도 아니다.언제나처럼, 이 '단어'가 의미하는 뜻은 '귤이 회수를 건너면서 언제나 탱자가 되는' 한국적인 환경에서는 매우 이상하게 와전된 의미로 사용되고 있다. 특히나 비개발자들인 경영진들이 그러하고, 개발자들도 가끔 잘못된 의미로 사용한다.와전된 의미의 '풀스택 개발자(Full Stack Developer)'는 프런트엔드와 서버 엔드를 넘나드는 모든 것을 다 아는 전지전능한 개발자인 것처럼 쓰인다. 죄송하지만, 풀스택 개발자의 의미는 프런트-엔드부터 서버-엔드까지 모든 것을 다룰 줄 아는 개발자를 의미하는 것이 아니다.이 '용어'가 쓰이는 분야를 조금은 국한시켜야 할 필요가 있다.그것은 '웹'환경의 프론트 영역으로 국한시키는 것이 매우 현명할 것이다. 다음의 링크를 참조하기를 권한다.http://www.sitepoint.com/full-stack-developer/위의 사이트에 있는 이미지와 단어를 차용한다. 아래의 그림을 살펴보라.[이미지출처 : http://www.sitepoint.com/full-stack-developer/ ]OS부터 Database, WebServer, Server Side Code, Browser, Client Side Code를 아우르는 능력을 가진 사람을 Full-Stack Developer라고 부를 수 있다.좀 더 쉽게 이야기하자면, 'Web'환경은 서버사이드 코드와 클라이언트 사이드 코드를 모두 이해하고 작성되어야 한다. 브라우저( 특히나 변덕스러운 호환성 문제들.. )의 스크립트 환경이 효과적으로 가동되기 위해서는 웹서버의 API를 적절하게 디자인하고 구현된 상태에서 동작되어야 하며, 대부분의 코드들은 직접 Database에 영향을 주는 경우가 많다. 더군다나, 소프트웨어 개발을 하려면 형상관리부터 배포 처리를 위한 기술도 할 줄 알아야 한다.맞다. 'Web'개발 환경에서는 Full-Stack Developer가 되지 않으면 제대로 된 개발이 어렵다. 그래서, '웹'에서는 풀스택 개발자를 지향해야 하고, 매우 당연하게 해당 스킬들을 익숙하게 다루어야 한다.풀스택 개발자는 Web의 개발환경에서는 어쩔 수 없이 매우 당연한 기술적인 한계이고 해야 할 업무를 위해서는 필연적인 형태 인 것이다.이렇게 '웹 환경에서의 풀스택 개발자'는 한국에도 많이 존재한다. 상당수의 PHP개발자 분들이 그러한 '풀스택 개발자'인 경우가 많다.그렇지만, 이 풀스택 개발자의 용어는 '개발'이나 '소프트웨어'를 잘 모르는 경영자의 머릿속으로 잘못 들어가서 마치, iOS나 Android APP도 개발하고 Rest API 디자인이나 구현도 하면서, AWS의 분산 환경에 대한 이해나 개발도 모두 가능한 '전지전능한 개발자'와 같은 의미로 잘못 사용되기도 한다.( 더군다나, 디자인능력이 극도로 필요한 자바스크립트나 능동형 웹-UI를 만들어 내는 능력은 전혀 다른 능력이다 )원래 의미의 '풀스택 개발자'는 '혼자서 웹서비스 하나를 만들 수 있는 개발자'라는 좁은 의미로는 맞다. 하지만, 이를 과도하게 해석하거나 아전인수격으로 해석하는 것은 매우 곤란하다. 그것은 바로 한국적인 특수한 환경 때문에 그러하다.슬프지만, 한국적인 의미의 풀스택 개발자가 존재하기는 하고 있다.프로그래머가 기획도 하면서, 서버 구입부터 설치까지 다진 행하고, DB도 일부 다룰 줄 알면서, 웹이나 클라이언트 프로그래밍의 일부도 할 줄 아는 매우 한국적인 풀스택 개발자가 존재하기는 한다. ( 근데, 그런 개발자들을 풀스택 개발자라고 표현하지 않는다. 거의 기업의 잡부(?)처럼 부려지는 경우다. )노가다 - dokata, 土方 -'막일'을 하는 노가다를 하는 잡부가 한국형 풀스택 개발자라고 표현하겠다.하지만, 그런 테크트리로 형성된 한국형 풀스택 개발자들의 실력은 매우 볼품이 없는 경우가 대부분이다. 필자가 공공 SI현장에서 만난 수많은 한국형 풀스택 개발자들이 그러했다.그들은 컴파일러가 만들어내는 에러 메시지에 대한 이해는 없지만, 10년 넘게 업무를 배운 경험과 대충 Linux나 Windows Server의 기본적인 경험과 온통 스파게티 식으로 구성되어진 소스로 만들어진 더 이상 시장이 커지지 않는 한계가 다다른 시장에서 소프트웨어 개발을 하고 있다.태생적으로 '잡부'가 될 수밖에 없는 작업현장에서 진정한 의미의 풀스택 개발자는 거의 형성되기 어렵다. 이런 한국형 풀스택 개발자들은 실제 하나하나의 스킬들을 확인하거나 체크해본다면 거의 대부분 매우 부족하거나, 특정 기능에만 적합한 일반적으로 쓸모없는 기술들이 대부분일 가능성이 크다고 단언하겠다.이런 경향은 게임업계도 비슷하다. 한국형 풀스택 게임 개발자는 게임 기획부터 스프라이트의 2D부터, 포토샵이나 일러스트레이트도 다룰 줄 알며, 3D Max로 3D도 만들고, Auto-Cad로 도면 데이터도 다루고, DirectX에 Unity도 다루며, 서버나 iOS의 앱까지 만들 줄 안다고 하지만, 정작 그 어느 하나도 제대로 못 다루는 경우가 태반이다.물론, 전부 다루는 사람이 없는 것은 아니다. 있기는 있지만... 그분들 굉장히 유명하거나 특정 기술하나 가 대가의 수준이기 때문에 자신이 가진 다른 기술들을 포함해서 자신을 '풀스택 개발자'라고 포장하지 않는다.하지만, 한국에서 유독 '개발자 구인 광고'를 보면 '풀스택 개발자'를 찾는 곳이 많은 이유는 무엇 때문일까?그것은, 무지한 경영진이나 무지한 비즈니스 모델, 무지한 리소스 활용이 난무하는 헬게이트의 주인들이나 그런 단어들을 주로 사용한다고 보면 된다.100% 단언컨대 한 사람의 개발자가 완벽한 풀스택 개발자라고 하더라도, 요구사항이 발생하고 유지보수업무가 존재하는 업무를 하드웨어적인 서버 관리부터 서버 API, 앱 프로그래밍, 웹 프로그래밍을 하기 위한 스킬은 알 수 있다고 하더라도 그 복잡하고 어지러운 업무량은 모두 다룰 수 없다.만일 그런 것이 가능하다고 이야기하는 경영진이 있거나 무지한 영업맨이 있다면 정신 차리라고 조언해주자. 심지어 그렇게 만들 수 있는 서비스는 존재하지 않고, 존재한다고 하더라도.. 어마어마한 '기술적 부채'가 존재하며, 대부분의 가장 비싼 개발자의 리소스를 그 기술적 부채를 해결하기 위해서 사용되고 있을 것이라고.물론, 그렇게 동작하는 허접하고 쓰레기 같은 코드라고 하더라도, 특정 조건과 특정 환경에서는 서비스가 가능한 경우가 한국에는 많이 존재한다. 경영진이나 영업, 기획은 고객들을 설득하고 고객들이 해당 제품과 서비스를 사용하기 위해서 일부를 희생할 것이다. 그리고, 분명 다른 영역에서 누수가 발생하거나 희생되고 있는 것을 잊지 말자.특히나 경쟁이 없는 제품이거나 더 이상 리소스를 투입하기 어려운 소프트웨어나 서비스의 경우에는 이런 형태로도 동작은 할 것이다. 하루에 한두 번 서버의 Oracle 커넥션을 모두 종료하는 유지보수 행위를 하는 전산실의 업무가 그러한 경우 때문에 벌어진다.중견기업이거나 제조업체, 병원의 전산실에 '야간 당직'업무가 있고, 시스템 모니터링에 민감하다면 대부분 '기술적 부채'를 안고 허접하게 만들어진 것뿐이라고 판단하면 된다.말 그대로, 헬조선의 헬게이트, 헬(!)한 업무환경으로 소프트웨어 개발자로서 비전이 없는 영역이라고 생각하면 된다. 하지만, 그럼에도 불구하고... 스타트업 경영진이나 대기업, 중소기업 경영진들은 '풀스택 개발자'의 환상에 대해서 이야기한다.'모든 것을 다 하는 개발자'가 있으면, 복잡한 커뮤니케이션 비용도 안 들고, 인건비도 적게 들것이라는 착각을 한다. 다만, 이 부분만큼은 명쾌하게 이야기하겠다. '그런 회사 가지 말라'는 것이다.'풀스택 개발자'를 구인하고 있는 회사는 개발자의 무덤이라는 것이다. 대부분 그러하다. 그 이유를 다음과 같이 정리하겠다. 그들이 '풀스택 개발자'를 뽑고 싶은 이유는 간단하다. '돈'이 없어서다. 그리고, 다음의 이유들이 있는 경우이다.하나. 경영진이 요구사항 정의도 제대로 못하므로 개발자와 의사소통에 자신이 없다. 그래서, 풀스택 개발자를 구하려고 한다. 한 명 하고만 이야기하면 될 것이라고 착각한다.둘. 개발자의 인력이 몇 명이 투입되는지에 대해서 평가나 정의가 불가능하므로, 풀스택 개발자를 구하려 한다.셋. 개발자가 두 명, 세명이 있다면 팀 리더도 있어야 하고, 관리자도 있어야 하므로 그 비용을 줄이기 위해서 풀스택 개발자가 필요하다. 한마디로, 돈이 없다.넷. 현대의 웹서비스들을 가동하기 위해서는 최소한의 비용과 인건비가 투여된다. 이 비용을 투자할 정도로 비즈니스 모델에 가치가 없기 때문에 여러 명의 개발자를 고용할 수 없기 때문에 풀스택 개발자를 구하려 한다.다섯. 풀스택 개발자라면 막연하게 다 해줄 것 같은 환상을 가진 경영진이 있는 경우이다. 슬프지만, 전설의 개발자인 '제프 딘'을 고용한다고 하더라도, 삽질을 할 것이다.물론, 스타트업에 초기에 합류하면서 CTO의 역할을 부여받았다면 조금은 입장이 달라진다. 정당한 지분을 받고, 미래의 가치에 대해서 나눌 수 있다면, 해당 롤을 가진 사람은 알아서 '풀스택 개발자'가 될 가능성이 크다. 그러므로, 매우 당연하지만 CTO는 풀스택 개발자에 근접되면 좋기는 할 것 같다. 하지만, 현실적으로는 그렇게 세팅하지 못하는 경우가 대부분이다.그리고, 냉정하게 초기 개발이나 Lab수준, 시리즈 A를 투자받기 전의 '소프트웨어'나 '서비스'는 대부분 비즈니스 모델을 증명하는 수준에서 끝내는 것이 바람직하다. 굳이, 환상의 개발자나 풀스택 개발자가 아니라도 비즈니스 모델을 검토하고 증명하는 모델을 구현하는 것은 충분하게 가능한 경우가 대부분이다.사용자가 수백만 명도 아니고, 구현된 기능들도 수백 가지가 아니며, 아직은 스파게티 식으로 구성하더라도 무방하기 때문이다. 해당 기술적 부채는 서비스의 증명 후에 해당 코드는 버려지고, 다시 개발팀을 제대로 세팅하여 구현하면 되기 때문이다. 더군다나, 대부분의 스타트업은 고속 개발을 해야 하기 때문에 '풀스택 개발'이 가능한 '웹'만으로는 모든 것을 커버하기 어려울 것이다.좌우지간, 간단하게 이야기해서 '풀스택 개발자'타령하는 구인광고를 보게 된다면, 그 회사나 팀은 무언가 잘못 생각하고 있거나, '돈'이 없는 조직이라고 생각하면 된다. 거기에, '기술'이나 '개발'에 대해서는 아무것도 모르는 사람이 사장이 존재하는 곳이라고 생각하면 된다.헬게이트에 입성하고픈 개발자라면 '풀스택 개발자'를 구인하는 곳으로 가면 된다. 엄청난 '일'의 쓰나미를 경험하고, 인성이 피폐해지는 것을 경험할 것이다.필자는 국내 최고의 개발자들을 여럿 알고 있다. 하지만, 그분들은 자신들을 '풀스택 개발자'라고 이야기하지 않는다. 그 용어가 의미하는 것 자체가 '날림'이라는 것을 너무도 잘 알고 있기 때문이다. 물론, 10년 20년을 소프트웨어 개발을 하다 보면 얻어지는 경험과 지식들이 있다.궁극적으로는 풀스택 개발자가 이야기하는 비슷한 테크트리를 대부분 알고는 있게 된다. 하지만, 경력 20년 되고 하나의 도메인에 익숙하며, 특정 분야의 대가인 분들을 스타트업에서 고용한다는 것은 거의 불가능에 가깝다. 간혹, 그런 분들이 직접 스타트업을 하는 것이라면 모를까 말이다.이제 이야기를 마무리하겠다.'웹 개발'을 하려면 '풀스택 개발'을 지향하는 것은 맞다. 하지만, 그것 자체가 완벽한 풀스택 개발을 의미하는 것이 아니라는 것을 생각하기 바란다. 그리고, 경영진이나 비개발자들에게도 다시 한번 이야기한다. '풀스택 개발자'를 구인하겠다는 환상을 버리기 바란다.그런 사람 없고, 있다고 하더라도... '풀스택 개발자'를 구인하겠다는 발상으로는 절대 초빙하거나 모실 수 없다는 것을... 깨몽 하기 바란다.물론, '풀스택 개발자'처럼 이것 저것 다하는 정성스럽고, 일에 애정 넘치는 개발자들을 제대로 대우해주시기를... 기술로써의 풀스택 개발자가 아니라, 그 기업이 원하는 일을 풀스택 개발자처럼 일할 뿐이다. 그들에 대한 애정 넘치는 말한마디... 경영진들에게 부탁드린다.갑자기, '풀스택 개발자'에 대한 환상에 대해서 정리하고 싶어서 한 번에 글을 써 내려갔다. ~.~

기업문화 엿볼 때, 더팀스

로그인

/