지난주엔 UI 부품을 깎았다.
말투를 정리했다.
앱이 어떤 표정으로 “안내/경고/에러/권유”를 말할지 통일했다.
근데… 그 다음 주에는 이런 생각이 들었다.
“좋은 말투만으로는 부족하다.”
말을 어떻게 하느냐만큼 중요한 게 있다.
어디서 말을 하느냐.
그리고 그 말이 ‘어떻게 이동하느냐’.
이 주는 그래서, 화면을 더 예쁘게 만드는 주가 아니었다.
길을 만드는 주였다.
Turbo Native(iOS/Android)로 가기 위한 “내비게이션 규칙”을 코드로 박는 주.
1. “서버가 모달을 닫아준다”는 발상
웹에서 모달은 보통 자바스크립트가 닫는다.
버튼 누르면 hide() 하고 끝.
근데 Turbo Native로 갈 생각을 하면 이야기가 달라진다.
모달이 화면의 일부이면서도, 네이티브에선 “별도의 화면”처럼 취급된다.
여기서 어설프게 닫았다 열었다 하면 경험이 깨진다.
그래서 이번 주에 잡은 핵심 원칙은 이거다.
모달은 클라이언트가 닫는 게 아니라,
서버 응답이 닫는다.
구조는 단순하다.
- 성공하면: turbo_stream.update(“modal”, “”) 로 모달을 비우고, turbo_stream.update(“flash”, …) 로 메시지 보여주고
- 실패하면(422): 모달 안에 폼을 다시 렌더해서 그대로 수정하게 한다
즉, 모달의 열림/닫힘을 “서버의 결정”으로 표준화했다.
이게 왜 중요하냐면,
Turbo Native에서 “화면 전환”은 곧 “신뢰”라서 그렇다.
사용자가 눌렀는데 앱이 이상하게 버벅이거나, 닫힌 줄 알았는데 남아있거나,
그 순간 앱은 바로 신뢰를 잃는다.
이번 주는 그 신뢰를 지키는 쪽으로 길을 냈다.
2. 모달 프레임 표준화: report_modal → modal (하지만 호환은 유지)
현실은 늘 타협이 필요하다.
이미 report_modal이라는 레거시 프레임이 깔려 있었고,
그걸 하루아침에 다 갈아엎는 건 “가능”은 해도 “현명”하진 않다.
그래서 전략은 이랬다.
- 새로운 표준 프레임:
modal - 기존 레거시:
report_modal유지 - 대신 서버가 닫을 때는 둘 다 닫는다
즉, “공존하는 동안은 둘 다 챙긴다.”
이런 방식은 개발자로서 약간 꼴사나울 때도 있는데,
나는 요즘 오히려 이런 게 성숙한 선택이라고 생각한다.
완벽주의로 크게 부수는 사람보다,
점진적으로 갈아타는 사람이 오래 간다.
3. flash도 규칙 안으로 넣기: turbo-frame “flash”
Turbo Streams로 플래시를 업데이트하려면
플래시가 “타겟”이 있어야 한다.
그래서 플래시를 그냥 렌더하는 대신,
<turbo-frame id="flash">로 감싸서- 서버가
turbo_stream.update("flash", ...)로 통일해서 보낼 수 있게 만들었다
이건 작은 수정 같지만, Turbo Native 관점에서는 엄청 큰 변화다.
서버가 말하는 방식(플래시)이,
네이티브에서도 동일하게 작동하는 기반이 생긴다.
즉, “웹 전용 트릭”이 아니라 “플랫폼 공용 규칙”이 된다.
4. /me 고정 URL — 딥링크 안정성의 핵심
내가 이번 주에 제일 마음에 들었던 결정은 이거다.
/me 로 고정한다.
프로필을 /users/:id로 두면,
Turbo Native에서 탭/딥링크/캐시가 점점 복잡해진다.
- 로그인 전후에 ID가 바뀌고
- 딥링크가 사용자마다 달라지고
- PathConfiguration 매핑도 길어진다
반면 /me는 단순하다.
- “내 프로필”은 항상 /me
- 네이티브 탭도 항상 같은 URL
- 캐시도 안정적
- PathConfiguration도 깔끔해진다
이건 개발 편의성만의 문제가 아니다.
사용자 경험의 안정성이다.
앱이 “항상 같은 길”로 나를 데려간다는 감각.
그게 신뢰다.
5. PathConfiguration: 이제 진짜 Turbo Native가 보이기 시작했다
Turbo Native의 핵심은 결국 이 한 파일로 드러난다.
PathConfiguration.json
이 파일은 말하자면
“이 URL은 네이티브에서 어떤 방식으로 보여줄 거냐”를 정하는 지도다.
이번 주에 정리된 방향:
- 기본 화면들: context: default — /posts, /chat_rooms, /me
- 모달로 띄울 화면들: context: modal — 신고, 리뷰
- 인증 계열: presentation: replace로 더 안전하게
여기서 중요한 포인트는 하나.
이제 조비엣은 “뷰”를 만드는 게 아니라 “네비게이션 규칙”을 만든다.
이 순간부터 프로젝트는
웹앱이 아니라 “앱”의 형태를 갖추기 시작한다.
6. 이번 주의 결론: “UI는 표면이고, 길은 뼈대다”
지난주엔 말투를 다듬었다.
이번 주엔 길을 냈다.
그리고 나는 요즘 이런 생각을 한다.
표면은 언제든 바꿀 수 있다.
하지만 길은 한 번 잘못 만들면 계속 돌아가게 된다.
그래서 이번 주의 작업은
겉보기엔 리팩터링이지만, 실제론 이런 선언이었다.
조비엣은 Turbo Native로 갈 수 있는 구조로 간다.
그리고 그 구조의 첫 단추는 “모달”과 “/me”와 “PathConfiguration”이다.
다음 주 예고 (3주차)
이제 남은 건 한 가지가 크다.
“이미지 확대(미디어)와 라우팅”
- 이미지 확대가 Turbo Native에서 어떻게 뜰지
- 모달인지, 별 화면인지
- 캐시와 뒤로가기 경험까지
여기까지 잡히면
iOS/Android Turbo Native 템플릿을 붙일 때,
앱이 “그냥 연결되는” 느낌이 날 거다.
조비엣은 지금 그 문턱 앞에 와 있다.
