PostgreSQL์์ ํด์ ์ฐ์๋ก ๋ณ์กฐ ๊ฐ์ง ๊ฐ๋ฅํ ๊ฐ์ฌ ์ถ์ ๋ง๋ค๊ธฐ
PostgreSQL์์ ์ถ๊ฐ ์ ์ฉ ํ ์ด๋ธ๊ณผ ํด์ ์ฐ์๋ฅผ ์ฌ์ฉํด ๋ฆฌ๋ทฐ๋ ์กฐ์ฌ ์ค ํธ์ง์ด ์ฝ๊ฒ ๊ฐ์ง๋๋๋ก ํ๋ ๋ณ์กฐ ๊ฐ์ง ๊ฐ์ฌ ๋ก๊ทธ๋ฅผ ์์๋ณด์ธ์.

์ผ๋ฐ ๊ฐ์ฌ ๋ก๊ทธ๋ ์ ์ฝ๊ฒ ๋ฌธ์ ์๋๋๊ฐ
๊ฐ์ฌ ์ถ์ (audit trail)์ ์ด์ํ ํ๋ถ, ์๋ฌด๋ ๊ธฐ์ตํ์ง ๋ชปํ๋ ๊ถํ ๋ณ๊ฒฝ, ๋๋ ๊ณ ๊ฐ ๋ ์ฝ๋๊ฐ โ์ฌ๋ผ์งโ ๊ฒฝ์ฐ์ฒ๋ผ ๋ญ๊ฐ ์๋ชป๋์ ๋ ๋๋์๋ณด๋ ๊ธฐ๋ก์ ๋๋ค. ๊ฐ์ฌ ๊ธฐ๋ก์ ํธ์งํ ์ ์๋ค๋ฉด ๊ทธ๊ฒ์ ์ฆ๊ฑฐ๊ฐ ์๋๋ผ ๋๊ตฐ๊ฐ๊ฐ ๋ค์ ์ธ ์ ์๋ ๋ ๋ค๋ฅธ ๋ฐ์ดํฐ๊ฐ ๋ฉ๋๋ค.
๋ง์ โ๊ฐ์ฌ ๋ก๊ทธโ๋ ๋จ์ง ์ผ๋ฐ ํ ์ด๋ธ์ผ ๋ฟ์ ๋๋ค. ํ์ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ์ญ์ ํ ์ ์๋ค๋ฉด ๊ทธ ์ฌ๊ฑด์ ๊ธฐ๋ก๋ ์ ๋ฐ์ดํธ๋๊ฑฐ๋ ์ญ์ ๋ ์ ์์ต๋๋ค.
์ค์ํ ์ฐจ์ด: ํธ์ง์ ๋ง๋ ๊ฒ๊ณผ ํธ์ง์ ๊ฐ์ง ๊ฐ๋ฅํ๊ฒ ๋ง๋๋ ๊ฒ์ ๋ค๋ฆ ๋๋ค. ๊ถํ์ผ๋ก ๋ณ๊ฒฝ์ ์ค์ผ ์๋ ์์ง๋ง ์ถฉ๋ถํ ์ ๊ทผ ๊ถํ์ ๊ฐ์ง ์ฌ๋(๋๋ ๋๋๋นํ ๊ด๋ฆฌ์ ์๊ฒฉ์ฆ๋ช )์ด ์ญ์ฌ๋ฅผ ๋ฐ๊ฟ ์ ์์ต๋๋ค. ๋ณ์กฐ ๊ฐ์ง๋ ๊ทธ ํ์ค์ ๋ฐ์๋ค์ด๊ณ , ๋ชจ๋ ๋ณ๊ฒฝ์ ๋ง์ง ๋ชปํ๋๋ผ๋ ๋ณ๊ฒฝ์ด ๋ถ๋ช ํ ์ง๋ฌธ์ ๋จ๊ธฐ๊ฒ ํฉ๋๋ค.
์ผ๋ฐ ๊ฐ์ฌ ๋ก๊ทธ๊ฐ ๋ ผ์๊ฑฐ๋ฆฌ๊ฐ ๋๋ ์ด์ ๋ ์์ธก ๊ฐ๋ฅํฉ๋๋ค. ๊ถํ์ด ์๋ ์ฌ์ฉ์๊ฐ ์ฌํ์ ๋ก๊ทธ๋ฅผ โ์์ โํ ์ ์์ต๋๋ค. ์นจํด๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณ์ ์ด ์ ์ ํธ๋ํฝ์ฒ๋ผ ๋ณด์ด๋ ๋ฏฟ์ ๋งํ ํญ๋ชฉ์ ์ธ ์ ์์ต๋๋ค. ํ์์คํฌํ๋ฅผ ๋ค์ฑ์ ๋ฆ์ ๋ณ๊ฒฝ์ ์จ๊ธธ ์ ์๊ณ , ๋๊ตฐ๊ฐ๋ ๊ฐ์ฅ ์ํด๊ฐ ํฐ ํ๋ง ์ญ์ ํ ์๋ ์์ต๋๋ค.
"๋ณ์กฐ ๊ฐ์ง(tamper-evident)"๋ ์์ ์์ (ํ๋ ํ๋ ๋ณ๊ฒฝ, ํ ํ๋ ์ ๊ฑฐ, ์ด๋ฒคํธ ์ฌ์ ๋ ฌ ๋ฑ)๋ ๋์ค์ ํ์ง๋๋๋ก ๊ฐ์ฌ ์ถ์ ์ ์ค๊ณํ๋ค๋ ๋ป์ ๋๋ค. ๋ง๋ฒ์ ์ฝ์ํ๋ ๊ฒ์ด ์๋๋ผ, ๋๊ตฐ๊ฐ โ์ด ๋ก๊ทธ๊ฐ ์ง์ง์ธ์ง ์ด๋ป๊ฒ ์์ฃ ?โ๋ผ๊ณ ๋ฌผ์ ๋ ๋ก๊ทธ๊ฐ ๊ฑด๋๋ ค์ก๋์ง ๋ณด์ฌ์ค ์ ์๋ ๊ฒ์ฌ๋ฅผ ์คํํ ์ ์์์ ์ฝ์ํ๋ ๊ฒ์ ๋๋ค.
๋ฌด์์ ์ฆ๋ช ํด์ผ ํ ์ง ๊ฒฐ์ ํ๊ธฐ
๋ณ์กฐ ๊ฐ์ง ๊ฐ์ฌ ์ถ์ ์ ๋์ค์ ๋ง์ฃผํ ์ง๋ฌธ์ ๋ตํ ์ ์์ ๋๋ง ์ ์ฉํฉ๋๋ค: ๋๊ฐ ๋ฌด์์ ํ๋๊ฐ, ์ธ์ ํ๋๊ฐ, ๋ฌด์์ด ๋ณ๊ฒฝ๋์๋๊ฐ.
๋น์ฆ๋์ค์ ์ค์ํ ์ด๋ฒคํธ๋ถํฐ ์์ํ์ธ์. ๋ฐ์ดํฐ ๋ณ๊ฒฝ(์์ฑ, ์ ๋ฐ์ดํธ, ์ญ์ )์ด ๊ธฐ๋ณธ์ด์ง๋ง, ์กฐ์ฌ๋ ๋ณด์๊ณผ ์ ๊ทผ ๊ด๋ จ ์ด๋ฒคํธ(๋ก๊ทธ์ธ, ๋น๋ฐ๋ฒํธ ์ฌ์ค์ , ๊ถํ ๋ณ๊ฒฝ, ๊ณ์ ์ ๊ธ)๋ก ํ๊ฐ๋ฆ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ๊ฒฐ์ ๋ฅผ ๋ค๋ฃฌ๋ค๋ฉด ํ๋ถ, ํฌ๋ ๋ง, ์ง๊ธ ๊ฐ์ ๊ธ์ ์ด๋์ ๋จ์ํ ํ ์ ๋ฐ์ดํธ์ ๋ถ์ ํจ๊ณผ๋ก ๋ค๋ฃจ์ง ๋ง๊ณ ์ผ๊ธ ์ด๋ฒคํธ๋ก ์ทจ๊ธํ์ธ์.
๋ค์์ผ๋ก ์ด๋ค ๊ฒ์ด ์ด๋ฒคํธ๋ฅผ ์ ๋ขฐํ ์ ์๊ฒ ๋ง๋๋์ง ๊ฒฐ์ ํ์ธ์. ๊ฐ์ฌ์๋ ๋ณดํต ํ์์(์ ์ ๋๋ ์๋น์ค), ์๋ฒ ์ธก ํ์์คํฌํ, ์ํ๋ ๋์, ์ํฅ์ ๋ฐ์ ๊ฐ์ฒด๋ฅผ ๊ธฐ๋ํฉ๋๋ค. ์ ๋ฐ์ดํธ์ ๊ฒฝ์ฐ์๋ ๋ณ๊ฒฝ ์ /ํ ๊ฐ(๋๋ ์ ์ด๋ ๋ฏผ๊ฐํ ํ๋)์ ์ ์ฅํ๊ณ , ์ฌ๋ฌ ์์ DB ๋ณ๊ฒฝ์ ํ๋์ ์ฌ์ฉ์ ํ๋์ ์ฐ๊ฒฐํ ์ ์๋๋ก ์์ฒญ id ๋๋ ์๊ด id(request/correlation id)๋ฅผ ํจ๊ป ์ ์ฅํ์ธ์.
๋ง์ง๋ง์ผ๋ก ์์คํ ์์ "๋ถ๋ณ(immutable)"์ด ๋ฌด์์ ์๋ฏธํ๋์ง ๋ช ํํ ํ์ธ์. ๊ฐ์ฅ ๊ฐ๋จํ ๊ท์น์: ๊ฐ์ฌ ํ์ ์ ๋ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ์ญ์ ํ์ง ๋ง๊ณ , ์ค์ง INSERT๋ง ํ๋ผ๋ ๊ฒ์ ๋๋ค. ์๋ชป๋ ์ ์ด ์์ผ๋ฉด ๊ธฐ์กด์ ๋ฎ์ด์ฐ์ง ๋ง๊ณ ์ด๋ฅผ ๋ฐ๋ก์ก๋ ์ ์ด๋ฒคํธ๋ฅผ ์์ฑํ๊ณ ์๋ณธ์ ๊ทธ๋๋ก ๋จ๊ฒจ๋์ธ์.
์ถ๊ฐ ์ ์ฉ(append-only) ๊ฐ์ฌ ํ ์ด๋ธ ๋ง๋ค๊ธฐ
๊ฐ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ผ๋ฐ ํ
์ด๋ธ๊ณผ ๋ถ๋ฆฌํ์ธ์. ์ ์ฉ audit ์คํค๋ง๋ ์ค์๋ก ์์ ํ๋ ์ผ์ ์ค์ด๊ณ ๊ถํ ๊ด๋ฆฌ๋ฅผ ๋ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
๋ชฉํ๋ ๊ฐ๋จํฉ๋๋ค: ํ์ ์ถ๊ฐํ ์ ์์ง๋ง ๋ณ๊ฒฝ๋๊ฑฐ๋ ์ ๊ฑฐ๋๋ฉด ์ ๋ฉ๋๋ค. PostgreSQL์์๋ ๊ถํ(privileges)๊ณผ ํ ์ด๋ธ ์ค๊ณ์ ๋ช ๊ฐ์ง ์์ ์ฅ์น๋ก ์ด๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค.
๋ค์์ ์ค์ฉ์ ์ธ ์์ ํ ์ด๋ธ ์์์ ๋๋ค:
CREATE SCHEMA IF NOT EXISTS audit;
CREATE TABLE audit.events (
id bigserial PRIMARY KEY,
entity_type text NOT NULL,
entity_id text NOT NULL,
event_type text NOT NULL CHECK (event_type IN ('INSERT','UPDATE','DELETE')),
actor_id text,
occurred_at timestamptz NOT NULL DEFAULT now(),
request_id text,
before_data jsonb,
after_data jsonb,
notes text
);
์กฐ์ฌ ์ ํนํ ์ ์ฉํ ๋ช๋ช ํ๋:
occurred_at์DEFAULT now()๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์๊ฐ์ ์ฐ๊ฒ ํ์ฌ ํด๋ผ์ด์ธํธ๊ฐ ์๋ ์๋ฒ๊ฐ ์๊ฐ์ ๊ธฐ๋กํ๋๋ก ํฉ๋๋ค.entity_type๊ณผentity_id๋ ํ๋์ ๋ ์ฝ๋๋ฅผ ๋ณ๊ฒฝ ์ด๋ ฅ ์ ์ฒด์์ ์ถ์ ํ ์ ์๊ฒ ํฉ๋๋ค.request_id๋ ํ๋์ ์ฌ์ฉ์ ํ๋์ด ์ฌ๋ฌ ํ์ผ๋ก ๋๋ ๊ฒฝ์ฐ ์ด๋ฅผ ์ถ์ ํ๋ ๋ฐ ๋์๋ฉ๋๋ค.
์ญํ (role)๋ก ์ ๊ทธ์ธ์. ์ ํ๋ฆฌ์ผ์ด์
์ญํ ์ audit.events์ ๋ํด INSERT์ SELECT๋ ํ ์ ์์ง๋ง UPDATE๋ DELETE๋ ๋ชปํ๊ฒ ํ์ธ์. ์คํค๋ง ๋ณ๊ฒฝ๊ณผ ๋ ๊ฐ๋ ฅํ ๊ถํ์ ์ฑ์์ ์ฌ์ฉํ์ง ์๋ ๊ด๋ฆฌ์ ์ญํ ์๋ง ๋์ธ์.
ํธ๋ฆฌ๊ฑฐ๋ก ๋ณ๊ฒฝ ์บก์ฒํ๊ธฐ(๊ฐ๋จํ๊ณ ์์ธก ๊ฐ๋ฅํ๊ฒ)
๋ณ์กฐ ๊ฐ์ง ๊ฐ์ฌ ์ถ์ ์ ์ํ๋ค๋ฉด ๋ณ๊ฒฝ์ ์บก์ฒํ๊ธฐ์ ๊ฐ์ฅ ์ ๋ขฐํ ์ ์๋ ์ฅ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ๋ก๊ทธ๋ ๊ฑด๋๋ฐ๊ฑฐ๋ ํํฐ๋ง๋๊ฑฐ๋ ์ฌ์์ฑ๋ ์ ์์ต๋๋ค. ํธ๋ฆฌ๊ฑฐ๋ ์ด๋ค ์ฑ, ์คํฌ๋ฆฝํธ, ๊ด๋ฆฌ์ ๋๊ตฌ๊ฐ ํ ์ด๋ธ์ ๊ฑด๋๋ ค๋ ํญ์ ์คํ๋ฉ๋๋ค.
ํธ๋ฆฌ๊ฑฐ๋ ์ง๋ฃจํ๊ฒ ์ ์งํ์ธ์. ํธ๋ฆฌ๊ฑฐ์ ์๋ฌด๋ ํ๋๋ฟ์ ๋๋ค: ์ค์ํ ํ ์ด๋ธ์์ INSERT, UPDATE, DELETE๊ฐ ๋ฐ์ํ ๋๋ง๋ค ๊ฐ์ฌ ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ.
์ค์ฉ์ ์ธ ๊ฐ์ฌ ๋ ์ฝ๋๋ ๋ณดํต ํ ์ด๋ธ ์ด๋ฆ, ์์ ์ ํ, ๊ธฐ๋ณธ ํค, ๋ณ๊ฒฝ ์ ํ ๊ฐ, ํ์์คํฌํ, ๊ทธ๋ฆฌ๊ณ ๊ด๋ จ ๋ณ๊ฒฝ์ ๊ทธ๋ฃนํํ ์ ์๋ ์๋ณ์(ํธ๋์ญ์ id์ ์๊ด id)๋ฅผ ํฌํจํฉ๋๋ค.
์๊ด id๋ "20๊ฐ ํ์ด ์
๋ฐ์ดํธ๋์๋ค"์ "์ด๊ฑด ๋ฒํผ ํด๋ฆญ ํ๋์๋ค"์ ์ฐจ์ด๋ฅผ ๋ง๋ญ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์
์ ์์ฒญ ๋น ํ ๋ฒ ์๊ด id๋ฅผ ์ค์ ํ ์ ์๊ณ (์: DB ์ธ์
์ค์ ), ํธ๋ฆฌ๊ฑฐ๋ ์ด๋ฅผ ์ฝ์ด ์ ์ฅํ ์ ์์ต๋๋ค. ์๊ด id๊ฐ ์์ ๋๋ ๊ทธ๋ฃนํํ ์ ์๋๋ก txid_current()๋ฅผ ์ ์ฅํ์ธ์.
๋ค์์ ๊ฐ์ฌ ํ ์ด๋ธ์๋ง INSERTํ๊ธฐ ๋๋ฌธ์ ์์ธก ๊ฐ๋ฅ์ฑ์ ์ ์งํ๋ ๊ฐ๋จํ ํธ๋ฆฌ๊ฑฐ ํจํด์ ๋๋ค(์คํค๋ง ์ด๋ฆ์ ๋ง๊ฒ ์กฐ์ ํ์ธ์):
CREATE OR REPLACE FUNCTION audit_row_change() RETURNS trigger AS $$
DECLARE
corr_id text;
BEGIN
corr_id := current_setting('app.correlation_id', true);
INSERT INTO audit_events(
occurred_at, table_name, op, row_pk,
old_row, new_row, db_user, txid, correlation_id
) VALUES (
now(), TG_TABLE_NAME, TG_OP, COALESCE(NEW.id, OLD.id),
to_jsonb(OLD), to_jsonb(NEW), current_user, txid_current(), corr_id
);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;
ํธ๋ฆฌ๊ฑฐ์ ๋ง์ ์ผ์ ํ๋ ค๋ ์ ํน์ ์ฐธ์ผ์ธ์. ์ถ๊ฐ ์ฟผ๋ฆฌ, ๋คํธ์ํฌ ํธ์ถ, ๋ณต์กํ ๋ถ๊ธฐ๋ฌธ์ ํผํ์ธ์. ์์ ํธ๋ฆฌ๊ฑฐ๋ ํ ์คํธํ๊ธฐ ์ฝ๊ณ ์คํ ์๋๊ฐ ๋น ๋ฅด๋ฉฐ ๊ฒํ ์ ๋ ผ์์ ์ฌ์ง๊ฐ ์ ์ต๋๋ค.
ํธ์ง์ด ํ์ ์ ๋จ๊ธฐ๋๋ก ํด์ ์ฐ์ ์ถ๊ฐํ๊ธฐ
์ถ๊ฐ ์ ์ฉ ํ ์ด๋ธ์ ๋์์ด ๋์ง๋ง ์ถฉ๋ถํ ๊ถํ์ ๊ฐ์ง ๋๊ตฐ๊ฐ๋ ์ฌ์ ํ ๊ณผ๊ฑฐ ํ์ ๋ค์ ์ธ ์ ์์ต๋๋ค. ํด์ ์ฐ์๋ ๊ทธ๋ฐ ์์ ์ ๋์ ๋๊ฒ ๋ง๋ญ๋๋ค.
๊ฐ ๊ฐ์ฌ ํ์ ๋ ๊ฐ์ ์ปฌ๋ผ์ ์ถ๊ฐํ์ธ์: prev_hash์ row_hash(๋๋๋ก chain_hash๋ผ๊ณ ๋ ํจ). prev_hash๋ ๋์ผ ์ฒด์ธ ๋ด ์ด์ ํ์ ํด์๋ฅผ ์ ์ฅํ๊ณ , row_hash๋ ํ์ฌ ํ์ ๋ฐ์ดํฐ์ prev_hash๋ฅผ ํฌํจํด ๊ณ์ฐํ ํ์ฌ ํ์ ํด์๋ฅผ ์ ์ฅํฉ๋๋ค.
๋ฌด์์ ํด์ฑํ๋๋๊ฐ ์ค์ํฉ๋๋ค. ๋์ผํ ํ์ด ํญ์ ๊ฐ์ ํด์๋ฅผ ๋ง๋ค์ด๋ด๋๋ก ์์ ์ ์ด๊ณ ๋ฐ๋ณต ๊ฐ๋ฅํ ์ ๋ ฅ์ ์ํฉ๋๋ค.
์ค์ฉ์ ์ธ ์ ๊ทผ์ ๊ณ ์ ๋ ์ปฌ๋ผ(ํ์์คํฌํ, ํ์์, ๋์, ์ํฐํฐ id)์ผ๋ก ๊ตฌ์ฑ๋ ํ์ค ๋ฌธ์์ด๊ณผ ์ผ๊ด๋ ์ง๋ ฌํ(์ข
์ข
jsonb) ๋ฐ prev_hash๋ฅผ ํด์ ์
๋ ฅ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
๊ณต๋ฐฑ, JSON ํค ์์, ๋ก์ผ์ผ๋ณ ํฌ๋งท์ฒ๋ผ ์๋ฏธ ์์ด ๋ฐ๋ ์ ์๋ ์ธ๋ถ์ฌํญ์ ์ฃผ์ํ์ธ์. ํ์ ์ ์ผ๊ด๋๊ฒ ์ ์งํ๊ณ ์์ธก ๊ฐ๋ฅํ ๋ฐฉ์์ผ๋ก ์ง๋ ฌํํ์ธ์.
์ ์ฒด DB๊ฐ ์๋ ์คํธ๋ฆผ๋ณ ์ฒด์ธ
๋ชจ๋ ๊ฐ์ฌ ์ด๋ฒคํธ๋ฅผ ํ๋์ ๊ธ๋ก๋ฒ ์ํ์ค๋ก ์ฒด์ธํ๋ฉด ์ฐ๊ธฐ ๋ณ๋ชฉ์ด ๋ ์ ์์ต๋๋ค. ๋ง์ ์์คํ ์ ํ ๋ํธ๋ณ, ์ํฐํฐ ํ์ ๋ณ, ๋๋ ๋น์ฆ๋์ค ๊ฐ์ฒด๋ณ ๊ฐ์ "์คํธ๋ฆผ" ๋ด์์ ์ฒด์ธํฉ๋๋ค.
๊ฐ ์๋ก์ด ํ์ ์์ ์ ์คํธ๋ฆผ์ ๋ํ ์ต์ row_hash๋ฅผ ์กฐํํด prev_hash๋ก ์ ์ฅํ ๋ค์ ์์ ์ row_hash๋ฅผ ๊ณ์ฐํฉ๋๋ค.
-- Requires pgcrypto
-- digest() returns bytea; store hashes as bytea
row_hash = digest(
concat_ws('|',
stream_key,
occurred_at::text,
actor_id::text,
action,
entity,
entity_id::text,
payload::jsonb::text,
encode(prev_hash, 'hex')
),
'sha256'
);
์ฒด์ธ ํค๋๋ฅผ ์ค๋ ์ท์ผ๋ก ์ ์ฅํ๊ธฐ
๊ฒํ ์๋๋ฅผ ๋์ด๋ ค๋ฉด ์คํธ๋ฆผ๋ณ๋ก ์ต์ row_hash(โ์ฒด์ธ ํค๋โ)๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ์์ ์ค๋
์ท ํ
์ด๋ธ์ ์ ์ฅํ์ธ์(์: ํ๋ฃจ ๋จ์). ์กฐ์ฌ ์ ์ ์ฒด ์ด๋ ฅ์ ํ ๋ฒ์ ์ค์บํ์ง ์๊ณ ๋ ๊ฐ ์ค๋
์ท๊น์ง ์ฒด์ธ์ ๊ฒ์ฆํ ์ ์์ต๋๋ค. ์ค๋
์ท์ ๋ด๋ณด๋ธ ๊ฒ๋ค์ ๋น๊ตํด ์์ฌ์ค๋ฌ์ด ๊ฐ๊ฒฉ์ ์ฐพ๊ธฐ๋ ์ฝ์ต๋๋ค.
๋์์ฑ ๋ฐ ์์ ๋ฌธ์ ์ฒ๋ฆฌ
์ค์ ํธ๋ํฝ์์๋ ํด์ ์ฐ์๊ฐ ๊น๋ค๋ก์์ง๋๋ค. ๋ ํธ๋์ญ์
์ด ๋์์ ๊ฐ์ฌ ํ์ ์ฐ๊ณ ๋ ๋ค ๊ฐ์ prev_hash๋ฅผ ์ฌ์ฉํ๋ฉด ํฌํฌ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ ๋จ์ผํ ๊นจ๋ํ ์ํ์ค๋ฅผ ์ฆ๋ช
ํ๋ ๋ฅ๋ ฅ์ ์ฝํ์ํต๋๋ค.
๋จผ์ ์ฒด์ธ์ด ๋ฌด์์ ๋ํ๋ด๋์ง ๊ฒฐ์ ํ์ธ์. ํ๋์ ๊ธ๋ก๋ฒ ์ฒด์ธ์ ์ค๋ช ํ๊ธฐ ์ฝ์ง๋ง ๊ฒฝ์์ด ๊ฐ์ฅ ์ฌํฉ๋๋ค. ์ฌ๋ฌ ์ฒด์ธ์ ๊ฒฝ์์ ์ค์ด์ง๋ง ๊ฐ ์ฒด์ธ์ด ๋ฌด์์ ์ฆ๋ช ํ๋์ง ๋ช ํํ ํด์ผ ํฉ๋๋ค.
์ด๋ค ๋ชจ๋ธ์ ์ ํํ๋ ๋จ์กฐ ์ฆ๊ฐํ๋ ์ด๋ฒคํธ id(๋ณดํต ์ํ์ค๋ก ์ง์๋๋ id)๋ก ์๊ฒฉํ ์์๋ฅผ ์ ์ํ์ธ์. ํ์์คํฌํ๋ ์ถฉ๋ํ ์ ์๊ณ ์กฐ์๋ ์ ์๊ธฐ ๋๋ฌธ์ ์ถฉ๋ถํ์ง ์์ต๋๋ค.
prev_hash๋ฅผ ๊ณ์ฐํ ๋์ ๋ ์ด์ค ์ปจ๋์
์ ํผํ๋ ค๋ฉด ๊ฐ ์คํธ๋ฆผ์ ๋ํด "๋ง์ง๋ง ํด์ ๊ฐ์ ธ์ค๊ธฐ + ๋ค์ ํ ์ฝ์
"์ ์ง๋ ฌํํ์ธ์. ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ์คํธ๋ฆผ ํค๋๋ฅผ ๋ํ๋ด๋ ๋จ์ผ ํ์ ์ ๊ทธ๊ฑฐ๋ ์คํธ๋ฆผ id๋ก ์ด๋๋ฐ์ด์ ๋ฆฌ ๋ฝ(advisory lock)์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค. ๋ชฉํ๋ ๊ฐ์ ์คํธ๋ฆผ์ ๋ ์์ฑ์๊ฐ ๋์์ ๊ฐ์ ๋ง์ง๋ง ํด์๋ฅผ ์ฝ์ง ๋ชปํ๊ฒ ํ๋ ๊ฒ์
๋๋ค.
ํํฐ์ ๋๊ณผ ์ค๋ฉ์ "๋ง์ง๋ง ํ"์ด ์ด๋์ ์๋์ง์ ์ํฅ์ ์ค๋๋ค. ๊ฐ์ฌ ๋ฐ์ดํฐ๋ฅผ ํํฐ์ ๋ํ ๊ฒ์ผ๋ก ์์ํ๋ฉด ์คํธ๋ฆผ ํค์ ๋์ผํ ํํฐ์ ํค๋ฅผ ์ฌ์ฉํด ๊ฐ ์ฒด์ธ์ด ํ๋์ ํํฐ์ ์์ ์์ ํ ํฌํจ๋๋๋ก ํ์ธ์(์: ํ ๋ํธ id). ์ด๋ ๊ฒ ํ๋ฉด ํ ๋ํธ ์ฒด์ธ์ ๋์ค์ ์๋ฒ ๊ฐ ์ด๋ํ๋๋ผ๋ ๊ฒ์ฆ ๊ฐ๋ฅํ๊ฒ ์ ์ง๋ฉ๋๋ค.
์กฐ์ฌ ์ค ์ฒด์ธ์ ๊ฒ์ฆํ๋ ๋ฐฉ๋ฒ
ํด์ ์ฐ์๋ ๋๊ตฐ๊ฐ ๋ฌผ์์ ๋ ์ฒด์ธ์ด ์ฌ์ ํ ์ ํจํจ์ ์ฆ๋ช ํ ์ ์์ด์ผ๋ง ๋์์ด ๋ฉ๋๋ค. ๊ฐ์ฅ ์์ ํ ๋ฐฉ๋ฒ์ ์ฝ๊ธฐ ์ ์ฉ ๊ฒ์ฆ ์ฟผ๋ฆฌ(๋๋ ์์ )๋ฅผ ์คํํด ์ ์ฅ๋ ๋ฐ์ดํฐ๋ก๋ถํฐ ๊ฐ ํ์ ํด์๋ฅผ ์ฌ๊ณ์ฐํ๊ณ ๊ธฐ๋ก๋ ๊ฐ๊ณผ ๋น๊ตํ๋ ๊ฒ์ ๋๋ค.
on-demand๋ก ์คํํ ์ ์๋ ๊ฐ๋จํ ๊ฒ์ฆ๊ธฐ
๊ฒ์ฆ๊ธฐ๋ ๊ฐ ํ์ ๋ํด ์์ ํด์๋ฅผ ์ฌ๋น๋ํ๊ณ , ๊ฐ ํ์ด ์ด์ ํ๊ณผ ์ฐ๊ฒฐ๋๋์ง ํ์ธํ๋ฉฐ, ์ด์ํ ์ ์ ํ๋๊ทธํด์ผ ํฉ๋๋ค.
๋ค์์ ์๋์ฐ ํจ์๋ฅผ ์ฌ์ฉํ๋ ์ผ๋ฐ์ ์ธ ํจํด์ ๋๋ค. ์ปฌ๋ผ ์ด๋ฆ์ ์์ ์ ํ ์ด๋ธ์ ๋ง๊ฒ ์กฐ์ ํ์ธ์.
WITH ordered AS (
SELECT
id,
created_at,
actor_id,
action,
entity,
entity_id,
payload,
prev_hash,
row_hash,
LAG(row_hash) OVER (ORDER BY created_at, id) AS expected_prev_hash,
/* expected row hash, computed the same way as in your insert trigger */
encode(
digest(
coalesce(prev_hash, '') || '|' ||
id::text || '|' ||
created_at::text || '|' ||
coalesce(actor_id::text, '') || '|' ||
action || '|' ||
entity || '|' ||
entity_id::text || '|' ||
payload::text,
'sha256'
),
'hex'
) AS expected_row_hash
FROM audit_log
)
SELECT
id,
created_at,
CASE
WHEN prev_hash IS DISTINCT FROM expected_prev_hash THEN 'BROKEN_LINK'
WHEN row_hash IS DISTINCT FROM expected_row_hash THEN 'HASH_MISMATCH'
ELSE 'OK'
END AS status
FROM ordered
WHERE prev_hash IS DISTINCT FROM expected_prev_hash
OR row_hash IS DISTINCT FROM expected_row_hash
ORDER BY created_at, id;
"๊นจ์ก๋๊ฐ ์๋๊ฐ"๋ฅผ ๋์ด์, ๋ฒ์ ๋ด ๋๋ฝ๋ id(๊ฐญ), ์์๊ฐ ๋ค๋ฐ๋ ๋งํฌ, ์ค์ ์ํฌํ๋ก์ ๋ง์ง ์๋ ์์ฌ์ค๋ฌ์ด ์ค๋ณต ๋ฑ์ ํ์ธํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ฒ์ฆ ๊ฒฐ๊ณผ๋ฅผ ๋ถ๋ณ ์ด๋ฒคํธ๋ก ๊ธฐ๋กํ๊ธฐ
์ฟผ๋ฆฌ๋ฅผ ์คํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํฐ์ผ์ ๋ฌป์ด๋์ง ๋ง์ธ์. ๊ฒ์ฆ ๊ฒฐ๊ณผ๋ ๋ณ๋์ ์ถ๊ฐ ์ ์ฉ ํ
์ด๋ธ(์: audit_verification_runs)์ ์คํ ์๊ฐ, ๊ฒ์ฆ๊ธฐ ๋ฒ์ , ๋๊ฐ ํธ๋ฆฌ๊ฑฐํ๋์ง, ์ ๊ฒํ ๋ฒ์, ๊นจ์ง ๋งํฌ์ ํด์ ๋ถ์ผ์น ์ ๋ฑ์ ํฌํจํด ์ ์ฅํ์ธ์.
์ด๋ ๊ฒ ํ๋ฉด ๋ ๋ฒ์งธ ์ถ์ ๊ธฐ๋ก์ด ์๊น๋๋ค: ๊ฐ์ฌ ๋ก๊ทธ๊ฐ ์จ์ ํ ๋ฟ ์๋๋ผ ์ ๊ธฐ์ ์ผ๋ก ์ ๊ฒํด์๋ค๋ ์ฌ์ค์ ์ฆ๋ช ํ ์ ์์ต๋๋ค.
์ค์ฉ์ ์ธ ์ฃผ๊ธฐ๋: ๊ฐ์ฌ ๋ก์ง์ ์ํฅ์ ์ฃผ๋ ๋ฐฐํฌ ํ, ํ์ฑ ์์คํ ์ ๋งค์ผ, ๊ณํ๋ ๊ฐ์ฌ ์ ์๋ ํญ์ ๊ฒ์ฆ์ ์คํํ๋ ๊ฒ์ ๋๋ค.
๋ณ์กฐ ๊ฐ์ง๋ฅผ ๊นจ๋ ํํ ์ค์๋ค
๋๋ถ๋ถ ์คํจ๋ ํด์ ์๊ณ ๋ฆฌ์ฆ ์์ฒด์ ๋ฌธ์ ๊ฐ ์๋๋ผ ์์ธ์ ๊ฐ๊ฒฉ(gap) ๋๋ฌธ์ ๋๋ค. ์ฌ๋๋ค์๊ฒ ๋ฐ๋ฐํ ์ฌ์ง๋ฅผ ์ฃผ๋ ์์ธ์ ๊ฐ๊ฒฉ์ด ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค.
์ ๋ขฐ๋ฅผ ์๋ ๊ฐ์ฅ ๋น ๋ฅธ ๋ฐฉ๋ฒ์ ๊ฐ์ฌ ํ์ ์ ๋ฐ์ดํธํ๋๋ก ํ์ฉํ๋ ๊ฒ์ ๋๋ค. "์ด๋ฒ ํ ๋ฒ๋ง"์ด๋ผ๋ ํ์ฉํ๋ฉด ์ ๋ก์ ์ค์ ๋ก ์ญ์ฌ๋ฅผ ๋ค์ ์ฐ๋ ๊ฒฝ๋ก๋ฅผ ๋ง๋ ๊ฒ์ ๋๋ค. ์์ ์ด ํ์ํ๋ฉด ๊ธฐ์กด์ ๋ฎ์ด์ฐ๊ธฐ๋ณด๋ค ์์ ์ฌ์ค์ ์ค๋ช ํ๋ ์ ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํ๊ณ ์๋ณธ์ ์ ์งํ์ธ์.
ํด์ ์ฐ์๋ ๋ถ์์ ํ ๋ฐ์ดํฐ๋ฅผ ํด์ฑํ ๋๋ ์คํจํฉ๋๋ค. JSON์ ํํ ํจ์ ์
๋๋ค. JSON ๋ฌธ์์ด์ ํด์ํ๋ฉด ํค ์์, ๊ณต๋ฐฑ, ์ซ์ ํฌ๋งท์ ์ฌ์ํ ์ฐจ์ด๋ก ํด์๊ฐ ๋ฌ๋ผ์ง๊ณ ๊ฒ์ฆ์ด ์๋๋ฌ์์ง๋๋ค. ์ ๊ทํ๋ ํํ, jsonb, ๋๋ ๋ค๋ฅธ ์ผ๊ด๋ ์ง๋ ฌํ๋ฅผ ์ ํธํ์ธ์.
๋ฐฉ์ด ๊ฐ๋ฅํ ์ถ์ ์ ์ฝํ์ํค๋ ๋ค๋ฅธ ํจํด๋ค:
- ์ปจํ ์คํธ(ํ์์คํฌํ, ํ์์, ๊ฐ์ฒด id, ๋์)๋ฅผ ๊ฑด๋๋ฐ๊ณ ํ์ด๋ก๋๋ง ํด์ฑํ๋ ๊ฒ.
- ๋ณ๊ฒฝ์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ง ์บก์ฒํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ํญ์ ์ผ์นํ๋ค๊ณ ๊ฐ์ ํ๋ ๊ฒ.
- ๋น์ฆ๋์ค ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ์ ์๊ณ ๊ฐ์ฌ ๊ธฐ๋ก๋ ๋ณ๊ฒฝํ ์ ์๋ ๋จ์ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ญํ ์ ์ฌ์ฉํ๋ ๊ฒ.
- ์ฒด์ธ ๋ด์์
prev_hash์ ๋ํด NULL์ ํ์ฉํ๋ ๋ช ํํ ๋ฌธ์ํ๋ ๊ท์น์ด ์๋ ๊ฒ.
๊ถํ ๋ถ๋ฆฌ(separation of duties)๊ฐ ์ค์ํฉ๋๋ค. ๋์ผํ ์ญํ ์ด ๊ฐ์ฌ ์ด๋ฒคํธ๋ฅผ ์ฝ์ ํ๋ฉด์ ๋์์ ์์ ํ ์ ์๋ค๋ฉด ๋ณ์กฐ ๊ฐ์ง๋ ํต์ ์ฑ ์ด ์๋๋ผ ๋จ์ง ์ฝ์์ ๋ถ๊ณผํด์ง๋๋ค.
๋ฐฉ์ด ๊ฐ๋ฅํ ๊ฐ์ฌ ์ถ์ ์ฒดํฌ๋ฆฌ์คํธ
๋ฐฉ์ด ๊ฐ๋ฅํ ๊ฐ์ฌ ์ถ์ ์ ๋ณ๊ฒฝํ๊ธฐ ์ด๋ ต๊ณ ๊ฒ์ฆํ๊ธฐ ์ฌ์์ผ ํฉ๋๋ค.
๊ถํ ๊ด๋ฆฌ๋ก ์์ํ์ธ์: ๊ฐ์ฌ ํ ์ด๋ธ์ ์ค์ ๋ก ์ถ๊ฐ ์ ์ฉ์ด์ด์ผ ํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ญํ ์ ์ฝ์ (๋ฐ ๋ณดํต์ ์กฐํ)๋ง ํ ์ ์๊ณ ์ ๋ฐ์ดํธ๋ ์ญ์ ๋ ๋ชป ํ๊ฒ ํ์ธ์. ์คํค๋ง ๋ณ๊ฒฝ์ ์๊ฒฉํ ์ ํํ์ธ์.
๊ฐ ํ์ด ์กฐ์ฌ์๊ฐ ๋ฌผ์ ์ง๋ฌธ์ ๋ตํ ์ ์๋๋ก ํ์ธ์: ๋๊ฐ ํ๋๊ฐ, ์ธ์ (์๋ฒ ์ธก) ํ๋๊ฐ, ๋ฌด์จ ์ผ์ด ์์๋๊ฐ(๋ช ํํ ์ด๋ฒคํธ ์ด๋ฆ๊ณผ ๋์), ๋ฌด์์ ๊ฑด๋๋ ธ๋๊ฐ(์ํฐํฐ ์ด๋ฆ๊ณผ id), ๊ทธ๋ฆฌ๊ณ ์ด๋ป๊ฒ ์ฐ๊ฒฐ๋๋๊ฐ(์์ฒญ/์๊ด id ๋ฐ ํธ๋์ญ์ id).
๋ค์์ผ๋ก ๋ฌด๊ฒฐ์ฑ ๊ณ์ธต์ ๊ฒ์ฆํ์ธ์. ๋น ๋ฅธ ํ
์คํธ๋ ๊ตฌ๊ฐ์ ์ฌ์ํด ๊ฐ prev_hash๊ฐ ์ด์ ํ์ ํด์์ ์ผ์นํ๋์ง, ์ ์ฅ๋ ํด์๊ฐ ์ฌ๊ณ์ฐ๋ ํด์์ ์ผ์นํ๋์ง ํ์ธํ๋ ๊ฒ์
๋๋ค.
์ด์์ ์ผ๋ก ๊ฒ์ฆ์ ์ผ๋ฐ ์์ ์ฒ๋ผ ์ทจ๊ธํ์ธ์:
- ์์ฝ๋ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฌ๋ฅผ ์คํํ๊ณ ํต๊ณผ/์คํจ ๊ฒฐ๊ณผ์ ๋ฒ์๋ฅผ ์ ์ฅํ์ธ์.
- ๋ถ์ผ์น, ๊ฐญ, ๊นจ์ง ๋งํฌ์ ๋ํด ๊ฒฝ๊ณ ๋ฅผ ์ค์ ํ์ธ์.
- ๋ณด๊ด ๊ธฐ๊ฐ(retention) ๋์ ์ถฉ๋ถํ ๋ฐฑ์ ์ ๋ณด๊ดํ๊ณ ๋ณด๊ด ์ ์ฑ ์ ์ ๊ฐ ๊ฐ์ฌ ๊ธฐ๋ก์ด ์กฐ๊ธฐ์ "์ ๋ฆฌ"๋์ง ์๊ฒ ํ์ธ์.
์์: ์ปดํ๋ผ์ด์ธ์ค ๊ฒํ ์์ ์์ฌ์ค๋ฌ์ด ์์ ์ ์ฐพ๊ธฐ
์์ฃผ ๋์ค๋ ํ ์คํธ ์ฌ๋ก๋ ํ๋ถ ๋ถ์์ ๋๋ค. ๊ณ ๊ฐ์ด $250 ํ๋ถ์ด ์น์ธ๋์๋ค๊ณ ์ฃผ์ฅํ๋๋ฐ ์์คํ ์๋ ์ด์ $25๋ก ๋ณด์ธ๋ค๊ณ ํฉ์๋ค. ์ง์ํ์ ์น์ธ ๋ด์ฉ์ด ๋ง๋ค๊ณ ์ฃผ์ฅํ๊ณ ์ปดํ๋ผ์ด์ธ์ค๋ ๋ต์ ์ํฉ๋๋ค.
์๊ด id(์ฃผ๋ฌธ id, ํฐ์ผ id, ๋๋ refund_request_id)์ ์๊ฐ ๋ฒ์๋ฅผ ์ฌ์ฉํด ๊ฒ์ ๋ฒ์๋ฅผ ์ขํ์ธ์. ํด๋น ์๊ด id์ ๋ํ ๊ฐ์ฌ ํ์ ๊ฐ์ ธ์ ์น์ธ ์๊ฐ ์ฃผ๋ณ์ ๋ธ๋ํทํ์ธ์.
์ํ๋ ๊ฒ์ ์ ์ฒด ์ด๋ฒคํธ ์งํฉ์ ๋๋ค: ์์ฒญ ์์ฑ, ํ๋ถ ์น์ธ, ํ๋ถ ๊ธ์ก ์ค์ , ๊ทธ๋ฆฌ๊ณ ์ดํ์ ์ ๋ฐ์ดํธ๋ค. ๋ณ์กฐ ๊ฐ์ง ์ค๊ณ๊ฐ ๋์ด ์๋ค๋ฉด ์ํ์ค๊ฐ ์จ์ ํ๋์ง๋ ํ์ธํฉ๋๋ค.
๊ฐ๋จํ ์กฐ์ฌ ํ๋ฆ:
- ์๊ด id์ ๋ํ ๋ชจ๋ ๊ฐ์ฌ ํ์ ์๊ฐ ์์ผ๋ก ๋น๊ฒจ์ต๋๋ค.
- ์ ์ฅ๋ ํ๋(๋ฐ
prev_hash)์์ ๊ฐ ํ์ ํด์๋ฅผ ์ฌ๊ณ์ฐํฉ๋๋ค. - ๊ณ์ฐํ ํด์์ ์ ์ฅ๋ ํด์๋ฅผ ๋น๊ตํฉ๋๋ค.
- ์ฒซ ๋ฒ์งธ๋ก ๋ค๋ฅธ ํ์ ์๋ณํ๊ณ ์ดํ ํ๋ค๋ ์คํจํ๋์ง ํ์ธํฉ๋๋ค.
๋๊ตฐ๊ฐ๊ฐ ๋จ์ผ ๊ฐ์ฌ ํ์ ํธ์ง(์: ๊ธ์ก์ 250์์ 25๋ก ๋ณ๊ฒฝ)ํ๋ค๋ฉด ๊ทธ ํ์ ํด์๋ ๋ ์ด์ ๋ง์ง ์์ ๊ฒ์ ๋๋ค. ๋ค์ ํ์ ์ด์ ํด์๋ฅผ ํฌํจํ๋ฏ๋ก ๋ถ์ผ์น๋ ๋ณดํต ์์ผ๋ก ์ ํ๋ฉ๋๋ค. ์ด ์ ํ(cascade)๊ฐ ์ง๋ฌธ์ ๋๋ค: ์ฌํ์ ๊ฐ์ฌ ๊ธฐ๋ก์ด ๋ณ๊ฒฝ๋์๋ค๋ ๊ฒ์ ๋ณด์ฌ์ค๋๋ค.
์ฒด์ธ์ด ์๋ ค์ค ์ ์๋ ๊ฒ: ์์ ์ด ๋ฐ์ํ๋์ง, ์ฒด์ธ์ด ์ฒ์ ๋๊ธด ์์น, ์ํฅ์ ๋ฐ์ ํ์ ๋ฒ์. ์ฒด์ธ๋ง์ผ๋ก ์๋ ค์ฃผ์ง ๋ชปํ๋ ๊ฒ: ๋๊ฐ ์์ ํ๋์ง, ๋ฎ์ด์จ์ง ๊ฒฝ์ฐ ์๋ ๊ฐ์ด ๋ฌด์์ด์๋์ง, ๋ค๋ฅธ ํ ์ด๋ธ๋ค๋ ๋ณ๊ฒฝ๋์๋์ง ์ฌ๋ถ ๋ฑ์ ๋๋ค.
๋ค์ ๋จ๊ณ: ์์ ํ๊ฒ ์ ์ฉํ๊ณ ์ ์ง ๊ฐ๋ฅํ๊ฒ ๋ง๋ค๊ธฐ
๊ฐ์ฌ ์ถ์ ์ ๋ค๋ฅธ ๋ณด์ ์ ์ด์ฒ๋ผ ๋ค๋ฃจ์ธ์. ์์ ๋จ๊ณ๋ก ๋กค์์ํ๊ณ ์๋์ ์ฆ๋ช ํ ๋ค ํ๋ํ์ธ์.
๋ ผ์์ด ์๊ธฐ๋ฉด ๊ฐ์ฅ ํฐ ํผํด๋ฅผ ์ ์ ์ก์ (๊ถํ ๋ณ๊ฒฝ, ์ง๊ธ, ํ๋ถ, ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ, ์๋ ์ค๋ฒ๋ผ์ด๋)๋ถํฐ ์์ํ์ธ์. ๊ทธ๊ฑธ ์ ์ฉํ ํ ํต์ฌ ์ค๊ณ๋ฅผ ๋ฐ๊พธ์ง ์๊ณ ์ํ์ด ๋ฎ์ ์ด๋ฒคํธ๋ค์ ์ถ๊ฐํ์ธ์.
๊ฐ์ฌ ์ด๋ฒคํธ ๊ณ์ฝ์ ๋ฌธ์ํํ์ธ์: ์ด๋ค ํ๋๋ฅผ ๊ธฐ๋กํ๋์ง, ๊ฐ ์ด๋ฒคํธ ํ์ ์ด ๋ฌด์์ ์๋ฏธํ๋์ง, ํด์๊ฐ ์ด๋ป๊ฒ ๊ณ์ฐ๋๋์ง, ๊ฒ์ฆ์ ์ด๋ป๊ฒ ์คํํ๋์ง ๊ธฐ๋กํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์์ ๋ฌธ์๋ฅผ ๋์ธ์. ๊ฒ์ฆ ์ ์ฐจ๋ฅผ ๋ฐ๋ณต ๊ฐ๋ฅํ๊ฒ ์ ์งํ์ธ์.
๋ณต์ ์ฐ์ต(restore drills)์ ์ค์ํฉ๋๋ค. ์กฐ์ฌ๋ ์ข ์ข ๋ผ์ด๋ธ ์์คํ ์ด ์๋ ๋ฐฑ์ ์์ ์์๋ฉ๋๋ค. ์ ๊ธฐ์ ์ผ๋ก ํ ์คํธ DB๋ก ๋ณต์ํด ์ฒด์ธ ๋์์ ๋๊น์ง ๊ฒ์ฆํ์ธ์. ๋ณต์ ํ ๋์ผํ ๊ฒ์ฆ ๊ฒฐ๊ณผ๋ฅผ ์ฌํํ ์ ์๋ค๋ฉด ๋ณ์กฐ ๊ฐ์ง๋ฅผ ๋ฐฉ์ดํ๊ธฐ ์ด๋ ต์ต๋๋ค.
๋ด๋ถ ๋๊ตฌ์ ๊ด๋ฆฌ์ ์ํฌํ๋ก๋ฅผ AppMaster (appmaster.io)๋ก ๋น๋ํ๋ ๊ฒฝ์ฐ, ์๋ฒ ์ธก์์ ๊ฐ์ฌ ์ด๋ฒคํธ ์ฐ๊ธฐ๋ฅผ ํ์คํํ๋ฉด ์ด๋ฒคํธ ์คํค๋ง์ ์๊ด id๊ฐ ๊ธฐ๋ฅ ์ ๋ฐ์์ ์ผ๊ด๋๊ฒ ์ ์ง๋์ด ๊ฒ์ฆ๊ณผ ์กฐ์ฌ๊ฐ ํจ์ฌ ๊ฐ๋จํด์ง๋๋ค.
์ด ์์คํ ์ ์ํ ์ ์ง๋ณด์ ์๊ฐ์ ์์ฝํ์ธ์. ํ์ด ์ ๊ธฐ๋ฅ์ ๋ฐฐํฌํ๋ฉด์ ์ด๋ฒคํธ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ํด์ ์ ๋ ฅ์ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ๊ฒ์ฆ ์์ ๊ณผ ๋ณต์ ์ฐ์ต์ ๊ณ์ ์คํํ๋ ๊ฒ์ ์์ผ๋ฉด ๊ฐ์ฌ ์ถ์ ์ ์กฐ์ฉํ ์คํจํฉ๋๋ค.


