■ 소개
목표: 419 (4업으로 1달러 벌기 시작해서 9달러까지 벌어보기)
오늘은 지난번에 이어서 오코오니 님이 주신 다른 피드백을 반영해봤습니다.
https://www.gpters.org/nocode/post/260204-level-response-rag-EeTz5rSqlRsU4eO
바로 사진넣기 버튼입니다.
한국어로 된 두 사람의 대화 스크린샷
.
■ 진행 및 결과
1.커서로 plan하기
오늘도 PLAN으로 시작합니다.
지금 $20 플랜 한도가 끝난 상황이라 Auto로 돌리다보니,
확실히 커서의 퍼포먼스가 예전같지는 않습니다. ㅠ
.
그래도 못돌릴 정도는 아닙니다.
한국어 메뉴 스크린샷
우선 계획을 짜달라고 했고
.
api너무 먹을까봐 걱정되어서 물어봅니다.
다른 기능도 추천받았는데,
고민 3초정도 하다가 그냥 기본 기능인 사진찍기와 앨범에서 선택만 구현해달라고 했습니다.
.
그 결과 받은 md파일입니다.
.
채팅 입력창 + 버튼 기능 설계 (사진/앨범 MVP)
1. 개요
기능 목적: 채팅 입력창 왼쪽 + 버튼을 통해, 사용자가 사진을 찍거나(카메라) 앨범에서 사진을 선택하여 상황을 빠르게 공유할 수 있게 한다.
범위 (MVP)
포함: 사진 찍기, 앨범에서 선택, 사진 미리보기, 선택 텍스트와 함께 전송
제외: 위치 공유, 파일 첨부, 여러 장 선택, 편집(크롭/스티커 등), 템플릿, AI 기능
2. 사용자 시나리오 (User Stories)
US-1: 사용자는 채팅 중에 + 버튼을 눌러 바로 사진을 찍어 현재 상황을 설명하고 싶다.
US-2: 사용자는 이미 찍어둔 사진을 앨범에서 골라 상대에게 보내고 싶다.
US-3: 사용자는 사진에 짧은 설명 텍스트를 덧붙여 함께 전송하고 싶다.
US-4: 사용자는 카메라/앨범 권한이 없을 때, 왜 안 되는지 이해하고 설정으로 이동할 수 있기를 원한다.
3. 화면 및 컴포넌트 구조
화면
ChatPage
PhotoPreviewPage (사진 미리보기 + 텍스트 입력 화면)
주요 위젯
ChatMessageList
메시지 리스트 렌더링 담당
ChatInputBar
텍스트 입력, + 버튼, 전송 버튼 UI
콜백:
onSendText(String text)
onTapPlus()
데이터 모델
enum MessageType { text, image }
ChatMessage
id, type, text?, imageUrl?, createdAt, isMine
MessageDraft
imageFile, text? (사진 + 선택 텍스트)
4. 플로우 정의
4.1 + 버튼 액션 시트
사용자가 ChatInputBar의 + 버튼 탭
하단 ModalBottomSheet 형태의 액션 시트 노출
액션 목록 (2개만):
사진 찍기 (카메라 아이콘)
앨범에서 선택 (사진 아이콘)
4.2 사진 찍기 플로우
액션 시트에서 사진 찍기 선택
카메라 화면 오픈 (image_picker의 ImageSource.camera 사용)
사진 촬영 성공 시 PhotoPreviewPage로 이동
PhotoPreviewPage 구성:
중앙: 촬영한 사진 미리보기
하단:
텍스트 입력창 (선택 입력)
전송 버튼
전송 탭 시:
MessageDraft(imageFile, text)를 ChatPage로 전달
서버 업로드 → 업로드 성공 시 ChatMessage(type: image, imageUrl, text) 리스트에 반영
4.3 앨범 선택 플로우
액션 시트에서 앨범에서 선택 선택
앨범 화면 오픈 (ImageSource.gallery)
사진 1장 선택
이후 플로우는 사진 찍기 플로우의 3~5단계와 동일
5. 권한 및 에러 처리
카메라/앨범 권한 없음
카메라/앨범 진입 실패 시:
간단한 안내 메시지:
예: “카메라(또는 앨범) 권한이 필요해요. 설정에서 권한을 허용해 주세요.”
(선택) 설정 열기 버튼 제공
업로드 실패
서버 업로드 실패 시:
스낵바/토스트: “사진 전송에 실패했어요. 다시 시도해 주세요.”
메시지는 전송 실패 상태로 표시하거나, 전송 안 된 상태로 유지 (정책에 따라 결정)
6. API 및 네트워크 고려사항
요청
사진 업로드 엔드포인트 (예: POST /v1/chat/images)
바디: 이미지 파일 (압축/리사이즈 후)
응답: imageUrl
메시지 전송 엔드포인트 (예: POST /v1/chat/messages)
바디:
type: "image"
imageUrl: 업로드된 이미지 URL
text: 선택 텍스트 (있으면)
클라이언트 측 처리
업로드 전 이미지 리사이즈/압축 (용량 제한 맞추기)
(선택) 낙관적 UI: 업로드 완료 전에도 채팅에 미리 보여주고, 실패 시 상태 업데이트
7. MVP 범위와 이후 확장
이번 MVP에서 하는 것
+ → 사진 찍기 / 앨범에서 선택
사진 1장 선택
사진 미리보기 + 선택 텍스트 입력
전송, 업로드, 채팅 리스트 반영
최소한의 권한/에러 안내
향후 확장 후보 (문서에만 기록, 구현 X)
여러 장 사진 전송
간단 편집(크롭, 회전)
위치 공유, 파일 첨부
템플릿/자주 쓰는 문장
AI 기반 문장 보정/요약.
빌드하고 나니 버튼이 작동합니다.
그런데 문제가 발생했습니다.
요렇게 설명을 넣어야하는데,
키보드가 올라오면서 화면이 잘려서
overflowed가 됩니다.
.
버그를 고쳐보기로 합니다.
.
2.overflowed 버그 고치기
캡쳐를 주면서 사진찍을때 생기는 버그를 고쳐달라고 합니다.
.
.
하는김에 앨범에서 선택도 같이 고쳐달라고 합니다.
.
md파일을 줍니다.
채팅 사진 미리보기 overflow 방지 설계서
1. 문제 요약
ChatScreen의 사진 미리보기 바텀시트 (_showImagePreviewSheet in [c:/flutter/msm/momsmom/lib/features/chat/chat_screen.dart])에서
카메라로 찍은 사진
앨범에서 선택한 사진
모두 동일한 미리보기 레이아웃을 사용하고 있음.
현재 구현은:
Column(mainAxisSize: MainAxisSize.min) + 큰 Image.file + viewInsets.bottom 패딩만 사용.
세로가 긴 영수증/문서 사진, 작은 화면, 키보드가 동시에 겹치면 BOTTOM OVERFLOWED BY XXX PIXELS 발생.
2. 개선 목표
카메라/앨범 모두에서:
어떤 기기/해상도/키보드 높이에서도 overflow 없이 사용 가능하게 만들기.
사진은 충분히 크게 보여주되, 공간이 부족하면 전체가 스크롤되도록 처리.
3. 레이아웃 변경 설계
공통 포인트
카메라, 앨범 모두 최종적으로 _showImagePreviewSheet를 호출하므로,
이 함수의 레이아웃만 안전하게 바꾸면 두 플로우를 한 번에 해결 가능.
3.1 바텀시트 루트 구조 변경
기존:
showModalBottomSheet(isScrollControlled: true, builder: (ctx) => Padding(..., child: Column(mainAxisSize: min, children: [Image, TextField, Button])))
변경(개념):
showModalBottomSheet( context: context, isScrollControlled: true, builder: (sheetContext) { final bottomInset = MediaQuery.of(sheetContext).viewInsets.bottom; return SafeArea( child: LayoutBuilder( builder: (context, constraints) { final maxHeight = constraints.maxHeight; return Padding( padding: EdgeInsets.only( left: 16, right: 16, top: 16, bottom: bottomInset, // 키보드 높이만큼 ), child: SizedBox( height: maxHeight, child: SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints( // 전체 컨텐츠가 화면 높이를 넘지 않도록 상한 설정 maxHeight: maxHeight - bottomInset, ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 이미지 + 텍스트 + 버튼 ], ), ), ), ), ); }, ), ); }, );
3.2 이미지 높이 제한
이미지가 화면을 과도하게 차지하지 않도록, 최대 높이를 비율로 제한:
final imageMaxHeight = (maxHeight - bottomInset) * 0.45; // 화면의 약 45% SizedBox( height: imageMaxHeight, width: double.infinity, child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Image.file( imageFile, fit: BoxFit.cover, ), ), );
세로로 매우 긴 카드영수증/서류 사진이라도,
상단 40~50% 영역 안에 안전하게 들어가고, 나머지는 스크롤로 확인.
3.3 텍스트 입력 + 전송 버튼 레이아웃
이미지 아래에 기존처럼 배치하되, 같은 스크롤 영역 안에 포함:
const SizedBox(height: 12), TextField( controller: textController, maxLines: 3, decoration: const InputDecoration( hintText: '사진에 대해 짧게 설명해 주세요 (선택)', border: OutlineInputBorder(), ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: FilledButton( onPressed: onSend, child: const Text('전송'), ), ),
키보드가 올라오면:
bottomInset만큼 패딩을 준 상태에서
전체가 SingleChildScrollView 안에 있으므로,
화면이 작아져도 overflow 대신 스크롤.
4. 적용 범위 (파일별)
[c:/flutter/msm/momsmom/lib/features/chat/chat_screen.dart]
_showImagePreviewSheet에서:
Padding + Column(min) 구조를 SafeArea + LayoutBuilder + SingleChildScrollView + ConstrainedBox 구조로 변경.
Image.file을 SizedBox(height: imageMaxHeight)로 감싸 최대 높이 제한.
_pickImage (카메라, 앨범 공통)에서 가져온 File은 동일한 _showImagePreviewSheet를 사용하므로,
별도 분기 없이 카메라/앨범 overflow를 동시에 해결.
5. 기대 효과
세로로 긴 영수증/문서 사진이든, 일반 사진이든:
미리보기가 항상 화면 안에 들어오고,
키보드가 올라와도 BOTTOM OVERFLOWED BY XXX PIXELS 에러 없이 사용 가능.
카메라와 앨범 플로우 모두 같은 개선을 공유하므로,
추가 유지보수 포인트도 한 군데로 유지된다..
짠!
고쳐졌습니다.
.
앨범에서 선택도 잘 작동합니다.
.
동영상 녹화해서 올리려고 했는데,
폰 램이 모자란지 꺼지네요 ㅎㅎ
.
무튼 성공적 으로 오코오니 님의 피드백 반영 완료입니다!
.
■ 느낀점 및 향후 계획
사진 입력은 되는데,
아직 사진을 토대로 말을 하지는 않는 것 같습니다.
.
그 부분도 마져 고쳐봐야겠습니다!