스토리 홈

인터뷰

피드

뉴스

조회수 1304

Event-Driven Programming

Overview마이크로 서비스 사이의 결합도를 낮추고 비동기적인 문제들을 처리할 때는 Event-driven 아키텍쳐가 유용합니다. 이번 글에서는 AWS에서 제공하는 SNS Topic을 이용해 Event-Driven을 알아보겠습니다. Event-Driven Programming프로그램의 제어 흐름이 이벤트의 발생에 의해 결정되는 컴퓨터 프로그래밍 패러다임입니다. publish/subscribe (이하 pub/sub)메시징서버리스 및 MSA에서 안정성 및 확장성을 높이기 위하여 사용되는 비동기 서비스 통신 방법입니다. 게시된 메시지를 다른 시스템에 비동기적으로 전달하고, Topic을 구독하는 모든 구독자는 모든 메시지를 받을 수 있습니다. 특히 게시자는 누가 구독하고 있는지 알지 않아도 되고, 구독자도 메시지의 출처를 알 필요는 없습니다. pub/sub 메시징 기본 / 출처: AWS Compute BlogAmazon SNS Topicpub/sub 방식의 메시징 서비스입니다. AWS의 여러 서비스들이 SNS에 이벤트를 게시할 수 있습니다. SNS Event Publishers / 출처: AWS Compute Blog위의 그림과 같이 구독자는 게시자 서비스에서 트리거된 이벤트에 응답해 필요한 작업을 진행합니다. 예시로 Elastic Transcoder 서비스에서의 Topic을 활용해보겠습니다. 네 가지의 순서를 거칩니다.1. SNS 토픽 생성2. Elastic Transcoder 등록Optional 항목인 Notification 영역에서 상태별 이벤트를 설정할 수 있습니다. On Completion Event에 위에서 생성한 Topic을 선택해 이벤트를 전달받도록 설정합니다. 3. SNS Topic에 구독자로 등록트랜스 코딩이 완료 후 처리할 프로세스를 가진 Lambda 함수 생성하여 위에서 생성한 SNS Topic에 구독자로 등록합니다. 현재 SNS Topic에서 제공하는 프로토콜은 HTTP, HTTPS, Email, Email-JSON, Amazon SQS, Application, AWS Lambda, SMS가 있습니다.4. 서비스 간 이벤트 전달출처: AWS Compute BlogSNS Topic으로 이벤트를 제공하는 AWS 서비스 중 하나를 살펴봤습니다. 이를 이용하면 마이크로 서비스 간에 이벤트를 전달하고 서비스의 분리 및 확장에 유용하게 사용할 수 있습니다.Conclusion오늘은 SNS Topic을 이용한 Event-Driven을 알아봤습니다. 다음 글에서는 마이크로 서비스에서 사용할 수 있는 AWS 서비스들을 다뤄보겠습니다.참고Event-Driven Computing with Amazon SNS and AWS Compute, Storage, Database, and Networking Services글이상근 팀장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1538

레진 기술 블로그 - SVG를 이용해 간단한 웹 게임 만들어보기

근래 소규모로 게임 프로그래밍 스터디를 시작했습니다. 서비스 UI를 개발하는 프론트엔드개발자에게 있어 게임 프로그래밍은 언제나 커튼 뒤에 비친 풍경처럼 흐릿하고 형체를 쉽게 알 수 없는 신비한 존재입니다. 이번에 미약하게나마 커튼을 걷어 창문 너머 펼쳐진 풍경을 감상해 보자는 게 이번 스터디의 개인적인 목표입니다.왜 SVG를 선택했나게임을 만드는 데 어떤 기술을 사용할지 고민했습니다. 일반적인 DOM은 쉽게 객체를 조작할 수 있지만, 문서의 엘리먼트를 추상화한 것에 불과하므로 다양한 도형을 만들거나 좌표계에 사상(寫像, Mapping)1하기 쉽지 않습니다.캔버스는 그래픽 처리에 환상적인 성능을 보여주고 원, 다각형 등 다양한 도형을 그리기 쉽지만 일일이 객체화해야 하고 이를 관리하기 쉽지 않습니다. 여기에 필자가 캔버스를 좀 처럼 써 본 경험이 없어서 무턱대고 사용하기에도 부담을 느꼈습니다.하지만 SVG는 이 두 장점을 모두 갖고 있습니다. 확장 가능한 벡터 그래픽(Scalable Vector Graphics)이라는 이름을 통해서 알 수 있듯이 그래픽 요소를 그리는데 적합한 포멧이며 DOM처럼 추상화된 객체도 지원합니다.어떤 게임을 만들었나필자가 만든 게임은 크롬에 내장된 Running T-Rex와 비슷한 것으로 JUMPING CAR라고 이름을 붙였습니다. 플레이해보고 싶은 분은 uyeong.github.io/jumping-car를 방문하시기 바랍니다.규칙은 단순합니다. 게임을 시작하면 자동차가 달려나가고 이윽고 장애물을 만나게 됩니다. 장애물을 뛰어넘으면 점수가 1씩 증가하지만 부딪히면 게임이 종료됩니다.이 글에서는 게임을 만드는 과정을 소개하기보다 SVG를 이용하면서 알게 된 몇 가지 주요한 내용을 다룹니다.Pattern을 사용한 요소는 느리다이미지를 반복해서 출력할 때 HTML에서는 CSS의 background-url 속성으로 간단히 해결할 수 있습니다. 하지만 SVG에서는 Pattern 요소를 이용해야 합니다.아래 그림처럼 pattern#pat-land 요소를 만들고 이를 rect.parallax에서 사용하여 그림을 반복 출력되도록 합니다. 그리고 rect.parallax를 조금씩 Transform 하여 앞으로 이동하도록 구현합니다.코드는 다음과 같습니다(예제: svg-parallax-test/parallax1).<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="..."> <defs> <pattern id="pat-land" x="0" y="0" width="..." height="100%" patternUnits="userSpaceOnUse"> <image x="0" y="0" xlink:href="../images/land.png" width="..." height="100%"></image> </pattern> </defs> <g> <rect class="parallax" x="0" y="0" width="..." height="100%" fill="url(#pat-land)" transform="translate(0,0)"></rect> </g> </svg> 표면상으론 전혀 문제가 없는 코드지만 크롬 브라우저에서 이 코드를 실행하면 프레임이 50 이하로 떨어지는 경우도 발생합니다. 이 정도면 육안으로도 화면의 움직임이 매끄럽지 않게 느껴지는 수치입니다.따라서 성능에 영향을 주는 pattern을 제거하고 image 요소로 대체합니다. image 요소는 자동으로 반복할 수 없으므로 두 개의 요소를 이어 붙여 사용합니다(예제: svg-parallax-test/parallax2).<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="..."> <g> <image x="0" y="0" xlink:href="../images/land.png" width="..." height="100%"></image> <image x="..." y="0" xlink:href="../images/land.png" width="..." height="100%"></image> </g> </svg> 실행 결과 프레임이 안정적이고 육안으로도 이질감을 느낄 수 없습니다. 이처럼 Pattern을 이용한 SVG 요소를 애니메이션 처리할 때에는 주의가 필요합니다.일부 안드로이드 기종에서의 성능 문제pattern을 제거하고 image로 대체하면서 Parallax 처리 시 발생한 문제를 해결할 수 있습니다. 하지만 image로 대체하더라도 일부 안드로이드 기종에서는 여전히 성능 문제가 발생합니다.아래 영상처럼 image 요소를 Transform 할 경우 프레임이 급격하게 떨어집니다. 이는 크롬 개발자 도구에서도 쉽게 발견하기 힘든데 CPU 성능을 10배 줄여 테스트해도 수치상으로는 크게 차이 나지 않기 때문입니다.<style>.video-container { position: relative; padding-bottom: 56.25%; padding-top: 30px; height: 0; overflow: hidden; } .video-container iframe, .video-container object, .video-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style><iframe width="560" height="315" src="https://www.youtube.com/embed/F_-zXf1jb8I?rel=0" frameborder="0" allowfullscreen="">이 처리를 DOM으로 바꿔보면 어떻게 될까. 놀랍게도 큰 차이를 보여줍니다(예제: svg-parallax-test/parallax3).<iframe width="560" height="315" src="https://www.youtube.com/embed/VXQ1aT79D2s?rel=0" frameborder="0" allowfullscreen="">SVG에 대한 최적화 상황은 브라우저마다 조금씩 다릅니다. DOM은 과거부터 최적화 노력이 많이 이뤄졌지만, SVG는 pattern 요소나 다음 절에서 이야기할 리페인팅 문제 등 성능 문제를 일으키는 부분이 아직 남아있습니다.따라서 충돌 계산처럼 특별히 좌표계 연산이 필요 없는 배경은 DOM으로 옮기고 자동차, 장애물만 SVG로 구현했습니다(예제: svg-parallax-test/parallax4).SVG는 항상 페인트를 발생시킨다SVG는 이상하게도 svg 요소의 크기를 고정하더라도 자식 요소를 변경하면 페인팅이 발생합니다. 아래는 svg 요소의 자식 요소인 rect의 좌표를 수정하는 예제 코드입니다.<svg"http://www.w3.org/2000/svg" width="500px" height="500px" viewBox="0 0 500 500"> width="500" height="500" x="0" y="0"> </svg> [removed] setTimeout(() => { rect.setAttribute('x', '100'); }, 3000); [removed] svg는 viewBox로 설정한 사이즈 만큼 내부에 그림을 그립니다. 즉, 내부의 어떠한 그래픽적 변화가 문서에 변화를 일으킬 가능성이 없습니다. 그래서 개인적으로 쉽게 이해가 되지 않는 렌더링 흐름입니다.그러면 SVG 요소의 크기나 좌표를 바꾸지 않고 색상 또는 투명도를 변경하면 어떨까요. 이번에는 rect 요소의 좌표가 아니라 색상을 바꿔봅니다.<svg"http://www.w3.org/2000/svg" width="500px" height="500px" viewBox="0 0 500 500"> width="500" height="500" x="0" y="0"> </svg> setTimeout(() => { rect.setAttribute('fill', '#ebebeb'); }, 3000); 그래도 페인트가 발생합니다. 하지만 앞서 진행한 테스트의 페인팅 시간은 수십 마이크로세컨드로 크게 의미가 없어 보입니다. 그래서 현재 서비스 중인 레진코믹스의 메인페이지에 SVG를 넣고 테스트했습니다.페인팅에 0.51ms가 소요됐습니다. 작다고 느낄 수 있지만 페이지 전반적으로 영향을 줄 수 있으며, 애니메이션 처리 중인 SVG라면 성능적 문제를 발생시킬 수 있는 부분입니다.그래서 svg 요소에 null transforms 핵을 선언해 문서 상위 레벨까지 페인팅이 전파되지 않도록 합니다.<svg"http://www.w3.org/2000/svg" width="500px" height="500px" viewBox="0 0 500 500" style="transform:translate3d(0,0,0)"> width="500" height="500" x="0" y="0"> </svg> 또는 아예 svg 내부의 요소를 개별로 분리하는 방법도 있습니다(참고: Doubling SVG FPS Rates at Khan Academy).<svg> fill="red" transform="translate(2px, 3px)"> fill="blue" transform="scale(2)"> </svg> style="transform:translate(2px, 3px)"> <svg> fill="red"> </svg> style="transform:scale(2)"> <svg> fill="blue"> </svg> 끝으로여기까지 SVG를 이용해 게임을 개발하면서 만나게 된 이슈와 해결 방법을 간단히 정리했습니다.필자는 간단한 게임은 SVG로 만들 수 있고 괜찮은 성능을 보장할 것이라고 기대했습니다. 하지만 현실은 달랐습니다. 이 글에서 다룬 문제 외에도 사파리와 크롬 브라우저의 성능 차이, 자동차를 움직일 때 버벅이는 현상 등 다양한 문제를 해결해야 했습니다. 객체의 개수도 적고 애니메이션도 복잡하지 않은 단순한 게임이었는데 말이죠.다음 게임은 캔버스로 시작하고자 합니다.공간(空間)의 한 점에 대(對)하여, 다른 공간(空間) 또는 동일(同一)한 공간(空間)의 한 점(點)을 어떤 일정(一定)한 법칙(法則)에 의(依)하여 대응(對應)시키는 일 ↩
조회수 1752

타운어스 신규플랫폼을 오픈하며

안녕하세요. 타운컴퍼니 R&D 파트의 백대현입니다.타운컴퍼니는 단체들를 위한 공동구매 플랫폼인 타운어스를 운영하는 스타트업입니다. 기술블로그를 시작하며 최근 오픈한 타운어스 2.0에 대한 소개, 타운컴퍼니 기술조직인 R&D 파트에 대한 소개를 드리려 합니다.타운어스는 대학생들이 학교 내에서 자주 진행하는 공동구매가 굉장히 복잡하고 만족하기 어렵다는 문제를 해결하기 위해 시작되었습니다. 수작업으로 진행하던 공동구매를 플랫폼화 시켜 편하게 공동구매를 진행할 수 있게 만들고, 전국단위로 공동구매를 진행하여 최저가로 구매할 수 있도록 하였습니다.2014년 9월 오픈베타서비스인 캠퍼스샵을 시작하였고 이어서 2015년 6월 정식서비스 타운어스를 런칭하여 2년 넘게 폭발적으로 성장해왔습니다.사실 그 동안 타운어스 사이트에 대한 아쉬움이 많았습니다. 사이트가 초기에 린(Lean)하게 만들어진 이후에 몇차례 업데이트를 거쳐 타운어스 비즈니스를 받쳐주고 있었지만 내부 개발팀의 부재, 인력부족으로 유지보수가 잘 되지 않고 있었습니다. 그 와중에 몇 차례의 비즈니스 전략 변경(Pivot)을 거치면서 기술과 비즈니스의 괴리가 점점 벌어지게 되었습니다.타운어스 신규플랫폼타운컴퍼니에서는 여러 고민을 거쳐 한층 업그레이드 된, 이제 대학생 뿐만 아니라 모든 단체를 위한 공동구매 커머스 플랫폼으로 거듭나고자 타운어스 2.0 프로젝트를 시작하며 기획팀, 디자인팀, 개발팀으로 이루어진 R&D 파트를 출범하고 기술조직을 구성하였습니다.새로 출범한 타운컴퍼니 R&D 파트에서는 물려받은 기존 코드베이스를 유지하며 플랫폼을 개선할 것인지, 부채를 청산하고 새로운 코드베이스를 쌓을 것인지 많은 고민을 했습니다. 결론적으로 새로운 코드베이스로 프로젝트를 진행하기로 하였는데 대표적인 이유는 아래와 같았습니다.타운어스 2.0에서 변경되어야할 기획이 상당히 많아 재설계가 필요함비즈니스적 전략 변경을 서비스에 반영하기 위해 변경해야할 기획이 상당히 많고 그 동안 기술에 반영되지 않은 비즈니스 요구사항이 많아 여려 요구사항이 복합적으로 고려되어야함서비스 아키텍처가 연속성이 없음초기에 MVP로 시작된 코드베이스가 연속적인 기술조직이 발전시키지 못하고 외주 개발과 인력 변경을 거치다 보니 일관적이지 않고 누수가 많음버그 빚더미인력부족으로 인해 그 동안 유지보수가 되지 않아 해결해야할 버그가 복합적으로 존재함 (해결되지 않은 리포팅된 이슈 450여개 OMG)위 외의 여러 이유로 타운컴퍼니 R&D 파트에서는 기술부채 파산을 선택하고, 새로운 코드베이스에서 프로젝트를 시작하기로 하였습니다.신규플랫폼 간략 소개결론적으로 타운어스 신규플랫폼은 전체적인 일정과 인력을 고려하여 우선 부분적으로 오픈하게 되었습니다. 가장 많은 사용자들이 사용하는 단체의류공동구매에 대한 서비스를 먼저 개발하게 되었고 그 외 카테고리의 공동구매는 기존플랫폼에서 서비스하고 있습니다.이렇게 시작한 타운어스 신규플랫폼의 현재 오픈한 변경 사항은 크게 아래와 같습니다.전체적인 UX/UI 업데이트공동구매를 더 편하게 시작하고 진행하고 마무리할 수 있도록 개선“공동구매방”을 친숙하게 사용할 수 있도록 유도하였습니다.단체의류 커스터마이징 기능 강화커스터마이징 의류 종류 확장 : 과잠, 코치자켓, 티셔츠 (점진적으로 확대할 예정)커스터마이징 예상가격 계산 기능편하게 단체의류 디자인을 미리 해볼 수 있는 서비스를 제공합니다.최근 오픈을 시작으로 타운컴퍼니 R&D 파트에서는 지속적으로 서비스를 발전시켜 세상 모든 단체활동을 위한 공동구매, 타운어스에 걸맞는 공동구매 커머스 플랫폼으로 만들어갈 예정입니다.기술 스택과 조직 문화타운컴퍼니 R&D 파트의 기술에 대해서는 꾸준히 소개를 드리려 합니다. 때문에 오늘은 조직 문화와 기술 스택에 대해서 간단하게 소개를 드리겠습니다.프로젝트 진행협업툴로 JIRA, Confluence, Slack을 사용하고 있습니다.프로젝트는 Agile Kanban 방식으로 테스트 주도 개발, 코드 리뷰, 페어프로그래밍을 통해 진행하고 있습니다.서비스에 대한 충분한 고민 이후에 개발을 진행하려 노력합니다.기술 스택Back-End는 Django 1.11 (DRF) 기반으로 개발하며, AWS, MySQL, Vagrant, Docker 같은 기술을 사용하고 있습니다.Front-End는 Angular 5.1을 사용해서 개발하고 있으며 Less, RxJS, Webpack 등의 기술을 사용하고 있습니다.UX/UI에 Sketch와 Zeplin을 주로 사용하고 있습니다.CI(Continuous Integration)로 Travis-CI, 배포 관리로 Fabric과 AWS CLI, 버그 리포팅으로 Sentry.io, 플랫폼 모니터링으로 ELK(ElasticSearch, Logstash, Kibana)를 사용하고 있습니다.지향하는 조직 문화 및 지원자유롭고 주도적인 조직 환경시간에 쫓겨 비루한 코드를 생산하지 않도록유연한 리모트 근무지시가 아니라 스스로 할 일을 찾는 자율 속에서 책임을 다하는 문화자유롭게 의견을 제시할 수 있는 편한 분위기건강한 비판과 토론을 장려하는 커뮤니케이션 문화이상을 추구하되 비즈니스에 기반한이상적인 기획, 디자인, 개발을 지향하지만 비즈니스 영역에 기반한 의사결정회사 모든 조직들이 더 부가가치가 높은 업무에 집중할 수 있는 환경 조성을 위해 노력TOY TIME매주 금요일 4시부터 7시까지 3시간 자유로운 주제로 프로젝트 진행세미나, 컨퍼런스 참가 적극 장려 및 도서 지원저희는 아직 성장하고 있는 만큼 개선할 점도 많고 배워야하는 부분도 많이 있습니다. 그 만큼 기술블로그를 통해 저희가 고민하고 겪었던 기술을 공유하고 소통하며 서로 성장할 수 있는 기회가 되었으면 합니다.잘 부탁드립니다.#타운컴퍼니 #서비스소개 #기업문화 #사내복지 #조직문화 #원격근무 #디지털노마드 #재택근무
조회수 4221

LSTM Tutorial

Summary:이 포스팅은 LSTM에 대한 기본 개념을 소개하고, tensorflow와 MNIST 데이터를 이용하여 구현해봅니다.LSTM1. 개념 설명LSTM(Long Short Term Memory)은 RNN(Recurrent Neural Networks)의 일종으로서, 시계열 데이터, 즉 sequential data를 분석하는 데 사용됩니다.기존 RNN모델은 구조적으로 vanishing gradients라는 문제를 가지고 있습니다. RNN은 기본적으로 Neural network이기 때문에 chain rule을 적용하여 backpropagation을 수행하고, 예측값과 실제 결과값 사이의 오차를 줄여나가면서 각 시간 단계의 gradient를 조정합니다. 그런데, 노드와 노드(시간 단계) 사이의 길이가 길어지다보면, 상대적으로 이전의 정보가 희석됩니다. 이 문제는 시퀀스 상 멀리 떨어져 있는 요소, 즉 오래 전에 발생한 이벤트 사이의 연관성을 분석할 수 없도록 만듭니다.LSTM은 RNN의 문제를 셀상태(Cell state)와 여러 개의 게이트(gate)를 가진 셀이라는 유닛을 통해 해결합니다. 이 유닛은 시퀀스 상 멀리 있는 요소를 잘 기억할 수 있도록 합니다. 셀상태는 기존 신경망의 은닉층이라고 생각할 수 있습니다. 셀상태를 갱신하기 위해 기본적으로 3가지의 게이트가 필요합니다. Forget, input, output 게이트는 각각 다음과 같은 역할을 합니다.Forget : 이전 단계의 셀 상태를 얼마나 기억할 지 결정합니다. 0(모두 잊음)과 1(모두 기억) 사이의 값을 가지게 됩니다. Input : 새로운 정보의 중요성에 따라 얼마나 반영할지 결정합니다. Output : 셀 상태로부터 중요도에 따라 얼마나 출력할지 결정합니다.게이트는 가중치(weight)를 가진 은닉층으로 생각할 수 있습니다. 각 가중치는 sigmoid층에서 갱신되며 0과 1사이의 값을 가지고 있습니다. 이 값에 따라 입력되는 값을 조절하고, 오차에 의해 각 단계(time step)에서 갱신됩니다.2. 응용 (MNIST data)MNIST는 손으로 쓴 숫자 이미지 데이터입니다. 하나의 이미지는 가로 28개, 세로 28개, 총 784개의 값으로 이루어져 있습니다.Many-to-One model는 여러 시퀀스를 넣었을 때 나오는 최종 결과물만을 이용하는 모델입니다. 이를 이용하여 784개의 input으로 1개의 output값(A) 을 도출합니다. 이 A를 하나의 층에 통과시켜 10개의 숫자 label중 하나를 할당합니다.784개의 입력값을 사이즈가 28인 벡터가 28번 이어지는 시퀀스(time step)로 보고, input의 크기를 28, 시퀀스 길이를 28로 각각 설정합니다. 28개의 input은 C라고 표현되어 있는 LSTM 셀로 순차적으로 들어가게 됩니다.output의 크기는 셀의 크기와 같으며, 64로 설정하였습니다. 셀크기가 너무 작으면 많은 정보를 담지 못하기 때문에 적당히 큰 값으로 설정합니다. 전체 output은 64개의 값을 가지고 있는 벡터 28개의 집합이 되고, 마지막 벡터만 사용합니다.1층의 fully connected layer를 이용하여 64차원 벡터를 10차원으로 줄이고 softmax를 이용하여 0부터 9까지 중 하나의 값을 예측합니다.LSTM으로부터 나온 예측값을 실제갑과 비교하여 cost를 개산합니다. cost function은 cross-entropy를 이용합니다. AdamOptimizer를 이용하여 cost를 최소화하는 방향으로 모델을 최적화 시킵니다.3. 토의구현 시 어려웠던 점을 중심으로 서술하였습니다. 전체 코드는 여기를 참고해주세요.batch sizebatch_size = 128 batch_x, batch_y = mnist.train.next_batch(batch_size) MNIST의 train data의 크기는 55,000개 입니다. 이는 (55000, 784) 크기의 데이터를 학습시켜야 한다는 것을 의미합니다. 이것을 한번에 학습시킨다는 것은 매우 어려운 일입니다. 전체 데이터를 메모리에 올리기 힘들뿐만 아니라, 너무 큰 data 한번에 학습시키면 가장 작은 cost값으로 수렴하기 힘들어진다는 문제가 있습니다. (너무 작아도 마찬가지입니다.) 그렇기 때문에 큰 덩어리를 일정크기의 작은 덩어리로 잘라서 모델에 넣어 학습시는데, 이 작은 덩어리의 크기를 batch size라고 합니다.작은 덩어리로 짜르는 것이 중요한 이유는, 작은 덩어리 단위로 모델에 밀어넣고(propagation) 네트워크의 파라미터들을 조정(update)하기 때문입니다. batch size는 분석하려고 하는 데이터가 어떻게 구성되어있는지에 따라 결정되는 경우가 많습니다. 어떤 수준의 batch size가 좋다고 이야기하기 어렵고, 아주 크지 않은 값으로 설정합니다.unstack모델 구현 시 static RNN을 사용하였습니다. Static RNN에서는 unstack을 해주지 않으면 TypeError가 발생합니다.unstack( value, num=none, axis=0, name=‘unstack’)unstack은 R차원(rank)의 데이터를 R-1 차원으로 줄여주는 역할을 합니다. value로부터 axis 차원을 기준으로 num개로 자른다고도 할 수 있습니다. 이 예제로 예를 들어보겠습니다.batch_x = batch_x.reshape((batch_size, input_steps, input_size)) x = tf.unstack(X, input_steps, axis=1) outputs1, states1 = tf.nn.static_rnn(lstm_cell, x, dtype=tf.float32) 실제 학습이 진행되는 순서로 보자면, batch size만큼 불러온 인풋 데이터는 (128, 784)에서 (128, 28, 28) 형식의 3차원 벡터로 reshape해 줍니다. 그리고 다시 unstack을 통해 time step을 기준으로(axis=1) 28개의 텐서를 만듭니다. 다시말해, (128, 28, 28)이라는 3차원 형식의 벡터는 (128, 28)이라는 2차원 벡터 28개로 변환되어 모델에 입력되게 됩니다. 이런 변환이 필요한 이유는 28*28의 크기를 가진input들을 차례로 넣게 되면 처리속도가 제한적이기 때문입니다. unstack을 이용하면 하나의 batch 안에 있는 input을 한꺼번에 한줄씩 병렬적으로 처리할 수 있게 됩니다.Dynamic RNN에서는 unstack을 해주는 과정이 필요 없습니다. Static과 Dynamic의 차이는 추후 포스팅에서 자세히 다루도록 하겠습니다.Training cycle참고한 다른 예제코드들은 서로 다른 스타일의 사이클로 학습시키고 있었습니다. 스타일은 크게 두가지로 나누어볼 수 있었습니다. 하나의 방법은 전체 학습 횟수를 정해놓고 while문을 통해 학습시키는 방법이었습니다. 다른 방법은 똑같은 데이터를 몇번 반복해서 학습시킬지 결정하는 것입니다. 이 반복 횟수를 epoch이라고 합니다. epoch의 사전적 의미는 ‘시대’ 또는 ‘세’이지만 예제 코드에서 만나는 epoch은 전체 데이터를 학습시키는 반복회수라고 이해하시면 되겠습니다. (이 두가지 방법은 스타일의 문제일 뿐입니다. 이것을 언급한 이유는 개인적으로 epoch을 처음 접했을 때 생소했기 때문입니다.for epoch in range(training_epochs): avg_cost = 0 total_batch = int(mnist.train.num_examples/batch_size) for i in range(total_batch): batch_x, batch_y = mnist.train.next_batch(batch_size) batch_x = batch_x.reshape((batch_size, input_steps, input_size)) c, _ = sess.run([cost2, optimizer2], feed_dict={X:batch_x, Y:batch_y}) avg_cost += c/total_batch 위의 코드는 두번째 스타일이고, 각 epoch마다 cost값과 test data로 예측의 accuracy를 계산하여 출력하였습니다. 당연하게도 학습이 반복 될수록 cost는 감소하고 accuracy는 증가하였습니다.4. 정리기본적으로 도식을 통해 input size, time step, hidden_size에 대한 개념을 이해하는 것이 도움이 됩니다.tensor의 shape을 이해하는 것이 중요하다고 생각합니다. input과 output의 형식(shape)을 머리속에 떠올릴 수 있다면 에러를 줄일 수 있고 해결하기도 수월합니다.batch size의 의미, unstack을 하는 이유, epoch의 의미를 알아두면 좋겠습니다.ReferenceDEEPLEARNING4J 초보자를 위한 RNNs과 LSTM 가이드Colah’s blog, Understanding LSTM Networks이태우, 엘에스티엠 네트워크 이해하기김성훈, 모두의 딥러닝 lec 9-2. Vanishing gadient
조회수 908

할아버지/할머니도 코딩을 해야 하는 이유

대부분의 교육은 초, 중, 고등학생이나 대학생 등 주로 젊은 층을 위주로 진행되고 있습니다. 프로그래밍 교육도 마찬가지로, 현재 10대에서 30대인 주로 젊은 층의 학생과 직장인들을 대상으로 교육이 서서히 일어나고 있습니다. 하지만 높아진 평균 수명으로 노인층이 급격히 늘어나고, 빠르게 변화하는 산업 아래 노인층의 재교육을 통한 지속적인 사회 활동이 요구 되는 시대가 되었습니다.2016년 한국의 인구분포도. 42–57세의 중장년층이 15–24의 청년층보다, 청년층이 0–9세의 유아층보다 월등히 많습니다. Wikipedia위 그래프에서 보이는 것처럼 이렇게 사회의 전체적인 평균 연령의 급격한 상승이 예고되어있음에도 불구하고, 고등학교나 대학까지의 일회성 교육이 아닌 전 연령층을 대상으로 한 지속적인 교육 제공은 아직 보편화 되어 있지 않습니다. 노인층 대상으로 진행되는 교육은 미미하며, 특히나 젊은층도 배우기 어려운 코딩 교육은 노인층에게는 교육이 불가능하거나 전혀 필요하지 않다고 여겨지고 있습니다.UC San Diego 대학의 Phillip Guo 교수Phillip Guo 교수는 HCI (사람-컴퓨터 인터랙션) 및 온라인/컴퓨터 교육 분야에서 명성이 높은 연구자입니다. Guo 교수는 처음으로 노인층에 대한 코딩 교육 연구를 진행하여 온라인에서 프로그래밍을 배운 52개국 60~80대 504명으로부터 다양한 설문조사와 심층조사를 진행한 결과를 CHI 국제 학술회에 출간했습니다. 본 연구 설명과 함께 엘리스에서 생각하는 로드맵을 소개합니다.연구본 연구는 http://www.pythontutor.com 웹사이트에서 실시된 온라인 코딩 교육 설문조사에 응한 504명의 60~85세 학생에 대한 심층 분석과 인터뷰로 이루어져있습니다. 이들이 코딩을 배우는 목적은 세가지 주요 요점으로 종합됩니다.첫째는 코딩을 배움으로서 노화되는 뇌를 자극하기 위함이고, 둘째로 젊은 시절 놓쳤던 새로운 기회를 잡기 위함, 그리고 마지막으로 어린 가족 구성원들과 소통하기 위함이었습니다.혼자 공부하는 방식의 교육은 온라인에서 특히 더 높은 이탈율을 보입니다.이들이 프로그래밍을 배우는 원동력은 교육을 통한 취업과 같은 정확히 정해진 목표보다는, 스스로의 동기부여 및 젊은층과의 소통을 위한 이유가 더 컸습니다. 코딩을 배우는 과정 중에 가장 힘든 세가지는 감퇴하는 인지력, 질문에 대답해 줄 수 있는 강사나 조교 혹은 학생이 없었고, 매번 변화하는 SW를 따라가기 어려움이 있었습니다. 첫번째를 제외한 나머지 어려움은 다른 연령층에서도 겪은 어려움이었습니다.마치며Philip Guo 교수의 논문에서 알 수 있는 것은 노인층이 노화하면서 겪을 수밖에 없는 배움의 어려움과 더불어, 현재 교육 시스템이 노인층을 전혀 고려하지 않고 있다는 것입니다. 이것은 노인층 대상의 교육을 더욱 어렵게 합니다.논문에서는 노인층에게 적합한 교육 시스템이 만들어지거나 제공된다면, 이들이 산업에 바로 투여될 수 있는 능력을 갖추기는 어려울 수도 있으나 프로그래밍 교육을 할 수 있는 선생님으로 활동할 수 있다고 서술하고 있습니다. 이를 활용하면 현재 현저히 부족한 SW 교육자 수로 어려움을 겪고 있는 공교육에 도움이 될 수 있습니다.엘리스에서는 라이브 교육 방송 진행, 헬프 센터 조교 도우미 등 학생들에게 좋은 교육을 제공하기 위해 부단히 노력하는 다양한 연령층의 온라인 조교님들이 계십니다. 언젠가는 60~80대 조교님이 활동하실 수 있다고 믿고 있습니다. 이러한 믿음을 주신 중2 아들을 둔 한 어머니의 피드백을 참조합니다. (엘리스 기초 자바 과정에서 최상위 점수를 받으셨습니다.)저는 전공도 인문학쪽이고 수학 싫어서 문과갔던 문과생인지라, 코딩처럼 논리력 요구하는 수업 따라가기나 할까 큰 기대없이 시작했었습니다.수업 초반에는 마냥 어리둥절했고, 시키는대로 따라하면 다 되었기 때문에 ‘어라 쉽잖아?’ 라고 느꼈습니다. 하지만 중반부 넘어가면서…클래스, 메소드라는 개념이 낯설기도 했고, 각종 연산자의 적용이나 변수들을 식에 적용시키는 다양한 패턴들이 적응이 잘 안되었어요. 반복문의 순서나 마침표,세미콜론, 콜론을 기억하지 못해서 다시 되돌아와서 확인한 것만도 수 십번이었습니다.다른 분들은 마치 초급 과정을 어디서 마스터 하고 온 것처럼 잘 따라가시는데, 저는 매 시간마다 헤매고 오류나고…하지만 똑똑한 것 보다 꾸준한 것이 더 낫다고… ‘머리가 안따라가면 더 오래 공부하면 되겠지’ 하고 다시 보고, 다시 풀고, 계속 질문하고그러나보니 어느 순간 이해가 가는 개념들, 저절로 외워지는 패턴들이 조금씩 늘어났어요.특히 실시간 강의라서 피드백을 받을 수 있고, 조교님이나 강사님들께 질문을 편하게 할 수 있는 시스템이 정말 좋았습니다. 비주얼 좋은 두 분이 수업을 진행해 주신 것도 좋았구요. 반응 좋은 우리 반 수강생들도 참 좋았습니다.저녁 설거지 해 두고 (때로는 저녁상을 치우기 바쁘게) 컴퓨터 앞에 앉아서 8주간 공부한 시간들이 저한테는 정말 소중한 시간이었습니다. 이렇게 집안 일 하고, 애들 챙기면서도 공부할 수 있고, 배울 수 있다는 것이 너무 좋습니다. 좋은 강의 열어주셔서 고맙습니다. ^^*p.s.수업 중에 어떤 분이 자바 알고리즘 강의 열어달라고 하시던데, 알고리즘이 뭔지 물어보고 싶었는데 못 물어봤네요 ㅋ#엘리스 #코딩교육 #교육기업 #기업문화 #조직문화 #서비스소개
조회수 2185

프로세스 마이닝과 AI를 통한 프로세스 혁신

지난해 이세돌과 알파고의 대결 이후에 인공 지능 (AI)과 기계 학습은 국내에서 많은 대중들의 관심을 얻어 중요한 추진력을 얻었으며, 모든 산업 분야의 기업들이 해당 기술을 빠른 속도로 계속 적용하여 사용하는 비중이 더욱 높아졌습니다. 실제로 Gartner는 2022년까지 스마트 머신과 로봇이 고학년 전문직 분야를 대체할 수 있을 것으로 내다봤으며, 심지어는 인공지능이 경영자 CEO도 대체 가능할 것인지에 대한 논의도 일어나고 있습니다. 이것은 사람이 과거 경험에 의해서 의사 결정을 내리 듯이 인공 지능도 확보한 데이터를 기반으로 의사 결정 모델을 만들 수 있다는 유사성에 기반합니다.  인공 지능에 의한 의사 결정은 사람한테 종종 있을 수 있는 감정이나 개인적 이해관계 및 관례에 의해 불합리한 판단에서 벗어나 데이터의 의한 객관적 판단을 할 수 있다는 장점이 있습니다.여기서 중요한 것은 인공지능이 학습하기 위한 “데이터”입니다.  지금까지 머신러닝이 막대한 이미지, 음성, 영상 데이터를 축적한 후 해당 데이터의 특징을 추출하여 패턴을 학습하여 자연어 처리 등을 통해 사람처럼 인식하여 분류하거나 상황을 판단하였듯이 기업 내 여러 가지 업무 활동에 머신 러닝을 적용하기 위해서는 이와 마찬가지로 관련 데이터가 필요합니다.제조 분야의 공정 관리, 공공 서비스, 물류 공급망 관리 등 전통적인 기업 내 업무 프로세스는 인공 지능에 의한 자동화과 효율화를 통해 혁신이 필요한 분야입니다. 기존에 외부 협력 업체로부터의 납기 예측, 소요되는 자재 인력 등 리소스 산정, 생산 스케줄, 장비 파라미터 입력값 등은 사람에 의해 수작업으로 진행 시 몇 주에서 수개월 소요되었지만, 인공 지능과 기계 학습 기반의 솔루션 도움으로 정확하게 지속적인 추세를 인식하고 인간의 개입 없이 데이터 중심의 결정이 가능해집니다.지금까지 기업 내 축적된 엄청난 양의 데이터를 활용하여 여러 산업 분야에서 숨겨진 패턴과 상관관계, 이상 징후 및 불량 탐지, 고객 수요 예측 등이 시도되었습니다. 하지만 이러한 시도들은 기업 내 문제 요인을 파악하여 우선적으로 어떤 부분에 초점을 맞추어 개선을 해야 하는지 알아야 하므로, 기업 경영 활동 전반에 걸쳐 돌아가는 판세를 읽는 노력이 필요합니다. 하지만, 기업 내에서 이뤄지고 있는 프로세스는 충분히 복잡하여, 개별 단위 작업의 전문가들은 존재하겠지만, 각 개별 부서, 구성원, 시스템 간에서 발생하는 다양한 상호작용과 이에 따른 예외 상황이 존재하여 이를 파악하기가 쉽지 않습니다.프로세스 마이닝은 데이터 기반의 프로세스 분석을 통해 문제 부분을 파악하여, 실제 인공 지능이나 머신 러닝을 적용하여 개선할 부분을 찾을 수 있도록 도와줍니다. 그리고, 프로세스 개선을 위해 머신러닝을 적용하기 위해서는 앞서 말한 것처럼 “데이터”가 학습될 수 있는 형태의 기반을 제공합니다.아래 그림과 같이 이벤트 로그를 기반으로 프로세스 모델을 생성하고, 수집된 패턴들과 각 분기 단계에서의 주요 성과 지표들을 디지털화하여 인공지능이 이해할 수 있는 형태로 축적합니다 이렇게 축적된 프로세스 패턴 데이터를 가지고 알파고가 최적화된 다음의 한 수를 예측하듯이 프로세스 마이닝은 인공 지능 기술과 결합하여 과거 프로세스에 대한 이해뿐만 아니라, 현재 시점에서 앞으로의 프로세스를 예측하여 합리적인 의사 결정을 도와줄 것입니다.#퍼즐데이터 #개발팀 #개발자 #개발후기 #인사이트
조회수 2025

다양한 형태를 지원하는 리스트 UI, 잘 그리고 계신가요?

대략 1년 반 전, 5.0 롤리팝과 함께 나타난 RecyclerView. ListView 를 이용할 때 아주 기초적이고 정석적인 개념으로 사용되던 ViewHolder pattern 을 반 강제화? 하면서 동시에 성능까지 개선한 ListView 의 개량버전.앱 시장이 활성화되면서 한 가지 타입의 뷰만 반복적으로 보여주는 단순한 구성보다는 다양한 타입의 뷰를 보여주는 앱들이 많아지고 보편화 된 시점에 이것을 구현하기 위한 Adapter.getView 메소드는 혼돈.chaos 가 되었지요. 가독성을 높일만한 나름대로의 시도를 해보고 있을 때, RecyclerView 가 갑툭튀 했고 이걸 이용하면 원하는 만큼의 많은 타입의 뷰를 “가독성 좋게 만들어 볼 수 있겠다” 라는 생각이 들었습니다.그래서 RecyclerView.Adapter 를 상속 받아 다양한 타입의 뷰를 바인딩 할 수 있게 도와주는 헬퍼 클래스, MultiItemAdapter 라는 것을 만들어 보게 됐습니다. 구 회사 프로덕트에 적용해보기도 하고, 개인 프로젝트에 넣어보기도 하고, 토스랩에서 서비스하고 있는 “잔디”에 녹여내보기도 했는데 나쁘지 않은 느낌이들어 그 과정을 공유하고 많은 분들께 피드백도 받고 싶습니다. 또, 어떻게 더 잘 활용하고 계신지 여쭙고 싶습니다.RecyclerView.Adapter 의 이해를 위해 단순단순하게 만들어보자public class BasicAdapter extends RecyclerView.Adapter { private List mItems = new ArrayList<>(); @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.mTextView.setText(mItems.get(position)); } class MyViewHolder extends RecyclerView.ViewHolder { private TextView mTextView; public MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } } ... 이런 식으로 구현하면 되는군, 하지만 내가 최종적으로 원하는 건 다양한 ViewHolder 를 다뤄야 되는 건데 ViewHolder 가 많아지는 경우 inner class 는 쓰면 안되겠다! ViewHolder 들은 따로 패키지 만들어서 관리하자. 음 근데 ViewHolder 를 구성하고 난 다음 어떻게 그려지는 지에 대해 궁금하면 다시 어댑터를 찾아가야 되고, 반대로 어댑터에서 ViewHolder 내 구성요소가 어떻게 생겼는지 궁금하면 다시 ViewHolder 찾아가서 뒤져봐야되는 군. 이건 비효율 적인 것 같다. ViewHolder에 뷰를 그리는 메소드를 하나 만들자. 아 기왕이면 추상화된 클래스를 만들어 돌려돌려 쓰자. 하나 더 Generic 을 사용하자.public abstract class BaseViewHolder extends RecyclerView.ViewHolder { public BaseViewHolder(View itemView) { super(itemView); } public abstract void onBindView(ITEM item); } 뷰를 그리는데 쓰이는 객체는 Generic 을 이용하면 ViewHolder 안에서 그리는 작업 또한 해결이 가능하겠군! 이걸 이용해서 다시 만들어보자.public class MyViewHolder extends BaseViewHolder { private TextView mTextView; public MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } @Override public void onBindView(String item) { mTextView.setText(item); } } ... public class BaseAdapter extends RecyclerView.Adapter { private List mItems = new ArrayList<>(); @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.onBindView(mItems.get(position)); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemCount() { return mItems.size(); } } 음 원하는 모양새다. 근데 이제 Adapter 에선 ViewHolder 에 들어갈 layout 이 어떤 건지 관심꺼도 되겠네. 게다가 ViewHolder 에서 layout 궁금하면 다시 또 찾아와야 되는게 문제다. 좀 더 명시적인 방법으로 Factory method 로 생성자를 제한해보자. RecyclerView.ViewHolder 는 View 를 가지는 생성자가 강제되니 이렇게 바꾸자.public static MyViewHolder newInstance(ViewGroup parent) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new MyViewHolder(itemView); } private MyViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(android.R.id.text1); } 이렇게 하면 어떤 layout 을 다루고 있는지도 금방 알 수 있겠다. 이 정도만 되도 구색을 다 갖춘듯하니 이 느낌으로 다양한 타입의 뷰들을 다뤄보자.public class BasicMultiTypeAdapter extends RecyclerView.Adapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; private List mItems = new ArrayList<>(); @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { return AViewHolder.newInstance(parent); } else { return BViewHolder.newInstance(parent); } } @Override public void onBindViewHolder(BaseViewHolder holder, int position) { holder.onBindView(mItems.get(position)); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemCount() { return mItems.size(); } @Override public int getItemViewType(int position) { if (position % 2 == 0) { return VIEW_TYPE_A; } else { return VIEW_TYPE_B; } } } 음 깔끔하긴 하다. 근데 getItemViewType 이 스크롤 할 때마다 불릴 텐데, 분기도 많고 연산이 생겼을 때 스크롤 속도에 괜한 영향을 줄 듯? view type 을 차라리 미리 가지고 있게 만들자. 또! 가만보니 한 타입의 객체를 이용해서 다른 스타일로 뷰를 보여줄 뿐이었네. 이것도 여러가지 객체를 담을 수 있게 만들어야지.뷰를 그릴 대상이 될 객체랑 타입을 가지는 Wrapper class 를 만들어서 해결하자. 이러면 Adapter.onBindViewHolder 랑 Adapter.getItemViewType 도 해결이 되겠군.public abstract class MultiItemAdapter extends RecyclerView.Adapter { private List mRows = new ArrayList<>(); @SuppressWarnings("unchecked") @Override public void onBindViewHolder(BaseViewHolder holder, int position) { holder.onBindView(getItem(position)); } @SuppressWarnings("unchecked") public ITEM getItem(int position) { return (ITEM) mRows.get(position).getItem(); } public void setRows(List mRows) { mRows.clear(); mRows.addAll(mRows); } @Override public int getItemCount() { return mRows.size(); } @Override public int getItemViewType(int position) { return mRows.get(position).getItemViewType(); } public static class Row { private ITEM item; private int itemViewType; private Row(ITEM item, int itemViewType) { this.item = item; this.itemViewType = itemViewType; } public static Row create(T item, int itemViewType) { return new Row<>(item, itemViewType); } public ITEM getItem() { return item; } public int getItemViewType() { return itemViewType; } } } MultiItemAdapter 완성.네, 저는 이렇게 만들어서 1년 반 정도 필요한 부분(복잡해 질만한 부분)에 이 클래스를 상속받아 구현했습니다. 사용방법을 예로들어 데이터베이스나 서버로부터 긁어온 아이템들을 타입에 따라 A, B로 나눠서 보워줘야 한다면,// MutiItemAdapter 구현 public class AdvancedItemAdapter extends MultiItemAdapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { return AViewHolder.newInstance(parent); } else { return BViewHolder.newInstance(parent); } } } // Activity 나 Fragment 등 view 요소에서 ListAdapter item setting. public void setItems(List items) { List rows = new ArrayList<>(); for (int i = 0; i < items xss=removed>이렇게 해주면 됩니다. 그런데 위 사용방법을 보면 추가적인 새로운 타입(Row)의 List 와 반복문을 돌려야 된다는 것이 단점으로 보이는데요. 그럼 이 클래스를 사용하지 않고 직접 구현한 결과를 좀 볼까요?public class NormalItemAdapter extends RecyclerView.Adapter { public static final int VIEW_TYPE_A = 0; public static final int VIEW_TYPE_B = 1; private List mItems = new ArrayList<>(); @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_A) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new AViewHolder(itemView); } else { View itemView = LayoutInflater.from(parent.getContext()) .inflate(android.R.layout.simple_list_item_1, parent, false); return new BViewHolder(itemView); } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof AViewHolder) { Item item = getItem(position); ((AViewHolder) holder).getTextView().setText(item.getName()); } else { ((BViewHolder) holder).getTextView().setText("I am B."); } } private Item getItem(int position) { return mItems.get(position); } public void setItems(List items) { mItems.clear(); mItems.addAll(items); } @Override public int getItemViewType(int position) { if (getItem(position).getType().equals(Item.ITEM_TYPE_A)) { return VIEW_TYPE_A; } else { return VIEW_TYPE_B; } } @Override public int getItemCount() { return mItems.size(); } } 뭐, 나쁘진 않습니다. 이 정도 수준으로 개발이 끝나도 되고 추가적인 확장이 필요하지 않아보인다면 굳이 MultiItemAdapter 를 쓸 필요가 없습니다.중요성을 가지는 리스트 위주의 화면에서 위와 같이 개발된다면 당장 보이는 제 불만은 onCreateViewHolder, onBindViewHolder 계속해서 분기가 들어가게 되고 getItemViewType 에서는 계속 해서 List 데이터에 접근해야 한다는 것입니다. 접근 자체가 큰 문제, 큰 영향을 끼치지 않을 정도 규모의 자료구조라면 논외로 치더라도, 뷰 타입이 조금만 늘어나도 onCreateViewHolder, onBindViewHolder 의 덩치는 엄청 커질 겁니다.예를들면 맨 마지막 아이템 타입이 B 이고 현재 추가 될 아이템 타입이 A인 경우에는 다른 형태의 디바이더를 넣어야 한다던지 하는 추가적인 확장이 이루어져야 한다면 골치가 꽤 아플겁니다. 특히 저는 위 예와 비슷하게 뷰 타입에 따라 각기 다른 아래 위 마진값을 요구받을 때, ViewHolder 마다 이전 데이터를 참고하게 만들고 동적으로 Visibility 처리를 하거나 MarginLayoutParams 를 고치는 것이 비효율적으로 느껴져서 height를 주입받는 DividerViewHolder 를 하나 만들어 사용하곤 했습니다. 이렇게 하니 각각의 ViewHolder 들이 데이터들에 의존적이지 않게 코딩이 가능했었습니다. 한 가지 더 예를들어 리스트 중간 중간 광고가 보여지게 되고 이 광고 클래스는 완전히 다른 객체로부터 보여줘야 한다 라고 했을 때 MultiItemAdapter 를 이용하면 쉽게 해결이 가능합니다.정작 근 1년간 “잔디”를 만들면서는 자주 쓰진 않았는데, 작년부터 각광받기 시작한 MVP 패턴을 사용할 때 View 에서의 로직을 최소화 하려고 한다면 써먹을 수 있는 모델로 적합하지 않나 생각이 들면서 다시 사용하기 시작했습니다. Presenter 에서 Row 를 만들어 던져주면 View 는 그것을 그대로 사용하게 만들 수 있다는 생각이 들었거든요.(아직까지는 비교적 크지 않은 부분에서만 사용하게 되서 View(MainThread)에서 Row 를 만들게 코딩해 놓은 컴퍼넌트가 더 많네요 흑흑) 더 복잡한 구조를 갖는 컴퍼넌트를 만들어야 할 때는 비동기 스레드에서 Row 까지 만들어 내보내는 것도 해볼까 하는 생각도 듭니다.제 눈에만 괜찮은 구조인지, 생각지도 못한 치명적인 단점이 있진 않은지, 구조나 설계 측면에서 안 좋은 점은 있지 않은지, 논리없이 Generic 으로 “퉁” 치고 있는 코드는 아닌지, 여러가지가 많이 궁금합니다 ^^ MultiItemAdapter 를 쓴 것과 안 쓴것의 정말 심플한 비교 소스를 열어놓았습니다 MultiItemAdapter 또, 여러분들은 어떻게 구현하고 계신지요? 여러분의 관심이 필요합니다 ! :)#토스랩 #잔디 #JANDI #개발 #개발자 #인사이트 #경험공유
조회수 1484

AWS, Kubernetes 그리고 WAF

모니터링을 지속적으로 강화하다 보니 사용자 약관에 어긋나게 행동하는 주체가 눈에 띄기 시작한다. 특정 시간대에 판매 목록을 긁어가려고 시도하는 크롤러가 대표적이다. 비정상적인 서비스 이용을 탐지한 건 좋은데 이를 어떻게 차단할지가 또 고민이다. 차단 방법이야 많지만 가급적유지보수가 쉽고현재 서비스 구조에 살짝 얹기만 하면 되는그런 멋진 구조가 없을까 잠시 조사를 해보았다. 결론적으로는 현재 우리의 구조에선 1시간만 작업하고 펑펑 놀아도 되는 그런 방법은 없었다. 하지만 조금만 더 참고 기다리면 꽤 괜찮은 접근방법이 있을 것도 같더라. 우선 현 상황을 보자면 우리의 인프라는 주로Kubernetes가 서비스의 90% 이상을 통제하며웹 서비스는 주로 AWS ELB를 통해 인터넷 망에 노출한다.그러니 이론상으로는 AWS의 WAF, 그러니까 Web Application Firewall을 이용하면 손 안대고 코 풀기가 딱이다. 하지만 문제가 하나 있으니!!!AWS WAF는 ELBv2 그러니까 Application Load Balancer만 지원하는데 Kubernetes 1.5.x는 ELBv1만 지원한다. AWS WAF가 L4 로드밸런서인 ELBv1을 지원하던가 Kubernetes가 AWS ELBv2도 External Load Balancer로 선택가능하게 지원하던가 해야 Kubernetes + AWS ELB + WAF를 조합할 수 있다. 이 문제만 해결되면 금방 적용가능한 구성이라 매우 땡긴다. 설사 Kubernetes이 ELBv2는 지원하되 WAF 연동을 지원하지 않더라도 이를 수행하는 Kubernetes 플러그인을 개발하는 건 이틀이면 충분하지 싶다.왜 WAF인가?그러고 보니 여태 왜 이런 구성이 제일 낫다고 생각하는지 설명하지 않았다. 웹애플리케이션 방화벽을 구현하는 방법이야 AWS WAF 말고도 많지만 이러한 구성에는 분명한 장점이 있는데IP 평판 목록을 수집해서 한데 정리하는 서비스를 AWS가 제공하기 때문에 내가 이걸 구현한다고 시간낭비할 필요 없고매우 간단한 구조라서 처음 설치하고 설정하는데 30분에서 1시간이면 족하고Classless Inter-Domain Routing (CIDR) 표기법을 지원하므로 특정 아이피 대역을 막는 건 일도 아니며무엇보다 내가 관리하는 평판 목록도 쉽게 추가할 수 있다.이러니 “굳이 다른 솔루션을 찾아서 생고생해야 하나?”라는 생각이 들 수밖에 없다.다른 읽을꺼리How to Import IP Address Reputation Lists to Automatically Update AWS WAF IP Blacklists: AWS WAF의 구조와 WAF를 CloudFront에 적용하는 방법을 설명한다.AWS WAFがALB(Application Load Balancer)で利用出来るようになりました: AWS WAF를 ELBv2에 적용하는 방법을 설명한다.Akamai — Protect your organization with a web application firewall.Originally published at Andromeda Rabbit.#데일리 #데일리호텔 #개발 #개발자 #개발팀 #인사이트
조회수 1761

TDD(파이썬) : 테스트 잘하고 계신가요?

Overview반복적인 테스트에 지쳐가고 있던 무렵, TDD방법론을 접하게 되었습니다. TDD(Test Driven Development)는 테스트 주도적인 개발로 소스코드 작업 전에 테스트 코드를 먼저 작성해 소스수정에 대한 부담을 덜고 디버깅 시간을 줄일 수 있습니다. TDD 장점소스코드의 품질이 높다.재설계 및 디버깅 시간이 절감된다.TDD 단점단기적 코드일 경우 생산성이 떨어진다.실제 코드보다 테스트 케이스가 더 커질 수 있다.파이썬에서 TDD가 필요한 이유1) 파이썬에는 정적 타입 검사 기능이 없다. (Python 3.6 에서는 정적 타입 선언 가능)2) 동적언어이기 때문에 TDD를 하기에 적합하다.3) 파이썬은 간결성과 단순함으로 생산성이 높은 반면 런타임 오류가 발생할 수도 있다.4) 파이썬을 신뢰할 수 있는 유일한 방법은 테스트를 하는 것이다.파이썬 테스트 모듈 unittest이번 글에서는 unittest를 사용해 단위 테스트를 해보겠습니다. unittest는 이미 내장되어 있어 따로 설치하지 않아도 되는 표준 라이브러리입니다. 사용방법1) import unittest 2) unittest.TestCase 상속받는 하위 클래스 생성3) TestCase.assert 메소드를 사용하여 테스트 코드를 간략화4) unittest.main() 실행그럼 간단한 예제로 단위 테스트를 해보겠습니다.1.사칙연산 함수를 추가합니다.def add(a, b):     return a + b   def substract(a, b):     return a - b   def division(a, b):     return a / b   def multiply(a, b):     return a * b 2. unittest.TestCase 상속받아 테스트 클래스를 생성합니다. 아래는 각각의 함수 결과값을 비교해 텍스트를 출력하는 코드입니다.import unittest class TddTest(unittest.TestCase): def testAdd(self):         result = lib_calc.add(10, 20)         if result == 30:             print('testAdd OK')      def testSubstract(self):         result = lib_calc.substract(20, 30)          if result > 0:             boolval = True         else:             boolval = False if boolval == False:             print('testSubstract Error')      def testDivision(self):         try:             lib_calc.division(4, 0)         except Exception as e:             print(e)      def testMultiply(self):         result = lib_calc.multiply(10, 9)          if result < 100>             print('testMultiply Error') if __name__ == '__main__':     unittest.main() 3.결과: 해당 조건에 만족해 작성한 텍스트가 출력됩니다.이번에는 unittest에서 지원하는 TestCase.assert 메소드를 사용해 간략하게 소스를 수정해보겠습니다.TestCase.assert 메소드1) assertEqual(A, B, Msg) - A, B가 같은지 테스트2) assertNotEqual(A, B, Msg) - A, B가 다른지 테스트3) assertTrue(A, Msg) - A가 True인지 테스트4) assertFalse(A, Msg) - A가 False인지 테스트5) assertIs(A, B, Msg) - A, B가 동일한 객체인지 테스트6) assertIsNot(A, B, Msg) - A, B가 동일하지 않는 객체인지 테스트7) assertIsNone(A, Msg) - A가 None인지 테스트8) assertIsNotNone(A, Msg) - A가 Not None인지 테스트9) assertRaises(ZeroDivisionError, myCalc.add, 4, 0) - 특정 에러 확인1. TestCase.assert 메소드 사용TestCase.assert 메소드를 사용하여 에러를 발생시켜 보겠습니다.import unittest class TddTest(unittest.TestCase): def testAdd(self):         result = lib_calc.add(10, 20)          # 결과 값이 일치 여부 확인         self.assertEqual(result, 31)      def testSubstract(self):         result = lib_calc.substract(20, 10)          if result > 10:             boolval = True         else:             boolval = False # 결과 값이 True 여부 확인         self.assertTrue(boolval)      def testDivision(self):         # 결과 값이 ZeroDivisionError 예외 발생 여부 확인         self.assertRaises(ZeroDivisionError, lib_calc.division, 4, 1)      def testMultiply(self):         nonechk = True result = lib_calc.multiply(10, 9)          if result > 100:             nonechk = None # 결과 값이 None 여부 확인         self.assertIsNone(nonechk) if __name__ == '__main__':     unittest.main() 2. 결과1) 테스트가 실패해도 다른 테스트에 영향을 미치지 않음2) 실패한 위치와 이유를 알 수 있음다음으로 setUp(), tearDown() 메소드를 사용하여 반복적인 테스트 메소드 실행 전, 실행 후의 동작을 처리해보겠습니다.TestCase 메소드1) setUp() - TestCase클래스의 매 테스트 메소드가 실행 전 동작2) tearDown() - 매 테스트 메소드가 실행 후 동작 1. setUp(), tearDown() 메소드 사용- setUp() 메소드로 전역 변수에 값을 지정- tearDown() 메소드로 “ 결과 값 : ” 텍스트 출력import unittest class TddTest(unittest.TestCase): aa = 0     bb = 0     result = 0 # 매 테스트 메소드 실행 전 동작     def setUp(self):        self.aa = 10        self.bb = 20 def testAdd(self):         self.result = lib_calc.add(self.aa, self.bb)          # 결과 값이 일치 여부 확인         self.assertEqual(self.result, 31)      def testSubstract(self):         self.result = lib_calc.substract(self.aa, self.bb)          if self.result > 10:             boolval = True         else:             boolval = False # 결과 값이 True 여부 확인         self.assertTrue(boolval)      def testDivision(self):         # 결과 값이 ZeroDivisionError 예외 발생 여부 확인         self.assertRaises(ZeroDivisionError, lib_calc.division, 4, 1)      def testMultiply(self):         nonechk = True self.result = lib_calc.multiply(10, 9)          if self.result > 100:             nonechk = None # 결과 값이 None 여부 확인         self.assertIsNone(nonechk)      # 매 테스트 메소드 실행 후 동작     def tearDown(self):         print(' 결과 값 : ' + str(self.result))   if __name__ == '__main__':     unittest.main() 2. 결과- setUp() 메소드로 지정한 값으로 테스트를 수행 - tearDown() 메소드로 각각의 테스트 메소드 마다 “ 결과 값 : ” 텍스트 출력실행 명령어 여러 옵션을 사용하여 실행 결과를 출력해보겠습니다.실행 명령어python -m unittest discover [option]1. -v : 상세 결과 2. -f : 첫 번째 실패 또는 오류시 중단3. -s : 시작할 디렉토리4. -p : 테스트 파일과 일치하는 패턴5. -t : 프로젝트의 최상위 디렉토리1. 상세 결과테스트 메소드명 및 해당 클래스명 출력 2. 첫 번째 실패 또는 오류시 중단첫 번째 테스트에서 오류 발생하여 중단3. 여러 옵션 실행현재경로 디렉토리 안에 tdd_test*.py 패턴에 속하는 모든 파일의 상세 결과Conclusion지금까지 파이썬에서 unittest 모듈을 이용한 테스트 코드를 작성했습니다. 처음에는 귀찮고 번거롭지만 테스트 코드를 먼저 작성하는 습관을 길러보세요. 분명 높은 품질의 소스코드를 만들 수 있을 겁니다!참고Python 테스트 시작하기파이썬 TDD 101글곽정섭 과장 | R&D 개발1팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발자 #개발팀 #인사이트 #경험공유 #파이썬 #Python
조회수 1362

스타트업 개발팀에서 일한다는 것

 대부분의 정보통신 분야, 특히 소프트웨어를 제작하는 "개발 조직"의 구성은개발,  디자인, 기획(또는 PM), QA가 팀으로서 분리되어있고, 규모 있는 회사들의 경우, 업무에 관련된 직군 간의 갈등 상황이나 문제가 생길 시, 파트장 또는 팀장님들(이하 중간관리자)이 중재를 하고 의사를 결정하는 정해져 있는 프로세스가 되어 있습니다. 그러나, 작은 규모의 스타트업이나, 업무에 책임을 지고 있는 인원이 단 한두명일 경우, 중간관리자들의 부재 때문에 개인 간 생기는 갈등 상황을 피할 수 없습니다. 그래서 오늘은 지금까지 제가 느낀 "정확하고 빠른 업무 진행을 위해 각 직군 간 인원들이 챙겨야 할 덕목들."을 말씀드리려 합니다. 그렇다면 무엇보다도, 왜 갈등 상황이 생기게 되는 걸까요?저는 "각 직군 간 종사자의 업무를 진행하는 과정과 목표가 다름에도, 이해보단 자신의 기준에서만 업무를 바라보는 경향"이 가장 큰 원인이라고 생각합니다. 저의 글(라고 적고 깨알 홍보라 읽는다...)에서 말씀드렸듯, 개발자, 디자이너, 기획자 들은 서로 일을 하는 방식도 다르고(심지어 개인차도 있지요), 각 직군마다 지향하는 부분들이 다르기 때문에 같은 방향을 보고 가더라도 서로가 서로 간에 집중하지 못하는 부분들이 분명히 존재합니다. 그리고 그런 부분들을 줄이기 위해선 업무 중 서로가 서로를 이해할 수 있도록 한 번씩 자신을 돌아보는 것들이 중요합니다. 그래서 아직 많이 부족하지만 각 직군에 종사하는 분들 그리고 공통적으로 업무 중에 한 번씩만 더 생각해 주셨으면 하는 것들과, 이유를 적어보려 합니다.기획 (또는 프로젝트 매니지먼트)1. 스펙 산정은 다 같이큰 회사라면 잘 모르겠지만, 작은 개발팀의 장점은 "많은 인원들이 비교적 짧은 시간 안에 많은 생각을 나누고 지향점을 찾아가는 과정을 같이 할 수 있는 것."이라고 생각합니다. 분명 기획자가 생각하고 만들어 내야 하는 스펙들이 있겠지만, 독단적으로 "이건 무조건 해야 하니 들어."라는 태도는 작은 팀일수록 업무의 동기를  꺾어버리는 일이 생길 수 있으니 항상 조심해야 합니다.2. 혼자서 "뭐... 개발이든 디자인이든 되겠지." 하는 추측은 절대 금물개발자 출신, 또는 디자이너 출신 또한 마찬가지라고 생각합니다. 몇 가지 예를 들자면, 1. 지금 개발자 또는 디자이너가 부딪힌 상황에 있지 않고, 2. 해당 직군에서 새롭게 화두 되는 트렌드에 덜 민감하고, 3. 각 개발자, 또는 디자이너가 생각하고 있는  스펙이나 디자인을 알지 못하고, 4. 어떤 라이브러리, 어떤 테마를 기반으로 작업할 지에 대한 기본적인 이해가 없으면서"이건 이렇게 되니깐 당연히 금방 될 거야."라는 생각은 절대 금물이라고 생각합니다.(디자이너나 개발자 분들이 그냥 "이거 간단하게 뭐 메뉴 만들어서 대충 어디 집어넣으면 되지 뭘 그리 어렵게 생각해?"라고 하면 피꺼솟 하는 거랑 마찬가지입니다ㅎㅎ) 3. 삼초 안에 정해진 내용도 항상 문서화 작은 개발팀일수록, 내용 저장과 공유, 그리고 의사 판단의 근거들이 약할 때도 있고, "우리 왜 이거 이렇게 가게 됐지?"라고 생각할 때가 많습니다. 적어도 "몇 월 며칠날 어떤 주제에 관해서 어떤 이유 때문에 어떤 방식으로 처리하기로 한다." 정도라도 항상 적어둘 필요가 있습니다. 디자이너1. 레퍼런스 자료 준비에 시간을 아끼지 말자 원하는 인터렉션, 원하는 디자인의 방향이 있다면, "왜 원하는지, 왜 이런 방향으로 개발을 해주었으면 하는지."에 대한 판단의 근거가 필요합니다. "이쁘잖아."는 상당히 설득력이 있지만 개발자 또는 기획자를 완전히 설득시킬 순 없어요... 특히 아직 개발이나 기획에 대한 로직을 잘 모르시는 디자이너 분들의 경우, 레퍼런스 자료를 찾을 때 드리블(Dribbble)이나 핀터레스트(Pinterest)도 좋지만, 스스로 프로토 타이핑 구현이 불가능하다면, 반드시 구동되고 있는 애플리케이션을 찾아보고 어떻게 작동하는지에 대해 면밀하게 파악해 주세요.2. 작은 부분이라도 시안에 변경이 있다면 반드시 공유하기 처음 팀 단위로 일을 하다 보면, "요거 내가 생각해보고 금방 슉 바꿔놔야지."라는 생각에 조용히 디자인을 바꿀 수 있습니다... 아? 아니에요 절대 안 됩니다.....  다 같이 협업하는 일을 하다 보면, 버전 관리와 변경내역 공유가 제작보다 더 중요한 상황이 올 수 있습니다. "내가 어디 부분을 변경했고, 변경한 이유는 이것 때문이다." 라는것 없이 홀로 조용히 변경한 디자인은 엄청나게 큰 갈등 상황을 부를 수 있어요!개발자1. 장애 발견 시 어디가 어떻게 안될 거 같은지에 대해서 설명하기 가장 힘든 줄 알지만, 할 수 있다면 가장 강점인 부분일 것 같아요. "개발하는 과정에서 이러한 부분은 지금 서비스에서는 이런 식으로 동작하는데, 원하시는 이런 부분은 이런 게 다르기 때문에 동작하는데 장애가 있을 수 있어요."를 설명해 줄 수 있는 개발자와 일한다는 것은 정말 같이 일하는 다른 직군들에게는 큰 축복이라고 할 수 있죠. "내가 백번 말해도 모르실 거예요."라고 말하는 건 결국, 무엇이 문제인지도 모르고, "다른데선 되는데 왜 우린 안돼?"라고 생각하는 다른 직군의 동료들에게 질타를 받을 수밖에 없습니다. 개발자는 소통이 잘 안 되는 사람이라는 고정관념을 깨고, 오래 걸려도 좋고, 다른 직군의 사람들이 당장 무슨 이야기를 하시는지 이해 못해도 좋아요. 같은 선상에서 고민한다는 것을 알려주는 것만으로도 큰 도움이 됩니다.전반적으로모든 작업의 종료는 내 결과물 발표가 아닌 다음 작업자의 업무 최적화입니다.기획자는 "문서 완료했을 때"디자이너는 "디자인 가이드 또는 산출물 나왔을 때"개발자는 "시킨 개발 다 했을 때"가 아니라,기획자는 "다음에 문서를 읽을 디자이너, 개발자가 스펙이 이해가 안돼서 업무를 진행하지 못하는 일이 없도록"디자이너는 "개발 중에 필요한 자료가 없어 업무를 진행하지 못하는 일이 없도록 "개발자는 "QA 중 개발에서 요구한 스펙에 미달되는 부분이 있어 업무를 진행하지 못하는 일이 없도록"하는 게 업무의 궁극적인 틀입니다. 결국, 일의 최종점은 결과물이 온전하게 나왔을 때 의미있는 것이기 때문에 최종 결과물이 나오는 과정에서 내 역할을 마지막까지 충실히 하는것이 업무의 종료라고 생각합니다. 분명, 모든 것들을 한 번에 모두 다 알고, 또는 모든 것들을 다 계산하면서 할 수는 없어요. 항상 실수는 할 수 있죠. 하지만, 실수가 아니라 이런 부분들을 알면서, 또는 이러한 고려를 하지 않고 업무를 지금까지 진행하셨다면, 말씀드린 부분은 분명히 한 번씩은 생각해 보아야 될 부분이라고 생각합니다. 굉장히 오랜만에 글을 쓰는 것 같네요! 다들 건강하게 잘 지내고 계시죠? 저는 마지막 글 이후 한 번의 이직과 다른 이런저런 일들에 치여 이제야 글을 쓰게 되네요. 이번글을 시작으로, 직군별로 하나하나 더 디테일하게 설명드리도록 할게요! 그리고 앞으로는 기획 업무 관련 뿐만이 아니라, 이번 글과 같이 각 직군 간의 이해관계나 업무를 진행하며 느끼는 것들에 대해 공유드리고, 서비스 기획 관련해서도 조금 더 자주 글 쓸 예정입니다. 앞으로도 자주자주 들러주세요, 감사합니다! :)#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 1366

개발팀의 유행어 제조기, Mark를 만나다

 * 2015년에 작성된 글입니다편집자 주: 잔디에는 현재 40명 가까운 구성원들이 일본, 대만, 한국 오피스에서 일하고 있습니다. 국적, 학력, 경험이 모두 다른 멤버들. 이들이 어떤 스토리를 갖고 잔디에 합류했는지, 잔디에서 무슨 일을 하고 있는지 궁금해하시는 분들이 많았습니다.  이에 잔디 블로그에서는 매 주 1회 ‘맛있는 인터뷰’라는 인터뷰 시리즈로 기업용 사내 메신저 ‘잔디’를 만드는 사람들의 이야기를 다루고자 합니다. 인터뷰는 매 주 선정된 인터뷰어와 인터뷰이가 1시간 동안 점심을 함께 하며 다양한 이야기를 나누며 진행됩니다. 인터뷰이에 대해 궁금한 점은 댓글 혹은 이메일([email protected])을 통해 문의 부탁드립니다.인터뷰 시작에 앞서 편집자 스스로 잔디의 개발팀에 궁금한 점이 있었다. 매 주 수요일 아침 8시, 오피스 근처 카페에서 스터디를 하는 그들의 문화가 바로 그 것이다. 회사의 강요가 아닌 공부를 하겠다는 자발적인 이유로 모인다는 그들. 그들 중 한 명인 Mark를 이번 주 맛있는 인터뷰에 어렵게 모시게 되었다.세렝게티의 한 마리 표범과 같은 그의 눈빛이 향한 곳은 가.츠.나.베반갑습니다. 우리 좀 걷지 않았나요? 회사에서 꽤나 멀리 떨어진 ‘오무라안’을 온 특별한 이유가 있다면?회사 바로 앞에 있는 ‘탄’보다는 조금 고급스러운 일식 레스토랑이에요. 우연히 알게 된 곳인데 맛이 딱 제 취향이라 즐겨 찾습니다. 항상 가츠나베를 먹는데요. 그 맛은.. 말로 형용하기 어렵네요.가츠나베성애자이시군요. 얼마나 있는지 모르겠으나 ‘맛있는 인터뷰’ 독자들을 위해 인사 부탁드립니다.안녕하세요, 부산 남자 Mark입니다. 잔디에 합류한 지 약 두 달 정도 되었어요. 잔디에서는 Front-end 개발 업무를 맡고 있습니다.주로 어떤 일을 하시나요?쉽게 말하자면 사용자들이 접하는 부분을 책임지는 역할이에요. 지금은 Jihoon, Young과 함께 일하고 있는데 궁합이 잘 맞는 것 같아요. 사람이 적으면 할 수 있는 일이 한정되어 있고 반면 사람이 많으면 커뮤니케이션이 힘든데 저희 세 명은 예외인 것 같습니다.왔노라, 보았노라, 달렸노라Mark님만의 유행어가 있더라고요?‘가자!’ 를 말씀하시는 것 같은데요. 맞나요? (웃음) 비글로벌 서울 2015 우승 후, 뒷풀이 회식에서 흥에 겨워 술과 함께 외친 ‘가자!’가 다른 분들에게 인상적으로 각인되었던 것 같아요.네, 저도 그 자리에 있었는데요. 굉장히 인상적이었어요. 술이 센 편이신 것 같은데요?아니에요. 사실 술을 잘 하는 편도, 자주 마시는 편도 아니에요. 주량이라면 소주 두 병 정도? 그 날은 저희 회사가 좋은 일도 있고 해서 평소보다 많이 마시긴 했지만 기분이 좋았던 게 그런 사태를 만든 주된 이유인 듯 합니다.잔디 비글로벌 서울 2015 우승!잔디의 개발자 채용 과정이 다른 곳에 비해 까다롭다고 들었어요. 직접 경험하신 분으로서 어땠는지 여쭤볼 수 있을까요?정말 까다로워요. 다른 곳도 코딩시험을 보기는 하는데 잔디는 인사부에서 1차 코딩 시험을 보고 2차 면접에서는 왜 그렇게 코딩을 했는지 설명을 해야 합니다. 그리고 나서 인성 면접을 봤습니다. (잔디에서는 이 면접을 Behavior Interview 라고 부르며, 여러 부서의 인원들이 참여해 해당 인터뷰이가 함께 일할 사람으로서 적합한지 판단하게 된다 – 편집자 주)마치 수험생 같다는 느낌이 들었어요. 면접 과정 중에는 ‘뭐 이리 깐깐하게 굴어?’ 라는 생각을 했었는데, 지금 돌이켜 보면 이런 과정을 거쳐 합류한 인재들이 모여 있어 잔디가 빠르게 성장할 수 있지 않을까 추측해 봅니다.잔디에서의 생활은 어떤가요?신기한 점이 참 많은 것 같아요. 좋은 점은 출중한 능력을 가진 분들이 많다는 점이에요. 그분들을 통해 배울 점도 많고, 개인적으로는 분발해야겠다는 생각을 하게 해요. 많은 자극을 받고 있어요.신기한 점이라면 어떤 부분일까요?예를 들면 아침에 출근하면 Dan(CEO)이 제게 다가와 영어로 말을 건네는 것이 가장 신기한 것 같아요. 당황스러우면서도 한편으로는 신기해요.이건 개인적으로 궁금한 건데요. 개발팀의 아침 스터디에 대해 어떻게 생각하시나요?사실 아직 참여해 보진 못했어요. 잔디 개발팀에서는 매주 아침 8시까지 나와서 자발적으로 스터디를 하고 있는데요. 강요가 아닌 자발적으로 업무 외에 스터디를 한다는 점이 참 인상 깊어요.그렇군요. 질문을 좀 바꿔볼게요. 쉬는 날엔 뭐 하시나요? 부산 사람이니 야구?보통 쉬는 날엔 서울에 있는 친구들을 만나거나 게임을 해요. 야구는 부산 사람이다 보니.. 삶의 일부 같은 느낌이죠. 우리가 공기를 좋아하거나 싫어할 수 없듯, 야구 역시 좋아하거나 싫어할 수 있는 대상이 아니에요.보통 ‘부산 사람=야구’라고 생각하는데 Mark도 여기에 해당하는 분이었군요. 게임은 어떤 걸 즐겨 하시나요?WOW(Wolrd of Warcraft)라고 아세요? 저는 게임에 있어서 저만의 철학이 있어요. 게임에도 레벨이 존재한다고 생각하는데요. 모바일 게임을 아주 안 하는 것은 아니지만 모바일 게임에 투자하는 시간은 아깝다고 느껴져요. 물론 개인적인 생각입니다.록타르.. 피바람을 몰고올 Mark여..그러면 Mark가 생각하기에 게임으로서 ‘와우’는 어느 정도 레벨인가요?제가 알고 있는 게임들 중 와우는 Top3에 듭니다. 물론 생각을 깊게 해 본 적은 없어서 나머지 2개에 뭘 넣어야 할지 고민해야겠지만 와우는 정말 잘 만든 수작이에요.이제 곧 휴가철이잖아요. 부산 여행 추천 장소 좀 해주세요.외지 사람들은 보통 해운대 많이 가는데, 사실 부산 사람들은 해운대를 잘 안가요. 사람이 너무 많잖아요? 부산 여행 장소를 찾으신다면 개인적으로 을숙도를 추천하고 싶어요. 여긴 가족 단위 여행객이 많은 곳인데요. 서울 사람들이 한강을 찾듯 부산 사람들은 을숙도를 찾아요.이번 여름 휴가는 을숙도로!을숙도? 섬인가요?네, 섬이긴 한데 엄청 큰 다리로 육지와 연결되어 있어서 차를 타고 들어갈 수 있는 곳이에요. 공원이 잘 조성되어 있어요. 자전거도 빌려 탈 수 있고 까페도 있어서 여행 장소로는 딱이에요.축구장도 엄청 많아서 축구 동호회 분들이 자주 찾으시는데요. 사람으로 북적거리지 않는 부산 여행지를 찾는다면 이번 여름 여행은 을숙도로 가보세요. 참고로 을숙도에는 음식점이 많지 않아요. 저 같은 경우, 을숙도 갈 때마다 도시락을 챙겨가곤 합니다.다음은 맛있는 인터뷰의 고정 코너 ‘어서 말을 해’입니다. Jinho가 남긴 질문 ‘잔디를 한문장으로 표현한다면?’에 대해 답을 주신다면?잔디란 ‘기회’ 입니다. IT 업에서 제가 어디까지 능력을 발휘할 수 있을지 확인해볼 수 있는 좋은 기회이기 때문이죠. 좀 진부한가요?전~혀 진부하지 않아요. 멋진 답변을 주셨으니 다음 인터뷰이를 위해 질문 하나 남겨주시겠어요?저는 이걸 꼭 물어보고 싶어요. ‘최근 3년 동안 당신에게 가장 행복했던 일은?’Mark와 개인적으로 얘기를 나눠보고 싶었는데 이렇게 소원이 이뤄졌네요. 개인적으로 뿌듯한 인터뷰였습니다.감사해요. 잘 좀 편집해 주세요.#토스랩 #잔디 #JANDI #개발팀 #개발자 #개발 #팀원소개 #팀원인터뷰 #팀원자랑 #기업문화 #사내문화 #조직문화

기업문화 엿볼 때, 더팀스

로그인

/