토비의 스프링 3.1 vol2

gga·2021년 9월 13일
0

spring

목록 보기
3/3
post-thumbnail

1장 IoC 컨테이너와 DI

빈 등록

@Configuration // 빈을 등록할 때 사용
@ComponentScan(basePackageClass = Application.class)
pubic class Config {
    @Bean // Singleton으로 생성됨
    public ...() { }
    
    @Bean
    public ...() { }
}
@Component // 직접 생성한 클래스를 빈으로 등록
@Scope("prototype") // 스코프 정의
public class BeanA {
    private BeanB beanB;
    
    @Autowired // 같은 타입의 빈을 자동 주입
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }
}

@Component
public class BeanB { }

자동주입

@Autowired

같은 타입의 빈을 자동 주입한다.

@Qualifier

@Qualifier에 지정된 이름과 같은 이름의 빈을 찾아 자동 주입한다.

@Autowired
@Qualifier("BeanC")
private BeanC bean1;

@Resource

필드명과 이름이 같은 빈을 찾아 자동 주입한다.

@Resource(name="beanD")
private BeanA beanD;

@Value

  • 기본 타입과 문자열 타입은 @Value를 사용해 주입할 값을 지정할 수 있다.
  • properties 값을 불러올 수 있다.
    ex) application.properties 파일에 name=spring 작성되어있다면,
@Value("${name}")
private String name;

SpEL(Spring Expression Language)

스프링에서 지원하는 표현식이다.
#{표현식}

  • 스프링 빈에 접근하여 프로퍼티 값을 가지고 올 수 있다.
@Configuration
public class AppConfig {
   @Bean
   public void hello() {
      String name = "Spring";
   }
   
   @Bean
   public void names() {
      @Value("#{hello.name}")
      String helloname;
   }
}

컨테이너가 자동등록하는 빈

스프링 컨테이너는 초기화 과정에서 몇 가지 빈을 기본적으로 등록해준다.
ApplicationContext, BeanFactory, ResourceLoader, ApplicationEventPublisher, systemProperties, systemEnvironment

스코프

싱글톤

기본적으로 스프링의 빈은 싱글톤으로 만들어진다. 즉, 애플리케이션 컨텍스트마다 빈 오브젝트는 한 개만 만들어진다.

프로토타입

프로토타입 스코프를 갖는 Bean은 요청이 있을 때마다 컨테이너가 생성, 초기화, DI까지 해주지만 일단 Bean을 제공하고 나면 컨테이너는 더 이상 Bean 오브젝트를 관리하지 않는다. 따라서 프로토타입 Bean 오브젝트는 DL이나 DI를 통해 컨테이너 밖으로 전달된 후에는 오브젝트를 가져가 코드나 DI로 주입받은 다른 Bean이 오브젝트를 관리하게 된다.

DL(Dependency Lookup) : 의존성 검색
Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 찾는 것을 의미한다.

@Component
@Scope("prototype")
public class ServiceRequest { ... }

2장 데이터 액세스 기술

DataSource : DB 연결 풀 기능을 지원한다.

DB Connection pool

WAS가 실행되면서 connection 객체를 미리 pool에 저장해두었다가 HTTP 요청에 따라 pool에서 connection 객체를 가져다 쓰고 반환한다.

이와 같은 방식으로 물리적인 데이터베이스 연결 부하를 줄이며, pool에 미리 connection이 생성되어 있어서 요청 시 생성 시간이 소비되지 않는다.

application.properties 파일에서 예약된 키에 프로퍼티 설정을 하면 자동으로 jpa 설정과 DataSource 빈을 생성해준다.

JDBC 일반적인 코드 구성

커넥션과 sql을 일일히 작성해야 된다.

JdbcTemplate

sql Mapper로 인해 개발 코드는 줄었다.

JPA

JPA가 적절한 sql을 생성해준다.

EntityManager를 사용해 JPA 코드를 작성한다. EntityManager를 직접 주입받으려면 @PersistenceContext 애노테이션을 사용한다.

Java Persistent API 약자로 자바의 ORM(Object Relational Mapping) 기술 표준으로 인터페이스의 모임이다.

ORM(Object Relational Mapping)
객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 것을 뜻한다.

CRUD

  • 저장 : jpa.persist(member);
  • 조회 : Member member = jpa.find(memberId);
  • 수정 : member.setName("변경할 이름");
  • 삭제 : jpa.remove(member);

엔티티와 매핑정보

@Entity
public class Memeber {
@Id
int id;

@Column(length=100)
String name;

@Column(nullable=false)
double point;
}

@Entity

JPA가 관리하는 엔티티 오브젝트로 지정한다. 다른 설정이 없으면 매핑되는 테이블 이름은 클래스 이름을 따른다.

@Id

DB의 기본키에 대응되는 필드이다.

@GeneratedValue

자동 생성키

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
  • GenerationType.AUTO : Oracle은 SEQUENCE, MYSQL은 IDENTITY를 선택한다.
  • GenerationType.IDENTITY : 기본키 생성을 DB에 위임한다.
  • GenerationType.SEQUENCE : DB 시퀀스 오브젝트를 사용한다. @SequenceGenerator 작성이 필요하다.
  • GenerationType.TABLE : 키 생성 전용 테이블을 만들어서 시퀀스처럼 사용한다.

@Column

@Column(name="이름") 속성 name에 해당하는 이름이나 name 속성이 없을 경우, 필드 이름이 DB 컬럼에 매핑된다.

하이버네이트

자바 기반의 ORM 프레임워크 오픈소스이다.

3,4장 Spring MVC

DispatcherServlet과 MVC 아키텍처

스프링이 직접 제공하는 서블릿 기반의 MVC 프레임워크이다. 프론트 컨트롤러 역할을 하는 DispatcherServlet을 핵심 엔진으로 사용한다.

(2) DispatcherServlet에서 핸들러 매핑을 통해 컨트롤러로 HTTP 요청 위임한다.

(3) 모델을 생성하고 정보를 넣어준다.

(4) 템플릿 파일 이름을 리턴해 준다.

(5) 뷰 오브젝트에게 모델을 전달해준다.

(6) 클라이언트에게 돌려줄 최종 결과물을 생성한다.

@RequestMapping

URL 패턴

@RequestMapping("/hello")
@RequestMapping("/view.*")
@RequestMapping("/admin/**/user")
@RequestMapping("/hello", "/hi")

패스 변수(path variable)를 사용하여 컨트롤러 메소드에서 파라미터로 값을 전달 받을 수 있다.

@RequestMapping("/user/{userid}")
public String index(@PathVariable String userid) {
   return userid + " 님이 접속하였습니다." ;
}

HTTP 요청 메소드

@RequestMapping("/user/add", method=RequestMethod.GET)

요청 파라미터

/user/edit?type=admin 이라는 URL로 요청 받을 시
@RequestMapping("/user/edit", params="type=admin")

type이라는 파라미터가 아예 존재하지 않는 경우 매핑
@RequestMapping("/user/edit", params="!type")

HTTP 헤더

@RequestMapping("/view", headers="content-type=text/*")
= @RequestMapping("/view", consumes="text/*")

@ResponseBody

@ResponseBody가 없다면 스트링 타입의 리턴 값은 뷰 이름으로 인식된다. 하지만 @ResponseBody는 리턴 값을 스트링 타입을 지원하는 메시지 컨버터를 통해 HTTP 응답 메시지 본문으로 전환된다.

@RequestMapping("hello")
@ResponseBody
public String hello() {
   return "<html><body>Hello Spring</body></html>";
}

@RequestBody

이 어노테이션이 붙은 파라미터에는 HTTP 요청 본문(Body) 부분이 그대로 전달된다. XML이나 JSON 기반의 메시지를 사용하는 요청의 경우에 사용된다.
public void message(@RequestBody String body) { ... }
위와 같다면 요청 메세지의 본문이 모두 스트링으로 변환돼서 body 변수에 전달된다.
보통 @ResponseBody와 함께 사용된다.

Model

Controller에서 생성된 데이터를 View 로 전달할 때 사용하는 객체이다.
model.addAtrribute("키", "값");

@RequestMapping("/hello")
public String hello(@RequestParam String name, Model model) {
   model.addAttribute("name", name);
   return "hello";
}

@RequestParam

HTTP 요청 파라미터를 받기 위해 사용한다. 1:1로 매칭된다.
예를 들어 사용자가 /?name=sik로 요청한다면, 매개변수 name에 sik이 매핑된다.

@ModelAttribute

@RequestParam 과 비슷하나 파라미터를 DTO/VO로 받을 경우 사용한다.

JSP EL

스프링은 JSP 뷰에서 모델 맵에 담긴 오브젝트를 JSP EL을 통해 접근할 수 있다.

컨트롤러 메소드에서 다음과 같이 모델 오브젝트를 추가했다고 하면,
model.addAttribute("name", "Spring");

JSP 뷰에서 다음과 같이 EL을 사용하여 모델 오브젝트의 값을 출력할 수 있다. <div>이름 : ${name}</div>

RedirectAttributes와 리다이렉트 뷰

핸들러 메소드에서 리다이렉트를 사용하려면 Redirectviewredirect: 접두어가 붙은 뷰 이름을 리턴하면 된다.
RedirectAttributes는 리다이렉트 뷰에서 필요하지 않은 Model 정보를 제외하고 제공해 준다.

public String saveForm(@ModelAttribute("user") User user, RedirectAttributes ra) {
   ...
   ra.addFlashAttribute("message", "저장됐습니다");
   ra.addAttribute("status", status);
   return "redirect:/경로";
}

flash attributes
RPG(POST/REDIRECT/GET) 패턴을 보완하기 위해 등장하였다.
flash attributes는 수명이 짧다. 따라서 리다이렉션 직전에 기본 저장소에 임시로 저장된다. 리다이렉션 후 후속 요청에 계속 사용할 수 있다가 사라진다.

이렇게 저장된 플래시 애트리뷰트는 다음 GET 요청에서 자동으로 모델에 추가된다.

<div class="flash_message">
  ${message}
</div>

5장 AOP

@AspectJ AOP

@Aspect 클래스에는 포인트컷과 어드바이스를 정의할 수 있다.
클래스를 aspect로 사용하려면 먼저 빈으로 등록해야 한다.

@Component
@Aspect
public class SimpleMonitoringAspect { 
   @Around("execution(* hello(..))")
   private void all() { }
}

pointcut과 advice에 대한 자세한 설명은 이전에 작성한
Spring & Spring Boot 글의 3. AOP 참조

advice

어드바이스는 다섯 가지 종류가 있다.
@Before, @After, @AfterReturning, @AfterThrowing, @Around

@Around

@Around("myPointcut()")
public Object doNothing(ProceedingJoinPoint pjp) throws Throwable {
   Object ret = pjp.proceed();
   return ret;
}

ProceedingJoinPointproceed()는 타깃 오브젝트의 메소드를 호출하고 그 결과값을 받을 수 있다.

@AfterReturning

@AfterReturning(pointcut="myPointcut()", returning="ret")
public void logReturnValue(Object ret) { ... }

타깃 오브젝트의 메소드가 정상 종료된 후에 호출되기 때문에 메소드의 리턴 값을 참조할 수 있다. 어노테이션의 returning 엘리먼트를 이용해서 리턴 값을 담을 파라미터 이름을 지정한다.

@AfterThrowing

@AfterThrowing(pointcut="daoLayer()", throwing="ex")
public void logDAException(DataAccessException ex) { ... }

throwing 엘리먼트를 이용해서 예외를 전달받을 메소드 파라미터 이름을 지정할 수 있다. 파라미터 타입이 발생한 예외와 일치할 경우만 어드바이스가 호출된다.

pointcut

return 타입은 반드시 void로 한다.

@Pointcut("execution(* *hello*(..))")
private void hello() {}

()까지 포함해서 포인트컷 이름으로 사용된다.

@Around("execution(* hello.hellospring..*(..)) && hello()")

6장 테스트 컨텍스트 프레임워크

JUnit : 자바 테스트 프레임워크

스프링은 테스트가 사용하는 컨텍스트를 캐싱해서 여러 테스트에서 하나의 컨텍스트를 공유할 수 있는 방법을 제공한다.

@Test

@Test가 붙은 모든 메소드가 각각 하나의 독립적인 테스트가 된다.

class BeanScannerTest {
    @Test
    @DisplayName("빈 등록 확인")
    void beanFind() { ... }
}

@DirtiesContext

컨텍스트의 빈 오브젝트를 조작하고 수정하는 테스트일 경우 사용한다. @DirtiesContext 어노테이션이 붙은 테스트가 수행되고 나면 스프링은 현 테스트 컨텍스트를 강제로 제거한다.

롤백 테스트

@Test@Transactional 어노테이션을 함께 사용했을 경우 테스트가 끝나면 롤백된다.

롤백하고 싶지 않다면 @Rollback(false)를 사용한다.

@Transactional

@Test
@Transactional
void txTest() {
   dao.deleteAll();
   dao.add(new Member(10, "Spring", 7.8));
   assertThat(dao.count()).isEqualTo(1);
}

@Before, @After은 트랜잭션 안에서 실행된다.

@BeforeTransaction

트랜잭션이 시작되기 전에 호출한다.

@BeforeTransaction
public void setUpBeforeTx() { ... }

@AfterTransaction

트랜잭션이 끝난 후에 호출된다.

@AfterTransaction
public void tearDownAfterTx() { ... }

자바 코드 설정정보와 프로파일 활용

@ContextConfiguration

AppConfig 설정을 이용한 테스트를 할 경우 @ContextConfiguration의 class 앨리먼트를 이용하여 사용한다.

@Configuration
@ComponentScan
public class AppConfig { ... }
@ContextConfiguration(classes=AppConfig.class)
@Transactional
public class DaoTest{ ... }

테스트에서 사용할 @Configuration 클래스가 여러 개라면 다음과 같이 {}에 모두 넣어준다.

@ContextConfiguration(classes={AppConfig.class, OtherConfig.class})

@ContextConfiguration에 아무 값도 지정하지 않는다면, 디폴트 설정정보가 있는지 확인하고 이를 사용한다.

다음 코드를 살펴보자.

@ContextConfiguration
public class MyAppTest{
   ...
   
   @Configuration
   static class MyAppConfig() { ... }
   
   @Configuration
   static class MyExtraConfig() { ... }
}

테스트 클래스에서 중첩된 스태틱 클래스 중 @Configuration이 붙은 것을 모두 디폴트 설정정보로 사용한다.

@ActiveProfile

@ActiveProfiles("default")
public class MyAppTest { ... }

다음 테스트 클래스 설정정보 중에서 default 프로파일만 활성화해서 테스트를 수행한다.

@Configuration 
public class HelloConfig {
   @Bean
   @Profile("default")
   HelloService defaultService() {
      return new DefaultService();
   }
   
   @Bean
   @Profile("trend")
   HelloService trendService() {
      return new TrendService();
   }
}

7장 기타 기술

@Scheduled

@Scheduled(cron="0 0 12 1 * MON-FRI")
public void checkSystemStatus() { ... }

@Async

@Async가 부여된 메소드는 비동기 방식으로 실행된다. 리턴타입은 void 또는 Future 타입이여야 한다.

@Async
void complexWork(String s) { ... }

캐시

캐시는 기본적으로 성능 향상을 위해 사용한다. 캐시는 값을 저장해두고 불러오기 때문에 반복적으로 동일한 결과가 돌아오는 작업에 주로 이용된다.

캐시 기능을 사용하려면 설정 파일에 @EnableCaching 어노테이션을 추가한다.

@EnableCaching
@Configuration
public class AppConfig { ... }

그 다음으로 캐시를 관리해줄 캐시매니저를 빈으로 등록해 주어야 한다. 스프링은 다음과 같은 캐시 매니저들을 제공하고 있다.
ConcurrentMapCacheManager, SimpleCacheManager, EhCacheCacheManager, CompositeCacheManager, NoOpCacheManager

@Cacheable

스프링의 캐시 서비스 추상화는 AOP를 이용한다. 보통 메소드 단위로 사용한다.

@Cacheable("product")
public Product bestProduct(String productNo) { ... }

다음과 같은 순서로 bestProduct 메소드가 호출된다면

bestProduct("A-001"); // (1)
bestProduct("A-001"); // (2)

(1)
product 캐시에 저장된 오브젝트가 없을 경우 캐시 어드바이스틑 bestProduct() 메소드를 실행한다. 그리고 메소드의 결과가 리턴되면 리턴 값인 Product 오브젝트를 A-001 키 값으로 캐시에 저장한다.

(2)
메소드가 실행 될 때 이미 product 캐시에 저장되어 있기 때문에 캐시에 저장된 Product 오브젝트를 가져와 바로 리턴한다. 이 때는 bestProduct() 메소드가 실행 되지 않는다

@CacheEvict

캐시를 제거하는 방법은 2가지가 있다.

  • 일정한 주기로 캐시 제거
  • 캐시에 저장한 값이 변경될 때 캐시 제거

캐시 제거에 사용될 메소드에 어노테이션을 붙여준다.

@CashEvict(value="product", key="#product.productNo")
public void updateProducts(Product product) { ... }

productNo와 같은 키 값을 가진 캐시만 제거된다.

@CachePut

캐시에 값을 저장하는 용도로만 사용한다. 저장된 캐시의 내용을 사용하지는 않고 항상 메소드를 실행한다.

ex ) usertypeadmin인 경우에만 캐시에 저장한다.

@CachePut(value="user", condition="#user.type == 'admin'")
public User addUser(User user) { ... }

참고

토비의 스프링 3.1 Vol2

Spring flash attributes
https://www.baeldung.com/spring-web-flash-attributes

DB 커넥션 풀
https://linked2ev.github.io/spring/2019/08/14/Spring-3-%EC%BB%A4%EB%84%A5%EC%85%98-%ED%92%80%EC%9D%B4%EB%9E%80/

0개의 댓글