์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฏ PHP ์ •๊ทœํ‘œํ˜„์‹๊ณผ PCRE ์™„์ „์ •๋ณต

๐ŸŽฏ PHP ์ •๊ทœํ‘œํ˜„์‹๊ณผ PCRE ์™„์ „์ •๋ณต

ํ…์ŠคํŠธ ํŒจํ„ด ๋งค์นญ์˜ ๋ชจ๋“  ๊ฒƒ, ์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ์•Œ๋ ค์ค„๊ฒŒ!

๐Ÿš€ ์ •๊ทœํ‘œํ˜„์‹, ๋„Œ ๋ˆ„๊ตฌ๋ƒ?

์•ผ, ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•˜๋‹ค ๋ณด๋ฉด ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ์ง„์งœ ๊ณจ์น˜ ์•„ํ”Œ ๋•Œ ๋งŽ์ง€ ์•Š์•„? ๐Ÿค”
์ด๋ฉ”์ผ ํ˜•์‹ ์ฒดํฌํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ „ํ™”๋ฒˆํ˜ธ ์ถ”์ถœํ•ด์•ผ ํ•˜๋Š”๋ฐ, URL ์ฐพ์•„์•ผ ํ•˜๋Š”๋ฐ...
์ด๋Ÿด ๋•Œ๋งˆ๋‹ค if๋ฌธ ์ˆ˜์‹ญ ๊ฐœ ์“ฐ๋ฉด์„œ "์ด๊ฒŒ ๋งž๋‚˜?" ์‹ถ์„ ๋•Œ ์žˆ์ž–์•„.

๋ฐ”๋กœ ๊ทธ๋•Œ ํ•„์š”ํ•œ ๊ฒŒ ์ •๊ทœํ‘œํ˜„์‹(Regular Expression)์ด์•ผ!
์˜์–ด๋กœ๋Š” Regex ๋˜๋Š” RegExp๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ, ์ด๊ฑด ํ…์ŠคํŠธ ํŒจํ„ด์„ ํ‘œํ˜„ํ•˜๋Š” ํŠน๋ณ„ํ•œ ๋ฌธ์ž์—ด์ด์•ผ.
๋งˆ์น˜ ํ…์ŠคํŠธ๋ฅผ ์ฐพ๋Š” ๋งˆ๋ฒ• ์ฃผ๋ฌธ ๊ฐ™์€ ๊ฑฐ์ง€. โœจ

๐ŸŽ“ ์ •๊ทœํ‘œํ˜„์‹์ด๋ž€? ํŠน์ •ํ•œ ๊ทœ์น™์„ ๊ฐ€์ง„ ๋ฌธ์ž์—ด์˜ ์ง‘ํ•ฉ์„ ํ‘œํ˜„ํ•˜๋Š” ํ˜•์‹ ์–ธ์–ด์•ผ.
์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด "์ด๋Ÿฐ ํŒจํ„ด์˜ ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„์ค˜!"๋ผ๊ณ  ์ปดํ“จํ„ฐ์—๊ฒŒ ๋ช…๋ นํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์ง€.

์˜ˆ๋ฅผ ๋“ค์–ด "์ˆซ์ž 3๊ฐœ-์ˆซ์ž 4๊ฐœ-์ˆซ์ž 4๊ฐœ" ํŒจํ„ด์ด๋ฉด ์ „ํ™”๋ฒˆํ˜ธ๊ฒ ์ง€?
์ด๊ฑธ ์ •๊ทœํ‘œํ˜„์‹์œผ๋กœ ํ‘œํ˜„ํ•˜๋ฉด /\d{3}-\d{4}-\d{4}/ ์ด๋ ‡๊ฒŒ ๊ฐ„๋‹จํ•ด์ ธ!

๐Ÿ”ง PHP์™€ PCRE์˜ ๋งŒ๋‚จ

PHP์—์„œ ์ •๊ทœํ‘œํ˜„์‹์„ ์“ธ ๋•Œ๋Š” PCRE(Perl Compatible Regular Expressions)๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด.
์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด Perl ์–ธ์–ด์˜ ์ •๊ทœํ‘œํ˜„์‹ ๋ฌธ๋ฒ•๊ณผ ํ˜ธํ™˜๋˜๋Š” ๋ฐฉ์‹์ด์•ผ. ๐Ÿช

PHP 4.2.0 ๋ฒ„์ „๋ถ€ํ„ฐ ๊ธฐ๋ณธ์œผ๋กœ ํƒ‘์žฌ๋˜์–ด ์žˆ์–ด์„œ ๋ณ„๋„ ์„ค์น˜ ์—†์ด ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ์–ด.
PCRE๋Š” ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์—ฐํ•ด์„œ ๊ฑฐ์˜ ๋ชจ๋“  ํ…์ŠคํŠธ ํŒจํ„ด ๋งค์นญ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€!

๐Ÿ’ก ์™œ PCRE๋ฅผ ์“ธ๊นŒ? PHP์—๋Š” ์‚ฌ์‹ค ๋‘ ๊ฐ€์ง€ ์ •๊ทœํ‘œํ˜„์‹ ์—”์ง„์ด ์žˆ์—ˆ์–ด.
- POSIX ์ •๊ทœํ‘œํ˜„์‹ (ereg ํ•จ์ˆ˜๊ตฐ) - PHP 5.3.0๋ถ€ํ„ฐ deprecated
- PCRE (preg ํ•จ์ˆ˜๊ตฐ) - ํ˜„์žฌ ํ‘œ์ค€

PCRE๊ฐ€ ๋” ๋น ๋ฅด๊ณ , ๊ธฐ๋Šฅ๋„ ๋งŽ๊ณ , ์œ ๋‹ˆ์ฝ”๋“œ ์ง€์›๋„ ์ข‹์•„์„œ ์ง€๊ธˆ์€ PCRE๋งŒ ์“ด๋‹ค๊ณ  ๋ณด๋ฉด ๋ผ!
PCRE ์—”์ง„ ํ…์ŠคํŠธ ์ž…๋ ฅ hello@example.com ๋งค์นญ ๊ฒฐ๊ณผ ์ด๋ฉ”์ผ ํ˜•์‹ OK! ํŒจํ„ด: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/

๐Ÿ“š PHP ์ •๊ทœํ‘œํ˜„์‹ ํ•ต์‹ฌ ํ•จ์ˆ˜๋“ค

PHP์—์„œ PCRE๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด preg_๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜๋“ค์„ ์จ์•ผ ํ•ด.
๊ฐ ํ•จ์ˆ˜๋งˆ๋‹ค ์—ญํ• ์ด ๋‹ค๋ฅด๋‹ˆ๊นŒ ํ•˜๋‚˜์”ฉ ์•Œ์•„๋ณด์ž! ๐ŸŽฏ

1. preg_match() - ํŒจํ„ด ์ฐพ๊ธฐ์˜ ๊ธฐ๋ณธ

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ•จ์ˆ˜์•ผ. ๋ฌธ์ž์—ด์—์„œ ํŒจํ„ด์ด ํ•œ ๋ฒˆ์ด๋ผ๋„ ๋‚˜ํƒ€๋‚˜๋Š”์ง€ ํ™•์ธํ•ด.
์ฐพ์œผ๋ฉด 1์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋ชป ์ฐพ์œผ๋ฉด 0์„ ๋ฐ˜ํ™˜ํ•˜์ง€.

<?php
// ์ด๋ฉ”์ผ ํ˜•์‹ ์ฒดํฌ
$email = "user@example.com";
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';

if (preg_match($pattern, $email)) {
    echo "์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค!";
} else {
    echo "์ด๋ฉ”์ผ ํ˜•์‹์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
}

// ๋งค์นญ๋œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ›๊ธฐ
$text = "์ œ ์ „ํ™”๋ฒˆํ˜ธ๋Š” 010-1234-5678์ž…๋‹ˆ๋‹ค.";
$pattern = '/\d{3}-\d{4}-\d{4}/';

if (preg_match($pattern, $text, $matches)) {
    echo "์ฐพ์€ ์ „ํ™”๋ฒˆํ˜ธ: " . $matches[0]; // 010-1234-5678
}
?>

2. preg_match_all() - ๋ชจ๋“  ๋งค์นญ ์ฐพ๊ธฐ

preg_match()๋Š” ์ฒซ ๋ฒˆ์งธ ๋งค์นญ๋งŒ ์ฐพ์ง€๋งŒ,
preg_match_all()์€ ๋ชจ๋“  ๋งค์นญ์„ ๋‹ค ์ฐพ์•„์ค˜. ๐Ÿ”

<?php
$text = "์—ฐ๋ฝ์ฒ˜: 010-1111-2222, 02-3333-4444, 031-5555-6666";
$pattern = '/\d{2,3}-\d{3,4}-\d{4}/';

preg_match_all($pattern, $text, $matches);

echo "์ฐพ์€ ์ „ํ™”๋ฒˆํ˜ธ ๊ฐœ์ˆ˜: " . count($matches[0]) . "\n";
foreach ($matches[0] as $phone) {
    echo $phone . "\n";
}
// ์ถœ๋ ฅ:
// ์ฐพ์€ ์ „ํ™”๋ฒˆํ˜ธ ๊ฐœ์ˆ˜: 3
// 010-1111-2222
// 02-3333-4444
// 031-5555-6666
?>

3. preg_replace() - ํŒจํ„ด ์น˜ํ™˜ํ•˜๊ธฐ

์ฐพ์€ ํŒจํ„ด์„ ๋‹ค๋ฅธ ๋ฌธ์ž์—ด๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ•จ์ˆ˜์•ผ.
ํ…์ŠคํŠธ ์ •์ œ๋‚˜ ํฌ๋งท ๋ณ€๊ฒฝํ•  ๋•Œ ์—„์ฒญ ์œ ์šฉํ•ด! โœ‚๏ธ

<?php
// ์ „ํ™”๋ฒˆํ˜ธ์—์„œ ํ•˜์ดํ”ˆ ์ œ๊ฑฐ
$phone = "010-1234-5678";
$clean_phone = preg_replace('/-/', '', $phone);
echo $clean_phone; // 01012345678

// HTML ํƒœ๊ทธ ์ œ๊ฑฐ
$html = "<p>์•ˆ๋…•ํ•˜์„ธ์š”</p><script>alert('xss')</script>";
$clean_text = preg_replace('/<[^>]+>/', '', $html);
echo $clean_text; // ์•ˆ๋…•ํ•˜์„ธ์š”alert('xss')

// ์—ฌ๋Ÿฌ ๊ณต๋ฐฑ์„ ํ•˜๋‚˜๋กœ
$text = "์•ˆ๋…•ํ•˜์„ธ์š”     ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค";
$clean = preg_replace('/\s+/', ' ', $text);
echo $clean; // ์•ˆ๋…•ํ•˜์„ธ์š” ๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค
?>

4. preg_split() - ํŒจํ„ด์œผ๋กœ ๋ฌธ์ž์—ด ๋‚˜๋ˆ„๊ธฐ

explode()์˜ ๊ฐ•๋ ฅํ•œ ๋ฒ„์ „์ด๋ผ๊ณ  ๋ณด๋ฉด ๋ผ.
๋ณต์žกํ•œ ๊ตฌ๋ถ„์ž๋กœ๋„ ๋ฌธ์ž์—ด์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์–ด!

<?php
// ์‰ผํ‘œ๋‚˜ ์„ธ๋ฏธ์ฝœ๋ก ์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ
$text = "์‚ฌ๊ณผ,๋ฐ”๋‚˜๋‚˜;ํฌ๋„,๋”ธ๊ธฐ;์ˆ˜๋ฐ•";
$fruits = preg_split('/[,;]/', $text);
print_r($fruits);
// Array ( [0] => ์‚ฌ๊ณผ [1] => ๋ฐ”๋‚˜๋‚˜ [2] => ํฌ๋„ [3] => ๋”ธ๊ธฐ [4] => ์ˆ˜๋ฐ• )

// ๊ณต๋ฐฑ ๋ฌธ์ž(์ŠคํŽ˜์ด์Šค, ํƒญ, ์ค„๋ฐ”๊ฟˆ)๋กœ ๋‚˜๋ˆ„๊ธฐ
$text = "PHP    JavaScript\tPython\nJava";
$languages = preg_split('/\s+/', $text);
print_r($languages);
?>

5. preg_grep() - ๋ฐฐ์—ด์—์„œ ํŒจํ„ด ๋งค์นญ ํ•„ํ„ฐ๋ง

๋ฐฐ์—ด์˜ ์š”์†Œ ์ค‘์—์„œ ํŒจํ„ด๊ณผ ๋งค์นญ๋˜๋Š” ๊ฒƒ๋งŒ ๊ณจ๋ผ๋‚ด๋Š” ํ•จ์ˆ˜์•ผ.
๋ฐ์ดํ„ฐ ํ•„ํ„ฐ๋งํ•  ๋•Œ ์ •๋ง ํŽธํ•ด! ๐ŸŽฃ

<?php
$emails = [
    "user@example.com",
    "invalid-email",
    "admin@test.co.kr",
    "wrong@",
    "contact@company.net"
];

$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
$valid_emails = preg_grep($pattern, $emails);

print_r($valid_emails);
// Array ( [0] => user@example.com [2] => admin@test.co.kr [4] => contact@company.net )
?>

๐ŸŽจ ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๋ฌธ๋ฒ• ์™„์ „์ •๋ณต

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด์„ ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”์ง€ ์•Œ์•„๋ณด์ž!
์ฒ˜์Œ์—” ์™ธ๊ณ„์–ด ๊ฐ™์ง€๋งŒ ์ต์ˆ™ํ•ด์ง€๋ฉด ์ง„์งœ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๊ฐ€ ๋ผ. ๐Ÿ’ช

๊ธฐ๋ณธ ๋ฉ”ํƒ€๋ฌธ์ž๋“ค

๋ฉ”ํƒ€๋ฌธ์ž ์˜๋ฏธ ์˜ˆ์‹œ
. ์ค„๋ฐ”๊ฟˆ์„ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ฌธ์ž ํ•˜๋‚˜ /a.c/ โ†’ abc, aXc, a9c ๋งค์นญ
^ ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘ /^Hello/ โ†’ Hello๋กœ ์‹œ์ž‘
$ ๋ฌธ์ž์—ด์˜ ๋ /world$/ โ†’ world๋กœ ๋๋‚จ
* 0๋ฒˆ ์ด์ƒ ๋ฐ˜๋ณต /ab*c/ โ†’ ac, abc, abbc ๋งค์นญ
+ 1๋ฒˆ ์ด์ƒ ๋ฐ˜๋ณต /ab+c/ โ†’ abc, abbc (ac๋Š” ์•ˆ๋จ)
? 0๋ฒˆ ๋˜๋Š” 1๋ฒˆ /colou?r/ โ†’ color, colour
| OR ์กฐ๊ฑด /cat|dog/ โ†’ cat ๋˜๋Š” dog
() ๊ทธ๋ฃนํ™” /(ab)+/ โ†’ ab, abab, ababab
[] ๋ฌธ์ž ํด๋ž˜์Šค /[abc]/ โ†’ a, b, c ์ค‘ ํ•˜๋‚˜
\ ์ด์Šค์ผ€์ดํ”„ ๋ฌธ์ž /\./ โ†’ ๋งˆ์นจํ‘œ ์ž์ฒด๋ฅผ ์ฐพ์Œ

์ˆ˜๋Ÿ‰์ž(Quantifiers) ์ƒ์„ธ

๐Ÿ”ข ์ •ํ™•ํ•œ ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์ง€์ •ํ•˜๊ธฐ {n} - ์ •ํ™•ํžˆ n๋ฒˆ
{n,} - n๋ฒˆ ์ด์ƒ
{n,m} - n๋ฒˆ ์ด์ƒ m๋ฒˆ ์ดํ•˜

์˜ˆ์‹œ:
/\d{3}/ โ†’ ์ˆซ์ž ์ •ํ™•ํžˆ 3๊ฐœ
/\d{3,}/ โ†’ ์ˆซ์ž 3๊ฐœ ์ด์ƒ
/\d{3,5}/ โ†’ ์ˆซ์ž 3~5๊ฐœ
<?php
// ์ „ํ™”๋ฒˆํ˜ธ ํŒจํ„ด (์ง€์—ญ๋ฒˆํ˜ธ 2-3์ž๋ฆฌ, ๊ตญ๋ฒˆ 3-4์ž๋ฆฌ, ๋ฒˆํ˜ธ 4์ž๋ฆฌ)
$pattern = '/\d{2,3}-\d{3,4}-\d{4}/';

// ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ (8~16์ž, ์˜๋ฌธ+์ˆซ์ž ํ•„์ˆ˜)
$password_pattern = '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/';

// ์šฐํŽธ๋ฒˆํ˜ธ (5์ž๋ฆฌ ์ˆซ์ž)
$zipcode_pattern = '/^\d{5}$/';
?>

๋ฌธ์ž ํด๋ž˜์Šค์™€ ๋‹จ์ถ• ํ‘œํ˜„

๋‹จ์ถ• ํ‘œํ˜„ ์˜๋ฏธ ๋™์ผ ํ‘œํ˜„
\d ์ˆซ์ž [0-9]
\D ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฒƒ [^0-9]
\w ๋‹จ์–ด ๋ฌธ์ž [a-zA-Z0-9_]
\W ๋‹จ์–ด ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ๊ฒƒ [^a-zA-Z0-9_]
\s ๊ณต๋ฐฑ ๋ฌธ์ž ์ŠคํŽ˜์ด์Šค, ํƒญ, ์ค„๋ฐ”๊ฟˆ ๋“ฑ
\S ๊ณต๋ฐฑ์ด ์•„๋‹Œ ๋ฌธ์ž -
\b ๋‹จ์–ด ๊ฒฝ๊ณ„ ๋‹จ์–ด์˜ ์‹œ์ž‘์ด๋‚˜ ๋
\B ๋‹จ์–ด ๊ฒฝ๊ณ„๊ฐ€ ์•„๋‹˜ -
<?php
// ๋‹จ์–ด ๊ฒฝ๊ณ„ ํ™œ์šฉ ์˜ˆ์‹œ
$text = "cat cats category";

// 'cat'์ด๋ผ๋Š” ๋‹จ์–ด๋งŒ ์ •ํ™•ํžˆ ์ฐพ๊ธฐ
preg_match_all('/\bcat\b/', $text, $matches);
print_r($matches[0]); // Array ( [0] => cat )

// 'cat'์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋‹จ์–ด
preg_match_all('/\bcat\w*/', $text, $matches);
print_r($matches[0]); // Array ( [0] => cat [1] => cats [2] => category )
?>
์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๊ตฌ์กฐ / ^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-z]{2,}$ /i ์‹œ์ž‘ ๊ตฌ๋ถ„์ž ํŒจํ„ด ๋ณธ๋ฌธ ํ”Œ๋ž˜๊ทธ ์ฃผ์š” ๋ฉ”ํƒ€๋ฌธ์ž ^ : ์‹œ์ž‘ $ : ๋ . : ์ž„์˜ ๋ฌธ์ž * : 0ํšŒ ์ด์ƒ + : 1ํšŒ ์ด์ƒ ? : 0 ๋˜๋Š” 1ํšŒ ๋ฌธ์ž ํด๋ž˜์Šค \d : ์ˆซ์ž \w : ๋‹จ์–ด๋ฌธ์ž \s : ๊ณต๋ฐฑ [abc] : a,b,c ์ค‘ ํ•˜๋‚˜ [^abc] : a,b,c ์ œ์™ธ [a-z] : a๋ถ€ํ„ฐ z๊นŒ์ง€ ์ˆ˜๋Ÿ‰์ž {n} : n๋ฒˆ {n,} : n๋ฒˆ ์ด์ƒ {n,m} : n~m๋ฒˆ * : {0,} + : {1,} ? : {0,1}

๐ŸŽฏ ์‹ค์ „ ํŒจํ„ด ์˜ˆ์ œ ๋ชจ์Œ

์ด๋ก ๋งŒ ์•Œ๋ฉด ๋ญํ•ด, ์‹ค์ „์—์„œ ๋ชป ์“ฐ๋ฉด! ๐Ÿ˜…
์ž์ฃผ ์“ฐ์ด๋Š” ํŒจํ„ด๋“ค์„ ๋ชจ์•„๋ดค์–ด. ๋ณต์‚ฌํ•ด์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ์ฃผ์„๋„ ๋‹ฌ์•„๋’€์œผ๋‹ˆ๊นŒ ์ฐธ๊ณ ํ•ด!

๐Ÿ“ง ์ด๋ฉ”์ผ ๊ฒ€์ฆ

<?php
// ๊ธฐ๋ณธ ์ด๋ฉ”์ผ ํŒจํ„ด
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';

// ๋” ์—„๊ฒฉํ•œ ์ด๋ฉ”์ผ ํŒจํ„ด (RFC 5322 ๊ธฐ๋ฐ˜)
$strict_pattern = '/^(?!.*\.\.)(?!.*\.$)[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';

function validateEmail($email) {
    $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
    return preg_match($pattern, $email) === 1;
}

// ํ…Œ์ŠคํŠธ
echo validateEmail("user@example.com") ? "์œ ํšจ" : "๋ฌดํšจ"; // ์œ ํšจ
echo validateEmail("invalid@") ? "์œ ํšจ" : "๋ฌดํšจ"; // ๋ฌดํšจ
?>

๐Ÿ“ฑ ์ „ํ™”๋ฒˆํ˜ธ ๊ฒ€์ฆ

<?php
// ํ•œ๊ตญ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ (010-1234-5678)
$mobile_pattern = '/^01[0-9]-\d{3,4}-\d{4}$/';

// ํ•˜์ดํ”ˆ ์žˆ๊ฑฐ๋‚˜ ์—†๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค ํ—ˆ์šฉ
$flexible_pattern = '/^01[0-9]-?\d{3,4}-?\d{4}$/';

// ์ผ๋ฐ˜ ์ „ํ™”๋ฒˆํ˜ธ (02-123-4567, 031-1234-5678)
$phone_pattern = '/^0\d{1,2}-\d{3,4}-\d{4}$/';

function formatPhone($phone) {
    // ์ˆซ์ž๋งŒ ์ถ”์ถœ
    $numbers = preg_replace('/[^0-9]/', '', $phone);
    
    // ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
    if (preg_match('/^(01[0-9])(\d{3,4})(\d{4})$/', $numbers, $matches)) {
        return $matches[1] . '-' . $matches[2] . '-' . $matches[3];
    }
    
    return $phone;
}

echo formatPhone("01012345678"); // 010-1234-5678
?>

๐Ÿ” ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„ ๊ฒ€์ฆ

<?php
// ์ตœ์†Œ 8์ž, ์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž ๊ฐ 1๊ฐœ ์ด์ƒ
$strong_password = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/';

// ์ตœ์†Œ 8์ž, ์˜๋ฌธ+์ˆซ์ž ์กฐํ•ฉ
$medium_password = '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/';

function checkPasswordStrength($password) {
    $patterns = [
        'weak' => '/^.{6,}$/', // 6์ž ์ด์ƒ
        'medium' => '/^(?=.*[A-Za-z])(?=.*\d).{8,}$/', // 8์ž ์ด์ƒ, ์˜๋ฌธ+์ˆซ์ž
        'strong' => '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/' // ๊ฐ•๋ ฅ
    ];
    
    if (preg_match($patterns['strong'], $password)) {
        return '๊ฐ•๋ ฅ';
    } elseif (preg_match($patterns['medium'], $password)) {
        return '๋ณดํ†ต';
    } elseif (preg_match($patterns['weak'], $password)) {
        return '์•ฝํ•จ';
    }
    return '๋งค์šฐ ์•ฝํ•จ';
}

echo checkPasswordStrength("Pass123!"); // ๊ฐ•๋ ฅ
echo checkPasswordStrength("password123"); // ๋ณดํ†ต
?>

๐ŸŒ URL ๊ฒ€์ฆ ๋ฐ ์ถ”์ถœ

<?php
// ๊ธฐ๋ณธ URL ํŒจํ„ด
$url_pattern = '/^https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/';

// ํ…์ŠคํŠธ์—์„œ URL ์ถ”์ถœ
$text = "๋ฐฉ๋ฌธํ•˜์„ธ์š”: https://www.example.com ๊ทธ๋ฆฌ๊ณ  http://test.co.kr/page";
preg_match_all('/https?:\/\/[^\s]+/', $text, $urls);
print_r($urls[0]);
// Array ( [0] => https://www.example.com [1] => http://test.co.kr/page )

// URL์—์„œ ๋„๋ฉ”์ธ๋งŒ ์ถ”์ถœ
function extractDomain($url) {
    if (preg_match('/^https?:\/\/([^\/]+)/', $url, $matches)) {
        return $matches[1];
    }
    return null;
}

echo extractDomain("https://www.example.com/page"); // www.example.com
?>

๐Ÿ’ณ ์‹ ์šฉ์นด๋“œ ๋ฒˆํ˜ธ ๊ฒ€์ฆ

<?php
// ์‹ ์šฉ์นด๋“œ ๋ฒˆํ˜ธ (4์ž๋ฆฌ-4์ž๋ฆฌ-4์ž๋ฆฌ-4์ž๋ฆฌ)
$card_pattern = '/^\d{4}-\d{4}-\d{4}-\d{4}$/';

// ํ•˜์ดํ”ˆ ์—†์ด๋„ ํ—ˆ์šฉ
$card_flexible = '/^\d{4}-?\d{4}-?\d{4}-?\d{4}$/';

function maskCardNumber($card) {
    // ๋งˆ์ง€๋ง‰ 4์ž๋ฆฌ๋งŒ ๋ณด์ด๊ฒŒ
    return preg_replace('/^(\d{4})-?(\d{4})-?(\d{4})-?(\d{4})$/', '****-****-****-$4', $card);
}

echo maskCardNumber("1234-5678-9012-3456"); // ****-****-****-3456
echo maskCardNumber("1234567890123456"); // ****-****-****-3456
?>

๐Ÿ“… ๋‚ ์งœ ํ˜•์‹ ๊ฒ€์ฆ

<?php
// YYYY-MM-DD ํ˜•์‹
$date_pattern = '/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/';

// YYYY.MM.DD ๋˜๋Š” YYYY/MM/DD
$date_flexible = '/^\d{4}[.\/-](0[1-9]|1[0-2])[.\/-](0[1-9]|[12]\d|3[01])$/';

function convertDateFormat($date) {
    // ๋‹ค์–‘ํ•œ ํ˜•์‹์„ YYYY-MM-DD๋กœ ํ†ต์ผ
    if (preg_match('/^(\d{4})[.\/-](\d{1,2})[.\/-](\d{1,2})$/', $date, $matches)) {
        return sprintf('%04d-%02d-%02d', $matches[1], $matches[2], $matches[3]);
    }
    return $date;
}

echo convertDateFormat("2024.3.5"); // 2024-03-05
echo convertDateFormat("2024/12/25"); // 2024-12-25
?>

๐Ÿ  ์ฃผ์†Œ ๋ฐ ์šฐํŽธ๋ฒˆํ˜ธ

<?php
// ํ•œ๊ตญ ์šฐํŽธ๋ฒˆํ˜ธ (5์ž๋ฆฌ)
$zipcode_pattern = '/^\d{5}$/';

// ๊ตฌ ์šฐํŽธ๋ฒˆํ˜ธ (123-456) ํ˜•์‹๋„ ํ—ˆ์šฉ
$zipcode_old = '/^\d{3}-?\d{3}$/';

// ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ (์•ž์ž๋ฆฌ๋งŒ, ๋’ท์ž๋ฆฌ ๋งˆ์Šคํ‚น)
$jumin_pattern = '/^(\d{6})-?([1-4]\d{6})$/';

function maskJumin($jumin) {
    return preg_replace('/^(\d{6})-?(\d{7})$/', '$1-*******', $jumin);
}

echo maskJumin("900101-1234567"); // 900101-*******
?>

๐Ÿš€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ: ๊ทธ๋ฃน๊ณผ ์บก์ฒ˜

์ •๊ทœํ‘œํ˜„์‹์˜ ์ง„์งœ ๊ฐ•๋ ฅํ•จ์€ ๊ทธ๋ฃนํ™”์™€ ์บก์ฒ˜์— ์žˆ์–ด!
ํŒจํ„ด์˜ ์ผ๋ถ€๋ฅผ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด์„œ ๋‚˜์ค‘์— ์žฌ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๐ŸŽ

์บก์ฒ˜ ๊ทธ๋ฃน (Capturing Groups)

๊ด„ํ˜ธ ()๋กœ ๋ฌถ์œผ๋ฉด ๊ทธ ๋ถ€๋ถ„์ด ๋ณ„๋„๋กœ ์บก์ฒ˜๋ผ์„œ ๋ฐฐ์—ด์— ์ €์žฅ๋ผ.

<?php
$text = "์ œ ์ด๋ฉ”์ผ์€ john.doe@example.com์ž…๋‹ˆ๋‹ค.";
$pattern = '/([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})/';

if (preg_match($pattern, $text, $matches)) {
    echo "์ „์ฒด ๋งค์นญ: " . $matches[0] . "\n"; // john.doe@example.com
    echo "์‚ฌ์šฉ์ž๋ช…: " . $matches[1] . "\n"; // john.doe
    echo "๋„๋ฉ”์ธ: " . $matches[2] . "\n"; // example
    echo "์ตœ์ƒ์œ„ ๋„๋ฉ”์ธ: " . $matches[3] . "\n"; // com
}

// ์ด๋ฆ„ ๋ถ„๋ฆฌํ•˜๊ธฐ
$name = "ํ™๊ธธ๋™";
$pattern = '/^([๊ฐ€-ํžฃ])([๊ฐ€-ํžฃ]+)$/';

if (preg_match($pattern, $name, $matches)) {
    echo "์„ฑ: " . $matches[1] . "\n"; // ํ™
    echo "์ด๋ฆ„: " . $matches[2] . "\n"; // ๊ธธ๋™
}
?>

๋ช…๋ช…๋œ ์บก์ฒ˜ ๊ทธ๋ฃน (Named Capturing Groups)

์ˆซ์ž ์ธ๋ฑ์Šค ๋Œ€์‹  ์ด๋ฆ„์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ์ฝ๊ธฐ ์‰ฌ์›Œ์ ธ!
(?P<name>pattern) ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•ด.

<?php
$text = "์ƒ๋…„์›”์ผ: 1990-05-15";
$pattern = '/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/';

if (preg_match($pattern, $text, $matches)) {
    echo "๋…„: " . $matches['year'] . "\n"; // 1990
    echo "์›”: " . $matches['month'] . "\n"; // 05
    echo "์ผ: " . $matches['day'] . "\n"; // 15
}

// URL ํŒŒ์‹ฑ
$url = "https://www.example.com:8080/path/to/page?id=123";
$pattern = '/^(?P<protocol>https?):\/\/(?P<domain>[^:\/]+)(:(?P<port>\d+))?(?P<path>\/[^?]*)?(\?(?P<query>.*))?$/';

if (preg_match($pattern, $url, $matches)) {
    echo "ํ”„๋กœํ† ์ฝœ: " . $matches['protocol'] . "\n"; // https
    echo "๋„๋ฉ”์ธ: " . $matches['domain'] . "\n"; // www.example.com
    echo "ํฌํŠธ: " . ($matches['port'] ?? '๊ธฐ๋ณธ') . "\n"; // 8080
    echo "๊ฒฝ๋กœ: " . ($matches['path'] ?? '/') . "\n"; // /path/to/page
    echo "์ฟผ๋ฆฌ: " . ($matches['query'] ?? '์—†์Œ') . "\n"; // id=123
}
?>

๋น„์บก์ฒ˜ ๊ทธ๋ฃน (Non-Capturing Groups)

๊ทธ๋ฃนํ™”๋Š” ํ•˜๊ณ  ์‹ถ์€๋ฐ ์บก์ฒ˜๋Š” ์•ˆ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ด.
(?:pattern) ํ˜•์‹์ด์•ผ. ์„ฑ๋Šฅ๋„ ์•ฝ๊ฐ„ ๋” ์ข‹์•„!

<?php
// ์บก์ฒ˜ ๊ทธ๋ฃน ์‚ฌ์šฉ (๋ถˆํ•„์š”ํ•œ ์บก์ฒ˜ ๋ฐœ์ƒ)
$pattern1 = '/(http|https):\/\/([a-z.]+)/';

// ๋น„์บก์ฒ˜ ๊ทธ๋ฃน ์‚ฌ์šฉ (ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์บก์ฒ˜)
$pattern2 = '/(?:http|https):\/\/([a-z.]+)/';

$url = "https://example.com";

preg_match($pattern1, $url, $matches1);
print_r($matches1);
// Array ( [0] => https://example.com [1] => https [2] => example.com )

preg_match($pattern2, $url, $matches2);
print_r($matches2);
// Array ( [0] => https://example.com [1] => example.com )
// [1]์ด http/https๊ฐ€ ์•„๋‹ˆ๋ผ ๋„๋ฉ”์ธ์ด ๋จ!
?>

์—ญ์ฐธ์กฐ (Backreferences)

์•ž์—์„œ ์บก์ฒ˜ํ•œ ๊ทธ๋ฃน์„ ํŒจํ„ด ์•ˆ์—์„œ ๋‹ค์‹œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์–ด.
\1, \2 ๊ฐ™์€ ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•˜์ง€.

<?php
// ๋ฐ˜๋ณต๋˜๋Š” ๋‹จ์–ด ์ฐพ๊ธฐ
$text = "์ด๊ฒƒ์€ ์ด๊ฒƒ์€ ์ค‘๋ณต๋œ ๋‹จ์–ด์ž…๋‹ˆ๋‹ค.";
$pattern = '/(\b\w+\b)\s+\1/u';

if (preg_match($pattern, $text, $matches)) {
    echo "์ค‘๋ณต ๋‹จ์–ด: " . $matches[1]; // ์ด๊ฒƒ์€
}

// HTML ํƒœ๊ทธ ์Œ ๋งค์นญ
$html = "<div>๋‚ด์šฉ</div>";
$pattern = '/<(\w+)>.*?<\/\1>/';

if (preg_match($pattern, $html)) {
    echo "์˜ฌ๋ฐ”๋ฅธ ํƒœ๊ทธ ์Œ์ž…๋‹ˆ๋‹ค.";
}

// ๊ฐ™์€ ๋”ฐ์˜ดํ‘œ๋กœ ๋ฌถ์ธ ๋ฌธ์ž์—ด ์ฐพ๊ธฐ
$text = 'He said "Hello" and she said \'Hi\'';
$pattern = '/(["\']).*?\1/';

preg_match_all($pattern, $text, $matches);
print_r($matches[0]);
// Array ( [0] => "Hello" [1] => 'Hi' )
?>

โš™๏ธ ์ •๊ทœํ‘œํ˜„์‹ ํ”Œ๋ž˜๊ทธ(Modifiers)

ํŒจํ„ด ๋’ค์— ๋ถ™์ด๋Š” ํ”Œ๋ž˜๊ทธ๋กœ ๋งค์นญ ๋™์ž‘์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์–ด.
PHP PCRE์—์„œ ์ž์ฃผ ์“ฐ๋Š” ํ”Œ๋ž˜๊ทธ๋“ค์„ ์•Œ์•„๋ณด์ž! ๐ŸŽš๏ธ

ํ”Œ๋ž˜๊ทธ ์ด๋ฆ„ ์„ค๋ช…
i PCRE_CASELESS ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์•ˆ ํ•จ
m PCRE_MULTILINE ^์™€ $๊ฐ€ ๊ฐ ์ค„์˜ ์‹œ์ž‘/๋์— ๋งค์นญ
s PCRE_DOTALL .(์ )์ด ์ค„๋ฐ”๊ฟˆ ๋ฌธ์ž๋„ ๋งค์นญ
x PCRE_EXTENDED ํŒจํ„ด ๋‚ด ๊ณต๋ฐฑ๊ณผ ์ฃผ์„ ๋ฌด์‹œ
u PCRE_UTF8 UTF-8๋กœ ์ฒ˜๋ฆฌ (ํ•œ๊ธ€ ๋“ฑ)
U PCRE_UNGREEDY ์ˆ˜๋Ÿ‰์ž๋ฅผ non-greedy๋กœ ๋ณ€๊ฒฝ
A PCRE_ANCHORED ๋ฌธ์ž์—ด ์‹œ์ž‘์—์„œ๋งŒ ๋งค์นญ
D PCRE_DOLLAR_ENDONLY $๊ฐ€ ๋ฌธ์ž์—ด ๋์—๋งŒ ๋งค์นญ

ํ”Œ๋ž˜๊ทธ ์‚ฌ์šฉ ์˜ˆ์ œ

<?php
// i ํ”Œ๋ž˜๊ทธ: ๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œ
$text = "Hello World";
echo preg_match('/hello/i', $text) ? "๋งค์นญ" : "๋ถˆ์ผ์น˜"; // ๋งค์นญ

// m ํ”Œ๋ž˜๊ทธ: ๋ฉ€ํ‹ฐ๋ผ์ธ ๋ชจ๋“œ
$text = "์ฒซ ๋ฒˆ์งธ ์ค„\n๋‘ ๋ฒˆ์งธ ์ค„\n์„ธ ๋ฒˆ์งธ ์ค„";
preg_match_all('/^.+$/m', $text, $matches);
print_r($matches[0]);
// Array ( [0] => ์ฒซ ๋ฒˆ์งธ ์ค„ [1] => ๋‘ ๋ฒˆ์งธ ์ค„ [2] => ์„ธ ๋ฒˆ์งธ ์ค„ )

// s ํ”Œ๋ž˜๊ทธ: ์ ์ด ์ค„๋ฐ”๊ฟˆ๋„ ๋งค์นญ
$text = "์ฒซ ์ค„\n๋‘˜์งธ ์ค„";
echo preg_match('/์ฒซ ์ค„.๋‘˜์งธ ์ค„/s', $text) ? "๋งค์นญ" : "๋ถˆ์ผ์น˜"; // ๋งค์นญ

// u ํ”Œ๋ž˜๊ทธ: UTF-8 ์ฒ˜๋ฆฌ (ํ•œ๊ธ€ ์‚ฌ์šฉ ์‹œ ํ•„์ˆ˜!)
$text = "์•ˆ๋…•ํ•˜์„ธ์š”";
preg_match_all('/./u', $text, $matches);
echo count($matches[0]); // 5 (u ์—†์œผ๋ฉด ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๊ณ„์‚ฐ๋จ)

// x ํ”Œ๋ž˜๊ทธ: ๊ฐ€๋…์„ฑ ์ข‹์€ ํŒจํ„ด ์ž‘์„ฑ
$pattern = '/
    ^                   # ๋ฌธ์ž์—ด ์‹œ์ž‘
    (?P<year>\d{4})     # ๋…„๋„ 4์ž๋ฆฌ
    -                   # ํ•˜์ดํ”ˆ
    (?P<month>\d{2})    # ์›” 2์ž๋ฆฌ
    -                   # ํ•˜์ดํ”ˆ
    (?P<day>\d{2})      # ์ผ 2์ž๋ฆฌ
    $                   # ๋ฌธ์ž์—ด ๋
/x';

preg_match($pattern, "2024-03-15", $matches);
print_r($matches);

// U ํ”Œ๋ž˜๊ทธ: non-greedy ๊ธฐ๋ณธ๊ฐ’
$html = "<div>์ฒซ ๋ฒˆ์งธ</div><div>๋‘ ๋ฒˆ์งธ</div>";
preg_match_all('/<div>.*<\/div>/U', $html, $matches);
print_r($matches[0]);
// Array ( [0] => <div>์ฒซ ๋ฒˆ์งธ</div> [1] => <div>๋‘ ๋ฒˆ์งธ</div> )
?>
๐Ÿ’ก ํ•œ๊ธ€ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ผญ ๊ธฐ์–ตํ•˜์„ธ์š”! ํ•œ๊ธ€์ด๋‚˜ ๋‹ค๋ฅธ ๋ฉ€ํ‹ฐ๋ฐ”์ดํŠธ ๋ฌธ์ž๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ u ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ถ™์—ฌ์•ผ ํ•ด์š”.
์•ˆ ๊ทธ๋Ÿฌ๋ฉด ๋ฌธ์ž๊ฐ€ ๊นจ์ง€๊ฑฐ๋‚˜ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์–ด์š”.

์˜ˆ: /[๊ฐ€-ํžฃ]+/u โ† ์ด๋ ‡๊ฒŒ!

๐ŸŽช Lookahead์™€ Lookbehind (์ „๋ฐฉํƒ์ƒ‰/ํ›„๋ฐฉํƒ์ƒ‰)

์ด๊ฑด ์ข€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์ธ๋ฐ, ์•Œ์•„๋‘๋ฉด ์—„์ฒญ ์œ ์šฉํ•ด!
ํŠน์ • ํŒจํ„ด์˜ ์•ž์ด๋‚˜ ๋’ค๋ฅผ ํ™•์ธํ•˜๋˜, ์‹ค์ œ๋กœ ๋งค์นญ์—๋Š” ํฌํ•จ์‹œํ‚ค์ง€ ์•Š๋Š” ๊ฑฐ์•ผ. ๐Ÿ”ญ

Positive Lookahead (?=...)

"์ด ํŒจํ„ด ๋’ค์— ํŠน์ • ํŒจํ„ด์ด ์žˆ์–ด์•ผ ํ•ด" ๋ผ๋Š” ์กฐ๊ฑด์ด์•ผ.

<?php
// ์ˆซ์ž ๋’ค์— '์›'์ด ์žˆ๋Š” ์ˆซ์ž๋งŒ ์ฐพ๊ธฐ
$text = "๊ฐ€๊ฒฉ์€ 10000์›์ด๊ณ , ์ˆ˜๋Ÿ‰์€ 5๊ฐœ์ž…๋‹ˆ๋‹ค.";
preg_match_all('/\d+(?=์›)/', $text, $matches);
print_r($matches[0]); // Array ( [0] => 10000 )

// ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ: ์ตœ์†Œ 8์ž, ์ˆซ์ž ํฌํ•จ ํ•„์ˆ˜
$password_pattern = '/^(?=.*\d).{8,}$/';
echo preg_match($password_pattern, "pass1234") ? "์œ ํšจ" : "๋ฌดํšจ"; // ์œ ํšจ

// ์—ฌ๋Ÿฌ ์กฐ๊ฑด ๋™์‹œ ์ฒดํฌ
$strong_pass = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/';
// ์†Œ๋ฌธ์ž, ๋Œ€๋ฌธ์ž, ์ˆซ์ž, ํŠน์ˆ˜๋ฌธ์ž ๋ชจ๋‘ ํฌํ•จ
?>

Negative Lookahead (?!...)

"์ด ํŒจํ„ด ๋’ค์— ํŠน์ • ํŒจํ„ด์ด ์—†์–ด์•ผ ํ•ด" ๋ผ๋Š” ์กฐ๊ฑด์ด์•ผ.

<?php
// ์ˆซ์ž ๋’ค์— '์›'์ด ์—†๋Š” ์ˆซ์ž๋งŒ ์ฐพ๊ธฐ
$text = "๊ฐ€๊ฒฉ์€ 10000์›์ด๊ณ , ์ˆ˜๋Ÿ‰์€ 5๊ฐœ์ž…๋‹ˆ๋‹ค.";
preg_match_all('/\d+(?!์›)/', $text, $matches);
print_r($matches[0]); // Array ( [0] => 1000 [1] => 5 )
// 10000์˜ ๋งˆ์ง€๋ง‰ 0๊ณผ 5๊ฐ€ ๋งค์นญ๋จ

// ํŠน์ • ๋‹จ์–ด๋ฅผ ์ œ์™ธํ•œ ๋‹จ์–ด ์ฐพ๊ธฐ
$text = "apple banana orange apple grape";
preg_match_all('/\b(?!apple\b)\w+/', $text, $matches);
print_r($matches[0]); // Array ( [0] => banana [1] => orange [2] => grape )
?>

Positive Lookbehind (?<=...)

"์ด ํŒจํ„ด ์•ž์— ํŠน์ • ํŒจํ„ด์ด ์žˆ์–ด์•ผ ํ•ด" ๋ผ๋Š” ์กฐ๊ฑด์ด์•ผ.

<?php
// '$' ๊ธฐํ˜ธ ๋’ค์˜ ์ˆซ์ž๋งŒ ์ฐพ๊ธฐ
$text = "๊ฐ€๊ฒฉ: $100, ํ• ์ธ: $20, ์ˆ˜๋Ÿ‰: 5๊ฐœ";
preg_match_all('/(?<=\$)\d+/', $text, $matches);
print_r($matches[0]); // Array ( [0] => 100 [1] => 20 )

// '@' ๋’ค์˜ ๋„๋ฉ”์ธ๋งŒ ์ถ”์ถœ
$text = "์—ฐ๋ฝ์ฒ˜: user@example.com, admin@test.co.kr";
preg_match_all('/(?<=@)[a-z0-9.-]+/', $text, $matches);
print_r($matches[0]); // Array ( [0] => example.com [1] => test.co.kr )
?>

Negative Lookbehind (?<!...)

"์ด ํŒจํ„ด ์•ž์— ํŠน์ • ํŒจํ„ด์ด ์—†์–ด์•ผ ํ•ด" ๋ผ๋Š” ์กฐ๊ฑด์ด์•ผ.

<?php
// '$' ๊ธฐํ˜ธ๊ฐ€ ์•ž์— ์—†๋Š” ์ˆซ์ž๋งŒ ์ฐพ๊ธฐ
$text = "๊ฐ€๊ฒฉ: $100, ์ˆ˜๋Ÿ‰: 5๊ฐœ";
preg_match_all('/(?<!\$)\b\d+\b/', $text, $matches);
print_r($matches[0]); // Array ( [0] => 5 )

// 'http://' ๋˜๋Š” 'https://'๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋Š” URL ์ฐพ๊ธฐ
$text = "example.com, https://secure.com, test.org";
preg_match_all('/(?<!https?:\/\/)\b[a-z]+\.com\b/', $text, $matches);
print_r($matches[0]); // Array ( [0] => example.com )
?>
โš ๏ธ Lookbehind ์ œํ•œ์‚ฌํ•ญ Lookbehind๋Š” ๊ณ ์ • ๊ธธ์ด ํŒจํ„ด๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.
์ฆ‰, *, + ๊ฐ™์€ ๊ฐ€๋ณ€ ์ˆ˜๋Ÿ‰์ž๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€!

โŒ (?<=\d+) - ์•ˆ ๋จ
โœ… (?<=\d{3}) - ๋จ
Lookaround ๋™์ž‘ ์›๋ฆฌ Lookahead (์ „๋ฐฉํƒ์ƒ‰) ํ˜„์žฌ ์œ„์น˜์—์„œ ์•ž์ชฝ์„ ํ™•์ธ (?=pattern) (?!pattern) ๊ธ์ • (์žˆ์–ด์•ผ ํ•จ) ๋ถ€์ • (์—†์–ด์•ผ ํ•จ) Lookbehind (ํ›„๋ฐฉํƒ์ƒ‰) ํ˜„์žฌ ์œ„์น˜์—์„œ ๋’ค์ชฝ์„ ํ™•์ธ (?<=pattern) (?<!pattern) ๊ธ์ • (์žˆ์–ด์•ผ ํ•จ) ๋ถ€์ • (์—†์–ด์•ผ ํ•จ) ์‹ค์ „ ์˜ˆ์ œ: ๊ฐ€๊ฒฉ ์ถ”์ถœ ํ…์ŠคํŠธ: "์ƒํ’ˆ ๊ฐ€๊ฒฉ์€ $100์ด๊ณ , ๋ฐฐ์†ก๋น„๋Š” $10์ž…๋‹ˆ๋‹ค." ํŒจํ„ด: /(?<=\$)\d+/ $ ๊ธฐํ˜ธ ๋’ค์˜ ์ˆซ์ž๋งŒ ์ถ”์ถœ ๊ฒฐ๊ณผ: [100, 10] $ ๊ธฐํ˜ธ๋Š” ์ œ์™ธ๋จ

๐Ÿ›ก๏ธ ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ ๊ณ ๋ ค์‚ฌํ•ญ

์ •๊ทœํ‘œํ˜„์‹์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ์ž˜๋ชป ์“ฐ๋ฉด ๋ณด์•ˆ ๋ฌธ์ œ๋‚˜ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.
์‹ค๋ฌด์—์„œ ๊ผญ ์•Œ์•„์•ผ ํ•  ์ฃผ์˜์‚ฌํ•ญ๋“ค์„ ์ •๋ฆฌํ•ด๋ดค์–ด! โšก

ReDoS (Regular Expression Denial of Service) ๊ณต๊ฒฉ

์•…์˜์ ์œผ๋กœ ์ž‘์„ฑ๋œ ์ž…๋ ฅ๊ฐ’์œผ๋กœ ์ •๊ทœํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ๋Š˜๋ ค์„œ
์„œ๋ฒ„๋ฅผ ๋งˆ๋น„์‹œํ‚ค๋Š” ๊ณต๊ฒฉ์ด์•ผ. ํŠนํžˆ ์ค‘์ฒฉ๋œ ์ˆ˜๋Ÿ‰์ž๊ฐ€ ์œ„ํ—˜ํ•ด!

โš ๏ธ ์œ„ํ—˜ํ•œ ํŒจํ„ด ์˜ˆ์‹œ โŒ /(a+)+$/
โŒ /(a|a)*$/
โŒ /(a|ab)*$/

์ด๋Ÿฐ ํŒจํ„ด๋“ค์€ ์ž…๋ ฅ์ด ๊ธธ์–ด์งˆ์ˆ˜๋ก ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ํญ๋ฐœ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•ด์š”!
<?php
// ๋‚˜์œ ์˜ˆ: ReDoS ์ทจ์•ฝ
$bad_pattern = '/(a+)+$/';
$input = str_repeat('a', 30) . 'b'; // aaaa...aaab
// ์ด ์ž…๋ ฅ์€ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆผ!

// ์ข‹์€ ์˜ˆ: ์•ˆ์ „ํ•œ ํŒจํ„ด
$good_pattern = '/^a+$/';
// ๋˜๋Š”
$good_pattern = '/^a{1,100}$/'; // ์ตœ๋Œ€ ๊ธธ์ด ์ œํ•œ

// ํƒ€์ž„์•„์›ƒ ์„ค์ •์œผ๋กœ ๋ณดํ˜ธ
ini_set('pcre.backtrack_limit', '100000');
ini_set('pcre.recursion_limit', '100000');

function safeMatch($pattern, $subject, $timeout = 1) {
    $start = microtime(true);
    $result = @preg_match($pattern, $subject);
    $elapsed = microtime(true) - $start;
    
    if ($elapsed > $timeout) {
        error_log("์ •๊ทœํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ ์ดˆ๊ณผ: {$elapsed}์ดˆ");
        return false;
    }
    
    return $result;
}
?>

์ž…๋ ฅ๊ฐ’ ๊ฒ€์ฆ๊ณผ ์ด์Šค์ผ€์ดํ”„

์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์ •๊ทœํ‘œํ˜„์‹์— ์ง์ ‘ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ์ด์Šค์ผ€์ดํ”„ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์•ผ ํ•ด!

<?php
// ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ํŒจํ„ด์— ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ
$user_input = "example.com"; // ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๋„๋ฉ”์ธ

// ๋‚˜์œ ์˜ˆ: ์ง์ ‘ ์‚ฌ์šฉ
$bad_pattern = "/$user_input/"; // ์œ„ํ—˜!

// ์ข‹์€ ์˜ˆ: preg_quote๋กœ ์ด์Šค์ผ€์ดํ”„
$safe_pattern = '/' . preg_quote($user_input, '/') . '/';

// preg_quote ์˜ˆ์‹œ
echo preg_quote("example.com"); // example\.com
echo preg_quote("$100.00"); // \$100\.00
echo preg_quote("a+b*c?"); // a\+b\*c\?

// ๊ฒ€์ƒ‰์–ด ํ•˜์ด๋ผ์ดํŠธ ์˜ˆ์ œ
function highlightSearch($text, $search) {
    $safe_search = preg_quote($search, '/');
    return preg_replace("/($safe_search)/i", '<mark>$1</mark>', $text);
}

echo highlightSearch("Hello World", "world");
// Hello <mark>World</mark>
?>

์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

๐Ÿš€ ์ •๊ทœํ‘œํ˜„์‹ ์„ฑ๋Šฅ ํ–ฅ์ƒ ๋ฐฉ๋ฒ• 1. ๊ตฌ์ฒด์ ์ธ ํŒจํ„ด ์‚ฌ์šฉ
โŒ /.*@.*/
โœ… /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/

2. ์•ต์ปค ํ™œ์šฉ
^์™€ $๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ํƒ์ƒ‰์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด์š”.

3. ๋น„์บก์ฒ˜ ๊ทธ๋ฃน ์‚ฌ์šฉ
์บก์ฒ˜๊ฐ€ ํ•„์š” ์—†์œผ๋ฉด (?:...)๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

4. ์›์ž ๊ทธ๋ฃน (Atomic Groups)
(?>...)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฑํŠธ๋ž˜ํ‚น์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.

5. ๋‹จ์ˆœํ•œ ๋ฌธ์ž์—ด ํ•จ์ˆ˜ ์šฐ์„  ๊ณ ๋ ค
๊ฐ„๋‹จํ•œ ์ž‘์—…์€ strpos(), str_replace() ๋“ฑ์ด ๋” ๋น ๋ฅผ ์ˆ˜ ์žˆ์–ด์š”.
<?php
// ์„ฑ๋Šฅ ๋น„๊ต ์˜ˆ์ œ
$text = str_repeat("Hello World ", 10000);

// ๋ฐฉ๋ฒ• 1: ์ •๊ทœํ‘œํ˜„์‹
$start = microtime(true);
$count1 = preg_match_all('/World/', $text);
$time1 = microtime(true) - $start;

// ๋ฐฉ๋ฒ• 2: substr_count (๋” ๋น ๋ฆ„!)
$start = microtime(true);
$count2 = substr_count($text, 'World');
$time2 = microtime(true) - $start;

echo "์ •๊ทœํ‘œํ˜„์‹: {$time1}์ดˆ\n";
echo "substr_count: {$time2}์ดˆ\n";
// substr_count๊ฐ€ ํ›จ์”ฌ ๋น ๋ฆ„!

// ์›์ž ๊ทธ๋ฃน ์˜ˆ์ œ
$pattern_slow = '/(a+)b/'; // ๋ฐฑํŠธ๋ž˜ํ‚น ๋ฐœ์ƒ
$pattern_fast = '/(?>a+)b/'; // ๋ฐฑํŠธ๋ž˜ํ‚น ๋ฐฉ์ง€

// ์ปดํŒŒ์ผ๋œ ํŒจํ„ด ์žฌ์‚ฌ์šฉ
class RegexCache {
    private static $cache = [];
    
    public static function match($pattern, $subject) {
        if (!isset(self::$cache[$pattern])) {
            // ํŒจํ„ด ๊ฒ€์ฆ ๋ฐ ์บ์‹ฑ
            self::$cache[$pattern] = $pattern;
        }
        return preg_match(self::$cache[$pattern], $subject);
    }
}
?>

๐ŸŽ“ ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š”
ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ๐Ÿ› ๏ธ

์ด๋Ÿฐ ํด๋ž˜์Šค ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋‘๋ฉด ํ”„๋กœ์ ํŠธ์—์„œ ๊ณ„์† ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ •๋ง ํŽธํ•ด.
์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ํ•  ๋•Œ๋„ ์œ ์šฉํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

<?php
/**
 * ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค
 * ์ •๊ทœํ‘œํ˜„์‹์„ ํ™œ์šฉํ•œ ๋‹ค์–‘ํ•œ ํ…์ŠคํŠธ ๊ฒ€์ฆ ๋ฐ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ์ œ๊ณต
 */
class TextValidator {
    
    /**
     * ์ด๋ฉ”์ผ ๊ฒ€์ฆ
     */
    public static function isValidEmail($email) {
        $pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
        return preg_match($pattern, $email) === 1;
    }
    
    /**
     * ํ•œ๊ตญ ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ๊ฒ€์ฆ
     */
    public static function isValidPhone($phone) {
        $pattern = '/^01[0-9]-?\d{3,4}-?\d{4}$/';
        return preg_match($pattern, $phone) === 1;
    }
    
    /**
     * ์ „ํ™”๋ฒˆํ˜ธ ํฌ๋งทํŒ…
     */
    public static function formatPhone($phone) {
        $numbers = preg_replace('/[^0-9]/', '', $phone);
        
        if (preg_match('/^(01[0-9])(\d{3,4})(\d{4})$/', $numbers, $matches)) {
            return $matches[1] . '-' . $matches[2] . '-' . $matches[3];
        }
        
        if (preg_match('/^(0\d{1,2})(\d{3,4})(\d{4})$/', $numbers, $matches)) {
            return $matches[1] . '-' . $matches[2] . '-' . $matches[3];
        }
        
        return $phone;
    }
    
    /**
     * URL ๊ฒ€์ฆ
     */
    public static function isValidUrl($url) {
        $pattern = '/^https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/';
        return preg_match($pattern, $url) === 1;
    }
    
    /**
     * ํ…์ŠคํŠธ์—์„œ URL ์ถ”์ถœ
     */
    public static function extractUrls($text) {
        $pattern = '/https?:\/\/[^\s]+/';
        preg_match_all($pattern, $text, $matches);
        return $matches[0];
    }
    
    /**
     * ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„ ์ฒดํฌ
     */
    public static function checkPasswordStrength($password) {
        $patterns = [
            'length' => strlen($password) >= 8,
            'lowercase' => preg_match('/[a-z]/', $password),
            'uppercase' => preg_match('/[A-Z]/', $password),
            'number' => preg_match('/\d/', $password),
            'special' => preg_match('/[@$!%*?&#]/', $password)
        ];
        
        $score = array_sum($patterns);
        
        if ($score === 5) return '๋งค์šฐ ๊ฐ•๋ ฅ';
        if ($score >= 4) return '๊ฐ•๋ ฅ';
        if ($score >= 3) return '๋ณดํ†ต';
        if ($score >= 2) return '์•ฝํ•จ';
        return '๋งค์šฐ ์•ฝํ•จ';
    }
    
    /**
     * HTML ํƒœ๊ทธ ์ œ๊ฑฐ (XSS ๋ฐฉ์ง€)
     */
    public static function stripTags($text, $allowedTags = []) {
        if (empty($allowedTags)) {
            return preg_replace('/<[^>]+>/', '', $text);
        }
        
        $allowed = implode('|', array_map('preg_quote', $allowedTags));
        $pattern = '/<(?!\/?(' . $allowed . ')\b)[^>]+>/';
        return preg_replace($pattern, '', $text);
    }
    
    /**
     * ์ด๋ฉ”์ผ ๋งˆ์Šคํ‚น
     */
    public static function maskEmail($email) {
        return preg_replace('/^(.{2})[^@]+(@.+)$/', '$1***$2', $email);
    }
    
    /**
     * ์ „ํ™”๋ฒˆํ˜ธ ๋งˆ์Šคํ‚น
     */
    public static function maskPhone($phone) {
        return preg_replace('/^(\d{3})-?(\d{3,4})-?(\d{4})$/', '$1-****-$3', $phone);
    }
    
    /**
     * ์‹ ์šฉ์นด๋“œ ๋ฒˆํ˜ธ ๋งˆ์Šคํ‚น
     */
    public static function maskCardNumber($card) {
        return preg_replace('/^(\d{4})-?(\d{4})-?(\d{4})-?(\d{4})$/', '****-****-****-$4', $card);
    }
    
    /**
     * ํ…์ŠคํŠธ์—์„œ ์ด๋ฉ”์ผ ์ถ”์ถœ
     */
    public static function extractEmails($text) {
        $pattern = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/';
        preg_match_all($pattern, $text, $matches);
        return $matches[0];
    }
    
    /**
     * ํ•œ๊ธ€๋งŒ ์ถ”์ถœ
     */
    public static function extractKorean($text) {
        preg_match_all('/[๊ฐ€-ํžฃ]+/u', $text, $matches);
        return implode('', $matches[0]);
    }
    
    /**
     * ์ˆซ์ž๋งŒ ์ถ”์ถœ
     */
    public static function extractNumbers($text) {
        return preg_replace('/[^0-9]/', '', $text);
    }
    
    /**
     * ์—ฌ๋Ÿฌ ๊ณต๋ฐฑ์„ ํ•˜๋‚˜๋กœ
     */
    public static function normalizeSpaces($text) {
        return preg_replace('/\s+/', ' ', trim($text));
    }
    
    /**
     * ๊ฒ€์ƒ‰์–ด ํ•˜์ด๋ผ์ดํŠธ
     */
    public static function highlight($text, $keyword, $tag = 'mark') {
        $safe_keyword = preg_quote($keyword, '/');
        return preg_replace("/($safe_keyword)/iu", "<{$tag}>$1</{$tag}>", $text);
    }
    
    /**
     * ๋‚ ์งœ ํ˜•์‹ ๋ณ€ํ™˜
     */
    public static function convertDateFormat($date, $outputFormat = 'Y-m-d') {
        // ๋‹ค์–‘ํ•œ ์ž…๋ ฅ ํ˜•์‹ ์ง€์›
        $patterns = [
            '/^(\d{4})[.\/-](\d{1,2})[.\/-](\d{1,2})$/' => '$1-$2-$3',
            '/^(\d{1,2})[.\/-](\d{1,2})[.\/-](\d{4})$/' => '$3-$1-$2',
        ];
        
        foreach ($patterns as $pattern => $replacement) {
            if (preg_match($pattern, $date)) {
                $normalized = preg_replace($pattern, $replacement, $date);
                $timestamp = strtotime($normalized);
                if ($timestamp !== false) {
                    return date($outputFormat, $timestamp);
                }
            }
        }
        
        return $date;
    }
    
    /**
     * ์•ˆ์ „ํ•œ ํŒŒ์ผ๋ช… ์ƒ์„ฑ
     */
    public static function sanitizeFilename($filename) {
        // ํ™•์žฅ์ž ๋ถ„๋ฆฌ
        $info = pathinfo($filename);
        $name = $info['filename'];
        $ext = isset($info['extension']) ? '.' . $info['extension'] : '';
        
        // ํŠน์ˆ˜๋ฌธ์ž ์ œ๊ฑฐ, ๊ณต๋ฐฑ์„ ์–ธ๋”์Šค์ฝ”์–ด๋กœ
        $name = preg_replace('/[^a-zA-Z0-9๊ฐ€-ํžฃ_-]/', '_', $name);
        $name = preg_replace('/_+/', '_', $name);
        
        return $name . $ext;
    }
    
    /**
     * ์Šฌ๋Ÿฌ๊ทธ ์ƒ์„ฑ (URL ์นœํ™”์  ๋ฌธ์ž์—ด)
     */
    public static function createSlug($text) {
        // ์†Œ๋ฌธ์ž ๋ณ€ํ™˜
        $text = strtolower($text);
        
        // ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ํ•˜์ดํ”ˆ์œผ๋กœ
        $text = preg_replace('/[^a-z0-9๊ฐ€-ํžฃ]+/u', '-', $text);
        
        // ์—ฐ์†๋œ ํ•˜์ดํ”ˆ์„ ํ•˜๋‚˜๋กœ
        $text = preg_replace('/-+/', '-', $text);
        
        // ์•ž๋’ค ํ•˜์ดํ”ˆ ์ œ๊ฑฐ
        return trim($text, '-');
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
echo "=== ์ด๋ฉ”์ผ ๊ฒ€์ฆ ===\n";
var_dump(TextValidator::isValidEmail("user@example.com")); // true
var_dump(TextValidator::isValidEmail("invalid@")); // false

echo "\n=== ์ „ํ™”๋ฒˆํ˜ธ ํฌ๋งทํŒ… ===\n";
echo TextValidator::formatPhone("01012345678") . "\n"; // 010-1234-5678
echo TextValidator::formatPhone("0212345678") . "\n"; // 02-1234-5678

echo "\n=== ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„ ===\n";
echo TextValidator::checkPasswordStrength("Pass123!") . "\n"; // ๊ฐ•๋ ฅ
echo TextValidator::checkPasswordStrength("password") . "\n"; // ์•ฝํ•จ

echo "\n=== ๋งˆ์Šคํ‚น ===\n";
echo TextValidator::maskEmail("john.doe@example.com") . "\n"; // jo***@example.com
echo TextValidator::maskPhone("010-1234-5678") . "\n"; // 010-****-5678
echo TextValidator::maskCardNumber("1234-5678-9012-3456") . "\n"; // ****-****-****-3456

echo "\n=== ํ…์ŠคํŠธ ์ถ”์ถœ ===\n";
$text = "์—ฐ๋ฝ์ฒ˜: user@example.com, ์ „ํ™”: 010-1234-5678, ๊ฐ€๊ฒฉ: 10000์›";
print_r(TextValidator::extractEmails($text));
echo TextValidator::extractNumbers($text) . "\n"; // 01012345678 10000

echo "\n=== ํ•˜์ด๋ผ์ดํŠธ ===\n";
echo TextValidator::highlight("Hello World", "world") . "\n";
// Hello <mark>World</mark>

echo "\n=== ์Šฌ๋Ÿฌ๊ทธ ์ƒ์„ฑ ===\n";
echo TextValidator::createSlug("์•ˆ๋…•ํ•˜์„ธ์š”! PHP ์ •๊ทœํ‘œํ˜„์‹") . "\n";
// ์•ˆ๋…•ํ•˜์„ธ์š”-php-์ •๊ทœํ‘œํ˜„์‹

?>
๐Ÿ’ก ์‹ค๋ฌด ํ™œ์šฉ ํŒ ์ด ํด๋ž˜์Šค๋ฅผ ํ”„๋กœ์ ํŠธ์— ํฌํ•จ์‹œ์ผœ๋‘๋ฉด:
- ํšŒ์›๊ฐ€์ž… ํผ ๊ฒ€์ฆ
- ๊ฒŒ์‹œํŒ ์ž…๋ ฅ๊ฐ’ ์ •์ œ
- ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„
- ๋ฐ์ดํ„ฐ ๋งˆ์Šคํ‚น ์ฒ˜๋ฆฌ
- ํŒŒ์ผ ์—…๋กœ๋“œ ๊ฒ€์ฆ

๋“ฑ ๋‹ค์–‘ํ•œ ๊ณณ์—์„œ ๋ฐ”๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”!
์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ์ ํŠธ ์˜๋ขฐ๋ฐ›์„ ๋•Œ๋„ ์ด๋Ÿฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๊ฐ€ ์žˆ์œผ๋ฉด ๊ฐœ๋ฐœ ์†๋„๊ฐ€ ํ›จ์”ฌ ๋นจ๋ผ์ ธ์š”!

๐Ÿ› ๋””๋ฒ„๊น…๊ณผ ํ…Œ์ŠคํŠธ

์ •๊ทœํ‘œํ˜„์‹์€ ๋ณต์žกํ•ด์„œ ๋””๋ฒ„๊น…์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด.
ํšจ๊ณผ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”

์ •๊ทœํ‘œํ˜„์‹ ํ…Œ์ŠคํŠธ ๋„๊ตฌ

๐Ÿ› ๏ธ ์ถ”์ฒœ ์˜จ๋ผ์ธ ๋„๊ตฌ 1. regex101.com - ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ์ •๊ทœํ‘œํ˜„์‹ ํ…Œ์Šคํ„ฐ
- ์‹ค์‹œ๊ฐ„ ๋งค์นญ ๊ฒฐ๊ณผ
- ์ƒ์„ธํ•œ ์„ค๋ช…
- ๋‹ค์–‘ํ•œ ์–ธ์–ด ์ง€์› (PHP PCRE ํฌํ•จ)

2. regexr.com - ์‹œ๊ฐ์ ์œผ๋กœ ๋ณด๊ธฐ ์ข‹์€ ์ธํ„ฐํŽ˜์ด์Šค
- ์ปค๋ฎค๋‹ˆํ‹ฐ ํŒจํ„ด ๊ณต์œ 
- ์น˜ํŠธ์‹œํŠธ ์ œ๊ณต

3. regexpal.com - ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅธ ํ…Œ์ŠคํŠธ

PHP ์ฝ”๋“œ๋กœ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š” ํ•ญ์ƒ ๋‹ค์–‘ํ•œ ์ผ€์ด์Šค๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”!

์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ๋””๋ฒ„๊น…

<?php
/**
 * ์ •๊ทœํ‘œํ˜„์‹ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ—ฌํผ ํ•จ์ˆ˜
 */
function safePreg($function, ...$args) {
    $result = @$function(...$args);
    
    if ($result === false) {
        $error_code = preg_last_error();
        $errors = [
            PREG_NO_ERROR => '์—๋Ÿฌ ์—†์Œ',
            PREG_INTERNAL_ERROR => '๋‚ด๋ถ€ PCRE ์—๋Ÿฌ',
            PREG_BACKTRACK_LIMIT_ERROR => '๋ฐฑํŠธ๋ž™ ์ œํ•œ ์ดˆ๊ณผ',
            PREG_RECURSION_LIMIT_ERROR => '์žฌ๊ท€ ์ œํ•œ ์ดˆ๊ณผ',
            PREG_BAD_UTF8_ERROR => '์ž˜๋ชป๋œ UTF-8 ๋ฐ์ดํ„ฐ',
            PREG_BAD_UTF8_OFFSET_ERROR => '์ž˜๋ชป๋œ UTF-8 ์˜คํ”„์…‹',
        ];
        
        $error_msg = $errors[$error_code] ?? '์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ';
        throw new Exception("์ •๊ทœํ‘œํ˜„์‹ ์—๋Ÿฌ: {$error_msg}");
    }
    
    return $result;
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
try {
    $pattern = '/[a-z]+/';
    $text = "Hello World";
    $result = safePreg('preg_match', $pattern, $text);
    echo "๋งค์นญ ๊ฒฐ๊ณผ: {$result}\n";
} catch (Exception $e) {
    echo "์—๋Ÿฌ ๋ฐœ์ƒ: " . $e->getMessage() . "\n";
}

/**
 * ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๊ฒ€์ฆ
 */
function validatePattern($pattern) {
    $test = @preg_match($pattern, '');
    
    if ($test === false) {
        $error = error_get_last();
        return [
            'valid' => false,
            'error' => $error['message'] ?? '์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ'
        ];
    }
    
    return ['valid' => true];
}

// ํŒจํ„ด ๊ฒ€์ฆ ์˜ˆ์ œ
$patterns = [
    '/[a-z]+/' => '์˜ฌ๋ฐ”๋ฅธ ํŒจํ„ด',
    '/[a-z/' => '๋‹ซ๋Š” ๊ด„ํ˜ธ ๋ˆ„๋ฝ',
    '/(?P<name>[a-z]+)/' => '๋ช…๋ช…๋œ ๊ทธ๋ฃน',
];

foreach ($patterns as $pattern => $desc) {
    $result = validatePattern($pattern);
    echo "{$desc}: " . ($result['valid'] ? 'โœ“' : 'โœ—') . "\n";
    if (!$result['valid']) {
        echo "  ์—๋Ÿฌ: {$result['error']}\n";
    }
}

/**
 * ์ •๊ทœํ‘œํ˜„์‹ ์„ฑ๋Šฅ ์ธก์ •
 */
function benchmarkRegex($pattern, $text, $iterations = 1000) {
    $start = microtime(true);
    
    for ($i = 0; $i < $iterations; $i++) {
        preg_match($pattern, $text);
    }
    
    $elapsed = microtime(true) - $start;
    $avg = $elapsed / $iterations * 1000; // ๋ฐ€๋ฆฌ์ดˆ
    
    return [
        'total' => $elapsed,
        'average' => $avg,
        'iterations' => $iterations
    ];
}

// ์„ฑ๋Šฅ ๋น„๊ต
$text = str_repeat("Hello World ", 100);
$patterns = [
    'Simple' => '/World/',
    'Complex' => '/\b[A-Z][a-z]+\b/',
    'Lookahead' => '/\b(?=\w{5})\w+\b/',
];

echo "\n=== ์„ฑ๋Šฅ ๋ฒค์น˜๋งˆํฌ ===\n";
foreach ($patterns as $name => $pattern) {
    $result = benchmarkRegex($pattern, $text);
    printf("%s: %.4f ms (ํ‰๊ท )\n", $name, $result['average']);
}

/**
 * ์œ ๋‹› ํ…Œ์ŠคํŠธ ์˜ˆ์ œ
 */
class RegexTest {
    private $passed = 0;
    private $failed = 0;
    
    public function test($name, $pattern, $text, $expected) {
        $result = preg_match($pattern, $text);
        $success = ($result === 1) === $expected;
        
        if ($success) {
            $this->passed++;
            echo "โœ“ {$name}\n";
        } else {
            $this->failed++;
            echo "โœ— {$name}\n";
            echo "  ์˜ˆ์ƒ: " . ($expected ? '๋งค์นญ' : '๋ถˆ์ผ์น˜') . "\n";
            echo "  ์‹ค์ œ: " . ($result ? '๋งค์นญ' : '๋ถˆ์ผ์น˜') . "\n";
        }
    }
    
    public function summary() {
        $total = $this->passed + $this->failed;
        echo "\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ===\n";
        echo "ํ†ต๊ณผ: {$this->passed}/{$total}\n";
        echo "์‹คํŒจ: {$this->failed}/{$total}\n";
    }
}

// ์ด๋ฉ”์ผ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ
$tester = new RegexTest();
$email_pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';

echo "\n=== ์ด๋ฉ”์ผ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ ===\n";
$tester->test('์œ ํšจํ•œ ์ด๋ฉ”์ผ 1', $email_pattern, 'user@example.com', true);
$tester->test('์œ ํšจํ•œ ์ด๋ฉ”์ผ 2', $email_pattern, 'test.user@sub.domain.co.kr', true);
$tester->test('๋ฌดํšจํ•œ ์ด๋ฉ”์ผ 1', $email_pattern, 'invalid@', false);
$tester->test('๋ฌดํšจํ•œ ์ด๋ฉ”์ผ 2', $email_pattern, '@example.com', false);
$tester->test('๋ฌดํšจํ•œ ์ด๋ฉ”์ผ 3', $email_pattern, 'user@.com', false);

$tester->summary();
?>

๐Ÿ“– ์‹ค๋ฌด ํŒจํ„ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด๋“ค์„ ๋ชจ์•„์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๋งŒ๋“ค์–ด๋ดค์–ด.
๋ณต์‚ฌํ•ด์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ์ฃผ์„๋„ ์ž์„ธํžˆ ๋‹ฌ์•„๋’€์œผ๋‹ˆ๊นŒ ํ™œ์šฉํ•ด๋ด! ๐Ÿ“š

<?php
/**
 * ์ •๊ทœํ‘œํ˜„์‹ ํŒจํ„ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
 */
class RegexPatterns {
    
    // ===== ๊ธฐ๋ณธ ๊ฒ€์ฆ =====
    
    /** ์ด๋ฉ”์ผ (๊ธฐ๋ณธ) */
    const EMAIL = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
    
    /** ์ด๋ฉ”์ผ (์—„๊ฒฉ) */
    const EMAIL_STRICT = '/^(?!.*\.\.)(?!.*\.$)[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
    
    /** URL (http/https) */
    const URL = '/^https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/';
    
    /** URL (ํ”„๋กœํ† ์ฝœ ์„ ํƒ) */
    const URL_FLEXIBLE = '/^(https?|ftp):\/\/[^\s]+$/';
    
    // ===== ์ „ํ™”๋ฒˆํ˜ธ =====
    
    /** ํ•œ๊ตญ ํœด๋Œ€ํฐ (ํ•˜์ดํ”ˆ ํ•„์ˆ˜) */
    const PHONE_KR_MOBILE = '/^01[0-9]-\d{3,4}-\d{4}$/';
    
    /** ํ•œ๊ตญ ํœด๋Œ€ํฐ (ํ•˜์ดํ”ˆ ์„ ํƒ) */
    const PHONE_KR_MOBILE_FLEX = '/^01[0-9]-?\d{3,4}-?\d{4}$/';
    
    /** ํ•œ๊ตญ ์ผ๋ฐ˜์ „ํ™” */
    const PHONE_KR_LANDLINE = '/^0\d{1,2}-\d{3,4}-\d{4}$/';
    
    /** ๊ตญ์ œ ์ „ํ™”๋ฒˆํ˜ธ */
    const PHONE_INTERNATIONAL = '/^\+[1-9]\d{1,14}$/';
    
    // ===== ๋น„๋ฐ€๋ฒˆํ˜ธ =====
    
    /** ๋น„๋ฐ€๋ฒˆํ˜ธ (8์ž ์ด์ƒ, ์˜๋ฌธ+์ˆซ์ž) */
    const PASSWORD_MEDIUM = '/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/';
    
    /** ๋น„๋ฐ€๋ฒˆํ˜ธ (8์ž ์ด์ƒ, ์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž+์ˆซ์ž+ํŠน์ˆ˜๋ฌธ์ž) */
    const PASSWORD_STRONG = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/';
    
    /** ๋น„๋ฐ€๋ฒˆํ˜ธ (10์ž ์ด์ƒ, ๋ชจ๋“  ์กฐ๊ฑด) */
    const PASSWORD_VERY_STRONG = '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{10,}$/';
    
    // ===== ๋‚ ์งœ/์‹œ๊ฐ„ =====
    
    /** ๋‚ ์งœ (YYYY-MM-DD) */
    const DATE_ISO = '/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/';
    
    /** ๋‚ ์งœ (YYYY.MM.DD ๋˜๋Š” YYYY/MM/DD) */
    const DATE_FLEXIBLE = '/^\d{4}[.\/-](0[1-9]|1[0-2])[.\/-](0[1-9]|[12]\d|3[01])$/';
    
    /** ์‹œ๊ฐ„ (HH:MM) */
    const TIME_24H = '/^([01]\d|2[0-3]):[0-5]\d$/';
    
    /** ์‹œ๊ฐ„ (HH:MM:SS) */
    const TIME_24H_FULL = '/^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/';
    
    /** ๋‚ ์งœ+์‹œ๊ฐ„ (ISO 8601) */
    const DATETIME_ISO = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/';
    
    // ===== ์ˆซ์ž =====
    
    /** ์ •์ˆ˜ */
    const INTEGER = '/^-?\d+$/';
    
    /** ์–‘์˜ ์ •์ˆ˜ */
    const POSITIVE_INTEGER = '/^\d+$/';
    
    /** ์†Œ์ˆ˜ */
    const DECIMAL = '/^-?\d+(\.\d+)?$/';
    
    /** ํ†ตํ™” (์›ํ™”) */
    const CURRENCY_KRW = '/^\d{1,3}(,\d{3})*์›?$/';
    
    /** ํ†ตํ™” (๋‹ฌ๋Ÿฌ) */
    const CURRENCY_USD = '/^\$\d{1,3}(,\d{3})*(\.\d{2})?$/';
    
    /** ํผ์„ผํŠธ */
    const PERCENTAGE = '/^\d+(\.\d+)?%$/';
    
    // ===== ํ•œ๊ตญ ํŠนํ™” =====
    
    /** ํ•œ๊ธ€๋งŒ */
    const KOREAN_ONLY = '/^[๊ฐ€-ํžฃ]+$/u';
    
    /** ํ•œ๊ธ€+์˜๋ฌธ */
    const KOREAN_ENGLISH = '/^[๊ฐ€-ํžฃa-zA-Z]+$/u';
    
    /** ํ•œ๊ธ€+์˜๋ฌธ+์ˆซ์ž */
    const KOREAN_ENGLISH_NUMBER = '/^[๊ฐ€-ํžฃa-zA-Z0-9]+$/u';
    
    /** ์šฐํŽธ๋ฒˆํ˜ธ (5์ž๋ฆฌ) */
    const ZIPCODE_KR = '/^\d{5}$/';
    
    /** ์šฐํŽธ๋ฒˆํ˜ธ (๊ตฌํ˜• 6์ž๋ฆฌ) */
    const ZIPCODE_KR_OLD = '/^\d{3}-?\d{3}$/';
    
    /** ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ */
    const JUMIN = '/^\d{6}-?[1-4]\d{6}$/';
    
    /** ์‚ฌ์—…์ž๋“ฑ๋ก๋ฒˆํ˜ธ */
    const BUSINESS_NUMBER = '/^\d{3}-?\d{2}-?\d{5}$/';
    
    // ===== ์‹ ์šฉ์นด๋“œ =====
    
    /** ์‹ ์šฉ์นด๋“œ ๋ฒˆํ˜ธ */
    const CREDIT_CARD = '/^\d{4}-?\d{4}-?\d{4}-?\d{4}$/';
    
    /** Visa */
    const VISA = '/^4\d{3}-?\d{4}-?\d{4}-?\d{4}$/';
    
    /** MasterCard */
    const MASTERCARD = '/^5[1-5]\d{2}-?\d{4}-?\d{4}-?\d{4}$/';
    
    /** American Express */
    const AMEX = '/^3[47]\d{2}-?\d{6}-?\d{5}$/';
    
    // ===== IP ์ฃผ์†Œ =====
    
    /** IPv4 */
    const IPV4 = '/^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/';
    
    /** IPv6 (๊ฐ„๋‹จ) */
    const IPV6 = '/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/';
    
    // ===== ํŒŒ์ผ =====
    
    /** ์ด๋ฏธ์ง€ ํŒŒ์ผ ํ™•์žฅ์ž */
    const IMAGE_FILE = '/\.(jpg|jpeg|png|gif|bmp|svg|webp)$/i';
    
    /** ๋ฌธ์„œ ํŒŒ์ผ ํ™•์žฅ์ž */
    const DOCUMENT_FILE = '/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt)$/i';
    
    /** ์••์ถ• ํŒŒ์ผ ํ™•์žฅ์ž */
    const ARCHIVE_FILE = '/\.(zip|rar|7z|tar|gz)$/i';
    
    // ===== HTML/XML =====
    
    /** HTML ํƒœ๊ทธ */
    const HTML_TAG = '/<[^>]+>/';
    
    /** HTML ์ฃผ์„ */
    const HTML_COMMENT = '/<!--.*?-->/s';
    
    /** XML ํƒœ๊ทธ */
    const XML_TAG = '/<\/?[\w\s="\']+>/';
    
    // ===== ์†Œ์…œ๋ฏธ๋””์–ด =====
    
    /** ํ•ด์‹œํƒœ๊ทธ */
    const HASHTAG = '/#[\w๊ฐ€-ํžฃ]+/u';
    
    /** ๋ฉ˜์…˜ (@username) */
    const MENTION = '/@[\w๊ฐ€-ํžฃ]+/u';
    
    /** ํŠธ์œ„ํ„ฐ ์‚ฌ์šฉ์ž๋ช… */
    const TWITTER_USERNAME = '/^@?[A-Za-z0-9_]{1,15}$/';
    
    /** ์ธ์Šคํƒ€๊ทธ๋žจ ์‚ฌ์šฉ์ž๋ช… */
    const INSTAGRAM_USERNAME = '/^@?[A-Za-z0-9_.]{1,30}$/';
    
    // ===== ๊ธฐํƒ€ =====
    
    /** 16์ง„์ˆ˜ ์ƒ‰์ƒ ์ฝ”๋“œ */
    const HEX_COLOR = '/^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/';
    
    /** MAC ์ฃผ์†Œ */
    const MAC_ADDRESS = '/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/';
    
    /** UUID */
    const UUID = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i';
    
    /** Base64 */
    const BASE64 = '/^[A-Za-z0-9+\/]*={0,2}$/';
    
    /**
     * ํŒจํ„ด ๊ฒ€์ฆ ํ—ฌํผ
     */
    public static function validate($pattern, $text) {
        return preg_match($pattern, $text) === 1;
    }
    
    /**
     * ์—ฌ๋Ÿฌ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋งค์นญ๋˜๋Š”์ง€ ํ™•์ธ
     */
    public static function validateAny($patterns, $text) {
        foreach ($patterns as $pattern) {
            if (self::validate($pattern, $text)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * ๋ชจ๋“  ํŒจํ„ด์ด ๋งค์นญ๋˜๋Š”์ง€ ํ™•์ธ
     */
    public static function validateAll($patterns, $text) {
        foreach ($patterns as $pattern) {
            if (!self::validate($pattern, $text)) {
                return false;
            }
        }
        return true;
    }
}

// ์‚ฌ์šฉ ์˜ˆ์ œ
echo "=== ํŒจํ„ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์˜ˆ์ œ ===\n\n";

// ์ด๋ฉ”์ผ ๊ฒ€์ฆ
$email = "user@example.com";
echo "์ด๋ฉ”์ผ ๊ฒ€์ฆ: ";
echo RegexPatterns::validate(RegexPatterns::EMAIL, $email) ? "โœ“" : "โœ—";
echo "\n";

// ์ „ํ™”๋ฒˆํ˜ธ ๊ฒ€์ฆ
$phone = "010-1234-5678";
echo "์ „ํ™”๋ฒˆํ˜ธ ๊ฒ€์ฆ: ";
echo RegexPatterns::validate(RegexPatterns::PHONE_KR_MOBILE, $phone) ? "โœ“" : "โœ—";
echo "\n";

// ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„ ์ฒดํฌ
$password = "Pass123!";
echo "๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋„: ";
if (RegexPatterns::validate(RegexPatterns::PASSWORD_VERY_STRONG, $password)) {
    echo "๋งค์šฐ ๊ฐ•๋ ฅ";
} elseif (RegexPatterns::validate(RegexPatterns::PASSWORD_STRONG, $password)) {
    echo "๊ฐ•๋ ฅ";
} elseif (RegexPatterns::validate(RegexPatterns::PASSWORD_MEDIUM, $password)) {
    echo "๋ณดํ†ต";
} else {
    echo "์•ฝํ•จ";
}
echo "\n";

// ๋‚ ์งœ ํ˜•์‹ ์ฒดํฌ
$date = "2024-03-15";
echo "๋‚ ์งœ ํ˜•์‹: ";
echo RegexPatterns::validate(RegexPatterns::DATE_ISO, $date) ? "โœ“" : "โœ—";
echo "\n";

// ํ•œ๊ธ€ ์ด๋ฆ„ ์ฒดํฌ
$name = "ํ™๊ธธ๋™";
echo "ํ•œ๊ธ€ ์ด๋ฆ„: ";
echo RegexPatterns::validate(RegexPatterns::KOREAN_ONLY, $name) ? "โœ“" : "โœ—";
echo "\n";

// URL ์ฒดํฌ
$url = "https://www.example.com";
echo "URL ํ˜•์‹: ";
echo RegexPatterns::validate(RegexPatterns::URL, $url) ? "โœ“" : "โœ—";
echo "\n";

?>

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€, ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋ฉด ์ •๋ง ๋Œ€๋‹จํ•ด! ๐Ÿ‘
PHP ์ •๊ทœํ‘œํ˜„์‹๊ณผ PCRE์— ๋Œ€ํ•ด ๊ฝค ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ์„ ๊ฑฐ์•ผ.

์ •๊ทœํ‘œํ˜„์‹์€ ์ฒ˜์Œ์—” ์–ด๋ ต๊ฒŒ ๋А๊ปด์ง€์ง€๋งŒ, ๊ณ„์† ์“ฐ๋‹ค ๋ณด๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ผ๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋  ๊ฑฐ์•ผ.
ํŠนํžˆ ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ๋งŽ์€ ์›น ๊ฐœ๋ฐœ์—์„œ๋Š” ํ•„์ˆ˜ ์Šคํ‚ฌ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€. ๐Ÿ’ช

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ฑฐ๋‚˜,
๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ๋„ ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์ด ํฐ ๋„์›€์ด ๋  ๊ฑฐ์•ผ!

๐ŸŽ“ ํ•ต์‹ฌ ์š”์•ฝ 1. ๊ธฐ๋ณธ ๊ฐœ๋…
- ์ •๊ทœํ‘œํ˜„์‹์€ ํ…์ŠคํŠธ ํŒจํ„ด์„ ํ‘œํ˜„ํ•˜๋Š” ํ˜•์‹ ์–ธ์–ด
- PHP๋Š” PCRE ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉ (preg_ ํ•จ์ˆ˜๊ตฐ)
- ๋ฉ”ํƒ€๋ฌธ์ž, ์ˆ˜๋Ÿ‰์ž, ๋ฌธ์ž ํด๋ž˜์Šค๋กœ ํŒจํ„ด ๊ตฌ์„ฑ

2. ์ฃผ์š” ํ•จ์ˆ˜
- preg_match(): ํŒจํ„ด ๋งค์นญ ํ™•์ธ
- preg_match_all(): ๋ชจ๋“  ๋งค์นญ ์ฐพ๊ธฐ
- preg_replace(): ํŒจํ„ด ์น˜ํ™˜
- preg_split(): ํŒจํ„ด์œผ๋กœ ๋ถ„ํ• 

3. ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ
- ์บก์ฒ˜ ๊ทธ๋ฃน๊ณผ ์—ญ์ฐธ์กฐ
- Lookahead/Lookbehind
- ํ”Œ๋ž˜๊ทธ๋กœ ๋™์ž‘ ์ œ์–ด
- ์›์ž ๊ทธ๋ฃน์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”

4. ์‹ค๋ฌด ํŒ
- ๋ณด์•ˆ: ReDoS ๊ณต๊ฒฉ ์ฃผ์˜, ์ž…๋ ฅ๊ฐ’ ์ด์Šค์ผ€์ดํ”„
- ์„ฑ๋Šฅ: ๊ตฌ์ฒด์  ํŒจํ„ด, ์•ต์ปค ํ™œ์šฉ, ๋น„์บก์ฒ˜ ๊ทธ๋ฃน
- ํ…Œ์ŠคํŠธ: ๋‹ค์–‘ํ•œ ์ผ€์ด์Šค, ์—๋Ÿฌ ์ฒ˜๋ฆฌ
- ์œ ์ง€๋ณด์ˆ˜: ํŒจํ„ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ์ฃผ์„ ํ™œ์šฉ

์ •๊ทœํ‘œํ˜„์‹์€ ์—ฐ์Šต์ด ์ •๋ง ์ค‘์š”ํ•ด.
์˜ค๋Š˜ ๋ฐฐ์šด ์˜ˆ์ œ๋“ค์„ ์ง์ ‘ ์‹คํ–‰ํ•ด๋ณด๊ณ , ์ž์‹ ๋งŒ์˜ ํŒจํ„ด๋„ ๋งŒ๋“ค์–ด๋ณด๋ฉด์„œ ์ตํ˜€๋ด!

๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด regex101.com ๊ฐ™์€ ์˜จ๋ผ์ธ ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ข‹์•„.
์‹ค์‹œ๊ฐ„์œผ๋กœ ํŒจํ„ด์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์„ค๋ช…๋„ ๋ณผ ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๐Ÿ› ๏ธ

๊ทธ๋ฆฌ๊ณ  ๊ธฐ์–ตํ•ด, ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ์ •๊ทœํ‘œํ˜„์‹์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ•˜์ง€ ๋งˆ!
๊ฐ„๋‹จํ•œ ์ž‘์—…์€ strpos(), str_replace() ๊ฐ™์€ ํ•จ์ˆ˜๊ฐ€ ๋” ๋น ๋ฅด๊ณ  ์ฝ๊ธฐ ์‰ฌ์šธ ์ˆ˜ ์žˆ์–ด.

์ž, ์ด์ œ ์‹ค์ „์—์„œ ๋งˆ์Œ๊ป ํ™œ์šฉํ•ด๋ด!
ํ™”์ดํŒ…! ๐Ÿš€โœจ

๐ŸŽ‰ ์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค! ๐ŸŽ‰

PHP ์ •๊ทœํ‘œํ˜„์‹ ๋งˆ์Šคํ„ฐ์˜ ๊ธธ์„ ํ–ฅํ•ด ํ•œ ๊ฑธ์Œ ๋” ๋‚˜์•„๊ฐ”์–ด์š”!
๊ณ„์† ์—ฐ์Šตํ•˜๊ณ  ์‹ค์ „์— ์ ์šฉํ•ด๋ณด์„ธ์š”.

๋” ๋งŽ์€ ๊ฐœ๋ฐœ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ
๋‹ค์–‘ํ•œ ์ „๋ฌธ๊ฐ€๋“ค๊ณผ ์†Œํ†ตํ•ด๋ณด์„ธ์š”!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.