feat(app): add manual entry and sharing flows

This commit is contained in:
JiWoong Sul
2025-11-19 16:36:39 +09:00
parent 5ade584370
commit 947fe59486
110 changed files with 5937 additions and 3781 deletions

View File

@@ -5,37 +5,234 @@ import 'package:flutter/foundation.dart';
class NaverHtmlExtractor {
// 제외할 UI 텍스트 패턴 (확장)
static const List<String> _excludePatterns = [
'로그인', '메뉴', '검색', '지도', '리뷰', '사진', '네이버', '영업시간',
'전화번호', '주소', '찾아오시는길', '예약', '', '이용약관', '개인정보',
'고객센터', '신고', '공유', '즐겨찾기', '길찾기', '거리뷰', '저장',
'더보기', '접기', '펼치기', '닫기', '취소', '확인', '선택', '전체', '삭제',
'플레이스', '지도보기', '상세보기', '평점', '별점', '추천', '인기', '최신',
'오늘', '내일', '영업중', '영업종료', '휴무', '정기휴무', '임시휴무',
'배달', '포장', '매장', '주차', '단체석', '예약가능', '대기', '웨이팅',
'수증', '현금', '카드', '계산서', '할인', '쿠폰', '적립', '포인트',
'회원', '비회원', '로그아웃', '마이페이지', '알림', '설정', '도움말',
'문의', '제보', '수정', '삭제', '등록', '작성', '댓글', '답글', '좋아요',
'싫어요', '스크랩', '북마크', '태그', '해시태그', '팔로우', '팔로잉',
'팔로워', '차단', '신고하기', '게시물', '프로필', '활동', '통계', '분석',
'다운로드', '업로드', '첨부', '파일', '이미지', '동영상', '음성', '링크',
'복사', '붙여넣기', '되돌리기', '다시실행', '새로고침', '뒤로', '앞으로',
'시작', '종료', '일시정지', '재생', '정지', '음량', '화면', '전체화면',
'최소화', '최대화', '창닫기', '새창', '새탭', '인쇄', '저장하기', '열기',
'가져오기', '내보내기', '동기화', '백업', '복원', '초기화', '재설정',
'업데이트', '버전', '정보', '소개', '안내', '공지', '이벤트', '혜택',
'쿠키', '개인정보처리방침', '서비스이용약관', '위치정보이용약관',
'청소년보호정책', '저작권', '라이선스', '제휴', '광고', '비즈니스',
'개발자', 'API', '오픈소스', '기여', '후원', '기부', '결제', '환불',
'교환', '반품', '배송', '택배', '운송장', '추적', '도착', '출발',
'네이버 지도', '카카오맵', '구글맵', 'T맵', '지도 앱', '내비게이션',
'경로', '소요시간', '거리', '도보', '자전거', '대중교통', '자동차',
'지하철', '버스', '택시', '기차', '비행기', '선박', '도보', '환승',
'출구', '입구', '승강장', '매표소', '화장실', '편의시설', '주차장',
'엘리베이터', '에스컬레이터', '계단', '경사로', '점자블록', '휠체어',
'유모차', '애완동물', '흡연', '금연', '와이파이', '콘센트', '충전',
'PC', '프린터', '팩스', '복사기', '회의실', '세미나실', '강당', '공연장',
'시장', '박물관', '미술관', '도서관', '체육관', '수영장', '운동장',
'놀이터', '공원', '산책로', '자전거도로', '등산로', '캠핑장', '낚시터'
'로그인',
'메뉴',
'검색',
'지도',
'리뷰',
'사진',
'네이버',
'업시간',
'전화번호',
'주소',
'찾아오시는길',
'예약',
'',
'이용약관',
'개인정보',
'고객센터',
'신고',
'공유',
'즐겨찾기',
'길찾기',
'거리뷰',
'저장',
'더보기',
'접기',
'펼치기',
'닫기',
'취소',
'확인',
'선택',
'',
'삭제',
'플레이스',
'지도보기',
'상세보기',
'평점',
'별점',
'추천',
'인기',
'최신',
'오늘',
'내일',
'영업중',
'영업종료',
'휴무',
'정기휴무',
'임시휴무',
'배달',
'포장',
'매장',
'주차',
'단체석',
'예약가능',
'대기',
'웨이팅',
'영수증',
'현금',
'카드',
'계산서',
'할인',
'쿠폰',
'적립',
'포인트',
'회원',
'비회원',
'로그아웃',
'마이페이지',
'알림',
'설정',
'도움말',
'문의',
'제보',
'수정',
'삭제',
'등록',
'작성',
'댓글',
'답글',
'좋아요',
'싫어요',
'스크랩',
'북마크',
'태그',
'해시태그',
'팔로우',
'팔로잉',
'팔로워',
'차단',
'신고하기',
'게시물',
'프로필',
'활동',
'통계',
'분석',
'다운로드',
'업로드',
'첨부',
'파일',
'이미지',
'동영상',
'음성',
'링크',
'복사',
'붙여넣기',
'되돌리기',
'다시실행',
'새로고침',
'뒤로',
'앞으로',
'시작',
'종료',
'일시정지',
'재생',
'정지',
'음량',
'화면',
'전체화면',
'최소화',
'최대화',
'창닫기',
'새창',
'새탭',
'인쇄',
'저장하기',
'열기',
'가져오기',
'내보내기',
'동기화',
'백업',
'복원',
'초기화',
'재설정',
'업데이트',
'버전',
'정보',
'소개',
'안내',
'공지',
'이벤트',
'혜택',
'쿠키',
'개인정보처리방침',
'서비스이용약관',
'위치정보이용약관',
'청소년보호정책',
'저작권',
'라이선스',
'제휴',
'광고',
'비즈니스',
'개발자',
'API',
'오픈소스',
'기여',
'후원',
'기부',
'결제',
'환불',
'교환',
'반품',
'배송',
'택배',
'운송장',
'추적',
'도착',
'출발',
'네이버 지도',
'카카오맵',
'구글맵',
'T맵',
'지도 앱',
'내비게이션',
'경로',
'소요시간',
'거리',
'도보',
'자전거',
'대중교통',
'자동차',
'지하철',
'버스',
'택시',
'기차',
'비행기',
'선박',
'도보',
'환승',
'출구',
'입구',
'승강장',
'매표소',
'화장실',
'편의시설',
'주차장',
'엘리베이터',
'에스컬레이터',
'계단',
'경사로',
'점자블록',
'휠체어',
'유모차',
'애완동물',
'흡연',
'금연',
'와이파이',
'콘센트',
'충전',
'PC',
'프린터',
'팩스',
'복사기',
'회의실',
'세미나실',
'강당',
'공연장',
'전시장',
'박물관',
'미술관',
'도서관',
'체육관',
'수영장',
'운동장',
'놀이터',
'공원',
'산책로',
'자전거도로',
'등산로',
'캠핑장',
'낚시터',
];
/// HTML에서 유효한 한글 텍스트 추출 (UI 텍스트 제외)
@@ -52,21 +249,35 @@ class NaverHtmlExtractor {
// 특정 태그의 내용만 추출 (제목, 본문 등 중요 텍스트가 있을 가능성이 높은 태그)
final contentTags = [
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'p', 'span', 'div', 'li', 'td', 'th',
'strong', 'em', 'b', 'i', 'a'
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'p',
'span',
'div',
'li',
'td',
'th',
'strong',
'em',
'b',
'i',
'a',
];
final tagPattern = contentTags.map((tag) =>
'<$tag[^>]*>([^<]+)</$tag>'
).join('|');
final tagPattern = contentTags
.map((tag) => '<$tag[^>]*>([^<]+)</$tag>')
.join('|');
final tagRegex = RegExp(tagPattern, multiLine: true, caseSensitive: false);
final tagMatches = tagRegex.allMatches(cleanHtml);
// 추출된 텍스트 수집
final extractedTexts = <String>[];
for (final match in tagMatches) {
final text = match.group(1)?.trim() ?? '';
if (text.isNotEmpty && text.contains(RegExp(r'[가-힣]'))) {
@@ -77,11 +288,11 @@ class NaverHtmlExtractor {
// 모든 태그 제거 후 남은 텍스트도 추가
final textOnly = cleanHtml.replaceAll(RegExp(r'<[^>]+>'), ' ');
final cleanedText = textOnly.replaceAll(RegExp(r'\s+'), ' ').trim();
// 한글 텍스트 추출
final koreanPattern = RegExp(r'[가-힣]+(?:\s[가-힣]+)*');
final koreanMatches = koreanPattern.allMatches(cleanedText);
for (final match in koreanMatches) {
final text = match.group(0)?.trim() ?? '';
if (text.length >= 2) {
@@ -91,17 +302,19 @@ class NaverHtmlExtractor {
// 중복 제거 및 필터링
final uniqueTexts = <String>{};
for (final text in extractedTexts) {
// UI 패턴 제외
bool isExcluded = false;
for (final pattern in _excludePatterns) {
if (text == pattern || text.startsWith(pattern) || text.endsWith(pattern)) {
if (text == pattern ||
text.startsWith(pattern) ||
text.endsWith(pattern)) {
isExcluded = true;
break;
}
}
if (!isExcluded && text.length >= 2 && text.length <= 50) {
uniqueTexts.add(text);
}
@@ -109,13 +322,13 @@ class NaverHtmlExtractor {
// 리스트로 변환하여 반환
final resultList = uniqueTexts.toList();
debugPrint('========== 유효한 한글 텍스트 추출 결과 ==========');
for (int i = 0; i < resultList.length; i++) {
debugPrint('[$i] ${resultList[i]}');
}
debugPrint('========== 총 ${resultList.length}개 추출됨 ==========');
return resultList;
}
@@ -217,7 +430,7 @@ class NaverHtmlExtractor {
return null;
}
/// HTML에서 Place URL 추출 (og:url 메타 태그)
static String? extractPlaceLink(String html) {
try {
@@ -232,7 +445,7 @@ class NaverHtmlExtractor {
debugPrint('NaverHtmlExtractor: og:url 추출 - $url');
return url;
}
// canonical 링크 태그에서 추출
final canonicalRegex = RegExp(
r'<link[^>]+rel="canonical"[^>]+href="([^"]+)"',
@@ -247,7 +460,7 @@ class NaverHtmlExtractor {
} catch (e) {
debugPrint('NaverHtmlExtractor: Place Link 추출 실패 - $e');
}
return null;
}
}
}