Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4기 - 박세영] Url Shortener 과제 PR입니다 #45

Open
wants to merge 29 commits into
base: SY97P
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
41b1706
chore: initial commit
onetuks Oct 4, 2023
ded88b0
feat: Url 엔티티 및 값 객체 생성
onetuks Oct 4, 2023
4e9c2c6
chore: 타임리프 의존성 추가
onetuks Oct 5, 2023
20c2897
docs: README 파일 수정
onetuks Oct 5, 2023
30c883f
feat: UrlMapping 모델 추가
onetuks Oct 5, 2023
b6340dc
feat: 매핑 정보 조회
onetuks Oct 5, 2023
aa32d66
chore: test 설정 추가
onetuks Oct 6, 2023
e12e000
feat: 단축 알고리즘 추가
onetuks Oct 6, 2023
b4afbd1
feat: url 단축 기능 추가 (등록)
onetuks Oct 6, 2023
09e4b55
feat: 단축 URL 리다이렉션 기능 추가
onetuks Oct 6, 2023
0376159
refactor: 매핑 단건 조회를 전체 조회로 변경
onetuks Oct 6, 2023
55557a2
docs: README 파일 수정
onetuks Oct 6, 2023
f32d336
refactor: 타임리프 UI 수정
onetuks Oct 6, 2023
7c9985b
docs: README 파일 완성
onetuks Oct 6, 2023
af827aa
style: 코드 포매팅
onetuks Oct 6, 2023
a2c16e3
refactor: 알고리즘 클래스에서 외부 변수명 제거
onetuks Oct 8, 2023
c01bc13
refactor: page 스펙을 백엔드에서 제어하도록 수정
onetuks Oct 8, 2023
6827891
refactor: NPE 방어 위해 result 에서 값 객체 값 찾도록 수정
onetuks Oct 8, 2023
7ca12ed
refactor: 단축url 생성 로직 수정
onetuks Oct 8, 2023
d80e827
refactor: 미디어 타입 명시
onetuks Oct 8, 2023
5aedbbf
refactor: 리다이렉션 경로 반환을 OriginUrl 객체 내에서 생성하도록 수정
onetuks Oct 8, 2023
ff0837d
refactor: Exception, logging 관련 처리
onetuks Oct 9, 2023
46dfa03
refactor: 문맥 따라 개행
onetuks Oct 9, 2023
adffb08
refactor: DTO 변환 static 메소드 인스턴스화
onetuks Oct 9, 2023
337bfe2
refactor: Algorithm enum 클래스의 JPA 의존성 제거
onetuks Oct 9, 2023
cb5fbf9
refactor: UrlMapping 엔티티 생성자 수정
onetuks Oct 9, 2023
f003c42
test: 테스트 DisplayName 변경
onetuks Oct 9, 2023
79620d8
refactor: POST 요청 body 방식으로 변경
onetuks Oct 9, 2023
bd55aad
fix: 패키지명 수정
onetuks Oct 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: Url 엔티티 및 값 객체 생성
onetuks committed Oct 4, 2023
commit ded88b0eee1a852c39e9b471484465ac9b0a3cfc
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# springboot-url-shortener

SprintBoot URL Shortener 구현 미션 Repository 입니다.

## 요구사항
@@ -7,7 +8,7 @@ SprintBoot URL Shortener 구현 미션 Repository 입니다.
- [ ] URL Shortening Key는 8 Character 이내로 생성
- [ ] 단축된 URL 요청시 원래 URL로 리다이렉트
- [ ] 단축된 URL에 대한 요청 수 정보저장 (optional)
- [ ] Shortening Key를 생성하는 알고리즘 2개 이상 제공하며 애플리케이션 실행중 동적으로 변경 가능 (optional)
- [ ] Shortening Key를 생성하는 알고리즘 2개 이상 제공하며 애플리케이션 실행중 동적으로 변경 가능 (optional)

## API 명세

@@ -16,20 +17,24 @@ SprintBoot URL Shortener 구현 미션 Repository 입니다.
## 동작화면

## Short URL Service

### 읽으면 좋은 레퍼런스

- [Naver 단축 URL API](https://developers.naver.com/docs/utils/shortenurl/)
- [짧게 줄인 URL의 실제 URL 확인 원리 및 방법](https://metalkin.tistory.com/50)
- [짧게 줄인 URL 알고리즘 고찰](https://metalkin.tistory.com/53)
- [단축 URL 원리 및 개발](https://blog.siyeol.com/26)

### Short URL의 동작 과정

1. 원본 URL을 입력하고 Shorten 버튼을 클릭합니다.
2. Unique Key를 7문자 생성합니다.
3. Unique Key와 원본 URL을 DB에 저장합니다.
4. bitly.com/{Unique Key} 로 접근하면, DB를 조회하여 원본 URL로 redirect합니다.

### Short URL의 특징
단축 URL서비스는 간편하지만, 단점(위험성)이 있습니다.

단축 URL서비스는 간편하지만, 단점(위험성)이 있습니다.
링크를 클릭하는 사용자는 단축된 URL만 보고 클릭하기 때문에 어떤 곳으로 이동할지 알 수 없습니다.

- Short URL 서비스는 주로 요청을 Redirect 시킵니다. (Redirect와 Forward의 차이점에 대해 검색해보세요.)
@@ -40,4 +45,5 @@ SprintBoot URL Shortener 구현 미션 Repository 입니다.
- 기존의 원본 URL 변경되었더라도 단축 URL을 유지하여, 혼란을 방지할 수 있다.

### 예시 사이트

[https://url.kr/](https://url.kr/)
27 changes: 27 additions & 0 deletions src/main/java/com/tangerine/urlshortener/Algorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.tangerine.urlshortener;

import io.micrometer.common.util.StringUtils;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.NotBlank;
import org.springframework.util.Assert;

@Embeddable
public class Algorithm {

@NotBlank
@Column(name = "algorithm", nullable = false)
private String algorithm;

public Algorithm(String algorithm) {
Assert.isTrue(StringUtils.isNotBlank(algorithm), "단축 Url 알고리즘은 반드시 주어져야 합니다.");
this.algorithm = algorithm;
}

protected Algorithm() {
}

public String getAlgorithm() {
return algorithm;
}
}
27 changes: 27 additions & 0 deletions src/main/java/com/tangerine/urlshortener/OriginUrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.tangerine.urlshortener;

import io.micrometer.common.util.StringUtils;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.NotBlank;
import org.springframework.util.Assert;

@Embeddable
public class OriginUrl {

@NotBlank
@Column(name = "origin_url", nullable = false, unique = true)
private String originUrlText;

public OriginUrl(String originUrlText) {
Assert.isTrue(StringUtils.isNotBlank(originUrlText), "원본 url은 반드시 주어져야 합니다.");
this.originUrlText = originUrlText;
}

protected OriginUrl() {
}

public String getOriginUrlText() {
return originUrlText;
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/tangerine/urlshortener/RequestCount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tangerine.urlshortener;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.Positive;
import org.springframework.util.Assert;

@Embeddable
public class RequestCount {

@Positive
@Column(name = "request_count", nullable = false)
private long requestCount;

public RequestCount(long requestCount) {
Assert.isTrue(requestCount >= 0, "호출 횟수는 양수여야 합니다.");
this.requestCount = requestCount;
}

protected RequestCount() {
}

public long getRequestCount() {
return requestCount;
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/tangerine/urlshortener/ShortUrl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.tangerine.urlshortener;

import io.micrometer.common.util.StringUtils;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.NotBlank;
import org.springframework.util.Assert;

@Embeddable
public class ShortUrl {

@NotBlank
@Column(name = "short_url", nullable = false, unique = true)
private String shortUrlText;

public ShortUrl(String shortUrlText) {
Assert.isTrue(StringUtils.isNotBlank(shortUrlText), "단축 url은 반드시 주어져야 합니다.");
Assert.isTrue(shortUrlText.length() <= 8, "단축 url은 8자 이하여야 합니다.");
this.shortUrlText = shortUrlText;
}

protected ShortUrl() {
}

public String getShortUrlText() {
return shortUrlText;
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/tangerine/urlshortener/Url.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.tangerine.urlshortener;

import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "urls")
public class Url {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Embedded
private OriginUrl originUrl;

@Embedded
private ShortUrl shortUrl;

@Embedded
private Algorithm algorithm;

@Embedded
private RequestCount requestCount;

public Url(OriginUrl originUrl, ShortUrl shortUrl, Algorithm algorithm, RequestCount requestCount) {
this.originUrl = originUrl;
this.shortUrl = shortUrl;
this.algorithm = algorithm;
this.requestCount = requestCount;
}

protected Url() {
}

public Long getId() {
return id;
}

public OriginUrl getOriginUrl() {
return originUrl;
}

public String getOriginUrlText() {
return originUrl.getOriginUrlText();
}

public ShortUrl getShortUrl() {
return shortUrl;
}

public String getShortUrlText() {
return shortUrl.getShortUrlText();
}

public Algorithm getAlgorithm() {
return algorithm;
}

public RequestCount getRequestCount() {
return requestCount;
}

}
1 change: 0 additions & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

spring:
h2:
console:
51 changes: 51 additions & 0 deletions src/test/java/com/tangerine/urlshortener/UrlTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.tangerine.urlshortener;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchException;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class UrlTest {

@Test
@DisplayName("올바른 정보로 생성한다.")
void instance_Success() {
// Given
String originUrlText = "origin";
String shortUrlText = "short";

// When
Url url = new Url(
new OriginUrl(originUrlText),
new ShortUrl(shortUrlText),
new Algorithm("알고리즘"),
new RequestCount(0)
);

// Then
assertThat(url.getOriginUrlText()).isEqualTo(originUrlText);
assertThat(url.getShortUrlText()).isEqualTo(shortUrlText);
}

@Test
@DisplayName("올바르지 않은 정보로 생성 실패한다.")
void instance_fail() {
// Given
String originUrlText = "";
String shortUrlText = "short";

// When
Exception exception = catchException(() ->
new Url(
new OriginUrl(originUrlText),
new ShortUrl(shortUrlText),
new Algorithm(""),
new RequestCount(-1)
));

// Then
assertThat(exception).isInstanceOf(IllegalArgumentException.class);
}

}