ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -15,6 +15,7 @@ import Util
 import RxDataSources
 import RxCocoa
 import RxSwift
+import StoreKit
 
 final class MainViewController: BBNavigationViewController<MainViewReactor>, UICollectionViewDelegateFlowLayout {
     private let familyViewController: MainFamilyViewController = MainFamilyViewControllerWrapper().makeViewController()
@@ -186,6 +187,13 @@ extension MainViewController {
             .bind(onNext: { $0.0.setInTimeView($0.1) })
             .disposed(by: disposeBag)
         
+        reactor.pulse(\.$isShowRatingAlert)
+            .filter { $0 }
+            .bind { _ in
+                SKStoreReviewController.requestReviewInCurrentScene()
+            }
+            .disposed(by: disposeBag)
+        
         reactor.pulse(\.$familySection)
             .distinctUntilChanged()
             .observe(on: MainScheduler.instance)

리뷰: 이거 있잖아요.. 이거 뭐 꼭 홈이여야 할까요? 이런 팝업 띄우고 하는게 꼭 홈만의 책임은 아니라 생각하는데
이거 최상단 네비게이션 바에서 띄우는건 어때요? 사실 이거말고도 그냥 토스트 메세지 생각해봐도 다음 뷰로 넘어가더라도 토스트메세지는 남아있어야하잖아여...? 그런거를 관리하기 위해 최상단을 하나 더 두는게 어떨지
@@ -15,6 +15,7 @@ import Util
 import RxDataSources
 import RxCocoa
 import RxSwift
+import StoreKit
 
 final class MainViewController: BBNavigationViewController<MainViewReactor>, UICollectionViewDelegateFlowLayout {
     private let familyViewController: MainFamilyViewController = MainFamilyViewControllerWrapper().makeViewController()
@@ -186,6 +187,13 @@ extension MainViewController {
             .bind(onNext: { $0.0.setInTimeView($0.1) })
             .disposed(by: disposeBag)
         
+        reactor.pulse(\.$isShowRatingAlert)
+            .filter { $0 }
+            .bind { _ in
+                SKStoreReviewController.requestReviewInCurrentScene()
+            }
+            .disposed(by: disposeBag)
+        
         reactor.pulse(\.$familySection)
             .distinctUntilChanged()
             .observe(on: MainScheduler.instance)

리뷰: 이게 맞는 것 같은게 사진찍고 성공하고, 네비게이터에 전달해서 가족 생성일 체크하고 팝업 띄우는게 맞는것 같네욥..?

왜냐면 지금 미션체크하는거에 그걸 두신것 같은데 그거랑 생존신고 한번하고 띄우는거랑은 차이가 있지않나요

미션은 4명가족이면 두명까지 올려야 확인이되어가지고 그때 리뷰팝업이 뜨게되는거아닌가엽?
@@ -15,6 +15,7 @@ import Util
 import RxDataSources
 import RxCocoa
 import RxSwift
+import StoreKit
 
 final class MainViewController: BBNavigationViewController<MainViewReactor>, UICollectionViewDelegateFlowLayout {
     private let familyViewController: MainFamilyViewController = MainFamilyViewControllerWrapper().makeViewController()
@@ -186,6 +187,13 @@ extension MainViewController {
             .bind(onNext: { $0.0.setInTimeView($0.1) })
             .disposed(by: disposeBag)
         
+        reactor.pulse(\.$isShowRatingAlert)
+            .filter { $0 }
+            .bind { _ in
+                SKStoreReviewController.requestReviewInCurrentScene()
+            }
+            .disposed(by: disposeBag)
+        
         reactor.pulse(\.$familySection)
             .distinctUntilChanged()
             .observe(on: MainScheduler.instance)

리뷰: > 이거 있잖아요.. 이거 뭐 꼭 홈이여야 할까요? 이런 팝업 띄우고 하는게 꼭 홈만의 책임은 아니라 생각하는데 이거 최상단 네비게이션 바에서 띄우는건 어때요? 사실 이거말고도 그냥 토스트 메세지 생각해봐도 다음 뷰로 넘어가더라도 토스트메세지는 남아있어야하잖아여...? 그런거를 관리하기 위해 최상단을 하나 더 두는게 어떨지
- 최상단 내비게이션 바에 띄우자는 게 어떤 말씀이실 까요?? 최상위 뷰에서 띄우자는 것일까요??
@@ -15,6 +15,7 @@ import Util
 import RxDataSources
 import RxCocoa
 import RxSwift
+import StoreKit
 
 final class MainViewController: BBNavigationViewController<MainViewReactor>, UICollectionViewDelegateFlowLayout {
     private let familyViewController: MainFamilyViewController = MainFamilyViewControllerWrapper().makeViewController()
@@ -186,6 +187,13 @@ extension MainViewController {
             .bind(onNext: { $0.0.setInTimeView($0.1) })
             .disposed(by: disposeBag)
         
+        reactor.pulse(\.$isShowRatingAlert)
+            .filter { $0 }
+            .bind { _ in
+                SKStoreReviewController.requestReviewInCurrentScene()
+            }
+            .disposed(by: disposeBag)
+        
         reactor.pulse(\.$familySection)
             .distinctUntilChanged()
             .observe(on: MainScheduler.instance)

리뷰: > 이게 맞는 것 같은게 사진찍고 성공하고, 네비게이터에 전달해서 가족 생성일 체크하고 팝업 띄우는게 맞는것 같네욥..?
> 
> 왜냐면 지금 미션체크하는거에 그걸 두신것 같은데 그거랑 생존신고 한번하고 띄우는거랑은 차이가 있지않나요
> 
> 미션은 4명가족이면 두명까지 올려야 확인이되어가지고 그때 리뷰팝업이 뜨게되는거아닌가엽?

오옹 맞네용 (best practice!!) 그럼 카메라 사진 찍고 나서 UseCase로 가족 생성일 체크하고 팝업 띄우는 형식으로 하겠습니다.
@@ -2,4 +2,6 @@
 
 public interface UserDetailsService {
     UserDetails loadUserByUsername(String username);
+
+    UserDetails singup(String username);
 }

리뷰: UserDetailsService`는 기본적으로 사용자 인증(로그인 시 사용자 조회)을 담당하는 인터페이스이며,  
회원가입과 같은 사용자 관리 기능은 포함되지 않는 것이 일반적입니다.  

만약 OAuth2 로그인 후 회원가입이 필요하다면,  
`OAuth2UserService`의 `loadUser()`에서 처리하는 것이 더 적절할 것 같습니다.  
2단계 미션에서 한번 개선해보면 좋을 것 같아요 😄
@@ -0,0 +1,65 @@
+package nextstep.security.authentication;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import nextstep.app.security.Oauth2LoginProperties;
+import nextstep.security.authentication.domain.OAuthLoginRedirectRequest;
+
+import java.io.IOException;
+
+public class OAuthLoginRedirectFilter implements Filter {
+
+    private static final String OAUTH2_LOGIN_REDIRECT_METHOD = "GET";
+    private static final String OAUTH2_LOGIN_REDIRECT_URI = "/oauth2/authorization/";
+
+    private final Oauth2LoginProperties oauth2LoginProperties;
+
+    public OAuthLoginRedirectFilter(Oauth2LoginProperties oauth2LoginProperties) {
+        this.oauth2LoginProperties = oauth2LoginProperties;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+
+        final String httpMethod = httpServletRequest.getMethod();
+        final String requestUri = httpServletRequest.getRequestURI();
+
+        if (!isOauth2Login(httpMethod, requestUri)) {
+            chain.doFilter(request, response);
+            return;
+        }
+
+        final String provider = requestUri.replace(OAUTH2_LOGIN_REDIRECT_URI, "");
+
+        Oauth2LoginProperties.OAuth2Provider oAuth2Provider = oauth2LoginProperties.getProvider(provider);
+
+        if (oAuth2Provider == null) {
+            chain.doFilter(request, response);
+        }
+
+        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+
+        final OAuthLoginRedirectRequest redirectRequest = createdRedirectRequest(oAuth2Provider);
+
+        httpServletResponse.sendRedirect(redirectRequest.getRedirectUri(oAuth2Provider.getLoginRequestUri()));
+    }
+
+    private boolean isOauth2Login(final String httpMethod, final String requestUri) {
+        return OAUTH2_LOGIN_REDIRECT_METHOD.equals(httpMethod) && requestUri.startsWith(OAUTH2_LOGIN_REDIRECT_URI);
+    }
+
+    private OAuthLoginRedirectRequest createdRedirectRequest(final Oauth2LoginProperties.OAuth2Provider provider) {
+        final String clientId = provider.getClientId();
+        final String responseType = "code";
+        final String scope = provider.getScope();
+        final String redirectUri = provider.getLoginRedirectUri();
+
+        return OAuthLoginRedirectRequest.created(responseType, clientId, scope, redirectUri);
+    }
+}

리뷰: chain.doFilter(request, response); 호출 이후에도 코드가 실행될 가능성이 있네요 🤔 

```suggestion
        if (oAuth2Provider == null) {
            chain.doFilter(request, response);
            return;
        }
```
@@ -0,0 +1,11 @@
+oauth2:
+  providers:
+    github:
+      client-id:
+      secret-key:
+      login-redirect-uri: /oauth2/authorization/github
+    google:
+      client-id:
+      secret-key:
+      login-redirect-uri: /o/oauth2/v2/auth
+      
\ No newline at end of file

리뷰: 설정 값들이 제대로 정의되어 있지 않네요 😢 
확인 부탁드립니다
@@ -0,0 +1,21 @@
+package com.coffeebean.domain.review.review;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+// 작성 가능 리뷰
+public class ReviewableOrderItemDto {
+
+    private Long orderItemId; // 주문 아이템 ID
+    private String itemName;  // 상품명
+    private LocalDateTime orderDate; // 주문 날짜
+}

리뷰: 현재 페이지에서 리뷰 작성을 했을 때엔 `작성 완료`로 표시되는데, 다시 접속하면 `리뷰 작성`으로 표시됩니다.
작성 가능 리뷰 응답 `ReviewableOrderItemDto`에 리뷰되었는지 여부 `isReviewed`나 `reviewId`를 추가해서 응답하면 페이지에 다시 접속했을 때 리뷰가 작성된 주문은 작성 완료로 표시하는데 활용할 수 있을 것 같습니다!
@@ -0,0 +1,21 @@
+package com.coffeebean.domain.review.review;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+// 작성 가능 리뷰
+public class ReviewableOrderItemDto {
+
+    private Long orderItemId; // 주문 아이템 ID
+    private String itemName;  // 상품명
+    private LocalDateTime orderDate; // 주문 날짜
+}

리뷰: 어 그런가요?! 필드 추가할까 하다가 프론트에서만 처리를 했는데 그럴 수 있겠어요 다시 한 번 확인해 볼게요 말씀해 주셔서 감사합니다 수정하고 다시 올릴게요! ㅎ.ㅎ