Redis 7.0의 정식 출시가 순조롭게 진행되고 있으며, 저희는 최근 두 번째 릴리스 후보(Release Candidate)를 공개했습니다. 이번 RC는 버전의 기능을 완성하기 위한 계획된 이정표이자, 새 버전에 담긴 추가적인 내용을 소개할 좋은 기회이기도 합니다. 예를 들어 Redis Functions는 버전 2.6부터 지원해 온 스크립팅 기능에서 한 단계 더 발전한 것입니다. 마찬가지로 Redis의 다른 많은 기능들도 함께 진화했습니다.
자연계에서 진화는 무작위로 일어나고 자연선택이 그 결과를 걸러냅니다. 하지만 일반적으로 소프트웨어, 특히 Redis에서는 이 과정이 반대로 진행됩니다. 즉, 원하는 방향을 먼저 선택하고 프로젝트를 그에 맞게 발전시키는 것입니다. 무작위성이 미래를 결정하게 두기보다는, 로드맵의 계획과 실행은 주로 사용자 피드백과 Redis가 적합한 새로운 사용 사례에 따라 결정됩니다.
ACLv2: 더 강력해진 접근 제어
Redis 7.0에서는 접근 제어 목록(Access Control List, ACL)도 한 단계 더 발전했습니다. Redis 6.0에서 처음 도입된 ACL은 보안을 프로젝트 범위 밖으로 여겨온 오랜 관점을 뒤집고, 사용자와 권한을 관리하는 메커니즘을 추가했습니다. 하지만 커뮤니티는 올바른 방향의 첫걸음임에는 분명하지만 아직 필요한 기능이 부족하다는 점을 빠르게 지적해 주었습니다.
ACL의 공백 중 하나인 Pub/Sub 채널 이름 패턴 제어는 이미 Redis 6.2에서 해결되었습니다. 그러나 이는 부분적인 임시방편에 불과했고, 나머지 문제들을 해결할 간단하고 효과적인 접근 방식을 찾는 데는 더 오랜 시간이 걸렸습니다.
원래 ACL 설계는 기본적인 권한 제어 사용 사례만을 염두에 두었습니다. 사용자당 하나의 명령 세트, 키 패턴, 채널 이름 패턴에 대해서만 접근을 허용하거나 거부할 수 있었습니다. 예를 들어 특정 사용자에게 SET 명령은 한 키 집합으로 제한하면서 동시에 GET 명령은 다른 키 집합을 허용하는 식의 설정은 불가능했습니다. 결과적으로 ACL은 보안 정책을 구현하기에 충분히 효과적인 메커니즘이 아니었습니다.
Redis 7.0의 접근 제어 목록, 줄여서 ACLv2는 기존 버전과 호환되면서도 두 가지 중요한 개선점을 추가했습니다. 첫째, ACLv2의 핵심은 '셀렉터(selector)'입니다. 둘째, ACLv2는 특정 키에 대한 접근 유형 권한을 설정할 수 있습니다. 이 기능 덕분에 사용자를 특정 키 집합에 대해 읽기 전용, 쓰기 전용 또는 읽기-쓰기 작업으로만 제한할 수 있습니다.
기존 ACL 설계는 사용자당 하나의 셀렉터, 즉 기본 셀렉터만 제공했습니다. 이 셀렉터는 사용자가 접근할 수 있는 키와 채널, 카테고리 및 명령을 기술합니다. ACLv2는 기본 셀렉터 위에 순서대로 적용되는 임의 개수의 셀렉터를 추가할 수 있습니다. 이러한 접근 방식은 더 엄격한 보안 정책의 요구 사항을 충족하며 ACL을 완성에 한층 가깝게 만듭니다.
명령 자기 성찰(Introspection) 능력의 발전
서버의 자기 성찰(introspection) 능력은 Redis 7.0에서 크게 발전한 또 다른 측면입니다. Redis는 API인 명령 사전(command dictionary)을 통해 외부와 상호작용합니다. 프로젝트가 발전함에 따라 다양한 명령(및 하위 명령)의 수가 늘어나 7.0 버전에서는 380개를 넘어섰습니다. 모든 Redis 명령은 특정 작업에 특화되어 있으므로, 각 명령의 호출 인수와 동작을 문서화하는 것이 프로젝트의 핵심 원칙입니다. 이 문서화는 서버와 클라이언트 간의 유일한 계약입니다.
역사적으로 명령 문서는 프로젝트 외부의 별도 코드 저장소에서 관리되었습니다. 문서는 사람이 읽기 위한 것이었으므로 (대체로) 사람이 읽기 좋은 형식으로 유지되었습니다. 이는 기계, 아니 정확히는 기계를 프로그래밍하는 사람들에게 어려운 과제였습니다. 산문(prose)을 코드로 변환하는 일은 지저분하고 깨지기 쉽기 때문입니다. 특히 Redis 클라이언트 개발자들은 문서 변경 사항을 모니터링하고 릴리스 노트를 읽어야만 프로젝트를 최신 상태로 유지할 수 있었습니다.
그래서 버전 2.8 무렵, 서버가 자신의 명령을 프로그래밍 방식으로 보고할 수 있는 방법이 필요하다는 것을 깨달았습니다. 이름 그대로(그리고 발음하기도 어려운) COMMAND 명령은 런타임에 서버가 지원하는 명령 목록을 나열합니다. 또한 COMMAND GETKEYS 하위 명령을 사용하면 클라이언트가 명령과 인수를 그대로 전송하여 서버가 키 이름을 추출하도록 할 수 있습니다. 클러스터 배포 환경에서 올바른 노드로 작업을 전달하려면 명령에서 키 이름을 추출하는 것이 필수적입니다.
ACLv2 관련 작업에 힘입은 바도 있지만, 클라이언트에게 런타임 명령 목록을 더 유용하게 만들기 위해 버전 7.0은 서버의 명령 관리 내부 메커니즘 상당 부분을 전면 개편했습니다. 또한 서버가 각 명령에 대해 유지하는 메타데이터를 풍부하게 만들어, 서버의 기능에 대한 사전 지식이 거의 없어도 정교한 클라이언트를 구축할 수 있게 했습니다. 마지막으로 개편된 명령 테이블은 Redis 모듈이 자신들의 명령으로 확장할 수 있도록 설계되어, 코어 명령과 동일한 수준의 자기 성찰 정보를 제공합니다.
새로운 명령 키 사양(key specification)을 통해 클라이언트는 클러스터 서버의 도움 없이도 명령문에서 로컬로 키를 추출할 수 있어 지연 시간이 개선되고 네트워크 대역폭이 절약됩니다. 명령 인수에 대한 메타데이터는 클라이언트가 서버 버전 간 명령 구문 변화를 감지하고 적응할 수 있게 해줍니다. 나아가 클라이언트는 명령 팁(command tips)을 통해 특수한 상황이나 다양한 배포 유형에서 명령을 실행하는 방법에 대한 더 많은 정보를 얻을 수 있습니다.
일급 시민이 된 하위 명령
이번 작업에는 하위 명령(subcommand)을 서버 명령 테이블의 일급 시민(first-class citizen)으로 격상시키는 것도 포함되었습니다. 원래 하위 명령은 끊임없이 커지는 API의 카디널리티(cardinality)에 대응하기 위해 도입되었습니다. 모든 작업마다 새 명령을 추가하는 대신, 관련된 작업들은 하나의 '부모' 명령 호출로 처리한다는 발상이었습니다. 부모 명령은 첫 번째 인수로 하위 명령 이름을 받아 어떤 동작을 수행할지 결정합니다. 기술적으로 하위 명령은 부모 명령의 모든 특성(예: ACL 카테고리, 읽기/쓰기 플래그, 키 사양 등)을 물려받았기 때문에 서로 다른 동작을 세밀하게 구분하는 것이 불가능했습니다.
예를 들어 CLIENT 명령은 연결 관리 작업을 위한 만능 바구니로, 무려 15개의 서로 다른 하위 명령을 보유하고 있습니다. CLIENT SETNAME처럼 일반 클라이언트 연결이 평소에 호출하는 하위 명령이 있는 반면, CLIENT KILL처럼 오용 가능성이 있어 관리자 용도로만 제한해야 하는 하위 명령도 있습니다. 이전 버전의 Redis에는 이런 구분을 지원하는 내부 메커니즘이 없어 개발자들이 문서에 의존할 수밖에 없었고 이는 혼란을 야기했습니다. 그러나 Redis 7.0에서는 모든 하위 명령이 부모나 형제 명령과 무관하게 자신만의 특성 세트를 가지므로 정확한 기술이 가능합니다.
이 글이 마지막 부분에서는 다소 기술적인 세부 사항까지 깊이 들어간 장문의 글이 되어버린 것 같습니다(너무 많은 기술적 세부 사항이라는 게 존재하기나 하는지 모르겠지만요). 그럼에도 지루하지 않았기를 바라며, 새 버전과 프로젝트 속에서의 위치에 대한 이해에 조금이나마 도움이 되었기를 희망합니다. 저희는 정식 출시(GA)를 향한 최종 릴리스 후보 작업을 진행 중이며, 새 버전 둘러보기를 이어갈 다음 연재 글도 기대해 주세요.