From 39e2b429957e605ed2caf6d4d3909647a38728f7 Mon Sep 17 00:00:00 2001 From: "hehihoho3@gmail.com" Date: Mon, 29 Jun 2026 17:04:15 +0900 Subject: [PATCH] =?UTF-8?q?feat(comment-cards):=20=EC=9C=A0=ED=8A=9C?= =?UTF-8?q?=EB=B8=8C=20videoId=20=ED=8C=8C=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit watch/youtu.be/shorts/embed/순수ID 추출, 단위 테스트 포함 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../service/YoutubeVideoIdParser.java | 30 +++++++++++++ .../service/YoutubeVideoIdParserTest.java | 43 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/main/java/com/hlab/yanalyst/service/YoutubeVideoIdParser.java create mode 100644 src/test/java/com/hlab/yanalyst/service/YoutubeVideoIdParserTest.java diff --git a/src/main/java/com/hlab/yanalyst/service/YoutubeVideoIdParser.java b/src/main/java/com/hlab/yanalyst/service/YoutubeVideoIdParser.java new file mode 100644 index 0000000..d6819cc --- /dev/null +++ b/src/main/java/com/hlab/yanalyst/service/YoutubeVideoIdParser.java @@ -0,0 +1,30 @@ +package com.hlab.yanalyst.service; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** 유튜브 URL 또는 11자리 영상 ID에서 videoId를 추출한다. 실패 시 null. */ +public final class YoutubeVideoIdParser { + + private static final Pattern BARE = Pattern.compile("^[A-Za-z0-9_-]{11}$"); + private static final Pattern[] PATTERNS = { + Pattern.compile("[?&]v=([A-Za-z0-9_-]{11})"), + Pattern.compile("youtu\\.be/([A-Za-z0-9_-]{11})"), + Pattern.compile("/shorts/([A-Za-z0-9_-]{11})"), + Pattern.compile("/embed/([A-Za-z0-9_-]{11})"), + }; + + private YoutubeVideoIdParser() {} + + public static String parse(String input) { + if (input == null) return null; + String s = input.trim(); + if (s.isEmpty()) return null; + if (BARE.matcher(s).matches()) return s; + for (Pattern p : PATTERNS) { + Matcher m = p.matcher(s); + if (m.find()) return m.group(1); + } + return null; + } +} diff --git a/src/test/java/com/hlab/yanalyst/service/YoutubeVideoIdParserTest.java b/src/test/java/com/hlab/yanalyst/service/YoutubeVideoIdParserTest.java new file mode 100644 index 0000000..cbaf2c7 --- /dev/null +++ b/src/test/java/com/hlab/yanalyst/service/YoutubeVideoIdParserTest.java @@ -0,0 +1,43 @@ +package com.hlab.yanalyst.service; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class YoutubeVideoIdParserTest { + + @Test + void parsesWatchUrl() { + assertEquals("dQw4w9WgXcQ", + YoutubeVideoIdParser.parse("https://www.youtube.com/watch?v=dQw4w9WgXcQ")); + } + + @Test + void parsesWatchUrlWithExtraParams() { + assertEquals("dQw4w9WgXcQ", + YoutubeVideoIdParser.parse("https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=42s&list=ABC")); + } + + @Test + void parsesShortLink() { + assertEquals("dQw4w9WgXcQ", + YoutubeVideoIdParser.parse("https://youtu.be/dQw4w9WgXcQ?si=xyz")); + } + + @Test + void parsesShortsLink() { + assertEquals("abc12345678", + YoutubeVideoIdParser.parse("https://www.youtube.com/shorts/abc12345678")); + } + + @Test + void parsesBareId() { + assertEquals("dQw4w9WgXcQ", YoutubeVideoIdParser.parse("dQw4w9WgXcQ")); + } + + @Test + void returnsNullForGarbage() { + assertNull(YoutubeVideoIdParser.parse("not a youtube link")); + assertNull(YoutubeVideoIdParser.parse("")); + assertNull(YoutubeVideoIdParser.parse(null)); + } +}