스토리 홈

인터뷰

피드

뉴스

조회수 1361

docker the cloud

당신의 기획안을 통과시키는 마법의 단어, 클라우드안녕, 여러분! 다들 다망하신 와중에 이렇게 지면으로 찾아뵙게 되어 굉장히 반갑습니다. 저는 spoqa의 노예 xym입니다. 어느덧 벌써 연말이네요. 온갖 골든 위크로 시작했던 4/4분기, 이제 한창 주말 외에는 법정공휴일이 없는 데스마치를 진행중이시리라 생각되는데요, 안 그래도 다들 크리스마스만 바라보고 미친듯이 달리고 계시죠?네, 그래서 제가 이렇게 잠시 여러분 머리를 식혀드리기 위해 한 번 재밌는 이야기를 하고자 찾아뵙게 되었습니다. 개발자가 아닌 분들에게도 별로 어렵지 않게 쓰고자 노력했으니 한번쯤 “오 이런 신기한 게 있구나”하고 읽어보시고 머리 좀 식히고 가세요.업계 분들이나, 이쪽 업계에 소식이 빠삭한 분들은 아시겠지만 몇년 전부터 이 바닥은 새롭게 몰아치는 파도를 맞고 있습니다. 2, 3년 전부터 올해 중순까지 업계 뜨거운 감자였던 키워드들에 대해서 기억하고 계신가요? 네, 그 소위 HTML5니 클라우드, 빅데이터, 소셜 게임 따위의, 기획안에 쓰면 사장님 입이 귀에 걸리게 만드는 마법의 단어들이요.이 글도 사실 그 마법의 단어들에 관련된 이야기입니다. 정확히는 클라우드 기술에 관련된 이야기예요.뜬구름 잡는 클라우드대관절 클라우드란 무엇이길래 여러분의 기획안을 통과시키게 하는가 궁금하지 않으셨나요? 알고 계신 분들도 많을 테니 간략하게 설명하고 넘어가겠습니다. 클라우드는 클라우드 컴퓨팅 기술의 약자입니다. 위키피디아에 있는 정의는 다음과 같습니다:인터넷 따위의 네트워크를 통해 실시간으로 많은 컴퓨터들을 관리하는 여러 컴퓨팅 기술과 관련된 개념들을 총칭얼핏 들으면 굉장히 뜬구름 잡는 소리입니다. 아니, 그럼 그 전까지는 그런 걸 안 했다는 건가? 물론 아닙니다. 클라우드 컴퓨팅이란 단어가 버즈워드로써 시장을 강타하기 전에도 소위 클라우드 컴퓨팅을 위한 기술들은 존재했습니다.엄밀히 말하면 클라우드 컴퓨팅은 ‘기술 융합’의 일종이라고 볼 수 있습니다. 기존에 존재하던 개념들과 기술들을 융합하여 새로운 접근법을 탄생시킨 것이죠. 간단히 소개하자면 그 클라우드 컴퓨팅을 이루는 기반에는 다음과 같은 두 개의 거대한 축이 있습니다.가상화(Virtualization) : 하나의 컴퓨팅 자원을 여러 개로 나누어 마치 여러 개의 독립된 컴퓨터처럼 사용하는 기술 혹은 개념그리드 컴퓨팅(Grid computing) : 하나의 작업을 동시에 여러 개의 컴퓨터가 분할하여 처리하는 기술 혹은 개념거기에 중요한 개념 하나만 더 얹고 넘어가겠습니다. 이것도 한 때는 버즈워드로 사람들을 흥분시켰었죠.Application Programming Interface(API) : 복잡한 내부 동작에 대해서는 잘 몰라도 정해진 규약(인터페이스)만 알고 있으면 해당 기능을 사용할 수 있도록 한다는 개념그러니까 어떤 작업을 하기 위해 하나의 컴퓨터를 여러 개로 분리하고(자르고), 또다시 그 분리된 컴퓨터들을 합쳐서(합치는), 어쨌든 정해진 규약대로 사용할 수 있게 만드는 것(편한 거).아, 너무 기네요. 줄여서 “난 잘 모르겠지만 뭔가 좀 편한 거군.” 정도로 해두죠. 그게 클라우드의 궁극적인 목표이자 본질이라고 볼 수 있겠습니다. 그래서 이름도 뜬구름 잡는 소리 같다고 클라우드잖아요?그래도 마냥 뜬구름 잡는 소리만 할 수는 없으니 한번 클라우드 서비스의 종류를 알아봅시다.IaaS(Infrastructure as a Service) - 인프라스트럭쳐, 한마디로 서버를 조립하고 설치하는 방법을 몰라도 쓸 수 있도록 편하게 제공한다고 보면 됩니다. Amazon Web Service 같은 애들이죠.PaaS(Platform as a Service) - 이번엔 IaaS를 잘 몰라도 서비스를 돌릴 수 있게 만들어진 플랫폼을 제공합니다. Heroku가 대표적입니다.SaaS(Software as a Service) - 그렇게 만들어진 플랫폼 위에 돌아가는 서비스들을 제공합니다. icloud.com의 keynote 따위가 있겠군요.생각보다 어렵지 않죠?docker 란 무엇인가사설이 길었네요. 이제부터가 본론입니다. 제가 오늘 소개할 녀석은 클라우드 컴퓨팅에 있어 “자르는” 축을 담당하는 가상화의 떠오르는 아이돌, LXC를 사용한 docker 입니다. LXC가 무엇인지는 여기서 중요하지 않습니다#2. 그냥 업계의 떠오르는 아이돌 정도로 해 둡시다. 그러니까 아이유 같은 존재죠.docker가 등장한 배경을 설명하자면 이렇습니다. Heroku와 함께 PaaS계에서 끗발을 날렸던 dotCloud는 어느 날 갑자기 충격적인 발표를 합니다. 자기네들이 쓰는 가상화 및 애플리케이션 플랫폼을 공개해 ‘오픈 소스로’ 제공하겠다는 것이죠. 아니, 이럴 수가! 이러시면… 이러시면 정말 감사합니다#3!docker의 가장 큰 특징은 다음과 같이 요약할 수 있습니다.image 관리의 간편화와 container 관리 간편화어떤 서비스를 돌리기 위해서는 필요한 서버들이 있습니다. 데이터베이스 서버, 웹 서버, 캐시 서버, 워커 서버 따위의 것들이죠. 이 모든 걸 한 군데로 퉁쳐서 모을 수도 있겠지만 그렇게 되면 데이터베이스, 웹, 캐시, 비동기 업무를 위한 설정과 프로그램들을 한 군데로 모아 관리해야 합니다. 그렇게 되면 설정이 복잡해지거나 애플리케이션이 거대해지거나 필요할 때 횡적인 확장을 하기가 어려워집니다.예를 들어 웹서버에서는 A라는 라이브러리의 1버전을 필요로 하는데 데이터베이스 서버에서는 2버전을 필요로 한다던지, 이벤트 하느라 접속자가 너무 증가했는데 다른 웹서버가 한시간 정도만 필요한 일을 그럴 수 없어서 서버를 통째로 하나 사야 한다던지 하는 일들이죠. docker는 그런 상황에 유연하게 대응하기 위해 서버 설정과 필요한 프로그램들을 따로 관리할 수 있는 환경을 제공합니다.docker는 이렇게 분리된 환경을 image라고 부르며, 이 image를 기반으로 여러 개의 container를 생성할 수 있습니다. 음… 이렇게 이해하시면 편할 것 같습니다. image는 유전자 설계도고, container는 그 유전자 지도에서 만들어진 생물체라고나 할까?즉, 이 설계도를 관리하면 필요할 때 목적에 적합하게 만들어진 생물체를 얼마든지 만들어낼 수 있게 되죠. 필요할 때는 설계도의 설계를 바꿔서 새로운 생물체를 만들어낼 수도 있습니다. 단순하지만 docker의 가장 커다란 컨셉이고 강력하기까지 합니다. 이렇게 단순하고 간편한 환경은 여러 가지 시도를 가능하게 합니다.오토스케일링(웹서버가 필요할 때 웹서버를 막 찍어낸다던가!)유연한 배포 정책(서버를 최신 버전으로 업데이트했는데 버그가 있어서 재빨리 옛날 버전으로 돌아가야 한다던가!)자원의 효율적인 활용(이 쪽 서버가 놀고 있으니까 여긴 웹서버 두개 정도 더 띄운다던지)거기다 수고를 좀 더 들이면, docker의 API를 활용해 Heroku 부럽지 않은 웹 GUI PaaS 서비스를 만들 수 있을지도 모릅니다(만들어 주시면 감사히 쓰겠습니다).한번 docker를 살펴봅시다이야기는 실컷 했으니 한번 설치해보고 실행시켜봅시다. 지면 관계상 모든 플랫폼을 다룰 수는 없기에 우분투 13.10을 기준으로 살펴보도록 하겠습니다. 필요하신 분들은 공식 홈페이지 설치 메뉴얼을 참고하여 진행해주세요.주의 : 이후 내용은 비 개발자 분들에게는 다소 지루한 내용일 수도 있습니다.docker 설치curl http://get.docker.io | sudo sh 참 쉽죠?자 이제 시작이야이제 여러분의 플랫폼에는 docker가 설치됐습니다. 한번 서버에서 기본 이미지를 다운받아 설치해 봅시다.sudo docker pull base 인터넷 환경에 따라 좀 기다리셔야 하실지도 모릅니다. 이미지가 설치되면 아래 명령으로 확인할 수 있습니다.sudo docker images 아래와 비슷한 화면이 나타났다면 성공한 겁니다.REPOSITORY TAG IMAGE ID CREATED SIZE base latest b750fe79269d 8 months ago 24.65 kB (virtual 180.1 MB) base ubuntu-12.10 b750fe79269d 8 months ago 24.65 kB (virtual 180.1 MB) …(생략) 이렇게 내려받은 image에는 다음과 같은 명령어로 접근할 수 있습니다.sudo docker run -i -t base /bin/bash 자세한 명령어 사양은 docker help run을 실행해 알아볼 수 있습니다. 여러분은 이제 base라는 image에 접속했습니다. 지금부터 하는 행동은 image에 영향을 미치게 되며, 이는 전부 로그로 남아 저장됩니다. 한번 이것저것 설치해봅시다.sudo apt-get install python ruby … 이후에 Ctrl+D를 눌러 이미지를 빠져나옵니다. 그리고 아래 명령을 입력하면 방금 전에 수정한 container 목록이 출력됩니다.sudo docker ps -a 아래와 같은 식으로 출력됩니다.CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eda0060b7af9 base:latest /bin/bash 6 minutes ago Exit 0 lavender_deer 66c849867834 busybox:latest echo Docker has been 8 minutes ago Exit 0 blue_cat 이제 image의 수정사항을 기반으로 새로운 이미지를 만들어 봅시다. 이미지를 만드려면 변경사항을 commit 해야 합니다. VCS나 DVCS를 쓰시는 분이라면 무슨 말인지 감이 오실 겁니다. 네, 바로 버전 관리 시스템의 그것입니다. 기존 base를 기반으로 변경사항을 만들고 commit하여 새로운 이미지를 생성할 수 있습니다. 매우 쉽군요. 한번 생성해봅시다.docker commit [ID] [image name] commit 명령의 구조는 단순합니다. container ID와 그리고 만들 이미지 이름입니다. 이미지 이름은 보통은 만든이/목적 같은 컨벤션으로 만들곤 합니다. 저는 아래와 같이 만들어보겠습니다.sudo docker commit eda0060b7af9 xymz/grocery 확인은 당연히 아래와 같이 할 수 있습니다.sudo docker images repository 에서 여러분이 만든 이미지 이름을 확인할 수 있다면 성공한 겁니다. 여러분의 첫 docker image 생성을 축하합니다!물론 이렇게 약간 거칠어보이는 방법과는 다르게 Dockerfile 이라고, 딱 봐도 버전관리 시스템에 넣을 수 있을 거 같고 정리가 잘 되는 방법도 존재합니다. 아마도 실제로 사용하실 땐 Dockerfile을 사용하게 되실 거고, 그 방법이 훨씬 낫습니다. 다만 본 포스트의 목적은 개발자나 비개발자 분들에게 docker를 한번 소개해보자는 취지라서 Dockerfile의 operation 을 일일히 설명하기엔 얘기가 너무 복잡해질 것 같아 직접 try-out 하기에 쉬운 commandline 쪽을 선택하게 되었습니다.당연히 이게 끝은 아닙니다여기까지 나온 내용으로 서비스를 구성하기에는 무리가 있습니다. 우리는 이제 막 docker image를 생성하고 저장하는 방법을 알았을 뿐이지 그 외에는 아무것도 모릅니다. docker를 제대로 사용하기 위해서는 아래와 같은 방법들을 추가적으로 알아야 합니다.생성된 이미지 관리 : 새로 만든 이미지를 어딘가에 업로드하여 다른 docker 시스템(host)에 배포하기 위한 방법에 대해 알아야 합니다.실제 서비스를 container 에 올리고 관리하는 방법 : 아까 언급한 것처럼 예시를 들자면, 현재 서버에서 웹서버를 를 몇개나 띄울 건지 등을 결정하고 관리하는 방법에 대해 알아야 힙니다.docker host와 guest간의 통신 관리 : docker가 설치된 실제 서버와 그 위에서 돌아가는 container들 간에 오가는 통신에 대한 이해가 필요합니다. 포트 바인딩, 포트포워딩이라고도 하죠.docker API : 이 모든 스택을 관리하기 위한 docker의 API를 알고 있다면 무한한 활용이 가능해집니다.하지만 이 방법들에 대해 여기서 다 열거하고 넘어가기에는 무리가 있으니 좋은 링크를 몇 개 소개토록 하겠습니다.파이썬 웹앱 올려보기docker를 개발환경으로 사용해보기Dockerfile 로 image 관리하기포트 리다이렉션적어놓고 보니 대부분 docker 공식 홈페이지 자료들이네요. 사실 docker는 documentation이 훌륭한 편이라, 그 쪽만 참고해도 많은 도움이 되실 겁니다.Deis?그리고 이 모든걸 쉽게 해주겠다는 Deis라는 녀석이 있습니다. Docker, Chef, Heroku Buildpacks를 이용해 하나의 PaaS스택을 만들고 그 위에 여러분의 서비스를 돌릴 수 있도록 해주겠다는 녀석인데요. 어쩌면 진정한 Open source PaaS 종결자일지도 모르겠습니다. 기회가 된다면 다음에 또 소개할 수 있었으면 좋겠네요.마치기 전에즐거우셨나요? 중간 이후 내용은 다소 비개발자분들에게 지루한 내용이었을지도 모르겠습니다만, 전반적으로 최대한 쉽게 설명하고자 노력했습니다. 다음 번에는 더욱 재밌는 글로 찾아볼 수 있도록 하겠습니다. 그럼 뿅!참고한 링크들docker.ioUsing Docker as a Development EnvironmentDocker: Error starting container: Unable to load the AUFS module주석사실 API는 거창한 기술적 개념이라기보단, 소소한 개발 방법론에 가까운 이야기입니다. 온갖 프로그래밍 언어와 다양한 기술들이 난립하는 와중에 그 모든 걸 알고 전부 뭉쳐서 하나의 덩어리를 만들면 관리/사용하는 비용이 너무 커지니 각 영역을 딱딱 잘라 구분하여 ‘정해진 규약’만 알면 서로 통할 수 있게 만들자. 라는 개념입니다.(약간의 지식이 있는 분들을 위해) LXC(LinuX Containers)는 기존 전가상화full virtualization나 반가상화paravirtualization와는 다르게 OS 위에 가상머신이 따로 돌아가는 게 아니라 OS영역에서 공유 라이브러리를 가지고 유저가 생성하는 프로세스 단위로 성능 분리를 합니다. 덕분에 이름에서 보이듯 특정 플랫폼밖에 지원을 하지 않는다는 단점이 있네요. 그래도 가상화에 따른 자원 손실이 최소화된다는 점에서 많이들 선호하고 있습니다. Heroku에서도 LXC를 통해 가상화를 하고 있죠.보통 이렇게 자신들의 플랫폼을 오픈소스로 공개하는 이유는 단순히 사회에 기여하기 위해서도 있지만, 사내에서 사용되는 기술의 수준을 오픈 소스 커뮤니티의 참여를 통해 향상시키고, 또 좋은 개발자들을 리크루팅 할 수 있게 되는 기회를 만드는 등 선순환을 유도하기 위해서입니다. 그러니까 여러분도 사내에서 사용하는 기술을 공개해 주시면 누이 좋고 매부 좋은 일이라 할 수 있죠.이 글은 __저의 개인 텀블러__에서도 찾아볼 수 있습니다.#스포카 #개발 #개발자 #개발팀 #인사이트 #Docker #클라우드 #꿀팁
조회수 930

조건문을 긍정적으로!

Overview“나 혼자 프로젝트를 하니 주석은 안 달아도 무방해요” 이렇게 말하는 개발자는 그 코드를 가장 많이 보는 것도 자신이라는 사실을 잊고 있을지도 모릅니다. 구린 코드를 보고 욕했는데 3개월 전 자신이 작성한 코드란 걸 알면 그제서야 얼굴이 붉어지기 일쑤죠. 작은 습관이지만 약간의 변화만 준다면 분명 즐겁고 생산적인 개발을 할 수 있을 겁니다. 얼굴 붉어질 일도 없고요. 오늘은 그 노하우를 전해드립니다. 혼돈을 피하는 여섯 가지 코드 작성법조건문은 긍정적으로 쓰자조건문의 성능은 생각하지 말자조건 검증을 깔끔하게 하자주석은 적절하게, 적당하게 하자상수를 활용하자복잡한 코드는 풀어서 쓰자과거의 나 자신아, 넌 나에게 똥을 줬어1.조건문은 긍정적으로 쓰자”쟤가 그 아이가 아니지 않지 않나?!” 프로그램 코드를 마지막으로 실행하는 건 컴퓨터지만 코드를 작성하고 관리하는 건 결국 사람입니다. 수많은 조건문이 존재하는 프로그램에서 조건이 부정적이라면 한 번 더 생각해야 합니다. 반대로 조건문을 긍정적으로 작성하면 보다 편리하게 개발을 진행할 수 있습니다. 가능하다면 긍정적인 마인드로 조건문을 적어봅시다.<?php // 예제는 PHP로 작성 되었습니다. $title = $_POST['title']; // 공지사항 제목 if (empty($title)) {     echo '제목을 입력해주세요';    return; } // 위의 경우보다 한번 더 생각해야한다. if (!isset($title)) {     echo '제목을 입력해주세요';    return; } cf)비슷한 사례 for 증감식을 i– 처럼 적는 경우 꼭 필요한 경우가 아니라면 삼가는 것이 좋다. for로 작성 가능한 반복을 while로 구현하는 경우 for는 끝이 명확하지만 while은 언제나 불안하다. 2.조건문의 성능을 생각하지 말자간혹 조건문에 성능을 고민해 줄여보려는 개발자가 있습니다. 10개의 and 조건을 2개로 줄인다면 얼마나 이득일까요? 하지만 이것은 티도 나지 않는 적은 양입니다.1) 조건문을 압축하지 마세요. 시간이 지나면 자신의 코드가 마치 보물지도처럼 보일 수도 있습니다. 조건문을 최적화하려고 하기보다는 보기 좋고 읽기 편하게 변경합시다. 3.조건 검증을 깔끔하게 하자만약 게시물에 글을 쓰는 프로그램을 제작한다면 요청된 값들이 정상인지 확인해야 합니다. ‘게시물 제목이 있고, 글 내용이 있고, 글 분류가 정상이고, 뭐뭐 하면 등록!’이라고 작성하면 논리적인 접근으로 보이지만 코드의 상태는 그렇지 않습니다.<?php $title = $_POST['title']; // 공지사항 제목 $content = $_POST['content']; // 공지사항 내용 $category = $_POST['category']; // 공지사항 분류 if (!empty($title)) {     if (!empty($content)) {         if (!empty($category)) {             // 게시글을 등록한다.!         } else {             echo '카테고리를 선택해주세요';         }     } else {         echo '내용을 입력해주세요';     } } else {     echo '제목을 입력해주세요'; } if문 블럭이 중첩되어 가로 스크롤 압박에 시달릴 것이기 때문입니다. 또한 나중에 수정하려면 많이 고생해야 합니다. 조건 검증을 하는 코드라면 아닌 경우를 체크하는 것이 더 좋습니다. 아래와 같은 형태로 작성하는 게 깔끔하고, 유지 보수에도 도움이 됩니다. 게시물 제목이 없으면 오류 출력글 내용이 없으면 오류 출력글 분류가 정상이 아니면 오류 출력그 외 등등…<?php // 예제는 PHP로 작성 되었습니다. $title = $_POST['title']; // 공지사항 제목 $content = $_POST['content']; // 공지사항 내용 $category = $_POST['category']; // 공지사항 분류 if (empty($title)) {     echo '제목을 입력해주세요';     return;  } if (empty($content)) {     echo '내용을 입력해주세요';     return;  } if (empty($category)) {     echo '카테고리를 입력해주세요';     return;  } // 게시글을 등록한다.! 4.주석은 적절하게, 적당하게 하자주석이 많아야 좋을까요, 아니면 적어야 좋을까요? 이 논제는 여전히 개발자 사이에서 뜨거운 감자입니다. 다양한 의견이 있지만 저는 ‘적당한게 좋다’고 생각합니다. 주석이 없어서 고생한 적도 있지만, 주석이 너무 많거나 쓸모없었던 적도 겪어봤기 때문입니다. 가끔 “코드 한 줄마다 주석을 달아”라는 미친 선임도 있었고 “주석이 필요 없게 깔끔하게 짜”라고 말하는 기괴한 선임도 있었습니다. 사고의 최종 결과물인 프로그램 코드가 아무런 설명 없이 다른 사람 혹은 미래의 자신을 이해시키는 건 불가능한 일이라고 생각합니다. 다양한 테크닉과 아름다운(?) 코딩으로 주석을 줄여나갈 수는 있겠지만 꼭 필요한 곳엔 적어야 한다고 생각합니다. 4-1) 주석이 꼭 필요하다고 생각할 때 깊은 사고의 결과를 코드로 작성하였고, 다음에 왜 그렇게 작성했는지 헷갈릴 것 같을 때함정 카드가 발동되어 헤맬 것 같은 코드일 때코드가 길어져 기능의 단위별로 나눠서 보는 게 좋을 때기술된 함수나 클래스가 이름과 다르게 동작하는 코드일 때한참 디버깅 후에 허무함을 안겨준 코드일 때함수 클래스 파일에 대한 주석일 때변수가 특이성을 가지고 있거나, 타입별로 세팅되는 값일 때플러그인이나 라이브러리 사용법을 공유할 때 4-2) 주석을 줄여 나가야 한다고 생각할 때 조건문의 내용을 한글로 다시 기술하고 있을 때프로그램과 관계 없는 내용일 때변수명으로 설명이 가능한 내용을 기술하고 있을 때4-3) 주석이 잘못 되었다고 생각할 때 나만 이해할 수 있는 단어나 문장으로 기술된 주석일 때주어가 없는 주석일 때5.상수를 활용하자코드값에 따라 분기를 작성 중이라면 상수를 활용하는 게 좋습니다. ‘F’보다는 FACEBOOK_SERVICE 가 더 직관적이기 때문입니다.<?php // 예제는 PHP로 작성 되었습니다. if ($userAccountType == 'F') {     // 페이스북 유저 처리 로직 } /** 유저 구분 값 페이스북 */ define('ACCOUNT_TYPE_FACEBOOK', 'F'); // 코드는 좀 더 길어보이지만 별다른 주석 없이도 어떤 코드인지 알 수 있다. if ($userAccountType == ACCOUNT_TYPE_FACEBOOK) {     // 페이스북 유저 처리 로직 } 상수는 프로그램마다 다양한 형태로 지원되기 때문에 선언 후 참조해서 쓴다면 주석을 줄이는 데에 많은 도움이 될 것입니다.6.복잡한 코드는 풀어서 쓰자여러 가지 사고의 결정이 다시금 엮여서 또 다른 결과를 만들어야 하는 복잡한 코드입니까? 우선 서술형 문장으로 먼저 정리하십시오. 그 다음 오류가 없다면 이어서 작성하는 것이 좋습니다. 2)// 기획전이 시작 되면 세팅한 값으로 할인을 하고 // 기획전이 끝나면 원래의 할인율로 돌아오게 하는 프로그램 이다. 1. 대상 기획전을 찾는다.     * 기획전 시작일이 오늘인가? 종료일이 오늘인가? 2. 트랜잭션을 연다. 3. 대상 기획전 건수 만큼 루프를 돌며     1. 조건 체크         case 1. 시작일이 오늘이면             1. 상품 상태를 기획전 데이터로 업데이트         case 2. 종료일이 오늘인면             1. 상품 상태를 시작일 이전 히스토리 데이터로 변경     2. 상품 히스토리를 남긴다. 4. 커밋한다. 저는 사고의 결과를 주석 형태로 작성하고, 순번을 달아서 진행을 정리합니다. 다음으로 정리된 내용을 검증하고, 주석을 중간 크기로 작성해 쪼갭니다. 그 밑에 코드를 작성하면 두 마리 토끼를 잡을 수 있습니다. 중간 크기의 주석은 프로그램의 진행 단위를 나눠서 보기 편하고, 단계별로 검증할 때에도 유용합니다.<?php /***************************************************** * 1. 대상 기획전을 찾는다. *     - 기획전 시작일이 오늘인가? 종료일이 오늘인가? **************************************************** */ // 세부 로직은 생략함 $list = getPlainedPromotionList(); /*****************************************************  * 2. 트랜잭션을 연다. **************************************************** */ beginTransaction(); /*****************************************************  * 3. 대상 기획전 건수 만큼 루프를 돌며 *****************************************************/ foreach ($list as $obj) { /*****************************************************  *        case 1. 시작일이 오늘이면  *            1. 상품 상태를 기획전 데이터로 업데이트 *****************************************************/     if ($obj['startDate'] == $today) {         updateProductDistRate($obj['productNo'], $obj['distRate']);    } /*****************************************************  *        case 2. 종료일이 오늘인면  *            1. 상품 상태를 시작일 이전 히스토리 데이터로 변경  *****************************************************/     if ($obj['endDate'] == $today) {        recoveryProductFromHistory($obj['productNo']);    } /*****************************************************  *    2. 상품 히스토리를 남긴다. *****************************************************/     addProductHistory($obj['productNo']); } /*****************************************************  * 4. 커밋한다. *****************************************************/ commit(); Conclusion영화 <인터스텔라(Interstellar, 2014)>의 주인공 쿠퍼(매튜 맥커너히)가 책장 너머 다른 차원에서 과거의 자신에게 신호를 보냈던 명장면이 생각납니다. “ STAY” 그의 메시지는 분명 후회의 몸부림이었을 겁니다. 마찬가지로 당신이 조건문을 부정적으로 만들고 있다면 잠시 키보드에서 손을 떼는 게 좋습니다. 다른 차원의 자신이 어딘가에서 메시지를 보내고 있을지도 모르니까요. “STOP….” 참고 1) 1초에 수백억 번 이상 연산이 가능한 컴퓨터에선 10회와 2회의 차이가 거의 없다. 2) 동료에게 정리한 문장을 이해시킬 수 있다면 정리가 잘 되었을 확률이 높다. 글천보성 팀장 | R&D 개발2팀[email protected]브랜디, 오직 예쁜 옷만#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 862

웹기반 컨텐츠 저작 도구 셀프(XELF) v1.0 GS인증 획득

웹기반 컨텐츠 저작 도구 셀프(XELF) v1.0 (Web-based Contents Authoring Tool XELF v1.0)이 한국정보통신기술협회(TTA) 소프트웨어 시험인증연구소로부터 GS인증 1등급을 획득하였습니다.  셀프(XELF)는 별도의 프로그램 설치 없이도 접속만으로 웹브라우저 상에서 다양한 용도의 콘텐츠를 저작할 수 있는 디자인 플랫폼입니다. 디자인 전문가가 아니어도 누구나 손쉽게 프리젠테이션, 웹브로셔, 유저 인터페이스, 문서 등 비즈니스 및 교육환경에 필요한 다양한 콘텐츠를 디자인할 수 있습니다. 또, 이렇게 제작된 콘텐츠는 클릭만으로 SNS에 공유하거나 이메일로 전달하는 등 간편하게 활용할 수 있는 장점을 가지고 있습니다.   GS인증은 엄격한 시험을 통해 품질이 우수한 소프트웨어를 인증해주는 국가공인 소프트웨어 품질인증제도로 공공기관에서 우선 구매 대상으로 지정되기도 합니다. ISO 국제표준을 기준으로 SW의 기능성, 신뢰성, 효율성, 사용성, 유지보수성, 이식성, 성능 등을 평가하고 검증을 거쳐 부여되었습니다. ㈜그로비스인포텍은 이번 GS인증을 계기로 디자인 플랫폼으로서의 기술성과에 자신감을 가지고 향후 계획된 베타서비스 준비에 최선을 다하고 있습니다. 더 나은 사용성과 기술적 안정성을 목표로 다양한 환경에 적용하고 테스트를 진행하고 있습니다. 곧이어 더 향상된 성능과 기능으로 찾아뵙길 기대하겠습니다. 감사합니다.
조회수 928

2017 NDC 리뷰) 몬스터 슈퍼리그 리텐션 프로젝트

 2017년 4월 25일부터 27일까지 진행된 Nexon Developer Conference 에 나녀온 후기입니다.제가 들었던 재밌는 세션들 하나하나 올릴 테니 기대해 주세요! :)2017 NDC 재밌었다능!!!몬스터 슈퍼리그 리텐션 15% 개선 리포트 - 숫자보다 매력적인 감성 테라피"몬스터 슈퍼리그"의 게임 리텐션 개선 리포트였는데요, 기본적인 서비스의 소비자를 향한 어프로치인"당신은 똑똑한 유저!"라는 인식 심기(쉬운 접근성/ 심도 깊은 진행 유도)"빠른 어필"(이벤트에 대한 빠른 피드백)"축복받은 계정" (다양하고 많은 초반 보상)이라는 인식과,"주어지면 알아서 하겠지""보상이 있으면 알아서 하겠지"라는 생각을 지양하고, "의도하지 않았지만 수행을 할 수 있도록" 하는 넛지(Nudge) 효과를 일으킬 수 있는 무의식을 자극하는 재밌는 전략에 대해서 흥미를 느꼈습니다. 그리고 이후 리텐션 강화를 위한 프로젝트로 가장 중요한 건,단지 "무슨 기능을 만들 것이냐?"가 아닌, "어떤 부분에서 유저가 이탈"하게 되고,이탈한 유저들 중 "우리가 진짜 챙겨야 할 유저"가 어떤 유저들인가에 집중한다.라는 부분에서 항상 우선돼야 하지만, 되지 못한 부분들을 생각하게 되었던 것 같아요. 그래서 이를 통해스스로 모험 입장을 몇 번 한 유저: 원하는 것에 접근하지 못한 유저에 대한 파악 후 개선다음 지역에 접근 한 유저: 지속 플레이 의향 있으나, 니치를 못 찾은 유저들의 의도 파악 후 개선이라는 개선에 필요한 정확한 목표를 가지고, 어떤 방식으로 접근해야 하는지에 대한 고민을 보는 것에 정말 재미를 느꼈고요, 이에 대한 진행방향을 듣는 것도 정말 값진 경험이었습니다.개선 시퀀스1차 개선 (-)보상 10배 상향에도 불구, 큰 성장 없음.>"가치비교가 익숙하지 않은 유저들에게 보상의 절대적 수치 증가는 큰 감흥이 없다."라는 점을 파악하고, 유저에 "감정"을 터치하는 방법을 고안.2차 개선 (+ & -)조사 결과, 첫 패배 지점에서 유저들의 높은 이탈률을 파악> 패배 지점을 인위적으로 미루지 않되, 패배에 신경 쓰지 않도록 다른 부분들에 대한 장치를 추가.> "도전 가능한 포인트를 생성하는 것은 유효하다."는 부분을 Metric으로 확인했으나, 타깃 유저 범위를 정확하게 파악하지 않고  Metric만 집중해서 정확한 범위 파악을 놓침.3차 개선 (+ & +)유저가 얻을 수 있는 보상의 기회를 꼭 찾아가도록 유도옵션 1. 텍스트 강조? 텍스트는 망각의 영역 (X)옵션 2. 강제 터치? 이미 자유 플레이가 된 유저에게 부자연스러운 접근 (X)옵션 3. 얻고 싶은 보상이 있다면, 어떨까? 그리고 보상 등에 대해서 스토리 텔링이 될 수 있다면?  (O)  - 부정 경험 개선을 통한 리텐션 향상 효과  - 초반에 한 일을 다시 하게 하는 것은 큰 부정 경험을 초래  - 강제적 이동보다는 원하는 보상을 통해 부여4차 개선 (+)스토리 텔링 요소 추가  1. 일러스트  2. 캐릭터에 대한 스토리 추가글로벌 원빌드로서 북미권 영역에서  특히 추가결과개선 프로세스 이후,"무언가가 무조건 있다."라는 이야기 보단, "기대치 않은 행동에 대해서 얻는 보상의 획득"으로 유저의 감성을 자극하는 스토리 텔링의 중요성 확인.교훈보상도 주지만, "보상을 준다"라는 이벤트를 행하는 것 만으로 서비스 제공자는 끝내선 안된다. 보다 감성적인 접근을 통해 유저의 감정을 이해하는 것이 더 중요하다. 첫날 첫 번째 세션이었는데요, 아침부터 정말 보람찬 세션 들을 수 있어서 정말로 감사했습니다. 사실, 게임이건, 모바일 서비스건, 웹 서비스건 "소비자를 이해한다."라는 부분은 언제 어디서나 필요한 부분이지만, 결과적으로 서비스 제공자들은 "보상을 제공했다."로 서비스 제공을 스스로 끝을 내버리는 순간들을 더 많이 마주하기 때문에, 다양한 분야들에서 생각해 볼만한 이야기라고 생각합니다. 한줄평: 중요한 건, "내가 이런 걸 줬다!"보다는 "이런 걸 줘서 고마워"라는 것을 느낄 수 있도록 소비자의 마음에 초점을 맞추는 서비스 제공이 맞다!#코인원 #블록체인 #기술기업 #암호화폐 #스타트업인사이트
조회수 4976

소스코드 리뷰에 대한 짧은 이야기...

개발자와 개발 조직에게 소스코드 리뷰는 필수적이다. 팀간의 협업과 대화를 보다 원활하게 만들어 주는 매우 필요한 절차이다. 슬랙과 같은 협업도구가 명쾌하게 의미 있게 활용되려면 개발팀 간의 소스코드 리뷰는 필수적으로 수행되는 것이 좋다.매우 당연한 이야기이지만, 소스코드 리뷰는 거북하고 불편하고 어렵고 힘들다. 그럼에도 불구하고 필수적인 이벤트가 되어야 하는 이유가 너무도 많다. 개발자들에게 코드리뷰에 대한 이슈를 설득하고 실제 행위를 발생시키는 것은 정말 어려운일이다. 더군다나 뜬금없이 코드리뷰 이야기를 회사나 팀리더에게서 갑자기 듣는다면 개발자는 매우 불편해 한다. 그것은 매우 당연한 반응이다. 그러므로, 가능하다면 팀 세팅 초기 시부터 이 소스코드 리뷰 문화는 만들어질 수 있게 노력하는 것이 최선일 것이다.초기에 세팅된다면 그 후에 들어오는 팀원들은 자연스럽게 그 문화에 익숙해진다. 이런 일련의 작업들은 결국 조직과 팀의 단결과 협력, 향후 유지보수에 매우 긍정적인 효과를 준다.매우 당연하지만 개발자들은 팀에 소속되고 빠져나가기를 반복한다. 이를 두려워하지 않는 방법 중에 가장 먼저 선택할 수 있는 것이 바로 코드 리뷰라는 행위다. 인수인계와 유지보수를 위해서 소스코드 리뷰를 각 단계별에 배치해두고, 그 시간을 투자하는 것을  아까워하지 않도록 하자.그렇다면, 소프트웨어의 본체인 소스코드를 타인이 리뷰한다는 것이 왜 어려울까? 그것은 소스코드는 언제나 완성상태가 아니라는 점 때문이다. 개발자의 생각은 무언가 다양한 변화를 예측하고 있고, 그 상세한 준비를 담고 있다. 언제나 소스코드는 완성 상태가 아니라, 변화되어야 하는 시간의 축을 담고 있기 때문이다.하지만, 소프트웨어 품질이 중요한 현재의 시점에서 본다면, 코드 리뷰라는 행위는 정말 필수 불가결한 행위에  해당한다고 생각한다.이런 필수적인 코드리뷰는 그 형태와 범위에 대해서 팀 내부에 잘 정의되어야 한다.그래서, 보통 이 코드리뷰를 어떻게 할 것인가에 대해서 조직이나 담당하는 사람의 경우에는 명쾌한 판단 기준이 있어야 한다. 그러한 ‘판단기준’을 가져야만 명확한  리뷰될 수 있다.이를 두고, 디자이너에게는 크리틱(critique-비평)이 있고, 개발자에게는 코드리뷰가 있다고 정의한다.좋은 비평을 받고 좋은 리뷰를 하려면 다음의 3가지 원칙이 필수이다.1. 리뷰는 언제나 상호 합의가 되어진 상황에서 진행되어야 한다.2. 리뷰어의 해당 결과물에 대해서 객관성을 가지고 서로 인지해야 한다3. 개발자 자신의 작업물에 대해서 정말 객관적으로 바라볼 수 있는 작성가가 선정되어야 한다.특히, 소프트웨어 코드는 정량적인 검토와 정성적인 검토를 구분해야 한다. 이 영역의 구분이 모호해지면, 리뷰는 그 방향성을 상실하게 된다. 그중에 특히, 정량적인 검토와 기본적인 규칙들은 가능한 자동화하고, 소스 형상관리 도구에서 기본적인 것들의 규칙들을 지키도록 권장하여야 한다. 최소한 이 정량적인 것만 자동화하고  규칙화해도 소프트웨어의 품질은 급상승한다.하지만, 코드는 논쟁을 발생시키고, 어떤 것이 우선적인지에 대해서 서술하기 매우 어렵다. 이러한 점은 정성적인 부분에 대해서 검토할 때에 고민하자.코드리뷰의 정도는 어느 정도 해주어야 하는가?그 전부터 주목하는 개발 방법론의 추세는 ‘테스팅’을 주로 하고, SRS와 같은 요구사항에 집중하기 보다는, TDD와 같은 방법으로 완성 산출물을 높이는 방법을 현재에는 주로 사용하고 있다.그것은 과거에는 요구사항을 통해서 결과물이 완성되는 SI성 개발이 주로였다면, 현재에는 요구사항은 계속 변화하고 버그 없는 결과물이 중요시되는 테스트를 얼마나 더 집중적으로 하느냐에 따른 웹서비스의 시대이기 때문에 그 방향성은 시대에 따라서 변화를 많이 하였다. 그래서, 슬프지만, 당장의 성과물을 위해서라면 코드리뷰보다는 테스팅에 집중하는 것이 더 효율적이다. 빠르게 고속 개발하고 테스트를 통해서 버그를 찾은 다음 수정하는 것이 ‘특정 기능들을 나열하고 기능을 만족하는 소프트웨어’의 경우에는 테스트 주도 개발 방법이 가장 적합하다고 할 수 있다.물론, 이러한 방향성이나 전체적인 틀에 대해서는 아키텍트가 잘 결정하여야 한다. 내가 속한 개발 결과물이 어떤 결과물이냐에 따라서 이 방법은 혼용되어져서 사용되어야 하기 때문이다.하지만, 이번 글의 주목적은 코드리뷰. SRS중심이건, TDD중심이건. 코드리뷰는 중요하다는 것을 강조하고 싶다. 특히, 코드리뷰는 ‘기능 나열’이 아닌, 어느 정도 이상의 복잡도나 코드 품질이 필요한 경우에는 필수적으로 수행하는 것이 매우 현명한 행동이다.물론, 코드리뷰 행위가 불필요한 업무들도 많다. 정해져 있는 단순한 업무를 수행하는 경우에는 굳이 할 필요 없다. 국내에서 SI를 하는 경우에는 대부분 코드리뷰가 필요 없는 업무를 하는 소프트웨어 개발자들이 절대 다수인 경우도 많이 보았다.일반적인 SI의 형태라면 워크 스루의 형태만 적합하다. 특정 도메인에 매몰되어 있고, 처리방법이 명쾌하기 때문에, 해당 경험들을 교환하는 것으로도 충분하기 때문이다. 그리고, 자동화된 테스트 수행방법을 최대한 갖추어두는 것이 가장 현명하다.그러므로, 코드리뷰는 어느 정도 솔루션이나 서비스 등을 고려하고 있는 곳에서 더욱 적합하다고 정의한다.코드리뷰는 특정 제품이나 서비스를 발전적으로 지향하고 있는 경우라면 필수적으로 선택해야 한다. 하지만, 일부 제품의 경우에는 발전적인 지향이 굳이 필요 없는 제품 라인업을 가진 경우에도 굳이 수행할 필요 없다.그 경우에는 선택적인 코드리뷰를 지향하면 된다. 비용상의 문제 때문에 굳이 코드리뷰를 억지로 진행할 필요는 없는 경우도 많다. 대부분의 소프트웨어 개발은 테스트 케이스를 잘 만들고, 통과시키는 것으로써 충분한 신뢰를 가지면 충분한 경우가 대부분이다.특히, 시장이 고착상태이거나, 특별한 변화의 폭이 없다면, 그 정도로 충분한 경우가 된다. 다만, 글로벌 서비스나 웹서비스 등의 지속적인 확장이 필요한 경우라면, 코드리뷰는 필수라고 할 수 있다.코드리뷰가 필요 없는 경우 체크리스트는 다음의 5가지 정도를 체크해보자.1. 특정 도메인만 다루는 팀이나 회사의 개발팀인가?2. 지난 2~3년 정도 솔루션이 크게 변한 것이 없으며, 향후로도 기업이나 팀에서 투자가 없을 예정이다.3. 현재 개발자들이 해당 솔루션에 대한 개발일을 5년 이상하고 있다.4. 기능 위주의 SI성 업무를 주로 처리하고 있으며, 복잡한 알고리즘은 존재하지 않는다.5. 비용과 일정상 개발팀에게 리소스 투여가 불가능하다위의 사례에서 1개 이상이라도 체크된다면, 코드리뷰는 성립하기 힘들다. 대부분 단념하고, TDD나 테스트 케이스를 가능한 많이 축적하여 소프트웨어 품질을 올리기를 권장한다.코드리뷰가 필요한 경우의 체크리스트도 다음의 5가지 정도를 체크해보자.1. 다국어와 시장이 다변화된 환경에서 소프트웨어가 구동되어야 한다.2. 코드의 복잡도가 높으며, 단순 기능 나열의 요구사항이 아니라, 소프트웨어 아키텍처가 별도로 구성되기 시작하였다.3. 사용자의 경험성을 증가하기 위하여 매우 많은 변화가 예측된다.4. 현재 개발 중인 서비스는 중단 없이, 지속적으로 발전되어야 하는 서비스이다.5. 목표 요구사항이 계속 변화하고 있고, 프레임워크를 지향하여 소프트웨어 품질의 요구사항이 매우 중요하다.위의 케이스에서 하나라도 해당이 된다면, 코드리뷰는 매우 효과적으로 소프트웨어에 의미 있는 결과물들을 얻어 내기 위한 좋은 방법이 된다.하지만, 다음과 같은 경우도 같이 고려하여야 한다.코드리뷰의 정도와 질에 대한 검토 리스트의 최소 체크리스트는 다음의 3가지이다. 물론, 이 정의는 조직 내의 아키텍트나 아키텍트 롤을 하는 사람이 결정하는 것이 좋다.1. 실험적인 코드인가?2. 1~2명 이상이 공동으로 작업하는 코드인가?3. 향후 버려질 가능성이 높은 코드인가?코드리뷰를 하지 않는 경우에는 해당 코드의 repository나 디렉터리를 완전하게 분리하고, 리뷰가 안된 코드를 명쾌하게 구분할 수 있어야 한다. 그리고, 그 정보는 팀 전체에게 공개되어야 한다.가장 첫 번째는 코딩규칙 가이드라인의 준수 여부를 체크하는 것이다.개발자들 간의 상호 중요한 것은 스타일 가이드이다. 하지만, 정말 지키기 어려운 것 또한 스타일 가이드라고 할 수 있다. 하지만, 스타일 가이드는 가능한 준수해야 한다. 하지만, 100% 준수하려는 것은 매우 비효율적인 상황을 만들 수 있다. 하지만, 이 경우에 최소한 리뷰어가 제시하는 기준이나 변경 방향에는 대부분 수긍하는 것이 가장 현명하며, 이 부분은 해당 팀의 가장 경험이 풍부한 사람이 리드하는 것이 좋다.그래서, 소프트웨어 개발에는 경험이 풍부한 아키텍트의 역할과 선임의 역할이 가장 중요하다. 소셜에서 이야기하는 가장 중요한 포인트는 이런 경험이 풍부한 선임 개발자가 있다면, 돈이 얼마가 들더라도 ‘개발팀’에 모셔야 한다! 가 정답일 것이다.아직까지 이 부분은 ‘공학’으로 해결할 수 없고, ‘엔지니어링’과 ‘경험’에 의존할  수밖에 없다.주석의 경우에도 ‘가독성’이 충 부한 코드에는 서술할 필요 없다. 이 부분에 대해서는 꾸준한 팀원들 간에 코딩 문화에 대해서  커뮤니케이션하면서 주석의 범위에 대해서 공론화하는 것이 현명하다. 그래서, 소프트웨어 개발은 대부분이 ‘커뮤니케이션’이고 ‘소통’이다. 그래서, ‘팀워크’이 가장 중요한 것이고. 변수의 명칭에 대해서도 ‘명확’하다는 선에서 합의해야 한다.테스트가 쉽지 않은 구조는 다른 문제를 야기한다. Junit과 같은 단위 테스트 도구로 손쉽게 정의가 가능한 구조가 아니라면, 변경해야 한다.코드리뷰 후에 분명하고 타당한 지적에도 고집이 세서 변화가 없는 경우에는 한두 번 이야기하고 더 이상 변화가 없다면, 포기하고. 해당 코드를 격리하여 관리하는 것이 현명하다.  팀원들 간에 감정이 상하는 것이 더 위험하다. 사람은 변하지 않는다 감정에 대한 다툼이나 기대를 할 필요가 없다.UI가 중요한 코드는 해당 코드들이 급변할 가능성이 농후하다. 처음부터 공을 들여서 추상화를 실현하지 않으면, 해당 코드 때문에 프로젝트가 심각해질 수 있다. 사용자에게 더 좋은 경험을 전달하려고 하면, UI코드는 계속 변화를 일으킨다.테스트 코드 여부? 로직에 대한 검토, 변수 네이밍 검토와 레이아웃에 대한 것들? 에 대해서는 다음과 같이 판단하고 체크해보자.코드리뷰는 대부분 ‘직관’에 의존한다. 그래서, 정말 어렵고. 경험이 풍부한 사람이 할  수밖에 없다. 다만, 이러한 코드 리뷰 시의 체크리스트 항목을 몇 가지 간단하게 정리할 수 있다. 최소한의 2가지는 꼭 지키자.코드 리뷰 시의 필수 내용 두 가지는 다음과 같다.1. 코드 검토는 1시간 이내에 끝낼 분량으로 검토한다.2. 코드는 200라인 이상을 한 번에 검토하지 마라이 기준이 어겨지면, 리뷰어는 제대로 된 리뷰를 하기 어려울 것이다.  그리고, 이러한 리뷰를 하는 동안 기능에 대한 검토 체크사항에 대해서 나열해 보면 다음과 같이 나열이 될 수 있을 것이다.1. 시스템의 요구사항이 제대로 반영되었는가?2. 시스템의 설계의 규격대로 구현되었는가?3. 과도한 코딩을 하고 있지 않는가?4. 같은 기능 구현을 더 단순하게 할 수 있는가?5. 함수의 입출력 값은 명확한가?6. 빌딩 블록들( 알고리즘, 자료구조, 데이터 타입, 템플릿, 라이브러리, API )등이 적절하게 사용되었는가?7. 좋은 패턴과 추상화( 상태도, 모듈화 )등을 사용해서 구현하고 있는가?8. 의존도가 높은 함수나 라이브러리 등의 의존관계에 대해서 별도 기술하고 있는가?9. 함수의 반환(exit)은 한 곳에서 이루어지고 있는가?10. 모든 변수는 사용 전에 초기화하고 있는가?11. 사용하지 않는 변수가 있는가?12. 하나의 함수는 하나의 기능만 수행하고 있는가?또한, 스타일과 코딩 가이드에 대해서고 검토하고 리딩을 해야 한다.1. 코딩 스타일 가이드를 준수하고 있는가?2. 각 파일의 헤더 정보가 존재하는가?3. 각 함수의 정보를 코드에 대해서 설명하기에 충분한가?4. 주석은 적절하게 기술되어있는가?5. 코드는 잘  구조화되어있는가? ( 가독성, 기능적 측면 )6. 헤더, 함수 정보를 도구로 추출해서 자동으로 문서화할 수 있는 구조인가?7. 변수와 함수의 이름이 일관되게 기술되어 있는가?8. 프로젝트의 가이드를 통한 네이밍 규칙을 준수하고 있는가?9. 숫자의 경우 단위에 대해서 기술하고 있는가?10. 숫자를 직접 서술하지 않고, 상수를 사용하고 있는가?11. 어셈블리 코드를 사용하였다면 이를 대체할 방법은 없는가?12. 수행되지 않는 코드는 없는가?13. 주석 처리된 코드는 삭제가 되었는가? ( 버전 체크가 되었는가? )14. 간결하지만 너무 특이한 코드가 존재하는가?15. 설명을 보거나 작성자에게 물어봐야만 이해가 가능한 코드가 있는가?16. 구현 예정인 기능이 있다면, ToDo주석으로 표시되어 있는가?가장 중요한 아키텍처에 대한 검토를 잊으면  안 된다.1. 함수의 길이는 적당한가? ( 화면을 넘기면  안 된다. )2. 이 코드는 재사용이 가능한가?3. 전역 변수는 최소로 사용하였는가?4. 변수의 범위는 적절하게 선언되었는가?5. 클래스와 함수가 관련된 기능끼리 그룹화가 되었는가? ( 응집도는 어떤가? )6. 관련된 함수들이 흩어져 있지 않는가?7. 중복된 함수나 클래스가 있지 않는가?8. 코드가 이식성을 고려하여 작성되었는가? ( 프로세스의 특성을 받는 변수 타입이 고려되어있는가? )9. 데이터에 맞게 타입이 구체적으로 선언되었는가?10. If/else구분이 2단계 이상 중접되었다면 이를 함수로 더 구분하라11. Switch/case문이 중첩되었다면 이를 더 구분하라12. 리소스에 lock이 있다면, unlock은 반드시 이루어지는가?13. 힙 메모리 할당과 해제는 항상 짝을 이루는가?14. 스택 변수를 반환하고 있는가?15. 외부/공개 라이브러리 사용하였을 경우에 MIT 라이선스를 확인했는가? GPL의 경우에는 관련된 영역에서만 사용해야 한다.16. 블로킹 api호출시에 비동기적인 방식으로 처리하고 있는가?당연하겠지만, 예외처리 관련 체크리스트도 제대로 검토해야 한다.1. 입력 파라미터의 유효 범위는 체크하고 있는가?2. 에러코드와 예외(exception)의 호출 함수는 분명하게 반환되고 있는가?3. 호출 함수가 어려와 예외처리 코드를 가지고 있는가?4. Null포인트와 음수가 처리되는 구조인가?5. 에러코드에 대해서 명쾌하게 선언하고 처리하고 있는가?6. switch문에 default가 존재하고, 예외처리를 하고 있는가?7. 배열 사용시에 index범위를 체크하는가?8. 포인트 사용시에 유요한 범위를 체크하는가?9. Garbage collection을 제대로 하고 있는가?10. 수학계 산시에 overflow, underflow가 발생할 가능성이 있는가?11. 에러 조건이 체크되고 에러 발생 시 로깅 정보를 남기는가?12. 에러 메시지와 에러코드가 에러의 의미를 잘  전달하는가?13. Try/catch 에러 핸들링 사용방법은 적절하게 구현되었는가?요즘 프로그램은 대부분 이벤트성으로 구동되지만, 시간의 흐름에 대한 체크는 프로그램의 뼈대를 이루게 된다. 이 부분에 대해서도 제대로 검토해야 한다.1. 최악의 조건에 대해서 고려하였는가?2. 무한루프와 재귀 함수는 특이사항이 아니라면 없어야 한다.3. 재귀 함수 사용시에 call stack값의 최댓값이 고정되어 있는가?4. 경쟁조건이 존재하는가?5. 스레드는 정상 생성, 정상 동작하는 코드를 가지고 있는가?6. 불필요한 최적화를 통해서 코드 가독성을 희생하였는가?7. 임베디드의 경우에도 최적화가 매우 중요하지 않다면, 가독성을 더 중요하게 해야 한다가장 중요한 검증과 시험에 대해서도 제대로 인지하여야 한다. 그리고, 테스트를 위해서 가능한 최대한 자동화를 하기 위한 방법들을 이용해야 한다.1. 코드는 시험하기 쉽게 작성되었는가?2. 단위 테스트가 쉽게 될 수 있는가?3. 에러 핸들링 코드도 잘  테스트되었는가?4. 컴파일, 링크 체크 시에 경고 메시지도 100% 처리하였는가?5. 경계값, 음수값, 0/1등의 가독성이 떨어지는 코드에 대해서 충분하게 경계하고 있는가?6. 테스트를 위한 fault 조건 재현을 쉽게 할 수 있는가?7. 모든 인터페이스와 모든 예외 조건에 대해서 테스트 코드가 있는가?8. 최악의 조건에서도 리소스 사용은 문제가 없는가?9. 런타임 시의 오류와 로그에 대비한 시스템이 있는가?10. 테스트를 위한 주석 코드가 존재하는가?간혹 등장하는 하드웨어에 대한 테스트도  마찬가지이다. 다음과 같은 기준들을 통해서 검토해야 한다.1. I/O 오퍼레이션 코드에 대한 테스트로 하드웨어가 정상적인 동작을 보장하는가?2. 최소/최대 타이밍 요구사항에 대해서도 하드웨어 인터페이스가 충족하는가?3. 멀티 바이트 하드웨어 레지스터가 read/write오퍼레이션 중에도 값이 바뀌지 않음을 보장하는가?4. 시스템이 잘 정의된 하드웨어 상태로 리셋하는 것을 S/W가 보장하는가?5. 하드웨어의 전압이 떨어지거나 전원이 차단되는 경우에 잘 처리하는가?6. 대기모드 진입 시와 빠져나 올 때에 시스템이 옳게 동작하는가?7. 사용하지 않는 인터럽트 벡터가 에러 핸들러에 연결되어 있는가?8. EEPROM손상(데이터 깨짐)을 막기 위한 메커니즘이 있는가? ( 쓰기 동작 중 powe loss)등구체적으로 코드리뷰를 하고자 한다면, 다음의 코드리뷰에 대한 기법과 적당한 방법을 다음과 같이 설명할 수 있다.이러한 코드 리뷰를 위한 몇 가지 방법들이 알려져 있다. 그것들을 몇 가지 정리하여 보면 다음과 같다. 코드 인스펙션은 가장 정형화된 기법으로 전문화된 코드리뷰팀을 통해서 구분하는 방법이다. 이 방법은 리소스가 풍부하고, 일정에 여유가 있는 경우에만 사용이 가능하다. 대부분 대기업이나 대형 포털에서 구현 가능한 방법이라고 할 수 있다. ( 이런 곳에 있다면 행복해 하자. ~.~ ) 하여간, 비용과 일정 등이 있다면 이 방법이 현명하다. 그리고, 코드리뷰에 대한 품질에 대해서 정량적인 보고와 구성을 만들어 낼 수 있다는 것은 코드 인스팩션의 가장 좋은 장점이다. 이 코드 인스팩션을 하기 위한 롤을 구분하면 다음과 같이 4가지 롤로 구분할 수 있다.1. ModeratorA. 실질적인 매니저로 팀 간의 인터페이스와 리소스, 인프라를 확보하고, 프로세스에 대한 정의와 산출물의 정리를 담당한다.2. ReaderA. 각 산출물을 읽고, 리뷰하고, 방향성을 제시한다. 보통, 지식이 많은 사람이 담당한다.3. Designer/CoderA. Reader의 지시에 따라서 코드를 검증하고 잠재적인 발견 등의 수정 방안을 만든다.4. TesterA. 진행 중인 코드와 권장 수정 코드에 대해서 검증한다.그리고, 코드 인스펙션은 다음과 같은 6단계로 진행된다.1. PlanningA. 계획 수립2. OverviewA. 교육과 역할 정의3. PreparationA. 인터뷰와 필요한 문서 습득, 툴 환경 구축4. Meeting(Inspection)A. 각자의 역할대로 수행5. ReworkA. 보고된 Defect 수정6. Follow-upA. 보고된 Defect가 수정되었는지 확인이러한 절차를 통해서, 코드 인스팩션이 수행되면, 상당히 명쾌한 리뷰가 진행되게 된다. 하지만, 일정과 비용 문제 때문에 이 작업은 대부분의 스타트업에서는 선택하기 어렵다. 그래서 사용하는 방법 중의 하나가 팀 리뷰이다.팀 리뷰는 일정한 계획과 프로세스만 따르는 방법으로, 코드 인스펙션보다는 좀 덜 정형화된 방법으로 진행한다. 보통은 일주일에 한번 정도 팀 리뷰를 수행하거나, 특정 모듈이나 기능이 완료되는 시점을 기준으로 테스트 결과를 가지고 리뷰를 하는 방법을 사용한다.또한, 위험하거나 의견이 필요한 경우에도 팀 리뷰는 유용하다. 일반적인 팀에서 사용하는 방법이다.하지만, 이 역시. ‘리뷰’에 대한 제대로 된 인식이 없다면, 적용하기 어렵다. 그래서, 가끔 사용되는 방법이고, 과거 국내 SI업체들이 주로 사용하던 방법 중의 하나가 ‘웍쓰로’이다.웍 쓰루(Walkthrough)는 단체로 하는 코드 리뷰 기법 중에 비정형적인 방법으로, 발표자가 리뷰의 주제나 시간을 정해서 발표하고 동료들로부터 의견이나 아이디어를 듣는 시간을 가지는 방법으로써 주로 사례에 대한 정보 공유나 아이디어 수집을 위해서 사용하는 방법이다.이 방법은 ‘특정 도메인’에 종속된 코드를 만들거나, 비슷한 SI성 형태의 업무를 수행하는 경우에 적합하다. 그래서, 국내의 SI업체에서는 적극적으로 사용되면 좋겠지만. 이 ‘시간’마저도 부정확하고, 갑을병정의 SI체게에서 ‘정보공유’나 ‘아이디어 수집’과 같은 커뮤니케이션이 자유롭게 일어나는 것은 매우 힘들다.이 웍 쓰루는 동일한 조직 내에서 동일한 목적의식이 분명한 팀에서나 활용이 가능한 방법이다. 웍 쓰루를 SI에서 시도한 경우에는 대부분 실패했거나, 목적의식이 다르기 때문에 불분명한 결론들이 대부분 도출되었다.대부분의 국내 스타트업이나 IT 전문기업들은 ‘리뷰’에 대해서 상급 관리자들이 제대로 허락을 해주지 않는다.대부분은 팀내에서 어떻게든 자체적으로 해보려고 한다. 그래서, 팀장의 권한 선에서 적절하게 리뷰를 하는 방법 중의 하나가 Peer review or over the shoulder review방법이다. 이 방법은 보통 2~3명이 진행하는 코드리 뷰로 코드의 작성자가 모니터를 보면서 코드를 설명하고, 다른 한 사람이 설명을 들으면서 아이디어를 제안하거나 Defect를 발견하는 방법이다.또한, 이 방법은 신입사원이나 인턴사원의 경우에 업무 이해도를 높이면서 해당 코드를 사용할 수 있는 수준으로 활용할 경우에 의미 있는 방법이다. 문제는 이 방법은 개발자의 인력 투입이 거의 두배 이상으로 증가하는 것으로써, 고품질의 영역을 개발하거나, 빠른 시간 안에 신입 개발자의 업무 이해도를 높이는 경우가 아니라면 시행하지 않는다.이렇게도 리뷰가 진행이 되지 않으면, Passaroud는 돌려 보기 방법을 사용한다. 이 방법은 원래 상세한 리뷰 방법은 아니다. 온라인이나 실시간성이 아니라, 리파지토리나 이메일 등을 사용하여 천천히 리뷰하는 방식에 해당하는데, 속도는 느리지만, 중요한 코드이거나, 제품의 기능 개선이 필요한 경우에는 아주 의미가 있다. 보통은 제품의 기능 개선을 위하여 사용하는 방법이다.이처럼 리뷰의 방법에는 다양한 방법이 있지만, 결론적으로는 어느 정도 개발 조직이 서로  커뮤니케이션하고, 목적의식을 통일하고, 적절한 시간 분배를 통해서 리뷰를 할 수 있는 시간을 만들어 내느냐가 리뷰의 핵심이라고 할 수 있다.리뷰를 통해서 소프트웨어의 품질을  끌어올리고, 개발자들과 소통하고, 방향성을 만들어 내며, 새로운 기능 개선 작업을 위해서 리뷰는 다양하게 활용된다. 어떤 관점으로 리뷰를 할 것이고, 어떤 관점으로 리뷰라는 프로세스를 개발 프로세스에 탑재할 것인가에 대해서 진지하게 고민하는 것. 그것이 아키텍트의 첫 번째 역할 아닌가 한다.
조회수 4417

Flask로 만들어 보는 WSGI 어플리케이션

안녕하세요. 스포카 크리에이터팀 문성원입니다. 오늘은 WSGI(Web Server Gateway Interface)어플리케이션을 직접 작성해보고, 또 이런 작성을 보다 쉽게 도와주는 프레임워크 중 하나인 Flask에 대해서 알아보겠습니다.WSGIWSGI에 대해 기억이 가물하신 분들을 위해 지난 글의 일부를 잠깐 다시 살펴보죠.이 경우 uwsgi는 일종의 어플리케이션 컨테이너(Application Container)로 동작하게 됩니다. 적재한 어플리케이션을 실행만 시켜주는 역할이죠. 이러한 uwsgi에 적재할 어플리케이션(스포카 서버)에는 일종의 규격이 존재하는데, 이걸 WSGI라고 합니다.(정확히는 WSGI에 의해 정의된 어플리케이션을 돌릴 수 있게 설계된 컨테이너가 uwsgi라고 봐야겠지만요.) WSGI는 Python 표준(PEP-333)으로 HTTP를 통해 요청을 받아 응답하는 어플리케이션에 대한 명세로 이러한 명세를 만족시키는 클래스나 함수, (__call__을 통해 부를 수 있는)객체를 WSGI 어플리케이션이라고 합니다.글로는 감이 잘 안오신다구요? 그럼 코드를 보면서 같이 살펴봅시다. (모든 코드는 Python 2.7에서 테스트 되었습니다.)Hello World!def app(environ, start_response):    response_body = 'Hello World!'    status = '200 OK'    response_headers = [('Content-Type', 'text/plain'),                         ('Content-Length', str(len(response_body)))]    start_response(status, response_headers)        return [response_body]view rawgistfile1.py hosted with ❤ by GitHubapp은 일반적인 Python 함수지만, 동시에 WSGI 어플리케이션이기도 합니다. environ과 start_response를 받는 함수기 때문이죠.(PEP-333) 사실은 꼭 함수일 필요도 없습니다. 다음은 위의 app과 동일한 동작을 하는 WSGI 어플리케이션입니다.class App(object):    def __init__(self, environ, start_response):        self.environ = environ        self.start_response = start_response    def __iter__(self):        status = '200 OK'        response_body = "Hello World!"        response_headers = [('Content-Type', 'text/plain'),                            ('Content-Length', str(len(response_body)))]        self.start_response(status, response_headers)        yield response_bodyview rawgistfile1.py hosted with ❤ by GitHubApp는 Python 클래스(Class)로 environ과 start_response를 멤버 변수로 가지는데, 여기에는 약간의 트릭이 있습니다. 생성자(Constructor)인 App를 함수처럼 사용하게 하여 리턴되는 결과(실제로는 생성자를 통해 생성된 객체겠죠.)가 \_\_iter\_\_를 구현한 순회 가능한(Iterable) 값이 되게 하는 것이죠. (덤으로 이 객체는 발생자(Generator)를 돌려주게 됩니다.)그럼 이제 이 코드들을 실행하려면 어떻게 해야할까요? 그러려면 먼저 WSGI 규격에 맞게 어플리케이션을 실행시켜 줄 서버를 작성해야합니다. 하지만 다행히도 Python 2.5부터 제공되는 wsgiref.simple_server를 이용하면 간단히 테스트 해 볼 수 있습니다.(서버를 직접 작성하는 부분에 대해선 나중에 다루도록 하겠습니다.)from wsgiref.simple_server import make_serverhttpd = make_server('', 8000, app)httpd.serve_forever()view rawgistfile1.py hosted with ❤ by GitHub위 코드를 실행시킨 후에(당연히 app이나 App도 만들어져 있어야겠죠?) 웹 브라우져를 통해 localhost:8000으로 접속하면 작성한 어플리케이션의 동작을 확인할 수 있습니다.environ과 start_response테스트도 해봤으니 코드를 조금만 더 자세히 살펴봅시다. 함수 버젼의 app이나 클래스 버젼의 App모두 공통적으로 environ과 start\_response를 인자로 받아 요청을 처리하는 것을 확인할 수 있습니다. (당연한 이야기겠지만, 반드시 이름이 environ이나 start\_response일 필요는 없습니다만 편의상 이후 계속 environ과 start_response로 표기하겠습니다.)하나씩 살펴보자면, environ은 Python 딕셔너리(dictionary)로 HTTP 요청을 처리하는데 필요한 정보가 저장되어있습니다. HTTP 요청에 대한 정보는 물론, 운영체제(OS)나 WSGI 서버의 설정 등도 정의되어있지요. 다음 코드는 이러한 environ의 내용을 응답으로 주게끔 수정한 WSGI어플리케이션입니다.def dump_environ_app(environ, start_response):    response_body = "\n".join(["{0}: {1}".format(k, environ[k]) for k in environ.keys()])    status = '200 OK'    response_headers = [('Content-Type', 'text/plain'),                         ('Content-Length', str(len(response_body)))]    start_response(status, response_headers)        return [response_body]view rawgistfile1.py hosted with ❤ by GitHublocalhost:8000에 각종 쿼리 스트링(Query String)을 붙이거나, 브라우져를 바꿔가면서 확인해보면 출력되는 값이 바뀌는 것을 확인하실 수 있습니다.그럼 이제 start\_response를 한번 볼까요. start\_response는 일종의 콜백(Callback)으로 인터페이스는 다음과 같습니다. start_response(status, response_headers, exc_info=None) 실제 서버에서 어플리케이션으로부터 응답(Response)의 상태(Status)와 헤더(Header), 그리고 예외(Exception)의 유무를 확인받아 실행하게 되는데, status와 response_headers는 HTTP 응답 명세에 근거하여 작성하게 됩니다.Middleware지금까지 우리는 어떻게 WSGI 어플리케이션을 작성하는지에 대해 살펴봤습니다. 요청을 받아 처리하는 HTTP의 기본 기능에 충실한 어플리케이션이었죠. 그런데 일반적으로 우리가 작성하는 웹 어플리케이션에서 주로 다루게 되는 쿠키(Cookie), 세션(Session)에 대해서는 어떻게 처리해야 좋을까요? WSGI 명세에는 이러한 내용을 직접적으로 다루고 있지 않습니다. WSGI 자체는 서버나 프레임워크 자체가 아니라 서버가 어플리케이션과 통신하는 명세를 다루고 있기 때문이죠. 따라서 이러한 기능은 작성자가 직접 이를 구현해야 합니다. 그런데 이런 구현을 어플리케이션을 작성할때마다 하는건 너무 번거로운 일입니다. 그것보다는 이미 작성한 어플리케이션을 확장하는 것이 간단하겠지요. 이러한 확장을 위해 필요한 것이 WSGI 미들웨어(Middleware)입니다.미들웨어는 어플리케이션을 처리하기 전후의 처리나 environ의 추가등을 통해 작성된 어플리케이션을 확장할 수 있습니다. 다음은 쿼리 스트링의 \_\_method\_\_에 따라 HTTP 메소드(Method)를 임의로 변경하는 처리를 도와주는 간단한 미들웨어입니다.from werkzeug import url_decodeclass MethodRewriteMiddleware(object):    """        app = MethodRewriteMiddleware(app)    """    def __init__(self, app, input_name = '__method__'):        self.app = app        self.input_name = input_name    def __call__(self, environ, start_response):        if self.input_name in environ.get('QUERY_STRING', ''):            args = url_decode(environ['QUERY_STRING'])            method = args.get(self.input_name)            if method:                method = method.encode('ascii', 'replace')                environ['REQUEST_METHOD'] = method        return self.app(environ, start_response)view rawgistfile1.py hosted with ❤ by GitHubMethodRewriteMiddleware는 \_\_call\_\_를 통해 app을 대체하게 됩니다. (데코레이터(Decorator)가 생각나셨다면 정확한 이해십니다.)Flask미들웨어를 통해 어플리케이션을 확장하는 방법까지 알아봤습니다. 그러나 이것만 가지고 웹 어플리케이션을 만들기에는 아직 귀찮은 부분이 많이 남아있습니다. 각종 파라미터를 처리하기 위해서는 environ를 일일히 뒤져야하며, 요청에 대한 응답으로 전달할 HTML도 일일히 문자열로 적어야하죠. 이런 여러가지 불편함을 해결하기 위해 알아볼 것이 WSGI 마이크로프레임워크를 자처하는 Flask입니다. Flask는 WSGI 라이브러리인 Werkzeug를 만들기도 한 Armin Ronacher가 만든 프레임워크로 “마이크로”라는 수식어에 어울리게 아주 핵심적인 부분만을 구현하고 있지만, 유연하게 확장이 가능하게 설계된 것이 특징입니다.다시 한번 Hello World!우선 Flask를 시스템에 설치해야하는데, pip가 설치되어있다면 pip install flask로 설치 가능합니다.(환경에 따라 루트(root)권한이 필요할 수도 있습니다.) easy_install의 경우도 마찬가지로 easy\_install flask로 설치 가능합니다.설치가 완료되었으면 다음과 같이 아주 간단한 어플리케이션을 작성해봅시다.from flask import Flaskapp = Flask(__name__)@app.route("/")def hello():    return "Hello World!"if __name__ == "__main__":    app.run()view rawgistfile1.py hosted with ❤ by GitHubFlask(정확히는 Werkzeug)는 테스트를 위해 간단한 WSGI 서버를 자체 내장하고 있기 때문에 app.run을 통해 어플리케이션을 직접 실행할 수 있습니다.Route이번에 작성한 Flask 어플리케이션에는 이전까지 보지 못하던 개념이 들어 있습니다. app.route가 바로 그것인데요. 이 메서드는 URL 규칙을 받아 해당하는 규칙의 URL로 요청이 들어온 경우 등록한 함수를 실행하게끔 설정합니다. 위의 Hello World! 예제 같은 경우엔 “/”가 해당되겠지요. 또한 이런 규칙을 URL로 부터 변수도 넘겨 받을 수 있습니다.# http://flask.pocoo.org/docs/api/#[email protected]('/')def index():    [email protected]('/')def show_user(username):    [email protected]('/post/')def show_post(post_id):    passview rawgistfile1.py hosted with ❤ by GitHub이렇게 URL을 통해 처리할 핸들러를 찾는 것을 일반적으로 URL 라우팅(Routing)이라고 합니다. 이런 URL 라우팅에서 중요한 기능 중 하나가 핸들러에서 해당하는 URL을 생성하는 기능인데, Flask는 이를 url_for 메서드를 통해 지원합니다[email protected]('/')def index():    return ""@app.route('/')def show_user(username):    return [email protected]('/post/')def show_post(post_id):    return str(post_id)from flask import [email protected]("/routes")def routes():    return "".join([            url_for("index"),            url_for("show_user", username="longfin"),            url_for("show_post", post_id=3)            ])view rawgistfile1.py hosted with ❤ by GitHub보다 자세한 사항은 API 문서를 참고하실 수 있습니다.Template여태까지 우리는 요청에 대한 응답으로 단순한 문자열을 사용했습니다. 하지만 일반적인 웹 어플리케이션의 응답은 대부분이 그것보다 훨씬 복잡하지요. 이를 보다 쉽게 작성할 수 있게끔 도와주는 것이 바로 Flask의 템플릿(Template)입니다. Flask는 기본 템플릿 엔진으로 (역시 Armin Ronacher가 작성한)Jinja2를 사용합니다.기본적으로 템플릿엔진은 별도의 규칙(여기서는 Jinja2)에 맞게 작성된 템플릿 파일을 읽어 환경(Context)에 맞게 적용한 결과물을 돌려주는데 이 과정을 Flask에서는 render_template()가 담당하고 있습니다. 다음 코드는 hello.html이라는 템플릿 파일을 읽어서 이름을 적용한 뒤에 돌려주는 코드입니다.# from http://flask.pocoo.org/docs/quickstart/#rendering-templatesfrom flask import [email protected]('/hello/')@app.route('/hello/')def hello(name=None):    return render_template('hello.html', name=name)view rawgistfile1.py hosted with ❤ by GitHub쉽게 작성할 수 있게 도와준다고 해도, 템플릿 역시 나름의 학습을 필요로 합니다. 자세한 사항은 Jinja2의 API 문서를 참고하시기 바랍니다.RequestHTTP 요청을 다루기 위해서 때로는 environ의 내용은 너무 원시적일때가 있습니다. HTML 폼(Form)으로부터 입력받는 값이 좋은 예인데요. Flask에서는 request라는 객체(역시 Werkzeug에서 가져다가쓰는 거지만요)를 통해 이를 보다 다루기 쉽게 해줍니다. 다음은 HTML 폼으로부터 입력받은 message라는 값을 뒤집어서 출력하는 코드입니다.from flask import [email protected]("/reverse")def reverse():    message = request.values["message"]    return "".join(reversed(message))view rawgistfile1.py hosted with ❤ by GitHubSession로그인등으로 대표되는 요청간의 상태를 유지해야하는 처리에 흔히 세션(Session)을 사용하실 겁니다. Flask에서는 session객체를 지원합니다.# from http://flask.pocoo.org/docs/quickstart/#sessionsfrom flask import Flask, session, redirect, url_for, escape, requestapp = Flask(__name__)@app.route('/')def index():    if 'username' in session:        return 'Logged in as %s' % escape(session['username'])    return 'You are not logged in'@app.route('/login', methods=['GET', 'POST'])def login():    if request.method == 'POST':        session['username'] = request.form['username']        return redirect(url_for('index'))    return '''        <form action="" method="post">           <input type=text name=username>           <input type=submit value=Login>        </form>    '''@app.route('/logout')def logout():    # remove the username from the session if its there    session.pop('username', None)    return redirect(url_for('index'))# set the secret key.  keep this really secret:app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'view rawgistfile1.py hosted with ❤ by GitHubFlask는 기본적으로 시큐어 쿠키(Secure Cookie)를 통해 세션을 구현하므로 길이에 제한이 있습니다. 때문에 파일이나 DB기반의 세션을 구현하려면 Beaker와 같은 프레임워크를 통한 확장이 필요합니다.(하지만 이 또한 매우 쉽습니다.)#스포카 #개발 #개발자 #개발팀 #인사이트 #기술스택 #꿀팁 #Flask
조회수 2496

MySQL의 Transaction Isolation Level (Lock에 관하여)

편집자 주문맥에 따라 ‘Transaction’과 ‘트랜잭션’으로 영어와 한글을 혼용함.문맥에 따라 ‘LOCK’과 ‘lock’으로 대문자와 소문자를 혼용함.OverviewMySQL DB는 일반적인 운영환경에서 뛰어난 성능을 제공합니다. 특히 적은 양의 자료가 빈번하게 교류되는 환경에서는 더욱 빛을 발하죠. 국내에서는 주로 작은 규모의 웹사이트를 구축할 때 MySQL을 사용합니다. 그런데 문제는 사이트의 규모가 커지면서부터 생긴다는 것이죠. 조금씩 느려지는 Query가 생기면 원인도 파악하고, Query를 튜닝하고, 설계도 변경하지만 MySQL의 특징적인 문제를 곧 만나게 됩니다.테이블을 복제(CREATE SELECT)하거나 다른 테이블로 옮기면(INSERT SELECT) 작업을 하는 동안 SELECT 절에 있는 테이블들이 Lock이 걸립니다. 게다가 다른 Session에서 해당 테이블을 수정(UPDATE / DELETE)하면 복제와 이동을 마칠 때까지 대기 상태로 있어야 한다는 것입니다. 이러한 문제는 시스템을 구축하고 자료가 일정량 쌓이기 전까지는 알 수 없습니다. 또한 Oracle과 같은 DB를 사용하던 사용자가, MySQL을 사용하면 이와 같은 문제가 있을 것이라고 생각하기도 어렵습니다.이러한 특징을 가진 MySQL의 Transaction Isolation Level을 알아보고자 합니다. Transaction Isolation Level 은 Transaction의 경리 수준을 말합니다. 트랜잭션 처리 시 다른 트랜잭션에서 접근해 자료를 수정하거나 볼 수 있도록 하는 수준입니다.Transaction Isolation Level의 종류와 특성Transaction Isolation Level에는 READ UNCOMMITTED, READ COMMIITED, REPEATABLE READ, SERIALIZE 네 가지 종류가 있습니다. 1)READ UNCOMMITTED1) COMMIT 되지 않은 데이터에 다른 트랜잭션에서 접근할수 있다.2) INSERT, UPDATE, DELETE 후 COMMIT 이나 ROLLBACK에 상관없이 현재의 데이터를 읽어온다.3) ROLLBACK이 될 데이터도 읽어올 수 있으므로 주의가 필요하다.4) LOCK이 발생하지 않는다.READ COMMIITED1) COMMIT 된 데이터에 다른 트랜잭션에서 접근할 수 있다.2) 구현 방식이 차이 때문에 Query를 수행한 시점의 데이터와 정확하게 일치하지 않을 수 있다.3) LOCK이 발생하지 않는다.4) MySQL에서 많은 양의 데이터를 복제하거나 이동할 때 이 LEVEL을 추천한다.REPEATABLE READ1) Default LEVEL이다.2) SELECT시 현재 시점의 스냅샷을 만들고 스냅샷을 조회한다.3) 동일 트랜잭션 내에서 일관성을 보장한다.4) record lock과 gap lock이 발생한다.5) CREATE SELECT, INSERT SELECT시 lock이 발생한다.SERIALIZE1) 가장 강력한 LEVEL이다.2) SELECT 문에 사용하는 모든 테이블에 shared lock이 발생한다.LOCK과 테이블, 어떻게 해결할 수 있을까?지금부터는 관련된 내용을 확인해보겠습니다. 우선 현재의 경리 수준부터 알아보겠습니다.mysql> SHOW VARIABLES WHERE VARIABLE_NAME='tx_isolation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | REPEATABLE-READ | +---------------+-----------------+ 1 row in set (0.00 sec) 다음으로 TEST 테이블을 만듭니다. 이때 SELECT절의 테이블을 UPDATE할 경우, 대기 상태로 빠지는 것을 확인해보겠습니다. 테이블을 만들고 상태를 확인합니다.CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 ; -- 생성시 INFORMATION_SCHEMA.PROCESSLIST 로 상태를 확인합니다. mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 5 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 1 row in set (0.00 sec) 다음으로 테이블 생성 시 UPDATE를 해 대기 상태로 빠지는지 확인해보겠습니다.UPDATE test.TB_PROD_BAS SET PROD_MEMO = 'TEST' WHERE PROD_ID = 1 ; mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 24 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 *************************** 2. row *************************** ID: 11006 USER: hansj HOST: 192.168.1.150:50719 DB: test COMMAND: Query TIME: 22 *****이부분 중요합니다.****** STATE: updating *****이부분 중요합니다.****** INFO: UPDATE test.TB_PROD_BAS SET PROD_MEMO = 'TEST' WHERE PROD_ID = 1 2 rows in set (0.00 sec) 위의 TIME을 보면 테이블이 생성될 때까지 대기하고, UPDATE 문의 상태가 updating 으로 표시됩니다. 하지만 이렇게 나올 경우 건수가 많으면 실제 UPDATE 중인지 대기상태인지 확인하기가 어렵습니다. LOCK이 걸린 테이블을 확인하려면 INNODB LOCK 테이블로 정확하게 알 수 있습니다. 아래 세 가지 테이블로 확인해보겠습니다. 보다 자세한 설명은 MySQL 홈페이지를 확인합니다.information_schema.INNODB_TRXLOCK을 걸고 있는 프로세스 정보information_schema.INNODB_LOCK_WAITS현재 LOCK이 걸려 대기중인 정보information_schema.INNODB_LOCKSLOCK을 건 정보위의 각 항목마다 테이블 생성 및 UPDATE 시 정보가 어떻게 나타나는지 확인해보겠습니다.1.information_schema.INNODB_TRXmysql> SELECT -> T101.TRX_ID -> ,T101.TRX_STATE -> ,T101.TRX_STARTED -> ,T101.TRX_REQUESTED_LOCK_ID -> ,T101.TRX_WAIT_STARTED -> ,T101.TRX_WEIGHT -> ,T101.TRX_MYSQL_THREAD_ID -> ,T101.TRX_ISOLATION_LEVEL -> ,SUBSTR(T101.TRX_QUERY,1,10)AS TRX_QUERY -> FROM information_schema.INNODB_TRX T101 -> ; +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ | TRX_ID | TRX_STATE | TRX_STARTED | TRX_REQUESTED_LOCK_ID | TRX_WAIT_STARTED | TRX_WEIGHT | TRX_MYSQL_THREAD_ID | TRX_ISOLATION_LEVEL | TRX_QUERY | +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ | 8771591 | LOCK WAIT | 2019-05-27 16:15:53 | 8771591:70031:4:306 | 2019-05-27 16:15:53 | 2 | 11006 | REPEATABLE READ | UPDATE tes | | 8771586 | RUNNING | 2019-05-27 16:15:51 | NULL | NULL | 1538969 | 11004 | REPEATABLE READ | CREATE TAB | +---------+-----------+---------------------+-----------------------+---------------------+------------+---------------------+---------------------+------------+ 2 rows in set (0.00 sec) TRX_ID_STATE트랜잭션의 상태를 나타냅니다. 실행 중인지 LOCK WAIT 상태인지 알 수 있습니다.TRX_MYSQL_THREAD_IDPROCESSLIST 의 ID를 나타냅니다.TRX_ISOLATION_LEVELISOLATION LEVEL을 나타냅니다.따라서 위의 내용을 보면 CREATE TABLE이 실행 중인 것과, UPDATE가 LOCK WAIT인 것, 그리고 관련된 PROCESSLIST의 ID까지도 알 수 있습니다2.information_schema.INNODB_LOCK_WAITSmysql> SELECT -> * -> FROM information_schema.INNODB_LOCK_WAITS T101 -> ; +-------------------+---------------------+-----------------+---------------------+ | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id | +-------------------+---------------------+-----------------+---------------------+ | 8771591 | 8771591:70031:4:306 | 8771586 | 8771586:70031:4:306 | +-------------------+---------------------+-----------------+---------------------+ 1 row in set (0.01 sec) requesting_trx_idLOCK WAIT 인 TRX_IDblocking_trx_idLOCK 을 건 TRX_ID현재 LOCK이 걸린 TRX_ID와 LOCK을 걸어둔 TRX_ID를 알 수 있습니다.3.information_schema.INNODB_LOCKSmysql> SELECT -> * -> FROM information_schema.INNODB_LOCKS -> ; +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data | +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ | 8771591:70031:4:306 | 8771591 | X | RECORD | `test`.`TB_PROD_BAS` | PRIMARY | 70031 | 4 | 306 | 1 | | 8771586:70031:4:306 | 8771586 | S | RECORD | `test`.`TB_PROD_BAS` | PRIMARY | 70031 | 4 | 306 | 1 | +---------------------+-------------+-----------+-----------+----------------------+------------+------------+-----------+----------+-----------+ 2 rows in set (0.01 sec) lock_trx_idLOCK 과 관련된 TRX_IDlock_modeX 쓰기, S 읽기 2)어떤 테이블이 LOCK을 걸고 있는지 알 수 있습니다.위의 내용들을 통해 REPEATABLE READ에서 CREATE SELECT시 SELECT 테이블에 LOCK이 걸려 UPDATE가 대기하게 되는 것을 알 수 있습니다. 이번에는 Transaction Isolation Level 을 READ COMMIITED로 변경하고 CREATE SELECT 및 UPDATE를 진행해보겠습니다.SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; SHOW VARIABLES WHERE VARIABLE_NAME='tx_isolation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | READ-COMMITTED | +---------------+-----------------+ 1 row in set (0.00 sec) UPDATE 문은 다음과 같이 수행됩니다. mysql> UPDATE test.TB_PROD_BAS -> SET PROD_MEMO = 'TEST' -> WHERE PROD_ID = 1 -> ; Query OK, 0 rows affected (0.04 sec) Rows matched: 1 Changed: 0 Warnings: 0 기존에 대기했던 것과 다르게 0.04초가 걸렸습니다.mysql> SELECT -> * -> FROM INFORMATION_SCHEMA.PROCESSLIST -> WHERE USER = 'hansj' -> AND COMMAND <> 'Sleep' -> \G *************************** 1. row *************************** ID: 11004 USER: hansj HOST: 192.168.1.150:50711 DB: test COMMAND: Query TIME: 9 STATE: Sending data INFO: CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 1 row in set (0.00 sec) -- 프로세스 정보도 CREATE TABLE 만 진행중임을 알수 있습니다. mysql> SELECT -> T101.TRX_ID -> ,T101.TRX_STATE -> ,T101.TRX_STARTED -> ,T101.TRX_REQUESTED_LOCK_ID -> ,T101.TRX_WAIT_STARTED -> ,T101.TRX_WEIGHT -> ,T101.TRX_MYSQL_THREAD_ID -> ,T101.TRX_ISOLATION_LEVEL -> ,T101.TRX_QUERY -> FROM information_schema.INNODB_TRX T101 -> ; +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | TRX_ID | TRX_STATE | TRX_STARTED | TRX_REQUESTED_LOCK_ID | TRX_WAIT_STARTED | TRX_WEIGHT | TRX_MYSQL_THREAD_ID | TRX_ISOLATION_LEVEL | TRX_QUERY | +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ | 8771856 | RUNNING | 2019-05-27 17:17:45 | NULL | NULL | 4594347 | 11004 | READ COMMITTED | CREATE TABLE test.TB_PROD_BAS_TEST ( PRIMARY KEY (PROD_ID) ) SELECT T101.PROD_ID ,T101.PROD_NM ,T101.PROD_EN_NM ,T101.PROD_MEMO FROM test.TB_PROD_BAS T101 | +---------+-----------+---------------------+-----------------------+------------------+------------+---------------------+---------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) READ COMMITTED LEVEL로 CREATE만 수행 중인 것을 알 수 있습니다.mysql> SELECT -> * -> FROM information_schema.INNODB_LOCK_WAITS T101 -> ; Empty set (0.00 sec) mysql> SELECT -> * -> FROM information_schema.INNODB_LOCKS -> ; Empty set (0.00 sec) LOCK을 걸고 걸린 것이 없어 내용도 없습니다.Conclusion지금까지 Transaction Isolation Level 을 기준으로 CREATE SELECT 시 SELECT 에 사용되는 테이블도 LOCK이 걸릴 수 있는 것을 확인했고, 그에 따른 해결 방법까지 알아봤습니다.INSERT INTO SELECT에서도 같은 현상이 나타납니다. 그렇기 때문에 운영 중인 테이블을 복제(CREATE SELECT)하거나 다른 테이블로 옮길 경우(INSERT SELECT) Transaction Isolation Level을 READ COMMITTED 변경하고 작업하기를 권장합니다.그렇지 않으면 관련된 TABLE은 LOCK이 걸리고, 관련 Query들이 대기 상태로 빠지면서 시스템 장애가 발생할지도 모릅니다.참고1)MySQL :: MySQL 5.6 Reference Manual :: 14.7.2.1 Transaction Isolation Levels2)MySQL :: MySQL 5.6 Reference Manual :: 14.7.1 InnoDB Locking글한석종 부장 | R&D 데이터팀[email protected]브랜디, 오직 예쁜 옷만
조회수 1796

Database를 왜 사용할까요?

개발자들이 Database 프로그램을 선택한 이유Database(이하 DB) 프로그램을 처음 접한 건 Dos에서 사용하는 Database III plus였습니다. 이때는 학생이었기 때문에 프로그램 개발에 관심이 많았지만 대량의 데이터를 다룰 일은 없었습니다. 다음으로 접한 건 clipper였습니다. 과거 C언어를 하던 사람이면 자료 처리를 위해 한 번쯤은 접해봤을 겁니다. 이때까지는 Dos를 주로 사용했고, 간단한 자료를 다루었기 때문에 File 처리만으로도 충분한 결과를 얻을 수 있었죠.그렇다면 DB는 다중 사용자 환경이 되고 바로 사용하게 되었을까요? 예전에 다중 사용자들이 사용했던 걸 꼽자면 PC 통신과 Web이 있을 것입니다. 초창기의 Web은 PHP, ASP가 개발되기 전이었고 Java는 C보다 성능이 낮아 CGI를 C로 구현했으니 게시판이나 자료실 등도 C로 개발했습니다.규모가 큰 PC 통신은 DB를 사용했지만 사설 BBS나 01410 등에 들어가는 외부 업체는 File로 처리했습니다. 이 시기에 사설 BBS나 01410 서비스를 제공하는 업체들은 Workstation을 구입하거나 x86 계열을 구입해 운영체제 (SCO UNIX, Free BSD, Linux 등)를 사용했지만 이때 역시 C로 개발을 했었습니다. 이런 환경에서 점점 File 처리의 한계가 나타나기 시작했던 것이죠.C File lock 예)int iFd, iResult; iFd = open(“LockTest”,O_RDWR);  iResult = lockf(iFd, F_LOCK,10L); /* 필요한 작업 처리 */ close(iFd); 유저가 늘어나고 운영 체제 내부적으로 동시에 처리하는 프로세스가 증가하면서 자료가 깨지는 현상이 나타납니다. 개발자들은 어쩔 수없이 DB를 선택하기 시작했습니다.DB의 장점들DB를 도입하면 여러 가지 장점이 생깁니다. SQL 문장만 익히면 프로그램으로 일일이 구현해야 했던 것들을 명령어만으로 수행할 수 있고 자료의 무결성 또한 보장해 주며, 개발의 생산성까지 높입니다. 만약 특정 날짜의 자료들을 읽어와서 제목 순으로 보여줘야 할 경우, 프로그램으로 개발한 자료를 날짜 별로 읽어 배열에 담고 Quick sort 알고리즘을 적용해 정렬한 후 자료를 보여줘야 합니다. 하지만 DB에서 SQL 문장을 사용하면 간단하게 완성할 수 있습니다. SELECT * FROM TABLE WHERE DATETIME = 날짜 ORDER BY TITLE ; 조심 또 조심!하지만 DB 역시 만능은 아니기 때문에 모든 자료를 처리할 수는 없습니다. 예를 들어 문서(pdf, doc, hwp등) , 이미지(jpg, gif 등), 압축(zip,rar 등) 등의 바이너리 파일입니다. (물론 DB에서 BLOB 자료형을 지원하므로 하드웨어 자원과 성능만 받쳐준다면 불가능한 것은 아닙니다.) 하드웨어 자원과 성능에는 한계가 있기 때문에 DB로 해야 할 일과 하지 말아야 할 일을 구분해야 합니다. 만약 이를 생각하지 않고 DB에 모든 자료를 넣는다면 어떤 문제가 생길까요? 크게 두 가지가 있습니다.첫 번째는 바이너리를 파일을 읽고 쓸 때 발생하는 시간이 문제가 될 수 있습니다. 그 이유는 DB가 Connection Pool로 접속을 관장하는데, 이는 한정된 자원으로 최소한의 시간을 사용해야 많은 유저가 사용할 수 있기 때문입니다. 만약 바이너리 파일을 DB에 올리면서 오랜 시간 접속을 유지한다면 그만큼 다른 유저가 사용할 수 없을 테고, 결국은 DB에서 감당할 수 있는 유저의 수가 줄어들 것입니다.두 번째는 백업의 문제가 있습니다. 우리는 DB에 장애가 발생할 때를 대비해 DB 전체 백업을 합니다. 그런데 DB에 바이너리 파일이 들어가면 백업 시간이 많이 늘어나 원하는 시간 안에 백업을 하지 못하는 일이 발생할 수도 있습니다. 따라서 DB에 바이너리 파일을 넣을 때는 아주 적은 용량의 파일만 넣어야 합니다. 배치에 대하여: OLTP, OLAPDB 용량이 커지면 Query를 수행해도 원하는 결과를 볼 수 없고 DB에 부담을 많이 주는 Query가 발생합니다. 그래서 주기적으로 Query를 돌려 결과를 테이블에 넣고 필요할 때마다 이를 볼 수 있게 배치 처리를 하며 해결합니다. 일, 월, 년 단위의 집계 자료를 구축하면서 시스템에 부하를 줄 수 있기 때문에 보통 야간에 처리를 하죠. 그런데 만약 DB 용량이 너무 커져서 전일자 집계를 배치로 처리하지 못하는 일이 발생하면 어떻게 할까요?여기서 사용할 수 있는 것이 OLAP(OnLine Analytical Processing) DB입니다. 일반적으로 유저가 사용하는 건 OLTP(OnLine Transaction Processing) 입니다. 대표적으로 Oracle, MySQL PostgreSQL 등이 있습니다. 여기서 MySQL 을 제외하고 Oracle과 PostgreSQL 은 Partition, HASH 조인, Parallel을 지원하여 OLAP 환경에서도 어느 정도 사용 가능합니다.OLAP DB는 주로 DW 환경에서 사용하며 대표적으로 Teradata와 Oracle Exadata 등이 있습니다. OLAP DB 와 비교가 안 될 정도를 빠르게 배치 작업을 처리할 수 있습니다. (자세한 내용은 다음 글에서 설명하겠습니다.)Conclusion지금까지의 이야기를 정리하면 ‘여러 유저가 동시에 안정적으로 자료 처리를 하려면 DB를 사용하고, 자료의 양과 처리 형태(OLAP, OLTP) 에 따라 DB를 선택하면 된다’는 것이었습니다. 자세한 설명을 하자면 각 DB별 특성을 기술해야 하기 때문에 오늘은 전체적인 내용부터 살펴봤습니다. 다음 글에서는 유저가 사용하는 OLTP에 대해 살펴보겠습니다. 글한석종 부장 | R&D 데이터팀[email protected]#브랜디 #개발문화 #개발팀 #업무환경 #인사이트 #경험공유
조회수 1054

맛있는 인터뷰: 잔디 그로스 팀 개발자, Hugo

 역삼 맛집▲ 금강산도 식후경이라 했던가? 맛있는 인터뷰가 맛있는 이유는 늘 음식과 함께 하기 때문이다오늘 온 맛집. 분위기가 심상치 않다. 어떤 곳인지 소개해달라. Hugo(이하 ‘휴’): 역삼역 근방에 있는 ‘산촌’이란 곳이다. 얼마 전 버디런치 장소 물색을 위해 ‘다이닝코드’로 역삼역 주변 한식집을 찾던 중 발견했다. 예로부터 어르신들이 찾는 곳은 맛집이라는 얘기가 있다. 보면 알겠지만 어르신들이 많다. 괜찮은 가격에 건강한 음식을 섭취할 수 있는 곳이라 많은 듯 하다. 그리고.. 우리도 건강을 챙겨야 하는 나이다. 몸에 좋은 곤드레밥, 메밀 전병을 먹으며 함께 하는 건강한 인터뷰가 되었으면 하는 바람에 이 곳을 선택했다.깔끔한 답변 고맙다. 이제 본인 소개를 부탁한다. 휴: 반갑다. 개발자 Hugo다. 잔디 그로스 팀(Growth Team)에서 로우 데이터(Raw data) 가공, 분석을 통해 유의미한 지표를 보여주는 데이터 분석 툴 ‘스프링클러’를 개발하고 있다. 잔디 멤버들 사이에서 유독 명성이 자자하다 휴: 여러분의 관심을 먹고 자라는 임무를 수행하다 보니 그런 듯 하다. Hannah, Jihoon, Jane 등과 함께 GWP 팀으로 활동해서 많이들 알아봐 주시고 격려해주시는 것 같다. * GWP 팀? GWP는 Great Working Place의 줄임 말이다. 단어 그대로 물리적+비물리적 최고의 업무 공간을 만들기 위해 TF팀 형태로 구성된 그룹이 다양한 활동을 한다. 예를 들면, 할로윈 파티 개최부터 탕비실 냉장고 음식 채우기 등 업무 환경 개선을 위해 크고 작은 일을 수행한다. ‘비선실세’라는 얘기도 돌던데? 휴: 천부당 만부당한 말씀이다. 그저 잔디를 사랑하는 멤버 중 한 명이다.스프링클러? 휴: 잔디 그로스 팀에서 자체적으로 만든 분석 툴이다. 쉽게 말하면 잔디 데이터 분석과 가공에 최적화된 잔디 전용 구글 어널리틱스(Google Analytics)로 보면 된다. 스프링클러를 통해 잔디 DAU(Daily Active User) 파악, 마케팅 채널 별 효율 측정, 유저 별 사용량 측정 등을 할 수 있다.  스프링쿨러▲ 잔디의 모든 데이터를 가공, 분석해 보여주는 스프링클러잠깐! 유저 별 사용량 측정도 스프링클러를 통해 가능하다고 했는데 잔디 팀이 유저의 모든 정보를 열람하는 건가? 휴: 많은 분들이 오해할 수 있을 것 같은데 그렇지 않다. 스프링클러에서 열람할 수 있는 유저 별 사용량 확인은 특정 채널을 통해 유입된 유저가 메시지를 몇 건 보내고, 파일 업로드를 얼마나 하는지 정도다. 유저가 어떤 메시지를 주고 받는지, 어떤 파일을 올리는지 등 개인 정보는 원칙적으로 잔디 팀이 접근할 수 없다.   스타트업은 시간과 리소스 관리가 생명이다. 구글 어널리틱스와 같은 훌륭한 툴이 있는데 굳이 자체 데이터 분석 툴을 만든 이유가 무엇인지? 휴: 날카로운 질문이다. 나도 처음에 왜 스타트업에서 데이터 관련 팀을 꾸려 분석 툴을 만들려고 하는지 이해가 가지 않았다. 하지만 좀 더 생각해보니 잔디에서 발생한 데이터에 특화된 분석 툴이 있어야 정확한 결과를 얻을 수 있고, 이를 통해 스타트업 특유의 린(Lean)한 개발이 가능할거란 결론에 도달하였다.   듣기엔 스프링클러의 사용성과 분석 능력이 뛰어나 독자 서비스로 나오는 것 아니냔 루머가 있었다. 사실인가? 휴: 하하. 루머일 뿐이다. 다만 그런 생각을 갖고 그로스 팀과 최선을 다해 스프링클러 개발을 하고 있다. 어쨌든 좋게 봐주셔서 이런 루머가 나온 것 같아 담당자로서 기쁘다.   스프링클러에 애정이 많을 것 같다 휴: 내게 잔디도 소중하지만 스프링클러도 무척 중요하다. 소박한 꿈이 있다면 스프링쿨러가 내가 없어도 100% 완벽히 잘 돌아가게 만들고 싶다. 물론, 분석 툴로서 멤버들이 원하는 결과를 보여줄 수 있도록 정교하게 만드는 것도 중요하다.   그로스 팀은 과거 ‘맛있는 인터뷰’의 Kevin을 통해 소개한 바 있다. 당시 개발자 중 몇 명을 차출해 그로스 팀에 합류시킨 걸로 알고 있는데 여러 개발자 중 Hugo가 차출된 이유가 있다면? 휴: 평소 데이터 마이닝 분야에 관심이 많아 대학원에서 관련 공부를 하기도 했고, 그로스 팀 초창기 모든 업무를 책임지던 팀장 겸 팀원 Kevin이 추천해 팀에 합류하게 되었다. 아, 그로스 팀 오기 전엔 백엔드(Back-end) 개발자 포지션으로 있었다. 팀을 옮길 땐 백엔드 개발자들로부터 ‘배신자’란 오명과 함께 모진 고문과 학대를 받았다. 하하.. 농담이다.   다른 얘기를 해보자. 잔디에 어떤 이유로 조인했는지 궁금하다 휴: 건방진 말일 수 있지만 내 의지대로 무언가 만들고 싶었다. 대한민국 수 많은 개발자들이 그렇겠지만 회사에서 내가 할 수 있는 건 생각보다 한정돼 있다. 아이디어를 내도 예산 때문에 혹은 기타 다른 이슈 때문에 반려되기 일쑤였다. 어떻게 보면 그런 현실에 대한 반발심으로 잔디를 선택한 게 가장 크다고 볼 수 있다.   잔디에서는 생활은 만족스러운가? 휴: 70% 정도?   왜 70%인가? 휴: 장-단점이 있지만 장점이 조금 더 크기 때문에?   그럼 장점부터 말해보자 휴: 합당한 이유가 있다면 내가 하고 싶은 일을 진행할 수 있다는 점이 큰 장점이다. 그 일을 실행하기까지 절차도 이전까지 다녔던 회사 대비 상당히 간소화되어 있어 부담감도 적다. 각 분야에서 두각을 드러낸 사람들과 함께 일하고 있다는 점도 매력적이다. 다들 자부심을 갖고 일하고 있어 자극을 받는다.   단점은? 휴: 장점이 때에 따라 단점으로 보일 때도 있다. 논리적인 어프로치가 필요할 때도 있지만 분명 전쟁터로 돌진하는 돌격병 같은 저돌성이 필요할 때도 있다. 그럴 땐 일부터 치고 보는 자세가 필요한데 그 때를 놓치는 경우가 눈에 보여 개인적으로 아쉽다.   스트레스는 어떻게 푸는지? 휴: 주말에 13시간 이상 잔다. 밤에 10시간, 낮에 3시간. 남는 시간엔 수영이나 등산을 한다.   등산? 휴: 집 바로 뒤에 나지막한 산이 있다. 평소 자연을 좋아하는데 등산을 하다 보면 산의 나무나 풀, 바람을 보고 즐길 수 있어 좋다.   생각보다 감성적인 남자라 당황스럽다 휴: ^^ ▲ 감성적인 남자로 보이는 그는 한 때 해병대 전우였다.수영은 시작한지 얼마 안됐다고 들었다 휴: 작년 10월부터 시작했다. 이제 갓 1년이 넘었다. 작년 초부터 체력적으로 처진 것 같은 느낌이 들어 이대론 안되겠다 싶어 수영을 시작했다. 등산과 함께 꾸준히 하는 운동 중 하나다.   꾸준히 운동하고 있는데 달라진 점이 있다면? 휴: 몸도 몸이지만 정신적으로 건강해졌다. 확실히 체력이 떨어지면 부정적인 생각을 하는 경향이 있는 것 같다. 운동을 시작하기 전과 후의 마음 상태가 정말 많이 다르다. 앞으로도 꾸준히 운동을 할 생각이다.   곤드레밥과 함께 한지 벌써 1시간 가까이 됐다. 인터뷰 질문도 다 소진되어 이전 맛있는 인터뷰 주인공이었던 David이 남긴 질문을 묻고 싶다 휴: 준비됐다.   잔디 멤버 중 전생에 공주나 왕자였을 것 같은 사람은? 휴: 왕자는 디자인 팀의 Ben. 도도하고 말수도 적고. 공주는 디자인 팀의 Yujin (A.K.A Summer)? 얘기는 많이 안 해봤지만 말도 고급스럽게 하는 것 같다. 두 사람 모두 괜찮은 사람들이라 이번 인터뷰를 통해 점수를 따보고 싶다.   전략적인 답변 감사하다 휴: ^^   마지막 질문이다. ‘맛있는 인터뷰’의 백미는 다음 인터뷰이에게 현재 인터뷰이가 질문을 남기는 것이다. 다음 사람에게 묻고 싶은 질문이 있다면? 휴: 잔디 멤버 중 내 주변 괜찮은 남자 사람이나 여자 사람을 소개시켜주고 싶은 사람은?   오늘 맛있는 곤드레밥 덕분에 잘 먹었다. 계산은 인터뷰이가 한다는 거 다시 한번 더 상기시켜 드리며 인터뷰 마무리하겠다 휴: …^^#토스랩 #잔디 #JANDI #개발 #개발자 #개발팀 #인터뷰 #팀원 #팀원소개 #팀원인터뷰 #기업문화 #조직문화
조회수 1869

프론트엔드 개발자라면!

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

Node.js - Event

Event(이후 '이벤트'로 통칭)Node.js(이후 '노드'로 통칭)는 이벤트 기반 비동기 방식으로 작동한다. 그러므로 노드를 잘 다루기 위해서는 이벤트에 대해 이해하여야 한다.노드에서 이벤트를 호출하고 여러 처리를 하기 위해서는 EventEmitter 객체를 상속받아 구현해야 한다.아래 예제 코드를 통해 EventEditter를 상속받은 객체를 가지고 이벤트를 생성하고 호출하는 등 여러 처리하는 법을 살펴보자.* 코드 복사붙여넣기가 필요한 경우 http://madeitwantit.tistory.com/32 에서 가능하다.EventEmitterEventEmitter 클래스는 events 모듈에 의해 정의되고 제공된다.EventEmitter = require('events');위와 같이 EventEmitter를 정의할 수 있다.EventEmitter의 메서드EventEmiter.on('이벤트 이름', '리스너 함수') - 지정한 '이벤트 이름' 이벤트에 '리스너 함수'를 리스너 배열 가장 끝에 추가한다. EventEmiter.once('이벤트 이름', '리스너 함수') - on() 메서드와 기능이 비슷하다. 다만 이 메서드로 등록된 리스너는 일회성으로 한 번 실행된 후 제거된다. EventEmiter.addListener('이벤트 이름', '리스너 함수') - on() 메서드와 같다.EventEmiter.emit('이벤트 이름'[, arg]...) - '이벤트 이름'  이벤트에 등록된 리스너 함수를 등록된 순서에 따라 호출한다. 이벤트가 존재한다면 true, 그 외에는 false를 반환한다.EventEmiter.setMaxListeners(n) - EventEmitter는 디폴트로 최대 리스너 수가 10으로 지정되어 있다. 10보다 더 많은 리스너를 등록할 때 사용한다. Infinity나 0을 지정하면 제한 없이 리스너를 등록할 수 있다.EventEmiter.getMaxListeners() - 현재 EventEmitter에 지정된 최대 리스너 수를 반환한다.EventEmiter.listenerCount('이벤트 이름') - '이벤트 이름'에 등록되어 있는 리스너의 수를 반환한다.EventEmiter.listeners('이벤트 이름') - '이벤트 이름'에 등록되어 있는 리스너 배열의 사본을 반환한다.EventEmiter.removeAllListeners(['이벤트 이름']) - 모든 리스너나 파라미터에 지정한 '이벤트 이름'의 리스너를 제거한다.EventEmiter.removeListeners('이벤트 이름', '리스너 함수') - '이벤트 이름'에 등록되어 있는 특정 '리스너 함수'를 제거한다. 같은 리스너가 여러 개 등록되어 있으면 이 메서드를 여러 번 호출해야 한다.EventEmitter의 이벤트'newListener' - 새로운 이벤트를 등록할 때, 추가될 리스너를 리스너 배열에 추가하기 전에 호출된다. 이벤트에 리스너가 전달되기 위해 이벤트 이름과 추가될 리스너가 전달된다.'removeListener' - 리스너가 제거된 후 호출된다.하단의 예제를 통해 newListener가 호출되는 시점에 대해 살펴보자.                                                              * 코드 복사붙여넣기가 필요한 경우 http://madeitwantit.tistory.com/32 에서 가능하다.참고문헌:모던 웹을 위한 Node.js 프로그래밍 - 윤인성Haruair (http://haruair.com/blog/3396)Node.js Documentation (https://nodejs.org/api)조대협의 블로그 (http://bcho.tistory.com/885)#트레바리 #개발자 #안드로이드 #앱개발 #Node.js #백엔드 #인사이트 #경험공유

기업문화 엿볼 때, 더팀스

로그인

/