Skip to content

NextMarket Stored Procedures 가이드

Database: MySQL 8.x 설계 원칙: StepPay API 응답 구조 기반, NMT_* 테이블 + NMP_* 프로시저 체계 관련 문서: Database_Schema.md 소스 위치: nextmarket-db/ (각 도메인별 __2_procedures/ 디렉토리)


1. 개요

NextMarket API는 ORM을 사용하지 않고 MySQL Stored Procedure를 직접 호출합니다.

1.1 호출 방식

DatabaseService.call() 메서드는 @nextpay/mysql 패턴을 적용하여 OUT 파라미터를 자동 처리합니다.

typescript
// IN 파라미터만 전달 → OUT(@code, @msg) 자동 추가
const result = await this.db.call<T>('NMP_NAME', param1, param2, ...);

// 내부 실행 SQL:
// CALL NMP_NAME(?, ?, @code, @msg); SELECT @code AS resultCode, @msg AS resultMessage;

const data = result[0]?.[0]; // 단일 결과
const list = result[0];      // 목록 결과

동작 원리:

  • IN 파라미터 뒤에 @code, @msg 자동 추가 (SP의 OUT P_RESULT_CODE, P_RESULT_MSG에 바인딩)
  • SELECT @code, @msg 결과로 성공/실패 판단
  • resultCode < 0 이면 DatabaseError throw
  • 반환값에서 OkPacket과 상태 결과 자동 제거 → 순수 SELECT ResultSet 배열만 반환

에러 처리:

typescript
import { DatabaseError } from '../database/database.service';

try {
    const result = await this.db.call('NMP_ORDER_CREATE', ...params);
} catch (error) {
    if (error instanceof DatabaseError) {
        // error.code: -1(이미존재), -2(미존재), -3(변경없음), -1000(예외)
        // error.message: SP에서 반환한 메시지
    }
}

1.2 명명 규칙

접두사용도예시
NMP_SYNC_*StepPay 데이터 동기화 (Upsert)NMP_SYNC_USER
NMP_GET_*단일/목록 조회NMP_GET_USER_BY_USERNAME
NMP_*_CREATE생성NMP_ORDER_CREATE
NMP_*_CREATE_STEPPAYStepPay 매핑 테이블 저장NMP_ORDER_CREATE_STEPPAY
NMP__UPDATE수정NMP_ORDER_UPDATE_STATUS
NMP_*_DELETE삭제NMP_PRODUCT_DELETE
NMP_SYS_*시스템 공통 (열거형, 다국어)NMP_SYS_ENUM_LIST

1.3 에러 처리 패턴

모든 SP에 OUT 파라미터 + EXIT HANDLER 패턴이 적용되어 있습니다:

sql
CREATE PROCEDURE NMP_EXAMPLE(
    IN P_PARAM1 ${TYPE},
    OUT P_RESULT_CODE ${IDX},
    OUT P_RESULT_MSG ${DESC}
)
PROC_BODY: BEGIN

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
        BEGIN
            GET DIAGNOSTICS CONDITION 1
                P_RESULT_CODE = MYSQL_ERRNO,
                P_RESULT_MSG  = MESSAGE_TEXT;
            SET P_RESULT_MSG  = CONCAT('[', P_RESULT_CODE, ']', P_RESULT_MSG);
            SET P_RESULT_CODE = ${EXCEPTION_NO};
        END;

    SET P_RESULT_CODE = 0;
    SET P_RESULT_MSG  = 'succeed';

    -- 비즈니스 로직
END
코드의미
0성공 (Succeed)
-1이미 존재 (Already exist)
-2존재하지 않음 (Not exist) / 파라미터 오류
-3변경 없음 (No change)
-1000SQL 예외 (Exception)

1.4 SP 전체 현황

도메인합계소스 위치
00.common (시스템 공통)8nextmarket-db/00.common/00__2_procedures/
02.users (고객+배송지)9nextmarket-db/02.users/02__2_procedures/
03.products (상품/카테고리/가격)19nextmarket-db/03.products/03__2_procedures/
04.orders (주문+구독)24nextmarket-db/04.orders/04__2_procedures/
06.webhooks (웹훅)2nextmarket-db/06.webhooks/06__2_procedures/

모든 SP에 OUT 파라미터 + 에러 처리 패턴 적용 완료 ✅

1.5 상태값 형식

모든 상태값은 GROUP:VALUE 형식으로 관리됩니다 (NMT_SYS_ENUM_GROUP/VALUE 테이블 기반):

USR_STAT:NORMAL, USR_STAT:DORMANT
ORD_TYPE:ONETIME, ORD_TYPE:RECURRING_INITIAL, ORD_TYPE:RECURRING, ORD_TYPE:PAID, ORD_TYPE:CANCELLED, ORD_TYPE:FAILED
SUB_STAT:PENDING, SUB_STAT:ACTIVE, SUB_STAT:PAUSED, SUB_STAT:CANCELLED, SUB_STAT:CANCEL_PENDING, SUB_STAT:EXPIRED, SUB_STAT:UNPAID, SUB_STAT:FAILED
PRD_STATUS:DRAFT, PRD_STATUS:SALE, PRD_STATUS:SOLDOUT, PRD_STATUS:HIDDEN, PRD_STATUS:DELETED
WHL_STAT:RECEIVED, WHL_STAT:PROCESSED, WHL_STAT:FAILED

API에서는 status.constants.ts의 상수를 사용합니다:

typescript
import { OrderType, SubStatus } from '@common/constants/status.constants';
// OrderType.PAID → 'ORD_TYPE:PAID'

2. 고객(User) 관련

2.1 NMP_SYNC_USER

StepPay 고객 데이터 동기화 (Upsert) 호출: users.service.ts

sql
CREATE PROCEDURE NMP_SYNC_USER(
    IN P_ID bigint,
    IN P_CODE varchar(100),
    IN P_USERNAME varchar(200),
    IN P_NAME varchar(50),
    IN P_EMAIL varchar(200),
    IN P_PHONE varchar(20),
    IN P_STATUS varchar(20),
    IN P_ROLE varchar(20),
    IN P_MARKETING_SMS tinyint,
    IN P_MARKETING_EMAIL tinyint,
    IN P_MARKETING_KAKAO tinyint,
    IN P_ATTRIBUTES JSON,
    IN P_COUNTRY_CODE varchar(20),
    IN P_DORMANT_TS timestamp,
    IN P_PASSWORD_HASH varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    -- EXIT HANDLER ...
    INSERT INTO NMT_USERS (...) VALUES (...)
    ON DUPLICATE KEY UPDATE ...;
    -- STP_USERS 매핑도 SP 내부에서 관리
END

2.2 NMP_GET_USER_BY_USERNAME

USERNAME으로 고객 조회 (로그인 시 사용) 호출: users.service.ts

sql
CREATE PROCEDURE NMP_GET_USER_BY_USERNAME(
    IN P_USERNAME varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_USERS WHERE USR_ID = P_USERNAME;
END

2.3 NMP_GET_USER_BY_IDX

IDX로 고객 조회 호출: users.service.ts, orders.service.ts, subscriptions.service.ts

sql
CREATE PROCEDURE NMP_GET_USER_BY_IDX(
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_USERS WHERE USR_IDX = P_USR_IDX;
END

2.4 NMP_GET_USER_BY_EMAIL

이메일로 고객 조회 호출: users.service.ts

sql
CREATE PROCEDURE NMP_GET_USER_BY_EMAIL(
    IN P_EMAIL varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_USERS WHERE USR_EMAIL = P_EMAIL;
END

2.5 NMP_UPDATE_USER

고객 기본 정보 수정 호출: users.service.ts

sql
CREATE PROCEDURE NMP_UPDATE_USER(
    IN P_USR_IDX int,
    IN P_NAME varchar(50),
    IN P_PHONE varchar(20),
    IN P_EMAIL varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_USERS
    SET USR_NM = COALESCE(P_NAME, USR_NM),
        USR_PHONE = COALESCE(P_PHONE, USR_PHONE),
        USR_EMAIL = COALESCE(P_EMAIL, USR_EMAIL)
    WHERE USR_IDX = P_USR_IDX;
END

2.6 NMP_UPDATE_USER_PASSWORD

비밀번호 변경 호출: users.service.ts

sql
CREATE PROCEDURE NMP_UPDATE_USER_PASSWORD(
    IN P_USR_IDX int,
    IN P_PASSWORD_HASH varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_USERS SET PASSWORD_HASH = P_PASSWORD_HASH WHERE USR_IDX = P_USR_IDX;
END

2.7 NMP_UPDATE_USER_LAST_LOGIN

마지막 로그인 시간 갱신 호출: users.service.ts

sql
CREATE PROCEDURE NMP_UPDATE_USER_LAST_LOGIN(
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_USERS SET LAST_LOGIN_TS = NOW() WHERE USR_IDX = P_USR_IDX;
END

2.8 NMP_SET_USER_DORMANT

회원 휴면 처리 (탈퇴) 호출: users.service.ts

sql
CREATE PROCEDURE NMP_SET_USER_DORMANT(
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_USERS
    SET USR_STATUS = 'USR_STAT:DORMANT', DORMANT_TS = NOW()
    WHERE USR_IDX = P_USR_IDX;
END

2.9 NMP_GET_USER_BY_PHONE

전화번호로 사용자 조회 호출: users.service.ts

sql
CREATE PROCEDURE NMP_GET_USER_BY_PHONE(
    IN P_PHONE varchar(20),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_USERS WHERE USR_PHONE = P_PHONE;
END

2.10 NMP_DELETE_USER

사용자 삭제 (STP_USERS 매핑 + NMT_USERS 데이터 삭제) 호출: users.service.ts

sql
CREATE PROCEDURE NMP_DELETE_USER(
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    DELETE FROM STP_USERS WHERE USR_IDX = P_USR_IDX;
    DELETE FROM NMT_USERS WHERE USR_IDX = P_USR_IDX;
END

3. 상품(Product) 관련

3.1 NMP_SYNC_PRODUCT

StepPay 상품 데이터 동기화 (Upsert)

sql
CREATE PROCEDURE NMP_SYNC_PRODUCT(
    IN P_ID bigint,
    IN P_CODE varchar(100),
    IN P_TYPE varchar(20),
    IN P_STATUS varchar(20),
    IN P_NAME varchar(200),
    IN P_SUB_TITLE varchar(200),
    IN P_DESCRIPTION text,
    IN P_SUMMARY text,
    IN P_SKU varchar(50),
    IN P_NOTICE text,
    IN P_FEATURED_IMAGE_URL varchar(200),
    IN P_IMAGE_URLS JSON,
    IN P_QUANTITY int,
    IN P_ENABLED_DEMO tinyint,
    IN P_DEMO_PERIOD int,
    IN P_DEMO_PERIOD_UNIT varchar(20),
    IN P_USE_COMBINATION tinyint,
    IN P_OPTION_GROUPS JSON,
    IN P_OPTION_COMBINATIONS JSON,
    IN P_COMBINED_PRODUCTS JSON,
    IN P_CATEGORIES JSON,
    IN P_IS_ONETIME_PURCHASABLE tinyint,
    IN P_PRODUCT_ORDER smallint,
    IN P_EVENT_BADGE JSON,
    IN P_USE_WIDGET JSON,
    IN P_COUNTRY_SETTING JSON,
    IN P_AVAILABLE_REGIONS JSON,
    IN P_VENDOR_UUID varchar(50),
    IN P_GROUP_ID bigint,
    IN P_REASON_OF_REJECT text,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_PRODUCTS (...) VALUES (...)
    ON DUPLICATE KEY UPDATE ...;
    -- STP_PRODUCTS 매핑도 SP 내부에서 관리
END

3.2 NMP_GET_PRODUCTS

상품 목록 조회 (페이지네이션, 필터링) 반환: ResultSet 1: 상품 목록, ResultSet 2: TOTAL 카운트 페이지네이션: page=1부터 시작 → (P_PAGE - 1) * P_SIZE

sql
CREATE PROCEDURE NMP_GET_PRODUCTS(
    IN P_TYPE varchar(20),       -- 상품 유형 필터 (NULL: 전체)
    IN P_STATUS varchar(20),     -- 상태 필터 (NULL: 전체)
    IN P_SEARCH varchar(200),    -- 검색어 (상품명)
    IN P_PAGE int,               -- 페이지 번호 (1부터)
    IN P_SIZE int,               -- 페이지 크기
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    DECLARE V_OFFSET int;
    SET V_OFFSET = (P_PAGE - 1) * P_SIZE;

    SELECT * FROM NMT_PRODUCTS
    WHERE (P_TYPE IS NULL OR TYPE = P_TYPE)
      AND (P_STATUS IS NULL OR STATUS = P_STATUS)
      AND (P_SEARCH IS NULL OR PRD_NM LIKE CONCAT('%', P_SEARCH, '%'))
    ORDER BY PRODUCT_ORDER, CREATE_TS DESC
    LIMIT V_OFFSET, P_SIZE;

    SELECT COUNT(*) AS TOTAL FROM NMT_PRODUCTS
    WHERE (P_TYPE IS NULL OR TYPE = P_TYPE)
      AND (P_STATUS IS NULL OR STATUS = P_STATUS)
      AND (P_SEARCH IS NULL OR PRD_NM LIKE CONCAT('%', P_SEARCH, '%'));
END

3.3 NMP_PRODUCT_CREATE

상품 생성 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_CREATE(
    IN P_NAME varchar(200),
    IN P_DESCRIPTION text,
    IN P_TYPE varchar(20),
    IN P_QUANTITY int,
    IN P_FEATURED_IMAGE_URL varchar(200),
    IN P_STATUS varchar(20),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    -- P_NAME 빈값 검증 → resultCode = -2
    INSERT INTO NMT_PRODUCTS (
        PRD_NM, PRD_DESCRIPTION, TYPE, QUANTITY,
        FEATURED_IMAGE_URL, STATUS
    ) VALUES (
        P_NAME, P_DESCRIPTION,
        COALESCE(P_TYPE, 'PRD_TYPE:SOFTWARE'),
        P_QUANTITY, P_FEATURED_IMAGE_URL,
        COALESCE(P_STATUS, 'PRD_STATUS:DRAFT')
    );
    SELECT LAST_INSERT_ID() AS PRD_IDX;
END

3.4 NMP_PRODUCT_GET_DETAIL

상품 상세 조회 (가격플랜 포함) 호출: products.service.ts 반환: ResultSet 1: 상품 기본정보, ResultSet 2: 가격플랜

sql
CREATE PROCEDURE NMP_PRODUCT_GET_DETAIL(
    IN P_PRD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_PRODUCTS WHERE PRD_IDX = P_PRD_IDX;

    SELECT * FROM NMT_PRICES WHERE PRD_IDX = P_PRD_IDX
    ORDER BY SORT_ORDER;
END

3.5 NMP_PRODUCT_GET_BY_IDX

IDX로 상품 기본 정보 조회 호출: products.service.ts, subscriptions.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_GET_BY_IDX(
    IN P_PRD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_PRODUCTS WHERE PRD_IDX = P_PRD_IDX;
END

3.6 NMP_PRODUCT_GET_BY_CODE

StepPay 상품 코드로 조회 (STP_PRODUCTS 매핑 테이블 JOIN) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_GET_BY_CODE(
    IN P_CODE varchar(100),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT p.* FROM NMT_PRODUCTS p
    INNER JOIN STP_PRODUCTS sp ON sp.PRD_IDX = p.PRD_IDX
    WHERE sp.STP_PRD_CODE = P_CODE;
END

3.7 NMP_PRODUCT_GET_FOR_ORDER

주문 가능 상품 정보 조회 (재고 및 판매 상태 확인 포함) 호출: orders.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_GET_FOR_ORDER(
    IN P_PRD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_PRODUCTS
    WHERE PRD_IDX = P_PRD_IDX
      AND STATUS = 'PRD_STATUS:SALE'
      AND (QUANTITY IS NULL OR QUANTITY > 0);
END

3.8 NMP_PRODUCT_UPDATE

상품 정보 수정 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_UPDATE(
    IN P_PRD_IDX int,
    IN P_NAME varchar(200),
    IN P_DESCRIPTION text,
    IN P_QUANTITY int,
    IN P_FEATURED_IMAGE_URL varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    -- NOT EXISTS 검증 → resultCode = -2
    UPDATE NMT_PRODUCTS
    SET PRD_NM = COALESCE(P_NAME, PRD_NM),
        PRD_DESCRIPTION = COALESCE(P_DESCRIPTION, PRD_DESCRIPTION),
        QUANTITY = COALESCE(P_QUANTITY, QUANTITY),
        FEATURED_IMAGE_URL = COALESCE(P_FEATURED_IMAGE_URL, FEATURED_IMAGE_URL)
    WHERE PRD_IDX = P_PRD_IDX;
END

3.9 NMP_PRODUCT_UPDATE_STATUS

상품 상태 변경 (DRAFT/SALE/SOLDOUT/HIDDEN) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_UPDATE_STATUS(
    IN P_PRD_IDX int,
    IN P_STATUS varchar(20),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS SET STATUS = P_STATUS WHERE PRD_IDX = P_PRD_IDX;
END

3.10 NMP_PRODUCT_DELETE

상품 삭제 (소프트 삭제) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_DELETE(
    IN P_PRD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS SET STATUS = 'PRD_STATUS:DELETED' WHERE PRD_IDX = P_PRD_IDX;
END

3.11 NMP_PRODUCT_CREATE_STEPPAY

상품에 StepPay 연동 정보 저장 (STP_PRODUCTS 매핑 테이블) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_CREATE_STEPPAY(
    IN P_PRD_IDX int,
    IN P_STP_PRD_IDX bigint,
    IN P_STP_PRD_CODE varchar(100),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO STP_PRODUCTS (PRD_IDX, STP_PRD_IDX, STP_PRD_CODE)
    VALUES (P_PRD_IDX, P_STP_PRD_IDX, P_STP_PRD_CODE)
    ON DUPLICATE KEY UPDATE
        STP_PRD_IDX = P_STP_PRD_IDX,
        STP_PRD_CODE = P_STP_PRD_CODE;
END

3.12 NMP_PRODUCT_OPTION_CREATE

상품 옵션 그룹 생성 (OPTION_GROUPS JSON 배열에 추가) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_OPTION_CREATE(
    IN P_PRD_IDX int,
    IN P_OPTION_NAME varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS
    SET OPTION_GROUPS = JSON_ARRAY_APPEND(
        COALESCE(OPTION_GROUPS, JSON_ARRAY()),
        '$',
        JSON_OBJECT('name', P_OPTION_NAME, 'values', JSON_ARRAY())
    )
    WHERE PRD_IDX = P_PRD_IDX;
END

3.13 NMP_PRODUCT_OPTION_VALUE_CREATE

옵션 값 생성 (OPTION_GROUPS JSON 내부의 values 배열에 추가) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_OPTION_VALUE_CREATE(
    IN P_PRD_IDX int,
    IN P_OPTION_INDEX int,           -- OPTION_GROUPS 배열 내 인덱스 (0부터)
    IN P_VALUE_NAME varchar(200),
    IN P_ADDITIONAL_PRICE numeric(12,3),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    DECLARE V_PATH varchar(200);
    SET V_PATH = CONCAT('$[', P_OPTION_INDEX, '].values');

    UPDATE NMT_PRODUCTS
    SET OPTION_GROUPS = JSON_ARRAY_APPEND(
        OPTION_GROUPS, V_PATH,
        JSON_OBJECT('name', P_VALUE_NAME, 'additionalPrice', P_ADDITIONAL_PRICE)
    )
    WHERE PRD_IDX = P_PRD_IDX;
END

3.14 NMP_PRODUCT_IMAGE_CREATE

상품 이미지 추가 (IMAGE_URLS JSON 배열에 추가) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRODUCT_IMAGE_CREATE(
    IN P_PRD_IDX int,
    IN P_IMAGE_URL varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS
    SET IMAGE_URLS = JSON_ARRAY_APPEND(
        COALESCE(IMAGE_URLS, JSON_ARRAY()), '$', P_IMAGE_URL
    )
    WHERE PRD_IDX = P_PRD_IDX;
END

4. 카테고리(Category) 관련

4.1 NMP_CATEGORY_LIST

카테고리 목록 조회 (NMT_PRODUCTS의 CATEGORIES JSON에서 고유 카테고리 추출) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_CATEGORY_LIST(
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT DISTINCT JT.CATEGORY_ID
    FROM NMT_PRODUCTS P,
         JSON_TABLE(P.CATEGORIES, '$[*]' COLUMNS (
             CATEGORY_ID bigint PATH '$'
         )) AS JT
    WHERE P.STATUS != 'PRD_STATUS:DELETED'
    ORDER BY JT.CATEGORY_ID;
END

4.2 NMP_CATEGORY_GET_BY_IDX

카테고리별 상품 목록 조회 호출: products.service.ts

sql
CREATE PROCEDURE NMP_CATEGORY_GET_BY_IDX(
    IN P_CATEGORY_ID bigint,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_PRODUCTS
    WHERE JSON_CONTAINS(CATEGORIES, CAST(P_CATEGORY_ID AS CHAR))
      AND STATUS != 'PRD_STATUS:DELETED'
    ORDER BY PRODUCT_ORDER, CREATE_TS DESC;
END

5. 가격 플랜(Price) 관련

5.1 NMP_PRICE_PLAN_CREATE

가격 플랜 생성 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRICE_PLAN_CREATE(
    IN P_PRD_IDX int,
    IN P_NAME varchar(200),
    IN P_PRICE numeric(12,3),
    IN P_RECURRING_INTERVAL varchar(20),     -- DAY/WEEK/MONTH/YEAR
    IN P_RECURRING_INTERVAL_COUNT int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_PRICES (
        PRD_IDX, TYPE, PRICE, PRC_NM, RECURRING
    ) VALUES (
        P_PRD_IDX,
        IF(P_RECURRING_INTERVAL IS NULL, 'PRC_TYPE:ONE_TIME', 'PRC_TYPE:FLAT'),
        P_PRICE, P_NAME,
        IF(P_RECURRING_INTERVAL IS NOT NULL,
           JSON_OBJECT('interval', P_RECURRING_INTERVAL, 'intervalCount', P_RECURRING_INTERVAL_COUNT),
           NULL)
    );
    SELECT LAST_INSERT_ID() AS PRC_IDX;
END

5.2 NMP_PRICE_GET_BY_IDX

가격 플랜 단건 조회 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_PRICE_GET_BY_IDX(
    IN P_PRC_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_PRICES WHERE PRC_IDX = P_PRC_IDX;
END

5.3 NMP_PRICE_CREATE_STEPPAY

가격 플랜에 StepPay 연동 정보 저장 (STP_PRICES 매핑 테이블) 호출: products.service.ts

sql
CREATE PROCEDURE NMP_PRICE_CREATE_STEPPAY(
    IN P_PRC_IDX int,
    IN P_STP_PRC_IDX bigint,
    IN P_STP_PRC_CODE varchar(100),
    IN P_STP_PRD_IDX bigint,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO STP_PRICES (PRC_IDX, STP_PRC_IDX, STP_PRC_CODE, STP_PRD_IDX)
    VALUES (P_PRC_IDX, P_STP_PRC_IDX, P_STP_PRC_CODE, P_STP_PRD_IDX)
    ON DUPLICATE KEY UPDATE
        STP_PRC_IDX = P_STP_PRC_IDX,
        STP_PRC_CODE = P_STP_PRC_CODE,
        STP_PRD_IDX = P_STP_PRD_IDX;
END

5.4 NMP_SYNC_PRICE_PLAN

StepPay 가격 플랜 데이터 동기화 (Upsert) NMT_PRICES + STP_PRICES 동시 저장 호출: sync.service.ts

sql
CREATE PROCEDURE NMP_SYNC_PRICE_PLAN(
    IN P_STP_PRC_IDX bigint,
    IN P_STP_PRC_CODE varchar(100),
    IN P_STP_PRD_IDX bigint,
    IN P_TYPE varchar(30),
    IN P_PRICE numeric(12,3),
    IN P_NAME varchar(200),
    IN P_RECURRING JSON,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    -- 1. STP_PRODUCTS에서 로컬 상품 IDX 조회
    -- 2. STP_PRICES에서 기존 매핑 확인
    -- 3A. 기존 → NMT_PRICES UPDATE
    -- 3B. 신규 → NMT_PRICES INSERT + STP_PRICES INSERT
END

6. 주문(Order) 관련

6.1 NMP_SYNC_ORDER

StepPay 주문 데이터 동기화 (Upsert)

sql
CREATE PROCEDURE NMP_SYNC_ORDER(
    IN P_ID bigint,
    IN P_CODE varchar(100),
    IN P_USER_IDX int,
    IN P_USER_NAME varchar(50),
    IN P_TYPE varchar(20),
    IN P_PRODUCT_NAME varchar(200),
    IN P_PRICE numeric(12,3),
    IN P_PAID_AMOUNT numeric(12,3),
    IN P_CURRENCY varchar(20),
    IN P_PAYMENT_METHOD varchar(50),
    IN P_PAYMENT_DATE timestamp,
    IN P_SHIPPING JSON,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_ORDERS (
        UNIX_ORD_DT, ORD_DT, ORD_TM,
        USR_IDX, USR_NM,
        ORD_TYPE, PRODUCT_NAME, PRICE, PAID_AMOUNT,
        CURRENCY, PAYMENT_METHOD, PAYMENT_DATE, SHIPPING
    ) VALUES (
        UNIX_TIMESTAMP(), DATE_FORMAT(NOW(), '%Y%m%d'), DATE_FORMAT(NOW(), '%H%i%S'),
        P_USER_IDX, P_USER_NAME,
        P_TYPE, P_PRODUCT_NAME, P_PRICE, P_PAID_AMOUNT,
        P_CURRENCY, P_PAYMENT_METHOD, P_PAYMENT_DATE, P_SHIPPING
    )
    ON DUPLICATE KEY UPDATE ...;
    -- STP_ORDERS 매핑도 SP 내부에서 관리
END

참고: NMT_ORDERS는 파티션 테이블이므로 UNIX_ORD_DT, ORD_DT, ORD_TM 반드시 설정 필요


6.2 NMP_ORDER_CREATE

주문 생성 호출: orders.service.ts

sql
CREATE PROCEDURE NMP_ORDER_CREATE(
    IN P_USR_IDX int,
    IN P_TYPE varchar(20),             -- ORD_TYPE:ONETIME 등
    IN P_PRODUCT_NAME varchar(200),
    IN P_PRICE numeric(12,3),
    IN P_TOTAL_AMOUNT numeric(12,3),
    IN P_RECIPIENT_NAME varchar(50),
    IN P_PHONE varchar(20),
    IN P_ZIPCODE varchar(10),
    IN P_ADDRESS varchar(200),
    IN P_ADDRESS_DETAIL varchar(200),
    IN P_MEMO varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_ORDERS (
        UNIX_ORD_DT, ORD_DT, ORD_TM,
        USR_IDX, USR_NM,
        ORD_TYPE, PRODUCT_NAME, PRICE, PAID_AMOUNT, CURRENCY,
        SHIPPING
    ) VALUES (
        UNIX_TIMESTAMP(), DATE_FORMAT(NOW(), '%Y%m%d'), DATE_FORMAT(NOW(), '%H%i%S'),
        P_USR_IDX, '',
        COALESCE(P_TYPE, 'ORD_TYPE:ONETIME'),
        P_PRODUCT_NAME, P_PRICE, P_TOTAL_AMOUNT, 'CURRENCY:KRW',
        JSON_OBJECT(
            'recipientName', P_RECIPIENT_NAME,
            'phone', P_PHONE,
            'zipCode', P_ZIPCODE,
            'address', P_ADDRESS,
            'addressDetail', P_ADDRESS_DETAIL,
            'memo', P_MEMO
        )
    );
    SELECT LAST_INSERT_ID() AS ORD_IDX;
END

6.3 NMP_ORDER_ITEM_CREATE

주문 항목 생성 호출: orders.service.ts

sql
CREATE PROCEDURE NMP_ORDER_ITEM_CREATE(
    IN P_ORD_IDX int,
    IN P_PRD_IDX int,
    IN P_PRC_IDX int,
    IN P_PRD_NM varchar(200),
    IN P_PRC_NM varchar(200),
    IN P_QUANTITY int,
    IN P_UNIT_PRICE numeric(12,3),
    IN P_TOTAL_PRICE numeric(12,3),
    IN P_SELECTED_OPTIONS JSON,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_ORDER_ITEMS (
        ORD_IDX, PRD_IDX, PRC_IDX,
        PRD_NM, PRC_NM, QUANTITY, UNIT_PRICE, TOTAL_PRICE,
        SELECTED_OPTIONS, UNIX_CREATE_DT
    ) VALUES (
        P_ORD_IDX, P_PRD_IDX, P_PRC_IDX,
        P_PRD_NM, P_PRC_NM, P_QUANTITY, P_UNIT_PRICE, P_TOTAL_PRICE,
        P_SELECTED_OPTIONS, UNIX_TIMESTAMP()
    );
    SELECT LAST_INSERT_ID() AS ORI_IDX;
END

참고: NMT_ORDER_ITEMS는 파티션 테이블이므로 UNIX_CREATE_DT 반드시 설정 필요


6.4 NMP_ORDER_CREATE_STEPPAY

주문에 StepPay 연동 정보 저장 (STP_ORDERS 매핑 테이블) 호출: orders.service.ts

sql
CREATE PROCEDURE NMP_ORDER_CREATE_STEPPAY(
    IN P_ORD_IDX int,
    IN P_STP_ORD_IDX bigint,
    IN P_STP_ORD_CODE varchar(100),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO STP_ORDERS (ORD_IDX, STP_ORD_IDX, STP_ORD_CODE)
    VALUES (P_ORD_IDX, P_STP_ORD_IDX, P_STP_ORD_CODE)
    ON DUPLICATE KEY UPDATE
        STP_ORD_IDX = P_STP_ORD_IDX,
        STP_ORD_CODE = P_STP_ORD_CODE;
END

6.5 NMP_ORDER_UPDATE_STATUS

주문 상태 업데이트 호출: orders.service.ts, webhooks.service.ts

sql
CREATE PROCEDURE NMP_ORDER_UPDATE_STATUS(
    IN P_ORD_IDX int,
    IN P_STATUS varchar(20),           -- ORD_TYPE:PAID, ORD_TYPE:CANCELLED 등
    IN P_PAYMENT_METHOD varchar(50),
    IN P_PAYMENT_DATE timestamp,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    -- 존재 여부 확인
    IF NOT EXISTS (SELECT 1 FROM NMT_ORDERS WHERE ORD_IDX = P_ORD_IDX) THEN
        SET P_RESULT_CODE = -2;
        SET P_RESULT_MSG = 'Order not found';
        LEAVE PROC_BODY;
    END IF;

    UPDATE NMT_ORDERS
    SET ORD_TYPE = P_STATUS,
        PAYMENT_METHOD = COALESCE(P_PAYMENT_METHOD, PAYMENT_METHOD),
        PAYMENT_DATE = COALESCE(P_PAYMENT_DATE, PAYMENT_DATE)
    WHERE ORD_IDX = P_ORD_IDX;
END

6.6 NMP_ORDER_LIST_BY_USER

사용자별 주문 목록 조회 (페이징, STP_ORDERS LEFT JOIN) 호출: orders.service.ts 반환: ResultSet 1: 주문 목록, ResultSet 2: TOTAL 페이지네이션: page=1부터 시작 → (P_PAGE - 1) * P_SIZE

sql
CREATE PROCEDURE NMP_ORDER_LIST_BY_USER(
    IN P_USR_IDX int,
    IN P_STATUS varchar(20),        -- NULL이면 전체
    IN P_PAGE int,
    IN P_SIZE int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    DECLARE V_OFFSET int;
    SET V_OFFSET = (P_PAGE - 1) * P_SIZE;

    SELECT o.*, so.STP_ORD_IDX, so.STP_ORD_CODE
    FROM NMT_ORDERS o
    LEFT JOIN STP_ORDERS so ON so.ORD_IDX = o.ORD_IDX
    WHERE o.USR_IDX = P_USR_IDX
      AND (P_STATUS IS NULL OR o.ORD_TYPE = P_STATUS)
    ORDER BY o.CREATE_TS DESC
    LIMIT V_OFFSET, P_SIZE;

    SELECT COUNT(*) AS TOTAL FROM NMT_ORDERS
    WHERE USR_IDX = P_USR_IDX
      AND (P_STATUS IS NULL OR ORD_TYPE = P_STATUS);
END

6.7 NMP_ORDER_GET_BY_IDX

주문 상세 조회 (항목 포함, STP_ORDERS LEFT JOIN) 호출: orders.service.ts 반환: ResultSet 1: 주문 정보, ResultSet 2: 주문 항목

sql
CREATE PROCEDURE NMP_ORDER_GET_BY_IDX(
    IN P_ORD_IDX int,
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT o.*, so.STP_ORD_IDX, so.STP_ORD_CODE
    FROM NMT_ORDERS o
    LEFT JOIN STP_ORDERS so ON so.ORD_IDX = o.ORD_IDX
    WHERE o.ORD_IDX = P_ORD_IDX AND o.USR_IDX = P_USR_IDX;

    SELECT * FROM NMT_ORDER_ITEMS
    WHERE ORD_IDX = P_ORD_IDX;
END

6.8 NMP_ORDER_CANCEL

주문 취소 호출: orders.service.ts

sql
CREATE PROCEDURE NMP_ORDER_CANCEL(
    IN P_ORD_IDX int,
    IN P_REASON varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_ORDERS
    SET ORD_TYPE = 'ORD_TYPE:CANCELLED'
    WHERE ORD_IDX = P_ORD_IDX;
END

6.9 NMP_ORDER_DECREASE_STOCK

결제 완료 시 재고 차감 호출: webhooks.service.ts

sql
CREATE PROCEDURE NMP_ORDER_DECREASE_STOCK(
    IN P_ORD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS P
    INNER JOIN NMT_ORDER_ITEMS OI ON OI.PRD_IDX = P.PRD_IDX
    SET P.QUANTITY = P.QUANTITY - OI.QUANTITY
    WHERE OI.ORD_IDX = P_ORD_IDX
      AND P.QUANTITY IS NOT NULL;
END

6.10 NMP_ORDER_RESTORE_STOCK

주문 취소 시 재고 복원 호출: orders.service.ts, webhooks.service.ts

sql
CREATE PROCEDURE NMP_ORDER_RESTORE_STOCK(
    IN P_ORD_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_PRODUCTS P
    INNER JOIN NMT_ORDER_ITEMS OI ON OI.PRD_IDX = P.PRD_IDX
    SET P.QUANTITY = P.QUANTITY + OI.QUANTITY
    WHERE OI.ORD_IDX = P_ORD_IDX
      AND P.QUANTITY IS NOT NULL;
END

6.11 NMP_ORDER_GET_BY_STP_CODE

StepPay 주문 코드로 로컬 주문 IDX 조회 호출: webhooks.service.ts 용도: 웹훅에서 StepPay 코드로 로컬 주문 IDX를 찾을 때 사용

sql
CREATE PROCEDURE NMP_ORDER_GET_BY_STP_CODE(
    IN P_STP_ORD_CODE varchar(100),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT so.ORD_IDX, so.STP_ORD_IDX, so.STP_ORD_CODE
    FROM STP_ORDERS so
    WHERE so.STP_ORD_CODE = P_STP_ORD_CODE;
END

7. 구독(Subscription) 관련

7.1 NMP_SYNC_SUBSCRIPTION

StepPay 구독 데이터 동기화 (Upsert) STP_SUBSCRIPTIONS 매핑 테이블도 관리

sql
CREATE PROCEDURE NMP_SYNC_SUBSCRIPTION(
    IN P_ID bigint,
    IN P_CODE varchar(100),
    IN P_USER_IDX int,
    IN P_STP_USER_IDX bigint,
    IN P_USER_NAME varchar(50),
    IN P_PRODUCT_NAME varchar(200),
    IN P_STATUS varchar(20),
    IN P_ITEMS JSON,
    IN P_RECURRING JSON,
    IN P_CURRENCY varchar(20),
    IN P_NEXT_PAYMENT_DATE_TIME timestamp,
    IN P_PAYMENT_METHOD JSON,
    IN P_START_DATE_TIME timestamp,
    IN P_END_DATE_TIME timestamp,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_SUBSCRIPTIONS (
        UNIX_CREATE_DT, USR_IDX, USR_NM, PRD_NM, SUB_STATUS,
        ITEMS, RECURRING, CURRENCY, NEXT_PAYMENT_DATE_TIME, PAYMENT_METHOD,
        START_DATE_TIME, END_DATE_TIME
    ) VALUES (
        UNIX_TIMESTAMP(), P_USER_IDX, P_USER_NAME, P_PRODUCT_NAME, P_STATUS,
        P_ITEMS, P_RECURRING, P_CURRENCY, P_NEXT_PAYMENT_DATE_TIME, P_PAYMENT_METHOD,
        P_START_DATE_TIME, P_END_DATE_TIME
    )
    ON DUPLICATE KEY UPDATE ...;
    -- STP_SUBSCRIPTIONS 매핑 테이블도 SP 내부에서 관리
END

참고: NMT_SUBSCRIPTIONS는 파티션 테이블이므로 UNIX_CREATE_DT 반드시 설정 필요


7.2 NMP_GET_USER_SUBSCRIPTIONS

고객별 구독 목록 조회 (STP_SUBSCRIPTIONS LEFT JOIN) 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_GET_USER_SUBSCRIPTIONS(
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT s.*, ss.STP_SUB_IDX, ss.STP_SUB_CODE
    FROM NMT_SUBSCRIPTIONS s
    LEFT JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    WHERE s.USR_IDX = P_USR_IDX
    ORDER BY s.CREATE_TS DESC;
END

7.3 NMP_SUBSCRIPTION_CREATE

구독 생성 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_CREATE(
    IN P_USR_IDX int,
    IN P_PRD_NM varchar(200),
    IN P_RECURRING_INTERVAL varchar(20),     -- DAY/WEEK/MONTH/YEAR
    IN P_RECURRING_INTERVAL_COUNT int,
    IN P_CURRENCY varchar(20),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_SUBSCRIPTIONS (
        UNIX_CREATE_DT, USR_IDX,
        PRD_NM, SUB_STATUS, CURRENCY, RECURRING
    ) VALUES (
        UNIX_TIMESTAMP(), P_USR_IDX,
        P_PRD_NM, 'SUB_STAT:PENDING',
        COALESCE(P_CURRENCY, 'CURRENCY:KRW'),
        JSON_OBJECT(
            'interval', P_RECURRING_INTERVAL,
            'intervalCount', COALESCE(P_RECURRING_INTERVAL_COUNT, 1)
        )
    );
    SELECT LAST_INSERT_ID() AS SUB_IDX;
END

7.4 NMP_SUBSCRIPTION_CREATE_STEPPAY

구독에 StepPay 연동 정보 저장 (STP_SUBSCRIPTIONS 매핑 테이블) 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_CREATE_STEPPAY(
    IN P_SUB_IDX int,
    IN P_STP_SUB_IDX bigint,
    IN P_STP_SUB_CODE varchar(100),
    IN P_STP_USR_IDX bigint,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO STP_SUBSCRIPTIONS (SUB_IDX, STP_SUB_IDX, STP_SUB_CODE, STP_USR_IDX)
    VALUES (P_SUB_IDX, P_STP_SUB_IDX, P_STP_SUB_CODE, P_STP_USR_IDX)
    ON DUPLICATE KEY UPDATE
        STP_SUB_IDX = P_STP_SUB_IDX,
        STP_SUB_CODE = P_STP_SUB_CODE,
        STP_USR_IDX = P_STP_USR_IDX;
END

7.5 NMP_SUBSCRIPTION_CHECK_ACTIVE

기존 활성 구독 확인 (중복 구독 방지, STP_SUBSCRIPTIONS LEFT JOIN) 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_CHECK_ACTIVE(
    IN P_USR_IDX int,
    IN P_PRD_NM varchar(200),           -- 상품명으로 매칭
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT s.*, ss.STP_SUB_IDX, ss.STP_SUB_CODE
    FROM NMT_SUBSCRIPTIONS s
    LEFT JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    WHERE s.USR_IDX = P_USR_IDX
      AND s.SUB_STATUS = 'SUB_STAT:ACTIVE'
      AND (P_PRD_NM IS NULL OR s.PRD_NM = P_PRD_NM);
END

7.6 NMP_SUBSCRIPTION_LIST_BY_USER

사용자별 구독 목록 조회 (페이징, STP_SUBSCRIPTIONS LEFT JOIN) 호출: subscriptions.service.ts 반환: ResultSet 1: 구독 목록, ResultSet 2: TOTAL 페이지네이션: page=1부터 시작 → (P_PAGE - 1) * P_SIZE

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_LIST_BY_USER(
    IN P_USR_IDX int,
    IN P_STATUS varchar(20),        -- NULL이면 전체
    IN P_PAGE int,
    IN P_SIZE int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    DECLARE V_OFFSET int;
    SET V_OFFSET = (P_PAGE - 1) * P_SIZE;

    SELECT s.*, ss.STP_SUB_IDX, ss.STP_SUB_CODE
    FROM NMT_SUBSCRIPTIONS s
    LEFT JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    WHERE s.USR_IDX = P_USR_IDX
      AND (P_STATUS IS NULL OR s.SUB_STATUS = P_STATUS)
    ORDER BY s.CREATE_TS DESC
    LIMIT V_OFFSET, P_SIZE;

    SELECT COUNT(*) AS TOTAL FROM NMT_SUBSCRIPTIONS
    WHERE USR_IDX = P_USR_IDX
      AND (P_STATUS IS NULL OR SUB_STATUS = P_STATUS);
END

7.7 NMP_SUBSCRIPTION_GET_BY_IDX

구독 상세 조회 (STP_SUBSCRIPTIONS LEFT JOIN) 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_GET_BY_IDX(
    IN P_SUB_IDX int,
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT s.*, ss.STP_SUB_IDX, ss.STP_SUB_CODE
    FROM NMT_SUBSCRIPTIONS s
    LEFT JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    WHERE s.SUB_IDX = P_SUB_IDX AND s.USR_IDX = P_USR_IDX;
END

7.8 NMP_SUBSCRIPTION_UPDATE_STATUS

구독 상태 변경 (STP_SUBSCRIPTIONS INNER JOIN) 호출: webhooks.service.ts, subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_UPDATE_STATUS(
    IN P_STP_SUB_IDX bigint,
    IN P_STATUS varchar(20),        -- SUB_STAT:ACTIVE, PAUSED, CANCELLED, EXPIRED, UNPAID
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS s
    INNER JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    SET s.SUB_STATUS = P_STATUS
    WHERE ss.STP_SUB_IDX = P_STP_SUB_IDX;
END

7.9 NMP_SUBSCRIPTION_ACTIVATE

구독 활성화 (결제 완료 웹훅 수신 시, STP_SUBSCRIPTIONS INNER JOIN) 호출: webhooks.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_ACTIVATE(
    IN P_STP_SUB_IDX bigint,
    IN P_START_DATE timestamp,
    IN P_END_DATE timestamp,
    IN P_NEXT_BILLING_DATE timestamp,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS s
    INNER JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    SET s.SUB_STATUS = 'SUB_STAT:ACTIVE',
        s.START_DATE_TIME = P_START_DATE,
        s.END_DATE_TIME = P_END_DATE,
        s.NEXT_PAYMENT_DATE_TIME = P_NEXT_BILLING_DATE
    WHERE ss.STP_SUB_IDX = P_STP_SUB_IDX;
END

7.10 NMP_SUBSCRIPTION_RENEW

구독 갱신 (정기 결제 웹훅 수신 시, STP_SUBSCRIPTIONS INNER JOIN) 호출: webhooks.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_RENEW(
    IN P_STP_SUB_IDX bigint,
    IN P_START_DATE timestamp,
    IN P_END_DATE timestamp,
    IN P_NEXT_BILLING_DATE timestamp,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS s
    INNER JOIN STP_SUBSCRIPTIONS ss ON ss.SUB_IDX = s.SUB_IDX
    SET s.START_DATE_TIME = P_START_DATE,
        s.END_DATE_TIME = P_END_DATE,
        s.NEXT_PAYMENT_DATE_TIME = P_NEXT_BILLING_DATE
    WHERE ss.STP_SUB_IDX = P_STP_SUB_IDX;
END

7.11 NMP_SUBSCRIPTION_CANCEL

구독 해지 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_CANCEL(
    IN P_SUB_IDX int,
    IN P_REASON varchar(200),
    IN P_IMMEDIATELY tinyint,       -- 1: 즉시 해지, 0: 기간 만료 후 해지
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS
    SET SUB_STATUS = IF(P_IMMEDIATELY = 1, 'SUB_STAT:CANCELLED', 'SUB_STAT:CANCEL_PENDING')
    WHERE SUB_IDX = P_SUB_IDX;
END

7.12 NMP_SUBSCRIPTION_PAUSE

구독 일시정지 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_PAUSE(
    IN P_SUB_IDX int,
    IN P_REASON varchar(200),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS
    SET SUB_STATUS = 'SUB_STAT:PAUSED'
    WHERE SUB_IDX = P_SUB_IDX;
END

7.13 NMP_SUBSCRIPTION_RESUME

구독 재개 호출: subscriptions.service.ts

sql
CREATE PROCEDURE NMP_SUBSCRIPTION_RESUME(
    IN P_SUB_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_SUBSCRIPTIONS
    SET SUB_STATUS = 'SUB_STAT:ACTIVE'
    WHERE SUB_IDX = P_SUB_IDX;
END

8. 배송지(Address) 관련

8.1 NMP_ADDRESS_GET_BY_IDX

배송지 조회 호출: orders.service.ts, subscriptions.service.ts

sql
CREATE PROCEDURE NMP_ADDRESS_GET_BY_IDX(
    IN P_SHP_IDX int,
    IN P_USR_IDX int,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    SELECT * FROM NMT_SHIPPING
    WHERE SHP_IDX = P_SHP_IDX AND USR_IDX = P_USR_IDX;
END

9. 웹훅 로그(Webhook Log) 관련

9.1 NMP_WEBHOOK_LOG_CREATE

웹훅 수신 로그 생성 호출: webhooks.service.ts

sql
CREATE PROCEDURE NMP_WEBHOOK_LOG_CREATE(
    IN P_EVENT_TYPE varchar(200),
    IN P_PAYLOAD JSON,
    IN P_SIGNATURE varchar(200),
    IN P_IP_ADDRESS varchar(20),
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    INSERT INTO NMT_WEBHOOK_LOGS (
        EVENT_TYPE, PAYLOAD, SIGNATURE, WHL_STATUS, IP_ADDRESS
    ) VALUES (
        P_EVENT_TYPE, P_PAYLOAD, P_SIGNATURE, 'WHL_STAT:RECEIVED', P_IP_ADDRESS
    );
    SELECT LAST_INSERT_ID() AS WHL_IDX;
END

9.2 NMP_WEBHOOK_LOG_UPDATE

웹훅 처리 결과 업데이트 호출: webhooks.service.ts

sql
CREATE PROCEDURE NMP_WEBHOOK_LOG_UPDATE(
    IN P_WHL_IDX int,
    IN P_STATUS varchar(20),        -- WHL_STAT:PROCESSED, WHL_STAT:FAILED
    IN P_ERROR_MESSAGE text,
    OUT P_RESULT_CODE int,
    OUT P_RESULT_MSG text
)
PROC_BODY: BEGIN
    UPDATE NMT_WEBHOOK_LOGS
    SET WHL_STATUS = P_STATUS,
        ERROR_MESSAGE = P_ERROR_MESSAGE,
        PROCESSED_TS = NOW()
    WHERE WHL_IDX = P_WHL_IDX;
END

10. 시스템 공통 (00.common)

시스템 공통 SP는 열거형 변수(Enum), 다국어 문자열, 시스템 메시지를 관리합니다. 모든 공통 SP는 구현 완료 상태입니다.

10.1 NMP_SYS_ENUM_LIST

열거형 시스템 변수 값 목록 조회

파라미터IN/OUT타입설명
P_GRP_CDINvarchar(20)그룹 코드
P_LANG_CDINvarchar(20)언어 코드
P_RESULT_CODEOUTint결과 코드
P_RESULT_MSGOUTtext결과 메시지

10.2 NMP_SYS_ENUM_REGIST

시스템 열거형 변수 값 등록

파라미터IN/OUT타입설명
P_GRP_CDINvarchar(20)그룹 코드
P_DTL_CDINvarchar(20)세부 코드 (빈 문자열이면 그룹만 등록)
P_LANG_CDINvarchar(20)언어 코드
P_SYS_TXTINvarchar(200)표시 문자열
P_DEF_YNINchar(1)기본 여부
P_CNF_YNINchar(1)승인 여부
P_RESULT_CODEOUTint결과 코드
P_RESULT_MSGOUTtext결과 메시지

10.3 NMP_SYS_ENUM_UPDATE

시스템 열거형 변수 값 수정

10.4 NMP_SYS_TEXT_LIST

시스템 언어별 문자열 전체 목록 조회 반환: 4개 ResultSet (키 VERSION, 값 VERSION, 키 목록, 값 목록)

10.5 NMP_SYS_TEXT_REGIST

언어별 시스템 문자열 등록 (키 + 값 동시 생성) TXT_CD 자동 생성 (YYYYMM00001 형식)

10.6 NMP_SYS_TEXT_ADD_REGIST

기존 문자열 키에 언어별 값 추가 등록

10.7 NMP_SYS_TEXT_UPDATE

언어별 시스템 문자열 값 수정

10.8 NMP_SYS_MESSAGE_REGIST

언어별 시스템 메시지 등록


11. 동기화 전략

11.1 데이터 흐름

StepPay (마스터) ──웹훅──> NextMarket API ──NMP_SYNC_*──> MySQL (미러)

                                └── 조회 요청 시: 로컬 DB 먼저 → 없으면 StepPay API

11.2 STP_* 매핑 테이블 경유 패턴

StepPay와 로컬 DB 간의 ID 매핑은 STP_ 매핑 테이블*을 통해 관리됩니다. 이전에는 NMT_* 테이블에 직접 STP_* 컬럼을 두었지만, 현재는 별도의 매핑 테이블로 분리되었습니다.

[로컬 테이블]                    [매핑 테이블]                    [StepPay]
NMT_PRODUCTS (PRD_IDX) ←→ STP_PRODUCTS (PRD_IDX, STP_PRD_IDX, STP_PRD_CODE)
NMT_PRICES   (PRC_IDX) ←→ STP_PRICES   (PRC_IDX, STP_PRC_IDX, STP_PRC_CODE)
NMT_ORDERS   (ORD_IDX) ←→ STP_ORDERS   (ORD_IDX, STP_ORD_IDX, STP_ORD_CODE)
NMT_SUBSCRIPTIONS (SUB_IDX) ←→ STP_SUBSCRIPTIONS (SUB_IDX, STP_SUB_IDX, STP_SUB_CODE)
NMT_USERS    (USR_IDX) ←→ STP_USERS    (USR_IDX, STP_USR_IDX, STP_USR_CODE)

매핑 저장 SP 패턴:

  • NMP_*_CREATE_STEPPAY SP로 매핑 테이블에 INSERT ... ON DUPLICATE KEY UPDATE
  • 예: NMP_ORDER_CREATE_STEPPAY, NMP_PRODUCT_CREATE_STEPPAY, NMP_SUBSCRIPTION_CREATE_STEPPAY, NMP_PRICE_CREATE_STEPPAY

조회 시 JOIN 패턴:

  • 조회 SP에서 LEFT JOIN STP_* 으로 StepPay 매핑 정보 함께 반환
  • 예: NMP_ORDER_GET_BY_IDXLEFT JOIN STP_ORDERS로 STP_ORD_IDX, STP_ORD_CODE 포함

11.3 웹훅에서 StepPay 코드 → 로컬 IDX 변환

웹훅은 StepPay 코드(STP_ORD_CODE 등)로 이벤트를 수신하므로, 로컬 IDX로 변환이 필요합니다:

웹훅 수신 (STP_ORD_CODE)
  → NMP_ORDER_GET_BY_STP_CODE로 ORD_IDX 조회
  → NMP_ORDER_UPDATE_STATUS(P_ORD_IDX, ...) 호출
  → NMP_ORDER_DECREASE_STOCK(P_ORD_IDX) 호출

이를 통해 모든 비즈니스 로직은 로컬 IDX 기반으로 동작하며, StepPay 의존성을 매핑 테이블로 캡슐화합니다.

11.4 동기화 방식

방식설명SP
실시간웹훅 수신 시 즉시 반영NMP_SYNC_*
배치주기적 전체 동기화NMP_SYNC_* (반복 호출)
온디맨드조회 시 API 호출 후 저장NMP_SYNC_*

11.5 충돌 해결

  • StepPay가 마스터, 로컬은 미러
  • ON DUPLICATE KEY UPDATE로 항상 최신 데이터 덮어쓰기
  • 로컬 전용 필드(PASSWORD_HASH 등)는 COALESCE로 보존

최종 업데이트: 2026년 3월 16일 - NMP_GET_USER_BY_PHONE, NMP_DELETE_USER, NMP_SYNC_PRICE_PLAN 추가