디자인 된 <select />에 placeholder 개념이 있어서 어떻게 하면 좋을지 찾아봤다. 간략하게 설명하자면 선택이 불가능하게 disabled 추가하고 hidden으로 숨김 마지막으로 selected로 디폴트값을 처리하면 완성.

function Select({ placeholder, children }) {
  return (
    <select>
      <option value="" disabled hidden selected>
        {placeholder}
      </option>
      {children}
    </select>
  )
}

#132

자동완성 기능은 편리할수도 있지만 비활성화가 필요한 경우도 있다. 하지만 chrome에서 무시되는 경우가 있는데…이게 거의 된다 지금은 안된다…되는 것 같다…이렇게 변질이 되는 것 같다.


Footnotes

  1. 가끔 들어가보면 여전히 진행중이다… ↩

  2. 글쓴이는 결국 안되서 스크립트로 focus될때 readonly 속성으로 제어했다고 한다. ↩

#168

blocking=render 는 <body> 전에만 등록된다 — 뒤에 붙으면 조용한 no-op

새로고침하면 한 프레임 동안 스타일이 붙기 전 default 상태가 보인다. 파서를 멈추는 것과 paint를 멈추는 것은 다르다 — 동기 <script>는 파서만 세우고, 그 위에 이미 파싱된 청크가 먼저 그려질지는 브라우저 재량이다. blocking="render"가 paint까지 막는 걸 보장하는데, <body> 뒤에 붙으면 조용히 아무 일도 안 한다.

1. 이 축의 문제인가

증상 원인 처방
스타일이 붙기 전 default 상태가 한 프레임 보인다 스타일 적용이 첫 페인트보다 늦다 blocking="render" — 여기서 계속
다크 테마인데 흰 화면이 번쩍인다 서버가 사용자 상태를 몰라 기본값으로 렌더했다 첫 페인트 전 인라인 스크립트 → 594

뒤쪽은 렌더를 막아도 안 고쳐진다 — 서버가 낸 HTML 자체가 이미 다른 값이다.

2. 붙였는데 먹었는지 무엇으로 아나

로그도 DOM도 답을 안 준다. <body> 뒤에 붙여도 console·Log·Audits 어디에도 아무것도 안 찍히고, el.blocking.contains('render')는 앞이든 뒤든 true다.1 갈리는 건 첫 페인트 시각 하나다 — 막는 리소스가 끝나기 전에 칠해졌으면 안 먹은 것이다.

3. CDP로 잰다

Page.navigate → Page.loadEventFired를 기다린 뒤, 페이지의 Performance 엔트리를 읽어 둘을 비교한다.

const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime
const done = performance.getEntriesByType('resource').find((e) => e.name.includes('slow.js')).responseEnd
fcp < done // true 면 막는 리소스가 오기 전에 이미 칠했다 — 안 먹었다

800ms 걸리는 <script async blocking="render" src="/slow.js"> 위치만 바꿔 잰 값이다.1

위치 FCP slow.js 끝 판정
blocking 없음 68ms 830ms default 가 먼저 칠해짐
<head> 836ms 816ms 스크립트 뒤에 첫 페인트 — 먹었다
<body> 안 16ms 807ms default 가 먼저 칠해짐 — 없는 것과 같다

파서는 head 를 읽고 body 를 연다. body 가 null 인 동안만 render-blocking 등록이 가능하고, body 가 열리는 순간 창이 닫힌다. head 안의 script blocking=render 는 등록되고, body 안의 같은 스크립트는 등록되지 않는데 경고도 없다. 실측: head 안이면 816ms 에 스크립트가 끝난 뒤 836ms 에 styled 상태로 첫 페인트한다. body 안이면 16ms 에 default 가 먼저 칠해지고 807ms 에 스크립트가 끝나서야 styled 로 바뀐다 — 그 사이 default 가 노출된다. 로그와 DOM(el.blocking)으로는 둘이 똑같고 첫 페인트 시각만 갈린다. 눈금은 비례가 아니다.

4. <body>가 열리기 전에 둔다

컴포넌트 마크업 안이면 이미 늦다. 스펙은 “head 안에 둬라”가 아니라 등록 창을 이렇게 닫는다.

A Document document allows adding render-blocking elements if document’s content type is “text/html” and the body element of document is null.

— HTML Standard

<body>가 생기는 순간 등록이 닫히고, 뒤에 붙인 속성은 실패 없이 무시된다. 속성을 모르는 브라우저가 옛 동작으로 떨어지는 것과 같은 모양이라 3처럼 재지 않으면 드러나지 않는다.

Footnotes

  1. Chrome 154 headless 에서 raw CDP 로 실측 (2026-10-01). 로컬 Node 서버가 /slow.js 를 800ms 늦게 주고, 스크립트는 <p> 텍스트를 바꾼다. Log·Runtime·Audits 를 enable 한 채 <head> / <body> 두 경우를 봤고 둘 다 메시지 0건이었다. ↩ ↩2

#567