요즘IT
위시켓
AIDP - AX
Rise ERP
콘텐츠프로덕트 밸리
요즘 작가들컬렉션물어봐
놀이터
콘텐츠
프로덕트 밸리
요즘 작가들
컬렉션
물어봐
놀이터
새로 나온
인기
개발
AI
IT서비스
기획
디자인
비즈니스
프로덕트
커리어
트렌드
스타트업
서비스 전체보기
위시켓요즘ITAIDP - AXRise ERP
고객 문의
02-6925-4867
10:00-18:00주말·공휴일 제외
yozm_help@wishket.com
요즘IT
요즘IT 소개작가 지원
기타 문의
콘텐츠 제안하기광고 상품 보기
요즘IT 슬랙봇크롬 확장 프로그램
이용약관
개인정보 처리방침
청소년보호정책
㈜위시켓
대표이사 : 박우범
서울특별시 강남구 테헤란로 211 3층 ㈜위시켓
사업자등록번호 : 209-81-57303
통신판매업신고 : 제2018-서울강남-02337 호
직업정보제공사업 신고번호 : J1200020180019
제호 : 요즘IT
발행인 : 박우범
편집인 : 노희선
청소년보호책임자 : 박우범
인터넷신문등록번호 : 서울,아54129
등록일 : 2022년 01월 23일
발행일 : 2021년 01월 10일
© 2013 Wishket Corp.
로그인
요즘IT 소개
콘텐츠 제안하기
광고 상품 보기
AI

Next.js 모바일 웹을 일주일 만에 앱으로 바꾼 방법 ②

핸디
12분
2시간 전
144
에디터가 직접 고른 실무 인사이트 매주 목요일에 만나요.
newsletter_profile0명 뉴스레터 구독 중
요즘IT 사례 카드: 프론트엔드 개발자가 디자이너와 협업해 AI 에이전트와 자체 스킬, Expo, Figma MCP, 모노레포로 Next.js 모바일 웹을 React Native 앱으로 전환, POC 1주에 디테일 체크 목표 2주였으나 2주 출시는 못 함
요즘IT 사례 카드: 프론트엔드 개발자가 AI 에이전트와 자체 스킬, Expo·Figma MCP·모노레포로 Next.js 모바일 웹을 React Native 앱으로 전환한 사례. 1주 만에 POC를 만들었지만 2주 출시는 못 했다 <출처: 요즘IT>

 

지난 1편의 목표는 명확했습니다. 어떻게든 빠르게 앱 POC를 만들어내는 것이었죠. 이미 돌아가던 Next.js 모바일 웹을 SSOT(Single Source of Truth)로 삼아 모노레포를 세팅하고, mobile-design-sync 스킬을 돌려가며 리셋된 토큰을 3일 만에 다 태울 만큼 무섭게 화면을 찍어냈습니다.

 

그렇게 일주일 만에 그럴듯한 앱이 나왔으니, 남은 일주일 동안 가볍게만 손보면 “앱 출시까지 2주면 된다”는 말을 증명할 수 있을 줄 알았습니다. 하지만 1편 마지막에 말씀드렸듯이, 바로 출시까지 가는 데는 실패했습니다.

 

속도를 최우선으로 두고 달렸던 만큼, 실제 스토어에 올리려니 그때는 미처 보지 못했던 빈틈들이 한꺼번에 드러났기 때문입니다. 마크다운 문서상으로는 분명 화면들이 다 올라와 있었지만, 막상 휴대폰으로 하나씩 눌러보니 여기저기서 문제들이 쏟아졌습니다.

 

  • 모바일 웹과 앱은 본질적으로 UI/UX 프로세스가 달랐습니다. 디자인만 맞춘다고 끝이 아니라 터치 영역, 키보드, 그리고 어떤 화면을 웹뷰(WebView)로 두고 네이티브로 갈지까지 다시 짚어야 했습니다.
  • 겉모습은 같은데 실제 로직이 어긋난 부분이 생각보다 너무 많이 발견됐습니다.
  • 푸시 알림이나 본인인증처럼 모바일 앱에서 지원해야 하는 기본 기능의 추가 개발이 필요했고, 픽셀·토큰 단위의 세밀한 싱크와 반응형 디자인 이슈도 남아 있었습니다.

 

결국 1편이 ‘속도’를 무기로 빠르게 POC를 올린 이야기였다면, 이번 하편은 속도를 잠시 내려놓고 ‘디테일한 체크’에 집중한 기록입니다.

 

초벌로 찍어낸 앱을 실제 유저 손에 쥐여줘도 부끄럽지 않은 상태로 만들기 위해 무엇을 하나씩 점검하고 고쳤는지, 그리고 “앱 출시까지 2주면 된다”를 직접 테스트해 본 최종 후기를 풀어보고자 합니다.

 

 

디테일 체크 1. 모바일 웹과 앱은 UI/UX 프로세스가 다르다

스크린샷에는 안 찍히는 차이: SafeArea, 키보드, 터치 영역

1편에서 만든 mobile-design-sync 스킬은 모바일 웹과 앱의 스크린샷을 찍어 나란히 비교하며 화면을 맞췄습니다. 빠르게 POC를 올릴 때는 이 방식이 정말 강력했습니다. 그런데 실제 휴대폰에 앱을 빌드해서 손가락으로 눌러보는 순간, 스크린샷 비교만으로는 절대 알 수 없었던 문제들이 줄줄이 나왔습니다.

 

대표적인 것이 상·하단 SafeArea 여백, 키보드 올라옴 처리, 그리고 손가락 터치 영역이었습니다.

 

화면 캡처본에서는 멀쩡해 보이던 상단 헤더가 실제 기기에서는 노치(Notch) 영역과 겹치거나, 반대로 상단 공통 헤더가 이미 상단 여백을 먹고 있는데 화면 컨테이너가 한 번 더 SafeArea를 잡아서 상단에 광활한 빈 공간이 생기기도 했습니다. 입력 폼에서는 키보드가 올라오는 순간 하단 ‘다음’ 버튼을 덮어버렸고, iOS에서 잘 되던 키보드 회피 처리가 Android에서는 먹히지 않았습니다. 웹에서는 마우스 커서로 정확히 클릭할 수 있던 32px짜리 아이콘이나 약관 체크박스도 앱에서는 손가락에 자꾸 빗나갔죠.

 

처음에는 문제가 보이는 화면마다 들어가서 패딩을 고쳤는데, 화면이 수십 개다 보니 한 곳을 고치면 다른 화면과 기준이 달라졌습니다. 그래서 개별 화면을 땜질하는 대신, 웹의 공통 레이아웃에 대응하는 앱 전용 루트 셸 컴포넌트(Screen)를 만들어 화면 규칙을 한곳에서 통제하기로 했습니다.

 

apps/mobile/src/ui/screen.tsx 코드: Screen 컴포넌트가 appHeader 유무로 SafeArea edge를 결정하고 안드로이드 키보드 높이만큼 하단 여백을 보정하는 로직
Screen 컴포넌트 <출처: 작가>

 

이렇게 루트 셸에서 세 가지 디테일을 일괄로 체크했습니다.

 

  • SafeArea(edges) 자동 분기: 상단 공통 헤더가 붙는 탭 화면은 헤더 자체가 이미 상단 SafeArea를 처리하므로 본문에는 하단(bottom)만 적용하고, 자체 스택 헤더를 쓰는 로그인·가입 화면은 상·하단(top, bottom)을 모두 적용하며, 웹뷰나 중첩 컨테이너는 여백이 두 번 잡히지 않도록 제외했습니다.
  • OS별 키보드 대응 분리: iOS는 기본 KeyboardAvoidingView의 padding 옵션만으로 충분했지만, Android edge-to-edge 환경에서는 창 높이가 기대대로 줄어들지 않았습니다. 그래서 Android에서는 키보드 노출 이벤트(keyboardDidShow/keyboardDidHide)에서 높이를 읽어 하단 패딩을 직접 밀어 올리는 커스텀 훅을 붙였습니다.
  • 터치 타깃(hitSlop) 확보: 피그마 시안상 아이콘 크기가 작더라도 손가락으로 누르기 쉽도록 헤더 아이콘 버튼 영역을 넉넉하게 키우고, 체크박스와 텍스트 라벨 전체에 hitSlop과 터치 영역을 넓혔습니다.

 

출강EO 홈 화면을 모바일 웹, 초벌 POC 앱, 디테일 체크 후 앱 세 열로 비교, 상태바·배너·클래스 카드 배치가 단계마다 조금씩 달라짐
홈 화면 비교 <출처: 작가>

 

사진상으로는 큰 차이가 없어 보일 수 있지만, 모바일 웹을 만들면서 함께 협업하는 디자이너분께 대략 350개의 디자인 QA를 받았던 저로서는 큰 수고로움 없이 이런 디테일을 일괄로 맞출 수 있다는 점이 아주 매력적이었습니다.

 

모든 화면을 네이티브로 만들 필요는 없다 - 웹뷰(WebView)로 싱크 비용 줄이기

빠르게 POC를 찍어낼 때는 “일단 웹에 있는 페이지를 다 앱 화면으로 옮기자”는 생각으로 달렸습니다. 하지만 출시 전 디테일 체크 단계에서 냉정하게 따져보니, 유지보수 관점에서 고민되는 지점들이 보이기 시작했습니다.

 

예를 들어 회사소개, 자주 묻는 질문(FAQ), 블로그, 릴리즈 노트, 1:1 문의, 파트너 등록 안내 랜딩 같은 페이지들은 복잡한 네이티브 인터랙션이 없는 정보 전달용 화면입니다. 이런 화면까지 전부 React Native로 따로 들고 있으면, 문구 하나나 배너 하나를 바꿀 때마다 웹을 고치고 앱도 고쳐서 스토어 심사를 다시 받아야 합니다. 물론 이런 불편함을 줄이기 위해 Expo로 프로젝트를 관리하고 있지만, 그럼에도 단순한 문구 변경 때문에 앱 버전을 올리고 배포하는 일은 너무 번거로울 것 같았습니다.

 

그래서 디테일 체크 과정에서 “어디까지 웹뷰(WebView)로 두고, 어디서부터 네이티브로 전환할 것인가”라는 경계선을 명확히 그었습니다.

 

apps/mobile/src/lib/webview-urls.ts 코드: 회사소개, 출강업체 등록 안내, 자주 묻는 질문, 릴리즈 노트, 블로그, 1:1 문의 경로를 담은 WEBVIEW_ALLOWLIST_PATHS 배열
웹뷰 허용 경로 <출처: 작가>

 

정적 페이지들은 과감하게 웹뷰 허용 경로(Allowlist)로 묶어 react-native-webview로 띄웠습니다. 대신 앱 안에서 웹뷰를 열었을 때 상단 헤더가 두 개(앱 헤더 + 웹 헤더)로 겹쳐 보이면 유저 입장에서는 단번에 “껍데기만 씌운 앱”처럼 느껴집니다. 이를 막기 위해 웹뷰 호출 시 식별용 쿼리(?webview=1)와 쿠키를 심어 보내고, Next.js 웹에서는 이 신호가 들어오면 웹 자체 헤더·푸터·플로팅 버튼(FAB)을 숨기도록 처리했습니다. 덕분에 상단은 앱의 네이티브 헤더가, 본문은 모바일 웹이 이질감 없이 이어졌습니다.

 

자주 묻는 질문 페이지를 모바일 웹과 네이티브 헤더+웹뷰 적용 앱에서 나란히 비교, 상단 헤더만 다르고 본문 레이아웃은 동일하게 이어짐
자주 묻는 질문 페이지 <출처: 작가>

 

그런데 이렇게 만들고 나니, 다시 프론트엔드의 전통적인 고민으로 돌아가게 됩니다. “아, 하이브리드 앱으로 가도 될 것 같은데?”

 

  • 하이브리드 앱은 네이티브 앱의 껍데기(셸) 안에 웹뷰(WebView)를 띄워, 웹으로 만든 화면과 네이티브 기능을 함께 쓰는 앱입니다.
  • 푸시 알림·생체 인증이나 복잡한 입력처럼 기기 성능과 UX가 중요한 곳은 네이티브로 처리하고, 자주 바뀌거나 정적인 화면은 웹을 그대로 재사용해 개발·유지보수 비용을 줄일 수 있습니다.

 

참 애매했습니다. 결국 모바일 웹과 모바일 앱을 일일이 싱크하는 것보다, 모바일 웹만 관리하고 앱 껍데기를 씌우는 구조로 간다면 굳이 이런 디테일한 싱크 작업을 하지 않아도 되니 말입니다.

 

하지만 이내 “하이브리드보단 네이티브가 더 성능이 좋지, 장기적으로 고민하자”라는 합리화를 하고 넘어가게 됩니다. 아직 이 부분은 저도 계속 고민 중인 지점입니다.

 

반면, 웹뷰에서 시작하더라도 입력과 검증이 핵심인 구간에서는 즉시 네이티브 화면으로 바통을 넘기도록 설계했습니다. 가장 좋은 예가 ‘출강업체 등록’ 흐름입니다.

 

출강업체 등록 안내 랜딩 페이지는 긴 소개 이미지와 안내 문구가 주를 이루므로 웹뷰로 보여줍니다. 하지만 유저가 그 웹뷰 안에서 ‘출강업체 등록하기’ 버튼을 누르는 순간, 웹뷰 안에 주입해 둔 브릿지 스크립트가 가입 페이지 이동을 가로채어 앱의 네이티브 회원가입 화면으로 라우팅을 전환합니다.

 

출강업체 등록 안내 랜딩 웹뷰 화면과, 등록하기 버튼 클릭 시 전환되는 출강업체 회원가입 네이티브 아이디 입력 화면을 나란히 배치
웹뷰 - 앱 네이티브 전환 <출처: 작가>

 

약관 동의부터 아이디, 사업자등록번호 실시간 검증, 비밀번호 입력, 포커스 자동 이동까지 이어지는 다단계 가입 폼은 웹뷰보다 네이티브로 구현했을 때 키보드 반응성과 입력 경험이 훨씬 쾌적하기 때문입니다.

 

여기에 더해, 네이티브 가입 도중 거쳐야 하는 통신사 PASS 본인인증은 외부 본인인증 모듈 특성상 다시 전용 웹뷰로 띄우고, 인증이 끝나 리다이렉트될 때 결과 URL에서 인증 식별값만 추출해 다시 네이티브 가입 단계로 복귀시켰습니다.

 

PASS 본인인증 전용 웹뷰의 아이디 찾기 결과 화면과, 인증 완료 후 복귀한 네이티브 회원가입 요청 완료 화면을 나란히 배치
본인인증 웹뷰와 화면 <출처: 작가>

 

이렇게 웹뷰와 네이티브의 적당한 타협과 조합으로 개발 속도와 앱다운 사용성을 동시에 잡을 수 있었습니다. 지금도 어떤 페이지를 웹뷰로 두고 어떤 페이지를 네이티브로 가져갈지 계속 고민하고 있습니다. 다만 이전과 다른 점은 개발 속도가 더 이상 큰 병목은 아니라는 것입니다.

 

의사결정 과정에서 네이티브가 더 좋다고 판단되면 이젠 바로 실행하면 됩니다.

 

 

디테일 체크 2. 겉모습은 같은데 어긋난 로직 바로잡기

모노레포를 깔아뒀는데도 왜 로직이 어긋났을까?

두 번째로 발목을 잡은 건 ‘겉모습은 똑같은데 속 로직이 다른’ 문제였습니다.

 

분명 1편에서 시작할 때부터 모노레포를 구축하고 공통 패키지(api, utils)에 비즈니스 로직을 모아두었습니다. 그러니 앱에서도 당연히 똑같은 로직이 돌아갈 거라 믿었죠. 하지만 QA를 시작해 보니 웹에서는 통과되는 입력값이 앱에서는 막히거나, 에러 토스트 문구가 미묘하게 다르거나, 버튼 연타 시 중복 요청 방지 가드가 빠진 곳들이 무더기로 발견됐습니다.

 

원인은 명확했습니다. POC 단계에서 AI 에이전트에게 “모바일 웹 화면을 보고 앱 화면을 빠르게 만들어줘”라고 지시할 때, 에이전트가 매번 공통 유틸 패키지의 검증 함수를 찾아 연결하기보다 각 화면 컴포넌트 안에 빈 값 체크나 정규식 몇 줄을 즉석에서 인라인으로 짜 넣는 쪽을 택했기 때문입니다. 눈으로 보는 UI는 100% 똑같이 돌아가니 초벌 작업 때는 눈치채지 못했던 겁니다.

 

디자인을 맞추기 전에 로직부터 잠그는 mobile-logic-sync

이 문제를 겪고 나서 워크플로를 뜯어고쳤습니다. 픽셀과 색상을 맞추기 전에, 반드시 웹과 앱의 입력 검증, API 호출, 상태 전이부터 대조하는 mobile-logic-sync 단계를 먼저 거치도록 강제한 것입니다.

 

가장 먼저 앱 화면 컴포넌트 안에 흩어져 있던 인라인 검증 코드들을 전수 검색해 걷어내고, 모든 검증 규칙과 에러 문구를 공통 유틸 패키지의 단일 함수로 끌어올렸습니다.

 

packages/utils/src/join-step-validation.ts 코드: validateJoinLoginId 함수가 아이디 공백·길이·정규식을 검사해 웹과 앱이 공유하는 에러 메시지를 반환
공통 로직 <출처: 작가>

 

이렇게 공통 패키지에 정의해 두면, Next.js 웹이든 React Native 앱이든 똑같은 함수를 호출해 똑같은 에러 메시지를 보여줍니다. 또한 이 함수들에 단위 테스트(Vitest)를 붙여두어, 나중에 누군가(아마도 대부분 경우에는 AI가) 화면 UI를 수정하더라도 검증 조건이 슬그머니 바뀌는 일을 원천 차단했습니다.

 

동시에 플랫폼 특성상 웹과 앱이 의도적으로 다르게 동작해야 하는 부분은 각 화면의 싱크 마크다운 문서에 명시해 두었습니다. 예를 들어 웹에서는 비밀번호 입력 중 실시간 강도 검증 API를 디바운스로 호출하지만, 모바일 앱에서는 네트워크 지연을 줄이기 위해 제출 시점에 공통 검증 규칙으로 일괄 검증하도록 결정했습니다. 이런 차이를 문서에 적어두지 않으면 다음번 QA나 에이전트 실행 때 “웹이랑 로직이 다른 버그”로 오해해 다시 덮어쓰는 일이 반복되기 때문입니다.

 

 

디테일 체크 3. 모바일 앱이라서 추가로 챙겨야 했던 기본 기능들

“알림 화면이 있는 것”과 “푸시 알림이 오는 것”은 전혀 다른 문제다

1편 도입부에서 말씀드렸듯, 애초에 모바일 웹만으로도 잘 돌아가던 서비스에 굳이 앱을 만들기로 결심한 이유는 단 하나였습니다. “알림을 보지 못해서 계약이 불발되었다”는 고객의 VOC 때문이었죠.

 

그런데 1주일 만에 찍어낸 POC 앱에서 ‘알림’을 점검해 보니 갈 길이 멀었습니다. 우선 UI부터가 어색했습니다. 모바일 웹에서는 상단 헤더의 종 모양 아이콘을 누르면 그 자리에서 바텀시트(Bottom Sheet)로 알림 인박스가 열렸는데, 초벌 앱에서는 종 아이콘을 누르면 아예 별도의 알림 페이지로 화면 전체가 이동해 버렸습니다.

 

디테일 체크 단계에서 이를 모바일 웹과 동일하게 바텀시트 형태의 알림 인박스로 다시 맞추고, 미읽음 배지와 탭 필터(전체/견적/공지), 그리고 외부 알림 링크를 타고 들어왔을 때 최대 3초간 ‘이동 중...’ 스피너를 보여준 뒤 목표 화면으로 넘겨주는 브릿지 화면까지 웹과 똑같이 정렬했습니다.

 

알림 인박스를 모바일 웹, 전체 화면 이동 방식의 초벌 POC 앱, 바텀시트로 맞춘 디테일 체크 후 앱 세 열로 비교
알림 인박스 비교 <출처: 작가>

 

알림 링크 클릭 시 뜨는 이동 중 스피너 화면을 모바일 웹과 모바일 앱에서 비교, 두 화면 모두 같은 문구와 로딩 모양을 보여줌
알림 리다이렉트 비교 <출처: 작가>

 

하지만 진짜 문제는 그다음이었습니다. 앱 안에 알림함 화면이 예쁘게 들어앉은 것과, 유저의 휴대폰이 꺼져 있을 때 잠금화면에 푸시 알림이 울리는 것은 완전히 별개의 기능이었기 때문입니다.

 

웹을 그대로 옮기는 스킬만으로는 네이티브 푸시가 저절로 생겨나지 않습니다. 실제 유저가 계약을 놓치지 않게 하려면 다음의 네이티브 파이프라인을 처음부터 끝까지 별도로 챙겨야 했습니다.

 

  • OS 권한 및 디바이스 토큰 수명주기: iOS(APNs)와 Android(FCM) 인증서를 세팅하고, 로그인 직후 적절한 타이밍에 알림 권한을 요청해 발급받은 푸시 토큰을 백엔드에 등록·갱신하고 로그아웃 시 해제하는 처리
  • 기존 알림톡과의 서버 팬아웃(Fan-out): 기존에 카카오 알림톡이나 웹 알림이 발송되던 이벤트 지점마다 앱 푸시 토큰 보유 여부를 확인해 푸시를 함께 쏘는 백엔드 연동
  • 콜드 스타트(Cold Start) 딥링크: 앱이 완전히 종료된 상태에서 푸시 배너를 탭했을 때, 페이로드에 담긴 URL을 읽어 브릿지 화면을 거쳐 해당 견적서나 채팅 화면으로 정확히 꽂아주는 라우팅 검증

 

여기까지 진행하니 이젠 앱다운 모습이 어느 정도 완성된 것을 확인할 수 있었습니다. 이젠 진짜 마지막 단계로 넘어가 보도록 하겠습니다.

 

 

디테일 체크 4. 스크린샷 비교를 넘어 픽셀·토큰 단위로 디자인 조이기

왜 스크린샷 비교만으로는 디자인이 계속 틀어졌을까?

마지막으로 가장 많은 시간을 쏟은 곳은 픽셀과 토큰 단위의 디자인 디테일 체크였습니다.

 

1편에서 소개한 mobile-design-sync의 초벌 방식은 심플했습니다. 피그마 전체 화면 스크린샷, 모바일 웹 스크린샷, 모바일 앱 스크린샷을 찍어 AI에게 보여주고 “다른 부분을 찾아 고쳐라”고 시키는 것이었죠. 레이아웃이 크게 무너진 초벌 단계에서는 이 방법이 마법처럼 통했습니다.

 

그런데 완성도를 80%에서 99%로 끌어올리려 하자, 전체 화면 스크린샷 비교가 오히려 디자인을 망치는 역효과를 내기 시작했습니다. 크게 두 가지 이유 때문이었습니다.

 

1) PNG 압축 착시와 피그마 캔버스의 함정 (#dbdbdb vs #aaaaaa)

가장 짜증 났던 사례가 견적 요청(RFQ) 1단계 화면의 카테고리 선택 버튼들이었습니다.

 

피그마 시안과 맞춘다며 에이전트가 수정을 끝냈는데, 앱 화면을 보니 2열 그리드로 놓인 버튼들의 테두리가 유독 시커멓고 답답해 보였습니다. 확인해 보니 에이전트가 버튼의 기본(rest) 테두리 색상을 #aaaaaa로 넣어둔 상태였습니다.

 

왜 #aaaaaa를 넣었는지 추적해 보니 두 가지 문제가 겹쳐 있었습니다.

 

첫째로, 전체 화면을 한 장의 PNG로 캡처해 축소하면 1px짜리 연한 회색 테두리(#dbdbdb)가 주변 픽셀과 섞이면서 눈대중으로는 진한 회색(#aaaaaa)과 구분이 되지 않았습니다.

 

둘째로, 피그마 전체 화면 프레임 안에 예시로 배치된 버튼 인스턴스 하나가 디자이너가 작업하던 중 우연히 마우스 오버(hover) 상태로 놓여 있었는데, 에이전트가 스크린샷만 보고 그 호버 상태의 진한 테두리를 ‘기본 테두리’로 착각해 모든 버튼에 덮어씌운 것이었습니다.

 

결국 Figma MCP로 화면 전체 캡처가 아니라 개별 버튼 컴포넌트의 리프 노드 변수를 직접 조회하고 그 값을 사용하도록 스킬을 변경하고 나서야 이슈가 해결되었습니다.

 

스크린샷을 눈으로만 비교했다면 영영 못 잡았을 1px 단위의 미묘한 톤 차이였습니다.

 

피그마 시안, #aaaaaa가 잘못 적용된 초벌 앱, #dbdbdb 토큰을 반영한 디테일 체크 후 앱의 클래스 선택 화면을 나란히 비교
색상 비교 <출처: 작가>

 

2) 데스크탑 웹과 모바일 웹의 반응형 차이에서 오는 혼선

또 하나 예상치 못한 복병은 기존 Next.js 웹 코드 안에 데스크탑 스타일과 모바일 스타일이 함께 섞여 있었다는 점이었습니다.

 

저희 웹은 Tailwind의 반응형 프리픽스(lg:gap-20, lg:p-10 등)로 데스크탑과 모바일을 한 컴포넌트에서 처리하거나, 화면에 따라 데스크탑에서는 컴팩트한 카드로 보여주고 모바일에서는 풀사이즈 카드나 바텀시트로 보여주는 곳들이 있었습니다.

 

견적서 등록 포트폴리오 선택 화면을 데스크탑 웹 반응형 뷰와 모바일 웹 뷰포트에서 비교, 데스크탑은 좁은 모바일 프레임 안에 축소 렌더링됨
반응형 사이즈 비교 <출처: 작가>

 

처음에는 피그마 시안과 웹 코드, 앱 코드를 한꺼번에 열어두고 싱크를 맞췄더니, 에이전트가 웹 코드에 적힌 데스크탑용 패딩(lg:p-10)이나 폰트 크기를 모바일 앱에 그대로 가져다 쓰는 실수가 반복됐습니다.

 

해결책: 스크린샷은 배치만 보고, 스타일은 수치로 읽되 ‘한 번에 한 축씩’ 조이기

이 시행착오 끝에 디자인 싱크 2라운드에서는 규칙을 완전히 바꿨습니다.

 

  • 반드시 모바일 웹(@375px)을 먼저 피그마와 맞춘 뒤 앱으로 넘어간다: 피그마-웹-앱 3자 비교를 금지했습니다. 무조건 1단계(Figma → 모바일 웹 375px 뷰포트)에서 반응형 깨짐이나 토큰 누락을 먼저 다 잡고, 완벽히 검증된 모바일 웹을 기준으로 2단계(모바일 웹 375px → 모바일 앱)를 진행했습니다.
  • 스크린샷과 노드 검사의 역할을 분리한다: 전체 화면 스크린샷은 블록의 순서나 하단 고정 버튼 위치 같은 큰 레이아웃 배치를 확인할 때만 쓰고, 색상·폰트·간격은 반드시 Figma 리프 노드의 토큰 이름과 픽셀 수치를 직접 읽어와 공통 디자인 토큰과 UI 패키지에 반영했습니다.
  • 한 번에 하나의 디자인 축(Axis)만 수정한다: 한 번에 테두리, 폰트 크기, 패딩을 다 고치라고 시키면 하나가 맞아도 다른 하나가 틀어졌습니다. 그래서 border → font → spacing처럼 하나의 기준점으로 쪼개어, 한 번에 한 가지씩만 체크하고 넘어가는 방식으로 회귀를 막았습니다.

 

 

마치며: “앱 출시까지 2주면 된다”를 직접 테스트해 본 최종 후기

1편과 하편에 걸친 실험을 마무리하며, 처음 던졌던 질문으로 돌아가 보려 합니다.

 

“정말 AI와 모노레포가 있으면 모바일 웹을 앱으로 만들어 출시하는 데 2주면 충분할까?”

 

직접 부딪혀 본 제 대답은 “절반은 맞고, 절반은 틀렸다”입니다.

 

맞았던 절반은 ‘속도’입니다. 예전 같았으면 React Native 프로젝트를 세팅하고 수십 개의 화면과 컴포넌트를 일일이 손으로 옮기는 데만 한 달이 훌쩍 넘게 걸렸을 겁니다. 이미 돌아가는 Next.js 모바일 웹을 SSOT로 삼고, 모노레포와 자동화 스킬을 엮어 단 일주일 만에 모든 화면이 연결된 앱 POC를 눈앞에 띄운 것은 분명 AI 시대에만 가능한 속도였습니다.

 

틀렸던 절반은 ‘디테일’입니다. 화면을 빠르게 찍어내는 것과, 그 앱을 스토어에 출시해 실제 고객의 계약을 책임지게 만드는 것은 전혀 다른 영역이었습니다.

 

물론 토큰을 더 태운다거나 디테일보다는 속도에 집중하면 큰 문제가 안 될 수도 있었습니다. 하지만 사이드 프로젝트인 만큼 토큰을 무작정 태울 수도 없었고, 디테일을 타협하는 것은 제 생각일 뿐 함께 일하는 디자이너분께는 포기할 수 없는 문제였죠.

 

그럼에도 한 가지 확실한 건, 1주일 만에 초벌 앱을 뽑아내는 ‘속도’가 있었기에 남은 시간과 에너지를 온전히 이 ‘디테일한 체크’에 쏟아부을 수 있었다는 점입니다. 코딩 자체에 치여 정작 중요한 앱 UX와 푸시 경험, 디자인 완성도를 타협하는 대신, 진짜 제품다운 디테일을 집요하게 파고들 수 있었습니다.

 

혹시 지금 돌아가는 웹 서비스를 바탕으로 빠르게 앱 출시를 고민하고 계신다면, 초반에는 과감하게 자동화 워크플로로 속도를 내어 POC를 올리시되, 그다음에는 반드시 속도를 내려놓고 UX 경계·로직 SSOT·네이티브 기본기·토큰 단위 디자인을 하나씩 체크하는 시간을 일정에 꼭 넣어두시길 추천합니다.

 

긴 호흡의 상·하편 여정을 끝까지 읽어주셔서 감사합니다.

 

ⓒ요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.