예전에 다언어 프로그래밍
이란 주제로 블로그에 글을 쓴 적이 있다. 그 글에서는 같은 일을 하는 프로그램을 세 가지 언어로 짠 것을 비교했었다1:
그루비:
def number=0
new File (args[0]).eachLine { line ->
number++
println "$number: $line"
}
C#:
using System;
using System.IO;
class LineNumbers {
static void Main(string[] args) {
int number = 1;
Array.ForEach(File.ReadAllLines(args[0]),
line => Console.WriteLine("{0}: {1}", number++, line));
}
}
자바:
package com.nealford.polyglot.linenumbers;
import java.io.*;
import static java.lang.System.*;
public class LineNumbers {
public LineNumbers(String path) {
File file = new File(path);
LineNumberReader reader = null;
try {
reader = new LineNumberReader(new FileReader(file));
while (reader.ready()) {
out.println(reader.getLineNumber() + ":"
+ reader.readLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException ignored) {
}
}
}
public static void main(String[] args) {
new LineNumbers(args[0]);
}
}
지금 봐도 자바 코드가 약간 더 길다. ^^
그리고 나서는 C#이 여러 모로 제일 편하니까 그냥 하나의 언어로 통일하는 것은 어떨까하는 물음으로 글을 끝맺었었는데, 그로부터 5년 동안 나는
C#보다 자바를 더 많이 쓰게 되었다.
아…
물론 과장을 좀 보태서 말이다. ^^ 그리고 여러가지 일들을 파이썬으로 하는 법도 익히게 되었다. 결과적으로 나는 글에서의 바램과는 정반대로 5년 사이 C/C++/C#/자바/파이썬 다언어 프로그래머가 된 것이다.
2014년에도 다언어 프로그래밍의 중요성은 여전히 유효할까? 아이러니컬하게도 이건 그때보다도 더 아니라는 생각이 드는데, 왜냐하면
C#이 그때보다 더 강력해졌다.
dynamic
C# 4.0부터는 dynamic
타입이라는 게 생겼다. 이 타입을 쓰면 컴파일 타임에 하던 타입 체크를 런타임으로 미루게 된다. 예를 들어 dynamic
타입을 사용한 아래 C# 코드는
class Dog {
void Run() { }
}
class Robot {
void Run() { }
}
static void Do(dynamic obj) {
obj.Run();
}
Do(new Dog());
Do(new Robot());
어떤 타입이든 void Run()
을 구현하기만 하면 Do()
의 파라미터가 될 수 있다. 굉장히 간단한 규칙이고, 어떻게 보면 안 되는 게 더 이상해 보이지만 C# 4.0 이전이나 일반적인 정적 타입 언어로는 불가능하다. Dog
과 Robot
사이에 공통 인터페이스가 없기 때문이다.
async/await
C# 5.0부터는 복잡하고 어려웠던 비동기 프로그래밍을 혁명적으로(!) 쉽고 편하게 짤 수 있게 되었다. 예를 들어 아래 같은 코드는
void FetchWebPage(string uri) {
var client = new WebClient();
string page = client.DownloadString(uri);
Console.WriteLine(page);
}
client.DownloadString(uri)
를 호출할 때 프로그램이 잠깐 멈추게 된다. 웹 사이트에서 페이지를 가져오는데 시간이 걸리기 때문이다. 최악의 경우—회선이 느리거나 사이트에 문제가 있는 등—페이지 다운로드에 수십 초가 걸렸다면, 프로그램도 그 시간 동안 멎어 있게 된다.
이런 문제는 현실에서 자주 일어나기 때문에 당연히 개발자들은 해결책을 마련해 두어야 한다. 한가지 방법은 저런 코드를 별도의 쓰레드로 분리하는 것이다:
handler.post(new Runnable() {
@Override
public void run() {
WebClient webClient = new WebClient();
string page = client.DownloadString(uri);
text.setText(page);
}
});
안드로이드 개발자라면 이런 코드들이 실제로 얼마나 개떡(?)같은지 잘 알고 있을 것이다. 코드를 좀 덜 지저분하게 만들기 위해 핸들러 대신 AsyncTask
같은 걸 쓰기도 하는데, 근본적으로 큰 차이는 없다.
두번째는 C/C++/자바에서 많이 쓰는 방법인데, 정교하게 설계된 비동기 I/O 프레임웍을 쓰는 것이다. 이 경우의 단점은 간단한 코드는 그냥 쓰레드 하나 만들어 돌리는 것보다 더 복잡해진다는 것이다. 물론 API를 배우고 쓰는 자체도 만만치 않다.
반면 C# 5.0에서는 그냥 단어 몇개 붙여주는 것으로 비동기 프로그래밍이 가능하다:
async void FetchWebPage(string uri) {
var client = new WebClient();
string page = await client.DownloadStringAsync(uri);
Console.WriteLine(page);
}
FetchWebPage()
는 호출되면 평상시처럼 실행되다가 await
지점에서 client.DownloadStringAsync(uri)
을 호출하고 곧바로 리턴한다. 바로 이 점이 이전의 코드와 결정적으로 다른 점이다. 이전의 동기 코드에선 client.DownloadString(uri)
이 리턴할 때까지 이 지점에서 무한정 머물러 있지만, 비동기 코드에선 FetchWebPage()
를 호출한 곳으로 바로 리턴한다. 그리고 나서 얼마간의 시간이 흘러 client.DownloadStringAsync(uri)
가 웹에서 가져온 페이지를 리턴하면 뒷부분의 남은 코드가 계속 실행된다. 별도의 쓰레드를 만드는 것, 리턴값을 보관하는 것, 남은 코드의 실행을 재개하는 것, 중간에 발생할 수 있는 예외 처리 등은 컴파일러가 자동으로 코드를 만들어 준다.
REPL
그렇지만 C#이 유일한 언어가 되려면 아직 넘어야 할 산이 많이 남아 있다. 그중 하나는 REPL의 부재다. REPL이란 read–eval–print loop
의 약자로 파이썬 대화형 환경 같은 걸 말한다. 컴파일 과정없이 즉석에서 코드를 입력하고 결과를 바로 알 수 있기 때문에 아주 편리하다.
그런데 사실 C#에도 이미 REPL이 있다. 제일 잘 알려진 것은 LINQPad:
굉장히 강력하고 편리하니 C# 개발자라면 꼭 써보기 바란다.
두번째는 Mono 프로젝트의 CsharpRepl이 있는데 LINQPad 만큼 좋진 않은 것 같다(몇번 안써봄).
다만 문제는 이 두 REPL이 마이크로소프트에서 만든 게 아니다 보니 비주얼 스튜디오와 연동이 안된다는 것이다. 마이크로소프트에서 만든 REPL은 차기 비주얼 스튜디오에 포함될 예정으로 있다.
보일러플레이트 코드
지난번 글에서도 다루었지만 C#이 자바보단 보일러플레이트가 훨씬 적지만 다른 언어에 비하면 여전히 너무(!) 많다. 이 문제의 해결은 전적으로 C# 언어 개발팀에 달려 있는데, 어떻게 될지 모르겠다.
그래서…?
쓰고 보니 C# 하나로는 역시 부족하다. ^^ C#과 파이썬 조합이 제일 무난해 보인다. 따라서 2014년에도 다언어 프로그래밍의 중요성은 여전히 유효한 것으로 최종 결론.
- 그루비와 자바 코드는 The ThoughtWorks Anthology 책에서 가져 왔다. ↩
댓글 없음:
댓글 쓰기
댓글을 입력하세요. 링크를 걸려면 <a href="">..</a> 태그를 쓰면 됩니다. <b>와 <i> 태그도 사용 가능합니다.
게시한지 14일이 지난 글에는 댓글이 등록되지 않습니다. 날짜를 반드시 확인해 주세요.