[WallFit] 월핏: 그리드, 여백, 비율 - 2
옵션이 아니라 결과를 만드는 설계
WallFit에서 가장 많은 시간을 쓴 부분은
의외로 눈에 잘 띄지 않는 영역이었다.
그리드, 여백, 비율.
UI에서 보면 단순한 선택지처럼 보이지만,
이 앱에서는 이 세 가지가 사실상 전부였다.
그리드는 “배치 옵션”이 아니라 구조였다
WallFit에서 제공하는
1×2, 2×2, 2+1 같은 그리드는
장식적인 기능이 아니다.
사진을 몇 장 배치할 수 있느냐보다 중요한 건
각 사진이 어떤 비율과 크기로 잘리는가였다.
기존 앱들을 쓰면서 가장 불편했던 점은 이거였다.
- 그리드를 선택했는데
- 결과물이 예상과 다르고
- 저장된 이미지는 다시 쓰기 애매해지는 경우
그래서 WallFit에서는
그리드를 “선택지”가 아니라
결과를 규정하는 구조로 취급했다.
그리드를 고르면
- 각 셀의 비율이 고정되고
- 여백 계산이 미리 결정되며
- 최종 출력 해상도가 예측 가능해진다
사용자는 옵션을 고르는 게 아니라,
결과를 고르고 있는 셈이다.
여백은 미세 조정 기능이 아니었다
여백은 대부분의 앱에서
“있으면 좋은 옵션” 정도로 취급된다.
WallFit에서는 반대로 접근했다.
여백은 디자인 요소가 아니라
이미지의 인상을 결정하는 핵심 변수였다.
- 바깥 여백이 조금만 달라도
전체가 답답해 보이거나 허전해지고 - 내부 여백이 조금만 틀어져도
사진 간 균형이 무너진다
그래서 여백은
단순히 숫자를 바꾸는 슬라이더가 아니라,
결과물이 어떻게 보일지를 예측하게 만드는 장치로 설계했다.
중요했던 원칙은 하나였다.
미리보기에서 보이는 그대로 저장돼야 한다.
화면에서는 괜찮아 보이는데
저장하고 나면 인상이 달라지는 경험은
이 앱에서 허용하지 않기로 했다.
비율과 해상도에 집착한 이유
WallFit은 편집 앱이지만,
목표는 편집이 아니라 사용이다.
결과물은
- 배경화면으로 쓰이거나
- 메시지 배경으로 쓰이거나
- 단순히 저장돼 다시 열어보게 된다
그렇다면 기준은 명확했다.
- 디바이스 기준 해상도를 명확히 할 것
- 스케일을 애매하게 타협하지 않을 것
- 고해상도 출력이 기본값일 것
“화면에서 예쁜 이미지”가 아니라
실제로 써도 깨지지 않는 이미지를 만드는 게 목표였다.
이 기준 때문에
기능이 단순해 보일 수는 있어도,
결과물은 흔들리지 않게 만들고 싶었다.
AI와 함께 설계한 기술적인 부분
WallFit을 만들면서
코드를 전부 혼자 끌고 간 건 아니다.
그리드 계산, 여백 로직, 해상도 처리 같은 부분은
Claude와 GPT를 병행해서 정리했다.
중요한 점은 하나였다.
방향은 사람이 정하고,
구조와 검증에서 AI를 활용한다.
AI가 “무엇을 만들지”를 정한 적은 없다.
대신, 내가 정한 기준이 논리적으로 일관되는지를 검증하는 데 도움을 줬다.
1. 그리드 계산 로직
WallFit의 그리드는 단순히 rows × columns가 아니다.
각 셀은 다음을 동시에 만족해야 했다.
- 전체 캔버스 비율 유지
- 외부 여백 반영
- 내부 여백 반영
- 디바이스 기준 해상도 대응
- 정수 픽셀 정렬 (pixel snapping)
예를 들어, 2x2 그리드를 계산하는 단순화된 코드 일부는 다음과 같다.
struct GridLayout {
let rows: Int
let columns: Int
let outerPadding: CGFloat
let innerSpacing: CGFloat
}
func calculateCellFrames(
canvasSize: CGSize,
layout: GridLayout
) -> [CGRect] {
let totalInnerSpacingWidth = CGFloat(layout.columns - 1) * layout.innerSpacing
let totalInnerSpacingHeight = CGFloat(layout.rows - 1) * layout.innerSpacing
let availableWidth = canvasSize.width - (layout.outerPadding * 2) - totalInnerSpacingWidth
let availableHeight = canvasSize.height - (layout.outerPadding * 2) - totalInnerSpacingHeight
let cellWidth = floor(availableWidth / CGFloat(layout.columns))
let cellHeight = floor(availableHeight / CGFloat(layout.rows))
var frames: [CGRect] = []
for row in 0..<layout.rows {
for col in 0..<layout.columns {
let x = layout.outerPadding + CGFloat(col) * (cellWidth + layout.innerSpacing)
let y = layout.outerPadding + CGFloat(row) * (cellHeight + layout.innerSpacing)
frames.append(CGRect(x: x, y: y, width: cellWidth, height: cellHeight))
}
}
return frames
}이 코드에서 중요한 건 복잡함이 아니라,
모든 계산이 예측 가능해야 한다는 점이었다.
AI와 함께 한 작업은 이런 것들이었다.
floor를 쓰는 게 맞는지,round가 더 안전한지 검토- 마지막 셀에서 1px 누락이 발생하는 케이스 점검
- 내부/외부 여백이 극단값일 때의 안정성 테스트
- 특정 해상도(예: 1290×2796)에서의 결과 검증
Claude는 전체 구조를 리뷰하는 데 강했고,
GPT는 “이 수식에서 깨질 수 있는 케이스”를 빠르게 찾아냈다.
AI는 코드를 대신 작성했다기보다,
수식을 리뷰하는 동료 개발자에 가까웠다.
2. UI 프리뷰와 실제 저장 이미지 일치
WallFit에서 가장 중요했던 원칙은 이것이었다.
화면에서 보이는 그대로 저장돼야 한다.
SwiftUI는 pt 단위로 레이아웃이 계산된다.
하지만 저장은 px 단위 이미지로 이뤄진다.
여기서 스케일 처리를 명확히 하지 않으면
프리뷰와 저장 결과가 미세하게 달라진다.
WallFit에서는 UIGraphicsImageRenderer를 사용해
명시적으로 scale을 지정했다.
func renderImage(from view: some View, size: CGSize, scale: CGFloat) -> UIImage {
let rendererFormat = UIGraphicsImageRendererFormat()
rendererFormat.scale = scale
let renderer = UIGraphicsImageRenderer(size: size, format: rendererFormat)
return renderer.image { context in
let hosting = UIHostingController(rootView: view)
hosting.view.bounds = CGRect(origin: .zero, size: size)
hosting.view.drawHierarchy(in: hosting.view.bounds, afterScreenUpdates: true)
}
}여기서 고민했던 건 다음이었다.
- 디바이스 scale(2x / 3x)을 그대로 쓸 것인가
- 고정 3x로 출력할 것인가
- fractional pixel이 발생할 경우 어떻게 보정할 것인가
AI는 여기서
“SwiftUI 렌더링 → UIKit 브리지 과정”의 내부 동작을 정리하는 데 도움을 줬다.
특히 도움이 됐던 건:
- pixel snapping이 필요한 이유 설명
- fractional coordinate가 흐림을 만드는 구조 분석
- scale과 size의 관계 정리
이 과정 덕분에
프리뷰와 저장 이미지의 오차를 최소화할 수 있었다.
3. 디자인과 아이콘 작업에서의 AI 활용
디자인 역시 혼자서 모든 결정을 감으로 내리지는 않았다.
아이콘 방향성, 컬러 밀도, 전체 UI 톤에 대해
텍스트 기반으로 정리하고 비교하면서
판단을 빠르게 내렸다.
AI는 여기서 이런 역할을 했다.
- 과한 그래디언트 사용 시 인상 변화 분석
- 도구형 앱 vs 감성형 앱의 시각적 차이 구조화
- 아이콘이 기능을 설명해야 하는지, 브랜드를 강조해야 하는지 정리
다만 최종 선택은 전부 사람이 했다.
AI는 선택지를 정리하는 데 유용했지만,
“이게 맞다”를 결정하는 건 결국 취향과 기준의 문제였다.
4. AI와 협업하면서 생긴 기준
WallFit을 만들면서 명확해진 건 하나다.
- 요구사항이 모호하면 AI 결과도 모호하다.
- 기준이 명확하면 AI는 꽤 정확하게 따라온다.
- 최종 책임은 여전히 사람에게 있다.
AI는 개발을 대신해주는 도구라기보다
사고를 빠르게 구조화해주는 인터페이스에 가깝다.
WallFit에서 AI는
코드를 자동 생성하는 기계가 아니라
설계를 검증하는 가속기였다.
디자인과 아이콘, 그리고 전체 톤
디자인에서도 기준은 동일했다.
- 과하게 꾸미지 않을 것
- 결과물이 주인공일 것
- 기능이 디자인을 압도하지 않을 것
아이콘과 앱 전반의 디자인도
“배경화면 앱처럼 보이는 것”보다는
도구처럼 보이게 만드는 데 집중했다.
눈에 띄는 디자인보다는
계속 써도 질리지 않는 디자인이 목표였다.
그래서 색상, 버튼, 아이콘 모두
한 번에 눈길을 끌기보다는
시간이 지나도 부담 없는 쪽을 택했다.
만들지 않기로 한 것들
이 과정에서
의도적으로 만들지 않은 기능들도 많다.
- 무제한 효과
- 과도한 필터
- 결과물보다 편집 재미에 치우친 기능
이런 것들은
WallFit이 추구하는 방향과 맞지 않았다.
이 앱은
“만지는 재미”보다
완성된 이미지를 쓰는 경험에 더 가깝다.
그래서 선택지는 적어 보일 수 있지만,
그 대신 결과는 예측 가능하게 만들고 싶었다.
다음으로 남은 이야기
그리드, 여백, 비율까지 정리하고 나니
그 다음 고민은 자연스럽게 이쪽으로 넘어갔다.
- 무료 앱을 어떻게 운영할 것인가
- 광고는 어디까지 허용할 것인가
- 어떤 데이터만 수집하는 게 맞는가
다음 글에서는
WallFit을 무료로 운영하면서
광고와 Analytics를 어떻게 받아들였는지,
그리고 어떤 기준으로 선을 그었는지 정리해보려고 한다.
#wallfit #월핏 #app #ai #llm #배경화면 #만들기

