TRENDSHIFT #15 딥다이브 · 2026-06-02

fredy 딥다이브
— 독일에서 집 구하기를 "봇"에게 맡기는 셀프호스팅 부동산 매물 추적기

독일에서 전월세 집을 구하는 건 악명 높게 힘듭니다. 좋은 매물은 올라온 지 몇 분 만에 사라지죠. Fredy(Find Real Estate Damn Easy)는 ImmoScout24·Immowelt·Immonet·eBay Kleinanzeigen·WG-Gesucht 같은 독일 부동산 사이트를 몇 분 간격으로 자동으로 긁어와, 새 매물이 뜨면 즉시 Slack·Telegram·이메일·Discord·ntfy로 알려줍니다. 직접 깔아 내 서버에서 돌리는(self-hosted) 오픈소스라 광고·구독료가 없고, 깔끔한 웹 UI로 검색을 관리합니다. (저장소: orangecoding/fredy · Node.js + React · 버전 22.1.1 · TrendShift 15위 · Apache 2.0 라이선스)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

한 문장으로: "여러 부동산 사이트를 대신 새로고침하며 지켜보다가, 새 매물이 뜨는 순간 나에게 알림을 쏴주는 개인용 자동 비서."

독일의 임대 시장은 수요가 공급을 한참 넘습니다. 인기 지역의 괜찮은 집은 포털에 올라오자마자 문의가 수십 통씩 몰리고, 늦게 본 사람은 연락조차 못 합니다. 그래서 사람들은 ImmoScout24 같은 사이트를 하루에도 수십 번 새로고침합니다. Fredy는 그 새로고침 노동을 기계에게 떠넘기는 도구입니다.

영웅 비유

여러 중고장터 앱을 24시간 대신 들여다봐 주는 "알바생"

당신이 "베를린, 방 2개, 월세 1200유로 이하" 조건을 한 번 알려주면, Fredy라는 알바생이 ImmoScout·Immowelt·당근마켓(독일판 eBay Kleinanzeigen) 등을 5분마다 돌며 새 글을 확인합니다. 조건에 맞는 새 매물을 발견하면 그 즉시 당신의 카카오톡(Slack·Telegram)으로 "이거 떴어요!" 하고 링크를 보냅니다. 한 번 본 매물은 다시 안 알려주고, 같은 집이 여러 사이트에 중복으로 올라와도 한 번만 알려주죠.

핵심은 내 서버에서 직접 돌린다(self-hosted)는 점입니다. 남의 유료 알림 서비스에 가입하는 게 아니라, Docker로 명령어 한 줄이면 내 PC나 NAS에 Fredy가 깔립니다. 검색 조건·알림 채널은 브라우저로 열리는 웹 UI에서 클릭 몇 번으로 설정합니다. 데이터는 전부 내 손 안에 있고, 비용은 0원입니다.

용어
셀프호스팅 (Self-hosted)
남이 운영하는 클라우드 서비스에 맡기는 대신, 소프트웨어를 내 컴퓨터·서버·NAS에 직접 설치해 돌리는 것. 구독료가 없고 데이터가 외부로 안 나간다는 장점, 대신 설치·관리는 내 몫이라는 단점이 있습니다. 음악을 스트리밍으로 듣는 대신 내 하드에 mp3를 모아두는 것과 비슷합니다.
용어
스크래핑 (Scraping)
웹사이트가 공식 데이터 통로(API)를 안 열어줄 때, 사람이 브라우저로 보는 화면(HTML)을 프로그램이 대신 읽어 필요한 정보를 뽑아내는 기술. Fredy는 부동산 포털의 검색 결과 페이지를 긁어 "제목·가격·면적·링크"를 추출합니다.

2왜 주목받는가

비슷한 매물 알림 봇은 많은데, Fredy가 트렌딩에 오른 이유 — 전방위·무료·봇 탐지 회피, 그리고 LLM 연결.

Fredy 자체는 오래된 프로젝트지만, 2026년에 다시 떠오른 데에는 몇 가지 분명한 이유가 있습니다. 첫째, 독일·유럽의 주거난이 심해지면서 "매물 알림 자동화" 수요가 커졌고, 둘째로 최근 버전(V20+)에서 MCP 서버를 내장해 Claude·ChatGPT 같은 LLM과 연결되는 길을 열었기 때문입니다. 셋째, 부동산 포털들이 봇 차단을 강화하자 Fredy가 봇 탐지 회피 기술로 정면 대응한 점이 개발자들의 흥미를 끌었습니다.

비슷한 도구·접근과 비교하면 Fredy의 위치가 또렷해집니다.

방식커버 범위비용 / 내 데이터
Fredy독일 5대 포털 + 추가 (스크래핑·역설계 API)무료 / 내 서버에 보관
포털 자체 알림그 포털 1곳만무료지만 여러 곳을 따로 관리
유료 매물 알림 서비스업체가 정한 범위구독료 / 업체 서버에 보관
직접 새로고침내 인내심만큼무료지만 사람 노동·놓침 발생
흔한 함정
"그냥 웹페이지를 긁으면 되는 거 아냐?"

요즘 대형 포털(특히 ImmoScout24)은 봇 탐지 시스템을 깔아둡니다. 평범한 스크래퍼로 접근하면 "당신은 봇입니다 :-/" 화면이 뜨고 차단당하죠. 단순 fetch나 기본 헤드리스 브라우저로는 몇 번 긁다가 막힙니다.

Fredy의 해결
위장 브라우저 + 역설계 모바일 API + 주거용 프록시

브라우저 기반 포털은 CloakBrowser(지문을 진짜 크롬과 구별 불가능하게 만든 하드닝 헤드리스 브라우저)로 긁고, ImmoScout24는 아예 그들의 모바일 앱 API를 역설계해 우회합니다. 서버(데이터센터 IP)에서 막힐 땐 "주거용 프록시(residential proxy)"를 꽂아 일반 가정집 인터넷처럼 보이게 합니다. 봇 탐지와의 싸움을 정면으로 다룬다는 게 차별점입니다.

마지막 한 방은 설치 장벽이 낮다는 점입니다. docker run 한 줄이면 9998 포트에 웹 UI가 뜨고, Unraid(가정용 NAS OS) 커뮤니티 스토어에서도 클릭 설치됩니다. 거기에 JetBrains의 오픈소스 후원까지 받는 꾸준히 관리되는 프로젝트라는 신뢰가 더해집니다.

3기술 스택 전체 지도

"Node.js 백엔드 + React 웹 UI + 헤드리스 브라우저 스크래퍼"가 한 저장소에 들어있는 모노리식 풀스택.

Fredy는 거창한 마이크로서비스가 아니라, 하나의 Node.js 프로세스가 백엔드·스케줄러·스크래퍼를 모두 돌리고, 별도의 React 앱이 그 위에 웹 UI를 얹는 구조입니다. 무겁지 않게 "개인이 자기 서버에 올려 돌리는" 목적에 딱 맞춘 설계입니다.

백엔드 (Node.js 22+)

레이어기술 / 버전역할
웹 서버Fastify ^5.8API·웹 UI 서빙. Express보다 빠른 경량 프레임워크
데이터베이스better-sqlite3 ^12매물·검색·설정 저장. 파일 한 개로 끝나는 내장 DB
스케줄러node-cron ^4.2"5분마다 검색 실행" 같은 주기 작업 타이머
스크래핑puppeteer-core + cloakbrowser + cheerio헤드리스 브라우저로 페이지 열고 HTML 파싱
세션·보안@fastify/session·cookie·helmet로그인 세션, 보안 헤더
LLM 연동@modelcontextprotocol/sdk ^1.29MCP 서버 — Claude 등이 매물 데이터를 도구로 조회

알림 채널 (Notification Adapters)

Fredy의 강점 중 하나는 알림 채널이 15종이라는 것. 각각이 독립된 어댑터 파일로, 메시지 SDK를 끌어옵니다.

분류채널 / 패키지
채팅Slack(slack·webhook)·Telegram·Discord·Mattermost
이메일SendGrid·Mailjet·Nodemailer(SMTP)·Resend
푸시·기타ntfy·Pushover·Apprise·HTTP(웹훅)·Console·SQLite

프론트엔드 (웹 UI)

레이어기술 / 버전역할
UI 프레임워크React 19.2 + react-router 7검색·매물·설정 화면
컴포넌트 키트@douyinfe/semi-ui 2.99ByteDance(틱톡)의 디자인 시스템. 버튼·폼·테이블
상태 관리zustand ^5가볍고 단순한 전역 상태 저장소
지도maplibre-gl + mapbox-gl-draw + @turf지도에서 다각형을 그려 "이 동네 안만" 필터
차트·빌드chart.js + Vite 8통계 시각화 / 초고속 번들러
용어
헤드리스 브라우저 (Headless Browser)
화면(GUI) 없이 메모리에서만 돌아가는 크롬·파이어폭스. 사람이 클릭·스크롤하는 것과 똑같이 페이지를 열고 자바스크립트를 실행하지만, 모니터엔 안 보입니다. 자바스크립트로 그려지는 현대 웹사이트를 긁으려면 단순 fetch가 아니라 이게 필요합니다. puppeteer-core가 이 브라우저를 코드로 조종하는 리모컨입니다.

인프라

Dockerfiledocker-compose.yml을 제공하고, 공식 이미지는 ghcr.io/orangecoding/fredy(GitHub Container Registry)로 배포됩니다. 설정은 /conf, 데이터베이스는 /db 볼륨에 보관해, 컨테이너를 지워도 검색·매물 기록이 살아남습니다. 9998 포트 하나만 열면 웹 UI에 접속됩니다.

4아키텍처 심화 분석

먼저 전체 그림(Job·Provider·Adapter 삼각형)을 본 뒤, 매물 하나가 알림이 되기까지의 파이프라인을 단계별로 추적합니다.

먼저 핵심 3개념 — Job · Provider · Adapter

Fredy를 이해하는 열쇠는 딱 세 단어입니다. 이 셋의 조합이 Fredy의 전부라고 해도 됩니다.

개념무엇인가
Provider(공급자)매물을 가져오는 출처 사이트. ImmoScout24·Immowelt·Kleinanzeigen 등. 포털의 검색 URL(날짜순 정렬!)을 붙여넣어 등록
Adapter(어댑터)새 매물을 알려주는 통로. Slack·Telegram·이메일 등. 여러 개를 동시에 쓸 수 있음
Job(작업)Provider들 + Adapter들을 묶은 한 묶음. "ImmoScout+Immowelt에서 찾아 Slack+Telegram으로 보내라"를 정해진 간격마다 자동 실행

전체 그림 한 장

여러 Job이 타이머에 따라 깨어나면, 각 Job은 자기가 가진 Provider들을 긁고, 중복을 걸러낸 뒤, 살아남은 새 매물만 Adapter들로 흘려보냅니다.

┌─ Job 1 ─┐ ┌─ Job 2 ─┐ ┌─ Job 3 ─┐ (node-cron 타이머가 깨움) │ 조건+ │ │ 조건+ │ │ 조건+ │ │ 채널 │ │ 채널 │ │ 채널 │ └────┬────┘ └────┬────┘ └────┬────┘ └───────────┼───────────┘ ▼ ┌───────────────────────────────┐ │ FredyPipelineExecutioner │ ← 매물 한 줄기의 13단계 컨베이어 └───────────────────────────────┘ ▼ (Provider별로 실행) ┌──────────┐ ┌──────────┐ ┌──────────┐ │ImmoScout │ │Immowelt │ │Kleinanz. │ ← 13개 Provider │(모바일API)│ │(CloakBr.)│ │(CloakBr.)│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └─────────────┼─────────────┘ ▼ 유사도 체크 (중복 매물 제거) ▼ 새 매물인가? ── 아니오 → 버림 │ 예 ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Slack │ │ Telegram │ │ Email … │ ← 15개 Adapter └──────────┘ └──────────┘ └──────────┘
용어
크론 / node-cron (Cron)
"매시 0분에" "5분마다" 같은 주기적 실행을 예약하는 타이머. 알람시계를 여러 개 맞춰두는 것과 같습니다. Fredy는 각 Job의 검색 간격을 크론으로 등록해, 시간이 되면 파이프라인을 깨웁니다.

매물 하나를 추적 — "파이프라인 13단계"

Fredy의 심장은 FredyPipelineExecutioner.js입니다. 매물은 이 컨베이어 벨트를 한 단계씩 통과하며 걸러지고 다듬어집니다. 코드의 execute()는 아래 단계를 .then()으로 줄세운 약속(Promise) 체인입니다.

① URL 정리 sortByDate 파라미터를 붙여 "최신순"으로 강제 ② 추출 Extractor가 CloakBrowser로 페이지를 열어 HTML 수집 ③ 정규화 포털마다 다른 모양을 {제목·가격·면적·링크} 표준형으로 ④ 1차 필터 필수 필드 없는 것·블랙리스트 단어 제거 ⑤ 새것 판별 저장된 해시와 대조 → 처음 보는 매물만 통과 중복 방지의 1차 관문 ⑥ 상세 보강 (옵션) 상세 페이지를 한 번 더 열어 정보 추가 ⑦ 지오코딩 주소 → 위도·경도 좌표로 변환 ⑧ 저장 새 매물을 SQLite에 기록 ⑨ 거리 계산 내 집(home) 좌표에서 몇 m 떨어졌나 ⑩ 유사도 필터 제목·주소·가격이 닮은 매물 제거(포털 간 중복) 중복 방지의 2차 관문 ⑪ 스펙 필터 방 수·면적·최대가격 조건 안 맞으면 버림 ⑫ 지역 필터 지도에 그린 다각형(polygon) 밖이면 버림 ⑬ 알림 발송 살아남은 매물을 모든 Adapter로 전송
용어
지오코딩 (Geocoding)
"베를린 미테구 OO거리 12번지" 같은 사람이 쓰는 주소를 위도·경도 숫자 좌표로 바꾸는 것. 좌표가 있어야 "내 직장에서 반경 3km" "지도에 그린 동네 안" 같은 위치 필터를 계산할 수 있습니다.
용어
유사도 체크 / 다각형 필터 (Similarity · Polygon)
유사도 체크는 제목·주소·가격이 거의 같은 매물을 "같은 집"으로 보고 한 번만 알리는 장치(같은 집이 ImmoScout과 Immowelt에 동시에 올라와도 중복 알림 방지). 다각형 필터는 지도 위에 손으로 그린 영역(다각형) 안에 매물 좌표가 들어오는지를 @turf 라이브러리로 계산해, 원하는 동네만 남깁니다.

이 레포에서 "정상"이 뭔지 — 설계 관례

코드를 열어보면 몇 가지 일관된 약속이 보입니다. 첫째, 각 Provider는 "데이터를 어떻게 긁고 정규화하나"만 책임지고, 흐름·순서·필터링은 전부 파이프라인이 담당합니다(관심사 분리). 그래서 새 포털을 추가할 때 lib/provider/에 파일 하나만 더하면 됩니다. 둘째, "새 매물 없음"을 에러가 아니라 신호로 취급합니다 — NoNewListingsWarning을 일부러 던져 파이프라인을 조용히 멈추죠. 셋째, 알림 어댑터도 똑같이 lib/notification/adapter/에 파일 하나 = 채널 하나로 격리돼, 새 알림 채널을 붙이기 쉽습니다.

5디렉토리 구조 해부

"백엔드 lib/ + 프론트엔드 ui/ + 인프라"가 한 저장소에 — 어디에 뭐가 있는지.
lib/ # ← 백엔드 본체 (Node.js) ├── FredyPipelineExecutioner.js # ★ 매물 한 줄기의 13단계 파이프라인 ├── provider/ # ★ 포털별 스크래퍼 (13개) │ ├── immoscout.js # ImmoScout24 (역설계 모바일 API) │ ├── immowelt.js # Immowelt (CloakBrowser) │ ├── kleinanzeigen.js # eBay Kleinanzeigen │ ├── wgGesucht.js # WG-Gesucht (셰어하우스) │ └── … (immonet·sparkasse·mcMakler 등) ├── notification/ # ★ 알림 채널 (15개 어댑터) │ ├── notify.js # 발송 오케스트레이션 │ └── adapter/ # slack·telegram·discord·smtp·ntfy… ├── services/ # 보조 서비스 모음 │ ├── extractor/ # CloakBrowser로 HTML 추출 │ ├── similarity-check/ # 중복 매물 판정 캐시 │ ├── geocoding/ # 주소→좌표 변환 │ ├── storage/ # SQLite 접근 + 마이그레이션 │ ├── crons/ # 주기 실행 스케줄러 │ └── jobs/ · listings/ · tracking/ ├── mcp/ # ★ LLM 연동 (V20+) — Claude 등이 조회 │ ├── mcpAdapter.js · stdio.js · mcpHttpRoute.js ├── api/ # REST 라우트 (job·provider·adapter…) └── utils/ · types/ · errors.js ui/src/ # ← 프론트엔드 (React 19 + Semi UI) conf/config.json # ← 설정 (sqlitepath 등) db/ # ← SQLite 데이터 파일 Dockerfile · docker-compose.yml # ← 인프라 reverse-engineered-immoscout.md # ← ImmoScout API 역설계 문서 test/ # ← online/offline 테스트 + 픽스처
위치역할
lib/provider/이 프로젝트의 입구. 각 파일이 한 포털의 "긁기+정규화" 규칙
lib/FredyPipelineExecutioner.js모든 Provider 결과를 13단계로 거르는 컨베이어
lib/notification/adapter/이 프로젝트의 출구. 각 파일이 한 알림 채널
lib/services/extractor/봇 탐지를 뚫는 CloakBrowser 추출 엔진
lib/services/storage/migrations/DB 스키마 버전 관리(17개+ 마이그레이션)
lib/mcp/LLM이 매물 데이터를 자연어로 묻게 하는 다리

6학습 포인트 (기술별)

이 레포 한 채에서 실전 기술을 골라 배웁니다 — 각각 "뭘 배우나 + 어떻게 해보나".
학습 1 · 플러그인 아키텍처

"파일 하나 = 기능 하나"로 확장 쉬운 구조 짜기

Fredy는 Provider도 Adapter도 폴더에 파일을 하나 더 넣으면 끝나도록 설계됐습니다. 핵심 파이프라인은 "어떤 Provider인지" 신경 쓰지 않고 정해진 인터페이스(normalize·filter·crawlFields 등)만 호출하죠. 새 포털·새 알림 채널을 추가할 때 기존 코드를 안 건드린다는 게 핵심입니다. 이게 "개방-폐쇄 원칙(OCP)"의 실전 예시입니다.

📖 한 줄 풀이: lib/provider/immowelt.js 하나를 열어보면, 한 포털을 Fredy에 가르치는 데 필요한 최소 규칙이 무엇인지 한눈에 보입니다.

학습 2 · Promise 체인 파이프라인

복잡한 다단계 처리를 .then()으로 줄세우기

13단계나 되는 처리를 거대한 함수 하나에 욱여넣으면 읽을 수 없습니다. Fredy는 각 단계를 작은 메서드로 쪼개고 .then()으로 연결해, 코드만 봐도 처리 순서가 그대로 읽히게 했습니다.

// 💡 하는 일: 매물을 13단계 컨베이어에 한 번 태운다
return Promise.resolve(urlModifier(url, sortByDate))
  .then(this._getListings)        // ② 추출
  .then(this._normalize)          // ③ 정규화
  .then(this._filter)             // ④ 1차 필터
  .then(this._findNew)            // ⑤ 새것만
  .then(this._filterBySimilarListings)  // ⑩ 중복 제거
  .then(this._notify)             // ⑬ 알림
  .catch(this._handleError);      // 에러는 한 곳에서

안심하세요 — 각 _단계는 입력을 받아 다음으로 넘길 값을 돌려주는 작은 함수일 뿐입니다. "데이터가 파이프를 따라 흐른다"는 그림이 코드에 그대로 박혀 있습니다.

학습 3 · 봇 탐지와의 싸움

왜 단순 스크래핑은 막히고, 어떻게 우회하나

실전 스크래핑의 8할은 "안 막히기"입니다. Fredy는 세 가지 무기를 씁니다 — ① CloakBrowser로 브라우저 지문을 진짜 크롬처럼 위장, ② ImmoScout24는 웹 대신 모바일 앱 API를 역설계해 우회, ③ 서버에서 막힐 땐 주거용 프록시로 가정집 IP인 척. "기술적으로 가능한가"와 "현실에서 안 막히나"는 다른 문제임을 배우는 좋은 교재입니다.

📖 한 줄 풀이: reverse-engineered-immoscout.md 문서가 "공식 API가 없을 때 모바일 앱 트래픽을 분석해 비공식 통로를 찾는" 과정을 보여줍니다.

학습 4 · 지리 데이터 다루기

주소를 좌표로, 좌표를 "이 동네 안인가"로

Fredy는 매물 주소를 지오코딩으로 좌표화하고, 웹 UI에서 지도에 직접 그린 다각형(polygon) 안에 그 좌표가 들어오는지 @turf/boolean-point-in-polygon으로 판정합니다. "점이 다각형 내부인가"는 게임·지도·물류 어디서나 쓰이는 고전 알고리즘이라, 한 번 익혀두면 두고두고 씁니다.

학습 5 · MCP로 앱에 LLM 붙이기

내 데이터를 Claude가 "도구"로 조회하게 만들기

V20+의 MCP 서버는 Fredy가 모은 매물 데이터를 LLM이 자연어로 질의할 수 있게 엽니다. "이번 주 새로 뜬 방 3개짜리 중 역세권인 거 추려줘" 같은 요청을 Claude·ChatGPT가 처리하죠. 자기 앱에 LLM 인터페이스를 붙이는 요즘 표준(MCP)을 실제 프로젝트에서 어떻게 쓰는지 볼 수 있습니다.

7하드웨어 / 시스템 요구사항

개인 PC·라즈베리파이·NAS 어디서나 가볍게 — 단, 헤드리스 브라우저가 도는 만큼의 여유는 필요합니다.
항목요구사항
실행 방식Docker(권장) 또는 Node.js 직접 실행
Node.js22 이상 (직접 실행 시) · 패키지 매니저 yarn
메모리헤드리스 크롬(CloakBrowser)이 도는 만큼 권장 1GB+ RAM
저장공간매물 DB는 SQLite 파일 한 개 — 가벼움(수십 MB)
포트9998 (웹 UI) 하나만 열면 됨
네트워크가정용 인터넷이면 충분 / 서버·VPS면 독일(DE) 주거용 프록시 권장
플랫폼Linux·macOS·Windows·NAS(Unraid 커뮤니티 스토어 지원)
비유

Fredy는 "항상 켜둬야 의미 있는" 도구라, 24시간 도는 작은 컴퓨터에 두는 게 이상적입니다. 매일 끄는 노트북보다 라즈베리파이·홈 NAS·저렴한 VPS가 제격이죠. 다만 클라우드 서버(데이터센터 IP)에 두면 봇으로 의심받기 쉬우니, 그때는 "가정집 인터넷처럼 보이게 하는" 주거용 프록시라는 변장 도구를 하나 더 챙겨야 합니다 — 집에서 돌리면 이 변장이 필요 없습니다.

주의
로컬에선 되는데 서버에선 "봇 탐지" 뜰 때

VPS에서 We have been detected as a bot :-/ 메시지가 뜨면 IP 평판 문제입니다. 데이터센터 IP는 지문을 아무리 완벽히 위장해도 평판만으로 차단됩니다. 해결은 독일 주거용(또는 모바일/4G) 프록시를 설정에 꽂는 것 — 일반 VPN·데이터센터 프록시는 도움이 안 됩니다(같은 나쁜 평판).

8직접 해볼 수 있는 실습 과제

읽기만 하면 안 남습니다. 직접 띄워 검색 하나를 알림까지 받아보며 구조를 몸에 익히세요.
실습 A

Docker로 1분 만에 띄우고 데모 둘러보기 난이도 ★☆☆ 입문

아래 한 줄로 Fredy를 띄우고 http://localhost:9998에 접속해보세요(기본 로그인 admin/admin). 먼저 공개 데모로 UI를 둘러본 뒤 내 것으로 옮기면 길을 잃지 않습니다.

# 💡 하는 일: Fredy 컨테이너를 띄우고 설정·DB 볼륨을 붙인다
docker run -d --name fredy \
  -v fredy_conf:/conf \
  -v fredy_db:/db \
  -p 9998:9998 \
  ghcr.io/orangecoding/fredy:master

안심하세요 — 위 명령은 복사만 하면 동작합니다. docker logs fredy -f로 무슨 일이 벌어지는지 실시간으로 볼 수 있습니다.

실습 B

검색 Job 하나를 끝까지 만들어 알림 받기 난이도 ★☆☆ 입문

포털(예: WG-Gesucht)에서 원하는 조건으로 검색하고 "날짜순 정렬"로 맞춘 뒤 그 URL을 복사하세요. Fredy 웹 UI에서 Provider로 붙여넣고, Adapter로 Telegram이나 ntfy(가장 설정이 쉬움)를 골라 Job을 만들면, 다음 실행 때 새 매물이 알림으로 옵니다. "정렬을 날짜순으로" 안 하면 최신 매물을 놓치니 꼭 확인하세요.

실습 C

지도 다각형으로 "이 동네만" 필터 걸기 난이도 ★★☆ 중급

웹 UI의 지도에서 원하는 동네를 다각형으로 그려 지역 필터를 걸어보세요. 그런 다음 코드에서 lib/FredyPipelineExecutioner.js_filterByArea를 읽어, 매물 좌표가 그 다각형 안인지 @turf로 어떻게 판정하는지 따라가 보면 "UI 조작 → 코드 동작"의 연결이 보입니다.

실습 D

나만의 알림 어댑터 추가하기 난이도 ★★☆ 중급

lib/notification/adapter/ntfy.js 같은 기존 어댑터 하나를 본보기로, 새 채널(예: 카카오워크·자체 웹훅)을 보내는 어댑터 파일을 하나 만들어 등록해보세요. 플러그인 구조 덕에 핵심 코드를 안 건드리고도 기능이 늘어나는 경험을 직접 할 수 있습니다.

실습 E

오프라인 테스트와 픽스처 갱신 돌려보기 난이도 ★★★ 고급

yarn run test:offline은 실제 포털 대신 저장된 픽스처(과거 응답 스냅샷)로 핵심 로직을 빠르게 검증합니다. 포털이 HTML 구조를 바꾸면 스크래퍼가 깨지는데, yarn run test:download-fixtures로 새 픽스처를 받아 테스트를 갱신하는 흐름을 따라가 보면 "스크래퍼 유지보수"의 현실을 배웁니다.

9관련 기술 심화 학습 로드맵

이 레포를 발판으로 "웹 스크래핑 + 풀스택 자동화 봇"으로 뻗어가는 4주 계획.
주차주제학습 자료 / 키워드
1주Node.js 백엔드 기초 + 스케줄링Fastify 라우팅, node-cron, better-sqlite3, 환경변수·세션
2주웹 스크래핑 & 봇 탐지 회피puppeteer-core, cheerio, 헤드리스 지문, 주거용 프록시, robots·법적 경계
3주React 풀스택 UI + 지도React 19, Semi UI, zustand, MapLibre·Turf 다각형 필터
4주에이전트 인프라 & LLM 연동MCP 서버, 알림 어댑터 패턴, Docker 배포, 마이그레이션
비유

이 4주는 "남이 만든 봇을 쓰는 사람"에서 "봇을 만드는 사람"으로 넘어가는 다리입니다. Fredy는 그 모든 조각(긁기·거르기·저장·알림·UI)이 한 저장소에 모여 있어, 큰 그림을 한 번에 볼 수 있는 흔치 않은 실전 교재입니다.

10핵심 키워드 사전

이 글과 README에 나온 주요 용어를 한자리에.
용어의미
Provider매물을 가져오는 출처 사이트(ImmoScout24·Immowelt 등). lib/provider/의 파일 하나 = 포털 하나
Adapter새 매물을 알리는 통로(Slack·Telegram·이메일 등). lib/notification/adapter/의 파일 하나 = 채널 하나
JobProvider들 + Adapter들을 묶어 정해진 간격마다 실행하는 단위
FredyPipelineExecutioner매물을 추출→정규화→필터→중복제거→알림까지 13단계로 처리하는 핵심 컨베이어
CloakBrowser브라우저 지문을 진짜 크롬과 구별 불가능하게 위장한 하드닝 헤드리스 브라우저(npm 패키지)
주거용 프록시 (Residential Proxy)실제 가정집 인터넷을 거쳐 요청을 보내, 데이터센터 IP 차단을 우회하는 유료 서비스
지오코딩 (Geocoding)주소를 위도·경도 좌표로 변환 — 거리·지역 필터의 전제
유사도 체크 (Similarity)제목·주소·가격이 닮은 매물을 같은 집으로 보고 중복 알림을 막는 캐시
다각형 필터 (Polygon / Turf)지도에 그린 영역 안에 매물 좌표가 있는지 판정해 동네를 한정
MCP (Model Context Protocol)LLM이 외부 기능·데이터를 "도구"로 부르는 표준. Fredy V20+가 매물 조회 도구를 제공
Semi UIByteDance(틱톡)가 만든 React 컴포넌트 디자인 시스템. Fredy 웹 UI의 외형

11참고 링크