[ spring ] Service class는 어떤식으로 구성해야하는가

신범철·2023년 3월 19일
2

스프링부트

목록 보기
6/20

사전 지식

Spring Web Layer의 동작과정을 공부하면 좋습니다.

사진 설명 링크

문제 상황

현재 LoginService는

  • private final MemberRepository : MemberDB 접근 로직
  • method : 스크래핑 기능
  • method : 포맷팅 기능
  • method : DB저장 메소드 호출 기능

을 포함하고 있습니다.

이를 코드로 어떻게 짜야 확장성이 좋은 코드인지를 고민해보는 글입니다.

생각하는 방안

1. 모든 기능을 인터페이스로 선언 후 각자의 기능을 컨트롤러에서 호출해서 사용

  • loginServiceInterface.class 파일
public interface LoginService {
	public spring 스크래핑();
    public Form 포맷팅();
    public Member DB저장 메소드 호출();
}
  • loginServiceImpl.class 파일
public class LoginServiceImpl {
	private final MemberRepository memberRepository;

	@override
	public spring 스크래핑();
    @override
    public Form 포맷팅();
    @override
    public Member DB저장 메소드 호출();
}
  • loginController.class 파일
public class LoginController {
	private final LoginService loginService;

	@PostMapping("/login")
    public String login(){
    	loginService.스크래핑();
    	loginService.포맷팅();
    	loginService.DB저장 메소드 호출();
    }
}

2. 하나의 포괄된 서비스 메소드 안에 private한 메소드로 구현

  • loginServiceInterface.class 파일
public interface LoginService {
	public Member login();
}
  • loginServiceImpl.class 파일
public class LoginServiceImpl {
	private final MemberRepository memberRepository;
    
    @override
    public Member login(){
    	private spring 스크래핑();
        private Form 포맷팅();
        private Member DB저장 메소드 호출();
    }
    
    private spring 스크래핑(){
    	//로직
    }
    private Form 포맷팅(){
    	//로직
    }
    private Member DB저장 메소드 호출(){
    	//로직
    }
}
  • loginController.class 파일
public class LoginController {
	private final LoginService loginService;

	@PostMapping("/login")
    public String login(){
    	loginService.Login();
    }
}

결론

결론부터 말하면 2번 방식으로 구현하였습니다.
이유 : 로그인서비스에 변화가 필요할 때 로그인서비스 구현체만 건드리면 되기 때문이다. 만약 다른 로그인 서비스 로직을 만들 경우에 스크래핑, 포맷팅, DB저장에 맞게 커스터마이징 해야하는데 이를 전부 다 커스텀마이징 쉽지 않기 때문이다. 그렇기에 LoginService를 2번 방식으로 구현하여 확장성을 높이는 방식을 사용하였다.

++TMI
1번 방식 또한 절때 틀린 것은 아니다. 상황에 따라 다르게 쓰는 것이다.
예를 들어 게임 서비스를 구현한다고 생각했을 때 게임모드 서비스가 있다면 이는 1번 방식을 사용해야한다. 저격 기능, 라이플 기능 등을 사용할 수 있는데 이는 공격이라는 메소드를 오버라이딩하여 구현할 수 있을 것이다!!

profile
https://github.com/beombu

3개의 댓글

comment-user-thumbnail
2023년 3월 19일

저는 2번의 방법이 조금 더 좋아보입니다. 컨트롤러가 어떻게 로그인할지 서비스에 대한 인터페이스를 알고 있다는 점이 확장성 측면에서 좋지 못할 것 같아요. (로그인 하는 방법이 여러 가지로 늘어나게 되는 경우..)

답글 달기

저는 결론부터 1번 방식이 향후 유지 보수에 더 도움이 될 것이라 생각합니다.

하지만

  • 스크래핑 메소드
  • 포맷팅 메소드
  • Member DB접근 메소드

이 세가지 메소드들은 모두 관심사가 다른 기능들이기 때문에 각각의 기능들을 인터페이스로 선언 하고 각 인터페이스의 구현체들을 구현해 놓은 후 loginController에서 사용하는 방안이 향후에 메소드 방식이 변경되더라도 유연하게 확장 가능하도록 구성하는 편이 좋다고 생각합니다.

위의 말을 코드로 말씀드리자면

public class LoginController {
	private final 스크래핑인터페이스 스크래핑;
	private final 포맷팅인터페이스 포맷팅;
	private final MemberDBService memberDBservice;

	@PostMapping("/login")
    public String login(){
    	스크래핑.스크래핑();
    	포맷팅.포맷팅();
	memberDBservice.save( data );
    }
}
답글 달기
comment-user-thumbnail
2023년 4월 3일

저는 후자를 선택할 것 같아요.
로그인서비스에 변화가 필요할 때
로그인서비스 구현체만 건드리면 되니까 그렇게 생각했구요.

보면서 생각난게
여기서 졸업여건 관련된 다른 서비스들도
스크래핑/포맷팅/db저장 메서드 구성으로 결국 이뤄질것 같은데.
이 메서드 들을 로그인서비스말고도 다른서비스에도 쓸 수 있게끔
상위 인터페이스로 또 만들어둘 수 있을거같은데

그러면 db저장 리턴객체는 구현체마다 달라야하니까
자바 리플렉션을 써볼수 있겠네요.

답글 달기