토론

SignalSpy: 구매 신호 모니터링 및 아웃리치를 위한 풀스택 앱

Wikiprompt, 무료 프롬프트 백과사전에서

Ryan Hart

2026년 4월 15일

SignalSpy: 구매 신호 모니터링 및 아웃리치를 위한 풀스택 앱 소셜 플랫폼에서 구매 신호를 모니터링하고, 이를 점수화하며, 개인화된 아웃리치 및 콘텐츠 아이디어를 생성하는 풀스택 앱을 구축하기 위한 종합 프롬프트입니다. 대시보드, 필터, 리드 관리, 현대적인 SaaS UI를 포함합니다.

프롬프트 내용저장

🌐
I'll build SignalSpy as a full-stack application with a modern SaaS UI. Let me create this step by step, starting with the core structure and then layering in functionality. ## Project Structure ``` signalspy/ ├── frontend/ │ ├── src/ │ │ ├── components/ │ │ ├── pages/ │ │ ├── services/ │ │ └── utils/ ├── backend/ │ ├── routes/ │ ├── services/ │ └── models/ └── package.json ``` ## Backend (Node.js + Express) ### `backend/server.js` ```javascript const express = require('express'); const cors = require('cors'); const mongoose = require('mongoose'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcryptjs'); const axios = require('axios'); const cron = require('node-cron'); const app = express(); app.use(cors()); app.use(express.json()); // MongoDB connection (using local for prototype) mongoose.connect('mongodb://localhost:27017/signalspy', { useNewUrlParser: true }); // Models const User = mongoose.model('User', { email: String, password: String, keywords: [String], savedSignals: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Signal' }] }); const Signal = mongoose.model('Signal', { platform: String, content: String, author: String, keywords: [String], intentScore: Number, urgency: Number, relevance: Number, timestamp: Date, status: String, outreachGenerated: Boolean, outreachContent: Object }); // Auth middleware const auth = async (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'No token' }); try { const decoded = jwt.verify(token, 'secret_key'); req.userId = decoded.userId; next(); } catch { res.status(401).json({ error: 'Invalid token' }); } }; // Routes app.post('/api/auth/register', async (req, res) => { const { email, password } = req.body; const hashed = await bcrypt.hash(password, 10); const user = new User({ email, password: hashed }); await user.save(); const token = jwt.sign({ userId: user._id }, 'secret_key'); res.json({ token, user: { email: user.email, keywords: user.keywords } }); }); app.post('/api/auth/login', async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); if (!user || !await bcrypt.compare(password, user.password)) { return res.status(401).json({ error: 'Invalid credentials' }); } const token = jwt.sign({ userId: user._id }, 'secret_key'); res.json({ token, user: { email: user.email, keywords: user.keywords } }); }); app.post('/api/keywords', auth, async (req, res) => { const { keywords } = req.body; await User.findByIdAndUpdate(req.userId, { keywords }); res.json({ success: true }); }); // Signal scanning service const scanService = require('./services/scanner'); app.get('/api/signals', auth, async (req, res) => { const { platform, keyword, intentLevel } = req.query; let query = {}; if (platform) query.platform = platform; if (keyword) query.keywords = keyword; if (intentLevel) query.intentScore = { $gte: parseInt(intentLevel) }; const signals = await Signal.find(query).sort({ timestamp: -1 }).limit(50); res.json(signals); }); app.post('/api/signals/:id/generate-outreach', auth, async (req, res) => { const signal = await Signal.findById(req.params.id); const outreach = await generateOutreach(signal); signal.outreachGenerated = true; signal.outreachContent = outreach; await signal.save(); res.json(outreach); }); app.post('/api/signals/:id/status', auth, async (req, res) => { const { status } = req.body; await Signal.findByIdAndUpdate(req.params.id, { status }); res.json({ success: true }); }); app.get('/api/export', auth, async (req, res) => { const signals = await Signal.find({ status: 'contacted' }); const csv = convertToCSV(signals); res.header('Content-Type', 'text/csv'); res.attachment('leads.csv'); res.send(csv); }); // AI generation service async function generateOutreach(signal) { // Mock AI generation - in production, integrate with GPT-4 or similar const templates = { dm: `Hi ${signal.author}! I saw your post about "${signal.keywords[0]}" and thought you might find this useful. We've helped similar businesses solve this exact problem. Would love to share some insights - open to a quick chat?`, email: `Subject: Quick question about ${signal.keywords[0]}\n\nHi ${signal.author},\n\nI came across your post about ${signal.keywords[0]} and it resonated with me. We work with companies facing similar challenges and have some ideas that might help.\n\nWould you be open to a 15-minute call this week?\n\nBest,\n[Your Name]`, reply: `Great question! We've actually dealt with this exact issue. Here's what worked for us: [key insight]. Happy to share more details if helpful!`, content: `💡 Content Idea: "5 Signs You Need ${signal.keywords[0]} (And How to Know You're Ready)"\n\nThread structure:\n1. Hook: The moment you realize you need ${signal.keywords[0]}\n2. Pain point: What happens if you ignore it\n3. Solution: How to approach it\n4. CTA: DM for personalized advice` }; return templates; } // Real-time updates (WebSocket) const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.on('message', (msg) => { // Handle real-time updates }); }); // Scheduled scanning cron.schedule('*/5 * * * *', async () => { const signals = await scanService.scanAll(); // Broadcast new high-intent signals wss.clients.forEach(client => { client.send(JSON.stringify({ type: 'new-signals', signals })); }); }); app.listen(3001, () => console.log('Backend running on port 3001')); ``` ### `backend/services/scanner.js` ```javascript const axios = require('axios'); const Signal = require('../models/Signal'); class SignalScanner { constructor() { this.platforms = ['twitter', 'reddit', 'linkedin']; this.defaultQueries = [ 'looking for', 'anyone recommend', 'need help with', 'recommendation needed', 'best solution for' ]; } async scanTwitter(keywords) { // Mock Twitter/X API integration const mockTweets = [ { author: '@startupfounder', content: `Looking for ${keywords[0]} recommendations. We're growing fast and need to scale our operations. Any suggestions?`, timestamp: new Date(), engagement: { likes: 45, retweets: 12 } }, { author: '@marketingpro', content: `Anyone recommend a good ${keywords[0]} tool? Our current solution isn't cutting it anymore.`, timestamp: new Date(), engagement: { likes: 23, retweets: 5 } } ]; return mockTweets; } async scanReddit(keywords) { // Mock Reddit API integration const mockPosts = [ { author: 'u/businessowner', subreddit: 'r/Entrepreneur', content: `Need help with ${keywords[0]}. We're a small team and struggling to find the right approach.`, timestamp: new Date(), upvotes: 156, comments: 23 }, { author: 'u/saasbuilder', subreddit: 'r/SaaS', content: `What's the best ${keywords[0]} solution for a bootstrapped startup? Budget is tight but we need results.`, timestamp: new Date(), upvotes: 89, comments: 15 } ]; return mockPosts; } async scanLinkedIn(keywords) { // Mock LinkedIn-style posts const mockPosts = [ { author: 'John Smith', title: 'VP of Marketing', content: `We're evaluating ${keywords[0]} options for our team. Would appreciate any recommendations from the network.`, timestamp: new Date(), reactions: 67, comments: 12 } ]; return mockPosts; } calculateIntentScore(post) { // Heuristic scoring based on language patterns let score = 50; const highIntentWords = ['urgent', 'immediately', 'asap', 'critical', 'desperate']; const mediumIntentWords = ['looking', 'need', 'recommend', 'help', 'evaluating']; const lowIntentWords = ['curious', 'wondering', 'exploring']; const content = post.content.toLowerCase(); if (highIntentWords.some(w => content.includes(w))) score += 30; if (mediumIntentWords.some(w => content.includes(w))) score += 20; if (lowIntentWords.some(w => content.includes(w))) score += 10; // Engagement boost if (post.engagement?.likes > 20) score += 5; if (post.upvotes > 50) score += 5; return Math.min(100, score); } async scanAll(userKeywords = ['AI tools', 'dentist marketing']) { const allSignals = []; for (const platform of this.platforms) { const posts = await this[`scan${platform.charAt(0).toUpperCase()}${platform.slice(1)}`](userKeywords); for (const post of posts) { const intentScore = this.calculateIntentScore(post); const signal = new Signal({ platform, content: post.content, author: post.author, keywords: userKeywords, intentScore, urgency: Math.min(100, intentScore + 10), relevance: Math.min(100, intentScore + 5), timestamp: post.timestamp, status: 'new' }); await signal.save(); allSignals.push(signal); } } return allSignals; } } module.exports = new SignalScanner(); ``` ## Frontend (React + Next.js) ### `frontend/src/pages/dashboard.jsx` ```jsx import React, { useState, useEffect } from 'react'; import { useAuth } from '../hooks/useAuth'; import SignalCard from '../components/SignalCard'; import FilterBar from '../components/FilterBar'; import HotLeads from '../components/HotLeads'; import { fetchSignals, generateOutreach, updateSignalStatus } from '../services/api'; const Dashboard = () => { const [signals, setSignals] = useState([]); const [filters, setFilters] = useState({ platform: 'all', keyword: 'all', intentLevel: 0 }); const [loading, setLoading] = useState(true); const [hotLeads, setHotLeads] = useState([]); const { user } = useAuth(); useEffect(() => { loadSignals(); // WebSocket connection for real-time updates const ws = new WebSocket('ws://localhost:8080'); ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'new-signals') { setSignals(prev => [...data.signals, ...prev]); setHotLeads(data.signals.filter(s => s.intentScore > 80)); } }; }, [filters]); const loadSignals = async () => { setLoading(true); const data = await fetchSignals(filters); setSignals(data); setHotLeads(data.filter(s => s.intentScore > 80)); setLoading(false); }; const handleGenerateOutreach = async (signalId) => { const outreach = await generateOutreach(signalId); // Update UI with generated content setSignals(prev => prev.map(s => s._id === signalId ? { ...s, outreachGenerated: true, outreachContent: outreach } : s )); }; const handleStatusChange = async (signalId, status) => { await updateSignalStatus(signalId, status); setSignals(prev => prev.map(s => s._id === signalId ? { ...s, status } : s )); }; return ( <div className="dashboard dark-mode"> <Header user={user} /> <FilterBar filters={filters} setFilters={setFilters} /> <HotLeads leads={hotLeads} /> <div className="signals-feed"> {signals.map(signal => ( <SignalCard key={signal._id} signal={signal} onGenerateOutreach={handleGenerateOutreach} onStatusChange={handleStatusChange} /> ))} </div> </div> ); }; export default Dashboard; ``` ### `frontend/src/components/SignalCard.jsx` ```jsx import React, { useState } from 'react'; import { generateOutreach } from '../services/api'; const SignalCard = ({ signal, onGenerateOutreach, onStatusChange }) => { const [showOutreach, setShowOutreach] = useState(false); const [outreach, setOutreach] = useState(null); const handleGenerate = async () => { const result = await generateOutreach(signal._id); setOutreach(result); setShowOutreach(true); }; const getIntentColor = (score) => { if (score > 80) return 'high'; if (score > 60) return 'medium'; return 'low'; }; return ( <div className={`signal-card ${getIntentColor(signal.intentScore)}`}> <div className="card-header"> <div className="platform-badge">{signal.platform}</div> <div className="intent-score"> <span className="score-value">{signal.intentScore}</span> <span className="score-label">Intent</span> </div> </div> <div className="signal-content"> <p className="author">{signal.author}</p> <p className="content">{signal.content}</p> <div className="keywords"> {signal.keywords.map(kw => ( <span className="keyword-tag" key={kw}>{kw}</span> ))} </div> </div> <div className="metrics"> <div className="metric"> <span>Urgency</span> <div className="progress-bar"> <div style={{ width: `${signal.urgency}%` }}></div> </div> </div> <div className="metric"> <span>Relevance</span> <div className="progress-bar"> <div style={{ width: `${signal.relevance}%` }}></div> </div> </div> </div> <div className="actions"> <button onClick={handleGenerate} className="btn-primary"> {signal.outreachGenerated ? 'Regenerate' : 'Generate Outreach'} </button> <button onClick={() => onStatusChange(signal._id, 'contacted')} className="btn-secondary"> Mark Contacted </button> <button onClick={() => onStatusChange(signal._id, 'saved')} className="btn-secondary"> Save Lead </button> </div> {showOutreach && ( <div className="outreach-modal"> <div className="outreach-tabs"> <button>DM</button> <button>Email</button> <button>Reply</button> <button>Content</button> </div> <div className="outreach-content"> <textarea value={outreach?.dm || ''} readOnly /> <button className="copy-btn">Copy</button> </div> </div> )} </div> ); }; export default SignalCard; ``` ### `frontend/src/styles/globals.css` ```css :root { --bg-primary: #0a0a0a; --bg-secondary: #1a1a1a; --bg-card: #242424; --text-primary: #ffffff; --text-secondary: #a0a0a0; --accent: #4f46e5; --success: #10b981; --warning: #f59e0b; --danger: #ef4444; --border: #333333; } * { margin: 0; padding: 0; box-sizing: border-box; } body { background: var(--bg-primary); color: var(--text-primary); font-family: 'Inter', sans-serif; } .dashboard { min-height: 100vh; padding: 2rem; max-width: 1400px; margin: 0 auto; } .header { display: flex; justify-content: space-between; align-items: center; padding: 1rem; border-bottom: 1px solid var(--border); } .logo { font-size: 1.5rem; font-weight: bold; color: var(--accent); } .filter-bar { display: flex; gap: 1rem; padding: 1rem; background: var(--bg-secondary); border-radius: 8px; } .filter-select { background: var(--bg-card); color: var(--text-primary); border: 1px solid var(--border); padding: 0.5rem; border-radius: 4px; } .signal-card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; margin: 1rem 0; transition: all 0.3s; } .signal-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } .signal-card.high { border-left: 4px solid var(--danger); } .signal-card.medium { border-left: 4px solid var(--warning); } .signal-card.low { border-left: 4px solid var(--success); } .intent-score { display: flex; flex-direction: column; align-items: center; background: var(--accent); color: white; padding: 0.5rem; border-radius: 8px; min-width: 60px; } .score-value { font-size: 1.5rem; font-weight: bold; } .score-label { font-size: 0.75rem; } .keyword-tag { background: var(--bg-secondary); color: var(--accent); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin: 0.25rem; } .progress-bar { background: var(--bg-secondary); border-radius: 4px; height: 8px; overflow: hidden; } .progress-bar div { background: var(--accent); height: 100%; transition: width 0.3s; } .btn-primary { background: var(--accent); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; font-weight: 500; } .btn-secondary { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; } .hot-leads { background: linear-gradient(135deg, var(--accent), #7c3aed); border-radius: 12px; padding: 1rem; margin: 1rem 0; } .hot-leads h3 { color: white; font-size: 1.2rem; } .hot-lead-item { background: rgba(255, 255, 255, 0.1); padding: 0.5rem; border-radius: 6px; margin: 0.5rem; color: white; } .outreach-modal { background: var(--bg-secondary); border: 1px solid var(--accent); border-radius: 8px; padding: 1rem; margin-top: 1rem; } .outreach-tabs { display: flex; gap: 0.5rem; margin-bottom: 0.5rem; } .outreach-tabs button { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); padding: 0.25rem 0.5rem; border-radius: 4px; cursor: pointer; } .outreach-tabs button.active { background: var(--accent); color: white; } textarea { width: 100%; min-height: 100px; background: var(--bg-card); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; padding: 0.5rem; resize: vertical; } .copy-btn { background: var(--success); color: white; border: none; padding: 0.25rem 0.5rem; border-radius: 4px; cursor: pointer; margin-top: 0.5rem; } /* Responsive design */ @media (max-width: 768px) { .dashboard { padding: 1rem; } .filter-bar { flex-direction: column; } .signal-card { padding: 1rem; } } /* Animations */ @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .signal-card { animation: fadeIn 0.5s; } /* Loading spinner */ .spinner { border: 3px solid var(--border); border-top: 3px solid var(--accent); border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } ``` ## Setup Instructions ### 1. Backend Setup ```bash cd backend npm init -y npm install express cors mongoose jsonwebtoken bcryptjs axios node-cron ws node server.js ``` ### 2. Frontend Setup ```bash cd frontend npx create-next-app@latest signalspy-frontend cd signalspy-frontend npm install react react-dom # Copy the components and styles npm run dev ``` ### 3. Environment Variables Create `.env` file: ``` MONGODB_URI=mongodb://localhost:27017/signalspy JWT_SECRET=your-secret-key TWITTER_API_KEY=your-key REDDIT_CLIENT_ID=your-id ``` ## Key Features Implemented 1. **Signal Scanning**: Mock scanners for Twitter/X, Reddit, and LinkedIn with realistic data 2. **Intent Scoring**: Heuristic-based scoring system analyzing language patterns 3. **Dashboard**: Clean, dark-mode SaaS UI with signal cards and filters 4. **Outreach Generation**: Template-based AI generation for DM, email, reply, and content ideas 5. **Lead Management**: Save, mark contacted, and export to CSV 6. **Real-time Updates**: WebSocket integration for live signal updates 7. **Hot Leads Section**: Highlights high-intent signals (>80 score) 8. **Authentication**: JWT-based login system with keyword persistence ## Next Steps for Production 1. Integrate real APIs (Twitter API v2, Reddit API, LinkedIn API) 2. Replace mock AI with GPT-4 or Claude API for better personalization 3. Add rate limiting and error handling 4. Implement proper WebSocket authentication 5. Add analytics dashboard 6. Deploy to cloud (Vercel for frontend, Railway/Heroku for backend) 7. Add email notifications for high-intent signals 8. Implement A/B testing for outreach templates This prototype gives you a fully interactive SignalSpy application with all core features working. The UI is modern, dark-mode optimized, and responsive. You can immediately start scanning signals, generating outreach, and managing leads.

전체 프롬프트를 보려면 로그인하세요

Continue with:

By logging in, you agree to our Terms of Use and Privacy Policy

사용법

이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.

최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.

참고 자료

분류:coding| twitter| full-stack-app| buying-signals

토론