yceffortnews
Daily20265편

10월 23일 금요일

오늘 하나만 본다면

아무도 쓰지 않는 회귀 테스트가 실제 동작을 고정한다

Stack Overflow 원문

응답은 그대로인데 뒤에서 DB 쿼리가 하나 늘어난 회귀는, 의도를 적은 일반 테스트로는 절대 잡히지 않습니다.

  • 오래 운영된 시스템에는 아무도 계획하지 않고 기록하지 않은 동작 층이 쌓입니다. 2019년에 누군가 정한 기본값, 이상한 엣지 케이스 같은 것들입니다. Hyrum의 법칙대로 사용자가 충분히 많으면 문서화된 계약뿐 아니라 결과 순서, 에러 문구, 필드가 null인지 아예 없는지까지 누군가는 의존하게 되는데, 일반 회귀 테스트는 작성자가 의도한 것만 검사하므로 이런 우발적 동작을 놓칩니다.
  • 해법은 Michael Feathers가 「레거시 코드 활용 전략」에서 말한 특성화 테스트입니다. 이해하지 못한 코드를 건드리기 전에 "해야 하는 일"이 아니라 "지금 하는 일"을 테스트로 고정하고, 버그처럼 보여도 일단 고정합니다. 정확성이 아니라 동작이 바뀌는 순간 알려주는 안전망이 목적입니다. 일반 테스트가 "코드야, 이걸 지켜라"라면 특성화 테스트는 "이게 현실이니 바꾸려면 이유가 있어야 한다"입니다.
  • 널리 쓰이지 않는 이유는 사람 쪽에 있습니다. 입력과 출력을 하나씩 적는 일이 고되고, 버그로 보이는 동작을 단언하는 것이 본능에 어긋납니다. 골든 마스터 테스트와 프런트엔드 스냅샷이 비용을 낮췄지만 함수나 컴포넌트에 강하고 서비스에는 약합니다. 서비스의 동작은 반환값뿐 아니라 어떤 쿼리를 날리고 어떤 서비스를 부르고 어떤 메시지를 보내는지까지 포함하기 때문입니다.
  • 저자의 제안은 운영 트래픽 자체를 특성화 테스트로 쓰는 것입니다. 네트워크 수준에서 요청과 응답, 그리고 DB와 큐를 포함한 의존 서비스로 나가는 호출을 녹화하고, 나중에 의존성을 같은 녹화로 응답시키며 재생합니다. 응답이 같아도 DB 호출이 하나 늘면 diff에 드러나므로, 테스트에서는 무해하고 규모에서는 치명적인 새 쿼리 같은 회귀를 잡습니다. 타임스탬프와 생성 ID 같은 잡음 필드만 정확히 무시해야 신호가 묻히지 않습니다.
  • 빨간 특성화 테스트는 버그가 아니라 "동작이 바뀌었다"는 뜻이고, 다음 질문은 "의도한 변화인가"입니다. 이 diff를 PR에서 리뷰하면 동작 변화가 사고에서 결정으로 바뀝니다. 효과가 큰 경우로 레거시 리팩터링, 프레임워크와 ORM 메이저 업그레이드, 언어나 아키텍처를 바꾸는 재작성(옛 서비스의 녹화가 곧 새 서비스의 명세가 됩니다), 그리고 AI가 코드와 테스트를 함께 고친 변경을 듭니다.

마지막 경우가 핵심입니다. AI가 코드와 테스트를 함께 고치면 테스트가 변경과 같은 가정을 공유하게 되는데, 사전에 캡처한 기준선은 그 가정을 공유하지 않는 드문 독립 검증입니다. 재작성이나 메이저 업그레이드를 앞둔 서비스가 있다면 바로 적용해 볼 만하고, 녹화와 재생 도구를 직접 만들지 않더라도 "응답이 같다"와 "동작이 같다"를 구분하는 리뷰 관점만 가져와도 값이 있습니다.

읽을 만한 것

AI가 생성한 HTML 미리보기는 샌드박스만으로 부족하다

Frontend Masters 원문

sandbox를 켜도 fetch와 new Image().src로 토큰이 밖으로 나가며, 이를 막는 것은 sandbox가 아니라 별개의 CSP입니다.

  • 가장 흔한 첫 구현은 모델 출력을 iframe.srcdoc에 넣는 두 줄입니다. sandbox 없는 srcdoc iframe은 호스트와 같은 origin에서 돌아서 parent.document, parent.localStorage, 쿠키에 접근합니다. 프롬프트에는 사용자나 모델이 읽은 문서가 넣은 내용이 담기므로, 이는 낯선 사람의 HTML을 읽지도 않고 앱에 붙이는 일입니다. 보안 엔지니어인 저자 자신도 읽지 않은 코드를 세션이 보이는 문서에 렌더링했다고 고백합니다.
  • sandbox="allow-scripts"를 켜면 프레임은 opaque origin을 받아 부모 DOM을 읽을 때 SecurityError가 납니다. 최상위 창 이동, 팝업, 폼 제출은 토큰을 따로 주지 않으면 막히는데, 사람들은 뭔가 깨질 때 allow-top-navigation이나 allow-same-origin을 무심코 붙입니다. 특히 allow-scripts와 allow-same-origin의 조합은 프레임이 자기 sandbox 속성을 지우고 다시 로드해 제한을 벗을 수 있게 하므로 금지입니다.
  • sandbox는 프레임이 페이지에 하는 일을 막을 뿐 네트워크를 막지 않습니다. fetch나 new Image().src로 URL에 데이터를 실어 밖으로 보낼 수 있고, 이를 막는 것은 미리보기 문서의 CSP(default-src 'none')입니다. sandbox와 CSP는 서로 다른 구멍을 막는 독립 통제이므로 둘 다 필요합니다. 다만 프레임이 스스로 이동하는 것까지 막지는 못하는 한계는 남습니다.
  • 메시지 채널은 event.source로 검증합니다. sandbox된 프레임은 origin이 모두 문자열 "null"로 직렬화되므로 이를 허용 목록에 넣어 발신자를 식별할 수 없습니다. 대신 임베드한 contentWindow와 event.source를 비교하고, 페이로드 형태를 검증하고, 높이 같은 값은 범위로 제한합니다. 비공개 통로가 필요하면 MessageChannel을 씁니다.
  • 고가치 서비스라면 사용자 콘텐츠를 별도의 버리는 도메인에서 서빙합니다. CodePen이 결과를 cdpn.io에서 서빙하는 이유와 같습니다. sandbox 탈출이 일어나도 세션, 쿠키, 다른 고객 데이터가 없는 origin에 떨어집니다.

저자가 제시한 순서는 빈 sandbox, 이유를 주석으로 단 allow-scripts 하나, 별도 origin, CSP, 좁은 메시지 검증입니다. LLM 출력물을 화면에 그리는 기능을 만들고 있다면 그대로 체크리스트로 쓸 수 있고, 특히 "sandbox를 켰으니 됐다"는 흔한 착각 하나를 바로잡아 줍니다.

Docker는 이상하다, 컨테이너는 실재하지 않는다

codeandcake.dev (Lobsters 경유) 원문

Docker의 런타임 runc는 Go에 fork가 없고 런타임이 멀티스레드라서, Go가 초기화되기 전에 도는 C 모듈과 /proc/self/exe 재실행으로 우회합니다.

  • 컨테이너는 VM과 달리 실재하는 단위가 아닙니다. 특수한 제한이 걸린 평범한 프로세스이고 호스트 커널이 그대로 실행하므로 /proc에서 찾을 수 있습니다. 리눅스에 "컨테이너"라는 기능은 없고, 호스트와 컨테이너가 보는 것을 가르는 namespace, 자원을 제한하는 cgroup, 네트워크 격리 같은 다섯 가지 커널 기능을 겹쳐 입은 것입니다.
  • 이미지는 Docker가 어딘가에 풀어 둔 폴더이고, 실행은 그 폴더를 chroot(또는 pivot_root)로 프로세스의 새 루트 디렉터리로 삼는 일입니다. docker run -it --rm ubuntu /bin/bash는 이미지를 받아 어딘가에 두고 그 안의 bash를 실행하는 것뿐입니다.
  • Docker의 런타임 runc는 Go로 쓰였는데, 필요한 syscall 일부는 단일 스레드 환경에서 해야 하지만 Go 런타임은 기본이 멀티스레드입니다. 그래서 runc는 Go 런타임이 초기화되기 전에 도는 C 모듈로 그 syscall을 처리합니다. Go에는 fork도 없습니다. 필요한 것은 fork 후 제한을 걸고 exec하는 흐름이라, runc는 특별한 인자를 준 /proc/self/exe를 다시 exec해 자식 프로세스를 흉내 냅니다.
  • 컨테이너 런타임이 쓰는 많은 커널 API는 root를 요구했거나 요구하므로 Docker 데몬은 보통 root로 돕니다. macOS에서 쓰면 놓치기 쉬운 점입니다. 요즘은 나아졌지만 rootless 런타임은 같은 네트워킹 기능을 주려고 네트워크 스택을 사용자 공간에서 다시 구현해야 합니다.

쓰는 도구를 직접 만들어 보는 것이 배우는 좋은 방법이라는 것이 저자의 결론입니다. SSR 컨테이너를 운영하면서도 그 아래가 궁금했던 분께 짧고 명확한 입문이 되고, "컨테이너는 그냥 프로세스"라는 한 문장은 디버깅할 때 호스트의 /proc과 일반 도구를 왜 그대로 쓸 수 있는지 설명해 줍니다.

장기 정적 자격증명을 없애자, OIDC로 가는 Vercel의 마감일

Vercel 원문

1,000곳 넘는 SaaS 벤더 중 안전한 인증 방식을 지원하는 곳은 8.4%뿐이고, Vercel은 마켓플레이스 벤더에 2026-11-30 경고, 2027년 1월 퇴출이라는 마감일을 겁니다.

  • 주장은 기계 간 인증이 지금 2010년의 HTTPS와 같다는 것입니다. OAuth, OIDC, DBSC 같은 표준은 이미 있지만 API 키와 DB 비밀번호 같은 만료 없는 정적 자격증명이 SaaS의 기본값입니다. 이런 자격증명은 저장소나 채팅에 새고, 침해된 의존성이 운영 환경에서 읽어 가고, 에이전트가 프롬프트 인젝션으로 노출할 수 있으며, 폐기될 때까지 권한이 그대로 쓰입니다. Vercel은 과거 키 교체에 수천 시간의 엔지니어링을 썼다고 밝힙니다.
  • 대안은 OIDC입니다. 신뢰하는 발급자가 앱을 식별하는 서명된 단기 토큰을 주고 수신 서비스가 검증합니다. 훔친 토큰은 만료와 함께 쓸모가 없어지고, 앱이 프로그램으로 새 토큰을 받으므로 키를 퍼뜨리고 수동 교체할 필요가 없습니다. Vercel의 OIDC 연합은 2024년부터 GA였고, 앱이 Vercel 토큰을 AWS, GCP, Azure의 임시 자격증명으로 바꿀 수 있습니다.
  • 채택이 안 되는 이유로 세 가지를 듭니다. 정적 키보다 설정이 복잡하고, 요청 경로에서 토큰을 받거나 교환하는 지연이 늘며, 벤더가 지원할 유인이 약합니다. 1,000곳 넘는 SaaS 벤더를 조사하니 안전한 인증 방식을 지원하는 곳은 8.4%였습니다. 환경 변수의 키는 네트워크 요청이 필요 없지만 토큰은 필요하다는 점을 인정하고, 요청 헤더로 받는 토큰의 재사용과 프로세스 내 캐시로 비용을 줄인다고 설명합니다.
  • 도구로는 신뢰 정책 폼과 서버 측 토큰 검증기를 담은 OIDC UI(shadcn/ui 레지스트리)와, 배포 신원으로 런타임에 제공자 토큰을 요청하는 Vercel Connect를 내놓습니다. 가장 눈에 띄는 것은 압박입니다. Chrome이 HTTP에 "Not secure" 표시를 붙이고 기한을 둔 방식을 따라, 2026-11-30까지 OIDC를 지원하지 않고 장기 비밀로 인증하는 마켓플레이스 벤더는 목록에 "Not secure"가 붙고 2027년 1월에는 퇴출됩니다.

제품 발표가 섞인 글이므로 도구 소개보다 주장과 마감일 설계에 주목할 만합니다. 에이전트에게 권한을 맡기는 시대에 자격증명의 수명을 줄이는 것이 공급망 방어의 일부라는 논지는 npm 공급망 쪽 관심사와 이어지고, 플랫폼이 기한으로 생태계를 미는 방식은 다른 곳에서도 따라 할 가능성이 큽니다.

Interop 2027 전에 보는 다음 CSS 후보

Josh Comeau 원문

Subgrid는 Firefox가 2019년에 구현하고도 정체했지만 Interop 2023에 들어가자 그해 안에 모든 브라우저로 퍼졌습니다.

  • Interop은 Google, Apple, Microsoft, Mozilla가 Igalia, Bocoup과 함께 브라우저 지원을 맞추는 프로젝트입니다. 포커스 영역에 들어가면 보통 1년 안에 모든 브라우저에서 거의 100% 지원에 이릅니다. Subgrid는 2019년에 Firefox가 구현하고 몇 년간 정체했지만 Interop 2023에 들어가자 그해 안에 전 브라우저에 퍼졌습니다. Mozilla가 2027 후보를 투표받고 있어 저자가 투표한 상위 10개를 풀어 씁니다.
  • CSS 함수(@function --name(--arg) { result: ... })는 Chrome 155와 Edge 154에 구현되어 있고 Firefox 157과 Safari 27에는 없습니다. 저자는 극좌표를 translate()로 바꾸는 함수처럼 반복되는 계산을 묶는 용도를 가장 기대합니다.
  • corner-shape(bevel, squircle 등)와 border-shape는 clip-path나 mask와 달리 border-box 자체의 모양을 바꾸므로 테두리, outline, box-shadow가 바뀐 모양을 따라갑니다. 지원이 없는 브라우저에서도 점진적 향상으로 지금 쓸 만하다는 판단입니다.
  • 앵커 포지셔닝 Level 2의 컨테이너 쿼리는 툴팁이 폴백 위치로 뒤집혔는지 CSS에서 알게 해 줍니다. 말풍선 꼬리 방향 같은 용도입니다. 아직 어떤 브라우저에도 없는 inherit() 함수는 상속값에 calc(inherit() - 4px)처럼 변환을 걸어 중첩 border-radius 문제를 풀고, 스크롤 트리거 애니메이션은 지금 IntersectionObserver로 하는 일을 CSS로 옮깁니다.
  • reading-flow와 reading-order는 반응형 레이아웃에서 시각 순서와 DOM 순서가 달라 탭 순서가 틀어지는 접근성 문제를 풉니다. interpolate-size와 calc-size()는 height: 0에서 auto로의 애니메이션을 가능하게 합니다.

지금 당장 쓸 기능이 아니라 1년 뒤 어디까지 쓸 수 있을지 보는 지도입니다. Interop에 들어간 기능은 그해 안에 전 브라우저에 퍼진다는 전례가 있으니, 투표 결과가 나오면 2027년의 CSS 계획을 세우는 근거가 됩니다. 이 요약은 글의 앞 8개 항목을 기준으로 합니다.

짧게 알아두기

  • Node.js 22.23.4 (LTS) Node.js Blog 보안 릴리스입니다. tls, dns, brotli 쪽 High 3건과 Medium 2건, Low 1건을 고쳤으니 22.x를 쓰는 환경은 올립니다.

수집 상태

수집 7609건, 본문 확보 24건, 실은 글 5편. 실패한 소스: CSS-Tricks (파싱 결과 0건)

지난 호전체 보기

2026-10-22코드를 이해하지 못할 때 에이전트로 디버깅하기2편2026-10-20프로덕션급 LLM과 에이전트 현장 가이드4편

주간전체 보기

2026-W4242주차23편