-
Notifications
You must be signed in to change notification settings - Fork 48
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
[터틀] API 테스트/문서자동화 미션 제출합니다. #11
Conversation
- Spring REST Docs 의존성 추가 - MemberControllerTest 내용 복사/붙여넣기 - AuthAcceptanceTest 임시 비활성화 - LineControllerTest를 SpringBootTest로 변경 - MemberDocumentation 주석 해제
- Utf8EncodingFilter 작성 - Session / Basic / Bearer AuthInterceptor 작성 - MockMvc 사용하여 AuthAcceptanceTest 작성
- JWT 토큰 활용하여 회원정보 CRUD시 인증절차 거치도록 구현 - 인증이 필요한 컨트롤러들의 패턴을 BearerAuthInterceptor에 추가 - 인증정보(LoginMember)가 없을 경우 200 상태로 빈 멤버를 응답 - 인증정보가 틀릴 경우 401 응답 - 사용하지 않는 다른 인증 및 관련 테스트 삭제 - 서버 프로젝트 스코프 컨벤션 리팩토링 진행
- RestAssured 의존성 삭제
- Login.js에서 비밀번호 input keypress event에 onLogin 추가
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.
안녕하세요 깔끔하게 구현 잘하셨어요 💯
피드백 남겼으니 확인해주세요!
@@ -7,6 +7,12 @@ | |||
private String name; | |||
private String password; | |||
|
|||
public MemberRequest(final String email, final String name, final String password) { |
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.
전체 테스트 실행시 MemberReq/Res 에 default constructor가 없어 테스트가 실패하네요 ㅜ
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.
제 로컬환경에선 모두 통과하는데 이상하네요😱
MemberRequest와 MemberResponse에 기본생성자를 추가했습니다!
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@DeleteMapping("/members/{id}") | ||
public ResponseEntity<MemberResponse> deleteMember(@PathVariable Long id) { | ||
memberService.deleteMember(id); | ||
public ResponseEntity<MemberResponse> deleteMember(@LoginMember(type = Type.ID) Member member) { |
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.
Entity를 같이 사용하는 대신 요청을 위한 Member 클래스를 만들어주세요!
} | ||
|
||
public String createToken(LoginRequest param) { | ||
Member member = memberRepository.findByEmail(param.getEmail()).orElseThrow(RuntimeException::new); | ||
Member member = memberRepository.findByEmail(param.getEmail()) |
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.
아래 메서드와 중복이네요! findMemberByEmail
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.
findByEmail
을 사용하는 부분을 모두 findMemberByEmail
을 사용하도록 변경했습니다!
.orElseThrow(MemberNotFoundException::new); | ||
} | ||
|
||
public void addFavorite(final Member member, final FavoriteRequest request) { |
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.
favorite 관련 서비스를 추가하면 어떨까요?
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.
그게 더 직관적이여서 처음엔 그렇게 하는 것을 고려했습니다.
하지만 다음과 같은 이유들로 만들지 않기로 했습니다.
- FavoriteService가 생겨도 MemberService와 갖고 있는 필드가 같다.
- Favorite에 대한 기능이 더 커질 경우 분리한다.
- Favorite의 변경도 결국 Member의 변경으로 이어진다.
- Member가 Favorites를 갖고 있기도 하고, Spring Data JDBC를 통해 Favorites의 CUD를 할 때 내부적으로 Member를 변경한다.
혹시 어떤 이유인지 자세히 알 수 있을까요? 🙂
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.
Member 기반으로 이루어지는 모든 행위들이 Member 변경이라는 이유하나만으로 모두 MemberService에 있다면 말씀하신대로 MemberService의역할이 너무 커질 것 같아요!!!
단순히 가지고 있는 필드가 같기 때문의 이유는 좋은 이유는 아니라고 생각합니다.
즐겨찾기와 관련있는 기능들은 담당하기 위한 Service를 분리해도 좋을 것 같아 남겼으며 위의 이유로 MemberService에 두고 싶다면 두어도 무관합니다.
verify(memberRepository).save(any()); | ||
} | ||
|
||
@DisplayName("토큰 생성 테스트") |
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.
생성에 실패하는 경우에 대한 테스트도 추가하면 어떨까요?
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.
토큰 생성에 실패하는 테스트를 추가했습니다 :)
@RestController | ||
public class LoginMemberController { | ||
private MemberService memberService; | ||
private final MemberService memberService; |
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.
해당 클래스에 대한 테스트는 작성하지 않아도 괜찮은걸까요?
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.
LoginMemberController에 대한 테스트도 추가했습니다 :)
- 클래스 수준의 RequestMapping 추가 - 메소드 이름 변경 - dependency-management를 통해 버전관리하도록 변경
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.
안녕하세요 피드백 반영 & 구현 모두 잘하셨어요 👍
간단한 피드백 추가로 남겼으니 확인해주세요 :)
.orElseThrow(MemberNotFoundException::new); | ||
} | ||
|
||
public void addFavorite(final Member member, final FavoriteRequest request) { |
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.
Member 기반으로 이루어지는 모든 행위들이 Member 변경이라는 이유하나만으로 모두 MemberService에 있다면 말씀하신대로 MemberService의역할이 너무 커질 것 같아요!!!
단순히 가지고 있는 필드가 같기 때문의 이유는 좋은 이유는 아니라고 생각합니다.
즐겨찾기와 관련있는 기능들은 담당하기 위한 Service를 분리해도 좋을 것 같아 남겼으며 위의 이유로 MemberService에 두고 싶다면 두어도 무관합니다.
return this.password.equals(password); | ||
} | ||
|
||
public boolean isAuthenticated(Long id) { |
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.
사용하지 않는 메서드인것 같은데 확인하고 삭제 하면 어떨까요?
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.
앗 불필요한 메소드를 제거하겠습니다😅
.orElseThrow(NoSuchElementException::new); | ||
Station target = stationRepository.findById(request.getTarget()) | ||
.orElseThrow(NoSuchElementException::new); | ||
member.addFavorite(new Favorite(source.getId(), target.getId())); |
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.
반영하지 않아도 됩니다!
다른 방법으로는 쿼리를 두번 날리는 대신, ids로 조회하고 그 결과를 source와 target에 mapping할수도 있을 것 같아요~
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.
ids로 조회해서 데이터베이스에 접근하는 횟수를 1회로 줄였습니다.
ids로 받아온 List<Stations>
의 역할이 생겨서 Stations로 만들었습니다.
Stations가 Favorite을 만들어서 리턴하는것은 범용적인 List<Station>
이 하는 일이 아닌, 요소가 2개 있는 List<Station>
의 역할이라고 생각해서 Favorite을 만드는 일은 꺼내서 하기로 했습니다.
다음과 같이 Station들의 아이디 리스트인 List<Long>
만 반환하는 역할만 주고 Favorite은 꺼내서 만드는데 get(0)
, get(1)
이 부분이 조금 불편합니다.
List<Long> ids = stations.getStationIds();
favorites.addFavorite(new Favorite(ids.get(0), ids.get(1)));
어떤식으로 처리하는 것이 좋을까요?🧐
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { | ||
String authorizationHeaderValue = authExtractor.extract(request, BEARER); | ||
if (jwtTokenProvider.validateToken(authorizationHeaderValue)) { | ||
request.setAttribute("loginMemberEmail", jwtTokenProvider.getSubject(authorizationHeaderValue)); |
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.
"loginMemberEmail"는 상수를 사용하면 어떨까요?
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.
loginMemeberEmail을 상수로 변경하고 ArgumentResolver에서도 해당 상수를 import해서 사용하도록 변경했습니다 :)
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
@RestControllerAdvice | ||
public class GlobalExceptionHandler { |
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.
ExceptionHandler에 정의되지 않은 에러가 발생하는 경우에 대한 처리도 추가하면 어떨까요?
(default)
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.
Exception.class를 처리하는 메소드를 추가했습니다! 500에러를 보내주는 것이 적절하다고 생각했는데 어떻게 생각하시나요?
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.
안녕하세요, 구현&피드백 반영 모두 잘하셨어요 👍
간단한 코멘트 남겼으니 확인해주세요,
머지할게요 :)
|
||
public void addFavorite(final Member member, final FavoriteRequest request) { | ||
List<Station> stations = stationRepository.findAllById( | ||
Arrays.asList(request.getSource(), request.getTarget())); |
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.
findAllById 내부 설명을 보면
Note that the order of elements in the result is not guaranteed.
와 같이 적혀있어요. 항상 순서가 source, target순으로 반환되지 않을 수 있을 것 같아요.
get(0), get(1) 대신, stations로 부터 source와 target 각각에 해당하는 Station을 먼저 찾아 mapping 하는 부분이 필요할것 같아요!
감사합니다 :)