์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ” JavaScript ์ •๊ทœํ‘œํ˜„์‹๊ณผ ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ• ์ด์ •๋ฆฌ: ์™ธ๊ณ„์–ด ๊ฐ™๋˜ ํŒจํ„ด์„ ๋‚ด ๋ฌด๊ธฐ๋กœ ๋งŒ๋“œ๋Š” ๋ฒ•

๐Ÿ” JavaScript ์ •๊ทœํ‘œํ˜„์‹๊ณผ ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ• ์ด์ •๋ฆฌ: ์™ธ๊ณ„์–ด ๊ฐ™๋˜ ํŒจํ„ด์„ ๋‚ด ๋ฌด๊ธฐ๋กœ ๋งŒ๋“œ๋Š” ๋ฒ•

์ •๊ทœํ‘œํ˜„์‹(RegExp)์„ ์ฒ˜์Œ ๋ณด๋ฉด /^(?=.*\d)(?=.*[a-z]).{8,}$/ ๊ฐ™์€ ์ค„์ด ๊ณ ์–‘์ด๊ฐ€ ํ‚ค๋ณด๋“œ ์œ„๋ฅผ ๊ฑธ์–ด๊ฐ„ ํ”์ ์ฒ˜๋Ÿผ ๋ณด์ด์ฃ . ๐Ÿ˜น
๊ทธ๋Ÿฐ๋ฐ ๊ทœ์น™ ๋ช‡ ๊ฐœ๋งŒ ์•Œ๋ฉด ์ด ๋ฌธ์žฅ์€ "์ˆซ์ž ํ•˜๋‚˜, ์†Œ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ํฌํ•จํ•œ 8๊ธ€์ž ์ด์ƒ"์ด๋ผ๋Š” ์•„์ฃผ ํ‰๋ฒ”ํ•œ ๋œป์ž…๋‹ˆ๋‹ค.
์ด ๊ธ€์—์„œ๋Š” ๊ธฐ์ดˆ ๋ฌธ๋ฒ•์€ ๋น ๋ฅด๊ฒŒ ์งš๊ณ , ํ”Œ๋ž˜๊ทธ, ์บก์ฒ˜ ๊ทธ๋ฃน, ์ „ํ›„๋ฐฉ ํƒ์ƒ‰, ์œ ๋‹ˆ์ฝ”๋“œ, matchAllยทreplaceAll, ์„ฑ๋Šฅ ํ•จ์ •(ReDoS)๊นŒ์ง€ ์‹ค๋ฌด์—์„œ ํ•„์š”ํ•œ ๊ธฐ๋ฒ•์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ •๊ทœํ‘œํ˜„์‹ = ๋ฌธ์ž์—ด ํƒ์ง€๊ธฐ ์—ฐ๋ฝ์ฒ˜: 010-1234-5678 / ๋ฉ”์ผ: dev@site.kr /\d+/ ํŒจํ„ด ์ฐพ์„ ๊ทœ์น™ ์ •์˜ ๋งค์นญ ๋ฌธ์ž์—ด ์Šค์บ” ์ฒ˜๋ฆฌ ์ถ”์ถœยท์น˜ํ™˜ยท๊ฒ€์ฆ

1. ์ •๊ทœํ‘œํ˜„์‹, ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ๋‘ ๊ฐ€์ง€ ๐Ÿงฑ

JavaScript์—์„œ ์ •๊ทœํ‘œํ˜„์‹์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ๋ฆฌํ„ฐ๋Ÿด๊ณผ ์ƒ์„ฑ์ž ๋‘ ๊ฐ€์ง€์˜ˆ์š”.

// 1) ๋ฆฌํ„ฐ๋Ÿด: ํŒจํ„ด์ด ๊ณ ์ •์ผ ๋•Œ
const re1 = /hello/gi;

// 2) ์ƒ์„ฑ์ž: ํŒจํ„ด์„ ๋™์ ์œผ๋กœ ๋งŒ๋“ค ๋•Œ
const word = "hello";
const re2 = new RegExp(word, "gi");

// ์ƒ์„ฑ์ž์—์„œ๋Š” ์—ญ์Šฌ๋ž˜์‹œ๋ฅผ ๋‘ ๋ฒˆ!
const digits = new RegExp("\\d+", "g");

โš ๏ธ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์œผ๋กœ RegExp๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ., *, ( ๊ฐ™์€ ๋ฉ”ํƒ€๋ฌธ์ž๋ฅผ ๋ฐ˜๋“œ์‹œ ์ด์Šค์ผ€์ดํ”„ํ•ด์•ผ ํ•ด์š”. ์•ˆ ๊ทธ๋Ÿฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ a.b๊ฐ€ "a์•„๋ฌด๊ธ€์žb"๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค.

const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
new RegExp(escapeRegExp("1+1=2"));   // /1\+1=2/

์ฐธ๊ณ ๋กœ ์ตœ์‹  ํ‘œ์ค€(ES2025)์—๋Š” RegExp.escape()๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €ยทNode.js์—์„œ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ตฌํ˜• ํ™˜๊ฒฝ์„ ์ง€์›ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์œ„ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์ง์ ‘ ๋งŒ๋“œ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

2. ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ๋น ๋ฅด๊ฒŒ ํ›‘๊ธฐ โšก

์—ฌ๊ธฐ๋Š” "์•„ ์ด๊ฑฐ ๋ดค๋˜ ๊ฑฐ~" ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋„ ๋˜๋Š” ๊ตฌ๊ฐ„์ž…๋‹ˆ๋‹ค. ๋Œ€์‹  ํ‘œ๋Š” ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ฐ!

๊ธฐํ˜ธ์˜๋ฏธ์˜ˆ์‹œ
.์ค„๋ฐ”๊ฟˆ ์ œ์™ธ ์•„๋ฌด ๋ฌธ์ž 1๊ฐœ/c.t/ โ†’ cat, cut
\d \w \s์ˆซ์ž / ์˜์ˆซ์ž+_ / ๊ณต๋ฐฑ๋ฅ˜\D \W \S๋Š” ๋ฐ˜๋Œ€
[abc] [^abc]๋ฌธ์ž ์ง‘ํ•ฉ / ๋ถ€์ • ์ง‘ํ•ฉ[๊ฐ€-ํžฃ] ํ•œ๊ธ€ ์™„์„ฑํ˜•
* + ?0๊ฐœ ์ด์ƒ / 1๊ฐœ ์ด์ƒ / 0~1๊ฐœ/colou?r/
{n,m}n~mํšŒ ๋ฐ˜๋ณต/\d{3,4}/
^ $์‹œ์ž‘ / ๋ ์•ต์ปค/^\d+$/ ์ˆซ์ž๋งŒ
\b๋‹จ์–ด ๊ฒฝ๊ณ„/\bcat\b/
a|b๋˜๋Š”/jpg|png/

๐Ÿ’ก ํ•จ์ • ์ฃผ์˜: \w์™€ \b๋Š” ASCII ๊ธฐ์ค€์ด์—์š”. ์ฆ‰ \w๋Š” ํ•œ๊ธ€์„ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. /\w+/.test("์•ˆ๋…•")์€ false! ํ•œ๊ธ€์„ ๋‹ค๋ฃจ๋ ค๋ฉด [๊ฐ€-ํžฃ]์ด๋‚˜ ๋’ค์— ๋‚˜์˜ฌ ์œ ๋‹ˆ์ฝ”๋“œ ์†์„ฑ ์ด์Šค์ผ€์ดํ”„๋ฅผ ์“ฐ์„ธ์š”.

3. ํ”Œ๋ž˜๊ทธ 7์ข… ์„ธํŠธ, ์ œ๋Œ€๋กœ ์•Œ๊ธฐ ๐Ÿšฉ

ํ”Œ๋ž˜๊ทธ๋Š” ์ •๊ทœ์‹์˜ "๋ชจ๋“œ ์Šค์œ„์น˜"์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ JavaScript์—๋Š” ๋‹ค์Œ ํ”Œ๋ž˜๊ทธ๋“ค์ด ์žˆ์–ด์š”.

g (global)์ฒซ ๋งค์นญ์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ์ „๋ถ€ ์ฐพ๊ธฐ
i (ignoreCase)๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œ
m (multiline)^, $๊ฐ€ ๊ฐ ์ค„์˜ ์‹œ์ž‘ยท๋์— ๋งค์นญ
s (dotAll, ES2018).์ด ์ค„๋ฐ”๊ฟˆ๊นŒ์ง€ ๋งค์นญ
u (unicode, ES2015)์œ ๋‹ˆ์ฝ”๋“œ ์ฝ”๋“œ ํฌ์ธํŠธ ๋‹จ์œ„ ์ฒ˜๋ฆฌ
y (sticky, ES2015)lastIndex ์œ„์น˜์—์„œ๋งŒ ๋งค์นญ ์‹œ๋„
d (hasIndices, ES2022)๋งค์นญ๋œ ์œ„์น˜ ์ •๋ณด(indices) ์ œ๊ณต
v (unicodeSets, ES2024)u์˜ ๊ฐ•ํ™”ํŒ, ์ง‘ํ•ฉ ์—ฐ์‚ฐ ์ง€์›

์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ๊ณ ๊ฐ€ ๋‚˜๋Š” ๊ฑด ๋‹จ์—ฐ g ํ”Œ๋ž˜๊ทธ + test() ์กฐํ•ฉ์ด์—์š”.

const re = /a/g;
re.test("a");  // true  (lastIndex โ†’ 1)
re.test("a");  // false (1๋ฒˆ ์œ„์น˜๋ถ€ํ„ฐ ์ฐพ์œผ๋‹ˆ ์—†์Œ! lastIndex โ†’ 0)
re.test("a");  // true

g๋‚˜ y ํ”Œ๋ž˜๊ทธ๊ฐ€ ์žˆ๋Š” ์ •๊ทœ์‹์€ lastIndex๋ผ๋Š” ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์ •๊ทœ์‹ ๊ฐ์ฒด๋ฅผ ์žฌ์‚ฌ์šฉํ•ด์„œ ๊ฒ€์ฆํ•˜๋ฉด ๊ฒฐ๊ณผ๊ฐ€ ๋ฒˆ๊ฐˆ์•„ ๋‚˜์˜ค๋Š” "์œ ๋ น ๋ฒ„๊ทธ"๊ฐ€ ์ƒ๊ธฐ์ฃ . ๐Ÿ‘ป ๊ฒ€์ฆ์šฉ ์ •๊ทœ์‹์—๋Š” g๋ฅผ ๋ถ™์ด์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ •๋‹ต์ž…๋‹ˆ๋‹ค.

4. ์บก์ฒ˜ ๊ทธ๋ฃน: ์ฐพ์€ ๊ฑธ ์ชผ๊ฐœ์„œ ๊ฐ€์ ธ์˜ค๊ธฐ ๐ŸŽฃ

4-1. ๋ฒˆํ˜ธ ๊ทธ๋ฃน vs ์ด๋ฆ„ ์žˆ๋Š” ๊ทธ๋ฃน

const date = "2024-05-17";

// ๋ฒˆํ˜ธ ๊ทธ๋ฃน
const m1 = date.match(/(\d{4})-(\d{2})-(\d{2})/);
m1[1]; // "2024"

// ์ด๋ฆ„ ์žˆ๋Š” ๊ทธ๋ฃน (ES2018)
const m2 = date.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
const { year, month, day } = m2.groups;  // ๊ตฌ์กฐ ๋ถ„ํ•ด๊นŒ์ง€ ๊น”๋”!

์ด๋ฆ„ ์žˆ๋Š” ๊ทธ๋ฃน์€ ํŒจํ„ด์ด ๊ธธ์–ด์ ธ๋„ "3๋ฒˆ ๊ทธ๋ฃน์ด ๋ญ์˜€๋”๋ผ?" ํ•˜๋ฉฐ ์†๊ฐ€๋ฝ์œผ๋กœ ๊ด„ํ˜ธ๋ฅผ ์„ธ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. ํ˜‘์—… ์ฝ”๋“œ๋ผ๋ฉด ๊ฐ•๋ ฅ ์ถ”์ฒœ!

4-2. ๋น„์บก์ฒ˜ ๊ทธ๋ฃน๊ณผ ์—ญ์ฐธ์กฐ

// (?:...) ๋ฌถ๊ธฐ๋งŒ ํ•˜๊ณ  ์ €์žฅ ์•ˆ ํ•จ โ†’ ๊ฒฐ๊ณผ ๋ฐฐ์—ด์ด ๊น”๋”
/(?:https?|ftp):\/\/(\S+)/.exec("https://a.com")[1]; // "a.com"

// ์—ญ์ฐธ์กฐ: ๊ฐ™์€ ๋ฌธ์ž ๋ฐ˜๋ณต ์ฐพ๊ธฐ
/(\w)\1/.test("hello");                 // true ("ll")
/(?<q>["']).*?\k<q>/.exec(`say "hi"`)[0];  // "\"hi\""
์บก์ฒ˜ ๊ทธ๋ฃน ๋ถ„ํ•ด๋„ 2024 - 05 - 17 groups.year "2024" groups.month "05" groups.day "17"

5. ์ „๋ฐฉยทํ›„๋ฐฉ ํƒ์ƒ‰: ์กฐ๊ฑด๋งŒ ํ™•์ธํ•˜๊ณ  ์•ˆ ๋จน๊ธฐ ๐Ÿ‘€

Lookaround๋Š” "๊ทธ ์œ„์น˜ ์•ž๋’ค์— ์ด๋Ÿฐ ๊ฒŒ ์žˆ๋Š”์ง€/์—†๋Š”์ง€"๋งŒ ํ™•์ธํ•˜๊ณ , ์‹ค์ œ ๋งค์นญ ๊ฒฐ๊ณผ์—๋Š” ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๋„ˆ๋น„ 0(zero-width) ๋‹จ์–ธ์ž…๋‹ˆ๋‹ค.

๋ฌธ๋ฒ•์ด๋ฆ„๋œป
X(?=Y)๊ธ์ • ์ „๋ฐฉํƒ์ƒ‰๋’ค์— Y๊ฐ€ ์˜ค๋Š” X
X(?!Y)๋ถ€์ • ์ „๋ฐฉํƒ์ƒ‰๋’ค์— Y๊ฐ€ ์˜ค์ง€ ์•Š๋Š” X
(?<=Y)X๊ธ์ • ํ›„๋ฐฉํƒ์ƒ‰ (ES2018)์•ž์— Y๊ฐ€ ์žˆ๋Š” X
(?<!Y)X๋ถ€์ • ํ›„๋ฐฉํƒ์ƒ‰ (ES2018)์•ž์— Y๊ฐ€ ์—†๋Š” X
// ๊ฐ€๊ฒฉ์—์„œ ์ˆซ์ž๋งŒ (โ‚ฉ ๊ธฐํ˜ธ๋Š” ์ œ์™ธ)
"โ‚ฉ3000, $20".match(/(?<=โ‚ฉ)\d+/)[0];   // "3000"

// ๋น„๋ฐ€๋ฒˆํ˜ธ: ์˜๋ฌธยท์ˆซ์žยทํŠน์ˆ˜๋ฌธ์ž ๊ฐ 1๊ฐœ ์ด์ƒ, 8~20์ž
const pw = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,20}$/;
pw.test("abc12345!");  // true

// ์ฒœ ๋‹จ์œ„ ์ฝค๋งˆ โ€” ์ •๊ทœ์‹ ๋ช…๋ฌผ
const comma = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
comma(1234567);  // "1,234,567"

์ฒœ ๋‹จ์œ„ ์ฝค๋งˆ ํŒจํ„ด์„ ํ•ด์„ํ•˜๋ฉด, "๋‹จ์–ด ๊ฒฝ๊ณ„๊ฐ€ ์•„๋‹Œ ์œ„์น˜ ์ค‘, ๋’ค์— ์ˆซ์ž 3๊ฐœ ๋ฌถ์Œ์ด 1๋ฒˆ ์ด์ƒ ์˜ค๊ณ  ๊ทธ ๋’ค์— ๋” ์ด์ƒ ์ˆซ์ž๊ฐ€ ์—†๋Š” ์ž๋ฆฌ"์— ์ฝค๋งˆ๋ฅผ ๊ฝ‚๋Š” ๊ฑฐ์˜ˆ์š”. ๋ฌผ๋ก  ์‹ค๋ฌด์—์„  (1234567).toLocaleString("ko-KR")๋‚˜ Intl.NumberFormat์ด ๋” ํ™•์‹คํ•ฉ๋‹ˆ๋‹ค. ์†Œ์ˆ˜์ ์ด ์žˆ๋Š” ์ˆ˜์— ์œ„ ์ •๊ทœ์‹์„ ์“ฐ๋ฉด ์†Œ์ˆ˜๋ถ€์—๋„ ์ฝค๋งˆ๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”. ๐Ÿ˜…

6. ํƒ์š•(Greedy) vs ๊ฒŒ์œผ๋ฆ„(Lazy) ๐Ÿฐ

์ˆ˜๋Ÿ‰์ž *, +๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋จน์„ ์ˆ˜ ์žˆ๋Š” ๋งŒํผ ์ตœ๋Œ€ํ•œ ๋จน์Šต๋‹ˆ๋‹ค. ๋’ค์— ?๋ฅผ ๋ถ™์ด๋ฉด ์ตœ์†Œํ•œ๋งŒ ๋จน์–ด์š”.

const html = "<b>๊ตต๊ฒŒ</b> ๊ทธ๋ฆฌ๊ณ  <i>๊ธฐ์šธ์ž„</i>";
html.match(/<.+>/)[0];   // ์ „์ฒด ๋ฌธ์ž์—ด ํ†ต์งธ๋กœ (ํƒ์š•)
html.match(/<.+?>/)[0];  // "<b>" (๊ฒŒ์œผ๋ฆ„)
html.match(/<[^>]+>/g); // ["<b>","</b>","<i>","</i>"] โ† ๋ถ€์ • ์ง‘ํ•ฉ์ด ๋” ๋น ๋ฅด๊ณ  ๋ช…ํ™•

๐Ÿ’ก .*?๋ณด๋‹ค [^>]*์ฒ˜๋Ÿผ ๋ถ€์ • ๋ฌธ์ž ์ง‘ํ•ฉ์œผ๋กœ ๋ฒ”์œ„๋ฅผ ์ขํžˆ๋Š” ๊ฒŒ ์˜๋„๋„ ๋ช…ํ™•ํ•˜๊ณ  ๋ฐฑํŠธ๋ž˜ํ‚น๋„ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค. ๋‹จ, HTML ์ „์ฒด ํŒŒ์‹ฑ์€ ์ •๊ทœ์‹ ๋Œ€์‹  DOMParser๋ฅผ ์“ฐ์„ธ์š”. ์ค‘์ฒฉ ๊ตฌ์กฐ๋Š” ์ •๊ทœ์‹์˜ ์˜์—ญ์ด ์•„๋‹ˆ์—์š”.

7. ๋ฌธ์ž์—ด ๋ฉ”์„œ๋“œ 6ํ˜•์ œ ์™„์ „ ์ •๋ณต ๐Ÿงฐ

๋ฉ”์„œ๋“œ๋ฐ˜ํ™˜๊ฐ’ํฌ์ธํŠธ
re.test(str)boolean๊ฒ€์ฆ ์ „์šฉ, ๊ฐ€์žฅ ๊ฐ€๋ฒผ์›€
re.exec(str)๋งค์นญ ๋ฐฐ์—ด | nullg์™€ ํ•จ๊ป˜ ๋ฐ˜๋ณต ํ˜ธ์ถœ ๊ฐ€๋Šฅ
str.match(re)๋ฐฐ์—ด | nullg ์žˆ์œผ๋ฉด ๊ทธ๋ฃน ์ •๋ณด ์—†์ด ์ „์ฒด ๋งค์นญ๋งŒ
str.matchAll(re)์ดํ„ฐ๋ ˆ์ดํ„ฐg ํ•„์ˆ˜, ๊ทธ๋ฃน ์ •๋ณด๊นŒ์ง€ ์ „๋ถ€ (ES2020)
str.replace / replaceAll์ƒˆ ๋ฌธ์ž์—ดreplaceAll์— ์ •๊ทœ์‹์ด๋ฉด g ํ•„์ˆ˜ (ES2021)
str.search / split์ธ๋ฑ์Šค / ๋ฐฐ์—ดsplit์— ์บก์ฒ˜ ๊ทธ๋ฃน ์“ฐ๋ฉด ๊ตฌ๋ถ„์ž๋„ ํฌํ•จ

7-1. matchAll: ๋ฐ˜๋ณต๋ฌธ ์ง€์˜ฅ ํƒˆ์ถœ ๐ŸŽ‰

const log = "user=kim age=31; user=lee age=27";
const re = /user=(?<name>\w+) age=(?<age>\d+)/g;

for (const m of log.matchAll(re)) {
  console.log(m.groups.name, Number(m.groups.age), m.index);
}
// kim 31 0
// lee 27 18

// ๋ฐฐ์—ด๋กœ ๋ฐ”๋กœ ๋ณ€ํ™˜
const users = [...log.matchAll(re)].map(m => ({ ...m.groups }));

์˜ˆ์ „์—” while ((m = re.exec(str)) !== null) ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ผ์ฃ . matchAll์€ g ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์œผ๋ฉด TypeError๋ฅผ ๋˜์ ธ ์‹ค์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ๋ง‰์•„์ค๋‹ˆ๋‹ค.

7-2. replace์˜ ์ˆจ์€ ํ•„์‚ด๊ธฐ: ์น˜ํ™˜ ํŒจํ„ด๊ณผ ํ•จ์ˆ˜

$&๋งค์นญ๋œ ์ „์ฒด ๋ฌธ์ž์—ด
$1, $2๋ฒˆํ˜ธ ์บก์ฒ˜ ๊ทธ๋ฃน
$<name>์ด๋ฆ„ ์žˆ๋Š” ๊ทธ๋ฃน
$` / $'๋งค์นญ ์•ž / ๋’ค ๋ฌธ์ž์—ด
// ๋‚ ์งœ ํ˜•์‹ ๋ณ€ํ™˜
"2024-05-17".replace(/(?<y>\d+)-(?<m>\d+)-(?<d>\d+)/, "$<d>/$<m>/$<y>");
// "17/05/2024"

// ์ „ํ™”๋ฒˆํ˜ธ ๋งˆ์Šคํ‚น
"010-1234-5678".replace(/(\d{3})-(\d{4})-(\d{4})/, "$1-****-$3");

// ํ•จ์ˆ˜๋กœ ์น˜ํ™˜: camelCase โ†’ kebab-case
"backgroundColor".replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());
// "background-color"

// kebab โ†’ camel
"font-size-large".replace(/-(\w)/g, (_, c) => c.toUpperCase());
// "fontSizeLarge"

โš ๏ธ ๋ฌธ์ž์—ด ์น˜ํ™˜๊ฐ’ ์•ˆ์˜ $๋Š” ํŠน์ˆ˜๋ฌธ์ž์˜ˆ์š”. ์‚ฌ์šฉ์ž ์ž…๋ ฅ์ฒ˜๋Ÿผ $๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๋„ฃ์„ ๋• ํ•จ์ˆ˜ ํ˜•ํƒœ () => value๋กœ ๋„˜๊ธฐ๋ฉด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

7-3. split์˜ ์˜์™ธ์˜ ์žฌ๋Šฅ

"a1b2c3".split(/\d/);     // ["a","b","c",""]
"a1b2c3".split(/(\d)/);   // ["a","1","b","2","c","3",""]  ๊ตฌ๋ถ„์ž ๋ณด์กด!
"์‚ฌ๊ณผ, ๋ฐฐ ,  ํฌ๋„".split(/\s*,\s*/);  // ["์‚ฌ๊ณผ","๋ฐฐ","ํฌ๋„"]

8. ์œ ๋‹ˆ์ฝ”๋“œ์™€ ํ•œ๊ธ€, ์ด๋ชจ์ง€ ์ œ๋Œ€๋กœ ๋‹ค๋ฃจ๊ธฐ ๐ŸŒ

JavaScript ๋ฌธ์ž์—ด์€ UTF-16 ๊ธฐ๋ฐ˜์ด๋ผ, ์ด๋ชจ์ง€ ๊ฐ™์€ ์ผ๋ถ€ ๋ฌธ์ž๋Š” 2๊ฐœ์˜ ์ฝ”๋“œ ์œ ๋‹›(์„œ๋กœ๊ฒŒ์ดํŠธ ์Œ)์œผ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ์ผ์ด ์ƒ๊ฒจ์š”.

"๐Ÿ˜€".length;              // 2  (?!)
/^.$/.test("๐Ÿ˜€");         // false
/^.$/u.test("๐Ÿ˜€");        // true  โ† u ํ”Œ๋ž˜๊ทธ ํšจ๊ณผ
[..."๐Ÿ˜€๊ฐ€"].length;       // 2  (์Šคํ”„๋ ˆ๋“œ๋Š” ์ฝ”๋“œ ํฌ์ธํŠธ ๋‹จ์œ„)
๊ธ€์ž ํ•˜๋‚˜๋ฅผ ์„ธ๋Š” ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ฝ”๋“œ ์œ ๋‹› \uD83D \uDE00 length 2 ์ฝ”๋“œ ํฌ์ธํŠธ U+1F600 ๐Ÿ˜€ u ํ”Œ๋ž˜๊ทธ, 1๊ฐœ ์ž์†Œ ํด๋Ÿฌ์Šคํ„ฐ ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง (์—ฌ๋Ÿฌ ์ฝ”๋“œ) Segmenter, 1๊ฐœ ์‚ฌ๋žŒ ๋ˆˆ์˜ "ํ•œ ๊ธ€์ž" = ์ž์†Œ ํด๋Ÿฌ์Šคํ„ฐ

8-1. ์œ ๋‹ˆ์ฝ”๋“œ ์†์„ฑ ์ด์Šค์ผ€์ดํ”„ \p{...}

ES2018๋ถ€ํ„ฐ u ํ”Œ๋ž˜๊ทธ์™€ ํ•จ๊ป˜ \p{...}๋ฅผ ์“ธ ์ˆ˜ ์žˆ์–ด์š”. ์–ธ์–ด๋ณ„ ๋ฌธ์ž๋ฅผ ๋ฒ”์œ„ ์™ธ์šฐ์ง€ ์•Š๊ณ  ์ด๋ฆ„์œผ๋กœ ์ฐพ์Šต๋‹ˆ๋‹ค.

/^\p{Script=Hangul}+$/u.test("์•ˆ๋…•ํ•˜์„ธ์š”");  // true
/\p{L}+/gu   // ๋ชจ๋“  ์–ธ์–ด์˜ ๋ฌธ์ž(Letter)
/\p{N}/u     // ๋ชจ๋“  ์ˆซ์ž
/\p{Extended_Pictographic}/u.test("๐Ÿ”ฅ");     // true (์ด๋ชจ์ง€ ํŒ๋ณ„)

// ํ•œ๊ธ€ ์ดˆ์„ฑ/์ž๋ชจ๊นŒ์ง€ ํฌํ•จ vs ์™„์„ฑํ˜•๋งŒ
/[๊ฐ€-ํžฃ]/      // ์™„์„ฑํ˜• 11,172์ž๋งŒ (ใ„ฑ, ใ… ๋‹จ๋…์€ ๋ถˆํฌํ•จ)
/[ใ„ฑ-ใ…Žใ…-ใ…ฃ๊ฐ€-ํžฃ]/  // ํ˜ธํ™˜ ์ž๋ชจ๊นŒ์ง€

8-2. ์‚ฌ๋žŒ ๋ˆˆ ๊ธฐ์ค€ ๊ธ€์ž ์ˆ˜: Intl.Segmenter

๊ฐ€์กฑ ์ด๋ชจ์ง€ ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง๋Š” ์—ฌ๋Ÿฌ ์ฝ”๋“œ ํฌ์ธํŠธ๊ฐ€ ZWJ(์ œ๋กœ ํญ ๊ฒฐํ•ฉ์ž)๋กœ ์ด์–ด์ง„ ๊ฒƒ์ด๋ผ u ํ”Œ๋ž˜๊ทธ๋กœ๋„ 1๊ฐœ๋กœ ์•ˆ ์…‰๋‹ˆ๋‹ค. ์ด๋•Œ๋Š” Intl.Segmenter๊ฐ€ ์ •๋‹ต์ด์—์š”. (์ฃผ์š” ๋ชจ๋˜ ๋ธŒ๋ผ์šฐ์ €์™€ Node.js์—์„œ ์ง€์›)

const seg = new Intl.Segmenter("ko", { granularity: "grapheme" });
const count = (s) => [...seg.segment(s)].length;
count("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง์•ˆ๋…•");  // 3

// ํ•œ๊ตญ์–ด ๋‹จ์–ด ๋ถ„๋ฆฌ๋„ ๊ฐ€๋Šฅ
const ws = new Intl.Segmenter("ko", { granularity: "word" });
[...ws.segment("์ •๊ทœ์‹ ์ •๋ง ์žฌ๋ฐŒ๋‹ค")].filter(s => s.isWordLike).map(s => s.segment);

SNS ๊ธ€์ž ์ˆ˜ ์ œํ•œ, ๋‹‰๋„ค์ž„ ๊ธธ์ด ๊ฒ€์ฆ์ฒ˜๋Ÿผ "์‚ฌ์šฉ์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ๊ธ€์ž ์ˆ˜"๊ฐ€ ์ค‘์š”ํ•  ๋•Œ ๊ผญ ์จ๋ณด์„ธ์š”.

8-3. ์ •๊ทœํ™”(normalize): ๊ฐ™์€๋ฐ ๋‹ค๋ฅธ ๋ฌธ์ž์—ด

macOS์—์„œ ์—…๋กœ๋“œ๋œ ํŒŒ์ผ๋ช…์˜ ํ•œ๊ธ€์ด ์ž๋ชจ ๋ถ„๋ฆฌ(NFD)๋˜์–ด ๊ฒ€์ƒ‰์ด ์•ˆ ๋˜๋Š” ๊ฒฝํ—˜, ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ํ•œ ๋ฒˆ์ฏค ์žˆ์ฃ . ๐Ÿ˜‡

const a = "ํ•œ";                   // NFC (์™„์„ฑํ˜• 1๊ฐœ)
const b = "ํ•œ".normalize("NFD");  // ์ž๋ชจ 3๊ฐœ๋กœ ๋ถ„ํ•ด
a === b;                          // false
a === b.normalize("NFC");         // true

๋น„๊ตยท๊ฒ€์ƒ‰ ์ „์— normalize("NFC")๋กœ ํ†ต์ผํ•˜๋Š” ์Šต๊ด€์ด ์‚ฌ๊ณ ๋ฅผ ๋ง‰์•„์ค๋‹ˆ๋‹ค.

9. ๊ณ ๊ธ‰ ํ”Œ๋ž˜๊ทธ ํ™œ์šฉ: y, d, v ๐Ÿ› ๏ธ

9-1. sticky(y)๋กœ ๋ฏธ๋‹ˆ ํ† ํฌ๋‚˜์ด์ € ๋งŒ๋“ค๊ธฐ

y ํ”Œ๋ž˜๊ทธ๋Š” lastIndex ์œ„์น˜์—์„œ ๋”ฑ ๊ทธ ์ž๋ฆฌ์—์„œ๋งŒ ๋งค์นญ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋›ฐ๋ฉฐ ์ฐพ์ง€ ์•Š์œผ๋‹ˆ ๋ ‰์„œ(ํ† ํฐ ๋ถ„์„๊ธฐ)์— ๋”ฑ์ด์—์š”.

function tokenize(src) {
  const rules = [
    ["num", /\d+(?:\.\d+)?/y],
    ["id",  /[A-Za-z_]\w*/y],
    ["op",  /[+\-*/=()]/y],
    ["ws",  /\s+/y],
  ];
  const tokens = [];
  let pos = 0;
  outer: while (pos < src.length) {
    for (const [type, re] of rules) {
      re.lastIndex = pos;
      const m = re.exec(src);
      if (m) {
        if (type !== "ws") tokens.push({ type, value: m[0] });
        pos = re.lastIndex;
        continue outer;
      }
    }
    throw new SyntaxError(`์•Œ ์ˆ˜ ์—†๋Š” ๋ฌธ์ž: ${src[pos]} (์œ„์น˜ ${pos})`);
  }
  return tokens;
}
tokenize("x = 3.14 * (r + 2)");

9-2. hasIndices(d)๋กœ ์œ„์น˜ ํ•˜์ด๋ผ์ดํŠธ

const m = /(?<key>\w+)=(?<val>\w+)/d.exec("id=42");
m.indices[0];            // [0, 5]  ์ „์ฒด ๋ฒ”์œ„
m.indices.groups.val;    // [3, 5]  ๊ฐ’์˜ ์‹œ์ž‘~๋

๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํ•˜์ด๋ผ์ดํŠธ, ์—๋””ํ„ฐ ์˜ค๋ฅ˜ ํ‘œ์‹œ์ฒ˜๋Ÿผ "์–ด๋””์„œ๋ถ€ํ„ฐ ์–ด๋””๊นŒ์ง€"๊ฐ€ ํ•„์š”ํ•œ ๊ณณ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

9-3. unicodeSets(v): ์ง‘ํ•ฉ ์—ฐ์‚ฐ

// ์ฐจ์ง‘ํ•ฉ: ๊ทธ๋ฆฌ์Šค ๋ฌธ์ž ์ค‘ ๋ฌธ์ž(Letter)๋งŒ, ๋‹จ ฮฑ ์ œ์™ธ
/[\p{Script=Greek}--[ฮฑ]]/v
// ๊ต์ง‘ํ•ฉ: ASCII ์ด๋ฉด์„œ ์ˆซ์ž
/[\p{ASCII}&&\p{N}]/v

v ํ”Œ๋ž˜๊ทธ๋Š” u์˜ ์ƒ์œ„ ํ˜ธํ™˜ ๊ฐœ๋…์ด๋ฉฐ u์™€ ๋™์‹œ์— ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋น„๊ต์  ์ตœ์‹  ๊ธฐ๋Šฅ์ด๋ผ ์ง€์› ํ™˜๊ฒฝ์„ ํ™•์ธํ•œ ๋’ค ์“ฐ๋Š” ๊ฒŒ ์ข‹์•„์š”.

10. ์„ฑ๋Šฅ๊ณผ ๋ณด์•ˆ: ReDoS๋ฅผ ์กฐ์‹ฌํ•˜์„ธ์š” ๐Ÿ’ฃ

์ •๊ทœ์‹ ์—”์ง„์€ ๋งค์นญ์— ์‹คํŒจํ•˜๋ฉด ๋‹ค๋ฅธ ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ๋˜์งš์–ด ๋ณด๋Š” ๋ฐฑํŠธ๋ž˜ํ‚น์„ ํ•ฉ๋‹ˆ๋‹ค. ์ค‘์ฒฉ๋œ ์ˆ˜๋Ÿ‰์ž๊ฐ€ ์žˆ์œผ๋ฉด ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ์ง€์ˆ˜์ ์œผ๋กœ ํญ๋ฐœํ•ด์š”. ์ด๊ฒŒ ๋ฐ”๋กœ ReDoS(Regular expression Denial of Service)์ž…๋‹ˆ๋‹ค.

์ž…๋ ฅ ๊ธธ์ด๋ณ„ ๋ฐฑํŠธ๋ž˜ํ‚น ๋น„์šฉ /(a+)+$/ ์ง€์ˆ˜ ์ฆ๊ฐ€ /a+$/ ์„ ํ˜• ์ฆ๊ฐ€ ์ž…๋ ฅ ๊ธธ์ด โ†’ ์‹œ๊ฐ„
// ๐Ÿšจ ์œ„ํ—˜ ํŒจํ„ด: ์ค‘์ฒฉ ์ˆ˜๋Ÿ‰์ž
const evil = /^(a+)+$/;
evil.test("a".repeat(30) + "!");  // ๋งค์šฐ ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Œ (๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋ฉˆ์ถค)

// โœ… ๊ฐœ์„ : ์ค‘์ฒฉ ์ œ๊ฑฐ
const safe = /^a+$/;

์ฒดํฌ๋ฆฌ์ŠคํŠธ
โœ… (x+)+, (x*)*, (a|a)* ๊ฐ™์€ ์ค‘์ฒฉยท์ค‘๋ณต ๋Œ€์•ˆ ํ”ผํ•˜๊ธฐ
โœ… .* ๋Œ€์‹  [^,]*์ฒ˜๋Ÿผ ๋ฒ”์œ„๋ฅผ ์ขํžŒ ๋ถ€์ • ์ง‘ํ•ฉ ์‚ฌ์šฉ
โœ… ์‚ฌ์šฉ์ž ์ž…๋ ฅ์€ ์ •๊ทœ์‹ ์ ์šฉ ์ „์— ๊ธธ์ด ์ œํ•œ๋ถ€ํ„ฐ
โœ… ๋ฃจํ”„ ์•ˆ์—์„œ new RegExp()๋ฅผ ๋ฐ˜๋ณต ์ƒ์„ฑํ•˜์ง€ ๋ง๊ณ  ๋ฐ”๊นฅ์—์„œ ํ•œ ๋ฒˆ๋งŒ
โœ… ๋‹จ์ˆœ ํฌํ•จ ์—ฌ๋ถ€๋Š” includes(), startsWith()๊ฐ€ ๋” ๋น ๋ฅด๊ณ  ์ฝ๊ธฐ ์‰ฌ์›€

Node.js ์„œ๋ฒ„์—์„œ ReDoS๊ฐ€ ํ„ฐ์ง€๋ฉด ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ง‰ํ˜€ ์ „์ฒด ์š”์ฒญ์ด ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์–ด์š”. ์‹ค์ œ๋กœ ์—ฌ๋Ÿฌ npm ํŒจํ‚ค์ง€์—์„œ ReDoS ์ทจ์•ฝ์ ์ด ๋ณด๊ณ ๋œ ์‚ฌ๋ก€๊ฐ€ ์žˆ์œผ๋‹ˆ, ๊ฒ€์ฆ ํŒจํ„ด์€ ๊ผญ ๊ทน๋‹จ์ ์ธ ์ž…๋ ฅ์œผ๋กœ ํ…Œ์ŠคํŠธํ•ด ๋ณด์„ธ์š”.

11. ์‹ค์ „ ๋ ˆ์‹œํ”ผ ๋ชจ์Œ ๐Ÿณ

// 1. ์•ž๋’ค ๊ณต๋ฐฑ + ์ค‘๋ณต ๊ณต๋ฐฑ ์ •๋ฆฌ
const clean = (s) => s.trim().replace(/\s+/g, " ");

// 2. ํœด๋Œ€ํฐ ๋ฒˆํ˜ธ ์ •๊ทœํ™” (์ˆซ์ž๋งŒ ๋ฝ‘์•„ ํ•˜์ดํ”ˆ)
const phone = (s) => s.replace(/\D/g, "").replace(/^(\d{3})(\d{3,4})(\d{4})$/, "$1-$2-$3");
phone("010 1234 5678");  // "010-1234-5678"

// 3. ๊ฐ„๋‹จํ•œ ์ด๋ฉ”์ผ ํ˜•์‹ ์ฒดํฌ (์™„๋ฒฝํ•œ RFC ๊ฒ€์ฆ์€ ์•„๋‹˜!)
const isEmail = (s) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s);

// 4. ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ํŒŒ์‹ฑ์€ ์ •๊ทœ์‹๋ณด๋‹ค ํ‘œ์ค€ API
const params = Object.fromEntries(new URLSearchParams("?q=js&page=2"));

// 5. ํ…œํ”Œ๋ฆฟ ์น˜ํ™˜
const tpl = (str, data) => str.replace(/\{\{\s*(\w+)\s*\}\}/g, (_, k) => data[k] ?? "");
tpl("์•ˆ๋…•, {{ name }}!", { name: "์žฌ๋Šฅ๋Ÿฌ" });

// 6. HTML ํŠน์ˆ˜๋ฌธ์ž ์ด์Šค์ผ€์ดํ”„ (XSS ๊ธฐ๋ณธ ๋ฐฉ์–ด)
const escHtml = (s) => s.replace(/[&<>"']/g, (c) =>
  ({ "&":"&amp;", "<":"&lt;", ">":"&gt;", '"':"&quot;", "'":"&#39;" })[c]);

๐Ÿ’ก ์ด๋ฉ”์ผ์€ ์ •๊ทœ์‹์œผ๋กœ 100% ๊ฒ€์ฆํ•˜๋ ค๋Š” ์ˆœ๊ฐ„ ๊ดด๋ฌผ ํŒจํ„ด์ด ํƒ„์ƒํ•ฉ๋‹ˆ๋‹ค. ํ˜•์‹์€ ๊ฐ€๋ณ๊ฒŒ ์ฒดํฌํ•˜๊ณ , ์‹ค์ œ ์œ ํšจ์„ฑ์€ ์ธ์ฆ ๋ฉ”์ผ๋กœ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์—…๊ณ„ ํ‘œ์ค€์ ์ธ ์ ‘๊ทผ์ด์—์š”.

12. ์ •๊ทœ์‹ ๋ง๊ณ ๋„ ์•Œ์•„๋‘๋ฉด ์ข‹์€ ๋ฌธ์ž์—ด ๋ฌด๊ธฐ๋“ค ๐Ÿ—ก๏ธ

padStart / padEnd"7".padStart(3,"0") โ†’ "007"
at()"hello".at(-1) โ†’ "o" (์Œ์ˆ˜ ์ธ๋ฑ์Šค)
localeCompareํ•œ๊ธ€ ๊ฐ€๋‚˜๋‹ค์ˆœ ์ •๋ ฌ, Intl.Collator๋กœ ์ˆซ์ž ์ž์—ฐ ์ •๋ ฌ
ํƒœ๊ทธ๋“œ ํ…œํ”Œ๋ฆฟString.raw`C:\new`๋กœ ์—ญ์Šฌ๋ž˜์‹œ ๊ทธ๋Œ€๋กœ
// ์ˆซ์ž ์„ž์ธ ํŒŒ์ผ๋ช… ์ž์—ฐ ์ •๋ ฌ
const files = ["ํŒŒ์ผ10", "ํŒŒ์ผ2", "ํŒŒ์ผ1"];
files.sort(new Intl.Collator("ko", { numeric: true }).compare);
// ["ํŒŒ์ผ1", "ํŒŒ์ผ2", "ํŒŒ์ผ10"]

// String.raw๋กœ ์ •๊ทœ์‹ ๋ฌธ์ž์—ด ์ž‘์„ฑ ์‹œ ์ด์Šค์ผ€์ดํ”„ ์ง€์˜ฅ ํƒˆ์ถœ
new RegExp(String.raw`\d+\.\d+`);

13. ๋””๋ฒ„๊น… ๊ฟ€ํŒ ๐Ÿž

์ •๊ทœ์‹์€ "์“ฐ๊ธฐ ์ „์šฉ ์–ธ์–ด"๋ผ๋Š” ๋†๋‹ด์ด ์žˆ์„ ์ •๋„๋กœ ๋‚˜์ค‘์— ์ฝ๊ธฐ ์–ด๋ ค์›Œ์š”. ๊ทธ๋ž˜์„œ ์ด๋ ‡๊ฒŒ ๊ด€๋ฆฌํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

โ‘  ์กฐ๊ฐ ์กฐ๋ฆฝํ•˜๊ธฐ โ€” ๊ธด ํŒจํ„ด์€ ์˜๋ฏธ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ ์ด๋ฆ„์„ ๋ถ™์ด๊ณ  ํ•ฉ์น˜๊ธฐ
โ‘ก ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ํ‘œ โ€” ํ†ต๊ณผ/์‹คํŒจ ์˜ˆ์‹œ๋ฅผ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค์–ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
โ‘ข ์‹œ๊ฐํ™” ๋„๊ตฌ โ€” regex101 ๊ฐ™์€ ์˜จ๋ผ์ธ ๋„๊ตฌ์—์„œ JavaScript(ECMAScript) ๋ชจ๋“œ๋กœ ์„ค๋ช…ยท๋‹จ๊ณ„๋ณ„ ๋งค์นญ ํ™•์ธ
โ‘ฃ ์ฃผ์„ โ€” ์ •๊ทœ์‹ ๋ฐ”๋กœ ์œ„์— "๋ฌด์—‡์„ ํ—ˆ์šฉ/๊ฑฐ๋ถ€ํ•˜๋Š”์ง€" ํ•œ ์ค„ ์„ค๋ช…

const YEAR  = String.raw`(?<y>\d{4})`;
const MONTH = String.raw`(?<m>0[1-9]|1[0-2])`;
const DAY   = String.raw`(?<d>0[1-9]|[12]\d|3[01])`;
const DATE_RE = new RegExp(`^${YEAR}-${MONTH}-${DAY}$`);

const cases = { "2024-05-17": true, "2024-13-01": false, "24-05-17": false };
for (const [input, expected] of Object.entries(cases)) {
  console.assert(DATE_RE.test(input) === expected, input);
}

์ฐธ๊ณ ๋กœ ์ด ํŒจํ„ด์€ ํ˜•์‹๋งŒ ๋ณด๊ธฐ ๋•Œ๋ฌธ์— 2์›” 31์ผ๋„ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. ๋‚ ์งœ์˜ ์‹ค์ œ ์œ ํšจ์„ฑ์€ Date ๊ฐ์ฒด๋‚˜ ๋‚ ์งœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ 2์ฐจ ๊ฒ€์ฆํ•˜์„ธ์š”.

์ •๊ทœ์‹์ด ์—ฌ์ „ํžˆ ๋ง‰๋ง‰ํ•˜๋‹ค๋ฉด ํ˜ผ์ž ๋™๋™๋Œ€๊ธฐ๋ณด๋‹ค ๊ฒฝํ—˜์ž์˜ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ๋ฐฉ๋ฒ•์ด์—์š”. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์—์„œ JavaScript ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋‚˜ ๋งž์ถค ๊ฐœ๋ฐœ์„ ๋„์™€์ค„ ๊ณ ์ˆ˜๋ฅผ ๋งŒ๋‚  ์ˆ˜๋„ ์žˆ๋‹ต๋‹ˆ๋‹ค. ๐Ÿ™Œ

๐Ÿ“Œ ํ•œ๋ˆˆ์— ์ •๋ฆฌ

โœ”๏ธ ๊ฒ€์ฆ์šฉ ์ •๊ทœ์‹์—๋Š” g ํ”Œ๋ž˜๊ทธ ๊ธˆ์ง€ (lastIndex ์œ ๋ น ๋ฒ„๊ทธ)
โœ”๏ธ ๊ฐ€๋…์„ฑ์€ ์ด๋ฆ„ ์žˆ๋Š” ๊ทธ๋ฃน (?<name>), ์—ฌ๋Ÿฌ ๋งค์นญ์€ matchAll
โœ”๏ธ ์กฐ๊ฑด ํ™•์ธ์€ ์ „ํ›„๋ฐฉ ํƒ์ƒ‰, ๋ฒ”์œ„ ์ขํžˆ๊ธฐ๋Š” ๋ถ€์ • ๋ฌธ์ž ์ง‘ํ•ฉ
โœ”๏ธ ํ•œ๊ธ€ยท์ด๋ชจ์ง€๋Š” u ํ”Œ๋ž˜๊ทธ + \p{}, ์ฒด๊ฐ ๊ธ€์ž ์ˆ˜๋Š” Intl.Segmenter, ๋น„๊ต ์ „ normalize
โœ”๏ธ ์ค‘์ฒฉ ์ˆ˜๋Ÿ‰์ž๋Š” ReDoS์˜ ์”จ์•—, ์ž…๋ ฅ ๊ธธ์ด ์ œํ•œ์€ ํ•„์ˆ˜
โœ”๏ธ URLยทHTMLยท์ˆซ์ž ํฌ๋งท์€ ์ •๊ทœ์‹๋ณด๋‹ค ํ‘œ์ค€ API๊ฐ€ ๋จผ์ €

์ •๊ทœํ‘œํ˜„์‹์€ ์ฒ˜์Œ์—” ์™ธ๊ณ„์–ด์ง€๋งŒ, ํ•œ ๋ฒˆ ์†์— ์ต์œผ๋ฉด ์ˆ˜์‹ญ ์ค„์งœ๋ฆฌ ๋ฌธ์ž์—ด ์ฒ˜๋ฆฌ ์ฝ”๋“œ๋ฅผ ํ•œ ์ค„๋กœ ์ค„์—ฌ์ฃผ๋Š” ๋งˆ๋ฒ• ์ง€ํŒก์ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๐Ÿช„ ์˜ค๋Š˜ ๋ฐฐ์šด ๋ ˆ์‹œํ”ผ ์ค‘ ํ•˜๋‚˜๋งŒ ๋‚ด ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด ๋ด๋„ ์ด๋ฏธ ์ ˆ๋ฐ˜์€ ์„ฑ๊ณต์ด์—์š”. ๋‹ค์Œ์—” ์—ฌ๋Ÿฌ๋ถ„์ด ๊ณ ์–‘์ด ๋ฐœ์ž๊ตญ ๊ฐ™์€ ํŒจํ„ด์„ ์ˆ ์ˆ  ์ฝ์–ด์ฃผ๋Š” ์‚ฌ๋žŒ์ด ๋  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค!

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

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

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

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