-
Notifications
You must be signed in to change notification settings - Fork 240
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
🚀 3단계 - 즐겨찾기 기능 구현 #558
Open
hellonayeon
wants to merge
25
commits into
next-step:hellonayeon
Choose a base branch
from
hellonayeon:step3/favorite
base: hellonayeon
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
🚀 3단계 - 즐겨찾기 기능 구현 #558
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
6f87f59
Docs: 즐겨찾기 기능 인수 조건 도출
hellonayeon 5867587
Chore: 테스트코드 외부 요청 컨트롤러 패키지 분리 (external 패키지 생성)
hellonayeon 410cd62
Chore: 테스트용 properties 파일 이동 (/src/test)
hellonayeon 7ee3362
Test: 즐겨찾기 기능 인수테스트 Fixture 생성
hellonayeon 6e27bd4
Docs: 즐겨찾기 인수 조건 README.md 패키지 분리
hellonayeon 59b729a
Docs: 인수 조건 오타 수정
hellonayeon 1e135e7
Build: Validation 의존성 추가
hellonayeon 2b4fdb5
Feature: Favorite Domain, Request/Response DTO 작성
hellonayeon 99d2393
Feature: Server Exception Code, Handler 정의 (Bean Validation Handling)
hellonayeon 888fa4f
Test: 즐겨찾기 인수 테스트 작성
hellonayeon 37d70ce
Feat: 즐겨찾기 도메인 Entity, JoinColumn name 설정
hellonayeon b6eedcf
Test: 회원, 비회원 요청 Steps 분리, 목록 조회 검증 조건 수정
hellonayeon 1e83088
Feat: TokenArgumentResolver Authorization null 검증 추가
hellonayeon c0b44d1
Feat: 즐겨찾기 API 구현
hellonayeon 2ead078
Refactor: ArgumentResolver Header Authorization 검증 책임연쇄패턴 적용
hellonayeon 6480e5e
Feat: 경로를 찾을 수 없는 경우 Exception 발생
hellonayeon f6bcc99
Feat: 즐겨찾기 생성 전 경로 조회 로직 추가
hellonayeon b3c7fb2
Docs: 비정상 경로를 즐겨찾기로 등록하는 경우의 인수 조건 도출
hellonayeon f086990
Test: 비정상 경로 즐겨찾기 등록 인수테스트 작성
hellonayeon 0451bfa
Chore: 즐겨찾기 조회 -> 즐겨찾기 목록 조회 워딩 수정, 불필요한 즐겨찾기 Steps 제거
hellonayeon 3b48f34
Fix: 인수 조건과 tc when 주석 통일 시키기
hellonayeon 8a0c8e8
Fix: for-if 를 사용하여 검증하는 로직 제거
hellonayeon eda7832
Fix: 엔티티 기본 생성자 protected 로 선언
hellonayeon 502ec5c
Fix: FavoriteService .save() 사용 메서드 @Transactional 적용
hellonayeon 943ace5
Style: 트랜잭션 확인을 위한 임시 System.out.println 제거
hellonayeon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,22 @@ | ||
package nextstep.auth.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import nextstep.auth.principal.AuthenticationPrincipalArgumentResolver; | ||
import nextstep.auth.token.JwtTokenProvider; | ||
import nextstep.auth.validation.AuthorizationValidatorGroup; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
import java.util.List; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
public class AuthConfig implements WebMvcConfigurer { | ||
private JwtTokenProvider jwtTokenProvider; | ||
|
||
public AuthConfig(JwtTokenProvider jwtTokenProvider) { | ||
this.jwtTokenProvider = jwtTokenProvider; | ||
} | ||
private final JwtTokenProvider jwtTokenProvider; | ||
private final AuthorizationValidatorGroup authorizationValidatorGroup; | ||
|
||
@Override | ||
public void addArgumentResolvers(List argumentResolvers) { | ||
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver(jwtTokenProvider)); | ||
argumentResolvers.add(new AuthenticationPrincipalArgumentResolver(jwtTokenProvider, authorizationValidatorGroup)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/nextstep/auth/validation/AuthorizationBearerStringValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package nextstep.auth.validation; | ||
|
||
import nextstep.auth.exception.AuthenticationException; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class AuthorizationBearerStringValidator extends AuthorizationValidator { | ||
|
||
@Override | ||
protected void validate(String authorization) { | ||
String authPrefix = authorization.split(" ")[0]; | ||
|
||
if (!"bearer".equalsIgnoreCase(authPrefix)) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/nextstep/auth/validation/AuthorizationNullValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package nextstep.auth.validation; | ||
|
||
import nextstep.auth.exception.AuthenticationException; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class AuthorizationNullValidator extends AuthorizationValidator { | ||
|
||
@Override | ||
protected void validate(String authorization) { | ||
if (authorization == null) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/nextstep/auth/validation/AuthorizationTokenValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package nextstep.auth.validation; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import nextstep.auth.exception.AuthenticationException; | ||
import nextstep.auth.token.JwtTokenProvider; | ||
import org.springframework.stereotype.Component; | ||
|
||
@RequiredArgsConstructor | ||
@Component | ||
public class AuthorizationTokenValidator extends AuthorizationValidator { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
protected void validate(String authorization) { | ||
String token = authorization.split(" ")[1]; | ||
|
||
if (!jwtTokenProvider.validateToken(token)) { | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/nextstep/auth/validation/AuthorizationValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package nextstep.auth.validation; | ||
|
||
public abstract class AuthorizationValidator { | ||
|
||
private AuthorizationValidator nextValidator = null; | ||
|
||
public AuthorizationValidator setNext(AuthorizationValidator validator) { | ||
this.nextValidator = validator; | ||
return validator; | ||
} | ||
|
||
protected abstract void validate(String authorization); | ||
|
||
public void execute(String authorization) { | ||
validate(authorization); | ||
|
||
if (nextValidator != null) | ||
nextValidator.execute(authorization); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/main/java/nextstep/auth/validation/AuthorizationValidatorGroup.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package nextstep.auth.validation; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class AuthorizationValidatorGroup { | ||
|
||
private final AuthorizationNullValidator authorizationNullValidator; | ||
private final AuthorizationBearerStringValidator authorizationBearerStringValidator; | ||
private final AuthorizationTokenValidator authorizationTokenValidator; | ||
|
||
private void chaining() { | ||
authorizationNullValidator | ||
.setNext(authorizationBearerStringValidator) | ||
.setNext(authorizationTokenValidator); | ||
} | ||
|
||
public void execute(String authorization) { | ||
chaining(); | ||
authorizationNullValidator.execute(authorization); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/main/java/nextstep/subway/applicaion/FavoriteService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package nextstep.subway.applicaion; | ||
|
||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import nextstep.subway.applicaion.dto.FavoriteRequest; | ||
import nextstep.subway.applicaion.dto.PathResponse; | ||
import nextstep.subway.applicaion.dto.StationResponse; | ||
import nextstep.subway.domain.Favorite; | ||
import nextstep.subway.domain.FavoriteRepository; | ||
import nextstep.subway.domain.Station; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class FavoriteService { | ||
|
||
private final FavoriteRepository favoriteRepository; | ||
private final StationService stationService; | ||
private final PathService pathService; | ||
|
||
|
||
public List<Favorite> getFavorites(Long memberId) { | ||
return favoriteRepository.findAllByMemberId(memberId); | ||
} | ||
|
||
public Favorite getFavorite(Long id) { | ||
return favoriteRepository.findById(id) | ||
.orElseThrow(IllegalArgumentException::new); | ||
} | ||
|
||
public Favorite createFavorite(Long memberId, FavoriteRequest request) { | ||
pathService.findPath(request.getSource(), request.getTarget()); | ||
|
||
Station source = stationService.findById(request.getSource()); | ||
Station target = stationService.findById(request.getTarget()); | ||
return favoriteRepository.save(new Favorite(source, target, memberId)); | ||
} | ||
|
||
public void deleteFavorite(Long id) { | ||
favoriteRepository.deleteById(id); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/nextstep/subway/applicaion/dto/FavoriteRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package nextstep.subway.applicaion.dto; | ||
|
||
import javax.validation.constraints.NotNull; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor | ||
@Getter | ||
public class FavoriteRequest { | ||
|
||
@NotNull(message = "출발역은 반드시 입력해야합니다.") | ||
private Long source; | ||
|
||
@NotNull(message = "도착역은 반드시 입력해야합니다.") | ||
private Long target; | ||
|
||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/nextstep/subway/applicaion/dto/FavoriteResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package nextstep.subway.applicaion.dto; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import nextstep.subway.domain.Favorite; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public class FavoriteResponse { | ||
private Long id; | ||
private StationResponse source; | ||
private StationResponse target; | ||
|
||
public static FavoriteResponse of(Favorite favorite) { | ||
return new FavoriteResponse(favorite.getId(), | ||
StationResponse.of(favorite.getSource()), | ||
StationResponse.of(favorite.getTarget())); | ||
} | ||
|
||
public static List<FavoriteResponse> listOf(List<Favorite> favorites) { | ||
return favorites.stream() | ||
.map(FavoriteResponse::of) | ||
.collect(Collectors.toList()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package nextstep.subway.domain; | ||
|
||
import javax.persistence.CascadeType; | ||
import javax.persistence.Entity; | ||
import javax.persistence.GeneratedValue; | ||
import javax.persistence.GenerationType; | ||
import javax.persistence.Id; | ||
import javax.persistence.JoinColumn; | ||
import javax.persistence.OneToOne; | ||
import lombok.Getter; | ||
|
||
@Entity | ||
@Getter | ||
public class Favorite { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@OneToOne(cascade = CascadeType.PERSIST) | ||
@JoinColumn(name = "source_id") | ||
private Station source; | ||
|
||
@OneToOne(cascade = CascadeType.PERSIST) | ||
@JoinColumn(name = "target_id") | ||
private Station target; | ||
|
||
@JoinColumn(name = "member_id") | ||
private Long memberId; | ||
|
||
public Favorite() { | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 엔티티의 기본 생성자는 protected로 선언해 보시는 것도 추천드립니다 😃 |
||
|
||
public Favorite(Station source, Station target, Long memberId) { | ||
this.source = source; | ||
this.target = target; | ||
this.memberId = memberId; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/nextstep/subway/domain/FavoriteRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package nextstep.subway.domain; | ||
|
||
import java.util.List; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface FavoriteRepository extends JpaRepository<Favorite, Long> { | ||
|
||
List<Favorite> findAllByMemberId(Long id); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/nextstep/subway/exception/BindingExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package nextstep.subway.exception; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.BindException; | ||
import org.springframework.validation.BindingResult; | ||
import org.springframework.validation.FieldError; | ||
import org.springframework.web.bind.annotation.ControllerAdvice; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
|
||
@ControllerAdvice | ||
@Slf4j | ||
public class BindingExceptionHandler { | ||
|
||
// @ModelAttribute 바인딩 파라미터 검증의 경우 BindException 핸들링 | ||
@ExceptionHandler({BindException.class}) | ||
public ResponseEntity<?> errorValid(BindException exception) { | ||
BindingResult bindingResult = exception.getBindingResult(); | ||
|
||
StringBuilder sb = new StringBuilder(); | ||
sb.append("parameter: binding error message"); | ||
for (FieldError fieldError : bindingResult.getFieldErrors()) { | ||
sb.append(fieldError.getField()).append(": "); | ||
sb.append(fieldError.getDefaultMessage()); | ||
sb.append(System.lineSeparator()); | ||
} | ||
log.error(sb.toString()); | ||
|
||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(new ErrorResponse(HttpStatus.BAD_REQUEST, ServerErrorCode.BAD_PARAMETER.getMessage())); | ||
} | ||
|
||
// @RequestBody 바인딩 파라미터 검증의 경우 MethodArgumentNotValidException 핸들링 | ||
// ... | ||
|
||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
스프링에서 제공하는 transactional을 적절히 활용해 보셔도 좋을 것 같아요