Ver5.0 ๐ JavaScript ์ ๊ทํํ์๊ณผ ๋ฌธ์์ด ์ฒ๋ฆฌ ๊ณ ๊ธ ๊ธฐ๋ฒ ์ด์ ๋ฆฌ: ์ธ๊ณ์ด ๊ฐ๋ ํจํด์ ๋ด ๋ฌด๊ธฐ๋ก ๋ง๋๋ ๋ฒ

๐ JavaScript ์ ๊ทํํ์๊ณผ ๋ฌธ์์ด ์ฒ๋ฆฌ ๊ณ ๊ธ ๊ธฐ๋ฒ ์ด์ ๋ฆฌ: ์ธ๊ณ์ด ๊ฐ๋ ํจํด์ ๋ด ๋ฌด๊ธฐ๋ก ๋ง๋๋ ๋ฒ
์ ๊ทํํ์(RegExp)์ ์ฒ์ ๋ณด๋ฉด /^(?=.*\d)(?=.*[a-z]).{8,}$/ ๊ฐ์ ์ค์ด ๊ณ ์์ด๊ฐ ํค๋ณด๋ ์๋ฅผ ๊ฑธ์ด๊ฐ ํ์ ์ฒ๋ผ ๋ณด์ด์ฃ . ๐น
๊ทธ๋ฐ๋ฐ ๊ท์น ๋ช ๊ฐ๋ง ์๋ฉด ์ด ๋ฌธ์ฅ์ "์ซ์ ํ๋, ์๋ฌธ์ ํ๋๋ฅผ ํฌํจํ 8๊ธ์ ์ด์"์ด๋ผ๋ ์์ฃผ ํ๋ฒํ ๋ป์
๋๋ค.
์ด ๊ธ์์๋ ๊ธฐ์ด ๋ฌธ๋ฒ์ ๋น ๋ฅด๊ฒ ์ง๊ณ , ํ๋๊ทธ, ์บก์ฒ ๊ทธ๋ฃน, ์ ํ๋ฐฉ ํ์, ์ ๋์ฝ๋, matchAllยทreplaceAll, ์ฑ๋ฅ ํจ์ (ReDoS)๊น์ง ์ค๋ฌด์์ ํ์ํ ๊ธฐ๋ฒ์ ์ ๋ฆฌํฉ๋๋ค.
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์๋ ๋ค์ ํ๋๊ทธ๋ค์ด ์์ด์.
^, $๊ฐ ๊ฐ ์ค์ ์์ยท๋์ ๋งค์นญ.์ด ์ค๋ฐ๊ฟ๊น์ง ๋งค์นญlastIndex ์์น์์๋ง ๋งค์นญ ์๋์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ฌ๊ณ ๊ฐ ๋๋ ๊ฑด ๋จ์ฐ 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\""
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) | ๋งค์นญ ๋ฐฐ์ด | null | g์ ํจ๊ป ๋ฐ๋ณต ํธ์ถ ๊ฐ๋ฅ |
str.match(re) | ๋ฐฐ์ด | null | g ์์ผ๋ฉด ๊ทธ๋ฃน ์ ๋ณด ์์ด ์ ์ฒด ๋งค์นญ๋ง |
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 (์คํ๋ ๋๋ ์ฝ๋ ํฌ์ธํธ ๋จ์)
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)์ ๋๋ค.
// ๐จ ์ํ ํจํด: ์ค์ฒฉ ์๋์
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) =>
({ "&":"&", "<":"<", ">":">", '"':""", "'":"'" })[c]);
๐ก ์ด๋ฉ์ผ์ ์ ๊ท์์ผ๋ก 100% ๊ฒ์ฆํ๋ ค๋ ์๊ฐ ๊ดด๋ฌผ ํจํด์ด ํ์ํฉ๋๋ค. ํ์์ ๊ฐ๋ณ๊ฒ ์ฒดํฌํ๊ณ , ์ค์ ์ ํจ์ฑ์ ์ธ์ฆ ๋ฉ์ผ๋ก ํ์ธํ๋ ๊ฒ์ด ์ ๊ณ ํ์ค์ ์ธ ์ ๊ทผ์ด์์.
12. ์ ๊ท์ ๋ง๊ณ ๋ ์์๋๋ฉด ์ข์ ๋ฌธ์์ด ๋ฌด๊ธฐ๋ค ๐ก๏ธ
"7".padStart(3,"0") โ "007""hello".at(-1) โ "o" (์์ ์ธ๋ฑ์ค)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
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.