안녕하세요
앵과장입니다.
Java로 10여년째 의식주를 해결하고 있는 개발자 입니다.
라떼는 한참 토비의 스프링이 인생에 바이블 처럼 구매하고 살짝만 읽었던 기억이 있는데요!!
토비의 스프링을 구매하고 아직도 다못읽었는데 토비 개정판이 나오고 이거 아직 다못읽었는데 Springboot가 나오고 Document문서가 인터넷에 영문으로 나온걸 보고 있는데 백기선님이 유투브에서 한땀한땀 설명해주는 시대가 왔네요
동영상을 듣고 있으면 무슨 시간과 공간의 방에 온기분이 듭니다. 그러고는 스르륵 잠이 옵니다!! (아 개운하다)
https://youtube.com/playlist?list=PLfI752FpVCS8tDT1QEYwcXmkKDz-_6nm3
IT에서는 정말 끝이 없는 공부 정리 입털기 손으로 코딩하기 다시 또 뫼비우스 처럼 말입니다!!
아 이럴줄 알았으면 공부 열심히 해서 부동산이나 코인으로 갔어야되는데 라는 아쉬움이 드는건 기분탓이겠져!!
최근에는 더많은 분들 인도형 러시아형 들이 유투브로 많이 만들어서 광고비를 받으려고 영상들로 빠르게 여러 샘플들로 내가 귀찮아 하지만 않다면 여러루트로 배울수 있는 기회가 높아진것 같습니다.
SpringBoot은 왜?!
사용되고 있고 Spring하고는 무슨차이 인가요?
이걸 장황하게 쓰기전에 한마디로 정리하면 Springboot는 Spring 이후에 나온거라서 더좋아요 로 끝내고 싶지만 이러면 왜요? 라고 물어보겠져 ㅜㅡㅜ
Spring은 많은 변화가 있었습니다.
아마도 초기에 EJB 대항마라며 책을 팔아재끼던 약팔던 시점부터 였던것같네요
그러더니 어느순간 Spring이 EJB 만큼 점점 구동하는것부터 무거워지기 시작했습니다.
테스트 해봐야지 하고 Run 했는데 10분뒤에도 안뜨는 상황을 본적이 있습니다.
편하자고 사용한 Spring은 우리가 늘상하는 CRUD를 만들수 있는 MVC부터 관점지향이니 객체 주입이니 하는 여러가지 기능을 Xml로 세팅하고 Source레벨에 Annotation형태로 제공하고 버전도 다양하고 표준도 다양하고 제공하는 온갖 라이브러리가 추가되고 뭔 공통 Jar는 이렇게 많은건지 common이 이렇게 많은데 뭐가 좋은거야!?
이게 처음에는 쉽게 밥빌어먹고 살수 있게 해준다더니 입개발자들이 많아지며 이런방법이 좋다 저런게 좋다 라는 참으로 주관적인 견해들로 수많은 스켈레톤 형태가 만들어 지기 시작하였습니다.
그래서 어떤게 가장좋은거야?!
이렇게 출발한 프로젝트가 Springboot가 만들어진 계기가 될수 있습니다.
여러 공통적인부분들 자주사용하는 부분들 자유도를 제공하기보다는 그냥 형식에 맞는 어느정도 약속된 가이드를 제공하고 사용하게끔 하는것이 오히려 오해와 견해에 대한 것들을 잡아줄수 있는 표준이 된것같습니다.
역시 튜닝에 끝은 순정입니다.
Springboot는 그이후로 지속적으로 업데이트 되고 있고 많은 부분들이 올바른 방향이 된것 같습니다
또 언제 새로운게 바뀔지는 모르겠지만
Springcloud로 바뀔것같은 느낌적인 느낌이라
요부분은 아직 공부를 더하고 다음에 정리 해보려고 합니다.
Spring 주요 특징 및 모듈
경량화
독립된 jar 파일로 구성되어있기 때문에 설치가 용이하고 POJO 를사용한다고 합니다.
POJO (Plain Old Java Object)
오래된 방식의 간단한 자바 오브젝트라는 말로 Java EE 등의 중량 프레임워크를 사용하게 되면서 해당 프레임워크에 종속된 무거운 객체를 만들게 된것에 반발해서 사용하게 된 용어 라고 합니다. 결국 가볍게 쓰자이런건가 보네요?
IoC (Inversion of Control)
기존에 모든제어를 클라이언트 코드가 가지도록 구현되었던 부분들을 Framework가 제어를 나누어 가져가 의존관계의 방향이 달라지게 된다는 겁니다.
Spring Framework 로 객체를 관리하고 객체의 생성을 책임지고, 의존성까지 관리해주는 컨테이너 라고 보시면됩니다.
IOC는 객체의 생명주기를 관리하며 DI 패턴을 제공하여 객체주입이되기때문에 개발자들이 비지니스 로직에 직중할수 있도록 해주는거라고 보면됩니다. (이런것들을 몰라도 그냥 그려러니 하시면되지만 앞으로 쭈욱 그려러니 하고 살게될거같아 미리 정리하시면 도움이 됩니다.)
Bean
- Legacy 형태 Xml 주입방법
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
// Bean을 등록하는 과정
<bean id="bookService" class="com.ex.forblog.book.BookService">
<property name="bookRepository" ref="bookRepository"/>
</bean>
// Bean을 의존성 주입(DI)하는 과정
<bean id="bookRepository" class="com.ex.forblog.book.BookRepository"/>
</beans>
SpringBoot 에서는
@Component, @Service, @Controller, @RestController, @Repository, @Bean, @Configuraton 등으로
Bean 등록이 가능합니다.
각 어노테이션들은 @Component 어노테이션을 상속받고있고
이러한 @Controller, @Service, @Repository 어노테이션은 @Component 어노테이션보다 용도에 따라 사용된다라는 구성으로 인지하시면 될것 같습니다.
@Bean, @Configuration 을 이용해서도 등록 가능합니다.
@Bean 어노테이션의 세부 내용은 다음과 같다.
- @Configuration 설정된 클래스의 메서드에서 사용가능하다.
- 메서드의 리턴 객체를 IoC의 Bean으로 등록한다.
- Bean의 이름은 기본적으로 메서드 이름으로 등록된다.
- @Bean(name="name") 으로 이름을 변경할 수 있다.
- @Scope를 통해 객체 생성을 조정할 수 있다.
- @Component 에너테이션을 통해 @Configuration 없이도 Bean을 등록할 수 있다.
- Bean에 init(), destory() 등 라이프사이클 메서드를 추가한 후 @Beam에 지정할 수도 있다.
- Annotation Bean 주입방법
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
public BookRepository bookRepository() {
return new BookRepository();
}
@Bean
public BookService bookService() {
return new BookService();
}
}
DI(Dependency Injection)
의존성 주입이란 IoC 컨테이너에서 관리될 객체들을 주입하는 방법이며 예전방식에서 최근방식까지 정리해보도록 하겠습니다.
- 생성자를 통한 Bean 의존성주입
@Component
public class UserController {
private SampleRepository sampleRepository;
@Autowired
public UserController(UserRepository UserRepository) {
this.userRepository = userRepository;
}
}
- 필드(Property)에 직접 @Autowired 어노테이션을 추가해 의존성을 주입
@Component
public class UserController {
@Autowired
private UserRepository userRepository;
}
- Setter를 통해 의존성을 주입
@Component
public class UserController {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
- Lombok 생성자를 통한 의존성을 주입
@RequiredArgsConstructor
public class GoodsController {
private final GoodsAppService goodsAppService;
@ApiOperation(value = "상품조회", notes = "상품조회", tags = "상품")
@ApiResponses({
@ApiResponse(code=200, message="goodsNo list로 전달받은 결과물을 리턴합니다.")
})
@GetMapping("/goods")
public List<GoodsResponse> getGoodsByNo(@RequestParam(required=true) List<Long> goodsNos){
return goodsAppService.getAllByGoods(goodsNos);
}
}
예전 소스들에서는 필드에 직접 @Autowird 사용방법이었는데 순환참조에 대한 이슈가 발생할경우가 있는데
닭이 먼저다 달걀이 먼저다 형태 샘플로 본기억이 있어서 최근에는 Lombok을 이용한 생성자 의존성을 사용하고 있는 추세 입니다.
필수적으로 사용해야하는 의존성 없이는 인스턴스를 만들지 못하도록 강제할수 있기때문입니다.
생성된 class 형태가 궁금하다면 직접 intellij에 class를 확인해보시면됩니다.
GoodsController.class Lombok Annotation이 어떻게 생성되었는지 확인해보도록 하겠습니다.
lombok annotation 은 아래처럼 생성됩니다.
public class GoodsController {
private static final Logger log = LoggerFactory.getLogger(GoodsController.class);
private final GoodsAppService goodsAppService;
public GoodsController(final GoodsAppService goodsAppService) {
this.goodsAppService = goodsAppService;
}
@GetMapping({"/goods"})
public List<GoodsResponse> getGoodsByNo(@RequestParam(required = true) List<Long> goodsNos) {
return this.goodsAppService.getAllByGoods(goodsNos);
}
@ComponentScan
Component 를 Scan 할 시작지점을 나타내는 어노테이션 입니다.
메인 함수에 붙어 있는 @SpringBootApplication 있는데 해당 어노테이션 내부를 확인하면
@ComponentScan 을 확인할수 있습니다.
@SpringBootApplication
public class GoodsServerApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsServerApplication.class,args);
}
}
@SpringBootApplication 클릭시
아래와같이 @componentScan 확인이 가능합니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
...
해당 ComponentScan 위치는 메인함수가 Component를 Scan 시작하는 지점이 됩니다.
@Configuration
@ComponentScan(
basePackages = "com.ch4njun.example.app1"
)
public class TestConfiguration {
}
@Configuration
@ComponentScan(
basePackageClasses = TestConfiguration.class
)
public class TestConfiguration {
}
두가지 방법중 basePackages 설정이 더 Type Safe 방법 이라고 합니다.
별도로 설정하지 않으면 자동으로 @ComponentScan 어노테이션이 추가되어 있는 TestConfiguration 클래스가 Component Scan 시작지점이 됩니다.
@ComponentScan 에서제공하는 속성중 excludeFilters 속성을 사용해서 특정 Bean을 등록하지 않도록 제외시킬수도 있는데 자주사용하지는 않지만 이런기능도 제공하고있다정도로 알고 계시면 좋을것 같습니다.
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
@Configuration
@ComponentScan(
basePackageClasses = ApplicationConfig.class,
excludeFilters = @Filter(
type = FilterType.ANNOTATION,
classes = {IgnoreDuringScan.class}
)
)
public class ApplicationConfig {
}