PWA 모바일 반응형 — 7번 핑퐁 끝에 배운 6가지 교훈

2026/06/11 PWA CSS 3346자 · 약 10분

Hook

“모바일에서 버튼이 안 보인다”는 피드백부터 시작된 반응형 디버깅. 7번의 수정-배포-확인 사이클 끝에 찾은 것은, 단위 선택과 CSS 우선순위라는 기본기였습니다.

TL;DR

  • px 대신 vwvw = preview_px / 375 × 100
  • 인라인 style이 CSS 클래스를 이긴다 — 1~3차 시도 실패의 근본 원인
  • flex:1은 양날의 검 — 남은 공간을 다 먹거나, 누락 시 0px
  • 데스크탑 모드를 먼저 확인 — @media 규칙이 완전히 달라짐
  • SW 캐시는 이름 변경으로 갱신 — CSS 변경이 안 보이면 캐시 의심

어떤 상황이었나요?

PWA 피트니스 앱을 모바일 반응형으로 작업하면서, 디자인 프리뷰 7개 페이지를 SPA 단일 파일로 통합했습니다. CSS 클래스에서 인라인 스타일로 전환했다가, 다시 인라인+CSS 병행으로 회귀하는 과정이었습니다.

타임라인:

회차시도결과원인
1차@media px→vw 변환실패인라인 style이 CSS 덮어씀
2차render() 인라인 px→vw실패원인 파악 실패
3차preview px 복원거부“왜 px인가”
4차preview 비율 그대로 vw 변환성공비율 기반 변환
5차“세트 추가 버튼 안 보임”해결flex:1 누락 + 데스크탑 모드
6차“포즈 가이드 너무 밑에”해결flex:1이 공간 다 차지
7차“홈 글자 줄넘김”해결px 고정값 + 인라인 배치

교훈 1: 단위는 무조건 vw/vh

현상: 모바일에서 요소 크기가 기기마다 다르게 보입니다.

원인: px은 물리적 픽셀 기준이라, 갤럭시 S시리즈(1080px+)에서는 작게, iPhone SE(375px)에서는 크게 보입니다.

해결: 모든 크기를 vw로 변환합니다.

/* ❌ */
font-size: 14px;

/* ✅ */
font-size: 3.7vw;
.container {
  padding: 4vw;
}

@media (min-width: 600px) {
  .container {
    padding: 24px;
  }
}

예외: @media (min-width: 600px) 데스크탑 구간에서만 px을 허용합니다.

교훈 2: 인라인 style이 CSS를 덮어쓴다

현상: CSS 클래스에 반응형을 적용했지만 모바일에서 전혀 반영되지 않습니다.

원인: 이것이 1~3차 시도가 모두 실패한 근본 원인이었습니다. style="width:136px" 같은 인라인 스타일은 CSS 클래스보다 항상 우선순위가 높습니다. 아무리 .card { width: 36vw !important; }를 적어도 인라인이 이깁니다.

해결: render 함수에서 style="..." 인라인 사용을 최소화하고, 크기와 위치는 반드시 CSS 클래스로 제어합니다.

// ❌
return `<div style="width:136px;height:40px;font-size:14px">...</div>`;

// ✅
return `<div class="card">...</div>`;

교훈 3: flex:1은 양날의 검

현상 A: flex:1이 남은 공간을 전부 차지해서 하위 요소가 화면 밑으로 밀려납니다.

원인: .btn-complete-wrap { flex: 1 }이 세로 공간을 전부 차지해, 포즈 가이드와 이전 세트가 보이지 않았습니다.

/* ❌ */
.btn-complete-wrap { flex: 1; }
.center-area { justify-content: space-between; }

/* ✅ */
.btn-complete-wrap { }
.center-area { justify-content: flex-start; }

현상 B: 반대로 flex:1이 누락되면 버튼이 0px로 렌더링됩니다.

원인: display:flex 컨테이너 안의 버튼에 flex:1이 없으면 width가 0이 됩니다.

/* ❌ */
.btn-outline { height: 15vw; }

/* ✅ */
.btn-outline { flex: 1; height: 15vw; }

교훈 4: “데스크탑 모드” 함정

현상: CSS를 수정했는데 모바일에서 전혀 반영되지 않습니다.

원인: 모바일 브라우저에서 “데스크탑 사이트 보기” 옵션이 켜져 있으면 User-Agent가 데스크탑으로 바뀌어, @media (max-width: 599px) 모바일 규칙이 전부 무시됩니다.

디버깅 체크리스트:

  1. 시크릿 모드로 접속했는가? → localStorage 없어서 로그인 안 됨
  2. “데스크탑 사이트 보기”가 켜져 있는가? → @media 모바일 규칙 무시
  3. Service Worker 캐시가 갱신되었는가? → 구버전 서빙 가능
  4. 브라우저 확장 프로그램이 CSS를 덮어쓰고 있지 않은가?

교훈 5: 캐시 갱신은 SW 이름 변경

현상: CSS를 변경해도 모바일에 반영되지 않습니다.

원인: 브라우저가 Service Worker 캐시에서 구버전을 서빙하고 있습니다.

해결: 캐시 이름만 바꿔도 activate 이벤트에서 이전 캐시를 삭제하고 새로 받아옵니다.

// sw.js
const CACHE_NAME = 'my-pwa-v6';

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/app.js'
      ]);
    })
  );
});

self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    })
  );
});

교훈 6: 긴 텍스트는 세로 리스트로

현상: 좁은 화면에서 텍스트가 줄바꿈되어 가독성이 떨어집니다.

원인: 긴 텍스트를 <p> 태그에 인라인으로 배치하면 줄넘김이 발생합니다.

해결: <ul> 불릿 리스트로 세로 배치합니다.

<!-- ❌ -->
<p>레그 — 스쿼트, RDL, 레그 컬, 레그 익스텐션</p>

<!-- ✅ -->
<ul>
  <li>스쿼트</li>
  <li>RDL</li>
  <li>레그 컬</li>
  <li>레그 익스텐션</li>
</ul>

vw 변환 치트시트

디자인 프리뷰(375px 기준)에서 자주 쓰는 크기 변환표입니다.

preview pxvw (375기준)비고
12px3.2vw보조 텍스트
14px3.7vw본문
20px5.3vw입력폼
56px15vw버튼 높이
136px36vw세트 완료 링
.text-secondary { font-size: 3.2vw; }
.text-body      { font-size: 3.7vw; }
.input-field    { height: 5.3vw; }
.btn-primary    { height: 15vw; }

추가 버그 수정 요약

반응형 작업 외에 사용자 피드백 기반으로 수정한 9건입니다.

#이슈해결
1개인정보처리방침 클릭 불가SPA privacy 페이지 추가, 데드링크 제거
2앱 설치(A2HS) 안됨manifest link + SW register 추가
3갤러리 다중 삭제 불가편집 모드 + 체크박스 일괄 삭제
4타임스탬프 사진 위 표시 안됨날짜 오버레이 + 공유 시 캔버스 워터마크
5바디 포토 날짜 UTC로 엉뚱함로컬 타임존 기준으로 변경
6식단 탭 UX 불편빠른 추가 모드 + 즐겨찾기 원클릭
7세트 자동세팅 안됨 + 모바일 작음변수명 수정 + 반응형 확대
8루틴 아이콘 큼 + 가이드 미동작아이콘 축소 + 가이드 인라인 표시
9내 몸의 변화에 촬영 없음카메라 버튼 + 바디탭 연동 추가

Takeaway

  1. 반응형 디버깅의 90%는 단위와 우선순위 — px→vw 변환, 인라인→CSS 클래스 전환만으로 대부분의 문제가 해결합니다.
  2. “안 된다”고 하면 먼저 확인할 것 — 데스크탑 모드, SW 캐시, 브라우저 확장 프로그램 순으로 체크합니다.
  3. 캐시가 의심되면 SW 이름을 바꾸세요 — 캐시 이름 변경이 가장 확실한 강제 갱신 방법입니다.
HeonJe Lee | 선임연구원
게이트웨이 On-promise 제품 팀에서 시스템 모니터링 및 관리를 쉽게 다가갈 수 있도록 하기 위한 업무를 하고 있습니다.

Contact: lhjnano@gmail.com

Search

    Table of Contents