framework/spring

1. 데이터 접근 기술 시작

wooweee 2023. 9. 24. 14:15
728x90

1. 데이터 접근 기술 

  • SQLMapper
    1. JdbcTemplate
    2. MyBatis
  • ORM 관련 기술
    1. JPA, Hibernate
    2. 스프링 데이터 JPA
    3. Querydsl

 

2. 프로젝트 구조 설명

 

2.1. 패키지 구조

 

2.2. Domain

  • ItemSearchCond & ItemUpdateDto
    • 두개 파일 모두 DTO를 의미
    • 팀 내, 규칙으로 파일명 설정
      • Cond같은 경우 목적이 검색조건 data를 넘겨 주는 것이기 때문에 dto이긴 하지만 Cond로 합의
      • 나머지 data 이동 Domain은 끝에 Dto 붙이는 규칙 설정
      • 완전 핵심 dto의 경우 딱 이름만 씀

  • Dto(data transform object)
    • 데이터 전송 객체
    • 기능은 없고 data를 전달만 하는 용도록 사용되는 객체

  • Dto 위치
    1. 최종적으로 사용하는 단계에 위치 시킴. 
      ex) 회원 조회시, 처음으로 data를 dto에 저장하는 위치는 repository. 따라서 repository에 dto를 위치시킴

    2. 애매한 경우, Domain 폴더를 따로 두어서 보관

dto 위치

3. bootApplication 설정

  • configFile : 수동 bean 등록 file

  • testDataInit : 초기 test file
    • @EventListener(ApplicationReadyEvent.class)
    • @PostConstruct

  • bootApplication
    • @Import
    • @SpringBootApplication(scanBasePackages = "")
    • app 내부 @Bean, @Profile("")

  • application.properties
    • main : local
    • test  : test

 

  • 해당 파일 code
package hello.itemservice.config;

@Configuration
public class MemoryConfig {
    @Bean
    public ItemService itemService() { return new ItemServiceV1(itemRepository()); } // bean 등록 + 의존관계 주입
    @Bean
    public ItemRepository itemRepository() { return new MemoryItemRepository(); } // bean 등록
}

/* * 수동 bean 설정
* 현재 sample code에서 수동 bean 등록과 자동빈 등록 annotation이 같이 존재해서 헷갈렸는데

* 실제적으로 실험을 해본 결과,
* 각 service, repository 구현체에서 @Service, @Repository 제거해도 문제가 발생하지 않음
* 그냥 중복으로 되어있다고 참고만 할 것, 원래는 2개 중 1개만 수행함

* 수동 bean 등록 복습
*  1. bean 등록    : bean 명은 method 명 | bean 객체는 new로 새로 생성한 객체
*  2. 의존 관계 주입  : 해당 class가 생성자 주입을 받는 경우 빈으로 등록될 객체를 매개변수로 넘겨주는 과정
*  결론
*  - 수동 bean 등록 시, 해당 class에서 빈 생성 및 등록과 의존관계 주입이 일괄적으로 수행
* */
package hello.itemservice;

@Slf4j
@RequiredArgsConstructor
public class TestDataInit {
    private final ItemRepository itemRepository;
    /**
     * 확인용 초기 데이터 추가
     */
    @EventListener(ApplicationReadyEvent.class)
    public void initData() {
        log.info("test data init");
        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
    }
}
package hello.itemservice;

// 현재 componentScan의 영역이 web 이하이므로 @Import로 수동 bean 등록 내용을 받아야 springContainer가 받아와야하는 bean을 받아올 수 있다.
@Import(MemoryConfig.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {
   public static void main(String[] args) {
      SpringApplication.run(ItemServiceApplication.class, args);  //web 내부 @Component만 bean으로 등록한다는 의미
   }
   @Bean // 수동 bean 등록 방법 중 직접 등록 방법
   @Profile("local") // application.properties의 설정 중 local 환경일 경우 @Bean으로 등록
   public TestDataInit testDataInit(ItemRepository itemRepository) { // 의존관계 주입도 들어감 - ItemRepository
      return new TestDataInit(itemRepository);
   }
}
# main/resources/application.properties
# 해당 설정이 없을 경우, profiles는 default로 지정된다.
spring.profiles.active=local
# test/resources/application.properties

# test package에 지정한 profiles로 boot test 실행시, 해당 profiles가 우선시 된다.
#설정이 없으면 main profiles를 따른다.

spring.profiles.active=test

 

 

3.1. ConfigFile

package hello.itemservice.config;

@Configuration
public class MemoryConfig {
    @Bean
    public ItemService itemService() { return new ItemServiceV1(itemRepository()); } // bean 등록 + 의존관계 주입
    @Bean
    public ItemRepository itemRepository() { return new MemoryItemRepository(); } // bean 등록
}

 

  • sample File에서 해당 service, repository 구현체에 @Component 및 @Autowired가 다 등록이 되어있는데, 수동 bean 등록하는 config file이 존재해서 혼란이 왔음

  • 확인 해본 결과, 각 service, repository 구현체에서 @Service, @Repository 제거해도 문제가 발생하지 않았다.

  • @springBootApplication의 componentScan 범위가 Controller pacakge로 한정이 되어있어서 수동 config파일을 통해서 @Import @Bean 등록을 수행

  • service와 repository의 DI 어노테이션은 관습적으로 들어가있다고만 참고 할 것

 

 

3.2. TestDataInit

package hello.itemservice;

@Slf4j
@RequiredArgsConstructor
public class TestDataInit {
    private final ItemRepository itemRepository;
    
    @EventListener(ApplicationReadyEvent.class)
    public void initData() {
        log.info("test data init");
        itemRepository.save(new Item("itemA", 10000, 10));
        itemRepository.save(new Item("itemB", 20000, 20));
    }
}

 

  • @EventListener(ApplicationReadyEvent.class)
    • AOP를 포함한 spring container가 완전히 초기화를 다 끝내고, 실행 준비가 됬을 경우 해당 어노테이션이 붙은 메서드가 호출
    • AOP설정 초기화는 @Transactional 읽고 proxy 객체 만드는 과정을 의미

  • @PostConstruct
    • @EventListener(ApplicationReadyEvent.class)와 유사한 기능이지만, AOP 같은 부분이 아직 다 처리되지 않은 시점에 호출될 수 있기 때문에, 간혹 문제가 발생

 

3.3. SpringBootApplication

package hello.itemservice;

// 현재 componentScan의 영역이 web 이하이므로 @Import로 수동 bean 등록 내용을 받아야 springContainer가 받아와야하는 bean을 받아올 수 있다.
@Import(MemoryConfig.class)
@SpringBootApplication(scanBasePackages = "hello.itemservice.web")
public class ItemServiceApplication {
   public static void main(String[] args) {
      SpringApplication.run(ItemServiceApplication.class, args);  //web 내부 @Component만 bean으로 등록한다는 의미
   }
   @Bean // 수동 bean 등록 방법 중 직접 등록 방법
   @Profile("local") // application.properties의 설정 중 local 환경일 경우 @Bean으로 등록
   public TestDataInit testDataInit(ItemRepository itemRepository) { // 의존관계 주입도 들어감 - ItemRepository
      return new TestDataInit(itemRepository);
   }
}

 

  • @SpringBootApplication(scanBasePackages = "hello.itemservice.web")
    • scanBasePackages : 해당 pacakge부터 componentScan을 해서 bean을 등록하겠다는 의미

  • @Import("CLASS")
    • 해당 class를 설정파일로 사용한다는 뜻 
    • @Configuration, @Bean으로 수동 주입하는 방법과 동일한 역할
    • @Import하면 해당 class를 빈으로 등록해주어서 수동 주입 할 필요없이 깔끔하게 처리된다.

  • @Bean
    • Application 에 직접적으로 @bean 등록하는 직접 수동 방법
    • @Configuration 없이 (@SpringBootApplication 내부에 있으므로) 자동으로 springContainer에 bean 등록 됨

    • bean 등록 방법 3가지
      1. 자동 등록 - @ComponentScan - @Controller, @Service, @Repository - @Autowired
      2. 수동 등록 (Config file 이용) - @Configuration @Bean
      3. 수동 등록 (boot file에 직접 등록) - @Bean
  • @Profile
    • spring은 로딩 시점에 application.propertiesspring.profiles.active 을 읽어서 프로필로 사용
    • @Profile("local")이 설정된 method나 설정, class는 application.properties가 local인 경우 작동

3.4. Profile

# main/resources/application.properties
# 해당 설정이 없을 경우, profiles는 default로 지정된다.
spring.profiles.active=local
# test/resources/application.properties

# test package에 지정한 profiles로 boot test 실행시, 해당 profiles가 우선시 된다.
#설정이 없으면 main profiles를 따른다.

spring.profiles.active=test

 

  • Profile
    • application.properties의 spring.profiles.active = "" 로 profile 지정이 가능
    • 따로 지정을 하지 않을 시, profile은 (default)로 설정
    • 관습적 분류 : dev, local, prod, test(test패키지에서 사용)
  • profile별 사용 방식
    1. @profile
    2. application-{profile}.properties || application-{profile}.yml

  • 동작 방식
    • 공통설정은 application.properties 에서 수행

    • 이후 profilte 지정 시,
      1) 해당 application-{profile}.yml 설정 내용 수행
      2) @Profile("{profile}")에 부합한 method 수행

    • 단, 중복 코드 발생시, 우선권은 application.properties에서 지정한 profile 파일의 설정이 우선권을 가진다.
      = profile설정으로 code를 덮어씌운다.

  • test package의 경우
    • test package 내부에 resources/application.properties를 생성 가능

    • test package에 application.properties 파일 자체가 없는 경우에는 main/resources/application.properties를 따름
    • test package에 applcation.properties가 생기는 순간 부터, test file은 application.properties에 profiles의 존재 유무에 관계없이 해당 application.properties를 따른다.

    • main/resources/application.properties와 test/resources/application.properties에 profile 설정을 안해서 둘다 default를 가지더라도 각각 다른 default임을 헷갈리지 말자