Software Development

Why Events Are A Bad Idea

HotOS IX 에 발표된 ‘Why Events Are A Bad Idea‘는 이벤트 기반 프로그래밍(event-based programming)에 비해 쓰레드 프로그래밍(thread programming)을 옹호하는 입장을 취하고 있는 논문 중의 하나다. 이 문서는 위 논문의 한글 요약이다.

1 Introduction

Highly concurrent application에 있어서 그동안 쓰레드를 사용한 시도들이 실패해왔기 때문에, 연구자들 사이에 이벤트 기반 프로그래밍이 쓰레드 프로그래밍에 비해 더 나은 선택이라고 결론 내려지고 있는데, 그 이유를 다음과 같이 요약하고 있다.

  • cooperative multitasking으로 인해 synchronization cost가 저렴하다.
  • state를 관리하기 위한 오버헤드가 낮다. (스택이 없음)
  • appplication 수준의 정보를 이용하여 보다 나은 스케줄링과 locality를 얻을 수 있다.
  • 보다 유연한 control flow.

이 논문의 기본적인 생각은

  • 쓰레드가 Highly concurrent application에 대해 좀 더 자연스러운 abstraction을 제공하며,
  • 컴파일러와 쓰레드 시스템 (패키지)의 약간의 개선을 통해서 쓰레드에 비해 이벤트를 사용해야만 했던 이유 (주로, 성능)가 없다는 것

을 지적하며, 쓰레드 프로그래밍이 이벤트 기반 프로그래밍에 비해 더 선호되어야 한다고 주장하고 있다.

2 Threads vs. Events

2.1 Duality Revisited

Lauer와 Needham은 ‘On the duality of operation system structures‘에서 쓰레드(threaded) 방식과 메시지 전달(message-passing, i.e., event-based) 방식은 대등하고 동등한 성능(equivalent performance)을 가지고 있기 때문에, 대상 애플리케이션에 더 자연스러운 방식을 선택해야한다고 주장한다. 이 논문의 저자는 (대상 애플리케이션이) high-concurrency server의 경우에는 쓰레드에 기반한 접근이 더 낫다고 생각한다.

저자는 Lauer와 Needham의 메시지 전달 시스템이 다음과 같은 점들에서 현재의 이벤트 시스템(modern event systems)과 정확하게 대응하지는 않는다고 얘기하고 있다.

  • 이벤트가 synchronization을 위해 사용하는 cooperative scheduling을 무시하고 있다.
  • 대부분의 이벤트 시스템들은 shared memory와 global data structure를 사용하고 있으나, Lauer와 Needham은 이것이 전형적이지 않은 (atypical) 것으로 표현하고 있다.
  • Lauer와 Needham이 주장한 동등한 성능(performance equivalence)은 제대로 된(equally good) 구현을 필요로 한다. 저자는 very high concurency에 적합한 쓰레드 구현이 존재하지 않다고 생각한다.

동등한 성능을 주장함에 있어서 Lauer와 Needham은 blocking/yielding point가 node이고 그러한 point들 사이에서 실행되는 code가 edge인 그래프를 사용하고 있는데, 두 방식은 본질적으로 같은 그래프를 가지고 있다고 얘기하고 있다. 저자는 이러한 사실이 쓰레드의 성능과 사용성에 대한 비판이 특정한 쓰레드 구현의 문제에서 나온 것이지 일반적인 쓰레드에서 발생한 것이 아니라고 보고 있다.

2.2 “Problems” with Threads

이 섹션에서 저자들은 쓰레드에 관련된 비판을 하나씩 반박하고 있다.

Performance

Criticism: Many attempts to use threads for high concurrency have not performed well.

저자들은 좋지 않은 쓰레드 구현 때문이라고 주장한다. 특히 현재의 쓰레드 구현들은 high concurrency나 blocking operation를 위해 설계되지 않았다고 지적하고 있다.

쓰레드 구현에서의 오버헤드의 원인은

  • 쓰레드의 수에 따른 O(n) operation의 존재와
  • 이벤트에 비해 상대적으로 높은 context switch 오버헤드 (preemtion을 위한 context 저장 오버헤드와 커널 쓰레드의 커널 진입에 의한 오버헤드)

라고 한다.

저자들은 이러한 단점들은 역시 쓰레드의 기본적인 특성은 아니라고 얘기하고 있다. 저자들이, O(n) operation들을 대부분 제거한 GNU Pth user-level 쓰레드 구현을 사용한 SEDA 쓰레드 서버 벤치마크를 반복해본 결과, 10만개의 쓰레드에 대해서도 충분히 scale하며, 성능도 이벤트 기반 서버와 거의 동등하다고 한다.

Control Flow

Criticism: Threads have restrictive control flow.

쓰레드를 사용하는 프로그래머는 control flow에 대해 너무 linear하게 생각하므로 효율적인 control flow 패턴을 사용하지 못한다는 주장에 대해, 저자들은 복잡한 control flow 패턴은 실제로는 드물다고 얘기하고 있다. 저자들이 실제 애플리케이션들의 코드 구조를 분석한 결과 control flow 패턴은 call/return, parallel calls, pipelines의 세가지로 떨어진다고 한다. 그리고 이 패턴들은 쓰레드로 보다 자연스럽게 표현할 수 있다.

저자들은 이보다 복잡한 패턴들은 제대로 사용하기가 어렵기 때문에 사용되지 않는다고 주장한다. 이벤트 시스템에서 자주 일어나는 비본질적인 비선형성은 애초에 이해하기 힘들며, 미묘한 에러가 발생하기 쉽게 만든다고 한다. 의도적인 복잡한 control flow도 마찬가지라고 한다.

multicast나 publish/subscribe 애플리케이션에서 사용되는 dynamic fan-in/fan-out 패턴은 쓰레드보다는 이벤트 시스템과 더 잘 어울리는데, 조사한 high concurrency 서버들에서는 어떤 서버도 이 패턴을 사용하지 않았다고 한다.

Synchronization

Criticism: Thread synchronization mechanisms are too heavyweight.

이벤트 시스템은 cooperative multitasking으로 인해서 synchronization을 위한 mutex 등의 오버헤드가 없다고 여겨지나, Adya 등은 Cooperative task management without manual stack management에서 이러한 장점은 이벤트 자체에서 얻어지는 것이 아니라 cooperative multitasking에 있다고 한다. 따라서, cooperative 쓰레드 시스템에서도 같은 이득을 볼 수 있다. 하지만, 이러한 이득은 단일 프로세서에서만 얻어지는 것이고 일반적으로 high concurrency 서버들이 실행되는 다중 프로세서에서는 얻을 수 없는 것이다.

State Management

Criticism: Thread stacks are an ineffective way to manage live state.

쓰레드 시스템은 stack overflow와 virtual address space 사이의 tradeoff 문제를 가지고 있다. 저자들은 dynamic stack growth를 사용해서 이 문제를 해결할 것을 제안한다. 이벤트 시스템의 경우 쓰레드를 거의 사용하지 않으므로 이러한 문제가 없지만, live state의 관리를 프로그래머가 모두 직접해주어야 한다. 반면, 쓰레드 시스템은 stack을 통한 자동적인 관리가 이루어진다.

Scheduling

Criticism: The virtual processor model provided by threads forces the runtime to be too generic and prevents it from making optimal scheduling decisons.

이벤트 시스템은 애플리케이션 레벨에서 이벤트를 스케줄링할 수 있으므로 몇가지 최적화가 가능할 것이다. 또한, Larus와 Parkes에 의하면, Using cohort sched
uling to enhance server perf
ormance
에서는 같은 종류의 이벤트를 한번에 처리함으로써 code locality를 개선함으로써 얻는 이익도 있을 수 있다고 한다. 하지만, 이러한 모든 이점들은 cooperatively scheduled threads를 사용함으로써 똑같이 얻을 수 있는 것들이다.

2.3 Summary

쓰레드도 high concurrency에 대해 적어도 이벤트 만큼 성능을 낼 수 있으며 이벤트에 중요한 질적인 이점은 없다. 이벤트 스타일을 많이 추구하게 된 것은 쓰레드의 기본적인 특성이라기보다는 scalable한 user-level 쓰레드가 없었기 때문이다.

3 The Case for Threads

다음의 두가지 이유에 기반하여 쓰레드가 high-concurrency 서버에 보다 적합한 abstraction이라고 얘기하고 있다.

  • 동시에 발생하는 request는 거의 독립적이다.
  • 각각의 request를 다루는 코드는 보통 순차적이다.
Control Flow

High-concurrency 시스템들에서 이벤트 기반 프로그래밍은 애플리케이션의 control flow을 이해하기 힘들게 만든다. 이벤트 시스템에서는 이벤트를 다른 모듈에 보냄으로써 어떤 메서드를 “call”하고, 그 메서드에서의 “return”을 기대한다. 프로그래머는 마음속으로 이러한 call/return pair를 파악하고 있어야한다. 더구나 프로그래머는 call/return pair를 위해 live state를 저장하고 복구해주어야 한다. Adya는 이를 “stack ripping”이라고 부르는데, 이벤트 시스템을 사용하려는 프로그래머의 가장 큰 부담이라고 얘기한다.

쓰레드 시스템에서는 control flow나 state를 좀 더 자연스러운 방식으로 표현할 수 있다. call과 return은 문법적으로 grouping이 되므로 1:1의 인과 관계를 이해하기 쉽고, call stack이 모든 live state를 encapsulate한다.

Exception Handling and State Lifetime

예외 상황 또는 정상적인 종료 이후, task의 state를 clean up하는 것은 쓰레드 시스템에서 더욱 간단하다. 이벤트 시스템에서는 task state가 일반적으로 heap에 할당되어 있기 때문에, control flow가 분기를 통해 복잡해지면, 적절한 시점에 이를 지우는 것은 매우 힘들다. Ninja난 SEDA와 같은 이벤트 시스템에서는 이 문제를 해결하기 위해 garbage collection을 사용하지만, Java의 general-purpose garbage collection 메커니즘은 high-performance 시스템에 부적합하다고 한다. Inktomi의 Traffic Server는 reference counting을 사용하고 있지만 정확한 counting은 어렵다고 한다.

Existing Systems

저자들이 만든 Ninja 시스템은 recovery와 같은 복잡한 부분에서 이벤트를 사용해 정확한 동작을 얻기가 거의 불가능했기 때문에, 결국 쓰레드를 사용했다고 한다. 이 외에도 high concurrency를 필요로 하지 않는 애플리케이션은 항상 쓰레드를 사용하고 있다.

Just Fix Events?

쓰레드를 사용하는 대신 이벤트 시스템의 문제를 해결하는 도구와 언어를 만들어야한다는 주장은 결국 쓰레드의 문법과 run-time behavior를 복제할 뿐이다. Adya가 기술한 cooperative task management 기술은 이벤트 시스템의 사용자로 하여금 쓰레드와 같은 코드를 쓰게 하고, 이것이 blocking call들 사이의 continuation으로 변환되도록 한다. 결국 대부분의 경우 이벤트의 문제를 수정하는 것은 쓰레드로 전환하는 것과 같다.

4 Compiler Support for Threads

4.1 Dynamic Stack Growth

run time에 stack의 크기가 조정될 수 있도록 하는 메커니즘을 통해 고정된 크기의 스택 때문에 stack overflow와 address space의 낭비 사이에서의 tradeoff 문제를 제거할 수 있다. compiler의 분석을 통해서 각각의 function을 호출할 때 필요한 stack 크기에 대한 upper bound를 알 수 있으므로 호출하는 곳에서 stack growth가 필요한가도 판단할 수 있다. recursive function이나 function pointer가 추가적인 문제를 던져주지만 이것도 추가적인 분석을 통해 해결할 수 있다.

4.2 Live State Management

function 호출을 하기전에 컴파일러가 불필요한 state를 쉽게 제거할 수 있다. (e.g. tail call optimization) 하나의 blocking call에 많은 양의 state가 걸려있다면 컴파일러가 프로그래머에게 경고해줄 수 있다.

4.3 Synchronization

컴파일 시간의 분석을 통해 프로그래머에게 race condition을 경고해줌으로써 버그를 줄일 수 있다. TinyOS에 기반한 networked sensor를 위한 언어인 nesC는 atomic section에 대해 지원하고 concurrency model에 대해서 이해한다. atomic section이 동시에 수행되어도 안전한지를 결정하는데 도움을 줄 수도 있다. 다중 프로세서에 대해서도 마찬가지고 libasync에서 직접 코딩한 graph coloring 기술을 자동화하는 것이다.

5 Evaluation

<생략>

6 Relative Work

<생략>

7 Conclusions

이벤트 시스템이 high concurrent 시스템에서 좋은 성능을 얻는데 사용되어왔지만, 쓰레드를 사용해서 비슷하거나 더 나은 성능을 얻을 수 있다. 간단한 프로그래밍 모델과 더 나은 컴파일러 분석이 가능하다는 점은 쓰레드에 중요한 이점을 제공한다. 저자들은 컴파일러와 쓰레드 시스템의 결합을 통해서 더욱 뛰어난 성능을 얻는 동시에 깔끔하고 간단한 인터페이스를 가진 프로그래밍 모델을 프로그래머에게 제공해줄 수 있을 것이라고 주장한다.

Why Events Are A Bad Idea 더 읽기"

Threads Considered Harmful

Threads Considered Harmful

Summary

쓰레드(thread)의 문제점을 지적한 것으로 상당히 유명한 글 중에 하나가 바로 Kuro5hin에 올라온 ‘Threads Considered Harmful ‘이라는 글이다. 이 글의 주요 논지는 shared state에 대한 synchronization을 제대로 하는 것과 dead lock을 방지하는 것이 어렵고, 이를 탐지하기가 어렵기 때문에, 쓰레드(thread)를 사용하지 말아야하고, 그 대안으로 Multiple processes, Event-based programming, Co-operative threads (co-routines) 등의 방법을 사용해야한다는 것이다.

Detailed Review

맨 첫 문단에서 이 글의 저자는 다음과 같이 얘기하고 있다.

once you have spawned a thread, there is no way to know anymore which line is being executed in parallel with yours.

이 얘기는 ‘Go To Considered Harmful‘에서 Dijkstra가 주장하고 있는 프로세스의 진행 정도를 표현할 수 있는 의미있는 좌표가 존재해야한다는 아이디어를 thread에 적용한 것이다. 하지만, 적어도 구조적인(structured) 프로그램을 수행하는 여러 thread의 진행 정도를 표현하는 의미있는 좌표는 존재한다. ‘Go To Considered Harmful’ 논문에 대한 리뷰 글에서 설명한 방식을 빌려오자면, 우리가 여러 쓰레드를 사용하는 프로그램을 디버깅하기 위해 멈췄을 때, 우리는 모든 쓰레드의 진행 정도를 각 쓰레드의 stack trace를 통해서 알 수 있다. 글의 저자는 문제를 약간 잘못 설명하고 있다. 진짜 문제는, 서로 간에 side-effect를 미칠 수 있는 쓰레드들이 어떤 식으로 수행되느냐에 따라 결과가 달라지기 때문에, 프로세스의 진행 정도를 알 수 있다고 해서 디버깅을 할 수 없게된 것이다. Dijkstra가 위의 논문 처음에서 내세웠던, 정적인 프로그램과 동적인 프로세스 간의 상응성을 단순하게 만들기 위해서는 프로세스의 진행 정도만으로는부족한것이다.

예를 들어, a-b-c-d라는 프로그램을 수행하는 두 thread가 있어서 어떤 시점의 프로세스들의 진행 정도가 둘다 d라는 걸 알더라도, 실제로 수행된 순서가 ababcc인지 aabbcc인지를 알지 못하면 디버깅을 불가능한 것이다.

여담으로, 쓰레드를 하나의 프로세스로 보지 않고,모든 thread를 각 쓰레드가 수행하는 프로그램이 랜덤하게 수행되는 하나의 프로세스로 본다면, 프로세스의 진행 정도를 표현할 수 있는 좌표 자체가 존재하지 않는다고 볼 수도있다. 이 문제에 여러 프로세서(SMP)의 문제까지 끼어들면 더욱 복잡한 모델이 된다.

한편, 다음 문장이 여러 쓰레드를 사용하는 프로그램의 디버깅이 힘든 이유를 한마디로 표현하고 있다.

The chief problem with threads are race conditions and dead locks. Both tend to occur randomly.

친절하게도 자세한 예를 들어 synchronization의 어려움과 dead lock을 방지하는 것의 어려움을 강조하고 있고, dead lock을 방지하기 위해 lock들의 순서를 지키는 것이 부담스러워서 coarse-grained lock을 쓴다면 performance 저하의 문제가 있음을 지적한다.

저자가 쓰레드를 사용하는 것에 어떠한 이익도 없다고 결론 내리고 대안으로 내세운 것은 Multiple processes, Event-based programming, Co-operative threads (co-routines)가 있다.

My Opinion

  1. 쓰레드의 필요 뿐만 아니라 쓰레드의 대안들에서 오는 복잡성은 모두 concurrently shared state (또는 resource)의 필요에서 발생한다. concurrently shared state가 전혀 없다면 물론 쓰레드를 사용할 필요는 없다. 글의 저자가 주장한 것과 같이 하나 또는 그 이상의 프로세스를 사용해도 되고, 설령 쓰레드를 사용한다고 하더라도 글의 저자가 우려하는 것과 같은 사태는 절대 발생하지 않는다. 하지만, 많은 프로그램은 어느 정도의 concurrently shared state를 필요로 한다. 그것이 메모리 조각이든, 객체든, 파일과 같은 커널 리소스든, 그것이 공유되기 시작하면, 여러 프로세스에서도 똑같은 문제가 발생하기 시작한다. 복잡성의 원인은 해결법(쓰레드)에 있는 것이 아니라 궁극적으로 우리가 풀어야하는 문제 자체에 내재하고 있는 것이다.
  2. shared state vs. messaging 문제. shared state와 messaging 사이에 어느 것을 선택할 것이냐 하는 것은 컴퓨팅의 역사에서 항상 반복되어온 문제다. OS의 디자인이나 구현을 보다보면, 어떤 resource (또는 state)에 대한 접근을 strict하게 제한할 것이냐, 아니면 시스템이 fragile하지 않는 한 느슨하게 허용할 것이냐의 trade-off 문제가 상당히 자주 등장한다. 여기에는 성능이나 디자인 등의 문제가 복잡하게 게재된다. 쓰레드를 쓸 것이냐의 문제도 trade-off 문제에 불과하다고 생각한다.
  3. 적절한 대안의 부재: 프로그래밍은 계속 더욱 높은 레벨의 abstraction을 지향해서 언젠가는 이런 고민을 하지 않아도 될 날이 올 것이다. 하지만, 현재로서는 만족할만한 대안이 없는 것이 사실이다. 프로세스는 concurrently shared state가 없다는 가정하에서 쓰레드와 다를 바는 없고, 이벤트 기반 프로그래밍은 쓰레드 만큼의 어쩌면 더 많은 복잡성을 프로그램 내에 도입할 뿐만 아니라, 쓰레드 지원이 허름하던 시절의 성능 이점을 가져다 주지도 않는다. (쓰레드 프로그래밍과 이벤트 기반 프로그래밍의 비교에 대해서는 다음 기회에 글을 써보도록 하겠다.) Parallel programming 문맥에서의 Co-routine은 go to를 대체하는 loop와 같은 programming constructs의 역할과 비슷한다고 생각되는데, 주요 프로그래밍 언어에서 구현되어 있지 않을 뿐만 아니라, (그 때문에) 보편적으로 practice가 확립되어있지 않다. (학교에서 가르치지 않는다.)

Conclusion

concurrently shared state라는 문제 자체의 복잡성이 쓰레드 프로그래밍이나 이벤트 기반 프로그래밍의 복잡성을 야기하고, 어느 해결책을 선택하느냐는 trade-off의 문제이나, 현재로서는 쓰레드가 더 나은 해결책으로 보인다. go to를 대체하는 loop constructs와 같이 쓰레드를 대체하는 프로그래밍 언어 레벨의 해결책이 필요하다는 것에는 크게 공감한다.

Threads Considered Harmful 더 읽기"

Prefer java.net.URI to java.net.URL

Prefer java.net.URI to java.net.URL

Problem of java.net.URL

URL의 host 부분의 equivalence가 host string의 equivalence를 기준으로 하는 것이 아니라, IP address의 equivalence를 기준으로 한다. 또한, equivalence 관련 메서드 (equils(), hashCode())이 일어날 때마다, IP address resolving을 동반한다.
equivalence 관련 operation이 사용되지 않는다면 별로 문제가 없으나, equivalence 관련 메서드를 자주 사용하면, 상당히 느릴 수 있으며, HashMap의 key로 java.util.URL을 사용할 경우, host 부분만 다르고 각 host들의 IP가 같다면, HashMap에는 하나의 key만이 등록되는 의도하지 않은 문제가 발생할 수 있다.

Solution

1. Overriding java.net.URLStreamHandler

java.util.URL 클래스의 equivalence 관련 구현은 모두 java.util.URLStreamHandler의 구현에 위임되어있다. 그리고 java.util.URL 클래스는 static 메서드인 setURLStreamHandler()를 통해, 이 구현을 변경할 수 있다.

2. Using java.net.URI

Java 1.4에 java.net.URI가 추가되었고, 이 클래스의 equivalence는 String representation의 equivalence와 동일하다. java.net.URL의 hashCode() 메서드는 각 파트에 해당하는 String.hashCode()의 합인 반면, java.net.URI의 hashCode() 구현은 제대로 된 hash 값을 구하도록 구현되어있다.

References

Prefer java.net.URI to java.net.URL 더 읽기"

Framework 2.1: RubyOnRails vs. TurboGears, Part 1

지난 토요일에 Framework 2.1이라는 행사에 다녀왔다. Framework 2.1은 웹 개발 프레임워크의 대안을 모색하기 위한 행사이고, 대안언어축제에서 파생된 행사인 듯 하다. 이번 행사는 RubyOnRails와 Django를 각각 소개하고, 그 둘을 비교하는 패널 토의를 하고, 다음번 행사에서 소개할 TurboGears와 Seaside를 간략하게 소개하는 형식으로 진행되었다.

행사장은 NCsoft에서 지원해주었는데, 참석한 인원이 꽤 많아서 행사장이 상당히 좁았다. 하지만, 이런 행사를 지원해주는 게 어딘가. 개발자들, 특히 뛰어난 개발자일 수록, 연봉이나 복지만으로 회사를 선택하지는 않는다. 치열해져가는 경쟁 속에서 뛰어난 개발자를 고용하기 해서는 이런 투자도 어느 정도 필요하다고 생각한다. NCsoft 외에도 어느 정도 업계에서 성공한 회사들이 개발자 커뮤너티를 지원해주는 문화가 앞으로는 널리 정착되었으면 좋겠다.

기조연설 – 이만용 님

기조연설은 이만용 님이 해주셨는데, 역시 연륜이 있으신만큼 (그렇게 보이시는 만큼) 언변도 좋으셔서서로 모르는 개발자들이 모인 자칫 딱딱할 수 있는 자리를 재미있고 부드럽게 만들어주셨다.

이만용님은 APM, Python CGI, Zope 등을 시도해보시다가 Django와 TurboGears들 중 최근 TurboGears를 선택했다고 하셨다. ‘Write-only code’라는 말로 웹 개발의 현주소를 표현하셨다. 애자일 프로그래밍 언어와 MVC가 필요하다는 언급을 하셨다.

RubyOnRails – 황대산

첫번째 세션은 황대산님의 RubyOnRails 세션이었다.

기본적인 튜터리얼을 진행하시고는 migrate, breakpoint, association, routing 등의 약간의 고급 기능도 설명해주셔서 재미있게 들을 수 있었다. 그 중에 migrate가 가장 흥미로웠는데, 대부분의 웹 어플리케이션들은초기에 DB 셋업이라는 벽을 뛰어넘어야하기 때문에, 대단히 사용하기가 어렵기 마련인데, migrate를 사용해 각 버전간의 변경사항에 대한 transaction을 ruby code로 기술해주는 것만으로, 초기 DB 셋업 뿐만 아니라 버전간 migration (원래의 목적으로 보이는)도 편하게 수행할 수 있었다.

breakpoint는 script를 통해서 설정된 breakpoint 시점의 환경에 접근할 수 있게 해주는 기능이고, association은 DB schema만으로 표현할 수 없는 객체간의 관계를 선언적으로 설정해주는 기능이다. association 선언에는 객체 이름 외에는 아무것도 필요하지 않기 때문에, 역시 rails의 전체를 관통하는 철학인 convention을 사용하고 있음을 알 수 있다. routing은 rails의 디폴트 URL convention에서 벗어나 이를 customize할 수 있는 방법을 제공한다.

이 외에도 기본적인 validation, javascript를 쓰지 않아도 되게 해주는 RJS, web services를 지원하기 위한 webresource와 같은 기능도 간략히 소개되었다.

질답 시간에는 웹 어플리케이션에서 자주 필요로 하는 기능 중 하나가 authentication/authorization인데, 이를 rails에서 어떤 방식으로 support하는가라는 질문을 던졌는데, rails에서는 이러한 기능이 core에 들어있진 않고 extension/plugin 수준에서 지원한다고 한다.

실제로 rails가 사용되는 곳이 어떤 곳이 있는가라는 질문이 2번씩이나 나왔는데, 미국에서는 역시 유명한 43things, basecamp, campfire, writeboard, 그리고 odeo라는 podcast directory 사이트가 있고, 한국에는 olaworks가 있다고 한다. (olaworks가 유일한 사이트?)

rails를 통해서 SQL, javascript, CSS를 쓸 필요가 없어진다고 한다. 여러가지 언어와 여러가지 환경에서 프로그래밍을 한다는 것은 생산성을 떨어뜨리는 요소중의 하나다임에 틀림없다. 하지만, SQL, javascript, CSS는 부차적인 문제다. 언제쯤이면 HTML을 쓰지 않고도 웹 어플리케이션을 만들 수 있을까? ;-)

Django – 김형용, 이정민

Django (장고, 혹은 쟁고) 는 웹 개발 프레임워크들간의 비교에서 흔히 등장하는터라 이름을 들어봤지만, 자세한 내용을 보게된 것은 처음이었다.

일단 Django는 Model과 View로 나누어지고, 여기에 template이 추가된다. 이 때 View는 Model 2의 Controller 역할, template은 View 역할이라고 보면 된다. (MFC에서도 사용되었던 Document-View pattern에 따라 모델링된 듯 하다.)

Django는 하나의 project 아래에 다수의 application을 생성하는 구조를 가지고 있다. 어느 정도 규모가 있는 어플리케이션 쪽을 노리고 만들어진 느낌이 들었다. application을 추가(startapp)할 때마다 configuration에 해당 application을 추가하지 않으면 웹에 반영되지 않는 것이라든가 Model을 변경할 때마다 syncdb를 하는 것도 그런 느낌이 들게 만들었다.

project에는 admin application이 기본적으로 생성되는데 이를 이용해서 다른 application들의 데이터에 대한 기본적인 조회나 조작이 가능했다. 이 admin application은 authentiation까지 지원되는 대단히 완성도 있는 admin application으로 웹 어플리케이션을 만들 때 필요한 어드민 페이지를 만드는 수고를 어느 정도 덜어주고 있었다.

Django의 template은 variable의 값이나 variable에 (helper에 해당하는) filter를 적용한 값을 표시할 수 있고, 조건이나 반복을 위해서 미리 정의된 tag가 존재하며 이를 확장할 수 있었는데, 이는 JSP와 거의 유사한 것이었다. Django의 View도 Struts의 Controller와 거의 유사한 모양새를 가지고 있었다. 즉, Model에서 특정한 context들을 만들어낸 후, 각각에 적당한 이름을 부여하여 template에 넘겨주는 방식이었다.

Django의 middleware는 정해진 interception point에서 동작하는 일종의 filter로 최소한의 AOP를 구현하고 있었다.

Framework 2.1: RubyOnRails vs. TurboGears, Part 1 더 읽기"

소프트웨어 품질 희생의 일상화

"그렇게 하는 게 좋은 건 알겠는데, 개발이 느려지잖아."

소프트웨어 프로젝트에 있어서 빠른 개발 즉, 효율성이 중요하다는 것은 누구나 공감하는 사실일 것이다. 수많은 방법론들과 도구, 라이브러리, 프레임워크들은 한결같이 ‘효율성’을 강조한다. 그렇다면 소프트웨어 제품에 있어서 효율성이란 무엇인가? 시간당 코드 라인 수? 일주일에 구현하는 기능의 수?

가장 간단하게 정의한다면, 시간당 추가되는 소프트웨어의 기능과 품질이 될 것이다.* 하지만, 많은 사람들은 이 효율성이란 기준에서 품질을 무시한다. De Marco가 Peopleware에서 언급했듯이, time-to-market 압박에 가장 먼저 희생되는 것은 기능이 아니라 소프트웨어 품질이다. 소프트웨어의 품질**은 고객이나 매니저의 눈에 띄지도 않기 때문이다.

물론, 현실적으로 기능에 대비해 품질을 희생할 수 밖에 없는 상황은 비일비재하다. 어느 날 갑자기 사장이 인터뷰를 통해서 다음달에 어떤 소프트웨어 제품이 나올 거라고 공언해놓고, ‘자 이제부터 이러이러한 소프트웨어를 만들어라’ 하면서 일감을 던져주는 것을 받아본 경험은, 소프트웨어 개발자로서의 경험이 어느 정도 있는 사람이라면 한번 정도씩은 있을 것이다. 이러한 문제가 너무나 심각해서, 어떤 사람들은 품질을 희생하는 것이 아니라 오히려 기능을 희생해야 한다고 얘기하기도 한다. 무엇이 옳든 간에, 소프트웨어의 기능과 품질을 둘 다 만족시킬 수 없는 상황에서 이 문제는 전형적인 trade-off 문제가 된다. 현재 개발자 커뮤너티의 대체적인 의견은 이러한 trade-off에 의한 품질의 희생은 불가피하며 iterative development를 통해서 극복이 가능하다고 생각하는 편이다.

문제는, 효율성을 이유로 들어 소프트웨어 품질의 희생을 일상화하는 경우다. 소프트웨어 품질을 일상적으로 희생하게 되면, 소프트웨어 라이프사이클이 이어질수록 소프트웨어 효율성에 나쁜 영향을 미친다. 결국에는 그 소프트웨어를 버리거나 그냥 대충 때우는 것 외의 대안이 없는 상태가 되어버린다. 물론 모든 소프트웨어가 긴 수명을 가지지는 않는다. 학교에서의 소프트웨어 프로젝트와 같은 일시적으로 필요한 단순한 소프트웨어나, 빠르게 사용자 요구가 변화하고 사라지곤 하는 웹 어플리케이션과 같은 경우에는 품질을 희생하더라도 별 문제가 없을 수 있다. 하지만 반대로, 긴 수명을 가지는 소프트웨어도 존재한다. 그러한 소프트웨어를 매번 버리고 새로 만드는 것은 프로토타이핑의 목적이 아니라면 매우 비효율적이다. 설령 어떤 목적으로 (이를 테면, 경험이 부족한 도메인의 실험적인 프로젝트) 매번 버리고 새로 만든다 하더라도, 그럴 때마다 기존의 것을 가능한 한 재사용하는 방식을 추구해야 한다.

소프트웨어의 품질이 지속적으로 효율성에 영향을 미치는 장기적인 소프트웨어 프로젝트에서, 소프트웨어 품질의 희생을 일상적으로 받아들이는 태도는 잘못된 것이다. 마치 고급 아파트를 지을 때의 마감공사를 조립식 건물을 지을 때처럼 대충하는 것과 다를 바가 없다. 이러한 태도는 소프트웨어 품질 자체에 관해 무지하거나, 소프트웨어 프로젝트의 수명에 따라 품질에 대한 태도를 다르게 해야 한다***는 사실에 대해 무지하거나, 소프트웨어 프로젝트의 수명에 대해서 착각을 하고 있기 때문에 발생하는 것이다.

어떤 사람은 소프트웨어 품질에 대한 태도가 단지 개발자 개개인의 ‘스타일’의 문제이고 이를 받아들여야 한다고 얘기한다. 물론, 특정 도메인에서 소프트웨어 개발을 지속적으로 해온 사람은 소프트웨어 품질에 대한 특정한 태도를 내면화하여 그것을 ‘스타일’로 가질 수는 있다. 하지만, 그것이 ‘스타일’이라고 해서 합리화될 수는 없는 것이다. 소프트웨어 품질에 대한 태도는 개발자에 따라 달라지는 것이 아니라 소프트웨어 제품 또는 프로젝트에 따라 달라져야 하는 것일 뿐이다. 여기서 ‘스타일’이라고 지칭되는 것은 사실상 ‘습관’에 불과한데, 대부분의 평범한 소프트웨어 개발자들은 자신의 습관에 따라 행동하고, 때로 특정한 도메인이나 특정한 소프트웨어 프로젝트에서는 그 습관을 통해 최고의 능력을 보여줄 수도 있다. 하지만, 뛰어난 개발자들은 그러한 습관을 내면적인 규칙(discipline)을 통해 극복하고, 도메인이나 소프트웨어 프로젝트에 따라 서로 다른 적절한 태도를 취하고, 어느 곳에서나 최고의 능력을 보여줄 수 있다.

결국, 장기적인 소프트웨어 프로젝트에서 조차도, 소프트웨어 품질 희생의 일상화를, 효율성을 근거로 하여, 합리화하기를 즐기는 사람들은 대체로 자신의 습관이나 부족한 능력에 대한 변명을 하고 있을 뿐이다. 정말로 뛰어난 개발자는 거의 항상 (최고가 아니라) 적절한 – 기능과 품질 양쪽 모두에서의 – 효율성을 성취할 수 있기 때문이다.

* 소프트웨어의 특성을 기능과 품질로 양분한 것은 논의의 간결함을 위한 것이다. 소프트웨어는 그 복잡성만큼이나 요구되는 특성들이 많다.
** 이 때, 소프트웨어의 품질이란, 소프트웨어라는 제품이 가지는 특성으로서의 품질이 아니라, 소프트웨어 자체의 품질, 굳이 예로 들자면 코드의 품질과 같은 내부적인 특성을 얘기한다.
*** 소프트웨어의 수명만이 소프트웨어 품질에 대해 요구되는 태도를 결정짓는 것은 아니다. 다양한 비즈니스적인 요구나 도메인에 대한 개발자의 지식 등 여러 가지 요소들이 모두 영향을 미칠 수 있다.

소프트웨어 품질 희생의 일상화 더 읽기"

An Interview with Tim Converse

Yahoo! Content group의 engineering manager인 Tim Converse가 Crawler trap과 redirection에 대해서 언급하고 있다.

Excerpted from An Interview with Tim Converse, part 2 (emphasis made by me):

JQ: You talked about comprehensiveness. There’s this perception that there’s the web that most of us see and then this dark web: the stuff that the crawlers don’t reach. How do we try to get that data into the index? Are there barriers that webmasters put up that they should avoid to help us better index the content?

A: At it’s simplest, webmasters aren’t aware of robots.txt and it’s uses. Redirection can also be problematic if people create content by creating lots of domains or hosts so we encourage people to organize their sites in many documents before they get a new host.

And of course, there’s also the issue of crawler traps which some people do intentionally but much more often, they’ve unintentionally created crawler traps….

JQ: …and a crawler trap is…

A: A crawler trap is something where you crawl a page and it has a link, usually in the same site that’s dynamically created and then you follow that link and it has another analogous link that’s dynamically created and often, just because people make mistakes, you’re attaching on another directory every time which doesn’t exist and takes you back to an automatically generated error page which has the same link. So you can fall into traps where there are an infinite number of pages that don’t have any content.

Another thing people can do to help us is, this is sort of geeky but, don’t make page not found pages that return a status 200.

JQ: I was just about to ask that. 404 pages back in the day, were these ugly grey things with block text that all looked the same and now they’re done up to look like regular pages to be more appealing to users.

A: We do actually have ways of detecting that but it’s a lot easier for us if a web server just says, "this page doesn’t exist" as opposed to creating a nice page for the user that to a crawler looks like any other page. In general, if the server tells us 404, then we discard it.

YQ: I worked for a company that used CIDs instead of cookies to follow users through the site and it turned out to be a disaster. We went from having pretty much every page indexed to hardly any. So what about CIDs and how they affect the crawlers?

A: If you have differences in the URL that don’t actually make a difference in the site, that can be hard for us to untangle. We’re getting better at it. One of the scenarios you’re talking about there would just create a lot of duplicates for us. So it’s nicer for us if we have one URL per actual content but we understand that you’re not designing this just for us. And we obviously do a lot of duplicate detection–actually, we do duplicate detection in a couple of different ways. Finding out if documents are the same; finding out if sites are mirrors of each other.

JQ: This question came up today on a mailing list that I’m on. The concern for this particular company is that they want to move their site to a new domain but they don’t want to become invisible for the next six months or year or however long it’ll take for people to point to their new website. What can we tell people like that?

A: We can tell them that in the future, if you actually want to move your site, you want to use a 301 redirect which will do as much of the right thing as we can.

YQ: What actually happens there? I’ve heard of companies who have used 301 redirects and yet their old pages continued to show up in the search engines anyway. Why is that?

A: The underlying problem is that people out there haven’t changed their links and search engines do pay attention to links.

I can’t give you a date, but we’re changing how we deal with redirects. The thing about redirects is that everyone thinks it’s obvious how a search engine should treat them and the obvious answer is not really that helpful. Any policy you develop with redirects is going to make someone unhappy but what we’re about to roll out we will pay better attention to 301 redirects and the exact problem you’re talking about should be less.

[In the time since we met with Tim, the team has rolled out a fix for 301/302 redirects. Documents will be handled by the new redirect policy as they are re-crawled and re-indexed and webmasters will start to see many of the sites change in the next couple of weeks. The index should be fully propagated within a month. See Tim Mayer’s Webmaster World presentation for details.]

An Interview with Tim Converse 더 읽기"

Sliding Gallery from Flickr

http://lastmind.net/beta/sliding_gallery/

팀 분위기도 어수선하고, 심심하고 해서, Kevin LeAjax-ready sliding gallery library를 이용해서 잡질을 약간 해보았습니다. Flickr (Flickr는 오늘도 다운타임이군요. 쯧쯧.)의 제 공개된 사진들을 연동하는 것이었는데요. 일단은 sliding gallery library를 활성화하고 데이터(즉, 사진들의 URL) 를 집어넣는, 그러니까 사용하는 js 파일을 생성하는 방식으로 만들어두었습니다. Flickr에서 데이터를 가져오는 부분은 Flickr.rb를 사용했습니다. (물론 ruby 라이브러리입니다.) 웹디자인은 그냥 라이브러리의 데모 페이지를 긁어왔으니 놀라시지는 않으시길 바랍니다.

동적으로 Flickr와 연동해주는 것이 최선이겠지만, 아직은 그럴 필요까지는 못 느끼고 있고, 귀찮은 면도 있어서, 적당히 돌아가는 것을 확인하는 정도로 그만두었습니다. 좀 더 public한 demo를 만들려면 물론 사용자 아이디를 입력받아주는 것이 좋겠지만, sliding gallery library 자체에도 몇가지 문제가 보이고 해서 일단은 hold하기로 했습니다. 당장 제 눈에 띈 library의 문제들은 다음과 같습니다.

  • 스크롤 크기가 static하게 정해져있어서, 사진 단위로 스크롤 되지 않는다.
  • Thumbnail 이미지들이 한꺼번에 로딩되서 수가 많아지면 페이지를 로딩하는데 한참 걸린다.
  • 처음 나올 큰 이미지가 html에 코딩되어있고, 적절한 Trigger를 발생시킬 지점이 없다. (전 직접 javascript로 코딩해넣었습니다.)
  • 클릭 이벤트 외에 스크롤 이벤트에 반응하도록 하는 콜백이 없다. (스크롤할 때 선택 이미지를 전환한다든가 하는 효과를 줄 수 없죠.)
  • 몇가지 버그들.

시간이 나면 제작자에게 컨택해서 얘기해보거나 직접 고쳐볼 요량입니다만, 귀찮아서 말이죠.

최근 들어 javascript 언어 기반의 라이브러리나 프레임워크들이 상당히 많아지고 성숙되고 있는 듯 하군요. Sliding Gallery library 같은 경우도 아직은 순수하게 따로 제작된 것들이나 상용 솔루션들보다는 못하지만, 조금만 더 다듬으면 재사용이 쉽다는 이익을 통해 그런 것들을 넘어서는 것도 시간 문제인 것 같습니다.

Sliding Gallery from Flickr 더 읽기"

A Brief Look at C++0x

Bjarne Stroustrup의 글 A Brief Look at C++0x에서는 C++ 프로그래밍 언어의 다음번 표준안인 C++0x에 대해서 소개하고 있다. C++0x에서의 변경사항에 대한 기본 원칙들을 먼저 설명하고, 언어상의 변화, 그리고 라이브러리상의 변화 순으로 내용을 전개하고 있으므로, C++0x에 관심이 있는 사람이라면 반드시 한번 읽어봐야할 글이다.

C++0x에 들어갈 라이브러리상의 변화에 대해서는 이 블로그에 여러번 적었기 때문에 이 글에서는 언급하지 않기로 하고, 다만 언어상의 흥미로운 변경사항들에 대해서 간략하게 적어보기로 한다.

Template alias (a.k.a. template typedefs)

template<class T> using Vec = vector<T,My_alloc<T>>;

template을 많이 활용하기 시작하면서 가장 먼저 느끼게 되는 불편함 중의 하나가 특정 template의 특정specialization을 반복해서 사용해야하는 경우가 잦아진다는 것이다. C/C++ 프로그래머가 이 때 떠올리는 것은 typedef 시스템이다. 즉, template의 specialization은 typedef로 재정의할 수 없을까? C++0x에서는 typedef 키워드를 사용하지는 않았지만, using 키워드를 사용해서 이러한 요구를 반영하고 있다.

template parameter의 끝에 달라붙는 >가 반복될 경우에 space가 더이상 필요하지 않아진 것도 눈여겨볼 만한 점이다.

Sequence constructor

Vec<double> v = { 2.3, 1.2, 6.7, 4.5 };

n1711문서를 소개하는 글에서도 언급을 했지만, array나 struct 뿐만 아니라 일반적인 user-defined type도 initializer list를 사용한 construction이 가능해졌다. 이를 통해서 array를 써야하는 경우는 거의 없어지고, vector 등 container들의 활용 가치는 훨씬 높아지리라고 예상된다.

Concepts

template<Container C, Predicate Cmp>
    where Can_call_with<Cmp,typename C::value_type>
    void sort(C& c, Cmp less);

최근 부각되고 C#, D 등의 언어에서는 이미 활용되고 있는 programming constructs 중 하나인 concepts는 C++의 커다란 특징 중 하나인 template의 강점을 더욱더 강하게 만들어주는 특징이 될 것이다. C++ standard library나 체계적으로 만들어진 C++ 라이브러리들에서는 이미 concepts의 개념이 항상 문서화되어왔었고, C++0x에서는 다만 그것을 프로그램 상에서도 명시 그리고 강제를 할 수 있게 해준 것이다. 이를 통해서 프로그래머가 생각하는 바를 좀 더 명확하게 표현할 수 있고 오류에도 무엇이 문제인지 좀 더 명확하게 표현될 수 있을 것이다.

Automatic type deduction

for (auto p = v.begin(); p!=v.end(); ++p)
    cout << *p << endl;

static typing을 사용하는 언어라고 해서 불필요하게 변수의 타입을 선언해야하는 것은 아니다. 주어진 context에서 컴파일러는 타입을 deduce해낼 수 있기 때문이다. auto라는 키워드가 그리 마음에 들지는 않지만, 당연해 보이는 루프 변수의 타입 따위를 기억해내는 데 노력을 들일 필요가 없어진다는 것은 마음에 쏙 든다.

More Information

그동안 C++0x의 라이브러리상의 변화에만 관심을 기울여서 언어상의 변화에는 소홀했었는데, 여러가지 고무적인 변화가 많은 언어상의 변화쪽에 흥미가 생기기 시작했다. Draft와 Issue 리스트를 살펴봐야할 듯 하다.

A Brief Look at C++0x 더 읽기"

Vim 7의 새로운 기능 사용하기

Vim 7을 설치하더라도 기존 버전과 달라진 점을 쉽게 깨닫기는 힘들다. First look at Vim 7이라는 글을 참고하여 Vim 7의 새로운 기능을 쓰는 방법을 간단하게(?) 정리해보았다.

Spellcheck

:help spell
Spellcheck 기능에 대한 도움말.
:set spell spelllang=en_us
Spellcheck 기능 켜기.
zg
cursor 위치의 단어를 good word로 취급. (zug로 undo)
zw
cursor 위치의 단어를 wrong word로 취급. (zuw로 undo)
z=
cursor 위치의 단어를 대체하는 추천 단어를 리스팅.

Undo branches

특정 시간이나 변경수를 이용하여 undo/redo가 가능해진 기능. 자주 쓸모가 있을 것 같지는 않다.

:help undo-branches
Undo branches 기능에 대한 도움말.
:undolist
undo. 기존의 u와 동일.
g-
undo. 기존의 u와 동일.
g+
redo. 기존의 Ctrl-r과 동일.
earlier 3
3번의 변경 이전으로 undo.
later 3
3번의 변경 이후로 redo.
earlier 3s
3초 이전으로 undo.
later 3h
3시간 이후로 redo.

Tabs

탭기능.

:tabnew
새로운 탭 추가.
gt
다음 탭으로 이동.
:tabdo %s/oldvariable/newvariable/g
모든 탭에 특정 명령(replace) 수행.

Etc.

Ctrl-x Ctrl-o
code completion. html element 같은 경우에는 잘 동작하지만, 프로그래밍 언어에서는 어떻게 동작하는지 잘 모르겠다.
:sort
라인들을 알파벳순으로 정렬.

Vim 7의 새로운 기능 사용하기 더 읽기"

Can't replace files with Subversion

refactoring을 하다보면 AAA라는 클래스를 지우고, BBB라는 클래스를 AAA로 바꾸고 싶을 때가 있다. eclipse의 refactoring 기능을 사용하면 이러한 refactoring은 매우 자연스럽고 쉽게 수행할 수 있다. 하지만, commit할 때가 문제다. 다음은 이를 시도할 때 subclipse (정확히는 subversion) 에서 발생하는 에러다.

delete --force AAA.java
D AAA.java
move BBB.java AAA.java
Entry already exists
svn: 'AAA.java' is scheduled for deletion; it must be committed before it can be overwritten

subclipse의 문제가 아니라 file 이름 기반으로 entry들을 관리하는 subversion 자체의 문제로 보인다. 물론 해결하는 방법이야 존재한다. 가장 간단하게는 deletion과 rename을 따로 commit하는 것이 바로 그것이다. 하지만, 항상 code repository에는 working version 만을 넣겠다는 rule을 깨뜨려야하기도 하다.

Can't replace files with Subversion 더 읽기"