FileSharer 개발 노트 7 — 외부 공유 주소를 받았다고 연결이 끝난 것은 아니다

FileSharer 개발 노트 · 07 / 10

세 개발자가 두 작업대 사이의 연결 경로를 시험하고 준비 상태를 확인합니다.
AI 일러스트 · 세 개발자가 두 작업대 사이의 연결 경로를 시험하고 준비 상태를 확인합니다.

사내망 공유 주소를 외부 사람에게 보낼 때는 경로가 하나 더 필요합니다. FileSharer는 C# 본체에 파일 전송 기능을 두고, 외부 연결을 만드는 작업을 별도 터널에 맡깁니다. 이 구조에서 흥미로운 지점은 터널을 켜는 명령보다 어느 시점부터 사용 가능하다고 판단하는가입니다.

C# 본체와 Go 중계 프로세스의 소스 구조를 정리한 설명도입니다. 실제 외부 터널을 개설하거나 전송 성능을 측정한 화면은 아닙니다.
C# 본체와 Go 중계 프로세스의 소스 구조를 정리한 설명도입니다. 실제 외부 터널을 개설하거나 전송 성능을 측정한 화면은 아닙니다.

이번 편에서는 FileSharer의 연결 상태 처리와 재연결 구조를 다룹니다. 실제 계정의 터널 개설·공개 링크 발급·중계 서비스 성능 시험은 이 글의 검증 범위에 포함하지 않습니다.

C# 프로그램 안에 Go 실행 파일을 넣은 이유

프로젝트에는 C# 본체 외에 tsproxy/main.go가 있습니다. Tailscale의 Go 라이브러리를 사용하는 작은 중계 프로그램입니다. 본체는 압축해 내장한 tsproxy.exe.gz를 풀어 자식 프로세스로 실행하고, 로컬 HTTP 서버로 연결하도록 주소를 전달합니다.

역할을 나누면 파일 선택과 Windows UI를 Go로 다시 만들 필요가 없습니다. 반대로 중계 라이브러리의 사용을 C#에 억지로 옮길 필요도 없습니다. 대신 실행 파일 포함, 프로세스 종료, 상태 전달이라는 새로운 관리 대상이 생깁니다.

현재 코드는 인증 값을 명령행 문자열에 직접 붙이지 않고 자식 프로세스의 환경 변수로 넘깁니다. 이것이 모든 로컬 접근으로부터 값을 숨긴다는 보장은 아닙니다. 여기서 확인할 사실은 전달 채널이 명령행 인자와 분리돼 있다는 점입니다.

표준 출력도 두 프로그램 사이의 인터페이스다

C#은 Go 프로세스의 표준 출력과 오류 출력을 읽습니다. 정해진 접두어를 발견하면 결과를 처리합니다.

FUNNEL_URL=<공개 주소>
TSPROXY_ERROR=<오류 설명>

임의의 진행 로그와 기계가 읽는 결과를 구분한 방식입니다. 이 규칙이 바뀌면 중계 프로그램은 정상으로 떠 있어도 본체가 주소를 받지 못할 수 있습니다. 따라서 실행 파일과 본체의 버전만큼 이 메시지 형식도 함께 관리해야 합니다.

준비 상태를 네 단계로 나눈다

프로세스가 존재하는지, 공개 이름이 조회되는지, HTTPS로 응답하는지, 실제 파일이 내려받아지는지는 서로 다른 확인입니다. Go 소스에는 DNS와 HTTPS 준비를 기다리는 함수가 있지만, 확인할 수 없거나 대기 시간이 지나도 별도 실패 결과를 반환하지 않습니다. 호출부는 경고 없이 준비 단계 로그와 공개 주소를 출력하므로, 주소가 나왔다는 사실만으로 준비 확인에 성공했다고 볼 수 없습니다.

관찰 확인할 수 있는 범위
자식 프로세스가 종료되지 않음 로컬 프로세스 생존
DNS 확인 성공 이름을 주소로 조회 가능
HTTPS 준비 확인 성공 해당 확인 경로의 응답
샘플 파일 수신 후 내용 비교 그 파일 전달 경로의 결과

제품 화면도 이 단계를 구분하면 사용자가 “주소는 있는데 왜 안 열리지?”를 이해하기 쉽습니다. 하나의 초록불에 모든 의미를 담으면 실패 원인이 흐려집니다.

감시가 정상 전송을 끊지 않도록 하려는 선택

WinForms 타이머는 25초 간격으로 감시하고, 세 번째 틱마다 더 깊은 접근 확인을 시도합니다. 한 번의 지연으로 바로 재연결하지 않고 연속 실패를 셉니다. 대용량 전송 중에는 확인 요청이 늦어져도 기존 다운로드를 끊지 않으려는 의도입니다.

다만 건너뛰기 판단에 사용하는 activeTransfers는 최근에 흐른 바이트 수가 아니라 열린 전송 작업 수입니다. LAN 전송도 같은 계수를 사용합니다. 따라서 “전송 작업이 있다”를 “외부 터널에서 데이터가 계속 흐른다”로 확대해서 설명할 수는 없습니다. 최근 진행 시각과 외부 경로의 상태를 따로 기록하는 것은 후속 개선 후보입니다.

재연결에는 링크 구성도 포함된다

소스 비교 중에는 복구 경로의 차이도 보였습니다. 최초 외부 링크 생성에는 복호화 정보가 담긴 fragment가 포함되지만, OnTunnelReconnected는 공개 주소와 공유 ID만으로 새 링크를 구성합니다. 브라우저의 복호화 시작 코드는 필요한 fragment가 없으면 중단합니다.

이 차이 때문에 재연결 시험은 프로세스와 주소 확인에서 끝내지 않고, PIN 화면을 거쳐 최종 파일 수신까지 확인해야 합니다. 코드상 실패 가능성이며 실제 외부 터널에서의 재현 여부는 별도 검증이 필요합니다.

Windows 자식 프로세스 정리에는 Job Object를 연결하려는 코드도 있습니다. 이 코드는 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 플래그를 설정합니다. 이 플래그가 적용된 Job의 마지막 핸들이 닫히면 연결된 프로세스도 종료됩니다. 이 조건은 Microsoft의 Job Objects 문서에 설명돼 있습니다. 실제 연결 실패나 종료 예외까지 검증해야 전체 수명이 관리됐다고 할 수 있습니다.

외부 공유 기능은 주소를 하나 얻는 작업보다 넓습니다. 시작, 준비, 사용, 재연결, 종료를 각각 관찰 가능한 상태로 만들 때 문제를 좁혀 갈 수 있습니다.

소스 근거
FileSharer.cs:1601–1689 TailscaleTunnel.Start · FileSharer.cs:1468–1553 ChildProcessGuard · FileSharer.cs:3290–3421 터널 감시와 재연결 · FileSharer.cs:4288 최초 외부 링크 구성 · tsproxy/main.go:110–232 DNS·HTTPS 확인 · tsproxy/main.go:393–407 서버 시작과 URL 출력
본문의 개선안은 현재 구현된 기능과 구분합니다.

댓글