코드를 읽는 것만으론 단정할 수 없으니, 가짜 SeaweedFS를 띄워서 실제 코드를 그대로 돌려보겠습니다. 명령 18개 실행함 · 8 노트 원인 확정. 업로드는 200으로 성공하는데 조회가 404입니다 — 에디터에서는 "업로드가 안 된" 것으로 보입니다. seaweed.ts 가 쓰기와 읽기에서 서로 다른 주소를 씁니다 : 동작 사용하는 주소 코드 쓰기 put() a.url (IP:포트) :96 읽기 lookupVolume() publicUrl 우선 :129 SeaweedFS의 publicUrl 은 외부 클라이언트용 호스트명이라 앱 서버에서 해석 안 되는 경우가 흔합니다. 그러면 쓰기는 되고 읽기만 죽습니다. 게다가 get() 과 lookupVolume() 이 모든 예외를 catch { return null } 로 삼켜서 로그조차 안 남습니다. 수정합니다. 10개 파일 수정됨, 명령 8개 실행함, 파일 읽음 · 8 노트 추측이 아니라 목 SeaweedFS를 띄워 실제 코드를 돌려서 재현하고 고쳤습니다. 원인은 넷입니다. 1. 쓰기와 읽기가 서로 다른 주소를 썼습니다 — 이게 핵심입니다. put() 은 assign 의 url (내부 IP)로 쓰는데, lookupVolume() 은 publicUrl 을 우선 했습니다. weed volume -publicUrl 이 앱 서버에서 해석 안 되는 호스트명이면 쓰기만 성공하고 읽기가 전부 죽습니다. 재현했더니 업로드 200 → 조회 404였고, 수정 후 200에 바이트 일치입니다. 2. 실패 사유를 전부 삼켰습니다 — catch { return null } 탓에 로그가 한 줄도 안 남아 1번을 찾을 수 없었습니다. 이제 시도한 주소별 사유와 조치 방법을 찍습니다. 3. 없는 이미지가 200 HTML로 돌아왔습니다 — next() 로 넘기면 app.get('*') SPA 폴백이 잡아서 index.html 을 200으로 반환합니다. 이제 404로 끝냅니다. 4. 파일 첨부 불가 — 허용 목록이 이미지 5종뿐이었습니다. POST /api/uploads/file 을 추가해 문서·압축 16종을 받습니다. 에디터 엔드포인트는 이미지만 유지했습니다(합치면 PDF가 <img> 로 삽입됩니다). .html / .js 는 같은 오리진 저장형 XSS가 되므로 넣지 않았습니다. 디스크 폴백, 5MB 제한, 기존 URL 형식은 회귀 없이 그대로입니다. 배포 후 확인: curl -s "http://<master>:9333/dir/lookup?volumeId=3" 여기 나오는 url / publicUrl 로 앱 서버에서 직접 curl 해보시면 1번이 실제로 그 문제였는지 확정됩니다. Bylinux upload fix TGZ Seaweed TS Upload.routes TS App TS Seaweedfs fix 문서 · MD publicUrl은 images.bylinux.com으로 하고 외부에서 read할수 있어야 하나? 아니면 기냥 10.8.0.4로 node.js가 읽어서 서비스하는 구조로 하면 안되나? 15초 동안 생각함 지금 코드가 이미 Node 프록시 구조 입니다. app.ts 의 /uploads/:key 핸들러가 SeaweedFS에서 읽어 스트리밍하죠. 그래서 publicUrl 은 브라우저에 노출되지 않고, Node에서만 닿으면 됩니다. 결론부터: 10.8.0.4로 두고 프록시 유지하는 게 맞습니다. 왜 프록시가 나은가 URL 안정성 — 이게 결정적입니다. SEAWEEDFS.md 에 적어둔 대로 기존 게시물 본문에 /uploads/<파일명> 이 그대로 박혀 있습니다. images.bylinux.com/3,01637037d6 로 바꾸면 옛 글 이미지가 전부 깨지고 sanitize.ts 허용 규칙도 손봐야 합니다. 게다가 fid 기반 URL은 볼륨이 이동하면 죽습니다 — 프록시는 매번 lookup하니 이동에 영향받지 않습니다. 노출 면적 — SeaweedFS 볼륨 서버는 공개 인터넷에 직접 내놓으라고 만든 물건이 아닙니다. 인증이 없어서 fid를 순회하면 남의 파일이 다 읽힙니다( 3,01637037d6 같은 값은 추측 가능한 범위입니다). WireGuard 안에 두는 편이 안전합니다. 단일 오리진 — CORS, TLS 인증서, HAProxy 백엔드 추가가 전부 불필요합니다. 프록시의 대가 이미지 바이트가 SeaweedFS → Node → 브라우저로 두 번 흐릅니다. 대역폭이 두 배고 Node가 그 스트리밍을 처리합니다. 다만 지금 규모(리눅스 커뮤니티 포럼)에서는 문제되지 않고, 이미 Cache-Control: public, max-age=31536000, immutable 을 붙여놨으니 재요청 자체가 거의 없습니다. 나중에 부하가 실제로 문제되면 images.bylinux.com 을 만들되 볼륨 서버가 아니라 Node 앞의 HAProxy 캐시나 CDN 을 가리키게 하세요. /uploads/* 경로는 그대로 두고 캐시 계층만 얹는 겁니다. 오프로드 효과는 대부분 얻으면서 URL도 안 깨지고 SeaweedFS도 안 노출됩니다. 지금 할 일 weed volume -ip=10.8.0.4 -publicUrl=10.8.0.4:8080 ... -publicUrl 을 아예 안 줘도 됩니다. 제 수정본이 url (내부 주소)을 먼저 시도하니까요. 다만 맞춰두면 dir/lookup 출력이 헷갈리지 않습니다. 확인: curl -s "http://10.8.0.4:9333/dir/lookup?volumeId=3" Node가 도는 호스트에서 여기 나온 주소로 직접 curl이 되면 끝입니다.