프로그래밍을 할 때도 다르지 않다.
아래 글은 꽤 오래전 페이스북에 짤막하게 남긴 글이다.
난 개발하면서 이름 지을 때 가장 많은 고민을 한다.
변수 이름, 메쏘드 이름, 클래스 이름, 모듈 이름….
이름을 잘 지어야 그것의 역할이 명확해지고 정체성도 분명해진다.
정체성이 분명한 객체들끼리 모여서 자기 역할에 집중하며 서로 상호작용할때 제대로 된 OOP가 구현된다.
이름을 제대로 지어놓지 않으면 처음 생각과는 다르게 덕지덕지 무언가가 붙게되고, 결국 형체도 없는 괴물이 되어간다.
난 내 이름(정체성)에 맞게 살아가고 있나?
개발자의 길을 걸어온지 이제 만 8년이 다 되어가는데, 이 고민은 아마 코딩을 손에서 놓을때까지 계속되지 않을까?
그런 의미에서 오늘은 “효과적인 이름짓기”라는 제목으로 포스팅을 해 보려고 한다. 오랫동안 책장에 꽂혀있던 Code Complete 2 책을 꺼내 “네이밍” 관련 부분에 대해 다시 찾아보았고 그 내용을 정리해보았다. “네이밍”에 관련돼 꽤 많은 내용들이 있었지만, 그 중에서도 꼭 기억했으면 하는 부분들에 대해서만 이야기를 해 보겠다.
먼저 아래 두 코드를 비교해 보자.
1 2 3 4 5 |
|
1 2 3 4 5 |
|
회사에서 일을 하다보면 종종 다른 사람들이 작성한 코드를 보게 되는데, 놀랍게도 첫번째 예제와 같은 코드를 자주 만나게 된다. 이러한 코드를 이해하려면 앞뒤전후의 코드들을 샅샅히 살펴봐야 하고, 실제 동작하는 것을 디버깅까지 해봐야 이 코드가 무엇을 의도하는지 알 수 있다. 이 얼마나 시간 낭비, 에너지 낭비인가? 오늘도 수많은 개발자들이 x1, xx, xxx가 도대체 무엇인지 알아내기 위해, 모든 에너지와 시간을 쏟고나서 힘없이 퇴근하는 하루하루를 보내고 있을 것이다.
변수의 이름을 짓는데 있어서 가장 중요한 고려사항은,
변수 이름이 변수가 표현하고 있는 것을 완벽하고 정확하게 설명해야 한다
는 것이다.
현재의 이자율을 표현하고 싶으면 r이나 x보다 rate나 interestRate가 더 좋은 이름이다.
이름은 가능한 구체적이어야 한다. 모호하거나 하나 이상의 목적으로 사용될 수 있는 일반적인 이름은 보통 나쁜 이름이다.
(x, temp, i와 같은 이름은 적절한 정보를 제공해 주지 않는다.)
다음의 예를 보자.
당연히 첫번째 변수의 의미는 ‘미국 올림픽 대표팀에 있는 선수의 수’이고, 두번째 변수의 의미는 ‘운동장의 좌석 수’이다. 세번째는 ‘올림픽에서 각 나라의 대표팀에서 획득한 최고 점수’를 나타내고 있다. 이러한 이름들은 판독하기가 쉽다. 실제로 이 변수 이름은 쉽게 읽을 수 있기 때문에 판독할 필요가 전혀 없다. 하지만 너무 길어서 실용적이지 않다.
일반적으로 변수 이름의 길이가 평균적으로 10~16일 때 프로그램을 디버깅하기 위해서 들이는 노력을 최소화 할 수 있고, 변수의 평균 길이가 8~20인 프로그램은 디버깅하기가 쉽다
. 모든 변수의 이름을 10~16의 길이로 작성하기 위해 애쓸 필요는 없겠지만, 코드에 짧은 이름의 변수, 또는 굉장히 긴 이름의 변수를 보게 된다면, 그 이름이 적절한지 확인해 보아야 한다.
그렇다면, 짧은 변수 이름이 항상 나쁜가? 항상 그렇지는 않다. i와 같이 짧은 이름의 변수가 있다면, 이 변수는 제한된 범위를 갖는 연산에서 사용되는 임시 값일 것이다. i라는 이름의 변수가 등장한다면 이 코드는 이렇게 얘기하고 있다. “이 변수는 일반적인 루프 카운터이거나 배열 인덱스이며, 몇 줄의 코드 외부에서는 전혀 중요하지 않다.”
하지만 짧은 이름은 문제를 야기할 수 있기 때문에, 주의 깊은 프로그래머들은 방어적인 프로그래밍 정책으로 그것들을 피한다.
많은 프로그램들은 계산된 값(총계, 평균, 최대값 등)을 보관하는 변수들을 갖는다. 만약 변수의 이름에 Total, Sub, Average, Max, Min, Record, String, Pointer 등의 한정자를 사용해야 한다면, 이름의 끝이 이런 수정자를 입력하는 것이 좋다.
다음의 예를 보자
좋은 예
revenueTotal
expenseTotal
revenueAverage
expenseAverage
나쁜 예
totalRevenue
expenseTotal
revenueAverage
averageExpense
단 예외적인 경우가 있는데, Num 한정자의 관습적인 위치이다.
변수의 이름 앞에 있는 Num은 총계를 가르킨다(numCustomer: 전체 고객의 수).
변수의 이름 끝에 있는 Num은 인덱스를 가르킨다(customerNum: 특정 고객의 번호).
이런 혼란을 피하기 위해 Num이라는 단어를 피하고 customerCount(전체 고객의 수), customerIndex(특정 고객의 번호)와 같은 이름을 쓰는 것이 좋다.
다음은 효과적인 루틴 이름을 만들기 위한 지침이다.
1. 루틴이 하는 모든 것을 표현하라
ComputeReportTotals()는 무엇을 하는지 모호하기 때문에 적절한 이름이 아니다. ComputeReportTotalAndOpenOutputFile()은 적절한 이름이겠지만, 너무 길고 우스꽝스럽다. 이에 대한 해결책은 부수적인 효과를 갖기보다는 직접적인 효과를 유발시키도록 프로그램을 작성하고 그에 맞는 새로운 이름을 짓는 것이다.
2. 의미가 없거나 모호하거나 뚜렷한 특징이 없는 동사들을 피하라.
HandleCalculation(), PerformServices(), OutputUser(), ProcessInput(), DealWithOutput()과 같은 이름들은 그 루틴이 무엇을 하는지 말해 주지 않는다. 실제로 루틴은 잘 설계되었지만, 루틴의 이름에 뚜렷한 특징이 없기 때문에 문제가 되기도 한다. 만약 HandleOutput()이 FormatAndPrintOutput()으로 대체된다면, 루틴이 무엇을 하는지 상당히 정확하게 이해할 수 있다.
때론 루틴이 처리하는 연산 자체가 모호하기 때문에 이름이 모호해지는 경우도 있다. 루틴의 목적이 취약하다는 것이 문제이고, 서투른 이름은 그로인한 증상이다. 이런 경우 해당 루틴을 적절하게 리팩토링하여 명확한 처리를 하도록 해야 한다.(리팩토링에 대해서는 다음에 포스팅을 하도록 하겠다.)
3. 루틴 이름을 숫자만으로 구분하지 말라.
Part1(), Part2() 또는 OutputUser1(), OutputUser2()와 같은 이름은 좋은 이름은 잘못된 이름이다. 루틴의 이름 끝에 있는 숫자는 루틴이 표현하는 서로 다른 추상화에 대해서 아무런 정보도 제공하지 않는다.
4. 함수의 이름을 지을 때, 리턴 값에 대한 설명을 사용하라.
리턴 값을 따서 이름을 작성하는 것은 좋은 방법이다. cos(), customerId.Next(), print.IsReady(), pen.CurrentColor()는 함수가 리턴하는 것을 정확하게 보여주고 있기 때문에 모두 좋은 이름이다.
5. 프로시저의 이름을 지을 때, 확실한 의미를 갖는 동사 다음에 객체를 사용하라.
프로시저의 이름은 프로시저가 무엇을 하는지를 반영해야 하기 때문에, 객체에 대한 연산은 동사+객체 형태의 이름을 갖는다. PrintDocument(), CalcMonthlyRevenues(), CheckOrderInfo(), RepaginateDocument()는 모두 좋은 프로시저 이름이다.
6. 공통적인 연산을 위한 규약을 만들어라.
1 2 3 4 |
|
위 코드는 모두 특정 객체의 식별자를 얻기 위한 코드이다. Employee 클래스는 자신의 id 객체를 노출하고, id 객체는 Get() 루틴을 노출했으며, Dependent 클래스는 GetId() 루틴을 노출했다. Supervisor 클래스는 id를 기본 리턴캆으로 만들었고, Candidate 클래스는 id 객체의 기본 리턴 값이 id라는 사실을 이용하여 id 객체를 노출시켰다.
id를 가져오는 이름 규칙이 있었다면 이러한 난잡한 코드가 생성되는 현상은 막을 수 있을 것이다.
루프에 있는 변수의 이름을 지을 때, i,j,k와 같은 이름들은 관습적으로 사용된다.
1 2 3 4 |
|
만약 변수가 루프 외부에서 사용되어야 한다면, 반드시 i,j,k 보다는 좀 더 의미 있는 이름을 제공해야 한다.
1 2 3 4 5 6 7 8 9 |
|
만약 루프가 길어진다면, i가 무엇을 나타내는지 쉽게 잊게 되기 때문에, 루프의 인덱스에 좀 더 의미 있는 이름을 제공하는 것이 좋을 것이다. 코드는 자주 변경되고 확장되고 다른 프로그램에 복사되기 때문에, 많은 숙련된 프로그래머들은 i와 같은 이름들은 피한다. 루프가 길어지는 한 가지 이유는 루프가 중첩되기 때문이다. 만약 여러 개의 중첩된 루프가 있다면, 가독성을 향상시키기 위해서 좀 더 긴 이름으로 루프 변수들을 작성한다.
1 2 3 4 5 6 |
|
score[ teamIndex ][ eventIndex ]는 scores[ i ][ j ] 보다 많은 정보를 제공한다.
상태 변수에 대해서 flag보다 더 나은 이름을 생각한다.
‘flag’는 변수 이름에 사용되지 않아야 한다. 왜나하면 ‘flag’라는 이름은 플래그가 무엇을 하는지 아무런 단서도 제공하지 않기 때문이다.
다음의 두 코드를 비교해보자.
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 10 |
|
임시 변수는 계산의 중간 결과를 보관하기 위한 임시 저장소로 사용되고 보조 수단으로 사용되는 값을 보관하기 위해 사용된다. 일반적으로 temp, x 또는 그 밖의 모호하고 설명적이지 않은 이름으로 만들어진다. 일반적으로, 임시 변수는 프로그래머가 프로그램을 완벽하게 이해하지 못하고 있다는 신호이다. 게다가 변수가 공식적으로 ‘임시’상태이기 때문에, 프로그래머는 임시 변수를 다른 변수들보다 별 생각 없이 다루게 되어 오류가 발생할 가능성이 높아진다.
1 2 3 4 5 |
|
temp라는 이름은 변수가 무엇을 하는지에 대해서 아무런 정보도 제공하지 않는다. 보다 나은 접근 방법은 다음 예제와 같다.
1 2 3 4 5 |
|
1. 전형적인 불린 변수의 이름을 기억한다.
2. 참이나 거짓의 의미를 함축하는 불린 변수의 이름을 사용한다.
status, sourceFile 같은 변수들은 참이나 거짓이 명백하지 않기 때문에 좋지 못한 불린 이름이다.
statusOK, sourceFileAvailable 또는 sourceFileFound와 같은 이름으로 대체해야 한다.
3. 긍정적인 불린 변수 이름을 사용한다
notFound, nonDone, notSuccessful과 같은 부정적인 이름은 이 변수의 값이 부정이 되었을 때 읽기가 어려워진다.
1
|
|
위와 같은 코드를 읽을 때는 한번 더 생각을 해야 한다. if(found == true) 가 훨씬 더 자연스럽다.
이런 이름은 반드시 found, done, success로 대체되어야 한다.
마지막으로 위 내용을 점검 할 수 있는 체크리스트를 적어보았다.
글이 꽤 길어졌다. 네이밍에 관련된 많은 지침들이 있지만, 내용이 너무 많으면 까먹을 것 같아서 반드시 지켜야 할 것이라고 생각되는 것들에 대해서만 이야기 해 보았다. 마지막으로 중요한 기억해야 할 사실은, 코드는 작성되는 것보다 훨씬 많이 읽혀진다. 코드 작성의 편의성 보다는 코드의 가독성에 좀 더 중점을 두는 습관을 들이도록 하자.
역자의 말 : 리치 자바스크립트 프레임워크가 모든 웹사이트에서 적용해야 하는 것으로 착각할 수 있지만 나름 이것들이 가지는 문제들이 여러가지로 지적되고 있다. 그러나 원인을 알면 문제를 해결할 수 있듯이, 아마도 멀지 않은 미래에 이러한 문제점이 하나 둘 해결되리라 기대하면서 번역해서 공유한다.
이렇게 많은 어려움이 예상되었지만, 실제로 얼마나 힘들 지는 몰랐다.
검색 엔진 크롤러(crawler)와 SNS 프리뷰 스크레이퍼(scraper)는 자바스크립트만 가능한 웹사이트를 로드할 수 없고 이를 위해서 대체 버전을 서비스하는 것은 복잡하고 속도가 늦다.
크롤러가 웹사이트를 읽을 수 있도록 하는데는 2가지 방법이 있다.
서버에서 브라우저 인스턴스를 만들고 이 브라우저 상에 어플리케이션의 자바스크립트를 실행해서 DOM객체로 부터 만들어지는 HTML을 덤프(PhantomJS 또는 WebLoop를 이용해서)할 수 있다. 또는 크롤러를 위해서 웹사이트 상에 서버에서 생성한는 다른 HTML 버전을 만들 수 있다.
전자의 경우는 각 페이지를 로드하기 위해 헤드가 없는(headless) 브라우저나 탭(tab)을 spawn(자식 프로세스를 만들어 실행하는 것)해야 하는데, 이것은 HTML만을 만드는 것보다 훨씬 많은 시간과 시스템 자원을 필요로 한다. 사용하는 프레임워크에 따라, 페이지를 렌더링해서 덤프해야 하는 시점을 결정하는데 또한약간의 작업이 더 소요된다. 페이지를 캐시할 수 있지만, 이것은 최적화를 위한 조치에 불과하고, 페이지가 자주 변경될 경우에는 더 복잡하게 구현해야 한다. 이것은 몇 초정도 페이지 로드 시간을 느리게 하여 검색 엔진의 순위에 악영향 을 미친다. (이 문장은 PhantomJS가 Xvfb와 WebKit를 필요로했다고 잘 못 언급했었다.)
간단한 사이트일 경우에는 (대체 서버사이드 사이트를 만드는) 후자의 방법만으로도 충분하지만, 수 많은 종류의 페이지가 있는 경우에는 악몽이다. 그리고 구글이 대체 사이트를 메인 사이트와 심하게 차이가 있다고 여기면 심한 벌칙을 부과할 것이다. 웹사이트의 트래픽이 급격하게 감소할 때까지는 선을 넘어 선 것을 알지 못 할 것이다.
대부분의 분석도구들은, 페이지 이동을 위해 HTML5 history API(pushState)를 사용할 때는, 에러가 쉽게 발생하기 쉬운 수작업으로 통합을 해야 필요가 있다. 이것은 어플리케이션이 pushState를 이용해서 새로운 페이지로 언제 이동해야 하는지를 자동으로 감지할 수 없기 때문이다. 설상 감지를 할 수 있다고 해도, 새로운 페이지에 대한 기타 정보(페이지 타이틀과 추적할 필요가 있는 기타 다른 페이지특이 지표)를 수집하기 위해서 어플리케이션으로부터의 신호를 여전히 기다릴 필요가 있을 것이다.
이러한 문제를 어떻게 수정해야 할까? 해결방안은 클라이언트측 라우팅 라이브러리와 통합하려는 특정 분석툴 둘 다에 달려 있다. Backbone.js와 함께 구글 분석기(Google Analytics)를 사용한다면? backbone.analytics를 시도해 보기 바란다. UI-Router와 함께 Heap을 사용한다면? 자신만의 $stateChangeSuccess 후크를 설정한 후 heap.track을 호출하기 바란다.
아직 아무런 조치를 취하지 않은 상태다. 최초 페이지 로딩을 추적할 것인가? 혹시 그것을 이중으로 추적할 것인가? 페이지 로드 실패를 추적할 것인가? pushState 대신에 replaceState를 사용한다면 어떨까? 분석기 후크를 잘 못 설정했다는 것을 아는 것 조차 힘들다. 또는 의존성(dependency)을 업그레이드했을 때, 분석기를 자주 상호 점검하지 않는다면, 후크들이 깨질 것인지를 아는 것조차 어렵니다. 그리고 문제꺼리를 발견할 때, 놓쳤던 분석 데이터를 복구하거나 중복된 것을 제거하는 것 조차 어렵다.
Grunt와 같은 프론트엔드 자바스크립트 빌드 툴은 설정이 복잡하고 때로는 속도가 느려질 수 있다. ng-boilerplate와 같은 놀라운 프로젝트가 있어서, 설정하는데 시간을 소비하지 않아도 되지만, 그래도 여전히 속도가 느리고, 빌드 단계를 추가하고자 할 때 복잡성을 피할 수 없다. (복잡성에 대햐서 설명한 것을 알고 싶다면, ngbp의 Gruntfile을 살펴 보기 바란다.)
Gruntfile과 기타 모든 옵션을 지정하여, 일단 어플리케이션을 완벽하게 설정했다 하더라도, 여전히 느린 자바스크립트 빌드 시간을 견뎌야 한다. 개발 속도를 향상시키기 위해서 개발과 운영 빌드 환경을 별도로 구분할 수 있지만, 나중에 발목을 잡힐 수 있다. 특히나 AngularJS에서 그런 상황을 접할 수 있는데, 임의의 기능을 사용할 경우 코드를 uglify 하기 전에 ngmin을 사용해야 한다. 사실, uglify 한 자바스크립트는 개발용 자바스크립트와는 다르게 동작하기 때문에 여러차례 Sourcegraph(주로 컨덴츠를 제공하는 사이트)가 깨진 적이 있다.
그러나, 상황은 점점 더 좋아지고 있다. Gulp는 거대한 기능 향상을 가져왔다.
자바스크립트만 있는(Javascript-only) 웹사이트를 테스트하기 위해서는 Selenium, PhantomJS, WebLoop와 같은 브라우저 기반의 테스트 프레임워크를 사용할 필요가 있다. PhantomJS를 제외하고 대개 이러한 테스트 프레임워크를 설치하기 위해서는, (최근 업데이트된 PhantomJS 빌드는 이러한 사전 요구사항들을 제거하기 했지만) Xvfb를 설정한 후 WebKit과 자바 의존성(dependencies)을 인스톨해야 하고, 아마도 테스트를 위해서 로컬 VNC 클라이어트와 서버를 실행해야 할 것이다. 최종적으로, 또한, CI(continuous integration) 서버 상에 이 모든 것을 설정해야 할 필요가 있다.
대조적으로 서버단에서 생성된 페이지를 테스트하는 것은 대개 URL을 불러와서 HTML을 파싱하는 라이브러리만 필요한데, 이것들은 설치하여 설정하기가 훨씬 간단하다.
브라우저 테스트 작성을 시작한 경우, 비동기적 로딩을 처리해야만 한다. 즉, 아직 로드되지 않는 페이지 요소에 대해서는 테스트를 할 수 없으며, 주어진 시간내에 로드되지 못하면, 테스트 실패하게 된다. 이러한 문제를 해결하기 위해서 브라우저 테스트 라이브러리는 헬퍼(helper) 함수를 제공해 주지만, 그 마저도 복잡한 페이지에서만 제법 도움이 될 수 있다.
(Selenium과 파이어폭스 또는 WebKit를 조합하는) 과중한 브라우저 테스트 장비들과 (브라우저 테스트시 비동기적 로딩 문제로 인한) 훨씬 더 복잡한 테스트 환경을 함께 구축할 때 얻는 것을 뭔가? 테스트는 더 많은 설정이 필요할 것이고 실행시 더 많은 시간이 걸릴 것이고, 훨씬 더 신뢰할지 못할 것이다.
(AngularJS, Backbone, Batman, CanJS, Ember, Meteor, Knockout, Spine 등과 같은) 리치 자바스크립트 어플리케이션(rich javascript applications)에서는, 페이지 전환이 대개 즉각적으로 일어나고 페이지 상의 모든 요소들은 서버로부터 비동기적으로 로드 된다. 서버측 어플리케이션에서는, 일반적으로 이와 반대이다. 즉, 모든 데이터가 서버단에서 로드되어야만 페이지가 클라이언트로 보내지게 된다.
이 말은 클라이언트단 어플리케이션의 승리 같아 보이지만, 사실은 멋지게 변장한 저주일 수 있다.
사용자가 링크를 클릭한 직 후 페이지를 로딩하는 모습을 보여 주는 클라이어트단 자바스크립트 어플리케이션을 생각해 보자. 로드되는데 5초 정도 걸리는 데이터를 심어 놓은 사이드바를 가진 페이지로 이동한다고 가정해 보자. 초면에는 빠르게 느껴지지만 사이드바에서 정보가 필요할 때는 사이트가 엄청나게 느리다고 느끼게 된다. 원하는 특정 컨텐츠를 즉각적으로 로드하고 싶어도, 스핀 로딩 이미지와 페이지에 데이터가 로드된 직 후에 나타나는 지터링(jitter) 현상을 여전히 감수해야만 한다.
특정 페이지에 새로운 기능 추가를 원하는 개발자가 있다고 가정해 보자. 이 기능이 빠르게 로드되어야 한다(모든 것이 비동기적인 상황에서)것은 논할 필요가 없는 것이어서, 페이지의 하단에 있는 어떤 내용이 몇 초 늦게 로드될 꺼라고 누가 걱정하겠는가? 그래서 몇번이고 반복해서 결국은 전체 사이트가 느려지고 지터링이 느껴지기 시작한다.
서버측 어플리케이션에서는, 특정 API 호출이 느려질 경우, 전체 페이지가 호출이 완료될 때까지 차단될 것이다. 서버측 속도를 측정하는 것이 더 쉽고 서버측 속도 저하는 모든 사람에게 똑 같이 영향을 미치기 때문에 서버측 속도 저하 문제는 무시할 수 없는 것이다. 그러나, 클라이언트측 자바스크립트 어플리케이션에서는 속도저하를 무시하는 것이 더 쉬운 일이다.
훌륭한 개발팀은 이러한 실수들을 피해야 한다고 주장할 수 있고, 클라이언트 측 자바스크립트 프레임워크가 원인이 아니라고 주장할 수 있다. 맞는 이야기지만, 클라이언트 측 자바스크립트 프레임워크는 속도저하의 비용을 겨우 조금 줄여 준다. 이것으로 개발팀의 인센티브가 변경되기도 한다.
끝
]]>혼자가면 빨리 가지만, 같이가면 멀리간다고 하였습니다. 음 별로 관계없는 말이긴 한데, 집단으로 개발(?)하는 방법에 대해서 알아보겠습니다. 그런데… 사실 방법들이 너무 쉬워서 간단히 Getting Started 느낌일꺼 같네요.
MadEye가 좋은 점은 행아웃과 연동이 된다는 것입니다. 즉 크롬과 같은 웹 브라우저를 통해서 모든게 가능합니다. 코딩, 대화, 거기다 host하시는분의 terminal까지 볼 수 있습니다.
설치는 매우 쉽습니다.
$ curl https://madeye.io/install | sh
또는 Node.js가 가능하다면,
$ npm install -g madeye
아마 대부분의 리눅스 혹은 OS X에서 무난하게 설치가 될텐데요, 사용도 아주 간단합니다.
설치가 되었을껀데요, 현시점 최신 버전은
$ madeye --version
0.5.1
이고, 이런게 가능하네요.
$ madeye --help
Usage: madeye [options]
Options:
-h, --help output usage information
-V, --version output the version number
-c --clean Start a new project, instead of reusing an existing one.
-d --debug Show debug output (may be noisy)
--trace Show trace-level debug output (will be very noisy)
--tunnel [port] create a tunnel from a public MadEye server to this local port
--ignorefile [file] .gitignore style file of patterns to not share with madeye (default .madeyeignore)
--update Update MadEye to the latest version and exit.
-t --terminal Share your terminal output with MadEye (read-only)
Run madeye in a directory to push its files and subdirectories to madeye.io.
Give the returned url to your friends, and you can edit the project
simultaneously. Type ^C to close the session and disable the online project.
실행법은 이미 아셨겠지만, 설명해보자면… 일단 집단코딩을 하고싶은 프로젝트의 최상위 디렉터리로 이동을 합니다. 그런다음 $ madeye
를 입력해주면 끝입니다. 너무 간단하죠…?!
실행 하셨다면 아래 와 같은 모습을 보실 수 있을겁니다.
$ madeye
View your project with MadEye at https://madeye.io/edit/dvSjfeYZnEwLxiCjt
Use MadEye within a Google Hangout at https://madeye.io/api/hangout/dvSjfeYZnEwLxiCjt
위 주소 아무거나를 자주 사용하시는 웹 브라우저를 이용해서 접속해보세요. 보이시나요…!?
madeye를 실행했한 디렉터리 하위의 파일 및 디렉터리들이 전부 보이고, 오른쪽엔 수정 및 저장이 가능한 에디터가 보입니다. 이제 이 주소를 같이 코딩하고 싶은 분들께 알려주시면 됩니다. 그럼 다같이 코딩을 할 수가 있어요.
이번엔 그냥 실행하는게 아니라 -t 옵션을 줘서 실행을 해보겠습니다.
$ madeye -t
View your project with MadEye at https://madeye.io/edit/hjS7SRnpqk3XMurB8
Use MadEye within a Google Hangout at https://madeye.io/api/hangout/hjS7SRnpqk3XMurB8
################################################################################
## MadEye Terminal ##########################################################
################################################################################
Anything output from this terminal will be shared within in your MadEye session.
The shared terminal is read-only. Only you can type commands at this shell
To exit this shell and end your MadEye session type exit
Setting prompt to reflect that you are in a MadEye session
export PS1="$PS1(madeye) "
export MADEYE_ACTIVE=1
$ export PS1="$PS1(madeye) "
$ (madeye)
$ (madeye) export MADEYE_ACTIVE=1
$ (madeye)
위와 같이 -t
옵션을 줘서 생성한 URL은 에디터 아래에 터미널이 보이고, $ (madeye)
에서 어떠한 내용을 입력했을때 참여하는사람들 모두에게 보여집니다. 행아웃과 같이 사용하면, 얼굴도 볼 수있고, 대화도 할 수있고, 꽤 좋은 에디터로 같이 코딩도 할 수 있고, 거기가 터미널까지…
여기까지 원격에서 집단 개발을 도와주는 도구인 MadEye
에 대해서 알아봤습니다!
tmux는 사실 MadEye처럼 원격에서 집단 개발을 위해서 만들어진 도구는 아닙니다. 여기서 설명하려는건 원격에서 집단으로 개발할 때 사용하면 좋을 tmux의 한 기능을 소개하겠습니다.
일단 tmux가 어떤 도구인지에 대한 설명은 잘 설명된 문서를 통해 설명하겠습니다.
tmux가 뭔지 아셨나요? 이제 tumx를 이용하여 원격에서 집단으로 개발할때 유용한 기능을 소개해드리겠습니다.
일단 tmux를 이용하여 집단 개발을 하려면 개발하려는 대상 프로젝트의 개발환경이 구성되어있는 서버에 ssh, telnet 같은 도구를 이용하여 참여자들이 접속 할 수 있어야 합니다.
기본 개념은 이렇습니다. 최초에 어떤 한 사람이 tmux session을 생성합니다. 그리고 나서 다른 모든 사람들이 해당 session에 붙는 형태가 되는거죠.
먼저 tmux session을 만들어 볼까요?
$ tmux
끝.
저러면 tmux session이 생겨요. 이제 tmux session이 생겼으니 다른 사람들은 저 session으로 붙으면 됩니다. 어떻게 붙느냐…!?
이렇게 붙으면 됩니다.
$ tmux attach
끝.
session을 생성하는것, 그리고 붙는 방법 둘다 정말 간단하죠?
조금 더 자세히… 어떻게 된건지 알아보겠습니다.
다시 최초에 session을 생성할때 $ tmux
를 입력하게되면 서버 소켓 파일인 /tmp/tmux/defalut
를 생성합니다.
그리고 나서 다른사람이 똑같이 $ tmux attach
했을때엔 최초에 누군가 session을 만들당시 생성된 /tmp/tmux-1000/default
를 이용하여 같은 세션에 붙을 수 있는 겁니다.
※ tmux man page에 보면 아래와 같이 설명되어있습니다.
tmux stores the server socket in a directory under /tmp (or TMPDIR if set); the default socket is named default.
여기까지만 보면 하나의 세션만 공유가 가능한 것 처럼 보이지만 그렇지 않습니다.
이제 소개해드릴 옵션은 -L
옵션과 -S
옵션 입니다.
먼저 -L
옵션 부터 살펴보면
-L socket-name
tmux stores the server socket in a directory under /tmp (or TMPDIR if set); the
default socket is named default. This option allows a different socket name to be
specified, allowing several independent tmux servers to be run. Unlike -S a full
path is not necessary: the sockets are all created in the same directory.
tmux session을 그냥 생성하게 되면, 즉 $ tmux
이렇게 입력하면 서버 소켓 파일은 /tmp/tmux-1000/default
에 생기게 됩니다.
다시말해서 위에서 설명할때 tmux session을 만든다 혹은 생성한다라는 표현을 사용했는데, 정확히 하자면 소켓파일을 생성하는 것이라고 할 수 있죠.
그런데 -L
옵션을 사용하면 서버 소켓 파일의 이름을 지정할 수 있습니다.
$ tmux -L remotty
위와 같이 서버 소켓파일을 생성하게되면
$ pwd
/tmp/tmux-1000
$ ls
default remotty
remotty라는 서버 소켓파일이 생성 되었네요.
이렇게 되면 다른사람들이 붙을때도 -L
옵션을 사용해서 붙으면 됩니다.
$ tmux -L remotty attach
붙을땐 attach
를 뒤에 붙이는 것 빼먹으시면 안되고요.
-L
옵션도 좋지만 서버 소켓 파일 자체를 다른 곳에 생성 하고 싶을때가 있죠. 그럴때 사용하는 옵션이 -S
옵션 입니다.
-S socket-path
Specify a full alternative path to the server socket. If -S is specified, the
default socket directory is not used and any -L flag is ignored.
-S
옵션에 대한 설명은 위와 같습니다. 어떻게 사용해야 할 지 아시겠죠?
$ tmux -S /tmp/mydir/haha/hoho/remotty
위 명령을 내리면 서버 소켓 파일은 /tmp/mydir/haha/hoho
디렉터리에 remotty
이름으로 생기게 됩니다.
-S
옵션으로 서버 소켓 파일을 생성했다면 다른 사람들은 모르니까 해당 소켓파일의 위치를 알려주고 붙으라고 하면됩니다.
철수: 영희야 tmux 서버 소켓파일은 /tmp/mydir/haha/hoho/remotty
에 있어.
영희: 아 그럼 내가 -S
옵션을 사용해서 붙으면 되겠구나!
영희는 이렇게 붙으면 되요.
$ tmux -S /tmp/mydir/haha/hoho/remotty attach
그런데 만약에 두사람이 같은 소켓파일을 생성하면 어떻게 될까요? 간단히 이렇게 생각해 볼 수 있습니다. 같은 서버에 접속한 두 사람이 $ tmux
명령을 내립니다. 그럼 위에서 설명한 것과 같이 서버 소켓파일 /tmp/tmux-1000/default
이 생성될텐데요.
이렇게되면 /tmp/tmux-1000/default
에 두개의 session이 생기게 되는겁니다. 첫번째로 $ tmux
를 한 사람은 0번 session에 두번째로 $ tmux
를 한 사람은 1번 session에서 작업을 하게 되는 겁니다.
자신이 작업중인 세션의 번호는 하단에 초록색 띠가 있는데 가장 왼쪽에 대괄호로 감싸있는 숫자가 세션 번호 입니다.
이렇게 하나의 소켓파일에 여러개의 세션이 생겼고, 누군가 붙으려 아래 명령을 내렸습니다.
$ tmux attach
어느 세션으로 붙게 될까요? 마지막에 생성된 세션에 붙게 됩니다. 즉 3개의 세션이 /tmp/tmux-1000/default
에 생성되어있으면 2번 세션에 붙게 됩니다.(왜냐하면 세션번호는 0부터 시작하니까…)
그런데 마지막 세션이 아니라 1번 세션에 붙고 싶다면? -t
옵션을 사용하면 됩니다
$ tmux attach -t 1
서버 소켓 파일 /home/smart/remotty/abc
의 2번 세션에 붙고 싶다면 -S
와 -t
를 같이 사용하면 됩니다.
$ tmux -S /home/smart/remotty/abc -t 2 attach
이렇게 하면 shell 자체를 공동으로 사용할 수 있게 되는 겁니다.
실제로 tmux의 이 기능을 이용하여 Facebook Django 그룹에서는 코딩도장을 운영한다고 합니다. 대화는 행아웃으로 하고, 코딩은 각자의 terminal로 하는거죠!
저희는 Remote Work를 지향하는 조직인 만큼 이러한 원격으로 집단 개발을 가능하게 해주는 도구는 참으로 유용합니다. 이 글을 보시는 분들께서도 여기서 소개해 드린 두가지 도구를 이용하여 멀리있는 친구와도 즐거운 개발 하시길 바랍니다.
]]>만들어진 remotty.github.io의 디렉토리에서
“$ ls source/_posts”를 통해 지우고자 하는 파일 명을 확인 후
“$ git rm source/_posts/지우고자 하는 파일 명” 을 통해 파일을 지운다.
“$ git status”을 통해 상태를 확인하고,
“$ git commit -m ‘메세지 내용’으로 로깅을 하고
“$ git push”를 통해 소스를 완전히 지운다.
“$ rake push”를 통해 렌더링을 통해 메인 페이지에 포스팅을 지운다.
“$ open http://blog.remotty.com“을 통해 지워졌는지 확인 후 안됬을 경우
“$ rake deploy” 하고, “$ rake gen_deploy”를 완료 후 다시 메인 홈페이지를 확인해 본다.
간단합니다.
]]>간단하게 frontend/backend의 개념과 angularjs를 소개하고 프로젝트를 구성하는 방법을 설명합니다.
지금까지의 웹개발은 서버 프레임워크에서 frontend와 backend를 모두 처리하였습니다. controller에서 요청을 받으면 database와 통신하여 데이터를 가져오고 view를 통해 응답하는 방식입니다.
하지만 최근 iOS와 Android등 모바일앱이 늘어나면서 서버는 API만 처리하고 각 앱에서는 서버로 부터 받은 데이터를 가지고 화면에 표현하는 패턴이 일반화 되었습니다. 복잡한 서버 프레임워크 대신 경량의 서버 프레임워크가 선호되고 frontend와 backend가 철저히 분리되어 개발할 수 있게 된 것입니다.
웹에서도 webapp이라는 이름으로 그러한 흐름이 나타나고 있으며 Angular.js/Backbone.js/Knockout.js 등 다양한 framework이 등장하였습니다.
AngularJS는 최근 엄청나게 인기를 끌고 있는 frontend webapp framework입니다. Google이 만들었다는 점에서 믿고 쓸수 있고 짧은 코드로 빠르고 강력한 웹앱을 만들 수 있습니다.
우선.. AngularJS 사용 == Rails에서 View를 사용하지 않음 이라는 생각으로 접근하면 이해가 빠를것 같습니다.
Rails에서 제공하는 강력한 View Helper의 기능을 사용하지 못한다고? 네 ㅠㅠ 맞습니다. Rails는 오로지 API를 제공하는 역할만 수행하고 View는 철저히 AngularJS에서 처리합니다.
기존 rails 디렉토리 구성은 다음과 같습니다
1 2 3 4 5 6 7 8 9 10 |
|
여기에 AngularJS 소스를 넣기 위해 angular
디렉토리와 public
디렉토리를 사용합니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
소스
파일이 위치합니다. yeoman/grunt/bower 파일과 셋팅이 있고 실제 작업은 여기서 이루어집니다.배포
파일을 생성합니다. 이렇게 구성하면 일반적인 rails 배포 방식으로 webapp을 같이 배포할 수 있습니다.사실 angular디렉토리는 rails 프로젝트에 포함되어 있지 않아도 상관없습니다. 따로 프로젝트를 만들고 dist파일을 public디렉토리에 복사해도 되지만 보통 frontend와 backend작업을 동시에 할 경우 같이 관리하는 것이 좀더 편한 것 같습니다.
rails는 view를 사용하지 않으므로 rails-api를 이용합니다.
1
|
|
yeoman을 이용합니다.
ruby/compass/nodejs/generator-angular등을 미리 설치해야합니다. 설치하는 방법은.. 여기서는 pass!
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 |
|
추가적으로 npm/bower install을 하면 기본 구성 완료!
1 2 3 4 |
|
서버 실행하기
1 2 |
|
rails서버는 3000
port로 동작하고 grunt서버는 9000
port로 동작합니다. 따라서 angular에서 rails api를 호출하기 위해서는 http://localhost:3000/api/v1/posts
와 같이 호출해야 합니다.
개발환경에서는 그렇게 하면 되는데.. 운영환경은 어떻게 해야 할까요?? 운영에서는 /api/v1/posts
와 같이 호출하면 되는데.. 환경에 따라 설정을 분리해야 할까요?
여기서는 grunt-connect-proxy plugin을 이용합니다.
grunt 서버에서 /api/v1/posts
를 호출할 경우 자동으로 localhost:300/api/v1/posts
로 연동해줍니다.
먼저 plugin을 설치하고..
1
|
|
Gruntfile.js
에 관련설정을 합니다.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
|
rails쪽은 CORS 설정을 합니다.
Gemfile
에 cors gem을 추가합니다.
1 2 |
|
그리고 application.rb
에 관련 설정을 추가합니다.
1 2 3 4 5 6 |
|
이제 proxy 설정이 완료되었습니다! 개발이나 운영이나 /api/v1/xxx
을 호출하면 됩니다. +_+/
현재 설정은 angular디렉토리 밑에 dist
폴더를 만들고 배포하게 되었습니다. 이부분을 ../public
으로 배포하게 수정합니다.
역시 Gruntfile
을 수정합니다.
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 |
|
잘 배포되는지 테스트 해봅니다.
1 2 |
|
http://localhost:3000 으로 접속해서 angular 화면이 보이면 성공입니다!!
이로써 Rails와 AngularJS를 조합하는데 성공했습니다.
사실 가장 기본적인 부분을 연동한 것이며 추후 인증 및 token설정과 관련하여 더 많은 작업이 필요합니다.. ㅠㅠ
그부분은 다음기회에 설명하기로 하고 이만 마칩니다 ㅎㅎ =_=/
]]>.git/hooks
에서 샘플 스크립트와 사용할 수 있는 후크 이벤트들을 확인해볼 수 있습니다.
깃허브(Github)에서도 이러한 후크 기능을 지원하고 있으며 깃허브와 연동된 부분에 대한 이벤트를 추가적으로 지원하고 있습니다. 대부분의 경우 서비스 후크 기능을 통해서 다른 서비스와의 통합을 쉽게 할 수록 지원하고 있으며, 재미있게도 이렇게 다른 서비스와 통합하는 부분도 공개가 되어있어 관심이 있으시면 실제 코드를 확인해볼 수도 있습니다. 특히 현재는 웹후크(Webhook) 기능을 추가되어 깃허브에서 서비스 후크를 지원하지 않는 서비스와도 중간에서 매개할 수 있는 서버나 통합기능이 있다면 얼마든지 활용가능합니다.
후크란 일반적인 API와는 반대 방향으로 작동합니다. 예를 들어 보통 API를 호출하면 어떤 정보를 되돌려줍니다만, 후크는 등록이 되어있으면 어떤 이벤트가 발생할 때 거꾸로 깃허브에서 내가 등록한 Webhook URL로 정보를 보내줍니다. 여기서는 깃허브 Hook API를 조작하는 방법에 대해서 간략히 살펴보고 하나의 예제로 깃헙에서 보내주는 웹후크 알림을 처리할 수 있는 간단한 Padrino 서버를 만들어 슬랙(Slack)라는 협엄&채팅 서비스로 알림을 보내는 과정을 다뤄보겠습니다.
API의 자세한 사항은 깃허브 API 문서에서 확인할 수 있습니다. 여기서는 깃허브에서 지원하는 이벤트 종류와 웹후크를 추가했을 때 어떤 이벤트들이 추가되는지 살펴보겠습니다.
이벤트가 발생했을 때 깃허브에서 보내주는 내용은 Event Types에서 자세히 확인할 수 있으며 배포에 관한 부분은 Deployments API를 확인하시기 바랍니다.
깃허브에서 저장소에 웹후크나 서비스 후크를 등록하기 위해서는 저장소의 관리 권한이 있어야합니다. 먼저 저장소의 오른쪽에 보이는 Settings 메뉴에 들어갑니다.
들어와서 왼쪽에 보시면 Service Hooks라는 메뉴를 찾을 수 있습니다. Service Hooks 누르면 Github에서 바로 통합 가능한 서비스 리스트들을 전부 확인할 수 있습니다. 채팅 서비스인 Hipchat을 비롯해, 빌드 서비스인 CircleCI, Travis, 코드 매트릭스 관리 서비스인 Code Climate, 테스트 커버리지 리포트 서비스인 Coveralls등 다양한 서비스를 지원하고 있습니다. 여기서는 맨 위에 있는 Webhook URLs를 사용하겠습니다.
Webhook URLs를 클릭하시면 이벤트가 발생했을 때 정보를 받을 URL을 지정할 수 있습니다. 아직 후크 메시지를 받아 처리할 수 있는 서버가 없으므로 여기서는 서버를 등록하면 어떤 식으로 메시지가 오는 지, 어떤 이벤트들을 등록되어있는지 보여드리도록 하겠습니다. Webhook URLs에 notifier.nacyot.com
라는 Slack에 깃허브 저장소의 변경사항을 전달해주는 어플리케이션을 등록했다고 가정해보죠.
깃허브에서 저장소에 등록된 후크 정보를 확인하는 API URL은 다음과 같습니다.
1
|
|
이제 curl
을 통해서 Github에 Hook가 어떻게 등록되어있는지 요청을 보내보도록하겠습니다. -u
플래그 뒤로는 인증을 할 계정이름을 넣어줍니다.
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 |
|
다음과 같은 응답이 되돌아 옵니다. 이는 현재 등록된 후크의 URL과 어떤 이벤트가 등록되어있는지를 비롯한 여러가지 정보를 담고 있습니다. 특히 주목할 부분은 id
와 events
그리고 url
부분입니다. 웹에서 지정한대로 정상적으로 등록이 된 걸 알 수 있습니다. 단, 웹에서 등록을 하면 기본 이벤트인 push
이벤트밖에 등록이 되지 않습니다. 그렇다면 이슈에 관련된 이벤트나 위키가 수정되었을 때 알림을 받고자 한다면 어떻게 해야할까요?
여기서는 마찬가지로 curl
을 사용해 새로운 이벤트를 등록해보겠습니다. 여기서 등록할 이벤트는 issue
, issue_comment
, 그리고 위키 업데이트를 알려주는 gollum
이벤트입니다.
1
|
|
위에서 후크 정보를 조회했던 curl
정보를 참고로 적당한 형태로 바꿔주시기 바랍니다. 특히 여기서는 /hooks/
뒤에 앞서서 조회했던 후크의 id
값을 집어넣어줘야합니다. 다시 처음 url로 후크 정보를 조회해보면 정상적으로 이벤트들이 추가된 것을 알 수 있습니다.
1 2 3 4 5 6 7 8 |
|
이제 저장소에서 이슈와 관련 이벤트가 발생하거나 위키에 페이지가 추가되거나 업데이트될 때 알림이 오게 됩니다. 이번엔 실제로 알림이 오는 것을 확인해보겠습니다. 위키 페이지를 하나 생성해보겠습니다.
이번엔 notifier.nacyot.com
에 요청이 들어오는지 확인해보겠습니다.
1 2 |
|
정상적으로 들어오네요. 네, 앞서서 이야기했듯이 이 서버가 해주는 역할은 깃허브로부터 후크를 받고 이를 Slack이라는 채팅 서비스에 연결해주는 역할을 합니다.
채팅방에도 메시지가 잘 들어오는 것을 알 수 있습니다. (시간 차이는 시간 설정 때문에 9시간 차이가 나서 그렇습니다)
약간 순서가 거꾸로된 듯한 느낌이 들기도 합니다만, 여기서부터는 위에서 다룬 Slack에 메시지를 전달하는 서버에 대해서 다루도록 하겠습니다. 위에서 다룬 이벤트의 종류만 봐도 알 수 있습니다만, 의외로 Github에서는 다양한 이벤트들을 지원하고 있다는 것을 알 수 있습니다. 그리고 이러한 이벤트들이 깃허브 생태계를 구성하는 강력한 원동력이 되고 있습니다. 깃허브에서 서비스 후크로 바로 통합할 수 있는 서비스라면 두 서비스 간의 통합 기능을 이용하는 게 가장 편리합니다. 하지만 원하는 기능을 직접 구현하고 싶다거나 아직 서비스 후크가 갖춰지지 않은 서비스와 통합을 하려는 경우엔 직접 깃허브에서 보내는 메시지를 처리해줄 서버를 만들 필요가 있습니다.
예를 들어서 제가 속해있는 Remotty 팀에서는 지금까지 힙챗(Hipchat)이라는 협업 채팅 툴을 사용해왔습니다만, 최근에 공개된 Slack이라는 서비스로 갈아탈 준비를 하고 있습니다.1 하지만 깃허브과 통합을 하는데 약간의 애로사항이 있어서 이전을 포기했습니다. 얼마 전까지만 해도 Webhook을 통해서 Slack 서비스를 연동하면 커밋이 푸쉬되는 알림밖에는 전해주질 않았습니다. Hipchat 같은 경우는 깃허브에서 다루는 거의 모든 이벤트를 전달해줍니다. 특히 이슈와 관련된 부분도 필수적이고, 위키를 적극 사용하고 있었기에 이런 알림이 비활성화되는 것은 치명적인 단점으로 부각될 수밖에 없었습니다. 그리고 조금 더 올라가보면 바로 그런 통합이 가능했기 때문에 Hipchat을 사용하기로 했었으니까요.
이러한 문제에 대해서 슬랙 쪽 통합 방식이 최근에 변경되면서 현재는 이슈와 풀리퀘스트 부분의 알림을 보내주도록 추가가 되었습니다. 원래는 Slack의 hook_url을 직접 추가하는 방식으로 통합을 했습니다만, 최근에는 Github 인증을 하면 Slack에서 hook url을 추가해주고 이슈와 풀리퀘스트 관련 이슈들을 더해줍니다. 이러한 약간의 변화가 다시 슬랙으로 넘어가자는 의견에 힘을 실어주었습니다. 하지만 여전히 위키를 지원해주지 않는 문제가 남아있었습니다.
금방 지원해 줄 것 같기는 했습니다만, 당장 필요했던 관계로 직접 알림을 보내주는 서버를 만들기로 했습니다. 현재 루비의 Padrino2로 만든 깃허브에서 위키 변경 사항 알림 서버를 slack_notifier라는 이름으로 올려둔 상태입니다. 이 서버가 하는 일은 정말 딱 위키 알림을 Github에서 받고 Slack으로 전달해주는 일뿐입니다.
로직도 정말 단순합니다. hook_controller
에 다음과 같은 내용이 들어가있을 뿐입니다.
1 2 3 4 5 6 |
|
내부적으로 [slack-post][slack-post]라는 젬을 사용해 깃허브에서 서버의 특정 페이지에 접근하면(이벤트를 알려주면), Slack에 메시지를 보내주는 방식입니다. 현재는 서버에 데이터를 저장하는 기능이 없어서 token과 메시지를 전달할 곳을 전부 Url 인자로 받아서 사용하고 있습니다.
자, 그럼 직접 사용해보도록 하죠.
루비 서버를 올리는 게 어려운 일은 아닙니다만, 부가적인 처리 과정이나 설명해질 부분이 많아지므로 해당하는 부분에 대해서는 다루지 않도록 하겠습니다. 여기서는 이러한 과정을 생략하기 위해 이전 포스트에서 이야기했던 [도커(Docker)]를 출동시키겠습니다. 빠밤.
먼저 도커를 설치합니다. (여기서는 우분투를 가정합니다. 필요한 경우 가상머신이나 클라우드 서비스를 사용하시기 바랍니다. Remotty 팀에서도 SKT에서 지원받고 있는 VM을 활용해 개발 지원 서비스들을 운영중에 있습니다)
1 2 3 4 5 6 7 8 9 10 |
|
다음으로 어플리케이션을 클론하고 docker build
를 수행합니다.
1 2 3 4 5 6 7 8 |
|
docker imaegs
명령어로 정상적으로 빌드된 것을 확인할 수 있습니다. 이제 생성한 이미지로부터 실제 어플리케이션 컨테이너를 실행시킵니다.
1 2 3 4 5 |
|
docker ps
를 통해서 컨테이너가 정상적으로 실행되고 있는 것을 확인할 수 있습니다. 이제 깃허브에 WebHook을 등록할 차례입니다. 그 전에 먼저 Slack 쪽에서 서비스 등록을 하고 토큰을 생성할 필요가 있습니다.
1
|
|
위 주소로 접속하시면 incoming-webhook을 바로 등록할 수 있습니다. Add integration
버튼을 누르면 오른쪽에 토큰 정보가 출력됩니다. 토큰 정보를 가지고 아래 URL을 완성합니다.
1
|
|
SLACK_ROOM에는 알림을 전달할 채널 이름을 지정합니다. 이제 이 URL을 원하시는 Github 저장소의 WebHook에 등록만 해주면 모든 준비는 완료됩니다. 하지만 여기까지 설정하고 위키를 수정해도 알림은 가지 않습니다. 앞서서 Github API에 대해서 다룬 바 있습니다만, 기본 Hook로는 push
밖에 등록이 되지 않기 때문입니다. 이 서버에서 인식할 수 있는 이벤트는 위키를 다루는 gollum
밖에 없습니다.
1
|
|
앞서 다룬 것과 마찬가지 방법으로 gollum
이벤트를 추가해줍니다. 네 이걸로 모든 설정이 끝났습니다. 이제 해당하는 저장소의 위키를 수정해보면 서버에 알림이 가고 서버가 Slack으로 위키를 수정했다는 알림이 가게 됩니다.
만약 정상적으로 메시지가 가지 않을 경우엔 Github Webhook 등록 페이지에서 Test Hook 버튼을 누르고 docker logs <CONTAINER_ID>
명령어를 통해서 요청이 정상 전달되는지부터 확인할 필요가 있습니다. 일단 이러한 방법을 통해서 무사히 Slack으로 이전을 마쳤습니다.
네, 정리하겠습니다.
이 글에서는 Github Hook API 서버를 다루고 중간에서 깃허브이 전달해주는 메시지를 처리하는 서비스를 소개해보았습니다. 여기서 든 예제는 팀의 필요에 기반해서 만들어진 정말 간단한 예제입니다만, 필요하다면 좀 더 복잡하고 고도화된 서버를 개발할 수도 있을 것입니다.
]]>http://bit.ly/19ZQTHy (한글번역)
provider vs factory vs service
AngularJS docs 에는 아래와 같이 정의되어 있다.
$get 메소드를 가지는 하나의 객체이다. injector는 바로 이 $get 메소드를 호출해서 새로운 서비스를 생성하게 되는 것이다.
provider
는 설정을 통해서 이와 같은 메소드를 추가할 수 있다.
AngularJS는 $provide
를 이용해서 새로운 provider
를 등록한다. provider
는 기본적으로 새로운 인스턴스를 생성하지만 provider
당 하나의 인스턴스만 만들게 된다(singleton). $provide
는 6개의 메소드를 사용해서 커스텀 provider
를 생성하게 되는데, 각각에 대해서 샘플 코드와 함께 설명할 것이다. 아래의 provider
는 $provide
상에서 사용할 수 있다.
constant(상수)
는 모든 곳에 inject(주입)할 수 있다. 상수는 그 값을 변경할 수 없다.
1 2 3 4 5 6 7 8 9 |
|
AngularJS는 상수를 만들기 위한 편리한 방법을 제공해 주어 코드를 단축할 수 있다.
1
|
|
value
는 configuration(설정)
로 주입할 수 없지만 값을 변경할 수는 있다.
1 2 3 4 5 6 7 8 9 |
|
AngularJS는 value
를 만들기 위한 편리한 방법을 제공해 주어 코드를 단축할 수 있다.
1
|
|
service
란 주입이 가능한 constructor(생성자)
이다. 원할 경우, 함수에 필요한 dependency를 명시할 수 있다. service
는 하나의 singleton이기 때문에 AngularJS가 한번만 생성하게 될 것이다. service
는 데이터를 공유하는 것 같은 컨트롤러간의 통신을 위한 좋은 방법이다.
1 2 3 4 5 6 7 8 9 10 11 |
|
AngularJS는 service
를 만들기 위한 편리한 방법을 제공해 주어 코드를 단축할 수 있다.
1 2 3 |
|
factory
는 주입이 가능한 함수이다. factory
는 singleton이고 dependency를 함수내에 명시할 수 있다는 점에서 service
와 많은 부분에서 흡사하다. 차이점은 factory
는 일반 함수를 주입해서 AngularJS가 호출하도록 하는 것이고 service
는 constructor
를 주입한다는 것이다. constructor
는 새로운 객체를 만들기 때문에 service
내에서는 new
메소드를 호출하지만, factory
를 사용하면 함수가 어떤 형태의 것이라고 반환할 수 있도록 해 준다. 나중에 알게 되겠지만, factory
는 $get
메소드만을 가지는 provider
이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
AngularJS는 factory
를 생성하는 편리한 방법을 제고해 주어 아래와 같이 단축할 수 있다.
1 2 3 4 5 |
|
decorator
는 다른 provider
를 변경하거나 encapulation할 수 있다. 그러나 하나의 예외가 있는데, constant
는 decorate 할 수 없다는 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
provider
는 모든 provider
중에서 가장 유연한 메소드이다. 이 메소드를 이용하면 복잡한 생성 함수에 다양한 옵션을 가지도록 할 수 있다. provider
는 실제로 구성을 변경할 수 있는 factory
이다. provider
는 하나의 객체나 constructor
를 취하게 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
providers
들은 단 한번만 인스턴스화 된다. 즉, singleton 이라는 것이다.constant
를 제외한 모든 provider
는 decorate할 수 있다.constant
은 어느 곳이나 주입할 수 있는 값을 가지만, 수정할 수 없다.value
는 간단하게 주입할 수 있는 값이다.service
는 주입할 수 있는 constructor
이다.factory
는 주입할 수 있는 함수이다.decorator
는 constant
를 제외한 모든 provider
를 변경하거나 encapuslation할 수 있다.provider
는 구성요소를 설정할 수 있는 factory
이다.바로 위에서 REST에 대해서 알아본다고 하였지만, REST의 정의와 같은 것은 생략할 예정입니다. 진짜로 이글에서 다룰 것은 실제 RESTFul한 API를 작성할 때 도움될만한 것들을 공부합니다. 또한 여러가지 규칙이 있지만 어느 규칙이 진짜이고, 표준화 된것이 없기때문에(이런것으로 알고있습니다.) 실제 많은 사이트들이 약간씩은 다른 형태로 REST API를 운영하고있습니다. 이 글에서도 필자가 중요하다고 생각하고 직관적이라고 생각하는 요소들을 선택 하여 소개 또는 설명 할 예정입니다.
요즘은 예전과 달리 소비를 위한 장비 및 프로그램이 다양합니다. 반대로 말하면 예전에는 어떠한 인터넷 컨텐츠를 소비하기위한 장비나 프로그램이 몇개 없었습니다. 그렇기때문에 서버와 클라이언트가 거의 1:1이였습니다. 그러나 요즘은 역시 서버는 1인데 클라이언트가 굉장히 다양해졌습니다. 안드로이드는 OS 버전도 굉장히 다양하고 단말기마다 굉장히 다른 특성을 갖기도 합니다. 또 IOS도 있고, 컴퓨터 브라우져의 종류도 많아졌죠.
그래서 예전처럼 하나의 클라이언트를 위한 서버를 구성하는건 비효율적인 일이 되어버렸습니다. 하나의 서버로 여러대의 클라이언트를 대응하도록 할때 필요한것이 RESTFul API입니다.
일단 URI(Uniform Resource Identifier)란 영어 약자를 풀어보면 ‘균등한 리소스 식별자’정도로 할 수 있습니다. 말그대로 인터넷의 어떠한 리소스를 식별하기 위해서 만들어진 것입니다. 잘 감이 안오는데 중간중간 특성들을 이야기하면서 내용을 보강하겠습니다.
밑에서 설명하는 규칙들은 RFC3986을 기반으로 작성되었습니다.
http://www.example.com:80/users/1?q=abc#title
|________________________|_______|__________|
1 2 3
URI는위와같이 크게 3부분으로 나뉘어져있습니다.
rfc3986에서는 1번부분(host와 port)을 제외한 2번 3번 부분은 대소문자를 구분하도록 하였습니다.
즉
a - http://abc.com/haha
b - HTTP://ABC.COM/haha
c - http://abc.com/HAHA
d - http://abc.com/hAhA
위 예제에서 a와 b만 같은 리소스이고 나머지 조합은 전부 다른 리소스입니다. 그러므로 대소문자를 섞어서 사용하는건 혼란을 가져올 수 있으므로 지양해야겠습니다. 그래도 URI에 사람이름과 같은 고유명사가 들어갈 수 도있죠.. 이땐 대문자를 쓰고싶은데… 사용해도 되긴합니다만 대소문자가 구분된다는걸 이해하셔야 합니다. 위 설명과 중복되긴 하지만 다시한번 언급하겠습니다.
http://www.remotty.com/countries/korea
http://www.remotty.com/countries/Korea
위 두개의 URI는 분명히 다른 리소스를 가리키고 있는 것입니다.
다만 국가코드를 포함하여 도메인을 의미있게 하기위해서 이와 같은 형태로 사용하는 것은 굉장히 좋다고 봅니다. http://cleanHo.me
, http://adBy.me
, http://dishBy.me
, http://googleSear.ch
이 경우에도 경로(path)부분은 소문자를 사용하는게 좋겠습니다.
경로(path)에 공백(띄어쓰기)가 들어갈땐 경우에따라 띄어쓰기 대신 %20
이 쓰일때가 있습니다. 이런건 보기에 별로 좋지 않기때문에 많은 사람들이 밑줄(_, underscore) 또는 하이픈(–, hyphen)을 사용하여 공백을 대체합니다. 여기서 권장하는건 하이픈만 사용하자 입니다. 보통 밑줄은 링크가 걸린부분에 표시되는데 그것과 중복되면 리소스로써의 밑줄은 가려질 수도있기때문에 하이픈을 사용하자.
기존의 많은 URI들이 확장자를 포함하고 있습니다. 하지만 REST API를 설계할때에는 확장자는 사용하지 않는게 좋겠습니다. 확장자를 사용하지 않으면 리소스가 더 유연해집니다. 어째든 확장자를 사용하지 않는다면 기존에 REST API를 접해보지 않는 사람들은 이런 의문을 갖을 수 있습니다?
결론적으로 Accept header를 사용해야 합니다. 예를들어 내용이 Hello,World
인 파일이 있습니다. 서버를 기존방식대로 설계한다면 해당 파일은 http://remotty.com/hello.txt
와 같이 요청하여 응답받을 것입니다. 기존의 방식은 분명하게 파일의 형태가 txt로 고정되어있습니다. csv 형태로도 제공하려면 http://remotty.com/hello.csv
URI도 준비해야 할것이고, 서버엔 hello.txt와 hello.csv 두개의 파일이 존재 하게 될것입니다. http://remotty.com/hello.txt
와 http://remotty.com/hello.csv
는 분명하게 다른 리소스를 식별하는 URI이지만, 실제론 하나의 리소스를 가르키고 있습니다. 이것은 비 효율적입니다. 리소스가 한개라면 URI도 한개여야합니다.
REST API에서는 http://remotty.com/hello
에 대한 대응만 해놓고, 해당 요청이 왔을때 Accept
header를 적절히 파싱(parsing)하여 클라이언트(client)가 요청한대로 응답해주면 됩니다.
REST API로 구현했을땐
GET /hello HTTP/1.1
Host: remotty.com
Accept: text/plain
또는
GET /hello HTTP/1.1
Host: remotty.com
Accept: text/csv
Accept
를 좀더 적극적으로 활용한다면 이렇게도 가능합니다.
GET /hello HTTP/1.1
Host: remotty.com
Accept: text/csv,text/html;q=0.5,application/xml;q=0.6,text/plain;q=0.9,application/pdf,*/*;q=0.3
Accept
header은 클라이언트가 자신이 선호하는 media type을 서버에 보내는 것입니다. 서버에 위 예제와 같은 요청이 왔다면 가장먼저 우선순위는 다음과 같습니다.
q가 생략된것은 자동으로 1로 설정되며, q의 범위는 0부터 1입니다. 서버가 판단하여 csv로 응답을 할 수 있다면 csv로 응답을 하고 csv가 준비가 되지 않았다면 그다음 우선순위인 pdf로 pdf도 준비되지 않았다면 계속 다음으로 넘어가다가 서버가 응답할수있는 media type가 Accept
에 명시되지 않았다면 http status code 406과 body엔 응답 가능한 media type를 명시하여 응답하여야 합니다. 더 자세한 내용은 http content negotiation
으로 검색하시어 저도 좀 알려주시기 바랍니다.
위와 같이 Accept
header를 적절히 잘 사용하면 하나의 URI로 클라이언트의 요청에 대한 응답을 좀더 유연하게 할 수 있습니다.
과거에 GET
, POST
만 사용하였을땐 CRUD를 URI에 표시해주어야 했습니다.
예를들면
GET /posts/13/delete HTTP/1.1
POST /posts/write HTTP/1.1
GET /delete?id=55 HTTP/1.1
하지만 당신은 이제부터 REST API를 설계할 수 있습니다. 뒷부분에 나오는 HTTP Method의 알맞은 역할을 참조하여 적절한 Method를 사용하여 CRUD는 URI에 사용하지 않기로 합시다.
이 글을 보기전 REST 관련된 글을 본적이 있다면 컬렉션(collection)이라고 이야기하는 도큐먼트(document)들의 집합을 들어보았을 겁니다. 도큐먼트는 우리말로 문서로 이해해도 되고, 정보라고 이해해도 무관합니다. 도큐먼트는 엘리먼트(element)라고도 하더라고요. 컬렉션은 정보(문서)들의 집합이 이라고 할 수 있겠네요. 컬렉션과 도큐먼트는 모두 리소스라고 표현할 수 있으며 URI에 나타납니다.
예제를 통해 이해도를 높여보겠습니다.
http://www.remotty.com/sports/soccer
http://www.remotty.com/sports/soccer/players
http://www.remotty.com/sports/soccer/players/13/skills
위 URI를 요청하면 어떤 응답이 오게 될지 대충 예감이 오시나요…?
http://www.remotty.com/sports
는 컬렉션입니다. sports
컬렉션에 soccer
도큐먼트가 있는거고요. 또 soccer
도큐먼트에 player
이라는 컬렉션이 존재하는겁니다! players
컬렉션에 뭐 등번호가 13
번인 선수가 있나봅니다. 여기서 13
은 도큐먼트이고요. 아시겠죠…?
조금 더 이야기 해보겠습니다. soccer
도큐먼트와 동일한 수준의 다른 도큐먼트는 뭐가 있을까요…? baseball
, marathon
등이 있겠네요. 그 하위의 players
의 컬렉션과 동일한 수준의 컬렉션은 뭐가있을까요…? rules
, leagues
등이 있겠네요. 이제 뭔지 아시겠죠…?
여기서… 중요한 법칙이랄꺼 까진 없지만 뭔가 있습니다. 바로 컬렉션은 복수로 사용하네요. 어쩌면 당연하지만 직관적인 REST API를 위해선 단수 복수도 정확하게 지켜주면 좋겠습니다. 요즘은 한글이 URI에 많이 들어가는데 players
는 선수들
로 하면 되려나요…?
HTTP Method는 여러가지가 있지만 REST API에서는 4개 혹은 5개의 Method만 사용됩니다. POST
, GET
, PUT
, DELETE
이 4가지의 Method를 가지고 CRUD를 할 수 있습니다. 그러나 REST API에서 사용되는 개수는 4개 혹은 5개라고 한 이유는 PATCH
를 포함하면 5개가 됩니다.
각 Method마다 올바른 역할이 있습니다. 아래 표를 보면서 이해를 높이겠습니다.
+--------------------------------------+------+-----+-----+--------+
| URI | POST | GET | PUT | DELETE |
+--------------------------------------+------+-----+-----+--------+
| http://www.remotty.com/sports | 1 | 2 | - | - |
+--------------------------------------+------+-----+-----+--------+
| http://www.remotty.com/sports/soccer | - | 3 | 4 | 5 |
+--------------------------------------+------+-----+-----+--------+
text table generator에서 만든 text table인데 한글을 사용하면 깨지므로… :(
번호를 이용하여 설명하겠습니다.
※추가로 주목해볼 만한 Method는 PATCH
입니다.
기존에 REST에 익숙하신분들은 수정(update)을 위한 Method는 PUT
가 익숙하실 겁니다. 하지만 앞으로는 PUT
대신 PATCH
를 자주 써야할꺼 같아요. 자세한 설명은 Edge Rails: PATCH is the new primary HTTP method for updates을 참조해주세요.
요즘은 정말 다양한 장비들이 존재합니다. 크기 역시 다양합니다. 그래서 요즘 작은 화면의 기기로 포털사이트를 접속해보면 어느 형태로든 m
이 붙은걸 볼수 있습니다. 아마 mobile
에서 맨앞의 m
인것같은데, 화면이 작은 장비에서는 800*600이 넘어가는 사이트를 돌아다니는건 정말 피곤한 일입니다. 그래서 작은 화면용 페이지가 필요한 이유입니다. 네, 여기까지는 좋습니다. 제가 이 부분에서 소개해드리려는 header는 User-Agent입니다. 그리고 결론적으로 말씀드리고 싶은 내용은 http://m.remotty.com/abc
또는 http://www.remotty.com/m/abc
처럼 사용하지 말자 입니다.
한가지 예를 들어보겠습니다.
http://m.remotty.com/fun
공유됨.http://m.remotty.com/fun
)클릭.뭐가 문제인지 감이 오시나요? 이미 많은 웹사이트가 User-Agent
header을 사용중입니다. User-Agent
header을 적절히 파싱하여 화면이 작은 장비는 모바일에 최적화된 사이트로 이동(redirect)시켜주고 있습니다.
RESTFul한 웹사이트에서는 User-Agent
를 이용하여 다른 곳으로 리다이렉트 시켜주는게 아니라 URI는 그대로이지만 화면만 장비에따라 알아서 최적화 되도록 설계해야합니다.
다시말해서 http://www.remotty.com/info
와 http://m.remotty.com/info
는 사실상 같은 정보를 보여주고 있지만 화면의 형태만 다를 뿐입니다. REST하게 설계할땐 리소스가 같다면 URI는 하나여야 합니다.
현재 많은 웹사이트들이 다국어를 지원하고있고, http://www.remotty.com/ko/info
나 http://en.remotty.com
등과 같이 언어마다 다른 URI를 운영중인 곳이 있습니다. 여기서 소개해드릴 것은 Accept-Language header입니다.
또 한번 예를 들어보겠습니다.
http://ko.remotty.com/fun
공유됨.뭐가 문제인지 감이 오시나요? 다국어 지원을 Accept-Language
에 맡긴다면 URI는 그대로인데, 사용자의 환경에 따라 알맞은 언어로 응답할 수 있습니다. 물론 Accept-Language
만 가지고 다국어를 하면 조금 어색할 수 있을꺼 같습니다. 사용자가 원하는 언어를 설정하게하여 해당 언어를 세션 또는 쿠키 등에 저장하여 보여줄 언어를 선정할때 우선순위를 약간 조정하여 보여주는게 좋은 방법일꺼 같습니다.
지금 약간 이글의 확장자를 사용하지 말자와 반응형 웹에서의 REST랑 약간 비슷한 느낌인데요, 모두 결론은 URI는 리소스를 식별하기 위해서 사용되었지, 리소스가 어떻게 보여지느냐는 별도의 header을 이용하여 처리하였습니다. 이 점을 생각하면서 다른 문제들도 좀더 REST하게 설계해야겠습니다.
rfc2616을 살펴보면 많은 종류의 상태코드가 존재합니다. 상태코드를 적절히 잘 사용하면 클라이언트에게 많은 정보를 줄 수 있습니다.
Location
header에 변경된 URI를 적어줘야 합니다.마크 마세가 쓴 REST API 디자인 규칙에 이런 말이 있네요.
REST API는 부실한 HTTP 클라이언트에 부합하려는 그 어떤 타협도 해서는 안된다.
순서가 약간 뒤죽박죽 작성된 느낌이 있네요. 사실 저도 RESTFul한게 좋은건지 나쁜건지 뭔지 아직도 잘 모르는 상태로 작성하였는데, 한가지 확실한건 REST라는 개념(?)이 널리 퍼지고 많은 API들이 RESTFul하다면 REST에서 다루는 내용들은 따로 문서화도 필요없이 자연스레 좀더 체계적인 느낌으로 API를 사용할 수 있을꺼 같은 느낌이 듭니다. 아직 미완성된 글이라 생각합니다. 틀린 내용이나 이해되지 않는 내용이 있으시면 댓글이나 기타 편하신 방법을 통해 적극적으로 글을 완성해주시길 바랍니다.
나름 효율적으로 협업을 하기 위해, 각각의 활동에 최적화(?)된 도구를 선정하고 그것을 사용하기로 했었는데
가장 큰 문제는 사용해야 할 도구가 너무 많아 커뮤니케이션이 분산된다는 것이었다.
페이스북, 트렐로, 구글 드라이브, 에버노트…
모두 다 좋은 도구다. 잘만 활용한다면 좋은 커뮤니케이션 도구로 사용할 수 있다.
하지만 개발자들간의 협업에 있어서 소스코드가 제일 중요할 수 밖에 없고,
그 커뮤니케이션의 중심에 github가 있는 것은 당연지사.
결국 github로 돌아오게 되더라.
이슈 관리도, 일정 관리도, 대화도, 각종 문서 정리도, 대화도…
모든 커뮤니케이션을 github안에서 해결할 수 있는 방법을 찾게 되었다.
그래서 결론을 얘기하면, 현재는 아래와 같은 방식으로 협업을 하고 있다.
태스크 관리
트렐로 github 이슈 + Waffle
문서 공유
에버노트 or 구글 드라이브 github 위키
단체 코딩
MadEye
소스 관리
github
잡다구리한 이야기들 + 각종 대화 & 논의 + 그 외 모든 의사소통
페이스북 포스트 or 메신저 HipChat
기타 파일 공유
드롭박스 HipChat으로 전달. 파일 관리는 따로 안함.
정말 지속적으로 관리할 필요가 있는 파일이 있다면 github에 올려도 될 것 같음
오프라인 모임
여기 이전 포스트에 소개되지 않았던 새로운 도구가 등장한다.
바로 Waffle과 HipChat이다.
이 두가지를 한번 소개해본다.
쉽게 말해서 github의 이슈들을 dashboard 형태로 보여주는 도구이다.
그것이 전부이다.
github 이슈는 단순히 목록 형태로 쭉 펼쳐져서 보이기 때문에,
원하는 것들을 필터링 해서 보거나 한눈에 dashboard 형태로 보기가 어렵다.
그러한 불편함을 해소해주는 도구가 바로 Waffle이다.
눈에 보기 쉽게 Column을 만들고 그 안에 github에 등록된 이슈들이 list 형태로 뿌려진다.
얼핏 보기엔 trello와 비슷해 보인다.
github의 이슈들이 trello의 card 형태로 나타나고, drag&drop으로 쉽게 상태를 변경할 수 있다.
Waffle에서 생성한 Column은 github 이슈의 라벨로 나타난다.
Waffle에서도 이슈를 등록할 수 있고, 당연히 github 이슈에도 등록이 된다.
백날 얘기해봤자 머리속의 상상만으로는 잘 이해가 안된다. 아래 사진을 보면 딱 이해가 될 것이다.
기본적으로 메신저는 실시간성이다.
온라인상의 누군가에게 메시지를 보내고 그것을 본 누군가는 응답을 한다. 이것이 메신저의 기본 개념.
하지만 단체 채팅의 경우는 상황이 좀 다르다.
대화가 오고 갈때 온라인상에 없었던 사용자는 그 대화에 참여할 수 없게 되고 그때 오고 갔던 대화의 내용들은 놓치게 된다.
물론 스크롤을 쭉~ 올려서 찾아서 읽으면 된다.
당연히 그렇게 필요한 사람이 내가 놓친 내용을 꼼꼼히 스스로 찾아 읽으면 되지만, 잘 되지 않는다.
그리고 지난 대화들을 찾아서 본다 하더라도 이미 마무리 된 대화를 또다시 개진하려면 좀 뒷북치는 느낌이다. ;;
물론 하면 되지만, 대부분의 메신저 도구들은 실시간성에 UI가 맞추어져 있다.
그렇다. 우리에게 필요한 것은 바로 비동기 채팅이다.
비동기 채팅이란, 한마디로 “로그를 읽는다”라는 개념이다. 대화의 기록을 보고 자신의 생각을 그냥 얘기하면 된다.
IRC가 바로 이러한 비동기 채팅에 적합한 도구이다. 하지만 왠지 irc는 말만 들어도 겁이 난다.(왜일까… ㅡ,.ㅡ;;)
이러한 용도로 최적화 된 툴이 바로 HipChat이다.
HipChat은 개발자들에게 좀 특화된 메신저 도구여서, 대화창에 각종 코드도 쓸 수 있다.
“/code”라고 prefix를 붙히면, 뒤에 따라오는 코드는 포맷팅이 되어 읽기 쉽게 나타난다.
뿐만 아니라 github와도 연동을 시킬 수 있어서, github에서 일어나는 모든 event를 hipchat에서 받을 수 있다.
hipchat의 api가 공개되어 있어서, github외에 다른 이벤트들도 hipchat에 전달하도록 구현할 수 있다.
여러 환경에서 활용 가능하도록, 각 언어별로 library를 제공하고 있다.(참고: HipChat Libraries & Plugins)
ruby 코드로 2줄이면 hipchat에 message를 날릴 수 있다.
1 2 |
|
HipChat이 제공하는 또다른 killer 기능이 있다. 바로 notify 기능이다.
대화를 할때 ‘@’ 표시와 함께 대상자에 대한 mention을 표기할 수 있는데, @로 mention을 날리면 상대방에게 notify가 간다.
상대방이 온라인이 아니더라도 mention을 받게 되면 notify가 오게 되고, 그때 대화에 동참하면 된다.
그리고 notify는 메일로도 전송되기 때문에, 내가 없는 자리에서 나와 관련된 중요한 대화가 오고 갔는지를 알 수 있다.
내가 없는 자리에서 오고 갔던 대화 히스토리에 나에게 mention 표시가 되어 있는 전/후의 내용을 좀 더 주의 깊게 보면 된다.
mention은 @all 이런 식으로 사용할 수도 있다. 당연히 모든 멤버에게 mention을 날린다는 의미다.
대화 중, 특정 누군가에게 또는 모두에게 공유해야 할 내용이 있을 때, @로 mention을 날리면 된다.
비동기 채팅에 notify 기능이 합쳐지고, 코드에 대한 마크업 기능까지…
remote로 일하는 개발 그룹에 꼭 필요한 도구이다!
하지만 단점이 있다.
5명 이상인 경우엔 유료라는 것. (매월 1명당 2$씩)
한달에 2$면 커피한잔 가격도 안된다.
효과적인 커뮤니케이션을 위해 한달에 커피 한잔 정도는 절약하는건 어떨까? ^^
active_model_serializers
젬은 레일스 API 를 작성할 때 JSON 데이터를 만들기 위해 추천되는 젬입니다.
Gemfile 에 추가하고 bundle install 합니다.
1
|
|
이후부터 scaffolding
이나 model generator
를 사용하여 특정 모델을 생성하면 자동으로 serializer
가 만들어 집니다.
이미 만들어진 모델에 대해서는 아래와 같이 직접 serializer
를 생성할 수 있습니다. 여기서는 Post
모델에 대한 serializer
를 생성하는 예를 들었습니다.
1
|
|
이제 app/serializers/post_serializer.rb
에서 Post
모델에 대한 serializer
를 볼 수 있게 됩니다.
컨트롤러에서 render :json
을 사용하면, 우선적으로 해당 객체에 대한 serializer
를 찾아보고 있으면 해당 serializer
를 사용하게 됩니다.
1 2 3 4 5 6 |
|
배열에 대해서도 render :json
을 사용할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 |
|
렌더링되는 결과는 아래와 같습니다.
1 2 3 4 5 6 7 |
|
디폴트로 컨트롤러의 이름이 루트 엘리먼트의 이름이 됩니다. 즉, PostsController
는 posts
라는 루트노드명을 만들어 줍니다. 또한 아래와 같이 루트노드명을 변경할 수도 있습니다.
1
|
|
모든 클래스에 대해서 루트 엘리먼트를 사용하지 않는 방법
initializer 파일을 새로 만들어 아래와 같이 추가해 줍니다.
1 2 3 4 5 6 |
|
1
|
|
1 2 3 4 5 6 |
|
default_serializer_options
메소드를 정의하는 방법1 2 3 4 5 |
|
serializer 클래스에서는 속성과 관계를 지정할 수 있습니다.
1 2 3 4 |
|
attributes로 명시한 속성들에 대해서 serializer는 render :json
호출시에 넘겨준 액티브레코드 객체에 대해서 해당 속성들을 찾아보게 됩니다. 이 때 serializer는, ActiveRecord
객체가 속성을 조회하기 위해서는 사용하는 read_attribute_for_serialization
메소드를 이용하게 됩니다.
특정 객체에 대한 속성을 조회해 보기 전에, serializer는 해당 속성과 같은 이름의 메소드가 정의되어 있는지를 알아 보고 있다면 모델 속성을 포함하기 전에 해당 메소드의 결과를 속성으로 포함하게 됩니다.
예를 들면,
1 2 3 4 5 6 7 |
|
serializer 메소드 내에서 객체는 object
로써 접근하게 됩니다.
따라서 속성명이 object
라는 이름을 가질 경우 그 이름이 감춰지게 되므로 이 때는 object.object
로써 접근할 수 있습니다. 예를 들면,
1 2 3 4 5 6 7 |
|
또한 scope
메소드를 사용할 수 있는데, 이것은 serializer에서 인증상태를 이용할 수 있게 해 줍니다. 디폴트로는 어플리케이션의 current user가 바로 이러한 인증상태에 해당하는 것이지만 다른 것으로 변경할 수도 있습니다.
serializer는 filter
라는 메소드를 제공해 줍니다. 이것은 결과에 보여줄 attributes와 associations을 포함하는 배열을 반환해 줍니다. 일반적으로 이것은 current_user
에 근거해서 결과를 다양하게 보여주기 위해서 사용합니다. 예를 들면 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
별도의 keys 배열을 추가로 만들 필요없이, keys.delete(:author)
를 이용하여 keys 인수를 변경하는 것이 안전할 것입니다. 주의할 것은 in-place 변경을 시도하더라도 변경된 배열을 여전히 반환할 필요가 있다는 것입니다.
액티브레코드 상의 이름과 다른 키를 사용하고 싶을 때는, 다른 이름의 키를 선언하고 메소드를 재정의하면 됩니다.
1 2 3 4 5 6 7 8 9 |
|
JSON 결과물에 메타 정보를 포함하고잘 할 경우에는, :meta
옵션을 사용하면 됩니다.
1
|
|
그러면 아래와 같은 결과를 보여 줄 것입니다.
1 2 3 4 5 6 7 |
|
또한 :meta_key
옵션을 사용하면 메타 키 이름을 변경할 수 있습니다.
1
|
|
:meta_key
옵션을 사용하면 아래와 같은 결과를 보여 줄 것입니다.
1 2 3 4 5 6 7 |
|
이와 같이 메타 정보를 이용할 경우에는, serializer는 { root: false }
옵션을 가질 수 없습니다. 결국 유효하지 않는 JSON 데이터를 반화하기 때문에 루트 키가 없는 경우에는 메타 정보가 무시될 것입니다.
attribute 직렬화 과정을 직접 로우레벌에서 조작하고자 할 경우에는, attributes
메소드를 덮어쓰기해서 해시를 반환해 주면 됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
association을 사용할 경우, serializer가 해당 association을 찾아보고 연관객체의 각 엘리먼트를 직렬화하게 됩니다. 예를 들어, has_many :comments
라고 지정하면 각 comment 객체에 대해서 CommentSerializer 객체를 만들어서 직렬화하게 되는 것입니다.
디폴트 상태에서는 오리지날 객체에 대해서 선언되어 있는 association을 찾게 됩니다. 그러나 해당 association 이름과 동일한 메소드를 정의하여 반환되는 객체들을 변경할 수 있습니다. 이것은 특정 scope(current_user와 같은)에 국한된 객체들을 반환할 때 사용하면 도움이 될 수 있습니다.
1 2 3 4 5 6 7 8 9 |
|
이 경우에도 attributes와 같이 JSON 키를 변경할 수 있습니다.
1 2 3 4 5 6 |
|
또한 attributes와 같이, filter
메소드를 정의하면, 결과로써 포함할 associations을 지정할 수 있습니다.
1 2 3 4 5 6 7 8 9 |
|
또는
1 2 3 4 5 6 7 8 9 10 11 |
|
:serializer
옵션을 이용하여 커스텀 serializer 클래스를 지정할 수 있고 :polymorphic
옵션을 지정하여 해당 association이 polymorphic 이라는 것을 알려줄 수 있습니다.
serializer에서는 belongs_to
association을 has_one
을 이용하여 포함하게 된다는 것을 주의해야 합니다.
디폴트 상태에서는 associations가 serializer 객체에 포함(embeded)됩니다. 그래서 하나의 post 가 있다고 가정할 때 다음과 같은 결과를 볼 수 있게 될 것입니다.
1 2 3 4 5 6 7 8 9 10 |
|
이러한 결과물은 간단한 경우에는 편리하지만, 복잡한 association이 존재할 경우에는 해당 association 에 대해서 ID이 구성된 배열을 포함하는 것이 더 좋을 것입니다. 이것은 전체적인 퍼포먼스 측면에서도 그렇고 불필요한 중복을 피할 수 있어서 좋습니다.
이를 위해서 embed
라는 클래스 메소드를 사용하면 됩니다.
1 2 3 4 5 6 |
|
이제 association들이 ID들로 구성된 배열을 포함하게 될 것입니다.
1 2 3 4 5 6 7 8 |
|
다른 방법으로는 클래스내의 측정 association에 대해서만 ID 또는 객체 배열만을 포함할 수 있게 할 수 있습니다.
1 2 3 4 5 6 |
|
따라서 JSON 데이터는 다음과 같이 보일 것입니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
게다가, ID 만을 포함하는 것 외에도 메인 객체에 데이터를 추가로 포함할 수도 있습니다. 이렇게 하므로써 포함된 정보를 검색하기 위해서 트리구조를 스캔할 필요없이 전체 데이터 패키지를 보다 쉽게 처리할 수 있게 될 것입니다. 또한 객체사이에 (tags와 같이) 공유되는 associations들은 전체 로드시에 단 한번만 전달된다는 것입니다.
아래와 같이 데이터가 포함되도록 명시할 수 있습니다.
1 2 3 4 5 6 |
|
이 때 comments 객체가 has_many :tags
association이 선언되어 있다고 가정하면, 다음과 같은 JSON 데이터를 얻게 될 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
위에서와 같이 데이터를 추가로 로드할 경우에는 { root: false }
옵션을 사용할 수 없습니다. 이 옵션을 지정할 경우에는 유효하지 않은 JSON 데이터를 만들게 되기 때문입니다. 따라서 이 옵션을 지정하게 되면 include
옵션이 작동하지 않게 됩니다.
또한 포함된 객체에 대해서는 참조하는 키외의 다른 루트를 지정할 수 있습니다.
1 2 3 4 5 6 |
|
이것은 다음과 같은 JSON 데이터를 민들게 됩니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
또한 포함된 객체의 ID외의 다른 속성을 지정할 수 있습니다.
1 2 3 4 5 6 |
|
이것은 다음과 같은 JSON 데이터를 만들게 됩니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
Note: embed :ids
기전은 주로 데이터를 대량으로 처리해서 로컬 저장소에 로드할 경우에 유용합니다. 이와 같은 경우에, 정보검색을 위해서 데이터를 반복적으로 스캔할 필요없이 종류별로 모든 데이터를 쉽게 볼 수 있다는 것은 매우 유용한 기능입니다.
대부분의 경우 간단히 시나리오 하에 데이터 작업을 하고 직접 Ajax 요청을 할 경우에는 아마도 디폴트 상태의 embed 기능을만을 사용하면 될 것입니다.
특정 serializer 클래스에서 대해서, current_user
는 render :json
을 호출할 때 컨트롤러가 해당 serializer에 제공하는 인증 scope에 해당합니다. 디폴트로, 이것은 current_user
가 되지만, 컨트롤러에서 serialization_scope
을 호출하여 이 scope을 변경할 수 있습니다.
1 2 3 |
|
위의 예는 scope을 current_user
에서 current_admin
으로 변경하게 될 것입니다.
주목할 것은, 지금까지 볼 때, serialization_scope
은 두번째 인수를 지정하여, 해당 scope을 적용할 액션들을 지정할 수 없습니다.
즉, 아래와 같이 액션들을 지정할 수 없다는 것입니다.
1 2 3 |
|
따라서 대신에 아래와 같이 처리할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
위에 예에서, current_admin
메소드가 데이터베이스에서 현재 사용자를 조회할 필요가 있다고 가정한다면, 이러한 방식의 접근방식을 통해서, serailization_scope
값은 nil
로 지정하므로써, index
액션이 더 이상 데이터베이스를 조회하기 않고 단지, show
액션만이 해당 메소드를 실행하게 되는 것입니다.
임의의 serializer 클래스를 테스트하기 위해서는, 단지 해당 serializer 클래스에 대해서 .new
메소드를 호출하여 모델 클래스 객체를 넘겨 주면 됩니다.
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 |
|
수고하셨습니다.
]]>예를 들어서 다른 사람이 만든 저장소를 자신의 계정에 fork해서 별도로 관리할 수 있고, 이렇게 fork해서 수정한 저장소의 브랜치를 pull-request를 통해서 원래의 저장소에 통합하도록 요청할 수도 있습니다. 이외에도 이슈 관리와 위키를 비롯한 매우 다양한 기능들이 지원됩니다. 이러한 Github의 장점들은 단순히 git을 활용한 좋은 버전관리 시스템이라는 것을 넘어서 프로젝트 관리를 위한 도구로서 Github 서비스를 차별화 시켜줍니다.
하지만 대부분의 기능들은 기본적으로 웹인터페이스로만 사용할 수 있다는 단점이 있습니다. Git의 가장 기본적인 클라이언트 프로그램은 git 명령어로 커맨드라인에서 사용할 수 있습니다. 하지만 Github는 일차적으로 웹서비스로서 부가적인 기능들을 웹을 통해서 제공합니다. GUI나 화면에 익숙한 분들에게는 이러한 면은 또다른 장점이 될 수도 있겠지만, 커맨드 라인에서 직접 git 명령어를 입력하고, 저장소의 상태를 확인하는 사람들에게는 워크 플로우가 웹과 커맨드라인으로 나눠진다는 게 영 장점이지만은 않습니다.
물론 Github에서 제공하는 API를 직접 이용하는 방법도 있긴있겠습니다만, 매우 번거로운 작업입니다. Github에서는 이러한 문제를 해결하기 위해서 저장소 생성, 포크 및 풀리퀘스트 등 주요한 기능을 커맨드라인 인터페이스로 제공해주는 Hub라는 git 명령어의 확장 인터페이스를 제공하고 있습니다. Hub라는 이름은 git + hub = Github 라는 공식에서 나온 이름도 참 앙증맞습니다.
이 글은 Hub를 설치하고 실제 커맨드라인에서 풀리퀘스트를 보내는 과정을 다룹니다. 기본적으로 Github의 풀리퀘스트 기능 정도는 익숙하다는 걸 전제로(최소한 개념 정도는 이해하고 있다는 전제로) 이야기합니다.
먼저 Hub를 사용하기 위해서는 공식 홈페이지를 참조해 프로그램을 설치해줄 필요가 있습니다. 우선 hub는 git과 ruby에 의존적인 프로그램이므로 시스템에 이러한 프로그램들이 있는지 확인해야합니다.
1 2 3 4 5 6 7 |
|
위의 프로그램들이 설치돼있다면 이제 Hub를 설치할 차례입니다. 맥에서는 Homebrew[^brew]를 이용해 hub를 쉽게 설치할 수 있습니다.
1
|
|
리눅스 계열에서는 소스 코드를 다운로드 받아 직접 설치할 수 있습니다. 아래 과정을 따라 hub를 설치할 수 있습니다.
1 2 3 |
|
설치 시 ruby의 rake
명령어가 지원되어야합니다. rake
명령어가 없다면 gem install rake
로 먼저 rake를 설치해주시기 바랍니다. 또한 rake install
명령어 실행시 기본적으로 메인 시스템 상에 프로그램을 설치하므로 sudo
등을 붙여 관리자 권한으로 설치해야할 수도 있습니다.
설치가 정상적으로 끝났다면 아래와 같이 hub
명령어를 사용할 수 있습니다.
1 2 3 |
|
hub는 hub
명령어를 통해서 독자적으로 사용할 수도 있지만 git
명령어와 통합해서 사용할 수도 있습니다. hub alias
명령어를 실행하면 git
명령어와 통합하는 방법을 알려줍니다.
1 2 |
|
예를 들어 zsh 사용하고 있다면 ~/.zshrc
파일에, bash를 사용하고 있다면 ~/.bash_profile
파일에 eval "$(hub alias -s)"
을 추가해주면 쉘 초기화 시에 git와 hub 명령어를 통합시켜줍니다. git와 hub 명령어는 기능적으로는 겹치지 않으며, hub가 git를 보완하는 역할을 하고 있으므로 이렇게 사용하더라도 별다른 문제가 되지 않습니다.
이제 설치가 끝났으니 쉘을 재실행 시켜줍니다. git 명령어를 통해서 제대로 alias 되었는지 확인합니다.
1 2 3 |
|
Hub에서는 쉘에서 명령어 및 옵션 자동 완성을 위한 completion 파일을 제공하고 있습니다. 여기서 zsh을 기준으로 git 명령어의 자동완성을 확장하는 법을 설명합니다. 먼저 hub 자동 완성 파일을 다운로드 받아 적절한 위치(우분투의 경우 기본적으로 usr/local/share/zsh/site-functions
디렉토리. 정확한 위치는 각 운영체제 별 zsh 환경 설정 파일 위치에 따릅니다)에 복사해줍니다.
1 2 3 4 |
|
zsh을 다시 실행하면 아래와 같이 hub 명령어 사용시 자동완성이 적용되는 것을 알 수 있습니다.
1 2 3 |
|
git
명령어와 통합해서 사용중인 경우에는 .zshrc에 아래 라인을 추가해 git 명령어에서도 hub 명령어의 자동완성을 사용할 수 있습니다.
1
|
|
아래와 같이 자동 완성이 적용된 것을 확인할 수 있습니다.
1 2 3 |
|
Hub의 다양한 명령어에 대해서는 공식 저장소에 간략한 사용법들이 나와있습니다. 여기서는 Hub를 사용해 Github에 저장소를 만들어보고, Github의 핵심 기능중 하나인 풀리퀘스트를 실제로 보내보겠습니다. (단, 여기서는 같은 저장소의 브랜치간에 풀리퀘스트를 보냅니다.)
먼저 Git 저장소를 가진 디렉토리를 하나 생성해줍니다.
1 2 3 4 5 6 7 8 |
|
일반적으로 Git 저장소를 초기화할 때는 git init
명령어를 사용합니다. 여기서는 추가적으로 git create
명령어를 사용했습니다. 이 명령어는 hub를 통해 확장된 명령어로 현재 git 저장소로 Github에 저장소를 생성해줍니다. git remote
원격 저장소 설정이 제대로 되었는지 확인해보겠습니다.
1 2 3 |
|
이를 통해서 Github nacyot 계정에 pull-request 저장소가 추가되었다는 것을 알 수 있습니다.
우선 master 브랜치를 활성화시키기 위해 커밋을 하나 해보겠습니다.
1 2 3 4 5 6 7 8 9 10 |
|
이 명령어들은 일반적으로 git에서 사용하는 명령어들입니다. 이제 파일들이 정상적으로 추가되었는지를 확인하기 위해 이 저장소의 웹페이지를 띄워보겠습니다.
1
|
|
git browse
명령어도 hub 확장으로 현재 디렉토리에 위치한 git 저장소의 원격 저장소를 근거로 Github 페이지를 찾아 바로 웹브라우져를 열어줍니다. 이 명령어를 통해서 따로 웹브라우져를 실행시키지 않더라도 저장소의 Github 페이지를 바로 확인할 수 있습니다.
이제 pull-request 브랜치를 만들고 실제로 풀리퀘스트를 보내보도록 하겠습니다.
1 2 3 4 5 |
|
위의 명령어들 역시 git에서 일반적으로 사용하는 명령어들로 추가적인 설명은 생략하겠습니다. 간단히만 얘기하면 pull-request 브랜치를 만들고 hello.rb
파일을 추가하고 Github 저장소에도 추가했습니다. 이제 nacyot/pull-request 에는 master 브랜치와 pull-request 두 브랜치가 존재합니다.
여기서는 pull-request 브랜치에서 master 브랜치로 풀리퀘스트를 보내겠습니다.
1
|
|
git pull-request
명령어를 실행시키면 현재 디렉토리의 github 저장소와 브랜치를 기준으로 풀리퀘스트를 보내기 위한 메시지를 입력할 수 있도록 기본 에디터를 실행해줍니다.
1 2 3 4 5 6 |
|
주석을 통해서 풀리퀘스트가 어디로 보내지는지 확인할 수 있습니다. 풀리퀘스트 메시지를 완성하고 저장한 후 에디터를 종료하면 풀리퀘스트가 보내집니다.
1
|
|
풀리퀘스트를 보내는데 성공하면 위와 같이 풀리퀘스트의 url을 보여줍니다.
git pull-request
명령어는 아래와 같이 좀 더 명시적으로 사용할 수도 있습니다.
1 2 |
|
여기서 -m
플래그는 풀리퀘스트 메시지, -b
플래그는 풀리퀘스트 목적지, -h
플래그는 풀리퀘스트를 보내는 브랜치가 됩니다. -b
와 -h
플래그는 <계정명>:<브랜치명>
형식으로 기술하며, 저장소는 작업 디렉토리 저장소를 근거로 자동으로 유추됩니다. 마찬가지로 풀리퀘스트에 성공하면 풀리퀘스트의 url을 보여줍니다.
여기까지 git 명령어를 hub로 확장해서 커맨드라인에서 Github 작업을 좀 더 편하게 하는 방법을 소개했습니다. hub는 웹에서 해야하는 귀찮은 일들을 많이 덜어주는 프로그램입니다. Github에서 저장소를 만들고 풀리퀘스트를 보내려고 웹상에서 브랜치 지정해주고 하는 일은 간단한 작업이지만 꽤나 번거롭습니다. 특히 해당 ‘저장소’를 찾아서 들어가는 일도 반복되면 귀찮기만 한 일입니다. hub와 함께 좀 더 즐거운 Github 라이프가 되길 바랍니다 >_<
]]>homebrew
를 이용하면 ffmpeg
를 쉽게 설치할 수 있습니다.
1
|
|
제법 시간이 걸리지만 기다릴만 합니다.
동영상을 쪼개는 command line 명령과 옵션은 아래와 같습니다.
1 2 3 4 |
|
아래에 이를 이용한 예를 소개 합니다.
ex 1) 1시간 44분 25초 크기의 remotty_3rd_hangout.mp4 동영상 파일을 50분 크기로 쪼개고자 할 때.
1
|
|
ex 2) 50분짜리 remotty3_1.mp4 동영상 파일을 25분 크기로 쪼개고자 할 때
1
|
|
ex 3) 54분 25초 크기의 remotty3_2.mp4 동영상 파일을 25분 크기로 쪼개고자 할 때
1
|
|
원본 repository를 본인 계정으로 fork하고 작업합니다.
user.name & user.email 설정
1 2 |
|
upstream 설정
pull request 관련 설정
이를 위해서는, TDD에 관한 여러 가지 책이 있지만, 저의 경우는 Aaron Sumner의 “Everday Rails Testing with RSpec(https://leanpub.com/everydayrailsrspec)”가 쉽게 이해가 되더군요.
이 책의 내용 중 모델 챕터를 보면 쉽게 따라해 볼 수 있습니다.
TDD를 위한 젬 중에 대표적인 것은 RSpec
입니다. 이 젬을 중심으로 TDD를 위한 환경구축을 위해서 Aaron이 추천하는 젬 구성은 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 |
|
각 모델에 대해서는 아래의 3가지 정도를 test해 보면 됩니다.
create
메소드에 유효한 데이터를 넘겨 주면 에러 없이 모델 객체가 생성됨.행아웃은 리모트로 일하면서 유일하게 얼굴을 보면서 이야기 할 수 있는 시간 입니다. 흔치 않은 기회(?)라 다양한 이야기를 나누게 되는데 그러다 보니 의도치 않게 시간이 너무 걸립니다. 2시간을 했다고 하면 6명*2시간=12시간을 미팅하는데 쓴 셈인데..
다음 행아웃에서는 주제를 최소로 하고 페이스북이나 채팅으로 해결할 수 있는 문제는 그 전에 처리하기로 했습니다. 항상 시간을 효율적으로 쓸 수 있게 노력해야겠습니다 ㅠ
]]>일단 theme 리스트는 다음 링크에서 확인하시고..
https://github.com/imathis/octopress/wiki/3rd-Party-Octopress-Themes
http://www.evolument.com/blog/2013/03/02/top-10-plus-octopress-themes/
설치는 아래와 같이 하면 됩니다.
1 2 3 4 |
|
zsh 사용시 에러가 발생한다면
1 2 3 4 |
|
다음과 같이 합니다.
]]>post글 상단 정보에 author: Your Name
을 입력하면 되고
글 수정시에는 author에 이름을 추가하는 식으로 작업하면 될 것 같습니다.
지금 이 글을 예로 들면 다음과 같습니다.
1 2 3 4 5 6 7 8 |
|
요령은 글 내용 중에서 한 문장이 끝나는 다음 줄에 아래의 표시를 삽입해 주면 인덱스 페이지에서는 자동으로 more
링크가 생성되면 한 문장만 보이게 됩니다. more
버튼을 클릭하면 전체 글 내용을 볼 수 있게 됩니다.
1
|
|
아래 방법대로 약 한달 가량 remote 방식으로 협업을 해보니, 불편한 점이 한두가지가 아니었다.
나름 효율적으로 협업을 하기 위해, 각각의 활동에 최적화(?)된 도구를 선정하고 그것을 사용하기로 했었는데
가장 큰 문제는 사용해야 할 도구가 너무 많아 커뮤니케이션이 분산된다는 것이었다.
최근 협업 방식에 대해 Remotty하게 일하기 2라는 제목으로 새로운 포스트를 올렸다.
Remotty 그룹의 협업 방식에 대해서는 Remotty하게 일하기 2 글을 참조하길 바란다.
이 포스트를 통해 Remotty 그룹에서 협업하는 방식을 소개합니다.
혹시 더 효율적이고 세련된 방식으로 일하고 계시거나 알고 계신 분들은 댓글이나 메일로 한수 가르쳐주시면 감사하겠습니다 ^^
admin@remotty.com
가벼운 생각들, 각종 팁, 유용한 지식 등 온갖 잡다구리한 정보들은 페이스북 그룹에 포스팅 한다.
멤버들은 각자 자신의 생각들을 댓글을 통해 등록을 하면서 점점 그 생각들을 발전시켜 나간다.
무언가를 결정할 사항이 생겼을 땐, 페이스북 설문조사 기능을 활용한다.
아이디어로 이어가고 싶은 것들은 trello에 등록을 하여 점점 구체화시켜 나간다.
대화를 통해 실시간 논의가 필요한 경우는 가겹게 페이스북 메신저를 활용한다.
대부분의 이야기들이 페이스북 타임라인에서 오고가기 때문에 페이스북 메신저를 사용하여 대화하는 것이 편리하다.
페이스북 메신저를 통한 대화는 시간, 장소에 관계없이 필요성을 느끼는 누군가가 먼저 시작을 하면 된다.
좀 더 진지하게 집중적으로 논의를 해야 할 경우엔 구글 행아웃을 활용한다.
태스크 관리는 trello를 활용한다.
Jira, Redmine과 같은 이슈트래커 도구는 태스크 관리 도구로는 쓸데없이 무겁다.
태스크 관리 뿐만 아니라, 구체화 시켜나갈 아이디어가 있을때에도 trello에 등록을 한다.
문서 공유는 에버노트와 구글드라이브를 사용한다.
임시적인 성격의 문서, 메모등의 글들은 에버노트를 사용하고, 산출물 성격의 문서들은 구글 드라이브를 사용한다.
에버노트가 문서를 작성하기는 편하지만, 공동 작업이 불가능한 단점이 있다.
그래서 산출물 성격의 문서는 구글드라이브로 관리한다.
소스코드, 문서 이외의 기타 파일은 dropbox를 사용한다.
주로 디자이너와 협업을 위한 파일 공유로 많이 사용한다.
MadEye(https://madeye.io)를 통해 정기적으로 단체 코딩을 한다.
여러 사람이 같은 화면을 보며 함께 코딩을 할 수 있고, 구글 행아웃과도 연결되기 때문에 서로 대화하면 함께 코딩할 수 있다.
굉장히 재밌고 편리하게 pair programming을 할 수 있는 도구이다.
소스 관리는 github가 정답이다. 이보다 편할 순 없다!
그룹 내에서 습득한 지식은 가능한 tech blog에 정리하여 공유하고 있다.
octopress를 사용하여 작성을 하고 github page(http://remotty.github.io)에 올려 공유한다.
현재 시도중이며 아직 확정되지 않은 방안들입니다.
기본적으로 일주일에 한번 토요일밤에 행아웃을 통해 화상회의를 진행합니다. 그전에 facebook을 통해 각자 한일과 다음주에 할일을 적습니다. 이것은 정확하고 강압적으로 적는것이 아니라 우리가 함께 일하고 있다라는 걸 상기시켜주는 목적이 더 큽니다. 부담갖지 말아요~
]]>1
|
|
1 2 |
|
1 2 |
|
1 2 3 |
|
위의 작업을 git clone 시 한번만 수행합니다.
새로운 글을 작성하기 위해서는 아래와 같이 명령을 실행합니다.
1
|
|
1
|
|
1
|
|
1 2 3 |
|
2014년 1월 30일 수정 보완함. 최효성
]]>