스프링 공부/게시판 프로젝트 만들기

[스프링] 33. 잠깐 쉬면서 만들었던 것들을 리펙토링 한 과정을 요약해보자.

장아장 2023. 2. 16. 22:57

일단, 블로그 포스팅을 잠깐 쉬면서, 코드를 다시 한번 돌아보았다. 

좌충우돌 만드는 게시판 수준이라지만, 그래도 나름 '생각' 이라는 것을 해보면서 쓰고있다. 

+ 플레이리스트에 아이묭(aimyon) 노래를 틀면서 하기 시작했는데, 기존에 듣던 재즈보다 힘차게 하는 느낌이다(TMI)

 

기존에, 회원가입 로직을 위해서 

Member -> MemberRepository(+MemberCustomRepository) -> MemberService -> MemberController

RefreshToken -> RefreshTokenRepository -> RefreshTokenServie -> RefreshTokenController

로 만들려던 계획을 조금 수정했다. 

 

DDD라는 것을 조금 찾아보면서, Member, Refresh라는 도메인이 같이 움직일 때는 하나로 움직이고, 따로 움직일 건 따로 움직이는 식으로, 나눠 놓았으면 나중에 편하게 합쳐쓰면 되는데 그러지 못한 느낌이 들었기 때문이다.

(아직 공부중이고, 머리속으로 정립되지 않은 개념이라 자세하게 쓰고 있지는 않다.)

 

그래서 결과는, 

MemberService  = Member 도메인, AuthService = Member 도메인 + RefreshToken 도메인 의 방식으로 합치게 되었다. 

결국, auth의 기능은, 회원가입 + 로그인 + 토큰 재발행 정도가 될 것 같다. 

이러한 방식으로 서비스를 리펙토링했다. 

 

이렇게 만들면서 고민이 생겼었다.

auth = 회원가입 중에 아이디, 닉네임, 이메일, 비밀번호를 검증해야 한다.

member = 회원의 데이터 수정 중에 닉네임, 이메일, 비밀번호를 검증해야 한다.

이 두개가 공통적인 부분을 가지게 되었다.

그래서 간단하게 멤버의 인스턴스를 검증하는 클래스를 만들어주었다.

public void validateRegisterRequest(RegisterRequestDto registerRequestDto){
    validateUsername(registerRequestDto.getUsername());
    validateNickname(registerRequestDto.getNickname());
    validateEmail(registerRequestDto.getEmail());
    validatePasswordCheck(registerRequestDto.getPassword(), registerRequestDto.getPasswordCheck());
}

public void validateSignInRequest(SignInRequestDto signInRequestDto){
    Member member = memberRepository.findByUsername(signInRequestDto.getUsername())
            .orElseThrow(MemberNotFoundException::new);
    if(!passwordEncoder.matches(signInRequestDto.getPassword(), member.getPassword()))
        throw new PasswordNotMatchingException();
}

public void validateUsername(String username){
    if(memberRepository.findByUsername(username).isPresent())
        throw new UsernameAlreadyInUseException();
}

public void validateNickname(String nickname){
    if(memberRepository.findByNickname(nickname).isPresent())
        throw new NicknameAlreadyInUseException();
}

public void validateEmail(String email){
    if(memberRepository.findByEmail(email).isPresent())
        throw new EmailAlreadyInUseException();
}

public void validatePasswordCheck(String password, String passwordCheck){
    if(!password.equals(passwordCheck)){
        throw new PasswordNotMatchingException();
    }
}

public void validateWithCurrentPassword(ChangePasswordRequestDto changePasswordRequestDto, String currentPassword){
    if(passwordEncoder.matches(changePasswordRequestDto.getNewPassword(), currentPassword))
        throw new PasswordNotChangedException();
}

기존에 있던 아이디, 닉네임, 이메일, 비밀번호 검증 로직을 그대로 가져왔다. 

추가로, 회원가입 때 필요한 것들끼리 모아서 회원가입 요청 검증을 만들었다.

 

이후, 

AuthService, MemberService 모두

@PostConstruct
private void setValidator(){
    memberInstanceValidator = new MemberInstanceValidator(memberRepository, passwordEncoder);
}

이런 메서드를 만들어주었다. 

이를 통해, Validator의 인스턴스를 초기화 시켜줄 수 있다.

 

AuthService의 내부 로직은

@Transactional
public void registerNewMember(RegisterRequestDto registerRequestDto){
    memberInstanceValidator.validateRegisterRequest(registerRequestDto);
    registerRequestDto.setPassword(passwordEncoder.encode(registerRequestDto.getPassword()));
    Member member = new Member(registerRequestDto);
    memberRepository.save(member);
}

@Transactional
public TokenResponseDto signIn(SignInRequestDto signInRequestDto){
    Authentication authentication = getAuthenticationToSignIn(signInRequestDto);
    return createTokenDtoByAuthentication(authentication);
}

@Transactional
public TokenResponseDto reissue(ReissueRequestDto req) {
    validateRefreshToken(req);
    RefreshToken refreshToken = refreshTokenRepository.findByKey(getAuthentication(req).getName())
            .orElseThrow(LogOutMemberException::new);
    validateTokenInfo(refreshToken, req);
    TokenDto tokenDto = tokenProvider.generateTokenDto(getAuthentication(req));
    refreshToken.updateValue(tokenDto.getRefreshToken());
    return new TokenResponseDto(tokenDto);
}

이렇게 회원가입, 로그인, 토큰 재발행을 넣었다.

(부수적인 메서드들은 기존 게시글 중TokenDto, MemberService, JWT부분들을 참고하면 될 것 같다. )

 

이에 따라 테스트도 수정해주었다(이걸 다시 다 해보는게, 귀찮을 수 있지만 개인적으로는 노가다같더라도 해보는걸 추천한다. 나도 아직 테스트를 잘 한다곤 못하지만, 계속 쓰다보니, 다양한 기능들을 클래스를 하나씩 뜯어보며 쓸 수 있게 된다는 느낌이 든다.)

 

서비스 로직을 그대로 가지는 컨트롤러 로직도 만들고있다. 

AuthController의 register, sign_in, reissue 순서로 게시글을 분리해서 올려야 할 것 같다. 

컨트롤러의 로직 자체는 만드는 데에 문제가 없었지만, 테스트를 만들어보고, 테스트에 통과되게 문제를 하나씩 고치는 과정이 재미있다. 

(그니까 테스트 만들자. 여러분들 테스트는 최고존엄이신거시다. )