

JavaScript를 매일 쓰면서도, 이 언어가 실제로 어떻게 실행되는지는 한 번도 직접 들여다본 적이 없었어요.
React, Next.js, 상태 관리, API 호출 — 프론트엔드 개발자로 일하면서 JavaScript는 늘 손에 익은 도구였어요. 그런데 "익숙하다"는 말과 "이해한다"는 말 사이에는 꽤 넓은 간극이 있어요.
어느 순간부터 그 간극이 자꾸 눈에 들어왔어요. 런타임은 정확히 어떤 구조로 코드를 실행하는지, 메모리는 어떤 전략으로 할당되고 회수되는지, GC는 왜 그렇게 어렵다고 하는지. 답보다 질문이 먼저 쌍였어요.
이 글은 그 질문들을 직접 파고들기로 결심한 시작점에 대한 기록이에요.
이유는 크게 두 가지예요.
런타임과 메모리 모델을 직접 보고 싶어요. 지금까지는 애플리케이션 레벨에서 JavaScript를 "잘 쓰는 것"에 집중해 왔어요. 하지만 이제는 그 아래에 어떤 구조와 비용이 숨어 있는지를 직접 들여다보고 싶어졌어요. console.log가 출력되기까지 어떤 경로를 거치는지, 클로저가 메모리에서 어떻게 살아남는지 — 그런 수준의 이해를 원해요.
커리어 방향으로서의 low-level 역량. 프론트엔드를 계속하더라도, 언어 실행 모델이나 시스템에 가까운 문제를 이해하는 개발자와 그렇지 않은 개발자 사이에는 분명한 차이가 있다고 생각해요. 단순히 API를 소비하는 사람이 아니라, 동작 원리를 구조적으로 설명할 수 있는 엔지니어가 되고 싶어요.
완전히 낯선 언어를 밑바닥부터 파는 것도 의미가 있겠지만, 지금의 저에게는 가장 익숙한 언어를 가장 낯선 시선으로 다시 보는 것이 학습 효율이 더 높다고 판단했어요.
JavaScript는 진입 장벽이 낮은 언어처럼 보이지만, 런타임 관점에서 들여다보면 이야기가 완전히 달라져요.
이미 결과물을 잘 알고 있는 언어를 구조적으로 다시 해체하고 재조립하는 경험 — 그것이 이번 공부의 핵심이에요.
C, C++, Zig 등 여러 시스템 언어를 검토했지만, 최종적으로 Rust를 택한 데에는 몇 가지 이유가 있어요.
소유권 모델이 곱 메모리 교육이에요. Rust의 ownership, borrowing, lifetime 시스템은 처음엔 장벽처럼 느껴지지만, 이 규칙을 체화하는 과정 자체가 메모리 관리에 대한 깊은 이해로 이어져요. GC 없이 메모리를 안전하게 다루는 법을 컴파일러가 끈임없이 가르쳐 줘요.
실수를 컴파일 타임에 잡아줘요. 런타임 엔진처럼 복잡한 프로젝트에서 dangling pointer나 use-after-free 같은 버그를 런타임에서 디버깅하는 건 학습 효율을 크게 떨어뜨려요. Rust는 이런 류의 실수를 컴파일 단계에서 차단해주므로, 메모리 버그 대신 엔진 로직 자체에 집중할 수 있어요.
생태계와 레퍼런스가 풍부해요. Rust로 작성된 JavaScript 엔진 프로젝트(Boa, Nova 등)와 파서(oxc, biome 등)가 활발히 개발되고 있어서, 막힐 때 참고할 수 있는 양질의 코드베이스가 존재해요. 혼자 삽질하는 시간을 줄이고, 올바른 방향으로 학습할 수 있는 환경이 갖춰져 있어요.
커리어 관점에서도 유의미해요. Rust는 프론트엔드 툴체인(SWC, Turbopack, Biome, oxc)에서 빠르게 채택되고 있어요. 이 프로젝트를 통해 Rust에 익숙해지는 건 단순한 학습을 넘어, 프론트엔드 인프라 레이어에 대한 실질적 역량으로 이어질 수 있어요.
이번 프로젝트에서 하나 더 실험하고 싶은 게 있어요. AI를 학습 파트너로 적극적으로 활용하는 것이에요.
이전까지는 새로운 영역을 공부할 때 문서를 읽고, 레퍼런스 코드를 분석하고, 스택오버플로를 뒤지는 게 전부였어요. 물론 그 과정도 의미가 있지만, 이번에는 조금 다른 접근을 해보려고 해요.
스펙을 읽다가 막히면 AI와 대화하며 이해를 확인해요. ECMA-262 스펙은 정말 난해한 문서예요. 특정 알고리즘이 왜 그렇게 정의되어 있는지, 엣지 케이스는 어떤 것이 있는지를 AI에게 물어보면, 혼자 읽을 때보다 이해의 밀도가 확실히 높아져요.
구현 방향을 잡을 때도 AI와 함께 설계를 검토해요. 예를 들어 GC 전략을 고를 때, mark-and-sweep와 아레나 할당의 트레이드오프를 AI와 논의하면서 정리하면, 혼자 고민할 때보다 선택지가 더 빨리 명확해져요.
코드 리뷰에도 활용해요. 작성한 코드에 대해 "Rust 관용적으로 더 나은 방법이 있는지", "이 구조가 나중에 확장하기 어려운 부분은 없는지"를 AI에게 물어보면, 혼자서는 놓치기 쉬운 관점을 얻을 수 있어요.
물론 AI가 내놓는 답을 그대로 복사하는 게 아니에요. AI의 응답을 검증하고, 스펙과 대조하고, 직접 구현해보는 과정이 핵심이에요. AI는 "오답을 주는 도구"가 아니라, "생각을 더 빨리 정리해주는 대화 상대"에 가까워요.
이 프로젝트의 1차 목표는 명확해요. 작은 toy engine과 test262 runner를 만드는 것.
구체적으로 달성하고 싶은 건:
test262 일부 테스트 통과 — 스펙 준수 여부를 객관적으로 검증여기서 중요한 기준은 완성도가 아니에요. "왜 이런 구조가 필요한지"를 설명할 수 있는 상태에 도달하는 것 — 그것이 이 프로젝트의 진짜 목표예요.
욕심을 줄이는 것도 프로젝트 설계의 일부예요. 적어도 초기에는 아래를 의도적으로 배제해요.
이건 프로덕션 런타임을 만드는 프로젝트가 아니에요. 언어 실행 모델을 공부하는 과정이에요. 범위를 넓게 잡는 순간 핵심을 놓치게 돼요.
솔직히, 쉬워 보이는 부분이 거의 없어요. 파서는 이전에 만들어본 경험이 있어 완전히 낯설지는 않지만, 이번에 특히 까다로울 것으로 예상하는 영역은 다섯 가지예요.
Gc<T> 래퍼, 아레나 할당, mark-and-sweep 등 전략 선택이 엔진 전체 아키텍처를 좌우해요.unsafe 경계 설정 등 언어 자체의 복잡도가 엔진 구현 난이도에 곱해져요.test262를 기준으로 삼는 이상, 감으로 구현하는 건 통하지 않아요.어쩌면 코드를 짜는 시간보다 ECMA-262 스펙을 읽는 시간이 더 길어질 수도 있어요. 그럴 때 AI와 함께 스펙을 해석하고, 구현 방향을 검증하는 과정이 큰 도움이 될 거라 기대해요.
한 번에 몰아서 끝내는 프로젝트가 아니에요. 2~3개월, 주 5~10시간 페이스로 천천히 쌍아갈 계획이에요.
접근 순서는 다음과 같아요:
속도보다 방향이 중요해요. 엔진 전체를 처음부터 설계하기보다, runner를 이해하는 것에서 자연스럽게 engine으로 넘어가는 흐름을 택했어요.
Rust를 선택한 덕분에, 참고할 수 있는 오픈소스 프로젝트가 풍부해요.
이 프로젝트들의 코드를 읽는 것 자체가 훌륭한 학습 자료가 돼요.
이런 종류의 공부는 머릿속으로만 하면 금방 흐려져요. 기록하지 않으면 남지 않아요.
이 블로그 시리즈가 세 가지 역할을 해주길 바라요.
어떤 질문을 던졌고, 어떤 기준으로 범위를 정했고, 어디에서 막혀는지. 그런 기록이 깔끔한 결과 정리보다 더 오래, 더 깊이 남는다고 믿어요.
이 프로젝트의 출발점은 "대단한 엔진을 만들겠다"는 선언이 아니에요. 오히려 정반대예요.
매일 쓰는 JavaScript를 한 층 더 깊은 곳에서 이해해보고 싶다는 것. 런타임, 메모리, 스펙, 테스트, 구현 — 이것들 사이의 관계를 직접 손으로 만지며 배우고 싶다는 것.
Rust의 엄격한 타입 시스템과 소유권 모델이 그 여정의 가이드레일이 되어줄 거라 기대해요. 컴파일러가 틀린 부분을 짚어줄 테니, 저는 구조를 이해하는 데 온전히 집중할 수 있을 거예요. 그리고 막힐 때마다 AI와 함께 돌파구를 찾을 거예요.
느리게 갈 거고, 자주 막힐 거예요. 그래도 이번에는 속도보다 축적을 택하려고 해요.
작은 runner부터. 아주 작은 engine부터. 설명할 수 있는 범위부터.