1️⃣ Entity에 사용한 롬복
@Entity
테이블과의 매핑
@Entity가 붙은 클래스는 JPA가 관리하는 것으로, 엔티티라고 불린다.
@DynamicInsert
null 인 필드값이 insert 나 update 시 제외되게 하는 방법은 org.hibernate.annotations. 패키지의
@DynamicInsert (insert 시 null 인필드 제외)
@DynamicUpdate (update 시 null 인필드 제외)
를 사용한다.
@AllArgsConstructor
모든 필드 값을 파라미터로 받는 생성자를 만든다.
https://yuja-kong.tistory.com/99
@NoArgsConstructor
파라미터가 없는 기본 생성자를 생성한다.
@SuperBuilder
@Builder처럼 어떤 객체의 필드값들을 편하게 지정하기 위해서 사용한다. 다만, super라는 말에서처럼 부모 객체를 상속받는 자식 객체를 만들 때, 부모 객체의 필드값도 지정할 수 있게 하기 위해서 사용한다.
@Getter
xxx라는 필드에 선언하면 자동으로 getXxx()(boolean 타입인 경우, isXxx())와 setXxx() 메소드를 생성해준다.
❗️@Data를 사용하지 않는 이유
@Data를 사용하면 constructor, getter, setter, toString, equals, hashcode 메소드를 자동으로 생성해주기 때문에 코드가 굉장히 간결해보이게 만들어준다.
- @EqualsAndHashCode : equals 메소드와 hashcode 메소드를 생성한다
- 예를 들어, 객체를 Set에 저장한 뒤 필드 값을 변경하면 hashCode가 변경되면서 이전에 저장한 객체를 찾을 수 없는 문제가 발생한다.
- @RequiredArgsConstructor 은 상속 받은 클래스에서는 적용이 안된다.
- @Setter는 값을 바꿀 수 있는 어노테이션이기 때문에, 값을 바꿀 필요가 있는 경우에만 설정하고 이외의 상황에는 선언하면 안된다.
- @Data 대신, @Getter, @Setter, @ToString으로 명시하는 것을 권장한다.
2️⃣ BaseTimeEntity에 사용한 롬복
@EntityListeners
@EntityListeners 는 엔티티를 DB에 적용하기 전, 이후에 커스텀 콜백을 요청할 수 있는 어노테이션이다.
@EntityListeners 의 인자로 커스텀 콜백을 요청할 클래스를 지정해주면 되는데, Auditing 을 수행할 때는 JPA 에서 제공하는 AuditingEntityListener.class 를 인자로 넘기면 된다.
@MappedSuperClass
@MappedSuperClass 은 엔티티의 공통 매핑 정보가 필요할 때 주로 사용한다.
즉, 부모 클래스(엔티티)에 필드를 선언하고 단순히 속성만 받아서 사용하고싶을 때 사용하는 방법이다.
BaseTimeEntity를 생성하고 Auditing 기능이 필요한 엔티티 클래스에서 사용할 것이기 때문에 @MappedSuperClass 어노테이션을 사용하는 것이다.
@CreatedDate
@CreatedDate 어노테이션은 우리가 해당 필드를 선언하면 엔티티가 작성된 날짜, created 된 날짜를 사용할 수 있다.
- CreatedDate
- 해당 엔티티가 생성될 때, 생성하는 시각을 자동으로 삽입해준다.
- CreatedBy
- 해당 엔티티가 생성될 때, 생성하는 사람이 누구인지 자동으로 삽입해준다.
- 생성하는 주체를 지정하기 위해서 AuditorAware<T> 를 지정해야 한다.
- LastModifiedDate
- 해당 엔티티가 수정될 때, 수정하는 시각을 자동으로 삽입해준다.
- LastModifiedBy
- 해당 엔티티가 수정될 때, 수정하는 주체가 누구인지 자동으로 삽입해준다.
- 생성하는 주체를 지정하기 위해서 AuditorAware<T> 를 지정해야 한다.
- 해당 엔티티가 수정될 때, 수정하는 주체가 누구인지 자동으로 삽입해준다.
@Column(updatable = false)
이 어노테이션은 JPA의 기본 어노테이션의 @Column과 동일하다.
해당 BaseEntity를 JPA가 테이블에 접근하는 시점에만 JPA가 사용하도록 하고 싶은데 개발자에 의해 수정되는 혹시 모를 상황에 대비해서 updatable을 false로 해주었다.
3️⃣ Controller에 사용한 롬복
@RequestMapping("") / @RequestMapping(value = "/hello", method = RequestMethod.GET)
공통적인 url은 클래스에 설정 후 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping 뒤에 추가적인 url 을 붙인다.
@RequestMapping은 Class와 Method에 붙일 수 있고 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping들은 Method에만 붙일 수 있다.
@RestController
@Controller VS @RestController
@Controller는 주로 View를 반환하기 위해 사용되지만 Data를 반환하고 싶은 경우 @ResponseBody를 통해 Json 형태로 반환한다.
@RestController는 @Controller에 @ResponseBody가 추가된것이다. Json 형태로 객체 데이터를 반환하는데 주로 쓰인다.
컨트롤러를 통해 객체를 반환할 때는 객체 그대로 반환할 경우 HttpStatus를 설정해줄 수 없기 때문에 객체를 상황에 맞는 ResponseEntity로 감싸서 반환한다.
@RequiredArgsConstructor
Spring DI(의존성 주입)에는 세 가지 방식을 사용할 수 있다.
- 설정자 기반 의존성 주입 방식(setter-based dependency injection)
- 메서드의 인수를 통해 의존성을 주입하는 방식이다.
- 생성자 기반 의존성 주입 방식(constructor-based dependency injection)
- 생성자 기반 의존성 주입 방식은 생성자의 인수를 사용해 의존성을 주입하는 방식이다. 생성자에 @Autowired를 부여한다.
- 필드 기반 의존성 주입 방식(field-based injection)
- DI 컨테이너의 힘을 빌려 의존성을 주입하는 방식이다. 의존성을 주입하고 싶은 필드에 @Autowired 어노테이션을 달아준다. 생성자나 설정자 메서드를 만들 필요가 없기 때문에 소스코드가 비교적 간결해 보이는 장점이 있다. 하지만 소스코드의 양을 줄이기 위해 생성자나 설정자 메서드를 생략하고 싶다면 반드시 DI 컨테이너를 사용해야 한다.
생성자 기반 의존성 주입 방식
@Service
public class AuthServiceImpl implements AuthService {
private AccountRepository accountRepository;
private PasswordEncoder passwordEncoder;
@Autowired
public AuthServiceImpl(AccountRepository accountRepository, PasswordEncoder passwordEncoder) {
this.accountRepository = accountRepository;
this.passwordEncoder = passwordEncoder;
}
...
생성자주입은 위의 Constructor(생성자) 코드처럼 생성자를 만들기 번거롭다는 단점이 있지만 @RequiredArgsConstructor을 사용하여 간단한 방법으로 생성자 주입 방식의 코딩을 할 수 있다.
@RequiredArgsConstructor는 final이 붙거나 @NotNull 이 붙은 필드의 생성자를 자동 생성해준다.
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
private final AccountRepository accountRepository;
private final PasswordEncoder passwordEncoder;
...
4️⃣ Repository에 사용한 롬복
@Query("") / @Query(value = "", countQuery = "")
https://docs.spring.io/spring-data/jpa/docs/2.4.2/reference/html/#jpa.query-methods.at-query
레포지토리 메서드에 쿼리를 직접 정의하는 방식이다. 메소드 이름으로 쿼리를 작성하는 기능은 메소드가 너무 길어질 수 있지만, 해당 방법은 간략하게 작성 가능하며 어플리케이션 로딩 시점에 컴파일 에러를 제공한다.
❗️ 페이징에 fetch join이 들어간 경우에는 CountQuery를 정상적으로 자동으로 만들어주지 못하기 때문에 countQuery를 직접 작성해준다.
@EntityGraph(attributePaths = {""})
Lazy 패치 타입으로 맺어져 있는 entity들을 N+1 문제 없이 한번에 가져오고 싶어서 사용했다.
예를 들어 회원과 주문 엔티티가 있고 각 회원마다 주문이 OneToMany Lazy 타입으로 맺어져 있다. 회원 조회 시 해당 회원의 주문도 같이 조회하기 위해서는 회원의 주문 개수만큼 select 쿼리를 날려야 한다. 이는 N+1 문제를 일으킨다.
따라서 @EntityGraph를 이용해 회원과 주문을 함께 fetch하도록 하면 1번의 fetch join 쿼리만 실행된다.
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
@Modifying
@Query 어노테이션(JPQL Query, Native Query)을 통해 작성된 INSERT, UPDATE, DELETE (SELECT 제외) 쿼리에서 사용되는 어노테이션이며 기본적으로 JpaRepository에서 제공하는 메서드 혹은 메서드 네이밍으로 만들어진 쿼리에는 적용되지 않는다. clearAutomatically, flushAutomatically 속성을 변경할 수 있으며 주로 벌크 연산과 같이 이용된다.
JPA Entity Life-cycle을 무시하고 쿼리가 실행되기 때문에 해당 어노테이션을 사용할 때는 영속성 컨텍스트 관리에 주의해야 한다.
벌크 연산이란 다건의 UPDATE, DELETE 연산을 하나의 쿼리로 하는 것을 의미한다. @Query에 벌크 연산 쿼리를 작성하고, @Modifying 어노테이션을 붙이지 않으면 InvalidDataAccessApiUsageException이 발생한다.
JPA의 1차 캐시는 DB에 접근 횟수를 줄여 성능을 개선하는 좋은 기능이지만 @Modifying과 @Query를 이용한 벌크 연산에서는 이 기능때문에 예측하지 못한 결과가 나올 수 있다. 따라서 clearAutomatically를 true로 변경해주면, 벌크 연산 직 후 자동으로 영속성 컨텍스트를 clear 해준다. 그 뒤 조회를 실행할 경우 1차 캐시에 해당 엔티티가 존재하지 않기 때문에 DB 조회 쿼리를 실행하게 되어 데이터 동기화 문제를 해결할 수 있다.
5️⃣ Service에 사용한 롬복
@Transactional(readOnly = true)
Hibernate는 readOnly 옵션이 설정된 경우는 Flush Mode를 'FlushMode.MANUAL' 모드로 설정한다.
이는 곧, '이 트랜젝션은 커밋 시 flush를 하지 않는다'는 것을 의미한다.
결국은 Hibernate는 Entity에 flush를 호출하지 않게 되고, 변경은 자연스럽게 무시되게 된다.
또한, flush가 호출되지 않고, Dirty Checking을 하지 않기 때문에 성능적으로도 이점을 얻을 수 있다.
@Transactional(rollbackFor = Exception.class)
스프링은 RuntimeException 과 Error를 기본적으로 롤백 정책으로 이해한다. 따라서 Checked Exception이 발생한 경우 예외가 발생했음에도 롤백이 되지 않는다. 따라서 모든 예외에 대해 전부 트랜잭션을 롤백하기 위해 rollbackFor을 설정해주었다.
6️⃣ Config에 사용한 롬복
기존의 Spring MVC에서는 xml을 활용하여 Bean을 등록했지만 프로젝트의 규모가 커짐에 따라 사용하는 요소들을 xml에 등록하는 것이 번거로워 어노테이션을를 활용한 Bean 등록 방법이 탄생하게 되었다. Spring에서 Bean을 등록하기 위해서는 @Bean, @Component, @Configuration 어노테이션이 있다.
❓Spring Bean이란?
Spring에서는 Spring의 DI Container에 의해 관리되는 POJO(Plain Old Java Object)를 Bean이라고 부르며, 이러한 Bean들은 Spring을 구성하는 핵심 요소이다.
- POJO(Plain Old Java Object)로써 Spring 애플리케이션을 구성하는 핵심 객체이다.
- Spring IoC 컨테이너(또는 DI 컨테이너)에 의해 생성 및 관리된다.
- class, id, scope, constructor-arg, property를 주요 속성으로 지닌다.
@Configuration, @Bean
- 수동으로 스프링 컨테이너에 빈을 등록하는 방법
- 개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용
- 유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용하여 여러 구현체를 빈으로 등록 할 때 사용
- 1개 이상의 @Bean을 제공하는 클래스의 경우 반드시 @Configuration을 명시해 주어야 싱글톤이 보장됨
@Component
- 자동으로 스프링 컨테이너에 빈을 등록하는 방법
- 스프링의 컴포넌트 스캔 기능이 @Component 어노테이션이 있는 클래스를 자동으로 찾아서 빈으로 등록함
- 대부분의 경우 @Component를 이용한 자동 등록 방식을 사용하는 것이 좋음
- @Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 있음
'Back-end' 카테고리의 다른 글
[AWS] SpringBoot + gradle 프로젝트(jar) EC2 서버 배포 (0) | 2023.02.11 |
---|---|
[Spring Security] CSRF disable? (0) | 2023.01.26 |
[Spring Boot] RestController, ResponseEntity란? (0) | 2023.01.20 |
[Spring Boot] H2 설치, Spring Boot H2 연결 (0) | 2023.01.18 |
[git] .gitignore 사용법, gitignore 적용하기 (0) | 2023.01.18 |