postflow 개선기: 오토포스트가 되기까지 고친 것들

TL;DR
- 첫 글에서 만든 postflow를 열흘 동안 실제로 쓰면서 PR 38개(#20 ~ #57)를 합쳤다. 화면 이름도 오토포스트로 바꿨다.
- 기능 추가보다 "쓰다 보니 이상한 것"을 고친 일이 훨씬 많았다. 사진 비율, 투명 배경, 엑박, 저장 버튼이 엉뚱한 곳으로 가는 문제 같은 것들이다.
- 버그 대부분은 실제 화면에서 재현해야 원인이 보였다. 코드만 읽어서는 찾지 못한 것들이다.
들어가며
첫 글은 카카오톡 테마 상품 하나를 등록하면 네이버 블로그, 포스타입, 인스타그램 글을 한 번에 만들어 주는 도구 postflow를 만든 이야기였다. 만들고 나서 실제 상품을 올리며 열흘 정도 써 보니, 처음 만들 때는 보이지 않던 것들이 하나씩 나왔다.
- postflow를 만들기 전에 손으로 올린 글은 도구 안에 없었다.
- 인스타에 올린 사진이 프로필 화면에서 양옆이 조금씩 잘려 보였다.
- 투명 배경으로 만든 캐릭터가 흰 사각형을 달고 나왔다.
- 사진을 고친 뒤 [적용]을 누르면 갑자기 "테라박스 URL을 입력하세요"가 떴다.
이 글은 그런 문제를 하나씩 따라가며 무엇이 원인이었고 어떻게 고쳤는지 정리한 기록이다. 화면이 어떻게 바뀌었는지도 함께 담았다.
한눈에 보기
| 영역 | 바뀐 것 |
|---|---|
| 화면 | 이름을 오토포스트로, 토스 느낌 디자인, 새로고침 없는 이동, 라이트·다크 모드, 로고 |
| 가져오기 | 예전 네이버·인스타·포스타입 글을 상품으로, 원래 사진과 캡션까지 |
| 사진 관리 | 플랫폼마다 들어가는 사진을 따로 보기, 출처별 묶음, 인스타 첫 장 고르기 팝업, 크게 보기 |
| 이미지 품질 | 4:5와 3:4 비율 정리, 프로필 그리드에서 안 잘리게, 투명 배경, 블로그 첫 사진 아이폰 목업 |
| 안정성 | 엑박 없는 다시 그리기, 두 번 클릭 막기, 작업 중 표시, 배포가 게시를 끊지 않게 |
1. postflow에서 오토포스트로
토스 느낌의 화면
처음 화면은 기능 위주라 정보가 빽빽했다. 매일 여는 도구라서 읽기 편한 게 중요했다. 기준을 토스 앱으로 잡았다.
- 글꼴은 Pretendard, 회색 바탕 위에 흰 카드, 큰 숫자와 작은 설명
- 이모지를 모두 빼고, 상태는 색 점과 글자 배지로
- 왼쪽 사이드바에는 메뉴만, 위쪽 헤더에는 로고·화면 모드·로그아웃
- 라이트, 다크, 시스템 모드를 고르고 브라우저에 기억

헤더는 한 번 없앴다가 되살렸다. 사이드바로 옮기면서 헤더를 지웠더니 "로그아웃이랑 밝기 전환은 위에 있어야지"라는 피드백이 바로 나왔다. 늘 쓰는 버튼은 늘 있던 자리에 있어야 한다.
글 목록에는 플랫폼 아이콘을 넣었다. 올린 곳은 원래 색, 안 올린 곳은 흐리게, 진행 중이거나 실패한 곳은 점으로 표시한다. 제목이 비슷한 글이 많아서 썸네일도 붙였다.

다크 모드는 화면을 그리기 전에 적용해야 한다. 페이지가 뜬 다음에 바꾸면 흰 화면이 한 번 번쩍인다. 그래서 <head> 맨 앞의 작은 스크립트에서 저장된 값을 읽어 data-theme을 먼저 붙인다.

새로고침 없는 이동: htmx boost와 idiomorph
화면은 Spring Boot + Thymeleaf로 서버에서 그린다. React로 옮길지도 고민했지만, 혼자 쓰는 도구에 그만한 비용을 들일 이유가 없었다. 대신 htmx의 boost를 켰다. 링크와 폼을 뒤에서 요청하고, 받은 HTML을 idiomorph로 지금 화면과 비교해 바뀐 부분만 고친다. 스크롤 위치와 열어 둔 탭이 그대로 남아서, 체감상 SPA와 거의 같아졌다.
이 선택이 나중에 버그 하나를 만든다. 7장에서 다룬다.
로고
로고는 파란 그라데이션의 둥근 네모 안에 하얀 종이비행기다. "알아서 보내 준다"는 뜻이다.

재미있는 일이 하나 있었다. 처음에는 글자 사이를 좁게(letter-spacing: -0.04em) 잡았는데, "오토포스트"에 밑줄이 그어진 것처럼 보였다. CSS를 다 뒤져도 text-decoration은 없었다. 확대해 보니 원인은 글자였다. 오·토·포·스·트는 다섯 글자 모두 맨 아래에 가로획(ㅗ, ㅡ)이 있어서, 자간을 좁히면 그 획들이 이어져 한 줄처럼 보인다. 자간을 넓히자 사라졌다.
2. 예전 글을 상품으로 가져오기
문제
postflow를 쓰기 전에 손으로 올린 카톡 테마 글이 네이버에 25개 있었다. 이 글들은 도구 안에 없으니 표지를 새로 바꾸거나, 인스타에 다시 올리거나, 통계에 넣을 수 없었다.
어디서 무엇을 읽나
로그인 없이 공개된 화면만 읽는다.
| 출처 | 읽는 곳 | 가져오는 것 |
|---|---|---|
| 네이버 블로그 | 글 목록 API(PostTitleListAsync), 글 본문 | 제목, 날짜, 본문 사진, 테라박스 주소 |
| 포스타입 | 채널 화면, 글 화면 | 글 번호, 제목, 발행 시각, 대표 이미지 |
| 인스타그램 | Graph API /me/media | 게시물 주소, 캡션, 캐러셀 사진 전부 |
자잘한 함정이 몇 개 있었다.
- 네이버 글 목록 응답은 JSON처럼 생겼지만 작은따옴표가
\'로 이스케이프돼 있어서, 그대로는 JSON 파서가 읽지 못한다. - 인스타 API의
fields=children{media_url}에는 중괄호가 들어간다. Spring의RestClient가 이걸 URI 템플릿 변수로 해석해서 요청이 깨졌다. 필드 값을 통째로 변수로 넘겨 해결했다. - 네이버 사진은 블로그 밖에서 바로 띄울 수 없어서 서버가 받아 넘긴다. 작은 크기(
w240)는 404가 나서w466을 쓴다.
제목이 대부분 "○○ 카톡 테마"라 비슷했다. 그래서 어떤 인스타 게시물·포스타입 글과 짝을 지을지 고르는 화면을 썸네일 카드로 만들었다.
사진은 출처를 기억한다
처음에는 가져온 사진을 한데 모았다. 그런데 네이버 글 사진과 인스타 게시물 사진은 크기도 쓰임도 다르다. 그래서 사진마다 출처(made_by = import:naver / import:instagram)를 적고, 네이버 사진은 블로그에만, 인스타 사진은 인스타에만 넣도록 바꿨다.
캡션도 원래대로
가져온 글을 인스타에 다시 올렸더니 캡션이 예전 게시물과 달랐다. "블러쉬 하트 핑크 시리즈를 출시했습니다!" 같은 시리즈 소개가 빠지고, 태그도 기본 태그 5개만 붙었다.
오토포스트의 인스타 캡션은 템플릿과 AI가 쓴 소개 문단으로 만든다. 그런데 가져온 글에는 AI 소개가 비어 있었다. 그래서 "인스타 캡션 전체" 칸을 따로 두고, 여기에 글이 있으면 템플릿 대신 그대로 쓰게 했다. 가져올 때는 예전 게시물의 캡션을 이 칸에 채운다.
여기서 한 번 더 걸렸다. 이미 다시 올린 글은 게시 기록이 새 게시물을 가리키고 있어서, 캡션을 가져오면 방금 올린 템플릿 캡션이 다시 들어왔다. 예전 게시물 주소를 직접 넘길 수 있게 해서 해결했다.
마이그레이션 두 번의 실수
- V39: 표지 작업을 기록하지 않으려고 상태 이력 트리거를 고치다가, 원래 있던
manual칸을 빠뜨렸다. V39 파일을 고치면 Flyway 체크섬이 깨지므로 V40을 새로 만들어 되살렸다. 공유된 마이그레이션은 고치지 않고 새로 쌓는다. - V41: 이미지 맞춤 방식에
card를 추가할 때 DB의 CHECK 제약에 넣는 걸 잊었다. 그래서 카톡 테마 사진을 저장할 때마다 500이 났다. 한참 전에 들어간 버그가 가져오기를 하면서 처음 드러났다.
3. 플랫폼마다 들어가는 사진을 따로
문제
상품 화면에는 사진이 한 줄로만 있었다. 이 사진이 인스타에 들어가는지, 블로그에 들어가는지, 첫 장은 무엇인지 버튼 상태를 하나하나 봐야 알 수 있었다.
바뀐 화면
사진 영역을 플랫폼별 묶음으로 나눴다. 각 묶음 맨 위에 실제로 올라가는 순서대로 작은 미리보기를 보여 준다.


- 인스타그램: 첫 장 → 넣은 사진 → 마지막 장
- 네이버 블로그: 블로그 첫 사진 → 본문 사진(정사각형)
- 포스타입: 대표 이미지
- 공통: 인스타와 블로그 카드에 같이 들어가는 캐릭터(대표 오브젝트)
- 오토포스트 글 목록: 썸네일. 어디에도 올라가지 않는다는 걸 따로 적었다.
썸네일을 처음에는 "공통"에 넣었다. 그러자 "인스타 썸네일이 왜 공통이야?"라는 질문이 나왔다. 실제로는 도구 안의 목록에만 쓰는 사진인데, 놓인 자리 때문에 어딘가에 올라가는 것처럼 보였다. 어디에 놓느냐가 곧 설명이다.
맨 아래 "모든 사진"도 가져온 곳별로 묶었다. 끌어서 순서를 바꾸는 건 묶음 안에서만 되고, 저장할 때는 모든 묶음의 순서를 한꺼번에 보낸다.

사진을 누르면 크게 보이고, ← → 키로 같은 묶음 안에서 넘길 수 있다.

4. 인스타 첫 장 고르기
팝업으로 고르기
[다른 사진으로 바꾸기]는 원래 파일 선택 창을 바로 열었다. 이미 올려 둔 사진 중에서 고르고 싶을 때가 더 많아서 팝업으로 바꿨다. 사진마다 번호와 출처가 붙어 있고, 아래에서 새 파일을 올릴 수도 있다.

"자동으로 만들기를 누르면 자꾸 예전으로 돌아가요"
다른 사진을 첫 장으로 고른 뒤 [자동으로 만들기]를 누르면, 예전 첫 장이 다시 나타난다는 제보였다. 재현해 보니 문제가 세 개 겹쳐 있었다.
- 자동 첫 장은 늘 1~3번 사진으로 그렸다. 방금 고른 사진은 무시했다. 그래서 누를 때마다 똑같은 그림이 나와 "되돌아간" 것처럼 보였다.
- 첫 장으로 쓴 사진은 캐러셀에서 빠지는데, 다시 돌아오지 않았다. 첫 장을 바꿀 때마다 캐러셀이 한 장씩 줄었다.
- "직접 고름"이 저장되지 않았다. 이게 가장 숨어 있던 원인이었다.
3번은 이런 구조였다.
// 컨트롤러: 트랜잭션 밖에서 읽은 상품을 넘긴다
images.useOriginalAsIgCover(products.findById(id).orElseThrow(), n);
// 서비스: @Transactional 안에서 넘겨받은 상품을 고친다
product.setAutoIgCover(false); // 저장되지 않는다spring.jpa.open-in-view를 꺼 두었기 때문에, 컨트롤러에서 읽은 엔티티는 서비스 트랜잭션이 시작될 때 이미 준영속(detached) 상태다. 여기에 값을 바꿔도 변경 감지가 일어나지 않는다. 그래서 첫 장은 "직접 고름"인데 상품에는 "자동"이 남아 있었고, 사진을 다시 그릴 때 둘이 섞였다. 서비스 안에서 ID로 다시 읽어 고쳤다.
나머지 둘은 "첫 장으로 고른 사진 번호"를 상품에 기억(V42)해서 풀었다. 첫 장을 바꾸면 그 사진을 캐러셀로 되돌리고, 자동 첫 장은 고른 사진을 맨 앞에 넣어 그린다. 사진 순서를 바꾸거나 사진을 빼면 번호도 따라간다.
5. 비율: 4:5, 3:4, 그리고 프로필 그리드
이번 작업에서 가장 오래 붙잡은 주제다.
인스타 API는 3:4를 받지 않는다
인스타 앱에서는 3:4 사진을 그대로 올릴 수 있다. 하지만 자동 게시에 쓰는 Content Publishing API는 4:5부터 1.91:1까지만 받는다. 그래서 오토포스트는 인스타 사진을 4:5(1080×1350)로 만든다.
문제는 예전에 앱에서 3:4로 올린 사진을 가져올 때 생겼다. 4:5 틀에 맞추느라 위아래가 잘렸고, 맨 위 프로필과 맨 아래 이모지 줄이 날아갔다. 이미 완성된 그림이니 자르지 않고 양옆을 채워 맞추도록 바꿨다.
프로필 그리드는 3:4로 자른다
그다음 피드백이 이거였다. "좌우가 인스타는 좀 잘려서 나오는데, 아무리 생각해도 비율이 잘못된 것 같아."
맞는 말이었다. 요즘 인스타 프로필 화면의 게시물 칸은 3:4다. 4:5 게시물을 3:4 칸에 넣으면 양옆이 약 3%씩 잘린다. 피드에서는 멀쩡한데, 프로필에서는 가장자리 말풍선이 잘려 보였다. 앱에서 3:4로 올린 예전 게시물만 온전히 보였던 이유도 이것이다.
3:4로 올릴 수는 없으니, 4:5 틀 안에 그리드에서 잘리지 않는 가운데 3:4 칸을 두기로 했다.
public BufferedImage toInstagramFrame(BufferedImage src, int width, int height, FitParams fit) {
if (width * 5 != height * 4) {
return toFrame(src, width, height, fit); // 1:1 등은 그대로
}
int safeW = (int) Math.round(height * 3 / 4.0); // 1350 → 1013
BufferedImage inner = toFrame(src, safeW, height, fit);
BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = canvas.createGraphics();
g.setColor(Color.decode(edgeColor(inner))); // 양옆 띠는 사진 가장자리 색
g.fillRect(0, 0, width, height);
g.drawImage(inner, (width - safeW) / 2, 0, null);
g.dispose();
return canvas;
}피드에서는 4:5 전체가 보이고, 프로필에서는 양옆 34px짜리 띠만 잘린다. 결과물에 프로필 칸 범위를 빨간 선으로 그려 확인했다.

자동 첫 장처럼 오토포스트가 처음부터 그리는 디자인은 이미 여백(80px)을 두고 있어서 이 처리를 하지 않는다.
"그럼 양 키티는 왜 안 잘렸어?"
같은 4:5로 올린 글인데 어떤 건 위아래가 안 잘려 보인다는 질문도 있었다. DB에 저장된 자르기 영역을 확인해 보니 그 글도 잘려 있었다. 다만 원본이 1:2짜리 긴 폰 캡처여서, 상태바와 입력창 부분만 잘려 나가 티가 나지 않았다. 반대로 이미 3:4로 다듬은 사진은 여유가 없어서, 조금만 잘라도 내용이 날아간다. 같은 "자르기"라도 원본에 따라 결과가 완전히 다르다.
6. 이미지 품질 버그
투명 배경이 흰 사각형으로
캐릭터 PNG는 분명 투명 배경이었다. 파일 헤더도 확인했다(color type 6, RGBA). 그런데 카드에는 흰 사각형째로 들어갔다.
원인은 이미지를 읽는 함수였다. 사진용 read()는 JPEG로 저장하기 좋게 투명한 곳을 흰색으로 채워 RGB로 바꾼다. 오브젝트도 같은 함수로 읽고 있었다. 투명도를 살려 읽는 readWithAlpha()를 따로 두어 해결했다.

블로그 첫 사진: 아이폰 목업과 화면 맞춤
블로그 첫 사진(네이버 대표 사진) 시안에서 두 가지 지적이 나왔다.
- 화면 칸 비율과 사진 비율이 달라서 양옆이 잘리고 레이아웃이 어색하다. 아이폰 목업에 넣어 달라.
- "제목+폰" 시안은 폰이 작아서 아래 여백이 너무 많다.

화면 칸에 사진을 넣는 규칙을 다시 정했다.
| 사진 | 넣는 방법 |
|---|---|
| 폰 캡처, 더 긴 스크롤 캡처 | 가로를 맞추고 위에서부터(아래만 잘림). 실제 폰 화면처럼 보인다 |
| 칸보다 넓은 사진(정사각형, 3:4) | 가로를 맞춰 위에 두고, 아래는 사진 아래 가장자리 색으로. 다이내믹 아일랜드에 가리지 않게 조금 내려서 |

중간에 한 번 헛발질도 했다. "긴 캡처를 먼저 쓰자"고 정렬했더니, 한 글에서 테라박스 저장 방법 안내 그림이 폰 화면에 들어갔다. 긴 캡처는 안내 그림인 경우가 많아서 우선순위에서 뺐다. 규칙은 그럴듯해도, 실제 데이터에 돌려 봐야 안다.
이 작업을 하다가 숨은 결합도 하나 발견했다. 블로그 첫 사진은 인스타 첫 장이 "자동"일 때만 만들어지고 있었다. 인스타 첫 장을 직접 고르면 블로그 첫 사진이 사라졌다. 둘은 서로 상관없는 것이라 분리했다.
첫 장을 바꾸면 사진이 전부 엑박
첫 장을 바꾸는 등 설정이 바뀌면, 그 글의 변환본(인스타·블로그·포스타입·썸네일)을 전부 다시 그린다. 코드는 이랬다.
// 고치기 전: 폴더를 먼저 통째로 지우고 하나씩 다시 그린다
FileSystemUtils.deleteRecursively(root.resolve("products/%d/%s".formatted(id, v.key())));
for (ProductImage original : originals(id)) {
renderVariants(product, series, original, wm);
}라즈베리파이에서는 다 그리는 데 20~30초가 걸린다. 그동안 화면을 보면 사진이 전부 404였다. 그래서 순서를 바꿨다.
// 고친 뒤: 새 파일을 임시 이름으로 다 쓴 다음 한 번에 바꿔 넣고,
private static void writeAtomically(Path target, byte[] bytes) throws IOException {
Path tmp = target.resolveSibling(target.getFileName() + ".tmp");
Files.write(tmp, bytes);
Files.move(tmp, target, REPLACE_EXISTING, ATOMIC_MOVE);
}
// 다 그린 뒤, 이번에 다시 그리지 않은(빠진 사진의) 파일만 수정 시각으로 골라 지운다같은 원인을 다시 확인할 때는 다른 창에서 1~2초마다 화면을 새로 열어 엑박 수를 셌다. 처리 중에도, 끝난 뒤에도 0이었다.
7. htmx가 폼을 가로챘다
증상
사진을 자르고 [적용]을 누르면 "상품 수정" 화면이 뜨면서 "테라박스 URL을 입력하세요. 버전명을 입력하세요."가 나왔다. 사진 조정은 상품 수정과 아무 관계가 없다.
재현이 먼저였다
상품 주소를 직접 열고 [적용]을 누르면 멀쩡했다. 그래서 처음에는 재현이 안 됐다. 사용자처럼 목록에서 상품을 눌러 들어간 뒤 누르니 그제야 재현됐다. 요청을 찍어 보니 이랬다.
POST /products/43 ← 사진 조정 주소가 아니라 상품 수정 주소
body: x=&y=&w=&h=&mode=pad&...목록에서 상품으로 넘어갈 때는 htmx boost가 화면을 바꾼다. 이때 htmx가 사진 조정 폼을 "boost할 폼"으로 먼저 잡아 두는데, 그 순간 폼에는 아직 action이 없었다. action은 팝업을 열 때 JS가 채운다. 그래서 htmx는 지금 주소(/products/43)를 보낼 곳으로 기억했다. 빈 값이 상품 수정으로 가니 검증 오류가 난 것이다. 폼에 hx-boost="false"를 붙이는 코드는 있었지만, htmx가 폼을 처리한 다음에 실행되고 있었다.
해결
그 자리에서 저장하는 폼은 자체 핸들러가 보내도록 되어 있다. 이 핸들러를 캡처 단계에 걸고, 처리한 뒤에는 이벤트 전파를 끊었다. 문서에서 캡처 단계로 받으면 폼 요소에 붙은 htmx 리스너보다 먼저 실행된다.
document.addEventListener('submit', async e => {
const form = e.target;
if (!isPartialForm(form)) return; // 일반 폼은 htmx에 맡긴다
e.preventDefault();
e.stopPropagation(); // htmx가 따로 보내지 않게
// ...fetch로 보내고 바뀐 영역만 교체
}, true); // 캡처 단계두 번 클릭과 작업 중 표시
서버 로그에는 낙관적 잠금 오류(ObjectOptimisticLockingFailureException)도 하나 있었다. [자동으로 만들기]를 처리하는 20초 동안 아무 반응이 없으니 한 번 더 누른 것이다. 두 요청이 같은 행을 지우려다 하나가 실패했다. 처리 중인 폼은 다시 보내지 않게 막았다.
근본 원인은 반응이 없는 20초다. 0.3초 넘게 걸리는 저장이면 화면을 흐리게 덮고, "작업 중이에요" 창에 막대가 차오르게 했다. 얼마나 걸릴지 모르니 막대는 처음엔 빠르게, 갈수록 느리게 90%까지 가고(90 × (1 − e^(−t/8))), 끝나면 100%를 채운 뒤 닫힌다.

8. 운영: 배포가 게시를 끊었다
이번 작업에서 가장 아찔한 일이었다.
인스타 게시는 서버 안에서 @Async로 돈다. 사용자가 41번 글을 인스타에 올리는 중이었는데, 그 사이에 내가 다른 수정을 배포했다. 컨테이너가 다시 켜지면서 게시가 중간에 끊겼다. 작업은 "게시 중(CLAIMED)"으로 멈춰 있었고, 아무도 그걸 실패로 바꿔 주지 않았다.
다행히 인스타 게시물 목록을 확인해 보니 올라가지 않은 상태였다. 작업을 "실패, 다시 게시해 주세요"로 바꾸고 두 가지 장치를 넣었다.
- 서버가 켜질 때 인스타 작업 중
READY·CLAIMED로 남은 것을 실패로 표시한다. 게시를 하던 프로세스는 이미 없으므로 그 상태는 거짓이다. - 배포 스크립트는 인스타 게시 중인 작업이 있으면 끝날 때까지(최대 5분) 기다렸다가 재시작한다.
for i in $(seq 1 60); do
busy=$(ssh "$PI" "... select count(*) from publish_job
where platform='instagram' and status in ('READY','CLAIMED')")
[ "${busy:-0}" = "0" ] && break
sleep 5
done배포 스크립트에는 앞서 다른 실수도 하나 반영했다. 테스트 하나가 실패한 상태로 합치고 배포한 적이 있어서, 그 뒤로는 테스트가 모두 통과해야만 배포하도록 막았다.
아직 남은 것
- 이미 올린 네이버 글의 표지만 바꾸는 기능은 만들었지만, 실제 글에서는 아직 한 번도 돌려 보지 않았다.
- 인스타는 이미 올린 게시물의 사진을 API로 바꿀 수 없다. 바뀐 첫 장은 다음 게시부터 들어간다.
- 앱에서 3:4로 올리던 때처럼 꽉 찬 그리드를 원하면, 지금 구조로는 양옆 띠가 최선이다. 언젠가 API가 3:4를 받으면 다시 볼 부분이다.
회고
잘한 것
- 고칠 때마다 실제 서버에서 확인했다. Playwright로 운영 화면을 열어 누르고, 요청과 결과 이미지를 직접 봤다. 엑박, 폼 가로채기, 블로그 첫 사진의 안내 그림은 모두 이 단계에서 잡혔다.
- 재현 경로를 사용자와 똑같이 맞췄다. 주소를 직접 열면 멀쩡하고 목록에서 들어가면 깨지는 버그는, 사용자의 동선을 그대로 밟지 않으면 찾을 수 없었다.
- DB를 먼저 봤다. "예전엔 3:4로 올렸는데?", "양 키티는 왜 안 잘렸어?" 같은 질문에는 저장된 사진 크기와 자르기 영역이 답을 갖고 있었다.
아쉬운 것
- 게시 중에 배포했다. 자동 게시가 서버 안에서 돈다는 걸 알면서도 배포 전에 확인하지 않았다. 지금은 스크립트가 기다리지만, 처음부터 있었어야 했다.
- 테스트가 실패한 채로 합친 적이 있다. 바로 고쳤지만, 그 뒤에야 배포를 테스트 통과에 묶었다.
- 규칙을 데이터에 돌려 보기 전에 정했다. "긴 캡처 먼저"는 그럴듯했지만 실제 글에서는 안내 그림을 골랐다.
마치며
첫 글을 "실물을 보는 일이 가장 많았다"로 마무리했다. 이번에도 같았다. 이번 실물은 사용하는 사람의 눈이었다. "좌우가 좀 잘려 보여", "이건 왜 공통이야", "누르면 이상한 화면이 떠" 같은 한 문장들이 대부분 진짜 버그로 이어졌다. 코드가 맞는지보다, 쓰는 사람에게 맞게 보이는지가 더 자주 틀려 있었다.