diff --git a/README.md b/README.md
index e147912..5422901 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ import {
// String utilities
const escaped = stringUtil.escapeHtml("
Hello
");
const unescaped = stringUtil.unescapeHtml("<div>Hello</div>");
+const slug = stringUtil.slugify("Hello World! 안녕하세요"); // "hello-world-안녕하세요"
// Object utilities
const cleaned = objectUtil.clearNullProperties({ a: 1, b: null, c: 3 });
@@ -150,6 +151,7 @@ storage.set("data", { key: "value" });
- `escapeHtml(str: string): string` - Escapes HTML special characters
- `unescapeHtml(str: string): string` - Unescapes HTML entities
+- `slugify(text: string): string` - Converts a string to URL-friendly slug format. Replaces spaces with hyphens, removes special characters, converts to lowercase, and supports Korean characters (e.g., "Hello World! 안녕" → "hello-world-안녕")
### ObjectUtil
@@ -213,6 +215,7 @@ storage.set("data", { key: "value" });
### Retry
- `retry(fn: () => Promise, loop?: number): Promise` - Retries an asynchronous function up to the specified number of times (default 3) if it fails. Automatically re-attempts on error and returns the result of the first successful execution, or throws the last error if all retries fail.
+
### SearchQueryUtil
- `getAllQuery(): Record` - Parses the current URL's query string and returns an object with key-value pairs. Values appear as arrays when the same key is used multiple times.
diff --git a/package/stringUtil/index.ts b/package/stringUtil/index.ts
index 14d19e3..2dabd38 100644
--- a/package/stringUtil/index.ts
+++ b/package/stringUtil/index.ts
@@ -1,2 +1,3 @@
-export { default as escapeHtml } from './escapeHtml';
-export { default as unescapeHtml } from './unescapeHtml';
+export { default as escapeHtml } from "./escapeHtml";
+export { default as unescapeHtml } from "./unescapeHtml";
+export { default as slugify } from "./slugify";
diff --git a/package/stringUtil/slugify/index.test.ts b/package/stringUtil/slugify/index.test.ts
new file mode 100644
index 0000000..a8e7b92
--- /dev/null
+++ b/package/stringUtil/slugify/index.test.ts
@@ -0,0 +1,52 @@
+import { describe, expect, test } from "vitest";
+import slugify from ".";
+
+describe("slugify", () => {
+ test("문자열을 slug 형태로 변환한다.", () => {
+ const input = " Hello World! This is a Test. ";
+ const output = slugify(input);
+ expect(output).toBe("hello-world-this-is-a-test");
+ });
+
+ test("특수 문자가 포함된 문자열을 올바르게 변환한다.", () => {
+ const input = "Café & Restaurant @ Downtown #1";
+ const output = slugify(input);
+ expect(output).toBe("caf-restaurant-downtown-1");
+ });
+
+ test("여러 공백과 대시가 포함된 문자열을 올바르게 변환한다.", () => {
+ const input = "This is---a test---string";
+ const output = slugify(input);
+ expect(output).toBe("this-is-a-test-string");
+ });
+
+ test("빈 문자열을 처리한다.", () => {
+ expect(slugify("")).toBe("");
+ expect(slugify(" ")).toBe("");
+ });
+
+ test("숫자가 포함된 문자열을 올바르게 변환한다.", () => {
+ const input = "Product 123 - Version 2.0";
+ const output = slugify(input);
+ expect(output).toBe("product-123-version-20");
+ });
+
+ test("특수 문자만 있는 경우를 처리한다.", () => {
+ const input = "!@#$%^&*()";
+ const output = slugify(input);
+ expect(output).toBe("");
+ });
+
+ test("한글이 포함된 문자열을 처리한다.", () => {
+ expect(slugify("안녕하세요")).toBe("안녕하세요");
+ expect(slugify("한글 테스트")).toBe("한글-테스트");
+ expect(slugify("안녕하세요 Hello World")).toBe("안녕하세요-hello-world");
+ expect(slugify("프로젝트 개발")).toBe("프로젝트-개발");
+ });
+
+ test("slug 형태의 문자열은 그대로 slug 형태로 반환한다.", () => {
+ const input = "Hello-World";
+ const output = slugify(input);
+ expect(output).toBe("hello-world");
+ });
+});
diff --git a/package/stringUtil/slugify/index.ts b/package/stringUtil/slugify/index.ts
new file mode 100644
index 0000000..d8d2c1d
--- /dev/null
+++ b/package/stringUtil/slugify/index.ts
@@ -0,0 +1,19 @@
+/**
+ * 문자열을 URL 친화적인 slug 형태로 변환합니다.
+ * 공백을 대시(-)로 변환하고, 특수문자를 제거하며, 소문자로 변환합니다.
+ * 한글도 지원합니다.
+ *
+ * @param text - slug로 변환할 문자열
+ * @returns URL 친화적인 slug 문자열
+ */
+export default function slugify(text: string): string {
+ return text
+ .toString()
+ .trim()
+ .toLowerCase()
+ .replace(/\s+/g, "-")
+ .replace(/[^a-zA-Z0-9가-힣\-]+/g, "")
+ .replace(/\-\-+/g, "-")
+ .replace(/^-+/, "")
+ .replace(/-+$/, "");
+}