프라이버시를 위한 클로저
Coddy Lua 여정의 객체 지향 프로그래밍 (Object Oriented Programming) 섹션에 포함된 레슨 — 70개 중 44번째.
명명 규칙은 의도를 나타내지만, 실제로 접근을 막지는 않습니다. 누구나 여전히 player._health = 999라고 작성할 수 있으며 이는 정상적으로 작동합니다. 진정한 프라이버시를 위해, Lua는 다른 접근 방식을 제공합니다: self 대신 생성자 내부의 지역 변수에 데이터를 저장하는 것입니다.
핵심적인 통찰은 함수 내부의 지역 변수는 해당 함수 및 그 내부에서 정의된 함수 내에서만 접근 가능하다는 점입니다. 이것을 클로저(closure)라고 합니다:
local Counter = {}
Counter.__index = Counter
function Counter:new()
local count = 0 -- 진정한 프라이빗 변수! self에 속하지 않습니다.
local obj = {}
setmetatable(obj, Counter)
return obj
end이 예제에서 count는 :new() 함수 내부에서만 존재합니다. 이것은 obj에 저장되지 않으므로 외부에서 접근할 방법이 없습니다. myCounter.count는 nil을 반환하며, 우회할 수 있는 밑줄 접두사가 붙은 필드도 없습니다.
변수 count는 클로저의 스코프 내에 존재합니다. 외부 코드로부터 완전히 숨겨진 상태로 유지됩니다. (작성된 대로라면, 아직은 아무것도 count를 캡처하지 않습니다. 다음 레슨에서는 생성자 내부에 이를 클로저로 감싸는 메서드를 추가하여, 객체가 존재하는 동안 유지되도록 할 것입니다.) 단지 정중한 제안일 뿐인 self._count와 달리, 이 방식은 데이터를 진정으로 접근 불가능하게 만듭니다.
물론, 숨겨진 데이터와 전혀 상호작용할 수 없다면 유용하지 않을 것입니다. 다음 레슨에서는 이러한 프라이빗 변수를 읽고 수정할 수 있는 메서드를 생성자(constructor) 내부에 정의하는 방법을 배우게 됩니다. 이를 통해 진정한 캡슐화를 유지하면서도 제어된 접근 권한을 부여할 수 있습니다.
챌린지
쉬움클로저를 사용하여 진정한 데이터 프라이버시를 보여주는 SecretKeeper 클래스를 만들어 봅시다! 이전 레슨의 언더스코어 컨벤션과 달리, 이 방식은 객체 외부에서 데이터에 절대 접근할 수 없게 만듭니다.
코드는 두 개의 파일로 구성됩니다:
SecretKeeper.lua: 생성자 내부의 로컬 변수에 비밀 값을 저장하는 클래스를 만드세요.self에 저장해서는 안 됩니다. 생성자:new(secretValue)는 클로저 내부에만 존재하는 로컬 변수에 비밀 값을 저장해야 합니다. 현재로서는 객체가 이 비밀 값에 접근할 방법이 없지만(다음 레슨에서 다룹니다!), 객체가 작동하는지 확인하기 위해 인스턴스에"Keeper"로 설정된 공개name필드를 저장해야 합니다.main.lua: SecretKeeper 모듈을 불러오고(require) 입력에서 비밀 값을 읽습니다. 해당 비밀 값으로 SecretKeeper 인스턴스를 생성합니다. 그런 다음 세 가지를 출력하여 비밀 값이 진정으로 프라이빗임을 증명하세요:- keeper의
name필드 (Keeper가 출력되어야 함) keeper.secret에 접근하려고 할 때의 결과 (nil이어야 함)keeper._secret에 접근하려고 할 때의 결과 (마찬가지로nil이어야 함)
- keeper의
하나의 입력을 받게 됩니다:
- 저장할 비밀 값 (예:
TopSecret123)
출력은 공개 필드는 작동하지만 비밀 값은 완전히 숨겨져 있음을 보여주는 세 줄이어야 합니다:
Name: Keeper
secret: nil
_secret: nil어떤 비밀 값을 전달하더라도 출력은 동일합니다. 접근할 방법이 없기 때문입니다! 생성자 내부의 로컬 변수는 외부 세계에서 보이지 않습니다. 이는 클로저 기반 프라이버시가 언더스코어 명명 규칙과 근본적으로 다르다는 것을 증명합니다.
예를 들어, 입력이 MyPassword인 경우에도 출력은 다음과 같아야 합니다:
Name: Keeper
secret: nil
_secret: nil직접 해보기
-- SecretKeeper 모듈을 불러옵니다
local SecretKeeper = require('SecretKeeper')
-- 입력으로부터 비밀 값을 읽습니다
local secretValue = io.read()
-- TODO: 비밀 값을 사용하여 SecretKeeper 인스턴스를 생성하세요
-- TODO: keeper의 name 필드를 출력하세요 (형식: "Name: <name>")
-- TODO: keeper.secret에 접근할 때 어떤 일이 일어나는지 출력하세요 (형식: "secret: <value>")
-- TODO: keeper._secret에 접근할 때 어떤 일이 일어나는지 출력하세요 (형식: "_secret: <value>")
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.