[MindDiary]ホットスポット2コントローラ・レイヤのビジネス・ロジックの大部分


ユーザーログインAPI機能の作成中に発生した問題.JWTインタフェース、Cookieインタフェース、キャッシュインタフェース、およびHttpServeretResponseクラスを使用する場合、ほとんどのロジックはコントローラレイヤで作成されます.
これらのコードは、エラーを修正したり、コードを修正したりする際に、クラスファイルをチェックしてコードを1つずつ修正する必要があり、mockitoを使用してAPIテストコードを作成する際に困難に直面します.
まず、ログインのロジックは次のとおりです.
  • 要求電子メールに一致するユーザをDBからインポートする.
  • DBからインポートしたユーザのハッシュパスワードと要求パスワードが一致するかどうかを比較します.
  • JWTトークンを使用してアクセストークンとリフレッシュトークンを発行します.
  • refresh tokenは、
  • トークンの再配布中にrefresh tokenを検証するためにRedisキャッシュに格納される.
  • access tokenはjsonペイロードであり、refresh tokenはhttponlyクッキーに格納され、返される.
  • 修正前のLoginコード

    @PostMapping("/login")
      public ResponseEntity login(@RequestBody @Valid UserLoginRequestDTO userLoginRequestDTO,
          HttpServletResponse httpServletResponse) {
    
        User user = userService.findByEmail(userLoginRequestDTO.getEmail());
        if (user == null) {
          return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    
        if (!passwordEncoder.matches(userLoginRequestDTO.getPassword(), user.getPassword())) {
          return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    
        String accessToken = jwtStrategy.createAccessToken(user.getId(), user.getRole(), user.getEmail());
        String refreshToken = jwtStrategy.createRefreshToken(user.getId(), user.getRole(), user.getEmail());
    
        AccessTokenResponseDTO accessTokenResponseDTO = new AccessTokenResponseDTO();
        accessTokenResponseDTO.setAccessToken(accessToken);
    
        Cookie cookie  = cookieStrategy.createCookie(cookieRefreshTokenKey, refreshToken);
    
        redisStrategy.setValueExpire(refreshToken, user.getEmail(), refreshTokenValidityInSeconds);
        httpServletResponse.addCookie(cookie);
        return new ResponseEntity(accessTokenResponseDTO, HttpStatus.OK);
      }
    
    これはコントローラのほとんどの論理です.
    図に示すように、このような構造です.

    このようにコードを記述するのは、コントローラ-サービス-レポート階層の役割が混同されているためです.
    WTStrategy、CookieStrategy、RedisStrategyインタフェースは、ビジネスロジックの一部であるため、サービス層であると考えられています.そうであれば、UserServiceクラスに移動することなく、コントローラを直接使用できると考えています.
    しかし,これらの構造はコードを修正する際に非常に困難であり,クラスに標準を確立することによって問題を解決する.

  • Controller:API要求と応答クラス
    -CookieまたはHttpServeret関連コードはコントローラによって作成されます.(論理をサービス層に移行すると、要求と応答時に何が伝達されるかを特定するのは難しい.これは、DTOを応答主体とし、サーブレットResponseに入れるクッキーを作成したため、コントローラ層に配置したためである.)

  • サービス:ビジネスロジックの実行
    -すべてのビジネスロジックコードがサービスによって生成されます.コントローラは、実際に実行された論理を表示せず、テストコードの作成を簡素化します.なお、以降のコード修正はサービス側をチェックするだけです.
  • 修正後のLoginコード


    上記の基準に従って、CookieヘッダをHttpServeretResponse応答に入れる部分はControllerクラスである.
    データベースの問合せ、パスワードの検証、およびキャッシュにタグを追加する論理セクションは、Serviceクラスで実行されます.(コントローラとサービスはDTOクラスを介して通信する.)
    図に示すように、構成は次のとおりです.
    //Controller 클래스
    
    @PostMapping("/login")
      public ResponseEntity login(@RequestBody @Valid UserLoginRequestDTO userLoginRequestDTO,
          HttpServletResponse httpServletResponse) {
    
        TokenResponseDTO tokenResponseDTO = userService.login(userLoginRequestDTO);
    
        if (tokenResponseDTO == null) {
          return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
    
        Cookie cookie = tokenResponseDTO.createTokenCookie(cookieStrategy, cookieRefreshTokenKey);
        httpServletResponse.addCookie(cookie);
    
        return new ResponseEntity(tokenResponseDTO.createAccessTokenResponseDTO(), HttpStatus.OK);
      }
      
      //Service 클래스
      @Override
      public TokenResponseDTO login(UserLoginRequestDTO userLoginRequestDTO) {
        User user = userRepository.findByEmail(userLoginRequestDTO.getEmail());
        if (user == null) {
          return null;
        }
    
        if (!passwordEncoder.matches(userLoginRequestDTO.getPassword(), user.getPassword())) {
          return null;
        }
    
        TokenResponseDTO tokenResponseDTO = user.createToken(tokenStrategy);
        redisStrategy.setValueExpire(tokenResponseDTO.getRefreshToken(), user.getEmail(),
            refreshTokenValidityInSeconds);
        return tokenResponseDTO;
      }
    上記のコード修正により、後でコードを修正する難易度を低減できます.しかし、コードには正解がないので、より良いコードを作成することを考慮する必要があるかもしれません.