의존 주입 DI(Dependency Injection)
어떠한 객체가 다른 객체를 필요로 함에 따라 setter나 생성자를 사용하여 필요로 하는 객체를 주입시켜 기능을 사용할 수 있게 하는 것 (또는 직접 주입 가능)
컨테이너
객체를 생성하고, 라이프 사이클을 관리하고 , 다른 객체에 의존 주입을 관리해주는 것
IoC(Inversion Of Control) 제어의 역전
개발자가 직접 객체를 제어하지 않고 컨테이너로 객체의 제어권이 넘어갔다는 의미
스프링은 IoC 컨테이너의 역할을 한다. 스프링은 객체를 생성, 라이프사이클 관리 및 필요로 하는 객체에 의존 주입을 하는 컨테이너이고, 라이브러리들의 집합체 이다.
강한결합
개발자가 직접 객체를 생성부터 소멸까지 해당 객체의 라이프 사이클을 개발자가 다 관리하는 것
약한결합
이미 누군가가 생성한 객체를 주입받을 경우, 사용하기만 하면 되므로 약한 결합이 된다. 객체지향 프로그래밍에서 약한 결합(느슨한 결합)을 사용하게 되면 개발자가 관리할 것이 적어진다.
또한 다른 클래스의 변화에 더욱 안전하고 유연하게 대처할 수 있다.
자바코드로 DI 사용
빈 설정 xml을 이용한 의존 주입
자바 코드를 이용한 의존 주입
어노테이션을 이용한 의존 주입
빈 설정 xml을 이용한 의존 주입
스프링에서는 보통의 경우 xml을 이용한 의존 주입이 많이 이용된다.
스프링 부트는 xml의 과도한 설정을 없애기 위해서 xml을 이용한 의존 주입을 사용하지 않는다.
빈 등록
스프링 컨테이너에 등록하고 사용할 클래스들
빈(bean)
Spring이 IoC 방식으로 관리하는 객체
빈 팩토리(BeanFactory)
스프링의 IoC를 담당하는 핵심 컨테이너
애플리케이션 컨텍스트(Application Context)
빈 팩토리를 확장한 IoC 컨테이너
자바 코드를 이용한 의존 주입
Setter Injection
Constructor Injection
@Bean 어노테이션에 이름을 지정하면 해당 클래스를 빈으로 등록할 때 입력한 이름으로 빈의 이름을 변경할 수 있다.
별도의 이름을 지정하지 않으면 메서드 이름이 빈의 이름으로 등록된다.
@SpringBootApplication
스프링 부트는 @SpringBootApplication 어노테이션에 의해 자동으로 설정이 이루어지고 컴포넌트가 등록이 된다.
내부적으로 살펴보면
- IoC 컨테이너 생성
- Bean 가져오기
이다.
//1. IoC 컨테이너 생성
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
// 2. Member Bean 가져오기
Member member1 = (Member)context.getBean("member1");
member1.print();
// 3. Member Bean 가져오기
Member member2 = context.getBean("hello", Member.class);
member2.print();
// 4.PrintB Bean 가져오기
Printer printer = context.getBean("printerB", Printer.class);
member1.setPrinter(printer);
member1.print();
Spring 컨테이너에서 기본적으로 객체 생성은 Singleton 형태로 제공된다.
2번은 빈을 가져올 때 스프링 컨테이너로부터 빈을 리턴받아 형변환 하여 사용
3번, 4번은 미리 형을 지정하여 빈을 리턴 받는다.
또한 3번에서 Bean 어노테이션을 지정할 때 별도의 이름을 지정하였으면 그 이름으로 빈을 요청하고 리턴받는 방법으로 사용할 수 도 있다.
4번 처럼 세터를 통해 빈에 등록된 값을 나중에 변경 할 수 도 있다.
생성된 객체들은 같은 클래스 타입의 변수지만 서로 다른 메서드에서 각각 new를 통해 생성되었으므로 같은 객체가 아니다.
어노테이션으로 DI 사용
이 방법은 스프링부트에서 가장 많이 사용되는 방식이다.
package com.study.springboot.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Member {
@Value("홍길동")
private String name;
@Value("도사")
private String nickname;
@Autowired
@Qualifier("printerA")
private Printer printer;
public Member() {}
public Member(String name, String nickname, Printer printer) {
super();
this.name = name;
this.nickname = nickname;
this.printer = printer;
}
public void setName(String name) {
this.name = name;
}
public void setNicknamme(String nickname) {
this.nickname = nickname;
}
public void setPrinter(Printer printer) {
this.printer = printer;
}
public void print() {
printer.print("Hello "+name + " : "+nickname);
}
}
@Component
다음에 나오는 클래스를 빈으로 등록하겠다는 의미이다.
이름을 지정하면 그 이름으로 지정이 가능하다.
이름을 설정하지 않으면 클래스의 첫 글자를 소문자로 한 클래스의 이름이 빈의 이름으로 등록이 된다.
@Value
빈이 생성될때 지정한 값을 기본값으로 설정한다. 세터의 역할을 수행하기에, 객체가 생성될 때 값을 가지고 만들 수 있도록 값의 지정이 가능하다.
@Autowired
해당 클래스의 객체를 찾아와 자동으로 연결한다.
컨테이너에 등록된 빈들 중에서 사용할 수 있는 객체를 찾아서 자동으로 연결해준다.
@Qualifier
유사한 객체가 여러 개 일 때 빈의 이름으로 정확하게 지정한다.
@SpringBootApplication의 세가지 역할
- @Configuration
- Bean을 생성할 때 Singleton으로 한 번만 생성한다. 그리고 각종 설정을 세팅한다.
- @EnableAutoConfiguration
- 스프링 애플리케이션 컨텍스트(Application Context)를 만들 때 자동으로 설정하는 기능을 켠다. 사용자가 필요할 것 같은 빈을 추측해서 ApplicationContext를 만들 때 필요한 설정을 한다.
- 클래스패스를 기준으로 클래스를 찾아 설정을 한다.
- 예를 들어 클래스패스에 tomcat-embeded.jar가 있으면 TomcatEmbeddedServletContainerFactory가 있을 것이라고 추측해서 설정을 한다.
- @ComponentScan
- 지정한 위치 이하에 있는 @Component와 @Configuration이 붙은 클래스를 스캔해서 빈으로 등록한다.
프로그램이 실행되면 @SpringBootApplication 어노테이션이 있는 클래스의 main 메서드가 가장 처음으로 실행된다.
이때 @SpringBootApplication 어노테이션을 통해 자동으로 빈을 검색하고 등록한 후 main 메서드의 SpringApplication.run을 통해서 내장 톰캣을 실행한다음, 자동으로 WebApplicationContext를 생성한다.
스프링부트 웹 프로젝트
웹 브라우저의 주소창에 URL을 등록하면 RequestMapping에 등록된 메서드가 호출되는 방식이다.
빈의 메서드를 호출하기 위해서도 이와 같은 방식을 사용해야 한다.
@Controller
Controller에서 사용할 변수의 값은 @Controller 어노테이션에 의해 자동으로 의존 주입을 받아 할당한다.
@Controller 다음에 나오는 클래스를 빈으로 등록한다는 의미이다.
@RequestMapping
웹 브라우저에서 사용자가 /로 get 방식의 url 호출을 하면 다음 라인의 메서드를 실행시킨다.
클래스를 추가하고 웹 브라우저의 요청을 처리할 메서드를 작성하는 것
리퀘스트 맵핑은 Controller에서 만들어서 작성한다.
@ResponseBody
Json이나 Xml등 REST Api 형태의 응답을 할경우, 다시 말해 html 태그 없이 순수하게 스트링 데이터로만 응답을 할 경우 지정한다.
View Resolver(뷰 리졸버)
클라이언트가 원하는 컨테츠 타입을 고려하여 뷰를 결정하는데 여러 뷰 형식을 동적인 문서를 만들때 사용한다.
- FreeMarker
- Groovy
- Thymeleaf
- Velocity
- JSP
Spring Web 디펜던시
스프링 MVC를 사용하는 RESTful 애플리케이션을 포함한 웹 구축을 위한 스타터이며, 톰캣을 기본 내장 컨테이너로 사용한다.
선택하고 프로젝트를 만들게 되면, 스프링 MVC를 사용하기 위한 의존성이 프로젝트 생성 시 자동으로 추가된다.
정적인 요소
스프링부트에서 정적인 요소는 resoureces 하위의 static 폴더에 저장하고 사용하면 된다. (html, css, image)
추가적인 디펜던시를 추가하면 다른 파일도 사용이 가능하다.
JSP
스프링부트에서는 JSP를 공식적으로 지원하지 않는다.
그래서 추가적인 설정을 해주어야 한다.
제약조건은
- 실행 가능한 jar 파일로 만들었을 때 JSP가 동작하지 않는다.
- 내장 서버로 제티(Jetty)를 사용할 수 없다.
이다.
추가적인 설정
1
build.gradle에 추가
//jstl을 사용하기 위한 라이브러리 추가
implementation 'javax.servlet:jstl'
//톰캣이 jsp 파일을 컴파일 할 수 있도록 만들어주는 라이브러리를 추가
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
2
스프링 부트에서 기본으로 제공하는 다른 템플릿 뷰들과 달리 JSP는 src/main/resources의 템플릿 폴더를 사용할 수 없어
사용자가 직접 폴더를 만들고 지정해야 한다.
다른 src (main, test) 폴더에 WEB-INF 폴더 생성
그 안에 views 생성
3
application.properties에 추가
//접두어
spring.mvc.view.prefix=/WEB-INF/views/
//접미어
spring.mvc.view.suffix=.jsp
여기서 설정한값 과 개발자가 파라미터로 넘긴 값을 조합해 JSP 파일을 읽어온다.
spring.mvc.view.prefix=/WEB-INF/views/
메서드 리턴값 : xxxx
spring.mvc.view.suffix=.jsp
/WEB-INF/views/ + xxxx + .jsp
즉 위의 결과를 토대로 폴더에 jsp 파일을 읽어온다.
모델 사용법의 이해
컨트롤러는 @RequestMapping 어노테이션이 적용된 메서드에서 파라미터로 모델(Model), 커맨드 객체 등을 받아 파라미터로 받은 객체에 데이터를 저장하고 다시 뷰에 전달해 뷰에서 데이터를 사용할 수 있게 한다.
모델의 내부방식
요청이 오면 Map 변수를 만들고 리퀘스트 매핑의 호출에 의해 메서드를 호출하면서 메서드에 객체 변수를 넘겨준다.
리퀘스트매핑의 호출에 실행된 메서드는 파라미터로 받은 model의 데이터를 원하는 만큼 넣고 사용자에게 보여줄 JSP 페이지의 이름을 리턴한다.
다시 컨테이너는 리턴 받은 정보를 이용하여 뷰를 출력하는 메서드를 호출한다.
이때 페이지 이름과 페이지에서 사용할 정보를 파라미터로 모두 넘겨준다.
호출이 종료되면 지역 메서드가 끝났으므로 객체 변수는 메모리에서 제거 된다.
Controller에서 Model의 이해
package com.study.springboot;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class MyController {
@RequestMapping("/")
public @ResponseBody String root() throws Exception{
return "Model & View";
}
@RequestMapping("/test1")
public String test1(Model model) {
//Model 객체를 이용해서, view로 Data 전달
// 데이터만 설정이 가능
model.addAttribute("name", "홍길동");
return "test1";
}
@RequestMapping("/mv")
public ModelAndView test2() {
// 데이터와 뷰를 동시에 설정 가능
ModelAndView mv = new ModelAndView();
List<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
list.add("test3");
mv.addObject("lists", list); //jstl로 호출
mv.addObject("ObjectTest", "테스트입니다."); //jstl로 호출
mv.addObject("name", "홍길동"); //jstl로 호출
mv.setViewName("view/myView");
return mv;
}
}
@RequestMapping의 url이 /인 경우는 @ResponseBody에 의해 내용 자체만 스트링으로 리턴한다. RESTfulAPI로 사용하는 경우에는 이부분에 xml이나 json 데이터를 만들어 리턴하면 된다.
@RequestMapping("/test1")에서 url 호출이 들어오면 test1 메서드가 실행이된다.
이 메서드는 모델을 파라미터로 받아 해당 객체에 값을 키, 밸류 값으로 추가하고 있다.
리턴으로 스트링을 넘기면 뷰 리졸버가 test1.jsp를 찾아온다.
이때 test1.jsp에서는 조금 전 모델의 데이터를 사용하여 뷰에 데이터를 출력할 수 있게 된다.
@RequestMapping(/mv)로 url 호출이 들어오면 test2 메서드가 실행된다.
이 메서드에서는 스트링값을 리턴하지 않고 ModelAndView 라는 객체 변수를 만들어 데이터 정보를 추가하고, 뷰 정보까지 함께 담아 객체 자체를 리턴한다.
커맨드 객체의 이해
스프링은 커맨드(Command) 객체를 지원한다.
http 요청 파라미터의 이름으로 클래스에 세터 메서드를 만들고 이 클래스의 객체(커맨드 객체)를 메서드의 파라미터 값으로 넣어주면, 스프링은 요청 파라미터의 값을 커맨드 객체에 담는다.
package com.study.springboot;
public class Member {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
이런 형태의 객체를 커맨드(Command) 객ㅊ라고 부른다.
스프링의 데이터 트랜스퍼 오브젝트(Data Transger Object , DTO) 객체와 똑같이 생겼다는 것을 알 수 있다.
DB 테이블과 관련해서 이야기 할 때는 DTO, 파라미터와 관련해서 이야기 할 때는 커맨드 객체라고 하면 된다.
package com.study.springboot;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping("/")
public @ResponseBody String root() throws Exception{
return "Form 데이터 전달받아 사용하기";
}
@RequestMapping("/test1")
public String test1(HttpServletRequest httpServletRequest, Model model) {
String id = httpServletRequest.getParameter("id");
String name = httpServletRequest.getParameter("name");
model.addAttribute("id", id);
model.addAttribute("name", name);
return "test1";
}
@RequestMapping("/test2")
public String test2(@RequestParam("id") String id, @RequestParam("name") String name, Model model) {
//파라미터가 많아지면 풀편해진다.
model.addAttribute("id", id);
model.addAttribute("name", name);
return "test2";
}
@RequestMapping("/test3")
public String test3(Member member, Model model) {
//파라미터와 일치하는 빈을 만들어서 사용할 수 있다.
// View 페이지에서 model을 사용하지 않고 member를 사용한다.
return "test3";
}
//패스 자체에 변수를 넣을 수 도 있다.
@RequestMapping("/test4/{studentId}/{name}")
public String getStudent(@PathVariable String studentId, @PathVariable String name, Model model) {
model.addAttribute("id", studentId);
model.addAttribute("name", name);
return "test4";
}
}
/test1 요청에 대한 리퀘스트 매핑 처리
JSP/Servlet에서 사용하던 전형적인 방법으로 리퀘스트(request)에서 파라미터의 이름으로 전달된 데이터를 추출한다.
/test2 요청에 대한 리퀘스트 매핑 처리
@RequsetParam 어노테이션으로 파라미터 변수에 직접 값을 넣어준다.
/test3 요청에 대한 리퀘스트 매핑 처리
파라미터와 이름이 같은 변수를 가진 커맨드 객체를 이용하면 쉽고 간편하게 많은 데이터를 받아서 처리할 수 있다.
또한 이 경우 모델과 별도로 커맨드 객체 자체도 뷰에 전달된다.
/test4 요청에 대한 리퀘스트 매핑 처리
패스 자체에 변수를 넣어줄 수 도 있다.
이 경우 변수라고 알려줘야 사용이 가능하다.
아무런 표시가 없다면 주소인지 변수인지 알 수 없어서 이다.
이 구분을 위해서 @PathVariable이라는 어노테이션을 사용한다.
롬복
롬복(Lombok)은 자바 클래스를 만들 때 흔히 만드는 코드들을 어노테이션을 이요해서 자동으로 만들어주는 유틸리티 라이브러리 이다.
흔히 만드는 코드란 DTO와 같은 모델에서 항상 만들게 되는 게터(Getter), 세터(Setter) 메서드와 상황에 따라 자주 만드는 toString, equals, hashCode와 같은 메서드를 의미한다.
롬복 어노테이션
@Data
Getter & Setter & RequiredArgsConstructor, ToString, EqualsAndHashCode
@Setter
Setter
@Getter
Getter
@NoArgsConstructor
파라미터가 없는 기본 생성자 생성
@AllArgsConstructor
모든 필드의 값을 파라미터로 받는 생성자 생성
@RequiredArgsConstructor
final이나 @NonNull 인 필드 값만 파라미터로 받는 생성자를 만들어 준다.
@Builder
필수 및 선택인자가 많아질수록 생성자 방식보다 가독성이 좋아진다.
또한 자바빈 패턴(Setter를 이용하는 방식) 보다 안전하다. 생성자위에 선언하며, builder().변수(넣을변수). ...build()로 적용한다.
@ToString
toString에서 exclude 옵션을 추가해주면 특정 필드를 결과에서 제외시켜줄 수 있다
@EqualsAndHashCode
equals, hashCode 메서드 생성
'Back-end' 카테고리의 다른 글
JSP 기초 (0) | 2021.08.14 |
---|---|
Spring 용어정리 (2) (0) | 2021.08.11 |
Spring Concept (0) | 2021.08.09 |
서블릿(Servlet) (0) | 2021.08.08 |
웹 프로그래밍 (0) | 2021.08.07 |