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

๐Ÿš€ 3๋‹จ๊ณ„ - ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ #352

Open
wants to merge 16 commits into
base: epicarts
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
43253c5
feat: AuthenticationManager ์ธํ„ฐํŽ˜์ด์Šค ์ž‘์„ฑ
epicarts Aug 15, 2022
a7c0499
feat: Spring Security ๊ตฌ์กฐ๋กœ ๋ฆฌํŽ™ํ† ๋ง
epicarts Aug 16, 2022
2528f56
feat: AbstractAuthenticationFilter๋กœ ์ถ”์ƒํ™”ํ•˜์—ฌ ๋ถ„๋ฆฌํ•จ
epicarts Aug 16, 2022
44420ad
test: ์ฆ๊ฒจ์ฐพ๊ธฐ ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ
epicarts Aug 17, 2022
9797b80
feat: favorite / favorites ๋„๋ฉ”์ธ ๊ฐ์ฒด ์ž‘์„ฑ. ์—ฐ๊ด€๊ด€๊ณ„ ์ถ”๊ฐ€
epicarts Aug 17, 2022
3c347ac
feat: FavoriteRepository ์ถ”๊ฐ€
epicarts Aug 17, 2022
17c2292
feat/test: favorites ๋„๋ฉ”์ธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ
epicarts Aug 17, 2022
064a532
feat favorites ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ dto ์Šค์ผˆ๋ ˆํ†ค ์ฝ”๋“œ ์ž‘์„ฑ
epicarts Aug 17, 2022
86b18b3
refactor: FavoriteException ์ด IllegalArgumentException๋ฅผ ์ƒ์† ๋ฐ›๋„๋ก ์ˆ˜์ •ํ•จ
epicarts Aug 17, 2022
61ddba8
test: ์ž˜๋ชป๋œ ์š”์ฒญ์œผ๋กœ ์ธํ•œ ์‹คํŒจ ์ธ์ˆ˜ ์ผ€์ด์Šค ์ถ”๊ฐ€
epicarts Aug 17, 2022
7a1b8ea
feat: ์˜ˆ์™ธ ์กฐ๊ฑด ์—†๋Š” ๋‹จ์ˆœํ•œ ์ง€ํ•˜์ฒ  ๋…ธ์„  ์ฆ๊ฒจ์ฐพ๊ธฐ ์ƒ์„ฑ ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Aug 17, 2022
eb5f981
feat: "์ฆ๊ฒจ์ฐพ๊ธฐ_๊ฐ™์€์—ญ_์ถ”๊ฐ€_์‹คํŒจ" ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Aug 25, 2022
ae1d3df
feat: "๊ฐ™์€ ์ฆ๊ฒจ์ฐพ๊ธฐ๋ฅผ ์ถ”๊ฐ€ํ• ์‹œ์— ์˜ˆ์™ธ ๋ฐœ์ƒ" ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Aug 25, 2022
3061cc5
feat: "์ฆ๊ฒจ์ฐพ๊ธฐ ์‚ญ์ œ" ์„œ๋น„์Šค ๋ ˆ์ด์–ด ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Sep 4, 2022
3048959
feat: "์ฆ๊ฒจ์ฐพ๊ธฐ ์กฐํšŒ" ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Sep 4, 2022
fa2e694
feat: "๋กœ๊ทธ์ธ ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ" ๊ธฐ๋Šฅ ๊ตฌํ˜„
epicarts Sep 4, 2022
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package nextstep.auth.secured;

import java.util.Optional;
import nextstep.auth.authentication.execption.AuthenticationException;
import nextstep.auth.context.Authentication;
import nextstep.auth.context.SecurityContextHolder;
import org.aspectj.lang.JoinPoint;
Expand All @@ -23,7 +25,9 @@ public void checkAuthorities(JoinPoint joinPoint) {
Secured secured = method.getAnnotation(Secured.class);
List<String> values = Arrays.stream(secured.value()).collect(Collectors.toList());

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
.orElseThrow(() -> new AuthenticationException("์ธ์ฆ์— ์‹คํŒจํ•˜์˜€์Šต๋‹ˆ๋‹ค."));

authentication.getAuthorities().stream()
.filter(values::contains)
.findFirst()
Expand Down
57 changes: 57 additions & 0 deletions src/main/java/nextstep/member/application/FavoriteService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package nextstep.member.application;

import java.util.List;
import java.util.stream.Collectors;
import nextstep.member.application.dto.FavoriteRequest;
import nextstep.member.application.dto.FavoriteResponse;
import nextstep.member.domain.Favorite;
import nextstep.member.domain.FavoriteRepository;
import nextstep.member.domain.LoginMember;
import nextstep.member.domain.Member;
import nextstep.subway.applicaion.StationService;
import nextstep.subway.domain.Station;
import org.springframework.stereotype.Service;

@Service
public class FavoriteService {
private final MemberService memberService;
private final StationService stationService;
private final FavoriteRepository favoriteRepository;

public FavoriteService(MemberService memberService, StationService stationService,
FavoriteRepository favoriteRepository) {
this.memberService = memberService;
this.stationService = stationService;
this.favoriteRepository = favoriteRepository;
}

public Long saveFavorite(LoginMember loginMember, FavoriteRequest favoriteRequest) {
Member member = memberService.findByEmail(loginMember.getEmail());

Station source = stationService.findById(favoriteRequest.getSource());
Station target = stationService.findById(favoriteRequest.getTarget());

Favorite favorite = member.addFavorite(source, target);

return favoriteRepository.save(favorite).getId();
}

public List<FavoriteResponse> findFavoriteResponses(LoginMember loginMember) {
Member member = memberService.findByEmail(loginMember.getEmail());
List<Favorite> favorites = member.getFavorites();

return favorites.stream()
.map(it -> FavoriteResponse.of(it, it.getSource(), it.getTarget()))
.collect(Collectors.toList());
}

public void deleteFavorite(LoginMember loginMember, Long favoriteId) {
Favorite favorite = favoriteRepository.findById(favoriteId)
.orElseThrow(IllegalArgumentException::new);

Member member = memberService.findByEmail(loginMember.getEmail());
member.removeFavorite(favorite);

Choose a reason for hiding this comment

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

member.removeFavorite(favorite); ํ•ด์ฃผ์…จ๋Š”๋ฐ์š”! ํ™•์ธํ•ด๋ณด๋‹ˆ favorites ์—์„œ ์˜ต์…˜์ด orphanRemoval = true ๋กœ ๋˜์–ด ์žˆ๋Š”๊ฑธ ๋ดค์Šต๋‹ˆ๋‹ค! ๊ณ ์•„๊ฐ์ฒด๊ฐ€ ๋œ favorite ๋„ ๊ฐ™์ด delete ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ ๋˜๋Š”๋ฐ์š”!
55 line ์˜ favoriteRepository.delete(favorite); ๋ถ€๋ถ„์ด ํ•„์š”ํ• ๊นŒ์š”? ๐Ÿง


favoriteRepository.delete(favorite);
}
}
4 changes: 4 additions & 0 deletions src/main/java/nextstep/member/application/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ public void deleteMember(Long id) {
public void deleteMember(String email) {
memberRepository.deleteByEmail(email);
}

public Member findByEmail(String email) {
return memberRepository.findByEmail(email).orElseThrow(IllegalArgumentException::new);
}
}
22 changes: 22 additions & 0 deletions src/main/java/nextstep/member/application/dto/FavoriteRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package nextstep.member.application.dto;

public class FavoriteRequest {
private Long source;
private Long target;

public FavoriteRequest() {
}

public FavoriteRequest(Long source, Long target) {
this.source = source;
this.target = target;
}

public Long getSource() {
return source;
}

public Long getTarget() {
return target;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package nextstep.member.application.dto;

import nextstep.member.domain.Favorite;
import nextstep.subway.applicaion.dto.StationResponse;
import nextstep.subway.domain.Station;

public class FavoriteResponse {
private Long id;
private StationResponse source;
private StationResponse target;


public static FavoriteResponse of(Favorite favorite, Station source, Station target) {
return new FavoriteResponse(favorite.getId(), StationResponse.of(source), StationResponse.of(target));
}

public FavoriteResponse(Long id, StationResponse source, StationResponse target) {
this.id = id;
this.source = source;
this.target = target;
}

public FavoriteResponse() {
}

public Long getId() {
return id;
}

public StationResponse getSource() {
return source;
}

public StationResponse getTarget() {
return target;
}
}
50 changes: 50 additions & 0 deletions src/main/java/nextstep/member/domain/Favorite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package nextstep.member.domain;

import nextstep.member.domain.exception.FavoriteException;
import nextstep.subway.domain.Station;

import javax.persistence.*;

import static nextstep.member.domain.exception.FavoriteException.SAME_SOURCE_AND_TARGET;

@Entity
public class Favorite {

Choose a reason for hiding this comment

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

ํ˜„์žฌ member ์— ๋Œ€ํ•œ ์ •๋ณด๋Š” Favorite ์—์„œ ์•Œ ์ˆ˜ ์—†๋‹ค๊ณ  ๋ณด๋Š”๋ฐ์š”!
JPA ์—์„œ ์™ธ๋ž˜ํ‚ค๊ฐ€ ์—†์ด๋„ OneToMany ์™€ ManyToOne ๊ด€๊ณ„๊ฐ€ ๋งบ์–ด์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค!
์ง์ ‘์ฐธ์กฐ๋‚˜ ๊ฐ„์ ‘์ฐธ์กฐ๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ๋ ๊นŒ์š”? ๐Ÿง

ํ…Œ์ŠคํŠธ์—์„œ ๋‚˜์˜ค๋Š” ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•ด๋ณธ ๊ฒฐ๊ณผ
update
favorite
set
member_id=?
where
id=?

๋กœ ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๊ณ  ์žˆ๋Š”๋ฐ์š”. favorite ์—๋Š” memberId ๊ฐ€ ์—†์Œ์—๋„ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค!
ํ•œ๋ฒˆ ํ™•์ธํ•ด๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ๐Ÿ˜Š

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

@ManyToOne
@JoinColumn(name = "source_id")
private Station source;

@ManyToOne
@JoinColumn(name = "target_id")
private Station target;

protected Favorite() {
}

public Favorite(Station source, Station target) {
if (source.isSame(target)) {
throw new FavoriteException(SAME_SOURCE_AND_TARGET);
}
this.source = source;
this.target = target;
}

public Long getId() {
return id;
}

public Station getSource() {
return source;
}

public Station getTarget() {
return target;
}

public boolean hasDuplicateFavorite(Station target, Station source) {
return this.target == target && this.source == source;
}
}
8 changes: 8 additions & 0 deletions src/main/java/nextstep/member/domain/FavoriteRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.member.domain;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface FavoriteRepository extends JpaRepository<Favorite, Long> {
}
43 changes: 43 additions & 0 deletions src/main/java/nextstep/member/domain/Favorites.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package nextstep.member.domain;

import nextstep.member.domain.exception.FavoriteException;
import nextstep.subway.domain.Section;
import nextstep.subway.domain.Station;

import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;

import static nextstep.member.domain.exception.FavoriteException.DUPLICATION_FAVORITE;

@Embeddable
public class Favorites {
@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true)
@JoinColumn(name = "member_id")
private List<Favorite> favorites = new ArrayList<>();

public List<Favorite> getFavorites() {
return this.favorites;
}

public void add(Favorite favorite) {
checkDuplicationFavorite(favorite);
this.favorites.add(favorite);
}

public void delete(Favorite favorite) {
this.favorites.remove(favorite);
}

private void checkDuplicationFavorite(Favorite favorite) {
favorites.stream()
.filter(it -> it.hasDuplicateFavorite(favorite.getTarget(), favorite.getSource()))
.findFirst()
.ifPresent(it -> {
throw new FavoriteException(DUPLICATION_FAVORITE);
});
}
}
30 changes: 29 additions & 1 deletion src/main/java/nextstep/member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
package nextstep.member.domain;

import javax.persistence.*;
import java.util.List;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import nextstep.subway.domain.Station;

@Entity
public class Member {
Expand All @@ -11,6 +21,8 @@ public class Member {
private String email;
private String password;
private Integer age;
@Embedded
private Favorites favorites = new Favorites();
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(
name = "MEMBER_ROLE",
Expand Down Expand Up @@ -56,6 +68,22 @@ public List<String> getRoles() {
return roles;
}

public List<Favorite> getFavorites() {
return favorites.getFavorites();
}

public Favorite addFavorite(Station source, Station target) {
Favorite favorite = new Favorite(source, target);

Choose a reason for hiding this comment

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

member domain ์—์„œ Favorite ์ƒ์„ฑ ์—ญํ• ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๊ฒŒ ๋งž์„๊นŒ์š”? ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์‹œ๋‚˜์š”? ๐Ÿ˜Š


favorites.add(favorite);

return favorite;
}

public void removeFavorite(Favorite favorite) {
favorites.delete(favorite);
}

public void update(Member member) {
this.email = member.email;
this.password = member.password;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nextstep.member.domain.exception;

public class FavoriteException extends IllegalArgumentException {

Choose a reason for hiding this comment

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

Custom ์˜ˆ์™ธ ๐Ÿ‘

public static final String SAME_SOURCE_AND_TARGET = "์ฆ๊ฒจ์ฐพ๊ธฐ์˜ ์ถœ๋ฐœ์ ๊ณผ ๋„์ฐฉ์—ญ์ด ๊ฐ™์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.";
public static final String DUPLICATION_FAVORITE = "์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด์žˆ๋Š” ์ฆ๊ฒจ์ฐพ๊ธฐ์ž…๋‹ˆ๋‹ค.";

public FavoriteException(String message) {
super(message);
}
}
51 changes: 51 additions & 0 deletions src/main/java/nextstep/member/ui/FavoriteController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package nextstep.member.ui;

import java.net.URI;
import java.util.List;
import nextstep.auth.authorization.AuthenticationPrincipal;
import nextstep.auth.secured.Secured;
import nextstep.member.application.FavoriteService;
import nextstep.member.application.dto.FavoriteRequest;
import nextstep.member.application.dto.FavoriteResponse;
import nextstep.member.domain.LoginMember;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/favorites")
public class FavoriteController {
private final FavoriteService favoriteService;

public FavoriteController(FavoriteService favoriteService) {
this.favoriteService = favoriteService;
}

@PostMapping
@Secured("ROLE_MEMBER")
public ResponseEntity<Void> createFavorite(@AuthenticationPrincipal LoginMember loginMember,
@RequestBody FavoriteRequest favoriteRequest) {
Long favoriteId = favoriteService.saveFavorite(loginMember, favoriteRequest);
return ResponseEntity.created(URI.create("/favorites/" + favoriteId)).build();
}

@GetMapping
@Secured("ROLE_MEMBER")
public ResponseEntity<List<FavoriteResponse>> showFavorites(@AuthenticationPrincipal LoginMember loginMember) {
List<FavoriteResponse> responses = favoriteService.findFavoriteResponses(loginMember);
return ResponseEntity.ok().body(responses);
}

@DeleteMapping("/{favoriteId}")
@Secured("ROLE_MEMBER")
public ResponseEntity<Void> deleteFavorite(@AuthenticationPrincipal LoginMember loginMember,
@PathVariable Long favoriteId) {
favoriteService.deleteFavorite(loginMember, favoriteId);
return ResponseEntity.noContent().build();
}
}
6 changes: 6 additions & 0 deletions src/main/java/nextstep/subway/domain/Station.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Objects;

@Entity
public class Station {
Expand All @@ -26,4 +27,9 @@ public Long getId() {
public String getName() {
return name;
}

public boolean isSame(Station target) {
return Objects.equals(this.id, target.getId()) &&
Objects.equals(this.name, target.getName());
}
}
Loading