Luau 치트시트
타입 검사 모드
파일 첫 줄의 주석이 Luau가 얼마나 엄격하게 검사할지 결정합니다.
| 모드 | 문법 |
|---|---|
| 타입 검사 없음 | --!nocheck |
| 기본: 주석이 붙은 곳만 검사 | --!nonstrict |
| strict: 추론하고 전부 검사 | --!strict |
| 파일 실행 | luau main.luau |
| 실행 없이 타입 검사 | luau-analyze main.luau |
| lint 경고 하나 끄기 | --!nolint UnknownGlobal |
기본 타입
원시 타입과 변수에 주석을 붙이는 방법.
| 작업 | 문법 |
|---|---|
| 숫자 | local age: number = 25 |
| 문자열 | local name: string = "Ada" |
| 불리언 | local ok: boolean = true |
| 옵셔널(nil 가능) | local nick: string? = nil |
| any(검사 제외) | local x: any = f() |
| unknown(더 안전한 any) | local x: unknown = f() |
| never(항상 오류를 던짐) | function fail(): never error("boom") end |
| nil | local n: nil = nil |
| 코루틴 / buffer | local co: thread, local b: buffer |
| 추론됨 - 주석 불필요 | local hp = 100 -- number |
타입 지정 테이블: 배열과 맵
여전히 Lua 테이블입니다. 타입은 그 안에 무엇이 들었는지 알려줄 뿐입니다.
| 작업 | 문법 |
|---|---|
| 숫자 배열 | local xs: {number} = {1, 2, 3} |
| 문자열 배열 | local names: {string} = {} |
| 맵(문자열 키) | local ages: {[string]: number} = {} |
| 맵(임의의 키 타입) | local seen: {[Player]: boolean} = {} |
| 중첩 배열 | local grid: {{number}} = {} |
| 형태 배열 | local items: {{id: number}} = {} |
| 읽기 전용 배열 | local t = table.freeze({1, 2, 3}) |
| 반복(일반화된 반복, Luau 전용) | for i, v in xs do print(i, v) end |
| 키와 값 반복 | for k, v in pairs(ages) do end |
함수
매개변수와 반환 타입에 주석을 붙입니다. -> ()는 반환값이 없다는 뜻입니다.
| 작업 | 문법 |
|---|---|
| 타입 지정 매개변수와 반환 | function add(a: number, b: number): number |
| 반환값 없음 | function log(msg: string): () end |
| 여러 값 반환 | function split(s: string): (string, string) |
| 옵셔널 매개변수 | function greet(name: string?) end |
| 기본값 | function greet(name: string?) name = name or "friend" end |
| 가변 인자 | function sum(...: number): number end |
| 익명 함수 | local f = function(x: number): number return x * 2 end |
| 함수 타입 | type Adder = (number, number) -> number |
| 콜백 매개변수 | function each(f: (number) -> ()) end |
타입 별칭, 유니온, 리터럴
타입에 한 번 이름을 붙이고 어디서든 재사용하세요.
| 작업 | 문법 |
|---|---|
| 원시 타입 별칭 | type Health = number |
| 유니온 | type Id = number | string |
| 옵셔널 = nil과의 유니온 | type Maybe = string | nil -- same as string? |
| 리터럴(싱글턴) 타입 | type Dir = "up" | "down" |
| 인터섹션 | type Both = Named & Aged |
| 제네릭 별칭 | type List<T> = {T} |
| 모듈에서 내보내기 | export type Point = { x: number, y: number } |
| 기존 값의 타입 | type Config = typeof(config) |
| 테이블 타입의 키 | type K = keyof<Point> |
테이블 형태와 메서드
객체를 서술하는 방법과 콜론 호출에 따라오는 self.
| 작업 | 문법 |
|---|---|
| 인라인 형태 | local p: { x: number, y: number } = { x = 0, y = 0 } |
| 이름 있는 형태 | type Point = { x: number, y: number } |
| 옵셔널 속성 | type User = { name: string, age: number? } |
| 형태 + 인덱서 | type Bag = { count: number, [string]: any } |
| 형태 확장 | type Admin = User & { level: number } |
| 메서드(콜론 단축 문법) | function Point.move(self: Point, dx: number) end |
| 타입 안의 메서드 | type Point = { move: (self: Point, dx: number) -> () } |
| 생성자 패턴 | function Point.new(x: number): Point end |
| 고정된 상수(enum 대용) | local Color = table.freeze({ Red = 1, Blue = 2 }) |
제네릭
여러 값 타입에 대해 동작하는 하나의 함수 또는 타입.
| 작업 | 문법 |
|---|---|
| 제네릭 함수 | function id<T>(x: T): T return x end |
| 타입 매개변수 2개 | function pair<A, B>(a: A, b: B): (A, B) end |
| 배열에 대한 제네릭 | function first<T>(xs: {T}): T? return xs[1] end |
| 제네릭 타입 별칭 | type Stack<T> = { items: {T} } |
| 별칭 인스턴스화 | local s: Stack<string> = { items = {} } |
| 제네릭 map 함수 | function map<T, U>(xs: {T}, f: (T) -> U): {U} end |
| 제네릭 팩(가변 인자) | function call<T...>(f: () -> T...): T... end |
좁히기, 어서션, 타입 가드
이미 알고 있는 값의 타입을 검사기에게 납득시키는 방법.
| 작업 | 문법 |
|---|---|
| 타입 어서션(캐스트) | local n = value :: number |
| any를 거친 캐스트 | local n = (value :: any) :: number |
| typeof로 좁히기 | if typeof(x) == "string" then -- x is string end |
| 옵셔널 좁히기 | if name then print(#name) end |
| nil이면 조기 반환 | if not name then return end |
| 리터럴 유니온 좁히기 | if dir == "up" then end |
| 런타임 검사 | assert(typeof(id) == "number", "id must be a number") |
| nil이 아님을 보장하고 사용 | local item = assert(find(id), "missing item") |
Lua에는 없는 연산자와 문법
일상적인 편의 기능 - Luau 코드가 Lua처럼 보이지 않게 되는 지점입니다.
| 작업 | 문법 |
|---|---|
| 더해서 대입 | hp += 10 |
| 빼기 / 곱하기 / 나누기 | hp -= 5, dmg *= 2, dmg /= 2 |
| 내림 나눗셈 후 대입 | n //= 2 |
| 나머지 / 거듭제곱 후 대입 | n %= 3, n ^= 2 |
| 이어붙여 대입 | msg ..= "!" |
| 내림 나눗셈 | local half = 7 // 2 -- 3 |
| 문자열 보간 | Hello, {name}! You have {n} items. (백틱으로 감싸기) |
| 식을 끼워 넣기 | Total: {price * qty} (백틱으로 감싸기) |
| 루프 계속하기 | for _, v in xs do if v < 0 then continue end end |
| if-then-else 표현식 | local label = if hp > 0 then "alive" else "dead" |
| else-if를 이은 표현식 | local t = if n > 0 then "pos" elseif n < 0 then "neg" else "zero" |
| 2진 리터럴 / 자리 구분 | 0b1010, 1_000_000 |
라이브러리 추가와 Lua와의 차이
Luau가 표준 라이브러리에 더한 함수와, Lua에서 제거한 기능.
| 항목 | 설명 |
|---|---|
| 배열에서 값 찾기 | table.find(xs, 42) -- 인덱스 또는 nil |
| 얕은 복사 | local copy = table.clone(t) |
| 불변으로 만들기 / 확인 | table.freeze(t), table.isfrozen(t) |
| 배열 미리 할당 | table.create(10, 0) |
| clamp / round / sign | math.clamp(x, 0, 1), math.round(x), math.sign(x) |
| 비트 연산 | bit32.band(a, b), bit32.lshift(a, 1) |
| 샌드박싱을 위해 제거 | io, package, loadstring, loadfile, dofile |
| 더 이상 권장되지 않지만 존재 | getfenv, setfenv (최적화가 비활성화됨) |
| Luau에 없음 | goto / 레이블 (Luau는 Lua 5.1 기반) |
| Coddy에서 stdin 읽기 | io.read()는 여기서 동작합니다 - Coddy가 Luau 위에 되살려 둡니다 |
| 파일 확장자 | main.luau (.lua도 동작) |
| Roblox 전용(코어 Luau 아님) | game, workspace, Instance.new, task.wait |
Luau는 Lua 5.1에 점진적 타입 시스템을 더한 언어로, Roblox가 만들어 오픈소스로 공개했습니다. 이 치트시트는 순수 Lua가 *아닌* 부분을 다룹니다: 타입 주석, 타입 지정 테이블, 유니온과 리터럴 타입, 제네릭, 타입 어서션, 그리고 Luau가 추가한 문법 설탕(+=, 문자열 보간, continue, if-then-else 표현식). 두 언어가 공유하는 테이블, 메타테이블, 코루틴, 문자열 라이브러리는 Lua 치트시트를 함께 열어 두세요.
여기 있는 모든 코드는 표준 Luau이며 luau CLI로 실행하고 luau-analyze로 타입을 검사할 수 있습니다. 필요한 줄을 복사하거나 Luau 플레이그라운드에서 바로 실행해 보세요 - 설치할 것이 없습니다. 타입 시스템을 처음부터 배우고 싶다면 Coddy의 무료 인터랙티브 Luau 강의가 레슨 단위로 쌓아 줍니다.
Luau 치트시트 FAQ
이 Luau 치트시트는 무료인가요?
Luau에 += 연산자가 있나요?
+=, -=, *=, /=, //=, %=, ^=, 그리고 문자열 연결용 ..=. 즉 hp += 10은 올바른 Luau(그리고 올바른 Roblox 코드)이며, Lua 5.1에서는 hp = hp + 10으로 써야 합니다. 참고로 Luau에는 ++나 --가 없습니다.Lua와 Luau의 차이는 무엇인가요?
continue, if-then-else 표현식, 내림 나눗셈 //, 일반화된 반복(for i, v in t do), table.find·table.clone·table.freeze 같은 함수를 추가합니다. 샌드박싱을 위해 loadstring, loadfile, dofile과 io·package 라이브러리를 제거했고, 5.1 기반이라 goto가 없습니다. 일반적인 Lua 코드는 대부분 수정 없이 Luau로 실행됩니다.Luau에서 타입 검사를 어떻게 켜나요?
--!strict는 모든 것을 검사하고 적극적으로 추론하며, --!nonstrict(기본값)는 주석을 붙인 곳만 검사하고, --!nocheck는 검사를 끕니다. 에디터 밖에서는 실행 없이 검사할 때 luau-analyze main.luau, 실행할 때 luau main.luau를 씁니다.TypeScript처럼 interface를 쓰나요?
interface 키워드가 없습니다. 객체 형태는 테이블 타입으로 쓰고 type으로 이름을 붙입니다: type User = { name: string, age: number? }. extends 대신 &로 형태를 합치고, 다른 모듈에 공개할 때는 export type을 사용합니다.이 치트시트가 Roblox 스크립팅과 같은 건가요?
game, workspace, Instance.new, 서비스, 이벤트)로, 언어 위에 올라간 라이브러리이며 Luau가 아니라 Roblox가 문서화합니다.이 Luau 코드는 어디서 실행할 수 있나요?
luau 인터프리터를 stdin 지원과 함께 실행하므로 이 페이지의 어떤 줄이든 붙여 넣고 실행을 누르면 됩니다. 체계적인 연습을 원한다면 인터랙티브 Luau 강의가 같은 내용을 연습 문제, 프로젝트, 수료증과 함께 다룹니다.