JustDoEat

가벼운 마음으로 Spring Bean, DI(Dependency Injection)에 대해 알아보자 본문

카테고리 없음

가벼운 마음으로 Spring Bean, DI(Dependency Injection)에 대해 알아보자

kingmusung 2024. 2. 10. 02:16

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%9E%85%EB%AC%B8-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

 

[지금 무료] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의 - 인프런

스프링 입문자가 예제를 만들어가면서 스프링 웹 애플리케이션 개발 전반을 빠르게 학습할 수 있습니다., 스프링 학습 첫 길잡이! 개발 공부의 길을 잃지 않도록 도와드립니다. 📣 확인해주세

www.inflearn.com

 

김영한 선생님의 강의를 듣고 제가 제 방식대로 이해를 한 과정들을 적어보려고 합니다.

스프링에 대한 전반적인 과정을 빠르게 알려주셔서 깊이있는 내용은 다른 강의로 공부를 할때 더 적어보도록 하겠습니다!!

 

강의를 듣고 공부 한 내용을 바탕으로 적었으나 추후 문제가 될 시 삭제하도록 하겠습니다.

 

💉 Dependency Injection(DI) 란?

 

DI 란 의존성 주입으로,  객체간의 관계를 외부에서 주입하여 객체간의 결합도를 낮추는 행위이다.

 

말만 들었을때는 이해가 잘 되지 않는다.

 

설명을 하기 전

 

일단 테스트 코드를 작성을 한다고 가정을 해보자.

 

1.DB를 연동을 안한 관계로, Repository에 public static MAP<>을 사용하여 객체들을 저장하고있음.(그냥 MAP사용중)

2. Sercice, Repository, Controller 클레스 작성했음.

3.컴포넌트 의존관계 설정 전임.

 

Repository

public class MemoryMemberRepository implements MemberRepository {

   private static Map<Long, Member> store = new HashMap<>(); //DB를 직접 사용을 안하므로, 메모리 형태로 DB를 구현한거임
   
   //이하 생략, 아래는 레포에 저장 할 수 있도록 만든 메서드들 존재

 

Service

public class MemberService {

    MemberRepository repository = new MemoryMemberRepository();
    
    //생략된 내용으로는 회원가입, 회원조회, 중복확인 등 서비스에 관련된 메서드가 있습니다.

 

serviceTestCode(Service Test Code들 중에서 회원가입 부분의 메서드만 가지고 온 상태.)

class MemberServiceTest {

    MemberService service = new MemberService();
    MemoryMemberRepository repository = new MemoryMemberRepository();
    
      @Test
    void join() {
        //given
        Member member = new Member();
        member.setName("string");
        Member member1 = new Member();
        member1.setName("string1");
        //when
        Long member_id = service.join(member);
        Long member_id1 = service.join(member1);
        //then
        Optional<Member> result = service.findone(member_id);

        Assertions.assertEquals(member_id, result.get().getId());

    }

 

테스트 코드를 보면

MemoryMemberRepository repository = new MemoryMemberRepository();

 

Repository 객체를 새로 생성을 하고, join 메서드를 이용해 저장소에 객체들을 넣고있다.

 

강의를 끝가지 듣기전에는 일반적으로 생각을해도 Repository는 저장소인데 객체를 하나 더 만들어서 쓴다? 그럼 저장소가 두개인건데? 왜 그러지? 라는 의문을 가지고있었다.(Service도 마찬가지)

-> 이러한 문제점을 해결하기위해 의존성주입(DI)를 해줄 것 이다.

 


 

📠 컴포넌트 스캔과 자동의존관계.

 

위와같은 문제점들을 해결하기위해. 스프링이 시작이 될때 Controller, Service, Repository 이 3가지 클레스의 객체를 시작과 동시에 만들어서 공유해서 쓰도록 할 것이다.

Service, Repository의 객체를 필요할때마다 만들어서 쓰는게 아니라, 미리 만들어서 필요할때 클라우드(?) 느낌으로 쓸것이다.

 

컴포넌트 스캔

@Controller, @Service, @Repository 이 3가지 어노테이션을 클레스에 붙혀주면, 스프링이 자동으로 인식을 한 후 시작과 동시에 객체를생성, 의존관계를 지정을 해준다. 

 

의존관계란, Controller에 요청이 들어오면 관련 요청을 처리하기 위해 Service의 기능들을 사용 할 것이고, Service는 당연히 Repository를 참조하여, 저장되어있는 값들을 빼서 클라이언트에게 보여주던가, 수정, 삭제를 해줄것이다.

이처럼 이 3가지는 의존관게를 필연적으로 가질 수 밖에 없다.

 

먼저 3가지 어노테이션을 보고 @AutiWired에 대해 설명을 해보겠다

 

 

Controller

@Controller
//컨트롤러라는 어노테이션이 있으면, 스프링이 자동으로 아래 객체 생성 한 후 컨테이너에 박아놓고(요청때마다 객체를 만 들 수 없어서), 요청이 올때마다 거기 맞는걸 찾아줌
public class MemberController {
    private final MemberService service;

    @Autowired // 컨트롤러의 객체가 스프링빈에 등록시(service,repo또한 어노테이션을 붙혀서 이미 스프링빈에 등록이 되어있음)
    //생성자에 오토와이어드가 붙어있으면 스프잉빈에 있는 컨테이너에 있는 객체를 가져다 쓴다는거임
    //여개의 객체를 만들어서 사용하면 안되니까
    public MemberController(MemberService service){
    //오토와이어가 있으면 스프링빈에 있는 Memberservice 객체를 가져다 쓴다
        this.service = service;
    }

 

Service

@Service
public class MemberService {

    MemberRepository repository;

    @Autowired
    public MemberService(MemberRepository repository){

        this.repository = repository;
    }

 

Repository

@Repository
public class MemoryMemberRepository implements MemberRepository {

   private static Map<Long, Member> store = new HashMap<>(); //DB를 직접 사용을 안하므로, 메모리 형태로 DB를 구현한거임
   
   //이하 생략, 아래는 레포에 저장 할 수 있도록 만든 메서드들 존재

 

이런식으로 어노테이션들을 붙혀주면 되고, 


테스트 코드와 바뀐점을 보자면, 생성자를 통해 객체를 만드는 부분이 사라졌을것이다(Repository부분 제외).

@Controller
public class MemberController {
private final MemberService service;

@Autowired
public MemberController(MemberService service){
    this.service = service;}}

 

대신 코드가 이런꼴들로 바뀌었을텐데, 차이점을 보자면 생성자를 직접 만들지 않는다는것이다.

public MemberController(MemberService service)

 

이제부터 스프링을 시작하면 스프링이 자동으로 스프링 빈을 뒤지면서 MemberService타입의 객체가 발견을 하면 해당 객체를

(MemberService service)

 

매개변수 부분에 알아서 주입을 시켜 줄 것이다.(다른 부분도 마찬가지)

 

우와.. 이해가 되었다.. 최고입니다..

 

그럼이제 @Autowired의 역할이 무엇인지 알아보겠습니다.

 

 Repository 부분은 직접 생성자를 만들던데, 이부분은 추후 공부를 통해 더 알아봐야겠다.. 내 추측에는 map을 static으로 선언을 함으로써 참조가 가능하니까 그런거 같기도하고, 혹은 아직 DB를 연동을 안해서그런건지 혼동이온다

물론 영한쌤께서 전반적인것을 알려주시는 강의니까 일단은 받아들이고 공부하려고 한다

 

this.service =service 이렇게 되면 this.service는 service를 참조만 하는거지, this.service 값이 바뀐다고 service도 바뀌는거야 ?

->네, 맞습니다. this.service = service라는 코드는 생성자의 매개변수로 전달된 service 객체를 현재 클래스의 service 필드에 할당합니다. 이것은 단순히 참조를 복사하는 것이며, 따라서 두 변수는 같은 객체를 가리킵니다. 따라서 this.service의 값이 변경되면 service도 동일한 객체를 참조하므로 그 변경을 반영하게 됩니다. 이것이 자바에서의 참조에 의한 값 전달이라는 개념입니다.


@Autowired

위에서도 간접적으로 설명을 하였는데.

@Controller,@Service, @Repository 어노테이션이 있으면 해당클레스 객체를 스프링빈에 자동으로 등록을 시켜주는건 맞는데

public MemberController(MemberService service)

자동으로 매개변수에 꽂아주지는 않는다. 그래서! @Autowired를 붙히는것이다.


 

자동의존관계는 지금까지 설명 한 내용이고, 추가적으로 컴포넌트 스캔은, @Service,@Repository,@Controller 어노테이션을 자세하게 들여다보면 @Component라는 어노테이션이 있는데. 이걸 보고 컴포넌트 스켄을 해주고 자동의존관계를 설정해준다는 뜻이다.

 

하나의 객체를 이렇게 돌려쓰는 방식을 싱글톤 방식이라고 한다

 


📠 컴포넌트 스캔과 자동의존관계2

@Configuration
public class SpringConfig {

    @Bean
    public MemberService MemberService(){

        return new MemberService(MemoryMemberRepository());
    }

    @Bean
    public MemoryMemberRepository MemoryMemberRepository(){

        return new MemoryMemberRepository();
    }
}

 

다른 방식으로는 @Service, @Repository를 안쓰고

@Configuration 어노테이션을 사용하여 의존관계를 설정할수있다.(@Controller, @Autowired는 사용해야함)