Spring web의 DI와 IoC

18957 단어 SpringSpring

Spring을 사용하지 않은 Java Web Project

control과 view는 생각하지 않고 Model service 로직만 생각하여 예시를 들어보겠습니다.

  • 회원가입
    1. Model (class) 모델 객체를 만듭니다.
    2. Service (Interface) 인터페이스를 만들고
    3. ServiceImpl (class) 구현체를 만듭니다.
    4. code를 테스트 해봅니다.
  • Model 객체 생성
public class Member {

    private Long id;
    private String name;

    public Member(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • Service Interface 생성
public interface MemberService {
    void join(Member member);
    Member findMember(Long memberId);
}
public interface MemberRepository { 
    void save(Member member);
    Member findById(Long memberId);
}
  • ServiceImpl 구현체 생성
public class MemoryMemberRepository implements MemberRepository {

    private static Map<Long, Member> store = new HashMap<>();

    @Override
    public void save(Member member) {
        store.put(member.getId(), member);
    }

    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();

    public void join(Member member) {
        memberRepository.save(member);
    }

    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}
  • 테스트 코드
class MemberServiceTest { 

    MemberService memberService = new MemberServiceImpl();

    @Test
    void join() {
        //given
        Member member = new Member(1L, "memberA");
        //when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);
        //then
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

위 예시의 코드들은 순수 java만으로 web의 model service 로직을 구현했습니다. 보기에는 Model 객체를 생성하고 Service로직 또한 인터페이스를 사용하여 나누었고 구현체는 해당 인터페이스를 상속받아서 서비스 로직을 구현했습니다. 웹 로직상 문제도 없고 테스트 또한 정상적으로 아이디를 만들고 find까지 실행이 됩니다. 하지만 spring[1]의 spring과 객체지향을 공부하셨다면 위 프로젝트의 구조가 잘못되었다는 걸 알수 있습니다.

그렇다면 위 프로젝트에서 지키지 못한 객체지향은 무엇일까요?

  1. 만약 다른 DB에 저장하기 위해서 OCP의 원칙을 지키면서 DB설정 내용을 변경할 수 있을까요?
  2. 프로젝트 service 로직 자체가 DIP를 잘 지키고 있을까요?

만약 구현 코드가 다른 구현코드로 변경이 생기게된다면

private final MemberRepository memberRepository = new MemoryMemberRepository();

코드가 직접 수정되어야하므로 구현체 내의 수정이 생기게 됩니다. 이는 OCP를 위반하게 됩니다. 또한 우선 DIP에 대해서는 지켜지지 않고 있습니다. 구현체는 인터페이스에만 의존해야하는데 지금 코드를 보시면 구현체에서 직접 다른 구현코드까지 의존하여 가져오게되죠. 이 문제를 spring을 아직 사용할수 없는 우리가 해결할 방법이 없을까요?

이 문제를 해결하기 위해 AppConfig class를 생성하여 구현체를 연결하는 책임을 가지는 별도의 class를 만들어서 해결해보겠습니다.

  • AppConfig.class
public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }
}
  • Service 구현체 수정
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void join(Member member) {
        memberRepository.save(member);
    }

    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

이렇게 코드가 만들어지면 MemberServiceImpl은 더이상 다른 구현 코드를 의존하지 않고 외부에서 주입되는 구현코드를 받아서 사용하게 되었습니다. 이제 test code를 통해 동작 원리를 정확히 보겠습니다.

  • test code
class MemberServiceTest { MemberService memberService;
    @BeforeEach
    public void beforeEach() {
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }
}

테스트 코드에서 AppConfig의 class에 구현되어진 memberService()를 통해 return 받은 memberService 구현 코드를 받아서 이후 테스트를 진행하시면 됩니다.

AppConfig를 통해 웹 프로젝트를 좀 더 객체지향적으로 구성할 수 있게 되었습니다. 우리는 우리도 모르는 사이에 AppConfig를 통해 IoC와 DI의 개념을 사용했는데요. 개념에 대해서 알아보겠습니다.

IoC (Inversion of Control) 제어의 역전

Ioc란 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말합니다. 구현체에서 직접 구현 코드를 제어하지 않고 AppConfig를 통해 외부 제어를 하는 것을 IoC라고 합니다.

DI (Dependency Injection) 의존관계 주입

의존관계 주입이란 실행 시점(RunTime)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달하여 클라이언트와 서버의 실제 의존관계가 연결되는 것을 의존관계 주입이라고 합니다.

DI 컨테이너

우리가 지금까지 사용한 AppConfig와 같이 의존관계를 외부에서 생성하고 관리하는 class를 IoC컨테이너 또는 DI 컨테이너라고 합니다.

다음 시간에는 현재 프로젝트를 Spring 프로젝트로 전환시켜가보겠습니다!

감사합니다~!

출처 https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

좋은 웹페이지 즐겨찾기