종우의 삶 (전체 공개)

Code refactoring - 1 본문

개발/Spring

Code refactoring - 1

jonggae 2023. 12. 31. 14:53

 

대상 repository : https://github.com/Jonggae/group-invitation ( Readme 참고 )

 

목표 : 가독성 향상, 부족한 기능 완성, 테스트코드 작성, 완성도 있는 코드 

 


 

1. @Setter anotation에 대한 내용

 

@Setter의 사용은 자제하는 것이 좋다. 

-> 자주 들은 내용이었는데 기능 구현을 더 신경쓰다보니 생각없이 @Setter를 박아넣고 시작.

리팩토링하여 좀더 안정성 있는 코드를 만들어보자. ->객체지향적인 내용

of, from toEntity등으로 변경하였음.

 

Entity 도메인에 setter를 사용하는 대신 생성자와 of를 이용하여 객체를 생성할 수 있도록 함.

 

@Entity
@Getter
@Setter
public class Member {
    @Id
    @GeneratedValue
    private long id;

    private String name; //초대받을 사용자 이름
    private String phoneNumber; //초대받을 사용자의 전화번호
    private String email; //초대받을 사용자의 이메일 주소
    
    {~생략~}
    }

 

이러한 코드의 형태에서 

package com.exam.invitation.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class Member {
    @Id
    @GeneratedValue
    private long id;

    private String name; //초대받을 사용자 이름
    private String phoneNumber; //초대받을 사용자의 전화번호
    private String email; //초대받을 사용자의 이메일 주소

//    private final boolean activated = Boolean.FALSE; //임시 회원이므로 활성화 태그를 만들어놓고 활성화 시키지 않음.

    private Member(String name, String phoneNumber, String email) {
        this.name = name;
        this.phoneNumber = phoneNumber;
        this.email = email;

    }

    public static Member of(String name, String phoneNumber, String email) {
        return new Member(name, phoneNumber, email);
    }


}

 

private로 선언된 생성자와 of를 사용한 객체 생성방법을 이용한 것으로 바꾸었다.

 

-> 이러한 방법의 의도는? (GPT쌤이 설명해줌)

private Member(String name ~~~) { ~ }

는 생성자이다. 생성자는 객체가 생성될 때 호출되는 특별한 메서드로 객체의 초기화를 담당한다. 

외부에서 직접 호출되지 않도록 private로 선언하고 외부에서는 'new Member(~) 로 객체를 생성할 수 있다.

 

여기서 of 메서드를 함께 사용하여 객체 생성시에 필요한 로직을 설정한다.

of 메서드는 Member 클래스의 객체를 생성하고 반환한다. 여러 생성 로직을 캡슐화 할 수 있는 장점이 있다.

(팩토리 메서드라고 하는데...!? 이것은 또 뭘까)

 

이렇게 객체를 생성할 때 필요한 부분들은 전부 변경해주었다.

 

 

사실 확실한 이해를 아직 못하겠으므로 다른 코드들을 많이 바꾸거나 해보자.

 


2. 메서드 분리

 

package com.exam.invitation.service;

import com.exam.invitation.domain.Member;
import com.exam.invitation.dto.MemberDto;
import com.exam.invitation.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository tempMemberRepository;

    // 초대 링크 생성과 함께 임시 멤버 생성해야함
    public Member createTempMember(MemberDto tempMemberDto) {
        Member tempMember = tempMemberDto.toTempMember();

        if (tempMemberRepository.findByName(tempMember.getName()).isPresent()
                || tempMemberRepository.findByEmail(tempMember.getEmail()).isPresent()) {
            throw new IllegalArgumentException("같은 이름, 이메일이 존재합니다");
        }
        tempMemberRepository.save(tempMember);
        // 임시 멤버 생성 완료
        return tempMember;
    }


}

 

위 코드의 의도는

1. 초대 링크를 생성한다 (uuid를 포함한 무작위 도메인)

2. 초대 링크를 보낼 임시 멤버를 추가한다.

    2- 1. #2에서 멤버의 이름, 이메일을 체크한 뒤에

    2- 2. 임시 멤버를 db에 추가 한다.

 

이러한 코드였는데 createTempMember 메서드에 모든 것을 넣어놨었다.

명확한 이름을 만들기 위해 우선 TempMember라는 명칭을 전부 Member로 변경하였다.

-> 말이 임시멤버지 그냥 멤버를 추가하는 것과 다름 없었다. (과제 설명이 약간 모호함)

 

이후 한눈에 알아보기 힘든 메서드의 로직들을 분리하였다.

 

package com.exam.invitation.service;

import com.exam.invitation.dto.MemberDto;
import com.exam.invitation.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;
    private final InvitationLinkService invitationLinkService;

    // 초대 링크 생성과 함께 임시 멤버 생성해야함
    public void createMember(MemberDto memberDto) {
        generateInvitationLink();
        checkNameAndEmail(memberDto);
        saveMember(memberDto);
    }

    private void generateInvitationLink() {
        invitationLinkService.generateInvitationLink();
    }

    private void saveMember(MemberDto memberDto) {
        memberRepository.save(memberDto.toEntity());
    }

    private void checkNameAndEmail(MemberDto memberDto) {
        if (memberRepository.findByName(memberDto.toEntity().getName()).isPresent()) {
            throw new IllegalArgumentException("같은 이름이 이미 존재합니다");
        }
        if (memberRepository.findByEmail(memberDto.toEntity().getEmail()).isPresent()) {
            throw new IllegalArgumentException("같은 이메일이 이미 존재합니다");
        }
    }
}

 

너무 많은 분리를 진행한것 같은데, 마지막의 중복된 데이터를 검색하는 메서드(checkNameAndEmail)만 분리하였어도 괜찮았을 하다. 우선은 이대로 마무리 하였다.

 


 

 

'개발 > Spring' 카테고리의 다른 글

Spring security - 4  (0) 2024.01.17
Spring security - 3 // Security Config  (0) 2024.01.15
Spring security - 2  (0) 2024.01.14
Spring Security - 1  (0) 2024.01.14
Code refactoring - 2  (1) 2024.01.10
Comments