tag: 번역

알고리즘 학습에 대한 조언

2018년 7월 4일

Buck Shlegeris의 My advice on studying algorithms를 번역했다.


알고리즘 학습에 대한 조언

소프트웨어공학 면접에서는 화이트보드 알고리즘 질문을 종종 냅니다. 이런 질문을 어떻게 공부해야 하는지 조언을 하려고 합니다. (저는 구글과 애플을 포함한 수많은 화이트보드 면접을 통과했습니다. 그리고 프로그래머가 이런 알고리즘 면접을 준비하도록 돕는 일이 제 직업의 일부입니다. 게다가 다양한 분야의 개발자를 대상으로 200회 이상의 기술 면접을 치뤘습니다.)

이 글은 Triplebyte이 아닌 제 자신으로서 쓰는 글입니다.

알고리즘 외에도 면접에 관한 여러 주제가 있습니다. 이런 주제는 Triplebyte의 포스트에서 잘 다루고 있습니다. 이 글에서 중요하게 다루려고 하는 내용은 Triplebyte의 포스트에서 2번 항목에 해당합니다.

배경: 왜 회사는 알고리즘 문제를 낼까요?

실생활에서 프로그래머가 이진트리 검색이나 그래프 탐색 알고리즘을 구현하는 시간은 거의 존재하지 않습니다. 그런데 왜 회사는 알고리즘에 대해 많은 질문을 낼까요?

이 질문을 존 왓슨과 코난 도일의 관점으로 해석할 수 있습니다. “회사가 알고리즘 문제를 내는 것이 왜 유용한가?”, 그리고 “어떤 회사가 알고리즘 질문을 하는 실제 일반적 원리가 무엇인가?”가 그 관점입니다.

먼저 알고리즘을 물어보는 이유를 설명하려고 합니다. 그리고 더 나아가 이 유행에 대한 냉소적인 입장에서 설명합니다.

먼저 직업 프로그래머 대다수가 아주 기초적인 일을 수행하지 못합니다. 예를 들어 고객 객체 목록이 있고 각 고객 객체에 구입 객체 배열이 존재합니다. 지난 주에 가장 많이 구입한 고객 다섯 명의 이름을 찾으려고 합니다. 제 예상에는 직업 프로그래머의 50%가 이 문제를 30분 이내에 풀지 못합니다. 이런 사람들을 실수로라도 채용하고 싶지 않을겁니다.

조금 덜 비관적으로 가정해봅시다. 프로그래밍 일을 위해 누군가 면접을 볼 때 어렵고 혼란스러운 문제를 잘 풀어낼 수 있는지 알아내려고 할겁니다. 모든 세세한 내용을 머리에 담고 있어야 풀 수 있는 것을 말이죠. 실생활에서 혼란스럽고 복잡한 문제가 존재하는 이유는 그 프로젝트를 몇 주 동안 봐야 할 만큼 큰 규모고 소프트웨어의 여러 부분을 동시에 고려해야 하기 때문입니다. 하지만 면접은 일반적으로 그렇게 깊은 프로그래밍 문제를 다룰 만큼 시간이 넉넉하지 않습니다. 그래서 규모가 크기 때문에 복잡한 문제를 물어보는 것보다 짧고 복잡한 질문을 물어보는 것입니다.

그럼 어떻게 복잡하지만 쉽게 설명할 수 있는 짧은 코딩 문제를 낼 수 있을까요? 그런 관점으로 생각해보면 제 생각에 여기서는 알고리즘이 좋은 선택입니다. 알고리즘은 컴퓨터 과학에서 대부분의 소프트웨어 엔지니어가 알고 있는 복잡한 분야며 쉽게 설명할 수 있고 구현하기 힘든 문제가 많이 존재합니다.

여기 조금 냉소적인 설명이 따라옵니다.

면접 프로세스는 이상할 정도로 끈적합니다. 엔지니어링팀은 팀의 기술 면접을 통과한 사람으로만 구성되어 있습니다. 그래서 모두가 면접이 옳은 방식이라고 믿게 되고 면접 과정이 소프트웨어 엔지니어링 능력을 측정하는데 매우 정확하다고 생각하게 됩니다. 그래서 회사에 알고리즘 면접 문화가 생기고 나면 그 이후로 바꾸기가 어려워집니다.

또한 모두가 알듯 구글은 10년 전에 놀라운 팀이 있었습니다. (지금은 그 당시보다 적습니다.) 그 당시에 구글은 알고리즘 면접 질문을 했습니다. 대부분의 회사가 자신들이 구글이 아니라는 점에 조금 불안했는지 (알다시피 최고의 지원자는 구글에 다 잃었으니까요), 이 회사도 구글 면접 과정을 따라하기 시작했습니다.

최악의 경우로 보면 알고리즘 면접은 기괴하고 못살게 구는 절차로 바뀌어 버립니다. 가끔 회사에서 무작위 난제를 내는 것이 위대한 지원자를 찾는 비밀 병기라고 단단히 착각해버려서 그런 생각을 바꾸는 일이 불가능한 경우가 있습니다.

종합해서 말하자면, 저는 이런 전통적이고 어려운 알고리즘 문제를 면접에서 안냈으면 합니다. 최악으로는 알고리즘 문제가 극단적으로 나쁜 면접 질문이 될겁니다. 제가 특히 싫어하는 질문은 이런 상황을 위해 따로 적어뒀습니다. 알고리즘 질문은 난제나 여러 통찰을 요구하는 경우에 특히 나쁜 질문입니다. (만약 알고리즘 면접 과정을 만들고 싶다면 언제든지 이메일을 보내시기 바랍니다. 이런 문제가 없는 질문을 하는 방법에 대해 더 자세한 의견을 드릴 수 있습니다.)

어떻게 공부하나요?

추가: Haseeb Qureshi의 블로그 포스트를 읽었는데 이 글에 동의합니다. 그리고 이 글이 좀 더 상세하다고 생각합니다. “일반 학습 전략”과 “프로그래밍 면접 공부 가이드” 부분을 읽으세요.

저는 알고리즘 문제에 답하려면 두 가지 다른 기술이 필요로 하다고 생각합니다. 첫째로 모든 대표적 알고리즘과 자료 구조 문제를 알아야 합니다. 둘째로는 부담되는 상황에서 알고리즘 논리를 화이트보드에 빠르게 풀어나갈 수 있어야 합니다. 이 두 주제를 나눠서 얘기해보려고 합니다.

표준 알고리즘 자료

회사의 시험을 준비하는데 있어 습득해야 하는 거의 필수적인 핵심 알고리즘 모음이 있습니다. 회사에서는 이런 목록에 들어있지 않은 질문은 하지 않으려고 합니다. 좋은 프로그래머 다수가 이 목록에 없는 질문에 대해서는 답을 모르기 때문이며 그래서 목록 외 질문을 냈다가 회사는 좋은 사람을 뽑는데 실패하게 됩니다.

알아야 할 자료구조는 다음과 같습니다.

  • list 구조: 배열, 동적 배열, 링크드 리스트(linked list)
  • set과 map 구조: 해시맵, 이진 검색 트리, 힙

여기서 언급한 자료구조는 필수 메소드가 어떻게 구현되어 있는지, 런타임은 어떻게 동작하는지 알아야 합니다. (list의 필수 메소드는 set, get, pushAtEnd, popAtEnd, insertByIndex, removeByIndex, set의 필수 메소드는 insert, remove, contains? 입니다.) 자료구조 구현을 어떻게 사용하는지 알아야 합니다. 예를 들면 getNearestElementTo(x) 메소드를 구현할 수 있어야 합니다. 이 메소드 즉, x와 가장 가까운 값을 찾는 구현을 하려면 이진 검색트리를 알아야 합니다.

이 문제를 해결하는데 이런 내용을 알아야 합니다.

  • 이진 검색트리 구현에 균형을 맞추는 코드가 필요하다는 점을 알아야 하지만 세부 내용은 몰라도 괜찮습니다. (선택 자료: 자기 균형 BST을 어떻게 구현하는지 빠르게 배우고 싶다면 이 트립을 참조하세요. 어떻게 레드블랙 트리가 동작하는지 이해하고 싶다면 좌편향 레드블랙 트리 또는 2-3-4 트리를 배우세요.)
  • 큐를 스택 두 개로 구현할 수 있다는 점을 알아야 합니다.

다음 알고리즘은 어떻게 구현하는지 알아야 합니다.

  • 그래프 알고리즘: 너비 우선 탐색(breadth first search), 깊이 우선 탐색(depth first search), 다익스트라 알고리즘 (dikstra’s algorithm)
  • 빠른 정렬 알고리즘 하나. 병합 정렬(mergesort) 또는 퀵 정렬(quicksort)
  • 배열에서 수행하는 이진 검색. 이 알고리즘은 제대로 작성하기 매우 까다롭고 대략적으로 알고리즘을 이해하고 있더라도 코드로 작성해볼 가치가 있습니다.

그리고 Big O 표기법도 대충이라도 편하게 사용할 수 있어야 합니다.

이 모든 내용을 어떻게 배워야 하나요? 제가 가장 좋아하는 자료는 Skiena의 Algorithm Design Manual입니다. 위에서 언급한 모든 내용을 챕터 26에서 다룹니다. 이 책을 좋아하는 이유는 저술 방식이 참여를 유도하고 각 부분에서 중요한 자료에 잘 초점을 맞추고 다루는데 이런 방식은 중요하다고 생각합니다. 이 책은 인터넷에서 무료로 찾을 수 있습니다. 이 책의 단점은 예제가 C로 작성되었다는 점인데 C를 읽지 못하는 개발자라면 접근성이 좋지 않습니다. 저는 챕터 16, 12는 꼭 읽어야 한다고 생각합니다. 이 부분은 인터뷰에서 나올 가능성이 극히 낮지만 필요 없다고 생각하는 부분이 진정 핵심적인 부분을 잘 보강한다고 생각하기 때문입니다.

이런 부분에 대략적인 설명을 보고 싶다면 Craking the Coding InterviewInterviewCake.com의 설명이 좋습니다.

저는 Skiena의 책이 극단적일 정도로 건조하고 딱딱한 유명 CLRS 교재보다 낫다고 생각합니다.

그래프 알고리즘에 대한 글을 쓴 적이 있는데 참고가 되었으면 좋겠습니다.

표준 알고리즘 기술

여기까지 인터뷰에 핵심적으로 필요한 부분을 확인했습니다. 이제 다른 종류의 프로그래밍 기술로 무엇을 테스트하는지 확인하고 제가 선호하는 학습 자료도 함께 확인합니다.

이런 기술에 있어서는 Cracking the Coding Interview(이하 CtCI) 책이 가장 유용합니다. 이 책에 대해서 작성한 글입니다.

알고리즘 면접 문제 중 가장 일반적이고 중점적으로 다뤄지는 요소는 다음과 같습니다.

  • 동적 프로그래밍: Skiena 책의 챕터 8 또는 CtCI에서 이 주제의 챕터에서 학습합니다.
  • 재귀: CtCI에 이 주제에 대한 멋진 챕터가 있습니다.
  • 유명 자료 구조를 반복(iterating)하는 문제: CtCI에서 각각의 자료 구조를 다룰 때 이 문제도 함께 다룹니다. 예를 들어 BST에서는 CtCI 트리 챕터를 참고할 수 있습니다.
  • 문제 해결을 위해 빠른 자료 구조를 조합하기: 이런 문제에 대한 예제는 이 글에서 확인할 수 있습니다.

CtCI에서 살펴볼 수 있는 많은 문제를 살펴보는 방법이 제가 드리는 가장 주요한 조언입니다. 이 문제에서 가장 중요하다고 생각하는 부분은 위 목록과 같습니다.

이런 부류의 문제를 어떻게 학습하는지에 대해 일반적인 생각은 이렇습니다. 제 생각엔 답안을 “훔쳐보는” 일은 그래도 괜찮다고 생각합니다. 면접 문제 푸는 일을 내던지고 아예 포기하는 것보다는 문제 풀다가 막히면 해결책을 보는 방법이 차라리 나은 접근이기 때문입니다.

알고리즘 면접에서 성공하기 위한 비기술적 측면

이런 질문은 실제로 부담되는 환경에서 답하는 연습을 해야 합니다. 진짜 사람이 질문하는 상황에서 말이죠. 이 부분에 대해서는 Triplebyte의 블로그 포스트에서 다루고 있고 2, 3, 7번을 읽어보기 바랍니다.

알고리즘과 자료구조에 대해 더 배우기

취업 목적 학습을 넘어서 본인을 위해 즐겁게 알고리즘과 자료구조를 배우고 싶다고 가정해봅시다. 어떻게 더 배워야 할까요?

가장 쉬운 방법은 위에서 필수로 배워야 한다고 한 핵심 자료 구조에 포함되지 않는 자료 구조 중 상대적으로 간단한 자료 구조를 학습하는 방법입니다. 트립, 스킵 리스트, 증강 이진검색트리, 서로소 집합 자료구조가 그 예로 모두 쉽게 이해할 수 있는 편이며 모두 멋진 알고리즘입니다.

자료구조 주제 중 이해하기 어렵지만 노력해서 이해하면 좋은 주제도 있습니다. 예를 들어 이 슬라이드에서는 이진트리와 2-3-4 트리를 설명합니다.

흥미로운 자료 구조를 배울 수 있는, 제가 좋아하는 자료는 다음과 같습니다.

  • Skiena의 챕터 12와 이후 챕터
  • 스탠포드의 멋진 강의인 CS166. 이 강의의 슬라이드는 멋지고 읽기 좋은 편입니다. 저는 여기서 다룬 문제가 즐거웠습니다. 자료구조와 더 놀고 싶다면 이 프로젝트 아이디어 핸드아웃을 추천합니다.
  • 저는 이런 작업처럼 그다지 어렵지 않은 자료 구조 문제를 아마추어 활동으로 재미삼아 한다는 점이 자랑스럽습니다. 이 문제를 풀기 위한 해결책으로 고급 자료구조에서 얻은 몇 아이디어를 적용했다는 점이 멋지지 않나 생각합니다.

자료

node.js의 module.exports와 exports

이 둘은 무엇이고 어떻게 사용하고 사용하지 말아야 하는가, 번역.

2018년 7월 2일

lazlojuly의 글 Node.js module.exports vs. exports을 번역했다.


node.js의 module.exports와 exports

(노트: 이 글은 Node.js 6.1.0 릴리즈 이후에 작성되었습니다.)

요약

  • module.exportsrequire() 함수를 사용했을 때 반환 받는 변수라고 생각해봅시다. 비어 있는 객체가 기본값이며 어떤 것으로도 자유롭게 변경할 수 있습니다.
  • exports 자체는 절대 반환되지 않습니다! exports는 단순히 module.exports를 참조하고 있습니다. 이 편리한 변수를 사용하면 모듈을 작성할 때 더 적은 양의 코드로 작성할 수 있습니다. 이 변수의 프로퍼티를 사용하는 방법도 안전하고 추천하는 방법입니다.
exports.method = function () { /* ... */ }
// vs.
module.exports.method = function () { /* ... */ }

간단한 모듈 예제

먼저 예제로 사용할 코드가 필요합니다. 간단한 계산기로 시작합니다.

// calculator.js

module.exports.add = (a, b) => a + b

다음처럼 사용할 수 있습니다.

// app-use-calculator.js

const calculator = require('./calculator.js')
console.log(calculator.add(2, 2) // 출력: 4

모듈 감싸기

Node.js는 require()로 모듈을 불러올 때 함수 래퍼(wrapper) 형태를 사용해 내부적으로 감싸서 호출합니다.

(function (exports, require, module, __filename, __dirname) {
  // calculator.js의 내용은 여기에 추가해 실행합니다.
  module.exports.add = (a, b) => a + b
});

module 객체

변수 **“module”**은 객체로 현재 모듈을 나타냅니다. 이 변수는 각 모듈에 지역적이며 비공개(private) 입니다. (모듈 코드에서만 접근할 수 있습니다.)

// calcualtor-printed.js

module.exports.add = (a, b) => a + b
console.log(module)

// 이 모듈을 require('./calculator-printed.js')로 호출하면 다음과 같은 결과를 볼 수 있습니다.
//
// Module {
//   id: '/Users/laz/repos/module-exports/calculator-printed.js',
//   exports: { add: [Function] },
//   parent: 
//     Module { ... }
//   filename: '/Users/laz/repos/module-exports/calculator-printed.js',
//   loaded: false,
//   children: [],
//   paths: [ ... ]
// }

module.exports

  • 객체 참조로 require() 호출을 하면 받는 값입니다.
  • Node.js에 의해 자동으로 생성됩니다.
  • 일반 JavaScript 객체를 참조합니다.
  • 또한 기본값은 비어 있습니다. (앞서 코드에서는 add() 메소드를 추가했습니다.)

다음 두 방법으로 module.exports를 사용합니다.

  1. 공개(public) 메소드를 붙여 사용합니다. (앞서 작성한 예제가 이 방법입니다.)
  2. 직접 작성한 객체나 함수로 대체하는 방식을 사용합니다.

왜 대체하는 방식으로 사용할까요? 대체하는 방식으로 사용하면 다른 클래스를 임의적으로 수정해 반환하는 것도 가능합니다. 다음 ES2015 예제를 보겠습니다.

// calculator-base.js

module.exports = class Calculator {
  add(a, b) {
    return a + b
  }
  substract(a, b) {
    return a - b
  }
}

이 calculator-base 예제에서는 클래스를 내보냈습니다. 다음 예제에서는 “Calcaulator” 클래스를 확장한 후, 클래스의 개체를 내보냅니다.

// calculator-advanced.js

const Calculator = require('./calculator-base.js')

class AdvancedCalculator extends Calculator {
  multiply(a, b) {
    return a * b
  }
  devide(a, b) {
    return a / b
  }
}

module.exports = new AdvancedCalculator()
// app-use-advanced-calculator.js

const calculator = require('calculator-advanced.js')

console.log(calculator.add(2, 2))      // 출력: 4
console.log(calculator.multiply(3, 3)) // 출력: 9

exports 별칭(alias)

  • exports는 편의 변수로 모듈 작성자가 코드를 덜 작성하도록 돕습니다.
  • 이 변수의 프로퍼티를 사용하는 방식은 안전하고 추천하는 방법입니다. (예를 들면 exports.add = function () { /* ... */ } 식으로)
  • exportsrequire() 함수에서 반환되지 않습니다. (module.exports는 반환하지만요!)

좋은 예제와 나쁜 예제를 확인합니다.

// calculator-exports-exmaples.js

// 좋음
module.exports = {
  add(a, b) { return a + b }
}

// 좋음
module.exports.subtract = (a, b) => a - b

// 가능함
exports = module.exports

// 위에서 작성한 코드보다 간단하고 짧은 코드
exports.multiply = (a, b) => a * b

// 나쁨, exports는 바깥으로 전달되지 않음
exports = {
  divide(a, b) { return a / b }
}

노트: module.exports를 함수나 객체로 대체하는 경우는 일반적인 접근법입니다. 이렇게 대체하면서도 여전히 exports 축약을 사용하고 싶다면 exports를 새로 대체할 객체를 가리키도록 설정해야 합니다. (위에서도 이런 방법을 사용헀습니다.)

exports = module.exports = {}
exports.method = function () { /* ... */ }

결론

변수명은 exports지만 실제로 내보내지 않는다는 사실은 좀 혼란스러울 수 있습니다. 막 node.js를 시작한 사람이라면 특히 그럴겁니다. 공식 문서에서도 이 부분이 조금 이상합니다.

exports와 module.exports의 관계가 마술처럼 느껴진다면 exports는 무시하고 module.exports만 사용하도록 지침을 드립니다.

제 경우엔 이런 코드가 마술이라고 생각하지 않습니다. 개발자라면 사용하는 플랫폼과 언어가 어떻게 동작하는지 깊게 이해하려는 노력을 항상 해야합니다. 이처럼 깊게 이해하는 과정에서 프로그래머는 값진 자신감과 지식을 얻으며 코드 품질, 시스템 구조와 생산성에 긍정적인 영향을 주게 됩니다.

제 글을 읽어주셔서 감사합니다. 의견이나 생각은 언제나 환영하니 덧글로 남겨주세요.

공변성과 반공변성은 무엇인가?

2018년 5월 2일

Stephan BoyerWhat are covariance and contravariance?을 번역한 글이다.


공변성과 반공변성은 무엇인가?

서브타이핑은 프로그래밍 언어 이론에서 까다로운 주제다. 공변성과 반공변성은 오해하기 쉬운 주제이기 때문에 까다롭다. 이 글에서는 이 용어를 설명하려고 한다.

이 글에서는 다음과 같은 표기법을 사용한다.

  • A <: BAB의 서브타입이라는 뜻이다.
  • A -> B는 함수 타입으로 함수의 인자 타입은 A며 반환 타입은 B라는 의미다.

동기부여 질문

다음과 같은 세 타입이 있다고 가정하자.

Greyhound <: Dog <: Animal

GreyhoundDog의 서브타입이고 DogAnimal의 서브타입이다. 서브타입은 일반적으로 추이적 관계(transitive)를 갖는다. 그래서 GreyhoundAnimal의 서브타입이라 할 수 있다.

여기서 질문이다. 다음 중 Dog -> Dog의 서브타입이 될 수 있는 경우는 어느 것일까?

  1. Greyhound -> Greyhound
  2. Greyhound -> Animal
  3. Animal -> Animal
  4. Animal -> Greyhound

이 질문에 어떻게 답할 수 있을까? Dog -> Dog 함수를 인자로 받는 f 함수를 살펴보자. 반환 타입에 대해서는 크게 생각하지 않는다. 구체적으로 적어보면 다음과 같다. f : (Dog -> Dog) -> String.

이제 f를 다른 함수인 g와 함께 호출해보자. g에는 위에서 나열했던 각각의 함수를 넣어서 어떤 일이 나타나는지 관찰한다.

g : Greyhound -> Greyhound로 가정하면 f(g)는 타입 안전(type safe)한가?

아니다. 함수 f는 인자 g를 사용하면서 Dog의 다른 서브타입, 예를 들면 GermanShepherd를 사용해서 호출할 수도 있기 때문이다.

g : Greyhound -> Animal로 가정하면 f(g)는 타입 안전한가?

아니다. 1과 동일한 이유다.

g : Animal -> Animal로 가정하면 f(g)는 타입 안전한가?

아니다. f에서 인자 g를 호출하면서 개가 어떻게 짖는지 그 반환 값을 얻으려고 할 수 있다. 하지만 모든 Animal이 짖는 것은 아니다.

g : Animal -> Greyhound로 가정하면 f(g)는 타입 안전한가?

그렇다. 이 경우는 안전하다. f 함수는 인자인 g를 호출할 때 어떤 종류의 Dog든 사용할 수 있다. 모든 DogAnimal이기 때문이다. 또한 반환값은 Dog로 가정할 수 있는데 모든 GreyhoundDog이기 때문이다.

무슨 일이 일어나고 있나요?

결과적으로 다음 경우에 안전하다.

(Animal -> Greyhound) <: (Dog -> Dog)

반환 타입은 간단하다. GreyhoundDog의 서브타입이다. 하지만 인자 타입은 반대다. AnimalDog의 수퍼타입이다!

이 독특한 동작 방식을 적당한 용어를 사용해서 설명한다. 함수 타입에서 반환 타입은 공변적(covariant) 이고, 인자 타입은 반공변적(contravariant) 이다. 반환 타입의 공변성은 A <: B(T -> A) <: (T -> B)로 적용된다는 뜻이다. (A<:의 좌측에, B는 우측에 남아 있다.) 인자 타입의 반공변성은 A <: B(B -> T) <: (A -> T)로 적용된다는 의미다. (AB가 위치를 바꾸게 된다.)

재미있는 사실 타입스크립트에서는 인자 타입이 이변적(bivariant), 즉 공변성과 반공변성을 동시에 지닌다. 뭔가 말이 안되는거 같겠지만 말이다. (TypeScript 2.6부터 --strictFunctionTypes 또는 --strict를 사용하면 이 문제를 해결할 수 있다.) Eiffel은 인자 타입을 반공변적이 아닌 공변적으로 잘못 구현했다.

다른 타입은?

또 다른 질문이다. List<Dog>List<Animal>의 서브타입이 될 수 있을까?

답변하기 좀 미묘하다. 만약 목록이 불변이면 맞다고 답할 수 있다. 하지만 가변적이라면 당연히 안전하지 않다!

이유를 생각해보자. 나는 List<Animal>이 필요한데 List<Dog>를 넘겨줬다고 해보자. 나는 당연히 List<Animal>을 갖고 있다고 생각하고 Cat을 집어넣었다. 이제 List<Dog>Cat이 들어있게 된다! 타입 시스템은 이런 동작을 허용하지 않을 것이다.

불변 목록의 타입이라면 타입 파라미터가 공변적일 수 있지만 가변 목록의 타입은 반드시 공변적이지도, 반공변적이지도 않아야(invariant) 한다.

재미있는 사실 자바에서의 배열은 가변적이면서도 공변적이다. 물론 부적절하다.


추가로, 원문의 덧글 중에 시각적으로 잘 설명하는 자료가 있었다.

오픈소스 메인테이너는 당신에게 빚진 적 없다

2018년 3월 31일

Mike McQuaid의 Open Source Maintainers Owe You Nothing를 번역한 글이다. 이 번역은 CC-BY-NC-SA를 준수한다.


오픈소스 메인테이너는 당신에게 빚진 적 없다

이 포스트는 지난 10년간 오픈소스 커뮤니티에 참여한 경험과 8년간 Homebrew의 (현재 시점에서 누구보다도 가장 오래 참여한) 메인테이너로서 겪은 경험에 따라서 작성했다.

오픈소스 소프트웨어 메인테이너에게 번아웃은 큰 문제다. 이 문제는 피할 수 있다. 메인테이너는 오픈소스 프로젝트를 즐겁고 건강하게, 오랜 기간동안 생산적으로 참여할 수 있다. 어떻게 가능할까? 바로 다른 메인테이너, 기여자, 또는 소프트웨어를 사용하는 사용자에게 의무감을 갖지 않는 것이다. 이 프로젝트가 개인적으로 이득을 보고 있는 상황도 상관 없다. (예를 들어 자기 홍보 가치가 있다거나 기부를 받고 있는 상황에도.)

메인테이너가 의무를 갖지 않는다는 근거를 명시한 경우가 있는가? 실제로 존재한다. 오픈소스 라이센스 자체에 말이다. 먼저 깃헙에서 가장 인기있는 오픈소스 라이센스MIT 라이센스를 보자.

이 소프트웨어는 상품성, 특정 목적 적합성, 그리고 비침해에 대한 보증을 포함한 어떠한 형태의 보증도 명시적이나 묵시적으로 설정되지 않은 “있는 그대로의” 상태로 제공된다. 소프트웨어를 개발한 프로그래머나 저작권자는 어떠한 경우에도 소프트웨어나 소프트웨어의 사용 등의 행위와 관련하여 일어나는 어떤 요구사항이나 손해 및 기타 책임에 대해 계약상, 불법행위 또는 기타 이유로 인한 책임을 지지 않는다.

이 난해한 법률 용어를 평범한 문장으로 살펴보자. (참고로 나는 변호사가 아니다):

  • 지금 제공하는 소프트웨어는 전부 메인테이너가 제공한다는 점에 동의한 것이다 (즉, 버그를 포함한 모든 것을 뜻한다)
  • 메인테이너는 모든 사용자와 사용 사례에서 동작하는 것을 보장하지 않는다 (문서화되어 있더라도 말이다)
  • 메인테이너는 어떤 소프트웨어 사용에서든 발생하는 어떤 문제에도 법적인 책임이 없다. (당신이 입은 피해를 고치기 위해 돈을 지불하는 경우에도)
  • 소프트웨어를 어떤 방식으로든 사용하려면 위 내용에 대해 필수적으로 동의해야 한다.

이 내용은 단순히 MIT 라이센스만 그런 것이 아니다. BSD 2-Clause, GPLv3, MPL v2.0, Apache v2.0, Unlicense 그리고 거의 대부분의 다른 오픈소스 라이센스에도 법적 책임에 대한 한계를 두고 어떤 보증도 명시적으로 제공하지 않는다. 요약하면, 어떤 오픈소스 소프트웨어를 사용했고 이 소프트웨어가 실서버의 모든 데이터를 지워버렸다고 해도 어느 누구에게도 소송을 걸 수 없다. 소프트웨어를 사용하는 것으로 어떤 부정적 결과도 개인의 책임이라고 동의했기 때문이다.

하지만 실제로는 어떤 문제가 발생하면 메인테이너는 문제를 빠르게 해결해주기도 하고 마치 회사처럼 사과를 하기도 한다. 이 부분이 번아웃의 가장 큰 원인이다. 대다수의 오픈소스 소프트웨어는 자유 시간의 자원 봉사로 개발된다. 하지만 오픈소스 메인테이너와 사용자가 그 관계를 회사/고객 같은 관계로 생각한다면 이는 지속하기 어렵다.

대다수의 메인테이너가 오픈소스 소프트웨어에 참여하기 시작한 이유는 재미도 있고 자신의 문제를 해결하고 있기 때문이다. 재미 대신 말도 안되는 의무만 지속되는 상황에서, 돈도 못버는데 지나치게 시간을 쏟고 재미 없는 일만 증가한다면 그건 메인테이너를 갈아넣는 일이다. 이런 상황에서 논쟁이 있을 만한 결정을 내리거나 그런 과정에서 모욕적인 얘기를 들으면 그 친구나 가족은 분명 “오픈 소스가 이런 고통을 감내할 만큼 가치있는 일이냐”고 질문할 것이다.

이 문제를 어떻게 고칠까? 오픈소스 생태계에 있는 모두가 오픈소스의 법적 현실을 수용하는 것이 답이다. 이 답을 실제로 적용하면 이런 의미다.

  • 메인테이너: 프로젝트에서 더이상 즐거움을 느끼지 못한다면 더 이상 하지 않는다. 사용자가 프로젝트에 대해 이야기할 때 무례하게 군다면 접근을 막아버리고 그 사람들이 요청하는 것을 하지 않는다. 버그를 만들었다고 미안해 하지 않는다. 모든 소프트웨어에는 결함이 있기 마련이다. 세상에 무언가를 내놓고도 어떤 댓가도 바라지 않았다는 점에서 스스로를 자랑스러워 하자.
  • 기여자: 결정은 메인테이너가 내리도록 하고 모든 관련 기여 문서를 읽었는지 확인한다. 메인테이너는 프로젝트를 운영하는 사람이고 궁극적으로 그들의 단어가 남게 된다. 어떤 식으로 프로젝트가 동작하는지 (사실, 무엇이든지) 당신에게 가르치기 위해서 메인테이너가 존재하는 것이 아니다.
  • 사용자: 프로젝트에 이슈를 생성하거나, 풀리퀘스트를 열거나 덧글을 작성할 때는 사람들이 자신들의 시간을 써서 소프트웨어를 만들고 무료로 사용하게 했다는 점에 감사하는 마음을 담아야 한다. 불만이든 어떻게 할 수 없는 부정적인 것들은 본인의 몫이다. (적어도 보이는 곳에서 말하지 말아야 한다.) 본인 스스로에게 더 시간을 들여 문제를 풀지 않으면서 다른 사람에게 물어서 해결하려고만 한다면 다른 사람이 그 문제를 해결해줄 거라고 기대하지 말아라. 즉, 어떤 도움이든 물어보기 전에 모든 문서를 읽어보고 스스로 문제를 해결하려고 노력하라는 의미다.

이런 역할을 모두 지킨다면, 메인테이너가 실제로 있지도 않은 의무에 치여서 프로젝트가 접히는 경우가 더 적어질 것이다. 오픈소스 생태계에서 본인이 어떤 역할로 참여하고 있는지 잘 인지한다면 더 행복한 메인테이너, 기꺼이 돕는 기여자와 감사하는 사용자가 될 수 있을 것이다.

Org-mode 기초부터 시작하기

Emacs에서 사용할 수 있는 강력한 생산성 도구 Org-mode 튜토리얼, 번역.

2018년 3월 22일

최근 emacs에 대한 유튜브를 보는데 전혀 IT쪽 일을 하지 않는 사람도 org-mode 때문에 emacs를 사용한다는 얘기를 듣고 문서를 찾아보게 되었다.

emacs는 아예 처음 사용해보는데 이 문서에 emacs 기능도 간략하게 설명하고 있어서 org-mode의 기능을 살펴보는데 불편함이 없었다. 각각 기능도 강력하고 일반 텍스트를 이렇게 멋지게 조작 가능한게 놀랍다. markdown이나 html로 내보내는 기능도 멋지다. 내보내는 포맷은 플러그인으로 추가도 가능하다. 익숙해지면 정말 유용할 것 같다.

이 번역 문서는 GNU 자유 문서 사용 허가서 버전 1.3 또는 그 이후의 버전이 적용된다. 코드 예제는 GNU GPL 버전 3 또는 이후 버전이 적용된다. 원본은 Org mode beginning at the basics 페이지에서 찾을 수 있다.


org mode unicorn logo

Org-mode 기초부터 시작하기

Org-mode는 공식 웹페이지에서 설명하는 것과 같이 노트와 할 일 목록을 관리하고 프로젝트 계획을 작성하거나 저작하는데 사용할 수 있는, 빠르고 효과적인 플레인 텍스트 시스템입니다. Emacs 22.2와 XEmacs 22.1부터 지원하기 시작했습니다. 아래 내용은 간단한 튜토리얼로 Emacs와 org-mode를 사용하는 방법을 설명합니다.

Emacs에 대해 알아야 할, 정말 최소한의 지식

무엇이든지 하고싶은 일을 하기 위해서 Emacs에서 필수적으로 알아야 할 최소 지식은 다른 애플리케이션에서 알아야 하는 양보다 많습니다. 하지만 이런 비교는 일반적인 장난감과 레고를 비교하는 것과 같습니다. 레고는 시작하기 어렵지만 (작은 플라스틱 조각이 가득한 상자에서 시작합니다) 장기적으로 봤을 때는 더 많은 것을 만들 수 있습니다.

Emacs는 단축키가 풍부합니다. 처음 시작할 때는 이런 특징에 짜증날지 모르지만 시간이 흐를수록 마우스를 점점 적게 사용하게 되고 실제로 더 빠르게 작업을 할 수 있게 될겁니다.

기본적인 모든 동작은 마우스를 사용할 수 있습니다. 파일을 열거나 저장하는 등의 작업은 메뉴에서 모두 가능한 동작입니다. 하지만 키보드에서 손을 때고 마우스를 잡는 방법보다 단축키를 사용하는 방식이 훨씬 빠르다는 것을 알게될 것입니다.

Emacs는 이중 단축키를 많이 사용합니다. 대부분의 애플리케이션처럼 Alt-F 나 Alt-S 대신에 Control-X Control-FControl-X Control-S 를 사용합니다. 처음에는 생산성에 반하는 것처럼 느껴질지 몰라도 금방 익숙해질 겁니다.

노트: 키 축약 표현

  • M – Alt (고대 키보드에서는 Meta 키였습니다.)
  • C – Control
  • S – Shift
  • C-x f – 이 표기는 Control x를 누른 후, 둘 다 손을 땐 다음에 f를 누른다는 의미입니다.

어느 버전의 Emacs를 사용해야 하나요?

어느 버전이든 다 똑같이 느껴진다면 XEmacs보다 Emacs를 선택하기 바랍니다. (이 말에 동의하지 못한다면 이미 이 문장을 넘겨도 될 만큼 알고 있다는 의미입니다.) 다음 링크가 도움이 될 겁니다.

리눅스에서는 패키지 매니저를 사용해서 Emacs를 설치합니다. 데비안이라면 다음처럼 설치합니다.

sudo apt-get install emacs

설정하기

Emacs를 시작할 때 가장 큰 고통은 바로 설정하기에 있습니다. 설정을 위한 메뉴도 없고 텍스트 파일을 수정해야 합니다. (메뉴가 있다고 얘기하긴 하는데 그건 그냥 순수한 사람들을 낚는 겁니다.) 설정 파일의 위치는 (심지어 이름까지도) 어떤 운영체제를 사용하느냐에 따라 다릅니다. 하지만 플랫폼에 상관없이 그 내용은 거의 일치합니다. 대다수 사람들은 동일한 설정 파일을 다른 운영체제서 사용합니다. 장기적으로 보면 최고의 선택이나 마찬가지죠!

설정 파일의 위치는 다음과 같습니다.

org-mode 시작하기

이 챕터에서 사용하는 새로운 단축키는 다음과 같습니다.

  • C-x s – 문서 저장하기
  • C-x f – 문서 열기

첫 org-mode 문서

이제 첫 org-mode 문서를 시작하기 위해서 알아야 할 모든 지식을 습득했습니다. Emacs를 시작합니다. 완전히 새로 설치한 Emacs라면 Emacs의 스플래시 화면을 볼 수 있을 겁니다. 이 화면에는 Emacs 튜토리얼과 여러 문서를 볼 수 있는 바로가기가 있지만 지금은 건너뛰고 다음으로 넘어갑니다.

C-x f 단축키를 사용해서 새 문서를 시작합니다. 이 단축키는 문서를 열기 위한 기능을 제공합니다. (Emacs에서는 버퍼라고 말합니다.) 여기에 *1.org*라고 입력합니다. 이제 새로운 빈 문서가 화면에 나타납니다.

이 문서를 저장하기 위해서 저장 아이콘을 누르거나 C-x s 단축키를 누르고 1.org를 입력합니다.

Emacs는 org-mode 문서를 편집하려고 한다는 점을 아직 이해하지 못합니다. 현재 문서에서 org-mode를 활성화하려면 다음 명령을 입력합니다.

M-x org-mode

이 명령으로 현재 문서에서 org-mode를 활성화 할 수 있습니다.

Emacs가 org-mode 문서를 인식하도록 다음 내용을 문서 상단 에 추가합니다.

MY PROJECT -*- mode: org -*-

여기에 사용한건 뺄셈 기호며 밑줄이 아닙니다. MY PROJECT는 문서의 제목이며 마음대로 지정할 수 있습니다.

이 한 줄의 내용이 이 문서에서 org-mode를 활성화합니다. 이 내용을 넣으면 파일 확장자와 상관없이 org-mode가 동작합니다.

org 파일에서 항상 org-mode를 활성화하려면 Emacs 설정을 수정해야 합니다. 수정하는 방법은 다음 섹션에서 설명합니다.

Emacs 설정 처음으로 수정하기

Emacs 설정 파일을 엽니다. Emacs에서 이 파일을 열기 위해서 C-x f 를 사용합니다. (설정 파일의 경로는 설정을 참고합니다.) 그리고 다음 내용을 추가합니다.

;; -*- mode: elisp -*-

;;스플래시 화면을 끔 (다시 켜려면 t를 0으로 변경)
(setq inhibit-splash-screen t)

;;문법 강조를 활성화
(global-font-lock-mode t)
(transient-mark-mode 1)

;;;;org-mode 설정
;;org-mode 활성화
(require 'org)
;;org-mode를 .org로 끝나는 파일에서 활성화
(add-to-list 'auto-mode-alist '("\\.org$" . org-mode))

Emacs를 재시작합니다.

노트: 앞에서 추가했던 mode 행은 1) Emacs의 설정에 지정한 org-mode 확장자와 다른 확장자를 사용하는 경우 (예로 myfile.txt) 2) auto-mode-alist 행을 설정에 추가하지 않은 경우에만 필요합니다.

목록과 노트 관리하기

이 장에서 사용하는 단축키입니다.

  • TAB / S-TAB – 접기/펴기
  • M-up/down – 제목행 위/아래로 이동하기
  • M-left/right – 제목행 수준 높이기/낮추기
  • M-RET – 새 제목행 추가하기
  • C-x s – 파일 저장하기
  • C-h t – Emacs 튜토리얼

이제 org-mode 문서를 사용할 수 있도록 설정한 Emacs가 있으니 이제 시작하기만 하면 됩니다. org-mode를 시작하는데 도움이 될 개요를 적는 것으로 시작합니다. 새 문서를 열고 (C-x b) 2.org를 입력한 다음에 아래 내용을 복사해서 붙여넣습니다.

#-*- mode: org -*-
#+STARTUP: showall

* org-mode 시작을 환영합니다
  Org-mode에 오신 것을 환영하고 감사드립니다. org에서 개요를 작성하기는 매우 간편합니다.
  그냥 텍스트거든요! 그저 입력하면 됩니다.

* 제목행은 하나 이상의 별 문자로 시작합니다.
  제목은 별 하나, 부제목은 별 두 개 방식으로 숫자를 늘려갑니다.

* 목록 작성하기
** 개요 이동하기
** 제목행 이동하기

파일을 2.org로 저장합니다. (C-x s) 저장하면 문법 강조가 켜져있어서 색상이 변경되는 것을 확인할 수 있습니다. Emacs가 org-mode에서 작업중인 것을 알고 있기 때문입니다.

이제 정말로 org-mode를 시작할 준비가 되었습니다!

목록 작업하기

목록은 브레인스토밍과 모든 항목을 관리하는데 뛰어난 방식입니다. 목록을 사용하면 기록을 하는 동안 큰 그림 그리기에 집중할 수 있도록 돕습니다.

가장 먼저 할 일은 접기입니다. 특히 문서가 길어질 때는 이 기능이 유용합니다. 예제 문서에서 첫 제목행인 이동합니다. (방향키를 사용하세요.) org-mode 시작을 환영합니다 에 커서를 위치한 후에 TAB 을 눌러봅니다. 그리고 S-TAB 도 사용해봅니다. Tab 은 현재 부분을 접거나 펼 수 있습니다. 시프트 키와 함께 누르면 전체 문서를 접고 펼 수 있습니다.

브레인스토밍의 기본적인 아이디어는 항목을 목록으로 적는 것입니다. 적은 후에 항목의 순서를 중요도 순서로 다시 정렬하고 싶을 것입니다. 제목행을 위로, 또는 아래로 이동하기 위해서 제목행에서 M-up/down 을 사용할 수 있습니다. 목록을 모두 접어서 제목만 보이는 상태는 전체 개요를 파악하는데 도움됩니다. 동시에 세부적인 내용도 잃어버리지 않고 잘 보존하고 있습니다.

다음으로 제목 계층을 올리거나 낮출 수 있습니다. 예를 들어 제목행은 하나 이상의 별 문자로 시작합니다. 행을 목록 작성하기 의 부제로 만들고 싶다면 제목의 위치를 아래로 이동한 후에 M-right 으로 계층을 낮출 수 있습니다.

마지막으로 새 제목행을 추가하기 위해서는 M-RET 을 사용합니다.

목록은 순서 없는 목록과 순서 있는 목록이 있습니다. 다음을 확인하세요.

** 반지의 제왕
   가장 좋아하는 장면 (순서대로)
   1. 로히림 전투
   2. 에오윈과 마술사왕의 싸움
      + 이 내용은 이미 책에서도 가장 좋아하는 장면
      + 미란다 오토를 정말 좋아함
   3. 레골라스에게 화살 맞는 피터 잭슨
      - DVD에만 있음
      화살 맞을 때 정말 웃긴 표정이었음.
   하지만 결과적으로 각각의 장면이 아니라 영화 전체가 좋았다.
   영화에서 중요했던 배우:
   - 일라이저 우드 :: 프로도 역
   - 숀 애스틴 :: 샘 역, 프로도 친구로 나옴. 여전히 그를 구니스에서 나온 미키 월쉬로
     잘 기억하고 있음.

순서 없는 목록은 -, + 또는 \*로 시작합니다. 순서 있는 목록은 숫자와 점으로 시작합니다. 설명은 ::이 붙습니다.

더 보기: 이 스크린캐스트 에서는 일반 목록의 몇 가지 기능을 설명합니다. 이 내용은 메뉴얼 에서도 볼 수 있습니다.

기록하기

내용을 작성할 때 쓸 수 있는 몇 가지 표준적인 마크업이 있습니다. 다음과 같은 마크업을 사용할 수 있습니다.

단어에 *굵게*, /기울임꼴/, _밑줄_, =코드=, ~요약~ 등의 마크업을 쓸 수 있다. 꼭 필요한 경우 +삭제선+도 가능하다.

다음처럼 표현됩니다.

단어에 굵게, 기울임꼴, 밑줄, 코드, 요약 등의 마크업을 쓸 수 있다. 꼭 필요한 경우 삭제선 도 가능하다.

여기까지 봤다면 Emacs 튜토리얼을 살펴보는 것도 좋습니다. C-h t 로 Emacs에 내장되어 있는 튜토리얼을실행할 수 있습니다. 튜토리얼은 몇 가지 Emacs 단축키와 함께 어떻게 문서를 이동하는지 알려줄 것입니다.

할 일 항목 사용하기

이 챕터에서 사용하는 새 단축키입니다.

  • S-left/right – 워크플로를 변경
  • C-c C-v – 현재 문서에 있는 할 일 목록 보기

기본 할 일 기능

org-mode를 사용하는 가장 큰 이유는 할 일을 관리하는데 사용하기 위해서 입니다. 먼저 할 일 기능을 사용하기 위해서는 특별히 해야 하는 작업 없이 TODO 키워드를 제목행에 추가하면 됩니다.

** TODO 비행기 구입하기

할일 목록을 빠르게 사용하려면 다음 단축키를 사용합니다.

  • S-left/right

이 단축키는 TODO 에서 DONE 까지, 그리고 빈 칸을 순환하며 상태를 변경합니다.

큰 문서를 작업하고 있고 문서 곳곳에 할 일 목록이 흩어져 있는 상황에서는 C-c / t 로 현재 할 일 항목만 남기고 나머지는 모두 접을 수 있습니다.

할 일 설정하기

  • 파일 내에서 설정하기

    Org-mode 파일에서는 워크플로 상태를 파일 시작 위치에서 설정할 수 있습니다. 다음과 같이 파일 시작부에 작성합니다.

    #+TODO: TODO IN-PROGRESS WAITING DONE
    

    이 행은 파일 상단에 위치해야 하며 상단과 #+TODO 행 사이에 빈 행이 없어야 합니다.

    새 워크플로를 활성화하기 위해서는 파일을 새로 열거나 파일 최상단에 #로 시작하는 행으로 이동한 다음에 C-c C-c 를 입력합니다.

    워크플로를 복사해서 테스트 파일인 1.org에 붙여놓고 차이를 확인해봅시다.

  • Emacs 설정 파일에서

    워크플로 상태를 모든 org 파일에 직접 추가하는 방법은 번거롭습니다. 어디서나 사용할 수 있도록 설정하려면 설정 파일에 추가합니다. 다음 내용은 (require 'org) 행 이후에 추가합니다.

    (setq org-todo-keywords
      '((sequence "TODO" "IN-PROGRESS" "WAITING" "DONE")))
    

    워크플로 상태를 활성화하기 위해서 Emacs를 재시작합니다.

아젠다

이 장에서 사용하는 단축키입니다.

  • C-c a – 아젠다
  • C-c [ – 아젠다 파일 목록에 문서를 추가
  • C-c ] – 아젠다 파일 목록에서 문서를 제거
  • C-c . – 일자 추가
  • C-u C-c . – 일자와 시각 추가
  • C-g – 하던 일을 멈추고 벗어남

아젠다(agenda)라는 단어의 기본적인 의미는 완료 해야 할 것 으로 라틴어인 agendum 에서 왔습니다. Org-mode는 다양한 종류의 아젠다, 일감 목록을 만들고 하나 또는 여러 org 문서에서 이런 일감을 수집하기 매우 좋습니다.

활성화된 모든 일감 목록 생성하기

1.org를 기본 아젠다 파일로 사용하고 나중에 Emacs의 설정 파일에서 어떻게 동작하는지 살펴보겠습니다.

1.org를 엽니다. C-c a 를 눌러서 아젠다를 엽니다. 아젠다는 다음처럼 나옵니다.

Press key for an agenda command
-------------------------------
a Agenda for the current week or day
t List of all TODO entries

위 내용은 일부 내용이고 실제로는 더 많은 내용이 출력될 것입니다.

아쉽게도 위 두 항목은 빈 목록을 보여줄 것입니다. (직접 눌러서 확인해볼 수 있습니다.) 그러므로 이 상태에서 C-g 를 눌러 빠져 나옵니다. 이제 1.org를 아젠다 파일로 추가하기 위해서 C-c [ 를 사용합니다. 이제 아젠다 메뉴로 가서 (C-c a) t 를 누르면 모든 할 일 항목 목록을 확인합니다.

할 일 항목 사용하기에서 설명한 방식대로 더 적절한 워크플로를 추가하는 과정을 했다면 DONE을 제외한 모든 항목이 모두 나타나는 것을 볼 수 있습니다.

이 과정은 여러 문서에서 반복할 수 있습니다. 아젠다는 할 일 전체 목록을 제공할 것입니다. 만약 문서를 아젠다 파일 목록에서 빼고 싶다면 C-c ] 를 사용하면 됩니다.

약속과 마감 일시

일반적으로 시간 설정이 필요한 일감은 달력에 표시합니다. org-mode는 이 방식도 지원합니다. 아젠다는 모든 할 일을 시간 기반 목록으로 볼 수 있습니다. 다음 내용을 참고하기 바랍니다.

1.org에 새 (부)제목을 프레드에게 전화하기 라고 추가합니다. (M-RET프레드에게 전화하기) 다 입력한 후에 C-c . 를 입력합니다. 이 명령을 입력하면 화면 밑에 일자 선택지가 표시됩니다. 직접 손으로 입력할 수도 있고 S-left/right 으로 선택할 일자를 변경할 수 있습니다. 만약 일자 외에 시간도 추가하고 싶다면 C-c . 대신에 C-u C-c . 를 입력합니다.

이제 아젠다 (C-c a)로 이동해서 a 를 누르면 아젠다 항목을 확인할 수 있습니다.

더 읽을 거리:

Emacs 설정 파일에서 아젠다 설정하기

C-c [ 을 사용해서 아젠다 목록에 추가한 후에 Emacs의 설정 파일을 확인하면 다음 내용을확인할 수 있습니다.

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(org-agenda-files (quote ("~/Documents/Projects/org4beginners/2.org"
 "~/Documents/Projects/org4beginners/1.org"))))
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 )

Emacs lisp의 세계에 오신 것을 환영합니다. Emacs가 설정 파일을 변경한 경우에 이런 방식으로 기록됩니다. (참고: Aquamacs 에서는 customizations.el이라는 별도 파일에 저장됩니다.)

여기서 중요한 내용은 중간에 있는데 (5, 6행) org-agenda-files 내용을 확인할 수 있습니다. 아젠다 목록을 만들기 위해 사용하는 아젠다 파일의 목록이 여기에 지정되어 있습니다. 지금은 일단 그대로 둡니다. 다음에 설정 파일을 살펴볼 일이 있다면 적어도 이게 무슨 기능을 하는지 알 수 있을 것입니다.

더 읽을 거리: 사용자 정의 아젠다 명령

GTD

이 챕터에서 사용하는 단축키입니다.

  • C-c C-c – 태그 추가

Getting things done 은 유명한 시간 관리 방법 중 하나로 구글에서 검색하면 1억 5천 여 항목에 이릅니다. 태그를 사용하면 org mode에서도 비슷한 방식으로 할 일 관리를 할 수 있습니다.

태그는 다른 종류의 할일 목록을 조직화하는데 사용합니다. 예를 들면 전화 일정, 읽을 책 목록, 장바구니 목록을 묶기 위해 씁니다.

태그를 추가하기 위해서 다음 설정을 문서 상단에 추가합니다.

#+TAGS: { @OFFICE(o) @HOME(h) } COMPUTER(c) PHONE(p) READING(r)

문서를 다시 불러오거나 #으로 시작하는 곳에서 C-c C-c 를 입력합니다.

이제 문서 어느 행에서든 하나 또는 그 이상의 태그를 등록할 수 있습니다. C-c C-c 를 누르면 다음과 같은 팝업이 나타납니다.

Inherited:
Current:
{ [o] @OFFICE     [h] @HOME    }
  [C] COMPUTER   [p] PHONE   [r] READING

문서 서두에 정의한 태그를 사용할 수 있는 바로가기입니다. 첫 두 태그는 (OFFICE와 HOME)은 상호배타적이라 둘 중 하나만 선택할 수 있지만 나머지는 자유롭게 추가할 수 있습니다.

GTD 설정의 좋은 예제로는 이 글을 참고하세요: Emacs에 Org-mode를 사용해서 GTD 구현하기

Emacs 설정 파일에 태그 추가하기

Emacs 설정 파일에 태그를 추가하려면 다음처럼 설정에 입력합니다.

(setq org-tag-alist '(("@work" . ?w) ("@home" . ?h) ("laptop" . ?l)))

설정 파일에서 상호배타적인 태그 그룹을 만들기 위해서는 메뉴얼 내용을 참고합니다.

이 설정은 문서 상단에 추가한 내용으로 덮어쓰는 것이 가능합니다. 그래서 각 문서에 개별적인 워크플로와 태그를 직접 설정해서 사용할 수 있습니다.

태그를 활발히 활용하는 예제는 여기서 확인할 수 있습니다.

내보내기

여기서 사용하는 단축키는 다음과 같습니다.

  • C-c C-e – 내보내기 메뉴

org-mode에서만 문서 작업을 한다면 큰 문제가 없습니다. 하지만 간혹 다른 포맷으로 문서를 내보내야 할 필요가 있습니다.

예를 들어 현재 문서를 내보내는데 html로 내보내려고 합니다. C-c C-e 를 누른 후에 h o 를 순서대로 누릅니다. 이 방법은 문서를 html로 내보낸 후, 그 내보낸 파일을 브라우저로 열게 됩니다.

더 읽을 거리: html 출판 튜토리얼을 보면 여기서 설명한 내용보다 더 상세하게 다룹니다. 이 방법으로 완전한 웹사이트를 출판하는 것도 가능합니다. 그리고 메뉴얼에서 html, latex, pdf 그리고 다른 포맷으로 내보내는 방법을 설명합니다.

org-mode에 능숙해지기

효율적인 도구를 사용해서 시간을 아끼려면 그 도구를 잘 알아야 합니다. org-mode를 잘 알기 위해서는 메뉴얼을 읽고 사용하는게 중요합니다. Org-mode는 문서화가 잘 되어 있습니다. 가장 빠르게 org-mode 문서를 Emacs에서 읽는 방법으로는 info browser를 사용할 수 있습니다.

이 창을 호출하기 위해서는 C-h i를 입력합니다. 그리고 링크 간 이동하기 위해서 TAB 키를 사용합니다.

info-browser를 이동할 때는 다음 키를 쓸 수 있습니다.

  • u – 위로(up)
  • n – 다음(next)
  • p – 이전(previous)

org-mode 메뉴얼 다음으로는 worg 웹사이트가 있습니다. 여러 재밌는 아이디어와 튜토리얼을 찾을 수 있습니다.

기능을 빠르게 참고할 수 있는 org-mode 치트시트와 emacs 치트시트가 있습니다. 이 두 문서는 귀찮은 단축키를 기억하는데 도움이 될 것입니다.

기초를 넘어서

Geek 유머에 "여기에 용이 있어요!"가 있습니다. 여기서부터는 org-mode를 자유롭지만 스스로 책임져야 하는 사용법을 설명합니다. 대부분 다음 내용은 실제로 정말 어렵거나 한 것은 아니지만 적어도 중요한 데이터는 백업을 해두시기 바랍니다. 만약 다음 내용에서 궁금한 부분이 있다면 메뉴얼과 질문 답변을 확인합니다. 또한 IRC (freenode의 #orgmode)에서 질문하는 것도 좋은 방법입니다.

TODO Quickly adding tasks with remember

(역주: 아직 내용이 존재하지 않는 항목입니다.)

최신 버전 org-mode 사용하기

여기서 사용하는 명령입니다.

  • M-x org-reload – 업데이트 후 org-mode를 다시 불러오기
  • M-x org-version – org-mode 버전 확인하기

Emacs가 업데이트 되는 속도보다 org-mode가 더 빠르게 개발되는 것을 아마 알 수 있을겁니다. 게다가 매일 org-mode의 개발 버전을 받아서 사용하는 것도 가능합니다.

어떻게 가능할까요?

  1. git 설치하기 org-mode 튜토리얼에서 다룰 부분은 아니지만 다음 내용을 참고하세요.
    sudo apt-get install git
  2. org-mode의 코드를 어디에 저장할지 결정합니다. 여기서는 ~/Build/Emacs/org-mode 에 저장하지만 어디 하나 별 차이가 없으니 편한 위치에 저장하기 바랍니다.
  3. org-mode의 최신 버전을 받습니다.
    mkdir ~/Build/Emacs
    cd ~/Build/Emacs
    git clone git://orgmode.org/org-mode.git
    cd org-mode && make && make doc
    
  4. Emacs-init 파일에 다음 내용을 추가합니다.
    (setq load-path (cons "~/Build/Emacs/org-mode/lisp" load-path))
    (setq load-path (cons "~/Build/Emacs/org-mode/contrib/lisp"
    load-path))
    

    (require 'org-install)

    중요! 일반 버전의 org-mode라면 다음을 사용합니다.

    :(require 'org) 다음을 사용한다면 위 행은 반드시 제거해야 합니다.

    :(require 'org-install)

  5. 최신 org-mode를 유지하려면 다음 명령을 사용합니다.
    cd ~/Build/Emacs/org-mode
    git pull && make clean && make && make doc
    
  6. org-mode를 다시 불러옵니다. M-x org-reload 또는 Emacs를 재시작하세요.

어떤 버전의 org-mode를 사용하고 있는지 확인하려면 M-x org-version 을 입력합니다.

ipify: 300억 요청 처리, 그 너머로

스케일 가능한 웹서비스를 Heroku로 엄청나게 단순한 방법으로 만들어 운영한 이야기. 번역.

2018년 1월 4일

Randall Degges의 포스트 To 30 Billion and Beyond를 번역했다. ipify를 만들고 확장하는 일련의 과정을 풀어 쓴 이야기다.

Thank you, Randall Degges for giving me the opportunity to translate this article. If you want to read the original, please check To 30 Billion and Beyond.


ipify: 300억 요청 처리, 그 너머로

Buzz Lightyear Charging Sketch

몇 년 전에 무료 웹서비스인 ipify를 만들었습니다. ipify는 무료로 사용할 수 있는, 고도로 확장 가능한 IP 주소 검색 서비스입니다. REST API를 호출하면 어느 퍼블릭 IP 주소를 사용하고 있는지 확인 할 수 있습니다.

ipify를 만든 당시에는 복잡한 인프라스트럭처 관리 소프트웨어를 만들고 있었고 클라우드 인스턴스에서 관리용 API를 사용하지 않고 동적으로 퍼블릭 IP 주소를 알아내야 했었습니다.

무료로 사용 가능한 역 IP 검색 서비스를 검색해봤지만 적합한 해결책을 찾지 못했습니다.

  • IP를 스크랩 할 수 있는 웹사이트가 있긴 했습니다. (하지만 별로 좋은 형태가 아니고 호스트에서 사용하기엔 불편합니다)
  • 비용을 청구하는 API도 있었습니다. (잌!)
  • 일간 조회 횟수를 제한하는 API도 있었습니다. (한꺼번에 많은 인스턴스를 관리해야 했기에 사용하기 두려웠습니다)
  • 제가 원하는 방식의 API도 있었지만, 오류가 발생하기도 하고 가끔 서비스가 내려가기도 하거나 질적으로 떨어지기도 했습니다. 어느 제공자를 dig로 살펴봤더니 전체 서비스가 단일 서버 (A 레코드)에서 동작하고 그 서버에서 요청이 직접 종료되었습니다. 세계 규모로 확장 가능하거나 고가용성의 서비스라고 말하기 어려웠습니다.
  • 보기엔 괜찮은 API도 있었지만, 지속해서 유지하기 위한 기부를 받으려고 했습니다. 사용하려는 API 서비스가 언제 죽을지 모를 상황이라니 마음 놓고 사용할 수 없었습니다.

이런 이유로 인해 작은 서비스를 직접 만들어서 내 문제와 가능한 한 많은 사람의 문제를 해결하고 싶었습니다. 더군다나 하나의 문자열을 반환하는 소프트웨어를 작성하는 일은 그렇게 엄청 어려운 일은 아니었습니다. 하지 말아야 할 이유가 있을까요?

가장 최악의 경우라고 상상해볼 수 있는 일은 기꺼해야 월 30달러가량 사용하고 공개 서비스라는 점을 염두에 두고 다루는 정도였습니다.

ipify 버전 0

ipify의 첫 코드는 상당히 단순했습니다. 50줄도 되지 않는 작은 코드를 Node로 작성했습니다. (당시에 많은 시간을 Node를 갖고 놀며 지냈습니다.) ipify 서비스가 문자열을 반환하는 일이 전부라는 전제에서는 Node를 사용할 가장 적합한 경우인 것을 알 수 있었습니다. 적은 CPU 사용량으로 수많은 요청을 처리하는 작업이죠.

API 서비스를 Node로 만들고 나서 간단한 정적 사이트를 프론트엔드로 붙여 아마존의 S3에 올렸습니다. 그리고 S3 버킷 앞에 아마존의 CDN 서비스인 CloudFront를 설정해서 페이지를 캐싱하는 것으로 엄청 빠르게 불러올 수 있도록 했습니다.

어떤 상상의 날개를 펼쳐봐도 저는 디자이너가 아니었습니다. 그래도 다행히 bootstrap을 사용해서 조금 나은 모습을 갖출 수 있었습니다. =)

모든 준비가 끝난 후에 간단하게 테스트를 수행했습니다. 모든 것이 준비되었다는 생각에 다음 단계인 배포로 넘어갔습니다.

Heroku 입성

Heroku Logo

저는 Heroku의 왕 팬입니다. (심지어 Heroku에 대한 도 썼습니다.) Heroku를 수년 간 사용하고 있고 개발 세계에 있어서 가장 저평가되고 있는 서비스 중 하나라고 생각합니다. 한 번도 사용해본 적이 없다면 지금 확인해보시길 바랍니다!

ipify를 확장 가능하고 고가용성으로, 그리고 저렴하게 운영하려면 Heroku 만큼 단순하고 좋은 선택지가 없다고 생각하고는 Heroku를 사용하기로 결정했습니다.

ipify를 Heroku에 1~2분 정도 걸려 배포하고 단일 dyno(웹서버)로 실행한 후에 제한적으로 테스트를 수행했습니다. 여전히 잘 동작하니 스스로 뿌듯한 기분이 들었습니다.

Heroku에 익숙하지 않다면 ipify 인프라스트럭처가 어떻게 동작하는지 살펴봅시다.

  • Heroku는 ipify 웹서비스를 512M 램과 제한된 CPU인 작은 dyno (웹서버)에서 구동합니다.
  • 만약 프로세스가 충돌하거나 어떤 심각한 문제가 발생한다면 Heroku는 자동으로 서비스를 재시작합니다.
  • Heroku는 앱으로 들어오는 모든 요청을 로드 벨런스를 거친 후 요청을 처리하기 위해 dyno (웹서버)로 전달합니다.

이런 설정이 좋은 이유는:

  • 고가용성: Heroku의 로드벨런서, 제 dyno, 모든 것이 고가용성입니다.
  • 유지보수도, 관리 설정도, 어떤 배포 코드도 필요하질 않습니다. 100% 자동입니다.
  • 저렴합니다. 단일 서버를 위해 월 ~7달러가량 냅니다.
  • 빠릅니다. 헤로쿠는 아마존 웹서비스(AWS) 상에서 돌아가며 모든 인프라스트럭처는 세계에서 가장 인기있는 클라우드 호스트 위치인 AWS 미동부 (버지니아)에서 운영됩니다. 이 의미는 지리적으로 US 동부 해안에서 운영된다는 점인데 물 건너 유럽이나 나머지 US 지역에서 그다지 멀지 않다는 의미입니다. 세계 대부분 지역의 사용자가 서비스를 사용한다고 해도 그렇게 오래 걸리지는 않을 겁니다.

지금까지는 상당히 괜찮았습니다. 만들고, 설정하고, 테스트하고, 프로덕션으로 옮기기까지 1일 이하의 노력으로 끝낼 수 있었습니다.

그리고나서 ipify를 제 인프라스트럭처 관리 코드와 통합하는 것으로 원래 해결하려던 문제를 처리했습니다.

한 달 정도 모든 것이 잘 흘러갔습니다. 몇 가지 문제를 알아차리기 전까지는 말이죠.

인기… 헐?

Spartan Warrior Sketch

ipify를 홍보한 적이 없지만 “ip address api”를 구글에서 검색하면 꽤 상단에 걸리게 되었습니다. SEO와 문구 수정에 사용한 수년 간의 노력이 빛을 발한 것 같습니다.

그동안 ipify가 구글 검색 결과에서 상당히 높은 순위로 노출된 덕분에 수천 명의 사용자가 생겼습니다. 서비스가 많이 노출되면서 몇 이슈가 나타나기 시작했습니다.

제 Heroku 로드 벨런서가 경고를 보내기 시작했습니다. Node 서버가 들어오는 요청을 충분히 빠르게 처리하지 못하고 있다는 내용이었습니다. 결과적으로 다음과 같은 일이 나타났습니다.

  • 너무 많은 사용자가 ipify에 API 요청을 보내고 있습니다
  • 제 Node 서버가 요청에 대해 느리게 응답해서 지연이 늘고 있었습니다
  • Heroku의 로드벨런서가 이 문제를 알아차리고 Node 서버에 보내기 전에 요청을 버퍼에 저장했다가 보내기 시작했습니다.
  • Node 서버가 빠르게 요청을 처리하지 못한 탓에 로드벨런서에서 사용자에게 503을 전달하고 요청을 끝내버렸습니다.

그다지 좋은 모습이 아닙니다.

이 문제를 해결하기 위해 한 일은 간단했습니다. Heroku에 dyno를 더 추가했습니다. 이 방식으로 수용량을 두 배로 늘렸고 모든 서비스가 부드럽게 동작하기 시작했습니다. 그 이면엔 두 “프로덕션” dyno를 구동한 탓에 거의 50달러 가량을 지불해야 했습니다. 첫 dyno 이후엔 일반 요금인 dyno당 월 25달러를 지불해야 했습니다.

서비스가 이 정도로 인기 있다면 월 50달러를 내는 것도 나쁘지 않다고 생각해서 지불하기로 결정하고 다시 안정적인 상황으로 돌아올 수 있었습니다.

하지만… 그렇게 말처럼 쉬운 문제가 아니었습니다.

한주도 채 지나기 전에 Heroku에서 이전과 동일한 경고를 받았습니다. 사용량을 보니 트래픽은 두 배로 늘었고 ipify는 더 많은 사용량을 보였습니다.

저는 또 다른 dyno를 추가하고 월 75달러를 지불하게 되었습니다. 그래도 좀 더 살펴보기로 했습니다. 저는 짠돌이라서 월 50달러 이상 쓰는 건 기쁜 일이 아니었습니다.

수사

L Sketch

수사를 시작하면서 가장 먼저 살펴본 것은 실제로 ipify가 초 당 얼마나 많은 요청을 처리하는가(requests per second, rps)를 살펴보는 일이었습니다. 숫자를 보고 상당히 놀랐습니다. 낮았거든요.

제 기억에는 ipify는 10rps 정도였습니다. 이렇게 작은 숫자를 처리할 수 없다면 뭔가 코드가 문제라고 생각했습니다. 만약 10rps를 작은 웹 서버 두 개를 사용하는데도 처리하지 못하는 상태라면 끔찍하게 잘못된 것이 분명했습니다.

처음 알아차린 것은 단일 Node 프로세스로 처리하고 있다는 점이었습니다. Node의 클러스터 모듈을 사용해서 쉽게 고칠 수 있었습니다. 이제 각각의 CPU 코어에서 하나의 프로세스를 구동할 수 있게 되었습니다. 효과적으로 dyno에서 두 배의 처리량을 다룰 수 있게 되었습니다.

하지만 20 rps는 여전히 작은 숫자로 느껴져서 좀 더 깊이 파헤쳐보기로 했습니다. Heroku에서 로드 테스트를 수행하기보다 제 랩탑에서 로컬로 테스트를 수행했습니다.

제 랩탑은 512M 램의 작은 Heroku dyno보다 훨씬 강력하기 때문에 더 처리량이 많을 것으로 생각했습니다.

ab 도구를 사용해서 테스트를 수행했고 놀랄 수 밖에 없었습니다. 훨씬 좋은 사양의 제 랩탑에서도 30 rps 정도 처리하지 못하는 것입니다. (제 랩탑은 리눅스로 구동되고 있었고 ab는 효율적으로 동작하는 환경이었습니다.) 그래서 기초적인 분석을 시작했고 Node에서 단순한 문자열 조작 동작을 하는데 많은 시간을 사용한다는 것을 확인할 수 있었습니다. (X-Forwarded-For 헤더에서 IP 주소를 추출하고 다듬는 일이었습니다) 여러 가지 실험을 해봤지만, 이 한계를 뛰어넘을 만큼 좋은 성능을 내는 것은 불가능했습니다.

이 시점에서 ipify 서비스는 두 dyno를 통해서도 겨우 20 RPS 정도 처리할 수 있었습니다. ipify는 월 ~5200만 요청을 처리하고 있었습니다. 그다지 인상적이지 않았습니다.

이 서비스를 Go로 다시 작성하기로 했습니다. (Go는 몇 달 전부터 사용하기 시작했습니다) 동일한 Go 서버를 작성했을 때 성능 측면에서 더 나은지 확인해보고 싶었습니다.

ipify 버전 1

Warrior Sketch

ipify를 Go로 다시 작성하는 일은 짧은 (그리고 즐거운) 실험이었습니다.

이 과정에서 Gorilla/mux, Martini와 httprouter 같은 다양한 Go 라우팅 스택을 사용해볼 수 있었습니다. 이 세 가지 라우팅 도구를 사용해보고 비교해본 결과 httprouter가 다른 둘에 비교해 비약적으로 성능이 좋은 것을 확인했습니다.

제 랩탑에서 Go 서버는 초당 ~2,500 요청을 처리할 수 있었습니다. 엄청난 향상이었습니다. 또한, 메모리 사용량도 엄청 적어서 5M 남짓을 사용했습니다.

Go와 사랑에 빠진 저는 즉시 행동으로 옮겼습니다. Go 기반 ipify 서비스를 Heroku에 배포했습니다.

결과는 환상적이었습니다. 단일 dyno에서 2천 RPS 정도를 처리할 수 있었습니다. 이런 변화는 월 25달러 수준으로 월 ~52억 요청을 처리할 수 있게 되었습니다.

며칠 후에 경험 있는 Go 개발자와 대화를 한 후, 문자열을 처리하는 기능 몇 가지를 다시 작성했고 그 덕분에 추가로 ~1천 RPS 정도를 확보할 수 있었습니다. 이 시점에 dyno 당 월 ~77억 요청을 처리하고 있었습니다. (조금 더 많거나 적었습니다.)

저는 말할 것도 없이 흥분했습니다.

더 큰 유명세

Tyrael Sketch

짧은 시간이긴 하지만 호스팅 비용을 줄일 수 있었습니다. 대략 2달 정도 지난 후에 ipify는 또 다른 문제를 경험하게 되었습니다.

엄청난 비율로 성장을 지속하고 있었습니다. 이즈음에 ipify에 대해 Google Alerts를 설정해뒀는데 그래서 사람들이 이 서비스에 대해 언급하면 바로 알 수 있었습니다.

점점 많은 사람이 ipify를 개인 프로젝트와 업무에서 사용한다는 점을 확인할 수 있었습니다. 그리고 몇 회사에서는 자신들의 프로덕트에 포함해도 되느냐는 질문을 받기 시작했습니다. (대형 스마트 TV 공급자, 다양한 미디어 대행사와 IoT 업체 등.)

ipify가 월간 150억 요청을 처리할 때쯤 호스팅 비용으로 월 50달러를 사용하고 있었습니다.

하지만 이런 상황은 오래 지속되지 않았습니다.

ipify의 트래픽은 급격히 성장하는걸 확인할 수 있었습니다. 아마 몇 달 후에는 수 십 억의 월간 요청을 더 받게 될 겁니다.

또한, 급작스러운 트래픽에 의한 문제도 겪기 시작했습니다. ipify가 짧은 시간에 엄청난 양의 트래픽을 받게 되면 순식간에 죽는 경우가 있었습니다. 아마 이 트래픽은 부트스트랩 스크립트, 스케쥴 작업(cron job) 그리고 다른 비슷한 방식의 작업으로 발생한 것이라 짐작하고 있습니다.

뒤늦게는 백신 업체에서 ipify를 차단하기 시작했다는 얘기도 들을 수 있었습니다. 루트킷이나 바이러스, 지저분한 소프트웨어가 ipify를 사용하기 시작한 탓입니다. 공격자는 공격 대상을 공격하기 전에 ipify를 사용해서 피해자의 퍼블릭 IP 주소를 얻어내는 등의 악의적인 용도로 사용했습니다. 이런 사용자도 분명 대규모의 급작스러운 트래픽에 책임이 있다고 가정했습니다.

이런 악의적인 사용자를 좋아하지도 않고 그 일을 돕는데 돈을 쓰고 싶지도 않았지만 ipify는 중립적으로 사용하고 싶은 사람은 아무나 사용할 수 있도록 그대로 운영하기로 결정했습니다. 개발자 서비스인데 어떤 사람만 사용하도록 고르고 선택하는 것은 탐탁치 않았습니다. 모든걸 단순하게 유지하기로 했습니다.

이런 결정에도 여전히 급작스러운 트래픽 문제는 해결해야 했습니다. 이런 트래픽은 쉽게 처리하기 어려운데 우선적으로 두 선택지가 있었습니다.

  • 추가적인 dyno를 운영해 비용을 지불하고 항상 급작스러운 트래픽에 대비하는 방법, 또는
  • Adept와 같은 자동 확장 도구를 사용해서 트래픽 패턴에 따라 dyno를 생성하고 제거하는 방법

결국엔 첫 번째를 선택했습니다. 단순히 Adept의 서비스를 사용해서 추가적인 비용을 지출하고 싶지 않았기 때문입니다. (물론 이 서비스를 사용해본 적이 있고 엄청나게 멋진 서비스입니다.) 이 즈음에 월간 150달러를 지불했고 ipify는 250억 월간 요청을 처리했습니다.

이렇게 최근까지 왔습니다.

ipify 300억 요청 도달

Buzz Lightyear Proud Sketch

지난 몇 달 동안 ipify는 새로운 기록을 냈고 월간 300억 요청을 몇 차례 넘었습니다. 이렇게 새로운 기록을 경신하는 일은 지켜보는 즐거움이 있습니다.

오늘날 ipify는 2천에서 2만 RPS를 유지하고 있습니다. (이 수치는 항상 절대 일관적이질 않았습니다.) 트래픽은 항상 달라졌고 사용량은 정기적으로 높아졌는데 트래픽 패턴에서 의미를 찾는 걸 완전히 포기했습니다. 평균 응답 시간은 트래픽 패턴에 따라 1~20ms 사이를 유지했습니다.

현재 서비스는 트래픽과 여러 요인에 따라 월간 150~200달러로 운영되고 있습니다. 월간 200달러를 소비한다고 가정하고 수치를 계산해보면 ipify는 각 요청을 0.000000007달러로 운영하고 있습니다. 충격적으로 낮은 비용이죠.

만약 이 서비스를 Lambda에서 돌린다고 가정하고 요금을 계산해본다면 월간 1,243.582달러 (연산) + 6,000달러 (요청) = ~ 7,243.58달러 정도를 사용하게 될 겁니다. 참고로 이건 간단한 입계산입니다. ipify의 수치를 Lambda 요금 계산기에 입력해서 얻은 결과입니다.

결론적으로 ipify에 들어가는 비용에 _엄청나게 만족_하고 있습니다. 간단한 용도를 제공하는 짠돌이 서비스죠.

ipify의 미래

이제 미래를 생각해보게 되었습니다. ipify는 지속해서 성장하고 있습니다. 서비스는 ipv6 지원 (Heroku가 현재 지원하지 않아요 🙁), 더 나은 웹디자인, IP 주소에 대한 다른 메타데이터 등 여러 가지 요청을 받고 있습니다.

최근 다른 프로젝트 덕분에 엄청나게 바쁜 탓에 ipify의 소유권을 제 좋은 친구인 https://www.whoisxmlapi.com에 넘겼습니다.

조나단과 그의 팀은 가치 있고 흥미로운 개발자 API 서비스를 만드는데 제격인 포트폴리오를 갖고 있습니다.

제가 지속해서 돕고 있긴 하지만 조나단과 그의 팀은 현재 ipify의 새로운 기능을 구현하고 있습니다. 제가 기대하는 좋은 변화를 반영하기 위해 열심히 작업하고 있습니다. (더 나은 UI와 데이터 엔드포인트를 포함해서 말입니다.)

저는 앞으로도 꾸준히 성장할 ipify가 기대됩니다.

저에게 질문이나 궁금한 점이 있다면 이메일로 남겨주세요.