퍼포스에 대한 개인적 불만

퍼포스를 개인적으로 잘 사용하고 있지만 불만이 없는 것은 아닙니다.

퍼포스에 대한 개인적 불만

이전 디지털 - 휴먼 API (2024)에서 디지털 쓰레기란 없다는 관점으로 개인 수준에서 파일을 관리하는데 퍼포스를 사용하고 있다고 소개했습니다. 오래 전부터 파일에 버전을 관리할 필요를 강하게 느껴 왔고 몇 가지 방법을 시도해 봤습니다. 가령 오래 전에는 ‘Visual SVN Server’를 사용했는데 이 시대의 SVN은 한 커밋을 한 파일에 묶어 저장하고 있었습니다. 사실 이 동작은 그저 특징일 뿐 장단점이 될 만한 것은 아니었지만 저는 코드를 만드는 사람이 아니어서 여러 소프트웨어로부터 생성한 바이너리 파일을 커밋하기를 반복할 때마다 커다란 파일이 하나씩 만들어지곤 해서 종종 이런 거대한 파일을이 안전하게 저장되고 있을지 의심한 적이 있긴 합니다. 그런데 시간이 지난 다음 어떤 파일의 오래된 리비전에 접근하려고 할 때 오래된 리비전을 묶은 거대한 파일 상당수가 깨져 접근이 불가능하다는 사실을 알게 되었을 때 당시의 SVN은 겉으로는 제 요구사항을 만족할 것처럼 동작했지만 막상 필요한 순간이 되자 오래된 파일을 아무 의미도 없는 바이너리 뭉치로 만들어버린 시가 소프트웨어로 등극합니다. 아마도 현대에는 더 이상 그런 문제가 일어나지 않을 것 같지만 이 때 한번 크게 당한 다음부터 SVN은 꽤 괜찮은 기능에도 불구하고 개인 파일의 버전 관리를 위한 도구로는 영원히 고려하지 않게 되었습니다.

한동안 기반의 Forgejo라는 도구를 사용했습니다. 하지만 이 도구 역시 근본적으로 깃에 기반한 관계로 용량이 큰 바이너리 파일에 대한 버전 관리가 필요한 제 요구사항에 잘 맞지 않았습니다. 깃은 분산 버전 관리 시스템인 관계로 컴퓨터 한 대에서 아무 서버 환경 없이 버전 관리에 사용할 수 있었습니다. 하지만 이미 깃을 고려하던 시점은 저 한 사람이 컴퓨터 여러 대를 사용하는 것이 너무 당연한 시점이 되고 나서도 한참이나 시간이 흐른 다음이었습니다. 당시에도 이미 저 한 사람이 데스크탑 한 대와 랩탑 한 대를 사용하고 있었고 이들 사이에 같은 파일 관리 시스템으로부터 파일을 관리하기를 원했고 현대에는 온프레미스 기계가 추가되어 항상 최소 세 대의 기계가 동기화된 상태를 유지해야 했습니다. 그래서 깃을 사용하더라도 서버에 의존해야 했고 Forgejo는 나쁘지 않은 선택처럼 보였습니다. 하지만 이 서비스는 설치 후 초반에 커밋 몇 개를 만든 다음 이를 서버로 푸시하려는 순간 제 요구사항을 만족하지 못할 거라는 사실을 직감합니다. 이전 SVN 역시 항상 커다란 바이너리 파일을 잘 다룰 수 있는지 걱정하곤 했는데 애초에 깃은 로컬에서 동작할 때에도 크기가 큰 파일에 대해 몹시 허약한 모습을 보여 왔습니다. 그런데 이런 특징이 서버 기반으로 변하자 로컬에서는 커밋에 성공했다 하더라도 이를 인터넷을 통해 서버로 푸시하는 일은 완전히 다른 차원의 문제였습니다. 바이너리 파일 뭉치는 종종 수 백 메가 단위를 초과하기도 했는데 로컬에서 동작하는 깃은 어떻게든 이를 처리할 수 있었지만 이걸 서버에 푸시하기는 거의 불가능했습니다.

결국 오래 전부터 회사에서 핵심 형상관리도구로 사용하며 거대한 바이너리 파일을 몹시 잘 다룬다는 사실을 이미 잘 알고 있었기 때문에 돌고 돌아 퍼포스를 개인 파일 관리 도구로 선택합니다. 퍼포스는 분명 회사에서 사용하기에는 높은 비용을 지불해야 하지만 개인 수준에서는 무료로 사용할 수 있어 바이너리 파일의 버전 관리를 원하는 개인이라면 추천합니다. 한동안 VPS에 서버를 구축해 놓고 사용하다가 스토리지 비용을 감당할 수 없음을 깨닫고 온프레미스로 전환했고 구동 환경을 도커로 전환한 다음 최근에는 제 요구사항에 맞춰 도커 이미지를 만들어 사용하고 있습니다. 개인 수준에서 드랍박스나 원드라이브 수준의 서비스를 사용하면 충분히 파일을 관리할 수 있고 또 버전 관리도 할 수 있는 것이 사실입니다. 이들은 사용하기 편리할 뿐 아니라 다양한 환경을 지원하고 비용도 그리 높은 편이 아니며 당연하게도 커다란 바이너리 파일을 잘 다루기까지 합니다. 하지만 이들은 파일이 변하면 일단 동기화 하고 보기 때문에 동기화 시점을 명시적으로 정의할 수 없어 미래에 이전 리비전을 필요로 할 때 분명 이전 리비전이 존재함은 확실하지만 어느 명시적이지 않은 시점으로 돌아가야 할 지 알기 아주 어렵습니다. 드랍박스는 웹 인터페이스를 통해 여러 과거 시점 중 편집이 많이 일어난 시점을 주요 명시적인 지점으로 제안하곤 하지만 실제로는 편집이 많이 일어난 시점과 중요한 변경이 일어난 시점이 일치하지 않을 때가 많습니다. 또 이들은 오래된 버전을 임의로 제거하는데 기본 요금제 기반으로는 30일 까지 버전을 유지해 주고 추가 비용을 내면 180일이나 1년까지 버전을 유지해 줍니다. 이 정도 수준은 대체로 충분하지만 그보다 더 과거의 파일이 단 한 번이라도 필요하다면 이 요구사항을 만족할 수 없습니다.

에이 비해 퍼포스를 사용해 직접 파일을 관리하기 시작하면 지금까지 언급한 단점이 간단히 사라집니다. 먼저 버전을 기록해야 할 시점을 제가 명시적으로 지정할 수 있습니다. 자동으로 동기화 되지 않기 때문에 귀찮다고 여길 가능성이 없지 않지만 개인적으로는 작업을 마치면 이 파일을 퍼포스를 통해 서버에 보내 새 버전을 만드는 행동이 너무나 자연스럽고 또 이 행동이 마음을 편안하게 만듭니다. 다른 도구에 비해 바이너리 파일을 잘 다룬다는 점은 너무 당연하니까 넘어가고 제가 직접 하드웨어를 통제하기 때문에 오래된 버전의 리텐션 정책 같은 것에 신경 쓸 필요가 완전히 없어집니다. 기본적으로 모든 버전의 리텐션 제한이 없으며 필요에 따라 제 결정에 의해 과거 리비전을 제거할 방법이 있긴 하지만 귀찮게 그런 행동을 하는 것 보다 그냥 하드디스크를 증설하는 쪽이 더 단순한 방법입니다. 또한 파일 각각의 버전을 관리할 뿐 아니라 분류 방법 변경에 따른 디렉토리 변경, 서로 다른 경로 사이에 파일을 이동하거나 복사할 때 퍼포스를 통하면 이 모든 기록을 모두 명시적인 커밋으로 남길 수 있습니다. 만약 퍼포스에 의존하지 않고 파일 분류 방법을 바꿔 경로를 여러 차례 수정했지만 어느 시점에 그 경로가 마음에 안 들거나 어떤 파일을 찾을 수 없는 상황에 처할 때 직접 파일시스템을 바꿨다면 이 모든 상황을 직접 감수하고 해결하는 수밖에 없지만 퍼포스에 의존했다면 모든 경로 변경에 대한 기록이 남아 있어 극단적으로는 그냥 이 모든 변경이 일어나기 전 상태로 돌아갈 수도 있습니다. 또 같은 파일이 서로 다른 여러 경로에 복사된 다음 수정되어야 할 때 파일을 그냥 파일시스템 상에서 복사하는 대신 원본으로부터 브랜칭 하면 한 파일이 과거에 어떤 다른 파일로부터 비롯되었는지 추적할 수도 있습니다.

결정적으로 모든 기계로부터 파일 관리를 퍼포스에 의존하면서 컴퓨터 딱 한 대만 백업하면 되는 상황이 되었습니다. 이전에는 각각의 컴퓨터를 따로 백업해야 했는데 백업 환경은 점점 더 나빠지고 있다에서 불평했던 것처럼 현대에 가까워질수록 단단하고 또 안정적이며 군말 없이 동작하는 백업 소프트웨어를 찾기 점점 더 어려워지고 있습니다. 이런 상황에서 기계 여러 대를 백업하는 일은 이전 시대에 비해 훨씬 더 피곤한 일로 바뀝니다. 하지만 모든 파일을 퍼포스로 관리하기 시작하면서 오직 퍼포스 서버 한 대만 백업하면 되는 상황으로 바뀌었고 여기에는 크래시플랜이라는 오프사이트 백업 서비스를 사용하고 있습니다. 백업 전략은 각각의 기계에서 변경한 파일은 모두 퍼포스를 통해 서버에 기록하고 이 서버 한 대만 크래시플랜을 통해 백업하는 것입니다. 만약 서버 이외의 기계에 문제가 생기면 그냥 퍼포스 클라이언트를 통해 서버로부터 파일을 가져오면 됩니다. 또 만약 서버 기계에 문제가 생기면 기계 문제를 해결한 다음 오프사이트 백업으로부터 서버 전체의 파일을 가져오면 됩니다. 퍼포스의 스토리지 포멧은 로컬 경로 상의 파일을 서버에서도 같은 경로 상에 각 버전 별로 파일 니음만 바꿔 그냥 저장해 버리는 방식이어서 좀 무식해 보일 수 있지만 문제가 생길 가능성이 훨씬 낮아 보입니다. SVN을 사용할 때 한 리비전에 올린 모든 파일을 한 덩어리로 묶은 파일이 깨지자 리비전 전체를 유실하는 것 외에는 방법이 없었지만 만약 퍼포스 서버 스토리지에 문제가 생기고 오프사이트 백업이 없는 상황이라 하더라도 그냥 서버 파일시스템에 접근해 파일을 꺼내 오기만 하면 됩니다. 무슨 복잡한 복원 과정 같은 건 필요 없습니다.

마지막으로 퍼포스를 개인적으로 사용하면서 얻은 장점은 업무에도 퍼포스를 사용하기 때문에 개인적으로 이 시스템에 대해 더 잘 이해하고 여러 가지 상황에 처할 때 제가 무슨 행동을 해야 하는지 더 잘 이해하며 각각의 행동을 할 때 정확히 무슨 일이 일어날 예정인지 잘 알고 있게 됐다는 점입니다. 사실 이건 제가 개인 위키에 컨플루언스를, 할일 관리 도구에 지라를 사용하면서 얻은 장점과 비슷합니다. 실수해도 아무 상관 없는 개인 환경에서 각각의 도구를 여러 가지 방법으로 사용해보고 더 나은 사용 방법을 탐색하면서 각각의 도구에 대한 이해가 깊어지고 상황에 따라 도구의 어떤 기능을 사용할 수 있는지 더 잘 알게 되었고 이는 일할 때 큰 도움이 됩니다. 퍼포스도 마찬가지인데 사실 회사에서 일하며 다루는 정도로는 파일을 고치고 커밋하는 것 이외의 방법으로 사용할 일이 많지 않습니다. 어쩌다 누군가의 변경사항을 머지할 일이 생기는 것이 고작입니다. 하지만 개인적으로 사용할 때는 이전 버전으로 되돌리고 다른 경로에 같은 파일을 복사하기 위해 브랜치를 사용하고 브랜치 사이에 파일을 머지하고 레이블을 달고 레이블에 기반해 필터링 한 결과를 머지하는 등 온갖 방법으로 도구를 사용해볼 수 있었습니다. 특히 중요한 점은 이 모든 시도는 실패를 걱정할 필요가 전혀 없었다는 점입니다. 어차피 문제가 생기면 저 혼자 감당하면 됐기 때문에 문제를 일으키고 이를 해결하는 과정을 여러 차례 경험하면서 도구의 설계 철학을 어렴풋이 이해할 수 있었고 또 실제로 파일이 어떻게 저장되는지 알고 있게 됐으며 스토리지 포멧과 구분되는 메타데이터가 데이터베이스에 저장되는 모양, 데이터베이스에 대한 올바른 백업 전략 등을 알고 있게 됩니다. 이를 업무에 적용할 일이 일어나지 않겠지만 일에 사용하는 도구를 더 잘 이해할 수 있게 되어 여러 상황에 걸쳐 자신 있게 행동할 수 있게 된 점은 큰 장점입니다.

글 제목을 ‘퍼포스에 대한 개인적 불만’이라고 해 놓고 지금까지 퍼포스를 개인이 사용할 때 얻을 수 있는 장점으로 도배해 뭔가 이거 개인용 퍼포스 사용기 및 영업을 또 하고 있는 사기가 아닌가 의심하실 수 있을 것 같습니다. 그래서 지금부터는 개인적으로 퍼포스를 사용하며 느낀 퍼포스의 단점을 늘어 놓아 보겠습니다. 사실 제가 생각하는 단점은 이 소프트웨어를 개발한 사람들이 이런 식으로 개인이 모든 파일을 퍼포스에 의존할 거라고 전혀 가정하지 않았기 때문에 생기는 올바르지 않은 도구를 올바르지 않은 용도로 사용하기 때문에 발생하는 것일 수도 있습니다. 하지만 제 관점에서 단점은 단점이기에 이런 한계에도 불구하고 제 경험을 단점으로 정의해 보겠습니다. 먼저 당연하다면 당연하지만 서버가 없으면 정말 아무것도 할 수 없습니다. 퍼포스는 깃과 달리 중앙 집중식으로 동작하는 형상관리도구이기에 모든 동작에 서버가 필요합니다. 파일을 사용하겠다고 선언하고 파일을 변경한 다음 이를 새 버전으로 선언하는 일련의 행동 하나하나에 서버가 필요합니다. 분산형 형상관리도구인 깃이라면 로컬 환경에서 파일을 사용한다고 선언하고 파일을 수정하고 이를 새 버전으로 선언할 수 있습니다. 물론 이 상태를 안전하게 서버에 보내는 것은 조금 다른 이야기이기는 하지만 이 행동들이 완전히 불가능해지지는 않습니다. 하지만 퍼포스는 네트워크가 없는 상황에서는 파일을 변경할 수는 있겠지만 그 외에 아무 것도 할 수 없습니다. 심지어 오프라인 상태에서 변경한 파일을 나중에 서버에 보내기 위해서는 오프라인에서 변경한 파일을 별도 경로에 백업한 다음 서버에 연결된 상태에서 파일을 수정하겠다고 선언하고 백업했던 파일을 덮어 쓴 다음 이를 커밋하는 귀찮고 필요 이상으로 복잡한 과정을 퍼포스의 제어 없이 감당해야 합니다. 깃은 오프아니 작업이라도 깃의 관리 하에서 모든 행동을 할 수 있는 것과 대조적입니다.

퍼포스는 한 디포 - 깃의 리파지토리 개념 - 상에 일어나는 모든 커밋은 오직 한 경로로만 관리합니다. 가령 깃에서는 한 가지 목적을 달성하기 위핸 여러 커밋은 브랜치를 만든 다음 브랜치 상에서 여러 커밋을 하며 파일을 변경한 다음 이를 다시 메인 브랜치에 머지하는 방식으로 커밋을 여러 경로에 걸쳐 관리할 수 있습니다. 이런 방식이 필요한 이유는 한 가지 목적을 달성하기 위해 한 번 이상의 커밋이 필요한 경우가 있고 여러 커밋을 한 가지 목적을 달성하기 위한 시도로써 구분할 필요가 있기 때문입니다. 제가 코드를 만드는 사람이었다면 한 가지 목적을 위해 여러 파일에 걸친 코드를 수정하는 사례를 들겠지만 저는 코드를 만드는 사람이 아니므로 디렉토리 구조를 고쳐 파일을 분류하는 방식을 변경한다고 해 보겠습니다. 먼저 디렉토리 하나를 만든 다음 서로 다른 경로에 있던 다른 디렉토리를 방금 만든 디렉토리 하위로 이동 시키고 싶습니다. 이 때 저는 최소 두 번의 커밋을 해야 합니다. 새 디렉토리를 만드는 것 하나, 그 안에 다른 디렉토리를 옮기는 것 하나입니다. 실제로는 여러 경로의 파일을 이동 시켜야 하니 새로운 분류 정책에 따라 경로를 수정한 행동은 커밋 여러 개를 만들게 됩니다. 그런데 이 모든 커밋은 같은 디포 상에서 단일 경로로만 관리되므로 체인지리스트 - 퍼포스에서 커밋 목록을 나열한 것 - 상에서 특정 목적을 달성하기 위한 커밋을 구분해내기 어렵습니다. 물론 저는 이 작업의 유일한 작업자이므로 커밋 메시지를 읽어본 다음 특정 목적을 달성하기 위한 커밋에 해당하는 체인지리스트를 모두 알아낼 수 있겠지만 여러 사람이 작업하는 환경에서 이는 전혀 간단하지 않습니다. 만약 깃으로 같은 작업을 한다면 그냥 브랜치를 나눠 나간 다음 그 안에서 필요한 만큼 커밋하고 나서 메인 브랜치에 머지해 돌아오면 그만이고 이 때 퍼포스의 체인지리스트 여러 개에 해당하는 변경사항은 브랜치 하나 단위로 확실히 구분할 수 있습니다.

퍼포스의 클래식 디포 - 리파지토리 - 에서 브랜치는 디렉토리 매핑 방식으로 동작해 다른 형상관리도구가 같은 경로에 대한 브랜치를 만드는 방식과 완전히 다르게 동작합니다. 이 방식에 적응할 수 있지만 여러 도구가 경로를 유지한 채 브랜치를 바꾸는 동작을 가정하고 만들어진 경우가 있어 브랜치 사용이 그리 편안하지 않습니다. 여느 형상관리도구의 브랜치 개념은 어떤 경로의 브랜치를 만들면 이 경로에 대한 논리적인 복사본을 만든 것과 같이 동작합니다. 같은 경로에 파일을 수정한 다음 이를 커밋하면 현재 브랜치에 수정이 일어난 것으로 처리하며 이 상태에서 브랜치를 전환하면 경로를 유지한 채 파일만 변경됩니다. 파일이 변경되었지만 앞서 다른 브랜치에서 수정한 파일은 여전히 저장되어 있으며 같은 경로 상에 있는 다른 브랜치에서 같은 파일을 편집하고 커밋하고 브랜치를 전환하기를 반복하면 됩니다. 그런데 퍼포스의 브랜치 모델은 사실상 다른 경로에 파일을 복사하는 것과 똑같습니다. 한 경로에 있는 파일을 브랜칭 하려면 다른 도구에서는 경로를 유지하므로 브랜치 이름만 입력하면 되지만 퍼포스에서는 아예 다른 경로를 입력해야 합니다. 다른 경로를 입력하고 나며 퍼포스는 다른 경로에 현재 경로의 파일을 복사한 다음 메타데이터 상 브랜치가 만들어졌다고 기록합니다. 이는 브랜치 전환 동작 없이 두 브랜치에 동시에 작업할 수 있다는 말이기도 하지만 다른 한 편으로는 두 경로의 파일에 동시에 접근할 수 있으므로 실수 여지가 많습니다. 또 동시에 둘 이상의 경로 상에 있는 브랜치로부터 파일을 머지하려고 할 때 방금 잘못된 파일을 수정할 수 있는 가능성과 마찬가지로 잘못된 방향으로 머지할 수 있습니다. 퍼포스도 바보는 아니어서 자신들의 브랜치 모델이 이런 문제를 가지고 있음을 알고 있고 이를 수정하기 위해 기존 ‘클래식 디포’ 대신 새로운 ‘스트림’을 사용할 것을 권장하고 있지만 개인 수준에서 관리 부담을 늘리는 조치여서 따를 결정을 하지 못하고 있습니다.

퍼포스가 바이너리 파일을 군말 없이 잘 다루는 것과 달리 제 관점에서 퍼포스가 텍스트 파일을 다루는 방식은 좀 ‘이상’합니다. 퍼포스는 파일을 크게 텍스트와 바이너리로 구분합니다. 바이너리는 각 리비전마다 압축 없이 저장하거나 압축해서 저장하는 것 중 하나를 선택할 수 있습니다. 현대적인 백업 소프트웨어가 바이너리 파일이라도 델타만 저장하는 방식으로 스토리지와 대역폭을 절약하는 것에 비하면 꽤 고전적인 접근이지만 퍼포스의 목표가 그런 자원 절약이 아니라는 점을 생각하면 이는 단점이 아닙니다. 텍스트는 좀 더 복잡한데 바이너리와 마찬가지로 모든 리비전을 저장하도록 선택할 수 있고 이번에는 텍스트 파일이기에 델타만 저장할 수도 있습니다. 텍스트는 상대적으로 크기가 더 작을 것을 가정해 델타만 저장했다가 과거 리비전을 요청하면 그동안 저장했던 각 리비전 별 델타에 근거해 과거 리비전을 만들어내는 방식으로 동작하는데 크기가 큰 텍스트 파일을 처음으로 커밋해보면 정확한 크기 경계는 잘 모르겠지만 마치 바이너리 파일처럼 매 리비전마다 파일 전체를 저장해 버리는데 이는 델타에 기반해 이전 리비전을 만들어내는데 시간이 많이 걸리기 때문인 것 같습니다. 텍스트는 크게 ‘text’, ‘unicode’ 타입으로 저장할 수 있는데 개인적으로 이 구분이 아주 이상합니다. 텍스트는 애초에 그 스스로 특정 인코딩에 의해 작성되지만 파일 스스로가 인코딩 정보를 가지고 있지는 않습니다. 때문에 미리 약속한 인코딩으로 읽어내지 않으면 호환되지 않는 문자가 깨지거나 완전히 잘못된 상태로 읽어낼 수 있습니다. 이 때 그 상태를 저장해 버리면 문자가 바뀐 상태로 저장되어 원래 상태로 돌아가지 못할 수 있습니다.

그런데 퍼포스의 ‘text’, ‘unicode’ 구분은 이런 문제가 일어나도록 부추기는 것 같습니다. 어느 쪽으로 저장하든 파일에 변경을 가하지는 않습니다. 그런데 ‘text’로 저장하면 파일 인코딩이 바뀔 때 이를 무시해 버립니다. 가령 누군가 UTF-8 인코딩으로 작성한 텍스트 파일을 퍼포스에 처음 커밋할 때 기본값인 ‘text’ 타입으로 설정했다고 해 보겠습니다. 다른 사람이 같은 파일을 체크아웃 해 수정한 다음 파일을 저장할 때 에디터가 ‘euc-kr’로 저장해 버릴 수 있습니다. 하지만 이 작업자는 자기 화면에서 아무 문제도 없었기에 문제가 일어난 상황 자체를 인지하지 못했을 수 있습니다. 그리고 퍼포스를 통해 수정한 파일을 커밋하면 서버 상에 파일의 인코딩이 바뀝니다. 이제 원래 파일을 처음 커밋 했던 사람이 새 리비전을 받아 자신이 편집했던 UTF-8 인코딩으로 파일을 열어 보면 파일이 깨진 것처럼 보일 겁니다. 만약 인코딩이 변경된 텍스트 파일에 기반해 동작하는 소프트웨어가 있었다면 분명 오동작을 일으킬 겁니다. 이 문제를 해결하려면 ‘unicode’ 타입으로 바꿔 주면 되는데 이 옵션의 동작은 더 이상합니다. 이번에도 아무 인코딩으로나 파일을 작성한 다음 이를 커밋하더라도 파일에 변경을 가하지 않습니다. 다만 이번에는 이를 체크아웃 한 누군가가 파일을 수정한 다음 커밋 할 때 이전과 인코딩이 달라진 것을 감지하면 커밋을 허용하지 않습니다. 이제 앞서 설명한 문제가 일어나지 않게 됩니다. 그런데 이 동작을 하게 하는 타입 이름이 왜 각각 ‘text’와 ‘unicode’인지 설명하기는 쉽지 않습니다. 어쩌면 오랜 세월에 걸쳐 그때그때 일어난는 문제를 해결하는 방식으로 개발되어 오다 보니 결정된 이상한 타입 이름일 수 있습니다.

퍼포스는 커밋한 파일을 크게 ‘메타데이터’와 ‘파일 스토리지’로 구분해서 저장합니다. 파일을 커밋하면 파일의 경로와 같은 구조와 파일 이름에 기반해 디렉토리를 만든 다음 그 안에 파일 이름을 리비전 번호로 바꿔 저장합니다. 그래서 앞서 뭔가 큰 사고가 일어나더라도 그냥 경로에 찾아 들어가 파일을 꺼내 오면 그만이라고 한 것입니다. 그런데 파일 별 리비전, 커밋 로그, 브랜치, 파일 별 체크섬 같은 데이터는 퍼포스가 직접 개발한 것 같은 별도 데이터베이스에 기록하고 이 데이터베이스는 파일시스템에 그냥 저장되는데 암만 생각해도 이 데이터베이스의 성능이 그리 좋지 않습니다. 퍼포스 서버를 저 한 사람이 사용하고 있으니 이론적으로 퍼포스를 사용할 때 데이터베이스 때문에 병목이 생겨서는 안된다고 생각합니다. 그런데 실제로는 많은 파일을 한번에 커밋하거나 커다란 파일 트리를 브랜칭 하거나 이동 시키려고 하면 작업을 시작하기도 전에 병목이 발생합니다. 여느 형상관리도구와 마찬가지로 퍼포스는 어떤 작업을 실제로 수행하기 전에 일종의 ‘예정’ 상태롤 만들어 놓는데 이를 ‘펜딩 체인지리스트’라고 합니다. 그런데 펜딩 체인지리스트에 많은 파일을 포함한 체인지리스트가 있으면 데이터베이스를 사용한다고 말하기 무색하게 퍼포스 클라이언트 전체의 동작에 영향을 받습니다. 또 다른 경로에 여러 파일을 커밋하고 있을 때 이 경로에 의존성이 없는 다른 경로로부터 파일을 체크아웃 하거나 새 파일을 받아오려고 할 때도 의존성이 없는 다른 경로에 일어나는 커밋에 영향을 받아 느리게 동작합니다. 이런 상황을 관리 도구를 사용해 살펴보면 각 작업이 데이터베이스 파일을 잠근 채 수행되는데 읽기 잠금과 쓰기 잠금이 현대적인 데이터베이스처럼 원활한 방식으로 수행되지 않는 것처럼 보입니다.

파일시스템과 데이터베이스의 인티그리티 체크를 스스로 하지 않는 점 역시 의아합니다. 퍼포스는 깃이 등장하기 10년 전부터 개발되어 널리 사용되어 온 형상관리도구로 이 도구의 근본적인 목표는 여러 사람이 파일을 수정하는 환경에서 그 모든 파일을 안전하게 보관하는 거라고 생각합니다. 이 목표는 현대에 조금 더 복잡해졌을 뿐 크게 변하지 않았습니다. 퍼포스 관리 매뉴얼을 살펴보면 다양한 방식으로 파일시스템의 인티그리티를 확인하고 데이터베이스 인티그리티를 확인하며 이들 사이의 관계를 종합적으로 평가하고 백업하고 또 복원하는 각각의 방식과 올바른 전략 수립에 관한 내용이 자세하게 설명되어 있습니다. 그런데 현대적인 관점에서 이런 동작이 자동화 되어 있지 않고 또 이런 동작에 대한 전략을 직접 수립하고 이를 직접 실행해야 하는 점은 잘 이해 되지 않습니다. 이 도구가 아주 오래 전에 만들어지기 시작했음을 이해하지만 현대에 가까워질수록 관리 동작을 스스로 수행하는 것을 기본으로 하고 이 동작이 일어나는 방식이나 빈도에 개입할 수 있는 방식이어야 한다고 생각합니다. 하지만 퍼포스는 관리자가 직접 수행하지 않는 이상 데이터베이스, 파일시스템 중 그 무엇도 스스로 정상 동작 여부를 확인하지 않으며 그 무엇도 스스로 백업하지 않습니다. 파일시스템이야 그냥 파일을 직접 기록하고 있을 뿐아니 이를 별도로 백업하지 않는 것을 쉽게 이해할 수 있습니다. 하지만 데이터베이스는 그렇지 않다고 생각합니다. 퍼포스는 데이터베이스에 대해 체크포인트라는 일종의 풀 덤프와 저널이라는 지난 덤프 이후에 일어난 변경사항을 별도로 기록한 파일을 만들어 주기는 하지만 체크포인트 로테이션과 저널 로테이션은 관리자가 직접 자동화 해서 수행되도록 설정하지 않는 한 영원히 아무것도 하지 않습니다. 그나마 저널은 서버를 시작할 때 기본으로 켜져 있지만 체크포인트를 통해 저널을 로테이션 시켜 주지 않으면 이론적으로 저널은 영원히 커질 것입니다.

또 퍼포스 이외의 회사에서 개발한 현대적인 정보시스템과 호환 시키기 어렵습니다. 가령 현대에 지라는 거의 업계 표준 이슈 트래커로 자리 잡았습니다. 물론 퍼포스는 지라가 세상에 등장하기 훨씬 이전부터 존재했으며 퍼포스 스스로가 ‘잡'이라는 단순한 이슈 트래커를 스스로 내장하고 있으며 그들의 릴리즈 노트를 살펴보면 그들이 계속해서 이 '잡’에 기반해 개발해 오고 있음을 알 수 있습니다. 하지만 현대에는 다른 도구와 연동을 위해 당연히 지라에 기반해 일하는 팀이 많으며 지라를 선택한 이상 정보시스템으로 컨플루언스를 선택한 팀 역시 많습니다. 하지만 퍼포스는 퍼포스 서버 그 스스로는 그 어떤 도구와도 연동되지 않습니다. 다른 도구와 연동하기 위해서는 같은 제조사의 다른 서비스, 다른 소프트웨어에 의존해야 하는데 이들을 별도로 설치하고 연동된 상태를 유지하는 일은 만만하지 않습니다. 단순히 퍼포스와 지라를 연동시키려고 하면 오랫동안 제대로 업데이트 된 것 같지 않은 낡은 도구를 사용해야 하는데 연동 방식은 지라 태스크를 퍼포스의 '잡'에 일대일로 대응 시켜 서로를 복사하는 방식으로만 동작해 굉장히 불편하고 또 서로 호환되지 않을 때 원하지 않는 방식으로 동작하기 십상입니다. 퍼포스 스스로가 스웜, 스웜 트리거 같은 웹 기반의 코드 리뷰 시스템과 일종의 어뎁터를 제공하지만 이 기능들이 왜 퍼포스 서버와 별도로 존재해야 하는지 납득하기는 쉽지 않습니다. 같은 맥락에서 퍼포스는 적어도 자기 스스로는 현대적인 웹 환경을 통한 접근 기능 자체를 제공하지 않습니다. 아주 오래 전에는 스스로 웹 클라이언트를 개발해 제공한 적도 있지만 이는 그들 스스로 개발한 좀 더 현대적이면서도 웹 클라이언트라는 관점과는 상당히 다른 제품을 출시하면서 개발이 중단되어 지금은 단순히 모바일이나 웹 환경에서 동작하는 단순한 클라이언트 소프트웨어는 존재하지 않습니다.

이 모든 단점에도 불구하고 퍼포스를 사용하고 있는 이유는 이 프로그램이 아주 오랜 세월에 걸쳐 개발되어 오면서 자신의 존재 이유를 잘 수행하고 있기 때문임과 동시에 제가 일하는 분야에서 널리 사용되는 형상관리도구여서 익숙하다는 것도 있습니다. 반대로 너무 오랜 세월에 걸쳐 계속해서 개발되어 온 덕분에 앞서 잠깐 설명한 파일 타입은 제 관점에서 ‘이상해' 보이고 또 현대적인 유지보수 기능이 모두 완전히 수동으로 제어되는 것 역시 그리 바람직해 보이지 않습니다. 또 현대적인 퍼포스 기준 서드파티 정보시스템에 연동 시키기 꽤 골치 아프며 웹이나 모바일 같은 다양한 환경을 전혀 지원하지 않는 점 역시 썩 달갑지 않습니다. 여전히 깃은 바이너리 파일을 너무나도 못 다루니 고려 대상이 아니지만 현대의 SVN은 과거와 같은 이상한 동작을 더 이상 하지 않는다는 사실을 알고 있어 퍼포스가 유일한 선택이 아니라는 사실을 알고 있습니다. 당분간 퍼포스에 제 모든 파일을 의탁하며 지낼 것인 확실하기는 하지만 이 상태가 영원히 지속되리라 생각하지 않습니다. 현대인의 관점에서 퍼포스는 오랜 세월에 걸쳐 안정적으로 동작해 왔지만 현대적인 요구사항을 아주 잘 받아들이고 있다고 보기 어렵습니다. 지난번엔 추천했지만 분명 단점도 있습니다.