1) 이미지 선택을 FormField로 편입하여 검증 일원화
- 변경 전: _submit() 내부에서 이미지 미선택 시 SnackBar로 경고 후 return.
- 변경 후: 이미지 섹션을 FormField<String?>로 감싸고 validator에 편입.
- 장점: 다른 TextFormField들과 동일한 방식으로 검증됨. 에러가 버튼/미리보기 바로 아래 빨간 텍스트로 표시되어 시인성 증가.
- 이미지 선택 시 didChange 호출로 에러 즉시 해제.
FormField<String?>(
key: _imageFieldKey,
validator: (_) => _imageSelection == null ? '이미지를 등록해 주세요.' : null,
builder: (field) { /* ... FilledButton, 미리보기, 에러텍스트 ... */ },
)
// 다이얼로그 내 선택 시
setState(() => _imageSelection = 'network');
_imageFieldKey.currentState?.didChange(_imageSelection);
- 결과: _submit()에서 이미지 미선택용 SnackBar 로직 삭제 → 검증 경로 단순화.
2) 가격 입력 중 천단위 콤마 자동 적용 + 안전한 검증
- 입력 UX: TextInputFormatter.withFunction으로 타이핑과 동시에 1,000 식 콤마 적용.
- 저장 값: 표시는 콤마가 있지만, 저장 시엔 replaceAll(',', '')로 순수 숫자만 파싱해 int로 보관(계산/정렬 안정성).
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
TextInputFormatter.withFunction((oldValue, newValue) {
if (newValue.text.isEmpty) return newValue;
final number = int.parse(newValue.text.replaceAll(',', ''));
final formatted = NumberFormat('#,###').format(number);
return TextEditingValue(
text: formatted,
selection: TextSelection.collapsed(offset: formatted.length),
);
}),
],
- 검증 로직(validator):
- 공백/빈값 방지
- 숫자 외 문자 제거 후 정수 변환 시도
- 범위 검증: 100원 이상 10,000,000원 이하
validator: (v) {
if (v == null || v.trim().isEmpty) return '상품 가격을 입력해 주세요.';
final clean = v.replaceAll(RegExp(r'[^0-9]'), '');
final n = int.tryParse(clean);
if (n == null || n < 0) return '유효한 가격을 입력해 주세요.';
if (n < 100 || n > 10000000) return '100원 이상 10,000,000원 이하로 입력해 주세요.';
return null;
}
3) 이미지 URL 오타 수정으로 리스트 썸네일 표시 정상화
final String imageUrl =
_imageSelection == 'network' ? 'https://picsum.photos/200/300' : '';
- 효과: 상품등록 후 홈 리스트 썸네일이 정상적으로 노출됨.
4) UUID로 상품 ID 안정적으로 발급
- uuid: ^4.x 사용, 제출 시 uuid.v4()로 고유 ID 생성.
- 충돌 위험 낮고, 로컬/서버 어디서든 식별자로 활용 가능.
final uuid = const Uuid();
final product = ProductEntity(
id: uuid.v4(),
name: _nameController.text.trim(),
price: int.parse(_priceController.text.replaceAll(',', '')),
description: _descController.text.trim(),
imageUrl: imageUrl,
);
5) 등록 완료 다이얼로그의 가격 표시는 항상 포맷 적용
- 사용자 피드백용 표시에서 intl로 콤마 포맷 보장.
Text('가격: ${NumberFormat('#,###').format(product.price)}원'),
6) 불필요 코드 정리 및 UI 폴리싱
- _hasSelectedImage 게터 제거(검증이 FormField로 이동했기 때문에 중복).
- AppBar에 centerTitle: true 반영(시각적 정렬).
- 공백 등 자잘한 문자열 정리.
7) 전체 흐름 요약
- 사용자가 “이미지 선택” 클릭 → 다이얼로그에서 picsum 선택 → 미리보기 즉시 반영, 에러 해제.
- 상품명(0/30), 가격(콤마 자동), 설명(0/1000) 입력.
- “등록하기” → Form.validate()로 모든 필드 검사(이미지 포함).
- 통과 시 ProductEntity 생성(가격은 숫자만), UUID 발급, 올바른 이미지 URL 저장.
- “등록 완료” 다이얼로그 출력 → 확인 시 Navigator.pop(context, product)로 상위(홈)로 전달.
- 홈 리스트가 전달받은 product를 목록에 append → 썸네일 정상 표시.
배운 점 / 메모
- **검증은 한 곳(Form/Field)**에 모아야 유지보수가 쉽다. SnackBar 경고는 즉시성은 좋지만, 폼의 정적 에러 텍스트가 사용성·접근성 면에서 우수.
- 표시와 보관 분리: 가격은 화면에선 String(콤마포맷), 저장은 int. 모델/DB/서버에서의 오류를 근본적으로 예방한다.
- 작은 오타(슬래시 누락) 하나로도 기능 전체가 깨질 수 있음 → 네트워크 리소스 경로는 상수화/재사용을 고려.
- 이미지 선택도 입력 필드(validator)로 모델링하면 검증/표시/에러처리가 일관된다.