GROWTH LOG STORIES

함께 만든 성장을,
기록으로 연결합니다.

AI와 함께 배우고, 직접 만들고, 서로 나눈
그로스로그 멤버들의 생생한 성장 기록을 만나보세요.

성장 기록
5기+이어진 커뮤니티
HEALTHIO
PROJECT

사진 한 장으로 시작하는 건강 루틴, Health IO

대표 이야기 읽기

ALL STORIES

성장의 순간을
한곳에서 만나보세요.

프로젝트의 시작부터 모임에서 나눈 인사이트까지,
그로스로그가 함께 만든 기록을 모았습니다.
개의 성장 기록

성장일지

[GL 4기 4회차 ①] 언제 정해지는가 — C++ 4대 캐스트, Kotlin init 블록, 오픈소스 컨퍼런스 🌱

GROWTH LOG🌱 2026. 8. 11. 00:09

안녕하세요! 그로스로그 입니다!😊 🌱

내 확신, 내 짐작, 그리고 열정. 셋 다 기댈 만해 보이지만 무너집니다. 그걸 확인 가능한 것으로 바꾼 세 편입니다.

성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 매 회차 운영진이 모든 글을 읽고, 그중 특히 마음에 남은 글을 함께 골라 큐레이션합니다.

4회차에는 C++ · Kotlin · 오픈소스 컨퍼런스 참관기가 올라왔습니다. 결이 아주 다른 세 편이에요.

성장일지 큐레이션 커버
 

01

🎭 C++ 4대 캐스트

서지영 님

이런 내용이에요
C++의 static_cast · dynamic_cast · const_cast · reinterpret_cast를 정리한 글입니다. C 스타일 캐스트 대신 이 넷을 권장하는 이유부터 짚어요.
본론에 들어가기 전에 업캐스팅과 다운캐스팅을 먼저 설명합니다. 강아지와 동물 비유로요.
백미는 dynamic_cast가 어떻게 타입을 검사하는가입니다. virtualRTTIvptr/vtable로 내려가면서, 겉으로 안 보이는 장치들을 하나씩 꺼냅니다.

2회차 Web Worker에 이어 두 번째로 소개하는 저자입니다. 이번엔 언어의 안쪽으로 들어갔어요.

조금 더 들어가 보면

C++을 배울 때 캐스트가 넷이나 되는 게 처음엔 번거롭게 느껴집니다. C에서는 (int)x 하나면 끝났으니까요.

글이 이유를 짚습니다.

"이들은 C-스타일 캐스트 보다 더 안전하고 의도가 명확하기 때문에 사용을 권장하고 있다."

"의도가 명확하다"가 핵심입니다. (Derived*)ptr이라고 쓰면 코드만 봐서는 무슨 변환인지 알 수 없어요. 숫자 변환인지, 상속 관계 변환인지, const를 떼는 건지, 완전히 다른 타입으로 억지로 바꾸는 건지요.

이름을 나눠놓으면 읽는 사람이 바로 압니다. 그리고 더 중요한 건, 컴파일러도 압니다. static_castconst를 떼려고 하면 컴파일 에러가 나요. 이름이 곧 제약이 됩니다.

🐕 업캐스팅은 왜 항상 안전한가

본론 전에 방향 이야기를 먼저 하는 구성이 좋았습니다.

"업캐스팅 (Upcasting) — 비유: '강아지는 동물이다.' (Dog is an Animal)"
"다운캐스팅 (Downcasting) — 비유: '동물이 강아지일 것이다.' (An Animal might be a Dog)"

"~이다"와 "~일 것이다"의 차이가 정확합니다.

강아지를 동물이라고 부르는 건 언제나 참이에요. 글이 설명한 대로 "자식 클래스는 부모 클래스의 모든 멤버를 이미 포함하고 있기 때문"입니다. 부모가 요구하는 걸 자식은 다 갖고 있어요.

반대는 다릅니다. 동물이 강아지인지는 알 수 없습니다. 고양이일 수도 있거든요. 고양이를 강아지로 취급해서 bark()를 부르면 정의되지 않은 동작이 일어납니다.

여기서 무서운 건 "에러가 난다"가 아니라 "정의되지 않았다"라는 점이에요. 크래시가 날 수도 있고, 엉뚱한 메모리를 읽어서 이상한 값이 나올 수도 있고, 아무 일 없이 잘 도는 것처럼 보이다가 몇 달 뒤 다른 데서 터질 수도 있습니다.

static_cast는 무엇에 기대는가

글이 static_cast의 성격을 정확히 적었습니다.

"다운캐스팅(부모 -> 자식)도 가능하지만, 런타임 안전 검사를 하지 않아 위험함 (프로그래머가 타입을 100% 확신할 때만 사용)"
"특징: 런타임 비용(오버헤드)이 없음"

이 두 줄이 짝입니다. 검사를 안 하니까 공짜예요.

그러면 무엇이 안전을 담보하냐면 — 프로그래머의 확신입니다. 오직 그것뿐이에요.

예제 코드가 이 차이를 잘 보여줍니다.

// 2. 다운캐스팅 (부모 -> 자식): 프로그래머가 타입을 확신할 때
Base* b_down = new Derived();
Derived* d_down = static_cast<Derived*>(b_down);
d_down->specialized_func();      // 출력: Derived Only

// 3. 잘못된 다운캐스팅 (위험한 예)
Base* b_only = new Base();
// 컴파일은 되지만, 런타임에 심각한 오류 발생 가능성이 높음
// Derived* d_wrong = static_cast<Derived*>(b_only);
// d_wrong->specialized_func();  // 정의되지 않은 동작(Undefined Behavior)

두 코드가 똑같이 생겼습니다. static_cast<Derived*>(어떤 Base 포인터)요. 그런데 하나는 맞고 하나는 재앙이에요. 차이는 그 포인터가 실제로 뭘 가리키느냐에 있는데, 그건 코드에 안 적혀 있습니다.

🪪 dynamic_cast — 확신 대신 신분증 검사

그래서 dynamic_cast가 있습니다. 글의 비유가 좋아요. "신분증 검사기."

동작이 명확합니다.

Base* b_ptr_another = new AnotherClass();
Derived* d_ptr_fail = dynamic_cast<Derived*>(b_ptr_another);

if (d_ptr_fail == nullptr) {
    std::cout << "Pointer Cast [2] Failed! (Returns nullptr)" << std::endl;
}

실패하면 nullptr을 돌려줍니다. 정의되지 않은 동작이 아니라 확인 가능한 값이에요. if로 걸러낼 수 있습니다.

참조로 하면 다르게 동작하는 것도 잘 짚었습니다.

try {
    Derived& d_ref_fail = dynamic_cast<Derived&>(b_ref_another);
} catch (const std::bad_cast& e) {
    std::cout << "Reference Cast [2] Failed! Exception: " << e.what() << std::endl;
}

참조는 nullptr이 될 수 없으니 예외를 던집니다. 언어 설계상 자연스러운 귀결인데, 모르고 있으면 참조 캐스트에서 갑자기 예외가 터져 당황하게 돼요.

🔬 virtual 하나가 켜는 스위치

이 글에서 가장 좋았던 부분이 여기입니다. dynamic_cast가 어떻게 실제 타입을 아는가.

"간단히 말해, virtual 키워드가 스위치를 켜면, RTTI라는 정보가 생성되고, dynamic_cast가 그 정보를 사용하는 것이다."

한 문장에 세 층이 다 들어 있습니다. 풀어보면요.

1단계 — virtual을 하나라도 쓰면 컴파일러가 "이 클래스는 다형적으로 쓰이겠구나"라고 판단합니다.

2단계 — 그러면 객체마다 숨은 포인터를 하나 넣습니다.

"컴파일러는 virtual 함수가 있는 클래스의 객체를 생성할 때, 객체 내부에 vptr(virtual pointer)이라는 숨겨진 포인터 멤버를 하나 추가한다."

내가 쓰지 않은 멤버가 객체 안에 들어갑니다. 그래서 virtual이 하나 있는 클래스는 sizeof가 포인터 크기만큼 커져요.

3단계 — 그 포인터가 표를 가리킵니다.

"vptrvtable(virtual table)이라는 정적 테이블을 가리킨다. vtable은 가상 함수들의 실제 주소 목록을 가지고 있는데, 바로 이 vtable 근처에 RTTI 정보가 함께 저장된다."

그러니까 이런 구조입니다.

객체 ──vptr──▶ vtable ──▶ speak() 실제 주소
                    │
                    └──▶ RTTI ("나는 사실 Derived다")

dynamic_cast이 길을 따라가서 신분증을 확인합니다. 그래서 virtual 함수가 하나도 없으면 vptr이 없고, 따라갈 길이 없어서 컴파일 에러가 나요. 글이 "필수 조건"이라고 적은 게 이 뜻입니다.

그리고 왜 dynamic_cast런타임 비용이 있는지도 자연스럽게 설명됩니다. 포인터를 따라가고, 표를 읽고, 상속 관계를 거슬러 올라가며 비교해야 하니까요. static_cast는 그 일을 아예 안 하기 때문에 공짜인 겁니다.

큐레이터 노트

"왜 되는가"까지 내려간 글이라 뽑았습니다.

4대 캐스트 정리 글은 검색하면 많이 나옵니다. 대부분 "각각 이럴 때 씁니다"로 끝나요. 이 글은 거기서 한 층 더 내려가서 virtual → RTTI → vptr/vtable까지 갑니다.

이게 왜 중요하냐면, 이 구조를 알면 따로 외울 게 사라집니다.

· "dynamic_cast는 가상 함수가 필요하다" → vptr이 없으면 따라갈 길이 없으니까

· "dynamic_cast는 느리다" → 표를 읽고 비교하니까

· "virtual을 쓰면 객체가 커진다" → vptr이 들어가니까

규칙 세 개가 원리 하나로 줄어듭니다.

실패하는 코드를 같이 실은 것도 좋았습니다. 성공 예제만 있는 글은 "잘 되는구나"까지만 알려줘요. 이 글은 nullptr 반환, std::bad_cast 예외, 그리고 주석 처리된 UB 코드까지 나란히 둡니다. 어떻게 망가지는지를 봐야 왜 조심해야 하는지 압니다.

표로 업캐스팅과 다운캐스팅을 비교한 것도 깔끔했어요. 방향, 비유, 안정성, 방법 네 축으로요.

원문 읽으러 가기  ↗www.createdoodle.dev/post/17
 

02

🧱 Kotlin에서 init 블록은 언제 실행되고 왜 쓰는가

박지훈 님

이런 내용이에요
Kotlin의 init 블록이 정확히 언제 실행되는지를 직접 출력을 찍어가며 확인한 글입니다.
네 가지를 다뤄요. 프로퍼티 초기화식과의 순서, init을 여러 개 쓸 때, 상속 관계에서의 순서, 보조 생성자와 함께 쓸 때.
결론이 통념과 다릅니다. init모든 프로퍼티가 초기화된 뒤 한 번에 실행되는 게 아니라, 선언된 위치에 따라 중간중간 실행됩니다.

2회차 generateSequence에 이어 두 번째로 소개하는 저자입니다. 이번에도 "헷갈려서 직접 확인해봤다" 계열이에요.

조금 더 들어가 보면

출발점이 솔직합니다.

"처음엔 '이게 그냥 생성자 같은 건가?' 하고 넘겼는데, 실제로 눈으로 확인하고 동작을 분석하다 보니 생각보다 미묘한 부분이 많더라고요."

"넘겼는데"가 정직합니다. 대충 알고 쓰다가 다시 돌아온 거예요.

그리고 "실제로 눈으로 확인하고"가 이 글의 방법입니다. 문서를 읽고 정리한 게 아니라 println을 찍어서 실측했어요.

📐 순서를 실측하기

첫 번째 실험이 이렇습니다.

class Person(val name: String, val age: Int) {
    val displayName = "이름: $name".also { println("프로퍼티 displayName 초기화: $it") }

    init {
        println("첫 번째 init: name=$name, age=$age")
    }

    val greeting = run {
        println("프로퍼티 greeting 초기화")
        "안녕하세요, $name 입니다."
    }

    init {
        println("두 번째 init: greeting='$greeting'")
    }
}

프로퍼티와 init을 번갈아 배치해뒀습니다. 이 배치가 실험 설계예요.

만약 "프로퍼티가 다 초기화된 뒤에 init이 돈다"면 출력은 displayName → greeting → 첫 번째 init → 두 번째 init 순서여야 합니다.

실제 결과는 다릅니다.

1. 프로퍼티 displayName 초기화: 이름: 로그
2. 첫 번째 init: name=로그, age=29
3. 프로퍼티 greeting 초기화
4. 두 번째 init: greeting='안녕하세요, 로그 입니다.'

적힌 순서대로 그냥 위에서 아래로 흘러갑니다.

글의 정리가 정확해요.

"즉, init은 모든 프로퍼티 초기화가 끝난 뒤 한 번에 실행되는 블록이 아니라, 선언된 위치에 따라 중간중간 실행되는 구조예요."

💥 이게 왜 중요한가 — 순서를 잘못 알면 터진다

이 사실이 실무에서 왜 중요한지 짚어두면요.

두 번째 init 블록은 greeting을 읽습니다. 그리고 greeting그 위에 선언돼 있어요. 그래서 잘 동작합니다.

그런데 만약 순서를 바꿔서 init아직 선언되지 않은 프로퍼티를 읽으려 하면 어떻게 될까요.

Kotlin은 대부분의 경우 이걸 컴파일 단계에서 잡아줍니다. "Variable 'x' must be initialized"처럼요. 언어가 이 함정을 알고 막아주는 거예요.

하지만 막지 못하는 경로도 있습니다. 특히 상속이 끼면요. 그래서 글이 다음 절에서 상속을 다루는 게 자연스럽습니다.

🧬 상속 — 부모가 먼저, 자식이 나중

정리가 명쾌합니다.

1. 부모 클래스의 프로퍼티 초기화식
2. 부모 클래스의 init 블록
3. 자식 클래스의 프로퍼티 초기화식
4. 자식 클래스의 init 블록
open class Parent(val p: String) {
    val parentProp = also("parentProp 초기화")
    init { println("Parent init: p=$p") }
}

class Child(val c: String) : Parent("부모 파라미터") {
    val childProp = also("childProp 초기화")
    init { println("Child init: c=$c") }
}
parentProp 초기화
Parent init: p=부모 파라미터
childProp 초기화
Child init: c=자식 파라미터

글의 요약이 좋습니다. "자식의 프로퍼티는 부모 초기화가 끝난 뒤에야 실행됩니다."

🕳️ 그리고 여기 유명한 함정이 하나 있습니다

이 순서를 알면 자연스럽게 보이는 문제가 있어요. 글이 명시하진 않았지만, 이 순서를 이해한 사람이 다음에 만나게 될 지점입니다.

부모의 init이 자식이 재정의한 함수를 부르면 어떻게 될까요?

부모의 init1~2단계에서 돕니다. 자식의 프로퍼티는 3단계에서 만들어지고요. 그러니까 부모가 자식 함수를 부르는 시점에 자식 프로퍼티는 아직 존재하지 않습니다.

open class Parent {
    init { printInfo() }              // 여기서 자식 함수 호출
    open fun printInfo() { println("parent") }
}

class Child : Parent() {
    val name = "child"
    override fun printInfo() { println(name) }   // name은 아직 null!
}

namenull로 찍힙니다. 타입은 String인데요. Kotlin의 null 안전성이 무력화되는 몇 안 되는 지점 중 하나예요.

그래서 부모 클래스의 init이나 생성자에서 open 함수를 부르지 말라는 규칙이 있습니다. 이 글이 정리한 4단계 순서를 알면 왜 그런지가 바로 보입니다.

🧭 언제 init을 쓰고 언제 프로퍼티 초기화식을 쓰나

실용적인 표도 있습니다.

상황 추천 방식
단순 계산, 표현식 프로퍼티 초기화식 (val x = ...)
여러 프로퍼티를 한꺼번에 설정해야 할 때 init 블록
검증, 로깅, 예외 처리 등 부가 로직 필요 init 블록
주 생성자 파라미터로부터 여러 프로퍼티를 파생시킬 때 init 블록

"검증"이 특히 init의 대표 용도입니다. 자바에서 생성자 안에 if (age < 0) throw ...를 넣던 자리예요. Kotlin은 주 생성자에 본문이 없으니 그 코드가 갈 곳이 init입니다.

"여러 프로퍼티를 한꺼번에"도 요점이에요. 프로퍼티 초기화식은 한 프로퍼티에 한 값입니다. 하나의 계산으로 두세 개를 함께 정해야 한다면 init이 자연스럽습니다.

🔗 보조 생성자와의 순서

마지막 조각입니다.

class User(val id: Int, val name: String) {
    init { println("init: id=$id, name=$name") }

    constructor(id: Int) : this(id, "unknown") {
        println("보조 생성자 실행")
    }
}

User(1)을 부르면 init이 먼저, 보조 생성자 본문이 나중입니다.

이유가 명확해요. 보조 생성자는 : this(id, "unknown")으로 주 생성자를 반드시 먼저 거쳐야 합니다. 그리고 init은 주 생성자에 붙어 있고요. 그러니 순서가 정해집니다.

Kotlin이 주 생성자를 유일한 관문으로 만든 설계의 결과입니다. 어느 생성자로 들어와도 init은 반드시 거쳐요. 검증 로직을 init에 두면 빠져나갈 구멍이 없다는 뜻입니다.

큐레이터 노트

짐작을 실측으로 바꾼 글이라 뽑았습니다.

init 블록은 "객체 만들 때 실행되는 코드"라고만 알아도 대부분 잘 돌아갑니다. 그래서 대충 알고 넘어가기 쉬워요. 이 글은 거기서 멈추지 않고 println을 찍었습니다.

그리고 실험 설계가 좋습니다. 프로퍼티와 init을 번갈아 배치해서, 두 가지 가설 중 어느 쪽인지 출력만 보면 알 수 있게 만들었어요. 결과가 아니라 방법이 배울 만합니다.

헷갈렸던 지점을 그대로 밝힌 것도 좋았습니다.

"특히 프로퍼티 초기화 순서, 상속 관계에서의 실행 순서, 그리고 보조 생성자와 같이 쓸 때의 동작 방식이 헷갈렸어요."

그리고 이 세 가지가 글의 목차가 됩니다. 자기가 막힌 지점이 곧 구조가 된 거예요. 같은 데서 막힌 사람에게 정확히 맞는 글이 됩니다.

"이 글은 코틀린을 어느 정도 써봤지만 init의 정확한 타이밍이나 역할이 궁금했던 분들께도 도움이 될 거라 생각합니다"독자를 명시한 것도 눈에 띄었습니다. 입문자용도 고급자용도 아닌, 정확히 중간에 있는 사람을 겨냥했어요.

원문 읽으러 가기  ↗velog.io/@shaynepark/kotlin-init-block-usage
 

03

🌍 FOSS for All 2025 참관 리포트

김노석 님

이런 내용이에요
2025년 11월 8일 광운대학교에서 열린 오픈소스 컨퍼런스 참관기입니다. 15개 세션을 전부 정리했어요.
범위가 넓습니다. 유럽 최대 FOSS 행사 FOSDEM의 운영 방식, 야크 쉐이빙, AI 하드웨어, AT 프로토콜과 블루스카이, Rust 내비게이션 SDK, 버려진 인텔 맥 되살리기, 그리고 사이버 보안 규제라는 새로운 위협까지.
관통하는 주제는 하나예요. 지속가능성.

기술 정리 글이 아니라 하루를 통째로 기록한 문서입니다. 성장일지에서 자주 볼 수 없는 종류라 반가웠어요.

조금 더 들어가 보면

행사가 왜 열렸는지부터 시작합니다.

"FOSS 커뮤니티가 열정만으로 운영되다가 재정적·운영적 문제로 사라지는 경우가 많은데, '어떻게 하면 커뮤니티를 지속가능하게 만들까?'라는 공통의 고민에서 시작되었다."

"열정만으로 운영되다가 사라진다"가 이 리포트 전체의 주제입니다.

그리고 3대 활동 계획이 구체적이에요. ① 커뮤니티 재정 관리 지원(Fiscal Host) ② 이벤트·컨퍼런스 개최 ③ 네트워킹 지원.

재정 관리가 첫 번째라는 게 인상적입니다. 오픈소스 커뮤니티가 돈을 받으려면 계좌가 필요하고, 계좌를 만들려면 법인이 필요하고, 법인을 만들려면 세무와 회계를 감당해야 해요. 코드를 잘 쓰는 것과 전혀 다른 능력이고, 여기서 많은 커뮤니티가 무너집니다. Fiscal Host는 그 일을 대신 맡아주는 구조예요.

🎪 FOSDEM — 25년간 지킨 선

가장 흥미로운 세션 기록이 FOSDEM 편이었습니다.

"핵심 철학은 제로 커머셜(Zero Commercial). 스폰서에게 부스나 발표 시간 같은 상업적 혜택을 전혀 주지 않고, 25년간 오직 커뮤니티의 힘과 100% 자원봉사로 운영. 보안·청소 인력만 유급."

스폰서에게 부스조차 주지 않습니다. 돈은 받되 아무것도 안 준다는 거예요. 이유가 명확합니다.

"만약 스폰서에게 콘텐츠 통제권을 한 번이라도 주면, 그걸 다시는 되돌릴 수 없다"

한 번 내주면 못 되돌린다 — 규모가 커질수록 지키기 어려워지는 종류의 원칙인데, 25년을 버텼습니다.

그런데 이 리포트에서 가장 배울 만한 건 데브룸 이야기라고 봤어요.

"1,105개 세션을 운영진이 기획하지 않고, 파이썬·자바 같은 각 커뮤니티가 '우리가 미니 컨퍼런스를 열겠다'고 자율 신청하면 FOSDEM은 방·장비·홍보 플랫폼만 빌려주는 플랫폼 역할만 한다."

1,105개 세션을 운영진이 기획하지 않습니다.

이게 자원봉사만으로 그 규모가 가능한 이유예요. 만약 운영진이 모든 발표를 심사하고 배치했다면 인원이 얼마가 있어도 부족합니다. 대신 방을 통째로 빌려주고 그 안은 각 커뮤니티가 알아서 하게 했어요.

권한을 나눠주는 게 곧 확장 방법이 된 구조입니다. 그리고 자원봉사 문화도 같은 결이에요.

"복잡한 사전 교육 없이 '그냥 하라(Just do it)'고 격려하고 '허락을 기다리지 않게(not wait for permission)' 하는 것"

🐃 야크 쉐이빙 — 딴짓이 본체가 된 이야기

두 번째 키노트 기록이 재미있습니다.

야크 쉐이빙은 A를 하려다 B가 필요해지고, B를 하려다 C가 필요해지는 상황이에요. 목표에서 자꾸 멀어지는 딴짓처럼 보이죠.

발표자의 실제 경로가 이렇습니다.

A. 마스토돈에서 국한문 혼용으로 글 쓰고 싶다
   ↓ 지원 안 함
B. 1인용 경량 서버 Holo를 만들자
   ↓ 너무 복잡함
C. 서버 개발 프레임워크 Fedify를 먼저 만들자
   ↓ 쓸 만한 게 없음
D·E·F. 로깅·CLI·이메일 라이브러리까지 다 만듦

그리고 결말이 반전입니다.

"원래 목표 Holo는 잘 안 됐지만, 부산물인 Fedify가 대박. 거대 블로그 플랫폼 Ghost가 채택했고, 투자(펀딩)까지 받아 풀타임 오픈소스 개발자가 되었다."

원래 목표는 실패했고, 딴짓이 성공했습니다.

그런데 이 발표가 좋은 건 "운이 좋았다"로 끝내지 않는다는 점이에요. 생산적인 야크 쉐이빙의 조건 4개를 제시합니다.

① 진짜 나의 문제에서 시작한다 ② 각 단계를 완성시킨다 ③ 공유하고 문서화한다 ④ 그 부산물의 가치를 스스로 믿는다

②가 특히 중요해 보입니다. 딴짓이 자산이 되려면 각 단계가 그 자체로 완성돼야 해요. Fedify를 "Holo를 만들다 남은 미완성 코드"로 뒀다면 Ghost가 채택할 리 없었을 겁니다. 딴짓을 하되 대충 하지 않는 것 — 이게 그냥 산만한 것과의 차이입니다.

리포트 후반에 나오는 홈서버 미러 세션도 같은 계열이에요.

"아치 리눅스(Asus용) 저장소가 독일에 있어서 너무 느린데, 전체를 미러링하기엔 홈서버 용량이 너무 작았다."

해결책이 영리합니다. 파일이 없으면 일단 원본으로 가라고 리다이렉트해주고, 동시에 백그라운드로 받아서 캐시해둡니다. 다음부터는 빠르죠. 자기가 쓰는 것만 알아서 쌓이는 미러가 됩니다.

"'내가 불편한 것을 해결한 작은 도구'가 나에게 가장 큰 이득을 주고, 커뮤니티에도 훌륭한 기여가 될 수 있다."

🔓 종속을 벗어나는 이야기들

리포트를 관통하는 두 번째 흐름이 lock-in 탈출입니다. 여러 세션에서 반복돼요.

AT 프로토콜(블루스카이)"당신의 계정은 당신의 것이 아닙니다."

핸들(내가 소유한 도메인) / DID(영구 식별자) / PDS(내 데이터가 실제로 있는 서버)를 분리했습니다. 그래서 핸들을 바꿔도 팔로워가 유지되고, 호스팅 업체를 옮겨도 계정이 살아 있어요. 블루스카이라는 앱은 그 위에 얹힌 껍데기일 뿐이고요.

텐스토렌트 — AI 모델은 열려 있는데 그걸 돌리는 반도체는 100% 닫혀 있다는 문제 제기. RISC-V 기반으로 칩을 만들고 소프트웨어 스택을 전부 공개합니다.

Open Chat Playground"최고의 LLM은 매일 바뀐다." 그래서 코드를 안 고치고 설정 파일만 바꿔 AWS·Azure·Google·로컬 모델을 갈아끼우게 만들었어요. 팩토리 패턴으로요.

AI 시대의 문서 작성"이중 종속"이라는 표현이 날카롭습니다. 예전엔 편집기(MS 워드)에만 묶였는데, 이제 AI(코파일럿)에까지 이중으로 묶인다는 것.

T2 리눅스 — 애플이 지원을 끊은 2018~2020년 인텔 맥을 커뮤니티가 되살리는 프로젝트입니다. 리포트가 "슬라이드도 없는 즉석 발표였지만 FOSS 정신이 가장 빛났던 세션"이라고 적었어요.

⚖️ 그리고 새로운 위협

마지막 키노트 기록이 무겁습니다.

"과거의 적은 저작권(Copyright) — FOSS 커뮤니티 내부에서 GPL 같은 라이선스로 이겨냈다. 현재의 적은 시장 규제(사이버 보안법) — 정부가 외부에서 강제하는 법이라 차원이 다르다."

그리고 최악의 시나리오가 구체적이에요.

"유럽 규제를 맞춘 프로젝트 A와 한국 규제를 맞춘 프로젝트 B를 합치면, 그 결과물(A+B)이 양국 모두에서 불법이 되는 상황이 올 수 있다."

오픈소스의 강점인 '조합 가능성' 자체가 위험해진다는 이야기입니다. 그래서 결론이 "기술이 아닌 정치의 영역"이고, 정책 전문가가 필요하다고요.

개발자 컨퍼런스의 키노트가 법과 정치로 끝난다는 게 이 시대의 단면 같습니다.

💰 마지막으로, 돈 이야기

Stadia Maps 세션에서 나온 두 문장이 오래 남았습니다.

"오픈소스는 협업 모델이지, 비즈니스 모델이 아니다." 하지만 "돈은 벌어야 한다."

그리고 상사를 설득하는 논리가 실용적이에요.

"우리끼리 뜯어고쳐 쓰는 것(Fork)은 세금 내는 짓이다. 차라리 본체(FOSS 프로젝트)에 기여해서 우리에게 유리하게 방향을 트는 게 훨씬 싸다."

포크는 세금이라는 비유가 정확합니다. 한 번 갈라내면 원본이 업데이트될 때마다 영원히 병합 비용을 냅니다. 그것보다 본체에 기여하는 게 싸다는 계산이에요. 오픈소스 기여를 선의가 아니라 비용 절감으로 설명한 논리입니다.

Rust를 택한 이유도 비슷한 결이었어요. "FOSS는 자원봉사 기반인데 C++ 코드는 메모리 누수 걱정 때문에 리뷰가 지옥"이라서요. 리뷰 부담이 낮은 언어 = 유지되는 프로젝트라는 계산입니다.

큐레이터 노트

하루를 통째로 남긴 기록이라 뽑았습니다.

컨퍼런스에 다녀오면 대부분 인상 깊은 세션 한둘만 남습니다. 이 리포트는 15개 세션을 전부 정리했어요. 그것도 각 세션의 문제 제기 → 해결책 → 남은 과제 구조로요.

세션마다 핵심 문장을 뽑아둔 것이 특히 좋았습니다. "당신의 계정은 당신의 것이 아닙니다", "최고의 LLM은 매일 바뀐다", "오픈소스는 협업 모델이지 비즈니스 모델이 아니다". 나중에 다시 읽을 때 이 문장들만 훑어도 그날이 돌아옵니다.

관심 없었을 세션까지 기록한 것도 눈에 띕니다. 인프라·쿠버네티스 쪽 관심사를 가진 분인데 문서 작성 도구, 법 규제, 소셜 프로토콜까지 다 담았어요. 자기 관심사 밖의 내용을 성실히 남긴 기록은 나중에 훨씬 값이 나갑니다.

즉석 발표를 높이 평가한 것도 좋았어요. "슬라이드도 없는 즉석 발표였지만 FOSS 정신이 가장 빛났던 세션" — 형식이 아니라 내용을 봤다는 뜻입니다.

이런 참관기는 가지 못한 사람에게 그 자리를 열어줍니다. 커뮤니티에 이런 기록이 쌓이면, 다음 사람은 "가볼까 말까"를 훨씬 잘 판단할 수 있어요.

원문 읽으러 가기  ↗nskim-workstation.notion.site/FOSS-for-All-2025-2a573c1ef9…

💡 세 편을 겹쳐 읽으며

C++ 캐스트, Kotlin 초기화 순서, 오픈소스 컨퍼런스. 이렇게 먼 세 글이 또 있을까 싶습니다.

그런데 셋 다 같은 질문에 답하고 있어요. "나는 지금 무엇에 기대고 있는가."

static_cast프로그래머의 확신에 기댑니다. 확신이 맞으면 공짜로 빠르고, 틀리면 정의되지 않은 동작이에요. 에러도 안 납니다. 그래서 dynamic_cast는 확신 대신 RTTI라는 확인 가능한 정보에 기대기로 합니다. 비용을 내고요.

init 블록은 "아마 이 순서겠지"라는 짐작에 기대기 쉽습니다. 그래서 박지훈 님은 println을 찍었어요. 짐작을 출력으로 바꾼 겁니다. 상속이 끼면 짐작이 특히 잘 틀리는데, 확인해두면 부모 생성자에서 open 함수를 부르면 안 되는 이유까지 함께 옵니다.

FOSS 커뮤니티는 오랫동안 열정에 기대왔습니다. 그리고 리포트의 첫 문장이 그 결과를 말해요. "열정만으로 운영되다가 재정적·운영적 문제로 사라지는 경우가 많은데." 그래서 Fiscal Host를 만들고, 데브룸으로 권한을 나누고, 행동 강령을 세웁니다. 열정을 구조로 바꾼 것입니다.

확신 → 검사. 짐작 → 실측. 열정 → 구조.

셋 다 방향이 같아요. 기대고 있던 것이 무너질 수 있다는 걸 알아채고, 확인 가능한 것으로 바꿔놓는 일.

그리고 셋 다 공짜가 아닙니다. dynamic_cast는 런타임 비용을 내고, 실측은 시간을 들이고, 구조를 만드는 데는 사람이 필요해요. 그런데 무너졌을 때 치르는 비용이 훨씬 큽니다. 정의되지 않은 동작, 원인을 모르는 null, 사라진 커뮤니티.

이번 회차에서 가져갈 것

1. static_cast 다운캐스팅은 확신에만 기댄다.

검사를 안 하니까 빠른 겁니다. 틀리면 에러가 아니라 정의되지 않은 동작이에요. 확실하지 않으면 dynamic_cast로 검사하고 nullptr을 확인하세요.

2. virtual 하나가 vptr·vtable·RTTI를 켠다.

이 구조를 알면 규칙 세 개가 원리 하나로 줄어요. 가상 함수가 필요한 이유, 느린 이유, 객체가 커지는 이유가 전부 여기서 나옵니다.

3. dynamic_cast는 포인터면 nullptr, 참조면 예외.

참조는 nullptr이 될 수 없어서 std::bad_cast를 던집니다. 모르면 갑작스러운 예외로 만나게 돼요.

4. Kotlin init은 위에서 아래로 흐른다.

프로퍼티 초기화식과 init적힌 순서대로 실행됩니다. 모든 프로퍼티가 끝난 뒤 한 번에 도는 게 아니에요.

5. 상속은 부모 전부 → 자식 전부.

그래서 부모의 init에서 open 함수를 부르면 안 됩니다. 그 시점에 자식 프로퍼티는 아직 없어서, 타입이 String인데 null이 나올 수 있어요.

6. 검증 로직은 init에.

어느 보조 생성자로 들어와도 주 생성자를 반드시 거치므로 init은 빠져나갈 구멍이 없습니다.

7. 헷갈리면 println을 찍어보기.

문서를 읽고 짐작하는 것보다 직접 출력을 보는 게 빠르고 확실합니다. 실험을 설계할 때는 가설이 갈리도록 배치하세요. 프로퍼티와 init을 번갈아 두는 식으로요.

8. 열정은 구조로 바꿔야 남는다.

FOSS 커뮤니티가 사라지는 이유가 열정 부족이 아니라 재정·운영 구조 부재였습니다. Fiscal Host, 권한 위임(데브룸), 행동 강령 — 사람이 바뀌어도 남는 장치를 만드세요.

9. 딴짓을 하되 각 단계를 완성하라.

야크 쉐이빙이 자산이 되는 조건은 ① 진짜 내 문제에서 시작 ② 각 단계를 완성 ③ 공유·문서화 ④ 부산물의 가치를 믿기입니다. Fedify가 미완성 코드였다면 아무도 안 썼을 거예요.

10. 포크는 세금이다.

오픈소스를 뜯어고쳐 혼자 쓰면 업데이트 때마다 영원히 병합 비용을 냅니다. 본체에 기여해서 방향을 트는 게 대개 더 쌉니다.

11. 종속을 확인하는 질문 하나.

"이 서비스를 떠날 때 내 데이터와 정체성을 가져갈 수 있는가?" AT 프로토콜이 핸들·DID·PDS를 분리한 이유가 이 질문이었습니다.


좋은 글 남겨주신 서지영 님, 박지훈 님, 김노석 님께 감사드립니다! 😊

다음 큐레이션으로 다시 찾아오겠습니다! 🌱

- 에디터 · 성장일지 큐레이터 -


※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.

WRITE YOUR GROWTH

오늘의 배움을
내일의 성장으로.

기록은 나를 돌아보게 하고, 공유는 우리를 연결합니다.
당신의 다음 성장 이야기도 그로스로그에서 시작해보세요.

커뮤니티 둘러보기