WAIA 개발일지 - 핵심 기능 확장 및 UI 개선
👋 개요
지난 개발일지 이후, WAIA 프로젝트의 핵심 기능을 확장하고 사용자 경험을 개선하는 작업을 진행했습니다. 특히 서비스 팔로워 수 트래킹, 개인정보 처리방침 변경 감지 크롤러 구현, 그리고 대시보드 UI 개선에 중점을 두었습니다.
📝 오늘 한 일
- 팔로워 수 트래킹 기능 구현:
- Firestore
services컬렉션에followerCount필드 추가. - 사용자 팔로우/언팔로우 시
followerCount자동 증감 로직 (handleFollow함수 내 트랜잭션 사용) 구현. displayServices함수에 팔로워 수 표시 기능 추가.initializeFollowerCounts()헬퍼 함수를 통해 기존 서비스의 팔로워 수 초기화 기능 제공.
- Firestore
- 개인정보 처리방침 변경 감지 크롤러 구현 (로컬 스크립트 방식):
- Firestore
services컬렉션에policyUrl및lastHash필드 추가. updateServicesWithUrls()헬퍼 함수를 통해 서비스별policyUrl업데이트 및lastHash초기화 기능 제공.- Firebase Blaze 요금제 이슈로 인해 Cloud Function 대신 로컬 Node.js 스크립 (
crawl.js) 방식으로 구현. firebase-admin,axios,crypto라이브러리를 사용하여 URL 콘텐츠 가져오기, 해시 계산, 변경 감지 및 Firestore 업데이트 로직 구현.waia-service-account-key.json및node_modules폴더를.gitignore에 추가하여 보안 및 Git 관리 효율성 증대.
- Firestore
- 대시보드 ‘원문 보기’ 버튼 추가:
- ‘내 대시보드’의 각 서비스 항목에 ‘원문 보기’ 버튼 추가.
- 버튼 클릭 시 해당 서비스의
policyUrl을 새 브라우저 탭에서 열도록 구현 (CORS 및 보안 문제로 인한 인앱 표시 대신).
- UI/UX 개선:
- 대시보드 서비스 항목 레이아웃 조정: 이름(왼쪽), 상태(중앙), 버튼(오른쪽)으로 균형 있게 배치.
- 상태 텍스트 및 ‘원문 보기’ 버튼 색상 동적 변경: ‘정상’ 상태는 녹색, 그 외 모든 상태(주의 필요, 크롤링 실패, 확인전)는 주황색으로 표시.
✨ 주요 작업 내용
1. 개인정보 처리방침 크롤러 (로컬 스크립트)
Firebase Cloud Functions 배포 시 Blaze 요금제 요구사항으로 인해, 초기 계획을 변경하여 로컬에서 실행 가능한 Node.js 크롤링 스크립트(crawl.js)를 구현했습니다. 이 스크립트는 Firebase Admin SDK를 사용하여 Firestore에 직접 접근하며, axios로 웹 페이지 내용을 가져오고 crypto 모듈로 해시를 계산하여 변경 여부를 판단합니다.
핵심 코드 (crawl.js):
const admin = require('firebase-admin');
const axios = require('axios');
const crypto = require('crypto');
// 서비스 계정 키를 통해 Firebase Admin SDK 초기화
const serviceAccount = require('./waia-service-account-key.json');
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });
const db = admin.firestore();
async function runCrawl() {
// Firestore에서 서비스 목록 가져오기
const servicesRef = db.collection('services');
const snapshot = await servicesRef.get();
for (const doc of snapshot.docs) {
const service = doc.data();
const serviceId = doc.id;
if (!service.policyUrl) {
console.log(`[SKIP] ${service.name}: policyUrl이 없습니다.`);
continue;
}
try {
// policyUrl에서 HTML 내용 가져오기
const response = await axios.get(service.policyUrl, {
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' }
});
const html = response.data;
const currentHash = crypto.createHash('sha256').update(html).digest('hex');
// 해시 비교 및 상태 업데이트
if (service.lastHash && service.lastHash !== currentHash) {
console.log(` -> [!!] 변경 감지! ${service.name} 상태를 '주의 필요'로 업데이트합니다.`);
await servicesRef.doc(serviceId).update({
status: '주의 필요',
lastHash: currentHash,
lastUpdated: admin.firestore.FieldValue.serverTimestamp()
});
} else {
console.log(` -> [OK] ${service.name} 변경 없음. 해시값만 업데이트합니다.`);
await servicesRef.doc(serviceId).update({
lastHash: currentHash,
lastUpdated: admin.firestore.FieldValue.serverTimestamp()
});
}
} catch (error) {
console.error(` -> [ERROR] ${service.name} 크롤링 실패:`, error.message);
await servicesRef.doc(serviceId).update({ status: '크롤링 실패' });
}
}
console.log('\n크롤링 작업이 모두 완료되었습니다.');
}
runCrawl();
2. 대시보드 UI 개선 및 ‘원문 보기’ 버튼
사용자 대시보드의 가독성을 높이고 기능을 추가했습니다. 각 서비스 항목은 이름, 상태, ‘원문 보기’ 버튼으로 구성되며, Flexbox를 활용하여 세 요소가 균형 있게 정렬되도록 했습니다. 상태에 따라 색상이 동적으로 변경되어 시각적인 피드백을 강화했습니다.
핵심 코드 (app.js - displayDashboard 함수 내):
// ... (생략) ...
const statusClass = service.status === '정상' ? 'ok' : 'warning';
const buttonClass = service.status === '정상' ? 'policy-btn-ok' : 'policy-btn-warning';
item.innerHTML = `
<span class="name">${service.name}</span>
<span class="status ${statusClass}">${service.status}</span>
<div class="service-actions">
${service.policyUrl ? `<a href="${service.policyUrl}" target="_blank" class="policy-link-btn ${buttonClass}">원문 보기</a>` : ''}
</div>
`;
// ... (생략) ...
겪었던 문제 및 해결 과정
- Firebase Cloud Functions 배포 문제:
- 문제: Cloud Functions 배포 시 Blaze 요금제(종량제)로의 업그레이드가 필수적이라는 오류 발생. 이는 Cloud Build API 사용 정책 때문이었음.
- 해결: Firebase 서버리스 환경 대신, 사용자의 로컬 환경에서 직접 실행 가능한 Node.js 크롤링 스크립트(
crawl.js) 방식으로 전환. 이를 위해 Firebase Admin SDK를 사용하고 서비스 계정 키를 통한 인증 방식을 도입.
firebase init과정 중 파일 덮어쓰기 문제:- 문제:
firebase init실행 시 기존에 생성해 둔functions/package.json및functions/index.js파일 덮어쓰기 여부 질문 발생. - 해결: 사용자에게 ‘덮어쓰지 않음(N)’을 명확히 안내하여 기존 작업물 보존.
- 문제:
node_modules및 서비스 계정 키 Git 관리:- 문제:
node_modules폴더와 민감한waia-service-account-key.json파일이 Git 저장소에 포함될 위험. - 해결:
.gitignore파일에node_modules/와waia-service-account-key.json을 추가하여 Git 추적에서 제외.
- 문제:
- 개인정보 처리방침 인앱 표시 보안 문제:
- 문제: 사용자가 요청한 ‘개인정보 처리방침 전문 인앱 표시’ 기능이 CORS 및 XSS 보안 취약점을 야기할 수 있음.
- 해결: 보안 위험을 설명하고, 대신 새 탭에서 원문 페이지를 여는 표준적이고 안전한 방식으로 구현.
💡 새롭게 배운 점
- Firebase Cloud Functions 배포 정책: Node.js 런타임의 Cloud Functions 배포는 Blaze 요금제와 Cloud Build API에 의존한다는 점을 명확히 인지.
- 로컬 Node.js 스크립트 활용: 서버리스 환경이 아닌 로컬 환경에서 Firebase Admin SDK를 사용하여 데이터베이스와 상호작용하는 방법 및 그 장단점 (무료 사용, 수동 실행, 보안 관리) 체득.
- 웹 보안 (CORS, XSS): 클라이언트 측에서 외부 콘텐츠를 직접 가져오거나 렌더링할 때 발생하는 보안 위험의 중요성 재확인 및 안전한 대안 제시의 필요성.
- Git
.gitignore의 중요성: 민감 정보 및 불필요한 빌드 아티팩트(예:node_modules)를 효과적으로 관리하는 방법.
🚀 다음 계획
- 이메일 알림 기능: 서비스 상태가 ‘주의 필요’로 변경될 경우 사용자에게 이메일 알림을 보내는 기능 (Blaze 요금제 업그레이드 시 Cloud Functions로 구현 가능).
- UI/UX 추가 개선: ‘마지막 확인 시각’ 표시 등 사용자에게 유용한 정보 추가.
- 서비스 추가/관리 UI: 관리자가 웹 앱 내에서 직접 서비스를 추가하거나 수정할 수 있는 기능.