Wonny
Wonny
🏅 AI 마스터
🎻 루키 파트너
🏅 베스트오브베스트

[맘스맘 개발기] 260206 사진찍기 & 앨범에서 선택 기능 구현하기

■ 소개

목표: 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 에러 없이 사용 가능.
카메라와 앨범 플로우 모두 같은 개선을 공유하므로,
추가 유지보수 포인트도 한 군데로 유지된다.

.

짠!

고쳐졌습니다.

.

앨범에서 선택도 잘 작동합니다.

.

동영상 녹화해서 올리려고 했는데,

폰 램이 모자란지 꺼지네요 ㅎㅎ

.

무튼 성공적으로 오코오니 님의 피드백 반영 완료입니다!

.

■ 느낀점 및 향후 계획

사진 입력은 되는데,

아직 사진을 토대로 말을 하지는 않는 것 같습니다.

.

그 부분도 마져 고쳐봐야겠습니다!

1
2개의 답글
지피터스 뉴스레터

이번 주 AI, 딱 쓸 것만

매주 월요일 오후,
읽고 바로 써먹을 AI 소식만