무료 오픈 소스 AI 스팸 필터로 LinkedIn 피드를 정리했습니다 — 실제 설정 방법 안내
요약
LinkedIn 피드의 스팸 및 AI 생성 콘텐츠를 제거하기 위해 오픈 소스 확장 프로그램을 활용하는 방법을 소개합니다. 단순 언팔로우가 아닌 콘텐츠 패턴과 정규 표현식을 이용한 필터링의 중요성을 강조합니다.
핵심 포인트
- 단순 언팔로우로는 알고리즘 기반 스팸 확산을 막기 어려움
- 콘텐츠 패턴(줄 바꿈, 문장 구조) 기반의 필터링이 효과적임
- 개인정보 보호를 위해 로컬에서 동작하는 오픈 소스 도구 권장
- 정규 표현식을 활용한 사용자 정의 필터링 가능
요약 (TL;DR): 저를 마침내 폭발하게 만든 것은 단 하나의 게시물이 아니었습니다. 화요일 아침에 LinkedIn을 열고 3분 동안 스크롤을 내렸는데도 제 실제 산업과 관련된 콘텐츠를 단 하나도 보지 못했다는 사실이었습니다. 대신 제가 본 것은: 한 창업자가 "거의 그만둘 뻔했다"고 발표하며...
📖 읽기 시간: 약 23분
이 글의 내용
- 내 LinkedIn 피드가 사용할 수 없게 되었다 — 그래서 직접 조치를 취했다
- 이 확장 프로그램이 실제로 하는 일 (마케팅 용어 제외)
- 설치하기: 실제 단계
- 필터 구성하기 — 설정이 실제로 하는 역할
- 잘 잡아내는 것 vs 어려워하는 것
- 일주일 사용 후 놀라웠던 3가지
- 이 도구를 사용하지 말아야 할 때
- 주요 오픈 소스 옵션 비교
내 LinkedIn 피드가 사용할 수 없게 되었다 — 그래서 직접 조치를 취했다
기본으로 제공되는 언팔로우 (unfollow) 버튼은 함정입니다. 저는 오후 내내 최악의 게시물 작성자들을 언팔로우하며 뿌듯함을 느꼈습니다. 하지만 문제가 단순히 10개의 계정이 아니라는 것을 깨닫기 전까지는 말이죠. 문제는 이러한 콘텐츠를 알고리즘적으로 보상하는 인센티브 구조에 있습니다. 클릭 유도형 (engagement-bait) 계정 하나를 언팔로우하면, 그들의 팔로워가 이제 당신의 확장된 네트워크에 포함되기 때문에 세 개의 계정이 더 나타납니다. 언팔로우만으로는 이 상황을 벗어날 수 없습니다. 스팸에 가까운 콘텐츠의 양은 수동 큐레이션 (manual curation)이 따라갈 수 없을 정도로 빠르게 증가합니다. 당신에게 실제로 필요한 것은 개별 사람이 아니라 _콘텐츠 패턴 (content patterns)_을 필터링하는 것입니다. 왜냐하면 정말 유용한 엔지니어링 분석 글을 올리는 사람도 2주마다 한 번씩 "발표하게 되어 감사하다"는 식의 겸손을 가장한 자랑 (humble-brag) 글을 올리기 때문입니다.
제 요구사항은 상당히 구체적이었습니다. 브라우저 확장 프로그램 (browser extension) 형태로 실행되어야 하고, 모든 과정을 DOM 내에서 로컬로 처리해야 하며, 18개월 뒤면 사라질지도 모를 어떤 스타트업의 서버로 외부 요청 (outbound requests)을 보내지 않아야 했습니다. 또한 구독 모델이 아니어야 했고, 이상적으로는 제가 내 피드에 실제로 무엇을 하고 있는지 감사 (audit)할 수 있도록 오픈 소스 (open source)여야 했습니다. 마지막 포인트는 사람들이 생각하는 것보다 훨씬 중요합니다. LinkedIn 세션에 접근하는 모든 확장 프로그램은 사용자의 메시지, 일촌 목록, 구직 활동에 접근할 수 있습니다. 월 10달러의 가격표가 붙은 폐쇄형 소스 (closed-source) 도구에 이를 넘겨주는 것은 진정으로 좋지 않은 생각입니다. 무료 오픈 소스 LinkedIn AI 스팸 제거 확장 프로그램 분야에는 몇 가지 실제적인 선택지가 있으며, 이들의 차이점은 패턴 매칭 (pattern-matching)을 얼마나 공격적으로 수행하는지와 필터를 직접 조정할 수 있는지 여부로 갈립니다.
실제로 효과가 있는 방식은 AI 생성 게시물의 구조적 특징 — 과도한 줄 바꿈, 단일 문장 문단, 극적인 문장 뒤에 바로 이어지는 강제 줄 바꿈 (hard return) 등 — 을 잡아내는 키워드 + 패턴 기반 필터링 (keyword + pattern-based filtering)입니다. 좋은 확장 프로그램은 단순히 계정 단위가 아니라 콘텐츠 패턴에 따라 차단할 수 있게 해줍니다. 저는 적절한 확장 프로그램을 찾기 전에 작은 유저스크립트 (userscript)를 직접 작성했는데, 다음과 같은 단순한 정규 표현식 (regex)만으로도 민망할 정도로 많은 노이즈를 잡아낼 수 있었습니다:
// 모든 "문단"이 단일 문장인 게시물을 숨깁니다 — 전형적인 AI 포맷팅
document.querySelectorAll('.feed-shared-update-v2').forEach(post => {
const text = post.innerText;
...
완벽하지는 않지만, 즉각적으로 저를 짜증 나게 했던 콘텐츠의 약 60%를 숨겨주었습니다. 더 정교한 오픈 소스 확장 프로그램(Open source extensions)들은 더 나아갑니다. 이들은 "참여 유도형 게시물(engagement bait)", "채용 공고 스팸(job announcement spam)", 또는 "동기 부여 콘텐츠(motivational content)"와 같은 카테고리를 정의하고, 카테고리별로 서로 다른 동작(숨기기, 흐리게 처리, 접기)을 적용할 수 있게 해줍니다. 만약 팀의 나머지 툴 스택(tooling stack)도 단순화하려고 노력 중이라면, 2026년 소기업을 위한 필수 SaaS 도구(Essential SaaS Tools for Small Business in 2026) 가이드에서 실제로 비용을 지불할 가치가 있는 것과 충분히 쓸만한 무료 티어(free tier)를 가진 것을 구분해 놓은 유용한 분석 내용을 참고해 보세요. 동일한 필터링 사고방식이 적용됩니다. 기본적으로 무료 및 오픈 소스를 선택하고, 실제 한계에 부딪혔을 때만 비용을 지불하십시오.
제가 예상하지 못했던 주의 사항(gotcha)은 다음과 같습니다: LinkedIn은 DOM 클래스 이름을 활발하게 변경합니다. .feed-shared-update-v2와 같이 선택자(selector)를 하드코딩한 확장 프로그램은 LinkedIn이 프론트엔드 업데이트를 배포할 때마다 몇 주 간격으로 작동이 중단됩니다. 더 잘 관리되는 오픈 소스 프로젝트들은 여러 개의 폴백 선택자(fallback selectors)를 사용하며, 기여자(contributors)들이 수정 사항을 빠르게 제출합니다. 무엇인가를 설치하기 전에 커밋 히스토리(commit history)를 확인하십시오. 마지막 커밋이 8개월 전인 저장소(repo)는 아마 이미 작동하지 않을 것입니다. 최근 활동이 있고, 선택자 오류가 보고되면 며칠 내에 수정되는 공개 이슈 트래커(issue tracker)가 있으며, 이상적으로는 소스 코드를 건드리지 않고도 편집할 수 있는 설정 파일(config file)이 있는 프로젝트를 찾으십시오.
이 확장 프로그램이 실제로 하는 일 (마케팅 용어 제외)
이 확장 프로그램을 발견했을 때 제가 가장 먼저 확인한 것은 네트워크 탭(network tab)이었습니다. LinkedIn을 탐색하는 동안 외부로 나가는 요청(outbound requests)이 전혀 없었습니다. 분류기(classifier)는 전적으로 클라이언트 측(client-side)에서 실행됩니다. 즉, 확장 프로그램 번들 자체에 내장된 학습된 모델을 사용하며, 외부로 데이터를 보내거나(phone-home), 토큰(token)이나 계정(account)을 요구하지 않습니다. 바로 그 점이 제가 실제로 설치를 결심하게 만든 요인이었습니다.
이 필터는 세 가지 뚜렷한 콘텐츠 패턴을 타겟팅합니다. 첫째는 스팸 시그니처(Spam signatures)로, "방금 40개 회사에서 탈락했습니다. 여기서 배운 점은 다음과 같습니다 🧵"와 같은 형식입니다. 둘째는 AI 생성 텍스트 마커(AI-generated text markers)로, GPT-4와 같은 모델들이 생성하는 경향이 있는 특정 구절 수준의 엔트로피(entropy) 패턴을 의미합니다. 예를 들어 지나치게 균형 잡힌 문장 길이, "X에 관한 것이 아니라 Y에 관한 것입니다"와 같은 완곡한 표현(hedging constructions), 그리고 특유의 가짜 취약성(fake vulnerability) 표현 등이 이에 해당합니다. 마지막으로 참여 유도(engagement bait)로, 게시물 끝에 붙는 "동의한다면 YES라고 댓글을 남겨주세요" 또는 "나중에 보려면 저장하세요"와 같은 명시적인 행동 유도(call-to-action) 문구입니다. 이 요소들은 독립적으로 확인되므로, 게시물이 다른 카테고리를 트리거하지 않고도 하나의 카테고리에만 걸릴 수 있습니다.
내부적으로는 LinkedIn 피드에 document_idle 시점에 주입되는 표준 콘텐츠 스크립트(content script)로 작동합니다. 스크립트는 DOM 변이(DOM mutations)를 감시하며 — LinkedIn은 스크롤할 때 게시물을 지연 로딩(lazy-loads)합니다 — 각 새로운 .feed-shared-update-v2 노드를 분류기(classifier)를 통해 실행합니다. 일치하는 게시물은 설정에 따라 완전히 숨겨지거나 토글(toggle) 뒤로 접힙니다. 페이지 새로고침이 필요 없으며, 피드가 채워짐에 따라 인라인(inline)으로 즉시 작동합니다.
// MutationObserver가 수행하는 작업의 단순화된 버전
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
...
이 프로젝트는 오픈 소스(MIT 라이선스, GitHub에 호스팅됨)이기 때문에, 사용자가 직접 classifier.js 파일을 감사(audit)하여 어떤 신호(signals)를 점수화하고 있는지 정확히 확인할 수 있습니다. API 키를 숨기기 위한 난독화된 번들(obfuscated bundle)도 없습니다. 모델 가중치(model weights)는 확장 프로그램에 포함된 약 180KB 크기의 JSON 파일이며, 이것이
솔직한 단점은 이렇습니다. 오탐 (False Positives)은 실제로 발생하며, 특정한 방식으로 매우 짜증스럽습니다. 구조화된 글머리 기호 (bullet points)를 사용하는 기술적인 포스트는 AI 생성 콘텐츠 점수가 더 높게 나오는데, 이는 포맷팅 패턴이 겹치기 때문입니다. 예를 들어, "이 Kafka 컨슈머 지연 (consumer lag)을 디버깅하며 배운 세 가지"와 같이 명확하게 작성된 시니어 엔지니어의 글은 단순한 분류기 (classifier) 입장에서 볼 때 GPT의 출력물과 의심스러울 정도로 유사해 보입니다. 저는 실제로 팔로우하는 몇몇 사람들의 글쓰기 스타일이 차단되는 바람에 직접 화이트리스트 (whitelist)에 추가해야 했습니다. 확장 프로그램은 바로 이러한 이유로 작성자별 허용 목록 (allowlist) 기능을 제공하지만, 이를 수동으로 관리해야 합니다. 만약 당신의 피드에 명확하고 간결하게 글을 쓰는 엔지니어들이 많다면, 처음 며칠 동안은 설정을 조정하는 데 시간을 써야 할 것입니다.
설치 방법: 실제 단계
사람들이 가장 혼란스러워하는 부분은 Chrome 웹 스토어(Chrome Web Store)나 Firefox 부가 기능(Firefox Add-ons)에 패키지 형태의 릴리스가 있을 것이라고 가정하는 점입니다. 없습니다. 적어도 이러한 대부분의 오픈 소스 LinkedIn 스팸 제거 도구들은 그렇습니다. 여러분은 이를 '압축 해제된 상태(unpacked)'로 로드하게 되는데, 이는 몇 가지 추가 단계가 필요함을 의미하지만, 동시에 세션 쿠키 (session cookies)를 맡기기 전에 코드가 실제로 무엇을 하는지 직접 읽어볼 수 있다는 뜻이기도 합니다.
먼저 저장소 (repo)를 클론 (cloning)하고, 다른 무엇을 하기 전에 반드시 README를 읽으십시오. 진심입니다. 빌드 (build) 단계는 릴리스마다 달라집니다. 어떤 버전은 직접 로드할 수 있는 사전 빌드된 /dist 폴더를 포함하여 배포하지만, 어떤 버전은 빌드 명령어를 먼저 실행할 것을 요구합니다:
git clone https://github.com/[repo-name]/linkedin-spam-filter
cd linkedin-spam-filter
npm install # 또는 yarn, README 확인
...
만약 빌드 단계를 건너뛰고 원본 소스 폴더를 그대로 로드하면, 에셋 (assets)이 누락되고 유용한 에러 메시지도 없는 고장 난 확장 프로그램을 마주하게 될 것입니다. 빌드를 실행하기 전에 저장소에 /dist 또는 /build 폴더가 이미 존재하는지 확인하십시오. 어떤 유지 관리자(maintainer)는 컴파일된 결과물을 커밋(commit)하기도 하고, 어떤 이들은 그렇지 않기 때문입니다.
Chrome의 경우 경로는 간단합니다: chrome://extensions로 이동하여 우측 상단의 개발자 모드 (Developer Mode) 토글을 켠 다음, **압축해제된 확장 프로그램 로드 (Load Unpacked)**를 클릭하고 프로젝트 루트가 아닌 해당 /dist 또는 /build 폴더를 지정하십시오. 만약 _"Manifest version 2 is deprecated"_라는 경고가 보이더라도 당황하지 마십시오. Chrome은 버전 110 이상부터 MV2 확장 프로그램에 대해 이 경고를 표시하기 시작했지만, 확장 프로그램은 여전히 잘 작동합니다. 이것은 경고일 뿐 에러가 아닙니다. 무언가 잘못된 것이 아닙니다.
Firefox는 임시 애드온 (add-ons)이 브라우저 재시작 시 유지되지 않기 때문에 약간 더 번거롭습니다. about:debugging → This Firefox → **임시 애드온 로드 (Load Temporary Add-on)**로 이동한 다음, 폴더가 아닌 manifest.json 파일을 직접 선택하십시오. Firefox를 재시작할 때마다 매번 다시 로드해야 합니다. 만약 Firefox에서 영구적으로 유지하고 싶다면 web-ext를 통해 직접 서명(signing)하는 방법을 찾아보십시오:
npm install -g web-ext
web-ext run --source-dir ./dist # 테스트 실행, 변경 시 자동 재로드
로드한 후에는 즉시 확장 프로그램 아이콘을 고정(pin)하십시오. Chrome에서는 퍼즐 조각 메뉴를 우클릭하여 → 확장 프로그램을 찾은 뒤 → 고정 아이콘을 클릭하면 됩니다. Firefox도 확장 프로그램 도구 모음을 통해 동일한 방식으로 진행하면 됩니다. 아이콘을 눈에 보이게 해두어야 하는 이유는, 필터에 걸린 게시물을 실제로 읽고 싶을 때 필터를 일시 중지하는 토글 기능이 매번 메뉴를 뒤지는 것보다 클릭 한 번으로 하는 것이 훨씬 쉽기 때문입니다.
소스 코드로부터 빌드하기 (패키지 버전이 오래된 경우)
Chrome 웹 스토어에 올라온 패키지 릴리스는 실제 저장소(repo)보다 몇 주 뒤처질 수 있습니다. 특히 스팸 탐지 모델이 빈번하게 업데이트되는 활발히 개발 중인 확장 프로그램의 경우 더욱 그렇습니다. 저도 이전에 이런 경험이 있습니다. 스토어 버전에는 메인 브랜치(main branch)에서 이미 수정된 채용 담당자 메시지 카테고리 전체에 대한 필터가 누락되어 있었습니다. 소스 코드로부터 빌드하는 데는 약 5분 정도가 더 소요되지만, 현재 main 브랜치에 있는 최신 상태를 그대로 사용할 수 있습니다.
이를 위해서는 Node.js 18 이상이 필요합니다. 16 버전도, 14 버전도 안 됩니다. 빌드 툴체인 (build toolchain)이 이전 Node 버전에서 제대로 처리하지 못하는 ES 모듈 (ES module) 구문과 fetch API를 사용하기 때문입니다. 먼저 본인의 버전을 확인하십시오:
# 버전을 확인하십시오 — 18.0.0 이상이어야 합니다
node --version
...
가장 흔한 실패 지점은 webpack 의존성(dependency)이 누락된 경우입니다. 만약 Cannot find module 'webpack'이라는 메시지가 보인다면, 이는 저장소의 package.json에 webpack이 devDependencies로 나열되어 있지만 누군가 제대로 포함하는 것을 잊었거나, npm install 단계에서 가져오지 못했음을 의미합니다. 다음과 같이 직접 수정하십시오:
# webpack과 CLI를 별도로 설치하십시오 — 둘 다 필요합니다
npm install --save-dev webpack webpack-cli
...
무엇이든 실행하기 전에 package.json을 열고 scripts 섹션을 확인하십시오. 일부 저장소는 압축되지 않은 코드를 출력하는 watch 모드용으로 npm run dev를 사용하고, 프로덕션 번들(production bundle)용으로 npm run build를 사용합니다. 그리고 이러한 출력물들은 Chrome에서 다르게 동작할 수 있습니다. 개발용(dev) 출력물은 때때로 콘텐츠 스크립트(content script) 최적화를 건너뛰는데, 이는 긴 LinkedIn 피드에서 스팸 필터가 더 느리게 작동함을 의미합니다. 실제로 설치할 때는 항상 npm run build를 사용하십시오.
// package.json에서 찾아야 할 내용
"scripts": {
"dev": "webpack --mode development --watch", // 핫 리로드(hot reload), 더 큰 파일 크기, 더 느린 속도
...
빌드에 성공하면, Chrome이 주목하는 것은 /dist 폴더입니다. chrome://extensions로 이동하여 개발자 모드(Developer Mode)를 활성화하고, "압축해제된 확장 프로그램을 로드합니다(Load unpacked)"를 클릭한 뒤, 저장소 루트(repo root)가 아닌 해당 /dist 디렉토리를 지정하십시오. 루트를 지정하는 것은 manifest 파일이 누락되었다는 혼란스러운 오류를 일으키는 전형적인 실수입니다. 실제 manifest.json은 빌드 단계 중에 /dist로 생성되며, 저장소 루트에는 존재하지 않습니다.
필터 설정하기 — 설정이 실제로 하는 역할
민감도 슬라이더(sensitivity slider)는 기본값으로 두었을 때 가장 큰 문제가 될 수 있는 설정입니다. 대부분의 설치 버전은 중간 정도로 설정되어 배포되는데, 이는 합리적으로 들릴 수 있지만 이 확장 프로그램이 정의하는 "중간"이 여전히 상당히 공격적이라는 사실을 깨닫기 전까지만 그렇습니다. 저는 낮은(low) 단계에서 시작하여 하루 동안 LinkedIn을 사용해 본 뒤, 조금씩 높여가는 것을 권장합니다. 낮은 단계에서는 명백한 것들, 즉 "새로운 역할을 맡게 되어 기쁩니다"라는 내용의 캐러셀(carousel) 포스트나 "실패를 통해 배운 10가지 교훈"과 같은 10개의 불렛 포인트(bullet point)로 구성된 에세이들만 잡아냅니다. 이를 높음(high)으로 올리면, 모델이 실제 게시물에서도 우연히 나타날 수 있는 어조 패턴(tone patterns)을 포착하기 때문에 당신이 관심을 가진 실제 사람들의 게시물까지 놓치기 시작할 것입니다.
키워드 차단 목록(keyword blocklist)은 이 도구가 제값을 하는 핵심적인 부분입니다. 기본 설정도 괜찮지만, 제가 물리적으로 앱을 종료하게 만드는 문구들은 어떤 기본 목록에도 포함되어 있지 않습니다. 저는 첫날에 다음과 같은 것들을 추가했습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기