클로저를 이용한 캡슐화
클로저 기반 객체 패턴으로 비공개 상태를 숨깁니다.
클로저를 이용한 캡슐화은(는) CoddyKit의 무료 Lua Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Lua Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
클로저 기반 객체
메타테이블 객체 지향 프로그래밍의 대안으로, 클로저를 사용해 상태를 숨길 수 있습니다. 객체는 함수 테이블이며 모든 함수가 비공개 변수에 대한 클로저를 형성합니다. 메타테이블은 필요하지 않습니다. 외부에서 접근할 수 없는 진정한 비공개 상태를 구현할 수 있습니다.
local function makeCounter(start)
local count = start or 0 -- truly private
return {
increment = function() count=count+1 end,
decrement = function() count=count-1 end,
get = function() return count end,
reset = function() count=0 end,
}
end
local c = makeCounter(10)
c.increment(); c.increment()
print(c.get()) -- 12
print(c.count) -- nil (truly private!)비공개와 공개
클로저를 사용하면 비공개와 공개의 구분이 절대적입니다. 클로저는 상위 값을 공유하고(비공개), 외부 코드는 반환된 테이블만 볼 수 있습니다(공개). 공개 API를 거치지 않고는 비공개 변수에 접근할 방법이 없습니다.
local function makeAccount(initial)
local balance = initial
local transactions = {}
local function record(type, amount)
transactions[#transactions+1] = {type=type,amount=amount}
end
return {
deposit = function(n) record("D",n); balance=balance+n end,
withdraw = function(n)
if n>balance then error("insufficient funds") end
record("W",n); balance=balance-n
end,
balance = function() return balance end,
history = function() return transactions end,
}
end공유 클로저
같은 반환 테이블에 있는 여러 함수는 동일한 상위 값을 공유합니다. 이것이 상태를 외부에 공개하지 않고 메서드 간에 공유하는 방법입니다. 모든 메서드는 동일한 비공개 변수를 확인합니다.
local function makeQueue()
local items = {} -- shared by all methods
local size = 0
return {
enqueue = function(v) size=size+1; items[size]=v end,
dequeue = function()
if size==0 then return nil end
local head=1
while items[head]==nil do head=head+1 end
local v=items[head]; items[head]=nil; return v
end,
isEmpty = function() return size==0 end,
count = function() return size end,
}
end클로저와 메타테이블
클로저: 상태가 진정으로 비공개이고 메타테이블 오버헤드가 없지만, 각 인스턴스가 함수의 자체 사본을 가지므로 메모리를 더 사용합니다. 메타테이블: 메서드를 공유하여 메모리를 덜 사용하고, 디버그 도구로 검사할 수 있으며, 상속을 지원합니다. 개인정보 보호 요구 사항과 인스턴스 수를 기준으로 선택하세요.
-- Closure: per-instance functions (more memory)
local function makePoint(x,y)
return {
x = function() return x end,
y = function() return y end,
distance = function()
return math.sqrt(x*x+y*y)
end
}
end
-- Metatable: shared methods (less memory)
-- local Point = {}; Point.__index = Point
-- function Point.new(x,y) return setmetatable({x=x,y=y},Point) end변경 가능한 클로저
변경 가능한 변수에 대한 클로저를 사용하면 테이블 없이도 상태를 가진 객체를 만들 수 있습니다. 하나의 클로저가 상태를 보유하고 결과를 계산할 수 있습니다.
local function makeAccumulator()
local sum, count = 0, 0
return {
add = function(n) sum=sum+n; count=count+1 end,
mean = function()
return count>0 and sum/count or 0
end,
total = function() return sum end,
n = function() return count end,
}
end
local acc = makeAccumulator()
for _, v in ipairs({10,20,30,40,50}) do
acc.add(v)
end
print(acc.total(),acc.mean(),acc.n()) -- 150 30.0 5검증을 포함한 생성자
클로저 기반 생성자는 메서드가 아니라 팩토리 함수에서 입력값을 검증합니다. 잘못된 입력이 들어오면 객체가 생성되기 전에 팩토리가 nil 또는 오류를 반환합니다.
local function makeRange(lo, hi)
assert(lo<=hi,"lo must be <= hi")
return {
contains = function(v) return v>=lo and v<=hi end,
clamp = function(v) return math.max(lo,math.min(hi,v)) end,
lo = function() return lo end,
hi = function() return hi end,
span = function() return hi-lo end,
}
end
local r = makeRange(0,100)
print(r.contains(50)) -- true
print(r.clamp(150)) -- 100클로저를 사용한 이벤트
클로저로 구현한 이벤트 발생기에서는 처리기 목록이 비공개이고, add/remove/emit이 공개 API입니다.
local function makeEmitter()
local handlers = {}
return {
on = function(event, fn)
handlers[event] = handlers[event] or {}
handlers[event][#handlers[event]+1] = fn
end,
emit = function(event, ...)
for _, fn in ipairs(handlers[event] or {}) do
fn(...)
end
end,
off = function(event) handlers[event] = {} end,
}
end
local emitter = makeEmitter()
emitter.on("data", function(v) print("Got:", v) end)
emitter.emit("data", 42) -- Got: 42클로저 메모이제이션
클로저를 사용하는 메모이제이션 래퍼에서는 캐시와 래핑된 함수가 비공개이며, 메모이제이션된 버전만 반환됩니다.
local function memoize(fn)
local cache = {}
return function(...)
local key = table.concat({...}, ",")
if cache[key] == nil then
cache[key] = fn(...)
end
return cache[key]
end
end
local expensiveFib
expensiveFib = memoize(function(n)
if n<=1 then return n end
return expensiveFib(n-1)+expensiveFib(n-2)
end)
for i=0,10 do io.write(expensiveFib(i).." ") end
print()클로저 객체 확장하기
기존 객체의 메서드를 호출하는 새 객체를 만들어 클로저 기반 객체를 확장할 수 있습니다. 이는 상속이 아니라 래핑과 유사하며, 기존 객체에 작업을 위임합니다.
local function makeTimedCounter(start)
local base = makeCounter and makeCounter(start) or (function()
local c=start or 0
return {inc=function()c=c+1 end,get=function()return c end}
end)()
local callCount = 0
return {
increment = function()
callCount=callCount+1
base.inc()
end,
get = base.get,
calls = function() return callCount end,
}
end클로저를 사용할 때
다음과 같은 경우 클로저 기반 객체 지향 프로그래밍을 사용하세요. 반드시 안전하게 보호해야 하는 진정한 비공개 상태가 필요할 때, 인스턴스가 적어 메모리가 문제가 되지 않을 때, 함수형 스타일의 코드를 작성할 때입니다. 다음과 같은 경우 메타테이블을 사용하세요. 상속이 필요할 때, 인스턴스가 많을 때, 객체 지향 프로그래밍 라이브러리와 통합하려 할 때입니다.
-- Guideline summary:
-- Closures: true privacy, no metatable, self-contained
-- Metatables: shared methods, inheritance, instanceof checks
-- Rule of thumb:
-- < 100 instances with private state -> closures
-- > 100 instances OR need inheritance -> metatables
print("Both patterns are valid Lua OOP.")빠른 확인
메타테이블 기반 객체에 비해 클로저 기반 객체가 갖는 가장 큰 장점은 무엇인가요?
복습: 캡슐화를 위한 클로저
요약:
- 클로저 패턴: 팩토리 함수가 메서드 테이블을 반환하고, 상태는 상위 값입니다
- 진정한 비공개 상태이므로 외부에서 상위 값에 접근할 방법이 없습니다
- 반환된 모든 메서드는 동일한 비공개 상위 값을 공유합니다
- 장단점: 인스턴스당 메모리는 더 많이 사용하지만 개인정보 보호 수준은 높습니다
- 보안이 중요한 코드나 함수형 스타일의 설계에 사용합니다
자주 묻는 질문
“클로저를 이용한 캡슐화” 강의는 무료인가요?
네 — “클로저를 이용한 캡슐화” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Lua Academy 강의 전체를 잠금 해제할 수 있습니다. Lua Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“클로저를 이용한 캡슐화”에서 뭘 배우나요?
클로저 기반 객체 패턴으로 비공개 상태를 숨깁니다. 브라우저에서 직접 실행하는 실습 코드로 Lua Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Lua Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Lua Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“클로저를 이용한 캡슐화” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Lua Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Lua Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 메타테이블을 이용한 클래스
- 생성자와 new()
- 인스턴스 메서드와 self
- 클로저를 이용한 캡슐화