한국어
BSD 시스템에서의 개발 (OpenBSD/FreeBSD)
이 가이드는 Tailwind CSS v4와 Patchright E2E 테스트를 위한 BSD 관련 설정 요구사항을 다룹니다.
개요
Tailwind CSS v4는 성능 향상을 위해 PostCSS에서 Rust 기반 네이티브 바이너리로 마이그레이션되었습니다. 두 개의 네이티브 모듈이 필요합니다:
- lightningcss - CSS 파싱, 변환, 압축
- @tailwindcss/oxide - Tailwind CSS v4 코어 엔진
이 패키지들은 OpenBSD/FreeBSD용 사전 빌드된 바이너리를 제공하지 않으므로 소스에서 직접 빌드해야 합니다.
사전 요구사항
bash
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify installation
cargo --version
rustc --versionlightningcss 빌드
1. 클론 및 빌드
bash
# Clone repository
cd /tmp
git clone https://github.com/parcel-bundler/lightningcss.git
cd lightningcss
# Build the Node.js addon
cargo build --release -p lightningcss_node
# Build the CLI tool (optional)
cargo build --release --bin lightningcss --features cli2. 바이너리 설치
bash
# Create storage directory
mkdir -p ~/.local/lib/node-native-openbsd
# Copy Node.js addon
cp target/release/liblightningcss_node.so \
~/.local/lib/node-native-openbsd/lightningcss.openbsd-x64.node
# Copy CLI tool (optional)
cp target/release/lightningcss ~/.cargo/bin/참고: .so 공유 라이브러리는 Node.js NAPI-RS 로딩을 위해 플랫폼 접미사가 붙은 .node로 이름이 변경됩니다.
@tailwindcss/oxide 빌드
1. 클론 및 빌드
bash
# Clone repository
cd /tmp
git clone https://github.com/tailwindlabs/tailwindcss.git
cd tailwindcss
# Build the oxide crate
cargo build --release -p tailwind-oxide2. 바이너리 설치
bash
# Copy to storage directory
cp target/release/libtailwind_oxide.so \
~/.local/lib/node-native-openbsd/tailwindcss-oxide.openbsd-x64.node의존성 설치
--legacy-peer-deps로 npm install
Vite 7과 (Vite 6을 요구하는) @sveltejs/[email protected] 사이의 피어 의존성 충돌로 인해 --legacy-peer-deps 플래그를 사용해야 합니다:
bash
# In web/admin
cd web/admin
npm install --legacy-peer-deps
# In web/site
cd web/site
npm install --legacy-peer-deps필요한 이유:
- Vite 7은 최신 기능을 위해 필요합니다
@sveltejs/[email protected]은 Vite 6에 대한 피어 의존성을 선언합니다- 버전 불일치에도 불구하고 두 패키지는 함께 정상적으로 작동합니다
--legacy-peer-deps는 npm의 엄격한 피어 의존성 검사를 우회합니다
SvelteKit Sync
의존성 설치 후, TypeScript 설정을 생성하기 위해 SvelteKit sync를 실행하세요:
bash
# In web/admin
cd web/admin
./node_modules/.bin/svelte-kit sync
# In web/site
cd web/site
./node_modules/.bin/svelte-kit sync자동화된 설치
바이너리를 한 번 빌드한 후에는 postinstall 스크립트를 사용하여 npm install 이후 자동으로 복원할 수 있습니다:
수동 실행
bash
# From project root
node scripts/postinstall-openbsd-natives.jspackage.json에 추가 (선택 사항)
json
{
"scripts": {
"postinstall": "node scripts/postinstall-openbsd-natives.js"
}
}Postinstall 스크립트가 하는 일
이 스크립트(scripts/postinstall-openbsd-natives.js)는 두 가지 작업을 수행합니다:
1. 네이티브 바이너리 복사
~/.local/lib/node-native-openbsd/에서 사전 빌드된 바이너리를 다음 위치로 복사합니다:
web/admin/node_modules/@tailwindcss/node/node_modules/lightningcss/lightningcss.openbsd-x64.nodeweb/admin/node_modules/@tailwindcss/oxide/tailwindcss-oxide.openbsd-x64.nodeweb/site/node_modules/@tailwindcss/node/node_modules/lightningcss/lightningcss.openbsd-x64.nodeweb/site/node_modules/@tailwindcss/oxide/tailwindcss-oxide.openbsd-x64.node
2. JavaScript 로더 패치
@tailwindcss/oxide/index.js의 경우:
"Unsupported OS" 오류 앞에 OpenBSD/FreeBSD 플랫폼 감지 코드를 추가합니다:
javascript
} else if (process.platform === 'openbsd' || process.platform === 'freebsd') {
if (process.arch === 'x64') {
try {
return require('./tailwindcss-oxide.openbsd-x64.node')
} catch (e) {
loadErrors.push(e)
}
} else {
loadErrors.push(new Error(`Unsupported architecture on BSD: ${process.arch}`))
}
} else {
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
}lightningcss의 경우:
패치가 필요하지 않습니다 — lightningcss는 NAPI-RS 네이밍 규칙을 통해 커스텀 플랫폼 바이너리를 이미 지원합니다.
패치가 필요한 이유
NAPI-RS(Rust-to-Node.js 바인딩)는 플랫폼 감지 코드를 자동으로 생성하지만, "공식적으로 지원되는" 플랫폼(Windows, macOS, Linux)에 대해서만 생성합니다. 생성된 index.js에는 process.platform을 확인하는 if/else 체인이 포함되어 있습니다:
javascript
if (process.platform === 'darwin') {
// macOS loading
} else if (process.platform === 'linux') {
// Linux loading
} else if (process.platform === 'win32') {
// Windows loading
} else {
// Error: Unsupported OS
}패치는 최종 오류 이전에 OpenBSD/FreeBSD 분기를 추가하여 Node.js가 .openbsd-x64.node 바이너리를 로드하도록 합니다.
파일 네이밍 규칙
네이티브 Node.js addon은 다음 패턴을 따릅니다:
<package-name>.<platform>-<arch>.node예시:
lightningcss.openbsd-x64.nodetailwindcss-oxide.openbsd-x64.nodetailwindcss-oxide.linux-x64.node(비교용)
검증
postinstall 스크립트 실행 후 빌드가 정상 작동하는지 확인하세요:
bash
# Test admin build
cd web/admin
npm run build
# Test site build
cd web/site
npm run build예상 출력:
vite v7.3.6 building ssr environment for production...
✓ 305 modules transformed.
✓ built in X.XXs문제 해결
npm ERESOLVE unable to resolve dependency tree
오류:
npm error ERESOLVE unable to resolve dependency tree
npm error peer vite@"^6.0.0" from @sveltejs/[email protected]원인: Vite 7과 @sveltejs/vite-plugin-svelte 간의 피어 의존성 충돌.
해결:
bash
npm install --legacy-peer-deps"Cannot find module '../lightningcss.openbsd-x64.node'"
원인: 바이너리가 node_modules에 복사되지 않았습니다.
해결:
bash
node scripts/postinstall-openbsd-natives.js"Cannot find native binding"
원인: oxide의 index.js가 패치되지 않았습니다.
해결:
bash
# Re-run postinstall (includes patching)
node scripts/postinstall-openbsd-natives.js"Unsupported OS: openbsd"로 빌드 실패
원인: 패치가 적용되지 않았거나 npm install에 의해 덮어써졌습니다.
해결:
bash
# Always run postinstall after npm install
npm install --legacy-peer-deps
node scripts/postinstall-openbsd-natives.jsCargo 빌드 실패
일반적인 원인:
- Rust 미설치: https://rustup.rs 에서 설치하세요
- lightningcss 저장소에서 npm install 중 Puppeteer 오류:
- 클론한 저장소에서
npm build를 사용하지 마세요 - 대신
cargo build를 직접 사용하세요
- 클론한 저장소에서
- 오래된 Rust 버전:
rustup update로 업데이트하세요
대안: 수동 설치
postinstall 스크립트가 작동하지 않으면 수동으로 복사하고 패치하세요:
bash
# Copy binaries
cp ~/.local/lib/node-native-openbsd/lightningcss.openbsd-x64.node \
web/admin/node_modules/@tailwindcss/node/node_modules/lightningcss/
cp ~/.local/lib/node-native-openbsd/tailwindcss-oxide.openbsd-x64.node \
web/admin/node_modules/@tailwindcss/oxide/
# Patch oxide (lightningcss needs no patch)
# Edit web/admin/node_modules/@tailwindcss/oxide/index.js
# Add the OpenBSD/FreeBSD branch shown above유지 보수
@tailwindcss/oxide 또는 lightningcss를 업데이트하는 npm install 이후:
bash
# Re-apply patches and binaries
npm install --legacy-peer-deps
node scripts/postinstall-openbsd-natives.js참고: Vite 7과 SvelteKit 플러그인 간의 피어 의존성 충돌을 피하려면 npm install 실행 시 항상 --legacy-peer-deps를 사용하세요.
자동으로 실행되도록 package.json 스크립트에 추가하는 것을 고려하세요.
Patchright를 사용한 E2E 테스트
개요
Patchright(Playwright의 포크)는 BSD 시스템에서 비디오 녹화를 위해 플랫폼별 패치와 ffmpeg 설정이 필요합니다.
Patchright 플랫폼 패치
scripts/patch-patchright-openbsd.js 스크립트는 npm install 후 자동으로 Patchright를 패치하여 OpenBSD/FreeBSD를 지원합니다:
적용되는 패치:
- 캐시 디렉토리 감지 (3곳)
- 사용자 에이전트 플랫폼 매핑
- 창 인셋(window insets) 설정
- Chrome 채널 실행 파일 경로
- BSD 시스템용 플랫폼 감지
- ffmpeg 플랫폼 매핑
이 패치들은 package.json의 postinstall 훅을 통해 자동으로 실행됩니다.
비디오 녹화를 위한 ffmpeg 설정
문제
Playwright/Patchright는 번들된 ffmpeg 바이너리가 다음 위치에 있을 것으로 예상합니다:
~/.cache/ms-playwright/ffmpeg-1011/ffmpeg-linux하지만 Patchright는 Linux용 ffmpeg 바이너리를 배포하지 않습니다 (linux-x64로 매핑되는 OpenBSD 포함). patchright install ffmpeg 명령은 Linux 시스템에 ffmpeg가 시스템 전역으로 설치되어 있을 것이라고 가정하기 때문에 실패합니다.
해결책
시스템의 ffmpeg는 필요한 모든 코덱을 포함하여 완전히 작동합니다. Patchright가 예상하는 위치로 심볼릭 링크만 걸어주면 됩니다:
bash
# Install ffmpeg (if not already installed)
doas pkg_add ffmpeg
# Create Patchright cache directory
mkdir -p ~/.cache/ms-playwright/ffmpeg-1011
# Symlink system ffmpeg to expected location
ln -sf /usr/local/bin/ffmpeg ~/.cache/ms-playwright/ffmpeg-1011/ffmpeg-linux검증
bash
# Verify symlink
ls -la ~/.cache/ms-playwright/ffmpeg-1011/
# Should show:
# lrwxr-xr-x 1 user group 21 Jul 24 08:55 ffmpeg-linux -> /usr/local/bin/ffmpeg비디오 녹화 설정
playwright.config.ts의 비디오 녹화 옵션:
typescript
video: 'off', // Disabled - no video recording
video: 'on', // Always record (requires ffmpeg)
video: 'retain-on-failure', // Only save videos when tests fail (recommended)
video: 'on-first-retry', // Only record on retries권장: retain-on-failure - 디스크 공간을 절약하면서 실패한 테스트의 디버깅용 비디오는 보존합니다.
E2E 테스트를 위한 시스템 요구사항
bash
# Required packages
doas pkg_add chromium ffmpeg
# Verify installations
chrome --version # Should show Chromium version
ffmpeg -version # Should show ffmpeg 8.x with codecsE2E 테스트 실행
bash
# Build and run all tests
npm run test:e2e
# Run without rebuilding
npm run test:e2e:nobuild
# Run with UI (interactive mode)
npm run test:e2e:ui
# Run with debugging
npm run test:e2e:debug
# View test report
npm run test:e2e:reportE2E 테스트 문제 해결
"Executable doesn't exist at .../ffmpeg-linux"
원인: ffmpeg 심볼릭 링크가 생성되지 않았습니다.
해결:
bash
mkdir -p ~/.cache/ms-playwright/ffmpeg-1011
ln -sf /usr/local/bin/ffmpeg ~/.cache/ms-playwright/ffmpeg-1011/ffmpeg-linux"ERROR: Patchright does not support ffmpeg on linux-x64"
원인: 예상된 동작입니다 — Patchright는 Linux용 ffmpeg를 배포하지 않습니다.
해결: patchright install ffmpeg 대신 위의 심볼릭 링크 방식을 사용하세요.
테스트에서 Chrome을 시작하지 못함
원인: Chromium이 설치되지 않았거나 경로가 잘못되었습니다.
해결:
bash
doas pkg_add chromium
which chrome # Should show /usr/local/bin/chromePatchright 패치가 적용되지 않음
원인: npm install은 실행되었지만 postinstall 훅이 실행되지 않았습니다.
해결:
bash
npm run postinstall
# Should show: "✅ Successfully applied X patches for openbsd"시스템 ffmpeg가 작동하는 이유
OpenBSD의 ffmpeg(버전 8.x)는 Playwright에 필요한 모든 코덱을 포함합니다:
- 비디오: H.264(libx264), H.265(libx265), VP8/VP9(libvpx), AV1(libaom, libdav1d, libsvtav1)
- 오디오: AAC, Opus, Vorbis, MP3(libmp3lame)
- 컨테이너: MP4, WebM, Matroska
이는 OpenBSD ports 팀에 의해 유지 관리되고 최신 상태로 유지되므로 번들 바이너리보다 더 신뢰할 수 있습니다.
관련 파일
scripts/postinstall-openbsd-natives.js- Tailwind용 자동화된 패치 및 바이너리 설치 스크립트scripts/patch-patchright-openbsd.js- E2E 테스트를 위한 Patchright 플랫폼 패치playwright.config.ts- E2E 테스트 설정~/.local/lib/node-native-openbsd/- 사전 빌드된 Tailwind 바이너리 저장소~/.cache/ms-playwright/- Patchright 캐시 디렉토리
기술 배경
Tailwind v4가 네이티브 바이너리를 필요로 하는 이유
Tailwind CSS v4 아키텍처:
- v3: PostCSS 플러그인 (순수 JavaScript, 느림)
- v4: Rust 기반 엔진 (컴파일된 네이티브 코드, 약 100배 빠름)
Rust 코드는 플랫폼별 공유 라이브러리(BSD/Linux에서는 .so, macOS에서는 .dylib, Windows에서는 .dll)로 컴파일되고 NAPI-RS를 사용하여 Node.js 네이티브 addon(.node 파일)으로 래핑됩니다.
.so → .node 이름 변경 이유
Node.js는 .node 확장자를 가진 네이티브 addon을 예상합니다. Rust 빌드는 다음을 생성합니다:
liblightningcss_node.so(Rust/Cargo 네이밍)libtailwind_oxide.so(Rust/Cargo 네이밍)
NAPI-RS 로더가 예상하는 형식:
<package>.<platform>-<arch>.node(Node.js 네이밍)
복사 단계에서 Node.js 규칙에 맞게 이 파일들의 이름을 변경하고 이동시킵니다.
NAPI-RS 로딩 메커니즘
- 패키지의
index.js가process.platform과process.arch를 확인합니다 - 파일명을 구성합니다:
<package>.${platform}-${arch}.node require('./constructed-filename')을 시도합니다- 찾지 못하면 대체 플랫폼을 시도합니다
- 모두 실패하면 "Cannot find native binding" 오류를 발생시킵니다
패치는 2.5단계를 추가합니다: "BSD라면 .openbsd-x64.node를 로드"