목록으로

Programming Notes

T-SQL의 LOB 타입에 대한 정규식(Regex) 지원 — Azure SQL 및 SQL Server 2025에서 사용 가능

한눈에 보기 — T-SQL의 기본 정규 표현식(regex) 함수가 이제 7개 함수 모두에서 최대 2MBvarchar(max)nvarchar(max) 입력을 지원합니다. 여기에는 두 가지 테이블 반환 함수(REGEXP_MATCHESREGEXP_SPLIT_TO_TABLE)도 포함됩니다. 이 기능은 SQL Server 2025 CU5에 포함되어 출시되었으며, Azure SQL Database, Fabric 내 SQL Database, 그리고 '최신 업데이트 유지(Always-up-to-date)' 정책이 구성된 Azure SQL Managed Instance에서 이미 사용할 수 있습니다. SQL Server 2025 업데이트 정책을 따르는 Managed Instance에는 CU5 배포의 일부로 적용될 예정입니다. 이제 패턴 매칭을 위해 로그 파일, HTML 문서 또는 대용량 JSON 페이로드를 8,000바이트씩 쪼개지 않아도 됩니다.

1. 서론

정규 표현식은 유효성 검사, 파싱, 변환 및 비정형 텍스트에서 구조화된 통찰력을 추출하는 등 현대 데이터 처리의 초석이 되어 왔습니다. SQL Server 2025Azure SQL을 통해 정규식은 이제 T-SQL의 기본 기능(First-class capability)이 되었으며, 더 이상 SQLCLR 함수나 애플리케이션 계층에서의 처리에 의존할 필요가 없습니다.

초기 릴리스에서는 기본 정규식 기능이 광범위하게 제공되었지만, 모든 함수에서 대형 객체(LOB) 입력을 지원하는 것은 아니었습니다. CU5는 그 간극을 메웠습니다.

내부적으로 T-SQL 정규식은 POSIX Extended Regular Expression (ERE) 의미 체계를 구현하고 엄선된 Perl 스타일 기능을 추가했으며, RE2 엔진을 기반으로 작동합니다. RE2는 선형 시간(linear-time) 내에 작동하며 백트래킹을 하지 않는 구현체입니다. 즉, 서비스 거부(DoS) 공격의 일종인 '치명적 백트래킹(Catastrophic backtracking, ReDoS)' 문제로부터 안전합니다. 이러한 안정성 보장은 입력값이 8,000바이트 문자열일 때보다 1.8MB 크기의 로그 블롭(blob)일 때 훨씬 더 중요해집니다.

출시 일정

마일스톤 출시 내용
Ignite 2025 — 정식 출시(GA) SQL Server 2025 및 Azure SQL에서 정규식 기능 정식 출시. 초기에는 REGEXP_LIKE, REGEXP_COUNT, REGEXP_INSTR에서만 LOB 입력 지원. REGEXP_REPLACEREGEXP_SUBSTR의 LOB 지원은 연기되었으며, 두 가지 테이블 반환 함수(TVF)는 LOB가 아닌 문자열 유형만 허용함.
Azure SQL (GA 이후 서비스 업데이트) 7개 모든 함수에서 LOB 입력 활성화.
SQL Server 2025 CU5 SQL Server의 7개 모든 함수에서 최대 2MB의 LOB 입력 활성화.

CU5의 새로운 기능

  • 모든 정규식 함수에서 varchar(max)nvarchar(max) 입력을 허용합니다.
  • 입력 문자열은 함수 호출당 2MB로 제한됩니다. 패턴 문자열은 여전히 8,000바이트로 제한되지만, 이는 유지 관리가 가능한 수준의 정규식으로는 충분히 큰 크기입니다.
  • 동작 방식이 Azure SQL과 SQL Server 간에 일관되므로 오늘 작성한 코드를 그대로 다른 환경으로 옮길 수 있습니다.

참고 — 2MB 제한은 컬럼이나 로우의 크기가 아니라 단일 함수 호출에 전달되는 입력값에 적용됩니다. varchar(max) 컬럼의 단일 값은 여전히 최대 2GB까지 저장할 수 있지만, 단일 정규식 평가에서는 해당 값 중 2MB까지만 처리할 수 있다는 제약입니다.

전제 조건

  • SQL Server 2025 CU5 이상, 또는 Azure SQL Database, 또는 Fabric 내 SQL Database, 또는 SQL Server 2025/최신 업데이트 유지 업데이트 정책으로 구성된 Azure SQL Managed Instance.
  • 두 가지 테이블 반환 함수(REGEXP_MATCHESREGEXP_SPLIT_TO_TABLE)를 사용하려면 데이터베이스 호환성 수준 170이 필요합니다. (단, ALLOW_BUILTIN_TVF_IN_ALL_COMPAT_LEVELS(미리 보기) 구성이 활성화된 경우는 제외)

참고 — Azure SQL Managed Instance(최신 업데이트 유지 정책)의 경우, 이 기능은 지역별로 순차적으로 배포 중입니다. 배포가 완료된 지역은 이미 활성화되어 있으며, 나머지 지역도 배포가 완료되는 대로 적용될 예정입니다. SQL Server 2025 업데이트 정책을 사용하는 인스턴스는 곧 출시될 CU5 롤아웃의 일부로 이 기능을 받게 됩니다.

호환성 수준 확인 (TVF 사용 시 170 필요):

SELECT name, compatibility_level FROM sys.databases WHERE name = DB_NAME(); 

-- 필요한 경우: 
-- ALTER DATABASE [<your-database>] SET COMPATIBILITY_LEVEL = 170;

2. LOB 데이터 작업하기

이 섹션에서는 실제 LOB 데이터를 사용하여 CU5의 기능을 시연합니다. 수 KB에서 수 MB에 이르는 웹 서버 및 애플리케이션 출력값이 포함된 RawPayload 컬럼을 가진 LogEntries 테이블과 HTML 정제 예제를 위한 HtmlPages 테이블을 만듭니다.

2.1 샘플 스키마 및 데이터 생성

IF OBJECT_ID('dbo.LogEntries', 'U') IS NOT NULL DROP TABLE dbo.LogEntries;
IF OBJECT_ID('dbo.HtmlPages',  'U') IS NOT NULL DROP TABLE dbo.HtmlPages;

CREATE TABLE dbo.LogEntries
(
    LogId       BIGINT IDENTITY(1,1) PRIMARY KEY,
    Source      SYSNAME       NOT NULL,
    IngestedAt  DATETIME2(3)  NOT NULL DEFAULT SYSUTCDATETIME(),
    RawPayload  VARCHAR(MAX)  NOT NULL   -- LOB 컬럼
);

CREATE TABLE dbo.HtmlPages
(
    PageId      INT IDENTITY(1,1) PRIMARY KEY,
    Url         NVARCHAR(2048) NOT NULL,
    Body        NVARCHAR(MAX)  NOT NULL  -- LOB 컬럼 (유니코드)
);

이제 현실적으로 큰 데이터를 생성합니다. REPLICATE 함수는 첫 번째 인수가 max 타입이 아니면 결과가 8,000바이트를 초과할 때 NULL을 반환하므로 REPLICATE(CAST(... AS varchar(max)), n) 패턴이 필요합니다.

-- 가상의 웹 액세스 로그 페이로드 (첫 번째 행은 약 252KB, 두 번째 행은 약 586KB).
DECLARE @logLine VARCHAR(500) =
    '127.0.0.1 - alice [21/May/2026:10:15:32 +0000] "GET /api/orders/42 HTTP/1.1" 200 1532 ' +
    'user-agent="Mozilla/5.0" ip=10.0.0.7 [email protected] card=4111-1111-1111-1234' + CHAR(10);

DECLARE @bigLog VARCHAR(MAX) =
    REPLICATE(CAST(@logLine AS VARCHAR(MAX)), 1500)                -- ~252 KB
    + '127.0.0.1 - mallory [21/May/2026:10:16:01 +0000] "POST /login HTTP/1.1" 500 0 ' +
      'ip=203.0.113.99 ssn=123-45-6789' + CHAR(10);

INSERT INTO dbo.LogEntries (Source, RawPayload) VALUES
    ('web-01', @bigLog),                                            -- ~252 KB
    ('web-02', REPLICATE(CAST('OK ' AS VARCHAR(MAX)), 200000));     -- ~586 KB

-- 가상의 HTML 페이지 (약 775KB / 약 396,000자).
DECLARE @htmlChunk NVARCHAR(MAX) =
    N'<div class="row"><p>Hello <b>world</b>! Contact <a href="mailto:[email protected]">bob</a>.</p></div>';

INSERT INTO dbo.HtmlPages (Url, Body) VALUES
    (N'https://contoso.example/page-1',
     N'<html><head><title>Big Page</title></head><body>'
     + REPLICATE(@htmlChunk, 4000)
     + N'</body></html>');

-- 페이로드 크기(바이트) 확인.
SELECT LogId, Source, DATALENGTH(RawPayload) AS PayloadBytes FROM dbo.LogEntries;
SELECT PageId, DATALENGTH(Body) AS BodyBytes, LEN(Body) AS BodyChars FROM dbo.HtmlPages;

결과:

LogId Source PayloadBytes
1 web-01 258,110
2 web-02 600,000

PageId BodyBytes BodyChars
1 792,124 396,062

CU5 이전에는 이러한 페이로드를 REGEXP_REPLACE, REGEXP_SUBSTR, REGEXP_MATCHES 또는 REGEXP_SPLIT_TO_TABLE에 넣으면 타입 불일치 오류가 발생하거나 LEFT(RawPayload, 8000) 처럼 강제 절단이 필요했습니다. 이제는 쿼리가 정상적으로 실행됩니다.

2.2 REGEXP_LIKE — LOB 콘텐츠로 로우 필터링

-- 적어도 하나의 HTTP 5xx 응답을 포함하는 로그 찾기.
SELECT LogId, Source, DATALENGTH(RawPayload) AS PayloadBytes
FROM   dbo.LogEntries
WHERE  REGEXP_LIKE(RawPayload, '"[A-Z]+\s[^"]+\sHTTP/1\.[01]"\s5[0-9]{2}\s');

REGEXP_LIKE는 불리언 술어(Boolean predicate)입니다. 패턴이 입력값 내 어디에서든 일치하면 true를, 그렇지 않으면 false를 반환합니다. 비트(bit)가 아닌 불리언을 반환하므로 WHERE, CASE WHEN, IIF 또는 CHECK 제약 조건에서 직접 사용하세요. = 1 또는 = 0과 비교하지 마십시오(구문 분석기에서 거부됨).

참고REGEXP_LIKE 자체는 데이터베이스 호환성 수준 170을 요구합니다. 다른 스칼라 정규식 함수(REGEXP_COUNT, REGEXP_INSTR, REGEXP_REPLACE, REGEXP_SUBSTR)는 모든 호환성 수준에서 사용할 수 있습니다.

결과:

LogId Source PayloadBytes
1 web-01 258,110

2.3 REGEXP_COUNT — 대규모 카운팅

-- 전체 LOB 페이로드에서 GET 요청, POST 요청 및 5xx 응답 횟수 집계.
SELECT LogId,
       Source,
       REGEXP_COUNT(RawPayload, '"GET\s')        AS Gets,
       REGEXP_COUNT(RawPayload, '"POST\s')       AS Posts,
       REGEXP_COUNT(RawPayload, '\s5[0-9]{2}\s') AS ServerErrors
FROM   dbo.LogEntries;

결과:

LogId Source Gets Posts ServerErrors
1 web-01 1,500 1 1
2 web-02 0 0 0

2.4 REGEXP_INSTR — 첫 번째 오류 위치 찾기

-- 각 페이로드에서 첫 번째 5xx 응답의 1부터 시작하는 문자 위치(없으면 0) 반환.
SELECT LogId,
       Source,
       REGEXP_INSTR(RawPayload, '\s5[0-9]{2}\s', 1, 1, 0) AS FirstErrorPos
FROM   dbo.LogEntries;

매개변수 설명: REGEXP_INSTR(문자열, 패턴, 시작위치, 발생회차, 반환옵션 [, 플래그 [, 그룹 ]]). 반환옵션0이면 일치하는 부분의 시작 위치를, 1이면 일치하는 부분 바로 다음 문자의 위치를 반환합니다.

결과:

LogId Source FirstErrorPos
1 web-01 258,072
2 web-02 0

2.5 REGEXP_REPLACE — 민감한 데이터 즉시 마스킹

LOB 페이로드에 대한 PII(개인식별정보) 마스킹은 CU5에서 가장 많이 요청된 시나리오 중 하나였습니다. CU5 이전에는 커스텀 청크 분할 대체 루틴이 필요했지만, 이제는 단일 표현식으로 가능합니다.

-- 페이로드 전반에 걸쳐 신용카드 번호, 미국 사회보장번호(SSN), 이메일 주소를 마스킹 처리.
SELECT LogId,
       REGEXP_REPLACE(
           REGEXP_REPLACE(
               REGEXP_REPLACE(
                   RawPayload,
                   '\b[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}\b',
                   '****-****-****-****'),
               '\b[0-9]{3}-[0-9]{2}-[0-9]{4}\b',
               '***-**-****'),
           '\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b',
           '[redacted-email]'
       ) AS RedactedPayload
FROM   dbo.LogEntries;

또는 단 한 번의 호출로 nvarchar(max) 페이지에서 모든 HTML 태그를 제거할 수 있습니다.

SELECT PageId,
       LEN(Body)                                     AS OriginalLen,
       LEN(REGEXP_REPLACE(Body, N'<[^>]+>', N''))    AS TextOnlyLen
FROM   dbo.HtmlPages;

결과 — 약 775KB의 HTML 문서가 단 한 번의 호출로 396,062자에서 100,008자의 일반 텍스트로 축소됩니다.

PageId OriginalLen TextOnlyLen
1 396,062 100,008

2.6 REGEXP_SUBSTR — 단일 값 추출

-- 각 로그 페이로드에서 첫 번째 IPv4 주소 추출.
SELECT LogId,
       REGEXP_SUBSTR(RawPayload,
                     '\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b',
                     1,    -- 시작 위치
                     1,    -- 발생 회차
                     'c',  -- 플래그: 대소문자 구분
                     0     -- 그룹: 0은 전체 일치 항목 반환
                    ) AS FirstIp
FROM   dbo.LogEntries;

전체 일치 항목 대신 특정 캡처 그룹의 내용을 반환하려면 마지막 인수로 1부터 시작하는 그룹 번호를 전달하세요.

결과:

LogId FirstIp
1 127.0.0.1
2 NULL

2.7 REGEXP_MATCHES — 모든 일치 항목을 세트 기반으로

이 기능은 TVF와 LOB의 조합이 가장 큰 생산성 향상을 제공하는 부분입니다. 수 메가바이트의 비정형 텍스트에서 모든 구조화된 값을 단일 세트 기반 쿼리로 추출하며, 클라이언트와의 왕복 통신이 필요 없습니다.

REGEXP_MATCHES는 일치하는 항목당 하나의 로우를 반환하며 다음 컬럼을 포함합니다.

컬럼 타입 설명
match_id bigint 일치 항목의 순번 (1부터 시작).
start_position int 일치 항목의 시작 인덱스 (1부터 시작).
end_position int 일치 항목의 종료 인덱스 (1부터 시작).
match_value 입력과 동일 일치하는 전체 부분 문자열.
substring_matches json 각 캡처 그룹을 설명하는 JSON 배열. [{"value":"…","start":N,"length":N}, …] 형태.
-- 모든 로그 페이로드에 있는 모든 이메일 주소와 원본 로우 ID 추출.
SELECT  l.LogId,
        m.match_id,
        m.match_value AS EmailFound
FROM    dbo.LogEntries AS l
CROSS APPLY REGEXP_MATCHES(
        l.RawPayload,
        '\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b'
) AS m
ORDER BY l.LogId, m.match_id;

캡처 그룹은 더욱 유용합니다. substring_matches JSON 문서를 읽어 각 로그 라인의 구성 요소를 컬럼으로 투영할 수 있습니다.

-- Common-Log-Format 스타일 엔트리를 ip, user, status, bytes 컬럼으로 파싱.
-- 패턴에는 4개의 캡처 그룹이 있으며 아래에서 $[0]부터 $[3]까지 접근함.
SELECT  l.LogId,
        m.match_id,
        JSON_VALUE(m.substring_matches, '$[0].value') AS Ip,
        JSON_VALUE(m.substring_matches, '$[1].value') AS UserName,
        JSON_VALUE(m.substring_matches, '$[2].value') AS Status,
        JSON_VALUE(m.substring_matches, '$[3].value') AS Bytes
FROM    dbo.LogEntries AS l
CROSS APPLY REGEXP_MATCHES(
        l.RawPayload,
        '^([0-9.]+)\s-\s(\S+)\s$$[^$$]+$$\s"[^"]+"\s([0-9]{3})\s([0-9]+)',
        'm'    -- multi-line 플래그: ^와 $가 전체 입력이 아닌 각 라인의 시작과 끝에 고정됨
) AS m
ORDER BY l.LogId, m.match_id;

중요'm' 플래그가 없으면 ^ 앵커는 전체 250KB 입력의 시작 부분에서만 일치하므로 첫 번째 라인에 대해서만 하나의 일치 항목을 받게 됩니다. 멀티라인 플래그가 있어야 라인별 추출이 가능해집니다.

결과 (파싱된 처음 두 행):

LogId match_id Ip UserName Status Bytes
1 1 127.0.0.1 alice 200 1532
1 2 127.0.0.1 alice 200 1532

2.8 REGEXP_SPLIT_TO_TABLE — LOB를 로우로 분할

-- 전체 로그 페이로드를 비어있지 않은 한 라인당 한 로우로 투영.
SELECT  l.LogId,
        s.ordinal AS [LineNo],
        s.value   AS LineText
FROM    dbo.LogEntries AS l
CROSS APPLY REGEXP_SPLIT_TO_TABLE(l.RawPayload, '\r?
') AS s
WHERE   l.LogId = 1
  AND   s.value <> ''
ORDER BY s.ordinal;

이제 엔진을 벗어나지 않고도 수 메가바이트의 텍스트 블롭을 테이블 형식으로 투영할 수 있습니다. 이를 CTE에 넣거나, 집계하거나, 차원 테이블과 조인하거나, 스테이징 테이블에 저장하는 등 모든 작업이 세트 기반으로 가능합니다.

결과 (처음 세 행):

LogId ordinal LineText (앞 80자)
1 1 127.0.0.1 - alice [21/May/2026:10:15:32 +0000] "GET /api/orders/42 HTTP/1.1" 200
1 2 127.0.0.1 - alice [21/May/2026:10:15:32 +0000] "GET /api/orders/42 HTTP/1.1" 200
1 3 127.0.0.1 - alice [21/May/2026:10:15:32 +0000] "GET /api/orders/42 HTTP/1.1" 200

팁 — LOB 정규식 파이프라인 구성CROSS APPLY(일치 항목이 없는 행도 유지해야 하는 경우 OUTER APPLY)는 가장 기본적인 구성 방식입니다. REGEXP_SPLIT_TO_TABLE(라인 분할) -> REGEXP_MATCHES(라인별 필드 추출) -> 일반 집계 함수 순으로 스택을 쌓아 단일 실행 계획 내에서 처리할 수 있습니다.

2.9 2MB 제한 — 더 큰 입력을 위한 전략

2MB 제한은 단일 정규식 호출의 입력 문자열에 적용됩니다. 정규식 함수에 전달된 값이 2MB를 초과하면 함수는 자동으로 절단하는 대신 오류(오류 번호 19311, 심각도 16)를 발생시킵니다. 이는 의도된 동작으로, 자동으로 절단할 경우 발생할 수 있는 데이터 정합성 버그를 방지하기 위함입니다.

실제로 2MB는 넉넉한 한도입니다. 단일 로그 파일이나 HTML 문서가 이 크기를 넘는 경우는 드물며, 대부분의 실제 LOB 데이터는 이 범위 안에 들어옵니다. 만약 개별 값이 이 한도를 초과한다면, 가장 확실한 접근 방식은 데이터가 컬럼에 저장되기 전에 더 작은 논리적 단위로 분할하는 것입니다. 예를 들어, 수집 시점에 라인당 한 로우, 문서 섹션당 한 로우, 또는 레코드당 한 로우씩 쓰는 식입니다. 모든 정규식 함수(TVF 포함)가 동일한 2MB 한도를 공유하므로 쿼리 시점에 샤딩(sharding)하는 것은 일반적으로 쉽지 않습니다. 로드 경로에서 처리하는 것이 정규식 호출을 한도 내로 유지하고 쿼리 시점의 복잡한 우회책을 피하는 길입니다.

바이트 vs. 문자 — 2MB 제한은 문자가 아닌 바이트 단위로 측정되며, 바이트 수는 컬럼의 선언된 유형과 관계없이 입력의 UTF-8 인코딩을 기준으로 합니다. ASCII 문자는 각각 1바이트를 차지하므로 일반 ASCII 텍스트는 약 200만 자까지 가능합니다. 비 ASCII 문자는 UTF-8에서 2~4바이트를 차지하므로 글자 수가 더 적게 들어갑니다. DATALENGTH()는 컬럼 자체의 인코딩에 따른 저장 크기를 보고하므로 제한 기준인 UTF-8 바이트 수와 다를 수 있으며, 글자 수를 세는 LEN()은 여기서 크기 확인용으로 적합하지 않습니다.

제한 사항이 체크하는 실제 UTF-8 바이트 길이를 측정하려면, 값을 UTF-8 콜레이션(Collation) 하에서 varchar(max)로 변환한 후 DATALENGTH를 확인하세요.

SELECT DATALENGTH(
           CONVERT(varchar(max),
                   Body COLLATE Latin1_General_100_CI_AS_SC_UTF8)
       ) AS Utf8Bytes
FROM   dbo.HtmlPages;

2 * 1024 * 1024 (2,097,152) 바이트를 초과하는 값은 정규식 호출 시 거부됩니다.

진정으로 2MB 이상이 필요한 시나리오가 있습니까? 현재의 2MB 한도보다 큰 개별 값에 대해 정규식 평가가 필요한 워크로드가 있다면 저희에게 알려주세요. Azure SQL 피드백 포털에 데이터 형태, 페이로드 크기, 패턴 및 비즈니스 요구 사항과 같은 세부 정보를 공유해 주십시오. 고객 피드백은 향후 한도 변경의 우선순위를 결정하는 데 직접적인 도움이 됩니다.

2.10 정리

DROP TABLE IF EXISTS dbo.LogEntries;
DROP TABLE IF EXISTS dbo.HtmlPages;

3. 요약

CU5의 변경 사항

  • CU5 이전REGEXP_LIKE, REGEXP_COUNT, REGEXP_INSTR에서만 LOB 입력을 허용했습니다. 나머지 함수들(REGEXP_REPLACE, REGEXP_SUBSTR, 두 가지 TVF)은 LOB가 아닌 문자열 입력이 필요하여 LEFT(..., 8000) 등으로 절단하거나 애플리케이션 계층에서 청크로 나누어야 했습니다.
  • CU5 이후 (및 Azure SQL) — 7개 모든 함수에서 최대 2MBvarchar(max)nvarchar(max) 입력을 지원합니다. 패턴 문자열 한도는 8,000바이트로 유지됩니다.

빠른 참조 가이드

함수 반환값 LOB 입력 지원 (CU5) 주요 사용 사례
REGEXP_LIKE 불리언 (술어) WHERE / CASE / CHECK 술어에서 로우 필터링
REGEXP_COUNT int 패턴 발생 횟수 카운팅
REGEXP_INSTR int n번째 일치 항목의 위치
REGEXP_REPLACE 문자열 텍스트 마스킹, 정제 또는 정규화
REGEXP_SUBSTR 문자열 단일 값 추출
REGEXP_MATCHES (TVF) 세트 (match_id, start_position, end_position, match_value, substring_matches) 모든 일치 항목 및 캡처 그룹(JSON 경유) 추출, 세트 기반
REGEXP_SPLIT_TO_TABLE (TVF) 세트 (value, ordinal) 정규식 구분자를 사용하여 LOB를 로우로 분할

추가 읽을거리

맺음말. 기본 정규식 기능은 정식 출시되었을 때 이미 상당한 삶의 질(QoL) 향상을 가져왔습니다. CU5는 그 그림을 완성했습니다. 7개의 모든 함수, 2MB까지의 모든 입력 크기, 스칼라 또는 테이블 반환 등 모든 형태를 지원합니다. 다음에 특정 컬럼을 grep하기 위해 데이터베이스 밖으로 내보내고 싶어질 때, 먼저 7개의 정규식 함수 중 하나를 사용해 보세요.

즐거운 매칭 되시길 바랍니다. 🧠