Files
lunchpick/lib/presentation/services/restaurant_form_validator.dart
JiWoong Sul 85fde36157 feat: 초기 프로젝트 설정 및 LunchPick 앱 구현
LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다.

주요 기능:
- 네이버 지도 연동 맛집 추가
- 랜덤 메뉴 추천 시스템
- 날씨 기반 거리 조정
- 방문 기록 관리
- Bluetooth 맛집 공유
- 다크모드 지원

기술 스택:
- Flutter 3.8.1+
- Riverpod 상태 관리
- Hive 로컬 DB
- Clean Architecture

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-30 19:03:28 +09:00

163 lines
4.3 KiB
Dart

import '../../core/utils/validators.dart';
import '../view_models/add_restaurant_view_model.dart';
/// 식당 폼 검증 서비스
class RestaurantFormValidator {
/// 폼 데이터 검증
static Map<String, String?> validateFormData(RestaurantFormData formData) {
final errors = <String, String?>{};
// 이름 검증
if (formData.name.isEmpty) {
errors['name'] = '가게 이름을 입력해주세요';
}
// 카테고리 검증
if (formData.category.isEmpty) {
errors['category'] = '카테고리를 입력해주세요';
}
// 도로명 주소 검증
if (formData.roadAddress.isEmpty) {
errors['roadAddress'] = '도로명 주소를 입력해주세요';
}
// 위도 검증
if (formData.latitude.isNotEmpty) {
final latitudeError = Validators.validateLatitude(formData.latitude);
if (latitudeError != null) {
errors['latitude'] = latitudeError;
}
}
// 경도 검증
if (formData.longitude.isNotEmpty) {
final longitudeError = Validators.validateLongitude(formData.longitude);
if (longitudeError != null) {
errors['longitude'] = longitudeError;
}
}
return errors;
}
/// 네이버 URL 검증
static String? validateNaverUrl(String url) {
if (url.trim().isEmpty) {
return 'URL을 입력해주세요';
}
// 네이버 지도 URL 패턴 검증
final naverMapRegex = RegExp(
r'^https?://(map\.naver\.com|naver\.me)',
caseSensitive: false,
);
if (!naverMapRegex.hasMatch(url)) {
return '네이버 지도 URL만 입력 가능합니다';
}
return null;
}
/// 전화번호 형식 검증
static String? validatePhoneNumber(String? phoneNumber) {
if (phoneNumber == null || phoneNumber.isEmpty) {
return null; // 선택 필드
}
// 전화번호 패턴: 02-1234-5678, 010-1234-5678 등
final phoneRegex = RegExp(
r'^0\d{1,2}-?\d{3,4}-?\d{4}$',
);
if (!phoneRegex.hasMatch(phoneNumber.replaceAll(' ', ''))) {
return '올바른 전화번호 형식이 아닙니다';
}
return null;
}
/// 주소 형식 검증
static String? validateAddress(String? address) {
if (address == null || address.isEmpty) {
return '주소를 입력해주세요';
}
// 최소 길이 검증
if (address.length < 5) {
return '올바른 주소를 입력해주세요';
}
return null;
}
/// 카테고리 검증
static String? validateCategory(String? category) {
if (category == null || category.isEmpty) {
return '카테고리를 입력해주세요';
}
// 허용된 카테고리 목록 (필요시 추가)
// final allowedCategories = [
// '한식', '중식', '일식', '양식', '아시안',
// '카페', '디저트', '분식', '패스트푸드', '기타'
// ];
// 정확한 매칭이 아니어도 허용 (사용자 입력 고려)
// 필요시 더 엄격한 검증 추가 가능
return null;
}
/// 전체 폼 유효성 검사
static bool isFormValid(RestaurantFormData formData) {
final errors = validateFormData(formData);
return errors.isEmpty;
}
/// 필수 필드만 검증
static bool hasRequiredFields(RestaurantFormData formData) {
return formData.name.isNotEmpty &&
formData.category.isNotEmpty &&
formData.roadAddress.isNotEmpty;
}
}
/// 폼 필드 에러 메시지 클래스
class FormFieldErrors {
final String? name;
final String? category;
final String? roadAddress;
final String? latitude;
final String? longitude;
final String? phoneNumber;
const FormFieldErrors({
this.name,
this.category,
this.roadAddress,
this.latitude,
this.longitude,
this.phoneNumber,
});
bool get hasErrors =>
name != null ||
category != null ||
roadAddress != null ||
latitude != null ||
longitude != null ||
phoneNumber != null;
Map<String, String> toMap() {
final map = <String, String>{};
if (name != null) map['name'] = name!;
if (category != null) map['category'] = category!;
if (roadAddress != null) map['roadAddress'] = roadAddress!;
if (latitude != null) map['latitude'] = latitude!;
if (longitude != null) map['longitude'] = longitude!;
if (phoneNumber != null) map['phoneNumber'] = phoneNumber!;
return map;
}
}