Compare commits

...

13 Commits

Author SHA1 Message Date
Gyubin Han
020a211ac8 Merge pull request #23 from Gyubin-Han/feature/url-short
Feature/url-short: Users 구조 적용, 원본 URL에 Protocol이 없는 경우 발생하는 오류 수정
2025-07-01 18:01:20 +09:00
Gyubin-Han
a2bf43d335 Fix: 원본 URL에 Protocol이 없을 때, 비정상적으로 Re-Direct되는 문제 해결 2025-07-01 17:54:46 +09:00
Gyubin-Han
23042618f0 Feat: Users Entity에 대응하는 UsersRepository 구현 2025-07-01 17:09:29 +09:00
Gyubin-Han
cfb900e4ed Chore: MainController 내 단축 URL로 접속하는 메소드의 주석 수정 2025-07-01 17:08:45 +09:00
Gyubin Han
013152c248 Merge pull request #22 from Gyubin-Han/feature/url-short
Fix: 단축 URL 접속 정보 Entity의 ID 값 누락 수정
2025-07-01 17:00:32 +09:00
Gyubin-Han
e0fec299f9 Fix: 단축 URL 접속 정보 Entity의 ID 값 누락 수정 2025-07-01 16:58:24 +09:00
Gyubin Han
7f6e2e6cd2 Merge pull request #20 from Gyubin-Han/feature/url-short
Feat: 단축 URL로 접속 시, 클라이언트 정보 저장 기능 구현
2025-07-01 16:36:44 +09:00
Gyubin-Han
a166c7cafa Feat: 단축 URL에 접속 시, 클라이언트 정보 저장 기능 구현 2025-07-01 16:33:39 +09:00
Gyubin-Han
12a66debc2 Feat: 단축 URL 접속 정보 Entity에 대한 Repository 구현 2025-07-01 16:31:50 +09:00
Gyubin-Han
1034a5fa44 Merge branch 'main' of https://github.com/Gyubin-Han/UrlShortener into feature/url-short 2025-07-01 16:29:47 +09:00
Gyubin Han
39fc4447a7 Merge pull request #19 from Gyubin-Han/feature/db-ddl
Fix: 단축 URL 접속 정보의 데이터 구조 수정
2025-07-01 16:28:34 +09:00
Gyubin-Han
44fbabed19 Merge branch 'main' of https://github.com/Gyubin-Han/UrlShortener into feature/db-ddl 2025-07-01 16:26:10 +09:00
Gyubin-Han
9ea8160395 Fix: 단축 URL 접속 정보 데이터 구조 수정
User Agent 값이 예상했던 것보다 길어서 기존 20자에서 255자로 수정
2025-07-01 16:25:48 +09:00
6 changed files with 64 additions and 7 deletions

View File

@@ -33,7 +33,7 @@ CREATE TABLE `click_stat` (
`click_stat_id` BIGINT NOT NULL AUTO_INCREMENT,
`url_map_id` BIGINT NOT NULL,
`click_stat_clicked_at` DATETIME NOT NULL,
`click_stat_user_agent` VARCHAR(20) NOT NULL,
`click_stat_user_agent` VARCHAR(255) NOT NULL,
`click_stat_ip_addr` VARCHAR(30) NOT NULL,
PRIMARY KEY(click_stat_id),
FOREIGN KEY(url_map_id) REFERENCES url_map(url_map_id)

View File

@@ -54,12 +54,17 @@ public class MainController {
/**
* 단축 URL 조회 및 원본 URL로 Re-Direct 처리 메소드
* @param shortUrl 단축 URL
* @param request 사용자의 접속 정보
* @param response Re-Direct 응답 처리를 위한 매개변수
*/
@GetMapping("/{shortUrl}")
public void getShortUrl(@PathVariable(name="shortUrl") String shortUrl, HttpServletResponse response){
public void getShortUrl(@PathVariable(name="shortUrl") String shortUrl, HttpServletRequest request, HttpServletResponse response){
// 접속 정보들 추출
String userAgent=request.getHeader("User-Agent");
String userIpAddr=request.getRemoteAddr();
// 단축 URL을 통해, 원본 URL을 가져옴.
String originalUrl=mainService.getOriginalUrl(shortUrl);
String originalUrl=mainService.getOriginalUrl(shortUrl,userAgent,userIpAddr);
// 가져온 원본 URL 값을 적잘한 URL이 될 수 있도록 Re-Direct하기 전에 URL-Encoding
String encodeUrl=UriComponentsBuilder.fromUriString(originalUrl).build().encode().toUriString();
// Re-Direct 상태 코드 설정

View File

@@ -3,6 +3,8 @@ package be.gyu.urlShortener.entity;
import java.time.LocalDateTime;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
@@ -18,6 +20,7 @@ import lombok.NoArgsConstructor;
@AllArgsConstructor
public class ClickStat {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long clickStatId;
@ManyToOne
@JoinColumn(name="url_map_id")

View File

@@ -0,0 +1,11 @@
package be.gyu.urlShortener.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import be.gyu.urlShortener.entity.ClickStat;
@Repository
public interface ClickStatRepository extends JpaRepository<ClickStat,Long>{
}

View File

@@ -0,0 +1,9 @@
package be.gyu.urlShortener.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import be.gyu.urlShortener.entity.Users;
public interface UsersRepository extends JpaRepository<Users,Long>{
}

View File

@@ -3,14 +3,17 @@ package be.gyu.urlShortener.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import be.gyu.urlShortener.entity.ClickStat;
import be.gyu.urlShortener.entity.UrlMap;
import be.gyu.urlShortener.exception.ShortUrlNotFoundException;
import be.gyu.urlShortener.exception.UrlValidationFailedException;
import be.gyu.urlShortener.repository.ClickStatRepository;
import be.gyu.urlShortener.repository.UrlMapRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.time.LocalDateTime;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -21,15 +24,19 @@ import io.seruco.encoding.base62.Base62;
public class MainService {
@Autowired
private UrlMapRepository urlMapRepository;
@Autowired
private ClickStatRepository clickStatRepository;
// HTTP(S) URL 검증 패턴식
private final String urlRegPattern="^((http|https):\\/\\/)?([a-z0-9-]{2,}\\.[a-z]{2,}|([0-9]{1,3}\\.){3}[0-9]{1,3})[\\w.\\/가-힣\\-\\ ?=&:%0-9A-Fa-f]*";
private final Pattern urlPattern=Pattern.compile("^((http|https):\\/\\/)?([a-z0-9-]{2,}\\.[a-z]{2,}|([0-9]{1,3}\\.){3}[0-9]{1,3})[\\w.\\/가-힣\\-\\ ?=&:%0-9A-Fa-f]*");
// URL 앞에 Protocol 존재 여부 확인 패턴식
private final Pattern protocolPattern=Pattern.compile("^(http|https):\\/\\/");
// Base62 Encoder Instance 생성 및 호출
private Base62 base62=Base62.createInstance();
// HTTP(S) URL 검증 메소드
public boolean validOriginalUrl(String url){
return url.matches(urlRegPattern);
return urlPattern.matcher(url).matches();
}
// URL 단축 메소드
@@ -89,14 +96,36 @@ public class MainService {
}
// 단축 URL로 원본 URL 조회 및 반환 메소드
public String getOriginalUrl(String shortUrl){
public String getOriginalUrl(String shortUrl, String userAgent, String ipAddr){
Optional<UrlMap> optional=urlMapRepository.findByUrlMapShort(shortUrl);
if(!optional.isPresent()){
throw new ShortUrlNotFoundException();
}
return optional.get().getUrlMapOriginal();
// 접속 정보 저장을 위해서 새 엔티티 객체 생성
ClickStat clickStat=ClickStat.builder()
.clickStatClickedAt(LocalDateTime.now())
.urlMap(optional.get())
.clickStatUserAgent(userAgent)
.clickStatIpAddr(ipAddr)
.build();
// 접속한 클라이언트의 정보를 DB에 저장
clickStatRepository.save(clickStat);
String originalUrl=clickStat.getUrlMap().getUrlMapOriginal();
// 저장된 원본 URL에 Protocol이 있는지 확인
// ㄴ 없으면, 원본 URL 값에 http 프로토콜을 추가
if(!protocolPattern.matcher(originalUrl).find()){
StringBuilder sb=new StringBuilder();
sb.append("http://");
sb.append(originalUrl);
originalUrl=sb.toString();
}
// 원본 URL을 반환
return originalUrl;
}
// 단축 URL 생성 메소드 (검증 및 생성)