콘텐츠로 이동

00. 설치

대상 독자: Roblox Studio 또는 Luau CLI 툴체인(Rojo, pesde 등) 환경에서 Quad를 프로젝트에 붙이려는 개발자 목표: 패키지를 내려받아 게임 트리에 올리고, 타입 검사 플래그를 켜기

이 장은 까는 것만 다룹니다. 실제로 화면에 무언가를 띄우는 건 다음 장인 01. 프레임워크 설정하기입니다.


Quad는 아래 세 가지 경로로 배포합니다. [2026-09-11 기준] 제공되는 건 pesde 경로 하나입니다(pesde 레지스트리에 3.1.0이 게시됨) — Wally 게시와 릴리스 페이지는 아직 없습니다.

배포 방식 추천 대상 필요한 도구 상태
1. pesde + Rojo Luau 패키지 매니저를 쓰는 프로젝트 pesde, rojo 제공 중 (3.1.0)
2. Wally + Rojo 이미 Wally를 쓰고 있는 프로젝트 wally, rojo 미제공
3. Standalone .rbxm CLI 도구 없이 Studio만 쓰는 경우 없음 미제공
Wally나 .rbxm으로는 못 쓰나요?

[2026-09-10 기준] 아직 그 경로가 없습니다. Wally 레지스트리 게시도, 배포용 .rbxm을 올릴 릴리스 페이지도 준비되지 않았습니다 — 생기면 이 절에 패키지 이름·버전과 링크를 채워 넣습니다.

Wally로 오게 되더라도 받을 것은 입니다. 01장의 설정 모듈이 quad_types를 require해 타입을 다시 내보내기 때문에, quad_base·quad_roblox만으로는 그 require가 풀리지 않습니다.


pesde는 의존성을 내려받아 배치하는 도구일 뿐, Studio 안의 ModuleScript로 싱크해 주지는 않습니다. 따라서 Rojo가 반드시 함께 필요합니다 — pesde가 디스크에 놓은 폴더를 게임 트리로 투영하는 건 Rojo의 몫입니다.

[dependencies]
quad_base = { name = "qwreey/quad_base", version = "^3.0.0" }
quad_roblox = { name = "qwreey/quad_roblox", version = "^3.0.0" }
quad_types = { name = "qwreey/quad_types", version = "^3.0.0" }

셋을 직접 적는 데는 각각 이유가 있습니다.

  • quad_roblox — Roblox 백엔드. 여러분이 실제로 쓰는 D가 여기서 옵니다.
  • quad_basequad_roblox는 이걸 개발 의존성으로만 잡습니다(런타임에는 모듈 인스턴스를 인자로 받으므로 require하지 않습니다). 개발 의존성은 소비자에게 전파되지 않으니 직접 적어야 합니다.
  • quad_types — 타입만 든 패키지입니다. 01장의 설정 모듈이 이걸 require해 타입을 다시 내보내는데, 직접 의존성으로 적어야 최상위에 링커가 생겨 그 require가 풀립니다.

quad_error / type_version_check는 위 셋의 의존성으로 따라 들어오므로 적을 필요가 없습니다. ^3.0.0은 3.x 안에서 최신을 받겠다는 뜻입니다 — 정확한 버전으로 고정하려면 version = "3.0.0"처럼 쓰세요.

이름을 적을 때 주의할 것이 하나 있습니다. 저장소상 패키지 이름은 언더스코어만 씁니다qwreey/quad_base, qwreey/quad_roblox처럼요(폴더 이름은 quad-base/quad-roblox로 하이픈이고, 매니페스트의 name만 언더스코어입니다). 다섯 패키지가 같은 버전으로 게시되며 현재 버전은 3.1.0입니다. 저장소 루트의 qwreey/quad는 워크스페이스 루트일 뿐 게시 대상이 아니라(private = true) 이 이름으로는 설치할 수 없습니다.

pesde는 설치 디렉터리를 “의존 대상 패키지 자신의 target” 이름으로 나눕니다. 다섯 패키지 중 quad_robloxroblox 타깃이고, 나머지 넷은 luau·roblox 두 타깃으로 게시됩니다. 그래서 여러분의 매니페스트 [target] environmentroblox이면 다섯 개가 전부 roblox_packages/ 하나에 들어옵니다. 이 경우 luau_packages/는 생기지 않습니다.

타깃이 roblox가 아니면 어떻게 되나요?

environmentroblox가 아니면(예: luau) 넷의 luau 사본이 luau_packages/로 들어가므로 그 디렉터리도 트리에 올려야 합니다.

pesde는 설치 디렉터리 안에 얇은 링커를 놓고 실체는 .pesde/ 아래에 둡니다 — 직접 적은 셋만 최상위에 링커가 생기지만, 따라 들어오는 둘도 그 아래로 링크되므로 트리에 올릴 건 roblox_packages/ 하나입니다(그 폴더를 통째로 매핑해야 하는 이유는 아래 매핑 지시에 있습니다).

roblox_sync_config_generator 스크립트 — pesde는 roblox 타깃 프로젝트의 매니페스트에 [scripts] roblox_sync_config_generator가 없으면 설치 때 not having a roblox_sync_config_generator script in the manifest might cause issues with linking 경고를 냅니다. pesde 공식 Roblox 가이드가 안내하는 scripts 패키지를 매니페스트에 넣어 두는 걸 권장합니다.

이 문서의 예제들은 아래 모양을 씁니다. 디스크의 src/client/UI/가 게임 트리의 ReplicatedStorage.Client.UI가 되고, 진입점 하나가 StarterPlayerScripts로 갑니다.

src/client/UI/Quad.luau → ReplicatedStorage/Client/UI/Quad (설정 모듈, 01장)
src/client/UI/Counter.luau → ReplicatedStorage/Client/UI/Counter (컴포넌트, 11장)
src/client/Main.client.luau → StarterPlayerScripts/Main (진입점, 01장)
roblox_packages/ → ReplicatedStorage/roblox_packages
{
"name": "MyQuadApp",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"$className": "ReplicatedStorage",
"roblox_packages": { "$path": "roblox_packages" },
"Client": {
"$className": "Folder",
"UI": { "$path": "src/client/UI" }
}
},
"StarterPlayer": {
"$className": "StarterPlayer",
"StarterPlayerScripts": {
"$className": "StarterPlayerScripts",
"Main": { "$path": "src/client/Main.client.luau" }
}
}
}
}

이 매핑에서 중요한 건 이름이 아니라 모양입니다. Quad 소스는 require("./roblox_packages/…")처럼 자기 폴더의 형제를 상대 경로로 가리키므로, 디스크에서 형제였던 것이 Instance 공간에서도 형제여야 합니다. 그리고 패키지 디렉터리는 통째로 매핑하세요 — pesde가 놓는 roblox_packages/quad_roblox.luau는 그 아래 .pesde/… 실체를 가리키는 얇은 링커라, 점으로 시작하는 그 하위 트리까지 같이 올라가야 require가 풀립니다(Rojo는 .pesde를 정상적으로 따라갑니다).

위 경로들은 프로젝트 구성에 따라 달라지는 자리입니다. 실제 이름은 여러분의 default.project.json에 맞춰 바꾸세요.

Quad를 쓰는 코드는 아래 네 플래그가 전부 켜져 있어야 타입 검사가 돕니다. 편집기(luau-lsp)와 CI 양쪽에 같은 값을 넣으세요.

터미널 창
luau-lsp analyze \
--flag:LuauSolverV2=true \
--flag:LuauTarjanChildLimit=160000 \
--flag:LuauSubtypingIterationLimit=100000 \
--flag:LuauTypeInferIterationLimit=1000000 \
--definitions=<Roblox 타입 정의 파일> \
<검사할 파일들>
  • LuauSolverV2=true가 없으면 quad 소스의 타입 검사가 실패합니다 — TypeError: read keyword is illegal here.
  • LuauTarjanChildLimit을 올리지 않으면 D.Frame { Name = "x" } 한 줄만 있어도 TypeError: Internal error: Code is too complex to typecheck!로 죽습니다. 생성된 D의 프로퍼티 유니언이 크기 때문입니다.
  • 나머지 둘(LuauSubtypingIterationLimit/LuauTypeInferIterationLimit)은 컴포넌트가 커질 때 같은 이유로 필요해집니다.