고냥 프로젝트를 생성하고 gradle 설정을 한다.
이미 알고있는 내용이라 훑으며 진행했다.
다만, 한가지 의문이 들었던 부분이 있었는데, 강의 자료 중
최근 IntelliJ 버전은 Gradle을 통해서 실행 하는 것이 기본 설정이다.
이렇게 하면 실행속도가 느리다.
다음과 같이 변경하면 자바로 바로 실행해서 실행속도가 더 빠르다.
라는 내용이 있어서 왜 Gradle보다 IntelliJ 로 빌드하는게 빠를까?
에 대한 의문이 들어서 알아봤다.
IntelliJ는 증분 빌드 (Increment Build) 라는것을 사용하는데,
간략하게 설명하면 변경된 내용만을 빌드
하기 때문이다.
반면에, Gradle은 프로젝트 전체를 빌드하기 때문에 시간이 오래걸린다.
여기서 생기는 차이점이 있는데
(.kts)
)이 변경될 경우 반드시 gradle rebuild 필요.resources/static/index.html
파일을 통해 Welcome Page를 만들 수 있다.
-> 공식 문서 참조
Server-Side 템플릿 엔진
이다.Controller 에서 문자열을 반환하면, viewResolver가 model을 매핑하여 정적 페이지를 반환한다.
매핑되는 템플릿 : resources/templates/{return된 문자열}
.html
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
}
resources/templates/hello.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'안녕하세요. ' + ${data}" >안녕하세요. 손님</p>
</body>
</html>
Controller 에 @ResponseBody
어노테이션을 붙여서 사용 가능하다.
@ResponseBody 어노테이션을 사용하면, viewResolver가 사용되지 않는다.
즉, 리턴되는 문자열이 template와 매핑되지 않고, 문자열 자체가 반환된다.
@Controller
@ResponseBody
public class HelloController {
@GetMapping("hello")
public String helloString(@RequestParam("name") String name) {
return "hello " + name;
}
}
{ // request body
"data1": "some-value",
"data2": "some-value"
}
스프링 빈을 사용하기 위해서는 여러 방법이 있지만,
아래 2가지 방법에 대해 소개한다.
스프링의 시작점 (main) 함수에는 @SpringBootApplication
어노테이션이 붙어있다. 내부를 살펴보면 @ComponentScan(...)
이 선언되있는 것을 확인할 수 있다.
BasePackage 가 별도로 설정되어있지 않다면, 현재 위치와 하위 위치를 탐색한다.
이 때, 각 클래스에 @Component
어노테이션이 붙어있다면, 스프링이 자동으로 빈으로 등록한다.
일반적으로 @Controller
, @Repository
, @Service
, @Configuration
어노테이션도 빈으로 등록되는데, 이 또한 내부를 살펴보면 @Component
어노테이션이 적용되어있는 것을 확인할 수 있다.
@Configuration 어노테이션을 적용한 클래스에서,
메소드에 @Bean 어노테이션을 붙이면 빈으로 관리할 수 있다.
package myPackage;
@Configuration
public class MyConfiguration {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
public MyRepository myRepository() {
return new MyRepository();
}
}
위 예제 코드에서는
MyService 클래스에 @Service, MyRepository 클래스에 @Repository 어노테이션이 없어도 된다.
명시적으로 MyService 및 MyRepository 클래스를 bean으로 등록했기 때문이다.
등록해둔 빈을 코드에서 변수에 할당하는 것을 '빈을 주입한다'고 표현한다.
빈을 주입하는 방법에는 3가지가 있다.
미리 스포를 하자면, 생성자 주입
을 사용하도록 권장된다.
아래는 각 주입 방법에 대한 설명과 왜 생성자 주입을 사용해야 하는거?
에 대한 간략한 소개를 해본다.
필드에 @Autowired
어노테이션을 통해 직접적으로 주입한다.
필드 주입을 사용하게되면 IntelliJ 의 경우에는 '필드주입 쓰지 말고 생성자 주입 쓰세요' 하고 경고를 표시해준다.
public class MyService {
@Autowired
private MyRepository repository;
...
}
setMethod 에 @Autowired 어노테이션을 통해 MyRepository빈을 주입받고,
주입받은 빈을 MyRepository에 할당한다.
주입받는 객체가 변경될 수 있는 경우에 사용하지만, 실무에서는 그럴 일이 99.99% 확률로 없다.
public class MyService {
private MyRepository repository;
@Autowired
public void setMyRepository(MyRepository repository) {
this.repository = repository;
}
}
가장 권장되는 방법이다.
클래스가 생성되는 시점에 주입된다. 즉, 1회만 주입됨이 보장된다.
생성자가 주 생성자 1개만 있는 경우 @Autowired 어노테이션을 생략할 수도 있다.
public class MyService {
private final MyRepository repository;
// 주 생성자 1개만 있는 경우 @Autowired 생략 가능
public MyService(MyRepository repository) {
this.repository = repository;
}
}
자바 언어의 경우 Lombok 에서 제공하는 @RequiredArgsConstructor
를 클래스 레벨에 붙여주면, Lombok이 생성자를 자동으로 생성해주면서 생성자 마저 안써도 주입이 된다고 한다.
하지만 코틀린은 기본적으로 제공해준 기능이라 코프링부트 개발자인 나는 '그러려니~' 하고 넘겼다 ㅋㅋ.
의존성 명시적 표현
생성자 파라미터로 필요한 의존성을 전달하기 때문에 코드에서 어떤 의존성이 필요한지 쉽게 파악할 수 있다. -> 가독성과 이해도가 향상
불변성 보장
생성자 주입을 사용하면 필드가 final로 선언되기 때문에, 한 번 주입된 의존성은 런타임 중에 변경할 수 없다.
의존성 순환 참조 방지
단위 테스트 용이성
의존성을 명시적으로 전달하므로, 테스트 중에 가짜(mock) 객체 또는 테스트 더미(dummy) 객체를 주입하여 의존성을 테스트할 수 있다.
Spring 프레임워크와 호환성
Spring Framework에서 기본적으로 지원되는 주입 방식을 사용하여, 의존성 주입을 더욱 쉽고 효과적으로 관리할 수 있다.
@Aspect
어노테이션을 적용하여 해당 클래스를 Aspect로 정의하고, 어떤 Join Point에서 어떤 Advice를 실행할지 정의할 수 있다. (정의된 Aspect는 스프링 빈으로 관리되어야 하기 때문에, @Component
어노테이션을 함께 붙여야 한다.)import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
private void pointcut() {}
@Before("pointcut()")
//@Before("execution(* com.example.service.*.*(..))") 로 줄여서 사용 가능
public void beforeServiceMethodExecution() {
// 메소드 호출 전에 로깅을 수행하는 어드바이스
System.out.println("Before executing a service method.");
}
}
Reference
김영한님 스프링 강의
https://haenny.tistory.com/394
https://programforlife.tistory.com/111