Projects/Levio: Workout Tracker

[운동 관리 앱 만들기] Levio - DAY 3 [트러블슈팅] iOS Safari에서 native date/time input이 flex 컨테이너를 overflow하는 문제 (WIP)

montmer27 2026. 5. 18. 23:06

[트러블슈팅] iOS Safari에서 native date/time input이 flex 컨테이너를 overflow하는 문제 minWidth: 0, overflowX: hidden 모두 효과 없음 — iOS 브라우저의 native input 최소 너비 강제 적용이 원인

목차

  1. 문제 상황
  2. 원인 분석 과정
  3. 수정 내용
  4. 교훈 및 인사이트
  5. 느낀 점

 

1. 문제 상황

운동 기록 앱(Levio)의 입력 폼에서, 모바일 환경(약 375px 뷰포트)에서 날짜와 시간 입력 필드가 컨테이너 밖으로 overflow되는 레이아웃 버그가 발생했다.

구체적인 증상은 세 가지였다.

  • 날짜 필드의 값이 input 박스 바깥으로 넘어간다.
  • 시작 시간과 종료 시간 라벨 및 입력이 서로 겹친다.
  • 종료 시간 필드가 카드 컨테이너의 오른쪽 경계를 넘어간다.

이미지: 모바일에서 시간 입력 필드가 컨테이너를 넘어가는 스크린샷

발생 환경은 다음과 같다.

  • React 19.2.6 (Create React App)
  • CSS-in-JS (inline style 객체)
  • iOS Safari 및 iOS Chrome (iPhone, 약 375px 뷰포트)
  • 브라우저 네이티브 date/time input 사용

이 문제는 아래와 같은 상황에서 발생할 수 있다.

  • flex 레이아웃 안에서 iOS 네이티브 input(type=date, type=time)을 사용하는 경우
  • 좁은 뷰포트에서 네이티브 input의 고유 렌더링 너비가 flex 컨테이너의 절반을 초과하는 경우

 

2. 원인 분석 과정

2-1. flex 자식의 minWidth: auto 의심

flex 자식 요소는 기본적으로 min-width: auto를 가지며, 콘텐츠의 고유 너비 아래로 줄어들지 않는다. 네이티브 time input이 시/분 선택 UI와 드롭다운 아이콘을 포함하고 있어 고유 너비가 크기 때문에, flex: 1의 50/50 분배가 무시되고 overflow가 발생한다고 판단했다.

시작/종료 시간 wrapper div에 minWidth: 0을 추가하여 auto 제약을 해제했다.

결과: 효과 없음. DevTools에서 스타일 반영은 확인되었으나 실제 iPhone에서 동일한 overflow가 지속되었다.

2-2. 컨테이너 overflow 제어 및 전역 box-sizing 적용

두 번째 가설은, iOS Safari가 네이티브 input에 최소 너비를 강제 지정하면서 부모 컨테이너가 팽창하고, 이것이 연쇄적으로 상위 요소까지 overflow를 전파한다는 것이었다. S.app에 overflow 제한이 없는 점이 근본 원인이라고 판단했다.

세 가지를 동시에 적용했다.

  • S.app에 overflowX: hidden 추가
  • S.wrap에 width: 100% 추가
  • index.css에 전역 box-sizing: border-box 추가

결과: 효과 없음. 배포 빌드에도 정상 반영된 것을 확인했으나, 실제 iPhone에서 Safari와 Chrome 모두 동일 증상이 발생했다.

2-3. 실제 원인

DevTools에서 모든 스타일 변경이 반영되고, 배포 빌드에서도 확인되었음에도 실기기에서 동일 증상이 재현되었다. 이 시점에서 CSS 코드 자체의 문제가 아님을 확인했다.

iOS는 네이티브 input type=date와 input type=time을 렌더링할 때 CSS 너비 제약을 무시하고 자체적으로 최소 너비를 강제 적용한다. 이 동작은 iOS의 날짜/시간 피커 UI를 보장하기 위한 브라우저 레벨 동작이며, 외부 CSS로 오버라이드할 수 없다. 결과적으로 flex: 1, minWidth: 0, overflowX: hidden 등 일반적인 CSS 해법은 모두 무효하다.


 

3. 수정 내용

CSS만으로는 해결할 수 없다는 결론에 도달했고, 레이아웃 구조 자체를 변경해야 한다. 현재 검토 중인 방향은 두 가지다.

방향 A — 날짜/시간 필드를 세로 스택으로 변경

row 레이아웃을 포기하고 날짜, 시작 시간, 종료 시간을 각각 별도 행으로 배치하는 방식이다.

Before

<div style={S.row}>
  <div style={{flex:1}}>
    <label style={S.label}>시작 시간</label>
    <input type="time" style={S.inp} value={startTime}
      onChange={e=>setStartTime(e.target.value)}/>
  </div>
  <div style={{flex:1}}>
    <label style={S.label}>종료 시간</label>
    <input type="time" style={S.inp} value={endTime}
      onChange={e=>setEndTime(e.target.value)}/>
  </div>
</div>

After (방향 A)

<label style={S.label}>시작 시간</label>
<input type="time" style={{...S.inp,marginBottom:12}} value={startTime}
  onChange={e=>setStartTime(e.target.value)}/>
<label style={S.label}>종료 시간</label>
<input type="time" style={{...S.inp,marginBottom:12}} value={endTime}
  onChange={e=>setEndTime(e.target.value)}/>

가장 확실하고 단순한 해결책이다. 각 input이 전체 너비를 사용하므로 iOS의 최소 너비 강제 적용과 충돌하지 않는다.

방향 B — native input 대신 커스텀 UI 사용

네이티브 input type=time을 제거하고, select 드롭다운 조합(시/분)으로 대체하는 방식이다. CSS로 완전히 제어 가능한 요소만 사용하므로 브라우저 간 동작 차이가 없어진다. 다만 시간 선택 UX를 직접 구현해야 하므로 구현 비용이 있다.

현재 방향 A를 우선 적용하는 쪽으로 검토 중이다.


 

4. 교훈 및 인사이트

iOS Safari는 네이티브 폼 요소(date, time, select 등)에 대해 자체적인 최소 크기와 렌더링 규칙을 강제한다. 이 동작은 CSS로 오버라이드할 수 없으며, DevTools에서는 스타일이 적용된 것처럼 보이지만 실기기에서는 무시되는 경우가 있다. 모바일 웹 개발에서 iOS 네이티브 폼 요소와 관련된 레이아웃 문제를 만나면, CSS 해법으로 시간을 쓰기 전에 먼저 브라우저 레벨 제약 여부를 의심해야 한다.

minWidth: 0은 flex overflow 문제의 범용적인 해결책이지만, 만능은 아니다. 이 기법은 flex 자식의 기본 min-width: auto 제약을 해제하는 것일 뿐, 브라우저가 input 요소 자체에 강제하는 최소 너비까지 해제하지는 못한다. 문제의 층위를 구분하는 것이 중요하다. flex 레이아웃의 크기 계산 문제인지, 브라우저의 네이티브 요소 렌더링 문제인지에 따라 해결 방향이 완전히 달라진다.

모바일 퍼스트로 개발할 때, 좁은 뷰포트에서 두 개 이상의 네이티브 input을 가로로 배치하는 레이아웃은 피하는 것이 안전하다. 네이티브 input의 고유 너비는 OS와 브라우저 버전에 따라 달라지므로 예측이 어렵다. 세로 스택 배치가 가장 안전하고, 가로 배치가 반드시 필요하면 네이티브 input 대신 커스텀 컴포넌트를 사용하는 것이 확실하다.


 

5. 느낀 점

DevTools로 모바일 화면 미리보기가 가능하다
근데 iOS Safari의 경우 안심할 수 없다. 네이티브 폼 요소에 대해 자체적인 최소 크기와 렌더링 규칙을 강제하기 때문이다.