본문 바로가기
기술/Spring-Boot

항해99 11/16(화) 스프링 TIL

by Zabee52 2021. 11. 17.

Jpa Repository 커스텀

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

JPA를 처음 접했을 때 느낀 의문점은 어떻게 이걸로 복잡한 쿼리를 작성할 수 있을까? 라는 것이었다. 위 문서는 이 의문에 대한 해답을 제시해주는 문서이다.

 

요약하자면 내용은 이렇다.

리포지토리에 상단 문서에 맞는 양식으로 메소드를 선언해주면 알아서 그 로직을 구성해준다.

자바답지 않게 친절한척하는데 조금 불친절하면서 그 형식이 충격적이다. 메소드명으로 어떻게 기능을 구분지어 구현시킬 생각을 했는지 참 창의적이다.

간단하게 내가 작성해본 것을 예시로 설명해보겠다.

public interface BoardRepository extends JpaRepository<Board, Long> {
    List<Board> findAllByOrderByModifiedAtDesc();
}

Board 테이블의 데이터 전부를 modifiedAt 필드를 기준으로 내림차순해서 반환 해줘!

SQL로 치자면

SELECT *
  FROM BOARD
 ORDER BY ModifiedAt DESC;

이정도 되지 않을까. SQL 안 쓴지 2년이 넘어서 문법이 맞는진 모르겠다.

정말 신기한 점은 내가 선언했던 Entity의 Column 요소도 이름에 가져다 쓸 수 있다는 점이다.

사용상의 주의사항으로는, 구문을 어떻게 구성하는가에 따라 매개변수도 입력받을 수 있으니 참고하자. 자세한건 상단의 사이트에서 확인해보면 되겠다.

 

 

Scheduler

- 정해진 주기로 특정 메소드를 반복시킬 수 있는 기능

@RequiredArgsConstructor // final 멤버 변수를 자동으로 생성합니다.
@Component // 스프링이 필요 시 자동으로 생성하는 클래스 목록에 추가합니다.
public class Scheduler {

    private final ProductRepository productRepository;
    private final ProductService productService;
    private final NaverShopSearch naverShopSearch;

    // 초, 분, 시, 일, 월, 주 순서
    @Scheduled(cron = "0 0 1 * * *")
    public void updatePrice() throws InterruptedException {
        System.out.println("가격 업데이트 실행");
        // 저장된 모든 관심상품을 조회합니다.
        List<Product> productList = productRepository.findAll();
        for (int i=0; i<productList.size(); i++) {
            // 1초에 한 상품 씩 조회합니다 (Naver 제한)
            TimeUnit.SECONDS.sleep(1);
            // i 번째 관심 상품을 꺼냅니다.
            Product p = productList.get(i);
            // i 번째 관심 상품의 제목으로 검색을 실행합니다.
            String title = p.getTitle();
            String resultString = naverShopSearch.search(title);
            // i 번째 관심 상품의 검색 결과 목록 중에서 첫 번째 결과를 꺼냅니다.
            List<ItemDto> itemDtoList = naverShopSearch.fromJsonToItems(resultString);
            ItemDto itemDto = itemDtoList.get(0);
            // i 번째 관심 상품 정보를 업데이트합니다.
            Long id = p.getId();
            productService.updateBySearch(id, itemDto);
        }
    }
}
@EnableScheduling // 스프링 부트에서 스케줄러가 작동하게 합니다.
@EnableJpaAuditing // 시간 자동 변경이 가능하도록 합니다.
@SpringBootApplication // 스프링 부트임을 선언합니다.
public class Week04Application {

    public static void main(String[] args) {
        SpringApplication.run(Week04Application.class, args);
    }

}

네이버 쇼핑 API를 이용해 만든 Scheduler 예시다.

내가 등록해놓은 관심상품의 최저가를 특정 시간마다 갱신해주는 것이다. 위의 예시는 매일 01시에 갱신해주도록 되어있다.

만드는 순서는 다음과 같다.

1. 스프링 어플리케이션 클래스에 @EnableScheduling 어노테이션 선언
2. Scheduler가 되어줄 클래스에 @Component(Bean 인스턴스 생성) 어노테이션 선언
3. 대상 메소드에 @Scheduled(cron = "초 분 시 일 월 주") 어노테이션 선언. * 사용시 와일드카드 적용.
4. 메소드 구현

좀 복잡해보인다고 생각했는데, 정리해보니 그냥 어노테이션만 해주면 구현을 제외한 모든 작업은 스프링이 해준다. 엄청 간편하다. 

 

 

Bean

- 사용자가 직접 생성한 인스턴스가 아닌, Spring IoC 컨테이너가 관리해주는 객체들을 의미한다.

 

 

[Spring] Spring의 IoC(Inversion of Control)과 Bean

컴퓨터/IT/알고리즘 정리 블로그

chanhuiseok.github.io

위 사이트에서 정리를 알차게 해 두어서 위 내용을 보고 Bean으로 등록하는 방법을 간략하게 정리해보겠다.

 

1. @Component 어노테이션 사용

-> @SpringBootApplication 어노테이션 내부에는 @ComponentScan 어노테이션이 정의되어 있는데, 이 어노테이션은 하위 클래스를 돌면서 @Component를 찾는 역할을 수행한다. 이후 확인된 Components는 Bean에 등록이 된다.

@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 {
...
}

실제로 SpringBootApplication 내부는 위처럼 @ComponentScan이 선언되어있음을 알 수 있다.

 

2. Bean 설정파일에 직접 등록

-> @Component 어노테이션을 사용하는 어노테이션인 @Configuration을 이용해 직접 등록해줄 수 있다.

@Configuration
public class SampleConfiguration {
    @Bean
    public SampleController sampleController() {
        return new SampleController;
    }
}

 

1.은 클래스 단위, 2.는 메소드 단위에서 등록한다는데, 둘의 차이점은 직접 구분해서 써야하는 상황을 맞이해봐야 알 것 같다.....

댓글