From 6d1c76cca635e9f8b1f1a222ac9c3bbdf7e2a885 Mon Sep 17 00:00:00 2001 From: Gyubin-Han Date: Tue, 1 Jul 2025 15:22:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Feat:=20=EB=8B=A8=EC=B6=95=20URL=20?= =?UTF-8?q?=EC=A0=91=EC=86=8D=20=EC=A0=95=EB=B3=B4=20Entity=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../be/gyu/urlShortener/entity/ClickStat.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/be/gyu/urlShortener/entity/ClickStat.java diff --git a/src/main/java/be/gyu/urlShortener/entity/ClickStat.java b/src/main/java/be/gyu/urlShortener/entity/ClickStat.java new file mode 100644 index 0000000..a71d69d --- /dev/null +++ b/src/main/java/be/gyu/urlShortener/entity/ClickStat.java @@ -0,0 +1,28 @@ +package be.gyu.urlShortener.entity; + +import java.time.LocalDateTime; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ClickStat { + @Id + private Long clickStatId; + @ManyToOne + @JoinColumn(name="url_map_id") + private UrlMap urlMap; + private LocalDateTime clickStatClickedAt; + private String clickStatUserAgent; + private String clickStatIpAddr; +} From 12a66debc27001f1458544ce35435804afdc0eb8 Mon Sep 17 00:00:00 2001 From: Gyubin-Han Date: Tue, 1 Jul 2025 16:31:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Feat:=20=EB=8B=A8=EC=B6=95=20URL=20?= =?UTF-8?q?=EC=A0=91=EC=86=8D=20=EC=A0=95=EB=B3=B4=20Entity=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20Repository=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../urlShortener/repository/ClickStatRepository.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/be/gyu/urlShortener/repository/ClickStatRepository.java diff --git a/src/main/java/be/gyu/urlShortener/repository/ClickStatRepository.java b/src/main/java/be/gyu/urlShortener/repository/ClickStatRepository.java new file mode 100644 index 0000000..b12710b --- /dev/null +++ b/src/main/java/be/gyu/urlShortener/repository/ClickStatRepository.java @@ -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{ + +} From a166c7cafa243966cf173ea9ae0d571fbf6bcc82 Mon Sep 17 00:00:00 2001 From: Gyubin-Han Date: Tue, 1 Jul 2025 16:33:39 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Feat:=20=EB=8B=A8=EC=B6=95=20URL=EC=97=90?= =?UTF-8?q?=20=EC=A0=91=EC=86=8D=20=EC=8B=9C,=20=ED=81=B4=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=96=B8=ED=8A=B8=20=EC=A0=95=EB=B3=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/MainController.java | 8 ++++++-- .../gyu/urlShortener/service/MainService.java | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/be/gyu/urlShortener/controller/MainController.java b/src/main/java/be/gyu/urlShortener/controller/MainController.java index b05fa60..21949a3 100644 --- a/src/main/java/be/gyu/urlShortener/controller/MainController.java +++ b/src/main/java/be/gyu/urlShortener/controller/MainController.java @@ -57,9 +57,13 @@ public class MainController { * @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 상태 코드 설정 diff --git a/src/main/java/be/gyu/urlShortener/service/MainService.java b/src/main/java/be/gyu/urlShortener/service/MainService.java index ac274a6..348d11a 100644 --- a/src/main/java/be/gyu/urlShortener/service/MainService.java +++ b/src/main/java/be/gyu/urlShortener/service/MainService.java @@ -3,9 +3,11 @@ 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; @@ -21,6 +23,8 @@ 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]*"; @@ -89,14 +93,24 @@ public class MainService { } // 단축 URL로 원본 URL 조회 및 반환 메소드 - public String getOriginalUrl(String shortUrl){ + public String getOriginalUrl(String shortUrl, String userAgent, String ipAddr){ Optional 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(); + + clickStatRepository.save(clickStat); + + return clickStat.getUrlMap().getUrlMapOriginal(); } // 단축 URL 생성 메소드 (검증 및 생성)