[SpringBoot] 회원가입 구조 퍼사드 패턴 적용과 SRP 원칙 적용

회원가입을 하기 위해서  3가지 흐름이 있다. 

 

회원가입 로직

1. 회원가입시 입력한 인증번호 확인

2. member 테이블에 회원정보 저장

3. account 테이블에 회원 인증정보 저장

 

위 로직 설명

account 테이블이 따로 있는 이유는 회원 종류가 늘어날때 회원 종류의 테이블을 모두 돌아야 해서 회원 종류의 확장을 고려해 account 인증 테이블을 추가하였다.

member테이블에 먼저 저장하는 이유는 member 테이블에 있는 전화번호 등 유효성검사를 하고 member 엔티티가 저장되고 생성된 id 값을 account 테이블에 넣기 위해서이다.

 

[기존 코드]

기존코드에서는 아래 코드처럼 Service 하나에서 위 로직을 모두 넣었다. 

@Transactional
override fun signUp(createMemberDTO: CreateMemberRequest): Long {
    //인증번호 확인
    val certifyNumFoundByRedis = redisUtil.getData(email)

    if (certifyNumFoundByRedis != certifyNum) {
        throw SignupCertifyNumAuthFailedException(ErrorCode.INVALID_AUTH_CERTIFY_NUM)
    }

    //인증정보 저장
    saveAccountValidCheck(createMemberDTO)
    
    val account = Account(
        email = createMemberDTO.email,
        password = passwordEncoder.encode(createMemberDTO.password),
        role = MemberRole.USER,
        userId = memberId,
    )

    accountRepository.save(account)
    
    //회원정보 저장
    signUpValidCheck(createMemberDTO)

    val member = Member(
        telNo = createMemberDTO.telNo,
        name = createMemberDTO.name,
        memberShoppingActivity = MemberShoppingActivity.init(),
    )

    memberRepository.save(member)

    return member.memberId!!
}

 

위 코드에서의 문제는 signUp() 하나의 메서드 안에서 인증번호 확인, 인증정보 저장, 회원정보 저장 3개의 기능이 분리 되지 않고 같이 있기 때문에 SOLID 원칙에서 SRP 규칙에 어긋난다.

그래서 아래처럼 Controller와 Service 가운데에 ServiceApplication 클래스를 한개둬서 3개의 기능을 분리 했다.

 SRP를 지키지 않은 경우에 코드 크기가 커지면서 연관성이 적은 코드가 한 클래스에 위치할 가능성이 높아진다. 이는 결과적으로 코드를 이해하는데 방해가 된다.  코드가 분리돼있지 않으면 인증번호 확인 로직만 수정하고 싶을때 인증정보 저장, 회원정보 저장 로직도 같이 볼수 밖에 없기 때문이다. 이것은 코드를 점점 얽히게 만들어서 유지보수와 확장성에 좋지 않다.

 

 

하나의 서비스에서 여러개의 서비스를 나눴을때 여러개의 서비스를 실행시켜주는 매개체 역할이 필요한데 그것이 위에서 MemberServiceApplication이다. 이것이 퍼사드와 같은 역할을 해줘서 퍼사드 패턴이 될수있다.

퍼사드 패턴이란 여러 요소들로 구성된 복잡한 시스템을 서브시스템으로 분리하고 하나의 시스템(퍼사드 객체) 에서 처리할수 있게 해주는 패턴이다. 퍼사드 패턴은 복잡한 시스템을 분리시켜서 유지보수와 확장성에 용이하기 해주게 때문에 위 문제를 해결하기 위해서 가장 적합한 디자인 패턴이다.

 

각각의 기능들을 서브시스템으로 나누기 위해 인증번호를 확인하는 AuthenticationService, 인증정보를 저장하는 AccountService, 회원정보를 저장하는 MemberService를 따로 따로 분리 하였다. 이 3개의 Service를 나누면 각각의 서브시스템으로 나뉘어 진것이다.

 

[AuthenticationService]

fun verifyEmailCertifyNum(email: String, certifyNum: String) {
    val certifyNumFoundByRedis = redisUtil.getData(email)

    if (certifyNumFoundByRedis != certifyNum) {
        throw SignupCertifyNumAuthFailedException(ErrorCode.INVALID_AUTH_CERTIFY_NUM)
    }
}

 

[AccountService]

@Transactional
override fun saveAccount(memberId: Long, createMemberDTO: CreateMemberRequest): Account {
    saveAccountValidCheck(createMemberDTO)

    val account = Account(
        email = createMemberDTO.email,
        password = passwordEncoder.encode(createMemberDTO.password),
        role = MemberRole.USER,
        userId = memberId,
    )

    return accountRepository.save(account)
}

 

 

[MemberService]

@Transactional
override fun saveMember(createMemberDTO: CreateMemberRequest): Long {
    signUpValidCheck(createMemberDTO)

    val member = Member(
        telNo = createMemberDTO.telNo,
        name = createMemberDTO.name,
        memberShoppingActivity = MemberShoppingActivity.init(),
    )

    memberRepository.save(member)

    return member.memberId!!
}

 

그리고 퍼사드 객체 역할을 해주는 MemberServiceApplication 클래스를 만들고 에 위 3개의 Service를 의존하고 각각의 기능을 적용시켰다. 그러면 복잡한 서브 시스템들을 하나의 객체에서 처리할수 있고 이것이 퍼사드 패턴이다.

 

[MemberServiceApplication]

class MemberServiceApplication(
    private val memberService: MemberService,
    private val accountService: AccountService,
    private val authenticationService: AuthenticationService,
) {
    @Transactional
    fun signUp(createMemberRequest: CreateMemberRequest): Long {
        //인증번호 확인
        authenticationService.verifyEmailCertifyNum(createMemberRequest.email, createMemberRequest.certifyNum)

        val memberId = memberService.saveMember(createMemberRequest)
        accountService.saveAccount(memberId, createMemberRequest)

        return memberId
    }
}

 

 

참고 : https://velog.io/@bagt/Design-Pattern-Facade-Pattern-%ED%8D%BC%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4

 

 

댓글

Designed by JB FACTORY