𝘚𝘭𝘰𝘸 𝘣𝘢𝘡 𝘴𝘡𝘦𝘒π˜₯𝘺

[λ°μ΄ν„°λ² μ΄μŠ€/DB] Stored Procedure vs Trigger? λ³Έλ¬Έ

λ°μ΄ν„°λ² μ΄μŠ€/이둠

[λ°μ΄ν„°λ² μ΄μŠ€/DB] Stored Procedure vs Trigger?

.23 2025. 4. 12. 00:46

λ‘μœ λ…Έ ν”„λ‘œμ‹œμ Έ ?

 

πŸ“˜ Stored Procedure(μ €μž₯ ν”„λ‘œμ‹œμ €)

자주 μ‚¬μš©ν•˜λŠ” SQL문을 ν•˜λ‚˜μ˜ μ΄λ¦„μœΌλ‘œ λ¬Άμ–΄μ„œ μ €μž₯ν•˜λŠ” λ°©λ²•μœΌλ‘œ, ν•„μš”ν•  λ•Œ λ§ˆλ‹€ ν˜ΈμΆœν•˜μ—¬ μ‹€ν–‰ν•  수 μžˆλŠ” ν”„λ‘œκ·Έλž¨ 객체λ₯Ό μ˜λ―Έν•œλ‹€. 주둜 λ°˜λ³΅λ˜λŠ” λ‘œμ§μ„ 'μž¬μ‚¬μš©' ν•˜κ±°λ‚˜, λ³΅μž‘ν•œ νŠΈλžœμž­μ…˜ 처리λ₯Ό μœ„ν•΄ μ‚¬μš©λœλ‹€.

 

μ΄λ ‡κ²Œ λ§ν•˜λ©΄ μ–΄λ €μš΄λ° λ‚˜λ¦„ λ§€κ°œλ³€μˆ˜μ˜ κ°œλ…λ„ 있고, μž¬μ‚¬μš©μ„±μ„ μœ„ν•΄ ν™œμš©λœλ‹€λŠ” μ μ—μ„œ μΌμ’…μ˜ method/ν•¨μˆ˜μ™€ μœ μ‚¬ν•œ ꡬ쑰둜 μž‘λ™ν•œλ‹€.

 

νŠΉμ§•μœΌλ‘œλŠ” μž…λ ₯(IN), 좜λ ₯(OUT), μž…μΆœλ ₯(INOUT)의 κ°œλ…μ΄ μ‘΄μž¬ν•˜κ³ , IF/WHILE/LOOP/CASE λ“±μ˜ μ œμ–΄λ¬Έ μ‚¬μš©λ„ κ°€λŠ₯ν•˜λ‹€.

 

ν”„λ‘œμ‹œμ €λŠ” κ΅¬ν˜„ 및 디버깅이 μ–΄λ ΅λ‹€λŠ” ν•œκ³„μ λ„ μžˆμœΌλ‚˜, 둜직 μΊ‘μŠν™”, 쀑볡 제거, μ„±λŠ₯ ν–₯상 λ“± μœ μ§€λ³΄μˆ˜ λ©΄μ—μ„œ λͺ…ν™•ν•œ μž₯점이 μ‘΄μž¬ν•œλ‹€. 

 

μ½”λ“œ μ˜ˆμ‹œ

상황: μ‡Όν•‘λͺ° 관리 DBλ₯Ό λ§Œλ“€μ—ˆλ‹€κ³  κ°€μ •ν•  λ•Œ, μƒν’ˆ μ£Όλ¬Έ μ‹œ μž¬κ³ κ°€ 없을 경우 μž¬κ³ κ°€ μ—†λ‹€λŠ” λ©”μ‹œμ§€λ₯Ό 좜λ ₯ν•˜λŠ” ν”„λ‘œμ‹œμ €λ₯Ό μž‘μ„±ν•΄λ³΄μ•˜λ‹€.

 

DELIMITER //

CREATE PROCEDURE PlaceOrder(
    IN p_ProductID INT,
    IN p_Quantity INT
)

 

이와 같이 PlaceOrder λΌλŠ” μ΄λ¦„μ˜ ν”„λ‘œμ‹œμ €λ₯Ό μ •μ˜ν•˜κ³ , μž…λ ₯(IN) λ§€κ°œλ³€μˆ˜λ‘œ 받을 μƒν’ˆμ˜ ID(p_ProductID), ꡬ맀 희망 μˆ˜λŸ‰(p_Quantity)을 λ¨Όμ € μ •μ˜ν•œλ‹€.

 

참고둜 DELIMITER // λΌλŠ” 것은.. ..

μ›λž˜ SQLμ—μ„œ μ‹€ν–‰ λ‹¨μœ„λ₯Ό μ„Έλ―Έμ½œλ‘  ; 으둜 κ΅¬λΆ„ν•˜μ§€λ§Œ, ν”„λ‘œμ‹œμ €μ—μ„œλŠ” λ‹€μ–‘ν•œ SQL문듀이 ν•˜λ‚˜μ˜ λΈ”λ‘μœΌλ‘œ κ΅¬μ„±λ˜κΈ° λ•Œλ¬Έμ—, 쿼리문이 쀑간에 μ‹€ν–‰λ˜λ‹€ μž˜λ¦¬λŠ” 일이 없도둝 ν•˜λ‚˜μ˜ μ‹€ν–‰ λ‹¨μœ„λ₯Ό DELIMITER 뒀에 μ„ μ–Έν•˜λŠ” 기호둜 κ΅¬λΆ„ν•˜κ² λ‹€λŠ” μ˜λ―Έμ΄λ‹€.

 

DELIMITER // λ©΄ λ‹€μŒ //이 λ‚˜μ˜¬λ•ŒκΉŒμ§€κ°€ ν•œ λΈ”λ‘μ΄λΌλŠ” 의미!

 

BEGIN
    DECLARE v_Stock INT;
    DECLARE v_OrderID INT;

    -- ν˜„μž¬ 재고 확인
    SELECT Stock INTO v_Stock
    FROM Product_SP
    WHERE ProductID = p_ProductID;

    -- μž¬κ³ κ°€ μ—†κ±°λ‚˜ λΆ€μ‘±ν•œ 경우
    IF v_Stock IS NULL OR v_Stock < p_Quantity THEN
        INSERT INTO Order_Log (OrderID, LogMessage)
        VALUES (NULL, CONCAT('ProductID ', p_ProductID, ' - 재고 λΆ€μ‘±μœΌλ‘œ μ£Όλ¬Έ μ‹€νŒ¨'));
    ELSE
        -- μ£Όλ¬Έ 생성
        INSERT INTO `Order` (ProductID, Quantity)
        VALUES (p_ProductID, p_Quantity);

        -- λ§ˆμ§€λ§‰ μ£Όλ¬Έ 번호 κ°€μ Έμ˜€κΈ°
        SET v_OrderID = LAST_INSERT_ID();

        -- 재고 차감
        UPDATE Product_SP
        SET Stock = Stock - p_Quantity
        WHERE ProductID = p_ProductID;

        -- 둜그 기둝
        INSERT INTO Order_Log (OrderID, LogMessage)
        VALUES (v_OrderID, CONCAT('ProductID ', p_ProductID, ' μ£Όλ¬Έ 성곡, μˆ˜λŸ‰: ', p_Quantity));
    END IF;
END;
//

DELIMITER ;

 

λ‹€μŒ BEGIN ~ END λΆ€λΆ„μ—μ„œ μ‹€ν–‰ λ‘œμ§μ„ μ •μ˜ν•œλ‹€.

 

DECLAREλ₯Ό 톡해 λ³Έλ¬Έ μ•ˆμ—μ„œ μ‚¬μš©ν•  둜컬 λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜κ³ (λͺ…λ Ήμ–΄ μ§„μ§œ μ§κ΄€μ μž„), μ›ν•˜λŠ” λ‘œμ§λŒ€λ‘œ 쿼리문을 κ΅¬μ„±ν•œλ‹€.

μš°μ„  μ£Όλ¬Έν•˜λ €λŠ” μƒν’ˆμ˜ ν˜„μž¬ 재고λ₯Ό κ°–κ³ μ˜€κΈ° μœ„ν•΄ Product_SP ν…Œμ΄λΈ”μ—μ„œ v_Stockμ΄λΌλŠ” λ³€μˆ˜μ— Stock을 ν• λ‹Ήν•΄μ£Όκ³ ,

 

미리 λ§€κ°œλ³€μˆ˜λ‘œ μž…λ ₯받은 p_Quantity보닀 v_Stock이 μ κ±°λ‚˜ 정보가 μ—†μœΌλ©΄ log ν…Œμ΄λΈ”μ— μ£Όλ¬Έ μ‹€νŒ¨ν–ˆλ‹€λŠ” 기둝을, μ£Όλ¬Έ κ°€λŠ₯ν•œ 경우 μ£Όλ¬Έ λ‚΄μ—­κ³Ό μˆ˜λŸ‰ 정보λ₯Ό μž…λ ₯ν•œλ‹€.

 

이후 μ£Όλ¬Έν•˜λ €λŠ” μˆ˜λŸ‰κ³Ό μ£Όλ¬Έν•˜λ €λŠ” 개수만큼 μˆ«μžλ‚˜ 값을 λ‹΄μ•„

CALL PlaceOrder(1, 2);
CALL PlaceOrder(2, 1);
CALL PlaceOrder(5, 15);
-- ....

 

와 같이 μ‚¬μš©ν•  경우, λ‹€μŒκ³Ό 같이 λ‘œκ·Έκ°€ λ‚¨λŠ” 것을 확인할 수 μžˆλ‹€.

μ‹€ν–‰ κ²°κ³Ό

 

πŸ“˜ Trigger(트리거)

νŠΈλ¦¬κ±°λŠ” νŠΉμ • 이벀트(INSERT, UPDATE, DELETE λ“±)κ°€ ν…Œμ΄λΈ”μ—μ„œ λ°œμƒν•  λ•Œ μžλ™μœΌλ‘œ μ‹€ν–‰λ˜λŠ” SQL 블둝이닀.

μΌμ’…μ˜ onClickκ³Ό 같은 이벀트 λ¦¬μŠ€λ„ˆ 역할을 μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ—, 감사 둜그(audit log)λ₯Ό μžλ™ κΈ°λ‘ν•˜κ±°λ‚˜, 데이터 무결성을 λ³΄μ‘°ν•˜κ±°λ‚˜ μ•Œλ¦Ό κΈ°λŠ₯을 ν•˜λŠ” λ“± κ°„λ‹¨ν•œ μž‘μ—…μ„ μžλ™μœΌλ‘œ μˆ˜ν–‰ν•  λ•Œ μ •μ˜ν•˜λ©΄ μ’‹λ‹€.

 

μ½”λ“œ μ˜ˆμ‹œ

사싀 μœ„μ—μ„œ ν”„λ‘œμ‹œμ €λ‘œ 보여쀀 μ˜ˆμ‹œμ²˜λŸΌ μ£Όλ¬Έ λ°œμƒ μ‹œ μ£Όλ¬Έ 정보에 λŒ€ν•œ 둜그λ₯Ό λ‚¨κΈ°λŠ” μ •λ„μ˜ μ•‘μ…˜μ€ 트리거둜 μž‘μ„±ν•˜λŠ” 것이 μΌλ°˜μ μ΄λ‹€.

 

DELIMITER //

CREATE TRIGGER trg_after_order_insert
AFTER INSERT ON `Order`
FOR EACH ROW
BEGIN
    INSERT INTO Order_Log (OrderID, LogMessage)
    VALUES (NEW.OrderID, CONCAT('μ£Όλ¬Έ 번호 ', NEW.OrderID, '이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'));
END;
//

DELIMITER ;

 

AFTER INSERT ON `Order` λ₯Ό 톡해 'INSERT' action이 λ°œμƒν•  λ•Œ 각 ν–‰λ§ˆλ‹€(For Each Row) BEGIN ~ END 사이에 기둝된 쿼리문이 μ‹€ν–‰λ˜λŠ” 트리거λ₯Ό μž‘μ„±ν•΄μ€€λ‹€.

 

그러면,

INSERT INTO `Order` (ProductID, Quantity)
VALUES (2, 1);

이처럼 trigger 이름을 ꡳ이 λͺ…μ‹œν•˜μ§€ μ•Šμ•„λ„ `Order` ν…Œμ΄λΈ”μ— INSERT문이 μ‹€ν–‰λ˜λ©΄ μžλ™μœΌλ‘œ Order_Log에 기둝이 λ‚¨κ²Œ λœλ‹€.

 

 

LogID 1 ~ 5κΉŒμ§€κ°€ ν”„λ‘œμ‹œμ €λ₯Ό 톡해 기둝된 둜그, LogID 6번이 insertλ₯Ό μˆ˜ν–‰ν–ˆμ„ λ•Œ 트리거λ₯Ό 톡해 기둝된 λ‘œκ·Έμ΄λ‹€.

 

정말 κ°„λ‹¨ν•˜μ£ ?

 

κ·ΈλŸ¬λ‚˜!!!!!!!

SQL은 μ–΄λ””κΉŒμ§€λ‚˜ 데이터λ₯Ό κ°€μ Έμ˜€κ³  μ²˜λ¦¬ν•˜λŠ” 데 μ΅œμ ν™”λœ 언어일 뿐, μž¦μ€ μ—°μ‚°μ΄λ‚˜ 흐름 μ œμ–΄ λ“± 'μ ˆμ°¨ν˜• μ œμ–΄'μ—λŠ” ν•œκ³„κ°€ μžˆλ‹€.

λ”°λΌμ„œ λ¬΄ν„±λŒ€κ³  λͺ¨λ“  λ‹¨μˆœ μž‘μ—…μ„ ν”„λ‘œμ‹œμ €λ‚˜ 트리거λ₯Ό μ •μ˜ν•˜λŠ” 것은 지양해야 ν•  것이닀.

 

πŸ“š 정리

λΉ„κ΅ν‘œ

 

 

 

Comments