Contents
1. Stored Procedure vs Stored Function
2. Stored Procedure와 계층 구조
3. Stored Procedure의 장점
4. Stored Procedure의 단점
5. 비즈니스 로직의 위치
1. Stored Procedure vs Stored Function
Stored Procedure는 여러 SQL을 묶어 하나의 작업 흐름을 처리하기 위한 절차에 가깝다. 예를 들어 주문 생성, 재고 차감, 정산 처리처럼 여러 테이블을 조회하거나 변경해야 하는 업무 로직에 사용한다.
반면 Stored Function은 SQL문 안에서 하나의 값처럼 사용하기 위한 함수에 가깝다. 예를 들어 할인 가격 계산, 회원 등급 계산, 날짜 계산처럼 특정 입력을 받아 하나의 값을 반환할 때 사용한다.
1.1 Stored Procedure
DB에 저장되어 실행되는 절차형 SQL 코드
CREATE PROCEDURE update_user_point(IN userId BIGINT, IN pointAmount INT)
BEGIN
UPDATE users
SET point = point + pointAmount
WHERE id = userId;
END;- 하나의 업무 절차를 DB 안에서 처리하고 싶을 때 사용(비즈니스 로직)
CALL로 호출- 입력값과 출력값을 가질 수 있음
- 조회뿐 아니라
INSERT,UPDATE,DELETE같은 데이터 변경 작업도 수행 가능
1.2 Stored Function
DB에 저장되어 실행되는 함수
CREATE FUNCTION get_discount_price(price INT, discount_rate INT)
RETURNS INT
BEGIN
RETURN price - (price * discount_rate / 100);
END;- 특정 값을 계산해서 반환하는 데 목적이 있음
SELECT,WHERE,ORDER BY등 SQL문 안에서 사용할 수 있음- 일반적으로 하나의 값을 반환
1.3 비교 표
| 구분 | Stored Procedure | Stored Function |
|---|---|---|
| 목적 | 특정 작업 수행 | 특정 값 계산 후 반환 |
| 호출 방식 | CALL procedure_name() | SELECT function_name() |
| 반환 방식 | OUT 파라미터 또는 결과셋 | RETURN 값 |
| SQL문 내부 사용 | 일반적으로 불가 | 가능 |
| 데이터 변경 작업 | 가능 | DBMS에 따라 제한적 |
| 사용 예시 | 주문 처리, 정산 처리, 일괄 업데이트 | 할인율 계산, 문자열 변환, 특정 값 계산 |
2. Stored Procedure와 계층 구조
Stored Procedure를 사용하면 Data Tier에서 Business Logic을 관리하게 된다.
2.1 일반적인 애플리케이션 계층 구조
- Presentation Tier
- Logic / Business Tier
- Data Tier
2.2 Stored Procedure의 위치
- Stored Procedure는 DB에 저장됨
- 따라서 Data Tier에 속함
- 그런데 그 안에 비즈니스 로직이 들어갈 수 있음
비즈니스 로직은 어디에 두는 것이 좋을까?
3. Stored Procedure의 장점
3.1 애플리케이션 코드 변경 없이 로직 변경이 가능할 수 있다
3.1.1 Logic Tier에서 관리할 때의 문제
- 비즈니스 로직이 애플리케이션 서버에 포함됨
- 로직 변경 시 애플리케이션 코드를 수정해야 함
- 수정 후 재빌드/재배포가 필요할 수 있음
- 서버가 여러 대라면 각 서버에 변경된 코드를 배포해야 함
3.1.2 Stored Procedure를 사용할 때의 장점
- 비즈니스 로직 일부를 DB의 프로시저로 관리할 수 있음
- 프로시저 내부 로직만 변경하면 애플리케이션 호출 코드는 그대로 둘 수 있음
- 여러 애플리케이션 서버가 같은 프로시저를 호출하므로, DB의 프로시저 변경만으로 동일한 로직을 적용할 수 있음
3.1.3 Application Transparent
- 내부 구현이 바뀌어도 애플리케이션 호출 방식이 그대로 유지되는 경우
- 즉, 애플리케이션 코드를 변경하지 않아도 되는 상태
- 예:
CALL calculate_order_price()는 그대로인데 내부 계산 로직만 변경
3.2 네트워크 트래픽을 줄일 수 있다
3.2.1 Logic Tier에서 처리하는 경우
- 애플리케이션 서버가 DB에 여러 번 요청해야 할 수 있음
예시:
- 주문 정보 조회
- 재고 확인
- 포인트 차감
- 주문 상태 업데이트
이 경우 애플리케이션과 DB 사이에 여러 번 네트워크 왕복이 발생한다.
3.2.2 Stored Procedure에서 처리하는 경우
DB 접근이 여러 번 필요한 로직이라면, Stored Procedure가 응답 속도 측면에서 유리할 수 있다.
- DB 내부에서 여러 SQL을 한 번에 실행
- 애플리케이션은 프로시저를 한 번만 호출
- 네트워크 왕복 횟수를 줄일 수 있음
3.3 여러 서비스에서 재사용할 수 있다
3.3.1 서비스마다 언어가 다른 경우
- Spring
- Django
- Node.js
각 서비스가 같은 비즈니스 로직을 직접 구현하면 중복이 발생할 수 있다.
3.3.2 Stored Procedure를 사용하면
- 각 서비스는 동일한 프로시저를 호출하면 됨
- 언어나 프레임워크와 무관하게 재사용 가능
3.4 민감한 데이터 접근을 제한할 수 있다
정해진 프로시저를 통해서만 데이터에 접근하게 하여, 민감한 컬럼에 대한 직접 조회를 제한할 수 있다.
직접 테이블 접근 제한:
- 애플리케이션에 테이블 직접 접근 권한을 주지 않음
- 대신 특정 프로시저 실행 권한만 부여
4. Stored Procedure의 단점
4.1 유지보수 비용이 커질 수 있다
4.1.1 비즈니스 로직이 분산된다
- 일부 로직은 애플리케이션 코드에 있음
- 일부 로직은 DB 프로시저에 있음
결과적으로 문제를 분석할 때 양쪽을 모두 확인해야 한다.
4.1.2 버전 관리가 복잡해진다
- 애플리케이션 소스 코드 버전 관리
- DB 프로시저 버전 관리
- 배포 순서 관리
- 롤백 전략 관리
4.1.3 개발자가 알아야 할 기술이 늘어난다
- 애플리케이션 언어
- SQL
- DBMS별 프로시저 문법
- DB 배포 방식
4.2 DB 서버에 부하가 집중될 수 있다
Logic Tier에서 처리할 수 있는 CPU 부하를 DB로 옮기면, 확장하기 어려운 계층에 부하가 집중될 수 있다.
4.2.1 비즈니스 로직이 DB에서 실행된다
- 단순 조회만 DB가 처리하는 것이 아님
- 조건 판단, 반복 처리, 계산 로직까지 DB가 수행할 수 있음
- DB CPU 사용량이 증가할 수 있음
4.2.2 DB 서버 확장은 쉽지 않다
- 애플리케이션 서버는 비교적 쉽게 수평 확장 가능
- 상태를 직접 들고 있지 않기 때문에 오토스케일링이 쉬움
- 반면 DB는 데이터를 가지고 있음
- 복제, 샤딩, 정합성, 락, 트랜잭션 등을 고려해야 함
4.3 Stored Procedure가 항상 Application Transparent한 것은 아니다
Stored Procedure는 내부 구현 변경에는 투명할 수 있지만, 호출 인터페이스가 바뀌면 결국 애플리케이션 코드도 변경이 필요하다.
내부 로직만 바꾸는 경우:
- 프로시저 이름과 파라미터가 그대로라면 애플리케이션 수정 없이 변경 가능
인터페이스가 바뀌는 경우:
CALL calculate_order_price();에서CALL calculate_order_price_v2();으로 바뀐다면 애플리케이션 코드도 수정해야 한다.
4.4 Transparent하다는 것이 항상 좋은 것은 아니다
4.4.1 프로시저 버그의 영향 범위
- 프로시저 하나를 변경하면 해당 프로시저를 호출하는 모든 서비스에 즉시 영향이 갈 수 있음
- 여러 서비스가 공유 중이라면 장애 범위가 커질 수 있음
4.4.2 애플리케이션 배포와의 차이
- 애플리케이션 서버는 순차 배포 가능
- 일부 서버에만 먼저 배포하고 모니터링 가능
- 문제가 있으면 해당 서버만 롤백 가능
4.4.3 Stored Procedure 변경의 위험
- DB에 반영되는 순간 모든 호출에 적용될 수 있음
- 변경 영향 범위를 통제하기 어려울 수 있음
4.5 재사용 가능하다는 점이 양날의 검이 될 수 있다
재사용은 중복 제거에는 좋지만, 소유권과 변경 통제가 없으면 장애 전파 지점이 될 수 있다.
4.5.1 통제되지 않는 재사용
- 여러 서비스가 같은 프로시저를 호출
- 호출량 증가
- DB 부하 증가
- 하나의 변경이 여러 서비스에 영향
4.5.2 Data Service의 필요성
- DB에 직접 접근하는 서비스가 많아지면 통제하기 어려움
- 앞단에 Data Service를 두고 API로 접근을 통제하는 방식이 더 안전할 수 있음
4.6 비즈니스 로직을 애플리케이션에 두고도 성능을 개선할 수 있다
Stored Procedure만이 성능 개선 방법은 아니다. 오히려 애플리케이션 계층에서 개선하면 DB 부하까지 줄일 수 있다.
병렬 처리:
- 서로 의존성이 없는 작업이라면 동시에 실행 가능
- 예:
insert와update가 독립적이라면 병렬 처리 검토 가능
비동기 처리:
- 즉시 응답이 필요 없는 작업은 비동기 처리 가능
- 메시지 큐, 이벤트 기반 처리 등을 활용할 수 있음
캐시 사용:
- 자주 조회되는 데이터는 캐시로 처리
- 응답 속도 개선
- DB 부하 감소
커넥션 풀, 스레드 풀, Non-blocking I/O:
- 애플리케이션 계층에서도 다양한 성능 개선 기법을 사용할 수 있음
4.7 보안을 완벽하게 보장하지는 않는다
Stored Procedure는 접근 제어 수단이 될 수 있지만, 보안의 완전한 해결책은 아니다.
4.7.1 Procedure 권한만으로 충분하지 않다
- 프로시저 실행 권한을 가진 사용자가 민감한 결과를 얻을 수 있음
- 프로시저 내부 로직이 잘못되면 의도치 않은 데이터 노출 가능
4.7.2 추가 보안 대책이 필요하다
- 담당자별 DB 권한 제한
- 테이블 직접 접근 제한
- 민감 정보 암호화
- 접근 로그 관리
- 보안 정책과 절차 강화
4.8 복잡하고 유연한 로직 작성에 불리할 수 있다
- Java, Kotlin, Python, JavaScript 등은 다양한 라이브러리와 구조화 기법을 제공
- 테스트, 디버깅, 리팩토링 도구도 풍부함
프로시저의 한계:
- 가독성이 떨어질 수 있음
- 디버깅이 어려움
- 테스트 자동화가 상대적으로 번거로움
- 복잡한 비즈니스 규칙을 표현하기 어려움
5. 비즈니스 로직의 위치
자주 바뀌고 복잡한 비즈니스 로직은 애플리케이션 계층에 두는 것이 일반적으로 유리하다.
- 테스트가 쉽다
- 디버깅이 쉽다
- 버전 관리가 쉽다
- 배포 전략을 세우기 쉽다
- 수평 확장이 쉽다
- 장애 영향 범위를 조절하기 쉽다
Stored Procedure는 보조 수단으로 보는 것이 좋다:
- 모든 비즈니스 로직을 DB에 넣기보다는
- DB 내부에서 처리하는 것이 명확히 유리한 경우에 제한적으로 사용