[Spring] IOC/DI 개념

WOOK JONG KIM·2022년 10월 21일
0

패캠_java&Spring

목록 보기
39/103
post-thumbnail

IOC(Inversion of Control)

스프링에서는 일반적인 JAVA 객체를 new로 생성하여 개발자가 관리하는 것이 아닌 Spring Container에 모두 맡긴다
-> 개발자에서 프레임워크로 제어의 객체 관리의 권한이 넘어갔음으로 제어의 역전이라고 한다

ex)
클래스와 서비스 간에 데이터를 주고받을 때 Spring Container에 생성하고자 하는 객체들이 이미 만들어져 들어가있고 Singleton 형태로 관리가 된다.

-> 컨테이너가 모든 객체를 관리(ex: 생명 주기)

DI(Dependency Injection)

내가 사용할 객체를 주입받는 다는 개념

ex) 웹으로부터 내가 사용할 객체를 주입받는다 가정
주입을 해주는 주체 -> Container(IOC)

장점

  • 의존성으로 부터 격리시켜 코드 테스트에 용이

  • DI를 통하여, 불가능한 상황을 MOCK(가짜 객체)와 같은 기술을 통하여, 안정적으로 테스트 가능

  • 코드를 확장하거나 변경 할 때 영향을 최소화 한다(추상화)

  • 순환 참조를 막을 수 있다

A객체가 B 객체에 의존하면 우선 코드로 부터 격리 -> B객체가 꼭 없더라도 A객체 테스트 가능

스프링 컨테이너

스프링 컨테이너는 스프링에서 자바 객체들을 관리하는 공간
자바 객체를 스프링에선 빈(Bean)이라고 하는데, 스프링 컨테이너에서는 이 빈의 생성부터 소멸까지를 개발자 대신 관리 해 줌

인코딩(encoding)

파일에 저장된 정보의 형태를 다른 것으로 변경하는 것

사람이 인지할 수 있는 형태의 데이터를 약속된 규칙에 의하여 컴퓨터가 사용하는 0과 1로 변환하는 과정을 통틀어 말하며 파일 압축이나 암호화 등의 목적으로 인코딩

DI이해 예시

public interface IEncoder {
    String encode(String message);
}
import java.util.Base64;

public class Base64Encoder implements IEncoder {

    public String encode(String message){
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class UrlEncoder implements IEncoder {

    public String encode(String message){
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}
public class Encoder implements IEncoder{

    // 의존을 가지고 있는 애를 외부에서 주입을 받기
    private IEncoder iEncoder;

    //default로 base64
    // DI(외부에서 사용하는 객체 주입해주는 것) 적용 전
	//    public Encoder(){
	//        this.iEncoder = new Base64Encoder();
	//    }

    public Encoder(IEncoder iEncoder){
        this.iEncoder = iEncoder;
    }

    public String encode(String message){
        return iEncoder.encode(message);
    }
}
public class Main {
    public static void main(String[] args) {

        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";

        //Base 64 encoding
//        Base64Encoder encoder = new Base64Encoder();
//        String result = encoder.encode(url);
//        System.out.println(result);

        // 어느날 url encoding을 만들어야 한다 가정
//        UrlEncoder urlEncoder = new UrlEncoder();
//        String urlResult = urlEncoder.encode(url);
//        System.out.println(urlResult);

        // 계속 필요할 때 마다 이렇게 생각 없이 만드는 것은 바람직 하지 않음 -> 추상화 생각
        // Encoder 와 urlEncoder의 역할이 비슷하니 인터페이스 한개 뽑을수 있다
        // 하지만 코드는 그래도 기존과 비슷.. 만들때 마다 계속 이렇게 생성해야할까?


//        IEncoder encoder = new Base64Encoder();
//        String result = encoder.encode(url);
//
//        IEncoder urlEncoder = new UrlEncoder();
//        String result2 = urlEncoder.encode(url);
//        System.out.println(result2);

        //DI로 바뀌는 과정을 보자
        // 처음엔 BASE64 사용
//        Encoder encoder = new Encoder();
//        String result = encoder.encode(url);
//        System.out.println(result);

        // 위 코드를 url로 바꾸고자 한다 하면 위에 코드 그대로 하고 encoder의 생성자를 바꿈-> this.iEncoder = new UrlEncoder();
        // 코드는 줄어들었지만 필요할때 마다 내부를 계속 바꾸는 건 비효율적

        Encoder encoder = new Encoder(new Base64Encoder());
        String result = encoder.encode(url);
        System.out.println(result);

        // url로 바꾸고자한다면 Encoder 안을 건드리는 것이 아닌 주입 객체만 바꿈
        // new Encoder(new UrlEncoder());

    }
}

IOC 이해 예시

package com.example.ioc2;

public interface IEncoder {
    String encode(String message);
}
package com.example.ioc2;

import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

@Component
public class UrlEncoder implements IEncoder {

    public String encode(String message){
        try {
            return URLEncoder.encode(message, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
}
package com.example.ioc2;

import org.springframework.stereotype.Component;

import java.util.Base64;

// 스프링한테 객체(bean)를 관리해달라는 요청 -> @component : bean으로 등록
// -> loc2Application에 @SpringBootApplication가 붙고 -> 돋보기 클릭해보면 다루는 bean들이 나옴
// 이렇게 등록한다는 것은 스프링이 실행될때 @componenet 어노테이션이 붙은 객체를 싱글톤 형태로 만들어 Spring Container에서 관리한다는 뜻!
// 컨테이너에 접근하여 객체를 가져오기 위해서는 ApplicationContextProvider가 필요

// Component() 괄호안에 이름 입력시 bean 이름을 지정해줄 수 있음 디폴트 값은 맨앞에 대문자를 소문자로 한 값
@Component("base74Encoder") // 이를 지정하여 메인 밑의 클래스와 충돌할 일 없음
public class Base64Encoder implements IEncoder {

    public String encode(String message){
        return Base64.getEncoder().encodeToString(message.getBytes());
    }
}
package com.example.ioc2;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

// @Component
public class Encoder implements IEncoder {

    private IEncoder iEncoder;

    // 두개의 인코더 사용하기 위해 위에 컴포넌트 어노테이션 제거하고 밑에처럼 생성자 수정
//    public Encoder(@Qualifier("urlEncoder") IEncoder iEncoder){
//        this.iEncoder = iEncoder;
//    }

    public Encoder(IEncoder iEncoder){
        this.iEncoder = iEncoder;
    }

    // bean을 주입받을 수 있는 장소 -> 변수/생성자/ set 메서드

    public void setiEncoder(IEncoder iEncoder){
        this.iEncoder = iEncoder;
    }
    public String encode(String message){
        return iEncoder.encode(message);
    }
}
package com.example.ioc2;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

// 이것 또한 컨테이너에서 관리하게끔!
@Component
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;
    //이것 또한 주입을 받는데 이는 스프링이 알아서 주입해줌
    // 주입받아 변수에 할당
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext(){
        return context;
    }
}
package com.example.ioc2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class Ioc2Application {

    public static void main(String[] args) {
        SpringApplication.run(Ioc2Application.class, args);

        ApplicationContext context = ApplicationContextProvider.getContext();

        // 여기를 name: urlEncode로 하면은 urlEncode로 바뀜
        Encoder encoder = context.getBean("base64Encode", Encoder.class);
        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
        String result = encoder.encode(url);
        System.out.println(result);


        // 클래스 타입으로 찾기
//        Base64Encoder base64Encoder = context.getBean(Base64Encoder.class);
//        UrlEncoder urlEncoder = context.getBean(UrlEncoder.class);

        //아직 까지는 코드내에서 직접 생성
//        Encoder encoder = new Encoder(base64Encoder);
//        String result = encoder.encode(url);
//        System.out.println(result);

        //이러한 방법도 가능
//        encoder.setiEncoder(urlEncoder);
//        result = encoder.encode(url);
//        System.out.println(result);

        // 인코더에 annotation을 붙이는 경우 Base64,url 둘다 컴포넌트 이기 때문에
        // 스프링에서 어떤것을 매칭해야 할지 결정을 못해줌
        // @Qualifier("urlEncoder") IEncoder iEncoder

//        String url = "www.naver.com/books/it?page=10&size=20&name=spring-boot";
//        Encoder encoder = context.getBean(Encoder.class);
//        String result = encoder.encode(url);
//        System.out.println(result);

        // 두개의 Encoder 를 사용하고 싶은 경우 밑에 클래스 생성
    }

}
@Configuration // 한개의클래스에서 여러가지 빈을 등록할거라는 의미
class AppConfig{

    @Bean("base64Encode") // 미리 빈을 등록, base64Encoder를 컨테이너가 알아서 주입
    public Encoder encoder(Base64Encoder base64Encoder){
        return new Encoder(base64Encoder);
    }

    @Bean("urlEncode")
    public Encoder encoder(UrlEncoder urlEncoder){
        return new Encoder(urlEncoder);
    }
}
profile
Journey for Backend Developer

0개의 댓글