[이것도 AI로 될까? — 차 관리 편] 5부 — 집 안에서만 되는 건 절반이었다

[이것도 AI로 될까? — 차 관리 편] 5부 — 집 안에서만 되는 건 절반이었다
이번 편의 요청의 요지
정비소나 외출 중에도 내 기록 도구를 쓰게 하되, 집 안의 데이터와 관리 화면은 안전하게 지켜 줘.

집 안에서만 되는 것은 절반만 된 것이다

여기까지 만든 것들은 전부 집 NAS 안에서 돈다. 기록을 넣는 곳도, 판정하는 곳도, 화면도 거기 있다.

집에서는 잘 된다. 문제는 정비소가 집에 없다는 것이다.

정비를 받고 나오면서 기록을 넣으려면 밖에서 서버에 닿아야 한다. 화면을 열어 "지금 뭐가 갈 때가 됐지"를 보는 것도 대개 밖에서다. 집 안에서만 되는 도구는 정작 필요한 순간에 안 되는 도구다.

그래서 AI에게 집 안의 서버를 밖에서도 쓰게 해 달라고 했다. 조건은 두 가지였다. 사람이 쓰는 화면은 인증 뒤에 열리고, 기계가 부르는 길은 필요한 범위만 열려야 했다. 편리함을 늘리면서 집 안의 다른 데이터까지 함께 내놓고 싶지는 않았다.

공유기에 구멍을 내는 방법은 쓰지 않았다

밖에서 집 안으로 들어오는 흔한 방법은 공유기에 포트를 여는 것이다. 특정 포트로 들어온 요청을 안쪽 기기로 넘겨 주면 된다. 설정도 몇 분이면 끝난다.

쓰지 않았다. 포트를 열면 그 순간부터 인터넷 전체가 그 문을 두드릴 수 있다. 내가 주소를 아무에게도 안 알려 줘도 상관없다. 온종일 모든 IP의 모든 포트를 훑고 다니는 것들이 있고, 열린 포트는 며칠 안에 목록에 오른다.

그 문 뒤에 있는 것이 로그인 화면이라 해도 마음이 놓이지 않았다. 로그인 화면이 있다는 것은 그 소프트웨어가 인터넷에 노출돼 있다는 뜻이고, 거기에 취약점이 발견되면 내가 패치를 올리기 전까지는 열려 있는 것이다. 집 NAS에는 정비 기록만 있는 것이 아니다.

터널은 안에서 밖으로 연결한다

대신 터널 방식을 썼다. Cloudflare가 제공하는 것이다.

Cloudflare 연결 상태 화면

Cloudflare 연결이 켜진 상태를 확인한 화면이다. 실제 터널은 NAS 안의 프로그램이 바깥으로 연결을 유지한다.

방향이 반대인 것이 핵심이다. 밖에서 안으로 들어오는 문을 여는 게 아니라, 안에 있는 작은 프로그램이 밖으로 나가서 연결을 유지한다. 방문자는 Cloudflare에 접속하고, Cloudflare가 그 연결을 통해 내 NAS로 요청을 넘긴다.

그래서 공유기에는 아무 구멍도 나지 않는다. 포트 스캔에도 안 잡힌다. 스캔할 열린 포트가 없기 때문이다. NAS의 실제 주소도 밖에서 보이지 않는다.

설치는 도커 컨테이너 하나 띄우고 관리 화면에서 어느 주소를 어디로 보낼지 정하는 것이 전부였다. 인증서도 알아서 붙는다.

열어 놓고 아무나 못 들어오게

터널만 놓으면 그 주소는 인터넷에 공개된 것이다. 포트는 안 열었지만 주소를 아는 사람은 들어올 수 있다.

그래서 앞에 인증을 하나 더 걸었다. Cloudflare가 방문자를 먼저 확인하고, 통과한 사람만 뒤로 보내 준다. 내 서버에 요청이 닿기 전에 걸러진다는 점이 중요했다. 서버의 로그인 화면이 첫 방어선이면 그 화면 자체가 공격 대상이 되는데, 이 방식에서는 서버가 아예 요청을 받지 않는다.

정책은 단순하게 뒀다. 허용 목록에 이메일 하나. 로그인하면 한동안 유지되고 그 뒤에는 다시 확인한다.

설정하고 나서 확인은 로그인하지 않은 상태로 해 봤다. 브라우저는 이미 로그인돼 있으니 소용이 없다. 터미널에서 주소를 두드려 보니 전부 로그인 화면으로 넘기는 응답이 왔다. 화면이 아니라 응답 코드로 확인해야 한다. 시크릿 창으로 여는 방법도 있지만 이쪽이 확실하다.

주소가 바뀌자 관리 화면이 깨졌다

터널을 붙이고 나서 데이터 관리 화면을 열었더니 껍데기만 나왔다. 로그인은 되는데 그 뒤가 하얗다.

브라우저 개발자 도구를 열어 보니 화면을 그리는 데 필요한 파일들이 전부 없다고 나오고 있었다. 서버는 멀쩡히 돌고 있는데 파일을 못 찾는다.

원인은 그 도구가 자기 주소를 알고 있어야 한다는 데 있었다. 설정에 공개 주소를 적는 칸이 있고, 화면에 필요한 파일들의 경로를 그 값으로 만들어 낸다. 나는 그 칸을 집 안에서 쓰던 주소로 둔 채 밖에서 열고 있었다. 서버는 밖에서 온 요청에 대해 안쪽 주소로 된 경로를 돌려주고 있었던 것이다.

값을 새 주소로 고치고 다시 띄우자 바로 열렸다.

터널을 붙이는 것은 앞단의 일이라고 생각했는데, 뒤에 있는 프로그램이 자기가 어디에 있다고 생각하는지도 함께 맞춰야 했다. 주소를 하나 새로 내면 그 주소를 아는 곳이 여러 군데 생긴다.

콘솔이 안 열려서 삼십 분을 썼다

또 하나. 관리 도구에서 컨테이너 안으로 들어가는 콘솔이 밖에서는 안 열렸다. 집 안에서는 되는데 밖에서만 안 된다.

콘솔은 일반 요청이 아니라 연결을 유지하는 방식을 쓴다. 처음에 보통의 HTTP로 접속한 다음 그 연결을 다른 방식으로 바꿔 달라고 요청하고, 그때부터 양쪽이 계속 주고받는다. 터미널이니 그래야 한다.

앞단 프록시에 그 전환을 지원하는 설정이 있어서 켜 뒀는데도 안 됐다. 알고 보니 인증을 붙이려고 따로 써 둔 설정 블록이 기본 설정을 통째로 덮고 있었다. 전환에 필요한 헤더 세 줄이 그 블록에는 없었다.

그 세 줄을 직접 넣으니 열렸다.

설정을 겹쳐 쓸 때 나중 것이 앞엣것을 부분적으로 보완할 거라고 기대했는데, 실제로는 그 자리를 통째로 가져갔다. 이런 것은 문서를 읽어도 잘 안 보인다. 안 되는 것을 만나고 나서야 어느 설정이 어느 설정을 덮는지 들여다보게 된다.

로그인을 걸었더니 봇이 로그인 화면을 받았다

여기서 이 편의 진짜 이야기가 시작된다.

같은 방식으로 워크플로 도구에도 인증을 걸려고 했다. 관리 화면이 열려 있으면 위험하니 당연한 수순이라고 생각했다.

그런데 그 도구는 웹훅을 받는다. 아이폰 단축어가 명세서 글자를 보내는 곳이고, 텔레그램이 새 메시지를 알려 주는 곳이다.

인증을 걸면 어떻게 되나. 단축어가 요청을 보내면 로그인 화면이 돌아온다. 단축어는 로그인할 수 없다. 텔레그램도 마찬가지다. 사람이 브라우저로 들어올 때만 성립하는 방식을 기계가 부르는 곳에 걸면, 그 순간 기계는 전부 막힌다.

당연한 이야기인데 걸어 보기 전까지는 생각하지 못했다. "보안을 걸었다"와 "쓸 수 있다"가 충돌하는 지점이 있다는 것을 그때 알았다.

사람이 쓰는 곳과 기계가 부르는 곳을 갈랐다

그래서 하나로 덮지 않고 나눴다.

누가 부르나 어떻게 지키나
화면·관리 도구 사람이 브라우저로 앞단에서 인증. 통과해야 서버에 닿는다
웹훅 기계가 프로그램으로 앞단 인증 없음. 대신 비밀 헤더를 요구한다

웹훅 쪽은 주소를 아는 것만으로는 안 되게 했다. 요청에 약속된 헤더가 실려 있어야 받아 준다. 단축어와 봇은 그 값을 갖고 있고, 지나가다 주소를 발견한 쪽은 갖고 있지 않다.

완벽한 방식은 아니다. 헤더 값이 새면 그걸로 끝이다. 다만 기계가 부르는 문에는 기계가 통과할 수 있는 자물쇠를 달아야 한다. 사람용 자물쇠를 달면 문이 잠기는 게 아니라 기능이 멈춘다.

관리 도구 중 하나는 아예 다른 방식으로 뒀다. 그건 원래 다른 인증 시스템 뒤에 있었고, 굳이 옮기지 않았다. 옮기다가 그 도구 자체를 못 쓰게 되면 고칠 수단까지 함께 잃는다. 컨테이너를 관리하는 도구를 컨테이너 관리 도구로 재배포하는 상황은 피하고 싶었다.

무엇이 열려 있는지 알고 있어야 한다

정리하고 나니 열린 것과 닫힌 것이 이렇게 됐다.

  • 공유기 포트: 없음
  • 사람이 쓰는 주소: 인증 뒤. 이메일 하나만
  • 기계가 부르는 주소: 열려 있고 헤더로 확인
  • 그 밖의 것: 집 안에서만

이 목록을 적어 두는 것이 실은 가장 중요한 일이었다. 하나씩 설정할 때는 각각 합리적인데, 몇 달 지나면 무엇이 왜 열려 있는지 기억나지 않는다. 기억나지 않는 문은 닫지도 못한다. 닫으면 뭐가 깨질지 모르니까.

그래서 설치 절차와 함께 이 목록을 문서로 남겼다. 새로 뭔가를 열 때마다 여기에 한 줄을 더하고, 왜 열었는지도 적는다.

편한 쪽과 안전한 쪽이 다를 때

이 편에서 두 번 결정을 뒤집었다.

한 번은 포트를 여는 대신 터널을 쓴 것이다. 포트를 여는 쪽이 훨씬 간단했다. 다른 한 번은 모든 곳에 인증을 걸려다 기계가 부르는 곳은 빼기로 한 것이다. 전부 거는 쪽이 마음은 편했다.

두 번 다 "더 안전한 쪽"이 답은 아니었다. 첫 번째는 더 안전한 쪽이 맞았고, 두 번째는 더 안전하게 만들려던 것이 기능을 멈추게 했다. 보안은 세게 걸수록 좋은 게 아니라 무엇을 지키려는지 정하고 거기에 맞춰야 한다는 것을 두 번째에서 배웠다.

지금 이 시스템에서 지키려는 것은 명확하다. 정비 기록에 차량번호와 정비소 정보가 들어 있고, 같은 NAS에 다른 것들도 있다. 사람이 들어오는 문은 좁게, 기계가 들어오는 문은 열되 열쇠를 요구하는 것. 그 정도면 지금 규모에는 맞는다고 봤다.

이제 정비소를 나와서 사진을 찍고, 알림을 확인하고, 필요하면 기록을 바로 넣을 수 있다. 도구가 집 안에 있다는 사실은 그대로지만, 사용하는 순간만큼은 장소에 묶이지 않는다.

돌아보면 AI가 내 생활을 대신한 것은 아니다. 내가 불편한 장면을 설명하면 AI가 코드를 만들고, 나는 실제로 써 보면서 틀린 곳을 고쳤다. 사진을 보낼지 말지, 버튼을 둘지, 무엇을 자동화하지 않을지는 사람이 정했다. AI가 코드를 썼고, 그 코드가 내 생활의 한 부분을 덜 번거롭게 만들었다. 이것도 AI로 될까라는 질문에 대한 내 대답은, 생활의 불편을 구체적으로 설명하고 계속 고칠 수 있다면 된다는 쪽이다.


제작 기록

이 글의 초고 작성과 자료 정리, 그리고 터널·인증 설정과 확인 절차의 문서화에는 Claude Code가 사용됐다. 시리즈의 제목·본문 윤문과 재구성, 게시용 이미지 편집과 Ghost 드래프트 반영에는 ChatGPT(Codex)가 함께했다. 공개 발행 전 전체 내용과 화면은 사람이 직접 검토하고 수정했다. 본문에는 실제 주소와 정책 이름을 쓰지 않았다.