Skip to content

아쉬운 점들 기록

zhyun edited this page Apr 29, 2024 · 18 revisions

본 프로젝트는 공부할 목적으로 구현했기 때문에 코드에 대한 아쉬운 점을 수정하지 않고면서 기록 ✍️


아쉬움 1 보완(완)

회원 탈퇴시 게시글 삭제하는 로직에 대한 아쉬움

24.04.29 예외를 발생시키지 않도록 수정하여 보완 (완)(66306d7d8673428f0cb3a8cd5a93ae59212e062c)

프로젝트를 종료하고 몇일 뒤 코드를 다시 보다가
삭제할 회원이 여럿인 경우 일부 회원에서 예외가 발생한다면
나머지 회원에 대한 게시글은 삭제되고 예외가 발생한 회원에 의해 exception이 발생되어
user 모듈에서 user 삭제 로직이 실행되지 않도록 구현되어있다.

예외가 발생하지 않아 게시글 삭제에 성공한 회원 id는 응답 결과로 출력하도록해서
게시글 삭제에 성공한 회원만 user 모듈에서도 삭제 될 수 있도록 구현했어야 했는데
개발 당시 체크하지 못해서 아쉽다.


코드

article 모듈 - service 코드

    @Override
    public void deleteUserAll(Collection<Long> userIds) {
        String failMessage = userIds.stream()
                .filter(userId -> {
                    Optional<JwtUserInfo> container = jwtUserInfoRepository.findById(userId);
                    
                    if (container.isPresent() && container.get().getGrade().equals(ROLE_WITHDRAWAL)) {
                        List<Article> list = articleRepository.findAllByUserIdOrderByCreatedAtDesc(userId);
                        articleRepository.deleteAllInBatch(list);
                        redisTemplate.delete(REDIS_ARTICLE_ID_KEY + userId);
                        return false;
                    }
                    
                    return true;
                })
                .map(userId -> {
                    Optional<JwtUserInfo> container = jwtUserInfoRepository.findById(userId);
                    
                    if (container.isEmpty())
                        return EXCEPTION_DELETED_WITHDRAWAL.formatted(userId);
                    
                    if (!container.get().getGrade().equals(ROLE_WITHDRAWAL))
                        return EXCEPTION_NOT_WITHDRAWAL.formatted(userId, container.get().getEmail());
                    
                    return "";
                })
                .collect(Collectors.joining());
        
        if (!StringUtil.isNullOrEmpty(failMessage.trim())) 
            throw new MemberException(failMessage);  // 여기서 예외를 던지지 말고 ApiResponse의 result 필드에 성공한 id 리스트와 실패한 id 리스트를 출력하도록 했어야 했다. 
                                                     // 만약 ApiResponse의 status 코드를 true로 설정해줬다면 user 삭제가 발생했겠지만 그렇게 되었다면 예외가 발생한 회원에 대해서도 삭제가 되어 설계 오류가 된다.
                                                     // 성공한 id 리스트와 실패한 id 리스트를 출력하고 user 모듈에서 응답을 받아 성공한 id 리스트는 user 삭제를 진행하고 실패한 id 리스트는 로그를 남기거나 응답으로 출력하게 했다면 좋았을걸 하는 아쉬움이 남았다.
    }
  • 240429 update
    // 변경 전
      @Override
      public void deleteUserAll(Collection<Long> userIds) {
          ...
          if (!StringUtil.isNullOrEmpty(failMessage.trim())) 
              throw new MemberException(failMessage);
      }
    // 변경 후
      @Override
      public String deleteUserAll(Collection<Long> userIds) {
          ...
          return failMessage;
      }

article 모듈 - controller 코드

    @Operation(summary = "탈퇴 유저 게시글 삭제")
    @DeleteMapping("/withdrawal/articles")
    public ResponseEntity<Object> deleteAllByUser(@RequestBody Collection<Long> userIds) {
        articleService.deleteUserAll(userIds);
        return ResponseEntity.ok(ApiResponse.builder()
                        .status(true)
                        .message(RESPONSE_ARTICLE_DELETE_FOR_WITHDRAWAL).build());  // 여기서 result 필드에 성공한 id 리스트와 실패한 id 리스트를 출력하도록 했어야 했다.
    }
  • 240429 update
    // 변경 전
      public ResponseEntity<Object> deleteAllByUser(@RequestBody Collection<Long> userIds) {
          articleService.deleteUserAll(userIds);
          return ResponseEntity.ok(ApiResponse.builder()
                          .status(true)
                          .message(RESPONSE_ARTICLE_DELETE_FOR_WITHDRAWAL).build()); 
      }
    // 변경 후
      public ResponseEntity<Object> deleteAllByUser(@RequestBody Collection<Long> userIds) {
          String failMessage = articleService.deleteUserAll(userIds);
          return ResponseEntity.ok(ApiResponse.builder()
                          .status(true)
                          .message(RESPONSE_ARTICLE_DELETE_FOR_WITHDRAWAL)
                          .result(failMessage).build()); 
      }

user 모듈 - service 코드

    @Scheduled(cron = "${withdrawal.cron}", zone = "Asia/Seoul")
    @Override
    public void userDeleteSchedule() {
        Set<User> rdbList = deleteListForRdb();
        Set<Long> userIds = deleteUserIdList(rdbList);
        Set<String> redisList = deleteListForRedis(rdbList);
        
        if (rdbList.size() > 0) {
            log.info("📆 Scheduler start - delete count = RDB: {}, userIds: {}, Redis: {} ----┐", rdbList.size(), userIds.size(), redisList.size());
            var response = articleClient.withdrawalArticleDelete(userIds);
            
            if (response.getBody().getStatus()) { // article 모듈에서 exception이 발생하면 실행되지 못하도록 구현
                userRepository.deleteAllInBatch(rdbList);
                redisTemplate.delete(redisList);
            }
            
            log.info("📆 Scheduler end--------------------------------------------------------┘");
        }
    }

아쉬움 2 보완(완)

gateway 서버에서 토큰 검증 후 요청 url에 대한 권한 확인을 진행했더라면

그렇게 구현했다면 시큐리티를 사용하지 않아도 구현이 가능했을 것으로 생각이 들었다.
처음 구현할 때는 시큐리티와 jwt 사용이 어려웠고 제대로 이해하지 못한 상태에서 구현을 시작했기 때문에 시큐리티를 통한 인증, 인가 작업이 꼭 있어야 한다고 생각되어 지금의 결과물이 나오게 되었다.
하지만 이제는 토큰과 redis에 사용자 정보를 일부분 저장한 후 gateway에서 접근하는 사용자의 토큰을 검증하여 필요에 따라 권한 확인 후 요청받은 경로로 라우팅해주었다면 서비스 로직 구현도 좀 더 명료했을 것이고 개발 일정도 줄어들었을 것이라는 생각이 들었다.
사용할 기술에 대해 제대로 파악하지 않았기 때문에 이런 아쉬움이 생긴것 같다.
좋은 시행착오라고 생각되었고 다음 프로젝트에서는 같은 아쉬움이 생기지 않도록 할 수 있을 것 같다.

24.04.29 이제는 시큐리티 학습 목적으로 좋은 프로젝트였다는 생각을 하게 되었다
다음엔 시큐리티 없이 같은 기능을 하는 프로젝트를 구현해봐야지 😀


아쉬움 3

모듈 네이밍 아쉬움

다음에 멀티 모듈 프로젝트를 시작하게 된다면 모듈 이름 앞에 module- prefix를 달아서 관리하면 좋을 것 같다.
이번 프로젝트에서는 모듈 이름을 a-jwt b-user c-article 이런식으로 작성해서 오히려 찾을때 버벅거리게 되었다.
분명 고민에 고민에 고민에 고민에 고민에 고민에 고민을 거쳐서 어렵게 지은 네이밍이었는데 😓
module- prefix를 붙여서 이름을 생성하면 알파벳 순서로 모듈끼리 모여있게 되어 좋을 것 같다는 생각을 하게 되었다


아쉬움 4 보완()

권한 설정

권한이 member, admin으로 2개인데 지금은 계정 당 1개의 권한만 가질 수 있도록 구현했다.
그런데 admin도 member니까 admin 계정은 member와 admin을 동시에 갖도록 한다면 더 좋았을 것 같다.