스프링 정리
DI (Dependency Injection - 의존성 지원)
스프링은 설정 파일이나 어노테이션을 통해서 객체 간의 의존 관계를 설정할 수 있다. 객체는 의존하고 있는 객체를 직접 생성하거나 검색할 필요가 없음.
IoC (Inversion of Control - 제어의 역전)
스프링이 갖고 있는 핵심적인 기능으로, 자바의 객체 생성 및 관리 책임을 개발자에서 전체 애플리케이션 또는 프레임워크에 위임하는 디자인 패턴을 일컫는다.
AOP (Aspect Oriented Programming - 관점 지향 프로그래밍)
문제를 해결하기 위한 핵심 관심 사항과 전체에 적용되는 공통 관심 사항을 기준으로 프로그래밍 함으로서 공통 모듈을 여러 코드에 쉽게 적용할 수 있다.
Container
컨테이너는 객체의 생성, 사용, 소멸에 해당하는 라이프사이클을 담당한다. 라이프사이클 관리와 Dependency 객체 제공, Thread 관리, 애플리케이션 실행에 필요한 환경 설정을 제공한다.
IoC Container는 객체의 생성과 관계 설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 독립된 컨테이너가 담당한다.
스프링에서 IoC를 담당하는 컨테이너에는 BeanFactory와 이를 확장한 ApplicationContext가 있다.
Spring DI Container
스프링 DI 컨테이너가 관리하는 객체를 빈(Bean)이라고 하고, 이 빈들의 생명주기를
관리하는 의미로 Bean Factory라고 한다.
BeanFactory에 여러 가지 컨테이너 기능을 추가한 것이 ApplicationContext이다.
Bean을 등록, 생성, 조회, 반환하는 것은 BeanFactory의 기능이고 getBean() Method가 정의되어 있다.
ApplicationContext는 BeanFactory의 기능과 Spring의 각종 부가 서비스를 추가로 제공한다. Spring이 제공하는 ApplcationContext의 구현 클래스는 여러가지가 존재한다.
빈 (Bean)
스프링이 IoC 방식으로 관리하는 오브젝트를 말한다. 스프링이 직접 그 생성과 제어를 담당하는 오브젝트만을 Bean이라고 부른다. POJO로 정의한다.
빈 팩토리 (Bean Factory)
스프링이 IoC를 담당하는 핵심 컨테이너이다.
Bean을 등록, 생성, 조회, 반환하는 기능을 담당한다. 라이프 사이클 관리하며 일반적으로 BeanFactory를 바로 사용하지 않고 이를 확장한 ApplicationContext를 이용한다.
빈 생성 범위
스프링 빈은 기본적으로 ‘singleton’으로 만들어진다. 따라서 컨테이너가 제공하는 모든 빈의 인스턴스는 항상 동일하다. 만약 컨테이너가 새로운 인스턴스를 반환하게 만들고 싶은 경우 scope를 ‘prototype’으로 지정한다
방법
@Scope(value = “prototype”)
<bean id=”” class=”” scope=”prototype”/>
범위
- singleton (스프링 컨테이너 당 하나의 인스턴스 빈만 생성)
- prototype (컨테이너에 빈을 요청할 때마다 새로운 인스턴스 생성)
- request (HTTP Request 별로 새로운 인스턴스 생성)
- session (HTTP Session별로 새로운 인스턴스 생성)
스프링 빈 설정 방법
- XML
XML문서 형태로 빈의 설정 메타 정보를 기술한다.
Annotation으로 빈을 설정할 경우 반드시 해당 클래스는 컴포넌트 스캔의 대상이 되어야 함
<bean> → 스프링 컨테이너가 관리할 Bean 객체를 설정한다.
기본 속성 name → 주입 받을 곳에서 호출 할 이름 설정
id → 주입 받을 곳에서 호출할 이름 설정(유일 값)
class → 주입 할 객체의 클래스
<bean id="boardService" class="com.ssafy.board.model.service.BoardServiceImpl">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="dbUtil"/>
</bean>
<bean id="boardController" class="com.ssayf.board.controller.BoardController>
<constructor-arg ref="boardService"/>
</bean>
컨텍스트 설정하기
ApplicationContext context = new ClassPathXmlApplicationContext("com/ssafy/.../ApplicationContext.xml");
BoardController boardController = context.getBean("boardController", BoardController.class);
<contructor-arg> → 하위 태그 이용 또는 속성 이용
객체 주입시 ref=”빈 이름”, 문자열이나 primitive data일 시 <constructor-arg value=”값”/>
의존성 주입 어노테이션
- @Autowired → 주입하려고 하는 객체의 타입이 일치하는 객체를 자동으로 주입한다. 필드, 생성자, Setter에 붙일 수 있다. 필드, Setter에 사용하는 경우 반드시 기본 생성자가 정의되어야 한다. 스프링에서만 사용 가능하며 타입에 맞춰 연결 → 동일한 타입의 bean 여러 개인 경우 @Qualifier(”이름”)으로 식별한다.
- @Resource → 멤버 변수, setter method에 사용 가능, 타입에 맞춰 연결
- @Inject → Framework에 종속적이지 않음. javax 이므로. 멤버 변수, setter, constructor, 일반 메소드 사용 가능. 이름으로 연결
스프링 빈의 생명 주기
빈 생성 → 의존성 주입 → init method 실행 → 빈 사용 → destory method 실행 → 빈 소멸
AOP
AOP 용어
- Target → 핵심 기능을 담고 있는 모듈로 target은 부가기능을 부여할 대상이다.
- Advice → 어느 시점에 어떤 공통 관심 기능(Aspect)을 적용할지 정의 한 것
- Target에 제공할 부가 기능을 담고 있는 모듈이다.
- JoinPoint → Aspect가 적용 될 수 있는 지점
- Pointcut → 공통 관심 사항이 적용될 JoinPoint. Advice를 적용할 target의 method를 선별하는 정규 표현식
- Aspect → Advice + Pointcut, 여러 객체에 공통으로 적용되는 공통 관심 사항
- Weaving → 어떤 Advice를 어떤 Pointcut에 적용시킬 것에 대한 설정
- 스프링은 프록시 기반 AOP를 지원한다. Proxy가 호출을 가로챈다 즉 Proxy는 Target 객체에 대한 호출을 가로 챈 다음 Advice의 부가 기능 로직을 수행하고 난 후 target을 호출한다. 또는 target 실행 후 부가기능 (Advice)를 수행하는 경우도 있음.
Before Advice → target 실행 전에 실행 된다.
After Returning Advice → target이 성공적으로 실행이 끝난 뒤 실행된다.
After Throwing Advice → target 실행 중 예외가 발생한 경우에 실행된다.
After → 성공 실패에 상관없이 target의 종료 후 실행 된다.
Around → 모든 상황에서 구현할 수 있는 advice
MVC 패턴
애플리케이션의 확장을 위해 Model, View, Controller 세가지 영역으로 분리한다.
컴포넌트 간의 결합성이 낮아 프로그램 수정이 용이하다.
장점
화면과 비즈니스 로직을 분리해서 작업할 수 있따.
영역별 개발로 인하여 확장성이 뛰어나다
표준화 된 코드를 사용하므로 공동작업이 용이하고 유지보수성이 좋다.
단점
개발과정이 복잡하다
Spring MVC의 특징
Spring MVC는 Model2 Architecture와 Front Controller 패턴을 Framework 차원에서 제공한다.
구성 요소
DispactherServlet(Front Controller)
- 모든 클라이언트의 요청을 전달받는다. Controller에게 클라이언트의 요청을 전달하고, Controller가 리턴한 결과 값을 view에 반영하여 알맞은 응답을 생성
HandlerMapping
- 클라이언트의 요청 URL을 어떤 Controller가 처리할 지 결정한다.
URL과 요청정보를 기준으로 어떤 핸들러 객체를 사용할지 결정하는 객체이다.
Controller
- 클라이언트의 요청을 처리한 뒤 Model을 호출하고 그 결과를 DispatcherSerlvet에 알려준다.
ViewResolver
Controller가 리턴 한 뷰 이름을 기반으로 Controller의 처리 결과를 보여줄 View를 결정한다.
View
Controller의 처리 결과를 보여줄 응답화면을 생성한다.
Spring MVC 요청 흐름
클라이언트의 요청 →
DispatcherServlet이 해당 요청을 매핑한 Controller가 있는지 찾기 위해 HandlerMapping에게 Controller를 찾아달라고 요청한다 →
HandlerMapping이 Controller를 찾으면 DispatcherServlet이 Controller에게 처리를 요청한다 →
Controller는 요청을 처리하고 그 결과와 반환할 viewName을 리턴한다. → Controller에 보낸 viewName을 전달하여 View를 검색한다 →
ViewResolver가 View를 결정하고 처리 결과를 View에 전달한다 →
처리 결과가 포함된 View를 DispatcherServlet에 전달한다 →
DispatcherServlet은 클라이언트에 응답한다.
web.xml에 DispatcherServlet 등록 및 Spring 설정파일(DispatcherServlet의 설정인 servlet-context.xml과 비 웹 관련 설정인 root-context의 환경 설정.
servlet-context.xml → 뷰 리졸버, 컴포넌트 스캔 대상(주로 컨트롤러), resources 설정
root-context.xml → 데이터베이스 관련 설정, 모델 관련 컴포넌트 스캔 대상 설정
context.xml → 데이터베이스 관련 설정을 주로 함
Spring Web MVC & MyBatis, Transaction
Spring Web MVC & MyBatis, Transaction 관련 환경설정 파일
- pom.xml : 의존관계 라이브러리 설정 등
- web.xml : 현재 웹어플리케이션(웹컨텍스트) 환경설정
- root-context.xml:
- schema: beans,context, aop, mybatis-spring, tx
- 비웹관련 설정
- AOP
- DB
- MyBatis : SqlSessionFactoryBean 설정(name="dataSource", name="mapperLocations", name="typeAliasesPackage")
- 웹관련 설정
- @Service, @Repository/@Mapper(mybatis), @Component
- servlet-context.xml
- schema: beans, context
- css,js,img(웹 resources)
- ViewResolver,
- @Controller,
- Interceptor,
- fileupload, filedownload
- context.xml :
- JNDI DataSource 설정
- DB 컨넥션풀을 위한 설정
- driver, url, user, password
- jndi name, maxTotal(최대 컨넥션갯수) 등
- log4j.xml : logging 관련 설정
- System.out.println("여기까지나왔어요..!!!");
- 개발후에 콘솔출력 메세지를 모두 삭제해야하는 불편함!!
- log level에 따라서 설정만으로 on/off 가능
- log level: debug(개발중 디버깅목적), info(정보-권장), warn(경고), error(오류), fatal(심각) 등
- mapper.xml
- db table과 매핑되는 sql mapper xml
- movie.xml, user.xml
- sql 구문
- ? 매핑되는 부분 : #{property-name} => pstmt.setString(1, "user01") => 'user01': sql data 형식으로 변환!!
- param-data 그대로 적용위한 설정: ${propert-name} => column-name을 param으로 전달받아서 sql에서 사용
- id=""
- parameterTepe="", parameterMap=""
- resultType="", resultMap=""
filter, aop, interceptor 호출 시점
1. client 요청
2. ::FILTER:: (DispatcherServlet이 실행 되기전: 요청/응답객체에 대한 설정 등)
3. dispatcherServlet
4. handlerMapping
5. ::INTERCEPTOR:: (DispatcherServlet이 실행되어 요청이 매핑된 후, @Controller가 실행되기전: 인증, 세션처리등)
6. @Controller
7. ::AOP:: (@Controller에서 @Service 메서드 대상으로 공통기능 처리)
8. @Service
spring 주요 어노테이션
mvc
-
- @Controller -- @Service -- @Repository / @Mapper(spring-mybatis) -- @Component -- @Autowired -- @Qualifier("identifier-name")
request-mapping
-
- @RequestMapping("/test") => get 방식 => 단일 매핑
-
- @RequestMapping({"/index", "/", "/home"}) => 다중 매핑
-
- @RequestMapping(value={"/index"}, method=RequestMethod.Get) -- @RequestMapping(value={"/index"}, method=RequestMethod.Post)
-
- @GetMapping(value={"/index"}) => get 전용 요청 매핑
-
- @PostMapping(value={"/index"}) => post 전용 요청 매핑
-
- get/post를 모두 처리하는 매핑 @RequestMapping(value="/index", method={RequestMethod.Get, RequestMethod.POST})
request parameter(요청 데이터 가져오기)
-
- Domain 속성, DomainDto 객체 -- parameter 매핑하기 @RequestParam("view-key") 타입 변수명 @RequestParam(value="key", required=false) 타입 변수명 @RequestParam(value = "key", required = false, defaultValue = "guest") 타입 변수명 -- Map 객체 @RequestParam Map<String, String>
응답 결과 설정하기
-
- API
Model#addAttribute(K, V) ModelAndView#addObject(K, V) RedirectAttributes # addAttribute(K, V) -- @ModelAttribute("view-key") 타입 변수명
요청에 대한 응답하기
-
- String: return "url-view"; return "forward:/url"; return "redirect:/url";
-
- ModelAndview: ModelAndView mav = new ModelAndView(); mav.addObject(K,V); mav.setViewName("/url"); return mav;
@RequestMapping(value = “/article/write”, method = RequestMethod.GET)
@RequestMapping("/article")
@GetMapping("/write")
public String write()... 동일하다
기존 서비스 CRUD 관련 url
-
- http methods: get, post -- 등록: post, /regist, /write -- 조회: get, /view.do?userid=user01 /detail?memberId=user01 -- 변경: post, /update.do, /modify -- 삭제: get, /delete, /remove
-
- 요청 데이터 전송
form 양식 : body 전송 Query String : url?key=value&key=value
Rest 구성
- 자원(resource) - URI
- 요청(행위:Verb) - http methods (CRUD 기능)
- 요청/응답 데이터 표현(Representations) - JSON, xml 등 언어로 표현
RestAPI
-
- Open API 사용: url 표준화 필요성!!! -- http methods:
등록 : POST => http://localhost:8080/context-name/uri 조회 : GET => http://localhost:8080/context-name/uri/user01 변경(전체속성) : PUT => http://localhost:8080/context-name/uri 변경(부분속성) : PATCH => http://localhost:8080/context-name/uri 삭제 : DELETE => http://localhost:8080/context-name/uri/user01
-
- frontend가 구현되지 않았어도 backend 개발 및 테스트 가능
postman 같은 RestAPI Client tools 활용 가능 SpringBoot + Swagger(API 제공 및 restapi 테스트 가능한 도움말)
RestAPI 관련 어노테이션 및 API
-
- 어노테이션
@RestController => @Controller + @ResponseBody => 모든 서비스가 RestAPI 서비스를 제공 => @ResponseBody 메서드 선언부에 생략!!! => class 선언부에 선언
>> @ResponseBody(변경!!)
=> @Controller의 특정 서비스에 대해서만 제한적으로 RestAPI 제공
=> method 선언부에 선언: @RestController 인 경우에는 생략 가능!!
=> 응답 데이터 JSON 변환
=> 응답시 뷰페이지 이동이 아니라 json 데이터 응답
>> @RequestBody(변경!!)
=> method 선언부의 파라미터에 선언
=> 요청 데이터 JSON 변환
=> 요청 json 데이터를 원하는데이터 타입(Java 객체)으로 바인딩
>> @PathVariable("property-name")
=> 요청 URL(패스)에 있는 변수(값)을 파라미터 데이터로 추출
>> @CrossOrigin("*") : 모든 도메인 허용
>> @CrossOrigin(origins="<http://domain1.com>, <http://domain2.com>") : 특정 도메인들만 허용, ,(컴마) 구분 표기
-
- API
ResponseEntity<?> 반환 => header + body(상태코드) => ResponseEntity는 http의 상태코드, 헤더 및 본문을 포함할 수 있는 클래스 => RestFul API에서 사용
RestAPI 개발 관련 파일
- pom.xml : json 관련 의존관계 추가 <!-- json : RESTAPI --> <jackson-databind-version>2.16.1</jackson-databind-version> <org.json-version>20240205</org.json-version>
- <!-- json : RESTAPI --> <jackson-databind-version>2.16.1</jackson-databind-version> <org.json-version>20240205</org.json-version>
- 컨트롤러 클래스가 변경
- @Controller ==> @RestController로 변경!!!
RestAPI url 지정시 주의사항!!!
-
- url 맨뒤에 "/" 붙여서는 안됨!!!!
요청 쿼리 스트링에 "name" 필드가 없을 경우 즉, /user 와 같이 @RequestParam이 적용된 필드가 없으면 Bad Request, Required String parameter 'name' is not present 라는 예외를 발생시킵니다.
이를 해결하기 위해 @RequestParam(required = false) 와 같이 required 속성을 추가하면 해당 필드가 쿼리스트링에 존재하지 않아도 예외가 발생하지 않습니다.
Spring Web Application의 동작 원리
web.xml을 로딩한다 →
ContextLoaderListener가 root-context.xml을 읽어 들인다 →
비 웹 클래스인 Service, Dao가 생성된다 →
DispatcherServlet이 생성된다 →
servlet-context.xml을 읽어 환경 설정한다 →
클라이언트 요청이 온다 →
컨트롤러가 동작한다.
Interceptor → Controller가 요청을 처리하기 전/후 처리.
공통 코드 사용으로 코드 재사용성 증가한다.
HandlerInterceptor method
- preHandle → Controller method가 실행되기 전 호출, false를 반환하면 request를 바로 종료한다
- postHandler → Controller method 실행 직후 view 페이지가 렌더링 되기전에 호출
- afterCompletion → view 페이지가 렌더링 되고 난 후 호출, 예외가 발생하여도 실행
정리
Filter, Interceptor, AOP 비교
필터
- Dispatcher Servlet에 전달되기 전(후)에 동작한다.
- 스프링에 무관한 web자원에 대한 처리.
- 사용자 인증이나 권한 검사와 같은 인증 프로세스
- 문자열 인코딩, 웹 보안 관련 기능
인터셉터
- 요청이 Controller에 전달 되기 전(후)에 동작한다.
- Controller에 관한 요청과 응답에 대한 처리
- 로그인이나 사용자 권한 관리 같은 인증 프로세스, 사용자 세션 처리, 프로그램 실행시간 측정
AOP
- Controller 처리 이후 주로 Business Logic에 대한 처리
- 로깅, 트랜잭션, 에러처리
Mybatis
자바 객체와 SQL문 사이의 자동 Mapping 기능을 지원하는 ORM 프레임워크
특징 → 쉬운 접근성과 코드의 간결함, SQL문과 프로그램이 코드의 분리
주요 컴포넌트 역할
MyBatis 설정파일 → 데이터베이스의 접속 주소 정보나 객체 alias, mapping파일 경로 설정
SqlSessionFactoryBuilder → mybatis 설정 파일을 바탕으로 SqlSessionFactory를 생성
SqlSessionFactory는 SqlSession을 생성
SqlSession은 핵심적인 역할을 하는 class로 SQL 실행이나 Transaction 관리를 실행
mapper파일 → SQL문과 ORMapping을 설정.
스프링부트
Spring의 경우 Application 개발시 사전에 많은 작업이 필요했다. 설정 파일 등
Spring boot는 웹 서버가 내장되어 있어 WAS를 추가로 설치하지 않아도 개발 가능하다. 또한 복잡한 설정을 자동으로 처리해준다.