-
Notifications
You must be signed in to change notification settings - Fork 6
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
[BE] Feat/#378 Admin API 구현 #405
Conversation
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.
바쁜 중에 수고 많으셨습니다 !! 🕹
좋은 점들 코멘트도 남길 게 많았지만
일정 상 쉽게 리뷰 확인 하실 수 있도록 질문/제안 위주로만 코멘트 남겼어요.
질문은 Q.
, 제안은 P2
, P3
남겼고, P2
부분만 변경 요청입니다!
그리고 쥬니 설명 듣고 나니, 기존의 회원 조회 API에서는 불필요한 정보들을 빼야할 것 같네요!!
관리자 API 잘 고민해주셔서 감사함당
이 부분은 다 같이 인지하고 넘어갔으면 해서 코멘트에 남깁니다 ~
GET /members/{memberId}
{
"id" : 1,
"nickName" : "member",
"email" : "member@naver.com", // 다른 회원도 이 회원의 이메일을 알게 됨. 빼야할듯
"imageUrl" : "https://map-befine-official.github.io/favicon.png",
"updatedAt" : "2023-09-13T15:11:47.683507704"
}
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@DeleteMapping("/pins/images/{imageId}") |
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.
P2.
deleteTopicImage와 같이 pins/{imageId}/images
로 통일하는 것은 어떨까요 ?
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.
pins/{imageId}/images
라는 URL은 조금 부자연스럽게 느껴지는 것 같아요.
imageId는 1:1로 매핑되는 값인데, 뒤에 /images
가 추가된다는게 어색하게 느껴지네요 ㅠ_ㅠ
차라리 pinImages/{imageId}
는 어떨까요 !?
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.
전 이 부분은 쥬니가 한 게 더 자연스럽다구 생각합니다.
Topics/ 구체적인 Topic/ 해당 토픽의 이미지
핀 / 핀의 이미지들 / 구체적인 이미지
이런 흐름이라고 생각해서요!
Pins 다음에 PinId가 들어가면 의미가 더 명확해지긴 하겠지만
불필요한 정보를 받을 필요도 없다고 생각하기 때문에 지금도 괜찮은 것 같네요
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.
아 이부분은 제가 생각을 잘못한 것 같아요!!! 쥬니가 하신대로 진행하는 것 동의합니다 ~
return; | ||
} | ||
|
||
throw new MemberForbiddenException(MemberErrorCode.FORBIDDEN_MEMBER_STATUS, member.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.
회원 상태 확인까지 추가해주셨군요!! 😀 감사합니다
P2.
-
특정 자원에 접근하는 것이 아니라 로그인을 시도하는 것이기 때문에 NOT_FOUND 보다 UNAUTHORIZED 로 하는 건 어떨까요 ?
-
탈퇴 혹은 차단된 회원입니다.
이 에러 메시지를 주는 군요!
에러 메시지에서 로그인을 시도한 사용자에게 해당 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.
특정 자원에 접근하는 것이 아니라 로그인을 시도하는 것이기 때문에 NOT_FOUND 보다 UNAUTHORIZED 로 하는 건 어떨까요 ?
NOT_FOUND ????? FORBIDDEN 이야기 하신거죠 ㅎㅎ?
해당 사항 동의합니당 ! 반영토록 하겠습니다.
탈퇴 혹은 차단된 회원입니다. 이 에러 메시지를 주는 군요!
에러 메시지에서 로그인을 시도한 사용자에게 해당 id가 탈퇴/차단된 회원이라는 정보를 노출하는 대신
에러 메시지는 다른 로그인 실패 시 메시지와 똑같이 하고, 저희끼리 에러 코드로만 식별하는 건 어떨까요?
에러 코드를 통해 식별한다는 것은 프론트도 마찬가지일까요 ?
차단당한 사용자가 로그인할 때, 차단되었다는 사실을 알려주는 것이 자연스럽다고 생각했어요.
프론트측에서 에러 코드로 다르게 처리해준다면, 도의 의견에 동의합니다 !!
(당연히 해줄거니까) 반영하도록 하겠습니다 ㅎㅎ
Auth 관련해서 ERROR_CODE가 너무 분산되어 있어서, 클래스 auth 패키지 내부에 exception 패키지 추가했는데 확인부탁드려용
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.
@@ -52,7 +52,7 @@ private void validateReadAuth(AuthMember member, Topic topic) { | |||
} | |||
|
|||
public List<PinResponse> findAllPinsByMemberId(AuthMember authMember, Long memberId) { | |||
return pinRepository.findByCreatorId(memberId) | |||
return pinRepository.findAllByCreatorId(memberId) |
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.
👍👍👍
NORMAL("STATUS_NORMAL", "정상 사용자"), | ||
DELETE("STATAUS_DELETE", "탈퇴한 사용자"), | ||
BLOCKED("STATUS_BLOCKED", "차단된 사용자"); |
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.
👍👍
Q.
- 탈퇴 : 사용자가 자의적으로 탈퇴해서 삭제 상태가 되는 경우
- 차단 : 관리자가 삭제 상태로 만드는 경우
로 이해하면 될까요 ?
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.
맞습니다 !!!!!!!!!!!!
Topic deletedTopic = topicRepository.findById(topic.getId()).get(); | ||
Pin deletedPin = pinRepository.findById(pin.getId()).get(); | ||
PinImage deletedPinImage = pinImageRepository.findById(pinImage.getId()).get(); |
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.
지금은 조회 메서드가 soft delete된 것까지 조회하는 경우 / soft delete된 것 빼고 조회하는 경우(여야 하는데 아직 반영안됨)
에 모두 쓰이고 있는데,
이제는 두 개를 나눠야 할 때가 온 거 같군용...
(이번 PR에 대한 변경 요청은 아닙니다)
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.
맞아요.......해야되요 ㅠㅠㅠㅠㅠㅠㅠㅠ
} | ||
|
||
@GetMapping("/members") | ||
public ResponseEntity<List<AdminMemberResponse>> findAllMembers(AuthMember authMember) { |
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.
P3.
AdminController에서도 AuthMember를 받아야 할까요?
AdminAuthInterceptor에서 해당 사용자가 관리자임을 확인하는 것 같은데,
이미 관리자가 호출한 게 확인이 되었다면 AuthMember를 아예 안받아도 되지는 않나요?!
서비스에서 이중검증 해주기 위함일까용?
아니면 Admin 객체를 받아도 되지 않을까요?
쥬니가 더 오래 잘 고민해주셨을 것이라 혹시 제가 생각 못한 이유가 있는지 궁금해요!!
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.
Service에서 이중으로 검증하기 위함이었어요 !
사실 Admin 객체를 받는 것이 조금 더 자연스러울 것 같긴 하네요.
아니면, ArgumentResolver 사용해서 Member 객체를 받아도 될 것 같아요.
어떤 방법이든 Service에서 검증해줄 수는 있으니까요.
어떤 방법이 좋을까요 ?
도이뿐만 아니라, 준팍과 매튜도 동일한 내용에 대해서 코멘트 남겨주셔서 의견 덧붙입니다.
서비스가 핵심 비즈니스 로직을 담고 있는 만큼, 서비스에서의 검증은 필수적이라고 생각했어요.
Admin 역할을 가진 Member만 해당 메서드를 정상적으로 처리해야 하는데, Interceptor를 믿고 별도의 검증 로직이 없다는 건 너무 불안할 것 같아요. (물론 스프링 프레임워크를 사용하는 만큼, 이 부분은 믿고 가도 될 것 같기도 해요.)
또한, 이 상황은 Service가 Controller(Interceptor) 계층에 의존적인 상황인 것 같아요.
혹여나, 다른 Controller에서 해당 Service를 호출하게 된다면, 권한과 상관없이 관리자 기능을 수행할수도 있지 않을까요 ?
관리 포인트가 늘어나게 된다는 점에서 동일한 리뷰를 주신 것 같은데요.
매튜의 의견은 Service가 꼭 검증을 수행해야 한다면, AdminAuthInterceptor를 삭제해도 되지 않을까 ?
였어요.
저는 Interceptor가 권한 검증
의 기능도 있겠지만, 잘못된 요청이 어플리케이션 자체로 넘어오지 않도록 하기 위함도 있다고 생각해요. 요청 자체를 거부해버리는 역할을 수행하는거죠.
중복 코드를 없애기 위해서, AOP를 통해 Admin관련 서비스 메서드들이 호출되기 이전에 매번 검증 메서드를 호출하도록 하는 방법이 있을 것 같아요. 다만, Admin만을 위해 AOP를 도입하는 것이 맞을까 라는 의문이 들기도 하네요.
내일 오전 강의 시작 전에(혹은 오후에) 잠깐 모여서 이야기 나누어봤으면 좋겠습니다.
Member member = findMember(memberId); | ||
|
||
return member.isAdmin(); | ||
} | ||
|
||
private Member findMember(Long memberId) { | ||
return memberRepository.findById(memberId) | ||
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId)); | ||
} |
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.
P2.
findMember에서 예외를 던져서 404를 주는 대신 false를 반환하고,
AdminAuthInterceptor의 validateAdmin 메서드에서 던지는 예외 하나로 통일하는 게 어떨까요?
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.
차라리 validateRole 자체를 AuthService에 두는 건 어떨가요?
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 boolean isAdmin(Long memberId) {
if (Objects.isNull(memberId)) {
return false;
}
Optional<Member> member = memberRepository.findById(memberId);
return member.map(Member::isAdmin)
.orElse(false);
}
import org.springframework.web.servlet.HandlerInterceptor; | ||
|
||
@Component | ||
public class AdminAuthInterceptor implements HandlerInterceptor { |
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.
P4.
Interceptor 간 같은 로직의 반복은 추후 수정해보는 걸로 하죠!!
private Member findMemberById(Long id) { | ||
return memberRepository.findById(id) | ||
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, 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.
P2.
이 코드 흐름에서 Member를 찾지 못한다면 400번대(사용자 잘못의 오류)보다는 코드나 데이터가 잘못되어서 발생하는 것 아닐까요 ??
Interceptor에서 이미 검증을 하니까요..!!
그래서 다른 서비스 클래스들에서는 같은 상황에 대해서 아래와 같이 internal server error로 처리되는 예외를 던지고 있습니당
(동일한 흐름의 코드들 모두 해당하는 코멘트입니다!)
private Member findMemberById(Long id) {
return memberRepository.findById(id)
.orElseThrow(() -> new NoSuchElementException("findCreatorByAuthMember; member not found; id=" + 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.
이전 PR들에서 해당 부분을 전혀 인지 못했었던 것 같네요.
우선, 코드 통일성을 위해 해당 사항은 반영하도록 하겠습니다.
다만, Interceptor에서 검증을 수행하기 때문에, Server ERROR로 본다는 것은 Service 계층이 Interceptor에 의존적인 것 같다는 생각이 듭니다.
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.
Service 계층이 Interceptor에 의존적인 것 같다는 생각이 듭니다.
이 부분 확실히 논의해야 할 부분인 것 같네요! 내일 얘기해보아요~
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 admin = findMemberById(authMember.getMemberId()); | ||
validateAdminPermission(admin); |
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.
P4. AuthMember 안에 isRole(Role role)
메서드를 두는 건 어떨까요?
Interceptor에서 이미 데이터를 가져오고 있는데 검증을 위해서 한번 더 가야하는 것 같아서용 ㅋㅋ
만약 해당 메서드 도입한다면 여기선 authMember.isRole(Role.ADMIN)
로 변경 가능할 것 같네요!
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.
위 코멘트와 관련이 있는 것 같아요.
다른 분들의 의견처럼 Interceptor에서 수행하기 때문에, Service에서 검증이 필요 없다고 하시면 기존 코드 그대로 가져가도 될 것 같아요.
만약, 지금처럼 이중 검증을 수행한다면 해당 사항 반영하는 것이 더 좋을 것 같긴 하네요 ㅎㅎ..
내일 이야기 나누어보고 결정하도록 하겠습니다.
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 admin = findMemberById(authMember.getMemberId()); | ||
validateAdminPermission(admin); | ||
|
||
List<Member> members = memberRepository.findAllByMemberInfoRole(Role.USER); |
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.
P5. 전체 사용자가 아닌 USER만 찾는 것 같은데 ADMIN은 어떤 이유에서 제외하셨는지 궁금합니당ㅋㅋ
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.
ADMIN이 ADMIN을 삭제할까봐...?
관리자들 사이에서의 개인정보는 지켜주고 싶어서 ..?
별 다른 이유는 없습니다 ㅎㅎ
그냥 관리자는 일반 유저들을 관리한다고 생각해서 위와 같이 구현했던 것 같아요.
모든 회원을 조회하도록 수정하는 것이 자연스럽다면, blockMember 메서드에서 삭제하려는 Member가 Admin인지 검증하는 로직이 필요하겠네요.
어떻게 생각하시나용
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@DeleteMapping("/pins/images/{imageId}") |
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.
전 이 부분은 쥬니가 한 게 더 자연스럽다구 생각합니다.
Topics/ 구체적인 Topic/ 해당 토픽의 이미지
핀 / 핀의 이미지들 / 구체적인 이미지
이런 흐름이라고 생각해서요!
Pins 다음에 PinId가 들어가면 의미가 더 명확해지긴 하겠지만
불필요한 정보를 받을 필요도 없다고 생각하기 때문에 지금도 괜찮은 것 같네요
Member member = findMember(memberId); | ||
|
||
return member.isAdmin(); | ||
} | ||
|
||
private Member findMember(Long memberId) { | ||
return memberRepository.findById(memberId) | ||
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, memberId)); | ||
} |
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.
차라리 validateRole 자체를 AuthService에 두는 건 어떨가요?
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.
쥬니 너무 고생했어요~~~
다른 분들이 이미 의견 많이 남겨주셔서 저는 대부분 궁금한 사항들로 남겼습니다.
고생하셨어요 알러뷰
LocalDateTime updatedAt | ||
) { | ||
|
||
// TODO: 2023/09/12 topics, pins 모두 member를 통해 얻어올 수 있다. Service에서 꺼내서 넘겨줄 것인가 ? 아니면 DTO에서 꺼내올 것인가 ? |
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.
제가 최근들어 N + 1 문제에 대해서 알게되서 그런지, Member 에서 꺼내오는 것이 추후 Join 을 통해서 성능 개선하기에 쉬울 것 같아요.
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.
근데 옆에서 석진씨가 OneToMany 컬럼이 2개 이상일 때 Fetch Join 하면 에러날 수도 있다고 하네요! 다음에 같이 한번 실험해봐요 홍홍ㅎ옿옿옿옿오
private void validateAdminPermission(Member member) { | ||
if (member.isAdmin()) { | ||
return; | ||
} | ||
|
||
throw new PermissionForbiddenException(PermissionErrorCode.PERMISSION_FORBIDDEN_BY_NOT_ADMIN); | ||
} | ||
|
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.
다른 분들이 이미 코멘트 달았나 모르긴 하겠지만, 현재 코드에서 Admin 관련 API 에 요청이 오는 경우 AdminAuthInterceptor 가 동작하고 거기에서 validateAdmin 을 진행해주는 걸로 알고 있습니다!
그렇기 때문에 실제 Service 로직에서는 admin 인지 검증하는 것이 불필요하지 않을까용?
제가 잘못생각한 부분이 있는지 쥬니짱이 맘껏 지적해주세용.
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.
어쩔 tv!
topicRepository.deleteAllByMemberId(memberId); | ||
pinRepository.deleteAllByMemberId(memberId); | ||
pinImageRepository.deleteAllByPinIds(pinIds); | ||
} |
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.
S3 연동되면 또 생각해야 하는게 많아지려나요.. 후후..
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.
그리고 Permission 과 Bookmark, Atlas 등등은 안지워도 될까요?
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.
사실 Permission, Bookmark 해야되는거 알고 있는데, 귀찮아서요 ...
너무 많은 Repository를 알고 있어요......
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.
그쵸 사실 굉장히 귀찮은 것 같아요.. 이 부분은 추후에 의논을 통해서 해결해볼까요?
private void validateAdminPermission(Member member) { | ||
if (member.isAdmin()) { | ||
return; | ||
} | ||
|
||
throw new PermissionForbiddenException(PERMISSION_FORBIDDEN_BY_NOT_ADMIN); | ||
} | ||
|
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.
요 부분도 AdminQueryService 에서 달았단 코멘트와 의견 동일합니다!
@@ -37,6 +37,7 @@ private Member findMemberById(Long id) { | |||
.orElseThrow(() -> new MemberNotFoundException(MemberErrorCode.MEMBER_NOT_FOUND, id)); | |||
} | |||
|
|||
// TODO: 2023/09/13 차단된 or 탈퇴한 사용자 필터링 필요 |
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.
다음 스프린트에 뭔가 필터링 작업을 한꺼번에 진행해야 할 것 같네요.
assertThatThrownBy(() -> adminCommandService.deletePinImage(userAuthMember, pinImageId)) | ||
.isInstanceOf(PermissionForbiddenException.class); | ||
} | ||
|
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.
테스트 꼼꼼하게 작성해주셨네요 정말 고생하셨습니다
"매튜의 산스장", | ||
"지번 주소", | ||
"매튜가 사랑하는 산스장", | ||
"매튜", | ||
37, |
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.
#405 (comment)
에 대한 논의는, 일단 변경 없이 가기로 이야기되었고 API 제공 후 고민할 수 있는 내용이라고 생각해서
approve 합니다!
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.
고생했어요 둥글레차
* feat: 전체 회원 조회 기능 구현 * feat: 회원 삭제(탈퇴) 기능 구현 * feat: 회원 삭제(탈퇴)시 Pin/Topic Soft-deleting 구현 * refactor: Admin DTO 분리 * feat: Member 상세 정보 조회 기능 구현 * feat: Topic 삭제 및 이미지 삭제 기능 구현 * feat: Pin 삭제 및 이미지 삭제 기능 구현 * feat: Admin API 구현 * refactor: Member 상태(차단, 탈퇴 등) 필드에 따른 로그인 로직 수정 * refactor: @SqlDelete 삭제 및 JPQL 대체 * feat: AdminInterceptor 구현 * test: Repository soft-deleting 테스트 구현 * test: AdminQueryService 테스트 구현 * test: AdminCommandService 테스트 구현 * test: AdminController Restdocs 테스트 구현 * test: AdminInterceptor Mocking * test: 통합 테스트 구현 * refactor: 오탈자 수정 * refactor: Auth 관련 예외 클래스 추가 * refactor: 불필요한 메서드 제거 * refactor: findMemberById 예외 수정 * test: GithubActions 실패 테스트 수정 * refactor: isAdmin() 메서드 추가 * refactor: 회원 삭제(탈퇴)시, 추가 정보(즐겨찾기 등) 삭제
This reverts commit 4722faa.
* [BE] Fix/#366 테스트 수행 시 로그 패턴 깨지는 오류 해결 (#367) * fix: 테스트 로그 패턴 설정 오류 수정 - 잘못된 로그 패턴 설정으로 인한 'LOG_PATTERN IS_UNDEFINED' 메시지 출력 오류 수정 * chore: 프론트엔드, 백엔드 develop 분리에 따른 워크플로우 수정 * fix: 테스트 로그 설정 파일명 변경 - springProperty 지원을 위해 파일명 변경 * feat: 로그 내용 및 설정 보완(색상 적용) - 로그에서 로거, 레벨, PID 확인 가능하도록 내용 보완 - 테스트 로그의 경우 프로젝트 패키지에 해당하는 로그만 DEBUG 레벨로 설정 - 콘솔 로그 색상 적용 * feat: 로그 내용 보완 - 스레드 출력하도록 수정 * refactor: DataBaseCleanup JdbcTemplate 적용 (#371) * [BE] Refactor/#376 로깅 환경 개선을 위한 설정 파일 리팩터링 (#377) * refactor: 로깅 전략 보완에 따른 설정 파일 수정 - 운영 환경 별 로그 종류(콘솔, 파일), 레벨 변경 (PR 첨부 설명 참조) - logback-spring.xml 에서 Appender 분리 - application-*.xml 에서 로그 패턴 값 삭제 * chore: 에러 로그 슬랙 알림을 위한 의존성 추가, 관련 주석 작성 * remove: 테스트 설정 파일에 불필요한 로그 설정 삭제 * style: 불필요한 빈 줄 삭제 * chore: 콘솔 파일 로그 설정 삭제로 인한 불필요한 설정 삭제 * chore: 로그 파일 롤링 용량 설정 모든 레벨 통일 * [BE] Chore/#372 submodule 적용 (#375) * chore: submodule 적용 Co-authored-by: jaeyeon kim <jakind@naver.com> * chore: workflow access token 적용 Co-authored-by: jaeyeon kim <jakind@naver.com> --------- Co-authored-by: jaeyeon kim <jakind@naver.com> Co-authored-by: yoondgu <doyou2ing@naver.com> * feat: 운영 서버 500 에러 슬랙 알림 적용, 로컬 환경변수 서브모듈에 저장 (#379) * hotfix: yml 문법으로 인한 오류 수정 (#381) * fix: 워크플로우 서브모듈 문제 해결 (#384) * chore: 워크플로우 서브모듈 문제 해결 확인을 위한 push (#385) * Feat/#386 image (#391) * chore : S3 의존성 추가 * feat : Amazon S3 Component 추가 * feat : S3 에 업로드 될 Image 의 이름을 설정해주는 ImageName 과 UploadFile 추가 * feat : S3 에 업로드 될 Image 의 이름을 설정해주는 ImageNae 과 UploadFile 추가 * feat : 파일을 업로드하고 해당하는 URL 을 반환하는 Service 구현 * chore : s3 환경 설정 추가 * feat : String image -> MultipartFile image 로 변경 * feat : @RequestPart 적용 및 S3 upload 로직 추가 * chore : Test 시 Profile 설정 * test : 테스트 시 S3 에 접근하지 않도록 하기 위해 Profile 별로 S3Service Bean 구분 * test : 추가된 Image 저장 기능에 맞춰 일부 Test 수정 * chore : S3, CloudFront 환경설정 적용 * test : RestDocs 수정중 * refactor : S3 Bean 추가 (#396) * [BE] Refactor/#390 지도 및 핀 상세 조회 API에 수정 권한 여부 필드 추가 (#392) * feat: 토픽 상세 조회 내용에 수정 권한 추가 * refactor: 토픽 상세 조회 DTO 정적 팩터리 메서드명 수정 * feat: 핀 상세 조회 내용에 수정 권한 추가 * fix: 토픽 RestDocs 깨지는 문제 수정, API 목차 순서 조정 - 목록 조회 API 기준으로 순서 조정, API 네이밍 보완 * refactor: 토픽, 핀 수정 권한 여부 필드명 직관적으로 수정 - hasUpdatePermission -> canUpdate * refactor: Guest 전용 토픽 상세조회 DTO 정적 팩터리 메서드 정의 * refactor: 메서드 순서 정리, Guest 전용 토픽 조회 DTO 정적 팩터리 메서드 정의 * refactor: Guest 전용 토픽 List 조회 DTO 정적 팩터리 메서드 정의 * refactor: Info, Debug 레벨 커스텀 로그만 출력하도록 변경 (#397) * feat : pin 생성시 image upload 기능 추가 (#401) * [BE] Feat/#378 Admin API 구현 (#405) * feat: 전체 회원 조회 기능 구현 * feat: 회원 삭제(탈퇴) 기능 구현 * feat: 회원 삭제(탈퇴)시 Pin/Topic Soft-deleting 구현 * refactor: Admin DTO 분리 * feat: Member 상세 정보 조회 기능 구현 * feat: Topic 삭제 및 이미지 삭제 기능 구현 * feat: Pin 삭제 및 이미지 삭제 기능 구현 * feat: Admin API 구현 * refactor: Member 상태(차단, 탈퇴 등) 필드에 따른 로그인 로직 수정 * refactor: @SqlDelete 삭제 및 JPQL 대체 * feat: AdminInterceptor 구현 * test: Repository soft-deleting 테스트 구현 * test: AdminQueryService 테스트 구현 * test: AdminCommandService 테스트 구현 * test: AdminController Restdocs 테스트 구현 * test: AdminInterceptor Mocking * test: 통합 테스트 구현 * refactor: 오탈자 수정 * refactor: Auth 관련 예외 클래스 추가 * refactor: 불필요한 메서드 제거 * refactor: findMemberById 예외 수정 * test: GithubActions 실패 테스트 수정 * refactor: isAdmin() 메서드 추가 * refactor: 회원 삭제(탈퇴)시, 추가 정보(즐겨찾기 등) 삭제 * Revert "[BE] Feat/#378 Admin API 구현 (#405)" (#414) This reverts commit 4722faa. * [BE] Feat/#378 Admin API 구현 (#415) * feat: 전체 회원 조회 기능 구현 * feat: 회원 삭제(탈퇴) 기능 구현 * feat: 회원 삭제(탈퇴)시 Pin/Topic Soft-deleting 구현 * refactor: Admin DTO 분리 * feat: Member 상세 정보 조회 기능 구현 * feat: Topic 삭제 및 이미지 삭제 기능 구현 * feat: Pin 삭제 및 이미지 삭제 기능 구현 * feat: Admin API 구현 * refactor: Member 상태(차단, 탈퇴 등) 필드에 따른 로그인 로직 수정 * refactor: @SqlDelete 삭제 및 JPQL 대체 * feat: AdminInterceptor 구현 * test: Repository soft-deleting 테스트 구현 * test: AdminQueryService 테스트 구현 * test: AdminCommandService 테스트 구현 * test: AdminController Restdocs 테스트 구현 * test: AdminInterceptor Mocking * test: 통합 테스트 구현 * refactor: 오탈자 수정 * refactor: Auth 관련 예외 클래스 추가 * refactor: 불필요한 메서드 제거 * refactor: findMemberById 예외 수정 * test: GithubActions 실패 테스트 수정 * refactor: isAdmin() 메서드 추가 * refactor: 회원 삭제(탈퇴)시, 추가 정보(즐겨찾기 등) 삭제 * refactor: Member status 기본값 설정 * remove: Member status 기본값 설정 삭제 * [BE] Feature/#399 내 정보(회원 닉네임) 수정 API 구현 (#408) * refactor: 사용하지 않는 MemberRepository 메서드 삭제 * refactor: 회원 업데이트 부분 변경으로 시그니처 변경 - 현재 회원 update에서 변경되는 부분만 인자로 남겨둠 - update 시, Member 에서 MemberInfo.getXX을 하는 대신 MemberInfo에서 부분 변경된 객체를 새로 반환하도록 수정 * feat: 회원 정보 수정 API 구현 및 테스트 작성 * test: JwtTokenProviderTest 작성 - 로컬에서 Postman 테스트 시 이 테스트를 사용하면 쉽게 토큰 발급 후 활용 가능 * chore: 로컬 환경용 더미데이터 sql 작성 * chore: 로컬 환경 data.sql을 위한 서브모듈 변경 * docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원) * chore: 로그 환경설정 파일 디렉터리 분리 * feat: 닉네임 중복 검증 구현 * refactor: 회원의 이메일 Unique 제약조건 삭제 - 닉네임, OauthId로 회원을 식별할 수 있다. - 같은 이메일로 네이버, 카카오에 가입한 사람이 소셜 로그인으로 두 계정을 만들 경우, 동일한 이메일이 저장될 수도 있다. * refactor: 모호한 메서드명 수정 * refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경 - findByEmail 대신 findById - 기본키가 아닌 유일키로 조회하는 건 테이블 구조 변경 여지가 있으므로 findById 사용 * refactor: 내 정보 수정 API URI 변경 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * [BE] Refactor/#406 토픽 권한을 가진 회원 목록 조회 시 공개 여부를 함께 반환하도록 변경 (#412) * refactor: 사용하지 않는 MemberRepository 메서드 삭제 * refactor: 회원 업데이트 부분 변경으로 시그니처 변경 - 현재 회원 update에서 변경되는 부분만 인자로 남겨둠 - update 시, Member 에서 MemberInfo.getXX을 하는 대신 MemberInfo에서 부분 변경된 객체를 새로 반환하도록 수정 * feat: 회원 정보 수정 API 구현 및 테스트 작성 * test: JwtTokenProviderTest 작성 - 로컬에서 Postman 테스트 시 이 테스트를 사용하면 쉽게 토큰 발급 후 활용 가능 * chore: 로컬 환경용 더미데이터 sql 작성 * chore: 로컬 환경 data.sql을 위한 서브모듈 변경 * docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원) * chore: 로그 환경설정 파일 디렉터리 분리 * feat: 닉네임 중복 검증 구현 * refactor: 회원의 이메일 Unique 제약조건 삭제 - 닉네임, OauthId로 회원을 식별할 수 있다. - 같은 이메일로 네이버, 카카오에 가입한 사람이 소셜 로그인으로 두 계정을 만들 경우, 동일한 이메일이 저장될 수도 있다. * refactor: 모호한 메서드명 수정 * refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경 - findByEmail 대신 findById - 기본키가 아닌 유일키로 조회하는 건 테이블 구조 변경 여지가 있으므로 findById 사용 * refactor: 내 정보 수정 API URI 변경 * refactor: 토픽 권한 회원 목록 조회 API를 접근 정보(권한 회원 목록 및 공개 여부) 조회로 명세 변경 - 관련 검토가 필요한 API 설계 및 구현 내용에 대한 TODO 주석 작성 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * refactor: 실수로 바꾼 기존 메서드명 원복 * refactor: 불필요한 import문 제거 * refactor: 불필요한 접근제어자, 중복 코드 제거 * docs: Restdocs API 네이밍 반영 * fix: 내 정보 수정 RestDocs 스니펫 누락 추가 * [BE] Feature/#388 refresh token 및 로그아웃 기능 구현 (#411) * chore: redis 의존성 추가 * refactor: OauthService 필드에 final 추가 * feat: refreshToken 엔티티 및 레포지토리 구현 * feat: JwtTokenProvider RefreshToken 발급 구현 * feat: 로그인 시 RefreshToken 발급 기능 구현 * feat: Auth 패키지 커스텀 예외 추가 * refactor: validate 메서드 리팩터링 * chore: refreshToken 만료 시간 추가 * test: Test를 위한 설정 변경 * feat: 액세스 토큰 재발급 및 로그아웃 기능 구현 * chore: Redis 의존성 제거 * test: TestTokenProvider 객체 구현 * refactor: /logout HttpMethod 변경, cookie 관련 cors설정 및 maxAge 설정, * test: DisplayName 추가 * feat: RTR 적용 및 OauthConntroller 제거, OauthService 및 TokenService 역할과 책임 재분배 * refactor : 피드백 반영 * refactor : 매직넘버 상수화 * refactor : 네이밍 수정 * feat: 쿠키 설정 추가 * [BE] Fix/#424 refresh token duplicated (#425) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * [BE] Fix/#426 Token CORS 재설정 (#427) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * [BE] HotFix/#426 Refresh Token 중복 저장 방지 로직 수정 (#431) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * [BE] HotFix/#426 delete 메서드에 clearAutomatically 속성 적용 (#432) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * [BE] HotFix/#426 tokenService flush 추가 (#433) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * fix: delete 메서드에 clearAutomatically 속성 제거 및 flush 추가 * [BE] Refactor/#400 토픽 조회 시 업데이트 일시를 최근에 핀이 추가/변경된 일시로 변경 (#429) * refactor: BaseEntity의 createdAt update 방지 * feat: Topic에 lastPinUpdatedAt 컬럼 추가, EntityListner 적용 - 기존 BaseEntity의 값들은 객체가 영속화될 때 저장된다. - 이에 대해 일관성을 유지해야 한다. (핀 생성 일시, 핀 변경 일시 = 토픽의 최근 핀 변경 일시가 서로 같아야 하므로) - 따라서 lastPinUpdatedAt 컬럼의 업데이트 또한 EntityListener 로 적용한다. * feat: 토픽 조회 DTO의 updatedAt 값 lastPinUpdatedAt 으로 변경 * feat: 토픽 최신순 조회 로직 수정 - Topic에 lastPinUpdatedAt 추가로 인해 로직 수정 가능 * test: 토픽 조회 시 updatedAt 검증 테스트 추가 * chore: 로컬 테스트용 SQL에 테이블 컬럼 추가 변경 반영 * refactor: 토픽 Response Dto에 lastPinUpdatedAt 반영 * fix : 토큰 만료시간 및 redirect uri 수정 --------- Co-authored-by: jaeyeon kim <jakind@naver.com> * [BE] Feature/#422 성능 측정을 위한 로깅 구현 (#434) * feat: QueryCounter 객체 구현 * feat: QueryInspector 객체 구현 * feat: LatencyRecorder 객체 구현 * feat: LatencyLoggingFilter 객체 구현 * feat: LatencyRecorder Thread-safe 테스트 구현 * feat: HibernateConfig 구현 * test: 테스트 수정 * style: 개행 추가 * refactor: 수식 표현 방식 수정 * [BE] HotFix/#424 refresh token duplicated (#441) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * [BE] HOTFix/#424 validateTokensForReissue 디버깅을 위한 에러코드 추가 (#443) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * fix: validateTokensForReissue 디버깅을 위한 에러코드 추가 * fix: isExpired 임시 log 처리 (#444) * Revert "fix: isExpired 임시 log 처리 (#444)" This reverts commit 445f0dd. * fix: cors Credentials 추가 (#458) * [BE] Hotfix/cors allowHeaders 와일드카드 적용 (#462) * fix: cors Credentials 추가 * fix: allowedHeaders 와일드카드 적용 * [BE] 부하테스트를 위한 Tomcat Log 추가 (#464) * chore: yml 변수 적용 확인을 위한 debug 로그 추가 * chore: 톰캣 설정 기본값 추가 * chore: 톰캣 설정 기본값 추가 --------- Co-authored-by: yoondgu <doyoungwork@gmail.com> * [BE] S3 를 통한 Image Upload 기능 구현 (#428) * refactor : s3 패키지 추가로 인한 에러 Code 수정 * feat : s3 exception 추가 * refactor : image extension 추출 방식 수정 * refactor : S3Client 가 IOException 을 throw 할 수 있도록 작성 * style : 프린트, 주석 제거 * test : imageExtension Test 작성 * refactor : image 가 요청으로 들어오지 않는 경우를 고려해 로직 수정 * test : 이미지가 null 로 들어오는 경우 test 작성 * feat : 병합시에도 S3 Image Upload 가 가능하도록 구현 * refactor : 기본 이미지 URL 변경 * refactor : 기본 이미지의 처리를 TopicInfo -> Image 에서 할 수 있도록 수정 * refactor : 주석 앞에 TODO 추가 * refactor : fromImageFileName -> from 으로 메서드 명 변경 * refactor : getExtension -> findExtension 으로 변경 * refactor : S3 관련 Service 네이밍 수정 * [BE] Fix/#426 Token CORS 재설정 (#427) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * refactor : S3 관련 Service 네이밍 수정 * [BE] HotFix/#426 Refresh Token 중복 저장 방지 로직 수정 (#431) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * [BE] HotFix/#426 delete 메서드에 clearAutomatically 속성 적용 (#432) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * [BE] HotFix/#426 tokenService flush 추가 (#433) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * fix: delete 메서드에 clearAutomatically 속성 제거 및 flush 추가 * [BE] Refactor/#400 토픽 조회 시 업데이트 일시를 최근에 핀이 추가/변경된 일시로 변경 (#429) * refactor: BaseEntity의 createdAt update 방지 * feat: Topic에 lastPinUpdatedAt 컬럼 추가, EntityListner 적용 - 기존 BaseEntity의 값들은 객체가 영속화될 때 저장된다. - 이에 대해 일관성을 유지해야 한다. (핀 생성 일시, 핀 변경 일시 = 토픽의 최근 핀 변경 일시가 서로 같아야 하므로) - 따라서 lastPinUpdatedAt 컬럼의 업데이트 또한 EntityListener 로 적용한다. * feat: 토픽 조회 DTO의 updatedAt 값 lastPinUpdatedAt 으로 변경 * feat: 토픽 최신순 조회 로직 수정 - Topic에 lastPinUpdatedAt 추가로 인해 로직 수정 가능 * test: 토픽 조회 시 updatedAt 검증 테스트 추가 * chore: 로컬 테스트용 SQL에 테이블 컬럼 추가 변경 반영 * refactor: 토픽 Response Dto에 lastPinUpdatedAt 반영 * fix : 토큰 만료시간 및 redirect uri 수정 --------- Co-authored-by: jaeyeon kim <jakind@naver.com> * [BE] Feature/#422 성능 측정을 위한 로깅 구현 (#434) * feat: QueryCounter 객체 구현 * feat: QueryInspector 객체 구현 * feat: LatencyRecorder 객체 구현 * feat: LatencyLoggingFilter 객체 구현 * feat: LatencyRecorder Thread-safe 테스트 구현 * feat: HibernateConfig 구현 * test: 테스트 수정 * style: 개행 추가 * refactor: 수식 표현 방식 수정 * [BE] HotFix/#424 refresh token duplicated (#441) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * [BE] HOTFix/#424 validateTokensForReissue 디버깅을 위한 에러코드 추가 (#443) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * fix: validateTokensForReissue 디버깅을 위한 에러코드 추가 * fix: isExpired 임시 log 처리 (#444) * Revert "fix: isExpired 임시 log 처리 (#444)" This reverts commit 445f0dd. * fix: cors Credentials 추가 (#458) * [BE] Hotfix/cors allowHeaders 와일드카드 적용 (#462) * fix: cors Credentials 추가 * fix: allowedHeaders 와일드카드 적용 * [BE] 부하테스트를 위한 Tomcat Log 추가 (#464) * chore: yml 변수 적용 확인을 위한 debug 로그 추가 * chore: 톰캣 설정 기본값 추가 * chore: 톰캣 설정 기본값 추가 --------- Co-authored-by: yoondgu <doyoungwork@gmail.com> * refactor : s3 패키지 추가로 인한 에러 Code 수정 * feat : s3 exception 추가 * refactor : image extension 추출 방식 수정 * refactor : S3Client 가 IOException 을 throw 할 수 있도록 작성 * style : 프린트, 주석 제거 * test : imageExtension Test 작성 * refactor : image 가 요청으로 들어오지 않는 경우를 고려해 로직 수정 * test : 이미지가 null 로 들어오는 경우 test 작성 * feat : 병합시에도 S3 Image Upload 가 가능하도록 구현 * refactor : 기본 이미지 URL 변경 * refactor : 기본 이미지의 처리를 TopicInfo -> Image 에서 할 수 있도록 수정 * refactor : 주석 앞에 TODO 추가 * refactor : fromImageFileName -> from 으로 메서드 명 변경 * refactor : getExtension -> findExtension 으로 변경 * refactor : S3 관련 Service 네이밍 수정 * refactor : S3 관련 Service 네이밍 수정 * refactor : topic, image errorCode 수정 * refactor : Exception 부분 네이밍 S3 -> Image 로 변경 * refactor : findExtension -> extractExtensio 으로 메서드 네이밍 변경 * refactor : 부정 조건문 제거 * refactor : Illegal Image File Extension 에러 메세지 수정 * refactor : action method consume type 순서 조정 --------- Co-authored-by: 준팍(junpak) <112045553+junpakPark@users.noreply.github.com> Co-authored-by: Doy <doyoungwork@gmail.com> Co-authored-by: zun <50602742+cpot5620@users.noreply.github.com> --------- Co-authored-by: Doy <doyoungwork@gmail.com> Co-authored-by: 준팍(junpak) <112045553+junpakPark@users.noreply.github.com> Co-authored-by: jaeyeon kim <jakind@naver.com> Co-authored-by: yoondgu <doyou2ing@naver.com> Co-authored-by: kpeel5839 <89840550+kpeel5839@users.noreply.github.com>
* [BE] Fix/#366 테스트 수행 시 로그 패턴 깨지는 오류 해결 (#367) * fix: 테스트 로그 패턴 설정 오류 수정 - 잘못된 로그 패턴 설정으로 인한 'LOG_PATTERN IS_UNDEFINED' 메시지 출력 오류 수정 * chore: 프론트엔드, 백엔드 develop 분리에 따른 워크플로우 수정 * fix: 테스트 로그 설정 파일명 변경 - springProperty 지원을 위해 파일명 변경 * feat: 로그 내용 및 설정 보완(색상 적용) - 로그에서 로거, 레벨, PID 확인 가능하도록 내용 보완 - 테스트 로그의 경우 프로젝트 패키지에 해당하는 로그만 DEBUG 레벨로 설정 - 콘솔 로그 색상 적용 * feat: 로그 내용 보완 - 스레드 출력하도록 수정 * refactor: DataBaseCleanup JdbcTemplate 적용 (#371) * [BE] Refactor/#376 로깅 환경 개선을 위한 설정 파일 리팩터링 (#377) * refactor: 로깅 전략 보완에 따른 설정 파일 수정 - 운영 환경 별 로그 종류(콘솔, 파일), 레벨 변경 (PR 첨부 설명 참조) - logback-spring.xml 에서 Appender 분리 - application-*.xml 에서 로그 패턴 값 삭제 * chore: 에러 로그 슬랙 알림을 위한 의존성 추가, 관련 주석 작성 * remove: 테스트 설정 파일에 불필요한 로그 설정 삭제 * style: 불필요한 빈 줄 삭제 * chore: 콘솔 파일 로그 설정 삭제로 인한 불필요한 설정 삭제 * chore: 로그 파일 롤링 용량 설정 모든 레벨 통일 * [BE] Chore/#372 submodule 적용 (#375) * chore: submodule 적용 Co-authored-by: jaeyeon kim <jakind@naver.com> * chore: workflow access token 적용 Co-authored-by: jaeyeon kim <jakind@naver.com> --------- Co-authored-by: jaeyeon kim <jakind@naver.com> Co-authored-by: yoondgu <doyou2ing@naver.com> * feat: 운영 서버 500 에러 슬랙 알림 적용, 로컬 환경변수 서브모듈에 저장 (#379) * hotfix: yml 문법으로 인한 오류 수정 (#381) * fix: 워크플로우 서브모듈 문제 해결 (#384) * chore: 워크플로우 서브모듈 문제 해결 확인을 위한 push (#385) * Feat/#386 image (#391) * chore : S3 의존성 추가 * feat : Amazon S3 Component 추가 * feat : S3 에 업로드 될 Image 의 이름을 설정해주는 ImageName 과 UploadFile 추가 * feat : S3 에 업로드 될 Image 의 이름을 설정해주는 ImageNae 과 UploadFile 추가 * feat : 파일을 업로드하고 해당하는 URL 을 반환하는 Service 구현 * chore : s3 환경 설정 추가 * feat : String image -> MultipartFile image 로 변경 * feat : @RequestPart 적용 및 S3 upload 로직 추가 * chore : Test 시 Profile 설정 * test : 테스트 시 S3 에 접근하지 않도록 하기 위해 Profile 별로 S3Service Bean 구분 * test : 추가된 Image 저장 기능에 맞춰 일부 Test 수정 * chore : S3, CloudFront 환경설정 적용 * test : RestDocs 수정중 * refactor : S3 Bean 추가 (#396) * [BE] Refactor/#390 지도 및 핀 상세 조회 API에 수정 권한 여부 필드 추가 (#392) * feat: 토픽 상세 조회 내용에 수정 권한 추가 * refactor: 토픽 상세 조회 DTO 정적 팩터리 메서드명 수정 * feat: 핀 상세 조회 내용에 수정 권한 추가 * fix: 토픽 RestDocs 깨지는 문제 수정, API 목차 순서 조정 - 목록 조회 API 기준으로 순서 조정, API 네이밍 보완 * refactor: 토픽, 핀 수정 권한 여부 필드명 직관적으로 수정 - hasUpdatePermission -> canUpdate * refactor: Guest 전용 토픽 상세조회 DTO 정적 팩터리 메서드 정의 * refactor: 메서드 순서 정리, Guest 전용 토픽 조회 DTO 정적 팩터리 메서드 정의 * refactor: Guest 전용 토픽 List 조회 DTO 정적 팩터리 메서드 정의 * refactor: Info, Debug 레벨 커스텀 로그만 출력하도록 변경 (#397) * feat : pin 생성시 image upload 기능 추가 (#401) * [BE] Feat/#378 Admin API 구현 (#405) * feat: 전체 회원 조회 기능 구현 * feat: 회원 삭제(탈퇴) 기능 구현 * feat: 회원 삭제(탈퇴)시 Pin/Topic Soft-deleting 구현 * refactor: Admin DTO 분리 * feat: Member 상세 정보 조회 기능 구현 * feat: Topic 삭제 및 이미지 삭제 기능 구현 * feat: Pin 삭제 및 이미지 삭제 기능 구현 * feat: Admin API 구현 * refactor: Member 상태(차단, 탈퇴 등) 필드에 따른 로그인 로직 수정 * refactor: @SqlDelete 삭제 및 JPQL 대체 * feat: AdminInterceptor 구현 * test: Repository soft-deleting 테스트 구현 * test: AdminQueryService 테스트 구현 * test: AdminCommandService 테스트 구현 * test: AdminController Restdocs 테스트 구현 * test: AdminInterceptor Mocking * test: 통합 테스트 구현 * refactor: 오탈자 수정 * refactor: Auth 관련 예외 클래스 추가 * refactor: 불필요한 메서드 제거 * refactor: findMemberById 예외 수정 * test: GithubActions 실패 테스트 수정 * refactor: isAdmin() 메서드 추가 * refactor: 회원 삭제(탈퇴)시, 추가 정보(즐겨찾기 등) 삭제 * Revert "[BE] Feat/#378 Admin API 구현 (#405)" (#414) This reverts commit 4722faa. * [BE] Feat/#378 Admin API 구현 (#415) * feat: 전체 회원 조회 기능 구현 * feat: 회원 삭제(탈퇴) 기능 구현 * feat: 회원 삭제(탈퇴)시 Pin/Topic Soft-deleting 구현 * refactor: Admin DTO 분리 * feat: Member 상세 정보 조회 기능 구현 * feat: Topic 삭제 및 이미지 삭제 기능 구현 * feat: Pin 삭제 및 이미지 삭제 기능 구현 * feat: Admin API 구현 * refactor: Member 상태(차단, 탈퇴 등) 필드에 따른 로그인 로직 수정 * refactor: @SqlDelete 삭제 및 JPQL 대체 * feat: AdminInterceptor 구현 * test: Repository soft-deleting 테스트 구현 * test: AdminQueryService 테스트 구현 * test: AdminCommandService 테스트 구현 * test: AdminController Restdocs 테스트 구현 * test: AdminInterceptor Mocking * test: 통합 테스트 구현 * refactor: 오탈자 수정 * refactor: Auth 관련 예외 클래스 추가 * refactor: 불필요한 메서드 제거 * refactor: findMemberById 예외 수정 * test: GithubActions 실패 테스트 수정 * refactor: isAdmin() 메서드 추가 * refactor: 회원 삭제(탈퇴)시, 추가 정보(즐겨찾기 등) 삭제 * refactor: Member status 기본값 설정 * remove: Member status 기본값 설정 삭제 * [BE] Feature/#399 내 정보(회원 닉네임) 수정 API 구현 (#408) * refactor: 사용하지 않는 MemberRepository 메서드 삭제 * refactor: 회원 업데이트 부분 변경으로 시그니처 변경 - 현재 회원 update에서 변경되는 부분만 인자로 남겨둠 - update 시, Member 에서 MemberInfo.getXX을 하는 대신 MemberInfo에서 부분 변경된 객체를 새로 반환하도록 수정 * feat: 회원 정보 수정 API 구현 및 테스트 작성 * test: JwtTokenProviderTest 작성 - 로컬에서 Postman 테스트 시 이 테스트를 사용하면 쉽게 토큰 발급 후 활용 가능 * chore: 로컬 환경용 더미데이터 sql 작성 * chore: 로컬 환경 data.sql을 위한 서브모듈 변경 * docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원) * chore: 로그 환경설정 파일 디렉터리 분리 * feat: 닉네임 중복 검증 구현 * refactor: 회원의 이메일 Unique 제약조건 삭제 - 닉네임, OauthId로 회원을 식별할 수 있다. - 같은 이메일로 네이버, 카카오에 가입한 사람이 소셜 로그인으로 두 계정을 만들 경우, 동일한 이메일이 저장될 수도 있다. * refactor: 모호한 메서드명 수정 * refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경 - findByEmail 대신 findById - 기본키가 아닌 유일키로 조회하는 건 테이블 구조 변경 여지가 있으므로 findById 사용 * refactor: 내 정보 수정 API URI 변경 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * [BE] Refactor/#406 토픽 권한을 가진 회원 목록 조회 시 공개 여부를 함께 반환하도록 변경 (#412) * refactor: 사용하지 않는 MemberRepository 메서드 삭제 * refactor: 회원 업데이트 부분 변경으로 시그니처 변경 - 현재 회원 update에서 변경되는 부분만 인자로 남겨둠 - update 시, Member 에서 MemberInfo.getXX을 하는 대신 MemberInfo에서 부분 변경된 객체를 새로 반환하도록 수정 * feat: 회원 정보 수정 API 구현 및 테스트 작성 * test: JwtTokenProviderTest 작성 - 로컬에서 Postman 테스트 시 이 테스트를 사용하면 쉽게 토큰 발급 후 활용 가능 * chore: 로컬 환경용 더미데이터 sql 작성 * chore: 로컬 환경 data.sql을 위한 서브모듈 변경 * docs: 기능 명세서 및 테스트 코드 용어 정리 (유저, 멤버 -> 회원) * chore: 로그 환경설정 파일 디렉터리 분리 * feat: 닉네임 중복 검증 구현 * refactor: 회원의 이메일 Unique 제약조건 삭제 - 닉네임, OauthId로 회원을 식별할 수 있다. - 같은 이메일로 네이버, 카카오에 가입한 사람이 소셜 로그인으로 두 계정을 만들 경우, 동일한 이메일이 저장될 수도 있다. * refactor: 모호한 메서드명 수정 * refactor: Email이 Unique하지 않음에 따라 테스트에서 사용하는 조회 쿼리 변경 - findByEmail 대신 findById - 기본키가 아닌 유일키로 조회하는 건 테이블 구조 변경 여지가 있으므로 findById 사용 * refactor: 내 정보 수정 API URI 변경 * refactor: 토픽 권한 회원 목록 조회 API를 접근 정보(권한 회원 목록 및 공개 여부) 조회로 명세 변경 - 관련 검토가 필요한 API 설계 및 구현 내용에 대한 TODO 주석 작성 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * fix: 디렉터리 분리에 따른 로그 설정 파일 appender 경로 수정 * refactor: 실수로 바꾼 기존 메서드명 원복 * refactor: 불필요한 import문 제거 * refactor: 불필요한 접근제어자, 중복 코드 제거 * docs: Restdocs API 네이밍 반영 * fix: 내 정보 수정 RestDocs 스니펫 누락 추가 * [BE] Feature/#388 refresh token 및 로그아웃 기능 구현 (#411) * chore: redis 의존성 추가 * refactor: OauthService 필드에 final 추가 * feat: refreshToken 엔티티 및 레포지토리 구현 * feat: JwtTokenProvider RefreshToken 발급 구현 * feat: 로그인 시 RefreshToken 발급 기능 구현 * feat: Auth 패키지 커스텀 예외 추가 * refactor: validate 메서드 리팩터링 * chore: refreshToken 만료 시간 추가 * test: Test를 위한 설정 변경 * feat: 액세스 토큰 재발급 및 로그아웃 기능 구현 * chore: Redis 의존성 제거 * test: TestTokenProvider 객체 구현 * refactor: /logout HttpMethod 변경, cookie 관련 cors설정 및 maxAge 설정, * test: DisplayName 추가 * feat: RTR 적용 및 OauthConntroller 제거, OauthService 및 TokenService 역할과 책임 재분배 * refactor : 피드백 반영 * refactor : 매직넘버 상수화 * refactor : 네이밍 수정 * feat: 쿠키 설정 추가 * [BE] Fix/#424 refresh token duplicated (#425) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * [BE] Fix/#426 Token CORS 재설정 (#427) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * [BE] HotFix/#426 Refresh Token 중복 저장 방지 로직 수정 (#431) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * [BE] HotFix/#426 delete 메서드에 clearAutomatically 속성 적용 (#432) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * [BE] HotFix/#426 tokenService flush 추가 (#433) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * fix: delete 메서드에 clearAutomatically 속성 제거 및 flush 추가 * [BE] Refactor/#400 토픽 조회 시 업데이트 일시를 최근에 핀이 추가/변경된 일시로 변경 (#429) * refactor: BaseEntity의 createdAt update 방지 * feat: Topic에 lastPinUpdatedAt 컬럼 추가, EntityListner 적용 - 기존 BaseEntity의 값들은 객체가 영속화될 때 저장된다. - 이에 대해 일관성을 유지해야 한다. (핀 생성 일시, 핀 변경 일시 = 토픽의 최근 핀 변경 일시가 서로 같아야 하므로) - 따라서 lastPinUpdatedAt 컬럼의 업데이트 또한 EntityListener 로 적용한다. * feat: 토픽 조회 DTO의 updatedAt 값 lastPinUpdatedAt 으로 변경 * feat: 토픽 최신순 조회 로직 수정 - Topic에 lastPinUpdatedAt 추가로 인해 로직 수정 가능 * test: 토픽 조회 시 updatedAt 검증 테스트 추가 * chore: 로컬 테스트용 SQL에 테이블 컬럼 추가 변경 반영 * refactor: 토픽 Response Dto에 lastPinUpdatedAt 반영 * fix : 토큰 만료시간 및 redirect uri 수정 --------- Co-authored-by: jaeyeon kim <jakind@naver.com> * [BE] Feature/#422 성능 측정을 위한 로깅 구현 (#434) * feat: QueryCounter 객체 구현 * feat: QueryInspector 객체 구현 * feat: LatencyRecorder 객체 구현 * feat: LatencyLoggingFilter 객체 구현 * feat: LatencyRecorder Thread-safe 테스트 구현 * feat: HibernateConfig 구현 * test: 테스트 수정 * style: 개행 추가 * refactor: 수식 표현 방식 수정 * [BE] HotFix/#424 refresh token duplicated (#441) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * [BE] HOTFix/#424 validateTokensForReissue 디버깅을 위한 에러코드 추가 (#443) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * fix: validateTokensForReissue 디버깅을 위한 에러코드 추가 * fix: isExpired 임시 log 처리 (#444) * Revert "fix: isExpired 임시 log 처리 (#444)" This reverts commit 445f0dd. * fix: cors Credentials 추가 (#458) * [BE] Hotfix/cors allowHeaders 와일드카드 적용 (#462) * fix: cors Credentials 추가 * fix: allowedHeaders 와일드카드 적용 * [BE] 부하테스트를 위한 Tomcat Log 추가 (#464) * chore: yml 변수 적용 확인을 위한 debug 로그 추가 * chore: 톰캣 설정 기본값 추가 * chore: 톰캣 설정 기본값 추가 --------- Co-authored-by: yoondgu <doyoungwork@gmail.com> * [BE] S3 를 통한 Image Upload 기능 구현 (#428) * refactor : s3 패키지 추가로 인한 에러 Code 수정 * feat : s3 exception 추가 * refactor : image extension 추출 방식 수정 * refactor : S3Client 가 IOException 을 throw 할 수 있도록 작성 * style : 프린트, 주석 제거 * test : imageExtension Test 작성 * refactor : image 가 요청으로 들어오지 않는 경우를 고려해 로직 수정 * test : 이미지가 null 로 들어오는 경우 test 작성 * feat : 병합시에도 S3 Image Upload 가 가능하도록 구현 * refactor : 기본 이미지 URL 변경 * refactor : 기본 이미지의 처리를 TopicInfo -> Image 에서 할 수 있도록 수정 * refactor : 주석 앞에 TODO 추가 * refactor : fromImageFileName -> from 으로 메서드 명 변경 * refactor : getExtension -> findExtension 으로 변경 * refactor : S3 관련 Service 네이밍 수정 * [BE] Fix/#426 Token CORS 재설정 (#427) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * refactor : S3 관련 Service 네이밍 수정 * [BE] HotFix/#426 Refresh Token 중복 저장 방지 로직 수정 (#431) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * [BE] HotFix/#426 delete 메서드에 clearAutomatically 속성 적용 (#432) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * [BE] HotFix/#426 tokenService flush 추가 (#433) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: refreshToken 존재 시 삭제 로직 변경 * fix: delete 메서드에 clearAutomatically 속성 적용 * fix: delete 메서드에 clearAutomatically 속성 제거 및 flush 추가 * [BE] Refactor/#400 토픽 조회 시 업데이트 일시를 최근에 핀이 추가/변경된 일시로 변경 (#429) * refactor: BaseEntity의 createdAt update 방지 * feat: Topic에 lastPinUpdatedAt 컬럼 추가, EntityListner 적용 - 기존 BaseEntity의 값들은 객체가 영속화될 때 저장된다. - 이에 대해 일관성을 유지해야 한다. (핀 생성 일시, 핀 변경 일시 = 토픽의 최근 핀 변경 일시가 서로 같아야 하므로) - 따라서 lastPinUpdatedAt 컬럼의 업데이트 또한 EntityListener 로 적용한다. * feat: 토픽 조회 DTO의 updatedAt 값 lastPinUpdatedAt 으로 변경 * feat: 토픽 최신순 조회 로직 수정 - Topic에 lastPinUpdatedAt 추가로 인해 로직 수정 가능 * test: 토픽 조회 시 updatedAt 검증 테스트 추가 * chore: 로컬 테스트용 SQL에 테이블 컬럼 추가 변경 반영 * refactor: 토픽 Response Dto에 lastPinUpdatedAt 반영 * fix : 토큰 만료시간 및 redirect uri 수정 --------- Co-authored-by: jaeyeon kim <jakind@naver.com> * [BE] Feature/#422 성능 측정을 위한 로깅 구현 (#434) * feat: QueryCounter 객체 구현 * feat: QueryInspector 객체 구현 * feat: LatencyRecorder 객체 구현 * feat: LatencyLoggingFilter 객체 구현 * feat: LatencyRecorder Thread-safe 테스트 구현 * feat: HibernateConfig 구현 * test: 테스트 수정 * style: 개행 추가 * refactor: 수식 표현 방식 수정 * [BE] HotFix/#424 refresh token duplicated (#441) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * [BE] HOTFix/#424 validateTokensForReissue 디버깅을 위한 에러코드 추가 (#443) * fix: RefreshToken Payload 추가 및 CORS 완화 * fix: Refresh Token Header 허용 * fix: CORS 재설정 및 sameSite None * fix: 디버깅을 위한 에러코드 추가 * fix: validateTokensForReissue 디버깅을 위한 에러코드 추가 * fix: isExpired 임시 log 처리 (#444) * Revert "fix: isExpired 임시 log 처리 (#444)" This reverts commit 445f0dd. * fix: cors Credentials 추가 (#458) * [BE] Hotfix/cors allowHeaders 와일드카드 적용 (#462) * fix: cors Credentials 추가 * fix: allowedHeaders 와일드카드 적용 * [BE] 부하테스트를 위한 Tomcat Log 추가 (#464) * chore: yml 변수 적용 확인을 위한 debug 로그 추가 * chore: 톰캣 설정 기본값 추가 * chore: 톰캣 설정 기본값 추가 --------- Co-authored-by: yoondgu <doyoungwork@gmail.com> * refactor : s3 패키지 추가로 인한 에러 Code 수정 * feat : s3 exception 추가 * refactor : image extension 추출 방식 수정 * refactor : S3Client 가 IOException 을 throw 할 수 있도록 작성 * style : 프린트, 주석 제거 * test : imageExtension Test 작성 * refactor : image 가 요청으로 들어오지 않는 경우를 고려해 로직 수정 * test : 이미지가 null 로 들어오는 경우 test 작성 * feat : 병합시에도 S3 Image Upload 가 가능하도록 구현 * refactor : 기본 이미지 URL 변경 * refactor : 기본 이미지의 처리를 TopicInfo -> Image 에서 할 수 있도록 수정 * refactor : 주석 앞에 TODO 추가 * refactor : fromImageFileName -> from 으로 메서드 명 변경 * refactor : getExtension -> findExtension 으로 변경 * refactor : S3 관련 Service 네이밍 수정 * refactor : S3 관련 Service 네이밍 수정 * refactor : topic, image errorCode 수정 * refactor : Exception 부분 네이밍 S3 -> Image 로 변경 * refactor : findExtension -> extractExtensio 으로 메서드 네이밍 변경 * refactor : 부정 조건문 제거 * refactor : Illegal Image File Extension 에러 메세지 수정 * refactor : action method consume type 순서 조정 --------- Co-authored-by: 준팍(junpak) <112045553+junpakPark@users.noreply.github.com> Co-authored-by: Doy <doyoungwork@gmail.com> Co-authored-by: zun <50602742+cpot5620@users.noreply.github.com> --------- Co-authored-by: zun <50602742+cpot5620@users.noreply.github.com> Co-authored-by: 준팍(junpak) <112045553+junpakPark@users.noreply.github.com> Co-authored-by: jaeyeon kim <jakind@naver.com> Co-authored-by: kpeel5839 <89840550+kpeel5839@users.noreply.github.com>
작업 대상
관리자 페이지에 사용할 API 구현
📄 작업 내용
🙋🏻 주의 사항
1. 기존에 존재하는 API를 활용할 수 없는 기능들에 대해서 별도의 Service를 통해 구현했습니다.
ex) 일반 사용자가 조회할 수 있는 Member의 정보와 Admin이 조회할 수 있는가 정보가 다르다.
-> AdminQueryService에서 구현
2. 특정 멤버를 서비스로부터 차단(탈퇴)시킬 경우, 관련된 Topic, Pin을 삭제해야합니다.
hard-deleting 할 경우, 추후 문제가 될 수 있는 요소가 존재한다고 판단했습니다.
ex) 범죄 등에 사용 될 경우 증거 자료 유지 필요 (?)
이에따라, soft-deleting을 수행해야했습니다.
cascde remove + @SqlDelete 어노테이션을 사용하려 했으나, Entity에 native Query가 존재한다는 점이 별로...라고 생각했습니다.
또한, @SqlDelete를 달아둘 경우, 영속성 컨텍스트가 flush 될 때 해당 쿼리문으로 바꿔서 나가기 때문에 테스트에 어려움이 생겼습니다.
(보다 자세한 설명이 필요하다면 요청 부탁드립니다)
이에따라, JPQL을 활용하여 soft-deleting을 구현했습니다.
3. 권한 등을 부여할 때 회원 조회가 일어나는데, 2번 상황으로 인해 차단 된 회원도 조회될 가능성이 존재합니다.
이에따라, MemberInfo 내부에 Status라는 필드를 추가했습니다.
해당 Status는 일반 사용자(Normal), 탈퇴 회원(DELETE), 차단 회원(BLOCK)으로 구성되어 있습니다.
탈퇴 회원과 차단 회원을 분리한 이유는, 추후 재 가입시에 차단 회원인 경우 가입을 금지시켜야한다고 판단했기 때문입니다.
4. RestDocs + Repository + Service 계층까지는 테스트를 구현했으나, E2E 테스트가 미구현된 상태입니다.
리뷰 반영을 고려했을 때, E2E 테스트 구현 후 PR 요청시 일정을 맞추기 어려울 것이라고 판단하여 우선적으로 PR 요청 보냅니다.
캠퍼스 문 닫을 시간이라 급하게 작성해서 글 정리가 잘 안 된 것 같습니다.
이해 안되시는 부분 있으면 질문주세요.
스크린샷
📎 관련 이슈
closed #378
레퍼런스