Skip to content

링크 저장 URL 정규화 및 검증 적용 - #300

Open
goder-0 wants to merge 1 commit into
mainfrom
feature/issue-298-url-normalization
Open

링크 저장 URL 정규화 및 검증 적용#300
goder-0 wants to merge 1 commit into
mainfrom
feature/issue-298-url-normalization

Conversation

@goder-0

@goder-0 goder-0 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

관련 이슈

PR 설명

  • 공통 UrlNormalizer를 추가해 앞뒤 공백과 줄바꿈을 제거하고, 중복된 HTTP/HTTPS 프로토콜을 하나로 정리합니다.
  • 프로토콜이 없는 URL에는 https://를 보완하고 HTTP/HTTPS 외 프로토콜과 점이 없는 도메인을 거부합니다.
  • 링크 생성과 URL 중복 확인이 동일한 정규화 결과를 사용하도록 LinkService에 적용했습니다.
  • 메타데이터 수집은 정규화된 URL을 대상으로 기존 SSRF 검증을 실행한 뒤 크롤링합니다.
  • http 프로토콜과 URL의 path, query, fragment는 그대로 보존합니다.

검증

  • URL 정규화, 링크 생성·중복 확인, 메타데이터 수집 전 SSRF 검증 단위 테스트 통과
  • main/test 체크스타일 통과
  • 전체 테스트는 로컬 테스트 환경의 AWS 설정 부재로 ApplicationContext 기반 테스트가 시작되지 못했으며, 변경 관련 테스트는 모두 통과했습니다.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

📊 코드 커버리지 리포트

Overall Project 91.11% -0.07% 🍏
Files changed 97.04% 🍏

File Coverage
LinkService.java 100% 🍏
UrlNormalizer.java 96.51% -3.49% 🍏

Comment on lines +22 to +32
public String normalize(String rawUrl) {
if (rawUrl == null || rawUrl.isBlank()) {
throw new BusinessException(LinkErrorCode.INVALID_URL);
}

String normalizedUrl = removeDuplicatedHttpScheme(rawUrl.strip());
if (!SCHEME_PATTERN.matcher(normalizedUrl).find()) {
normalizedUrl = "https://" + normalizedUrl;
}

validateHost(normalizedUrl);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalize() 가 대소문자를 보존해서, 같은 링크가 케이스만 다르면 서로 다른 문자열로 저장됩니다.
중복 판정이 existsByMemberAndUrlAndIsDeleteFalse 의 문자열 동등 비교라서 아래 입력에 대해서 전부 별개 링크로 저장됩니다.

urlNormalizer.normalize("Naver.com");          // -> "https://Naver.com"
urlNormalizer.normalize("naver.com");          // -> "https://naver.com"  (다른 문자열)
urlNormalizer.normalize("HTTPS://NAVER.COM");  // -> "HTTPS://NAVER.COM"  (그대로 통과)

validateHost 가 파싱한 URI 를 반환하게 하고, 스킴·호스트 구간만 소문자화하도록 수정 부탁드립니다.

추가로 수정 후 아래 케이스에 대한 테스트 추가를 제안합니다

	"Naver.com, https://naver.com",
	"HTTPS://NAVER.COM, https://naver.com",
	"'https://Naver.com/Path?Q=Abc#Frag', 'https://naver.com/Path?Q=Abc#Frag'"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다. URI 파싱 후 스킴과 호스트만 Locale.ROOT 기준으로 소문자화하고, path/query/fragment는 원문의 대소문자와 값을 그대로 보존하도록 수정했습니다. 제안해 주신 대소문자 입력 케이스도 테스트에 추가했습니다.

Comment on lines +14 to +33
@Test
@DisplayName("메타데이터 수집 전 정규화된 URL을 SSRF 검증한다")
void validateNormalizedUrlBeforeCrawling() {
UrlValidator urlValidator = mock(UrlValidator.class);
UrlNormalizer urlNormalizer = mock(UrlNormalizer.class);
OgTagCrawler ogTagCrawler = new OgTagCrawler(urlValidator, urlNormalizer);
String rawUrl = " 192.168.0.1 ";
String normalizedUrl = "https://192.168.0.1";

given(urlNormalizer.normalize(rawUrl)).willReturn(normalizedUrl);
willThrow(new BusinessException(LinkErrorCode.INVALID_URL_PRIVATE_IP))
.given(urlValidator).validateSafeUrl(normalizedUrl);

assertThatThrownBy(() -> ogTagCrawler.crawl(rawUrl))
.isInstanceOf(BusinessException.class)
.hasFieldOrPropertyWithValue("errorCode", LinkErrorCode.INVALID_URL_PRIVATE_IP);

then(urlNormalizer).should().normalize(rawUrl);
then(urlValidator).should().validateSafeUrl(normalizedUrl);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OgTagCrawlerTest 는 "정규화된 URL 을 SSRF 검증한다"는 잘 테스트하고 있지만, "정규화된 URL 로 접속한다"는 검증하지 못하고 있습니다.

실제로 OgTagCrawler 파일을 아래와 같이 수정한 후 OgTagCrawlerTest 를 실행하면 그대로 통과하고 있습니다.

// OgTagCrawler.crawl() 에 회귀 주입: 검증은 정규화 URL, 접속은 원본 URL
Document document = Jsoup.connect(url)   // normalizedUrl -> url 로 되돌려도

이 상태에서 현재 테스트는 검증 스텁이 먼저 예외를 던지는 경로만 타서 Jsoup.connect(...) 라인이 실행되지 않기 때문입니다.
나중에 리팩토링 과정에서 접속 대상이 원본 url 로 돌아가는 실수가 생기면, 스킴 없는 입력(naver.com)의 meta-scrape 가 Jsoup IllegalArgumentException 으로 500 을 반환하는데 이를 잡아줄 테스트가 없습니다. 실수 방지를 위해 접속 대상까지 잠그는 테스트 보강을 부탁드립니다

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다. MockWebServer를 이용해 스킴이 없는 원본 입력이 정규화된 서버 URL로 실제 요청되는지 검증했습니다. 요청 경로와 쿼리 파라미터까지 확인하도록 테스트를 보강했습니다.

public OgTagCrawler(UrlValidator urlValidator, UrlNormalizer urlNormalizer) {
this.urlValidator = urlValidator;
this.urlNormalizer = urlNormalizer;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

의존성 주입이 생성자 주입 방식으로 진행되고 있는데 통일성을 위해 해당 방식을 @RequiredArgsConstructor 로 수정부탁드립니다.

@goder-0 goder-0 Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다. 직접 작성한 생성자를 제거하고 @RequiredArgsConstructor를 사용하도록 변경했습니다.

@goder-0
goder-0 force-pushed the feature/issue-298-url-normalization branch from 8c81acf to e826212 Compare August 25, 2026 17:35
@goder-0
goder-0 force-pushed the feature/issue-298-url-normalization branch from e826212 to 9efce97 Compare August 26, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

링크 저장 URL 정규화 및 검증 적용

2 participants