Chef.Yeon
Code Cook
Chef.Yeon
전체 방문자
오늘
어제
  • 분류 전체보기 (230)
    • 게임 개발 (1)
      • Unity (1)
    • Android (27)
      • Kotlin (19)
      • 우아한테크코스 5기 (4)
    • Language (11)
      • 파이썬 (3)
      • Java (7)
    • DB (2)
      • SQL (16)
    • Spring (25)
    • 코딩테스트 (56)
    • Git (1)
    • TIL (85)
    • DevOps (6)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • spring
  • elasticsearch
  • enum
  • 파이썬
  • SQL
  • 내림차순
  • Wil
  • MariaDB
  • 안드로이드
  • Android
  • Docker
  • ec2
  • 프리코스
  • 백준
  • kotlin
  • webflux
  • java
  • 코딩테스트
  • til
  • 프로그래머스
  • 우아한테크코스
  • rsocket
  • kibana
  • 다이나믹 프로그래밍
  • 문자열
  • 레포지토리
  • 코틀린 인 액션
  • grafana
  • 에라토스테네스의 체
  • 코틀린

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Chef.Yeon

Code Cook

[TIL - 20230510] Service 유효성 검사 메서드 분리
TIL

[TIL - 20230510] Service 유효성 검사 메서드 분리

2023. 5. 11. 03:45

 

💻문제점

MemberService, PostService, CommentService, LikeService 클래스마다 클래스 내부에서 사용하는 유효성 검사 메서드가 존재한다. 각 클래스에서 사용하는 유효성 검사 메서드는 다음과 같다.

 

MemberService 클래스

// ==== 유효성 검사 ====
    private void tokenNullCheck(String token) {
        if (token == null) {
            throw new NullPointerException(ExceptionMessage.HAS_NO_TOKEN.getMessage());
        }
    }

    private void tokenValidateCheck(String token) {
        if (!jwtUtil.validateToken(token)) {
            throw new IllegalArgumentException(ExceptionMessage.EXPIRED_TOKEN.getMessage());
        }
    }

    private Member findMemberByToken(String token) {
        String userInfo = jwtUtil.getUserInfoFromToken(token);
        return memberRepository.findByEmail(userInfo).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.EXPIRED_TOKEN.getMessage())
        );
    }

    private void validateMemberByEmail(String email) {
        memberRepository.findByEmail(email).ifPresent(member -> {
            throw new IllegalArgumentException(ExceptionMessage.DUPLICATED_MEMBER.getMessage());
        });
    }

    private void validateMemberByNickname(String nickname) {
        memberRepository.findByNickname(nickname).ifPresent(member -> {
            throw new IllegalArgumentException(ExceptionMessage.DUPLICATED_NICKNAME.getMessage());
        });
    }

    private Member validateMember(String email) {
        return memberRepository.findByEmail(email).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_MEMBER.getMessage())
        );
    }

    private void validatePassword(String password, Member member) {
        if (!passwordEncoder.matches(password, member.getPassword())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_MATCH_PASSWORD.getMessage());
        }
    }

 

PostService 클래스

    // ==== 유효성 검증 ====
    private void validatePostAuthor(Member member, Post post) {
        if (!member.getNickname().equals(post.getNickname())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_AUTHORIZATION.getMessage());
        }
    }

    private Post ValidateExistPost(Long id) {
        return postRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_POST.getMessage())
        );
    }

 

CommentService 클래스

// ==== 유효성 검증 ====

    private void validateCommentAuthor(Member member, Comment comment) {
        if (!member.getId().equals(comment.getMember().getId())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_AUTHORIZATION.getMessage());
        }
    }

    private Post ValidateExistPost(Long id) {
        return postRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_POST.getMessage())
        );
    }

    private Comment ValidateExistComment(Long id) {
        return commentRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_COMMENT.getMessage())
        );
    }

 

LikeService 클래스

    // 게시글 여부확인
    private Post validatePost(Long id) {
        return postRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_POST.getMessage())
        );
    }

 

메서드들을 보면, CommentService에서 PostService에 있는 유효성 검사 메서드와 같은 로직을 또 다시 사용한다. LikeService에서도 게시글 존재 여부에 대한 유효성 검사 메서드를 만들어 사용한다.

 

이런 식으로 중복되는 유효성 검사 메서드들을 효율적으로 처리할 수 있는 방법을 고민했다.


📃시도

MemberValidator, PostValidator와 같은 공통 모듈 클래스를 만들어서 해당 클래스 안에 유효성 검사 메서드를 작성하고, 전역적으로 사용하고자 하였다. 싱글톤 패턴을 적용하여 작성하려 했으나, 각 메서드 안에 있는 MemberRepository나 PostRepository가 필요했다. 단순한 java 클래스이기 때문에 @Autowired는 당연히 동작하지 않는다.


🔍해결

클래스에 @Component 애노테이션을 사용하여 빈 객체로 관리하도록 했다. 빈 객체는 기본적으로 싱글톤으로 생성되고 관리되기 때문에 의존성 주입을 통해 어디서든 사용할 수 있었고, 빈 객체이기 때문에 MemberRepository 와 같은 클래스를 @Autowired나 lombok의 @RequiredArgsConstructor를 통해 주입 받을 수 있었다.

 

이로써 CommentService나 PostService에서 게시글 존재 여부를 검사한다고 해도, 공통적으로 PostValidator의 검증 메서드를 사용할 수 있다.

 

MemberValidator 클래스

@Component
@RequiredArgsConstructor
public class MemberValidator {
    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    public void validateMemberByEmail(String email) {
        memberRepository.findByEmail(email).ifPresent(member -> {
            throw new IllegalArgumentException(ExceptionMessage.DUPLICATED_MEMBER.getMessage());
        });
    }

    public void validateMemberByNickname(String nickname) {
        memberRepository.findByNickname(nickname).ifPresent(member -> {
            throw new IllegalArgumentException(ExceptionMessage.DUPLICATED_NICKNAME.getMessage());
        });
    }

    public Member validateMember(String email) {
        return memberRepository.findByEmail(email).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_MEMBER.getMessage())
        );
    }

    public void validatePassword(String password, Member member) {
        if (!passwordEncoder.matches(password, member.getPassword())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_MATCH_PASSWORD.getMessage());
        }
    }
}

 

TokenValidator 클래스

@Component
@RequiredArgsConstructor
public class TokenValidator {
    private final JwtUtil jwtUtil;
    private final MemberRepository memberRepository;

    public void tokenNullCheck(String token) {
        if (token == null) {
            throw new NullPointerException(ExceptionMessage.HAS_NO_TOKEN.getMessage());
        }
    }

    public void tokenValidateCheck(String token) {
        if (!jwtUtil.validateToken(token)) {
            throw new IllegalArgumentException(ExceptionMessage.EXPIRED_TOKEN.getMessage());
        }
    }

    public Member findMemberByToken(String token) {
        String userInfo = jwtUtil.getUserInfoFromToken(token);
        return memberRepository.findByEmail(userInfo).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.EXPIRED_TOKEN.getMessage())
        );
    }
}

 

PostValidator 클래스

@Component
@RequiredArgsConstructor
public class PostValidator {
    private final PostRepository postRepository;

    public void validatePostAuthor(Member member, Post post) {
        if (!member.getNickname().equals(post.getNickname())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_AUTHORIZATION.getMessage());
        }
    }

    public Post validateExistPost(Long id) {
        return postRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_POST.getMessage())
        );
    }
}

 

CommentValidator 클래스

@Component
@RequiredArgsConstructor
public class CommentValidator {
    private final CommentRepository commentRepository;

    public void validateCommentAuthor(Member member, Comment comment) {
        if (!member.getId().equals(comment.getMember().getId())) {
            throw new IllegalArgumentException(ExceptionMessage.NO_AUTHORIZATION.getMessage());
        }
    }

    public Comment validateExistComment(Long id) {
        return commentRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException(ExceptionMessage.NO_EXIST_COMMENT.getMessage())
        );
    }
}
728x90

'TIL' 카테고리의 다른 글

[TIL - 20230515] @ReqeustParam enum 타입 받기  (0) 2023.05.15
[TIL - 20230513] 게시글 수정(썸네일 변경 포함)을 위한 MutipartFile과 Dto 함께 받기  (0) 2023.05.14
[TIL - 20230506] 회원/비회원 전체 게시글 조회 처리  (0) 2023.05.06
[TIL - 202030504] WebSocket+Stomp 채팅방에 직접 참여하지 않은 사용자 구독시키기2 + 자잘한 문제점 해결  (0) 2023.05.04
[TIL - 20230503] WebSocket+Stomp 채팅방에 직접 참여하지 않은 사용자 구독시키기1  (0) 2023.05.04
    'TIL' 카테고리의 다른 글
    • [TIL - 20230515] @ReqeustParam enum 타입 받기
    • [TIL - 20230513] 게시글 수정(썸네일 변경 포함)을 위한 MutipartFile과 Dto 함께 받기
    • [TIL - 20230506] 회원/비회원 전체 게시글 조회 처리
    • [TIL - 202030504] WebSocket+Stomp 채팅방에 직접 참여하지 않은 사용자 구독시키기2 + 자잘한 문제점 해결
    Chef.Yeon
    Chef.Yeon
    보기 좋고 깔끔한 코드를 요리하기 위해 노력하고 있습니다.

    티스토리툴바