Writing Redis in Rust & Reading the C Source code
Quick Summary
Rust로 Redis를 직접 작성하고 C 소스의 설계 의도를 읽으려면 RESP 프레임 경계, 파이프라인 순서, 버퍼 소유권, TCP 읽기·쓰기 분리를 하나의 처리 흐름으로 이해해야 한다.
영상 보기
클릭 전까지는 가벼운 미리보기만 먼저 불러옵니다.
🖼️ 인포그래픽

🖼️ 4컷 인포그래픽

💡 한 줄 결론
Rust로 Redis를 직접 작성하고 C 소스의 설계 의도를 읽으려면 RESP 프레임 경계, 파이프라인 순서, 버퍼 소유권, TCP 읽기·쓰기 분리를 하나의 처리 흐름으로 이해해야 한다.
📌 핵심 요점
- 구현의 중심은 RESP 직렬화·역직렬화다. 수신 버퍼에서 프레임과 인자 범위를 식별하고, 명령어와 값을 복원해 올바른 Redis 응답으로 연결해야 한다.
BytesMut의split_to결과를freeze해Bytes로 바꾸면 계산된 범위별 슬라이스를 만들 수 있다. 이는 인자 데이터를 불필요하게 복사하지 않는 파싱 구조의 기반이다.- Redis 파이프라이닝은 여러 명령을 한 번에 받는 것에 그치지 않는다.
PING,SET,GET처럼 연속된 명령의 경계를 RESP 구조로 파악하고 입력 순서에 맞춰 응답해야 한다. Command열거형과Unknown변형,filter_map,while let을 조합하면 파싱된 바이트를 명령으로 모델링하고 명령과 후속 인자를 순차적으로 소비할 수 있다.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이고slice는Bytes에서 사용할 수 있으므로, 분리한 버퍼를 먼저freeze해 불변 타입으로 바꾸면 슬라이스를 만들 수 있다. [17:59]- 가변성 관련 오류와 인자 범위 오류를 함께 수정한 뒤 테스트를 실행했고, 명령과 추가 값이 들어 있는 배열 형태로 인자들이 정상 복구됐다. [19:00]
5. 엔지니어링 경력 전환과 페어 프로그래밍 면접
- 성능 테스터가 엔지니어링 직무로 전환하려면 성능 테스트를 돕는 자체 도구를 만들고, 그 결과를 실제 소프트웨어 엔지니어링 경험으로 연결할 수 있다. [19:44]
- 페어 프로그래밍 면접에서는 바로 구현하지 않고 요구사항을 충분히 수집해 모호성을 제거한 뒤, 선택한 계획과 장단점·감수한 제약을 먼저 공유해야 한다. [20:26]
6. Redis 파이프라이닝의 순서 보장
- 일반 요청은
PING의PONG을 받은 뒤SET을 보내는 식으로 명령마다 한 번의 왕복이 발생하지만, 파이프라이닝은PING,SET,GET같은 여러 명령을 한 번에 전송한다. [22:35] - 한 번에 들어온 명령들도 입력 순서대로 처리하고 응답해야 하므로, 단일 프레임만 가정한 현재 결과 구조를 복수 명령에 대응하도록 확장해야 한다. [23:06]
7. 파이프라인 결과와 명령 문자열 변환
- 파이프라인 입력에서는 하나의 명령 결과가 여러 명령 배열을 포함할 수 있고, 클라이언트도 처리 순서에 맞춘 연속 응답을 기대한다. [25:31]
- 각 명령 바이트를 UTF-8 문자열로 바꾸는 과정에서 변환 실패를 제외하기 위해
Result를Option으로 바꾸고filter_map을 적용하는 방식을 선택했다. [27:16]
8. Rust 선택과 실무·면접 역량
- Rust를 사용하는 주된 이유는 언어와 작성 경험 자체를 선호하기 때문이며, Go보다 Rust를 더 선호한다는 선택도 같은 기준에 따른다. [28:13]
- 직접 만든 동적 모듈은 스트리밍 서버 인증서 생성에 이미 사용되고 있어 핵심 기능은 동작하지만, 서버 CPU 제한은 모듈과 별개의 운영 리스크로 남았다. [29:20]
9. Redis 명령을 열거형으로 모델링
- 수신 데이터를 명령 단위로 다루기 위해
Command열거형에Echo와Ping변형을 만들고, 바이트 입력을 명령으로 변환하는 구조를 구상한다. [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. ECHO와 PING 응답 흐름 구체화
ECHO는 반복자에서 다음 값을 인자로 꺼내 반환하고,PING은PONG을 보내는 분기 구조가 필요하다. [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 바이트를 포함하는 디코더 테스트를 추가한다.
-
PING과ECHO응답을 실제 Redis 클라이언트로 호출해 바이트 형식과 입력 순서 보존을 통합 검증한다. - TCP 스트림 분리 후
FramedRead와writer의 종료, 오류 전파, 부분 쓰기 처리 방식을 점검한다.
❓ 열린 질문
- Redis C 소스의 명령 디스패치 구조를 Rust의
Command열거형으로 옮길 때 어떤 부분을 단순화하고 어떤 부분을 그대로 보존해야 하는가? - 파이프라인 입력을 하나의 프레임 안에 여러 명령 배열로 표현할지, 디코더가 명령별 프레임을 연속 반환하게 할지 어느 구조가 더 안전한가?
- 복사 없는 인자 슬라이스의 수명과 소유권을 유지하면서 명령 처리를 비동기로 분산할 수 있는 경계는 어디인가?