YouTubeVasilios Syrakis·2026년 8월 22일·0

Writing Redis in Rust & Reading the C Source code

Quick Summary

Rust로 Redis를 직접 작성하고 C 소스의 설계 의도를 읽으려면 RESP 프레임 경계, 파이프라인 순서, 버퍼 소유권, TCP 읽기·쓰기 분리를 하나의 처리 흐름으로 이해해야 한다.

영상 보기

클릭 전까지는 가벼운 미리보기만 먼저 불러옵니다.

원본 열기

🖼️ 인포그래픽

Writing Redis in Rust & Reading the C Source code 내용을 설명하는 본문 이미지

🖼️ 4컷 인포그래픽

Writing Redis in Rust & Reading the C Source code의 핵심 내용을 4단계로 요약한 인포그래픽
Writing Redis in Rust & Reading the C Source code 핵심 내용을 4단계로 압축한 4컷 인포그래픽

💡 한 줄 결론

Rust로 Redis를 직접 작성하고 C 소스의 설계 의도를 읽으려면 RESP 프레임 경계, 파이프라인 순서, 버퍼 소유권, TCP 읽기·쓰기 분리를 하나의 처리 흐름으로 이해해야 한다.

📌 핵심 요점

  1. 구현의 중심은 RESP 직렬화·역직렬화다. 수신 버퍼에서 프레임과 인자 범위를 식별하고, 명령어와 값을 복원해 올바른 Redis 응답으로 연결해야 한다.
  2. BytesMutsplit_to 결과를 freezeBytes로 바꾸면 계산된 범위별 슬라이스를 만들 수 있다. 이는 인자 데이터를 불필요하게 복사하지 않는 파싱 구조의 기반이다.
  3. Redis 파이프라이닝은 여러 명령을 한 번에 받는 것에 그치지 않는다. PING, SET, GET처럼 연속된 명령의 경계를 RESP 구조로 파악하고 입력 순서에 맞춰 응답해야 한다.
  4. Command 열거형과 Unknown 변형, filter_map, while let을 조합하면 파싱된 바이트를 명령으로 모델링하고 명령과 후속 인자를 순차적으로 소비할 수 있다.
  5. FramedRead가 TCP 스트림을 소유한 상태에서는 같은 스트림으로 응답을 쓰기 어렵다. 스트림을 읽기 절반과 쓰기 절반으로 분리해 reader는 디코더에 넘기고 writer는 응답 전용 핸들로 유지하는 구조가 소유권 충돌을 해소한다.

🧩 배경과 문제 정의

  • Rust로 Redis 호환 서버를 처음부터 구현하며, RESP 직렬화·역직렬화와 명령 처리 구조를 실제 코드로 다듬는 단계다.
  • 수신 버퍼에서 명령과 인자를 복사 없이 분리하고, 여러 명령을 한 번에 보내는 Redis 파이프라이닝까지 처리해야 한다.
  • 라이브 스트리밍 서버의 CPU가 클라우드 제공업체에 의해 제한되면서 Twitch 송출이 중단됐고, 서버 증설이나 제공업체 이전도 필요한 상황이다.

🕒 시간순 섹션별 상세정리

1. 클라우드 CPU 제한과 스트리밍 장애

  • 클라우드 제공업체가 서비스 품질 보호를 이유로 스트리밍 서버의 CPU를 제한했고, 해당 서버가 멈추면서 Twitch 송출도 중단됐다. [01:25]
  • 영상 스트리밍 자체는 금지되지 않았지만 높은 CPU 부하가 제한 사유가 됐으며, 한두 시간 사용만으로 제한이 걸린 데다 실제 CPU 사용량 그래프도 제공되지 않았다. [05:42]

2. Rust 기반 Redis 구현의 현재 단계

  • CodeCrafters 과제를 따라 Redis를 Rust로 처음부터 만들고 있으며, 현재는 들어온 명령을 해석해 적절한 응답을 돌려주는 처리 경로를 구현하고 있다. [06:32]
  • 핵심 작업은 RESP 직렬화·역직렬화이며, 파싱한 인자 범위로부터 명령어와 값을 다시 구성해야 한다. [07:53]

3. 프레임 분리와 인자 범위 처리

  • 전체 프레임 길이를 기준으로 소스 버퍼를 분리한 뒤, 계산해 둔 범위마다 바이트 슬라이스를 얻어 인자 목록에 넣는 흐름이 필요하다. [11:28]
  • BytesMut에서 기대한 슬라이스 메서드를 바로 찾을 수 없었고, 가변 버퍼와 불변 바이트 타입의 API 차이가 컴파일 오류의 원인이 됐다. [14:39]

4. BytesMut 동결과 명령 인자 복구

  • split_to가 반환하는 타입은 BytesMut이고 sliceBytes에서 사용할 수 있으므로, 분리한 버퍼를 먼저 freeze해 불변 타입으로 바꾸면 슬라이스를 만들 수 있다. [17:59]
  • 가변성 관련 오류와 인자 범위 오류를 함께 수정한 뒤 테스트를 실행했고, 명령과 추가 값이 들어 있는 배열 형태로 인자들이 정상 복구됐다. [19:00]

5. 엔지니어링 경력 전환과 페어 프로그래밍 면접

  • 성능 테스터가 엔지니어링 직무로 전환하려면 성능 테스트를 돕는 자체 도구를 만들고, 그 결과를 실제 소프트웨어 엔지니어링 경험으로 연결할 수 있다. [19:44]
  • 페어 프로그래밍 면접에서는 바로 구현하지 않고 요구사항을 충분히 수집해 모호성을 제거한 뒤, 선택한 계획과 장단점·감수한 제약을 먼저 공유해야 한다. [20:26]

6. Redis 파이프라이닝의 순서 보장

  • 일반 요청은 PINGPONG을 받은 뒤 SET을 보내는 식으로 명령마다 한 번의 왕복이 발생하지만, 파이프라이닝은 PING, SET, GET 같은 여러 명령을 한 번에 전송한다. [22:35]
  • 한 번에 들어온 명령들도 입력 순서대로 처리하고 응답해야 하므로, 단일 프레임만 가정한 현재 결과 구조를 복수 명령에 대응하도록 확장해야 한다. [23:06]

7. 파이프라인 결과와 명령 문자열 변환

  • 파이프라인 입력에서는 하나의 명령 결과가 여러 명령 배열을 포함할 수 있고, 클라이언트도 처리 순서에 맞춘 연속 응답을 기대한다. [25:31]
  • 각 명령 바이트를 UTF-8 문자열로 바꾸는 과정에서 변환 실패를 제외하기 위해 ResultOption으로 바꾸고 filter_map을 적용하는 방식을 선택했다. [27:16]

8. Rust 선택과 실무·면접 역량

  • Rust를 사용하는 주된 이유는 언어와 작성 경험 자체를 선호하기 때문이며, Go보다 Rust를 더 선호한다는 선택도 같은 기준에 따른다. [28:13]
  • 직접 만든 동적 모듈은 스트리밍 서버 인증서 생성에 이미 사용되고 있어 핵심 기능은 동작하지만, 서버 CPU 제한은 모듈과 별개의 운영 리스크로 남았다. [29:20]

9. Redis 명령을 열거형으로 모델링

  • 수신 데이터를 명령 단위로 다루기 위해 Command 열거형에 EchoPing 변형을 만들고, 바이트 입력을 명령으로 변환하는 구조를 구상한다. [30:53]
  • 패턴 매칭이 모든 경우를 처리하지 못해 컴파일 오류가 발생하자 알 수 없는 입력을 받을 Unknown 변형을 추가한다. [32:22]

10. RESP 파이프라인의 명령 경계 파악

  • ECHO 명령과 인자는 캐리지 리턴과 줄바꿈으로 분리되며, RESP 배열 길이와 문자열 길이 표기까지 함께 처리해야 한다. [33:50]
  • 파이프라인으로 들어온 여러 ECHO 요청은 줄 단위로 이어지므로 단순 문자열 분리가 아니라 RESP 프레임 구조에 맞춘 순차 파싱이 필요하다. [34:09]

11. 명령 반복 처리를 반복자로 전환

  • 파싱 결과를 Command로 변환한 뒤 명령별로 매칭하는 흐름을 먼저 구성하지만, 직접 작성한 for 루프가 후속 인자 소비에 불편한 구조가 된다. [34:44]
  • filter_map으로 유효한 명령만 남기고 반복자의 next()를 직접 호출하는 방식으로 바꾸며, 값이 없을 수 있어 Option 처리가 필요해진다. [35:32]

12. while let으로 명령과 인자를 순차 소비

  • while let Some(command) = commands.next() 구조를 사용하면 반복자를 유지한 채 현재 명령과 다음 인자를 차례로 꺼낼 수 있다. [37:27]
  • 스트림 쓰기와 다음 값의 unwrap()을 결합하는 과정에서 반환 타입이 단위 타입과 맞지 않아, 쓰기 결과를 별도로 처리해야 한다. [37:51]

13. 문자열과 바이트 타입 불일치 해결

  • 응답 처리 코드가 기대하는 타입과 실제 문자열 타입이 달라 컴파일 오류가 발생하며, 명령 인자를 네트워크 출력 형식으로 변환해야 한다. [39:00]
  • 바이트 입력에 String::from_utf8 변환을 적용하는 방향으로 수정하지만, 변환 위치와 값의 소유권 때문에 코드를 하위 처리 단계로 옮긴다. [39:23]

14. TCP 스트림의 대여와 가변성 충돌

  • 스트림이 이미 대여된 상태에서 다시 사용되면서 value borrowed 오류가 발생하고, 읽기 프레임이 스트림을 얼마나 오래 참조하는지가 핵심 제약이 된다. [40:47]
  • 반복자를 가변으로 소비하는 문제를 정리한 뒤에도 TCP 스트림에는 가변 대여가 필요 없다는 진단이 남아, 실제 읽기·쓰기 API의 소유권 요구와 직관이 어긋난다. [42:02]

15. 시청률보다 학습 목적을 우선하는 방송

  • 시청자 수와 화제성을 유지하는 일보다 자신이 필요한 구현을 계속하는 데 우선순위를 두며, 관심 있는 사람이 함께하면 충분하다는 기준을 세운다. [42:42]
  • 오락성을 극대화한다면 리액션이나 자극적인 콘텐츠를 택해야 하지만, 현재 방송은 그런 최적화보다 실제 코딩 작업 자체가 목적이다. [43:49]

16. FramedRead의 스트림 소유권 확인

  • 스트림을 프레임 읽기 객체에 이동시킨 뒤 다시 대여할 수 있는지 검토하며, 변수의 가변성보다 FramedRead가 스트림 소유권을 가져간다는 점이 문제로 좁혀진다. [44:58]
  • 읽기 객체가 스트림을 소유하면 원래 변수로 쓰기 작업을 수행하기 어려워져, 읽기와 쓰기 접근을 분리할 구조가 필요하다. [45:19]

17. 자동 자막과 AI 의존 의사결정의 한계

  • 라이브 영상에도 자동 자막이 존재하며 별도 추출도 가능하지만, 자동 생성 결과는 정확하지 않을 수 있다. [45:48]
  • 조직 리더가 AI 채팅 결과를 근거로 무리한 요구를 내놓으면 기술 리드가 현실성과 품질을 조율해야 하지만, 외교적으로 대응하기 어려운 갈등이 생긴다. [46:19]

18. 응답 쓰기를 위한 가변 스트림 설계

  • 파싱한 명령에 응답하려면 스트림에 데이터를 써야 하고, 쓰기 API가 &mut self를 요구하므로 가변 스트림 참조를 확보하는 방향으로 돌아간다. [48:45]
  • 명령 목록을 반환하는 우회 방식보다 스트림의 가변 참조를 직접 전달하는 편이 적합하며, Clippy 진단에 따라 불필요한 문자열 변환과 오류 처리도 정리한다. [49:07]

19. 응답 바이트 구성과 개발 도구 활용

  • 다음 명령 값을 출력하거나 스트림에 기록하려 하지만 응답은 문자열이 아닌 바이트 형식이어야 해, 명령 반복자와 출력 버퍼 사이의 타입 구성이 복잡해진다. [50:10]
  • 편집기에 표시되는 변수·반환 타입은 Rust Analyzer가 제공하며, 중첩 while 루프는 현재 파싱 구조에서 특별히 비정상적인 패턴이 아니다. [51:10]

20. 대규모 시스템 경험을 직접 만드는 방법

  • 대형 인프라를 접할 기회가 없다면 직접 규모 있는 예제 시스템을 구축해야 하며, 실제 부하를 만들려면 비용까지 감수해야 할 수 있다. [51:40]
  • 시스템 설계의 확장성을 이론과 추론만으로 논증할 수는 있지만, 현실의 부하·장애·비용 조건은 이론과 달라 실제 실행 경험이 중요하다. [52:19]

21. ECHOPING 응답 흐름 구체화

  • ECHO는 반복자에서 다음 값을 인자로 꺼내 반환하고, PINGPONG을 보내는 분기 구조가 필요하다. [53:01]
  • PONG 바이트를 작성하려는 순간 스트림이 프레임 읽기로 이동된 뒤 다시 대여되는 오류가 발생해, 단순한 가변 참조 추가만으로는 해결되지 않는다. [53:34]

22. Pop!_OS·Arch·CachyOS의 사용성 차이

  • Arch와 비슷한 최신 패키지·롤링 릴리스 환경이 필요하다면 직접 모든 것을 관리하는 Arch보다 설정 부담을 줄인 CachyOS가 현실적인 대안이다. [56:00]
  • Arch의 최소 구성과 높은 통제권보다 설치 직후 바로 작동하는 환경을 선호하므로 Pop!_OS나 CachyOS가 일상 작업에 더 적합하다. [56:39]

23. 읽기·쓰기 스트림 분리로 구조 전환

  • 서버 측 실행 루프의 구현을 다시 살펴보면서 프레임 반복자를 수동으로 중첩 순회할 필요가 없고, 기존 접근이 문제를 과도하게 복잡하게 만들었음을 파악한다. [57:07]
  • TCP 스트림을 읽기 절반과 쓰기 절반으로 나누면 읽기 쪽은 FramedRead에 넘기고 쓰기 쪽만 가변으로 유지할 수 있어 소유권 충돌이 해소된다. [59:24]

24. ECHO 응답 작성과 응답 디코더 확인

  • 인수가 두 개이고 명령이 ECHO이면 메시지 길이를 담은 헤더, 메시지 본문, 캐리지 리턴을 차례로 모두 기록한 뒤 출력 버퍼를 비우며, 조건에 맞지 않으면 오류를 반환한다. [1:00:25]
  • AsyncWriteExt를 이용한 수동 응답 작성은 Tokio의 관용적인 방식이며, 클라이언트 응답을 처리하려면 simple·bulk·error·nil 유형을 구분하는 별도의 디코더도 필요하다. [1:01:40]

25. 장시간 구현 세션 종료와 다음 작업 예고

  • 현재 시각이 12시 7분이고 작업 시간이 약 4시간에 가까워지면서 이번 세션을 여기서 종료하고, 다음 날에는 한 시간에서 한 시간 반 정도 이어갈 계획을 세운다. [1:02:39]
  • 채팅 참여, 구현 기여, 질문을 남긴 시청자들에게 감사를 전하며 다음 세션에서 작업을 계속하기로 한다. [1:03:01]

🧾 결론

  • Redis 호환 서버 구현의 난점은 개별 명령 분기보다 바이트 프레임, 인자 수명, 복수 명령의 경계를 일관되게 관리하는 데 있다.
  • Rust의 타입·소유권 오류는 단순한 장애물이 아니라 버퍼 변환 위치와 네트워크 스트림 책임을 다시 설계하게 만드는 피드백으로 작동한다.
  • 파이프라인 지원을 위해서는 단일 프레임을 가정한 결과 구조를 복수 명령과 연속 응답을 표현하는 구조로 확장해야 한다.
  • 직접 구현한 동작을 Redis의 C 소스와 대조하면 프로토콜 표면뿐 아니라 원본 구현이 명령 경계와 응답 순서를 관리하는 방식까지 탐구할 수 있다.

📈 투자·시사 포인트

  • 클라우드 제공업체의 불투명한 CPU 제한은 스트리밍과 개발 인프라의 운영 연속성을 훼손할 수 있어, 인스턴스 증설과 멀티 제공업체 이전 가능성을 함께 검토해야 한다.
  • 프로토콜·비동기 I/O·소유권을 직접 다루는 프로젝트는 성능 테스트 경험을 소프트웨어 엔지니어링 역량으로 전환하는 실질적인 포트폴리오가 될 수 있다.
  • 시스템 확장성은 이론만으로 완전히 검증하기 어렵고 실제 부하·장애·비용을 경험해야 하므로, 대규모 시스템 역량에는 실험 인프라 비용이 수반될 수 있다.
  • AI 채팅 결과나 자동 자막은 보조 자료로 활용할 수 있지만 정확성과 현실성을 기술 리드가 별도로 검증해야 하며, 이를 생략하면 품질과 일정 판단이 왜곡될 수 있다.

⚠️ 불확실하거나 확인이 필요한 부분

  • 제목에는 Redis의 C 소스 읽기가 포함돼 있지만, 제공된 상세 내용에는 구체적으로 어떤 C 파일·함수·자료구조를 분석했는지 나타나지 않는다.
  • 구현된 RESP 디코더가 불완전한 프레임, 잘못된 길이, 비 UTF-8 입력, nil·error 응답을 어디까지 처리하는지는 확인되지 않았다.
  • 여러 파이프라인 명령의 응답 순서가 실제 Redis 클라이언트와의 통합 테스트에서도 보장되는지는 시험용 출력만으로 판단하기 어렵다.
  • 자막 기반 정리: 타임스탬프가 있는 자막을 기준으로 정리했으며, 고유명사·수치·인용은 원문 확인 필요 시 별도 검증한다.
  • 영상 속 주장: 발표자의 해석·전망·비교는 확인된 외부 사실이 아니라 영상 속 주장으로 분리해 읽는다.
  • 검증 필요: 수치, 기업 실적, 정책·시장 전망은 발행 전 최신 자료로 별도 검증이 필요하다.

✅ 액션 아이템

  • Redis C 소스에서 RESP 입력 처리, 명령 디스패치, 파이프라인 응답 순서와 관련된 함수·자료구조를 찾아 현재 Rust 설계와 비교한다.
  • 단일 명령, 복수 파이프라인 명령, 분할 수신 프레임, 잘못된 길이, 비 UTF-8 바이트를 포함하는 디코더 테스트를 추가한다.
  • PINGECHO 응답을 실제 Redis 클라이언트로 호출해 바이트 형식과 입력 순서 보존을 통합 검증한다.
  • TCP 스트림 분리 후 FramedReadwriter의 종료, 오류 전파, 부분 쓰기 처리 방식을 점검한다.

❓ 열린 질문

  • Redis C 소스의 명령 디스패치 구조를 Rust의 Command 열거형으로 옮길 때 어떤 부분을 단순화하고 어떤 부분을 그대로 보존해야 하는가?
  • 파이프라인 입력을 하나의 프레임 안에 여러 명령 배열로 표현할지, 디코더가 명령별 프레임을 연속 반환하게 할지 어느 구조가 더 안전한가?
  • 복사 없는 인자 슬라이스의 수명과 소유권을 유지하면서 명령 처리를 비동기로 분산할 수 있는 경계는 어디인가?

관련 문서

공통 태그와 주제 흐름을 기준으로 같이 보면 좋은 문서를 이어서 제안합니다.