# 이미지 크롭 범위 보정 PRD ## 문서 정보 | 항목 | 내용 | |---|---| | 문서 상태 | 구현 기준 확정 | | 작성일 | 2026-08-03 | | 최종 수정일 | 2026-08-03 | | 대상 제품 | SodaLive 이미지 선택 후 크롭 편집기 | | 관련 구현 계획 | `docs/20260803_이미지_크롭_범위_보정/plan-task.md` | | 관련 과거 기록 | `docs/plan-task/20260317_이미지등록크롭재구현.md`, `docs/plan-task/20260317_이미지선택크롭지연및미적용수정.md` | ## 1. Overview 이미지를 선택한 뒤 표시되는 공통 `ImageCropEditorView`가 crop 비율에 맞는 원본 최대 범위를 사용하도록 보정한다. 외부 cropper 의존성은 추가하지 않고 현재 편집 흐름과 서버 전송 규격을 유지한다. ## 2. Problem Statement - 고정 1:1 crop 영역은 canvas 최소 변의 72%로 계산되어 원본 표시 영역보다 불필요하게 작을 수 있다. - 자유 crop 영역의 초기 크기와 최대 크기도 canvas 기준이라 원본 표시 영역과 일치하지 않는다. - 따라서 crop 비율에 따라 원본의 가로 또는 세로 한 축 전체를 포함할 수 있어도 더 작은 영역만 잘린다. 문제 해결 여부는 세로·가로 이미지에서 현재 crop 비율을 유지하는 최대 사각형이 원본 표시 영역의 가로 또는 세로 한 축과 일치하는지로 판정한다. ## 3. Goals - 어떤 crop 비율에서도 원본 표시 영역 안에 들어가는 최대 crop 사각형을 계산한다. - 세로 이미지는 가능한 경우 표시된 이미지의 전체 가로를, 가로 이미지는 전체 세로를 crop 영역에 포함한다. - 자유 crop은 최대 범위를 fitted image 기준으로 제한하면서 기존 모서리 resize 동작을 유지한다. - 최종 crop 결과의 긴 변 최대 800px 정책과 기존 서버 multipart 경로를 유지한다. ## 4. Non-Goals - 외부 cropper 라이브러리 추가 또는 기존 편집기 교체 - 새로운 crop 비율 선택 UI 추가 - 이미지 선택 화면, 업로드 API, multipart 필드 변경 - 회전, 반전, 원근 보정 기능 추가 ## 5. 핵심 사용자 흐름 1. 사용자가 기존 화면에서 이미지를 선택한다. 2. 앱이 이미지를 정규화하고 `ImageCropEditorView`를 표시한다. 3. 편집기는 현재 crop 비율에 맞는 최대 영역을 fitted image 안에서 계산한다. 4. 사용자가 이동·확대 또는 자유 crop resize 후 적용한다. 5. 앱은 기존처럼 crop 결과를 긴 변 최대 800px로 축소해 각 ViewModel의 기존 업로드 경로로 전달한다. ## 6. 기능 요구사항 | ID | 상태 | 요구사항 | 수용 기준 | |---|---|---|---| | `CROP-001` | 확정 | crop 비율을 유지하는 최대 크기를 fitted image 안에서 계산한다. | 반환 rect의 가로 또는 세로가 fitted image의 같은 축과 일치하고 두 축 모두 경계를 넘지 않는다. | | `CROP-002` | 확정 | 고정 1:1 crop에 최대 범위 계산을 적용한다. | 세로 이미지는 전체 가로, 가로 이미지는 전체 세로를 초기 crop 영역으로 사용한다. | | `CROP-003` | 확정 | 자유 crop의 초기 영역과 resize 상한을 fitted image 기준으로 계산한다. | 초기 영역은 현재 기본 비율의 최대 크기이며 모서리 resize 결과가 fitted image 크기를 넘지 않는다. | | `CROP-004` | 확정 | 최종 전송 이미지 크기 정책을 유지한다. | `cropImage()`가 계속 `resizedToMaxDimension(800)` 결과를 반환한다. | | `CROP-005` | 확정 | 기존 호출부 계약을 유지한다. | `ImageCropAspectPolicy`, `onCancel`, `onComplete(UIImage)`와 5개 호출부 수정이 없다. | ## 7. 기술 제약 - iOS 16.6 이상, SwiftUI와 UIKit을 유지한다. - geometry 계산은 UI와 분리된 순수 함수로 두어 독립 실행 검증이 가능해야 한다. - 테스트 번들 타깃이 없는 현재 저장소 구조에서는 `swiftc`로 순수 geometry check를 실행한다. - `Pods/**`, `generated/**`, 업로드 API는 수정하지 않는다. ## 8. 성공 기준 - [ ] 세로 fitted image `350x700`, 비율 `1:1`에서 crop 크기가 `350x350`이다. - [ ] 가로 fitted image `400x200`, 비율 `4:3`에서 crop 크기가 약 `266.67x200`이다. - [ ] fitted image와 crop 비율이 같으면 전체 fitted image 크기를 반환한다. - [ ] 0 이하 크기 또는 비율은 `.zero`를 반환한다. - [ ] 자유 crop resize 상한이 fitted image를 넘지 않는다. - [ ] 앱 빌드가 성공하고 최종 crop 결과의 긴 변 최대 800px 정책이 유지된다. ## 9. Open Questions 해당 없음. ## 10. Decision Log | 날짜 | ID | 상태 | 결정 | 근거 | 영향 요구사항 | |---|---|---|---|---|---| | 2026-08-03 | `DEC-001` | 확정 | 외부 라이브러리 대신 기존 cropper geometry를 보정한다. | 현재 문제는 공통 `cropSize`와 fitted image 경계 계산에서 발생하며 한 컴포넌트에서 해결 가능하다. | `CROP-001~005` | | 2026-08-03 | `DEC-002` | 확정 | 1:1은 예시이며 모든 crop 비율에 최대 fitted 영역 규칙을 적용한다. | 사용자 확인 | `CROP-001~003` | | 2026-08-03 | `DEC-003` | 확정 | 최종 이미지 긴 변 최대 800px 정책을 유지한다. | 서버 전송 크기를 기존과 동일하게 유지한다는 사용자 확인 | `CROP-004` |