달이 차오른다 경험을 좋아하는 개발자 https://samslow.github.io/ Sat, 28 May 2022 16:11:34 +0000 Sat, 28 May 2022 16:11:34 +0000 Jekyll v3.9.2 https://samslow.github.io/logo.png React Code Review를 잘하기 위한 10가지 질문 <p>이 글은 <a href="https://www.chakshunyu.com/blog/this-is-my-10-questions-react-code-reviewing-routine/">This Is My 10 Questions React Code Reviewing Routine</a> 를 작성자인 <a href="https://twitter.com/keraito">Chak Shun Yu</a> 의 동의를 받고 작성되었습니다. 글의 재미를 위해 적절한 의역이 있지만, 만약 오역이 있다면 댓글로 알려주세요.</p> <blockquote> <p>원글은 Merge Request(MR)라고하지만, 이 글에서 핵심이 아니기 때문에 전통적인 표현인 PR을 사용합니다</p> </blockquote> <hr /> <h1 id="이-글을-쓰는-이유">이 글을 쓰는 이유</h1> <p>어떤 개발자인지에 상관없이 코드리뷰는 엔지니어링 팀에서 일할 때 일상적인 책임의 일부가 되었습니다. React 개발자도 예외는 아니죠. 더 나은 React 코드를 작성하는 방법을 알려주는 글은 많지만, React 코드 리뷰를 잘하는데 데 도움이 되는 블로그, 비디오 또는 자습서는 거의 없습니다.</p> <p>물론 동료의 코드를 리뷰하는 것은 개발자로서의 우리 책임의 중요한 부분이지만, 많은 개발자가 기대하는 책임은 아닙니다. 그들은 코드를 작성하는 대신 읽는 것이 지루하다고 느끼고, 자신의 리뷰가 의미가 없으며, 그저 리뷰어로서 머리수를 채우는 것일 뿐이라고 생각합니다.</p> <p><img src="https://c.tenor.com/7LgMNfVC2H0AAAAC/buffy-chair-naughty.gif" alt="boring" /></p> <p>개인적으로 저는 PR마다 같은 의견을 리뷰하곤 했고 코드 리뷰를 별로 좋아하지 않았습니다. 그러나 이 주제에 대해 많은 시간을 보내고 React 개발자로서 다양한 측면을 살펴본 결과, 개발에서 발생하는 문제가 React 코드를 제대로 리뷰하는 방법을 몰라서 발생했다는 사실로 귀결된다는 사실을 알게 되었습니다.</p> <p>원래 제 루틴은 PR을 열고 생각 없이 모든 코드를 위에서 아래로 읽고 제가 발견한 모든 항목에 대해 코멘트를 추가하는 것이었습니다. 당연히 이 과정은 재미없었고 제 리뷰의 품질은 자연스럽게 별로 좋지 않았습니다.</p> <p>요즘 저는 항상 계획과 주제를 염두에 두고 React 코드를 검토합니다. 이 일을 시작한 이후로 리뷰가 지루하지 않았고 시간이 지날수록 리뷰의 질이 높아졌다고 동료들에게 피드백을 받았습니다.</p> <p>저는 이제 동료들의 코드를 검토하는 것을 정말 즐깁니다. 그들의 코드를 검토함으로써 그들의 코딩 스타일에 대해 배우고, 코드를 이해하고, 코드에 대해 질문하고, 새로운 것을 배울 수 있습니다. 궁극적으로는 코드 품질을 향상시키기 위해 작업에 대한 더 좋은 다른 방법을 제공 할수도 있고요.</p> <p>이 글은 제가 React 코드를 검토하는 동안 스스로에게 묻는 모든 질문을 공유합니다. React 코드를 검토하는 방법을 잘 모르거나 무엇에 중점을 둬야 할지 모르겠다면 이 질문들이 시작하는 데 도움이 될 것입니다. 이것들을 기초로 제가 했던 것처럼 검토 체크리스트를 만들고, 의식적인 프로세스를 리뷰하기 시작하고, 팀에 더 의미 있는 리뷰를 제공할수 있고, 어쩌면 리뷰를 즐기기 시작할 수도 있습니다. 🙂</p> <h2 id="1-이-코드가-동작하나">1. 이 코드가 동작하나?</h2> <p>리뷰에서 가장 중요한건 역시 코드가 작동하는지 확인하는 것입니다. 이건 코드 자체에서 쉽게 확인할 수 는 없지만 그래서 대부분 CI에 의존하거나 로컬에서 직접 코드를 확인하죠. 또한 PR까지 올라온 거라면.. 여러분은 당연히 동작할거라고 믿을 거고요 ^^;</p> <p><img src="https://c.tenor.com/aP_vrzh86ukAAAAd/cat-kitty.gif" alt="fixing-cat" /></p> <div align="center" style="color: gray; margin-bottom: 15px;">하나를 고치면 어디선가 또 문제가 생기는 험난한 디버깅 여정</div> <p>그러나 그 모든 것에도 불구하고 코드를 읽을 때 코드 동작할지 의심하는 것은 좋은 자세입니다. 최악이라고 해봤자 여러분은 코드를 더 잘 이해할 수 있게 되고, 최선의 시나리오는 코드 제안자가 놓치고 있던, 어쩌면 품질을 개선하는 데 도움이 될 수 있는 몇 가지 디테일을 알 수 있게 될테니까요.</p> <h2 id="2-pr이-무엇을-위한-것인지-이해하고-있나">2. PR이 무엇을 위한 것인지 이해하고 있나?</h2> <p>종종 리뷰어는 PR을 볼때 자기가 Linter나 다른 정적 분석 도구인 것처럼 검토합니다. 그들은 코드 구현에만 주의를 기울이고 모든 것이 제대로 구현되었는지 확인하죠. 하지만 문제는 정적 분석 도구가 동일한 작업을 더 빠르고 효율적이며 안정적으로 수행할 수 있다는 것입니다. 코드 디테일에 주의를 기울이는 것은 좋지만, 코드가 실제로 무엇을 하기위한 것인지 이해하는것이 포인트가 되어야 합니다.</p> <p><img src="https://c.tenor.com/6LHoxNXuK20AAAAC/spy-skykids.gif" alt="magnifying-glass" /></p> <div align="center" style="color: gray; margin-bottom: 15px;">내가 다 린팅한다!</div> <p>PR에서는 정적 분석 도구도 할수있는 일보다는 코드 제안자와 함께 생각하는게 중요합니다. 더 의미 있는 피드백을 제공하기 위한 것이든, 향후 작업을 예상하기 위한 것이든, 먼저 무슨 일이 일어나고 있는지 이해해야 합니다. 그러나 그렇게 하려면 먼저 PR에서 무슨 일이 일어나고 있는지 이해해야 합니다. 여기에는 맥락, 목적 및 구현과 같은 모든 것이 포함됩니다.</p> <h2 id="3-코드가-가독성있나">3. 코드가 가독성있나?</h2> <p>우리는 작동하는 코드에 만족해서는 안 됩니다. 작동되는 코드는 최소한의 요구 사항이어야 하지 결정적인 요소가 되어서는 안됩니다. 우리는 결국 코드베이스에 병합되어 사용자에게 배포되는 코드를 유지 관리하는 것입니다. 그러나 코드 자체와 달리 코드를 만든 개발자는 영원히 함께 작업할 수 없습니다. 제작자 자신조차도 몇 달 후에 자신의 코드를 이해하는 데 어려움을 겪을 가능성이 큽니다.</p> <p>이러한 이유로 읽기 좋은 코드를 갖는 것은 중요합니다. 더 읽기 쉬운 코드가 있다는 것은 다른 개발자가 코드를 이해하고 향후 유지 보수 관련 작업을 더 쉽게 수행할 수 있다는 것을 의미합니다. 구체적인 예로는 <a href="https://www.chakshunyu.com/blog/how-to-write-readable-react-content-states/">콘텐츠 상태</a>, <a href="https://www.chakshunyu.com/blog/react-readability-analysis-of-inline-conditional-rendering/">인라인 조건부 렌더링</a>, <a href="https://www.chakshunyu.com/blog/react-readability-analysis-of-implementing-custom-hooks/">커스텀 훅</a>뿐 아니라 코드를 배치할 위치, 구성, 변수 및 함수의 이름 등이 있습니다.</p> <p>그러나 가독성은 특정 코드 패턴이나 주제에만 국한되지 않습니다. 개발자가 코드를 이해하기가 얼마나 쉬운지에 관한 것입니다. 결국 가독성은 PR에서 주요한 요소이고, 이는 엔지니어링 팀의 장기적인 투자가 필요합니다.</p> <h2 id="4-컴포넌트나-훅이-너무-크진-않나">4. 컴포넌트나 훅이 너무 크진 않나?</h2> <p>소프트웨어 개발의 고전적인 안티 패턴은 소위 God Object 입니다.(역자. 뭐든 알아서 해주는 객체). 이것은 모든 책임을 저장하는 object, class 또는 function와 같은 모든 프로그래밍 요소를 나타냅니다. God Object는 너무 많이 알고 너무 많이 하며 너무 많은 맥락을 포함합니다. 결과는 유지 관리, 이해, 리팩토링, 작업이 어렵고 매우 취약한 괴물이 되어버리고 말죠.</p> <p><img src="https://i.pinimg.com/550x/20/b6/ee/20b6ee7214b1f733f9fc995572f7a42b.jpg" alt="macguiver" /></p> <p>God Object 말고도 우리는 React 개발에서 피해야 할것이 몇가지 더 있습니다. 특히 재사용성, 확장성, 단일 책임원칙같은 것들입니다. React 컴포넌트와 훅을 보며 그 목적을 이해하고, 너무 많은 일을 하고 있는지 확인하세요. 만약 이런 문제가 있다면 코드 품질이 떨어지게 하지 않기 위해 컴포넌트나 훅으로 분리하고 추상화하여 이 문제를 해결하는 것이 좋습니다.</p> <h2 id="5-컴포넌트나-훅이어야-하나">5. 컴포넌트나 훅이어야 하나?</h2> <p>4번처럼 충분히 추상화하지 않아 잠재적으로 God Object 훅이 만들어질 수 있는 반면, 너무 많은 추상화는 꼭 좋지많은 않습니다. 모든 것을 구성 요소나 훅으로 만드는 것은 궁극적으로 React 코드 품질을 저하시킬 수도 있습니다.</p> <p>레이어를 여러개로 분리하면 Prop Drilling이나 React의 안티 패턴을 방지할 수 있습니다. 모든 로직을 담는 커스텀 훅은 너무 많은 추상화가 발생하여 코드베이스가 체계화되지 않고 어수선하게 될 수 있거든요.</p> <p>이를 염두에 두고 그 구성이 필요한지 여부를 끊임없이 자문하면 실제로 코드를 건드리지 않고도 코드 아키텍처에 기여할 수 있게 되겠죠?</p> <h2 id="6-이-api-디자인을-단순화할-수-있나">6. 이 API 디자인을 단순화할 수 있나?</h2> <p>함수 매개변수든, React 컴포넌트의 Props든, 커스텀 훅 매개변수든 이를 구현하는 것은 쉬운 일이 아닙니다. 결국 API 디자인의 한 형태입니다. 특히 React props를 사용하면 복잡하고 중복되는 API가 양산되기 만들기 쉽습니다.</p> <p>비슷한 UI를 가진 두 개의 컴포넌트가 boolean props을 가졌다면 enumeration props를 고려하는 것이 더 나을 수 있습니다. 항상 함께 사용하는 두 개의 props가 있다면 하나로 결합하는 것이 더 나을 테니까요. enumeration 또는 boolean prop가 특정 분기에만 관련이 있다면, 컴포넌트를 분할하는 것이 좋습니다. 각 컴포넌트가 배열 및 렌더함수를 전달만 하는 경우 렌더 props 패턴을 고려하는 것이 더 나을 수 있습니다.혹은 prop drilling을 많이 하는 경우 컴포넌트 합성 패턴을 고려해 볼 가치가 있습니다.</p> <p>위 사항은 React props의 API 디자인을 고려할 때 제안할 수 있는 구체적인 예입니다. 그러나 일반적으로 이러한 특정 예제나 props에만 국한되지 않고 유틸리티 함수 및 커스텀 훅에도 적용될 수 있습니다. 가장 중요한 것은 이를 염두에 두고 코드 제안자의 API 설계를 이해하고 그에 따라 피드백을 제공하는 것입니다.</p> <h2 id="7-테스트가-있나">7. 테스트가 있나?</h2> <p>리뷰할 때 테스트를 고려하라는 것이 무슨 의미일까요? 테스트 코드는 늘 있어야 한다고 모두들 생각하지만 실전 리뷰에서 테스트가 얼마나 자주 잊혀지고 무시되는지 알기에 이에 실망하죠. 하지만 새로운 기능, 수정 사항 또는 일반적으로 코드가 테스트되었는지 확인하는 것은 엔지니어링 팀에 장기적으로 엄청난 도움이 될 것입니다.</p> <p>여기에는 새 코드에 대한 새 테스트 작성뿐만 아니라 이미 있던 이전 테스트 수정도 포함됩니다. 일반적으로 PR은 그 목적과 추가되는 코드를 마치 문서처럼 볼수있도록 테스트를 하나 이상 갖는 것이 최소 요구 사항이어야 합니다. 그렇지 않은 경우 팀이 이를 처리하는 방식에 따라 Request Change가 필요할수 있겠죠.</p> <h2 id="8-테스트가-의미있나">8. 테스트가 의미있나?</h2> <p><img src="https://thumbs.gfycat.com/DrearyExhaustedGreatdane.webp" alt="test" /></p> <p>어떤 사람들은 Test가 있는게 없는 것보다 낫다고 주장하지만 저는 그 의견에 전적으로 동의하지 않습니다. 일반적으로 Test는 코드베이스의 코드 품질을 보장하는 첫 번째 단계일 수 있지만 단순히 PR을 넘기기 위한 기만적인 단계일 수도 있습니다. 잘못된 테스트는 잘못된 보안 감각, 실제로 작동하지 않는 배포 코드, 시간과 노력 낭비로 이어질 수 있습니다.</p> <p>코드 제안자가 테스트를 포함했다면 그들이 설정, 트리거 및 검증같은 필수로 해야 할 일을 하고 있는지 확인하세요. 구현 세부 정보에만 의존적이진 않은지, 설정은 맞는지, 적절한 상태관리가 되고있는지. 다른 모든 부분은 매우 다르게 구현할 수 있지만 아주 작은 변경이라도 테스트의 신뢰성에는 상당한 영향을 미칩니다.</p> <h2 id="9-이-기능의-접근성-면은-어떻게-되어있나">9. 이 기능의 접근성 면은 어떻게 되어있나?</h2> <p>웹 개발의 필수적인 부분은 모든 유형의 사용자가 애플리케이션에 액세스할 수 있어야 한다는 것입니다. 불행히도 모든 웹 응용 프로그램이 Screen Reader, 키보드를 사용한 제어 또는 기타 그룹에 최적화된 것은 아닙니다.</p> <p>어떤 개발자도 존재하는 모든 기능에 대해 적절한 접근성을 구현하는 방법을 알지 못할 것입니다. 그렇다면 당신이 할일은 명백합니다. 검토자로서 이 주제를 인지하는 사람이 되어 제안자가 접근성을 구현하는 것을 잊었다면 상기시키거나 리소스를 제공하거나 필요한 경우 먼저 코드 제안을 하면 됩니다.</p> <h2 id="10-해당-문서를-최신화-했나">10. 해당 문서를 최신화 했나?</h2> <p>기존 코드를 리팩토링하거나 변경할 때 가장 자주 잊어버리는 것은 문서화입니다. 코드 주석, 문서 또는 README이든, 그것들이 최신 상태라고 가정하는 것은 항상 위험한 행동입니다.</p> <p>업데이트해야 하는 공식 문서, 문서 페이지 또는 주석이 있다는 것을 알고 있다면 리뷰에서 확실히 말해주세요. 특히 코드 제안자가 코드베이스의 특정 부분에서 정상적으로 작동하지 않을 때 기존 문서의 모든 부분을 아는 것은 거의 불가능합니다. 문서를 최신 상태로 유지하는 것은 당신을 포함한 팀의 노력이 있어야 합니다.</p> <h1 id="그리고-코드리뷰에-대한-나의-생각">그리고 코드리뷰에 대한 나의 생각</h1> <p>동료들의 코드를 리뷰할때 중요한 건 무엇에 집중해야 하는지 알고 이를 의식적인 프로세스로 만드는 것입니다. 불행하게도, 많은 개발자들은 이렇게 하지 않아 검토가 지루하고 싫증나며 의미없다고 느낄 수 있습니다.</p> <p>하지만, 실제로 코드 리뷰는 그런 느낌이 들지 않게 할수 있을 뿐더러 팀의 코드 품질을 올릴 수 있는 효과적인 방법입니다. 이걸 돕기위해 여기서는 당신이 React 코드 리뷰를 하며 스스로 질문해야할 10가지 질문을 제시합니다. 이 질문들은 당신이 집중해야할 주제를 깨닫고 리뷰 루틴을 만드는데 도움을 주고, 당신의 리뷰 품질을 강화시키는데 도움을 줄것입니다.</p> <hr /> <h1 id="역자의-변">역자의 변</h1> <p>실제로 여기있는 10가지 질문은 평소에 지나쳤던 사소하지만 중요한 내용에 대해서 다루고 있습니다. 어쩌면 애써 무시하려고 했던 저의 모습이 떠올라서인지 뜨끔하면서 읽게 되기도 했는데요. 저 또한 이런 질문들을 보고 공감했으며, 더 많은 개발자 분들이 보고 한국 Frontend 생태계가 나아졌으면 해서 이 글을 번역하게 되었습니다.</p> <p>이 글을 읽는다고 10가지 질문을 모든 PR 에서 할수는 없겠지만, 계속해서 의식하고 리뷰하는 문화를 만들어가다보면 더 나은 개발자가 될수 있겠죠 ?</p> <p>끝까지 읽어주셔서 감사합니다.</p> Sat, 28 May 2022 00:00:00 +0000 https://samslow.github.io/development/2022/05/28/best-pr-reviewer/ https://samslow.github.io/development/2022/05/28/best-pr-reviewer/ web development SameSite는 무엇인데 이리도 헷갈리게 한다는 말이냐 <ul> <li>SameSite에 대해 처음 알았을땐 사실 도메인에 대한 기준에 별 관심이 없어서 그냥 지나쳤었는데, 최근에 이와 관련해서 테스트 해보고 여러 의견을 나눠본 결과.. 매우 중요 하다는 것을 알게되었다.</li> </ul> <p>그 이유는 same-site는 cookie를 서로 공유할수 있기 때문이다.</p> <p>이 글은 cookie의 설정값을 알아보기 더불어 same-site의 정확한 기준이 무엇인지 기록한다.</p> <h1 id="엥-우린-cookie-잘-쓰고-있는데-왜그러세요">엥;; 우린 cookie 잘 쓰고 있는데 왜그러세요</h1> <p>맞다. 잘 쓰고 있었을 것이다. 왜냐하면 원래 cookie에 대한 정책 자체가 그렇게 빡세지 않았다.</p> <p><img src="https://uc8c2e13ba114b03c0e50f50772b.dl.dropboxusercontent.com/cd/0/inline/BW0PSqXFO7faD8KixHst1qu5ypUm8tA6HZE0z7IShwNFG9GKCv89VrXmNK9e_SPuWfArdElsf4qb5V4lxdIm70RBE6s1_SFFvBe0r4rIRROvkpQiPtRztnAjGWSfXA_ZeHP91nJtMUHuY5j0uoVgpIU2/file#" alt="브라우저 점유율" /></p> <p>현재 가장 널리 쓰이고 있는 Chrome 브라우저 기준으로 Chrome 84 버전부터 cookie 정책이 바뀌게 되었다.</p> <p>(아마 이때 많은 개발자들이 혼란을 겪었으리라)</p> <p>cookie의 속성중 <code class="language-plaintext highlighter-rouge">SameSite</code> 라는 속성이 Chrome 84 부터는 아무 설정이 없으면 <code class="language-plaintext highlighter-rouge">Lax</code> 를 기본으로 설정하게 된다.</p> <p>SameSite의 속성에는 아래 3가지가 있다.</p> <ol> <li>Strict: 같은 도메인에서만 접근 가능</li> <li>Lax: a tag, link tag 같은 곳을 통해서 이동했을때만 cookie 전송됨.</li> <li>None: cross-site에서도 쿠키 전송 가능해짐. 단, Secure 옵션을 추가해야함. 그렇지 않으면 거부됨 (ex.<code class="language-plaintext highlighter-rouge">SameSite=None; Secure</code> )</li> </ol> <p>의도한것이 아니라면 <code class="language-plaintext highlighter-rouge">SameSite=None;</code> 이 사용되지 않도록 하려는 것이 보인다.</p> <h1 id="cookie의-전송이-무엇을-말하는데">cookie의 ‘전송’이 무엇을 말하는데?</h1> <p>cookie의 전송에는 <code class="language-plaintext highlighter-rouge">브라우저 → 서버</code>와 <code class="language-plaintext highlighter-rouge">서버 → 브라우저</code> 가 있다.</p> <p>원래 브라우저는 도메인별로 쿠키를 가지고 있고 다른 도메인으로 이동한다고 쿠키가 삭제되지 않는다.</p> <p>GET이나 POST 등의 요청을 서버로 보낼때 브라우저는 현재 가지고 있는 cookie를 서버로 보내도록 되어있다.</p> <p>반대로, 서버는 클라이언트로 응답을 보낼때 set-cookie라는 속성으로 브라우저에게 cookie를 저장하라고 명령할수 있다.</p> <p>자, 이제 위에서 나온 same-site 규칙을 기반으로 이 상황을 다시 보면 클라이언트가 아무리 웃기는 짬뽕짓을 해서 cookie를 갖고 뭘 해도 same-site 규칙이 올바르게 정해져 있다면 다른 도메인의 cookie가 실수로 전송 될 일이 없다. 서버는 의도하지 않은 도메인에 cookie를 전달해도 이 세팅에 의해 cookie는 전송되지 않을 것이다.</p> <h1 id="그럼-same-site를-판단하는-기준은-뭘까">그럼 Same-site를 판단하는 기준은 뭘까</h1> <p><code class="language-plaintext highlighter-rouge">www.google.com</code></p> <p>과</p> <p><code class="language-plaintext highlighter-rouge">aaa.google.com</code></p> <p>즉 서브 도메인만 다른 경우 SameSite인가? 이고 결론은 SameSite 이다.</p> <p>다만 하나 알고가야할건, 이를 나누는 기준이 <a href="https://publicsuffix.org/list/public_suffix_list.dat">Public suffix</a>(공개 접미사)에 명시된 최상위 도메인을 비교한다는 것이다. suffix가 <code class="language-plaintext highlighter-rouge">.com</code> 이라면 suffix 앞까지를 하나의 site로 보는 것이다. 위 예시에서는 <code class="language-plaintext highlighter-rouge">google.com</code>이 하나의 site이다.</p> <p>그래서,</p> <p><code class="language-plaintext highlighter-rouge">a.google.com</code></p> <p>과</p> <p><code class="language-plaintext highlighter-rouge">b.google.com</code></p> <p>은 SameSite이지만,</p> <p><code class="language-plaintext highlighter-rouge">a.github.io</code></p> <p>와</p> <p><code class="language-plaintext highlighter-rouge">b.github.io</code></p> <p>는 cross-site 이다. 여기서는 io가 public suffix가 아니고 <code class="language-plaintext highlighter-rouge">github.io</code> 가 suffix이기 때문에 <code class="language-plaintext highlighter-rouge">a.github.io</code> 가 하나의 site이기 때문이다.</p> <p>또한 suffix도 <code class="language-plaintext highlighter-rouge">apigee.io</code> 같이 .io로 다양하게 있지만 현재 도메인에 적용되는 제일 긴 suffix를 기준으로 한다는 점도 잊지 말아야 한다.</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://web.dev/samesite-cookies-explained/#explicitly-state-cookie-usage-with-the-samesite-attribute">web.dev</a></li> </ul> Sat, 25 Sep 2021 00:00:00 +0000 https://samslow.github.io/development/2021/09/25/samesite/ https://samslow.github.io/development/2021/09/25/samesite/ web development 개발자로 이직하기(feat. Toss NEXT 개발자 채용를 마치며) <ul> <li><a href="https://toss.im/career/next-developer-2020">2020 토스 Next 개발자 채용</a> 과정을 겪으면 느꼈던 점을 기록했습니다.</li> </ul> <h1 id="개발자로-이직하기feat-toss-next-개발자-채용를-마치며">개발자로 이직하기(feat. Toss NEXT 개발자 채용를 마치며)</h1> <p>필자는 2020년 7월 20일부터 접수를 시작한 토스 3년차 미만 개발자 공개채용에 지원했다.</p> <p>보통 기업들의 공개채용은 상·하반기를 나눠 3월과 9월에 채용이 열리고 모든 과정을 진행하는데에는 평균적으로 3개월정도가 소요된다.</p> <p>하지만, 토스의 경우 단 1달만에 모든 전형을 진행다는 점에서 <code class="language-plaintext highlighter-rouge">토스답다</code> 라는 생각이 들었는데 그 이유는 전형의 큰 틀은 비슷했지만 디테일한 부분이 지원자를 생각한게 느껴졌기 때문이다.</p> <p>이 포스팅에서는 이번 채용을 진행하며 느꼈던 점들을 솔직하게 기록하고, 토스를 준비하는 다른 경력직 분들에게 도움이 되었으면 해서 적는다.</p> <p>또한 경험 자체가 기존의 채용 절차에서 느낀점과 꽤 달랐기 때문에 그 경험을 적고자 한다.</p> <p>다만, 모든 코딩테스트 문제를 공유한다거나 어떤 질문이 오갔는지에 대한 디테일한 설명은 제외하고 전형의 진행방식에 대한 느낀점 위주로 작성했다.</p> <p>각 유형별 난이도에 대한 설명은 지극히 개인적인 것이니 너무 신경쓰지 말고 읽어주시길 바란다.</p> <h1 id="시작이-반이다-서류작성부터">시작이 반이다 서류작성부터</h1> <p><img src="https://www.dropbox.com/s/reecvotpcga8lh0/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-08-27%2020.59.02.png?raw=1" alt="전형정보" /></p> <div align="center" style="color: gray">토스 NEXT 개발자 채용 전형 일정</div> <p>2020년을 관통하는 키워드는 COVID 19(a.k.a 코로나)이고 불행하게도 현재도 진행중이다.</p> <p>이미 모든 기업들이 대면 채용대신 비대면 채용으로 전환해서 진행하고 있었고 토스또한 모든 전형을 처음부터 끝까지 비대면으로 진행한다고 했다.</p> <p>이미 몇몇 기업들이 비대면 채용을 잘 진행 한 곳이 있었고 필자도 이미 경험 해 보았기 때문에 새로울 건 없었다.</p> <p>이번 공개채용은 토스팀에서 처음으로 열린 것이었고 보통은 내부 추천 혹은 상시 채용 공고로 주로 구인한다고 한다.</p> <p>올해 3월에 다니던 스타트업을 퇴사하고 다음 회사부터는 내가 세운 ‘회사는 이래야 한다’라는 기준을 갖고 찾았다.</p> <p>당연히 나는 그 기준을 맞추기 위해 나 또한 <a href="https://github.com/Febase/FeBase">Febase</a>같은 스터디를 운영하며 스스로 단련하려 노력했다.</p> <p>주로 K사나 N사같은 대기업이 내가 생각한 청사진과 유사해서 이쪽 기업위주로 알아보았고 기술 면접을 진행했었다.</p> <p>그러는 동안 토스는 나에게 대기업보다 더 큰 산이라는 생각에 지원조차 하지 않았고, 실제 과거 토스에 재직중인 지인의 권유에도 “다음에 언젠가”라며 운을 떼기도 했었다.</p> <blockquote> <p>“토스는 개발자를 갈아서 연봉이 높은것이다.”</p> </blockquote> <blockquote> <p>“토스 퇴사율은 연봉만큼 높다.”</p> <p>“워라밸을 포기하고 가는 회사다.”</p> </blockquote> <p>위의 생각들이 지원을 주저하게 만드는 큰 원인이지 않았나 싶다. 저 문장들은 내가 그 환경에서 살만한 개발자인가 싶은 생각이 들게 만든다.</p> <p>하지만 이번에 열린 TOSS NEXT 개발자 채용은 나같이 토스팀에 대한 두려움 있는 사람들에게 그 벽을 허물어줄 수 있는 기회가 되었다.</p> <blockquote> <p>‘3년 차 이하 개발자 채용’</p> </blockquote> <blockquote> <p>‘학력 및 전공 무관’</p> </blockquote> <blockquote> <p>‘이력서는 과제전형까지 합격자 한해 제출’</p> </blockquote> <p>이 3가지가 내가 지원버튼을 누를 수 있게 만들어 주었고 롤모델로 삼던 분이 최근 토스에 입사하셨다는 말을 듣고 지원이라도 해볼까 하는 생각이었다.</p> <p>지원버튼을 누르면 이름과 이메일정도면 몇 초만에 지원이 가능하고 코딩테스트 링크가 메일로 온다.</p> <p>그래서 그런지 이번 공채는 서버, 프론트, 클라이언트 등 총 20명을 선발하는 자리였는데 5,000명이나 되는 인원이 지원한 이유일 것이다.</p> <p>하지만 지원은 쉬웠더라도 코딩테스트까지 쉬운것은 아니다.</p> <h1 id="코딩-테스트라-부르고-실무-문제-해결이라-쓴다">코딩 테스트라 부르고 실무 문제 해결이라 쓴다</h1> <p>기본적으로 일반적인 IT 회사에서 이루어지는 코딩테스트는 DFS, Brute Force, Dynamic Programing , ··· 등 실무에서 쓰려면 어떻게든 쓰겠지만 그다지 사용되지 않는 알고리즘을 검증하는 문제들이 거의 대부분이고, 이는 ‘실제로는 쓰이지 않는것을 공부해야 한다’는 생각이 들며 지원자로 하여금 어떤 괴리감을 들게 한다.</p> <p>또한 어떤 기업은 Frontend, Backend, DataScience ··· 등 포지션의 구분이 없이 일관된 코딩 테스트로 보니 이 부분에 대해선 더 얘기할 건 없다.</p> <p>하지만 위의 예상과 완전 정 반대로 이번 토스 코딩테스트는 총 9문제로 모두 실무에서 볼법한 문제들로 위주였고 <strong>포지션별로 서로 다른</strong> 코딩 테스트문제가 주어졌다. 아마 문제들이 실무에서 마주칠만한 문제였다는 점은 모든 지원자들이 공감하는 부분이 아니었을까 한다.</p> <p>공개채용의 이름이 <strong>‘3년차 이하 경력자’</strong>인 것이 문제 출제의 힌트라면 힌트일 수 있겠다.</p> <p>필자 또한 1년 반정도의 실무 경력을 갖고있어서 알고리즘보다 더 자신있었다. 또 네 다섯 문제는 실무에서 한번씩 마주쳤던 문제였기 때문에 정답을 inline으로 간단하게 풀었지만 나와 같은 포지션에 지원한 다른 경력이 없던 대학생 개발자는 문제는 어렵지 않았지만 정석대로 풀다 시간 부족으로 많이 풀지 못했다고 했다.</p> <p><img src="https://pbs.twimg.com/media/EgMN9OvU0AUUTxQ?format=jpg&amp;name=medium" alt="아시아 최상위 대학" /></p> <div align="center" style="color: gray">알고리즘이 아니라 실무 문제 해결이라면.. '나'도?</div> <p>국내 다른 기업들이 코딩 테스트에 P사의 플랫폼을 사용하는것과는 다르게 G사 플랫폼을 사용 한 점도 맘에 들었다. P사가 기업 코딩 테스트를 대부분 가져가니 너무 독과점이 되어버릴까 괜한 걱정을 하던 나로썬 토스가 ‘보이지 않는 손’ 역할을 한 것 같아 보였기 때문이다.</p> <p>필자도 물론 9문제를 다 풀지는 못 했지만 100점이 나오지 않더라도 최대한 빠르게 다음 문제로 넘어가려고 했다.</p> <p>평소 다른 기업 코딩 테스트에서는 각 문제당 보통 최소 40분 이상이 주어지지만 토스의 문제당 시간을 계산 해 보니 10분안에 하나씩 풀어야 했기 때문에 엣지 케이스까지 챙겨가며 하기보다는 한 문제라도 더 푸는게 낫다고 생각했다.</p> <p>필자는 운이 좋게도 다음 전형인 과제 전형으로 넘어갈 수 있었는데 응시자 입장에선 이번 테스트는 실무 위주로 한 Algoless한 문제들이 3년 이하 경력직들에게는 Algoful한 것보다 훨씬 유리하게 다가왔기 때문이다.</p> <p>시험 종료 후 테스트에 응시한 몇몇 지인 개발자들이 있길래 문제 난이도에 대해 물어보니 “시간은 부족했지만 문제가 엄청 어려운건 아니었고 문제 풀이의 경험이 매우 좋았다”라는 분위기였다.</p> <p>필자도 이번 전형으로 서류에서 느낀 <code class="language-plaintext highlighter-rouge">토스답다</code> 를 다시 느낄 수 있어서 합/불 여부과 상관없이 문제를 푸는 과정 자체 만으로도 기분이 좋았다.</p> <h1 id="운수-좋은-날--과제-전형">운수 좋은 날(?) : 과제 전형</h1> <p>이전까지 겪어본 다른 전형에서는 보통 코딩테스트 이후에는 바로 테크 인터뷰가 있었고 그게 아니면 서류 전형 이후 코딩테스트가 아닌 과제 전형을 진행하고 테크 인터뷰로 넘어간다.</p> <p><strong>하지만 이번 토스 전형에서는 테스트와 과제 전형이 모두 진행됐다.</strong></p> <p>과제전형은 주어진 API와 스타일 가이드와 요구사항을 기반으로 간단한 모바일 웹 서비스를 만드는 것이었다.</p> <p>다른 회사 과제 전형같은 경우 플랫폼에서 대신 문제를 내줘서 그런지 Vanila JS를 가지고 과제 전형을 진행하고 다른 라이브러리를 쓸 수 없을 뿐더러 웹 IDE라는 생소한 환경에서 응시해야해서 원래 준비했던것이 아니라면 평소같은 스피드와 결과물을 내기가 힘들다.</p> <p>여기서도 토스에서만 느낄 수 있었던 차이점이 있었는데 그것은 위의 교과서적인 내용들은 모두 던져버리고 지원자가 원하는대로 할 수 있도록 했다는 점이다.</p> <p>React를 쓰던 Vue를 쓰던 그리고 라이브러리도 맘껏 쓰게 해 주어서 아마 대부분의 지원자들이 과제전형을 주어진 시간보다 훨씬 일찍 끝냈을 거라고 본다.</p> <p>스타일을 제대로 구현하고 요구사항을 모두 잘 만드는 것은 당연히 해야 할 일이고 내 생각에 당락을 결정한것은 아래와 같지 않을까 한다.</p> <ul> <li>요구사항을 ‘정확히’ 구현하였는가 <ul> <li>생각보다 요구사항을 제대로 이해하지 못하고 구현하는 사람이 꽤 있다.</li> <li>혹은 요구사항보다 더 잘하고 싶어서 요구하지 않은 것까지 사족을 붙이는 경우</li> </ul> </li> <li>요구사항에 애매한 내용이 있을때 어떻게 대처하는가 <ul> <li>질문을 할 수 없는 상황이기 때문에 스스로 이런 문제에 대해서 가설을 세우고 할 수 있는가</li> </ul> </li> <li>요구사항 이외에 UX를 개선 할 수 있는 추가 기능을 얼마나 잘 구현하는가 <ul> <li>예를들면 404 페이지가 없는데 이를 구현한다던지, 빠른 성능을 위해 캐시 기능을 넣었다던지</li> <li>단 요구사항 범위를 해치는 범위는 제외한다.</li> </ul> </li> </ul> <p>다시한번 말하지만 위 예시는 내가 생각한 채점기준이지 절대 토스에서 생각하는 채점기준이 아니다.</p> <p>당연하게도 필자는 이 채점기준이 다른 과제전형에도 똑같이 적용 될수 있다고 생각한다.</p> <p><img src="https://jjalbot.com/media/2018/12/qYY0kJrH9/zzal.jpg" alt="운이 좋다" /></p> <div align="center" style="color: gray">가재는 내편(?)</div> <p>과제 전형중 정말 운이 좋다고 생각했던 부분이 있었다. 최근 <a href="https://github.com/Febase/FeBase">FeBase</a>에서 스터디를 하다 실무에 써먹을 수 있을 것 같아 따로 공부한 부분이 있었는데, 마침 이 기술을 써먹을 수 있는 요구사항이 있어서 바로 적용했다.</p> <p>추후 이 부분에 대한 내용이 테크 인터뷰에서도 다뤄졌기 때문에 지나고보니 결코 헛된 공부는 아니었나보다.</p> <p>전체적인 진행에 아쉬운 점이 있다면, 과제전형에 대한 제출이 Github Repo url이나 플랫폼을 통한것이 아닌, 메일을 통한 제출이었고 안내받은 gmail로 프로젝트 폴더 ZIP을 첨부파일로 보내면 소스코드가 Gmail에서 바이러스가 포함된 파일로 잘못 인식되기 때문에 전송되지 않아 마지막에 급하게 제출을 하려고 했던 사람들은 시간을 놓칠 가능성이 높았다는 점이다.</p> <p>결과적으로 토스측에서는 다른 방법으로 제출 할 수 있도록 빠르게 대처 해 주어 제출 할 수 있었지만 나도 마지막까지 검토하다가 하마터면 제출하지 못할 뻔 했다.</p> <p>위에서 살짝 나왔다시피 역시 이번에도 운이 따라주어 4개의 전형 중 3번째 전형인 테크 인터뷰에 참여 할 수 있었다.</p> <p>사실 이때까지도 별로 큰 감흥은 없었다. 제대로 구직하던 기간 동안 테크 인터뷰나 심지어 컬쳐 인터뷰에서도 떨어져 본 경험이 있기 때문에 칠전팔기 운칠기삼의 정신으로 다음 전형인 테크 인터뷰 전형 메일을 열어보았다.</p> <p>서두에 언급한 것처럼 이력서는 코딩 테스트를 붙는 단계에서 제출하게 된다.</p> <p>나는 이미 가지고 있던 General한 이력서가 있었기 때문에 이를 제출했고 몇일 뒤 테크 인터뷰 일정을 잡기위한 연락을 받았다.</p> <h1 id="테크-인터뷰aka-뉴비-절단기">테크 인터뷰(a.k.a 뉴비 절단기)</h1> <p>테크 인터뷰는 3 : 1로 진행되었으며 면접은 1시간 ~ 1시간 30분정도 시간동안 Google Meet or Zoom으로 진행된다.</p> <p>처음에 테크 인터뷰를 <code class="language-plaintext highlighter-rouge">넘어야 할 산</code> 중 하나라고 생각했지만, 2~3번 해보니 이 시간을 통해 배울 수 있는게 정말 많아서 테크 인터뷰를 참 좋아한다.</p> <p>모르는 내용이 나오면 최대한 머리를 써서 알아내려 머리를 쥐어짜보고 그래도 모르겠다면 아는 내용까지만 설명하려고 노력한다.</p> <p>하지만 테크 인터뷰는 많은 사람들이 어려워하는 전형중 하나이기도 하다.</p> <p><img src="https://steamcdn-a.akamaihd.net/steam/apps/1097150/capsule_616x353.jpg?t=1597424899" alt="Fall Guys: Ultimate Knockout on Steam" /></p> <div align="center" style="color: gray">폴가이즈에서 슬라임 오르기 맵은 뉴비 절단기라는 별칭이 있다.</div> <p>최근에 알게된 <code class="language-plaintext highlighter-rouge">폴가이즈</code>라는 게임을 예시로 들어보려고 한다. 이 게임은 출발 드림팀의 게임 버전이라고 생각하면 되는데 약 70명의 사람들 중 매 라운드마다 참여하고 매 라운드마다 거의 절반의 인원만 살아남는 방식으로 최종 1인이 우승하는 게임이다.</p> <p>보통 1라운드에서 40명 2라운드에서 20명 3라운드에서 10명이 살아남고 4라운드에서 최종 승자를 결정한다.</p> <p>이 게임의 맵에는 <code class="language-plaintext highlighter-rouge">슬라임 오르기</code>라는 맵이 있는데 <strong>이 맵은 원래는 20명이 살아남을 수 있는 맵</strong>이지만 그 난이도가 여타 맵들과는 수준이 달라서 결승선을 통과할 20명 자리는 충분함에도 불구하고 대부분의 뉴비들은 이 단계에서 계속해서 차오르는 슬라임때문에 탈락하여 20명이 살아남아야 할 맵에서 어떤 경우엔단 1명만 살아남을 때도 있어 이럴땐 결승없이 바로 그 한명이 최종 우승을 한다.</p> <p>서론이 길었지만 매번 테크 인터뷰는 그만큼 나에게도 참으로 <strong>어려운 <code class="language-plaintext highlighter-rouge">뉴비절단기</code> 같은 전형이다.</strong></p> <p>이런 두려움 반 설레임 반의 마음을 가지고 면접이 시작되었다.</p> <p>면접 진행 방식은 전체 시간 중 처음 절반은 이력서에 대한 질문이고 나머지 절반은 과제 코드에 대한 리뷰로 진행된다.</p> <p>이력서 질문시간에는 내가 경력에 대해서 설명하면 면접관들은 그걸 받아 기술적으로 내가 얼마나 알고있는지 파고들어 질문하니 이때는 절대 모르는 것을 아는척하는 우매한짓은 하지 말도록 하자.</p> <p>면접관은 인터뷰이가 지식을 제대로 아는지 파악하러 온 전문가라 어떤것을 알고 모르는지 다 간파하고 있다.</p> <p>첫번째 시간이 끝나면 이어서 5분간 휴식 후 면접이 다시 시작된다.</p> <p>두번째 과제 코드 리뷰 시간에는 과제전형에서 작성한 코드를 내가 먼저 전체적으로 무엇을 어떻게 왜 그렇게 코드를 짰는지 설명하고 이후 면접관이 질문 하는 방식이다.</p> <p>필자는 이때 강조하고 싶은(라고 쓰고 칭찬 받고싶은 이라고 읽는다) 부분을 특히나 더 언급하려고 했다. 내가 공들여 짠 부분을 보지 않고 넘어간다면 그것만큼 후회되는 일도 없을테니 말이다.</p> <p>이외에도 코드 중 안티패턴이 있다면 이를 알려주며 어떻게 개선할 수 있는지, 특정 부분에서 과도한 리렌더링이 우려되는데 이를 어떻게 해결하면 좋을지 같은 질문들을 받게 된다.</p> <p>위 시간들이 지나고 지원자에게 질문 할 시간이 주어졌는데 이때 필자는 토스에서 일하는 방식이나 버그 트래킹 방식 등 평소에 궁금했던 기술적인 질문을 했다. 사실 이것 말고도 토스 문화에 대한 것도 궁금하긴 했지만 기술 인터뷰인 만큼 기술적인 질문을 하면 좋겠다고 생각했다.</p> <p>그렇게 면접을 모두 마치고 필자는 떨어질거란 불길한 예감이 들었다. 왜냐하면 면접 후 혼자 리뷰해 보니 기본이라고 느껴지는 질문을 대답하지 못했고 면접관이 대답을 유도해 주었음에도 내가 답을 잘 찾지 못했던 것만 기억났기 때문이다.</p> <p>결과 통보는 이틀 뒤 전화로 이루어 졌는데 ‘안녕하세요 토스 채용관련해서 연락드린 OOO 입니다.’라는 말을 듣자마자 심장이 쿵쾅대기 시작했다.</p> <p>졌지만 잘 싸웠다고 스스로 위안까지 했으면서도 사람마음 간사하게도 기대하는건 어쩔 수 없나보다.</p> <p><img src="https://image.fmkorea.com/files/attach/new/20160814/486616/32048814/437342597/5878189c885a775a1450a144dceee587.jpg" alt="졌잘싸" /></p> <p>그래서인지 몰라도 인삿말 이후에 이어지는 설명은 ‘OO님은 우수한 인재라는것을 저희도 잘 알고··· ‘ 같이 불합격을 암시하는 말처럼 들려 내 목소리톤은 점점 낮아져 갔는데 어느순간 갑자기 ‘그런 OO님에게 다음 컬쳐 인터뷰를 안내 드릴 수 있어 기쁘다’라는 말을 듣고 너무 놀라서 ‘근데 왜 말을 그렇게 하세요 ㅜㅜ’하고 괜히 서러운 마음을 말해버렸다.</p> <p>아직도 그 당시를 회상하면, 전화를 끊자마자 소리지르며 우리집 강아지를 붙잡고 춤추고 하소연했던게 기억난다.</p> <p>이때부터 토스는 내게 가능성을 보여주었고 어쩌면 나도 토스에서 일할 수 있다는 기대감이 들기 시작했던 것 같다.</p> <p>통과한거 같으면 떨어지고 떨어진거 같으면 붙으니 이런 측면에서 인터뷰는 운칠기삼이 과학일수도…</p> <p>하지만 <strong>‘설레면 필히 패한다’</strong>고 하지 않던가 잠시 설레는 마음은 고이 접어두고 다음 전형을 위한 일정을 전화로 잡고, 토스에서 메일로 보내준 컬쳐 인터뷰를 위한 자료들을 찾아보았다.</p> <h1 id="토스의-기업문화-멘틀까지-낱낱이-파헤쳐보자">토스의 기업문화, 멘틀까지 낱낱이 파헤쳐보자</h1> <p><img src="https://www.dropbox.com/s/38p92pgqs98956y/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-08-29%2017.35.54.png?raw=1" alt="루머루머루머" /></p> <p>토스가 어떤 곳인지 아무것도 모르고 이때쯤 와서 토스가 어떤 곳인지 알아보기 시작한다면 적지않은 충격을 받을 수 있다.</p> <p>바야흐로 현재의 기업들은 인재들을 영입하기위해 누구라고 할것없이 앞다퉈 ‘워라밸’, ‘퇴근후 개인의 삶 보장’ 등의 카드로 사람들을 끌어모으려고 하고있는 세상이다.</p> <p>지금도 많은 기업들이 이 방법으로 많은 인재들을 불러모으고 있고 이 방법은 지원자로 하여금 분명히 메리트가 있는 것은 맞다.</p> <p>하지만 이때쯤 알아본 토스는 이런 기업들의 흐름과 정반대로 가는 회사였다.</p> <p>물론, 이미 수년 전부터 다양한 전설(?)에 대해 들었기 때문에 익히 알고있던 소문들이었다.</p> <ul> <li>“토스는 연봉을 많이 주는 만큼 사람들이 갈려나간다.”</li> <li>“야근을 무조건 해야하는 회사”</li> <li>“성과주의로 사람들을 자르는 회사”</li> </ul> <p>뭐 이외에도 많이 있지만 이미 충분히 어떤 느낌인지 전달되는 문장들인 것 같다.</p> <p>그래서 도대체 이렇게 지금 다른 회사들이 만들어가는 기업문화와 반대로 가는 이유는 무엇일까, 왜 사람들은 이 회사를 가려할까 궁금해서 찾다가 한 영상을 접하게 된다.</p> <p><a href="https://youtu.be/HYxpv8a7I4I">토스, 루머의 루머의 루머 영상 보러가기</a></p> <p><img src="https://www.dropbox.com/s/orpt1eprijzy8ur/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-26%2020.29.41.png?raw=1" alt="토스 썸네일" /></p> <div align="center" style="color: gray">영상에서는 실무자 다섯명이 나와 이야기한다</div> <p>자동차를 좋아하는 사람이라면 차의 성능을 측정하는 방법 중 하나인 <code class="language-plaintext highlighter-rouge">제로백</code> 이 무엇인지 알것이다.</p> <p><code class="language-plaintext highlighter-rouge">제로백</code> 이란 ‘차가 정지된 시점으로부터 시속이 100Km/h로 도달하는데 걸리는 시간’을 의미한다.</p> <p>나는 이 영상을 보고 갑자기 문득 지금의 내가 차라면 나의 제로백은 얼마일까 ? 라는 궁금증이 들었다.</p> <p>그저 9시에 일어나 6시에 퇴근하는 전통적인 기업 문화에선 절대 측정 해 볼 수 없을 <code class="language-plaintext highlighter-rouge">내 자신의 제로백</code> 토스에서라면 몇 초정도 나올 수 있을까가 정말 강렬하게 궁금해졌다.</p> <p><img src="https://t1.daumcdn.net/cfile/tistory/273B4249577CB9F60F" alt="제로백" /></p> <p>이때부터 머릿속 생각은 ‘토스에 갈 수 있을까’ 에서 <strong>‘토스에 꼭 가고 싶다’</strong>로 생각이 바뀐 것 같다.</p> <p>둘 다 토스를 가고싶다라는 의미에선 공통점이 있지만 미묘하게 전자와 후자는 <code class="language-plaintext highlighter-rouge">내가</code> 토스를 가고싶은지 아닌지의 차이가 있다.</p> <p>이외에도 정말 많은 정보들이 <a href="https://blog.toss.im/">토스피드</a>에 있어서 일하는 방식에 대한 거의 모든 글들을 읽은 것 같다.</p> <p>혹여나 ‘그럼 일을 많이 하면 행복하다는건가?’라고 생각하시는 회사가 있다면 오해하지 마시라.</p> <p>이 모든 조건에는 당연히 <code class="language-plaintext highlighter-rouge">노력과 비례하는 보상 </code> 이 뒷받침 되어야한다. 이 글을 잘못 이해해서 ‘일을 좋아하는 개발자도 있으니 워라밸말고 일하는 기업문화를 만들자!’고 생각한다면 어느날 분명 잡플래닛, 리멤버 같은 구인/구직 서비스에서 <code class="language-plaintext highlighter-rouge">열정 페이를 요구하는 회사</code> 라는 이야기를 듣게 될 것이다.</p> <p>그럼 이제 토스를 알아본 내용은 이쯤 하고 이제 내가 할 수 있는 준비들을 한것 같으니 컬쳐 인터뷰로 가보자.</p> <p>그럼 이제 내가 할 수 있는 준비들을 한것 같으니 컬쳐 인터뷰로 가보자</p> <p>컬쳐 인터뷰는 약 1시간정도 토스의 문화적 지향성에 대해서 이야기를 나누며 내가 토스가 지향하는 방향과 맞을지 이야기를 나누는 시간이다.</p> <p>내가 토스에 대해서 미리 조사하기도 했고, 이력서 상에 실제로 기업문화에 관심이 많은 Action이 있기도 해서 나도 이야기 할게 많았다.</p> <p>끝나고 시간을 보니 거의 2시간이 흘러 있어서 면접관에게 미안하기도 했지만, 나도 회사를 알아야 하는 시간이기도 하고 그 역도 중요하기 떄문에 전체적으로 핑퐁을 잘 하면서 시간가는 줄 모르고 이야기 한 것 같다.</p> <p>만약 토스에 들어가고 싶다는 일념과 들어가서 일하다보면 어떻게든 되겠지 라는 마음으로 이 과정을 준비하면 누구나 100% 떨어질 것이라는 생각이 들었다. 왜냐하면 1시간이 넘도록 이런 이야기를 다양한 방면으로 하게 될텐데 본인을 속여서 잘 보이려고 하다 논리적 허점에 빠지게 될 것이기 때문이다.</p> <p><img src="https://pbs.twimg.com/media/ET9Rqo9U0AE9C5p?format=jpg&amp;name=small" alt="Billede" /></p> <div align="center" style="color: gray">기억하자, 채용 담당자들은 그 일의 전문가다. 속이려 하지도 속일수도 없다는것을 명심하자 </div> <p>그렇게 인터뷰를 마치고 약 2시간뒤 (이때도 이 빠른 채용 속도는 적응이 안됐다) 채용 담당자에게 전화로 연락이 왔고, 떨어져도 연락은 줄것이라고 했기 때문에 두근세근 전화를 받았다.</p> <p>회사에 지원하고 떨어지면 받는 답변에는 ‘OO님은 정말 실력이 대단하신 분’, ‘미래가 기대 되는 분’이라는 말과 함께 ‘하지만 우리 회사와는 맞지 않는 분’이라고 말을 맺으며 불합격 소식을 받을 때가 있다.</p> <p>테크 인터뷰때와 마찬가지로 이번에도 이런 이야기를 하시길래 이번에는 정말로 ‘아.. 떨어졌구나.. 정말 가고싶었는데’ 라며 인간이 들을 수 있는 가장 낮은 소리인 20Hz로 ‘네.. 네..’라고 대답을 하고있었다.</p> <p>그러다 갑자기 합격 소식을 짠하고 전해주시길래 거실 쇼파에서 전화를 받았던 난 정말 그자리에서 펄쩍 뛰었다.</p> <p>결론적으로 합격을 하게 되었고 어쨋든 이 장에서 컬쳐 인터뷰에 대한 팁을 제공하자면 위에서 말한 토스와 그 기업 문화를 조사하는 것도 좋지만, <strong>스스로에게 솔직해지고 그것을 무기로 나아가면 분명 좋은 결과를 얻을 수 있을것이다.</strong></p> <h1 id="믿을수가-없어-꿈을꾸는-걸까">🤭믿을수가 없어 꿈을꾸는 걸까</h1> <p><img src="https://www.dropbox.com/s/a0s8zsxx832nyb3/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-08-26%2015.02.36.png?raw=1" alt="데스노트 홍광호" /></p> <div align="center" style="color: gray">대충 믿을수가 없다는 눈빛임 암튼 맞음 -뮤지컬 데스노트(홍광호)</div> <p>학부 2학년까지는 치위생학과에 있었고 실제 치과도 약 1년정도 근무했었다. 잠이 부족한 것도 아닌데 치아신경치료 어시스트로 들어가서 졸고있는 나 자신을 본 어느 날 ‘이 길은 내 길이 아니다’라고 깨달음을 얻고 2학년으로 복학 후 친구의 제안으로 컴퓨터에 큰 흥미를 느껴 바로 3학년에 바로 컴퓨터 과학과로 전과를 했다.</p> <p>그만큼 흥미를 느껴서 온 학과라서 그런지 학부시절(그래봤자 1년 반 전)에는 어렴풋이 빠르게 실무에서 일해보고 싶다는 생각을 갖고 있었고, 실제로 졸업 전에 회사에 들어갔었다. 그러면서 점점 내가 바라는 회사를 꿈꾸었고 지금은 토스에서 프론트엔드 개발자로 입사까지 하게 되었다. <del>물론 3개월의 수습 기간을 잘 넘겨야겠지만</del></p> <p>매 순간을 살아갈 때에는 그렇게 다이나믹하다고 느끼지 못했는데, 지금 돌아보니 정말 많은 경험을 한 것 같다. 치과 외에도 영상회사에서도 일하고, 엔터테인먼트 쪽에서도 일한 적이 있으니 말이다. <del>헛소리를 늘어놓는 것 보니 이만 글을 줄일때가 된게 확실하다</del></p> <p><a href="https://news.einfomax.co.kr/news/articleView.html?idxno=4099076">뉴스 기사</a>에 나온 내용으로는 <a href="https://toss.im/career/next-developer-2020">2020 토스 Next 개발자 채용</a>에 사흘만에 약 3천명 정도가 지원했고 최종적으로 약 5천명이 지원했다고 봤다.</p> <p>채용 규모는 20명 정도였다는 점을 생각하면 대충 경쟁률이 250 : 1 정도인데 이름이 알려진 대기업이 80 : 1이나 100 : 1정도 되고 토스보다 먼저 이력서없는 채용으로 인기를 끈 카카오 블라인드 신입 공채는 200 : 1 정도 된다. 이 결과로 봤을때 토스가 개발자들 사이에서 힘든 곳이라는 소문은 있지만, 또 동시에 일하고 싶은 회사라는 인식을 보여주는 반례가 되는 것 같다.</p> <p>아직도 토스팀에 합류하게 된게 믿기지가 않고 그동안 겪은 회사와 180도 달라서 매일매일이 새롭다.</p> <p>부족한 글을 여기까지 읽어주셔서 감사드리며 혹시 아직 궁금증이 해소되지 않았다면 <a href="mailto:shsdf302@gmail.com">메일</a>이나 아래 댓글로 남겨주세요!</p> <p>마지막으로 요즘 최애 노래 <a href="https://music.youtube.com/watch?v=rrI7tOhoVzA&amp;list=RDAMVMrrI7tOhoVzA">홍광호 데스노트</a> 띄우고 갑니다..🎶</p> Sat, 26 Sep 2020 00:00:00 +0000 https://samslow.github.io/diary/2020/09/26/tossNextReview/ https://samslow.github.io/diary/2020/09/26/tossNextReview/ diary diary React Native TDD 길도 테스트 자동화부터 <ul> <li>이 글에서는 <a href="https://callstack.github.io/react-native-testing-library/docs/getting-started/">React Native Testing Library</a> 를 가지고 React Native에서 컴포넌트를 테스팅 하는 방법을 다룹니다.</li> </ul> <h1 id="react-native-tdd-길도-테스트-자동화부터">React Native TDD 길도 테스트 자동화부터</h1> <p>TDD가 한번 나오더니 우후죽순으로 테스트를 하려는 조직이 늘어났다. 기존에도 이미 Testing은 꾸준히 사용되어 왔지만, ‘지금 잘 되는데 왜 굳이?’라며 잘 시도하지 않았고 코드가 늘어날 뿐이라며 시도하지 않던 분위기와는 사뭇 다르다.</p> <p>또 약간 계륵같은것이 그렇다고 안하자니 집에 에어컨을 틀고나온 것 같은 찝찝함에 ‘언젠간 할거야!’라는 불편함을 주는 부분이기도 하다.</p> <p>하지만 점점 서비스들의 코드가 순식간에 불어나 예상치 못한 오류들을 잡기 위해 몇일 밤을 지새고, 별것 아닌 곳에서 에러를 발견했을때 비로소 ‘처음부터 테스트 코드 짤걸..’ 하는 후회를 주변에서 종종 듣는다.</p> <p>오늘은 미래에서 온 필자 자신 혹은 독자에게 이 ‘처음부터 테스트 코드 짤걸···’에서 ‘처음부터’의 시점에 왔다고 생각하고 글을 써보려고 한다.</p> <p>이 글은 Jest기반으로 React의 React Testing Library에서 영감을 받은 React Native의 React Native Testing Library를 기본으로 사용해 react native testing tutorial방식으로 설명하려고 한다.</p> <h1 id="테스트를-계속-하는-사람은-있어도-한번만-한-사람은-없다">테스트를 계속 하는 사람은 있어도 한번만 한 사람은 없다</h1> <p>개발자 김두부는 react native로 할일 관리 앱을 6개월간 개발해서 런칭했다. 시장 반응이 좋아서 유저들은 계속해서 새로운 기능을 요구했고 할일 관리에 이미지 추가, 알람 설정 등의 다양한 기능들을 계속해서 추가하고 있다.</p> <p>새로운 기능들이 추가될 때마다 모든 기능들이 잘 동작하는지 테스트하기 위해 테스팅 시나리오 테이블을 만들어 손수 앱을 다 테스트 하려고한다.</p> <p>이번에는 기존에 이미지 업로드 기능이 갑자기 동작하지 않았는데, 알고보니 최근에 추가한 이미지 선택 관련 코드가 이미지 업로드 코드에 영향을 미쳐서 버그가 발생했고, 이를 테스트해보지 못하고 프로덕션 릴리즈 후에 이를 깨달은 개발자 김두부는 눈물을 머금고 남들 다 퇴근하고 있을 때 hotfix branch를 만들어 작업을 시작했다.</p> <p>이렇게 가끔가다 시나리오를 빼먹을 때도 있고 안일하게 넘어가다가 이미지가 제대로 업로드 되지 않았다거나, 할일 수정이 되지 않는다거나 하는 문제를 일으키는 적이 한두번이 아니다.</p> <p>테스트 자동화는 이런 과정의 시간을 모두 줄여주고 CI 단계에서 문제를 발견할 수 있도록 해 결국 코드 질의 향상으로 이어지도록 돕는다.</p> <p>소제목 ‘테스트를 계속 하는 사람은 있어도 한번만 한 사람은 없다’는 과장이 좀 있긴 하지만 한번 테스트를 겪어 본 사람은 간단하게라도 테스트를 작성하는 재미와 효율성을 느낀 사람이기 때문에 계속 할 가능성이 높다는건 직접 해보니까 알 수 있던 부분이었다.</p> <p>단순하게 화면이 잘 나오도록 하는 스냅샷 테스팅은 쉽게할 수 있기 때문이기도 하다.</p> <h1 id="테스트의-종류">테스트의 종류</h1> <p>테스트의 종류에는 여러가지가 있지만, 가장 간단하고 자주 사용되는 테스트는 아래 2가지가 있다.</p> <ol> <li>유닛 테스트: 가장 조그마한 단위로 테스트하여 하나의 컴포넌트씩 테스트하는 것</li> <li>통합 테스트: 다른 컴포넌트들끼리의 상관 관계까지 테스트하는 것</li> </ol> <p>당장 이해가 가지 않는다고해서 걱정하지 말자. 컴포넌트 기반으로 테스트를 하다보면 나도 모르게 어느새 이런 테스트를 고려해서 짜고있는 모습을 볼 수 있다.</p> <p>그래도 이해를 조금 더 돕기 위해 아래 유닛 테스트와 통합 테스트의 가벼운 짤방을 위 설명과 함께 다시 보자.</p> <p><img src="https://cdn-images-1.medium.com/max/1600/1*KoTFh3xRPgkzD0FlzsYKjA.gif" alt="img" /></p> <p><img src="https://cdn-images-1.medium.com/max/1600/1*4-T6VVnULaszi9ydHQ7Sfw.gif" alt="img" /></p> <h1 id="한번-뽑은-칼이라면-프로젝트에-설치는-해-봐야지">한번 뽑은 칼이라면 프로젝트에 설치는 해 봐야지</h1> <p>지금부터는 실제 코드를 기반으로 어떤 식으로 테스트 코드를 짤 수 있는지 A-Z 로 실습을 해 보자.</p> <p>우리가 만들 앱은 단순하게 유저가 들어와서 버튼을 눌러 출퇴근을 찍는 서비스를 만들어본다.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npx react-native init testingBasic <span class="c"># 프로젝트 시작</span> <span class="nv">$ </span>yarn ios <span class="c"># ios 시뮬레이터 빌드</span> </code></pre></div></div> <p>버전에 따라 다르겠지만 처음 설치를 하면 아래와 같은 의존성을 갖는다.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"react"</span><span class="p">:</span><span class="w"> </span><span class="s2">"16.13.1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"react-native"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.63.2"</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span><span class="nl">"devDependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"@babel/core"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^7.11.5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"@babel/runtime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^7.11.2"</span><span class="p">,</span><span class="w"> </span><span class="nl">"@react-native-community/eslint-config"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^2.0.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"babel-jest"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^26.3.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"eslint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^7.8.1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"jest"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^26.4.2"</span><span class="p">,</span><span class="w"> </span><span class="nl">"metro-react-native-babel-preset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^0.63.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"react-test-renderer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"16.13.1"</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span></code></pre></div></div> <p>여기서 주의깊게 보아야 할 몇가지 library는 <code class="language-plaintext highlighter-rouge">jest</code> <code class="language-plaintext highlighter-rouge">react-test-renderer</code> 이다.</p> <p><code class="language-plaintext highlighter-rouge">react-test-renderer</code> 는 React 컴포넌트를 순수한 JS객체로렌더링하는데 사용 할 수 있는 React 렌더러이다.</p> <p>기본으로 설치되기 때문에 폴더 구조에도 아래와 같이 테스트 폴더가 내장되어있다.</p> <p>이때 사용되는 <code class="language-plaintext highlighter-rouge">__tests__</code> 폴더 내부에 있는 파일은 기본적으로 <code class="language-plaintext highlighter-rouge">jest</code>로 테스트시에 자동으로 테스팅 파일에 포함이 되는데,</p> <p>그 이유는 <code class="language-plaintext highlighter-rouge">packages.js</code>에 있던 <code class="language-plaintext highlighter-rouge">jest</code>의 <code class="language-plaintext highlighter-rouge">preset</code>이 <code class="language-plaintext highlighter-rouge">react-native</code>로 되어있기 떄문에 별도의 복잡한 설정 없이 사용 할 수 있는 것이다.</p> <p>이는 jest의 장점 중 하나인 <code class="language-plaintext highlighter-rouge">Zero config</code> 이기도 하다.</p> <p><img src="https://www.dropbox.com/s/zyvgqjo2axd2zvc/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2018.30.20.png?raw=1" alt="내장된테스팅" /></p> <p>우리는 기본 테스팅 라이브러리 대신 React Native Testing Library를 사용 할 것이고 autocomplete도 지원되길 바라기 때문에 아래 라이브러리들을 함께 설치한다.</p> <p>단 모든 테스팅은 프로덕션 릴리즈에는 포함될 필요가 없기 떄문에 개발 의존성에만 추가 해준다.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yarn add <span class="nt">-D</span> @testing-library/react-native @types/jest </code></pre></div></div> <p>먼저 기본적인 디자인 작업을 해준다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./App.js</span> <span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">StyleSheet</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">View</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react-native</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">Profile</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./src/Profile</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">App</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">return</span> <span class="p">(</span> <span class="o">&lt;</span><span class="nx">View</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">Profile</span> <span class="nx">userName</span><span class="o">=</span><span class="p">{</span><span class="dl">"</span><span class="s2">samslow</span><span class="dl">"</span><span class="p">}</span> <span class="nx">name</span><span class="o">=</span><span class="p">{</span><span class="dl">"</span><span class="s2">서현석</span><span class="dl">"</span><span class="p">}</span> <span class="sr">/</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/View</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">};</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nx">StyleSheet</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span> <span class="na">container</span><span class="p">:</span> <span class="p">{</span> <span class="na">flex</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">alignItems</span><span class="p">:</span> <span class="dl">"</span><span class="s2">center</span><span class="dl">"</span><span class="p">,</span> <span class="na">justifyContent</span><span class="p">:</span> <span class="dl">"</span><span class="s2">center</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">});</span> </code></pre></div></div> <p>기본 큰 틀은 <code class="language-plaintext highlighter-rouge">App.js</code>를 위 내용으로 덮어써서 구성하고</p> <p>아래 <code class="language-plaintext highlighter-rouge">Profile.js</code> 와 <code class="language-plaintext highlighter-rouge">Greeting.js</code> 는 <code class="language-plaintext highlighter-rouge">src</code> 폴더를 만들어 각각 파일을 생성 해준다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./src/Profile.js</span> <span class="k">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useState</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">StyleSheet</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">View</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react-native</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">Greeting</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./Greeting</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">Profile</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">userName</span><span class="p">,</span> <span class="nx">name</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">setMsg</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="dl">"</span><span class="s2">Select your status</span><span class="dl">"</span><span class="p">);</span> <span class="k">return</span> <span class="p">(</span> <span class="o">&lt;</span><span class="nx">View</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">Text</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">textBox</span><span class="p">}</span><span class="o">&gt;</span> <span class="p">{</span><span class="nx">userName</span><span class="p">}({</span><span class="nx">name</span><span class="p">})</span> <span class="o">&lt;</span><span class="sr">/Text</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">Text</span> <span class="nx">style</span><span class="o">=</span><span class="p">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">textBox</span><span class="p">}</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">msg</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/Text</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">Greeting</span> <span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">Bye!</span><span class="dl">"</span> <span class="nx">onPress</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="nx">setMsg</span><span class="p">(</span><span class="dl">"</span><span class="s2">Seeya!</span><span class="dl">"</span><span class="p">)}</span> <span class="sr">/</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">Greeting</span> <span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span> <span class="nx">onPress</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="nx">setMsg</span><span class="p">(</span><span class="dl">"</span><span class="s2">Welcome!</span><span class="dl">"</span><span class="p">)}</span> <span class="sr">/</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/View</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">};</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">Profile</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nx">StyleSheet</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span> <span class="na">container</span><span class="p">:</span> <span class="p">{</span> <span class="na">alignItems</span><span class="p">:</span> <span class="dl">"</span><span class="s2">center</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="na">textBox</span><span class="p">:</span> <span class="p">{</span> <span class="na">marginBottom</span><span class="p">:</span> <span class="mi">15</span><span class="p">,</span> <span class="p">},</span> <span class="p">});</span> </code></pre></div></div> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./src/Greeting.js</span> <span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">Button</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react-native</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">Greeting</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">onPress</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">Button</span> <span class="nx">title</span><span class="o">=</span><span class="p">{</span><span class="nx">title</span><span class="p">}</span> <span class="nx">onPress</span><span class="o">=</span><span class="p">{</span><span class="nx">onPress</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="err">; </span><span class="p">};</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">Greeting</span><span class="p">;</span> </code></pre></div></div> <p><img src="https://www.dropbox.com/s/nj2bdshg687m46a/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2018.40.05.png?raw=1" alt="레이아웃 끝" /></p> <p>모든 디자인과 레이아웃 작업이 끝났다.</p> <p>이제 현재사진을 찍어 Snapshot으로 저장해 매 테스트마다 처음 상태와 같은 사진이 나오는지 비교 해 보는 테스팅인 스냅샷 테스팅을 해보자</p> <p>실제로 캡처를 뜬다거나 하지는 않으면 스냅샷 파일을 가지고 사진을 찍듯 비교를 하기때문에 직접 코드로 볼 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./__tests__/App-test.js</span> <span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">App</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../App</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">render</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@testing-library/react-native</span><span class="dl">"</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">props</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">component</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">getTempComponent</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">App</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="err">; </span><span class="p">}</span> <span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">[App] render</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">props</span> <span class="o">=</span> <span class="p">{};</span> <span class="c1">// fill test props</span> <span class="nx">component</span> <span class="o">=</span> <span class="nx">getTempComponent</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">renders without crashing</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">rendered</span><span class="p">).</span><span class="nx">toMatchSnapshot</span><span class="p">();</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">rendered</span><span class="p">).</span><span class="nx">toBeTruthy</span><span class="p">();</span> <span class="p">});</span> <span class="p">});</span> </code></pre></div></div> <p>위 코드는 <code class="language-plaintext highlighter-rouge">App.js</code>컴포넌트를 불러와서 우리가 의도한 <code class="language-plaintext highlighter-rouge">props</code>를 주입하여 스냅샷을 비교하는 테스트이다.</p> <p>jest 메서드인 <code class="language-plaintext highlighter-rouge">describe</code>는 각각의 <code class="language-plaintext highlighter-rouge">test</code> 들을 그룹핑 하도록 해주고 모든 테스트는 <code class="language-plaintext highlighter-rouge">test</code> 메서드로 할 수 있는데 <code class="language-plaintext highlighter-rouge">test</code> 는 alias로 <code class="language-plaintext highlighter-rouge">it</code> 이라는 메서드로도 사용할 수 있다. 차이점은 없고 <code class="language-plaintext highlighter-rouge">it</code> 을 사용하면 첫번째 매개변수와 함께 자연스러운 문장처럼 읽히게 할 수 있다.</p> <p><code class="language-plaintext highlighter-rouge">component</code> 를 <code class="language-plaintext highlighter-rouge">render</code> 하고 <code class="language-plaintext highlighter-rouge">expect</code> 를 통해 프로그램에게 테스트 대상이 어때야 한다는 내용을 전달 해주면 된다.</p> <ul> <li> <p>스냅샷이 기존 스냅샷과 일치하는지 → <code class="language-plaintext highlighter-rouge">expect(rendered).toMatchSnapshot();</code></p> </li> <li> <p>컴포넌트가 <code class="language-plaintext highlighter-rouge">null</code>, <code class="language-plaintext highlighter-rouge">undefined</code> 같이 <code class="language-plaintext highlighter-rouge">falsy</code> 한 값을 가지지는 않았는지 → <code class="language-plaintext highlighter-rouge">expect(rendered).toBeTruthy();</code></p> </li> </ul> <p>프로젝트 터미널에 <code class="language-plaintext highlighter-rouge">jest</code> 혹은 <code class="language-plaintext highlighter-rouge">yarn test</code> 를 통해 테스트를 실행 해 보자.</p> <p>처음에 이 테스트를 실행하면 <code class="language-plaintext highlighter-rouge">Snapshot missing</code> 경고가 나오는데 당연하게도 우리는 스냅샷을 찍은적이 없기 때문에 이런 경고가 나온다.</p> <p>이럴때는 <code class="language-plaintext highlighter-rouge">yarn test -u</code> 로 최신 스냅샷을 다시 찍어 업데이트 한다는 옵션을 함께 주면 된다.</p> <p>그럼 <code class="language-plaintext highlighter-rouge">__tests__</code>디렉토리에 <code class="language-plaintext highlighter-rouge">__snapshots__</code> 폴더가 생성되며 아래와 같은 내용을 볼 수 있다.</p> <p><img src="https://www.dropbox.com/s/x7bzbzbsdj5ur6v/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2018.55.49.png?raw=1" alt="스냅샷" /></p> <p>뭔가 많이 익숙하면서도 미묘하게 다른 코드를 볼 수 있는데 이게 바로 앞으로 테스팅의 기준일 될 스냅샷이다.</p> <p>스냅샷 테스팅은 UI가 예측못하게 바뀌는 걸 방지하도록 할 때 사용되는 유용한 툴이다.</p> <p>만약 화면에 보이는 요소가 하나라도 변경된다면 다시 스냅샷을 찍어야 한다는 점만 기억하자.</p> <p>사실 여기까지 이해했다면 테스팅에 대한 모든것을 알았다고 해도 무방하다.</p> <p>이후에 설명하는 내용들은 <a href="https://callstack.github.io/react-native-testing-library/">react native testing library 공식문서</a>에서 설명하는 Queries에 대한 설명이 될것이며 위 내용과 비슷할 것이다.</p> <p>이번에는 <code class="language-plaintext highlighter-rouge">Greeting</code> Button에 대해서 위에서 설명한 컴포넌트 하나하나를 테스트하는 <code class="language-plaintext highlighter-rouge">단위 테스트</code>를 해보자.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./src/__tests__/Greeting-test.js</span> <span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">render</span><span class="p">,</span> <span class="nx">fireEvent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@testing-library/react-native</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">Greeting</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../Greeting</span><span class="dl">"</span><span class="p">;</span> <span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">[Greeting] Test</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">onPressMock</span> <span class="o">=</span> <span class="nx">jest</span><span class="p">.</span><span class="nx">fn</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">,</span> <span class="na">onPress</span><span class="p">:</span> <span class="nx">onPressMock</span><span class="p">,</span> <span class="p">};</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">버튼이 눌린다.</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">Greeting</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="se">)</span><span class="err">; </span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="nx">fireEvent</span><span class="p">(</span><span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">),</span> <span class="dl">"</span><span class="s2">onPress</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">onPressMock</span><span class="p">).</span><span class="nx">toBeCalledTimes</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="nx">expect</span><span class="p">(</span><span class="nx">rendered</span><span class="p">.</span><span class="nx">toJSON</span><span class="p">().</span><span class="nx">children</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">children</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">children</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="nx">toEqual</span><span class="p">(</span> <span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">,</span> <span class="p">);</span> <span class="p">});</span> <span class="p">});</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">CRA</code>에서 이미 <code class="language-plaintext highlighter-rouge">jest</code>에 대한 <code class="language-plaintext highlighter-rouge">preset</code>으로 <code class="language-plaintext highlighter-rouge">react-native</code> 가 있다고 위에서 설명 한 이유로 <code class="language-plaintext highlighter-rouge">__tests__</code> 폴더를 만들어 규칙에 맞게 테스트 파일을 만들어준다.</p> <p>이번 테스트에서는 <code class="language-plaintext highlighter-rouge">jest.fn()</code> 이라는 새로운 <code class="language-plaintext highlighter-rouge">jest</code> 함수가 등장했다.</p> <p><code class="language-plaintext highlighter-rouge">jest.fn()</code> 은 mocking 하는 목적으로 사용되었는데, 우리가 실제로 함수를 사용할 수도 있지만 실제 함수를 호출하기 어려운 상황이나 꼭 그럴 필요가 없을때 ‘이 함수가 있겠다~’ 하고 만들어 두는 가짜 함수라고 볼 수 있겠다.</p> <p>이를 통해서 우리는 해당 함수가 얼마나 호출되었는지 어떤 매개변수와 실행되었는지 같은 내용을 추적 할 수 있게 되었다.</p> <p>또 <code class="language-plaintext highlighter-rouge">fireEvent</code> 가 새로 보이는데 이 아이는 <code class="language-plaintext highlighter-rouge">onPress</code>, <code class="language-plaintext highlighter-rouge">onChange</code>,<code class="language-plaintext highlighter-rouge">scroll</code> 같이 어떤 이벤트로써 상호작용하는 컴포넌트들을 트리거하는데 사용한다.</p> <p>위의 예시에서는 <code class="language-plaintext highlighter-rouge">Hello!</code> 라는 텍스트가 있는 컴포넌트를 5번 누르고 5번이 잘 눌렸는지와 눌려진 컴포넌트의 텍스트가 변경되지 않았는지 테스트한다.</p> <p>마지막으로 <code class="language-plaintext highlighter-rouge">getByText</code> 는 매개변수로 전달된 텍스트를 가진 컴포넌트 노드가 존재하는지 테스트함과 동시에 그 값을 리턴하는 함수이다.</p> <p>이런 메서드를 테스팅 라이브러리에서는 Queries로 통칭하는데 Variant로 알려진 접두사와 Queries로 알려진 접미사의 합성으로 구성된다.</p> <ul> <li>Variant <ul> <li>getBy* : 조건에 일치하는 엘리먼트를 하나 선택한다. 없다면 테스트 실패로 처리한다.</li> <li>getAllBy* : 조건에 일치하는 엘리먼트를 여러개 선택한다. 없다면 테스트 실패로 처리한다.</li> <li>queryBy* : 조건에 일치하는 엘리먼트를 하나 선택한다. 존재하지 않아도 실패는 하지 않는다.</li> <li>queryAllBy* : 조건에 일치하는 엘리먼트를 여러개 선택한다. 존재하지 않아도 실패는 하지 않는다.</li> <li>findBy* : 조건에 일치하는 엘리먼트를 하나 선택한다. 단 Promise 객체를 리턴하며 조건에 만족하는 엘리먼트가 나타날 때 까지 기다리고 만약 4500ms 내에 발견되지 않으면 테스트는 실패한다.</li> <li>findAllBy* : 조건에 일치하는 엘리먼트를 여러개 선택한다. 단 Promise 객체를 리턴하며 조건에 만족하는 엘리먼트가 나타날 때 까지 기다리고 만약 4500ms 내에 발견되지 않으면 테스트는 실패한다.</li> </ul> </li> <li>Queries( 주로 사용하는 것만 다룸 ) <ul> <li>ByText : 엘리먼트가 가진 텍스트 값으로 선택</li> <li>ByAltText : 엘리먼트가 가진 alt 속성으로 선택</li> <li>ByTestId : 엘리먼트가 가진 TestId 속성으로 선택</li> </ul> </li> </ul> <p>누군가는 ByTestId만으로 테스트를 모두 할 수 있을 것 같다고 하지만, 테스트를 위해 코드에 testId를 넣는 것은 가독성을 해치므로 지양해야한다.</p> <p>Greeting에 대한 단위테스트가 끝났다면 이제 한단계 위의 컴포넌트인 Profile로 이동해 함께 있어도 잘 동작하는지 통합 테스트를 진행하면 된다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ./src/__tests__/Profile-test.js</span> <span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">render</span><span class="p">,</span> <span class="nx">fireEvent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@testing-library/react-native</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">Profile</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../Profile</span><span class="dl">"</span><span class="p">;</span> <span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">[Profile] Test</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="p">{</span> <span class="na">userName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">samslow</span><span class="dl">"</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">서현석</span><span class="dl">"</span><span class="p">,</span> <span class="p">};</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">유저 이름이 알맞게 나온다.</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">Profile</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="se">)</span><span class="err">; </span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="sr">/samslow/</span><span class="p">);</span> <span class="p">});</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">버튼이 눌리면 메시지가 적절히 변경된다.</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">Profile</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="se">)</span><span class="err">; </span> <span class="c1">// rendered.debug();</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Select your status</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">byeBtn</span> <span class="o">=</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bye!</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">helloBtn</span> <span class="o">=</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">);</span> <span class="nx">fireEvent</span><span class="p">(</span><span class="nx">byeBtn</span><span class="p">,</span> <span class="dl">"</span><span class="s2">onPress</span><span class="dl">"</span><span class="p">);</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Seeya!</span><span class="dl">"</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> </code></pre></div></div> <p>이번 통합 테스트에서는 기존에 나왔던 메서드들을 그대로 사용했기 때문에 새로운 내용은 없지만, 이 테스트의 목적이 무엇인지 상기하면서 작성해야한다.</p> <p>통합테스트는 다른 요소들과 상호작용에서도 그 값이나 레이아웃이 깨지지 않는지를 보는 것이기 떄문에 화면에 렌더링된 요소들을 선택하고 또 그 요소로 인해 변경되는 다른 요소들의 값이 올바른지까지 체크해야한다.</p> <p>이제 모든 테스트는 끝났다. 하지만 내 테스트가 올바로 모든 부분을 잘 처리 했는지 의문이 들지 않는가?</p> <p>내가 작성한 코드가 모두 테스트 되었는지 알고싶다면 아래 명령어로 jest에서 제공하는 report를 확인하면 된다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">$</span> <span class="nx">yarn</span> <span class="nx">test</span> <span class="o">--</span><span class="nx">coverage</span> </code></pre></div></div> <p>위 명령어를 실행하면 <code class="language-plaintext highlighter-rouge">coverage</code> 라는 폴더가 생성되며 <code class="language-plaintext highlighter-rouge">coverage report</code> 를 이용 할 수 있게 된다.</p> <p><img src="https://www.dropbox.com/s/ekt8scfohtlfgt3/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2019.36.42.png?raw=1" alt="coverage" /></p> <p>현재 우리의 테스트코드의 전체 커버리지는 90%이다.</p> <p>report를 보니 <code class="language-plaintext highlighter-rouge">Profile.js</code> 의 15번째 줄의 내용이 테스트 되지 않았다고 한다.</p> <p>방금 생성된 <code class="language-plaintext highlighter-rouge">coverage</code> 폴더에서 index.html을 열어 바로 확인 해 보자</p> <p><img src="https://www.dropbox.com/s/94wprtu27vzc6vk/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2019.41.06.png?raw=1" alt="커버리지 확인" /></p> <p><img src="https://www.dropbox.com/s/33h5atmrt54vknh/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2019.41.29.png?raw=1" alt="profile coverage" /></p> <p>리포트에서 확인 해 보니 누가봐도 잘못했다는 의미로 빨간색으로 잘못한 부분을 짚어준다.</p> <p>Profile-test에서 Greeting 버튼 중 Bye 는 테스트 되었는데 Hello 는 테스트되지 않아서 그런 것 같다.</p> <p>Profile-test에서 테스트를 조금 수정 해 보자</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">render</span><span class="p">,</span> <span class="nx">fireEvent</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@testing-library/react-native</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">Profile</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">../Profile</span><span class="dl">"</span><span class="p">;</span> <span class="nx">describe</span><span class="p">(</span><span class="dl">"</span><span class="s2">[Profile] Test</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="p">{</span> <span class="na">userName</span><span class="p">:</span> <span class="dl">"</span><span class="s2">samslow</span><span class="dl">"</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">서현석</span><span class="dl">"</span><span class="p">,</span> <span class="p">};</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">유저 이름이 알맞게 나온다.</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">Profile</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="se">)</span><span class="err">; </span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="sr">/samslow/</span><span class="p">);</span> <span class="p">});</span> <span class="nx">test</span><span class="p">(</span><span class="dl">"</span><span class="s2">버튼이 눌리면 메시지가 적절히 변경된다.</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">rendered</span> <span class="o">=</span> <span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">Profile</span> <span class="p">{...</span><span class="nx">props</span><span class="p">}</span> <span class="sr">/&gt;</span><span class="se">)</span><span class="err">; </span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Select your status</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">byeBtn</span> <span class="o">=</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Bye!</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">helloBtn</span> <span class="o">=</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello!</span><span class="dl">"</span><span class="p">);</span> <span class="nx">fireEvent</span><span class="p">(</span><span class="nx">byeBtn</span><span class="p">,</span> <span class="dl">"</span><span class="s2">onPress</span><span class="dl">"</span><span class="p">);</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Seeya!</span><span class="dl">"</span><span class="p">);</span> <span class="nx">fireEvent</span><span class="p">(</span><span class="nx">helloBtn</span><span class="p">,</span> <span class="dl">"</span><span class="s2">onPress</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// 추가된 라인 1</span> <span class="nx">rendered</span><span class="p">.</span><span class="nx">getByText</span><span class="p">(</span><span class="dl">"</span><span class="s2">Welcome!</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// 추가된 라인 2</span> <span class="p">});</span> <span class="p">});</span> </code></pre></div></div> <p>이제 다시 <code class="language-plaintext highlighter-rouge">yarn test --coverage</code> 를 실행하면?</p> <p><img src="https://www.dropbox.com/s/krs02qngnow3k9n/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-09-02%2019.43.59.png?raw=1" alt="coverage Complete!" /></p> <p>축하한다. 이제 이 코드는 모든 경우의 수를 커버하는 코드이기 때문에 여러분은 자신을 더 믿고 다른 기능들을 추가 할 수 있는 무적의 인피니트 건틀렛을 얻었다! 세상을 반으로 쪼개보기도 하고(?) 또 붙여보기도 하자(?)</p> <p><img src="https://m.maniahouse.co.kr/web/product/big/201804/4388_shop1_490593.jpg" alt="타노스" /></p> <h1 id="저기-근데-coverage-report는-어떻게-읽나요-">저기 근데.. Coverage Report는 어떻게 읽나요 ?</h1> <p>코드를 작성해서 테스트하긴 하는데 jest가 뭘 안다고 우리 코드의 커버리지를 스스로 측정할 수 있는지 궁금할것이다.</p> <p>jest의 coverage는 총 4가지 종류를 기반으로 모든 코드를 검토한다.</p> <ol> <li>Fucntion Coverage: 각 함수가 잘 호출되었는가?</li> <li>Statement Coverage: 각 구문이 잘 실행되었는가?</li> <li>Branch Coverage: 각 분기가 모두 테스트 되었는가(if-else statement), 모든 케이스가 실행 되었는가?</li> <li>Line Coverage: 실행 가능한 라인이 잘 실행 되었는가?</li> </ol> <p>이런 결과로써 퍼센트는 실행된 코드와 그렇지 않은 코드를 보여주는 것이다.</p> <p>Coverage Report에서 각 심볼과 색상이 의미하는 것은 아래와 같다.</p> <ul> <li><code class="language-plaintext highlighter-rouge">E</code>: ‘else path not taken’으로 if/else에서 else가 테스트 되지 않은 경우</li> <li><code class="language-plaintext highlighter-rouge">I</code>: ‘if path not taken’으로 if/else에서 if가 테스트되지 않은 경우</li> <li><code class="language-plaintext highlighter-rouge">xN</code>: 해당 행이 실행된 횟수 N</li> <li><code class="language-plaintext highlighter-rouge">Red</code>: 실행되지 않은 줄이나 코드 조각</li> <li><code class="language-plaintext highlighter-rouge">Pink</code>: statements 커버되지 않음</li> <li><code class="language-plaintext highlighter-rouge">Orange</code>: functions 커버되지 않음</li> <li><code class="language-plaintext highlighter-rouge">Yellow</code>: branches 커버되지 않음</li> </ul> <p>위 내용만 알면 Coverage Report는 쉽게 읽을 수 있다.</p> <p>구분하는게 다소 헷갈린다면 Report에서 Test failed가 떠있는 line을 친절히 보여주므로 해당 부분에 대한 테스트를 작성하기만 하면 된다.</p> <h1 id="jest-가는데에-plugin-따라간다">Jest 가는데에 Plugin 따라간다</h1> <p>VScode 터미널 근처에 보면 <code class="language-plaintext highlighter-rouge">output</code> 이라는 별도의 탭이 있는데, 이 탭을 jest 로 세팅 해 두면 저장할 때마다 자동으로 테스트를 돌려주는 멋진 기능을제공한다.</p> <p>또 <code class="language-plaintext highlighter-rouge">VScode</code> 를 사용한다면 아래 Plugin들을 사용하는 것을 고려하자.</p> <ul> <li><a href="https://marketplace.visualstudio.com/items?itemName=IBM.output-colorizer">Output Colorizer</a> <ul> <li>기본적으로 Output 탭은 다른 결과물도 함께 보여주기 때문에 터미널에서 처럼 컬러풀하지 않다. 색을 칠해주자</li> </ul> </li> <li><a href="https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest">Jest</a> <ul> <li>모든 테스트 옆에 이 테스트가 잘 통과했는지 여부를 표시해준다.</li> <li>스냅샷 갱신이 필요하다면 자동으로 버튼을 띄워준다.</li> <li>암튼 전체적으로 디버깅을 쉽게 해줌! 정말임!</li> </ul> </li> </ul> <h1 id="마치며">마치며</h1> <p>여기서는 다루지 않은 <code class="language-plaintext highlighter-rouge">beforeEach</code>나 <code class="language-plaintext highlighter-rouge">afterEach</code>같은 유용한 API들도 있는데 한꺼번에 소개하긴 너무 많아 소개하지 않았다.</p> <p>너무 많은 개념들이 나와 여러 개념들이 나와서 헷갈리는 부분이 있을 수 있다.</p> <p>예를들어 jest는 뭐고 testing-library랑 어떤 차이가 있는지 같은 경우 jest는 테스팅 프레임워크로 우리가 따라야하는 규칙이며, testing-library는 말 그대로 라이브러로 우리가 컨트롤 할 수 있는 더 작은 개념이다.</p> <p>하지만 결국 모든것은 테스팅의 일환이다. 이정도만 알아도 다른 부분들은 충분히 찾아서 해볼 수 있다. (Jest 공식 문서가 잘 되어있으니 참고하자)</p> <p>이 글을 기반으로 이제 우리는 TDD를 시도 해 볼 수도 있게 되었고 복잡했던 테스트 파일을 쉽게 읽을 수 있게 되었다.</p> <p>누군가는 테스트 할 시간에 한 줄이라도 더 작성해서 빨리 개발하는게 낫지 않냐고 할 수 있지만 적어도 이 글을 읽은 독자들 만큼은 테스트가 왜 중요하고 조직의 규모가 커질수록 어떤 의미가 있는지 이해할 수 있었으면 좋겠다.</p> <h1 id="reference">Reference</h1> <ul> <li>https://velog.io/@velopert/react-testing</li> <li>https://medium.com/@krishankantsinghal/how-to-read-test-coverage-report-generated-using-jest-c2d1cb70da8b</li> </ul> Wed, 02 Sep 2020 00:00:00 +0000 https://samslow.github.io/development/2020/09/02/reactnativeTesting/ https://samslow.github.io/development/2020/09/02/reactnativeTesting/ reactnative development FE Interview - 자주 나오는 면접 질문 3 <ul> <li>아래 질문에 답할 수 있습니다. <ul> <li>mutable object와 immutable object에 관해 설명 해주세요.</li> <li>동기방식과 비동기 방식 함수의 차이에 관해서 설명 해주세요.</li> </ul> </li> </ul> <h1 id="mutable-object와-immutable-object에-관해-설명-해주세요">mutable object와 immutable object에 관해 설명 해주세요</h1> <h3 id="javascript에서-immutable-객체에는-어떤것이-있을까">JavaScript에서 immutable 객체에는 어떤것이 있을까</h3> <p>Javascript의 원시타입(primitive data type)은 변경 불가능한 값(immutable value) 이다.</p> <ul> <li>Boolean</li> <li>null</li> <li>undefined</li> <li>Number</li> <li>String</li> <li>Symbol (New in ECMAScript 6)</li> </ul> <p>기본 자료형 이외의 모든 값은 객체(Object) 타입이며 객체 타입은 변경 가능한 값(mutable value)이다.</p> <p>즉, 객체는 새로운 값을 다시 만들 필요없이 직접 변경이 하고 변경이 불가능하다는 뜻은 메모리 영역에서의 변경이 불가능하다는 의미이며 재할당은 가능하다.</p> <p>단 <code class="language-plaintext highlighter-rouge">defineProperty</code> 처럼 <code class="language-plaintext highlighter-rouge">객체 상수 속성</code>을 이용하면 mutable한 객체도 immutable하게 바꿀 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">myObject</span> <span class="o">=</span> <span class="p">{};</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">myObject</span><span class="p">,</span> <span class="dl">"</span><span class="s2">number</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span> <span class="na">writable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// 새 값을 선언 가능하게 할 것인가?</span> <span class="na">configurable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="c1">// 값을 변경 할 수 있고 삭제 할수도 있는가?</span> <span class="p">});</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">myObject</span><span class="p">.</span><span class="nx">number</span><span class="p">);</span> <span class="c1">// 42</span> <span class="nx">myObject</span><span class="p">.</span><span class="nx">number</span> <span class="o">=</span> <span class="mi">43</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">myObject</span><span class="p">.</span><span class="nx">number</span><span class="p">);</span> <span class="c1">// 42</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">객체 확장 방지</code> 로 <code class="language-plaintext highlighter-rouge">preventExtensions</code> 를 사용하거나, <code class="language-plaintext highlighter-rouge">seal</code> 메소드로 객체를 확장 방지 할 뿐만 아니라 제거, 선언도 못하게 하는 방법, 즉 <code class="language-plaintext highlighter-rouge">preventExtensions</code> 에 <code class="language-plaintext highlighter-rouge">configurable</code> 를 <code class="language-plaintext highlighter-rouge">false</code>로 하는 것이라 보면 된다. 또 <code class="language-plaintext highlighter-rouge">freeze</code> 를 사용해서 <code class="language-plaintext highlighter-rouge">seal</code> 의 모든 immutable 속성에 프로토타입 변경 방지까지 할 수 있는데 이는 js에서 객체 불변성의 끝판왕이라고 볼 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">myObject</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="p">};</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">preventExtensions</span><span class="p">(</span><span class="nx">myObject</span><span class="p">);</span> <span class="nx">myObject</span><span class="p">.</span><span class="nx">b</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span> <span class="c1">// 에러가 남!</span> <span class="nx">myObject</span><span class="p">.</span><span class="nx">b</span><span class="p">;</span> <span class="c1">// undefined</span> <span class="kd">var</span> <span class="nx">immutable</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">freeze</span><span class="p">({});</span> <span class="kd">var</span> <span class="nx">immutable</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">seal</span><span class="p">({});</span> </code></pre></div></div> <h3 id="immutability의-장점과-단점">Immutability의 장점과 단점</h3> <p>불변한 객체들은 기본적으로 위에 명시한 원시타입들이고 기본적으로 객체의 상태를 변경 할 수 없다.</p> <p>클래스가 가지고 있는 값은 오직 생성자에 의해서만 설정 될 수 있으며, 변경을 원한다면 원하는 값을 가진 새로운 객체를 생성해야한다. 비교하기가 애매해서 아래 예시가 적절한 예시는 아니니 참고만 하길 바란다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">str</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">string is immutable</span><span class="dl">"</span><span class="p">;</span> <span class="nx">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">c</span><span class="dl">"</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span> <span class="c1">// "string is immutable"</span> </code></pre></div></div> <p>이들의 장점과 단점은 아래와 같다.</p> <ul> <li>장점 <ul> <li>값이 보장되므로 안전하게 변수를 공유 할 수 있다.</li> <li>값이 보장되므로 객체비교시 더 쉽고 빠르게 가능하다.</li> <li>함수형 프로그래밍에서 특히, 불변객체를 이용하면 프로그램 파악이 쉽다.</li> <li>여러 스레드가 동시에 immutable 변수를 사용해도 값이 변경되지 않으므로 걱정이 없다.</li> </ul> </li> <li>단점 <ul> <li>매번 새로운 객체를 만들어버리면 퍼포먼스가 나빠지기 때문에, 이를 고려해여 하므로 Facebook의 immutablejs를 사용하는 등의 라이브러리를 사용하는 것도 좋다.</li> <li>그래프같은 순환 구조를 만들기가 어렵다. <ul> <li>수정, 초기화도 될 수 없는 두 객체가 어떻게 서로 참조하게 할 것인가?</li> </ul> </li> </ul> </li> </ul> <h1 id="동기-방식과-비동기-방식의-차이에-관해서-설명-해주세요">동기 방식과 비동기 방식의 차이에 관해서 설명 해주세요.</h1> <p>동기방식은 현재의 명령문이 컴파일되고 에러없이 실행되기 전까지 다음 명령문을 실행하지 않는다.</p> <p>반면 비동기 방식은 현재 명령문의 완료와 상관 없이 다음 명령문을 실행한다.</p> <p><img src="https://blog.kakaocdn.net/dn/bztSy0/btqCz451jcO/1UjnGAajLPDoBmh3VqNRjK/img.jpg" alt="" /></p> <p>js에서 <code class="language-plaintext highlighter-rouge">setTimeout</code> 가 대표적인 예시가 될 수 있는데, 이 함수는 2번째 매개변수로 주어진 시간만큼 이후에 1번째 매개변수로 주어진 함수를 실행한다.</p> <p>콜백함수는 모든 작업이 완료되고 호출 스택이 비어 있을 때만 호출된다.</p> <p>하지만 전체적인 프로그램 실행에는 방해되지 않게 진행되는데 이것이 가능한 이유는 이 함수가 비동기방식으로 동작하기 때문이다.</p> <p>만약 비동기라는 개념이 없었다면, 싱글 스레드 프로그램에서는 이런 단순 대기 함수에서 모든 작업이 멈출 것이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">);</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">,</span> <span class="mi">5000</span><span class="p">,</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">3</span><span class="dl">"</span><span class="p">);</span> </code></pre></div></div> <p>위 결과가 예측 가능한가? 처음 본 사람이라면 1 2 3 이라고 답 할 것이고, setTimeout이 호출 스택이 비면 콜백 큐에서 소환된다는 것을 아는 사람은 1 3 2 라고 답할 것이다.</p> <p>JS가 싱글 스레드 방식이지만 이 함수가 비동기방식으로 동작 할 수 있는 이유는 Web API에 setTimeout의 처리를 위임하기 때문이다.</p> <p>JS 엔진과 별도로 따로 비동기 처리를 따로 돌면서 콜백 함수를 가지고 이벤트 루프에 들어가 처리되는대로 다시 JS 엔진으로 이를 돌려보내준다.</p> <p>JS에서 비동기를 다루는 아이들은 아래와 같은 객체들이 있다.</p> <ul> <li>Promise</li> <li>async/await</li> <li>콜백 함수</li> </ul> <h1 id="self-check">Self check</h1> <ol> <li>js에서 원시타입이 아닌 객체는 (immutability/ mutability)를 갖지만, <code class="language-plaintext highlighter-rouge">________</code> 는 객체를 Immutable하게 하게 할 뿐더러 prototype마저도 변경 할 수 없게 한다.</li> <li>immutable 객체가 그래프같은 순환 구조를 만들기가 어려운 이유는?</li> <li>만약 <code class="language-plaintext highlighter-rouge">setTimeout</code> 이 동기 방식이었다면 어떤 문제가 발생할까?</li> <li>JS는 (싱글/ 멀티) 스레드이고 브라우저에서는 <code class="language-plaintext highlighter-rouge">________</code> 같은 도구로 (싱글/ 멀티) 스레드를 구현할 수 있다.</li> </ol> <h1 id="reference">Reference</h1> <ul> <li>https://blog.rhostem.com/posts/2020-04-13-fe-interview-handbook-js-2</li> </ul> Tue, 11 Aug 2020 00:00:00 +0000 https://samslow.github.io/development/2020/08/11/FE-interview-3/ https://samslow.github.io/development/2020/08/11/FE-interview-3/ web development FE Interview - 자주 나오는 면접 질문2 <ul> <li>아래 질문에 답할 수 있습니다. <ul> <li>document load event와 DOMContentLoaded event의 차이점은 무엇인가요?</li> <li><code class="language-plaintext highlighter-rouge">==</code>와 <code class="language-plaintext highlighter-rouge">===</code>의 차이점은 무엇인가요?</li> <li>JavaScript의 “동일출처정책(the same-origin policy)”에 대해서 설명하세요.</li> </ul> </li> </ul> <h1 id="document-load-이벤트와-document-domcontentloaded-이벤트의-차이점은-무엇인가요"><code class="language-plaintext highlighter-rouge">document load</code> 이벤트와 <code class="language-plaintext highlighter-rouge">document DOMContentLoaded</code> 이벤트의 차이점은 무엇인가요?</h1> <p>document는 HTML 문서의 핵심을 담고있는 객체로 window의 자식 객체이다.</p> <p>문제의 2가지 이벤트는 모두 생명주기중 하나인데 DOM이 로드될때 갖는 흐름이다.</p> <ul> <li><code class="language-plaintext highlighter-rouge">DOMContentLoaded</code> <ul> <li>브라우저가 HTML을 전부 읽고 DOM 트리를 모두 완성하면 즉시 발생한다.</li> <li>이미지파일, CSS는 기다리지 않는다.</li> <li>이때 <code class="language-plaintext highlighter-rouge">&lt;img /&gt;</code> 가 참조는 되지만 사이즈는 0x0으로 나온다.</li> <li><code class="language-plaintext highlighter-rouge">document.addEventListenr('DOMContentLoaded', ...)</code>로 사용</li> </ul> </li> <li><code class="language-plaintext highlighter-rouge">load</code> <ul> <li>HTML로 DOM 트리가 만들어지고 CSS, JS와 Assets들이 모두 load되면 발생한다.</li> <li>Assets들의 실제 크기를 알려면 이때를 이용해야한다.</li> <li><code class="language-plaintext highlighter-rouge">window.onload</code>로 사용</li> </ul> </li> </ul> <h1 id="와-의-차이점은-무엇인가요"><code class="language-plaintext highlighter-rouge">==</code>와 <code class="language-plaintext highlighter-rouge">===</code>의 차이점은 무엇인가요?</h1> <p>JS에서 JS Magic을 쉽게 경험 할 수 있는 비교연산자이다.</p> <p>다만, <code class="language-plaintext highlighter-rouge">==</code> 는 타입이 다르면 타입을 서로 같게 변환 해 주고, <code class="language-plaintext highlighter-rouge">===</code> 는 타입 변환도 하지 않고 값을 비교한다.</p> <p>즉, <code class="language-plaintext highlighter-rouge">==</code> 는 타입이 달라도 값만 같으면 <code class="language-plaintext highlighter-rouge">true</code> 를 반환하고 <code class="language-plaintext highlighter-rouge">===</code> 는 타입과 값 모두 값아야 <code class="language-plaintext highlighter-rouge">true</code>를 반환한다.</p> <p>다음 JS Magic을 통해서 <code class="language-plaintext highlighter-rouge">==</code> 이 어떻게 위험한지 확인 해 보자</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// true</span> <span class="mi">1</span> <span class="o">==</span> <span class="p">[</span><span class="mi">1</span><span class="p">];</span> <span class="c1">// true</span> <span class="mi">1</span> <span class="o">==</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// true</span> <span class="mi">0</span> <span class="o">==</span> <span class="dl">''</span><span class="p">;</span> <span class="c1">// true</span> <span class="mi">0</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">0</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// true</span> <span class="mi">0</span> <span class="o">==</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// true</span> </code></pre></div></div> <div align="center" style="color: gray">WONDERFUL</div> <p>편의를 위해 <code class="language-plaintext highlighter-rouge">null</code>과 <code class="language-plaintext highlighter-rouge">undefined</code> 를 비교할 때를 제외하고는 <code class="language-plaintext highlighter-rouge">==</code> 은 사용하지 않도록 하자.</p> <h1 id="javascript의-동일출처정책the-same-origin-policy에-대해서-설명하세요">JavaScript의 “동일출처정책(the same-origin policy)”에 대해서 설명하세요.</h1> <blockquote> <p>동일 출처 정책(same-origin policy)은 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. 동일 출처 정책은 잠재적으로 해로울 수 있는 문서를 분리해, 공격받을 수 있는 경로를 줄입니다. <em>MDN</em></p> </blockquote> <p>즉, 웹 브라우저가 보안을 위해 프로토콜, 호스트, 포트가 동일한 서버로만 비동기 요청을 주고 받을 수 있도록 한 정책이다.</p> <p>프로토콜, 호스트, 포트는 크롬 개발자 도구에서 <code class="language-plaintext highlighter-rouge">location</code>을 찍어보면 알 수 있다.</p> <p><img src="https://www.dropbox.com/s/6t272nxvf1bh23v/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-08-05%2004.24.41.png?dl=1" alt="location" /></p> <p>다시 말해 동일 출처 정책이란 같은 <strong>Origin 출처의 서버로만 요청을 주고 받을 수 있다는 것이다.</strong></p> <ul> <li><code class="language-plaintext highlighter-rouge">http://www.same-domain.com/</code> –&gt; <code class="language-plaintext highlighter-rouge">http://www.same-domain.com</code> = <code class="language-plaintext highlighter-rouge">same-origin</code></li> <li><code class="language-plaintext highlighter-rouge">http://www.same-domain.com</code> -/-&gt; <code class="language-plaintext highlighter-rouge">http://www.cross-domain.com</code> = <code class="language-plaintext highlighter-rouge">cross-origin</code></li> </ul> <p>뿐만 아니라 <code class="language-plaintext highlighter-rouge">https:</code> 의 기본 포트인 443에서 같은 origin의 8080포트로 요청을 보내도 <code class="language-plaintext highlighter-rouge">cross-origin</code>이 뜬다.</p> <p>이를 해결하기 위해서 자주 접하는 단어인 CORS를 사용한다.</p> <blockquote> <p>Cross-Origin Resource Sharing(CORS) 은 추가 HTTP 헤더를 사용하여 브라우저가 한 출처에서 실행중인 웹 애플리케이션에 선택된 액세스 권한을 부여하도록하는 메커니즘입니다. 다른 출처의 자원. <strong>웹 응용 프로그램은 자체와 다른 출처 (도메인, 프로토콜 또는 포트)를 가진 리소스를 요청할 때 cross-origin HTTP 요청을 실행</strong>합니다.<em>MDN</em></p> </blockquote> <p>즉, <em>Same-Origin Policy의 문제점을 해결하기 위한 정책인 만큼</em> <strong>CORS란 cross-Origin 즉, 출처가 다른 도메인에서의 AJAX요청이라도 서버 단에서 데이터 접근 권한을 허용하는 정책</strong>이다.</p> <p>이를 해결하려면 아래 방법 중 하나를 시도 할 수 있다.</p> <ul> <li>서버 <ul> <li><code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin</code> 응답 헤더에 <code class="language-plaintext highlighter-rouge">*</code> 로 처리해서 모든 도메인 가능하도록 하기</li> <li>미들 웨어(보통 library 사용)</li> </ul> </li> <li>클라이언트 <ul> <li>프록시 설정 <ul> <li><img src="https://media.vlpt.us/post-images/yejinh/8650aaa0-3111-11ea-9a27-fbf26c473eb5/Proxy-Server.png" alt="img" /></li> <li>다양한 이유로 (주로 보안의 문제로) 직접 통신하지 못하는 두 개의 컴퓨터 사이에서 서로 통신할 수 있도록 돕는 역할을 가리켜 프록시라 일컫는다.</li> <li>클라이언트 - 서버 중간에서 요청을 받아 <code class="language-plaintext highlighter-rouge">Access-Control-Allow-Origin</code>헤더를 추가해주는 중간 다리 역할을 놔주는 방법으로 해결할 수 있다.</li> </ul> </li> </ul> </li> </ul> <h1 id="mini-coding-test">Mini Coding Test</h1> <p>다음이 작동하도록 JS 를 작성 해 보세요.(EASY)</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">duplicate</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">]);</span> <span class="c1">// [1,2,3,4,5,1,2,3,4,5]</span> </code></pre></div></div> <p>해결 방법에는 여러가지가 있지만 아래와 같이 함수를 만들어 해결 할 수 있을 것이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">duplicate</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">arr</span><span class="p">)</span> <span class="p">}</span> </code></pre></div></div> <h1 id="self-check">Self check</h1> <ol> <li> <p>만약 HTML 문서 끝에 <code class="language-plaintext highlighter-rouge">&lt;script /&gt;</code> 가 있다면, <code class="language-plaintext highlighter-rouge">DOMContentLoaded</code> 는 <code class="language-plaintext highlighter-rouge">&lt;script /&gt;</code> (이전/ 이후)에 발생한다.</p> <ul> <li>단, <code class="language-plaintext highlighter-rouge">______</code> 속성이 있는 <code class="language-plaintext highlighter-rouge">&lt;script /&gt;</code>나 <code class="language-plaintext highlighter-rouge">&lt;script /&gt;</code> 내에 동적으로 생성된 엘리먼트가 있을 경우에는 <code class="language-plaintext highlighter-rouge">DOMContentLoaded</code> 를 막지 않는다.</li> </ul> </li> <li> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">));</span> <span class="nb">window</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">window onload</span><span class="dl">'</span><span class="p">);</span> <span class="o">&lt;</span><span class="sr">/script</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">iframe</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">iframe.html</span><span class="dl">"</span> <span class="nx">onload</span><span class="o">=</span><span class="dl">"</span><span class="s2">log('iframe onload')</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/iframe</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">img</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">http://en.js.cx/clipart/train.gif</span><span class="dl">"</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">img</span><span class="dl">"</span> <span class="o">/&gt;</span> <span class="o">&lt;</span><span class="nx">script</span><span class="o">&gt;</span> <span class="nx">img</span><span class="p">.</span><span class="nx">onload</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">img onload</span><span class="dl">'</span><span class="p">);</span> <span class="o">&lt;</span><span class="sr">/script</span><span class="err">&gt; </span></code></pre></div> </div> <p>위 코드에서 log가 찍히는 결과는 ?</p> </li> <li><code class="language-plaintext highlighter-rouge">null == undefined</code>의 결과와 이유는 ?</li> <li>Array.prototype의 method중 concat()과 join()의 차이점은?</li> </ol> <h1 id="reference">Reference</h1> <ul> <li>https://ko.javascript.info/onload-ondomcontentloaded</li> <li>https://velog.io/@yejinh/CORS-4tk536f0db</li> </ul> Wed, 05 Aug 2020 00:00:00 +0000 https://samslow.github.io/development/2020/08/05/FE-interview-2/ https://samslow.github.io/development/2020/08/05/FE-interview-2/ web development FE Interview - 자주 나오는 면접 질문1 <ul> <li>Javascript 상속과 Prototype에 관한 FE Interview 질문을 알아봅니다.</li> </ul> <h1 id="어떻게-할-것이냐">어떻게 할 것이냐</h1> <p>이번주부터는 FE Interview에 있는 질문들에서 Keyword를 뽑아 JS 기초를 다져보려고 한다.</p> <p>질문들은 <a href="https://github.com/h5bp/Front-end-Developer-Interview-Questions/tree/master/src/translations/korean#JS-관련-질문">여기</a>에서 볼 수 있고, 필자가 진행중인 스터디에서 동시에 진행중인 다른 질문은 <a href="https://github.com/Febase/FeBase">여기</a>에서 볼 수 있다.</p> <p>이번에는 JS Prototype과 상속에 관한 질문 위주로 4가지를 뽑아서 정리하려고 한다.</p> <h1 id="call과-apply의-차이점은-무엇인가요"><code class="language-plaintext highlighter-rouge">.call</code>과 <code class="language-plaintext highlighter-rouge">.apply</code>의 차이점은 무엇인가요?</h1> <p>기본적으로 함수 호출과 연관이 있는 메서드들이다.</p> <p>예시로 이해하는게 빠르니 아래 코드를 보자</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">person1</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Jo</span><span class="dl">"</span><span class="p">,</span> <span class="p">};</span> <span class="kd">let</span> <span class="nx">person2</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Kim</span><span class="dl">"</span><span class="p">,</span> <span class="na">study</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">lastPoint1</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">,</span> <span class="nx">lastPoint2</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">!</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">이/가 공부를 하고 있습니다</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lastPoint1</span> <span class="o">+</span> <span class="nx">lastPoint2</span><span class="p">,</span> <span class="p">);</span> <span class="p">},</span> <span class="p">};</span> <span class="nx">person2</span><span class="p">.</span><span class="nx">study</span><span class="p">();</span> <span class="c1">// Kim이/가 공부를 하고 있습니다.!</span> <span class="c1">// call()</span> <span class="nx">person2</span><span class="p">.</span><span class="nx">study</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">person1</span><span class="p">,</span> <span class="dl">"</span><span class="s2">?</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">~</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Jo이/가 공부를 하고 있습니다?~</span> <span class="c1">// apply()</span> <span class="nx">person2</span><span class="p">.</span><span class="nx">study</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">person1</span><span class="p">,</span> <span class="p">[</span><span class="dl">"</span><span class="s2">?</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">~</span><span class="dl">"</span><span class="p">]);</span> <span class="c1">// Jo이/가 공부를 하고 있습니다?~</span> </code></pre></div></div> <p><strong>여기서 call()과 apply()의 사용을 보면 <code class="language-plaintext highlighter-rouge">person2</code> 에만 있는 <code class="language-plaintext highlighter-rouge">study</code> 메서드를 <code class="language-plaintext highlighter-rouge">person1</code> 이 사용할 수 있도록 하고있다.</strong></p> <p>즉, 첫번째 매개변수를 통해 실행 문맥의 this를 정할 수 있도록 하는 것이다.</p> <p>그리고 2번째 인자부터는 해당 메서드의 매개변수를 넣는 것이다.</p> <p><strong>여기서 call()과 apply()의 차이점은 call은 인수를 순서대로 받지만 apply는 인수를 유사 배열로 받는다.</strong></p> <p>그럼 유사배열이란 무엇일까?</p> <p><img src="https://www.dropbox.com/s/lzw34u94ltvx3yj/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-07-28%2004.44.47.png?dl=1" alt="유사배열" /></p> <p>크롬 개발 도구로 찍으면 나오는 위 사진처럼, <strong>배열은 배열인데 Array.prototype을 상속받는 배열은 아닌 배열을 유사배열이라고 한다.</strong></p> <p>이것을 더 확실히 알고 싶다면 <code class="language-plaintext highlighter-rouge">instanceof</code>를 사용 해 볼 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">];</span> <span class="kd">const</span> <span class="nx">tags</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="dl">"</span><span class="s2">div</span><span class="dl">"</span><span class="p">);</span> <span class="nx">arr</span> <span class="k">instanceof</span> <span class="nb">Array</span><span class="p">;</span> <span class="c1">// true</span> <span class="nx">tags</span> <span class="k">instanceof</span> <span class="nb">Array</span><span class="p">;</span> <span class="c1">// false</span> </code></pre></div></div> <p>유사배열과 배열의 차이점은 단지 Array.prototype의 메서드를 사용 할 수 없다.</p> <p>유사배열이 가능한 이유는 Array도 Object이기 때문임을 알고 넘어가자.</p> <p>call()과 apply()는 this 실행 문맥을 가져와서 실행 할 수 있다는 공통점이 있지만, 매개변수를 받는 방법에 있어서</p> <p>call은 순서대로 받고 apply는 유사배열로 받는다는 차이점이 있다.</p> <h1 id="funtionprototypebind-를-설명하세요">Funtion.prototype.bind 를 설명하세요</h1> <p>위에서 설명한 apply, call과 세트인 3종 세트인 메서드다.</p> <p>간단하게 말하면, apply·call은 메서드의 결과를 리턴하고 bind는 메서드 자체를 리턴한다.</p> <p>bind는 원본 함수 객체를 감싸서 새롭게 바인딩하는 함수를 만드는데 이때 this를 바꾸는 것은 apply·call과 같지만 새롭게 감싸진 함수를 리턴한다.</p> <p>이때 추가 매개변수는 call의 그것처럼 인자를 순서대로 받는 방식을 사용한다.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">person1</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Jo</span><span class="dl">"</span><span class="p">,</span> <span class="p">};</span> <span class="kd">let</span> <span class="nx">person2</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Kim</span><span class="dl">"</span><span class="p">,</span> <span class="na">study</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">lastPoint1</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">.</span><span class="dl">"</span><span class="p">,</span> <span class="nx">lastPoint2</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">!</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">이/가 공부를 하고 있습니다</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">lastPoint1</span> <span class="o">+</span> <span class="nx">lastPoint2</span><span class="p">,</span> <span class="p">);</span> <span class="p">},</span> <span class="p">};</span> <span class="nx">person2</span><span class="p">.</span><span class="nx">study</span><span class="p">();</span> <span class="c1">// Kim이/가 공부를 하고 있습니다.!</span> <span class="c1">// bind()</span> <span class="kd">let</span> <span class="nx">student</span> <span class="o">=</span> <span class="nx">person2</span><span class="p">.</span><span class="nx">study</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">person1</span><span class="p">,</span> <span class="dl">"</span><span class="s2">!</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">?</span><span class="dl">"</span><span class="p">);</span> <span class="nx">student</span><span class="p">();</span> <span class="c1">// Jo이/가 공부를 하고 있습니다!?</span> </code></pre></div></div> <p>bind는 주로 React에서 Class Component를 작성할때 자주 마주치게 되는데 Component 상속을 할 때</p> <p><strong>부모 Component의 this 실행 문맥을 가져오기 때문에</strong> bind로 현재 Component의 this 실행 문맥으로 교체 해 주는 것이다.</p> <h1 id="documentwrite는-언제-사용하나요-"><code class="language-plaintext highlighter-rouge">document.write()</code>는 언제 사용하나요 ?</h1> <p>위 메서드를 Google에 검색 해 보면 알겠지만 2010년 글이 최신으로 올라 올 만큼 지금은 자주 사용되지 않는 메서드로 알려져있다.</p> <p>왜냐하면, 크롬 개발 도구에서 그대로 쳐보면 알 수 있듯이 모든 document를 날려버리고(document.open) 새로 인자로 들어온 요소를 사용하기 때문이다.</p> <p>단, IE에 대한 호환성 문제나 스크립트 병렬 로딩이 특수한 상황에서 필요할 때, 그리고 성능적인 문제로 새로 모든것을 시작하고 싶을 때(?) 사용 할 수 있다.</p> <p>예를 들면, 계속 사용하던 브라우저 탭에서 A라는 링크를 클릭하는 시간보다 해당 링크를 새창에서 입력하면 더 빠르게 로딩 될 수 있다.</p> <p>이게 가능한 이유는 현재까지 사용하던 브라우저는 과거의 내용에 대한 네비게이션과 JS 그리고 요새 브라우저들에 따라다니는 트래커들 때문에 리소스가 더 사용되지만</p> <p>새창은 document.write()를 사용하므로 기존 리소스를 로드 할 필요가 없기 때문이다.</p> <h1 id="ua-문자열을-이용하여-기능-검출feature-detection과-기능-추론featrue-inference의-차이점을-설명하세요">UA 문자열을 이용하여 기능 검출(feature detection)과 기능 추론(featrue inference)의 차이점을 설명하세요</h1> <p>이 질문에서는 새로운 Keyword가 3가지나 등장하므로 이를 먼저 알고가자</p> <ul> <li> <p>UA 문자열: User Agent String의 약어로 브라우저에서 어떤 사이트에 접속하면 서버는 요청한 브라우저가 어떤 브라우저인지에 따라 그에 맞게 결과를 보여준다. 예를들어 내가 Chrome으로 Naver를 들어가면 Naver 서버에는 User Agent로 아래 값을 받게 될 것이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36</span><span class="dl">"</span><span class="p">;</span> </code></pre></div> </div> <p>이러한 정보는 광고 솔루션에서 유저를 트래킹할 때도 유용하고, 특정 브라우저에서 사이트 이용을 제한할 때도 사용 할 수 이싿.</p> </li> <li> <p>기능 검출(feature detection) : 브라우저가 환경이 특정 기능을 제공하거나 제공하지 않을 수있는 단서를 프로그래밍 방식으로 테스트하여 런타임 환경 간의 차이를 처리하기 위해 웹 개발에 사용되는 기술</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="dl">"</span><span class="s2">geolocation</span><span class="dl">"</span> <span class="k">in</span> <span class="nb">navigator</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// navigator.geolocation를 사용할 수 있습니다</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// 부족한 기능 핸들링</span> <span class="p">}</span> </code></pre></div> </div> </li> <li> <p>기능 추론(feature inference): feature detection 처럼 기능을 확인하는 것은 똑같지만, A 라는 기능이 있을 때 B 도 있을거라고 추론하기 때문에 특정 상황이 아니면 사용 하지 않는 기법</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="dl">"</span><span class="s2">div</span><span class="dl">"</span><span class="p">))</span> <span class="p">{</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> </code></pre></div> </div> </li> </ul> <p>이렇게 3가지를 알고 문제를 다시 보면 조금 이해가 되는가?</p> <p>UA 문자열을 가지고 해당 유저의 환경에 대한 정보를 모두 가져 올 수 있는것을 활용해 두 패턴에 대해 예시를 들어 설명 해 보자면</p> <p>호환성 문제로 인해 Internet Explorer 지원을 하지 않기로 한 웹 서비스가 있다고 해보자.</p> <p>그럼 이때 FE 개발자는 UA를 가지고 Internet Explorer 를 감지 할 수 있을 것이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nb">navigator</span><span class="p">.</span><span class="nx">userAgent</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">Chrome</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// navigator.userAgent는 UA 를 얻을 수 있는 메서드</span> <span class="c1">// do original things</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">IE에서는 동작하지 않습니다. chrome을 이용하세요</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Feature Detection의 예시</span> <span class="nx">history</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="dl">"</span><span class="s2">http://chrome-download-link</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Featrue inference의 예시</span> <span class="p">}</span> </code></pre></div></div> <p>두 방식을 pseudo code 로 구현한 내용이다.</p> <p>alert()으로 알려주는 방식은 IE에서 잘 동작해서 경고를 띄워주겠지만(IE에서 페이지를 이동 해 주는 메서드가 없다는 전제)</p> <p>Feature inference 방식으로 작성한 history.push()는 IE에서 지원하지 않기 때문에 작동하지 않을 가능성이 크다.</p> <p>둘의 차이는 얼마나 추론과 탐지를 하느냐인데, 후자인 Feature inference는 특정 상황이 아니라면 사용하지 말아야 한다.</p> <h1 id="self-check">Self check</h1> <ol> <li>(본문에 없음) js에서 예약어로 사용되고있으며, 함수의 매개변수를 모두 가져 올 수 있는 것은?</li> <li>유사 배열이란 무엇인가요?</li> <li>링크를 클릭해 웹 사이트를 여는 것과 새창에서 여는것의 시간 차이가 서로 다른 이유는 무엇인가요?</li> <li>User Agent를 크롬 개발 도구에서 알 수 있는 방법은?</li> </ol> <h1 id="reference">Reference</h1> <ul> <li><a href="https://velog.io/@josworks27/함수호출-call-apply-bind-차이">https://velog.io/@josworks27/%ED%95%A8%EC%88%98%ED%98%B8%EC%B6%9C-call-apply-bind-%EC%B0%A8%EC%9D%B4</a></li> <li>https://www.zerocho.com/category/JavaScript/post/5af6f9e707d77a001bb579d2</li> </ul> Wed, 29 Jul 2020 00:00:00 +0000 https://samslow.github.io/development/2020/07/29/FE-interview-1/ https://samslow.github.io/development/2020/07/29/FE-interview-1/ web development Javascript 기초 - 버블 버블 버블 팝! 이벤트 버블링! <ul> <li>Javascript 이벤트 버블링과 이벤트캡처링 그리고 이벤트 위임에 대해 설명합니다.</li> </ul> <h1 id="기초가-흔들려서는-아니되오">기초가 흔들려서는 아니되오</h1> <p>JS 공부를 하면서 기초를 배우고 있지만 이벤트 버블링 외 2종(이벤트 캡처링, 이벤트 위임)은 JS를 배우기 시작했다면, 공부 할 수 있는 내용 중에 가장 이해도 쉽고, 내용도 상대적으로 적은 부분 중에 하나이다.</p> <p>하지만, 머리로 이해하는 것과, 실제로 사용하는 것은 매우 다르다.</p> <p>필자의 경우 이벤트 버블링 개념에 대해서 이해하고 있었지만, 얼마전 해커톤에서 Vanila JS로 JS 이벤트 핸들링을 개발하다가 그만 실수를 하고 말았다.</p> <p>특정 아이콘이 생성하고 다른 곳을 클릭하면 삭제하여 화면에 보였다 사라지는 기능을 개발 중 이었는데</p> <p>JS의 기본 이벤트 전파 방식은 이벤트 버블링인것을 알았으면서도, 문제를 파악하는데 수 시간을 삽질했다.(물론 다른 이유도 있지만) 이론과 실제가 다르고, 학교 공부와 실무는 다르며 내가 이해하는 것과 사용하는 것은 정말 다르다.</p> <p>늘 이 포스트에서는 내가 아는 것 이라도 기초는 여러번 봐도 부족하다는 마음으로 따라와 주시길 바란다.</p> <p>이 포스트는 내가 한 실수를 예제로 진행하려고 한다.</p> <h1 id="js-이벤트-핸들러-ㄷㄷㄷㅈ">JS 이벤트 핸들러 ㄷㄷㄷㅈ</h1> <p><img src="https://i.ytimg.com/vi/YV5OzhMaZYk/maxresdefault.jpg" alt="img" /></p> <div align="center" style="color: gray"> JS event 두둥등장!</div> <p>JS 의 이벤트 전파방식은 크게 2가지가 있다.</p> <ol> <li>이벤트 버블링 방식 Event Bubling</li> <li>이벤트 캡쳐 방식 Event Capture</li> </ol> <p>이 중 JS의 이벤트 정책은 <code class="language-plaintext highlighter-rouge">Event Bubling</code> 방식을 따른다.</p> <p>개념을 설명하기 앞서 Event Bubling은 Bottom-up방식이고, Event Captrue는 Top-Down 방식이다.</p> <p><img src="https://scx2.b-cdn.net/gfx/news/hires/2016/protectingco.jpg" alt="Protecting coral reefs with bubbles" /></p> <div align="center" style="color: gray">저 방울들이 다 우리의 Event 라는 Object!</div> <p>이름부터 느낌이 오지 않는가, 물속에 있는 기체는 방울(Bubble)을 형성하여 부력으로 위로 뜨게되고 Event Capture는 그냥 이것의 반대라고 생각하면 외우기 쉽다.</p> <h1 id="등잔밑이-씨꺼매서-아무것도-안보여">등잔밑이 씨꺼매서 아무것도 안보여</h1> <p><img src="https://mblogthumb-phinf.pstatic.net/20160225_197/zzingko2273_1456380245455vLKy3_JPEG/%BF%B5%C8%AD%BD%C5%BC%BC%B0%E8%C8%B2%C1%A4%B9%CE%BC%B1%B1%DB%B6%F3%BD%BA%C5%A9%B7%D2%C7%CF%C3%F7%BC%B1%B1%DB%B6%F3%BD%BAGRANDBEAST.jpg?type=w2" alt="" /></p> <div align="center" style="color: gray">이 짤만 보면 황정민 배우님이 "씨꺼매서 아무것도 안보여~"라고 하는게 들린다.</div> <p>혹시 크롬에서 드래그만으로 쉽게 번역 기능을 제공 해 주는 <a href="https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb/RK%3D2/RS%3DBBFW_pnWkPY0xPMYsAZI5xOgQEE-">Google Translation Extention</a>(이하 ‘구글 번역앱’)을 아는가?</p> <p>영어가 익숙치 않은 사람들에게 생산성과 번역 기능을 모두 제공 해 주는 혜자로운 앱이다(무료다!)</p> <p>바야흐로 2020년 엔젤핵 해커톤에서 필자는 구글 번역 앱 같은 크롬 익스텐션을 개발하고 있었다.</p> <p><img src="https://www.dropbox.com/s/kwsml4y47yniu2l/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-07-15%2000.10.45.png?dl=1" alt="" /></p> <p>위 사진처럼 텍스트를 드래그하면, 구글 번역앱 아이콘이 나타나고, 이를 선택하면 한글 뜻이 나오는 동작 방식을 가지고 있다.</p> <p>하지만 이상하게 버튼을 눌러도 아무 동작도 하지 않는 것이었다.</p> <p>설상가상 필자의 익스텐션을 설치하면 구글 번역 아이콘까지도 뜨지 않으니,</p> <p>50줄도 안되는 코드에 무슨 문제가 있겠나 싶어 크롬 익스텐션의 특별한 무언가가 있을거라 생각하고 검색을 했더랫다</p> <p>알고보니 다른 이벤트에서 버블링이 일어나 아이콘 기능에까지 영향을 미치고 있었고</p> <p>mouse 이벤트로 트리거 되고 있었기 때문에 다른 이벤트로 돌리고, 이벤트 전파를 막는 방향으로 현재는 잘 해결한 상태다.</p> <p>이벤트 버블링은 무엇이길래 서로 다른 요소들끼리 이벤트를 주고받게 하고, 또 어떻게 주고받게 하는 것일까?</p> <p>알고 있던 내용인데 실전에선 씨꺼매서 아무것도 보지 못한 맹인의 입장에서 설명을 시작한다.</p> <h1 id="이벤트-버블링-event-bubling">이벤트 버블링 Event bubling</h1> <p>이벤트 버블링은 아래 그림으로 모두 설명이 가능하다.</p> <p><img src="https://joshua1988.github.io/images/posts/web/javascript/event/event-bubble.png" alt="img" /></p> <div align="center" style="color: gray">by 캡틴팡요</div> <p>쉽게 말하면 버튼, div, label 등의 HTML 요소를 클릭하면 이벤트가 발생하는데, 이 이벤트는 상위의 요소들(조상 요소)로 전파되는 것을 이벤트 버블링이라고 한다. 버블링은 전파를 멈추지 않는한 무조건 최상위 객체까지 간다. 이것이 body일 수도 있고 다른 요소일 수도 있다.이것보다 더 쉽게 설명을 할 수 있을지는 모르겠다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">showIcon</span><span class="p">()</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">icon</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">div</span><span class="dl">"</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mousedown</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span> <span class="p">});</span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">icon</span><span class="p">);</span> <span class="p">}</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseup</span><span class="dl">"</span><span class="p">,</span> <span class="nx">showIcon</span><span class="p">);</span> </code></pre></div></div> <p>필자의 코드다.</p> <p>icon을 create하여 event를 붙여 body의 최하단에 appendChild 하는 간단한 함수이다.</p> <p>document는 우리가 보는 모든 문서이고, 여기에 <code class="language-plaintext highlighter-rouge">mouseup Event</code>가 발생하면 showIcon 함수가 동작하도록 한 것이다.</p> <p>여기서 드래그한 텍스트를 추출하는 기능과 아이콘을 숨기는 코드를 추가 해 보겠다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">selectHandler</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">text</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getSelection</span><span class="p">().</span><span class="nx">toString</span><span class="p">();</span> <span class="k">if</span> <span class="p">(</span><span class="nx">text</span> <span class="o">!==</span> <span class="dl">""</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 선택된 요소가 텍스트라면 아이콘을 보여주도록 한다.</span> <span class="nx">showIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">showIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> <span class="nx">removeIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span> <span class="c1">// 여기서 removeIcon을 한번 더 호출해 화면에 있는 다른 아이콘을 삭제한다.</span> <span class="kd">let</span> <span class="nx">icon</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">div</span><span class="dl">"</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">studyMouseIcon</span><span class="dl">"</span><span class="p">;</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mousedown</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">selected Text</span><span class="dl">"</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span> <span class="p">});</span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">icon</span><span class="p">);</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">removeIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">icon</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">studyMouseIcon</span><span class="dl">"</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">선택된 텍스트</span><span class="dl">"</span><span class="p">,</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getSelection</span><span class="p">().</span><span class="nx">toString</span><span class="p">());</span> <span class="c1">// e.stopPropagation 대신 텍스트가 없을때 지워지도록 했다.</span> <span class="k">if</span> <span class="p">(</span><span class="nx">icon</span> <span class="o">!==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getSelection</span><span class="p">().</span><span class="nx">toString</span><span class="p">()</span> <span class="o">==</span> <span class="dl">""</span><span class="p">)</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">hide icon</span><span class="dl">"</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mouseup</span><span class="dl">"</span><span class="p">,</span> <span class="nx">selectHandler</span><span class="p">);</span> <span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mousedown</span><span class="dl">"</span><span class="p">,</span> <span class="nx">removeIcon</span><span class="p">);</span> </code></pre></div></div> <p>이벤트 버블링을 막는 방법은 아주 단순하게 해당 이벤트를 전파 정지하는 <code class="language-plaintext highlighter-rouge">stopPropagation()</code>API 를 사용하면 된다.</p> <p>하지만 필자는, 완전히 이벤트가 멈추는것을 바라지 않았기 떄문에 다른 방식으로 처리 해 주었다.</p> <p>또 주의해서 보아야 할 것은 <code class="language-plaintext highlighter-rouge">mouseup</code>이벤트와 <code class="language-plaintext highlighter-rouge">mousedown</code> 이벤트의 차이점이다. 말 그대로 down은 누르는 단계, up은 버튼에서 손을 떼는 단계이다.</p> <p>일반적인 mouse는 down과 up 이벤트가 한 세트이지만, mac의 TrackPad에서는 신기하게 터치로 누르면 down 이벤트만 발생하고, 꾹 눌렀다 떼야만 down과 up이 모두 발생한다.( 필자의 실수일 수도 있으니, 혹시 틀렸다면 댓글 남겨주세요. )</p> <p>여기서 이벤트 전파를 막아주지 않으면 생성과 삭제가 동시에 이루어지기 때문에 절대 의도한대로 동작하지 않는다.</p> <h1 id="이벤트-캡처-event-capture">이벤트 캡처 Event Capture</h1> <p>위에서 설명한 것 처럼 이벤트 캡처는 이벤트 버블링의 반대다.</p> <p><img src="https://joshua1988.github.io/images/posts/web/javascript/event/event-capture.png" alt="img" /></p> <p>JS에서 이벤트 캡처는 특정 상황에서 옵션으로 설정 할 때만 사용 할 수 있다.</p> <p><code class="language-plaintext highlighter-rouge">addEventListener</code>의 3번째 객체에 <code class="language-plaintext highlighter-rouge">{captrue: true}</code> 를 전달 해 주면 된다.</p> <p>내가 클릭한 요소의 자손 요소에게 이벤트를 Top-down으로 내려주는 방식이며, 위에서 설명한 <code class="language-plaintext highlighter-rouge">stopPropagation()</code> 을 이용하면 똑같이 전파를 막아 줄 수 있다.</p> <p>다른 것들은 모두 이벤트 버를링과 같다. 아마 사용할 일은 극히 적을 것이지만 알아두어서 나쁠 것은 없다.</p> <h1 id="이벤트-위임-event-delegation">이벤트 위임 Event Delegation</h1> <p>아까의 코드로 잠깐 돌아가보자</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">showIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span> <span class="nx">removeIcon</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span> <span class="kd">let</span> <span class="nx">icon</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">div</span><span class="dl">"</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">studyMouseIcon</span><span class="dl">"</span><span class="p">;</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">mousedown</span><span class="dl">"</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">selected Text</span><span class="dl">"</span><span class="p">,</span> <span class="nx">text</span><span class="p">);</span> <span class="nx">icon</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span> <span class="p">});</span> <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">icon</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <p>icon에 매번 <code class="language-plaintext highlighter-rouge">addEventListener</code> 를 추가해 이벤트를 트리거 해주고 있다.</p> <p>여기서는 이런 방식의 코드가 낫겠지만, <code class="language-plaintext highlighter-rouge">&lt;ul&gt;</code>과 <code class="language-plaintext highlighter-rouge">&lt;ol&gt;</code> 하위의 <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code> 를 넣고 여기에도 이벤트를 달아주어야 하는 상황이라면</p> <p><code class="language-plaintext highlighter-rouge">&lt;ul&gt;</code>과 <code class="language-plaintext highlighter-rouge">&lt;ol&gt;</code> 에서 이를 관리하도록 하고 <code class="language-plaintext highlighter-rouge">&lt;li&gt;</code> 는 이벤트를 신경쓰지 않아도 되는 가장 바람직한 코드 일 것이다.</p> <p>이렇게 조상 요소에서 자식 요소의 이벤트 처리를 위임 받는 것을 이벤트 위임이라고 한다.</p> <p>단, 이벤트의 종류가 모두 같다는 가정이므로 만약 list 마다 다른 요소가 적용된다면, 필자의 코드대로 동적으로 적용하는 것이 유용 할것이다.</p> <h1 id="self-check">Self check</h1> <ol> <li>이벤트 버블링은 (Top-down/ Bottom-up) 방식으로 일어난다.</li> <li>이벤트 버블링은 DOM의 모든 이벤트를 전달 할 수 있다. ( O / X )</li> <li>이벤트 캡처링은 이벤트를 캡쳐하는 순간 전파를 정지시킨다 ( O / X )</li> </ol> <h1 id="closing">Closing</h1> <p>이 포스트를 보았으니 이제 면접에서도 잘 대답 할 수 있어야 하고 실제 코드를 작성 할 때도 이벤트 관련해서 실수하는 일이 없어야 한다. 사실 이렇게 말은 해도 할 수 있을지는 모르곘다. 그래서 기초가 중요한 것 같다. 지루하고 뻔해도 그것을 설명하고 사용하는 것은 별개의 문제이다.</p> <p>Javascript 기초는 Fun하고 Cool하게 계속 될 것이다. 기술 블로그는 계속되어야 하니깐(끄덕)</p> <p><img src="https://thumbs.gfycat.com/MarvelousQueasyCavy-size_restricted.gif" alt="MarvelousQueasyCavy" /></p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/">Captain pangyo</a></li> </ul> Tue, 14 Jul 2020 00:00:00 +0000 https://samslow.github.io/development/2020/07/14/Event-handling/ https://samslow.github.io/development/2020/07/14/Event-handling/ web development Javascript 기초 - JavaScript 개발한다면 JIT은 알아야JIT <ul> <li>Javascript Engine 최적화 기법 중 하나인 JIT(Just-In-Time)을 설명 합니다. <ul> <li>VM의 장점 2가지를 설명 할 수 있습니다.</li> <li>JIT의 장점을 설명 할 수 있습니다.</li> <li>JIT의 동작 방식을 설명 할 수 있습니다.</li> </ul> </li> </ul> <h1 id="vm의-산소같은-너-jit">VM의 산소같은 너, JIT</h1> <p>JIT을 알기 전에 JIT은 생소 할 수 있어도, JVM이나 운영체제의 VM은 들어 본 사람이 있을 것이다.</p> <p>VM은 Virtual Machine의 준말로 가상 머신을 의미한다. 즉 컴퓨터에 들어있는 CPU, Storage, Memory 는 한 세트지만, 이것을 기반으로 새로운 컴퓨터를 여러개를 띄우는 것을 말한다.</p> <p>Docker는 <code class="language-plaintext highlighter-rouge">컨테이너</code>지만 그 뿌리로 보았을때 VM과 비슷하고, AWS Lambda 도 사용하고 있다고 볼 수 있는 것이다. 컨테이너와 VM의 차이를 알고싶다면 <a href="[https://food4ithought.com/2019/10/26/%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0virtual-machine-vs-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88container/](https://food4ithought.com/2019/10/26/가상머신virtual-machine-vs-컨테이너container/)">여기</a>를 참고하시라</p> <p><img src="https://subicura.com/assets/article_images/2017-01-19-docker-guide-for-beginners-1/docker-logo.png" alt="초보를 위한 도커 안내서 - 도커란 무엇인가?" /></p> <p><img src="https://www.dropbox.com/s/yfib3t6jipe8rvt/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-07-06%2014.45.38.png?dl=1" alt="" /></p> <p>가상 환경의 장점은 아래와 같다.</p> <ol> <li>하나의 장치를 여러 개처럼 동작시키거나, 반대로 여러개의 장치를 하나처럼 만들 수 있다.</li> <li>분할된 가상 환경들이 서로 상호 간섭 없는 독립성을 보장 받을 수 있다.</li> </ol> <p>즉 이런 가상환경이 있어서 macOS에서도 windows나 Linux를 돌릴 수 있는 것이고 이외에도 다양한 쓰임새가 있는 기술이다.</p> <p>이런 VM기술이 가능하게 하는 것중 하나가 바로 JIT이다.</p> <p>JIT은 JVM에서도 쓰이지만 Chrome 의 JS엔진인 V8에서도 사용되는 산소같은 존재이다.</p> <h1 id="jit금-부터-jit을-배워보자">JIT금 부터 JIT을 배워보자</h1> <p>JITC(just-in-time compilation)은 동적 번역이라고도 불리우고, 이름에 맞게 프로그램을 실제 실행하는 시점에 바이트 코드를 기계어로 번역하는 컴파일 기법이다. 굳이 한글로 하자면 <code class="language-plaintext highlighter-rouge">그때그떄</code> 로 해석 할 수 있겠다.</p> <p><img src="https://image.toast.com/aaaadh/real/2016/techblog/jit%281%29.png" alt="jit.png" /></p> <div align="center" style="color: gray">JITC 동작 방식-IR(Intermediate representation)</div> <p>JITC 외에도 컴파일 기법에는 실행 중에 읽어가면서 대응하는 <strong>인터프리터 방식</strong>과 실행 전에 컴파일하는 <strong>정적 컴파일 방식</strong>이 있다.</p> <p>인터프리터 언어에는 스크립트 언어가 대부분인데, JS나 HTML, python, Ruby 등이 있고,</p> <p>정적 컴파일 언어에는 자료형이 고정되어 있는 언어 즉 JAVA나 C같은 언어들이다.</p> <p>언어가 인터프리터 방식이라고 JIT이 아닌것은 아닌데, 그 엔진이 구현하는 방식에 의존적이기 때문이다.</p> <p>현대의 Safari, Chrome, FireFox의 JS엔진은 모두 JITC 방식을 사용한다.</p> <p>JIT은 하나씩 읽어가며 실행하는 인터프리터보다는 당연히 빠르고, 미리 컴파일하는 정적 컴파일보다는 성능이 떨어진다.</p> <p>JITC는 컴파일 과정이 한번에(just in time) 일어나긴 하지만 기계어로 변환한 것이 인터프리터의 수행시간보다 낫기 때문에 더 빠를 수 있는 것이다.</p> <h1 id="그럼-js는-무조건-jitc-쓰는게-이득이네">그럼 JS는 무조건 JITC 쓰는게 🐶이득이네</h1> <p><img src="https://jjalbot.com/media/2018/12/Q0ntedUTY/zzal.gif" alt="" /></p> <p>라고 말한다면 <del>경기도</del> 큰 오산이다.</p> <p>JITC을 사용함으로써 얻는 장점은</p> <ol> <li>정적 컴파일처럼 미리 바이트 코드로 컴파일을 하지않고 실행 시간에 이를 결정하는 장점</li> <li>정적 컴파일 처럼 최적화를 일부 적용할 수 있는 장점</li> </ol> <p>하지만 최적화라는것이 무엇인가, 내가 생각하는 흐름대로 오버헤드가 발생할 때 적용하면 효과가 극대화 되는 것이다.</p> <p>JS는 <a href="https://samslow.github.io/development/2020/06/09/Javascript_Basic_Prototype-Chaining/">이전 포스트</a>에서 보다시피 <code class="language-plaintext highlighter-rouge">동적 언어</code> 이기 때문에 <code class="language-plaintext highlighter-rouge">let</code>, <code class="language-plaintext highlighter-rouge">const</code>, <code class="language-plaintext highlighter-rouge">var</code> 에 저장된 타입이 실행 시점에서 결정되고, 한번 결정되었다고해서 변수의 타입이 바뀌지 않을 것이라는 보장도 없다.</p> <p>이런 JS의 특성 때문에 에러처리나 예외 케이스를 고려하면 기계어로 변환했을때에 그 양이 매우 많아진다.</p> <p>많은 분들이 아는 JS 매직(?)은 아래와 같다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">99</span><span class="dl">"</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// "991"</span> <span class="dl">"</span><span class="s2">99</span><span class="dl">"</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// 98 ?</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span> <span class="o">*</span> <span class="dl">"</span><span class="s2">3</span><span class="dl">"</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">4</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// "64"</span> <span class="dl">"</span><span class="s2">2</span><span class="dl">"</span> <span class="o">*</span> <span class="dl">"</span><span class="s2">3</span><span class="dl">"</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">4</span><span class="dl">"</span> <span class="o">*</span> <span class="dl">"</span><span class="s2">5</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// 26</span> </code></pre></div></div> <p>어떤가.. 타입이 예상할 수 없이 튀는 코드들을 제어하려면 기계어가 많아지는 것은 당연지사이다.</p> <p>이렇게 되면 최적화를 활용하는 JITC의 장점이 퇴색되고, 인터프리터 방식과 성능 면에서 큰 차이가 안나게 된다.</p> <p>또한, JS는 Web에서 layout을 주로 건드리기 때문에 for-loop이나 알고리즘이 들어가는 어떤 로직을 잘 사용하지 않기로 알려져있기 때문에 자주 반복되어서 실행되는 부분이 적다.(물론 현대의 Frontend는 Backend의 로직을 점점 가져오고 있긴 하지만, 여기선 고전적인 JS라고 생각한다.)</p> <p>자주 반복되는 부분이 적으면 최적화를 적용해서 기껏 만들어 놔 봤자, 그것을 활용하지 않기 때문에 결과적으로 JITC으로 한번에 기계어까지 번역하는 것이 효용이 적기 때문에 인터프리터 방식으로 실행하는 것이 낫다.</p> <h1 id="아-그니까-그럼-결국-인터프리터쓰라는-거죠">아.. 그니까 그럼 결국 인터프리터쓰라는 거죠?</h1> <p>라고 생각하면 경기도 오···(ㅈㅅ)</p> <p>이런 갭을 극복하기 위해서 최근의 JS엔진들은 JIT의 업그레이드 버전인 Adaptive JITC를 사용한다.</p> <p>AJITC는 모든 코드를 일괄적으로 같은 최적화를 적용하지 않고, 반복 수행 정도에 따라 유동적으로(adaptive) 서로 다른 최적화 수준을 적용하는 방식이다.</p> <p>모든 코드는 인터프리터로 수행하다 자주 반복된다 싶으면 그 부분만 JITC를 적용한다는 것이다.</p> <p>Chrome V8에서는 crankshaft라는 Adaptive JITC을 아래와 같이 동작하게한다.</p> <p><img src="https://image.toast.com/aaaadh/real/2016/techblog/crankshaft.png" alt="crankshaft.png" /></p> <p>여기서 핵심은 RuntimeProfiler가 존재해서 함수 호출의 빈도를 측정해 JITC으로 할지 인터프리터로 할지 결정한다는 것이다.</p> <p>단, 이 경우에도 최적화의 한 방식이기 때문에 타입이 변하는 경우는 역시 비효율을 발생 시킬 수 있다.</p> <p>대부분의 개발자가 평소에 JS로 개발할 때 한가지 변수에 대해 여러 타입을 할당하는 경우는 적지만,</p> <p>이것을 알고 안 쓰는 것과 모르고 안 쓰는 것은 다르다.</p> <h1 id="self-check">Self Check</h1> <ol> <li>VM(Virtual machine)의 장점 2가지를 설명하시오.</li> <li>컴파일 방식의 대표적인 3종류와 성능 순서를 설명하시오.</li> <li>고전 JIT에는 (JITC / 인터프리터) 가 (JITC / 인터프리터) 보다 효율이 좋다.</li> <li>최근 Frontend의 역할이 커짐에 따라 <code class="language-plaintext highlighter-rouge">_____</code> 방식이 효율적이다.</li> <li><code class="language-plaintext highlighter-rouge">______</code> 는 Adaptive JITC의 핵심으로, 함수 호출 빈도를 측정해준다.</li> </ol> <h1 id="closing">Closing</h1> <p>JIT은 들어는 보았지만, 어떤식으로 동작하는지는 몰랐었는데, 이번 기회로 더 디테일하게 볼 수 있었던 것 같다.</p> <p>사실 최근 본 면접에서 물어봤을때 몰라서 공부하게 되었다. 면접은 최고의 교본이 되는 것 같다.</p> <p>알고보면 어렵지 않은데(물론 깊이 파면 논문 파야한다) 그것을 안다는 것은 다른 문제이다.</p> <p>다음에는 잘 대답 할 수 있겠지..?</p> <p>이외에도 꿀팁은 배열에는 하나의 타입만 넣는것이 최적화 측면에서 유용하므로 기억하자.</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://meetup.toast.com/posts/77">TOAST 기술 블로그</a></li> </ul> Mon, 06 Jul 2020 00:00:00 +0000 https://samslow.github.io/development/2020/07/06/JIT/ https://samslow.github.io/development/2020/07/06/JIT/ web development Javascript 기초 - 별에서 온 그대, Web이 동작하는 방식 <ul> <li>Client가 버튼을 누른 결과가 Server를 거쳐 다시 Client에 오는 과정을 설명합니다. <ul> <li>Web에서 사용되는 기본 개념들에 대해서 알 수 있습니다.</li> <li>Client to Client 의 순환 과정을 알 수 있다.</li> <li>Browser가 하는 역할에 대해서 알 수 있다.</li> </ul> </li> </ul> <h1 id="web-기본부터-alaboza">Web 기본부터 Alaboza</h1> <p>Web Frontend를 지향하는 개발자라고 한다면 JS 기초나 CS 지식을 필수적으로 알아야 하겠지만, 역시 그 중심에는 Web이 어떻게 동작하여 사용자에게 까지 도달 하는지를 아는 것이 중요하다.</p> <p>Web이란 기본적으로 <code class="language-plaintext highlighter-rouge">거미줄</code> 이라는 뜻을 가지고 있는데 전 세계에 퍼진 회선의 모양이 거미줄 같아서 붙여진 뜻이다.</p> <p>또한 Web은 <code class="language-plaintext highlighter-rouge">World Wide Web(WWW)</code>의 준말으로도 알려져 있다.</p> <p><img src="https://cdn.guidingtech.com/media/assets/WordPress-Import/2016/06/shutterstock_112158140.jpg" alt="GT Answers: The Difference Between the Internet and the World Wide Web" /></p> <div align="center"><em style="color: gray;">뭔가 최신 기술이 나올때면 익숙하게 보던 그 그림..</em></div> <p>Web 동작 방식에 대한 Shallow한 개념은 아래와 같다.</p> <ol> <li>사용자가 도메인(ex. www.google.com) 을 주소창에 입력한다.</li> <li>브라우저는 이 도메인을 가진 IP 주소를 DNS 서버로 부터 얻는다.</li> <li>IP를 가진 서버에서는 HTML, CSS, JS로 구성된 파일을 브라우저로 보낸다.</li> <li>브라우저는 이를 해석하여 다시 사용자에게 보여준다.</li> </ol> <p>하지만, 이같은 내용은 매우 기본적인 내용이고, 사실 이 안에는 더 복잡하고 상세한 내용이 담겨져 있다.</p> <p>이 포스팅에서는 이런 내부 구조와 동작에 대해 상세히 알아보고</p> <p>마지막에는 이런 일련의 동작들을 Deep한 개념으로 정리 해 보려고 한다.</p> <h1 id="천리길도-한걸음부터-web-기본개념">천리길도 한걸음부터, Web 기본개념</h1> <p>위에서 설명했듯 Web은 알아갈수록 더 알아야 할 것이 많아지는 필드 중 하나이다.</p> <p>도대체 우리가 쓰는 safari, chrome, firefox 같은 브라우저들은 지들이 뭘 안다고 naver를 보여주고 google을 보여주는걸까?</p> <p>이런 천리길을 가기전에 기본 개념을 확실히 이해한다면, 본인의 사전만을 가지고 하나씩 내용을 추가하며 정상까지 도달 할 수 있을 것이다.</p> <h3 id="호스팅">호스팅</h3> <p>호스팅(hosting)이란 서버 컴퓨터의 전체 또는 일정 공간을 이용할 수 있도록 임대해 주는 서비스이다.</p> <p>즉, 호스팅 업체가 회사나 개인에게 항시 가동중인 서버를 임대 해 주고 그 대가를 받는 서비스가 호스팅이다.</p> <p>Web을 제공하는 회사들은 대부분 별도의 호스팅 서버를 운용하여 외부 요청에 응답한다. 즉, 24시간 꺼지지 않도록 해야하며 필요하다면 이중화구조로 장애 발생시에도 응답에 문제가 없도록 대처 할 수 있도록 구성해야 한다.</p> <p>최근에 Twitch에서 스트리머들의 생방송을 본 적이 있는데, 이때에도 A 라는 스트리머가 방송을 종료하며 B 라는 스트리머에게 시청자들을 <strong>호스팅</strong> 한다는 개념이 등장한다. 쉽게 이해하기 위해서는 영어 단어 그대로 <code class="language-plaintext highlighter-rouge">Host + ~ing</code> 이므로 주최(Host)하다 로 이해할 수도 있을 것 같다.</p> <h3 id="ip">IP</h3> <p>Internet Protocol의 준말로 송신 호스트와 수신 호스트가 패킷 교환 네트워크에서 정보를 주고받는 데 사용하는 정보 위주의 규약(프로토콜, Protocol)이며, OSI 네트워크 계층에서 호스트의 주소지정과 패킷 분할 및 조립 기능을 담당한다.(<a href="[https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%EB%84%B7_%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C](https://ko.wikipedia.org/wiki/인터넷_프로토콜)">위키</a>)</p> <p>그냥 쉽게 데이터가 어떤 규약에 의해 주고받아지고, 이를 분해해서 보내고 조립해서 사용자에게 보여주는 규약이다.</p> <p>그리고 이를 기반으로 한 IP 주소는 장치들이 서로의 위치를 인식하고 통신하기 위해 사용하는 번호이다.</p> <p>IP주소는 모든 네트워크를 지원하는 장비에 할당되어있다. 우리가 쓰는 스마트폰부터, 랩탑, 데스크탑, 심지어 애플워치나 인공지능 스피커까지 다양하게 있다.</p> <h3 id="dns">DNS</h3> <p>위에서 설명한 IP 주소의 형태는 IPv4 기준으로 172.16.254.1 같은 표기법을 가지고 있는데, 사용자가 매번 이런 IP 주소를 외우기는 쉽지 않기 때문에 더 쉽게 IP 주소를 알아내개 위해 만든 것이 Domain이고 이를 관리하는 곳이 Domain Name Server(DNS)이다.</p> <p>DNS의 주 기능은 특정 컴퓨터(또는 네트워크로 연결된 임의의 장치)의 주소를 찾기 위해, 사람이 이해하기 쉬운 도메인을 숫자로 된 식별 번호(IP 주소)로 변환해 준다.</p> <p>DNS에는 우리가 아는 KT나 LG유플러스를 시작으로, CloudFlare나 Google 도 DNS 를 가지고 있다.</p> <p>우리가 흔히 사용하는 www.google.com 이나 www.naver.com 도 Domain으로 DNS에서 IP 주소로 변환된다.</p> <p>당연히 이런 이름들은 중복되서는 안되기 때문에 DNS에서는 이를 조절해준다. 또한 서로 다른 DNS로 정보를 전달하는 시간때문에 DNS 등록 직후 바로 조회되지 않고 각 DNS 업데이트 주기에 맞추어 1~2일 정도 있다가 사용 가능해지기도 한다.</p> <p>여담이지만, 좋은 이름엔 높은 가격이 붙어있어서 이런 도메인을 미리 사 놓고 도메인을 판다고 올려 놓은 뒤 재테크를 하는 사람도 있다고 한다.</p> <h3 id="http">HTTP</h3> <p>간단하게 Request와 Response를 통해 이뤄지는 통신 구조로 두 서버끼리 연락할 때 그 방식을 정해놓은 규약이라고 보면 된다.</p> <p>우리가 흔히 쓰는 GET, POST, DELETE, PATCH, PUT ··· 는 HTTP Request이고</p> <p>200 OK, 300 Rediredt, 404 Not Found 등은 HTTP Response의 종류이다.</p> <p>또한 무상태 Stateless이기 때문에 단순히 통신만으로는 로그인 정보, 방문 횟수 등의 브라우저 정보를 저장할 수 없고 이를 위해서는 쿠키나 세션을 이용해야만 한다.</p> <p>HTTP 규약은 전통적으로 HTTP/1.x을 사용되었지만 TLS 추가, 속도 및 기능이 개선된 HTTP/2를 사용하는 것이(아마 나도모르게 사용하고 있겠지만) 여러모로 더 낫다.</p> <h3 id="isp">ISP</h3> <p>ISP(Internet service provider)는 인터넷에 접속하는 수단을 제공하는 주체를 가리키는 말이다.</p> <p>쉽게 말해 Web(거미줄)을 물리적으로 만드는 곳이라고도 볼 수 있는데, 대륙간 통신이나 적도에서 극지방에 이르기까지 광섬유를 산넘고 바다건너 지하에다가 쭉 깔아두는 회사라고 보면 된다. 혹자는 위성 인터넷을 쓰면 되지 않느냐고하는데, 맞다. 위성 인터넷도 하나의 대안일 수 있지만 위성까지 왕복하는 거리가 광섬유로 연결하는 것보다 배이상 길기 때문에 지연시간도 거기에 정비례하게 되기 때문에 속도나 안정성 측면에서 광섬유로 연결하는것이 낫다.</p> <p><img src="https://t1.daumcdn.net/cfile/tistory/9978B53C5C21E3AE37" alt="img" />ISP도 다 같은 ISP가 아니고 Tier 1, 2, 3으로 구분된 제공자로 구분된다.</p> <p>Tier 1은 주로 대륙간 트래픽 교환을 담당하고 Tier 2는 Tier 1과 3을 이어주는 중매및 피어링을 담당한다.(다른 ISP끼리 트래픽을 교환하는 것) 우리가 가장 가까이에서 볼 수 있는 Tier는 Tier 3으로 대한민국에서는 KT, Uplus 등이 이에 해당한다.</p> <h1 id="클라이언트-to-클라이언트의-전체-흐름">클라이언트 to 클라이언트의 전체 흐름</h1> <p><img src="https://media.springernature.com/original/springer-static/image/chp%3A10.1007%2F978-981-13-1747-7_14/MediaObjects/464682_1_En_14_Fig1_HTML.png" alt="" /></p> <div align="center"><em style="color: gray;">생각보다 이것을 제대로 설명한 그림을 찾기가 힘들다..</em></div> <p>전체적인 흐름은 사용자가 특정 도메인 주소를 주소창에 입력하는 것부터 시작해서 요청한 도메인의 컨텐츠가 화면에 나오는 것까지를 다룬다.</p> <p>다음은 www.google.com을 요청하는 것을 예시로 한다.</p> <h3 id="웹-서버로-요청">웹 서버로 요청</h3> <ol> <li>사용자는 주소창에 www.google.com을 입력한다.</li> <li>브라우저는 해당 도메인을 HTTP 규약에 맞춰 데이터 패킷을 준비한다.</li> <li>준비된 패킷은 랜선 혹은 AP를 통해 해당 지역의 Tier 3 ISP 까지 전달된다.</li> <li>이때 클라이언트는 빠른 응답을 위해 Cache Server에 캐싱 해 놓은 결과가 있는지 먼저 확인하고 만약 캐시된 데이터가 있으면 더 진행하지 않고 이를 다시 클라이언트에 되돌려준다.</li> <li>ISP는 DNS를 겸하기도 하기 때문에 요청으로 들어온 www.google.com의 IP 주소를 확인한다.</li> <li>만약 해당 DNS에 정보가 없다면 다른 DNS 서버에 해당 도메인이 있는지 확인한다.</li> <li><code class="language-plaintext highlighter-rouge">216.58.220.142</code> 가 www.google.com 의 IP 주소임을 브라우저가 알게 된다.</li> <li>브라우저는 해당 IP 주소로 HTTP Request를 보낸다.</li> <li>Google의 WAS(Web Application Server)는 요청을 받아서 DB작업 필요하다면 이를 처리한다.</li> <li>사용자 요청에 맞는 컨텐츠를 Status Code같은 내용과 함께 HTTP Response로 돌려 보낸다.</li> <li>다시 수많은 Router들과 ISP를 거쳐 사용자의 브라우저에 컨텐츠가 도달한다.</li> </ol> <h3 id="웹-서버로부터-응답-받고-브라우저가-하는-일">웹 서버로부터 응답 받고 브라우저가 하는 일</h3> <ol> <li>처음 브라우저가 응답을 받으면, 브라우저가 가지고있는 파서를 이용해 HTML문서를 브라우저가 이해할 수 있는 DOM 트리 형식으로 파싱한다.</li> <li>CSS를 파싱하여 스타일 구조체의 형식으로 만든다. 이를 CSSOM이라고 한다.</li> <li>DOM과 CSSOM을 실제 화면에 표현하기 위한 데이터 구조인 렌더링 트리로 변환한다.</li> <li>해당 렌더링 트리를 그리고 화면에 표시한다.</li> </ol> <h1 id="self-check">Self Check</h1> <ol> <li>호스팅의 개념에 대해서 설명 해 주세요.</li> <li>IP 주소를 외우기 어려운 사용자를 위해 더 쉽게 외울 수 있는 이름을 사용하도록 한 서버는?</li> <li>ISP의 Tier 1이 하는 일은 무엇일까요?</li> <li>브라우저가 HTML 문서를 사용자가 볼 수 있도록 해석하는 역할을 하는 것은 ?</li> </ol> <h1 id="closing">Closing</h1> <p>나름 상세하게 다룬다고 다뤘지만, 사실 위에서 설명한 내용 외에도 Router 끼리의 통신이나 TCP/IP, UDP, 3 hand shaking 등의 내용을 담기에는 너무 글이 길어질 것 같아 어느정도 갈무리하여 작성했다.</p> <p>핵심은 누군가 ‘웹이 동작하는 방식을 설명 해 주세요.’라고 했을때 그 과정을 보다 Deep 하게 설명을 할 수 있다면 이 글은 유의미할 것이다. 생각보다 ISP의 존재를 모르는 개발자가 많기 때문에(뭐 꼭 알아야 하나 싶기도하지만) 가볍게라도 알아두면 좋을 것이다.</p> <p>또한 이 글을 위해 여러가지 문서와 영상을 조사하면서 위성 인터넷이나 실제 WAS 서버의 동작 방식에 대해서도 접할 수 있었는데 이것은 기회가 닿는대로 또다른 post 하려고 한다.</p> <h1 id="reference">Reference</h1> <ul> <li><a href="[https://velog.io/@ppl8709/WEB%EC%9D%B4-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%EC%9B%90%EB%A6%AC](https://velog.io/@ppl8709/WEB이-동작하는-원리)">ppl8709</a></li> </ul> Mon, 29 Jun 2020 00:00:00 +0000 https://samslow.github.io/development/2020/06/29/WebBasicConcept/ https://samslow.github.io/development/2020/06/29/WebBasicConcept/ web development Javascript 기초 - ES 6 대격변의 시대 <ul> <li>javascript는 ES6 이전과 이후로 달라진 내용을 하나씩 톺아보고 최신 표준에 대해 알아봅니다. <ul> <li>ECMAScript 에 대해 설명 할 수 있습니다.</li> <li>ES6의 주요 특징 5가지를 이야기 할 수 있습니다.</li> <li>ES11에 추가된 주요 특징을 이야기 할 수 있습니다.</li> </ul> </li> </ul> <h1 id="그냥-es11인가-뭐시깽이-쓰면-되는거-아님">그냥 ES11인가 뭐시깽이 쓰면 되는거 아님?</h1> <p><img src="https://poiemaweb.com/img/es6.png" alt="es6 Logo" /></p> <p>우리가 흔히 쓰는 ES*은 넷스케이프에서 Javascript의 표준화를 위해 그 표준을 <a href="[https://ko.wikipedia.org/wiki/ECMA%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8](https://ko.wikipedia.org/wiki/ECMA스크립트)">ECMAScript</a> 로 정리 한 것입니다.</p> <p>뒤에 붙는 숫자는 해당 표준의 Edition Number 이고 보통 연단위로 하나씩 나오기 때문에 2020년에는 ES11을 사용 할 수 있습니다.</p> <p>하지만 최신 표준인 ES11 혹은 그 이상을 쓰지 않고 왜 JD나 개발자들 사이에서는 ES6를 이야기하는 것일까?</p> <p>진부한 대답이지만, 이 시기의 표준으로 추가된 문법이나 개념이 많았고, 지금도 매우 중요하기 떄문이다.</p> <p>새로 추가되는 표준에는 과거의 표준에 대한 설명이 있지 않기에 ES6는 그 중요성이 남다르기 때문에 더욱 강조되는 것이다.</p> <p>ES6는 2015년 6월에 업데이트된 ECMAScript의 6th Edition 이다.</p> <h1 id="한눈에-보는-es6-특징">한눈에 보는 ES6 특징</h1> <p>아래 5가지 목록은 ES6에서 새롭게 지원하는 기능들이고, 주요 요소들만 뽑아놓고 정리할 것이다.</p> <p>그리고 그 아래 4가지로 정리한 Bonus feature는 개인적으로 생각하기에 편의성이 향상된 부분들이라 알아두면 좋은 요소들을 정리했다.</p> <ol> <li>Class 지원</li> <li>let &amp; const 문법 추가</li> <li>Arrow Functions</li> <li>Modules</li> <li>Promises</li> </ol> <h3 id="bonus-feature">Bonus feature</h3> <ol> <li>비구조화 할당 지원</li> <li>객체 리터럴</li> <li>Template Literals</li> <li>매개변수 기본값</li> </ol> <p>사실 여기에 소개된 기능들 외에도 Array, String class의 메서드 추가, loop 문법 추가 등이 있지만, 주요 변경사항은 아니기 때문에 (필요할 때마다 검색하면 바로 나오는 정도) 패스하고 작성했다.</p> <p>아래에서는 이런 주요 기능 및 보너스 기능에 대해서 기본적인 내용 위주로만 설명한다.</p> <h1 id="class-전격-지원">Class 전격 지원</h1> <p>사실 이 블로그를 정독 해 보신 분이라면 이전 글에서 이미 <a href="https://samslow.github.io/development/2020/06/09/Javascript_Basic_Prototype-Chaining/">ES6에서 Class문법이 추가 된 이유</a>에 대해서 알 것이다.</p> <p>요약하자면, 다른 언어에서 지원되는 Class에 발 맞추어 다중 패러다임을 지향하는 JS에서도 이를 똑같이 묘사하기 위해 Syntatic Sugar가 첨가된 기존 Prototype으로 지원되던 OOP의 기능을 Class로 래핑한 기능이다.</p> <p>즉 Prototype은 그 개념자체가 배울 것도 많고, 오묘하게 Class와 다르기 때문에 혼란을 줄이기 위해 Class 가 ES6부터 지원되는 것이다.</p> <p>아래 예제에서 ES5의 prototype에서 ES6의 class로의 변화를 볼 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">////////////////////////////////////// ES5</span> <span class="kd">var</span> <span class="nx">Person</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="c1">// Constructor</span> <span class="kd">function</span> <span class="nx">Person</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">_name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// public method</span> <span class="nx">Person</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">sayHi</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hi! </span><span class="dl">"</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">_name</span><span class="p">);</span> <span class="p">};</span> <span class="c1">// return constructor</span> <span class="k">return</span> <span class="nx">Person</span><span class="p">;</span> <span class="p">})();</span> <span class="c1">// 인스턴스 생성</span> <span class="kd">var</span> <span class="nx">me</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="dl">"</span><span class="s2">Lee</span><span class="dl">"</span><span class="p">);</span> <span class="nx">me</span><span class="p">.</span><span class="nx">sayHi</span><span class="p">();</span> <span class="c1">// Hi! Lee.</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">me</span> <span class="k">instanceof</span> <span class="nx">Person</span><span class="p">);</span> <span class="c1">// true</span> <span class="c1">////////////////////////////////////// ES6</span> <span class="kd">class</span> <span class="nx">Person</span> <span class="p">{</span> <span class="c1">// constructor</span> <span class="kd">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">_name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span> <span class="p">}</span> <span class="nx">sayHi</span><span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`Hi! </span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">_name</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">// 인스턴스 생성</span> <span class="kd">const</span> <span class="nx">me</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="dl">"</span><span class="s2">Lee</span><span class="dl">"</span><span class="p">);</span> <span class="nx">me</span><span class="p">.</span><span class="nx">sayHi</span><span class="p">();</span> <span class="c1">// Hi! Lee</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">me</span> <span class="k">instanceof</span> <span class="nx">Person</span><span class="p">);</span> <span class="c1">// true</span> </code></pre></div></div> <p><img src="https://poiemaweb.com/img/prototype-class.png" alt="prototype" /></p> <p>위 코드와 함께 prototype에서 이해하기 쉬운 그림을 보면 ES6 이전의 prototype을 이해하는데에도 도움이 될 것이다.</p> <p><code class="language-plaintext highlighter-rouge">Person</code> 객체가 생성되면 prototype과 생성자 함수가 생기고 이를 기반으로 인스턴스를 <code class="language-plaintext highlighter-rouge">new</code> 예약어로 생성 가능하며 인스턴스는 <code class="language-plaintext highlighter-rouge">__proto__</code> 로 다시 <code class="language-plaintext highlighter-rouge">Person.prototype</code> 을 가르키게 된다.</p> <h1 id="let--const로-엄격해진-js-집안">let &amp; const로 엄격해진 JS 집안</h1> <p>let, const의 존재는 알지만 왜 기존에 잘 쓰던 var가 있는데 굳이 차이가 나는 것 같지도 않은 let const를 쓰는지 모르겠다는 개발자를 최근에 보았다.</p> <p>아직 JS공부를 시작한 지 얼마 안 된 분이었기 때문에 충분히 그럴 수 있다고 생각 했고, 과거에 JS를 잘 몰랐을 때의 나도 그럤을 수 있을거라 생각하고 웃어넘겼지만, 이 글을 봤다면 더이상 이를 묵과해서는 안된다.</p> <p><strong>간단히 말하면 <code class="language-plaintext highlighter-rouge">let &amp; const</code>는 <code class="language-plaintext highlighter-rouge">block scope</code>이고 <code class="language-plaintext highlighter-rouge">var</code>는 <code class="language-plaintext highlighter-rouge">function scope</code>이다.</strong></p> <p>어쨋건 이 셋은 모두 변수를 선언할 때 쓰는 예약어 이다.</p> <ol> <li>var <ul> <li>함수 블럭(중괄호로 감싸진 부분)만을 스코프로 인정하여 전역 변수 남용 이 발생 할 수 있다.</li> <li>변수 중복 선언 허용으로 상수의 개념이 없음</li> <li>변수 선언 이전에 참조 할 수 있다. <code class="language-plaintext highlighter-rouge">undefined</code>로 결과가 나오긴 하지만, 이는 올바른 에러핸들링을 방해한다.</li> </ul> </li> <li>let &amp; const <ul> <li>대부분의 언어에서와 마찬가지로 모든 블록들을 개별적인 스코프로 사용한다.</li> <li>변수 중복 선언 금지</li> <li>let은 값이 변할 수 있는 변수, const는 초기화 후 값이 변하지 않는 변수 <ul> <li>여기서 값이란 주소값으로, const로 선언 했더라도 배열의 경우는 배열 요소가 바뀔 수는 있다.</li> </ul> </li> <li>변수 선언 이전 참조 불가 <ul> <li>호이스팅 동작은 var와 같지만, 선언 → 초기화 → 할당의 기본 주기중 선언과 초기화 사이에 TDZ(Temporal Dead Zone)이 추가되며 선언 사각지대를 형성하기 때문에 선언 이전 참조시 에러를 발생시킴</li> </ul> </li> </ul> </li> </ol> <p>이러한 새로운 변수 예약어의 등장으로 js 는 그 용도에 따라 더 세분화된 변수 사용이 가능해졌고, 의미가 명확해지도록 하여 엄격한 js 문법을 만들어갔다.</p> <p>엄격하다는 것은 그만큼 신뢰성 있는 코드를 만들 수 있다는 의미이기 때문에 긍정적인 변화라고 할 수 있다.</p> <h1 id="함수-리터럴과-화살표-함수">함수 리터럴과 화살표 함수</h1> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// ES6 이전</span> <span class="kd">const</span> <span class="nx">foo</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">);</span> <span class="p">};</span> </code></pre></div></div> <p>ES6에서 새로 생긴 화살표 함수는 리터럴 함수 선언보다 간단히 함수를 표현하고, 그 의미를 명확히 하기 위해서 탄생했다.</p> <ul> <li> <p>화살표 함수는 항상 익명이며, 이를 const &amp; let에 할당해 이름을 갖도록 할 수 있다.</p> </li> <li> <p>화살표 함수에서 this의 의미</p> <ul> <li> <p>일반 함수와 사용성에서 가장 큰 차이임</p> </li> <li> <p>일반 함수는 전역 컨텍스트에서 실행 될 때에 this가 동적으로 정의됨</p> </li> <li> <p>화살표 함수는 언제나 함수가 선언된 상위 스코프에 binding 됨</p> <ul> <li> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">study</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">FeBase</span><span class="dl">"</span><span class="p">,</span> <span class="na">assemble</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span> <span class="c1">// FeBase 출력</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">this는 객체 study인가? </span><span class="dl">"</span><span class="p">,</span> <span class="k">this</span> <span class="o">===</span> <span class="nx">study</span><span class="p">);</span> <span class="c1">// true 출력</span> <span class="kd">function</span> <span class="nx">garthered</span><span class="p">()</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">this.name은?</span><span class="dl">"</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span> <span class="c1">// undefined 출력</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">this는 객체 o? </span><span class="dl">"</span><span class="p">,</span> <span class="k">this</span> <span class="o">===</span> <span class="nx">study</span><span class="p">);</span> <span class="c1">// false 출력</span> <span class="p">}</span> <span class="nx">garthered</span><span class="p">();</span> <span class="c1">// 함수 형태로 호출</span> <span class="p">},</span> <span class="p">};</span> <span class="nx">study</span><span class="p">.</span><span class="nx">assemble</span><span class="p">();</span> <span class="c1">// 메서드의 형태로 호출</span> <span class="kd">const</span> <span class="nx">study2</span> <span class="o">=</span> <span class="nx">study</span><span class="p">.</span><span class="nx">assemble</span><span class="p">;</span> <span class="nx">study2</span><span class="p">();</span> <span class="c1">// 이런 방식으로 호출된다면?</span> </code></pre></div> </div> </li> </ul> </li> </ul> </li> </ul> <h1 id="js-모두모여-modules-모둘쓰">Js 모두모여 Modules 모둘쓰~</h1> <p>모듈이란 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말한다. 모듈은 세부 사항을 캡슐화하고 공개가 필요한 API만을 외부에 노출한다.</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;html&gt;</span> <span class="nt">&lt;body&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"foo.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"bar.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;/body&gt;</span> <span class="nt">&lt;/html&gt;</span> </code></pre></div></div> <p>하지만 JS는 브라우저에서 동작을 서포트하기 위해 만들어진 한계로 인해 그동안은 전역 스코프에서 사용 되어 왔다.(주로 windows 객체)</p> <p>이런 문제점을 ES6 를 지원하는 Client Browser에서 모듈 기능을 지원하게 되었다.</p> <ul> <li> <p>파일 단위의 모듈 스코프를 지원한다.</p> </li> <li> <p>모듈에서 선언된 변수는 모두 내부에서만 사용 가능하다.</p> <ul> <li> <p>변수에 <code class="language-plaintext highlighter-rouge">export</code> 예약어를 선언하면 외부에서도 사용 할 수 있게 된다.</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">export</code>된 변수나 클래스를 다른 모듈에서 <code class="language-plaintext highlighter-rouge">import</code> 예약어로 사용 할 수 있다.</p> </li> <li> <p>이때 비구조화 할당으로 가져오는게 불편하다면, <code class="language-plaintext highlighter-rouge">export</code> 뒤에 <code class="language-plaintext highlighter-rouge">default</code> 를 붙이면 된다.</p> <ul> <li> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// lib.js</span> <span class="k">export</span> <span class="k">default</span> <span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">x</span> <span class="o">*</span> <span class="nx">x</span><span class="p">;</span> </code></pre></div> </div> </li> <li> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// app.js</span> <span class="k">import</span> <span class="nx">myModule</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./lib.js</span><span class="dl">"</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">myModule</span><span class="p">(</span><span class="mi">10</span><span class="p">));</span> <span class="c1">// 100</span> </code></pre></div> </div> </li> </ul> </li> </ul> </li> </ul> <h1 id="약속-해줘-es6-promise">약속 해줘~ ES6 Promise</h1> <p>순차적이지 않은 비동기 함수의 실행 순서를 제어할 수 있도록 지원 해 주는 기능이다.</p> <p>Promise에 대해서는 <a href="https://samslow.github.io/development/2020/06/13/Javascript_Basic_Asyncronous/">이전 글</a> 에서 한번 다룬 내용이고, 여전히 중요한 내용이다.</p> <p>이번에는 장점들 위주로 다시한번 리마인드 해 본다.</p> <ul> <li>장점 <ul> <li>callback hell에서 벗어 날 수 있음 <ul> <li>.then()의 return은 다음 .then()의 매개변수로 사용되므로 가독성 👆</li> </ul> </li> <li>.then() 을 이용하여 가독성 좋은 연속적인 비동기 코드를 작성 가능</li> <li>Promise.all()을 통해 병렬 비동기 코드 작성 가능 <ul> <li>매개변수로 던져진 함수들이 모두 완료되면 return</li> </ul> </li> <li>callback 함수를 제 때 실행하지 못하거나 변수 전달 실패 없음</li> </ul> </li> <li>단점</li> <li>ES5 이전에서는 polyfill을 통해 로드해야 함</li> </ul> <h1 id="bonus">Bonus</h1> <ol> <li> <p>비구조화 할당 지원</p> <p>비구조화 할당(구조분해 할당)을 처음 들었을때는 한글이나 영어나 무슨 말인지 알 수 없었지만, 사용 방법을 보면 바로 이해 할 수 있다.</p> <p>즉, 구조가 없는 객체를 알맞게 처리 하도록 돕는 기능이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">person</span> <span class="o">=</span> <span class="p">{</span> <span class="na">head</span><span class="p">:</span> <span class="dl">"</span><span class="s2">top</span><span class="dl">"</span><span class="p">,</span> <span class="na">body</span><span class="p">:</span> <span class="dl">"</span><span class="s2">middle</span><span class="dl">"</span><span class="p">,</span> <span class="na">foot</span><span class="p">:</span> <span class="dl">"</span><span class="s2">bottom</span><span class="dl">"</span><span class="p">,</span> <span class="p">};</span> <span class="kd">const</span> <span class="p">{</span> <span class="nx">head</span><span class="p">,</span> <span class="nx">body</span><span class="p">,</span> <span class="nx">foot</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">person</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">head</span><span class="p">,</span> <span class="nx">body</span><span class="p">,</span> <span class="nx">foot</span><span class="p">);</span> <span class="c1">// top middle bottom</span> </code></pre></div> </div> </li> <li> <p>객체 리터럴</p> <p>객체를 생성 할 때 더욱 동적이고 간결하게 표현 할 수 있도록 해주고, 중복을 효과적으로 줄일 수 있는 문법이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ES6</span> <span class="kd">let</span> <span class="nx">x</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="p">};</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// { x: 1, y: 2 }</span> </code></pre></div> </div> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ES6</span> <span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Lee</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// 메소드 축약 표현</span> <span class="nx">sayHi</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// sayHi: function() { 과 같은 표현</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hi! </span><span class="dl">"</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span> <span class="p">},</span> <span class="p">};</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">sayHi</span><span class="p">();</span> <span class="c1">// Hi! Lee</span> </code></pre></div> </div> <p>이외에도 ES6에서는 객체 리터럴 내부에서 <code class="language-plaintext highlighter-rouge">__proto__</code> 프로퍼티를 직접 설정할 수 있다. 이것은 객체 리터럴에 의해 생성된 객체의 <strong>proto</strong> 프로퍼티에 다른 객체를 직접 바인딩하여 상속을 표현할 수 있음을 의미한다.</p> <p>뭐 사실 class도 같이지원 해 주기 떄문에 이는 보조적인 지원에 그친다고 보면된다.</p> </li> <li> <p>Template Literals</p> <p>이 문법 역시 이미 다른 언어에서는 널리 지원되던 문법으로 String 안에서 변수를 더 단순하게 사용 할 수 있도록 하는 문법이다. 또 개행시 <code class="language-plaintext highlighter-rouge">\n</code> 를 생략 할 수 있는 장점도 제공한다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">first</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Samuel</span><span class="dl">"</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">last</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">slow</span><span class="dl">"</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">`제 이름은 </span><span class="p">${</span><span class="nx">first</span><span class="p">}</span><span class="s2"> </span><span class="p">${</span><span class="nx">last</span><span class="p">}</span><span class="s2"> 입니다.`</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">` 긴글을 아무리 써도 단지 개행을 자연스럽게 해주면? // 원래는 + "\n" + 를 해주지만 따로 \n을 해주지 않더라도 출력시 개행이 되지! `</span><span class="p">);</span> </code></pre></div> </div> </li> <li> <p>매개변수 기본값</p> <p>사실 이런것 까지 해야하나 싶지만, 이 내용도 일단 ES6에서 시작된 것이므로 함께 기재한다.</p> <p>필자같은 경우 이미 이것을 사용하고 있었는데 ES6 에서 새로 생긴것을 뒤늦게 알게 되었다.</p> <p>즉 함수의 매개변수에 기본값을 할당하여, 해당 자리에 적절한 값이 없을 때 기본으로 들어가게하는 값이다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">multiply</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">a</span> <span class="o">*</span> <span class="nx">b</span><span class="p">;</span> <span class="p">}</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">multiply</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span> <span class="c1">// 10</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">multiply</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span> <span class="c1">// 5</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">multiply</span><span class="p">(</span><span class="mi">5</span><span class="p">));</span> <span class="c1">// 5</span> </code></pre></div> </div> </li> </ol> <h1 id="self-check">Self Check</h1> <ol> <li>최신 ECMAScript의 Edition Number는 ?</li> <li>ES6의 주요 특징 중 최소 3가지를 말하고 설명 하시오</li> <li>화살표 함수의 this scope는 (동적/정적) scope로 주로 <code class="language-plaintext highlighter-rouge">____</code> 의 scope를 따른다.</li> </ol> <h1 id="closing">Closing</h1> <p>ES6의 특징은 줄곧 알고 있던 것들인데, 예제와 함께 이해하고 설명한다고 생각하니 잘 이해되지 않는 부분들 떄문에 글을 쓰는데 시간이 좀 걸렸다. 역시 설명 할 때는 이해했다고 생각한 것과 다른 것을 알 수 있는 것 같다. 특히 arrow function의 this의 의미는 제대로 알고 넘어갔으면 좋겠다.</p> <p>let &amp; const 를 설명하면서 Hoisting을 설명하고 싶었는데, Hoisting 자체는 var에도 있던 내용이라, 이 게시글에서 다루지 않고, 다음 포스트에서 단독 포스트로 다루려고한다.</p> <p>빠잉</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://woowabros.github.io/experience/2017/12/01/es6-experience.html">우아한 형제들 기술 블로그</a></li> <li><a href="https://poiemaweb.com/es6-class">poiemaweb</a></li> </ul> Wed, 24 Jun 2020 00:00:00 +0000 https://samslow.github.io/development/2020/06/24/ES6-spec/ https://samslow.github.io/development/2020/06/24/ES6-spec/ web development 디자인 패턴 - 당신은 MVC를 안다고 할 수 있는가 <ul> <li>MVC를 알고 사용할 순 있지만, 그것에 대해 설명하긴 어려울 때 도움이 되는 글</li> </ul> <h1 id="디자인-패턴의-개념과-필요한-이유">디자인 패턴의 개념과 필요한 이유</h1> <p>SW 디자인 패턴은 <strong>공통으로 발생하는 문제에 대한 재사용 가능한 해결책</strong>이다.(<a href="[https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4](https://ko.wikipedia.org/wiki/소프트웨어_디자인_패턴)">위키백과</a>)</p> <p>즉 이런 방식으로 SW를 개발하면, 일반적인 문제에 대한 해결책이 패턴화 되어있기 때문에 개발 주기가 빨라 질 수 있다.</p> <p>우리가 알고있는 프레임워크들과 라이브러리도 하나의 디자인 패턴으로 볼 수 있다.</p> <p>이러한 디자인 패턴은 권장 사항이지 필수 사항은 아닌데 왜 다들 지키려 할까?</p> <p>그 이유는 품질 좋은 클린 코드를 빠르고 높은 생산성으로 만들 수 있기 때문 일 것이다. <del>( 이게 실존하는지는 모르겠지만 )</del></p> <p>아래의 <code class="language-plaintext highlighter-rouge">SW 개발 3대 원칙</code> 이론에서 그 근거를 아래와 같이 찾아 볼 수 있다.</p> <ul> <li>KISS - Keep It Simple Stupid <ul> <li>코드를 되도록이면 간단하고 단순하게 짜고, 불필요하게 장황해 지는 것을 경계하라는 원칙</li> <li>말은 쉽지만 실제로 깔끔하게 짜는것은 마냥 쉽지는 않다.</li> </ul> </li> <li>YAGNI - You Ain’t Gonna Need It <ul> <li>지나치게 먼 미래를 생각해 미리 작업하지 말고 현재에 집중하라는 원칙</li> <li>어차피 미래가 되면 그 코드를 다시 처음부터 이해해야 한다.</li> </ul> </li> <li>DRY - Do not Repeat Yourself <ul> <li>동일 코드를 반복하지 말라는 원칙</li> <li>같은 기능을 하는 코드가 여러 군데에 산재 해 있으면 모든 코드를 수정 해야하고 이 과정에서 실수가 있을 경우 버그로 쉽게 이어진다.</li> <li>개인적으로 가장 좋아하는 원칙이자 Ruby On Rails 의 철학 중 하나</li> </ul> </li> </ul> <p>결국 모두 개발 습관이고, 몇가지는 알게 모르게 지켜오는 것도 있고, 혹자는 이런 것을 굳이 정해놓고 하느냐고 할 수도 있다.</p> <p>틀린 말도 아니고, 결국 원칙은 원칙일 뿐 본인에게 맞는 스타일로 현재에 집중해서 개발하면 된다.</p> <h1 id="디자인-패턴의-종류">디자인 패턴의 종류</h1> <p>디자인 패턴은 오래전부터 연구되었던 소프트웨어 공학 분야이기 때문에 종류도 그만큼 많다.</p> <p>아마 당신이 개발을 어느정도 해 봤다면 여기에 있는 패턴을 모두 알지는 못하더라도 최소한 2~3개는 들어 본 적이 있을 것이다.</p> <p><img src="https://gmlwjd9405.github.io/images/design-pattern/types-of-designpattern.png" alt="types-of-designpattern" /></p> <p><a href="https://gmlwjd9405.github.io/2018/07/06/design-pattern.html">by @gmlwjd9405</a></p> <p>이러한 디자인 패턴을 모두 알면 좋겠지만, 그렇지 않다면 싱글턴, 커맨드, 팩토리, 빌더, 옵저버 패턴 정도 알면 도움이 될 것이다.</p> <p>그리고, 오늘은 이러한 일반적인 GoF Design Pattern에서 굳이 분류하자면 <strong>옵저버 패턴과 미디에이터 패턴</strong>이 적절히 섞인듯한</p> <p>MVC 패턴에 대해 알아보고 이 다음 포스팅으로 MVVM에 대해 알아볼 것이다.</p> <h1 id="mvc-pattern">MVC Pattern</h1> <p>위에 설명 한 것 처럼 디자인 패턴이란 권장사항이지 에러를 일으키거나 고정된 사용방법이 명확한 것은 아니기 때문에 다양한 사용 방법이 존재한다.</p> <p>MVC는 Model-View-Controller의 약자로 아래 두 패턴을 적절히 섞어놓은 패턴으로 알려져있다.</p> <ul> <li>Observer Pattern: 하나의 Model에 다양한 View를 두어 상태가 변할 때 그 모델을 구독하는 객체들이 자동으로 갱신 됨</li> <li>Mediator Pattern: Observer Pattern에서 M:N 관계가 많이 생성되면 전체가 복잡해지기 때문에, 중간에 중재자(Mediator)를 두어 이벤트가 발생하고 전달하는 것을 단순화 함</li> </ul> <p>실제 사용에 성공하게 되면, UI로부터 Logic가 분리되어 App의 다양한 부분들을 더 간결하게 볼 수 있어 버그픽스에 유리하다.</p> <p>아래 그림은 클라이언트와 DB를 고려한 일반적인 MVC 패턴의 대표적인 예시이다.</p> <p><img src="https://upload.wikimedia.org/wikipedia/commons/5/53/Router-MVC-DB.svg" alt="img" /></p> <p><a href="[https://ko.wikipedia.org/wiki/%EB%AA%A8%EB%8D%B8-%EB%B7%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC](https://ko.wikipedia.org/wiki/모델-뷰-컨트롤러)">by MDN</a></p> <ul> <li>Controller: Model이나 View의 요소들을 유저의 요청에 맞게 “어떻게” 처리 할 것인지 정의</li> <li>Model: 데이터, 알고리즘, DB 등을 Model이라고 하는데, App이 “무엇”을 처리할 것인지 정의</li> <li>View: 사용자에게 보여 줄 정보들을 Controller나 Model에서 가져와 보여주고 입력을 받을 수 있도록 함</li> </ul> <p>잘 이해가 가지 않는가 ? 위의 3가지를 다시 바꿔 말해보자</p> <ul> <li>Controller는 무엇을 처리할지나 보여줌에 있어서 신경쓰지 않는다.</li> <li>Model은 어떻게 보일지(View, Controller)에 대해 신경쓰지 않아도 된다.</li> <li>View는 데이터 저장이나 로직을 신경쓰지 않고 변경 처리 이벤트에만 관심있다.</li> </ul> <p>결국은 이런 세가지 역할이 분업이 잘 될수록, 서로 역할이 완전히 나눠져 있을수록 디자인 패턴이 잘 적용되었다고 볼 수 있다.</p> <p>예를들어 계산기를 만든다고 했을 때는 아래와 같이 구성 할 수 있다.</p> <ul> <li>Model <ul> <li>데이터 베이스 및 데이터 표현</li> <li>유저가 입력하는 숫자를 저장, 이전 계산 기록을 저장</li> </ul> </li> <li>View <ul> <li>계산 결과를 보여줌</li> <li>현재 입력중인 숫자나 사칙연산이 무엇인지 보여줌</li> </ul> </li> <li>Controller <ul> <li>입력값이 초과하지 않았는지</li> <li>사용자가 View에서 입력한 숫자를 Model로 변경 해 줌</li> <li>사칙연산 계산</li> </ul> </li> </ul> <h1 id="그럼-왜-mvvm이-나왔을까">그럼 왜 MVVM이 나왔을까?</h1> <p>세상에 프로그래밍 언어가 C언어만 있는것은 아닌 것 처럼, MVC도 이론상은 그럴 듯 하지만, 시스템의 규모가 커질 수록 그 한계가 명확해져갔다.</p> <ul> <li>Model과 View가 M:N 관계로 매우 복잡 해 진다. 즉 이런 의존성이 문제를 일으킬 확률이 올라간다.</li> <li>만약 MVC를 얕게 아는 상태에서 MVC를 적용하려 했다면, 높은 확률로 Controller에 모든 Logic이 들어갈 가능성이 크다. 하지만, 그럼 Controller의 할일이 불필요하게 많아지는 경우가 있어 필요에 따라선 Model에 로직과 메소드가 설정해 이를 분산 시켜야 한다.(getter, setter, ··· )</li> </ul> <p>이런 문제는 MVVM에서는 어떻게 개선시킬 수 있었을까?</p> <p>다음 포스트에서는 MVC의 구성을 알아본 것 처럼 MVVM도 그 유래와 뜻을 알아보도록 한다.</p> <h1 id="작성-후기">작성 후기</h1> <p>처음에는 MVC를 써보기만 했지 개념적으로 접하기 쉽지 않았는데, 제대로 각을 잡고 보니 잘못 사용하고 있던 부분도 있었고</p> <p>과도하게 컨트롤러에 비중이 크거나 유지보수를 고려하지않은 Massive한 코드도 있었다.</p> <p>이제 누군가 MVC에 대해서 아는대로 설명하라고 한다면, 위 글을 기반으로</p> <p>M,V,C에 대해 설명하고 Client입장에서 이런 흐름이 어떻게 동작하는 지 설명 하면 되겠다.</p> <p>마지막으로 당부하고 싶은 말은, 디자인 패턴이란 것이 정답이 없기 때문에 각자 큰 틀안에서 각자 조직에 맞도록 알맞게 변형하면 된다고 생각한다.</p> <p>정답은 없기 때문에 이것을 정석으로 보기 보다는 이제까지 본인의 코드를 되돌아보면서 “우린 이렇게 썼었네”, “이게 MVC로 구성된것이었구나” 라고 느끼고 더 잘 할 수 있는 방법에 대해서 고민하면 그게 진짜 정답이지 않을까</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://blog.naver.com/PostView.nhn?blogId=complusblog&amp;logNo=221163007357&amp;redirect=Dlog&amp;widgetTypeCall=true&amp;directAccess=false">테크 스케치-SW 개발 3대 원칙</a></li> <li><a href="https://stackoverflow.com/questions/9119657/how-do-gang-of-four-design-patterns-fit-into-the-mvc-paradigm">MVC Orientation</a></li> </ul> Tue, 16 Jun 2020 00:00:00 +0000 https://samslow.github.io/development/2020/06/16/Design_pattern-MVC/ https://samslow.github.io/development/2020/06/16/Design_pattern-MVC/ web development Javascript 기초 - 비동기 통신 <ul> <li>Javascript에서 비동기 통신하는 방법의 초기 부터 어떤 식으로 발전했는지 살펴봅니다. <ul> <li>XMLHttpRequest(XHR), Ajax, Promise, Async-await</li> </ul> </li> </ul> <h1 id="비동기-통신이-필요한-이유">비동기 통신이 필요한 이유</h1> <p>Synchronous(동기식)에서 <code class="language-plaintext highlighter-rouge">반대의-</code> 라는 의미를 가진 A- 를 붙임으로써 Asynchronous(비동기식)이 나오게 된다.</p> <p>초기의 HTML은 Static한 성질을 가지고 있었고, 각 페이지의 이동은 Anchor tag의 href 속성을 통해 서로 다른 화면으로 이동이 가능했다.</p> <p>하지만, 이러한 방식대로라면 모든 페이지를 이동할때 화면의 깜빡임이 발생하게 되고 이는 좋지 않은 유저경험까지 이어지게 될 뿐더러 하나의 Static한 html 파일에 용량이 큰 Assets나 Library를 사용하게되면 화면이 완전히 뜰때까지의 시간이 길어지게 된다. 또한 전체 페이지가 리로드되어 중복되는 시멘틱 태그도 다시 보여주기 때문에 비효율적이다.</p> <p>싱글스레드로 동작하는 Javascript Engine과 위의 문제점을 해결하기 위해 비동기 통신이 등장했다.</p> <p>비동기 통신은 처음 화면에 꼭 보여주지 않아야 할 것들은 순차적으로 로딩하면서 첫 화면이 빠르게 보여지게 할 수 있고, 다른 화면으로의 이동을 자연스럽게 한다.</p> <p>예를들어 Lazy Loading이나 Infinite Scroll이 비동기 통신을 활용한 예시이다.</p> <p>Javascript의 내장 객체에서 지원 해 주는 <code class="language-plaintext highlighter-rouge">XMLHttpRequest</code> 객체가 있고, 이를 쉽게 사용 할 수 있도록 해주는 라이브러리가 <code class="language-plaintext highlighter-rouge">Jquery</code>의 <code class="language-plaintext highlighter-rouge">Ajax</code> 이고 이외에도 axios나 Promise 등이 있다.</p> <p>아래에서는 이러한 여러 개념들을 구현 방식으로 분류해서 살펴보겠다.</p> <h1 id="javascript-engine의-settimeout">Javascript Engine의 setTimeout()</h1> <p>setTimeout()은 정확히는 JS Engine의 API가 아니라 브라우저에서 지원하는 Web API의 일종이다.</p> <p>그리고 이것을 이해하기 위해선 JS의 싱글 스레드 동작 방식과 이벤트 루프에 대해서 이해하고 넘어가야 한다.</p> <p>후술할 Promise 패턴이나 Async-Await를 제대로 이해하기 위해서는 이같은 개념이 필요하다.</p> <p>JS 이벤트 루프에 대해서는 별도의 글을 작성하겠지만, 간단히 정리하자면 아래와 같다.</p> <p><img src="https://www.dropbox.com/s/fq1rxbl9xvorcep/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-13%2012.43.59.png?dl=1" alt="" /></p> <ul> <li>JS Engine에는 프로세스가 실행될 Memory Heap과 그 순서를 담은 Call Stack이 있다.</li> <li>Web API는 Browser에서 DOM을 조작하기 쉽도록 하는 여러 메소드를 제공한다.</li> <li>Event <strong>Loop(중요!)는</strong> TaskQueue(CallbackQueue)와 Call Stack을 지속적으로 관찰하며 CallStack이 비었을 때 Task Queue를 비워간다.</li> </ul> <p>이러한 흐름을 JS EventLoop라고 한다.</p> <p>setTimeout()을 적절히 활용하면 아래와 같이 비동기 통신을 구현 할 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">addTen</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">x</span> <span class="o">+</span> <span class="mi">10</span><span class="p">;</span> <span class="p">}</span> <span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">num</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">addTen</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">);</span> <span class="c1">// 15</span> <span class="p">},</span> <span class="mi">1000</span><span class="p">);</span> </code></pre></div></div> <p>1초 후에 callback함수를 통해 addTen()이라는 함수를 실행하는 예제이다.</p> <p>위 예시에서는 add()라는 간단한 함수를 사용했지만 이를 HttpRequest로 생각 할 수도 있다.</p> <p>단, 여기서 주의하여야 할 점은 TaskQueue 에 있는 callback들은 실행 될 때의 클로저로 변수를 파악하기 때문에 CallStack이 비워지고 나서 정해진 변수들을 사용하므로 이를 고려해 별도의 callback함수에 변수를 지정하는 방법을 사용할 수 있다.</p> <h1 id="xmlhttprequest">XMLHttpRequest</h1> <p>간단하게 XMLHttpRequest는 아래와 같은 구조로 동작한다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">XMLHttp</span><span class="p">()</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">req</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="c1">// XMLHttpRequest 객체</span> <span class="nx">req</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="c1">// 서버가 데이터를 반환했을때 발생하는 이벤트 핸들러</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 요청한 데이터를 문자열로 반환</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">text</span><span class="dl">"</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">responseText</span><span class="p">;</span> <span class="p">}</span> <span class="p">};</span> <span class="c1">// GET 요청 작성</span> <span class="nx">req</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">"</span><span class="s2">GET</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">/examples/media/xml_httpxmlrequest_data.txt</span><span class="dl">"</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="c1">// 요청 전송</span> <span class="nx">req</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span> <span class="p">}</span> </code></pre></div></div> <p>여기서 중요한 것은 <code class="language-plaintext highlighter-rouge">req.open()</code>의 3번째 인자로 <code class="language-plaintext highlighter-rouge">true</code> 를 전달 해 주었다는 점이다.</p> <p>2번째 자리까지만 쓰면( default는 <code class="language-plaintext highlighter-rouge">false</code>), 해당 코드는 더이상 아래로 진행되지 않고 동기식으로 처리되어 요청이 완료될때까지 기다리게된다.</p> <p>만약 여기서 <code class="language-plaintext highlighter-rouge">false</code> 옵션을 사용하게 되면 코드 수준의 동기가 되기 때문에 브라우저가 멈추므로 유의하여 사용하여야 한다.</p> <p>예를들어, 무언가를 로딩하게 되면 브라우저의 모든 이벤트나 버튼들이 먹통이 되는 것이다.</p> <p>이 방법은 EventListener와 http 요청을 위한 작업등을 하나씩 사용하고 실제 요청을 send()로 보내는 것등을 직접 해 주어야 한다는 것인데, 직관적이라는 장점이 있지만 코드의 Line 수가 늘어나 가독성을 해치게 된다.</p> <p>위 객체를 이용하면 위에 사용한 JS setTimeout() 보다는 더 유려하게 코드를 작성 할 수 있다.</p> <p>요새는 개선된 방법이 더 많기 때문에 상대적으로 Low Level인 위 방법은 잘 사용되지 않고 Library를 활용한 Request가 선호된다.</p> <p>계속해서 아래로 갈수록 더욱 개선된 방법이 소개 된다.</p> <h1 id="jquery">JQuery</h1> <p>Vanila JS의 DOM 조작이 가독성이나 코드 조작 면에서 불편하여 나오게 된 것이 JQuery 이다.</p> <p>물론 JQuery가 사용되는 원리를 이해하지 못하고 짠 코드라면 getElementById같은 WebAPI도 10배 이상 느려지는 신세계를 경험 할 수 있고, 사용 자체가 쉽다보니 코드에 사족이 많아지는 단점이 있으므로 주의해야한다.</p> <p>JQuery의 ajax를 사용하여 비동기 통신을 하는 기본적인 방법은 아래와 같다</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({</span> <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://samslow.github.io/example.txt</span><span class="dl">"</span><span class="p">,</span> <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">GET</span><span class="dl">"</span><span class="p">,</span> <span class="na">data</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// 보낼 데이터</span> <span class="p">},</span> <span class="na">done</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 성공 시 동작</span> <span class="p">},</span> <span class="na">fail</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 실패 시 동작</span> <span class="p">},</span> <span class="p">});</span> </code></pre></div></div> <p>위에 소개된 것과 상반되게 정말 쉽게 사용 할 수 있기 때문에 더 주의해야 하는 코드이다.</p> <p>비슷하게 Axios나 request.js같은 라이브러리를 사용하는 방식은 사실 위의 코드와 많이 비슷하다.</p> <h1 id="promise">Promise</h1> <p>Javascript ECMA2015부터 소개된 Promise 객체는 MDN에서 아래와 같이 설명한다.</p> <blockquote> <p><strong><code class="language-plaintext highlighter-rouge">Promise</code></strong> 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.</p> </blockquote> <p>즉, 최종 결과를 동기식으로 멈추면서 제공하지 않고 Promise라는 객체를 반환하여 callback을 받을 준비만 하는 것을 의미한다.</p> <p><a href="https://samslow.github.io/development/2020/06/09/Javascript_Basic_Prototype-Chaining/">이전 포스트</a>에서 Prototype을 배웠으니 Promise의 prototype을 살펴보면 아래와 같다.</p> <p><img src="https://www.dropbox.com/s/d9ee6cz03yayvbk/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-13%2013.31.26.png?dl=1" alt="" /></p> <p>callback을 준비하는 객체라고 한 것 처럼 then(), catch(), finally() 등의 메소드로 비교적 간단하게 구성되어있다.</p> <p>이것을 조금 더 쉽게 이행하면 3가지 상태로 분류 할 수 있다.</p> <ul> <li>대기(<em>pending)</em>: 이행하거나 거부되지 않은 초기 상태.</li> <li>이행(<em>fulfilled)</em>: 연산이 성공적으로 완료됨.</li> <li>거부(<em>rejected)</em>: 연산이 실패함.</li> </ul> <p>pending은 resolve되거나 reject 되는것을 기다리는 상태이고, fulfiled는 이것이 결정 된 상태, 거부는 catch()로 잡히는 에러나 reject을 처리한다.</p> <p>실제 동작하는 코드는 아래와 같다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">$</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">https://samslow.github.io/logo.png</span><span class="dl">"</span><span class="p">)</span> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">Avatar</span><span class="dl">"</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span> <span class="p">})</span> <span class="p">.</span><span class="k">catch</span><span class="p">((</span><span class="nx">result</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">Avatar load Fail</span><span class="dl">"</span><span class="p">);</span> <span class="p">});</span> </code></pre></div></div> <p>어떤가. 위에 소개된 Ajax의 장점은 취하고 단점이었던 코드의 성능은 덤으로 향상 될 것이다.</p> <p>하지만, Promise가 단순할때는 문제가 되지 않지만, 3~4중이상으로 callback function이 실행된다면 가독성이 심히 나빠진다.</p> <p>이것을 <code class="language-plaintext highlighter-rouge">Callback Hell</code>이라고도 부르는데 이를 개선하기 위해 ECMA2017(ES8)에서 소개된 Async-Await를 사용 할 수 있다.</p> <h1 id="async-await">Async-Await</h1> <p>Promise 패턴의 단점은 아직도 나아지지 않은 callback Hell과 이해해야 할 복잡한 개념이 많다는 것이다.</p> <p>필자가 처음에 Promise를 접했을 떄도 이를 이해하기까지 여러 시행착오를 겪어야 했다.</p> <p>Async-Await는 이런 문제점들을 잡아주고, 형재는 비동기 통신의 비교적 완성형으로 널리 사용되고있다.</p> <p>하지만, 역시 Promise 객체를 기반으로 동작하기 때문에 이를 이해하는것은 조금 미루더라도, 그 개념은 꼭 짚고 넘어가야한다.</p> <p>이 둘이 세트인 이유는 Async로 선언되어야만 Await로 동기와 비동기를 구분지을 수 있게 되기 때문에 바늘가는데 실가듯 항상 같이 따라오는 것이다.</p> <p>위의 소개된 것들과 같이 역시나 이것도 단점은 존재하는데, JQuery처럼 잘못 사용하면 병목현상을 유발 할 수 있다는 점이다.</p> <p>이것에 관해선 <a href="[https://medium.com/@sijk0331/javascript%EC%97%90%EC%84%9C-async-await%EC%9D%98-%EB%B3%91%EB%AA%A9-%EB%AC%B8%EC%A0%9C-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0-60d54795c4fa](https://medium.com/@sijk0331/javascript에서-async-await의-병목-문제-개선하기-60d54795c4fa)">더 잘 소개된 포스트</a>가 있으니 이것을 읽고 오길 바란다. 잘만 사용하면 획기적으로 비동기 통신 성능을 개선 할 수 있으니 가볍게 읽고 오면 좋다.</p> <p><img src="https://ww.namu.la/s/622f344027a76589bb7f5e7458392cb354f3db63738a7d2b060d04adf2162c306b971c84ffb061e281f0fc35196e95b112043578e5cf56e263e8a655a1ad3a1fb998a10470c9d821775f470e695d05dc6ba1696f20748cbeb24f8b414a0fb934" alt="" /></p> <p>제일 위에 소개된 XMLHttpRequest가 디지몬중에 아구몬이었다면 궁극체인 워그레이몬은 Async-Await는 아래와 같이 사용 할 수 있다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// inner async function</span> <span class="c1">// Load Image from server</span> <span class="kd">const</span> <span class="nx">url</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">https://samslow.github.io/logo.png</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">$</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">url</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">applyLogo</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">Avatar</span><span class="dl">'</span><span class="p">).</span><span class="nx">src</span> <span class="o">=</span> <span class="nx">response</span><span class="p">;</span> <span class="c1">// Upload image</span> <span class="kd">const</span> <span class="nx">uploadNewLogo</span> <span class="o">=</span> <span class="k">await</span> <span class="kd">function</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">logoImgPath</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">uploaded</span><span class="dl">'</span><span class="p">).</span><span class="nx">path</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">logoImg</span> <span class="o">=</span> <span class="nx">base64</span><span class="p">(</span><span class="nx">require</span><span class="p">(</span><span class="nx">logoImgPath</span><span class="p">));</span> <span class="kd">const</span> <span class="nx">req</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="p">{</span><span class="na">encoded</span><span class="p">:</span> <span class="nx">logoImg</span><span class="p">})</span> <span class="k">return</span> <span class="nx">req</span><span class="p">;</span> <span class="p">}</span> <span class="k">if</span><span class="p">(</span><span class="nx">uploadNewLogo</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Good response</span><span class="dl">"</span><span class="p">)</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Something wrong</span><span class="dl">"</span><span class="p">)</span> <span class="p">}</span> </code></pre></div></div> <p>여기서는 await를 사용함으로써 코드를 callbackHell로 빠지지 않게 한다는것만 알고 넘어가도 된다.</p> <h1 id="summary">Summary</h1> <ul> <li>비동기 통신은 JS EventLoop에 기초를 두고 JQuery, Promise 패턴이 동작한다.</li> <li>비동기 통신의 궁극체인 Async-Await 를 잘 사용하면 좋지만, 상황에 맞게 Promise 패턴을 적절히 사용하면 유용 하다.</li> <li>점점 비동기 통신은 개선되지만 그 개념은 변하지 않는다.</li> </ul> <p>이상으로 Javascript 기초 - 비동기 통신 글을 마칩니다.</p> <p>전체적인 코드는 완벽하지 않을 수 있으니 참고용으로만 봐주시고, 만약 부족하거나 잘못된 개념이 있다면 댓글로 알려주시면 수정 하겠습니다. 비난대신 비판으로 함께 해 주세요.</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://m.blog.naver.com/dndlab/221783285664">D7D Lab</a></li> <li><a href="https://hudi.kr/%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-javascript-%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B8%B0%EB%B0%98-js%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95/](https://hudi.kr/비동기적-javascript-싱글스레드-기반-js의-비동기-처리-방법/)">hudi.kr</a></li> </ul> Sat, 13 Jun 2020 00:00:00 +0000 https://samslow.github.io/development/2020/06/13/Javascript_Basic_Asyncronous/ https://samslow.github.io/development/2020/06/13/Javascript_Basic_Asyncronous/ web development Javascript 기초 - Prototype Chaining이 뭔가요 <ul> <li>이 글은 JS Prototype Chaining을 설명하기위해 Prototype Object, Prototype Link을 설명합니다.</li> </ul> <h1 id="자바스크립트의-태생">자바스크립트의 태생</h1> <p>JS는 class 기반의 객체지향 언어로 대부분 알고있다. 사용하기에 따라서 React, Vue 같은 곳에서는 함수형 프로그래밍을 흉내 낼 수도 있다. 하지만, JS는 ES6에서 생긴 개념인 class가 직접 존재하는 게 아니다. Prototype을 통해 생성되고 여기서 생성 자체는 또 함수를 사용(?)하기 때문에 <code class="language-plaintext highlighter-rouge">객체 → 프로토타입 → 함수 → 객체</code>로 이상한 Cycle이 형성되어버린다고 생각이 들 수도 있다. 그래서 JS를 객체지향 언어, 함수형 언어, 프로토 타입 언어 등의 다양한 이름으로 불리는데, 결국 MDN에서는 JS를 <code class="language-plaintext highlighter-rouge">세계에서 가장 오해받고있는 프로그래밍 언어</code>로 정의하기도 했다.</p> <p>JS 자체가 처음 출시된 날짜는 1995년 12월 4일이니 그래도 언어들 사이에선 셋째 형 정도의 레벨을 갖고 있는데, 그러다보니 넷스케이프에서 처음 출시될 때는 둘째 형인 Java처럼 되고싶다며 구문이 첫째형인 C언어와 같다는 것만 빼고 완전 다른 셋째 JavaScript가 나오게 되었다.</p> <p>이런 이유들로 JS를 정의하자면 <code class="language-plaintext highlighter-rouge">다중 패러다임, 동적 언어</code> 이라고 할 수 있다. (너무 뭉뚱그린다고 할 수 있지만 보는 사람마다 시야가 다른것을 ㅜㅜ)</p> <h1 id="따라쟁이-셋째-js가-지키고-싶었던-것---prototype">따라쟁이 셋째 JS가 지키고 싶었던 것 - Prototype</h1> <p>객체 지향 언어로 명시된 Java, Python, Ruby 에서 Class가 빠질 수 없는 개념 인 것처럼 JS에서도 Prototype을 알면 JS의 그 자체를 이해하는 것이라고 봐도 무방하다.</p> <p>Java와 다르다는 것은 명백하지만 비슷한 부분은 있고, 그것이 Java의 class가 아니라는것을 구분하기 위해 Prototype이라는 개념이 나온 것이다.</p> <p>ES6에서 class의 개념이 나온 것은 맞지만 오해하지 말야아 할 것은, prototype이 class로 변화된 것은 아니고, 원래도 js는 class의 기능을 묘사하기 위해 prototype로 상속같은 것을 구현 할 수 있었고 이를 문법적으로 쉽게 만들어 사용 할 수 있게 한 것이 ES6의 class이다.</p> <p>따라서 class가 생겼다고 prototype의 개념이 중요해지지 않는 것이 아니고, 오히려 보이지 않아졌기 때문에 그것을 알기가 어려워져서 더 알아야 하는 개념이 된 것이다.</p> <p>그럼 Prototype의 개념은 여기까지 하고 Prototype Chaining은 무엇일까?</p> <p>이것을 설명하기 위해선 <code class="language-plaintext highlighter-rouge">Prototype Object</code>와 <code class="language-plaintext highlighter-rouge">Prototype Link</code>를 알고 넘어가야 한다.</p> <h1 id="prototype-object">Prototype Object</h1> <p>JS에서 객체가 생성되면 언제나 함수로써 생성이 된다.</p> <div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">person</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">// 함수 선언</span> <span class="kd">const</span> <span class="nx">personObject</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">person</span><span class="p">();</span> <span class="c1">// 함수로 객체 인스턴스 생성</span> </code></pre></div></div> <p>그리고 우리가 편하게 쓰고있는 객체나 함수, 배열 생성도 예외는 아니다.</p> <p>겉으로 보기에는 Primitive type으로 선언 된 것 처럼 보이지만, 별칭처럼 사용 할 수 있게 된 것 뿐이지 실제 동작은 모두 Object 함수 호출로써 객체가 만들어지고 있다.</p> <div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span> <span class="c1">// const obj = new Object();</span> <span class="kd">const</span> <span class="nx">arr</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// const arr = new Array();</span> </code></pre></div></div> <p>이렇게 생성된 객체는 해당 객체의 Prototype Object 생성과 함께 연결되게 된다.</p> <p><img src="https://www.dropbox.com/s/ueiuxqgnyijcwjp/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-09%2007.41.22.png?raw=1" alt="https://www.dropbox.com/s/ueiuxqgnyijcwjp/스크린샷 2020-06-09 07.41.22.png?raw=1" /></p> <p>by https://medium.com/@bluesh55 이하 동일</p> <p>쉽게 말해서 객체 생성을 부모를 만들어주고(Prototype Object) 그 관계를 연결(Prototype)해 주는 것이다.</p> <p>생성된 Prototype Object는 <code class="language-plaintext highlighter-rouge">constructor</code>와 <code class="language-plaintext highlighter-rouge">__proto__</code> 를 갖게 된다.</p> <p><code class="language-plaintext highlighter-rouge">constructor</code>는 Prototype Object로 인스턴스 생성시 <code class="language-plaintext highlighter-rouge">new</code>로 객체 생성을 할 수 있게 해 주고,</p> <p><code class="language-plaintext highlighter-rouge">__proto__</code> 는 인스턴스 생성시 Prototype Object와의 연결 관계를 만들어 준다.</p> <div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">Person</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">// 객체 생성 및 Product Object 연결</span> <span class="nx">Person</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">eyes</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// 객체의 prototype 조작</span> <span class="nx">Person</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">nose</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">kim</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">();</span> <span class="c1">// constructor 예약어로 만든 인스턴스</span> <span class="kd">var</span> <span class="nx">park</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">():</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">kim</span><span class="p">.</span><span class="nx">eyes</span><span class="p">);</span> <span class="c1">// 2</span> </code></pre></div></div> <h1 id="prototype-link">Prototype Link</h1> <p>이 부분은 위에서 Prototype Object 의 부산물의 핵심이자 Prototype Chaining을 만들어주는 <code class="language-plaintext highlighter-rouge">__proto__</code> 의 설명이다.</p> <p>Prototype은 class와 비교하면 쉬운데, <strong>class에서 생성된 변수나 함수들을 <code class="language-plaintext highlighter-rouge">상속</code> 해서 쓸 수 있게 하는 것 처럼 JS의 Prototype도 상속을 <code class="language-plaintext highlighter-rouge">__proto__</code> 로 구현 한 것이다.</strong></p> <p><img src="https://www.dropbox.com/s/eqtgc18dd1mlgo0/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-09%2008.46.44.png?raw=1" alt="" /></p> <p>Prototype 속성은 함수만 갖지만 <code class="language-plaintext highlighter-rouge">__proto__</code> 는 모든 객체가 갖고있다.</p> <p>따라서 인스턴스 수준에서는 인스턴스의 Prototype Object의 변수를 가져다 쓰는것이 가능해지고, 만약 여기에도 존재하지 않는다면 Object Prototype Object까지 거슬러 올라가서 확인하고 값이 없다면 <code class="language-plaintext highlighter-rouge">undefined</code>를 리턴하는 구조이다.</p> <p>그래서 모든 객체는 Object의 손자이자 모든 객체는 Object가 최초의 인간 아담과 하와인 것이다.</p> <p>이런 다양한 Prototype 특징을 생각하면 String이나 Array로 생성된 인스턴스들의 prototype에 우리가 흔히 쓰는 toString, concat ··· 을 찾아 볼 수 있다.</p> <p><img src="https://www.dropbox.com/s/wrhei21s1421tbl/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-09%2008.44.40.png?raw=1" alt="" /></p> <p><img src="https://www.dropbox.com/s/0sv8w7kgww12x26/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-09%2008.44.13.png?raw=1" alt="" /></p> <p><strong>이렇게 <code class="language-plaintext highlighter-rouge">__proto__</code> 를 통해 상위 프로토타입들과의 연결성이 Chain처럼 연결되어있어서 이를 <code class="language-plaintext highlighter-rouge">Prototype Chaining</code>이라고 한다.</strong></p> <h1 id="정리">정리</h1> <ul> <li> <p>JS는 다중 패러다임, 동적 언어이기 때문에 보는 시각에 따라 다른 다형성을 갖는다.</p> </li> <li>JS는 OOP(Object Oriented Programing)의 class를 묘사하기 위해 prototype의 개념이 생겼다.</li> <li>Prototype이 생성되면 Prototype Object 와 Prototype Link가 함께 따라간다.</li> </ul> <p>Prototype은 js이전에도 일상에서 종종 들어 볼 수 있는 말이니 쉽게 생각 하면 객체 생성시 그 객체에 대한 금형을 만드는 것을 prototype이라고 이해하고 이를 기반으로 인스턴스를 찍어내는 개념이라고 쉬울 것 같다.</p> <p>JS의 Prototype에 대한 공부를 다소 미뤄왔는데 대충만 알고있었지 Prototype 이 생성되며 Prototype Object나 Prototype Link가 생성되는 부분까지 디테일하게 알지는 못해서 개념을 정립하고 나니 JS와 조금 더 친해진 것 같다는 생각이 든다.(나만 그러니.. 우리 친해지자 자스)</p> <h1 id="reference">Reference</h1> <ul> <li><a href="https://velog.io/@sik2/JS-CoreJavaScript-프로토타입-체이닝Prototype-Link-Prototype-Object">sik2.log</a></li> <li><a href="https://medium.com/@bluesh55/javascript-prototype-이해하기-f8e67c286b67">javascript 프로토타입 이해하기</a></li> </ul> Tue, 09 Jun 2020 00:00:00 +0000 https://samslow.github.io/development/2020/06/09/Javascript_Basic_Prototype-Chaining/ https://samslow.github.io/development/2020/06/09/Javascript_Basic_Prototype-Chaining/ web development 2020 Naver Hackday Review 네이버 핵데이 후기 <ul> <li>2020년 5월 6일부터 28일까지 진행한 <a href="https://github.com/2020-NAVER-CAMPUS-HACKDAY/commonhttps://github.com/2020-NAVER-CAMPUS-HACKDAY/common">2020 Naver Hackday</a> 네이버 핵데이 후기 입니다.</li> </ul> <h1 id="서류-지원">서류 지원</h1> <p><img src="https://www.dropbox.com/s/taetursrrtl2do7/Untitled.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled.png" /></p> <p>사실 핵데이 지원 자체는 이번이 처음이지만 시도로는 2번째(?)다.</p> <p>처음 시도는 2019년 초에 회사를 다니면서 작성했었는데, 지원 해 본 사람은 알겠지만, 지원서에 문항이 정말 많다.</p> <p>다른 어떤 지원서와 비교할 수 없을 정도로 문항수와 그 길이가 남다른 편인데, 나름 처음 지원 하는 거라고 힘을 주고 썼다가 마지막 제출 시간 1시간을 남겨두고 퇴근과 함께 쏟아진 잠에 깜빡 졸았다가 기간을 놓쳐버린 케이스다.</p> <p>그래서, 이번에는 너무 힘을 주고 쓰기보다는 내용 전달에 집중해서 미사여구는 빼고 담백함을 위주로 담은 것 같다.</p> <p>내가 한 것들을 최대한 이해하기 쉽게 실제 사례에 비추어 전달하는것에 초점을 맞추었고 기술 역량에는 공부한 내용과 적절히 섞어서 작성했더니 바로 다음 전형인 코딩테스트로 이어졌다.</p> <h1 id="코딩-테스트">코딩 테스트</h1> <p>서류를 합격하고, 바로 코딩테스트를 진행했는데 계속 코테를 봐 온 입장에서 문제 난이도는 그렇게 어렵지 않았다. 대부분의 분들도 그렇겠지만 3문제 중 2번까지는 쉬운 편이었고 3번문제는 갑자기 확 어려워지는 느낌이라서 100점을 맞지는 못 했지만, 다행히도 합격해서 네이버 핵데이를 할 수 있게 되었다.</p> <p>여담이지만, 어떻게 뽑게 됐는지를 물어보았을 때, 모든 문제를 푼 사람도 좋지만, 최소한 2문제 이상을 푼 사람들은 서류를 보고 뽑았다고 이야기를 들었다.</p> <p><img src="https://www.dropbox.com/s/e7x35532b4hxvw4/_2020-06-05_19.03.09.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/_2020-06-05_19.03.09.png" /></p> <p>다만 COVID-19의 영향으로 그렇게 가보고 싶었던 NAVER 춘천 연수원은 못 가보고 온라인 해커톤 방식으로 진행되었다. 춘천 구봉산을 놀러가면 그 옆에 펼쳐진 네이버 데이터센터가 정말 멋져보였고 언젠가 한번 방문 해 보고 싶다고 느낄 정도였는데 첫 핵데이에서 그것을 이루지 못해서 아쉬움이 이만저만이 아니었다.</p> <h1 id="네이버-웹툰-프로젝트">네이버 웹툰 프로젝트</h1> <p>내가 참여한 프로젝트는 전체 총 15개의 다양한 법인에서 나온 프로젝트 중 네이버 웹툰에서 진행하는 <code class="language-plaintext highlighter-rouge">네이버 웹툰 썸네일 저작 도구</code>를 만드는 것이었다.</p> <p><a href="https://github.com/2020-NAVER-CAMPUS-HACKDAY/common/issues/13">요구사항</a>은 쉽게 말해서 온라인으로 편집 할 수 있는 포토샵을 만드는 것이었다.</p> <p>나는 커리어를 Backend로 시작했다가 Frontend에서 적성을 찾아서 이쪽으로 깊이 공부하는 중이다보니 이런 프로젝트 위주로 보았고, 2지망으로도 <code class="language-plaintext highlighter-rouge">플레이스 리뷰 실시간 Dashboard</code>를 선택했었다. 운이 좋게도 1지망으로 선택한 것이 선정되어 프로젝트에 참여 할 수 있게 되었다.</p> <h1 id="팀-구성-및-회의">팀 구성 및 회의</h1> <p>저번 핵데이까지는 한 팀에 3명씩이었는데 이번 핵데이는 온라인으로 진행되어서 그런지 한 팀에 5명씩 배정 된 것 같다. 우리팀의 멘토분들은 한 프로젝트에 5명은 다소 많다고 생각이 드셨는지 2, 3 명으로 나누어 같은 내용으로 프로젝트를 하자고 제안하셨고 나는 2명팀으로 배정되어 프로젝트를 진행하게 되었다.</p> <h2 id="초기-디자인">초기 디자인</h2> <p><img src="https://www.dropbox.com/s/ubhqkkybfwpuuke/_2020-06-05_19.33.20.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/_2020-06-05_19.33.20.png" /></p> <p>팀 구성을 마친 당일날 바로 어떤식으로 진행할지 회의를 했다.</p> <p>운이 좋게도 함께 팀원으로 활동 해 주신 분이 오픈마인드로 여러가지를 시도해보자는 요청에도 OK 해주셔서 굉장히 좋았었다. 사실 이 단계에서 마음이 맞지 않으면 일이 축축 쳐지기 마련인데 당일에 바로 페이퍼 디자인까지 뽑을 수 있었던것은 합이 척척 맞았기 때문이 아니었을까.</p> <h2 id="convention-맞추기">Convention 맞추기</h2> <p>이 외에도 협업을 위해 코딩 컨벤션을 맞추기 위해 vscode의 prettier 설정을 서로 맞추었는데, <code class="language-plaintext highlighter-rouge">.prettierrc</code> 파일을 만들어 맞추자는 얘기도 나올 만큼 서로 협업의 중요성은 잘 알고 있었던 것 같다. 이 외에도 commit message 규칙을 만들어 작업 기록에 대한 중요성도 인지하고 있었고, 아마 이 프로젝트가 장기화 되었으면 더 가치를 빛낼 여러가지 것들을 하루만에 맞출 수 있었던 것 같다.</p> <h1 id="개발-개발막혀도-go">개발.. 개발..막혀도 Go!</h1> <p><img src="https://www.dropbox.com/s/6yiwdoxteabaxa0/Untitled%201.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%201.png" /></p> <p><img src="https://www.dropbox.com/s/ajda6i8mt37c3yw/Untitled%202.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%202.png" /></p> <p>처음에는 다양한 색상을 사용해 각 컴포넌트를 분리하는 작업을 CRA와 함께 레이아웃을 먼저 잡았다.</p> <p>이후에는 <code class="language-plaintext highlighter-rouge">store management</code>나 <code class="language-plaintext highlighter-rouge">typescript</code> 등을 도입하여 프로젝트 생산성을 높일 수 있었다.</p> <p>컴포넌트 단위로 구분해서 처음에 레이아웃을 잡아놓으니 이후에 개발을 할 때에도 <code class="language-plaintext highlighter-rouge">issue</code>를 각자 assign 해서 작업하니 <code class="language-plaintext highlighter-rouge">gitflow</code> 덕분에 충돌도 나지 않아서 빠른 개발을 할 수 있었던 것 같다.</p> <p><del><strong>mobx 가 문제를 일으키기 전 까지는</strong></del></p> <h2 id="mobx의-이면-black-magic">MobX의 이면.. Black Magic!</h2> <p>이 글을 쓴 이유라고 할 수도 있는 mobx의 저주에 대한 이야기이다.</p> <p>실제로 PR에도 가장 comment가 많이 달린 issue이기도 하다.</p> <p><img src="https://www.dropbox.com/s/q7oav6zws7njqtk/Untitled%203.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%203.png" /></p> <p>개발을 하다보면 <strong>Magic</strong> 이라는 말에 쉽게 혹하곤 하는데 아무래도 Java Spring 처럼 A-Z 보다는 Ruby On Rails 처럼 <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">Convention over Configuration</a> 방식이 손이 덜 가고 API 콜만으로 완성되기 때문에 빠르게 개발해야되는 이번 프로젝트에서는 겁도없이 MobX를 사용하고자 했다. 왜냐하면 이전에 class component에서는 잘 써 왔고, 문제를 일으킨 적이 없이 잘 작동했기 때문이었다.</p> <p>하지만, 이것이 재앙의 시작이 될 지는 아무도 몰랐다.</p> <p><img src="https://www.dropbox.com/s/kruam1semb3vugz/Untitled%204.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%204.png" /></p> <p><img src="https://www.dropbox.com/s/i2rlvzgr0efjp58/Untitled%205.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%205.png" /></p> <p>위의 버그들을 설명하기 위해서는 Redux의 불변성 법칙을 먼저 알고 가야 한다.</p> <p>Redux의 상태는 읽기 전용이며, Reducer는 순수 함수를 유지하면서 상태의 불변성을 개발자가 만들어주어야 한다. 이외에도 객체는 상태로 할당하지 말아야하며 스토어는 하나만 써야한다는 법칙이 있지만, <code class="language-plaintext highlighter-rouge">불변성</code> 을 유지하는것이 핵심인 상태 관리 라이브러리이다.</p> <p>MobX에서는 <code class="language-plaintext highlighter-rouge">Simple, scalable state management</code> 이라는 소개로 이러한 설정들을 스스로 알아서 해주는 것에 초점을 맞춘 상태 관리 라이브러리이다.</p> <p>하지만, 이번 프로젝트에서 MobX는 예상대로 동작하지 않았고, 그 이유는 Functional component기반의 React Hooks API 사용에서 비롯된다고 판단하였다.</p> <p>예를들어 설명하면 <code class="language-plaintext highlighter-rouge">Layer store</code> 에는 각각의 이미지 객체가 배열에 들어가있는데, 새로운 레이어가 추가될 때 마다 배열의 제일 앞에 새 객체가 추가되는 식으로 구현을 했다. 하지만, <code class="language-plaintext highlighter-rouge">Layer store</code>를 구독하는 Component 들은 이런 배열이 바뀌는 것을 객체 전체의 데이터를 변경했고, shift되어 자연스럽게 구현될 것이라 생각 했던 것과 다르게 동작했다. 또한 class component를 hooks와 연동하며 발생한 문제도 자연스럽지 않은 흐름을 만드는 것에 한몫 한 것 같다. 그렇게 나는 너무나 소중한 4일을 날려버린 끝에 Redux로 library 를 변경하며 Mobx 와는 정을 떼게 된다.</p> <p><img src="https://www.dropbox.com/s/4mt3komoalfo08y/Untitled%206.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%206.png" /></p> <p><strong>결론은 MobX는 Magic은 Magic인데 Black Magic일 수도 있으니 제대로 알고 사용하자</strong></p> <h1 id="이게-완성된다고">이게 완성된다고?</h1> <p>4일이라는 귀중한 시간을 MobX 하나 때문에 날려버린건 정말 천추의 한이지만, Redux로 변경하고나서 부터는 프로젝트에 속도가 제대로 붙기 시작했다.</p> <p>Redux는 MobX에 비해서 편리함과 사용성은 떨어지지만, 직관성 만큼은 Magic이라는 이름 아래 나에게는 빚좋은 개살구같던 MobX 보다 뛰어났다.</p> <p><img src="https://www.dropbox.com/s/tyffulkrntmk8gz/Untitled%207.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%207.png" /></p> <p>Zerocho Blog 발췌</p> <p>각각의 상태는 컴포넌트를 렌더링하고, 컴포넌트에서 발생한 액션은 정의된 Reducer에 의해 상태로 반영된다.</p> <p>MobX도 이와 같은 흐름을 비슷하게 갖고 있긴 하지만 코드에서는 이것이 많이 숨겨져 있는 편이다.</p> <p>일단 4일의 번뇌를 넘고나니 머릿속에 그려져있던 그림은 실체화에 속도가 붙기 시작했고 결과물은 아래와 같이 나왔다.</p> <p><img src="https://www.dropbox.com/s/hwg8oiv1t8m51su/Untitled%208.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%208.png" /></p> <p><a href="https://samslow.github.io/majoongGrim/">마중그림 프로젝트</a></p> <h1 id="프로젝트-회고">프로젝트 회고</h1> <p>Frontend를 React Native로 처음 접하고 React를 배우고 써본 것이 이번이 처음인데, 언젠간 하겠지 하는 마음으로 미뤄두었던 기술 스택을 이번 기회에 React(with Hooks)를 하며 많은 것을 접했다.</p> <p><img src="https://www.dropbox.com/s/nom933wej9rx0fs/Untitled%209.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%209.png" /></p> <p>2020년 6월이 되어서 개인 프로젝트와 과거의 코드들을 되돌아 보았는데, Styled Components 를 왜 쓰지 않았지 TSC를 왜쓰지 않았지 하는 의문 투성이인 코드들이 산재해 있었다. 과장 좀 보태서 HTML &amp; CSS로만 작업하다가 React Native로 앱을 처음 접하게 되었던 과거가 생각 날 만큼 개인적으로는 엄청 성장한 시간이었다.</p> <h2 id="아쉬운-점">아쉬운 점</h2> <p>그럼에도 불구하고 아쉬운것은 어느 프로젝트나 마찬가지인데 대표적으로 2가지가 생각난다.</p> <p>첫째로 성능 개선을 이루어보지 못 한 것.</p> <p>React dev tools 를 켜고 re rendering 성능을 측정 해 보면 이미지를 추가할 때 서로 관련이 없는 Component 들도 re render되는 현상이 발견되는데, 비단 이런 상황 뿐 아니라 이미지 리사이징이나 로테이션에서도 똑같이 rerender 되는 현상이 발견된다. 이런 현상은 Redux에다가 객체를 넣고, 이를 불변성 유지를 위해 통째로 교체를 하기 떄문에 일어나는 현상인데, 이를 인지하고 있는 지금 다시 코드를 짠다면 특정 Flag로 변화를 알리거나 JSON type으로 데이터를 다루며 layers 전체를 교체하지 않고 특정 데이터만 수정하여 re render를 방지 할 수 있을 것 같다.</p> <p>둘째, HTML Element로 image controll을 구현했지만 mouse Event 로 자연스럽게 하지 못 한 것</p> <p>현재 마중그림은 이미지 리사이징이나 로테이션이 controller 에서 slider로 조절하는 방식으로 되어있는데, 일반적으로 포토샵이나 기타 이미지 편집툴에서는 image border쪽을 Mouse Event로 조절할 수 있게 되어있기 때문에 부자연스럽게 받아들여지는 UX가 만들어지게 되었다.</p> <p>구현하는 방법은 이게 쉬워서 일단 이렇게 만들어 놨는데, 개발 기간이 막판에 쫄려서 쳐내지게 된 경우이다.</p> <p>이게 더 아쉬운 이유는 Canvas 로 구현했다면 더 까다롭게 했었어야 했는데, 이런 단점을 HTML Element로 극복했으면서 십분 활용하지 못한 아쉬움이 매우 크다.</p> <h2 id="그래도-잘-한-것">그래도 잘 한 것</h2> <p>프로젝트를 하며 코드 충돌을 방지하기 위해 GitHub을 잘 사용했다고 생각한다.</p> <p>그리고 추가적으로 개발 일정 관리를 위해 GitHub Projects나 Issues를 적극 활용했는데, 소규모의 프로젝트에서 Trello나 Jira로 티켓을 만들어 하는 것 보다는 GitHub Projects를 사용하면 더 쉽고 편리하게 작업 할 수 있었던 것 같다.</p> <p><img src="https://www.dropbox.com/s/wu7shxnr4glsdht/Untitled%2010.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%2010.png" /></p> <p>한가지 아쉬웠던 점은 Github Actions를 만질 일도 있었는데, 이를 활용하면 코드가 merge 될 때마다 CI/CD 를 깃헙 페이지로 자동으로 배포되게 할 수 있었는데 권한 문제로 결국엔 적용하지 못했지만, 어떤식으로 돌아가는지 파악 할 수 있어서 다음 프로젝트에서 기회가 되는대로 적극 사용 할 예정이다.</p> <p><img src="https://www.dropbox.com/s/t62z0j5camq14p2/Untitled%2011.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%2011.png" /></p> <p>그렇게 완성하게 된 <del>인피니티 스톤</del> 깃헙의 다양한 기능들이 마치 타노스의 그것을 보는 느낌이었다 ㅋㅋ</p> <p>툴로써 완전해지지는 않겠지만, 그것으로 더욱 강력 해 지는 개발자가 되는 기분이다.</p> <p>GitHub은 알면 알수록 공부할 게 많은 곳이지만, 역시 그럴 때마다 성장하게 되는 것 같다.</p> <p><img src="https://www.dropbox.com/s/3n4ja2ryewicda3/Untitled%2012.png?dl=1" alt="2020%20Naver%20HackDay%202020%20Review%202021bff732804af18f824e6bad6b971b/Untitled%2012.png" /></p> <p>이번에는 특히 <code class="language-plaintext highlighter-rouge">Container Presenter Pattern</code>을 사용하려고 의식했는데 이는 마치 양 목장에서 디자인 패턴이라는 울타리를 쳐 두고 그 안에서 React를 자유롭게 다뤄 볼 수 있는 안전장치 역할을 했기 때문에 이러한 방식을 앞으로도 계속 사용 할 수 있을 것 같다.</p> <h1 id="참여-후기">참여 후기</h1> <p>사실 오늘 이 글을 쓰는 날에 인턴으로의 전환 면접에는 참석 할 수 없다는 통보를 받고 기분이 다운되어 있었지만, 생각 해 보면 이 프로젝트를 통해 성장한 내 자신이 그것보다 더 가치있었던 시간이었는데 나무를 너무 보다보면 숲을 놓친다는 것을 잊을 뻔했다.</p> <p>네이버 핵데이는 학생때만 참여 할 수 있는 해커톤이지만 그 수준만큼은 학생 그 이상의 것을 얻어 갈 수 있는 행사이다. 온라인으로 지원되어 춘천 연수원에는 방문 해 보지 못했지만 그에 상응하는 다양한 온라인 지원과 음식(?)과 공간 지원을 받았기 때문에 한달동안 시간과 정신의 방에서 수련을 하다 돌아온 손오공의 느낌이 나기도 한다.</p> <p><img src="https://www.dropbox.com/s/k9b3znyhiuiu5kh/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202020-06-06%2000.14.41.png?dl=1" alt="" /></p> <p>위 내용의 후기들은 <a href="https://www.slideshare.net/HyeonseokSeo1/naver-hackday-2020-235055067">슬라이드 쉐어에 더 잘 정리된 발표자료</a>가 있다.</p> <p>혹시 더 자세한 내용을 알고싶으신 분들은 발표자료를 참고 하시면 될 것 같다.</p> Fri, 05 Jun 2020 00:00:00 +0000 https://samslow.github.io/diary/2020/06/05/2020-Naver-Hackday-Review/ https://samslow.github.io/diary/2020/06/05/2020-Naver-Hackday-Review/ hackathon diary Flipper와 함께하는 React Native 0.62 릴리즈노트 <ul> <li>React Native 0.62 버전이 공식 릴리즈되었습니다. 어떤것이 변했는지, 공식 릴리즈 노트를 번역 한 내용입니다.</li> </ul> <h1 id="flipper와-함께하는-react-native-062-버전-안내">Flipper와 함께하는 React Native 0.62 버전 안내</h1> <p>2020년 3월 26일, Facebook React Native core 팀의 <a href="https://twitter.com/rickhanlonii">Rick Hanlon</a> 작성</p> <p>오늘 Flipper를 기본으로 내장한 ReactNative 0.62가 릴리즈 되었습니다.</p> <p>이번 발표는 Global pandemic 중에 버전을 출시 할 것 입니다. 이 출시를 가능하게 한 수백 명의 contributor들에게 존경을 표하고 개발되고 있는 버전이 릴리즈 버전과 너무 뒤떨어지는 것을 막기 위해서 입니다. issue 해결과 필요시 업그레이드 지연을 준비할 contributor가 다소 부족한 것을 양해 해 주세요.</p> <h2 id="flipper가-기본으로-들어갑니다">Flipper가 기본으로 들어갑니다.</h2> <p><a href="https://fbflipper.com/">Flipper</a>는 모바일 앱들을 디버깅하는 개발 툴 입니다. 이미 Android나 iOS 커뮤니티에서 유명하고 이번 버전에서 React Native App에서도 기본으로 사용할 수 있게 되었습니다.</p> <p><img src="https://reactnative.dev/blog/assets/0.62-flipper.png" alt="Flipper" /></p> <p>Flipper는 아래 기능들을 즉시 제공합니다.</p> <ul> <li><strong>Metro Actions:</strong> 앱을 다시로드하고 툴바에서 바로 개발자 메뉴를 트리거합니다.</li> <li><strong>Crash Reporter:</strong> Android 및 iOS 기기에서 충돌 보고서를 봅니다.</li> <li><strong>React DevTools:</strong> 다른 모든 도구와 함께 최신 버전의 React DevTools를 사용합니다.</li> <li><strong>Network Inspector:</strong> 장치의 앱에서 오고 가는 모든 네트워크 요청을 봅니다.</li> <li><strong>Metro and Device Logs:</strong> Metro 및 Device에서 모든 로그를 보고 검색하고 필터링합니다.</li> <li><strong>Native Layout Inspector:</strong> React Native 렌더러의 기본 레이아웃 출력을 보고 편집합니다.</li> <li><strong>Database and Preference Inspectors:</strong> 장치 데이터베이스 및 환경 설정을 보고 편집합니다.</li> </ul> <p>추가적으로, Flipper는 확장 가능한 플랫폼이므로 NPM에서 플러그인을 가져오는 마켓 플레이스를 제공하므로 워크 플로에 맞는 사용자 지정 플러그인을 게시하고 설치할 수 있습니다. 사용 가능한 플러그인을 <a href="https://www.npmjs.com/search?q=flipper-plugin">여기</a>에서 확인하십시오.</p> <p>더많은 정보는 <a href="https://fbflipper.com/docs/features/react-native.html">Fipper 문서</a>를 참조하세요.</p> <h2 id="새로운-다크모드-기능들">새로운 다크모드 기능들</h2> <p>선호하는 색 구성표 (밝거나 어두운)와 같은 사용자의 모양 기본 설정에 액세스 할 수 있도록 새로운 <code class="language-plaintext highlighter-rouge">Appearance</code> 모듈을 추가했습니다.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>const colorScheme = Appearance.getColorScheme(); if (colorScheme === 'dark') { // Use dark color scheme } </code></pre></div></div> <p>또한 사용자 환경 설정에 대한 상태 업데이트를 구독하기 위한 hook를 추가했습니다.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import {Text, useColorScheme} from 'react-native'; const MyComponent = () =&gt; { const colorScheme = useColorScheme(); return &lt;Text&gt;useColorScheme(): {colorScheme}&lt;/Text&gt;; }; </code></pre></div></div> <p>자세한 내용은 <a href="https://reactnative.dev/docs/appearance">Appearance</a>와 <a href="https://reactnative.dev/docs/usecolorscheme">useColorScheme</a> 문서를 참조 해 주세요.</p> <h2 id="react-native-tvos로-apple-tv를-옮깁니다">react-native-tvos로 Apple TV를 옮깁니다.</h2> <p><a href="https://reactnative.dev/blog/#lean-core">Lean Core effort</a>에서 언급한 바와 같이 Apple TV를 React Native Windows 및 React Native macOS와 같은 다른 플랫폼에 맞추기 위해 Apple TV 전용 코드를 코어에서 제거하기 시작했습니다.</p> <p>앞으로 React Native에 대한 Apple TV 지원은 <code class="language-plaintext highlighter-rouge">react-native-tvos</code> NPM 패키지와 함께 <a href="https://github.com/react-native-community/react-native-tvos">react-native-community/react-native-tvos</a>에서 유지될 것입니다. Apple TV를 지원하는 데 필요한 변경 사항만 가지고 있는 코어에서의 repository fork입니다.</p> <p><code class="language-plaintext highlighter-rouge">react-native-tvos</code>의 릴리즈는 React Native의 공개 릴리스를 기반으로 합니다. <code class="language-plaintext highlighter-rouge">react-native</code>의 0.62 릴리스의 경우 <code class="language-plaintext highlighter-rouge">react-native-tvos</code> 0.62를 사용하도록 Apple TV 프로젝트를 업그레이드하십시오.</p> <h2 id="더-나아진-버전-업그레이드-지원">더 나아진 버전 업그레이드 지원</h2> <p>0.61이 출시되자 community에서는 개발자들이 새로운 버전의 React Native로 업그레이드하는 것을 지원하기 위해 새로운 <a href="https://react-native-community.github.io/upgrade-helper/">upgrade helper</a> tool을 도입했습니다. upgrade helper는 업그레이드 할 버전과 현재 여러분이 쓰고 있는 버전과의 다른 차이를 제공하여 특정 upgrade에 필요한 변경사항을 볼 수 있습니다.</p> <p>이 툴을 사용하더라도 업그레이드 시 문제가 발생합니다. 오늘은 <a href="https://github.com/react-native-community/upgrade-support">Upgrade-Support</a>를 발표하여 보다 전문적이 된 Upgrade-Support를 소개합니다. Upgrade-Support는 GitHub Issue tracker에서, 사용자가 프로젝트 업그레이드와 관련된 문제를 게시하여 커뮤니티의 도움을 받을 수 있습니다.</p> <p>우리는 항상 업그레이드 환경을 개선하기 위해 노력하고 있으며, 이러한 tool이 사용자가 아직 다루지 못한 특이 케이스에 필요한 지원을 제공하기를 바랍니다.</p> <h2 id="다른-개선점들">다른 개선점들</h2> <ul> <li><strong>LogBox:</strong> 새로운 LogBox 오류 및 경고 환경을 opt-in으로 추가하고 있습니다. 이를 사용하려면 index.js 파일에 <code class="language-plaintext highlighter-rouge">require('react-native').unstable_enableLogBox()</code>를 추가하십시오.</li> <li><strong>React DevTools v4:</strong> 이번 버전은 상당한 성능 향상, 개선된 탐색 경험 및 React Hooks에 대한 완전한 지원을 제공하는 최신 <a href="https://reactjs.org/blog/2019/08/15/new-react-devtools.html">React DevTools</a>로 업그레이드하는 것이 포함됩니다.</li> <li><strong>접근성 향상:</strong> <a href="https://reactnative.dev/docs/accessibility#accessibilityvalue-ios-androidhttps://reactnative.dev/docs/accessibility#accessibilityvalue-ios-android">accessibilityValue</a> 추가, <a href="https://github.com/facebook/react-native/commit/8c0c860e38f57e18296f689e47dfb4a54088c260">Touchables</a>에 빠진 props들, <code class="language-plaintext highlighter-rouge">onSlidingComplete</code> <a href="https://github.com/facebook/react-native/commit/c7aa6dc8270c0eabc913fe6c617c8131e3f4b3c5">accessibility events</a>, 스위치 컴포넌트의 기본 역할을 <code class="language-plaintext highlighter-rouge">"button"</code>에서 <code class="language-plaintext highlighter-rouge">"switch"</code>로 변경하는 등 접근성을 개선했습니다.</li> </ul> <h2 id="주요-변경-사항들">주요 변경 사항들</h2> <ul> <li><strong>PropTypes 제거:</strong> React Native 코어가 미치는 앱 크기에 대한 영향을 줄이고 런타임 대신 컴파일 타임에 확인하는 것이 더 낫기 때문에 코어 컴포넌트에서 <code class="language-plaintext highlighter-rouge">propTypes</code> 를 제거했습니다.</li> <li><strong>accessibilityStates 제거:</strong> 컴포넌트가 상태에 대한 정보를 접근성 서비스에 설명 할 수있는 보다 의미있는 방법 인 새로운 <code class="language-plaintext highlighter-rouge">accessibilityState</code> 소품을 위해 사용되지 않는 <code class="language-plaintext highlighter-rouge">accessibilityStates</code> 속성을 <a href="https://github.com/facebook/react-native/commit/7b35f427fd66cb0f36921b992095fe5b3c14d8b9">제거했습니다.</a></li> <li><strong>TextInput 변화</strong>: 일반적이지 않고 W3C와 호환되지 않으며 <a href="https://github.com/react-native-community/discussions-and-proposals/issues/4">Fabric</a>에서 구현하기가 어렵기 때문에 <a href="https://github.com/facebook/react-native/commit/3f7e0a2c9601fc186f25bfd794cd0008ac3983ab">TextInput</a>에서 <code class="language-plaintext highlighter-rouge">onTextInput</code>을 제거했습니다. 또한 문서화되지 않은 <code class="language-plaintext highlighter-rouge">inputView</code> prop 및 <code class="language-plaintext highlighter-rouge">selectionState</code>를 제거했습니다.</li> </ul> <h2 id="향후-사용되지-않을-것들">향후 사용되지 않을 것들</h2> <ul> <li><code class="language-plaintext highlighter-rouge">AccessibilityInfo.fetch</code> 는 이미 사용되지 않습니다.하지만 우리는 이번 업데이트에서 경고창이 뜨도록 했습니다.</li> <li>향후 기본값 전환을 지원하려면 <code class="language-plaintext highlighter-rouge">useNativeDriver</code> <a href="https://github.com/facebook/react-native/commit/5876052615f4858ed5fc32fa3da9b64695974238">설정이 필요합니다.</a></li> <li><code class="language-plaintext highlighter-rouge">Animated</code> 컴포넌트의 <code class="language-plaintext highlighter-rouge">ref</code>는 이제 내부 컴포넌트이며 더 이상 <a href="https://github.com/facebook/react-native/commit/66e72bb4e00aafbcb9f450ed5db261d98f99f82a">사용되지 않는</a> <code class="language-plaintext highlighter-rouge">getNode</code>입니다.</li> </ul> <h2 id="감사">감사</h2> <p>0.62를 가능하게 한 수백 명의 contributors에게 감사합니다! 모든 업데이트를 보려면 <a href="https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#0620">0.62 변경 로그</a>를 살펴보십시오.</p> Fri, 27 Mar 2020 00:00:00 +0000 https://samslow.github.io/development/2020/03/27/ReactNative-0.62-kor-translation/ https://samslow.github.io/development/2020/03/27/ReactNative-0.62-kor-translation/ reactnative development kakao FE(Front end) meetup 후기 <ul> <li>2019년 11월 13일 (수) 오후 07:00 - 오후 09:00 선릉역 Dcamp에서 열린 Kakao의 Front End Meetup에 다녀온 후기입니다.</li> </ul> <h1 id="행사-개요">행사 개요</h1> <p><img src="https://cf.festa.io/img/2019-10-25/9b5b949f-5b29-48ad-a389-ccf9b37bcc4e.jpeg" alt="img" /></p> <p>IF Kakao 라는 컨퍼런스는 들어 보았는데 Kakao FE 라는 이름의 행사는 처음 들어본다.</p> <p>찾아보니 이번에 열리는 밋업이 처음이라고 한다.</p> <p><img src="https://www.dropbox.com/s/h19g772x0mows9u/%EB%B0%8B%EC%97%85PT.png?dl=1" alt="" /></p> <p>행사는 총 3개의 세션 각 35분간 진행되었고 평일 퇴근 이후 진행되는 행사이다보니, 차분하며 필요한 내용만 딱딱 짚어 진행 한 것 같다.</p> <p>살짝 아쉬운 점은, 행사 특성상 네트워킹이 없다는 점이었고 어떤 방식으로든 퇴근하고 온 사람들을 조금 활력을 돋을 수 있는 장치가 있었으면 좋았을 듯.</p> <p><img src="https://www.dropbox.com/s/1ny74yn5mzz2qzk/%ED%96%89%EC%82%AC%20%EA%B0%9C%EC%9A%94.png?dl=1" alt="" /></p> <p>카카오 행사 답게 처음에 오자마자 등록을 하면 카카오 프렌즈 생수를 준다.</p> <p>중국에 샤오미에게 없는 전자제품이 없듯 한국에 카카오에게는 없는 굿즈가 없는듯</p> <p>모든게 굿즈화 되어있는 것 같다.</p> <p>그리고 물과 함께 저녁을 못 먹고 온사람들을 위한 샌드위치를 받았는데, 다행인지 모르겠지만 샌드위치는 카카오가 아니었다.</p> <p><img src="https://www.dropbox.com/s/lyfqpywocpskuxd/%ED%94%84%EB%A0%8C%EC%A6%88%20%EC%9B%8C%ED%84%B0.png?dl=1" alt="" /></p> <p><img src="https://www.dropbox.com/s/y7ju5vz8jpxlbz6/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98.png?dl=1" alt="" /></p> <p><img src="https://www.dropbox.com/s/sdq0kc1dyvsfd5z/%EC%83%8C%EB%93%9C%EC%9C%84%EC%B9%98%EB%82%B4%EB%B6%80.png?dl=1" alt="" /></p> <p>사진으로 회고 해 보니 이런것만 열심히 찍은 것 같다.</p> <p>사실 발표하는걸 찍는건 느낌이 없으니 이런 거라도 열심히 찍어야지.. KakaoFE는 그렇지 않았지만</p> <p>경험상 홍보가 잘 되고 흥하는 행사는 사진찍을 거리가 많았었던 것 같다.</p> <p>그리고 놀랍게도 내가 다니는 회사가 Dcamp 5층에 있고 행사장이 바로 위층에 있어서, 퇴근하고 바로 올라가서 들을 수 있던게 참 좋았다.</p> <p>사실 평일 행사는 부담스러우니 잘 안 가려고하는데, 이렇게 바로 위에서 하는 행사를 안 가 볼 수가 없어 신청하고 다녀왔다.</p> <h1 id="세션-발표-시작">세션 발표 시작!</h1> <p>집에 있는 강아지가 아프기도 하고, 다음날 출근해야되는 리스크도 안고 있었기 때문에 차마 3개의 세션을 모두 들을 수는 없었고,</p> <p>요새는 컨퍼런스, 밋업을 가도 필요한 부분만 알맹이로 쏙쏙 뽑아듣는게 더 유익하고 집중도 잘 되기때문에 나에게 필요하다고 생각하는 세션으로 2가지를 들었다.</p> <p>다행히도(?) 마지막 세션은 내가 아직 겪어 보지 않은 <code class="language-plaintext highlighter-rouge">Vue</code>, <code class="language-plaintext highlighter-rouge">Test</code>, <code class="language-plaintext highlighter-rouge">리팩토링</code> 의 삼합이었기 때문에 과감히 제끼기로 했다.</p> <p>그렇게 듣게 된 2개의 세션은</p> <ol> <li>프렌즈타임 웹앱 삽질기 - 송명현</li> <li>카카오 커머스를 지탱하는 Angular - 김민형</li> </ol> <p>행사 소개 페이지나 행사장 내에서도 연사자에 대한 어떠한 정보도 없었기에 (심지어 사회자분도 잘 모르셨음) 들은대로 썼지만 2번 연사자분은 잘 못 들었다.</p> <p>혹시 아시는 분은 댓글로 달아주세요!</p> <h2 id="프렌즈타임-웹앱">프렌즈타임 웹앱</h2> <p>이 세션은 <code class="language-plaintext highlighter-rouge">역시 남들 삽질이 제일 재미있어 !</code> 라는 마인드로 듣기 시작했는데 의외로 들을 구석이 많았다.</p> <h4 id="프렌즈타임은">프렌즈타임은</h4> <p>카카오톡내에서 정해진 시간에 실시간으로 모여 가위바위보를 하여 상금을 얻는 퀴즈쇼이다.</p> <p>카카오톡에서 바로 웹앱으로 실행가능!(하지만 카톡 인앱 브라우저에서만)</p> <h4 id="개발-스택-정하기--네이티브-vs-웹앱-승자는">개발 스택 정하기 ; 네이티브 vs 웹앱 승자는?</h4> <p>카카오톡은 한달의 한번 업데이트를 해서 빈도가 적다. → But, 서비스는 매 주 진행되어야 함</p> <p>따라서 웹앱으로 진행하기로 했다고 함</p> <h4 id="프레임워크는-뭘로-">프레임워크는 뭘로 ?</h4> <p>전문 웹 개발자가 없어서 러닝커브가 낮은 Vue.js를 선택했다고 한다.</p> <p>근데 사실 러닝커브는 셋이 비슷하고 개인적으로는 React.js가 더 쉬울 수도 있다고 생각했다.</p> <h4 id="그럼-웹앱의-장단점은-">그럼 웹앱의 장단점은 ?</h4> <p>장점: 별도 설치 불필요, 한 벌의 코드로 크로스 플랫폼</p> <p>단점: 네트웤 환경에 의존되고 퍼포먼스 구림</p> <h4 id="spa를-사용하자">SPA를 사용하자!</h4> <p>빠르게 화면 전환이 가능해서 UX 유리</p> <p>한순간에 트래픽이 몰리면 비교적 부하가 적다.</p> <p>하지만, 초기에 받아오는 정적자원의 용량이 크기 때문에 최적화가 필요 → Lazy loading 활용</p> <p>Dynamic import 를 사용해서 Promise로 필요한 시점에 컴포넌트 로드</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span><span class="p">(</span><span class="dl">"</span><span class="s2">./Export.js</span><span class="dl">"</span><span class="p">).</span><span class="nx">then</span><span class="p">(({</span> <span class="na">default</span><span class="p">:</span> <span class="nx">consoleFunction</span><span class="p">,</span> <span class="nx">exString</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">consoleFunction</span><span class="p">(</span><span class="nx">exString</span><span class="p">);</span> <span class="p">});</span> </code></pre></div></div> <h4 id="용량-최적화를-위한-webpack-번들-분석">용량 최적화를 위한 WebPack 번들 분석</h4> <ul> <li> <p>Bundler Analazer 쓰면 이미지로 어떤게 큰 용량을 차지하는지 볼 수 있어서 분석 쉬움</p> <ul> <li><img src="https://i.stack.imgur.com/fkecT.jpg" alt="" /></li> </ul> </li> <li> <p>Moment.js를 Timezone 때문에 사용했는데 생각보다 용량이 커서 드러냈다.</p> <ul> <li>하지만 day.js라는 좋은 수단이 있지 않은가? 라는 의문이 들어 질문을 했다.</li> <li>(요새 유튜브를 하고 있어서 이런거 하나하나 놓치지 않으려고 찍었다)</li> <li><img src="https://www.dropbox.com/s/y27kfn8wrdg585n/%EC%A7%88%EB%AC%B8%EC%83%98.png?dl=1" alt="질문샘" /> <ul> <li>답: 본인들도 몰랐는데 moment.js가 원래도 필요 없는 라이브러리 라서 대체제를 넣지 않음</li> </ul> </li> </ul> </li> </ul> <h4 id="웹앱에서-이미지처리는-어떻게">웹앱에서 이미지처리는 어떻게?</h4> <ul> <li>처음에 로딩할때 18.5mb정도인데 너무 크니까 비동기로 처리하려고 함 → Lazy loading</li> <li>하지만 여러 문제 <ul> <li>화면이 바뀌면?</li> <li>게다가 사람이 몰린다면!?</li> <li>CDN도 한계가 있지</li> </ul> </li> <li> <p>그래서, <strong>다음 화면에 필요한 이미지를 preload한다!</strong> → 비동기로</p> </li> <li>애니메이션은 <a href="https://docs.cocos2d-x.org/cocos2d-x/v3/en/sprites/spritesheets.html">스프라이트 시트</a>로 이미지를 연결시켜 애니메이션을 실행 <ul> <li>화면의 각 라이언별 20장의 사진 → 라이언이 깜빡거리는 문제 발생(모두가 동시에 로딩되지 않기 때문에) <ul> <li>네트워크 문제였음 → 100% 잘 도착한다고 보장 할 수 없네?</li> <li>만약에 완전히 로드 되지 않았다면 첫 프레임만 보여주자</li> </ul> </li> <li>Animation loader와 imageLoader 두개를 둬서 Image loader 가 Promise로 준비중인 상태를 Animation Loader에게 알려줬음 → Resolve 되면 애니메이션 작동</li> </ul> </li> </ul> <h4 id="생산성을-올려주는-도구들">생산성을 올려주는 도구들</h4> <h5 id="eslint">ESLint</h5> <ul> <li><a href="https://github.com/airbnb/javascript">airbnb</a> base 로 린팅</li> <li>커밋할때 git hook으로 precommit 하여 팀 컨벤션을 해치지 않도록 commit 안되게 강제</li> </ul> <h5 id="sentry">Sentry</h5> <ul> <li>유저에게 발생한 에러 트래킹 툴 <a href="https://sentry.io/welcome/">Sentry</a></li> <li>유저가 문의하지 않은 에러까지도 모두 알려준다.</li> <li>유저의 디바이스 정보까지 모두 상세하게 나오기 때문에 유저에게 따로 정보를 받을 필요가 없다.</li> </ul> <h3 id="세션-후기">세션 후기</h3> <p>나는 중간 중간에 공감되는 웃긴 포인트들이 너무 많았는데(예를 들어, 다 된 줄 알았는데 자꾸 문제 생기는 걸 볼때) 행사장이 엄숙해서 소리내서 웃지는 못했다. 전체적인 레벨은 연사자인 송명현님이 말하셨듯 쉬운 레벨이었다고 생각한다.</p> <p>조금더 딥하고 전문적인 내용을 다뤄주면 좋겠다고 생각했지만 아마 그랬으면 졸았을 것 같다. 힘들때는 역시 다른사람들 삽질하는 얘기를 들으며 <code class="language-plaintext highlighter-rouge">나만 고생하는게 아니구나</code> 하며 힐링하는 게 최고!</p> <p>비동기처리에 대한 부분을 최근에 Promise와 Async-await를 공부하고 적용하며 내가 몰랐던 부분들이 정말 많았다고 느끼고 있던 차에 들었던 세션이라 그런지 몇몇 부분은 영감을 얻기도 했다.</p> <p>프렌즈타임 이야기 외에도 Sentry라던지 ESLint는 원래도 알고있었지만, 다시한번 더 잘 사용 할 수 있을 것 같다는 생각이 든 세션이었다.</p> <h1 id="카카오-커머스-angular">카카오 커머스 Angular</h1> <p>카카오 커머스는 판매자와 소비자를 이어주는 플랫폼</p> <p>Angular가 너무 좋아서 react, vanila를 Angular로 전환중</p> <p>앞의 내용은 Angular가 왜 좋은지에 대한 내용이었는데, 사실 이 부분은 다른 프레임워크의 장점이기도 하고 앞 세션과 비슷한 내용도 있어서 나에게는 크게 와 닿지 않았다.</p> <p>그 이후 뒤에는 여러가지 공부 해 볼만한 Angular에서 지원하는 기능들에 대한 키워드들을 들었는데 이 부분은 다른 프레임웤에도 있을 기능들이라 흥미있게 들었다.</p> <h4 id="http-interceptor">http interceptor</h4> <p>앱 내 XHR 요청 전, 응답 직후 직전 추가 처리 가능</p> <p>서버에서 특정 헤더를 원할때, 캐시때문에 타임스탬프 추가해야 할때 등</p> <p>컴포넌트 레벨에서 에러 처리 고민 할 걱정 덜어준다고 한다.</p> <h4 id="rxjs로-이벤트-처리-쉽게">rxjs로 이벤트 처리 쉽게</h4> <p>RxJS는 이벤트 스트림과 데이터를 쉽게 만들고 다룰 수 있도록 돕는 라이브러리다.</p> <p>복잡하지만 가독성이 좋은 비동기적 코드를 더 쉽게 작성할 수 있도록 돕기 때문에 한번쯤 더 찾아보면 좋을거라 생각했다.</p> <h4 id="differencial-loading">Differencial Loading</h4> <p>브라우저 사양에 맞게 서비스 제공하게 하여 하위 호환 번들 따로 만들어주는 Angular CLI의 기능이라고 하는데 이따금씩 IE 11 이하의 브라우저에서 안되는 기능들이 있다거나, 여러 브라우저 호환성을 고려해야 할 때 유용 해 보였다.</p> <p>하지만 이에 따른 노력대비 얻는 점들이 적기 때문에 내가 다니는 회사에서는 B2B2C 서비스이기도 해서 그냥 Chrome을 강제하여 사용하도록 한다. 약간 Apple식(?)(불편하면 너네들이 적응해!) 느낌이긴 하지만, 시대의 흐름이 이런것을 어찌하리..</p> <h1 id="tldl">TL;DL</h1> <p>위 글을 요약할 겸 너무 길어 읽지 못한 분들을 위한 3줄 요약</p> <ol> <li>카카오 FE에 갔는데 피곤해서 세션 2개만 듣고 왔다.</li> <li>각 세션이 FE라면 공감할만한 재미 요소가 있어 평일 퇴근 후 쉬운 수준으로 듣기에 좋았다.</li> <li>질문해서 에어팟 케이스 받았는데 난 에없찐이다. 다음부턴 더 유용한걸 달라! <ul> <li><img src="https://www.dropbox.com/s/or8xvdptqts6ck6/%EC%97%90%EC%97%86%EC%B0%90.jpeg?dl=1" alt="" /></li> </ul> </li> </ol> <p>P.S 아 그리고 앞으로 행사도 계속 Dcamp에서 해주세요.. 회사가 5층에 있는데 6층에 가서 세션 듣는게 이렇게 행복할 줄은 몰랐어요.</p> Sun, 17 Nov 2019 00:00:00 +0000 https://samslow.github.io/etc/2019/11/17/kakaoFE-review/ https://samslow.github.io/etc/2019/11/17/kakaoFE-review/ lecture etc 맥북 사용자를 위한 터치바 커스터마이징 (BTT 완벽 가이드) <ul> <li>이 글은 맥북을 사용하는 분들의 터치바 활용성을 증대시키고 맥북을 더 잘 쓰고싶은 분들을 위하여 기고되었습니다.</li> </ul> <p><img src="https://www.dropbox.com/s/m0dh5lyrwcgk8rl/touchbarEx.png?raw=1" alt="touchbarEx" /></p> <p><img src="https://www.dropbox.com/s/hhptr7fo3d0r0uj/touchbarEx2.png?raw=1" alt="touchbarEx2" /></p> <ul> <li>위 사진 두장은 제가 현재 사용하고 있는 터치바 디자인 입니다. 여러분도 아래 과정을 따라하면 똑같이 나오게 됩니다.</li> </ul> <p>📢 두번째 사진의 날씨는 1번 터치바 그림에서 날씨 아이콘을 꾹 누르면 펼쳐집니다. 이 외에도 cmd와 opt를 누르면 나오는 창은 캡쳐가 안되서 올리진 못했지만 직접 올려보시고 봐보시길 바랍니다!</p> <p>📢 재생 버튼을 꾹 누르면 YoutubeMusic이 켜집니다.</p> <h1 id="커스터마이징-툴-bettertouchtoolbtt-설치">커스터마이징 툴 BetterTouchTool(BTT) 설치</h1> <ol> <li><a href="https://folivora.ai/">BTT 공식 사이트</a>로 이동하여 <code class="language-plaintext highlighter-rouge">DOWNLOAD TRIAL (45DAYS)</code>를 다운받는다.</li> <li>다운 받는 중에 손쉬운 설정을 까지 마치면 설치가 완료된다.</li> </ol> <p>45일동안 체험 해 볼 수 있는 버전으로 저는 현재 2년동안 업데이트를 받을 수 있는 라이센스를 구매하여 사용하고 있습니다.</p> <p>45일이라 하여도 실제 해 본 결과 알게된 정보는</p> <ul> <li>45일은 절대시간 기준이 아니라 실제로 맥북이 잠자기에서 깨어나 활성상태로 돌아가고 있는 시간을 기준으로 계산한다. ( 2달넘게 사용해도 체험판이 끝나지 않았음 )</li> <li>TRIAL이 끝나도 완전히 삭제 한 후 다시 설치하면 다시 처음으로 돌아가 45일동안 사용 가능 ( 대신 기존 설정이 날아감 ) <ul> <li>이를 막기 위해 현재 Preset을 저장하여 재설치 하는 방법도 있음 ( 시도 해 보지는 못함 )</li> </ul> </li> <li>2년동안 업데이트를 받을 수 있는 LICENSE는 약 8달러로 한화 약 1만원 정도로 저렴한 편 <ul> <li>단, LICENSE 파일을 메일로 보내주는데 이를 잊어버리면 골치아파지므로 주의 요망</li> </ul> </li> </ul> <h1 id="btt가-제공하는-기능">BTT가 제공하는 기능</h1> <ul> <li>키보드 단축키 커스터마이징 <ul> <li>한영변환키, 카카오톡, 캘린더, 슬랙 단축키를 등록할 수 있고 잠자기, 잠그기 등의 맥북 내장 API를 사용 할 수 있다.</li> </ul> </li> <li>터치바 커스터마이징 <ul> <li>글 상단에 있는 사진은 실제 제 맥북의 터치바를 캡처(ctrl + cmd + shift + 6) 한 화면입니다.</li> </ul> </li> <li>이외 트랙패드, 매직마우스 그리고 Siri Remote 등 커스터마이징 할 수 있는건 싹다 가능</li> <li>보통 window를 이동하려면 상단바를 드래그하여 창을 움직여야 하지만, 단축키를 누른상태로 해당 윈도우에 마우스가 올라가 있기만하면 이동 간편 <ul> <li>이게 별거 아닌거 같아도 저는 처음에 이거하나만을 위해 LICENSE를 구입 했습니다.</li> </ul> </li> <li>Snapping Area를 만들어 window를 해당 Area에 놓기만 하면 사전설정한 사이즈와 위치로 설정됨.</li> <li>이외에도 뜬금없이 Webserver 기능을 제공하는 등 잡다한 기능이 종종 들어가 있음</li> </ul> <h1 id="사용법--preset-적용법">사용법 &amp; Preset 적용법</h1> <p>BTT는 현재 계속 개발되고있는 툴로써 UI가 계속 개편되고 있는 중인데, 최근 새로운 UI가 도입되면서 때아닌 과도기(!?)를 겪는 중입니다.</p> <p>그래서 저는 충분히 안정화되고 유용한 이전 UI로 설명 하겠습니다.</p> <ol> <li> <p>설치가 되면 Btt를 Spotlight로 실행 한 뒤 상단바에 있는 트랙패드 아이콘 클릭</p> <p><img src="https://www.dropbox.com/s/g17m1d1w0lo1ztp/btt1.png?raw=1" alt="btt1" /></p> </li> <li> <p>이중 Old Configuration UI를 클릭하여 메뉴 설정 진입</p> <ul> <li>기능들을 직접 설명 하는 것 보다 아래 화면에서 본인이 커스터마이징 해 보는것이 빠름.</li> </ul> </li> <li> <p>Advanced Settings &gt; Moving &amp; Resizing 에 진입하여 아래 Move windows 와 Resize windows를 어떤 단축키와 쓸 것인지 체크</p> <ul> <li>저는 Move windows는 ctrl + cmd와 함께, Resize windows는 Ctrl + opt 로 사용 합니다. <img src="https://www.dropbox.com/s/op2ur5blfq9mvow/btt2.png?raw=1" alt="btt2" /></li> </ul> </li> <li> <p>Advanced Settings &gt; Window Snapping 에서 Window Snapping Enabled 에 체크하고 맥북 상단 바에서 Snap Areas가 활성화 되면 입맛에 맞게 커스텀 <img src="https://www.dropbox.com/s/9ftwvvmworrg3zl/btt3.png?raw=1" alt="btt3" /></p> </li> <li> <p>터치바의 완벽한 호환을 위해 <code class="language-plaintext highlighter-rouge">General Touch Bar Settings(첫번째 사진 오른쪽 위)</code> 을 클릭하여 <code class="language-plaintext highlighter-rouge">Default Settings</code>와 <code class="language-plaintext highlighter-rouge">Advanced</code>를 아래 사진처럼 세팅한다.</p> <p><img src="https://www.dropbox.com/s/66l6i18rf3k0akh/bttset1.png?raw=1" alt="bttset1" /></p> <p><img src="https://www.dropbox.com/s/lsottv6oyfxvhbd/bttset2.png?raw=1" alt="bttset1" /><img src="https://www.dropbox.com/s/1jadj5irt0giucd/bttset3.png?raw=1" alt="bttset3" /></p> </li> <li> <p>마지막으로, 아래 첨부되어 있는 bttPreset 파일을 다운받아 왼쪽 아래 <code class="language-plaintext highlighter-rouge">Manage Presets</code> 에서 <code class="language-plaintext highlighter-rouge">import</code>로 적용하면 대부분의 세팅이 완료된다.</p> <ul> <li><a href="http://bit.ly/2nmyLVZ" target="_blank">Samslow-Btt-preset</a></li> </ul> </li> <li> <p>이후에는 본인 입맛에 맞게 슥 샥 쇽 끼우면 나만의 맥북 터치바 완성!</p> </li> </ol> <p>이상으로 BTT 튜토리얼을 마칩니다.</p> <p><strong>맥북 터치바가 구리다고 안 쓰시는 분들이 많은데 이 글을 읽고 많은 분들이 본인 입맛에 맞게 커스터마이징해서 사용하셨으면 좋겠습니다.</strong></p> Mon, 30 Sep 2019 00:00:00 +0000 https://samslow.github.io/etc/2019/09/30/BTT-for-mac/ https://samslow.github.io/etc/2019/09/30/BTT-for-mac/ utility etc GraphQL이란 무엇인가 (feat. 무한한 공간 저 너머로) <p>매주 사내 개발자들을 대상으로 하는 세미나에서 GraphQL을 발표한 내용입니다. 기본적인 개념과 어떤 것을 공부 하면 익힐 수 있는지 가벼운 코드와 라이브러리를 소개합니다.</p> <h2 id="graphql">GraphQL</h2> <p><img src="https://www.dropbox.com/s/wlmsfalcdcrc7tp/image-20190724165449802.png?dl=1" alt="image-20190724165449802" /></p> <p>Facebook이 만든 <code class="language-plaintext highlighter-rouge">어플리케이션 레이어 쿼리 언어</code>로 기존의 <code class="language-plaintext highlighter-rouge">REST API</code> 도 편하지만, 쿼리가 복잡해지면 URL이 길어지고 수많은 Endpoint가 생겨 관리가 어려운 단점이 있으므로, 해당 부분을 해결하기 위해 REST를 저격하고 나왔다.</p> <p>2015년에 처음으로 공개적으로 릴리즈 되었고 2016년 10월에 Stable 버전이 처음으로 공개되었다.</p> <p>Wiki의 정의를 보면</p> <ul> <li><a href="https://en.wikipedia.org/wiki/GraphQL">GraphQL</a> is a data query language developed by <a href="https://en.wikipedia.org/wiki/Facebook">Facebook</a> as an alternate to <a href="https://en.wikipedia.org/wiki/REST">REST</a> and ad-hoc <a href="https://en.wikipedia.org/wiki/Webservice">webservice</a> architectures.</li> </ul> <p><del>REST 와 ad-hoc webservice architectures를 대체할 수 있나보다</del> (뭔지 모르지만 뭔가 더 안좋은 거겠거니 하고 같으니 패스)</p> <p>공식 홈페이지를 보면</p> <h3 id="a-query-language-for-your-api">A query language for your API</h3> <p>GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.</p> <p>유레카!</p> <p>이제는 <strong>우리가 원하는 방식대로 Query를 날릴 수 있다</strong></p> <p>언제까지 백엔드가 만들어주는 것만 쓸것이냐</p> <p>이젠 프론트가 직접 찾는다. 그것도 원래도 잘 쓰던 JSON을 활용해서!</p> <p>이해를 돕기 위하여 REST API 와 GraphQL의 차이를 기준으로 설명 해 보겠다.</p> <h3 id="rest-api-vs-graphql">REST API vs GraphQL</h3> <p>REST API는 서버 - 클라이언트 간의 약속 Representational State Transfer 표현에 의한 상태 전달</p> <h4 id="rest-api의-특징">REST API의 특징</h4> <p><img src="https://miro.medium.com/max/294/1*zIHqyyQZ9R_SjoFbFUdu0A.jpeg" alt="RestAPI" /></p> <ul> <li>직관적이고 구현하기 단순</li> <li>브라우저단에서 캐싱이 되고 확장이 용이</li> <li><strong>SOAP(REST 이전의 XMl 기반 통신)</strong> 에 비해 굉장히 가벼운게 장점! <ul> <li>하지만 타입기반 데이터 교환은 Bye~(하지만 GraphQL 너라면..)</li> </ul> </li> <li>Client에서 어떤 데이터가 필요한지 API 설계를 잘 해야한다. <ul> <li>쉽게말해, Backend가 짜준대로만 써야한다.</li> </ul> </li> <li>메뉴얼이 필요하다. <ul> <li>문서 업데이트하는 공수가 또 든다..</li> <li>GraphQL은 DB Schema만 알면, 그게 바로 API문서가 된다.</li> <li>scheme 구조만 알면 Query가 쨘! GraphiQL 활용 <ul> <li><a href="https://lucasconstantino.github.io/graphiql-online/">GraphiQL 예시</a></li> </ul> </li> </ul> </li> <li>정적이다. Static <ul> <li>상황에따라 API를 만들어 줘야 하는 경우가 있을때 상당히 비슷한 API임에도 동적 활용이 어렵다.</li> <li>원하는대로 Query만 바꿔주면 되므로 프론트엔드 개발이 빨라 질 수 있다.</li> </ul> </li> </ul> <h4 id="graphql의-특징">GraphQL의 특징</h4> <p><img src="https://miro.medium.com/max/294/1*PHxksCXy0Wh5ryANEZkfDQ.jpeg" alt="GraphQL" /></p> <ul> <li> <p>원하는 데이터를 그대로 받아 올 수 있음</p> <ul> <li> <p>REST에서 처럼 성능상 문제로 JOIN을 많이하는 API와 덜 하는 API를 나누어 구현할 필요가 없어졌다!</p> </li> <li> <p>Client가 원하는 Depth까지 잘 끊어서 처리 할 수 있게 되었으니까</p> </li> <li> <p>요청의 횟수는 줄이고 응답의 Size는 줄인다!</p> </li> <li> <p>예를 들어, 전화걸어주는 어플이 갖는 엔드포인트는</p> <ul> <li> <p>REST : 세탁소 빨래 가져가세요. 교환 요청받으세요. 경비아저씨 배송이 왔나요 3개의 요청을 따로따로 보내 확인해야 한다.</p> </li> <li> <p>GraphQL: 3개의 요청을 GraphQL Query 하나로 처리 할 수 있다.</p> <p><img src="https://images.velog.io/post-images/jakeseo_me/6a884bb0-6953-11e9-9a35-d1042a1399f2/gq3.png" alt="gq3.png" /></p> <p><img src="https://images.velog.io/post-images/jakeseo_me/b8cfd0e0-6953-11e9-9ecb-354d9d20c9dc/gq4.png" alt="gq4.png" /></p> </li> </ul> </li> </ul> </li> <li> <p>크로스플랫폼 개발에 용이하다.</p> </li> <li> <p><img src="https://www.dropbox.com/s/48nq1iy7o747m36/image-20190724155752870.png?dl=1" alt="image-20190724155752870" /></p> <ul> <li>예를들어, <code class="language-plaintext highlighter-rouge">포스트</code>에 <code class="language-plaintext highlighter-rouge">저자</code> 와 <code class="language-plaintext highlighter-rouge">좋아요</code> 기능이 있을때, 웹에서는 모두 보여주지만 칸이 부족하여 모바일에서는 <code class="language-plaintext highlighter-rouge">좋아요</code> 를 안 보여준다고 가정하였을 때, GraphQL은 요청만 다르게 보내면 되지만 REST는 아예 새로운 코드를 작성하여야 한다.</li> <li><img src="https://images.velog.io/post-images/jakeseo_me/5a4a0a60-6951-11e9-9ecb-354d9d20c9dc/gq2.png" alt="gq2.png" /></li> </ul> </li> <li> <p>Foreign Key 를 이용한 검색 시, 리졸버를 사용하여 다른 데이터의 id만 가지고 바로 검색 가능 (REST도 설계에 따라 가능하지만 설계가 유연하진 않다, 때때로 어떤 요청은 다른 요청의 응답이 온 후에 보내야 하기도 한다.)</p> <ul> <li> <p>아래와 같이 authorid, postid,comment 필드를 가진 Post타입이 있고 CountComment 필드가 없어도 있는 것처럼 흉내 낼 수 있다. → <strong>API 스키마와 DB 스키마가 분리되어 처리된다!!</strong> 장점이라면 장점</p> </li> <li> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Post: { author(post) { return Users.find({ id: post.authorId}) }, commentsCount(post) { return Comments.find({ postId: post.id}).count() } } </code></pre></div> </div> </li> </ul> </li> <li> <p>단점으로는 캐싱과 예외처리등이 어렵다는 점</p> <ul> <li>하지만, 최근 Apollo의 등장으로 <strong>Client, Server 단의 라이브러리, 캐싱 및 쿼리 분석도구</strong>를 제공한다.</li> <li>기본적으로 형제인 React와 스핀오프가 좋다.</li> </ul> </li> <li> <p>타입 기반 API 스키마 작성하여 클라이언트가 타입체크 가능</p> </li> <li> <p>재미로 보는 아이콘 해설</p> <ul> <li> <p><img src="https://t1.daumcdn.net/cfile/tistory/991CC53A5A54399B05" alt="img" /></p> <p><img src="https://t1.daumcdn.net/cfile/tistory/996EF9345A5439A832" alt="img" /></p> <ul> <li>각 점들은 Node이자 Table 이고 이 Table들은 서로간을 Network로 연결한다.</li> <li>다른곳에서 호출 받는 것이 아니라 끼리끼리 호출한다는 의미</li> </ul> </li> </ul> </li> </ul> <h3 id="기존-rest-api-와의-차이점으로-friend--의-정보를-보자">기존 <code class="language-plaintext highlighter-rouge">REST API</code> 와의 차이점으로 <code class="language-plaintext highlighter-rouge">Friend</code> 의 정보를 보자.</h3> <p><code class="language-plaintext highlighter-rouge">Friend</code> 는 id, username, email 등의 기본 정보를 가지고 있다.</p> <ol> <li>1번 유저의 친구 목록을 호출할때 <ul> <li>REST API: <code class="language-plaintext highlighter-rouge">GET /account/1</code></li> <li>GraphQl</li> </ul> </li> </ol> <ul> <li> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">GET</span><span class="w"> </span><span class="err">/graphQL</span><span class="w"> </span><span class="err">query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">account(id:</span><span class="w"> </span><span class="s2">"1"</span><span class="err">)</span><span class="p">{</span><span class="w"> </span><span class="err">username</span><span class="w"> </span><span class="err">email</span><span class="w"> </span><span class="err">firstName</span><span class="w"> </span><span class="err">lastName</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div> </div> </li> </ul> <ol> <li> <p>서비스의 요구사항이 바뀌어서 1번 유저의 친구들의 목록 까지도 봐야 할 때</p> <ul> <li> <p>REST API: <code class="language-plaintext highlighter-rouge">GET /account/1/?friends_detail=username, firstname</code></p> </li> <li> <p>GraphQL</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">GET</span><span class="w"> </span><span class="err">/graphQL</span><span class="w"> </span><span class="err">query</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">account(id:</span><span class="w"> </span><span class="s2">"1"</span><span class="err">)</span><span class="p">{</span><span class="w"> </span><span class="err">username</span><span class="w"> </span><span class="err">email</span><span class="w"> </span><span class="err">firstName</span><span class="w"> </span><span class="err">lastName</span><span class="w"> </span><span class="err">friends</span><span class="p">{</span><span class="w"> </span><span class="err">firstName</span><span class="w"> </span><span class="err">userName</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div> </div> </li> </ul> </li> <li> <p>친구를 추가해야 할때!</p> <ul> <li> <p>REST API: <code class="language-plaintext highlighter-rouge">POST /account/1/edit_friend/?username="samslow"&amp;email="naver··· </code></p> </li> <li> <p>GraphQL</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">//</span><span class="w"> </span><span class="err">GET</span><span class="w"> </span><span class="err">/graphQL</span><span class="w"> </span><span class="err">mutaion</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">createAccount(</span><span class="w"> </span><span class="err">userName:</span><span class="w"> </span><span class="s2">"samslow"</span><span class="w"> </span><span class="err">email:</span><span class="w"> </span><span class="s2">"naver.com"</span><span class="w"> </span><span class="err">firstName:</span><span class="w"> </span><span class="s2">"Hyeonseok"</span><span class="w"> </span><span class="err">lastName:</span><span class="w"> </span><span class="s2">"Seo"</span><span class="w"> </span><span class="err">)</span><span class="w"> </span><span class="p">}{</span><span class="w"> </span><span class="err">username</span><span class="w"> </span><span class="err">email</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div> </div> </li> </ul> </li> </ol> <h2 id="외부-라이브러리---prisma">외부 라이브러리 - Prisma</h2> <p>Prisma는 DB 프록시이다. GraphQL 스키마를 기반으로 DB를 자동으로 생성해준다.</p> <p>GraphQL 스키마 자체가 엄격한 타입기반으로 구성되어 있고 DB를 구현하기에 충분한 데이터가 있기 때문이다. MySql, Postgresql등의 스키마 및 쿼리를 자동으로 생성해준다. 사용자는 GraphQL 스키마만 설계하면 되고 DB를 설계할 필요가 없다.</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>type Character { name: String! appearsIn: [Episode]! }type Query { hero(episode: Episode): Character droid(id: ID!): Droid } </code></pre></div></div> <p>**→ 여기에선 자세히 다루지 않지만, <code class="language-plaintext highlighter-rouge">RubyOnRails</code> 의 ActiveRecord 처럼 간편하게 쓸 수 있는 인터페이스로 이해하고 넘어가면 된다. 나중에 자세히 다뤄보겠다. **</p> <h2 id="하지만-그렇다고-모두-대체되는-것일까">하지만 그렇다고 모두 대체되는 것일까?</h2> <p>정답은 NoNo, 한번 예측 해 보시라.아래의 경우를 보자.</p> <ul> <li>외부로 오픈되는 API일 경우</li> <li>자주 쓰이는 Request의 경우 REST로 만들어놔도 좋다.</li> <li>etc</li> </ul> <h2 id="그럼-여기서-해결하지-못한-질문들">그럼 여기서 해결하지 못한 질문들</h2> <ul> <li> <h5 id="graphql은-라이브러리다">GraphQL은 라이브러리다?</h5> </li> </ul> <p>누군가는 DB라고도하고, 누군가는 라이브러리라고도 하는데, 정확한 정의는 새로운 스펙이다.</p> <p>마치 RESTful한 것이 라이브러리가 아닌 것처럼. 하지만 오해할 여지는 있는것이. 많은 언어에서 GraphQL 구현체가 이미 존재하기 때문에 해당 부분을 가져다 쓰다보니 생길 수 있는 질문.</p> <ul> <li> <h5 id="기존-엔드포인트에-graphql을-적용-할-수-있다-">기존 엔드포인트에 GraphQL을 적용 할 수 있다 ?</h5> </li> </ul> <p>제가 바로 그렇게 해보려고 했습니다.. 스프링의 대한 이해부족과 시간제약때문에 실패하긴 했지만 불가능하지는 않다고 합니다. 하지만, GraphQL 은 단일 엔드포인트를 권장하기 때문에 REST가 URL과 Resource를 매칭시키는 개념적 모델을 사용했기 때문에 수많은 엔드포인트가 있었다면, GraphQL은 모든 리소스가 그래프처럼 서로 연결되어있기 때문에 URL을 분리 할 필요가 없다. 서버는 Query와 Mutaion(명령어)만 제공하면 된다.</p> <p><strong>리소스를 url이 아니라 Query로 표현하기 때문에 GraphQL은 창구개념의 하나의 엔드포인트 면 충분하다.</strong></p> <ul> <li> <h5 id="rest를-잘-쓰고-있는데-graphql로-가야-할-이유가-있나">REST를 잘 쓰고 있는데 GraphQL로 가야 할 이유가 있나?</h5> <ul> <li>유연성의 증대와 시스템의 단순화가 목적인 GraphQL → 하나의 엔드포인드만 사용하여도 다양한 쿼리로 정보 획득 가능 → API 만드는 시간 감소 → 개발 시간의 증대</li> <li>타입체크를 클라이언트에서 하여 실수를 막아 줄 수도 있음</li> <li>전달될 데이터 양을 줄이고 받아올 데이터 양도 필요한 만큼만 받아온다 → 과유불급</li> <li>다양한 디자인패턴 적용하기가 수월하다. <ul> <li>컴포짓 패턴으로 다양한 소스에서 데이터 한데 모아 볼때 <ul> <li><img src="https://www.dropbox.com/s/gwr1dfs2v49g3sm/image-20190725005546670.png?dl=1" alt="image-20190725005546670" /></li> </ul> </li> <li>프록시 패턴으로 레거시 시스템에 인증을 추가할때(Resolver 사용하는 방법) <ul> <li><img src="https://www.dropbox.com/s/vv8r4yfev9pup3u/image-20190725005658089.png?dl=1" alt="image-20190725005658089" /></li> </ul> </li> </ul> </li> </ul> </li> <li>이외 다양한 디자인패턴으로 사용 가능하다</li> </ul> <h2 id="graphql을-공부-한-후기">GraphQL을 공부 한 후기</h2> <p>처음에 아무것도 모르고 시작했는데 생각보다 자료가 많아서 어떤걸 배워야할지 또 개념은 뭔지에 대해 쉽게 접근 할 수 있었던 것같다.</p> <p>처음에 왜 <code class="language-plaintext highlighter-rouge">무한한 공간 저 너머로</code> 라고 한 이유를 이제는 유추 할 수 있겠는가? 단 하나의 엔드포인트로 어디든지 갈 수 있는 그 장점이 내게는 버즈라이트이어의 한마디처럼 들린 것이다.</p> <p>앞으로 모든 Router 들이 GraphQL 로 바뀌진 않겠지만, 서비스 규모가 커져 EndPoint가 많아지면 관리하는 문서가 많아지거나 정적인 여러 단점때문에 장점이 확실한 GraphQL로 바뀔 것 같다.</p> <p>어떤 기술스택에도 붙을 수 있다는 장점 외에도 수많은 장점들이 있기 때문에 역으로 장점이 단점이 되는 기술 스펙이 될 수도 있다. 하지만 프론트엔드를 사랑하는 개발자로서 GraphQL은 개발에 날개를 달아줄 기술임은 분명하다.</p> <p>기회가 된다면 본문에 소개한 <code class="language-plaintext highlighter-rouge">prisma</code>나 <code class="language-plaintext highlighter-rouge">Apollo</code>에 대해서도 포스팅 해보려고 한다.</p> <p>그리고, 이 글을 쓰고나서 다른 개발자분이 <a href="https://dev.classmethod.jp/cloud/aws/aws-appsync-re-introduction-2019-korean-ver/?fbclid=IwAR2KRZ778M1UuWHe30FR3oliZpHIlQTIkvBJf7XkkJ5cRBapIXA_rVRA_tg">GraphQL을 AWS AppSync에서 쓰는 법</a>을 공유 해 주었는데 <code class="language-plaintext highlighter-rouge">GraphQL 서버리스</code> 개념으로 사용 할 수 있는 방법 인듯 하여 한번 따라 해 보면 좋을 것 같다.</p> <h3 id="오늘-내용을-작성한-키노트-파일도-slideshare에-깔끔하게-정리해서-이쁘게-올려-놓았다">오늘 내용을 작성한 키노트 파일도 <a href="https://www.slideshare.net/HyeonseokSeo1/graphql-overview-and-practice-159382659">SlideShare</a>에 깔끔하게 정리해서 (이쁘게) 올려 놓았다.</h3> <ul> <li>팀장님曰 꾸미는건 좋은데 사내 세미나니 안꾸미는게 낫지 않으냐고 하셨지만 꾸미는게 좋은걸 어떻게해… 다음엔 꾸안꾸st로 해보겠읍니다..</li> </ul> <p><img src="https://www.dropbox.com/s/p1cndsgbo4keiwg/image-20190731110634070.png?dl=1" alt="image-20190731110634070" /></p> <hr /> <h3 id="reference">Reference</h3> <ul> <li><a href="https://medium.com/@kiyeopyang/가장-현대적인-웹을-만들자-3편-graphql-cb69ce1a64b5">가장 현대적인 웹을 만들자 3편 (GraphQL))</a></li> <li><a href="graphql.org">GraphQL 공식 홈페이지</a></li> <li><a href="https://medium.com/@FourwingsY/graphql을-오해하다-3216f404134">GraphQL을 오해하다</a></li> <li><a href="https://medium.com/@JeffLombardJr/when-and-why-to-use-graphql-24f6bce4839d">when-and-why-to-use-graphql</a></li> <li><a href="https://velopert.com/2318">GraphQL이 무엇인가</a></li> </ul> Wed, 31 Jul 2019 00:00:00 +0000 https://samslow.github.io/development/2019/07/31/What-is-GraphQL/ https://samslow.github.io/development/2019/07/31/What-is-GraphQL/ web development 2019 AngelHack 엔젤핵 후기 <p>2019년 6월 1일~2일 진행한 <a href="https://event-us.kr/angelhackseoul/event/7465">2019 AngelHackathon Seoul</a> 엔젤핵 해커톤 참여 후기입니다.</p> <h2 id="algelhackathon-엔젤핵">Algelhackathon 엔젤핵</h2> <p>엔젤해커톤 서울 2019는 하나의 앱, 하나의 서비스로 여러가지의 챌린지에 도전해서, 이 각각의 챌린지들을 만족시키는 팀에게 챌린지 별 상품/상금을 수여하는 멀티 챌린지 방식의 글로벌 해커톤입니다.</p> <p><img src="https://eventusstorage.blob.core.windows.net/evs/Image/angelhackseoul/7465/ProjectInfo/bad24412eaf04c1fb6b40cd6d04c0c8a.png" /></p> <p>수상으로는 엔젤핵상, IBM 상, AWS 상, LG U+ 상이 있었는데 사실 다른곳은 소정의 상금과 기념품이 전부였지만, 이 해커톤의 메리트이자 최대 수상액인 <code class="language-plaintext highlighter-rouge">엔젤핵상</code> 은 팀 전원이 미국 샌프란시스코에서 열리는 <a href="https://medium.com/angelhack/meet-the-hackcelerators-37-new-startups-dbaeede057ad">엔젤핵 헥셀러레이터 프로그램</a> 에 초대되며 모든 경비가 지원됩니다.</p> <p>이벤터스에서 신청을 받아 약 120명의 인원이 22개의 팀을 이루어 무박 2일동안 진행되었습니다.</p> <p>행사 기간동안 카톡이 아닌 <code class="language-plaintext highlighter-rouge">Slack</code> 으로 정보와 공지를 받을 수 있었고 우리 팀은 아예 Private Channel을 해당 Slack에 만들어 소통했습니다.</p> <h2 id="우리팀은">우리팀은?</h2> <p><img src="https://www.dropbox.com/s/13hjyrd753nuwmu/ImgTextLogo.png?dl=1" style="width:300px;" /></p> <p><img src="https://www.dropbox.com/s/47fwa1asbz3ry3b/messages1.png?dl=1" /></p> <p>저희 팀의 아이디어는 <code class="language-plaintext highlighter-rouge">쉽게 작성하는 유언 작성 서비스 GlassWill</code> 입니다.</p> <p>팀명은 ‘무소식이 희소식이다’ 였는데 유언은 안 들리는게 좋은 거라고 생각해서 지어진 이름입니다.</p> <p>아래는 <a href="https://github.com/samslow/GlassLetter-Front">팀 깃헙</a> 에 있는 간단한 소개입니다.</p> <div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code>여러분은 언제 죽음을 생각하시나요? 그리고 얼마나 준비하고 계신가요? <span class="sb">`가는데는 순서 없다`</span> 라는 말처럼 누구든 어느날 갑자기 죽음을 맞이 할 수 있습니다. 하지만, 유언을 작성하는 법을 아시나요? 어떻게 작성하는지 알아도, 첫 삽을 뜨는 것은 누구에게나 어렵습니다. GlassWill은 '유언은 어려운 것이다'라는 기존의 유언문화에서 벗어나 누구든 간단하게 작성 할 수 있는 1분 유언 서비스 입니다. 유언을 쉽고 빠르게 작성해보세요. 어쩌면 오늘이 당신의 인생을 되돌아 보고 동기부여를 할 수 있는 역사적인 하루가 될 지도 모르죠. <span class="p"> -</span> 팀 이름: 무소식이 희소식 <span class="p">-</span> 서비스 요약: 1분안에 간단하게 쓰는 디지털 유언 <span class="p">-</span> 왜 이 서비스? <span class="p"> -</span> 젊은 사람들은 죽음을 미리 준비하지 않는다. <span class="p"> -</span> 하지만 화산, 지진, 인재로 갑자기 내가 죽는다면? <span class="p"> -</span> 살아서 미리 만드는 남아있는 사람을 위한 글 <span class="p">-</span> 서비스 기능 <span class="p"> 1.</span> 유언을 쉽게 작성하도록 해줌 <span class="p"> 2.</span> 일정 주기마다 유언을 카톡 플친, 메일등으로 보내줌(<span class="sb">`아직 살아있다!`</span>표시 하지않으면 내가 설정한 사람들에게 연락이 감) </code></pre></div></div> <h2 id="엔젤핵-1일차61">엔젤핵 1일차(6/1)</h2> <p>그동안 참여했던 해커톤은 첫째 날은 오후 1시쯤 모여 다음날 오전 10시면 끝나는 해커톤 이었는데, 엔젤핵은 아침 10시에 모여서 다음날 오후 5시까지 진행했습니다.</p> <p>행사 장소는 <a href="https://maru180.com/">역삼역 마루180</a>이었고, 집에서는 40분 정도면 가는 거리였지만 전날 잠을 설치는 바람에 30분 정도 늦게 도착했습니다.</p> <p><img src="https://www.dropbox.com/s/z8ylqsoyf0215ln/%EC%97%94%EC%A0%A4%ED%95%B5%EA%B5%BF%EC%A6%880.jpeg?dl=1" /></p> <p>굵직한 후원사 외에 17여개의 후원사들이 있었는데, 도착하자마자 티셔츠와 에코백에 모두 적혀있어서 확인 할 수 있었습니다.</p> <p><img src="https://www.dropbox.com/s/0l4imeba3skrf2d/%EC%97%94%EC%A0%A4%ED%95%B5%EA%B5%BF%EC%A6%881.jpeg?dl=1" /></p> <p>앞면은 이렇게 생겼습니다. 당연히 엔젤핵 트레이드 마크인 왕관을 했을 것 같은데 저 짹짹이는 아직도 뭔지 모르겠습니다.</p> <p>가벼운 개회식과 함께 팀빌딩이 시작. 70% 사람들은 이미 시작 전에 팀을 이뤄서 온 것 같았고, 30%정도가 팀을 구하는 것 같았습니다.</p> <p>우리팀은 프론트인 저와 백엔드 개발자 그리고 디자이너 한명으로 구성되어 있었고 팀 제한인원이 5명 이기에 2명을 더 모셔야 하는 상황이어서 기획자 한 명과 프론트엔드 개발자를 추가로 영입했습니다.</p> <p>팀빌딩 이후에는 개발과 함께 스폰서들의 브레이크아웃 세션이 진행되었습니다. 후원을 받는 해커톤 이다 보니 이런 시간을 주는 것은 이해를 하지만 너무 길고 지루하며 몇몇 후원사를 제외하고는 유익한 내용이 없어서 힘든 기억이 남네요.</p> <p>각 후원사마다 30분씩을 했는데, 예고된 4개의 후원사 외에도 몇 군데가 더 해서 시간으로만 따지면 3시간정도를 후원사들의 스피치를 들어야했고, 우리팀을 포함해 반정도의 팀들이 행사장을 이탈해 다른 곳에서 회의와 개발을 진행 한 것 같습니다.</p> <p>다음에는 이런 시간이 길어야 한 세션에 10분 정도였으면 하는 바램.</p> <p><img src="https://www.dropbox.com/s/8odyc193oaugvdb/food.jpg?dl=1" /></p> <p>시작시간인 아침 10시부터 밥을 주기 시작해서 야식포함 총 6끼 정도가 나왔습니다. 보통의 해커톤이 3끼정도를 주는 것에 비하면 잘 챙겨 주는 편.</p> <p>메뉴도 밥과 가벼운 샐러드를 번갈아 줘서 해커톤이 끝나고는 피곤한 것 빼면 몸은 건강 해지는 기분일 정도.</p> <p><img src="https://www.dropbox.com/s/ivxqr9qpbq5fmnu/tip-for-pitch.png?dl=1" /></p> <p>엔젤핵은 개발시간은 다른 해커톤에 비해 길지만, 발표시간은 2분 QnA는 1분으로 다른 해커톤에 비해 굉장히 짧았습니다.</p> <p>이런 이유로 저녁 8시에는 행사 오거나이저 인 Jonas의 피칭을 위한 팁 세미나가 있었습니다. 짧은 시간에 어떻게 하면 빠르게 의견 전달을 할 수 있는가를 주제로 세션을 가졌습니다.</p> <p>내용 자체가 정말 유익했고 발표자 Jonas의 텐션은 처음부터 고공행진이라 이 발표때도 미친듯이 폭발했는데 그에 반해 대중은 텐션을 따라 갈 수 없었습니다..ㅋㅋ</p> <p>사람들의 반응이 없는데도 불구하고 행사가 끝날 때까지 더 올라가는 그의 텐션은 제가 만나본 그 어떤 사람보다도 대단한 진행자라고 생각합니다.</p> <p>밤 11시에는 Dance Break가 있었는데 정말 춤을 잘 추는 안무가 분이 오셔서 가볍게 머리를 푸는 시간이 있었지만, 우리팀은 개발하느라 저만 살짝 가서 훔쳐보고 왔습니다.</p> <p>여유가 된다면 꼭 참여 해 보고 싶은 시간이었던 듯!</p> <h2 id="엔젤핵-2일차62">엔젤핵 2일차(6/2)</h2> <p>밤을 꼬박 새며 한 2시간 정도를 선잠을 자고 아침이 되었습니다.</p> <p>Code Freeze가 오후 1시에 되고, 발표하고 시상까지 하니 5시 가량이 되었습니다.</p> <p><img src="https://www.dropbox.com/s/3tz3tpy63ij2wr5/pitch-list.jpg?dl=1" /></p> <p>처음에 발표는 다른 사람이 하기로 했지만, 프로젝트를 진행하다 보니 아이디어를 가장 잘 이해하고 있는 사람이 저인거 같다는 여러 의견을 수용해 발표를 제가 맡게 되었습니다.</p> <p>순서는 14번째로 나쁘지 않은 순번이었습니다.</p> <p>VC투자사와 IBM, AWS, LG U+,야놀자 등의 총 8명의 심사위원들 앞에서 결과물 피칭을 2분안에 하려니 여간 어려운 것이 아니더라구요.. 심지어 시연 영상에서만 거의 1분이니 제가 순수하게 말을 할 수 있는 시간은 정말 얼마 안 됐습니다.</p> <p>덕분에 저의 피칭 능력을 되돌아보고, 또 평가 받을 수 있는 자리이기도 했습니다.</p> <p>발표를 짧은 시간안에 잘 하는 것은 엄청난 능력이라는 것을 깨달았네요.</p> <p>대망의 엔젤핵 Prize를 가져간 팀은 <a href="https://www.hackathon.io/connectus3">커넥터스</a> 였습니다.</p> <p>재난상황시 인터넷이 안 될때 커넥터스를 사용하는 사람들 끼리는 핫스팟이 자동으로 활성화 되어 모든 사람들을 연결하여 인터넷이 되는 사람이 한 명이라도 있다면 모두가 인터넷이 되게 한다는 아이디어 였습니다.</p> <p>이 팀의 팀장과는 예전에 다른 해커톤에서 친해진 분이라 이야기를 들어보니</p> <p>지난 <a href="https://ko.wikipedia.org/wiki/KT_아현지사_통신구_화재">KT 아현지사 화재</a>에서 영감을 얻어 다른 통신사는 되는 상황에서 이 서비스를 사용하면 딱이지 않을까 해서 낸 아이디어라고 합니다.</p> <h2 id="그래서-엔젤핵-후기는">그래서 엔젤핵 후기는?</h2> <ul> <li> <p>개인적으로는 프론트엔드를 React로 개발해 결과물을 내자는 목표가 있었습니다. 공부를 시작 한지는 얼마 안 되었지만, 어디서 나온 자신감으로 하겠다고 했는지 모르겠네요. 결과적으론는 Next에 AntD까지 깨작깨작 쓰면서 완성을 하긴 했습니다!</p> <ul> <li>혹시나 궁금하신 분들은 <a href="https://github.com/samslow/GlassLetter-Front">GlassWill GitHub</a> 에서 구경 해 보시길 바랍니다.</li> </ul> </li> <li>수상하지는 못 했지만, 쟁쟁한 후보들 속에서 크고작은것들을 배웠다. <ul> <li>영어를 열심히 공부하자, 발표와 질답을 영어와 한국어 자유롭게 왔다갔다 하는 발표자가 너무 멋있었다.</li> <li>React를 진짜 열심히 공부해야곘다. 잘하는 사람 많더라</li> </ul> </li> <li>Next는 React를 사용한다면 대부분 가져가야할듯하다. 물론 실무에서 <ul> <li>해커톤에서는 React-router가 직관적이고 협업에 용이한 듯</li> </ul> </li> <li>더이상 해커톤은 당일날 와서 아이디어 회의부터 해서 만드는 것이 아니다. <ul> <li>대부분의 팀들이 사전에 팀이 구성 되어 있었고, 아이디어 또한 이미 나온 상태 심지어 어느정도 진행을 한 상태로 온 팀도 있는 것 같았다.</li> <li>요새 대부분 해커톤이 이런 식으로 하는 듯</li> </ul> </li> <li>누군가 “정말 많이 배웠다”라는 말은 누군가는 정말 많이 가르쳐 줬다는 말이다. 팀워크에 감사하고, 역시 공부해야한다.</li> <li>밥은 전반적으로 만족했다. 샐러드를 주는 해커톤은 질이 정말 좋은 해커톤이다!</li> <li>후원사 세션을 아무리 길어도 10분만 했으면 좋겠다. 개발을 하라는 건지 홍보를 들으러 온 건지 모르겠다.</li> <li>운영을 맡은 Jonas같은 텐션을 갖고 싶다. <ul> <li>누군가 듣던 말던 소리지를 수 있고, 춤추며 좌중을 휘어잡을 수 있는 능력</li> </ul> </li> <li>밤샘은 언제나 힘들다. 체력은 곧 국력!</li> </ul> <h2 id="결론">결론</h2> <p>해커톤을 준비해 주신 분들, 후원해준 후원사들 정말 감사드립니다.</p> <p>해커톤 오거나이징을 해 본 입장에서 얼마나 힘든 일인지 잘 알기에 더 즐겁고 편한 해커톤 이었던 것 같습니다.</p> <p>같이 참여한 참가자 분들도 너무 쟁쟁한 분들이라 마음 같아선 모든 분들을 만나 이야기를 듣고 싶을 만큼 유익한 자리였습니다.</p> <p>수상한 분들 다시한번 축하드리고 그렇지 못한 분들이라도 다들 좋은 경험 얻어가셨을 거라 믿습니다.</p> <p>이 글이 도움이 되셨다면 댓글을 남겨주세요. 작성자에게 힘이 됩니다.</p> <p>감사합니다.</p> Tue, 04 Jun 2019 00:00:00 +0000 https://samslow.github.io/diary/2019/06/04/2019-AngelHack-review/ https://samslow.github.io/diary/2019/06/04/2019-AngelHack-review/ hackathon diary Rails 에서 JS와 CSS의 로딩은 어떻게 하나 <p>Today I Learn의 Rails 시리즈로써 Asset pipeline에 대해 다룹니다.</p> <h2 id="asset-pipeline">Asset pipeline</h2> <h3 id="rails-에서-js와-css의-로딩은-어떻게-하나">Rails 에서 JS와 CSS의 로딩은 어떻게 하나</h3> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;head&gt;</span> <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">type=</span><span class="s">"text/css"</span> <span class="na">href=</span><span class="s">"mycss.css"</span><span class="nt">&gt;</span> <span class="nt">&lt;/head&gt;</span> </code></pre></div></div> <p>assets(Javascript, css)는 기본적으로 위와같이 HTML 문서에서 <code class="language-plaintext highlighter-rouge">&lt;Head&gt;</code> 안에서 호출된다.</p> <p>이 방법은 보기에 직관적이라 어디있는지 바로 알 수 있는 장점이 있지만,</p> <p>조금만 프로젝트가 커져 여러 js, css를 가져와야할때 코드의 길이가 길어져 관리가 힘들어지는 단점이 있다.</p> <p><del>한마디로 머리가 드럽게 큰 가분수형 인간이된다(?)</del></p> <p>하지만 Rails의 경우 Assets 폴더를 별도로 관리하여 아래와 같이 단 두줄에 CSS와 JS를 가져온다.</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- views/layouts/application.html.erb --&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;title&gt;</span>my service<span class="nt">&lt;/title&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">csrf_meta_tags</span> <span class="err">%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">stylesheet_link_tag</span> <span class="err">'</span><span class="na">application</span><span class="err">',</span> <span class="na">media:</span> <span class="err">'</span><span class="na">all</span><span class="err">'%</span><span class="nt">&gt;</span> <span class="nt">&lt;</span><span class="err">%=</span> <span class="na">javascript_include_tag</span> <span class="err">'</span><span class="na">application</span><span class="err">'%</span><span class="nt">&gt;</span> <span class="nt">&lt;/head&gt;</span> </code></pre></div></div> <p>위 코드를 부연설명 하자면 <code class="language-plaintext highlighter-rouge">app/assets</code>에서 stylesheets 와 javscripts 폴더내의 <code class="language-plaintext highlighter-rouge">application</code>이라는 <code class="language-plaintext highlighter-rouge">manifest</code>파일을 호출하는것이다.</p> <p>수십줄의 반복적인 CSS link 태그대신 잘 정돈된 <code class="language-plaintext highlighter-rouge">manifest</code> 파일만 호출한다면 깔끔하게 정리된 수납함 하나만 가져오면 된다.</p> <p><u>이와같이 DRY하게 최소화 하는 과정을 **asset pipeline** 이라 한다.</u></p> <p>레일즈에서는 기본적으로 asset pipeline으로 asset을 가져오며 이방법이 아닌 <code class="language-plaintext highlighter-rouge">&lt;link&gt;</code> 태그로 사용하고 싶다면</p> <p><code class="language-plaintext highlighter-rouge">config/application.rb</code> 에서 <code class="language-plaintext highlighter-rouge">config.assets.enabled</code>를 <code class="language-plaintext highlighter-rouge">false</code>로 추가해주면 된다.</p> <h3 id="그렇다면-manifest-파일은-무엇인가">그렇다면 manifest 파일은 무엇인가?</h3> <p>OS나 Android를 해 본 사람이면 쉽게 이해 할 수 있을텐데, 위에서도 한번 언급했지만 간단히말하면 <code class="language-plaintext highlighter-rouge">잘 정돈된 수납함</code> 으로 이해 할 수 있다.</p> <p>양말을 정리하는 상황이라고 해보자.</p> <p>수납함이 긴양말, 짧은 양말, 덧신 등으로 ‘잘’ 나누어져 있으면 여름이던 겨울이던 바로 제 위치에서 찾아 갈 수 있다.</p> <p>이렇게 잘 찾아가도록 한꺼번에 관리하며 모아놓은 파일이 <code class="language-plaintext highlighter-rouge">manifest</code>이다.</p> <p>다양한 분야에서 자주 등장하는 용어이니 이번 기회에 알아가도록 하자.</p> <p>위와같이 HTML 파일이 선언되었다면 이제 <code class="language-plaintext highlighter-rouge">app/assets/stylesheets</code> 를 가보자</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="sr">/* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/</span><span class="n">assets</span><span class="o">/</span><span class="n">stylesheets</span><span class="p">,</span> <span class="n">or</span> <span class="n">any</span> <span class="n">plugin</span><span class="s1">'s * vendor/assets/stylesheets directory can be referenced here using a relative path. * * You'</span><span class="n">re</span> <span class="n">free</span> <span class="n">to</span> <span class="n">add</span> <span class="n">application</span><span class="o">-</span><span class="n">wide</span> <span class="n">styles</span> <span class="n">to</span> <span class="n">this</span> <span class="n">file</span> <span class="n">and</span> <span class="n">they</span><span class="err">'</span><span class="n">ll</span> <span class="n">appear</span> <span class="n">at</span> <span class="n">the</span> <span class="n">bottom</span> <span class="n">of</span> <span class="n">the</span> <span class="o">*</span> <span class="n">compiled</span> <span class="n">file</span> <span class="n">so</span> <span class="n">the</span> <span class="n">styles</span> <span class="n">you</span> <span class="n">add</span> <span class="n">here</span> <span class="n">take</span> <span class="n">precedence</span> <span class="n">over</span> <span class="n">styles</span> <span class="n">defined</span> <span class="k">in</span> <span class="n">any</span> <span class="n">other</span> <span class="no">CSS</span><span class="o">/</span><span class="no">SCSS</span> <span class="o">*</span> <span class="n">files</span> <span class="k">in</span> <span class="n">this</span> <span class="n">directory</span><span class="o">.</span> <span class="no">Styles</span> <span class="k">in</span> <span class="n">this</span> <span class="n">file</span> <span class="n">should</span> <span class="n">be</span> <span class="n">added</span> <span class="n">after</span> <span class="n">the</span> <span class="n">last</span> <span class="n">require_</span><span class="o">*</span> <span class="n">statement</span><span class="p">.</span> <span class="nf">*</span> <span class="no">It</span> <span class="n">is</span> <span class="n">generally</span> <span class="n">better</span> <span class="n">to</span> <span class="n">create</span> <span class="n">a</span> <span class="n">new</span> <span class="n">file</span> <span class="n">per</span> <span class="n">style</span> <span class="n">scope</span><span class="p">.</span> <span class="nf">*</span> <span class="o">*=</span> <span class="n">require_tree</span> <span class="p">.</span> <span class="nf">*</span><span class="o">=</span> <span class="n">require_self</span> <span class="o">*</span><span class="sr">/ </span></code></pre></div></div> <p>처음 위의 파일을 접했을때 모두 주석처리 되어있길래 별로 중요하지 않은가보다라고 생각했지만 일부는 맞고 일부는 아니다.</p> <p>위의 텍스트들은 해당 파일에 대한 주석 설명이 맞지만</p> <p>코드 하단에 위치한 <code class="language-plaintext highlighter-rouge">*= require_tree . </code> 처럼 표기되는 부분들은 주석이 아니라 위의 예시에서의 <code class="language-plaintext highlighter-rouge">양말들</code> 이 되는 것이다.</p> <p>양말을 하나 추가 할 때마다 <code class="language-plaintext highlighter-rouge">*= require socks.css</code> 로 추가해 주면 전체 manifest에 반영이 되는 것이다.</p> <p>이는 코드를 간결하게 보여지게 할 뿐만 아니라, 실제로 미리 컴파일하고 압축하는 과정을 통해 request를 최소화 하게 되어 서버에 부담도 덜게 된다.</p> <h3 id="directives">Directives</h3> <p>그럼 manifest에서 파일을 가져오는 다양한 방법에 대해 더 알아보자</p> <p><code class="language-plaintext highlighter-rouge">*=</code> 뒤에 오는 명령어(?) 들을 <code class="language-plaintext highlighter-rouge">Directives</code> 라고 하는데 이는 코드를 더 짧고 간단하게 보이게 해 준다.</p> <p>제일 많이 사용되는 것은 <code class="language-plaintext highlighter-rouge">require</code>와 <code class="language-plaintext highlighter-rouge">require_tree</code> 이다.</p> <p>후자의 경우 경로를 설정 해 주면 해당 경로의 assets을 모두 가져오기 때문에 나열하지 않아도 되는 장점이 있지만</p> <p>반대로 모두 가져오기 때문에폴더구조가 복잡 해 질 수 있다.</p> <p>사실 이 모든것들을 더욱 최소화하기위해(최소화의 끝은 어디인가) <code class="language-plaintext highlighter-rouge">.css</code> 대신 <code class="language-plaintext highlighter-rouge">.scss</code> 의 <code class="language-plaintext highlighter-rouge">@import "00/0"</code> 를 사용한다고 한다.</p> <p>다음에는 이에 대해서도 다뤄보겠다. 이번 포스트에서는 아래에 다른 Directives를 소개하며 마무리한다.</p> <p>본인의 사용 용도에 맞게 다양한 directive를 사용하면 된다.</p> <p><strong>require</strong> <strong><em>[path]</em></strong> 는 경로상에 있는 asset 소스파일의 내용을 삽입해 준다. 동일한 소스파일을 여러번 require해도 컴파일시 한번만 포함</p> <p><strong>include</strong> <strong><em>[path]</em></strong> 는 require와 같은 기능을 가지지만, 해당 소스파일이 이미 include 되었거나 require 되었더라도 소소파일의 내용을 삽입해 주게 된다.</p> <p><strong>require_directory</strong> <strong><em>[path]</em></strong> 는 경로로 지정된 디렉토리에 있는 동일한 포맷의 모든 소스파일을 require해 준다. 이 때 파일들은 알파벳순으로 require된다.</p> <p><strong>require_tree</strong> <strong><em>[path]</em></strong> 는 require_directory와 같은 기능을 가지지만, 경로로 지정된 디렉토리의 모든 하위디렉토리들에 있는 파일들까지 모두 require되도록 한다.</p> <p><strong>require_self</strong>는 모든 require 또는 include directives 전에서 사용하여 현재 소스파일의 내용을 삽입하도록 한다.</p> <p><strong>depend_on</strong> <strong><em>[path]</em></strong> 는 경로상의 소스파일의 내용을 포함하지 않은 채로 의존성만 선언한다. 다른 파일이 수정될 때 asset 캐시를 expire할 필요가 있을 때 유용하게 사용할 수 있다.</p> <p><strong>stub</strong> <strong><em>[path]</em></strong> 는 경로상의 소스파일을 제외하도록 해준다. 경로는 하나의 유효한 asset 이어야 하고 이미 포함되었거나 포함되지 않을 수 있다. 일단 해당 소스파일이 제외되면, 블랙리스트에 등록되어 추후 require 하더라도 불러올 수 없게 된다.</p> <p>Directives와 Asset pipeline에 대한 더 자세한 정보는 https://rorlab.org/rblogs/152 를 참조하면 된다.</p> Tue, 19 Feb 2019 00:00:00 +0000 https://samslow.github.io/development/2019/02/19/Where-be-started-js-and-css/ https://samslow.github.io/development/2019/02/19/Where-be-started-js-and-css/ rails development 식품안전나라 크롤링 가이드 <p>API를 개떡같이 제공해줘서 <code class="language-plaintext highlighter-rouge">Ruby on Rails</code> <code class="language-plaintext highlighter-rouge">Nokogiri</code>로 직접 크롤링하는 삽질을 쓰는 글</p> <h1 id="식품안전나라-크롤링">식품안전나라 크롤링</h1> <p>우리나라에서 식품정보를 얻을 수 있는 방법을 시도 해 본 개발자라면, <a href="https://www.data.go.kr/">공공데이터포털</a>을 한번 쯤 들어 봤을 것이다.</p> <p>사용 용도에 따라 음식의 이름만 사용하기도 하고, 레시피를 쓰기도 하지만 필자는 영양소 정보가 필요했다.</p> <p>공공 API에서 제공해주는 정보는 <code class="language-plaintext highlighter-rouge">'탄수화물', '단백질', '지방'</code> 외의 기본 영양소 9종을 제공해준다.</p> <p>따라서 처음에 필자는 이 정보만을 사용했지만 팀원으로부터 <code class="language-plaintext highlighter-rouge">추가 영양소</code> 가 있는 곳을 찾았다는 제보를 받았다.</p> <p>하지만, 놀랍게도 그곳은 같은 DB를 사용하는 <code class="language-plaintext highlighter-rouge">식품안전나라</code> 였고, 아직도 왜 이곳만 영양소 정보가 더 있는지는 모른다.</p> <p>현재까지는 영양소정보를 34종까지 제공 해주는것을 봤다. (다른 음식엔 더 많은 영양소가 있을 수도 있다)</p> <p><img src="/assets/post_img/image-20190211083747629.png" /></p> <p>필자의 서비스는 이런 영양소 정보가 많을수록 자세한 통계정보를 제공 해 줄 수 있기 때문에 해당 DB를 가져오려고 했으나</p> <p>어째서인지 API형태로는 기본 영양소 9종만 제공해주었고 위와같은 추가영양소는 상세페이지에서만 제공되었기 때문에</p> <p>해당 페이지들을 모두 크롤링 하는것을 시도했다.</p> <p>그 첫번째 시도로 크롤링 해야 할 URL을 분석했다.</p> <h3 id="url분석">URL분석</h3> <ul> <li><code class="language-plaintext highlighter-rouge">간편검색 페이지</code> URL 간소화 <ul> <li>기존버전 <ul> <li><code class="language-plaintext highlighter-rouge">https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/simpleSearch.do?menu_no=&amp;menu_grp=&amp;code4=1&amp;code2=&amp;search_name=&amp;page=245</code></li> <li>주소에 불필요한 키값이 너무 많고, 있다해도 실제 검색과는 독립적으로 존재하는 부분의 대한 정보이기에 아래와같이 간소화 할 수 있다.</li> </ul> </li> <li>간소화 버전 <ul> <li><code class="language-plaintext highlighter-rouge">https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/simpleSearch.do?&amp;page=245&amp;code4=1</code></li> <li>추가적으로 3개의 탭을 이동하는 변수는<code class="language-plaintext highlighter-rouge">code4=[1,2,3]</code> 이다.</li> <li>전체 변수를 6개에서 2개로 줄일 수 있다.</li> </ul> </li> </ul> </li> <li><code class="language-plaintext highlighter-rouge">음식 세부정보 페이지</code> URL 간소화 <ul> <li>기존 버전 <ul> <li><code class="language-plaintext highlighter-rouge">https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/searchDetail.do?menu_no=&amp;menu_grp=MENU_NEW03&amp;code4=1&amp;code2=&amp;search_name=&amp;food_cd=100101000100000001&amp;labor_cd=201&amp;year2=2001&amp;page=1</code></li> <li>세부정보 페이지는 변수가 하나만 잘못 전달해주어도 아예 결과가 뜨지 않기 때문에 더 세세하게 봐야한다.</li> <li>위의 <code class="language-plaintext highlighter-rouge">간편검색 페이지</code> 와 마찬가지로 불필요한 변수를 제거한다.</li> </ul> </li> <li>간소화 버전 <ul> <li><code class="language-plaintext highlighter-rouge">https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/searchDetail.do?food_cd=100101000100000001&amp;labor_cd=201&amp;year2=2001</code></li> <li>필요없는 키를 제거하고 실제 필요한 키값은 <code class="language-plaintext highlighter-rouge">food_cd</code>,<code class="language-plaintext highlighter-rouge">labor_cd</code>,<code class="language-plaintext highlighter-rouge">year2</code> 3가지</li> <li>전체 변수를 9에서 3개로 줄일 수 있다.</li> <li>이를 지금부터 <code class="language-plaintext highlighter-rouge">baseform</code> 이라고 한다.</li> </ul> </li> </ul> </li> </ul> <h3 id="문제-정의">문제 정의</h3> <ol> <li> <p><code class="language-plaintext highlighter-rouge">간편검색 페이지</code>에서도 음식 별 최대 10가지 정보만을 제공 해주기 때문에 <code class="language-plaintext highlighter-rouge">음식 세부정보 페이지</code> 를 크롤링해야한다.</p> </li> <li> <p>각 페이지의 데이터를 <code class="language-plaintext highlighter-rouge">간편검색 페이지</code> 에서 가져와서 모든 페이지를 한번씩 들어가서 영양소를 크롤링한다.</p> </li> <li> <p>각 페이지의 <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> 태그는 아래와같이 되어있다.</p> <p><img src="/assets/post_img/image-20190211085007813.png" /></p> </li> </ol> <h3 id="간편검색-페이지의-a-태그들의-href-속성-크롤링"><code class="language-plaintext highlighter-rouge">간편검색 페이지</code>의 <code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> 태그들의 <code class="language-plaintext highlighter-rouge">href</code> 속성 크롤링</h3> <h4 id="문제인식">문제인식</h4> <ul> <li><code class="language-plaintext highlighter-rouge">&lt;a href="javascript:goDetail('100106002100100002', '201', '2017');"&gt;</code></li> <li><code class="language-plaintext highlighter-rouge">&lt;a&gt;</code> 태그의 <code class="language-plaintext highlighter-rouge">href</code> 속성이 일반적인 링크가 아니라 js function으로 인자를 넘긴다.</li> <li><code class="language-plaintext highlighter-rouge">baseForm</code>을 모두 활용하지 않고 3가지 정보만을 js function으로 주고있다.</li> </ul> <h4 id="시도-할-것">시도 할 것</h4> <ol> <li>해당 attr받아서 인자를 추출할 수 있다면 <code class="language-plaintext highlighter-rouge">음식 세부정보 페이지 URL</code> 간소화폼에 넣을 수 있을 것 같다.</li> <li>인자추출은 <code class="language-plaintext highlighter-rouge">('')</code> 을 기준으로 <code class="language-plaintext highlighter-rouge">,</code> 로 나누면 되지 않을까?</li> <li>시도!</li> </ol> <h4 id="시도과정">시도과정</h4> <ol> <li> <p>아래는 Ruby on Rails 를 Nokogiri 를 사용하여 구현한 Controller 코드이다.</p> <ul> <li> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">pull_cptfood</span> <span class="nb">require</span> <span class="s1">'open-uri'</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">3</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">tab</span><span class="o">|</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">126</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">page</span><span class="o">|</span> <span class="vi">@doc</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s2">"https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/simpleSearch.do?&amp;page=</span><span class="si">#{</span><span class="n">page</span><span class="si">}</span><span class="s2">&amp;code4=</span><span class="si">#{</span><span class="n">tab</span><span class="si">}</span><span class="s2">"</span><span class="p">))</span> <span class="vi">@foods</span> <span class="o">=</span> <span class="vi">@doc</span><span class="p">.</span><span class="nf">css</span><span class="p">(</span><span class="s1">'#tab1 th &gt; a'</span><span class="p">)</span> <span class="vi">@foods</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">tit</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">text</span><span class="p">.</span><span class="nf">strip</span> <span class="n">url</span> <span class="o">=</span> <span class="n">x</span><span class="p">[</span><span class="s1">'href'</span><span class="p">]</span> <span class="vi">@factors</span> <span class="o">=</span> <span class="n">url</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s1">'(\''</span><span class="p">)[</span><span class="mi">1</span><span class="p">].</span><span class="nf">split</span><span class="p">(</span><span class="s1">','</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span><span class="o">|</span><span class="n">x</span><span class="o">|</span> <span class="n">x</span><span class="p">[</span><span class="sr">/\d+/</span><span class="p">]}</span> <span class="vi">@food_cd</span> <span class="o">=</span> <span class="vi">@factors</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="vi">@labor_cd</span> <span class="o">=</span> <span class="vi">@factors</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="vi">@year</span> <span class="o">=</span> <span class="vi">@factors</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="no">Cptfood</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">tab: </span><span class="n">tab</span><span class="p">,</span> <span class="ss">page: </span><span class="n">page</span><span class="p">,</span> <span class="ss">name: </span><span class="n">tit</span><span class="p">,</span> <span class="ss">food_cd: </span><span class="vi">@food_cd</span><span class="p">,</span> <span class="ss">labor_cd: </span><span class="vi">@labor_cd</span><span class="p">,</span> <span class="ss">year: </span><span class="vi">@year</span><span class="p">)</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div> </div> </li> </ul> </li> <li> <p>모든탭 &gt; 모든 페이지 &gt; 모든 음식 정보( 키의 3요소 ) 저장 성공!</p> </li> </ol> <h4 id="시도-결과">시도 결과</h4> <ul> <li> <p>3개의 탭마다 최대 페이지수를 알아내 탭별로 크롤링 하였다. (위의 경우 1번째 탭은 126가지)</p> </li> <li> <p>나머지 2개의 탭도 위의 코드와 동일하고 최대 페이지수만 달라짐</p> <p>크롤링 결과는 아래와 같다.</p> <p><img src="/assets/post_img/image-20190211090850674.png" /></p> </li> </ul> <h4 id="참고godetail함수-구성">(참고)goDetail함수 구성</h4> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">goDetail</span><span class="p">(</span><span class="nx">food_cd</span><span class="p">,</span> <span class="nx">labor_cd</span><span class="p">,</span> <span class="nx">year2</span><span class="p">)</span> <span class="p">{</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">menu_no</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">menu_grp</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">code4</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">code2</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">search_name</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">food_cd</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">labor_cd</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">year2</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_addRequiredClassForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">page</span><span class="dl">'</span><span class="p">);</span> <span class="nx">fn_removeFormInputElementsForId</span><span class="p">(</span><span class="dl">'</span><span class="s1">baseForm</span><span class="dl">'</span><span class="p">);</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#food_cd</span><span class="dl">'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="nx">food_cd</span><span class="p">);</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#labor_cd</span><span class="dl">'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="nx">labor_cd</span><span class="p">);</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#year2</span><span class="dl">'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="nx">year2</span><span class="p">);</span> <span class="c1">//$('#search_name').val(encodeURI($('#search_name').val()));</span> <span class="nb">document</span><span class="p">.</span><span class="nx">baseForm</span><span class="p">.</span><span class="nx">target</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">_self</span><span class="dl">"</span><span class="p">;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">baseForm</span><span class="p">.</span><span class="nx">method</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">get</span><span class="dl">"</span><span class="p">;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">baseForm</span><span class="p">.</span><span class="nx">action</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">searchDetail.do?menu_grp=MENU_NEW03&amp;menu_no=2805</span><span class="dl">"</span><span class="p">;</span> <span class="nb">document</span><span class="p">.</span><span class="nx">baseForm</span><span class="p">.</span><span class="nx">submit</span><span class="p">();</span> <span class="p">}</span> </code></pre></div></div> <h3 id="음식-세부정보-페이지-의-각-영양소-크롤링"><code class="language-plaintext highlighter-rouge">음식 세부정보 페이지</code> 의 각 영양소 크롤링</h3> <p><img src="/assets/post_img/image-20190211091323857.png" /></p> <p>페이지 구성은 위와 같다.</p> <p><code class="language-plaintext highlighter-rouge">식품 안전 나라</code> 의 정보는 어쩔땐 모든 영양소가 <code class="language-plaintext highlighter-rouge">N/A</code>로 표기되어 있음에도 행이 존재하기도 하고</p> <p>어쩔때는 정보가 없으면 행 자체가 없기도 하여 일관성이 없다. <del>역시 이럴땐 개발자가 고생해야지!</del></p> <p>위의 이유로 여기서는 저장된 주소값 3요소를 가지고 세부 페이지에 들어가 이터레이터로 모든 행을 담아 DB에 저장하면 된다.</p> <div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">cptfoodInfo</span> <span class="nb">require</span> <span class="s1">'open-uri'</span> <span class="no">Cptfood</span><span class="p">.</span><span class="nf">all</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="vi">@doc</span> <span class="o">=</span> <span class="no">Nokogiri</span><span class="o">::</span><span class="no">HTML</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s2">"https://www.foodsafetykorea.go.kr/portal/healthyfoodlife/foodnutrient/searchDetail.do?food_cd=</span><span class="si">#{</span><span class="n">f</span><span class="p">.</span><span class="nf">food_cd</span><span class="si">}</span><span class="s2">&amp;labor_cd=</span><span class="si">#{</span><span class="n">f</span><span class="p">.</span><span class="nf">labor_cd</span><span class="si">}</span><span class="s2">&amp;year2=</span><span class="si">#{</span><span class="n">f</span><span class="p">.</span><span class="nf">year</span><span class="si">}</span><span class="s2">"</span><span class="p">))</span> <span class="vi">@rows</span> <span class="o">=</span> <span class="vi">@doc</span><span class="p">.</span><span class="nf">css</span><span class="p">(</span><span class="s1">'article &gt; table &gt; tbody &gt; tr'</span><span class="p">)</span> <span class="vi">@rows</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">r</span><span class="o">|</span> <span class="vi">@cols</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="s1">'td'</span><span class="p">)</span> <span class="no">InfoCptfood</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span> <span class="ss">name: </span><span class="n">f</span><span class="p">.</span><span class="nf">name</span><span class="p">,</span> <span class="ss">year: </span><span class="n">f</span><span class="p">.</span><span class="nf">year</span><span class="p">,</span> <span class="ss">num: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nf">text</span><span class="p">,</span> <span class="ss">fact_id: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nf">text</span><span class="p">,</span> <span class="ss">short: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nf">text</span><span class="p">,</span> <span class="ss">fact_name: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">3</span><span class="p">].</span><span class="nf">text</span><span class="p">,</span> <span class="ss">content: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">4</span><span class="p">].</span><span class="nf">text</span><span class="p">.</span><span class="nf">strip</span><span class="p">().</span><span class="nf">to_f</span><span class="p">,</span> <span class="ss">unit: </span><span class="vi">@cols</span><span class="p">[</span><span class="mi">5</span><span class="p">].</span><span class="nf">text</span> <span class="p">)</span> <span class="k">end</span> <span class="k">end</span> <span class="k">end</span> </code></pre></div></div> <p>위의 코드로 세부정보 페이지의 모든 영양소까지 크롤링이 가능하고 <a href="https://samslow.me/development/2018/12/09/Dialog-Flow%EC%97%90-csv-%EB%84%A3%EA%B8%B0/">CSV로 추출</a>하여 필요한 곳에 전달해주면 된다.</p> <p>다만, 여기서 사소한 문제가 있는데 가끔씩 <code class="language-plaintext highlighter-rouge">음식 세부정보 페이지</code> <code class="language-plaintext highlighter-rouge">음식이름</code> 속에 <code class="language-plaintext highlighter-rouge">\n</code> 심볼이 끼어들어가 있는 곳이 있어</p> <p>CSV로 추출하게되면 말썽을 일으키는 데이터가 발견되는데 잊지말고 잘 처리하여 전달받는 사람이 정상적으로 데이터를 볼 수 있도록 하자.</p> <h3 id="후기">후기</h3> <p>이런식의 이중 구조 크롤링은 처음이라 잘 모르기도 하고, 사실 더 간단하게 할 수 있는 방법이 있을텐데</p> <p>필자는 테이블을 2개를 만들어 따로따로 작업을 해주었다.</p> <p>그래도 다른 누군가가 보기 편하도록 기능이 원자단위로 되니 유익한 글이 될것으로 본다.</p> Mon, 11 Feb 2019 00:00:00 +0000 https://samslow.github.io/diary/2019/02/11/sickfoom-crawling-guide/ https://samslow.github.io/diary/2019/02/11/sickfoom-crawling-guide/ diary diary JS Integer Validation <p>회사에서 Javascript로 입력을 받는 알고리즘을 짠 경험중 정수검사( Integer Validation )을 한 경험을 나눕니다.</p> <hr /> <p>자바스크립트에서 정수 검사를 평소에 어떻게 하는가?</p> <p>사용하는 각자의 언어에서 제공해주는 함수를 사용 하는 방법도 있고 직접 정규표현식을 돌려서 맞추는 방법도 있을 것이다.</p> <p>오늘은 이런 전반적인 부분에 대해 얻은 지식과 삽질기를 공유하고자 글을 써 보았다.</p> <p>총 3가지의 시도를 해서 문제를 해결했으며 시도별 과정을 아래에 기술한다.</p> <h4 id="시도-1-df에서-action에-value를-dafult로-설정해본다">시도 1. DF에서 action에 value를 dafult로 설정해본다.</h4> <p>이미 앞의 글들을 몇개 읽고 온 독자들 이라면 현재 본인이 챗봇을 개발중인 플랫폼중 하나는 <code class="language-plaintext highlighter-rouge">Dialogflow</code>(이하 ‘DF’) 라는 것을 알 것이다.</p> <p>시스템의 리소스를 최대한 적게 사용하기 위해 DF에서 처리 가능하면 해당 방향으로 진행하기에 이번에도 이와같이 진행 해 보았다.</p> <p>DF에는 각 <code class="language-plaintext highlighter-rouge">Intent</code>에는 <code class="language-plaintext highlighter-rouge">Action and parameters</code> 가 있는데</p> <p>쉽게 설명하자면 프로그래밍에서 추상화 개념을 설명 할 때 <code class="language-plaintext highlighter-rouge">상자에 담아 보내준다</code> 라는 개념을 자주 사용하는 것 처럼(<del>이해하는 사람만 이해하길 바란다</del>)</p> <p>해당 <code class="language-plaintext highlighter-rouge">intent</code> 가 발동될때마다 발견된 인자들을 각 엔티티에 맞게 예쁘게 포장 해 주는 역할을 하는 기능이다.</p> <p>​<img src="https://www.dropbox.com/s/teennv2ybyaee4r/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202019-02-04%2002.05.36.png?raw=1" /></p> <p>저 <code class="language-plaintext highlighter-rouge">action</code> 의 활용 방법은 정말 다양한데, 일단 여기서는 해당 <code class="language-plaintext highlighter-rouge">intent</code> 가 발동 될 때 마다 아래와 같이 값을 Default로 반환 해 주는 역할을 한다고 보면 된다.</p> <p><img src="https://www.dropbox.com/s/z50ag196321k7v3/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202019-02-04%2002.08.18.png?raw=1" style="height:500px" /></p> <p>유저의 입력에대해서 숫자를 Catch해서 정수타입으로 바꿔주고 action은 내가 정한 String으로 잘 들어갔다.</p> <p>이 방법은 해당 액션이 발동되면 정수가 들어오는 것이므로 <code class="language-plaintext highlighter-rouge">parseInt()</code> 로 바로 받아 줄 수 있겠지만 조금만 더 생각해보면 숫자가 없는 일반 String 타입을 처리 해 줄 수 없다는 한계를 바로 느낄 수 있다.</p> <h4 id="시도-2-typeof-를-통해-number가-아닐때-if-else-사용">시도 2. <code class="language-plaintext highlighter-rouge">typeof</code> 를 통해 <code class="language-plaintext highlighter-rouge">number</code>가 아닐때 <code class="language-plaintext highlighter-rouge">if-else</code> 사용</h4> <p>사실 이번 시도가 이 글을 쓴 이유기도 하다.</p> <p>필자의 생각과 Javascript의 생각(?)이 달라서 안되는 이유를 찾아보았더니 의외였기 때문이다.</p> <p>먼저 <code class="language-plaintext highlighter-rouge">시도1</code>이 DF에서의 검사였다면, 이번시도는 Webhook이 걸릴 서버에서의 검사이다.(정확히는 회사 사내 플랫폼 서버지만)</p> <p>이 방법마저 안 된다면 내가 구동중인 서버까지 값을 저장해서 가져와야하기에 여기까지가 마지노선이라고 생각하고 고민해보았다.</p> <p>이번 시도는 앞선 시도와 다르게 코딩을 할 수 있기때문에 <code class="language-plaintext highlighter-rouge">if-else</code> 를 사용하여 접근했다.</p> <p>JS는 필요할때만 찾아서 쓰는 생존코딩으로 했기 때문에 어려웠던 부분이 아닐까 싶다.</p> <p>문제의 핵심은 아래 코드와 같다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">_req</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">$request</span><span class="p">);</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">_req</span><span class="p">.</span><span class="nx">queryResult</span><span class="p">.</span><span class="nx">parameters</span><span class="p">.</span><span class="nx">number</span><span class="p">;</span> <span class="nx">goal</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">params</span><span class="p">);</span> <span class="c1">// Google Assistant JSON으로부터 Input</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">goal</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">){</span> <span class="nx">pof</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">pass</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">pof</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">fail</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">pof</span><span class="p">)</span> <span class="c1">// "pass"</span> </code></pre></div></div> <p>여기서 필자가 겪은 문제가 무엇인지 바로 파악 할 수 있다면 아래는 더 안 읽어도 무관하다.</p> <p>핵심은 조건문을 걸었지만 결과는 늘 pass가 나오는 문제다.</p> <p><code class="language-plaintext highlighter-rouge">parseInt()</code> 를 걸면 당연히 숫자가 있을 땐 타입이 <code class="language-plaintext highlighter-rouge">number</code>여아 하고 이외엔 <code class="language-plaintext highlighter-rouge">NaN</code>이라는 타입이니</p> <p>당연히 else로 가야하는게 정상이라고 생각했다.</p> <p>NaN을 Js에선 Null쯤으로 취급한다고 생각해서 난 오류인데</p> <p><img src="https://www.dropbox.com/s/zl279q9ry4lkzqc/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202019-02-04%2002.39.29.png?raw=1" /></p> <p>검색을 해도 <code class="language-plaintext highlighter-rouge">잘못된 입력</code> 이라는 키워드만 봐도 Null과 굉장히 유사하다고 생각되고</p> <p>약어를 살펴봐도 <code class="language-plaintext highlighter-rouge">Not A Number</code>이기때문에 왜 if문으로 계속 들어가는가에 대한 의문을 가지게 되었다.</p> <p>하지만, 글머리에서 서술 한 것처럼 JS의 생각은 나와 달랐고, 이건 JS가 설치 해 둔 함정에 빠진 느낌까지 들었다..</p> <p><img src="https://i.ytimg.com/vi/lTlObkX0paI/hqdefault.jpg" /></p> <p>오류의 요점은 NaN이 <code class="language-plaintext highlighter-rouge">number</code>로 인식되어서 실패한다는 것인데, NaN을 단순하게 <code class="language-plaintext highlighter-rouge">숫자가 아니다</code> 라고 인식했기 때문이다.</p> <p>그리고 NaN에 대해서 별도로 구글링을 해보니 JS에서는 NaN조차도 number에 한 종류로 취급되고 있었다.</p> <p><code class="language-plaintext highlighter-rouge">NaN</code> 은 unordered data type이고 숫자는 또 아니어서 아래의 결과가 나타난다.</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kc">NaN</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// false</span> <span class="kc">NaN</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// false</span> <span class="kc">NaN</span> <span class="o">==</span> <span class="kc">NaN</span><span class="p">;</span> <span class="c1">// false</span> <span class="nb">isNaN</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// true</span> </code></pre></div></div> <p>Js가 정말 멋진 언어라고 생각이 들다가도 이럴때 보면 사람을 당황시키는 언어인것 같기도 하다.</p> <p>NaN에 대해서 더 알고 싶으신 분들은 <a href="https://javascriptrefined.io/nan-and-typeof-36cd6e2a4e43">javascriptrefined 미디움</a>에서 확인해 보시길 바란다.</p> <h4 id="시도-3--isnanvalue-을-통해-검사하기">시도 3. <code class="language-plaintext highlighter-rouge">isNaN(value)</code> 을 통해 검사하기</h4> <p>그럼, 이제 문제를 알았으니 최종장에 들어설 자격을 갖추었다.</p> <p>이제는 number로 검사할 것이 아니라, parseInt의 반환형을 확인해 숫자가 들어올때와 아닐때를 구별 해 주면 됐다.</p> <p>그 방법으로 <code class="language-plaintext highlighter-rouge">isNaN()</code> 함수를 사용 해 NaN일때는 fail로 가는것을 아래 코드에서 확인했다.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">_req</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">$request</span><span class="p">);</span> <span class="nx">params</span> <span class="o">=</span> <span class="nx">_req</span><span class="p">.</span><span class="nx">queryResult</span><span class="p">.</span><span class="nx">parameters</span><span class="p">.</span><span class="nx">number</span><span class="p">;</span> <span class="nx">goal</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">params</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">_check</span> <span class="o">=</span> <span class="nb">isNaN</span><span class="p">(</span><span class="nx">goal</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">_check</span><span class="p">){</span> <span class="nx">pof</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">pass</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span><span class="k">else</span><span class="p">{</span> <span class="nx">pof</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">fail</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div> <p>다만, 여기서 추가적으로 알아가야할 것은 <code class="language-plaintext highlighter-rouge">isNaN()</code>과 <code class="language-plaintext highlighter-rouge">Number.isNaN()</code> 이 다르다는 것인데, <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/isNaN">MDN문서</a>에서는 후자가 ES5부터 사용되는 규격이며, 더 엄격하게 검사하니 이를 사용할 것을 권한다.</p> <p>아래 코드에서 다른 점을 확인 해보자</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">isNaN</span><span class="p">(</span><span class="dl">'</span><span class="s1">123</span><span class="dl">'</span><span class="p">)</span> <span class="c1">//false</span> <span class="nb">isNaN</span><span class="p">(</span><span class="dl">'</span><span class="s1">samslow</span><span class="dl">'</span><span class="p">)</span> <span class="c1">//true</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="dl">'</span><span class="s1">samslow</span><span class="dl">'</span><span class="p">)</span> <span class="c1">//false</span> </code></pre></div></div> <p>충분히 이상하고 혼란스러운 케이스를 발견했는가? <code class="language-plaintext highlighter-rouge">Number.isNaN()</code> 을 사용 해야 할 이유를 알겠다면 성공이다.</p> <p>글을 다 쓰긴 했는데 기술적인 부분을 다루는 글이다보니 충분히 필자와 다른 의견도 있을 것이고 틀린 부분도 있을 텐데</p> <p>댓글로 이런 부분에서자유롭게 의견을 주시고 가르쳐주시면 감사하겠습니다.</p> <p>글이 도움이 되셨길 바라며 이만 줄이겠습니다.</p> Mon, 04 Feb 2019 00:00:00 +0000 https://samslow.github.io/development/2019/02/04/JS-Integer-Validation/ https://samslow.github.io/development/2019/02/04/JS-Integer-Validation/ web development Jekyll Blog, PCDATA invalid Char value Error 해결 방법 <p>Jekyll 블로그 포스팅을 하다가 평소에는 문제가 없지만 rss 문서 즉, feed.xml 문서로 들어가면 나오는 PCDATA error를 해결하는 방법에 대한 글입니다.</p> <h1 id="jekyll-blog-pcdata-invalid-char-value-error-해결-방법">Jekyll Blog, PCDATA invalid Char value Error 해결 방법</h1> <h2 id="에러의-시작">에러의 시작</h2> <p>일주일간의 노동을 마치고 주말에는 맘 편히 쉬면서 블로그를 할까 하여 글 소재를 찾고있었다.</p> <p>그러다 우연히 <a href="https://www.slideshare.net/zzsza/intro-102870757">Slack Channel을 통한 개발자 블로그 구독 방법</a>을 새로 알게 되었다.</p> <p><img src="/assets/post_img/image-20190128093037359.png" height="600px" /></p> <p>사실, 초기에는 Rss의 필요성을 못 느껴서 블로그 사이드에 있는 링크를 아예 빼버렸었는데</p> <p>첫문단의 링크 글을 통해 rss 구독의 필요성을 느끼게 되었고 내 사이트도 미래의 누군가를 위해 붙여놓기로 했다.</p> <p>안에 들어가게되면 아래와 같이 일반 독자는 보기힘든 글이 나온다.</p> <p><img src="/assets/post_img/image-20190128093620578.png" /></p> <p>그러나, 해당 아이콘에 링크를 달아 붙이는것까진 잘 됐으나 제일 처음 들어가자마자 아래와 같은 에러가 뜨게 되는데</p> <p><img src="/assets/post_img/s2019-01-28 01.16.05.png" /></p> <p>처음보는 유형의 에러라서 일단 개발자의 본능으로 구글링을 해보았지만</p> <p>아래와같이 다른 곳에도 나는 에러로 내것과는 해결방법이 근본부터 다른 것 같았다.</p> <p><img src="/assets/post_img/s2019-01-28 01.16.34.png" /></p> <h2 id="원인-분석">원인 분석</h2> <p>몇번 삽질끝에 에러문장의 단어들을 자체분석 해 보기로 하였고 대분류는 <code class="language-plaintext highlighter-rouge">XML</code>과 관련된 에러라는걸 알게되고</p> <p>자세한 소분류로는 PCDATA 라는 것의 문제라는것을 알게되었다.</p> <p>여기서 PCDATA란 Parsed Character DATA의 준말인데 자세한 설명은 <a href="http://tcpschool.com/xml/xml_dtd_component">링크</a>의 아래 설명이 나와있다.</p> <p><img src="/assets/post_img/s2019-01-28 01.17.00.png" /></p> <p>물론, 이를 처음보는 나로썬 이것조차 무슨 말인지 알 수 없었지만 여기서 알 수 있었던것은 <code class="language-plaintext highlighter-rouge">시작태그</code>와 <code class="language-plaintext highlighter-rouge">종료태그</code> 사이 위치한 <code class="language-plaintext highlighter-rouge">텍스트</code>데이터 라는 것이었다.</p> <p>여기서 이것은 보이지않는 <code class="language-plaintext highlighter-rouge">문장마감심볼</code>의 문제라는 느낌이 왔다. 뭐 CR, LF 같은 줄바꿈 심볼들말이다.</p> <p>에러메시지를 자세히 보니 138번째 줄에 140번대 컬럼이 문제가 있는 것과 더 아래에 어떤 파일이 문제있는지 알게 되었고</p> <p>해당 파일 부분은 아래와 같았다.</p> <p><img src="/assets/post_img/s2019-01-28 01.16.16.png" /></p> <p>겉보기엔 아무 문제 없으나 132줄 부터 148줄을 지우고 <code class="language-plaintext highlighter-rouge">feed.xml</code> 을 빌드해보면 잘 나오는걸로 보아</p> <p>원인을 찾는데엔 성공했다.</p> <h2 id="문제-해결">문제 해결</h2> <p>아마 본인의 에디터(Typora) 버그로 인해 들어가야할 심볼이 안들어간것 같은데 이것저것 지우고 돌려놓고 해봐도 정확히 문제가 되는 부분을 찾을 순 없었다.</p> <p>그리하여, 저 부분만 다시 VSCODE로 타이핑 하는 방법을 사용했더니 정상적으로 나올 수 있게 되었다.</p> <p>다행히 에러메시지에 정확하게 문제가 있는 부분이 표기가 되어서 XML 지식이 없어도 쉽게 문제를 해결 할 수 있었지만</p> <p>관련된 게시글이 없어 다른 사람들의 삽질을 막기위해 글을 남기게 되었다.</p> Mon, 28 Jan 2019 00:00:00 +0000 https://samslow.github.io/development/2019/01/28/Jekyll-Blog,-PCDATA-invalid-Char-value-Error-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95/ https://samslow.github.io/development/2019/01/28/Jekyll-Blog,-PCDATA-invalid-Char-value-Error-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95/ bugFix development 물들어올때 노 젓는 당근마켓 방문 후기 <ul> <li>당근마켓을 방문하여 들은 이야기를 정리하고 기록을 남기기 위한 목적으로 주관적인 글</li> </ul> <hr /> <p><br /></p> <p><img src="https://cdn-images-1.medium.com/fit/t/1600/480/1*fTQOUpe09RAWcotdIl2G5w.jpeg" alt="당근마켓 로고" /></p> <h1 id="-당근마켓-오피스-가는-길">🥕 당근마켓 오피스 가는 길</h1> <p><img src="/assets/post_img/KakaoTalk_Photo_2019-01-08-22-42-38-2.jpeg" style="width:50%;" /></p> <p>당근마켓 오피스는 서초역에서 3분정도 걸으면 있다.</p> <p>지도를 켜고 따라가다 보면 익숙한 로고와 함께 당근마켓 위치가 보인다.</p> <p>이 입간판(?)이 서 있는 곳 근처 문은 다른 곳으로 가는 문이어서 잠깐 혼란을 겪었지만</p> <p>다행히 건물을 빙 둘러 다른 입구를 찾아서 들어갔다.</p> <p>혹 다른 분들이 가신다면 헷갈리지 마시길!</p> <h1 id="-당근마켓-오피스-첫-인상">🥕 당근마켓 오피스 첫 인상</h1> <p><img src="/assets/post_img/KakaoTalk_Photo_2019-01-08-22-42-38-1.jpeg" style="width:50%;" /></p> <p>엘레베이터를 내리면 바로 당근마켓로고가 크게 버티고 있다.</p> <p>들어가자마자 채용 홈페이지에서 볼 수 있었던 공간이 있어서 굉장히 반가웠다.</p> <p>사진으로 보던 것처럼 푸릇푸릇하고(?) 어찌 보면 분위기 좋은 카페에 와 있는 느낌이 들 정도로 순간 모각코를 하러 온 건지 착각 할 정도로 첫 인상이 좋았다.</p> <p><img src="/assets/post_img/image-20190110221201880.png" style="width:80%;" /></p> <p>사진의 오른쪽에 보면 코카콜라 냉장고가 있는데 저기에서 웰컴드링크(?)로 원하는것을 꺼내 먹을 수 있게 해 주셔서 일하고 와서 정신이 살짝 혼미 해 져있는 나는 몬스터에너지를 골라 쭈욱 들이키며 회의실을 소개받으러 갔다.</p> <p>한쪽 벽면엔 바닥에 깔면 내가 눕고도 남을 만큼의 거대한 모니터가 있었고 거기에는 <code class="language-plaintext highlighter-rouge">애플TV</code>가 물려있었다.</p> <p>10명정도가 둘러앉을 수 있는 책상에는 <code class="language-plaintext highlighter-rouge">맥북프로</code>와 이번에 새로 나온 <code class="language-plaintext highlighter-rouge">아이패드 프로 3세대</code>가 <code class="language-plaintext highlighter-rouge">애플펜슬 2세대</code>와 함께 나란히 놓여 있었다.</p> <p>회의실용으로 이렇게 완벽하고 이쁜 기기들이라니, 애플 생태계에 들어온지 몇달 안 된 내가 보기엔 최고의 회의실 첫 인상 이었다.</p> <p>이후에 다시 이야기하겠지만, CTO님이 오셔서 화면으로 자료들을 보여주셨는데 그때 지나간 애플 TV 화면 보호기로 나오는 영상들이 너무 아름다웠다.</p> <p>사실 CTO님의 이야기를 들으면서 감탄을 몇 번 했는데 그중 몇개는 TV의 영상을 보며 한 것이었다.. 너무 멋져서 저도 모르게..</p> <p>쩍 벌어지는 입과 자꾸만 눈이 가는 공간들을 뒤로하고 다른곳을 둘러 보러 이동했다.</p> <p>앞의 두 공간이 너무 강렬해서 이후의 탕비실이나 휴게실은 평범한 느낌</p> <p>중간에 기계학습쪽 직원분이 탕비실을 들리시며 우리에게 먼저 말을 걸어주셔서 이런저런 이야기를 주고 받았다.</p> <p>여러 지식을 말씀하시는게 보통 분은 아니시겠구나 생각하던 와중 내가 진행중인 네이버 NSML AI해커톤 관련 이야기가 나왔고, 알고보니 당근마켓에서 일하시는 개발자분들은 모두 네이버/카카오 출신 개발자였던 것.</p> <p>그런 분들이 70억 규모의 VC를 받아 만든 당근마켓은 귀여운 이름과는 이질감이 들어 안 어울릴 정도로 대단한 분들이 많았다.</p> <p>나와는 다른 개발자의 세계에 감탄하며 탕비실을 뒤로하고 직원들이 일하고 있는 사무실에 들렸다.</p> <p>실제로 일을 하는 사무실 공간 각자의 책상은 <code class="language-plaintext highlighter-rouge">스탠딩 데스크</code>로 전동으로 책상이 올라갔다 내려갔다 하는데 무지 심플하게 생겨선 그렇게 올라가는 줄 몰랐다.</p> <p>회사 성장이 빨라서 이곳도 조만간 떠나실 예정이라고 들었다. 다음 이사 갈 곳은 얼마나 더 예쁠까. 기회되면 또 방문하고 싶다.</p> <h1 id="-회사-복지-및-워크스테이션">🥕 회사 복지 및 워크스테이션</h1> <p>회사의 전반적인 워크스테이션 장비들은 주로 애플계열로 되어있다. 위에서 이야기했듯 대부분 애플 제품이었다.</p> <p>나는 그래서 애플 친화적인 기업이다 라고 판단했는데 CTO님이 말씀하시길</p> <blockquote> <p>“우리는 좋은 것을 사용하려고 하는 편입니다. 애플보다 좋다고 느껴지는 것이 있으면 사용 할겁니다.”</p> </blockquote> <p>라고 하셔서 괜스레 깨달음을 얻었다.</p> <p>맞다.</p> <p>내가 맥북을 쓰는 것은 애플이 좋아서가 아니라 맥북이 개발하기에 나에게 가장 잘 맞기 때문이었다.</p> <p>당근마켓은 좋은 툴이 있다면 제안하고 함께 사용할 수 있도록 열려있는 곳이다.</p> <p>또 맘에 들었던 복지중 하나는 매주 목요일을 원격근무하는 날로 정해서 한다고 하는데 무작정 좋은것은 아니고</p> <p>어차피 출근하지 않더라도 자신의 자리에서 일하고 산출물을 내야되기 때문에</p> <p>오히려 출근하는 것이 나을 수도 있다. 나라도 특별한 일이 있는 것이 아니라면 출근해서 일 하는 게 나을 듯!</p> <p>개발 환경은 <code class="language-plaintext highlighter-rouge">Ruby on Rails</code> 와 <code class="language-plaintext highlighter-rouge">AWS</code> 등을 포함해 빠른 개발을 할 수 있는 스타트업 중심의 스택으로 구성되어 있었다.</p> <p>내가 당근 마켓에 끌린 것도 처음 시작한 개발 언어인 <code class="language-plaintext highlighter-rouge">Ruby on Rails</code> 가 모집공고 상단에 있어서 관심 있게 보기 시작해서이다.</p> <p>파도 파도 좋은 것들 투성인데 또 맘에 드는건 요새 핫한 markdown 문서 공유 툴인 <code class="language-plaintext highlighter-rouge">Notion</code> 사용을 한다는 것이다.</p> <p>나는 개인적으로 기존에 <code class="language-plaintext highlighter-rouge">Typora</code>로 사용하고 있었는데 개발의 초기 단계라 문서가 많이 나오기도 하고 꼭 한번 써 보고 싶어서 내가 다니는 회사의 팀에서도 <code class="language-plaintext highlighter-rouge">Notion</code>을 통한 문서 공유를 제안했는데 아무래도 보안에 걸리는 것이 있을 수도 있다 보니 아직은 논의를 하는 중이다.</p> <p>전반적으로 정리하면 누구든 <code class="language-plaintext highlighter-rouge">원하는 것을 제안할 수 있는 환경</code> 인 점이 맘에 드는 곳이었다.</p> <p>어쩌면 당연하지만 이런 환경을 실현하기가 어려운 것이라 더 좋아 보였던 것 같다.</p> <p>더 많은 정보 및 채용은 <a href="https://dngn.kr/join-us">공식채용공고</a> 페이지에서 확인하길 바란다.</p> <h1 id="-cto와의-만남-">🥕 CTO와의 만남 (?)</h1> <p>회사 내의 문화 중 하나는 수평적인 문화를 위해 서로를 영어 이름으로 부르는데 CTO님의 닉네임은 Seapy다.</p> <p>개인적으로 이런 발표를 기대했지만 바쁘신 분에게 요구하기가 죄송스러워 희망하기만 했는데 너무도 친절히 설명해 주셨다.</p> <p>그리고 CTO 님이 입고 있었던 WWDC에 다녀오면 가져오셨다던 애플 조끼가 너무 멋졌다..</p> <p>(여기서 다시한번 애플에 들어오긴 쉬웠지만 나가긴 어려우리라 생각도 들었다..)</p> <p>설명을 잘 해 주셔서 말주변이 좋으신가 보다 생각했는데 알고 보니 유튜브/ 블로그 등의 SNS를 많이 하시고 또 글도 많이 쓰시는 분이셨다. 사실 여러 군데 찾아보니 안 하시는 SNS를 찾는 것이 빠를 정도로 많이 하고 계셨다.. 😂</p> <p>이건 믿고 봐야 한다는 생각에 모두 구독··!</p> <p>당근마켓 운영의 기본 원칙은 <code class="language-plaintext highlighter-rouge">투자금</code> 이나 <code class="language-plaintext highlighter-rouge">광고 수입</code> 들을 쟁여두지 않고 모두 다시 당근마켓을 알리고 광고하는데 태운다고 한다.</p> <p>이는 글로벌 스트리밍 기업 <code class="language-plaintext highlighter-rouge">넷플릭스</code>나 <code class="language-plaintext highlighter-rouge">실리콘 밸리</code> 에서도 지켜지는 원칙이며 물 들어올 때 노를 저어야 하는 스타트업에 꼭 필요한 원칙이지 싶다.</p> <p>아래에 들은 내용을 간단히 정리해보았다.</p> <h2 id="현재까지의-당근마켓">현재까지의 당근마켓</h2> <p>성장곡선을 이야기할 때 보통 기업은 가입자 수를 그래프화시켜서 이야기하며 투자를 쉽게 받을 목적으로 이 수치를 <code class="language-plaintext highlighter-rouge">누적가입자 수</code>로 뻥튀기시키는데</p> <p>당근마켓 같은 경우는 실제로 유입되어 활동하는 유저들만을 기반으로 하므로 더욱더 유의미한 자료였던 것 같다.</p> <p><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F7b3f49a5-6ad2-4280-891c-82db67df3b42%2Fdaangn-3years-mau.png" style="width:80%;" /></p> <p>위 그래프는 공식 채용 사이트에 있는 <code class="language-plaintext highlighter-rouge">MAU</code> 그래프이다.</p> <p>중간에 한 번씩 떨어지는 구간에 대한 해석은 여러분이 직접 맞춰보는 것도 좋을 것 같다.</p> <p>답을 알고 보면 그럴만한 이유가 있는 구간이지만 모를땐 안보이더라.</p> <p>2016년에 그래프가 시작하여 2018년 10월까지 시간이 흐를수록 거의 수직에 가깝게 가팔라지고 있다.</p> <p>2019년 지금까지도 그래프는 계속 상승 중이며 비슷하게 커졌던 기업인 <code class="language-plaintext highlighter-rouge">배달의 민족</code> 의 아성을 위협할 만하지 않은가?</p> <h2 id="앞으로의-당근마켓">앞으로의 당근마켓</h2> <p>어떤 기업이든 현재 머물러 수익을 창출하려 한다면 반드시 주저앉게 되어있다.</p> <p>끊임없이 새로운 기술들과 서비스들이 우후죽순으로 생겨나기 때문이다.</p> <p>아마 당근마켓은 지역 서비스로서의 굳건한 입지를 다져나가지 않을까 싶다.</p> <p><code class="language-plaintext highlighter-rouge">중고나라</code>같은 서비스는 네이버 종속적인 점을 제하고 보더라도 크게 달라지는 점이 없다.</p> <p><code class="language-plaintext highlighter-rouge">대체재</code>가 없어서 쓸 수밖에 없는 서비스지 않은가? 이미 업자 잡기나 사기꾼 잡기도 손을 놓은 듯하고..</p> <h2 id="기타-등등">기타 등등</h2> <p>서비스를 운영하는 경험적으로 배운 점은 테스트 방법이었다.</p> <p>테스트 라는 말이 적합한 지는 모르겠는데, 유저에게는 속도로 비추어지는 <code class="language-plaintext highlighter-rouge">비기능적인 요구</code> 에 대한 업데이트를 배포 전에 일부 지역에서 테스트를 해 보는 방식에 대해 새로운 인사이트를 얻었다.</p> <p>만약 로컬 테스트 후 문제없다고 판단되어 배포했다가 버그가 나면 큰일 이기에 이런 지역적인 테스트 방법이 유용하게 다가왔다. 이건 진짜 서비스를 운용해 보지 않으면 알 수 없는 것 😭</p> <p>이외에도 당근마켓이 잘 돌아가도록 하는 여러 장치들에 대해서도 이야기했는데 블로그에 쓸 것은 아닌 것 같아서 여기까지.</p> <h1 id="-방문-후기">🥕 방문 후기</h1> <p>평소에도 장난처럼 커뮤니티 댓글을 보면 “혹시 누군가 시켜서 하는 거라면 다음에 당근을 흔들어 주세요!”라는 글에서부터</p> <p>꽤 자연스럽게 우리 삶에 당근이라는 채소가 많이 등장하는 것 같다. (?)</p> <p>물론 그 당근과 이 당근은 다르지만··그만큼 친숙하게 느껴졌던 회사였던 것 같다.</p> <p>더불어서, 회사 방문을 통해 여러 이야기를 듣고 현업자분들에게 이야기를 들으니 내 손으로 할 수 있는 것들이 정말 많다는 것을 다시 한번 느끼게 되는 자극제가 되었다.</p> <p>이렇게 블로그를 쓰는 것만 봐도 내가 얼마나 자극받았는지 알 수 있는 것 같다.</p> <p>( 직장인이 퇴근하고 글을 쓰는 것은 정말 힘들다는 것을 깨달았다.)</p> <p>전과를 하여 컴퓨터를 배운지 이제 2년이 되었는데 취업이라는 문을 두고 여러 생각과 정체가 오는 지금 더욱 열정을 불태울 수 있는 시간이었다.</p> <p>취준생의 마음가짐으로 다녀온 것이라 더 재미있었을 수도··ㅎ</p> <p>그리고 역시 애플이 짱이다.. 사무실에 있는 <code class="language-plaintext highlighter-rouge">애플숲</code>은 나로 하여금 이곳이 아니더라도 맥북으로 모두가 생산성을 논하면서 일하는 회사를 상상하게 해주었다.</p> <p>처음 배운 프로그래밍 언어였던 레일즈를 실무에서도 이렇게 잘 쓰인다는 것을 직접 보니 레일즈.. 죽지 않았다! 라는 마음도 들었고, 받아온 스티커 중에 Ruby를 들고 있는 토끼는 내 마음에 다시 한번 Ruby를 각인시켜 불태워 주었다.</p> <p><img src="/assets/post_img/KakaoTalk_Photo_2019-01-10-23-39-40.jpeg" style="width: 70%;" /></p> <p>조만간 당근마켓에서 <a href="https://festa.io/events/183">루비세미나</a>를 여는데 이미 신청도 완료한 상태라 기대하는 중이다!</p> <p>가서 또 반가운 얼굴들을 만나길</p> <p><img src="/assets/post_img/KakaoTalk_Photo_2019-01-10-22-37-19.jpeg" style="width: 70%;" /></p> <p>마지막으로, 초대해준 재호! 같이 가준 치오형, 채채 덕분에 기억에 남을 시간을 가질 수 있어서 감사하다.</p> <p>다음에 또 기회가 돼서 이런 시간 자주 가지길·· 늘 선한 영향력 감사합니다.</p> Thu, 10 Jan 2019 00:00:00 +0000 https://samslow.github.io/diary/2019/01/10/DangnMarket-visit-review/ https://samslow.github.io/diary/2019/01/10/DangnMarket-visit-review/ diary diary MacOs DialogFlow에 Csv 넣기 <p>GoogleAssistant를 사용하기위해 DialogFlow를 사용하며 겪은 문제중</p> <p><code class="language-plaintext highlighter-rouge">csv import</code> 에 관한 해결을 적은 글입니다.</p> <h1 id="macos-dialogflow-에-csv-넣기">MacOS DialogFlow 에 csv 넣기</h1> <h2 id="크롤링까진-무난했다">크롤링까진 무난했다</h2> <p>공공데이터API에서 가져온 식품 DB를 Ruby on Rails로 가져오는 것 까진 좋았는데</p> <p>Mac에서는 UTF Encoding의 문제로 DB로부터 csv파일을 추출하여 열면 한글이 와장창 깨지게 된다.</p> <p><img src="/assets/post_img/image-20181209001235334.png" alt="image-20181209001235334" /></p> <center>"쎄뎅 대쩧?"</center> <p>Excel 내에서 Encoding을 변경하는 방법도 있을 수 있겠지만 어떻게 하는지 몰라서</p> <p>Mac에서 csv파일을 열 때 한글 깨짐 오류는 아래의 방법으로 해결했다.</p> <ol> <li> <p>csv파일을 <code class="language-plaintext highlighter-rouge">텍스트 편집기</code> 로 연다</p> </li> <li> <p>cmd+shift+s를 눌러 다른 이름으로 저장</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">UTF-16</code> 방식으로 인코딩설정을 한다.</p> </li> <li> <p>파일 이름을 <code class="language-plaintext highlighter-rouge">파일이름.csv</code> 로 저장한다</p> <p><img src="/assets/post_img/screenshot12.10.02.png" alt="스크린샷 2018-12-09 오전 12.10.02" /></p> <p><img src="/assets/post_img/image-20181209001421956.png" alt="image-20181209001421956" /></p> <center>다행히 정상적으로 출력된다!</center> </li> </ol> <h2 id="끝날때까지-끝낸게-아니다">끝날때까지 끝낸게 아니다.</h2> <p>이 데이터를 그대로 DialogFlow에 Upload Entity로 import하게되면 대충 아래와 같은 문구를 접하게 된다.</p> <p><img src="/assets/post_img/스크린샷 2018-12-09 오전 12.16.34.png" alt="image-20181209001704947" /></p> <p>그래서 조금더 짱구를 굴려서 Entity를 추가하고 오른쪽 위 <code class="language-plaintext highlighter-rouge">···</code>버튼을 눌러 <code class="language-plaintext highlighter-rouge">Switch to raw mode</code> 에서 데이터를 긁어서 넣어보는 시도를 해보았다.</p> <p><img src="/assets/post_img/스크린샷%202018-12-09%20오전%2012.18.21.png" alt="스크린샷 2018-12-09 오전 12.18.21" /></p> <p>이번에는 <code class="language-plaintext highlighter-rouge">CSV format</code> 이 안 맞다는 아래와 같은 오류가 뜬다.</p> <p><img src="/assets/post_img/image-20181209002016894.png" alt="image-20181209002016894" /></p> <p>도대체 그 포맷이 뭘까 생각하여 단순하게 대충 만든 Entity를 csv로 추출하여 보니 <code class="language-plaintext highlighter-rouge">" A "," B " </code> 의 구조를 가져야만 했다.</p> <p>그리고 추가적으로 한 Row는 하나의 Entity이다.</p> <p>오류 메세지 진짜 엄청 많네</p> <p>내 데이터의 문제는</p> <ol> <li>모든 Row가 <code class="language-plaintext highlighter-rouge">""</code> 로 둘러싸여 있지 않다.</li> <li>쉼표 뒤에 데이터가 없다 ( 공란으로 Download는 가능하지만 올리는건 안됨 ^^ )</li> <li>아래 경고메시지에 무방비하다; 괄호의 사용 금지<code class="language-plaintext highlighter-rouge">(), {}, [], &lt;&gt;</code> <img src="/assets/post_img/스크린샷%202018-12-09%20오전%2012.24.47.png" alt="스크린샷 2018-12-09 오전 12.24.47" /></li> </ol> <p>그래서 데이터 전처리를 위해 새롭게 코드를 짜게된다.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"16extract_name_full_foods_converted.csv"</span><span class="p">,</span> <span class="s">'r'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf16"</span><span class="p">,</span> <span class="n">errors</span><span class="o">=</span><span class="s">'ignore'</span><span class="p">)</span> <span class="n">o</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">"converted_name_foods.csv"</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="n">line</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">().</span><span class="n">splitlines</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">13531</span><span class="p">):</span> <span class="k">if</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="s">'</span><span class="se">\"</span><span class="s">'</span><span class="p">:</span> <span class="c1"># 더블쿼트가 없다면 추가! </span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="s">'</span><span class="se">\"</span><span class="s">'</span> <span class="o">+</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="s">'</span><span class="se">\"</span><span class="s">'</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="s">", "</span> <span class="o">+</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="c1"># 콤마와 데이터 복사 붙이기 </span> <span class="c1">#괄호를 공백 혹은 제거하기 </span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]).</span><span class="n">replace</span><span class="p">(</span><span class="s">'('</span><span class="p">,</span> <span class="s">' '</span><span class="p">)</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]).</span><span class="n">replace</span><span class="p">(</span><span class="s">')'</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]).</span><span class="n">replace</span><span class="p">(</span><span class="s">'&gt;'</span><span class="p">,</span> <span class="s">' '</span><span class="p">)</span> <span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]).</span><span class="n">replace</span><span class="p">(</span><span class="s">'&lt;'</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span> <span class="n">o</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">line</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">+</span> <span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span> <span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span> <span class="n">o</span><span class="p">.</span><span class="n">close</span><span class="p">()</span> </code></pre></div></div> <p>이 모든것을 하고나면 데이터는 예쁘게 나오게 된다.</p> <p><img src="/assets/post_img/image-20181209003431221.png" alt="image-20181209003431221" /></p> <p>저대로 예쁘게 긁어서 Raw mode에 넣어주면 끗!</p> Sun, 09 Dec 2018 00:00:00 +0000 https://samslow.github.io/development/2018/12/09/Insert-CSV-in-dialogFlow/ https://samslow.github.io/development/2018/12/09/Insert-CSV-in-dialogFlow/ python development 개발자의 두가지 커리어 <p>이 글은 2019년 졸업 예정자의 커리어에 대한 고민을 담고 있습니다.</p> <p>깃헙 첫 글이기도 하고 스스로 고민이 담긴 글이라 두서가 없을 수 있는 점 양해 부탁 드립니다.</p> <p>일단은 일기 형식으로 시작 해 보고자 반말로 작성</p> <h1 id="개발자-두가지-길-중-어디로-가야-하는가">개발자, 두가지 길 중 어디로 가야 하는가</h1> <h2 id="음-이길로-가면-되겠군">음 이길로 가면 되겠군</h2> <p>2018년 10월 아는 형에게 같이 교육 스타트업을 시작하며 강사를 해보지 않겠냐는 전화를 받게 된다. 이미 멀캠에서 보조강사로 약 2달간 한번 합을 맞춰 본 형님의 연락이었고, 늘 개발자로서 배울것도 많고 인간적으로도 참 멋있는 사람이라는 생각했기에 흔쾌히 수락했다. 살짝 갈등 된 것은</p> <ol> <li>서울이면 좋지만 <em>‘대전’</em>을 가게 될 수도 있다는 것 <ul> <li>눈 딱 감고 <strong>시간과 정신의 방</strong>에서 수련을 쌓고 나오는 손오공의 마음으로 가기로 마음 먹었다.</li> </ul> </li> <li>상반기에 <code class="language-plaintext highlighter-rouge">자격증</code>이나 <code class="language-plaintext highlighter-rouge">영어성적</code> ,<code class="language-plaintext highlighter-rouge">공채</code> 등에 지원하기 어렵다는 점 <ul> <li>중요한 시기에 커리어 스토리텔링이 개발자가 아닌 강사의 길이 될 수도 있다.</li> <li>결론은, 뭘 하던 내가 하기 나름이고 개발자로서 이 경험을 어떻게 표현하냐에 따라 달린 문제라고 판단</li> </ul> </li> </ol> <p><img src="http://pds54.cafe.daum.net/image/4/cafe/2008/06/15/13/47/48549ee534900" alt="시간과 정신의 방" /></p> <blockquote> <p>강사 일은 나에게 개발자의 내실을 다져 줄 시간과 정신의 방 이라 생각했다.</p> </blockquote> <p>11월이 되자 슬슬 강의안과 일정표를 받아 가르칠 것에 대해 스스로 공부 시작. Python Django를 가르치면 됐고, Ruby on Rails 기반 지식으로 공부를 시작했다. 기존 보조강사에서 주강사로 승격(?)되었지만 그만큼 할일도 많아지고 한 반을 책임져야한다는 부담의 무게는 애써 괜찮은 척 해도 늘 마음 한켠을 차지했고 그만큼 성장 할 수 있을 거라는 기대감이 들었다.</p> <p>그리고 이때까지는 이것만 길이라고 생각했다.</p> <h2 id="이-길도-있었잖아">이 길도 있었잖아?</h2> <p>그러던 어느 금요일, 홍대에서 친구와 만나 놀기로 한 날이었다. 약속시간 1시간 30분 전쯤 나가야 겠다고 생각하곤 2시간 전에 자리에서 일어나는데 문득 친구의 전화가 온다.</p> <blockquote> <p>친구: 너 바로 우리 회사로 와라</p> <p>나: 갑자기?</p> <p>친구: 누가 널 모셔가고 싶대. 빨리와</p> </blockquote> <p>지맴대로 하는 친구답게 앞뒤 다 짤라먹고 하는 그 말에 <strong>허풍은 있을 지 언정 없는 말을 하지는 않는 친구</strong>이기에 어차피 갈거 조금만 더 가면 되기에 뭔소리를 하는지 들어나 봐야겠다고 생각하고 예상보다 30분 일찍 집을 나섰다.(진짜 저 말이 다였음)</p> <p>알고보니 친구 회사의 이제 막 시작 한 사내벤처팀의 PM에게 나를 추천 한 것이었다. 이유는 이 팀에서 활용할 플랫폼이 <code class="language-plaintext highlighter-rouge">Chatbot</code> 이었고, 내가 그걸 다뤄본게 생각 나서 추천 했다는 것. 그리고, 마침 그날이 친구를 만나러 가는 날. 당시 미팅에서 PM의 열정과 야망이 내 맘을 뒤흔들었고 나는 마치 이 일을 위해 오늘을 살아온 사람마냥 이 스타트업에 꽂히게 된다.</p> <blockquote> <p>사실 이날 <code class="language-plaintext highlighter-rouge">강사</code>를 제안한 형과의 미팅이 먼저 있었고 부득이하게 취소되어 올 수 있었던 것이었다.</p> <p>그래서 이게 더 운명처럼 다가왔었을런지도.. 역시 사람 일은 당장 하루 앞도 내다 볼 수 없다.</p> </blockquote> <p>다만 여기에도 조그마할수도 클 수도 있는 걱정거리가 2개가 있는데</p> <ol> <li><code class="language-plaintext highlighter-rouge">선임 개발자</code> 없이 나 혼자 개발해야 한다는 것. <ul> <li>팀원이 나까지 총 3명인데, 배움에 있어서 <em>알려 줄 수 있는 사람의 부재</em>는 큰 것 같으면서도 또 결국 <em>공부는 혼자 하는 것</em> 이라는 생각들이 충돌했다.</li> </ul> </li> <li><code class="language-plaintext highlighter-rouge">Round 1</code>이 1월 말까지 인 점. <ul> <li>사실 1월 말에 프로젝트가 지속 가능성을 인정 받지 못하면 <code class="language-plaintext highlighter-rouge">Round2</code> 를 갈 수 없게 되고 해변의 모래성처럼, 하룻밤의 꿈처럼 모든게 흩어져 버릴 수도 있다.</li> </ul> </li> </ol> <h2 id="어느-길이-내-목적지로-향하는-길일까">어느 길이 내 목적지로 향하는 길일까</h2> <h3 id="강사-vs-스타트업">강사 vs 스타트업</h3> <table> <thead> <tr> <th style="text-align: left">강사의 커리어</th> <th>스타트업 개발자의 커리어</th> </tr> </thead> <tbody> <tr> <td style="text-align: left">대전으로 가며, 숙소가 제공된다.</td> <td>강서구로 가게되며, 부대비용이 다수 발생.</td> </tr> <tr> <td style="text-align: left">선임 개발자가 있다.</td> <td>혼자 스스로 뚝배기 깨지면서 공부해야 한다.</td> </tr> <tr> <td style="text-align: left">내가 미숙하게 아는것을 더 깊이 공부할 수 있다.<br />그리고 가르친다.</td> <td>모든것을 실전으로 경험하여 익숙 해 질 수 있다.</td> </tr> <tr> <td style="text-align: left">내년 상반기(19.01~19.05)까지 일하게 된다.</td> <td>사내 벤처이기 때문에 단기 성과가 있어야 <br />투자가 이어지면서 프로젝트를 이어 나갈 수 있다<br />1월 31일이 Round 1</td> </tr> </tbody> </table> <p>두개로 나눴지만 사실, 목표는 명확하다. 어쨋던 개발자가 되는 것.</p> <p>고민의 초점을 단순하게 <code class="language-plaintext highlighter-rouge">자기성장</code> 에 놓고 처음에는 당연히 <strong>강사</strong>를 선택했지만, 조금더 고민을 해 본 결과 내가 해본 것을 조금 더 깊게 가르치는 것보다, <strong>스타트업</strong> 에서 혼자 박살나게 깨지면 <code class="language-plaintext highlighter-rouge">그게 바로 참공부</code>가 아닌가 싶어 결론적으로 <strong>스타트업의 길을 선택</strong> 했다. 물론 이 과정에서 스타트업 PM이 정말 날 설득 해 주었다.</p> <h2 id="아모른직다">아모른직다</h2> <p>두개의 길을 놓고 하나의 길을 선택했지만, 선택 한 후에도 영 후련하지가 않다. 하지만 확실한 것은 두 길 모두 나에게는 엄청난 기회이고 내가 욕심이 많기에 이렇게 고민도 많이 한다는 것. 강사는 다음에 기회가 되면 꼭 다시 하는걸로 이야기를 하고 스타트업 사무실로 출근하는데 이곳에도 산넘어 산이 있었으니..</p> <p>다음 글에 이어서 써야지</p> Fri, 23 Nov 2018 00:00:00 +0000 https://samslow.github.io/diary/2018/11/23/Two-career-who-developer/ https://samslow.github.io/diary/2018/11/23/Two-career-who-developer/ diary diary