일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- gradle
- jdk
- IntelliJ
- post
- AOP
- ansible
- 허브
- STREAM
- mybatis
- 방화벽
- LAN어댑터
- 액세스회선
- DevOps
- map
- Pipeline
- container
- sonarQube
- docker
- Linux
- 라우터
- Spring
- Jenkins
- Java
- Set
- 캐시서버
- tomcat
- 소켓
- cloud
- Collection
- JPA
- Today
- Total
거북이-https://velog.io/@violet_evgadn 이전완료
Spring Web 메인 클래스 본문
들어가기 앞서...
Spring 개념을 한 번 복습하자라는 차원에서 블로그를 새로 만들어 글을 썼는데 생각보다 내가 개념을 대충 공부하고 Spring을 활용하고 있음을 알 수 있었다.
이 부족한 개념에 대해서 채우려다보니 이론적 조사와 설명에만 많은 시간을 쏟은 것 같다.
하지만, 부족한 개념을 정확히 이해하고 코드 활용법을 이해하다보니 "왜 그 방식으로 프로그래밍을 수행하는가"가 명확히 보이며 프로그램의 난이도가 쉬워졌음을 알 수 있었다.
(특히 AOP... 처음 AOP 코드 짤 때는 주먹구구식으로 짰는데 개념을 완벽히 이해하고 짜니 에러 1개 없이 빠르고 정확한 구현이 가능해졌고, 컨닝 페이퍼 없이도 AOP에 대한 코드를 짤 수 있게 되었다)
앞으로도 코드에 대한 설명을 하면서 필요할 때마다 새로운 Section을 파서 이론에 대해 조사하고 정리할 것 같다. 이론에 대해 명확히 알야 더욱 쉬운 코딩이 가능해지니 이론에 대해 꼭 정확히 파악하고 가자!
Spring Web 클래스
Spring Initializr에서 Spring Web 의존성을 포함하여 프로젝트를 수행하였다면 Main Class에 여러 가지 어노테이션과 메서드가 자동으로 추가되어 있음을 알 수 있다.
이 클래스가 조금 신기한 부분이 많은데 한 번 자세히 파악해보고 가자
@SpringBootApplication
public class ReviewApplication {
public static void main(String[] args) {
SpringApplication.run(ReviewApplication.class, args);
}
}
◎ @SpringBootApplication
@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{
...
}
Spring Web에서는 무조건 기본적으로 적용해야 하는 Annotation이다.
여기에서 활용하는 3가지 어노테이션, "@SpringBootConfiguration", "@EnableAutoConfiguration", "@ComponentScan"이 모두 중요한 역할을 하기 때문이다. 그렇다면 각각의 어노테이션이 어떤 역할을 하는지 확인해보자
(결국 @SpringBootApplication 어노테이션은은 위 3가지 어노테이션 역할을 모두 수행하게 될 것이다)
@ComponentScan
이전에 컴포넌트 스캔을 공부할 때 배웠다.
우리는 @Component로 Class를 설정하면 @ComponentScan 어노테이션을 가진 클래스가 경로상에 존재하는 모든 @Component Bean들을 스캔하여 Bean 저장소에 등록하는 것이다.
Spring에서 활용하는 @Repository, @Service, @Controller, @RestController 등은 모두 @Component 어노테이션을 가지고 있다. 따라서, 만약 @ComponentScan를 가지고 있지 않는다면 Bean에 등록되어야 할 클래스들을 Spring Bean 저장소에 올리지 못하게 되며, 자연스럽게 Spring Bean을 통한 DI 활용이 불가해질 것이다.
@EnableAutoConfiguration
AutoConfiguration도 결국은 Configuration이다. 즉, Bean을 등록하는 자바 설정 파일이다.
@EnableAutoConfiguration은 Spring Boot의 Meta 파일을 읽어 미리 정의 되어 있는 자바 설정 파일(@Configuration)들을 빈으로 등록하는 역할을 수행한다.
이 때 meta 파일은 spring.factories라는 이름으로 저장되어 있다.
즉, @ComponentScan을 통해 "내가 설정한 Spring Bean"들을 활용할 수 있게 된다면, @EnableAutoConfiguration 어노테이션을 통해 spring.factories 파일을 읽고 Spring Boot에서 미리 정의해 둔 Bean 설정(Configuration)들을 활용할 수 있게 되어 아무런 설정 없이도 Spring Boot 기능을 자유자재로 활용할 수 있게 되는 것이다
@SpringBootConfiguration
스프링에선 @Configuration을 통해 Bean들을 관리한다. @SpringBootConfiguration은 "Spring Boot만을 위한" Configuration임을 알리는 어노테이션이다.
@SpringBootConfiguration의 가장 큰 특징은 구성을 자동으로 찾을 수 있다는 것이다.
원래 @Configuration을 활용하면 @Bean으로 구성을 내가 짜야하는데, @SpringBootConfiguration은 이런 과정 필요 없이 자동으로 구성을 찾는다는 것이다.
그렇다보니 1개의 서비스에서는 2개 이상의 @SpringBootConfiguration을 활용하지 못한다(똑같은 구성을 1개의 프로젝트에서 2번 이상 활용할 필요가 없기 때문)
@SpringBootConfiguration이 중요한 이유는 단위 테스트 때문이다.
Spring에서 테스트를 할 때 테스트 클래스에 @SpringBootTest 어노테이션을 활용한다. 그런데 이 어노테이션을 활용하여 테스트를 진행하면 @SpringBootConfiguration 어노테이션을 찾게 된다.
즉, @SpringBootConfiguration이 없다면 단위 테스트가 아예 불가하기 때문에 Spring Boot에서는 필수적인 어노테이션이며 단위 테스트를 진행할 때는 @ComponentScan이 포함되어 Spring Bean을 가장 잘 활용할 수 있는 Main Class에 존재하는 것이 베스트이므로 @SpringBootApplication 어노테이션에 포함되는 것이다
◎ SpringApplication.run(X.class, args)
내장 WAS(Tomcat)을 실행시켜 Spring 기반 프로젝트를 웹 상에서 실행시키는 역할을 한다.
참고로 Tomcat 대신 외부 WAS로 변경이 가능하지만 언제 어디서나 같은 환경에서 Spring Boot를 배포하기 위해서 내장 WAS 활용을 추천한다.
위 코드처럼 SpringApplication.run(X.class, args) 방식을 활용해도 큰 문제는 없으면 다양한 커스터마이징 기능을 사용하기는 어렵다고 한다.
만약 다양한 커스터 마이징 기능을 활용하고 싶다면 아래 코드를 활용하도록 하자
@SpringBootApplication
public class ReviewApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ReviewApplication.class);
app.run(args);
}
}
스프링 구동 시점에 코드 실행 시키기
SpringApplication.run을 통해 Tomcat 서버(WAS)에 연결시켜 웹 프로젝트를 구동하는 것이 Spring의 동작 방식이다.
그런데 톰캣 서버로 연결시키기 이전 특정 로직을 수행하고 싶을 때는 어떻게 해야할까?
이 때 활용할 수 있는 인터페이스가 CommandLineRunner와 ApplicationRunner이다.
Spring Boot가 구동되기 이전 2개 인터페이스를 상속하여 Override한 클래스의 run() 메서드를 수행시키고, 이후에 SpringApplication.run을 통해 Tomcat 서버에 구동시킨다. 그렇다면 두 인터페이스는 어떤 차이가 있고, 어떻게 활용할까?
◎ CommandLineRunner
CommandLineRunner 인터페이스는 구동 시점에 실행되는 코드 중 "자바 문자열 Argument 배열"에 접근해야 할 필요가 있는 경우 활용된다.
말이 조금 어려우니 실제 예시를 통해 알아보자.
내가 만든 웹 프로젝트를 X.jar로 패키징시켰다고 가정하자. 이후 X.jar를 실행시킬 때는 "java -jar X.jar" 명령어를 통해 구동할 것이다. 그런데 이 때 명령어에 Argument를 전달하는 상황이 존재할 수 있다. 예를 들어, "java -jar X.jar abc 123"과 같이 명령어를 입력하는 Case가 존재한다.
이렇게 입력하는 대부분의 경우 "abc"와 "123"이라는 값을 애플리케이션 내에서 활용되기 때문에(주로 설정값으로) Arugment로 전달되는 것이고, 이 때문에 "abc"와 "123"을 활용할 수 있는 방법을 생각해야 한다.
CommandLineRunner는 이런 Argument 값을 run() 메서드를 통해 인자로 받는다. 이 때 CommandLineRunner는 인자를 String 배열으로 처리하게 된다.
(즉, 위 예시에서는 배열 ["abc", "123"]으로 Argument를 처리하는 것이다)
◎ ApplicationRunner
ApplicationRunner 인터페이스 또한 구동 시점에 run() 메서드를 실행시키지만 입력받는 인자의 Type이 다르다.
ApplicationRunner는 ApplicationArguments라는 타입의 객체를 통해 인자를 받는데, CommandLineRunner와 같이 String 배열로 받는 것이 아닌 추상화된 타입의 객체를 활용한 것이라고 생각하면 된다. 물론, ApplicatinoArguments 객체를 Array로 바꾸어 String도 처리할 수 있으므로, 어떻게 보면 CommandLineRunner의 확장 버전이라고 생각하면 편하다
◎ 활용법
ApplicationRunner와 CommandLineRunner의 run()은 결국 "터미널로 수행한 명령어"의 Argument를 처리하는 메서드이기 때문에 "Spring Boot가 Tomcat 위에서 구동되기 이전에" 실행되어야하며, run() 메서드 실행이 종료되면 그제서야 Spring Boot가 구동어야 할 것이다.
이 성질을 이용하면 Spring Boot가 Tomcat 상에서 구동되기 이전 특정한 로직을 수행해야 하거나 특정 객체를 초기화해야 할 수 있는 것이다.
아래 코드를 예시로 보자
@SpringBootApplication
@RequiredArgsConstructor
public class DemoApplication implements CommandLineRunner {
private final PostsRepository repository;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
repository.save(Posts.builder().title("title")
.content("content")
.author("jojoldu@gmail.com")
.build());
}
}
먼저 repository.save() 명령어는 Post 객체를 DB에 저장시키는 로직을 수행한다. 이를 바탕으로 프로젝트 구동 흐름을 보면 아래와 같다.
먼저 java X.jar 명령어를 통해 패키징된 애플리케이션이 구동될 준비를 할 것이다. 그런데 CommandLineRunner 인터페이스의 run() 메서드가 존재하므로 먼저 run() 메서드가 선행되어 실행된다.
run() 메서드에서는 repository.save() 명령어가 존재한다. 따라서, 내 임의대로 만든 Post 객체가 DB에 저장될 것이다.
만들어진 Post 객체가 DB에 저장되면 그 때서야 SpringApplication.run() 메서드로 인해 Tomcat 상에서 Spring Boot가 구동되는 것이다
즉, 위 코드를 실행시키면 repository.save()를 통해 "구동 이전에 먼저 DB에 데이터를 저장하고" 이후에 Tomcat 상에서 Spring Boot가 구동되는 것이다.
물론 현업에서는 이런 방식으로 데이터를 DB에 넣는 것보다는 실제 활용하는 DB에 SQL 등을 통해 데이터를 넣어주는게 깔끔하고 더 편하며 안전하므로 잘 활용하진 않는다.
하지만 Project에 대한 테스트를 반복해서 진행할 때는 DB에 데이터가 쌓이면 이전 테스트의 실행결과가 현재 테스트의 실행 결과에 영향을 끼칠수도 있을 것이다. 따라서 Project에 대한 테스트를 진행할 때는 계속해서 DB를 초기화시킨 이후 전체 애플리케이션 테스트를 진행할텐데 이 때 비어있는 DB를 활용하기 보다는 그래도 어느정도 데이터가 저장된 DB를 활용하는 것이 테스트에 더 용이할 것이다. 따라서 테스트할 때에는 활용할만한 기법이라고 생각한다.
'웹 개발 > Spring(활용)' 카테고리의 다른 글
build.gradle 사용법 (0) | 2022.11.27 |
---|---|
AOP 실습 (0) | 2022.08.04 |
DI 주입 실습 (0) | 2022.08.04 |
스프링 빈 활용 (0) | 2022.08.03 |