오딘으로 작은 키-값 스토어와 Pub/Sub를 구현하며 메모리 관리, 동시성, 표준·벤더 라이브러리, 컴파일 속도, 장단점, 그리고 어떤 분야에 적합한지까지 살펴본다.
URL: https://dayvster.com/blog/is-odin-just-a-more-boring-c/
제목: 오딘은 그냥 더 지루한 C인가?
읽는 데 15분
2025년 10월 4일 토요일
작은 키-값 스토어와 Pub/Sub로 살펴보는 오딘
최근 글들은 React와 JavaScript에 집중하던 예전과 달리 Zig와 C를 깊게 파고들고 있다. 이건 방향 전환이라기보다 뿌리로 돌아가는 것이다. 나는 13살에 C와 C++로 프로그래밍을 시작했고, 그 뒤로 C, C++, Rust, 그리고 지금은 Zig 같은 시스템 프로그래밍 언어로 정말 다양한 프로젝트를 만들어왔다. 취미로 하는 실험과 커스텀 리눅스 유틸리티부터 차량 인포테인먼트, 추적 솔루션, 저수준 컴포넌트 같은 임베디드 실무까지—나는 늘 시스템 프로그래밍의 힘과 정밀함에 끌렸다. 동시에 내 환경을 위한 도구들을 만들고 백엔드 엔지니어링도 꽤 해오며, 풀스택 전문성과 저수준 제어에 대한 열정을 잘 섞어왔다.
나를 포함한 많은 사람들이 처음엔 오딘을 주로 게임 개발을 위한 언어로 치부했다. 그런데 그게 얼마나 멍청한 생각이었는지 깨닫는 데 시간이 걸렸다—정말 여러 번. 게임 개발이란 게 뭔지 곰곰이 따져보자. 복잡한 시스템을 효율적이고 빠르며 신뢰성 있게 만들어야 한다. 그래픽, 물리, 입력 처리, 네트워킹 등 온갖 것을 다룬다. 동시성, 메모리 관리, 저수준 최적화를 마주한다. 다시 말해 게임 개발은 오딘 같은 시스템 프로그래밍 언어에 딱 맞는 영역이다.
즉, 게임 개발을 염두에 둔 언어라면 일반적인 시스템 프로그래밍이나 데스크탑 애플리케이션에도 매우 잘 맞을 것이고, 대체로 GC 없이 수동 메모리 관리를 하는 게임 dev 특성상 임베디드에도 어느 정도 쓸 수 있어야 한다.
그래서 이 멍청한 선입견을 반성하고는, 오딘을 공정하게 한 번 써보며 유용한 걸 만들어 보기로 했다.
유용하다고 표현했지만 사실 내가 새 언어를 체험할 때마다 만들어보는 걸로 정해둔, 아주 작은 키-값 스토어와 Pub/Sub 시스템을 만들기로 했다. 독창성으로 상을 탈 일은 없고, redis 팀이 긴장할 일도 없다. 프로덕션에서 쓸 만한 기능은 거의 없는, 정말 최소 뼈대만 있는 구현이다. 그래도 언어와 그 능력을 이해하기엔 좋은 연습이다.
시스템 프로그래밍과 관련 있는 몇 가지 측면—자료구조, 메모리 관리, 동시성, 네트워킹—을 다루기 때문이다.
그리고 이렇게 기본적이고 빈약한 걸 만들더라도, 더 많은 기능을 얹어 실험하고 탐구할 여지는 충분하다.
내 초기 최소 POC는 단순하고 직관적이었다.
package main
import "core:fmt"
import "core:time"
KVStore :: struct {
store: map[string]string,
}
kvstore_init :: proc() -> KVStore {
return KVStore{store = map[string]string{}}
}
kv_put :: proc(kv: ^KVStore, key: string, value: string) {
kv.store[key] = value
}
kv_get :: proc(kv: ^KVStore, key: string) -> string {
if value, ok := kv.store[key]; ok {
return value
}
return ""
}
PubSub :: struct {
subscribers: map[string][]proc(msg: string),
}
pubsub_init :: proc() -> PubSub {
return PubSub{subscribers = map[string][]proc(msg: string){}}
}
subscribe :: proc(ps: ^PubSub, topic: string, handler: proc(msg: string)) {
if arr, ok := ps.subscribers[topic]; ok {
new_arr := make([]proc(msg: string), len(arr)+1);
for i in 0..<len(arr) {
new_arr[i] = arr[i];
}
new_arr[len(arr)] = handler;
ps.subscribers[topic] = new_arr;
} else {
ps.subscribers[topic] = []proc(msg: string){handler};
}
}
publish :: proc(ps: ^PubSub, topic: string, msg: string) {
if handlers, ok := ps.subscribers[topic]; ok {
for handler in handlers {
handler(msg);
}
}
}
kv: KVStore;
main :: proc() {
kv = kvstore_init();
ps := pubsub_init();
handler1 :: proc(msg: string) {
fmt.println("Sub1 got:", msg);
kv_put(&kv, "last_msg", msg);
}
handler2 :: proc(msg: string) {
fmt.println("Sub2 got:", msg);
}
handler3 :: proc(msg: string) {
fmt.println("Sub3 got:", msg);
}
subscribe(&ps, "demo", handler1);
subscribe(&ps, "demo", handler2);
subscribe(&ps, "demo", handler3);
publish(&ps, "demo", "Welcome to dayvster.com");
time.sleep(2 * time.Second);
publish(&ps, "demo", "Here's another message after 2 seconds");
last := kv_get(&kv, "last_msg");
fmt.println("Last in kvstore:", last);
}
보듯이 현재는 제대로 된 에러 처리, 동시성, 지속성(persistence)이 없다. 그래도 Pub/Sub 기능을 갖춘 키-값 스토어의 기본 동작은 보여준다. 나는 KVStore와 PubSub 두 가지 주요 구조체를 만들었다. KVStore는 키-값 쌍을 저장하는 맵을 가지고 값을 넣고 꺼내는 함수를 제공한다. PubSub은 토픽별 구독자 맵을 가지고 있으며, 토픽을 구독하고 메시지를 발행하는 함수를 제공한다.
main 함수는 키-값 스토어와 Pub/Sub 시스템을 초기화하고, 메시지 핸들러 몇 개를 정의해 토픽에 구독시킨 뒤, 동작을 보여주기 위해 몇 개의 메시지를 발행한다.
이 기본 예시를 통해 오딘에서 메모리 관리를 어떻게 다루는지, 맵과 슬라이스 같은 자료구조를 어떻게 쓰는지, 그리고 프로시저를 어떻게 정의하고 사용하는지를 살펴봤다.
C와 Zig처럼 오딘은 수동 메모리 관리를 사용하지만, C의 더 원시적인 접근과 달리 Zig처럼 사용자 친화적인 유틸리티를 제공한다. 예를 들어 오딘의 make 함수는 정의된 길이와 용량을 가진 슬라이스를 만들 수 있게 해준다. 위 코드의 make([]proc(msg: string), len(arr)+1)은 길이가 len(arr)+1인 프로시저 포인터 슬라이스를 만든다. 본질적으로 힙에 메모리를 할당하고, 그 메모리에 대한 포인터와 슬라이스의 길이·용량을 포함한 슬라이스 헤더를 반환한다.
하지만 그 메모리는 어떻게, 언제 해제될까? 이 코드에서는 make로 할당된 메모리(예: subscribe의 슬라이스)와 맵(예: kv.store, ps.subscribers)에 대해 명시적으로 해제하지 않는다. 이 프로그램은 짧게 실행되고 끝나므로, 종료 시 운영체제가 메모리를 회수한다. 하지만 장시간 실행되는 애플리케이션에서는 오딘의 delete 프로시저로 슬라이스와 맵을 명시적으로 해제해야 한다. 예를 들어:
kvstore_deinit :: proc(kv: ^KVStore) {
delete(kv.store);
}
pubsub_deinit :: proc(ps: ^PubSub) {
for topic, handlers in ps.subscribers {
delete(handlers);
}
delete(ps.subscribers);
}
그러니 main이 끝나기 전에 깔끔히 정리하도록 추가해보자:
// ... existing code ...
main :: proc() {
// ... existing code ...
pubsub_deinit(&ps);
kvstore_deinit(&kv);
} // end of main
이렇게 해서, 몇 줄만으로 우리의 작은 KV 스토어와 Pub/Sub 시스템에 제대로 된 메모리 관리를 추가했다. 나는 여전히 C의 열렬한 팬이지만, 이건 깔끔하고 읽기 쉽다. 이해하기도 좋다.
우리 코드가 이제 완벽하고 완전한 메모리 안전을 보장할까? 아직은 아니다. 프로덕션용이라면 에러 처리와 스레드 안전성(훨씬 나중)이 필요하다. 그래도 책임 있는 메모리 관리로 한 발 크게 내딛었다.
오딘에서 동시성으로 Pub/Sub 향상하기
우리 Pub/Sub 시스템을 좀 더 현실적으로 만들기 위해, 오딘의 core:thread 라이브러리를 사용해 publish 프로시저에 동시성을 도입했다. 이렇게 하면 구독자들이 메시지를 동시에 처리할 수 있어, 실제 Pub/Sub 동작을 흉내 낸다. handler1은 kv_put을 통해 kv.store를 수정하므로, 공유 맵에 대한 스레드 안전한 접근을 보장하기 위해 KVStore에 뮤텍스를 추가했다. 작동 방식은 다음과 같다.
스레드로 동시 실행: publish 프로시저는 이제 각 핸들러를 thread.create로 만든 개별 스레드에서 실행한다. 각 스레드는 t.user_args로 핸들러와 메시지를 받고, thread.start로 시작한다. 스레드는 동적 배열(threads)에 모아두고, defer delete(threads)로 정리한다. thread.join은 모든 스레드가 끝날 때까지 대기하고, thread.destroy는 스레드 리소스를 해제한다. 이로써 handler1, handler2, handler3가 메시지를 동시에 처리할 수 있고, 출력 순서는 스케줄링에 따라 달라진다.
뮤텍스로 스레드 안전성 확보: handler1이 kv_put을 통해 kv.store를 갱신하므로, 동시 접근 시 경쟁 상태가 생길 수 있다. 오딘의 맵은 본질적으로 스레드 안전하지 않다. 이를 해결하기 위해 KVStore에 sync.Mutex를 추가한다. kv_put과 kv_get은 맵 접근 시 뮤텍스를 잠가, 한 번에 하나의 스레드만 kv.store를 수정하거나 읽게 한다. 뮤텍스는 kvstore_init에서 초기화하고 kvstore_deinit에서 파괴한다.
publish :: proc(ps: ^PubSub, topic: string, msg: string) {
if handlers, ok := ps.subscribers[topic]; ok {
threads := make([dynamic]^thread.Thread, 0, len(handlers))
defer delete(threads)
// Allocate ThreadArgs for each handler
thread_args := make([dynamic]^ThreadArgs, 0, len(handlers))
defer {
for args in thread_args {
free(args)
}
delete(thread_args)
}
for handler in handlers {
msg_ptr := new(string)
msg_ptr^ = msg
t := thread.create(proc(t: ^thread.Thread) {
handler := cast(proc(msg: string)) t.user_args[0]
msg_ptr := cast(^string) t.user_args[1]
handler(msg_ptr^)
free(msg_ptr)
})
t.user_args[0] = rawptr(handler)
t.user_args[1] = rawptr(msg_ptr)
thread.start(t)
append(&threads, t)
}
for t in threads {
thread.join(t)
thread.destroy(t)
}
}
}
이 구현은 각 핸들러를 자신의 스레드에서 실행해, 메시지를 병렬로 처리한다. 뮤텍스는 handler1에서 kv.store를 갱신할 때 경쟁 상태를 방지해 스레드 안전을 보장한다. 오딘의 core:thread 라이브러리는 스레드 관리가 깔끔해, pthread와 비슷한 경험을 주면서도 훨씬 덜 고통스럽다.
오딘의 스레딩은 C의 pthreads 느낌이 나지만 흔한 골칫거리 없이 읽고 쓰기 쉽다. 데모에서는 뮤텍스로 깔끔하게 유지했지만, 실제 애플리케이션이라면 더 견고한 에러 처리와 효율을 위한 스레드 풀, 스레드 수명 주기 및 에러 처리 전략 등을 고려해야 한다.
이 코드 블록에는 개인적으로 지속성을 추가하지 않았다. 데모를 단순하고 집중되게 유지하려다 보면 금방 복잡해지기 때문이다. 그래도 지속성을 넣고 싶다면, 오딘의 core:file 라이브러리로 kv.store 맵을 파일에 읽고 쓸 수 있다. 저장 시 맵을 문자열 형식(JSON이나 CSV)으로 직렬화하고, 로드 시 역직렬화하면 된다. 다행히 오딘에는 core:encoding/json과 core:encoding/csv 라이브러리가 있어 이 작업이 최소한 비교적 수월하다.
직접 시도해보고 어떻게 됐는지 알려주면 좋겠다. 단, 이 단계는 겉보기보다 훨씬 어렵다—제대로, 그리고 성능 좋게 하려면 특히 그렇다.
처음 odin build .를 실행했을 때, 뭔가 실수한 줄 알았다. 순식간에 끝났고, 출력도 경고도 아무것도 없었기 때문이다. 대신 현재 폴더 이름을 딴 바이너리가 하나 생겼다. 그래서 실행해봤다.
❯ ./kvpub
Sub1 got: Welcome to dayvster.com
Sub2 got: Welcome to dayvster.com
Sub3 got: Welcome to dayvster.com
Sub1 got: Here's another message after 2 seconds
Sub2 got: Here's another message after 2 seconds
Sub3 got: Here's another message after 2 seconds
Last in kvstore: Here's another message after 2 seconds
보시다시피, 오딘으로 만든 작은 Pub/Sub 기능의 키-값 스토어다. 컴파일 속도는 이상할 정도로 빨랐다. 내가 만든 유틸(pulse)로 프로세스 실행 시간을 벤치마크했더니 컴파일이 0.4초로 찍혔다.
❯ pulse --benchmark --cmd 'odin build .' --runs 3
┌──────────────┬──────┬─────────┬─────────┬─────────┬───────────┬────────────┐
│ Command ┆ Runs ┆ Avg (s) ┆ Min (s) ┆ Max (s) ┆ Max CPU% ┆ Max RAM MB │
╞══════════════╪══════╪═════════╪═════════╪═════════╪═══════════╪════════════╡
│ odin build . ┆ 3 ┆ 0.401 ┆ 0.401 ┆ 0.401 ┆ 0.00 ┆ 0.00 │
└──────────────┴──────┴─────────┴─────────┴─────────┴───────────┴────────────┘
혹시나 해서 --runs 16으로 다시 돌려 평균을 더 잘 보려 했는데, 최대 0.45초로 여전히 꽤 훌륭했다.
오, 꽤 인상적이다. 그런데 이게 계속 일관적이라니, 혹시 내 도구가 고장 난 걸까? 나도 완벽하진 않다. 그래서 hyperfine으로 재확인했더니 결과는 다음과 같았다.
❯ hyperfine "odin build ."
Benchmark 1: odin build .
Time (mean ± σ): 385.1 ms ± 12.5 ms [User: 847.1 ms, System: 354.6 ms]
Range (min … max): 357.3 ms … 400.1 ms 10 runs
세상에, 정말 빠르다. 프로그램이 아주 작고 단순하긴 해도, 이 정도면 인상적이다. 더 큰 코드베이스에선 어떻게 나올지 궁금해진다. 관련 피드백이나 인사이트가 있다면 꼭 알려달라. 정말 궁금하다.
마지막으로 검증 차 time odin build .도 돌려봤는데, 예상대로 0.4초가 나왔다.
전반적으로 꽤 매끄럽다. 컴파일러는 빠르고, 에러 메시지는 대체로 명확하고 도움이 된다. 다만 내 취향엔 조금… 장황하다.
예를 들어 map 키워드를 일부러 masp로 오타 내서 어떤지 보여주겠다:
❯ odin build .
/home/dave/Workspace/TMP/odinest/main.odin(44:31) Error: Expected an operand, got ]
subscribers: masp[string][]proc(msg: string),
^
/home/dave/Workspace/TMP/odinest/main.odin(44:32) Syntax Error: Expected '}', got 'proc'
subscribers: masp[string][]proc(msg: string),
^
/home/dave/Workspace/TMP/odinest/main.odin(44:40) Syntax Error: Expected ')', got ':'
subscribers: masp[string][]proc(msg: string),
^
/home/dave/Workspace/TMP/odinest/main.odin(44:41) Syntax Error: Expected ';', got identifier
subscribers: masp[string][]proc(msg: string),
^
빌드시 에러를 어떻게 처리하는지 보여주려고 이 예를 고른 것이다. 단순히 Error: Unknown type 'masp'라고만 할 수도 있는데, 실제로는 같은 원인에서 비롯된 에러 4개를 줄줄이 낸다. 파서가 혼란스러워서 더 이상 코드를 이해하지 못하기 때문이다. 즉, 최초 실수에서 파생된 모든 에러를 같은 줄에서도 다 보여준다. 개인적으로는 같은 줄, 같은 근본 원인이라면 하나의 메시지로 요약해 보여주면 좋겠다는 생각이 든다. 어디까지나 취향이다.
오딘은 현대화된—어쩌면 더 지루해진—C처럼 느껴지는데, 최고의 의미에서 그렇다. 단순하고 직설적이며 읽기 쉽다. 교묘한 문법이나 화려한 기능을 억지로 넣지 않는다. 군더더기 없이 바로 코딩하고 생산성을 낼 수 있게 하는 언어라는 느낌이다.
그리고 다음 포인트로 자연스럽게 연결된다.
표준 라이브러리와 벤더(이건 뒤에 더) 라이브러리에 포함된 양과 질이 솔직히 놀라웠다. 현대 시스템 프로그래밍 언어에 기대하는 건 거의 다 있고, C나 심지어 Zig에서라면 서드파티에 기대야 할 완성도 높은 자료구조, 알고리즘, 유틸리티도 수두룩하다.
더 알고 싶다면 Odin의 Core Library를 정말 꼼꼼히 읽어보라. 대충 훑지 말고. 예컨대 완전한 커맨드라인 인자 파서인 flags, 바로 가져다 쓸 수 있는 레드-블랙 트리 구현 rbtree 같은 것들.
하지만 정말 놀란 건 바로 이거다.
오딘은 SDL2/3, OpenGL, Vulkan, Raylib, DirectX 등 유용한 바인딩을 제공하는 벤더 라이브러리 묶음을 함께 제공한다. 이건 엄청나다. 굳이 바인딩을 세팅하거나 C 인터롭을 신경 쓰지 않고도 바로 게임이나 그래픽 앱을 만들 수 있기 때문이다. 이 벤더 바인딩들이 전부 오딘 팀이 관리/제공하는 건지는 100% 확신하진 못하겠지만, 지금까지 본 바로는 그런 듯하다. 내가 틀렸다면 알려주기 바란다.
어쨌든 바인딩의 완성도와 쓰기 편함은 확실하다. 예를 들어 오딘에서 SDL2로 간단한 창을 띄우는 방법은 이렇다:
package main
import sdl "vendor:sdl2"
main :: proc() {
sdl.Init(sdl.INIT_VIDEO)
defer sdl.Quit()
window := sdl.CreateWindow(
"Odin SDL2 Black Window",
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
800,
600,
sdl.WINDOW_SHOWN,
)
defer sdl.DestroyWindow(window)
renderer := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
defer sdl.DestroyRenderer(renderer)
event: sdl.Event
running := true
for running {
for sdl.PollEvent(&event) {
if event.type == sdl.EventType.QUIT {
running = false
}
}
sdl.SetRenderDrawColor(renderer, 0, 0, 0, 255)
sdl.RenderClear(renderer)
sdl.RenderPresent(renderer)
}
}
이 코드는 SDL2로 검은 배경의 간단한 창을 띄운다. SDL2나 SDL3에 익숙하다면 특히 더 직관적으로 보일 것이다.
오딘은 C 라이브러리와의 인터롭을 정말 손쉽게 해준다. foreign import로 라이브러리 파일을 연결하고, foreign 블록으로 개별 함수나 타입을 선언해 연결하는 식이다.
여기서 예를 길게 들기보다, 오딘 문서가 훨씬 잘 설명하므로 참고하길 바란다. 글도 더 길어지는 걸 막아준다. 자세한 내용은 Odin의 C interop 문서를 보자.
오딘의 표준 라이브러리는 꽤 포괄적이지만, 여전히 몇몇 빈틈과 빠진 기능이 있어서 특정 작업이 번거로울 수 있다. 예컨대 파일 I/O는 기본 기능이 있지만 파일 변경 감시나 비동기 I/O 같은 고급 기능은 없다. 자료구조도 꽤 갖춰져 있지만, 트라이(trie)나 블룸 필터 같은 특화된 것들은 빠져 있다. 개인적으로는 B+트리 구현이 코어에 있으면 좋겠다.
하지만 이건 트집 수준에 가깝고, 서드파티 라이브러리를 찾거나 직접 구현하는 건 대체로 간단하다.
다만…
나는 자체 패키지 매니저가 있는 언어를 좋아한다. 서드파티 라이브러리/의존성을 발견·설치·관리하기가 훨씬 수월하기 때문이다. 오딘에는 아직 내장 패키지 매니저가 없어서, 서드파티를 직접 내려받아 프로젝트에 포함시켜야 한다. 특히 의존성이 많은 큰 프로젝트에선 번거로울 수 있다.
디렉터리 관련 일관성: 현재 폴더 이름을 따서 바이너리를 자동으로 지어주는 건 마음에 든다. 그런데 odin run이나 odin build를 실행할 땐 매번 odin run ., odin build .처럼 명시해줘야 했다. 내가 있는 폴더를 알고 있다면, 현재 디렉터리에서 실행/빌드하는 걸 기본값으로 써도 좋지 않을까?
에러 메시지: 앞서 말했듯 대체로 명확하지만, 하나의 원인에서 파생된 여러 에러를 과하게 나열할 때가 있다. 이런 경우 더 간결했다면 좋겠다.
이걸 개선하려면, 같은 줄에서 나온 메시지를 배열로 묶어 하나로 접거나, 관련 메시지를 블록으로 그룹화해 보여주는 식이면 좋겠다.
나는 독일식 키보드를 쓰는데, ^를 입력하는 게 좀 번거롭다. *는 엔터 옆에 있어서 훨씬 쉽다. 오딘이 C/C++와 차별화하려는 의도는 이해하지만, 이 작은 변화는 불필요한 마찰을 만드는 느낌이다.
제목에서 말했듯 이건 정말 사소한 트집이고, 오딘 사용 경험을 해칠 정도는 아니다. 어디까지나 개인적 불편일 뿐이고, 당신은 전혀 신경 쓰이지 않을 수도 있다.
어느 정도는, 그렇다. 접근과 철학이 꽤 비슷하다. 다만 더 많은 “가드레일”과 유용한 유틸리티로 경험을 부드럽고 즐겁게 만들어 준다. 게다가 벤더 패키지를 통한 유명 라이브러리 바인딩(내가 알기론 1st-party)이 큰 강점이라, 같은 라이브러리를 C에서 쓸 때보다 일관성과 예측 가능성이 높다.
아마 그게 오딘의 힘일 것이다. 너무 “지루”해서, 당신이 생산적으로 일하도록 방해하지 않고 똑똑한 척도 하지 않는다.
여기서 “지루하다”는 말은 애정 어린 표현이다. 내 다른 글을 읽어본 사람은 알겠지만, 나는 프로그래밍에서 복잡함과 불필요한 영리함을 좋아하지 않는다. 그래서 러스트를 특정 용도에선 좋아하면서도 자주 비판적인 편이다.
이 점에서 오딘은 Go와 매우 비슷하다. 둘 다 훌륭하고 “지루한” 언어라 큰 허 fuss 없이 일을 끝낼 수 있게 해준다. 차이는 Go는 가비지 컬렉터를 채택했고, 오딘은 그러지 않았다는 것. 개인적으로 이 차이가 오딘을 훨씬 매력적으로 만든다.
오딘의 문법은 현대적으로 다듬은 C 같다. 깔끔하고 읽기 좋고, 보일러플레이트도 덜하다. 포인터 표기를 * 대신 ^로 쓰고, 함수 키워드를 func, fn, fun, function 대신 proc로 쓰는 것에 적응하는 데 시간이 좀 걸렸다. 하지만 초반 고비만 넘으면 꽤 자연스럽다.
또 타입 선언의 ::도 다소 낯설었다. C++과 Rust에서 ::는 주로 스코프 해석에 쓰여서 그렇다. 그래도 익숙해지고 나니 괜찮다.
그 외 문법은 전반적으로 직관적이고 단순했다.
게임 개발: 사람들이 오딘이 게임 개발에 좋다고 말하는 이유가 이제는 완전히 이해된다. SDL2/3, OpenGL, Vulkan, Raylib 등 벤더 라이브러리가 기본 제공되어 시작이 무척 쉽다. 성능과 저수준 제어도 게임에 딱 맞다.
시스템 프로그래밍: 수동 메모리 관리, 저수준 접근, 성능 덕분에 운영체제, 디바이스 드라이버, 임베디드 같은 시스템 프로그래밍 작업에 적합하다. 조만간 내 리눅스 환경을 위한 유틸 몇 개를 오딘으로 만들 생각이다.
데스크탑 애플리케이션: 여기서도 벤더 라이브러리가 빛난다. 그래픽 인터페이스를 수작업으로 어느 정도 그리는 것에 동의한다면, 크로스플랫폼 데스크탑 앱을 쉽게 만들 수 있다. 장차 벤더 패키지에 GTK나 Qt 바인딩도 있으면 좋겠다.
범용 프로그래밍: 서두에서 말했듯, 오딘이 게임 개발에 적합하다면 사실 거의 무엇이든 만드는 데도 적합하다고 보는 게 합리적이다. 그러니 한 번 써보고 멋진 걸 만들어보라.
당연하다. 실제로 이미 리눅스 환경을 위한 작은 유틸 몇 개를 오딘으로 계획 중이다. 언어의 단순함과 가독성이 마음에 들고, 표준/벤더 라이브러리의 포괄성도 훌륭하다. 성능 역시 인상적이고, 특히 컴파일 속도가 놀랍다.
Pub/Sub 기능이 있는 작은 키-값 스토어의 전체 소스 코드는 내 GitHub에서 볼 수 있다: dayvster/odin-kvpub
오딘으로 멋진 걸 만들었다면 꼭 보여달라. 내 SNS 어디든지 환영이다.
오딘에 대한 당신의 생각과 경험도 듣고 싶다. 이미 써본 사람도, 이제 막 시도해보려는 사람도 환영한다. 댓글을 남기거나 트위터 @dayvster로 연락 주시라.
읽어줘서 고맙고, 해피 코딩!