LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
163 lines
4.3 KiB
Dart
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;
|
|
}
|
|
} |