블로그로 돌아가기
커뮤니티

PTT 이미지 링크가 자동으로 안 열리는 이유, 소스코드 4개를 직접 뜯어봤다

게시일25 min read
공유

링크는 분명히 붙였는데 이미지가 안 열린다 — 처음엔 내 실수인 줄 알았다

PTT를 안 써본 사람을 위해 짧게 설명하면, 대만에서 20년 넘게 운영 중인 텍스트 기반 BBS(전자게시판)다. 지금도 텔넷 방식 터미널 화면을 그대로 쓰는데도 활성 계정만 수백만 명 규모다. 여기 글을 올려본 사람이라면 누구나 한 번쯤 겪는 게 있다. 이미지 링크를 붙였는데 어떤 때는 썸네일로 자동 전환되고, 어떤 때는 그냥 파란 밑줄 텍스트로 남는다는 것.

다들 처음엔 자기가 링크 형식을 잘못 붙였다고 생각한다. 아니다. 진짜 헷갈리는 건 "어떤 호스팅이 좋은가"가 아니다. 완전히 똑같은 링크가 모바일 앱에서는 자동으로 썸네일이 되고, 컴퓨터 웹 버전에서는 그냥 문자열 URL로만 남는다는 사실이다.

이건 우연도 아니고 내가 형식을 잘못 붙인 것도 아니다. "PTT"라는 이름 아래에는 사실 서로 아무 관계도 없는 소프트웨어 최소 네 종류가 숨어 있고, 각각 완전히 다른 로직으로 "이 URL을 이미지로 바꿀지 말지"를 판단한다. 어떤 건 확장자만 본다. 어떤 건 도메인 화이트리스트만 본다. 둘 다 요구하는 곳도 있다. 심지어 확장자 검사를 아예 건너뛰고 그냥 Imgur 링크일 거라고 도박을 거는 방식도 있는데, 이 얘기는 뒤에서 다룬다.

PTT의 질문 게시판(ask 게시판)에 2025년 10월 자동 이미지 열림을 지원하는 호스팅을 묻는 글이 올라왔다. 글쓴이 말로는 예전에 term.ptt.cc에서 Imgur 링크에 마우스를 올리면 이미지 미리보기가 떴는데, 반년쯤 전부터 안 되기 시작했다고 한다. 이 한 문장이 많은 사람이 놓치는 사실을 보여준다. 같은 클라이언트, 같은 이미지 호스팅이라도 규칙은 시간이 지나면서 바뀌고, 아무도 공지해주지 않는다는 것. 커뮤니티에 떠도는 이야기만 믿는 대신 주요 클라이언트의 소스코드를 직접 뜯어봤다. 아래 표의 모든 칸에는 근거가 되는 출처가 있다.

PTT에서 이미지를 보는 경로는 사실 5가지, "PTT"는 하나의 소프트웨어가 아니다

비교표를 보기 전에 먼저 상황부터 정리하자.

  • 텍스트 전용 BBS 터미널(PCMan, Welly 등): 텍스트만 취급하고, 이미지 링크는 항상 그냥 문자열로 남는다.
  • www.ptt.cc 웹 버전 게시글: 게시글 페이지를 불러올 때 서버 쪽에서 조건에 맞는 URL을 <img> 태그로 바꿔서 내려준다.
  • term.ptt.cc: 웹소켓으로 BBS에 접속하는 웹 터미널. 링크에 마우스를 올리면 미리보기가 뜨고, 판정은 프런트엔드 JavaScript가 담당한다.
  • PttChrome: 오래된 브라우저용 텔넷 클라이언트. 이미지가 게시글 화면에 직접 삽입된다.
  • 모바일 앱(Mo PTT, JPTT, PiTT, BePTT 등): 링크를 바로 썸네일로 보여준다. 다만 이쪽은 비공개 소스라 커뮤니티 제보에 의존할 수밖에 없다.

앞의 둘과 뒤의 셋 사이 차이는 생각보다 크다.

소스코드를 직접 읽어봤다 — 4가지 규칙, 하나도 똑같지 않다

www.ptt.cc 웹 버전 게시글: 서버 쪽 판정, 확장자 우선, WebP는 안 됨

www.ptt.cc의 웹 프런트엔드는 pttweb이라는 이름이고, 소스코드는 GitHub의 ptt/pttweb 저장소에 공개돼 있다. 게시글 안의 URL을 이미지로 바꾸는 처리를 담당하는 파일이 richcontent/url_based_finder.go이고, 순서대로 비교하는 패턴 목록이 들어 있다.

var defaultUrlPatterns = []*UrlPattern{
    newUrlPattern(`^https?://(?:www\.youtube\.com/watch\?(?:.+&)*v=|youtu\.be/)([\w\-]+)`, handleYoutube),
    newUrlPattern(`^https?://i\.imgur\.com/([\w]+)\.((?i)png|jpeg|jpg|gif)$`, handleImgurSingle),
    newUrlPattern(`^http://picmoe\.net/d\.php\?id=(\d+)`, handlePicmoe),
    newUrlPattern(`\.(?i:png|jpeg|jpg|gif)$`, handleGenericImage),
}

중요한 건 마지막 줄이다. 앞의 세 개는 YouTube, Imgur, picmoe.net 전용 규칙이고, 네 번째가 핵심이다. 어떤 도메인이든 상관없이 URL 끝이 .png, .jpeg, .jpg, .gif(대소문자 구분 없음)로 끝나기만 하면 무조건 <img> 태그로 감싸서 보여준다. 즉 www.ptt.cc 웹 버전 게시글은 "확장자는 보고 도메인은 안 본다"는 방식이고, 뒤에서 설명할 term.ptt.cc와는 완전히 반대다.

다만 정규식이 $로 끝을 고정해놨기 때문에 URL이 정말로 확장자로 끝나야 한다. xxx.jpg?token=abc123처럼 쿼리 문자열이 붙은 형식은 이 마지막 규칙에 걸리지 않는다. 게다가 .webp는 이 목록에 아예 없다. 진짜 정상적인 WebP 직링크라 해도 www.ptt.cc 웹 버전 게시글에서는 절대 자동으로 열리지 않고 링크 문자열만 남는다는 뜻이다. 이 부분은 다른 정리 글에서는 잘 안 짚어주는 대목이다.

term.ptt.cc: 도메인 화이트리스트가 1차 관문, 확장자가 2차 관문

term.ptt.cc 소스코드는 GitHub의 ptt/ptt-term에 있고, 웹소켓으로 BBS에 접속하는 웹 터미널이다. 마우스를 올렸을 때 미리보기를 담당하는 모듈이 src/plugins/media_previewer/image_preview_util.js이고, 기본 신뢰 도메인 목록이 코드에 직접 박혀 있다.

export const INITIAL_TRUSTED_IMAGE_DOMAINS = [
  "imgur.com", "imgtok.com", "meee.com.tw", "duk.tw",
  "upload.cc", "ibb.co", "imgbb.com", "postimg.cc",
  "twimg.com", "gyazo.com",
];

이 목록에는 urusai.cc도, vgy.me도, lpic.cc도 없다. 그런데 같은 ask 게시판 글에는 urusai.cc나 vgy.me가 자기 환경에서는 자동으로 열린다는 제보가 있다. 모순처럼 보이지만 사실 그렇지 않다. 코드 안에 parseTrustedDomains라는 함수가 따로 있어서, 이 화이트리스트는 설정에서 도메인을 추가할 수 있게 돼 있다. 기본값 10개는 그냥 "기본 세팅"일 뿐 전부가 아니라는 뜻이다. 솔직히 그 제보자는 이미 개인 설정에 urusai.cc를 추가해뒀을 가능성이 높지, term.ptt.cc가 모든 사용자에게 기본으로 지원해주는 건 아닐 가능성이 크다. 여기는 "전체 규칙"으로 오해하기 딱 좋은 지점이다.

판정 로직 자체는 두 단계로 나뉜다. Imgur는 전용 정규식이 있어서 확장자가 없어도 인식된다. pbs.twimg.com/media/로 시작하는 링크는 확장자 검사 없이 바로 통과된다. 그 외 화이트리스트 도메인은 URL 끝이 .jpg, .jpeg, .png, .gif, .webp, .bmp 중 하나여야 하고(쿼리 문자열이 붙어 있어도 상관없다, 이 점은 www.ptt.cc 웹 버전과 다르다), term.ptt.cc는 "도메인 AND 확장자" 둘 다 통과해야 하는 방식이다.

PttChrome: 도메인만 보고, 확장자는 아예 신경도 안 씀

PttChrome은 더 오래된 프로그램이고, 지금 유지보수되는 버전은 iamchucky/PttChrome에 있다. 화면에 이미지 미리보기를 담당하는 파일은 js/term_view.js이고, 대상 링크를 골라낼 때 이런 jQuery selector를 쓴다.

var aNodes = $(".main a[href^='http://ppt.cc/'], .main a[type='p'], " +
  ".main a[href^='http://imgur.com/'], .main a[href^='https://imgur.com/'], " +
  ".main a[href^='https://flic.kr/p/'], .main a[href^='https://www.flickr.com/photos/']")
  .not("a[href^='http://imgur.com/a/'], a[href^='https://imgur.com/a/'], " +
       "a[href^='http://imgur.com/gallery/'], a[href^='https://imgur.com/gallery/']");

여기서 좀 난감해진다. PttChrome은 확장자를 아예 검사 안 하고, URL 앞부분만 본다. 화이트리스트에는 ppt.cc 단축 URL, imgur.com(앨범과 gallery 페이지는 제외), flic.kr, flickr.com/photos만 있다. urusai.cc, vgy.me, duk.tw, meee, ImgBB, Postimages, lpic.cc는 전부 목록 밖이라 아무리 표준적인 직링크를 붙여도 자동으로 안 열리고, 항상 그냥 파란 링크 텍스트로만 남는다. 더 재미있는 건 Imgur 링크 처리 방식인데, 코드에서 URL을 아예 i.imgur.com/ID.jpg로 다시 써버린다. 확장자를 .jpg로 못 박아버리는 것이다. 원본이 PNG든 GIF든 상관없다. 이건 10년도 더 된 작은 함정 같은 건데, 평소에는 아무도 눈치 못 챈다. Imgur가 확장자가 안 맞는 요청에도 보통 정상적으로 이미지를 돌려주기 때문이다.

모바일 앱(Mo PTT, JPTT, PiTT): 비공개 소스라 커뮤니티 증언에만 의존

솔직히 말해서, 이 부분만큼은 소스코드 수준의 답을 줄 수가 없다. Mo PTT, JPTT, PiTT는 전부 비공개 소스 상용 앱이라 GitHub에 공개 저장소가 없다. 내가 찾을 수 있었던 가장 확실한 근거는 2025년 6월과 10월, 시기가 가까운 두 개의 PTT ask 게시판 글에서 사용자들이 실제로 테스트하고 올린 후기다. meee.com.tw는 .jpg 확장자를 수동으로 붙이면 자동으로 열린다, urusai.cc의 i.urusai.cc/ID.png 형식은 된다, imgpoi.com은 "애초에 자동 열림을 지원 안 하는 것 같다"는 의문이 제기됐다는 식이다. 이런 내용들은 전부 "확장자 기반 판정" 쪽을 가리키고 있고, www.ptt.cc 웹 버전 로직과 비슷해 보이지만, 이 앱들의 소스코드나 공식 기술 문서를 제시해서 도메인 제한 여부까지 증명한 사람은 아무도 없다. 이 항목은 명확히 미검증으로 표시한다. 직접 코드를 뜯어본 것처럼 꾸미지 않겠다.

4가지 규칙 한눈에 비교

클라이언트 판정 기준 확장자 요건 WebP 근거 수준
www.ptt.cc 웹 버전 게시글 확장자만, 도메인은 안 봄 끝이 .jpg/.jpeg/.png/.gif여야 함, 쿼리 문자열 불가 미지원 소스코드(ptt/pttweb)
term.ptt.cc 도메인 화이트리스트 + 확장자 둘 다 화이트리스트 도메인에서 끝이 .jpg/.jpeg/.png/.gif/.webp/.bmp, 쿼리 문자열 가능 지원 소스코드(ptt/ptt-term)
PttChrome 도메인만, 확장자 검사 없음 검사 안 함(Imgur 링크는 강제로 .jpg로 변환) 해당 없음 소스코드(iamchucky/PttChrome)
모바일 앱(Mo PTT/JPTT/PiTT) 확장자 기반으로 추정 .jpg/.png/.gif가 된다는 제보 있음 미검증 커뮤니티 제보만 있고 소스 확인 불가

표로 정리해보면 분명해진다. "PTT 공통 규칙" 같은 건 존재하지 않는다. 같은 이미지 호스팅의 직링크라도 네 클라이언트에서 네 가지 다른 결과가 나올 수 있다.

디시인사이드나 뽐뿌, 네이버 밴드 같은 국내 커뮤니티에 익숙한 사람이라면 오히려 이 상황이 낯설지 않을 수도 있다. 디시인사이드도 게시판마다, 심지어 모바일 앱과 PC 웹에서 이미지 자동 미리보기 처리 방식이 조금씩 다르고, 통일된 표준이 있는 게 아니다. PTT가 특별히 이상한 게 아니라, 게시판 문화 자체가 "각 클라이언트가 알아서 규칙을 만든다"는 역사를 갖고 있다고 보는 게 더 정확하다.

이미지 호스팅 × 클라이언트 호환표

위 규칙들과 자주 쓰이는 이미지 호스팅의 직링크 형식을 대조해서 만든 표다. "✅"는 규칙상 자동으로 열려야 함, "❌"는 안 열림, "⚠️"는 설정이나 확장자를 수동으로 조정했는지에 따라 다름을 뜻한다.

이미지 호스팅(직링크 예시) www.ptt.cc 웹 버전 term.ptt.cc(기본값) PttChrome 앱(커뮤니티 제보)
Imgur i.imgur.com/ID.jpg ✅ ✅ ✅(강제로 .jpg 변환) ✅
meee meee.com.tw/ID.jpg ✅ ✅ ❌ ✅(수동으로 확장자 필요)
duk.tw ✅(확장자 붙이면) ✅ ❌ 미검증
urusai.cc i.urusai.cc/ID.png ✅(확장자 붙이면) ⚠️ 직접 화이트리스트에 추가해야 함 ❌ ✅(커뮤니티 제보)
vgy.me ✅(확장자 붙이면) ⚠️ 직접 화이트리스트에 추가해야 함 ❌ 미검증, 커뮤니티에서는 "제일 까다롭다"는 평
ImgBB ibb.co / i.ibb.co ✅(확장자 붙이면) ✅ ❌ 미검증
Postimages postimg.cc ✅(확장자 붙이면) ✅ ❌ 미검증
lpic.cc lpic.cc/{code} ❌(확장자 없음) ❌(화이트리스트 밖, 확장자도 없음) ❌ ❌(직링크 자체가 없음)

마지막 줄은 솔직하게 짚고 넘어가겠다.

lpic.cc 링크는 지금 PTT에서 자동으로 열리나? 안 된다, 이게 현재 한계다

이 글을 쓰면서 lpic.cc 자체의 공유 링크 형식을 다시 확인했다. 지금 업로드 후 받는 건 https://lpic.cc/{짧은코드} 형태의 작품 페이지 링크이지, i.imgur.com/xxx.jpg처럼 끝이 이미지 확장자로 끝나는 직링크가 아니다. 그래서 www.ptt.cc의 확장자 규칙도, term.ptt.cc나 PttChrome의 도메인 화이트리스트도 전부 걸리지 않는다. 공유 페이지 자체는 썸네일을 보여주지만, 그건 페이지를 열었을 때 보이는 것이지 "링크만 붙이면 자동으로 펼쳐지는" 게 아니다.

솔직히, 자동으로 이미지가 열리길 원하는 사람한테는 좋은 소식이 아니고, 이걸 다르게 포장할 생각도 없다. "링크만 붙이면 앱이 알아서 썸네일로 바꿔주는" 걸 원한다면 위 표에서 체크 표시가 된 이미지 호스팅(Imgur, meee, term.ptt.cc 기본 화이트리스트에 있는 몇 곳)이 더 맞는다. lpic.cc 같은 공유 페이지 방식의 장점은 다른 데 있다. 비밀번호 보호, 조회수, 댓글 같은 페이지 상호작용 기능은 "누가 볼 수 있는지", "본 기록이 남는지"를 직접 통제하고 싶은 상황, 예를 들면 거래 스크린샷이나 아무나 자동으로 미리보기 하는 걸 원치 않는 콘텐츠에 더 어울린다. 두 가지 필요는 애초에 다른 거라서, 한쪽 도구에 다른 쪽 역할을 억지로 맡기면 쓰기 불편해진다.

그래서 결국 뭘 써야 하나

"붙이자마자 무조건 자동으로 열렸으면 좋겠다"가 전부라면, 주로 쓰는 클라이언트를 기준으로 정하면 된다. 모바일 앱 위주라면 Imgur, 아니면 확장자를 수동으로 붙이는 습관이 있다면 meee가 적당하다. 웹 버전 게시글 위주라면 표준 확장자로 끝나는 직링크를 주는 이미지 호스팅은 대체로 통하지만, WebP만은 www.ptt.cc 웹 버전 게시글에서 절대 안 먹힌다는 걸 기억해야 한다. term.ptt.cc를 쓴다면 먼저 자기 화이트리스트 설정에 그 이미지 호스팅이 들어 있는지 확인하고, 없으면 직접 추가하면 된다.

여러 클라이언트를 넘나들면서 전부 통하는 걸 원한다면, 지금은 Imgur가 네 가지 규칙 전부를 통과하는 유일한 선택지다. 다만 대가로 대만 IP에서의 업로드가 이미 막혀 있다는 별개의 문제가 있는데, 이 부분은 PTT 이미지 게시 완전 가이드에서 더 자세히 다뤘다. 어떤 상황에서도 형식 걱정을 완전히 없애주는 이미지 호스팅은 솔직히 없다. 이미 여러 번 겪어봤기 때문에 특정 서비스를 좋게 포장할 생각은 없다.

자주 묻는 질문

Q1: 어제는 자동으로 열리던 Imgur 링크가 오늘은 왜 안 열리나요?

Imgur는 2025년 5월부터 대만 IP를 차단하고 있다. 대만에서 접속 중이라면 애초에 i.imgur.com 자체에 연결이 안 될 수도 있고, 이건 PTT 클라이언트 규칙과는 무관하다. 별개로 PttChrome은 Imgur 링크를 강제로 .jpg로 끝나게 바꾸는데, 원본이 투명 배경 PNG였다면 화질이나 투명도가 깨질 수 있다. 다만 이건 표시 품질 문제일 뿐, "이미지가 열리느냐 안 열리느냐"에는 영향을 안 준다.

Q2: term.ptt.cc의 신뢰 도메인 목록을 직접 수정할 수 있나요?

소스코드를 보면 parseTrustedDomains라는 함수가 커스텀 목록을 읽어오는 걸 지원하니까, 이건 조정 가능한 설정이지 고정된 규칙이 아니다. 다만 실제로 어느 설정 화면에서 조정하는지, 변경 후 새로고침이 필요한지까지는 직접 조작해서 확인해보지 못했다. term.ptt.cc의 설정 메뉴에서 직접 찾아보길 권한다.

Q3: 모바일 앱이 도메인 화이트리스트를 보는지 안 보는지, 왜 확답을 못 하나요?

Mo PTT, JPTT, PiTT가 전부 비공개 소스라서 한 줄 한 줄 대조해볼 공개 저장소가 없기 때문이다. 이 글에서 이 세 앱에 대해 쓴 내용은 전부 PTT ask 게시판의 사용자 제보에 근거한 것이지, 내가 직접 앱을 분해해서 얻은 결론이 아니다. 앞에서 이미 미검증이라고 명시했고, 여기서 갑자기 확정된 사실인 것처럼 바꾸지는 않겠다.

Q4: www.ptt.cc는 왜 WebP를 지원 안 하는데 term.ptt.cc는 지원하나요?

둘이 완전히 별개의 코드베이스이기 때문이다. 하나는 서버 쪽 Go로 짜인 pttweb이고, 다른 하나는 프런트엔드 JavaScript로 짜인 ptt-term이라 유지보수하는 사람도, 개발 시점도 다르다. WebP 형식이 널리 퍼진 건 비교적 나중 일이고, pttweb 쪽 규칙이 단순히 그걸 따라가지 못한 것뿐이다. 이게 이 글에서 가장 강조하고 싶은 핵심이기도 하다. PTT는 하나로 통합된 시스템이 아니라, 여러 개의 독립된 소프트웨어가 모여서 이뤄진 생태계다.

Q5: 모든 PTT 클라이언트에서 자동으로 열리는 링크를 만들려면 어떻게 조합해야 하나요?

모든 상황에 통하는 단일 링크는 없지만, 가장 가깝게 만드는 방법은 있다. 이미지 호스팅이 주는 직링크 끝이 표준 확장자(.jpg/.png/.gif, WebP 하나만 믿지 말 것)로 끝나도록 하고, 쿼리 문자열을 붙이지 말고, term.ptt.cc 기본 화이트리스트에 있는 이미지 호스팅(Imgur, meee, duk.tw, ImgBB, Postimages 등)을 우선 고르는 것이다. PttChrome 사용자는 선택지가 거의 없고, 지금은 사실상 Imgur와 Flickr 정도만 통한다.

참고 자료

#PTT#term.ptt.cc#PttChrome#이미지 호스팅#자동 이미지 임베드#직링크#게시판 이미지 첨부#대만 BBS
A

작성자

Alex Chen

제품 테스터

모든 툴을 직접 사용해보고, 1인칭 시점으로 실제 사용 경험을 전달합니다.