기사 평가 알고리즘 개발에 도커(Docker)를 써보았습니다


슬로워크 디지털 사업부는 한국언론진흥재단과 함께 ‘뉴스트러스트’ 프로젝트를 진행 중입니다. 최대한 간단히 요약하자면 ‘자연어 처리 및 딥러닝 기술을 활용한 뉴스 기사 평가 알고리즘 개발 프로젝트’라고 할 수 있습니다. 인터넷 뉴스 기사를 모으고 선별해서 제공하는 기준을 정하는 프로젝트입니다. 쉽게 답이 나오는 문제는 아니지만 적어도 포털 사이트보다는 검증된 기관에서 대안을 모색해보자는 취지로 시작됐습니다. 더 자세한 배경과 의미는 한국언론진흥재단의 선임연구위원 오세욱님의 기사 '
언론진흥재단, 뉴스트러스트 알고리즘 개발과 공개 의미 : 정보 편향 막아줄 알고리즘, 소스 공개로 투명성도 확보'를 통해 확인하세요.


이 글에서는 배경 설명보다는 ‘아이디어를 구현하기 위해 어떤 기술을 활용할 수 있는가’에 초점을 맞춘 이야기를 해보려고 합니다. 즉 뉴스트러스트라는 아이디어를 실제로 구현하기 위해 도커 기술을 사용한 이유와 프로젝트 구성 예시 일부를 보여드리겠습니다.


도커는 피할 수 없는 선택?!


뉴스트러스트가 추구하는 가치는 공공성입니다. 모두가 더 좋은 뉴스를 소비하도록 돕는 프로젝트이며, 누구든 그 과정에 참여하고 기여할 수 있는 기반을 만들려고 합니다. 그래서 거의 모든 소스코드를 공개하고, 누구든 쉽게 받아서 실행할 수 있도록 최대한 간편한 구성으로 프로젝트를 꾸리고 있습니다.


이런 상황에서 도커를 사용하는 것은 거의 피할 수 없는 선택이었습니다. 아무리 소스가 공개돼 있어도 그걸 받아서 실행하기 위해 운영체제(OS)나 실행환경, 버전, 환경변수 등을 맞추는 과정은 모든 개발자에게 고난이기 때문입니다. 그런데 도커는 다양한 오픈소스 프로그램을 배포하고 관리할 때 쓸 수 있는 단순한 인터페이스를 제공하는 플랫폼으로써, 기존의 OS 가상화보다 가벼운 프로세스 격리 방식(컨테이너)으로 구동됩니다.


즉 ‘모두가 더 좋은 뉴스를 소비하도록 돕는’ 프로젝트의 공공성을 기술적으로 잘 뒷받침해줄 수 있다는 점에서, 또 개발편의성이 높은 가상화 기술이라는 점에서 도커가 유용했습니다.


프로젝트 설명글(README)에는 도커 실행 가이드가 적혀있습니다. 프로젝트를 내려받은 다음 도커 명령어 몇줄만 실행하면 뉴스트러스트의 전체 시스템을 각자의 PC에 배포할 수 있습니다. 다만 깃(git)과 도커는 설치해야 합니다. 아래 스크린샷은 제 PC에 뉴스트러스트를 배포하고 형태소분석 API를 테스트한 결과입니다.



도커 기반 프로젝트 구성 예시


앞서 명령어 몇 줄만 실행하면 뉴스트러스트를 배포할 수 있다고 했는데요, 그렇게 간단한 방법으로 프로그램을 배포할 수 있다는 건 결국 복잡한 일을 누군가 대신 해주고 있다는 의미입니다. 이번에는 도커로부터 도움을 받았습니다. 이제 도커를 어떤 식으로 활용했는지 간단히 살펴보도록 하겠습니다.


우선 뉴스트러스트를 배포하는 과정을 풀어서 설명해보면 이렇습니다.


1. 리눅스 환경을 기본으로 구성한다.

2. 웹 애플리케이션을 구동시키는데, 언어는 GO를 쓴다.

3. 웹 애플리케이션의 세션을 저장하기 위해 Redis를 연동한다.

4. 데이터 처리를 위해 Python을 쓰고, 자연어처리를 위한 관련 라이브러리도 설치한다.

5. MongoDB를 설치하고 위의 GO/Python 모듈과 연동한다.

6. RabbitMQ를 설치하고 위의 GO/Python 모듈간 메시지 큐잉을 관리한다.


현재 뉴스트러스트 프로젝트는 위 단위들을 하나로 묶어서 공개하고 한번에 배포할 수 있도록 개발 중입니다. 여섯 단계 과정은 모두 일종의 도커 명세서로 정리했기 때문에 개발자는 이 프로젝트를 받아서 다음 명령어로 실행할 수 있습니다.


docker-compose up -d --scale worker=5


아래 스크린샷은 현재 개발 중인 프로젝트의 docker-compose 명세 파일 중 일부를 옮긴 것입니다. 각 모듈이 어떻게 연결되고 실행되는지만 간단히 살펴보겠습니다.


(docker-compose.yml)


명세 파일을 살펴보면 총 5개의 서비스 - web, mongo, redis, mq, worker를 운영하고 있습니다. 모두 각각의 이미지에 의해 컨테이너로 구현되며, 배치 작업할 것을 고려해서 여러 개의 worker 컨테이너만 동시에 실행시키기도 합니다. mq는 그런 병렬작업을 염두에 둔 모듈입니다.


다시 docker-compose 명세로 돌아가서, 다섯 개의 서비스가 공통으로 가지는 속성부터 차례대로 살펴보겠습니다.


1. image, build


컨테이너를 구동시키기 위한 image에 언어 혹은 프로그램 이름과 버전이 명시돼 있는데, 이는 도커 측에서 관리하는 DockerHub라는 공개저장소에서 가져옵니다. 그러나 worker에서 명시하는 nt-worker라는 이미지는 DockerHub에 없습니다. 이는 뉴스트러스트 프로젝트가 자체적으로 구성한 이미지를 따로 사용했기 때문입니다. 따라서 25번째 라인에 있는 build를 함께 봐야 합니다. worker는 따로 작성한 Dockerfile을 빌드해서 사용합니다(Python 모듈).


2. working_dir, volumes


working_dir은 루트 경로 대신 직접 지정하는 워킹 디렉토리고, volumes는 도커 컨테이너 안과 밖이 서로 공유하는 디렉토리입니다(여기서 컨테이너 밖이란 제 PC환경 그 자체를 의미합니다). web의 경우엔 수정한 소스가 컨테이너에도 바로 반영돼 디버깅에 도움이 되고, mongoDB의 경우 DB 관련 파일들을 외부에도 저장하는 일종의 백업 용도로 썼습니다.

3. command


command는 web과 worker에만 명시돼 있습니다. DB나 큐는 설정에 맞게 구동만 하면 되지만, 애플리케이션 성격의 web과 worker는 컨테이너가 구동된 직후에 실행돼야 할만한 세밀한 작업이 있습니다. 그래서 셸 파일을 별도로 작성했고 도커에게 이 파일을 실행하라고 알려주는 부분입니다.


4. ports


아홉 번째 라인에 명시된 “60000:8091”은 외부의 60000번 포트로 접속하면 도커 환경의 8091에 연결한다는 뜻입니다. 첫번째 스크린샷의 주소창을 보면 “localhost:60000”으로 접속한 걸 알수 있습니다. 제 PC의 60000번 포트가 도커 내 8091번 포트로 포워딩된 것입니다. 보안을 위해 접속 경로의 외부 노출은 최소화하는게 좋습니다. 예시에선 불가피하게 웹서비스에 대한 노출만 한 상황이고, 개발하는 과정에선 편의상 다른 서비스들도 열어놓곤 합니다.


이상 자연어 처리 및 딥러닝 기술을 활용해 뉴스 기사 평가 알고리즘을 개발하는 ‘뉴스트러스트’ 프로젝트에 도커를 사용한 이유와 구성 예시 한 개를 함께 살펴봤습니다. 비개발자에게는 외계어로 들릴 수도 있고, 어떤 개발자에게는 허점 많은 프로젝트로 보일지도 모르겠습니다. 하지만 공공 프로젝트에 걸맞는 접근성과 유용성을 갖출 수 있도록 앞으로도 계속 개선해 나가려고 합니다. 미처 다루지 못한 자연어 처리나 기계학습 관련된 이야기도 기회가 닿으면 이어서 소개하겠습니다.  


(디지털 사업부는 지금 UI/UX 기획자 채용중!)


글, 이미지 | 슬로워크 디지털 사업부 개발자 벤

편집 | 슬로워크 오렌지랩 테크니컬 라이터 메이



Posted by slowalk



2005년 Tim O'ReillyWeb 2.0을 표방한 이래 12년이 지났습니다. 우리는 얼리어답터가 아니더라도 한번쯤 개방과 참여, 공유라는 단어를 접해보았고, 구글독스가 오피스 프로그램을 대체하는 현재를 살아가고 있습니다. 플랫폼으로서의 웹사이트, 불특정 다수의 집단지성 이용, 프로그램 릴리스 사이클의 종언, 경량화 프로그래밍 모델, 디바이스의 경계를 넘는 소프트웨어, 폭넓은 사용자경험(UX)의 중요성 등 당시에 제안된 Web 2.0의 원칙을 그리 어렵지 않게 느낄 수 있게 된 것입니다.


이러한 흐름에 발맞추어 최근 민간영역 뿐만 아니라 공공영역에서도 발전된 형태의 웹사이트를 구축하고 운용하는 방법을 고민하고 있습니다. 이러한 시도 중에서도 아카이브 형식을 차용해 웹사이트를 구축하는 사례가 늘고 있습니다. 단순히 기관 또는 사업의 소개를 담거나 정보 저장 및 나열 형식으로 운영되던 이른바 ‘홈페이지'에서 벗어나, 제공되는 정보를 손쉽게 찾을 수 있도록 세밀한 검색기능을 제공하고 수집된 데이터의 큐레이션과 2차 이용 및 가공이 가능한 방법을 제공하는 등 차별화된 이용 방식으로 활용성을 높이는 것을 목표로 하고 있습니다.


여러 사례 중 공공기관으로서는 이례적으로 2013년부터 조기 구축되어 현재까지 운용 중인 서울정책아카이브의 사례를 들어 이러한 흐름에 대하여 살펴보겠습니다.

서울정책아카이브?

서울시 해외도시협력담당관과 서울연구원 글로벌미래연구센터가 공동으로 운영하는 서울정책아카이브는 서울시의 도시 발전에 관한 축적된 지식을 공유하며, 세계 각국의 도시정보를 제공하고, 국내외 도시개발에 관한 민관협력을 촉진하기 위해 만들어졌습니다.


  • 서울정책아카이브는 서울 발전에 큰 영향을 미친 정책들을 체계적으로 수집·연구 저장하고, 국제기구, 기업, 일반 시민들이 이를 통합적으로 검색하고 활용하는 것을 목표로 하고 있습니다.

  • 서울정책아카이브는 시민사회, 기업, 정부 및 학계가 공동으로 활용할 수 있는 세계 도시정보의 저장소로, 세계 도시의 정보 체계적인 축적과 활발한 활용을 지원하는 시스템입니다.

  • 서울정책아카이브는 민간기업 해외진출 사업 행정적 지원과 해외 도시인프라 개발 사업에 대한 정보제공을 주요 기능으로 하며. 성공적인 해외진출을 원하는 민간기업인들의 많은 동참을 기대하고 있습니다.

서울정책아카이브 ‘소개’ 중 발췌


여기서 짚고 넘어갈 것은, 서울정책아카이브 웹사이트는 일반적이고 학술적인 의미의 ‘아카이브'의 정의/개념과는 다소 차이가 있으며, 아카이브의 일부 형식을 차용하여 서울의 우수한 ‘정책’을 중심으로 체계적인 노출을 통해 서울시 우수정책의 해외 진출을 목표로 하고 있다는 점입니다.


더 쉽게 말하자면, 일반적인 기관 웹사이트의 정책 자료실이 발전/특화된 형태라고 할 수 있습니다. 서울정책아카이브 웹사이트는 기본적으로 디바이스의 구분이 없이 PC/태블릿/모바일 등 온라인에서 쉽게 접근과 이용이 가능하도록 적응형(Adaptive) 디자인이 적용되어 있습니다. PDF, DOC 등 바이너리(Binary) 파일로 제공된 정책 콘텐츠들은 웹에 최적화된 문서로 작성하여 검색엔진 또는 자체 검색 기능으로는 데이터의 상세 내용을 찾을 수 없던 점을 개선했습니다. 뿐만 아니라 모든 콘텐츠 페이지 목록을 XML기반의 API를 제공하여 필요시 최신 정보의 공유와 활용이 가능하도록 구성하였습니다.


시작

기획 당시 약 100여 개의 서울시 우수 정책을 효과적으로 노출하고 관리하기 위한 방식부터 고민했습니다. 1950년대부터 지금까지 서울시의 발전과 함께 작성된 자료들은 오프라인에 하드카피로 존재하던 지류 정보와 어플리케이션이 있어야만 조회가 가능한 HWP(한글) 문서, PDF 파일 등 각기 다른 포맷으로 작성되어 있었습니다. 이에 따라, 유관 부처 및 담당자별로 각기 다른 곳에 보관되어 있는 정책 자료들을 모두 취합하고, 이를 ‘온라인’ 상에서 편리하게 제공하는 것이 주요 사업 목적이었습니다.


제공 방향성 수립

우선 ‘정책’ 데이터는 문서의 메타(Meta) 항목만을 데이터로 저장하고, 본문 주요 내용은 일반적인 PDF 또는 HWP 문서 등 바이너리 파일 형태로 제공하는 방식이 아닌, 텍스트로 검색이 가능한 웹 문서 형태로 게시하기로 결정했습니다. 이 경우 최초 콘텐츠의 생산(입력)과 관리에 훨씬 많은 수고가 들지만, 내부의 검색기능이 아닌 외부 검색엔진의 노출이 매우 용이해지고 이로 인한 활용과 노출도 증대에 효과적이기 때문입니다.


<구글 검색시 노출되는 서울정책아카이브의 ‘중앙버스전용차로’ 콘텐츠>


플랫폼 선정

서울정책아카이브는 오픈소스 CMS(콘텐츠관리시스템)엔진인 드루팔(Drupal)을 이용하여 개발되었습니다. 웹 개발을 위한 여러 CMS가 존재하지만 드루팔은 콘텐츠 취급 및 생산, 설계와 활용성 면에서 뛰어난 장점이 있고 분류(Taxonomy)의 그룹핑(Grouping) 및 개별 분류를 콘텐츠 페이지로 사용할 수 있는 점, 그리고 메타 정보를 필드에 구성하거나 이를 자동화하여 SEO(검색엔진최적화) 정책을 세부적으로 수립할 수 있는 점 등 이 사업에 필요한 기술요건에 부합하였습니다.


구현

콘텐츠 생산

웹 콘텐츠의 생산은 일반적인 CMS에서 제공하는 기본 기능입니다. 하지만 아카이브의 속성을 가지고 계속 누적되는 콘텐츠들을 사용자에게 어떤 형태나 분류별로 제공해야 하는 사이트의 성격상, 일반적인 입/출력(쉽게 말해 게시판형) 양식보다 고도화된 생산 기능이 필요하다고 생각했습니다.


사이트의 콘텐츠 중 가장 중요한 정책 콘텐츠는 서울시가 보유한 모든 정책 문서가 동일한 목차나 규격을 가진 형태로 작성되지 않았기 때문에, 이를 최대한 규격화하기 위해 단락별 주제(제목)와 내용을 사용자가 원하는 만큼 추가 입력하고 추후 순서 변경을 쉽게 할 수 있도록 에디터를 개발하여 적용했습니다. 이로 인해 아무리 긴 정책 내용이라도 하나의 텍스트 구역(Textarea)에서 데이터를 입력할 필요 없이 요구되는 섹션을 생성하여 적용하거나 추후 해당 섹션만 수정/삭제하는 것이 매우 쉬워졌습니다.


섹션의 내용을 작성하는 영역은 웹 에디터 중 ‘CKEditor’로 구현해 문서 편집에 필요한 적정 요건을 제공하도록 하였습니다. ‘CKEditor’는 잘못된 HTML태그의 수정이나 보안에 위배되는 코드를 입력단에서 제거하고, 미리 설정한 컬러 Scheme 및 문서 스타일을 편리하게 정의할 수 있는 등 사용자와 관리자의 편의성을 모두 고려한 강력한 편집 기능을 가지고 있습니다.


서울아카이브_정책작성화면캡쳐.jpg

서울아카이브_정책캡쳐.jpg

<정책 콘텐츠 편집 화면(위)과 실제 콘텐츠의 출력 화면(아래) 캡쳐>


또 다른 자료 형식으로 위치정보 기반(Geo Location)의 콘텐츠를 들 수 있습니다. 지금까지 서울시와 정책교류를 하고 있는 도시들의 정보를 지도상에 표시하여 위치와 밀집도, 분포율 등을 시각적인 정보로 체감할 수 있게 구현하는 것을 목표로 하였습니다.


이렇게 구현된 기능으로 단순히 개발자가 코딩한 데이터만 출력되는 것이 아니라, CMS 내부에서 콘텐츠의 생산기능을 이용해 도시별 정책 진출 분야나 사업 연관 내용을 관리자가 직접 작성합니다. 사용자가 조회하는 화면에서는 정보들을 구글지도의 지정된 위치에 출력됩니다. 이후 추가/변경되는 내용을 직접 수정할 수 있고 이를 통해서 기존 이미지 1장으로 표시되던 해외진출도시 정보를 동적으로 제공할 수 있게 되었습니다.


<정책해외진출현황 콘텐츠 편집화면(위)과 실제 콘텐츠의 출력화면(아래) 캡쳐>


이외에도 연구원에서 발행하는 정기 계간지 <세계와 도시>의 섹션별 자료를 단행본 권호별 또는 전체 권호의 특정 섹션만 모아서 볼 수 있도록 분류 체계를 연동하였습니다. 분류가 하위 연결되는 것 뿐만 아니라, 인용이나 참고 형식으로 연계가 가능한 드루팔 CMS의 강점인 ‘분류’ 기능의 이용해 서로 다른 2개 이상의 분류 체계를 동시에 적용하여 콘텐츠를 필터링하도록 구현한 것입니다.


<계간 ’세계와 도시’ 콘텐츠 리스트>


콘텐츠 관리

콘텐츠 관리 시스템인 드루팔은 특정 기능에서 생산된 것을 제외한 모든 콘텐츠가 노드(node) 단위로 구성됩니다. 워드프레스(WordPress) 등 해외에서 제작된 CMS의 대표적 특징은 콘텐츠는 단일 형식으로 규정하고, 콘텐츠의 분류나 속성을 별도로 부여하여 사용하는 것인데, 쉽게 생각하면 서류함에 들어있는 파일을 생각하시면 됩니다.



<node 단위의 도식화>


이 노드에 분류(Taxonomy)를 추가하거나, 입력 항목(필드, Field)를 자유롭게 설정하여 콘텐츠를 생산, 관리할 수 있습니다. 해당 콘텐츠를 분류할뿐만 아니라 노드 입력항목의 속성값 등을 이용해 콘텐츠를 다양한 형태와 방식으로 사용자에게 제공할 수 있습니다.


그리고 이러한 노드들을 콘텐츠(Contents) 화면에서 한 번에 조회하고 관리할 수 있습니다.

<콘텐츠 관리 화면>


또한, 개별 노드에 다국어 기능을 적용하고, 논리적으로 연결되는 node를 추가하여 동일한 콘텐츠의 여러 언어 버전을 효율적으로 관리할 수 있습니다.



콘텐츠 출력 및 API

우리가 게시판으로 인식하는 콘텐츠 목록은 위 노드(Node)의 묶음이라고 볼 수 있습니다. 제목, 작성일, 작성자, 분류 등 콘텐츠의 메타정보를 이용해 원하는 콘텐츠를 선택하거나 검색할 수 있는 화면입니다. 노드가 개별 파일이라면, 콘텐츠 목록은 파일이 들어있는 하나의 서랍(Drawer)으로 이해하시면 되겠습니다.

<파일 서랍, 출처: Pixabay>

<콘텐츠 목록화면>


필요한 경우 목록 우측 상단의 구독하기 버튼으로 XML 형식 출력을 파싱(Parsing) 받아 최신 문서의 정보를 별도의 용도로 활용할 수도 있습니다.

우리도 할 수 있을까?

지금까지 서울정책아카이브에 대해서 특징과 장점을 중심으로 살펴보았습니다. 만일 공공이나 민간영역 등에서 이와 유사한 사업을 준비할 경우 아래의 사항들을 중점으로 접근하시길 바랍니다.

준비

누구를 위해 무엇을 만드는 것인가요?

이것이 완성되었을 때 이용하고 혜택 받을 사람들이 누군지 생각해 보세요. 누가 어떤 식으로 이용하게 될지, 무엇을 위해 이용하는지 고민하는 것은 모든 시스템 구현에서 방향성을 수립하는 시작점입니다. 불특정 개인은 IT 감수성(이용 수준)도 모두 다르고, 이용하는 환경이나 익숙한 형식에도 차이가 있습니다. 물론 모든 사람들을 만족시키는 것은 매우 어렵지만, 주요 사용자 그룹을 정하여 그 범위를 설정할 때 프로젝트가 나아갈 수 있는 방향을 설정할 수 있습니다.


어떤 것을 담게 되나요?

콘텐츠의 종류와 속성을 정의하는 것은 생산과 제공방식을 결정할 수 있는 ‘키포인트’입니다. 접시에 국을 담을 수 없고, 호리병에 밥을 담아 제공하는 것은 이것을 먹으려는 사람을 난처하게 만듭니다. 웹 문서는 단순한 텍스트로 구성된 콘텐트부터 영상과 음성 등의 미디어 자료가 섞인 콘텐트까지 제공할 수 있는 범위에 거의 제한이 없습니다. 다만 이것이 보관되어 운영될 서버의 성능과 사양은 이용자의 수와 함께 보관될 콘텐츠의 용량, 사양, 속성이 복합적으로 고려되어야 하고, 제공되는 모양과 형태를 가장 이상적인 형태로 기획함과 동시에 실제 운용-생산 및 관리-할 수 있는 인적/물적 자원을 분배해야 합니다. 무한정의 자원을 가진 경우가 아니라면 보유한 자원을 효율적으로 이용하기 위해 충분한 고민이 필요합니다.


오픈까지 남은 시간은 얼마나 되나요?

사업을 진행하기에 앞서 허용된 투입자원과 일정을 면밀히 파악하여 분석-기획-설계-제작에 이르는 기간을 각각 충분히 확보하세요. 앞서 열거한 2가지의 속성이 미리 정의되었다면 바로 기획과 설계를 진행할 수 있겠지만, 충분한 분석과 계획 없이 일정에 쫒겨 진행된다면 제작 단계에서 마주치게 될 난관보다 완성되고 난 뒤 제대로 이용될 것이라는 기대를 하기 어렵습니다. 시간적 여유가 충분하다면 본격적인 사업에 앞서 소규모로 시제품(프로토타입)을 만들어 시장성 조사나 내부 테스트를 거치는 것이 큰 도움이 될 수 있습니다.


언제까지 제공될 예정인가요?

시일이 지나 완전히 바뀐 내용을 반영하지 못한 정보들은 오히려 정보가 없는 것보다 사용자를 실망시킬 수도 있습니다. 지난주에 한 블로그에서 찾은 2012년의 맛집을 찾아갔다가 부동산 앞에서 망연자실한 표정을 지었던 저처럼요. 아카이브의 특성상 특정 시일이 지난다면 파기해야 하는 자료가 있을지도 모릅니다. 문서의 생산과 제공만큼 중요한 것은 언제까지 자료를 공개/제공하는지, 언제 파기되어야 하는지 등에 대한 계획입니다.


정리

분류와 정리

이러한 사항들이 어느 정도 정리되었다면, 이제 종이와 비디오 테이프, 한글 및 PDF, 엑셀 파일, 녹음 테이프, 사진, 자필 편지 등 제공하려는 모든 자료들을 찾아 분류를 시작하시기 바랍니다. 분류 체계는 이를 접하는 사용자가 원하는 자료를 찾을 수 있도록 정확하고 상징적인 표현으로 구분되는 것이 중요하고, 이 때 아키비스트(Archivist, 기록보관인)의 도움을 받는 것도 좋은 방법입니다. 어떤 경우는 연대별로 정리하게 될 수도 있고, 특정 사건이나 주제별로 정리되기도 합니다. 웹사이트에 존재하는 콘텐츠들은 이를 다중으로 적용하여 필터링이 가능하므로 기본 분류를 중복으로 부여해도 무방합니다.


디지털 변환

하드카피(물리적 실체)로 존재하는 원본은 안전하게 보관되어야 하지만, 인터넷과 웹사이트를 통해 제공하려는 자료는 디지털 변환이 필요합니다. 스캔, 음성 및 영상을 디지털 전환(Digitization)하거나 수작업으로 전자문서화 하는 노력이 요구됩니다. 파일의 제목은 일정한 형식을 가지고 있어야 추후 일일이 문서를 열어보는 노력을 줄일 수 있으며, 특정 주제별로 폴더를 정리하면 이후 단계에서 더욱 시간을 절약할 수 있습니다. MacOS등 일부 운영체제의 경우 파일에 태그를 부여할 수 있으므로 이를 이용하는 것도 좋은 방법입니다.


마치며

이상으로 아카이브 형식의 웹사이트에 대해 살펴보고 이러한 것을 만들때 필요한 사항을 정리해보았습니다. 충분한 준비되었다면 이를 실행하고 구현할 팀과 함께 더욱 의미있는 사이트를 만들 수 있을 것입니다. 꼭 잊지 말아야 할 점은 ‘어떤 것’을 만드는 것 보다 ‘무엇을 위해’ 만드는 것인지를 매 순간 떠올리는 것입니다. 형식은 목적을 위해 존재하고 방법은 목적을 달성하기 위한 수단이라는 것을 기억하시길 바랍니다.





Posted by slowalk

마케팅 목적으로 보내는 이메일 중에서 가장 돋보이는 것은 정기적으로 보내는 뉴스레터입니다. 많은 사람들이 ‘마케팅 이메일'하면 ‘뉴스레터'를 떠올릴 정도로 큰 비중을 차지하고 있습니다. 실제로 슬로워크의 이메일마케팅 서비스 ‘스티비(Stibee)’에서 마케터 180명을 대상으로 조사한 결과, ‘마케팅 이메일을 발송하는 가장 중요한 목적'으로 ‘회사, 단체의 소식 알리기(35.6%)’가 가장 많았고 ‘구매, 후원 등의 직접적인 행동 유도(29.4%)’가 두번째였습니다. 이 두 가지는 뉴스레터의 발송목적과도 일치합니다.


상세 내용은 2017 이메일마케팅 리포트에서 확인할 수 있다


독점 콘텐츠가 뉴스레터의 효과를 높인다?

그렇다면 뉴스레터의 효과를 극대화하려면 무엇이 가장 중요할까요? 뉴스레터에서만 접할 수 있는 독점 콘텐츠를 제작해서 발송한다면 어떨까요? 홈페이지, 페이스북, 인스타그램에서도 볼 수 있는 소식을 단순히 모아놓은 뉴스레터가 많은데요, 여러 소식을 모아서 보내다 보면 이미 접한 내용을 한발 늦게 보내는 뉴스레터가 될 수밖에 없습니다. 그렇지만 뉴스레터에서만 접할 수 있는 독점 콘텐츠가 있다면 고객을 기대하게 만드는, 살아있는 뉴스레터를 만들 수 있습니다.


독점 콘텐츠를 효과적으로 사용하여 이메일 뉴스레터의 인게이지먼트를 높이는 3가지 사례를 소개합니다.




퍼블리의 ‘What We’re Reading’

콘텐츠 스타트업 퍼블리에서 2015년 6월부터 매주 금요일에 발행하고 있는 뉴스레터입니다. 퍼블리가 100번째 레터(2017.6.30)에서 밝힌 바에 따르면, 구독자 수는 1호 506명에서 100호 8,257명으로 증가했고, 평균 오픈율은 약 30%이며, 이메일을 통해 유료결제로 이어지는 전환율은 약 10%라고 합니다.


‘What We’re Reading’는 단순히 한 주간 새로 발행된 상품을 소개하는 것을 넘어서, (1) 매주 다른 팀원이 전체 편집을 맡고, (2) 편집자 개인의 감상이 들어간 글로 시작하며, (3) ‘PUBLY 팀이 읽은 이번 주 콘텐츠’라는 섹션을 통해 퍼블리 상품 외에 추천할만한 다른 콘텐츠도 소개하는 것이 특징입니다.


매주 다른 팀원이 뉴스레터 편집을 맡고, 편집자 개인의 글로 뉴스레터를 시작한다


퍼블리 상품이 아닌 외부 콘텐츠도 소개하고 있다



퍼블리 뉴스레터에는 특별한 점이 한 가지 더 있습니다. 매주 이메일을 발송한 뒤, 박소령 대표의 페이스북에서 뉴스레터의 내용을 소개합니다. 이를 통해 퍼블리가 이메일 뉴스레터를 얼마나 중요한 매체로 생각하고, 그 콘텐츠 작성에 심혈을 기울이는지 알 수 있습니다.


뉴스레터의 내용을 소개하는 퍼블리 박소령 대표의 페이스북 글



얼마 전 퍼블리에서는 뉴스레터 하나를 더 발행하기 시작했습니다. 유료 멤버십 가입 회원에게만 보내는 ‘멤버십 뉴스레터’인데요, 매주 화요일에 발행되고, 현재 오픈율은 약 60%라고(!) 하네요. 퍼블리의 새로운 시도도 응원합니다.


> 퍼블리 뉴스레터 구독하기



농사펀드의 ‘에디터가 쓰다'

농업 크라우드펀딩 스타트업 농사펀드가 2017년 3월부터 매주 발행하고 있는 뉴스레터입니다(발행 요일은 일정하지 않습니다).

‘에디터가 쓰다'도 독점 콘텐츠의 조건을 갖추고 있습니다. (1) 매주 다른 에디터가 전체 편집을 맡고, (2) 에디터 개인의 감상이 들어간 글로 시작합니다. 특히 음악과 시를 소개하기도 하는 점이 인상적입니다.


매주 다른 에디터가 뉴스레터 편집을 맡고, 에디터 개인의 글로 뉴스레터를 시작한다


가끔은 시 한 편도 소개받을 수 있다


특히 제가 가장 흥미롭게 읽은 ‘에디터가 쓰다'는 식재료를 판매하는 서비스답게 디저트 레시피를 소개한 이메일이었습니다.


‘에디터가 쓰다' 14호(2017.7.7) 중


농사펀드에서 듣기로 ‘에디터가 쓰다'를 친근하게 느껴서 답장을 보내오는 고객들도 있다고 합니다.


> 농사펀드 뉴스레터 구독하기(회원가입)


텀블벅의 ‘스포트라이트'

창작자를 위한 크라우드펀딩 스타트업 텀블벅에서도 매주 목요일에 뉴스레터를 발행합니다. 퍼블리와 농사펀드처럼 매주 독점 콘텐츠를 발행하지는 않지만, <텀블벅 직원들은 어떤 프로젝트를 후원할까?>라는 콘텐츠를 이메일로만 발행한 적이 있습니다.


이 콘텐츠에서 인상깊었던 점은 편집자뿐만 아니라 개발자, CS담당자, 디자이너 등 다양한 포지션의 직원들이 작성에 참여했다는 점입니다. 특히 개인의 경험을 바탕으로 쓴 글이라서 더 마음에 와 닿았습니다.


‘텀블벅 직원들은 어떤 프로젝트를 후원할까?’ 콘텐츠 중



> 텀블벅 뉴스레터 구독하기(회원가입)



독점 콘텐츠를 효과적으로 사용하여 이메일 뉴스레터의 인게이지먼트를 높이는 사례 3가지를 살펴봤습니다. 단순히 지난 소식을 모아놓는 것이 아니라, 독점 콘텐츠로 살아있는 뉴스레터를 보내보세요. 그리고 인게이지먼트 변화를 체감해보세요.



Posted by slowalk

 

Framer, Flinto, Origami, Invision. 많은 프로토타이핑 도구가 존재합니다. 디자인에 활력을 불어넣고 개발팀과의 커뮤니케이션을 위해 필수라고 하는 프로토타이핑. 어떻게 하기는 해야겠는데 어려운 도구나 코드를 공부하기엔 시간이 없고, 막상 열심히 공부하면 새로운 버전이 나오고, 더 좋은 도구가 나오고. 이런 경험을 많이 하셨을 겁니다. 프로토타이핑 도구로 멋지고 완결된 시나리오를 가진 결과물을 만들 수도 있습니다. 하지만 우리에게 당장 필요한 것은 지금 당장 떠오르는 아이디어를 보여줄 수 있는 아이콘의 간단한 모션, 쓱 움직이는 화면 전환 같은 것이 아닐까요? 오늘 배워서 바로 쓸 수 있는, CSS 애니메이션으로 하는 간단한 프로토타이핑 방법을 소개합니다. 



어디서, playground

코딩을 공부하려면 텍스트 에디터도 설치해야 하고, 각종 패키지도 설치해야 합니다. 결과물이 담길 파일도 생성해야 하고, 여러 파일이 연결되니까 폴더 구조도 고민해야 하죠. 이런 고민을 하다 보면 시작조차 하기 싫어집니다. 그래서 브라우저에서 바로 작성하고 확인하고 공유할 수 있는 온라인 코딩 플레이 그라운드가 있습니다. 대표적으로 jsbincodepen이 있습니다. 그냥 해당 서비스에서 가서 각 섹션(HTML 또는 CSS)에 맞게 코드를 입력하기만 하면 됩니다. 우리는 HTML과 CSS섹션만 사용할 예정입니다. js와 같은 다른 섹션은 최소화(minimize)해주세요.


어떻게 시작하나

html에 내용을 담고, CSS에 디자인(스타일)을 담을 겁니다. 당장 직접 작성하기는 어려우니 예제(https://codepen.io/yunkimoon/pen/BZEYgY)의 HTML과 CSS코드를 그대로 복사합니다. 코드의 주석(회색글씨)을 확인해 봅니다. 요약하면 아래와 같습니다. 


- 가장 바깥의 파란 배경 상자
- 이미지와 그걸 담고 있는 상자
- 파란 배경 상자에 hover(마우스 오버 이벤트)를 하면,
- left 포지션을 2%에서 80%로 변경


여기서 중요한 건 .box상자에 설정된 transition이라는 속성입니다. transition은 딱딱한 움직임을 부드럽게 해줍니다. 여기서는 position left를 2%에서 80%로 부드럽게 바꿔주었습니다. 위치뿐만 아니라 색상(color, background), 크기(width, height)도 자연스럽게 바꿔주는 속성입니다. “all 3s”라는 값을 가지고 있는데 “모든 변경사항에 대해 3초 동안 움직여라”라는 의미입니다. 


꼭 알아야할 3가지

CSS 애니메이션의 맛을 잠깐 보았습니다. transition을 통해 부드러운 움직임을 줄 수 있습니다. 하지만 더 복잡하고 멋진 움직임을 위해서는 많은 속성들을 이해하고 응용할 수 있어야 합니다. 하지만 모든 속성을 다 알아볼 수는 없으므로 가장 중요한 3가지를 알아보도록 하겠습니다. 미리 살펴본 transition과 transform, keyframe(s) 입니다. 


1. transition 
위에서 살펴본 것처럼 대상의 위치, 크기, 색상 등에 부드러운 움직임을 줍니다. 

2. transform 
대상의 위치, 크기, 방향 등을 상대적으로 변경합니다. 예제를 통해 알아보겠습니다. 



2.1. rotate 
대상에 각도 값을 설정합니다. 즉, 주어진 값만큼 회전합니다. 첫 번째 예제와 조금 다른 부분은 :hover { }에 작성된 내용입니다. transform:rotate(360deg)에서 rotate는 회전을 뜻하고, 360deg는 각도입니다. 즉, 360도(한 바퀴)만큼 회전하라는 의미입니다. 미리 transition이 걸려있었기 때문에 부드럽게 회전하는 모습을 볼 수 있습니다. 


2.2. translate 
대상의 이동 값을 설정합니다. 주어진 값만큼 이동합니다. 값은 좌푯값으로 x축, y축 값을 나눠서 줍니다. transform: translate(100px, 100px)에서 translate는 이동을 뜻하고, 이후에 나오는 값은 순서대로 x축의 이동값, y축의 이동 값입니다. 그런데 y축 이동 값이면 위로 올라가야 할 것 같은데, 그림은 아래로 이동합니다. 그 이유는 스크린에서 좌측 위가 기준점이기 때문입니다. 


2.3. scale 
대상의 크기를 설정합니다. 즉, 주어진 값만큼 늘어나거나 줄어듭니다. 값은 가로 값, 세로 값을 차례로 줍니다. transform:scale(1.5, 2)에서 scale은 크기를 뜻하고, 1.5와 2는 각각 가로값, 세로값을 뜻합니다. 가로는 1.5배가 늘어나고 세로는 2배가 늘어납니다. 그래서 그림은 세로로 긴 비율로 보입니다. 


이제 우리는 CSS만으로 대상의 위치, 크기, 회전 애니메이션을 줄 수 있습니다 :) 


3. keyframes

마우스 오버 액션에 대한 애니메이션을 보아왔습니다. 이렇게 사용자의 특정 반응(마우스 오버)이 없어도 자동으로 움직이도록 할 수는 없을까요? 앞의 두 예제보다 조금 복잡하지만 keyframes를 사용하면 가능합니다. keyframes는 미리 움직임을 지정해두고, 대상에 해당 애니메이션의 속성을 부여하는 방식으로 작성됩니다. 예제를 확인해 보겠습니다. 



3.1. spin

앞서 살펴 본 transform의 rotate를 미리 애니메이션을 만들어 놓고 대상에 animation이라는 속성을 설정했습니다. 

@keyframes spin 처럼 spin이라는 애니메이션을 설정합니다. 그 안에는 from과  to가 있는데 각각 시작과 끝을 뜻합니다. 즉, 시작할 때는 회전이 0(rotate(0deg))이고 끝날 때는 회전이 360도(rotate(360deg))입니다. 

대상과 keyframes를 연결할 때는 대상에 animation: spin 8s infinite linear;와같이 애니메이션 속성을 줍니다. spin은 keyframes의 이름, 8s는 8초 동안, infinite는 무한 반복을 뜻합니다. 여기서 linear는 easing을 나타내는데, 우선은 조금 딱딱한 애니메이션이라고 해둡시다. 


3.2. leftAndRight

transform의 translate를 활용해서 우측으로 이동했다 돌아오는 애니메이션을 반복시키는 예제입니다. from과 to대신 조금 상세한 타임라인을 가지고 있습니다. 0%, 50%, 100%는 타임라인을 구성하는 속성들로 전체 애니메이션 시간 동안 해당하는 타이밍에 맞게 속성이 변경됩니다. 역시 infinite 속성이 있어 계속 반복되고 있습니다. 그리고 마지막에 linear대신 ease라는 속성을 넣어서 조금 부드러운 움직임 표현했습니다. 


3.3. hideAndShow

앞서 다루지 않은 opacity(투명도)를 활용했습니다. 1이 100%이고 0은 보이지 않는 상태입니다. 1 → 0 → 1을 반복하며 보였다 안 보였다 하는 애니메이션을 보여줍니다. 

이제 우리는 CSS만으로 대상의 위치, 크기, 회전 애니메이션 반복적으로 사용할 수 있게 되었습니다. 그리고 무한 반복 애니메이션도 만들 수 있습니다. 


마무리 예제


앞서 살펴본 예제들을 활용한 마무리 예제를 만들어 보았습니다. 앞서 공부한 내용을 바탕으로 소스를 분석해 보시기 바랍니다. 각 버튼에는 transiton으로 부드러운 hover 전환 효과를 주었고, 녹색의 메시지는 keyframes를 주어 상하로 계속 움직이도록 했습니다. frame에 마우스가 올라가면 메시지는 프레임 바깥으로 밀려나고 사용자 메뉴가 프레임 안으로 이동하도록 했습니다. 메뉴는 하위 메뉴가 펼쳐지는 인터렉션을 가지고 있습니다. 


마치며

전문 프로토타이핑 도구보다 결과물이 투박하고, 지금 당장 만들 수 있는 장면도 제한적입니다. 자바스크립트 같은 동적 언어가 들어가 있지 않아 표현할 수 있는 화면도 많지 않습니다. 기본적으로 제공되는 템플릿이나 자원이 없으므로 하나하나 HTML로 코딩하거나 공개 소스를 넣어가면서 만들어야 하는 수고로움도 존재합니다. 

하지만 실행만 해도 막막한 도구들을 바라보며 “언제 한 번 해보나”하는 생각을 할 시간에 간단히 익혀 한 번이라도 써먹을 수 있다면 그 자체로 의미가 있지 않을까요? 물론 탄탄한 시나리오와 설계를 가지고, 제대로 만든다면 전문 프로토타이핑 도구보다 절대 뒤쳐지지 않을 것입니다. 그리고 우리가 만든 코드들은 커뮤니케이션을 위한 전달용이 아니고 실제로 쓰일 수도 있는 코드라는 점에서도 의미가 있습니다. 간단한 프로토타이핑이라도 지금 시작해 보면 어떨까요? 



참고 

w3school

- https://www.w3schools.com/css/css3_animations.asp

- https://www.w3schools.com/css/css3_transitions.asp

- https://www.w3schools.com/css/css3_2dtransforms.asp

- http://report.stibee.com/2017 by 슬로워크 스티비팀 조은지 디자이너



Posted by slowalk

안녕하세요. 스티비팀 서버 개발자 이학진 입니다. 저희는 최근 서비스에서 사용 중이던 MySQL DB를 RDS로 이관하는 작업을 진행하였습니다. 무엇 때문에 이관을 결정하게 되었는지와 어떻게 이관을 진행하였는지에 대해 글을 써보도록 하겠습니다.


배경

stibee.com은 작년 11월에 정식 오픈한 새내기 이메일마케팅 서비스 입니다. 사실 오픈 초기부터 얼마전까지만 해도 AWS EC2의 m4.large 인스턴스 하나로 운영되던 서비스였습니다.(사실 웹+API 서버 1대, 메일발송 서버 1대) 그리고 이 싱글 인스턴스에 무려 6개의 서버, MySQL 1개, Kafka™ 1개, Redis 1개가 돌고 있었습니다. 그럼에도 불구하고 CPU 사용률은 20%를 넘지 않았습니다.

 

하지만 최근 사용자도 점점 늘어났고, 네이버에서 메일 수신정책을 변경하면서 메일발송 서버에 대한 요청이 급증했습니다.

 

스티비에서 네이버로 대량메일을 발송했을 때 해당 메일의 본문 링크를 자동검사하는 것을 발견했는데요, 따라서 네이버로부터 비정상적으로 많은 요청이 들어오고 있었습니다. (어떤 기준으로 이런 검사를 하는 것인지 정확한 정책은 아직 모릅니다. 담당자분 이 글을 보신다면 연락주세요. 친하게 지냈으면 합니다 )

 

이내 곧 이메일을 마구 쏘아대던 스티비 서버는 네이버의 분노를 감당하지 못했습니다. (사실 네이버의 요청은 무난하게 처리하였으나, 요청 후 발생되는 이벤트 후처리에 DB를 과하게 사용하여…) 결국 CPU가 비명을 질렀습니다. 

 

일단 네이버의 IP를 달고오는 패킷을 drop함으로써 문제를 해결하였지만, 이로 인해 이제 "우리도 때가 왔다!'라는 판단을 하게 되었습니다.

 

그리하여 앞으로 대박날 스티비를 대비하여 스케일아웃이 가능하도록 각각의 서비스를 분리하는 작업을 진행하게 되었습니다. (사실 지금도 조금씩 떼어내고 있습니다. )


준비

앞서 RDS로 이관한다고 말씀드렸는데요. RDS는 AWS에서 제공하는 관계형 데이터베이스 서비스(링크)입니다. RDS로 이용 가능한 DB는 Amazon Aurora, PostgreSQL, MySQL, MariaDB, Oracle, Microsoft SQL Server 등이 있습니다.

 

이 중 저희는 아마존에서 MySQL과 호환되며 최대 5배까지 성능을 낼 수 있다는 Aurora DB를 사용하기로 결정하였습니다. (결정권자=1인=저)

 

AWS의 모든 서비스가 그러하듯이 RDS를 생성하는 방법은 매우 간단합니다. RDS 콘솔로 진입하신 뒤, 메인 대쉬보드의 “Launch a DB Instance” 를 클릭면 됩니다.



그러면 아래와 같이 사용 가능한 DB 목록들이 나타나는데 이중 기본값인 Aurora DB를 선택하고 “Select” 버튼을 누르시면 됩니다.


 


그리고 회사의 자금사정(?)에 따라 인스턴스 타입을 결정하시면 됩니다.


인스턴스 타입의 선택은 서비스의 특성에 따라 다르므로, 다양한 성능 테스트를 해보시기 바랍니다. 저희는 당시 EC2 인스턴스에서 DB가 같이 돌고 있었던 지라, EC2와 비슷한 사양이되 그 중 제일 저렴한 사양인 r3.large를 골랐습니다.


사양 선택에 있어 너무 고심할 필요는 없습니다. AWS에서는 클릭 한번으로 언제든 사양을 변경할 수 있으니까요~


사양을 변경하기 위해서는 인스턴스의 재부팅이 필요하므로 실서비스에 물리기 전에 결정하시길 권장합니다.



여기서 한 가지 주의할 점, Aurora DB는 기본적으로 클러스터로 구성됩니다. 클러스터란 쉽게 말해 여러 Aurora DB들을 하나로 묶어서 마치 하나의 DB처럼 사용 가능하게 해주는 역할을 합니다. (이로 인한 부하 분산, Fail-Over, 고가용성 등의 장점이 있습니다.)


앞서 저희는 하나의 인스턴스를 런칭 시키고자 하였으나, 설정페이지의 “Multi-AZ Deployment”의 기본설정을 보시면 “Create Replica In Difference Zone”으로 되어 있습니다. 이는 동일한 인스턴스를 다른 AZ에 추가적으로 둠으로써 고가용성을 보장하고 부하분산을 통해 성능향상을 해주겠다는 아마존 형님의 배려이십니다. (하지만 돈은 두배? ^^) 그래서 스티비는 “No”... (나중에 추가 할 수 있습니다.)


그 다음엔 DB Instance를 식별할 수 있게 이름을 부여하고 Root 역할을 할 Master Username과 패스워드를 설정해 줍니다. 그리고 “Next Step”


자! 이제 마지막 설정 페이지 입니다. 모든 옵션에 대해서 언급하고 넘어가기에는 설명이 너무 길어지니, 이것 정도는 알아야 한다는 옵션에 대해서만 간략히 설명하도록 하겠습니다.

  • VPC Security Group(s): DB Instance의 인/아웃바운드 트래픽 규칙을 정할 수 있습니다. 여기에서는 이미 생성된 Security Group을 사용할지, 아니면 새로 생성할 지를 정할 수 있으며, 선택된 Security Group의 세부 설정은 EC2 콘솔 페이지의 Security Group에서 할 수 있습니다.
  • Auto Minor Version Upgrade: Aurora DB의 마이너 패치가 나왔을 때, 자동으로 업그레이드를 할지 여부입니다. Yes로 하면 두세 달에 한번 업그레이드가 된다고 합니다. 주의하실 점은 업그레이드 시점에 DB는 사용 불가…(꺅!)
  • Maintenance Window: 자동으로 마이너 업그레이드를 한다면, 사용자의 이용이 가장 적은 시간대로 패치 시간을 적용할 수 있습니다.


모든 설정을 마치고 “Launch DB Instance”를 클릭하면 잠시 후 Aurora DB가 뙇!!하고 모습을 드러내시니, 이로써 이관에 대한 기본적인 준비를 마쳤습니다.

런칭된 오호라(?) DB

 

이관

AWS는 이관 방법에 대해 많은 정보를 문서로 제공합니다.

MySQL DB 인스턴스에서 데이터 가져오기 및 내보내기(링크)


운영 중인 DB를 중단하지 않고 RDS로 이관하는 방법도 있지만 이는 이관 후 데이터의 정합성을 검증해야하고 운영 DB의 부하발생 등의 이유로 해당 방법은 사용하지 않았습니다. 비록 점검 공지를 띄우고 이관 종료까지 서비스를 사용할 수 없지만 엔지니어에게 부담없는 방법을 택했습니다.

먼저 대략적인 이관 시간을 파악하고 스티비 활성 사용자가 가장 적은 시간대를 선택하여 이관을 진행하였습니다.


이관 절차는 아래와 같았습니다.

1. 자정(0시)을 기점으로 점검 페이지 교체

2. DB를 사용하는 모든 서비스 종료

3. MySQLdump를 이용한 DB백업

4. 백업된 DB 파일을 EC2로 복사

5. EC2에서 MySQL 명령어로 RDS 접속

6. source 명령어를 통한 백업파일 import

7. 명령어 실행 종료 후, 서비스 구동 및 점검 페이지 교체


이관을 진행함에 있어 EC2를 사용하는 이유는 다운타임을 최소화 하기 위해서 입니다. EC2와 RDS를 같은 VPC 내에 위치 시킴으로써 네트워크 속도가 가장 빠르기 때문입니다. (혹여나 그렇지 않을 경우 RDS가 속해 있는 VPC에 EC2를 생성해 주시기 바랍니다.)


아래는 3번부터 사용된 명령어 입니다.

DB 덤프 뜨기

$ mysqldump -u db_user -p --databases db_name --single-transaction --compress --order-by-primary > backup.sql


#필요에 따라서 압축

$ tar -zcvf backup.tar.gz backup.sql 


#EC2로 덤프 파일 복사

$ scp -r -i <key pair>.pem backup.sql.gz ec2-user@<EC2 DNS>:/<target_directory>/backup.sql.gz

#덤프 파일 복사는 편하신 방법으로 진행하시면 됩니다.

#MySQL 서버가 EC2에서 돌고 있었다면 생략


#EC2에서 RDS mysql 접속

$ mysql -h <host_name> -P 3306 -u <db_master_user> -p

#host_name은 RDS 콘솔 페이지에서 확인 할 수 있는 Cluster Endpoint 입니다.


#DB 및 User 생성

$ create database db_user;

$ grant all privileges on db_user.* to 'db_user'@'localhost' identified by 'db_password' with grant option;

$ grant all privileges on db_user.* to 'db_user'@'%' identified by 'db_password' with grant option;

$ flush privileges;


#이관 시작

$ source path/backup.sql



여기까지가 제가 진행한 이관절차의 끝 입니다. 그런데 과연 이렇게 간단하게 끝났을까요? 물론 아닙니다. 문제점 한두 개 정도는 나와줘야 제맛 아니겠습니까? (사실 악 악 하면서 심장이 쫄깃해졌습니다.)

 

문제 발생 및 해결

사실 지금까지 기술한 내용들은 AWS의 기술 문서에 매우 자세히 나와있어서 RDS를 사용하고자 하신 분들은 굳이 이 글을 읽지 않으셔도 무방하셨을 거 같습니다. 하지만 저 문서들을 보고 진행했음에도 불구하고 문제가 발생하더군요. 저희 서비스만의 문제였는지 모르겠지만 발생된 문제와 해결법을 공유해 보도록 하겠습니다.


1) Timezone

토종 한국 서비스라 Timezone이 한국 표준시였습니다. 그런데 기본 옵션으로 RDS 인스턴스를 생성하시면 국제 표준시로 설정이 되어 시간값이 다르게 나왔습니다.

DB Cluster Parameter Group의 time_zone 값을 Asia/Seoul로 변경한다.
기본 옵션값은 변경되지 않으므로 새로 생성한 뒤 변경하고, 인스턴스에 적용한다.


2) Import 중 ‘MySQL server has gone away’ 에러

MySQL의 옵션중 클라이언트가 한번에 전송 할 수 있는 패킷량의 제한이 있습니다. 이관 테이블 중 Long Text 타입의 컬럼이 있었고 해당 컬럼의 데이터를 옮기지 못해 발생된 에러 입니다. (이와 같은 이유로 아마존 기술 문서에 기술된 dump와 동시에 import하는 방식을 사용했을 때도 최대 1GB 밖에 전송할 수 없었습니다.)

DB Parameter Group의 max_allowed_packet의 값을 늘린다.
기본 옵션값은 변경되지 않으므로 새로 생성한 뒤 변경하고, 인스턴스에 적용한다.


3) 이모지 적용 안됨 

스티비는 메일 제목과 본문에 이모지 사용을 적극 권장하고 있습니다. 하지만 이관 후 작성된 메일의 이모지가 깨지는 현상이 발생 되었습니다. 이미 아시는 분들은 딱하고 감이 오시겠지만 네, CharacterSet 문제입니다. 그리고 이모지는 utf8mb4에서 지원됩니다.

DB Cluster Parameter Group의 character 옵션들을 바꾼다.
character_set_client = utf8mb4
character_set_connection = utf8mb4
character_set_database = utf8mb4
character_set_filesystem = utf8mb4
character_set_results = utf8mb4
character_set_server = utf8mb4
collation_connection = utf8mb4_unicode_ci


여기까지 스티비의 이관기를 마칩니다. 감사합니다.



Posted by slowalk

스타벅스에서 노트북으로 공부를 하거나 일을 하시는 분들이 많을 텐데요. 인터넷을 사용하려고 할 때 마다 만나는 지겨운 화면이 있죠?


다음 버튼을 누르고, 이름과 이메일을 입력하고 동의 체크박스 두 개 눌러주고 동의 버튼만 누르면 되는데 스타벅스에 갈 때마다 하려니 귀찮습니다. ‘자동으로 와이파이가 잡히면 좋을텐데’ 하는 생각을 하신 분들이 많을 것 같아서 오늘 그런 매크로*를 직접 만들어 보려고 합니다.

*매크로: 여러 개의 명령을 묶어 하나의 명령으로 만든 것. 여러 개의 명령을 수행하는 반복적인 작업에서 하나의 매크로 명령만으로 효과적인 작업을 수행할 수 있다. (출처: 네이버 국어사전)


매크로를 만들기 위해서 Nightwatch라는 녀석을 사용하겠습니다.


Nightwatch 초간단 소개


(사진 출처: nightwatchjs.org)


Nightwatch는 테스트 자동화를 위한 프레임워크입니다.


giphy.gif

쉽게는 이런 테스트를 생각하시면 됩니다. (출처: GIPHY)


제가 로그인 페이지를 만들었다고 가정해보겠습니다. 사용자는 로그인을 하기위해 로그인 페이지에 가서 아이디와 비밀번호를 입력하고 로그인 버튼을 눌러서 로그인을 시도합니다. 제가 잘 만들었다면 로그인이 성공하겠죠.


웹페이지에는 로그인 뿐만 아니라 수많은 기능들이 있습니다. 수많은 기능들에 대해 위와 같은 과정들을 사람이 매번 하기는 힘들고 실수를 할 가능성도 있습니다. 컴퓨터가 이런 테스트 과정을 자동으로 할 수 있도록 만들어야겠죠.셀레니움 웹드라이버(Selenium webdriver)라는 녀석이 위와 같은 테스트를 자동화 할 수 있게 도와줍니다. Nightwatch는 Node.js를 기반으로 셀레니움 웹드라이버를 더욱 쉽게 사용할 수 있도록 만들어 준 녀석입니다.


Selenium webdriver는 테스트 자동화를 위해 만들어진 녀석이지만, 결국 명령을 내린대로 돌아가는 것이라서 매크로처럼 사용해도 되겠습니다.


Nightwatch를 사용하기 위해 필요한 것들을 설치해 보겠습니다. 이 글은 Windows를 기준으로 작성됐습니다.


설치


  • Nightwatch

    • 커맨드라인 창을 열고 아래 명령어를 입력합니다.

    • npm install -g nightwatch




    • 설치가 완료된 것을 확인합니다.



  • Selenium-server-standalone 다운로드(링크)

    • 최신 버전의 selenium-server-standalone-{VERSION}.jar 을 다운로드합니다. (2017년 4월 24일 기준으로 3.3.1이 최신입니다.)


  • chromedriver 다운로드(링크)

    • 최신 버전을 다운받아 주세요. (2017년 4월 24일 기준으로 2.29가 최신입니다.)




준비

작업할 공간을 마련합니다. 저는 C드라이브안에 work 디렉토리에 starbucks 디렉토리를 만들었습니다. 그 안에 bin이라는 디렉토리를 하나 더 만들고 아까 다운받은 Selenium jar파일과 chromdriver을 bin 디렉토리 안에 집어넣으세요.


nightwatch 설정 파일이 필요합니다. 아래 설정을 그대로 복사 붙여넣기 해서 nightwatch.json이라는 이름의 파일을 만들어서 starbucks 디렉토리에 저장합니다. 그리고 설정 파일에서 selenium의 server_path에 본인이 받은 셀레니움 파일이름을 넣습니다. 저는 3.3.1버전이므로 아래와 같이 작성했습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/* nightwatch.json */

{
  "src_folders" : [""],
  "output_folder" : "",
  "selenium" : {
    "start_process" : false//여기를 true로 바꿔주면 나중에 selenium-server를 따로 실행 시키지 않아도 됩니다. 이따가 다시 설명하겠습니다.
    "server_path" : "./bin/selenium-server-standalone-3.3.1.jar"// <- 여기에 자신이 받은 selenium 파일 이름을 집어넣어주세요.
    "log_path" : "",
    "host" : "127.0.0.1",
    "port" : 4444,
    "cli_args" : {
      "webdriver.chrome.driver" : "./bin/chromedriver.exe"// <- 맥 유저는 .exe를 빼주세요.
      "webdriver.ie.driver" : ""
    }
  },
  "test_settings" : {
    "default" : {
      "launch_url" : "http://localhost",
      "selenium_port"  : 4444,
      "selenium_host"  : "localhost",
      "silent"true,
      "screenshots": {
        "enabled" : false,
        "path" : ""
      },
      "desiredCapabilities": {
        "browserName""chrome",
        "javascriptEnabled"true,
        "acceptSslCerts"true
      }
    }
  }
}
cs


위의 세팅 내용이 무엇인지 궁금하시다면 Nightwatch.js의 도움말을 참조하시면 됩니다.


웹 페이지 띄우기

starbucks 디렉토리에 starbucks_login.js라는 이름의 파일을 만들고 아래의 내용을 입력한 뒤 저장합니다.

브라우저에게 저희가 항상 마주치는 스타벅스 와이파이 URL로 접속하라는 명령입니다.

1
2
3
4
5
6
/* starbucks_login.js */

module.exports = {
  "스타벅스 로그인" : function(browser) {
    browser
      .url(‘https://first.wifi.olleh.com/starbucks/index_new.html’)
  }
}
cs


실행을 해보겠습니다. 명령프롬프트를 열고 현재 디렉토리로 이동해서 selenium 서버를 실행 시킵니다.


selenium 서버를 먼저 켜야합니다. 아까 nightwatch.json에서 selenium 안에 start_process를 true로 준다면 자동으로 selenium server를 실행시켜주지만 현재 저는 잘 되지 않아서 그냥 따로 실행을 시키겠습니다. 아래 명령을 커맨드라인에 입력해주세요.


1
$ java -jar bin\selenium-server-standalone.3.3.1.jar
cs



Selenium Server is up and running 이라고 뜹니다. 잘 실행이 됐습니다.


셀레니움 서버는 끄지말고 새로운 명령 프롬프트 창을 열어서 이번에는 저희가 만든 starbuck_login.js 를 실행시키겠습니다. 마찬가지로 커맨드라인에서 현재 디렉토리로 이동해서 아래 명령을 입력하세요.


1
$ nightwatch starbucks_login.js 





sb_01_index.PNG


잘 실행이 됐습니다.(참고로 이미 스타벅스에 로그인을 한 상태라면 위 화면 대신 KT 사이트로 연결이 됩니다.)


클릭하기

한 걸음 더 나가서 클릭을 해보겠습니다. 클릭을 하기 위해서는 제가 클릭하려는 엘레먼트의 CSS Selector를 찾아야합니다.


CSS Selector에 대해서는 CSS Selector reference - w3schoolCSS Selector 이해 참조 콘텐츠를 통해 자세히 확인해보세요. 우리가 클릭해야 하는 녀석은 <input type=”checkbox”> 이므로  input[type=checkbox]라는 셀렉터를 사용하면 됩니다.


이 과정이 어려운 분들은 선택하고 싶은 곳을 우클릭해서 검사를 누릅니다. 태그를 우클릭을 하고 copy를 누른 다음 copy selector를 눌러주시면 됩니다.


sb_inspect.png

[무료 인터넷 시작하기 버튼을 우클릭]

sb_inspect_clicked.png

[검사(inspect) 클릭 - 오른쪽에 파란색 처리 된 소스가 해당 버튼의 소스입니다.]


sb_inspect_copy_select.png

[파란색 처리된 태그를 우클릭 해서 copy - copy selector를 클릭]


이제 다시 starbucks_login.js에 가서 .click(‘input[type=checkbox]’)를 추가로 입력하고 저장합니다. copy selector를 하신 분들은 input[type=checkbox] 대신에 복사한 내용을 붙여넣기 하시면 됩니다.

한 가지 더! 페이지가 전환 됐을 때, 페이지가 다 뜰 때까지 기다려줘야 합니다. 그래서 클릭을 하기 전에 waitForElementVisible(‘body’, 5000)이라고 적어줍니다. body라는 태그가 보일 때 까지 최대 5000밀리세컨(5초)을 기다리라는 의미입니다.


1
2
3
4
5
6
7
8
9
/* starbucks_login.js */

module.exports = {
  "스타벅스 로그인" : function(browser) {
    browser
      .url(‘https://first.wifi.olleh.com/starbucks/index_new.html’)
      .waitForElementVisible('body'5000)
      .click('input[type=checkbox]')
 
  }
}
cs


이제 다시 돌려 보면 체크박스가 해제된 화면을 보실 수 있습니다.


start_page_checkbox.png

[체크박스가 해제된 화면]


이제 방금과 같은 작업을 반복하시면 됩니다.

  1. 클릭하거나 값을 변경해야 할 엘레먼트의 selector를 찾는다.

  2. 해당 selector를 클릭 하거나 값을 집어넣는다. (값을 집어넣는 명령은 setValue(‘selector’, ‘값’) 입니다.)

  3. 페이지가 변경되면 제대로 뜰 때까지 기다려준다.


아래는 완성본입니다. 이름과 이메일주소를 입력해주세요.


소스코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* starbucks_login.js */

module.exports = {
  '스타벅스 로그인' : function(browser){
    console.log(url)
    browser
      .url('https://first.wifi.olleh.com/starbucks/index_new.html')
      .waitForElementVisible('body'5000)
      .click('input[type=checkbox]')
      .click('.goWifi a img')
      .waitForElementVisible('body'5000)
      .setValue('input[name=userNm]''여기에 이름을 입력하세요!!!')
      .setValue('input[name=cust_email_addr]''여기에 이메일을 입력하세요~~')
      .click('input[id=agree1]')
      .click('input[id=agree2]')
      .click('.serviceButton1 > li:nth-child(2) img')
      .waitForElementVisible('body'5000)
      .end() //마지막에 꼭 종료를 시켜주세요.
  }
};
cs


실행 확인하기

다시 셀레늄 서버를 켜고, starbucks_login.js를 실행하면 와이파이에 자동으로 접속되는 것을 확인할 수 있습니다. 이 블로그를 스타벅스에서 보고 계시다면 이미 로그인을 하셨을테니 다음번에 실행을 시켜보셔야 합니다.


login_sucess.png

[와이파이 로그인에 성공한 화면]


배치파일 만들기

그런데 매번 이렇게 명령을 날려서 접속을 하느니 그냥 입력을 하는게 편할 것 같습니다. 한 걸음 더 편한 쪽으로 가보겠습니다. 배치파일을 만들겠습니다.


에디터를 열어서 starbucks_login.bat 라는 이름의 파일을 만듭니다. 아래의 내용을 넣고 starbucks 디렉토리에 저장합니다. (맥은 배쉬 쉘 스크립트를 참고하세요. Selenium이 자동 실행 되도록 하신 분들은 nightwatch starbucks.js만 입력하시면 됩니다.)


1
java -jar bin\selenium-server-standalone.3.3.1.jar | nightwatch starbucks.js
cs



batch.PNG


배치파일을 만들었다면 바탕화면에 바로가기를 복사합니다. 모양이 예쁘지 않습니다. 인터넷에서 스타벅스 이미지를 다운받아서 이미지를 ico 파일로 변환시켜주는 사이트에서 ico 파일을 얻어내서 starbucks 디렉토리에 저장합니다. 그런 다음 바탕화면에 있는 바로가기 파일을 우클릭해서 설정에 들어갑니다. 아이콘 변경하기를 누르고 ico 파일을 찾아서 선택해줍니다.


[완성]


이제 이 배치파일만 실행시키면 스타벅스 자동 로그인이 가능합니다.


마무리

자동 로그인을 하기 위해 험난한 길을 걸었습니다. 배보다 배꼽이 더 컸을지도 모르겠네요. 이제와서 얘기지만 더 간편하게 로그인을 도와주는 크롬 확장 프로그램도 있습니다. 하지만 일상에서 겪는 문제를 직접 해결하는 것도 재미있으니까요. 이제 여러분은 정말 강력한 도구를 얻었습니다. 그동안 귀찮게 여겼던 작업들을 Nightwatch.js를 이용해서 매크로로 만들어 보세요.




Posted by slowalk

이번 글에서는 아주 아주 간단한 슬랙봇을 만들어 보도록 하겠습니다.


봇(bot)이란? 봇은 인간이 하는 행동을 흉내내도록 만들어진 프로그램을 의미합니다. 예를 들어, 검색엔진의 웹 크롤러는 웹 사이트 정보를 자동으로 읽어들여 저장하는 프로그램이며, 이외에도 인터넷 채팅에서 자동 응답을 하도록 만든 채터봇 프로그램 등이 있습니다. (출처: 위키백과) 슬랙봇은 업무용 메신저인 슬랙(Slack)에서 위와 같은 역할을 할 수 있는 봇을 말합니다.


이번에 만들어 볼 봇은, 제가 하는 말을 그대로 두 번 따라 하는 앵무새 봇입니다.


내 말만 따라하는 봇을 워따 써!? 라고 생각하실 수 있겠지만, 내가 하는 말을 두 번 따라 하려면 세 가지 기능이 가능해야 합니다.


첫째, 내가 하는 말을 입력받을 수 있다.

둘째, 입력받은 말을 가공할 수 있다.

셋째, 가공한 말을 다시 나에게 출력할 수 있다.


이 세 가지 조건을 처리할 수 있다는 것은, 응용을 통해 다양한 봇을 만들 수 있는 기반이 될 수 있다는 뜻입니다.


자, 그럼 이제부터 봇을 직접 만들어 보도록 하겠습니다.


시작하기에 앞서

앞서, 말한 앵무새 봇은 일종의 챗봇이라고 할 수 있습니다. 메신저 상에서 사람을 대신해 특수한 기능을 수행하는 로봇의 일종이죠.



위 세 가지 조건을 모두 다 충족하지 못하셔도 일단 따라하기만 하면 만들 수 있도록 최대한 쉽게 작성해 보도록 하겠습니다.


하지만, 앞으로 다양한 봇을 만들어 보고 싶다면 언급된 기술들에 대해서 천천히 공부해 보시기 바랍니다.


웹서버 구현하기

챗봇을 만든다는 것은 사용자가 입력한 채팅 메시지를 분석 및 가공하고 이를 다시 사용자에게 출력할 수 있는 프로그램이 필요하다는 뜻입니다. 따라서 우리는 24시간 동작하며, 사용자의 요청(메시지)를 받아 처리할 수 있는 서버를 구현해야 합니다.

조금 더 엄밀히 말하자면, 슬랙봇을 만들기 위해서는 웹서버를 구현해야 합니다. 왜냐하면, 슬랙이 지원하는 모든 API가 웹기반이기 때문입니다. 다른 대부분의 서비스도 웹기반의 API를 제공합니다.


웹서버를 만들 수 있는 방법은 매우 많지만, 이번 글에서는 요즘 핫하고 힙하다는 개발언어인 파이썬(Python)을 통해서 구현해 볼까 합니다. 웹기반이라는 자체가 표준을 통해 구현한다는 뜻이므로, 내용만 이해한다면 다른 언어로도 충분히 구현하실 수 있습니다.


일단 파이썬과 코딩을 도와줄 IDE인 파이참(PyCharm)을 다음의 링크를 참고하여 설치하시기 바랍니다.(파이썬은 3.6.x 버전을 사용하며, 파이참 커뮤니티 버전은 무료입니다.)



파이썬으로 웹서버를 처음부터 직접 구현할 수도 있지만, 인간은 도구를 쓰는 존재이므로, 이미 만들어져 있는 도구(라이브러리)를 가져다 쓰기로 하겠습니다.


파이썬의 대표적인 웹프레임워크로는 장고(Django)와 플라스크(Flask)가 있으며, 이번 웹서버를 만들어줄 도구는 플라스크를 사용하도록 하겠습니다. 나중에 기회가 되신다면 장고로 서버를 한번 구현해 보시기 바랍니다.


파이썬과 파이참의 설치가 완료 되었다면, pip 명령어를 통해 플라스크를 설치해보도록 하겠습니다. 아래의 명령어를 콘솔(윈도우) 또는 터미널()에 입력해 주세요.

$ sudo pip install Flask # 윈도우 사용자라면 sudo는 입력하지 않아도 됩니다.


만약, 윈도우 사용자이며 콘솔창에 python이나 pip를 입력해도 아무 반응이 없다면 Path 환경변수를 설정해 주셔야 합니다.
Path 값의 맨 마지막 줄에
;파이썬_설치_경로\Python36-32;파이썬_설치_경로\Python36-32\Scripts;


플라스크의 설치가 완료된 후, 파이참을 실행합니다.

파이참을 처음 설치하신 분은 프로젝트를 생성하여야 하는데, 원하시는 프로젝트명으로 프로젝트를 하나 만듭니다. 저는 그냥 기본값(untitled)으로 하였습니다.


해당 프로젝트 밑에 slack_server.py 파일을 하나 생성하고 다음의 코드를 붙여넣기 합니다.

from flask import Flask

app = Flask(__name__)


@app.route("/")

def hello():

  return "Hello World!"


if __name__ == "__main__":

  app.run(host=’0.0.0.0’, port=5000)

그리고 Ctrl + Shift + F10 으로 코드를 실행하고, 웹 브라우저를 띄워서 http://localhost:5000 에 접속합니다.



“Hello World!”를 보셨나요? 수고하셨습니다. 방금 웹서버 하나를 만들었습니다!

슬랙 웹훅 이해하기

자! 이제 웹서버도 준비 되었겠다, 슬랙에서 데이터를 넘겨받고 이를 가공해서 다시 슬랙으로 돌려 보내주면 우리의 앵무새 봇이 완성됩니다!


슬랙은 특정 이벤트가 발생하면 해당 이벤트를 나에게 넘겨 주거나, 또는 나의 웹서버에서 발생한 이벤트를 슬랙으로 전달하는 다양한 방법을 제공합니다. 이러한 다양한 방법 중, 우리는 가장 쉽고 간단한 방법인 아웃고잉 웹훅(Outgoing Webhook)을 사용할 것입니다.

아웃고잉 웹훅은 [슬랙] -> [사용자의 웹서버]로 데이터를 전달하는 역할을 합니다.

그럼 이제부터 아웃고잉 웹훅을 만들어 보도록 하겠습니다. 먼저, 아래의 주소로 접속해 주시기 바랍니다.

https://{your-team-url}.slack.com/apps/manage/custom-integrations

{your-team-url}은 내가 봇을 붙이고 싶은 채널을 가진 team으로 명시하시기 바랍니다.



위와 같은 화면이 보이셨나요? 그렇다면 화면의 “Outgoing WebHooks”를 클릭해 주세요.

그리고 “Add Configuration”과 “Add Outgoing WebHooks integration” 버튼을 클릭 합니다.

마지막으로 최종 설정 화면이 나타나게 되는데, 이 화면에서 우리가 설정해야 할 값은 아래와 같습니다.

Channel

어떤 채널에서 발생하는 메시지를 수신할 것인가?

전체(Any) 또는 하나의 채널만 지정할 수 있습니다.


Trigger Word(s)

어떤 단어로 시작하는 메시지만 수신할 것인가?

Channel을 Any로 선택하셨다면 필수사항 입니다. 여러 단어를 콤마로 구분하여 등록할 수 있습니다.


URL(s)

선택한 Channel에서 메시지가 발생하면 어느 URL로 해당 메시지를 전달해 줄 것인가?

URL을 라인으로 구분해서 여러 개 등록할 수 있습니다.

앞서 우리가 구현한 웹서버 주소를 입력하면 됩니다.

(자세한 내용은 아래에서 설명하겠습니다)

Customized Name

봇 이름

Customize Icon

봇 아이콘


저는 iconfinder.com에서 앵무새 아이콘을 하나 찾아서 적용해 보았습니다.


마지막으로 “Save Settings” 클릭!

웹서버 더 자세히 구현하기

저는 앞서 만든 웹서버를 자체 서버에 올려두었습니다.

자체 서버의 아이피 주소는 192.168.0.8 인데요. 이 주소로 슬랙은 데이터를 넘겨줄 수 없습니다. 왜냐!? 내부에서만 사용되는 사설 아이피이기 때문이죠. 그래서 공유기를 찾아서 공유기에 할당된 공인 아이피를 확인하고 포트포워딩을 설정하였습니다.

내가 쓰는 공유기가 iptime 이라면 여기를 참고하시기 바랍니다.


공유기에서 확인한 공인 아이피 주소: 1.215.xxx.70

자체 서버 아이피 주소: 192.168.0.8

웹서버가 돌아가는 포트 번호: 5000번


1.215.xxx.70:5000 -> 192.168.0.8:5000 으로 포트포워딩 하였습니다.


이렇게 설정하고 나면 1.215.xxx.70에 5000번 포트로 들어오는 데이터는 내부 아이피 192.168.0.8번의 5000번 포트로 전송되게 됩니다.

위와 같은 설정은 일반적으로 카페24나 AWS에서 서버를 운영할 경우 필요없는 설정입니다. 왜냐하면 각 서버에 바로 공인 아이피가 할당되기 때문입니다.


자, 그럼 위에서 아웃고잉 웹훅을 설정할때 URL(s) 기억나시나요?

해당 칸에 공인아이피 + 웹서버 포트번호(http://1.215.xxx.70:5000)를 적어주도록 합시다.


이제 슬랙으로부터 내가 지정한 채널에서 메시지가 발생 되었을 경우, 해당 메시지를 받을 준비가 완료되었습니다.


슬랙은 채널에서 메시지가 발생하면 해당 이벤트를 나의 웹서버에 POST 메소드로 전송 해줍니다. 따라서 우리의 웹서버도 POST 메소드를 수신할 수 있도록 변경해줘야 합니다.


아래와 같이 slack_server.py 코드를 수정합니다.

from flask import Flask, request

app = Flask(__name__)


@app.route("/", methods=['GET', 'POST'])

def hello():

  print(request.form['text'])

  return "Hello World!"


if __name__ == "__main__":

  app.run(host='0.0.0.0', port=5000)


코드를 보시면 @app.route("/", methods=[‘GET’, ‘POST’])가 추가되었습니다.

GET 그리고 POST메소드를 http://서버주소:5000/ 경로에 허용하겠다는 뜻입니다.


그리고 request.form[‘text’]라는 녀석이 등장했는데요. 슬랙에서는 데이터를 다음과 같은 형태로 넘겨줍니다.

token=Eqn4cHo1NM44rAvMQxK26pgH&team_id=T0433FX78&team_domain=slowalk&

service_id=165336103011&channel_id=C3FJCN6CS&channel_name=test&

timestamp=1491540739.372491&user_id=U1D5UGLEB&user_name=hakjin&text=hello


이중 우리가 필요한 데이터는 text 값(사용자가 입력한 채팅 메시지)이고, 이를 flask의 request.form을 이용해서 추출하고 화면에 출력해 본 것입니다.


이제 고지가 눈앞에 보입니다. 이제 text값을 복사해서 두번 반복되게 만들고 이를 슬랙으로 보내면 앵무새봇이 완성되겠습니다.


그럼, 슬랙으로 데이터는 어떻게 보낼까요?


제가 선택한 채널에서 메시지가 발생하면 슬랙은 저희 서버로 해당 이벤트를 POST 메소드를 사용해 요청합니다. 그리고 모든 HTTP 요청에는 적당한 응답을 주어야 합니다. 이 응답값을 특정한 포맷으로 맞춰서 주면 선택한 채널로 메시지가 표시되게 됩니다. (이것은 아웃고잉 웹훅을 사용했을 경우 입니다.)


여기서 말한 특정한 포맷은 아웃고잉 웹훅 설정화면에 잘 나타나 있는데요. 아래와 같은 JSON 형태의 값 입니다.

{

"text" : "안녕하세요?"

}


위와 같은 포맷으로 응답까지 수행하는 slack_server.py 서버 코드는 다음과 같습니다.

from flask import Flask, request, Response

app = Flask(__name__)


@app.route("/", methods=['GET', 'POST'])

def hello():

  if request.form['user_id'] != 'USLACKBOT':

      text = '{{"text":"{} {}"}}'.format(request.form['text'], request.form['text'])

      resp = Response(response=text, status=200, mimetype="application/json")

      return resp

  else:

      return "ok"


if __name__ == "__main__":

  app.run(host='0.0.0.0', port=5000)


일단 코드에 if 문이 추가되었는데요. user_id가 USLACKBOT이 아닐때만 응답하도록 코드를 수정했습니다. 이유가 궁금하신 분은 if문을 제거하고 돌려보세요. 그리고 Flask의 Response를 사용해서 JSON 포맷으로 응답하도록 수정되었습니다.


마치며

여기까지 앵무새봇을 아주 간단하게 만들어 보았습니다. 앵무새봇은 슬랙의 아웃고잉 웹훅으로만 구현되었습니다. 따라서 다른 여러 채널에 적용하기에는 범용성이 떨어지는 점과 사람이 먼저 말을 해야 반응을 보이는 단점이 있습니다. 여러분은 이러한 단점을 보완하기 위해 슬랙에서 제공하는 다양한 API를 활용해서 봇을 생성해 보시기 바랍니다.



더 읽기

슬로워크의 식스맨, mrslo 봇 개발기

슬랙봇으로 업무 커뮤니케이션 즐겁게 하기



Posted by slowalk


세상에 온전하게 혼자 만든 물건은 매우 드뭅니다. (풀스택이라는 개념도 있지만) 웹서비스 역시 여러 사람의 협업으로 만듭니다. 슬로워크에서 운영하는 이메일마케팅 서비스 스티비도 예외는 아닙니다. 살짝 말씀드리면 스티비는 기획/PM 1명, 디자이너 1명, 개발자 2명이 만들고 있습니다. 큰 조직은 아니지만 소통의 틈은 늘 존재하기 마련입니다. 


그중 하나가 UI 용어입니다. 동상이몽이라는 말처럼 각자 웹서비스 개발을 해왔지만, 모두가 같은 상황과 맥락에서 학습한 것이 아니고, 머릿속에 그리는 이미지가 달라 사용하는 용어가 서로 다를 수 있습니다. 그리고 같은 용어를 사용하면서도 그 의미와 구현된 결과물이 다를 수 있습니다. 


“‘드롭다운'이 들어가야 해요"라고 요청받고 나온 결과물은 ‘버튼을 클릭하면 아래로 펼쳐지는 메뉴'일 수 있습니다. 하지만 요청한 사람이 실제로 원했던 것은 <select>일 수 있다는 것이죠. 이런 소통의 틈을 채우기 위해 우리는 장문의 기획서를 쓰고 시간과 공을 들여 프로토타이핑을 합니다. 시간과 인력 자원이 허락된다면 아주 좋은 과정입니다. 하지만 자원이 적은 스타트업 팀에게는 부담스러울 수 있습니다. 모든 것이 비용이죠. 그저 “‘드롭다운'은 아래로 펼쳐지는 메뉴이고, 옵션 선택을 위해서는 셀렉트(<select>)를 쓰자"고 미리 약속하면 많은 부분이 해결됩니다. 그래서 UI 용어 통일은 중요합니다.



이런 것이 헷갈리고, 이렇게 씁니다. 


몇 가지 사례를 살펴보겠습니다. 서비스를 2년 가까이 만들어 오면서 헷갈렸던 용어와 서로 약속을 통해 바로 잡은 것들, 그리고 아직도 헷갈리는 것들이 섞여 있습니다. 그리고 다른 팀에서 사용하는 의미와 또는 웹표준과 다를 수 있습니다. 그저 “스티비는 이렇게 쓰는구나"하고 봐주시면 되겠습니다.



1. 버튼(button)


사용자의 클릭을 끌어내는 버튼. 마우스와 키보드를 가지고 할 수 있는 많은 액션이 있지만 무언가를 클릭하는 것만큼 직관적이고 친숙한 UX는 없을 것입니다. 그 중심에 버튼이 있습니다. 어떤 때는 이동을, 어떤 때는 실행이나 취소를 위해 버튼을 클릭합니다. 


버튼의 개념과 역할은 아주 명확한 것처럼 보이지만 프론트엔드 개발자 입장에서는 때로 ‘링크'와 혼동될 때가 있습니다. 어떤 것은 <a>로 만들어진 링크로 만들어야 하고, 어떤 것은 <button>으로, 또 어떤 때는 <input type=”submit”>처럼 하기도 합니다. 하지만 표현되는 결과물은 마우스를 올리면 색이 변하는 ‘버튼'이죠. 보통 <a>는 페이지의 이동을 나타내고, <button>은 실행이나 취소, <input type=”submit”>은 양식의 전송을 말합니다. 


스티비에서는 ‘버튼', ‘링크', ‘링크 버튼'을 혼용해서 사용합니다. 결과물은 버튼이지만 개발자의 재량에 따라 어떤 방식으로 구현할지 정합니다. 위 용어들에 대한 추가 질문은 따로 하지 않습니다. 그리고 SPA 방식으로 개발된 탓에 실제로 구분이 명확히 되지 않는 경우가 있습니다. 


* 이렇게 씁니다.

→ “개발자가 알아서 한다"



2. 팝업(popup)과 모달(modal) 


다음으로 헷갈리는 것이 팝업과 모달입니다. 과거 ‘팝업’은 작은 새로운 윈도우를 띄우는 기능을 말했습니다. 최근 팝업 차단이나 모던 브라우저들의 다중탭 기능 덕분에 많이 사용하지 않는 기능이 되었습니다만 아직도 많이 사용되는 용어입니다. 그리고 모달은 비교적 최근에 등장한 개념으로 화면 위에 레이어를 덮어 마치 새로운 창이 나타나는 것처럼 보여주는 방식입니다. 


“이 부분은 모달로 해주시고요.", “다음 페이지는 역시 같은 팝업에서 이동하는 것으로...”. 이처럼 초기에는 위 용어를 혼재하여 사용했습니다. 새로운 윈도우를 띄우는 상황은 없거나 매우 희박하므로 소통에 큰 문제는 없었습니다. 다만 모달은 ‘기존(부모) 페이지와 맥락을 달리하는...”이라는 함의를 가지고 있습니다. 이런 경우는 되도록 ‘모달'이라는 용어로 통일하려고 합니다. 


* 이렇게 씁니다. 

→ 팝업/모달은 중에 하나를 선택하지는 않지만 열리는 상황과 맥락에 따라 용어를 구분하면 좋다. 구현은 하나의 통일된 템플릿으로 진행한다. 

 


3. 얼럿(alert)



‘얼럿’은 사용자가 무언가 잘못된 길로 갔을 때, “띵"하고 뜨는 그 경고창입니다. 과거에는 브라우저에 내장된 기본 기능을 많이 사용했지만, 디자인과 사용성을 위해 최근에는 디자인이 입혀진 레이어로 구현된 유사 얼럿이나 하단에 위치한 토스트얼럿UI 등 다양한 변형이 사용되고 있습니다. “사용자가 취소하려고 하면 이런 메시지로 경고를 해주세요"라는 요청을 받는다면 개발자는 이것을 단순히 alert()으로 처리할지 상단에 뜨는 예쁜 레이어로 띄웠다가 일정 시간이 지나면 없앨지, 하단에 커다랗게 보여줄지 고민이 됩니다. 앞서 살펴본 모달 형식의 경고도 있으니 혼란은 커집니다. 


대부분 서비스가 그렇겠지만 스티비는 미리 설계된 얼럿 디자인을 사용합니다. 보통의 경우 당연히 이 UI를 사용하고, 추가 액션이 필요하거나 화면의 가운데 모달 형식으로 보여줘야 할 경우라면 디자인 작업물에 명시합니다. 화면에 붉은 글씨로 보여주는 경우도 있어 이 부분은 대부분 디자인 결과물로 소통합니다.


* 이렇게 씁니다.

→ 디자이너가 각 상황과 맥락을 파악하며 적당한 경고 방식을 선택, 디자인 작업물에 배치하여 개발팀에 전달합니다. (디자인 결과물은 제플린으로 전달합니다)



4. 드롭다운(dropdown)과 셀렉트(select)



결과부터 말씀드리면 ‘드롭다운'과 ‘셀렉트'는 다른 UI입니다. 하지만 비슷한 역할을 하는 경우가 있어 혼용하여 사용되는 것 같습니다. ‘드롭다운'은 하위 메뉴가 숨겨져 있다가 사용자의 마우스 오버나 클릭에 숨겨진 메뉴를 보여주는 UI입니다. 셀렉트는 <select>태그로 구현되며 사용자에게 내재된 옵션값 중 하나(또는 여러 개)를 받기 위한 양식 UI입니다. 


예쁜 디자인을 위해 레이어로 구현된 드롭다운처럼 구현한 셀렉트도 있고, 셀렉트인데 옵션의 선택에서 그치는 것이 아니라 선택과 동시에 페이지가 이동된다든지 하는 액션을 가진 경우가 있어 혼란이 생긴 것으로 생각됩니다. 


* 이렇게 씁니다.

→  초기 기획 단계에서 이 둘은 명확히 구분합니다. 사용자에게 어떤 값의 입력(선택)을 요구하기 위해서는 셀렉트를 사용합니다. 이때 디자인은 변형될 수 있지만, 선택이라는 핵심 기능은 그대로 둡니다.

버튼 뒤에 숨겨진 메뉴를 표현하기 위해서는 드롭다운을 사용합니다. 하위 메뉴에서 어떤 액션이 있어야 한다면 드롭다운으로 합니다. 구현은 기획에 맞추어 진행합니다.



5. 인풋(input)


‘인풋’, ‘입력창', ‘필드' 등 여러 이름으로 불리웁니다. 사용자에게 텍스트 형식으로 어떤 내용을 입력받기 위한 UI로 보통은 그냥 사각형이고, 여기에 테두리(border)나 옅은 배경(background)를 주어 사용합니다. 


딱히 헷갈릴 일이 없긴합니다. 하지만 뭔가 용어 통일을 한다면? 아마도 ‘텍스트 입력'이나 ‘텍스트 인풋'이 적당하지 않을까 합니다. 결과물은 입력을 위한 상자이지만 구현은 보통 <input>태그로 합니다. 하지만 단순히 인풋이라고만은 할 수 없는 것이 <input type=”checkbox”>나 <input type=”radio”>, <input type=”submit”> 같은 예외가 있기 때문입니다. “인풋으로 해주세요", “인풋 중에 뭐요?”같은 상황이 생길 수 있습니다. 그렇다고 ‘텍스트 입력'이라고 한다면 <textarea>와 혼동할 수 있습니다. 구현 과정을 생각하여 되도록 명확한 용어가 사용되는 편이 좋습니다.


* 이렇게 씁니다. 

→ 무엇을 입력할지 디테일한 전달 필요. 용어 통일은 조금 더 논의해 본다.



마치며 

쓰임에 따라, 상황에 따라 다를 수 있는 UI 관련 용어들. 각자 편한 대로 쓰면 되지 왜 꼭 통일해야 할까요? 오히려 하나의 단어로 통일하는 순간 그 단어만 제한되는 것은 아닐까요? 개발 조직마다 다르겠지만, 스타트업이나 스타트업처럼 작고 빨라야 하는 조직에서의 팀원 사이의 이런 작은 ‘싱크'들은 매우 중요합니다. 드롭다운을 열심히 그렸는데, 실제로 필요한 건 셀렉트였다면? 이렇게 소통이 어긋났을 때 발생하는 시간과 자원의 낭비가 줄어듭니다. 세세한 UI까지 디자이너가 그리는 시간을 줄일 수 있습니다. 미리 약속된 UI(일종의 스타일 가이드)가 있다면 개발자는 상세 디자인 없이도 기존 것을 재사용하면 되기 때문입니다. UI 용어의 싱크만 잘해도 많은 시간을 절약할 수 있습니다. 그 시간에 더 많은 아이디어를 실험하고 구현해볼 수 있습니다. 



Posted by slowalk